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