1 /*
2  * Copyright (C) Pedram Pourang (aka Tsu Jan) 2018 <tsujan2000@gmail.com>
3  *
4  * Kvantum is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Kvantum is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "KvCommand.h"
19 #include <QDir>
20 #include <QSettings>
21 
22 namespace KvManager {
23 
getHomeConfig()24 static const QString getHomeConfig()
25 {
26     QString xdg_config_home;
27     char * _xdg_config_home = getenv ("XDG_CONFIG_HOME");
28     if (!_xdg_config_home)
29         xdg_config_home = QString ("%1/.config").arg (QDir::homePath());
30     else
31         xdg_config_home = QString (_xdg_config_home);
32     return xdg_config_home;
33 }
34 
isThemeDir(const QString & folderPath)35 static bool isThemeDir (const QString &folderPath)
36 {
37     if (folderPath.isEmpty()) return false;
38     QDir dir = QDir (folderPath);
39     if (!dir.exists()) return false;
40 
41     QString themeName = dir.dirName();
42 
43     QStringList parts = folderPath.split ("/");
44     if (parts.last() == "Kvantum")
45     {
46         if (parts.size() < 3)
47             return false;
48         else
49             themeName = parts.at (parts.size() - 2);
50     }
51 
52     /* "Default" is reserved for the copied default theme,
53        "Kvantum" for the alternative installation paths. */
54     if (themeName == "Default" || themeName == "Kvantum")
55         return false;
56     /* QSettings doesn't accept spaces in the name */
57     QString s = themeName.simplified();
58     if (s.contains (" "))
59         return false;
60 
61     if (QFile::exists (folderPath + QString ("/%1.kvconfig").arg (themeName))
62         || QFile::exists (folderPath + QString ("/%1.svg").arg (themeName)))
63     {
64       return true;
65     }
66 
67     return false;
68 }
69 
isLightWithDarkDir(const QString & folderPath)70 static bool isLightWithDarkDir (const QString &folderPath)
71 {
72     if (folderPath.endsWith ("Dark"))
73         return false;
74     QDir dir = QDir (folderPath);
75     QString themeName = dir.dirName();
76     QStringList parts = folderPath.split ("/");
77     if (parts.last() == "Kvantum")
78     {
79         if (parts.size() < 3)
80             return false;
81         else
82             themeName = parts.at (parts.size() - 2);
83     }
84     if (themeName == "Default" || themeName == "Kvantum")
85         return false;
86     if (QFile::exists (folderPath + QString ("/%1.kvconfig").arg (themeName + "Dark"))
87         || QFile::exists (folderPath + QString ("/%1.svg").arg (themeName + "Dark")))
88     {
89       return true;
90     }
91     return false;
92 }
93 
getAllThemes()94 static const QStringList getAllThemes()
95 {
96     const QString xdg_config_home = getHomeConfig();
97     QStringList list;
98 
99     /* first add the user themes to the list */
100     QDir kv = QDir (QString ("%1/Kvantum").arg (xdg_config_home));
101     if (kv.exists())
102     {
103         const QStringList folders = kv.entryList (QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
104         for (const QString &folder : folders)
105         {
106             QString path = QString ("%1/Kvantum/%2").arg (xdg_config_home).arg (folder);
107             if (isThemeDir (path))
108             {
109                 list << folder;
110                 if (isLightWithDarkDir (path))
111                     list << (folder + "Dark");
112             }
113         }
114     }
115     QString homeDir = QDir::homePath();
116     kv = QDir (QString ("%1/.themes").arg (homeDir));
117     if (kv.exists())
118     {
119         const QStringList folders = kv.entryList (QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
120         for (const QString &folder : folders)
121         {
122             QString path = QString ("%1/.themes/%2/Kvantum").arg (homeDir).arg (folder);
123             if (isThemeDir (path) && !folder.contains ("#"))
124             {
125                 if (!list.contains (folder)) // the themes installed in the config folder have priority
126                     list << folder;
127                 if (isLightWithDarkDir (path) && !list.contains (folder + "Dark"))
128                     list << (folder + "Dark");
129             }
130         }
131     }
132     kv = QDir (QString ("%1/.local/share/themes").arg (homeDir));
133     if (kv.exists())
134     {
135         const QStringList folders = kv.entryList (QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
136         for (const QString &folder : folders)
137         {
138             QString path = QString ("%1/.local/share/themes/%2/Kvantum").arg (homeDir).arg (folder);
139             if (isThemeDir (path) && !folder.contains ("#"))
140             {
141                 if (!list.contains (folder)) // the user themes installed in the above paths have priority
142                     list << folder;
143                 if (isLightWithDarkDir (path) && !list.contains (folder + "Dark"))
144                     list << (folder + "Dark");
145             }
146         }
147     }
148 
149     /* now add the root themes */
150     QStringList rootList;
151     kv = QDir (QString (DATADIR) + QString ("/Kvantum"));
152     if (kv.exists())
153     {
154         const QStringList folders = kv.entryList (QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
155         for (const QString &folder : folders)
156         {
157             QString path = QString (DATADIR) + QString ("/Kvantum/%1").arg (folder);
158             if (!folder.contains ("#") && isThemeDir (path))
159             {
160                 if (!list.contains (folder) // a user theme with the same name takes priority
161                     && !list.contains (folder + "#"))
162                 {
163                     rootList << folder;
164                 }
165                 if (isLightWithDarkDir (path)
166                     && !list.contains (folder + "Dark")
167                     && !list.contains (folder + "Dark" + "#"))
168                 {
169                     rootList << (folder + "Dark");
170                 }
171             }
172         }
173     }
174     kv = QDir (QString (DATADIR) + QString ("/themes"));
175     if (kv.exists())
176     {
177         const QStringList folders = kv.entryList (QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
178         for (const QString &folder : folders)
179         {
180             QString path = QString (DATADIR) + QString ("/themes/%1/Kvantum").arg (folder);
181             if (!folder.contains ("#") && isThemeDir (path))
182             {
183                 if (!list.contains (folder) // a user theme with the same name takes priority
184                     && !list.contains (folder + "#")
185                     // a root theme inside 'DATADIR/Kvantum/' with the same name takes priority
186                     && !rootList.contains (folder))
187                 {
188                     rootList << folder;
189                 }
190                 if (isLightWithDarkDir (path)
191                     && !list.contains (folder + "Dark")
192                     && !list.contains (folder + "Dark" + "#")
193                     && !rootList.contains (folder + "Dark"))
194                 {
195                     rootList << (folder + "Dark");
196                 }
197             }
198         }
199     }
200     if (!rootList.isEmpty())
201         list << rootList;
202 
203     if (list.isEmpty() || !list.contains ("Default#"))
204         list << "Default";
205 
206     return list;
207 }
208 
setTheme(const QString & theme)209 bool setTheme (const QString& theme)
210 {
211     QString theTheme = theme;
212     const QStringList themes = getAllThemes();
213     if (!themes.contains (theTheme))
214     {
215         if (!theTheme.endsWith("#") && themes.contains (theTheme + "#"))
216             theTheme += "#";
217         else return false;
218     }
219     QString configFile = QString ("%1/Kvantum/kvantum.kvconfig").arg (getHomeConfig());
220     QSettings settings (configFile, QSettings::NativeFormat);
221     if (!settings.isWritable()) return false;
222 
223     if (settings.value ("theme").toString() != theTheme)
224         settings.setValue ("theme", theTheme);
225 
226     return true;
227 }
228 
assignTheme(const QString & theme,const QStringList & apps)229 bool assignTheme (const QString& theme, const QStringList& apps)
230 {
231     QString configFile = QString ("%1/Kvantum/kvantum.kvconfig").arg (getHomeConfig());
232     QSettings settings (configFile, QSettings::NativeFormat);
233     if (!settings.isWritable()) return false;
234 
235     if (theme.isEmpty()) // remove all assignments
236         settings.remove ("Applications");
237     else
238     {
239         QString theTheme = theme;
240         const QStringList themes = getAllThemes();
241         if (!themes.contains (theTheme))
242         {
243             if (!theTheme.endsWith("#") && themes.contains (theTheme + "#"))
244                 theTheme += "#";
245             else return false;
246         }
247 
248         settings.beginGroup ("Applications");
249         if (apps.isEmpty())
250             settings.remove (theTheme);
251         else
252             settings.setValue (theTheme, apps);
253         settings.endGroup();
254     }
255 
256     return true;
257 }
258 
259 }
260