uistyle: Add more type-safety to UiStyle, and clean up a bit
[quassel.git] / src / uisupport / uistyle.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2016 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #pragma once
22
23 #include <utility>
24 #include <vector>
25
26 #include <QDataStream>
27 #include <QFontMetricsF>
28 #include <QHash>
29 #include <QIcon>
30 #include <QTextCharFormat>
31 #include <QTextLayout>
32 #include <QPalette>
33 #include <QVector>
34
35 #include "bufferinfo.h"
36 #include "message.h"
37 #include "networkmodel.h"
38 #include "settings.h"
39
40 class UiStyle : public QObject
41 {
42     Q_OBJECT
43
44 public:
45     UiStyle(QObject *parent = 0);
46     virtual ~UiStyle();
47
48     //! This enumerates the possible formats a text element may have. */
49     /** These formats are ordered on increasing importance, in cases where a given property is specified
50      *  by multiple active formats.
51      *  \NOTE: Do not change/add values here without also adapting the relevant
52      *         methods in this class (in particular mergedFormat())!
53      *         Also, we _do_ rely on certain properties of these values in styleString() and friends!
54      */
55     enum class FormatType : quint32 {
56         Base            = 0x00000000,
57         Invalid         = 0xffffffff,
58
59         // Message Formats (mutually exclusive!)
60         PlainMsg        = 0x00000001,
61         NoticeMsg       = 0x00000002,
62         ActionMsg       = 0x00000003,
63         NickMsg         = 0x00000004,
64         ModeMsg         = 0x00000005,
65         JoinMsg         = 0x00000006,
66         PartMsg         = 0x00000007,
67         QuitMsg         = 0x00000008,
68         KickMsg         = 0x00000009,
69         KillMsg         = 0x0000000a,
70         ServerMsg       = 0x0000000b,
71         InfoMsg         = 0x0000000c,
72         ErrorMsg        = 0x0000000d,
73         DayChangeMsg    = 0x0000000e,
74         TopicMsg        = 0x0000000f,
75         NetsplitJoinMsg = 0x00000010,
76         NetsplitQuitMsg = 0x00000020,
77         InviteMsg       = 0x00000030,
78
79         // Standard Formats
80         Bold            = 0x00000100,
81         Italic          = 0x00000200,
82         Underline       = 0x00000400,
83         Reverse         = 0x00000800,
84
85         // Individual parts of a message
86         Timestamp       = 0x00001000,
87         Sender          = 0x00002000,
88         Contents        = 0x00004000,
89         Nick            = 0x00008000,
90         Hostmask        = 0x00010000,
91         ChannelName     = 0x00020000,
92         ModeFlags       = 0x00040000,
93
94         // URL is special, we want that to take precedence over the rest...
95         Url             = 0x00080000
96
97                           // mIRC Colors - we assume those to be present only in plain contents
98                           // foreground: 0x0.400000
99                           // background: 0x.0800000
100     };
101
102     enum class MessageLabel : quint32 {
103         None            = 0x00000000,
104         OwnMsg          = 0x00000001,
105         Highlight       = 0x00000002,
106         Selected        = 0x00000004 // must be last!
107     };
108
109     enum class ItemFormatType : quint32 {
110         None              = 0x00000000,
111
112         BufferViewItem    = 0x00000001,
113         NickViewItem      = 0x00000002,
114
115         NetworkItem       = 0x00000010,
116         ChannelBufferItem = 0x00000020,
117         QueryBufferItem   = 0x00000040,
118         IrcUserItem       = 0x00000080,
119         UserCategoryItem  = 0x00000100,
120
121         InactiveBuffer    = 0x00001000,
122         ActiveBuffer      = 0x00002000,
123         UnreadBuffer      = 0x00004000,
124         HighlightedBuffer = 0x00008000,
125         UserAway          = 0x00010000,
126
127         Invalid           = 0xffffffff
128     };
129
130     enum class ColorRole {
131         MarkerLine,
132         // Sender colors (16 + self)
133         // These aren't used directly to avoid having storing all of the sender color options in the
134         // rendering routine of each item.  Also, I couldn't figure out how to do that.
135         // It would be nice to have the UseSenderColors preference also toggle sender colors set by
136         // themes, so hopefully this can be extended in the future.
137         // Furthermore, using this palette directly would mean separate sets of colors couldn't be
138         // used for different message types.
139         SenderColorSelf,
140         SenderColor00,
141         SenderColor01,
142         SenderColor02,
143         SenderColor03,
144         SenderColor04,
145         SenderColor05,
146         SenderColor06,
147         SenderColor07,
148         SenderColor08,
149         SenderColor09,
150         SenderColor0a,
151         SenderColor0b,
152         SenderColor0c,
153         SenderColor0d,
154         SenderColor0e,
155         SenderColor0f,
156         NumRoles // must be last!
157     };
158
159     struct Format {
160         FormatType type;
161     };
162
163     using FormatList = std::vector<std::pair<quint16, Format>>;
164
165     struct StyledString {
166         QString plainText;
167         FormatList formatList; // starting pos, ftypes
168     };
169
170     class StyledMessage;
171
172     /**
173      * List of default sender colors
174      *
175      * In order from 1 - 16, matching the Sender## format in the settings file.
176      * Don't change the length or values of the colors without updating the UI and color roles, too.
177      *
178      * @see ../qtui/settingspages/chatviewsettingspage.ui
179      * @see UiStyle::ColorRole
180      */
181     const QList<QColor> defaultSenderColors = QList<QColor> {
182         QColor(204,   0,   0),  /// Sender00
183         QColor(  0, 108, 173),  /// Sender01
184         QColor( 77, 153,   0),  /// Sender02
185         QColor(102,   0, 204),  /// Sender03
186         QColor(166, 125,   0),  /// Sender04
187         QColor(  0, 153,  39),  /// Sender05
188         QColor(  0,  48, 192),  /// Sender06
189         QColor(204,   0, 154),  /// Sender07
190         QColor(185,  70,   0),  /// Sender08
191         QColor(134, 153,   0),  /// Sender09
192         QColor( 20, 153,   0),  /// Sender10
193         QColor(  0, 153,  96),  /// Sender11
194         QColor(  0, 108, 173),  /// Sender12
195         QColor(  0, 153, 204),  /// Sender13
196         QColor(179,   0, 204),  /// Sender14
197         QColor(204,   0,  77),  /// Sender15
198     };
199     // Explicitly declare QList<QColor> type for defaultSenderColors, otherwise error C2797
200     // "list initialization inside member initializer list" will occur in Windows builds with Visual
201     // Studio's compiler.
202     //
203     // See https://blogs.msdn.microsoft.com/vcblog/2014/08/19/the-future-of-non-static-data-member-initialization/
204     // Note: Qt Creator flags this as invalid unless you set Clang in
205     // Settings -> C++ -> Code Model -> Code Completion and Semantic Highlighting -> C
206     //
207     // See https://bugreports.qt.io/browse/QTCREATORBUG-1902
208
209     /**
210      * Default sender color for sent messages
211      */
212     const QColor defaultSenderColorSelf = QColor(0, 0, 0);
213
214     static FormatType formatType(Message::Type msgType);
215     static StyledString styleString(const QString &string, FormatType baseFormat = FormatType::Base);
216     static QString mircToInternal(const QString &);
217
218     /**
219      * Gets if a custom timestamp format is used.
220      *
221      * @return True if custom timestamp format used, otherwise false
222      */
223     static inline bool useCustomTimestampFormat() { return _useCustomTimestampFormat; }
224
225     /**
226      * Gets the format string for chat log timestamps according to the system locale.
227      *
228      * This will return " hh:mm:ss" for system locales with 24-hour time or " h:mm:ss AP" for
229      * systems with 12-hour time.
230      *
231      * @return String representing timestamp format according to system locale, e.g. " hh:mm:ss"
232      */
233     static QString systemTimestampFormatString();
234
235     /**
236      * Gets the format string for chat log timestamps, either system locale or custom.
237      *
238      * Depending on useCustomTimestampFormat(), this will return either the system locale based
239      * time format, or the custom user-specified string.
240      *
241      * @return String representing timestamp format, e.g. "[hh:mm:ss]" or " hh:mm:ss"
242      */
243     static QString timestampFormatString();
244
245     QTextCharFormat format(FormatType formatType, MessageLabel messageLabel) const;
246     QFontMetricsF *fontMetrics(FormatType formatType, MessageLabel messageLabel) const;
247
248     QList<QTextLayout::FormatRange> toTextLayoutList(const FormatList &, int textLength, MessageLabel messageLabel) const;
249
250     inline const QBrush &brush(ColorRole role) const { return _uiStylePalette.at((int)role); }
251     inline void setBrush(ColorRole role, const QBrush &brush) { _uiStylePalette[(int)role] = brush; }
252
253     QVariant bufferViewItemData(const QModelIndex &networkModelIndex, int role) const;
254     QVariant nickViewItemData(const QModelIndex &networkModelIndex, int role) const;
255
256 public slots:
257     void reload();
258
259 signals:
260     void changed();
261
262 protected:
263     void loadStyleSheet();
264     QString loadStyleSheet(const QString &name, bool shouldExist = false);
265
266     QTextCharFormat format(quint64 key) const;
267     QTextCharFormat cachedFormat(FormatType formatType, MessageLabel messageLabel) const;
268     void setCachedFormat(const QTextCharFormat &format, FormatType formatType, MessageLabel messageLabel) const;
269     void mergeFormat(QTextCharFormat &format, FormatType formatType, MessageLabel messageLabel) const;
270     void mergeSubElementFormat(QTextCharFormat &format, FormatType formatType, MessageLabel messageLabel) const;
271
272     static FormatType formatType(const QString &code);
273     static QString formatCode(FormatType);
274
275     /**
276      * Cache the system locale timestamp format string
277      *
278      * Based on whether or not AM/PM designators are used in the QLocale::system().timeFormat(),
279      * this extends the system locale timestamp format string to include seconds.
280      *
281      * @see UiStyle::systemTimestampFormatString()
282      */
283     static void updateSystemTimestampFormat();
284
285     /**
286      * Updates the local setting cache of whether or not to use the custom timestamp format
287      *
288      * @param[in] enabled  If true, custom timestamp format used, otherwise false
289      */
290     static void setUseCustomTimestampFormat(bool enabled);
291
292     /**
293      * Updates the local setting cache of the timestamp format string
294      *
295      * @param[in] format   Timestamp format string
296      */
297     static void setTimestampFormatString(const QString &format);
298     /**
299      * Updates the local setting cache of whether or not to show sender prefixmodes
300      *
301      * @param[in] enabled  If true, sender prefixmodes are enabled, otherwise false.
302      */
303     static void enableSenderPrefixes(bool enabled);
304
305     /**
306      * Updates the local setting cache of whether or not to show sender brackets
307      *
308      * @param[in] enabled  If true, sender brackets are enabled, otherwise false.
309      */
310     static void enableSenderBrackets(bool enabled);
311
312     QVariant itemData(int role, const QTextCharFormat &format) const;
313
314 private slots:
315     void allowMircColorsChanged(const QVariant &);
316     void showItemViewIconsChanged(const QVariant &);
317
318 private:
319     QVector<QBrush> _uiStylePalette;
320     QBrush _markerLineBrush;
321     QHash<quint64, QTextCharFormat> _formats;
322     mutable QHash<quint64, QTextCharFormat> _formatCache;
323     mutable QHash<quint64, QFontMetricsF *> _metricsCache;
324     QHash<UiStyle::ItemFormatType, QTextCharFormat> _listItemFormats;
325     static QHash<QString, FormatType> _formatCodes;
326     static bool _useCustomTimestampFormat;        /// If true, use the custom timestamp format
327     static QString _systemTimestampFormatString;  /// Cached copy of system locale timestamp format
328     static QString _timestampFormatString;        /// Timestamp format string
329     static bool _showSenderPrefixes;              /// If true, show prefixmodes before sender names
330     static bool _showSenderBrackets;              /// If true, show brackets around sender names
331
332     QIcon _channelJoinedIcon;
333     QIcon _channelPartedIcon;
334     QIcon _userOfflineIcon;
335     QIcon _userOnlineIcon;
336     QIcon _userAwayIcon;
337     QIcon _categoryOpIcon;
338     QIcon _categoryVoiceIcon;
339     int _opIconLimit;
340     int _voiceIconLimit;
341     bool _showNickViewIcons;
342     bool _showBufferViewIcons;
343     bool _allowMircColors;
344 };
345
346
347 class UiStyle::StyledMessage : public Message
348 {
349     Q_DECLARE_TR_FUNCTIONS(UiStyle::StyledMessage)
350
351 public:
352     explicit StyledMessage(const Message &message);
353
354     QString decoratedTimestamp() const;
355     QString plainSender() const;           //!< Nickname (no decorations) for Plain and Notice, empty else
356     QString decoratedSender() const;
357     const QString &plainContents() const;
358
359     const FormatList &contentsFormatList() const;
360
361     quint8 senderHash() const;
362
363 protected:
364     void style() const;
365
366 private:
367     mutable StyledString _contents;
368     mutable quint8 _senderHash;
369 };
370
371 #if QT_VERSION < 0x050000
372 uint qHash(UiStyle::ItemFormatType key);
373 #else
374 uint qHash(UiStyle::ItemFormatType key, uint seed);
375 #endif
376
377 // ---- Operators for dealing with enums ----------------------------------------------------------
378
379 UiStyle::FormatType operator|(UiStyle::FormatType lhs, UiStyle::FormatType rhs);
380 UiStyle::FormatType& operator|=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
381 UiStyle::FormatType operator|(UiStyle::FormatType lhs, quint32 rhs);
382 UiStyle::FormatType& operator|=(UiStyle::FormatType &lhs, quint32 rhs);
383 UiStyle::FormatType operator&(UiStyle::FormatType lhs, UiStyle::FormatType rhs);
384 UiStyle::FormatType& operator&=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
385 UiStyle::FormatType operator&(UiStyle::FormatType lhs, quint32 rhs);
386 UiStyle::FormatType& operator&=(UiStyle::FormatType &lhs, quint32 rhs);
387 UiStyle::FormatType& operator^=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
388
389 UiStyle::MessageLabel operator|(UiStyle::MessageLabel lhs, UiStyle::MessageLabel rhs);
390 UiStyle::MessageLabel& operator|=(UiStyle::MessageLabel &lhs, UiStyle::MessageLabel rhs);
391 UiStyle::MessageLabel operator&(UiStyle::MessageLabel lhs, quint32 rhs);
392 UiStyle::MessageLabel& operator&=(UiStyle::MessageLabel &lhs, UiStyle::MessageLabel rhs);
393
394 // Shifts the label into the upper half of the return value
395 quint64 operator|(UiStyle::FormatType lhs, UiStyle::MessageLabel rhs);
396
397 UiStyle::ItemFormatType operator|(UiStyle::ItemFormatType lhs, UiStyle::ItemFormatType rhs);
398 UiStyle::ItemFormatType& operator|=(UiStyle::ItemFormatType &lhs, UiStyle::ItemFormatType rhs);
399
400 // ---- Allow for FormatList in QVariant ----------------------------------------------------------
401
402 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList);
403 QDataStream &operator>>(QDataStream &in, UiStyle::FormatList &formatList);
404
405 Q_DECLARE_METATYPE(UiStyle::FormatList)
406 Q_DECLARE_METATYPE(UiStyle::MessageLabel)