2 * Copyright (C) 2013-2014 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU 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 General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17#include "ApplicationManager.h"
18#include "ApplicationInfo.h"
19#include "MirSurfaceItem.h"
20#include "MirSurfaceItemModel.h"
21#include "ApplicationTestInterface.h"
26#include <QGuiApplication>
29#include <QQmlComponent>
32#include <QtDBus/QtDBus>
34ApplicationManager *ApplicationManager::the_application_manager = nullptr;
36ApplicationManager *ApplicationManager::singleton()
38 if (!the_application_manager) {
39 the_application_manager = new ApplicationManager();
41 return the_application_manager;
44ApplicationManager::ApplicationManager(QObject *parent)
45 : ApplicationManagerInterface(parent)
48 m_roleNames.insert(RoleSurface, "surface");
49 m_roleNames.insert(RoleFullscreen, "fullscreen");
50 m_roleNames.insert(RoleApplication, "application");
52 buildListOfAvailableApplications();
54 startApplication("unity8-dash");
55 focusApplication("unity8-dash");
58ApplicationManager::~ApplicationManager()
62int ApplicationManager::rowCount(const QModelIndex& parent) const {
63 return !parent.isValid() ? m_runningApplications.size() : 0;
66QVariant ApplicationManager::data(const QModelIndex& index, int role) const {
67 if (index.row() < 0 || index.row() >= m_runningApplications.size())
70 auto app = m_runningApplications.at(index.row());
77 return app->comment();
85 return app->focused();
87 return app->screenshot();
89 return QVariant::fromValue(app->surface());
91 return app->fullscreen();
93 return QVariant::fromValue(app);
99ApplicationInfo *ApplicationManager::get(int row) const {
100 if (row < 0 || row >= m_runningApplications.size())
102 return m_runningApplications.at(row);
105ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
106 for (ApplicationInfo *app : m_runningApplications) {
107 if (app->appId() == appId) {
114QModelIndex ApplicationManager::findIndex(ApplicationInfo* application)
116 for (int i = 0; i < m_runningApplications.size(); ++i) {
117 if (m_runningApplications.at(i) == application) {
122 return QModelIndex();
125void ApplicationManager::add(ApplicationInfo *application) {
130 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
131 m_runningApplications.append(application);
133 Q_EMIT applicationAdded(application->appId());
134 Q_EMIT countChanged();
135 if (count() == 1) Q_EMIT emptyChanged(isEmpty()); // was empty but not anymore
137 connect(application, &ApplicationInfo::surfaceChanged, this, [application, this]() {
138 QModelIndex appIndex = findIndex(application);
139 if (!appIndex.isValid()) return;
140 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleSurface);
144void ApplicationManager::remove(ApplicationInfo *application) {
145 int i = m_runningApplications.indexOf(application);
147 beginRemoveRows(QModelIndex(), i, i);
148 m_runningApplications.removeAt(i);
150 Q_EMIT applicationRemoved(application->appId());
151 Q_EMIT countChanged();
152 if (isEmpty()) Q_EMIT emptyChanged(isEmpty());
154 disconnect(application, &ApplicationInfo::surfaceChanged, this, 0);
157void ApplicationManager::move(int from, int to) {
158 if (from == to) return;
160 if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) {
162 /* When moving an item down, the destination index needs to be incremented
163 * by one, as explained in the documentation:
164 * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
165 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
166 m_runningApplications.move(from, to);
171int ApplicationManager::sideStageWidth() const
176ApplicationManager::StageHint ApplicationManager::stageHint() const
181ApplicationManager::FormFactorHint ApplicationManager::formFactorHint() const
183 return PhoneFormFactor;
186ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
187 const QStringList &arguments)
189 return startApplication(appId, NoFlag, arguments);
192ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
194 const QStringList &arguments)
197 ApplicationInfo *application = 0;
199 for (ApplicationInfo *availableApp : m_availableApplications) {
200 if (availableApp->appId() == appId) {
201 application = availableApp;
209 if (flags.testFlag(ApplicationManager::ForceMainStage)
210 && application->stage() == ApplicationInfo::SideStage) {
211 application->setStage(ApplicationInfo::MainStage);
214 application->setState(ApplicationInfo::Running);
219bool ApplicationManager::stopApplication(const QString &appId)
221 if (appId == "unity8-dash") {
225 ApplicationInfo *application = findApplication(appId);
226 if (application == nullptr)
229 if (application->appId() == focusedApplicationId()) {
230 unfocusCurrentApplication();
232 application->setState(ApplicationInfo::Stopped);
237bool ApplicationManager::updateScreenshot(const QString &appId)
240 ApplicationInfo *application = nullptr;
241 for (int i = 0; i < m_availableApplications.count(); ++i) {
242 application = m_availableApplications.at(i);
243 if (application->appId() == appId) {
253 QModelIndex appIndex = index(idx);
254 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
258QString ApplicationManager::focusedApplicationId() const {
259 for (ApplicationInfo *app : m_runningApplications) {
260 if (app->focused()) {
267bool ApplicationManager::suspended() const
272void ApplicationManager::setSuspended(bool suspended)
274 ApplicationInfo *focusedApp = findApplication(focusedApplicationId());
277 focusedApp->setState(ApplicationInfo::Suspended);
279 focusedApp->setState(ApplicationInfo::Running);
282 m_suspended = suspended;
283 Q_EMIT suspendedChanged();
286bool ApplicationManager::focusApplication(const QString &appId)
288 ApplicationInfo *application = findApplication(appId);
289 if (application == nullptr)
292 if (application->stage() == ApplicationInfo::MainStage) {
293 // unfocus currently focused mainstage app
294 for (ApplicationInfo *app : m_runningApplications) {
295 if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
296 app->setFocused(false);
297 app->setState(ApplicationInfo::Suspended);
302 application->setFocused(true);
303 application->setState(ApplicationInfo::Running);
304 } else if (application->stage() == ApplicationInfo::SideStage) {
305 // unfocus currently focused sidestage app
306 for (ApplicationInfo *app : m_runningApplications) {
307 if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
308 app->setFocused(false);
309 app->setState(ApplicationInfo::Suspended);
314 application->setFocused(true);
315 application->setState(ApplicationInfo::Running);
318 // move app to top of stack
319 move(m_runningApplications.indexOf(application), 0);
320 Q_EMIT focusedApplicationIdChanged();
324bool ApplicationManager::requestFocusApplication(const QString &appId)
326 QMetaObject::invokeMethod(this, "focusRequested", Qt::QueuedConnection, Q_ARG(QString, appId));
330void ApplicationManager::unfocusCurrentApplication()
332 for (ApplicationInfo *app : m_runningApplications) {
333 if (app->focused()) {
334 app->setFocused(false);
337 Q_EMIT focusedApplicationIdChanged();
340void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
342 application->setScreenshot(QString("file://%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory())
343 .arg(application->icon().toString()));
346void ApplicationManager::buildListOfAvailableApplications()
348 ApplicationInfo *application;
350 application = new ApplicationInfo(this);
351 application->setAppId("unity8-dash");
352 application->setName("Unity 8 Mock Dash");
353 application->setIcon(QUrl("unity8-dash"));
354 application->setStage(ApplicationInfo::MainStage);
355 generateQmlStrings(application);
356 m_availableApplications.append(application);
358 application = new ApplicationInfo(this);
359 application->setAppId("dialer-app");
360 application->setName("Dialer");
361 application->setIcon(QUrl("dialer"));
362 application->setStage(ApplicationInfo::SideStage);
363 generateQmlStrings(application);
364 m_availableApplications.append(application);
366 application = new ApplicationInfo(this);
367 application->setAppId("camera-app");
368 application->setName("Camera");
369 application->setIcon(QUrl("camera"));
370 application->setFullscreen(true);
371 generateQmlStrings(application);
372 m_availableApplications.append(application);
374 application = new ApplicationInfo(this);
375 application->setAppId("gallery-app");
376 application->setName("Gallery");
377 application->setIcon(QUrl("gallery"));
378 generateQmlStrings(application);
379 m_availableApplications.append(application);
381 application = new ApplicationInfo(this);
382 application->setAppId("facebook-webapp");
383 application->setName("Facebook");
384 application->setIcon(QUrl("facebook"));
385 application->setStage(ApplicationInfo::SideStage);
386 generateQmlStrings(application);
387 m_availableApplications.append(application);
389 application = new ApplicationInfo(this);
390 application->setAppId("webbrowser-app");
391 application->setFullscreen(true);
392 application->setName("Browser");
393 application->setIcon(QUrl("browser"));
394 generateQmlStrings(application);
395 m_availableApplications.append(application);
397 application = new ApplicationInfo(this);
398 application->setAppId("twitter-webapp");
399 application->setName("Twitter");
400 application->setIcon(QUrl("twitter"));
401 application->setStage(ApplicationInfo::SideStage);
402 generateQmlStrings(application);
403 m_availableApplications.append(application);
405 application = new ApplicationInfo(this);
406 application->setAppId("gmail-webapp");
407 application->setName("GMail");
408 application->setIcon(QUrl("gmail"));
409 m_availableApplications.append(application);
411 application = new ApplicationInfo(this);
412 application->setAppId("ubuntu-weather-app");
413 application->setName("Weather");
414 application->setIcon(QUrl("weather"));
415 application->setStage(ApplicationInfo::SideStage);
416 generateQmlStrings(application);
417 m_availableApplications.append(application);
419 application = new ApplicationInfo(this);
420 application->setAppId("notes-app");
421 application->setName("Notepad");
422 application->setIcon(QUrl("notepad"));
423 application->setStage(ApplicationInfo::SideStage);
424 m_availableApplications.append(application);
426 application = new ApplicationInfo(this);
427 application->setAppId("calendar-app");
428 application->setName("Calendar");
429 application->setIcon(QUrl("calendar"));
430 application->setStage(ApplicationInfo::SideStage);
431 m_availableApplications.append(application);
433 application = new ApplicationInfo(this);
434 application->setAppId("mediaplayer-app");
435 application->setName("Media Player");
436 application->setIcon(QUrl("mediaplayer-app"));
437 application->setFullscreen(true);
438 m_availableApplications.append(application);
440 application = new ApplicationInfo(this);
441 application->setAppId("evernote");
442 application->setName("Evernote");
443 application->setIcon(QUrl("evernote"));
444 m_availableApplications.append(application);
446 application = new ApplicationInfo(this);
447 application->setAppId("map");
448 application->setName("Map");
449 application->setIcon(QUrl("map"));
450 generateQmlStrings(application);
451 m_availableApplications.append(application);
453 application = new ApplicationInfo(this);
454 application->setAppId("pinterest");
455 application->setName("Pinterest");
456 application->setIcon(QUrl("pinterest"));
457 m_availableApplications.append(application);
459 application = new ApplicationInfo(this);
460 application->setAppId("soundcloud");
461 application->setName("SoundCloud");
462 application->setIcon(QUrl("soundcloud"));
463 m_availableApplications.append(application);
465 application = new ApplicationInfo(this);
466 application->setAppId("wikipedia");
467 application->setName("Wikipedia");
468 application->setIcon(QUrl("wikipedia"));
469 m_availableApplications.append(application);
471 application = new ApplicationInfo(this);
472 application->setAppId("youtube");
473 application->setName("YouTube");
474 application->setIcon(QUrl("youtube"));
475 m_availableApplications.append(application);
478QStringList ApplicationManager::availableApplications()
481 Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
482 appIds << app->appId();
487bool ApplicationManager::isEmpty() const
489 return m_runningApplications.isEmpty();
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