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