Changeset View
Changeset View
Standalone View
Standalone View
kcms/cursortheme/kcmcursortheme.cpp
Show All 10 Lines | |||||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | 12 | * GNU General Public License for more details. | ||
13 | * | 13 | * | ||
14 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | 15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | 17 | */ | ||
18 | 18 | | |||
19 | #include <config-X11.h> | ||||
20 | | ||||
19 | #include "kcmcursortheme.h" | 21 | #include "kcmcursortheme.h" | ||
20 | 22 | | |||
23 | #include "xcursor/thememodel.h" | ||||
24 | #include "xcursor/sortproxymodel.h" | ||||
25 | #include "xcursor/cursortheme.h" | ||||
26 | #include "xcursor/previewwidget.h" | ||||
27 | #include "../krdb/krdb.h" | ||||
28 | | ||||
21 | #include <KAboutData> | 29 | #include <KAboutData> | ||
22 | #include <KPluginFactory> | 30 | #include <KPluginFactory> | ||
23 | #include <KLocalizedString> | 31 | #include <KLocalizedString> | ||
24 | 32 | #include <KConfig> | |||
25 | #include <QVBoxLayout> | 33 | #include <KConfigGroup> | ||
26 | 34 | #include <KMessageBox> | |||
27 | K_PLUGIN_FACTORY(CursorThemeConfigFactory, | 35 | #include <KUrlRequesterDialog> | ||
28 | registerPlugin<CursorThemeConfig>(); | 36 | #include <KIO/Job> | ||
29 | ) | 37 | #include <KIO/DeleteJob> | ||
30 | K_EXPORT_PLUGIN(CursorThemeConfigFactory("kcm_cursortheme", "kcminput")) | 38 | #include <kio/netaccess.h> | ||
31 | 39 | #include <KTar> | |||
32 | 40 | #include <KGlobalSettings> | |||
33 | CursorThemeConfig::CursorThemeConfig(QWidget *parent, const QVariantList &args) | 41 | | ||
34 | : KCModule(parent, args) | 42 | #include <KNewStuff3/KNS3/DownloadDialog> | ||
43 | | ||||
44 | #include <QX11Info> | ||||
45 | #include <QStandardItemModel> | ||||
46 | | ||||
47 | #include <X11/Xlib.h> | ||||
48 | #include <X11/Xcursor/Xcursor.h> | ||||
49 | | ||||
50 | #include <klauncher_iface.h> | ||||
51 | | ||||
52 | #ifdef HAVE_XFIXES | ||||
53 | # include <X11/extensions/Xfixes.h> | ||||
54 | #endif | ||||
55 | | ||||
56 | K_PLUGIN_FACTORY_WITH_JSON(CursorThemeConfigFactory, "kcm_cursortheme.json", registerPlugin<CursorThemeConfig>();) | ||||
57 | | ||||
58 | CursorThemeConfig::CursorThemeConfig(QObject *parent, const QVariantList &args) | ||||
59 | : KQuickAddons::ConfigModule(parent, args), | ||||
60 | m_appliedSize(0), | ||||
61 | m_preferredSize(0), | ||||
62 | m_selectedThemeRow(-1), | ||||
63 | m_canInstall(true), | ||||
64 | m_canResize(true), | ||||
65 | m_canConfigure(true) | ||||
35 | { | 66 | { | ||
36 | QLayout *layout = new QVBoxLayout(this); | 67 | qmlRegisterType<PreviewWidget>("org.kde.private.kcm_cursortheme", 1, 0, "PreviewWidget"); | ||
37 | layout->setMargin(0); | 68 | qmlRegisterType<SortProxyModel>(); | ||
38 | | ||||
39 | themepage = new ThemePage(this); | | |||
40 | connect(themepage, SIGNAL(changed(bool)), SLOT(changed())); | | |||
41 | layout->addWidget(themepage); | | |||
42 | 69 | | |||
43 | KAboutData* aboutData = new KAboutData(QStringLiteral("kcm_cursortheme"), i18n("Cursor Theme"), | 70 | KAboutData* aboutData = new KAboutData(QStringLiteral("kcm_cursortheme"), i18n("Cursor Theme"), | ||
44 | QStringLiteral("1.0"), QString(), KAboutLicense::GPL, i18n("(c) 2003-2007 Fredrik Höglund")); | 71 | QStringLiteral("1.0"), QString(i18n("Mouse Cursor Theme settings")), KAboutLicense::GPL, i18n("(c) 2003-2007 Fredrik Höglund")); | ||
45 | aboutData->addAuthor(i18n("Fredrik Höglund")); | 72 | aboutData->addAuthor(i18n("Fredrik Höglund")); | ||
73 | aboutData->addAuthor(i18n("Marco Martin")); | ||||
46 | setAboutData(aboutData); | 74 | setAboutData(aboutData); | ||
75 | | ||||
76 | m_model = new CursorThemeModel(this); | ||||
77 | | ||||
78 | m_proxyModel = new SortProxyModel(this); | ||||
79 | m_proxyModel->setSourceModel(m_model); | ||||
80 | m_proxyModel->setFilterCaseSensitivity(Qt::CaseSensitive); | ||||
81 | m_proxyModel->sort(NameColumn, Qt::AscendingOrder); | ||||
82 | | ||||
83 | m_sizesModel = new QStandardItemModel(this); | ||||
84 | | ||||
85 | // Disable the install button if we can't install new themes to ~/.icons, | ||||
86 | // or Xcursor isn't set up to look for cursor themes there. | ||||
87 | if (!m_model->searchPaths().contains(QDir::homePath() + "/.icons") || !iconsIsWritable()) { | ||||
88 | setCanInstall(false); | ||||
89 | } | ||||
47 | } | 90 | } | ||
48 | 91 | | |||
49 | CursorThemeConfig::~CursorThemeConfig() | 92 | CursorThemeConfig::~CursorThemeConfig() | ||
50 | { | 93 | { | ||
51 | /* */ | 94 | /* */ | ||
52 | } | 95 | } | ||
53 | 96 | | |||
54 | void CursorThemeConfig::load() | 97 | void CursorThemeConfig::setCanInstall(bool can) | ||
98 | { | ||||
99 | if (m_canInstall == can) { | ||||
100 | return; | ||||
101 | } | ||||
102 | | ||||
103 | m_canInstall = can; | ||||
104 | emit canInstallChanged(); | ||||
105 | } | ||||
106 | | ||||
107 | bool CursorThemeConfig::canInstall() const | ||||
108 | { | ||||
109 | return m_canInstall; | ||||
110 | } | ||||
111 | | ||||
112 | void CursorThemeConfig::setCanResize(bool can) | ||||
113 | { | ||||
114 | if (m_canResize == can) { | ||||
115 | return; | ||||
116 | } | ||||
117 | | ||||
118 | m_canResize = can; | ||||
119 | emit canResizeChanged(); | ||||
120 | } | ||||
121 | | ||||
122 | bool CursorThemeConfig::canResize() const | ||||
123 | { | ||||
124 | return m_canResize; | ||||
125 | } | ||||
126 | | ||||
127 | void CursorThemeConfig::setCanConfigure(bool can) | ||||
128 | { | ||||
129 | if (m_canConfigure == can) { | ||||
130 | return; | ||||
131 | } | ||||
132 | | ||||
133 | m_canConfigure = can; | ||||
134 | emit canConfigureChanged(); | ||||
135 | } | ||||
136 | | ||||
137 | bool CursorThemeConfig::canConfigure() const | ||||
138 | { | ||||
139 | return m_canConfigure; | ||||
140 | } | ||||
141 | | ||||
142 | void CursorThemeConfig::setSelectedThemeRow(int row) | ||||
143 | { | ||||
144 | if (m_selectedThemeRow == row) { | ||||
145 | return; | ||||
146 | } | ||||
147 | | ||||
148 | m_selectedThemeRow = row; | ||||
149 | emit selectedThemeRowChanged(); | ||||
150 | setNeedsSave(true); | ||||
hein: Incorrect Apply button management | |||||
151 | updateSizeComboBox(); | ||||
152 | | ||||
153 | QModelIndex selected = selectedIndex(); | ||||
154 | | ||||
155 | if (selected.isValid()) { | ||||
156 | const CursorTheme *theme = m_proxyModel->theme(selected); | ||||
157 | } | ||||
158 | } | ||||
159 | | ||||
160 | int CursorThemeConfig::selectedThemeRow() const | ||||
161 | { | ||||
162 | return m_selectedThemeRow; | ||||
163 | } | ||||
164 | | ||||
165 | void CursorThemeConfig::setPreferredSize(int size) | ||||
166 | { | ||||
167 | if (m_preferredSize == size) { | ||||
168 | return; | ||||
169 | } | ||||
170 | | ||||
171 | m_preferredSize = size; | ||||
172 | emit preferredSizeChanged(); | ||||
173 | setNeedsSave(true); | ||||
174 | } | ||||
175 | | ||||
176 | int CursorThemeConfig::preferredSize() const | ||||
177 | { | ||||
178 | return m_preferredSize; | ||||
179 | } | ||||
180 | | ||||
181 | | ||||
182 | | ||||
183 | QAbstractItemModel *CursorThemeConfig::cursorsModel() | ||||
184 | { | ||||
185 | return m_proxyModel; | ||||
186 | } | ||||
187 | | ||||
188 | QAbstractItemModel *CursorThemeConfig::sizesModel() | ||||
189 | { | ||||
190 | return m_sizesModel; | ||||
191 | } | ||||
192 | | ||||
193 | bool CursorThemeConfig::iconsIsWritable() const | ||||
194 | { | ||||
195 | const QFileInfo icons = QFileInfo(QDir::homePath() + "/.icons"); | ||||
196 | const QFileInfo home = QFileInfo(QDir::homePath()); | ||||
197 | | ||||
198 | return ((icons.exists() && icons.isDir() && icons.isWritable()) || | ||||
199 | (!icons.exists() && home.isWritable())); | ||||
200 | } | ||||
201 | | ||||
202 | | ||||
203 | void CursorThemeConfig::updateSizeComboBox() | ||||
204 | { | ||||
205 | | ||||
206 | // clear the combo box | ||||
207 | m_sizesModel->clear(); | ||||
208 | | ||||
209 | // refill the combo box and adopt its icon size | ||||
210 | QModelIndex selected = selectedIndex(); | ||||
211 | int maxIconWidth = 0; | ||||
212 | int maxIconHeight = 0; | ||||
213 | if (selected.isValid()) | ||||
214 | { | ||||
215 | const CursorTheme *theme = m_proxyModel->theme(selected); | ||||
216 | const QList<int> sizes = theme->availableSizes(); | ||||
217 | QIcon m_icon; | ||||
218 | if (sizes.size() > 1) // only refill the combobox if there is more that 1 size | ||||
219 | { | ||||
220 | int i; | ||||
221 | QList<int> comboBoxList; | ||||
222 | QPixmap m_pixmap; | ||||
223 | | ||||
224 | // insert the items | ||||
225 | m_pixmap = theme->createIcon(0); | ||||
226 | if (m_pixmap.width() > maxIconWidth) | ||||
227 | maxIconWidth = m_pixmap.width(); | ||||
228 | if (m_pixmap.height() > maxIconHeight) | ||||
229 | maxIconHeight = m_pixmap.height(); | ||||
230 | QStandardItem *item = new QStandardItem(QIcon(m_pixmap), | ||||
231 | i18nc("@item:inlistbox size", "Resolution dependent")); | ||||
232 | item->setData(0); | ||||
233 | m_sizesModel->appendRow(item); | ||||
234 | comboBoxList << 0; | ||||
235 | foreach (i, sizes) | ||||
236 | { | ||||
237 | m_pixmap = theme->createIcon(i); | ||||
238 | if (m_pixmap.width() > maxIconWidth) | ||||
239 | maxIconWidth = m_pixmap.width(); | ||||
240 | if (m_pixmap.height() > maxIconHeight) | ||||
241 | maxIconHeight = m_pixmap.height(); | ||||
242 | QStandardItem *item = new QStandardItem(QIcon(m_pixmap), QString::number(i)); | ||||
243 | item->setData(i); | ||||
244 | m_sizesModel->appendRow(item); | ||||
245 | comboBoxList << i; | ||||
246 | }; | ||||
247 | | ||||
248 | // select an item | ||||
249 | int selectItem = comboBoxList.indexOf(m_preferredSize); | ||||
250 | if (selectItem < 0) // m_preferredSize not available for this theme | ||||
251 | { | ||||
252 | /* Search the value next to m_preferredSize. The first entry (0) | ||||
253 | is ignored. (If m_preferredSize would have been 0, then we | ||||
254 | would had found it yet. As m_preferredSize is not 0, we won't | ||||
255 | default to "automatic size".)*/ | ||||
256 | int j; | ||||
257 | int distance; | ||||
258 | int smallestDistance; | ||||
259 | selectItem = 1; | ||||
260 | j = comboBoxList.value(selectItem); | ||||
261 | smallestDistance = j < m_preferredSize ? m_preferredSize - j : j - m_preferredSize; | ||||
262 | for (int i = 2; i < comboBoxList.size(); ++i) | ||||
263 | { | ||||
264 | j = comboBoxList.value(i); | ||||
265 | distance = j < m_preferredSize ? m_preferredSize - j : j - m_preferredSize; | ||||
266 | if (distance < smallestDistance || (distance == smallestDistance && j > m_preferredSize)) | ||||
267 | { | ||||
268 | smallestDistance = distance; | ||||
269 | selectItem = i; | ||||
270 | }; | ||||
271 | } | ||||
272 | }; | ||||
273 | setPreferredSize(selectItem); | ||||
274 | }; | ||||
275 | }; | ||||
276 | | ||||
277 | // enable or disable the combobox | ||||
278 | KConfig c("kcminputrc"); | ||||
279 | KConfigGroup cg(&c, "Mouse"); | ||||
280 | if (cg.isEntryImmutable("cursorSize")) { | ||||
281 | setCanResize(false); | ||||
282 | } else { | ||||
283 | setCanResize(m_sizesModel->rowCount() > 0); | ||||
284 | }; | ||||
285 | | ||||
286 | } | ||||
287 | | ||||
288 | bool CursorThemeConfig::applyTheme(const CursorTheme *theme, const int size) | ||||
289 | { | ||||
290 | // Require the Xcursor version that shipped with X11R6.9 or greater, since | ||||
291 | // in previous versions the Xfixes code wasn't enabled due to a bug in the | ||||
292 | // build system (freedesktop bug #975). | ||||
293 | #if HAVE_XFIXES && XFIXES_MAJOR >= 2 && XCURSOR_LIB_VERSION >= 10105 | ||||
294 | if (!theme) | ||||
295 | return false; | ||||
296 | | ||||
297 | if (!CursorTheme::haveXfixes()) | ||||
298 | return false; | ||||
299 | | ||||
300 | QByteArray themeName = QFile::encodeName(theme->name()); | ||||
301 | | ||||
302 | // Set up the proper launch environment for newly started apps | ||||
303 | OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), | ||||
304 | QStringLiteral("/KLauncher"), | ||||
305 | QDBusConnection::sessionBus()); | ||||
306 | klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), themeName); | ||||
307 | | ||||
308 | // Update the Xcursor X resources | ||||
309 | runRdb(0); | ||||
310 | | ||||
311 | // Notify all applications that the cursor theme has changed | ||||
312 | KGlobalSettings::self()->emitChange(KGlobalSettings::CursorChanged); | ||||
313 | | ||||
314 | // Reload the standard cursors | ||||
315 | QStringList names; | ||||
316 | | ||||
317 | // Qt cursors | ||||
318 | names << "left_ptr" << "up_arrow" << "cross" << "wait" | ||||
319 | << "left_ptr_watch" << "ibeam" << "size_ver" << "size_hor" | ||||
320 | << "size_bdiag" << "size_fdiag" << "size_all" << "split_v" | ||||
321 | << "split_h" << "pointing_hand" << "openhand" | ||||
322 | << "closedhand" << "forbidden" << "whats_this" << "copy" << "move" << "link"; | ||||
323 | | ||||
324 | // X core cursors | ||||
325 | names << "X_cursor" << "right_ptr" << "hand1" | ||||
326 | << "hand2" << "watch" << "xterm" | ||||
327 | << "crosshair" << "left_ptr_watch" << "center_ptr" | ||||
328 | << "sb_h_double_arrow" << "sb_v_double_arrow" << "fleur" | ||||
329 | << "top_left_corner" << "top_side" << "top_right_corner" | ||||
330 | << "right_side" << "bottom_right_corner" << "bottom_side" | ||||
331 | << "bottom_left_corner" << "left_side" << "question_arrow" | ||||
332 | << "pirate"; | ||||
333 | | ||||
334 | foreach (const QString &name, names) | ||||
55 | { | 335 | { | ||
56 | themepage->load(); | 336 | XFixesChangeCursorByName(QX11Info::display(), theme->loadCursor(name, size), QFile::encodeName(name)); | ||
57 | emit changed(false); | | |||
58 | } | 337 | } | ||
59 | 338 | | |||
339 | return true; | ||||
340 | #else | ||||
341 | Q_UNUSED(theme) | ||||
342 | return false; | ||||
343 | #endif | ||||
344 | } | ||||
345 | | ||||
346 | | ||||
60 | void CursorThemeConfig::save() | 347 | void CursorThemeConfig::save() | ||
61 | { | 348 | { | ||
62 | themepage->save(); | 349 | const CursorTheme *theme = selectedIndex().isValid() ? m_proxyModel->theme(selectedIndex()) : NULL; | ||
63 | emit changed(false); | 350 | | ||
351 | KConfig config("kcminputrc"); | ||||
352 | KConfigGroup c(&config, "Mouse"); | ||||
353 | if (theme) | ||||
354 | { | ||||
355 | c.writeEntry("cursorTheme", theme->name()); | ||||
356 | }; | ||||
357 | c.writeEntry("cursorSize", m_preferredSize); | ||||
358 | c.sync(); | ||||
359 | | ||||
360 | if (!applyTheme(theme, m_preferredSize)) | ||||
361 | { | ||||
362 | KMessageBox::information(0, | ||||
363 | i18n("You have to restart KDE for these changes to take effect."), | ||||
364 | i18n("Cursor Settings Changed"), "CursorSettingsChanged"); | ||||
365 | } | ||||
366 | | ||||
367 | m_appliedIndex = selectedIndex(); | ||||
368 | m_appliedSize = m_preferredSize; | ||||
369 | setNeedsSave(false); | ||||
64 | } | 370 | } | ||
65 | 371 | | |||
372 | | ||||
373 | void CursorThemeConfig::load() | ||||
374 | { | ||||
375 | | ||||
376 | // Get the name of the theme libXcursor currently uses | ||||
377 | QString currentTheme; | ||||
378 | if (QX11Info::isPlatformX11()) { | ||||
379 | currentTheme = XcursorGetTheme(QX11Info::display()); | ||||
380 | } | ||||
381 | | ||||
382 | // Get the name of the theme KDE is configured to use | ||||
383 | KConfig c("kcminputrc"); | ||||
384 | KConfigGroup cg(&c, "Mouse"); | ||||
385 | currentTheme = cg.readEntry("cursorTheme", currentTheme); | ||||
386 | | ||||
387 | // Find the theme in the listview | ||||
388 | if (!currentTheme.isEmpty()) | ||||
389 | m_appliedIndex = m_proxyModel->findIndex(currentTheme); | ||||
390 | else | ||||
391 | m_appliedIndex = m_proxyModel->defaultIndex(); | ||||
392 | | ||||
393 | // Disable the listview and the buttons if we're in kiosk mode | ||||
394 | if (cg.isEntryImmutable("cursorTheme")) | ||||
395 | { | ||||
396 | setCanConfigure(false); | ||||
397 | setCanInstall(false); | ||||
398 | } | ||||
399 | | ||||
400 | // Load cursor size | ||||
401 | int size = cg.readEntry("cursorSize", 0); | ||||
402 | if (size <= 0) | ||||
403 | m_preferredSize = 0; | ||||
404 | else | ||||
405 | m_preferredSize = size; | ||||
406 | updateSizeComboBox(); // This handles also the kiosk mode | ||||
407 | | ||||
408 | m_appliedSize = size; | ||||
409 | | ||||
410 | const CursorTheme *theme = m_proxyModel->theme(m_appliedIndex); | ||||
411 | | ||||
412 | setSelectedThemeRow(m_appliedIndex.row()); | ||||
413 | | ||||
414 | setNeedsSave(false); | ||||
415 | } | ||||
416 | | ||||
417 | | ||||
66 | void CursorThemeConfig::defaults() | 418 | void CursorThemeConfig::defaults() | ||
67 | { | 419 | { | ||
68 | themepage->defaults(); | 420 | QModelIndex defaultIndex = m_proxyModel->findIndex("breeze_cursors"); | ||
69 | changed(); | 421 | setSelectedThemeRow(defaultIndex.row()); | ||
422 | m_preferredSize = 0; | ||||
423 | updateSizeComboBox(); | ||||
424 | setNeedsSave(true); | ||||
425 | } | ||||
426 | | ||||
427 | | ||||
428 | void CursorThemeConfig::selectionChanged() | ||||
429 | { | ||||
430 | updateSizeComboBox(); | ||||
431 | | ||||
432 | setNeedsSave(m_appliedIndex != selectedIndex()); | ||||
433 | } | ||||
434 | | ||||
435 | QModelIndex CursorThemeConfig::selectedIndex() const | ||||
436 | { | ||||
437 | return m_proxyModel->index(m_selectedThemeRow, 0); | ||||
438 | } | ||||
439 | | ||||
440 | void CursorThemeConfig::getNewClicked() | ||||
441 | { | ||||
442 | KNS3::DownloadDialog dialog("xcursor.knsrc", 0); | ||||
443 | if (dialog.exec()) { | ||||
444 | KNS3::Entry::List list = dialog.changedEntries(); | ||||
445 | if (list.count() > 0) | ||||
446 | m_model->refreshList(); | ||||
447 | } | ||||
448 | } | ||||
449 | | ||||
450 | void CursorThemeConfig::installClicked() | ||||
451 | { | ||||
452 | // Get the URL for the theme we're going to install | ||||
453 | QUrl url = KUrlRequesterDialog::getUrl(QUrl(), 0, i18n("Drag or Type Theme URL")); | ||||
454 | | ||||
455 | if (url.isEmpty()) | ||||
456 | return; | ||||
457 | | ||||
458 | QString tempFile; | ||||
459 | if (!KIO::NetAccess::download(url, tempFile, 0)) | ||||
460 | { | ||||
461 | QString text; | ||||
462 | | ||||
463 | if (url.isLocalFile()) | ||||
464 | text = i18n("Unable to find the cursor theme archive %1.", | ||||
465 | url.toDisplayString()); | ||||
466 | else | ||||
467 | text = i18n("Unable to download the cursor theme archive; " | ||||
468 | "please check that the address %1 is correct.", | ||||
469 | url.toDisplayString()); | ||||
470 | | ||||
471 | KMessageBox::sorry(0, text); | ||||
472 | return; | ||||
473 | } | ||||
474 | | ||||
475 | if (!installThemes(tempFile)) | ||||
476 | KMessageBox::error(0, i18n("The file %1 does not appear to be a valid " | ||||
477 | "cursor theme archive.", url.fileName())); | ||||
478 | | ||||
479 | KIO::NetAccess::removeTempFile(tempFile); | ||||
480 | } | ||||
481 | | ||||
482 | | ||||
483 | void CursorThemeConfig::removeTheme(int row) | ||||
484 | { | ||||
485 | QModelIndex idx = m_proxyModel->index(row, 0); | ||||
486 | if (!idx.isValid()) { | ||||
487 | return; | ||||
488 | } | ||||
489 | | ||||
490 | const CursorTheme *theme = m_proxyModel->theme(idx); | ||||
491 | | ||||
492 | // Don't let the user delete the currently configured theme | ||||
493 | if (idx == m_appliedIndex) { | ||||
494 | KMessageBox::sorry(0, i18n("<qt>You cannot delete the theme you are currently " | ||||
495 | "using.<br />You have to switch to another theme first.</qt>")); | ||||
496 | return; | ||||
497 | } | ||||
498 | | ||||
499 | // Get confirmation from the user | ||||
500 | QString question = i18n("<qt>Are you sure you want to remove the " | ||||
501 | "<i>%1</i> cursor theme?<br />" | ||||
502 | "This will delete all the files installed by this theme.</qt>", | ||||
503 | theme->title()); | ||||
504 | | ||||
505 | int answer = KMessageBox::warningContinueCancel(0, question, | ||||
506 | i18n("Confirmation"), KStandardGuiItem::del()); | ||||
507 | | ||||
508 | if (answer != KMessageBox::Continue) | ||||
509 | return; | ||||
510 | | ||||
511 | // Delete the theme from the harddrive | ||||
512 | KIO::del(QUrl::fromLocalFile(theme->path())); // async | ||||
513 | | ||||
514 | // Remove the theme from the model | ||||
515 | m_proxyModel->removeTheme(idx); | ||||
516 | | ||||
517 | // TODO: | ||||
518 | // Since it's possible to substitute cursors in a system theme by adding a local | ||||
519 | // theme with the same name, we shouldn't remove the theme from the list if it's | ||||
520 | // still available elsewhere. We could add a | ||||
521 | // bool CursorThemeModel::tryAddTheme(const QString &name), and call that, but | ||||
522 | // since KIO::del() is an asynchronos operation, the theme we're deleting will be | ||||
523 | // readded to the list again before KIO has removed it. | ||||
524 | } | ||||
525 | | ||||
526 | | ||||
527 | bool CursorThemeConfig::installThemes(const QString &file) | ||||
528 | { | ||||
529 | KTar archive(file); | ||||
530 | | ||||
531 | if (!archive.open(QIODevice::ReadOnly)) | ||||
532 | return false; | ||||
533 | | ||||
534 | const KArchiveDirectory *archiveDir = archive.directory(); | ||||
535 | QStringList themeDirs; | ||||
536 | | ||||
537 | // Extract the dir names of the cursor themes in the archive, and | ||||
538 | // append them to themeDirs | ||||
539 | foreach(const QString &name, archiveDir->entries()) | ||||
540 | { | ||||
541 | const KArchiveEntry *entry = archiveDir->entry(name); | ||||
542 | if (entry->isDirectory() && entry->name().toLower() != "default") | ||||
543 | { | ||||
544 | const KArchiveDirectory *dir = static_cast<const KArchiveDirectory *>(entry); | ||||
545 | if (dir->entry("index.theme") && dir->entry("cursors")) | ||||
546 | themeDirs << dir->name(); | ||||
547 | } | ||||
548 | } | ||||
549 | | ||||
550 | if (themeDirs.isEmpty()) | ||||
hein: Coding style (many other places) | |||||
551 | return false; | ||||
552 | | ||||
553 | // The directory we'll install the themes to | ||||
554 | QString destDir = QDir::homePath() + "/.icons/"; | ||||
555 | QDir().mkpath(destDir); // Make sure the directory exists | ||||
556 | | ||||
557 | // Process each cursor theme in the archive | ||||
558 | foreach (const QString &dirName, themeDirs) | ||||
559 | { | ||||
560 | QDir dest(destDir + dirName); | ||||
561 | if (dest.exists()) | ||||
562 | { | ||||
563 | QString question = i18n("A theme named %1 already exists in your icon " | ||||
564 | "theme folder. Do you want replace it with this one?", dirName); | ||||
565 | | ||||
566 | int answer = KMessageBox::warningContinueCancel(0, question, | ||||
567 | i18n("Overwrite Theme?"), | ||||
568 | KStandardGuiItem::overwrite()); | ||||
569 | | ||||
570 | if (answer != KMessageBox::Continue) | ||||
571 | continue; | ||||
572 | | ||||
573 | // ### If the theme that's being replaced is the current theme, it | ||||
574 | // will cause cursor inconsistencies in newly started apps. | ||||
575 | } | ||||
576 | | ||||
577 | // ### Should we check if a theme with the same name exists in a global theme dir? | ||||
578 | // If that's the case it will effectively replace it, even though the global theme | ||||
579 | // won't be deleted. Checking for this situation is easy, since the global theme | ||||
580 | // will be in the listview. Maybe this should never be allowed since it might | ||||
581 | // result in strange side effects (from the average users point of view). OTOH | ||||
582 | // a user might want to do this 'upgrade' a global theme. | ||||
583 | | ||||
584 | const KArchiveDirectory *dir = static_cast<const KArchiveDirectory*> | ||||
585 | (archiveDir->entry(dirName)); | ||||
586 | dir->copyTo(dest.path()); | ||||
587 | m_model->addTheme(dest); | ||||
588 | } | ||||
589 | | ||||
590 | archive.close(); | ||||
591 | return true; | ||||
70 | } | 592 | } | ||
71 | 593 | | |||
72 | #include "kcmcursortheme.moc" | 594 | #include "kcmcursortheme.moc" |
Incorrect Apply button management