Temporarily fixed the bug where the backlog would be displayed multiple times.
[quassel.git] / src / uisupport / uistyle.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 by the Quassel IRC Team                         *
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) any later version.                                   *
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  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include "uistyle.h"
22
23 UiStyle::UiStyle() {
24   // Default format
25   QTextCharFormat def;
26   def.setForeground(QBrush("#000000"));
27   def.setFont(QFont("Verdana",9));
28
29   _formats = QVector<QTextCharFormat>(NumFormatTypes, def);
30
31   // Initialize color codes according to mIRC "standard"
32   QStringList colors;
33   //colors << "white" << "black" << "navy" << "green" << "red" << "maroon" << "purple" << "orange";
34   //colors << "yellow" << "lime" << "teal" << "aqua" << "royalblue" << "fuchsia" << "grey" << "silver";
35   colors << "#ffffff" << "#000000" << "#000080" << "#008000" << "#ff0000" << "#800000" << "#800080" << "#ffa500";
36   colors << "#ffff00" << "#00ff00" << "#008080" << "#00ffff" << "#4169E1" << "#ff00ff" << "#808080" << "#c0c0c0";
37
38   // Now initialize the mapping between FormatCodes and FormatTypes...
39   _formatCodes["%O"] = None;
40   _formatCodes["%B"] = Bold;
41   _formatCodes["%S"] = Italic;
42   _formatCodes["%U"] = Underline;
43   _formatCodes["%R"] = Reverse;
44
45   _formatCodes["%D0"] = PlainMsg;
46   _formatCodes["%Dn"] = NoticeMsg;
47   _formatCodes["%Ds"] = ServerMsg;
48   _formatCodes["%De"] = ErrorMsg;
49   _formatCodes["%Dj"] = JoinMsg;
50   _formatCodes["%Dp"] = PartMsg;
51   _formatCodes["%Dq"] = QuitMsg;
52   _formatCodes["%Dk"] = KickMsg;
53   _formatCodes["%Dr"] = RenameMsg;
54   _formatCodes["%Dm"] = ModeMsg;
55   _formatCodes["%Da"] = ActionMsg;
56
57   _formatCodes["%DT"] = Timestamp;
58   _formatCodes["%DS"] = Sender;
59   _formatCodes["%DN"] = Nick;
60   _formatCodes["%DH"] = Hostmask;
61   _formatCodes["%DC"] = ChannelName;
62   _formatCodes["%DM"] = ModeFlags;
63   _formatCodes["%DU"] = Url;
64
65   // Set color formats
66   for(int i = 0; i < 16; i++) {
67     QString idx = QString("%1").arg(i, (int)2, (int)10, (QChar)'0');
68     _formatCodes[QString("%Dcf%1").arg(idx)] = (FormatType)(FgCol00 + i);
69     _formatCodes[QString("%Dcb%1").arg(idx)] = (FormatType)(BgCol00 + i);
70     QTextCharFormat fgf, bgf;
71     fgf.setForeground(QBrush(QColor(colors[i]))); _formats[FgCol00 + i] = fgf;
72     bgf.setBackground(QBrush(QColor(colors[i]))); _formats[BgCol00 + i] = bgf;
73   }
74
75   // Set a few more standard formats
76   QTextCharFormat bold; bold.setFontWeight(QFont::Bold);
77   setFormat(Bold, bold);
78
79   QTextCharFormat italic; italic.setFontItalic(true);
80   setFormat(Italic, italic);
81
82   QTextCharFormat underline; underline.setFontUnderline(true);
83   setFormat(Underline, underline);
84
85   // All other formats should be defined in derived classes.
86 }
87
88 UiStyle::~ UiStyle() {
89   
90 }
91
92 void UiStyle::setFormat(FormatType ftype, QTextCharFormat fmt) {
93   _formats[ftype] = fmt;
94 }
95
96 QTextCharFormat UiStyle::format(FormatType ftype) const {
97   return _formats[ftype];
98 }
99
100 UiStyle::FormatType UiStyle::formatType(const QString & code) const {
101   if(_formatCodes.contains(code)) return _formatCodes.value(code);
102   return Invalid;
103 }
104
105 QString UiStyle::formatCode(FormatType ftype) const {
106   return _formatCodes.key(ftype);
107 }
108
109 UiStyle::StyledText UiStyle::styleString(QString s) {
110   StyledText result;
111   QList<FormatType> fmtList;
112   fmtList.append(None);
113   QTextLayout::FormatRange curFmtRng;
114   curFmtRng.format = format(None);
115   result.formats.append(curFmtRng);
116   int pos = 0; int length;
117   int fgCol = -1, bgCol = -1;  // marks current mIRC color
118   for(;;) {
119     pos = s.indexOf('%', pos);
120     if(pos < 0) break;
121     if(s[pos+1] == '%') { // escaped %, just remove one and continue
122       s.remove(pos, 1);
123       pos++;
124       continue;
125     } else if(s[pos+1] == 'D' && s[pos+2] == 'c') { // color code
126       if(s[pos+3] == '-') { // color off
127         if(fgCol >= 0) {
128           fmtList.removeAll((FormatType)(FgCol00 + fgCol));
129           fgCol = -1;
130         }
131         if(bgCol >= 0) {
132           fmtList.removeAll((FormatType)(BgCol00 + bgCol));
133           bgCol = -1;
134         }
135         curFmtRng.format = mergedFormat(fmtList);
136         length = 4;
137       } else {
138         int color = 10 * s[pos+4].digitValue() + s[pos+5].digitValue();
139         int *colptr; FormatType coltype;
140         if(s[pos+3] == 'f') { // foreground
141           colptr = &fgCol; coltype = FgCol00;
142         } else {              // background
143           Q_ASSERT(s[pos+3] == 'b');
144           colptr = &bgCol; coltype = BgCol00;
145         }
146         if(*colptr >= 0) {
147           // color already set, remove format code and add new one
148           Q_ASSERT(fmtList.contains((FormatType)(coltype + *colptr)));
149           fmtList.removeAll((FormatType)(coltype + *colptr));
150           fmtList.append((FormatType)(coltype + color));
151           curFmtRng.format = mergedFormat(fmtList);
152         } else {
153           fmtList.append((FormatType)(coltype + color));
154           curFmtRng.format.merge(format(fmtList.last()));
155         }
156         *colptr = color;
157         length = 6;
158       }
159     } else if(s[pos+1] == 'O') { // reset formatting
160       fmtList.clear(); fmtList.append(None);
161       curFmtRng.format = format(None);
162       fgCol = bgCol = -1;
163       length = 1;
164     } else if(s[pos+1] == 'R') { // reverse
165       // TODO: implement reverse formatting
166
167       length = 1;
168     } else { // all others are toggles
169       QString code = QString("%") + s[pos+1];
170       if(s[pos+1] == 'D') code += s[pos+2];
171       FormatType ftype = formatType(code);
172       Q_ASSERT(ftype != Invalid);
173       length = code.length();
174       if(!fmtList.contains(ftype)) {
175         // toggle it on
176         fmtList.append(ftype);
177         curFmtRng.format.merge(format(ftype));
178       } else {
179         // toggle it off
180         fmtList.removeAll(ftype);
181         curFmtRng.format = mergedFormat(fmtList);
182       }
183     }
184     s.remove(pos, length); // remove format code from string
185     // now see if something changed and else insert the format
186     if(curFmtRng.format == result.formats.last().format) continue;  // no change, so we just ignore
187     curFmtRng.start = pos;
188     if(pos == result.formats.last().start) {
189       // same starting point -> we just overwrite the old format
190       result.formats.last() = curFmtRng;
191     } else {
192       // fix length of last format
193       result.formats.last().length = pos - result.formats.last().start;
194       result.formats.append(curFmtRng);
195     }
196   }
197   result.formats.last().length = s.length() - result.formats.last().start;
198   if(result.formats.last().length == 0) result.formats.removeLast();
199   result.text = s;
200   return result;
201 }
202
203 QTextCharFormat UiStyle::mergedFormat(QList<FormatType> formatList) {
204   QTextCharFormat fmt;
205   foreach(FormatType ftype, formatList) {
206     fmt.merge(format(ftype));
207   }
208   return fmt;
209 }
210
211