Adding work-in-progress for the new QGraphicsScene-based chatview.
[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("black"));
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
36   // Now initialize the mapping between FormatCodes and FormatTypes...
37   _formatCodes["%O"] = None;
38   _formatCodes["%B"] = Bold;
39   _formatCodes["%S"] = Italic;
40   _formatCodes["%U"] = Underline;
41   _formatCodes["%R"] = Reverse;
42
43   _formatCodes["%D0"] = PlainMsg;
44   _formatCodes["%Dn"] = NoticeMsg;
45   _formatCodes["%Ds"] = ServerMsg;
46   _formatCodes["%De"] = ErrorMsg;
47   _formatCodes["%Dj"] = JoinMsg;
48   _formatCodes["%Dp"] = PartMsg;
49   _formatCodes["%Dq"] = QuitMsg;
50   _formatCodes["%Dk"] = KickMsg;
51   _formatCodes["%Dr"] = RenameMsg;
52   _formatCodes["%Dm"] = ModeMsg;
53   _formatCodes["%Da"] = ActionMsg;
54
55   _formatCodes["%DT"] = Timestamp;
56   _formatCodes["%DS"] = Sender;
57   _formatCodes["%DN"] = Nick;
58   _formatCodes["%DH"] = Hostmask;
59   _formatCodes["%DC"] = ChannelName;
60   _formatCodes["%DM"] = ModeFlags;
61   _formatCodes["%DU"] = Url;
62
63   // Set color formats
64   for(int i = 0; i < 16; i++) {
65     QString idx = QString("%1").arg(i, (int)2, (int)10, (QChar)'0');
66     _formatCodes[QString("%Dcf%1").arg(idx)] = (FormatType)(FgCol00 + i);
67     _formatCodes[QString("%Dcb%1").arg(idx)] = (FormatType)(BgCol00 + i);
68     QTextCharFormat fgf, bgf;
69     fgf.setForeground(QBrush(QColor(colors[i]))); _formats[FgCol00 + i] = fgf;
70     bgf.setBackground(QBrush(QColor(colors[i]))); _formats[BgCol00 + i] = bgf;
71   }
72
73   // Set a few more standard formats
74   QTextCharFormat bold; bold.setFontWeight(QFont::Bold);
75   setFormat(Bold, bold);
76
77   QTextCharFormat italic; italic.setFontItalic(true);
78   setFormat(Italic, italic);
79
80   QTextCharFormat underline; underline.setFontUnderline(true);
81   setFormat(Underline, underline);
82
83   // All other formats should be defined in derived classes.
84 }
85
86 UiStyle::~ UiStyle() {
87   
88 }
89
90 void UiStyle::setFormat(FormatType ftype, QTextCharFormat fmt) {
91   _formats[ftype] = fmt;
92 }
93
94 QTextCharFormat UiStyle::format(FormatType ftype) const {
95   return _formats[ftype];
96 }
97
98 UiStyle::FormatType UiStyle::formatType(const QString & code) const {
99   if(_formatCodes.contains(code)) return _formatCodes.value(code);
100   return Invalid;
101 }
102
103 QString UiStyle::formatCode(FormatType ftype) const {
104   return _formatCodes.key(ftype);
105 }
106
107 UiStyle::StyledText UiStyle::styleString(QString s) {
108   StyledText result;
109   QList<FormatType> fmtList;
110   fmtList.append(None);
111   QTextLayout::FormatRange curFmtRng;
112   curFmtRng.format = format(None);
113   result.formats.append(curFmtRng);
114   int pos = 0; int length;
115   int fgCol = -1, bgCol = -1;  // marks current mIRC color
116   for(;;) {
117     pos = s.indexOf('%', pos);
118     if(pos < 0) break;
119     if(s[pos+1] == '%') { // escaped %, just remove one and continue
120       s.remove(pos, 1);
121       pos++;
122       continue;
123     } else if(s[pos+1] == 'D' && s[pos+2] == 'c') { // color code
124       if(s[pos+3] == '-') { // color off
125         if(fgCol >= 0) {
126           fmtList.removeAll((FormatType)(FgCol00 + fgCol));
127           fgCol = -1;
128         }
129         if(bgCol >= 0) {
130           fmtList.removeAll((FormatType)(BgCol00 + bgCol));
131           bgCol = -1;
132         }
133         curFmtRng.format = mergedFormat(fmtList);
134         length = 4;
135       } else {
136         int color = 10 * s[pos+4].digitValue() + s[pos+5].digitValue();
137         int *colptr; FormatType coltype;
138         if(s[pos+3] == 'f') { // foreground
139           colptr = &fgCol; coltype = FgCol00;
140         } else {              // background
141           Q_ASSERT(s[pos+3] == 'b');
142           colptr = &bgCol; coltype = BgCol00;
143         }
144         if(*colptr >= 0) {
145           // color already set, remove format code and add new one
146           Q_ASSERT(fmtList.contains((FormatType)(coltype + *colptr)));
147           fmtList.removeAll((FormatType)(coltype + *colptr));
148           fmtList.append((FormatType)(coltype + color));
149           curFmtRng.format = mergedFormat(fmtList);
150         } else {
151           fmtList.append((FormatType)(coltype + color));
152           curFmtRng.format.merge(format(fmtList.last()));
153         }
154         *colptr = color;
155         length = 6;
156       }
157     } else if(s[pos+1] == 'O') { // reset formatting
158       fmtList.clear(); fmtList.append(None);
159       curFmtRng.format = format(None);
160       fgCol = bgCol = -1;
161       length = 1;
162     } else if(s[pos+1] == 'R') { // reverse
163       // TODO: implement reverse formatting
164
165       length = 1;
166     } else { // all others are toggles
167       QString code = QString("%") + s[pos+1];
168       if(s[pos+1] == 'D') code += s[pos+2];
169       FormatType ftype = formatType(code);
170       Q_ASSERT(ftype != Invalid);
171       length = code.length();
172       if(!fmtList.contains(ftype)) {
173         // toggle it on
174         fmtList.append(ftype);
175         curFmtRng.format.merge(format(ftype));
176       } else {
177         // toggle it off
178         fmtList.removeAll(ftype);
179         curFmtRng.format = mergedFormat(fmtList);
180       }
181     }
182     s.remove(pos, length); // remove format code from string
183     // now see if something changed and else insert the format
184     if(curFmtRng.format == result.formats.last().format) continue;  // no change, so we just ignore
185     curFmtRng.start = pos;
186     if(pos == result.formats.last().start) {
187       // same starting point -> we just overwrite the old format
188       result.formats.last() = curFmtRng;
189     } else {
190       // fix length of last format
191       result.formats.last().length = pos - result.formats.last().start;
192       result.formats.append(curFmtRng);
193     }
194   }
195   result.formats.last().length = s.length() - result.formats.last().start;
196   if(result.formats.last().length == 0) result.formats.removeLast();
197   result.text = s;
198   return result;
199 }
200
201 QTextCharFormat UiStyle::mergedFormat(QList<FormatType> formatList) {
202   QTextCharFormat fmt;
203   foreach(FormatType ftype, formatList) {
204     fmt.merge(format(ftype));
205   }
206   return fmt;
207 }
208
209