Make selections in bufferviews (mostly) synchronous again.
[quassel.git] / gui / style.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 by The Quassel 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 "style.h"
22
23 void Style::init() {
24    // Colors (mIRC standard)
25   colors["00"] = QColor("white");
26   colors["01"] = QColor("black");
27   colors["02"] = QColor("navy");
28   colors["03"] = QColor("green");
29   colors["04"] = QColor("red");
30   colors["05"] = QColor("maroon");
31   colors["06"] = QColor("purple");
32   colors["07"] = QColor("orange");
33   colors["08"] = QColor("yellow");
34   colors["09"] = QColor("lime");
35   colors["10"] = QColor("teal");
36   colors["11"] = QColor("aqua");
37   colors["12"] = QColor("royalblue");
38   colors["13"] = QColor("fuchsia");
39   colors["14"] = QColor("grey");
40   colors["15"] = QColor("silver");
41
42   QTextCharFormat def;
43   def.setForeground(QBrush("black"));
44   def.setFont(QFont("Verdana",9));
45   formats["default"] = def;
46
47   // %B - 0x02 - bold
48   QTextCharFormat bold;
49   bold.setFontWeight(QFont::Bold);
50   formats["%B"] = bold;
51
52   // %O - 0x0f - plain
53   formats["%O"] = def;
54
55   // %R - 0x12 - reverse
56   // -- - 0x16 - reverse
57   // (no format)
58
59   // %S - 0x1d - italic
60   QTextCharFormat italic;
61   italic.setFontItalic(true);
62   formats["%S"] = italic;
63
64   // %U - 0x1f - underline
65   QTextCharFormat underline;
66   underline.setFontUnderline(true);
67   formats["%U"] = underline;
68
69   // %C - 0x03 - mIRC colors
70   for(uint i = 0; i < 16; i++) {
71     QString idx = QString("%1").arg(i, (int)2, (int)10, (QChar)'0');
72     QString fg = QString("%C%1").arg(idx);
73     QString bg = QString("%C,%1").arg(idx);
74     QTextCharFormat fgf; fgf.setForeground(QBrush(colors[idx])); formats[fg] = fgf;
75     QTextCharFormat bgf; bgf.setBackground(QBrush(colors[idx])); formats[bg] = bgf;
76   }
77
78   // Internal formats - %D<char>
79   // %D0 - plain msg
80   QTextCharFormat plainMsg;
81   plainMsg.setForeground(QBrush("black"));
82   formats["%D0"] = plainMsg;
83   // %Dn - notice
84   QTextCharFormat notice;
85   notice.setForeground(QBrush("navy"));
86   formats["%Dn"] = notice;
87   // %Ds - server msg
88   QTextCharFormat server;
89   server.setForeground(QBrush("navy"));
90   formats["%Ds"] = server;
91   // %De - error msg
92   QTextCharFormat error;
93   error.setForeground(QBrush("red"));
94   formats["%De"] = error;
95   // %Dj - join
96   QTextCharFormat join;
97   join.setForeground(QBrush("green"));
98   formats["%Dj"] = join;
99   // %Dp - part
100   QTextCharFormat part;
101   part.setForeground(QBrush("indianred"));
102   formats["%Dp"] = part;
103   // %Dq - quit
104   QTextCharFormat quit;
105   quit.setForeground(QBrush("indianred"));
106   formats["%Dq"] = quit;
107   // %Dk - kick
108   QTextCharFormat kick;
109   kick.setForeground(QBrush("indianred"));
110   formats["%Dk"] = kick;
111   // %Dr - nick rename
112   QTextCharFormat nren;
113   nren.setForeground(QBrush("magenta"));
114   formats["%Dr"] = nren;
115   // %Dm - mode change
116   QTextCharFormat mode;
117   mode.setForeground(QBrush("steelblue"));
118   formats["%Dm"] = mode;
119   // %Da - ctcp action
120   QTextCharFormat action;
121   action.setFontItalic(true);
122   action.setForeground(QBrush("darkmagenta"));
123   formats["%Da"] = action;
124
125   // %DT - timestamp
126   QTextCharFormat ts;
127   ts.setForeground(QBrush("grey"));
128   formats["%DT"] = ts;
129   // %DS - sender
130   QTextCharFormat sender;
131   sender.setAnchor(true);
132   sender.setForeground(QBrush("navy"));
133   formats["%DS"] = sender;
134   // %DN - nickname
135   QTextCharFormat nick;
136   nick.setAnchor(true);
137   nick.setFontWeight(QFont::Bold);
138   formats["%DN"] = nick;
139   // %DH - hostmask
140   QTextCharFormat hostmask;
141   hostmask.setFontItalic(true);
142   formats["%DH"] = hostmask;
143   // %DC - channame
144   QTextCharFormat channel;
145   channel.setAnchor(true);
146   channel.setFontWeight(QFont::Bold);
147   formats["%DC"] = channel;
148   // %DM - modeflags
149   QTextCharFormat flags;
150   flags.setFontWeight(QFont::Bold);
151   formats["%DM"] = flags;
152   // %DU - clickable URL
153   QTextCharFormat url;
154   url.setFontUnderline(true);
155   url.setAnchor(true);
156   formats["%DU"] = url;
157 }
158
159 QString Style::mircToInternal(QString mirc) {
160   mirc.replace('%', "%%");      // escape % just to be sure
161   mirc.replace('\x02', "%B");
162   mirc.replace('\x03', "%C");
163   mirc.replace('\x0f', "%O");
164   mirc.replace('\x12', "%R");
165   mirc.replace('\x16', "%R");
166   mirc.replace('\x1d', "%S");
167   mirc.replace('\x1f', "%U");
168   return mirc;
169 }
170
171 /** Returns a string stripped of format codes, and a list of FormatRange objects
172  *  describing the formats of the string.
173  * \param s string in internal format (% style format codes)
174  */ 
175 Style::FormattedString Style::internalToFormatted(QString s) {
176   QHash<QString, int> toggles;
177   QString p;
178   FormattedString sf;
179   QTextLayout::FormatRange rng;
180   rng.format = formats["default"]; rng.start = 0; rng.length = -1; sf.formats.append(rng);
181   toggles["default"] = sf.formats.count() - 1;
182   int i, j;
183   for(i = 0, j = 0; i < s.length(); i++) {
184     if(s[i] != '%') { p += s[i]; j++; continue; }
185     i++;
186     if(s[i] == '%') { p += '%'; j++; continue; }
187     else if(s[i] == 'C') {
188       if(!s[i+1].isDigit() && s[i+1] != ',') {
189         if(toggles.contains("bg")) {
190           sf.formats[toggles["bg"]].length = j - sf.formats[toggles["bg"]].start;
191           toggles.remove("bg");
192         }
193       }
194       if(s[i+1].isDigit() || s[i+1] != ',') {
195         if(toggles.contains("fg")) {
196           sf.formats[toggles["fg"]].length = j - sf.formats[toggles["fg"]].start;
197           toggles.remove("fg");
198         }
199         if(s[i+1].isDigit()) {
200           QString n(s[++i]);
201           if(s[i+1].isDigit()) n += s[++i];
202           int num = n.toInt() & 0xf;
203           n = QString("%C%1").arg(num, (int)2, (int)10, (QChar)'0');
204           //qDebug() << n << formats[n].foreground();
205           QTextLayout::FormatRange range; 
206           range.format = formats[n]; range.start = j; range.length = -1; sf.formats.append(range);
207           toggles["fg"] = sf.formats.count() - 1;
208         }
209       }
210       if(s[i+1] == ',') {
211         if(toggles.contains("bg")) {
212           sf.formats[toggles["bg"]].length = j - sf.formats[toggles["bg"]].start;
213           toggles.remove("bg");
214         }
215         i++;
216         if(s[i+1].isDigit()) {
217           QString n(s[++i]);
218           if(s[i+1].isDigit()) n += s[++i];
219           int num = n.toInt() & 0xf;
220           n = QString("%C,%1").arg(num, (int)2, (int)10, (QChar)'0');
221           QTextLayout::FormatRange range;
222           range.format = formats[n]; range.start = j; range.length = -1;
223           sf.formats.append(range);
224           toggles["bg"] = sf.formats.count() - 1;
225         }
226       }
227     } else if(s[i] == 'O') {
228       foreach(QString key, toggles.keys()) {
229         if(key == "default") continue;
230         sf.formats[toggles[key]].length = j - sf.formats[toggles[key]].start;
231         toggles.remove(key);
232       }
233
234     } else if(s[i] == 'R') {
235       // TODO implement reverse formatting
236
237     } else {
238       // all others are toggles
239       QString key = "%"; key += s[i];
240       if(s[i] == 'D') key += s[i+1];
241       if(formats.contains(key)) {
242         if(s[i] == 'D') i++;
243         if(toggles.contains(key)) {
244           sf.formats[toggles[key]].length = j - sf.formats[toggles[key]].start;
245           if(key == "%DU") {
246             // URL handling
247             // FIXME check for and handle format codes within URLs
248             QString u = s.mid(i - sf.formats[toggles[key]].length - 2, sf.formats[toggles[key]].length);
249             UrlInfo url;
250             url.start = sf.formats[toggles[key]].start;
251             url.end   = j;
252             url.url = QUrl(u);
253             sf.urls.append(url);
254           }
255           toggles.remove(key);
256         } else {
257           QTextLayout::FormatRange range;
258           range.format = formats[key]; range.start = j; range.length = -1;
259           sf.formats.append(range);
260           toggles[key] = sf.formats.count() -1;
261         }
262       } else {
263         // unknown format
264         p += '%'; p += s[i]; j+=2;
265       }
266     }
267   }
268   foreach(int idx, toggles.values()) {
269     sf.formats[idx].length = j - sf.formats[idx].start;
270   }
271   sf.text = p;
272   return sf;
273 }
274
275 QHash<QString, QTextCharFormat> Style::formats;
276 QHash<QString, QColor> Style::colors;
277