diff --git a/plasma/appletbrowser.cpp b/plasma/appletbrowser.cpp index 27e4b141d4..b07adbe833 100644 --- a/plasma/appletbrowser.cpp +++ b/plasma/appletbrowser.cpp @@ -1,388 +1,401 @@ /* * Copyright (C) 2007 Ivan Cukic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * 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 Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plasma/appletbrowser.h" #include #include #include #include #include #include #include #include #include "plasma/corona.h" #include "plasma/containment.h" #include "plasma/applet.h" #include "plasma/appletbrowser/plasmaappletitemmodel_p.h" #include "plasma/appletbrowser/kcategorizeditemsview_p.h" namespace Plasma { class AppletBrowserWidget::Private { public: Private(Containment* cont, AppletBrowserWidget* w) : containment(cont), appletList(0), config("plasmarc"), configGroup(&config, "Applet Browser"), itemModel(configGroup, w), filterModel(w) { } void initFilters(); QString application; Plasma::Containment *containment; KCategorizedItemsView *appletList; QHash runningApplets; // applet name => count //extra hash so we can look up the names of deleted applets QHash appletNames; KConfig config; KConfigGroup configGroup; PlasmaAppletItemModel itemModel; KCategorizedItemsViewModels::DefaultFilterModel filterModel; }; void AppletBrowserWidget::Private::initFilters() { filterModel.clear(); filterModel.addFilter(i18n("All Widgets"), KCategorizedItemsViewModels::Filter(), new KIcon("plasma")); // Recommended emblems and filters QRegExp rx("recommended[.]([0-9A-Za-z]+)[.]caption"); QMapIterator i(configGroup.entryMap()); while (i.hasNext()) { i.next(); if (!rx.exactMatch(i.key())) { continue; } //kDebug() << "These are the key/vals in rc file " << rx.cap(1) << "\n"; QString id = rx.cap(1); QString caption = configGroup.readEntry("recommended." + id + ".caption"); QString icon = configGroup.readEntry("recommended." + id + ".icon"); QString plugins = configGroup.readEntry("recommended." + id + ".plugins"); appletList->addEmblem(i18n("Recommended by %1", caption), new KIcon(icon), KCategorizedItemsViewModels::Filter("recommended." + id, true)); filterModel.addFilter(i18n("Recommended by %1", caption), KCategorizedItemsViewModels::Filter("recommended." + id, true), new KIcon(icon)); } // Filters: Special filterModel.addFilter(i18n("My Favorite Widgets"), KCategorizedItemsViewModels::Filter("favorite", true), new KIcon("bookmarks")); filterModel.addFilter(i18n("Widgets I Have Used Before"), KCategorizedItemsViewModels::Filter("used", true), new KIcon("view-history")); filterModel.addFilter(i18n("Currently Running Widgets"), KCategorizedItemsViewModels::Filter("running", true), new KIcon("view-history")); filterModel.addSeparator(i18n("Categories:")); foreach (const QString& category, Plasma::Applet::knownCategories(application)) { filterModel.addFilter(category, KCategorizedItemsViewModels::Filter("category", category)); } } AppletBrowserWidget::AppletBrowserWidget(Plasma::Containment * containment, QWidget * parent, Qt::WindowFlags f) : QWidget(parent, f), d(new Private(containment, this)) { init(); } AppletBrowserWidget::~AppletBrowserWidget() { delete d; } void AppletBrowserWidget::init() { QVBoxLayout *layout = new QVBoxLayout(this); d->appletList = new KCategorizedItemsView(this); connect(d->appletList, SIGNAL(activated(const QModelIndex &)), this, SLOT(addApplet())); layout->addWidget( d->appletList ); // Other Emblems d->appletList->addEmblem(i18n("Widgets I Have Used Before"), new KIcon("view-history"), KCategorizedItemsViewModels::Filter("used", true)); d->initFilters(); d->appletList->setFilterModel(&d->filterModel); // Other models d->appletList->setItemModel(&d->itemModel); initRunningApplets(); setLayout(layout); } void AppletBrowserWidget::initRunningApplets() { //get applets from corona, count them, send results to model if (!d->containment) { return; } kDebug() << d->runningApplets.count(); Plasma::Corona *c = d->containment->corona(); //we've tried our best to get a corona //we don't want just one containment, we want them all if (!c) { kDebug() << "can't happen"; return; } d->appletNames.clear(); d->runningApplets.clear(); QList containments = c->containments(); foreach (Containment * containment,containments) { connect(containment, SIGNAL(appletAdded(Plasma::Applet*)), this, SLOT(appletAdded(Plasma::Applet*))); //TODO track containments too? QListapplets=containment->applets(); foreach (Applet *applet,applets) { d->runningApplets[applet->name()]++; d->appletNames.insert(applet, applet->name()); connect(applet, SIGNAL(destroyed(QObject*)), this, SLOT(appletDestroyed(QObject*))); } } kDebug() << d->runningApplets; d->itemModel.setRunningApplets(d->runningApplets); } void AppletBrowserWidget::setApplication(const QString& app) { d->application = app; d->initFilters(); d->itemModel.setApplication(app); //FIXME: AFAIK this shouldn't be necessary ... but here it is. need to find out what in that // maze of models and views is screwing up d->appletList->setItemModel(&d->itemModel); kDebug() << d->runningApplets; d->itemModel.setRunningApplets(d->runningApplets); } QString AppletBrowserWidget::application() { return d->application; } void AppletBrowserWidget::setContainment(Plasma::Containment *containment) { d->containment = containment; } Containment *AppletBrowserWidget::containment() const { return d->containment; } void AppletBrowserWidget::addApplet() { kDebug() << "Button ADD clicked"; if (!d->containment) { return; } foreach (AbstractItem *item, d->appletList->selectedItems()) { PlasmaAppletItem *selectedItem = (PlasmaAppletItem *) item; kDebug() << "Adding applet " << selectedItem->name() << "to containment"; d->containment->addApplet(selectedItem->pluginName(), selectedItem->arguments()); } } void AppletBrowserWidget::appletAdded(Plasma::Applet* applet) { QString name = applet->name(); kDebug() << name; d->runningApplets[name]++; d->appletNames.insert(applet, name); connect(applet, SIGNAL(destroyed(QObject*)), this, SLOT(appletDestroyed(QObject*))); d->itemModel.setRunningApplets(name, d->runningApplets[name]); } void AppletBrowserWidget::appletDestroyed(QObject* applet) { kDebug() << applet; Plasma::Applet* a = (Plasma::Applet*)applet; //don't care if it's valid, just need the address QString name = d->appletNames.take(a); int count = 0; if (d->runningApplets.contains(name)) { count = d->runningApplets[name] - 1; if (count < 1) { d->runningApplets.remove(name); } else { d->runningApplets[name] = count; } } d->itemModel.setRunningApplets(name, count); } void AppletBrowserWidget::destroyApplets(const QString &name) { if (!d->containment) { return; } Plasma::Corona *c = d->containment->corona(); //we've tried our best to get a corona //we don't want just one containment, we want them all if (!c) { kDebug() << "can't happen"; return; } foreach (Containment *containment, c->containments()) { QList applets = containment->applets(); foreach (Applet *applet,applets) { d->appletNames.remove(applet); if (applet->name() == name) { applet->disconnect(this); applet->destroy(); } } } d->runningApplets.remove(name); d->itemModel.setRunningApplets(name, 0); } void AppletBrowserWidget::downloadWidgets() { //TODO: implement kDebug() << "GHNS button clicked"; } void AppletBrowserWidget::openWidgetFile() { KService::List offers = KServiceTypeTrader::self()->query("Plasma/PackageStructure"); QStringList filters; - filters << ("*.plasmoid|Plasma Widget"); -/* + filters << i18nc("File dialog filter", "%1|PlasmaWidget", "*.plasma"); + QStringList mimetypes; + foreach (const KService::Ptr &offer, offers) { - QString glob = offer - QString filter( + //filters << offer->property("X-Plasma-PackageMimeFilter").toStringList(); + QString glob = offer->property("X-Plasma-PackageFileFilter").toString(); + + if (!glob.isEmpty()) { + glob = QString("%1|%2").arg(glob).arg(offer->name()); + filters << glob; + } } -*/ + + kDebug() << "filters are" << filters; KFileDialog fd(KUrl(), QString(), this); fd.setOperationMode(KFileDialog::Opening); fd.setMode(KFile::Files | KFile::ExistingOnly); - fd.setFilter(filters.join("\n")); + fd.setFilter(filters.join("\n"));// + mimetypes.join("\n")); fd.exec(); - kDebug() << "selected file" << fd.selectedUrl(); + kDebug() << "selected file" << fd.selectedUrl() << "of type" << fd.currentFilter(); } class AppletBrowser::Private { public: void init(AppletBrowser*, Plasma::Containment*); AppletBrowserWidget *widget; }; AppletBrowser::AppletBrowser(Plasma::Containment * containment, QWidget * parent, Qt::WindowFlags f) : KDialog(parent, f), d(new Private) { d->init(this, containment); } void AppletBrowser::Private::init(AppletBrowser *q, Plasma::Containment *containment) { widget = new AppletBrowserWidget(containment, q); q->setMainWidget(widget); q->setWindowTitle(i18n("Widgets")); q->setButtons(KDialog::Apply | KDialog::Close | KDialog::User1); q->setButtonText(KDialog::Apply, i18n("Add Widget")); - q->setButtonText(KDialog::User1, i18n("Get New Widgets")); + q->setButtonText(KDialog::User1, i18n("Install New Widgets")); KMenu *widgetsMenu = new KMenu(i18n("Get New Widgets"), q); QAction *action = new QAction(KIcon("applications-internet"), i18n("Download from the Internet"), q); connect(action, SIGNAL(triggered(bool)), widget, SLOT(downloadWidgets())); widgetsMenu->addAction(action); action = new QAction(KIcon("applications-internet"), - i18n("Load from file"), q); + i18n("Install from file"), q); connect(action, SIGNAL(triggered(bool)), widget, SLOT(openWidgetFile())); widgetsMenu->addAction(action); q->button(KDialog::User1)->setMenu(widgetsMenu); q->setButtonToolTip(KDialog::Close, i18n("Close the dialog")); q->setButtonWhatsThis(KDialog::Close, i18n("When clicking Close, this dialog will be closed with no further action taken.")); q->setButtonToolTip(KDialog::Apply, i18n("Add selected widgets")); q->setButtonWhatsThis(KDialog::Apply, i18n("When clicking Add Widget, the selected widgets will be added to your desktop.")); q->setButtonToolTip(KDialog::User1, i18n("Download new widgets")); q->setButtonWhatsThis(KDialog::User1, i18n("When clicking Get New Widgets, a dialog will open to allow you to download new widgets. You need to be connected to the Internet.")); connect(q, SIGNAL(applyClicked()), widget, SLOT(addApplet())); + + q->setInitialSize(QSize(400, 600)); + KConfigGroup cg(KGlobal::config(), "PlasmaAppletBrowserDialog"); + q->restoreDialogSize(cg); } AppletBrowser::~AppletBrowser() { + KConfigGroup cg(KGlobal::config(), "PlasmaAppletBrowserDialog"); + saveDialogSize(cg); } void AppletBrowser::setApplication(const QString& app) { d->widget->setApplication( app ); } QString AppletBrowser::application() { return d->widget->application(); } void AppletBrowser::setContainment(Plasma::Containment *containment) { d->widget->setContainment(containment); } Containment* AppletBrowser::containment() const { return d->widget->containment(); } } // namespace Plasma #include "appletbrowser.moc" diff --git a/plasma/package.cpp b/plasma/package.cpp index d558ab3903..d0a0f0ae32 100644 --- a/plasma/package.cpp +++ b/plasma/package.cpp @@ -1,313 +1,325 @@ /****************************************************************************** * Copyright 2007 by Aaron Seigo * * Copyright 2007 by Riccardo Iaconelli * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "package.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "packagemetadata.h" namespace Plasma { class Package::Private { public: Private(const PackageStructure::Ptr st, const QString& p) : structure(st), basePath(p), valid(QFile::exists(basePath)), metadata(0) { if (valid && basePath[basePath.length() - 1] != '/') { basePath.append('/'); } } ~Private() { delete metadata; } - const PackageStructure::Ptr structure; + PackageStructure::Ptr structure; QString basePath; bool valid; PackageMetadata *metadata; }; -Package::Package(const QString& packageRoot, const QString& package, - const PackageStructure::Ptr structure) +Package::Package(const QString& packageRoot, const QString& package, PackageStructure::Ptr structure) : d(new Private(structure, packageRoot + '/' + package)) { + structure->setPath(d->basePath); } -Package::Package(const QString &packagePath, const PackageStructure::Ptr structure) +Package::Package(const QString &packagePath, PackageStructure::Ptr structure) : d(new Private(structure, packagePath)) { + structure->setPath(d->basePath); } Package::~Package() { delete d; } bool Package::isValid() const { if (!d->valid) { return false; } foreach (const char *dir, d->structure->requiredDirectories()) { if (!QFile::exists(d->basePath + "contents/" + d->structure->path(dir))) { kWarning(505) << "Could not find required directory" << dir; d->valid = false; return false; } } foreach (const char *file, d->structure->requiredFiles()) { if (!QFile::exists(d->basePath + "contents/" + d->structure->path(file))) { kWarning(505) << "Could not find required file" << file << ", look in" << d->basePath + "contents/" + d->structure->path(file) << endl; d->valid = false; return false; } } return true; } QString Package::filePath(const char* fileType, const QString& filename) const { if (!d->valid) { return QString(); } QString path = d->structure->path(fileType); if (path.isEmpty()) { return QString(); } path.prepend(d->basePath + "contents/"); if (!filename.isEmpty()) { path.append("/").append(filename); } if (QFile::exists(path)) { return path; } return QString(); } QString Package::filePath(const char* fileType) const { return filePath(fileType, QString()); } QStringList Package::entryList(const char* fileType) const { if (!d->valid) { return QStringList(); } QString path = d->structure->path(fileType); if (path.isEmpty()) { return QStringList(); } QDir dir(d->basePath + "contents/" + path); if (!dir.exists()) { return QStringList(); } return dir.entryList(QDir::Files | QDir::Readable); } const PackageMetadata* Package::metadata() const { + //FIXME: this only works for native plasma packges; should fall back to... PackageStructure? if (!d->metadata) { d->metadata = new PackageMetadata(d->basePath + "metadata.desktop"); } return d->metadata; } +const QString Package::path() const +{ + return d->basePath; +} + +const PackageStructure::Ptr Package::structure() const +{ + return d->structure; +} + //TODO: provide a version of this that allows one to ask for certain types of packages, etc? // should we be using KService here instead/as well? QStringList Package::knownPackages(const QString& packageRoot) // static { QDir dir(packageRoot); if (!dir.exists()) { return QStringList(); } QStringList packages; foreach (const QString& sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) { QString metadata = packageRoot + '/' + sdir + "/metadata.desktop"; if (QFile::exists(metadata)) { PackageMetadata m(metadata); packages << m.name(); } } return packages; } bool Package::installPackage(const QString& package, const QString& packageRoot) // static { //TODO: report *what* failed if something does fail QDir root(packageRoot); if (!root.exists()) { KStandardDirs::makeDir(packageRoot); if (!root.exists()) { kWarning(505) << "Could not create package root directory:" << packageRoot; return false; } } if (!QFile::exists(package)) { kWarning(505) << "No such file:" << package; return false; } KZip archive(package); if (!archive.open(QIODevice::ReadOnly)) { kWarning(505) << "Could not open package file:" << package; return false; } const KArchiveDirectory* source = archive.directory(); const KArchiveEntry* metadata = source->entry("metadata.desktop"); if (!metadata) { kWarning(505) << "No metadata file in package" << package; return false; } QFile f(package); KTempDir tempdir; source->copyTo(tempdir.name()); QString metadataPath = tempdir.name() + "metadata.desktop"; if (!QFile::exists(metadataPath)) { kWarning(505) << "No metadata file in package" << package; return false; } PackageMetadata meta(metadataPath); QString targetName = meta.name(); if (targetName.isEmpty()) { kWarning(505) << "Package name not specified"; return false; } targetName = packageRoot + '/' + targetName; if (QFile::exists(targetName)) { kWarning(505) << targetName << "already exists"; return false; } KIO::FileCopyJob* job = KIO::file_move(tempdir.name(), targetName, -1, KIO::HideProgressInfo); if (!job->exec()) { kWarning(505) << "Could not move package to destination:" << targetName; return false; } // no need to remove the temp dir (which has been moved) tempdir.setAutoRemove(false); // and now we register it as a service =) targetName.append("/metadata.desktop"); // should not installing it as a service disqualify it? // i don't think so since KServiceTypeTrader may not be // used by the installing app in any case, and the // package is properly installed - aseigo registerPackage(targetName); return true; } bool Package::registerPackage(const QString &desktopFilePath) { QString service = KStandardDirs::locateLocal("services", KGlobal::mainComponent().componentName()); KPluginInfo pluginInfo(desktopFilePath); if (pluginInfo.pluginName().isEmpty()) { return false; } service.append(pluginInfo.pluginName()).append(".desktop"); KIO::FileCopyJob *job = KIO::file_copy(desktopFilePath, service, -1, KIO::HideProgressInfo); return job->exec(); } bool Package::createPackage(const PackageMetadata &metadata, const QString &source, const QString &destination, const QString &icon) // static { if (!metadata.isComplete()) { kWarning(550) << "Metadata file is not complete"; return false; } // write metadata in a temporary file KTemporaryFile metadataFile; if (!metadataFile.open()) { return false; } metadata.write(metadataFile.fileName(), icon); // put everything into a zip archive KZip creation(destination); creation.setCompression(KZip::NoCompression); if (!creation.open(QIODevice::WriteOnly)) { return false; } creation.addLocalFile(metadataFile.fileName(), "metadata.desktop"); creation.addLocalDirectory(source, "contents"); creation.close(); return true; } } // Namespace diff --git a/plasma/package.h b/plasma/package.h index 44fefe6a13..cea579a760 100644 --- a/plasma/package.h +++ b/plasma/package.h @@ -1,158 +1,168 @@ /****************************************************************************** * Copyright 2007 by Aaron Seigo * * Copyright 2007 by Riccardo Iaconelli * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #ifndef PLASMA_PACKAGE_H #define PLASMA_PACKAGE_H #include #include #include namespace Plasma { /** * @brief object representing an installed Plasmagik package **/ class PackageMetadata; class PLASMA_EXPORT Package { public: /** * Default constructor * * @arg packageRoot path to the package installation root * @arg package the name of the package * @arg structure the package structure describing this package **/ Package(const QString& packageRoot, const QString& package, const PackageStructure::Ptr structure); /** * Construct a Package object. * * @arg packagePath full path to the package directory * @arg structure the package structure describing this package */ Package(const QString &packagePath, const PackageStructure::Ptr structure); //TODO for 4.1: be able to load an uninstalled/uncompressed file. ~Package(); /** * @return true if all the required components as defined in * the PackageStructure exist **/ bool isValid() const; /** * Get the path to a given file. * * @arg fileType the type of file to look for, as defined in the * package structure * @arg filename the name of the file * @return path to the file on disk. QString() if not found. **/ QString filePath(const char* fileType, const QString& filename) const; /** * Get the path to a given file. * * @arg fileType the type of file to look for, as defined in the * package structure. The type must refer to a file * in the package structure and not a directory. * @return path to the file on disk. QString() if not found **/ QString filePath(const char* fileType) const; /** * Get the list of files of a given type. * * @arg fileType the type of file to look for, as defined in the * package structure. * @return list of files by name, suitable for passing to filePath **/ QStringList entryList(const char* fileType) const; /** * @return the package metadata object. */ const PackageMetadata *metadata() const; + /** + * @return the path to the root of this particular package + */ + const QString path() const; + + /** + * @return the PackageStructure use in this Package + */ + const PackageStructure::Ptr structure() const; + /** * Returns a list of all installed packages * * @param packageRoot path to the directory where Plasmagik packages * have been installed to * @return a list of installed Plasmagik packages **/ static QStringList knownPackages(const QString &packageRoot); /** * Installs a package. * * @param package path to the Plasmagik package * @param packageRoot path to the directory where the package should be * installed to * @return true on successful installation, false otherwise **/ static bool installPackage(const QString &package, const QString &packageRoot); /** * Registers a package described by the given desktop file * * @arg the full path to the desktop file (must be KPluginInfo compatible) * @return true on success, false on failure */ static bool registerPackage(const QString &desktopFilePath); //TODO implement uninstall //static bool uninstallPackage(const QString& package, const QString& packageRoot); /** * Creates a package based on the metadata from the files contained * in the source directory * * @arg metadata description of the package to create * @arg source path to local directory containing the individual * files to be added to the package * @arg destination path to the package that should be created * @arg icon path to the package icon **/ static bool createPackage(const PackageMetadata &metadata, const QString &source, const QString &destination, const QString &icon = QString()); private: Q_DISABLE_COPY(Package) class Private; Private * const d; }; } // Namespace #endif diff --git a/plasma/packagestructure.cpp b/plasma/packagestructure.cpp index 8ebcc2bc0b..49148dbfe7 100644 --- a/plasma/packagestructure.cpp +++ b/plasma/packagestructure.cpp @@ -1,341 +1,357 @@ /****************************************************************************** * Copyright 2007 by Aaron Seigo * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "packagestructure.h" #include #include #include #include #include "package.h" namespace Plasma { class ContentStructure { public: ContentStructure() : directory(false), required(false) { } ContentStructure(const ContentStructure& other) { path = other.path; name = other.name; mimetypes = other.mimetypes; directory = other.directory; required = other.required; } QString path; QString name; QStringList mimetypes; bool directory; bool required; }; class PackageStructure::Private { public: QString type; + QString path; QMap contents; QStringList mimetypes; static QHash structures; -}; + }; QHash PackageStructure::Private::structures; PackageStructure::PackageStructure(QObject *parent, const QString &type) : QObject(parent), d(new Private) { d->type = type; } PackageStructure::~PackageStructure() { delete d; } PackageStructure::Ptr PackageStructure::load(const QString &packageFormat) { if (packageFormat.isEmpty()) { return Ptr(new PackageStructure()); } PackageStructure::Ptr structure = Private::structures[packageFormat]; if (structure) { return structure; } // first we check for plugins in sycoca QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(packageFormat); KService::List offers = KServiceTypeTrader::self()->query("Plasma/PackageStructure", constraint); QVariantList args; QString error; foreach (const KService::Ptr &offer, offers) { PackageStructure::Ptr structure(offer->createInstance(0, args, &error)); if (structure) { - Private::structures[packageFormat] = structure; return structure; } kDebug() << "Couldn't load PackageStructure for" << packageFormat << "! reason given: " << error; } // if that didn't give us any love, then we try to load from a config file structure = new PackageStructure(); QString configPath("plasma/packageformats/%1rc"); configPath = KStandardDirs::locate("data", configPath.arg(packageFormat)); if (!configPath.isEmpty()) { KConfig config(configPath); structure->read(&config); + Private::structures[packageFormat] = structure; } - Private::structures[packageFormat] = structure; return structure; } PackageStructure& PackageStructure::operator=(const PackageStructure& rhs) { if (this == &rhs) { return *this; } *d = *rhs.d; return *this; } QString PackageStructure::type() const { return d->type; } QList PackageStructure::directories() const { QList dirs; QMap::const_iterator it = d->contents.constBegin(); while (it != d->contents.constEnd()) { if (it.value().directory) { dirs << it.key().constData(); } ++it; } return dirs; } QList PackageStructure::requiredDirectories() const { QList dirs; QMap::const_iterator it = d->contents.constBegin(); while (it != d->contents.constEnd()) { if (it.value().directory && it.value().required) { dirs << it.key(); } ++it; } return dirs; } QList PackageStructure::files() const { QList files; QMap::const_iterator it = d->contents.constBegin(); while (it != d->contents.constEnd()) { if (!it.value().directory) { files << it.key(); } ++it; } return files; } QList PackageStructure::requiredFiles() const { QList files; QMap::const_iterator it = d->contents.constBegin(); while (it != d->contents.constEnd()) { if (!it.value().directory && it.value().required) { files << it.key(); } ++it; } return files; } void PackageStructure::addDirectoryDefinition(const char* key, const QString& path, const QString& name) { ContentStructure s; s.name = name; s.path = path; s.directory = true; d->contents[key] = s; } void PackageStructure::addFileDefinition(const char* key, const QString& path, const QString& name) { ContentStructure s; s.name = name; s.path = path; s.directory = false; d->contents[key] = s; } QString PackageStructure::path(const char* key) const { QMap::const_iterator it = d->contents.find(key); if (it == d->contents.constEnd()) { return QString(); } return it.value().path; } QString PackageStructure::name(const char* key) const { QMap::const_iterator it = d->contents.find(key); if (it == d->contents.constEnd()) { return QString(); } return it.value().name; } void PackageStructure::setRequired(const char* key, bool required) { QMap::iterator it = d->contents.find(key); if (it == d->contents.end()) { return; } it.value().required = required; } bool PackageStructure::required(const char* key) const { QMap::const_iterator it = d->contents.find(key); if (it == d->contents.constEnd()) { return false; } return it.value().required; } void PackageStructure::setDefaultMimetypes(QStringList mimetypes) { d->mimetypes = mimetypes; } void PackageStructure::setMimetypes(const char* key, QStringList mimetypes) { QMap::iterator it = d->contents.find(key); if (it == d->contents.end()) { return; } it.value().mimetypes = mimetypes; } QStringList PackageStructure::mimetypes(const char* key) const { QMap::const_iterator it = d->contents.find(key); if (it == d->contents.constEnd()) { return QStringList(); } if (it.value().mimetypes.isEmpty()) { return d->mimetypes; } return it.value().mimetypes; } +void PackageStructure::setPath(const QString &path) +{ + d->path = path; + pathChanged(); +} + +QString PackageStructure::path() const +{ + return d->path; +} + +void PackageStructure::pathChanged() +{ + // Do nothing ... subclasses might, however. +} + void PackageStructure::read(const KConfigBase *config) { d->contents.clear(); d->mimetypes.clear(); d->type = config->group("").readEntry("Type", QString()); QStringList groups = config->groupList(); foreach (QString group, groups) { QByteArray key = group.toAscii(); KConfigGroup entry = config->group(group); QString path = entry.readEntry("Path", QString()); QString name = entry.readEntry("Name", QString()); QStringList mimetypes = entry.readEntry("Mimetypes", QStringList()); bool directory = entry.readEntry("Directory", false); bool required = entry.readEntry("Required", false); if (directory) { addDirectoryDefinition(key, path, name); } else { addFileDefinition(key, path, name); } setMimetypes(key, mimetypes); setRequired(key, required); } } void PackageStructure::write(KConfigBase *config) const { config->group("").writeEntry("Type", type()); QMap::const_iterator it = d->contents.constBegin(); while (it != d->contents.constEnd()) { KConfigGroup group = config->group(it.key()); group.writeEntry("Path", it.value().path); group.writeEntry("Name", it.value().name); if (!it.value().mimetypes.isEmpty()) { group.writeEntry("Mimetypes", it.value().mimetypes); } if (it.value().directory) { group.writeEntry("Directory", true); } if (it.value().required) { group.writeEntry("Required", true); } ++it; } } bool PackageStructure::installPackage(const QString &package, const QString &packageRoot) { return Package::installPackage(package, packageRoot); } } // Plasma namespace #include "packagestructure.moc" diff --git a/plasma/packagestructure.h b/plasma/packagestructure.h index e17cd41640..b1899b81e7 100644 --- a/plasma/packagestructure.h +++ b/plasma/packagestructure.h @@ -1,229 +1,243 @@ /****************************************************************************** * Copyright 2007 by Aaron Seigo * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #ifndef PACKAGESTRUCTURE_H #define PACKAGESTRUCTURE_H #include #include #include #include #include #include class KConfigBase; namespace Plasma { /** * @brief A description of the expected file structure of a given package type * * PackageStructure defines what is in a package. This information is used * to create packages and provides a way to programatically refer to contents. * * An example usage of this class might be: * @code PackageStructure structure; structure.addDirectoryDefinition("images", "pics/", i18n("Images")); QStringList mimetypes; mimetypes << "image/svg" << "image/png" << "image/jpeg"; structure.setMimetypes("images", mimetypes); structure.addDirectoryDefinition("scripts", "code/", i18n("Executable Scripts")); mimetypes.clear(); mimetypes << "text/\*"; structure.setMimetypes("scripts", mimetypes); structure.addFileDefinition("mainscript", "code/main.js", i18n("Main Script File")); structure.setRequired("mainscript", true); @endcode * One may also choose to create a subclass of PackageStructure and include the setup * in the constructor. * * Either way, PackageStructure creates a sort of "contract" between the packager and * the application which is also self-documenting. **/ class PLASMA_EXPORT PackageStructure : public QObject, public QSharedData { Q_OBJECT public: typedef KSharedPtr Ptr; /** * Default constructor for a package structure definition * * @arg type the type of package. This is often application specific. **/ explicit PackageStructure(QObject *parent = 0, const QString &type = i18n("Invalid")); /** * Destructor **/ virtual ~PackageStructure(); /** * Assignment operator **/ PackageStructure& operator=(const PackageStructure& rhs); /** * Loads a package format by name. * * @arg format If not empty, attempts to locate the given format, either * from built-ins or via plugins. * @return a package that matches the format, if available. The caller * is responsible for deleting the object. */ - static PackageStructure::Ptr load(const QString &package); + static PackageStructure::Ptr load(const QString &packageFormat); /** * Type of package this structure describes **/ QString type() const; /** * The directories defined for this package **/ QList directories() const; /** * The required directories defined for this package **/ QList requiredDirectories() const; /** * The individual files, by key, that are defined for this package **/ QList files() const; /** * The individual required files, by key, that are defined for this package **/ QList requiredFiles() const; /** * Adds a directory to the structure of the package. It is added as * a not-required element with no associated mimetypes. * * @param key used as an internal label for this directory * @param path the path within the the package for this directory * @param name the user visible (translated) name for the directory **/ void addDirectoryDefinition(const char* key, const QString& path, const QString& name); /** * Adds a file to the structure of the package. It is added as * a not-required element with no associated mimetypes. * * @param key used as an internal label for this file * @param path the path within the the package for this file * @param name the user visible (translated) name for the file **/ void addFileDefinition(const char* key, const QString& path, const QString& name); /** * @return path relative to the package root for the given entry **/ QString path(const char* key) const; /** * @return user visible name for the given entry **/ QString name(const char* key) const; /** * Sets whether or not a given part of the structure is required or not. * The path must already have been added using addDirectoryDefinition * or addFileDefinition. * * @param path the path of the entry within the package * @param required true if this entry is required, false if not */ void setRequired(const char* key, bool required); /** * @return true if the item at path exists and is required **/ bool required(const char* key) const; /** * Defines the default mimetypes for any definitions that do not have * associated mimetypes. Handy for packages with only one or predominantly * one file type. * * @param mimetypes a list of mimetypes **/ void setDefaultMimetypes(QStringList mimetypes); /** * Define mimetypes for a given part of the structure * The path must already have been added using addDirectoryDefinition * or addFileDefinition. * * @param path the path of the entry within the package * @param mimetypes a list of mimetypes **/ void setMimetypes(const char* key, QStringList mimetypes); /** * @return the mimetypes associated with the path, if any **/ QStringList mimetypes(const char* key) const; + /** + * Sets the path to the package. Useful for package formats + * which do not have well defined contents prior to installation. + */ + void setPath(const QString &path); + + /** + * @return the path to the package, or QString() if none + */ + QString path() const; + /** * Read a package structure from a config file. */ void read(const KConfigBase *config); /** * Write this package structure to a config file. */ void write(KConfigBase *config) const; /** * Installs a package matching this package structure. By default simply calls * Plasma::Package::install. * - * @param package path to the Plasmagik package + * @param archivePath path to the package archive file * @param packageRoot path to the directory where the package should be * installed to * @return true on successful installation, false otherwise **/ - virtual bool installPackage(const QString &package, const QString &packageRoot); + virtual bool installPackage(const QString &archivePath, const QString &packageRoot); + +protected: + virtual void pathChanged(); private: class Private; Private * const d; }; /** * Register an applet when it is contained in a loadable module */ #define K_EXPORT_PLASMA_PACKAGESTRUCTURE(libname, classname) \ K_PLUGIN_FACTORY(factory, registerPlugin();) \ K_EXPORT_PLUGIN(factory("plasma_packagestructure_" #libname)) } // Plasma namespace #endif diff --git a/plasma/servicetypes/plasma-packagestructure.desktop b/plasma/servicetypes/plasma-packagestructure.desktop index 3de242023d..e4b9bdd921 100644 --- a/plasma/servicetypes/plasma-packagestructure.desktop +++ b/plasma/servicetypes/plasma-packagestructure.desktop @@ -1,7 +1,10 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/PackageStructure Comment=Plasma package structure definition Comment[km]=កា​រកំណត់​រចនាសម្ព័ន្ធ​កញ្ចប់​ប្លាស្មា Comment[zh_TW]=Plasma 套件結構定義 +[PropertyDef::X-Plasma-PackageFileFilter] +Type=QString +