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