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