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