mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 19:02:53 +00:00
479 lines
14 KiB
C++
479 lines
14 KiB
C++
/*
|
|
Copyright (C) 2008-2009 by Eike Hein <hein@kde.org>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License or (at your option) version 3 or any later version
|
|
accepted by the membership of KDE e.V. (or its successor appro-
|
|
ved by the membership of KDE e.V.), which shall act as a proxy
|
|
defined in Section 14 of version 3 of the license.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
*/
|
|
|
|
|
|
#include "appearancesettings.h"
|
|
#include "settings.h"
|
|
#include "skinlistdelegate.h"
|
|
|
|
#include <KApplication>
|
|
#include <KFileDialog>
|
|
#include <KIO/DeleteJob>
|
|
#include <KIO/NetAccess>
|
|
#include <KMessageBox>
|
|
#include <KStandardDirs>
|
|
#include <KTar>
|
|
#include <KUrl>
|
|
#include <KLocale>
|
|
|
|
#include <QFile>
|
|
#include <QStandardItemModel>
|
|
|
|
#include <unistd.h>
|
|
|
|
AppearanceSettings::AppearanceSettings(QWidget* parent) : QWidget(parent)
|
|
{
|
|
setupUi(this);
|
|
|
|
kcfg_Skin->hide();
|
|
kcfg_SkinInstalledWithKns->hide();
|
|
|
|
m_skins = new QStandardItemModel(this);
|
|
|
|
m_skinListDelegate = new SkinListDelegate(this);
|
|
|
|
skinList->setModel(m_skins);
|
|
skinList->setItemDelegate(m_skinListDelegate);
|
|
|
|
connect(skinList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
|
this, SLOT(updateSkinSetting()));
|
|
connect(skinList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
|
this, SLOT(updateRemoveSkinButton()));
|
|
connect(installButton, SIGNAL(clicked()), this, SLOT(installSkin()));
|
|
connect(removeButton, SIGNAL(clicked()), this, SLOT(removeSelectedSkin()));
|
|
|
|
installButton->setIcon(KIcon("folder"));
|
|
removeButton->setIcon(KIcon("edit-delete"));
|
|
|
|
m_selectedSkinId = Settings::skin();
|
|
|
|
// Get all local skin directories.
|
|
// One for manually installed skins, one for skins installed
|
|
// through KNS3.
|
|
m_localSkinsDir = KStandardDirs::locateLocal("data", "yakuake/skins/");
|
|
m_knsSkinDir = KStandardDirs::locateLocal("data", "yakuake/kns_skins/");
|
|
|
|
populateSkinList();
|
|
}
|
|
|
|
AppearanceSettings::~AppearanceSettings()
|
|
{
|
|
}
|
|
|
|
void AppearanceSettings::showEvent(QShowEvent* event)
|
|
{
|
|
populateSkinList();
|
|
|
|
if (skinList->currentIndex().isValid())
|
|
skinList->scrollTo(skinList->currentIndex());
|
|
|
|
QWidget::showEvent(event);
|
|
}
|
|
|
|
void AppearanceSettings::populateSkinList()
|
|
{
|
|
m_skins->clear();
|
|
|
|
QStringList titleDirs = KGlobal::dirs()->findAllResources("data", "yakuake/skins/*/title.skin")
|
|
+ KGlobal::dirs()->findAllResources("data", m_knsSkinDir + "*/title.skin");
|
|
QStringList tabDirs = KGlobal::dirs()->findAllResources("data", "yakuake/skins/*/tabs.skin")
|
|
+ KGlobal::dirs()->findAllResources("data", m_knsSkinDir + "*/tabs.skin");
|
|
|
|
QStringList skinDirs;
|
|
QStringListIterator i(titleDirs);
|
|
|
|
while (i.hasNext())
|
|
{
|
|
const QString& titleDir = i.next();
|
|
const QString& skinDir(titleDir.section('/', 0, -2));
|
|
|
|
if (tabDirs.contains(skinDir + "/tabs.skin")
|
|
&& !skinDirs.filter(QRegExp(QRegExp::escape(skinDir.section('/', -2, -1)) + '$')).count())
|
|
{
|
|
skinDirs << skinDir;
|
|
}
|
|
}
|
|
|
|
if (skinDirs.count() == 0)
|
|
return;
|
|
|
|
QStringListIterator i2(skinDirs);
|
|
|
|
while (i2.hasNext())
|
|
{
|
|
const QString& skinDir = i2.next();
|
|
|
|
QStandardItem* skin = createSkinItem(skinDir);
|
|
|
|
if (!skin)
|
|
continue;
|
|
|
|
m_skins->appendRow(skin);
|
|
|
|
if (skin->data(SkinId).toString() == m_selectedSkinId)
|
|
skinList->setCurrentIndex(skin->index());
|
|
}
|
|
|
|
m_skins->sort(0);
|
|
|
|
updateRemoveSkinButton();
|
|
}
|
|
|
|
QStandardItem* AppearanceSettings::createSkinItem(const QString& skinDir)
|
|
{
|
|
QString skinId = skinDir.section('/', -1, -1);
|
|
QString titleName, tabName, skinName;
|
|
QString titleAuthor, tabAuthor, skinAuthor;
|
|
QString titleIcon, tabIcon;
|
|
QIcon skinIcon;
|
|
|
|
// Check if the skin dir starts with the path where all
|
|
// KNS3 skins are found in.
|
|
bool isKnsSkin = skinDir.startsWith(m_knsSkinDir);
|
|
|
|
KConfig titleConfig(skinDir + "/title.skin", KConfig::SimpleConfig);
|
|
KConfigGroup titleDescription = titleConfig.group("Description");
|
|
|
|
KConfig tabConfig(skinDir + "/tabs.skin", KConfig::SimpleConfig);
|
|
KConfigGroup tabDescription = tabConfig.group("Description");
|
|
|
|
titleName = titleDescription.readEntry("Skin", "");
|
|
titleAuthor = titleDescription.readEntry("Author", "");
|
|
titleIcon = skinDir + titleDescription.readEntry("Icon", "");
|
|
|
|
tabName = tabDescription.readEntry("Skin", "");
|
|
tabAuthor = tabDescription.readEntry("Author", "");
|
|
tabIcon = skinDir + tabDescription.readEntry("Icon", "");
|
|
|
|
skinName = titleName.isEmpty() ? tabName : titleName;
|
|
skinAuthor = titleAuthor.isEmpty() ? tabAuthor : titleAuthor;
|
|
titleIcon.isEmpty() ? skinIcon.addPixmap(tabIcon) : skinIcon.addPixmap(titleIcon);
|
|
|
|
if (skinName.isEmpty() || skinAuthor.isEmpty())
|
|
skinName = skinId;
|
|
|
|
if (skinAuthor.isEmpty())
|
|
skinAuthor = i18nc("@item:inlistbox Unknown skin author", "Unknown");
|
|
|
|
QStandardItem* skin = new QStandardItem(skinName);
|
|
|
|
skin->setData(skinId, SkinId);
|
|
skin->setData(skinDir, SkinDir);
|
|
skin->setData(skinName, SkinName);
|
|
skin->setData(skinAuthor, SkinAuthor);
|
|
skin->setData(skinIcon, SkinIcon);
|
|
skin->setData(isKnsSkin, SkinInstalledWithKns);
|
|
|
|
return skin;
|
|
}
|
|
|
|
void AppearanceSettings::updateSkinSetting()
|
|
{
|
|
QString skinId = skinList->currentIndex().data(SkinId).toString();
|
|
|
|
if (!skinId.isEmpty())
|
|
{
|
|
m_selectedSkinId = skinId;
|
|
kcfg_Skin->setText(skinId);
|
|
kcfg_SkinInstalledWithKns->setChecked(skinList->currentIndex().data(SkinInstalledWithKns).toBool());
|
|
}
|
|
}
|
|
|
|
void AppearanceSettings::resetSelection()
|
|
{
|
|
m_selectedSkinId = Settings::skin();
|
|
|
|
QModelIndexList skins = m_skins->match(m_skins->index(0, 0), SkinId,
|
|
Settings::skin(), 1, Qt::MatchExactly | Qt::MatchWrap);
|
|
|
|
if (skins.count() > 0) skinList->setCurrentIndex(skins.at(0));
|
|
}
|
|
|
|
void AppearanceSettings::installSkin()
|
|
{
|
|
QString mimeFilter = "application/x-tar application/x-compressed-tar "
|
|
"application/x-bzip-compressed-tar application/zip";
|
|
|
|
KUrl skinUrl = KFileDialog::getOpenUrl(KUrl(), mimeFilter, parentWidget());
|
|
|
|
if (skinUrl.isEmpty()) return;
|
|
|
|
if (!KIO::NetAccess::download(skinUrl, m_installSkinFile, KApplication::activeWindow()))
|
|
{
|
|
KMessageBox::error(parentWidget(), KIO::NetAccess::lastErrorString(),
|
|
i18nc("@title:window", "Failed to Download Skin"));
|
|
|
|
return;
|
|
}
|
|
|
|
QDir skinDir(m_installSkinFile);
|
|
|
|
if (!skinDir.exists())
|
|
{
|
|
KIO::ListJob* job = KIO::listRecursive(KUrl(m_installSkinFile), KIO::HideProgressInfo, false);
|
|
|
|
connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
|
|
this, SLOT(listSkinArchive(KIO::Job*,KIO::UDSEntryList)));
|
|
|
|
connect(job, SIGNAL(result(KJob*)), this, SLOT(validateSkinArchive(KJob*)));
|
|
}
|
|
else
|
|
failInstall(i18nc("@info", "The installer was given a directory, not a file."));
|
|
}
|
|
|
|
void AppearanceSettings::listSkinArchive(KIO::Job* /* job */, const KIO::UDSEntryList& list)
|
|
{
|
|
if (list.count() == 0) return;
|
|
|
|
QListIterator<KIO::UDSEntry> i(list);
|
|
|
|
while (i.hasNext())
|
|
m_installSkinFileList.append(i.next().stringValue(KIO::UDSEntry::UDS_NAME));
|
|
}
|
|
|
|
void AppearanceSettings::validateSkinArchive(KJob* job)
|
|
{
|
|
if (!job->error())
|
|
{
|
|
m_installSkinId = m_installSkinFileList.at(0);
|
|
|
|
if (validateSkin(m_installSkinId, m_installSkinFileList))
|
|
checkForExistingSkin();
|
|
else
|
|
failInstall(i18nc("@info", "Unable to locate required files in the skin archive.<nl/><nl/>The archive appears to be invalid."));
|
|
}
|
|
else
|
|
failInstall(i18nc("@info", "Unable to list the skin archive contents.") + "\n\n" + job->errorString());
|
|
}
|
|
|
|
bool AppearanceSettings::validateSkin(const QString &skinId, const QStringList& fileList)
|
|
{
|
|
bool titleFileFound = false;
|
|
bool tabsFileFound = false;
|
|
QString titleFileName = skinId + "/title.skin";
|
|
QString tabsFileName = skinId + "/tabs.skin";
|
|
|
|
foreach (const QString& fileName, fileList)
|
|
{
|
|
if (fileName.endsWith(titleFileName))
|
|
{
|
|
titleFileFound = true;
|
|
}
|
|
else if (fileName.endsWith(tabsFileName))
|
|
{
|
|
tabsFileFound = true;
|
|
}
|
|
}
|
|
|
|
return titleFileFound && tabsFileFound;
|
|
}
|
|
|
|
void AppearanceSettings::checkForExistingSkin()
|
|
{
|
|
QModelIndexList skins = m_skins->match(m_skins->index(0, 0), SkinId,
|
|
m_installSkinId, 1, Qt::MatchExactly | Qt::MatchWrap);
|
|
|
|
int exists = skins.count();
|
|
|
|
foreach (const QModelIndex& skin, skins)
|
|
{
|
|
if (m_skins->item(skin.row())->data(SkinInstalledWithKns).toBool())
|
|
--exists;
|
|
}
|
|
|
|
if (exists > 0)
|
|
{
|
|
QString skinDir = skins.at(0).data(SkinDir).toString();
|
|
QFile skin(skinDir + "/titles.skin");
|
|
|
|
if (!skin.open(QIODevice::ReadWrite))
|
|
{
|
|
failInstall(i18nc("@info", "This skin appears to be already installed and you lack the required permissions to overwrite it."));
|
|
}
|
|
else
|
|
{
|
|
skin.close();
|
|
|
|
int remove = KMessageBox::warningContinueCancel(parentWidget(),
|
|
i18nc("@info", "This skin appears to be already installed. Do you want to overwrite it?"),
|
|
i18nc("@title:window", "Skin Already Exists"),
|
|
KGuiItem(i18nc("@action:button", "Reinstall Skin")));
|
|
|
|
if (remove == KMessageBox::Continue)
|
|
{
|
|
unlink(QFile::encodeName(skinDir));
|
|
KIO::DeleteJob* job = KIO::del(KUrl(skinDir), KIO::HideProgressInfo);
|
|
connect(job, SIGNAL(result(KJob*)), this, SLOT(installSkinArchive(KJob*)));
|
|
}
|
|
else
|
|
cleanupAfterInstall();
|
|
}
|
|
}
|
|
else
|
|
installSkinArchive();
|
|
}
|
|
|
|
void AppearanceSettings::installSkinArchive(KJob* deleteJob)
|
|
{
|
|
if (deleteJob && deleteJob->error())
|
|
{
|
|
KMessageBox::error(parentWidget(), deleteJob->errorString(), i18nc("@title:Window", "Could Not Delete Skin"));
|
|
|
|
return;
|
|
}
|
|
|
|
KTar skinArchive(m_installSkinFile);
|
|
|
|
if (skinArchive.open(QIODevice::ReadOnly))
|
|
{
|
|
const KArchiveDirectory* skinDir = skinArchive.directory();
|
|
skinDir->copyTo(m_localSkinsDir);
|
|
skinArchive.close();
|
|
|
|
populateSkinList();
|
|
|
|
if (Settings::skin() == m_installSkinId)
|
|
emit settingsChanged();
|
|
|
|
cleanupAfterInstall();
|
|
}
|
|
else
|
|
failInstall(i18nc("@info", "The skin archive file could not be opened."));
|
|
}
|
|
|
|
void AppearanceSettings::failInstall(const QString& error)
|
|
{
|
|
KMessageBox::error(parentWidget(), error, i18nc("@title:window", "Cannot Install Skin"));
|
|
|
|
cleanupAfterInstall();
|
|
}
|
|
|
|
void AppearanceSettings::cleanupAfterInstall()
|
|
{
|
|
KIO::NetAccess::removeTempFile(m_installSkinFile);
|
|
m_installSkinId.clear();
|
|
m_installSkinFile.clear();
|
|
m_installSkinFileList.clear();
|
|
}
|
|
|
|
void AppearanceSettings::updateRemoveSkinButton()
|
|
{
|
|
if (m_skins->rowCount() <= 1)
|
|
{
|
|
removeButton->setEnabled(false);
|
|
return;
|
|
}
|
|
|
|
QString skinDir;
|
|
|
|
QVariant value = skinList->currentIndex().data(SkinDir);
|
|
if (value.isValid())
|
|
skinDir = value.toString();
|
|
|
|
value = skinList->currentIndex().data(SkinInstalledWithKns);
|
|
bool isKnsSkin = value.toBool();
|
|
|
|
// We don't allow the user to remove the default skin
|
|
// or any skin which was installed through KNS3.
|
|
if (skinDir.isEmpty() || isKnsSkin)
|
|
{
|
|
removeButton->setEnabled(false);
|
|
return;
|
|
}
|
|
|
|
QFile titleSkin(skinDir + "/title.skin");
|
|
|
|
if (!titleSkin.open(QIODevice::ReadWrite))
|
|
removeButton->setEnabled(false);
|
|
else
|
|
removeButton->setEnabled(true);
|
|
|
|
titleSkin.close();
|
|
}
|
|
|
|
void AppearanceSettings::removeSelectedSkin()
|
|
{
|
|
if (m_skins->rowCount() <= 1) return;
|
|
|
|
QString skinId = skinList->currentIndex().data(SkinId).toString();
|
|
QString skinDir = skinList->currentIndex().data(SkinDir).toString();
|
|
QString skinName = skinList->currentIndex().data(SkinName).toString();
|
|
QString skinAuthor = skinList->currentIndex().data(SkinAuthor).toString();
|
|
|
|
if (skinDir.isEmpty()) return;
|
|
|
|
int remove = KMessageBox::warningContinueCancel(parentWidget(),
|
|
i18nc("@info", "Do you want to remove \"%1\" by %2?", skinName, skinAuthor),
|
|
i18nc("@title:window", "Remove Skin"),
|
|
KStandardGuiItem::del());
|
|
|
|
if (remove == KMessageBox::Continue)
|
|
{
|
|
unlink(QFile::encodeName(skinDir));
|
|
|
|
bool deleted = KIO::NetAccess::del(KUrl(skinDir), KApplication::activeWindow());
|
|
|
|
if (deleted)
|
|
{
|
|
if (skinId == Settings::skin())
|
|
{
|
|
Settings::setSkin("default");
|
|
Settings::setSkinInstalledWithKns(false);
|
|
Settings::self()->writeConfig();
|
|
emit settingsChanged();
|
|
}
|
|
|
|
resetSelection();
|
|
populateSkinList();
|
|
}
|
|
else
|
|
KMessageBox::error(parentWidget(), i18nc("@info", "Could not remove skin \"%1\".", skinName));
|
|
}
|
|
}
|
|
|
|
QSet<QString> AppearanceSettings::extractKnsSkinIds(const QStringList& fileList)
|
|
{
|
|
QSet<QString> skinIdList;
|
|
|
|
foreach (const QString& file, fileList)
|
|
{
|
|
// We only care about files/directories which are subdirectories of our KNS skins dir.
|
|
if (file.startsWith(m_knsSkinDir, Qt::CaseInsensitive))
|
|
{
|
|
// Get the relative filename (this removes the KNS install dir from the filename).
|
|
QString relativeName = QString(file).remove(m_knsSkinDir, Qt::CaseInsensitive);
|
|
|
|
// Get everything before the first slash - that should be our skins ID.
|
|
QString skinId = relativeName.section('/', 0, QString::SectionSkipEmpty);
|
|
|
|
// Skip all other entries in the file list if we found what we were searching for.
|
|
if (!skinId.isEmpty())
|
|
{
|
|
// First remove all remaining slashes (as there could be leading or trailing ones).
|
|
skinId = skinId.replace('/', QString());
|
|
|
|
skinIdList.insert(skinId);
|
|
}
|
|
}
|
|
}
|
|
|
|
return skinIdList;
|
|
}
|
|
|