diff --git a/plugin/lipstickplugin.cpp b/plugin/lipstickplugin.cpp index 9fbe2313..b748ec5b 100644 --- a/plugin/lipstickplugin.cpp +++ b/plugin/lipstickplugin.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,9 @@ void LipstickPlugin::registerTypes(const char *uri) qmlRegisterType("org.nemomobile.lipstick", 0, 1, "NotificationListModel"); qmlRegisterType("org.nemomobile.lipstick", 0, 1, "Notification"); qmlRegisterType("org.nemomobile.lipstick", 0, 1, "LauncherItem"); + qmlRegisterType("org.nemomobile.lipstick", 0, 1, "LauncherFolderModel"); + qmlRegisterType("org.nemomobile.lipstick", 0, 1, "LauncherFolderItem"); + qmlRegisterUncreatableType("org.nemomobile.lipstick", 0, 1, "NotificationPreviewPresenter", "This type is initialized by HomeApplication"); qmlRegisterUncreatableType("org.nemomobile.lipstick", 0, 1, "NotificationFeedbackPlayer", "This type is initialized by HomeApplication"); qmlRegisterUncreatableType("org.nemomobile.lipstick", 0, 1, "VolumeControl", "This type is initialized by HomeApplication"); diff --git a/src/components/launcherfoldermodel.cpp b/src/components/launcherfoldermodel.cpp new file mode 100644 index 00000000..bcd3084b --- /dev/null +++ b/src/components/launcherfoldermodel.cpp @@ -0,0 +1,508 @@ +// This file is part of lipstick, a QML desktop library +// +// Copyright (c) 2014 Jolla Ltd. +// Contact: Martin Jones +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation +// and appearing in the file LICENSE.LGPL included in the packaging +// of this file. +// +// This code 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 Lesser General Public License for more details. + +#include "launcheritem.h" +#include "launcherfoldermodel.h" +#include "launchermodel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const int FOLDER_MODEL_SAVE_TIMER_MS = 1000; +static const QString CONFIG_FOLDER_SUBDIRECTORY("/lipstick/"); +static const QString CONFIG_MENU_FILENAME("applications.menu"); +static const QString DEFAULT_ICON_ID("icon-launcher-folder-01"); + +// This is modeled after the freedesktop.org menu files http://standards.freedesktop.org/menu-spec/latest/ +// but handles only the basic elements, i.e. no merging, filtering, layout, etc. is supported. + +LauncherFolderItem::LauncherFolderItem(QObject *parent) + : QObjectListModel(parent), mIconId(DEFAULT_ICON_ID) +{ + connect(this, SIGNAL(itemRemoved(QObject*)), this, SLOT(handleRemoved(QObject*))); + connect(this, SIGNAL(itemAdded(QObject*)), this, SLOT(handleAdded(QObject*))); + connect(this, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SIGNAL(saveNeeded())); +} + +LauncherModel::ItemType LauncherFolderItem::type() const +{ + return LauncherModel::Folder; +} + +const QString &LauncherFolderItem::title() const +{ + return mTitle; +} + +void LauncherFolderItem::setTitle(const QString &title) +{ + if (title == mTitle) + return; + + mTitle = title; + emit titleChanged(); + emit saveNeeded(); +} + +const QString &LauncherFolderItem::iconId() const +{ + return mIconId; +} + +void LauncherFolderItem::setIconId(const QString &icon) +{ + if (icon == mIconId) + return; + + mIconId = icon; + saveDirectoryFile(); + emit iconIdChanged(); +} + +bool LauncherFolderItem::isUpdating() const +{ + LauncherFolderItem *me = const_cast(this); + for (int i = 0; i < rowCount(); ++i) { + const LauncherItem *launcherItem = qobject_cast(me->get(i)); + if (launcherItem && launcherItem->isUpdating()) + return true; + } + + return false; +} + +int LauncherFolderItem::updatingProgress() const +{ + int updatingCount = 0; + int updatingTotal = 0; + LauncherFolderItem *me = const_cast(this); + for (int i = 0; i < rowCount(); ++i) { + const LauncherItem *launcherItem = qobject_cast(me->get(i)); + if (launcherItem && launcherItem->isUpdating()) { + int progress = launcherItem->updatingProgress(); + if (progress < 0 || progress > 100) + return progress; + ++updatingCount; + updatingTotal += progress; + } + } + + return updatingCount ? updatingTotal / updatingCount : 0; +} + +LauncherFolderItem *LauncherFolderItem::parentFolder() const +{ + return mParentFolder; +} + +void LauncherFolderItem::setParentFolder(LauncherFolderItem *parent) +{ + if (parent == mParentFolder) + return; + + mParentFolder = parent; + emit parentFolderChanged(); +} + +// Creates a folder and moves the item at that index into the folder +LauncherFolderItem *LauncherFolderItem::createFolder(int index, const QString &name) +{ + if (index < 0 || index > rowCount()) + return 0; + + LauncherFolderItem *folder = new LauncherFolderItem(this); + folder->setTitle(name); + folder->setParentFolder(this); + QObject *item = get(index); + insertItem(index, folder); + if (item) { + removeItem(item); + folder->addItem(item); + } + + emit saveNeeded(); + + return folder; +} + +void LauncherFolderItem::destroyFolder() +{ + if (itemCount() != 0) + qWarning() << "Removing a folder that is not empty."; + if (mParentFolder) + mParentFolder->removeItem(this); + if (!mDirectoryFile.isEmpty()) { + QFile file(mDirectoryFile); + file.remove(); + } + + emit saveNeeded(); + + deleteLater(); +} + +LauncherFolderItem *LauncherFolderItem::findContainer(QObject *item) +{ + LauncherFolderItem *me = const_cast(this); + for (int i = 0; i < rowCount(); ++i) { + QObject *obj = me->get(i); + if (obj == item) { + return this; + } else if (LauncherFolderItem *subFolder = qobject_cast(obj)) { + LauncherFolderItem *folder = subFolder->findContainer(item); + if (folder) + return folder; + } + } + + return 0; +} + +QString LauncherFolderItem::directoryFile() const +{ + return mDirectoryFile; +} + +void LauncherFolderItem::loadDirectoryFile(const QString &filename) +{ + mDirectoryFile = filename; + if (!mDirectoryFile.startsWith('/')) { + QFileInfo fi(LauncherFolderModel::configFile()); + mDirectoryFile = fi.absoluteDir().absoluteFilePath(mDirectoryFile); + } + + GKeyFile *keyfile = g_key_file_new(); + GError *err = NULL; + + if (g_key_file_load_from_file(keyfile, mDirectoryFile.toLatin1(), G_KEY_FILE_NONE, &err)) { + mIconId = QString::fromLatin1(g_key_file_get_string(keyfile, "Desktop Entry", "Icon", &err)); + emit iconIdChanged(); + } + + if (err != NULL) { + qWarning() << "Failed to load .directory file" << err->message; + g_error_free(err); + } + + g_key_file_free(keyfile); +} + +void LauncherFolderItem::saveDirectoryFile() +{ + QScopedPointer dirFile; + if (mDirectoryFile.isEmpty()) { + QFileInfo fi(LauncherFolderModel::configFile()); + QTemporaryFile *tempFile = new QTemporaryFile(fi.absoluteDir().absoluteFilePath("FolderXXXXXX.directory")); + dirFile.reset(tempFile); + tempFile->open(); + tempFile->setAutoRemove(false); + mDirectoryFile = tempFile->fileName(); + emit directoryFileChanged(); + emit saveNeeded(); + } else { + dirFile.reset(new QFile(mDirectoryFile)); + dirFile.data()->open(QIODevice::WriteOnly); + } + + if (!dirFile.data()->isOpen()) { + qWarning() << "Cannot open" << mDirectoryFile; + return; + } + + GKeyFile *keyfile = g_key_file_new(); + GError *err = NULL; + + g_key_file_load_from_file(keyfile, mDirectoryFile.toLatin1(), G_KEY_FILE_NONE, &err); + g_key_file_set_string(keyfile, "Desktop Entry", "Icon", mIconId.toLatin1()); + + gchar *data = g_key_file_to_data(keyfile, NULL, &err); + dirFile.data()->write(data); + dirFile.data()->close(); + g_free(data); + + g_key_file_free(keyfile); +} + +void LauncherFolderItem::handleAdded(QObject *item) +{ + const LauncherItem *launcherItem = qobject_cast(item); + const LauncherFolderItem *folder = qobject_cast(item); + + if (launcherItem) { + if (launcherItem->isUpdating()) { + emit isUpdatingChanged(); + emit updatingProgressChanged(); + } + connect(item, SIGNAL(isTemporaryChanged()), this, SIGNAL(saveNeeded())); + } else if (folder) { + if (folder->isUpdating()) { + emit isUpdatingChanged(); + emit updatingProgressChanged(); + } + connect(item, SIGNAL(saveNeeded()), this, SIGNAL(saveNeeded())); + } + + if (launcherItem || folder) { + connect(item, SIGNAL(isUpdatingChanged()), this, SIGNAL(isUpdatingChanged())); + connect(item, SIGNAL(updatingProgressChanged()), this, SIGNAL(updatingProgressChanged())); + } + + emit saveNeeded(); +} + +void LauncherFolderItem::handleRemoved(QObject *item) +{ + const LauncherItem *launcherItem = qobject_cast(item); + const LauncherFolderItem *folder = qobject_cast(item); + + if (launcherItem) { + if (launcherItem->isUpdating()) { + emit isUpdatingChanged(); + emit updatingProgressChanged(); + } + disconnect(item, SIGNAL(isTemporaryChanged()), this, SIGNAL(saveNeeded())); + } else if (folder) { + if (folder->isUpdating()) { + emit isUpdatingChanged(); + emit updatingProgressChanged(); + } + disconnect(item, SIGNAL(saveNeeded()), this, SIGNAL(saveNeeded())); + } + + if (launcherItem || folder) { + disconnect(item, SIGNAL(isUpdatingChanged()), this, SIGNAL(isUpdatingChanged())); + disconnect(item, SIGNAL(updatingProgressChanged()), this, SIGNAL(updatingProgressChanged())); + } + + emit saveNeeded(); +} + +//============ + +LauncherFolderModel::LauncherFolderModel(QObject *parent) + : LauncherFolderItem(parent) + , mLauncherModel(new LauncherModel(this)) +{ + connect(mLauncherModel, SIGNAL(itemRemoved(QObject*)), + this, SLOT(appRemoved(QObject*))); + connect(mLauncherModel, SIGNAL(itemAdded(QObject*)), + this, SLOT(appAdded(QObject*))); + connect(&mSaveTimer, SIGNAL(timeout()), this, SLOT(save())); + + QDir config; + config.mkpath(configDir()); + + load(); + + connect(this, SIGNAL(saveNeeded()), this, SLOT(scheduleSave())); +} + +QStringList LauncherFolderModel::directories() const +{ + return mLauncherModel->directories(); +} + +void LauncherFolderModel::setDirectories(QStringList dirs) +{ + mLauncherModel->setDirectories(dirs); +} + +QStringList LauncherFolderModel::iconDirectories() const +{ + return mLauncherModel->iconDirectories(); +} + +void LauncherFolderModel::setIconDirectories(QStringList dirs) +{ + mLauncherModel->setIconDirectories(dirs); +} + +// Move item to folder at index. If index < 0 the item will be appended. +bool LauncherFolderModel::moveToFolder(QObject *item, LauncherFolderItem *folder, int index) +{ + if (!item || !folder) + return false; + + LauncherFolderItem *source = findContainer(item); + if (!source) + return false; + + source->removeItem(item); + + if (index >= 0) + folder->insertItem(index, item); + else + folder->addItem(item); + if (LauncherFolderItem *f = qobject_cast(item)) + f->setParentFolder(folder); + + scheduleSave(); + + return true; +} + +// An app removed from system +void LauncherFolderModel::appRemoved(QObject *item) +{ + LauncherFolderItem *folder = findContainer(item); + if (folder) { + folder->removeItem(item); + scheduleSave(); + } +} + +// An app added to system +void LauncherFolderModel::appAdded(QObject *item) +{ + addItem(item); + scheduleSave(); +} + +void LauncherFolderModel::import() +{ + for (int i = 0; i < mLauncherModel->rowCount(); ++i) { + addItem(mLauncherModel->get(i)); + } +} + +void LauncherFolderModel::scheduleSave() +{ + mSaveTimer.start(FOLDER_MODEL_SAVE_TIMER_MS); +} + +QString LauncherFolderModel::configDir() +{ + return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + CONFIG_FOLDER_SUBDIRECTORY; +} + +QString LauncherFolderModel::configFile() +{ + return configDir() + CONFIG_MENU_FILENAME; +} + +void LauncherFolderModel::save() +{ + mSaveTimer.stop(); + QFile file(configFile()); + if (!file.open(QIODevice::WriteOnly)) { + qWarning() << "Failed to save apps menu" << configFile(); + return; + } + + QXmlStreamWriter xml(&file); + xml.setAutoFormatting(true); + xml.writeStartDocument(); + saveFolder(xml, this); + xml.writeEndDocument(); +} + +void LauncherFolderModel::saveFolder(QXmlStreamWriter &xml, LauncherFolderItem *folder) +{ + xml.writeStartElement("Menu"); + xml.writeTextElement("Name", folder->title()); + if (!folder->directoryFile().isEmpty()) + xml.writeTextElement("Directory", folder->directoryFile()); + + for (int i = 0; i < folder->rowCount(); ++i) { + LauncherItem *item = qobject_cast(folder->get(i)); + if (item) { + if (!item->isTemporary()) + xml.writeTextElement("Filename", item->filename()); + } else if (LauncherFolderItem *subFolder = qobject_cast(folder->get(i))) { + saveFolder(xml, subFolder); + } + } + xml.writeEndElement(); +} + +void LauncherFolderModel::load() +{ + QStack menus; + QString textData; + + QFile file(configFile()); + if (!file.open(QIODevice::ReadOnly)) { + // We haven't saved a folder model yet - import all apps. + import(); + return; + } + + QVector loadedItems(mLauncherModel->itemCount()); + loadedItems.fill(false); + int loadedCount = 0; + + QXmlStreamReader xml(&file); + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement()) { + if (xml.name() == QLatin1String("Menu")) { + LauncherFolderItem *folder = 0; + if (menus.isEmpty()) + folder = this; + else + folder = new LauncherFolderItem(this); + if (!menus.isEmpty()) { + folder->setParentFolder(menus.top()); + menus.top()->addItem(folder); + } + menus.push(folder); + } + } else if (xml.isEndElement()) { + if (xml.name() == QLatin1String("Menu")) { + menus.pop(); + } else if (xml.name() == QLatin1String("Name")) { + if (!menus.isEmpty()) { + menus.top()->setTitle(textData); + } + } else if (xml.name() == QLatin1String("Directory")) { + if (!menus.isEmpty()) { + LauncherFolderItem *folder = menus.top(); + folder->loadDirectoryFile(textData); + } + } else if (xml.name() == QLatin1String("Filename")) { + if (!menus.isEmpty()) { + int idx = mLauncherModel->indexInModel(textData); + if (idx >= 0) { + loadedItems[idx] = true; + LauncherItem *item = qobject_cast(mLauncherModel->get(idx)); + if (item) { + loadedCount++; + LauncherFolderItem *folder = menus.top(); + folder->addItem(item); + } + } + } + } + textData.clear(); + } else if (xml.isCharacters()) { + textData = xml.text().toString(); + } + } + + for (int i = 0; i < loadedItems.count(); ++i) { + if (!loadedItems.at(i)) { + addItem(mLauncherModel->get(i)); + } + } +} diff --git a/src/components/launcherfoldermodel.h b/src/components/launcherfoldermodel.h new file mode 100644 index 00000000..49a3d95a --- /dev/null +++ b/src/components/launcherfoldermodel.h @@ -0,0 +1,133 @@ +// This file is part of lipstick, a QML desktop library +// +// Copyright (c) 2014 Jolla Ltd. +// Contact: Martin Jones +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation +// and appearing in the file LICENSE.LGPL included in the packaging +// of this file. +// +// This code 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 Lesser General Public License for more details. + +#ifndef LAUNCHERFOLDERMODEL_H +#define LAUNCHERFOLDERMODEL_H + +#include +#include +#include +#include +#include +#include "qobjectlistmodel.h" +#include "launchermodel.h" +#include "lipstickglobal.h" + +class LauncherModel; +class QXmlStreamWriter; +class MDesktopEntry; + +class LIPSTICK_EXPORT LauncherFolderItem : public QObjectListModel +{ + Q_OBJECT + Q_PROPERTY(LauncherModel::ItemType type READ type CONSTANT) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(QString iconId READ iconId WRITE setIconId NOTIFY iconIdChanged) + Q_PROPERTY(bool isUpdating READ isUpdating NOTIFY isUpdatingChanged) + Q_PROPERTY(int updatingProgress READ updatingProgress NOTIFY updatingProgressChanged) + Q_PROPERTY(LauncherFolderItem *parentFolder READ parentFolder NOTIFY parentFolderChanged) + +public: + LauncherFolderItem(QObject *parent = 0); + + LauncherModel::ItemType type() const; + + const QString &title() const; + void setTitle(const QString &name); + + const QString &iconId() const; + void setIconId(const QString &icon); + + bool isUpdating() const; + int updatingProgress() const; + + LauncherFolderItem *parentFolder() const; + void setParentFolder(LauncherFolderItem *parent); + + Q_INVOKABLE LauncherFolderItem *createFolder(int index, const QString &name); + Q_INVOKABLE void destroyFolder(); + + LauncherFolderItem *findContainer(QObject *item); + + QString directoryFile() const; + void loadDirectoryFile(const QString &filename); + void saveDirectoryFile(); + +signals: + void titleChanged(); + void iconIdChanged(); + void isUpdatingChanged(); + void updatingProgressChanged(); + void parentFolderChanged(); + void directoryFileChanged(); + void saveNeeded(); + +private slots: + void handleAdded(QObject*); + void handleRemoved(QObject*); + +private: + QString mTitle; + QString mIconId; + QString mDirectoryFile; + QSharedPointer mDesktopEntry; + QPointer mParentFolder; +}; + + +class LIPSTICK_EXPORT LauncherFolderModel : public LauncherFolderItem +{ + Q_OBJECT + Q_PROPERTY(QStringList directories READ directories WRITE setDirectories NOTIFY directoriesChanged) + Q_PROPERTY(QStringList iconDirectories READ iconDirectories WRITE setIconDirectories NOTIFY iconDirectoriesChanged) + +public: + LauncherFolderModel(QObject *parent = 0); + + QStringList directories() const; + void setDirectories(QStringList); + + QStringList iconDirectories() const; + void setIconDirectories(QStringList); + + Q_INVOKABLE bool moveToFolder(QObject *item, LauncherFolderItem *folder, int index = -1); + + void import(); + + static QString configFile(); + +public slots: + void load(); + void save(); + +signals: + void directoriesChanged(); + void iconDirectoriesChanged(); + +private slots: + void scheduleSave(); + void appRemoved(QObject *item); + void appAdded(QObject *item); + +private: + void saveFolder(QXmlStreamWriter &xml, LauncherFolderItem *folder); + static QString configDir(); + + LauncherModel *mLauncherModel; + QTimer mSaveTimer; +}; + +#endif diff --git a/src/components/launcheritem.cpp b/src/components/launcheritem.cpp index fc0d6fd0..b45a5471 100644 --- a/src/components/launcheritem.cpp +++ b/src/components/launcheritem.cpp @@ -70,6 +70,11 @@ LauncherItem::~LauncherItem() { } +LauncherModel::ItemType LauncherItem::type() const +{ + return LauncherModel::Application; +} + void LauncherItem::setFilePath(const QString &filePath) { if (!filePath.isEmpty() && QFile(filePath).exists()) { @@ -86,6 +91,16 @@ QString LauncherItem::filePath() const return !_desktopEntry.isNull() ? _desktopEntry->fileName() : QString(); } +QString LauncherItem::filename() const +{ + QString filename = filePath(); + int sep = filename.lastIndexOf('/'); + if (sep == -1) + return QString(); + + return filename.mid(sep+1); +} + QString LauncherItem::exec() const { return !_desktopEntry.isNull() ? _desktopEntry->exec() : QString(); diff --git a/src/components/launcheritem.h b/src/components/launcheritem.h index 3a599e61..e3091093 100644 --- a/src/components/launcheritem.h +++ b/src/components/launcheritem.h @@ -30,6 +30,7 @@ #define LAUNCHER_DEBUG(things) #endif +#include "launchermodel.h" #include "lipstickglobal.h" class MDesktopEntry; @@ -39,6 +40,7 @@ class LIPSTICK_EXPORT LauncherItem : public QObject Q_OBJECT Q_DISABLE_COPY(LauncherItem) + Q_PROPERTY(LauncherModel::ItemType type READ type CONSTANT) Q_PROPERTY(QString filePath READ filePath WRITE setFilePath NOTIFY itemChanged) Q_PROPERTY(QString exec READ exec NOTIFY itemChanged) Q_PROPERTY(QString title READ title NOTIFY itemChanged) @@ -73,8 +75,10 @@ public slots: const QString &iconPath, const QString &desktopFile, QObject *parent); virtual ~LauncherItem(); + LauncherModel::ItemType type() const; void setFilePath(const QString &filePath); QString filePath() const; + QString filename() const; QString exec() const; QString title() const; QString entryType() const; diff --git a/src/components/launchermodel.cpp b/src/components/launchermodel.cpp index 166092d2..b2142362 100644 --- a/src/components/launchermodel.cpp +++ b/src/components/launchermodel.cpp @@ -21,6 +21,7 @@ #include #include +#include "launcheritem.h" #include "launchermodel.h" @@ -514,14 +515,34 @@ void LauncherModel::savePositions() _fileSystemWatcher.addPath(_launcherSettings.fileName()); } -LauncherItem *LauncherModel::itemInModel(const QString &path) +int LauncherModel::findItem(const QString &path, LauncherItem **item) { - foreach (LauncherItem *item, *getList()) { - if (item->filePath() == path) { - return item; + QList *list = getList(); + for (int i = 0; i < list->count(); ++i) { + LauncherItem *listItem = list->at(i); + if (listItem->filePath() == path || listItem->filename() == path) { + if (item) + *item = listItem; + return i; } } - return 0; + + if (item) + *item = 0; + + return -1; +} + +LauncherItem *LauncherModel::itemInModel(const QString &path) +{ + LauncherItem *result = 0; + (void)findItem(path, &result); + return result; +} + +int LauncherModel::indexInModel(const QString &path) +{ + return findItem(path, 0); } LauncherItem *LauncherModel::packageInModel(const QString &packageName) diff --git a/src/components/launchermodel.h b/src/components/launchermodel.h index dd7345cf..b06e6c62 100644 --- a/src/components/launchermodel.h +++ b/src/components/launchermodel.h @@ -23,12 +23,12 @@ #include #include -#include "launcheritem.h" #include "qobjectlistmodel.h" #include "lipstickglobal.h" #include "launchermonitor.h" #include "launcherdbus.h" +class LauncherItem; class LIPSTICK_EXPORT LauncherModel : public QObjectListModel { @@ -38,6 +38,8 @@ class LIPSTICK_EXPORT LauncherModel : public QObjectListModel Q_PROPERTY(QStringList directories READ directories WRITE setDirectories NOTIFY directoriesChanged) Q_PROPERTY(QStringList iconDirectories READ iconDirectories WRITE setIconDirectories NOTIFY iconDirectoriesChanged) + Q_ENUMS(ItemType) + QFileSystemWatcher _fileSystemWatcher; QSettings _launcherSettings; QSettings _globalSettings; @@ -57,6 +59,11 @@ private slots: explicit LauncherModel(QObject *parent = 0); virtual ~LauncherModel(); + enum ItemType { + Application, + Folder + }; + QStringList directories() const; void setDirectories(QStringList); @@ -69,6 +76,8 @@ private slots: void updatingFinished(const QString &packageName, const QString &serviceName); void requestLaunch(const QString &packageName); + LauncherItem *itemInModel(const QString &path); + int indexInModel(const QString &path); public slots: void savePositions(); @@ -81,7 +90,7 @@ public slots: private: void reorderItems(const QMap &itemsWithPositions); void loadPositions(); - LauncherItem *itemInModel(const QString &path); + int findItem(const QString &path, LauncherItem **item); LauncherItem *packageInModel(const QString &packageName); QVariant launcherPos(const QString &path); LauncherItem *addItemIfValid(const QString &path, QMap &itemsWithPositions); diff --git a/src/src.pro b/src/src.pro index aa53a00a..70d5e571 100644 --- a/src/src.pro +++ b/src/src.pro @@ -37,6 +37,7 @@ PUBLICHEADERS += \ components/launcherwatchermodel.h \ components/launchermonitor.h \ components/launcherdbus.h \ + components/launcherfoldermodel.h \ notifications/notificationmanager.h \ notifications/lipsticknotification.h \ notifications/notificationlistmodel.h \ @@ -84,6 +85,7 @@ SOURCES += \ components/launcherwatchermodel.cpp \ components/launchermonitor.cpp \ components/launcherdbus.cpp \ + components/launcherfoldermodel.cpp \ notifications/notificationmanager.cpp \ notifications/notificationmanageradaptor.cpp \ notifications/lipsticknotification.cpp \ diff --git a/src/utilities/qobjectlistmodel.cpp b/src/utilities/qobjectlistmodel.cpp index a3c0e70a..c8a3eed2 100644 --- a/src/utilities/qobjectlistmodel.cpp +++ b/src/utilities/qobjectlistmodel.cpp @@ -106,6 +106,7 @@ void QObjectListModel::removeItem(QObject *item) _list->removeAt(index); disconnect(item, SIGNAL(destroyed()), this, SLOT(removeDestroyedItem())); endRemoveRows(); + emit itemRemoved(item); emit itemCountChanged(); } } @@ -114,8 +115,9 @@ void QObjectListModel::removeItem(int index) { beginRemoveRows(QModelIndex(), index, index); disconnect(((QObject*)_list->at(index)), SIGNAL(destroyed()), this, SLOT(removeDestroyedItem())); - _list->removeAt(index); + QObject *item = _list->takeAt(index); endRemoveRows(); + emit itemRemoved(item); emit itemCountChanged(); } diff --git a/src/utilities/qobjectlistmodel.h b/src/utilities/qobjectlistmodel.h index 2c422217..af530081 100644 --- a/src/utilities/qobjectlistmodel.h +++ b/src/utilities/qobjectlistmodel.h @@ -43,7 +43,7 @@ class LIPSTICK_EXPORT QObjectListModel : public QAbstractListModel void removeItem(QObject *item); void removeItem(int index); Q_INVOKABLE QObject* get(int index); - int indexOf(QObject *obj) const; + Q_INVOKABLE int indexOf(QObject *obj) const; template QList *getList(); @@ -58,6 +58,7 @@ private slots: signals: void itemAdded(QObject *item); + void itemRemoved(QObject *item); void itemCountChanged(); }; diff --git a/tests/ut_launchermodel/ut_launchermodel.cpp b/tests/ut_launchermodel/ut_launchermodel.cpp index a8d9ab28..550e4e6b 100644 --- a/tests/ut_launchermodel/ut_launchermodel.cpp +++ b/tests/ut_launchermodel/ut_launchermodel.cpp @@ -15,6 +15,7 @@ #include +#include "launcheritem.h" #include "launchermodel.h" #include "ut_launchermodel.h" #include "mdesktopentry.h"