QssParser: Interpret "oblique" as italic
[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 #if QT_VERSION >= 0x050000
168     enum class SenderPrefixMode {
169 #else
170     enum SenderPrefixMode {
171 #endif
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::system().timeFormat(),
302      * this extends the system 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 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 #if QT_VERSION < 0x050000
395 uint qHash(UiStyle::ItemFormatType key);
396 #else
397 uint qHash(UiStyle::ItemFormatType key, uint seed);
398 #endif
399
400 // ---- Operators for dealing with enums ----------------------------------------------------------
401
402 UiStyle::FormatType operator|(UiStyle::FormatType lhs, UiStyle::FormatType rhs);
403 UiStyle::FormatType& operator|=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
404 UiStyle::FormatType operator|(UiStyle::FormatType lhs, quint32 rhs);
405 UiStyle::FormatType& operator|=(UiStyle::FormatType &lhs, quint32 rhs);
406 UiStyle::FormatType operator&(UiStyle::FormatType lhs, UiStyle::FormatType rhs);
407 UiStyle::FormatType& operator&=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
408 UiStyle::FormatType operator&(UiStyle::FormatType lhs, quint32 rhs);
409 UiStyle::FormatType& operator&=(UiStyle::FormatType &lhs, quint32 rhs);
410 UiStyle::FormatType& operator^=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
411
412 UiStyle::MessageLabel operator|(UiStyle::MessageLabel lhs, UiStyle::MessageLabel rhs);
413 UiStyle::MessageLabel& operator|=(UiStyle::MessageLabel &lhs, UiStyle::MessageLabel rhs);
414 UiStyle::MessageLabel operator&(UiStyle::MessageLabel lhs, quint32 rhs);
415 UiStyle::MessageLabel& operator&=(UiStyle::MessageLabel &lhs, UiStyle::MessageLabel rhs);
416
417 // Shifts the label into the upper half of the return value
418 quint64 operator|(UiStyle::FormatType lhs, UiStyle::MessageLabel rhs);
419
420 UiStyle::ItemFormatType operator|(UiStyle::ItemFormatType lhs, UiStyle::ItemFormatType rhs);
421 UiStyle::ItemFormatType& operator|=(UiStyle::ItemFormatType &lhs, UiStyle::ItemFormatType rhs);
422
423 // ---- Allow for FormatList in QVariant ----------------------------------------------------------
424
425 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList);
426 QDataStream &operator>>(QDataStream &in, UiStyle::FormatList &formatList);
427
428 Q_DECLARE_METATYPE(UiStyle::FormatList)
429 Q_DECLARE_METATYPE(UiStyle::MessageLabel)
430 Q_DECLARE_METATYPE(UiStyle::SenderPrefixMode)