RazorAutostartEntry => LxQt::AutostartEntry
[lxde/liblxqt.git] / razorsettings.cpp
CommitLineData
4ef6cee9 1/* BEGIN_COMMON_COPYRIGHT_HEADER
c4af778e 2 * (c)LGPL2+
4ef6cee9 3 *
4 * Razor - a lightweight, Qt based, desktop toolset
5 * http://razor-qt.org
6 *
7 * Copyright: 2010-2011 Razor team
8 * Authors:
626f1f31 9 * Alexander Sokoloff <sokoloff.a@gmail.com>
4ef6cee9 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
c4af778e 15 * version 2.1 of the License, or (at your option) any later version.
4ef6cee9 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>
00f0a23a
AS
31#include <qtxdg/xdgdirs.h>
32#include <QtCore/QDebug>
4ef6cee9 33#include <QtCore/QEvent>
34#include <QtCore/QDir>
35#include <QtCore/QStringList>
36#include <QtCore/QMutex>
37#include <QtCore/QFileSystemWatcher>
63a934d5 38#include <QtCore/QSharedData>
4ef6cee9 39
4ef6cee9 40class RazorSettingsPrivate
41{
42public:
43 RazorSettingsPrivate(RazorSettings* parent):
44 mParent(parent)
45 {
46 }
47
de53bc2d
AS
48 QString localizedKey(const QString& key) const;
49
7ee52b88
PV
50 QFileSystemWatcher mWatcher;
51
4ef6cee9 52private:
53 RazorSettings* mParent;
54};
55
56
57RazorTheme* RazorTheme::mInstance = 0;
58
63a934d5 59class RazorThemeData: public QSharedData {
4ef6cee9 60public:
63a934d5 61 RazorThemeData(): mValid(false) {}
4ef6cee9 62 QString loadQss(const QString& qssFile) const;
63a934d5 63 QString findTheme(const QString &themeName);
4ef6cee9 64
63a934d5
AS
65 QString mName;
66 QString mPath;
67 QString mPreviewImg;
68 bool mValid;
4ef6cee9 69
63a934d5 70};
4ef6cee9 71
4ef6cee9 72
63a934d5
AS
73class GlobalRazorSettingsPrivate
74{
75public:
76 GlobalRazorSettingsPrivate(GlobalRazorSettings *parent):
2b0063a8
KS
77 mParent(parent),
78 mThemeUpdated(0ull)
4ef6cee9 79 {
63a934d5 80
4ef6cee9 81 }
82
63a934d5 83 GlobalRazorSettings *mParent;
63a934d5
AS
84 QString mIconTheme;
85 QString mRazorTheme;
3e771e4e 86 qlonglong mThemeUpdated;
63a934d5
AS
87
88};
4ef6cee9 89
90
91/************************************************
4ef6cee9 92
93 ************************************************/
94RazorSettings::RazorSettings(const QString& module, QObject* parent) :
00f0a23a 95 QSettings("razor", module, parent),
4ef6cee9 96 d_ptr(new RazorSettingsPrivate(this))
97{
7ee52b88
PV
98 // HACK: we need to ensure that the user (~/.config/razor/<module>.conf)
99 // exists to have functional mWatcher
d6b9c3e5
AM
100 if (!contains("__userfile__"))
101 {
102 setValue("__userfile__", true);
103 sync();
104 }
7ee52b88
PV
105 d_ptr->mWatcher.addPath(this->fileName());
106 connect(&(d_ptr->mWatcher), SIGNAL(fileChanged(QString)), this, SLOT(fileChanged()));
4ef6cee9 107}
108
109
110/************************************************
111
112 ************************************************/
e6173915
AS
113RazorSettings::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 ************************************************/
4ef6cee9 132RazorSettings::RazorSettings(const QSettings* parentSettings, const QString& subGroup, QObject* parent):
094599aa 133 QSettings(parentSettings->organizationName(), parentSettings->applicationName(), parent),
4ef6cee9 134 d_ptr(new RazorSettingsPrivate(this))
135{
136 beginGroup(subGroup);
137}
138
139
140/************************************************
141
142 ************************************************/
143RazorSettings::RazorSettings(const QSettings& parentSettings, const QString& subGroup, QObject* parent):
094599aa 144 QSettings(parentSettings.organizationName(), parentSettings.applicationName(), parent),
4ef6cee9 145 d_ptr(new RazorSettingsPrivate(this))
146{
147 beginGroup(subGroup);
148}
149
00f0a23a
AS
150
151/************************************************
152
153 ************************************************/
4ef6cee9 154RazorSettings::~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
0de9564b
PV
164bool RazorSettings::event(QEvent *event)
165{
166 if (event->type() == QEvent::UpdateRequest)
167 {
168 emit settingsChanged();
169 }
170
171 return QSettings::event(event);
172}
173
7ee52b88 174void RazorSettings::fileChanged()
4ef6cee9 175{
7ee52b88
PV
176// Q_D(RazorSettings);
177 sync();
178 emit settingsChanged();
4ef6cee9 179}
180
181
182/************************************************
183
184 ************************************************/
63a934d5 185const GlobalRazorSettings *RazorSettings::globalSettings()
4ef6cee9 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/************************************************
de53bc2d
AS
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 ************************************************/
211QString 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 ************************************************/
282QVariant 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 ************************************************/
292void RazorSettings::setLocalizedValue(const QString &key, const QVariant &value)
293{
294 Q_D(const RazorSettings);
295 setValue(d->localizedKey(key), value);
296}
297
298
299/************************************************
4ef6cee9 300
301 ************************************************/
302RazorTheme::RazorTheme():
63a934d5 303 d(new RazorThemeData)
4ef6cee9 304{
4ef6cee9 305}
306
63a934d5
AS
307
308/************************************************
309
310 ************************************************/
311RazorTheme::RazorTheme(const QString &path):
312 d(new RazorThemeData)
4ef6cee9 313{
63a934d5
AS
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";
4ef6cee9 333}
334
335
336/************************************************
337
338 ************************************************/
63a934d5 339QString RazorThemeData::findTheme(const QString &themeName)
4ef6cee9 340{
63a934d5
AS
341 if (themeName.isEmpty())
342 return "";
343
344 QStringList paths;
345 paths << XdgDirs::dataHome(false);
346 paths << XdgDirs::dataDirs();
4ef6cee9 347
63a934d5 348 foreach(QString path, paths)
4ef6cee9 349 {
63a934d5
AS
350 QDir dir(QString("%1/razor/themes/%2").arg(path, themeName));
351 if (dir.isReadable())
352 return dir.absolutePath();
353 }
4ef6cee9 354
63a934d5
AS
355 return QString();
356}
4ef6cee9 357
4ef6cee9 358
63a934d5
AS
359/************************************************
360
361 ************************************************/
362RazorTheme::RazorTheme(const RazorTheme &other):
363 d(other.d)
364{
365}
366
367
368/************************************************
369
370 ************************************************/
371RazorTheme::~RazorTheme()
372{
373}
374
375
376/************************************************
377
378 ************************************************/
379RazorTheme& RazorTheme::operator=(const RazorTheme &other)
380{
381 d = other.d;
382 return *this;
383}
384
385
386/************************************************
387
388 ************************************************/
389bool RazorTheme::isValid() const
390{
391 return d->mValid;
392}
393
394
395/************************************************
396
397 ************************************************/
398QString RazorTheme::name() const
399{
400 return d->mName;
401}
402
403/************************************************
404
405 ************************************************/
406QString RazorTheme::path() const
407{
408 return d->mPath;
409}
410
411
412/************************************************
413
414 ************************************************/
415QString RazorTheme::previewImage() const
416{
417 return d->mPreviewImg;
4ef6cee9 418}
419
420
421/************************************************
422
423 ************************************************/
424QString RazorTheme::qss(const QString& module) const
425{
63a934d5 426 QString path = QString("%1/%2.qss").arg(d->mPath, module);
4ef6cee9 427
63a934d5 428 QString styleSheet;
4ef6cee9 429 if (!path.isEmpty())
63a934d5
AS
430 styleSheet = d->loadQss(path);
431 else
432 qWarning() << QString("QSS file %1 cannot be found").arg(path);
4ef6cee9 433
63a934d5
AS
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;
4ef6cee9 440}
441
442
443/************************************************
444
445 ************************************************/
63a934d5 446QString RazorThemeData::loadQss(const QString& qssFile) const
4ef6cee9 447{
448 QFile f(qssFile);
449 if (! f.open(QIODevice::ReadOnly | QIODevice::Text))
450 {
6137b996 451 qWarning() << "Theme: Cannot open file for reading:" << qssFile;
96acff74 452 return QString();
4ef6cee9 453 }
454
455 QString qss = f.readAll();
456 f.close();
457
458 if (qss.isEmpty())
96acff74 459 return QString();
4ef6cee9 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 ************************************************/
472QString RazorTheme::desktopBackground(int screen) const
473{
fa20bdad 474 QString wallpaperCfgFileName = QString("%1/wallpaper.cfg").arg(d->mPath);
4ef6cee9 475
fa20bdad 476 if (wallpaperCfgFileName.isEmpty())
96acff74 477 return QString();
4ef6cee9 478
fa20bdad
KS
479 QSettings s(wallpaperCfgFileName, QSettings::IniFormat);
480 QString themeDir = QFileInfo(wallpaperCfgFileName).absolutePath();
4ef6cee9 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 ************************************************/
63a934d5
AS
500const 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 ************************************************/
515QList<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 {
f8267402 531 if (!processed.contains(dir.fileName()) &&
e7a48ec0 532 QDir(dir.absoluteFilePath()).exists("razor-panel.qss"))
63a934d5
AS
533 {
534 processed << dir.fileName();
535 ret << RazorTheme(dir.absoluteFilePath());
536 }
537
538 }
539 }
540
541 return ret;
542}
543
544
545/************************************************
546
547 ************************************************/
4ef6cee9 548RazorSettingsCache::RazorSettingsCache(QSettings &settings) :
549 mSettings(settings)
550{
551 loadFromSettings();
552}
553
554
555/************************************************
556
557 ************************************************/
558RazorSettingsCache::RazorSettingsCache(QSettings *settings) :
559 mSettings(*settings)
560{
561 loadFromSettings();
562}
563
564
565/************************************************
566
567 ************************************************/
568void RazorSettingsCache::loadFromSettings()
569{
570 foreach (QString key, mSettings.allKeys())
571 {
572 mCache.insert(key, mSettings.value(key));
573 }
574}
575
576
577/************************************************
578
579 ************************************************/
580void 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 ************************************************/
4ef6cee9 597GlobalRazorSettings::GlobalRazorSettings():
598 RazorSettings("razor"),
599 d_ptr(new GlobalRazorSettingsPrivate(this))
600{
4ef6cee9 601 if (value("icon_theme").toString().isEmpty())
602 {
603 QStringList failback;
604 failback << "oxygen";
6ec51b5d
AS
605 failback << "Tango";
606 failback << "Prudence-icon";
4ef6cee9 607 failback << "Humanity";
6ec51b5d
AS
608 failback << "elementary";
609 failback << "gnome";
610
4ef6cee9 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
627GlobalRazorSettings::~GlobalRazorSettings()
628{
629 delete d_ptr;
630}
631
632
633/************************************************
634
635 ************************************************/
636void 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
63a934d5 650 QString rt = value("theme").toString();
3e771e4e 651 qlonglong themeUpdated = value("__theme_updated__").toLongLong();
2b0063a8 652 if ((d->mRazorTheme != rt) || (d->mThemeUpdated != themeUpdated))
4ef6cee9 653 {
654 d->mRazorTheme = rt;
655 emit razorThemeChanged();
656 }
657
c16a53ee 658 emit settingsChanged();
4ef6cee9 659}
63a934d5 660