661e02577ece14a776101c0392297f6b6c3dfc8e
[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,
109         Hovered         = 0x00000008,
110         Last            = Hovered
111     };
112
113     enum class ItemFormatType : quint32 {
114         None              = 0x00000000,
115
116         BufferViewItem    = 0x00000001,
117         NickViewItem      = 0x00000002,
118
119         NetworkItem       = 0x00000010,
120         ChannelBufferItem = 0x00000020,
121         QueryBufferItem   = 0x00000040,
122         IrcUserItem       = 0x00000080,
123         UserCategoryItem  = 0x00000100,
124
125         InactiveBuffer    = 0x00001000,
126         ActiveBuffer      = 0x00002000,
127         UnreadBuffer      = 0x00004000,
128         HighlightedBuffer = 0x00008000,
129         UserAway          = 0x00010000,
130
131         Invalid           = 0xffffffff
132     };
133
134     enum class FormatProperty {
135         AllowForegroundOverride = QTextFormat::UserProperty,
136         AllowBackgroundOverride
137     };
138
139     enum class ColorRole {
140         MarkerLine,
141         // Sender colors (16 + self)
142         // These aren't used directly to avoid having storing all of the sender color options in the
143         // rendering routine of each item.  Also, I couldn't figure out how to do that.
144         // It would be nice to have the UseSenderColors preference also toggle sender colors set by
145         // themes, so hopefully this can be extended in the future.
146         // Furthermore, using this palette directly would mean separate sets of colors couldn't be
147         // used for different message types.
148         SenderColorSelf,
149         SenderColor00,
150         SenderColor01,
151         SenderColor02,
152         SenderColor03,
153         SenderColor04,
154         SenderColor05,
155         SenderColor06,
156         SenderColor07,
157         SenderColor08,
158         SenderColor09,
159         SenderColor0a,
160         SenderColor0b,
161         SenderColor0c,
162         SenderColor0d,
163         SenderColor0e,
164         SenderColor0f,
165         NumRoles // must be last!
166     };
167
168     /// Display of sender prefix modes
169     enum class SenderPrefixMode {
170         NoModes = 0,      ///< Hide sender modes
171         HighestMode = 1,  ///< Show the highest active sender mode
172         AllModes = 2      ///< Show all active sender modes
173     };
174     // Do not change SenderPrefixMode numbering without also adjusting
175     // ChatViewSettingsPage::initSenderPrefixComboBox() and chatviewsettingspage.ui "defaultValue"
176
177     struct Format {
178         FormatType type;
179         QColor foreground;
180         QColor background;
181     };
182
183     using FormatList = std::vector<std::pair<quint16, Format>>;
184
185     struct StyledString {
186         QString plainText;
187         FormatList formatList; // starting pos, ftypes
188     };
189
190     class StyledMessage;
191
192     /**
193      * List of default sender colors
194      *
195      * In order from 1 - 16, matching the Sender## format in the settings file.
196      * Don't change the length or values of the colors without updating the UI and color roles, too.
197      *
198      * @see ../qtui/settingspages/chatviewsettingspage.ui
199      * @see UiStyle::ColorRole
200      */
201     const QList<QColor> defaultSenderColors = QList<QColor> {
202         QColor(204,   0,   0),  ///< Sender00
203         QColor(  0, 108, 173),  ///< Sender01
204         QColor( 77, 153,   0),  ///< Sender02
205         QColor(102,   0, 204),  ///< Sender03
206         QColor(166, 125,   0),  ///< Sender04
207         QColor(  0, 153,  39),  ///< Sender05
208         QColor(  0,  48, 192),  ///< Sender06
209         QColor(204,   0, 154),  ///< Sender07
210         QColor(185,  70,   0),  ///< Sender08
211         QColor(134, 153,   0),  ///< Sender09
212         QColor( 20, 153,   0),  ///< Sender10
213         QColor(  0, 153,  96),  ///< Sender11
214         QColor(  0, 108, 173),  ///< Sender12
215         QColor(  0, 153, 204),  ///< Sender13
216         QColor(179,   0, 204),  ///< Sender14
217         QColor(204,   0,  77),  ///< Sender15
218     };
219     // Explicitly declare QList<QColor> type for defaultSenderColors, otherwise error C2797
220     // "list initialization inside member initializer list" will occur in Windows builds with Visual
221     // Studio's compiler.
222     //
223     // See https://blogs.msdn.microsoft.com/vcblog/2014/08/19/the-future-of-non-static-data-member-initialization/
224     // Note: Qt Creator flags this as invalid unless you set Clang in
225     // Settings -> C++ -> Code Model -> Code Completion and Semantic Highlighting -> C
226     //
227     // See https://bugreports.qt.io/browse/QTCREATORBUG-1902
228
229     /**
230      * Default sender color for sent messages
231      */
232     const QColor defaultSenderColorSelf = QColor(0, 0, 0);
233
234     static FormatType formatType(Message::Type msgType);
235     static StyledString styleString(const QString &string, FormatType baseFormat = FormatType::Base);
236     static QString mircToInternal(const QString &);
237
238     /**
239      * Gets if a custom timestamp format is used.
240      *
241      * @return True if custom timestamp format used, otherwise false
242      */
243     static inline bool useCustomTimestampFormat() { return _useCustomTimestampFormat; }
244
245     /**
246      * Gets the format string for chat log timestamps according to the system locale.
247      *
248      * This will return " hh:mm:ss" for system locales with 24-hour time or " h:mm:ss AP" for
249      * systems with 12-hour time.
250      *
251      * @return String representing timestamp format according to system locale, e.g. " hh:mm:ss"
252      */
253     static QString systemTimestampFormatString();
254
255     /**
256      * Gets the format string for chat log timestamps, either system locale or custom.
257      *
258      * Depending on useCustomTimestampFormat(), this will return either the system locale based
259      * time format, or the custom user-specified string.
260      *
261      * @return String representing timestamp format, e.g. "[hh:mm:ss]" or " hh:mm:ss"
262      */
263     static QString timestampFormatString();
264
265     QTextCharFormat format(const Format &format, MessageLabel messageLabel) const;
266     QFontMetricsF *fontMetrics(FormatType formatType, MessageLabel messageLabel) const;
267
268     QList<QTextLayout::FormatRange> toTextLayoutList(const FormatList &, int textLength, MessageLabel messageLabel) const;
269
270     inline const QBrush &brush(ColorRole role) const { return _uiStylePalette.at((int)role); }
271     inline void setBrush(ColorRole role, const QBrush &brush) { _uiStylePalette[(int)role] = brush; }
272
273     QVariant bufferViewItemData(const QModelIndex &networkModelIndex, int role) const;
274     QVariant nickViewItemData(const QModelIndex &networkModelIndex, int role) const;
275
276 public slots:
277     void reload();
278
279 signals:
280     void changed();
281
282 protected:
283     void loadStyleSheet();
284     QString loadStyleSheet(const QString &name, bool shouldExist = false);
285
286     QTextCharFormat parsedFormat(quint64 key) const;
287     QTextCharFormat cachedFormat(const Format &format, MessageLabel messageLabel) const;
288     void setCachedFormat(const QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
289     void mergeFormat(QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
290     void mergeSubElementFormat(QTextCharFormat &charFormat, FormatType formatType, MessageLabel messageLabel) const;
291     void mergeColors(QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
292
293     static FormatType formatType(const QString &code);
294     static QString formatCode(FormatType);
295
296     /**
297      * Cache the system locale timestamp format string
298      *
299      * Based on whether or not AM/PM designators are used in the QLocale.timeFormat(), this extends
300      * the application locale timestamp format string to include seconds.
301      *
302      * @see UiStyle::systemTimestampFormatString()
303      */
304     static void updateSystemTimestampFormat();
305
306     /**
307      * Updates the local setting cache of whether or not to use the custom timestamp format
308      *
309      * @param[in] enabled  If true, custom timestamp format used, otherwise false
310      */
311     static void setUseCustomTimestampFormat(bool enabled);
312
313     /**
314      * Updates the local setting cache of the timestamp format string
315      *
316      * @param[in] format   Timestamp format string
317      */
318     static void setTimestampFormatString(const QString &format);
319     /**
320      * Updates the local setting cache of how to display sender prefix modes
321      *
322      * @param[in] mode  Display format for sender prefix modes
323      */
324     static void setSenderPrefixDisplay(UiStyle::SenderPrefixMode mode);
325
326     /**
327      * Updates the local setting cache of whether or not to show sender brackets
328      *
329      * @param[in] enabled  If true, sender brackets are enabled, otherwise false.
330      */
331     static void enableSenderBrackets(bool enabled);
332
333     QVariant itemData(int role, const QTextCharFormat &format) const;
334
335 private slots:
336     void allowMircColorsChanged(const QVariant &);
337     void showItemViewIconsChanged(const QVariant &);
338
339 private:
340     QVector<QBrush> _uiStylePalette;
341     QBrush _markerLineBrush;
342     QHash<quint64, QTextCharFormat> _formats;
343     mutable QHash<QString, QTextCharFormat> _formatCache;
344     mutable QHash<quint64, QFontMetricsF *> _metricsCache;
345     QHash<UiStyle::ItemFormatType, QTextCharFormat> _listItemFormats;
346     static QHash<QString, FormatType> _formatCodes;
347     static bool _useCustomTimestampFormat;        ///< If true, use the custom timestamp format
348     static QString _systemTimestampFormatString;  ///< Cached copy of system locale timestamp format
349     static QString _timestampFormatString;        ///< Timestamp format string
350     static UiStyle::SenderPrefixMode _senderPrefixDisplay; ///< Prefix mode display before sender
351     static bool _showSenderBrackets;              ///< If true, show brackets around sender names
352
353     QIcon _channelJoinedIcon;
354     QIcon _channelPartedIcon;
355     QIcon _userOfflineIcon;
356     QIcon _userOnlineIcon;
357     QIcon _userAwayIcon;
358     QIcon _categoryOpIcon;
359     QIcon _categoryVoiceIcon;
360     int _opIconLimit;
361     int _voiceIconLimit;
362     bool _showNickViewIcons;
363     bool _showBufferViewIcons;
364     bool _allowMircColors;
365 };
366
367
368 class UiStyle::StyledMessage : public Message
369 {
370     Q_DECLARE_TR_FUNCTIONS(UiStyle::StyledMessage)
371
372 public:
373     explicit StyledMessage(const Message &message);
374
375     QString decoratedTimestamp() const;
376     QString plainSender() const;           //!< Nickname (no decorations) for Plain and Notice, empty else
377     QString decoratedSender() const;
378     const QString &plainContents() const;
379
380     const FormatList &contentsFormatList() const;
381
382     quint8 senderHash() const;
383
384 protected:
385     void style() const;
386
387 private:
388     mutable StyledString _contents;
389     mutable quint8 _senderHash;
390 };
391
392 uint qHash(UiStyle::ItemFormatType key, uint seed);
393
394 // ---- Operators for dealing with enums ----------------------------------------------------------
395
396 UiStyle::FormatType operator|(UiStyle::FormatType lhs, UiStyle::FormatType rhs);
397 UiStyle::FormatType& operator|=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
398 UiStyle::FormatType operator|(UiStyle::FormatType lhs, quint32 rhs);
399 UiStyle::FormatType& operator|=(UiStyle::FormatType &lhs, quint32 rhs);
400 UiStyle::FormatType operator&(UiStyle::FormatType lhs, UiStyle::FormatType rhs);
401 UiStyle::FormatType& operator&=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
402 UiStyle::FormatType operator&(UiStyle::FormatType lhs, quint32 rhs);
403 UiStyle::FormatType& operator&=(UiStyle::FormatType &lhs, quint32 rhs);
404 UiStyle::FormatType& operator^=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
405
406 UiStyle::MessageLabel operator|(UiStyle::MessageLabel lhs, UiStyle::MessageLabel rhs);
407 UiStyle::MessageLabel& operator|=(UiStyle::MessageLabel &lhs, UiStyle::MessageLabel rhs);
408 UiStyle::MessageLabel operator&(UiStyle::MessageLabel lhs, quint32 rhs);
409 UiStyle::MessageLabel& operator&=(UiStyle::MessageLabel &lhs, UiStyle::MessageLabel rhs);
410
411 // Shifts the label into the upper half of the return value
412 quint64 operator|(UiStyle::FormatType lhs, UiStyle::MessageLabel rhs);
413
414 UiStyle::ItemFormatType operator|(UiStyle::ItemFormatType lhs, UiStyle::ItemFormatType rhs);
415 UiStyle::ItemFormatType& operator|=(UiStyle::ItemFormatType &lhs, UiStyle::ItemFormatType rhs);
416
417 // ---- Allow for FormatList in QVariant ----------------------------------------------------------
418
419 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList);
420 QDataStream &operator>>(QDataStream &in, UiStyle::FormatList &formatList);
421
422 Q_DECLARE_METATYPE(UiStyle::FormatList)
423 Q_DECLARE_METATYPE(UiStyle::MessageLabel)
424 Q_DECLARE_METATYPE(UiStyle::SenderPrefixMode)