Added new theme. Made detail screen also themable.
[jspeed] / src / theme.cpp
1 /*
2  * This file is part of jSpeed.
3  *
4  * jSpeed is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * jSpeed is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  */
18
19 #include <QtCore/QDir>
20 #include <QtCore/QString>
21 #include <QtCore/QDebug>
22 #include <QtGui/QApplication>
23 #include <QtGui/QDesktopWidget>
24 #include <QtGui/QStackedWidget>
25 #include <QtXml/QDomNode>
26 #include <QtXml/QDomDocument>
27 #include <QtXml/QDomAttr>
28 #include <QMaemo5InformationBox>
29 #include "theme.h"
30 #include "reader.h"
31 #include "zipreader.h"
32 #include "filereader.h"
33 #include "themescreen.h"
34 #include "detailscreen.h"
35 #include "settings.h"
36
37 namespace
38 {
39     QString const RESOURCE_DIR = ":/resources/themes/";
40     QString const THEME_SUFFIX = ".jspeed";
41 }
42
43 Theme::Theme(DetailScreen* detailScreen, QWidget* parent): WidgetScreen(parent),
44 portraitId_(-1), landscapeId_(-1), reader_(0),
45 portrait_(0), landscape_(0), detailScreen_(detailScreen), portraitMode_(false)
46 {
47 }
48
49 Theme::~Theme()
50 {
51 }
52
53 bool Theme::load()
54 {
55     if(landscape_)
56     {
57         removeWidget(landscape_);
58         delete landscape_;
59         landscape_ = 0;
60         landscapeId_ = -1;
61     }
62
63     if(portrait_)
64     {
65         removeWidget(portrait_);
66         delete portrait_;
67         portrait_ = 0;
68         portraitId_ = -1;
69     }
70
71     QString theme = Settings::instance().value("theme", "default").toString();
72
73     QString themeDir = getThemeDir();
74
75     if(QFile::exists(themeDir + theme + THEME_SUFFIX))
76     {
77         reader_ = new ZipReader(themeDir + theme + THEME_SUFFIX);
78
79         if(read())
80         {
81             return true;
82         }
83         else
84         {
85             QMaemo5InformationBox::information(this,
86                                                tr("Unable to load theme: %1.").arg(error_),
87                                                QMaemo5InformationBox::NoTimeout);
88         }
89     }
90
91     theme = "default";
92     Settings::instance().setValue("theme", theme);
93
94     if(QFile::exists(RESOURCE_DIR + theme))
95     {
96         if(reader_)
97         {
98             delete reader_;
99         }
100
101         reader_ = new FileReader(RESOURCE_DIR + theme);
102         return read();
103     }
104
105     error_ = "No themes found";
106
107     return false;
108 }
109
110 bool Theme::read()
111 {
112     Q_ASSERT(reader_ != 0);
113
114     if(!reader_->open())
115     {
116         error_ = reader_->errorString();
117         return false;
118     }
119
120     QByteArray xmlData;
121
122     if(!reader_->readFile("theme.xml", xmlData))
123     {
124         error_ = "Unable to find <b>theme.xml</b> from theme file";
125         return false;
126     }
127
128     QDomDocument doc;
129     int line = 0;
130     int column = 0;
131     QString msg;
132
133     if(!doc.setContent(xmlData, false, &msg, &line, &column))
134     {
135         error_ = "Invalid xml file, " + msg + " (line "+line+", column "+column+")";
136         return false;
137     }
138
139     QDomNodeList detailConfigs = doc.elementsByTagName("detailscreen");
140
141     if(detailConfigs.size() > 1)
142     {
143         error_ = "Multiple <detailscreen> tags specified";
144         return false;
145     }
146
147     if(detailConfigs.size() == 1)
148     {
149         detailScreen_->removeElements();
150
151         QDomNode color = detailConfigs.at(0).attributes().namedItem("color");
152
153         if(!color.isNull())
154         {
155             detailScreen_->setColor(color.toAttr().value());
156         }
157
158         detailScreen_->load(detailConfigs.at(0), reader_);
159     }
160
161     QDomNodeList orientations = doc.elementsByTagName("orientation");
162
163     if(orientations.isEmpty())
164     {
165         error_ = "No <orientation> tags found";
166         return false;
167     }
168
169     bool ok = true;
170
171     for(int i = 0; i < orientations.size(); i++)
172     {
173         QDomNode data = orientations.at(i);
174         QString type = data.attributes().namedItem("name").toAttr().value();
175
176         if(type == "landscape")
177         {
178             if(landscape_)
179             {
180                 error_ = "More than one <orientation name=\"landscape\"> specified";
181                 ok = false;
182             }
183
184             landscape_ = new ThemeScreen();
185             ok = ok && landscape_->load(data, reader_);
186
187         }
188         else if(type == "portrait")
189         {
190             if(portrait_)
191             {
192                 error_ = "More than one <orientation name=\"portrait\"> specified";
193                 ok = false;
194             }
195
196             portrait_ = new ThemeScreen();
197             ok = ok && portrait_->load(data, reader_);
198         }
199         else
200         {
201             error_ = "Invalid orientation: " + type;
202             ok = false;
203         }
204     }
205
206     reader_->close();
207     delete reader_;
208     reader_ = 0;
209
210     if(!portrait_ && !landscape_)
211     {
212         error_ = "No valid orientation tags found";
213         ok = false;
214     }
215
216     if(ok)
217     {
218         if(landscape_)
219         {
220             landscapeId_ = addWidget(landscape_);
221             connectSignals(landscape_);
222         }
223
224         if(portrait_)
225         {
226             portraitId_ = addWidget(portrait_);
227             connectSignals(portrait_);
228         }
229
230         if(landscape_ && portrait_)
231         {
232             QRect rect = QApplication::desktop()->screenGeometry();
233
234             if(rect.height() > rect.width())
235             {
236                 setCurrentIndex(portraitId_);
237                 portraitMode_ = true;
238             }
239             else
240             {
241                 setCurrentIndex(landscapeId_);
242                 portraitMode_ = false;
243             }
244         }
245     }
246     else
247     {
248         delete portrait_;
249         portrait_ = 0;
250         delete landscape_;
251         landscape_ = 0;
252     }
253
254     return ok;
255 }
256
257 QString Theme::getThemeDir()
258 {
259     return Settings::getDir() + "themes" + QDir::separator();
260 }
261
262 QString const& Theme::getThemeSuffix()
263 {
264     return THEME_SUFFIX;
265 }
266
267 QString const& Theme::error() const
268 {
269     return error_;
270 }
271
272 bool Theme::portraitEnabled() const
273 {
274     return portrait_ != 0;
275 }
276
277 bool Theme::landscapeEnabled() const
278 {
279     return landscape_ != 0;
280 }
281
282 void Theme::reArrange()
283 {
284     QRect rect = QApplication::desktop()->screenGeometry();
285
286     bool portrait = rect.height() > rect.width();
287
288     if(portrait != portraitMode_)
289     {
290         if((portrait && portraitId_ == -1) ||
291            (!portrait && landscapeId_ == -1))
292         {
293             return;
294         }
295
296         portraitMode_ = portrait;
297
298         if(portrait)
299         {
300             setCurrentIndex(portraitId_);
301             portrait_->reArrange();
302         }
303         else
304         {
305             setCurrentIndex(landscapeId_);
306             landscape_->reArrange();
307         }
308     }
309 }
310
311 void Theme::flip()
312 {
313     if(portrait_)
314     {
315         portrait_->flip();
316     }
317
318     if(landscape_)
319     {
320         landscape_->flip();
321     }
322 }
323
324 void Theme::connectSignals(ThemeScreen* screen)
325 {
326     connect(screen, SIGNAL(minimizePressed()), this, SIGNAL(minimizePressed()));
327     connect(screen, SIGNAL(settingsPressed()), this, SIGNAL(settingsPressed()));
328     connect(screen, SIGNAL(closePressed()), this, SIGNAL(closePressed()));
329     connect(screen, SIGNAL(clicked()), this, SIGNAL(clicked()));
330 }
331