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