RazorAutostartEntry => LxQt::AutostartEntry
[lxde/liblxqt.git] / razorsettings.cpp
1 /* BEGIN_COMMON_COPYRIGHT_HEADER
2 * (c)LGPL2+
3 *
4 * Razor - a lightweight, Qt based, desktop toolset
5 * http://razor-qt.org
6 *
7 * Copyright: 2010-2011 Razor team
8 * Authors:
9 * Alexander Sokoloff <sokoloff.a@gmail.com>
10 * Petr Vanek <petr@scribus.info>
11 *
12 * This program or library is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21
22 * You should have received a copy of the GNU Lesser General
23 * Public License along with this library; if not, write to the
24 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301 USA
26 *
27 * END_COMMON_COPYRIGHT_HEADER */
28
29 #include "razorsettings.h"
30 #include <qtxdg/xdgicon.h>
31 #include <qtxdg/xdgdirs.h>
32 #include <QtCore/QDebug>
33 #include <QtCore/QEvent>
34 #include <QtCore/QDir>
35 #include <QtCore/QStringList>
36 #include <QtCore/QMutex>
37 #include <QtCore/QFileSystemWatcher>
38 #include <QtCore/QSharedData>
39
40 class RazorSettingsPrivate
41 {
42 public:
43 RazorSettingsPrivate(RazorSettings* parent):
44 mParent(parent)
45 {
46 }
47
48 QString localizedKey(const QString& key) const;
49
50 QFileSystemWatcher mWatcher;
51
52 private:
53 RazorSettings* mParent;
54 };
55
56
57 RazorTheme* RazorTheme::mInstance = 0;
58
59 class RazorThemeData: public QSharedData {
60 public:
61 RazorThemeData(): mValid(false) {}
62 QString loadQss(const QString& qssFile) const;
63 QString findTheme(const QString &themeName);
64
65 QString mName;
66 QString mPath;
67 QString mPreviewImg;
68 bool mValid;
69
70 };
71
72
73 class GlobalRazorSettingsPrivate
74 {
75 public:
76 GlobalRazorSettingsPrivate(GlobalRazorSettings *parent):
77 mParent(parent),
78 mThemeUpdated(0ull)
79 {
80
81 }
82
83 GlobalRazorSettings *mParent;
84 QString mIconTheme;
85 QString mRazorTheme;
86 qlonglong mThemeUpdated;
87
88 };
89
90
91 /************************************************
92
93 ************************************************/
94 RazorSettings::RazorSettings(const QString& module, QObject* parent) :
95 QSettings("razor", module, parent),
96 d_ptr(new RazorSettingsPrivate(this))
97 {
98 // HACK: we need to ensure that the user (~/.config/razor/<module>.conf)
99 // exists to have functional mWatcher
100 if (!contains("__userfile__"))
101 {
102 setValue("__userfile__", true);
103 sync();
104 }
105 d_ptr->mWatcher.addPath(this->fileName());
106 connect(&(d_ptr->mWatcher), SIGNAL(fileChanged(QString)), this, SLOT(fileChanged()));
107 }
108
109
110 /************************************************
111
112 ************************************************/
113 RazorSettings::RazorSettings(const QString &fileName, QSettings::Format format, QObject *parent):
114 QSettings(fileName, format, parent),
115 d_ptr(new RazorSettingsPrivate(this))
116 {
117 // HACK: we need to ensure that the user (~/.config/razor/<module>.conf)
118 // exists to have functional mWatcher
119 if (!contains("__userfile__"))
120 {
121 setValue("__userfile__", true);
122 sync();
123 }
124 d_ptr->mWatcher.addPath(this->fileName());
125 connect(&(d_ptr->mWatcher), SIGNAL(fileChanged(QString)), this, SLOT(fileChanged()));
126 }
127
128
129 /************************************************
130
131 ************************************************/
132 RazorSettings::RazorSettings(const QSettings* parentSettings, const QString& subGroup, QObject* parent):
133 QSettings(parentSettings->organizationName(), parentSettings->applicationName(), parent),
134 d_ptr(new RazorSettingsPrivate(this))
135 {
136 beginGroup(subGroup);
137 }
138
139
140 /************************************************
141
142 ************************************************/
143 RazorSettings::RazorSettings(const QSettings& parentSettings, const QString& subGroup, QObject* parent):
144 QSettings(parentSettings.organizationName(), parentSettings.applicationName(), parent),
145 d_ptr(new RazorSettingsPrivate(this))
146 {
147 beginGroup(subGroup);
148 }
149
150
151 /************************************************
152
153 ************************************************/
154 RazorSettings::~RazorSettings()
155 {
156 // because in the RazorSettings::RazorSettings(const QString& module, QObject* parent)
157 // constructor there is no beginGroup() called...
158 if (!group().isEmpty())
159 endGroup();
160
161 delete d_ptr;
162 }
163
164 bool RazorSettings::event(QEvent *event)
165 {
166 if (event->type() == QEvent::UpdateRequest)
167 {
168 emit settingsChanged();
169 }
170
171 return QSettings::event(event);
172 }
173
174 void RazorSettings::fileChanged()
175 {
176 // Q_D(RazorSettings);
177 sync();
178 emit settingsChanged();
179 }
180
181
182 /************************************************
183
184 ************************************************/
185 const GlobalRazorSettings *RazorSettings::globalSettings()
186 {
187 static QMutex mutex;
188 static GlobalRazorSettings *instance = 0;
189 if (!instance)
190 {
191 mutex.lock();
192
193 if (!instance)
194 instance = new GlobalRazorSettings();
195
196 mutex.unlock();
197 }
198
199 return instance;
200 }
201
202
203 /************************************************
204 LC_MESSAGES value Possible keys in order of matching
205 lang_COUNTRY@MODIFIER lang_COUNTRY@MODIFIER, lang_COUNTRY, lang@MODIFIER, lang,
206 default value
207 lang_COUNTRY lang_COUNTRY, lang, default value
208 lang@MODIFIER lang@MODIFIER, lang, default value
209 lang lang, default value
210 ************************************************/
211 QString RazorSettingsPrivate::localizedKey(const QString& key) const
212 {
213
214 QString lang = getenv("LC_MESSAGES");
215
216 if (lang.isEmpty())
217 lang = getenv("LC_ALL");
218
219 if (lang.isEmpty())
220 lang = getenv("LANG");
221
222
223 QString modifier = lang.section('@', 1);
224 if (!modifier.isEmpty())
225 lang.truncate(lang.length() - modifier.length() - 1);
226
227 QString encoding = lang.section('.', 1);
228 if (!encoding.isEmpty())
229 lang.truncate(lang.length() - encoding.length() - 1);
230
231
232 QString country = lang.section('_', 1);
233 if (!country.isEmpty())
234 lang.truncate(lang.length() - country.length() - 1);
235
236
237
238 //qDebug() << "LC_MESSAGES: " << getenv("LC_MESSAGES");
239 //qDebug() << "Lang:" << lang;
240 //qDebug() << "Country:" << country;
241 //qDebug() << "Encoding:" << encoding;
242 //qDebug() << "Modifier:" << modifier;
243
244 if (!modifier.isEmpty() && !country.isEmpty())
245 {
246 QString k = QString("%1[%2_%3@%4]").arg(key, lang, country, modifier);
247 //qDebug() << "\t try " << k << mParent->contains(k);
248 if (mParent->contains(k))
249 return k;
250 }
251
252 if (!country.isEmpty())
253 {
254 QString k = QString("%1[%2_%3]").arg(key, lang, country);
255 //qDebug() << "\t try " << k << mParent->contains(k);
256 if (mParent->contains(k))
257 return k;
258 }
259
260 if (!modifier.isEmpty())
261 {
262 QString k = QString("%1[%2@%3]").arg(key, lang, modifier);
263 //qDebug() << "\t try " << k << mParent->contains(k);
264 if (mParent->contains(k))
265 return k;
266 }
267
268 QString k = QString("%1[%2]").arg(key, lang);
269 //qDebug() << "\t try " << k << mParent->contains(k);
270 if (mParent->contains(k))
271 return k;
272
273
274 //qDebug() << "\t try " << key << mParent->contains(key);
275 return key;
276 }
277
278
279 /************************************************
280
281 ************************************************/
282 QVariant RazorSettings::localizedValue(const QString& key, const QVariant& defaultValue) const
283 {
284 Q_D(const RazorSettings);
285 return value(d->localizedKey(key), defaultValue);
286 }
287
288
289 /************************************************
290
291 ************************************************/
292 void RazorSettings::setLocalizedValue(const QString &key, const QVariant &value)
293 {
294 Q_D(const RazorSettings);
295 setValue(d->localizedKey(key), value);
296 }
297
298
299 /************************************************
300
301 ************************************************/
302 RazorTheme::RazorTheme():
303 d(new RazorThemeData)
304 {
305 }
306
307
308 /************************************************
309
310 ************************************************/
311 RazorTheme::RazorTheme(const QString &path):
312 d(new RazorThemeData)
313 {
314 if (path.isEmpty())
315 return;
316
317 QFileInfo fi(path);
318 if (fi.isAbsolute())
319 {
320 d->mPath = path;
321 d->mName = fi.fileName();
322 d->mValid = fi.isDir();
323 }
324 else
325 {
326 d->mName = path;
327 d->mPath = d->findTheme(path);
328 d->mValid = !(d->mPath.isEmpty());
329 }
330
331 if (QDir(path).exists("preview.png"))
332 d->mPreviewImg = path + "/preview.png";
333 }
334
335
336 /************************************************
337
338 ************************************************/
339 QString RazorThemeData::findTheme(const QString &themeName)
340 {
341 if (themeName.isEmpty())
342 return "";
343
344 QStringList paths;
345 paths << XdgDirs::dataHome(false);
346 paths << XdgDirs::dataDirs();
347
348 foreach(QString path, paths)
349 {
350 QDir dir(QString("%1/razor/themes/%2").arg(path, themeName));
351 if (dir.isReadable())
352 return dir.absolutePath();
353 }
354
355 return QString();
356 }
357
358
359 /************************************************
360
361 ************************************************/
362 RazorTheme::RazorTheme(const RazorTheme &other):
363 d(other.d)
364 {
365 }
366
367
368 /************************************************
369
370 ************************************************/
371 RazorTheme::~RazorTheme()
372 {
373 }
374
375
376 /************************************************
377
378 ************************************************/
379 RazorTheme& RazorTheme::operator=(const RazorTheme &other)
380 {
381 d = other.d;
382 return *this;
383 }
384
385
386 /************************************************
387
388 ************************************************/
389 bool RazorTheme::isValid() const
390 {
391 return d->mValid;
392 }
393
394
395 /************************************************
396
397 ************************************************/
398 QString RazorTheme::name() const
399 {
400 return d->mName;
401 }
402
403 /************************************************
404
405 ************************************************/
406 QString RazorTheme::path() const
407 {
408 return d->mPath;
409 }
410
411
412 /************************************************
413
414 ************************************************/
415 QString RazorTheme::previewImage() const
416 {
417 return d->mPreviewImg;
418 }
419
420
421 /************************************************
422
423 ************************************************/
424 QString RazorTheme::qss(const QString& module) const
425 {
426 QString path = QString("%1/%2.qss").arg(d->mPath, module);
427
428 QString styleSheet;
429 if (!path.isEmpty())
430 styleSheet = d->loadQss(path);
431 else
432 qWarning() << QString("QSS file %1 cannot be found").arg(path);
433
434 // Single/double click ...........................
435 RazorSettings s("desktop");
436 bool singleClick = s.value("icon-launch-mode", "singleclick").toString() == "singleclick";
437 styleSheet += QString("QAbstractItemView {activate-on-singleclick : %1; }").arg(singleClick ? 1 : 0);
438
439 return styleSheet;
440 }
441
442
443 /************************************************
444
445 ************************************************/
446 QString RazorThemeData::loadQss(const QString& qssFile) const
447 {
448 QFile f(qssFile);
449 if (! f.open(QIODevice::ReadOnly | QIODevice::Text))
450 {
451 qWarning() << "Theme: Cannot open file for reading:" << qssFile;
452 return QString();
453 }
454
455 QString qss = f.readAll();
456 f.close();
457
458 if (qss.isEmpty())
459 return QString();
460
461 // handle relative paths
462 QString qssDir = QFileInfo(qssFile).canonicalPath();
463 qss.replace(QRegExp("url.[ \\t\\s]*", Qt::CaseInsensitive, QRegExp::RegExp2), "url(" + qssDir + "/");
464
465 return qss;
466 }
467
468
469 /************************************************
470
471 ************************************************/
472 QString RazorTheme::desktopBackground(int screen) const
473 {
474 QString wallpaperCfgFileName = QString("%1/wallpaper.cfg").arg(d->mPath);
475
476 if (wallpaperCfgFileName.isEmpty())
477 return QString();
478
479 QSettings s(wallpaperCfgFileName, QSettings::IniFormat);
480 QString themeDir = QFileInfo(wallpaperCfgFileName).absolutePath();
481 // There is something strange... If I remove next line the wallpapers array is not found...
482 s.childKeys();
483 s.beginReadArray("wallpapers");
484
485 s.setArrayIndex(screen - 1);
486 if (s.contains("file"))
487 return QString("%1/%2").arg(themeDir, s.value("file").toString());
488
489 s.setArrayIndex(0);
490 if (s.contains("file"))
491 return QString("%1/%2").arg(themeDir, s.value("file").toString());
492
493 return QString();
494 }
495
496
497 /************************************************
498
499 ************************************************/
500 const RazorTheme &RazorTheme::currentTheme()
501 {
502 static RazorTheme theme;
503 QString name = RazorSettings::globalSettings()->value("theme").toString();
504 if (theme.name() != name)
505 {
506 theme = RazorTheme(name);
507 }
508 return theme;
509 }
510
511
512 /************************************************
513
514 ************************************************/
515 QList<RazorTheme> RazorTheme::allThemes()
516 {
517 QList<RazorTheme> ret;
518 QSet<QString> processed;
519
520 QStringList paths;
521 paths << XdgDirs::dataHome(false);
522 paths << XdgDirs::dataDirs();
523
524 foreach(QString path, paths)
525 {
526 QDir dir(QString("%1/razor/themes").arg(path));
527 QFileInfoList dirs = dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot);
528
529 foreach(QFileInfo dir, dirs)
530 {
531 if (!processed.contains(dir.fileName()) &&
532 QDir(dir.absoluteFilePath()).exists("razor-panel.qss"))
533 {
534 processed << dir.fileName();
535 ret << RazorTheme(dir.absoluteFilePath());
536 }
537
538 }
539 }
540
541 return ret;
542 }
543
544
545 /************************************************
546
547 ************************************************/
548 RazorSettingsCache::RazorSettingsCache(QSettings &settings) :
549 mSettings(settings)
550 {
551 loadFromSettings();
552 }
553
554
555 /************************************************
556
557 ************************************************/
558 RazorSettingsCache::RazorSettingsCache(QSettings *settings) :
559 mSettings(*settings)
560 {
561 loadFromSettings();
562 }
563
564
565 /************************************************
566
567 ************************************************/
568 void RazorSettingsCache::loadFromSettings()
569 {
570 foreach (QString key, mSettings.allKeys())
571 {
572 mCache.insert(key, mSettings.value(key));
573 }
574 }
575
576
577 /************************************************
578
579 ************************************************/
580 void RazorSettingsCache::loadToSettings()
581 {
582 QHash<QString, QVariant>::const_iterator i = mCache.constBegin();
583
584 while(i != mCache.constEnd())
585 {
586 mSettings.setValue(i.key(), i.value());
587 ++i;
588 }
589
590 mSettings.sync();
591 }
592
593
594 /************************************************
595
596 ************************************************/
597 GlobalRazorSettings::GlobalRazorSettings():
598 RazorSettings("razor"),
599 d_ptr(new GlobalRazorSettingsPrivate(this))
600 {
601 if (value("icon_theme").toString().isEmpty())
602 {
603 QStringList failback;
604 failback << "oxygen";
605 failback << "Tango";
606 failback << "Prudence-icon";
607 failback << "Humanity";
608 failback << "elementary";
609 failback << "gnome";
610
611
612 QDir dir("/usr/share/icons/");
613 foreach (QString s, failback)
614 {
615 if (dir.exists(s))
616 {
617 setValue("icon_theme", s);
618 sync();
619 break;
620 }
621 }
622 }
623
624 fileChanged();
625 }
626
627 GlobalRazorSettings::~GlobalRazorSettings()
628 {
629 delete d_ptr;
630 }
631
632
633 /************************************************
634
635 ************************************************/
636 void GlobalRazorSettings::fileChanged()
637 {
638 Q_D(GlobalRazorSettings);
639 sync();
640
641
642 QString it = value("icon_theme").toString();
643 if (d->mIconTheme != it)
644 {
645 d->mIconTheme = it;
646 XdgIcon::setThemeName(it);
647 emit iconThemeChanged();
648 }
649
650 QString rt = value("theme").toString();
651 qlonglong themeUpdated = value("__theme_updated__").toLongLong();
652 if ((d->mRazorTheme != rt) || (d->mThemeUpdated != themeUpdated))
653 {
654 d->mRazorTheme = rt;
655 emit razorThemeChanged();
656 }
657
658 emit settingsChanged();
659 }
660