Some bugs fixed
[qstardict] / qstardict / cssedit.cpp
1 /*****************************************************************************
2  * cssedit.cpp - QStarDict, a StarDict clone written with using Qt           *
3  * Copyright (C) 2008 Alexander Rodin                                        *
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 along   *
16  * with this program; if not, write to the Free Software Foundation, Inc.,   *
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.               *
18  *****************************************************************************/
19
20 #include "cssedit.h"
21
22 #include <QApplication>
23 #include <QColorDialog>
24
25 namespace QStarDict
26 {
27 CSSEdit::CSSEdit(QWidget *parent)
28     : QWidget(parent)
29 {
30     setupUi(this);
31     //m_preview->setProperty("FingerScrollable", true);
32
33     connect(m_elementCombo, SIGNAL(currentIndexChanged(int)), SLOT(setCurrentElement(int)));
34     connect(m_fontCombo, SIGNAL(currentFontChanged(const QFont&)), SLOT(propertyChanged()));
35     connect(m_sizeSpin, SIGNAL(valueChanged(int)), SLOT(propertyChanged()));
36     connect(m_boldButton, SIGNAL(toggled(bool)), SLOT(propertyChanged()));
37     connect(m_italicButton, SIGNAL(toggled(bool)), SLOT(propertyChanged()));
38     connect(m_underlineButton, SIGNAL(toggled(bool)), SLOT(propertyChanged()));
39     connect(m_colorButton, SIGNAL(clicked()), SLOT(colorSelectClicked()));
40     connect(m_backgroundButton, SIGNAL(clicked()), SLOT(colorSelectClicked()));
41 }
42
43 void CSSEdit::setCSS(const QString &css)
44 {
45     m_elements.clear();
46     bool inBlock = false;
47     QString element;
48     QString currentProperty;
49     QString currentValue;
50     for (int i = 0; i < css.length(); ++i)
51     {
52         if (! inBlock)
53         {
54             element.clear();
55             for (; i < css.length() && css[i] != '{'; ++i)
56                 if (! css[i].isSpace())
57                     element += css[i];
58             if (i < css.length() && css[i] == '{')
59             {
60                 inBlock = true;
61                 ++i;
62             }
63         }
64         else
65         {
66             currentProperty.clear();
67             for (; i < css.length() && css[i] != ':'; ++i)
68                 if (! css[i].isSpace())
69                     currentProperty += css[i];
70             if (! inBlock)
71                 continue;
72             currentValue.clear();
73             char quote = '\0';
74             for (++i; i < css.length() && css[i] != ';'; ++i)
75             {
76                 if (quote)
77                 {
78                     if (css[i] == quote)
79                     {
80                         while (i < css.length() && css[i] != ';')
81                             ++i;
82                         break;
83                     }
84                     else
85                         currentValue += css[i];
86                 }
87                 else if (! css[i].isSpace())
88                 {
89                     if (css[i] == '\'' || css[i] == '\"')
90                         quote = css[i].toAscii();
91                     else
92                         currentValue += css[i];
93                 }
94             }
95             m_elements[element][currentProperty] = currentValue;
96             while (css[i + 1].isSpace())
97                 ++i;
98             if (css[i + 1] == '}')
99             {
100                 ++i;
101                 inBlock = false;
102             }
103         }
104     }
105
106     updateElementCombo();
107     updatePreview();
108 }
109
110 void CSSEdit::setElementsAliases(const QHash<QString, QString> &aliases)
111 {
112     m_elementsAliases = aliases;
113     updateElementCombo();
114     updatePreview();
115 }
116
117 QString CSSEdit::css() const
118 {
119     QString result;
120     for (QHash<QString, Element>::const_iterator i = m_elements.begin(); i != m_elements.end(); ++i)
121     {
122         result += i.key() + "\n{\n";
123         for (Element::const_iterator j = i->begin(); j != i->end(); ++j)
124         {
125             result += j.key() + ": ";
126             if (j->contains(' '))
127                 result += "\"" + *j + "\";\n";
128             else
129                 result += *j + ";\n";
130         }
131         result += "}\n";
132     }
133     return result;
134 }
135
136 void CSSEdit::setCurrentElement(int index)
137 {
138     if (! m_elements.contains(m_elementCombo->itemData(index).toString()))
139         return;
140     m_currentElement = m_elementCombo->itemData(index).toString();
141     Element *element = &m_elements[m_currentElement];
142     Element parentElement = getParentElement(m_currentElement);
143
144     QColor color(element->contains("color") ? element->value("color") : parentElement["color"]);
145     m_colorButton->setText(color.name());
146     QPalette palette = m_colorButton->palette();
147     palette.setColor(QPalette::Normal, QPalette::ButtonText, color);
148     m_colorButton->setPalette(palette);
149
150     color = QColor(element->contains("background-color") ?
151         element->value("background-color") : parentElement["background-color"]);
152     m_backgroundButton->setText(color.name());
153     palette = m_backgroundButton->palette();
154     palette.setColor(QPalette::Normal, QPalette::ButtonText, color);
155     m_backgroundButton->setPalette(palette);
156
157     m_fontCombo->setCurrentFont(element->contains("font-family") ?
158             element->value("font-family") : parentElement["font-family"]);
159
160     QString value = element->contains("font-size") ?
161         element->value("font-size") : parentElement["font-size"];
162     if (! value.endsWith("pt"))
163         value = parentElement["font-size"];
164     m_sizeSpin->setValue(value.left(value.length() - 2).toInt());
165
166     value = element->contains("font-weight") ? element->value("font-weight") : parentElement["font-weight"];
167     m_boldButton->setChecked(value == "bold");
168
169     value = element->contains("font-style") ? element->value("font-style") : parentElement["font-style"];
170     m_italicButton->setChecked(value == "italic");
171
172     value = element->contains("text-decoration") ? element->value("text-decoration") : parentElement["text-decoration"];
173     m_underlineButton->setChecked(value == "underline");
174
175     updatePreview();
176 }
177
178 void CSSEdit::propertyChanged()
179 {
180     if (! sender())
181         return;
182     if (! m_elements.contains(m_currentElement))
183         return;
184     Element *element = &m_elements[m_currentElement];
185     Element parentElement = getParentElement(m_currentElement);
186
187     if (sender() == m_fontCombo)
188     {
189         QString font = m_fontCombo->currentText();
190         if (parentElement["font-family"] == font)
191             element->remove("font-family");
192         else
193             element->insert("font-family", font);
194     }
195     else if (sender() == m_sizeSpin)
196     {
197         QString size = QString::number(m_sizeSpin->value()) + "pt";
198         if (parentElement["font-size"] == size)
199             element->remove("font-size");
200         else
201             element->insert("font-size", size);
202     }
203     else if (sender() == m_boldButton)
204     {
205         QString weight = (m_boldButton->isChecked() ? "bold" : "normal");
206         if (parentElement["font-weight"] == weight)
207             element->remove("font-weight");
208         else
209             element->insert("font-weight", weight);
210     }
211     else if(sender() == m_italicButton)
212     {
213         QString style = (m_italicButton->isChecked() ? "italic" : "normal");
214         if (parentElement["font-style"] == style)
215             element->remove("font-style");
216         else
217             element->insert("font-style", style);
218     }
219     else if(sender() == m_underlineButton)
220     {
221         QString decoration = (m_underlineButton->isChecked() ? "underline" : "none");
222         if (parentElement["text-decoration"] == decoration)
223             element->remove("text-decoration");
224         else
225             element->insert("text-decoration", decoration);
226     }
227
228     updatePreview();
229 }
230
231 void CSSEdit::colorSelectClicked()
232 {
233     QToolButton *colorButton;
234     QString propertyName;
235     if (sender() == m_colorButton)
236     {
237         colorButton = m_colorButton;
238         propertyName = "color";
239     }
240     else if (sender() == m_backgroundButton)
241     {
242         colorButton = m_backgroundButton;
243         propertyName = "background-color";
244     }
245     else
246         return;
247
248     if (! m_elements.contains(m_currentElement))
249         return;
250     Element *element = &m_elements[m_currentElement];
251     Element parentElement = getParentElement(m_currentElement);
252     QColor color = QColorDialog::getColor(QColor(colorButton->text()), this);
253     if (color.isValid())
254     {
255         colorButton->setText(color.name());
256         QPalette palette = colorButton->palette();
257         palette.setColor(QPalette::Normal, QPalette::ButtonText, color);
258         colorButton->setPalette(palette);
259         if (parentElement[propertyName] == color.name())
260             element->remove(propertyName);
261         else
262             element->insert(propertyName, color.name());
263         updatePreview();
264     }
265 }
266
267 void CSSEdit::updatePreview()
268 {
269     QString html = "<style>" +  css()  +  "</style>";
270     html += "<body>";
271     for (QHash<QString, Element>::const_iterator i = m_elements.begin(); i != m_elements.end(); ++i)
272     {
273         QString alias;
274         if (m_elementsAliases.contains(i.key()))
275             alias = m_elementsAliases.value(i.key());
276         else
277             alias = i.key();
278         int pos = i.key().indexOf('.');
279         if (pos == -1)
280             html += "<" + i.key() + ">" + alias + "</" + i.key() + "><br>";
281         else
282         {
283             QString parent = i.key().left(pos);
284             QString class_ = i.key().mid(pos + 1);
285             html += "<" + parent + " class=\'" + class_ + "\'>" + alias + "</" + parent + "><br>";
286         }
287     }
288     html += "</body>";
289     m_preview->setHtml(html);
290 }
291
292 void CSSEdit::updateElementCombo()
293 {
294     m_elementCombo->clear();
295     for (QHash<QString, Element>::const_iterator i = m_elements.begin(); i != m_elements.end(); ++i)
296     {
297         QString alias;
298         if (m_elementsAliases.contains(i.key()))
299             alias = m_elementsAliases.value(i.key());
300         else
301             alias = i.key();
302         m_elementCombo->addItem(alias, i.key());
303     }
304     if (m_elements.begin() != m_elements.end())
305         m_currentElement = m_elements.begin().key();
306     else
307         m_currentElement.clear();
308 }
309
310 CSSEdit::Element CSSEdit::getParentElement(const QString &elementName)
311 {
312     Element body = m_elements.value("body");
313
314     if (elementName == "body")
315     {
316         body["color"] = QApplication::palette().color(QPalette::Normal, QPalette::Text).name();
317         body["background-color"] = QApplication::palette().color(QPalette::Normal, QPalette::Base).name();
318         body["font-family"] = QApplication::font().family();
319         body["font-size"] = QString::number(QApplication::font().pointSize()) + "pt";
320         body["font-weight"] = (QApplication::font().weight() == QFont::Bold) ? "bold" : "normal";
321         body["font-style"] = (QApplication::font().style() == QFont::StyleItalic) ? "italic" : "normal";
322         body["text-decoration"] = QApplication::font().underline() ? "underline" : "none";
323         return body;
324     }
325     else
326     {
327         if (! body.contains("color"))
328             body["color"] = QApplication::palette().color(QPalette::Normal, QPalette::Text).name();
329         if (! body.contains("background-color"))
330             body["background-color"] = QApplication::palette().color(QPalette::Normal, QPalette::Base).name();
331         if (! body.contains("font-family"))
332             body["font-family"] = QApplication::font().family();
333         if (! body.contains("font-size"))
334             body["font-size"] = QString::number(QApplication::font().pointSize()) + "pt";
335         if (! body.contains("font-weight"))
336             body["font-weight"] = (QApplication::font().weight() == QFont::Bold) ? "bold" : "normal";
337         if (! body.contains("font-style"))
338             body["font-style"] = (QApplication::font().style() == QFont::StyleItalic) ? "italic" : "normal";
339         if (! body.contains("text-decoration"))
340             body["text-decoration"] = QApplication::font().underline() ? "underline" : "none";
341     }
342
343     QString parentName;
344     int pos = elementName.indexOf('.');
345     if (pos != -1)
346     {
347         parentName = elementName.left(pos);
348         if (m_elements.contains(parentName))
349         {
350             Element parent = m_elements[parentName];
351             for (Element::const_iterator i = body.begin(); i != body.end(); ++i)
352                 if (! parent.contains(i.key()))
353                     parent[i.key()] = *i;
354             return parent;
355         }
356         else
357             return body;
358     }
359     return body;
360 }
361 }
362
363 // vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab cindent textwidth=120 formatoptions=tc
364