add more spacing
[personal-kdebase.git] / workspace / ksplash / kcm / installer.cpp
blob9211ffd8f82e3e09efee14739a8ac5796991f4fd
1 /***************************************************************************
2 * Copyright Ravikiran Rajagopal 2003 *
3 * ravi@ee.eng.ohio-state.edu *
4 * Copyright (c) 1998 Stefan Taferner <taferner@kde.org> *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License (version 2) as *
8 * published by the Free Software Foundation. *
9 * *
10 ***************************************************************************/
12 #include <unistd.h>
13 #include <stdlib.h>
15 #include <QDir>
16 #include <QLabel>
17 #include <QLayout>
18 #include <QTextEdit>
19 #include <QPixmap>
20 #include <QFrame>
21 #include <QHBoxLayout>
22 #include <QDropEvent>
23 #include <QVBoxLayout>
24 #include <QDragEnterEvent>
25 #include <QMouseEvent>
26 #include <QScrollArea>
28 #include "installer.h"
30 #include <kdebug.h>
31 #include <kfiledialog.h>
32 #include <kglobalsettings.h>
33 #include <klocale.h>
34 #include <kmessagebox.h>
35 #include <kprocess.h>
36 #include <kpushbutton.h>
37 #include <kstandarddirs.h>
38 #include <ktar.h>
39 #include <kservicetypetrader.h>
40 #include <kio/netaccess.h>
41 #include <knewstuff2/engine.h>
43 ThemeListBox::ThemeListBox(QWidget *parent)
44 : KListWidget(parent)
46 setAcceptDrops(true);
49 void ThemeListBox::dragEnterEvent(QDragEnterEvent* event)
51 event->setAccepted((event->source() != this) && KUrl::List::canDecode(event->mimeData()));
54 void ThemeListBox::dragMoveEvent(QDragMoveEvent* event)
56 event->setAccepted((event->source() != this) && KUrl::List::canDecode(event->mimeData()));
59 void ThemeListBox::dropEvent(QDropEvent* event)
61 KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
62 if (!urls.isEmpty())
64 emit filesDropped(urls);
68 void ThemeListBox::mousePressEvent(QMouseEvent *e)
70 if ((e->buttons() & Qt::LeftButton) != 0)
72 mOldPos = e->globalPos();;
73 mDragFile.clear();
74 int cur = row(itemAt(e->pos()));
75 if (cur >= 0)
76 mDragFile = text2path[item(cur)->text()];
78 KListWidget::mousePressEvent(e);
81 void ThemeListBox::mouseMoveEvent(QMouseEvent *e)
83 if (((e->buttons() & Qt::LeftButton) != 0) && !mDragFile.isEmpty())
85 int delay = KGlobalSettings::dndEventDelay();
86 QPoint newPos = e->globalPos();
87 if(newPos.x() > mOldPos.x()+delay || newPos.x() < mOldPos.x()-delay ||
88 newPos.y() > mOldPos.y()+delay || newPos.y() < mOldPos.y()-delay)
90 KUrl url;
91 url.setPath(mDragFile);
92 KUrl::List urls;
93 urls.append(url);
94 QDrag *drag = new QDrag(this);
95 QMimeData *mime = new QMimeData();
96 urls.populateMimeData(mime);
97 drag->setMimeData(mime);
98 drag->start();
101 KListWidget::mouseMoveEvent(e);
104 //-----------------------------------------------------------------------------
105 SplashInstaller::SplashInstaller (QWidget *aParent, const char *aName, bool aInit)
106 : QWidget(aParent), mGui(!aInit)
108 setObjectName(aName);
109 KGlobal::dirs()->addResourceType("ksplashthemes", "data", "ksplash/Themes");
111 if (!mGui)
112 return;
114 QHBoxLayout* hbox = new QHBoxLayout( this );
115 hbox->setMargin( 0 );
117 QVBoxLayout* leftbox = new QVBoxLayout( );
118 hbox->addItem( leftbox );
119 leftbox->setSpacing( KDialog::spacingHint() );
120 hbox->setStretchFactor( leftbox, 1 );
122 mThemesList = new ThemeListBox(this);
123 mThemesList->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
124 connect(mThemesList, SIGNAL(currentRowChanged(int)), SLOT(slotSetTheme(int)));
125 connect(mThemesList, SIGNAL(filesDropped(const KUrl::List&)), SLOT(slotFilesDropped(const KUrl::List&)));
126 leftbox->addWidget(mThemesList);
128 mBtnNew = new KPushButton( KIcon("get-hot-new-stuff"), i18n("Get New Themes..."), this );
129 mBtnNew->setToolTip(i18n("Get new themes from the Internet"));
130 mBtnNew->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."));
131 leftbox->addWidget( mBtnNew );
132 connect(mBtnNew, SIGNAL(clicked()), SLOT(slotNew()));
134 mBtnAdd = new KPushButton( KIcon("document-import"), i18n("Install Theme File..."), this );
135 mBtnAdd->setToolTip(i18n("Install a theme archive file you already have locally"));
136 mBtnAdd->setWhatsThis(i18n("If you already have a theme archive locally, this button will unpack it and make it available for KDE applications"));
137 leftbox->addWidget( mBtnAdd );
138 connect(mBtnAdd, SIGNAL(clicked()), SLOT(slotAdd()));
140 mBtnRemove = new KPushButton( KIcon("edit-delete"), i18n("Remove Theme"), this );
141 mBtnRemove->setToolTip(i18n("Remove the selected theme from your disk"));
142 mBtnRemove->setWhatsThis(i18n("This will remove the selected theme from your disk."));
143 mBtnRemove->setEnabled( false );
144 leftbox->addWidget( mBtnRemove );
145 connect(mBtnRemove, SIGNAL(clicked()), SLOT(slotRemove()));
147 mBtnTest = new KPushButton( KIcon("document-preview"), i18n("Test Theme"), this );
148 mBtnTest->setToolTip(i18n("Test the selected theme"));
149 mBtnTest->setWhatsThis(i18n("This will test the selected theme."));
150 mBtnTest->setEnabled( false );
151 leftbox->addWidget( mBtnTest );
152 connect(mBtnTest, SIGNAL(clicked()), SLOT(slotTest()));
154 QVBoxLayout* rightbox = new QVBoxLayout( );
155 hbox->addItem( rightbox );
156 rightbox->setSpacing( KDialog::spacingHint() );
157 hbox->setStretchFactor( rightbox, 3 );
159 QScrollArea* scrollarea = new QScrollArea(this);
160 scrollarea->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
161 mPreview = new QLabel(this);
162 scrollarea->setWidget(mPreview);
163 mPreview->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
164 mPreview->setFrameStyle(QFrame::Panel|QFrame::Sunken);
165 mPreview->setMinimumSize(QSize(320,240));
166 mPreview->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
167 rightbox->addWidget(scrollarea);
168 rightbox->setStretchFactor( scrollarea, 3 );
170 mText = new QTextEdit(this);
171 mText->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
172 mText->setMinimumSize(mText->sizeHint().width(), 7 * mText->fontMetrics().height());
173 mText->setReadOnly(true);
174 rightbox->addWidget(mText);
175 rightbox->setStretchFactor( mText, 1 );
178 readThemesList();
179 load();
183 //-----------------------------------------------------------------------------
184 SplashInstaller::~SplashInstaller()
188 int SplashInstaller::addTheme(const QString &path, const QString &name)
190 //kDebug() << "SplashInstaller::addTheme: " << path << " " << name;
191 QString tmp(i18n( name.toUtf8() ));
192 int i = mThemesList->count();
193 while((i > 0) && (mThemesList->item(i-1)->text() > tmp))
194 i--;
195 if ((i > 0) && (mThemesList->item(i-1)->text() == tmp))
196 return i-1;
197 mThemesList->insertItem(i , tmp);
198 mThemesList->text2path.insert( tmp, path+'/'+name );
199 return i;
202 // Copy theme package into themes directory
203 void SplashInstaller::addNewTheme(const KUrl &srcURL)
205 QString dir = KGlobal::dirs()->saveLocation("ksplashthemes");
207 KUrl url;
208 QStringList themeNames;
209 QString filename = srcURL.fileName();
210 int i = filename.lastIndexOf('.');
211 // Convert extension to lower case.
212 if (i >= 0)
213 filename = filename.left(i)+filename.mid(i).toLower();
214 url.setPath(KStandardDirs::locateLocal("tmp",filename));
216 // Remove file from temporary directory if it aleady exists - usually the result of a failed install.
217 if ( KIO::NetAccess::exists( url, KIO::NetAccess::SourceSide, 0 ) )
218 KIO::NetAccess::del( url, 0 );
220 if (srcURL.fileName().toLower() == "theme.rc" ) // uncompressed theme selected
222 QString themeName;
223 // Get the name of the Theme from the theme.rc file
224 KConfig _cnf(srcURL.path());
225 KConfigGroup cnf(&_cnf, QString("KSplash Theme: %1").arg(themeName) );
227 // copy directory of theme.rc to ThemesDir
228 KIO::NetAccess::dircopy(KUrl(srcURL.directory()), KUrl(dir + themeName));
230 themeNames << themeName;
232 else
234 bool rc = KIO::NetAccess::file_copy(srcURL, url, 0);
235 if (!rc)
237 kWarning() << "Failed to copy theme " << srcURL.fileName()
238 << " into temporary directory " << url.path() << endl;
239 return;
242 // Extract into theme directory: we may have multiple themes in one tarball!
243 KTar tarFile(url.path());
244 if (!tarFile.open(QIODevice::ReadOnly))
246 kWarning() << "Unable to open archive: " << url.path();
247 KIO::NetAccess::del( url, 0 );
248 return;
250 KArchiveDirectory const *ad = tarFile.directory();
252 // Find first directory entry.
253 QStringList entries = ad->entries();
254 foreach(const QString& s, entries)
256 if( ad->entry(s)->isDirectory() )
258 // each directory may contain one theme
259 themeNames << s;
262 if (themeNames.count() < 1)
264 kWarning() << "No directory in archive: " << url.path();
265 tarFile.close();
266 KIO::NetAccess::del( url, 0 );
267 return;
270 // copy the theme into the "ksplashthemes" directory
271 ad->copyTo(dir);
273 tarFile.close();
274 KIO::NetAccess::del( url, 0 );
278 readThemesList();
279 mThemesList->setCurrentRow(findTheme(themeNames.first()));
280 if (mThemesList->currentItem())
282 mThemesList->currentItem()->setSelected(true);
286 //-----------------------------------------------------------------------------
287 void SplashInstaller::readThemesList()
289 mThemesList->clear();
291 // Read local themes
292 QStringList entryList = KGlobal::dirs()->resourceDirs("ksplashthemes");
293 //kDebug() << "readThemesList: " << entryList;
294 QDir dir;
295 QStringList subdirs;
296 QStringList::ConstIterator name;
297 for(name = entryList.constBegin(); name != entryList.constEnd(); name++)
299 dir = *name;
300 if (!dir.exists())
301 continue;
302 subdirs = dir.entryList( QDir::Dirs );
303 // kDebug() << "readThemesList: " << subdirs;
304 // TODO: Make sure it contains a *.rc file.
305 for (QStringList::const_iterator l = subdirs.constBegin(); l != subdirs.constEnd(); l++ )
306 if ( !(*l).startsWith(QString(".")) )
308 mThemesList->blockSignals( true ); // Don't activate any theme until all themes are loaded.
309 addTheme(dir.path(),*l);
310 mThemesList->blockSignals( false );
315 //-----------------------------------------------------------------------------
316 void SplashInstaller::defaults()
318 mThemesList->setCurrentRow(findTheme("Default"));
319 emit changed( true );
322 void SplashInstaller::load()
324 KConfig _cnf( "ksplashrc" );
325 KConfigGroup cnf(&_cnf, "KSplash");
326 QString curTheme = cnf.readEntry("Theme","Default");
327 mThemesList->setCurrentRow(findTheme(curTheme));
328 emit changed( false );
331 //-----------------------------------------------------------------------------
332 void SplashInstaller::save()
334 KConfig _cnf( "ksplashrc" );
335 KConfigGroup cnf(&_cnf, "KSplash");
336 int cur = mThemesList->currentRow();
337 if (cur < 0)
338 return;
339 QString path = mThemesList->item(cur)->text();
340 if ( mThemesList->text2path.contains( path ) )
341 path = mThemesList->text2path[path];
342 cur = path.lastIndexOf('/');
343 cnf.writeEntry("Theme", path.mid(cur+1) );
344 // save also the engine, so that it's known at KDE startup which splash implementation to use
345 cnf.writeEntry("Engine", mEngineOfSelected );
346 cnf.sync();
347 emit changed( false );
350 //-----------------------------------------------------------------------------
351 void SplashInstaller::slotRemove()
353 int cur = mThemesList->currentRow();
354 if (cur < 0)
355 return;
357 bool rc = false;
358 QString themeName = mThemesList->item(cur)->text();
359 QString themeDir = mThemesList->text2path[themeName];
360 if (!themeDir.isEmpty())
362 KUrl url;
363 url.setPath(themeDir);
364 if (KMessageBox::warningContinueCancel(this,i18n("Delete folder %1 and its contents?", themeDir),"",KGuiItem(i18n("&Delete"),"edit-delete"))==KMessageBox::Continue)
365 rc = KIO::NetAccess::del(url,this);
366 else
367 return;
369 if (!rc)
371 KMessageBox::sorry(this, i18n("Failed to remove theme '%1'", themeName));
372 return;
374 //mThemesList->removeItem(cur);
375 readThemesList();
376 cur = (cur >= mThemesList->count())?mThemesList->count()-1:cur;
377 mThemesList->setCurrentRow(cur);
381 //-----------------------------------------------------------------------------
382 void SplashInstaller::slotSetTheme(int id)
384 bool enabled;
385 QString path = QString();
386 QString infoTxt;
388 if (id < 0)
390 mPreview->setText(QString());
391 mText->setText(QString());
392 enabled = false;
394 else
396 QString error = i18n("(Could not load theme)");
397 path = mThemesList->item(id)->text();
398 if ( mThemesList->text2path.contains( path ) )
399 path = mThemesList->text2path[path];
400 enabled = false;
401 KUrl url;
402 QString themeName;
403 if (!path.isEmpty())
405 // Make sure the correct plugin is installed.
406 int i = path.lastIndexOf('/');
407 if (i >= 0)
408 themeName = path.mid(i+1);
409 url.setPath( path + "/Theme.rc" );
410 if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
412 url.setPath( path + "/Theme.RC" );
413 if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
415 url.setPath( path + "/theme.rc" );
416 if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
417 url.setPath( path + '/' + themeName + ".rc" );
420 if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
422 KConfig _cnf(url.path());
423 KConfigGroup cnf(&_cnf, QString("KSplash Theme: %1").arg(themeName) );
425 // Get theme information.
426 infoTxt = "<qt>";
427 if ( cnf.hasKey( "Name" ) )
428 infoTxt += i18n( "<b>Name:</b> %1", cnf.readEntry( "Name", i18nc( "Unknown name", "Unknown" ) ) ) + "<br />";
429 if ( cnf.hasKey( "Description" ) )
430 infoTxt += i18n( "<b>Description:</b> %1", cnf.readEntry( "Description", i18nc( "Unknown description", "Unknown" ) ) ) + "<br />";
431 if ( cnf.hasKey( "Version" ) )
432 infoTxt += i18n( "<b>Version:</b> %1", cnf.readEntry( "Version", i18nc( "Unknown version", "Unknown" ) ) ) + "<br />";
433 if ( cnf.hasKey( "Author" ) )
434 infoTxt += i18n( "<b>Author:</b> %1", cnf.readEntry( "Author", i18nc( "Unknown author", "Unknown" ) ) ) + "<br />";
435 if ( cnf.hasKey( "Homepage" ) )
436 infoTxt += i18n( "<b>Homepage:</b> %1", cnf.readEntry( "Homepage", i18nc( "Unknown homepage", "Unknown" ) ) ) + "<br />";
437 infoTxt += "</qt>";
439 QString pluginName( cnf.readEntry( "Engine", "KSplashX" ).trimmed() );
440 if( pluginName == "Simple" || pluginName == "None" || pluginName == "KSplashX" )
441 enabled = true; // these are not plugins
442 else if ((KServiceTypeTrader::self()->query("KSplash/Plugin", QString("[X-KSplash-PluginName] == '%1'").arg(pluginName))).isEmpty())
444 enabled = false;
445 error = i18n("This theme requires the plugin %1 which is not installed.", pluginName);
447 else
448 enabled = true; // Hooray, there is at least one plugin which can handle this theme.
449 mEngineOfSelected = pluginName;
451 else
453 error = i18n("Could not load theme configuration file.");
456 mBtnTest->setEnabled(enabled && themeName != "None" );
457 mText->setHtml(infoTxt);
458 if (!enabled)
460 url.setPath( path + '/' + "Preview.png" );
461 if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
462 mPreview->setPixmap(QPixmap(url.path()));
463 else
464 mPreview->setText(i18n("(Could not load theme)"));
465 KMessageBox::sorry(this, error);
467 else
469 url.setPath( path + '/' + "Preview.png" );
470 if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
471 mPreview->setPixmap(QPixmap(url.path()));
472 else
473 mPreview->setText(i18n("No preview available."));
474 emit changed(true);
477 mBtnRemove->setEnabled( !path.isEmpty() && QFileInfo(path).isWritable());
480 //-----------------------------------------------------------------------------
481 void SplashInstaller::slotNew()
483 KNS::Engine engine(this);
484 if (engine.init("ksplash.knsrc")) {
485 KNS::Entry::List entries = engine.downloadDialogModal(this);
486 if ( !entries.isEmpty() )
487 readThemesList();
491 //-----------------------------------------------------------------------------
492 void SplashInstaller::slotAdd()
494 static QString path;
495 if (path.isEmpty()) path = QDir::homePath();
497 KFileDialog dlg(path, "*.tgz *.tar.gz *.tar.bz2 theme.rc|" + i18n( "KSplash Theme Files" ), this);
498 dlg.setCaption(i18n("Add Theme"));
499 if (!dlg.exec())
500 return;
502 path = dlg.baseUrl().url();
503 addNewTheme(dlg.selectedUrl());
506 //-----------------------------------------------------------------------------
507 void SplashInstaller::slotFilesDropped(const KUrl::List &urls)
509 for(KUrl::List::ConstIterator it = urls.begin();
510 it != urls.end();
511 ++it)
512 addNewTheme(*it);
515 //-----------------------------------------------------------------------------
516 int SplashInstaller::findTheme( const QString &theme )
518 // theme is untranslated, but the listbox contains translated items
519 QString tmp(i18n( theme.toUtf8() ));
520 int id = mThemesList->count()-1;
522 while (id >= 0)
524 if (mThemesList->item(id)->text() == tmp)
525 return id;
526 id--;
529 return 0;
532 //-----------------------------------------------------------------------------
533 void SplashInstaller::slotTest()
535 int i = mThemesList->currentRow();
536 if (i < 0)
537 return;
538 QString themeName = mThemesList->text2path[mThemesList->item(i)->text()];
539 int r = themeName.lastIndexOf('/');
540 if (r >= 0)
541 themeName = themeName.mid(r+1);
543 // special handling for none and simple splashscreens
544 if( mEngineOfSelected == "None" )
545 return;
546 else if( mEngineOfSelected == "Simple" )
548 KProcess proc;
549 proc << "ksplashsimple" << themeName << "--test";
550 if (proc.execute())
551 KMessageBox::error(this,i18n("Failed to successfully test the splash screen."));
552 return;
554 else if( mEngineOfSelected == "KSplashX" )
556 KProcess proc;
557 proc << "ksplashx" << themeName << "--test";
558 if (proc.execute())
559 KMessageBox::error(this,i18n("Failed to successfully test the splash screen."));
560 return;
562 else // KSplashML engines
564 KProcess proc;
565 proc << "ksplash" << "--test" << "--theme" << themeName;
566 if (proc.execute())
567 KMessageBox::error(this,i18n("Failed to successfully test the splash screen."));
571 //-----------------------------------------------------------------------------
572 #include "installer.moc"