2 * Copyright 2013 Canonical Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * Michael Zanetti <michael.zanetti@canonical.com>
20#include "launchermodel.h"
21#include "launcheritem.h"
23#include "desktopfilehandler.h"
24#include "dbusinterface.h"
26#include <unity/shell/application/ApplicationInfoInterface.h>
30using namespace unity::shell::application;
32LauncherModel::LauncherModel(QObject *parent):
33 LauncherModelInterface(parent),
34 m_settings(new GSettings(this)),
35 m_dbusIface(new DBusInterface(this)),
38 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
39 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
40 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
42 Q_FOREACH (const QString &entry, m_settings->storedApplications()) {
43 DesktopFileHandler desktopFile(entry);
44 if (!desktopFile.isValid()) {
45 qWarning() << "Couldn't find a .desktop file for" << entry << ". Skipping...";
49 LauncherItem *item = new LauncherItem(entry,
50 desktopFile.displayName(),
53 item->setPinned(true);
58LauncherModel::~LauncherModel()
60 while (!m_list.empty()) {
61 m_list.takeFirst()->deleteLater();
65int LauncherModel::rowCount(const QModelIndex &parent) const
68 return m_list.count();
71QVariant LauncherModel::data(const QModelIndex &index, int role) const
73 LauncherItem *item = m_list.at(index.row());
82 return item->pinned();
85 case RoleCountVisible:
86 return item->countVisible();
88 return item->progress();
90 return item->focused();
96unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
98 if (index < 0 || index >= m_list.count()) {
101 return m_list.at(index);
104void LauncherModel::move(int oldIndex, int newIndex)
106 // Make sure its not moved outside the lists
110 if (newIndex >= m_list.count()) {
111 newIndex = m_list.count()-1;
115 if (oldIndex == newIndex) {
119 // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
120 // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
121 // adjust the model's index by +1 in case we're moving upwards
122 int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
124 beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
125 m_list.move(oldIndex, newIndex);
128 if (!m_list.at(newIndex)->pinned()) {
129 pin(m_list.at(newIndex)->appId());
135void LauncherModel::pin(const QString &appId, int index)
137 int currentIndex = findApplication(appId);
139 if (currentIndex >= 0) {
140 if (index == -1 || index == currentIndex) {
141 m_list.at(currentIndex)->setPinned(true);
142 QModelIndex modelIndex = this->index(currentIndex);
143 Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
145 move(currentIndex, index);
146 // move() will store the list to the backend itself, so just exit at this point.
151 index = m_list.count();
154 DesktopFileHandler desktopFile(appId);
155 if (!desktopFile.isValid()) {
156 qWarning() << "Can't pin this application, there is no .destkop file available.";
160 beginInsertRows(QModelIndex(), index, index);
161 LauncherItem *item = new LauncherItem(appId,
162 desktopFile.displayName(),
165 item->setPinned(true);
166 m_list.insert(index, item);
173void LauncherModel::requestRemove(const QString &appId)
175 int index = findApplication(appId);
180 if (m_appManager->findApplication(appId)) {
181 m_list.at(index)->setPinned(false);
182 QModelIndex modelIndex = this->index(index);
183 Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
187 beginRemoveRows(QModelIndex(), index, index);
188 m_list.takeAt(index)->deleteLater();
194void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
196 int index = findApplication(appId);
201 LauncherItem *item = m_list.at(index);
202 QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
204 QString actionId = model->get(actionIndex).actionId();
206 // Check if this is one of the launcher actions we handle ourselves
207 if (actionId == "pin_item") {
208 if (item->pinned()) {
209 requestRemove(appId);
214 // Nope, we don't know this action, let the backend forward it to the application
216 // TODO: forward quicklist action to app, possibly via m_dbusIface
221void LauncherModel::setUser(const QString &username)
224 qWarning() << "This backend doesn't support multiple users";
227QString LauncherModel::getUrlForAppId(const QString &appId) const
229 // appId is either an appId or a legacy app name. Let's find out which
230 if (appId.isEmpty()) {
234 if (!appId.contains("_")) {
235 return "application:///" + appId + ".desktop";
238 QStringList parts = appId.split('_');
239 QString package = parts.value(0);
240 QString app = parts.value(1, "first-listed-app");
241 return "appid://" + package + "/" + app + "/current-user-version";
244ApplicationManagerInterface *LauncherModel::applicationManager() const
249void LauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager)
251 // Is there already another appmanager set?
253 // Disconnect any signals
254 disconnect(this, SLOT(applicationAdded(QModelIndex,int)));
255 disconnect(this, SLOT(applicationRemoved(QModelIndex,int)));
256 disconnect(this, SLOT(focusedAppIdChanged()));
258 // remove any recent/running apps from the launcher
259 QList<int> recentAppIndices;
260 for (int i = 0; i < m_list.count(); ++i) {
261 if (m_list.at(i)->recent()) {
262 recentAppIndices << i;
266 while (recentAppIndices.count() > 0) {
267 beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
268 m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
270 recentAppIndices.takeFirst();
275 m_appManager = appManager;
276 connect(m_appManager, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(applicationAdded(QModelIndex,int)));
277 connect(m_appManager, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(applicationRemoved(QModelIndex,int)));
278 connect(m_appManager, SIGNAL(focusedApplicationIdChanged()), SLOT(focusedAppIdChanged()));
280 Q_EMIT applicationManagerChanged();
282 for (int i = 0; i < appManager->count(); ++i) {
283 applicationAdded(QModelIndex(), i);
288void LauncherModel::storeAppList()
291 Q_FOREACH(LauncherItem *item, m_list) {
292 if (item->pinned()) {
293 appIds << item->appId();
296 m_settings->setStoredApplications(appIds);
299int LauncherModel::findApplication(const QString &appId)
301 for (int i = 0; i < m_list.count(); ++i) {
302 LauncherItem *item = m_list.at(i);
303 if (item->appId() == appId) {
310void LauncherModel::progressChanged(const QString &appId, int progress)
312 int idx = findApplication(appId);
314 LauncherItem *item = m_list.at(idx);
315 item->setProgress(progress);
316 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
321void LauncherModel::countChanged(const QString &appId, int count)
323 int idx = findApplication(appId);
325 LauncherItem *item = m_list.at(idx);
326 item->setCount(count);
327 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
331void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
333 int idx = findApplication(appId);
335 LauncherItem *item = m_list.at(idx);
336 item->setCountVisible(countVisible);
337 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible);
339 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
340 if (!countVisible && !item->pinned() && !item->recent()) {
341 beginRemoveRows(QModelIndex(), idx, idx);
342 m_list.takeAt(idx)->deleteLater();
346 // Need to create a new LauncherItem and show the highlight
347 DesktopFileHandler desktopFile(appId);
348 if (countVisible && desktopFile.isValid()) {
349 LauncherItem *item = new LauncherItem(appId,
350 desktopFile.displayName(),
352 item->setCountVisible(true);
353 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
360void LauncherModel::refresh()
362 QList<LauncherItem*> toBeRemoved;
363 Q_FOREACH (LauncherItem* item, m_list) {
364 DesktopFileHandler desktopFile(item->appId());
365 if (!desktopFile.isValid()) {
368 int idx = m_list.indexOf(item);
369 item->setName(desktopFile.displayName());
370 item->setIcon(desktopFile.icon());
371 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
375 Q_FOREACH (LauncherItem* item, toBeRemoved) {
376 requestRemove(item->appId());
380void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
384 ApplicationInfoInterface *app = m_appManager->get(row);
386 qWarning() << "LauncherModel received an applicationAdded signal, but there's no such application!";
390 if (app->appId() == "unity8-dash") {
391 // Not adding the dash app
396 Q_FOREACH(LauncherItem *item, m_list) {
397 if (app->appId() == item->appId()) {
403 // Shall we paint some running/recent app highlight? If yes, do it here.
405 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
406 item->setRecent(true);
407 item->setFocused(app->focused());
409 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
415void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
420 for (int i = 0; i < m_list.count(); ++i) {
421 if (m_list.at(i)->appId() == m_appManager->get(row)->appId()) {
427 if (appIndex > -1 && !m_list.at(appIndex)->pinned()) {
428 beginRemoveRows(QModelIndex(), appIndex, appIndex);
429 m_list.takeAt(appIndex)->deleteLater();
434void LauncherModel::focusedAppIdChanged()
436 QString appId = m_appManager->focusedApplicationId();
437 for (int i = 0; i < m_list.count(); ++i) {
438 LauncherItem *item = m_list.at(i);
439 if (!item->focused() && item->appId() == appId) {
440 item->setFocused(true);
441 Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
442 } else if (item->focused() && item->appId() != appId) {
443 item->setFocused(false);
444 Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list