1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 1999-2011 Licq developers
4  *
5  * Licq 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  * Licq 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 Licq; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "skin.h"
21 
22 #include "config.h"
23 
24 #include "core/gui-defines.h"
25 
26 #include <QPainter>
27 #include <QWidget>
28 
29 #include <licq/daemon.h>
30 #include <licq/inifile.h>
31 #include <licq/logging/log.h>
32 
33 using namespace LicqQtGui;
34 /* TRANSLATOR LicqQtGui::Config::Skin */
35 
36 Config::Skin* Config::Skin::myInstance = NULL;
37 
createInstance(const QString & skinName,QObject * parent)38 void Config::Skin::createInstance(const QString& skinName, QObject* parent)
39 {
40   myInstance = new Skin(skinName, parent);
41 }
42 
Skin(const QString & skinName,QObject * parent)43 Config::Skin::Skin(const QString& skinName, QObject* parent)
44   : QObject(parent),
45     mySkinName("\0")
46 {
47   loadSkin(skinName);
48 }
49 
loadSkin(const QString & skinName)50 void Config::Skin::loadSkin(const QString& skinName)
51 {
52   if (skinName == mySkinName)
53     return;
54 
55   Licq::gLog.info("Applying %s skin", skinName.toLocal8Bit().constData());
56 
57   // Set default values even if skin is valid as skin may not include all settings
58   SetDefaultValues();
59 
60   if (skinName.isEmpty())
61   {
62     emit changed();
63     emit frameChanged();
64     return;
65   }
66 
67   QString skinFileName = skinName + ".skin";
68   QString subdir = QString(QTGUI_DIR) + SKINS_DIR + skinName + "/";
69   QString baseSkinDir = QString::fromLocal8Bit(Licq::gDaemon.baseDir().c_str()) + subdir;
70   Licq::IniFile skinFile((baseSkinDir + skinFileName).toLocal8Bit().constData());
71   if (!skinFile.loadFile())
72   {
73     baseSkinDir = QString::fromLocal8Bit(Licq::gDaemon.shareDir().c_str()) + subdir;
74     skinFile.setFilename((baseSkinDir + skinFileName).toLocal8Bit().constData());
75     if (!skinFile.loadFile())
76     {
77       emit changed();
78       emit frameChanged();
79       return;
80     }
81   }
82 
83   mySkinName = skinName;
84 
85   skinFile.setSection("skin", false);
86 
87   // Frame
88   frame.loadSkin(skinFile, "frame", baseSkinDir);
89 
90   // System Button
91   btnSys.loadSkin(skinFile, "btnSys", baseSkinDir);
92 
93   // Status Label
94   lblStatus.loadSkin(skinFile, "lblStatus", baseSkinDir);
95 
96   // Message Label
97   lblMsg.loadSkin(skinFile, "lblMsg", baseSkinDir);
98 
99   // Group Combo Box
100   cmbGroups.loadSkin(skinFile, "cmbGroups");
101 
102   std::string temp;
103 #define GET_COLOR(param, var) \
104   skinFile.get(param, temp, "default"); \
105   if (temp != "default") \
106     var.setNamedColor(temp.c_str());
107 
108   // Read in the colors
109   GET_COLOR("colors.background", backgroundColor);
110   GET_COLOR("colors.gridlines", gridlineColor);
111   GET_COLOR("colors.scrollbar", scrollbarColor);
112   GET_COLOR("colors.btnTxt", buttonTextColor);
113   GET_COLOR("colors.online", onlineColor);
114   GET_COLOR("colors.offline", offlineColor);
115   GET_COLOR("colors.away", awayColor);
116   GET_COLOR("colors.newuser", newUserColor);
117   GET_COLOR("colors.authwait", awaitingAuthColor);
118   GET_COLOR("colors.highlight.bg", highBackColor);
119   GET_COLOR("colors.highlight.fg", highTextColor);
120   GET_COLOR("colors.group.bg", groupBackColor);
121   GET_COLOR("colors.group.fg", groupTextColor);
122   GET_COLOR("colors.group.highlight.bg", groupHighBackColor);
123   GET_COLOR("colors.group.highlight.fg", groupHighTextColor);
124 
125 #undef GET_COLOR
126 
127   // And images
128   skinFile.get("images.groupBack", temp, "none");
129   if (temp != "none")
130     groupBackImage.load(baseSkinDir + QString::fromLocal8Bit(temp.c_str()));
131 
132   skinFile.get("images.groupBack.tile", tileGroupBackImage, false);
133 
134   emit changed();
135   emit frameChanged();
136 }
137 
setFrameTransparent(bool transparent)138 void Config::Skin::setFrameTransparent(bool transparent)
139 {
140   if (transparent == frame.transparent)
141     return;
142 
143   frame.transparent = transparent;
144   emit frameChanged();
145 }
146 
setFrameStyle(unsigned frameStyle)147 void Config::Skin::setFrameStyle(unsigned frameStyle)
148 {
149   if (frameStyle == frame.frameStyle)
150     return;
151 
152   frame.frameStyle = frameStyle;
153   emit frameChanged();
154 }
155 
SetDefaultValues()156 void Config::Skin::SetDefaultValues()
157 {
158   mySkinName = "";
159   myMenuBarHeight = 0;
160 
161   frame.pixmap = QPixmap();
162   frame.mask = QPixmap();
163   frame.border.top = 0;
164   frame.border.bottom = 80;
165   frame.border.left = 0;
166   frame.border.right = 0;
167   frame.hasMenuBar = true;
168   frame.frameStyle = 33;
169   frame.transparent = false;
170 
171   lblStatus.rect.setCoords(5, -25, -5, -5);
172   lblStatus.foreground = QColor();
173   lblStatus.background = QColor();
174   lblStatus.frameStyle = 51;
175   lblStatus.pixmap = QPixmap();
176   lblStatus.margin = 5;
177 
178   btnSys.rect.setCoords(20, -65, 70, -45);
179   btnSys.pixmapUpFocus = QPixmap();
180   btnSys.pixmapUpNoFocus = QPixmap();
181   btnSys.pixmapDown = QPixmap();
182   btnSys.foreground = QColor();
183   btnSys.background = QColor();
184   btnSys.caption = QString();
185 
186   lblMsg.rect.setCoords(5, -50, -5, -30);
187   lblMsg.foreground = QColor();
188   lblMsg.background = QColor();
189   lblMsg.frameStyle = 51;
190   lblMsg.pixmap = QPixmap();
191   lblMsg.margin = 5;
192 
193   cmbGroups.rect.setCoords(5, -75, -5, -55);
194   cmbGroups.foreground = QColor();
195   cmbGroups.background = QColor();
196 
197   backgroundColor = QColor();
198   gridlineColor.setNamedColor("black");
199   scrollbarColor = QColor();
200   buttonTextColor = QColor();
201 
202   onlineColor.setNamedColor("blue");
203   offlineColor.setNamedColor("firebrick");
204   awayColor.setNamedColor("darkgreen");
205   newUserColor.setNamedColor("yellow");
206   awaitingAuthColor.setNamedColor("darkcyan");
207 
208   highBackColor = QColor();
209   highTextColor = QColor();
210 
211   groupBackColor = QColor();
212   groupTextColor = QColor();
213   groupHighBackColor = QColor();
214   groupHighTextColor = QColor();
215 
216   groupBackImage = QImage();
217   tileGroupBackImage = false;
218 }
219 
220 
AdjustForMenuBar(int h)221 void Config::Skin::AdjustForMenuBar(int h)
222 {
223   frame.border.AdjustForMenuBar(myMenuBarHeight, h);
224   lblStatus.AdjustForMenuBar(myMenuBarHeight, h);
225   btnSys.AdjustForMenuBar(myMenuBarHeight, h);
226   lblMsg.AdjustForMenuBar(myMenuBarHeight, h);
227   cmbGroups.AdjustForMenuBar(myMenuBarHeight, h);
228 
229   myMenuBarHeight = h;
230 }
231 
loadSkin(const Licq::IniFile & skinFile,const QString & name,const QString & baseSkinDir)232 void Config::FrameSkin::loadSkin(const Licq::IniFile& skinFile,
233     const QString& name, const QString& baseSkinDir)
234 {
235   std::string temp;
236   skinFile.get((name + ".pixmap").toLatin1().constData(), temp, "none");
237   if (temp != "none")
238     if (!pixmap.load(baseSkinDir + QString::fromLocal8Bit(temp.c_str())))
239       Licq::gLog.error("Error loading background pixmap (%s)", temp.c_str());
240 
241   skinFile.get((name + ".mask").toLatin1().constData(), temp, "none");
242   if (temp != "none")
243     if (!mask.load(baseSkinDir + QString::fromLocal8Bit(temp.c_str())))
244       Licq::gLog.error("Error loading background mask (%s)", temp.c_str());
245 
246   skinFile.get((name + ".border.top").toLatin1().constData(), border.top);
247   skinFile.get((name + ".border.bottom").toLatin1().constData(), border.bottom);
248   skinFile.get((name + ".border.left").toLatin1().constData(), border.left);
249   skinFile.get((name + ".border.right").toLatin1().constData(), border.right);
250   skinFile.get((name + ".hasMenuBar").toLatin1().constData(), hasMenuBar, hasMenuBar);
251   skinFile.get((name + ".frameStyle").toLatin1().constData(), frameStyle, frameStyle);
252   skinFile.get((name + ".transparent").toLatin1().constData(), transparent, transparent);
253 }
254 
loadSkin(const Licq::IniFile & skinFile,const QString & name)255 void Config::ShapeSkin::loadSkin(const Licq::IniFile& skinFile, const QString& name)
256 {
257   int x1, y1, x2, y2;
258   skinFile.get((name + ".rect.x1").toLatin1().constData(), x1);
259   skinFile.get((name + ".rect.y1").toLatin1().constData(), y1);
260   skinFile.get((name + ".rect.x2").toLatin1().constData(), x2);
261   skinFile.get((name + ".rect.y2").toLatin1().constData(), y2);
262   rect.setCoords(x1, y1, x2, y2);
263 
264   std::string temp;
265   skinFile.get((name + ".color.fg").toLatin1().constData(), temp, "default");
266   foreground = (temp == "default" ? QColor() : QColor(temp.c_str()));
267   if (temp == "transparent")
268     foreground.setAlpha(0);
269   skinFile.get((name + ".color.bg").toLatin1().constData(), temp, "default");
270   background = (temp == "default" ? QColor() : QColor(temp.c_str()));
271   if (temp == "transparent")
272     background.setAlpha(0);
273 }
274 
loadSkin(const Licq::IniFile & skinFile,const QString & name,const QString & baseSkinDir)275 void Config::ButtonSkin::loadSkin(const Licq::IniFile& skinFile, const QString& name, const QString& baseSkinDir)
276 {
277   Config::ShapeSkin::loadSkin(skinFile, name);
278 
279   std::string temp;
280   skinFile.get((name + ".caption").toLatin1().constData(), temp, "default");
281   caption = (temp == "default" ? QString() : QString::fromLocal8Bit(temp.c_str()));
282 
283   skinFile.get((name + ".pixmapUpFocus").toLatin1().constData(), temp, "none");
284   if (temp != "none")
285     pixmapUpFocus.load(baseSkinDir + QString::fromLocal8Bit(temp.c_str()));
286 
287   skinFile.get((name + ".pixmapUpNoFocus").toLatin1().constData(), temp, "none");
288   if (temp != "none")
289     pixmapUpNoFocus.load(baseSkinDir + QString::fromLocal8Bit(temp.c_str()));
290 
291   skinFile.get((name + ".pixmapDown").toLatin1().constData(), temp, "none");
292   if (temp != "none")
293     pixmapDown.load(baseSkinDir + QString::fromLocal8Bit(temp.c_str()));
294 }
295 
loadSkin(const Licq::IniFile & skinFile,const QString & name,const QString & baseSkinDir)296 void Config::LabelSkin::loadSkin(const Licq::IniFile& skinFile, const QString& name, const QString& baseSkinDir)
297 {
298   Config::ShapeSkin::loadSkin(skinFile, name);
299   transparent = (background.alpha() == 0);
300 
301   std::string temp;
302   skinFile.get((name + ".pixmap").toLatin1().constData(), temp, "none");
303   if (temp != "none")
304     pixmap.load(baseSkinDir + QString::fromLocal8Bit(temp.c_str()));
305 
306   skinFile.get((name + ".margin").toLatin1().constData(), margin, margin);
307   skinFile.get((name + ".frameStyle").toLatin1().constData(), frameStyle, frameStyle);
308 }
309 
AdjustForMenuBar(int h_old,int h_new)310 void Config::ShapeSkin::AdjustForMenuBar(int h_old, int h_new)
311 {
312   if (rect.top() >= 0)
313     rect.setTop(rect.top() + h_new - h_old);
314   if (rect.bottom() >= 0)
315     rect.setBottom(rect.bottom() + h_new - h_old);
316 }
317 
AdjustForMenuBar(int h_old,int h_new)318 void Config::Border::AdjustForMenuBar(int h_old, int h_new)
319 {
320   top += (h_new - h_old);
321 }
322 
323 
frameWidth()324 int Config::Skin::frameWidth()
325 {
326    return (frame.border.right + frame.border.left);
327 }
328 
frameHeight()329 int Config::Skin::frameHeight()
330 {
331    return (frame.border.top + frame.border.bottom);
332 }
333 
borderToRect(const QWidget * w) const334 QRect Config::ShapeSkin::borderToRect(const QWidget* w) const
335 {
336    QRect ret;
337 
338    // X1
339    if (rect.left() >= 0)
340       ret.setX(rect.left());
341    else
342       ret.setX(w->width() + rect.left());
343 
344    // Y1
345    if (rect.top() >= 0)
346       ret.setY(rect.top());
347    else
348       ret.setY(w->height() + rect.top());
349 
350    // X2
351    if (rect.right() >= 0)
352       ret.setWidth(rect.right() - ret.x());
353    else
354       ret.setWidth((w->width() + rect.right()) - ret.x());
355 
356    // Y2
357    if (rect.bottom() >= 0)
358       ret.setHeight(rect.bottom() - ret.y());
359    else
360       ret.setHeight((w->height() + rect.bottom()) - ret.y());
361 
362    return ret;
363 }
364 
scaleWithBorder(const QPixmap & pm,int width,int height) const365 QPixmap Config::Skin::scaleWithBorder(const QPixmap& pm, int width, int height) const
366 {
367   if (pm.isNull())
368     return QPixmap();
369 
370   QPainter p;
371   const Border& border(frame.border);
372 
373   // top left corner
374   QPixmap pmTL(border.left, border.top);
375   p.begin(&pmTL);
376   p.drawPixmap(0, 0, pm, 0, 0, pmTL.width(), pmTL.height());
377   p.end();
378 
379   // top border
380   QPixmap pmT(pm.width() - border.left - border.right, border.top);
381   p.begin(&pmT);
382   p.drawPixmap(0, 0, pm, border.left, 0, pmT.width(), pmT.height());
383   p.end();
384   QImage imT( (pmT.toImage()).scaled(width - border.left - border.right, pmT.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) );
385 
386   // top right corner
387   QPixmap pmTR(border.right, border.top);
388   p.begin(&pmTR);
389   p.drawPixmap(0, 0, pm, pm.width() - border.right, 0, pmTR.width(), pmTR.height());
390   p.end();
391 
392   // left border
393   QPixmap pmL(border.left, pm.height() - border.top - border.bottom);
394   p.begin(&pmL);
395   p.drawPixmap(0, 0, pm, 0, border.top, pmL.width(), pmL.height());
396   p.end();
397   QImage imL( (pmL.toImage()).scaled(pmL.width(), height - border.top - border.bottom, Qt::IgnoreAspectRatio, Qt::SmoothTransformation) );
398 
399   // center
400   QPixmap pmC(pmT.width(), pmL.height());
401   p.begin(&pmC);
402   p.drawPixmap(0, 0, pm, border.left, border.top, pmC.width(), pmC.height());
403   p.end();
404   QImage imC( (pmC.toImage()).scaled(imT.width(), imL.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) );
405 
406   // right border
407   QPixmap pmR(border.right, pm.height() - border.top - border.bottom);
408   p.begin(&pmR);
409   p.drawPixmap(0, 0, pm, pm.width() - border.right, border.top, pmR.width(), pmR.height());
410   p.end();
411   QImage imR ( (pmR.toImage()).scaled(pmR.width(), height - border.top - border.bottom, Qt::IgnoreAspectRatio, Qt::SmoothTransformation) );
412 
413   // bottom left border
414   QPixmap pmBL(border.left, border.bottom);
415   p.begin(&pmBL);
416   p.drawPixmap(0, 0, pm, 0, pm.height() - border.bottom, pmBL.width(), pmBL.height());
417   p.end();
418 
419   // bottom border
420   QPixmap pmB(pm.width() - border.left - border.right, border.bottom);
421   p.begin(&pmB);
422   p.drawPixmap(0, 0, pm, border.left, pm.height() - border.bottom, pmB.width(), pmB.height());
423   p.end();
424   QImage imB( (pmB.toImage()).scaled(width - border.left - border.right, pmB.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) );
425 
426   // bottom right border
427   QPixmap pmBR(border.right, border.bottom);
428   p.begin(&pmBR);
429   p.drawPixmap(0, 0, pm, pm.width() - border.right, pm.height() - border.bottom, pmBR.width(), pmBR.height());
430   p.end();
431 
432   // put the image together
433   QPixmap pmFinal(width, height);
434   p.begin(&pmFinal);
435   p.drawPixmap(0, 0, pmTL, 0, 0, -1, -1);
436   p.drawImage(border.left, 0, imT, 0, 0, -1, -1);
437   p.drawPixmap(pmFinal.width() - border.right, 0, pmTR, 0, 0, -1, -1);
438   p.drawImage(0, border.top, imL, 0, 0, -1, -1);
439   p.drawImage(pmFinal.width() - border.right, border.top, imR, 0, 0, -1, -1);
440   p.drawPixmap(0, pmFinal.height() - border.bottom, pmBL, 0, 0, -1, -1);
441   p.drawImage(border.left, pmFinal.height() - border.bottom, imB, 0, 0, -1, -1);
442   p.drawPixmap(pmFinal.width() - border.right, pmFinal.height() - border.bottom, pmBR, 0, 0, -1, -1);
443   p.drawImage(border.left, border.top, imC, 0, 0, -1, -1);
444   p.end();
445 
446   return pmFinal;
447 }
448 
mainwinPixmap(int width,int height) const449 QPixmap Config::Skin::mainwinPixmap(int width, int height) const
450 {
451   return scaleWithBorder(frame.pixmap, width, height);
452 }
453 
mainwinMask(int width,int height) const454 QPixmap Config::Skin::mainwinMask(int width, int height) const
455 {
456   return scaleWithBorder(frame.mask, width, height);
457 }
458 
459 /*! \brief Returns a palette with colored scrollbars
460  *
461  *  This method creates a palette with skin specific scrollbar colors.
462  *  Parent should be a widget that holds a "default" active palette, which will
463  *  be used as base for the resulting palette.
464  *  The returned QPalette is a copy of the parent palette but with modified
465  *  scrollbar colors: QColorGroup::Highlight, QColorGroup::Button,
466  *  QColorGroup::Foreground, QColorGroup::Background and QColorGroup::ButtonText.
467  */
palette(QWidget * parent)468 QPalette Config::Skin::palette(QWidget* parent)
469 {
470   QPalette pal = parent->palette();
471   // ButtonText +  arrow of scrollbar
472   if (buttonTextColor.isValid())
473   {
474     pal.setColor(QPalette::ButtonText, buttonTextColor);
475     pal.setColor(QPalette::Foreground, buttonTextColor);
476   }
477   // Scrollbar
478   if (scrollbarColor.isValid())
479   {
480     pal.setColor(QPalette::Highlight, scrollbarColor);
481     pal.setColor(QPalette::Button, scrollbarColor);
482     pal.setColor(QPalette::Background, scrollbarColor);
483   }
484   return pal;
485 }
486