Changeset View
Changeset View
Standalone View
Standalone View
kcms/cursortheme/xcursor/thememodel.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Copyright © 2005-2007 Fredrik Höglund <fredrik@kde.org> | 2 | * Copyright © 2005-2007 Fredrik Höglund <fredrik@kde.org> | ||
3 | * Copyright © 2016 Jason A. Donenfeld <jason@zx2c4.com> | | |||
4 | * | 3 | * | ||
5 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public | 5 | * modify it under the terms of the GNU General Public | ||
7 | * License version 2 as published by the Free Software Foundation. | 6 | * License version 2 as published by the Free Software Foundation. | ||
8 | * | 7 | * | ||
9 | * This program is distributed in the hope that it will be useful, | 8 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | * General Public License for more details. | 11 | * General Public License for more details. | ||
13 | * | 12 | * | ||
14 | * You should have received a copy of the GNU General Public License | 13 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; see the file COPYING. If not, write to | 14 | * along with this program; see the file COPYING. If not, write to | ||
16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 15 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
17 | * Boston, MA 02110-1301, USA. | 16 | * Boston, MA 02110-1301, USA. | ||
18 | */ | 17 | */ | ||
19 | 18 | | |||
20 | 19 | | |||
21 | #include <KLocalizedString> | 20 | #include <KLocalizedString> | ||
22 | #include <KConfig> | 21 | #include <KConfig> | ||
23 | #include <KConfigGroup> | 22 | #include <KConfigGroup> | ||
24 | #include <KShell> | | |||
25 | #include <QStringList> | 23 | #include <QStringList> | ||
26 | #include <QDir> | 24 | #include <QDir> | ||
27 | #include <QX11Info> | 25 | #include <QX11Info> | ||
28 | 26 | | |||
29 | #include "thememodel.h" | 27 | #include "thememodel.h" | ||
30 | #include "xcursortheme.h" | 28 | #include "xcursortheme.h" | ||
31 | 29 | | |||
30 | #include <X11/Xlib.h> | ||||
32 | #include <X11/Xcursor/Xcursor.h> | 31 | #include <X11/Xcursor/Xcursor.h> | ||
33 | 32 | | |||
33 | // Check for older version | ||||
34 | #if !defined(XCURSOR_LIB_MAJOR) && defined(XCURSOR_MAJOR) | ||||
35 | # define XCURSOR_LIB_MAJOR XCURSOR_MAJOR | ||||
36 | # define XCURSOR_LIB_MINOR XCURSOR_MINOR | ||||
37 | #endif | ||||
38 | | ||||
39 | | ||||
40 | | ||||
34 | CursorThemeModel::CursorThemeModel(QObject *parent) | 41 | CursorThemeModel::CursorThemeModel(QObject *parent) | ||
35 | : QAbstractTableModel(parent) | 42 | : QAbstractTableModel(parent) | ||
36 | { | 43 | { | ||
37 | insertThemes(); | 44 | insertThemes(); | ||
38 | } | 45 | } | ||
39 | 46 | | |||
40 | CursorThemeModel::~CursorThemeModel() | 47 | CursorThemeModel::~CursorThemeModel() | ||
41 | { | 48 | { | ||
42 | qDeleteAll(list); | 49 | qDeleteAll(list); | ||
43 | list.clear(); | 50 | list.clear(); | ||
44 | } | 51 | } | ||
45 | 52 | | |||
53 | QHash<int, QByteArray> CursorThemeModel::roleNames() const | ||||
54 | { | ||||
55 | QHash<int, QByteArray> roleNames = QAbstractTableModel::roleNames(); | ||||
56 | roleNames[CursorTheme::DisplayDetailRole] = "description"; | ||||
57 | roleNames[CursorTheme::IsWritableRole] = "isWritable"; | ||||
58 | | ||||
59 | return roleNames; | ||||
60 | } | ||||
61 | | ||||
46 | void CursorThemeModel::refreshList() | 62 | void CursorThemeModel::refreshList() | ||
47 | { | 63 | { | ||
48 | beginResetModel(); | 64 | beginResetModel(); | ||
49 | qDeleteAll(list); | 65 | qDeleteAll(list); | ||
50 | list.clear(); | 66 | list.clear(); | ||
51 | endResetModel(); | 67 | endResetModel(); | ||
52 | insertThemes(); | 68 | insertThemes(); | ||
53 | } | 69 | } | ||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Line(s) | 98 | { | |||
103 | // Description for the first name column | 119 | // Description for the first name column | ||
104 | if (role == CursorTheme::DisplayDetailRole && index.column() == NameColumn) | 120 | if (role == CursorTheme::DisplayDetailRole && index.column() == NameColumn) | ||
105 | return theme->description(); | 121 | return theme->description(); | ||
106 | 122 | | |||
107 | // Icon for the name column | 123 | // Icon for the name column | ||
108 | if (role == Qt::DecorationRole && index.column() == NameColumn) | 124 | if (role == Qt::DecorationRole && index.column() == NameColumn) | ||
109 | return theme->icon(); | 125 | return theme->icon(); | ||
110 | 126 | | |||
127 | if (role == CursorTheme::IsWritableRole) { | ||||
128 | return theme->isWritable(); | ||||
129 | } | ||||
130 | | ||||
111 | return QVariant(); | 131 | return QVariant(); | ||
112 | } | 132 | } | ||
113 | 133 | | |||
114 | 134 | | |||
115 | void CursorThemeModel::sort(int column, Qt::SortOrder order) | 135 | void CursorThemeModel::sort(int column, Qt::SortOrder order) | ||
116 | { | 136 | { | ||
117 | Q_UNUSED(column); | 137 | Q_UNUSED(column); | ||
118 | Q_UNUSED(order); | 138 | Q_UNUSED(order); | ||
Show All 36 Lines | |||||
155 | } | 175 | } | ||
156 | 176 | | |||
157 | 177 | | |||
158 | const QStringList CursorThemeModel::searchPaths() | 178 | const QStringList CursorThemeModel::searchPaths() | ||
159 | { | 179 | { | ||
160 | if (!baseDirs.isEmpty()) | 180 | if (!baseDirs.isEmpty()) | ||
161 | return baseDirs; | 181 | return baseDirs; | ||
162 | 182 | | |||
163 | baseDirs = QString(XcursorLibraryPath()).split(':', QString::SkipEmptyParts); | 183 | #if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1 | ||
164 | std::transform(baseDirs.begin(), baseDirs.end(), baseDirs.begin(), KShell::tildeExpand); | 184 | // These are the default paths Xcursor will scan for cursor themes | ||
165 | baseDirs.removeDuplicates(); | 185 | QString path("~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons"); | ||
186 | | ||||
187 | // If XCURSOR_PATH is set, use that instead of the default path | ||||
188 | char *xcursorPath = std::getenv("XCURSOR_PATH"); | ||||
189 | if (xcursorPath) | ||||
190 | path = xcursorPath; | ||||
191 | #else | ||||
192 | // Get the search path from Xcursor | ||||
193 | QString path = XcursorLibraryPath(); | ||||
194 | #endif | ||||
195 | | ||||
196 | // Separate the paths | ||||
197 | baseDirs = path.split(':', QString::SkipEmptyParts); | ||||
198 | | ||||
199 | // Remove duplicates | ||||
200 | QMutableStringListIterator i(baseDirs); | ||||
201 | while (i.hasNext()) | ||||
202 | { | ||||
203 | const QString path = i.next(); | ||||
204 | QMutableStringListIterator j(i); | ||||
205 | while (j.hasNext()) | ||||
206 | if (j.next() == path) | ||||
207 | j.remove(); | ||||
208 | } | ||||
166 | 209 | | |||
210 | // Expand all occurrences of ~/ to the home dir | ||||
211 | baseDirs.replaceInStrings(QRegExp("^~\\/"), QDir::home().path() + '/'); | ||||
167 | return baseDirs; | 212 | return baseDirs; | ||
168 | } | 213 | } | ||
169 | 214 | | |||
170 | 215 | | |||
171 | bool CursorThemeModel::hasTheme(const QString &name) const | 216 | bool CursorThemeModel::hasTheme(const QString &name) const | ||
172 | { | 217 | { | ||
173 | const uint hash = qHash(name); | 218 | const uint hash = qHash(name); | ||
174 | 219 | | |||
Show All 14 Lines | 229 | { | |||
189 | // Search each icon theme directory for 'theme' | 234 | // Search each icon theme directory for 'theme' | ||
190 | foreach (const QString &baseDir, searchPaths()) | 235 | foreach (const QString &baseDir, searchPaths()) | ||
191 | { | 236 | { | ||
192 | QDir dir(baseDir); | 237 | QDir dir(baseDir); | ||
193 | if (!dir.exists() || !dir.cd(theme)) | 238 | if (!dir.exists() || !dir.cd(theme)) | ||
194 | continue; | 239 | continue; | ||
195 | 240 | | |||
196 | // If there's a cursors subdir, we'll assume this is a cursor theme | 241 | // If there's a cursors subdir, we'll assume this is a cursor theme | ||
197 | if (dir.exists(QStringLiteral("cursors"))) | 242 | if (dir.exists("cursors")) | ||
198 | return true; | 243 | return true; | ||
199 | 244 | | |||
200 | // If the theme doesn't have an index.theme file, it can't inherit any themes. | 245 | // If the theme doesn't have an index.theme file, it can't inherit any themes. | ||
201 | if (!dir.exists(QStringLiteral("index.theme"))) | 246 | if (!dir.exists("index.theme")) | ||
202 | continue; | 247 | continue; | ||
203 | 248 | | |||
204 | // Open the index.theme file, so we can get the list of inherited themes | 249 | // Open the index.theme file, so we can get the list of inherited themes | ||
205 | KConfig config(dir.path() + "/index.theme", KConfig::NoGlobals); | 250 | KConfig config(dir.path() + "/index.theme", KConfig::NoGlobals); | ||
206 | KConfigGroup cg(&config, "Icon Theme"); | 251 | KConfigGroup cg(&config, "Icon Theme"); | ||
207 | 252 | | |||
208 | // Recurse through the list of inherited themes, to check if one of them | 253 | // Recurse through the list of inherited themes, to check if one of them | ||
209 | // is a cursor theme. | 254 | // is a cursor theme. | ||
Show All 23 Lines | 277 | { | |||
233 | QFileInfo target(info.symLinkTarget()); | 278 | QFileInfo target(info.symLinkTarget()); | ||
234 | if (target.exists() && (target.isDir() || target.isSymLink())) | 279 | if (target.exists() && (target.isDir() || target.isSymLink())) | ||
235 | defaultName = target.fileName(); | 280 | defaultName = target.fileName(); | ||
236 | 281 | | |||
237 | return true; | 282 | return true; | ||
238 | } | 283 | } | ||
239 | 284 | | |||
240 | // If there's no cursors subdir, or if it's empty | 285 | // If there's no cursors subdir, or if it's empty | ||
241 | if (!themeDir.exists(QStringLiteral("cursors")) || QDir(themeDir.path() + "/cursors") | 286 | if (!themeDir.exists("cursors") || QDir(themeDir.path() + "/cursors") | ||
242 | .entryList(QDir::Files | QDir::NoDotAndDotDot ).isEmpty()) | 287 | .entryList(QDir::Files | QDir::NoDotAndDotDot ).isEmpty()) | ||
243 | { | 288 | { | ||
244 | if (themeDir.exists(QStringLiteral("index.theme"))) | 289 | if (themeDir.exists("index.theme")) | ||
245 | { | 290 | { | ||
246 | XCursorTheme theme(themeDir); | 291 | XCursorTheme theme(themeDir); | ||
247 | if (!theme.inherits().isEmpty()) | 292 | if (!theme.inherits().isEmpty()) | ||
248 | defaultName = theme.inherits().at(0); | 293 | defaultName = theme.inherits().at(0); | ||
249 | } | 294 | } | ||
250 | return true; | 295 | return true; | ||
251 | } | 296 | } | ||
252 | 297 | | |||
253 | defaultName = QStringLiteral("default"); | 298 | defaultName = QLatin1String("default"); | ||
254 | return false; | 299 | return false; | ||
255 | } | 300 | } | ||
256 | 301 | | |||
257 | 302 | | |||
258 | void CursorThemeModel::processThemeDir(const QDir &themeDir) | 303 | void CursorThemeModel::processThemeDir(const QDir &themeDir) | ||
259 | { | 304 | { | ||
260 | bool haveCursors = themeDir.exists(QStringLiteral("cursors")); | 305 | bool haveCursors = themeDir.exists("cursors"); | ||
davidedmundson: why change these QStringLiterals? Is a rebase needed? | |||||
yeah, it's so long this review is open that doesn't merge cleanly with master anymore :/ mart: yeah, it's so long this review is open that doesn't merge cleanly with master anymore :/ | |||||
261 | 306 | | |||
262 | // Special case handling of "default", since it's usually either a | 307 | // Special case handling of "default", since it's usually either a | ||
263 | // symlink to another theme, or an empty theme that inherits another | 308 | // symlink to another theme, or an empty theme that inherits another | ||
264 | // theme. | 309 | // theme. | ||
265 | if (defaultName.isNull() && themeDir.dirName() == QLatin1String("default")) | 310 | if (defaultName.isNull() && themeDir.dirName() == "default") | ||
266 | { | 311 | { | ||
267 | if (handleDefault(themeDir)) | 312 | if (handleDefault(themeDir)) | ||
268 | return; | 313 | return; | ||
269 | } | 314 | } | ||
270 | 315 | | |||
271 | // If the directory doesn't have a cursors subdir and lacks an | 316 | // If the directory doesn't have a cursors subdir and lacks an | ||
272 | // index.theme file it can't be a cursor theme. | 317 | // index.theme file it can't be a cursor theme. | ||
273 | if (!themeDir.exists(QStringLiteral("index.theme")) && !haveCursors) | 318 | if (!themeDir.exists("index.theme") && !haveCursors) | ||
274 | return; | 319 | return; | ||
275 | 320 | | |||
321 | static bool isX11 = QX11Info::isPlatformX11(); | ||||
davidedmundson: This seems unrelated, adn I'm not sure is right. | |||||
322 | if (!isX11) { | ||||
323 | // TODO: implement Wayland Cursor Theme support | ||||
324 | return; | ||||
325 | } | ||||
276 | // Create a cursor theme object for the theme dir | 326 | // Create a cursor theme object for the theme dir | ||
277 | XCursorTheme *theme = new XCursorTheme(themeDir); | 327 | XCursorTheme *theme = new XCursorTheme(themeDir); | ||
278 | 328 | | |||
279 | // Skip this theme if it's hidden. | 329 | // Skip this theme if it's hidden. | ||
280 | if (theme->isHidden()) { | 330 | if (theme->isHidden()) { | ||
281 | delete theme; | 331 | delete theme; | ||
282 | return; | 332 | return; | ||
283 | } | 333 | } | ||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Line(s) | 369 | { | |||
326 | 376 | | |||
327 | processThemeDir(dir); | 377 | processThemeDir(dir); | ||
328 | dir.cdUp(); // Return to the base dir | 378 | dir.cdUp(); // Return to the base dir | ||
329 | } | 379 | } | ||
330 | } | 380 | } | ||
331 | 381 | | |||
332 | // The theme Xcursor will end up using if no theme is configured | 382 | // The theme Xcursor will end up using if no theme is configured | ||
333 | if (defaultName.isNull() || !hasTheme(defaultName)) | 383 | if (defaultName.isNull() || !hasTheme(defaultName)) | ||
334 | defaultName = QStringLiteral("KDE_Classic"); | 384 | defaultName = QLatin1String("KDE_Classic"); | ||
335 | } | 385 | } | ||
336 | 386 | | |||
337 | 387 | | |||
338 | bool CursorThemeModel::addTheme(const QDir &dir) | 388 | bool CursorThemeModel::addTheme(const QDir &dir) | ||
339 | { | 389 | { | ||
340 | XCursorTheme *theme = new XCursorTheme(dir); | 390 | XCursorTheme *theme = new XCursorTheme(dir); | ||
341 | 391 | | |||
342 | // Don't add the theme to the list if it's hidden | 392 | // Don't add the theme to the list if it's hidden | ||
Show All 39 Lines |
why change these QStringLiterals? Is a rebase needed?