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>
29 #include <QVBoxLayout>
31 #include <QHBoxLayout>
32 #include <QTreeWidget>
36 #include <kapplication.h>
37 #include <kbuildsycocaprogressdialog.h>
39 #include <kiconcache.h>
40 #include <kpushbutton.h>
41 #include <ksvgrenderer.h>
42 #include <kstandarddirs.h>
45 #include <knewstuff2/engine.h>
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>
55 #include <kio/deletejob.h>
56 #include <kio/netaccess.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
);
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"));
84 lh2
->addWidget(m_previewExec
);
86 lh2
->addWidget(m_previewFolder
);
88 lh2
->addWidget(m_previewDocument
);
92 m_iconThemes
=new QTreeWidget(this/*"IconThemeList"*/);
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
);
134 m_defaultTheme
=iconThemeItem(KIconTheme::current());
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
);
155 void IconThemesConfig::loadThemes()
157 m_iconThemes
->clear();
158 const QStringList
themelist(KIconTheme::list());
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();
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)) {
199 if (themeURL
.isLocalFile())
200 sorryText
= i18n("Unable to find the icon theme archive %1.",
201 themeURL
.prettyUrl());
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
);
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
);
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();
233 QTreeWidgetItem
*item
=iconThemeItem(KIconTheme::current());
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"),
247 progressDiag
.setModal(true);
248 progressDiag
.setAutoClose(true);
249 QProgressBar
* progressBar
= progressDiag
.progressBar();
250 progressBar
->setMaximum(themes
.count());
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();
263 progressDiag
.setLabelText(
264 i18n("<qt>Installing <strong>%1</strong> theme</qt>",
266 kapp
->processEvents();
268 if (progressDiag
.wasCancelled())
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
277 everythingOk
= false;
281 currentTheme
->copyTo(localThemesDir
+ *it
);
282 progressBar
->setValue(progressBar
->value()+1);
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();
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());
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();
342 QTreeWidgetItem
*item
=iconThemeItem(KIconTheme::current());
344 m_iconThemes
->setCurrentItem(item
);
345 updateRemoveButton();
350 void IconThemesConfig::removeSelectedTheme()
352 QTreeWidgetItem
*selected
= m_iconThemes
->currentItem();
356 QString question
=i18n("<qt>Are you sure you want to remove the "
357 "<strong>%1</strong> icon theme?<br />"
359 "This will delete the files installed by this theme.</qt>",
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
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();
379 QTreeWidgetItem
*item
=0L;
380 //Fallback to the default if we've deleted the current theme
381 if (!deletingCurrentTheme
)
382 item
=iconThemeItem(KIconTheme::current());
384 item
=iconThemeItem(KIconTheme::defaultThemeName());
387 m_iconThemes
->setCurrentItem(item
);
388 updateRemoveButton();
390 if (deletingCurrentTheme
) // Change the configuration
394 void IconThemesConfig::updateRemoveButton()
396 QTreeWidgetItem
*selected
= m_iconThemes
->currentItem();
397 bool enabled
= false;
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())
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
));
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() ) {
429 if (renderer
.load(icon
.path
)) {
430 QPixmap
pix(size
, size
);
431 pix
.fill(label
->palette().color(QPalette::Background
));
433 p
.setViewport(0, 0, size
, size
);
435 label
->setPixmap(pix
.scaled(size
, size
));
439 label
->setPixmap(QPixmap());
442 void IconThemesConfig::themeSelected(QTreeWidgetItem
*item
)
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");
460 void IconThemesConfig::load()
462 m_defaultTheme
=iconThemeItem(KIconTheme::current());
464 m_iconThemes
->setCurrentItem(m_defaultTheme
);
469 void IconThemesConfig::save()
473 QTreeWidgetItem
*selected
= m_iconThemes
->currentItem();
477 KConfigGroup
config(KSharedConfig::openConfig("kdeglobals", KConfig::SimpleConfig
), "Icons");
478 config
.writeEntry("Theme", selected
->data(0, ThemeNameRole
).toString());
481 KIconTheme::reconfigure();
484 KIconCache::deleteCache();
486 for (int i
=0; i
<KIconLoader::LastGroup
; i
++)
488 KGlobalSettings::self()->emitChange(KGlobalSettings::IconChanged
, i
);
491 KBuildSycocaProgressDialog::rebuildKSycoca(this);
494 m_removeButton
->setEnabled(false);
497 void IconThemesConfig::defaults()
499 if (m_iconThemes
->currentItem()==m_defaultTheme
) return;
502 m_iconThemes
->setCurrentItem(m_defaultTheme
);
503 updateRemoveButton();
509 #include "iconthemes.moc"