add more spacing
[personal-kdebase.git] / runtime / kcontrol / icons / iconthemes.cpp
blob473d13d00c2967184c14e364b17f05422850c856
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 //Added by qt3to4:
28 #include <QPixmap>
29 #include <QVBoxLayout>
30 #include <QFrame>
31 #include <QHBoxLayout>
32 #include <QTreeWidget>
33 #include <QPainter>
35 #include <kdebug.h>
36 #include <kapplication.h>
37 #include <kbuildsycocaprogressdialog.h>
38 #include <klocale.h>
39 #include <kiconcache.h>
40 #include <kpushbutton.h>
41 #include <ksvgrenderer.h>
42 #include <kstandarddirs.h>
43 #include <kservice.h>
44 #include <kconfig.h>
45 #include <knewstuff2/engine.h>
47 #undef Unsorted
49 #include <kurlrequesterdialog.h>
50 #include <kmessagebox.h>
51 #include <kiconloader.h>
52 #include <k3icon_p.h> // this private header is only installed for us!
53 #include <kprogressdialog.h>
54 #include <kio/job.h>
55 #include <kio/deletejob.h>
56 #include <kio/netaccess.h>
57 #include <ktar.h>
59 #include "iconthemes.h"
60 #include <kglobalsettings.h>
62 static const int ThemeNameRole = Qt::UserRole + 1;
64 IconThemesConfig::IconThemesConfig(const KComponentData &inst, QWidget *parent)
65 : KCModule(inst, parent)
67 QVBoxLayout *topLayout = new QVBoxLayout(this);
68 topLayout->setMargin(KDialog::marginHint());
69 topLayout->setSpacing(KDialog::spacingHint());
71 QFrame *m_preview=new QFrame(this);
72 m_preview->setMinimumHeight(80);
74 QHBoxLayout *lh2=new QHBoxLayout( m_preview );
75 lh2->setSpacing(0);
76 m_previewExec=new QLabel(m_preview);
77 m_previewExec->setPixmap(DesktopIcon("system-run"));
78 m_previewFolder=new QLabel(m_preview);
79 m_previewFolder->setPixmap(DesktopIcon("folder"));
80 m_previewDocument=new QLabel(m_preview);
81 m_previewDocument->setPixmap(DesktopIcon("document"));
83 lh2->addStretch(10);
84 lh2->addWidget(m_previewExec);
85 lh2->addStretch(1);
86 lh2->addWidget(m_previewFolder);
87 lh2->addStretch(1);
88 lh2->addWidget(m_previewDocument);
89 lh2->addStretch(10);
92 m_iconThemes=new QTreeWidget(this/*"IconThemeList"*/);
93 QStringList columns;
94 columns.append(i18n("Name"));
95 columns.append(i18n("Description"));
96 m_iconThemes->setHeaderLabels(columns);
97 m_iconThemes->setAllColumnsShowFocus( true );
98 m_iconThemes->setRootIsDecorated(false);
99 m_iconThemes->setSortingEnabled(true);
100 connect(m_iconThemes,SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
101 SLOT(themeSelected(QTreeWidgetItem *)));
103 KPushButton *installButton=new KPushButton( KIcon("document-import"), i18n("Install Theme File..."), this);
104 installButton->setObjectName("InstallNewTheme");
105 installButton->setToolTip(i18n("Install a theme archive file you already have locally"));
106 installButton->setWhatsThis(i18n("If you already have a theme archive locally, this button will unpack it and make it available for KDE applications"));
107 connect(installButton,SIGNAL(clicked()),SLOT(installNewTheme()));
109 KPushButton *newButton=new KPushButton( KIcon("get-hot-new-stuff"), i18n("Get New Themes..."), this);
110 newButton->setObjectName("GetNewTheme");
111 newButton->setToolTip(i18n("Get new themes from the Internet"));
112 newButton->setWhatsThis(i18n("You need to be connected to the Internet to use this action. A dialog will display a list of themes from the http://www.kde.org website. Clicking the Install button associated with a theme will install this theme locally."));
113 connect(newButton,SIGNAL(clicked()),SLOT(getNewTheme()));
115 m_removeButton=new KPushButton( KIcon("edit-delete"), i18n("Remove Theme"), this);
116 m_removeButton->setObjectName("RemoveTheme");
117 m_removeButton->setToolTip(i18n("Remove the selected theme from your disk"));
118 m_removeButton->setWhatsThis(i18n("This will remove the selected theme from your disk."));
119 connect(m_removeButton,SIGNAL(clicked()),SLOT(removeSelectedTheme()));
121 topLayout->addWidget(
122 new QLabel(i18n("Select the icon theme you want to use:"), this));
123 topLayout->addWidget(m_preview);
124 topLayout->addWidget(m_iconThemes);
125 QHBoxLayout *lg = new QHBoxLayout();
126 topLayout->addItem(lg);
127 lg->setSpacing(KDialog::spacingHint());
128 lg->addWidget(newButton);
129 lg->addWidget(installButton);
130 lg->addWidget(m_removeButton);
132 loadThemes();
134 m_defaultTheme=iconThemeItem(KIconTheme::current());
135 if (m_defaultTheme)
136 m_iconThemes->setCurrentItem(m_defaultTheme);
137 updateRemoveButton();
139 m_iconThemes->setFocus();
142 IconThemesConfig::~IconThemesConfig()
146 QTreeWidgetItem *IconThemesConfig::iconThemeItem(const QString &name)
148 for (int i = 0; i < m_iconThemes->topLevelItemCount(); ++i)
149 if (m_iconThemes->topLevelItem(i)->data(0, ThemeNameRole).toString()==name)
150 return m_iconThemes->topLevelItem(i);
152 return 0L;
155 void IconThemesConfig::loadThemes()
157 m_iconThemes->clear();
158 const QStringList themelist(KIconTheme::list());
159 QString name;
160 QString tname;
161 QStringList::const_iterator it;
162 QMap <QString, QString> themeNames;
163 for (it=themelist.constBegin(); it != themelist.constEnd(); ++it)
165 KIconTheme icontheme(*it);
166 if (!icontheme.isValid()) kDebug() << "notvalid\n";
167 if (icontheme.isHidden()) continue;
169 name=icontheme.name();
170 tname=name;
172 // Just in case we have duplicated icon theme names on separate directories
173 for (int i = 2; themeNames.find(tname) != themeNames.end(); ++i)
174 tname=QString("%1-%2").arg(name).arg(i);
176 QTreeWidgetItem *newitem = new QTreeWidgetItem();
177 newitem->setText(0, name);
178 newitem->setText(1, icontheme.description());
179 newitem->setData(0, ThemeNameRole, *it);
180 m_iconThemes->addTopLevelItem(newitem);
182 themeNames.insert(name, *it);
186 void IconThemesConfig::installNewTheme()
188 KUrl themeURL = KUrlRequesterDialog::getUrl(QString(), this,
189 i18n("Drag or Type Theme URL"));
190 kDebug() << themeURL.prettyUrl();
192 if (themeURL.url().isEmpty()) return;
194 QString themeTmpFile;
195 // themeTmpFile contains the name of the downloaded file
197 if (!KIO::NetAccess::download(themeURL, themeTmpFile, this)) {
198 QString sorryText;
199 if (themeURL.isLocalFile())
200 sorryText = i18n("Unable to find the icon theme archive %1.",
201 themeURL.prettyUrl());
202 else
203 sorryText = i18n("Unable to download the icon theme archive;\n"
204 "please check that address %1 is correct.",
205 themeURL.prettyUrl());
206 KMessageBox::sorry(this, sorryText);
207 return;
210 QStringList themesNames = findThemeDirs(themeTmpFile);
211 if (themesNames.isEmpty()) {
212 QString invalidArch(i18n("The file is not a valid icon theme archive."));
213 KMessageBox::error(this, invalidArch);
215 KIO::NetAccess::removeTempFile(themeTmpFile);
216 return;
219 if (!installThemes(themesNames, themeTmpFile)) {
220 //FIXME: make me able to know what is wrong....
221 // QStringList instead of bool?
222 QString somethingWrong =
223 i18n("A problem occurred during the installation process; "
224 "however, most of the themes in the archive have been installed");
225 KMessageBox::error(this, somethingWrong);
228 KIO::NetAccess::removeTempFile(themeTmpFile);
230 KIconLoader::global()->newIconLoader();
231 loadThemes();
233 QTreeWidgetItem *item=iconThemeItem(KIconTheme::current());
234 if (item)
235 m_iconThemes->setCurrentItem(item);
236 updateRemoveButton();
239 bool IconThemesConfig::installThemes(const QStringList &themes, const QString &archiveName)
241 bool everythingOk = true;
242 QString localThemesDir(KStandardDirs::locateLocal("icon", "./"));
244 KProgressDialog progressDiag(this,
245 i18n("Installing icon themes"),
246 QString());
247 progressDiag.setModal(true);
248 progressDiag.setAutoClose(true);
249 QProgressBar* progressBar = progressDiag.progressBar();
250 progressBar->setMaximum(themes.count());
251 progressDiag.show();
253 KTar archive(archiveName);
254 archive.open(QIODevice::ReadOnly);
255 kapp->processEvents();
257 const KArchiveDirectory* rootDir = archive.directory();
259 KArchiveDirectory* currentTheme;
260 for (QStringList::ConstIterator it = themes.begin();
261 it != themes.end();
262 ++it) {
263 progressDiag.setLabelText(
264 i18n("<qt>Installing <strong>%1</strong> theme</qt>",
265 *it));
266 kapp->processEvents();
268 if (progressDiag.wasCancelled())
269 break;
271 currentTheme = dynamic_cast<KArchiveDirectory*>(
272 const_cast<KArchiveEntry*>(
273 rootDir->entry(*it)));
274 if (currentTheme == NULL) {
275 // we tell back that something went wrong, but try to install as much
276 // as possible
277 everythingOk = false;
278 continue;
281 currentTheme->copyTo(localThemesDir + *it);
282 progressBar->setValue(progressBar->value()+1);
285 archive.close();
286 return everythingOk;
289 QStringList IconThemesConfig::findThemeDirs(const QString &archiveName)
291 QStringList foundThemes;
293 KTar archive(archiveName);
294 archive.open(QIODevice::ReadOnly);
295 const KArchiveDirectory* themeDir = archive.directory();
297 KArchiveEntry* possibleDir = 0L;
298 KArchiveDirectory* subDir = 0L;
300 // iterate all the dirs looking for an index.theme or index.desktop file
301 QStringList entries = themeDir->entries();
302 for (QStringList::const_iterator it = entries.constBegin();
303 it != entries.constEnd();
304 ++it) {
305 possibleDir = const_cast<KArchiveEntry*>(themeDir->entry(*it));
306 if (possibleDir->isDirectory()) {
307 subDir = dynamic_cast<KArchiveDirectory*>( possibleDir );
308 if (subDir && (subDir->entry("index.theme") != NULL ||
309 subDir->entry("index.desktop") != NULL))
310 foundThemes.append(subDir->name());
314 archive.close();
315 return foundThemes;
318 void IconThemesConfig::getNewTheme()
320 KNS::Engine engine(this);
321 if (engine.init("icons.knsrc")) {
322 KNS::Entry::List entries = engine.downloadDialogModal(this);
324 for(int i = 0; i < entries.size(); i ++) {
325 if(entries.at(i)->status() == KNS::Entry::Installed) {
326 const QString themeTmpFile = entries.at(i)->installedFiles().at(0);
327 const QString name = entries.at(i)->installedFiles().at(0).section('/', -2, -2);
328 kDebug()<<"IconThemesConfig::getNewTheme() themeTmpFile="<<themeTmpFile<<"name="<<name;
329 QStringList themeNames = findThemeDirs(themeTmpFile);
330 if (themeNames.isEmpty()) {
331 //entries.at(i)->setStatus(KNS::Entry::Invalid);
333 else if (! installThemes(themeNames, themeTmpFile)) {
334 //entries.at(i)->setStatus(KNS::Entry::Invalid);
339 // reload the display icontheme items
340 KIconLoader::global()->newIconLoader();
341 loadThemes();
342 QTreeWidgetItem *item=iconThemeItem(KIconTheme::current());
343 if (item)
344 m_iconThemes->setCurrentItem(item);
345 updateRemoveButton();
346 load();
350 void IconThemesConfig::removeSelectedTheme()
352 QTreeWidgetItem *selected = m_iconThemes->currentItem();
353 if (!selected)
354 return;
356 QString question=i18n("<qt>Are you sure you want to remove the "
357 "<strong>%1</strong> icon theme?<br />"
358 "<br />"
359 "This will delete the files installed by this theme.</qt>",
360 selected->text(0));
362 bool deletingCurrentTheme=(selected==iconThemeItem(KIconTheme::current()));
364 int r=KMessageBox::warningContinueCancel(this,question,i18n("Confirmation"),KStandardGuiItem::del());
365 if (r!=KMessageBox::Continue) return;
367 KIconTheme icontheme(selected->data(0, ThemeNameRole).toString());
369 // delete the index file before the async KIO::del so loadThemes() will
370 // ignore that dir.
371 unlink(QFile::encodeName(icontheme.dir()+"/index.theme").data());
372 unlink(QFile::encodeName(icontheme.dir()+"/index.desktop").data());
373 KIO::del(KUrl( icontheme.dir() ));
375 KIconLoader::global()->newIconLoader();
377 loadThemes();
379 QTreeWidgetItem *item=0L;
380 //Fallback to the default if we've deleted the current theme
381 if (!deletingCurrentTheme)
382 item=iconThemeItem(KIconTheme::current());
383 if (!item)
384 item=iconThemeItem(KIconTheme::defaultThemeName());
386 if (item)
387 m_iconThemes->setCurrentItem(item);
388 updateRemoveButton();
390 if (deletingCurrentTheme) // Change the configuration
391 save();
394 void IconThemesConfig::updateRemoveButton()
396 QTreeWidgetItem *selected = m_iconThemes->currentItem();
397 bool enabled = false;
398 if (selected)
400 QString selectedtheme = selected->data(0, ThemeNameRole).toString();
401 KIconTheme icontheme(selectedtheme);
402 QFileInfo fi(icontheme.dir());
403 enabled = fi.isWritable();
404 // Don't let users remove the current theme.
405 if (selectedtheme == KIconTheme::current() ||
406 selectedtheme == KIconTheme::defaultThemeName())
407 enabled = false;
409 m_removeButton->setEnabled(enabled);
412 void loadPreview(QLabel *label, KIconTheme& icontheme, const QStringList& iconnames)
414 const int size = qMin(48, icontheme.defaultSize(KIconLoader::Desktop));
415 KSvgRenderer renderer;
416 foreach(const QString &name, iconnames) {
417 K3Icon icon = icontheme.iconPath(QString("%1.png").arg(name), size, KIconLoader::MatchBest);
418 if (icon.isValid()) {
419 label->setPixmap(QPixmap(icon.path).scaled(size, size));
420 return;
422 icon = icontheme.iconPath(QString("%1.svg").arg(name), size, KIconLoader::MatchBest);
423 if( ! icon.isValid() ) {
424 icon = icontheme.iconPath(QString("%1.svgz").arg(name), size, KIconLoader::MatchBest);
425 if( ! icon.isValid() ) {
426 continue;
429 if (renderer.load(icon.path)) {
430 QPixmap pix(size, size);
431 pix.fill(label->palette().color(QPalette::Background));
432 QPainter p(&pix);
433 p.setViewport(0, 0, size, size);
434 renderer.render(&p);
435 label->setPixmap(pix.scaled(size, size));
436 return;
439 label->setPixmap(QPixmap());
442 void IconThemesConfig::themeSelected(QTreeWidgetItem *item)
444 if (!item) return;
446 QString dirName(item->data(0, ThemeNameRole).toString());
447 KIconTheme icontheme(dirName);
448 if (!icontheme.isValid()) kDebug() << "notvalid\n";
450 updateRemoveButton();
452 loadPreview(m_previewExec, icontheme, QStringList() << "system-run" << "exec");
453 loadPreview(m_previewFolder, icontheme, QStringList() << "folder");
454 loadPreview(m_previewDocument, icontheme, QStringList() << "document" << "text-x-generic");
456 emit changed(true);
457 m_bChanged = true;
460 void IconThemesConfig::load()
462 m_defaultTheme=iconThemeItem(KIconTheme::current());
463 if (m_defaultTheme)
464 m_iconThemes->setCurrentItem(m_defaultTheme);
465 emit changed(false);
466 m_bChanged = false;
469 void IconThemesConfig::save()
471 if (!m_bChanged)
472 return;
473 QTreeWidgetItem *selected = m_iconThemes->currentItem();
474 if (!selected)
475 return;
477 KConfigGroup config(KSharedConfig::openConfig("kdeglobals", KConfig::SimpleConfig), "Icons");
478 config.writeEntry("Theme", selected->data(0, ThemeNameRole).toString());
479 config.sync();
481 KIconTheme::reconfigure();
482 emit changed(false);
484 KIconCache::deleteCache();
486 for (int i=0; i<KIconLoader::LastGroup; i++)
488 KGlobalSettings::self()->emitChange(KGlobalSettings::IconChanged, i);
491 KBuildSycocaProgressDialog::rebuildKSycoca(this);
493 m_bChanged = false;
494 m_removeButton->setEnabled(false);
497 void IconThemesConfig::defaults()
499 if (m_iconThemes->currentItem()==m_defaultTheme) return;
501 if (m_defaultTheme)
502 m_iconThemes->setCurrentItem(m_defaultTheme);
503 updateRemoveButton();
505 emit changed(true);
506 m_bChanged = true;
509 #include "iconthemes.moc"