Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / runtime / kcontrol / icons / iconthemes.cpp
blob5b6ac77b72c33b4ad37f281d3d8c6ca1996fad43
1 /**
2 * Copyright (c) 2000 Antonio Larrosa <larrosa@kde.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
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
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include <config-runtime.h>
21 #include <stdlib.h>
22 #include <unistd.h>
24 #include <QFileInfo>
25 #include <QLayout>
26 #include <QLabel>
27 #include <QPushButton>
28 //Added by qt3to4:
29 #include <QPixmap>
30 #include <QVBoxLayout>
31 #include <QFrame>
32 #include <QHBoxLayout>
33 #include <QTreeWidget>
35 #include <kdebug.h>
36 #include <kapplication.h>
37 #include <kbuildsycocaprogressdialog.h>
38 #include <klocale.h>
39 #include <kstandarddirs.h>
40 #include <kservice.h>
41 #include <kconfig.h>
42 #undef Unsorted
44 #include <kurlrequesterdialog.h>
45 #include <kmessagebox.h>
46 #include <kiconloader.h>
47 #include <k3icon_p.h> // this private header is only installed for us!
48 #include <kprogressdialog.h>
49 #include <kio/job.h>
50 #include <kio/deletejob.h>
51 #include <kio/netaccess.h>
52 #include <ktar.h>
54 #ifdef HAVE_LIBAGG
55 #include <ksvgiconengine.h>
56 #endif
58 #include "iconthemes.h"
59 #include <kglobalsettings.h>
61 static const int ThemeNameRole = Qt::UserRole + 1;
63 IconThemesConfig::IconThemesConfig(const KComponentData &inst, QWidget *parent)
64 : KCModule(inst, parent)
66 QVBoxLayout *topLayout = new QVBoxLayout(this);
67 topLayout->setMargin(KDialog::marginHint());
68 topLayout->setSpacing(KDialog::spacingHint());
70 QFrame *m_preview=new QFrame(this);
71 m_preview->setMinimumHeight(50);
73 QHBoxLayout *lh2=new QHBoxLayout( m_preview );
74 m_previewExec=new QLabel(m_preview);
75 m_previewExec->setPixmap(DesktopIcon("system-run"));
76 m_previewFolder=new QLabel(m_preview);
77 m_previewFolder->setPixmap(DesktopIcon("folder"));
78 m_previewDocument=new QLabel(m_preview);
79 m_previewDocument->setPixmap(DesktopIcon("document"));
81 lh2->addStretch(10);
82 lh2->addWidget(m_previewExec);
83 lh2->addStretch(1);
84 lh2->addWidget(m_previewFolder);
85 lh2->addStretch(1);
86 lh2->addWidget(m_previewDocument);
87 lh2->addStretch(10);
90 m_iconThemes=new QTreeWidget(this/*"IconThemeList"*/);
91 QStringList columns;
92 columns.append(i18n("Name"));
93 columns.append(i18n("Description"));
94 m_iconThemes->setHeaderLabels(columns);
95 m_iconThemes->setAllColumnsShowFocus( true );
96 m_iconThemes->setRootIsDecorated(false);
97 m_iconThemes->setSortingEnabled(true);
98 connect(m_iconThemes,SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
99 SLOT(themeSelected(QTreeWidgetItem *)));
101 QPushButton *installButton=new QPushButton( i18n("Install New Theme..."), this);
102 installButton->setObjectName("InstallNewTheme");
103 connect(installButton,SIGNAL(clicked()),SLOT(installNewTheme()));
104 m_removeButton=new QPushButton( i18n("Remove Theme"), this);
105 m_removeButton->setObjectName("RemoveTheme");
106 connect(m_removeButton,SIGNAL(clicked()),SLOT(removeSelectedTheme()));
108 topLayout->addWidget(
109 new QLabel(i18n("Select the icon theme you want to use:"), this));
110 topLayout->addWidget(m_preview);
111 topLayout->addWidget(m_iconThemes);
112 QHBoxLayout *lg = new QHBoxLayout();
113 topLayout->addItem(lg);
114 lg->setSpacing(KDialog::spacingHint());
115 lg->addWidget(installButton);
116 lg->addWidget(m_removeButton);
118 loadThemes();
120 m_defaultTheme=iconThemeItem(KIconTheme::current());
121 if (m_defaultTheme)
122 m_iconThemes->setCurrentItem(m_defaultTheme);
123 updateRemoveButton();
125 load();
127 m_iconThemes->setFocus();
130 IconThemesConfig::~IconThemesConfig()
134 QTreeWidgetItem *IconThemesConfig::iconThemeItem(const QString &name)
136 for (int i = 0; i < m_iconThemes->topLevelItemCount(); ++i)
137 if (m_iconThemes->topLevelItem(i)->data(0, ThemeNameRole).toString()==name)
138 return m_iconThemes->topLevelItem(i);
140 return 0L;
143 void IconThemesConfig::loadThemes()
145 m_iconThemes->clear();
146 QStringList themelist(KIconTheme::list());
147 QString name;
148 QString tname;
149 QStringList::Iterator it;
150 QMap <QString, QString> themeNames;
151 for (it=themelist.begin(); it != themelist.end(); ++it)
153 KIconTheme icontheme(*it);
154 if (!icontheme.isValid()) kDebug() << "notvalid\n";
155 if (icontheme.isHidden()) continue;
157 name=icontheme.name();
158 tname=name;
160 // Just in case we have duplicated icon theme names on separate directories
161 for (int i = 2; themeNames.find(tname) != themeNames.end(); ++i)
162 tname=QString("%1-%2").arg(name).arg(i);
164 QTreeWidgetItem *newitem = new QTreeWidgetItem();
165 newitem->setText(0, name);
166 newitem->setText(1, icontheme.description());
167 newitem->setData(0, ThemeNameRole, *it);
168 m_iconThemes->addTopLevelItem(newitem);
170 themeNames.insert(name, *it);
174 void IconThemesConfig::installNewTheme()
176 KUrl themeURL = KUrlRequesterDialog::getUrl(QString(), this,
177 i18n("Drag or Type Theme URL"));
178 kDebug() << themeURL.prettyUrl();
180 if (themeURL.url().isEmpty()) return;
182 QString themeTmpFile;
183 // themeTmpFile contains the name of the downloaded file
185 if (!KIO::NetAccess::download(themeURL, themeTmpFile, this)) {
186 QString sorryText;
187 if (themeURL.isLocalFile())
188 sorryText = i18n("Unable to find the icon theme archive %1.",
189 themeURL.prettyUrl());
190 else
191 sorryText = i18n("Unable to download the icon theme archive;\n"
192 "please check that address %1 is correct.",
193 themeURL.prettyUrl());
194 KMessageBox::sorry(this, sorryText);
195 return;
198 QStringList themesNames = findThemeDirs(themeTmpFile);
199 if (themesNames.isEmpty()) {
200 QString invalidArch(i18n("The file is not a valid icon theme archive."));
201 KMessageBox::error(this, invalidArch);
203 KIO::NetAccess::removeTempFile(themeTmpFile);
204 return;
207 if (!installThemes(themesNames, themeTmpFile)) {
208 //FIXME: make me able to know what is wrong....
209 // QStringList instead of bool?
210 QString somethingWrong =
211 i18n("A problem occurred during the installation process; "
212 "however, most of the themes in the archive have been installed");
213 KMessageBox::error(this, somethingWrong);
216 KIO::NetAccess::removeTempFile(themeTmpFile);
218 KIconLoader::global()->newIconLoader();
219 loadThemes();
221 QTreeWidgetItem *item=iconThemeItem(KIconTheme::current());
222 if (item)
223 m_iconThemes->setCurrentItem(item);
224 updateRemoveButton();
227 bool IconThemesConfig::installThemes(const QStringList &themes, const QString &archiveName)
229 bool everythingOk = true;
230 QString localThemesDir(KStandardDirs::locateLocal("icon", "./"));
232 KProgressDialog progressDiag(this,
233 i18n("Installing icon themes"),
234 QString());
235 progressDiag.setModal(true);
236 progressDiag.setAutoClose(true);
237 QProgressBar* progressBar = progressDiag.progressBar();
238 progressBar->setMaximum(themes.count());
239 progressDiag.show();
241 KTar archive(archiveName);
242 archive.open(QIODevice::ReadOnly);
243 kapp->processEvents();
245 const KArchiveDirectory* rootDir = archive.directory();
247 KArchiveDirectory* currentTheme;
248 for (QStringList::ConstIterator it = themes.begin();
249 it != themes.end();
250 ++it) {
251 progressDiag.setLabelText(
252 i18n("<qt>Installing <strong>%1</strong> theme</qt>",
253 *it));
254 kapp->processEvents();
256 if (progressDiag.wasCancelled())
257 break;
259 currentTheme = dynamic_cast<KArchiveDirectory*>(
260 const_cast<KArchiveEntry*>(
261 rootDir->entry(*it)));
262 if (currentTheme == NULL) {
263 // we tell back that something went wrong, but try to install as much
264 // as possible
265 everythingOk = false;
266 continue;
269 currentTheme->copyTo(localThemesDir + *it);
270 progressBar->setValue(progressBar->value()+1);
273 archive.close();
274 return everythingOk;
277 QStringList IconThemesConfig::findThemeDirs(const QString &archiveName)
279 QStringList foundThemes;
281 KTar archive(archiveName);
282 archive.open(QIODevice::ReadOnly);
283 const KArchiveDirectory* themeDir = archive.directory();
285 KArchiveEntry* possibleDir = 0L;
286 KArchiveDirectory* subDir = 0L;
288 // iterate all the dirs looking for an index.theme or index.desktop file
289 QStringList entries = themeDir->entries();
290 for (QStringList::Iterator it = entries.begin();
291 it != entries.end();
292 ++it) {
293 possibleDir = const_cast<KArchiveEntry*>(themeDir->entry(*it));
294 if (possibleDir->isDirectory()) {
295 subDir = dynamic_cast<KArchiveDirectory*>( possibleDir );
296 if (subDir && (subDir->entry("index.theme") != NULL ||
297 subDir->entry("index.desktop") != NULL))
298 foundThemes.append(subDir->name());
302 archive.close();
303 return foundThemes;
306 void IconThemesConfig::removeSelectedTheme()
308 QTreeWidgetItem *selected = m_iconThemes->currentItem();
309 if (!selected)
310 return;
312 QString question=i18n("<qt>Are you sure you want to remove the "
313 "<strong>%1</strong> icon theme?<br />"
314 "<br />"
315 "This will delete the files installed by this theme.</qt>",
316 selected->text(0));
318 bool deletingCurrentTheme=(selected==iconThemeItem(KIconTheme::current()));
320 int r=KMessageBox::warningContinueCancel(this,question,i18n("Confirmation"),KStandardGuiItem::del());
321 if (r!=KMessageBox::Continue) return;
323 KIconTheme icontheme(selected->data(0, ThemeNameRole).toString());
325 // delete the index file before the async KIO::del so loadThemes() will
326 // ignore that dir.
327 unlink(QFile::encodeName(icontheme.dir()+"/index.theme").data());
328 unlink(QFile::encodeName(icontheme.dir()+"/index.desktop").data());
329 KIO::del(KUrl( icontheme.dir() ));
331 KIconLoader::global()->newIconLoader();
333 loadThemes();
335 QTreeWidgetItem *item=0L;
336 //Fallback to the default if we've deleted the current theme
337 if (!deletingCurrentTheme)
338 item=iconThemeItem(KIconTheme::current());
339 if (!item)
340 item=iconThemeItem(KIconTheme::defaultThemeName());
342 if (item)
343 m_iconThemes->setCurrentItem(item);
344 updateRemoveButton();
346 if (deletingCurrentTheme) // Change the configuration
347 save();
350 void IconThemesConfig::updateRemoveButton()
352 QTreeWidgetItem *selected = m_iconThemes->currentItem();
353 bool enabled = false;
354 if (selected)
356 QString selectedtheme = selected->data(0, ThemeNameRole).toString();
357 KIconTheme icontheme(selectedtheme);
358 QFileInfo fi(icontheme.dir());
359 enabled = fi.isWritable();
360 // Don't let users remove the current theme.
361 if (selectedtheme == KIconTheme::current() ||
362 selectedtheme == KIconTheme::defaultThemeName())
363 enabled = false;
365 m_removeButton->setEnabled(enabled);
368 void IconThemesConfig::themeSelected(QTreeWidgetItem *item)
370 if (!item) return;
372 #ifdef HAVE_LIBAGG
373 KSVGIconEngine engine;
374 #endif
375 QString dirName(item->data(0, ThemeNameRole).toString());
376 KIconTheme icontheme(dirName);
377 if (!icontheme.isValid()) kDebug() << "notvalid\n";
379 updateRemoveButton();
380 const int size = icontheme.defaultSize(KIconLoader::Desktop);
382 K3Icon icon=icontheme.iconPath("exec.png", size, KIconLoader::MatchBest);
383 if (!icon.isValid()) {
384 #ifdef HAVE_LIBAGG
385 icon=icontheme.iconPath("exec.svg", size, KIconLoader::MatchBest);
386 if(engine.load(size, size, icon.path))
387 m_previewExec->setPixmap(QPixmap(*engine.image()));
388 else {
389 icon=icontheme.iconPath("exec.svgz", size, KIconLoader::MatchBest);
390 if(engine.load(size, size, icon.path))
391 m_previewExec->setPixmap(QPixmap(*engine.image()));
393 #endif
395 else
396 m_previewExec->setPixmap(QPixmap(icon.path));
398 icon=icontheme.iconPath("folder.png",size,KIconLoader::MatchBest);
399 if (!icon.isValid()) {
400 #ifdef HAVE_LIBAGG
401 icon=icontheme.iconPath("folder.svg", size, KIconLoader::MatchBest);
402 if(engine.load(size, size, icon.path))
403 m_previewFolder->setPixmap(QPixmap(*engine.image()));
404 else {
405 icon=icontheme.iconPath("folder.svgz", size, KIconLoader::MatchBest);
406 if(engine.load(size, size, icon.path))
407 m_previewFolder->setPixmap(QPixmap(*engine.image()));
409 #endif
411 else
412 m_previewFolder->setPixmap(QPixmap(icon.path));
414 icon=icontheme.iconPath("text-x-generic.png",size,KIconLoader::MatchBest);
415 if (!icon.isValid()) {
416 #ifdef HAVE_LIBAGG
417 icon=icontheme.iconPath("text-x-generic.svg", size, KIconLoader::MatchBest);
418 if(engine.load(size, size, icon.path))
419 m_previewDocument->setPixmap(QPixmap(*engine.image()));
420 else {
421 icon=icontheme.iconPath("text-x-generic.svgz", size, KIconLoader::MatchBest);
422 if(engine.load(size, size, icon.path))
423 m_previewDocument->setPixmap(QPixmap(*engine.image()));
425 #endif
427 else
428 m_previewDocument->setPixmap(QPixmap(icon.path));
430 emit changed(true);
431 m_bChanged = true;
434 void IconThemesConfig::load()
436 emit changed(false);
437 m_bChanged = false;
440 void IconThemesConfig::save()
442 if (!m_bChanged)
443 return;
444 QTreeWidgetItem *selected = m_iconThemes->currentItem();
445 if (!selected)
446 return;
448 KConfigGroup config(KSharedConfig::openConfig("kdeglobals", KConfig::SimpleConfig), "Icons");
450 config.writeEntry("Theme", selected->data(0, ThemeNameRole).toString());
451 KIconTheme::reconfigure();
452 emit changed(false);
454 for (int i=0; i<KIconLoader::LastGroup; i++)
456 KGlobalSettings::self()->emitChange(KGlobalSettings::IconChanged, i);
459 KBuildSycocaProgressDialog::rebuildKSycoca(this);
461 m_bChanged = false;
462 m_removeButton->setEnabled(false);
465 void IconThemesConfig::defaults()
467 if (m_iconThemes->currentItem()==m_defaultTheme) return;
469 if (m_defaultTheme)
470 m_iconThemes->setCurrentItem(m_defaultTheme);
471 updateRemoveButton();
473 emit changed(true);
474 m_bChanged = true;
477 #include "iconthemes.moc"