Refactor the Variants factory to clearly separate KDE-specific stuff.
[tagua/yd.git] / src / pref_theme.cpp
blobcc75837499c791dd54552e435f132daa28fe4d8c
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@gmail.com>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
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 // */
11 #include <QDir>
12 #include <QFileInfo>
13 #include <QListWidgetItem>
14 #include <QStringList>
15 #include <KDebug>
16 #include <KStandardDirs>
18 #include "foreach.h"
19 #include "luaapi/loader.h"
20 #include "mastersettings.h"
21 #include "pref_theme.h"
22 #include "variant.h"
23 #include "variantfactory.h"
25 typedef boost::shared_ptr<Variant> VariantPtr;
27 PrefTheme::ThemeInfoList PrefTheme::to_theme_info_list(const QStringList& files, const Settings& s) {
28 //kDebug() << "about to examine " << files.size() << " desktop files";
29 std::map<QString, ThemeInfo> cached_info;
31 SettingArray themes = s.group("themes").array("theme");
32 foreach (Settings s_theme, themes) {
33 ThemeInfo info = ThemeInfo::fromSettings(s_theme);
34 cached_info[info.desktopFile] = info;
37 ThemeInfoList all_themes;
38 bool updated = false;
40 for(int i = 0; i < files.size(); i++) {
41 QFileInfo file_info(files[i]);
42 std::map<QString, ThemeInfo>::iterator it = cached_info.find(files[i]);
44 if (it != cached_info.end()
45 && file_info.exists()
46 && it->second.last_modified == file_info.lastModified() ) {
47 all_themes << it->second;
48 cached_info.erase(it);
50 else {
51 updated = true;
53 ThemeInfo theme_info = ThemeInfo::fromDesktopFile(files[i]);
54 all_themes << theme_info;
56 if (theme_info.name.isEmpty()) {
57 kError() << "No name property in" << files[i];
62 if(!cached_info.empty())
63 updated = true;
65 /* rewrite the cached configuration */
66 if(updated) {
67 SettingArray themes = s.group("themes").newArray("theme");
69 for (int i = 0; i < all_themes.size(); i++) {
70 Settings s_theme = themes.append();
71 all_themes[i].save(s_theme);
75 return all_themes;
79 OptList PrefTheme::get_file_options(const QString& f, bool reload_defaults) {
80 //kDebug() << "get file options for " << f;
82 if(!reload_defaults) {
83 std::map<QString, OptList>::iterator it = m_new_theme_options.find(f);
85 if(it != m_new_theme_options.end())
86 return it->second;
89 LuaApi::Loader lua_context;
90 lua_context.runFile(f);
92 if(lua_context.error()) {
93 kError() << lua_context.errorString();
94 lua_context.clearError();
96 m_new_theme_options[f] = OptList();
97 return OptList();
100 OptList o = lua_context.getValue<OptList>("options", 0, NULL, true);
101 if(lua_context.error()) {
102 kError() << lua_context.errorString();
103 lua_context.clearError();
106 if(!reload_defaults) {
107 SettingMap<QString> s_lua = settings().group("lua-settings").map<QString>("entry", "file-name");
108 Settings entry = s_lua.insert(f);
109 options_list_load_from_settings(o, entry.group("options"));
111 m_new_theme_options[f] = o;
113 return o;
117 int PrefTheme::theme_ok_for_variant(const ThemeInfo& theme_info, const QString& variant_name) {
118 if(theme_info.variants.contains(variant_name+"[default]", Qt::CaseInsensitive))
119 return 4;
120 if(theme_info.variants.contains(variant_name, Qt::CaseInsensitive))
121 return 3;
122 if(theme_info.variants.contains("any[default]", Qt::CaseInsensitive))
123 return 2;
124 if(theme_info.variants.contains("any", Qt::CaseInsensitive))
125 return 1;
126 return 0;
130 PrefTheme::PrefTheme(const QString& currentVariant, QWidget *parent)
131 : QWidget(parent) {
132 setupUi(this);
134 Category *c;
136 c = new Category(NULL, this);
137 m_categories["pieces"] = c;
138 tabWidget->addTab(c, "&Pieces");
140 c = new Category(NULL, this);
141 m_categories["squares"] = c;
142 tabWidget->addTab(c, "&Squares");
144 c = new Category(NULL, this);
145 m_categories["controls"] = c;
146 tabWidget->addTab(c, "&Controls");
148 MasterSettings cached_theme_info("tagua_config_cache.xml");
149 connect(comboVariant, SIGNAL(currentIndexChanged(int)), this, SLOT(variantChanged()));
151 for(CategoryMap::iterator cit = m_categories.begin(); cit != m_categories.end(); ++cit) {
152 Category* c = cit->second;
154 c->m_list->setSelectionMode(QAbstractItemView::SingleSelection);
155 connect(c->m_list, SIGNAL(itemSelectionChanged()), c, SLOT(themeChanged()));
156 connect(c->m_check, SIGNAL(toggled(bool)), c, SLOT(themeChecked(bool)));
157 c->m_opt_layout = new QHBoxLayout(c->m_widget);
158 c->m_opt_layout->setMargin(0);
160 c->m_themes = to_theme_info_list(
161 KGlobal::dirs()->findAllResources("appdata",
162 "themes/"+cit->first+"/*.desktop",
163 KStandardDirs::Recursive),
164 cached_theme_info.group(cit->first)
167 //kDebug() << "loaded " << c->m_themes.size() << " themes";
170 QStringList all = VariantFactory::self().all();
171 int index = 0;
172 int current = -1;
173 foreach (QString service, all) {
174 if (service == currentVariant) {
175 current = index;
177 comboVariant->addItem(service, QVariant(service));
178 index++;
181 if (current != -1)
182 comboVariant->setCurrentIndex(current);
184 variantChanged();
187 PrefTheme::~PrefTheme() {
191 void PrefTheme::apply() {
192 SettingMap<QString> variants = settings().group("variants").map<QString>("variant", "name");
194 for(CategoryMap::iterator cit = m_categories.begin(); cit != m_categories.end(); ++cit) {
195 Category* c = cit->second;
197 for(std::map<QString, QString>::iterator it = c->m_new_themes.begin();
198 it != c->m_new_themes.end(); ++it) {
199 Settings var = variants.insert(it->first);
200 var[cit->first+"-theme"] = it->second;
202 for(std::map<QString, bool>::iterator it = c->m_new_use_def.begin();
203 it != c->m_new_use_def.end(); ++it) {
204 Settings var = variants.insert(it->first);
205 var[cit->first+"-use-def"] = it->second;
209 for(std::map<QString, OptList>::iterator it = m_new_theme_options.begin();
210 it != m_new_theme_options.end(); ++it) {
211 SettingMap<QString> s_lua = settings().group("lua-settings").map<QString>("entry", "file-name");
212 Settings entry = s_lua.insert(it->first);
213 options_list_save_to_settings(it->second, entry.group("options"));
217 void PrefTheme::update_list_view(QListWidget* list, const ThemeInfoList& themes,
218 QString variant_name, QString settings_theme) {
219 list->clear();
221 int selected_ok = 0;
222 QListWidgetItem *item_to_select = NULL;
224 for (int i = 0; i < themes.size(); i++) {
225 int ok = theme_ok_for_variant(themes[i], variant_name);
226 if (!ok)
227 continue;
229 ok = (themes[i].desktopFile == settings_theme) ? 5 : ok;
230 QListWidgetItem *list_item = new QListWidgetItem(themes[i].name, list);
232 list_item->setData(Qt::UserRole, i);
233 if(ok > selected_ok) {
234 item_to_select = list_item;
235 selected_ok = ok;
239 if(item_to_select)
240 list->setItemSelected(item_to_select, true);
243 void PrefTheme::variantChanged() {
244 QString category = comboVariant->itemData(comboVariant->currentIndex()).toString();
245 Variant* variant = VariantFactory::self().create(category);
247 if (!variant) {
248 // FIXME: explain this
249 for (CategoryMap::iterator cit = m_categories.begin(); cit != m_categories.end(); ++cit) {
250 Category* c = cit->second;
252 c->m_check->hide();
253 c->m_list->clear();
254 c->m_list->setEnabled(false);
255 c->m_label->setText(QString());
256 c->m_label->setEnabled(false);
259 return;
262 SettingMap<QString> variants = settings().group("variants").map<QString>("variant", "name");
263 Settings var = variants.insert(variant->name());
264 bool check_proxy = variant->name() != variant->proxy();
266 for(CategoryMap::iterator cit = m_categories.begin(); cit != m_categories.end(); ++cit) {
267 Category* c = cit->second;
269 c->m_check->setVisible(check_proxy);
270 if(check_proxy) {
271 bool use_def = c->m_new_use_def.count(variant->name())
272 ? c->m_new_use_def[variant->name()]
273 : (var[cit->first+"-use-def"] | true).value();
274 c->m_check->setText("Same as "+variant->proxy());
275 c->m_check->setChecked(use_def);
276 c->m_list->setEnabled(!use_def);
277 c->m_label->setEnabled(!use_def);
279 else {
280 c->m_list->setEnabled(true);
281 c->m_label->setEnabled(true);
284 QString settings_theme = c->m_new_themes.count(variant->name())
285 ? c->m_new_themes[variant->name()]
286 : (var[cit->first+"-theme"] | QString()).value();
287 update_list_view(c->m_list, c->m_themes, variant->proxy(), settings_theme);
291 ThemeInfo PrefTheme::getBestTheme(Variant* vi, const QString& category) {
292 QString tag = category + "-theme";
293 QString deftag = category + "-use-def";
294 QString variant_name = vi->name();
295 QString variant_proxy_name = vi->proxy();
296 SettingMap<QString> variants = settings().group("variants").map<QString>("variant", "name");
297 if (variant_name != vi->proxy() &&
298 (variants.insert(variant_name)[deftag] | true) )
299 variant_name = vi->proxy();
301 Settings var_settings = variants.insert(variant_name);
302 if (var_settings[tag] && QFile::exists(var_settings[tag].value<QString>()) ) {
304 // there is a theme in the settings, so pick this
305 ThemeInfo res = ThemeInfo::fromDesktopFile(var_settings[tag].value<QString>());
306 if(theme_ok_for_variant(res, variant_proxy_name))
307 return res;
310 MasterSettings cached_theme_info("tagua_config_cache.xml");
311 KStandardDirs* std_dirs = KGlobal::dirs();
312 ThemeInfoList themes = to_theme_info_list(std_dirs->findAllResources("appdata",
313 "themes/" + category + "/*.desktop",
314 KStandardDirs::Recursive ),
315 cached_theme_info.group(category));
317 int best = 0;
318 ThemeInfo* retv = 0;
319 for(int i = 0; i < themes.size(); i++) {
320 int ok = theme_ok_for_variant(themes[i], variant_proxy_name);
321 if (!ok)
322 continue;
324 if (ok > best) {
325 retv = &themes[i];
326 best = ok;
331 if (retv && *retv) {
332 var_settings[tag] = retv->desktopFile;
335 return retv ? *retv : ThemeInfo();
338 PrefThemeCategory::PrefThemeCategory(QWidget* parent, PrefTheme* owner)
339 : QWidget(parent)
340 , m_parent(owner)
341 , m_opt_layout(NULL)
342 , m_opt_widget(NULL) {
343 setupUi(this);
344 m_reset = new QAction(KIcon("edit-clear"), i18n("&Reset to Default"), this);
345 m_reset->setShortcut(Qt::CTRL+Qt::Key_Z);
346 connect(m_reset, SIGNAL(triggered()), this, SLOT(reset()));
347 m_resetButton->setDefaultAction(m_reset);
348 m_resetButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
351 void PrefThemeCategory::reset() {
352 if(!m_opt_widget)
353 return;
355 QList<QListWidgetItem *> l = m_list->selectedItems();
356 if(l.isEmpty())
357 return;
359 int i = l[0]->data(Qt::UserRole).toInt();
360 if (i >= 0 && i < m_themes.size()) {
361 OptList ol = m_parent->get_file_options(m_themes[i].file_name, true);
362 qobject_cast<OptionWidget*>(m_opt_widget)->setValues(ol);
366 void PrefThemeCategory::themeChanged() {
367 QList<QListWidgetItem *> l = m_list->selectedItems();
368 if(!l.isEmpty()) {
369 int i = l[0]->data(Qt::UserRole).toInt();
370 if(i>=0 && i<m_themes.size()) {
371 m_label->setText(m_themes[i].description);
373 QString c = m_parent->comboVariant->itemData(m_parent->comboVariant->currentIndex()).toString();
374 Variant* variant = VariantFactory::self().create(c);
375 if (variant)
376 m_new_themes[variant->name()] = m_themes[i].desktopFile;
378 if(m_opt_widget) {
379 delete m_opt_widget;
380 m_opt_widget = NULL;
382 OptList ol = m_parent->get_file_options(m_themes[i].file_name);
383 if (ol.size() != 0 && m_list->isEnabled()) {
384 m_opt_widget = new OptionWidget(ol, m_widget);
385 m_opt_layout->addWidget(m_opt_widget);
386 m_reset->setEnabled(true);
388 else
389 m_reset->setEnabled(false);
390 return;
393 m_label->setText(QString());
396 void PrefThemeCategory::themeChecked(bool ck) {
397 m_list->setEnabled(!ck);
398 m_label->setEnabled(!ck);
400 QString c = m_parent->comboVariant->itemData(m_parent->comboVariant->currentIndex()).toString();
401 Variant* variant = VariantFactory::self().create(c);
402 if (variant)
403 m_new_use_def[variant->name()] = ck;
404 themeChanged();