/* * Copyright (C) 2013 Canonical, Ltd. * * 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; version 3. * * 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 . */ #include "ApplicationManager.h" #include "ApplicationInfo.h" #include #include #include #include #include #include ApplicationManager::ApplicationManager(QObject *parent) : ApplicationManagerInterface(parent) , m_mainStageComponent(0) , m_mainStage(0) , m_sideStageComponent(0) , m_sideStage(0) { buildListOfAvailableApplications(); } ApplicationManager::~ApplicationManager() { } int ApplicationManager::rowCount(const QModelIndex& parent) const { return !parent.isValid() ? m_runningApplications.size() : 0; } QVariant ApplicationManager::data(const QModelIndex& index, int role) const { if (index.row() < 0 || index.row() >= m_runningApplications.size()) return QVariant(); auto app = m_runningApplications.at(index.row()); switch(role) { case RoleAppId: return app->appId(); case RoleName: return app->name(); case RoleComment: return app->comment(); case RoleIcon: return app->icon(); case RoleStage: return app->stage(); case RoleState: return app->state(); case RoleFocused: return app->focused(); default: return QVariant(); } } ApplicationInfo *ApplicationManager::get(int row) const { if (row < 0 || row >= m_runningApplications.size()) return nullptr; return m_runningApplications.at(row); } ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const { for (ApplicationInfo *app : m_runningApplications) { if (app->appId() == appId) { return app; } } return nullptr; } void ApplicationManager::add(ApplicationInfo *application) { if (!application) return; beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size()); m_runningApplications.append(application); endInsertRows(); Q_EMIT countChanged(); } void ApplicationManager::remove(ApplicationInfo *application) { int i = m_runningApplications.indexOf(application); if (i != -1) { beginRemoveRows(QModelIndex(), i, i); m_runningApplications.removeAt(i); endRemoveRows(); Q_EMIT countChanged(); } } void ApplicationManager::move(int from, int to) { if (from == to) return; if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) { QModelIndex parent; /* When moving an item down, the destination index needs to be incremented * by one, as explained in the documentation: * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */ beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0)); m_runningApplications.move(from, to); endMoveRows(); } } int ApplicationManager::keyboardHeight() const { return 0; } bool ApplicationManager::keyboardVisible() const { return false; } int ApplicationManager::sideStageWidth() const { return 0; } ApplicationManager::StageHint ApplicationManager::stageHint() const { return MainStage; } ApplicationManager::FormFactorHint ApplicationManager::formFactorHint() const { return PhoneFormFactor; } ApplicationInfo* ApplicationManager::startApplication(const QString &appId, const QStringList &arguments) { return startApplication(appId, NoFlag, arguments); } ApplicationInfo* ApplicationManager::startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments) { Q_UNUSED(arguments) ApplicationInfo *application = 0; for (ApplicationInfo *availableApp : m_availableApplications) { if (availableApp->appId() == appId) { application = availableApp; break; } } if (!application) return 0; if (flags.testFlag(ApplicationManager::ForceMainStage) && application->stage() == ApplicationInfo::SideStage) { application->setStage(ApplicationInfo::MainStage); } add(application); return application; } bool ApplicationManager::stopApplication(const QString &appId) { ApplicationInfo *application = findApplication(appId); if (application == nullptr) return false; remove(application); Q_EMIT focusedApplicationIdChanged(); return true; } QString ApplicationManager::focusedApplicationId() const { for (ApplicationInfo *app : m_runningApplications) { if (app->focused()) { return app->appId(); } } return QString(); } bool ApplicationManager::focusApplication(const QString &appId) { ApplicationInfo *application = findApplication(appId); if (application == nullptr) return false; if (application->stage() == ApplicationInfo::MainStage) { // unfocus currently focused mainstage app for (ApplicationInfo *app : m_runningApplications) { if (app->focused() && app->stage() == ApplicationInfo::MainStage) { app->setFocused(false); app->hideWindow(); } } // focus this app application->setFocused(true); if (!m_mainStage) createMainStage(); application->showWindow(m_mainStage); m_mainStage->setZ(-1000); if (m_sideStage) m_sideStage->setZ(-2000); } else if (application->stage() == ApplicationInfo::SideStage) { // unfocus currently focused sidestage app for (ApplicationInfo *app : m_runningApplications) { if (app->focused() && app->stage() == ApplicationInfo::SideStage) { app->setFocused(false); app->hideWindow(); } } // focus this app application->setFocused(true); if (!m_sideStage) createSideStage(); application->showWindow(m_sideStage); m_sideStage->setZ(-1000); if (m_mainStage) m_mainStage->setZ(-2000); } // move app to top of stack move(m_runningApplications.indexOf(application), 0); Q_EMIT focusedApplicationIdChanged(); return true; } void ApplicationManager::unfocusCurrentApplication() { for (ApplicationInfo *app : m_runningApplications) { if (app->focused()) { app->hideWindow(); app->setFocused(false); } } Q_EMIT focusedApplicationIdChanged(); } void ApplicationManager::generateQmlStrings(ApplicationInfo *application) { // TODO: Is there a better way of solving this fullscreen vs. regular // application height? QString topMargin; if (application->fullscreen()) { topMargin.append("0"); } else { // Taken from Panel.panelHeight topMargin.append("units.gu(3) + units.dp(2)"); } QString windowQml = QString( "import QtQuick 2.0\n" "Image {\n" " anchors.fill: parent\n" " anchors.topMargin: %1\n" " source: \"file://%2/Dash/graphics/phone/screenshots/%3.png\"\n" " smooth: true\n" " fillMode: Image.PreserveAspectCrop\n" "}").arg(topMargin) .arg(qmlDirectory()) .arg(application->icon().toString()); application->setWindowQml(windowQml); QString imageQml = QString( "import QtQuick 2.0\n" "Image {\n" " anchors.fill: parent\n" " source: \"file://%1/Dash/graphics/phone/screenshots/%2.png\"\n" " smooth: true\n" " fillMode: Image.PreserveAspectCrop\n" "}").arg(qmlDirectory()) .arg(application->icon().toString()); application->setImageQml(imageQml); } void ApplicationManager::buildListOfAvailableApplications() { ApplicationInfo *application; application = new ApplicationInfo(this); application->setAppId("phone-app"); application->setName("Phone"); application->setIcon(QUrl("phone")); application->setStage(ApplicationInfo::SideStage); generateQmlStrings(application); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("camera-app"); application->setName("Camera"); application->setIcon(QUrl("camera")); application->setFullscreen(true); generateQmlStrings(application); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("gallery-app"); application->setName("Gallery"); application->setIcon(QUrl("gallery")); generateQmlStrings(application); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("facebook-webapp"); application->setName("Facebook"); application->setIcon(QUrl("facebook")); application->setStage(ApplicationInfo::SideStage); generateQmlStrings(application); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("webbrowser-app"); application->setName("Browser"); application->setIcon(QUrl("browser")); generateQmlStrings(application); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("twitter-webapp"); application->setName("Twitter"); application->setIcon(QUrl("twitter")); application->setStage(ApplicationInfo::SideStage); generateQmlStrings(application); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("gmail-webapp"); application->setName("GMail"); application->setIcon(QUrl("gmail")); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("ubuntu-weather-app"); application->setName("Weather"); application->setIcon(QUrl("weather")); application->setStage(ApplicationInfo::SideStage); generateQmlStrings(application); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("notes-app"); application->setName("Notepad"); application->setIcon(QUrl("notepad")); application->setStage(ApplicationInfo::SideStage); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("calendar-app"); application->setName("Calendar"); application->setIcon(QUrl("calendar")); application->setStage(ApplicationInfo::SideStage); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("mediaplayer-app"); application->setName("Media Player"); application->setIcon(QUrl("mediaplayer-app")); application->setFullscreen(true); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("evernote"); application->setName("Evernote"); application->setIcon(QUrl("evernote")); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("map"); application->setName("Map"); application->setIcon(QUrl("map")); generateQmlStrings(application); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("pinterest"); application->setName("Pinterest"); application->setIcon(QUrl("pinterest")); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("soundcloud"); application->setName("SoundCloud"); application->setIcon(QUrl("soundcloud")); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("wikipedia"); application->setName("Wikipedia"); application->setIcon(QUrl("wikipedia")); m_availableApplications.append(application); application = new ApplicationInfo(this); application->setAppId("youtube"); application->setName("YouTube"); application->setIcon(QUrl("youtube")); m_availableApplications.append(application); } void ApplicationManager::createMainStageComponent() { // The assumptions I make here really should hold. QQuickView *quickView = qobject_cast(QGuiApplication::topLevelWindows()[0]); QQmlEngine *engine = quickView->engine(); m_mainStageComponent = new QQmlComponent(engine, this); QString mainStageQml = "import QtQuick 2.0\n" "Rectangle {\n" " anchors.fill: parent\n" " color: 'black'\n" " z: -2000\n" "}\n"; m_mainStageComponent->setData(mainStageQml.toLatin1(), QUrl()); } void ApplicationManager::createMainStage() { if (!m_mainStageComponent) createMainStageComponent(); // The assumptions I make here really should hold. QQuickView *quickView = qobject_cast(QGuiApplication::topLevelWindows()[0]); QQuickItem *shell = quickView->rootObject(); m_mainStage = qobject_cast(m_mainStageComponent->create()); m_mainStage->setParentItem(shell); } void ApplicationManager::createSideStageComponent() { // The assumptions I make here really should hold. QQuickView *quickView = qobject_cast(QGuiApplication::topLevelWindows()[0]); QQmlEngine *engine = quickView->engine(); m_sideStageComponent = new QQmlComponent(engine, this); QString sideStageQml = "import QtQuick 2.0\n" "import Ubuntu.Components 0.1\n" "Item {\n" " width: units.gu(40)\n" // from SideStage in Shell.qml " anchors.top: parent.top\n" " anchors.bottom: parent.bottom\n" " anchors.right: parent.right\n" " z: -1000\n" "}\n"; m_sideStageComponent->setData(sideStageQml.toLatin1(), QUrl()); } void ApplicationManager::createSideStage() { if (!m_sideStageComponent) createSideStageComponent(); // The assumptions I make here really should hold. QQuickView *quickView = qobject_cast(QGuiApplication::topLevelWindows()[0]); QQuickItem *shell = quickView->rootObject(); m_sideStage = qobject_cast(m_sideStageComponent->create()); m_sideStage->setParentItem(shell); m_sideStage->setFlag(QQuickItem::ItemHasContents, false); }