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