kde-workspace/kcontrol/splash/installer.cpp
Ivailo Monev f1cfe7bdba generic: replace KProcess with QProcess where feasable
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2016-04-30 16:22:26 +00:00

548 lines
18 KiB
C++

/***************************************************************************
* Copyright Ravikiran Rajagopal 2003 *
* ravi@ee.eng.ohio-state.edu *
* Copyright (c) 1998 Stefan Taferner <taferner@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License (version 2) as *
* published by the Free Software Foundation. *
* *
***************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <QProcess>
#include <QDir>
#include <QLabel>
#include <QLayout>
#include <QTextEdit>
#include <QPixmap>
#include <QFrame>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QScrollArea>
#include <QtGui/qevent.h>
#include "installer.h"
#include <kdebug.h>
#include <kfiledialog.h>
#include <kglobalsettings.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpushbutton.h>
#include <kstandarddirs.h>
#include <ktar.h>
#include <kservicetypetrader.h>
#include <kio/netaccess.h>
ThemeListBox::ThemeListBox(QWidget *parent)
: KListWidget(parent)
{
setAcceptDrops(true);
}
void ThemeListBox::dragEnterEvent(QDragEnterEvent* event)
{
event->setAccepted((event->source() != this) && KUrl::List::canDecode(event->mimeData()));
}
void ThemeListBox::dragMoveEvent(QDragMoveEvent* event)
{
event->setAccepted((event->source() != this) && KUrl::List::canDecode(event->mimeData()));
}
void ThemeListBox::dropEvent(QDropEvent* event)
{
KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
if (!urls.isEmpty())
{
emit filesDropped(urls);
}
}
void ThemeListBox::mousePressEvent(QMouseEvent *e)
{
if ((e->buttons() & Qt::LeftButton) != 0)
{
mOldPos = e->globalPos();;
mDragFile.clear();
int cur = row(itemAt(e->pos()));
if (cur >= 0)
mDragFile = text2path[item(cur)->text()];
}
KListWidget::mousePressEvent(e);
}
void ThemeListBox::mouseMoveEvent(QMouseEvent *e)
{
if (((e->buttons() & Qt::LeftButton) != 0) && !mDragFile.isEmpty())
{
int delay = KGlobalSettings::dndEventDelay();
QPoint newPos = e->globalPos();
if(newPos.x() > mOldPos.x()+delay || newPos.x() < mOldPos.x()-delay ||
newPos.y() > mOldPos.y()+delay || newPos.y() < mOldPos.y()-delay)
{
KUrl url;
url.setPath(mDragFile);
KUrl::List urls;
urls.append(url);
QDrag *drag = new QDrag(this);
QMimeData *mime = new QMimeData();
urls.populateMimeData(mime);
drag->setMimeData(mime);
drag->start();
}
}
KListWidget::mouseMoveEvent(e);
}
//-----------------------------------------------------------------------------
SplashInstaller::SplashInstaller (QWidget *aParent, const char *aName, bool aInit)
: QWidget(aParent), mGui(!aInit)
{
setObjectName(aName);
KGlobal::dirs()->addResourceType("ksplashthemes", "data", "ksplash/Themes");
if (!mGui)
return;
QHBoxLayout* hbox = new QHBoxLayout( this );
hbox->setMargin( 0 );
QVBoxLayout* leftbox = new QVBoxLayout( );
hbox->addLayout( leftbox );
hbox->setStretchFactor( leftbox, 1 );
mThemesList = new ThemeListBox(this);
mThemesList->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
connect(mThemesList, SIGNAL(currentRowChanged(int)), SLOT(slotSetTheme(int)));
connect(mThemesList, SIGNAL(filesDropped(KUrl::List)), SLOT(slotFilesDropped(KUrl::List)));
leftbox->addWidget(mThemesList);
mBtnAdd = new KPushButton( KIcon("document-import"), i18n("Install Theme File..."), this );
mBtnAdd->setToolTip(i18n("Install a theme archive file you already have locally"));
mBtnAdd->setWhatsThis(i18n("If you already have a theme archive locally, this button will unpack it and make it available for KDE applications"));
leftbox->addWidget( mBtnAdd );
connect(mBtnAdd, SIGNAL(clicked()), SLOT(slotAdd()));
mBtnRemove = new KPushButton( KIcon("edit-delete"), i18n("Remove Theme"), this );
mBtnRemove->setToolTip(i18n("Remove the selected theme from your disk"));
mBtnRemove->setWhatsThis(i18n("This will remove the selected theme from your disk."));
mBtnRemove->setEnabled( false );
leftbox->addWidget( mBtnRemove );
connect(mBtnRemove, SIGNAL(clicked()), SLOT(slotRemove()));
mBtnTest = new KPushButton( KIcon("document-preview"), i18n("Test Theme"), this );
mBtnTest->setToolTip(i18n("Test the selected theme"));
mBtnTest->setWhatsThis(i18n("This will test the selected theme."));
mBtnTest->setEnabled( false );
leftbox->addWidget( mBtnTest );
connect(mBtnTest, SIGNAL(clicked()), SLOT(slotTest()));
QVBoxLayout* rightbox = new QVBoxLayout( );
hbox->addLayout( rightbox );
hbox->setStretchFactor( rightbox, 3 );
QScrollArea* scrollarea = new QScrollArea(this);
scrollarea->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
mPreview = new QLabel(this);
scrollarea->setWidget(mPreview);
mPreview->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
mPreview->setMinimumSize(QSize(320,240));
mPreview->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
rightbox->addWidget(scrollarea);
rightbox->setStretchFactor( scrollarea, 3 );
mText = new QTextEdit(this);
mText->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
mText->setMinimumSize(mText->sizeHint().width(), 7 * mText->fontMetrics().height());
mText->setReadOnly(true);
rightbox->addWidget(mText);
rightbox->setStretchFactor( mText, 1 );
readThemesList();
load();
}
//-----------------------------------------------------------------------------
SplashInstaller::~SplashInstaller()
{
}
int SplashInstaller::addTheme(const QString &path, const QString &name)
{
qDebug() << "SplashInstaller::addTheme: " << path << " " << name;
QString tmp(i18n( name.toUtf8() ));
int i = mThemesList->count();
while((i > 0) && (mThemesList->item(i-1)->text() > tmp))
i--;
if ((i > 0) && (mThemesList->item(i-1)->text() == tmp))
return i-1;
mThemesList->insertItem(i , tmp);
mThemesList->text2path.insert( tmp, path+'/'+name );
return i;
}
// Copy theme package into themes directory
void SplashInstaller::addNewTheme(const KUrl &srcURL)
{
const QString dir = KGlobal::dirs()->saveLocation("ksplashthemes");
KUrl url;
QStringList themeNames;
QString filename = srcURL.fileName();
int i = filename.lastIndexOf('.');
// Convert extension to lower case.
if (i >= 0)
filename = filename.left(i)+filename.mid(i).toLower();
url.setPath(KStandardDirs::locateLocal("tmp",filename));
// Remove file from temporary directory if it aleady exists - usually the result of a failed install.
if ( KIO::NetAccess::exists( url, KIO::NetAccess::SourceSide, 0 ) )
KIO::NetAccess::del( url, 0 );
if (srcURL.fileName().toLower() == "theme.rc" ) // uncompressed theme selected
{
QString themeName;
// Get the name of the Theme from the theme.rc file
KConfig _cnf(srcURL.path());
KConfigGroup cnf(&_cnf, QString("KSplash Theme: %1").arg(themeName) );
// copy directory of theme.rc to ThemesDir
KIO::NetAccess::dircopy(KUrl(srcURL.directory()), KUrl(dir + themeName));
themeNames << themeName;
}
else
{
bool rc = KIO::NetAccess::file_copy(srcURL, url, 0);
if (!rc)
{
kWarning() << "Failed to copy theme " << srcURL.fileName()
<< " into temporary directory " << url.path() << endl;
return;
}
// Extract into theme directory: we may have multiple themes in one tarball!
KTar tarFile(url.path());
if (!tarFile.open(QIODevice::ReadOnly))
{
kWarning() << "Unable to open archive: " << url.path();
KIO::NetAccess::del( url, 0 );
return;
}
KArchiveDirectory const *ad = tarFile.directory();
// Find first directory entry.
const QStringList entries = ad->entries();
foreach(const QString& s, entries)
{
if( ad->entry(s)->isDirectory() )
{
// each directory may contain one theme
themeNames << s;
}
}
if (themeNames.count() < 1)
{
kWarning() << "No directory in archive: " << url.path();
tarFile.close();
KIO::NetAccess::del( url, 0 );
return;
}
// copy the theme into the "ksplashthemes" directory
ad->copyTo(dir);
tarFile.close();
KIO::NetAccess::del( url, 0 );
}
readThemesList();
mThemesList->setCurrentRow(findTheme(themeNames.first()));
if (mThemesList->currentItem())
{
mThemesList->currentItem()->setSelected(true);
}
}
//-----------------------------------------------------------------------------
void SplashInstaller::readThemesList()
{
mThemesList->clear();
// Read local themes
const QStringList entryList = KGlobal::dirs()->resourceDirs("ksplashthemes");
//kDebug() << "readThemesList: " << entryList;
QDir dir;
QStringList subdirs;
QStringList::ConstIterator name;
for(name = entryList.constBegin(); name != entryList.constEnd(); name++)
{
dir = *name;
if (!dir.exists())
continue;
subdirs = dir.entryList( QDir::Dirs );
// kDebug() << "readThemesList: " << subdirs;
// TODO: Make sure it contains a *.rc file.
for (QStringList::const_iterator l = subdirs.constBegin(); l != subdirs.constEnd(); l++ )
if ( !(*l).startsWith(QString(".")) )
{
mThemesList->blockSignals( true ); // Don't activate any theme until all themes are loaded.
addTheme(dir.path(),*l);
mThemesList->blockSignals( false );
}
}
}
//-----------------------------------------------------------------------------
void SplashInstaller::defaults()
{
mThemesList->setCurrentRow(findTheme("Default"));
emit changed( true );
}
void SplashInstaller::load()
{
KConfig _cnf( "ksplashrc" );
KConfigGroup cnf(&_cnf, "KSplash");
QString curTheme = cnf.readEntry("Theme","Default");
mThemesList->setCurrentRow(findTheme(curTheme));
emit changed( false );
}
//-----------------------------------------------------------------------------
void SplashInstaller::save()
{
KConfig _cnf( "ksplashrc" );
KConfigGroup cnf(&_cnf, "KSplash");
int cur = mThemesList->currentRow();
if (cur < 0)
return;
QString path = mThemesList->item(cur)->text();
if ( mThemesList->text2path.contains( path ) )
path = mThemesList->text2path[path];
cur = path.lastIndexOf('/');
cnf.writeEntry("Theme", path.mid(cur+1) );
// save also the engine, so that it's known at KDE startup which splash implementation to use
cnf.writeEntry("Engine", mEngineOfSelected );
cnf.sync();
emit changed( false );
}
//-----------------------------------------------------------------------------
void SplashInstaller::slotRemove()
{
int cur = mThemesList->currentRow();
if (cur < 0)
return;
bool rc = false;
const QString themeName = mThemesList->item(cur)->text();
const QString themeDir = mThemesList->text2path[themeName];
if (!themeDir.isEmpty())
{
KUrl url;
url.setPath(themeDir);
if (KMessageBox::warningContinueCancel(this,i18n("Delete folder %1 and its contents?", themeDir),"",KGuiItem(i18n("&Delete"),"edit-delete"))==KMessageBox::Continue)
rc = KIO::NetAccess::del(url,this);
else
return;
}
if (!rc)
{
KMessageBox::sorry(this, i18n("Failed to remove theme '%1'", themeName));
return;
}
//mThemesList->removeItem(cur);
readThemesList();
cur = (cur >= mThemesList->count())?mThemesList->count()-1:cur;
mThemesList->setCurrentRow(cur);
}
//-----------------------------------------------------------------------------
void SplashInstaller::slotSetTheme(int id)
{
bool enabled;
QString path = QString();
QString infoTxt;
if (id < 0)
{
mPreview->setText(QString());
mText->setText(QString());
enabled = false;
}
else
{
QString error = i18n("(Could not load theme)");
path = mThemesList->item(id)->text();
if ( mThemesList->text2path.contains( path ) )
path = mThemesList->text2path[path];
enabled = false;
KUrl url;
QString themeName;
if (!path.isEmpty())
{
// Make sure the correct plugin is installed.
int i = path.lastIndexOf('/');
if (i >= 0)
themeName = path.mid(i+1);
url.setPath( path + "/Theme.rc" );
if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
{
url.setPath( path + "/Theme.RC" );
if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
{
url.setPath( path + "/theme.rc" );
if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
url.setPath( path + '/' + themeName + ".rc" );
}
}
if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
{
KConfig _cnf(url.path());
KConfigGroup cnf(&_cnf, QString("KSplash Theme: %1").arg(themeName) );
// Get theme information.
infoTxt = "<qt>";
if ( cnf.hasKey( "Name" ) )
infoTxt += i18n( "<b>Name:</b> %1", cnf.readEntry( "Name", i18nc( "Unknown name", "Unknown" ) ) ) + "<br />";
if ( cnf.hasKey( "Description" ) )
infoTxt += i18n( "<b>Description:</b> %1", cnf.readEntry( "Description", i18nc( "Unknown description", "Unknown" ) ) ) + "<br />";
if ( cnf.hasKey( "Version" ) )
infoTxt += i18n( "<b>Version:</b> %1", cnf.readEntry( "Version", i18nc( "Unknown version", "Unknown" ) ) ) + "<br />";
if ( cnf.hasKey( "Author" ) )
infoTxt += i18n( "<b>Author:</b> %1", cnf.readEntry( "Author", i18nc( "Unknown author", "Unknown" ) ) ) + "<br />";
if ( cnf.hasKey( "Homepage" ) )
infoTxt += i18n( "<b>Homepage:</b> %1", cnf.readEntry( "Homepage", i18nc( "Unknown homepage", "Unknown" ) ) ) + "<br />";
infoTxt += "</qt>";
QString pluginName( cnf.readEntry( "Engine", "KSplashX" ).trimmed() );
if( pluginName == "Simple"
|| pluginName == "None"
|| pluginName == "KSplashX"
|| pluginName == "KSplashQML"
)
enabled = true; // these are not plugins
else if ((KServiceTypeTrader::self()->query("KSplash/Plugin", QString("[X-KSplash-PluginName] == '%1'").arg(pluginName))).isEmpty())
{
enabled = false;
error = i18n("This theme requires the plugin %1 which is not installed.", pluginName);
}
else
enabled = true; // Hooray, there is at least one plugin which can handle this theme.
mEngineOfSelected = pluginName;
}
else
{
error = i18n("Could not load theme configuration file.");
}
}
mBtnTest->setEnabled(enabled && themeName != "None" );
mText->setHtml(infoTxt);
if (!enabled)
{
url.setPath( path + '/' + "Preview.png" );
if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
mPreview->setPixmap(QPixmap(url.path()));
else
mPreview->setText(i18n("(Could not load theme)"));
KMessageBox::sorry(this, error);
}
else
{
url.setPath( path + '/' + "Preview.png" );
if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0))
mPreview->setPixmap(QPixmap(url.path()));
else
mPreview->setText(i18n("No preview available."));
emit changed(true);
}
}
mBtnRemove->setEnabled( !path.isEmpty() && QFileInfo(path).isWritable());
}
//-----------------------------------------------------------------------------
void SplashInstaller::slotAdd()
{
static QString path;
if (path.isEmpty()) path = QDir::homePath();
KFileDialog dlg(path, "*.tgz *.tar.gz *.tar.bz2 theme.rc|" + i18n( "KSplash Theme Files" ), this);
dlg.setCaption(i18n("Add Theme"));
if (!dlg.exec())
return;
path = dlg.baseUrl().url();
addNewTheme(dlg.selectedUrl());
}
//-----------------------------------------------------------------------------
void SplashInstaller::slotFilesDropped(const KUrl::List &urls)
{
for(KUrl::List::ConstIterator it = urls.constBegin();
it != urls.end();
++it)
addNewTheme(*it);
}
//-----------------------------------------------------------------------------
int SplashInstaller::findTheme( const QString &theme )
{
// theme is untranslated, but the listbox contains translated items
QString tmp(i18n( theme.toUtf8() ));
int id = mThemesList->count()-1;
while (id >= 0)
{
if (mThemesList->item(id)->text() == tmp)
return id;
id--;
}
return 0;
}
//-----------------------------------------------------------------------------
void SplashInstaller::slotTest()
{
int i = mThemesList->currentRow();
if (i < 0)
return;
QString themeName = mThemesList->text2path[mThemesList->item(i)->text()];
int r = themeName.lastIndexOf('/');
if (r >= 0)
themeName = themeName.mid(r+1);
// special handling for none and simple splashscreens
qDebug() << "the engine is " << mEngineOfSelected << "for" << themeName;
if( mEngineOfSelected == "None" )
return;
else if( mEngineOfSelected == "KSplashX" )
{
if (QProcess::execute("ksplashx", QStringList() << themeName << "--test"))
KMessageBox::error(this,i18n("Failed to successfully test the splash screen."));
return;
}
else if( mEngineOfSelected == "KSplashQML" )
{
if (QProcess::execute("ksplashqml", QStringList() << themeName << "--test"))
KMessageBox::error(this,i18n("Failed to successfully test the splash screen."));
return;
}
else // KSplashML engines
{
if (QProcess::execute("ksplash", QStringList() << "--test" << "--theme" << themeName))
KMessageBox::error(this,i18n("Failed to successfully test the splash screen."));
}
}
//-----------------------------------------------------------------------------
#include "moc_installer.cpp"