client: Highest sender prefix mode, migrate, fix
[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     enum class SenderPrefixMode {
168         NoModes = 0,      ///< Hide sender modes
169         HighestMode = 1,  ///< Show the highest active sender mode
170         AllModes = 2      ///< Show all active sender modes
171     };
172     // Do not change SenderPrefixMode numbering without also adjusting
173     // ChatViewSettingsPage::initSenderPrefixComboBox() and chatviewsettingspage.ui "defaultValue"
174
175     struct Format {
176         FormatType type;
177         QColor foreground;
178         QColor background;
179     };
180
181     using FormatList = std::vector<std::pair<quint16, Format>>;
182
183     struct StyledString {
184         QString plainText;
185         FormatList formatList; // starting pos, ftypes
186     };
187
188     class StyledMessage;
189
190     /**
191      * List of default sender colors
192      *
193      * In order from 1 - 16, matching the Sender## format in the settings file.
194      * Don't change the length or values of the colors without updating the UI and color roles, too.
195      *
196      * @see ../qtui/settingspages/chatviewsettingspage.ui
197      * @see UiStyle::ColorRole
198      */
199     const QList<QColor> defaultSenderColors = QList<QColor> {
200         QColor(204,   0,   0),  /// Sender00
201         QColor(  0, 108, 173),  /// Sender01
202         QColor( 77, 153,   0),  /// Sender02
203         QColor(102,   0, 204),  /// Sender03
204         QColor(166, 125,   0),  /// Sender04
205         QColor(  0, 153,  39),  /// Sender05
206         QColor(  0,  48, 192),  /// Sender06
207         QColor(204,   0, 154),  /// Sender07
208         QColor(185,  70,   0),  /// Sender08
209         QColor(134, 153,   0),  /// Sender09
210         QColor( 20, 153,   0),  /// Sender10
211         QColor(  0, 153,  96),  /// Sender11
212         QColor(  0, 108, 173),  /// Sender12
213         QColor(  0, 153, 204),  /// Sender13
214         QColor(179,   0, 204),  /// Sender14
215         QColor(204,   0,  77),  /// Sender15
216     };
217     // Explicitly declare QList<QColor> type for defaultSenderColors, otherwise error C2797
218     // "list initialization inside member initializer list" will occur in Windows builds with Visual
219     // Studio's compiler.
220     //
221     // See https://blogs.msdn.microsoft.com/vcblog/2014/08/19/the-future-of-non-static-data-member-initialization/
222     // Note: Qt Creator flags this as invalid unless you set Clang in
223     // Settings -> C++ -> Code Model -> Code Completion and Semantic Highlighting -> C
224     //
225     // See https://bugreports.qt.io/browse/QTCREATORBUG-1902
226
227     /**
228      * Default sender color for sent messages
229      */
230     const QColor defaultSenderColorSelf = QColor(0, 0, 0);
231
232     static FormatType formatType(Message::Type msgType);
233     static StyledString styleString(const QString &string, FormatType baseFormat = FormatType::Base);
234     static QString mircToInternal(const QString &);
235
236     /**
237      * Gets if a custom timestamp format is used.
238      *
239      * @return True if custom timestamp format used, otherwise false
240      */
241     static inline bool useCustomTimestampFormat() { return _useCustomTimestampFormat; }
242
243     /**
244      * Gets the format string for chat log timestamps according to the system locale.
245      *
246      * This will return " hh:mm:ss" for system locales with 24-hour time or " h:mm:ss AP" for
247      * systems with 12-hour time.
248      *
249      * @return String representing timestamp format according to system locale, e.g. " hh:mm:ss"
250      */
251     static QString systemTimestampFormatString();
252
253     /**
254      * Gets the format string for chat log timestamps, either system locale or custom.
255      *
256      * Depending on useCustomTimestampFormat(), this will return either the system locale based
257      * time format, or the custom user-specified string.
258      *
259      * @return String representing timestamp format, e.g. "[hh:mm:ss]" or " hh:mm:ss"
260      */
261     static QString timestampFormatString();
262
263     QTextCharFormat format(const Format &format, MessageLabel messageLabel) const;
264     QFontMetricsF *fontMetrics(FormatType formatType, MessageLabel messageLabel) const;
265
266     QList<QTextLayout::FormatRange> toTextLayoutList(const FormatList &, int textLength, MessageLabel messageLabel) const;
267
268     inline const QBrush &brush(ColorRole role) const { return _uiStylePalette.at((int)role); }
269     inline void setBrush(ColorRole role, const QBrush &brush) { _uiStylePalette[(int)role] = brush; }
270
271     QVariant bufferViewItemData(const QModelIndex &networkModelIndex, int role) const;
272     QVariant nickViewItemData(const QModelIndex &networkModelIndex, int role) const;
273
274 public slots:
275     void reload();
276
277 signals:
278     void changed();
279
280 protected:
281     void loadStyleSheet();
282     QString loadStyleSheet(const QString &name, bool shouldExist = false);
283
284     QTextCharFormat parsedFormat(quint64 key) const;
285     QTextCharFormat cachedFormat(const Format &format, MessageLabel messageLabel) const;
286     void setCachedFormat(const QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
287     void mergeFormat(QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
288     void mergeSubElementFormat(QTextCharFormat &charFormat, FormatType formatType, MessageLabel messageLabel) const;
289     void mergeColors(QTextCharFormat &charFormat, const Format &format, MessageLabel messageLabel) const;
290
291     static FormatType formatType(const QString &code);
292     static QString formatCode(FormatType);
293
294     /**
295      * Cache the system locale timestamp format string
296      *
297      * Based on whether or not AM/PM designators are used in the QLocale::system().timeFormat(),
298      * this extends the system locale timestamp format string to include seconds.
299      *
300      * @see UiStyle::systemTimestampFormatString()
301      */
302     static void updateSystemTimestampFormat();
303
304     /**
305      * Updates the local setting cache of whether or not to use the custom timestamp format
306      *
307      * @param[in] enabled  If true, custom timestamp format used, otherwise false
308      */
309     static void setUseCustomTimestampFormat(bool enabled);
310
311     /**
312      * Updates the local setting cache of the timestamp format string
313      *
314      * @param[in] format   Timestamp format string
315      */
316     static void setTimestampFormatString(const QString &format);
317     /**
318      * Updates the local setting cache of how to display sender prefix modes
319      *
320      * @param[in] mode  Display format for sender prefix modes
321      */
322     static void setSenderPrefixDisplay(UiStyle::SenderPrefixMode mode);
323
324     /**
325      * Updates the local setting cache of whether or not to show sender brackets
326      *
327      * @param[in] enabled  If true, sender brackets are enabled, otherwise false.
328      */
329     static void enableSenderBrackets(bool enabled);
330
331     QVariant itemData(int role, const QTextCharFormat &format) const;
332
333 private slots:
334     void allowMircColorsChanged(const QVariant &);
335     void showItemViewIconsChanged(const QVariant &);
336
337 private:
338     QVector<QBrush> _uiStylePalette;
339     QBrush _markerLineBrush;
340     QHash<quint64, QTextCharFormat> _formats;
341     mutable QHash<QString, QTextCharFormat> _formatCache;
342     mutable QHash<quint64, QFontMetricsF *> _metricsCache;
343     QHash<UiStyle::ItemFormatType, QTextCharFormat> _listItemFormats;
344     static QHash<QString, FormatType> _formatCodes;
345     static bool _useCustomTimestampFormat;        /// If true, use the custom timestamp format
346     static QString _systemTimestampFormatString;  /// Cached copy of system locale timestamp format
347     static QString _timestampFormatString;        /// Timestamp format string
348     static UiStyle::SenderPrefixMode _senderPrefixDisplay; /// Display of prefix modes before sender
349     static bool _showSenderBrackets;              /// If true, show brackets around sender names
350
351     QIcon _channelJoinedIcon;
352     QIcon _channelPartedIcon;
353     QIcon _userOfflineIcon;
354     QIcon _userOnlineIcon;
355     QIcon _userAwayIcon;
356     QIcon _categoryOpIcon;
357     QIcon _categoryVoiceIcon;
358     int _opIconLimit;
359     int _voiceIconLimit;
360     bool _showNickViewIcons;
361     bool _showBufferViewIcons;
362     bool _allowMircColors;
363 };
364
365
366 class UiStyle::StyledMessage : public Message
367 {
368     Q_DECLARE_TR_FUNCTIONS(UiStyle::StyledMessage)
369
370 public:
371     explicit StyledMessage(const Message &message);
372
373     QString decoratedTimestamp() const;
374     QString plainSender() const;           //!< Nickname (no decorations) for Plain and Notice, empty else
375     QString decoratedSender() const;
376     const QString &plainContents() const;
377
378     const FormatList &contentsFormatList() const;
379
380     quint8 senderHash() const;
381
382 protected:
383     void style() const;
384
385 private:
386     mutable StyledString _contents;
387     mutable quint8 _senderHash;
388 };
389
390 #if QT_VERSION < 0x050000
391 uint qHash(UiStyle::ItemFormatType key);
392 #else
393 uint qHash(UiStyle::ItemFormatType key, uint seed);
394 #endif
395
396 // ---- Operators for dealing with enums ----------------------------------------------------------
397
398 UiStyle::FormatType operator|(UiStyle::FormatType lhs, UiStyle::FormatType rhs);
399 UiStyle::FormatType& operator|=(UiStyle::FormatType &lhs, UiStyle::FormatType rhs);
400 UiStyle::FormatType operator|(UiStyle::FormatType lhs, quint32 rhs);
401 UiStyle::FormatType& operator|=(UiStyle::FormatType &lhs, quint32 rhs);
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
408 UiStyle::MessageLabel operator|(UiStyle::MessageLabel lhs, UiStyle::MessageLabel rhs);
409 UiStyle::MessageLabel& operator|=(UiStyle::MessageLabel &lhs, UiStyle::MessageLabel rhs);
410 UiStyle::MessageLabel operator&(UiStyle::MessageLabel lhs, quint32 rhs);
411 UiStyle::MessageLabel& operator&=(UiStyle::MessageLabel &lhs, UiStyle::MessageLabel rhs);
412
413 // Shifts the label into the upper half of the return value
414 quint64 operator|(UiStyle::FormatType lhs, UiStyle::MessageLabel rhs);
415
416 UiStyle::ItemFormatType operator|(UiStyle::ItemFormatType lhs, UiStyle::ItemFormatType rhs);
417 UiStyle::ItemFormatType& operator|=(UiStyle::ItemFormatType &lhs, UiStyle::ItemFormatType rhs);
418
419 // ---- Allow for FormatList in QVariant ----------------------------------------------------------
420
421 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList);
422 QDataStream &operator>>(QDataStream &in, UiStyle::FormatList &formatList);
423
424 Q_DECLARE_METATYPE(UiStyle::FormatList)
425 Q_DECLARE_METATYPE(UiStyle::MessageLabel)
426 Q_DECLARE_METATYPE(UiStyle::SenderPrefixMode)