on xenial help sign in
.bazaar/Makefiledeleted−6
since splitsnapshots
unifiedsplit
@@ -1,6 +0,0 @@
1−install:
2− mkdir -p ~/.bazaar/plugins
3− cp plugins/* ~/.bazaar/plugins
4−
5−uninstall:
6− rm ~/.bazaar/plugins/makecheck_unity_phablet.py*
.bazaar/plugins/makecheck_unity_phablet.pydeleted−51
@@ -1,51 +0,0 @@
1−#!/usr/bin/python
2−# Copyright 2013, 2014 Canonical Ltd.
3−#
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.
7−#
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.
12−#
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/>.
15−
16−import os
17−import subprocess
18−from bzrlib import branch
19−from bzrlib.urlutils import local_path_from_url
20−
21−
22−def execute_makecheck(
23− local_branch, master_branch, old_revision_number, old_revision_id,
24− future_revision_number, future_revision_id, tree_delta, future_tree):
25− if not master_branch.basis_tree().has_filename("qml/Shell.qml"):
26− return
27−
28− os.chdir(local_path_from_url(master_branch.base))
29−
30− print "Executing 'make -C builddir test'.."
31− os.environ['CTEST_OUTPUT_ON_FAILURE'] = "1"
32− if (subprocess.call("make -C builddir test", shell=True) != 0):
33−
34− print("\n\n*** Warning ***\n\n"
35− "Basic tests failed. This commit will not pass continuous "
36− "integration.")
37−
38− branch = local_branch or master_branch
39− revision = branch.repository.get_revision(future_revision_id)
40− msg_file = open('message.txt', 'w')
41− msg_file.write(revision.message)
42− msg_file.close()
43−
44− print("\n\nSaved commit message to $SRC_DIR/message.txt.")
45− print("You can uncommit this revision, fix the tests and reuse your "
46− "message running:\n\nbzr commit -F message.txt\n\n")
47− elif os.path.isfile("message.txt"):
48− os.remove("message.txt")
49−
50−branch.Branch.hooks.install_named_hook(
51− 'pre_commit', execute_makecheck, 'make check pre-commit')
.bzr-builddeb/default.confdeleted−2
@@ -1,2 +0,0 @@
1−[BUILDDEB]
2−split = True
.bzrignoredeleted−7
@@ -1,7 +0,0 @@
1−# default build dir
2−/builddir
3−# QtCreator project files
4−/*.user
5−# Eclipse project files
6−.settings
7−message.txt
.gitignore+7
@@ -2,8 +2,15 @@
2/builddir
2/builddir
3# QtCreator project files
3# QtCreator project files
4/*.user
4/*.user
5+*.qmlc
6+*.jsc
5# Eclipse project files
7# Eclipse project files
6/.project
8/.project
7/.cproject
9/.cproject
8/.settings
10/.settings
9/.pydevproject
11/.pydevproject
12+# KDevelop project Files
13+/.kdev4/
14+/build/
15+/*.kdev4
16+.clickable/
CMakeLists.txt+31−13
@@ -40,6 +40,19 @@ if(cmake_build_type_lower MATCHES coverage)
40 ENABLE_COVERAGE_REPORT(TARGETS ${SHELL_APP} FILTER /usr/include ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/*)
40 ENABLE_COVERAGE_REPORT(TARGETS ${SHELL_APP} FILTER /usr/include ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/*)
41endif()
41endif()
42
42
43+find_package (ECM 1.7.0 QUIET NO_MODULE)
44+if (ECM_FOUND)
45+ # Provides us with -DECM_ENABLE_SANITIZERS='X'
46+ # Where X can be address, thread, memory, leak, undefined
47+ include("${ECM_MODULE_DIR}/ECMEnableSanitizers.cmake")
48+endif()
49+
50+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
51+ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0")
52+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override" )
53+ endif()
54+endif()
55+
43# Make sure we have all the needed symbols
56# Make sure we have all the needed symbols
44set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs")
57set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs")
45set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,defs")
58set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,defs")
@@ -49,22 +62,31 @@ add_custom_target(cppcheck COMMAND cppcheck --enable=all -q --error-exitcode=2
49 ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
62 ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
50
63
51include(FindPkgConfig)
64include(FindPkgConfig)
52−find_package(Qt5Core 5.4 REQUIRED)
65+find_package(Qt5Core 5.6 REQUIRED)
53−find_package(Qt5Qml 5.4 REQUIRED)
66+find_package(Qt5Qml 5.6 REQUIRED)
54−find_package(Qt5Quick 5.4 REQUIRED)
67+find_package(Qt5Quick 5.6 REQUIRED)
55−find_package(Qt5Gui 5.4 REQUIRED)
68+find_package(Qt5Gui 5.6 REQUIRED)
56−find_package(Qt5DBus 5.4 REQUIRED)
69+find_package(Qt5DBus 5.6 REQUIRED)
57−find_package(Qt5Concurrent 5.4 REQUIRED)
70+find_package(Qt5Concurrent 5.6 REQUIRED)
58−find_package(Qt5Sql 5.4 REQUIRED)
71+find_package(Qt5Sql 5.6 REQUIRED)
59−
72+find_package(Qt5Test 5.6 REQUIRED)
73+find_package(Qt5Svg 5.6 REQUIRED)
74+find_package(Qt5Network 5.6 REQUIRED)
75+find_package(Qt5Xml 5.6 REQUIRED)
76+
60−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=22)
77+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=27)
61pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
78pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
62pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
79pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
63pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
80pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
81+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=13)
64pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
82pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
65pkg_check_modules(GD3 REQUIRED gnome-desktop-3.0)
83pkg_check_modules(GD3 REQUIRED gnome-desktop-3.0)
66pkg_check_modules(UAL REQUIRED ubuntu-app-launch-2)
84pkg_check_modules(UAL REQUIRED ubuntu-app-launch-2)
67pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
85pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
86+pkg_check_modules(QTDBUSTEST REQUIRED
87+ libqtdbustest-1
88+ libqtdbusmock-1
89+ )
68
90
69### Check UbuntuGestures private headers. No pkg-config (.pc) file is provided for them
91### Check UbuntuGestures private headers. No pkg-config (.pc) file is provided for them
70find_path(UBUNTUGESTUREPRIV
92find_path(UBUNTUGESTUREPRIV
@@ -126,8 +148,6 @@ include(autopilot)
126declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
148declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
127
149
128set(SHELL_APP unity8)
150set(SHELL_APP unity8)
129−set(DASH_APP unity8-dash)
130−set(SCOPE_TOOL unity-scope-tool)
131set(INDICATORS_CLIENT_APP indicators-client)
151set(INDICATORS_CLIENT_APP indicators-client)
132set(GREETER_APP unity8-greeter)
152set(GREETER_APP unity8-greeter)
133set(MOCK_INDICATOR_SERVICE_APP unity-mock-indicator-service)
153set(MOCK_INDICATOR_SERVICE_APP unity-mock-indicator-service)
@@ -138,8 +158,6 @@ include_directories(
138 ${APPLICATION_API_INCLUDE_DIRS}
158 ${APPLICATION_API_INCLUDE_DIRS}
139)
159)
140
160
141−set(ARTIFACTS_DIR "" CACHE PATH "Store test artifacts in this directory.")
142−
143# Quiet logger
161# Quiet logger
144set(QUIET_LOGGER OFF CACHE BOOL "Disable test output on stdout.")
162set(QUIET_LOGGER OFF CACHE BOOL "Disable test output on stdout.")
145if(NOT QUIET_LOGGER)
163if(NOT QUIET_LOGGER)
CODING+24
@@ -162,3 +162,27 @@ $ PYTHONPATH=../tests/autopilot autopilot3 run unity8.some.very.long.test.name
162or run them all with
162or run them all with
163
163
164$ PYTHONPATH=../tests/autopilot autopilot3 run unity8
164$ PYTHONPATH=../tests/autopilot autopilot3 run unity8
165+
166+Debugging techniques
167+====================
168+
169+Unity8 heavily uses Qt and with that Qt debugging techniques mostly apply in the
170+same fashion. There are, however, some additional unity8-specific notes:
171+
172+* Enable/disable Qt scenegraph visualizer on the fly:
173+
174+ $ qdbus com.canonical.Unity8.Debugging /com/canonical/Unity8/Debugging SetSceneGraphVisualizer <mode>
175+
176+ This is equivalent to exporting QSG_VISUALIZE=<mode> before running unity8, except it does it on the
177+ fly.
178+
179+* Toggle slow animations on the fly:
180+
181+ $ qdbus com.canonical.Unity8.Debugging /com/canonical/Unity8/Debugging SetSlowAnimations <true|false>
182+
183+* Change logging filter rules on the fly:
184+
185+ $ qdbus com.canonical.Unity8.Debugging /com/canonical/Unity8/Debugging SetLoggingFilterRules <filter>
186+
187+ This is equivalent to exporting QT_LOGGING_RULES or defining them in QT_LOGGING_CONF, except it does
188+ it on the fly.
Jenkinsfilenew file+2
@@ -0,0 +1,2 @@
1+@Library('ubports-build-tools') _
2+buildAndProvideDebianPackage()
README.mdnew file+31
@@ -0,0 +1,31 @@
1+# Lomiri
2+
3+Lomiri is the operating environment for everywhere. It is able to span the gaps between touch, mouse, and keyboard; between phones, tablets, and workstations; and look good doing it.
4+
5+If you want to contribute to Lomiri on Ubuntu Touch, you've come to the right place. If you want to package Lomiri for other operating systems, you'll be better served by the main branch on the repository that we're migrating to: https://gitlab.com/ubports/core/lomiri
6+
7+Lomiri has been renamed from Unity8. Most of the software on-device in Ubuntu Touch based on Ubuntu 16.04 is still called `unity8`.
8+
9+## Where to report issues
10+
11+Issues related to the version of Lomiri that currently runs on Ubuntu Touch devices may still be reported on this repository. We will move these reports to the GitLab repository as part of our final migration.
12+
13+## Developing Lomiri on Ubuntu Touch
14+
15+This repository contains the code for Lomiri currently running on Ubuntu Touch. If you would like to modify this version of Lomiri, follow the steps below. If you would like to modify Lomiri in another operating system, check out our new repository: https://gitlab.com/ubports/core/lomiri
16+
17+Lomiri can be built and its test suite run using [`clickable`](https://clickable-ut.dev). This is a convenient method to try out most graphical changes to Lomiri without an annoying redeployment process. If you wish to test your changes on your device or make changes that are more difficult to test without real hardware, check out [Making changes and testing locally on the UBports documentation](https://docs.ubports.com/en/latest/systemdev/testing-locally.html). If not, read on.
18+
19+Before you start, [install Clickable](https://clickable-ut.dev/en/latest/install.html).
20+
21+Now, clone this repository to your computer: `git clone https://github.com/ubports/unity8.git`
22+
23+Move into this directory: `cd unity8`
24+
25+Now you can use the full suite of tools provided by this repository's [clickable.yaml](clickable.yaml). For example:
26+
27+* `clickable ide qtcreator` will open QtCreator with this repository open as a project. You can edit and build the project this way, but running the tests will be a bit difficult.
28+* `clickable build --libs --verbose` will build Lomiri in the same environment as it would receive in Ubuntu Touch.
29+* `clickable test --libs` will run the entire Lomiri test suite. This test suite includes graphical tests (which are run on a virtual, invisible desktop) and non-graphical unit tests. These tests make sure Lomiri functions as prescribed and prevents new bugs from being added. You should run them before you create a PR on this repository. This command takes about 7 minutes on an Intel i7-8550U, so plan your time accordingly and use the next two options to reduce the number of times you need to run the whole test suite...
30+* `clickable ide 'cd build/x86_64-linux-gnu/unity8/ && make tryShell'` and similar commands that replace the `Shell` with another test target defined in [tests/qmltests/CMakeLists.txt](tests/qmltests/CMakeLists.txt) allow trying out some Lomiri components in a mock environment. You can use this to try out your changes to Lomiri as you develop.
31+* `clickable ide 'cd build/x86_64-linux-gnu/unity8/ && make xvfbtestShell'` and similar commands that replace the `Shell` with another test target defined in [tests/qmltests/CMakeLists.txt](tests/qmltests/CMakeLists.txt) allow running a single TestCase. You can remove the `xvfb` part of the make target to run the test case graphically, which might help you see what is causing the test to fail.
build.sh+16
@@ -1,5 +1,21 @@
1#!/bin/sh
1#!/bin/sh
2
2
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
3SETUP=false
19SETUP=false
4CLEAN=false
20CLEAN=false
5NUM_JOBS=$(( `grep -c ^processor /proc/cpuinfo` + 1 ))
21NUM_JOBS=$(( `grep -c ^processor /proc/cpuinfo` + 1 ))
clickable.yamlnew file+153
@@ -0,0 +1,153 @@
1+clickable_minimum_required: '7'
2+builder: custom
3+build: echo Run \'clickable build --libs --verbose\' to build && false
4+restrict_arch: amd64
5+libraries:
6+ unity8:
7+ builder: cmake
8+ src_dir: "${ROOT}"
9+ test: make xvfballtests -j${NUM_PROCS}
10+ dependencies_host:
11+ - android-headers
12+ - cmake
13+ - cmake-extras
14+ - dbus-test-runner
15+ - debhelper
16+ - dh-apparmor
17+ - doxyqml
18+ - g++:native
19+ - gdb
20+ - graphviz
21+ - gsettings-ubuntu-schemas
22+ - libandroid-properties-dev
23+ - libconnectivity-qt1-dev
24+ - libevdev-dev
25+ - libgeonames-dev
26+ - libgl1-mesa-dev
27+ - libgl1-mesa-dri
28+ - libgles2-mesa-dev
29+ - libglib2.0-dev
30+ - libgnome-desktop-3-12
31+ - libgnome-desktop-3-dev
32+ - libgsettings-qt-dev
33+ - libhardware-dev
34+ - liblightdm-qt5-3-dev
35+ - libpam0g-dev
36+ - libpulse-dev
37+ - libqmenumodel-dev
38+ - libqt5sql5-sqlite
39+ - libqt5svg5-dev
40+ - libqt5xmlpatterns5-dev
41+ - libqtdbusmock1-dev
42+ - libqtdbustest1-dev
43+ - libsystemsettings-dev
44+ - libubuntu-app-launch2-dev
45+ - libubuntu-download-manager-common-dev
46+ - libubuntugestures5-dev
47+ - libubuntugestures5-private-dev
48+ - libudev-dev
49+ - libudm-common-dev
50+ - libunity-api-dev
51+ - libusermetricsoutput1-dev
52+ - libx11-dev
53+ - libxcb1-dev
54+ - libxi-dev
55+ - pkg-config
56+ - python3-all:any
57+ - python3-setuptools:native
58+ - qml-module-qt-labs-folderlistmodel
59+ - qml-module-qt-labs-settings
60+ - qml-module-qtmultimedia
61+ - qml-module-qtqml-statemachine
62+ - qml-module-qtquick-layouts
63+ - qml-module-qtquick-xmllistmodel
64+ - qml-module-qtquick2
65+ - qml-module-qtsysteminfo
66+ - qml-module-qttest
67+ - qml-module-ubuntu-components
68+ - qml-module-ubuntu-layouts
69+ - qml-module-ubuntu-settings-components
70+ - qml-module-ubuntu-test
71+ - qtbase5-dev
72+ - qtbase5-dev-tools
73+ - qtbase5-private-dev
74+ - qtdbustest-runner
75+ - qtdeclarative5-dev
76+ - qtdeclarative5-dev-tools
77+ - qtdeclarative5-private-dev
78+ - qtdeclarative5-ubuntu-content1
79+ - ttf-ubuntu-font-family
80+ - ubports-wallpapers
81+ - xvfb
82+dependencies_target:
83+ - android-headers
84+ - cmake
85+ - cmake-extras
86+ - dbus-test-runner
87+ - debhelper
88+ - dh-apparmor
89+ - doxyqml
90+ - g++:native
91+ - gdb
92+ - graphviz
93+ - gsettings-ubuntu-schemas
94+ - libandroid-properties-dev
95+ - libconnectivity-qt1-dev
96+ - libevdev-dev
97+ - libgeonames-dev
98+ - libgl1-mesa-dev
99+ - libgl1-mesa-dri
100+ - libgles2-mesa-dev
101+ - libglib2.0-dev
102+ - libgnome-desktop-3-12
103+ - libgnome-desktop-3-dev
104+ - libgsettings-qt-dev
105+ - libhardware-dev
106+ - liblightdm-qt5-3-dev
107+ - libpam0g-dev
108+ - libpulse-dev
109+ - libqmenumodel-dev
110+ - libqt5sql5-sqlite
111+ - libqt5svg5-dev
112+ - libqt5xmlpatterns5-dev
113+ - libqtdbusmock1-dev
114+ - libqtdbustest1-dev
115+ - libsystemsettings-dev
116+ - libubuntu-app-launch2-dev
117+ - libubuntu-download-manager-common-dev
118+ - libubuntugestures5-dev
119+ - libubuntugestures5-private-dev
120+ - libudev-dev
121+ - libudm-common-dev
122+ - libunity-api-dev
123+ - libusermetricsoutput1-dev
124+ - libx11-dev
125+ - libxcb1-dev
126+ - libxi-dev
127+ - pkg-config
128+ - python3-all:any
129+ - python3-setuptools:native
130+ - qml-module-qt-labs-folderlistmodel
131+ - qml-module-qt-labs-settings
132+ - qml-module-qtmultimedia
133+ - qml-module-qtqml-statemachine
134+ - qml-module-qtquick-layouts
135+ - qml-module-qtquick-xmllistmodel
136+ - qml-module-qtquick2
137+ - qml-module-qtsysteminfo
138+ - qml-module-qttest
139+ - qml-module-ubuntu-components
140+ - qml-module-ubuntu-layouts
141+ - qml-module-ubuntu-settings-components
142+ - qml-module-ubuntu-test
143+ - qtbase5-dev
144+ - qtbase5-dev-tools
145+ - qtbase5-private-dev
146+ - qtdbustest-runner
147+ - qtdeclarative5-dev
148+ - qtdeclarative5-dev-tools
149+ - qtdeclarative5-private-dev
150+ - qtdeclarative5-ubuntu-content1
151+ - ttf-ubuntu-font-family
152+ - ubports-wallpapers
153+ - xvfb
cmake/modules/QmlPlugins.cmakedeleted−148
@@ -1,148 +0,0 @@
1−# If you need to override the qmlplugindump binary, create the qmlplugin executable
2−# target before loading this plugin.
3−
4−if(NOT TARGET qmlplugindump)
5− find_program(qmlplugindump_exe qmlplugindump)
6−
7− if(NOT qmlplugindump_exe)
8− msg(FATAL_ERROR "Could not locate qmlplugindump.")
9− endif()
10−
11− add_executable(qmlplugindump IMPORTED)
12− set_target_properties(qmlplugindump PROPERTIES IMPORTED_LOCATION ${qmlplugindump_exe})
13−endif()
14−
15−#
16−# A custom target for building the qmltypes files manually.
17−#
18−if (NOT TARGET qmltypes)
19− add_custom_target(qmltypes)
20−endif()
21−
22−# Creates a target for copying resource files into build dir and optionally installing them.
23−#
24−# Files will be copied into ${BINARY_DIR}/${path} or ${CMAKE_CURRENT_BINARY_DIR} and installed
25−# into ${DESTINATION}/${path}.
26−#
27−# Resource file names are matched against {*.{qml,js,jpg,png,sci,svg},qmldir}.
28−#
29−# export_qmlfiles(plugin path
30−# [SEARCH_PATH path] # Path to search for resources in (defaults to ${CMAKE_CURRENT_SOURCE_DIR})
31−# [BINARY_DIR path]
32−# [DESTINATION path]
33−# [TARGET_PREFIX string] # Will be prefixed to the target name
34−# )
35−#
36−# Created target:
37−# - ${TARGET_PREFIX}${plugin}-qmlfiles - Copies resources into the binary dir.
38−
39−macro(export_qmlfiles PLUGIN PATH)
40− set(single SEARCH_PATH BINARY_DIR DESTINATION TARGET_PREFIX)
41− cmake_parse_arguments(QMLFILES "" "${single}" "" ${ARGN})
42−
43− if(NOT QMLFILES_SEARCH_PATH)
44− set(QMLFILES_SEARCH_PATH ${CMAKE_CURRENT_SOURCE_DIR})
45− endif()
46−
47− if(QMLFILES_BINARY_DIR)
48− set(qmlfiles_dir ${QMLFILES_BINARY_DIR}/${PATH})
49− else()
50− set(qmlfiles_dir ${CMAKE_CURRENT_BINARY_DIR})
51− endif()
52−
53− file(GLOB QMLFILES
54− ${QMLFILES_SEARCH_PATH}/*.qml
55− ${QMLFILES_SEARCH_PATH}/*.js
56− ${QMLFILES_SEARCH_PATH}/*.jpg
57− ${QMLFILES_SEARCH_PATH}/*.png
58− ${QMLFILES_SEARCH_PATH}/*.sci
59− ${QMLFILES_SEARCH_PATH}/*.svg
60− ${QMLFILES_SEARCH_PATH}/*.qmltypes
61− ${QMLFILES_SEARCH_PATH}/qmldir
62− )
63−
64− execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${qmlfiles_dir})
65−
66− # copy the files
67− add_custom_target(${QMLFILES_TARGET_PREFIX}${PLUGIN}-qmlfiles ALL
68− COMMAND cp ${QMLFILES} ${qmlfiles_dir}
69− DEPENDS ${QMLFILES}
70− SOURCES ${QMLFILES}
71− )
72−
73− if(QMLFILES_DESTINATION)
74− # install the qmlfiles file.
75− install(FILES ${QMLFILES}
76− DESTINATION ${QMLFILES_DESTINATION}/${PATH}
77− )
78− endif()
79−endmacro()
80−
81−
82−# Creates a target for generating the typeinfo file for a QML plugin and/or installs the plugin
83−# targets.
84−#
85−# Files will be copied into ${BINARY_DIR}/${path} or ${CMAKE_CURRENT_BINARY_DIR} and installed
86−# into ${DESTINATION}/${path}. If you don't pass BINARY_DIR, it's assumed that current source
87−# path ends with ${path}.
88−#
89−# The generated file will be named after the last segment of the plugin name, e.g. Foo.qmltypes.
90−#
91−# export_qmlplugin(plugin version path
92−# [BINARY_DIR path]
93−# [DESTINATION path]
94−# [TARGET_PREFIX string] # Will be prefixed to the target name
95−# [ENVIRONMENT string] # Will be added to qmlplugindump's env
96−# [TARGETS target1 [target2 ...]] # Targets to depend on and install (e.g. the plugin shared object)
97−# [NO_TYPES] # Do not create the qmltypes target
98−# )
99−#
100−# Created target:
101−# - ${TARGET_PREFIX}${plugin}-qmltypes - Generates the qmltypes file in the source dir.
102−# It will be made a dependency of the "qmltypes" target.
103−
104−macro(export_qmlplugin PLUGIN VERSION PATH)
105− set(options NO_TYPES)
106− set(single BINARY_DIR DESTINATION TARGET_PREFIX ENVIRONMENT)
107− set(multi TARGETS)
108− cmake_parse_arguments(QMLPLUGIN "${options}" "${single}" "${multi}" ${ARGN})
109−
110− if(QMLPLUGIN_BINARY_DIR)
111− set(qmlplugin_dir ${QMLPLUGIN_BINARY_DIR}/${PATH})
112− else()
113− # Find import path to point qmlplugindump at
114− string(REGEX REPLACE "/${PATH}$" "" QMLPLUGIN_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
115− set(qmlplugin_dir ${CMAKE_CURRENT_BINARY_DIR})
116− endif()
117−
118− if(NOT QMLPLUGIN_NO_TYPES)
119− # Relative path for the module
120− string(REPLACE "${CMAKE_BINARY_DIR}/" "" QMLPLUGIN_MODULE_DIR "${QMLPLUGIN_BINARY_DIR}")
121−
122− # Find the last segment of the plugin name to use as qmltypes basename
123− string(REGEX MATCH "[^.]+$" plugin_suffix ${PLUGIN})
124− set(target_prefix ${QMLPLUGIN_TARGET_PREFIX}${PLUGIN})
125− set(qmltypes_path ${CMAKE_CURRENT_SOURCE_DIR}/${plugin_suffix}.qmltypes)
126−
127− add_custom_target(${target_prefix}-qmltypes
128− COMMAND env ${QMLPLUGIN_ENVIRONMENT} $<TARGET_FILE:qmlplugindump> -notrelocatable
129− ${PLUGIN} ${VERSION} ${QMLPLUGIN_MODULE_DIR} > ${qmltypes_path}
130− WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
131− )
132− add_dependencies(${target_prefix}-qmltypes ${target_prefix}-qmlfiles ${QMLPLUGIN_TARGETS})
133− add_dependencies(qmltypes ${target_prefix}-qmltypes)
134− endif()
135−
136− set_target_properties(${QMLPLUGIN_TARGETS} PROPERTIES
137− ARCHIVE_OUTPUT_DIRECTORY ${qmlplugin_dir}
138− LIBRARY_OUTPUT_DIRECTORY ${qmlplugin_dir}
139− RUNTIME_OUTPUT_DIRECTORY ${qmlplugin_dir}
140− )
141−
142− if (QMLPLUGIN_DESTINATION)
143− # Install additional targets
144− install(TARGETS ${QMLPLUGIN_TARGETS}
145− DESTINATION ${QMLPLUGIN_DESTINATION}/${PATH}
146− )
147− endif()
148−endmacro()
cmake/modules/QmlTest.cmake+188−12
@@ -55,6 +55,55 @@ function(add_qml_test PATH COMPONENT_NAME)
55endfunction()
55endfunction()
56
56
57
57
58+# add_qml_test_data(path component_name
59+# [...]
60+# )
61+#
62+# Install file called ${component_name} (or ${component_name}.qml) under
63+# ${path}.
64+
65+function(add_qml_test_data PATH COMPONENT_NAME)
66+ cmake_parse_arguments(TEST "" "DESTINATION" "" ${ARGN})
67+
68+ set(filename "${CMAKE_CURRENT_SOURCE_DIR}/${PATH}/${COMPONENT_NAME}")
69+
70+ if (IS_DIRECTORY "${filename}")
71+ # As a convenience, allow specifying a directory and we will install
72+ # all files in the dir. We do it this way rather than passing
73+ # DIRECTORY to install() because we want to process any qml files.
74+ file(GLOB subfiles RELATIVE "${filename}" "${filename}/*")
75+ foreach(subfile ${subfiles})
76+ add_qml_test_data("${PATH}/${COMPONENT_NAME}" "${subfile}")
77+ endforeach()
78+ return()
79+ endif()
80+
81+ if (NOT EXISTS "${filename}")
82+ set(filename "${filename}.qml")
83+ set(COMPONENT_NAME "${COMPONENT_NAME}.qml")
84+ endif()
85+
86+ if ("${filename}" MATCHES "\\.qml$")
87+ file(READ "${filename}" contents)
88+ string(REGEX REPLACE "(\"[./]*)/qml(/|\")" "\\1\\2" contents "${contents}")
89+ # this is for (at least) cardcreatortest which pulls in an architecture-specific
90+ # import into the plugins directory (which is a 'qml' once installed).
91+ string(REGEX REPLACE "(import \"[./]*)/plugins(/|\")" "\\1/qml\\2" contents "${contents}")
92+ set(filename "${CMAKE_CURRENT_BINARY_DIR}/${PATH}/${COMPONENT_NAME}")
93+ file(WRITE "${filename}" "${contents}")
94+ endif()
95+
96+ if (TEST_DESTINATION)
97+ set(DESTINATION "${TEST_DESTINATION}")
98+ else()
99+ file(RELATIVE_PATH relcurpath "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
100+ set(DESTINATION "${SHELL_APP_DIR}/${relcurpath}/${PATH}")
101+ endif()
102+
103+ install(FILES "${filename}" DESTINATION "${DESTINATION}")
104+endfunction()
105+
106+
58# add_qml_unittest(path component_name
107# add_qml_unittest(path component_name
59# [...]
108# [...]
60# )
109# )
@@ -71,6 +120,10 @@ function(add_qml_unittest PATH COMPONENT_NAME)
71 ${ARGN}
120 ${ARGN}
72 ARGS -input ${CMAKE_CURRENT_SOURCE_DIR}/${PATH}/tst_${COMPONENT_NAME}.qml ${QMLTEST_ARGS}
121 ARGS -input ${CMAKE_CURRENT_SOURCE_DIR}/${PATH}/tst_${COMPONENT_NAME}.qml ${QMLTEST_ARGS}
73 )
122 )
123+
124+ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PATH}/tst_${COMPONENT_NAME}.qml")
125+ add_qml_test_data("${PATH}" "tst_${COMPONENT_NAME}.qml")
126+ endif()
74endfunction()
127endfunction()
75
128
76
129
@@ -91,6 +144,10 @@ function(add_manual_qml_test PATH COMPONENT_NAME)
91 ${ARGN}
144 ${ARGN}
92 ARGS ${CMAKE_CURRENT_SOURCE_DIR}/${PATH}/tst_${COMPONENT_NAME}.qml ${QMLTEST_ARGS}
145 ARGS ${CMAKE_CURRENT_SOURCE_DIR}/${PATH}/tst_${COMPONENT_NAME}.qml ${QMLTEST_ARGS}
93 )
146 )
147+
148+ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PATH}/tst_${COMPONENT_NAME}.qml")
149+ add_qml_test_data("${PATH}" "tst_${COMPONENT_NAME}.qml")
150+ endif()
94endfunction()
151endfunction()
95
152
96
153
@@ -108,8 +165,7 @@ endfunction()
108#
165#
109# Logging options in the standard form of "-o filename,format"
166# Logging options in the standard form of "-o filename,format"
110# will be appended to the arguments list, prefixed with ARG_PREFIX.
167# will be appended to the arguments list, prefixed with ARG_PREFIX.
111−# XUnitXML files will be stored in current binary dir or under
168+# XUnitXML files will be stored in current binary dir.
112−# ARTIFACTS_DIR, if set.
113#
169#
114# Three targets will be created:
170# Three targets will be created:
115# - test${component_name} - Runs the test
171# - test${component_name} - Runs the test
@@ -122,14 +178,8 @@ function(add_executable_test COMPONENT_NAME TARGET)
122 cmake_parse_arguments(QMLTEST "${QMLTEST_OPTIONS}" "${QMLTEST_SINGLE}" "${QMLTEST_MULTI}" ${ARGN})
178 cmake_parse_arguments(QMLTEST "${QMLTEST_OPTIONS}" "${QMLTEST_SINGLE}" "${QMLTEST_MULTI}" ${ARGN})
123 mangle_arguments()
179 mangle_arguments()
124
180
125− if(ARTIFACTS_DIR)
181+ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
126− file(RELATIVE_PATH path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
127− file(MAKE_DIRECTORY ${ARTIFACTS_DIR}/${path})
128− set(file_logger -o ${ARTIFACTS_DIR}/${path}/test${COMPONENT_NAME}.xml,xunitxml)
129− else()
130− file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
131− set(file_logger -o ${CMAKE_CURRENT_BINARY_DIR}/test${COMPONENT_NAME}.xml,xunitxml)
182+ set(file_logger -o ${CMAKE_CURRENT_BINARY_DIR}/test${COMPONENT_NAME}.xml,xunitxml)
132− endif()
133
183
134 bake_arguments("${QMLTEST_ARG_PREFIX}" args ${iterations} ${file_logger} ${STDOUT_LOGGER})
184 bake_arguments("${QMLTEST_ARG_PREFIX}" args ${iterations} ${file_logger} ${STDOUT_LOGGER})
135
185
@@ -151,7 +201,7 @@ function(add_executable_test COMPONENT_NAME TARGET)
151 add_qmltest_target(xvfbtest${COMPONENT_NAME} ${TARGET}
201 add_qmltest_target(xvfbtest${COMPONENT_NAME} ${TARGET}
152 COMMAND $<TARGET_FILE:xvfb-run> --server-args "-screen 0 1024x768x24" --auto-servernum ${qmltest_command}
202 COMMAND $<TARGET_FILE:xvfb-run> --server-args "-screen 0 1024x768x24" --auto-servernum ${qmltest_command}
153 ${depends}
203 ${depends}
154− ENVIRONMENT QML2_IMPORT_PATH=${imports} ${QMLTEST_ENVIRONMENT} LD_PRELOAD=/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/mesa/libGL.so.1
204+ ENVIRONMENT QT_QPA_PLATFORM=xcb QML2_IMPORT_PATH=${imports} LD_PRELOAD=/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/mesa/libGL.so.1 ${QMLTEST_ENVIRONMENT}
155 TARGETS ${xvfb_targets}
205 TARGETS ${xvfb_targets}
156 )
206 )
157 endif()
207 endif()
@@ -207,8 +257,129 @@ function(add_manual_test COMPONENT_NAME TARGET)
207endfunction()
257endfunction()
208
258
209
259
260+# add_meta_test(target)
261+#
262+# Adds a test target that will run one of our "meta" test targets, like
263+# xvfbuitests. This script will run the specified suite of tests on an
264+# installed system.
265+
266+function(add_meta_test TARGET_NAME)
267+ cmake_parse_arguments(TEST "SERIAL" "" "DEPENDS" ${ARGN})
268+
269+ add_custom_target(${TARGET_NAME})
270+
271+ set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${TARGET_NAME}.sh")
272+ if(TEST_SERIAL)
273+ file(WRITE "${filename}" "#!/bin/sh\n\n")
274+ else()
275+ file(WRITE "${filename}" "#!/usr/bin/parallel --shebang --no-notice\n\n")
276+ endif()
277+
278+ add_meta_dependencies(${TARGET_NAME} DEPENDS ${TEST_DEPENDS})
279+ # else we will write the rest of the script as we add cmake targets
280+
281+ install(FILES "${filename}"
282+ PERMISSIONS OWNER_EXECUTE OWNER_READ OWNER_WRITE
283+ GROUP_EXECUTE GROUP_READ
284+ WORLD_EXECUTE WORLD_READ
285+ DESTINATION "${SHELL_PRIVATE_LIBDIR}/tests/scripts"
286+ )
287+endfunction()
288+
289+
210################### INTERNAL ####################
290################### INTERNAL ####################
211
291
292+function(install_test_script TARGET_NAME)
293+ cmake_parse_arguments(TEST "" "" "COMMAND;ENVIRONMENT" ${ARGN})
294+
295+ # Now write the above test into a shell script that we can run on an
296+ # installed system.
297+ set(script "#!/bin/sh\n\nset -x\n\n")
298+ foreach(ONE_ENV ${TEST_ENVIRONMENT})
299+ set(script "${script}export ${ONE_ENV}\n")
300+ endforeach()
301+ set(script "${script}export UNITY_TESTING_DATADIR=\"${CMAKE_INSTALL_PREFIX}/${SHELL_APP_DIR}\"\n")
302+ set(script "${script}export UNITY_TESTING_LIBDIR=\"${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}\"\n")
303+ set(script "${script}\n")
304+ set(script "${script}XML_ARGS=\n")
305+ set(script "${script}if [ -n \"\$ARTIFACTS_DIR\" ]; then\n")
306+ set(script "${script} XML_ARGS=\"@XML_ARGS@\"\n")
307+ set(script "${script} mkdir -p \"@XML_DIR@\"\n")
308+ set(script "${script} touch \"@XML_FILE@\"\n")
309+ set(script "${script}fi\n")
310+ set(script "${script}\n")
311+ foreach(ONE_CMD ${TEST_COMMAND})
312+ set(script "${script}'${ONE_CMD}' ")
313+ endforeach()
314+ set(script "${script}\"\$@\"") # Allow passing arguments if desired
315+
316+ set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${TARGET_NAME}.sh")
317+
318+ # Generate script to file then read it back to resolve any generator
319+ # expressions before we try to replace paths.
320+ file(GENERATE
321+ OUTPUT "${filename}"
322+ CONTENT "${script}"
323+ )
324+
325+ # Do replacement at install time to save needless work and to make sure
326+ # we are modifying file after generate step above (which doesn't happen
327+ # immediately). We can't use a custom-defined function or macro here...
328+ # So instead we use a giant ugly code block.
329+
330+ # START OF CODE BLOCK --------------------------------------------------
331+ install(CODE "
332+ file(READ \"${filename}\" replacestr)
333+
334+ # Now some replacements...
335+ # tests like to write xml output to our builddir; we don't need that, but we do want them in ARTIFACTS_DIR
336+ string(REGEX MATCH \"( '--parameter')? '-o'( '--parameter')? '[^']*,xunitxml' \" xmlargs \"\${replacestr}\")
337+ string(REGEX REPLACE \"( '--parameter')? '-o'( '--parameter')? '[^']*,xunitxml' \" \" \\\$XML_ARGS \" replacestr \"\${replacestr}\")
338+ string(REGEX REPLACE \"'[^']*/tests/\" \"'\\\$ARTIFACTS_DIR/tests/\" xmlargs \"\${xmlargs}\")
339+ string(REGEX REPLACE \".*'([^']*),xunitxml'.*\" \"\\\\1\" xmlfile \"\${xmlargs}\")
340+ string(REGEX REPLACE \"(.*)/[^/]*\" \"\\\\1\" xmldir \"\${xmlfile}\")
341+ string(REGEX REPLACE \"'\" \"\" xmlargs \"\${xmlargs}\") # strip single quotes
342+ string(REGEX REPLACE \"@XML_ARGS@\" \"\${xmlargs}\" replacestr \"\${replacestr}\")
343+ string(REGEX REPLACE \"@XML_DIR@\" \"\${xmldir}\" replacestr \"\${replacestr}\")
344+ string(REGEX REPLACE \"@XML_FILE@\" \"\${xmlfile}\" replacestr \"\${replacestr}\")
345+ # replace build/source roots with their install paths
346+ string(REPLACE \"${CMAKE_BINARY_DIR}/libs\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}\" replacestr \"\${replacestr}\")
347+ string(REPLACE \"${CMAKE_BINARY_DIR}/plugins\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}\" replacestr \"\${replacestr}\")
348+ string(REPLACE \"${CMAKE_BINARY_DIR}/tests/libs\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}/tests/libs\" replacestr \"\${replacestr}\")
349+ string(REPLACE \"${CMAKE_BINARY_DIR}/tests/mocks\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}/mocks\" replacestr \"\${replacestr}\")
350+ string(REPLACE \"${CMAKE_BINARY_DIR}/tests/plugins\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}/tests/plugins\" replacestr \"\${replacestr}\")
351+ string(REPLACE \"${CMAKE_BINARY_DIR}/tests/qmltests\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}/tests/qmltests\" replacestr \"\${replacestr}\")
352+ string(REPLACE \"${CMAKE_BINARY_DIR}/tests/uqmlscene\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}\" replacestr \"\${replacestr}\")
353+ string(REPLACE \"${CMAKE_BINARY_DIR}/tests/utils/modules\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}/utils\" replacestr \"\${replacestr}\")
354+ string(REPLACE \"${CMAKE_SOURCE_DIR}/tests/plugins\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_APP_DIR}/tests/plugins\" replacestr \"\${replacestr}\")
355+ string(REPLACE \"${CMAKE_SOURCE_DIR}/tests/qmltests\" \"${CMAKE_INSTALL_PREFIX}/${SHELL_APP_DIR}/tests/qmltests\" replacestr \"\${replacestr}\")
356+
357+ file(WRITE \"${filename}\" \"\${replacestr}\")
358+ ")
359+ # END OF CODE BLOCK --------------------------------------------------
360+
361+ install(FILES "${filename}"
362+ PERMISSIONS OWNER_EXECUTE OWNER_READ OWNER_WRITE
363+ GROUP_EXECUTE GROUP_READ
364+ WORLD_EXECUTE WORLD_READ
365+ DESTINATION "${SHELL_PRIVATE_LIBDIR}/tests/scripts"
366+ )
367+endfunction()
368+
369+function(add_meta_dependencies UPSTREAM_TARGET)
370+ cmake_parse_arguments(TEST "" "" "DEPENDS" ${ARGN})
371+
372+ foreach(depend ${TEST_DEPENDS})
373+ add_dependencies(${UPSTREAM_TARGET} ${depend})
374+
375+ # add depend to the meta test script that we will install on system
376+ set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${UPSTREAM_TARGET}.sh")
377+ if (EXISTS "${filename}")
378+ file(APPEND "${filename}" "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}/tests/scripts/${depend}.sh \"\$@\" 2>&1\n")
379+ endif()
380+ endforeach()
381+endfunction()
382+
212# add_qmltest_target(target_name target
383# add_qmltest_target(target_name target
213# COMMAND test_exe [arg1 [...]] # execute this test with arguments
384# COMMAND test_exe [arg1 [...]] # execute this test with arguments
214# [...] # see above for available arguments:
385# [...] # see above for available arguments:
@@ -231,6 +402,11 @@ function(add_qmltest_target TARGET_NAME TARGET)
231 DEPENDS ${TARGET} ${QMLTEST_DEPENDS}
402 DEPENDS ${TARGET} ${QMLTEST_DEPENDS}
232 )
403 )
233
404
405+ install_test_script(${TARGET_NAME}
406+ ENVIRONMENT ${QMLTEST_ENVIRONMENT}
407+ COMMAND ${QMLTEST_COMMAND}
408+ )
409+
234 if(QMLTEST_ADD_TEST)
410 if(QMLTEST_ADD_TEST)
235 add_test(
411 add_test(
236 NAME ${TARGET_NAME}
412 NAME ${TARGET_NAME}
@@ -248,7 +424,7 @@ function(add_qmltest_target TARGET_NAME TARGET)
248 endif()
424 endif()
249
425
250 foreach(UPSTREAM_TARGET ${QMLTEST_TARGETS})
426 foreach(UPSTREAM_TARGET ${QMLTEST_TARGETS})
251− add_dependencies(${UPSTREAM_TARGET} ${TARGET_NAME})
427+ add_meta_dependencies(${UPSTREAM_TARGET} DEPENDS ${TARGET_NAME})
252 endforeach()
428 endforeach()
253endfunction()
429endfunction()
254
430
data/CMakeLists.txt+1−9
@@ -1,13 +1,11 @@
1# generate desktop files
1# generate desktop files
2configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
2configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
3−configure_file(${DASH_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop @ONLY)
4configure_file(${INDICATORS_CLIENT_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${INDICATORS_CLIENT_APP}.desktop @ONLY)
3configure_file(${INDICATORS_CLIENT_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${INDICATORS_CLIENT_APP}.desktop @ONLY)
5configure_file(${GREETER_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${GREETER_APP}.desktop @ONLY)
4configure_file(${GREETER_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${GREETER_APP}.desktop @ONLY)
6
5
7# install desktop files
6# install desktop files
8install(FILES
7install(FILES
9 ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
8 ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
10− ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop
11 ${CMAKE_CURRENT_BINARY_DIR}/${INDICATORS_CLIENT_APP}.desktop
9 ${CMAKE_CURRENT_BINARY_DIR}/${INDICATORS_CLIENT_APP}.desktop
12 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
10 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
13 )
11 )
@@ -22,14 +20,8 @@ install(FILES
22 DESTINATION ${CMAKE_INSTALL_DATADIR}/lightdm/lightdm.conf.d
20 DESTINATION ${CMAKE_INSTALL_DATADIR}/lightdm/lightdm.conf.d
23 )
21 )
24
22
25−# install dash icon
26−install(FILES
27− ${CMAKE_CURRENT_SOURCE_DIR}/${DASH_APP}.png
28− DESTINATION ${SHELL_APP_DIR}/Dash/
29− )
30−
31add_custom_target(pkgversion ALL COMMAND dpkg-parsechangelog -l${CMAKE_SOURCE_DIR}/debian/changelog --show-field version > ${CMAKE_CURRENT_BINARY_DIR}/version)
23add_custom_target(pkgversion ALL COMMAND dpkg-parsechangelog -l${CMAKE_SOURCE_DIR}/debian/changelog --show-field version > ${CMAKE_CURRENT_BINARY_DIR}/version)
32
24
33install(FILES com.canonical.Unity8.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
25install(FILES com.canonical.Unity8.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
34−install(FILES unity8-dash.url-dispatcher DESTINATION ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls)
26+install(FILES com.canonical.Unity.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
35install(FILES ${CMAKE_CURRENT_BINARY_DIR}/version DESTINATION ${CMAKE_INSTALL_LOCALSTATEDIR}/lib/unity8)
27install(FILES ${CMAKE_CURRENT_BINARY_DIR}/version DESTINATION ${CMAKE_INSTALL_LOCALSTATEDIR}/lib/unity8)
data/com.canonical.Unity.gschema.xmlnew file+255
@@ -0,0 +1,255 @@
1+<schemalist>
2+ <enum id="form-factor-enum">
3+ <value nick="Automatic" value="0" />
4+ <value nick="Desktop" value="1" />
5+ <value nick="Netbook" value="2" />
6+ <value nick="TV" value="3" />
7+ </enum>
8+ <enum id="home-expanded-enum">
9+ <value nick="Not Expanded" value="0" />
10+ <value nick="Expanded" value="1" />
11+ </enum>
12+ <enum id="launcher-position-enum">
13+ <value nick="Left" value="0" />
14+ <value nick="Bottom" value="1" />
15+ </enum>
16+ <enum id="desktop-type-enum">
17+ <value nick="Ubuntu" value="0" />
18+ <value nick="UbuntuKylin" value="1" />
19+ </enum>
20+
21+ <schema path="/com/canonical/unity/" id="com.canonical.Unity" gettext-domain="unity">
22+ <key enum="form-factor-enum" name="form-factor">
23+ <default>"Automatic"</default>
24+ <summary>The form factor Unity should target.</summary>
25+ <description>The form factor chosen will affect the size and appearance of the Dash, Launcher and also the Window Management behaviour.</description>
26+ </key>
27+ <key enum="home-expanded-enum" name="home-expanded">
28+ <default>"Expanded"</default>
29+ <summary>Whether the home screen should be expanded.</summary>
30+ <description>Whether the home screen should be expanded.</description>
31+ </key>
32+ <key enum="desktop-type-enum" name="desktop-type">
33+ <default>"Ubuntu"</default>
34+ <summary>The current desktop name.</summary>
35+ <description>The current desktop name.</description>
36+ </key>
37+ <key type="i" name="minimize-count">
38+ <default>0</default>
39+ <summary>Number of times a normal window has been minimized.</summary>
40+ <description>This is used to adjust the animation speed by making it progressively faster the more it is used.</description>
41+ </key>
42+ <key type="i" name="minimize-fast-duration">
43+ <default>300</default>
44+ <summary>The duration for the minimize animation when it is at its fastest setting.</summary>
45+ <description>This is the duration that the minimize animation will have when it has been used more than an number of times equal minimize-speed-threshold.</description>
46+ </key>
47+ <key type="i" name="minimize-slow-duration">
48+ <default>800</default>
49+ <summary>The duration for the minimize animation when it is at its slowest setting.</summary>
50+ <description>This is the duration that the minimize animation will have when it has never been used.</description>
51+ </key>
52+ <key type="i" name="minimize-speed-threshold">
53+ <default>100</default>
54+ <summary>The number of minimizations required to reach maximum speed.</summary>
55+ <description>The speed of the minimize animation will progressively get faster as minimize-count approaches this value.</description>
56+ </key>
57+ <key type="b" name="double-click-activate">
58+ <default>true</default>
59+ <summary>Enable/Disable dash result double-click activation.</summary>
60+ <description>This is used to enable/disable double-click result activation in the dash. Double-click to activate, single click preview</description>
61+ </key>
62+ <key type="b" name="integrated-menus">
63+ <default>false</default>
64+ <summary>Enable/Disable the integrated menus in Unity.</summary>
65+ <description>When this is enabled, the application menus will be shown
66+ on the window decoration, otherwise they will be always shown on the
67+ unity top panel</description>
68+ </key>
69+ <key type="u" name="show-menus-now-delay">
70+ <default>180</default>
71+ <range min="0" max="5000"/>
72+ <summary>Delay (in ms) before showing the menus when pressing Alt</summary>
73+ <description>This allows tune the delay before presenting unity menus
74+ (in non always-show-menus mode).</description>
75+ </key>
76+ <key type="b" name="always-show-menus">
77+ <default>false</default>
78+ <summary>Toggle the menu visibility based on mouse hovering.</summary>
79+ <description>When this is enabled, the application menus will be always
80+ shown (on the window decoration or in the unity panel, depending whether
81+ integrated menus are enabled), otherwise they will be shown only when
82+ the mouse cursor is over the relative mouse area.</description>
83+ </key>
84+ <key type="as" name="whitelist-repeated-keys">
85+ <default>[
86+ 'XF86KbdBrightnessUp',
87+ 'XF86KbdBrightnessDown',
88+ 'XF86MonBrightnessUp',
89+ 'XF86MonBrightnessDown',
90+ 'XF86AudioRaiseVolume',
91+ 'XF86AudioLowerVolume'
92+ ]</default>
93+ <summary>List of keycodes that should be processed even if auto-repated.</summary>
94+ <description>These keycodes are processed even if they are auto-repeated.</description>
95+ </key>
96+ <key type="b" name="pam-check-account-type">
97+ <default>false</default>
98+ <summary>Enable/Disable PAM account checking</summary>
99+ <description>Whether PAM should check the result of account modules
100+ when authenticating. Only do this if you have account
101+ configured properly on your system.</description>
102+ </key>
103+ <key type="b" name="lowgfx">
104+ <default>false</default>
105+ <summary>Enable/Disable Low Graphics Mode</summary>
106+ <description>Whether Unity runs in Low Graphics Mode.</description>
107+ </key>
108+ </schema>
109+ <schema path="/com/canonical/unity/interface/" id="com.canonical.Unity.Interface" gettext-domain="unity">
110+ <key type="d" name="text-scale-factor">
111+ <default>1.0</default>
112+ <range min="0.5" max="3.0"/>
113+ <summary>Font scaling for unity and applications.</summary>
114+ <description>This value defines the global font scaling used by both
115+ unity and applications. It will match the current system font scaling.</description>
116+ </key>
117+ <key type="d" name="cursor-scale-factor">
118+ <default>1.0</default>
119+ <range min="0.5" max="3.0"/>
120+ <summary>Mouse cursor scaling.</summary>
121+ <description>This value defines the global mouse cursor scaling used by both
122+ unity and applications. Changing this value allows to use a custom scaling
123+ for the mouse cursor.</description>
124+ </key>
125+ <key type="s" name="app-scale-factor-monitor">
126+ <default>''</default>
127+ <summary>The name of the monitor that controls the global app scaling factor.</summary>
128+ <description>This value defines the monitor that unity will use (if found) as reference
129+ for scaling all the applications.</description>
130+ </key>
131+ <key type="b" name="app-fallback-to-maximum-scale-factor">
132+ <default>true</default>
133+ <summary>Use the maximum per-monitor scale-factor as application scale factor if no target monitor is found.</summary>
134+ <description>When this is enabled, the applications scale factor will match
135+ the scale factor of the monitor with the greater value, in case the monitor
136+ defined in 'app-scale-factor-monitor' is not provided or available.</description>
137+ </key>
138+ </schema>
139+ <schema path="/com/canonical/unity/launcher/" id="com.canonical.Unity.Launcher" gettext-domain="unity">
140+ <key type="as" name="favorites">
141+ <default>[
142+ 'application://ubiquity.desktop',
143+ 'application://org.gnome.Nautilus.desktop',
144+ 'application://firefox.desktop',
145+ 'application://libreoffice-writer.desktop',
146+ 'application://libreoffice-calc.desktop',
147+ 'application://libreoffice-impress.desktop',
148+ 'application://org.gnome.Software.desktop',
149+ 'application://ubuntu-amazon-default.desktop',
150+ 'application://unity-control-center.desktop',
151+ 'unity://running-apps',
152+ 'unity://expo-icon',
153+ 'unity://devices'
154+ ]</default>
155+ <summary>List of items that should be shown by default in the launcher</summary>
156+ <description>These items can be: application://desktop-id.desktop, device://uiid and unity://special-id (including: unity://running-apps (icons of running applications) unity://devices (icons of attached devices), unity://expo-icon (icon of the workspace switcher) and unity://desktop-icon (the show-desktop icon)); the order of this list determines the launcher item's position.</description>
157+ </key>
158+ <key type="as" name="items">
159+ <default>[
160+ 'application:///dialer-app.desktop',
161+ 'application:///messaging-app.desktop',
162+ 'application:///address-book-app.desktop',
163+ 'application:///ubuntu-system-settings.desktop',
164+ 'appid://openstore.openstore-team/openstore/current-user-version',
165+ 'appid://com.ubuntu.camera/camera/current-user-version',
166+ 'appid://com.ubuntu.gallery/gallery/current-user-version',
167+ 'application:///morph-browser.desktop'
168+ ]</default>
169+ <summary>List of items that should be shown by default in the launcher</summary>
170+ <description>These items can be: application:///desktop-id.desktop or appid://package/app/current-user-version.</description>
171+ </key>
172+ <key type="s" name="favorite-migration">
173+ <default>''</default>
174+ <summary>Version of last migration done</summary>
175+ <description>This is a detection key for the favorite migration script to know whether the needed migration is done or not.</description>
176+ </key>
177+ <key enum="launcher-position-enum" name="launcher-position">
178+ <default>"Left"</default>
179+ <summary>The position of launcher.</summary>
180+ <description>The position of launcher.</description>
181+ </key>
182+ </schema>
183+ <schema path="/com/canonical/unity/devices/" id="com.canonical.Unity.Devices" gettext-domain="unity">
184+ <key type="as" name="blacklist">
185+ <default>[]</default>
186+ <summary>List of device uuid blacklist from the launcher.</summary>
187+ <description>These devices are not shown in the launcher by default.</description>
188+ </key>
189+ </schema>
190+ <schema path="/com/canonical/unity/dash/" id="com.canonical.Unity.Dash" gettext-domain="unity">
191+ <key type="as" name="scopes">
192+ <default>[ 'home.scope', 'applications.scope', 'files.scope', 'video.scope', 'music.scope', 'photos.scope', 'social.scope' ]</default>
193+ <summary>List of scope ids specifying which scopes should be created and the order to display them in.</summary>
194+ <description>The scopes listed in the scope bar will be ordered according to this list.</description>
195+ </key>
196+ <key type="as" name="favorite-scopes">
197+ <default>[ 'scope://clickscope', 'scope://musicaggregator', 'scope://videoaggregator' ]</default>
198+ <summary>List of scope queries specifying what should be displayed in the dash.</summary>
199+ <description>List of favorite scopes displayed in the dash.</description>
200+ </key>
201+ </schema>
202+ <schema path="/com/canonical/unity/decorations/" id="com.canonical.Unity.Decorations" gettext-domain="unity">
203+ <key type="u" name="grab-wait">
204+ <default>175</default>
205+ <range min="0" max="1000"/>
206+ <summary>Milliseconds to wait before considering a mouse pressure a grab</summary>
207+ <description>The number of ms to wait before starting the window movement (and then
208+ showing the proper mouse cursor, and grabbing the window)</description>
209+ </key>
210+ </schema>
211+ <schema path="/com/canonical/unity/integrated-menus/" id="com.canonical.Unity.IntegratedMenus" gettext-domain="unity">
212+ <key type="u" name="click-movement-threshold">
213+ <default>15</default>
214+ <range min="0" max="30"/>
215+ <summary>Number of pixels to ignore on mouse pressure</summary>
216+ <description>This value define how many pixels the user can move the mouse
217+ when holding the mouse button, before we consider his action a movement
218+ (to grab the window).</description>
219+ </key>
220+ <key type="u" name="double-click-wait">
221+ <default>0</default>
222+ <range min="0" max="300"/>
223+ <summary>Milliseconds to wait before activating a menu after mouse release</summary>
224+ <description>The number of ms to wait after a mouse button release event before considering
225+ it a click. When this value is greater than 0, then it's possible to maximize
226+ a window by double-clicking on its menus if the second click happens before the
227+ specified value of ms is elapsed</description>
228+ </key>
229+ <key type="b" name="unfocused-windows-popup">
230+ <default>true</default>
231+ <summary>Whether to pop-up a menu when clicking over unfocused windows entries</summary>
232+ <description>By disabling this setting you can make unity to only focus a window
233+ when clicking over unfocused menu entries</description>
234+ </key>
235+ </schema>
236+ <schema path="/com/canonical/unity/gestures/" id="com.canonical.Unity.Gestures" gettext-domain="unity">
237+ <key type="b" name="launcher-drag">
238+ <default>true</default>
239+ <summary>Multi-touch gesture to reveal the launcher.</summary>
240+ <description>When this is enabled, a 4 finger swipe from left to right will reveal launcher,
241+ provided that the launcher is set to auto-hide.</description>
242+ </key>
243+ <key type="b" name="dash-tap">
244+ <default>true</default>
245+ <summary>Multi-touch gesture to open the dash.</summary>
246+ <description>When this is enabled, a 4 finger tap will open the dash.</description>
247+ </key>
248+ <key type="b" name="windows-drag-pinch">
249+ <default>true</default>
250+ <summary>Multi-touch gestures to manage the windows.</summary>
251+ <description>When this is enabled, 3 finger gestures such as drag, and pinch, will
252+ help manage the windows.</description>
253+ </key>
254+ </schema>
255+</schemalist>
data/com.canonical.Unity8.gschema.xml+22−7
@@ -11,8 +11,8 @@
11 <summary>The usage mode.</summary>
11 <summary>The usage mode.</summary>
12 <description>The usage mode chosen will affect the Window Management behaviour.</description>
12 <description>The usage mode chosen will affect the Window Management behaviour.</description>
13 </key>
13 </key>
14− <key type="y" name="edge-barrier-sensitivity">
14+ <key type="u" name="edge-barrier-sensitivity">
15− <default>35</default>
15+ <default>50</default>
16 <range min="1" max="100"/>
16 <range min="1" max="100"/>
17 <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary>
17 <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary>
18 <description>Some UI actions like revealing the launcher or the applications spread are triggered by pushing the mouse pointer against a screen edge. This key defines how much you have to push in order to trigger the associated action.</description>
18 <description>Some UI actions like revealing the launcher or the applications spread are triggered by pushing the mouse pointer against a screen edge. This key defines how much you have to push in order to trigger the associated action.</description>
@@ -23,7 +23,7 @@
23 <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description>
23 <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description>
24 </key>
24 </key>
25 <key type="u" name="edge-barrier-max-push">
25 <key type="u" name="edge-barrier-max-push">
26− <default>60</default>
26+ <default>120</default>
27 <summary>Maximum push needed to overcome edge barrier</summary>
27 <summary>Maximum push needed to overcome edge barrier</summary>
28 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
28 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
29 </key>
29 </key>
@@ -33,7 +33,7 @@
33 <description>How big (in grid units) the edge-drag sensitive area should be.</description>
33 <description>How big (in grid units) the edge-drag sensitive area should be.</description>
34 </key>
34 </key>
35 <key type="b" name="autohide-launcher">
35 <key type="b" name="autohide-launcher">
36− <default>true</default>
36+ <default>false</default>
37 <summary>Autohide the launcher</summary>
37 <summary>Autohide the launcher</summary>
38 <description>This will only be applied in windowed mode. In staged mode, the launcher will always hide.</description>
38 <description>This will only be applied in windowed mode. In staged mode, the launcher will always hide.</description>
39 </key>
39 </key>
@@ -43,10 +43,25 @@
43 <summary>Width of the launcher in grid units.</summary>
43 <summary>Width of the launcher in grid units.</summary>
44 <description>Changes the width of the launcher in all usage modes.</description>
44 <description>Changes the width of the launcher in all usage modes.</description>
45 </key>
45 </key>
46− <key type="b" name="enable-launcher">
46+ <key type="b" name="enable-indicator-menu">
47 <default>true</default>
47 <default>true</default>
48− <summary>Enable or disable the launcher</summary>
48+ <summary>Enable or disable the indicator pull down menus</summary>
49− <description>Toggle the availability of the launcher</description>
49+ <description>Toggle the availability of the indicator pull down menus</description>
50+ </key>
51+ <key type="s" name="appstore-uri">
52+ <default>'appid://openstore.openstore-team/openstore/current-user-version'</default>
53+ <summary>The uri to the app store</summary>
54+ <description>This will be used whenever the user triggers an action to open the app store.</description>
55+ </key>
56+ <key type="b" name="always-show-osk">
57+ <default>false</default>
58+ <summary>Force showing/hiding the OSK</summary>
59+ <description>Toggle the visibility of the On Screen Keyboard unconditionally</description>
60+ </key>
61+ <key type="b" name="osk-switch-visible">
62+ <default>false</default>
63+ <summary>Whether the OSK switch should be visible</summary>
64+ <description>Toggle the visibility of the OSK switch</description>
50 </key>
65 </key>
51 </schema>
66 </schema>
52
67
data/test.sensorsnew file+12
@@ -0,0 +1,12 @@
1+create light 0 10 1
2+create accel 0 1000 0.1
3+create proximity
4+
5+200 proximity near
6+500 light 5
7+# simulate crash on the ground
8+500 accel 0.2 0.1 10
9+100 accel 0.2 0.2 1000
10+20 accel 0 0 0
11+10 proximity far
12+10 light 10
data/unity8-dash.confdeleted−25
@@ -1,25 +0,0 @@
1−description "Unity Shell v8 Dash"
2−author "Michael Zanetti <michael.zanetti@canonical.com>"
3−
4−emits scope-ui-starting
5−
6−start on started unity8
7−stop on stopping unity8
8−
9−kill timeout 120
10−
11−# Focused app is 100 so we want to be less likely to get
12−# killed than the focused app but more than Unity8
13−
14−oom score 50
15−
16−respawn
17−respawn limit unlimited
18−
19−env APP_ID=unity8-dash
20−
21−pre-start script
22− initctl emit scope-ui-starting
23−end script
24−
25−exec ${BINARY:-unity8-dash} $ARGS --desktop_file_hint=unity8-dash.desktop
data/unity8-dash.desktop.indeleted−9
@@ -1,9 +0,0 @@
1−[Desktop Entry]
2−Type=Application
3−Name=Scopes
4−Comment=An interface for using the Scopes system
5−Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity8-dash
6−Terminal=false
7−Icon=@CMAKE_INSTALL_PREFIX@/@SHELL_APP_DIR@/Dash/unity8-dash.png
8−NoDisplay=true
9−X-Ubuntu-Touch=true
data/unity8-dash.pngdeleted

Binary file not shown.

data/unity8-dash.svgdeleted−326
@@ -1,326 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2−<!-- Created with Inkscape (http://www.inkscape.org/) -->
3−
4−<svg
5− xmlns:dc="http://purl.org/dc/elements/1.1/"
6− xmlns:cc="http://creativecommons.org/ns#"
7− xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8− xmlns:svg="http://www.w3.org/2000/svg"
9− xmlns="http://www.w3.org/2000/svg"
10− xmlns:xlink="http://www.w3.org/1999/xlink"
11− xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12− xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13− viewBox="0 0 512 512"
14− style="enable-background:new"
15− inkscape:export-ydpi="180"
16− inkscape:export-xdpi="180"
17− inkscape:export-filename="app_shape02.png"
18− sodipodi:docname="apps-scope.svg"
19− inkscape:version="0.91+devel r"
20− version="1.1"
21− id="svg3336"
22− height="512"
23− width="512">
24− <sodipodi:namedview
25− inkscape:snap-global="false"
26− fit-margin-bottom="18"
27− fit-margin-right="0"
28− fit-margin-left="0"
29− fit-margin-top="18"
30− inkscape:snap-grids="true"
31− inkscape:guide-bbox="true"
32− showguides="false"
33− inkscape:snap-center="true"
34− inkscape:snap-object-midpoints="true"
35− inkscape:snap-midpoints="true"
36− inkscape:snap-smooth-nodes="true"
37− inkscape:snap-intersection-paths="true"
38− inkscape:object-nodes="true"
39− inkscape:object-paths="true"
40− inkscape:snap-bbox-midpoints="true"
41− inkscape:snap-bbox-edge-midpoints="true"
42− inkscape:bbox-nodes="true"
43− inkscape:bbox-paths="true"
44− inkscape:snap-bbox="true"
45− showborder="true"
46− inkscape:showpageshadow="false"
47− inkscape:snap-page="true"
48− inkscape:window-maximized="1"
49− inkscape:window-y="24"
50− inkscape:window-x="0"
51− inkscape:window-height="1029"
52− inkscape:window-width="1920"
53− showgrid="false"
54− inkscape:current-layer="layer3"
55− inkscape:document-units="px"
56− inkscape:cy="204.04409"
57− inkscape:cx="-384.5809"
58− inkscape:zoom="0.64"
59− inkscape:pageshadow="2"
60− inkscape:pageopacity="0"
61− borderopacity="1.0"
62− bordercolor="#666666"
63− pagecolor="#4f4f4f"
64− id="base">
65− <inkscape:grid
66− type="xygrid"
67− id="grid811"
68− originx="0"
69− originy="15.999999"
70− visible="true"
71− enabled="true" />
72− <sodipodi:guide
73− orientation="1,0"
74− position="104,176"
75− id="guide819" />
76− <sodipodi:guide
77− orientation="1,0"
78− position="120,160"
79− id="guide821" />
80− <sodipodi:guide
81− orientation="1,0"
82− position="392,144"
83− id="guide823" />
84− <sodipodi:guide
85− orientation="1,0"
86− position="408,144"
87− id="guide825" />
88− <sodipodi:guide
89− orientation="0,1"
90− position="328,408"
91− id="guide827" />
92− <sodipodi:guide
93− orientation="0,1"
94− position="336,392"
95− id="guide829" />
96− <sodipodi:guide
97− orientation="0,1"
98− position="176,120"
99− id="guide831" />
100− <sodipodi:guide
101− orientation="0,1"
102− position="216,104"
103− id="guide833" />
104− <inkscape:grid
105− type="axonomgrid"
106− id="grid835"
107− units="px"
108− empspacing="60"
109− gridanglex="42.9"
110− gridanglez="42.9"
111− spacingy="3.522px"
112− enabled="false"
113− originx="0"
114− originy="16.999996" />
115− <sodipodi:guide
116− orientation="0,1"
117− position="344,376"
118− id="guide4126" />
119− <sodipodi:guide
120− orientation="0,1"
121− position="160,136"
122− id="guide4128" />
123− <sodipodi:guide
124− orientation="1,0"
125− position="136,96"
126− id="guide4130" />
127− <sodipodi:guide
128− orientation="1,0"
129− position="376,136"
130− id="guide4134" />
131− <sodipodi:guide
132− orientation="-0.70710678,0.70710678"
133− position="544,544"
134− id="guide4136" />
135− <sodipodi:guide
136− orientation="1,0"
137− position="256,-32.000001"
138− id="guide4138" />
139− <sodipodi:guide
140− orientation="0,1"
141− position="544,256"
142− id="guide4140" />
143− <sodipodi:guide
144− orientation="0.70710678,0.70710678"
145− position="544,-32.000001"
146− id="guide4142" />
147− <sodipodi:guide
148− orientation="0,1"
149− position="248,15.999999"
150− id="guide4152" />
151− <sodipodi:guide
152− orientation="0,1"
153− position="336,496"
154− id="guide4154" />
155− <sodipodi:guide
156− position="344,96"
157− orientation="1,0"
158− id="guide4361" />
159− <sodipodi:guide
160− position="576,168"
161− orientation="0,1"
162− id="guide4363" />
163− <sodipodi:guide
164− position="168,192"
165− orientation="1,0"
166− id="guide4229" />
167− <sodipodi:guide
168− position="312,344"
169− orientation="0,1"
170− id="guide4233" />
171− <sodipodi:guide
172− position="180.48,-96"
173− orientation="0,1"
174− id="guide4235" />
175− </sodipodi:namedview>
176− <defs
177− id="defs3338">
178− <linearGradient
179− inkscape:collect="always"
180− id="linearGradient4203">
181− <stop
182− style="stop-color:#ea5420;stop-opacity:1"
183− offset="0"
184− id="stop4205" />
185− <stop
186− style="stop-color:#f37e40;stop-opacity:1"
187− offset="1"
188− id="stop4207" />
189− </linearGradient>
190− <clipPath
191− clipPathUnits="userSpaceOnUse"
192− id="clipPath4755">
193− <path
194− style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.24899998;fill:#5e2c73;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
195− d="M 360,50 594,284 V 215.89844 C 594,70.73665 573.2426,50 427.94531,50 Z"
196− id="path4757"
197− inkscape:connector-curvature="0" />
198− </clipPath>
199− <clipPath
200− clipPathUnits="userSpaceOnUse"
201− id="clipPath4767">
202− <path
203− style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f6f6f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
204− d="M 543.8125,454.1875 312.18945,685.81055 367.94531,681 C 512.70468,668.50896 526.55393,654.72881 539,510.10156 Z"
205− id="path4769"
206− inkscape:connector-curvature="0" />
207− </clipPath>
208− <linearGradient
209− inkscape:collect="always"
210− xlink:href="#linearGradient4203"
211− id="linearGradient4209"
212− x1="167.5"
213− y1="17.25"
214− x2="343.25"
215− y2="491"
216− gradientUnits="userSpaceOnUse" />
217− <filter
218− inkscape:collect="always"
219− style="color-interpolation-filters:sRGB"
220− id="filter4220"
221− x="-0.012"
222− width="1.024"
223− y="-0.012"
224− height="1.024">
225− <feGaussianBlur
226− inkscape:collect="always"
227− stdDeviation="1.4390401"
228− id="feGaussianBlur4222" />
229− </filter>
230− <linearGradient
231− inkscape:collect="always"
232− xlink:href="#linearGradient4226"
233− id="linearGradient4232"
234− x1="255.99998"
235− y1="-255.99998"
236− x2="405.35773"
237− y2="-255.99998"
238− gradientUnits="userSpaceOnUse"
239− spreadMethod="reflect" />
240− <linearGradient
241− inkscape:collect="always"
242− id="linearGradient4226">
243− <stop
244− style="stop-color:#a73812;stop-opacity:1"
245− offset="0"
246− id="stop4228" />
247− <stop
248− style="stop-color:#973310;stop-opacity:0"
249− offset="1"
250− id="stop4230" />
251− </linearGradient>
252− <filter
253− inkscape:collect="always"
254− style="color-interpolation-filters:sRGB"
255− id="filter4234"
256− x="-0.096000001"
257− width="1.192"
258− y="-0.096000001"
259− height="1.192">
260− <feGaussianBlur
261− inkscape:collect="always"
262− stdDeviation="11.512321"
263− id="feGaussianBlur4236" />
264− </filter>
265− </defs>
266− <metadata
267− id="metadata3341">
268− <rdf:RDF>
269− <cc:Work
270− rdf:about="">
271− <dc:format>image/svg+xml</dc:format>
272− <dc:type
273− rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
274− <dc:title></dc:title>
275− </cc:Work>
276− </rdf:RDF>
277− </metadata>
278− <g
279− inkscape:groupmode="layer"
280− id="layer3"
281− inkscape:label="Background">
282− <path
283− style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient4209);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
284− d="M 0 0 L 0 183.89844 L 0 328.10156 L 0 512 L 512 512 L 512 328.10156 L 512 183.89844 L 512 0 L 0 0 z "
285− id="rect4158" />
286− <path
287− style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
288− d="M 0 256 L 0 328.10156 L 0 462.53516 C 0 479.69677 0.052846476 496.03174 0.13085938 512 L 511.86914 512 C 511.94715 496.03174 512 479.69677 512 462.53516 L 512 256 L 0 256 z "
289− id="rect4286" />
290− </g>
291− <g
292− inkscape:groupmode="layer"
293− id="layer1"
294− inkscape:label="Pictogram"
295− style="display:inline"
296− transform="translate(-32,-32)">
297− <circle
298− style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.4;fill:#d04616;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.99999952;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;filter:url(#filter4220);enable-background:accumulate"
299− id="circle4218"
300− cx="258"
301− cy="-256"
302− r="143.90401"
303− transform="rotate(90,-7.1132638e-7,31.999996)" />
304− <circle
305− transform="rotate(90,-7.1132638e-7,31.999996)"
306− r="143.90401"
307− cy="-256"
308− cx="258"
309− id="circle4224"
310− style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#linearGradient4232);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.99999952;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;filter:url(#filter4234);enable-background:accumulate" />
311− <circle
312− transform="rotate(90)"
313− r="143.90401"
314− cy="-288"
315− cx="288"
316− id="circle4206"
317− style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.99999952;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
318− <path
319− style="display:inline;fill:#ea5420;fill-opacity:1;stroke-width:2.9398191;enable-background:new"
320− id="path4258"
321− d="m 193.9963,269.54024 c -10.19878,0 -18.46137,8.2634 -18.46137,18.46139 0,10.19309 8.26259,18.45568 18.46137,18.45568 10.19311,0 18.45569,-8.26259 18.45569,-18.45568 0,-10.19799 -8.26258,-18.46139 -18.45569,-18.46139 z m 131.78525,83.88906 c -8.82832,5.09898 -11.85536,16.38129 -6.75557,25.20475 5.0933,8.82834 16.37561,11.85455 25.20393,6.75556 8.82752,-5.0933 11.85457,-16.37561 6.75559,-25.20475 -5.0933,-8.82263 -16.38212,-11.84887 -25.20395,-6.75556 z m -91.69319,-65.42767 c 0,-18.24028 9.05999,-34.35498 22.92309,-44.11321 l -13.49406,-22.6012 c -16.14883,10.79461 -28.16435,27.28971 -33.15766,46.60863 5.82648,4.75434 9.55584,11.99194 9.55584,20.10497 0,8.10813 -3.72936,15.34572 -9.55584,20.10006 4.98681,19.32463 17.00232,35.82055 33.15766,46.61514 l 13.49406,-22.60689 c -13.8631,-9.75253 -22.92309,-25.86641 -22.92309,-44.1075 z m 53.91207,-53.91776 c 28.16514,0 51.27274,21.59491 53.69828,49.13495 l 26.30048,-0.38772 c -1.29405,-20.33173 -10.17604,-38.58421 -23.82372,-52.00594 -7.01814,2.65149 -15.12464,2.2459 -22.13139,-1.79314 -7.01894,-4.0504 -11.41969,-10.88319 -12.62271,-18.30044 -6.8214,-1.88254 -14.00046,-2.91323 -21.42257,-2.91323 -12.75925,0 -24.82271,2.99695 -35.53359,8.29836 l 12.82509,22.98243 c 6.89944,-3.21075 14.5963,-5.01526 22.71013,-5.01526 z m 0,107.82982 c -8.11383,0 -15.81069,-1.80533 -22.70931,-5.01528 l -12.82511,22.98163 c 10.71088,5.3079 22.77434,8.30568 35.53442,8.30568 7.42211,0 14.60118,-1.0315 21.42177,-2.91975 1.20301,-7.41724 5.60537,-14.24352 12.6227,-18.30125 7.01161,-4.04555 15.11326,-4.44382 22.13137,-1.79232 13.64769,-13.42174 22.52968,-31.67421 23.82372,-52.00595 l -26.30696,-0.38772 c -2.41986,27.54738 -25.52665,49.13496 -53.6926,49.13496 z m 37.77461,-119.35113 c 8.82834,5.09818 20.11145,2.07845 25.20475,-6.74987 5.099,-8.82833 2.07928,-20.11145 -6.74988,-25.21126 -8.82832,-5.0933 -20.11063,-2.06705 -25.21125,6.76127 -5.09247,8.82345 -2.06625,20.10657 6.75638,25.19986 z"
322− inkscape:connector-curvature="0"
323− inkscape:transform-center-x="16.232534"
324− inkscape:transform-center-y="-0.0024900663" />
325− </g>
326−</svg>
data/unity8-dash.url-dispatcherdeleted−6
@@ -1,6 +0,0 @@
1−[
2− {
3− "protocol": "scope"
4− }
5−]
6−
data/unity8-greeter-wrapper+7−4
@@ -49,9 +49,12 @@ if which ubuntu-touch-session >/dev/null; then
49 TOUCH_WRAPPER=ubuntu-touch-session
49 TOUCH_WRAPPER=ubuntu-touch-session
50fi
50fi
51
51
52−# Pretend that we're in an ubuntu-touch session, so that indicator-network
52+# Advertise as a unity8 session, so that indicator-network will start.
53−# will start.
53+export XDG_SESSION_DESKTOP=unity8
54−export DESKTOP_SESSION=ubuntu-touch
54+export DESKTOP_SESSION=$XDG_SESSION_DESKTOP
55+
56+set_greeter_var QT_IM_MODULE maliitphablet
57+set_greeter_var QT_QPA_PLATFORM ubuntumirclient
55
58
56# We disable ofono using pulse. It causes problems with racing with the user's
59# We disable ofono using pulse. It causes problems with racing with the user's
57# pulse. We need to come up with a better long-term fix for this, because we
60# pulse. We need to come up with a better long-term fix for this, because we
@@ -100,7 +103,7 @@ if [ "$(id -u phablet 2>/dev/null)" = "32011" ]; then
100fi
103fi
101
104
102# And finally actually start the greeter
105# And finally actually start the greeter
103−exec env QT_QPA_PLATFORM=ubuntumirclient MIR_SERVER_FILE=$SUB_SOCKET $TOUCH_WRAPPER $@ &
106+exec env MIR_SERVER_FILE=$SUB_SOCKET $TOUCH_WRAPPER $@ &
104CMD_PID=$!
107CMD_PID=$!
105wait $CMD_PID
108wait $CMD_PID
106CMD_PID=
109CMD_PID=
data/unity8.conf+39−29
@@ -2,37 +2,51 @@ description "Unity Shell v8"
2author "Ricardo Mendoza <ricmm@ubuntu.com>"
2author "Ricardo Mendoza <ricmm@ubuntu.com>"
3
3
4emits indicator-services-start
4emits indicator-services-start
5+emits indicator-services-end
5
6
6start on ((xsession SESSION=ubuntu-touch) or (xsession SESSION=unity8-mir) or (xsession SESSION=unity8)) and started dbus
7start on ((xsession SESSION=ubuntu-touch) or (xsession SESSION=unity8-mir) or (xsession SESSION=unity8)) and started dbus
7stop on desktop-end
8stop on desktop-end
8
9
9kill timeout 120
10kill timeout 120
10
11
12+#
13+# Ensure the shell always gets unthrottled touch events, so that applications
14+# who want full speed low-latency input can get it (LP: #1497105) and so that
15+# apps can use QML touch compression safely (the QML touch compression
16+# algorithm does not support nesting well - LP: #1486341, LP: #1556763 - so
17+# must be fed by the raw input event stream from Unity8).
18+#
19+env QML_NO_TOUCH_COMPRESSION=1
20+
11respawn
21respawn
12expect stop
22expect stop
13
23
24+# FIXME Create an autopilot test that makes sure start+stop and restart
25+# work fine at least 2 times
26+
14pre-start script
27pre-start script
15 # Tell unity-mir to raise SIGSTOP after we start
28 # Tell unity-mir to raise SIGSTOP after we start
16 initctl set-env UNITY_MIR_EMITS_SIGSTOP=1
29 initctl set-env UNITY_MIR_EMITS_SIGSTOP=1
17
30
18− if [ -n "$MIR_SOCKET" ]; then
19− # FIXME Create an autopilot test that makes sure start+stop and restart
20− # work fine at least 2 times
21− # Save original value of MIR_SOCKET in case we are stopped,
31+ # Save original value of MIR_SOCKET so that we can restore it when stopped,
22− # as we modify the variable for future jobs, including ourself.
32+ # as we modify the variable for future jobs, including ourself.
23− UNITY_MIR_SOCKET=$(initctl get-env --global MIR_SOCKET)
24− initctl set-env --global UNITY_MIR_SOCKET=$UNITY_MIR_SOCKET
33+ initctl set-env UNITY_MIR_SOCKET=$MIR_SOCKET
25
34
26− # Point unity8 at unity-system-compositor
35+ # Decide where we are going to put our own socket file
36+ if [ -n "$SNAP" ]; then
37+ # In snappy, we want to hard code our path because our apparmor profile
38+ # only lets us put the socket in one place. And consumers expect it
39+ # there.
40+ MIR_SERVER_FILE=/run/user/$(id -u)/mir_socket
41+ else
27 MIR_SERVER_FILE=$XDG_RUNTIME_DIR/mir_socket
42 MIR_SERVER_FILE=$XDG_RUNTIME_DIR/mir_socket
28− initctl set-env MIR_SERVER_FILE=$MIR_SERVER_FILE
29− initctl set-env MIR_SERVER_HOST_SOCKET=$UNITY_MIR_SOCKET
30−
31− # Point all future jobs in this session to our Mir socket instead of
32− # unity-system-compositor's socket.
33− initctl set-env --global MIR_SOCKET=$MIR_SERVER_FILE
34− gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "@a{ss} {'MIR_SOCKET': '$MIR_SERVER_FILE'}"
35 fi
43 fi
44+ initctl set-env MIR_SERVER_FILE=$MIR_SERVER_FILE
45+
46+ # Point all future jobs in this session to our Mir socket instead of
47+ # unity-system-compositor's socket.
48+ initctl set-env --global MIR_SOCKET=$MIR_SERVER_FILE
49+ gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "@a{ss} {'MIR_SOCKET': '$MIR_SERVER_FILE'}"
36
50
37 # Remove the normal and trusted sockets if still there
51 # Remove the normal and trusted sockets if still there
38 if [ -S "$MIR_SERVER_FILE" ]; then
52 if [ -S "$MIR_SERVER_FILE" ]; then
@@ -51,6 +65,9 @@ pre-start script
51 # reduces lag seen in apps:
65 # reduces lag seen in apps:
52 initctl set-env --global QML_NO_TOUCH_COMPRESSION=1
66 initctl set-env --global QML_NO_TOUCH_COMPRESSION=1
53
67
68+ # Workaround for text not rendering correctly
69+ initctl set-env --global QT_ENABLE_GLYPH_CACHE_WORKAROUND=1
70+
54 if [ "$XDG_SESSION_DESKTOP" = "ubuntu-touch" ]; then
71 if [ "$XDG_SESSION_DESKTOP" = "ubuntu-touch" ]; then
55 # On Ubuntu Touch, we currently use a trick where we auto-login the
72 # On Ubuntu Touch, we currently use a trick where we auto-login the
56 # user without a LightDM greeter at all. In this case, we want to
73 # user without a LightDM greeter at all. In this case, we want to
@@ -60,30 +77,23 @@ pre-start script
60 else
77 else
61 initctl set-env MODE=full-shell
78 initctl set-env MODE=full-shell
62 fi
79 fi
80+
81+ # HACK! WORKAROUND FOR HALIUM MISSING UBUNTU_PLATFORM_API
82+ if [ ! -f /system/lib/libubuntu_application_api.so ]; then
83+ initctl set-env --global UBUNTU_PLATFORM_API_BACKEND=test.so.3
84+ initctl set-env --global UBUNTU_PLATFORM_API_SENSOR_TEST=/etc/ubuntusensors/test.sensors
85+ fi
63end script
86end script
64
87
65exec ${BINARY:-unity8} --mode=$MODE $ARGS
88exec ${BINARY:-unity8} --mode=$MODE $ARGS
66
89
67post-start script
90post-start script
68− # If no MIR_SOCKET was defined on launch, we try to set one here
69− if [ -z "$(initctl get-env --global UNITY_MIR_SOCKET)" ]; then
70− if [ -S "$XDG_RUNTIME_DIR/mir_socket" ]; then
71− mir_socket=$XDG_RUNTIME_DIR/mir_socket
72− elif [ -S "/tmp/mir_socket" ]; then
73− mir_socket=/tmp/mir_socket
74− fi
75−
76− if [ -n "$mir_socket" ]; then
77− initctl set-env --global MIR_SOCKET="$mir_socket"
78− fi
79− fi
80−
81 initctl emit --no-wait indicator-services-start
91 initctl emit --no-wait indicator-services-start
82end script
92end script
83
93
84post-stop script
94post-stop script
85 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
95 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
86− initctl unset-env --global UNITY_MIR_SOCKET
87 initctl unset-env --global MIR_SERVER_PROMPT_FILE
96 initctl unset-env --global MIR_SERVER_PROMPT_FILE
88 initctl emit application-end
97 initctl emit application-end
98+ initctl emit indicator-services-end
89end script
99end script
debian/changelog+479−20
@@ -1,3 +1,457 @@
1+unity8 (8.20+ubports) xenial; urgency=medium
2+
3+ * Replace dependency on ubuntu-wallpapers with ubports-wallpapers
4+
5+ -- Dalton Durst <dalton@ubports.com> Fri, 5 Oct 2018 13:28:20 -0500
6+
7+unity8 (8.17+ubports3) xenial; urgency=medium
8+
9+ * Imported to UBports
10+
11+ -- UBports auto importer <infra@ubports.com> Mon, 31 Jul 2017 03:12:43 +0200
12+
13+unity8 (8.15+17.04.20170404.7-0ubuntu2) zesty; urgency=medium
14+
15+ [ Michael Zanetti ]
16+ * Make the default store uri point to gnome software center
17+
18+ -- Timo Jyrinki <timo-jyrinki@ubuntu.com> Thu, 06 Apr 2017 10:49:29 +0300
19+
20+unity8 (8.15+17.04.20170404.7-0ubuntu1) zesty; urgency=medium
21+
22+ [ Albert Astals Cid ]
23+ * Tweak app drawer a bit (LP: #1667570)
24+
25+ [ Josh Arenson ]
26+ * Make some small changes to the greeter's session list
27+ * Handle a user in the greeter having never selected a session better
28+ (LP: #1673199)
29+
30+ [ Lukáš Tinkl ]
31+ * Do not occlude windows with an ongoing unmaximize transition (LP:
32+ #1666363)
33+ * Show and implement close buttons for child windows (LP: #1668053)
34+ * Sanitize the logic for pointer vs. touch menus, fixes hiding the
35+ window title in staged mode, when hovering the panel with mouse and
36+ no menus available.
37+
38+ [ Michael Zanetti ]
39+ * Don't hide the launcher on super press if it's locked visible (LP:
40+ #1675373)
41+ * fix searchfield selection in drawer (LP: #1677259)
42+ * make sure the stage has focus when going to spread (LP: #1672053,
43+ #1672673, #1676995)
44+ * don't autostart the dash app, but show its .desktop file instead
45+
46+ -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 04 Apr 2017 22:56:06 +0000
47+
48+unity8 (8.15+17.04.20170328.3-0ubuntu1) zesty; urgency=medium
49+
50+ [ Albert Astals Cid ]
51+ * mock: Remove empty property and move invokable
52+ * Tune quicklist item selection
53+ * Fix unminimizing going to the wrong position the second time (LP:
54+ #1671458)
55+ * Remove unclickable gaps on the menubar (LP: #1672722)
56+ * indicators client is a tool
57+ * Make sure we destroy the popups when the item goes away (LP:
58+ #1670338)
59+ * UnityApplicationMocks: Initialize m_state and m_requestedState
60+ * Don't use appInfo after checking it if is null
61+ * Initialize m_privateMode
62+ * Hook up aboutToShow for overflow menus (LP: #1676016)
63+
64+ [ Daniel d'Andrada ]
65+ * Give active focus to child surface qml items (LP: #1671072)
66+ * Satellite child windows (LP: #1673415)
67+ * Tell qtmir/miral about the available desktop area
68+
69+ [ Florian Boucault ]
70+ * WindowStateStorage: use a private QThreadPool to ensure that
71+ WindowStateStorage::saveValue always has a thread available to
72+ execute the query in. (LP: #1675424)
73+
74+ [ Lukáš Tinkl ]
75+ * Protect against loading invalid window geometry (LP: #1674262)
76+ * Remove usage of the deprecated (and non-functional) system-image-
77+ dbus service.
78+ * Implement Ctrl+Alt+T to launch the terminal app (LP: #1673500)
79+ * Only take normal and dialog surface types into account for launcher
80+ items (LP: #1669047)
81+ * Fix the shutdown dialog after recent refactoring in
82+ DBusUnitySessionService (LP: #1676426)
83+
84+ [ Michael Zanetti ]
85+ * drop the grey background behind icons in the drawer (LP: #1675688)
86+
87+ [ Pete Woods ]
88+ * Fix Unity/Platform::isPC, and add tests (LP: #1670657)
89+
90+ -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 28 Mar 2017 21:50:20 +0000
91+
92+unity8 (8.15+17.04.20170321-0ubuntu1) zesty; urgency=medium
93+
94+ [ Albert Astals Cid ]
95+ * Signal aboutToShow (LP: #1664578)
96+ * Support Panel top level items to be disabled (LP: #1670694)
97+ * Improve Launcher ←→ touch menu interaction (LP: #1667620, #1671069)
98+ * Fix real world submenus (e.g. kate) not getting their first item
99+ selected on open (LP: #1666859)
100+
101+ [ Daniel d'Andrada ]
102+ * Really disable the window decoration when in staged mode (LP:
103+ #1670361)
104+ * Don't let clients resize their surfaces while in staged
105+ (phone/tablet) mode (LP: #1670390)
106+ * TopLevelWindowModel: don't put hidden windows in the model (LP:
107+ #1665286)
108+
109+ [ Lukáš Tinkl ]
110+ * Reset always-show-osk gsetting on startup to default value (false
111+ atm)
112+ * Fix the restored-to position with Miral due to the state changes
113+ being async (LP: #1669819)
114+ * Add a cursor name fallback mapping "grabbing" -> "closedhand" for
115+ Breeze
116+ * Fix unsnapping a maximized window from the panel (LP: #1671721)
117+ * Disable altDrag feature outside windowed mode
118+
119+ [ Michael Terry ]
120+ * Don't lock the screen for guest users or users in the nopasswdlogin
121+ group. (LP: #1644237)
122+ * Support GNOME-style logout DBus API (making the Logout, Reboot, and
123+ Shutdown launcher commands work) (LP: #1673229)
124+
125+ [ Michael Zanetti ]
126+ * Add support for closing apps from the spread with "Q" (LP: #1670327)
127+ * hide "private" launcher quicklist entries when the greeter is locked
128+ (LP: #1667649)
129+ * add surface titles to launcher's quicklist (LP: #1661668)
130+ * properly reset the drawer state when cancelling a horizontal drag
131+ (LP: #1669536)
132+ * fix the focus moving correctly between launcher and drawer (LP:
133+ #1669880)
134+ * unfocus the drawer's textfield when it is moved (LP: #1669839)
135+
136+ [ Pete Woods ]
137+ * Fix up indicators-client after refactoring (LP: #1672797)
138+
139+ -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 21 Mar 2017 10:58:13 +0000
140+
141+unity8 (8.15+17.04.20170308-0ubuntu1) zesty; urgency=medium
142+
143+ [ Albert Astals Cid ]
144+ * Let's follow the keyboard order
145+ * Enable the license check (LP: #1590044)
146+ * Show a barebones error message if loading the QML doesn't work
147+ * Make sure cursor is back to normal when closing the window (LP:
148+ #1666158)
149+ * Fix small warning when sensitive is undefined
150+ * Remove unused member variable
151+ * Initialize m_msecsSinceReference
152+ * Add warning if registerService fails
153+ * Move menus Component {} out of the repeater
154+ * Add tool to browse menus of a running app
155+
156+ [ Daniel d'Andrada ]
157+ * Drag child windows with Alt+LeftMouseButton (LP: #1664947)
158+
159+ [ Gerry Boland ]
160+ * TopLevelWindowModel: connect to the OSK surface to know when it goes
161+ away so we can clean up after it. (LP: #1670681)
162+
163+ [ Josh Arenson ]
164+ * Initialize sessionMode to single when mock is reset (LP: #1667463)
165+
166+ [ Lukáš Tinkl ]
167+ * Make the menu string "Back" translatable
168+ * Let the OSK be driven by a switch in indicator-keyboard (LP:
169+ #1521518)
170+ * Disable the tutorial when there's no touchscreen (LP: #1661557)
171+ * Shell chrome fixes (LP: #1658117, #1665723, #1665724, #1669010)
172+ * Cancel the drag as soon as we're entering the spread (LP: #1668642)
173+ * Fix being unable to use window control buttons or menus with touch
174+ (LP: #1667604, #1668387)
175+
176+ [ Michael Terry ]
177+ * Set QT_IM_MODULE in the greeter so that the OSK can come up. (LP:
178+ #1670383)
179+ * Simplify our startup handling of Mir environment variables and set
180+ the right socket path in snappy.
181+
182+ [ Michael Zanetti ]
183+ * Cancel the home key activation if something else is pressed along
184+ with Meta (LP: #1670678)
185+ * close the drawer when something is launched from the launcher (LP:
186+ #1660367)
187+ * use a svg instead of a png for the Launcher's BFB (LP: #1668057)
188+ * change the default setting for the launcher autohiding (LP:
189+ #1670655)
190+
191+ [ Nick Dedekind ]
192+ * Split up Shell::test_spreadDisabled to ensure initial state for each
193+ test.
194+
195+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 08 Mar 2017 09:56:46 +0000
196+
197+unity8 (8.15+17.04.20170221-0ubuntu1) zesty; urgency=medium
198+
199+ * Handle ubuntu-app-launch API/ABI break
200+
201+ -- Ken VanDine <ken.vandine@canonical.com> Tue, 21 Feb 2017 13:23:23 +0000
202+
203+unity8 (8.15+17.04.20170216.1-0ubuntu1) zesty; urgency=medium
204+
205+ [ Albert Astals Cid ]
206+ * Make menus items close the menu when clicking on them (LP: #1657082,
207+ #1657399)
208+ * Clicking on an open menu closes it + test
209+ * Make the menus and submenus do not go outside the screen when
210+ popping out
211+ * Auto open submenus on hover (LP: #1657085)
212+ * Make the double click on a menu not be maximize the window below
213+ (LP: #1657079)
214+ * Make the MenuBar InverseMouseArea eat hover events when there's a
215+ popup open (LP: #1657763)
216+ * Make double click on the window decoration maximize (LP: #1657758)
217+ * Eat hover events when the launcher quicklist is open (LP: #1640776)
218+ * Remove TODO now that we require Qt > 5.5
219+ * Serialize two make targets that want to cp to the same target file
220+ (LP: #1660576)
221+ * Remove variable that is always false (LP: #1585910)
222+
223+ [ Daniel d'Andrada ]
224+ * Consolidate launcher API checks
225+
226+ [ Lukáš Tinkl ]
227+ * Fixes for activating minimized windows and restoring fullscreen ones
228+ (LP: #1656808, #1658937, #1658938)
229+
230+ [ Michael Zanetti ]
231+ * move right edge push area up some levels so it can be used on top of
232+ the indicators
233+ * fix minimizing/restoring transitions (LP: #1658936)
234+ * fix some glitches with spread positioning
235+ * fix jumpy animation when closing an item from the spread
236+ * Show the wallpaper while the focused app is moving in staged mode
237+ (LP: #1648251)
238+ * Update the tutorial to mention the app drawer instead of the dash on
239+ long left edge swipe (LP: #1658932)
240+ * prevent hiding the launcher when the mouse hover event is eaten by
241+ the EdgePushArea (LP: #1657045)
242+ * properly invalidate the whole model (LP: #1658190)
243+ * stabilize OrientedShell test (LP: #1658994)
244+
245+ [ Nick Dedekind ]
246+ * Added overflow support to application menus.
247+ * Updated menu colors
248+
249+ -- Michał Sawicz <michal.sawicz@canonical.com> Thu, 16 Feb 2017 13:49:18 +0000
250+
251+unity8 (8.15+17.04.20170206-0ubuntu1) zesty; urgency=medium
252+
253+ [ Daniel d'Andrada ]
254+ * Initial support for child windows (menus, dialogs, tooltips) (LP:
255+ #1543467, #1591384, #1656727)
256+
257+ -- Albert Astals Cid <albert.astals@canonical.com> Mon, 06 Feb 2017 09:12:23 +0000
258+
259+unity8 (8.15+17.04.20170131.1-0ubuntu1) zesty; urgency=medium
260+
261+ [ Rodney Dawes ]
262+ * Remove dependencies on clickscope.
263+
264+ -- Albert Astals Cid <albert.astals@canonical.com> Tue, 31 Jan 2017 14:40:33 +0000
265+
266+unity8 (8.15+17.04.20170124-0ubuntu1) zesty; urgency=medium
267+
268+ [ Albert Astals Cid ]
269+ * Limit tab-focus travelling on dialogs with a fence
270+ * Restore focus to where it was when our ShellDialogs get unloaded
271+ * Update current session after changing the user
272+ * Add keyboard navigation for Indicators
273+ * a window -> the current window
274+ * There's no spreadDelegate_ anymore
275+
276+ [ Daniel d'Andrada ]
277+ * Simplify DecoratedWindow
278+ * Remove unnecessary warning message
279+
280+ [ Josh Arenson ]
281+ * Add a test for the session chooser icon in the greeter's sessions
282+ list
283+
284+ [ Lukáš Tinkl ]
285+ * Fix keymap not being applied on the shell itself (LP: #1626435)
286+ * Shell dialog improvements (kbd focus, mouse eater)
287+ * Start searching directly as you type, w/o having to first
288+ focus/click the search field.
289+ * Add a test for the real implementation of WindowStateStorage
290+ * Use a four finger gesture to open the drawer, much like in u7
291+
292+ [ Michael Terry ]
293+ * Simplify the lightdm mock to make future greeter improvements easier
294+ to test.
295+ * Add support for guest sessions in unity8-greeter.
296+ * Use a model for PAM prompts, supporting more possible interactions.
297+ * Fix grouping of autopkg output and allow optionally passing
298+ arguments to installed test scripts.
299+ * Add support for LightDM hints for manual logins and hiding normal
300+ users.
301+
302+ [ Michael Zanetti ]
303+ * hint the launcher to indicate a successful size change to the user
304+ (LP: #1646457)
305+ * Improvements for the appdrawer (LP: #1648173)
306+ * Adjust home key to still focus the dash instead of messing with the
307+ drawer
308+ * allow 4 finger simulation with mousetouchadaptor
309+
310+ [ Nick Dedekind ]
311+ * Added Alt+F10 shortcut to open app menus. (LP: #1656896)
312+ * Fixed menu layout width calculations. (LP: #1657050)
313+ * Skip Panel::test_drag_indicator_item_down_shows_menu
314+
315+ -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 24 Jan 2017 07:46:58 +0000
316+
317+unity8 (8.15+17.04.20170110.4-0ubuntu1) zesty; urgency=medium
318+
319+ [ Albert Astals Cid ]
320+ * Give focus to one of the buttons of the dialog
321+ * tst_WindowResizeArea: Use default values for mouseFlick speed and
322+ iterations (LP: #1651580)
323+ * Require Qt 5.6 & misc fixes
324+
325+ [ Daniel van Vugt ]
326+ * Deprecate usage of Mir's input resampling, instead opting for: (LP:
327+ #1497105, #1591328)
328+
329+ [ Josh Arenson ]
330+ * Allow the scopes list to automatically scroll when a scope is being
331+ dragged past the bounds of the screen. (LP: #1575319)
332+
333+ [ Lukáš Tinkl ]
334+ * Fix touch window controls being unreachable when the overlay is
335+ being displayed (LP: #1648167)
336+ * Fixup paths for window state storage in snappy environment
337+ * Add Unity.Platform mock for our tests (LP: #1655336)
338+
339+ [ Michael Zanetti ]
340+ * PreviewRatingInput: Use displayText instead of text to
341+ enable/disable the Send button (LP: #1595910)
342+ * Add a D-Bus interface to control some debug facilities on the fly
343+ * some launcher workarounds for the snapping
344+
345+ [ Michał Sawicz ]
346+ * Nuke leftover Platform in IndicatorsManager
347+
348+ [ Nick Dedekind ]
349+ * Added registry for application menus
350+
351+ [ Michael Terry, Nick Dedekind ]
352+ * Run the qmluitests.sh autopkg test against the installed package.
353+
354+ [ Michał Sawicz, Nick Dedekind ]
355+ * Application menus
356+
357+ [ Rodney Dawes ]
358+ * Remove the payments widget and dependency on libpay as no longer
359+ needed.
360+
361+ -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 10 Jan 2017 14:48:42 +0000
362+
363+unity8 (8.15+17.04.20161215-0ubuntu2~1) zesty; urgency=medium
364+
365+ * No change rebuild
366+
367+ -- Timo Jyrinki <timo-jyrinki@ubuntu.com> Wed, 04 Jan 2017 10:54:32 +0200
368+
369+unity8 (8.15+17.04.20161215-0ubuntu1) zesty; urgency=medium
370+
371+ * Let the model deal with some window management decisions (LP:
372+ #1346633)
373+
374+ -- Daniel d'Andrada <daniel.dandrada@canonical.com> Thu, 15 Dec 2016 16:17:29 +0000
375+
376+unity8 (8.15+17.04.20161207.1-0ubuntu1) zesty; urgency=medium
377+
378+ [ Albert Astals Cid ]
379+ * Add the Wsuggest-override flag to gcc
380+ * Add support for compiler sanitizers via ECM
381+ * Use timeStep as delay time (LP: #1642919)
382+ * Bring back fix for 1517830 (LP: #1517830)
383+ * Fix compile warnings in mocks
384+ * Do not hide panel when launching an application if the mouse is on
385+ the panel (LP: #1591311)
386+
387+ [ Daniel d'Andrada ]
388+ * Fix "make tryApplicationWindow"
389+ * Take save/restore functions out of WindowResizeArea
390+
391+ [ Josh Arenson ]
392+ * Enable the greeter to remember which session the user last logged
393+ into (LP: #1631365)
394+
395+ [ Lukáš Tinkl ]
396+ * Fix the Super key not invoking the dash scope home (LP: #1607427)
397+
398+ [ Michael Zanetti ]
399+ * Add the ApplicationDrawer
400+ * tune right edge push (LP: #1646094)
401+ * improve close button visiblity when hovering with the mouse
402+ * Update virtual touchpad visuals and add a tutorial. (LP: #1585220)
403+
404+ [ Pete Woods ]
405+ * MenuItemFactory: Add subtitle support to SwitchItem widget
406+
407+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 07 Dec 2016 13:49:24 +0000
408+
409+unity8 (8.15+17.04.20161129-0ubuntu1) zesty; urgency=medium
410+
411+ [ Albert Astals Cid ]
412+ * Fix PreviewProgress implicitHeight (LP: #1641943)
413+ * Adapt to dummy notification being gone
414+ * Fix autopilot test_lock_screen tests
415+ * Autopilot: Add more applications to the list for wider screens
416+ * Fix autopilot DashHelperTestCase.test_search
417+ * Also install the Screens mock
418+ * Give default value to gu-px size
419+
420+ [ Andrea Azzarone ]
421+ * Set Mir.cursorName to "grabbing" on first mouse press on a window
422+ decoration. Don't wait for press+motion. (LP: #1618078)
423+ * Do not show a divider between each quicklist entry but just between
424+ each section, similar to how unity7 does. (LP: #1637478)
425+
426+ [ Andrea Cimitan ]
427+ * Fix white text (LP: #1644468)
428+ * Make MascotLoader in CardCreator flat
429+ * Make the remaining UbuntuShape flat
430+ * added dropshadow from panel indicators to dash page header extra
431+ panel, so there is right shadow when the extra panel is narrow
432+ * Look up for expandable template dash category flag
433+
434+ [ Brian Douglass ]
435+ * Added a setting to enable/disable the indicator dropdown menu.
436+
437+ [ Daniel d'Andrada ]
438+ * There's no need for WindowDecoration to access the appDelegate
439+
440+ [ Lukáš Tinkl ]
441+ * Enable brightness (laptop backlight) handling on desktop/laptop PCs
442+ (LP: #1595947)
443+
444+ [ Michael Zanetti ]
445+ * disable spread interaction while locked (LP: #1641578)
446+
447+ [ Andrea Azzarone, Michał Sawicz ]
448+ * Implement launcher tooltips.
449+
450+ [ Olivier Tilloy ]
451+ * Remove dependency on transitional package. (LP: #1583079)
452+
453+ -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 29 Nov 2016 09:43:33 +0000
454+
1unity8 (8.15+17.04.20161116.1-0ubuntu1) zesty; urgency=medium
455unity8 (8.15+17.04.20161116.1-0ubuntu1) zesty; urgency=medium
2
456
3 [ Marco Trevisan (Treviño) ]
457 [ Marco Trevisan (Treviño) ]
@@ -770,7 +1224,7 @@ unity8 (8.12+16.04.20160330-0ubuntu1) xenial; urgency=medium
770unity8 (8.12+16.04.20160323.2-0ubuntu1) xenial; urgency=medium
1224unity8 (8.12+16.04.20160323.2-0ubuntu1) xenial; urgency=medium
771
1225
772 [ Michael Terry ]
1226 [ Michael Terry ]
773− *
1227+ *
774 * Fix setting OSK layout in OOBE wizard
1228 * Fix setting OSK layout in OOBE wizard
775 * Stop the left edge tutorial from being shown when the bottom edge
1229 * Stop the left edge tutorial from being shown when the bottom edge
776 tutorial wants to be shown after receiving a call.
1230 tutorial wants to be shown after receiving a call.
@@ -3482,7 +3936,7 @@ unity8 (8.00+14.10.20140729-0ubuntu1) utopic; urgency=medium
3482 * Re-architecture unity8 to use the QtMirCompositor library so that
3936 * Re-architecture unity8 to use the QtMirCompositor library so that
3483 the Qt scenegraph renderer is used as the Mir compositor, and
3937 the Qt scenegraph renderer is used as the Mir compositor, and
3484 application surfaces are added to the QML scene as items.
3938 application surfaces are added to the QML scene as items.
3485−
3939+
3486 [ Michael Zanetti ]
3940 [ Michael Zanetti ]
3487 * Port phone right-edge spread code to use QtCompositor
3941 * Port phone right-edge spread code to use QtCompositor
3488 * Add right-edge spread animation for tablet
3942 * Add right-edge spread animation for tablet
@@ -3756,8 +4210,8 @@ unity8 (7.90+14.10.20140703.1-0ubuntu1) utopic; urgency=low
3756
4210
3757unity8 (7.90+14.10.20140701.2-0ubuntu2) utopic; urgency=medium
4211unity8 (7.90+14.10.20140701.2-0ubuntu2) utopic; urgency=medium
3758
4212
3759− * debian/control:
4213+ * debian/control:
3760− list qtdeclarative5-ubuntu-ui-toolkit-plugin-gles as an alternative
4214+ list qtdeclarative5-ubuntu-ui-toolkit-plugin-gles as an alternative
3761 choice, since provides are not versionned, should restore installability
4215 choice, since provides are not versionned, should restore installability
3762 on amd64 and i386
4216 on amd64 and i386
3763
4217
@@ -6859,7 +7313,7 @@ unity8 (7.81.2) saucy; urgency=low
6859 [ Michał Sawicz ]
7313 [ Michał Sawicz ]
6860 * Fix icons in ApplicationsFilterGrid.qml.
7314 * Fix icons in ApplicationsFilterGrid.qml.
6861 * Support flipped image in run_on_device.
7315 * Support flipped image in run_on_device.
6862−
7316+
6863 [ Michael Zanetti ]
7317 [ Michael Zanetti ]
6864 * Don't clear lockscreen when it's fading out.
7318 * Don't clear lockscreen when it's fading out.
6865 * Reworked Launcher folding.
7319 * Reworked Launcher folding.
@@ -6944,7 +7398,7 @@ qml-phone-shell (1.79) raring; urgency=low
6944
7398
6945 [ Michael Terry ]
7399 [ Michael Terry ]
6946 * Make greeter look like the desktop one
7400 * Make greeter look like the desktop one
6947−
7401+
6948 [ Gerry Boland ]
7402 [ Gerry Boland ]
6949 * Fix a bug where a minimized app gets focus after closing another app
7403 * Fix a bug where a minimized app gets focus after closing another app
6950 * Applied a workaround to reduce flickering when launching apps
7404 * Applied a workaround to reduce flickering when launching apps
@@ -6960,7 +7414,7 @@ qml-phone-shell (1.79) raring; urgency=low
6960qml-phone-shell (1.78) raring; urgency=low
7414qml-phone-shell (1.78) raring; urgency=low
6961
7415
6962 [ Sergio Schvezov ]
7416 [ Sergio Schvezov ]
6963− * Replacing the music and ski safari mock apps with calendar and weather in
7417+ * Replacing the music and ski safari mock apps with calendar and weather in
6964 the launcher (LP: #1178262).
7418 the launcher (LP: #1178262).
6965
7419
6966 [ Daniel d'Andrada ]
7420 [ Daniel d'Andrada ]
@@ -7002,7 +7456,7 @@ qml-phone-shell (1.77) raring; urgency=low
7002 [ Mathieu Trudel-Lapierre ]
7456 [ Mathieu Trudel-Lapierre ]
7003 * debian/control:
7457 * debian/control:
7004 - Drop indicators-client-plugin* Depends for qml-phone-shell to Recommends.
7458 - Drop indicators-client-plugin* Depends for qml-phone-shell to Recommends.
7005− - Add/update Vcs-Browser, Vcs-Bzr and add a notice to uploaders.
7459+ - Add/update Vcs-Browser, Vcs-Bzr and add a notice to uploaders.
7006 * debian/copyright: fix syntax.
7460 * debian/copyright: fix syntax.
7007
7461
7008 [ Michał Sawicz ]
7462 [ Michał Sawicz ]
@@ -7012,7 +7466,7 @@ qml-phone-shell (1.77) raring; urgency=low
7012 * Renamed Media player files to match with new application name.
7466 * Renamed Media player files to match with new application name.
7013
7467
7014 [ Daniel d'Andrada ]
7468 [ Daniel d'Andrada ]
7015− * New: DirectionalDragArea component. An area that detects axis-aligned
7469+ * New: DirectionalDragArea component. An area that detects axis-aligned
7016 single-finger drag gestures.
7470 single-finger drag gestures.
7017 * Make it possible to send touch events from within QML test code.
7471 * Make it possible to send touch events from within QML test code.
7018
7472
@@ -7449,7 +7903,7 @@ qml-phone-shell (1.39) quantal; urgency=low
7449
7903
7450qml-phone-shell (1.38) quantal; urgency=low
7904qml-phone-shell (1.38) quantal; urgency=low
7451
7905
7452− * fix launching of notepad
7906+ * fix launching of notepad
7453
7907
7454 -- Bill Filler <bill.filler@canonical.com> Fri, 01 Feb 2013 03:21:29 -0500
7908 -- Bill Filler <bill.filler@canonical.com> Fri, 01 Feb 2013 03:21:29 -0500
7455
7909
@@ -7462,14 +7916,14 @@ qml-phone-shell (1.37) quantal; urgency=low
7462
7916
7463qml-phone-shell (1.36) quantal; urgency=low
7917qml-phone-shell (1.36) quantal; urgency=low
7464
7918
7465− * launch real notepad app
7919+ * launch real notepad app
7466 * fix launching of mock apps
7920 * fix launching of mock apps
7467
7921
7468 -- Bill Filler <bill.filler@canonical.com> Thu, 31 Jan 2013 21:36:05 -0500
7922 -- Bill Filler <bill.filler@canonical.com> Thu, 31 Jan 2013 21:36:05 -0500
7469
7923
7470qml-phone-shell (1.35) quantal; urgency=low
7924qml-phone-shell (1.35) quantal; urgency=low
7471
7925
7472− * integrate ubuntu-browser instead of snowshoe
7926+ * integrate ubuntu-browser instead of snowshoe
7473
7927
7474 -- Bill Filler <bill.filler@canonical.com> Thu, 31 Jan 2013 17:33:37 -0500
7928 -- Bill Filler <bill.filler@canonical.com> Thu, 31 Jan 2013 17:33:37 -0500
7475
7929
@@ -7553,7 +8007,7 @@ qml-phone-shell (1.22) quantal; urgency=low
7553
8007
7554qml-phone-shell (1.21) quantal; urgency=low
8008qml-phone-shell (1.21) quantal; urgency=low
7555
8009
7556− * New release with fullscreen launcher fixes
8010+ * New release with fullscreen launcher fixes
7557
8011
7558 -- Bill Filler <bill.filler@canonical.com> Fri, 07 Dec 2012 09:36:37 +0000
8012 -- Bill Filler <bill.filler@canonical.com> Fri, 07 Dec 2012 09:36:37 +0000
7559
8013
@@ -7565,7 +8019,7 @@ qml-phone-shell (1.20) quantal; urgency=low
7565
8019
7566qml-phone-shell (1.19) quantal; urgency=low
8020qml-phone-shell (1.19) quantal; urgency=low
7567
8021
7568− * enable multi-threaded render for apps
8022+ * enable multi-threaded render for apps
7569
8023
7570 -- Bill Filler <bill.filler@canonical.com> Wed, 05 Dec 2012 17:34:09 +0000
8024 -- Bill Filler <bill.filler@canonical.com> Wed, 05 Dec 2012 17:34:09 +0000
7571
8025
@@ -7573,7 +8027,7 @@ qml-phone-shell (1.18) quantal; urgency=low
7573
8027
7574 [ Bill Filler ]
8028 [ Bill Filler ]
7575 * update launcher to use wk2-render (chromeless webkit) for facebook
8029 * update launcher to use wk2-render (chromeless webkit) for facebook
7576− and twitter
8030+ and twitter
7577
8031
7578 -- Florian Boucault <florian.boucault@canonical.com> Wed, 05 Dec 2012 12:20:50 +0000
8032 -- Florian Boucault <florian.boucault@canonical.com> Wed, 05 Dec 2012 12:20:50 +0000
7579
8033
@@ -7585,7 +8039,7 @@ qml-phone-shell (1.17) quantal; urgency=low
7585
8039
7586qml-phone-shell (1.16) quantal; urgency=low
8040qml-phone-shell (1.16) quantal; urgency=low
7587
8041
7588− * fix to launch new gallery with correct args
8042+ * fix to launch new gallery with correct args
7589
8043
7590 -- Bill Filler <bill.filler@canonical.com> Thu, 29 Nov 2012 17:04:36 -0500
8044 -- Bill Filler <bill.filler@canonical.com> Thu, 29 Nov 2012 17:04:36 -0500
7591
8045
@@ -7647,7 +8101,7 @@ qml-phone-shell (1.2) quantal; urgency=low
7647
8101
7648qml-phone-shell (1.1) quantal; urgency=low
8102qml-phone-shell (1.1) quantal; urgency=low
7649
8103
7650− * comment out console.log() to prevent crash
8104+ * comment out console.log() to prevent crash
7651
8105
7652 -- Bill Filler <bill.filler@canonical.com> Sun, 28 Oct 2012 22:18:36 +0100
8106 -- Bill Filler <bill.filler@canonical.com> Sun, 28 Oct 2012 22:18:36 +0100
7653
8107
@@ -7675,19 +8129,19 @@ qml-phone-shell (0.7) quantal; urgency=low
7675 * qml-phone-shell.conf: better setup of env vars and launch
8129 * qml-phone-shell.conf: better setup of env vars and launch
7676 via dbus-luanch to properly setup session bus
8130 via dbus-luanch to properly setup session bus
7677 * shellapplication.cpp: don't setup custom env before launching
8131 * shellapplication.cpp: don't setup custom env before launching
7678− processes
8132+ processes
7679
8133
7680 -- Bill Filler <bill.filler@canonical.com> Sun, 21 Oct 2012 11:32:42 +0200
8134 -- Bill Filler <bill.filler@canonical.com> Sun, 21 Oct 2012 11:32:42 +0200
7681
8135
7682qml-phone-shell (0.6) quantal; urgency=low
8136qml-phone-shell (0.6) quantal; urgency=low
7683
8137
7684− * added additional Android env vars to upstart script
8138+ * added additional Android env vars to upstart script
7685
8139
7686 -- Bill Filler <bill.filler@canonical.com> Fri, 19 Oct 2012 09:35:25 -0400
8140 -- Bill Filler <bill.filler@canonical.com> Fri, 19 Oct 2012 09:35:25 -0400
7687
8141
7688qml-phone-shell (0.5) quantal; urgency=low
8142qml-phone-shell (0.5) quantal; urgency=low
7689
8143
7690− * added support to launch telephony-app
8144+ * added support to launch telephony-app
7691
8145
7692 -- Bill Filler <bill.filler@canonical.com> Thu, 18 Oct 2012 13:45:25 -0400
8146 -- Bill Filler <bill.filler@canonical.com> Thu, 18 Oct 2012 13:45:25 -0400
7693
8147
@@ -7714,3 +8168,8 @@ qml-phone-shell (0.1) quantal; urgency=low
7714 * Initial release
8168 * Initial release
7715
8169
7716 -- Bill Filler <bill.filler@canonical.com> Wed, 10 Oct 2012 10:19:53 -0400
8170 -- Bill Filler <bill.filler@canonical.com> Wed, 10 Oct 2012 10:19:53 -0400
8171+unity8 (8.15+17.04.20170404.7-0ubuntu2) zesty; urgency=medium
8172+
8173+ [ Michael Zanetti ]
8174+ * Make the default store uri point to gnome software center
8175+
debian/control+219−188
@@ -2,240 +2,271 @@ Source: unity8
2Section: x11
2Section: x11
3Priority: optional
3Priority: optional
4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
5−Build-Depends: cmake,
5+Build-Depends:
6+ android-headers,
7+ cmake,
8+ cmake-extras,
6− dbus-test-runner,
9+ dbus-test-runner,
7− debhelper (>= 9),
10+ debhelper (>= 9),
8− dh-apparmor,
11+ dh-apparmor,
9− doxyqml,
12+ doxyqml,
10−# To allow cross-compiling to work, we also must append :native
11−# to g++ so we don't try to run arm g++
12−# on an x86 CPU for example, when cross-compiling.
13− g++:native,
13+ g++:native,
14+ gdb,
14− graphviz,
15+ graphviz,
15− gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
16+ gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
16− libandroid-properties-dev,
17+ libandroid-properties-dev,
17− libconnectivity-qt1-dev (>= 0.7.1),
18+ libconnectivity-qt1-dev (>= 0.7.1),
18− libevdev-dev,
19+ libevdev-dev,
19− libgeonames-dev (>= 0.2),
20+ libgeonames-dev (>= 0.2),
20− libgnome-desktop-3-dev,
21− libgl1-mesa-dev[!arm64 !armhf] | libgl-dev[!arm64 !armhf],
21+ libgl1-mesa-dev[!arm64 !armhf] | libgl-dev[!arm64 !armhf],
22− libgl1-mesa-dri,
22+ libgl1-mesa-dri,
23− libgles2-mesa-dev[arm64 armhf],
23+ libgles2-mesa-dev[arm64 armhf],
24− libglib2.0-dev,
24+ libglib2.0-dev,
25+ libgnome-desktop-3-dev,
25− libgsettings-qt-dev,
26+ libgsettings-qt-dev,
26− libhardware-dev,
27+ libhardware-dev,
27− liblightdm-qt5-3-dev,
28+ liblightdm-qt5-3-dev,
28− libpam0g-dev,
29+ libpam0g-dev,
29− libpay2-dev,
30− libpulse-dev,
30+ libpulse-dev,
31− libqmenumodel-dev (>= 0.2.10),
31+ libqmenumodel-dev (>= 0.2.12),
32+ libqt5sql5-sqlite,
32− libqt5svg5-dev,
33+ libqt5svg5-dev,
33− libqt5xmlpatterns5-dev,
34+ libqt5xmlpatterns5-dev,
35+ libqtdbusmock1-dev (>= 0.7),
36+ libqtdbustest1-dev,
34− libsystemsettings-dev,
37+ libsystemsettings-dev,
35− libubuntu-app-launch2-dev,
38+ libubuntu-app-launch2-dev,
36− libubuntu-download-manager-common-dev,
39+ libubuntu-download-manager-common-dev,
37− libubuntugestures5-dev (>= 1.3.2030),
40+ libubuntugestures5-dev (>= 1.3.2030),
38− libubuntugestures5-private-dev (>= 1.3.2030),
41+ libubuntugestures5-private-dev (>= 1.3.2030),
39− libudev-dev,
42+ libudev-dev,
40− libudm-common-dev,
43+ libudm-common-dev,
41− libunity-api-dev (>= 7.119),
44+ libunity-api-dev (>= 8.7),
42− libusermetricsoutput1-dev,
45+ libusermetricsoutput1-dev,
43−# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
44− libx11-dev[!arm64 !armhf],
46+ libx11-dev[!arm64 !armhf],
45− libxcb1-dev[!arm64 !armhf],
47+ libxcb1-dev[!arm64 !armhf],
46− libxi-dev[!arm64 !armhf],
48+ libxi-dev[!arm64 !armhf],
47−# End of X11 libs
48− pkg-config,
49+ pkg-config,
49− python3-all:any,
50+ python3-all:any,
50− python3-setuptools,
51+ python3-setuptools,
51− qml-module-qt-labs-folderlistmodel,
52+ qml-module-qt-labs-folderlistmodel,
53+ qml-module-qt-labs-settings,
52− qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
54+ qml-module-qtmultimedia (>= 5.6),
55+ qml-module-qtqml-statemachine,
53− qml-module-qtquick-layouts,
56+ qml-module-qtquick-layouts,
54− qml-module-qtquick-xmllistmodel,
57+ qml-module-qtquick-xmllistmodel,
55− qml-module-qtquick2,
58+ qml-module-qtquick2,
56− qml-module-qtsysteminfo,
59+ qml-module-qtsysteminfo,
57− qml-module-qttest,
60+ qml-module-qttest,
58− qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
61+ qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
59− qml-module-ubuntu-web,
62+ qml-module-ubuntu-layouts,
60− qt5-default,
63+ qml-module-ubuntu-settings-components,
64+ qml-module-ubuntu-test,
61− qtbase5-dev (>= 5.4),
65+ qtbase5-dev (>= 5.6),
62− qtbase5-dev-tools,
66+ qtbase5-dev-tools,
63− qtbase5-private-dev (>= 5.4),
67+ qtbase5-private-dev (>= 5.6),
68+ qtdbustest-runner,
64− qtdeclarative5-dev (>= 5.4),
69+ qtdeclarative5-dev (>= 5.6),
65− qtdeclarative5-dev-tools,
70+ qtdeclarative5-dev-tools,
66− qtdeclarative5-private-dev (>= 5.4),
71+ qtdeclarative5-private-dev (>= 5.6),
67− qtdeclarative5-ubuntu-content1,
72+ qtdeclarative5-ubuntu-content1,
68− qtdeclarative5-ubuntu-settings-components (>= 0.11),
69− ttf-ubuntu-font-family,
73+ ttf-ubuntu-font-family,
74+ ubports-wallpapers,
75+ xvfb,
70Standards-Version: 3.9.4
76Standards-Version: 3.9.4
71−Homepage: http://launchpad.net/unity
77+Homepage: https://github.com/ubports/unity8
72−# If you aren't a member of ~unity-team but need to upload
78+Vcs-Git: https://github.com/ubports/unity8
73−# packaging changes, just go ahead. ~unity-team will notice
74−# and sync up the code again.
75−Vcs-Bzr: https://code.launchpad.net/unity8
76−Vcs-Browser: https://bazaar.launchpad.net/~unity-team/unity8/trunk
77XS-Testsuite: autopkgtest
79XS-Testsuite: autopkgtest
78X-Ubuntu-Use-Langpack: yes
80X-Ubuntu-Use-Langpack: yes
79
81
80Package: indicators-client
82Package: indicators-client
81Architecture: any
83Architecture: any
84+Depends:
82−Depends: qmenumodel-qml (>= 0.2.10),
85+ qmenumodel-qml (>= 0.2.11),
83− qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
86+ qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
84− unity8 (= ${binary:Version}),
87+ unity8 (= ${binary:Version}),
85− ${misc:Depends},
88+ ${misc:Depends},
86− ${shlibs:Depends},
89+ ${shlibs:Depends},
87Description: Indicators client test application
90Description: Indicators client test application
88 This package contains the client application for indicators used by autopilot
91 This package contains the client application for indicators used by autopilot
89
92
90Package: unity8-greeter
93Package: unity8-greeter
91Architecture: any
94Architecture: any
95+Depends:
96+ unity-system-compositor,
92−Depends: unity8 (= ${binary:Version}),
97+ unity8 (= ${binary:Version}),
93− unity-system-compositor,
94− ${misc:Depends},
98+ ${misc:Depends},
95− ${shlibs:Depends}
99+ ${shlibs:Depends},
96−Recommends: lightdm
100+Recommends:
101+ lightdm,
102+Provides:
97−Provides: lightdm-greeter
103+ lightdm-greeter,
98Description: The Unity8 Greeter
104Description: The Unity8 Greeter
99 The Unity 8 greeter is the primary login greeter for Ubuntu devices.
105 The Unity 8 greeter is the primary login greeter for Ubuntu devices.
100
106
101−Package: unity-scope-tool
102−Architecture: any
103−Depends: unity8-common (= ${source:Version}),
104− unity8-private (= ${binary:Version}),
105− ${misc:Depends},
106− ${shlibs:Depends},
107−# We do call pkg-config libunity-scopes from the code
108− pkg-config,
109− libunity-scopes-dev,
110−Description: Development tool for Unity scopes
111− This package contains the development tool for new generation Unity scopes, based
112− on Unity8.
113−
114Package: unity8
107Package: unity8
115Architecture: any
108Architecture: any
109+Provides:
116−Provides: indicator-renderer,
110+ indicator-renderer,
111+Depends:
117−Depends: dmz-cursor-theme,
112+ dmz-cursor-theme,
118− gsettings-desktop-schemas,
113+ gsettings-desktop-schemas,
119− libcap2-bin,
114+ libcap2-bin,
120− libglib2.0-bin,
115+ libglib2.0-bin,
121− qmenumodel-qml (>= 0.2.10),
116+ qmenumodel-qml (>= 0.2.11),
122− qml-module-biometryd,
117+ qml-module-biometryd,
123− qml-module-qt-labs-folderlistmodel,
118+ qml-module-qt-labs-folderlistmodel,
119+ qml-module-qt-labs-settings,
120+ qml-module-qtqml-statemachine,
124− qml-module-qtquick-xmllistmodel,
121+ qml-module-qtquick-xmllistmodel,
125− qml-module-qtsysteminfo,
122+ qml-module-qtsysteminfo,
126− qml-module-ubuntu-web,
127− qtdeclarative5-qtmir-plugin (>= 0.4.8),
123+ qtdeclarative5-qtmir-plugin (>= 0.4.8),
128− qtdeclarative5-ubuntu-telephony0.1,
124+ qtdeclarative5-ubuntu-telephony0.1,
129− qtdeclarative5-ubuntu-web-plugin,
130− ubuntu-system-settings (>= 0.4),
125+ ubuntu-system-settings (>= 0.4),
131− unity-launcher-impl-7,
126+ unity-launcher-impl-13,
132− unity8-common (= ${source:Version}),
127+ unity8-common (= ${source:Version}),
133− unity8-private (= ${binary:Version}),
128+ unity8-private (= ${binary:Version}),
134− unity8-private | unity-launcher-impl,
129+ unity8-private | unity-launcher-impl,
135− ${misc:Depends},
130+ ${misc:Depends},
136− ${shlibs:Depends},
131+ ${shlibs:Depends},
132+Recommends:
137−Recommends: indicator-keyboard,
133+ indicator-keyboard,
138− indicator-session,
134+ indicator-session,
139− unity-greeter-session-broadcast,
135+ unity-greeter-session-broadcast,
140− unity-scope-click,
136+Breaks:
141−Suggests: unity-scope-mediascanner2,
142−Breaks: indicator-network (<< 0.5.1+14.10.20141014),
137+ indicator-network (<< 0.5.1+14.10.20141014),
138+ ubuntu-keyboard (<< 0.100),
143− ubuntu-touch-session (<< 0.107),
139+ ubuntu-touch-session (<< 0.107),
140+ unity-scope-tool,
144− unity8-greeter (<< 7.89),
141+ unity8-greeter (<< 7.89),
145− ubuntu-keyboard (<< 0.100),
142+Replaces:
146−Replaces: ubuntu-touch-session (<< 0.82~),
143+ ubuntu-touch-session (<< 0.82~),
144+ unity-scope-tool,
147− unity8-greeter (<< 7.89),
145+ unity8-greeter (<< 7.89),
146+Conflicts:
148−Conflicts: ubuntu-system-settings-wizard,
147+ ubuntu-system-settings-wizard,
149Description: Unity 8 shell
148Description: Unity 8 shell
150 The Unity 8 shell is the primary user interface for Ubuntu devices.
149 The Unity 8 shell is the primary user interface for Ubuntu devices.
151
150
152Package: unity8-common
151Package: unity8-common
153Architecture: all
152Architecture: all
153+Depends:
154−Depends: qml-module-qtquick-layouts,
154+ qml-module-qtquick-layouts,
155− qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
155+ qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
156+ qml-module-ubuntu-settings-components,
156− qml-module-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
157+ qml-module-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
157− qtdeclarative5-ubuntu-settings-components (>= 0.11),
158− qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
158+ qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
159− qttranslations5-l10n,
159+ qttranslations5-l10n,
160− ubuntu-thumbnailer-impl-0,
160+ ubuntu-thumbnailer-impl-0,
161− ubuntu-wallpapers,
162− unity-application-impl-22,
161+ unity-application-impl-27,
163− unity-notifications-impl-3,
162+ unity-notifications-impl-3,
164− unity-plugin-scopes | unity-scopes-impl,
163+ unity8-schemas,
165− unity-scopes-impl-12,
166− unity8-fake-env | unity-application-impl,
164+ unity8-tests | unity-application-impl,
167− ${misc:Depends},
165+ ${misc:Depends},
166+Breaks:
168−Breaks: unity8 (<< 7.86),
167+ unity8 (<< 7.86),
168+Replaces:
169−Replaces: unity8 (<< 7.86),
169+ unity8 (<< 7.86),
170Description: Unity 8 shell (common files)
170Description: Unity 8 shell (common files)
171 The Unity 8 shell is the primary user interface for Ubuntu devices.
171 The Unity 8 shell is the primary user interface for Ubuntu devices.
172 .
172 .
173− This package contains the QML, graphics and locale files shared between
173+ This package contains the QML, graphics and locale files for unity8.
174− unity8 and unity-scope-tool.
175
174
176Package: unity8-autopilot
175Package: unity8-autopilot
177Architecture: all
176Architecture: all
177+Depends:
178−Depends: autopilot-qt5 (>= 1.4),
178+ autopilot-qt5 (>= 1.4),
179− gir1.2-glib-2.0,
179+ gir1.2-glib-2.0,
180− gir1.2-notify-0.7,
180+ gir1.2-notify-0.7,
181− libqt5test5,
181+ libqt5test5,
182− libqt5widgets5,
182+ libqt5widgets5,
183− ofono-phonesim,
183+ ofono-phonesim,
184− python3-autopilot,
184+ python3-autopilot,
185− python3-evdev,
185+ python3-evdev,
186− python3-fixtures,
186+ python3-fixtures,
187− python3-gi,
187+ python3-gi,
188− qttestability-autopilot (>= 1.4),
188+ qttestability-autopilot (>= 1.4),
189− ubuntu-ui-toolkit-autopilot (>= 1.3.2030),
189+ ubuntu-ui-toolkit-autopilot (>= 1.3.2030),
190− unity-scope-click,
191− unity8 (= ${source:Version}),
190+ unity8 (= ${source:Version}),
192− unity8-fake-env (= ${source:Version}),
191+ unity8-tests (= ${source:Version}),
193− url-dispatcher-tools,
192+ url-dispatcher-tools,
194− xvfb,
193+ xvfb,
195− ${misc:Depends},
194+ ${misc:Depends},
196− ${python3:Depends},
197− ${shlibs:Depends},
195+ ${python3:Depends},
198Description: Test package for Unity 8 shell
196Description: Test package for Unity 8 shell
199 Autopilot tests for the unity8 package
197 Autopilot tests for the unity8 package
200
198
201−Package: unity8-fake-env
199+Package: unity8-tests
202Architecture: any
200Architecture: any
203Multi-Arch: same
201Multi-Arch: same
202+Pre-Depends:
204−Pre-Depends: ${misc:Pre-Depends},
203+ ${misc:Pre-Depends},
204+Depends:
205+ dbus-test-runner,
206+ parallel,
207+ qt5-default,
208+ qtdbustest-runner,
209+ qtdeclarative5-dev-tools,
210+ unity8 (= ${source:Version}),
211+ xvfb,
205−Depends: ${misc:Depends},
212+ ${misc:Depends},
206− ${shlibs:Depends},
213+ ${shlibs:Depends},
214+Provides:
207−Provides: unity-application-impl,
215+ unity-application-impl,
208− unity-application-impl-22,
216+ unity-application-impl-27,
217+ unity8-fake-env,
218+Replaces:
209−Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
219+ unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1),
220+ unity8-fake-env,
221+Conflicts:
222+ unity8-fake-env,
210−Description: Fake environment for running Unity 8 shell
223+Description: Scripts and mocks for running Unity 8 shell tests
211 Provides fake implementations of some QML modules used by Unity 8 shell
224 Provides fake implementations of some QML modules used by Unity 8 shell
212− (e.g Ubuntu.Application) so that you can run it in a sandboxed environment.
225+ (e.g Ubuntu.Application) so that you can run it in a sandboxed environment
226+ and scripts to run its test suite.
213
227
214Package: unity8-private
228Package: unity8-private
215Architecture: any
229Architecture: any
216Multi-Arch: same
230Multi-Arch: same
231+Pre-Depends:
217−Pre-Depends: ${misc:Pre-Depends},
232+ ${misc:Pre-Depends},
233+Depends:
218−Depends: accountsservice-ubuntu-schemas (>= 0.0.7),
234+ accountsservice-ubuntu-schemas (>= 0.0.7),
219− gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
235+ gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
220− libhardware2,
236+ libhardware2,
221− pay-service,
222− unity-schemas (>= 7.3.1+14.10.20140915),
223− qtdeclarative5-gsettings1.0,
224− qml-module-qtmultimedia,
237+ qml-module-qtmultimedia,
238+ qtdeclarative5-gsettings1.0,
239+ unity8-schemas,
225− ${misc:Depends},
240+ ${misc:Depends},
226− ${shlibs:Depends},
241+ ${shlibs:Depends},
242+Provides:
227−Provides: unity-launcher-impl,
243+ unity-launcher-impl,
228− unity-launcher-impl-7,
244+ unity-launcher-impl-13,
229Description: Unity 8 private libs
245Description: Unity 8 private libs
230 The Unity 8 shell is the primary user interface for Ubuntu devices.
246 The Unity 8 shell is the primary user interface for Ubuntu devices.
231 .
247 .
232 This package contains the private libraries for QML and other components
248 This package contains the private libraries for QML and other components
233 only used by the shell.
249 only used by the shell.
234
250
251+Package: unity8-schemas
252+Architecture: all
253+Multi-Arch: foreign
254+Depends:
255+ ${misc:Depends},
256+Replaces:
257+ unity-schemas (>= 7.3.1+14.10.20140915),
258+Breaks:
259+ unity-schemas (>= 7.3.1+14.10.20140915),
260+Description: Unity 8 schemas
261+ The Unity 8 shell is the primary user interface for Ubuntu devices.
262+ .
263+ This package contains the configuration schemas used by unity.
264+
235Package: unity8-doc
265Package: unity8-doc
236Section: doc
266Section: doc
237Architecture: all
267Architecture: all
238Multi-Arch: foreign
268Multi-Arch: foreign
269+Depends:
239−Depends: ${misc:Depends},
270+ ${misc:Depends},
240Description: Documentation for Unity8
271Description: Documentation for Unity8
241 The Unity 8 shell is the primary user interface for Ubuntu devices. (documentation)
272 The Unity 8 shell is the primary user interface for Ubuntu devices. (documentation)
debian/rules+21−7
@@ -7,6 +7,12 @@ include /usr/share/dpkg/default.mk
7#export DH_VERBOSE=1
7#export DH_VERBOSE=1
8export DPKG_GENSYMBOLS_CHECK_LEVEL=4
8export DPKG_GENSYMBOLS_CHECK_LEVEL=4
9export NO_PNG_PKG_MANGLE=1
9export NO_PNG_PKG_MANGLE=1
10+export QT_SELECT=qt5
11+
12+DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH)
13+
14+# Skip tests on the archs they are known to be flaky with current configuration
15+testskip_architectures := arm64 powerpc ppc64el s390x
10
16
11%:
17%:
12 dh $@ --parallel --fail-missing --with python3
18 dh $@ --parallel --fail-missing --with python3
@@ -18,7 +24,19 @@ override_dh_auto_configure:
18
24
19override_dh_auto_build:
25override_dh_auto_build:
20 # doc is not a default target
26 # doc is not a default target
21− dh_auto_build -- all doc
27+ dh_auto_build -- -O all doc
28+
29+override_dh_auto_test:
30+ # Run the entire test battery
31+ # Parallel tests have a risk of starting xvfb on the same DISPLAY multiple times,
32+ # causing the whole battery to fail.
33+ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
34+ifneq (,$(filter $(DEB_HOST_ARCH),$(testskip_architectures)))
35+ -dh_auto_test --no-parallel -- -O -k xvfballtests
36+else
37+ dh_auto_test --no-parallel -- -O -k xvfballtests
38+endif
39+endif
22
40
23override_dh_install:
41override_dh_install:
24 # install autopilot tests
42 # install autopilot tests
@@ -29,15 +47,11 @@ override_dh_install:
29 cd $(CURDIR)
47 cd $(CURDIR)
30 dh_install -X'*.pyc' --fail-missing
48 dh_install -X'*.pyc' --fail-missing
31
49
32−override_dh_installdeb:
33− dh_apparmor --profile-name=usr.bin.unity8-dash -punity8-dash
34− dh_installdeb
35−
36# use private lib directories
50# use private lib directories
37override_dh_makeshlibs:
51override_dh_makeshlibs:
38− dh_makeshlibs -Nunity8-private -Nunity8-fake-env
52+ dh_makeshlibs -Nunity8-private -Nunity8-tests
39
53
40override_dh_shlibdeps:
54override_dh_shlibdeps:
41 # Some mock libraries link against liblightdm-qt5-3.so which we want to
55 # Some mock libraries link against liblightdm-qt5-3.so which we want to
42 # avoid, since we only really link against our mock one, not the system one.
56 # avoid, since we only really link against our mock one, not the system one.
43− dh_shlibdeps -XlibMockLightDM-qml.so -XlibMockAccountsService-qml.so
57+ dh_shlibdeps -XlibMockAccountsService-qml.so -Lunity8-private
debian/shlibs.localnew file+1
@@ -0,0 +1 @@
1+libunity8-private 0 unity8-private
debian/source/formatdeleted−1
@@ -1 +0,0 @@
1−1.0
debian/tests/control+5−4
@@ -1,10 +1,11 @@
1Tests: qmluitests.sh
1Tests: qmluitests.sh
2Restrictions: allow-stderr
2Restrictions: allow-stderr
3−Depends: @,
3+Depends:
4− @builddeps@,
4+ dpkg-dev,
5− xvfb,
5+ unity8-tests,
6
6
7Tests: autopilot.sh
7Tests: autopilot.sh
8Restrictions: allow-stderr,
8Restrictions: allow-stderr,
9 isolation-machine,
9 isolation-machine,
10+Depends:
10−Depends: unity8-autopilot,
11+ unity8-autopilot,
debian/tests/qmluitests.sh+4−14
@@ -1,21 +1,11 @@
1#!/bin/sh
1#!/bin/sh
2+# -*- Mode: sh; indent-tabs-mode: nil; tab-width: 4 -*-
2
3
3# log all commands and abort on error
4# log all commands and abort on error
4set -xe
5set -xe
5
6
6−SHELL_QML_PATH=$(pkg-config --variable=plugindir unity-shell-api)
7+DEB_HOST_MULTIARCH=$(dpkg-architecture -qDEB_HOST_MULTIARCH)
7−UNITY_SOURCE_DIR=$(readlink -f $(dirname $(readlink -f $0))/../..)
8
8
9−dh_auto_configure -- -DCMAKE_INSTALL_LOCALSTATEDIR="/var" \
10− -DARTIFACTS_DIR=${ADT_ARTIFACTS} \
9+export ARTIFACTS_DIR="${ADT_ARTIFACTS}"
11− -DUNITY_PLUGINPATH=${SHELL_QML_PATH} \
12− -DUNITY_MOCKPATH=${SHELL_QML_PATH}/mocks
13−dh_auto_build --parallel -- -C tests/mocks
14−dh_auto_build --parallel -- -C tests/plugins
15−dh_auto_build --parallel -- -C tests/qmltests
16−dh_auto_build --parallel -- -C tests/uqmlscene
17−dh_auto_build --parallel -- -C tests/utils
18
10
19−export UNITY_SOURCE_DIR
11+/usr/lib/$DEB_HOST_MULTIARCH/unity8/tests/scripts/xvfballtests.sh
20−
21−dh_auto_build --parallel -- -k xvfballtests
debian/unity-scope-tool.installdeleted−2
@@ -1,2 +0,0 @@
1−usr/bin/unity-scope-tool
2−usr/share/unity8/ScopeTool.qml
debian/unity8-common.install−2
@@ -1,7 +1,5 @@
1−usr/share/glib-2.0/schemas
2usr/share/locale
1usr/share/locale
3usr/share/unity8/Components
2usr/share/unity8/Components
4−usr/share/unity8/Dash
5usr/share/unity8/Notifications
3usr/share/unity8/Notifications
6usr/share/unity8/graphics
4usr/share/unity8/graphics
7var/lib/polkit-1/localauthority/10-vendor.d
5var/lib/polkit-1/localauthority/10-vendor.d
debian/unity8-greeter.install+1−1
@@ -1,5 +1,5 @@
1−data/unity8-greeter-wrapper usr/bin/
2data/unity8-greeter-init.conf usr/share/upstart/sessions/
1data/unity8-greeter-init.conf usr/share/upstart/sessions/
2+data/unity8-greeter-wrapper usr/bin/
3data/unity8-greeter.pkla /var/lib/polkit-1/localauthority/10-vendor.d/
3data/unity8-greeter.pkla /var/lib/polkit-1/localauthority/10-vendor.d/
4usr/share/lightdm/greeters/unity8-greeter.desktop
4usr/share/lightdm/greeters/unity8-greeter.desktop
5usr/share/lightdm/lightdm.conf.d/51-unity8-greeter.conf
5usr/share/lightdm/lightdm.conf.d/51-unity8-greeter.conf
debian/unity8-private.install+1−3
@@ -1,18 +1,16 @@
1usr/lib/*/libunity8-private.*
1usr/lib/*/libunity8-private.*
2usr/lib/*/unity8/qml/AccountsService
2usr/lib/*/unity8/qml/AccountsService
3usr/lib/*/unity8/qml/Cursor
3usr/lib/*/unity8/qml/Cursor
4−usr/lib/*/unity8/qml/Dash
5usr/lib/*/unity8/qml/GlobalShortcut
4usr/lib/*/unity8/qml/GlobalShortcut
6usr/lib/*/unity8/qml/Greeter
5usr/lib/*/unity8/qml/Greeter
7−usr/lib/*/unity8/qml/ImageCache
8usr/lib/*/unity8/qml/LightDM
6usr/lib/*/unity8/qml/LightDM
9usr/lib/*/unity8/qml/Lights
7usr/lib/*/unity8/qml/Lights
10usr/lib/*/unity8/qml/Powerd
8usr/lib/*/unity8/qml/Powerd
11usr/lib/*/unity8/qml/ScreenshotDirectory
9usr/lib/*/unity8/qml/ScreenshotDirectory
12usr/lib/*/unity8/qml/SessionBroadcast
10usr/lib/*/unity8/qml/SessionBroadcast
11+usr/lib/*/unity8/qml/UInput
13usr/lib/*/unity8/qml/Ubuntu
12usr/lib/*/unity8/qml/Ubuntu
14usr/lib/*/unity8/qml/Unity
13usr/lib/*/unity8/qml/Unity
15−usr/lib/*/unity8/qml/UInput
16usr/lib/*/unity8/qml/Utils
14usr/lib/*/unity8/qml/Utils
17usr/lib/*/unity8/qml/WindowManager
15usr/lib/*/unity8/qml/WindowManager
18usr/lib/*/unity8/qml/Wizard
16usr/lib/*/unity8/qml/Wizard
debian/unity8-schemas.installnew file+1
@@ -0,0 +1 @@
1+usr/share/glib-2.0/schemas
debian/unity8-fake-env.install → debian/unity8-tests.installrenamed+4−1
@@ -2,5 +2,8 @@ data/unity-mock-indicator-service.conf usr/share/upstart/sessions/
2usr/bin/unity-mock-indicator-service
2usr/bin/unity-mock-indicator-service
3usr/lib/*/unity8/qml/mocks
3usr/lib/*/unity8/qml/mocks
4usr/lib/*/unity8/qml/nonmirplugins
4usr/lib/*/unity8/qml/nonmirplugins
5−usr/lib/*/unity8/qml/scopefakes
5+usr/lib/*/unity8/qml/utils
6+usr/lib/*/unity8/tests
7+usr/lib/*/unity8/uqmlscene
6usr/share/unity8/mocks
8usr/share/unity8/mocks
9+usr/share/unity8/tests
debian/unity8-tests.lintian-overridesnew file+1
@@ -0,0 +1 @@
1+unity8-tests: unusual-interpreter usr/lib/*/unity8/tests/scripts/*.sh #!/usr/bin/parallel
debian/unity8.install+7−10
@@ -1,22 +1,19 @@
1−debian/usr.bin.unity8-dash etc/apparmor.d
1+data/devices.conf etc/ubuntu/
2−var/lib/unity8/version
2+data/test.sensors etc/ubuntusensors/
3−data/unity8-dash.conf usr/share/upstart/sessions/
4data/unity8-filewatcher.conf usr/share/upstart/sessions/
3data/unity8-filewatcher.conf usr/share/upstart/sessions/
5data/unity8.conf usr/share/upstart/sessions/
4data/unity8.conf usr/share/upstart/sessions/
6−data/devices.conf etc/ubuntu/
7usr/bin/unity8
5usr/bin/unity8
8−usr/bin/unity8-dash
9−usr/share/applications/unity8-dash.desktop
10usr/share/applications/unity8.desktop
6usr/share/applications/unity8.desktop
7+usr/share/unity8/ApplicationMenus
8+usr/share/unity8/DeviceConfiguration.qml
9+usr/share/unity8/DisabledScreenNotice.qml
11usr/share/unity8/Greeter
10usr/share/unity8/Greeter
12usr/share/unity8/Launcher
11usr/share/unity8/Launcher
12+usr/share/unity8/OrientedShell.qml
13usr/share/unity8/Panel
13usr/share/unity8/Panel
14usr/share/unity8/Rotation
14usr/share/unity8/Rotation
15−usr/share/unity8/DeviceConfiguration.qml
16−usr/share/unity8/OrientedShell.qml
17−usr/share/unity8/DisabledScreenNotice.qml
18usr/share/unity8/Shell.qml
15usr/share/unity8/Shell.qml
19usr/share/unity8/Stage
16usr/share/unity8/Stage
20usr/share/unity8/Tutorial
17usr/share/unity8/Tutorial
21usr/share/unity8/Wizard
18usr/share/unity8/Wizard
22−usr/share/url-dispatcher/urls/unity8-dash.url-dispatcher
19+var/lib/unity8/version
debian/usr.bin.unity8-dashdeleted−21
@@ -1,21 +0,0 @@
1−# vim:syntax=apparmor
2−
3−#include <tunables/global>
4−
5−# v2 compatible wildly permissive profile
6−profile unity8-dash /usr/bin/unity8-dash (attach_disconnected) {
7− capability,
8− network,
9− / rwkl,
10− /** rwlkm,
11− /** pix,
12−
13− mount,
14− remount,
15− umount,
16− pivot_root,
17− dbus,
18− signal,
19− ptrace,
20− unix,
21−}
include/paths.h.in+26−1
@@ -41,7 +41,10 @@ inline QString installRoot() {
41}
41}
42
42
43inline bool isRunningInstalled() {
43inline bool isRunningInstalled() {
44+ // Tests don't use the normal binary, so we want to check binary *and*
45+ // the environment. Installed tests have UNITY_TESTING_DATADIR set.
46+ static bool installed_test_env = !qgetenv("UNITY_TESTING_DATADIR").isEmpty();
44− return !installRoot().isNull();
47+ return !installRoot().isNull() || installed_test_env;
45}
48}
46
49
47inline QString buildDirectory() {
50inline QString buildDirectory() {
@@ -62,6 +65,28 @@ inline QString translationDirectory() {
62 }
65 }
63}
66}
64
67
68+inline QString testDataDir() {
69+ // Can't use isRunningInstalled, since tests don't use the normal unity8
70+ // executable. $UNITY_TESTING_DATADIR is set by installed tests.
71+ QString datadir(qgetenv("UNITY_TESTING_DATADIR"));
72+ if (datadir.isEmpty()) {
73+ return sourceDirectory() + "/tests";
74+ } else {
75+ return datadir + "/tests";
76+ }
77+}
78+
79+inline QString testLibDir() {
80+ // Can't use isRunningInstalled, since tests don't use the normal unity8
81+ // executable. $UNITY_TESTING_DATADIR is set by installed tests.
82+ QString libdir(qgetenv("UNITY_TESTING_LIBDIR"));
83+ if (libdir.isEmpty()) {
84+ return buildDirectory() + "/tests";
85+ } else {
86+ return libdir + "/tests";
87+ }
88+}
89+
65inline QString qmlDirectory() {
90inline QString qmlDirectory() {
66 if (isRunningInstalled()) {
91 if (isRunningInstalled()) {
67 return installRoot() + QStringLiteral("@CMAKE_INSTALL_PREFIX@/@SHELL_APP_DIR@/");
92 return installRoot() + QStringLiteral("@CMAKE_INSTALL_PREFIX@/@SHELL_APP_DIR@/");
plugins/AccountsService/AccountsService.cpp+1−30
@@ -25,7 +25,6 @@
25#include <glib.h>
25#include <glib.h>
26
26
27#define IFACE_ACCOUNTS_USER QStringLiteral("org.freedesktop.Accounts.User")
27#define IFACE_ACCOUNTS_USER QStringLiteral("org.freedesktop.Accounts.User")
28−#define IFACE_LOCATION_HERE QStringLiteral("com.ubuntu.location.providers.here.AccountsService")
29#define IFACE_UBUNTU_INPUT QStringLiteral("com.ubuntu.AccountsService.Input")
28#define IFACE_UBUNTU_INPUT QStringLiteral("com.ubuntu.AccountsService.Input")
30#define IFACE_UBUNTU_SECURITY QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy")
29#define IFACE_UBUNTU_SECURITY QStringLiteral("com.ubuntu.AccountsService.SecurityPrivacy")
31#define IFACE_UBUNTU_SECURITY_OLD QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy")
30#define IFACE_UBUNTU_SECURITY_OLD QStringLiteral("com.ubuntu.touch.AccountsService.SecurityPrivacy")
@@ -33,7 +32,7 @@
33#define IFACE_UNITY_PRIVATE QStringLiteral("com.canonical.unity.AccountsService.Private")
32#define IFACE_UNITY_PRIVATE QStringLiteral("com.canonical.unity.AccountsService.Private")
34
33
35#define PROP_BACKGROUND_FILE QStringLiteral("BackgroundFile")
34#define PROP_BACKGROUND_FILE QStringLiteral("BackgroundFile")
36−#define PROP_DEMO_EDGES QStringLiteral("demo-edges")
35+#define PROP_DEMO_EDGES QStringLiteral("DemoEdges2")
37#define PROP_DEMO_EDGES_COMPLETED QStringLiteral("DemoEdgesCompleted")
36#define PROP_DEMO_EDGES_COMPLETED QStringLiteral("DemoEdgesCompleted")
38#define PROP_EMAIL QStringLiteral("Email")
37#define PROP_EMAIL QStringLiteral("Email")
39#define PROP_ENABLE_FINGERPRINT_IDENTIFICATION QStringLiteral("EnableFingerprintIdentification")
38#define PROP_ENABLE_FINGERPRINT_IDENTIFICATION QStringLiteral("EnableFingerprintIdentification")
@@ -93,8 +92,6 @@ AccountsService::AccountsService(QObject* parent, const QString &user)
93 registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));
92 registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));
94 registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));
93 registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));
95 registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged"));
94 registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged"));
96− registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));
97− registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));
98 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_FINGERPRINT_IDENTIFICATION, QStringLiteral("enableFingerprintIdentificationChanged"));
95 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_FINGERPRINT_IDENTIFICATION, QStringLiteral("enableFingerprintIdentificationChanged"));
99 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
96 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
100 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged"));
97 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_INDICATORS_WHILE_LOCKED, QStringLiteral("enableIndicatorsWhileLockedChanged"));
@@ -216,32 +213,6 @@ AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() cons
216 return (PasswordDisplayHint)value.toInt();
213 return (PasswordDisplayHint)value.toInt();
217}
214}
218
215
219−bool AccountsService::hereEnabled() const
220−{
221− auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED);
222− return value.toBool();
223−}
224−
225−void AccountsService::setHereEnabled(bool enabled)
226−{
227− setProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, enabled);
228−}
229−
230−QString AccountsService::hereLicensePath() const
231−{
232− auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
233− QString hereLicensePath = value.toString();
234− if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
235− hereLicensePath = QStringLiteral("");
236− return hereLicensePath;
237−}
238−
239−bool AccountsService::hereLicensePathValid() const
240−{
241− auto value = getProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH);
242− return !value.toString().isNull();
243−}
244−
245QString AccountsService::realName() const
216QString AccountsService::realName() const
246{
217{
247 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME);
218 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME);
plugins/AccountsService/AccountsService.h+1−17
@@ -29,7 +29,6 @@ class QDBusInterface;
29class AccountsService: public QObject
29class AccountsService: public QObject
30{
30{
31 Q_OBJECT
31 Q_OBJECT
32− Q_ENUMS(PasswordDisplayHint)
33 Q_PROPERTY (QString user
32 Q_PROPERTY (QString user
34 READ user
33 READ user
35 WRITE setUser
34 WRITE setUser
@@ -67,16 +66,6 @@ class AccountsService: public QObject
67 READ failedFingerprintLogins
66 READ failedFingerprintLogins
68 WRITE setFailedFingerprintLogins
67 WRITE setFailedFingerprintLogins
69 NOTIFY failedFingerprintLoginsChanged)
68 NOTIFY failedFingerprintLoginsChanged)
70− Q_PROPERTY(bool hereEnabled
71− READ hereEnabled
72− WRITE setHereEnabled
73− NOTIFY hereEnabledChanged)
74− Q_PROPERTY(QString hereLicensePath
75− READ hereLicensePath
76− NOTIFY hereLicensePathChanged)
77− Q_PROPERTY(bool hereLicensePathValid // qml sees a null string as "", so we use proxy setting for that
78− READ hereLicensePathValid
79− NOTIFY hereLicensePathChanged)
80 Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged)
69 Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged)
81 Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)
70 Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)
82 Q_PROPERTY(QStringList keymaps READ keymaps WRITE setKeymaps NOTIFY keymapsChanged)
71 Q_PROPERTY(QStringList keymaps READ keymaps WRITE setKeymaps NOTIFY keymapsChanged)
@@ -86,6 +75,7 @@ public:
86 Keyboard,
75 Keyboard,
87 Numeric,
76 Numeric,
88 };
77 };
78+ Q_ENUM(PasswordDisplayHint)
89
79
90 explicit AccountsService(QObject *parent = 0, const QString & user = QString());
80 explicit AccountsService(QObject *parent = 0, const QString & user = QString());
91 ~AccountsService() = default;
81 ~AccountsService() = default;
@@ -106,10 +96,6 @@ public:
106 void setFailedLogins(uint failedLogins);
96 void setFailedLogins(uint failedLogins);
107 uint failedFingerprintLogins() const;
97 uint failedFingerprintLogins() const;
108 void setFailedFingerprintLogins(uint failedFingerprintLogins);
98 void setFailedFingerprintLogins(uint failedFingerprintLogins);
109− bool hereEnabled() const;
110− void setHereEnabled(bool enabled);
111− QString hereLicensePath() const;
112− bool hereLicensePathValid() const;
113 QString realName() const;
99 QString realName() const;
114 void setRealName(const QString &realName);
100 void setRealName(const QString &realName);
115 QString email() const;
101 QString email() const;
@@ -129,8 +115,6 @@ Q_SIGNALS:
129 void passwordDisplayHintChanged();
115 void passwordDisplayHintChanged();
130 void failedLoginsChanged();
116 void failedLoginsChanged();
131 void failedFingerprintLoginsChanged();
117 void failedFingerprintLoginsChanged();
132− void hereEnabledChanged();
133− void hereLicensePathChanged();
134 void realNameChanged();
118 void realNameChanged();
135 void emailChanged();
119 void emailChanged();
136 void keymapsChanged();
120 void keymapsChanged();
plugins/AccountsService/CMakeLists.txt+1−2
@@ -7,9 +7,8 @@ add_library(AccountsService-qml MODULE
7 plugin.cpp
7 plugin.cpp
8 )
8 )
9
9
10−qt5_use_modules(AccountsService-qml DBus Qml)
11−
12target_link_libraries(AccountsService-qml
10target_link_libraries(AccountsService-qml
11+ Qt5::DBus Qt5::Qml
13 ${GLIB_LIBRARIES}
12 ${GLIB_LIBRARIES}
14 )
13 )
15
14
plugins/AccountsService/com.canonical.unity.AccountsService.xml+6
@@ -9,11 +9,17 @@
9 <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
9 <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
10 value="com.ubuntu.AccountsService.GreeterChangeAny"/>
10 value="com.ubuntu.AccountsService.GreeterChangeAny"/>
11
11
12+ <!-- Marks that V1 (pre-8.21) edge demos have been run. Legacy value. -->
12 <!-- Should have been named DemoEdges, sorry folks. -mterry -->
13 <!-- Should have been named DemoEdges, sorry folks. -mterry -->
13 <property name="demo-edges" type="b" access="readwrite">
14 <property name="demo-edges" type="b" access="readwrite">
14 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
15 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
15 </property>
16 </property>
16
17
18+ <!-- Marks that V2 (post-8.21) edge demos have been run -->
19+ <property name="DemoEdges2" type="b" access="readwrite">
20+ <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
21+ </property>
22+
17 <!-- List of tutorial pages that have been completed by the user -->
23 <!-- List of tutorial pages that have been completed by the user -->
18 <property name="DemoEdgesCompleted" type="as" access="readwrite">
24 <property name="DemoEdgesCompleted" type="as" access="readwrite">
19 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[]"/>
25 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[]"/>
plugins/CMakeLists.txt+1−3
@@ -1,4 +1,4 @@
1−include(QmlPlugins)
1+find_package(QmlPlugins)
2
2
3# Set up and install a plugin for unity8.
3# Set up and install a plugin for unity8.
4#
4#
@@ -15,10 +15,8 @@ add_subdirectory(AccountsService)
15add_subdirectory(Cursor)
15add_subdirectory(Cursor)
16add_subdirectory(GlobalShortcut)
16add_subdirectory(GlobalShortcut)
17add_subdirectory(Greeter)
17add_subdirectory(Greeter)
18−add_subdirectory(ImageCache)
19add_subdirectory(LightDM)
18add_subdirectory(LightDM)
20add_subdirectory(Lights)
19add_subdirectory(Lights)
21−add_subdirectory(Dash)
22add_subdirectory(Powerd)
20add_subdirectory(Powerd)
23add_subdirectory(ScreenshotDirectory)
21add_subdirectory(ScreenshotDirectory)
24add_subdirectory(SessionBroadcast)
22add_subdirectory(SessionBroadcast)
plugins/Cursor/CMakeLists.txt+1−2
@@ -26,8 +26,7 @@ add_library(Cursor-qml SHARED
26target_link_libraries(Cursor-qml
26target_link_libraries(Cursor-qml
27 xcursorloader-static
27 xcursorloader-static
28 ${QT5PLATFORM_SUPPORT_LDFLAGS}
28 ${QT5PLATFORM_SUPPORT_LDFLAGS}
29+ Qt5::Qml Qt5::Quick Qt5::Svg
29)
30)
30
31
31−qt5_use_modules(Cursor-qml Qml Quick Svg)
32−
33add_unity8_plugin(Cursor 1.1 Cursor TARGETS Cursor-qml)
32add_unity8_plugin(Cursor 1.1 Cursor TARGETS Cursor-qml)
plugins/Cursor/CursorImageInfo.cpp+1−2
@@ -108,8 +108,7 @@ int CursorImageInfo::frameDuration() const
108QUrl CursorImageInfo::imageSource() const
108QUrl CursorImageInfo::imageSource() const
109{
109{
110 auto urlString = QString("image://cursor/%1/%2/%3")
110 auto urlString = QString("image://cursor/%1/%2/%3")
111− .arg(m_themeName)
111+ .arg(m_themeName, m_cursorName)
112− .arg(m_cursorName)
113 .arg(m_cursorHeight);
112 .arg(m_cursorHeight);
114
113
115 return QUrl(urlString);
114 return QUrl(urlString);
plugins/Cursor/CursorImageProvider.cpp+2
@@ -174,6 +174,8 @@ CursorImageProvider::CursorImageProvider()
174 m_fallbackNames[QStringLiteral("forbidden")].append(QStringLiteral("not-allowed"));
174 m_fallbackNames[QStringLiteral("forbidden")].append(QStringLiteral("not-allowed"));
175 m_fallbackNames[QStringLiteral("forbidden")].append(QStringLiteral("circle"));
175 m_fallbackNames[QStringLiteral("forbidden")].append(QStringLiteral("circle"));
176
176
177+ m_fallbackNames[QStringLiteral("grabbing")].append(QStringLiteral("closedhand")); // Breeze
178+
177 m_fallbackNames[QStringLiteral("hand")].append(QStringLiteral("pointing_hand"));
179 m_fallbackNames[QStringLiteral("hand")].append(QStringLiteral("pointing_hand"));
178 m_fallbackNames[QStringLiteral("hand")].append(QStringLiteral("pointer"));
180 m_fallbackNames[QStringLiteral("hand")].append(QStringLiteral("pointer"));
179
181
plugins/Dash/AudioProgressBar.qmldeleted−54
@@ -1,54 +0,0 @@
1−/*
2− * Copyright 2015 Canonical Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−import QtQuick 2.4
18−import Ubuntu.Components 1.3
19−import Dash 0.1
20−
21−Item {
22− id: root
23−
24− implicitHeight: progressBarImage.height
25−
26− property url source
27− readonly property double progress: AudioUrlComparer.compare(source, DashAudioPlayer.currentSource) ? DashAudioPlayer.progress : 0
28−
29− Image {
30− id: progressBarImage
31− anchors { left: parent.left; right: parent.right }
32− height: units.dp(6)
33− source: "graphics/music_progress_bg.png"
34− asynchronous: true
35− sourceSize.width: width
36− sourceSize.height: height
37− }
38−
39− UbuntuShape {
40− id: progressBarFill
41− objectName: "progressBarFill"
42−
43− readonly property int maxWidth: progressBarImage.width
44−
45− anchors {
46− left: progressBarImage.left
47− right: progressBarImage.right
48− verticalCenter: progressBarImage.verticalCenter
49− rightMargin: maxWidth - (maxWidth * root.progress)
50− }
51− height: units.dp(2)
52− backgroundColor: theme.palette.normal.activity
53− }
54−}
plugins/Dash/CMakeLists.txtdeleted−47
@@ -1,47 +0,0 @@
1−# There's no cmake var for v8 include path :-/
2−# so create one
3−LIST(GET Qt5Core_INCLUDE_DIRS 0 QtCoreDir0)
4−if(${Qt5Core_VERSION_STRING} VERSION_LESS "5.1.0")
5− SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/../QtV8/${Qt5Core_VERSION_STRING}/QtV8)
6−else()
7− SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/QtV8/${Qt5Core_VERSION_STRING}/QtV8)
8−endif()
9−
10−include_directories(
11− ${CMAKE_CURRENT_SOURCE_DIR}
12− ${CMAKE_CURRENT_BINARY_DIR}
13−)
14−
15−include_directories(
16− SYSTEM
17− ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
18− ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
19− ${Qt5V8_PRIVATE_INCLUDE_DIR}
20−)
21−
22−remove_definitions(-DQT_NO_KEYWORDS)
23−
24−set(QMLPLUGIN_SRC
25− plugin.cpp
26− listviewwithpageheader.cpp
27− abstractdashview.cpp
28− verticaljournal.cpp
29− horizontaljournal.cpp
30− organicgrid.cpp
31− )
32−
33−add_library(Dash-qml MODULE
34− ${QMLPLUGIN_SRC}
35− )
36−
37−target_link_libraries(Dash-qml
38− ${Qt5Gui_LIBRARIES}
39− ${Qt5Quick_LIBRARIES}
40− )
41−
42−qt5_use_modules(Dash-qml Qml Quick Concurrent)
43−
44−add_unity8_plugin(Dash 0.1 Dash TARGETS Dash-qml)
45−
46−install(FILES graphics/music_progress_bg.png DESTINATION ${SHELL_INSTALL_QML}/Dash/graphics/)
47−file(COPY graphics/music_progress_bg.png DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/graphics/)
plugins/Dash/CardAttributes.qmldeleted−71
@@ -1,71 +0,0 @@
1−/*
2− * Copyright 2014 Canonical Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−import QtQuick 2.4
18−import QtQuick.Layouts 1.1
19−import Ubuntu.Components 1.3
20−
21−GridLayout {
22− id: grid
23− anchors {
24− left: parent.left;
25− right: parent.right;
26− }
27− columns: 2 + repeater.count % 2
28− rowSpacing: units.gu(.5)
29− property alias model: repeater.model
30− property color color: theme.palette.normal.baseText
31− property real fontScale: 1.0
32−
33− Repeater {
34− id: repeater
35− delegate: Row {
36− spacing: units.gu(0.5)
37− readonly property int column: index % grid.columns;
38− Layout.alignment: {
39− if (column == 0) return Qt.AlignLeft;
40− if (column == grid.columns - 1 || index == repeater.count - 1) return Qt.AlignRight;
41− if (column == 1) return Qt.AlignHCenter;
42− }
43− Layout.column: index % grid.columns
44− Layout.row: index / grid.columns
45− Layout.columnSpan: index == repeater.count - 1 && grid.columns == 3 && column == 1 ? 2 : 1
46− Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
47− Layout.fillWidth: true
48− height: units.gu(2)
49− Icon {
50− id: icon
51− height: units.gu(2)
52− // FIXME Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293
53− width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth
54− source: "icon" in modelData && modelData["icon"] || ""
55− color: grid.color
56− }
57− Label {
58− id: label
59− width: parent.width - x
60− anchors.verticalCenter: parent.verticalCenter
61− text: "value" in modelData && modelData["value"] || "";
62− elide: Text.ElideRight
63− maximumLineCount: 1
64− font.weight: "style" in modelData && modelData["style"] === "highlighted" ? Font.Bold : Font.Light
65− fontSize: "x-small"
66− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
67− color: grid.color
68− }
69− }
70− }
71−}
plugins/Dash/CardAudioProgress.qmldeleted−59
@@ -1,59 +0,0 @@
1−/*
2− * Copyright 2015 Canonical Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−import QtQuick 2.4
18−import Ubuntu.Components 1.3
19−import Dash 0.1
20−
21−Item {
22− id: root
23−
24− implicitHeight: positionLabel.y + positionLabel.height
25− visible: AudioUrlComparer.compare(source, DashAudioPlayer.currentSource)
26−
27− property int duration: 0
28− property alias source: progress.source
29− property color color: theme.palette.normal.baseText
30− readonly property int position: root.visible ? DashAudioPlayer.position / 1000 : 0
31−
32− AudioProgressBar {
33− id: progress
34− anchors { left: parent.left; right: parent.right }
35− }
36−
37− Label {
38− id: positionLabel
39− anchors {
40− left: parent.left
41− top: progress.bottom
42− }
43− verticalAlignment: Text.AlignBottom
44− fontSize: "x-small"
45− text: DashAudioPlayer.lengthToString(root.position)
46− color: root.color
47− }
48−
49− Label {
50− anchors {
51− right: parent.right
52− top: progress.bottom
53− }
54− verticalAlignment: Text.AlignBottom
55− fontSize: "x-small"
56− text: DashAudioPlayer.lengthToString(duration)
57− color: root.color
58− }
59−}
plugins/Dash/CardCreator.jsdeleted−999
@@ -1,999 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−.pragma library
18−
19−// %1 is the template["card-background"]["elements"][0]
20−// %2 is the template["card-background"]["elements"][1]
21−// %3 is whether the loader should be asynchronous or not
22−// %4 is the template["card-background"] string
23−var kBackgroundLoaderCode = 'Loader {\n\
24− id: backgroundLoader; \n\
25− objectName: "backgroundLoader"; \n\
26− anchors.fill: parent; \n\
27− asynchronous: %3; \n\
28− visible: status === Loader.Ready; \n\
29− sourceComponent: UbuntuShape { \n\
30− objectName: "background"; \n\
31− radius: "small"; \n\
32− aspect: { \n\
33− switch (root.backgroundShapeStyle) { \n\
34− case "inset": return UbuntuShape.Inset; \n\
35− case "shadow": return UbuntuShape.DropShadow; \n\
36− default: \n\
37− case "flat": return UbuntuShape.Flat; \n\
38− } \n\
39− } \n\
40− backgroundColor: getColor(0) || "white"; \n\
41− secondaryBackgroundColor: getColor(1) || backgroundColor; \n\
42− backgroundMode: UbuntuShape.VerticalGradient; \n\
43− anchors.fill: parent; \n\
44− source: backgroundImage.source ? backgroundImage : null; \n\
45− property real luminance: Style.luminance(backgroundColor); \n\
46− property Image backgroundImage: Image { \n\
47− objectName: "backgroundImage"; \n\
48− source: { \n\
49− if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\
50− else return %4; \n\
51− } \n\
52− } \n\
53− function getColor(index) { \n\
54− if (cardData && typeof cardData["background"] === "object" \n\
55− && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\
56− return cardData["background"]["elements"][index]; \n\
57− } else return index === 0 ? %1 : %2; \n\
58− } \n\
59− } \n\
60− }\n';
61−
62−// %1 is the aspect of the UbuntuShape
63−var kArtUbuntuShapeCode = 'UbuntuShape { \n\
64− anchors.fill: parent; \n\
65− source: artImage; \n\
66− sourceFillMode: UbuntuShape.PreserveAspectCrop; \n\
67− radius: "small"; \n\
68− aspect: %1; \n\
69− }';
70−
71−var kArtProportionalShapeCode = 'ProportionalShape { \n\
72− anchors.left: parent.left; \n\
73− anchors.right: parent.right; \n\
74− source: artImage; \n\
75− aspect: UbuntuShape.DropShadow; \n\
76− }';
77−
78−// %1 is used as anchors of artShapeLoader
79−// %2 is used as image width
80−// %3 is used as image height
81−// %4 is whether the image should be visible
82−// %5 is whether the loader should be asynchronous or not
83−// %6 is the shape code we want to use
84−// %7 is injected as code to artImage
85−// %8 is used as image fallback
86−var kArtShapeHolderCode = 'Loader { \n\
87− id: artShapeLoader; \n\
88− height: root.fixedArtShapeSize.height; \n\
89− width: root.fixedArtShapeSize.width; \n\
90− anchors { %1 } \n\
91− objectName: "artShapeLoader"; \n\
92− readonly property string cardArt: cardData && cardData["art"] || %8; \n\
93− onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
94− active: cardArt != ""; \n\
95− asynchronous: %5; \n\
96− visible: status === Loader.Ready; \n\
97− sourceComponent: Item { \n\
98− id: artShape; \n\
99− objectName: "artShape"; \n\
100− visible: image.status === Image.Ready; \n\
101− readonly property alias image: artImage; \n\
102− %6 \n\
103− width: root.fixedArtShapeSize.width; \n\
104− height: root.fixedArtShapeSize.height; \n\
105− CroppedImageMinimumSourceSize { \n\
106− id: artImage; \n\
107− objectName: "artImage"; \n\
108− source: artShapeLoader.cardArt; \n\
109− asynchronous: %5; \n\
110− visible: %4; \n\
111− width: %2; \n\
112− height: %3; \n\
113− %7 \n\
114− } \n\
115− } \n\
116− }\n';
117−
118−// %1 is used as anchors of artShapeLoader
119−// %2 is used as image width
120−// %3 is used as image height
121−// %4 is whether the image should be visible
122−// %5 is whether the loader should be asynchronous or not
123−// %6 is the shape code we want to use
124−// %7 is injected as code to artImage
125−// %8 is used as image fallback
126−var kArtShapeHolderCodeCardToolCard = 'Loader { \n\
127− id: artShapeLoader; \n\
128− anchors { %1 } \n\
129− objectName: "artShapeLoader"; \n\
130− readonly property string cardArt: cardData && cardData["art"] || %8; \n\
131− onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
132− active: cardArt != ""; \n\
133− asynchronous: %5; \n\
134− visible: status === Loader.Ready; \n\
135− sourceComponent: Item { \n\
136− id: artShape; \n\
137− objectName: "artShape"; \n\
138− visible: image.status === Image.Ready; \n\
139− readonly property alias image: artImage; \n\
140− %6 \n\
141− width: image.status !== Image.Ready ? 0 : image.width; \n\
142− height: image.status !== Image.Ready ? 0 : image.height; \n\
143− CroppedImageMinimumSourceSize { \n\
144− id: artImage; \n\
145− objectName: "artImage"; \n\
146− source: artShapeLoader.cardArt; \n\
147− asynchronous: %5; \n\
148− visible: %4; \n\
149− width: %2; \n\
150− height: %3; \n\
151− %7 \n\
152− } \n\
153− } \n\
154− }\n';
155−
156−// %1 is anchors.fill
157−// %2 is width
158−// %3 is height
159−// %4 is whether the icon should be asynchronous or not
160−var kAudioButtonCode = 'AbstractButton { \n\
161− id: audioButton; \n\
162− anchors.fill: %1; \n\
163− width: %2; \n\
164− height: %3; \n\
165− readonly property url source: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["uri"]) || ""; \n\
166− UbuntuShape { \n\
167− anchors.fill: parent; \n\
168− visible: parent.pressed; \n\
169− radius: "small"; \n\
170− } \n\
171− Rectangle { \n\
172− color: Qt.rgba(0, 0, 0, 0.5); \n\
173− anchors.centerIn: parent; \n\
174− width: parent.width * 0.5; \n\
175− height: width; \n\
176− radius: width / 2; \n\
177− } \n\
178− Icon { \n\
179− anchors.centerIn: parent; \n\
180− width: parent.width * 0.3; \n\
181− height: width; \n\
182− opacity: 0.9; \n\
183− name: DashAudioPlayer.playing && AudioUrlComparer.compare(parent.source, DashAudioPlayer.currentSource) ? "media-playback-pause" : "media-playback-start"; \n\
184− color: "white"; \n\
185− asynchronous: %4; \n\
186− } \n\
187− onClicked: { \n\
188− if (AudioUrlComparer.compare(source, DashAudioPlayer.currentSource)) { \n\
189− if (DashAudioPlayer.playing) { \n\
190− DashAudioPlayer.pause(); \n\
191− } else { \n\
192− DashAudioPlayer.play(); \n\
193− } \n\
194− } else { \n\
195− var playlist = (cardData["quickPreviewData"] && cardData["quickPreviewData"]["playlist"]) || null; \n\
196− DashAudioPlayer.playSource(source, playlist); \n\
197− } \n\
198− } \n\
199− onPressAndHold: { \n\
200− root.pressAndHold(); \n\
201− } \n\
202− }';
203−
204−// %1 is whether the loader should be asynchronous or not
205−// %2 is the header height code
206−var kOverlayLoaderCode = 'Loader { \n\
207− id: overlayLoader; \n\
208− readonly property real overlayHeight: %2 + units.gu(2); \n\
209− anchors.fill: artShapeLoader; \n\
210− active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \n\
211− asynchronous: %1; \n\
212− visible: showHeader && status === Loader.Ready; \n\
213− sourceComponent: UbuntuShapeOverlay { \n\
214− id: overlay; \n\
215− property real luminance: Style.luminance(overlayColor); \n\
216− aspect: UbuntuShape.Flat; \n\
217− radius: "small"; \n\
218− overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
219− overlayRect: Qt.rect(0, 1 - overlayLoader.overlayHeight / height, 1, 1); \n\
220− } \n\
221− }\n';
222−
223−// multiple row version of HeaderRowCode
224−function kHeaderRowCodeGenerator() {
225− var kHeaderRowCodeTemplate = 'Row { \n\
226− id: row; \n\
227− objectName: "outerRow"; \n\
228− property real margins: units.gu(1); \n\
229− spacing: margins; \n\
230− %2\
231− anchors { %1 } \n\
232− anchors.right: parent.right; \n\
233− anchors.margins: margins; \n\
234− anchors.rightMargin: 0; \n\
235− data: [ \n\
236− %3 \n\
237− ] \n\
238− }\n';
239− var args = Array.prototype.slice.call(arguments);
240− var isCardTool = args.shift();
241− var heightCode = isCardTool ? "" : "height: root.fixedHeaderHeight; \n";
242− var code = kHeaderRowCodeTemplate.arg(args.shift()).arg(heightCode).arg(args.join(',\n'));
243− return code;
244−}
245−
246−// multiple item version of kHeaderContainerCode
247−function kHeaderContainerCodeGenerator() {
248− var headerContainerCodeTemplate = 'Item { \n\
249− id: headerTitleContainer; \n\
250− anchors { %1 } \n\
251− width: parent.width - x; \n\
252− implicitHeight: %2; \n\
253− data: [ \n\
254− %3 \n\
255− ]\n\
256− }\n';
257− var args = Array.prototype.slice.call(arguments);
258− var code = headerContainerCodeTemplate.arg(args.shift()).arg(args.shift()).arg(args.join(',\n'));
259− return code;
260−}
261−
262−// %1 is used as anchors of mascotShapeLoader
263−// %2 is whether the loader should be asynchronous or not
264−var kMascotShapeLoaderCode = 'Loader { \n\
265− id: mascotShapeLoader; \n\
266− objectName: "mascotShapeLoader"; \n\
267− asynchronous: %2; \n\
268− active: mascotImage.status === Image.Ready; \n\
269− visible: showHeader && active && status === Loader.Ready; \n\
270− width: units.gu(6); \n\
271− height: units.gu(5.625); \n\
272− sourceComponent: UbuntuShape { image: mascotImage } \n\
273− anchors { %1 } \n\
274− }\n';
275−
276−// %1 is used as anchors of mascotImage
277−// %2 is used as visible of mascotImage
278−// %3 is injected as code to mascotImage
279−// %4 is used as fallback image
280−var kMascotImageCode = 'CroppedImageMinimumSourceSize { \n\
281− id: mascotImage; \n\
282− objectName: "mascotImage"; \n\
283− anchors { %1 } \n\
284− source: cardData && cardData["mascot"] || %4; \n\
285− width: units.gu(6); \n\
286− height: units.gu(5.625); \n\
287− horizontalAlignment: Image.AlignHCenter; \n\
288− verticalAlignment: Image.AlignVCenter; \n\
289− visible: %2; \n\
290− %3 \n\
291− }\n';
292−
293−// %1 is used as anchors of titleLabel
294−// %2 is used as color of titleLabel
295−// %3 is used as extra condition for visible of titleLabel
296−// %4 is used as title width
297−// %5 is used as horizontal alignment
298−var kTitleLabelCode = 'Label { \n\
299− id: titleLabel; \n\
300− objectName: "titleLabel"; \n\
301− anchors { %1 } \n\
302− elide: Text.ElideRight; \n\
303− fontSize: "small"; \n\
304− wrapMode: Text.Wrap; \n\
305− maximumLineCount: 2; \n\
306− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
307− color: %2; \n\
308− visible: showHeader %3; \n\
309− width: %4; \n\
310− text: root.title; \n\
311− font.weight: Font.Normal; \n\
312− horizontalAlignment: %5; \n\
313− }\n';
314−
315−// %1 is used as extra anchors of emblemIcon
316−// %2 is used as color of emblemIcon
317−// FIXME The width code is a
318−// Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293
319−var kEmblemIconCode = 'Icon { \n\
320− id: emblemIcon; \n\
321− objectName: "emblemIcon"; \n\
322− anchors { \n\
323− bottom: titleLabel.baseline; \n\
324− right: parent.right; \n\
325− %1 \n\
326− } \n\
327− source: cardData && cardData["emblem"] || ""; \n\
328− color: %2; \n\
329− height: source != "" ? titleLabel.font.pixelSize : 0; \n\
330− width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth; \n\
331− }\n';
332−
333−// %1 is used as anchors of touchdown effect
334−var kTouchdownCode = 'Loader { \n\
335− active: root.pressed; \n\
336− anchors { %1 } \n\
337− sourceComponent: UbuntuShape { \n\
338− objectName: "touchdown"; \n\
339− anchors.fill: parent; \n\
340− radius: "small"; \n\
341− borderSource: "radius_pressed.sci" \n\
342− } \n\
343− }\n';
344−
345−// %1 is used as anchors of subtitleLabel
346−// %2 is used as color of subtitleLabel
347−var kSubtitleLabelCode = 'Label { \n\
348− id: subtitleLabel; \n\
349− objectName: "subtitleLabel"; \n\
350− anchors { %1 } \n\
351− anchors.topMargin: units.dp(2); \n\
352− elide: Text.ElideRight; \n\
353− maximumLineCount: 1; \n\
354− fontSize: "x-small"; \n\
355− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
356− color: %2; \n\
357− visible: titleLabel.visible && titleLabel.text; \n\
358− text: cardData && cardData["subtitle"] || ""; \n\
359− font.weight: Font.Light; \n\
360− }\n';
361−
362−// %1 is used as anchors of attributesRow
363−// %2 is used as color of attributesRow
364−var kAttributesRowCode = 'CardAttributes { \n\
365− id: attributesRow; \n\
366− objectName: "attributesRow"; \n\
367− anchors { %1 } \n\
368− color: %2; \n\
369− fontScale: root.fontScale; \n\
370− model: cardData && cardData["attributes"]; \n\
371− }\n';
372−
373−// %1 is used as anchors of socialActionsRow
374−// %2 is used as color of socialActionsRow
375−var kSocialActionsRowCode = 'CardSocialActions { \n\
376− id: socialActionsRow; \n\
377− objectName: "socialActionsRow"; \n\
378− anchors { %1 } \n\
379− color: %2; \n\
380− model: cardData && cardData["socialActions"]; \n\
381− onClicked: root.action(actionId); \n\
382− }\n';
383−
384−// %1 is used as top anchor of summary
385−// %2 is used as topMargin anchor of summary
386−// %3 is used as color of summary
387−var kSummaryLabelCode = 'Label { \n\
388− id: summary; \n\
389− objectName: "summaryLabel"; \n\
390− anchors { \n\
391− top: %1; \n\
392− left: parent.left; \n\
393− right: parent.right; \n\
394− margins: units.gu(1); \n\
395− topMargin: %2; \n\
396− } \n\
397− wrapMode: Text.Wrap; \n\
398− maximumLineCount: 5; \n\
399− elide: Text.ElideRight; \n\
400− text: cardData && cardData["summary"] || ""; \n\
401− height: text ? implicitHeight : 0; \n\
402− fontSize: "x-small"; \n\
403− font.weight: Font.Light; \n\
404− color: %3; \n\
405− }\n';
406−
407−// %1 is used as bottom anchor of audio progress bar
408−// %2 is used as left anchor of audio progress bar
409−// %3 is used as text color
410−var kAudioProgressBarCode = 'CardAudioProgress { \n\
411− id: audioProgressBar; \n\
412− duration: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["duration"]) || 0; \n\
413− source: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["uri"]) || ""; \n\
414− anchors { \n\
415− bottom: %1; \n\
416− left: %2; \n\
417− right: parent.right; \n\
418− margins: units.gu(1); \n\
419− } \n\
420− color: %3; \n\
421− }';
422−
423−function sanitizeColor(colorString) {
424− if (colorString !== undefined) {
425− if (colorString.match(/^[#a-z0-9]*$/i) === null) {
426− // This is not the perfect regexp for color
427− // but what we're trying to do here is just protect
428− // against injection so it's ok
429− return "";
430− }
431− }
432− return colorString;
433−}
434−
435−function cardString(template, components, isCardTool, artShapeStyle, categoryLayout) {
436− var code;
437−
438− var templateInteractive = (template == null ? true : (template["non-interactive"] !== undefined ? !template["non-interactive"] : true)) ? "true" : "false";
439−
440− code = 'AbstractButton { \n\
441− id: root; \n\
442− property var cardData; \n\
443− property string backgroundShapeStyle: "flat"; \n\
444− property real fontScale: 1.0; \n\
445− property var scopeStyle: null; \n\
446− readonly property string title: cardData && cardData["title"] || ""; \n\
447− property bool showHeader: true; \n\
448− implicitWidth: childrenRect.width; \n\
449− enabled: %1; \n\
450− \n'.arg(templateInteractive);
451−
452− if (!isCardTool) {
453− code += "property int fixedHeaderHeight: -1; \n\
454− property size fixedArtShapeSize: Qt.size(-1, -1); \n";
455− }
456−
457− var hasArt = components["art"] && components["art"]["field"] || false;
458− var hasSummary = components["summary"] || false;
459− var isConciergeMode = components["art"] && components["art"]["conciergeMode"] || false;
460− var artAndSummary = hasArt && hasSummary && !isConciergeMode;
461− var isHorizontal = template["card-layout"] === "horizontal";
462− var hasBackground = (!isHorizontal && (template["card-background"] || components["background"] || artAndSummary)) ||
463− (hasSummary && (template["card-background"] || components["background"]));
464− var hasTitle = components["title"] || false;
465− var hasMascot = components["mascot"] || false;
466− var hasEmblem = components["emblem"] && !(hasMascot && template["card-size"] === "small") || false;
467− var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
468− var hasSubtitle = hasTitle && components["subtitle"] || false;
469− var hasHeaderRow = hasMascot && hasTitle;
470− var hasAttributes = hasTitle && components["attributes"] && components["attributes"]["field"] || false;
471− var hasSocialActions = hasTitle && components["social-actions"] || false;
472− var isAudio = template["quick-preview-type"] === "audio";
473− var asynchronous = isCardTool ? "false" : "true";
474−
475− code += 'signal action(var actionId);\n';
476− if (isAudio) {
477− // For now we only support audio cards with [optional] art, title, subtitle
478− // in horizontal mode
479− // Anything else makes it behave not like an audio card
480− if (hasSummary) isAudio = false;
481− if (!isHorizontal) isAudio = false;
482− if (hasMascot) isAudio = false;
483− if (hasEmblem) isAudio = false;
484− if (headerAsOverlay) isAudio = false;
485− if (hasAttributes) isAudio = false;
486− }
487−
488− if (hasBackground) {
489− var templateCardBackground;
490− if (template && typeof template["card-background"] === "string") {
491− templateCardBackground = 'decodeURI("' + encodeURI(template["card-background"]) + '")';
492− } else {
493− templateCardBackground = '""';
494− }
495−
496− var backgroundElements0;
497− var backgroundElements1;
498− if (template && typeof template["card-background"] === "object" && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
499− var element0 = sanitizeColor(template["card-background"]["elements"][0]);
500− var element1 = sanitizeColor(template["card-background"]["elements"][1]);
501− if (element0 !== undefined) {
502− backgroundElements0 = '"%1"'.arg(element0);
503− }
504− if (element1 !== undefined) {
505− backgroundElements1 = '"%1"'.arg(element1);
506− }
507− }
508− code += kBackgroundLoaderCode.arg(backgroundElements0).arg(backgroundElements1).arg(asynchronous).arg(templateCardBackground);
509− }
510−
511− if (hasArt) {
512− var artShapeAspect;
513− if (isCardTool) {
514− code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
515−
516− var artShapeAspect = components["art"] && components["art"]["aspect-ratio"] || 1;
517− if (isNaN(artShapeAspect)) {
518− artShapeAspect = 1;
519− }
520− } else {
521− artShapeAspect = "(root.fixedArtShapeSize.width / root.fixedArtShapeSize.height)";
522− }
523−
524− var widthCode, heightCode;
525− var artAnchors;
526− if (isHorizontal) {
527− artAnchors = 'left: parent.left';
528− if (hasMascot || hasTitle) {
529− widthCode = 'height * ' + artShapeAspect;
530− heightCode = 'headerHeight + 2 * units.gu(1)';
531− } else {
532− // This side of the else is a bit silly, who wants an horizontal layout without mascot and title?
533− // So we define a "random" height of the image height + 2 gu for the margins
534− widthCode = 'height * ' + artShapeAspect
535− heightCode = 'units.gu(7.625)';
536− }
537− } else {
538− artAnchors = 'horizontalCenter: parent.horizontalCenter;';
539− widthCode = 'root.width'
540− heightCode = 'width / ' + artShapeAspect;
541− }
542−
543− var fallback = !isCardTool && components["art"] && components["art"]["fallback"] || "";
544− fallback = encodeURI(fallback);
545− var fallbackStatusCode = "";
546− var fallbackURICode = '""';
547− if (fallback !== "") {
548− // fallbackStatusCode has %8 in it because we want to substitute it for fallbackURICode
549− // which in kArtShapeHolderCode is %8
550− fallbackStatusCode += 'onStatusChanged: if (status === Image.Error) source = %8;';
551− fallbackURICode = 'decodeURI("%1")'.arg(fallback);
552− }
553− var artShapeHolderShapeCode;
554− if (!isConciergeMode) {
555− if (artShapeStyle === "icon") {
556− artShapeHolderShapeCode = kArtProportionalShapeCode;
557− } else {
558− var artShapeHolderShapeAspect;
559− switch (artShapeStyle) {
560− case "inset": artShapeHolderShapeAspect = "UbuntuShape.Inset"; break;
561− case "shadow": artShapeHolderShapeAspect = "UbuntuShape.DropShadow"; break;
562− default:
563− case "flat": artShapeHolderShapeAspect = "UbuntuShape.Flat"; break;
564− }
565− artShapeHolderShapeCode = kArtUbuntuShapeCode.arg(artShapeHolderShapeAspect);
566− }
567− } else {
568− artShapeHolderShapeCode = "";
569− }
570− var artShapeHolderCode = isCardTool ? kArtShapeHolderCodeCardToolCard : kArtShapeHolderCode;
571− code += artShapeHolderCode.arg(artAnchors)
572− .arg(widthCode)
573− .arg(heightCode)
574− .arg(isConciergeMode ? "true" : "false")
575− .arg(asynchronous)
576− .arg(artShapeHolderShapeCode)
577− .arg(fallbackStatusCode)
578− .arg(fallbackURICode);
579− } else if (isCardTool) {
580− code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
581− }
582−
583− if (headerAsOverlay) {
584− var headerHeightCode = isCardTool ? "headerHeight" : "root.fixedHeaderHeight";
585− code += kOverlayLoaderCode.arg(asynchronous).arg(headerHeightCode);
586− }
587−
588− var headerVerticalAnchors;
589− if (headerAsOverlay) {
590− headerVerticalAnchors = 'bottom: artShapeLoader.bottom; \n\
591− bottomMargin: units.gu(1);\n';
592− } else {
593− if (hasArt) {
594− if (isHorizontal) {
595− headerVerticalAnchors = 'top: artShapeLoader.top; \n\
596− topMargin: units.gu(1);\n';
597− } else {
598− headerVerticalAnchors = 'top: artShapeLoader.bottom; \n\
599− topMargin: units.gu(1);\n';
600− }
601− } else {
602− headerVerticalAnchors = 'top: parent.top; \n\
603− topMargin: units.gu(1);\n';
604− }
605− }
606−
607− var headerLeftAnchor;
608− var headerLeftAnchorHasMargin = false;
609− if (isHorizontal && hasArt) {
610− headerLeftAnchor = 'left: artShapeLoader.right; \n\
611− leftMargin: units.gu(1);\n';
612− headerLeftAnchorHasMargin = true;
613− } else if (isHorizontal && isAudio) {
614− headerLeftAnchor = 'left: audioButton.right; \n\
615− leftMargin: units.gu(1);\n';
616− headerLeftAnchorHasMargin = true;
617− } else {
618− headerLeftAnchor = 'left: parent.left;\n';
619− }
620−
621− var touchdownOnArtShape = !hasBackground && hasArt && !hasMascot && !hasSummary && !isAudio;
622−
623− if (hasHeaderRow) {
624− code += 'readonly property int headerHeight: row.height;\n'
625− } else if (hasMascot) {
626− code += 'readonly property int headerHeight: mascotImage.height;\n'
627− } else if (hasAttributes) {
628− if (hasTitle && hasSubtitle) {
629− code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + attributesRow.height + attributesRow.anchors.topMargin;\n'
630− } else if (hasTitle) {
631− code += 'readonly property int headerHeight: titleLabel.height + attributesRow.height + attributesRow.anchors.topMargin;\n'
632− } else {
633− code += 'readonly property int headerHeight: attributesRow.height;\n'
634− }
635− } else if (isAudio) {
636− if (hasSubtitle) {
637− code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + audioProgressBar.height + audioProgressBar.anchors.topMargin;\n'
638− } else if (hasTitle) {
639− code += 'readonly property int headerHeight: titleLabel.height + audioProgressBar.height + audioProgressBar.anchors.topMargin;\n'
640− } else {
641− code += 'readonly property int headerHeight: audioProgressBar.height;\n'
642− }
643− } else if (hasSubtitle) {
644− code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
645− } else if (hasTitle) {
646− code += 'readonly property int headerHeight: titleLabel.height;\n'
647− } else {
648− code += 'readonly property int headerHeight: 0;\n'
649− }
650−
651− var mascotShapeCode = '';
652− var mascotCode = '';
653− if (hasMascot) {
654− var useMascotShape = !hasBackground && !headerAsOverlay;
655− var mascotAnchors = '';
656− if (!hasHeaderRow) {
657− mascotAnchors += headerLeftAnchor;
658− mascotAnchors += headerVerticalAnchors;
659− if (!headerLeftAnchorHasMargin) {
660− mascotAnchors += 'leftMargin: units.gu(1);\n'
661− }
662− } else {
663− mascotAnchors = 'verticalCenter: parent.verticalCenter;'
664− }
665−
666− if (useMascotShape) {
667− mascotShapeCode = kMascotShapeLoaderCode.arg(mascotAnchors).arg(asynchronous);
668− }
669−
670− var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
671− var fallback = !isCardTool && components["mascot"] && components["mascot"]["fallback"] || "";
672− fallback = encodeURI(fallback);
673− var fallbackStatusCode = "";
674− var fallbackURICode = '""';
675− if (fallback !== "") {
676− // fallbackStatusCode has %4 in it because we want to substitute it for fallbackURICode
677− // which in kMascotImageCode is %4
678− fallbackStatusCode += 'onStatusChanged: if (status === Image.Error) source = %4;';
679− fallbackURICode = 'decodeURI("%1")'.arg(fallback);
680− }
681− mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible).arg(fallbackStatusCode).arg(fallbackURICode);
682− }
683−
684− var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white")';
685−
686− var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
687− var titleSubtitleCode = '';
688− if (hasTitle) {
689− var titleColor;
690− if (headerAsOverlay) {
691− titleColor = 'root.scopeStyle && overlayLoader.item ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item && overlayLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white")';
692− } else if (hasSummary) {
693− titleColor = 'summary.color';
694− } else if (hasBackground) {
695− titleColor = summaryColorWithBackground;
696− } else {
697− titleColor = 'root.scopeStyle ? root.scopeStyle.foreground : theme.palette.normal.baseText';
698− }
699−
700− var titleAnchors;
701− var subtitleAnchors;
702− var attributesAnchors;
703− var titleContainerAnchors;
704− var titleRightAnchor;
705− var titleWidth = "undefined";
706−
707− var extraRightAnchor = '';
708− var extraLeftAnchor = '';
709− if (!touchdownOnArtShape) {
710− extraRightAnchor = 'rightMargin: units.gu(1); \n';
711− extraLeftAnchor = 'leftMargin: units.gu(1); \n';
712− } else if (headerAsOverlay && !hasEmblem) {
713− extraRightAnchor = 'rightMargin: units.gu(1); \n';
714− }
715−
716− if (hasMascot) {
717− titleContainerAnchors = 'verticalCenter: parent.verticalCenter; ';
718− } else {
719− titleContainerAnchors = 'right: parent.right; ';
720− titleContainerAnchors += headerLeftAnchor;
721− titleContainerAnchors += headerVerticalAnchors;
722− if (!headerLeftAnchorHasMargin) {
723− titleContainerAnchors += extraLeftAnchor;
724− }
725− }
726− if (hasEmblem) {
727− titleRightAnchor = 'right: emblemIcon.left; \n\
728− rightMargin: emblemIcon.width > 0 ? units.gu(0.5) : 0; \n';
729− } else {
730− titleRightAnchor = 'right: parent.right; \n'
731− titleRightAnchor += extraRightAnchor;
732− }
733−
734− if (hasTitleContainer) {
735− // Using headerTitleContainer
736− titleAnchors = titleRightAnchor;
737− titleAnchors += 'left: parent.left; \n\
738− top: parent.top;';
739− subtitleAnchors = 'right: parent.right; \n\
740− left: parent.left; \n';
741− subtitleAnchors += extraRightAnchor;
742− if (hasSubtitle) {
743− attributesAnchors = subtitleAnchors + 'top: subtitleLabel.bottom;\n';
744− subtitleAnchors += 'top: titleLabel.bottom;\n';
745− } else {
746− attributesAnchors = subtitleAnchors + 'top: titleLabel.bottom;\n';
747− }
748− } else if (hasMascot) {
749− // Using row without titleContainer
750− titleAnchors = 'verticalCenter: parent.verticalCenter;\n';
751− titleWidth = "parent.width - x";
752− } else {
753− if (headerAsOverlay) {
754− // Using anchors to the overlay
755− titleAnchors = titleRightAnchor;
756− titleAnchors += 'left: parent.left; \n\
757− leftMargin: units.gu(1); \n\
758− top: overlayLoader.top; \n\
759− topMargin: units.gu(1) + overlayLoader.height - overlayLoader.overlayHeight; \n';
760− } else {
761− // Using anchors to the mascot/parent
762− titleAnchors = titleRightAnchor;
763− titleAnchors += headerLeftAnchor;
764− titleAnchors += headerVerticalAnchors;
765− if (!headerLeftAnchorHasMargin) {
766− titleAnchors += extraLeftAnchor;
767− }
768− }
769− subtitleAnchors = 'left: titleLabel.left; \n\
770− leftMargin: titleLabel.leftMargin; \n';
771− subtitleAnchors += extraRightAnchor;
772− if (hasEmblem) {
773− // using container
774− subtitleAnchors += 'right: parent.right; \n';
775− } else {
776− subtitleAnchors += 'right: titleLabel.right; \n';
777− }
778−
779− if (hasSubtitle) {
780− attributesAnchors = subtitleAnchors + 'top: subtitleLabel.bottom;\n';
781− subtitleAnchors += 'top: titleLabel.bottom;\n';
782− } else {
783− attributesAnchors = subtitleAnchors + 'top: titleLabel.bottom;\n';
784− }
785− }
786−
787− var titleAlignment = "Text.AlignHCenter";
788− if (template["card-layout"] === "horizontal"
789− || typeof components["title"] !== "object"
790− || components["title"]["align"] === "left") titleAlignment = "Text.AlignLeft";
791− var keys = ["mascot", "emblem", "subtitle", "attributes", "summary"];
792− for (var key in keys) {
793− key = keys[key];
794− try {
795− if (typeof components[key] === "string"
796− || typeof components[key]["field"] === "string") titleAlignment = "Text.AlignLeft";
797− } catch (e) {
798− continue;
799− }
800− }
801−
802− // code for different elements
803− var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
804− var titleCode = kTitleLabelCode.arg(titleAnchors).arg(titleColor).arg(titleLabelVisibleExtra).arg(titleWidth).arg(titleAlignment);
805− var subtitleCode;
806− var attributesCode;
807−
808− // code for the title container
809− var containerCode = [];
810− var containerHeight = 'titleLabel.height';
811− containerCode.push(titleCode);
812− if (hasSubtitle) {
813− subtitleCode = kSubtitleLabelCode.arg(subtitleAnchors).arg(titleColor);
814− containerCode.push(subtitleCode);
815− containerHeight += ' + subtitleLabel.height';
816− }
817− if (hasEmblem) {
818− containerCode.push(kEmblemIconCode.arg(extraRightAnchor).arg(titleColor));
819− }
820− if (hasAttributes) {
821− attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(titleColor);
822− containerCode.push(attributesCode);
823− containerHeight += ' + attributesRow.height';
824− }
825−
826− if (hasTitleContainer) {
827− // use container
828− titleSubtitleCode = kHeaderContainerCodeGenerator(titleContainerAnchors, containerHeight, containerCode);
829− } else {
830− // no container
831− titleSubtitleCode = titleCode;
832− if (hasSubtitle) {
833− titleSubtitleCode += subtitleCode;
834− }
835− if (hasAttributes) {
836− titleSubtitleCode += attributesCode;
837− }
838− }
839− }
840−
841− if (hasHeaderRow) {
842− var rowCode = [mascotCode, titleSubtitleCode];
843− if (mascotShapeCode != '') {
844− rowCode.unshift(mascotShapeCode);
845− }
846− code += kHeaderRowCodeGenerator(isCardTool, headerVerticalAnchors + headerLeftAnchor, rowCode)
847− } else {
848− code += mascotShapeCode + mascotCode + titleSubtitleCode;
849− }
850−
851− if (isAudio) {
852− var audioProgressBarLeftAnchor = 'audioButton.right';
853− var audioProgressBarBottomAnchor = 'audioButton.bottom';
854− var audioProgressBarTextColor = 'root.scopeStyle ? root.scopeStyle.foreground : theme.palette.normal.baseText';
855−
856− code += kAudioProgressBarCode.arg(audioProgressBarBottomAnchor)
857− .arg(audioProgressBarLeftAnchor)
858− .arg(audioProgressBarTextColor);
859−
860− var audioButtonAnchorsFill;
861− var audioButtonWidth;
862− var audioButtonHeight;
863− if (hasArt) {
864− audioButtonAnchorsFill = 'artShapeLoader';
865− audioButtonWidth = 'undefined';
866− audioButtonHeight = 'undefined';
867− } else {
868− audioButtonAnchorsFill = 'undefined';
869− audioButtonWidth = 'height';
870− audioButtonHeight = isCardTool ? 'headerHeight + 2 * units.gu(1)'
871− : 'root.fixedHeaderHeight + 2 * units.gu(1)';
872− }
873− code += kAudioButtonCode.arg(audioButtonAnchorsFill).arg(audioButtonWidth).arg(audioButtonHeight).arg(asynchronous);
874− }
875−
876− if (hasSummary) {
877− var summaryTopAnchor;
878− if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
879− else if (headerAsOverlay && hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
880− else if (hasHeaderRow) summaryTopAnchor = 'row.bottom';
881− else if (hasTitleContainer) summaryTopAnchor = 'headerTitleContainer.bottom';
882− else if (hasMascot) summaryTopAnchor = 'mascotImage.bottom';
883− else if (hasAttributes) summaryTopAnchor = 'attributesRow.bottom';
884− else if (hasSubtitle) summaryTopAnchor = 'subtitleLabel.bottom';
885− else if (hasTitle) summaryTopAnchor = 'titleLabel.bottom';
886− else if (hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
887− else summaryTopAnchor = 'parent.top';
888−
889− var summaryColor;
890− if (hasBackground) {
891− summaryColor = summaryColorWithBackground;
892− } else {
893− summaryColor = 'root.scopeStyle ? root.scopeStyle.foreground : theme.palette.normal.baseText';
894− }
895−
896− var summaryTopMargin = (hasMascot || hasSubtitle || hasAttributes ? 'anchors.margins' : '0');
897−
898− code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(summaryColor);
899− }
900−
901− if (hasSocialActions) {
902− var socialAnchors;
903− var socialTopAnchor;
904−
905− if (hasSummary) socialTopAnchor = 'summary.bottom;';
906− else if (isHorizontal && hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
907− else if (headerAsOverlay && hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
908− else if (hasHeaderRow) socialTopAnchor = 'row.bottom;';
909− else if (hasTitleContainer) socialTopAnchor = 'headerTitleContainer.bottom;';
910− else if (hasMascot) socialTopAnchor = 'mascotImage.bottom;';
911− else if (hasAttributes) socialTopAnchor = 'attributesRow.bottom;';
912− else if (hasSubtitle) socialTopAnchor = 'subtitleLabel.bottom;';
913− else if (hasTitle) socialTopAnchor = 'titleLabel.bottom;';
914− else if (hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
915− else socialTopAnchor = 'parent.top';
916−
917− socialAnchors = 'top: ' + socialTopAnchor + ' left: parent.left; right: parent.right; topMargin: units.gu(1);'
918−
919− var socialColor;
920− if (hasBackground) {
921− socialColor = summaryColorWithBackground;
922− } else {
923− socialColor = 'root.scopeStyle ? root.scopeStyle.foreground : theme.palette.normal.baseText';
924− }
925−
926− code += kSocialActionsRowCode.arg(socialAnchors).arg(socialColor);
927− }
928−
929− if (artShapeStyle != "shadow" && artShapeStyle != "icon" && !isCardTool) {
930− var touchdownAnchors;
931− if (hasBackground) {
932− touchdownAnchors = 'fill: backgroundLoader';
933− } else if (touchdownOnArtShape) {
934− touchdownAnchors = 'fill: artShapeLoader';
935− } else {
936− touchdownAnchors = 'fill: root'
937− }
938− code += kTouchdownCode.arg(touchdownAnchors);
939− }
940−
941− if (isCardTool || categoryLayout !== "grid") {
942− var implicitHeight = 'implicitHeight: ';
943− if (hasSocialActions) {
944− implicitHeight += 'socialActionsRow.y + socialActionsRow.height + units.gu(1);\n';
945− } else if (hasSummary) {
946− implicitHeight += 'summary.y + summary.height + units.gu(1);\n';
947− } else if (isAudio) {
948− implicitHeight += 'audioButton.height;\n';
949− } else if (headerAsOverlay) {
950− implicitHeight += 'artShapeLoader.height;\n';
951− } else if (hasHeaderRow) {
952− implicitHeight += 'row.y + row.height + units.gu(1);\n';
953− } else if (hasMascot) {
954− implicitHeight += 'mascotImage.y + mascotImage.height;\n';
955− } else if (hasTitleContainer) {
956− implicitHeight += 'headerTitleContainer.y + headerTitleContainer.height + units.gu(1);\n';
957− } else if (hasAttributes) {
958− implicitHeight += 'attributesRow.y + attributesRow.height + units.gu(1);\n';
959− } else if (hasSubtitle) {
960− implicitHeight += 'subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
961− } else if (hasTitle) {
962− implicitHeight += 'titleLabel.y + titleLabel.height + units.gu(1);\n';
963− } else if (hasArt) {
964− implicitHeight += 'artShapeLoader.height;\n';
965− } else {
966− implicitHeight = '';
967− }
968− } else {
969− implicitHeight = '';
970− }
971−
972− // Close the AbstractButton
973− code += implicitHeight + '}\n';
974−
975− return code;
976−}
977−
978−function createCardComponent(parent, template, components, isCardTool, artShapeStyle, categoryLayout, identifier) {
979− var imports = 'import QtQuick 2.4; \n\
980− import Ubuntu.Components 1.3; \n\
981− import Ubuntu.Settings.Components 0.1; \n\
982− import Dash 0.1;\n\
983− import Utils 0.1;\n';
984− var card = cardString(template, components, isCardTool, artShapeStyle, categoryLayout);
985− var code = imports + 'Component {\n' + card + '}\n';
986−
987− try {
988− return Qt.createQmlObject(code, parent, identifier);
989− } catch (e) {
990− console.error("ERROR: Invalid component created.");
991− console.error("Template:");
992− console.error(JSON.stringify(template));
993− console.error("Components:");
994− console.error(JSON.stringify(components));
995− console.error("Code:");
996− console.error(code);
997− throw e;
998− }
999−}
plugins/Dash/CardCreatorCache.qmldeleted−40
@@ -1,40 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−pragma Singleton
18−import QtQuick 2.4
19−import "CardCreator.js" as CardCreator
20−
21−QtObject {
22− id: root
23−
24− property var cache: new Object();
25−
26− function getCardComponent(template, components, isCardTool, artShapeStyle, categoryLayout) {
27− if (template === undefined || components === undefined)
28− return undefined;
29−
30− var tString = JSON.stringify(template);
31− var cString = JSON.stringify(components);
32− var allString = tString + cString + isCardTool + artShapeStyle + categoryLayout;
33− var component = cache[allString];
34− if (component === undefined) {
35− component = CardCreator.createCardComponent(root, template, components, isCardTool, artShapeStyle, categoryLayout, allString);
36− cache[allString] = component;
37− }
38− return component;
39− }
40−}
plugins/Dash/CardSocialActions.qmldeleted−94
@@ -1,94 +0,0 @@
1−/*
2− * Copyright 2016 Canonical Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−import QtQuick 2.4
18−import QtQuick.Layouts 1.1
19−import Ubuntu.Components 1.3
20−
21−Column {
22− id: socialActions
23− spacing: units.gu(0.5)
24−
25− property alias model: repeater.model
26− property color color: theme.palette.normal.baseText
27−
28− signal clicked(var actionId)
29−
30− Rectangle {
31− id: divider
32− visible: repeater.count > 0
33− anchors {
34− left: parent.left;
35− right: parent.right;
36− leftMargin:units.dp(1);
37− rightMargin: units.dp(1);
38− }
39− color: Qt.darker(theme.palette.normal.background, 1.12)
40− height: visible ? units.dp(1) : 0
41− }
42−
43− Row {
44− id: row
45− anchors {
46− left: parent.left
47− right: parent.right
48− leftMargin: units.gu(1)
49− rightMargin: units.gu(1)
50− }
51− spacing: units.gu(2)
52− readonly property int visibleItems: {
53− if (width <= units.gu(12)) // small card
54− return 2;
55− else if (width <= units.gu(21)) // medium card
56− return 3;
57− else // large or horizontal card
58− return 4;
59− }
60−
61− Repeater {
62− id: repeater
63− delegate: Loader {
64− height: units.gu(2)
65− active: index < row.visibleItems
66− sourceComponent: AbstractButton {
67− objectName: "delegate" + index
68− height: units.gu(2)
69− width: icon.width
70− Icon {
71− id: icon
72− objectName: "icon"
73−
74− readonly property url urlIcon: modelData && modelData["icon"] || ""
75− readonly property url urlTemporaryIcon: "temporaryIcon" in modelData && modelData["temporaryIcon"] || ""
76−
77− height: units.gu(2)
78− // FIXME Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293
79− width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth
80− source: urlIcon
81− color: socialActions.color
82−
83− onUrlIconChanged: {
84− if (urlIcon) source = urlIcon
85− }
86− }
87−
88− onClicked: socialActions.clicked(modelData["id"]);
89− onPressedChanged: if (pressed && icon.urlTemporaryIcon != "") icon.source = icon.urlTemporaryIcon
90− }
91− }
92− }
93− }
94−}
plugins/Dash/CroppedImageMinimumSourceSize.qmldeleted−38
@@ -1,38 +0,0 @@
1−/*
2− * Copyright (C) 2014, 2015 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−import QtQuick 2.4
18−
19−Image {
20− id: root
21−
22− fillMode: Image.PreserveAspectCrop
23−
24− property bool useHeight: false
25− function updateUseHeight()
26− {
27− // Do not turn into a binding since otherwise the qml
28− // engine complains about binding loops
29− useHeight = (implicitWidth / implicitHeight) > (width / height);
30− }
31−
32− onHeightChanged: updateUseHeight();
33− onWidthChanged: updateUseHeight();
34− onImplicitHeightChanged: updateUseHeight();
35− onImplicitWidthChanged: updateUseHeight();
36−
37− sourceSize: useHeight ? Qt.size(0, height) : Qt.size(width, 0)
38−}
plugins/Dash/DashAudioPlayer.qmldeleted−120
@@ -1,120 +0,0 @@
1−/*
2− * Copyright (C) 2015 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−pragma Singleton
18−import QtQuick 2.4
19−import QtMultimedia 5.6
20−import Dash 0.1
21−
22−QtObject {
23− id: root
24− readonly property real progress: priv.audio ? priv.audio.position / priv.audio.duration : 0.0
25− readonly property bool playing: priv.audio ? priv.audio.playbackState === Audio.PlayingState : false
26− readonly property bool paused: priv.audio ? priv.audio.playbackState === Audio.PausedState : false
27− readonly property bool stopped: priv.audio ? priv.audio.playbackState === Audio.StoppedState : true
28− readonly property int position: priv.audio ? priv.audio.position : 0
29− readonly property url currentSource: priv.audio ? priv.audio.playlist.currentItemSource : ""
30− readonly property Playlist playlist: priv.audio ? priv.audio.playlist : null
31−
32− function playSource(newSource, newPlaylist) {
33− if (!priv.audio) {
34− console.info("DashAudioPlayer: creating player");
35− priv.audio = priv.audioComponent.createObject(root);
36− }
37− stop();
38− priv.audio.playlist.clear();
39− if (newPlaylist) {
40− // Look for newSource in newPlaylist
41− var sourceIndex = -1;
42− for (var i in newPlaylist) {
43− if (AudioUrlComparer.compare(newSource, newPlaylist[i])) {
44− sourceIndex = i;
45− break;
46− }
47− }
48− var urls = [];
49− if (sourceIndex === -1 && newSource != "") {
50− // If the playing song is not in the playlist, add it
51− urls.push(newSource);
52− sourceIndex = 0;
53− }
54− for (var i in newPlaylist) {
55− urls.push(newPlaylist[i]);
56− }
57− priv.audio.playlist.addItems(urls);
58− priv.audio.playlist.currentIndex = sourceIndex;
59− } else {
60− priv.audio.playlist.addItem(newSource);
61− priv.audio.playlist.currentIndex = 0;
62− }
63− play();
64− }
65−
66− function stop() {
67− if (priv.audio) {
68− priv.audio.stop();
69− }
70− }
71−
72− function play() {
73− if (priv.audio) {
74− priv.audio.play();
75− }
76− }
77−
78− function pause() {
79− if (priv.audio) {
80− priv.audio.pause();
81− }
82− }
83−
84− property QtObject priv: QtObject {
85− id: priv
86− property Audio audio: null
87− property Component audioComponent: Component {
88− Audio {
89− playlist: Playlist {
90− objectName: "playlist"
91− }
92− /* Remove player in case of error so it gets recreated next time
93− * we need it. Happens if backend media player restarted, for
94− * instance. qtmultimedia should probably handle this
95− * transparently (LP: #1616425).
96− */
97− onError: {
98− console.warn("DashAudioPlayer: error event (" +
99− priv.audio.errorString + "), destroying");
100− priv.audio.destroy();
101− }
102− }
103− }
104− }
105−
106− function lengthToString(s) {
107− if (typeof(s) !== "number" || s < 0) return "";
108−
109− var sec = "" + s % 60;
110− if (sec.length == 1) sec = "0" + sec;
111− var hour = Math.floor(s / 3600);
112− if (hour < 1) {
113− return Math.floor(s / 60) + ":" + sec;
114− } else {
115− var min = "" + Math.floor(s / 60) % 60;
116− if (min.length == 1) min = "0" + min;
117− return hour + ":" + min + ":" + sec;
118− }
119− }
120−}
plugins/Dash/ScopeStyle.qmldeleted−95
@@ -1,95 +0,0 @@
1−/*
2− * Copyright 2014 Canonical Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−import QtQuick 2.4
18−import Utils 0.1
19−import Ubuntu.Components 1.3
20−
21−/*! \brief Helper for processing scope customization options.
22−
23− It will take the customizations object passed by the scope and
24− process it to provide the scope UI with data such as colors or
25− image paths.
26− */
27−
28−QtObject {
29− /// Style object passed from the scope
30− property var style: Object()
31−
32− /// Color used for text and symbolic icons
33− readonly property color foreground: "foreground-color" in style ? style["foreground-color"] : d.defaultDark
34−
35− /// Luminance of the foreground color
36− readonly property real foregroundLuminance: foreground ? Style.luminance(foreground) : Style.luminance(d.defaultDark)
37−
38− /// Color used for the overall background
39− readonly property color background: "background-color" in style ? style["background-color"] : "#00f5f5f5"
40−
41− /// Luminance of the background color
42− readonly property real backgroundLuminance: background ? Style.luminance(background) : Style.luminance(d.defaultLight)
43−
44− /*! \brief Get the most contrasting available color based on luminance
45− *
46− * If background color is transparent, theme provided colors are taken into account
47− */
48− function getTextColor(luminance) {
49− if (Math.abs(foregroundLuminance - luminance) >
50− Math.abs(d.opaqueBackgroundLuminance - luminance)) {
51− return foreground;
52− } else {
53− return d.opaqueBackground;
54− }
55− }
56−
57− /// Source of the logo image for the header
58− readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
59−
60− /// Background style for the header
61− readonly property url headerBackground: "background" in d.headerStyle ? d.headerStyle["background"] : "color:///#ffffff"
62−
63− /// Foreground color for the header
64− readonly property color headerForeground: "foreground-color" in d.headerStyle ? d.headerStyle["foreground-color"] : foreground
65−
66− /// Color of the header divider
67− readonly property color headerDividerColor: "divider-color" in d.headerStyle ? d.headerStyle["divider-color"] : "#e0e0e0"
68−
69− /// Background style for the navigation
70− readonly property url navigationBackground: "navigation-background" in d.headerStyle ? d.headerStyle["navigation-background"] : "color:///#f5f5f5"
71−
72− /// Color of the primary preview button
73− readonly property color previewButtonColor: "preview-button-color" in style ? style["preview-button-color"] : theme.palette.normal.positive
74−
75− //! @cond
76− property var d: QtObject {
77− // FIXME: should be taken from the theme
78− readonly property color defaultLight: "white"
79− readonly property color defaultDark: UbuntuColors.darkGrey
80− readonly property real defaultLightLuminance: Style.luminance(defaultLight)
81− readonly property real defaultDarkLuminance: Style.luminance(defaultDark)
82−
83− readonly property color opaqueBackground: {
84− background.a > 0 ?
85− background :
86− (Math.abs(foregroundLuminance - defaultLightLuminance) >
87− Math.abs(foregroundLuminance - defaultDarkLuminance)) ?
88− defaultLight : defaultDark
89− }
90− readonly property real opaqueBackgroundLuminance: Style.luminance(opaqueBackground)
91−
92− readonly property var headerStyle: "page-header" in style ? style["page-header"] : { }
93− }
94− //! @endcond
95−}
plugins/Dash/abstractdashview.cppdeleted−359
@@ -1,359 +0,0 @@
1−/*
2− * Copyright (C) 2013, 2014 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#include "abstractdashview.h"
18−
19−#include <private/qquickitem_p.h>
20−
21−AbstractDashView::AbstractDashView()
22− : m_delegateModel(nullptr)
23− , m_asyncRequestedIndex(-1)
24− , m_columnSpacing(0)
25− , m_rowSpacing(0)
26− , m_buffer(320) // Same value used in qquickitemview.cpp in Qt 5.4
27− , m_displayMarginBeginning(0)
28− , m_displayMarginEnd(0)
29− , m_needsRelayout(false)
30− , m_delegateValidated(false)
31− , m_implicitHeightDirty(false)
32−{
33− connect(this, &AbstractDashView::widthChanged, this, &AbstractDashView::relayout);
34− connect(this, &AbstractDashView::heightChanged, this, &AbstractDashView::onHeightChanged);
35−}
36−
37−QAbstractItemModel *AbstractDashView::model() const
38−{
39− return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
40−}
41−
42−void AbstractDashView::setModel(QAbstractItemModel *model)
43−{
44− if (model != this->model()) {
45− if (!m_delegateModel) {
46− createDelegateModel();
47− } else {
48− disconnect(m_delegateModel, &QQmlDelegateModel::modelUpdated, this, &AbstractDashView::onModelUpdated);
49− }
50− m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
51− connect(m_delegateModel, &QQmlDelegateModel::modelUpdated, this, &AbstractDashView::onModelUpdated);
52−
53− cleanupExistingItems();
54−
55− Q_EMIT modelChanged();
56− polish();
57− }
58−}
59−
60−QQmlComponent *AbstractDashView::delegate() const
61−{
62− return m_delegateModel ? m_delegateModel->delegate() : nullptr;
63−}
64−
65−void AbstractDashView::setDelegate(QQmlComponent *delegate)
66−{
67− if (delegate != this->delegate()) {
68− if (!m_delegateModel) {
69− createDelegateModel();
70− }
71−
72− cleanupExistingItems();
73−
74− m_delegateModel->setDelegate(delegate);
75−
76− Q_EMIT delegateChanged();
77− m_delegateValidated = false;
78− polish();
79− }
80−}
81−
82−qreal AbstractDashView::columnSpacing() const
83−{
84− return m_columnSpacing;
85−}
86−
87−void AbstractDashView::setColumnSpacing(qreal columnSpacing)
88−{
89− if (columnSpacing != m_columnSpacing) {
90− m_columnSpacing = columnSpacing;
91− Q_EMIT columnSpacingChanged();
92−
93− if (isComponentComplete()) {
94− relayout();
95− }
96− }
97−}
98−
99−qreal AbstractDashView::rowSpacing() const
100−{
101− return m_rowSpacing;
102−}
103−
104−void AbstractDashView::setRowSpacing(qreal rowSpacing)
105−{
106− if (rowSpacing != m_rowSpacing) {
107− m_rowSpacing = rowSpacing;
108− Q_EMIT rowSpacingChanged();
109−
110− if (isComponentComplete()) {
111− relayout();
112− }
113− }
114−}
115−
116−int AbstractDashView::cacheBuffer() const
117−{
118− return m_buffer;
119−}
120−
121−void AbstractDashView::setCacheBuffer(int buffer)
122−{
123− if (buffer < 0) {
124− qmlInfo(this) << "Cannot set a negative cache buffer";
125− return;
126− }
127−
128− if (m_buffer != buffer) {
129− m_buffer = buffer;
130− if (isComponentComplete()) {
131− polish();
132− }
133− emit cacheBufferChanged();
134− }
135−}
136−
137−qreal AbstractDashView::displayMarginBeginning() const
138−{
139− return m_displayMarginBeginning;
140−}
141−
142−void AbstractDashView::setDisplayMarginBeginning(qreal begin)
143−{
144− if (m_displayMarginBeginning == begin)
145− return;
146− m_displayMarginBeginning = begin;
147− if (isComponentComplete()) {
148− polish();
149− }
150− emit displayMarginBeginningChanged();
151−}
152−
153−qreal AbstractDashView::displayMarginEnd() const
154−{
155− return m_displayMarginEnd;
156−}
157−
158−void AbstractDashView::setDisplayMarginEnd(qreal end)
159−{
160− if (m_displayMarginEnd == end)
161− return;
162− m_displayMarginEnd = end;
163− if (isComponentComplete()) {
164− polish();
165− }
166− emit displayMarginEndChanged();
167−}
168−
169−void AbstractDashView::createDelegateModel()
170−{
171− m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
172− connect(m_delegateModel, &QQmlDelegateModel::createdItem, this, &AbstractDashView::itemCreated);
173− if (isComponentComplete())
174− m_delegateModel->componentComplete();
175−}
176−
177−void AbstractDashView::refill()
178−{
179− if (!isComponentComplete() || height() < 0) {
180− return;
181− }
182−
183− const qreal from = -m_displayMarginBeginning;
184− const qreal to = height() + m_displayMarginEnd;
185− const qreal bufferFrom = from - m_buffer;
186− const qreal bufferTo = to + m_buffer;
187−
188− bool added = addVisibleItems(from, to, false);
189− bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
190− added |= addVisibleItems(bufferFrom, bufferTo, true);
191−
192− if (added || removed) {
193− m_implicitHeightDirty = true;
194− polish();
195− }
196−}
197−
198−bool AbstractDashView::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
199−{
200− if (fillToY <= fillFromY)
201− return false;
202−
203− if (!delegate())
204− return false;
205−
206− if (m_delegateModel->count() == 0)
207− return false;
208−
209− int modelIndex;
210− qreal yPos;
211− findBottomModelIndexToAdd(&modelIndex, &yPos);
212− bool changed = false;
213− while (modelIndex < m_delegateModel->count() && yPos <= fillToY) {
214− if (!createItem(modelIndex, asynchronous))
215− break;
216−
217− changed = true;
218− findBottomModelIndexToAdd(&modelIndex, &yPos);
219− }
220−
221− findTopModelIndexToAdd(&modelIndex, &yPos);
222− while (modelIndex >= 0 && yPos > fillFromY) {
223− if (!createItem(modelIndex, asynchronous))
224− break;
225−
226− changed = true;
227− findTopModelIndexToAdd(&modelIndex, &yPos);
228− }
229−
230− return changed;
231−}
232−
233−QQuickItem *AbstractDashView::createItem(int modelIndex, bool asynchronous)
234−{
235− if (asynchronous && m_asyncRequestedIndex != -1)
236− return nullptr;
237−
238− m_asyncRequestedIndex = -1;
239− QObject* object = m_delegateModel->object(modelIndex, asynchronous);
240− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
241− if (!item) {
242− if (object) {
243− m_delegateModel->release(object);
244− if (!m_delegateValidated) {
245− m_delegateValidated = true;
246− QObject* delegateObj = delegate();
247− qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
248− }
249− } else {
250− m_asyncRequestedIndex = modelIndex;
251− }
252− return nullptr;
253− } else {
254− QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
255− addItemToView(modelIndex, item);
256− return item;
257− }
258−}
259−
260−void AbstractDashView::releaseItem(QQuickItem *item)
261−{
262− QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
263− QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
264− if (flags & QQmlDelegateModel::Destroyed) {
265− item->setParentItem(nullptr);
266− }
267−}
268−
269−void AbstractDashView::setImplicitHeightDirty()
270−{
271− m_implicitHeightDirty = true;
272−}
273−
274−void AbstractDashView::itemCreated(int modelIndex, QObject *object)
275−{
276− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
277− if (!item) {
278− qWarning() << "AbstractDashView::itemCreated got a non item for index" << modelIndex;
279− return;
280− }
281− item->setParentItem(this);
282−
283− // We only need to call createItem if we are here because of an asynchronous generation
284− // otherwise we are in this slot because createItem is creating the item sync
285− // and thus we don't need to call createItem again, nor need to set m_implicitHeightDirty
286− // and call polish because the sync createItem was called from addVisibleItems that
287− // is called from refill that will already do those if an item was added
288− if (modelIndex == m_asyncRequestedIndex) {
289− createItem(modelIndex, false);
290− m_implicitHeightDirty = true;
291− polish();
292− }
293−}
294−
295−void AbstractDashView::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
296−{
297− if (reset) {
298− cleanupExistingItems();
299− } else {
300− processModelRemoves(changeSet.removes());
301−
302− // The current AbstractDashViews do not support insertions that are not at the end
303− // so reset if that happens
304− Q_FOREACH(const QQmlChangeSet::Change insert, changeSet.inserts()) {
305− if (insert.index < m_delegateModel->count() - 1) {
306− cleanupExistingItems();
307− break;
308− }
309− }
310− }
311− polish();
312−}
313−
314−
315−void AbstractDashView::relayout()
316−{
317− m_needsRelayout = true;
318− polish();
319−}
320−
321−void AbstractDashView::onHeightChanged()
322−{
323− polish();
324−}
325−
326−void AbstractDashView::updatePolish()
327−{
328− if (!model())
329− return;
330−
331− if (m_needsRelayout) {
332− doRelayout();
333− m_needsRelayout = false;
334− m_implicitHeightDirty = true;
335− }
336−
337− refill();
338−
339− const qreal from = -m_displayMarginBeginning;
340− const qreal to = height() + m_displayMarginEnd;
341− updateItemCulling(from, to);
342−
343− if (m_implicitHeightDirty) {
344− calculateImplicitHeight();
345− m_implicitHeightDirty = false;
346− }
347−}
348−
349−void AbstractDashView::componentComplete()
350−{
351− if (m_delegateModel)
352− m_delegateModel->componentComplete();
353−
354− QQuickItem::componentComplete();
355−
356− m_needsRelayout = true;
357−
358− polish();
359−}
plugins/Dash/abstractdashview.hdeleted−128
@@ -1,128 +0,0 @@
1−/*
2− * Copyright (C) 2013, 2014 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#ifndef ABSTRACTDASHVIEW_H
18−#define ABSTRACTDASHVIEW_H
19−
20−#include <QQuickItem>
21−
22−class QAbstractItemModel;
23−class QQmlComponent;
24−
25−#include <private/qqmldelegatemodel_p.h>
26−#include <private/qquickitemchangelistener_p.h>
27−#include <qqmlinfo.h>
28−
29−class AbstractDashView : public QQuickItem, public QQuickItemChangeListener
30−{
31− Q_OBJECT
32−
33− Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
34− Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
35− Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
36− Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
37− Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
38− Q_PROPERTY(qreal displayMarginBeginning READ displayMarginBeginning
39− WRITE setDisplayMarginBeginning
40− NOTIFY displayMarginBeginningChanged)
41− Q_PROPERTY(qreal displayMarginEnd READ displayMarginEnd
42− WRITE setDisplayMarginEnd
43− NOTIFY displayMarginEndChanged)
44−
45−friend class VerticalJournalTest;
46−friend class HorizontalJournalTest;
47−friend class OrganicGridTest;
48−
49−public:
50− AbstractDashView();
51−
52− QAbstractItemModel *model() const;
53− void setModel(QAbstractItemModel *model);
54−
55− QQmlComponent *delegate() const;
56− void setDelegate(QQmlComponent *delegate);
57−
58− qreal columnSpacing() const;
59− void setColumnSpacing(qreal columnSpacing);
60−
61− qreal rowSpacing() const;
62− void setRowSpacing(qreal rowSpacing);
63−
64− int cacheBuffer() const;
65− void setCacheBuffer(int);
66−
67− qreal displayMarginBeginning() const;
68− void setDisplayMarginBeginning(qreal);
69−
70− qreal displayMarginEnd() const;
71− void setDisplayMarginEnd(qreal);
72−
73−Q_SIGNALS:
74− void modelChanged();
75− void delegateChanged();
76− void columnSpacingChanged();
77− void rowSpacingChanged();
78− void cacheBufferChanged();
79− void displayMarginBeginningChanged();
80− void displayMarginEndChanged();
81−
82−protected Q_SLOTS:
83− void relayout();
84−
85−protected:
86− void updatePolish() override;
87− void componentComplete() override;
88−
89− void releaseItem(QQuickItem *item);
90− void setImplicitHeightDirty();
91−
92−private Q_SLOTS:
93− void itemCreated(int modelIndex, QObject *object);
94− void onModelUpdated(const QQmlChangeSet &changeSet, bool reset);
95− void onHeightChanged();
96−
97−private:
98− void createDelegateModel();
99− void refill();
100− bool addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous);
101− QQuickItem *createItem(int modelIndex, bool asynchronous);
102−
103− virtual void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
104− virtual void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
105− virtual void addItemToView(int modelIndex, QQuickItem *item) = 0;
106− virtual bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) = 0;
107− virtual void cleanupExistingItems() = 0;
108− virtual void doRelayout() = 0;
109− virtual void updateItemCulling(qreal visibleFromY, qreal visibleToY) = 0;
110− virtual void calculateImplicitHeight() = 0;
111− virtual void processModelRemoves(const QVector<QQmlChangeSet::Change> &removes) = 0;
112−
113− QQmlDelegateModel *m_delegateModel;
114−
115− // Index we are waiting because we requested it asynchronously
116− int m_asyncRequestedIndex;
117−
118− int m_columnSpacing;
119− int m_rowSpacing;
120− int m_buffer;
121− qreal m_displayMarginBeginning;
122− qreal m_displayMarginEnd;
123− bool m_needsRelayout;
124− bool m_delegateValidated;
125− bool m_implicitHeightDirty;
126−};
127−
128−#endif
plugins/Dash/graphics/music_progress_bg.pngdeleted

Binary file not shown.

plugins/Dash/horizontaljournal.cppdeleted−239
@@ -1,239 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−/*
18− * The implementation is centered around m_visibleItems
19− * that a list for each of the items in the view.
20− * m_firstVisibleIndex is the index of the first item in m_visibleItems
21− * m_lastInRowIndexPosition is a map that contains the x position
22− * of items that are the last ones of a row so we can reconstruct the rows
23− * when building back
24− */
25−
26−#include "horizontaljournal.h"
27−
28−#include <qqmlengine.h>
29−#include <qqmlinfo.h>
30−#include <private/qqmldelegatemodel_p.h>
31−#include <private/qquickitem_p.h>
32−
33−HorizontalJournal::HorizontalJournal()
34− : m_firstVisibleIndex(-1)
35− , m_rowHeight(0)
36−{
37−}
38−
39−qreal HorizontalJournal::rowHeight() const
40−{
41− return m_rowHeight;
42−}
43−
44−void HorizontalJournal::setRowHeight(qreal rowHeight)
45−{
46− if (rowHeight != m_rowHeight) {
47− m_rowHeight = rowHeight;
48− Q_EMIT rowHeightChanged();
49−
50− if (isComponentComplete()) {
51− Q_FOREACH(QQuickItem *item, m_visibleItems) {
52− item->setHeight(rowHeight);
53− }
54− relayout();
55− }
56− }
57−}
58−
59−void HorizontalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
60−{
61− if (m_visibleItems.isEmpty()) {
62− *modelIndex = 0;
63− *yPos = 0;
64− } else {
65− *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
66− if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
67− *yPos = m_visibleItems.last()->y() + m_rowHeight + rowSpacing();
68− } else {
69− *yPos = m_visibleItems.last()->y();
70− }
71− }
72−}
73−
74−void HorizontalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
75−{
76− if (m_visibleItems.isEmpty()) {
77− *modelIndex = -1;
78− *yPos = INT_MIN;
79− } else {
80− *modelIndex = m_firstVisibleIndex - 1;
81− if (m_lastInRowIndexPosition.contains(*modelIndex)) {
82− *yPos = m_visibleItems.first()->y() - rowSpacing() - m_rowHeight;
83− } else {
84− *yPos = m_visibleItems.first()->y();
85− }
86− }
87−}
88−
89−bool HorizontalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
90−{
91− bool changed = false;
92−
93− while (!m_visibleItems.isEmpty() && m_visibleItems.first()->y() + m_rowHeight < bufferFromY) {
94− releaseItem(m_visibleItems.takeFirst());
95− changed = true;
96− m_firstVisibleIndex++;
97− }
98−
99− while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferToY) {
100− releaseItem(m_visibleItems.takeLast());
101− changed = true;
102− m_lastInRowIndexPosition.remove(m_firstVisibleIndex + m_visibleItems.count());
103− }
104−
105− if (m_visibleItems.isEmpty()) {
106− m_firstVisibleIndex = -1;
107− }
108−
109− return changed;
110−}
111−
112−void HorizontalJournal::addItemToView(int modelIndex, QQuickItem *item)
113−{
114− if (item->height() != m_rowHeight) {
115− qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
116− item->setHeight(m_rowHeight);
117− }
118−
119− if (m_visibleItems.isEmpty()) {
120− Q_ASSERT(modelIndex == 0);
121− item->setY(0);
122− item->setX(0);
123− m_visibleItems << item;
124− m_firstVisibleIndex = 0;
125− } else {
126− // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
127− if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
128− QQuickItem *lastItem = m_visibleItems.last();
129− if (lastItem->x() + lastItem->width() + columnSpacing() + item->width() <= width()) {
130− // Fits in the row
131− item->setY(lastItem->y());
132− item->setX(lastItem->x() + lastItem->width() + columnSpacing());
133− } else {
134− // Starts a new row
135− item->setY(lastItem->y() + m_rowHeight + rowSpacing());
136− item->setX(0);
137− m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
138− }
139− m_visibleItems << item;
140− } else if (modelIndex == m_firstVisibleIndex - 1) {
141− QQuickItem *firstItem = m_visibleItems.first();
142− if (m_lastInRowIndexPosition.contains(modelIndex)) {
143− // It is the last item of its row, so start a new one since we're going back
144− item->setY(firstItem->y() - rowSpacing() - m_rowHeight);
145− item->setX(m_lastInRowIndexPosition[modelIndex]);
146− } else {
147− item->setY(firstItem->y());
148− item->setX(firstItem->x() - columnSpacing() - item->width());
149− }
150− m_firstVisibleIndex = modelIndex;
151− m_visibleItems.prepend(item);
152− } else {
153− qWarning() << "HorizontalJournal::addItemToView - Got unexpected modelIndex"
154− << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
155− }
156− }
157−}
158−
159−void HorizontalJournal::cleanupExistingItems()
160−{
161− // Cleanup the existing items
162− Q_FOREACH(QQuickItem *item, m_visibleItems)
163− releaseItem(item);
164− m_visibleItems.clear();
165− m_lastInRowIndexPosition.clear();
166− m_firstVisibleIndex = -1;
167− setImplicitHeightDirty();
168−}
169−
170−void HorizontalJournal::calculateImplicitHeight()
171−{
172− if (m_firstVisibleIndex >= 0) {
173− const int nIndexes = m_firstVisibleIndex + m_visibleItems.count();
174− const double bottomMostY = m_visibleItems.last()->y() + m_rowHeight;
175− const double averageHeight = bottomMostY / nIndexes;
176− setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - nIndexes));
177− } else {
178− setImplicitHeight(0);
179− }
180−}
181−
182−void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
183−{
184− Q_FOREACH(const QQmlChangeSet::Change remove, removes) {
185− for (int i = remove.count - 1; i >= 0; --i) {
186− const int indexToRemove = remove.index + i;
187− // We only support removing from the end so
188− // any of the last items of a column has to be indexToRemove
189− const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
190− if (indexToRemove == lastIndex) {
191− releaseItem(m_visibleItems.takeLast());
192− m_lastInRowIndexPosition.remove(indexToRemove);
193− } else {
194− if (indexToRemove < lastIndex) {
195− qDebug() << "HorizontalJournal only supports removal from the end of the model, resetting instead";
196− cleanupExistingItems();
197− break;
198− } else {
199− setImplicitHeightDirty();
200− }
201− }
202− }
203− }
204− if (m_visibleItems.isEmpty()) {
205− m_firstVisibleIndex = -1;
206− }
207−}
208−
209−
210−void HorizontalJournal::doRelayout()
211−{
212− // If m_firstVisibleIndex is not 0 we need to drop all the delegates
213− // since we can't consistently relayout without the first item being there
214−
215− if (m_firstVisibleIndex == 0) {
216− int i = 0;
217− const QList<QQuickItem*> allItems = m_visibleItems;
218− m_visibleItems.clear();
219− m_lastInRowIndexPosition.clear();
220− Q_FOREACH(QQuickItem *item, allItems) {
221− addItemToView(i, item);
222− ++i;
223− }
224− } else {
225− Q_FOREACH(QQuickItem *item, m_visibleItems) {
226− releaseItem(item);
227− }
228− m_visibleItems.clear();
229− m_lastInRowIndexPosition.clear();
230− m_firstVisibleIndex = -1;
231− }
232−}
233−
234−void HorizontalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
235−{
236− Q_FOREACH(QQuickItem *item, m_visibleItems) {
237− QQuickItemPrivate::get(item)->setCulled(item->y() + m_rowHeight <= visibleFromY || item->y() >= visibleToY);
238− }
239−}
plugins/Dash/horizontaljournal.hdeleted−63
@@ -1,63 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#ifndef HORIZONTALJOURNAL_H
18−#define HORIZONTALJOURNAL_H
19−
20−#include "abstractdashview.h"
21−
22− /** A horizontal journal is a view that creates delegates
23− * based on a model and layouts them one after the other
24− * in the same row until there is no more free space for the next item
25− * and so that item is layouted in the next row
26− *
27− * All delegates are forced to rowHeight if they don't have it.
28− */
29− class HorizontalJournal : public AbstractDashView
30−{
31− Q_OBJECT
32−
33− Q_PROPERTY(qreal rowHeight READ rowHeight WRITE setRowHeight NOTIFY rowHeightChanged)
34−
35−friend class HorizontalJournalTest;
36−
37−public:
38− HorizontalJournal();
39−
40− qreal rowHeight() const;
41− void setRowHeight(qreal rowHeight);
42−
43−Q_SIGNALS:
44− void rowHeightChanged();
45−
46−private:
47− void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
48− void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
49− bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
50− void addItemToView(int modelIndex, QQuickItem *item) override;
51− void cleanupExistingItems() override;
52− void calculateImplicitHeight() override;
53− void doRelayout() override;
54− void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
55− void processModelRemoves(const QVector<QQmlChangeSet::Change> &removes) override;
56−
57− int m_firstVisibleIndex;
58− QList<QQuickItem*> m_visibleItems;
59− QMap<int, double> m_lastInRowIndexPosition;
60− int m_rowHeight;
61−};
62−
63−#endif
plugins/Dash/listviewwithpageheader.cppdeleted−1394
@@ -1,1394 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−/*
18− * Some documentation on how this thing works:
19− *
20− * A flickable has two very important concepts that define the top and
21− * height of the flickable area.
22− * The top is returned in minYExtent()
23− * The height is set using setContentHeight()
24− * By changing those two values we can make the list grow up or down
25− * as needed. e.g. if we are in the middle of the list
26− * and something that is above the viewport grows, since we do not
27− * want to change the viewport because of that we just adjust the
28− * minYExtent so that the list grows up.
29− *
30− * The implementation on the list relies on the delegateModel doing
31− * most of the instantiation work. You call createItem() when you
32− * need to create an item asking for it async or not. If returns null
33− * it means the item will be created async and the model will call the
34− * itemCreated slot with the item.
35− *
36− * updatePolish is the central point of dispatch for the work of the
37− * class. It is called by the scene graph just before drawing the class.
38− * In it we:
39− * * Make sure all items are positioned correctly
40− * * Add/Remove items if needed
41− * * Update the content height if it was dirty
42− *
43− * m_visibleItems contains all the items we have created at the moment.
44− * Actually not all of them are visible since it includes the ones
45− * in the cache area we create asynchronously to help performance.
46− * The first item in m_visibleItems has the m_firstVisibleIndex in
47− * the model. If you actually want to know what is the first
48− * item in the viewport you have to find the first non culled element
49− * in m_visibleItems
50− *
51− * All the items (except the header) are childs of m_clipItem which
52− * is a child of the contentItem() of the flickable (The contentItem()
53− * is what actually 'moves' in a a flickable). This way
54− * we can implement the clipping needed so we can have the header
55− * shown in the middle of the list over the items without the items
56− * leaking under the header in case it is transparent.
57− *
58− * The first item of m_visibleItems is the one that defines the
59− * positions of all the rest of items (see updatePolish()) and
60− * this is why sometimes we move it even if it's not the item
61− * that has triggered the function (i.e. in itemGeometryChanged())
62− *
63− * m_visibleItems is a list of ListItem. Each ListItem
64− * will contain a item and potentially a sectionItem. The sectionItem
65− * is only there when the list is using sectionDelegate+sectionProperty
66− * and this is the first item of the section. Each ListItem is vertically
67− * layouted with the sectionItem first and then the item.
68− *
69− * For sectioning we also have a section item alone (m_topSectionItem)
70− * that is used for the cases we need to show the sticky section item at
71− * the top of the view.
72− *
73− * Each delegate item has a context property called heightToClip that is
74− * used to communicate to the delegate implementation in case it has to
75− * clip itself because of overlapping with the top sticky section item.
76− * This is an implementation decision since it has been agreed it
77− * is easier to implement the clipping in QML with this info than to
78− * do it at the C++ level.
79− *
80− * Note that minYExtent and height are not always totally accurate, since
81− * we don't have the items created we can't guess their heights
82− * so we can only guarantee the values are correct when the first/last
83− * items of the list are visible, otherwise we just live with good enough
84− * values that make the list scrollable
85− *
86− * There are a few things that are not really implemented or tested properly
87− * which we don't use at the moment like changing the model, changing
88− * the section delegate, having a section delegate that changes its size, etc.
89− * The known missing features are marked with TODOs along the code.
90− */
91−
92−#include "listviewwithpageheader.h"
93−
94−#include <QCoreApplication>
95−#include <QDebug>
96−#include <qqmlinfo.h>
97−#include <qqmlengine.h>
98−#include <private/qqmlcontext_p.h>
99−#include <private/qqmldelegatemodel_p.h>
100−#include <private/qqmlglobal_p.h>
101−#include <private/qquickitem_p.h>
102−#include <private/qquickanimation_p.h>
103−// #include <private/qquickrectangle_p.h>
104−
105−qreal ListViewWithPageHeader::ListItem::height() const
106−{
107− return m_item->height() + (m_sectionItem ? m_sectionItem->height() : 0);
108−}
109−
110−qreal ListViewWithPageHeader::ListItem::y() const
111−{
112− return m_item->y() - (m_sectionItem ? m_sectionItem->height() : 0);
113−}
114−
115−void ListViewWithPageHeader::ListItem::setY(qreal newY)
116−{
117− if (m_sectionItem) {
118− m_sectionItem->setY(newY);
119− m_item->setY(newY + m_sectionItem->height());
120− } else {
121− m_item->setY(newY);
122− }
123−}
124−
125−bool ListViewWithPageHeader::ListItem::culled() const
126−{
127− return QQuickItemPrivate::get(m_item)->culled;
128−}
129−
130−void ListViewWithPageHeader::ListItem::setCulled(bool culled)
131−{
132− QQuickItemPrivate::get(m_item)->setCulled(culled);
133− if (m_sectionItem)
134− QQuickItemPrivate::get(m_sectionItem)->setCulled(culled);
135−}
136−
137−void ListViewWithPageHeader::ListItem::setSectionItem(QQuickItem *sectionItem)
138−{
139− m_sectionItem = sectionItem;
140−}
141−
142−ListViewWithPageHeader::ListViewWithPageHeader()
143− : m_delegateModel(nullptr)
144− , m_asyncRequestedIndex(-1)
145− , m_delegateValidated(false)
146− , m_firstVisibleIndex(-1)
147− , m_minYExtent(0)
148− , m_contentHeightDirty(false)
149− , m_headerItem(nullptr)
150− , m_previousContentY(0)
151− , m_headerItemShownHeight(0)
152− , m_sectionDelegate(nullptr)
153− , m_topSectionItem(nullptr)
154− , m_forceNoClip(false)
155− , m_inLayout(false)
156− , m_inContentHeightKeepHeaderShown(false)
157− , m_cacheBuffer(0)
158−{
159− m_clipItem = new QQuickItem(contentItem());
160−// m_clipItem = new QQuickRectangle(contentItem());
161−// ((QQuickRectangle*)m_clipItem)->setColor(Qt::gray);
162−
163− m_contentYAnimation = new QQuickNumberAnimation(this);
164− m_contentYAnimation->setEasing(QEasingCurve::OutQuad);
165− m_contentYAnimation->setProperty(QStringLiteral("contentY"));
166− m_contentYAnimation->setDuration(200);
167− m_contentYAnimation->setTargetObject(this);
168−
169− connect(contentItem(), &QQuickItem::widthChanged, this, &ListViewWithPageHeader::onContentWidthChanged);
170− connect(this, &ListViewWithPageHeader::contentHeightChanged, this, &ListViewWithPageHeader::onContentHeightChanged);
171− connect(this, &ListViewWithPageHeader::heightChanged, this, &ListViewWithPageHeader::onHeightChanged);
172− connect(m_contentYAnimation, &QQuickNumberAnimation::runningChanged, this, &ListViewWithPageHeader::contentYAnimationRunningChanged);
173−
174− setFlickableDirection(VerticalFlick);
175−}
176−
177−ListViewWithPageHeader::~ListViewWithPageHeader()
178−{
179−}
180−
181−QAbstractItemModel *ListViewWithPageHeader::model() const
182−{
183− return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
184−}
185−
186−void ListViewWithPageHeader::setModel(QAbstractItemModel *model)
187−{
188− if (model != this->model()) {
189− if (!m_delegateModel) {
190− createDelegateModel();
191− } else {
192− disconnect(m_delegateModel, &QQmlDelegateModel::modelUpdated, this, &ListViewWithPageHeader::onModelUpdated);
193− }
194− m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
195− connect(m_delegateModel, &QQmlDelegateModel::modelUpdated, this, &ListViewWithPageHeader::onModelUpdated);
196− Q_EMIT modelChanged();
197− polish();
198− // TODO?
199−// Q_EMIT contentHeightChanged();
200−// Q_EMIT contentYChanged();
201− }
202−}
203−
204−QQmlComponent *ListViewWithPageHeader::delegate() const
205−{
206− return m_delegateModel ? m_delegateModel->delegate() : nullptr;
207−}
208−
209−void ListViewWithPageHeader::setDelegate(QQmlComponent *delegate)
210−{
211− if (delegate != this->delegate()) {
212− if (!m_delegateModel) {
213− createDelegateModel();
214− }
215−
216− // Cleanup the existing items
217− Q_FOREACH(ListItem *item, m_visibleItems)
218− releaseItem(item);
219− m_visibleItems.clear();
220− initializeValuesForEmptyList();
221−
222− m_delegateModel->setDelegate(delegate);
223−
224− Q_EMIT delegateChanged();
225− m_delegateValidated = false;
226− m_contentHeightDirty = true;
227− polish();
228− }
229−}
230−
231−void ListViewWithPageHeader::initializeValuesForEmptyList()
232−{
233− m_firstVisibleIndex = -1;
234− adjustMinYExtent();
235− setContentY(0);
236− m_clipItem->setY(0);
237− if (m_topSectionItem) {
238− QQuickItemPrivate::get(m_topSectionItem)->setCulled(true);
239− }
240−}
241−
242−QQuickItem *ListViewWithPageHeader::header() const
243−{
244− return m_headerItem;
245−}
246−
247−void ListViewWithPageHeader::setHeader(QQuickItem *headerItem)
248−{
249− if (m_headerItem != headerItem) {
250− qreal oldHeaderHeight = 0;
251− qreal oldHeaderY = 0;
252− if (m_headerItem) {
253− oldHeaderHeight = m_headerItem->height();
254− oldHeaderY = m_headerItem->y();
255− m_headerItem->setParentItem(nullptr);
256− QQuickItemPrivate::get(m_headerItem)->removeItemChangeListener(this, QQuickItemPrivate::ImplicitHeight);
257− }
258− m_headerItem = headerItem;
259− if (m_headerItem) {
260− m_headerItem->setParentItem(contentItem());
261− m_headerItem->setZ(1);
262− m_previousHeaderImplicitHeight = m_headerItem->implicitHeight();
263− QQuickItemPrivate::get(m_headerItem)->addItemChangeListener(this, QQuickItemPrivate::ImplicitHeight);
264− }
265− qreal newHeaderHeight = m_headerItem ? m_headerItem->height() : 0;
266− if (!m_visibleItems.isEmpty() && newHeaderHeight != oldHeaderHeight) {
267− headerHeightChanged(newHeaderHeight, oldHeaderHeight, oldHeaderY);
268− polish();
269− m_contentHeightDirty = true;
270− }
271− Q_EMIT headerChanged();
272− }
273−}
274−
275−QQmlComponent *ListViewWithPageHeader::sectionDelegate() const
276−{
277− return m_sectionDelegate;
278−}
279−
280−void ListViewWithPageHeader::setSectionDelegate(QQmlComponent *delegate)
281−{
282− if (delegate != m_sectionDelegate) {
283− // TODO clean existing sections
284−
285− m_sectionDelegate = delegate;
286−
287− m_topSectionItem = getSectionItem(QString(), false /*watchGeometry*/);
288− if (m_topSectionItem) {
289− m_topSectionItem->setZ(3);
290− QQuickItemPrivate::get(m_topSectionItem)->setCulled(true);
291− connect(m_topSectionItem, &QQuickItem::heightChanged, this, &ListViewWithPageHeader::stickyHeaderHeightChanged);
292− }
293−
294− // TODO create sections for existing items
295−
296− Q_EMIT sectionDelegateChanged();
297− Q_EMIT stickyHeaderHeightChanged();
298− }
299−}
300−
301−QString ListViewWithPageHeader::sectionProperty() const
302−{
303− return m_sectionProperty;
304−}
305−
306−void ListViewWithPageHeader::setSectionProperty(const QString &property)
307−{
308− if (property != m_sectionProperty) {
309− m_sectionProperty = property;
310−
311− updateWatchedRoles();
312−
313− // TODO recreate sections
314−
315− Q_EMIT sectionPropertyChanged();
316− }
317−}
318−
319−bool ListViewWithPageHeader::forceNoClip() const
320−{
321− return m_forceNoClip;
322−}
323−
324−void ListViewWithPageHeader::setForceNoClip(bool noClip)
325−{
326− if (noClip != m_forceNoClip) {
327− m_forceNoClip = noClip;
328− updateClipItem();
329− Q_EMIT forceNoClipChanged();
330− }
331−}
332−
333−int ListViewWithPageHeader::stickyHeaderHeight() const
334−{
335− return m_topSectionItem ? m_topSectionItem->height() : 0;
336−}
337−
338−qreal ListViewWithPageHeader::headerItemShownHeight() const
339−{
340− return m_headerItemShownHeight;
341−}
342−
343−int ListViewWithPageHeader::cacheBuffer() const
344−{
345− return m_cacheBuffer;
346−}
347−
348−void ListViewWithPageHeader::setCacheBuffer(int cacheBuffer)
349−{
350− if (cacheBuffer < 0) {
351− qmlInfo(this) << "Cannot set a negative cache buffer";
352− return;
353− }
354−
355− if (cacheBuffer != m_cacheBuffer) {
356− m_cacheBuffer = cacheBuffer;
357− Q_EMIT cacheBufferChanged();
358− polish();
359− }
360−}
361−
362−void ListViewWithPageHeader::positionAtBeginning()
363−{
364− if (m_delegateModel->count() <= 0)
365− return;
366−
367− qreal headerHeight = (m_headerItem ? m_headerItem->height() : 0);
368− if (m_firstVisibleIndex != 0) {
369− // TODO This could be optimized by trying to reuse the interesection
370− // of items that may end up intersecting between the existing
371− // m_visibleItems and the items we are creating in the next loop
372− Q_FOREACH(ListItem *item, m_visibleItems)
373− releaseItem(item);
374− m_visibleItems.clear();
375− m_firstVisibleIndex = -1;
376−
377− // Create the item 0, it will be already correctly positioned at createItem()
378− m_clipItem->setY(0);
379− ListItem *item = createItem(0, false);
380− // Create the subsequent items
381− int modelIndex = 1;
382− qreal pos = item->y() + item->height();
383− const qreal bufferTo = height() + m_cacheBuffer;
384− while (modelIndex < m_delegateModel->count() && pos <= bufferTo) {
385− if (!(item = createItem(modelIndex, false)))
386− break;
387− pos += item->height();
388− ++modelIndex;
389− }
390−
391− m_previousContentY = m_visibleItems.first()->y() - headerHeight;
392− }
393− setContentY(m_visibleItems.first()->y() + m_clipItem->y() - headerHeight);
394− if (m_headerItem) {
395− // TODO This should not be needed and the code that adjust the m_headerItem position
396− // in viewportMoved() should be enough but in some cases we have not found a way to reproduce
397− // yet the code of viewportMoved() fails so here we make sure that at least if we are calling
398− // positionAtBeginning the header item will be correctly positioned
399− m_headerItem->setY(-m_minYExtent);
400− }
401−}
402−
403−static inline bool uFuzzyCompare(qreal r1, qreal r2)
404−{
405− return qFuzzyCompare(r1, r2) || (qFuzzyIsNull(r1) && qFuzzyIsNull(r2));
406−}
407−
408−void ListViewWithPageHeader::showHeader()
409−{
410− if (!m_headerItem)
411− return;
412−
413− const auto to = qMax(-minYExtent(), contentY() - m_headerItem->height() + m_headerItemShownHeight);
414− if (!uFuzzyCompare(to, contentY())) {
415− const bool headerShownByItsOwn = contentY() < m_headerItem->y() + m_headerItem->height();
416− if (headerShownByItsOwn && m_headerItemShownHeight == 0) {
417− // We are not clipping since we are just at the top of the viewport
418− // but because of the showHeader animation we will need to, so
419− // enable the clipping without logically moving the items
420− m_headerItemShownHeight = m_headerItem->y() + m_headerItem->height() - contentY();
421− if (!m_visibleItems.isEmpty()) {
422− updateClipItem();
423− ListItem *firstItem = m_visibleItems.first();
424− firstItem->setY(firstItem->y() - m_headerItemShownHeight);
425− layout();
426− }
427− Q_EMIT headerItemShownHeightChanged();
428− }
429− m_contentYAnimation->setTo(to);
430− contentYAnimationType = ContentYAnimationShowHeader;
431− m_contentYAnimation->start();
432− }
433−}
434−
435−int ListViewWithPageHeader::firstCreatedIndex() const
436−{
437− return m_firstVisibleIndex;
438−}
439−
440−int ListViewWithPageHeader::createdItemCount() const
441−{
442− return m_visibleItems.count();
443−}
444−
445−QQuickItem *ListViewWithPageHeader::item(int modelIndex) const
446−{
447− ListItem *item = itemAtIndex(modelIndex);
448− if (item)
449− return item->m_item;
450− else
451− return nullptr;
452−}
453−
454−bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex)
455−{
456− ListItem *listItem = itemAtIndex(modelIndex);
457− if (listItem) {
458− return maximizeVisibleArea(listItem, listItem->height());
459− }
460−
461− return false;
462−}
463−
464−bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex, int itemHeight)
465−{
466− if (itemHeight < 0)
467− return false;
468−
469− ListItem *listItem = itemAtIndex(modelIndex);
470− if (listItem) {
471− return maximizeVisibleArea(listItem, itemHeight + (listItem->sectionItem() ? listItem->sectionItem()->height() : 0));
472− }
473−
474− return false;
475−}
476−
477−bool ListViewWithPageHeader::maximizeVisibleArea(ListItem *listItem, int listItemHeight)
478−{
479− if (listItem) {
480− layout();
481− const auto listItemY = m_clipItem->y() + listItem->y();
482− if (listItemY > contentY() && listItemY + listItemHeight > contentY() + height()) {
483− // we can scroll the list up to show more stuff
484− const auto to = qMin(listItemY, listItemY + listItemHeight - height());
485− m_contentYAnimation->setTo(to);
486− contentYAnimationType = ContentYAnimationMaximizeVisibleArea;
487− m_contentYAnimation->start();
488− } else if ((listItemY < contentY() && listItemY + listItemHeight < contentY() + height()) ||
489− (m_topSectionItem && !listItem->sectionItem() && listItemY - m_topSectionItem->height() < contentY() && listItemY + listItemHeight < contentY() + height()))
490− {
491− // we can scroll the list down to show more stuff
492− auto realVisibleListItemY = listItemY;
493− if (m_topSectionItem) {
494− // If we are showing the top section sticky item and this item doesn't have a section
495− // item we have to make sure to scroll it a bit more so that it is not underlapping
496− // the top section sticky item
497− bool topSectionShown = !QQuickItemPrivate::get(m_topSectionItem)->culled;
498− if (topSectionShown && !listItem->sectionItem()) {
499− realVisibleListItemY -= m_topSectionItem->height();
500− }
501− }
502− const auto to = qMax(realVisibleListItemY, listItemY + listItemHeight - height());
503− m_contentYAnimation->setTo(to);
504− contentYAnimationType = ContentYAnimationMaximizeVisibleArea;
505− m_contentYAnimation->start();
506− }
507− return true;
508− }
509− return false;
510−}
511−
512−qreal ListViewWithPageHeader::minYExtent() const
513−{
514−// qDebug() << "ListViewWithPageHeader::minYExtent" << m_minYExtent;
515− return m_minYExtent;
516−}
517−
518−qreal ListViewWithPageHeader::maxYExtent() const
519−{
520− return height() - contentHeight();
521−}
522−
523−void ListViewWithPageHeader::componentComplete()
524−{
525− if (m_delegateModel)
526− m_delegateModel->componentComplete();
527−
528− QQuickFlickable::componentComplete();
529−
530− polish();
531−}
532−
533−void ListViewWithPageHeader::viewportMoved(Qt::Orientations orient)
534−{
535− // Check we are not being taken down and don't paint anything
536− // TODO Check if we still need this in 5.2
537− // For reproduction just inifnite loop testDash or testDashContent
538− if (!QQmlEngine::contextForObject(this)->parentContext())
539− return;
540−
541− QQuickFlickable::viewportMoved(orient);
542−// qDebug() << "ListViewWithPageHeader::viewportMoved" << contentY();
543− const qreal diff = m_previousContentY - contentY();
544− adjustHeader(diff);
545− m_previousContentY = contentY();
546− layout();
547− polish();
548−}
549−
550−void ListViewWithPageHeader::adjustHeader(qreal diff)
551−{
552− const bool showHeaderAnimationRunning = m_contentYAnimation->isRunning() && contentYAnimationType == ContentYAnimationShowHeader;
553− if (m_headerItem) {
554− const auto oldHeaderItemShownHeight = m_headerItemShownHeight;
555− if (uFuzzyCompare(contentY(), -m_minYExtent) || contentY() > -m_minYExtent) {
556− m_headerItem->setHeight(m_headerItem->implicitHeight());
557− // We are going down (but it's not because of the rebound at the end)
558− // (but the header was not shown by it's own position)
559− // or the header is partially shown and we are not doing a maximizeVisibleArea either
560− const bool scrolledUp = m_previousContentY > contentY();
561− const bool notRebounding = qRound(contentY() + height()) < qRound(contentHeight());
562− const bool notShownByItsOwn = contentY() + diff >= m_headerItem->y() + m_headerItem->height();
563− const bool maximizeVisibleAreaRunning = m_contentYAnimation->isRunning() && contentYAnimationType == ContentYAnimationMaximizeVisibleArea;
564−
565− if (!scrolledUp && (contentY() == -m_minYExtent || (m_headerItemShownHeight == 0 && m_previousContentY == m_headerItem->y()))) {
566− m_headerItemShownHeight = 0;
567− m_headerItem->setY(-m_minYExtent);
568− } else if ((scrolledUp && notRebounding && notShownByItsOwn && !maximizeVisibleAreaRunning) || (m_headerItemShownHeight > 0) || m_inContentHeightKeepHeaderShown) {
569− if (maximizeVisibleAreaRunning && diff > 0) {
570− // If we are maximizing and the header was shown, make sure we hide it
571− m_headerItemShownHeight -= diff;
572− } else {
573− m_headerItemShownHeight += diff;
574− }
575− if (uFuzzyCompare(contentY(), -m_minYExtent)) {
576− m_headerItemShownHeight = 0;
577− } else {
578− m_headerItemShownHeight = qBound(static_cast<qreal>(0.), m_headerItemShownHeight, m_headerItem->height());
579− }
580− if (m_headerItemShownHeight > 0) {
581− if (uFuzzyCompare(m_headerItem->height(), m_headerItemShownHeight)) {
582− m_headerItem->setY(contentY());
583− m_headerItemShownHeight = m_headerItem->height();
584− } else {
585− m_headerItem->setY(contentY() - m_headerItem->height() + m_headerItemShownHeight);
586− }
587− } else {
588− m_headerItem->setY(-m_minYExtent);
589− }
590− } else if (m_headerItemShownHeight == 0 && m_previousContentY > m_headerItem->y() && contentY() < m_headerItem->y()) {
591− // The header was hidden but now that we've moved up (e.g. because of item removed) it's visible
592− // make sure it isn't
593− m_headerItem->setY(-m_minYExtent);
594− }
595− Q_EMIT headerItemShownHeightChanged();
596− } else {
597− // Stick the header item to the top when dragging down
598− m_headerItem->setY(contentY());
599− m_headerItem->setHeight(m_headerItem->implicitHeight() + (-m_minYExtent - contentY()));
600− }
601− // We will be changing the clip item, need to accomadate for it
602− // otherwise we move the firstItem down/up twice (unless the
603− // show header animation is running, where we want to keep the viewport stable)
604− if (!showHeaderAnimationRunning) {
605− diff += oldHeaderItemShownHeight - m_headerItemShownHeight;
606− } else {
607− diff = -diff;
608− }
609− }
610− if (!m_visibleItems.isEmpty()) {
611− updateClipItem();
612− ListItem *firstItem = m_visibleItems.first();
613− firstItem->setY(firstItem->y() + diff);
614− if (showHeaderAnimationRunning) {
615− adjustMinYExtent();
616− }
617− }
618−}
619−
620−void ListViewWithPageHeader::createDelegateModel()
621−{
622− m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
623− connect(m_delegateModel, &QQmlDelegateModel::createdItem, this, &ListViewWithPageHeader::itemCreated);
624− if (isComponentComplete())
625− m_delegateModel->componentComplete();
626− updateWatchedRoles();
627−}
628−
629−void ListViewWithPageHeader::refill()
630−{
631− if (m_inLayout) {
632− return;
633− }
634− if (!isComponentComplete()) {
635− return;
636− }
637−
638− const qreal from = contentY();
639− const qreal to = from + height();
640− const qreal bufferFrom = from - m_cacheBuffer;
641− const qreal bufferTo = to + m_cacheBuffer;
642−
643− bool added = addVisibleItems(from, to, false);
644− bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
645− added |= addVisibleItems(bufferFrom, bufferTo, true);
646−
647− if (added || removed) {
648− m_contentHeightDirty = true;
649− }
650−}
651−
652−bool ListViewWithPageHeader::addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous)
653−{
654− if (!delegate())
655− return false;
656−
657− if (m_delegateModel->count() == 0)
658− return false;
659−
660− ListItem *item;
661−// qDebug() << "ListViewWithPageHeader::addVisibleItems" << fillFrom << fillTo << asynchronous;
662−
663− int modelIndex = 0;
664− qreal pos = 0;
665− if (!m_visibleItems.isEmpty()) {
666− modelIndex = m_firstVisibleIndex + m_visibleItems.count();
667− item = m_visibleItems.last();
668− pos = item->y() + item->height() + m_clipItem->y();
669− }
670− bool changed = false;
671−// qDebug() << (modelIndex < m_delegateModel->count()) << pos << fillTo;
672− while (modelIndex < m_delegateModel->count() && pos <= fillTo) {
673−// qDebug() << "refill: append item" << modelIndex << "pos" << pos << "asynchronous" << asynchronous;
674− if (!(item = createItem(modelIndex, asynchronous)))
675− break;
676− pos += item->height();
677− ++modelIndex;
678− changed = true;
679− }
680−
681− modelIndex = 0;
682− pos = 0;
683− if (!m_visibleItems.isEmpty()) {
684− modelIndex = m_firstVisibleIndex - 1;
685− item = m_visibleItems.first();
686− pos = item->y() + m_clipItem->y();
687− }
688− while (modelIndex >= 0 && pos > fillFrom) {
689−// qDebug() << "refill: prepend item" << modelIndex << "pos" << pos << "fillFrom" << fillFrom << "asynchronous" << asynchronous;
690− if (!(item = createItem(modelIndex, asynchronous)))
691− break;
692− pos -= item->height();
693− --modelIndex;
694− changed = true;
695− }
696−
697− return changed;
698−}
699−
700−void ListViewWithPageHeader::reallyReleaseItem(ListItem *listItem)
701−{
702− QQuickItem *item = listItem->m_item;
703− QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
704− if (flags & QQmlDelegateModel::Destroyed) {
705− item->setParentItem(nullptr);
706− }
707− if (listItem->sectionItem()) {
708− listItem->sectionItem()->deleteLater();
709− }
710− delete listItem;
711−}
712−
713−void ListViewWithPageHeader::releaseItem(ListItem *listItem)
714−{
715− QQuickItemPrivate::get(listItem->m_item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
716− if (listItem->sectionItem()) {
717− QQuickItemPrivate::get(listItem->sectionItem())->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
718− }
719− m_itemsToRelease << listItem;
720−}
721−
722−void ListViewWithPageHeader::updateWatchedRoles()
723−{
724− if (m_delegateModel) {
725− QList<QByteArray> roles;
726− if (!m_sectionProperty.isEmpty())
727− roles << m_sectionProperty.toUtf8();
728− m_delegateModel->setWatchedRoles(roles);
729− }
730−}
731−
732−QQuickItem *ListViewWithPageHeader::getSectionItem(int modelIndex, bool alreadyInserted)
733−{
734− if (!m_sectionDelegate)
735− return nullptr;
736−
737− const QString section = m_delegateModel->stringValue(modelIndex, m_sectionProperty);
738−
739− if (modelIndex > 0) {
740− const QString prevSection = m_delegateModel->stringValue(modelIndex - 1, m_sectionProperty);
741− if (section == prevSection)
742− return nullptr;
743− }
744− if (modelIndex + 1 < model()->rowCount() && !alreadyInserted) {
745− // Already inserted items can't steal next section header
746− const QString nextSection = m_delegateModel->stringValue(modelIndex + 1, m_sectionProperty);
747− if (section == nextSection) {
748− // Steal the section header
749− ListItem *nextItem = itemAtIndex(modelIndex); // Not +1 since not yet inserted into m_visibleItems
750− if (nextItem) {
751− QQuickItem *sectionItem = nextItem->sectionItem();
752− nextItem->setSectionItem(nullptr);
753− return sectionItem;
754− }
755− }
756− }
757−
758− return getSectionItem(section);
759−}
760−
761−QQuickItem *ListViewWithPageHeader::getSectionItem(const QString &sectionText, bool watchGeometry)
762−{
763− QQuickItem *sectionItem = nullptr;
764−
765− QQmlContext *creationContext = m_sectionDelegate->creationContext();
766− QQmlContext *context = new QQmlContext(creationContext ? creationContext : qmlContext(this));
767− QObject *nobj = m_sectionDelegate->beginCreate(context);
768− if (nobj) {
769− QQml_setParent_noEvent(context, nobj);
770− sectionItem = qobject_cast<QQuickItem *>(nobj);
771− if (!sectionItem) {
772− delete nobj;
773− } else {
774− sectionItem->setProperty("text", sectionText);
775− sectionItem->setProperty("delegate", QVariant());
776− sectionItem->setZ(2);
777− QQml_setParent_noEvent(sectionItem, m_clipItem);
778− sectionItem->setParentItem(m_clipItem);
779− }
780− } else {
781− delete context;
782− }
783− m_sectionDelegate->completeCreate();
784−
785− if (watchGeometry && sectionItem) {
786− QQuickItemPrivate::get(sectionItem)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
787− }
788−
789− return sectionItem;
790−}
791−
792−void ListViewWithPageHeader::updateSectionItem(int modelIndex)
793−{
794− ListItem *item = itemAtIndex(modelIndex);
795− if (item) {
796− const QString sectionText = m_delegateModel->stringValue(modelIndex, m_sectionProperty);
797−
798− bool needSectionHeader = true;
799− // if it is the same section as the previous item need to drop the section
800− if (modelIndex > 0) {
801− const QString prevSection = m_delegateModel->stringValue(modelIndex - 1, m_sectionProperty);
802− if (sectionText == prevSection) {
803− needSectionHeader = false;
804− }
805− }
806−
807− if (needSectionHeader) {
808− if (!item->sectionItem()) {
809− item->setSectionItem(getSectionItem(sectionText));
810− } else {
811− item->sectionItem()->setProperty("text", sectionText);
812− }
813− } else {
814− if (item->sectionItem()) {
815− item->sectionItem()->deleteLater();
816− item->setSectionItem(nullptr);
817− }
818− }
819− }
820−}
821−
822−bool ListViewWithPageHeader::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
823−{
824−// qDebug() << "ListViewWithPageHeader::removeNonVisibleItems" << bufferFrom << bufferTo;
825− // Do not remove items if we are overshooting up or down, since we'll come back
826− // to the "stable" position and delete/create items without any reason
827− if (contentY() < -m_minYExtent) {
828− return false;
829− } else if (contentY() + height() > contentHeight()) {
830− return false;
831− }
832− bool changed = false;
833−
834− bool foundVisible = false;
835− int i = 0;
836− int removedItems = 0;
837− const auto oldFirstVisibleIndex = m_firstVisibleIndex;
838− while (i < m_visibleItems.count()) {
839− ListItem *item = m_visibleItems[i];
840− const qreal pos = item->y() + m_clipItem->y();
841−// qDebug() << i << pos << (pos + item->height()) << bufferFrom << bufferTo;
842− if (pos + item->height() < bufferFrom || pos > bufferTo) {
843−// qDebug() << "Releasing" << i << (pos + item->height() < bufferFrom) << pos + item->height() << bufferFrom << (pos > bufferTo) << pos << bufferTo;
844− releaseItem(item);
845− m_visibleItems.removeAt(i);
846− changed = true;
847− ++removedItems;
848− } else {
849− if (!foundVisible) {
850− foundVisible = true;
851− const int itemIndex = m_firstVisibleIndex + removedItems + i;
852− m_firstVisibleIndex = itemIndex;
853− }
854− ++i;
855− }
856− }
857− if (!foundVisible) {
858− initializeValuesForEmptyList();
859− }
860− if (m_firstVisibleIndex != oldFirstVisibleIndex) {
861− adjustMinYExtent();
862− }
863−
864− return changed;
865−}
866−
867−ListViewWithPageHeader::ListItem *ListViewWithPageHeader::createItem(int modelIndex, bool asynchronous)
868−{
869−// qDebug() << "CREATE ITEM" << modelIndex;
870− if (asynchronous && m_asyncRequestedIndex != -1)
871− return nullptr;
872−
873− m_asyncRequestedIndex = -1;
874− QObject* object = m_delegateModel->object(modelIndex, asynchronous);
875− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
876− if (!item) {
877− if (object) {
878− m_delegateModel->release(object);
879− if (!m_delegateValidated) {
880− m_delegateValidated = true;
881− QObject* delegateObj = delegate();
882− qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
883− }
884− } else {
885− m_asyncRequestedIndex = modelIndex;
886− }
887− return 0;
888− } else {
889−// qDebug() << "ListViewWithPageHeader::createItem::We have the item" << modelIndex << item;
890− ListItem *listItem = new ListItem;
891− listItem->m_item = item;
892− listItem->setSectionItem(getSectionItem(modelIndex, false /*Not yet inserted into m_visibleItems*/));
893− QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
894− ListItem *prevItem = itemAtIndex(modelIndex - 1);
895− bool lostItem = false; // Is an item that we requested async but because of model changes
896− // it is no longer attached to any of the existing items (has no prev nor next item)
897− // nor is the first item
898− if (prevItem) {
899− listItem->setY(prevItem->y() + prevItem->height());
900− } else {
901− ListItem *currItem = itemAtIndex(modelIndex);
902− if (currItem) {
903− // There's something already in m_visibleItems at out index, meaning this is an insert, so attach to its top
904− listItem->setY(currItem->y() - listItem->height());
905− } else {
906− ListItem *nextItem = itemAtIndex(modelIndex + 1);
907− if (nextItem) {
908− listItem->setY(nextItem->y() - listItem->height());
909− } else if (modelIndex == 0) {
910− listItem->setY(-m_clipItem->y() + (m_headerItem ? m_headerItem->height() : 0));
911− } else if (!m_visibleItems.isEmpty()) {
912− lostItem = true;
913− }
914− }
915− }
916− if (lostItem) {
917− listItem->setCulled(true);
918− releaseItem(listItem);
919− listItem = nullptr;
920− } else {
921− listItem->setCulled(listItem->y() + listItem->height() + m_clipItem->y() <= contentY() || listItem->y() + m_clipItem->y() >= contentY() + height());
922− if (m_visibleItems.isEmpty()) {
923− m_visibleItems << listItem;
924− } else {
925− m_visibleItems.insert(modelIndex - m_firstVisibleIndex, listItem);
926− }
927− if (m_firstVisibleIndex < 0 || modelIndex < m_firstVisibleIndex) {
928− m_firstVisibleIndex = modelIndex;
929− polish();
930− }
931− if (listItem->sectionItem()) {
932− listItem->sectionItem()->setProperty("delegate", QVariant::fromValue(listItem->m_item));
933− }
934− adjustMinYExtent();
935− m_contentHeightDirty = true;
936− }
937− return listItem;
938− }
939−}
940−
941−void ListViewWithPageHeader::itemCreated(int modelIndex, QObject *object)
942−{
943− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
944− if (!item) {
945− qWarning() << "ListViewWithPageHeader::itemCreated got a non item for index" << modelIndex;
946− return;
947− }
948−// qDebug() << "ListViewWithPageHeader::itemCreated" << modelIndex << item;
949− // Check we are not being taken down and don't paint anything
950− // TODO Check if we still need this in 5.2
951− // For reproduction just inifnite loop testDash or testDashContent
952− if (!QQmlEngine::contextForObject(this)->parentContext())
953− return;
954−
955− item->setParentItem(m_clipItem);
956− // FIXME Why do we need the refreshExpressions call?
957− QQmlContext *context = QQmlEngine::contextForObject(item)->parentContext();
958− QQmlContextPrivate::get(context)->data->refreshExpressions();
959− item->setProperty("heightToClip", QVariant::fromValue<int>(0));
960− if (modelIndex == m_asyncRequestedIndex) {
961− createItem(modelIndex, false);
962− refill();
963− }
964−}
965−
966−void ListViewWithPageHeader::updateClipItem()
967−{
968− m_clipItem->setHeight(height() - m_headerItemShownHeight);
969− m_clipItem->setY(contentY() + m_headerItemShownHeight);
970− m_clipItem->setClip(!m_forceNoClip && m_headerItemShownHeight > 0);
971−}
972−
973−void ListViewWithPageHeader::onContentHeightChanged()
974−{
975− updateClipItem();
976−}
977−
978−void ListViewWithPageHeader::onContentWidthChanged()
979−{
980− m_clipItem->setWidth(contentItem()->width());
981−}
982−
983−void ListViewWithPageHeader::onHeightChanged()
984−{
985− m_clipItem->setHeight(height() - m_headerItemShownHeight);
986− polish();
987−}
988−
989−
990−void ListViewWithPageHeader::onModelUpdated(const QQmlChangeSet &changeSet, bool /*reset*/)
991−{
992− // TODO Do something with reset
993−// qDebug() << "ListViewWithPageHeader::onModelUpdated" << changeSet << reset;
994− const auto oldFirstVisibleIndex = m_firstVisibleIndex;
995−
996− Q_FOREACH(const QQmlChangeSet::Change remove, changeSet.removes()) {
997−// qDebug() << "ListViewWithPageHeader::onModelUpdated Remove" << remove.index << remove.count;
998− if (remove.index + remove.count > m_firstVisibleIndex && remove.index < m_firstVisibleIndex + m_visibleItems.count()) {
999− const qreal oldFirstValidIndexPos = m_visibleItems.first()->y();
1000− // If all the items we are removing are either not created or culled
1001− // we have to grow down to avoid viewport changing
1002− bool growDown = true;
1003− for (int i = 0; growDown && i < remove.count; ++i) {
1004− const int modelIndex = remove.index + i;
1005− ListItem *item = itemAtIndex(modelIndex);
1006− if (item && !item->culled()) {
1007− growDown = false;
1008− }
1009− }
1010− for (int i = remove.count - 1; i >= 0; --i) {
1011− const int visibleIndex = remove.index + i - m_firstVisibleIndex;
1012− if (visibleIndex >= 0 && visibleIndex < m_visibleItems.count()) {
1013− ListItem *item = m_visibleItems[visibleIndex];
1014− // Pass the section item down if needed
1015− if (item->sectionItem() && visibleIndex + 1 < m_visibleItems.count()) {
1016− ListItem *nextItem = m_visibleItems[visibleIndex + 1];
1017− if (!nextItem->sectionItem()) {
1018− nextItem->setSectionItem(item->sectionItem());
1019− item->setSectionItem(nullptr);
1020− }
1021− }
1022− releaseItem(item);
1023− m_visibleItems.removeAt(visibleIndex);
1024− }
1025− }
1026− if (growDown) {
1027− adjustMinYExtent();
1028− } else if (remove.index <= m_firstVisibleIndex && !m_visibleItems.isEmpty()) {
1029− m_visibleItems.first()->setY(oldFirstValidIndexPos);
1030− }
1031− if (m_visibleItems.isEmpty()) {
1032− m_firstVisibleIndex = -1;
1033− } else {
1034− m_firstVisibleIndex -= qMax(0, m_firstVisibleIndex - remove.index);
1035− }
1036− } else if (remove.index + remove.count <= m_firstVisibleIndex) {
1037− m_firstVisibleIndex -= remove.count;
1038− }
1039− for (int i = remove.count - 1; i >= 0; --i) {
1040− const int modelIndex = remove.index + i;
1041− if (modelIndex == m_asyncRequestedIndex) {
1042− m_asyncRequestedIndex = -1;
1043− } else if (modelIndex < m_asyncRequestedIndex) {
1044− m_asyncRequestedIndex--;
1045− }
1046− }
1047− }
1048−
1049− Q_FOREACH(const QQmlChangeSet::Change insert, changeSet.inserts()) {
1050−// qDebug() << "ListViewWithPageHeader::onModelUpdated Insert" << insert.index << insert.count;
1051− const bool insertingInValidIndexes = insert.index > m_firstVisibleIndex && insert.index < m_firstVisibleIndex + m_visibleItems.count();
1052− const bool firstItemWithViewOnTop = insert.index == 0 && m_firstVisibleIndex == 0 && m_visibleItems.first()->y() + m_clipItem->y() > contentY();
1053− if (insertingInValidIndexes || firstItemWithViewOnTop)
1054− {
1055− // If the items we are adding won't be really visible
1056− // we grow up instead of down to not change the viewport
1057− bool growUp = false;
1058− if (!firstItemWithViewOnTop) {
1059− for (int i = 0; i < m_visibleItems.count(); ++i) {
1060− if (!m_visibleItems[i]->culled()) {
1061− if (insert.index <= m_firstVisibleIndex + i) {
1062− growUp = true;
1063− }
1064− break;
1065− }
1066− }
1067− }
1068−
1069− const qreal oldFirstValidIndexPos = m_visibleItems.first()->y();
1070− for (int i = insert.count - 1; i >= 0; --i) {
1071− const int modelIndex = insert.index + i;
1072− ListItem *item = createItem(modelIndex, false);
1073− if (growUp) {
1074− ListItem *firstItem = m_visibleItems.first();
1075− firstItem->setY(firstItem->y() - item->height());
1076− }
1077− // Adding an item may break a "same section" chain, so check
1078− // if we need adding a new section item
1079− if (m_sectionDelegate) {
1080− ListItem *nextItem = itemAtIndex(modelIndex + 1);
1081− if (nextItem && !nextItem->sectionItem()) {
1082− nextItem->setSectionItem(getSectionItem(modelIndex + 1, true /* alredy inserted into m_visibleItems*/));
1083− if (growUp && nextItem->sectionItem()) {
1084− ListItem *firstItem = m_visibleItems.first();
1085− firstItem->setY(firstItem->y() - nextItem->sectionItem()->height());
1086− }
1087− }
1088− }
1089− }
1090− if (firstItemWithViewOnTop) {
1091− ListItem *firstItem = m_visibleItems.first();
1092− firstItem->setY(oldFirstValidIndexPos);
1093− }
1094− adjustMinYExtent();
1095− } else if (insert.index <= m_firstVisibleIndex) {
1096− m_firstVisibleIndex += insert.count;
1097− }
1098−
1099− for (int i = insert.count - 1; i >= 0; --i) {
1100− const int modelIndex = insert.index + i;
1101− if (modelIndex <= m_asyncRequestedIndex) {
1102− m_asyncRequestedIndex++;
1103− }
1104− }
1105− }
1106−
1107− Q_FOREACH(const QQmlChangeSet::Change change, changeSet.changes()) {
1108− for (int i = change.start(); i < change.end(); ++i) {
1109− updateSectionItem(i);
1110− }
1111− // Also update the section header for the next item after the change since it may be influenced
1112− updateSectionItem(change.end());
1113− }
1114−
1115− if (m_firstVisibleIndex != oldFirstVisibleIndex) {
1116− if (m_visibleItems.isEmpty()) {
1117− initializeValuesForEmptyList();
1118− } else {
1119− adjustMinYExtent();
1120− }
1121− }
1122−
1123− for (int i = 0; i < m_visibleItems.count(); ++i) {
1124− ListItem *item = m_visibleItems[i];
1125− if (item->sectionItem()) {
1126− item->sectionItem()->setProperty("delegate", QVariant::fromValue(item->m_item));
1127− }
1128− }
1129−
1130− layout();
1131− polish();
1132− m_contentHeightDirty = true;
1133−}
1134−
1135−void ListViewWithPageHeader::contentYAnimationRunningChanged(bool running)
1136−{
1137− setInteractive(!running);
1138− if (!running) {
1139− m_contentHeightDirty = true;
1140− polish();
1141− }
1142−}
1143−
1144−void ListViewWithPageHeader::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1145−{
1146− const qreal heightDiff = newGeometry.height() - oldGeometry.height();
1147− if (heightDiff != 0) {
1148− if (!m_visibleItems.isEmpty()) {
1149− ListItem *firstItem = m_visibleItems.first();
1150− const auto prevFirstItemY = firstItem->y();
1151− if (!m_inContentHeightKeepHeaderShown && oldGeometry.y() + oldGeometry.height() + m_clipItem->y() <= contentY()) {
1152− firstItem->setY(firstItem->y() - heightDiff);
1153− } else if (item == firstItem->sectionItem()) {
1154− firstItem->setY(firstItem->y() + heightDiff);
1155− }
1156−
1157− if (firstItem->y() != prevFirstItemY) {
1158− adjustMinYExtent();
1159− layout();
1160− }
1161− }
1162− refill();
1163− adjustMinYExtent();
1164− polish();
1165− m_contentHeightDirty = true;
1166− }
1167−}
1168−
1169−void ListViewWithPageHeader::itemImplicitHeightChanged(QQuickItem *item)
1170−{
1171− if (item == m_headerItem) {
1172− const qreal diff = m_headerItem->implicitHeight() - m_previousHeaderImplicitHeight;
1173− if (diff != 0) {
1174− adjustHeader(diff);
1175− m_previousHeaderImplicitHeight = m_headerItem->implicitHeight();
1176− layout();
1177− polish();
1178− m_contentHeightDirty = true;
1179− }
1180− }
1181−}
1182−
1183−void ListViewWithPageHeader::headerHeightChanged(qreal newHeaderHeight, qreal oldHeaderHeight, qreal oldHeaderY)
1184−{
1185− const qreal heightDiff = newHeaderHeight - oldHeaderHeight;
1186− if (m_headerItemShownHeight > 0) {
1187− // If the header is shown because of the clip
1188− // Change its size
1189− m_headerItemShownHeight += heightDiff;
1190− m_headerItemShownHeight = qBound(static_cast<qreal>(0.), m_headerItemShownHeight, newHeaderHeight);
1191− updateClipItem();
1192− adjustMinYExtent();
1193− Q_EMIT headerItemShownHeightChanged();
1194− } else {
1195− if (oldHeaderY + oldHeaderHeight > contentY()) {
1196− // If the header is shown because its position
1197− // Change its size
1198− ListItem *firstItem = m_visibleItems.first();
1199− firstItem->setY(firstItem->y() + heightDiff);
1200− layout();
1201− } else {
1202− // If the header is not on screen, just change the start of the list
1203− // so the viewport is not changed
1204− adjustMinYExtent();
1205− }
1206− }
1207−}
1208−
1209−
1210−void ListViewWithPageHeader::adjustMinYExtent()
1211−{
1212− if (m_visibleItems.isEmpty() || (contentHeight() + m_minYExtent < height())) {
1213− m_minYExtent = 0;
1214− } else {
1215− qreal nonCreatedHeight = 0;
1216− if (m_firstVisibleIndex != 0) {
1217− // Calculate the average height of items to estimate the position of the list start
1218− const int visibleItems = m_visibleItems.count();
1219− qreal visibleItemsHeight = 0;
1220− Q_FOREACH(ListItem *item, m_visibleItems) {
1221− visibleItemsHeight += item->height();
1222− }
1223− nonCreatedHeight = m_firstVisibleIndex * visibleItemsHeight / visibleItems;
1224−// qDebug() << m_firstVisibleIndex << visibleItemsHeight << visibleItems << nonCreatedHeight;
1225− }
1226− const qreal headerHeight = (m_headerItem ? m_headerItem->implicitHeight() : 0);
1227− m_minYExtent = nonCreatedHeight - m_visibleItems.first()->y() - m_clipItem->y() + headerHeight;
1228− if (m_minYExtent != 0 && qFuzzyIsNull(m_minYExtent)) {
1229− m_minYExtent = 0;
1230− m_visibleItems.first()->setY(nonCreatedHeight - m_clipItem->y() + headerHeight);
1231− }
1232− }
1233−}
1234−
1235−ListViewWithPageHeader::ListItem *ListViewWithPageHeader::itemAtIndex(int modelIndex) const
1236−{
1237− const int visibleIndexedModelIndex = modelIndex - m_firstVisibleIndex;
1238− if (visibleIndexedModelIndex >= 0 && visibleIndexedModelIndex < m_visibleItems.count())
1239− return m_visibleItems[visibleIndexedModelIndex];
1240−
1241− return nullptr;
1242−}
1243−
1244−void ListViewWithPageHeader::layout()
1245−{
1246− if (m_inLayout)
1247− return;
1248−
1249− m_inLayout = true;
1250− if (!m_visibleItems.isEmpty()) {
1251− const qreal visibleFrom = contentY() - m_clipItem->y() + m_headerItemShownHeight;
1252− const qreal visibleTo = contentY() + height() - m_clipItem->y();
1253−
1254− qreal pos = m_visibleItems.first()->y();
1255−
1256−// qDebug() << "ListViewWithPageHeader::layout Updating positions and heights. contentY" << contentY() << "minYExtent" << minYExtent();
1257− int firstReallyVisibleItem = -1;
1258− int modelIndex = m_firstVisibleIndex;
1259− Q_FOREACH(ListItem *item, m_visibleItems) {
1260− const bool cull = pos + item->height() <= visibleFrom || pos >= visibleTo;
1261− item->setCulled(cull);
1262− item->setY(pos);
1263− if (!cull && firstReallyVisibleItem == -1) {
1264− firstReallyVisibleItem = modelIndex;
1265− if (m_topSectionItem) {
1266− // Positing the top section sticky item is a two step process
1267− // First we set it either we cull it (because it doesn't need to be sticked to the top)
1268− // or stick it to the top
1269− // Then after the loop we'll make sure that if there's another section just below it
1270− // pushed the sticky section up to make it disappear
1271− const qreal topSectionStickPos = m_headerItemShownHeight + contentY() - m_clipItem->y();
1272− bool showStickySectionItem;
1273− // We need to show the "top section sticky item" when the position at the "top" of the
1274− // viewport is bigger than the start of the position of the first visible item
1275− // i.e. the first visible item starts before the viewport, or when the first
1276− // visible item starts just at the viewport start and it does not have its own section item
1277− if (topSectionStickPos > pos) {
1278− showStickySectionItem = true;
1279− } else if (topSectionStickPos == pos) {
1280− showStickySectionItem = !item->sectionItem();
1281− } else {
1282− showStickySectionItem = false;
1283− }
1284− if (!showStickySectionItem) {
1285− QQuickItemPrivate::get(m_topSectionItem)->setCulled(true);
1286− if (item->sectionItem()) {
1287− // This seems it should happen since why would we cull the top section
1288− // if the first visible item has no section header? This only happens briefly
1289− // when increasing the height of a list that is at the bottom, the m_topSectionItem
1290− // gets shown shortly in the next polish call
1291− QQuickItemPrivate::get(item->sectionItem())->setCulled(false);
1292− }
1293− } else {
1294− // Update the top sticky section header
1295− const QString section = m_delegateModel->stringValue(modelIndex, m_sectionProperty);
1296− m_topSectionItem->setProperty("text", section);
1297−
1298− QQuickItemPrivate::get(m_topSectionItem)->setCulled(false);
1299− m_topSectionItem->setY(topSectionStickPos);
1300− m_topSectionItem->setProperty("delegate", QVariant::fromValue(item->m_item));
1301− if (item->sectionItem()) {
1302− QQuickItemPrivate::get(item->sectionItem())->setCulled(true);
1303− }
1304− }
1305− }
1306− }
1307− const qreal clipFrom = visibleFrom + (!item->sectionItem() && m_topSectionItem && !QQuickItemPrivate::get(m_topSectionItem)->culled ? m_topSectionItem->height() : 0);
1308− if (!cull && pos < clipFrom) {
1309− item->m_item->setProperty("heightToClip", clipFrom - pos);
1310− } else {
1311− item->m_item->setProperty("heightToClip", QVariant::fromValue<int>(0));
1312− }
1313−// qDebug() << "ListViewWithPageHeader::layout" << item->m_item;
1314− pos += item->height();
1315− ++modelIndex;
1316− }
1317−
1318− // Second step of section sticky item positioning
1319− // Look at the next section header, check if it's pushing up the sticky one
1320− if (m_topSectionItem) {
1321− if (firstReallyVisibleItem >= 0) {
1322− for (int i = firstReallyVisibleItem - m_firstVisibleIndex + 1; i < m_visibleItems.count(); ++i) {
1323− ListItem *item = m_visibleItems[i];
1324− if (item->sectionItem()) {
1325− if (m_topSectionItem->y() + m_topSectionItem->height() > item->y()) {
1326− m_topSectionItem->setY(item->y() - m_topSectionItem->height());
1327− }
1328− break;
1329− }
1330− }
1331− }
1332− }
1333− }
1334− if (m_headerItem) {
1335− const bool cullHeader = m_headerItem->y() + m_headerItem->height() < contentY();
1336− QQuickItemPrivate::get(m_headerItem)->setCulled(cullHeader);
1337− }
1338− m_inLayout = false;
1339−}
1340−
1341−void ListViewWithPageHeader::updatePolish()
1342−{
1343− // Check we are not being taken down and don't paint anything
1344− // TODO Check if we still need this in 5.2
1345− // For reproduction just inifnite loop testDash or testDashContent
1346− if (!QQmlEngine::contextForObject(this)->parentContext())
1347− return;
1348−
1349− Q_FOREACH(ListItem *item, m_itemsToRelease)
1350− reallyReleaseItem(item);
1351− m_itemsToRelease.clear();
1352−
1353− if (!model())
1354− return;
1355−
1356− layout();
1357−
1358− refill();
1359−
1360− if (m_contentHeightDirty) {
1361− qreal contentHeight;
1362− if (m_visibleItems.isEmpty()) {
1363− contentHeight = m_headerItem ? m_headerItem->height() : 0;
1364− } else {
1365− const int modelCount = model()->rowCount();
1366− const int visibleItems = m_visibleItems.count();
1367− const int lastValidIndex = m_firstVisibleIndex + visibleItems - 1;
1368− qreal nonCreatedHeight = 0;
1369− if (lastValidIndex != modelCount - 1) {
1370− const int visibleItems = m_visibleItems.count();
1371− qreal visibleItemsHeight = 0;
1372− Q_FOREACH(ListItem *item, m_visibleItems) {
1373− visibleItemsHeight += item->height();
1374− }
1375− const int unknownSizes = modelCount - (m_firstVisibleIndex + visibleItems);
1376− nonCreatedHeight = unknownSizes * visibleItemsHeight / visibleItems;
1377− }
1378− ListItem *item = m_visibleItems.last();
1379− contentHeight = nonCreatedHeight + item->y() + item->height() + m_clipItem->y();
1380− if (m_firstVisibleIndex != 0) {
1381− // Make sure that if we are shrinking we tell the view we still fit
1382− m_minYExtent = qMax(m_minYExtent, -(contentHeight - height()));
1383− }
1384− }
1385−
1386− m_contentHeightDirty = false;
1387− adjustMinYExtent();
1388− m_inContentHeightKeepHeaderShown = m_headerItem && m_headerItem->y() == contentY();
1389− setContentHeight(contentHeight);
1390− m_inContentHeightKeepHeaderShown = false;
1391− }
1392−}
1393−
1394−#include "moc_listviewwithpageheader.cpp"
plugins/Dash/listviewwithpageheader.hdeleted−218
@@ -1,218 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#ifndef LISTVIEWWITHPAGEHEADER_H
18−#define LISTVIEWWITHPAGEHEADER_H
19−
20−#include <private/qquickitemchangelistener_p.h>
21−#include <private/qquickflickable_p.h>
22−
23−class QAbstractItemModel;
24−class QQuickNumberAnimation;
25−class QQmlChangeSet;
26−class QQmlDelegateModel;
27−
28−
29−/**
30− Note for users of this class
31−
32− ListViewWithPageHeader already loads delegates async when appropiate so if
33− your delegate uses a Loader you should not enable the asynchronous feature since
34− that will need to introduce sizing problems
35−
36− With the double async it may happen what while we are scrolling down
37− we reach to a point where given the size of the just created delegate with loader not yet loaded (which will be very close to 0)
38− we are already "at the end" of the list, but then a few milliseconds later the loader finishes loading and we could
39− have kept scrolling. This is specially visible at the end of the list where you realize
40− that scrolling ended a bit before the end of the list but the speed of the flicking was good
41− to reach the end
42−
43− By not having the second async we get a better sizing when the delegate is created and things work better
44−*/
45−
46−class ListViewWithPageHeader : public QQuickFlickable, public QQuickItemChangeListener
47−{
48− Q_OBJECT
49− Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
50− Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
51− Q_PROPERTY(QQuickItem *pageHeader READ header WRITE setHeader NOTIFY headerChanged)
52− Q_PROPERTY(QQmlComponent *sectionDelegate READ sectionDelegate WRITE setSectionDelegate NOTIFY sectionDelegateChanged)
53− Q_PROPERTY(QString sectionProperty READ sectionProperty WRITE setSectionProperty NOTIFY sectionPropertyChanged)
54− Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
55− Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
56− Q_PROPERTY(qreal headerItemShownHeight READ headerItemShownHeight NOTIFY headerItemShownHeightChanged)
57− Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
58−
59− friend class ListViewWithPageHeaderTest;
60− friend class ListViewWithPageHeaderTestSection;
61− friend class ListViewWithPageHeaderTestExternalModel;
62−
63−public:
64− ListViewWithPageHeader();
65− ~ListViewWithPageHeader();
66−
67− QAbstractItemModel *model() const;
68− void setModel(QAbstractItemModel *model);
69−
70− QQmlComponent *delegate() const;
71− void setDelegate(QQmlComponent *delegate);
72−
73− QQuickItem *header() const;
74− void setHeader(QQuickItem *header);
75−
76− QQmlComponent *sectionDelegate() const;
77− void setSectionDelegate(QQmlComponent *delegate);
78−
79− QString sectionProperty() const;
80− void setSectionProperty(const QString &property);
81−
82− bool forceNoClip() const;
83− void setForceNoClip(bool noClip);
84−
85− int stickyHeaderHeight() const;
86− qreal headerItemShownHeight() const;
87−
88− int cacheBuffer() const;
89− void setCacheBuffer(int cacheBuffer);
90−
91− Q_INVOKABLE void positionAtBeginning();
92− Q_INVOKABLE void showHeader();
93− Q_INVOKABLE int firstCreatedIndex() const;
94− Q_INVOKABLE int createdItemCount() const;
95− Q_INVOKABLE QQuickItem *item(int modelIndex) const;
96−
97− // The index has to be created for this to try to do something
98− // Created items are those visible and the precached ones
99− // Returns if the item existed or not
100− Q_INVOKABLE bool maximizeVisibleArea(int modelIndex);
101− Q_INVOKABLE bool maximizeVisibleArea(int modelIndex, int itemHeight);
102−
103−Q_SIGNALS:
104− void modelChanged();
105− void delegateChanged();
106− void headerChanged();
107− void sectionDelegateChanged();
108− void sectionPropertyChanged();
109− void forceNoClipChanged();
110− void stickyHeaderHeightChanged();
111− void headerItemShownHeightChanged();
112− void cacheBufferChanged();
113−
114−protected:
115− void componentComplete() override;
116− void viewportMoved(Qt::Orientations orient) override;
117− qreal minYExtent() const override;
118− qreal maxYExtent() const override;
119− void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override;
120− void itemImplicitHeightChanged(QQuickItem *item) override;
121− void updatePolish() override;
122−
123−private Q_SLOTS:
124− void itemCreated(int modelIndex, QObject *object);
125− void onContentHeightChanged();
126− void onContentWidthChanged();
127− void onHeightChanged();
128− void onModelUpdated(const QQmlChangeSet &changeSet, bool reset);
129− void contentYAnimationRunningChanged(bool running);
130−
131−private:
132− class ListItem
133− {
134− public:
135− qreal height() const;
136−
137− qreal y() const;
138− void setY(qreal newY);
139−
140− bool culled() const;
141− void setCulled(bool culled);
142−
143− QQuickItem *sectionItem() const { return m_sectionItem; }
144− void setSectionItem(QQuickItem *sectionItem);
145−
146− QQuickItem *m_item;
147− private:
148− QQuickItem *m_sectionItem;
149− };
150−
151− bool maximizeVisibleArea(ListItem *listItem, int listItemHeight);
152−
153− void createDelegateModel();
154−
155− void layout();
156− void refill();
157− bool addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous);
158− bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
159− ListItem *createItem(int modelIndex, bool asynchronous);
160−
161− void adjustHeader(qreal diff);
162− void adjustMinYExtent();
163− void updateClipItem();
164− void headerHeightChanged(qreal newHeaderHeight, qreal oldHeaderHeight, qreal oldHeaderY);
165− ListItem *itemAtIndex(int modelIndex) const; // Returns the item at modelIndex if has been created
166− void releaseItem(ListItem *item);
167− void reallyReleaseItem(ListItem *item);
168− void updateWatchedRoles();
169− QQuickItem *getSectionItem(int modelIndex, bool alreadyInserted);
170− QQuickItem *getSectionItem(const QString &sectionText, bool watchGeometry = true);
171− void updateSectionItem(int modelIndex);
172− void initializeValuesForEmptyList();
173−
174− QQmlDelegateModel *m_delegateModel;
175−
176− // Index we are waiting because we requested it asynchronously
177− int m_asyncRequestedIndex;
178−
179− // Used to only give a warning once if the delegate does not return objects
180− bool m_delegateValidated;
181−
182− // Visible indexes, [0] is m_firstValidIndex, [0+1] is m_firstValidIndex +1, ...
183− QList<ListItem *> m_visibleItems;
184− int m_firstVisibleIndex;
185−
186− qreal m_minYExtent;
187−
188− QQuickItem *m_clipItem;
189−
190− // If any of the heights has changed
191− // or new items have been added/removed
192− bool m_contentHeightDirty;
193−
194− QQuickItem *m_headerItem;
195− qreal m_previousContentY;
196− qreal m_previousHeaderImplicitHeight;
197− qreal m_headerItemShownHeight; // The height of header shown when the header is shown outside its topmost position
198− // i.e. it's being shown after dragging down in the middle of the list
199− enum ContentYAnimationType { ContentYAnimationShowHeader, ContentYAnimationMaximizeVisibleArea };
200− ContentYAnimationType contentYAnimationType;
201− QQuickNumberAnimation *m_contentYAnimation;
202−
203− QQmlComponent *m_sectionDelegate;
204− QString m_sectionProperty;
205− QQuickItem *m_topSectionItem;
206−
207− bool m_forceNoClip;
208− bool m_inLayout;
209− bool m_inContentHeightKeepHeaderShown;
210− int m_cacheBuffer;
211−
212− // Qt 5.0 doesn't like releasing the items just after itemCreated
213− // so we delay the releasing until the next updatePolish
214− QList<ListItem *> m_itemsToRelease;
215−};
216−
217−
218−#endif
plugins/Dash/organicgrid.cppdeleted−259
@@ -1,259 +0,0 @@
1−/*
2− * Copyright (C) 2014, 2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#include "organicgrid.h"
18−
19−#include <math.h>
20−
21−#include <private/qquickitem_p.h>
22−
23−OrganicGrid::OrganicGrid()
24− : m_firstVisibleIndex(-1)
25− , m_numberOfModulesPerRow(-1)
26−{
27−}
28−
29−QSizeF OrganicGrid::smallDelegateSize() const
30−{
31− return m_smallDelegateSize;
32−}
33−
34−void OrganicGrid::setSmallDelegateSize(const QSizeF size)
35−{
36− if (m_smallDelegateSize != size) {
37− m_smallDelegateSize = size;
38− Q_EMIT smallDelegateSizeChanged();
39−
40− if (isComponentComplete()) {
41− relayout();
42− }
43− }
44−}
45−
46−QSizeF OrganicGrid::bigDelegateSize() const
47−{
48− return m_bigDelegateSize;
49−}
50−
51−void OrganicGrid::setBigDelegateSize(const QSizeF size)
52−{
53− if (m_bigDelegateSize != size) {
54− m_bigDelegateSize = size;
55− Q_EMIT bigDelegateSizeChanged();
56−
57− if (isComponentComplete()) {
58− relayout();
59− }
60− }
61−}
62−
63−QPointF OrganicGrid::positionForIndex(int modelIndex) const
64−{
65− const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
66− const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
67− const int itemsPerRow = m_numberOfModulesPerRow * 6;
68− const int rowIndex = floor(modelIndex / itemsPerRow);
69− const int columnIndex = floor((modelIndex - rowIndex * itemsPerRow) / 6);
70−
71− qreal yPos = (moduleHeight + rowSpacing()) * rowIndex;
72− const int moduleIndex = modelIndex % 6;
73− if (moduleIndex == 2) {
74− yPos += m_smallDelegateSize.height() + rowSpacing();
75− } else if (moduleIndex == 3 || moduleIndex == 5) {
76− yPos += m_bigDelegateSize.height() + rowSpacing();
77− }
78−
79− qreal xPos = (moduleWidth + columnSpacing()) * columnIndex;
80− if (moduleIndex == 1) {
81− xPos += m_smallDelegateSize.width() + columnSpacing();
82− } else if (moduleIndex == 3) {
83− xPos += m_bigDelegateSize.width() + columnSpacing();
84− } else if (moduleIndex == 4) {
85− xPos += (m_smallDelegateSize.width() + columnSpacing()) * 2;
86− } else if (moduleIndex == 5) {
87− xPos += m_bigDelegateSize.width() + m_smallDelegateSize.width() + columnSpacing() * 2;
88− }
89−
90− return QPointF(xPos, yPos);
91−}
92−
93−QSizeF OrganicGrid::sizeForIndex(int modelIndex) const
94−{
95− const int moduleIndex = modelIndex % 6;
96− if (moduleIndex == 0 || moduleIndex == 1 || moduleIndex == 3 || moduleIndex == 5) {
97− return m_smallDelegateSize;
98− } else {
99− return m_bigDelegateSize;
100− }
101−}
102−
103−void OrganicGrid::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
104−{
105− if (m_visibleItems.isEmpty()) {
106− *modelIndex = 0;
107− *yPos = 0;
108− } else {
109− *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
110− // We create stuff in a 6-module basis, so always return back
111− // the y position of the first item
112− const int firstModuleIndex = ((*modelIndex) / 6) * 6;
113− *yPos = positionForIndex(firstModuleIndex).y();
114− }
115−}
116−
117−void OrganicGrid::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
118−{
119− if (m_visibleItems.isEmpty()) {
120− *modelIndex = 0;
121− *yPos = 0;
122− } else {
123− *modelIndex = m_firstVisibleIndex - 1;
124− // We create stuff in a 6-module basis, so always return back
125− // the y position of the last item bottom
126− const int lastModuleIndex = ((*modelIndex) / 6) * 6 + 5;
127− *yPos = positionForIndex(lastModuleIndex).y();
128− *yPos += sizeForIndex(lastModuleIndex).height();
129− }
130−}
131−
132−void OrganicGrid::addItemToView(int modelIndex, QQuickItem *item)
133−{
134− // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count() or the first
135− if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
136− m_visibleItems << item;
137− } else if (modelIndex == m_firstVisibleIndex - 1) {
138− m_firstVisibleIndex = modelIndex;
139− m_visibleItems.prepend(item);
140− } else if (modelIndex == 0) {
141− m_firstVisibleIndex = 0;
142− m_visibleItems << item;
143− } else {
144− qWarning() << "OrganicGrid::addItemToView - Got unexpected modelIndex"
145− << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
146− return;
147− }
148−
149− const QPointF pos = positionForIndex(modelIndex);
150− item->setPosition(pos);
151−
152− item->setSize(sizeForIndex(modelIndex));
153−}
154−
155−bool OrganicGrid::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
156−{
157− bool changed = false;
158−
159− // As adding, we also remove in a 6-module basis
160− int lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
161− bool removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
162− while (removeIndex && !m_visibleItems.isEmpty()) {
163− releaseItem(m_visibleItems.takeFirst());
164− changed = true;
165− m_firstVisibleIndex++;
166−
167− lastModuleIndex = (m_firstVisibleIndex / 6) * 6 + 5;
168− removeIndex = positionForIndex(lastModuleIndex).y() + sizeForIndex(lastModuleIndex).height() < bufferFromY;
169− }
170−
171− int firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
172− removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
173− while (removeIndex && !m_visibleItems.isEmpty()) {
174− releaseItem(m_visibleItems.takeLast());
175− changed = true;
176−
177− firstModuleIndex = ((m_firstVisibleIndex + m_visibleItems.count() - 1) / 6) * 6;
178− removeIndex = positionForIndex(firstModuleIndex).y() > bufferToY;
179− }
180−
181− if (m_visibleItems.isEmpty()) {
182− m_firstVisibleIndex = -1;
183− }
184−
185− return changed;
186−}
187−
188−void OrganicGrid::cleanupExistingItems()
189−{
190− Q_FOREACH(QQuickItem *item, m_visibleItems)
191− releaseItem(item);
192− m_visibleItems.clear();
193− m_firstVisibleIndex = -1;
194− setImplicitHeightDirty();
195−}
196−
197−void OrganicGrid::doRelayout()
198−{
199− const qreal moduleWidth = m_smallDelegateSize.width() * 2 + columnSpacing() * 2 + m_bigDelegateSize.width();
200− m_numberOfModulesPerRow = floor((width() + columnSpacing()) / (moduleWidth + columnSpacing()));
201− m_numberOfModulesPerRow = qMax(1, m_numberOfModulesPerRow);
202−
203− int i = m_firstVisibleIndex;
204− const QList<QQuickItem*> allItems = m_visibleItems;
205− m_visibleItems.clear();
206− Q_FOREACH(QQuickItem *item, allItems) {
207− addItemToView(i, item);
208− ++i;
209− }
210−}
211−
212−void OrganicGrid::updateItemCulling(qreal visibleFromY, qreal visibleToY)
213−{
214− Q_FOREACH(QQuickItem *item, m_visibleItems) {
215− QQuickItemPrivate::get(item)->setCulled(item->y() + item->height() <= visibleFromY || item->y() >= visibleToY);
216− }
217−}
218−
219−void OrganicGrid::calculateImplicitHeight()
220−{
221− const qreal moduleHeight = m_smallDelegateSize.height() + rowSpacing() + m_bigDelegateSize.height();
222− const int itemCount = !model() ? 0 : model()->rowCount();
223− const int itemsPerRow = m_numberOfModulesPerRow * 6;
224− const int fullRows = floor(itemCount / itemsPerRow);
225− const qreal fullRowsHeight = fullRows == 0 ? 0 : fullRows * moduleHeight + rowSpacing() * (fullRows - 1);
226−
227− const int remainingItems = itemCount - fullRows * itemsPerRow;
228− if (remainingItems == 0) {
229− setImplicitHeight(fullRowsHeight);
230− } else if (remainingItems <= 2) {
231− setImplicitHeight(fullRowsHeight + m_smallDelegateSize.height() + rowSpacing());
232− } else {
233− setImplicitHeight(fullRowsHeight + rowSpacing() + moduleHeight);
234− }
235−}
236−
237−void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
238−{
239− Q_FOREACH(const QQmlChangeSet::Change remove, removes) {
240− for (int i = remove.count - 1; i >= 0; --i) {
241− const int indexToRemove = remove.index + i;
242− // We only support removing from the end
243− const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
244− if (indexToRemove == lastIndex) {
245− releaseItem(m_visibleItems.takeLast());
246− } else {
247− if (indexToRemove < lastIndex) {
248− qDebug() << "OrganicGrid only supports removal from the end of the model, resetting instead";
249− cleanupExistingItems();
250− break;
251− }
252− }
253− }
254− }
255− if (m_visibleItems.isEmpty()) {
256− m_firstVisibleIndex = -1;
257− }
258− setImplicitHeightDirty();
259−}
plugins/Dash/organicgrid.hdeleted−91
@@ -1,91 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#ifndef ORGANICGRID_H
18−#define ORGANICGRID_H
19−
20−#include "abstractdashview.h"
21−
22− /** An Organic Grid is is a view that creates delegates
23− * based on a model and layouts them in groups of six items (called module).
24− *
25− * In each module there are 4 items that are forced to the small delegate size
26− * and two that are forced to the big delegate size.
27− *
28− * Example:
29− *
30− * +---+ +---+ +-----+
31− * | 1 | | 2 | | |
32− * +---+ +---+ | 5 |
33− * +-----+ | |
34− * | | +-----+
35− * | 3 | +---+ +---+
36− * | | | 4 | | 6 |
37− * +-----+ +---+ +---+
38− *
39− * Modules are positioned one after the other in a grid like fashion, i.e.
40− *
41− * +---+ +---+
42− * | 1 | | 2 |
43− * +---+ +---+
44− * +---+ +---+
45− * | 3 | | 4 |
46− * +---+ +---+
47− */
48−
49−class OrganicGrid : public AbstractDashView
50−{
51− Q_OBJECT
52−
53− Q_PROPERTY(QSizeF smallDelegateSize READ smallDelegateSize WRITE setSmallDelegateSize NOTIFY smallDelegateSizeChanged)
54− Q_PROPERTY(QSizeF bigDelegateSize READ bigDelegateSize WRITE setBigDelegateSize NOTIFY bigDelegateSizeChanged)
55−
56−friend class OrganicGridTest;
57−
58−public:
59− OrganicGrid();
60−
61− QSizeF smallDelegateSize() const;
62− void setSmallDelegateSize(const QSizeF size);
63−
64− QSizeF bigDelegateSize() const;
65− void setBigDelegateSize(const QSizeF size);
66−
67−Q_SIGNALS:
68− void smallDelegateSizeChanged();
69− void bigDelegateSizeChanged();
70−private:
71− QPointF positionForIndex(int modelIndex) const;
72− QSizeF sizeForIndex(int modelIndex) const;
73−
74− void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
75− void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
76− void addItemToView(int modelIndex, QQuickItem *item) override;
77− bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
78− void cleanupExistingItems() override;
79− void doRelayout() override;
80− void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
81− void calculateImplicitHeight() override;
82− void processModelRemoves(const QVector<QQmlChangeSet::Change> &removes) override;
83−
84− QSizeF m_smallDelegateSize;
85− QSizeF m_bigDelegateSize;
86− int m_firstVisibleIndex;
87− int m_numberOfModulesPerRow;
88− QList<QQuickItem*> m_visibleItems;
89−};
90−
91−#endif
plugins/Dash/plugin.cppdeleted−70
@@ -1,70 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− *
16− */
17−
18−#include "plugin.h"
19−
20−#include "horizontaljournal.h"
21−#include "listviewwithpageheader.h"
22−#include "organicgrid.h"
23−#include "verticaljournal.h"
24−
25−#include <QAbstractItemModel>
26−#include <QUrlQuery>
27−
28−static QUrl oauthCleanedUrl(QUrl u)
29−{
30− QUrlQuery q(u);
31− q.removeQueryItem(QStringLiteral("oauth_nonce"));
32− q.removeQueryItem(QStringLiteral("oauth_timestamp"));
33− q.removeQueryItem(QStringLiteral("oauth_consumer_key"));
34− q.removeQueryItem(QStringLiteral("oauth_signature_method"));
35− q.removeQueryItem(QStringLiteral("oauth_version"));
36− q.removeQueryItem(QStringLiteral("oauth_signature"));
37− u.setQuery(q);
38− return u;
39−}
40−
41−class AudioComparer : public QObject
42−{
43− Q_OBJECT
44−public:
45− Q_INVOKABLE bool compare(const QUrl &url1, const QUrl &url2)
46− {
47− return oauthCleanedUrl(url1) == oauthCleanedUrl(url2);
48− }
49−};
50−
51−static QObject *audio_comparer_singleton_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
52−{
53− Q_UNUSED(engine)
54− Q_UNUSED(scriptEngine)
55−
56− return new AudioComparer();
57−}
58−
59−void DashPlugin::registerTypes(const char *uri)
60−{
61− Q_ASSERT(uri == QLatin1String("Dash"));
62− qmlRegisterType<QAbstractItemModel>();
63− qmlRegisterType<HorizontalJournal>(uri, 0, 1, "HorizontalJournal");
64− qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
65− qmlRegisterType<OrganicGrid>(uri, 0, 1, "OrganicGrid");
66− qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
67− qmlRegisterSingletonType<AudioComparer>(uri, 0, 1, "AudioUrlComparer", audio_comparer_singleton_provider);
68−}
69−
70−#include "plugin.moc"
plugins/Dash/plugin.hdeleted−33
@@ -1,33 +0,0 @@
1−/*
2− * Copyright (C) 2012 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− *
16− */
17−
18−#ifndef DASH_PLUGIN_H
19−#define DASH_PLUGIN_H
20−
21−#include <QtQml/QQmlEngine>
22−#include <QtQml/QQmlExtensionPlugin>
23−
24−class DashPlugin : public QQmlExtensionPlugin
25−{
26− Q_OBJECT
27− Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28−
29−public:
30− void registerTypes(const char *uri) override;
31−};
32−
33−#endif
plugins/Dash/qmldirdeleted−11
@@ -1,11 +0,0 @@
1−module Dash
2−plugin Dash-qml
3−typeinfo Dash.qmltypes
4−singleton CardCreatorCache 0.1 CardCreatorCache.qml
5−singleton DashAudioPlayer 0.1 DashAudioPlayer.qml
6−ScopeStyle 0.1 ScopeStyle.qml
7−CardAttributes 0.1 CardAttributes.qml
8−CardSocialActions 0.1 CardSocialActions.qml
9−CroppedImageMinimumSourceSize 0.1 CroppedImageMinimumSourceSize.qml
10−AudioProgressBar 0.1 AudioProgressBar.qml
11−CardAudioProgress 0.1 CardAudioProgress.qml
plugins/Dash/verticaljournal.cppdeleted−280
@@ -1,280 +0,0 @@
1−/*
2− * Copyright (C) 2013, 2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−/*
18− * The implementation is centered around m_columnVisibleItems
19− * that holds a vector of lists. There's a list for each of the
20− * columns the view has. In the list the items of the column are
21− * ordered as they appear topdown in the view. m_indexColumnMap is
22− * used when re-building the list up since given a position
23− * in the middle of the list and the need to create the previous does
24− * not give us enough information to know in which column we have
25− * to position the item so that when we reach the item the view is
26− * correctly layouted at 0 for all the columns
27− */
28−#include "verticaljournal.h"
29−
30−#include <math.h>
31−
32−#include <private/qquickitem_p.h>
33−
34−VerticalJournal::VerticalJournal()
35− : m_columnWidth(0)
36−{
37−}
38−
39−qreal VerticalJournal::columnWidth() const
40−{
41− return m_columnWidth;
42−}
43−
44−void VerticalJournal::setColumnWidth(qreal columnWidth)
45−{
46− if (columnWidth != m_columnWidth) {
47− m_columnWidth = columnWidth;
48− Q_EMIT columnWidthChanged();
49−
50− if (isComponentComplete()) {
51− Q_FOREACH(const auto &column, m_columnVisibleItems) {
52− Q_FOREACH(const ViewItem item, column) {
53− item.m_item->setWidth(columnWidth);
54− }
55− }
56− relayout();
57− }
58− }
59−}
60−
61−void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
62−{
63− *modelIndex = 0;
64− *yPos = std::numeric_limits<qreal>::max();
65−
66− Q_FOREACH(const auto &column, m_columnVisibleItems) {
67− if (!column.isEmpty()) {
68− const ViewItem &item = column.last();
69− *yPos = qMin(*yPos, item.y() + item.height() + rowSpacing());
70− *modelIndex = qMax(*modelIndex, item.m_modelIndex + 1);
71− } else {
72− *yPos = 0;
73− }
74− }
75−}
76−
77−void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
78−{
79− *modelIndex = 0;
80− *yPos = std::numeric_limits<qreal>::lowest();
81− int columnToAddTo = -1;
82−
83− // Find the topmost free column
84− for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
85− const auto &column = m_columnVisibleItems[i];
86− if (!column.isEmpty()) {
87− const ViewItem &item = column.first();
88− const auto itemTopPos = item.y() - rowSpacing();
89− if (itemTopPos > *yPos) {
90− *yPos = itemTopPos;
91− *modelIndex = item.m_modelIndex - 1;
92− columnToAddTo = i;
93− }
94− }
95− }
96−
97− if (*modelIndex > 0) {
98− Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
99− while (*modelIndex > 0 && m_indexColumnMap[*modelIndex] != columnToAddTo) {
100− // We found out that we have to add to columnToAddTo
101− // and thought that we had to add *modelIndex, but history tells
102− // it is not correct, so find up from *modelIndex until we found the index
103− // that has to end up in columnToAddTo
104− *modelIndex = *modelIndex - 1;
105− Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
106− }
107− }
108−}
109−
110−bool VerticalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
111−{
112− bool changed = false;
113−
114− for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
115− QList<ViewItem> &column = m_columnVisibleItems[i];
116− while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFromY) {
117− releaseItem(column.takeFirst().m_item);
118− changed = true;
119− }
120−
121− while (!column.isEmpty() && column.last().y() > bufferToY) {
122− releaseItem(column.takeLast().m_item);
123− changed = true;
124− }
125− }
126−
127− return changed;
128−}
129−
130−void VerticalJournal::addItemToView(int modelIndex, QQuickItem *item)
131−{
132− if (item->width() != m_columnWidth) {
133− qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
134− item->setWidth(m_columnWidth);
135− }
136−
137− // Check if we add it to the bottom of existing column items
138− const QList<ViewItem> &firstColumn = m_columnVisibleItems[0];
139− qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -rowSpacing();
140− int columnToAddTo = 0;
141− for (int i = 1; i < m_columnVisibleItems.count(); ++i) {
142− const QList<ViewItem> &column = m_columnVisibleItems[i];
143− const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -rowSpacing();
144− if (iY < columnToAddY) {
145− columnToAddTo = i;
146− columnToAddY = iY;
147− }
148− }
149−
150− const QList<ViewItem> &columnToAdd = m_columnVisibleItems[columnToAddTo];
151− if (columnToAdd.isEmpty() || columnToAdd.last().m_modelIndex < modelIndex) {
152− item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
153− item->setY(columnToAddY + rowSpacing());
154−
155− m_columnVisibleItems[columnToAddTo] << ViewItem(item, modelIndex);
156− m_indexColumnMap[modelIndex] = columnToAddTo;
157− } else {
158− Q_ASSERT(m_indexColumnMap.contains(modelIndex));
159− columnToAddTo = m_indexColumnMap[modelIndex];
160− columnToAddY = m_columnVisibleItems[columnToAddTo].first().y();
161−
162− item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
163− item->setY(columnToAddY - rowSpacing() - item->height());
164−
165− m_columnVisibleItems[columnToAddTo].prepend(ViewItem(item, modelIndex));
166− }
167−}
168−
169−void VerticalJournal::cleanupExistingItems()
170−{
171− // Cleanup the existing items
172− for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
173− QList<ViewItem> &column = m_columnVisibleItems[i];
174− Q_FOREACH(const ViewItem item, column)
175− releaseItem(item.m_item);
176− column.clear();
177− }
178− m_indexColumnMap.clear();
179− setImplicitHeightDirty();
180−}
181−
182−void VerticalJournal::calculateImplicitHeight()
183−{
184− int lastModelIndex = -1;
185− qreal bottomMostY = 0;
186− Q_FOREACH(const auto &column, m_columnVisibleItems) {
187− if (!column.isEmpty()) {
188− const ViewItem &item = column.last();
189− lastModelIndex = qMax(lastModelIndex, item.m_modelIndex);
190− bottomMostY = qMax(bottomMostY, item.y() + item.height());
191− }
192− }
193− if (lastModelIndex >= 0) {
194− const double averageHeight = bottomMostY / (lastModelIndex + 1);
195− setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - lastModelIndex - 1));
196− } else {
197− setImplicitHeight(0);
198− }
199−}
200−
201−void VerticalJournal::doRelayout()
202−{
203− QList<ViewItem> allItems;
204− Q_FOREACH(const auto &column, m_columnVisibleItems)
205− allItems << column;
206−
207− qSort(allItems);
208−
209− const int nColumns = qMax(1., floor((double)(width() + columnSpacing()) / (m_columnWidth + columnSpacing())));
210− m_columnVisibleItems.resize(nColumns);
211− m_indexColumnMap.clear();
212− for (int i = 0; i < nColumns; ++i)
213− m_columnVisibleItems[i].clear();
214−
215− // If the first of allItems doesn't contain index 0 we need to drop them
216− // all since we can't consistently relayout without the first item being there
217−
218− if (!allItems.isEmpty()) {
219− if (allItems.first().m_modelIndex == 0) {
220− Q_FOREACH(const ViewItem item, allItems)
221− addItemToView(item.m_modelIndex, item.m_item);
222− } else {
223− Q_FOREACH(const ViewItem item, allItems)
224− releaseItem(item.m_item);
225− }
226− }
227−}
228−
229−void VerticalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
230−{
231− Q_FOREACH(const auto &column, m_columnVisibleItems) {
232− Q_FOREACH(const ViewItem item, column) {
233− const bool cull = item.y() + item.height() <= visibleFromY || item.y() >= visibleToY;
234− QQuickItemPrivate::get(item.m_item)->setCulled(cull);
235− }
236− }
237−}
238−
239−void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
240−{
241− Q_FOREACH(const QQmlChangeSet::Change remove, removes) {
242− for (int i = remove.count - 1; i >= 0; --i) {
243− const int indexToRemove = remove.index + i;
244− // Since we only support removing from the end, indexToRemove
245− // must refer to the last item of one of the columns or
246− // be bigger than them (because it's not in the viewport and
247− // thus we have not created a delegate for it)
248− bool found = false;
249− int lastCreatedIndex = INT_MIN;
250− for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
251− QList<ViewItem> &column = m_columnVisibleItems[i];
252− if (!column.isEmpty()) {
253− const int lastColumnIndex = column.last().m_modelIndex;
254− if (lastColumnIndex == indexToRemove) {
255− releaseItem(column.takeLast().m_item);
256− found = true;
257− }
258− lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
259− }
260− }
261− if (!found) {
262− if (indexToRemove < lastCreatedIndex) {
263− qDebug() << "VerticalJournal only supports removal from the end of the model, resetting instead";
264− cleanupExistingItems();
265− break;
266− } else {
267− setImplicitHeightDirty();
268− }
269− }
270− }
271− }
272−}
273−
274−void VerticalJournal::itemGeometryChanged(QQuickItem * /*item*/, const QRectF &newGeometry, const QRectF &oldGeometry)
275−{
276− const qreal heightDiff = newGeometry.height() - oldGeometry.height();
277− if (heightDiff != 0) {
278− relayout();
279− }
280−}
plugins/Dash/verticaljournal.hdeleted−103
@@ -1,103 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#ifndef VERTICALJOURNAL_H
18−#define VERTICALJOURNAL_H
19−
20−#include "abstractdashview.h"
21−
22− /** A vertical journal is a view that creates delegates
23− * based on a model and layouts them in columns following
24− * a top-left most position rule.
25− *
26− * The number of columns is calculated using the width of
27− * the view itself, the columnWidth (i.e. the width of each individual delegate)
28− * and the columnSpacing between columns.
29− *
30− * All delegates are forced to columnWidth if they don't have it.
31− *
32− * The first nColumns items are layouted at row 0 from column 0
33− * to column nColumns-1 in order. After that every new item
34− * is positioned in the column which provides the free topmost
35− * position as possible. If more than one column tie in providing
36− * the topmost free position the leftmost column will be used.
37− *
38− * Example:
39− *
40− * +-----+ +-----+ +-----+
41− * | | | 2 | | |
42− * | | | | | |
43− * | 1 | +-----+ | 3 |
44− * | | +-----+ | |
45− * | | | | +-----+
46− * +-----+ | 4 | +-----+
47− * +-----+ | | | 5 |
48− * | 6 | +-----+ | |
49− * | | +-----+
50− * +-----+
51− *
52− */
53−
54−class VerticalJournal : public AbstractDashView
55−{
56− Q_OBJECT
57−
58− Q_PROPERTY(qreal columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged)
59−
60−friend class VerticalJournalTest;
61−
62−public:
63− VerticalJournal();
64−
65− qreal columnWidth() const;
66− void setColumnWidth(qreal columnWidth);
67−
68−protected:
69− void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override;
70−
71−Q_SIGNALS:
72− void columnWidthChanged();
73−
74−private:
75− class ViewItem
76− {
77− public:
78− ViewItem(QQuickItem *item, int modelIndex) : m_item(item), m_modelIndex(modelIndex) {}
79− qreal x() const { return m_item->x(); }
80− qreal y() const { return m_item->y(); }
81− qreal height() const { return m_item->height(); }
82− bool operator<(const ViewItem v) const { return m_modelIndex < v.m_modelIndex; }
83−
84− QQuickItem *m_item;
85− int m_modelIndex;
86− };
87−
88− void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
89− void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
90− bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
91− void addItemToView(int modelIndex, QQuickItem *item) override;
92− void cleanupExistingItems() override;
93− void calculateImplicitHeight() override;
94− void doRelayout() override;
95− void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
96− void processModelRemoves(const QVector<QQmlChangeSet::Change> &removes) override;
97−
98− QVector<QList<ViewItem>> m_columnVisibleItems;
99− QHash<int, int> m_indexColumnMap;
100− int m_columnWidth;
101−};
102−
103−#endif
plugins/GlobalShortcut/CMakeLists.txt+1−1
@@ -5,6 +5,6 @@ set(globalshortcutplugin_SRCS
5
5
6add_library(GlobalShortcut-qml SHARED ${globalshortcutplugin_SRCS})
6add_library(GlobalShortcut-qml SHARED ${globalshortcutplugin_SRCS})
7
7
8−qt5_use_modules(GlobalShortcut-qml Quick Gui)
8+target_link_libraries(GlobalShortcut-qml Qt5::Quick Qt5::Gui)
9
9
10add_unity8_plugin(GlobalShortcut 1.0 GlobalShortcut TARGETS GlobalShortcut-qml)
10add_unity8_plugin(GlobalShortcut 1.0 GlobalShortcut TARGETS GlobalShortcut-qml)
plugins/GlobalShortcut/globalshortcut.cpp+15−4
@@ -58,15 +58,26 @@ void GlobalShortcut::setActive(bool active)
58
58
59void GlobalShortcut::componentComplete()
59void GlobalShortcut::componentComplete()
60{
60{
61+ if (this->window()) {
62+ QMetaObject::invokeMethod(this, "setupFilterOnWindow", Q_ARG(QQuickWindow*, this->window()));
63+ }
61 connect(this, &QQuickItem::windowChanged, this, &GlobalShortcut::setupFilterOnWindow);
64 connect(this, &QQuickItem::windowChanged, this, &GlobalShortcut::setupFilterOnWindow);
62}
65}
63
66
64void GlobalShortcut::keyPressEvent(QKeyEvent * event)
67void GlobalShortcut::keyPressEvent(QKeyEvent * event)
65{
68{
66− Q_UNUSED(event)
67− if (m_active) {
69+ if (!m_active) return;
70+
71+ event->accept();
68− Q_EMIT triggered(m_shortcut.toString());
72+ Q_EMIT triggered(m_shortcut.toString());
69− }
73+}
74+
75+void GlobalShortcut::keyReleaseEvent(QKeyEvent * event)
76+{
77+ if (!m_active) return;
78+
79+ event->accept();
80+ Q_EMIT released(m_shortcut.toString());
70}
81}
71
82
72void GlobalShortcut::setupFilterOnWindow(QQuickWindow *window)
83void GlobalShortcut::setupFilterOnWindow(QQuickWindow *window)
plugins/GlobalShortcut/globalshortcut.h+5
@@ -55,6 +55,7 @@ public:
55protected:
55protected:
56 void componentComplete() override;
56 void componentComplete() override;
57 void keyPressEvent(QKeyEvent * event) override;
57 void keyPressEvent(QKeyEvent * event) override;
58+ void keyReleaseEvent(QKeyEvent * event) override;
58
59
59Q_SIGNALS:
60Q_SIGNALS:
60 void shortcutChanged(const QVariant &shortcut);
61 void shortcutChanged(const QVariant &shortcut);
@@ -62,6 +63,10 @@ Q_SIGNALS:
62 * Emitted when a global keypress of @p shortcut is detected
63 * Emitted when a global keypress of @p shortcut is detected
63 */
64 */
64 void triggered(const QString &shortcut);
65 void triggered(const QString &shortcut);
66+ /**
67+ * Emitted when a global keypress of @p shortcut is released
68+ */
69+ void released(const QString &shortcut);
65 void activeChanged(bool active);
70 void activeChanged(bool active);
66
71
67private Q_SLOTS:
72private Q_SLOTS:
plugins/GlobalShortcut/globalshortcutregistry.cpp+14−4
@@ -73,20 +73,30 @@ bool GlobalShortcutRegistry::eventFilter(QObject *obj, QEvent *event)
73 Q_ASSERT(m_filteredWindow);
73 Q_ASSERT(m_filteredWindow);
74 Q_ASSERT(obj == static_cast<QObject*>(m_filteredWindow.data()));
74 Q_ASSERT(obj == static_cast<QObject*>(m_filteredWindow.data()));
75
75
76− if (event->type() == QEvent::KeyPress) {
76+ if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
77+
77 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
78 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
79+
80+ // Make a copy of the event so we don't alter it for passing on.
81+ QKeyEvent eCopy(keyEvent->type(),
82+ keyEvent->key(),
83+ keyEvent->modifiers(),
84+ keyEvent->text(),
85+ keyEvent->isAutoRepeat(),
86+ keyEvent->count());
87+ eCopy.ignore();
88+
78 int seq = keyEvent->key() + keyEvent->modifiers();
89 int seq = keyEvent->key() + keyEvent->modifiers();
79 if (m_shortcuts.contains(seq)) {
90 if (m_shortcuts.contains(seq)) {
80 const auto shortcuts = m_shortcuts.value(seq);
91 const auto shortcuts = m_shortcuts.value(seq);
81 Q_FOREACH(const auto &shortcut, shortcuts) {
92 Q_FOREACH(const auto &shortcut, shortcuts) {
82 if (shortcut) {
93 if (shortcut) {
83− qApp->sendEvent(shortcut, keyEvent);
94+ qApp->sendEvent(shortcut, &eCopy);
84− event->accept();
85 }
95 }
86 }
96 }
87 }
97 }
88
98
89− return event->isAccepted();
99+ return eCopy.isAccepted();
90 }
100 }
91
101
92 return QObject::eventFilter(obj, event);
102 return QObject::eventFilter(obj, event);
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−3
@@ -1,4 +1,3 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=9)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3
2
4add_definitions(-DSM_BUSNAME=systemBus)
3add_definitions(-DSM_BUSNAME=systemBus)
@@ -30,8 +29,7 @@ add_library(UnityLauncherAS-qml MODULE
30target_link_libraries(UnityLauncherAS-qml
29target_link_libraries(UnityLauncherAS-qml
31 unity8-private
30 unity8-private
32 ${GSETTINGS_QT_LDFLAGS}
31 ${GSETTINGS_QT_LDFLAGS}
32+ Qt5::DBus Qt5::Qml Qt5::Gui
33 )
33 )
34
34
35−qt5_use_modules(UnityLauncherAS-qml DBus Qml Gui)
36−
37add_unity8_greeter_plugin(Unity.Launcher 0.1 Unity/Launcher TARGETS UnityLauncherAS-qml)
35add_unity8_greeter_plugin(Unity.Launcher 0.1 Unity/Launcher TARGETS UnityLauncherAS-qml)
plugins/Greeter/Unity/Launcher/launcheritem.cpp+19
@@ -76,6 +76,25 @@ void LauncherItem::setIcon(const QString &icon)
76 }
76 }
77}
77}
78
78
79+QStringList LauncherItem::keywords() const
80+{
81+ return m_keywords;
82+}
83+
84+void LauncherItem::setKeywords(const QStringList &keywords)
85+{
86+ if (m_keywords != keywords) {
87+ m_keywords = keywords;
88+ Q_EMIT keywordsChanged(keywords);
89+ }
90+}
91+
92+uint LauncherItem::popularity() const
93+{
94+ // Not exposing usage order in greeter session at this point.
95+ return 0;
96+}
97+
79bool LauncherItem::pinned() const
98bool LauncherItem::pinned() const
80{
99{
81 return m_pinned;
100 return m_pinned;
plugins/Greeter/Unity/Launcher/launcheritem.h+4
@@ -34,6 +34,8 @@ public:
34 QString appId() const override;
34 QString appId() const override;
35 QString name() const override;
35 QString name() const override;
36 QString icon() const override;
36 QString icon() const override;
37+ QStringList keywords() const override;
38+ uint popularity() const override;
37 bool pinned() const override;
39 bool pinned() const override;
38 bool running() const override;
40 bool running() const override;
39 bool recent() const override;
41 bool recent() const override;
@@ -49,6 +51,7 @@ public:
49private:
51private:
50 void setName(const QString &name);
52 void setName(const QString &name);
51 void setIcon(const QString &icon);
53 void setIcon(const QString &icon);
54+ void setKeywords(const QStringList &keywords);
52 void setPinned(bool pinned);
55 void setPinned(bool pinned);
53 void setRunning(bool running);
56 void setRunning(bool running);
54 void setRecent(bool recent);
57 void setRecent(bool recent);
@@ -63,6 +66,7 @@ private:
63 QString m_appId;
66 QString m_appId;
64 QString m_name;
67 QString m_name;
65 QString m_icon;
68 QString m_icon;
69+ QStringList m_keywords;
66 bool m_pinned;
70 bool m_pinned;
67 bool m_running;
71 bool m_running;
68 bool m_recent;
72 bool m_recent;
plugins/Greeter/Unity/Launcher/quicklistmodel.cpp+2
@@ -65,6 +65,8 @@ QVariant QuickListModel::data(const QModelIndex &index, int role) const
65 return m_list.at(index.row()).icon();
65 return m_list.at(index.row()).icon();
66 case RoleClickable:
66 case RoleClickable:
67 return m_list.at(index.row()).clickable();
67 return m_list.at(index.row()).clickable();
68+ case RoleIsPrivate:
69+ return false; // The greeter plugin would just not add any private entries
68 }
70 }
69 return QVariant();
71 return QVariant();
70}
72}
plugins/ImageCache/CMakeLists.txtdeleted−8
@@ -1,8 +0,0 @@
1−add_library(ImageCache-qml MODULE
2− ImageCache.cpp
3− plugin.cpp
4− )
5−
6−qt5_use_modules(ImageCache-qml Gui Qml Quick)
7−
8−add_unity8_plugin(ImageCache 0.1 ImageCache TARGETS ImageCache-qml)
plugins/ImageCache/ImageCache.cppdeleted−135
@@ -1,135 +0,0 @@
1−/*
2− * Copyright (C) 2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#include <QDateTime>
18−#include <QDebug>
19−#include <QDir>
20−#include <QUrl>
21−#include <QUrlQuery>
22−
23−#include "ImageCache.h"
24−
25−ImageCache::ImageCache()
26− : QQuickImageProvider(QQmlImageProviderBase::Image,
27− QQmlImageProviderBase::ForceAsynchronousImageLoading)
28−{
29−}
30−
31−QString ImageCache::imageCacheRoot()
32−{
33− QString xdgCache(qgetenv("XDG_CACHE_HOME"));
34− if (xdgCache.isEmpty()) {
35− xdgCache = QDir::homePath() + QStringLiteral("/.cache");
36− }
37−
38− return QDir::cleanPath(xdgCache) + QStringLiteral("/unity8/imagecache");
39−}
40−
41−QFileInfo ImageCache::imagePath(const QUrl &image)
42−{
43− QUrlQuery query(image);
44−
45− auto name = query.queryItemValue(QStringLiteral("name"));
46− if (name.isEmpty()) {
47− name = QStringLiteral("/paths") + image.toLocalFile();
48− } else {
49− name = QStringLiteral("/names/") + name;
50− }
51−
52− return QFileInfo(imageCacheRoot() + name);
53−}
54−
55−bool ImageCache::needsUpdate(const QUrl &image, const QFileInfo &cachePath, const QSize &imageSize, const QSize &requestedSize, QSize &finalSize)
56−{
57− if (!cachePath.exists())
58− return true;
59−
60− QFileInfo imageInfo(image.toLocalFile());
61− if (imageInfo.lastModified() > cachePath.lastModified())
62− return true;
63−
64− QSize cacheSize(QImageReader(cachePath.filePath()).size());
65− finalSize = calculateSize(imageSize, requestedSize);
66− if (finalSize.isValid() && cacheSize != finalSize)
67− return true;
68−
69− return false;
70−}
71−
72−QSize ImageCache::calculateSize(const QSize &imageSize, const QSize &requestedSize)
73−{
74− QSize finalSize(requestedSize);
75−
76− if (finalSize.width() == 0) {
77− finalSize.setWidth(imageSize.width() * (((double)finalSize.height()) / imageSize.height()));
78− } else if (finalSize.height() == 0) {
79− finalSize.setHeight(imageSize.height() * (((double)finalSize.width()) / imageSize.width()));
80− }
81−
82− return finalSize;
83−}
84−
85−QImage ImageCache::loadAndCacheImage(QImageReader &reader, const QFileInfo &cachePath, const QSize &finalSize)
86−{
87− reader.setQuality(100);
88− reader.setScaledSize(finalSize);
89− auto format = reader.format(); // can't get this after reading
90−
91− QImage loadedImage(reader.read());
92− if (loadedImage.isNull()) {
93− qWarning() << "ImageCache could not read image" << reader.fileName() << ":" << reader.errorString();
94− return QImage();
95− }
96−
97− cachePath.dir().mkpath(QStringLiteral("."));
98− loadedImage.save(cachePath.filePath(), format, 100);
99−
100− return loadedImage;
101−}
102−
103−QImage ImageCache::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
104−{
105− QUrl image(id);
106− QImageReader imageReader(image.toLocalFile());
107− QSize imageSize(imageReader.size());
108− QImage result;
109−
110− // Early exit here, with no sourceSize, scaled-up sourceSize, or bad source image
111− if ((requestedSize.width() <= 0 && requestedSize.height() <= 0) ||
112− imageSize.isEmpty() ||
113− requestedSize.height() >= imageSize.height() ||
114− requestedSize.width() >= imageSize.width()) {
115− // We're only interested in scaling down, not up.
116− result = imageReader.read();
117− *size = result.size();
118− return result;
119− }
120−
121− auto cachePath = imagePath(image);
122− QSize finalSize;
123−
124− if (needsUpdate(image, cachePath, imageSize, requestedSize, finalSize)) {
125− if (finalSize.isEmpty()) {
126− finalSize = calculateSize(imageSize, requestedSize);
127− }
128− result = loadAndCacheImage(imageReader, cachePath, finalSize);
129− } else {
130− result = QImage(cachePath.filePath());
131− }
132−
133− *size = result.size();
134− return result;
135−}
plugins/ImageCache/ImageCache.hdeleted−53
@@ -1,53 +0,0 @@
1−/*
2− * Copyright (C) 2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#pragma once
18−
19−#include <QFileInfo>
20−#include <QImageReader>
21−#include <QQuickImageProvider>
22−#include <QSize>
23−
24−/**
25− * This class accepts an id formulated like a URL. So you'd use something like:
26− *
27− * Image {
28− * source: "image://unity8imagecache/file:///usr/share/..."
29− * }
30− *
31− * Right now, we only support file:/// URLs. We do accept some flags though:
32− *
33− * ?name=NAME
34− * - This will use NAME as the cache lookup key instead of the provided URL.
35− *
36− * We don't do any cleaning of old cached files yet. So you may want to always
37− * provide a name to avoid leaving lots of files around.
38− */
39−
40−class ImageCache: public QQuickImageProvider
41−{
42−public:
43− ImageCache();
44−
45− QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
46−
47−private:
48− static QString imageCacheRoot();
49− static QFileInfo imagePath(const QUrl &image);
50− static bool needsUpdate(const QUrl &image, const QFileInfo &cachePath, const QSize &imageSize, const QSize &requestedSize, QSize &finalSize);
51− static QSize calculateSize(const QSize &imageSize, const QSize &requestedSize);
52− static QImage loadAndCacheImage(QImageReader &reader, const QFileInfo &cachePath, const QSize &finalSize);
53−};
plugins/ImageCache/qmldirdeleted−2
@@ -1,2 +0,0 @@
1−module ImageCache
2−plugin ImageCache-qml
plugins/LightDM/CMakeLists.txt+1−1
@@ -7,7 +7,6 @@ include_directories(
7 ${CMAKE_CURRENT_SOURCE_DIR}
7 ${CMAKE_CURRENT_SOURCE_DIR}
8 ${CMAKE_CURRENT_BINARY_DIR}
8 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_SOURCE_DIR}/plugins/Utils
9 ${CMAKE_SOURCE_DIR}/plugins/Utils
10− ${CMAKE_BINARY_DIR}/tests/mocks/LightDM/IntegratedLightDM
11 ${libunity8-private_SOURCE_DIR}
10 ${libunity8-private_SOURCE_DIR}
12 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
11 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
13)
12)
@@ -18,6 +17,7 @@ set(QMLPLUGIN_SRC
18 DBusGreeterList.cpp
17 DBusGreeterList.cpp
19 Greeter.cpp
18 Greeter.cpp
20 plugin.cpp
19 plugin.cpp
20+ PromptsModel.cpp
21 SessionsModel.cpp
21 SessionsModel.cpp
22 UsersModel.cpp
22 UsersModel.cpp
23 )
23 )
plugins/LightDM/DBusGreeterList.cpp+3−3
@@ -43,10 +43,10 @@ bool DBusGreeterList::entryIsLocked() const
43 return !m_greeter->promptless();
43 return !m_greeter->promptless();
44}
44}
45
45
46−void DBusGreeterList::authenticationUserChangedHandler(const QString &user)
46+void DBusGreeterList::authenticationUserChangedHandler()
47{
47{
48− notifyPropertyChanged(QStringLiteral("ActiveEntry"), user);
48+ notifyPropertyChanged(QStringLiteral("ActiveEntry"), m_greeter->authenticationUser());
49− Q_EMIT EntrySelected(user);
49+ Q_EMIT EntrySelected(m_greeter->authenticationUser());
50}
50}
51
51
52void DBusGreeterList::promptlessChangedHandler()
52void DBusGreeterList::promptlessChangedHandler()
plugins/LightDM/DBusGreeterList.h+1−1
@@ -47,7 +47,7 @@ Q_SIGNALS:
47 void entryIsLockedChanged();
47 void entryIsLockedChanged();
48
48
49private Q_SLOTS:
49private Q_SLOTS:
50− void authenticationUserChangedHandler(const QString &user);
50+ void authenticationUserChangedHandler();
51 void promptlessChangedHandler();
51 void promptlessChangedHandler();
52
52
53private:
53private:
plugins/LightDM/FullLightDM/CMakeLists.txt+6−2
@@ -6,10 +6,14 @@ add_definitions(-DFULL_LIGHTDM)
6
6
7include_directories(
7include_directories(
8 ../
8 ../
9− ${LIBLIGHTDM_INCLUDE_DIRS}
10 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_CURRENT_BINARY_DIR}
11 )
10 )
12
11
12+include_directories(
13+ SYSTEM
14+ ${LIBLIGHTDM_INCLUDE_DIRS}
15+ )
16+
13foreach(source_file ${QMLPLUGIN_SRC})
17foreach(source_file ${QMLPLUGIN_SRC})
14 list(APPEND RELATIVE_QMLPLUGIN_SRC ../${source_file})
18 list(APPEND RELATIVE_QMLPLUGIN_SRC ../${source_file})
15endforeach()
19endforeach()
@@ -22,7 +26,7 @@ target_link_libraries(FullLightDM-qml
22 unity8-private
26 unity8-private
23 ${LIBLIGHTDM_LDFLAGS}
27 ${LIBLIGHTDM_LDFLAGS}
24 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
28 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
29+ Qt5::DBus Qt5::Gui Qt5::Qml
25 )
30 )
26
31
27−qt5_use_modules(FullLightDM-qml DBus Gui Qml)
28add_unity8_plugin(FullLightDM 0.1 LightDM/FullLightDM TARGETS FullLightDM-qml)
32add_unity8_plugin(FullLightDM 0.1 LightDM/FullLightDM TARGETS FullLightDM-qml)
plugins/LightDM/Greeter.cpp+144−14
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013, 2015 Canonical, Ltd.
2+ * Copyright (C) 2013-2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -17,12 +17,16 @@
17
17
18#include "Greeter.h"
18#include "Greeter.h"
19#include "GreeterPrivate.h"
19#include "GreeterPrivate.h"
20+#include <QCoreApplication>
20#include <libintl.h>
21#include <libintl.h>
21
22
23+static Greeter *singleton = nullptr;
24+
22GreeterPrivate::GreeterPrivate(Greeter* parent)
25GreeterPrivate::GreeterPrivate(Greeter* parent)
23 : m_greeter(new QLightDM::Greeter(parent)),
26 : m_greeter(new QLightDM::Greeter(parent)),
24 m_active(false),
27 m_active(false),
25− wasPrompted(false),
28+ responded(false),
29+ everResponded(false),
26 promptless(false),
30 promptless(false),
27 q_ptr(parent)
31 q_ptr(parent)
28{
32{
@@ -41,9 +45,32 @@ Greeter::Greeter(QObject* parent)
41 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,
45 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,
42 this, &Greeter::authenticationCompleteFilter);
46 this, &Greeter::authenticationCompleteFilter);
43
47
48+ // Don't get stuck waiting for PAM as we shut down.
49+ connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
50+ d->m_greeter, &QLightDM::Greeter::cancelAuthentication);
51+
44 d->m_greeter->connectSync();
52 d->m_greeter->connectSync();
45}
53}
46
54
55+Greeter::~Greeter()
56+{
57+ singleton = nullptr;
58+}
59+
60+Greeter *Greeter::instance()
61+{
62+ if (!singleton) {
63+ singleton = new Greeter;
64+ }
65+ return singleton;
66+}
67+
68+PromptsModel *Greeter::promptsModel()
69+{
70+ Q_D(Greeter);
71+ return &d->prompts;
72+}
73+
47bool Greeter::isActive() const
74bool Greeter::isActive() const
48{
75{
49 Q_D(const Greeter);
76 Q_D(const Greeter);
@@ -68,7 +95,16 @@ bool Greeter::isAuthenticated() const
68QString Greeter::authenticationUser() const
95QString Greeter::authenticationUser() const
69{
96{
70 Q_D(const Greeter);
97 Q_D(const Greeter);
98+ return d->cachedAuthUser;
99+}
100+
101+void Greeter::checkAuthenticationUser()
102+{
103+ Q_D(Greeter);
104+ if (d->cachedAuthUser != d->m_greeter->authenticationUser()) {
71− return d->m_greeter->authenticationUser();
105+ d->cachedAuthUser = d->m_greeter->authenticationUser();
106+ Q_EMIT authenticationUserChanged();
107+ }
72}
108}
73
109
74QString Greeter::defaultSessionHint() const
110QString Greeter::defaultSessionHint() const
@@ -86,26 +122,65 @@ bool Greeter::promptless() const
86QString Greeter::selectUser() const
122QString Greeter::selectUser() const
87{
123{
88 Q_D(const Greeter);
124 Q_D(const Greeter);
125+ if (hasGuestAccount() && d->m_greeter->selectGuestHint()) {
126+ return QStringLiteral("*guest");
127+ } else {
89− return d->m_greeter->selectUserHint();
128+ return d->m_greeter->selectUserHint();
129+ }
130+}
131+
132+bool Greeter::hasGuestAccount() const
133+{
134+ Q_D(const Greeter);
135+ return d->m_greeter->hasGuestAccountHint();
136+}
137+
138+bool Greeter::showManualLoginHint() const
139+{
140+ Q_D(const Greeter);
141+ return d->m_greeter->showManualLoginHint();
142+}
143+
144+bool Greeter::hideUsersHint() const
145+{
146+ Q_D(const Greeter);
147+ return d->m_greeter->hideUsersHint();
90}
148}
91
149
92void Greeter::authenticate(const QString &username)
150void Greeter::authenticate(const QString &username)
93{
151{
94 Q_D(Greeter);
152 Q_D(Greeter);
95− d->wasPrompted = false;
153+ d->prompts.clear();
154+ d->responded = false;
155+ d->everResponded = false;
96 if (d->promptless) {
156 if (d->promptless) {
97 d->promptless = false;
157 d->promptless = false;
98 Q_EMIT promptlessChanged();
158 Q_EMIT promptlessChanged();
99 }
159 }
100
160
161+ if (authenticationUser() == username) {
162+ d->prompts = d->leftovers;
163+ }
164+ d->leftovers.clear();
165+
166+ if (username == QStringLiteral("*guest")) {
167+ d->m_greeter->authenticateAsGuest();
168+ } else if (username == QStringLiteral("*other")) {
101− d->m_greeter->authenticate(username);
169+ d->m_greeter->authenticate(nullptr);
170+ } else {
171+ d->m_greeter->authenticate(username);
172+ }
173+
174+ Q_EMIT authenticationStarted();
102 Q_EMIT isAuthenticatedChanged();
175 Q_EMIT isAuthenticatedChanged();
103− Q_EMIT authenticationUserChanged(username);
176+ checkAuthenticationUser();
104}
177}
105
178
106void Greeter::respond(const QString &response)
179void Greeter::respond(const QString &response)
107{
180{
108 Q_D(Greeter);
181 Q_D(Greeter);
182+ d->responded = true;
183+ d->everResponded = true;
109 d->m_greeter->respond(response);
184 d->m_greeter->respond(response);
110}
185}
111
186
@@ -118,32 +193,87 @@ bool Greeter::startSessionSync(const QString &session)
118void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type)
193void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type)
119{
194{
120 Q_D(Greeter);
195 Q_D(Greeter);
121− d->wasPrompted = true;
196+
197+ checkAuthenticationUser(); // may have changed in liblightdm
122
198
123 bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: "));
199 bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: "));
200+ bool isSecret = type == QLightDM::Greeter::PromptTypeSecret;
201+
202+ QString trimmedText;
203+ if (!isDefaultPrompt)
204+ trimmedText = text.trimmed();
124
205
125 // Strip prompt of any colons at the end
206 // Strip prompt of any colons at the end
126− QString trimmedText = text.trimmed();
127 if (trimmedText.endsWith(':') || trimmedText.endsWith(QStringLiteral(":"))) {
207 if (trimmedText.endsWith(':') || trimmedText.endsWith(QStringLiteral(":"))) {
128 trimmedText.chop(1);
208 trimmedText.chop(1);
129 }
209 }
130
210
131− Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret, isDefaultPrompt);
211+ if (trimmedText == "login") {
212+ // 'login' is provided untranslated by LightDM when asking for a manual
213+ // login username.
214+ trimmedText = gettext("Username");
215+ }
216+
217+ if (d->responded) {
218+ d->prompts.clear();
219+ d->responded = false;
220+ }
221+
222+ d->prompts.append(trimmedText, isSecret ? PromptsModel::Secret : PromptsModel::Question);
132}
223}
133
224
134void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)
225void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)
135{
226{
227+ Q_D(Greeter);
228+
229+ checkAuthenticationUser(); // may have changed in liblightdm
230+
136− Q_EMIT showMessage(text, type == QLightDM::Greeter::MessageTypeError);
231+ bool isError = type == QLightDM::Greeter::MessageTypeError;
232+
233+ if (d->responded) {
234+ d->prompts.clear();
235+ d->responded = false;
236+ }
237+ d->prompts.append(text, isError? PromptsModel::Error : PromptsModel::Message);
137}
238}
138
239
139void Greeter::authenticationCompleteFilter()
240void Greeter::authenticationCompleteFilter()
140{
241{
141 Q_D(Greeter);
242 Q_D(Greeter);
142− if (!d->wasPrompted) {
243+
244+ Q_EMIT isAuthenticatedChanged();
245+
246+ bool automatic = !d->everResponded;
247+ bool pamHasLeftoverMessages = !d->prompts.hasPrompt() && d->prompts.rowCount() > 0;
248+
249+ if (isAuthenticated() && automatic) {
143 d->promptless = true;
250 d->promptless = true;
144 Q_EMIT promptlessChanged();
251 Q_EMIT promptlessChanged();
145 }
252 }
146
253
147− Q_EMIT isAuthenticatedChanged();
254+ if (!isAuthenticated()) {
148− Q_EMIT authenticationComplete();
255+ if (pamHasLeftoverMessages) {
256+ d->leftovers = d->prompts; // Prefer PAM's messages
257+ } else if (automatic) {
258+ d->leftovers.append(gettext("Failed to authenticate"), PromptsModel::Error);
259+ } else {
260+ d->leftovers.append(gettext("Invalid password, please try again"), PromptsModel::Error);
261+ }
262+ } else if (pamHasLeftoverMessages) {
263+ automatic = true; // treat this successful login as automatic, so user sees message
264+ d->leftovers = d->prompts;
265+ }
266+
267+ if (automatic) {
268+ d->prompts = d->leftovers; // OK, we'll just use these now
269+ d->leftovers.clear();
270+ d->prompts.append(isAuthenticated() ? gettext("Log In") : gettext("Retry"),
271+ PromptsModel::Button);
272+ }
273+
274+ if (isAuthenticated()) {
275+ Q_EMIT loginSuccess(automatic);
276+ } else {
277+ Q_EMIT loginError(automatic);
278+ }
149}
279}
plugins/LightDM/Greeter.h+17−10
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2012,2013,2015 Canonical, Ltd.
2+ * Copyright (C) 2012-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -21,13 +21,13 @@
21 such edits in the future, and by inserting ourselves here, we have more
21 such edits in the future, and by inserting ourselves here, we have more
22 control. */
22 control. */
23
23
24−#ifndef UNITY_GREETER_H
24+#pragma once
25−#define UNITY_GREETER_H
26
25
27#include <QLightDM/Greeter>
26#include <QLightDM/Greeter>
28#include <QtCore/QObject>
27#include <QtCore/QObject>
29
28
30class GreeterPrivate;
29class GreeterPrivate;
30+class PromptsModel;
31
31
32class Greeter : public QObject
32class Greeter : public QObject
33{
33{
@@ -41,7 +41,8 @@ class Greeter : public QObject
41 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)
41 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)
42
42
43public:
43public:
44− explicit Greeter(QObject* parent=0);
44+ static Greeter *instance();
45+ virtual ~Greeter();
45
46
46 bool isActive() const;
47 bool isActive() const;
47 bool isAuthenticated() const;
48 bool isAuthenticated() const;
@@ -49,6 +50,11 @@ public:
49 QString defaultSessionHint() const;
50 QString defaultSessionHint() const;
50 bool promptless() const;
51 bool promptless() const;
51 QString selectUser() const;
52 QString selectUser() const;
53+ bool hasGuestAccount() const;
54+ bool showManualLoginHint() const;
55+ bool hideUsersHint() const;
56+
57+ PromptsModel *promptsModel();
52
58
53public Q_SLOTS:
59public Q_SLOTS:
54 void authenticate(const QString &username=QString());
60 void authenticate(const QString &username=QString());
@@ -57,21 +63,23 @@ public Q_SLOTS:
57 void setIsActive(bool isActive);
63 void setIsActive(bool isActive);
58
64
59Q_SIGNALS:
65Q_SIGNALS:
60− void showMessage(const QString &text, bool isError);
61− void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
62− void authenticationComplete();
63− void authenticationUserChanged(const QString &user);
66+ void authenticationUserChanged();
64 void isActiveChanged();
67 void isActiveChanged();
65 void isAuthenticatedChanged();
68 void isAuthenticatedChanged();
66 void promptlessChanged();
69 void promptlessChanged();
67 void showGreeter();
70 void showGreeter();
68 void hideGreeter();
71 void hideGreeter();
72+ void loginError(bool automatic);
73+ void loginSuccess(bool automatic);
74+ void authenticationStarted(); // useful for testing
69
75
70 // This signal is emitted by external agents like indicators, and the UI
76 // This signal is emitted by external agents like indicators, and the UI
71 // should switch to this user if possible.
77 // should switch to this user if possible.
72 void requestAuthenticationUser(const QString &user);
78 void requestAuthenticationUser(const QString &user);
73
79
74protected:
80protected:
81+ explicit Greeter(QObject* parent=0);
82+
75 GreeterPrivate * const d_ptr;
83 GreeterPrivate * const d_ptr;
76
84
77 Q_DECLARE_PRIVATE(Greeter)
85 Q_DECLARE_PRIVATE(Greeter)
@@ -80,6 +88,5 @@ private Q_SLOTS:
80 void showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type);
88 void showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type);
81 void showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type);
89 void showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type);
82 void authenticationCompleteFilter();
90 void authenticationCompleteFilter();
91+ void checkAuthenticationUser();
83};
92};
84−
85−#endif
plugins/LightDM/GreeterPrivate.h+13−7
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2015-2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,10 +14,14 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−#ifndef UNITY_GREETER_PRIVATE_H
17+#pragma once
18−#define UNITY_GREETER_PRIVATE_H
19
18
19+#include "PromptsModel.h"
20+#include <QObject>
21+
20−#include <QLightDM/Greeter>
22+namespace QLightDM {
23+ class Greeter;
24+}
21
25
22class GreeterPrivate
26class GreeterPrivate
23{
27{
@@ -26,8 +30,12 @@ public:
26
30
27 QLightDM::Greeter *m_greeter;
31 QLightDM::Greeter *m_greeter;
28 bool m_active;
32 bool m_active;
29− bool wasPrompted;
33+ PromptsModel prompts;
34+ PromptsModel leftovers; // prompts to show during next auth for same user
35+ bool responded;
36+ bool everResponded;
30 bool promptless;
37 bool promptless;
38+ QString cachedAuthUser;
31
39
32protected:
40protected:
33 Greeter * const q_ptr;
41 Greeter * const q_ptr;
@@ -35,5 +43,3 @@ protected:
35private:
43private:
36 Q_DECLARE_PUBLIC(Greeter)
44 Q_DECLARE_PUBLIC(Greeter)
37};
45};
38−
39−#endif // UNITY_GREETER_PRIVATE_H
plugins/LightDM/IntegratedLightDM/CMakeLists.txt+1−1
@@ -25,9 +25,9 @@ target_link_libraries(IntegratedLightDM-qml
25 integratedLightDM
25 integratedLightDM
26 unity8-private
26 unity8-private
27 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
27 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
28+ Qt5::DBus Qt5::Gui Qt5::Qml
28 )
29 )
29
30
30−qt5_use_modules(IntegratedLightDM-qml DBus Gui Qml)
31add_unity8_plugin(IntegratedLightDM 0.1 LightDM/IntegratedLightDM TARGETS IntegratedLightDM-qml)
31add_unity8_plugin(IntegratedLightDM 0.1 LightDM/IntegratedLightDM TARGETS IntegratedLightDM-qml)
32
32
33add_subdirectory(liblightdm)
33add_subdirectory(liblightdm)
plugins/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt+3−4
@@ -26,22 +26,21 @@ target_link_libraries(integratedLightDM
26 ${GLIB_LIBRARIES}
26 ${GLIB_LIBRARIES}
27 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
27 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
28 -lpam
28 -lpam
29+ Qt5::Concurrent Qt5::DBus Qt5::Gui
29)
30)
30target_link_libraries(integratedLightDMSession
31target_link_libraries(integratedLightDMSession
31 ${GLIB_LIBRARIES}
32 ${GLIB_LIBRARIES}
32 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
33 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
33 -lpam
34 -lpam
35+ Qt5::Concurrent Qt5::DBus Qt5::Gui
34)
36)
35target_link_libraries(MockLightDM-demo-shared
37target_link_libraries(MockLightDM-demo-shared
36 ${GLIB_LIBRARIES}
38 ${GLIB_LIBRARIES}
37 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
39 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
38 -lpam
40 -lpam
41+ Qt5::Concurrent Qt5::DBus Qt5::Gui
39)
42)
40
43
41−qt5_use_modules(integratedLightDM Concurrent DBus Gui)
42−qt5_use_modules(integratedLightDMSession Concurrent DBus Gui)
43−qt5_use_modules(MockLightDM-demo-shared Concurrent DBus Gui)
44−
45set_target_properties(integratedLightDM PROPERTIES
44set_target_properties(integratedLightDM PROPERTIES
46 COMPILE_FLAGS "-DSM_BUSNAME=systemBus -fPIC")
45 COMPILE_FLAGS "-DSM_BUSNAME=systemBus -fPIC")
47set_target_properties(integratedLightDMSession PROPERTIES
46set_target_properties(integratedLightDMSession PROPERTIES
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp+7−4
@@ -49,7 +49,7 @@ QString Greeter::authenticationUser() const
49
49
50bool Greeter::hasGuestAccountHint() const
50bool Greeter::hasGuestAccountHint() const
51{
51{
52− return true;
52+ return false;
53}
53}
54
54
55QString Greeter::getHint(const QString &name) const
55QString Greeter::getHint(const QString &name) const
@@ -70,12 +70,12 @@ bool Greeter::hideUsersHint() const
70
70
71bool Greeter::showManualLoginHint() const
71bool Greeter::showManualLoginHint() const
72{
72{
73− return true;
73+ return false;
74}
74}
75
75
76bool Greeter::showRemoteLoginHint() const
76bool Greeter::showRemoteLoginHint() const
77{
77{
78− return true;
78+ return false;
79}
79}
80
80
81QString Greeter::selectUserHint() const
81QString Greeter::selectUserHint() const
@@ -146,7 +146,10 @@ void Greeter::authenticateRemote(const QString &session, const QString &username
146}
146}
147
147
148void Greeter::cancelAuthentication()
148void Greeter::cancelAuthentication()
149−{}
149+{
150+ Q_D(Greeter);
151+ d->cancelAuthentication();
152+}
150
153
151void Greeter::setLanguage (const QString &language)
154void Greeter::setLanguage (const QString &language)
152{
155{
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.h+2−2
@@ -46,18 +46,18 @@ class Q_DECL_EXPORT Greeter : public QObject
46 Q_PROPERTY(QString hostname READ hostname CONSTANT)
46 Q_PROPERTY(QString hostname READ hostname CONSTANT)
47 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
47 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
48
48
49− Q_ENUMS(PromptType MessageType)
50−
51public:
49public:
52 enum PromptType {
50 enum PromptType {
53 PromptTypeQuestion,
51 PromptTypeQuestion,
54 PromptTypeSecret
52 PromptTypeSecret
55 };
53 };
54+ Q_ENUM(PromptType)
56
55
57 enum MessageType {
56 enum MessageType {
58 MessageTypeInfo,
57 MessageTypeInfo,
59 MessageTypeError
58 MessageTypeError
60 };
59 };
60+ Q_ENUM(MessageType)
61
61
62 explicit Greeter(QObject* parent=0);
62 explicit Greeter(QObject* parent=0);
63 virtual ~Greeter();
63 virtual ~Greeter();
plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp+27−22
@@ -206,6 +206,28 @@ public Q_SLOTS:
206 }
206 }
207 }
207 }
208
208
209+ void cancelPam()
210+ {
211+ if (pamHandle != nullptr) {
212+ QFuture<int> pamFuture = futureWatcher.future();
213+ pam_handle *handle = pamHandle;
214+ pamHandle = nullptr; // to disable normal finishPam() handling
215+ pamFuture.cancel();
216+
217+ // Note the empty loop, we just want to clear the futures queue.
218+ // Any further prompts from the pam thread will be immediately
219+ // responded to/dismissed in handlePrompt().
220+ while (respond(QString()));
221+
222+ // Now let signal/slot handling happen so the thread can finish
223+ while (!pamFuture.isFinished()) {
224+ QCoreApplication::processEvents();
225+ }
226+
227+ pam_end(handle, PAM_CONV_ERR);
228+ }
229+ }
230+
209Q_SIGNALS:
231Q_SIGNALS:
210 void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);
232 void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);
211 void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
233 void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
@@ -247,28 +269,6 @@ private Q_SLOTS:
247 }
269 }
248
270
249private:
271private:
250− void cancelPam()
251− {
252− if (pamHandle != nullptr) {
253− QFuture<int> pamFuture = futureWatcher.future();
254− pam_handle *handle = pamHandle;
255− pamHandle = nullptr; // to disable normal finishPam() handling
256− pamFuture.cancel();
257−
258− // Note the empty loop, we just want to clear the futures queue.
259− // Any further prompts from the pam thread will be immediately
260− // responded to/dismissed in handlePrompt().
261− while (respond(QString()));
262−
263− // Now let signal/slot handling happen so the thread can finish
264− while (!pamFuture.isFinished()) {
265− QCoreApplication::processEvents();
266− }
267−
268− pam_end(handle, PAM_CONV_ERR);
269− }
270− }
271−
272 Greeter *greeter;
272 Greeter *greeter;
273 GreeterPrivate *greeterPrivate;
273 GreeterPrivate *greeterPrivate;
274 pam_handle* pamHandle;
274 pam_handle* pamHandle;
@@ -299,6 +299,11 @@ void GreeterPrivate::handleRespond(const QString &response)
299 m_impl->respond(response);
299 m_impl->respond(response);
300}
300}
301
301
302+void GreeterPrivate::cancelAuthentication()
303+{
304+ m_impl->cancelPam();
305+}
306+
302}
307}
303
308
304#include "GreeterPrivate.moc"
309#include "GreeterPrivate.moc"
plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h+1
@@ -38,6 +38,7 @@ public:
38
38
39 void handleAuthenticate();
39 void handleAuthenticate();
40 void handleRespond(const QString &response);
40 void handleRespond(const QString &response);
41+ void cancelAuthentication();
41
42
42protected:
43protected:
43 GreeterImpl *m_impl; // if the backend needs more private data
44 GreeterImpl *m_impl; // if the backend needs more private data
plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp−1
@@ -38,7 +38,6 @@ SessionsModel::SessionsModel(QObject* parent) :
38 QAbstractListModel(parent),
38 QAbstractListModel(parent),
39 d_ptr(new SessionsModelPrivate(this))
39 d_ptr(new SessionsModelPrivate(this))
40{
40{
41− Q_D(SessionsModel);
42 m_roleNames = QAbstractListModel::roleNames();
41 m_roleNames = QAbstractListModel::roleNames();
43 m_roleNames[KeyRole] = "key";
42 m_roleNames[KeyRole] = "key";
44 m_roleNames[TypeRole] = "type";
43 m_roleNames[TypeRole] = "type";
plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h+2−2
@@ -35,8 +35,6 @@ class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
35 {
35 {
36 Q_OBJECT
36 Q_OBJECT
37
37
38− Q_ENUMS(SessionModelRoles SessionType)
39−
40 public:
38 public:
41
39
42 enum SessionModelRoles {
40 enum SessionModelRoles {
@@ -46,11 +44,13 @@ class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
46 IdRole = KeyRole, /** Deprecated */
44 IdRole = KeyRole, /** Deprecated */
47 TypeRole
45 TypeRole
48 };
46 };
47+ Q_ENUM(SessionModelRoles)
49
48
50 enum SessionType {
49 enum SessionType {
51 LocalSessions,
50 LocalSessions,
52 RemoteSessions
51 RemoteSessions
53 };
52 };
53+ Q_ENUM(SessionType)
54
54
55 explicit SessionsModel(QObject* parent=0); /** Deprecated. Loads local sessions*/
55 explicit SessionsModel(QObject* parent=0); /** Deprecated. Loads local sessions*/
56 explicit SessionsModel(SessionsModel::SessionType, QObject* parent=0);
56 explicit SessionsModel(SessionsModel::SessionType, QObject* parent=0);
plugins/LightDM/IntegratedLightDM/liblightdm/UsersModel.h+1−2
@@ -37,8 +37,6 @@ class Q_DECL_EXPORT UsersModel : public QAbstractListModel
37{
37{
38 Q_OBJECT
38 Q_OBJECT
39
39
40− Q_ENUMS(UserModelRoles)
41−
42public:
40public:
43 explicit UsersModel(QObject *parent = 0);
41 explicit UsersModel(QObject *parent = 0);
44 virtual ~UsersModel() = default;
42 virtual ~UsersModel() = default;
@@ -53,6 +51,7 @@ public:
53 BackgroundPathRole,
51 BackgroundPathRole,
54 UidRole
52 UidRole
55 };
53 };
54+ Q_ENUM(UserModelRoles)
56
55
57 int rowCount(const QModelIndex &parent) const override;
56 int rowCount(const QModelIndex &parent) const override;
58 QVariant data(const QModelIndex &index, int role) const override;
57 QVariant data(const QModelIndex &index, int role) const override;
plugins/LightDM/PromptsModel.cppnew file+97
@@ -0,0 +1,97 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ *
16+ */
17+
18+#include "PromptsModel.h"
19+
20+PromptsModel::PromptsModel(QObject* parent)
21+ : QAbstractListModel(parent)
22+{
23+ m_roleNames[TypeRole] = "type";
24+ m_roleNames[TextRole] = "text";
25+}
26+
27+PromptsModel& PromptsModel::operator=(const PromptsModel &other)
28+{
29+ beginResetModel();
30+ m_prompts = other.m_prompts;
31+ endResetModel();
32+ Q_EMIT countChanged();
33+ return *this;
34+}
35+
36+int PromptsModel::rowCount(const QModelIndex &parent) const
37+{
38+ if (parent.isValid())
39+ return 0;
40+
41+ return m_prompts.size();
42+}
43+
44+QVariant PromptsModel::data(const QModelIndex &index, int role) const
45+{
46+ if (!index.isValid() || index.column() > 0 || index.row() >= m_prompts.size())
47+ return QVariant();
48+
49+ switch (role) {
50+ case Qt::DisplayRole: // fallthrough
51+ case TextRole: return m_prompts[index.row()].prompt;
52+ case TypeRole: return m_prompts[index.row()].type;
53+ default: return QVariant();
54+ }
55+}
56+
57+QHash<int, QByteArray> PromptsModel::roleNames() const
58+{
59+ return m_roleNames;
60+}
61+
62+void PromptsModel::prepend(const QString &text, PromptType type)
63+{
64+ beginInsertRows(QModelIndex(), 0, 0);
65+ m_prompts.prepend(PromptInfo{text, type});
66+ endInsertRows();
67+
68+ Q_EMIT countChanged();
69+}
70+
71+void PromptsModel::append(const QString &text, PromptType type)
72+{
73+ beginInsertRows(QModelIndex(), m_prompts.size(), m_prompts.size());
74+ m_prompts.append(PromptInfo{text, type});
75+ endInsertRows();
76+
77+ Q_EMIT countChanged();
78+}
79+
80+void PromptsModel::clear()
81+{
82+ beginResetModel();
83+ m_prompts.clear();
84+ endResetModel();
85+
86+ Q_EMIT countChanged();
87+}
88+
89+bool PromptsModel::hasPrompt() const
90+{
91+ Q_FOREACH(const PromptInfo &info, m_prompts) {
92+ if (info.type == PromptType::Secret || info.type == PromptType::Question) {
93+ return true;
94+ }
95+ }
96+ return false;
97+}
plugins/LightDM/PromptsModel.hnew file+70
@@ -0,0 +1,70 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ *
16+ */
17+
18+#pragma once
19+
20+#include <QAbstractListModel>
21+
22+class PromptsModel : public QAbstractListModel
23+{
24+ Q_OBJECT
25+
26+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
27+
28+public:
29+ enum PromptsModelRoles {
30+ TypeRole = Qt::UserRole,
31+ TextRole,
32+ };
33+ Q_ENUM(PromptsModelRoles)
34+
35+ enum PromptType {
36+ Message,
37+ Error,
38+ Secret,
39+ Question,
40+ Button,
41+ };
42+ Q_ENUM(PromptType)
43+
44+ explicit PromptsModel(QObject* parent=0);
45+
46+ PromptsModel& operator=(const PromptsModel &other);
47+
48+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
49+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
50+ QHash<int, QByteArray> roleNames() const override;
51+
52+ Q_INVOKABLE void prepend(const QString &text, PromptType type);
53+ Q_INVOKABLE void append(const QString &text, PromptType type);
54+
55+ void clear();
56+
57+ bool hasPrompt() const;
58+
59+Q_SIGNALS:
60+ void countChanged();
61+
62+private:
63+ struct PromptInfo {
64+ QString prompt;
65+ PromptType type;
66+ };
67+
68+ QList<PromptInfo> m_prompts;
69+ QHash<int, QByteArray> m_roleNames;
70+};
plugins/LightDM/SessionsModel.cpp+1−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -128,7 +128,6 @@ SessionsModel::SessionsModel(QObject* parent)
128 }
128 }
129
129
130 setModel(m_model);
130 setModel(m_model);
131− setSourceModel(m_model);
132 setSortCaseSensitivity(Qt::CaseInsensitive);
131 setSortCaseSensitivity(Qt::CaseInsensitive);
133 setSortLocaleAware(true);
132 setSortLocaleAware(true);
134 setSortRole(Qt::DisplayRole);
133 setSortRole(Qt::DisplayRole);
plugins/LightDM/SessionsModel.h+5−7
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -15,8 +15,7 @@
15 *
15 *
16 */
16 */
17
17
18−#ifndef UNITY_SESSIONSMODEL_H
18+#pragma once
19−#define UNITY_SESSIONSMODEL_H
20
19
21#include <unitysortfilterproxymodelqml.h>
20#include <unitysortfilterproxymodelqml.h>
22#include <QLightDM/SessionsModel>
21#include <QLightDM/SessionsModel>
@@ -28,10 +27,9 @@ class SessionsModel : public UnitySortFilterProxyModelQML
28{
27{
29 Q_OBJECT
28 Q_OBJECT
30
29
31− Q_ENUMS(SessionModelRoles)
32−
33 Q_PROPERTY(QList<QUrl> iconSearchDirectories READ iconSearchDirectories
30 Q_PROPERTY(QList<QUrl> iconSearchDirectories READ iconSearchDirectories
34 WRITE setIconSearchDirectories NOTIFY iconSearchDirectoriesChanged)
31 WRITE setIconSearchDirectories NOTIFY iconSearchDirectoriesChanged)
32+
35Q_SIGNALS:
33Q_SIGNALS:
36 void iconSearchDirectoriesChanged();
34 void iconSearchDirectoriesChanged();
37
35
@@ -45,6 +43,7 @@ public:
45 TypeRole = QLightDM::SessionsModel::SessionModelRoles::TypeRole,
43 TypeRole = QLightDM::SessionsModel::SessionModelRoles::TypeRole,
46 IconRole
44 IconRole
47 };
45 };
46+ Q_ENUM(SessionModelRoles)
48
47
49 explicit SessionsModel(QObject* parent=nullptr);
48 explicit SessionsModel(QObject* parent=nullptr);
50
49
@@ -55,6 +54,7 @@ public:
55 Q_INVOKABLE QUrl iconUrl(const QString sessionName) const;
54 Q_INVOKABLE QUrl iconUrl(const QString sessionName) const;
56
55
57 void setIconSearchDirectories(const QList<QUrl> searchDirectories);
56 void setIconSearchDirectories(const QList<QUrl> searchDirectories);
57+
58private:
58private:
59 QLightDM::SessionsModel* m_model;
59 QLightDM::SessionsModel* m_model;
60 QHash<int, QByteArray> m_roleNames;
60 QHash<int, QByteArray> m_roleNames;
@@ -64,5 +64,3 @@ private:
64 QUrl("/usr/share/unity-greeter/")};
64 QUrl("/usr/share/unity-greeter/")};
65
65
66};
66};
67−
68−#endif // UNITY_SESSIONSMODEL_H
plugins/LightDM/UsersModel.cpp+176−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -12,20 +12,22 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
17+#include "Greeter.h"
19#include "UsersModel.h"
18#include "UsersModel.h"
19+#include <QIdentityProxyModel>
20#include <QLightDM/UsersModel>
20#include <QLightDM/UsersModel>
21−#include <QtCore/QSortFilterProxyModel>
21+
22+#include <libintl.h>
22
23
23// First, we define an internal class that wraps LightDM's UsersModel. This
24// First, we define an internal class that wraps LightDM's UsersModel. This
24// class will modify some of the data coming from LightDM. For example, we
25// class will modify some of the data coming from LightDM. For example, we
25−// modify any empty Real Names into just normal Names.
26+// modify any empty Real Names into just normal Names. We also add optional
27+// rows, depending on configuration.
26// (We can't modify the data directly in UsersModel below because it won't sort
28// (We can't modify the data directly in UsersModel below because it won't sort
27// using the modified data.)
29// using the modified data.)
28−class MangleModel : public QSortFilterProxyModel
30+class MangleModel : public QIdentityProxyModel
29{
31{
30 Q_OBJECT
32 Q_OBJECT
31
33
@@ -33,29 +35,177 @@ public:
33 explicit MangleModel(QObject* parent=0);
35 explicit MangleModel(QObject* parent=0);
34
36
35 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
37 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
38+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
39+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
40+
41+private:
42+ struct CustomRow {
43+ QString name;
44+ QString realName;
45+ };
46+
47+ int sourceRowCount() const;
48+
49+ void updateGuestRow();
50+ void updateManualRow();
51+ void updateCustomRows();
52+
53+ void addCustomRow(const CustomRow &newRow);
54+ void removeCustomRow(const QString &rowName);
55+
56+ QList<CustomRow> m_customRows;
57+ bool m_updatingCustomRows;
36};
58};
37
59
38MangleModel::MangleModel(QObject* parent)
60MangleModel::MangleModel(QObject* parent)
39− : QSortFilterProxyModel(parent)
61+ : QIdentityProxyModel(parent)
62+ , m_updatingCustomRows(false)
40{
63{
41 setSourceModel(new QLightDM::UsersModel(this));
64 setSourceModel(new QLightDM::UsersModel(this));
65+
66+ updateCustomRows();
67+
68+ // Would be nice if there were a rowCountChanged signal in the base class.
69+ // We redo all custom rows on any row count change, because (A) some of
70+ // custom rows (manual login) use row count information and (B) when
71+ // testing, we use a modelReset signal as a way to indicate that a custom
72+ // row has been toggled off or on.
73+ connect(this, &QIdentityProxyModel::modelReset,
74+ this, &MangleModel::updateCustomRows);
75+ connect(this, &QIdentityProxyModel::rowsInserted,
76+ this, &MangleModel::updateCustomRows);
77+ connect(this, &QIdentityProxyModel::rowsRemoved,
78+ this, &MangleModel::updateCustomRows);
42}
79}
43
80
44QVariant MangleModel::data(const QModelIndex &index, int role) const
81QVariant MangleModel::data(const QModelIndex &index, int role) const
45{
82{
46− QVariant variantData = QSortFilterProxyModel::data(index, role);
83+ QVariant variantData;
84+
85+ if (index.row() >= rowCount())
86+ return QVariant();
87+
88+ bool isCustomRow = index.row() >= sourceRowCount();
89+ if (isCustomRow && index.column() == 0) {
90+ int customIndex = index.row() - sourceRowCount();
91+ if (role == QLightDM::UsersModel::NameRole) {
92+ variantData = m_customRows[customIndex].name;
93+ } else if (role == QLightDM::UsersModel::RealNameRole) {
94+ variantData = m_customRows[customIndex].realName;
95+ } else if (role == QLightDM::UsersModel::LoggedInRole) {
96+ variantData = false;
97+ } else if (role == QLightDM::UsersModel::SessionRole) {
98+ variantData = Greeter::instance()->defaultSessionHint();
99+ }
100+ } else {
101+ variantData = QIdentityProxyModel::data(index, role);
102+ }
47
103
48 // If user's real name is empty, switch to unix name
104 // If user's real name is empty, switch to unix name
49 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
105 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
50− variantData = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
106+ variantData = data(index, QLightDM::UsersModel::NameRole);
51 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
107 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
52 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
108 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
53 variantData = stringData;
109 variantData = stringData;
54 }
110 }
55
111
112+ // Workaround for liblightdm returning "" when a user has no default session
113+ if (Q_UNLIKELY(role == QLightDM::UsersModel::SessionRole && variantData.toString().isEmpty())) {
114+ variantData = Greeter::instance()->defaultSessionHint();
115+ }
116+
56 return variantData;
117 return variantData;
57}
118}
58
119
120+void MangleModel::addCustomRow(const CustomRow &newRow)
121+{
122+ for (int i = 0; i < m_customRows.size(); i++) {
123+ if (m_customRows[i].name == newRow.name) {
124+ return; // we don't have custom rows that change content yet
125+ }
126+ }
127+
128+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
129+ m_customRows << newRow;
130+ endInsertRows();
131+}
132+
133+void MangleModel::removeCustomRow(const QString &rowName)
134+{
135+ for (int i = 0; i < m_customRows.size(); i++) {
136+ if (m_customRows[i].name == rowName) {
137+ int rowNum = sourceRowCount() + i;
138+ beginRemoveRows(QModelIndex(), rowNum, rowNum);
139+ m_customRows.removeAt(i);
140+ endRemoveRows();
141+ break;
142+ }
143+ }
144+}
145+
146+void MangleModel::updateManualRow()
147+{
148+ bool hasAnotherEntry = sourceRowCount() > 0;
149+ for (int i = 0; !hasAnotherEntry && i < m_customRows.size(); i++) {
150+ if (m_customRows[i].name != QStringLiteral("*other")) {
151+ hasAnotherEntry = true;
152+ }
153+ }
154+
155+ // Show manual login if we are asked to OR if no other entry exists
156+ if (Greeter::instance()->showManualLoginHint() || !hasAnotherEntry)
157+ addCustomRow({QStringLiteral("*other"), gettext("Login")});
158+ else
159+ removeCustomRow(QStringLiteral("*other"));
160+}
161+
162+void MangleModel::updateGuestRow()
163+{
164+ if (Greeter::instance()->hasGuestAccount())
165+ addCustomRow({QStringLiteral("*guest"), gettext("Guest Session")});
166+ else
167+ removeCustomRow(QStringLiteral("*guest"));
168+}
169+
170+void MangleModel::updateCustomRows()
171+{
172+ // We update when rowCount changes, but we also insert/remove rows here.
173+ // So guard this function to avoid recursion.
174+ if (m_updatingCustomRows)
175+ return;
176+
177+ m_updatingCustomRows = true;
178+ updateGuestRow();
179+ updateManualRow();
180+ m_updatingCustomRows = false;
181+}
182+
183+int MangleModel::rowCount(const QModelIndex &parent) const
184+{
185+ if (parent.isValid())
186+ return 0;
187+ else
188+ return sourceRowCount() + m_customRows.size();
189+}
190+
191+int MangleModel::sourceRowCount() const
192+{
193+ return Greeter::instance()->hideUsersHint() ? 0 : sourceModel()->rowCount();
194+}
195+
196+QModelIndex MangleModel::index(int row, int column, const QModelIndex &parent) const
197+{
198+ if (row >= rowCount())
199+ return QModelIndex();
200+
201+ bool isCustomRow = row >= sourceRowCount();
202+ if (isCustomRow && !parent.isValid()) {
203+ return createIndex(row, column);
204+ } else {
205+ return QIdentityProxyModel::index(row, column, parent);
206+ }
207+}
208+
59// **** Now we continue with actual UsersModel class ****
209// **** Now we continue with actual UsersModel class ****
60
210
61UsersModel::UsersModel(QObject* parent)
211UsersModel::UsersModel(QObject* parent)
@@ -68,4 +218,21 @@ UsersModel::UsersModel(QObject* parent)
68 sort(0);
218 sort(0);
69}
219}
70
220
221+bool UsersModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
222+{
223+ auto leftName = source_left.data(QLightDM::UsersModel::NameRole);
224+ auto rightName = source_right.data(QLightDM::UsersModel::NameRole);
225+
226+ if (leftName == QStringLiteral("*guest"))
227+ return false;
228+ if (rightName == QStringLiteral("*guest"))
229+ return true;
230+ if (leftName == QStringLiteral("*other"))
231+ return false;
232+ if (rightName == QStringLiteral("*other"))
233+ return true;
234+
235+ return UnitySortFilterProxyModelQML::lessThan(source_left, source_right);
236+}
237+
71#include "UsersModel.moc"
238#include "UsersModel.moc"
plugins/LightDM/UsersModel.h+6−8
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2012,2013 Canonical, Ltd.
2+ * Copyright (C) 2012-2013,2015-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -12,19 +12,16 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
19/* This class is a really tiny filter around QLightDM::UsersModel. There are
17/* This class is a really tiny filter around QLightDM::UsersModel. There are
20 some operations that we want to edit a bit for the benefit of Qml.
18 some operations that we want to edit a bit for the benefit of Qml.
21 Specifically, we want to sort users according to realName. */
19 Specifically, we want to sort users according to realName. */
22
20
23−#ifndef UNITY_USERSMODEL_H
21+#pragma once
24−#define UNITY_USERSMODEL_H
25
22
26#include <unitysortfilterproxymodelqml.h>
23#include <unitysortfilterproxymodelqml.h>
27−#include <QtCore/QObject>
24+#include <QObject>
28
25
29class UsersModel : public UnitySortFilterProxyModelQML
26class UsersModel : public UnitySortFilterProxyModelQML
30{
27{
@@ -32,6 +29,7 @@ class UsersModel : public UnitySortFilterProxyModelQML
32
29
33public:
30public:
34 explicit UsersModel(QObject* parent=0);
31 explicit UsersModel(QObject* parent=0);
35−};
36
32
37−#endif
33+protected:
34+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
35+};
plugins/LightDM/plugin.cpp+12−4
@@ -19,6 +19,7 @@
19#include "DBusGreeter.h"
19#include "DBusGreeter.h"
20#include "DBusGreeterList.h"
20#include "DBusGreeterList.h"
21#include "Greeter.h"
21#include "Greeter.h"
22+#include "PromptsModel.h"
22#include "SessionsModel.h"
23#include "SessionsModel.h"
23#include "UsersModel.h"
24#include "UsersModel.h"
24#include <libusermetricsoutput/ColorTheme.h>
25#include <libusermetricsoutput/ColorTheme.h>
@@ -32,16 +33,24 @@
32
33
33static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
34static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
34{
35{
35− Q_UNUSED(engine)
36 Q_UNUSED(scriptEngine)
36 Q_UNUSED(scriptEngine)
37
37
38− Greeter *greeter = new Greeter();
38+ Greeter *greeter = Greeter::instance();
39 new DBusGreeter(greeter, QStringLiteral("/"));
39 new DBusGreeter(greeter, QStringLiteral("/"));
40 new DBusGreeterList(greeter, QStringLiteral("/list"));
40 new DBusGreeterList(greeter, QStringLiteral("/list"));
41
41
42+ engine->setObjectOwnership(greeter, QQmlEngine::CppOwnership);
43+
42 return greeter;
44 return greeter;
43}
45}
44
46
47+static QObject *prompts_provider(QQmlEngine *engine, QJSEngine *)
48+{
49+ auto model = Greeter::instance()->promptsModel();
50+ engine->setObjectOwnership(model, QQmlEngine::CppOwnership);
51+ return model;
52+}
53+
45static QObject *sessions_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
54static QObject *sessions_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
46{
55{
47 Q_UNUSED(engine)
56 Q_UNUSED(engine)
@@ -78,8 +87,7 @@ void PLUGIN_CLASSNAME::registerTypes(const char *uri)
78 #error No library defined in LightDM plugin
87 #error No library defined in LightDM plugin
79#endif
88#endif
80
89
81− qRegisterMetaType<QLightDM::Greeter::MessageType>("QLightDM::Greeter::MessageType");
90+ qmlRegisterSingletonType<PromptsModel>(uri, 0, 1, "Prompts", prompts_provider);
82− qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
83
91
84 qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
92 qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
85 qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));
93 qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));
plugins/Lights/CMakeLists.txt+8−2
@@ -1,5 +1,6 @@
1pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
1pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
2pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
2pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
3+pkg_check_modules(ANDROID_PROPERTIES REQUIRED libandroid-properties)
3
4
4include_directories(
5include_directories(
5 SYSTEM
6 SYSTEM
@@ -9,9 +10,14 @@ include_directories(
9add_library(Lights-qml MODULE
10add_library(Lights-qml MODULE
10 plugin.cpp
11 plugin.cpp
11 Lights.cpp
12 Lights.cpp
13+ LegacyLights.cpp
14+ HfdLights.cpp
12 )
15 )
13
16
14−qt5_use_modules(Lights-qml Qml Gui)
17+target_link_libraries(Lights-qml
18+ Qt5::Qml Qt5::Gui Qt5::DBus
15−target_link_libraries(Lights-qml ${ANDROID_HARDWARE_LIBRARIES})
19+ ${ANDROID_HARDWARE_LIBRARIES}
20+ ${ANDROID_PROPERTIES_LIBRARIES}
21+ )
16
22
17add_unity8_plugin(Lights 0.1 Lights TARGETS Lights-qml)
23add_unity8_plugin(Lights 0.1 Lights TARGETS Lights-qml)
plugins/Lights/HfdLights.cppnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2021 UBports Foundation.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ *
16+ * Author: Florian Leeber <florian@ubports.com>
17+ */
18+
19+#include "HfdLights.h"
20+#include <QtCore/QDebug>
21+#include <QDBusArgument>
22+#include <QDBusConnection>
23+#include <iostream>
24+
25+#define HFD_SERVICE_NAME "com.lomiri.hfd"
26+#define HFD_SERVICE_PATH "/com/lomiri/hfd"
27+#define HFD_SERVICE_INTERFACE "com.lomiri.hfd.Leds"
28+
29+HfdLights::HfdLights(QObject* parent)
30+ : Lights(parent)
31+{
32+ m_hfdInterface = new QDBusInterface(
33+ QStringLiteral(HFD_SERVICE_NAME),
34+ QStringLiteral(HFD_SERVICE_PATH),
35+ QStringLiteral(HFD_SERVICE_INTERFACE),
36+ QDBusConnection::systemBus(),
37+ this
38+ );
39+}
40+
41+bool HfdLights::init()
42+{
43+ // hfd does not need any initalization to be used
44+ return true;
45+}
46+
47+void HfdLights::turnOn()
48+{
49+ m_hfdInterface->call(QStringLiteral("setState"), 0);
50+ m_hfdInterface->call(QStringLiteral("setColor"), (quint32)m_color.rgba());
51+ m_hfdInterface->call(QStringLiteral("setOnMs"), m_onMs);
52+ m_hfdInterface->call(QStringLiteral("setOffMs"), m_offMs);
53+ m_hfdInterface->call(QStringLiteral("setState"), 1);
54+}
55+
56+void HfdLights::turnOff()
57+{
58+ m_hfdInterface->call(QStringLiteral("setState"), 0);
59+ m_hfdInterface->call(QStringLiteral("setColor"), (quint32)0);
60+ m_hfdInterface->call(QStringLiteral("setOnMs"), 0);
61+ m_hfdInterface->call(QStringLiteral("setOffMs"), 0);
62+}
plugins/Unity/DashCommunicator/plugin.h → plugins/Lights/HfdLights.hrenamed+17−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2021 UBports Foundation.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -13,22 +13,30 @@
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
15 *
16− * Authors: Michael Zanetti <michael.zanetti@canonical.com>
16+ * Author: Florian Leeber <florian@ubports.com>
17 */
17 */
18
18
19−#ifndef DASHCOMMUNICATOR_PLUGIN_H
19+#ifndef UNITY_HFDLIGHTS_H
20−#define DASHCOMMUNICATOR_PLUGIN_H
20+#define UNITY_HFDLIGHTS_H
21
21
22−#include <QtQml/QQmlEngine>
22+#include <QtCore/QObject>
23−#include <QtQml/QQmlExtensionPlugin>
23+#include <QDBusInterface>
24+#include "Lights.h"
24
25
25−class DashCommunicatorPlugin : public QQmlExtensionPlugin
26+class HfdLights: public Lights
26{
27{
27 Q_OBJECT
28 Q_OBJECT
28− Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri) override;
31+ explicit HfdLights(QObject *parent = 0);
32+
33+protected:
34+ bool init() override;
35+ void turnOff() override;
36+ void turnOn() override;
37+
38+private:
39+ QDBusInterface* m_hfdInterface;
32};
40};
33
41
34#endif
42#endif
plugins/Lights/LegacyLights.cppnew file+91
@@ -0,0 +1,91 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ * Copyright (C) 2021 UBports Foundation.
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ *
17+ * Author: Renato Araujo Oliveira Filho <renato.filho@canonical.com>
18+ */
19+
20+#include "LegacyLights.h"
21+#include <QtCore/QDebug>
22+
23+extern "C" {
24+#include "android-hardware.h"
25+#include <string.h>
26+}
27+
28+LegacyLights::~LegacyLights()
29+{
30+ if (m_lightDevice) {
31+ hw_device_t* device = (hw_device_t*) m_lightDevice;
32+ device->close(device);
33+ }
34+}
35+
36+bool LegacyLights::init()
37+{
38+ if (m_lightDevice) {
39+ return true;
40+ }
41+
42+ int err;
43+ hw_module_t* module;
44+
45+ err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
46+ if (err == 0) {
47+ hw_device_t* device;
48+ err = module->methods->open(module, LIGHT_ID_NOTIFICATIONS, &device);
49+ if (err == 0) {
50+ m_lightDevice = (light_device_t*)device;
51+ turnOff();
52+ return true;
53+ } else {
54+ qWarning() << "Failed to access notification lights";
55+ }
56+ } else {
57+ qWarning() << "Failed to initialize lights hardware.";
58+ }
59+ return false;
60+}
61+
62+void LegacyLights::turnOn()
63+{
64+ // pulse
65+ light_state_t state;
66+ memset(&state, 0, sizeof(light_state_t));
67+ state.color = m_color.rgba();
68+ state.flashMode = LIGHT_FLASH_TIMED;
69+ state.flashOnMS = m_onMs;
70+ state.flashOffMS = m_offMs;
71+ state.brightnessMode = BRIGHTNESS_MODE_USER;
72+
73+ if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
74+ qWarning() << "Failed to turn the light off";
75+ }
76+}
77+
78+void LegacyLights::turnOff()
79+{
80+ light_state_t state;
81+ memset(&state, 0, sizeof(light_state_t));
82+ state.color = 0x00000000;
83+ state.flashMode = LIGHT_FLASH_NONE;
84+ state.flashOnMS = 0;
85+ state.flashOffMS = 0;
86+ state.brightnessMode = 0;
87+
88+ if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
89+ qWarning() << "Failed to turn the light off";
90+ }
91+}
tests/mocks/Unity/DashCommunicator/plugin.h → plugins/Lights/LegacyLights.hrenamed+19−8
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
2 * Copyright (C) 2014 Canonical, Ltd.
3+ * Copyright (C) 2021 UBports Foundation.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * 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 * it under the terms of the GNU General Public License as published by
@@ -13,22 +14,32 @@
13 * You should have received a copy of the GNU General Public License
14 * 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/>.
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 *
16− * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17+ * Author: Renato Araujo Oliveira Filho <renato.filho@canonical.com>
17 */
18 */
18
19
19−#ifndef DASHCOMMUNICATOR_PLUGIN_H
20+#ifndef UNITY_LEGACYLIGHTS_H
20−#define DASHCOMMUNICATOR_PLUGIN_H
21+#define UNITY_LEGACYLIGHTS_H
21
22
22−#include <QtQml/QQmlEngine>
23+#include <QtCore/QObject>
23−#include <QtQml/QQmlExtensionPlugin>
24+#include "Lights.h"
24
25
25−class DashCommunicatorPlugin : public QQmlExtensionPlugin
26+struct light_device_t;
27+
28+class LegacyLights: public Lights
26{
29{
27 Q_OBJECT
30 Q_OBJECT
28− Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
31
30public:
32public:
31− void registerTypes(const char *uri) override;
33+ ~LegacyLights();
34+
35+protected:
36+ bool init() override;
37+ void turnOff() override;
38+ void turnOn() override;
39+
40+private:
41+ light_device_t* m_lightDevice;
42+
32};
43};
33
44
34#endif
45#endif
plugins/Lights/Lights.cpp−65
@@ -26,20 +26,12 @@ extern "C" {
26
26
27Lights::Lights(QObject* parent)
27Lights::Lights(QObject* parent)
28 : QObject(parent),
28 : QObject(parent),
29− m_lightDevice(0),
30 m_color("blue"),
29 m_color("blue"),
31 m_state(Lights::Off),
30 m_state(Lights::Off),
32 m_onMs(1000),
31 m_onMs(1000),
33 m_offMs(3000)
32 m_offMs(3000)
34{
33{
35−}
36
34
37−Lights::~Lights()
38−{
39− if (m_lightDevice) {
40− hw_device_t* device = (hw_device_t*) m_lightDevice;
41− device->close(device);
42− }
43}
35}
44
36
45void Lights::setState(Lights::State newState)
37void Lights::setState(Lights::State newState)
@@ -107,60 +99,3 @@ void Lights::setOffMillisec(int offMs)
107 // FIXME: update the property if the light is already on
99 // FIXME: update the property if the light is already on
108 }
100 }
109}
101}
110−
111−bool Lights::init()
112−{
113− if (m_lightDevice) {
114− return true;
115− }
116−
117− int err;
118− hw_module_t* module;
119−
120− err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
121− if (err == 0) {
122− hw_device_t* device;
123− err = module->methods->open(module, LIGHT_ID_NOTIFICATIONS, &device);
124− if (err == 0) {
125− m_lightDevice = (light_device_t*)device;
126− turnOff();
127− return true;
128− } else {
129− qWarning() << "Failed to access notification lights";
130− }
131− } else {
132− qWarning() << "Failed to initialize lights hardware.";
133− }
134− return false;
135−}
136−
137−void Lights::turnOn()
138−{
139− // pulse
140− light_state_t state;
141− memset(&state, 0, sizeof(light_state_t));
142− state.color = m_color.rgba();
143− state.flashMode = LIGHT_FLASH_TIMED;
144− state.flashOnMS = m_onMs;
145− state.flashOffMS = m_offMs;
146− state.brightnessMode = BRIGHTNESS_MODE_USER;
147−
148− if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
149− qWarning() << "Failed to turn the light off";
150− }
151−}
152−
153−void Lights::turnOff()
154−{
155− light_state_t state;
156− memset(&state, 0, sizeof(light_state_t));
157− state.color = 0x00000000;
158− state.flashMode = LIGHT_FLASH_NONE;
159− state.flashOnMS = 0;
160− state.flashOffMS = 0;
161− state.brightnessMode = 0;
162−
163− if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
164− qWarning() << "Failed to turn the light off";
165− }
166−}
plugins/Lights/Lights.h+5−9
@@ -22,12 +22,9 @@
22#include <QtCore/QObject>
22#include <QtCore/QObject>
23#include <QtGui/QColor>
23#include <QtGui/QColor>
24
24
25−struct light_device_t;
26−
27class Lights: public QObject
25class Lights: public QObject
28{
26{
29 Q_OBJECT
27 Q_OBJECT
30− Q_ENUMS(State)
31 Q_PROPERTY(State state READ state WRITE setState NOTIFY stateChanged)
28 Q_PROPERTY(State state READ state WRITE setState NOTIFY stateChanged)
32 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
29 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
33 Q_PROPERTY(int onMillisec READ onMillisec WRITE setOnMillisec NOTIFY onMillisecChanged)
30 Q_PROPERTY(int onMillisec READ onMillisec WRITE setOnMillisec NOTIFY onMillisecChanged)
@@ -38,9 +35,9 @@ public:
38 Off,
35 Off,
39 On,
36 On,
40 };
37 };
38+ Q_ENUM(State)
41
39
42 explicit Lights(QObject *parent = 0);
40 explicit Lights(QObject *parent = 0);
43− ~Lights();
44
41
45 void setState(State newState);
42 void setState(State newState);
46 State state() const;
43 State state() const;
@@ -60,16 +57,15 @@ Q_SIGNALS:
60 void onMillisecChanged(int onMs);
57 void onMillisecChanged(int onMs);
61 void offMillisecChanged(int offMs);
58 void offMillisecChanged(int offMs);
62
59
63−private:
60+protected:
64− light_device_t* m_lightDevice;
65 QColor m_color;
61 QColor m_color;
66 State m_state;
62 State m_state;
67 int m_onMs;
63 int m_onMs;
68 int m_offMs;
64 int m_offMs;
69
65
70− bool init();
66+ virtual bool init() = 0;
71− void turnOff();
67+ virtual void turnOff() = 0;
72− void turnOn();
68+ virtual void turnOn() = 0;
73};
69};
74
70
75#endif
71#endif
plugins/Lights/plugin.cpp+20−1
@@ -18,6 +18,11 @@
18
18
19#include "plugin.h"
19#include "plugin.h"
20#include "Lights.h"
20#include "Lights.h"
21+#include "HfdLights.h"
22+#include "LegacyLights.h"
23+
24+// libandroid-properties
25+#include <hybris/properties/properties.h>
21
26
22#include <QtQml/qqml.h>
27#include <QtQml/qqml.h>
23
28
@@ -25,7 +30,21 @@ static QObject *lights_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25{
30{
26 Q_UNUSED(engine)
31 Q_UNUSED(engine)
27 Q_UNUSED(scriptEngine)
32 Q_UNUSED(scriptEngine)
33+
34+ bool use_hfd = false;
35+ char buffer[PROP_VALUE_MAX];
36+ auto result = property_get("ro.build.version.sdk", buffer, "0");
37+ if (result) {
38+ auto sdkVersion = QString(buffer).toInt();
39+ if (sdkVersion >= 27 || sdkVersion == 0) {
40+ use_hfd = true;
41+ }
42+ }
43+ if (use_hfd) {
28− return new Lights();
44+ return new HfdLights();
45+ } else {
46+ return new LegacyLights();
47+ }
29}
48}
30
49
31void LightsPlugin::registerTypes(const char *uri)
50void LightsPlugin::registerTypes(const char *uri)
plugins/Powerd/CMakeLists.txt+4−2
@@ -9,7 +9,9 @@ add_library(Powerd-qml MODULE
9 Powerd.cpp
9 Powerd.cpp
10 )
10 )
11
11
12−qt5_use_modules(Powerd-qml DBus Qml)
12+target_link_libraries(Powerd-qml
13+ Qt5::DBus Qt5::Qml
13−target_link_libraries(Powerd-qml ${GIO_LDFLAGS})
14+ ${GIO_LDFLAGS}
15+ )
14
16
15add_unity8_plugin(Powerd 0.1 Powerd TARGETS Powerd-qml)
17add_unity8_plugin(Powerd 0.1 Powerd TARGETS Powerd-qml)
plugins/Powerd/Powerd.h+2−2
@@ -27,8 +27,6 @@
27class Powerd: public QObject
27class Powerd: public QObject
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30− Q_ENUMS(Status)
31− Q_ENUMS(DisplayStateChangeReason)
32 Q_PROPERTY(Status status READ status NOTIFY statusChanged)
30 Q_PROPERTY(Status status READ status NOTIFY statusChanged)
33
31
34public:
32public:
@@ -41,11 +39,13 @@ public:
41 SnapDecision = 5, // Display changed state due to snap decision prompt
39 SnapDecision = 5, // Display changed state due to snap decision prompt
42 CallDone = 6, // Display changed state due to voice call end
40 CallDone = 6, // Display changed state due to voice call end
43 };
41 };
42+ Q_ENUM(DisplayStateChangeReason)
44
43
45 enum Status {
44 enum Status {
46 Off,
45 Off,
47 On,
46 On,
48 };
47 };
48+ Q_ENUM(Status)
49
49
50 explicit Powerd(QObject *parent = 0);
50 explicit Powerd(QObject *parent = 0);
51 ~Powerd();
51 ~Powerd();
plugins/ScreenshotDirectory/CMakeLists.txt+1−1
@@ -4,6 +4,6 @@ set(PLUGIN_SOURCES
4)
4)
5
5
6add_library(ScreenshotDirectory-qml MODULE ${PLUGIN_SOURCES})
6add_library(ScreenshotDirectory-qml MODULE ${PLUGIN_SOURCES})
7−qt5_use_modules(ScreenshotDirectory-qml Qml Gui Quick)
7+target_link_libraries(ScreenshotDirectory-qml Qt5::Qml Qt5::Gui Qt5::Quick)
8
8
9add_unity8_plugin(ScreenshotDirectory 0.1 ScreenshotDirectory TARGETS ScreenshotDirectory-qml)
9add_unity8_plugin(ScreenshotDirectory 0.1 ScreenshotDirectory TARGETS ScreenshotDirectory-qml)
plugins/SessionBroadcast/CMakeLists.txt+1−2
@@ -6,9 +6,8 @@ add_library(SessionBroadcast-qml MODULE
6 plugin.cpp
6 plugin.cpp
7 )
7 )
8
8
9−qt5_use_modules(SessionBroadcast-qml DBus Qml)
10−
11target_link_libraries(SessionBroadcast-qml
9target_link_libraries(SessionBroadcast-qml
10+ Qt5::DBus Qt5::Qml
12 ${GLIB_LIBRARIES}
11 ${GLIB_LIBRARIES}
13 )
12 )
14
13
plugins/UInput/CMakeLists.txt+1−1
@@ -3,5 +3,5 @@ add_library(UInput-qml MODULE
3 uinput.cpp
3 uinput.cpp
4)
4)
5
5
6−qt5_use_modules(UInput-qml Qml)
6+target_link_libraries(UInput-qml Qt5::Qml)
7add_unity8_plugin(UInput 0.1 UInput TARGETS UInput-qml)
7add_unity8_plugin(UInput 0.1 UInput TARGETS UInput-qml)
plugins/UInput/uinput.h+1−1
@@ -27,7 +27,6 @@
27class UInput : public QObject
27class UInput : public QObject
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30− Q_ENUMS(Button)
31
30
32public:
31public:
33 enum Button {
32 enum Button {
@@ -35,6 +34,7 @@ public:
35 ButtonRight,
34 ButtonRight,
36 ButtonMiddle
35 ButtonMiddle
37 };
36 };
37+ Q_ENUM(Button)
38
38
39 explicit UInput(QObject *parent = nullptr);
39 explicit UInput(QObject *parent = nullptr);
40 ~UInput();
40 ~UInput();
plugins/Ubuntu/CMakeLists.txt−2
@@ -1,4 +1,2 @@
1add_subdirectory(Gestures)
1add_subdirectory(Gestures)
2add_subdirectory(DownloadDaemonListener)
2add_subdirectory(DownloadDaemonListener)
3−add_subdirectory(Payments)
4−add_subdirectory(SystemImage)
plugins/Ubuntu/DownloadDaemonListener/CMakeLists.txt+4−2
@@ -11,8 +11,10 @@ set(DOWNLOADDAEMONLISTENER_SOURCES
11
11
12add_library(DownloadDaemonListener MODULE ${DOWNLOADDAEMONLISTENER_SOURCES})
12add_library(DownloadDaemonListener MODULE ${DOWNLOADDAEMONLISTENER_SOURCES})
13
13
14−qt5_use_modules(DownloadDaemonListener Qml Quick DBus Core)
15pkg_check_modules(DDLADAPTER REQUIRED udm-common)
14pkg_check_modules(DDLADAPTER REQUIRED udm-common)
16add_definitions(${DDLADAPTER_CFLAGS} ${DDLADAPTER_CFLAGS_OTHER})
15add_definitions(${DDLADAPTER_CFLAGS} ${DDLADAPTER_CFLAGS_OTHER})
17−target_link_libraries(DownloadDaemonListener ${DDLADAPTER_LIBRARIES})
16+target_link_libraries(DownloadDaemonListener
17+ Qt5::Qml Qt5::Quick Qt5::DBus Qt5::Core
18+ ${DDLADAPTER_LIBRARIES}
19+ )
18add_unity8_plugin(Ubuntu.DownloadDaemonListener 0.1 Ubuntu/DownloadDaemonListener TARGETS DownloadDaemonListener)
20add_unity8_plugin(Ubuntu.DownloadDaemonListener 0.1 Ubuntu/DownloadDaemonListener TARGETS DownloadDaemonListener)
plugins/Ubuntu/Gestures/CMakeLists.txt+2−4
@@ -16,10 +16,8 @@ pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
16
16
17add_definitions(-DUBUNTUGESTURESQML_LIBRARY)
17add_definitions(-DUBUNTUGESTURESQML_LIBRARY)
18
18
19−add_library(UbuntuGesturesQml MODULE ${UbuntuGesturesQml_SOURCES})
19+add_library(UbuntuGesturesQml SHARED ${UbuntuGesturesQml_SOURCES})
20−target_link_libraries(UbuntuGesturesQml ${UBUNTUGESTURES_LIBRARIES})
20+target_link_libraries(UbuntuGesturesQml ${UBUNTUGESTURES_LIBRARIES} Qt5::Core Qt5::Quick)
21−
22−qt5_use_modules(UbuntuGesturesQml Core Quick)
23
21
24# So that Foo.cpp can #include "Foo.moc"
22# So that Foo.cpp can #include "Foo.moc"
25include_directories(${CMAKE_CURRENT_BINARY_DIR})
23include_directories(${CMAKE_CURRENT_BINARY_DIR})
plugins/Ubuntu/Gestures/Direction.h+1−1
@@ -26,7 +26,6 @@
26 */
26 */
27class UBUNTUGESTURESQML_EXPORT Direction : public QObject {
27class UBUNTUGESTURESQML_EXPORT Direction : public QObject {
28 Q_OBJECT
28 Q_OBJECT
29− Q_ENUMS(Type)
30
29
31public:
30public:
32 // Make sure it is kept synchronized with SDK UCSwipeArea::Direction
31 // Make sure it is kept synchronized with SDK UCSwipeArea::Direction
@@ -38,6 +37,7 @@ public:
38 Horizontal, // Along the X axis, in any direction
37 Horizontal, // Along the X axis, in any direction
39 Vertical // Along the Y axis, in any direction
38 Vertical // Along the Y axis, in any direction
40 };
39 };
40+ Q_ENUM(Type)
41
41
42 Q_INVOKABLE static bool isHorizontal(Direction::Type type);
42 Q_INVOKABLE static bool isHorizontal(Direction::Type type);
43 Q_INVOKABLE static bool isVertical(Direction::Type type);
43 Q_INVOKABLE static bool isVertical(Direction::Type type);
plugins/Ubuntu/Gestures/TouchGestureArea.cpp+6−5
@@ -328,6 +328,12 @@ void TouchGestureArea::touchEvent_rejected(QTouchEvent *event)
328 }
328 }
329 }
329 }
330 }
330 }
331+
332+ // If this event is a new touch, ignore it and let the item below have it.
333+ // We won't recognize any new touch until the whole current gesture is done
334+ // i.e. we're in WaitingForTouch state.
335+ // And even if it isn't, just ignore it anyway; we don't care about it.
336+ event->ignore();
331}
337}
332
338
333void TouchGestureArea::unownedTouchEvent(QTouchEvent *unownedTouchEvent)
339void TouchGestureArea::unownedTouchEvent(QTouchEvent *unownedTouchEvent)
@@ -735,7 +741,6 @@ void TouchGestureArea::resyncCachedTouchPoints()
735 bool added = false;
741 bool added = false;
736 bool ended = false;
742 bool ended = false;
737 bool moved = false;
743 bool moved = false;
738− bool wantsDrag = false;
739
744
740 // list of deletes
745 // list of deletes
741 QMutableHashIterator<int, GestureTouchPoint*> removeIter(m_cachedTouchPoints);
746 QMutableHashIterator<int, GestureTouchPoint*> removeIter(m_cachedTouchPoints);
@@ -765,10 +770,6 @@ void TouchGestureArea::resyncCachedTouchPoints()
765 }
770 }
766 }
771 }
767
772
768− if (wantsDrag && !dragging()) {
769− setDragging(true);
770− }
771−
772 if (ended) {
773 if (ended) {
773 if (m_cachedTouchPoints.isEmpty()) {
774 if (m_cachedTouchPoints.isEmpty()) {
774 if (!dragging()) Q_EMIT clicked();
775 if (!dragging()) Q_EMIT clicked();
plugins/Ubuntu/Gestures/TouchGestureArea.h+1−1
@@ -114,7 +114,6 @@ private:
114class UBUNTUGESTURESQML_EXPORT TouchGestureArea : public QQuickItem
114class UBUNTUGESTURESQML_EXPORT TouchGestureArea : public QQuickItem
115{
115{
116 Q_OBJECT
116 Q_OBJECT
117− Q_ENUMS(Status)
118
117
119 Q_PROPERTY(int status READ status NOTIFY statusChanged)
118 Q_PROPERTY(int status READ status NOTIFY statusChanged)
120 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
119 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
@@ -137,6 +136,7 @@ public:
137 Recognized,
136 Recognized,
138 Rejected
137 Rejected
139 };
138 };
139+ Q_ENUM(Status)
140 TouchGestureArea(QQuickItem* parent = nullptr);
140 TouchGestureArea(QQuickItem* parent = nullptr);
141 ~TouchGestureArea();
141 ~TouchGestureArea();
142
142
plugins/Ubuntu/Payments/CMakeLists.txtdeleted−20
@@ -1,20 +0,0 @@
1−include_directories(
2− ${CMAKE_CURRENT_SOURCE_DIR}
3− ${CMAKE_CURRENT_BINARY_DIR}
4−)
5−
6−set(PAYMENTS_SOURCES
7− plugin.cpp
8− Payments.cpp
9−)
10−
11−add_library(Payments MODULE ${PAYMENTS_SOURCES})
12−
13−qt5_use_modules(Payments Qml Quick DBus Core)
14−
15−add_unity8_plugin(Ubuntu.Payments 0.1 Ubuntu/Payments TARGETS Payments)
16−
17−pkg_check_modules(LIBPAY REQUIRED pay-2)
18−add_definitions(${LIBPAY_CFLAGS} ${LIBPAY_CFLAGS_OTHER})
19−
20−target_link_libraries(Payments ${LIBPAY_LDFLAGS})
plugins/Ubuntu/Payments/Payments.cppdeleted−136
@@ -1,136 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#include "Payments.h"
18−
19−#include <QLocale>
20−
21−#include <libpay/pay-package.h>
22−
23−
24−// The observer callback for the package we are watching.
25−static void observer(PayPackage* /* package */, const char* itemid, PayPackageItemStatus status, void* user_data) {
26− // This function is called in libpay's thread, so be careful what you call
27− // Emitting signals should be fine as long as they use Queued or Auto
28− // connections (the default)
29− // http://qt-project.org/doc/qt-5/threads-qobject.html#signals-and-slots-across-threads
30−
31− Payments *self = static_cast<Payments*>(user_data);
32−
33− // If the item ID is different, ignore it.
34− if (itemid != self->storeItemId()) {
35− return;
36− }
37−
38− // FIXME: No error reporting from libpay, but we need to show some
39− // types of errors to the user. https://launchpad.net/bugs/1333403
40− switch (status) {
41− case PAY_PACKAGE_ITEM_STATUS_PURCHASED:
42− Q_EMIT self->purchaseCompleted();
43− break;
44− case PAY_PACKAGE_ITEM_STATUS_PURCHASING:
45− self->setPurchasing(true);
46− break;
47− case PAY_PACKAGE_ITEM_STATUS_NOT_PURCHASED:
48− if (self->purchasing()) {
49− self->setPurchasing(false);
50− Q_EMIT self->purchaseCancelled();
51− }
52− break;
53− default:
54− break;
55− }
56−}
57−
58−Payments::Payments(QObject *parent)
59− : QObject(parent),
60− m_purchasing(false)
61−{
62− m_package = pay_package_new("click-scope");
63− pay_package_item_observer_install(m_package, observer, this);
64−}
65−
66−Payments::~Payments()
67−{
68− pay_package_item_observer_uninstall(m_package, observer, this);
69− pay_package_delete(m_package);
70−}
71−
72−QString Payments::currency() const
73−{
74− return m_currency;
75−}
76−
77−double Payments::price() const
78−{
79− return m_price;
80−}
81−
82−QString Payments::storeItemId() const
83−{
84− return m_store_item_id;
85−}
86−
87−QString Payments::formattedPrice() const
88−{
89− QLocale locale;
90− return locale.toCurrencyString(m_price, m_currency);
91−}
92−
93−bool Payments::purchasing() const
94−{
95− return m_purchasing;
96−}
97−
98−void Payments::setCurrency(const QString &currency)
99−{
100− if(m_currency != currency) {
101− m_currency = currency;
102− Q_EMIT currencyChanged(currency);
103− Q_EMIT formattedPriceChanged(formattedPrice());
104− }
105−}
106−
107−void Payments::setPrice(const double price)
108−{
109− if(m_price != price) {
110− m_price = price;
111− Q_EMIT priceChanged(price);
112− Q_EMIT formattedPriceChanged(formattedPrice());
113− }
114−}
115−
116−void Payments::setStoreItemId(const QString &store_item_id)
117−{
118− if (m_store_item_id != store_item_id) {
119− m_store_item_id = store_item_id;
120− Q_EMIT storeItemIdChanged(m_store_item_id);
121− }
122−
123− if (m_store_item_id.isEmpty()) {
124− return;
125− }
126−}
127−
128−void Payments::setPurchasing(bool is_purchasing)
129−{
130− m_purchasing = is_purchasing;
131−}
132−
133−void Payments::start()
134−{
135− pay_package_item_start_purchase(m_package, m_store_item_id.toLocal8Bit().data());
136−}
plugins/Ubuntu/Payments/Payments.hdeleted−69
@@ -1,69 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#ifndef PAYMENTS_H
18−#define PAYMENTS_H
19−
20−#include <QObject>
21−
22−#include <libpay/pay-package.h>
23−
24−
25−class Payments : public QObject
26−{
27− Q_OBJECT
28− Q_DISABLE_COPY(Payments)
29− Q_PROPERTY(QString currency READ currency WRITE setCurrency NOTIFY currencyChanged)
30− Q_PROPERTY(double price READ price WRITE setPrice NOTIFY priceChanged)
31− Q_PROPERTY(QString storeItemId READ storeItemId WRITE setStoreItemId NOTIFY storeItemIdChanged)
32− Q_PROPERTY(QString formattedPrice READ formattedPrice NOTIFY formattedPriceChanged)
33−
34−public:
35− explicit Payments(QObject *parent = 0);
36− virtual ~Payments();
37−
38− QString currency() const;
39− double price() const;
40− QString storeItemId() const;
41− QString formattedPrice() const;
42− bool purchasing() const;
43−
44− void setCurrency(const QString& currency);
45− void setPrice(const double price);
46− void setStoreItemId(const QString& store_item_id);
47− void setPurchasing(bool is_purchasing);
48− Q_INVOKABLE void start();
49−
50−Q_SIGNALS:
51− void currencyChanged(const QString& currency);
52− void priceChanged(const double price);
53− void storeItemIdChanged(const QString &store_item_id);
54− void formattedPriceChanged(const QString &formatted_price);
55−
56− void purchaseError(const QString &error);
57− void purchaseCompleted();
58− void purchaseCancelled();
59−
60−private:
61− QString m_currency;
62− double m_price;
63− QString m_store_item_id;
64− bool m_purchasing;
65−
66− PayPackage *m_package;
67−};
68−
69−#endif // PAYMENTS_H
plugins/Ubuntu/Payments/qmldirdeleted−3
@@ -1,3 +0,0 @@
1−module Ubuntu.Payments
2−plugin Payments
3−typeinfo Payments.qmltypes
plugins/Ubuntu/SystemImage/CMakeLists.txtdeleted−15
@@ -1,15 +0,0 @@
1−include_directories(
2− ${GLIB_INCLUDE_DIRS}
3−)
4−
5−set(SYSTEMIMAGE_SOURCES
6− plugin.cpp
7− SystemImage.cpp
8−)
9−
10−add_library(SystemImage MODULE ${SYSTEMIMAGE_SOURCES})
11−
12−qt5_use_modules(SystemImage Qml DBus Core)
13−target_link_libraries(SystemImage ${GLIB_LIBRARIES})
14−
15−add_unity8_plugin(Ubuntu.SystemImage 0.1 Ubuntu/SystemImage TARGETS SystemImage)
plugins/Ubuntu/SystemImage/SystemImage.cppdeleted−155
@@ -1,155 +0,0 @@
1−/*
2− * Copyright (C) 2014-2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#include <QDBusConnection>
18−#include <QDBusPendingCall>
19−#include <QDBusReply>
20−#include <QDebug>
21−
22−#include <glib.h>
23−
24−#include "SystemImage.h"
25−
26−#define SYSTEMIMAGE_SERVICE QStringLiteral("com.canonical.SystemImage")
27−#define SYSTEMIMAGE_PATH QStringLiteral("/Service")
28−#define SYSTEMIMAGE_IFACE QStringLiteral("com.canonical.SystemImage")
29−
30−Q_LOGGING_CATEGORY(SYSTEMIMAGEPLUGIN, "unity8.systemimage", QtWarningMsg)
31−
32−#define DEBUG_MSG qCDebug(SYSTEMIMAGEPLUGIN).nospace().noquote() << Q_FUNC_INFO
33−#define WARNING_MSG qCWarning(SYSTEMIMAGEPLUGIN).nospace().noquote() << Q_FUNC_INFO
34−
35−SystemImage::SystemImage(QObject *parent)
36− : QObject(parent)
37−{
38− QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("UpdateAvailableStatus"),
39− this, SLOT(onUpdateAvailableStatus(bool,bool,QString,int,QString,QString)));
40− QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("UpdateDownloaded"),
41− this, SLOT(onUpdateDownloaded()));
42− QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("UpdateFailed"),
43− this, SLOT(onUpdateFailed(int,QString)));
44− QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("Applied"),
45− this, SLOT(onUpdateApplied(bool)));
46− QDBusConnection::systemBus().connect(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE, QStringLiteral("Rebooting"),
47− this, SLOT(onRebooting(bool)));
48−}
49−
50−void SystemImage::checkForUpdate()
51−{
52− DEBUG_MSG << "Checking for update";
53− const QDBusMessage msg = QDBusMessage::createMethodCall(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE,
54− QStringLiteral("CheckForUpdate"));
55− QDBusConnection::systemBus().asyncCall(msg);
56−}
57−
58−void SystemImage::applyUpdate()
59−{
60− DEBUG_MSG << "Applying update";
61− setUpdateApplying(true);
62−
63− const QDBusMessage msg = QDBusMessage::createMethodCall(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE,
64− QStringLiteral("ApplyUpdate"));
65− QDBusConnection::systemBus().asyncCall(msg);
66−}
67−
68−void SystemImage::factoryReset()
69−{
70− const QDBusMessage msg = QDBusMessage::createMethodCall(SYSTEMIMAGE_SERVICE, SYSTEMIMAGE_PATH, SYSTEMIMAGE_IFACE,
71− QStringLiteral("FactoryReset"));
72− QDBusConnection::systemBus().asyncCall(msg);
73−}
74−
75−void SystemImage::onUpdateAvailableStatus(bool is_available, bool downloading, const QString &available_version, int update_size,
76− const QString &last_update_date, const QString &error_reason)
77−{
78− DEBUG_MSG << "A new update is " << (is_available ? "" : "NOT") << "available";
79−
80− if (is_available == m_updateAvailable) {
81− return;
82− }
83−
84− m_updateAvailable = is_available;
85− m_downloading = downloading;
86− m_availableVersion = available_version;
87− m_updateSize = formatSize(update_size);
88− m_lastUpdateDate = last_update_date;
89− m_errorReason = error_reason;
90− Q_EMIT updateAvailableStatus();
91−
92− DEBUG_MSG << "Downloading: " << downloading << ", version: " << available_version << ", last update: " << last_update_date <<
93− ", size: " << m_updateSize;
94−}
95−
96−void SystemImage::onUpdateDownloaded()
97−{
98− DEBUG_MSG << "Update downloaded";
99−
100− m_downloaded = true;
101− Q_EMIT updateDownloadedChanged();
102−}
103−
104−void SystemImage::onUpdateFailed(int /*consecutive_failure_count*/, const QString &last_reason)
105−{
106− WARNING_MSG << "System Update failed: " << last_reason;
107− setUpdateApplying(false);
108−}
109−
110−void SystemImage::onUpdateApplied(bool applied)
111−{
112− DEBUG_MSG << "System Update applied with status: " << applied;
113− setUpdateApplying(false);
114− if (applied) {
115− resetUpdateStatus();
116− Q_EMIT updateAvailableStatus();
117− }
118−}
119−
120−void SystemImage::onRebooting(bool status)
121−{
122− setUpdateApplying(false);
123− DEBUG_MSG << "Rebooting: " << status;
124−}
125−
126−void SystemImage::setUpdateApplying(bool status)
127−{
128− if (status != m_updateApplying) {
129− m_updateApplying = status;
130− Q_EMIT updateApplyingChanged();
131− }
132−}
133−
134−void SystemImage::resetUpdateStatus()
135−{
136− m_updateAvailable = false;
137− m_updateApplying = false;
138− m_downloading = false;
139− m_downloaded = false;
140− m_availableVersion.clear();
141− m_updateSize.clear();
142− m_lastUpdateDate.clear();
143− m_errorReason.clear();
144−}
145−
146−QString SystemImage::formatSize(quint64 size) const
147−{
148− guint64 g_size = size;
149−
150− gchar * formatted_size = g_format_size(g_size);
151− QString q_formatted_size = QString::fromLocal8Bit(formatted_size);
152− g_free(formatted_size);
153−
154− return q_formatted_size;
155−}
plugins/Ubuntu/SystemImage/SystemImage.hdeleted−84
@@ -1,84 +0,0 @@
1−/*
2− * Copyright (C) 2014-2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#ifndef SYSTEMIMAGE_H
18−#define SYSTEMIMAGE_H
19−
20−#include <QObject>
21−#include <QLoggingCategory>
22−
23−Q_DECLARE_LOGGING_CATEGORY(SYSTEMIMAGEPLUGIN)
24−
25−class SystemImage : public QObject
26−{
27− Q_OBJECT
28− Q_DISABLE_COPY(SystemImage)
29−
30− Q_PROPERTY(bool updateAvailable READ updateAvailable NOTIFY updateAvailableStatus)
31− Q_PROPERTY(bool updateDownloading READ updateDownloading NOTIFY updateAvailableStatus)
32− Q_PROPERTY(QString availableVersion READ availableVersion NOTIFY updateAvailableStatus)
33− Q_PROPERTY(QString updateSize READ updateSize NOTIFY updateAvailableStatus)
34− Q_PROPERTY(bool updateApplying READ updateApplying NOTIFY updateApplyingChanged)
35− Q_PROPERTY(bool updateDownloaded READ updateDownloaded NOTIFY updateDownloadedChanged)
36−
37−public:
38− explicit SystemImage(QObject *parent = nullptr);
39− ~SystemImage() = default;
40−
41− bool updateAvailable() const { return m_updateAvailable; }
42− bool updateDownloading() const { return m_downloading; }
43− QString availableVersion() const { return m_availableVersion; }
44− QString updateSize() const { return m_updateSize; }
45− bool updateApplying() const { return m_updateApplying; }
46− bool updateDownloaded() const { return m_downloaded; }
47−
48−public Q_SLOTS:
49− Q_INVOKABLE void checkForUpdate();
50− Q_INVOKABLE void applyUpdate();
51− Q_INVOKABLE void factoryReset();
52−
53−private Q_SLOTS:
54− void onUpdateAvailableStatus(bool is_available, bool updateDownloading, const QString &available_version,
55− int update_size, const QString &last_update_date, const QString &error_reason);
56− void onUpdateDownloaded();
57− void onUpdateFailed(int consecutive_failure_count, const QString & last_reason);
58− void onUpdateApplied(bool applied);
59− void onRebooting(bool status);
60−
61−Q_SIGNALS:
62− void updateAvailableStatus();
63− void updateDownloadedChanged();
64− void updateApplyingChanged();
65−
66−private Q_SLOTS:
67− void setUpdateApplying(bool status);
68−
69−private:
70− void resetUpdateStatus();
71− QString formatSize(quint64 size) const;
72−
73−private:
74− bool m_updateAvailable = false;
75− bool m_updateApplying = false;
76− bool m_downloading = false;
77− bool m_downloaded = false;
78− QString m_availableVersion;
79− QString m_updateSize;
80− QString m_lastUpdateDate;
81− QString m_errorReason;
82−};
83−
84−#endif // SYSTEMIMAGE_H
plugins/Ubuntu/SystemImage/plugin.cppdeleted−28
@@ -1,28 +0,0 @@
1−/*
2− * Copyright (C) 2014-2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#include "plugin.h"
18−#include "SystemImage.h"
19−
20−#include <QtQml>
21−
22−void BackendPlugin::registerTypes(const char *uri)
23−{
24− Q_ASSERT(uri == QLatin1String("Ubuntu.SystemImage"));
25−
26− qmlRegisterSingletonType<SystemImage>(uri, 0, 1, "SystemImage",
27− [](QQmlEngine*, QJSEngine*) -> QObject* { return new SystemImage; });
28−}
plugins/Ubuntu/SystemImage/qmldirdeleted−3
@@ -1,3 +0,0 @@
1−module Ubuntu.SystemImage
2−plugin SystemImage
3−typeinfo SystemImage.qmltypes
plugins/Unity/ApplicationMenu/CMakeLists.txtnew file+19
@@ -0,0 +1,19 @@
1+find_package(Qt5DBus REQUIRED)
2+
3+include_directories(
4+ ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_CURRENT_BINARY_DIR}
6+)
7+
8+set(applicationMenu_SRCS
9+ applicationmenuregistry.cpp
10+ dbusapplicationmenuregistry.cpp
11+ plugin.cpp)
12+
13+qt5_add_dbus_adaptor(applicationMenu_SRCS com.ubuntu.MenuRegistrar.xml dbusapplicationmenuregistry.h DBusApplicationMenuRegistry)
14+
15+add_library(ApplicationMenu-qml MODULE ${applicationMenu_SRCS})
16+
17+target_link_libraries(ApplicationMenu-qml Qt5::DBus Qt5::Qml)
18+
19+add_unity8_plugin(Unity.ApplicationMenu 0.1 Unity/ApplicationMenu TARGETS ApplicationMenu-qml)
plugins/Unity/ApplicationMenu/applicationmenuregistry.cppnew file+141
@@ -0,0 +1,141 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include "applicationmenuregistry.h"
18+
19+#include <QQmlEngine>
20+#include <QDebug>
21+
22+Q_LOGGING_CATEGORY(UNITY_APPMENU, "unity.appmenu", QtDebugMsg)
23+
24+#define DEBUG_MSG qCDebug(UNITY_APPMENU).nospace().noquote() << "ApplicationMenuRegistry::" << __func__
25+#define WARNING_MSG qCWarning(UNITY_APPMENU).nospace().noquote() << "ApplicationMenuRegistry::" << __func__
26+
27+ApplicationMenuRegistry::ApplicationMenuRegistry(QObject *parent)
28+ : QObject(parent)
29+{
30+}
31+
32+ApplicationMenuRegistry::~ApplicationMenuRegistry()
33+{
34+ qDeleteAll(m_appMenus);
35+ m_appMenus.clear();
36+
37+ qDeleteAll(m_surfaceMenus);
38+ m_surfaceMenus.clear();
39+}
40+
41+void ApplicationMenuRegistry::RegisterAppMenu(pid_t processId,
42+ const QDBusObjectPath &menuObjectPath,
43+ const QDBusObjectPath &actionObjectPath,
44+ const QString &service)
45+{
46+ DEBUG_MSG << "(pid=" << processId
47+ << ", menuPath=" << menuObjectPath.path()
48+ << ", actionPath=" << actionObjectPath.path()
49+ << ", service=" << service;
50+
51+ auto i = m_appMenus.find(processId);
52+ while (i != m_appMenus.end() && i.key() == processId) {
53+ if (i.value()->m_menuPath == menuObjectPath.path().toUtf8()) {
54+ WARNING_MSG << "Already have a menu for application (pid= " << processId
55+ << ", service=" << service
56+ << ", menuPath=" << menuObjectPath.path() << ")";
57+ return;
58+ }
59+ ++i;
60+ }
61+
62+ auto menu = new MenuServicePath(service, menuObjectPath, actionObjectPath);
63+ QQmlEngine::setObjectOwnership(menu, QQmlEngine::CppOwnership);
64+
65+ m_appMenus.insert(processId, menu);
66+ Q_EMIT appMenuRegistered(processId);
67+}
68+
69+void ApplicationMenuRegistry::UnregisterAppMenu(pid_t processId, const QDBusObjectPath &menuObjectPath)
70+{
71+ DEBUG_MSG << "(pid=" << processId
72+ << ", menuPath=" << menuObjectPath.path();
73+
74+ auto i = m_appMenus.find(processId);
75+ while (i != m_appMenus.end() && i.key() == processId) {
76+ if (i.value()->m_menuPath == menuObjectPath.path().toUtf8()) {
77+ i.value()->deleteLater();
78+ m_appMenus.erase(i);
79+ Q_EMIT appMenuUnregistered(processId);
80+ break;
81+ }
82+ ++i;
83+ }
84+}
85+
86+void ApplicationMenuRegistry::RegisterSurfaceMenu(const QString &surfaceId,
87+ const QDBusObjectPath &menuObjectPath,
88+ const QDBusObjectPath &actionObjectPath,
89+ const QString &service)
90+{
91+ DEBUG_MSG << "(surfaceId=" << surfaceId
92+ << ", menuPath=" << menuObjectPath.path()
93+ << ", actionPath=" << actionObjectPath.path()
94+ << ", service=" << service;
95+
96+ auto i = m_surfaceMenus.find(surfaceId);
97+ while (i != m_surfaceMenus.end() && i.key() == surfaceId) {
98+ if (i.value()->m_menuPath == menuObjectPath.path().toUtf8()) {
99+ WARNING_MSG << "Already have a menu for surface (surfaceId= " << surfaceId
100+ << ", service=" << service
101+ << ", menuPath=" << menuObjectPath.path() << ")";
102+ return;
103+ }
104+ ++i;
105+ }
106+
107+ auto menu = new MenuServicePath(service, menuObjectPath, actionObjectPath);
108+ QQmlEngine::setObjectOwnership(menu, QQmlEngine::CppOwnership);
109+
110+ m_surfaceMenus.insert(surfaceId, menu);
111+ Q_EMIT surfaceMenuRegistered(surfaceId);
112+}
113+
114+void ApplicationMenuRegistry::UnregisterSurfaceMenu(const QString &surfaceId, const QDBusObjectPath &menuObjectPath)
115+{
116+ DEBUG_MSG << "(surfaceId=" << surfaceId
117+ << ", menuPath=" << menuObjectPath.path();
118+
119+ auto i = m_surfaceMenus.find(surfaceId);
120+ while (i != m_surfaceMenus.end() && i.key() == surfaceId) {
121+ if (i.value()->m_menuPath == menuObjectPath.path().toUtf8()) {
122+ i.value()->deleteLater();
123+ m_surfaceMenus.erase(i);
124+ Q_EMIT surfaceMenuUnregistered(surfaceId);
125+ break;
126+ }
127+ ++i;
128+ }
129+}
130+
131+QList<QObject*> ApplicationMenuRegistry::getMenusForSurface(const QString &surfaceId) const
132+{
133+ QList<QObject*> list;
134+
135+ auto i = m_surfaceMenus.find(surfaceId);
136+ while (i != m_surfaceMenus.constEnd() && i.key() == surfaceId) {
137+ list << i.value();
138+ ++i;
139+ }
140+ return list;
141+}
plugins/Unity/ApplicationMenu/applicationmenuregistry.hnew file+83
@@ -0,0 +1,83 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#ifndef APPLICATIONMENUREGISTRY_H
18+#define APPLICATIONMENUREGISTRY_H
19+
20+#include <QObject>
21+#include <QDBusContext>
22+#include <QDBusObjectPath>
23+#include <QtQml>
24+
25+Q_DECLARE_LOGGING_CATEGORY(UNITY_APPMENU)
26+
27+class MenuServicePath : public QObject
28+{
29+ Q_OBJECT
30+ Q_PROPERTY(QByteArray service MEMBER m_service CONSTANT)
31+ Q_PROPERTY(QByteArray menuPath MEMBER m_menuPath CONSTANT)
32+ Q_PROPERTY(QByteArray actionPath MEMBER m_actionPath CONSTANT)
33+public:
34+ explicit MenuServicePath(const QString &service,
35+ const QDBusObjectPath &menuPath,
36+ const QDBusObjectPath &actionPath)
37+ : m_service(service.toUtf8())
38+ , m_menuPath(menuPath.path().toUtf8())
39+ , m_actionPath(actionPath.path().toUtf8())
40+ {}
41+
42+ const QByteArray m_service;
43+ const QByteArray m_menuPath;
44+ const QByteArray m_actionPath;
45+};
46+
47+class ApplicationMenuRegistry : public QObject
48+{
49+ Q_OBJECT
50+public:
51+ virtual ~ApplicationMenuRegistry();
52+
53+ // for qml
54+ Q_INVOKABLE QList<QObject*> getMenusForSurface(const QString &surfaceId) const;
55+
56+ // for dbus
57+ void RegisterAppMenu(pid_t processId,
58+ const QDBusObjectPath &menuObjectPath,
59+ const QDBusObjectPath &actionObjectPath,
60+ const QString &service);
61+ void UnregisterAppMenu(pid_t processId, const QDBusObjectPath &menuObjectPath);
62+
63+ void RegisterSurfaceMenu(const QString &surfaceId,
64+ const QDBusObjectPath &menuObjectPath,
65+ const QDBusObjectPath &actionObjectPath,
66+ const QString &service);
67+ void UnregisterSurfaceMenu(const QString &surfaceId, const QDBusObjectPath &menuObjectPath);
68+
69+Q_SIGNALS:
70+ void appMenuRegistered(uint processId);
71+ void appMenuUnregistered(uint processId);
72+
73+ void surfaceMenuRegistered(const QString& surfaceId);
74+ void surfaceMenuUnregistered(const QString& surfaceId);
75+
76+protected:
77+ explicit ApplicationMenuRegistry(QObject *parent = 0);
78+
79+ QMultiMap<pid_t, MenuServicePath*> m_appMenus;
80+ QMultiMap<QString, MenuServicePath*> m_surfaceMenus;
81+};
82+
83+#endif // APPLICATIONMENUREGISTRY_H
plugins/Unity/ApplicationMenu/com.ubuntu.MenuRegistrar.xmlnew file+83
@@ -0,0 +1,83 @@
1+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
2+<node xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
3+ <dox:d><![CDATA[
4+ @mainpage
5+
6+ An interface to register menus that are associated with a window in an application. The
7+ main interface is documented here: @ref com::ubuntu::MenuRegistrar.
8+
9+ The actual menus are transported using the gmenumodel protocol
10+ ]]></dox:d>
11+ <interface name="com.ubuntu.MenuRegistrar" xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
12+ <dox:d>
13+ An interface to register a menu from an application to be displayed in another
14+ window. This manages that association between processes and/or Mir surface IDs and the dbus
15+ address and object that provides the menus using the org.gtk.Menus interface.
16+ </dox:d>
17+ <method name="RegisterAppMenu">
18+ <dox:d><![CDATA[
19+ Associates a gmenumodel with an application
20+
21+ /note this method assumes that the connection from the caller is the DBus connection
22+ to use for the object. Applications that use multiple DBus connections will need to
23+ ensure this method is called with the same connection that implements the object.
24+ ]]></dox:d>
25+ <arg name="pid" type="u" direction="in">
26+ <dox:d>The process ID of the application for which the menu is associated</dox:d>
27+ </arg>
28+ <arg name="menuObjectPath" type="o" direction="in">
29+ <dox:d>The dbus path where the gmenumodel interface for the application menu has been exported</dox:d>
30+ </arg>
31+ <arg name="actionObjectPath" type="o" direction="in">
32+ <dox:d>The dbus path where the gactionmenu interface for the application menu actions has been exported</dox:d>
33+ </arg>
34+ <arg name="service" type="s" direction="in">
35+ <dox:d>The dbus conection name of the client application to be registered (e.g. :1.23 or org.example.service)</dox:d>
36+ </arg>
37+ </method>
38+ <method name="UnregisterAppMenu">
39+ <dox:d>
40+ A method to allow removing an application menu from the database.
41+ </dox:d>
42+ <arg name="pid" type="u" direction="in">
43+ <dox:d>The process id of the application</dox:d>
44+ </arg>
45+ <arg name="menuObjectPath" type="o" direction="in">
46+ <dox:d>The dbus path for the registered application menu to be unregistered</dox:d>
47+ </arg>
48+ </method>
49+
50+ <method name="RegisterSurfaceMenu">
51+ <dox:d><![CDATA[
52+ Associates a gmenumodel with a surface
53+
54+ /note this method assumes that the connection from the caller is the DBus connection
55+ to use for the object. Applications that use multiple DBus connections will need to
56+ ensure this method is called with the same connection that implements the object.
57+ ]]></dox:d>
58+ <arg name="surface" type="s" direction="in">
59+ <dox:d>The surface ID of the surface</dox:d>
60+ </arg>
61+ <arg name="menuObjectPath" type="o" direction="in">
62+ <dox:d>The dbus path where the gmenumodel interface for the surface menu has been exported</dox:d>
63+ </arg>
64+ <arg name="actionObjectPath" type="o" direction="in">
65+ <dox:d>The dbus path where the gactionmenu interface for the surface menu actions has been exported</dox:d>
66+ </arg>
67+ <arg name="service" type="s" direction="in">
68+ <dox:d>The dbus conection name of the client application to be registered (e.g. :1.23 or org.example.service)</dox:d>
69+ </arg>
70+ </method>
71+ <method name="UnregisterSurfaceMenu">
72+ <dox:d>
73+ A method to allow removing a surface menu from the database.
74+ </dox:d>
75+ <arg name="surfaceId" type="s" direction="in">
76+ <dox:d>The surface id of the surface</dox:d>
77+ </arg>
78+ <arg name="menuObjectPath" type="o" direction="in">
79+ <dox:d>The dbus path for the registered surface menu to be unregistered</dox:d>
80+ </arg>
81+ </method>
82+ </interface>
83+</node>
plugins/Unity/ApplicationMenu/dbusapplicationmenuregistry.cppnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include "dbusapplicationmenuregistry.h"
18+#include "menuregistraradaptor.h"
19+
20+DBusApplicationMenuRegistry::DBusApplicationMenuRegistry(QObject *parent)
21+ : ApplicationMenuRegistry(parent)
22+{
23+ new MenuRegistrarAdaptor(this);
24+
25+ QDBusConnection connection = QDBusConnection::sessionBus();
26+ if (!connection.registerObject("/com/ubuntu/MenuRegistrar", this)) {
27+ qCWarning(UNITY_APPMENU) << "Unable to register DBus object /com/ubuntu/MenuRegistrar";
28+ }
29+ if (!connection.registerService("com.ubuntu.MenuRegistrar")) {
30+ qCWarning(UNITY_APPMENU) << "Unable to register DBus service com.ubuntu.MenuRegistrar";
31+ }
32+}
33+
34+DBusApplicationMenuRegistry::~DBusApplicationMenuRegistry()
35+{
36+ QDBusConnection connection = QDBusConnection::sessionBus();
37+ connection.unregisterObject("/com/ubuntu/MenuRegistrar");
38+}
39+
40+ApplicationMenuRegistry *DBusApplicationMenuRegistry::instance()
41+{
42+ static ApplicationMenuRegistry* reg(new DBusApplicationMenuRegistry());
43+ return reg;
44+}
plugins/Ubuntu/SystemImage/plugin.h → plugins/Unity/ApplicationMenu/dbusapplicationmenuregistry.hrenamed+12−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,18 +14,21 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−#ifndef SYSTEMIMAGE_PLUGIN_H
17+#ifndef DBUSAPPLICATIONMENUREGISTRY_H
18−#define SYSTEMIMAGE_PLUGIN_H
18+#define DBUSAPPLICATIONMENUREGISTRY_H
19
19
20−#include <QQmlExtensionPlugin>
20+#include "applicationmenuregistry.h"
21
21
22−class BackendPlugin : public QQmlExtensionPlugin
22+class DBusApplicationMenuRegistry : public ApplicationMenuRegistry,
23+ protected QDBusContext
23{
24{
24 Q_OBJECT
25 Q_OBJECT
25− Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
26−
27public:
26public:
28− void registerTypes(const char *uri) override;
27+ ~DBusApplicationMenuRegistry();
28+ static ApplicationMenuRegistry* instance();
29+
30+protected:
31+ explicit DBusApplicationMenuRegistry(QObject *parent = 0);
29};
32};
30
33
31−#endif // SYSTEMIMAGE_PLUGIN_H
34+#endif // DBUSAPPLICATIONMENUREGISTRY_H
plugins/Unity/ApplicationMenu/plugin.cppnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include "plugin.h"
18+#include "dbusapplicationmenuregistry.h"
19+
20+#include <QtQml>
21+
22+static QObject *menuRegistry(QQmlEngine *engine, QJSEngine *scriptEngine)
23+{
24+ Q_UNUSED(engine)
25+ Q_UNUSED(scriptEngine)
26+ return DBusApplicationMenuRegistry::instance();
27+}
28+
29+void ApplicationMenuPlugin::registerTypes(const char *uri)
30+{
31+ Q_ASSERT(uri == QLatin1String("Unity.ApplicationMenu"));
32+
33+ qmlRegisterUncreatableType<MenuServicePath>(uri, 0, 1, "MenuServicePath", "You cannot create a MenuServicePath");
34+ qmlRegisterSingletonType<DBusApplicationMenuRegistry>(uri, 0, 1, "ApplicationMenuRegistry", menuRegistry);
35+}
36+
37+void ApplicationMenuPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
38+{
39+ QQmlExtensionPlugin::initializeEngine(engine, uri);
40+
41+ menuRegistry(nullptr, nullptr);
42+}
plugins/Ubuntu/Payments/plugin.h → plugins/Unity/ApplicationMenu/plugin.hrenamed+6−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,18 +14,19 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−#ifndef PAYMENTS_PLUGIN_H
17+#ifndef APPLICATIONMENU_PLUGIN_H
18−#define PAYMENTS_PLUGIN_H
18+#define APPLICATIONMENU_PLUGIN_H
19
19
20#include <QQmlExtensionPlugin>
20#include <QQmlExtensionPlugin>
21
21
22−class BackendPlugin : public QQmlExtensionPlugin
22+class ApplicationMenuPlugin : public QQmlExtensionPlugin
23{
23{
24 Q_OBJECT
24 Q_OBJECT
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28 void registerTypes(const char *uri) override;
28 void registerTypes(const char *uri) override;
29+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
29};
30};
30
31
31−#endif // PAYMENTS_PLUGIN_H
32+#endif // APPLICATIONMENU_PLUGIN_H
plugins/Unity/ApplicationMenu/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Unity.ApplicationMenu
2+plugin ApplicationMenu-qml
3+typeinfo ApplicationMenu.qmltypes
plugins/Unity/CMakeLists.txt+1−1
@@ -1,7 +1,7 @@
1+add_subdirectory(ApplicationMenu)
1add_subdirectory(Connectivity)
2add_subdirectory(Connectivity)
2add_subdirectory(Indicators)
3add_subdirectory(Indicators)
3add_subdirectory(Launcher)
4add_subdirectory(Launcher)
4add_subdirectory(Session)
5add_subdirectory(Session)
5−add_subdirectory(DashCommunicator)
6add_subdirectory(InputInfo)
6add_subdirectory(InputInfo)
7add_subdirectory(Platform)
7add_subdirectory(Platform)
plugins/Unity/Connectivity/CMakeLists.txt+1−1
@@ -5,6 +5,6 @@ set(CONNECTIVITY_SOURCES
5
5
6add_library(Connectivity MODULE ${CONNECTIVITY_SOURCES})
6add_library(Connectivity MODULE ${CONNECTIVITY_SOURCES})
7
7
8−qt5_use_modules(Connectivity Qml DBus Core)
8+target_link_libraries(Connectivity Qt5::Qml Qt5::DBus Qt5::Core)
9
9
10add_unity8_plugin(Unity.Connectivity 0.1 Unity/Connectivity TARGETS Connectivity)
10add_unity8_plugin(Unity.Connectivity 0.1 Unity/Connectivity TARGETS Connectivity)
plugins/Unity/DashCommunicator/CMakeLists.txtdeleted−25
@@ -1,25 +0,0 @@
1−include_directories(
2− ${CMAKE_CURRENT_SOURCE_DIR}
3− ${libunity8-private_SOURCE_DIR}
4−)
5−
6−set(QMLDASHCOMMUNICATORPLUGIN_SRC
7− plugin.cpp
8− dbusdashcommunicatorservice.cpp
9− dashcommunicatorservice.cpp
10− dashcommunicator.cpp
11− dashconnection.cpp
12−)
13−
14−add_library(DashCommunicator-qml MODULE
15− ${QMLDASHCOMMUNICATORPLUGIN_SRC}
16−)
17−
18−qt5_use_modules(DashCommunicator-qml DBus Qml)
19−
20−target_link_libraries(DashCommunicator-qml
21− unity8-private
22−)
23−
24−# export the qmldir and qmltypes files
25−add_unity8_plugin(Unity.DashCommunicator 0.1 Unity/DashCommunicator TARGETS DashCommunicator-qml)
plugins/Unity/DashCommunicator/dashcommunicator.cppdeleted−58
@@ -1,58 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it under
5− * the terms of the GNU Lesser General Public License version 3, as published by
6− * the Free Software Foundation.
7− *
8− * This program is distributed in the hope that it will be useful, but WITHOUT
9− * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10− * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11− * Lesser General Public License for more details.
12− *
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/>.
15− */
16−
17−#include "dashcommunicator.h"
18−#include "dashconnection.h"
19−
20−#include <QObject>
21−
22−DashCommunicator::DashCommunicator(QObject *parent):
23− QThread(parent),
24− m_dashConnection(nullptr),
25− m_created(false)
26−{
27− start();
28−}
29−
30−DashCommunicator::~DashCommunicator()
31−{
32− quit();
33− wait();
34−}
35−
36−void DashCommunicator::setCurrentScope(int index, bool animate, bool isSwipe)
37−{
38− m_mutex.lock();
39− if (m_created) {
40− QMetaObject::invokeMethod(m_dashConnection, "setCurrentScope",
41− Q_ARG(int, index),
42− Q_ARG(bool, animate),
43− Q_ARG(bool, isSwipe));
44− }
45− m_mutex.unlock();
46−}
47−
48−void DashCommunicator::run()
49−{
50− m_dashConnection = new DashConnection(QStringLiteral("com.canonical.UnityDash"),
51− QStringLiteral("/com/canonical/UnityDash"),
52− QLatin1String(""), this);
53− m_mutex.lock();
54− m_created = true;
55− m_mutex.unlock();
56−
57− exec();
58−}
plugins/Unity/DashCommunicator/dashcommunicatorservice.cppdeleted−30
@@ -1,30 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it under
5− * the terms of the GNU Lesser General Public License version 3, as published by
6− * the Free Software Foundation.
7− *
8− * This program is distributed in the hope that it will be useful, but WITHOUT
9− * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10− * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11− * Lesser General Public License for more details.
12− *
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/>.
15− */
16−
17−#include "dashcommunicatorservice.h"
18−
19−DashCommunicatorService::DashCommunicatorService(QObject *parent):
20− QObject(parent),
21− m_dbusService(new DBusDashCommunicatorService(this))
22−{
23− connect(m_dbusService, &DBusDashCommunicatorService::setCurrentScopeRequested, this, &DashCommunicatorService::setCurrentScopeRequested);
24−}
25−
26−
27−DashCommunicatorService::~DashCommunicatorService()
28−{
29−
30−}
plugins/Unity/DashCommunicator/dashcommunicatorservice.hdeleted−38
@@ -1,38 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it under
5− * the terms of the GNU Lesser General Public License version 3, as published by
6− * the Free Software Foundation.
7− *
8− * This program is distributed in the hope that it will be useful, but WITHOUT
9− * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10− * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11− * Lesser General Public License for more details.
12− *
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/>.
15− */
16−
17−#ifndef DASHCOMMUNICATORSERVICE_H
18−#define DASHCOMMUNICATORSERVICE_H
19−
20−#include "dbusdashcommunicatorservice.h"
21−
22−#include <QObject>
23−
24−class DashCommunicatorService: public QObject
25−{
26− Q_OBJECT
27−public:
28− DashCommunicatorService(QObject *parent = 0);
29− ~DashCommunicatorService();
30−
31−Q_SIGNALS:
32− void setCurrentScopeRequested(int index, bool animate, bool isSwipe);
33−
34−private:
35− DBusDashCommunicatorService *m_dbusService;
36−};
37−
38−#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Unity/DashCommunicator/dashconnection.cppdeleted−63
@@ -1,63 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it under
5− * the terms of the GNU Lesser General Public License version 3, as published by
6− * the Free Software Foundation.
7− *
8− * This program is distributed in the hope that it will be useful, but WITHOUT
9− * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10− * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11− * Lesser General Public License for more details.
12− *
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/>.
15− */
16−
17−#include "dashconnection.h"
18−
19−#include <QDBusInterface>
20−#include <QDBusPendingCall>
21−
22−/* The default implementation of AbstractDBusServiceMonitor creates a QDBusInterface when the service
23− * appears on the bus.
24− *
25− * On construction QDBusInterface synchronously introspects the service, which will block the GUI
26− * thread of this process if the service is busy. QDBusAbstractInterface does not perform this
27− * introspection, so let's subclass that and avoid the blocking scenario.
28− *
29− * However we lose Qt's wrapping of the DBus service with a MetaObject, with the result that we
30− * cannot easily connect to DBus signals with the usual connect() calls. So this approach only
31− * suited to push communication with the DBus service.
32− */
33−class AsyncDBusInterface : public QDBusAbstractInterface
34−{
35−public:
36− AsyncDBusInterface(const QString &service, const QString &path,
37− const QString &interface, const QDBusConnection &connection,
38− QObject *parent = 0)
39− : QDBusAbstractInterface(service, path, interface.toLatin1().data(), connection, parent)
40− {}
41− ~AsyncDBusInterface() = default;
42−};
43−
44−
45−DashConnection::DashConnection(const QString &service, const QString &path, const QString &interface, QObject *parent):
46− AbstractDBusServiceMonitor(service, path, interface, SessionBus, parent)
47−{
48−
49−}
50−
51−/* Override the default implementation to create a non-blocking DBus interface (see note above). */
52−QDBusAbstractInterface* DashConnection::createInterface(const QString &service, const QString &path,
53− const QString &interface, const QDBusConnection &connection)
54−{
55− return new AsyncDBusInterface(service, path, interface, connection);
56−}
57−
58−void DashConnection::setCurrentScope(int index, bool animate, bool isSwipe)
59−{
60− if (dbusInterface()) {
61− dbusInterface()->asyncCall(QStringLiteral("SetCurrentScope"), index, animate, isSwipe);
62− }
63−}
plugins/Unity/DashCommunicator/dashconnection.hdeleted−37
@@ -1,37 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it under
5− * the terms of the GNU Lesser General Public License version 3, as published by
6− * the Free Software Foundation.
7− *
8− * This program is distributed in the hope that it will be useful, but WITHOUT
9− * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10− * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11− * Lesser General Public License for more details.
12− *
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/>.
15− */
16−
17−#ifndef THREADEDDASHCONNECTION_H
18−#define THREADEDDASHCONNECTION_H
19−
20−// local
21−#include "abstractdbusservicemonitor.h"
22−
23−class DashConnection: public AbstractDBusServiceMonitor
24−{
25− Q_OBJECT
26−public:
27− DashConnection(const QString &service, const QString &path, const QString &interface, QObject *parent = 0);
28−
29−public Q_SLOTS:
30− void setCurrentScope(int index, bool animate, bool isSwipe);
31−
32−private:
33− QDBusAbstractInterface* createInterface(const QString &service, const QString &path,
34− const QString &interface, const QDBusConnection &connection) override;
35−};
36−
37−#endif
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cppdeleted−35
@@ -1,35 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it under
5− * the terms of the GNU Lesser General Public License version 3, as published by
6− * the Free Software Foundation.
7− *
8− * This program is distributed in the hope that it will be useful, but WITHOUT
9− * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10− * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11− * Lesser General Public License for more details.
12− *
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/>.
15− */
16−
17−#include "dbusdashcommunicatorservice.h"
18−
19−#include <QDBusConnection>
20−#include <QDBusInterface>
21−#include <QDebug>
22−
23−DBusDashCommunicatorService::DBusDashCommunicatorService(QObject *parent):
24− UnityDBusObject(QStringLiteral("/com/canonical/UnityDash"), QStringLiteral("com.canonical.UnityDash"), false, parent)
25−{
26−}
27−
28−DBusDashCommunicatorService::~DBusDashCommunicatorService()
29−{
30−}
31−
32−void DBusDashCommunicatorService::SetCurrentScope(int index, bool animate, bool isSwipe)
33−{
34− Q_EMIT setCurrentScopeRequested(index, animate, isSwipe);
35−}
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.hdeleted−39
@@ -1,39 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it under
5− * the terms of the GNU Lesser General Public License version 3, as published by
6− * the Free Software Foundation.
7− *
8− * This program is distributed in the hope that it will be useful, but WITHOUT
9− * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10− * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11− * Lesser General Public License for more details.
12− *
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/>.
15− */
16−
17−#ifndef DBUSDASHCOMMUNICATORSERVICE_H
18−#define DBUSDASHCOMMUNICATORSERVICE_H
19−
20−#include "unitydbusobject.h"
21−
22−class DBusDashCommunicatorService: public UnityDBusObject
23−{
24− Q_OBJECT
25− Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
26−
27−public:
28− DBusDashCommunicatorService(QObject *parent = 0);
29− ~DBusDashCommunicatorService();
30−
31−Q_SIGNALS:
32− void setCurrentScopeRequested(int index, bool animate, bool isSwipe);
33−
34−public Q_SLOTS:
35− Q_SCRIPTABLE void SetCurrentScope(int index, bool animate, bool isSwipe);
36−
37−};
38−
39−#endif // DBUSDASHCOMMUNICATORSERVICE_H
plugins/Unity/DashCommunicator/plugin.cppdeleted−32
@@ -1,32 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− *
16− * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17− */
18−
19−#include "plugin.h"
20−#include "dashcommunicator.h"
21−#include "dashcommunicatorservice.h"
22−
23−#include <QDBusConnection>
24−#include <QtQml/qqml.h>
25−
26−void DashCommunicatorPlugin::registerTypes(const char *uri)
27−{
28− // @uri Unity.DashCommunicator
29− Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
30− qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
31− qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
32−}
plugins/Unity/DashCommunicator/qmldirdeleted−3
@@ -1,3 +0,0 @@
1−module Unity.DashCommunicator
2−plugin DashCommunicator-qml
3−typeinfo DashCommunicator.qmltypes
plugins/Unity/Indicators/CMakeLists.txt+4−2
@@ -11,6 +11,9 @@ include_directories(
11include_directories(
11include_directories(
12 SYSTEM
12 SYSTEM
13 ${GLIB_INCLUDE_DIRS}
13 ${GLIB_INCLUDE_DIRS}
14+)
15+include_directories(
16+ SYSTEM
14 ${GIO_INCLUDE_DIRS}
17 ${GIO_INCLUDE_DIRS}
15 ${QMENUMODEL_INCLUDE_DIRS}
18 ${QMENUMODEL_INCLUDE_DIRS}
16)
19)
@@ -41,8 +44,7 @@ target_link_libraries(IndicatorsQml
41 ${GLIB_LIBRARIES}
44 ${GLIB_LIBRARIES}
42 ${GIO_LIBRARIES}
45 ${GIO_LIBRARIES}
43 ${QMENUMODEL_LDFLAGS}
46 ${QMENUMODEL_LDFLAGS}
47+ Qt5::Core Qt5::Qml Qt5::Quick Qt5::DBus
44)
48)
45
49
46−qt5_use_modules(IndicatorsQml Core Qml Quick DBus)
47−
48add_unity8_plugin(Unity.Indicators 0.1 Unity/Indicators TARGETS IndicatorsQml)
50add_unity8_plugin(Unity.Indicators 0.1 Unity/Indicators TARGETS IndicatorsQml)
plugins/Unity/Indicators/indicators.h+5−5
@@ -27,13 +27,13 @@ class UNITYINDICATORS_EXPORT ActionState : public QObject
27{
27{
28 Q_OBJECT
28 Q_OBJECT
29public:
29public:
30− Q_ENUMS(ActionStates)
31 enum ActionStates {
30 enum ActionStates {
32 Label = 0x00,
31 Label = 0x00,
33 IconSource = 0x01,
32 IconSource = 0x01,
34 AccessableName = 0x02,
33 AccessableName = 0x02,
35 Visible = 0x03,
34 Visible = 0x03,
36 };
35 };
36+ Q_ENUM(ActionStates)
37
37
38 ActionState(QObject*parent=0):QObject(parent) {}
38 ActionState(QObject*parent=0):QObject(parent) {}
39};
39};
@@ -42,11 +42,11 @@ class UNITYINDICATORS_EXPORT NetworkActionState : public QObject
42{
42{
43 Q_OBJECT
43 Q_OBJECT
44public:
44public:
45− Q_ENUMS(NetworkActionStates)
46 enum NetworkActionStates {
45 enum NetworkActionStates {
47 Connection = 0x01,
46 Connection = 0x01,
48 SignalStrength = 0x02,
47 SignalStrength = 0x02,
49 };
48 };
49+ Q_ENUM(NetworkActionStates)
50
50
51 NetworkActionState(QObject*parent=0):QObject(parent) {}
51 NetworkActionState(QObject*parent=0):QObject(parent) {}
52};
52};
@@ -55,13 +55,13 @@ class UNITYINDICATORS_EXPORT NetworkConnection : public QObject
55{
55{
56 Q_OBJECT
56 Q_OBJECT
57public:
57public:
58− Q_ENUMS(NetworkConnectionStates)
59 enum NetworkConnectionStates {
58 enum NetworkConnectionStates {
60 Initial = 0x00,
59 Initial = 0x00,
61 Activating = 0x01,
60 Activating = 0x01,
62 Activated = 0x02,
61 Activated = 0x02,
63 Deactivating = 0x03,
62 Deactivating = 0x03,
64 };
63 };
64+ Q_ENUM(NetworkConnectionStates)
65
65
66 NetworkConnection(QObject*parent=0):QObject(parent) {}
66 NetworkConnection(QObject*parent=0):QObject(parent) {}
67};
67};
@@ -70,12 +70,12 @@ class UNITYINDICATORS_EXPORT IndicatorsModelRole : public QObject
70{
70{
71 Q_OBJECT
71 Q_OBJECT
72public:
72public:
73− Q_ENUMS(Roles)
74 enum Roles {
73 enum Roles {
75 Identifier = 0,
74 Identifier = 0,
76 Position,
75 Position,
77 IndicatorProperties
76 IndicatorProperties
78 };
77 };
78+ Q_ENUM(Roles)
79
79
80 IndicatorsModelRole(QObject*parent=0):QObject(parent) {}
80 IndicatorsModelRole(QObject*parent=0):QObject(parent) {}
81};
81};
@@ -84,7 +84,6 @@ class UNITYINDICATORS_EXPORT FlatMenuProxyModelRole : public QObject
84{
84{
85 Q_OBJECT
85 Q_OBJECT
86public:
86public:
87− Q_ENUMS(Roles)
88 enum Roles {
87 enum Roles {
89 Action = Qt::DisplayRole + 1,
88 Action = Qt::DisplayRole + 1,
90 Label,
89 Label,
@@ -93,6 +92,7 @@ public:
93 hasSection,
92 hasSection,
94 hasSubMenu
93 hasSubMenu
95 };
94 };
95+ Q_ENUM(Roles)
96
96
97 FlatMenuProxyModelRole(QObject*parent=0):QObject(parent) {}
97 FlatMenuProxyModelRole(QObject*parent=0):QObject(parent) {}
98};
98};
plugins/Unity/Indicators/indicatorsmanager.cpp+1−4
@@ -282,13 +282,10 @@ Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
282 // convergence:
282 // convergence:
283 // 1) enable session indicator
283 // 1) enable session indicator
284 // 2) enable keyboard indicator
284 // 2) enable keyboard indicator
285− // 3) on a PC, switch the battery/power indicator to desktop mode,
286− // can't control brightness for now and phone-on-desktop broken (FIXME)
287 //
285 //
288 // The rest of the indicators respect their default profile (which is "phone", even on desktop PCs)
286 // The rest of the indicators respect their default profile (which is "phone", even on desktop PCs)
289 if ((new_indicator->identifier() == QStringLiteral("indicator-session"))
287 if ((new_indicator->identifier() == QStringLiteral("indicator-session"))
290− || new_indicator->identifier() == QStringLiteral("indicator-keyboard")
288+ || new_indicator->identifier() == QStringLiteral("indicator-keyboard")) {
291− || (new_indicator->identifier() == QStringLiteral("indicator-power") && m_platform.isPC())) {
292 new_indicator->setProfile(QString(m_profile).replace(QStringLiteral("phone"), QStringLiteral("desktop")));
289 new_indicator->setProfile(QString(m_profile).replace(QStringLiteral("phone"), QStringLiteral("desktop")));
293 } else {
290 } else {
294 new_indicator->setProfile(m_profile);
291 new_indicator->setProfile(m_profile);
plugins/Unity/Indicators/indicatorsmanager.h−2
@@ -76,8 +76,6 @@ private:
76 QSharedPointer<QFileSystemWatcher> m_fsWatcher;
76 QSharedPointer<QFileSystemWatcher> m_fsWatcher;
77 bool m_loaded;
77 bool m_loaded;
78 QString m_profile;
78 QString m_profile;
79−
80− Platform m_platform;
81};
79};
82
80
83#endif // INDICATORS_MANAGER_H
81#endif // INDICATORS_MANAGER_H
plugins/Unity/Indicators/indicatorsmodel.cpp+13
@@ -103,6 +103,19 @@ void IndicatorsModel::setProfile(const QString &profile)
103 m_manager->setProfile(profile);
103 m_manager->setProfile(profile);
104}
104}
105
105
106+bool IndicatorsModel::light() const
107+{
108+ return m_light;
109+}
110+
111+void IndicatorsModel::setLight(const bool &light)
112+{
113+ if (m_light != light) {
114+ m_light = light;
115+ Q_EMIT lightChanged();
116+ }
117+}
118+
106/*!
119/*!
107 \qmlmethod IndicatorsModel::unload()
120 \qmlmethod IndicatorsModel::unload()
108
121
plugins/Unity/Indicators/indicatorsmodel.h+9−1
@@ -32,10 +32,12 @@ class IndicatorsManager;
32class UNITYINDICATORS_EXPORT IndicatorsModel : public QAbstractListModel
32class UNITYINDICATORS_EXPORT IndicatorsModel : public QAbstractListModel
33{
33{
34 Q_OBJECT
34 Q_OBJECT
35− Q_ENUMS(Roles)
36 Q_PROPERTY(int count READ count NOTIFY countChanged)
35 Q_PROPERTY(int count READ count NOTIFY countChanged)
37 Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
36 Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
38
37
38+ // Used for tests
39+ Q_PROPERTY(bool light READ light WRITE setLight NOTIFY lightChanged)
40+
39public:
41public:
40
42
41 IndicatorsModel(QObject *parent=nullptr);
43 IndicatorsModel(QObject *parent=nullptr);
@@ -49,6 +51,9 @@ public:
49 QString profile() const;
51 QString profile() const;
50 void setProfile(const QString& profile);
52 void setProfile(const QString& profile);
51
53
54+ bool light() const;
55+ void setLight(const bool &light);
56+
52 /* QAbstractItemModel */
57 /* QAbstractItemModel */
53 QHash<int, QByteArray> roleNames() const override;
58 QHash<int, QByteArray> roleNames() const override;
54 int columnCount(const QModelIndex &parent = QModelIndex()) const override;
59 int columnCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -61,6 +66,7 @@ Q_SIGNALS:
61 void countChanged();
66 void countChanged();
62 void profileChanged();
67 void profileChanged();
63 void indicatorDataChanged(const QVariant& data);
68 void indicatorDataChanged(const QVariant& data);
69+ void lightChanged();
64
70
65private Q_SLOTS:
71private Q_SLOTS:
66 void onIdentifierChanged();
72 void onIdentifierChanged();
@@ -75,6 +81,8 @@ private:
75
81
76 void notifyDataChanged(QObject *sender, int role);
82 void notifyDataChanged(QObject *sender, int role);
77 int count() const;
83 int count() const;
84+
85+ bool m_light;
78};
86};
79
87
80#endif // INDICATORSMODEL_H
88#endif // INDICATORSMODEL_H
plugins/Unity/Indicators/sharedunitymenumodel.cpp+4
@@ -81,12 +81,16 @@ void SharedUnityMenuModel::initialize()
81 }
81 }
82 } else {
82 } else {
83 QSharedPointer<UnityMenuModel> model = UnityMenuModelCache::singleton()->model(m_menuObjectPath);
83 QSharedPointer<UnityMenuModel> model = UnityMenuModelCache::singleton()->model(m_menuObjectPath);
84+
84 if (model != m_model) {
85 if (model != m_model) {
85 if (model->busName() != m_busName) model->setBusName(m_busName);
86 if (model->busName() != m_busName) model->setBusName(m_busName);
86 if (model->actions() != m_actions) model->setActions(m_actions);
87 if (model->actions() != m_actions) model->setActions(m_actions);
87
88
88 m_model = model;
89 m_model = model;
89 Q_EMIT modelChanged();
90 Q_EMIT modelChanged();
91+ } else if (m_model) {
92+ if (m_model->busName() != m_busName) m_model->setBusName(m_busName);
93+ if (m_model->actions() != m_actions) m_model->setActions(m_actions);
90 }
94 }
91 }
95 }
92}
96}
plugins/Unity/InputInfo/CMakeLists.txt+1−2
@@ -32,8 +32,7 @@ add_library(InputInfo SHARED
32target_link_libraries(InputInfo
32target_link_libraries(InputInfo
33 ${LIBUDEV_LDFLAGS}
33 ${LIBUDEV_LDFLAGS}
34 ${LIBEVDEV_LDFLAGS}
34 ${LIBEVDEV_LDFLAGS}
35+ Qt5::Core Qt5::Qml Qt5::Quick
35)
36)
36
37
37−qt5_use_modules(InputInfo Core Qml Quick)
38−
39add_unity8_plugin(Unity.InputInfo 0.1 Unity/InputInfo TARGETS InputInfo)
38add_unity8_plugin(Unity.InputInfo 0.1 Unity/InputInfo TARGETS InputInfo)
plugins/Unity/InputInfo/qinputinfo.h+2−3
@@ -57,8 +57,6 @@ class QInputDeviceManager;
57class QInputDevice : public QObject
57class QInputDevice : public QObject
58{
58{
59 Q_OBJECT
59 Q_OBJECT
60− Q_ENUMS(InputType)
61− Q_FLAGS(InputType InputTypeFlags)
62 friend class QInputDeviceManagerPrivate;
60 friend class QInputDeviceManagerPrivate;
63
61
64public:
62public:
@@ -72,8 +70,9 @@ public:
72 Keyboard = 16,
70 Keyboard = 16,
73 Switch = 32
71 Switch = 32
74 };
72 };
75− Q_ENUMS(InputType)
73+ Q_FLAG(InputType)
76 Q_DECLARE_FLAGS(InputTypeFlags, InputType)
74 Q_DECLARE_FLAGS(InputTypeFlags, InputType)
75+ Q_FLAG(InputTypeFlags)
77
76
78 explicit QInputDevice(QObject *parent = 0);
77 explicit QInputDevice(QObject *parent = 0);
79 QString name() const;
78 QString name() const;
plugins/Unity/Launcher/CMakeLists.txt+7−3
@@ -1,4 +1,3 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=9)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3
2
4add_definitions(-DSM_BUSNAME=systemBus)
3add_definitions(-DSM_BUSNAME=systemBus)
@@ -25,13 +24,19 @@ set(QMLLAUNCHERPLUGIN_SRC
25 dbusinterface.cpp
24 dbusinterface.cpp
26 gsettings.cpp
25 gsettings.cpp
27 asadapter.cpp
26 asadapter.cpp
27+ appdrawermodel.cpp
28+ ualwrapper.cpp
29+ xdgwatcher.cpp
28 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
30 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
29 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
31 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
30 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
32 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
31 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
33 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
34+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
35+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
32 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
36 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
33 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
37 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
34 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
38 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
39+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
35 )
40 )
36
41
37add_library(UnityLauncher-qml MODULE
42add_library(UnityLauncher-qml MODULE
@@ -43,8 +48,7 @@ target_link_libraries(UnityLauncher-qml
43 ${GSETTINGS_QT_LDFLAGS}
48 ${GSETTINGS_QT_LDFLAGS}
44 ${GLIB_LIBRARIES}
49 ${GLIB_LIBRARIES}
45 ${UAL_LIBRARIES}
50 ${UAL_LIBRARIES}
51+ Qt5::DBus Qt5::Qml Qt5::Gui Qt5::Concurrent
46 )
52 )
47
53
48−qt5_use_modules(UnityLauncher-qml DBus Qml Gui)
49−
50add_unity8_plugin(Unity.Launcher 0.1 Unity/Launcher TARGETS UnityLauncher-qml)
54add_unity8_plugin(Unity.Launcher 0.1 Unity/Launcher TARGETS UnityLauncher-qml)
plugins/Unity/Launcher/appdrawermodel.cppnew file+192
@@ -0,0 +1,192 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#include "appdrawermodel.h"
19+#include "ualwrapper.h"
20+#include "xdgwatcher.h"
21+
22+#include <QDebug>
23+#include <QDateTime>
24+#include <QtConcurrentRun>
25+
26+static std::shared_ptr<LauncherItem> makeSharedLauncherItem(
27+ const QString &appId, const QString &name, const QString &icon, QObject * parent)
28+{
29+ return std::shared_ptr<LauncherItem>(
30+ new LauncherItem(appId, name, icon, parent),
31+ [] (LauncherItem *item) { item->deleteLater(); });
32+}
33+
34+AppDrawerModel::AppDrawerModel(QObject *parent):
35+ AppDrawerModelInterface(parent),
36+ m_ual(new UalWrapper(this)),
37+ m_xdgWatcher(new XdgWatcher(this)),
38+ m_refreshing(false)
39+{
40+ connect(&m_refreshFutureWatcher, &QFutureWatcher<ItemList>::finished,
41+ this, &AppDrawerModel::onRefreshFinished);
42+
43+ // keep this a queued connection as it's coming from another thread.
44+ connect(m_xdgWatcher, &XdgWatcher::appAdded, this, &AppDrawerModel::appAdded, Qt::QueuedConnection);
45+ connect(m_xdgWatcher, &XdgWatcher::appRemoved, this, &AppDrawerModel::appRemoved, Qt::QueuedConnection);
46+ connect(m_xdgWatcher, &XdgWatcher::appInfoChanged, this, &AppDrawerModel::appInfoChanged, Qt::QueuedConnection);
47+
48+ refresh();
49+}
50+
51+int AppDrawerModel::rowCount(const QModelIndex &parent) const
52+{
53+ Q_UNUSED(parent)
54+ return m_list.count();
55+}
56+
57+QVariant AppDrawerModel::data(const QModelIndex &index, int role) const
58+{
59+ switch (role) {
60+ case RoleAppId:
61+ return m_list.at(index.row())->appId();
62+ case RoleName:
63+ return m_list.at(index.row())->name();
64+ case RoleIcon:
65+ return m_list.at(index.row())->icon();
66+ case RoleKeywords:
67+ return m_list.at(index.row())->keywords();
68+ case RoleUsage:
69+ return m_list.at(index.row())->popularity();
70+ }
71+
72+ return QVariant();
73+}
74+
75+void AppDrawerModel::appAdded(const QString &appId)
76+{
77+ if (m_refreshing)
78+ // Will be replaced by the refresh result anyway.
79+ return;
80+
81+ UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
82+ if (!info.valid) {
83+ qWarning() << "App added signal received but failed to get app info for app" << appId;
84+ return;
85+ }
86+
87+ beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
88+ auto item = makeSharedLauncherItem(appId, info.name, info.icon, /* parent */ nullptr);
89+ item->setKeywords(info.keywords);
90+ item->setPopularity(info.popularity);
91+ m_list.append(std::move(item));
92+ endInsertRows();
93+}
94+
95+void AppDrawerModel::appRemoved(const QString &appId)
96+{
97+ if (m_refreshing)
98+ // Will be replaced by the refresh result anyway.
99+ return;
100+
101+ int idx = -1;
102+ for (int i = 0; i < m_list.count(); i++) {
103+ if (m_list.at(i)->appId() == appId) {
104+ idx = i;
105+ break;
106+ }
107+ }
108+ if (idx < 0) {
109+ qWarning() << "App removed signal received but app doesn't seem to be in the drawer model";
110+ return;
111+ }
112+ beginRemoveRows(QModelIndex(), idx, idx);
113+ m_list.removeAt(idx);
114+ endRemoveRows();
115+}
116+
117+void AppDrawerModel::appInfoChanged(const QString &appId)
118+{
119+ if (m_refreshing)
120+ // Will be replaced by the refresh result anyway.
121+ return;
122+
123+ std::shared_ptr<LauncherItem> item;
124+ int idx = -1;
125+
126+ for(int i = 0; i < m_list.count(); i++) {
127+ if (m_list.at(i)->appId() == appId) {
128+ item = m_list.at(i);
129+ idx = i;
130+ break;
131+ }
132+ }
133+
134+ if (!item) {
135+ return;
136+ }
137+
138+ UalWrapper::AppInfo info = m_ual->getApplicationInfo(appId);
139+ item->setPopularity(info.popularity);
140+ Q_EMIT dataChanged(index(idx), index(idx), {AppDrawerModelInterface::RoleUsage});
141+}
142+
143+bool AppDrawerModel::refreshing() {
144+ return m_refreshing;
145+}
146+
147+void AppDrawerModel::refresh() {
148+ if (m_refreshing)
149+ return;
150+
151+ m_refreshFutureWatcher.setFuture(QtConcurrent::run([](QThread *thread) {
152+ ItemList list;
153+
154+ Q_FOREACH (const QString &appId, UalWrapper::installedApps()) {
155+ UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId);
156+ if (!info.valid) {
157+ qWarning() << "Failed to get app info for app" << appId;
158+ continue;
159+ }
160+ // We don't pass parent in because this may run after the model is destroyed.
161+ // (And, in fact, we can't, because the model is in a diferent thread.)
162+ auto item = makeSharedLauncherItem(appId, info.name, info.icon, /* parent */ nullptr);
163+ item->setKeywords(info.keywords);
164+ item->setPopularity(info.popularity);
165+ item->moveToThread(thread);
166+ list.append(std::move(item));
167+ }
168+
169+ return list;
170+ }, this->thread()));
171+
172+ m_refreshing = true;
173+ Q_EMIT refreshingChanged();
174+}
175+
176+void AppDrawerModel::onRefreshFinished() {
177+ if (m_refreshFutureWatcher.isCanceled())
178+ // This is the result of setting canceled future below.
179+ return;
180+
181+ beginResetModel();
182+
183+ m_list = m_refreshFutureWatcher.result();
184+ // Remove the future & its result, so that future modifications won't
185+ // create a copy.
186+ m_refreshFutureWatcher.setFuture(QFuture<ItemList>());
187+
188+ endResetModel();
189+
190+ m_refreshing = false;
191+ Q_EMIT refreshingChanged();
192+}
plugins/Unity/Launcher/appdrawermodel.hnew file+64
@@ -0,0 +1,64 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ * Copyright (C) 2020 UBports Foundation.
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#include <memory>
19+#include <QFutureWatcher>
20+#include <unity/shell/launcher/AppDrawerModelInterface.h>
21+
22+#include "launcheritem.h"
23+
24+class UalWrapper;
25+class XdgWatcher;
26+
27+class AppDrawerModel: public AppDrawerModelInterface
28+{
29+ Q_OBJECT
30+ // TODO: Add this to AppDrawerModelInterface in unity-api.
31+ // Or, better yet, remove AppDrawerModelInterface from unity-api.
32+ Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged)
33+public:
34+ AppDrawerModel(QObject* parent = nullptr);
35+
36+ int rowCount(const QModelIndex &parent) const override;
37+ QVariant data(const QModelIndex &index, int role) const override;
38+
39+ // TODO: add these to AppDrawerModelInterface too.
40+ bool refreshing();
41+ Q_INVOKABLE void refresh();
42+
43+Q_SIGNALS:
44+ void refreshingChanged();
45+
46+private Q_SLOTS:
47+ void appAdded(const QString &appId);
48+ void appRemoved(const QString &appId);
49+ void appInfoChanged(const QString &appId);
50+
51+ void onRefreshFinished();
52+
53+private:
54+ // Using shared_ptr is unavoidable in order to share the refresh result
55+ // from the worker thread safely without a memory leak, in case the model
56+ // is destructed before the worker finishes.
57+ typedef QList<std::shared_ptr<LauncherItem>> ItemList;
58+
59+ ItemList m_list;
60+ UalWrapper *m_ual;
61+ XdgWatcher *m_xdgWatcher;
62+ QFutureWatcher<ItemList> m_refreshFutureWatcher;
63+ bool m_refreshing;
64+};
plugins/Unity/Launcher/launcheritem.cpp+57−7
@@ -35,23 +35,24 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
35 m_countVisible(false),
35 m_countVisible(false),
36 m_focused(false),
36 m_focused(false),
37 m_alerting(false),
37 m_alerting(false),
38− m_surfaceCount(0),
39 m_quickList(new QuickListModel(this))
38 m_quickList(new QuickListModel(this))
40{
39{
41− Q_ASSERT(parent != nullptr);
42 QuickListEntry nameAction;
40 QuickListEntry nameAction;
43 nameAction.setActionId(QStringLiteral("launch_item"));
41 nameAction.setActionId(QStringLiteral("launch_item"));
44 nameAction.setText(m_name);
42 nameAction.setText(m_name);
43+ nameAction.setHasSeparator(true);
45 m_quickList->appendAction(nameAction);
44 m_quickList->appendAction(nameAction);
46
45
47 QuickListEntry pinningAction;
46 QuickListEntry pinningAction;
48 pinningAction.setActionId(QStringLiteral("pin_item"));
47 pinningAction.setActionId(QStringLiteral("pin_item"));
49 pinningAction.setText(gettext("Pin shortcut"));
48 pinningAction.setText(gettext("Pin shortcut"));
49+ pinningAction.setIsPrivate(true);
50 m_quickList->appendAction(pinningAction);
50 m_quickList->appendAction(pinningAction);
51
51
52 m_quitAction.setActionId(QStringLiteral("stop_item"));
52 m_quitAction.setActionId(QStringLiteral("stop_item"));
53 m_quitAction.setIcon(QStringLiteral("application-exit"));
53 m_quitAction.setIcon(QStringLiteral("application-exit"));
54 m_quitAction.setText(gettext("Quit"));
54 m_quitAction.setText(gettext("Quit"));
55+ m_quitAction.setIsPrivate(true);
55}
56}
56
57
57QString LauncherItem::appId() const
58QString LauncherItem::appId() const
@@ -89,6 +90,19 @@ void LauncherItem::setIcon(const QString &icon)
89 }
90 }
90}
91}
91
92
93+QStringList LauncherItem::keywords() const
94+{
95+ return m_keywords;
96+}
97+
98+void LauncherItem::setKeywords(const QStringList &keywords)
99+{
100+ if (m_keywords != keywords) {
101+ m_keywords = keywords;
102+ Q_EMIT keywordsChanged(keywords);
103+ }
104+}
105+
92bool LauncherItem::pinned() const
106bool LauncherItem::pinned() const
93{
107{
94 return m_pinned;
108 return m_pinned;
@@ -106,6 +120,7 @@ void LauncherItem::setPinned(bool pinned)
106 QuickListEntry entry;
120 QuickListEntry entry;
107 entry.setActionId(QStringLiteral("pin_item"));
121 entry.setActionId(QStringLiteral("pin_item"));
108 entry.setText(pinned ? gettext("Unpin shortcut") : gettext("Pin shortcut"));
122 entry.setText(pinned ? gettext("Unpin shortcut") : gettext("Pin shortcut"));
123+ entry.setIsPrivate(true);
109 m_quickList->updateAction(entry);
124 m_quickList->updateAction(entry);
110}
125}
111
126
@@ -207,14 +222,49 @@ void LauncherItem::setAlerting(bool alerting)
207
222
208int LauncherItem::surfaceCount() const
223int LauncherItem::surfaceCount() const
209{
224{
210− return m_surfaceCount;
225+ return m_surfaces.count();
226+}
227+
228+void LauncherItem::setSurfaces(const QList<QPair<QString, QString> > &surfaces)
229+{
230+ if (m_surfaces != surfaces) {
231+ m_surfaces = surfaces;
232+
233+ QList<QuickListEntry> removedEntries;
234+ for (int i = 0; i < m_quickList->rowCount(); ++i) {
235+ QuickListEntry entry = m_quickList->get(i);
236+ if (entry.actionId().startsWith(QStringLiteral("surface_"))) {
237+ removedEntries.append(entry);
238+ }
239+ }
240+ Q_FOREACH (const QuickListEntry &entry, removedEntries) {
241+ m_quickList->removeAction(entry);
242+ }
243+ for (int i = 0; i < surfaces.count(); ++i) {
244+ QuickListEntry entry;
245+ entry.setActionId(QStringLiteral("surface_") + surfaces.at(i).first);
246+ entry.setText(surfaces.at(i).second);
247+ entry.setIsPrivate(true);
248+ if (i == surfaces.count() - 1) {
249+ entry.setHasSeparator(true);
250+ }
251+ m_quickList->insertAction(entry, i + 1);
252+ }
253+
254+ Q_EMIT surfaceCountChanged(m_surfaces.count());
255+ }
256+}
257+
258+uint LauncherItem::popularity() const
259+{
260+ return m_popularity;
211}
261}
212
262
213−void LauncherItem::setSurfaceCount(int surfaceCount)
263+void LauncherItem::setPopularity(uint popularity)
214{
264{
215− if (m_surfaceCount != surfaceCount) {
265+ if (m_popularity != popularity) {
216− m_surfaceCount = surfaceCount;
266+ m_popularity = popularity;
217− Q_EMIT surfaceCountChanged(surfaceCount);
267+ Q_EMIT popularityChanged(popularity);
218 }
268 }
219}
269}
220
270
plugins/Unity/Launcher/launcheritem.h+10−2
@@ -23,6 +23,7 @@
23#include "quicklistmodel.h"
23#include "quicklistmodel.h"
24
24
25#include <unity/shell/launcher/LauncherItemInterface.h>
25#include <unity/shell/launcher/LauncherItemInterface.h>
26+#include <unity/shell/application/MirSurfaceInterface.h>
26
27
27class QuickListModel;
28class QuickListModel;
28
29
@@ -37,6 +38,8 @@ public:
37 QString appId() const override;
38 QString appId() const override;
38 QString name() const override;
39 QString name() const override;
39 QString icon() const override;
40 QString icon() const override;
41+ QStringList keywords() const override;
42+ uint popularity() const override;
40 bool pinned() const override;
43 bool pinned() const override;
41 bool running() const override;
44 bool running() const override;
42 bool recent() const override;
45 bool recent() const override;
@@ -52,6 +55,8 @@ public:
52private:
55private:
53 void setName(const QString &name);
56 void setName(const QString &name);
54 void setIcon(const QString &icon);
57 void setIcon(const QString &icon);
58+ void setKeywords(const QStringList &keywords);
59+ void setPopularity(uint popularity);
55 void setPinned(bool pinned);
60 void setPinned(bool pinned);
56 void setRunning(bool running);
61 void setRunning(bool running);
57 void setRecent(bool recent);
62 void setRecent(bool recent);
@@ -60,12 +65,14 @@ private:
60 void setCountVisible(bool countVisible);
65 void setCountVisible(bool countVisible);
61 void setFocused(bool focused);
66 void setFocused(bool focused);
62 void setAlerting(bool alerting);
67 void setAlerting(bool alerting);
63− void setSurfaceCount(int surfaceCount);
68+ void setSurfaces(const QList<QPair<QString, QString >> &surfaces);
64
69
65private:
70private:
66 QString m_appId;
71 QString m_appId;
67 QString m_name;
72 QString m_name;
68 QString m_icon;
73 QString m_icon;
74+ QStringList m_keywords;
75+ uint m_popularity;
69 bool m_pinned;
76 bool m_pinned;
70 bool m_running;
77 bool m_running;
71 bool m_recent;
78 bool m_recent;
@@ -74,11 +81,12 @@ private:
74 bool m_countVisible;
81 bool m_countVisible;
75 bool m_focused;
82 bool m_focused;
76 bool m_alerting;
83 bool m_alerting;
77− int m_surfaceCount;
84+ QList<QPair<QString, QString> > m_surfaces;
78 QuickListModel *m_quickList;
85 QuickListModel *m_quickList;
79 QuickListEntry m_quitAction;
86 QuickListEntry m_quitAction;
80
87
81 friend class LauncherModel;
88 friend class LauncherModel;
89+ friend class AppDrawerModel;
82};
90};
83
91
84#endif // LAUNCHERITEM_H
92#endif // LAUNCHERITEM_H
plugins/Unity/Launcher/launchermodel.cpp+57−57
@@ -19,18 +19,15 @@
19#include "gsettings.h"
19#include "gsettings.h"
20#include "dbusinterface.h"
20#include "dbusinterface.h"
21#include "asadapter.h"
21#include "asadapter.h"
22+#include "ualwrapper.h"
22
23
23−#include <ubuntu-app-launch/appid.h>
24−#include <ubuntu-app-launch/application.h>
25−#include <ubuntu-app-launch/registry.h>
26#include <unity/shell/application/ApplicationInfoInterface.h>
24#include <unity/shell/application/ApplicationInfoInterface.h>
27#include <unity/shell/application/MirSurfaceListInterface.h>
25#include <unity/shell/application/MirSurfaceListInterface.h>
26+#include <unity/shell/application/MirSurfaceInterface.h>
28
27
29#include <QDesktopServices>
28#include <QDesktopServices>
30#include <QDebug>
29#include <QDebug>
31
30
32−namespace ual = ubuntu::app_launch;
33−
34using namespace unity::shell::application;
31using namespace unity::shell::application;
35
32
36LauncherModel::LauncherModel(QObject *parent):
33LauncherModel::LauncherModel(QObject *parent):
@@ -38,8 +35,7 @@ LauncherModel::LauncherModel(QObject *parent):
38 m_settings(new GSettings(this)),
35 m_settings(new GSettings(this)),
39 m_dbusIface(new DBusInterface(this)),
36 m_dbusIface(new DBusInterface(this)),
40 m_asAdapter(new ASAdapter()),
37 m_asAdapter(new ASAdapter()),
41− m_appManager(nullptr),
38+ m_appManager(nullptr)
42− m_ualRegistry(std::make_shared<ual::Registry>())
43{
39{
44 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
40 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
45 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
41 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
@@ -159,7 +155,7 @@ void LauncherModel::pin(const QString &appId, int index)
159 index = m_list.count();
155 index = m_list.count();
160 }
156 }
161
157
162− auto appInfo = getApplicationInfo(appId);
158+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId);
163 if (!appInfo.valid) {
159 if (!appInfo.valid) {
164 qWarning() << "Can't pin application, appId not found:" << appId;
160 qWarning() << "Can't pin application, appId not found:" << appId;
165 return;
161 return;
@@ -171,6 +167,7 @@ void LauncherModel::pin(const QString &appId, int index)
171 appInfo.icon,
167 appInfo.icon,
172 this);
168 this);
173 item->setPinned(true);
169 item->setPinned(true);
170+ item->setPopularity(appInfo.popularity);
174 m_list.insert(index, item);
171 m_list.insert(index, item);
175 endInsertRows();
172 endInsertRows();
176 }
173 }
@@ -203,12 +200,26 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
203 } else {
200 } else {
204 pin(appId);
201 pin(appId);
205 }
202 }
206− } else if (actionId == QLatin1String("launch_item")) {
203+ } else if (actionId == QStringLiteral("launch_item")) {
207 QDesktopServices::openUrl(getUrlForAppId(appId));
204 QDesktopServices::openUrl(getUrlForAppId(appId));
208− } else if (actionId == QLatin1String("stop_item")) { // Quit
205+ } else if (actionId == QStringLiteral("stop_item")) { // Quit
209 if (m_appManager) {
206 if (m_appManager) {
210 m_appManager->stopApplication(appId);
207 m_appManager->stopApplication(appId);
211 }
208 }
209+ } else if (actionId.startsWith(QStringLiteral("surface_"))){
210+ ApplicationInfoInterface *appInfo = m_appManager->findApplication(appId);
211+ if (appInfo) {
212+ for (int i = 0; i < appInfo->surfaceList()->count(); ++i) {
213+ MirSurfaceInterface *iface = appInfo->surfaceList()->get(i);
214+ QString id = actionId;
215+ id.remove(QRegExp("^surface_"));
216+ if (id == iface->persistentId()) {
217+ iface->activate();
218+ }
219+ }
220+ } else {
221+ qWarning() << "App for" << appId << "not found in launcher. Cannot invoke quicklist action";
222+ }
212 // Nope, we don't know this action, let the backend forward it to the application
223 // Nope, we don't know this action, let the backend forward it to the application
213 } else {
224 } else {
214 // TODO: forward quicklist action to app, possibly via m_dbusIface
225 // TODO: forward quicklist action to app, possibly via m_dbusIface
@@ -219,7 +230,6 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
219void LauncherModel::setUser(const QString &username)
230void LauncherModel::setUser(const QString &username)
220{
231{
221 Q_UNUSED(username)
232 Q_UNUSED(username)
222− qWarning() << "This backend doesn't support multiple users";
223}
233}
224
234
225QString LauncherModel::getUrlForAppId(const QString &appId) const
235QString LauncherModel::getUrlForAppId(const QString &appId) const
@@ -335,32 +345,6 @@ int LauncherModel::findApplication(const QString &appId)
335 return -1;
345 return -1;
336}
346}
337
347
338−LauncherModel::AppInfo LauncherModel::getApplicationInfo(const QString &appId)
339−{
340− AppInfo info;
341−
342− ual::AppID ualAppId = ual::AppID::find(m_ualRegistry, appId.toStdString());
343− if (ualAppId.empty()) {
344− return info;
345− }
346−
347− std::shared_ptr<ual::Application> ualApp;
348− try
349− {
350− ualApp = ual::Application::create(ualAppId, m_ualRegistry);
351− }
352− catch (std::runtime_error &e)
353− {
354− qWarning() << "Couldn't find application info for" << appId << "-" << e.what();
355− return info;
356− }
357−
358− info.valid = true;
359− info.name = QString::fromStdString(ualApp->info()->name());
360− info.icon = QString::fromStdString(ualApp->info()->iconPath());
361− return info;
362−}
363−
364void LauncherModel::progressChanged(const QString &appId, int progress)
348void LauncherModel::progressChanged(const QString &appId, int progress)
365{
349{
366 const int idx = findApplication(appId);
350 const int idx = findApplication(appId);
@@ -408,7 +392,7 @@ void LauncherModel::countVisibleChanged(const QString &appId, bool countVisible)
408 }
392 }
409 } else {
393 } else {
410 // Need to create a new LauncherItem and show the highlight
394 // Need to create a new LauncherItem and show the highlight
411− auto appInfo = getApplicationInfo(appId);
395+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId);
412 if (countVisible && appInfo.valid) {
396 if (countVisible && appInfo.valid) {
413 LauncherItem *item = new LauncherItem(appId,
397 LauncherItem *item = new LauncherItem(appId,
414 appInfo.name,
398 appInfo.name,
@@ -428,7 +412,7 @@ void LauncherModel::refresh()
428 // First walk through all the existing items and see if we need to remove something
412 // First walk through all the existing items and see if we need to remove something
429 QList<LauncherItem*> toBeRemoved;
413 QList<LauncherItem*> toBeRemoved;
430 Q_FOREACH (LauncherItem* item, m_list) {
414 Q_FOREACH (LauncherItem* item, m_list) {
431− auto appInfo = getApplicationInfo(item->appId());
415+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(item->appId());
432 if (!appInfo.valid) {
416 if (!appInfo.valid) {
433 // Application no longer available => drop it!
417 // Application no longer available => drop it!
434 toBeRemoved << item;
418 toBeRemoved << item;
@@ -480,7 +464,7 @@ void LauncherModel::refresh()
480 if (itemIndex == -1) {
464 if (itemIndex == -1) {
481 // Need to add it. Just add it into the addedIndex to keep same ordering as the list
465 // Need to add it. Just add it into the addedIndex to keep same ordering as the list
482 // in the settings.
466 // in the settings.
483− auto appInfo = getApplicationInfo(entry);
467+ UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(entry);
484 if (!appInfo.valid) {
468 if (!appInfo.valid) {
485 continue;
469 continue;
486 }
470 }
@@ -537,11 +521,6 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
537 return;
521 return;
538 }
522 }
539
523
540− if (app->appId() == QLatin1String("unity8-dash")) {
541− // Not adding the dash app
542− return;
543− }
544−
545 const int itemIndex = findApplication(app->appId());
524 const int itemIndex = findApplication(app->appId());
546 if (itemIndex != -1) {
525 if (itemIndex != -1) {
547 LauncherItem *item = m_list.at(itemIndex);
526 LauncherItem *item = m_list.at(itemIndex);
@@ -549,40 +528,61 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
549 item->setRecent(true);
528 item->setRecent(true);
550 Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRecent});
529 Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRecent});
551 }
530 }
552− if (item->surfaceCount() != app->surfaceCount()) {
553− item->setSurfaceCount(app->surfaceCount());
554− Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleSurfaceCount});
555− }
556−
557 item->setRunning(true);
531 item->setRunning(true);
558 } else {
532 } else {
559 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
533 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
560 item->setRecent(true);
534 item->setRecent(true);
561 item->setRunning(true);
535 item->setRunning(true);
562 item->setFocused(app->focused());
536 item->setFocused(app->focused());
563− item->setSurfaceCount(app->surfaceCount());
564 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
537 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
565 m_list.append(item);
538 m_list.append(item);
566 endInsertRows();
539 endInsertRows();
567 }
540 }
568− connect(app, &ApplicationInfoInterface::surfaceCountChanged, this, &LauncherModel::applicationSurfaceCountChanged);
541+ connect(app, &ApplicationInfoInterface::surfaceCountChanged, this, &LauncherModel::updateSurfaceList);
569 m_asAdapter->syncItems(m_list);
542 m_asAdapter->syncItems(m_list);
570 Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRunning});
543 Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRunning});
571}
544}
572
545
573−void LauncherModel::applicationSurfaceCountChanged(int count)
546+void LauncherModel::updateSurfaceList()
574{
547{
575 ApplicationInfoInterface *app = static_cast<ApplicationInfoInterface*>(sender());
548 ApplicationInfoInterface *app = static_cast<ApplicationInfoInterface*>(sender());
549+ updateSurfaceListForApp(app);
550+}
551+
552+void LauncherModel::updateSurfaceListForSurface()
553+{
554+ MirSurfaceInterface *iface = static_cast<MirSurfaceInterface*>(sender());
555+ ApplicationInfoInterface* app = m_appManager->findApplication(iface->appId());
556+ if (!app) {
557+ return;
558+ }
559+ updateSurfaceListForApp(app);
560+}
561+
562+void LauncherModel::updateSurfaceListForApp(ApplicationInfoInterface* app)
563+{
576 int idx = findApplication(app->appId());
564 int idx = findApplication(app->appId());
577 if (idx < 0) {
565 if (idx < 0) {
578 qWarning() << "Received a surface count changed event from an app that's not in the Launcher model";
566 qWarning() << "Received a surface count changed event from an app that's not in the Launcher model";
579 return;
567 return;
580 }
568 }
581 LauncherItem *item = m_list.at(idx);
569 LauncherItem *item = m_list.at(idx);
582− if (item->surfaceCount() != count) {
570+ QList<QPair<QString, QString> > surfaces;
583− item->setSurfaceCount(count);
571+ for (int i = 0; i < app->surfaceList()->count(); ++i) {
584− Q_EMIT dataChanged(index(idx), index(idx), {RoleSurfaceCount});
572+ MirSurfaceInterface* iface = app->surfaceList()->get(i);
573+ if (iface->type() == Mir::NormalType || iface->type() == Mir::DialogType) {
574+ // Avoid duplicate connections, so let's just disconnect first to be sure
575+ disconnect(iface, &MirSurfaceInterface::nameChanged, this, &LauncherModel::updateSurfaceListForSurface);
576+ connect(iface, &MirSurfaceInterface::nameChanged, this, &LauncherModel::updateSurfaceListForSurface);
577+ QString name = iface->name();
578+ if (name.isEmpty()) {
579+ name = app->name();
580+ }
581+ surfaces.append({iface->persistentId(), name});
582+ }
585 }
583 }
584+ item->setSurfaces(surfaces);
585+ Q_EMIT dataChanged(index(idx), index(idx), {RoleSurfaceCount});
586}
586}
587
587
588void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
588void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
@@ -603,7 +603,7 @@ void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
603 return;
603 return;
604 }
604 }
605
605
606− disconnect(app, &ApplicationInfoInterface::surfaceCountChanged, this, &LauncherModel::applicationSurfaceCountChanged);
606+ disconnect(app, &ApplicationInfoInterface::surfaceCountChanged, this, &LauncherModel::updateSurfaceList);
607
607
608 LauncherItem * item = m_list.at(appIndex);
608 LauncherItem * item = m_list.at(appIndex);
609
609
plugins/Unity/Launcher/launchermodel.h+3−15
@@ -27,12 +27,6 @@ class GSettings;
27class DBusInterface;
27class DBusInterface;
28class ASAdapter;
28class ASAdapter;
29
29
30−namespace ubuntu {
31− namespace app_launch {
32− class Registry;
33− }
34−}
35−
36using namespace unity::shell::launcher;
30using namespace unity::shell::launcher;
37using namespace unity::shell::application;
31using namespace unity::shell::application;
38
32
@@ -73,13 +67,6 @@ private:
73
67
74 void unpin(const QString &appId);
68 void unpin(const QString &appId);
75
69
76− struct AppInfo {
77− bool valid = false;
78− QString name;
79− QString icon;
80− };
81− AppInfo getApplicationInfo(const QString &appId);
82−
83private Q_SLOTS:
70private Q_SLOTS:
84 void countChanged(const QString &appId, int count);
71 void countChanged(const QString &appId, int count);
85 void countVisibleChanged(const QString &appId, bool count);
72 void countVisibleChanged(const QString &appId, bool count);
@@ -88,7 +75,9 @@ private Q_SLOTS:
88 void applicationAdded(const QModelIndex &parent, int row);
75 void applicationAdded(const QModelIndex &parent, int row);
89 void applicationRemoved(const QModelIndex &parent, int row);
76 void applicationRemoved(const QModelIndex &parent, int row);
90 void focusedAppIdChanged();
77 void focusedAppIdChanged();
91− void applicationSurfaceCountChanged(int);
78+ void updateSurfaceList();
79+ void updateSurfaceListForApp(ApplicationInfoInterface *app);
80+ void updateSurfaceListForSurface();
92
81
93private:
82private:
94 QList<LauncherItem*> m_list;
83 QList<LauncherItem*> m_list;
@@ -98,7 +87,6 @@ private:
98 ASAdapter *m_asAdapter;
87 ASAdapter *m_asAdapter;
99
88
100 ApplicationManagerInterface *m_appManager;
89 ApplicationManagerInterface *m_appManager;
101− std::shared_ptr<ubuntu::app_launch::Registry> m_ualRegistry;
102
90
103 friend class LauncherModelTest;
91 friend class LauncherModelTest;
104};
92};
plugins/Unity/Launcher/plugin.cpp+2−1
@@ -26,7 +26,7 @@
26// local
26// local
27#include "launchermodel.h"
27#include "launchermodel.h"
28#include "launcheritem.h"
28#include "launcheritem.h"
29−
29+#include "appdrawermodel.h"
30
30
31using namespace unity::shell::launcher;
31using namespace unity::shell::launcher;
32
32
@@ -46,4 +46,5 @@ void UnityLauncherPlugin::registerTypes(const char *uri)
46 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
47 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", QStringLiteral("Can't create new Launcher Items in QML. Get them from the LauncherModel."));
47 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", QStringLiteral("Can't create new Launcher Items in QML. Get them from the LauncherModel."));
48 qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", QStringLiteral("Can't create a QuickListModel in QML. Get them from the LauncherItems."));
48 qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", QStringLiteral("Can't create a QuickListModel in QML. Get them from the LauncherItems."));
49+ qmlRegisterType<AppDrawerModel>(uri, 0, 1, "AppDrawerModel");
49}
50}
plugins/Unity/Launcher/quicklistentry.cpp+22
@@ -19,6 +19,8 @@
19#include "quicklistentry.h"
19#include "quicklistentry.h"
20
20
21QuickListEntry::QuickListEntry()
21QuickListEntry::QuickListEntry()
22+ : m_hasSeparator(false)
23+ , m_isPrivate(false)
22{
24{
23}
25}
24
26
@@ -57,7 +59,27 @@ bool QuickListEntry::clickable() const
57 return !m_actionId.isEmpty();
59 return !m_actionId.isEmpty();
58}
60}
59
61
62+void QuickListEntry::setHasSeparator(bool hasSeparator)
63+{
64+ m_hasSeparator = hasSeparator;
65+}
66+
67+bool QuickListEntry::hasSeparator() const
68+{
69+ return m_hasSeparator;
70+}
71+
60bool QuickListEntry::operator==(const QuickListEntry &other)
72bool QuickListEntry::operator==(const QuickListEntry &other)
61{
73{
62 return !other.actionId().isEmpty() && other.actionId() == m_actionId;
74 return !other.actionId().isEmpty() && other.actionId() == m_actionId;
63}
75}
76+
77+bool QuickListEntry::isPrivate() const
78+{
79+ return m_isPrivate;
80+}
81+
82+void QuickListEntry::setIsPrivate(bool isPrivate)
83+{
84+ m_isPrivate = isPrivate;
85+}
plugins/Unity/Launcher/quicklistentry.h+8
@@ -37,12 +37,20 @@ public:
37
37
38 bool clickable() const;
38 bool clickable() const;
39
39
40+ void setHasSeparator(bool hasSeparator);
41+ bool hasSeparator() const;
42+
40 bool operator==(const QuickListEntry & other);
43 bool operator==(const QuickListEntry & other);
41
44
45+ bool isPrivate() const;
46+ void setIsPrivate(bool isPrivate);
47+
42private:
48private:
43 QString m_actionId;
49 QString m_actionId;
44 QString m_text;
50 QString m_text;
45 QString m_icon;
51 QString m_icon;
52+ bool m_hasSeparator;
53+ bool m_isPrivate;
46};
54};
47
55
48#endif // QUICKLISTENTRY
56#endif // QUICKLISTENTRY
plugins/Unity/Launcher/quicklistmodel.cpp+11
@@ -37,6 +37,13 @@ void QuickListModel::appendAction(const QuickListEntry &entry)
37 endInsertRows();
37 endInsertRows();
38}
38}
39
39
40+void QuickListModel::insertAction(const QuickListEntry &entry, int index)
41+{
42+ beginInsertRows(QModelIndex(), index, index);
43+ m_list.insert(index, entry);
44+ endInsertRows();
45+}
46+
40void QuickListModel::updateAction(const QuickListEntry &entry)
47void QuickListModel::updateAction(const QuickListEntry &entry)
41{
48{
42 for (int i = 0; i < m_list.count(); ++i) {
49 for (int i = 0; i < m_list.count(); ++i) {
@@ -79,6 +86,10 @@ QVariant QuickListModel::data(const QModelIndex &index, int role) const
79 return m_list.at(index.row()).icon();
86 return m_list.at(index.row()).icon();
80 case RoleClickable:
87 case RoleClickable:
81 return m_list.at(index.row()).clickable();
88 return m_list.at(index.row()).clickable();
89+ case RoleHasSeparator:
90+ return m_list.at(index.row()).hasSeparator();
91+ case RoleIsPrivate:
92+ return m_list.at(index.row()).isPrivate();
82 }
93 }
83 return QVariant();
94 return QVariant();
84}
95}
plugins/Unity/Launcher/quicklistmodel.h+1
@@ -36,6 +36,7 @@ public:
36 ~QuickListModel();
36 ~QuickListModel();
37
37
38 void appendAction(const QuickListEntry &entry);
38 void appendAction(const QuickListEntry &entry);
39+ void insertAction(const QuickListEntry &entry, int index);
39
40
40 /**
41 /**
41 * @brief Update an existing action
42 * @brief Update an existing action
plugins/Unity/Launcher/ualwrapper.cppnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include <QDebug>
18+
19+#include "ualwrapper.h"
20+
21+#include <ubuntu-app-launch/registry.h>
22+using namespace ubuntu::app_launch;
23+
24+UalWrapper::UalWrapper(QObject *parent):
25+ QObject(parent)
26+{
27+}
28+
29+QStringList UalWrapper::installedApps()
30+{
31+ QStringList appIds;
32+ try {
33+ for (const std::shared_ptr<Application> &app : Registry::installedApps()) {
34+ if (!app->appId().package.value().empty()) {
35+ appIds << QString::fromStdString(app->appId().package.value() + "_" + app->appId().appname.value());
36+ } else {
37+ appIds << QString::fromStdString(app->appId().appname);
38+ }
39+ }
40+ } catch (const std::runtime_error &e) {
41+ qWarning() << "ubuntu-app-launch threw an exception listing apps:" << e.what();
42+ }
43+
44+ return appIds;
45+}
46+
47+UalWrapper::AppInfo UalWrapper::getApplicationInfo(const QString &appId)
48+{
49+ AppInfo info;
50+
51+ try {
52+ AppID ualAppId = AppID::find(appId.toStdString());
53+ if (ualAppId.empty()) {
54+ qWarning() << "Empty ualAppId result for" << appId;
55+ return info;
56+ }
57+
58+ std::shared_ptr<Application> ualApp;
59+ ualApp = Application::create(ualAppId, Registry::getDefault());
60+
61+ info.appId = appId;
62+ info.name = QString::fromStdString(ualApp->info()->name());
63+ info.icon = QString::fromStdString(ualApp->info()->iconPath());
64+ for (const std::string &keyword : ualApp->info()->keywords().value()) {
65+ info.keywords << QString::fromStdString(keyword);
66+ }
67+ //info.popularity = ualApp->info()->popularity();
68+ info.valid = true;
69+ } catch (const std::runtime_error &e) {
70+ qWarning() << "ubuntu-app-launch threw an exception getting app info for appId:" << appId << ":" << e.what();
71+ }
72+
73+ return info;
74+}
plugins/ImageCache/plugin.h → plugins/Unity/Launcher/ualwrapper.hrenamed+15−10
@@ -14,18 +14,23 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−#pragma once
17+#include <QObject>
18
18
19−#include <QQmlExtensionPlugin>
19+class UalWrapper: public QObject
20−
21−class QQmlEngine;
22−
23−class ImageCachePlugin : public QQmlExtensionPlugin
24{
20{
25 Q_OBJECT
21 Q_OBJECT
26− Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27−
28public:
22public:
29− void registerTypes(const char* uri) override;
23+ struct AppInfo {
30− void initializeEngine(QQmlEngine* engine, const char* uri) override;
24+ QString appId;
25+ bool valid = false;
26+ QString name;
27+ QString icon;
28+ QStringList keywords;
29+ uint popularity = 0;
30+ };
31+
32+ UalWrapper(QObject* parent = nullptr);
33+
34+ static QStringList installedApps();
35+ static AppInfo getApplicationInfo(const QString &appId);
31};
36};
plugins/Unity/Launcher/xdgwatcher.cppnew file+129
@@ -0,0 +1,129 @@
1+/*
2+ * Copyright (C) 2019 UBports Foundation
3+ * Author(s): Marius Gripsgard <marius@ubports.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#include "xdgwatcher.h"
19+
20+#include <QDebug>
21+#include <QDir>
22+#include <QFile>
23+#include <QStandardPaths>
24+#include <QTextStream>
25+
26+XdgWatcher::XdgWatcher(QObject* parent)
27+ : QObject(parent),
28+ m_watcher(new QFileSystemWatcher(this))
29+{
30+ connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &XdgWatcher::onDirectoryChanged);
31+ connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &XdgWatcher::onFileChanged);
32+
33+ const auto paths = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
34+ for (const auto &path: paths) {
35+ const auto qdir = QDir(path);
36+ if (!qdir.exists()) {
37+ continue;
38+ }
39+
40+ // Add the path itself to watch for newly added apps
41+ m_watcher->addPath(path);
42+
43+ // Add watcher for eatch app to watch for changes
44+ const auto files = qdir.entryInfoList(QDir::Files);
45+ for (const auto &file: files) {
46+ if (file.suffix() == "desktop") {
47+ const auto path = file.absoluteFilePath();
48+ m_watcher->addPath(path);
49+ m_registry.insert(path, getAppId(file));
50+ }
51+ }
52+ }
53+}
54+
55+// "Ubuntu style" appID is filename without versionNumber after last "_"
56+const QString XdgWatcher::stripAppIdVersion(const QString rawAppID) const {
57+ auto appIdComponents = rawAppID.split("_");
58+ appIdComponents.removeLast();
59+ return appIdComponents.join("_");
60+}
61+
62+// Standard appID see:
63+// https://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id
64+const QString XdgWatcher::toStandardAppId(const QFileInfo fileInfo) const {
65+ const auto paths = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
66+ for (const auto &path: paths) {
67+ if (fileInfo.absolutePath() == path) {
68+ break;
69+ }
70+ if (fileInfo.absolutePath().contains(path)) {
71+ auto fileStr = fileInfo.absoluteFilePath();
72+ fileStr.replace(path, "");
73+ fileStr.replace("/", "-");
74+ fileStr.replace(".desktop", "");
75+ return fileStr;
76+ }
77+ }
78+ return fileInfo.completeBaseName();
79+}
80+
81+const QString XdgWatcher::getAppId(const QFileInfo fileInfo) const {
82+ // We need to open the file to check if its and Ual application
83+ // as we cant just rely on the app name as "normal" apps can also
84+ // contain 3 _ causing us to belive its an ubuntu app
85+ // Example kde_org_kate would become kde_org
86+ QFile qFile(fileInfo.absoluteFilePath());
87+ qFile.open(QIODevice::ReadOnly);
88+ QTextStream fileStream(&qFile);
89+ QString line;
90+ while (fileStream.readLineInto(&line)) {
91+ if (line.startsWith("X-Ubuntu-Application-ID=")) {
92+ auto rawAppID = line.replace("X-Ubuntu-Application-ID=", "");
93+ qFile.close();
94+ return stripAppIdVersion(rawAppID);
95+ }
96+ }
97+ qFile.close();
98+
99+ // If it's not an "Ubuntu" appID, we follow freedesktop standard
100+ return toStandardAppId(fileInfo);
101+}
102+
103+// Watch for newly added apps
104+void XdgWatcher::onDirectoryChanged(const QString &path) {
105+ const auto files = QDir(path).entryInfoList(QDir::Files);
106+ const auto watchedFiles = m_watcher->files();
107+ for (const auto &file: files) {
108+ const auto appPath = file.absoluteFilePath();
109+ if (file.suffix() == "desktop" && !watchedFiles.contains(appPath)) {
110+ m_watcher->addPath(appPath);
111+
112+ const auto appId = getAppId(file);
113+ m_registry.insert(appPath, appId);
114+ Q_EMIT appAdded(appId);
115+ }
116+ }
117+}
118+
119+void XdgWatcher::onFileChanged(const QString &path) {
120+ QFileInfo file(path);
121+ if (file.exists()) {
122+ // The file exists, this must be an modify event
123+ Q_EMIT appInfoChanged(m_registry.value(path));
124+ } else {
125+ // File does not exist, assume this is an remove event.
126+ // onDirectoryChanged will handle rename event
127+ Q_EMIT appRemoved(m_registry.take(path));
128+ }
129+}
plugins/Unity/Launcher/xdgwatcher.hnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright (C) 2019 UBports Foundation
3+ * Author(s): Marius Gripsgard <marius@ubports.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#include <QFileSystemWatcher>
19+#include <QHash>
20+#include <QFileInfo>
21+
22+class QFileInfo;
23+
24+class XdgWatcher: public QObject
25+{
26+ Q_OBJECT
27+public:
28+ XdgWatcher(QObject* parent = nullptr);
29+
30+Q_SIGNALS:
31+ void appAdded(const QString &appId);
32+ void appRemoved(const QString &appId);
33+ void appInfoChanged(const QString &appId);
34+
35+private Q_SLOTS:
36+ void onDirectoryChanged(const QString &path);
37+ void onFileChanged(const QString &path);
38+
39+private:
40+ const QString toStandardAppId(const QFileInfo fileInfo) const;
41+ const QString getAppId(const QFileInfo file) const;
42+ const QString stripAppIdVersion(const QString rawAppID) const;
43+
44+ QFileSystemWatcher* m_watcher;
45+ QHash<const QString, QString> m_registry;
46+};
plugins/Unity/Platform/CMakeLists.txt+1−1
@@ -6,6 +6,6 @@ set(platformplugin_SRCS
6
6
7add_library(Platform-qml SHARED ${platformplugin_SRCS})
7add_library(Platform-qml SHARED ${platformplugin_SRCS})
8
8
9−qt5_use_modules(Platform-qml DBus Qml)
9+target_link_libraries(Platform-qml Qt5::DBus Qt5::Qml)
10
10
11add_unity8_plugin(Unity.Platform 1.0 Unity/Platform TARGETS Platform-qml)
11add_unity8_plugin(Unity.Platform 1.0 Unity/Platform TARGETS Platform-qml)
plugins/Unity/Platform/platform.cpp+16−2
@@ -17,9 +17,11 @@
17#include "platform.h"
17#include "platform.h"
18
18
19#include <QDBusConnection>
19#include <QDBusConnection>
20+#include <QSet>
21+#include <QString>
20
22
21Platform::Platform(QObject *parent)
23Platform::Platform(QObject *parent)
22− : QObject(parent)
24+ : QObject(parent), m_isPC(true), m_isMultiSession(true)
23{
25{
24 QMetaObject::invokeMethod(this, "init");
26 QMetaObject::invokeMethod(this, "init");
25}
27}
@@ -31,8 +33,20 @@ void Platform::init()
31 QDBusInterface seatIface("org.freedesktop.login1", "/org/freedesktop/login1/seat/self", "org.freedesktop.login1.Seat",
33 QDBusInterface seatIface("org.freedesktop.login1", "/org/freedesktop/login1/seat/self", "org.freedesktop.login1.Seat",
32 QDBusConnection::systemBus(), this);
34 QDBusConnection::systemBus(), this);
33
35
36+ // From the source at https://cgit.freedesktop.org/systemd/systemd/tree/src/hostname/hostnamed.c#n130
37+ // "vm\0"
38+ // "container\0"
39+ // "desktop\0"
40+ // "laptop\0"
41+ // "server\0"
42+ // "tablet\0"
43+ // "handset\0"
44+ // "watch\0"
45+ // "embedded\0",
34 m_chassis = iface.property("Chassis").toString();
46 m_chassis = iface.property("Chassis").toString();
35− m_isPC = (m_chassis == "desktop" || m_chassis == "laptop" || m_chassis == "server");
47+
48+ // A PC is not a handset, tablet or watch.
49+ m_isPC = !QSet<QString>{"handset", "tablet", "watch"}.contains(m_chassis);
36 m_isMultiSession = seatIface.property("CanMultiSession").toBool() && seatIface.property("CanGraphical").toBool();
50 m_isMultiSession = seatIface.property("CanMultiSession").toBool() && seatIface.property("CanGraphical").toBool();
37}
51}
38
52
plugins/Unity/Session/CMakeLists.txt+1−1
@@ -18,8 +18,8 @@ add_library(UnitySession-qml MODULE
18 ${QMLSESSIONPLUGIN_SRC}
18 ${QMLSESSIONPLUGIN_SRC}
19 )
19 )
20
20
21−qt5_use_modules(UnitySession-qml DBus Qml)
22target_link_libraries(UnitySession-qml
21target_link_libraries(UnitySession-qml
22+ Qt5::DBus Qt5::Qml
23 unity8-private
23 unity8-private
24 ${GIO_LDFLAGS}
24 ${GIO_LDFLAGS}
25 ${GLIB_LIBRARIES}
25 ${GLIB_LIBRARIES}
plugins/Unity/Session/dbusunitysessionservice.cpp+75−4
@@ -18,6 +18,7 @@
18#include "dbusunitysessionservice.h"
18#include "dbusunitysessionservice.h"
19
19
20// system
20// system
21+#include <grp.h>
21#include <sys/types.h>
22#include <sys/types.h>
22#include <unistd.h>
23#include <unistd.h>
23#include <pwd.h>
24#include <pwd.h>
@@ -211,6 +212,23 @@ public:
211 QDBusConnection::SM_BUSNAME().asyncCall(msg);
212 QDBusConnection::SM_BUSNAME().asyncCall(msg);
212 }
213 }
213
214
215+ bool isUserInGroup(const QString &user, const QString &groupName) const
216+ {
217+ auto group = getgrnam(groupName.toUtf8().data());
218+
219+ if (group && group->gr_mem)
220+ {
221+ for (int i = 0; group->gr_mem[i]; ++i)
222+ {
223+ if (g_strcmp0(group->gr_mem[i], user.toUtf8().data()) == 0) {
224+ return true;
225+ }
226+ }
227+ }
228+
229+ return false;
230+ }
231+
214private Q_SLOTS:
232private Q_SLOTS:
215 void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
233 void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
216 {
234 {
@@ -297,7 +315,13 @@ bool DBusUnitySessionService::CanShutdown() const
297
315
298bool DBusUnitySessionService::CanLock() const
316bool DBusUnitySessionService::CanLock() const
299{
317{
318+ auto user = UserName();
319+ if (user.startsWith(QStringLiteral("guest-")) ||
320+ d->isUserInGroup(user, QStringLiteral("nopasswdlogin"))) {
321+ return false;
322+ } else {
300− return true; // FIXME
323+ return true;
324+ }
301}
325}
302
326
303QString DBusUnitySessionService::UserName() const
327QString DBusUnitySessionService::UserName() const
@@ -335,8 +359,10 @@ void DBusUnitySessionService::PromptLock()
335 // Prompt as in quick. No locking animation needed. Usually used by
359 // Prompt as in quick. No locking animation needed. Usually used by
336 // indicator-session in combination with a switch to greeter or other
360 // indicator-session in combination with a switch to greeter or other
337 // user session.
361 // user session.
362+ if (CanLock()) {
338− Q_EMIT LockRequested();
363+ Q_EMIT LockRequested();
339− Q_EMIT lockRequested();
364+ Q_EMIT lockRequested();
365+ }
340}
366}
341
367
342void DBusUnitySessionService::Lock()
368void DBusUnitySessionService::Lock()
@@ -468,11 +494,56 @@ void performAsyncUnityCall(const QString &method)
468
494
469
495
470DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
496DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
497+ : UnityDBusObject(QStringLiteral("/org/gnome/SessionManager"), QStringLiteral("org.gnome.SessionManager"))
498+{
499+}
500+
501+void DBusGnomeSessionManagerWrapper::Logout(quint32 mode)
502+{
503+ auto call = QStringLiteral("RequestLogout");
504+
505+ // These modes are documented as bitwise flags, not an enum, even though
506+ // they only ever seem to be used as enums.
507+
508+ if (mode & 1) // without dialog
509+ call = QStringLiteral("Logout");
510+ if (mode & 2) // without dialog, ignoring inhibitors (which we don't have)
511+ call = QStringLiteral("Logout");
512+
513+ performAsyncUnityCall(call);
514+}
515+
516+void DBusGnomeSessionManagerWrapper::Reboot()
517+{
518+ // GNOME's Reboot means with dialog (they use Request differently than us).
519+ performAsyncUnityCall(QStringLiteral("RequestReboot"));
520+}
521+
522+void DBusGnomeSessionManagerWrapper::RequestReboot()
523+{
524+ // GNOME's RequestReboot means no dialog (they use Request differently than us).
525+ performAsyncUnityCall(QStringLiteral("Reboot"));
526+}
527+
528+void DBusGnomeSessionManagerWrapper::RequestShutdown()
529+{
530+ // GNOME's RequestShutdown means no dialog (they use Request differently than us).
531+ performAsyncUnityCall(QStringLiteral("Shutdown"));
532+}
533+
534+void DBusGnomeSessionManagerWrapper::Shutdown()
535+{
536+ // GNOME's Shutdown means with dialog (they use Request differently than us).
537+ performAsyncUnityCall(QStringLiteral("RequestShutdown"));
538+}
539+
540+
541+DBusGnomeSessionManagerDialogWrapper::DBusGnomeSessionManagerDialogWrapper()
471 : UnityDBusObject(QStringLiteral("/org/gnome/SessionManager/EndSessionDialog"), QStringLiteral("com.canonical.Unity"))
542 : UnityDBusObject(QStringLiteral("/org/gnome/SessionManager/EndSessionDialog"), QStringLiteral("com.canonical.Unity"))
472{
543{
473}
544}
474
545
475−void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
546+void DBusGnomeSessionManagerDialogWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
476{
547{
477 Q_UNUSED(arg_1);
548 Q_UNUSED(arg_1);
478 Q_UNUSED(max_wait);
549 Q_UNUSED(max_wait);
plugins/Unity/Session/dbusunitysessionservice.h+18−4
@@ -270,17 +270,31 @@ private:
270class DBusGnomeSessionManagerWrapper : public UnityDBusObject
270class DBusGnomeSessionManagerWrapper : public UnityDBusObject
271{
271{
272 Q_OBJECT
272 Q_OBJECT
273− Q_CLASSINFO("D-Bus Interface", "org.gnome.SessionManager.EndSessionDialog")
273+ Q_CLASSINFO("D-Bus Interface", "org.gnome.SessionManager")
274
274
275public:
275public:
276 DBusGnomeSessionManagerWrapper();
276 DBusGnomeSessionManagerWrapper();
277 ~DBusGnomeSessionManagerWrapper() = default;
277 ~DBusGnomeSessionManagerWrapper() = default;
278
278
279public Q_SLOTS:
279public Q_SLOTS:
280− Q_SCRIPTABLE void Open(const unsigned int type, const unsigned int arg_1, const unsigned int max_wait, const QList<QDBusObjectPath> &inhibitors);
280+ Q_SCRIPTABLE void Logout(quint32 mode);
281+ Q_SCRIPTABLE void Reboot();
282+ Q_SCRIPTABLE void RequestReboot();
283+ Q_SCRIPTABLE void RequestShutdown();
284+ Q_SCRIPTABLE void Shutdown();
285+};
281
286
282−private:
287+class DBusGnomeSessionManagerDialogWrapper : public UnityDBusObject
283− void performAsyncCall(const QString &method);
288+{
289+ Q_OBJECT
290+ Q_CLASSINFO("D-Bus Interface", "org.gnome.SessionManager.EndSessionDialog")
291+
292+public:
293+ DBusGnomeSessionManagerDialogWrapper();
294+ ~DBusGnomeSessionManagerDialogWrapper() = default;
295+
296+public Q_SLOTS:
297+ Q_SCRIPTABLE void Open(const unsigned int type, const unsigned int arg_1, const unsigned int max_wait, const QList<QDBusObjectPath> &inhibitors);
284};
298};
285
299
286class DBusGnomeScreensaverWrapper: public UnityDBusObject
300class DBusGnomeScreensaverWrapper: public UnityDBusObject
plugins/Unity/Session/plugin.cpp+1
@@ -26,6 +26,7 @@
26
26
27static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
27static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
28{
28{
29+ new DBusGnomeSessionManagerDialogWrapper();
29 new DBusGnomeSessionManagerWrapper();
30 new DBusGnomeSessionManagerWrapper();
30 new DBusGnomeScreensaverWrapper();
31 new DBusGnomeScreensaverWrapper();
31 new DBusScreensaverWrapper();
32 new DBusScreensaverWrapper();
plugins/Utils/CMakeLists.txt+10−4
@@ -7,7 +7,6 @@ include_directories(
7 SYSTEM
7 SYSTEM
8 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
8 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
9 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
9 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
10− ${GIO_INCLUDE_DIRS}
11)
10)
12
11
13set(QMLPLUGIN_SRC
12set(QMLPLUGIN_SRC
@@ -15,6 +14,10 @@ set(QMLPLUGIN_SRC
15 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
14 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
16 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
15 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
17 applicationsfiltermodel.cpp
16 applicationsfiltermodel.cpp
17+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h
18+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
19+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
20+ appdrawerproxymodel.cpp
18 constants.cpp
21 constants.cpp
19 WindowInputMonitor.cpp
22 WindowInputMonitor.cpp
20 inputwatcher.cpp
23 inputwatcher.cpp
@@ -30,6 +33,9 @@ set(QMLPLUGIN_SRC
30 deviceconfigparser.cpp
33 deviceconfigparser.cpp
31 globalfunctions.cpp
34 globalfunctions.cpp
32 URLDispatcher.cpp
35 URLDispatcher.cpp
36+ tabfocusfence.cpp
37+ expressionfiltermodel.cpp
38+ quicklistproxymodel.cpp
33 plugin.cpp
39 plugin.cpp
34 )
40 )
35
41
@@ -37,7 +43,9 @@ add_library(Utils-qml SHARED
37 ${QMLPLUGIN_SRC}
43 ${QMLPLUGIN_SRC}
38 )
44 )
39
45
40−target_link_libraries(Utils-qml ${GIO_LDFLAGS})
46+target_link_libraries(Utils-qml
47+ Qt5::Qml Qt5::Quick Qt5::DBus Qt5::Network Qt5::Gui Qt5::Sql Qt5::Concurrent
48+ )
41
49
42# Because this is an internal support library, we want
50# Because this is an internal support library, we want
43# to expose all symbols in it. Consider changing this
51# to expose all symbols in it. Consider changing this
@@ -45,6 +53,4 @@ target_link_libraries(Utils-qml ${GIO_LDFLAGS})
45# files directly in targets.
53# files directly in targets.
46set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
54set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
47
55
48−qt5_use_modules(Utils-qml Qml Quick DBus Network Gui Sql Concurrent)
49−
50add_unity8_plugin(Utils 0.1 Utils TARGETS Utils-qml)
56add_unity8_plugin(Utils 0.1 Utils TARGETS Utils-qml)
plugins/Utils/ElapsedTimer.h+1−1
@@ -41,7 +41,7 @@ class ElapsedTimer : public AbstractElapsedTimer {
41public:
41public:
42 void start() override { m_timer.start(); }
42 void start() override { m_timer.start(); }
43 qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); }
43 qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); }
44− qint64 elapsed() const override { return m_timer.elapsed(); }
44+ qint64 elapsed() const override { return m_timer.isValid() ? m_timer.elapsed() : 0; }
45private:
45private:
46 QElapsedTimer m_timer;
46 QElapsedTimer m_timer;
47};
47};
plugins/Utils/Timer.cpp+5−2
@@ -37,13 +37,16 @@ void Timer::setInterval(int msecs)
37void Timer::start()
37void Timer::start()
38{
38{
39 m_timer.start();
39 m_timer.start();
40− AbstractTimer::start();
41}
40}
42
41
43void Timer::stop()
42void Timer::stop()
44{
43{
45 m_timer.stop();
44 m_timer.stop();
46− AbstractTimer::stop();
45+}
46+
47+bool Timer::isRunning() const
48+{
49+ return m_timer.isActive();
47}
50}
48
51
49bool Timer::isSingleShot() const
52bool Timer::isSingleShot() const
plugins/Utils/Timer.h+5−6
@@ -30,18 +30,16 @@ class AbstractTimer : public QObject
30{
30{
31 Q_OBJECT
31 Q_OBJECT
32public:
32public:
33− AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
33+ AbstractTimer(QObject *parent) : QObject(parent) {}
34 virtual int interval() const = 0;
34 virtual int interval() const = 0;
35 virtual void setInterval(int msecs) = 0;
35 virtual void setInterval(int msecs) = 0;
36− virtual void start() { m_isRunning = true; }
36+ virtual void start() = 0;
37− virtual void stop() { m_isRunning = false; }
37+ virtual void stop() = 0;
38− bool isRunning() const { return m_isRunning; }
38+ virtual bool isRunning() const = 0;
39 virtual bool isSingleShot() const = 0;
39 virtual bool isSingleShot() const = 0;
40 virtual void setSingleShot(bool value) = 0;
40 virtual void setSingleShot(bool value) = 0;
41Q_SIGNALS:
41Q_SIGNALS:
42 void timeout();
42 void timeout();
43−private:
44− bool m_isRunning;
45};
43};
46
44
47/** A QTimer wrapper */
45/** A QTimer wrapper */
@@ -55,6 +53,7 @@ public:
55 void setInterval(int msecs) override;
53 void setInterval(int msecs) override;
56 void start() override;
54 void start() override;
57 void stop() override;
55 void stop() override;
56+ bool isRunning() const override;
58 bool isSingleShot() const override;
57 bool isSingleShot() const override;
59 void setSingleShot(bool value) override;
58 void setSingleShot(bool value) override;
60private:
59private:
plugins/Utils/URLDispatcher.cpp+5−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2016 Canonical, Ltd.
2+ * Copyright (C) 2016-2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
17#include "URLDispatcher.h"
17#include "URLDispatcher.h"
18
18
19#include <QDBusConnection>
19#include <QDBusConnection>
20+#include <QDebug>
20
21
21class URLDispatcherInterface : public QObject
22class URLDispatcherInterface : public QObject
22{
23{
@@ -63,7 +64,9 @@ void URLDispatcher::setActive(bool value)
63 connection.registerObject(QStringLiteral("/com/canonical/URLDispatcher"),
64 connection.registerObject(QStringLiteral("/com/canonical/URLDispatcher"),
64 dispatcher,
65 dispatcher,
65 QDBusConnection::ExportScriptableContents);
66 QDBusConnection::ExportScriptableContents);
66− connection.registerService(QStringLiteral("com.canonical.URLDispatcher"));
67+ if (!connection.registerService(QStringLiteral("com.canonical.URLDispatcher"))) {
68+ qWarning() << "Unable to register DBus service com.canonical.URLDispatcher";
69+ }
67 m_dispatcher = dispatcher;
70 m_dispatcher = dispatcher;
68 } else {
71 } else {
69 connection.unregisterService(QStringLiteral("com.canonical.URLDispatcher"));
72 connection.unregisterService(QStringLiteral("com.canonical.URLDispatcher"));
plugins/Utils/WindowInputMonitor.cpp+3
@@ -73,6 +73,9 @@ void WindowInputMonitor::update(QEvent *event)
73 && m_windowLastTouchedTimer->elapsed() >= msecsWithoutTouches) {
73 && m_windowLastTouchedTimer->elapsed() >= msecsWithoutTouches) {
74 m_pressedHomeKey = keyEvent->key();
74 m_pressedHomeKey = keyEvent->key();
75 m_activationTimer->start();
75 m_activationTimer->start();
76+ } else if (m_pressedHomeKey != 0 && !m_homeKeys.contains(keyEvent->key())) {
77+ // something else came in... cancel activation
78+ m_activationTimer->stop();
76 }
79 }
77
80
78 } else if (event->type() == QEvent::KeyRelease) {
81 } else if (event->type() == QEvent::KeyRelease) {
plugins/Utils/appdrawerproxymodel.cppnew file+189
@@ -0,0 +1,189 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include "appdrawerproxymodel.h"
18+
19+#include <unity/shell/launcher/LauncherItemInterface.h>
20+
21+#include <QDebug>
22+
23+AppDrawerProxyModel::AppDrawerProxyModel(QObject *parent):
24+ QSortFilterProxyModel(parent)
25+{
26+ setSortRole(AppDrawerModelInterface::RoleName);
27+ setSortLocaleAware(true);
28+ sort(0);
29+
30+ connect(this, &QAbstractListModel::rowsInserted, this, &AppDrawerProxyModel::countChanged);
31+ connect(this, &QAbstractListModel::rowsRemoved, this, &AppDrawerProxyModel::countChanged);
32+ connect(this, &QAbstractListModel::layoutChanged, this, &AppDrawerProxyModel::countChanged);
33+}
34+
35+QAbstractItemModel *AppDrawerProxyModel::source() const
36+{
37+ return m_source;
38+}
39+
40+void AppDrawerProxyModel::setSource(QAbstractItemModel *source)
41+{
42+ if (m_source != source) {
43+ m_source = source;
44+ setSourceModel(m_source);
45+ setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage);
46+ connect(m_source, &QAbstractItemModel::rowsRemoved, this, &AppDrawerProxyModel::invalidate);
47+ connect(m_source, &QAbstractItemModel::rowsInserted, this, &AppDrawerProxyModel::invalidate);
48+ Q_EMIT sourceChanged();
49+ }
50+}
51+
52+AppDrawerProxyModel::GroupBy AppDrawerProxyModel::group() const
53+{
54+ return m_group;
55+}
56+
57+void AppDrawerProxyModel::setGroup(AppDrawerProxyModel::GroupBy group)
58+{
59+ if (m_group != group) {
60+ m_group = group;
61+ Q_EMIT groupChanged();
62+ invalidateFilter();
63+ }
64+}
65+
66+QString AppDrawerProxyModel::filterLetter() const
67+{
68+ return m_filterLetter;
69+}
70+
71+void AppDrawerProxyModel::setFilterLetter(const QString &filterLetter)
72+{
73+ if (m_filterLetter != filterLetter) {
74+ m_filterLetter = filterLetter;
75+ Q_EMIT filterLetterChanged();
76+ invalidateFilter();
77+ }
78+}
79+
80+QString AppDrawerProxyModel::filterString() const
81+{
82+ return m_filterString;
83+}
84+
85+void AppDrawerProxyModel::setFilterString(const QString &filterString)
86+{
87+ if (m_filterString != filterString) {
88+ m_filterString = filterString;
89+ Q_EMIT filterStringChanged();
90+ invalidateFilter();
91+ }
92+}
93+
94+AppDrawerProxyModel::SortBy AppDrawerProxyModel::sortBy() const
95+{
96+ return m_sortBy;
97+}
98+
99+void AppDrawerProxyModel::setSortBy(AppDrawerProxyModel::SortBy sortBy)
100+{
101+ if (m_sortBy != sortBy) {
102+ m_sortBy = sortBy;
103+ Q_EMIT sortByChanged();
104+ setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage);
105+ sort(0);
106+ }
107+}
108+
109+int AppDrawerProxyModel::count() const
110+{
111+ return rowCount();
112+}
113+
114+QVariant AppDrawerProxyModel::data(const QModelIndex &index, int role) const
115+{
116+ QModelIndex idx = mapToSource(index);
117+ if (role == Qt::UserRole) {
118+ QString name = m_source->data(idx, AppDrawerModelInterface::RoleName).toString();
119+ return name.length() > 0 ? QString(name.at(0)).toUpper() : QChar();
120+ }
121+ return m_source->data(idx, role);
122+}
123+
124+QHash<int, QByteArray> AppDrawerProxyModel::roleNames() const
125+{
126+ if (m_source) {
127+ QHash<int, QByteArray> roles = m_source->roleNames();
128+ roles.insert(Qt::UserRole, "letter");
129+ return roles;
130+ }
131+ return QHash<int, QByteArray>();
132+}
133+
134+bool AppDrawerProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
135+{
136+ Q_UNUSED(source_parent)
137+
138+ if (m_group == GroupByAToZ && source_row > 0) {
139+ QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString();
140+ QChar currentLetter = currentName.length() > 0 ? currentName.at(0) : QChar();
141+ QString previousName = m_source->data(m_source->index(source_row - 1,0 ), AppDrawerModelInterface::RoleName).toString();
142+ QChar previousLetter = previousName.length() > 0 ? previousName.at(0) : QChar();
143+ if (currentLetter.toLower() == previousLetter.toLower()) {
144+ return false;
145+ }
146+ } else if(m_group == GroupByAll && source_row > 0) {
147+ return false;
148+ }
149+ if (!m_filterLetter.isEmpty()) {
150+ QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString();
151+ QString currentLetter = currentName.length() > 0 ? QString(currentName.at(0)) : QString();
152+ if (currentLetter.toLower() != m_filterLetter.toLower()) {
153+ return false;
154+ }
155+ }
156+ if (!m_filterString.isEmpty()) {
157+ QStringList allWords = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleKeywords).toStringList();
158+ allWords.prepend(m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString());
159+ bool found = false;
160+ Q_FOREACH (const QString &currentWord, allWords) {
161+ if (currentWord.contains(m_filterString, Qt::CaseInsensitive)) {
162+ found = true;
163+ break;
164+ }
165+ }
166+ if (!found) {
167+ return false;
168+ }
169+ }
170+ return true;
171+}
172+
173+QString AppDrawerProxyModel::appId(int index) const
174+{
175+ if (index >= 0 && index < rowCount()) {
176+ QModelIndex sourceIndex = mapToSource(this->index(index, 0));
177+
178+ AppDrawerModelInterface* adm = dynamic_cast<AppDrawerModelInterface*>(m_source);
179+ if (adm) {
180+ return adm->data(sourceIndex, AppDrawerModelInterface::RoleAppId).toString();
181+ }
182+
183+ AppDrawerProxyModel* adpm = qobject_cast<AppDrawerProxyModel*>(m_source);
184+ if (adpm) {
185+ return adpm->appId(sourceIndex.row());
186+ }
187+ }
188+ return QString();
189+}
plugins/Utils/appdrawerproxymodel.hnew file+87
@@ -0,0 +1,87 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include <QSortFilterProxyModel>
18+
19+#include <unity/shell/launcher/AppDrawerModelInterface.h>
20+
21+using namespace unity::shell::launcher;
22+
23+class AppDrawerProxyModel: public QSortFilterProxyModel
24+{
25+ Q_OBJECT
26+ Q_PROPERTY(QAbstractItemModel* source READ source WRITE setSource NOTIFY sourceChanged)
27+ Q_PROPERTY(GroupBy group READ group WRITE setGroup NOTIFY groupChanged)
28+ Q_PROPERTY(QString filterLetter READ filterLetter WRITE setFilterLetter NOTIFY filterLetterChanged)
29+ Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged)
30+ Q_PROPERTY(SortBy sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged)
31+ Q_PROPERTY(int count READ count NOTIFY countChanged)
32+
33+public:
34+ enum GroupBy {
35+ GroupByNone,
36+ GroupByAll,
37+ GroupByAToZ
38+ };
39+ Q_ENUM(GroupBy)
40+ enum SortBy {
41+ SortByAToZ,
42+ SortByUsage
43+ };
44+ Q_ENUM(SortBy)
45+
46+ AppDrawerProxyModel(QObject* parent = nullptr);
47+
48+ QAbstractItemModel* source() const;
49+ void setSource(QAbstractItemModel* source);
50+
51+ GroupBy group() const;
52+ void setGroup(GroupBy group);
53+
54+ QString filterLetter() const;
55+ void setFilterLetter(const QString &filterLetter);
56+
57+ QString filterString() const;
58+ void setFilterString(const QString &filterString);
59+
60+ SortBy sortBy() const;
61+ void setSortBy(SortBy sortBy);
62+
63+ int count() const;
64+
65+ QVariant data(const QModelIndex &index, int role) const override;
66+ QHash<int, QByteArray> roleNames() const override;
67+
68+ Q_INVOKABLE QString appId(int index) const;
69+
70+protected:
71+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
72+
73+Q_SIGNALS:
74+ void sourceChanged();
75+ void groupChanged();
76+ void filterLetterChanged();
77+ void filterStringChanged();
78+ void sortByChanged();
79+ void countChanged();
80+
81+private:
82+ QAbstractItemModel* m_source = nullptr;
83+ GroupBy m_group = GroupByNone;
84+ QString m_filterLetter;
85+ QString m_filterString;
86+ SortBy m_sortBy = SortByAToZ;
87+};
plugins/Utils/deviceconfigparser.cpp+18
@@ -116,6 +116,24 @@ QString DeviceConfigParser::category() const
116 return value;
116 return value;
117}
117}
118
118
119+bool DeviceConfigParser::supportsMultiColorLed() const
120+{
121+ return readBoolFromConfig("SupportsMultiColorLed", true);
122+}
123+
124+bool DeviceConfigParser::readBoolFromConfig(const QString &key, bool defaultValue) const
125+{
126+ m_config->beginGroup(m_name);
127+
128+ bool ret = defaultValue;
129+ if (m_config->contains(key)) {
130+ ret = m_config->value(key).toBool();
131+ }
132+
133+ m_config->endGroup();
134+ return ret;
135+}
136+
119QStringList DeviceConfigParser::readOrientationsFromConfig(const QString &key) const
137QStringList DeviceConfigParser::readOrientationsFromConfig(const QString &key) const
120{
138{
121 m_config->beginGroup(m_name);
139 m_config->beginGroup(m_name);
plugins/Utils/deviceconfigparser.h+3
@@ -33,6 +33,7 @@ class DeviceConfigParser: public QObject
33 Q_PROPERTY(Qt::ScreenOrientation portraitOrientation READ portraitOrientation NOTIFY changed)
33 Q_PROPERTY(Qt::ScreenOrientation portraitOrientation READ portraitOrientation NOTIFY changed)
34 Q_PROPERTY(Qt::ScreenOrientation invertedPortraitOrientation READ invertedPortraitOrientation NOTIFY changed)
34 Q_PROPERTY(Qt::ScreenOrientation invertedPortraitOrientation READ invertedPortraitOrientation NOTIFY changed)
35 Q_PROPERTY(QString category READ category NOTIFY changed)
35 Q_PROPERTY(QString category READ category NOTIFY changed)
36+ Q_PROPERTY(bool supportsMultiColorLed READ supportsMultiColorLed NOTIFY changed)
36
37
37public:
38public:
38 DeviceConfigParser(QObject *parent = nullptr);
39 DeviceConfigParser(QObject *parent = nullptr);
@@ -47,6 +48,7 @@ public:
47 Qt::ScreenOrientation portraitOrientation() const;
48 Qt::ScreenOrientation portraitOrientation() const;
48 Qt::ScreenOrientation invertedPortraitOrientation() const;
49 Qt::ScreenOrientation invertedPortraitOrientation() const;
49 QString category() const;
50 QString category() const;
51+ bool supportsMultiColorLed() const;
50
52
51Q_SIGNALS:
53Q_SIGNALS:
52 void changed();
54 void changed();
@@ -58,6 +60,7 @@ private:
58 QStringList readOrientationsFromConfig(const QString &key) const;
60 QStringList readOrientationsFromConfig(const QString &key) const;
59 QString readOrientationFromConfig(const QString &key) const;
61 QString readOrientationFromConfig(const QString &key) const;
60 Qt::ScreenOrientation stringToOrientation(const QString &orientationString, Qt::ScreenOrientation defaultValue) const;
62 Qt::ScreenOrientation stringToOrientation(const QString &orientationString, Qt::ScreenOrientation defaultValue) const;
63+ bool readBoolFromConfig(const QString &key, bool defaultValue) const;
61};
64};
62
65
63#endif
66#endif
plugins/Utils/easingcurve.h−1
@@ -36,7 +36,6 @@
36class EasingCurve: public QObject
36class EasingCurve: public QObject
37{
37{
38 Q_OBJECT
38 Q_OBJECT
39− Q_ENUMS(QEasingCurve::Type)
40 Q_PROPERTY(QEasingCurve::Type type READ type WRITE setType NOTIFY typeChanged)
39 Q_PROPERTY(QEasingCurve::Type type READ type WRITE setType NOTIFY typeChanged)
41 Q_PROPERTY(qreal period READ period WRITE setPeriod NOTIFY periodChanged)
40 Q_PROPERTY(qreal period READ period WRITE setPeriod NOTIFY periodChanged)
42 Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
41 Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
plugins/Utils/expressionfiltermodel.cppnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include "expressionfiltermodel.h"
18+
19+ExpressionFilterModel::ExpressionFilterModel(QObject *parent)
20+ : UnitySortFilterProxyModelQML(parent)
21+{
22+}
23+
24+QJSValue ExpressionFilterModel::matchExpression() const
25+{
26+ return m_matchExpression;
27+}
28+
29+void ExpressionFilterModel::setMatchExpression(const QJSValue &value)
30+{
31+ m_matchExpression = value;
32+ invalidateFilter();
33+}
34+
35+bool
36+ExpressionFilterModel::filterAcceptsRow(int sourceRow,
37+ const QModelIndex &sourceParent) const
38+{
39+ if (m_matchExpression.isCallable()) {
40+ QJSValueList args;
41+ args << sourceRow;
42+ QJSValue ret = m_matchExpression.call(args);
43+ if (ret.isBool()) {
44+ return ret.toBool();
45+ }
46+ }
47+
48+ return UnitySortFilterProxyModelQML::filterAcceptsRow(sourceRow, sourceParent);
49+}
plugins/Utils/expressionfiltermodel.hnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#ifndef EXPRESSIONFILTERMODEL_H
18+#define EXPRESSIONFILTERMODEL_H
19+
20+#include "unitysortfilterproxymodelqml.h"
21+#include <QJSValue>
22+
23+class ExpressionFilterModel : public UnitySortFilterProxyModelQML
24+{
25+ Q_OBJECT
26+ Q_PROPERTY(QJSValue matchExpression READ matchExpression WRITE setMatchExpression NOTIFY matchExpressionChanged)
27+public:
28+ explicit ExpressionFilterModel(QObject *parent = 0);
29+
30+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
31+
32+ QJSValue matchExpression() const;
33+ void setMatchExpression(const QJSValue& value);
34+
35+Q_SIGNALS:
36+ void matchExpressionChanged();
37+
38+private:
39+ mutable QJSValue m_matchExpression;
40+};
41+
42+#endif // EXPRESSIONFILTERMODEL_H
plugins/Utils/globalfunctions.cpp+5
@@ -51,3 +51,8 @@ QQuickItem *GlobalFunctions::itemAt(QQuickItem* parent, int x, int y, QJSValue m
51 }
51 }
52 return nullptr;
52 return nullptr;
53}
53}
54+
55+bool GlobalFunctions::itemUnderMouse(QQuickItem* item)
56+{
57+ return item && item->isUnderMouse();
58+}
plugins/Utils/globalfunctions.h+2
@@ -37,6 +37,8 @@ public:
37 int x,
37 int x,
38 int y,
38 int y,
39 QJSValue matcher);
39 QJSValue matcher);
40+
41+ static Q_INVOKABLE bool itemUnderMouse(QQuickItem* item);
40};
42};
41
43
42#endif // GLOBALFUNCTIONS_H
44#endif // GLOBALFUNCTIONS_H
plugins/Utils/plugin.cpp+9−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2012-2015 Canonical, Ltd.
2+ * Copyright (C) 2012-2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -39,6 +39,10 @@
39#include "deviceconfigparser.h"
39#include "deviceconfigparser.h"
40#include "globalfunctions.h"
40#include "globalfunctions.h"
41#include "URLDispatcher.h"
41#include "URLDispatcher.h"
42+#include "appdrawerproxymodel.h"
43+#include "tabfocusfence.h"
44+#include "expressionfiltermodel.h"
45+#include "quicklistproxymodel.h"
42
46
43static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
47static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
44{
48{
@@ -82,4 +86,8 @@ void UtilsPlugin::registerTypes(const char *uri)
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
86 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
87 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
88 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
89+ qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");
90+ qmlRegisterType<TabFocusFenceItem>(uri, 0, 1, "TabFocusFence");
91+ qmlRegisterType<ExpressionFilterModel>(uri, 0, 1, "ExpressionFilterModel");
92+ qmlRegisterType<QuickListProxyModel>(uri, 0, 1, "QuickListProxyModel");
85}
93}
plugins/Utils/quicklistproxymodel.cppnew file+72
@@ -0,0 +1,72 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include "quicklistproxymodel.h"
18+
19+#include <QDebug>
20+
21+QuickListProxyModel::QuickListProxyModel(QObject *parent):
22+ QSortFilterProxyModel(parent)
23+{
24+ connect(this, &QAbstractListModel::rowsInserted, this, &QuickListProxyModel::countChanged);
25+ connect(this, &QAbstractListModel::rowsRemoved, this, &QuickListProxyModel::countChanged);
26+ connect(this, &QAbstractListModel::layoutChanged, this, &QuickListProxyModel::countChanged);
27+}
28+
29+QAbstractItemModel *QuickListProxyModel::source() const
30+{
31+ return m_source;
32+}
33+
34+bool QuickListProxyModel::privateMode() const
35+{
36+ return m_privateMode;
37+}
38+
39+void QuickListProxyModel::setPrivateMode(bool privateMode)
40+{
41+ if (m_privateMode != privateMode) {
42+ m_privateMode = privateMode;
43+ invalidateFilter();
44+ Q_EMIT privateModeChanged();
45+ }
46+}
47+
48+void QuickListProxyModel::setSource(QAbstractItemModel *source)
49+{
50+ if (m_source != source) {
51+ m_source = source;
52+ setSourceModel(m_source);
53+ connect(m_source, &QAbstractItemModel::rowsRemoved, this, &QuickListProxyModel::invalidate);
54+ connect(m_source, &QAbstractItemModel::rowsInserted, this, &QuickListProxyModel::invalidate);
55+ Q_EMIT sourceChanged();
56+ }
57+}
58+
59+int QuickListProxyModel::count() const
60+{
61+ return rowCount();
62+}
63+
64+bool QuickListProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
65+{
66+ Q_UNUSED(source_parent)
67+
68+ if (m_privateMode) {
69+ return !m_source->data(m_source->index(source_row, 0), QuickListModelInterface::RoleIsPrivate).toBool();
70+ }
71+ return true;
72+}
plugins/Utils/quicklistproxymodel.hnew file+52
@@ -0,0 +1,52 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include <QSortFilterProxyModel>
18+
19+#include <unity/shell/launcher/QuickListModelInterface.h>
20+
21+using namespace unity::shell::launcher;
22+
23+class QuickListProxyModel: public QSortFilterProxyModel
24+{
25+ Q_OBJECT
26+ Q_PROPERTY(QAbstractItemModel* source READ source WRITE setSource NOTIFY sourceChanged)
27+ Q_PROPERTY(bool privateMode READ privateMode WRITE setPrivateMode NOTIFY privateModeChanged)
28+ Q_PROPERTY(int count READ count NOTIFY countChanged)
29+
30+public:
31+ QuickListProxyModel(QObject* parent = nullptr);
32+
33+ QAbstractItemModel* source() const;
34+ void setSource(QAbstractItemModel* source);
35+
36+ bool privateMode() const;
37+ void setPrivateMode(bool privateMode);
38+
39+ int count() const;
40+
41+protected:
42+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
43+
44+Q_SIGNALS:
45+ void sourceChanged();
46+ void privateModeChanged();
47+ void countChanged();
48+
49+private:
50+ QAbstractItemModel* m_source = nullptr;
51+ bool m_privateMode = false;
52+};
plugins/Utils/tabfocusfence.cppnew file+72
@@ -0,0 +1,72 @@
1+/*
2+ * Copyright 2017 Canonical Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+*/
16+
17+#include "tabfocusfence.h"
18+
19+#include <private/qquickitem_p.h>
20+
21+TabFocusFenceItem::TabFocusFenceItem(QQuickItem *parent) : QQuickItem(parent)
22+{
23+ QQuickItemPrivate *d = QQuickItemPrivate::get(this);
24+ d->isTabFence = true;
25+ setFlag(ItemIsFocusScope);
26+}
27+
28+bool TabFocusFenceItem::focusNext()
29+{
30+ QQuickItem * current = scopedFocusItem();
31+ if (current) {
32+ QQuickItem * next = current->nextItemInFocusChain(true);
33+ if (next) {
34+ next->setFocus(true, Qt::TabFocusReason);
35+ return true;
36+ }
37+ }
38+ return false;
39+}
40+
41+bool TabFocusFenceItem::focusPrev()
42+{
43+ QQuickItem * current = scopedFocusItem();
44+ if (current) {
45+ QQuickItem * prev = current->nextItemInFocusChain(false);
46+ if (prev) {
47+ prev->setFocus(true, Qt::BacktabFocusReason);
48+ return true;
49+ }
50+ }
51+ return false;
52+}
53+
54+void TabFocusFenceItem::keyPressEvent(QKeyEvent *event)
55+{
56+ // Needed so we eat Tab keys when there's only one item inside the fence
57+ if (event->key() == Qt::Key_Tab) {
58+ event->accept();
59+ } else {
60+ QQuickItem::keyPressEvent(event);
61+ }
62+}
63+
64+void TabFocusFenceItem::keyReleaseEvent(QKeyEvent *event)
65+{
66+ // Needed so we eat Tab keys when there's only one item inside the fence
67+ if (event->key() == Qt::Key_Tab) {
68+ event->accept();
69+ } else {
70+ QQuickItem::keyReleaseEvent(event);
71+ }
72+}
plugins/Utils/tabfocusfence.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright 2017 Canonical Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+*/
16+
17+#ifndef TABFOCUSFENCE_H
18+#define TABFOCUSFENCE_H
19+
20+#include <QQuickItem>
21+
22+// An item that restricts focus Tab travelling
23+// to its children
24+class TabFocusFenceItem : public QQuickItem
25+{
26+Q_OBJECT
27+
28+public:
29+ TabFocusFenceItem(QQuickItem *parent = nullptr);
30+
31+ Q_INVOKABLE bool focusNext();
32+ Q_INVOKABLE bool focusPrev();
33+
34+ void keyPressEvent(QKeyEvent *event) override;
35+ void keyReleaseEvent(QKeyEvent *event) override;
36+};
37+
38+#endif
plugins/Utils/unitysortfilterproxymodelqml.h+5
@@ -38,6 +38,11 @@ public:
38 Q_INVOKABLE int mapRowToSource(int row);
38 Q_INVOKABLE int mapRowToSource(int row);
39 bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
39 bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
40
40
41+ // Make the QSortFilterProxyModel variants also accessible
42+ using QSortFilterProxyModel::data;
43+ using QSortFilterProxyModel::mapFromSource;
44+ using QSortFilterProxyModel::mapToSource;
45+
41 /* getters */
46 /* getters */
42 int totalCount() const;
47 int totalCount() const;
43 bool invertMatch() const;
48 bool invertMatch() const;
plugins/Utils/windowstatestorage.cpp+316−124
@@ -1,5 +1,6 @@
1/*
1/*
2− * Copyright 2015 Canonical Ltd.
2+ * Copyright 2015-2016 Canonical Ltd.
3+ * Copyright 2021 UBports Foundation
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * 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 * it under the terms of the GNU Lesser General Public License as published by
@@ -16,176 +17,367 @@
16
17
17#include "windowstatestorage.h"
18#include "windowstatestorage.h"
18
19
19−#include <QtConcurrent>
20#include <QDebug>
20#include <QDebug>
21−#include <QFutureSynchronizer>
21+#include <QDir>
22+#include <QMetaObject>
23+#include <QObject>
22#include <QSqlQuery>
24#include <QSqlQuery>
23#include <QSqlError>
25#include <QSqlError>
24#include <QSqlResult>
26#include <QSqlResult>
27+#include <QStandardPaths>
25#include <QRect>
28#include <QRect>
26#include <unity/shell/application/ApplicationInfoInterface.h>
29#include <unity/shell/application/ApplicationInfoInterface.h>
27
30
28−QMutex WindowStateStorage::s_mutex;
29
31
30−inline QString sanitiseString(QString string) {
32+class AsyncQuery: public QObject
31− return string.remove(QLatin1Char('\"'))
32− .remove(QLatin1Char('\''))
33− .remove(QLatin1Char('\\'));
34−}
35−
36−WindowStateStorage::WindowStateStorage(QObject *parent):
37− QObject(parent)
38{
33{
39− QString dbPath = QDir::homePath() + "/.cache/unity8/";
34+ Q_OBJECT
40− m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
41− QDir dir;
42− dir.mkpath(dbPath);
43− m_db.setDatabaseName(dbPath + "windowstatestorage.sqlite");
44− initdb();
45−}
46
35
47−WindowStateStorage::~WindowStateStorage()
36+public:
48−{
37+ AsyncQuery(const QString &dbName):
49− QFutureSynchronizer<void> futureSync;
38+ m_dbName(dbName)
50− for (int i = 0; i < m_asyncQueries.count(); ++i) {
39+ {
51− futureSync.addFuture(m_asyncQueries[i]);
52 }
40 }
53− futureSync.waitForFinished();
54− m_db.close();
55−}
56
41
57−void WindowStateStorage::saveState(const QString &windowId, WindowStateStorage::WindowState state)
42+ ~AsyncQuery()
58−{
43+ {
59− const QString queryString = QStringLiteral("INSERT OR REPLACE INTO state (windowId, state) values ('%1', '%2');")
44+ QSqlDatabase::removeDatabase(m_connectionName);
60− .arg(sanitiseString(windowId))
45+ }
61− .arg((int)state);
62
46
63− saveValue(queryString);
47+ Q_INVOKABLE const QString getDbName()
48+ {
49+ if (!m_ok) {
50+ return "ERROR";
51+ }
52+ QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
53+ return connection.databaseName();
64−}
54+ }
65
55
66−WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const
56+ Q_INVOKABLE bool initdb()
67−{
57+ {
68− const QString queryString = QStringLiteral("SELECT * FROM state WHERE windowId = '%1';")
58+ if (m_ok) {
69− .arg(sanitiseString(windowId));
59+ return true;
60+ }
61+ QSqlDatabase connection = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), m_connectionName);
62+ connection.setDatabaseName(m_dbName);
63+ connection.setConnectOptions(QStringLiteral("QSQLITE_BUSY_TIMEOUT=1000"));
64+ if (!connection.open()) {
65+ qWarning() << "AsyncQuery::initdb: Error opening state database. Window positions will not be saved or restored." << m_dbName << connection.lastError().driverText() << connection.lastError().databaseText();
66+ return false;
67+ }
68+ QSqlQuery query(connection);
69+
70+ if (!connection.tables().contains(QStringLiteral("geometry"))) {
71+ QString geometryQuery = QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);");
72+ if (!query.exec(geometryQuery)) {
73+ logSqlError(query);
74+ return false;
75+ }
76+ }
77+
78+ if (!connection.tables().contains(QStringLiteral("state"))) {
79+ QString stateQuery = QStringLiteral("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);");
80+ if (!query.exec(stateQuery)) {
81+ logSqlError(query);
82+ return false;
83+ }
84+ }
85+
86+ if (!connection.tables().contains(QStringLiteral("stage"))) {
87+ QString stageQuery = QStringLiteral("CREATE TABLE stage(appId TEXT UNIQUE, stage INTEGER);");
88+ if (!query.exec(stageQuery)) {
89+ logSqlError(query);
90+ return false;
91+ }
92+ }
93+ m_ok = true;
94+ return true;
95+ }
70
96
97+ Q_INVOKABLE int getState(const QString &windowId) const
98+ {
99+ if (!m_ok) {
100+ return -1;
101+ }
102+ QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
71− QSqlQuery query = getValue(queryString);
103+ QSqlQuery query(connection);
104+ query.prepare(m_getStateQuery);
105+ query.bindValue(":windowId", windowId);
106+ query.exec();
107+ if (!query.isActive() || !query.isSelect()) {
108+ logSqlError(query);
109+ return -1;
110+ }
111+ if (!query.first()) {
112+ return -1;
113+ }
114+ bool converted = false;
115+ QVariant resultStr = query.value(0);
116+ int result = resultStr.toInt(&converted);
117+ if (converted) {
118+ return result;
119+ } else {
120+ qWarning() << "getState result expected integer, got " << resultStr;
121+ return -1;
122+ }
123+ }
124+
125+ Q_INVOKABLE QRect getGeometry(const QString &windowId) const
126+ {
127+ if (!m_ok) {
128+ return QRect();
129+ }
130+ QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
131+ QSqlQuery query(connection);
132+ query.prepare(m_getGeometryQuery);
133+ query.bindValue(":windowId", windowId);
134+ query.exec();
135+ if (!query.isActive() || !query.isSelect()) {
136+ logSqlError(query);
137+ return QRect();
138+ }
139+
140+ if (!query.first()) {
141+ return QRect();
142+ }
143+
144+ bool xConverted, yConverted, widthConverted, heightConverted = false;
145+ int x, y, width, height;
146+ QVariant xResultStr = query.value(QStringLiteral("x"));
147+ QVariant yResultStr = query.value(QStringLiteral("y"));
148+ QVariant widthResultStr = query.value(QStringLiteral("width"));
149+ QVariant heightResultStr = query.value(QStringLiteral("height"));
150+ x = xResultStr.toInt(&xConverted);
151+ y = yResultStr.toInt(&yConverted);
152+ width = widthResultStr.toInt(&widthConverted);
153+ height = heightResultStr.toInt(&heightConverted);
154+
155+ if (xConverted && yConverted && widthConverted && heightConverted) {
156+ return QRect(x, y, width, height);
157+ } else {
158+ qWarning() << "getGeometry result expected integers, got x:"
159+ << xResultStr << "y:" << yResultStr << "width" << widthResultStr
160+ << "height:" << heightResultStr;
161+ return QRect();
162+ }
72
163
73− if (!query.first()) {
74− return defaultValue;
75 }
164 }
76− return (WindowState)query.value(QStringLiteral("state")).toInt();
77−}
78
165
79−void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
166+ Q_INVOKABLE int getStage(const QString &appId) const
80−{
167+ {
81− const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
168+ if (!m_ok) {
82− .arg(sanitiseString(windowId))
169+ return -1;
83− .arg(rect.x())
170+ }
84− .arg(rect.y())
171+ QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
85− .arg(rect.width())
172+ QSqlQuery query(connection);
86− .arg(rect.height());
173+ query.prepare(m_getStageQuery);
87−
174+ query.bindValue(":appId", appId);
88− saveValue(queryString);
175+ query.exec();
176+ if (!query.isActive() || !query.isSelect()) {
177+ logSqlError(query);
178+ return -1;
179+ }
180+ if (!query.first()) {
181+ return -1;
182+ }
183+ bool converted = false;
184+ QVariant resultStr = query.value(0);
185+ int result = resultStr.toInt(&converted);
186+ if (converted) {
187+ return result;
188+ } else {
189+ qWarning() << "getStage result expected integer, got " << resultStr;
190+ return -1;
191+ }
89−}
192+ }
90
193
91−void WindowStateStorage::saveStage(const QString &appId, int stage)
194+public Q_SLOTS:
195+
196+ void saveState(const QString &windowId, WindowStateStorage::WindowState state)
92−{
197+ {
93− const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');")
198+ if (!m_ok) {
94− .arg(sanitiseString(appId))
199+ return;
95− .arg(stage);
200+ }
201+ QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
202+ QSqlQuery query(connection);
203+ query.prepare(m_saveStateQuery);
204+ query.bindValue(":windowId", windowId);
205+ query.bindValue(":state", (int)state);
206+ if (!query.exec()) {
207+ logSqlError(query);
208+ }
209+ }
96
210
97− saveValue(queryString);
211+ void saveGeometry(const QString &windowId, const QRect &rect)
212+ {
213+ if (!m_ok) {
214+ return;
215+ }
216+ QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
217+ QSqlQuery query(connection);
218+ query.prepare(m_saveGeometryQuery);
219+ query.bindValue(":windowId", windowId);
220+ query.bindValue(":x", rect.x());
221+ query.bindValue(":y", rect.y());
222+ query.bindValue(":width", rect.width());
223+ query.bindValue(":height", rect.height());
224+ if (!query.exec()) {
225+ logSqlError(query);
226+ }
98−}
227+ }
99
228
100−int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
229+ void saveStage(const QString &appId, int stage)
101−{
230+ {
102− const QString queryString = QStringLiteral("SELECT * FROM stage WHERE appId = '%1';")
231+ if (!m_ok) {
103− .arg(sanitiseString(appId));
232+ return;
233+ }
234+ QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
235+ QSqlQuery query(connection);
236+ query.prepare(m_saveStageQuery);
237+ query.bindValue(":appId", appId);
238+ query.bindValue(":stage", stage);
239+ if (!query.exec()) {
240+ logSqlError(query);
241+ }
242+ }
104
243
105− QSqlQuery query = getValue(queryString);
244+private:
245+ static const QString m_connectionName;
246+ static const QString m_getStateQuery;
247+ static const QString m_saveStateQuery;
248+ static const QString m_getGeometryQuery;
249+ static const QString m_saveGeometryQuery;
250+ static const QString m_getStageQuery;
251+ static const QString m_saveStageQuery;
252+
253+ static void logSqlError(const QSqlQuery query)
254+ {
255+ qWarning() << "Error executing query" << query.lastQuery()
256+ << "Driver error:" << query.lastError().driverText()
257+ << "Database error:" << query.lastError().databaseText();
258+ }
106
259
107− if (!query.first()) {
260+ QString m_dbName;
108− return defaultValue;
261+ bool m_ok = false;
262+};
263+
264+const QString AsyncQuery::m_connectionName = QStringLiteral("WindowStateStorage");
265+const QString AsyncQuery::m_getStateQuery = QStringLiteral("SELECT state FROM state WHERE windowId = :windowId");
266+const QString AsyncQuery::m_saveStateQuery = QStringLiteral("INSERT OR REPLACE INTO state (windowId, state) values (:windowId, :state)");
267+const QString AsyncQuery::m_getGeometryQuery = QStringLiteral("SELECT * FROM geometry WHERE windowId = :windowId");
268+const QString AsyncQuery::m_saveGeometryQuery = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values (:windowId, :x, :y, :width, :height)");
269+const QString AsyncQuery::m_getStageQuery = QStringLiteral("SELECT stage FROM stage WHERE appId = :appId");
270+const QString AsyncQuery::m_saveStageQuery = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values (:appId, :stage)");
271+
272+WindowStateStorage::WindowStateStorage(const QString &dbName, QObject *parent):
273+ QObject(parent),
274+ m_thread()
275+{
276+ qRegisterMetaType<WindowStateStorage::WindowState>("WindowStateStorage::WindowState");
277+ QString dbFile;
278+ if (dbName.isEmpty()) {
279+ const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/unity8/");
280+ QDir dir;
281+ dir.mkpath(dbPath);
282+ dbFile = QString(dbPath) + QStringLiteral("windowstatestorage.sqlite");
283+ } else {
284+ dbFile = dbName;
109 }
285 }
110− return query.value("stage").toInt();
286+ m_asyncQuery = new AsyncQuery(dbFile);
287+ m_asyncQuery->moveToThread(&m_thread);
288+ connect(&m_thread, &QThread::finished, m_asyncQuery, &QObject::deleteLater);
289+ m_thread.start();
290+ // Note that we're relying on initdb being called before any other methods
291+ // on AsyncQuery. Given the current behavior of QueuedConnection (slots
292+ // invoked in the order they are received), this is fine.
293+ QMetaObject::invokeMethod(m_asyncQuery, "initdb",
294+ Qt::QueuedConnection);
295+ connect(this, &WindowStateStorage::saveState, m_asyncQuery, &AsyncQuery::saveState);
296+ connect(this, &WindowStateStorage::saveGeometry, m_asyncQuery, &AsyncQuery::saveGeometry);
297+ connect(this, &WindowStateStorage::saveStage, m_asyncQuery, &AsyncQuery::saveStage);
111}
298}
112
299
113−void WindowStateStorage::executeAsyncQuery(const QString &queryString)
300+WindowStateStorage::~WindowStateStorage()
114{
301{
115− QMutexLocker l(&s_mutex);
302+ m_thread.quit();
116− QSqlQuery query;
303+ m_thread.wait();
117−
118− bool ok = query.exec(queryString);
119− if (!ok) {
120− qWarning() << "Error executing query" << queryString
121− << "Driver error:" << query.lastError().driverText()
122− << "Database error:" << query.lastError().databaseText();
123− }
124}
304}
125
305
126−QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
306+WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const
127{
307{
128− QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
308+ int state;
129− .arg(sanitiseString(windowId));
130
309
131− QSqlQuery query = getValue(queryString);
310+ QMetaObject::invokeMethod(m_asyncQuery, "getState", Qt::BlockingQueuedConnection,
311+ Q_RETURN_ARG(int, state),
312+ Q_ARG(const QString&, windowId)
313+ );
132
314
133− if (!query.first()) {
315+ if (state == -1) {
134 return defaultValue;
316 return defaultValue;
135 }
317 }
136− return QRect(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(),
318+
137− query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt());
319+ return (WindowState)state;
138}
320}
139
321
140−void WindowStateStorage::initdb()
322+int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
141{
323{
142− m_db.open();
324+ int stage;
143− if (!m_db.open()) {
144− qWarning() << "Error opening state database:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
145− return;
146− }
147
325
148− if (!m_db.tables().contains(QStringLiteral("geometry"))) {
326+ QMetaObject::invokeMethod(m_asyncQuery, "getStage", Qt::BlockingQueuedConnection,
149− QSqlQuery query;
327+ Q_RETURN_ARG(int, stage),
150− query.exec(QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);"));
328+ Q_ARG(const QString&, appId)
151− }
329+ );
152
330
153− if (!m_db.tables().contains(QStringLiteral("state"))) {
331+ if (stage == -1) {
154− QSqlQuery query;
332+ return defaultValue;
155− query.exec(QStringLiteral("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);"));
156 }
333 }
157
334
158− if (!m_db.tables().contains(QStringLiteral("stage"))) {
335+ return stage;
159− QSqlQuery query;
160− query.exec(QStringLiteral("CREATE TABLE stage(appId TEXT UNIQUE, stage INTEGER);"));
161− }
162}
336}
163
337
164−void WindowStateStorage::saveValue(const QString &queryString)
338+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
165{
339{
166− QMutexLocker mutexLocker(&s_mutex);
340+ QRect geometry;
167−
341+ QMetaObject::invokeMethod(m_asyncQuery, "getGeometry", Qt::BlockingQueuedConnection,
168− QFuture<void> future = QtConcurrent::run(executeAsyncQuery, queryString);
342+ Q_RETURN_ARG(QRect, geometry),
169− m_asyncQueries.append(future);
343+ Q_ARG(const QString&, windowId)
170−
344+ );
171− QFutureWatcher<void> *futureWatcher = new QFutureWatcher<void>();
345+ if (geometry.isNull() || !geometry.isValid()) {
172− futureWatcher->setFuture(future);
346+ return defaultValue;
173− connect(futureWatcher, &QFutureWatcher<void>::finished,
347+ }
174− this,
348+ return geometry;
175− [=](){ m_asyncQueries.removeAll(futureWatcher->future());
176− futureWatcher->deleteLater(); });
177}
349}
178
350
179−QSqlQuery WindowStateStorage::getValue(const QString &queryString) const
351+const QString WindowStateStorage::getDbName()
180{
352{
181− QMutexLocker l(&s_mutex);
353+ QString dbName;
182− QSqlQuery query;
354+ QMetaObject::invokeMethod(m_asyncQuery, "getDbName", Qt::BlockingQueuedConnection,
355+ Q_RETURN_ARG(QString, dbName)
356+ );
357+ return QString(dbName);
358+}
183
359
184− bool ok = query.exec(queryString);
360+Mir::State WindowStateStorage::toMirState(WindowState state) const
185− if (!ok) {
361+{
186− qWarning() << "Error retrieving database query:" << queryString
362+ // assumes a single state (not an OR of several)
187− << "Driver error:" << query.lastError().driverText()
363+ switch (state) {
188− << "Database error:" << query.lastError().databaseText();
364+ case WindowStateMaximized: return Mir::MaximizedState;
365+ case WindowStateMinimized: return Mir::MinimizedState;
366+ case WindowStateFullscreen: return Mir::FullscreenState;
367+ case WindowStateMaximizedLeft: return Mir::MaximizedLeftState;
368+ case WindowStateMaximizedRight: return Mir::MaximizedRightState;
369+ case WindowStateMaximizedHorizontally: return Mir::HorizMaximizedState;
370+ case WindowStateMaximizedVertically: return Mir::VertMaximizedState;
371+ case WindowStateMaximizedTopLeft: return Mir::MaximizedTopLeftState;
372+ case WindowStateMaximizedTopRight: return Mir::MaximizedTopRightState;
373+ case WindowStateMaximizedBottomLeft: return Mir::MaximizedBottomLeftState;
374+ case WindowStateMaximizedBottomRight: return Mir::MaximizedBottomRightState;
375+
376+ case WindowStateNormal:
377+ case WindowStateRestored:
378+ default:
379+ return Mir::RestoredState;
189 }
380 }
190− return query;
191}
381}
382+
383+#include "windowstatestorage.moc"
plugins/Utils/windowstatestorage.h+19−17
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright 2015-2016 Canonical Ltd.
2 * Copyright 2015-2016 Canonical Ltd.
3+ * Copyright 2021 UBports Foundation
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * 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 * it under the terms of the GNU Lesser General Public License as published by
@@ -16,13 +17,19 @@
16
17
17#include <QObject>
18#include <QObject>
18#include <QSqlDatabase>
19#include <QSqlDatabase>
20+#include <QSqlQuery>
19#include <QMutex>
21#include <QMutex>
20#include <QFuture>
22#include <QFuture>
23+#include <QThread>
24+
25+// unity-api
26+#include <unity/shell/application/Mir.h>
27+
28+class AsyncQuery;
21
29
22class WindowStateStorage: public QObject
30class WindowStateStorage: public QObject
23{
31{
24 Q_OBJECT
32 Q_OBJECT
25− Q_ENUMS(WindowState)
26public:
33public:
27 enum WindowState {
34 enum WindowState {
28 WindowStateNormal = 1 << 0,
35 WindowStateNormal = 1 << 0,
@@ -39,34 +46,29 @@ public:
39 WindowStateMaximizedBottomRight = 1 << 11,
46 WindowStateMaximizedBottomRight = 1 << 11,
40 WindowStateRestored = 1 << 12
47 WindowStateRestored = 1 << 12
41 };
48 };
49+ Q_ENUM(WindowState)
42 Q_DECLARE_FLAGS(WindowStates, WindowState)
50 Q_DECLARE_FLAGS(WindowStates, WindowState)
43−#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
44 Q_FLAG(WindowStates)
51 Q_FLAG(WindowStates)
45−#endif
46
52
47− WindowStateStorage(QObject *parent = 0);
53+ WindowStateStorage(const QString &dbName = QString(), QObject *parent = nullptr);
48 virtual ~WindowStateStorage();
54 virtual ~WindowStateStorage();
49
55
50− Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
51 Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
56 Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
52
57
53− Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
54 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue) const;
58 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue) const;
55
59
56− Q_INVOKABLE void saveStage(const QString &appId, int stage);
57 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
60 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
58
61
59−private:
62+ Q_INVOKABLE Mir::State toMirState(WindowState state) const;
60− void initdb();
61
63
62− void saveValue(const QString &queryString);
64+ const QString getDbName();
63− QSqlQuery getValue(const QString &queryString) const;
64
65
65− static void executeAsyncQuery(const QString &queryString);
66+Q_SIGNALS:
66− static QMutex s_mutex;
67+ void saveStage(const QString &appId, int stage);
68+ void saveGeometry(const QString &windowId, const QRect &rect);
69+ void saveState(const QString &windowId, WindowStateStorage::WindowState state);
67
70
68− // NB: This is accessed from threads. Make sure to mutex it.
71+private:
69− QSqlDatabase m_db;
72+ QThread m_thread;
70−
73+ AsyncQuery *m_asyncQuery;
71− QList< QFuture<void> > m_asyncQueries;
72};
74};
plugins/WindowManager/AvailableDesktopArea.cppnew file+50
@@ -0,0 +1,50 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
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/>.
15+ */
16+
17+#include "AvailableDesktopArea.h"
18+
19+#include <QGuiApplication>
20+#include <QQuickWindow>
21+#include <qpa/qplatformnativeinterface.h>
22+
23+AvailableDesktopArea::AvailableDesktopArea(QQuickItem *parent)
24+ : QQuickItem(parent)
25+{
26+ connect(this, &QQuickItem::xChanged, this, &AvailableDesktopArea::updatePlatformWindowProperty);
27+ connect(this, &QQuickItem::yChanged, this, &AvailableDesktopArea::updatePlatformWindowProperty);
28+ connect(this, &QQuickItem::widthChanged, this, &AvailableDesktopArea::updatePlatformWindowProperty);
29+ connect(this, &QQuickItem::heightChanged, this, &AvailableDesktopArea::updatePlatformWindowProperty);
30+}
31+
32+void AvailableDesktopArea::updatePlatformWindowProperty()
33+{
34+ if (!window()) {
35+ return;
36+ }
37+
38+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
39+
40+ QRect rect(x(), y(), width(), height());
41+
42+ nativeInterface->setWindowProperty(window()->handle(), "availableDesktopArea", QVariant(rect));
43+}
44+
45+void AvailableDesktopArea::itemChange(ItemChange change, const ItemChangeData &)
46+{
47+ if (change == ItemSceneChange) {
48+ updatePlatformWindowProperty();
49+ }
50+}
plugins/Unity/DashCommunicator/dashcommunicator.h → plugins/WindowManager/AvailableDesktopArea.hrenamed+14−21
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it under
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -14,32 +14,25 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−#ifndef DASHCOMMUNICATOR_H
17+#ifndef AVAILABLEDESKTOPAREA_H
18−#define DASHCOMMUNICATOR_H
18+#define AVAILABLEDESKTOPAREA_H
19
19
20−// Qt
21−#include <QThread>
20+#include <QQuickItem>
22−#include <QMutex>
23
21
24−class DashConnection;
22+/**
23+ @brief Used to inform qtmir/miral about the available desktop area of shell
25
24
26−class DashCommunicator: public QThread
25+ So that qtmir/miral can take correct window management decisions.
26+ */
27+class AvailableDesktopArea : public QQuickItem
27{
28{
28 Q_OBJECT
29 Q_OBJECT
29public:
30public:
30− explicit DashCommunicator(QObject *parent = 0);
31+ AvailableDesktopArea(QQuickItem *parent = nullptr);
31− ~DashCommunicator();
32−
33−public Q_SLOTS:
34− void setCurrentScope(int index, bool animate, bool isSwipe);
35−
36protected:
32protected:
37− void run() override;
33+ void itemChange(ItemChange change, const ItemChangeData &value) override;
38−
39−private:
34+private Q_SLOTS:
40− DashConnection *m_dashConnection;
35+ void updatePlatformWindowProperty();
41− bool m_created;
42− QMutex m_mutex;
43};
36};
44
37
45−#endif
38+#endif // AVAILABLEDESKTOPAREA_H
plugins/WindowManager/CMakeLists.txt+14−2
@@ -1,14 +1,26 @@
1set(WINDOWMANAGER_SRC
1set(WINDOWMANAGER_SRC
2− TopLevelSurfaceList.cpp
2+ AvailableDesktopArea.cpp
3+ TopLevelWindowModel.cpp
4+ Window.cpp
3 WindowManagerPlugin.cpp
5 WindowManagerPlugin.cpp
6+ WindowMargins.cpp
7+ InputMethodManager.cpp
4 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
8 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
5 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
9 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
6 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
10 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
7 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
11 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
12+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
8 )
13 )
9
14
15+add_definitions(-DWINDOWMANAGERQML_LIBRARY)
16+
17+include_directories(
18+ SYSTEM
19+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
20+)
21+
10add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
22add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
11
23
12−qt5_use_modules(windowmanager-qml Qml Quick Gui)
24+target_link_libraries(windowmanager-qml Qt5::Qml Qt5::Quick Qt5::Gui)
13
25
14add_unity8_plugin(WindowManager 0.1 WindowManager TARGETS windowmanager-qml)
26add_unity8_plugin(WindowManager 0.1 WindowManager TARGETS windowmanager-qml)
plugins/WindowManager/InputMethodManager.cppnew file+56
@@ -0,0 +1,56 @@
1+/*
2+ * Copyright (C) 2019 UBports Foundation.
3+ * Author(s) Marius Gripsgard <marius@ubports.com>
4+ *
5+ * This program is free software: you can redistribute it and/or modify it under
6+ * the terms of the GNU Lesser General Public License version 3, as published by
7+ * the Free Software Foundation.
8+ *
9+ * This program is distributed in the hope that it will be useful, but WITHOUT
10+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+ * Lesser General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU Lesser General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#include "InputMethodManager.h"
19+#include <QDebug>
20+
21+#include <unity/shell/application/MirSurfaceInterface.h>
22+
23+// local
24+#include "Window.h"
25+
26+Q_LOGGING_CATEGORY(INPUTMETHODMANAGER, "InputMethodManager", QtInfoMsg)
27+#define DEBUG_MSG qCDebug(INPUTMETHODMANAGER).nospace().noquote() << __func__
28+
29+namespace unityapi = unity::shell::application;
30+
31+InputMethodManager *InputMethodManager::instance()
32+{
33+ static InputMethodManager* inputMethod(new InputMethodManager());
34+ return inputMethod;
35+}
36+
37+InputMethodManager::InputMethodManager()
38+{
39+}
40+
41+void InputMethodManager::setWindow(Window* window)
42+{
43+ if (window == m_inputMethodWindow) {
44+ return;
45+ }
46+
47+ DEBUG_MSG << "(" << window << ")";
48+
49+ m_inputMethodWindow = window;
50+ Q_EMIT surfaceChanged(surface());
51+}
52+
53+unityapi::MirSurfaceInterface* InputMethodManager::surface() const
54+{
55+ return m_inputMethodWindow ? m_inputMethodWindow->surface() : nullptr;
56+}
plugins/WindowManager/InputMethodManager.hnew file+55
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright (C) 2019 UBports Foundation.
3+ * Author(s) Marius Gripsgard <marius@ubports.com>
4+ *
5+ * This program is free software: you can redistribute it and/or modify it under
6+ * the terms of the GNU Lesser General Public License version 3, as published by
7+ * the Free Software Foundation.
8+ *
9+ * This program is distributed in the hope that it will be useful, but WITHOUT
10+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+ * Lesser General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU Lesser General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#pragma once
19+
20+#include <QLoggingCategory>
21+#include <QObject>
22+
23+#include "WindowManagerGlobal.h"
24+
25+Q_DECLARE_LOGGING_CATEGORY(INPUTMETHODMANAGER)
26+
27+class Window;
28+
29+namespace unity {
30+ namespace shell {
31+ namespace application {
32+ class MirSurfaceInterface;
33+ }
34+ }
35+}
36+
37+class WINDOWMANAGERQML_EXPORT InputMethodManager : public QObject
38+{
39+ Q_OBJECT
40+ Q_PROPERTY(unity::shell::application::MirSurfaceInterface* surface READ surface NOTIFY surfaceChanged)
41+
42+public:
43+ InputMethodManager();
44+ static InputMethodManager* instance();
45+
46+ void setWindow(Window* window);
47+
48+Q_SIGNALS:
49+ void surfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface);
50+
51+private:
52+ unity::shell::application::MirSurfaceInterface* surface() const;
53+
54+ Window* m_inputMethodWindow{nullptr};
55+};
plugins/WindowManager/TopLevelSurfaceList.cppdeleted−481
@@ -1,481 +0,0 @@
1−/*
2− * Copyright (C) 2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#include "TopLevelSurfaceList.h"
18−
19−// unity-api
20−#include <unity/shell/application/ApplicationInfoInterface.h>
21−#include <unity/shell/application/MirSurfaceInterface.h>
22−#include <unity/shell/application/MirSurfaceListInterface.h>
23−
24−#include <QMetaObject>
25−
26−Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist", QtDebugMsg)
27−
28−#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__
29−
30−using namespace unity::shell::application;
31−
32−TopLevelSurfaceList::TopLevelSurfaceList(QObject *parent) :
33− QAbstractListModel(parent)
34−{
35− DEBUG_MSG << "()";
36−}
37−
38−TopLevelSurfaceList::~TopLevelSurfaceList()
39−{
40− DEBUG_MSG << "()";
41−}
42−
43−int TopLevelSurfaceList::rowCount(const QModelIndex &parent) const
44−{
45− return !parent.isValid() ? m_surfaceList.size() : 0;
46−}
47−
48−QVariant TopLevelSurfaceList::data(const QModelIndex& index, int role) const
49−{
50− if (index.row() < 0 || index.row() >= m_surfaceList.size())
51− return QVariant();
52−
53− if (role == SurfaceRole) {
54− MirSurfaceInterface *surface = m_surfaceList.at(index.row()).surface;
55− return QVariant::fromValue(surface);
56− } else if (role == ApplicationRole) {
57− return QVariant::fromValue(m_surfaceList.at(index.row()).application);
58− } else if (role == IdRole) {
59− return QVariant::fromValue(m_surfaceList.at(index.row()).id);
60− } else {
61− return QVariant();
62− }
63−}
64−
65−void TopLevelSurfaceList::raise(MirSurfaceInterface *surface)
66−{
67− if (!surface)
68− return;
69−
70− DEBUG_MSG << "(MirSurface[" << (void*)surface << "])";
71−
72− int i = indexOf(surface);
73− if (i != -1) {
74− raiseId(m_surfaceList.at(i).id);
75− }
76−}
77−
78−void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application)
79−{
80− DEBUG_MSG << "(" << application->appId() << ")";
81−
82− appendSurfaceHelper(nullptr, application);
83−}
84−
85−void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
86−{
87− Q_ASSERT(surface != nullptr);
88−
89− bool filledPlaceholder = false;
90− for (int i = 0; i < m_surfaceList.count() && !filledPlaceholder; ++i) {
91− ModelEntry &entry = m_surfaceList[i];
92− if (entry.application == application && entry.surface == nullptr) {
93− entry.surface = surface;
94− connectSurface(surface);
95− DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface
96− << ", filling out placeholder. after: " << toString();
97− Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
98− filledPlaceholder = true;
99− }
100− }
101−
102− if (!filledPlaceholder) {
103− DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
104− appendSurfaceHelper(surface, application);
105− }
106−}
107−
108−void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
109−{
110− if (m_modelState == IdleState) {
111− m_modelState = InsertingState;
112− beginInsertRows(QModelIndex(), m_surfaceList.size() /*first*/, m_surfaceList.size() /*last*/);
113− } else {
114− Q_ASSERT(m_modelState == ResettingState);
115− // No point in signaling anything if we're resetting the whole model
116− }
117−
118− int id = generateId();
119− m_surfaceList.append(ModelEntry(surface, application, id));
120− if (surface) {
121− connectSurface(surface);
122− }
123−
124− if (m_modelState == InsertingState) {
125− endInsertRows();
126− Q_EMIT countChanged();
127− Q_EMIT listChanged();
128− m_modelState = IdleState;
129− }
130−
131− DEBUG_MSG << " after " << toString();
132−}
133−
134−void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
135−{
136− connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
137− if (!live) {
138− onSurfaceDied(surface);
139− }
140− });
141− connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); });
142−}
143−
144−void TopLevelSurfaceList::onSurfaceDied(MirSurfaceInterface *surface)
145−{
146− int i = indexOf(surface);
147− if (i == -1) {
148− return;
149− }
150−
151− auto application = m_surfaceList[i].application;
152−
153− // can't be starting if it already has a surface
154− Q_ASSERT(application->state() != ApplicationInfoInterface::Starting);
155−
156− if (application->state() == ApplicationInfoInterface::Running) {
157− m_surfaceList[i].removeOnceSurfaceDestroyed = true;
158− } else {
159− // assume it got killed by the out-of-memory daemon.
160− //
161− // So leave entry in the model and only remove its surface, so shell can display a screenshot
162− // in its place.
163− m_surfaceList[i].removeOnceSurfaceDestroyed = false;
164− }
165−}
166−
167−void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface)
168−{
169− int i = indexOf(surface);
170− if (i == -1) {
171− return;
172− }
173−
174− if (m_surfaceList[i].removeOnceSurfaceDestroyed) {
175− removeAt(i);
176− } else {
177− m_surfaceList[i].surface = nullptr;
178− Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
179− DEBUG_MSG << " Removed surface from entry. After: " << toString();
180− }
181−}
182−
183−void TopLevelSurfaceList::removeAt(int index)
184−{
185− if (m_modelState == IdleState) {
186− beginRemoveRows(QModelIndex(), index, index);
187− m_modelState = RemovingState;
188− } else {
189− Q_ASSERT(m_modelState == ResettingState);
190− // No point in signaling anything if we're resetting the whole model
191− }
192−
193− m_surfaceList.removeAt(index);
194−
195− if (m_modelState == RemovingState) {
196− endRemoveRows();
197− Q_EMIT countChanged();
198− Q_EMIT listChanged();
199− m_modelState = IdleState;
200− }
201−
202− DEBUG_MSG << " after " << toString();
203−}
204−
205−int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
206−{
207− for (int i = 0; i < m_surfaceList.count(); ++i) {
208− if (m_surfaceList.at(i).surface == surface) {
209− return i;
210− }
211− }
212− return -1;
213−}
214−
215−void TopLevelSurfaceList::move(int from, int to)
216−{
217− if (from == to) return;
218− DEBUG_MSG << " from=" << from << " to=" << to;
219−
220− if (from >= 0 && from < m_surfaceList.size() && to >= 0 && to < m_surfaceList.size()) {
221− QModelIndex parent;
222− /* When moving an item down, the destination index needs to be incremented
223− by one, as explained in the documentation:
224− http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
225−
226− Q_ASSERT(m_modelState == IdleState);
227− m_modelState = MovingState;
228−
229− beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
230− m_surfaceList.move(from, to);
231− endMoveRows();
232− Q_EMIT listChanged();
233−
234− m_modelState = IdleState;
235−
236− DEBUG_MSG << " after " << toString();
237− }
238−}
239−
240−MirSurfaceInterface *TopLevelSurfaceList::surfaceAt(int index) const
241−{
242− if (index >=0 && index < m_surfaceList.count()) {
243− return m_surfaceList[index].surface;
244− } else {
245− return nullptr;
246− }
247−}
248−
249−ApplicationInfoInterface *TopLevelSurfaceList::applicationAt(int index) const
250−{
251− if (index >=0 && index < m_surfaceList.count()) {
252− return m_surfaceList[index].application;
253− } else {
254− return nullptr;
255− }
256−}
257−
258−int TopLevelSurfaceList::idAt(int index) const
259−{
260− if (index >=0 && index < m_surfaceList.count()) {
261− return m_surfaceList[index].id;
262− } else {
263− return 0;
264− }
265−}
266−
267−int TopLevelSurfaceList::indexForId(int id) const
268−{
269− for (int i = 0; i < m_surfaceList.count(); ++i) {
270− if (m_surfaceList[i].id == id) {
271− return i;
272− }
273− }
274− return -1;
275−}
276−
277−void TopLevelSurfaceList::doRaiseId(int id)
278−{
279− int fromIndex = indexForId(id);
280− if (fromIndex != -1) {
281− move(fromIndex, 0 /* toIndex */);
282− }
283−}
284−
285−void TopLevelSurfaceList::raiseId(int id)
286−{
287− if (m_modelState == IdleState) {
288− DEBUG_MSG << "(id=" << id << ") - do it now.";
289− doRaiseId(id);
290− } else {
291− DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop.";
292− // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts
293− // if we perform yet another model change straight away.
294− //
295− // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says
296− // the index is definitely within bounds.
297− QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id));
298− }
299−}
300−
301−int TopLevelSurfaceList::generateId()
302−{
303− int id = m_nextId;
304− m_nextId = nextFreeId(m_nextId + 1);
305− Q_EMIT nextIdChanged();
306− return id;
307−}
308−
309−int TopLevelSurfaceList::nextFreeId(int candidateId)
310−{
311− if (candidateId > m_maxId) {
312− return nextFreeId(1);
313− } else {
314− if (indexForId(candidateId) == -1) {
315− // it's indeed free
316− return candidateId;
317− } else {
318− return nextFreeId(candidateId + 1);
319− }
320− }
321−}
322−
323−QString TopLevelSurfaceList::toString()
324−{
325− QString str;
326− for (int i = 0; i < m_surfaceList.count(); ++i) {
327− auto item = m_surfaceList.at(i);
328−
329− QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)")
330− .arg(i)
331− .arg(item.application->appId())
332− .arg((qintptr)item.surface, 0, 16)
333− .arg(item.id);
334−
335− if (i > 0) {
336− str.append(",");
337− }
338− str.append(itemStr);
339− }
340− return str;
341−}
342−
343−void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
344−{
345− DEBUG_MSG << "(" << application->appId() << ")";
346− Q_ASSERT(!m_applications.contains(application));
347− m_applications.append(application);
348−
349− MirSurfaceListInterface *surfaceList = application->surfaceList();
350−
351− if (application->state() != ApplicationInfoInterface::Stopped) {
352− if (surfaceList->count() == 0) {
353− appendPlaceholder(application);
354− } else {
355− for (int i = 0; i < surfaceList->count(); ++i) {
356− appendSurface(surfaceList->get(i), application);
357− }
358− }
359− }
360−
361− connect(surfaceList, &QAbstractItemModel::rowsInserted, this,
362− [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last)
363− {
364− for (int i = last; i >= first; --i) {
365− this->appendSurface(surfaceList->get(i), application);
366− }
367− });
368−}
369−
370−void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *application)
371−{
372− DEBUG_MSG << "(" << application->appId() << ")";
373− Q_ASSERT(m_applications.contains(application));
374−
375− MirSurfaceListInterface *surfaceList = application->surfaceList();
376−
377− disconnect(surfaceList, 0, this, 0);
378−
379− Q_ASSERT(m_modelState == IdleState);
380− m_modelState = RemovingState;
381−
382− int i = 0;
383− while (i < m_surfaceList.count()) {
384− if (m_surfaceList.at(i).application == application) {
385− beginRemoveRows(QModelIndex(), i, i);
386− m_surfaceList.removeAt(i);
387− endRemoveRows();
388− Q_EMIT countChanged();
389− Q_EMIT listChanged();
390− } else {
391− ++i;
392− }
393− }
394−
395− m_modelState = IdleState;
396−
397− DEBUG_MSG << " after " << toString();
398−
399− m_applications.removeAll(application);
400−}
401−
402−QAbstractListModel *TopLevelSurfaceList::applicationsModel() const
403−{
404− return m_applicationsModel;
405−}
406−
407−void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
408−{
409− if (m_applicationsModel == value) {
410− return;
411− }
412−
413− DEBUG_MSG << "(" << value << ")";
414−
415− Q_ASSERT(m_modelState == IdleState);
416− m_modelState = ResettingState;
417−
418− beginResetModel();
419−
420− if (m_applicationsModel) {
421− m_surfaceList.clear();
422− m_applications.clear();
423− disconnect(m_applicationsModel, 0, this, 0);
424− }
425−
426− m_applicationsModel = value;
427−
428− if (m_applicationsModel) {
429− findApplicationRole();
430−
431− connect(m_applicationsModel, &QAbstractItemModel::rowsInserted,
432− this, [this](const QModelIndex &/*parent*/, int first, int last) {
433− for (int i = first; i <= last; ++i) {
434− auto application = getApplicationFromModelAt(i);
435− addApplication(application);
436− }
437− });
438−
439− connect(m_applicationsModel, &QAbstractItemModel::rowsAboutToBeRemoved,
440− this, [this](const QModelIndex &/*parent*/, int first, int last) {
441− for (int i = first; i <= last; ++i) {
442− auto application = getApplicationFromModelAt(i);
443− removeApplication(application);
444− }
445− });
446−
447− for (int i = 0; i < m_applicationsModel->rowCount(); ++i) {
448− auto application = getApplicationFromModelAt(i);
449− addApplication(application);
450− }
451− }
452−
453− endResetModel();
454− m_modelState = IdleState;
455−}
456−
457−ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)
458−{
459− QModelIndex modelIndex = m_applicationsModel->index(index);
460−
461− QVariant variant = m_applicationsModel->data(modelIndex, m_applicationRole);
462−
463− // variant.value<ApplicationInfoInterface*>() returns null for some reason.
464− return static_cast<ApplicationInfoInterface*>(variant.value<QObject*>());
465−}
466−
467−void TopLevelSurfaceList::findApplicationRole()
468−{
469− QHash<int, QByteArray> namesHash = m_applicationsModel->roleNames();
470−
471− m_applicationRole = -1;
472− for (auto i = namesHash.begin(); i != namesHash.end() && m_applicationRole == -1; ++i) {
473− if (i.value() == "application") {
474− m_applicationRole = i.key();
475− }
476− }
477−
478− if (m_applicationRole == -1) {
479− qFatal("TopLevelSurfaceList: applicationsModel must have a \"application\" role.");
480− }
481−}
plugins/WindowManager/TopLevelSurfaceList.hdeleted−223
@@ -1,223 +0,0 @@
1−/*
2− * Copyright (C) 2016 Canonical, Ltd.
3− *
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.
7− *
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.
12− *
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/>.
15− */
16−
17−#ifndef TOPLEVELSURFACELIST_H
18−#define TOPLEVELSURFACELIST_H
19−
20−#include <QAbstractListModel>
21−#include <QList>
22−#include <QLoggingCategory>
23−
24−Q_DECLARE_LOGGING_CATEGORY(UNITY_TOPSURFACELIST)
25−
26−namespace unity {
27− namespace shell {
28− namespace application {
29− class ApplicationInfoInterface;
30− class MirSurfaceInterface;
31− }
32− }
33−}
34−
35−/**
36− * @brief A model of top-level surfaces
37− *
38− * It's an abstraction of top-level application windows.
39− *
40− * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is
41− * still starting up. A shell should then display a splash screen or saved screenshot of the application
42− * until its surface comes up.
43− *
44− * As applications can have multiple surfaces and you can also have entries without surfaces at all,
45− * the only way to unambiguously refer to an entry in this model is through its id.
46− */
47−class TopLevelSurfaceList : public QAbstractListModel
48−{
49−
50− Q_OBJECT
51−
52− /**
53− * @brief A list model of applications.
54− *
55− * It's expected to have a role called "application" which returns a ApplicationInfoInterface
56− */
57− Q_PROPERTY(QAbstractListModel* applicationsModel READ applicationsModel
58− WRITE setApplicationsModel
59− NOTIFY applicationsModelChanged)
60−
61− /**
62− * @brief Number of top-level surfaces in this model
63− *
64− * This is the same as rowCount, added in order to keep compatibility with QML ListModels.
65− */
66− Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
67−
68− /**
69− The id to be used on the next entry created
70− Useful for tests
71− */
72− Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged)
73−public:
74−
75− /**
76− * @brief The Roles supported by the model
77− *
78− * SurfaceRole - A MirSurfaceInterface. It will be null if the application is still starting up
79− * ApplicationRole - An ApplicationInfoInterface
80− * IdRole - A unique identifier for this entry. Useful to unambiguosly track elements as they move around in the list
81− */
82− enum Roles {
83− SurfaceRole = Qt::UserRole,
84− ApplicationRole = Qt::UserRole + 1,
85− IdRole = Qt::UserRole + 2,
86− };
87−
88− explicit TopLevelSurfaceList(QObject *parent = nullptr);
89− virtual ~TopLevelSurfaceList();
90−
91− // QAbstractItemModel methods
92− int rowCount(const QModelIndex &parent = QModelIndex()) const override;
93− QVariant data(const QModelIndex& index, int role) const override;
94− QHash<int, QByteArray> roleNames() const override {
95− QHash<int, QByteArray> roleNames { {SurfaceRole, "surface"},
96− {ApplicationRole, "application"},
97− {IdRole, "id"} };
98− return roleNames;
99− }
100−
101− int nextId() const { return m_nextId; }
102−
103− QAbstractListModel *applicationsModel() const;
104− void setApplicationsModel(QAbstractListModel*);
105−
106−public Q_SLOTS:
107− /**
108− * @brief Returns the surface at the given index
109− *
110− * It will be a nullptr if the application is still starting up and thus hasn't yet created
111− * and drawn into a surface.
112− */
113− unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const;
114−
115− /**
116− * @brief Returns the application at the given index
117− */
118− unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const;
119−
120− /**
121− * @brief Returns the unique id of the element at the given index
122− */
123− int idAt(int index) const;
124−
125− /**
126− * @brief Returns the index where the row with the given id is located
127− *
128− * Returns -1 if there's no row with the given id.
129− */
130− int indexForId(int id) const;
131−
132− /**
133− * @brief Raises the row with the given id to index 0
134− */
135− void raiseId(int id);
136−
137− void doRaiseId(int id);
138−
139−Q_SIGNALS:
140− void countChanged();
141−
142− /**
143− * @brief Emitted when the list changes
144− *
145− * Emitted when model gains an element, loses an element or when elements exchange positions.
146− */
147− void listChanged();
148−
149− void nextIdChanged();
150−
151− void applicationsModelChanged();
152−
153−private:
154− void addApplication(unity::shell::application::ApplicationInfoInterface *application);
155− void removeApplication(unity::shell::application::ApplicationInfoInterface *application);
156−
157− int indexOf(unity::shell::application::MirSurfaceInterface *surface);
158− void raise(unity::shell::application::MirSurfaceInterface *surface);
159− void move(int from, int to);
160− void appendSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
161− unity::shell::application::ApplicationInfoInterface *application);
162− void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
163− int generateId();
164− int nextFreeId(int candidateId);
165− QString toString();
166− void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface);
167− void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface);
168− void removeAt(int index);
169− void findApplicationRole();
170−
171− unity::shell::application::ApplicationInfoInterface *getApplicationFromModelAt(int index);
172−
173− /*
174− Placeholder for a future surface from a starting or running application.
175− Enables shell to give immediate feedback to the user by showing, eg,
176− a splash screen.
177−
178− It's a model row containing a null surface and the given application.
179− */
180− void appendPlaceholder(unity::shell::application::ApplicationInfoInterface *application);
181−
182− /*
183− Adds a model row with the given surface and application
184−
185− Alternatively, if a placeholder exists for the given application it's
186− filled with the given surface instead.
187− */
188− void appendSurface(unity::shell::application::MirSurfaceInterface *surface,
189− unity::shell::application::ApplicationInfoInterface *application);
190−
191− struct ModelEntry {
192− ModelEntry(unity::shell::application::MirSurfaceInterface *surface, unity::shell::application::ApplicationInfoInterface *application, int id)
193− : surface(surface), application(application), id(id) {}
194− unity::shell::application::MirSurfaceInterface *surface;
195− unity::shell::application::ApplicationInfoInterface *application;
196− int id;
197− bool removeOnceSurfaceDestroyed{false};
198− };
199−
200− QList<ModelEntry> m_surfaceList;
201− int m_nextId{1};
202− static const int m_maxId{1000000};
203−
204− // applications that are being monitored
205− QList<unity::shell::application::ApplicationInfoInterface *> m_applications;
206−
207− QAbstractListModel* m_applicationsModel{nullptr};
208− int m_applicationRole{-1};
209−
210− enum ModelState {
211− IdleState,
212− InsertingState,
213− RemovingState,
214− MovingState,
215− ResettingState
216− };
217− ModelState m_modelState{IdleState};
218−};
219−
220−Q_DECLARE_METATYPE(TopLevelSurfaceList*)
221−Q_DECLARE_METATYPE(QAbstractListModel*)
222−
223−#endif // TOPLEVELSURFACELIST_H
plugins/WindowManager/TopLevelWindowModel.cppnew file+782
@@ -0,0 +1,782 @@
1+/*
2+ * Copyright (C) 2016-2017 Canonical, Ltd.
3+ * Copyright 2019 UBports Foundation
4+ *
5+ * This program is free software: you can redistribute it and/or modify it under
6+ * the terms of the GNU Lesser General Public License version 3, as published by
7+ * the Free Software Foundation.
8+ *
9+ * This program is distributed in the hope that it will be useful, but WITHOUT
10+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+ * Lesser General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU Lesser General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#include "TopLevelWindowModel.h"
19+
20+// unity-api
21+#include <unity/shell/application/ApplicationInfoInterface.h>
22+#include <unity/shell/application/ApplicationManagerInterface.h>
23+#include <unity/shell/application/MirSurfaceInterface.h>
24+#include <unity/shell/application/MirSurfaceListInterface.h>
25+#include <unity/shell/application/SurfaceManagerInterface.h>
26+
27+// Qt
28+#include <QGuiApplication>
29+#include <QDebug>
30+
31+// local
32+#include "Window.h"
33+#include "InputMethodManager.h"
34+
35+Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
36+
37+#define DEBUG_MSG qCDebug(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__
38+#define INFO_MSG qCInfo(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__
39+
40+namespace unityapi = unity::shell::application;
41+
42+TopLevelWindowModel::TopLevelWindowModel()
43+ : m_nullWindow(createNullWindow()),
44+ m_surfaceManagerBusy(false)
45+{
46+ connect(m_nullWindow, &Window::focusedChanged, this, [this] {
47+ Q_EMIT rootFocusChanged();
48+ });
49+}
50+
51+void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInterface* value)
52+{
53+ if (m_applicationManager == value) {
54+ return;
55+ }
56+
57+ DEBUG_MSG << "(" << value << ")";
58+
59+ Q_ASSERT(m_modelState == IdleState);
60+ m_modelState = ResettingState;
61+
62+ beginResetModel();
63+
64+ if (m_applicationManager) {
65+ m_windowModel.clear();
66+ disconnect(m_applicationManager, 0, this, 0);
67+ }
68+
69+ m_applicationManager = value;
70+
71+ if (m_applicationManager) {
72+ connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
73+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
74+ for (int i = first; i <= last; ++i) {
75+ auto application = m_applicationManager->get(i);
76+ addApplication(application);
77+ }
78+ });
79+
80+ connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
81+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
82+ for (int i = first; i <= last; ++i) {
83+ auto application = m_applicationManager->get(i);
84+ removeApplication(application);
85+ }
86+ });
87+
88+ for (int i = 0; i < m_applicationManager->rowCount(); ++i) {
89+ auto application = m_applicationManager->get(i);
90+ addApplication(application);
91+ }
92+ }
93+
94+ endResetModel();
95+ m_modelState = IdleState;
96+}
97+
98+void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *surfaceManager)
99+{
100+ if (surfaceManager == m_surfaceManager) {
101+ return;
102+ }
103+
104+ DEBUG_MSG << "(" << surfaceManager << ")";
105+
106+ if (m_surfaceManager) {
107+ disconnect(m_surfaceManager, 0, this, 0);
108+ }
109+
110+ m_surfaceManager = surfaceManager;
111+
112+ if (m_surfaceManager) {
113+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfaceCreated, this, &TopLevelWindowModel::onSurfaceCreated);
114+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised);
115+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted);
116+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded);
117+ }
118+
119+ Q_EMIT surfaceManagerChanged(m_surfaceManager);
120+}
121+
122+void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application)
123+{
124+ DEBUG_MSG << "(" << application->appId() << ")";
125+
126+ if (application->state() != unityapi::ApplicationInfoInterface::Stopped && application->surfaceList()->count() == 0) {
127+ prependPlaceholder(application);
128+ }
129+}
130+
131+void TopLevelWindowModel::removeApplication(unityapi::ApplicationInfoInterface *application)
132+{
133+ DEBUG_MSG << "(" << application->appId() << ")";
134+
135+ Q_ASSERT(m_modelState == IdleState);
136+
137+ int i = 0;
138+ while (i < m_windowModel.count()) {
139+ if (m_windowModel.at(i).application == application) {
140+ deleteAt(i);
141+ } else {
142+ ++i;
143+ }
144+ }
145+}
146+
147+void TopLevelWindowModel::prependPlaceholder(unityapi::ApplicationInfoInterface *application)
148+{
149+ INFO_MSG << "(" << application->appId() << ")";
150+
151+ prependSurfaceHelper(nullptr, application);
152+}
153+
154+void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
155+{
156+ Q_ASSERT(surface != nullptr);
157+
158+ connectSurface(surface);
159+
160+ bool filledPlaceholder = false;
161+ for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
162+ ModelEntry &entry = m_windowModel[i];
163+ if (entry.application == application && entry.window->surface() == nullptr) {
164+ entry.window->setSurface(surface);
165+ INFO_MSG << " appId=" << application->appId() << " surface=" << surface
166+ << ", filling out placeholder. after: " << toString();
167+ filledPlaceholder = true;
168+ }
169+ }
170+
171+ if (!filledPlaceholder) {
172+ INFO_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
173+ prependSurfaceHelper(surface, application);
174+ }
175+}
176+
177+void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
178+{
179+
180+ Window *window = createWindow(surface);
181+
182+ connect(window, &Window::stateChanged, this, [=](Mir::State newState) {
183+ if (newState == Mir::HiddenState) {
184+ // Comply, removing it from our model. Just as if it didn't exist anymore.
185+ removeAt(indexForId(window->id()));
186+ } else {
187+ if (indexForId(window->id()) == -1) {
188+ // was probably hidden before. put it back on the list
189+ auto *application = m_applicationManager->findApplicationWithSurface(window->surface());
190+ Q_ASSERT(application);
191+ prependWindow(window, application);
192+ }
193+ }
194+ });
195+
196+ prependWindow(window, application);
197+
198+ // Activate the newly-prepended window.
199+ window->activate();
200+
201+ INFO_MSG << " after " << toString();
202+}
203+
204+void TopLevelWindowModel::prependWindow(Window *window, unityapi::ApplicationInfoInterface *application)
205+{
206+ if (m_modelState == IdleState) {
207+ m_modelState = InsertingState;
208+ beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
209+ } else {
210+ Q_ASSERT(m_modelState == ResettingState);
211+ // No point in signaling anything if we're resetting the whole model
212+ }
213+
214+ m_windowModel.prepend(ModelEntry(window, application));
215+
216+ if (m_modelState == InsertingState) {
217+ endInsertRows();
218+ Q_EMIT countChanged();
219+ Q_EMIT listChanged();
220+ m_modelState = IdleState;
221+ }
222+}
223+
224+void TopLevelWindowModel::connectWindow(Window *window)
225+{
226+ connect(window, &Window::focusRequested, this, [this, window]() {
227+ if (!window->surface()) {
228+ activateEmptyWindow(window);
229+ }
230+ });
231+
232+ connect(window, &Window::focusedChanged, this, [this, window](bool focused) {
233+ if (window->surface()) {
234+ // Condense changes to the focused window
235+ // eg: Do focusedWindow=A to focusedWindow=B instead of
236+ // focusedWindow=A to focusedWindow=null to focusedWindow=B
237+ if (focused) {
238+ Q_ASSERT(m_newlyFocusedWindow == nullptr);
239+ m_focusedWindowChanged = true;
240+ m_newlyFocusedWindow = window;
241+ } else if (m_focusedWindow == window) {
242+ m_focusedWindowChanged = true;
243+ } else {
244+ // don't clear the focused window if you were not there in the first place
245+ // happens when a filled window gets replaced with an empty one (no surface) as the focused window.
246+ }
247+ }
248+ });
249+
250+ connect(window, &Window::closeRequested, this, [this, window]() {
251+ if (!window->surface()) {
252+ // do things ourselves as miral doesn't know about this window
253+ int id = window->id();
254+ int index = indexForId(id);
255+ bool focusOther = false;
256+ Q_ASSERT(index >= 0);
257+ if (window->focused()) {
258+ focusOther = true;
259+ }
260+ m_windowModel[index].application->close();
261+ if (focusOther) {
262+ activateTopMostWindowWithoutId(id);
263+ }
264+ }
265+ });
266+
267+ connect(window, &Window::emptyWindowActivated, this, [this, window]() {
268+ activateEmptyWindow(window);
269+ });
270+
271+ connect(window, &Window::liveChanged, this, [this, window](bool isAlive) {
272+ if (!isAlive && window->state() == Mir::HiddenState) {
273+ // Hidden windows are not in the model. So just delete it right away.
274+ delete window;
275+ }
276+ });
277+}
278+
279+void TopLevelWindowModel::activateEmptyWindow(Window *window)
280+{
281+ Q_ASSERT(!window->surface());
282+ DEBUG_MSG << "(" << window << ")";
283+
284+ // miral doesn't know about empty windows (ie, windows that are not backed up by MirSurfaces)
285+ // So we have to activate them ourselves (instead of asking SurfaceManager to do it for us).
286+
287+ window->setFocused(true);
288+ raiseId(window->id());
289+ Window *previousWindow = m_focusedWindow;
290+ setFocusedWindow(window);
291+ if (previousWindow && previousWindow->surface() && previousWindow->surface()->focused()) {
292+ m_surfaceManager->activate(nullptr);
293+ }
294+}
295+
296+void TopLevelWindowModel::connectSurface(unityapi::MirSurfaceInterface *surface)
297+{
298+ connect(surface, &unityapi::MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
299+ if (!live) {
300+ onSurfaceDied(surface);
301+ }
302+ });
303+ connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); });
304+}
305+
306+void TopLevelWindowModel::onSurfaceDied(unityapi::MirSurfaceInterface *surface)
307+{
308+ if (surface->type() == Mir::InputMethodType) {
309+ removeInputMethodWindow();
310+ return;
311+ }
312+
313+ int i = indexOf(surface);
314+ if (i == -1) {
315+ return;
316+ }
317+
318+ auto application = m_windowModel[i].application;
319+
320+ DEBUG_MSG << " application->name()=" << application->name()
321+ << " application->state()=" << application->state();
322+
323+ if (application->state() == unityapi::ApplicationInfoInterface::Running
324+ || application->state() == unityapi::ApplicationInfoInterface::Starting) {
325+ m_windowModel[i].removeOnceSurfaceDestroyed = true;
326+ } else {
327+ // assume it got killed by the out-of-memory daemon.
328+ //
329+ // So leave entry in the model and only remove its surface, so shell can display a screenshot
330+ // in its place.
331+ m_windowModel[i].removeOnceSurfaceDestroyed = false;
332+ }
333+}
334+
335+void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surface)
336+{
337+ int i = indexOf(surface);
338+ if (i == -1) {
339+ return;
340+ }
341+
342+ if (m_windowModel[i].removeOnceSurfaceDestroyed) {
343+ deleteAt(i);
344+ } else {
345+ auto window = m_windowModel[i].window;
346+ window->setSurface(nullptr);
347+ window->setFocused(false);
348+ INFO_MSG << " Removed surface from entry. After: " << toString();
349+ }
350+}
351+
352+Window *TopLevelWindowModel::createWindow(unityapi::MirSurfaceInterface *surface)
353+{
354+ int id = m_nextId.fetchAndAddAcquire(1);
355+ return createWindowWithId(surface, id);
356+}
357+
358+Window *TopLevelWindowModel::createNullWindow()
359+{
360+ return createWindowWithId(nullptr, 0);
361+}
362+
363+Window *TopLevelWindowModel::createWindowWithId(unityapi::MirSurfaceInterface *surface, int id)
364+{
365+ Window *qmlWindow = new Window(id, this);
366+ connectWindow(qmlWindow);
367+ if (surface) {
368+ qmlWindow->setSurface(surface);
369+ }
370+ return qmlWindow;
371+}
372+
373+void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface)
374+{
375+ DEBUG_MSG << "(" << surface << ")";
376+
377+ if (surface->parentSurface()) {
378+ // Wrap it in a Window so that we keep focusedWindow() up to date.
379+ Window *window = createWindow(surface);
380+ connect(surface, &QObject::destroyed, window, [=](){
381+ window->setSurface(nullptr);
382+ window->deleteLater();
383+ });
384+ } else {
385+ if (surface->type() == Mir::InputMethodType) {
386+ connectSurface(surface);
387+ setInputMethodWindow(createWindow(surface));
388+ } else {
389+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
390+ if (application) {
391+ if (surface->state() == Mir::HiddenState) {
392+ // Ignore it until it's finally shown
393+ connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [=](Mir::State newState) {
394+ Q_ASSERT(newState != Mir::HiddenState);
395+ disconnect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, 0);
396+ prependSurface(surface, application);
397+ });
398+ } else {
399+ prependSurface(surface, application);
400+ }
401+ } else {
402+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
403+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
404+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
405+ Window *promptWindow = createWindow(surface);
406+ connect(surface, &QObject::destroyed, promptWindow, [=](){
407+ promptWindow->setSurface(nullptr);
408+ promptWindow->deleteLater();
409+ });
410+ }
411+ }
412+ }
413+}
414+
415+void TopLevelWindowModel::deleteAt(int index)
416+{
417+ auto window = m_windowModel[index].window;
418+
419+ removeAt(index);
420+
421+ window->setSurface(nullptr);
422+
423+ delete window;
424+}
425+
426+void TopLevelWindowModel::removeAt(int index)
427+{
428+ if (m_modelState == IdleState) {
429+ beginRemoveRows(QModelIndex(), index, index);
430+ m_modelState = RemovingState;
431+ } else {
432+ Q_ASSERT(m_modelState == ResettingState);
433+ // No point in signaling anything if we're resetting the whole model
434+ }
435+
436+ auto window = m_windowModel[index].window;
437+
438+ if (!window->surface()) {
439+ window->setFocused(false);
440+ }
441+
442+ m_windowModel.removeAt(index);
443+
444+ if (m_modelState == RemovingState) {
445+ endRemoveRows();
446+ Q_EMIT countChanged();
447+ Q_EMIT listChanged();
448+ m_modelState = IdleState;
449+ }
450+
451+ if (m_focusedWindow == window) {
452+ setFocusedWindow(nullptr);
453+ }
454+
455+ if (m_previousWindow == window) {
456+ m_previousWindow = nullptr;
457+ }
458+
459+ if (m_closingAllApps) {
460+ if (m_windowModel.isEmpty()) {
461+ Q_EMIT closedAllWindows();
462+ }
463+ }
464+
465+ INFO_MSG << " after " << toString() << " apps left " << m_windowModel.count();
466+}
467+
468+void TopLevelWindowModel::setInputMethodWindow(Window *window)
469+{
470+ if (m_inputMethodWindow) {
471+ qWarning("Multiple Input Method Surfaces created, removing the old one!");
472+ delete m_inputMethodWindow;
473+ }
474+ m_inputMethodWindow = window;
475+ Q_EMIT inputMethodSurfaceChanged(m_inputMethodWindow->surface());
476+ InputMethodManager::instance()->setWindow(window);
477+}
478+
479+void TopLevelWindowModel::removeInputMethodWindow()
480+{
481+ if (m_inputMethodWindow) {
482+ delete m_inputMethodWindow;
483+ m_inputMethodWindow = nullptr;
484+ Q_EMIT inputMethodSurfaceChanged(nullptr);
485+ InputMethodManager::instance()->setWindow(nullptr);
486+ }
487+}
488+
489+void TopLevelWindowModel::onSurfacesRaised(const QVector<unityapi::MirSurfaceInterface*> &surfaces)
490+{
491+ DEBUG_MSG << "(" << surfaces << ")";
492+ const int raiseCount = surfaces.size();
493+ for (int i = 0; i < raiseCount; i++) {
494+ int fromIndex = findIndexOf(surfaces[i]);
495+ if (fromIndex != -1) {
496+ move(fromIndex, 0);
497+ }
498+ }
499+}
500+
501+int TopLevelWindowModel::rowCount(const QModelIndex &/*parent*/) const
502+{
503+ return m_windowModel.count();
504+}
505+
506+QVariant TopLevelWindowModel::data(const QModelIndex& index, int role) const
507+{
508+ if (index.row() < 0 || index.row() >= m_windowModel.size())
509+ return QVariant();
510+
511+ if (role == WindowRole) {
512+ Window *window = m_windowModel.at(index.row()).window;
513+ return QVariant::fromValue(window);
514+ } else if (role == ApplicationRole) {
515+ return QVariant::fromValue(m_windowModel.at(index.row()).application);
516+ } else {
517+ return QVariant();
518+ }
519+}
520+
521+int TopLevelWindowModel::findIndexOf(const unityapi::MirSurfaceInterface *surface) const
522+{
523+ for (int i=0; i<m_windowModel.count(); i++) {
524+ if (m_windowModel[i].window->surface() == surface) {
525+ return i;
526+ }
527+ }
528+ return -1;
529+}
530+
531+QString TopLevelWindowModel::toString()
532+{
533+ QString str;
534+ for (int i = 0; i < m_windowModel.count(); ++i) {
535+ auto item = m_windowModel.at(i);
536+
537+ QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)")
538+ .arg(QString::number(i),
539+ item.application->appId(),
540+ QString::number((qintptr)item.window->surface(), 16),
541+ QString::number(item.window->id()));
542+
543+ if (i > 0) {
544+ str.append(",");
545+ }
546+ str.append(itemStr);
547+ }
548+ return str;
549+}
550+
551+int TopLevelWindowModel::indexOf(unityapi::MirSurfaceInterface *surface)
552+{
553+ for (int i = 0; i < m_windowModel.count(); ++i) {
554+ if (m_windowModel.at(i).window->surface() == surface) {
555+ return i;
556+ }
557+ }
558+ return -1;
559+}
560+
561+int TopLevelWindowModel::indexForId(int id) const
562+{
563+ for (int i = 0; i < m_windowModel.count(); ++i) {
564+ if (m_windowModel[i].window->id() == id) {
565+ return i;
566+ }
567+ }
568+ return -1;
569+}
570+
571+Window *TopLevelWindowModel::windowAt(int index) const
572+{
573+ if (index >=0 && index < m_windowModel.count()) {
574+ return m_windowModel[index].window;
575+ } else {
576+ return nullptr;
577+ }
578+}
579+
580+unityapi::MirSurfaceInterface *TopLevelWindowModel::surfaceAt(int index) const
581+{
582+ if (index >=0 && index < m_windowModel.count()) {
583+ return m_windowModel[index].window->surface();
584+ } else {
585+ return nullptr;
586+ }
587+}
588+
589+unityapi::ApplicationInfoInterface *TopLevelWindowModel::applicationAt(int index) const
590+{
591+ if (index >=0 && index < m_windowModel.count()) {
592+ return m_windowModel[index].application;
593+ } else {
594+ return nullptr;
595+ }
596+}
597+
598+int TopLevelWindowModel::idAt(int index) const
599+{
600+ if (index >=0 && index < m_windowModel.count()) {
601+ return m_windowModel[index].window->id();
602+ } else {
603+ return 0;
604+ }
605+}
606+
607+void TopLevelWindowModel::raiseId(int id)
608+{
609+ if (m_modelState == IdleState) {
610+ DEBUG_MSG << "(id=" << id << ") - do it now.";
611+ doRaiseId(id);
612+ } else {
613+ DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop.";
614+ // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts
615+ // if we perform yet another model change straight away.
616+ //
617+ // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says
618+ // the index is definitely within bounds.
619+ QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id));
620+ }
621+}
622+
623+void TopLevelWindowModel::doRaiseId(int id)
624+{
625+ int fromIndex = indexForId(id);
626+ // can't raise something that doesn't exist or that it's already on top
627+ if (fromIndex != -1 && fromIndex != 0) {
628+ auto surface = m_windowModel[fromIndex].window->surface();
629+ if (surface) {
630+ m_surfaceManager->raise(surface);
631+ } else {
632+ // move it ourselves. Since there's no mir::scene::Surface/miral::Window, there's nothing
633+ // miral can do about it.
634+ move(fromIndex, 0);
635+ }
636+ }
637+}
638+
639+void TopLevelWindowModel::setFocusedWindow(Window *window)
640+{
641+ if (window != m_focusedWindow) {
642+ INFO_MSG << "(" << window << ")";
643+
644+ m_previousWindow = m_focusedWindow;
645+
646+ m_focusedWindow = window;
647+ Q_EMIT focusedWindowChanged(m_focusedWindow);
648+
649+ if (m_previousWindow && m_previousWindow->focused() && !m_previousWindow->surface()) {
650+ // do it ourselves. miral doesn't know about this window
651+ m_previousWindow->setFocused(false);
652+ }
653+ }
654+
655+ // Reset
656+ m_pendingActivation = false;
657+}
658+
659+unityapi::MirSurfaceInterface* TopLevelWindowModel::inputMethodSurface() const
660+{
661+ return m_inputMethodWindow ? m_inputMethodWindow->surface() : nullptr;
662+}
663+
664+Window* TopLevelWindowModel::focusedWindow() const
665+{
666+ return m_focusedWindow;
667+}
668+
669+void TopLevelWindowModel::move(int from, int to)
670+{
671+ if (from == to) return;
672+ DEBUG_MSG << " from=" << from << " to=" << to;
673+
674+ if (from >= 0 && from < m_windowModel.size() && to >= 0 && to < m_windowModel.size()) {
675+ QModelIndex parent;
676+ /* When moving an item down, the destination index needs to be incremented
677+ by one, as explained in the documentation:
678+ http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
679+
680+ Q_ASSERT(m_modelState == IdleState);
681+ m_modelState = MovingState;
682+
683+ beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
684+#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
685+ const auto &window = m_windowModel.takeAt(from);
686+ m_windowModel.insert(to, window);
687+#else
688+ m_windowModel.move(from, to);
689+#endif
690+ endMoveRows();
691+
692+ Q_EMIT listChanged();
693+ m_modelState = IdleState;
694+
695+ INFO_MSG << " after " << toString();
696+ }
697+}
698+void TopLevelWindowModel::onModificationsStarted()
699+{
700+ m_surfaceManagerBusy = true;
701+}
702+
703+void TopLevelWindowModel::onModificationsEnded()
704+{
705+ if (m_focusedWindowChanged) {
706+ setFocusedWindow(m_newlyFocusedWindow);
707+ }
708+ // reset
709+ m_focusedWindowChanged = false;
710+ m_newlyFocusedWindow = nullptr;
711+ m_surfaceManagerBusy = false;
712+}
713+
714+void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
715+{
716+ DEBUG_MSG << "(" << forbiddenId << ")";
717+
718+ for (int i = 0; i < m_windowModel.count(); ++i) {
719+ Window *window = m_windowModel[i].window;
720+ if (window->id() != forbiddenId) {
721+ window->activate();
722+ break;
723+ }
724+ }
725+}
726+
727+void TopLevelWindowModel::closeAllWindows()
728+{
729+ m_closingAllApps = true;
730+ for (auto win : m_windowModel) {
731+ win.window->close();
732+ }
733+
734+ // This is done after the for loop in the unlikely event that
735+ // an app starts in between this
736+ if (m_windowModel.isEmpty()) {
737+ Q_EMIT closedAllWindows();
738+ }
739+}
740+
741+bool TopLevelWindowModel::rootFocus()
742+{
743+ return !m_nullWindow->focused();
744+}
745+
746+void TopLevelWindowModel::setRootFocus(bool focus)
747+{
748+ INFO_MSG << "(" << focus << "), surfaceManagerBusy is " << m_surfaceManagerBusy;
749+
750+ if (m_surfaceManagerBusy) {
751+ // Something else is probably being focused already, let's not add to
752+ // the noise.
753+ return;
754+ }
755+
756+ if (focus) {
757+ // Give focus back to previous focused window, only if null window is focused.
758+ // If null window is not focused, a different app had taken the focus and we
759+ // should repect that, or if a pendingActivation is going on.
760+ if (m_previousWindow && !m_previousWindow->focused() && !m_pendingActivation &&
761+ m_nullWindow == m_focusedWindow && m_previousWindow != m_nullWindow) {
762+ m_previousWindow->activate();
763+ } else if (!m_pendingActivation) {
764+ // The previous window does not exist any more, focus top window.
765+ activateTopMostWindowWithoutId(-1);
766+ }
767+ } else {
768+ if (!m_nullWindow->focused()) {
769+ m_nullWindow->activate();
770+ }
771+ }
772+}
773+
774+// Pending Activation will block refocus of previous focused window
775+// this is needed since surface activation with miral is async,
776+// and activation of placeholder is sync. This causes a race condition
777+// between placeholder and prev window. This results in prev window
778+// gets focused, as it will always be later than the placeholder.
779+void TopLevelWindowModel::pendingActivation()
780+{
781+ m_pendingActivation = true;
782+}
plugins/WindowManager/TopLevelWindowModel.hnew file+308
@@ -0,0 +1,308 @@
1+/*
2+ * Copyright (C) 2016-2017 Canonical, Ltd.
3+ * Copyright 2019 UBports Foundation
4+ *
5+ * This program is free software: you can redistribute it and/or modify it under
6+ * the terms of the GNU Lesser General Public License version 3, as published by
7+ * the Free Software Foundation.
8+ *
9+ * This program is distributed in the hope that it will be useful, but WITHOUT
10+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+ * Lesser General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU Lesser General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#ifndef TOPLEVELWINDOWMODEL_H
19+#define TOPLEVELWINDOWMODEL_H
20+
21+#include <QAbstractListModel>
22+#include <QAtomicInteger>
23+#include <QLoggingCategory>
24+
25+#include "WindowManagerGlobal.h"
26+
27+Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL)
28+
29+class Window;
30+
31+namespace unity {
32+ namespace shell {
33+ namespace application {
34+ class ApplicationInfoInterface;
35+ class ApplicationManagerInterface;
36+ class MirSurfaceInterface;
37+ class SurfaceManagerInterface;
38+ }
39+ }
40+}
41+
42+/**
43+ * @brief A model of top-level surfaces
44+ *
45+ * It's an abstraction of top-level application windows.
46+ *
47+ * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is
48+ * still starting up. A shell should then display a splash screen or saved screenshot of the application
49+ * until its surface comes up.
50+ *
51+ * As applications can have multiple surfaces and you can also have entries without surfaces at all,
52+ * the only way to unambiguously refer to an entry in this model is through its id.
53+ */
54+class WINDOWMANAGERQML_EXPORT TopLevelWindowModel : public QAbstractListModel
55+{
56+ Q_OBJECT
57+
58+ /**
59+ * @brief Number of top-level surfaces in this model
60+ *
61+ * This is the same as rowCount, added in order to keep compatibility with QML ListModels.
62+ */
63+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
64+
65+ /**
66+ * @brief The input method surface, if any
67+ *
68+ * The surface of a onscreen keyboard (akak "virtual keyboard") would be kept here and not in the model itself.
69+ */
70+ Q_PROPERTY(unity::shell::application::MirSurfaceInterface* inputMethodSurface READ inputMethodSurface NOTIFY inputMethodSurfaceChanged)
71+
72+ /**
73+ * @brief The currently focused window, if any
74+ */
75+ Q_PROPERTY(Window* focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
76+
77+ Q_PROPERTY(unity::shell::application::SurfaceManagerInterface* surfaceManager
78+ READ surfaceManager
79+ WRITE setSurfaceManager
80+ NOTIFY surfaceManagerChanged)
81+
82+ Q_PROPERTY(unity::shell::application::ApplicationManagerInterface* applicationManager
83+ READ applicationManager
84+ WRITE setApplicationManager
85+ NOTIFY applicationManagerChanged)
86+
87+ /**
88+ The id to be used on the next entry created
89+ Useful for tests
90+ */
91+ Q_PROPERTY(int nextId READ nextId)
92+
93+
94+ /**
95+ * @brief Sets whether a user Window or "nothing" should be focused
96+ *
97+ * This implementation of TLWM must have something focused. However, the
98+ * user may wish to have nothing in some cases - for example, when they
99+ * minimize all their windows on the desktop or unfocus the app they're
100+ * using by clicking the background or indicators.
101+ *
102+ * Unsetting rootFocus effectively focuses "nothing" by setting up a Window
103+ * that has no displayable Surfaces and bringing it into focus.
104+ *
105+ * Setting rootFocus attempts to focus the Window which was focused last -
106+ * unless another app is attempting to gain focus (as determined by
107+ * pendingActivation) and that's why we got rootFocus.
108+ *
109+ * If the previously-focused Window was closed before rootFocus was set,
110+ * the next available window will be focused.
111+ */
112+ Q_PROPERTY(bool rootFocus READ rootFocus WRITE setRootFocus NOTIFY rootFocusChanged)
113+
114+public:
115+ /**
116+ * @brief The Roles supported by the model
117+ *
118+ * WindowRole - A Window.
119+ * ApplicationRole - An ApplicationInfoInterface
120+ */
121+ enum Roles {
122+ WindowRole = Qt::UserRole,
123+ ApplicationRole = Qt::UserRole + 1,
124+ };
125+
126+ TopLevelWindowModel();
127+
128+ // From QAbstractItemModel
129+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
130+ QVariant data(const QModelIndex& index, int role) const override;
131+ QHash<int, QByteArray> roleNames() const override {
132+ QHash<int, QByteArray> roleNames { {WindowRole, "window"},
133+ {ApplicationRole, "application"} };
134+ return roleNames;
135+ }
136+
137+ // Own API
138+
139+ unity::shell::application::MirSurfaceInterface* inputMethodSurface() const;
140+ Window* focusedWindow() const;
141+
142+ unity::shell::application::ApplicationManagerInterface *applicationManager() const { return m_applicationManager; }
143+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface*);
144+
145+ unity::shell::application::SurfaceManagerInterface *surfaceManager() const { return m_surfaceManager; }
146+ void setSurfaceManager(unity::shell::application::SurfaceManagerInterface*);
147+
148+ int nextId() const { return m_nextId.load(); }
149+
150+public:
151+ /**
152+ * @brief Returns the surface at the given index
153+ *
154+ * It will be a nullptr if the application is still starting up and thus hasn't yet created
155+ * and drawn into a surface.
156+ *
157+ * Same as windowAt(index).surface()
158+ */
159+ Q_INVOKABLE unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const;
160+
161+ /**
162+ * @brief Returns the window at the given index
163+ *
164+ * Will always be valid
165+ */
166+ Q_INVOKABLE Window *windowAt(int index) const;
167+
168+ /**
169+ * @brief Returns the application at the given index
170+ */
171+ Q_INVOKABLE unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const;
172+
173+ /**
174+ * @brief Returns the unique id of the element at the given index
175+ */
176+ Q_INVOKABLE int idAt(int index) const;
177+
178+ /**
179+ * @brief Returns the index where the row with the given id is located
180+ *
181+ * Returns -1 if there's no row with the given id.
182+ */
183+ Q_INVOKABLE int indexForId(int id) const;
184+
185+ /**
186+ * @brief Raises the row with the given id to the top of the window stack (index == count-1)
187+ */
188+ Q_INVOKABLE void raiseId(int id);
189+
190+ /**
191+ * @brief Closes all windows, emits closedAllWindows when done
192+ */
193+ Q_INVOKABLE void closeAllWindows();
194+
195+ /**
196+ * @brief Sets pending activation flag
197+ */
198+ Q_INVOKABLE void pendingActivation();
199+
200+ void setRootFocus(bool focus);
201+ bool rootFocus();
202+
203+Q_SIGNALS:
204+ void countChanged();
205+ void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface);
206+ void focusedWindowChanged(Window *focusedWindow);
207+ void applicationManagerChanged(unity::shell::application::ApplicationManagerInterface*);
208+ void surfaceManagerChanged(unity::shell::application::SurfaceManagerInterface*);
209+
210+ /**
211+ * @brief Emitted when the list changes
212+ *
213+ * Emitted when model gains an element, loses an element or when elements exchange positions.
214+ */
215+ void listChanged();
216+
217+ void closedAllWindows();
218+
219+ void rootFocusChanged();
220+
221+private Q_SLOTS:
222+ void onSurfaceCreated(unity::shell::application::MirSurfaceInterface *surface);
223+ void onSurfacesRaised(const QVector<unity::shell::application::MirSurfaceInterface*> &surfaces);
224+
225+ void onModificationsStarted();
226+ void onModificationsEnded();
227+
228+private:
229+ void doRaiseId(int id);
230+ int generateId();
231+ int nextFreeId(int candidateId, const int latestId);
232+ int nextId(int id) const;
233+ QString toString();
234+ int indexOf(unity::shell::application::MirSurfaceInterface *surface);
235+
236+ void setInputMethodWindow(Window *window);
237+ void setFocusedWindow(Window *window);
238+ void removeInputMethodWindow();
239+ int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const;
240+ void deleteAt(int index);
241+ void removeAt(int index);
242+
243+ void addApplication(unity::shell::application::ApplicationInfoInterface *application);
244+ void removeApplication(unity::shell::application::ApplicationInfoInterface *application);
245+
246+ void prependPlaceholder(unity::shell::application::ApplicationInfoInterface *application);
247+ void prependSurface(unity::shell::application::MirSurfaceInterface *surface,
248+ unity::shell::application::ApplicationInfoInterface *application);
249+ void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
250+ unity::shell::application::ApplicationInfoInterface *application);
251+ void prependWindow(Window *window, unity::shell::application::ApplicationInfoInterface *application);
252+
253+ void connectWindow(Window *window);
254+ void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
255+
256+ void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface);
257+ void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface);
258+
259+ void move(int from, int to);
260+
261+ void activateEmptyWindow(Window *window);
262+
263+ void activateTopMostWindowWithoutId(int forbiddenId);
264+
265+ Window *createWindow(unity::shell::application::MirSurfaceInterface *surface);
266+ Window *createWindowWithId(unity::shell::application::MirSurfaceInterface *surface, int id);
267+ Window *createNullWindow();
268+
269+ struct ModelEntry {
270+ ModelEntry() {}
271+ ModelEntry(Window *window,
272+ unity::shell::application::ApplicationInfoInterface *application)
273+ : window(window), application(application) {}
274+ Window *window{nullptr};
275+ unity::shell::application::ApplicationInfoInterface *application{nullptr};
276+ bool removeOnceSurfaceDestroyed{false};
277+ };
278+
279+ QVector<ModelEntry> m_windowModel;
280+ Window* m_inputMethodWindow{nullptr};
281+ Window* m_focusedWindow{nullptr};
282+ Window* m_nullWindow;
283+ Window* m_previousWindow{nullptr};
284+ bool m_pendingActivation;
285+
286+ QAtomicInteger<int> m_nextId{1};
287+
288+ unity::shell::application::ApplicationManagerInterface* m_applicationManager{nullptr};
289+ unity::shell::application::SurfaceManagerInterface *m_surfaceManager{nullptr};
290+ bool m_surfaceManagerBusy;
291+
292+ enum ModelState {
293+ IdleState,
294+ InsertingState,
295+ RemovingState,
296+ MovingState,
297+ ResettingState
298+ };
299+ ModelState m_modelState{IdleState};
300+
301+ // Valid between modificationsStarted and modificationsEnded
302+ bool m_focusedWindowChanged{false};
303+ Window *m_newlyFocusedWindow{nullptr};
304+
305+ bool m_closingAllApps{false};
306+};
307+
308+#endif // TOPLEVELWINDOWMODEL_H
plugins/WindowManager/Window.cppnew file+277
@@ -0,0 +1,277 @@
1+/*
2+ * Copyright (C) 2016-2017 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include "Window.h"
18+
19+// unity-api
20+#include <unity/shell/application/MirSurfaceInterface.h>
21+
22+#include <QQmlEngine>
23+#include <QTextStream>
24+
25+namespace unityapi = unity::shell::application;
26+
27+Q_LOGGING_CATEGORY(UNITY_WINDOW, "unity.window", QtWarningMsg)
28+
29+#define DEBUG_MSG qCDebug(UNITY_WINDOW).nospace() << qPrintable(toString()) << "::" << __func__
30+#define WARNING_MSG qCWarning(UNITY_WINDOW).nospace() << qPrintable(toString()) << "::" << __func__
31+
32+Window::Window(int id, QObject *parent)
33+ : QObject(parent)
34+ , m_id(id)
35+{
36+ DEBUG_MSG << "()";
37+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
38+}
39+
40+Window::~Window()
41+{
42+ DEBUG_MSG << "()";
43+}
44+
45+QPoint Window::position() const
46+{
47+ return m_position;
48+}
49+
50+QPoint Window::requestedPosition() const
51+{
52+ return m_requestedPosition;
53+}
54+
55+void Window::setRequestedPosition(const QPoint &value)
56+{
57+ m_positionRequested = true;
58+ if (value != m_requestedPosition) {
59+ m_requestedPosition = value;
60+ Q_EMIT requestedPositionChanged(m_requestedPosition);
61+ if (m_surface) {
62+ m_surface->setRequestedPosition(value);
63+ } else {
64+ // fake-miral: always comply
65+ m_position = m_requestedPosition;
66+ Q_EMIT positionChanged(m_position);
67+ }
68+ }
69+}
70+
71+bool Window::allowClientResize() const
72+{
73+ return m_allowClientResize;
74+}
75+
76+void Window::setAllowClientResize(bool value)
77+{
78+ if (value != m_allowClientResize) {
79+ DEBUG_MSG << "("<<value<<")";
80+ m_allowClientResize = value;
81+ if (m_surface) {
82+ m_surface->setAllowClientResize(value);
83+ }
84+ Q_EMIT allowClientResizeChanged(m_allowClientResize);
85+ }
86+}
87+
88+Mir::State Window::state() const
89+{
90+ return m_state;
91+}
92+
93+bool Window::focused() const
94+{
95+ return m_focused;
96+}
97+
98+bool Window::confinesMousePointer() const
99+{
100+ if (m_surface) {
101+ return m_surface->confinesMousePointer();
102+ } else {
103+ return false;
104+ }
105+}
106+
107+int Window::id() const
108+{
109+ return m_id;
110+}
111+
112+unityapi::MirSurfaceInterface* Window::surface() const
113+{
114+ return m_surface;
115+}
116+
117+void Window::requestState(Mir::State state)
118+{
119+ m_stateRequested = true;
120+ if (m_surface) {
121+ m_surface->requestState(state);
122+ } else if (m_state != state) {
123+ m_state = state;
124+ Q_EMIT stateChanged(m_state);
125+ }
126+}
127+
128+void Window::close()
129+{
130+ if (m_surface) {
131+ m_surface->close();
132+ } else {
133+ Q_EMIT closeRequested();
134+ }
135+}
136+
137+void Window::activate()
138+{
139+ DEBUG_MSG << "()";
140+ if (m_surface) {
141+ m_surface->activate();
142+ } else {
143+ Q_EMIT emptyWindowActivated();
144+ }
145+}
146+
147+void Window::setSurface(unityapi::MirSurfaceInterface *surface)
148+{
149+ DEBUG_MSG << "(" << surface << ")";
150+ if (m_surface) {
151+ disconnect(m_surface, 0, this, 0);
152+ }
153+
154+ m_surface = surface;
155+
156+ if (m_surface) {
157+ connect(surface, &unityapi::MirSurfaceInterface::focusRequested, this, [this]() {
158+ Q_EMIT focusRequested();
159+ });
160+
161+ connect(surface, &unityapi::MirSurfaceInterface::closeRequested, this, &Window::closeRequested);
162+
163+ connect(surface, &unityapi::MirSurfaceInterface::positionChanged, this, [this]() {
164+ updatePosition();
165+ });
166+
167+ connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [this]() {
168+ updateState();
169+ });
170+
171+ connect(surface, &unityapi::MirSurfaceInterface::focusedChanged, this, [this]() {
172+ updateFocused();
173+ });
174+
175+ connect(surface, &unityapi::MirSurfaceInterface::allowClientResizeChanged, this, [this]() {
176+ if (m_surface->allowClientResize() != m_allowClientResize) {
177+ m_allowClientResize = m_surface->allowClientResize();
178+ Q_EMIT allowClientResizeChanged(m_allowClientResize);
179+ }
180+ });
181+
182+ connect(surface, &unityapi::MirSurfaceInterface::liveChanged, this, &Window::liveChanged);
183+
184+ connect(surface, &QObject::destroyed, this, [this]() {
185+ setSurface(nullptr);
186+ });
187+
188+ // Surface should never be focused at this point!
189+ if (m_surface->focused()) {
190+ WARNING_MSG << "Inital surface is focused!";
191+ }
192+
193+ // bring it up to speed
194+ if (m_focused) {
195+ m_surface->activate();
196+ }
197+ if (m_positionRequested) {
198+ m_surface->setRequestedPosition(m_requestedPosition);
199+ }
200+ if (m_stateRequested && m_surface->state() == Mir::RestoredState) {
201+ m_surface->requestState(m_state);
202+ }
203+ m_surface->setAllowClientResize(m_allowClientResize);
204+
205+ // and sync with surface
206+ updatePosition();
207+ updateState();
208+ updateFocused();
209+ }
210+
211+ Q_EMIT surfaceChanged(surface);
212+}
213+
214+void Window::updatePosition()
215+{
216+ if (m_surface->position() != m_position) {
217+ m_position = m_surface->position();
218+ Q_EMIT positionChanged(m_position);
219+ }
220+}
221+
222+void Window::updateState()
223+{
224+ if (m_surface->state() != m_state) {
225+ m_state = m_surface->state();
226+ Q_EMIT stateChanged(m_state);
227+ }
228+}
229+
230+void Window::updateFocused()
231+{
232+ if (m_surface->focused() != m_focused) {
233+ m_focused = m_surface->focused();
234+ Q_EMIT focusedChanged(m_focused);
235+ }
236+}
237+
238+void Window::setFocused(bool value)
239+{
240+ if (value != m_focused) {
241+ DEBUG_MSG << "(" << value << ")";
242+ m_focused = value;
243+ Q_EMIT focusedChanged(m_focused);
244+ // when we have a surface we get focus changes from updateFocused() instead
245+ Q_ASSERT(!m_surface);
246+ }
247+}
248+
249+QString Window::toString() const
250+{
251+ QString result;
252+ {
253+ QTextStream stream(&result);
254+ stream << "Window["<<(void*)this<<", id="<<id()<<", ";
255+ if (surface()) {
256+ stream << "MirSurface["<<(void*)surface()<<",\""<<surface()->name()<<"\"]";
257+ } else {
258+ stream << "null";
259+ }
260+ stream << "]";
261+ }
262+ return result;
263+}
264+
265+QDebug operator<<(QDebug dbg, const Window *window)
266+{
267+ QDebugStateSaver saver(dbg);
268+ dbg.nospace();
269+
270+ if (window) {
271+ dbg << qPrintable(window->toString());
272+ } else {
273+ dbg << (void*)(window);
274+ }
275+
276+ return dbg;
277+}
plugins/WindowManager/Window.hnew file+176
@@ -0,0 +1,176 @@
1+/*
2+ * Copyright (C) 2016-2017 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#ifndef UNITY_WINDOW_H
18+#define UNITY_WINDOW_H
19+
20+#include <QLoggingCategory>
21+#include <QObject>
22+#include <QPoint>
23+
24+// Unity API
25+#include <unity/shell/application/Mir.h>
26+
27+#include "WindowManagerGlobal.h"
28+
29+namespace unity {
30+ namespace shell {
31+ namespace application {
32+ class MirSurfaceInterface;
33+ }
34+ }
35+}
36+
37+Q_DECLARE_LOGGING_CATEGORY(UNITY_WINDOW)
38+
39+/**
40+ @brief A slightly higher concept than MirSurface
41+
42+ A Window exists before its MirSurface gets created (for splashscreen purposes)
43+ and might also hang around after the backing surface is gone (In case the application
44+ was killed to free up memory, as it should still remain in the window list since the user
45+ did not explicitly close it).
46+ */
47+class WINDOWMANAGERQML_EXPORT Window : public QObject
48+{
49+ Q_OBJECT
50+
51+ /**
52+ * @brief Position of the current surface buffer, in pixels.
53+ */
54+ Q_PROPERTY(QPoint position READ position NOTIFY positionChanged)
55+
56+ /**
57+ * @brief Requested position of the current surface buffer, in pixels.
58+ */
59+ Q_PROPERTY(QPoint requestedPosition READ requestedPosition WRITE setRequestedPosition NOTIFY requestedPositionChanged)
60+
61+ /**
62+ * @brief State of the surface
63+ */
64+ Q_PROPERTY(Mir::State state READ state NOTIFY stateChanged)
65+
66+ /**
67+ * @brief Whether the surface is focused
68+ *
69+ * It will be true if this surface is MirFocusControllerInterface::focusedSurface
70+ */
71+ Q_PROPERTY(bool focused READ focused NOTIFY focusedChanged)
72+
73+ /**
74+ * @brief Whether the surface wants to confine the mouse pointer within its boundaries
75+ *
76+ * If true, the surface doesn't want the mouse pointer to leave its boundaries while it's focused.
77+ */
78+ Q_PROPERTY(bool confinesMousePointer READ confinesMousePointer NOTIFY confinesMousePointerChanged)
79+
80+ /**
81+ * @brief A unique identifier for this window.
82+ * Useful for telling windows apart in a list model as they get moved around
83+ */
84+ Q_PROPERTY(int id READ id CONSTANT)
85+
86+ /**
87+ * @brief Surface backing up this window
88+ * It might be null if a surface hasn't been created yet (application is starting up) or if
89+ * the corresponding application has been killed (but can still get restarted to continue from
90+ * where it left)
91+ */
92+ Q_PROPERTY(unity::shell::application::MirSurfaceInterface* surface READ surface NOTIFY surfaceChanged)
93+
94+ /**
95+ * @brief Whether to comply to resize requests coming from the client side
96+ *
97+ * It's true by default
98+ */
99+ Q_PROPERTY(bool allowClientResize READ allowClientResize WRITE setAllowClientResize NOTIFY allowClientResizeChanged)
100+
101+public:
102+ Window(int id, QObject *parent = nullptr);
103+ virtual ~Window();
104+ QPoint position() const;
105+ QPoint requestedPosition() const;
106+ void setRequestedPosition(const QPoint &);
107+ Mir::State state() const;
108+ bool focused() const;
109+ bool confinesMousePointer() const;
110+ int id() const;
111+ unity::shell::application::MirSurfaceInterface* surface() const;
112+
113+ void setSurface(unity::shell::application::MirSurfaceInterface *surface);
114+ void setFocused(bool value);
115+
116+ bool allowClientResize() const;
117+ void setAllowClientResize(bool);
118+
119+ QString toString() const;
120+
121+public Q_SLOTS:
122+ /**
123+ * @brief Requests a change to the specified state
124+ */
125+ void requestState(Mir::State state);
126+
127+ /**
128+ * @brief Sends a close request
129+ *
130+ */
131+ void close();
132+
133+ /**
134+ * @brief Focuses and raises the window
135+ */
136+ void activate();
137+
138+Q_SIGNALS:
139+ void closeRequested();
140+ void emptyWindowActivated();
141+
142+ void positionChanged(QPoint position);
143+ void requestedPositionChanged(QPoint position);
144+ void stateChanged(Mir::State value);
145+ void focusedChanged(bool value);
146+ void confinesMousePointerChanged(bool value);
147+ void surfaceChanged(unity::shell::application::MirSurfaceInterface *surface);
148+ void allowClientResizeChanged(bool value);
149+ void liveChanged(bool value);
150+
151+ /**
152+ * @brief Emitted when focus for this window is requested by an external party
153+ */
154+ void focusRequested();
155+
156+private:
157+ void updatePosition();
158+ void updateState();
159+ void updateFocused();
160+
161+ QPoint m_position;
162+ QPoint m_requestedPosition;
163+ bool m_positionRequested{false};
164+ bool m_focused{false};
165+ int m_id;
166+ Mir::State m_state{Mir::RestoredState};
167+ bool m_stateRequested{false};
168+ unity::shell::application::MirSurfaceInterface *m_surface{nullptr};
169+
170+ bool m_allowClientResize{true};
171+};
172+
173+QDebug operator<<(QDebug dbg, const Window *window);
174+
175+Q_DECLARE_METATYPE(Window*)
176+#endif // UNITY_WINDOW_H
qml/Dash/ScopeSettings/ScopeSettingNumber.qml → plugins/WindowManager/WindowManagerGlobal.hrenamed+7−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,8 +14,10 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.4
17+#include <QtCore/QtGlobal>
18
18
19−ScopeSettingString {
19+#if defined(WINDOWMANAGERQML_LIBRARY)
20− mode: "number"
20+# define WINDOWMANAGERQML_EXPORT Q_DECL_EXPORT
21−}
21+#else
22+# define WINDOWMANAGERQML_EXPORT Q_DECL_IMPORT
23+#endif
plugins/WindowManager/WindowManagerPlugin.cpp+19−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2016 Canonical, Ltd.
2+ * Copyright (C) 2016-2017 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -16,13 +16,29 @@
16
16
17#include "WindowManagerPlugin.h"
17#include "WindowManagerPlugin.h"
18
18
19−#include "TopLevelSurfaceList.h"
19+#include "AvailableDesktopArea.h"
20+#include "TopLevelWindowModel.h"
21+#include "Window.h"
22+#include "WindowMargins.h"
23+#include "InputMethodManager.h"
20
24
21#include <QtQml>
25#include <QtQml>
22
26
27+QObject *inputMethodManager(QQmlEngine *engine, QJSEngine *scriptEngine)
28+{
29+ Q_UNUSED(engine)
30+ Q_UNUSED(scriptEngine)
31+ return InputMethodManager::instance();
32+}
33+
23void WindowManagerPlugin::registerTypes(const char *uri)
34void WindowManagerPlugin::registerTypes(const char *uri)
24{
35{
25− qmlRegisterType<TopLevelSurfaceList>(uri, 0, 1, "TopLevelSurfaceList");
36+ qmlRegisterType<AvailableDesktopArea>(uri, 1, 0, "AvailableDesktopArea");
37+ qmlRegisterType<TopLevelWindowModel>(uri, 1, 0, "TopLevelWindowModel");
38+ qmlRegisterType<WindowMargins>(uri, 1, 0, "WindowMargins");
39+ qmlRegisterSingletonType<InputMethodManager>(uri, 1, 0, "InputMethodManager", inputMethodManager);
40+
41+ qRegisterMetaType<Window*>("Window*");
26
42
27 qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*");
43 qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*");
28}
44}
plugins/WindowManager/WindowMargins.cppnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
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/>.
15+ */
16+
17+#include "WindowMargins.h"
18+
19+#include <QGuiApplication>
20+#include <QQuickWindow>
21+#include <qpa/qplatformnativeinterface.h>
22+
23+void WindowMargins::setNormal(QRectF value)
24+{
25+ if (m_normal == value) {
26+ return;
27+ }
28+
29+ m_normal = value;
30+
31+ if (window()) {
32+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
33+ nativeInterface->setWindowProperty(window()->handle(), "normalWindowMargins", QVariant(m_normal.toRect()));
34+ }
35+
36+ Q_EMIT normalChanged();
37+}
38+
39+QRectF WindowMargins::normal() const
40+{
41+ return m_normal;
42+}
43+
44+void WindowMargins::setDialog(QRectF value)
45+{
46+ if (m_dialog == value) {
47+ return;
48+ }
49+
50+ m_dialog = value;
51+
52+ if (window()) {
53+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
54+ nativeInterface->setWindowProperty(window()->handle(), "dialogWindowMargins", QVariant(m_dialog.toRect()));
55+ }
56+
57+ Q_EMIT dialogChanged();
58+}
59+
60+QRectF WindowMargins::dialog() const
61+{
62+ return m_dialog;
63+}
64+
65+void WindowMargins::itemChange(ItemChange change, const ItemChangeData &data)
66+{
67+ if (change == ItemSceneChange && data.window != nullptr) {
68+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
69+ if (!m_normal.isNull()) {
70+ nativeInterface->setWindowProperty(data.window->handle(), "normalWindowMargins", QVariant(m_normal.toRect()));
71+ nativeInterface->setWindowProperty(data.window->handle(), "dialogWindowMargins", QVariant(m_dialog.toRect()));
72+ }
73+ }
74+}
plugins/WindowManager/WindowMargins.hnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
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/>.
15+ */
16+
17+#ifndef WINDOWMARGINS_H
18+#define WINDOWMARGINS_H
19+
20+#include <QQuickItem>
21+#include <QRectF>
22+
23+/*
24+ * Specifies window margins for different Mir window types
25+ *
26+ * Used to inform MirAL so that it can take window management decisions that match
27+ * the visuals drawn by Unity.
28+ */
29+class WindowMargins : public QQuickItem
30+{
31+ Q_OBJECT
32+
33+ // Margins for windows of normal type
34+ Q_PROPERTY(QRectF normal READ normal WRITE setNormal NOTIFY normalChanged)
35+
36+ // Margins for windows of dialog type
37+ Q_PROPERTY(QRectF dialog READ dialog WRITE setDialog NOTIFY dialogChanged)
38+
39+ // TODO: Add margins for other window types as needed
40+
41+public:
42+ QRectF normal() const;
43+ void setNormal(QRectF value);
44+
45+ QRectF dialog() const;
46+ void setDialog(QRectF value);
47+
48+protected:
49+ void itemChange(ItemChange change, const ItemChangeData &value) override;
50+
51+Q_SIGNALS:
52+ void normalChanged();
53+ void dialogChanged();
54+private:
55+ QRectF m_normal;
56+ QRectF m_dialog;
57+};
58+
59+#endif // WINDOWMARGINS_H
plugins/Wizard/CMakeLists.txt+5−2
@@ -10,10 +10,13 @@ add_library(Wizard-qml MODULE
10 timezonemodel.cpp
10 timezonemodel.cpp
11 Status.cpp
11 Status.cpp
12 keyboardLayoutsModel.cpp
12 keyboardLayoutsModel.cpp
13+ Changelog.cpp
13)
14)
14
15
15−qt5_use_modules(Wizard-qml DBus Qml)
16+target_link_libraries(Wizard-qml
17+ Qt5::DBus Qt5::Qml
16−target_link_libraries(Wizard-qml ${GD3_LDFLAGS} ${GLIB_LDFLAGS} ${GEONAMES_LDFLAGS})
18+ ${GD3_LDFLAGS} ${GLIB_LDFLAGS} ${GEONAMES_LDFLAGS}
19+)
17add_unity8_plugin(Wizard 0.1 Wizard TARGETS Wizard-qml)
20add_unity8_plugin(Wizard 0.1 Wizard TARGETS Wizard-qml)
18
21
19set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
22set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
plugins/Wizard/Changelog.cppnew file+58
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (C) 2018 The UBports project
3+ * Written by: Marius Gripsgard <marius@ubports.com>
4+ *
5+ * This program is free software: you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 3, as published
7+ * by the Free Software Foundation.
8+ *
9+ * This program is distributed in the hope that it will be useful, but
10+ * WITHOUT ANY WARRANTY; without even the implied warranties of
11+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12+ * PURPOSE. See the GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License along
15+ * with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#include "Changelog.h"
19+
20+#include <QDir>
21+#include <QFile>
22+#include <QTextStream>
23+
24+Changelog::Changelog()
25+ : QObject()
26+{
27+ readChangelog();
28+ if(QFile::exists(changelogPath()))
29+ m_fsWatcher.addPath(changelogPath());
30+ connect(&m_fsWatcher, &QFileSystemWatcher::fileChanged, this, &Changelog::watcherFileChanged);
31+}
32+
33+QString Changelog::changelogPath()
34+{
35+ return "/usr/share/ubports/changelogs/current";
36+}
37+
38+QString Changelog::text() const
39+{
40+ return m_text;
41+}
42+
43+void Changelog::readChangelog()
44+{
45+ if(!QFile::exists(changelogPath()))
46+ return;
47+ QFile f(changelogPath());
48+ if (!f.open(QFile::ReadOnly | QFile::Text)) return;
49+ QTextStream in(&f);
50+ m_text = in.readAll();
51+ Q_EMIT textChanged();
52+}
53+
54+void Changelog::watcherFileChanged()
55+{
56+ readChangelog();
57+ m_fsWatcher.removePath(changelogPath());
58+}
plugins/Wizard/Changelog.hnew file+52
@@ -0,0 +1,52 @@
1+/*
2+ * Copyright (C) 2018 The UBports project
3+ * Written by: Marius Gripsgard <marius@ubports.com>
4+ *
5+ * This program is free software: you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 3, as published
7+ * by the Free Software Foundation.
8+ *
9+ * This program is distributed in the hope that it will be useful, but
10+ * WITHOUT ANY WARRANTY; without even the implied warranties of
11+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12+ * PURPOSE. See the GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License along
15+ * with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#ifndef WIZARD_CHANGELOG_H
19+#define WIZARD_CHANGELOG_H
20+
21+#include <QFileSystemWatcher>
22+#include <QObject>
23+#include <QString>
24+
25+class Changelog : public QObject
26+{
27+ Q_OBJECT
28+ Q_PROPERTY(QString text READ text NOTIFY textChanged)
29+
30+public:
31+ Changelog();
32+ ~Changelog() = default;
33+
34+ QString text() const;
35+
36+Q_SIGNALS:
37+ void textChanged();
38+
39+private Q_SLOTS:
40+ void watcherFileChanged();
41+
42+private:
43+ Q_DISABLE_COPY(Changelog)
44+
45+ static QString changelogPath();
46+ void readChangelog();
47+
48+ QFileSystemWatcher m_fsWatcher;
49+ QString m_text;
50+};
51+
52+#endif
plugins/Wizard/System.cpp+69−7
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2014-2016 Canonical Ltd.
3 * Copyright (C) 2014-2016 Canonical Ltd.
3 *
4 *
4 * This program is free software: you can redistribute it and/or modify it
5 * This program is free software: you can redistribute it and/or modify it
@@ -46,29 +47,93 @@ QString System::wizardEnabledPath()
46 return QDir::home().filePath(QStringLiteral(".config/ubuntu-system-settings/wizard-has-run"));
47 return QDir::home().filePath(QStringLiteral(".config/ubuntu-system-settings/wizard-has-run"));
47}
48}
48
49
50+QString System::currentFrameworkPath()
51+{
52+ QFileInfo f("/usr/share/click/frameworks/current");
53+ return f.canonicalFilePath();
54+}
55+
56+/*
57+wizardEnabled and isUpdate logic
58+
59+if wizard-has-run does NOT exist == is new install
60+if wizard-has-run exists but does NOT match current framework == is update
61+if wizard-has-run exists but does match current framework == show no wizard
62+*/
63+
64+bool System::wizardPathExists() {
65+ return QFile::exists(wizardEnabledPath());
66+}
67+
49bool System::wizardEnabled() const
68bool System::wizardEnabled() const
50{
69{
70+ if (!wizardPathExists()) {
71+ return true;
72+ }
73+ return isUpdate();
74+}
75+
76+QString System::readCurrentFramework()
77+{
78+ QFile f(currentFrameworkPath());
79+ if (!f.open(QFile::ReadOnly | QFile::Text)) return "";
80+ QTextStream in(&f);
81+ return in.readAll();
82+}
83+
84+QString System::readWizardEnabled()
85+{
51− return !QFile::exists(wizardEnabledPath());
86+ QFile f(wizardEnabledPath());
87+ if (!f.open(QFile::ReadOnly | QFile::Text)) return "";
88+ QTextStream in(&f);
89+ return in.readAll();
90+}
91+
92+QString System::version() const
93+{
94+ return readCurrentFramework();
95+}
96+
97+bool System::isUpdate() const
98+{
99+ if (!wizardPathExists()) {
100+ return false;
101+ }
102+
103+ return readCurrentFramework() != readWizardEnabled();
52}
104}
53
105
54void System::setWizardEnabled(bool enabled)
106void System::setWizardEnabled(bool enabled)
55{
107{
56− if (wizardEnabled() == enabled)
108+ if (wizardEnabled() == enabled && !isUpdate())
57 return;
109 return;
58
110
59 if (enabled) {
111 if (enabled) {
60 QFile::remove(wizardEnabledPath());
112 QFile::remove(wizardEnabledPath());
61 } else {
113 } else {
62 QDir(wizardEnabledPath()).mkpath(QStringLiteral(".."));
114 QDir(wizardEnabledPath()).mkpath(QStringLiteral(".."));
115+ if (QFile::exists(wizardEnabledPath())) {
116+ QFile::remove(wizardEnabledPath());
117+ }
118+ // For special cases check if wizardEnabledPath is a folder
119+ if (QDir(wizardEnabledPath()).exists()) {
120+ QDir(wizardEnabledPath()).removeRecursively();
121+ }
63− QFile(wizardEnabledPath()).open(QIODevice::WriteOnly);
122+ if (!QFile::copy(currentFrameworkPath(), wizardEnabledPath())) {
123+ // Make en empty file if framework does not exist
124+ QFile f(wizardEnabledPath());
125+ f.open(QFile::WriteOnly);
126+ }
64 m_fsWatcher.addPath(wizardEnabledPath());
127 m_fsWatcher.addPath(wizardEnabledPath());
65 Q_EMIT wizardEnabledChanged();
128 Q_EMIT wizardEnabledChanged();
129+ Q_EMIT isUpdateChanged();
66 }
130 }
67}
131}
68
132
69void System::watcherFileChanged()
133void System::watcherFileChanged()
70{
134{
71 Q_EMIT wizardEnabledChanged();
135 Q_EMIT wizardEnabledChanged();
136+ Q_EMIT isUpdateChanged();
72 m_fsWatcher.removePath(wizardEnabledPath());
137 m_fsWatcher.removePath(wizardEnabledPath());
73}
138}
74
139
@@ -103,13 +168,10 @@ void System::updateSessionLocale(const QString &locale)
103
168
104 // Restart bits of the session to pick up new language.
169 // Restart bits of the session to pick up new language.
105 QProcess::startDetached(QStringLiteral("sh -c \"initctl emit indicator-services-end; \
170 QProcess::startDetached(QStringLiteral("sh -c \"initctl emit indicator-services-end; \
106− initctl stop scope-registry; \
107− initctl stop smart-scopes-proxy; \
108 initctl emit --no-wait indicator-services-start; \
171 initctl emit --no-wait indicator-services-start; \
109 initctl restart --no-wait ubuntu-location-service-trust-stored; \
172 initctl restart --no-wait ubuntu-location-service-trust-stored; \
110 initctl restart --no-wait maliit-server; \
173 initctl restart --no-wait maliit-server; \
111− initctl restart --no-wait indicator-messages; \
174+ initctl restart --no-wait indicator-messages"));
112− initctl restart --no-wait unity8-dash\""));
113}
175}
114
176
115void System::skipUntilFinishedPage()
177void System::skipUntilFinishedPage()
plugins/Wizard/System.h+17
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2014-2016 Canonical Ltd.
3 * Copyright (C) 2014-2016 Canonical Ltd.
3 *
4 *
4 * This program is free software: you can redistribute it and/or modify it
5 * This program is free software: you can redistribute it and/or modify it
@@ -18,6 +19,7 @@
18#define WIZARD_SYSTEM_H
19#define WIZARD_SYSTEM_H
19
20
20#include <QFileSystemWatcher>
21#include <QFileSystemWatcher>
22+#include <QVersionNumber>
21#include <QObject>
23#include <QObject>
22#include <QString>
24#include <QString>
23
25
@@ -25,12 +27,21 @@ class System : public QObject
25{
27{
26 Q_OBJECT
28 Q_OBJECT
27 Q_PROPERTY(bool wizardEnabled READ wizardEnabled WRITE setWizardEnabled NOTIFY wizardEnabledChanged)
29 Q_PROPERTY(bool wizardEnabled READ wizardEnabled WRITE setWizardEnabled NOTIFY wizardEnabledChanged)
30+ Q_PROPERTY(QString version READ version NOTIFY versionChanged)
31+ Q_PROPERTY(bool isUpdate READ isUpdate NOTIFY isUpdateChanged)
28
32
29public:
33public:
30 System();
34 System();
31 ~System() = default;
35 ~System() = default;
32
36
37+ /**
38+ * Checks whether the wizard is enabled for first-run or otherwise.
39+ */
33 bool wizardEnabled() const;
40 bool wizardEnabled() const;
41+
42+ QString version() const;
43+ bool isUpdate() const;
44+
34 void setWizardEnabled(bool enabled);
45 void setWizardEnabled(bool enabled);
35
46
36public Q_SLOTS:
47public Q_SLOTS:
@@ -42,6 +53,8 @@ public Q_SLOTS:
42
53
43Q_SIGNALS:
54Q_SIGNALS:
44 void wizardEnabledChanged();
55 void wizardEnabledChanged();
56+ void versionChanged();
57+ void isUpdateChanged();
45
58
46private Q_SLOTS:
59private Q_SLOTS:
47 void watcherFileChanged();
60 void watcherFileChanged();
@@ -50,7 +63,11 @@ private:
50 Q_DISABLE_COPY(System)
63 Q_DISABLE_COPY(System)
51
64
52 static QString wizardEnabledPath();
65 static QString wizardEnabledPath();
66+ static QString currentFrameworkPath();
53 static void setSessionVariable(const QString &variable, const QString &value);
67 static void setSessionVariable(const QString &variable, const QString &value);
68+ static QString readCurrentFramework();
69+ static QString readWizardEnabled();
70+ static bool wizardPathExists();
54
71
55 QFileSystemWatcher m_fsWatcher;
72 QFileSystemWatcher m_fsWatcher;
56};
73};
plugins/Wizard/Utils/CMakeLists.txt+1−1
@@ -20,7 +20,7 @@ add_library(WizardUtils-qml SHARED
20# files directly in targets.
20# files directly in targets.
21set_target_properties(WizardUtils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
21set_target_properties(WizardUtils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
22
22
23−qt5_use_modules(WizardUtils-qml DBus Qml Quick)
23+target_link_libraries(WizardUtils-qml Qt5::DBus Qt5::Qml Qt5::Quick)
24
24
25set(PLUG_DIR ${PLUGIN_PRIVATE_MODULE_DIR}/Ubuntu/SystemSettings/Wizard/Utils)
25set(PLUG_DIR ${PLUGIN_PRIVATE_MODULE_DIR}/Ubuntu/SystemSettings/Wizard/Utils)
26install(FILES qmldir DESTINATION ${PLUG_DIR})
26install(FILES qmldir DESTINATION ${PLUG_DIR})
plugins/Wizard/plugin.cpp+2
@@ -20,6 +20,7 @@
20#include "timezonemodel.h"
20#include "timezonemodel.h"
21#include "LocalePlugin.h"
21#include "LocalePlugin.h"
22#include "Status.h"
22#include "Status.h"
23+#include "Changelog.h"
23#include "keyboardLayoutsModel.h"
24#include "keyboardLayoutsModel.h"
24
25
25#include <QtQml/qqml.h>
26#include <QtQml/qqml.h>
@@ -30,6 +31,7 @@ void WizardPlugin::registerTypes(const char *uri)
30 qmlRegisterType<PageList>(uri, 0, 1, "PageList");
31 qmlRegisterType<PageList>(uri, 0, 1, "PageList");
31 qmlRegisterSingletonType<System>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new System; });
32 qmlRegisterSingletonType<System>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new System; });
32 qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; });
33 qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; });
34+ qmlRegisterSingletonType<Changelog>(uri, 0, 1, "Changelog", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Changelog; });
33 qmlRegisterType<TimeZoneLocationModel>(uri, 0, 1, "TimeZoneModel");
35 qmlRegisterType<TimeZoneLocationModel>(uri, 0, 1, "TimeZoneModel");
34 qmlRegisterType<KeyboardLayoutsModel>(uri, 0, 1, "KeyboardLayoutsModel");
36 qmlRegisterType<KeyboardLayoutsModel>(uri, 0, 1, "KeyboardLayoutsModel");
35 qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin");
37 qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin");
plugins/Wizard/test.shdeleted−18
@@ -1,18 +0,0 @@
1−#!/bin/sh
2−
3−TOPDIR=$(readlink -e "$(dirname ${0})/..")
4−
5−LOCAL_PRIVATE_DIR=$(ls -d ${TOPDIR}/debian/tmp/usr/lib/*/ubuntu-system-settings/private)
6−if [ -n ${LOCAL_PRIVATE_DIR} ]; then
7− echo "Testing against locally built version"
8− export UBUNTU_SYSTEM_SETTINGS_WIZARD_ROOT="${TOPDIR}/debian/tmp/usr/share"
9− export UBUNTU_SYSTEM_SETTINGS_WIZARD_MODULES="${LOCAL_PRIVATE_DIR}"
10− export QML2_IMPORT_PATH=${LOCAL_PRIVATE_DIR}:${QML2_IMPORT_PATH}
11− export PATH=${TOPDIR}/debian/tmp/usr/bin:${PATH}
12−else
13− echo "Testing against system version"
14−fi
15−
16−export QML2_IMPORT_PATH=${TOPDIR}/tests/mocks:${QML2_IMPORT_PATH}
17−
18−exec system-settings-wizard
po/aa.po+263−319
@@ -6,24 +6,54 @@
6msgid ""
6msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9−"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9+"Report-Msgid-Bugs-To: \n"
10−"POT-Creation-Date: 2016-11-16 10:44+0000\n"
10+"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2016-02-12 10:18+0000\n"
11+"PO-Revision-Date: 2021-03-08 10:42+0000\n"
12−"Last-Translator: Charif AYFARAH <ayfarah@ymail.com>\n"
12+"Last-Translator: TigranKhachatryan0 <tigrank2008@gmail.com>\n"
13−"Language-Team: Afar <aa@li.org>\n"
13+"Language-Team: Afar <https://translate.ubports.com/projects/ubports/lomiri/"
14+"aa/>\n"
15+"Language: aa\n"
14"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
18"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
19"Plural-Forms: nplurals=2; plural=n != 1;\n"
20+"X-Generator: Weblate 3.11.3\n"
18"X-Launchpad-Export-Date: 2016-11-23 06:34+0000\n"
21"X-Launchpad-Export-Date: 2016-11-23 06:34+0000\n"
19−"X-Generator: Launchpad (build 18269)\n"
20
22
21−#: plugins/LightDM/Greeter.cpp:123
23+#: plugins/LightDM/Greeter.cpp:199
22msgid "Password: "
24msgid "Password: "
23−msgstr "Cuumita "
25+msgstr ""
26+
27+#: plugins/LightDM/Greeter.cpp:214
28+msgid "Username"
29+msgstr ""
30+
31+#: plugins/LightDM/Greeter.cpp:258
32+msgid "Failed to authenticate"
33+msgstr ""
34+
35+#: plugins/LightDM/Greeter.cpp:260
36+msgid "Invalid password, please try again"
37+msgstr ""
38+
39+#: plugins/LightDM/Greeter.cpp:270
40+msgid "Log In"
41+msgstr ""
24
42
43+#: plugins/LightDM/Greeter.cpp:270
44+msgid "Retry"
45+msgstr "Qagis"
46+
47+#: plugins/LightDM/UsersModel.cpp:157
48+msgid "Login"
49+msgstr ""
50+
51+#: plugins/LightDM/UsersModel.cpp:165
52+msgid "Guest Session"
53+msgstr ""
54+
25−#: plugins/Unity/Launcher/launcheritem.cpp:49
55+#: plugins/Unity/Launcher/launcheritem.cpp:48
26−#: plugins/Unity/Launcher/launcheritem.cpp:108
56+#: plugins/Unity/Launcher/launcheritem.cpp:122
27msgid "Pin shortcut"
57msgid "Pin shortcut"
28msgstr ""
58msgstr ""
29
59
@@ -31,80 +61,83 @@ msgstr ""
31msgid "Quit"
61msgid "Quit"
32msgstr "Kat"
62msgstr "Kat"
33
63
34−#: plugins/Unity/Launcher/launcheritem.cpp:108
64+#: plugins/Unity/Launcher/launcheritem.cpp:122
35msgid "Unpin shortcut"
65msgid "Unpin shortcut"
36msgstr ""
66msgstr ""
37
67
38−#: qml/Components/Dialogs.qml:149
68+#: qml/Components/Dialogs.qml:176
39msgctxt "Title: Lock/Log out dialog"
69msgctxt "Title: Lock/Log out dialog"
40msgid "Log out"
70msgid "Log out"
41msgstr "Eweq"
71msgstr "Eweq"
42
72
43−#: qml/Components/Dialogs.qml:150
73+#: qml/Components/Dialogs.qml:177
44msgid "Are you sure you want to log out?"
74msgid "Are you sure you want to log out?"
45msgstr ""
75msgstr ""
46
76
47−#: qml/Components/Dialogs.qml:152
77+#: qml/Components/Dialogs.qml:180
48msgctxt "Button: Lock the system"
78msgctxt "Button: Lock the system"
49msgid "Lock"
79msgid "Lock"
50msgstr "Koflis"
80msgstr "Koflis"
51
81
52−#: qml/Components/Dialogs.qml:159
82+#: qml/Components/Dialogs.qml:191
53msgctxt "Button: Log out from the system"
83msgctxt "Button: Log out from the system"
54msgid "Log Out"
84msgid "Log Out"
55msgstr "Eweq"
85msgstr "Eweq"
56
86
57−#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
87+#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:284
58−#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:232
59−#: qml/Wizard/Pages/passcode-confirm.qml:32
88+#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
60#: qml/Wizard/Pages/passcode-set.qml:32
89#: qml/Wizard/Pages/passcode-set.qml:32
61msgid "Cancel"
90msgid "Cancel"
62msgstr "Bayis"
91msgstr "Bayis"
63
92
64−#: qml/Components/Dialogs.qml:178
93+#: qml/Components/Dialogs.qml:211
65msgctxt "Title: Reboot dialog"
94msgctxt "Title: Reboot dialog"
66msgid "Reboot"
95msgid "Reboot"
67msgstr "Wadir-ugus"
96msgstr "Wadir-ugus"
68
97
69−#: qml/Components/Dialogs.qml:179
98+#: qml/Components/Dialogs.qml:212
70msgid "Are you sure you want to reboot?"
99msgid "Are you sure you want to reboot?"
71msgstr "Wadir-ugussuh digga maay litoo?"
100msgstr "Wadir-ugussuh digga maay litoo?"
72
101
73−#: qml/Components/Dialogs.qml:181
102+#: qml/Components/Dialogs.qml:215
74msgid "No"
103msgid "No"
75msgstr "Maleey"
104msgstr "Maleey"
76
105
77−#: qml/Components/Dialogs.qml:187
106+#: qml/Components/Dialogs.qml:223
78msgid "Yes"
107msgid "Yes"
79msgstr "Yeey"
108msgstr "Yeey"
80
109
81−#: qml/Components/Dialogs.qml:202
110+#: qml/Components/Dialogs.qml:243
82msgctxt "Title: Power off/Restart dialog"
111msgctxt "Title: Power off/Restart dialog"
83msgid "Power"
112msgid "Power"
84msgstr "Cayla"
113msgstr "Cayla"
85
114
86−#: qml/Components/Dialogs.qml:203
115+#: qml/Components/Dialogs.qml:244
87msgid ""
116msgid ""
88"Are you sure you would like\n"
117"Are you sure you would like\n"
89"to power off?"
118"to power off?"
90msgstr "Timmi haytuh digga maay litoo?"
119msgstr "Timmi haytuh digga maay litoo?"
91
120
92−#: qml/Components/Dialogs.qml:205
121+#: qml/Components/Dialogs.qml:248
93msgctxt "Button: Power off the system"
122msgctxt "Button: Power off the system"
94msgid "Power off"
123msgid "Power off"
95msgstr "Timmi hayis"
124msgstr "Timmi hayis"
96
125
97−#: qml/Components/Dialogs.qml:214
126+#: qml/Components/Dialogs.qml:263
98msgctxt "Button: Restart the system"
127msgctxt "Button: Restart the system"
99msgid "Restart"
128msgid "Restart"
100msgstr "Wadir-qimbis"
129msgstr "Wadir-qimbis"
101
130
131+#: qml/Components/Dialogs.qml:276
132+msgid "Screenshot"
133+msgstr ""
134+
102#: qml/Components/KeyboardShortcutsOverlay.qml:41
135#: qml/Components/KeyboardShortcutsOverlay.qml:41
103msgid "Keyboard Shortcuts"
136msgid "Keyboard Shortcuts"
104msgstr ""
137msgstr ""
105
138
106#: qml/Components/KeyboardShortcutsOverlay.qml:55
139#: qml/Components/KeyboardShortcutsOverlay.qml:55
107−msgid "Unity 8"
140+msgid "Lomiri"
108msgstr ""
141msgstr ""
109
142
110#: qml/Components/KeyboardShortcutsOverlay.qml:62
143#: qml/Components/KeyboardShortcutsOverlay.qml:62
@@ -120,7 +153,7 @@ msgid "Alt + PrtScr"
120msgstr ""
153msgstr ""
121
154
122#: qml/Components/KeyboardShortcutsOverlay.qml:80
155#: qml/Components/KeyboardShortcutsOverlay.qml:80
123−msgid "Takes a screenshot of a window."
156+msgid "Takes a screenshot of the current window."
124msgstr ""
157msgstr ""
125
158
126#: qml/Components/KeyboardShortcutsOverlay.qml:88
159#: qml/Components/KeyboardShortcutsOverlay.qml:88
@@ -139,127 +172,131 @@ msgstr ""
139msgid "Switches to previous keyboard layout."
172msgid "Switches to previous keyboard layout."
140msgstr ""
173msgstr ""
141
174
142−#: qml/Components/KeyboardShortcutsOverlay.qml:118
175+#: qml/Components/KeyboardShortcutsOverlay.qml:114
143−msgid "Launcher"
176+msgid "Ctrl + Alt + T"
144msgstr ""
177msgstr ""
145
178
146−#: qml/Components/KeyboardShortcutsOverlay.qml:125
179+#: qml/Components/KeyboardShortcutsOverlay.qml:119
147−msgid "Super (Hold)"
180+msgid "Starts the Terminal application."
148msgstr ""
181msgstr ""
149
182
150−#: qml/Components/KeyboardShortcutsOverlay.qml:130
183+#: qml/Components/KeyboardShortcutsOverlay.qml:131
151−msgid "Opens the launcher, displays shortcuts."
184+msgid "Launcher"
152msgstr ""
185msgstr ""
153
186
154#: qml/Components/KeyboardShortcutsOverlay.qml:138
187#: qml/Components/KeyboardShortcutsOverlay.qml:138
155−msgid "Alt + F1"
188+msgid "Super (Hold)"
156msgstr ""
189msgstr ""
157
190
158#: qml/Components/KeyboardShortcutsOverlay.qml:143
191#: qml/Components/KeyboardShortcutsOverlay.qml:143
159−msgid "Opens launcher keyboard navigation mode."
192+msgid "Opens the launcher, displays shortcuts."
160msgstr ""
193msgstr ""
161
194
162#: qml/Components/KeyboardShortcutsOverlay.qml:151
195#: qml/Components/KeyboardShortcutsOverlay.qml:151
163−msgid "Super + Tab"
196+msgid "Alt + F1"
164msgstr ""
197msgstr ""
165
198
166#: qml/Components/KeyboardShortcutsOverlay.qml:156
199#: qml/Components/KeyboardShortcutsOverlay.qml:156
167−msgid "Switches applications via the launcher."
200+msgid "Opens launcher keyboard navigation mode."
168msgstr ""
201msgstr ""
169
202
170#: qml/Components/KeyboardShortcutsOverlay.qml:164
203#: qml/Components/KeyboardShortcutsOverlay.qml:164
171−msgid "Super + 0 to 9"
204+msgid "Super + Tab"
172msgstr ""
205msgstr ""
173
206
174#: qml/Components/KeyboardShortcutsOverlay.qml:169
207#: qml/Components/KeyboardShortcutsOverlay.qml:169
175−msgid "Same as clicking on a launcher icon."
208+msgid "Switches applications via the launcher."
176msgstr ""
209msgstr ""
177
210
178−#: qml/Components/KeyboardShortcutsOverlay.qml:181
211+#: qml/Components/KeyboardShortcutsOverlay.qml:177
179−msgid "Scopes"
212+msgid "Super + 1 to 0"
180msgstr ""
213msgstr ""
181
214
182−#: qml/Components/KeyboardShortcutsOverlay.qml:188
215+#: qml/Components/KeyboardShortcutsOverlay.qml:182
183−msgid "Super (Tap)"
216+msgid "Same as clicking on a launcher icon."
184msgstr ""
217msgstr ""
185
218
186−#: qml/Components/KeyboardShortcutsOverlay.qml:193
219+#: qml/Components/KeyboardShortcutsOverlay.qml:190
187−msgid "Opens the Scopes home."
220+msgid "Super + A"
188msgstr ""
221msgstr ""
189
222
190−#: qml/Components/KeyboardShortcutsOverlay.qml:209
223+#: qml/Components/KeyboardShortcutsOverlay.qml:195
224+msgid "Opens the Application Drawer."
225+msgstr ""
226+
227+#: qml/Components/KeyboardShortcutsOverlay.qml:235
191msgid "Switching"
228msgid "Switching"
192msgstr ""
229msgstr ""
193
230
194−#: qml/Components/KeyboardShortcutsOverlay.qml:216
231+#: qml/Components/KeyboardShortcutsOverlay.qml:242
195msgid "Alt + Tab"
232msgid "Alt + Tab"
196msgstr ""
233msgstr ""
197
234
198−#: qml/Components/KeyboardShortcutsOverlay.qml:221
235+#: qml/Components/KeyboardShortcutsOverlay.qml:247
199msgid "Switches between applications."
236msgid "Switches between applications."
200msgstr ""
237msgstr ""
201
238
202−#: qml/Components/KeyboardShortcutsOverlay.qml:229
239+#: qml/Components/KeyboardShortcutsOverlay.qml:255
203msgid "Super + W"
240msgid "Super + W"
204msgstr ""
241msgstr ""
205
242
206−#: qml/Components/KeyboardShortcutsOverlay.qml:234
243+#: qml/Components/KeyboardShortcutsOverlay.qml:260
207msgid "Opens the desktop spread."
244msgid "Opens the desktop spread."
208msgstr ""
245msgstr ""
209
246
210−#: qml/Components/KeyboardShortcutsOverlay.qml:242
247+#: qml/Components/KeyboardShortcutsOverlay.qml:268
211msgid "Cursor Left or Right"
248msgid "Cursor Left or Right"
212msgstr ""
249msgstr ""
213
250
214−#: qml/Components/KeyboardShortcutsOverlay.qml:247
251+#: qml/Components/KeyboardShortcutsOverlay.qml:273
215msgid "Moves the focus."
252msgid "Moves the focus."
216msgstr ""
253msgstr ""
217
254
218−#: qml/Components/KeyboardShortcutsOverlay.qml:259
255+#: qml/Components/KeyboardShortcutsOverlay.qml:285
219msgid "Windows"
256msgid "Windows"
220msgstr ""
257msgstr ""
221
258
222−#: qml/Components/KeyboardShortcutsOverlay.qml:266
259+#: qml/Components/KeyboardShortcutsOverlay.qml:292
223msgid "Ctrl + Super + D"
260msgid "Ctrl + Super + D"
224msgstr ""
261msgstr ""
225
262
226−#: qml/Components/KeyboardShortcutsOverlay.qml:271
263+#: qml/Components/KeyboardShortcutsOverlay.qml:297
227msgid "Minimizes all windows."
264msgid "Minimizes all windows."
228msgstr ""
265msgstr ""
229
266
230−#: qml/Components/KeyboardShortcutsOverlay.qml:279
267+#: qml/Components/KeyboardShortcutsOverlay.qml:305
231msgid "Ctrl + Super + Up"
268msgid "Ctrl + Super + Up"
232msgstr ""
269msgstr ""
233
270
234−#: qml/Components/KeyboardShortcutsOverlay.qml:284
271+#: qml/Components/KeyboardShortcutsOverlay.qml:310
235msgid "Maximizes the current window."
272msgid "Maximizes the current window."
236msgstr ""
273msgstr ""
237
274
238−#: qml/Components/KeyboardShortcutsOverlay.qml:292
275+#: qml/Components/KeyboardShortcutsOverlay.qml:318
239msgid "Ctrl + Super + Down"
276msgid "Ctrl + Super + Down"
240msgstr ""
277msgstr ""
241
278
242−#: qml/Components/KeyboardShortcutsOverlay.qml:297
279+#: qml/Components/KeyboardShortcutsOverlay.qml:323
243msgid "Minimizes or restores the current window."
280msgid "Minimizes or restores the current window."
244msgstr ""
281msgstr ""
245
282
246−#: qml/Components/KeyboardShortcutsOverlay.qml:305
283+#: qml/Components/KeyboardShortcutsOverlay.qml:331
247msgid "Ctrl + Super + Left or Right"
284msgid "Ctrl + Super + Left or Right"
248msgstr ""
285msgstr ""
249
286
250−#: qml/Components/KeyboardShortcutsOverlay.qml:310
287+#: qml/Components/KeyboardShortcutsOverlay.qml:336
251msgid "Semi-maximizes the current window."
288msgid "Semi-maximizes the current window."
252msgstr ""
289msgstr ""
253
290
254−#: qml/Components/KeyboardShortcutsOverlay.qml:318
291+#: qml/Components/KeyboardShortcutsOverlay.qml:344
255msgid "Alt + F4"
292msgid "Alt + F4"
256msgstr ""
293msgstr ""
257
294
258−#: qml/Components/KeyboardShortcutsOverlay.qml:323
295+#: qml/Components/KeyboardShortcutsOverlay.qml:349
259msgid "Closes the current window."
296msgid "Closes the current window."
260msgstr ""
297msgstr ""
261
298
262−#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252
299+#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:235
263msgid "Return to Call"
300msgid "Return to Call"
264msgstr "Seecol gacis"
301msgstr "Seecol gacis"
265
302
@@ -267,7 +304,7 @@ msgstr "Seecol gacis"
267msgid "Emergency Call"
304msgid "Emergency Call"
268msgstr ""
305msgstr ""
269
306
270−#: qml/Components/Lockscreen.qml:244
307+#: qml/Components/Lockscreen.qml:245
271msgid "OK"
308msgid "OK"
272msgstr "Hayya"
309msgstr "Hayya"
273
310
@@ -281,29 +318,29 @@ msgstr ""
281msgid "%1:%2"
318msgid "%1:%2"
282msgstr ""
319msgstr ""
283
320
284−#: qml/Components/ModeSwitchWarningDialog.qml:32
321+#: qml/Components/ModeSwitchWarningDialog.qml:33
285msgid "Apps may have unsaved data:"
322msgid "Apps may have unsaved data:"
286msgstr ""
323msgstr ""
287
324
288−#: qml/Components/ModeSwitchWarningDialog.qml:57
325+#: qml/Components/ModeSwitchWarningDialog.qml:60
289msgctxt ""
326msgctxt ""
290"Re-dock means connect the device again to an external screen/mouse/keyboard"
327"Re-dock means connect the device again to an external screen/mouse/keyboard"
291msgid "Re-dock, save your work and close these apps to continue."
328msgid "Re-dock, save your work and close these apps to continue."
292msgstr ""
329msgstr ""
293
330
294−#: qml/Components/ModeSwitchWarningDialog.qml:63
331+#: qml/Components/ModeSwitchWarningDialog.qml:67
295msgid "Or force close now (unsaved data will be lost)."
332msgid "Or force close now (unsaved data will be lost)."
296msgstr ""
333msgstr ""
297
334
298−#: qml/Components/ModeSwitchWarningDialog.qml:75
335+#: qml/Components/ModeSwitchWarningDialog.qml:80
299msgid "OK, I will reconnect"
336msgid "OK, I will reconnect"
300msgstr ""
337msgstr ""
301
338
302−#: qml/Components/ModeSwitchWarningDialog.qml:76
339+#: qml/Components/ModeSwitchWarningDialog.qml:81
303msgid "Reconnect now!"
340msgid "Reconnect now!"
304msgstr ""
341msgstr ""
305
342
306−#: qml/Components/ModeSwitchWarningDialog.qml:88
343+#: qml/Components/ModeSwitchWarningDialog.qml:94
307msgid "Close all"
344msgid "Close all"
308msgstr "Inkih alif"
345msgstr "Inkih alif"
309
346
@@ -311,107 +348,26 @@ msgstr "Inkih alif"
311msgid "Preview Share Item"
348msgid "Preview Share Item"
312msgstr ""
349msgstr ""
313
350
314−#: qml/Dash/DashPageHeader.qml:361
351+#: qml/Components/VirtualTouchPad.qml:317
315−msgctxt "Button: Open the Ubuntu Store"
316−msgid "Store"
352+msgid ""
317−msgstr ""
353+"Your device is now connected to an external display. Use this screen as a "
318−
354+"touch pad to interact with the pointer."
319−#: qml/Dash/DashPageHeader.qml:368
320−msgctxt "Button: Start a search in the current dash scope"
321−msgid "Search"
322−msgstr "Goranto"
323−
324−#: qml/Dash/DashPageHeader.qml:378
325−msgctxt "Button: Show the current dash scope settings"
326−msgid "Settings"
327−msgstr "Massosu"
328−
329−#: qml/Dash/DashPageHeader.qml:385
330−msgid "Remove from Favorites"
331−msgstr ""
332−
333−#: qml/Dash/DashPageHeader.qml:385
334−msgid "Add to Favorites"
335−msgstr ""
336−
337−#: qml/Dash/FiltersPopover.qml:61
338−msgid "Refine your results"
339−msgstr ""
340−
341−#: qml/Dash/FiltersPopover.qml:70
342−msgid "Reset"
343−msgstr ""
344−
345−#: qml/Dash/GenericScopeView.qml:545 qml/Dash/GenericScopeView.qml:716
346−msgid "Show less"
347−msgstr ""
348−
349−#: qml/Dash/GenericScopeView.qml:545
350−msgid "Show all"
351−msgstr ""
352−
353−#: qml/Dash/GenericScopeView.qml:576
354−msgctxt "Label: Hint for dash search line edit"
355−msgid "Search"
356−msgstr "Goranto"
357−
358−#: qml/Dash/PageHeaderExtraPanel.qml:57
359−msgid "Recent Searches"
360−msgstr ""
361−
362−#: qml/Dash/PageHeaderExtraPanel.qml:68
363−msgid "Clear All"
364−msgstr ""
365−
366−#: qml/Dash/Previews/PreviewActionCombo.qml:36
367−msgid "More..."
368−msgstr ""
369−
370−#: qml/Dash/Previews/PreviewActionCombo.qml:36
371−msgid "Less..."
372−msgstr ""
373−
374−#: qml/Dash/Previews/PreviewCommentInput.qml:83
375−#: qml/Dash/Previews/PreviewRatingInput.qml:202
376−msgid "Send"
377−msgstr "Rub"
378−
379−#: qml/Dash/Previews/PreviewRatingInput.qml:109
380−msgid "Rate this"
381−msgstr ""
382−
383−#: qml/Dash/Previews/PreviewRatingInput.qml:184
384−msgid "Add a review"
385−msgstr ""
386−
387−#: qml/Dash/PullToRefreshScopeStyle.qml:56
388−msgid "Pull to refresh…"
389−msgstr ""
390−
391−#: qml/Dash/PullToRefreshScopeStyle.qml:61
392−msgid "Release to refresh…"
393msgstr ""
355msgstr ""
394
356
395−#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
357+#: qml/Components/VirtualTouchPad.qml:327
396−msgid "Enable location data"
358+msgid "Tap left button to click."
397msgstr ""
359msgstr ""
398
360
399−#: qml/Dash/ScopesList.qml:57
361+#: qml/Components/VirtualTouchPad.qml:339
400−msgid "Manage"
362+msgid "Tap right button to right click."
401msgstr ""
363msgstr ""
402
364
403−#: qml/Dash/ScopesList.qml:104
365+#: qml/Components/VirtualTouchPad.qml:351
404−msgid "Home"
366+msgid "Swipe with two fingers to scroll."
405−msgstr "Dabqi"
406−
407−#: qml/Dash/ScopesList.qml:105
408−msgid "Also installed"
409msgstr ""
367msgstr ""
410
368
411−#: qml/DisabledScreenNotice.qml:109
369+#: qml/Components/VirtualTouchPad.qml:396
412−msgid ""
370+msgid "Find more settings in the system settings."
413−"Your device is now connected to an external display. Use this screen as a "
414−"touch pad to interact with the pointer."
415msgstr ""
371msgstr ""
416
372
417#: qml/Greeter/CoverPage.qml:127
373#: qml/Greeter/CoverPage.qml:127
@@ -437,32 +393,51 @@ msgid_plural "Please wait %1 minutes and then try again…"
437msgstr[0] "Maganak %1 minit qambaalaay, kaadu gabbat"
393msgstr[0] "Maganak %1 minit qambaalaay, kaadu gabbat"
438msgstr[1] "Maganak %1 minit qambaalaay, kaadu gabbat"
394msgstr[1] "Maganak %1 minit qambaalaay, kaadu gabbat"
439
395
440−#: qml/Greeter/Greeter.qml:596
396+#: qml/Greeter/Greeter.qml:550
441msgid "Try again"
397msgid "Try again"
442msgstr ""
398msgstr ""
443
399
444−#: qml/Greeter/LoginList.qml:70
400+#: qml/Greeter/Greeter.qml:551
401+#, fuzzy
402+#| msgid "Enter passphrase"
403+msgid "Enter passphrase to unlock"
404+msgstr "Tatur-maxco culus"
405+
406+#: qml/Greeter/Greeter.qml:552
407+#, fuzzy
408+#| msgid "Enter passcode"
409+msgid "Enter passcode to unlock"
410+msgstr "Tatur-mudenta culus"
411+
412+#: qml/Greeter/NarrowView.qml:235
413+msgid "Emergency"
414+msgstr ""
415+
416+#: qml/Greeter/PromptList.qml:126
445msgid "Passphrase"
417msgid "Passphrase"
446msgstr ""
418msgstr ""
447
419
448−#: qml/Greeter/LoginList.qml:71
420+#: qml/Greeter/PromptList.qml:126
449msgid "Passcode"
421msgid "Passcode"
450msgstr ""
422msgstr ""
451
423
452−#: qml/Greeter/LoginList.qml:98
424+#: qml/Greeter/SessionsList.qml:124
453−msgid "Retry"
425+msgid "Select desktop environment"
454−msgstr "Qagis"
455−
456−#: qml/Greeter/LoginList.qml:99
457−msgid "Log In"
458msgstr ""
426msgstr ""
459
427
460−#: qml/Greeter/NarrowView.qml:252
428+#: qml/Launcher/Drawer.qml:250
461−msgid "Emergency"
429+#, fuzzy
430+#| msgctxt "Button: Start a search in the current dash scope"
431+#| msgid "Search"
432+msgid "Search…"
433+msgstr "Goranto"
434+
435+#: qml/Launcher/PullToRefreshScopeStyle.qml:56
436+msgid "Pull to refresh…"
462msgstr ""
437msgstr ""
463
438
464−#: qml/Greeter/SessionsList.qml:122
439+#: qml/Launcher/PullToRefreshScopeStyle.qml:61
465−msgid "Select desktop environment"
440+msgid "Release to refresh…"
466msgstr ""
441msgstr ""
467
442
468#: qml/Notifications/NotificationMenuItemFactory.qml:124
443#: qml/Notifications/NotificationMenuItemFactory.qml:124
@@ -477,62 +452,62 @@ msgstr ""
477msgid "Conference"
452msgid "Conference"
478msgstr "Koboxu"
453msgstr "Koboxu"
479
454
480−#: qml/Panel/Indicators/MenuItemFactory.qml:847
455+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:869
481msgid "Nothing is playing"
456msgid "Nothing is playing"
482msgstr ""
457msgstr ""
483
458
484−#: qml/Panel/Indicators/MenuItemFactory.qml:975
459+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:997
485#, qt-format
460#, qt-format
486msgid "%1 hour"
461msgid "%1 hour"
487msgid_plural "%1 hours"
462msgid_plural "%1 hours"
488msgstr[0] "%1 saaqata"
463msgstr[0] "%1 saaqata"
489msgstr[1] "%1 saaqata"
464msgstr[1] "%1 saaqata"
490
465
491−#: qml/Panel/Indicators/MenuItemFactory.qml:979
466+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
492#, qt-format
467#, qt-format
493msgid "%1 minute"
468msgid "%1 minute"
494msgid_plural "%1 minutes"
469msgid_plural "%1 minutes"
495msgstr[0] "%1 miniti"
470msgstr[0] "%1 miniti"
496msgstr[1] "%1 miniti"
471msgstr[1] "%1 miniti"
497
472
498−#: qml/Panel/Indicators/MenuItemFactory.qml:984
473+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1006
499#, qt-format
474#, qt-format
500msgid "%1 second"
475msgid "%1 second"
501msgid_plural "%1 seconds"
476msgid_plural "%1 seconds"
502msgstr[0] "%1 nammayye"
477msgstr[0] "%1 nammayye"
503msgstr[1] "%1 nammayye"
478msgstr[1] "%1 nammayye"
504
479
505−#: qml/Panel/Indicators/MenuItemFactory.qml:987
480+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1009
506msgid "0 seconds"
481msgid "0 seconds"
507msgstr "0 nammayye"
482msgstr "0 nammayye"
508
483
509#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
484#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
510−#: qml/Panel/Indicators/MenuItemFactory.qml:989
485+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1011
511#, qt-format
486#, qt-format
512msgid "%1 remaining"
487msgid "%1 remaining"
513msgstr "%1 raaqe"
488msgstr "%1 raaqe"
514
489
515−#: qml/Panel/Indicators/MenuItemFactory.qml:995
490+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1017
516msgid "In queue…"
491msgid "In queue…"
517msgstr ""
492msgstr ""
518
493
519−#: qml/Panel/Indicators/MenuItemFactory.qml:999
494+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1021
520msgid "Downloading"
495msgid "Downloading"
521msgstr "Qulluumah"
496msgstr "Qulluumah"
522
497
523−#: qml/Panel/Indicators/MenuItemFactory.qml:1001
498+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1023
524msgid "Paused, tap to resume"
499msgid "Paused, tap to resume"
525msgstr ""
500msgstr ""
526
501
527−#: qml/Panel/Indicators/MenuItemFactory.qml:1003
502+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1025
528msgid "Canceled"
503msgid "Canceled"
529msgstr "Bayteh"
504msgstr "Bayteh"
530
505
531−#: qml/Panel/Indicators/MenuItemFactory.qml:1005
506+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1027
532msgid "Finished"
507msgid "Finished"
533msgstr "Duudumteh"
508msgstr "Duudumteh"
534
509
535−#: qml/Panel/Indicators/MenuItemFactory.qml:1007
510+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1029
536msgid "Failed, tap to retry"
511msgid "Failed, tap to retry"
537msgstr ""
512msgstr ""
538
513
@@ -552,23 +527,34 @@ msgctxt "Button: Call back on phone"
552msgid "Call back"
527msgid "Call back"
553msgstr ""
528msgstr ""
554
529
530+#: qml/Panel/PanelMenuPage.qml:92
531+#, fuzzy
532+#| msgctxt "Button: Go back one page in the Wizard"
533+#| msgid "Back"
534+msgid "Back"
535+msgstr "Derre"
536+
555#: qml/Stage/SideStage.qml:76
537#: qml/Stage/SideStage.qml:76
556msgid "Drag using 3 fingers any application from one window to the other"
538msgid "Drag using 3 fingers any application from one window to the other"
557msgstr ""
539msgstr ""
558
540
541+#: qml/Stage/Stage.qml:671
542+msgid "No running apps"
543+msgstr ""
544+
559#: qml/Tutorial/TutorialLeftLong.qml:49
545#: qml/Tutorial/TutorialLeftLong.qml:49
560−msgid "Long swipe from the left edge to open the Today scope"
546+msgid "Long swipe from the left edge to open the Application Drawer"
561msgstr ""
547msgstr ""
562
548
563#: qml/Tutorial/TutorialLeft.qml:47
549#: qml/Tutorial/TutorialLeft.qml:47
564msgid "Short swipe from the left edge to open the launcher"
550msgid "Short swipe from the left edge to open the launcher"
565msgstr ""
551msgstr ""
566
552
567−#: qml/Tutorial/TutorialRight.qml:55
553+#: qml/Tutorial/TutorialRight.qml:54
568msgid "Push your mouse against the right edge to view your open apps"
554msgid "Push your mouse against the right edge to view your open apps"
569msgstr ""
555msgstr ""
570
556
571−#: qml/Tutorial/TutorialRight.qml:56
557+#: qml/Tutorial/TutorialRight.qml:55
572msgid "Swipe from the right edge to view your open apps"
558msgid "Swipe from the right edge to view your open apps"
573msgstr ""
559msgstr ""
574
560
@@ -576,25 +562,41 @@ msgstr ""
576msgid "Swipe from the top edge to access notifications and quick settings"
562msgid "Swipe from the top edge to access notifications and quick settings"
577msgstr ""
563msgstr ""
578
564
579−#: qml/Wizard/Page.qml:54
565+#: qml/Wizard/Page.qml:59
580msgctxt "Button: Go back one page in the Wizard"
566msgctxt "Button: Go back one page in the Wizard"
581msgid "Back"
567msgid "Back"
582msgstr "Derre"
568msgstr "Derre"
583
569
584−#: qml/Wizard/Pages/10-welcome.qml:28
570+#: qml/Wizard/Pages/10-welcome.qml:29
585msgid "Language"
571msgid "Language"
586msgstr ""
572msgstr ""
587
573
588−#: qml/Wizard/Pages/10-welcome.qml:173 qml/Wizard/Pages/20-keyboard.qml:152
574+#: qml/Wizard/Pages/10-welcome.qml:176
589−#: qml/Wizard/Pages/30-wifi.qml:207 qml/Wizard/Pages/40-location.qml:271
575+#: qml/Wizard/Pages/10-welcome-update.qml:126
576+#: qml/Wizard/Pages/11-changelog.qml:74 qml/Wizard/Pages/20-keyboard.qml:152
590−#: qml/Wizard/Pages/50-timezone.qml:271 qml/Wizard/Pages/60-account.qml:65
577+#: qml/Wizard/Pages/30-wifi.qml:214 qml/Wizard/Pages/50-timezone.qml:272
591−#: qml/Wizard/Pages/70-passwd-type.qml:145
578+#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/70-passwd-type.qml:146
592−#: qml/Wizard/Pages/75-report-check.qml:84
593−#: qml/Wizard/Pages/passcode-desktop.qml:142
579+#: qml/Wizard/Pages/passcode-desktop.qml:154
594−#: qml/Wizard/Pages/password-set.qml:142
580+#: qml/Wizard/Pages/password-set.qml:153
595msgid "Next"
581msgid "Next"
596msgstr ""
582msgstr ""
597
583
584+#: qml/Wizard/Pages/10-welcome-update.qml:91
585+msgid "Welcome to "
586+msgstr ""
587+
588+#: qml/Wizard/Pages/10-welcome-update.qml:106
589+msgid "We will make sure your device is ready to use "
590+msgstr ""
591+
592+#: qml/Wizard/Pages/11-changelog.qml:28
593+msgid "What's new"
594+msgstr ""
595+
596+#: qml/Wizard/Pages/11-changelog.qml:74
597+msgid "Loading..."
598+msgstr ""
599+
598#: qml/Wizard/Pages/20-keyboard.qml:31
600#: qml/Wizard/Pages/20-keyboard.qml:31
599msgid "Select Keyboard"
601msgid "Select Keyboard"
600msgstr ""
602msgstr ""
@@ -607,8 +609,8 @@ msgstr ""
607msgid "Keyboard layout"
609msgid "Keyboard layout"
608msgstr ""
610msgstr ""
609
611
610−#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:207
612+#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:214
611−#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/77-system-update.qml:152
613+#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/79-system-update.qml:144
612#: qml/Wizard/Pages/sim.qml:101
614#: qml/Wizard/Pages/sim.qml:101
613msgid "Skip"
615msgid "Skip"
614msgstr "Kaqit"
616msgstr "Kaqit"
@@ -617,55 +619,23 @@ msgstr "Kaqit"
617msgid "Connect to Wi‑Fi"
619msgid "Connect to Wi‑Fi"
618msgstr ""
620msgstr ""
619
621
620−#: qml/Wizard/Pages/30-wifi.qml:132
622+#: qml/Wizard/Pages/30-wifi.qml:139
621msgid "Connected"
623msgid "Connected"
622msgstr ""
624msgstr ""
623
625
624−#: qml/Wizard/Pages/30-wifi.qml:165
626+#: qml/Wizard/Pages/30-wifi.qml:172
625msgid "Available Wi-Fi networks"
627msgid "Available Wi-Fi networks"
626msgstr ""
628msgstr ""
627
629
628−#: qml/Wizard/Pages/30-wifi.qml:166
630+#: qml/Wizard/Pages/30-wifi.qml:173
629msgid "No available Wi-Fi networks"
631msgid "No available Wi-Fi networks"
630msgstr ""
632msgstr ""
631
633
632−#: qml/Wizard/Pages/40-location.qml:27
633−msgid "Location Services"
634−msgstr ""
635−
636−#: qml/Wizard/Pages/40-location.qml:83
637−msgid ""
638−"Use GPS, Wi-Fi hotspots and mobile network anonymously to detect location "
639−"(recommended)"
640−msgstr ""
641−
642−#: qml/Wizard/Pages/40-location.qml:105
643−#, qt-format
644−msgid "By selecting this option you agree to the Nokia HERE %1."
645−msgstr ""
646−
647−#: qml/Wizard/Pages/40-location.qml:106
648−msgctxt "part of: Nokia HERE terms and conditions"
649−msgid "terms and conditions"
650−msgstr ""
651−
652−#: qml/Wizard/Pages/40-location.qml:160
653−msgid "GPS only"
654−msgstr ""
655−
656−#: qml/Wizard/Pages/40-location.qml:214
657−msgid "Don't use my location"
658−msgstr ""
659−
660−#: qml/Wizard/Pages/40-location.qml:260
661−msgid "You can change it later in System Settings."
662−msgstr ""
663−
664#: qml/Wizard/Pages/50-timezone.qml:30
634#: qml/Wizard/Pages/50-timezone.qml:30
665msgid "Time Zone"
635msgid "Time Zone"
666msgstr ""
636msgstr ""
667
637
668−#: qml/Wizard/Pages/50-timezone.qml:182
638+#: qml/Wizard/Pages/50-timezone.qml:183
669msgid "Enter your city"
639msgid "Enter your city"
670msgstr ""
640msgstr ""
671
641
@@ -673,7 +643,7 @@ msgstr ""
673msgid "Personalize Your Device"
643msgid "Personalize Your Device"
674msgstr ""
644msgstr ""
675
645
676−#: qml/Wizard/Pages/60-account.qml:48
646+#: qml/Wizard/Pages/60-account.qml:54
677msgid "Preferred Name"
647msgid "Preferred Name"
678msgstr ""
648msgstr ""
679
649
@@ -681,110 +651,72 @@ msgstr ""
681msgid "Lock Screen"
651msgid "Lock Screen"
682msgstr ""
652msgstr ""
683
653
684−#: qml/Wizard/Pages/70-passwd-type.qml:102
654+#: qml/Wizard/Pages/70-passwd-type.qml:103
685msgctxt "Label: Type of security method"
655msgctxt "Label: Type of security method"
686msgid "Create new password"
656msgid "Create new password"
687msgstr ""
657msgstr ""
688
658
689−#: qml/Wizard/Pages/70-passwd-type.qml:104
659+#: qml/Wizard/Pages/70-passwd-type.qml:105
690msgctxt "Label: Type of security method"
660msgctxt "Label: Type of security method"
691msgid "Create passcode (numbers only)"
661msgid "Create passcode (numbers only)"
692msgstr ""
662msgstr ""
693
663
694−#: qml/Wizard/Pages/70-passwd-type.qml:106
664+#: qml/Wizard/Pages/70-passwd-type.qml:107
695msgctxt "Label: Type of security method"
665msgctxt "Label: Type of security method"
696msgid "No lock code"
666msgid "No lock code"
697msgstr ""
667msgstr ""
698
668
699−#: qml/Wizard/Pages/75-report-check.qml:26 qml/Wizard/Pages/here-terms.qml:108
700−msgid "Privacy Policy"
701−msgstr ""
702−
703−#: qml/Wizard/Pages/75-report-check.qml:26
704−msgid "Help Us Improve"
705−msgstr ""
706−
707−#: qml/Wizard/Pages/75-report-check.qml:59
708−msgid "Improve system performance by sending us crashes and error reports."
709−msgstr ""
710−
711−#: qml/Wizard/Pages/75-report-check.qml:60
712−msgid "Privacy policy"
713−msgstr ""
714−
715−#: qml/Wizard/Pages/77-system-update.qml:28
669+#: qml/Wizard/Pages/79-system-update.qml:28
716msgid "Update Device"
670msgid "Update Device"
717msgstr ""
671msgstr ""
718
672
719−#: qml/Wizard/Pages/77-system-update.qml:55
673+#: qml/Wizard/Pages/79-system-update.qml:55
720msgid ""
674msgid ""
721"There is a system update available and ready to install. Afterwards, the "
675"There is a system update available and ready to install. Afterwards, the "
722"device will automatically restart."
676"device will automatically restart."
723msgstr ""
677msgstr ""
724
678
725−#: qml/Wizard/Pages/77-system-update.qml:76
679+#: qml/Wizard/Pages/79-system-update.qml:76
726msgctxt "string identifying name of the update"
680msgctxt "string identifying name of the update"
727−msgid "Ubuntu system"
681+msgid "Ubuntu Touch system"
728msgstr ""
682msgstr ""
729
683
730−#: qml/Wizard/Pages/77-system-update.qml:83
684+#: qml/Wizard/Pages/79-system-update.qml:83
731#, qt-format
685#, qt-format
732msgctxt "version of the system update"
686msgctxt "version of the system update"
733msgid "Version %1"
687msgid "Version %1"
734msgstr ""
688msgstr ""
735
689
736−#: qml/Wizard/Pages/77-system-update.qml:102
690+#: qml/Wizard/Pages/79-system-update.qml:95
737msgid "This could take a few minutes..."
691msgid "This could take a few minutes..."
738msgstr ""
692msgstr ""
739
693
740−#: qml/Wizard/Pages/77-system-update.qml:116
694+#: qml/Wizard/Pages/79-system-update.qml:109
741msgid "Install and restart now"
695msgid "Install and restart now"
742msgstr ""
696msgstr ""
743
697
744−#: qml/Wizard/Pages/80-finished.qml:89
698+#: qml/Wizard/Pages/80-finished.qml:91
745−msgid "Welcome to Ubuntu"
699+msgid "Welcome Back"
746msgstr ""
700msgstr ""
747
701
748−#: qml/Wizard/Pages/80-finished.qml:104
702+#: qml/Wizard/Pages/80-finished.qml:91
749−msgid "You are ready to use your device now"
703+msgid "Welcome to Ubuntu"
750−msgstr ""
751−
752−#: qml/Wizard/Pages/80-finished.qml:124
753−msgid "Get Started"
754−msgstr ""
755−
756−#: qml/Wizard/Pages/here-terms.qml:25
757−msgid "Terms & Conditions"
758−msgstr ""
759−
760−#: qml/Wizard/Pages/here-terms.qml:69
761−msgid "Your device uses positioning technologies provided by HERE."
762−msgstr ""
763−
764−#: qml/Wizard/Pages/here-terms.qml:81
765−msgid ""
766−"To provide you with positioning services and to improve their quality, HERE "
767−"collects information about nearby cell towers and Wi-Fi hotspots around your "
768−"current location whenever your position is being found."
769msgstr ""
704msgstr ""
770
705
771−#: qml/Wizard/Pages/here-terms.qml:93
706+#: qml/Wizard/Pages/80-finished.qml:106
772−msgid ""
707+msgid "You are ready to use your device now"
773−"The information collected is used to analyze the service and to improve the "
774−"use of service, but not to identify you personally."
775msgstr ""
708msgstr ""
776
709
777−#: qml/Wizard/Pages/here-terms.qml:106
710+#: qml/Wizard/Pages/80-finished.qml:126
778−#, qt-format
711+msgid "Continue"
779−msgid "By continuing, you agree to the HERE platform %1 and %2."
780−msgstr ""
712+msgstr "Tatur"
781
713
782−#: qml/Wizard/Pages/here-terms.qml:107
714+#: qml/Wizard/Pages/80-finished.qml:126
783−msgid "Service Terms"
715+msgid "Get Started"
784msgstr ""
716msgstr ""
785
717
786#: qml/Wizard/Pages/passcode-confirm.qml:43
718#: qml/Wizard/Pages/passcode-confirm.qml:43
787−#: qml/Wizard/Pages/passcode-desktop.qml:97
719+#: qml/Wizard/Pages/passcode-desktop.qml:104
788msgid "Confirm passcode"
720msgid "Confirm passcode"
789msgstr ""
721msgstr ""
790
722
@@ -801,24 +733,24 @@ msgstr ""
801msgid "Lock Screen Passcode"
733msgid "Lock Screen Passcode"
802msgstr ""
734msgstr ""
803
735
804−#: qml/Wizard/Pages/passcode-desktop.qml:61
736+#: qml/Wizard/Pages/passcode-desktop.qml:68
805msgid "Enter 4 numbers to setup your passcode"
737msgid "Enter 4 numbers to setup your passcode"
806msgstr ""
738msgstr ""
807
739
808−#: qml/Wizard/Pages/passcode-desktop.qml:77
740+#: qml/Wizard/Pages/passcode-desktop.qml:84
809#: qml/Wizard/Pages/passcode-set.qml:54
741#: qml/Wizard/Pages/passcode-set.qml:54
810msgid "Choose passcode"
742msgid "Choose passcode"
811msgstr ""
743msgstr ""
812
744
813−#: qml/Wizard/Pages/passcode-desktop.qml:127
745+#: qml/Wizard/Pages/passcode-desktop.qml:139
814msgid "Passcode too short"
746msgid "Passcode too short"
815msgstr ""
747msgstr ""
816
748
817−#: qml/Wizard/Pages/passcode-desktop.qml:129
749+#: qml/Wizard/Pages/passcode-desktop.qml:141
818msgid "Passcodes match"
750msgid "Passcodes match"
819msgstr ""
751msgstr ""
820
752
821−#: qml/Wizard/Pages/passcode-desktop.qml:131
753+#: qml/Wizard/Pages/passcode-desktop.qml:143
822msgid "Passcodes do not match"
754msgid "Passcodes do not match"
823msgstr ""
755msgstr ""
824
756
@@ -830,15 +762,15 @@ msgstr ""
830msgid "Lock Screen Password"
762msgid "Lock Screen Password"
831msgstr ""
763msgstr ""
832
764
833−#: qml/Wizard/Pages/password-set.qml:62
765+#: qml/Wizard/Pages/password-set.qml:68
834msgid "Enter at least 8 characters"
766msgid "Enter at least 8 characters"
835msgstr ""
767msgstr ""
836
768
837−#: qml/Wizard/Pages/password-set.qml:74
769+#: qml/Wizard/Pages/password-set.qml:80
838msgid "Choose password"
770msgid "Choose password"
839msgstr ""
771msgstr ""
840
772
841−#: qml/Wizard/Pages/password-set.qml:104
773+#: qml/Wizard/Pages/password-set.qml:110
842msgid "Confirm password"
774msgid "Confirm password"
843msgstr ""
775msgstr ""
844
776
@@ -894,6 +826,28 @@ msgstr ""
894msgid "Very weak password"
826msgid "Very weak password"
895msgstr ""
827msgstr ""
896
828
829+#~ msgctxt "Button: Show the current dash scope settings"
830+#~ msgid "Settings"
831+#~ msgstr "Massosu"
832+
833+#~ msgctxt "Label: Hint for dash search line edit"
834+#~ msgid "Search"
835+#~ msgstr "Goranto"
836+
837+#~ msgid "Send"
838+#~ msgstr "Rub"
839+
840+#~ msgid "Home"
841+#~ msgstr "Dabqi"
842+
843+#, fuzzy
844+#~ msgid "Download"
845+#~ msgstr "Qulluumah"
846+
847+#, fuzzy
848+#~ msgid "Downloaded"
849+#~ msgstr "Qulluumah"
850+
897#~ msgctxt "Title: Reboot/Shut down dialog"
851#~ msgctxt "Title: Reboot/Shut down dialog"
898#~ msgid "Shut down"
852#~ msgid "Shut down"
899#~ msgstr "Bargus"
853#~ msgstr "Bargus"
@@ -909,16 +863,9 @@ msgstr ""
909#~ msgid "Reboot"
863#~ msgid "Reboot"
910#~ msgstr "Wadir-ugus"
864#~ msgstr "Wadir-ugus"
911
865
912−#, qt-format
913#~ msgid "Enter %1"
866#~ msgid "Enter %1"
914#~ msgstr "%1 culus"
867#~ msgstr "%1 culus"
915
868
916−#~ msgid "Enter passphrase"
917−#~ msgstr "Tatur-maxco culus"
918−
919−#~ msgid "Enter passcode"
920−#~ msgstr "Tatur-mudenta culus"
921−
922#~ msgctxt "Label: Type of security method"
869#~ msgctxt "Label: Type of security method"
923#~ msgid "Swipe"
870#~ msgid "Swipe"
924#~ msgstr "Uysuttuc"
871#~ msgstr "Uysuttuc"
@@ -931,6 +878,3 @@ msgstr ""
931
878
932#~ msgid "Let’s get started."
879#~ msgid "Let’s get started."
933#~ msgstr "Hayya qimbisnay."
880#~ msgstr "Hayya qimbisnay."
934−
935−#~ msgid "Continue"
936−#~ msgstr "Tatur"
po/am.po+471−358
@@ -6,24 +6,54 @@
6msgid ""
6msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9−"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9+"Report-Msgid-Bugs-To: \n"
10−"POT-Creation-Date: 2016-11-16 10:44+0000\n"
10+"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2016-08-13 14:45+0000\n"
11+"PO-Revision-Date: 2021-08-29 20:18+0000\n"
12−"Last-Translator: samson <Unknown>\n"
12+"Last-Translator: Samson <sambelet@yahoo.com>\n"
13−"Language-Team: Amharic <am@li.org>\n"
13+"Language-Team: Amharic <https://translate.ubports.com/projects/ubports/"
14+"lomiri/am/>\n"
15+"Language: am\n"
14"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
18"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
19"Plural-Forms: nplurals=2; plural=n > 1;\n"
20+"X-Generator: Weblate 3.11.3\n"
18"X-Launchpad-Export-Date: 2016-11-23 06:34+0000\n"
21"X-Launchpad-Export-Date: 2016-11-23 06:34+0000\n"
19−"X-Generator: Launchpad (build 18269)\n"
20
22
21−#: plugins/LightDM/Greeter.cpp:123
23+#: plugins/LightDM/Greeter.cpp:199
22msgid "Password: "
24msgid "Password: "
23msgstr "የ መግቢያ ቃል: "
25msgstr "የ መግቢያ ቃል: "
24
26
27+#: plugins/LightDM/Greeter.cpp:214
28+msgid "Username"
29+msgstr "የ ተጠቃሚ ስም"
30+
31+#: plugins/LightDM/Greeter.cpp:258
32+msgid "Failed to authenticate"
33+msgstr "ማረጋገጥ አልተቻለም"
34+
35+#: plugins/LightDM/Greeter.cpp:260
36+msgid "Invalid password, please try again"
37+msgstr "ዋጋ የሌለው የ መግቢያ ቃል: እባክዎን እንደገና ይሞክሩ"
38+
39+#: plugins/LightDM/Greeter.cpp:270
40+msgid "Log In"
41+msgstr "መግቢያ"
42+
43+#: plugins/LightDM/Greeter.cpp:270
44+msgid "Retry"
45+msgstr "እንደገና ይሞክሩ"
46+
47+#: plugins/LightDM/UsersModel.cpp:157
48+msgid "Login"
49+msgstr "መግቢያ"
50+
51+#: plugins/LightDM/UsersModel.cpp:165
52+msgid "Guest Session"
53+msgstr "የ እንግዳ ክፍለ ጊዜ"
54+
25−#: plugins/Unity/Launcher/launcheritem.cpp:49
55+#: plugins/Unity/Launcher/launcheritem.cpp:48
26−#: plugins/Unity/Launcher/launcheritem.cpp:108
56+#: plugins/Unity/Launcher/launcheritem.cpp:122
27msgid "Pin shortcut"
57msgid "Pin shortcut"
28msgstr "አቋራጭ ማጣበቂያ"
58msgstr "አቋራጭ ማጣበቂያ"
29
59
@@ -31,59 +61,58 @@ msgstr "አቋራጭ ማጣበቂያ"
31msgid "Quit"
61msgid "Quit"
32msgstr "ማጥፊያ"
62msgstr "ማጥፊያ"
33
63
34−#: plugins/Unity/Launcher/launcheritem.cpp:108
64+#: plugins/Unity/Launcher/launcheritem.cpp:122
35msgid "Unpin shortcut"
65msgid "Unpin shortcut"
36msgstr "አቋራጭ መለያያ"
66msgstr "አቋራጭ መለያያ"
37
67
38−#: qml/Components/Dialogs.qml:149
68+#: qml/Components/Dialogs.qml:176
39msgctxt "Title: Lock/Log out dialog"
69msgctxt "Title: Lock/Log out dialog"
40msgid "Log out"
70msgid "Log out"
41msgstr "መውጫ"
71msgstr "መውጫ"
42
72
43−#: qml/Components/Dialogs.qml:150
73+#: qml/Components/Dialogs.qml:177
44msgid "Are you sure you want to log out?"
74msgid "Are you sure you want to log out?"
45msgstr "በእርግጥ መውጣት ይፈልጋሉ?"
75msgstr "በእርግጥ መውጣት ይፈልጋሉ?"
46
76
47−#: qml/Components/Dialogs.qml:152
77+#: qml/Components/Dialogs.qml:180
48msgctxt "Button: Lock the system"
78msgctxt "Button: Lock the system"
49msgid "Lock"
79msgid "Lock"
50msgstr "መቆለፊያ"
80msgstr "መቆለፊያ"
51
81
52−#: qml/Components/Dialogs.qml:159
82+#: qml/Components/Dialogs.qml:191
53msgctxt "Button: Log out from the system"
83msgctxt "Button: Log out from the system"
54msgid "Log Out"
84msgid "Log Out"
55msgstr "መውጫ"
85msgstr "መውጫ"
56
86
57−#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
87+#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:284
58−#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:232
59−#: qml/Wizard/Pages/passcode-confirm.qml:32
88+#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
60#: qml/Wizard/Pages/passcode-set.qml:32
89#: qml/Wizard/Pages/passcode-set.qml:32
61msgid "Cancel"
90msgid "Cancel"
62msgstr "መሰረዣ"
91msgstr "መሰረዣ"
63
92
64−#: qml/Components/Dialogs.qml:178
93+#: qml/Components/Dialogs.qml:211
65msgctxt "Title: Reboot dialog"
94msgctxt "Title: Reboot dialog"
66msgid "Reboot"
95msgid "Reboot"
67msgstr "እንደገና ማስነሻ"
96msgstr "እንደገና ማስነሻ"
68
97
69−#: qml/Components/Dialogs.qml:179
98+#: qml/Components/Dialogs.qml:212
70msgid "Are you sure you want to reboot?"
99msgid "Are you sure you want to reboot?"
71msgstr "በእርግጥ እንደገና ማስነሳት ይፈልጋሉ?"
100msgstr "በእርግጥ እንደገና ማስነሳት ይፈልጋሉ?"
72
101
73−#: qml/Components/Dialogs.qml:181
102+#: qml/Components/Dialogs.qml:215
74msgid "No"
103msgid "No"
75msgstr "አይ"
104msgstr "አይ"
76
105
77−#: qml/Components/Dialogs.qml:187
106+#: qml/Components/Dialogs.qml:223
78msgid "Yes"
107msgid "Yes"
79msgstr "አዎ"
108msgstr "አዎ"
80
109
81−#: qml/Components/Dialogs.qml:202
110+#: qml/Components/Dialogs.qml:243
82msgctxt "Title: Power off/Restart dialog"
111msgctxt "Title: Power off/Restart dialog"
83msgid "Power"
112msgid "Power"
84msgstr "ሐይል"
113msgstr "ሐይል"
85
114
86−#: qml/Components/Dialogs.qml:203
115+#: qml/Components/Dialogs.qml:244
87msgid ""
116msgid ""
88"Are you sure you would like\n"
117"Are you sure you would like\n"
89"to power off?"
118"to power off?"
@@ -91,23 +120,27 @@ msgstr ""
91"በእርግጥ ሐይሉን ማጥፋት\n"
120"በእርግጥ ሐይሉን ማጥፋት\n"
92"ይፈልጋሉ?"
121"ይፈልጋሉ?"
93
122
94−#: qml/Components/Dialogs.qml:205
123+#: qml/Components/Dialogs.qml:248
95msgctxt "Button: Power off the system"
124msgctxt "Button: Power off the system"
96msgid "Power off"
125msgid "Power off"
97msgstr "ሐይል ማጥፊያ"
126msgstr "ሐይል ማጥፊያ"
98
127
99−#: qml/Components/Dialogs.qml:214
128+#: qml/Components/Dialogs.qml:263
100msgctxt "Button: Restart the system"
129msgctxt "Button: Restart the system"
101msgid "Restart"
130msgid "Restart"
102msgstr "እንደገና ማስጀመሪያ"
131msgstr "እንደገና ማስጀመሪያ"
103
132
133+#: qml/Components/Dialogs.qml:276
134+msgid "Screenshot"
135+msgstr "መመልከቻ ፎቶ ማንሻ"
136+
104#: qml/Components/KeyboardShortcutsOverlay.qml:41
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
105msgid "Keyboard Shortcuts"
138msgid "Keyboard Shortcuts"
106msgstr "የ ፊደል ገበታ አቋራጮች"
139msgstr "የ ፊደል ገበታ አቋራጮች"
107
140
108#: qml/Components/KeyboardShortcutsOverlay.qml:55
141#: qml/Components/KeyboardShortcutsOverlay.qml:55
109−msgid "Unity 8"
142+msgid "Lomiri"
110−msgstr "Unity 8"
143+msgstr "Lomiri"
111
144
112#: qml/Components/KeyboardShortcutsOverlay.qml:62
145#: qml/Components/KeyboardShortcutsOverlay.qml:62
113msgid "PrtScr"
146msgid "PrtScr"
@@ -122,8 +155,8 @@ msgid "Alt + PrtScr"
122msgstr "Alt + PrtScr"
155msgstr "Alt + PrtScr"
123
156
124#: qml/Components/KeyboardShortcutsOverlay.qml:80
157#: qml/Components/KeyboardShortcutsOverlay.qml:80
125−msgid "Takes a screenshot of a window."
158+msgid "Takes a screenshot of the current window."
126−msgstr "የ መስኮት መመልከቻ ፎቶ ማንሻ"
159+msgstr "የ አሁኑን መስኮት መመልከቻ ፎቶ ማንሻ"
127
160
128#: qml/Components/KeyboardShortcutsOverlay.qml:88
161#: qml/Components/KeyboardShortcutsOverlay.qml:88
129msgid "Super + Space"
162msgid "Super + Space"
@@ -141,127 +174,131 @@ msgstr "Super + Shift + Space"
141msgid "Switches to previous keyboard layout."
174msgid "Switches to previous keyboard layout."
142msgstr "ወዳለፈው የ ፊደል ገበታ እቅድ መቀየሪያ"
175msgstr "ወዳለፈው የ ፊደል ገበታ እቅድ መቀየሪያ"
143
176
144−#: qml/Components/KeyboardShortcutsOverlay.qml:118
177+#: qml/Components/KeyboardShortcutsOverlay.qml:114
178+msgid "Ctrl + Alt + T"
179+msgstr "Ctrl + Alt + T"
180+
181+#: qml/Components/KeyboardShortcutsOverlay.qml:119
182+msgid "Starts the Terminal application."
183+msgstr "የ ተርሚናል መተግበሪያ ማስጀመሪያ"
184+
185+#: qml/Components/KeyboardShortcutsOverlay.qml:131
145msgid "Launcher"
186msgid "Launcher"
146msgstr "ማስነሻ"
187msgstr "ማስነሻ"
147
188
148−#: qml/Components/KeyboardShortcutsOverlay.qml:125
189+#: qml/Components/KeyboardShortcutsOverlay.qml:138
149msgid "Super (Hold)"
190msgid "Super (Hold)"
150msgstr "Super (ይጫኑ)"
191msgstr "Super (ይጫኑ)"
151
192
152−#: qml/Components/KeyboardShortcutsOverlay.qml:130
193+#: qml/Components/KeyboardShortcutsOverlay.qml:143
153msgid "Opens the launcher, displays shortcuts."
194msgid "Opens the launcher, displays shortcuts."
154msgstr "ማስነሻውን መክፈቻ እና አቋራጮች ማሳያ"
195msgstr "ማስነሻውን መክፈቻ እና አቋራጮች ማሳያ"
155
196
156−#: qml/Components/KeyboardShortcutsOverlay.qml:138
197+#: qml/Components/KeyboardShortcutsOverlay.qml:151
157msgid "Alt + F1"
198msgid "Alt + F1"
158msgstr "Alt + F1"
199msgstr "Alt + F1"
159
200
160−#: qml/Components/KeyboardShortcutsOverlay.qml:143
201+#: qml/Components/KeyboardShortcutsOverlay.qml:156
161msgid "Opens launcher keyboard navigation mode."
202msgid "Opens launcher keyboard navigation mode."
162msgstr "መክፈቻ በ ፊደል ገበታ መቃኛ ዘዴ ማስነሻ"
203msgstr "መክፈቻ በ ፊደል ገበታ መቃኛ ዘዴ ማስነሻ"
163
204
164−#: qml/Components/KeyboardShortcutsOverlay.qml:151
205+#: qml/Components/KeyboardShortcutsOverlay.qml:164
165msgid "Super + Tab"
206msgid "Super + Tab"
166msgstr "Super + Tab"
207msgstr "Super + Tab"
167
208
168−#: qml/Components/KeyboardShortcutsOverlay.qml:156
209+#: qml/Components/KeyboardShortcutsOverlay.qml:169
169msgid "Switches applications via the launcher."
210msgid "Switches applications via the launcher."
170msgstr "መተግበሪያዎችን በ ማስጀመሪያ መቀየሪያ"
211msgstr "መተግበሪያዎችን በ ማስጀመሪያ መቀየሪያ"
171
212
172−#: qml/Components/KeyboardShortcutsOverlay.qml:164
213+#: qml/Components/KeyboardShortcutsOverlay.qml:177
173−msgid "Super + 0 to 9"
214+msgid "Super + 1 to 0"
174msgstr "Super + 0 እስከ 9"
215msgstr "Super + 0 እስከ 9"
175
216
176−#: qml/Components/KeyboardShortcutsOverlay.qml:169
217+#: qml/Components/KeyboardShortcutsOverlay.qml:182
177msgid "Same as clicking on a launcher icon."
218msgid "Same as clicking on a launcher icon."
178msgstr "ተመሳሳይ በ ማስጀመሪያ ምልክት ላይ እንደ መጫን"
219msgstr "ተመሳሳይ በ ማስጀመሪያ ምልክት ላይ እንደ መጫን"
179
220
180−#: qml/Components/KeyboardShortcutsOverlay.qml:181
221+#: qml/Components/KeyboardShortcutsOverlay.qml:190
181−msgid "Scopes"
222+msgid "Super + A"
182−msgstr "ክልል"
183−
184−#: qml/Components/KeyboardShortcutsOverlay.qml:188
185−msgid "Super (Tap)"
186−msgstr "Super (Tap)"
223+msgstr "Super + A"
187
224
188−#: qml/Components/KeyboardShortcutsOverlay.qml:193
225+#: qml/Components/KeyboardShortcutsOverlay.qml:195
189−msgid "Opens the Scopes home."
226+msgid "Opens the Application Drawer."
190−msgstr "የ ክልል ቤት መክፈቻ"
227+msgstr "የ መሳያ መተግበሪያ ማስጀመሪያ"
191
228
192−#: qml/Components/KeyboardShortcutsOverlay.qml:209
229+#: qml/Components/KeyboardShortcutsOverlay.qml:235
193msgid "Switching"
230msgid "Switching"
194msgstr "በ መቀየር ላይ"
231msgstr "በ መቀየር ላይ"
195
232
196−#: qml/Components/KeyboardShortcutsOverlay.qml:216
233+#: qml/Components/KeyboardShortcutsOverlay.qml:242
197msgid "Alt + Tab"
234msgid "Alt + Tab"
198msgstr "Alt + Tab"
235msgstr "Alt + Tab"
199
236
200−#: qml/Components/KeyboardShortcutsOverlay.qml:221
237+#: qml/Components/KeyboardShortcutsOverlay.qml:247
201msgid "Switches between applications."
238msgid "Switches between applications."
202msgstr "በ መተግበሪያዎች መካከል መቀያየሪያ"
239msgstr "በ መተግበሪያዎች መካከል መቀያየሪያ"
203
240
204−#: qml/Components/KeyboardShortcutsOverlay.qml:229
241+#: qml/Components/KeyboardShortcutsOverlay.qml:255
205msgid "Super + W"
242msgid "Super + W"
206msgstr "Super + W"
243msgstr "Super + W"
207
244
208−#: qml/Components/KeyboardShortcutsOverlay.qml:234
245+#: qml/Components/KeyboardShortcutsOverlay.qml:260
209msgid "Opens the desktop spread."
246msgid "Opens the desktop spread."
210msgstr "የ ዴስክቶፕ ማስፊያ መክፈቻ"
247msgstr "የ ዴስክቶፕ ማስፊያ መክፈቻ"
211
248
212−#: qml/Components/KeyboardShortcutsOverlay.qml:242
249+#: qml/Components/KeyboardShortcutsOverlay.qml:268
213msgid "Cursor Left or Right"
250msgid "Cursor Left or Right"
214msgstr "መጠቆሚያ የ ግራ ወይንም የ ቀኝ"
251msgstr "መጠቆሚያ የ ግራ ወይንም የ ቀኝ"
215
252
216−#: qml/Components/KeyboardShortcutsOverlay.qml:247
253+#: qml/Components/KeyboardShortcutsOverlay.qml:273
217msgid "Moves the focus."
254msgid "Moves the focus."
218msgstr "ትኩረት ማንቀሳቀሻ"
255msgstr "ትኩረት ማንቀሳቀሻ"
219
256
220−#: qml/Components/KeyboardShortcutsOverlay.qml:259
257+#: qml/Components/KeyboardShortcutsOverlay.qml:285
221msgid "Windows"
258msgid "Windows"
222msgstr "መስኮቶች"
259msgstr "መስኮቶች"
223
260
224−#: qml/Components/KeyboardShortcutsOverlay.qml:266
261+#: qml/Components/KeyboardShortcutsOverlay.qml:292
225msgid "Ctrl + Super + D"
262msgid "Ctrl + Super + D"
226msgstr "Ctrl + Super + D"
263msgstr "Ctrl + Super + D"
227
264
228−#: qml/Components/KeyboardShortcutsOverlay.qml:271
265+#: qml/Components/KeyboardShortcutsOverlay.qml:297
229msgid "Minimizes all windows."
266msgid "Minimizes all windows."
230msgstr "ሁሉንም መስኮቶች ማሳነሻ"
267msgstr "ሁሉንም መስኮቶች ማሳነሻ"
231
268
232−#: qml/Components/KeyboardShortcutsOverlay.qml:279
269+#: qml/Components/KeyboardShortcutsOverlay.qml:305
233msgid "Ctrl + Super + Up"
270msgid "Ctrl + Super + Up"
234msgstr "Ctrl + Super + Up"
271msgstr "Ctrl + Super + Up"
235
272
236−#: qml/Components/KeyboardShortcutsOverlay.qml:284
273+#: qml/Components/KeyboardShortcutsOverlay.qml:310
237msgid "Maximizes the current window."
274msgid "Maximizes the current window."
238msgstr "የ አሁኑን መስኮት ማሳደጊያ"
275msgstr "የ አሁኑን መስኮት ማሳደጊያ"
239
276
240−#: qml/Components/KeyboardShortcutsOverlay.qml:292
277+#: qml/Components/KeyboardShortcutsOverlay.qml:318
241msgid "Ctrl + Super + Down"
278msgid "Ctrl + Super + Down"
242msgstr "Ctrl + Super + Down"
279msgstr "Ctrl + Super + Down"
243
280
244−#: qml/Components/KeyboardShortcutsOverlay.qml:297
281+#: qml/Components/KeyboardShortcutsOverlay.qml:323
245msgid "Minimizes or restores the current window."
282msgid "Minimizes or restores the current window."
246msgstr "የ አሁኑን መስኮትማሳነሻ ወይንም እንደ ነበር መመለሻ"
283msgstr "የ አሁኑን መስኮትማሳነሻ ወይንም እንደ ነበር መመለሻ"
247
284
248−#: qml/Components/KeyboardShortcutsOverlay.qml:305
285+#: qml/Components/KeyboardShortcutsOverlay.qml:331
249msgid "Ctrl + Super + Left or Right"
286msgid "Ctrl + Super + Left or Right"
250msgstr "Ctrl + Super + Left or Right"
287msgstr "Ctrl + Super + Left or Right"
251
288
252−#: qml/Components/KeyboardShortcutsOverlay.qml:310
289+#: qml/Components/KeyboardShortcutsOverlay.qml:336
253msgid "Semi-maximizes the current window."
290msgid "Semi-maximizes the current window."
254msgstr "የ አሁኑን መስኮት በ ንዑስ-ማሳደጊያ"
291msgstr "የ አሁኑን መስኮት በ ንዑስ-ማሳደጊያ"
255
292
256−#: qml/Components/KeyboardShortcutsOverlay.qml:318
293+#: qml/Components/KeyboardShortcutsOverlay.qml:344
257msgid "Alt + F4"
294msgid "Alt + F4"
258msgstr "Alt + F4"
295msgstr "Alt + F4"
259
296
260−#: qml/Components/KeyboardShortcutsOverlay.qml:323
297+#: qml/Components/KeyboardShortcutsOverlay.qml:349
261msgid "Closes the current window."
298msgid "Closes the current window."
262msgstr "የ አሁኑን መስኮት መዝጊያ"
299msgstr "የ አሁኑን መስኮት መዝጊያ"
263
300
264−#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252
301+#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:235
265msgid "Return to Call"
302msgid "Return to Call"
266msgstr "ወደ ጥሪ መመለሻ"
303msgstr "ወደ ጥሪ መመለሻ"
267
304
@@ -269,7 +306,7 @@ msgstr "ወደ ጥሪ መመለሻ"
269msgid "Emergency Call"
306msgid "Emergency Call"
270msgstr "የ ድንገተኛ ጥሪ"
307msgstr "የ ድንገተኛ ጥሪ"
271
308
272−#: qml/Components/Lockscreen.qml:244
309+#: qml/Components/Lockscreen.qml:245
273msgid "OK"
310msgid "OK"
274msgstr "እሺ"
311msgstr "እሺ"
275
312
@@ -283,29 +320,29 @@ msgstr "%1:%2:%3"
283msgid "%1:%2"
320msgid "%1:%2"
284msgstr "%1:%2"
321msgstr "%1:%2"
285
322
286−#: qml/Components/ModeSwitchWarningDialog.qml:32
323+#: qml/Components/ModeSwitchWarningDialog.qml:33
287msgid "Apps may have unsaved data:"
324msgid "Apps may have unsaved data:"
288msgstr "መተግበሪያው ያልተቀመጠ ዳታ ይዟል:"
325msgstr "መተግበሪያው ያልተቀመጠ ዳታ ይዟል:"
289
326
290−#: qml/Components/ModeSwitchWarningDialog.qml:57
327+#: qml/Components/ModeSwitchWarningDialog.qml:60
291msgctxt ""
328msgctxt ""
292"Re-dock means connect the device again to an external screen/mouse/keyboard"
329"Re-dock means connect the device again to an external screen/mouse/keyboard"
293msgid "Re-dock, save your work and close these apps to continue."
330msgid "Re-dock, save your work and close these apps to continue."
294msgstr "እንደገና-ማሳረፊያ: ስራዎትን ያስቀምጡ እና ይዝጉ መተግበሪያው እንዲቀጥል"
331msgstr "እንደገና-ማሳረፊያ: ስራዎትን ያስቀምጡ እና ይዝጉ መተግበሪያው እንዲቀጥል"
295
332
296−#: qml/Components/ModeSwitchWarningDialog.qml:63
333+#: qml/Components/ModeSwitchWarningDialog.qml:67
297msgid "Or force close now (unsaved data will be lost)."
334msgid "Or force close now (unsaved data will be lost)."
298msgstr "ወይንም አሁን አስገድዶ ማጥፊያ (ያልተቀመጠ ዳታ ይጠፋል)"
335msgstr "ወይንም አሁን አስገድዶ ማጥፊያ (ያልተቀመጠ ዳታ ይጠፋል)"
299
336
300−#: qml/Components/ModeSwitchWarningDialog.qml:75
337+#: qml/Components/ModeSwitchWarningDialog.qml:80
301msgid "OK, I will reconnect"
338msgid "OK, I will reconnect"
302msgstr "እሺ: እኔ አሁን እንደገና እገናኛለሁ"
339msgstr "እሺ: እኔ አሁን እንደገና እገናኛለሁ"
303
340
304−#: qml/Components/ModeSwitchWarningDialog.qml:76
341+#: qml/Components/ModeSwitchWarningDialog.qml:81
305msgid "Reconnect now!"
342msgid "Reconnect now!"
306msgstr "አሁን እንደገና መገናኛ!"
343msgstr "አሁን እንደገና መገናኛ!"
307
344
308−#: qml/Components/ModeSwitchWarningDialog.qml:88
345+#: qml/Components/ModeSwitchWarningDialog.qml:94
309msgid "Close all"
346msgid "Close all"
310msgstr "ሁሉንም መዝጊያ"
347msgstr "ሁሉንም መዝጊያ"
311
348
@@ -313,109 +350,28 @@ msgstr "ሁሉንም መዝጊያ"
313msgid "Preview Share Item"
350msgid "Preview Share Item"
314msgstr "የሚካፈሉትን እቃ ቅድመ እይታ"
351msgstr "የሚካፈሉትን እቃ ቅድመ እይታ"
315
352
316−#: qml/Dash/DashPageHeader.qml:361
353+#: qml/Components/VirtualTouchPad.qml:317
317−msgctxt "Button: Open the Ubuntu Store"
318−msgid "Store"
319−msgstr "ማስቀመጫ"
320−
321−#: qml/Dash/DashPageHeader.qml:368
322−msgctxt "Button: Start a search in the current dash scope"
323−msgid "Search"
324−msgstr "መፈለጊያ"
325−
326−#: qml/Dash/DashPageHeader.qml:378
327−msgctxt "Button: Show the current dash scope settings"
328−msgid "Settings"
329−msgstr "ማሰናጃዎች"
330−
331−#: qml/Dash/DashPageHeader.qml:385
332−msgid "Remove from Favorites"
333−msgstr "ከምወዳቸው ማስወገጃ"
334−
335−#: qml/Dash/DashPageHeader.qml:385
336−msgid "Add to Favorites"
337−msgstr "ወደምወዳቸው መጨመሪያ"
338−
339−#: qml/Dash/FiltersPopover.qml:61
340−msgid "Refine your results"
341−msgstr "የ እርስዎን ውጤቶች እንደገና ማጣሪያ"
342−
343−#: qml/Dash/FiltersPopover.qml:70
344−msgid "Reset"
345−msgstr "እንደ ነበረ መመለሻ"
346−
347−#: qml/Dash/GenericScopeView.qml:545 qml/Dash/GenericScopeView.qml:716
348−msgid "Show less"
349−msgstr "ትንሽ ማሳያ"
350−
351−#: qml/Dash/GenericScopeView.qml:545
352−msgid "Show all"
353−msgstr "ሁሉንም ማሳያ"
354−
355−#: qml/Dash/GenericScopeView.qml:576
356−msgctxt "Label: Hint for dash search line edit"
357−msgid "Search"
358−msgstr "መፈለጊያ"
359−
360−#: qml/Dash/PageHeaderExtraPanel.qml:57
361−msgid "Recent Searches"
362−msgstr "የ ቅርብ ጊዜ ፍለጋዎች"
363−
364−#: qml/Dash/PageHeaderExtraPanel.qml:68
365−msgid "Clear All"
366−msgstr "ሁሉንም ማጽጃ"
367−
368−#: qml/Dash/Previews/PreviewActionCombo.qml:36
369−msgid "More..."
370−msgstr "ተጨማሪ..."
371−
372−#: qml/Dash/Previews/PreviewActionCombo.qml:36
373−msgid "Less..."
374−msgstr "አነስተኛ..."
375−
376−#: qml/Dash/Previews/PreviewCommentInput.qml:83
377−#: qml/Dash/Previews/PreviewRatingInput.qml:202
378−msgid "Send"
379−msgstr "መላኪያ"
380−
381−#: qml/Dash/Previews/PreviewRatingInput.qml:109
382−msgid "Rate this"
383−msgstr "ለዚህ ደረጃ ይስጡ"
384−
385−#: qml/Dash/Previews/PreviewRatingInput.qml:184
386−msgid "Add a review"
387−msgstr "ግምገማ መጨመሪያ"
388−
389−#: qml/Dash/PullToRefreshScopeStyle.qml:56
390−msgid "Pull to refresh…"
391−msgstr "ይሳቡ ለማነቃቃት…"
392−
393−#: qml/Dash/PullToRefreshScopeStyle.qml:61
394−msgid "Release to refresh…"
395−msgstr "ይልቀቁ ለማነቃቃት…"
396−
397−#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
398−msgid "Enable location data"
399−msgstr "የአካባቢ ዳታ ማስቻያ"
400−
401−#: qml/Dash/ScopesList.qml:57
402−msgid "Manage"
403−msgstr "አስተዳዳሪ"
404−
405−#: qml/Dash/ScopesList.qml:104
406−msgid "Home"
407−msgstr "ቤት"
408−
409−#: qml/Dash/ScopesList.qml:105
410−msgid "Also installed"
411−msgstr "እንዲሁም የተገጠሙ"
412−
413−#: qml/DisabledScreenNotice.qml:109
414msgid ""
354msgid ""
415"Your device is now connected to an external display. Use this screen as a "
355"Your device is now connected to an external display. Use this screen as a "
416"touch pad to interact with the pointer."
356"touch pad to interact with the pointer."
417msgstr "የ እርስዎ አካል አሁን ከ ውጪ ማሳያ ጋር ተገናኝቷል: ይህን መመልከቻ እንደ ተችፓድ መጠቀሚያ"
357msgstr "የ እርስዎ አካል አሁን ከ ውጪ ማሳያ ጋር ተገናኝቷል: ይህን መመልከቻ እንደ ተችፓድ መጠቀሚያ"
418
358
359+#: qml/Components/VirtualTouchPad.qml:327
360+msgid "Tap left button to click."
361+msgstr "የ ግራ ቁልፍ መታ ያድርጉ ለ መጫን"
362+
363+#: qml/Components/VirtualTouchPad.qml:339
364+msgid "Tap right button to right click."
365+msgstr "የ ቀኝ ቁልፍ መታ ያድርጉ በ ቀኝ ለ መጫን"
366+
367+#: qml/Components/VirtualTouchPad.qml:351
368+msgid "Swipe with two fingers to scroll."
369+msgstr "ለ መሸብለል በ ሁለት ጣቶች ይጥረጉ"
370+
371+#: qml/Components/VirtualTouchPad.qml:396
372+msgid "Find more settings in the system settings."
373+msgstr "ተጨማሪ ማሰናጃ በ ስርአት ማሰናጃ ውስጥ መፈለጊያ"
374+
419#: qml/Greeter/CoverPage.qml:127
375#: qml/Greeter/CoverPage.qml:127
420msgid "Unlock"
376msgid "Unlock"
421msgstr "መክፈቻ"
377msgstr "መክፈቻ"
@@ -439,33 +395,45 @@ msgid_plural "Please wait %1 minutes and then try again…"
439msgstr[0] "እባክዎን ይቆዩ %1 ደቂቃ እና ከዛ እንደ ገና ይምክሩ…"
395msgstr[0] "እባክዎን ይቆዩ %1 ደቂቃ እና ከዛ እንደ ገና ይምክሩ…"
440msgstr[1] "እባክዎን ይቆዩ %1 ደቂቃዎች እና ከዛ እንደ ገና ይምክሩ…"
396msgstr[1] "እባክዎን ይቆዩ %1 ደቂቃዎች እና ከዛ እንደ ገና ይምክሩ…"
441
397
442−#: qml/Greeter/Greeter.qml:596
398+#: qml/Greeter/Greeter.qml:550
443msgid "Try again"
399msgid "Try again"
444msgstr "እንደገና ይሞክሩ"
400msgstr "እንደገና ይሞክሩ"
445
401
446−#: qml/Greeter/LoginList.qml:70
402+#: qml/Greeter/Greeter.qml:551
403+msgid "Enter passphrase to unlock"
404+msgstr "የ ማለፊያ ሀረግ ያስገቡ"
405+
406+#: qml/Greeter/Greeter.qml:552
407+msgid "Enter passcode to unlock"
408+msgstr "የ ማለፊያ ኮድ ያስገቡ ለ መክፈት"
409+
410+#: qml/Greeter/NarrowView.qml:235
411+msgid "Emergency"
412+msgstr "ድንገተኛ"
413+
414+#: qml/Greeter/PromptList.qml:126
447msgid "Passphrase"
415msgid "Passphrase"
448msgstr "የማለፊያ ሐረግ"
416msgstr "የማለፊያ ሐረግ"
449
417
450−#: qml/Greeter/LoginList.qml:71
418+#: qml/Greeter/PromptList.qml:126
451msgid "Passcode"
419msgid "Passcode"
452msgstr "ማለፊያ ኮድ"
420msgstr "ማለፊያ ኮድ"
453
421
454−#: qml/Greeter/LoginList.qml:98
422+#: qml/Greeter/SessionsList.qml:124
455−msgid "Retry"
423+msgid "Select desktop environment"
456−msgstr "እንደገና ይሞክሩ"
424+msgstr "የ ዴስክቶፕ አይነት ይምረጡ"
457
425
458−#: qml/Greeter/LoginList.qml:99
426+#: qml/Launcher/Drawer.qml:250
459−msgid "Log In"
427+msgid "Search…"
460−msgstr "መግቢያ"
428+msgstr "መፈለጊያ…"
461
429
462−#: qml/Greeter/NarrowView.qml:252
430+#: qml/Launcher/PullToRefreshScopeStyle.qml:56
463−msgid "Emergency"
431+msgid "Pull to refresh…"
464−msgstr "ድንገተኛ"
432+msgstr "ይሳቡ ለማነቃቃት…"
465
433
466−#: qml/Greeter/SessionsList.qml:122
434+#: qml/Launcher/PullToRefreshScopeStyle.qml:61
467−msgid "Select desktop environment"
435+msgid "Release to refresh…"
468−msgstr "የ ዴስክቶፕ አይነት ይምረጡ"
436+msgstr "ይልቀቁ ለማነቃቃት…"
469
437
470#: qml/Notifications/NotificationMenuItemFactory.qml:124
438#: qml/Notifications/NotificationMenuItemFactory.qml:124
471msgid "Show password"
439msgid "Show password"
@@ -479,62 +447,62 @@ msgstr "ወደ ጥሪው ለመመለስ መታ ያድርጉ..."
479msgid "Conference"
447msgid "Conference"
480msgstr "ስብሰባ"
448msgstr "ስብሰባ"
481
449
482−#: qml/Panel/Indicators/MenuItemFactory.qml:847
450+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:869
483msgid "Nothing is playing"
451msgid "Nothing is playing"
484msgstr "ምንም አይጫወትም"
452msgstr "ምንም አይጫወትም"
485
453
486−#: qml/Panel/Indicators/MenuItemFactory.qml:975
454+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:997
487#, qt-format
455#, qt-format
488msgid "%1 hour"
456msgid "%1 hour"
489msgid_plural "%1 hours"
457msgid_plural "%1 hours"
490msgstr[0] "%1 ሰአት"
458msgstr[0] "%1 ሰአት"
491msgstr[1] "%1 ሰአቶች"
459msgstr[1] "%1 ሰአቶች"
492
460
493−#: qml/Panel/Indicators/MenuItemFactory.qml:979
461+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
494#, qt-format
462#, qt-format
495msgid "%1 minute"
463msgid "%1 minute"
496msgid_plural "%1 minutes"
464msgid_plural "%1 minutes"
497msgstr[0] "%1 ደቂቃ"
465msgstr[0] "%1 ደቂቃ"
498msgstr[1] "%1 ደቂቃዎች"
466msgstr[1] "%1 ደቂቃዎች"
499
467
500−#: qml/Panel/Indicators/MenuItemFactory.qml:984
468+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1006
501#, qt-format
469#, qt-format
502msgid "%1 second"
470msgid "%1 second"
503msgid_plural "%1 seconds"
471msgid_plural "%1 seconds"
504msgstr[0] "%1 ሰከንድ"
472msgstr[0] "%1 ሰከንድ"
505msgstr[1] "%1 ሰከንዶች"
473msgstr[1] "%1 ሰከንዶች"
506
474
507−#: qml/Panel/Indicators/MenuItemFactory.qml:987
475+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1009
508msgid "0 seconds"
476msgid "0 seconds"
509msgstr "0 ሰከንዶች"
477msgstr "0 ሰከንዶች"
510
478
511#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
479#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
512−#: qml/Panel/Indicators/MenuItemFactory.qml:989
480+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1011
513#, qt-format
481#, qt-format
514msgid "%1 remaining"
482msgid "%1 remaining"
515msgstr "%1 የቀረው"
483msgstr "%1 የቀረው"
516
484
517−#: qml/Panel/Indicators/MenuItemFactory.qml:995
485+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1017
518msgid "In queue…"
486msgid "In queue…"
519msgstr "ወረፋ በመጠበቅ ላይ…"
487msgstr "ወረፋ በመጠበቅ ላይ…"
520
488
521−#: qml/Panel/Indicators/MenuItemFactory.qml:999
489+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1021
522msgid "Downloading"
490msgid "Downloading"
523msgstr "በማውረድ ላይ"
491msgstr "በማውረድ ላይ"
524
492
525−#: qml/Panel/Indicators/MenuItemFactory.qml:1001
493+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1023
526msgid "Paused, tap to resume"
494msgid "Paused, tap to resume"
527msgstr "ማስቆሚያ: ለመቀጠል መታ ያድርጉ"
495msgstr "ማስቆሚያ: ለመቀጠል መታ ያድርጉ"
528
496
529−#: qml/Panel/Indicators/MenuItemFactory.qml:1003
497+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1025
530msgid "Canceled"
498msgid "Canceled"
531msgstr "ተሰርዟል"
499msgstr "ተሰርዟል"
532
500
533−#: qml/Panel/Indicators/MenuItemFactory.qml:1005
501+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1027
534msgid "Finished"
502msgid "Finished"
535msgstr "ጨርሷል"
503msgstr "ጨርሷል"
536
504
537−#: qml/Panel/Indicators/MenuItemFactory.qml:1007
505+#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1029
538msgid "Failed, tap to retry"
506msgid "Failed, tap to retry"
539msgstr "አልተቻለም: እንደገና ለመሞከር መታ ያድርጉ"
507msgstr "አልተቻለም: እንደገና ለመሞከር መታ ያድርጉ"
540
508
@@ -554,23 +522,33 @@ msgctxt "Button: Call back on phone"
554msgid "Call back"
522msgid "Call back"
555msgstr "መልሶ መደወያ"
523msgstr "መልሶ መደወያ"
556
524
525+#: qml/Panel/PanelMenuPage.qml:92
526+msgid "Back"
527+msgstr "ወደ ኋላ"
528+
557#: qml/Stage/SideStage.qml:76
529#: qml/Stage/SideStage.qml:76
558msgid "Drag using 3 fingers any application from one window to the other"
530msgid "Drag using 3 fingers any application from one window to the other"
559msgstr "መጎተቻ 3 ጣቶች በ መጠቀም ማንኛውንም መተግበሪያ ከ አንድ መስኮት ወደ ሌላ"
531msgstr "መጎተቻ 3 ጣቶች በ መጠቀም ማንኛውንም መተግበሪያ ከ አንድ መስኮት ወደ ሌላ"
560
532
533+#: qml/Stage/Stage.qml:671
534+msgid "No running apps"
535+msgstr ""
536+
561#: qml/Tutorial/TutorialLeftLong.qml:49
537#: qml/Tutorial/TutorialLeftLong.qml:49
538+#, fuzzy
562−msgid "Long swipe from the left edge to open the Today scope"
539+#| msgid "Long swipe from the left edge to open the Today scope"
540+msgid "Long swipe from the left edge to open the Application Drawer"
563msgstr "በ ረጅሙ ከ ግራ ጠርዝ በኩል ይጥረጉ የ ዛሬን ክልል ለ መክፈት"
541msgstr "በ ረጅሙ ከ ግራ ጠርዝ በኩል ይጥረጉ የ ዛሬን ክልል ለ መክፈት"
564
542
565#: qml/Tutorial/TutorialLeft.qml:47
543#: qml/Tutorial/TutorialLeft.qml:47
566msgid "Short swipe from the left edge to open the launcher"
544msgid "Short swipe from the left edge to open the launcher"
567msgstr "በ አጭሩ ይጥረጉ ከ ግራ ጠርዝ በኩል ማስጀመሪያውን ለ መክፈት"
545msgstr "በ አጭሩ ይጥረጉ ከ ግራ ጠርዝ በኩል ማስጀመሪያውን ለ መክፈት"
568
546
569−#: qml/Tutorial/TutorialRight.qml:55
547+#: qml/Tutorial/TutorialRight.qml:54
570msgid "Push your mouse against the right edge to view your open apps"
548msgid "Push your mouse against the right edge to view your open apps"
571msgstr "የ እርስዎን አይጥ መጠቆሚያ ወደ ቀኝ ጠርዝ በኩል ያስጠጉ የ ተከፈቱ መተግበሪያዎችን ለ መመልከት"
549msgstr "የ እርስዎን አይጥ መጠቆሚያ ወደ ቀኝ ጠርዝ በኩል ያስጠጉ የ ተከፈቱ መተግበሪያዎችን ለ መመልከት"
572
550
573−#: qml/Tutorial/TutorialRight.qml:56
551+#: qml/Tutorial/TutorialRight.qml:55
574msgid "Swipe from the right edge to view your open apps"
552msgid "Swipe from the right edge to view your open apps"
575msgstr "ከ ቀኝ በኩል ይጥረጉ የ ተከፈቱ መተግበሪያዎችን ለ መመልከት"
553msgstr "ከ ቀኝ በኩል ይጥረጉ የ ተከፈቱ መተግበሪያዎችን ለ መመልከት"
576
554
@@ -578,28 +556,44 @@ msgstr "ከ ቀኝ በኩል ይጥረጉ የ ተከፈቱ መተግበሪያዎ
578msgid "Swipe from the top edge to access notifications and quick settings"
556msgid "Swipe from the top edge to access notifications and quick settings"
579msgstr "ከ ላይ ጠረዝ በኩል ይጥረጉ ማስታወቂያዎች እና በፍጥነት ማሰናጃዎች ጋር ለ መድረስ"
557msgstr "ከ ላይ ጠረዝ በኩል ይጥረጉ ማስታወቂያዎች እና በፍጥነት ማሰናጃዎች ጋር ለ መድረስ"
580
558
581−#: qml/Wizard/Page.qml:54
559+#: qml/Wizard/Page.qml:59
582msgctxt "Button: Go back one page in the Wizard"
560msgctxt "Button: Go back one page in the Wizard"
583msgid "Back"
561msgid "Back"
584msgstr "ወደ ኋላ"
562msgstr "ወደ ኋላ"
585
563
586−#: qml/Wizard/Pages/10-welcome.qml:28
564+#: qml/Wizard/Pages/10-welcome.qml:29
587msgid "Language"
565msgid "Language"
588msgstr "ቋንቋ"
566msgstr "ቋንቋ"
589
567
590−#: qml/Wizard/Pages/10-welcome.qml:173 qml/Wizard/Pages/20-keyboard.qml:152
568+#: qml/Wizard/Pages/10-welcome.qml:176
591−#: qml/Wizard/Pages/30-wifi.qml:207 qml/Wizard/Pages/40-location.qml:271
569+#: qml/Wizard/Pages/10-welcome-update.qml:126
570+#: qml/Wizard/Pages/11-changelog.qml:74 qml/Wizard/Pages/20-keyboard.qml:152
592−#: qml/Wizard/Pages/50-timezone.qml:271 qml/Wizard/Pages/60-account.qml:65
571+#: qml/Wizard/Pages/30-wifi.qml:214 qml/Wizard/Pages/50-timezone.qml:272
593−#: qml/Wizard/Pages/70-passwd-type.qml:145
572+#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/70-passwd-type.qml:146
594−#: qml/Wizard/Pages/75-report-check.qml:84
595−#: qml/Wizard/Pages/passcode-desktop.qml:142
573+#: qml/Wizard/Pages/passcode-desktop.qml:154
596−#: qml/Wizard/Pages/password-set.qml:142
574+#: qml/Wizard/Pages/password-set.qml:153
597msgid "Next"
575msgid "Next"
598msgstr "ይቀጥሉ"
576msgstr "ይቀጥሉ"
599
577
578+#: qml/Wizard/Pages/10-welcome-update.qml:91
579+msgid "Welcome to "
580+msgstr "እንኳን ደህና መጡ ወደ: "
581+
582+#: qml/Wizard/Pages/10-welcome-update.qml:106
583+msgid "We will make sure your device is ready to use "
584+msgstr "የ እርስዎ አካል ለ መጠቀም ዝግጁ ነው: "
585+
586+#: qml/Wizard/Pages/11-changelog.qml:28
587+msgid "What's new"
588+msgstr "ምን አዲስ ነገር አለ"
589+
590+#: qml/Wizard/Pages/11-changelog.qml:74
591+msgid "Loading..."
592+msgstr "በ መጫን ላይ..."
593+
600#: qml/Wizard/Pages/20-keyboard.qml:31
594#: qml/Wizard/Pages/20-keyboard.qml:31
601msgid "Select Keyboard"
595msgid "Select Keyboard"
602−msgstr "ቋንቋ ይምረጡ"
596+msgstr "የ ፊደል ገበታ ይምረጡ"
603
597
604#: qml/Wizard/Pages/20-keyboard.qml:71
598#: qml/Wizard/Pages/20-keyboard.qml:71
605msgid "Keyboard language"
599msgid "Keyboard language"
@@ -609,8 +603,8 @@ msgstr "የ ፊደል ገበታ ቋንቋ"
609msgid "Keyboard layout"
603msgid "Keyboard layout"
610msgstr "የ ፊደል ገበታ አቀራረብ"
604msgstr "የ ፊደል ገበታ አቀራረብ"
611
605
612−#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:207
606+#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:214
613−#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/77-system-update.qml:152
607+#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/79-system-update.qml:144
614#: qml/Wizard/Pages/sim.qml:101
608#: qml/Wizard/Pages/sim.qml:101
615msgid "Skip"
609msgid "Skip"
616msgstr "መዝለያ"
610msgstr "መዝለያ"
@@ -619,55 +613,23 @@ msgstr "መዝለያ"
619msgid "Connect to Wi‑Fi"
613msgid "Connect to Wi‑Fi"
620msgstr "ወደ ዋይ‑ፋይ መገናኛ"
614msgstr "ወደ ዋይ‑ፋይ መገናኛ"
621
615
622−#: qml/Wizard/Pages/30-wifi.qml:132
616+#: qml/Wizard/Pages/30-wifi.qml:139
623msgid "Connected"
617msgid "Connected"
624msgstr "ተገናኝቷል"
618msgstr "ተገናኝቷል"
625
619
626−#: qml/Wizard/Pages/30-wifi.qml:165
620+#: qml/Wizard/Pages/30-wifi.qml:172
627msgid "Available Wi-Fi networks"
621msgid "Available Wi-Fi networks"
628msgstr "ዝግጁ የ ዋይ-ፋይ ኔትዎርኮች"
622msgstr "ዝግጁ የ ዋይ-ፋይ ኔትዎርኮች"
629
623
630−#: qml/Wizard/Pages/30-wifi.qml:166
624+#: qml/Wizard/Pages/30-wifi.qml:173
631msgid "No available Wi-Fi networks"
625msgid "No available Wi-Fi networks"
632msgstr "ምንም ዝግጁ ዋይ-ፋይ ኔትዎርኮች አልተገኙም"
626msgstr "ምንም ዝግጁ ዋይ-ፋይ ኔትዎርኮች አልተገኙም"
633
627
634−#: qml/Wizard/Pages/40-location.qml:27
635−msgid "Location Services"
636−msgstr "የ አካባቢ ግልጋሎት"
637−
638−#: qml/Wizard/Pages/40-location.qml:83
639−msgid ""
640−"Use GPS, Wi-Fi hotspots and mobile network anonymously to detect location "
641−"(recommended)"
642−msgstr "ይጠቀሙ ጂፒኤስ: ዋይ-ፋይ: hotspots እና ተንቀሳቃሽ ኔትዎርክ ሳይታወቁ አካባቢ ለማግኘት (ይመከራሉ)"
643−
644−#: qml/Wizard/Pages/40-location.qml:105
645−#, qt-format
646−msgid "By selecting this option you agree to the Nokia HERE %1."
647−msgstr "ይህን ምርጫ በ መምረጥ እርስዎ ተስማምተዋል ከ Nokia HERE %1."
648−
649−#: qml/Wizard/Pages/40-location.qml:106
650−msgctxt "part of: Nokia HERE terms and conditions"
651−msgid "terms and conditions"
652−msgstr "ስምምነት እና ሁኔታዎች"
653−
654−#: qml/Wizard/Pages/40-location.qml:160
655−msgid "GPS only"
656−msgstr "ጂፒኤስ ብቻ"
657−
658−#: qml/Wizard/Pages/40-location.qml:214
659−msgid "Don't use my location"
660−msgstr "የ እኔን አካባቢ አትጠቀም"
661−
662−#: qml/Wizard/Pages/40-location.qml:260
663−msgid "You can change it later in System Settings."
664−msgstr "እርስዎ በኋላ በ ስርአት ማሰናጃ ውስጥ መቀየር ይችላሉ"
665−
666#: qml/Wizard/Pages/50-timezone.qml:30
628#: qml/Wizard/Pages/50-timezone.qml:30
667msgid "Time Zone"
629msgid "Time Zone"
668msgstr "የ ሰአት ክልል"
630msgstr "የ ሰአት ክልል"
669
631
670−#: qml/Wizard/Pages/50-timezone.qml:182
632+#: qml/Wizard/Pages/50-timezone.qml:183
671msgid "Enter your city"
633msgid "Enter your city"
672msgstr "የ እርስዎን ከተማ ያስገቡ"
634msgstr "የ እርስዎን ከተማ ያስገቡ"
673
635
@@ -675,7 +637,7 @@ msgstr "የ እርስዎን ከተማ ያስገቡ"
675msgid "Personalize Your Device"
637msgid "Personalize Your Device"
676msgstr "የ እርስዎን አካል የ ግልዎ ያድርጉ"
638msgstr "የ እርስዎን አካል የ ግልዎ ያድርጉ"
677
639
678−#: qml/Wizard/Pages/60-account.qml:48
640+#: qml/Wizard/Pages/60-account.qml:54
679msgid "Preferred Name"
641msgid "Preferred Name"
680msgstr "የመረጡት ስም"
642msgstr "የመረጡት ስም"
681
643
@@ -683,112 +645,75 @@ msgstr "የመረጡት ስም"
683msgid "Lock Screen"
645msgid "Lock Screen"
684msgstr "መመልከቻውን መቆለፊያ"
646msgstr "መመልከቻውን መቆለፊያ"
685
647
686−#: qml/Wizard/Pages/70-passwd-type.qml:102
648+#: qml/Wizard/Pages/70-passwd-type.qml:103
687msgctxt "Label: Type of security method"
649msgctxt "Label: Type of security method"
688msgid "Create new password"
650msgid "Create new password"
689msgstr "አዲስ የ መግቢያ ቃል መፍጠሪያ"
651msgstr "አዲስ የ መግቢያ ቃል መፍጠሪያ"
690
652
691−#: qml/Wizard/Pages/70-passwd-type.qml:104
653+#: qml/Wizard/Pages/70-passwd-type.qml:105
692msgctxt "Label: Type of security method"
654msgctxt "Label: Type of security method"
693msgid "Create passcode (numbers only)"
655msgid "Create passcode (numbers only)"
694msgstr "አዲስ የ ማለፊያ ኮድ መፍጠሪያ (ቁጥር ብቻ)"
656msgstr "አዲስ የ ማለፊያ ኮድ መፍጠሪያ (ቁጥር ብቻ)"
695
657
696−#: qml/Wizard/Pages/70-passwd-type.qml:106
658+#: qml/Wizard/Pages/70-passwd-type.qml:107
697msgctxt "Label: Type of security method"
659msgctxt "Label: Type of security method"
698msgid "No lock code"
660msgid "No lock code"
699msgstr "ምንም መቆለፊያ ኮድ አልተገኘም"
661msgstr "ምንም መቆለፊያ ኮድ አልተገኘም"
700
662
701−#: qml/Wizard/Pages/75-report-check.qml:26 qml/Wizard/Pages/here-terms.qml:108
702−msgid "Privacy Policy"
703−msgstr "የ ግላዊነት መመሪያ"
704−
705−#: qml/Wizard/Pages/75-report-check.qml:26
706−msgid "Help Us Improve"
707−msgstr "እንድንሻሻል ይርዱን"
708−
709−#: qml/Wizard/Pages/75-report-check.qml:59
710−msgid "Improve system performance by sending us crashes and error reports."
711−msgstr "የ ስርአቱን አፈጻጸም ለማሻሻል የ ግጭት እና የ ስህተት መልእክቶች ይላኩልን"
712−
713−#: qml/Wizard/Pages/75-report-check.qml:60
714−msgid "Privacy policy"
715−msgstr "የ ግላዊነት መመሪያ"
716−
717−#: qml/Wizard/Pages/77-system-update.qml:28
663+#: qml/Wizard/Pages/79-system-update.qml:28
718msgid "Update Device"
664msgid "Update Device"
719msgstr "አካሉን ማሻሻያ"
665msgstr "አካሉን ማሻሻያ"
720
666
721−#: qml/Wizard/Pages/77-system-update.qml:55
667+#: qml/Wizard/Pages/79-system-update.qml:55
722msgid ""
668msgid ""
723"There is a system update available and ready to install. Afterwards, the "
669"There is a system update available and ready to install. Afterwards, the "
724"device will automatically restart."
670"device will automatically restart."
725msgstr "የ ስርአት ማሻሻያ ዝግጁ ነው ለ መግጠም: ከዚያ በኋላ አካሉ ራሱ በራሱ እንደገና ይጀምራል"
671msgstr "የ ስርአት ማሻሻያ ዝግጁ ነው ለ መግጠም: ከዚያ በኋላ አካሉ ራሱ በራሱ እንደገና ይጀምራል"
726
672
727−#: qml/Wizard/Pages/77-system-update.qml:76
673+#: qml/Wizard/Pages/79-system-update.qml:76
674+#, fuzzy
675+#| msgctxt "string identifying name of the update"
676+#| msgid "Ubuntu system"
728msgctxt "string identifying name of the update"
677msgctxt "string identifying name of the update"
729−msgid "Ubuntu system"
678+msgid "Ubuntu Touch system"
730msgstr "የ ኡቡቱ ስርአት"
679msgstr "የ ኡቡቱ ስርአት"
731
680
732−#: qml/Wizard/Pages/77-system-update.qml:83
681+#: qml/Wizard/Pages/79-system-update.qml:83
733#, qt-format
682#, qt-format
734msgctxt "version of the system update"
683msgctxt "version of the system update"
735msgid "Version %1"
684msgid "Version %1"
736msgstr "እትም %1"
685msgstr "እትም %1"
737
686
738−#: qml/Wizard/Pages/77-system-update.qml:102
687+#: qml/Wizard/Pages/79-system-update.qml:95
739msgid "This could take a few minutes..."
688msgid "This could take a few minutes..."
740−msgstr "ይህ ትንሽ ደቂቆች ይወስዳል"
689+msgstr "ይህ ትንሽ ደቂቃዎች ይወስዳል..."
741
690
742−#: qml/Wizard/Pages/77-system-update.qml:116
691+#: qml/Wizard/Pages/79-system-update.qml:109
743msgid "Install and restart now"
692msgid "Install and restart now"
744msgstr "መግጠሚያ እና እንደገና ማስጀመሪያ"
693msgstr "መግጠሚያ እና እንደገና ማስጀመሪያ"
745
694
746−#: qml/Wizard/Pages/80-finished.qml:89
695+#: qml/Wizard/Pages/80-finished.qml:91
696+msgid "Welcome Back"
697+msgstr "እንኳን ደህና መጡ:"
698+
699+#: qml/Wizard/Pages/80-finished.qml:91
747msgid "Welcome to Ubuntu"
700msgid "Welcome to Ubuntu"
748msgstr "እንኳን ደህና መጡ ወደ ኡቡንቱ"
701msgstr "እንኳን ደህና መጡ ወደ ኡቡንቱ"
749
702
750−#: qml/Wizard/Pages/80-finished.qml:104
703+#: qml/Wizard/Pages/80-finished.qml:106
751msgid "You are ready to use your device now"
704msgid "You are ready to use your device now"
752msgstr "የ እርስዎን አካል ለ መጠቀም አሁን ዝግጁ ነው"
705msgstr "የ እርስዎን አካል ለ መጠቀም አሁን ዝግጁ ነው"
753
706
754−#: qml/Wizard/Pages/80-finished.qml:124
707+#: qml/Wizard/Pages/80-finished.qml:126
708+msgid "Continue"
709+msgstr "ይቀጥሉ"
710+
711+#: qml/Wizard/Pages/80-finished.qml:126
755msgid "Get Started"
712msgid "Get Started"
756msgstr "እንጀምር"
713msgstr "እንጀምር"
757
714
758−#: qml/Wizard/Pages/here-terms.qml:25
759−msgid "Terms & Conditions"
760−msgstr "ውሎች & ሁኔታዎች"
761−
762−#: qml/Wizard/Pages/here-terms.qml:69
763−msgid "Your device uses positioning technologies provided by HERE."
764−msgstr "የ እርስዎ አካል ይጠቀማል ያሉበትን አካባቢ ቴክኖሎጂ የቀረበውን በ HERE."
765−
766−#: qml/Wizard/Pages/here-terms.qml:81
767−msgid ""
768−"To provide you with positioning services and to improve their quality, HERE "
769−"collects information about nearby cell towers and Wi-Fi hotspots around your "
770−"current location whenever your position is being found."
771−msgstr ""
772−"እርስዎ ያሉበትን አካባቢ ግልጋሎት ለ መስጠት እና ለማሻሻል: HERE መረጃ ይሰበስባል በቅርብ ያሉ የ ሴል ምሶሶዎች እና "
773−"ዋይ-ፋይ hotspots እርስዎ አሁን ያሉበትን አካባቢ ሲታወቅ"
774−
775−#: qml/Wizard/Pages/here-terms.qml:93
776−msgid ""
777−"The information collected is used to analyze the service and to improve the "
778−"use of service, but not to identify you personally."
779−msgstr "የ ተሰበሰበው መረጃ የሚጠቅመው ግልጋሎት ለማሻሻል ነው: ነገር ግን እርስዎን በ ግል ለ መለየት አይደለም"
780−
781−#: qml/Wizard/Pages/here-terms.qml:106
782−#, qt-format
783−msgid "By continuing, you agree to the HERE platform %1 and %2."
784−msgstr "በ መቀጠል እርስዎ ተስማምተዋል ከ HERE platform %1 እና %2."
785−
786−#: qml/Wizard/Pages/here-terms.qml:107
787−msgid "Service Terms"
788−msgstr "የ ግልጋሎት ስምምነቶች"
789−
790#: qml/Wizard/Pages/passcode-confirm.qml:43
715#: qml/Wizard/Pages/passcode-confirm.qml:43
791−#: qml/Wizard/Pages/passcode-desktop.qml:97
716+#: qml/Wizard/Pages/passcode-desktop.qml:104
792msgid "Confirm passcode"
717msgid "Confirm passcode"
793msgstr "የማለፊያ ኮድ ማረጋገጫ"
718msgstr "የማለፊያ ኮድ ማረጋገጫ"
794
719
@@ -805,24 +730,24 @@ msgstr "እባክዎን እንደገና-ያስገቡ"
805msgid "Lock Screen Passcode"
730msgid "Lock Screen Passcode"
806msgstr "የ መመልከቻው መቆለፊያ የ ማለፊያ ኮድ"
731msgstr "የ መመልከቻው መቆለፊያ የ ማለፊያ ኮድ"
807
732
808−#: qml/Wizard/Pages/passcode-desktop.qml:61
733+#: qml/Wizard/Pages/passcode-desktop.qml:68
809msgid "Enter 4 numbers to setup your passcode"
734msgid "Enter 4 numbers to setup your passcode"
810msgstr "ያስገቡ 4 ቁጥሮች የ እርስዎን የ ማለፊያ ኮድ ለማሰናዳት"
735msgstr "ያስገቡ 4 ቁጥሮች የ እርስዎን የ ማለፊያ ኮድ ለማሰናዳት"
811
736
812−#: qml/Wizard/Pages/passcode-desktop.qml:77
737+#: qml/Wizard/Pages/passcode-desktop.qml:84
813#: qml/Wizard/Pages/passcode-set.qml:54
738#: qml/Wizard/Pages/passcode-set.qml:54
814msgid "Choose passcode"
739msgid "Choose passcode"
815msgstr "የ ማለፊያ ኮድ ይምረጡ"
740msgstr "የ ማለፊያ ኮድ ይምረጡ"
816
741
817−#: qml/Wizard/Pages/passcode-desktop.qml:127
742+#: qml/Wizard/Pages/passcode-desktop.qml:139
818msgid "Passcode too short"
743msgid "Passcode too short"
819msgstr "የ ማለፊያ ኮዱ በጣም አጭር ነው"
744msgstr "የ ማለፊያ ኮዱ በጣም አጭር ነው"
820
745
821−#: qml/Wizard/Pages/passcode-desktop.qml:129
746+#: qml/Wizard/Pages/passcode-desktop.qml:141
822msgid "Passcodes match"
747msgid "Passcodes match"
823msgstr "የ ማለፊያ ኮዶቹ ተመሳሳይ ናቸው"
748msgstr "የ ማለፊያ ኮዶቹ ተመሳሳይ ናቸው"
824
749
825−#: qml/Wizard/Pages/passcode-desktop.qml:131
750+#: qml/Wizard/Pages/passcode-desktop.qml:143
826msgid "Passcodes do not match"
751msgid "Passcodes do not match"
827msgstr "የ ማለፊያ ኮዶቹ አይመሳሰሉም"
752msgstr "የ ማለፊያ ኮዶቹ አይመሳሰሉም"
828
753
@@ -834,15 +759,15 @@ msgstr "የ ማለፊያ ኮድ 4 ባህሪዎች መሆን አለበት"
834msgid "Lock Screen Password"
759msgid "Lock Screen Password"
835msgstr "የ መመልከቻ መቆለፊያ የ መግቢያ ቃል"
760msgstr "የ መመልከቻ መቆለፊያ የ መግቢያ ቃል"
836
761
837−#: qml/Wizard/Pages/password-set.qml:62
762+#: qml/Wizard/Pages/password-set.qml:68
838msgid "Enter at least 8 characters"
763msgid "Enter at least 8 characters"
839msgstr "ያስገቡ ቢያንስ 8 ባህሪዎች"
764msgstr "ያስገቡ ቢያንስ 8 ባህሪዎች"
840
765
841−#: qml/Wizard/Pages/password-set.qml:74
766+#: qml/Wizard/Pages/password-set.qml:80
842msgid "Choose password"
767msgid "Choose password"
843msgstr "የ መግቢያ ቃል ይምረጡ"
768msgstr "የ መግቢያ ቃል ይምረጡ"
844
769
845−#: qml/Wizard/Pages/password-set.qml:104
770+#: qml/Wizard/Pages/password-set.qml:110
846msgid "Confirm password"
771msgid "Confirm password"
847msgstr "የ መግቢያ ቃሉን ያረጋግጡ"
772msgstr "የ መግቢያ ቃሉን ያረጋግጡ"
848
773
@@ -898,6 +823,217 @@ msgstr "ደካማ የ መግቢያ ቃል"
898msgid "Very weak password"
823msgid "Very weak password"
899msgstr "በጣም ደካማ የ መግቢያ ቃል"
824msgstr "በጣም ደካማ የ መግቢያ ቃል"
900
825
826+#~ msgid "Unity 8"
827+#~ msgstr "Unity 8"
828+
829+#~ msgid "Scopes"
830+#~ msgstr "ክልል"
831+
832+#~ msgid "Super (Tap)"
833+#~ msgstr "Super (Tap)"
834+
835+#~ msgid "Opens the Scopes home."
836+#~ msgstr "የ ክልል ቤት መክፈቻ"
837+
838+#~ msgctxt "Button: Open the Ubuntu Store"
839+#~ msgid "Store"
840+#~ msgstr "ማስቀመጫ"
841+
842+#~ msgctxt "Button: Start a search in the current dash scope"
843+#~ msgid "Search"
844+#~ msgstr "መፈለጊያ"
845+
846+#~ msgctxt "Button: Show the current dash scope settings"
847+#~ msgid "Settings"
848+#~ msgstr "ማሰናጃዎች"
849+
850+#~ msgid "Remove from Favorites"
851+#~ msgstr "ከምወዳቸው ማስወገጃ"
852+
853+#~ msgid "Add to Favorites"
854+#~ msgstr "ወደምወዳቸው መጨመሪያ"
855+
856+#~ msgid "Refine your results"
857+#~ msgstr "የ እርስዎን ውጤቶች እንደገና ማጣሪያ"
858+
859+#~ msgid "Reset"
860+#~ msgstr "እንደ ነበረ መመለሻ"
861+
862+#~ msgid "Show less"
863+#~ msgstr "ትንሽ ማሳያ"
864+
865+#~ msgid "Show all"
866+#~ msgstr "ሁሉንም ማሳያ"
867+
868+#~ msgctxt "Label: Hint for dash search line edit"
869+#~ msgid "Search"
870+#~ msgstr "መፈለጊያ"
871+
872+#~ msgid "Recent Searches"
873+#~ msgstr "የ ቅርብ ጊዜ ፍለጋዎች"
874+
875+#~ msgid "Clear All"
876+#~ msgstr "ሁሉንም ማጽጃ"
877+
878+#~ msgid "More..."
879+#~ msgstr "ተጨማሪ..."
880+
881+#~ msgid "Less..."
882+#~ msgstr "አነስተኛ..."
883+
884+#~ msgid "Send"
885+#~ msgstr "መላኪያ"
886+
887+#~ msgid "Rate this"
888+#~ msgstr "ለዚህ ደረጃ ይስጡ"
889+
890+#~ msgid "Add a review"
891+#~ msgstr "ግምገማ መጨመሪያ"
892+
893+#~ msgid "Enable location data"
894+#~ msgstr "የአካባቢ ዳታ ማስቻያ"
895+
896+#~ msgid "Manage"
897+#~ msgstr "አስተዳዳሪ"
898+
899+#~ msgid "Home"
900+#~ msgstr "ቤት"
901+
902+#~ msgid "Also installed"
903+#~ msgstr "እንዲሁም የተገጠሙ"
904+
905+#~ msgid "Privacy Policy"
906+#~ msgstr "የ ግላዊነት መመሪያ"
907+
908+#~ msgid "Help Us Improve"
909+#~ msgstr "እንድንሻሻል ይርዱን"
910+
911+#~ msgid "Improve system performance by sending us crashes and error reports."
912+#~ msgstr "የ ስርአቱን አፈጻጸም ለማሻሻል የ ግጭት እና የ ስህተት መልእክቶች ይላኩልን"
913+
914+#~ msgid "Privacy policy"
915+#~ msgstr "የ ግላዊነት መመሪያ"
916+
917+#~ msgid "Update Apps"
918+#~ msgstr "መተግበሪያ ማሻሻያ"
919+
920+#~ msgid "This device is not connected to the internet."
921+#~ msgstr "ይህ አካል ከ ኢንተርኔት ጋር አልተገናኘም:"
922+
923+#~ msgid "Software is up to date"
924+#~ msgstr "ሶፍትዌሩ ዘመናዊ ነው:"
925+
926+#~ msgid "The update server is not responding."
927+#~ msgstr "የ ማሻሻያ ሰርቨር አይመልስም:"
928+
929+#~ msgid "Please wait..."
930+#~ msgstr "እባክዎን ይቆዩ..."
931+
932+#~ msgid "Installation failed"
933+#~ msgstr "መግጠሙ አልተሳካም:"
934+
935+#~ msgid "Checking for updates…"
936+#~ msgstr "ማሻሻያ በ መፈለግ ላይ…"
937+
938+#~ msgid "%1 update available"
939+#~ msgid_plural "%1 updates available"
940+#~ msgstr[0] "%1 ማሻሻያ ዝግጁ ነው:"
941+#~ msgstr[1] "%1 ማሻሻያዎች ዝግጁ ናቸው:"
942+
943+#~ msgid "Update all…"
944+#~ msgstr "ሁሉንም ማሻሻያ…"
945+
946+#~ msgid "Update all"
947+#~ msgstr "ሁሉንም ማሻሻያ"
948+
949+#~ msgid "Update"
950+#~ msgstr "ማሻሻያ"
951+
952+#~ msgid "Download"
953+#~ msgstr "የ ወረደ"
954+
955+#~ msgid "Resume"
956+#~ msgstr "ይቀጥሉ"
957+
958+#~ msgid "Install…"
959+#~ msgstr "መግጠሚያ…"
960+
961+#~ msgid "Install"
962+#~ msgstr "መግጠሚያ"
963+
964+#~ msgid "Open"
965+#~ msgstr "መክፈቻ"
966+
967+#~ msgid "Installing"
968+#~ msgstr "በ መግጠም ላይ"
969+
970+#~ msgid "Waiting to download"
971+#~ msgstr "ለ ማውረድ በ መጠበቅ ላይ"
972+
973+#~ msgid "%1 of %2"
974+#~ msgstr "%1 ከ %2"
975+
976+#~ msgid "Downloaded"
977+#~ msgstr "የ ወረደ"
978+
979+#~ msgid "Installed"
980+#~ msgstr "ተገጥሟል:"
981+
982+#~ msgid "Updated %1"
983+#~ msgstr "ተሻሽሏል %1"
984+
985+#~ msgid "Update failed"
986+#~ msgstr "ማሻሻል አልተቻለም:"
987+
988+#~ msgid "Location Services"
989+#~ msgstr "የ አካባቢ ግልጋሎት"
990+
991+#~ msgid ""
992+#~ "Use GPS, Wi-Fi hotspots and mobile network anonymously to detect location "
993+#~ "(recommended)"
994+#~ msgstr "ይጠቀሙ ጂፒኤስ: ዋይ-ፋይ: hotspots እና ተንቀሳቃሽ ኔትዎርክ ሳይታወቁ አካባቢ ለማግኘት (ይመከራሉ)"
995+
996+#~ msgid "By selecting this option you agree to the Nokia HERE %1."
997+#~ msgstr "ይህን ምርጫ በ መምረጥ እርስዎ ተስማምተዋል ከ Nokia HERE %1."
998+
999+#~ msgctxt "part of: Nokia HERE terms and conditions"
1000+#~ msgid "terms and conditions"
1001+#~ msgstr "ስምምነት እና ሁኔታዎች"
1002+
1003+#~ msgid "GPS only"
1004+#~ msgstr "ጂፒኤስ ብቻ"
1005+
1006+#~ msgid "Don't use my location"
1007+#~ msgstr "የ እኔን አካባቢ አትጠቀም"
1008+
1009+#~ msgid "You can change it later in System Settings."
1010+#~ msgstr "እርስዎ በኋላ በ ስርአት ማሰናጃ ውስጥ መቀየር ይችላሉ"
1011+
1012+#~ msgid "Terms & Conditions"
1013+#~ msgstr "ውሎች & ሁኔታዎች"
1014+
1015+#~ msgid "Your device uses positioning technologies provided by HERE."
1016+#~ msgstr "የ እርስዎ አካል ይጠቀማል ያሉበትን አካባቢ ቴክኖሎጂ የቀረበውን በ HERE."
1017+
1018+#~ msgid ""
1019+#~ "To provide you with positioning services and to improve their quality, "
1020+#~ "HERE collects information about nearby cell towers and Wi-Fi hotspots "
1021+#~ "around your current location whenever your position is being found."
1022+#~ msgstr ""
1023+#~ "እርስዎ ያሉበትን አካባቢ ግልጋሎት ለ መስጠት እና ለማሻሻል: HERE መረጃ ይሰበስባል በቅርብ ያሉ የ ሴል ምሶሶዎች "
1024+#~ "እና ዋይ-ፋይ hotspots እርስዎ አሁን ያሉበትን አካባቢ ሲታወቅ"
1025+
1026+#~ msgid ""
1027+#~ "The information collected is used to analyze the service and to improve "
1028+#~ "the use of service, but not to identify you personally."
1029+#~ msgstr "የ ተሰበሰበው መረጃ የሚጠቅመው ግልጋሎት ለማሻሻል ነው: ነገር ግን እርስዎን በ ግል ለ መለየት አይደለም"
1030+
1031+#~ msgid "By continuing, you agree to the HERE platform %1 and %2."
1032+#~ msgstr "በ መቀጠል እርስዎ ተስማምተዋል ከ HERE platform %1 እና %2."
1033+
1034+#~ msgid "Service Terms"
1035+#~ msgstr "የ ግልጋሎት ስምምነቶች"
1036+
901#~ msgid "Top edge"
1037#~ msgid "Top edge"
902#~ msgstr "የላይ ጠርዝ"
1038#~ msgstr "የላይ ጠርዝ"
903
1039
@@ -913,21 +1049,12 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
913#~ msgid "Type or say a command"
1049#~ msgid "Type or say a command"
914#~ msgstr "ትእዛዝ ይናገሩ ወይንም ይጻፉ"
1050#~ msgstr "ትእዛዝ ይናገሩ ወይንም ይጻፉ"
915
1051
916−#~ msgid "Loading. Please Wait..."
917−#~ msgstr "እባክዎን ይቆዩ በመጫን ላይ..."
918−
919−#~ msgid "Speaking..."
920−#~ msgstr "በመናገር ላይ..."
921−
922#~ msgid "Speak Now..."
1052#~ msgid "Speak Now..."
923#~ msgstr "አሁን ይናገሩ..."
1053#~ msgstr "አሁን ይናገሩ..."
924
1054
925#~ msgid "Confirm"
1055#~ msgid "Confirm"
926#~ msgstr "ማረጋገጫ"
1056#~ msgstr "ማረጋገጫ"
927
1057
928−#~ msgid "Search"
929−#~ msgstr "መፈለጊያ"
930−
931#~ msgid "See less"
1058#~ msgid "See less"
932#~ msgstr "አነስተኛ መመልከቻ"
1059#~ msgstr "አነስተኛ መመልከቻ"
933
1060
@@ -970,7 +1097,6 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
970#~ msgid "This will be your last attempt."
1097#~ msgid "This will be your last attempt."
971#~ msgstr "ይህ የመጨረሻ ሙከራዎ ነው"
1098#~ msgstr "ይህ የመጨረሻ ሙከራዎ ነው"
972
1099
973−#, qt-format
974#~ msgid "Sorry, incorrect %1"
1100#~ msgid "Sorry, incorrect %1"
975#~ msgstr "አዝናለሁ: የተሳሳተ%1"
1101#~ msgstr "አዝናለሁ: የተሳሳተ%1"
976
1102
@@ -1011,11 +1137,12 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
1011#~ "You have now mastered the edge gestures and can start using the "
1137#~ "You have now mastered the edge gestures and can start using the "
1012#~ "phone<br><br>Tap on the screen to start"
1138#~ "phone<br><br>Tap on the screen to start"
1013#~ msgstr ""
1139#~ msgstr ""
1014−#~ "አሁን የ ጠርዝ እንቅስቃሴን አውቀዋል እና መጀመር ይችላሉ ስልኩን መጠቀም <br><br>መመልከቻውን መታ ያድርጉ ለመጀመር"
1140+#~ "አሁን የ ጠርዝ እንቅስቃሴን አውቀዋል እና መጀመር ይችላሉ ስልኩን መጠቀም <br><br>መመልከቻውን መታ ያድርጉ "
1141+#~ "ለመጀመር"
1015
1142
1016#~ msgid ""
1143#~ msgid ""
1017−#~ "If passcode is entered incorrectly, your phone will conduct a factory reset "
1144+#~ "If passcode is entered incorrectly, your phone will conduct a factory "
1018−#~ "and all personal data will be deleted."
1145+#~ "reset and all personal data will be deleted."
1019#~ msgstr ""
1146#~ msgstr ""
1020#~ "የተሳሳተ የማለፊያ ኮድ ካስገቡ የ እርስዎ ስልክ ወደ ነበረበት ወደ ፋክቶሪው ዘዴ ይመለሳል: እና የ ግል ዳታ በሙሉ "
1147#~ "የተሳሳተ የማለፊያ ኮድ ካስገቡ የ እርስዎ ስልክ ወደ ነበረበት ወደ ፋክቶሪው ዘዴ ይመለሳል: እና የ ግል ዳታ በሙሉ "
1021#~ "ይጠፋል"
1148#~ "ይጠፋል"
@@ -1024,8 +1151,8 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
1024#~ "If passphrase is entered incorrectly, your phone will conduct a factory "
1151#~ "If passphrase is entered incorrectly, your phone will conduct a factory "
1025#~ "reset and all personal data will be deleted."
1152#~ "reset and all personal data will be deleted."
1026#~ msgstr ""
1153#~ msgstr ""
1027−#~ "የተሳሳተ የማለፊያ ሐረግ ካስገቡ የ እርስዎ ስልክ ወደ ነበረበት ወደ ፋክቶሪው ዘዴ ይመለሳል: እና የ ግል ዳታ በሙሉ "
1154+#~ "የተሳሳተ የማለፊያ ሐረግ ካስገቡ የ እርስዎ ስልክ ወደ ነበረበት ወደ ፋክቶሪው ዘዴ ይመለሳል: እና የ ግል ዳታ "
1028−#~ "ይጠፋል"
1155+#~ "በሙሉ ይጠፋል"
1029
1156
1030#~ msgid "Roaming"
1157#~ msgid "Roaming"
1031#~ msgstr "ማዘዋወሪያ"
1158#~ msgstr "ማዘዋወሪያ"
@@ -1033,19 +1160,12 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
1033#~ msgid "Manage Scopes"
1160#~ msgid "Manage Scopes"
1034#~ msgstr "የ ክልል አስተዳዳሪ"
1161#~ msgstr "የ ክልል አስተዳዳሪ"
1035
1162
1036−#, qt-format
1037#~ msgid "Enter %1"
1163#~ msgid "Enter %1"
1038#~ msgstr "ማስገቢያ %1"
1164#~ msgstr "ማስገቢያ %1"
1039
1165
1040#~ msgid "Please re-enter"
1166#~ msgid "Please re-enter"
1041#~ msgstr "እባክዎን እንደገና-ያስገቡ"
1167#~ msgstr "እባክዎን እንደገና-ያስገቡ"
1042
1168
1043−#~ msgid "Enter passphrase"
1044−#~ msgstr "የ ማለፊያ ሀረግ ያስገቡ"
1045−
1046−#~ msgid "Enter passcode"
1047−#~ msgstr "የ ማለፊያ ኮድ ያስገቡ"
1048−
1049#~ msgid "Lock security"
1169#~ msgid "Lock security"
1050#~ msgstr "ደህንነት መቆለፊያ"
1170#~ msgstr "ደህንነት መቆለፊያ"
1051
1171
@@ -1068,7 +1188,8 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
1068#~ "This can be disabled in <b>System Settings</b> under <b>Security &amp; "
1188#~ "This can be disabled in <b>System Settings</b> under <b>Security &amp; "
1069#~ "Privacy</b>"
1189#~ "Privacy</b>"
1070#~ msgstr ""
1190#~ msgstr ""
1071−#~ "ይህን ግልጋሎት ማሰናከል ይችላሉ ከ <b>ስርአት ማሰናጃዎች</b> under <b>Security &amp; Privacy</b>"
1191+#~ "ይህን ግልጋሎት ማሰናከል ይችላሉ ከ <b>ስርአት ማሰናጃዎች</b> under <b>Security &amp; "
1192+#~ "Privacy</b>"
1072
1193
1073#~ msgid "Nice work!"
1194#~ msgid "Nice work!"
1074#~ msgstr "ጥሩ ሰርተዋል!"
1195#~ msgstr "ጥሩ ሰርተዋል!"
@@ -1103,9 +1224,6 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
1103#~ msgid "Let’s get started."
1224#~ msgid "Let’s get started."
1104#~ msgstr "ስራ እንጀምር"
1225#~ msgstr "ስራ እንጀምር"
1105
1226
1106−#~ msgid "Continue"
1107−#~ msgstr "ይቀጥሉ"
1108−
1109#~ msgid "Passphrase must be 4 characters long"
1227#~ msgid "Passphrase must be 4 characters long"
1110#~ msgstr "የ ማለፊያ ሀረግ ቢያንስ 4 ባህሪዎች መሆን አለበት"
1228#~ msgstr "የ ማለፊያ ሀረግ ቢያንስ 4 ባህሪዎች መሆን አለበት"
1111
1229
@@ -1115,11 +1233,9 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
1115#~ msgid "Let the phone detect your location:"
1233#~ msgid "Let the phone detect your location:"
1116#~ msgstr "እባክዎን እርስዎ ያሉበትን አካባቢ ስልኩ እራሱ ይፈልግ:"
1234#~ msgstr "እባክዎን እርስዎ ያሉበትን አካባቢ ስልኩ እራሱ ይፈልግ:"
1117
1235
1118−#, qt-format
1119#~ msgid "〈 %1"
1236#~ msgid "〈 %1"
1120#~ msgstr "〈 %1"
1237#~ msgstr "〈 %1"
1121
1238
1122−#, qt-format
1123#~ msgid "%1 〉"
1239#~ msgid "%1 〉"
1124#~ msgstr "%1 〉"
1240#~ msgstr "%1 〉"
1125
1241
@@ -1136,12 +1252,12 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
1136#~ "Your phone is set up to automatically report errors to Canonical and its "
1252#~ "Your phone is set up to automatically report errors to Canonical and its "
1137#~ "partners, the makers of the operating system."
1253#~ "partners, the makers of the operating system."
1138#~ msgstr ""
1254#~ msgstr ""
1139−#~ "የ እርስዎ ስልክ ራሱ በራሱ የ ስህተት መግለጫዎች ወደ Canonical and its partners, የ መስሪያ ስርአት "
1255+#~ "የ እርስዎ ስልክ ራሱ በራሱ የ ስህተት መግለጫዎች ወደ Canonical and its partners, የ መስሪያ "
1140−#~ "አምራቾች"
1256+#~ "ስርአት አምራቾች"
1141
1257
1142#~ msgid ""
1258#~ msgid ""
1143−#~ "By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
1259+#~ "By selecting this option you agree to the Nokia HERE <a href='#'>terms "
1144−#~ "conditions</a>."
1260+#~ "and conditions</a>."
1145#~ msgstr "ይህን ምርጫ በመምረጥ ተስማምቻለሁ ከ Nokia HERE <a href='#'>ውሎች እና ሁኔታዎች ጋር</a>."
1261#~ msgstr "ይህን ምርጫ በመምረጥ ተስማምቻለሁ ከ Nokia HERE <a href='#'>ውሎች እና ሁኔታዎች ጋር</a>."
1146
1262
1147#~ msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
1263#~ msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
@@ -1156,9 +1272,6 @@ msgstr "በጣም ደካማ የ መግቢያ ቃል"
1156#~ msgid "Short swipe from the left edge."
1272#~ msgid "Short swipe from the left edge."
1157#~ msgstr "ከ ግራ ጠርዝ በኩል በትንሹ ይጥረጉ"
1273#~ msgstr "ከ ግራ ጠርዝ በኩል በትንሹ ይጥረጉ"
1158
1274
1159−#~ msgid "Open the launcher"
1160−#~ msgstr "ማስጀመሪያውን መክፈቻ"
1161−
1162#~ msgid "Tap here to continue."
1275#~ msgid "Tap here to continue."
1163#~ msgstr "እዚህ መታ ይድርጉ ለመቀጠል"
1276#~ msgstr "እዚህ መታ ይድርጉ ለመቀጠል"
1164
1277
po/ar.po+516−489

This diff is too big to show here.

po/ast.po+571−476

This diff is too big to show here.

po/az.po+271−323

This diff is too big to show here.

po/be.po+472−484

This diff is too big to show here.

po/bg.po+248−318

This diff is too big to show here.

po/bn.po+447−470

This diff is too big to show here.

po/br.po+414−352

This diff is too big to show here.

po/bs.po+318−337

This diff is too big to show here.

po/ca.po+499−380

This diff is too big to show here.

po/ca@valencia.po+303−343

This diff is too big to show here.

po/ce.po+222−301

This diff is too big to show here.

po/ckb.po+242−315

This diff is too big to show here.

po/co.po+389−332

This diff is too big to show here.

po/cs.po+566−485

This diff is too big to show here.

po/cy.po+318−336

This diff is too big to show here.

po/da.po+393−410

This diff is too big to show here.

po/de.po+503−384

This diff is too big to show here.

po/el.po+483−439

This diff is too big to show here.

po/en_AU.po+556−414

This diff is too big to show here.

po/en_GB.po+452−354

This diff is too big to show here.

po/en_US.ponew file+814

This diff is too big to show here.

po/eo.po+287−328

This diff is too big to show here.

po/es.po+511−393

This diff is too big to show here.

po/et.ponew file+816

This diff is too big to show here.

po/eu.po+323−345

This diff is too big to show here.

po/fa.po+522−379

This diff is too big to show here.

po/fi.po+484−365

This diff is too big to show here.

po/fo.po+294−321

This diff is too big to show here.

po/fr.po+544−424

This diff is too big to show here.

po/ga.po+311−335

This diff is too big to show here.

po/gd.po+468−370

This diff is too big to show here.

po/gl.po+499−379

This diff is too big to show here.

po/gu.ponew file+814

This diff is too big to show here.

po/gv.po+260−313

This diff is too big to show here.

po/he.po+443−465

This diff is too big to show here.

po/hi.po+236−311

This diff is too big to show here.

po/hr.po+464−394

This diff is too big to show here.

po/hu.po+508−393

This diff is too big to show here.

po/hy.po+348−406

This diff is too big to show here.

po/ia.po+459−359

This diff is too big to show here.

po/id.po+303−342

This diff is too big to show here.

po/is.po+281−336

This diff is too big to show here.

po/it.po+508−389

This diff is too big to show here.

po/ja.po+352−390

This diff is too big to show here.

po/kk.ponew file+841

This diff is too big to show here.

po/km.po+319−343

This diff is too big to show here.

po/kn.po+230−311

This diff is too big to show here.

po/ko.po+455−458

This diff is too big to show here.

po/ku.po+269−320

This diff is too big to show here.

po/ky.po+230−311

This diff is too big to show here.

po/lo.po+229−308

This diff is too big to show here.

po/lt.po+506−419

This diff is too big to show here.

po/lv.po+331−352

This diff is too big to show here.

po/ml.po+243−317

This diff is too big to show here.

po/mr.po+245−306

This diff is too big to show here.

po/ms.po+421−365

This diff is too big to show here.

po/my.po+280−316

This diff is too big to show here.

po/nb.po+486−369

This diff is too big to show here.

po/ne.po+226−302

This diff is too big to show here.

po/nl.po+571−455

This diff is too big to show here.

po/nn.po+222−301

This diff is too big to show here.

po/oc.po+413−341

This diff is too big to show here.

po/or.po+222−301

This diff is too big to show here.

po/pa.po+327−348

This diff is too big to show here.

po/pl.po+514−396

This diff is too big to show here.

po/pt.po+504−386

This diff is too big to show here.

po/pt_BR.po+437−444

This diff is too big to show here.

po/ro.po+333−356

This diff is too big to show here.

po/ru.po+506−381

This diff is too big to show here.

po/sc.ponew file+904

This diff is too big to show here.

po/shn.po+222−301

This diff is too big to show here.

po/si.po+249−321

This diff is too big to show here.

po/sk.po+498−378

This diff is too big to show here.

po/sl.po+445−358

This diff is too big to show here.

po/sq.po+310−323

This diff is too big to show here.

po/sr.po+460−362

This diff is too big to show here.

po/sv.po+481−364

This diff is too big to show here.

po/sw.po+296−329

This diff is too big to show here.

po/ta.po+242−316

This diff is too big to show here.

po/te.po+250−315

This diff is too big to show here.

po/th.po+241−319

This diff is too big to show here.

po/tr.po+518−368

This diff is too big to show here.

po/ug.po+370−352

This diff is too big to show here.

po/uk.po+463−368

This diff is too big to show here.

po/unity8.pot+220−300

This diff is too big to show here.

po/update-unity-pot+17

This diff is too big to show here.

po/ur.po+244−320

This diff is too big to show here.

po/uz.po+222−301

This diff is too big to show here.

po/vi.po+222−301

This diff is too big to show here.

po/wo.po+327−349

This diff is too big to show here.

po/xh.po+222−301

This diff is too big to show here.

po/zh_CN.po+495−376

This diff is too big to show here.

po/zh_HK.po+259−316

This diff is too big to show here.

po/zh_TW.po+505−426

This diff is too big to show here.

qml/ApplicationMenus/ApplicationMenuItemFactory.qmlnew file+135

This diff is too big to show here.

tests/plugins/ImageCache/test.qml → qml/ApplicationMenus/ApplicationMenusLimits.qmlrenamed+6−5

This diff is too big to show here.

qml/ApplicationMenus/MenuBar.qmlnew file+513

This diff is too big to show here.

qml/ApplicationMenus/MenuItem.qmlnew file+146

This diff is too big to show here.

qml/ApplicationMenus/MenuNavigator.qmlnew file+101

This diff is too big to show here.

qml/ApplicationMenus/MenuPopup.qmlnew file+510

This diff is too big to show here.

qml/ApplicationMenus/RegisteredApplicationMenuModel.qmlnew file+56

This diff is too big to show here.

qml/ApplicationMenus/qmldirnew file+1

This diff is too big to show here.

qml/CMakeLists.txt+1−1

This diff is too big to show here.

qml/Components/Background.qml+1−1

This diff is too big to show here.

qml/Components/BaseCarouselDelegate.qmldeleted−42

This diff is too big to show here.

qml/Components/BrightnessControl.qmlnew file+54

This diff is too big to show here.

qml/Components/Carousel.qmldeleted−335

This diff is too big to show here.

qml/Components/Dialogs.qml+93−27

This diff is too big to show here.

qml/Components/DragHandle.qml+1

This diff is too big to show here.

qml/Components/EdgeBarrier.qml+1

This diff is too big to show here.

qml/Components/EdgeBarrierController.qml+1

This diff is too big to show here.

qml/Panel/Handle.qml → qml/Components/Handle.qmlrenamed+1

This diff is too big to show here.

qml/Components/InputMethod.qml+6−4

This diff is too big to show here.

qml/Components/ItemGrabber.qml+1−1

This diff is too big to show here.

qml/Components/KeyboardShortcutsOverlay.qml+19−17

This diff is too big to show here.

qml/Components/KeymapSwitcher.qml+19−8

This diff is too big to show here.

qml/Components/LazyImage.qml+2

This diff is too big to show here.

qml/Components/Lockscreen.qml+5−1

This diff is too big to show here.

qml/Components/ModeSwitchWarningDialog.qml+8−2

This diff is too big to show here.

qml/Components/PanelState/PanelState.qml+6−3

This diff is too big to show here.

qml/Components/PhysicalKeysMapper.qml+4−4

This diff is too big to show here.

qml/Components/ResponsiveGridView.qmldeleted−98

This diff is too big to show here.

qml/Components/ResponsiveVerticalJournal.qmldeleted−81

This diff is too big to show here.

qml/Components/ScrollCalculator.qml+21−2

This diff is too big to show here.

qml/Components/ShellDialog.qml+27−12

This diff is too big to show here.

qml/Components/Showable.qml+15−12

This diff is too big to show here.

qml/Components/VirtualTouchPad.qml+269−1

This diff is too big to show here.

qml/Components/Wallpaper.qml+68−16

This diff is too big to show here.

qml/Components/WallpaperResolver.qml+17−6

This diff is too big to show here.

qml/Components/WindowControlButtons.qml+2

This diff is too big to show here.

qml/Dash/CardCarousel.qmldeleted−74

This diff is too big to show here.

qml/Dash/CardGrid.qmldeleted−84

This diff is too big to show here.

qml/Dash/CardHorizontalList.qmldeleted−66

This diff is too big to show here.

qml/Dash/CardTool.qmldeleted−298

This diff is too big to show here.

qml/Dash/CardVerticalJournal.qmldeleted−85

This diff is too big to show here.

qml/Dash/Dash.qmldeleted−381

This diff is too big to show here.

qml/Dash/DashCategoryBase.qmldeleted−54

This diff is too big to show here.

qml/Dash/DashContent.qmldeleted−211

This diff is too big to show here.

qml/Dash/DashContentTabBarStyle.qmldeleted−24

This diff is too big to show here.

qml/Dash/DashNavigation.qmldeleted−199

This diff is too big to show here.

qml/Dash/DashNavigationHeader.qmldeleted−73

This diff is too big to show here.

qml/Dash/DashNavigationList.qmldeleted−118

This diff is too big to show here.

qml/Dash/DashPageHeader.qmldeleted−448

This diff is too big to show here.

qml/Dash/DashRenderer.qmldeleted−66

This diff is too big to show here.

qml/Dash/DashSectionHeader.qmldeleted−64

This diff is too big to show here.

qml/Dash/Filters/FilterExpandableWidget.qmldeleted−97

This diff is too big to show here.

qml/Dash/Filters/FilterOptionSelector.qmldeleted−97

This diff is too big to show here.

qml/Dash/Filters/FilterRangeInput.qmldeleted−136

This diff is too big to show here.

qml/Dash/Filters/FilterValueSlider.qmldeleted−87

This diff is too big to show here.

qml/Dash/Filters/FilterWidget.qmldeleted−32

This diff is too big to show here.

qml/Dash/Filters/FilterWidgetFactory.qmldeleted−89

This diff is too big to show here.

qml/Dash/FiltersPopover.qmldeleted−106

This diff is too big to show here.

qml/Dash/GenericScopeView.qmldeleted−795

This diff is too big to show here.

qml/Dash/PageHeaderExtraPanel.qmldeleted−202

This diff is too big to show here.

qml/Dash/PreviewView.qmldeleted−76

This diff is too big to show here.

qml/Dash/Previews/Preview.qmldeleted−145

This diff is too big to show here.

qml/Dash/Previews/PreviewActionButton.qmldeleted−27

This diff is too big to show here.

qml/Dash/Previews/PreviewActionCombo.qmldeleted−79

This diff is too big to show here.

qml/Dash/Previews/PreviewActions.qmldeleted−73

This diff is too big to show here.

qml/Dash/Previews/PreviewAudioPlayback.qmldeleted−151

This diff is too big to show here.

qml/Dash/Previews/PreviewComment.qmldeleted−86

This diff is too big to show here.

qml/Dash/Previews/PreviewCommentInput.qmldeleted−88

This diff is too big to show here.

qml/Dash/Previews/PreviewExpandable.qmldeleted−104

This diff is too big to show here.

qml/Dash/Previews/PreviewHeader.qmldeleted−170

This diff is too big to show here.

qml/Dash/Previews/PreviewIconActions.qmldeleted−88

This diff is too big to show here.

qml/Dash/Previews/PreviewImageGallery.qmldeleted−150

This diff is too big to show here.

qml/Dash/Previews/PreviewInlineVideo.qmldeleted−112

This diff is too big to show here.

qml/Dash/Previews/PreviewMediaToolbar.qmldeleted−57

This diff is too big to show here.

qml/Dash/Previews/PreviewOverlay.qmldeleted−166

This diff is too big to show here.

qml/Dash/Previews/PreviewPayments.qmldeleted−81

This diff is too big to show here.

qml/Dash/Previews/PreviewProgress.qmldeleted−74

This diff is too big to show here.

qml/Dash/Previews/PreviewRatingDisplay.qmldeleted−117

This diff is too big to show here.

qml/Dash/Previews/PreviewRatingEdit.qmldeleted−83

This diff is too big to show here.

qml/Dash/Previews/PreviewRatingInput.qmldeleted−207

This diff is too big to show here.

qml/Dash/Previews/PreviewRatingSingleDisplay.qmldeleted−73

This diff is too big to show here.

qml/Dash/Previews/PreviewSingleton/qmldirdeleted−1

This diff is too big to show here.

qml/Dash/Previews/PreviewTable.qmldeleted−91

This diff is too big to show here.

qml/Dash/Previews/PreviewTextSummary.qmldeleted−78

This diff is too big to show here.

qml/Dash/Previews/PreviewVideoPlayback.qmldeleted−87

This diff is too big to show here.

qml/Dash/Previews/PreviewWidget.qmldeleted−60

This diff is too big to show here.

qml/Dash/Previews/PreviewWidgetFactory.qmldeleted−102

This diff is too big to show here.

qml/Dash/Previews/PreviewZoomableImage.qmldeleted−103

This diff is too big to show here.

qml/Dash/ScopeListView.qmldeleted−28

This diff is too big to show here.

qml/Dash/ScopeSettings/ScopeSetting.qmldeleted−46

This diff is too big to show here.

qml/Dash/ScopeSettings/ScopeSettingBoolean.qmldeleted−63

This diff is too big to show here.

qml/Dash/ScopeSettings/ScopeSettingList.qmldeleted−50

This diff is too big to show here.

qml/Dash/ScopeSettings/ScopeSettingString.qmldeleted−89

This diff is too big to show here.

qml/Dash/ScopeSettings/ScopeSettingsWidgetFactory.qmldeleted−61

This diff is too big to show here.

qml/Dash/ScopeSettingsPage.qmldeleted−72

This diff is too big to show here.

qml/Dash/ScopesList.qmldeleted−125

This diff is too big to show here.

qml/Dash/ScopesListCategory.qmldeleted−181

This diff is too big to show here.

qml/Dash/ScopesListCategoryItem.qmldeleted−112

This diff is too big to show here.

qml/Dash/graphics/dark_background.jpgdeleted

Binary file not shown.

qml/Dash/graphics/header_handlearrow2@27.pngdeleted

Binary file not shown.

qml/Dash/graphics/header_handlearrow@27.pngdeleted

Binary file not shown.

qml/Dash/graphics/icon_clear@20.pngdeleted

Binary file not shown.

qml/Dash/graphics/icon_search_active@20.pngdeleted

Binary file not shown.

qml/Dash/graphics/icon_search_inactive@20.pngdeleted

Binary file not shown.

qml/Dash/graphics/navigation_shadow@18.pngdeleted

Binary file not shown.

qml/Dash/graphics/navigation_shadow_light@18.pngdeleted

Binary file not shown.

qml/Dash/graphics/overview_hint@30.pngdeleted

Binary file not shown.

qml/Dash/graphics/pixel.pngdeleted

Binary file not shown.

qml/Dash/graphics/play_button@18.pngdeleted

Binary file not shown.

qml/Dash/graphics/play_button_active@18.pngdeleted

Binary file not shown.

qml/Dash/graphics/play_button_active_big@20.pngdeleted

Binary file not shown.

qml/Dash/graphics/play_button_big@20.pngdeleted

Binary file not shown.

qml/Dash/graphics/tabbarchevron@27.pngdeleted

Binary file not shown.

qml/Dash/graphics/tablet/icon_close_preview@20.pngdeleted

Binary file not shown.

qml/Dash/graphics/tooltip_arrow@27.pngdeleted

Binary file not shown.

qml/DeviceConfiguration.qml+12

This diff is too big to show here.

qml/DisabledScreenNotice.qml+3−55

This diff is too big to show here.

qml/Greeter/ButtonPrompt.qmlnew file+51

This diff is too big to show here.

qml/Greeter/Circle.qml+1−1

This diff is too big to show here.

qml/Greeter/CoverPage.qml+32−11

This diff is too big to show here.

qml/Greeter/FullLightDMImpl.qml+1

This diff is too big to show here.

qml/Greeter/Greeter.qml+79−78

This diff is too big to show here.

qml/Greeter/GreeterPrompt.qml+66−167

This diff is too big to show here.

qml/Greeter/GreeterView.qmlnew file+534

This diff is too big to show here.

qml/Greeter/IntegratedLightDMImpl.qml+1

This diff is too big to show here.

qml/Greeter/LightDMService.qml+4−4

This diff is too big to show here.

qml/Greeter/LoginAreaContainer.qml+3−3

This diff is too big to show here.

qml/Greeter/LoginList.qml+111−191

This diff is too big to show here.

qml/Greeter/NarrowView.qmldeleted−287

This diff is too big to show here.

qml/Greeter/PinPrompt.qmlnew file+184

This diff is too big to show here.

qml/Greeter/PromptList.qmlnew file+151

This diff is too big to show here.

qml/Greeter/SessionsList.qml+36−18

This diff is too big to show here.

qml/Greeter/ShimGreeter.qml+2

This diff is too big to show here.

qml/Greeter/TextPrompt.qmlnew file+198

This diff is too big to show here.

qml/Greeter/WideView.qmldeleted−213

This diff is too big to show here.

qml/Launcher/BackgroundBlur.qmlnew file+81

This diff is too big to show here.

qml/Launcher/Drawer.qmlnew file+413

This diff is too big to show here.

qml/Launcher/DrawerGridView.qmlnew file+85

This diff is too big to show here.

qml/Launcher/Launcher.qml+245−54

This diff is too big to show here.

qml/Launcher/LauncherDelegate.qml+10−12

This diff is too big to show here.

qml/Launcher/LauncherPanel.qml+169−39

This diff is too big to show here.

qml/Dash/PullToRefreshScopeStyle.qml → qml/Launcher/PullToRefreshScopeStyle.qmlrenamed+1−1

This diff is too big to show here.

qml/Launcher/Tooltip.qmlnew file+72

This diff is too big to show here.

qml/Launcher/graphics/home.svgnew file+243

This diff is too big to show here.

qml/Launcher/graphics/home@18.pngdeleted

Binary file not shown.

qml/Launcher/graphics/launcher-app-focus-ring.svgdeleted−12

This diff is too big to show here.

qml/Notifications/Notification.qml+8−3

This diff is too big to show here.

qml/Notifications/NotificationButton.qml+2−1

This diff is too big to show here.

qml/Notifications/SwipeToAct.qml+4

This diff is too big to show here.

qml/OrientedShell.qml+26−7

This diff is too big to show here.

qml/Panel/ActiveCallHint.qml+3

This diff is too big to show here.

qml/Dash/DashBackground.qml → qml/Panel/FakePanelMenu.qmlrenamed+8−3

This diff is too big to show here.

qml/Panel/IndicatorPage.qmldeleted−188

This diff is too big to show here.

qml/Panel/IndicatorItem.qml → qml/Panel/Indicators/IndicatorItem.qmlrenamed+16−3

This diff is too big to show here.

qml/Panel/Indicators/MenuItemFactory.qml → qml/Panel/Indicators/IndicatorMenuItemFactory.qmlrenamed+30−12

This diff is too big to show here.

qml/Panel/Indicators/IndicatorsLight.qml+185−12

This diff is too big to show here.

qml/Panel/Indicators/client/IndicatorRepresentation.qml+18−5

This diff is too big to show here.

qml/Panel/MenuContent.qml+12−12

This diff is too big to show here.

qml/Panel/Panel.qml+372−95

This diff is too big to show here.

qml/Panel/IndicatorsBar.qml → qml/Panel/PanelBar.qmlrenamed+87−36

This diff is too big to show here.

qml/Panel/IndicatorItemRow.qml → qml/Panel/PanelItemRow.qmlrenamed+65−106

This diff is too big to show here.

qml/Panel/IndicatorsMenu.qml → qml/Panel/PanelMenu.qmlrenamed+80−36

This diff is too big to show here.

qml/Panel/PanelMenuPage.qmlnew file+226

This diff is too big to show here.

qml/Rotation/RotationStates.qml+2−5

This diff is too big to show here.

qml/ScopeTool.qmldeleted−165

This diff is too big to show here.

qml/Shell.qml+256−116

This diff is too big to show here.

qml/Stage/AbstractStage.qmldeleted−93

This diff is too big to show here.

qml/Stage/ApplicationWindow.qml+16−4

This diff is too big to show here.

qml/Stage/ChildWindow.qmlnew file+203

This diff is too big to show here.

qml/Stage/ChildWindowRepeater.qmlnew file+39

This diff is too big to show here.

qml/Stage/ChildWindowTree.qmlnew file+138

This diff is too big to show here.

qml/Stage/DecoratedWindow.qml+97−44

This diff is too big to show here.

qml/Stage/DesktopSpread.qmldeleted−576

This diff is too big to show here.

qml/Stage/DesktopSpreadDelegate.qmldeleted−126

This diff is too big to show here.

qml/Stage/FakeMaximizeDelegate.qml+7−7

This diff is too big to show here.

qml/Stage/MoveHandler.qml+54−33

This diff is too big to show here.

qml/Stage/PhoneStage.qmldeleted−806

This diff is too big to show here.

qml/Stage/SideStage.qml+10−6

This diff is too big to show here.

qml/Stage/Splash.qml+4−4

This diff is too big to show here.

qml/Stage/Spread/Spread.qml+11

This diff is too big to show here.

qml/Stage/Spread/WindowedRightEdgeMaths.qml+19−4

This diff is too big to show here.

qml/Stage/SpreadDelegate.qmldeleted−424

This diff is too big to show here.

qml/Stage/SpreadMaths.qmldeleted−164

This diff is too big to show here.

qml/Stage/Stage.qml+591−295

This diff is too big to show here.

qml/Stage/StageMaths.qml+23−9

This diff is too big to show here.

qml/Stage/StagedFullscreenPolicy.qml+7−13

This diff is too big to show here.

qml/Stage/TabletStage.qmldeleted−1201

This diff is too big to show here.

qml/Stage/TopLevelSurfaceRepeater.qmldeleted−67

This diff is too big to show here.

qml/Stage/TransformedSpreadDelegate.qmldeleted−391

This diff is too big to show here.

qml/Stage/TransformedTabletSpreadDelegate.qmldeleted−437

This diff is too big to show here.

qml/Stage/WindowControlsOverlay.qml+23−26

This diff is too big to show here.

qml/Stage/WindowDecoration.qml+87−24

This diff is too big to show here.

qml/Stage/WindowInfoItem.qml+16

This diff is too big to show here.

qml/Stage/WindowResizeArea.qml+15−70

This diff is too big to show here.

qml/Stage/WindowStateSaver.qmlnew file+64

This diff is too big to show here.

qml/Stage/WindowedFullscreenPolicy.qml+4−12

This diff is too big to show here.

qml/Tutorial/Tutorial.qml+1

This diff is too big to show here.

qml/Tutorial/TutorialContent.qml+61−43

This diff is too big to show here.

qml/Tutorial/TutorialLeft.qml+1−1

This diff is too big to show here.

qml/Tutorial/TutorialLeftLong.qml+3−3

This diff is too big to show here.

qml/Tutorial/TutorialRight.qml+2−3

This diff is too big to show here.

qml/Tutorial/TutorialTop.qml+2−2

This diff is too big to show here.

qml/Wizard/Components/InputMethod.qml+9−9

This diff is too big to show here.

qml/Wizard/Page.qml+15

This diff is too big to show here.

qml/Wizard/Pages.qml+90−16

This diff is too big to show here.

qml/Wizard/Pages/10-welcome-update.qmlnew file+137

This diff is too big to show here.

qml/Wizard/Pages/10-welcome.qml+3

This diff is too big to show here.

qml/Wizard/Pages/11-changelog.qmlnew file+95

This diff is too big to show here.

qml/Wizard/Pages/30-wifi.qml+13−10

This diff is too big to show here.

qml/Wizard/Pages/40-location.qmldeleted−290

This diff is too big to show here.

qml/Wizard/Pages/50-timezone.qml+11−12

This diff is too big to show here.

qml/Wizard/Pages/60-account.qml+12−10

This diff is too big to show here.

qml/Wizard/Pages/70-passwd-type.qml+1

This diff is too big to show here.

qml/Wizard/Pages/75-report-check.qmldeleted−91

This diff is too big to show here.

qml/Wizard/Pages/78-firmware-update.qml.disablednew file+246

This diff is too big to show here.

qml/Wizard/Pages/77-system-update.qml → qml/Wizard/Pages/79-system-update.qmlrenamed+4−12

This diff is too big to show here.

qml/Wizard/Pages/80-finished.qml+4−2

This diff is too big to show here.

qml/Wizard/Pages/here-terms.qmldeleted−126

This diff is too big to show here.

qml/Wizard/Pages/passcode-desktop.qml+14−5

This diff is too big to show here.

qml/Wizard/Pages/password-set.qml+12−4

This diff is too big to show here.

qml/Wizard/Wizard.qml+5−2

This diff is too big to show here.

src/ApplicationArguments.h+5

This diff is too big to show here.

src/CMakeLists.txt+15−6

This diff is too big to show here.

src/Dash/CMakeLists.txtdeleted−29

This diff is too big to show here.

src/Dash/main.cppdeleted−146

This diff is too big to show here.

src/DebuggingController.cppnew file+92

This diff is too big to show here.

src/DebuggingController.hnew file+57

This diff is too big to show here.

src/MouseTouchAdaptor.cpp+28−1

This diff is too big to show here.

src/MouseTouchAdaptor.h+1

This diff is too big to show here.

src/Panel/CMakeLists.txtdeleted−3

This diff is too big to show here.

src/Panel/Indicators/CMakeLists.txtdeleted−1

This diff is too big to show here.

src/ShellApplication.cpp+22−5

This diff is too big to show here.

src/ShellApplication.h+1−3

This diff is too big to show here.

src/ShellView.cpp+22

This diff is too big to show here.

src/libunity8-private/CMakeLists.txt+1−1

This diff is too big to show here.

src/libunity8-private/abstractdbusservicemonitor.h+1−1

This diff is too big to show here.

src/main.cpp+10−2

This diff is too big to show here.

tests/CMakeLists.txt+30−11

This diff is too big to show here.

tests/autopilot/unity8/dash.pydeleted−263

This diff is too big to show here.

tests/autopilot/unity8/fixture_setup.py+1−1

This diff is too big to show here.

tests/autopilot/unity8/greeter/__init__.py+1−1

This diff is too big to show here.

tests/autopilot/unity8/shell/__init__.py+2

This diff is too big to show here.

tests/autopilot/unity8/shell/fixture_setup.py+1−27

This diff is too big to show here.

tests/autopilot/unity8/shell/tests/__init__.py+5−2

This diff is too big to show here.

tests/autopilot/unity8/shell/tests/test_helpers.py−122

This diff is too big to show here.

tests/autopilot/unity8/shell/tests/test_notifications.py+9−9

This diff is too big to show here.

tests/autopilot/unity8/shell/tests/test_rotation.py+3−3

This diff is too big to show here.

tests/copyright/check_copyright.sh+35−4

This diff is too big to show here.

tests/data/unity/backgrounds/20x30.pngnew file

Binary file not shown.

tests/data/unity/backgrounds/black.pngnew file

Binary file not shown.

tests/graphics/screenshots/gallery@12.pngnew file

Binary file not shown.

tests/imports/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/AccountsService/AccountsService.cpp+2−39

This diff is too big to show here.

tests/mocks/AccountsService/AccountsService.h+4−27

This diff is too big to show here.

tests/mocks/AccountsService/CMakeLists.txt+4−5

This diff is too big to show here.

tests/mocks/Biometryd/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Biometryd/MockOperation.h−1

This diff is too big to show here.

tests/mocks/CMakeLists.txt+16−16

This diff is too big to show here.

tests/mocks/Cursor/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/GSettings.1.0/CMakeLists.txt+1−2

This diff is too big to show here.

tests/mocks/GSettings.1.0/fake_gsettings.cpp+45−14

This diff is too big to show here.

tests/mocks/GSettings.1.0/fake_gsettings.h+17−8

This diff is too big to show here.

tests/mocks/LightDM/CMakeLists.txtdeleted−1

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txtdeleted−51

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/MockGreeter.cppdeleted−51

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/MockGreeter.hdeleted−42

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cppdeleted−66

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.hdeleted−44

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cppdeleted−44

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/QLightDM/Greeterdeleted−17

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModeldeleted−17

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/QLightDM/UsersModeldeleted−17

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txtdeleted−20

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.cppdeleted−201

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModel.cppdeleted−123

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/plugin.cppdeleted−79

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/plugin.hdeleted−32

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/qmldirdeleted−2

This diff is too big to show here.

tests/mocks/LightDMController/CMakeLists.txtnew file+14

This diff is too big to show here.

tests/mocks/Ubuntu/SystemImage/plugin.cpp → tests/mocks/LightDMController/plugin.cpprenamed+11−10

This diff is too big to show here.

tests/mocks/Ubuntu/Payments/plugin.h → tests/mocks/LightDMController/plugin.hrenamed+4−6

This diff is too big to show here.

tests/mocks/LightDMController/qmldirnew file+2

This diff is too big to show here.

tests/mocks/Lights/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Lights/Lights.h+1−1

This diff is too big to show here.

tests/mocks/MeeGo/QOfono/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Powerd/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Powerd/Powerd.h+2−2

This diff is too big to show here.

tests/mocks/QMenuModel/CMakeLists.txt+1−2

This diff is too big to show here.

tests/mocks/QMenuModel/dbus-enums.h+2−3

This diff is too big to show here.

tests/mocks/QMenuModel/unitymenumodel.cpp+78−13

This diff is too big to show here.

tests/mocks/QMenuModel/unitymenumodel.h+12−1

This diff is too big to show here.

tests/mocks/QtMultimedia/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/QtMultimedia/mediaplayer.cpp+2−2

This diff is too big to show here.

tests/mocks/QtMultimedia/mediaplayer.h+6−6

This diff is too big to show here.

tests/mocks/QtMultimedia/videooutput.h+1−1

This diff is too big to show here.

tests/mocks/SessionBroadcast/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/UInput/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/UInput/mockuinput.h+1−1

This diff is too big to show here.

tests/mocks/Ubuntu/CMakeLists.txt−2

This diff is too big to show here.

tests/mocks/Ubuntu/Connectivity/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Ubuntu/Connectivity/networking-status.cpp+7−1

This diff is too big to show here.

tests/mocks/Ubuntu/Connectivity/networking-status.h+7−6

This diff is too big to show here.

tests/mocks/Ubuntu/DownloadDaemonListener/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Ubuntu/Payments/CMakeLists.txtdeleted−10

This diff is too big to show here.

tests/mocks/Ubuntu/Payments/MockPayments.cppdeleted−95

This diff is too big to show here.

tests/mocks/Ubuntu/Payments/MockPayments.hdeleted−62

This diff is too big to show here.

tests/mocks/Ubuntu/Payments/qmldirdeleted−3

This diff is too big to show here.

tests/mocks/Ubuntu/SystemImage/CMakeLists.txtdeleted−10

This diff is too big to show here.

tests/mocks/Ubuntu/SystemImage/MockSystemImage.cppdeleted−50

This diff is too big to show here.

tests/mocks/Ubuntu/SystemImage/MockSystemImage.hdeleted−56

This diff is too big to show here.

tests/mocks/Ubuntu/SystemImage/qmldirdeleted−3

This diff is too big to show here.

tests/mocks/Ubuntu/SystemSettings/CMakeLists.txt+1

This diff is too big to show here.

tests/mocks/Ubuntu/SystemSettings/SecurityPrivacy/CMakeLists.txt+2−2

This diff is too big to show here.

tests/mocks/Ubuntu/SystemSettings/SecurityPrivacy/MockSecurityPrivacy.h+1−1

This diff is too big to show here.

tests/mocks/Ubuntu/SystemSettings/Update/CMakeLists.txtnew file+1

This diff is too big to show here.

plugins/Ubuntu/Payments/plugin.cpp → tests/mocks/Ubuntu/SystemSettings/Update/MockSystemImage.qmlrenamed+11−9

This diff is too big to show here.

tests/mocks/Ubuntu/SystemSettings/Update/qmldirnew file+2

This diff is too big to show here.

tests/mocks/Ubuntu/Telephony/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Unity/Application/ApplicationInfo.cpp+61−27

This diff is too big to show here.

tests/mocks/Unity/Application/ApplicationInfo.h+8−9

This diff is too big to show here.

tests/mocks/Unity/Application/ApplicationManager.cpp+114−64

This diff is too big to show here.

tests/mocks/Unity/Application/ApplicationManager.h+32−10

This diff is too big to show here.

tests/mocks/Unity/Application/CMakeLists.txt+5−4

This diff is too big to show here.

tests/mocks/Unity/Application/MirMock.cppnew file+62

This diff is too big to show here.

tests/mocks/Unity/fake_valueslidervalues.h → tests/mocks/Unity/Application/MirMock.hrenamed+17−10

This diff is too big to show here.

tests/mocks/Unity/Application/MirSurface.cpp+121−82

This diff is too big to show here.

tests/mocks/Unity/Application/MirSurface.h+45−27

This diff is too big to show here.

tests/mocks/Unity/Application/MirSurfaceItem.cpp+24−9

This diff is too big to show here.

tests/mocks/Unity/Application/MirSurfaceItem.h+1−2

This diff is too big to show here.

tests/mocks/Unity/Application/MirSurfaceListModel.cpp+17−39

This diff is too big to show here.

tests/mocks/Unity/Application/MirSurfaceListModel.h+7−15

This diff is too big to show here.

tests/mocks/Unity/Application/SurfaceManager.cpp+186−31

This diff is too big to show here.

tests/mocks/Unity/Application/SurfaceManager.h+32−10

This diff is too big to show here.

tests/mocks/Unity/Application/VirtualKeyboard.cpp+5−4

This diff is too big to show here.

tests/mocks/Unity/Application/plugin.cpp+6−27

This diff is too big to show here.

tests/mocks/Unity/Application/resources/Kate.qmlnew file+49

This diff is too big to show here.

tests/mocks/Unity/Application/resources/KateDialog.qmlnew file+48

This diff is too big to show here.

qml/Dash/DashApplication.qml → tests/mocks/Unity/Application/resources/KateMenu.qmlrenamed+17−15

This diff is too big to show here.

tests/mocks/Unity/Application/resources/surfaces.qrc+3

This diff is too big to show here.

tests/mocks/Unity/ApplicationMenu/CMakeLists.txtnew file+18

This diff is too big to show here.

tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.cppnew file+47

This diff is too big to show here.

tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.hnew file+41

This diff is too big to show here.

plugins/ImageCache/plugin.cpp → tests/mocks/Unity/ApplicationMenu/plugin.cpprenamed+14−11

This diff is too big to show here.

tests/mocks/Ubuntu/SystemImage/plugin.h → tests/mocks/Unity/ApplicationMenu/plugin.hrenamed+6−5

This diff is too big to show here.

tests/mocks/Unity/ApplicationMenu/qmldirnew file+3

This diff is too big to show here.

tests/mocks/Unity/CMakeLists.txt+2−63

This diff is too big to show here.

tests/mocks/Unity/Connectivity/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Unity/DashCommunicator/CMakeLists.txtdeleted−18

This diff is too big to show here.

tests/mocks/Unity/DashCommunicator/dashcommunicator.cppdeleted−42

This diff is too big to show here.

tests/mocks/Unity/DashCommunicator/dashcommunicator.hdeleted−39

This diff is too big to show here.

tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.cppdeleted−46

This diff is too big to show here.

tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.hdeleted−41

This diff is too big to show here.

tests/mocks/Unity/DashCommunicator/plugin.cppdeleted−31

This diff is too big to show here.

tests/mocks/Unity/DashCommunicator/qmldirdeleted−3

This diff is too big to show here.

tests/mocks/Unity/Indicators/CMakeLists.txt+4−1

This diff is too big to show here.

tests/mocks/Unity/Indicators/IndicatorsModel.qml+30−12

This diff is too big to show here.

tests/mocks/Unity/Indicators/fakeindicatorsmodel.h−1

This diff is too big to show here.

tests/mocks/Unity/Indicators/fakeindicatorsmodeldata.js+23−6

This diff is too big to show here.

tests/mocks/Unity/InputInfo/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Unity/InputInfo/qinputdeviceinfo_mock.cpp+6−2

This diff is too big to show here.

tests/mocks/Unity/Launcher/CMakeLists.txt+5−3

This diff is too big to show here.

tests/mocks/Unity/Launcher/MockAppDrawerModel.cppnew file+98

This diff is too big to show here.

tests/mocks/Unity/fake_filters.h → tests/mocks/Unity/Launcher/MockAppDrawerModel.hrenamed+14−17

This diff is too big to show here.

tests/mocks/Unity/Launcher/MockLauncherItem.cpp+27−1

This diff is too big to show here.

tests/mocks/Unity/Launcher/MockLauncherItem.h+9

This diff is too big to show here.

tests/mocks/Unity/Launcher/MockLauncherModel.cpp+4−1

This diff is too big to show here.

tests/mocks/Unity/Launcher/MockLauncherModel.h+1−1

This diff is too big to show here.

tests/mocks/Unity/Launcher/MockQuickListModel.cpp+5−1

This diff is too big to show here.

tests/mocks/Unity/Launcher/MockQuickListModel.h+2

This diff is too big to show here.

tests/mocks/Unity/Launcher/plugin.cpp+4

This diff is too big to show here.

tests/mocks/Unity/Notifications/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/Unity/Notifications/MockActionModel.h+1−1

This diff is too big to show here.

tests/mocks/Unity/Notifications/MockNotification.h+2−1

This diff is too big to show here.

tests/mocks/Unity/Platform/CMakeLists.txtnew file+1

This diff is too big to show here.

qml/Dash/Previews/PreviewSingleton/PreviewSingleton.qml → tests/mocks/Unity/Platform/MockPlatform.qmlrenamed+6−3

This diff is too big to show here.

tests/mocks/Unity/Platform/qmldirnew file+2

This diff is too big to show here.

tests/mocks/Unity/Screens/CMakeLists.txt+2−2

This diff is too big to show here.

tests/mocks/Unity/Screens/screens.cpp+1−1

This diff is too big to show here.

tests/mocks/Unity/Screens/screens.h+1−1

This diff is too big to show here.

tests/mocks/Unity/fake_categories.cppdeleted−161

This diff is too big to show here.

tests/mocks/Unity/fake_categories.hdeleted−54

This diff is too big to show here.

tests/mocks/Unity/fake_expandablefilterwidget.cppdeleted−53

This diff is too big to show here.

tests/mocks/Unity/fake_expandablefilterwidget.hdeleted−48

This diff is too big to show here.

tests/mocks/Unity/fake_filters.cppdeleted−136

This diff is too big to show here.

tests/mocks/Unity/fake_navigation.cppdeleted−123

This diff is too big to show here.

tests/mocks/Unity/fake_navigation.hdeleted−61

This diff is too big to show here.

tests/mocks/Unity/fake_optionselectorfilter.cppdeleted−70

This diff is too big to show here.

tests/mocks/Unity/fake_optionselectorfilter.hdeleted−54

This diff is too big to show here.

tests/mocks/Unity/fake_optionselectoroptions.cppdeleted−90

This diff is too big to show here.

tests/mocks/Unity/fake_optionselectoroptions.hdeleted−49

This diff is too big to show here.

tests/mocks/Unity/fake_previewmodel.cppdeleted−103

This diff is too big to show here.

tests/mocks/Unity/fake_previewmodel.hdeleted−58

This diff is too big to show here.

tests/mocks/Unity/fake_previewwidgetmodel.cppdeleted−97

This diff is too big to show here.

tests/mocks/Unity/fake_previewwidgetmodel.hdeleted−47

This diff is too big to show here.

tests/mocks/Unity/fake_rangeinputfilter.cppdeleted−167

This diff is too big to show here.

tests/mocks/Unity/fake_rangeinputfilter.hdeleted−79

This diff is too big to show here.

tests/mocks/Unity/fake_resultsmodel.cppdeleted−92

This diff is too big to show here.

tests/mocks/Unity/fake_resultsmodel.hdeleted−52

This diff is too big to show here.

tests/mocks/Unity/fake_scope.cppdeleted−372

This diff is too big to show here.

tests/mocks/Unity/fake_scope.hdeleted−128

This diff is too big to show here.

tests/mocks/Unity/fake_scopes.cppdeleted−280

This diff is too big to show here.

tests/mocks/Unity/fake_scopes.hdeleted−83

This diff is too big to show here.

tests/mocks/Unity/fake_scopesoverview.cppdeleted−340

This diff is too big to show here.

tests/mocks/Unity/fake_scopesoverview.hdeleted−117

This diff is too big to show here.

tests/mocks/Unity/fake_settingsmodel.cppdeleted−95

This diff is too big to show here.

tests/mocks/Unity/fake_settingsmodel.hdeleted−54

This diff is too big to show here.

tests/mocks/Unity/fake_unity_plugin.cppdeleted−62

This diff is too big to show here.

tests/mocks/Unity/fake_unity_plugin.hdeleted−34

This diff is too big to show here.

tests/mocks/Unity/fake_valuesliderfilter.cppdeleted−84

This diff is too big to show here.

tests/mocks/Unity/fake_valuesliderfilter.hdeleted−55

This diff is too big to show here.

tests/mocks/Unity/fake_valueslidervalues.cppdeleted−51

This diff is too big to show here.

tests/mocks/Unity/qmldirdeleted−3

This diff is too big to show here.

tests/mocks/Utils/CMakeLists.txt+12−3

This diff is too big to show here.

tests/mocks/Utils/plugin.cpp+9−1

This diff is too big to show here.

tests/mocks/Utils/windowstatestorage.cpp+23

This diff is too big to show here.

tests/mocks/Utils/windowstatestorage.h+6−3

This diff is too big to show here.

tests/mocks/Wizard/CMakeLists.txt+2−2

This diff is too big to show here.

tests/mocks/Wizard/MockSystem.cpp+19−4

This diff is too big to show here.

tests/mocks/Wizard/MockSystem.h+11−1

This diff is too big to show here.

tests/mocks/Wizard/mockplugin.cpp+2

This diff is too big to show here.

tests/mocks/liblightdm/CMakeLists.txtnew file+18

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp → tests/mocks/liblightdm/GreeterPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h → tests/mocks/liblightdm/GreeterPrivate.hrenamed

The content is the same.

tests/mocks/liblightdm/MockController.cppnew file+192

This diff is too big to show here.

tests/mocks/liblightdm/MockController.hnew file+106

This diff is too big to show here.

tests/mocks/liblightdm/MockGreeter.cppnew file+316

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.h → tests/mocks/liblightdm/MockGreeter.hrenamed+10−23

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp → tests/mocks/liblightdm/MockSessionsModel.cpprenamed+39−42

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h → tests/mocks/liblightdm/MockSessionsModel.hrenamed+11−19

This diff is too big to show here.

tests/mocks/liblightdm/MockUsersModel.cppnew file+220

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModel.h → tests/mocks/liblightdm/MockUsersModel.hrenamed+9−18

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp → tests/mocks/liblightdm/SessionsModelPrivate.cpprenamed+1−1

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h → tests/mocks/liblightdm/SessionsModelPrivate.hrenamed

The content is the same.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp → tests/mocks/liblightdm/UsersModelPrivate.cpprenamed+23−23

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.h → tests/mocks/liblightdm/UsersModelPrivate.hrenamed

The content is the same.

tests/mocks/liblightdm/demo/CMakeLists.txt+1−1

This diff is too big to show here.

tests/mocks/libusermetrics/CMakeLists.txt+2−1

This diff is too big to show here.

tests/mocks/libusermetrics/ColorTheme.cpp+4

This diff is too big to show here.

tests/mocks/libusermetrics/UserMetrics.cpp+5

This diff is too big to show here.

tests/plugins/AccountsService/CMakeLists.txt+8−2

This diff is too big to show here.

tests/plugins/AccountsService/PropertiesServer.cpp+1−1

This diff is too big to show here.

tests/plugins/AccountsService/client.cpp+6−34

This diff is too big to show here.

tests/plugins/CMakeLists.txt+2−3

This diff is too big to show here.

tests/plugins/Dash/CMakeLists.txtdeleted−79

This diff is too big to show here.

tests/plugins/Dash/cardcreator/1.resdeleted−82

This diff is too big to show here.

tests/plugins/Dash/cardcreator/1.res.cardcreatordeleted−69

This diff is too big to show here.

tests/plugins/Dash/cardcreator/1.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/10.resdeleted−129

This diff is too big to show here.

tests/plugins/Dash/cardcreator/10.res.cardcreatordeleted−127

This diff is too big to show here.

tests/plugins/Dash/cardcreator/10.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/11.resdeleted−176

This diff is too big to show here.

tests/plugins/Dash/cardcreator/11.res.cardcreatordeleted−160

This diff is too big to show here.

tests/plugins/Dash/cardcreator/11.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/12.resdeleted−108

This diff is too big to show here.

tests/plugins/Dash/cardcreator/12.res.cardcreatordeleted−95

This diff is too big to show here.

tests/plugins/Dash/cardcreator/12.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/2.resdeleted−128

This diff is too big to show here.

tests/plugins/Dash/cardcreator/2.res.cardcreatordeleted−126

This diff is too big to show here.

tests/plugins/Dash/cardcreator/2.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/3.resdeleted−90

This diff is too big to show here.

tests/plugins/Dash/cardcreator/3.res.cardcreatordeleted−86

This diff is too big to show here.

tests/plugins/Dash/cardcreator/3.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/4.resdeleted−111

This diff is too big to show here.

tests/plugins/Dash/cardcreator/4.res.cardcreatordeleted−99

This diff is too big to show here.

tests/plugins/Dash/cardcreator/4.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/5.resdeleted−102

This diff is too big to show here.

tests/plugins/Dash/cardcreator/5.res.cardcreatordeleted−99

This diff is too big to show here.

tests/plugins/Dash/cardcreator/5.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/6.resdeleted−127

This diff is too big to show here.

tests/plugins/Dash/cardcreator/6.res.cardcreatordeleted−116

This diff is too big to show here.

tests/plugins/Dash/cardcreator/6.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/7.resdeleted−141

This diff is too big to show here.

tests/plugins/Dash/cardcreator/7.res.cardcreatordeleted−139

This diff is too big to show here.

tests/plugins/Dash/cardcreator/7.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/8.resdeleted−109

This diff is too big to show here.

tests/plugins/Dash/cardcreator/8.res.cardcreatordeleted−97

This diff is too big to show here.

tests/plugins/Dash/cardcreator/8.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreator/9.resdeleted−120

This diff is too big to show here.

tests/plugins/Dash/cardcreator/9.res.cardcreatordeleted−109

This diff is too big to show here.

tests/plugins/Dash/cardcreator/9.tstdeleted−4

This diff is too big to show here.

tests/plugins/Dash/cardcreatortest.cppdeleted−121

This diff is too big to show here.

tests/plugins/Dash/cardcreatortest.qmldeleted−29

This diff is too big to show here.

tests/plugins/Dash/horizontaljournaltest.cppdeleted−487

This diff is too big to show here.

tests/plugins/Dash/horizontaljournaltest.qmldeleted−44

This diff is too big to show here.

tests/plugins/Dash/horizontaljournaltry.cppdeleted−89

This diff is too big to show here.

tests/plugins/Dash/horizontaljournaltry.qmldeleted−120

This diff is too big to show here.

tests/plugins/Dash/listviewwithpageheadersectionexternalmodeltest.cppdeleted−243

This diff is too big to show here.

tests/plugins/Dash/listviewwithpageheadersectiontest.cppdeleted−2210

This diff is too big to show here.

tests/plugins/Dash/listviewwithpageheadertest.cppdeleted−2036

This diff is too big to show here.

tests/plugins/Dash/listviewwithpageheadertest.qmldeleted−96

This diff is too big to show here.

tests/plugins/Dash/listviewwithpageheadertestsection.qmldeleted−119

This diff is too big to show here.

tests/plugins/Dash/listviewwithpageheadertestsectionexternalmodel.qmldeleted−76

This diff is too big to show here.

tests/plugins/Dash/organicgridtest.cppdeleted−530

This diff is too big to show here.

tests/plugins/Dash/organicgridtest.qmldeleted−44

This diff is too big to show here.

tests/plugins/Dash/organicgridtry.cppdeleted−97

This diff is too big to show here.

tests/plugins/Dash/organicgridtry.qmldeleted−114

This diff is too big to show here.

tests/plugins/Dash/tst_CardAttributes.qmldeleted−57

This diff is too big to show here.

tests/plugins/Dash/tst_CroppedImageMinimumSourceSize.qmldeleted−56

This diff is too big to show here.

tests/plugins/Dash/tst_ListViewWithPageHeaderQML.qmldeleted−140

This diff is too big to show here.

tests/plugins/Dash/tst_ScopeStyle.qmldeleted−188

This diff is too big to show here.

tests/plugins/Dash/verticaljournaltest.cppdeleted−561

This diff is too big to show here.

tests/plugins/Dash/verticaljournaltest.qmldeleted−45

This diff is too big to show here.

tests/plugins/Dash/verticaljournaltry.cppdeleted−89

This diff is too big to show here.

tests/plugins/Dash/verticaljournaltry.qmldeleted−121

This diff is too big to show here.

tests/plugins/GlobalShortcut/CMakeLists.txt+12−7

This diff is too big to show here.

tests/plugins/GlobalShortcut/GlobalShortcutTest.cpp+3−2

This diff is too big to show here.

tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+5−22

This diff is too big to show here.

tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+4−1

This diff is too big to show here.

tests/plugins/ImageCache/CMakeLists.txtdeleted−20

This diff is too big to show here.

tests/plugins/ImageCache/graphics/wide.jpgdeleted

Binary file not shown.

tests/plugins/ImageCache/test.cppdeleted−214

This diff is too big to show here.

tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+58−14

This diff is too big to show here.

tests/plugins/LightDM/IntegratedLightDM/dbus.cpp+22−12

This diff is too big to show here.

tests/plugins/LightDM/IntegratedLightDM/greeter.qml+2−7

This diff is too big to show here.

tests/plugins/LightDM/IntegratedLightDM/promptsmodel.cppnew file+175

This diff is too big to show here.

tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp+19−13

This diff is too big to show here.

tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+100−16

This diff is too big to show here.

tests/plugins/SessionBroadcast/CMakeLists.txt+8−2

This diff is too big to show here.

tests/plugins/Ubuntu/Gestures/CMakeLists.txt+12−6

This diff is too big to show here.

tests/plugins/Ubuntu/Gestures/GestureTest.cpp+12−2

This diff is too big to show here.

tests/plugins/Ubuntu/Gestures/tst_FloatingFlickable.qml+1−1

This diff is too big to show here.

tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+14−3

This diff is too big to show here.

tests/plugins/Ubuntu/Gestures/tst_TouchGestureArea.cpp+30

This diff is too big to show here.

tests/plugins/Unity/CMakeLists.txt+1

This diff is too big to show here.

tests/plugins/Unity/Indicators/CMakeLists.txt+12−2

This diff is too big to show here.

tests/plugins/Unity/Indicators/IndicatorsManagerTest.cpp+1−1

This diff is too big to show here.

tests/plugins/Unity/Indicators/IndicatorsModelTest.cpp+1−1

This diff is too big to show here.

tests/plugins/Unity/Launcher/CMakeLists.txt+48−4

This diff is too big to show here.

tests/plugins/Unity/Launcher/appdrawermodeltest.cppnew file+68

This diff is too big to show here.

tests/plugins/Unity/Launcher/launchermodeltest.cpp+113−11

This diff is too big to show here.

tests/plugins/Unity/Launcher/ualwrapper.cppnew file+58

This diff is too big to show here.

tests/plugins/Unity/Launcher/ualwrapper.hnew file+55

This diff is too big to show here.

tests/mocks/Ubuntu/Payments/plugin.cpp → tests/plugins/Unity/Launcher/xdgwatcher.cpprenamed+24−7

This diff is too big to show here.

tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h → tests/plugins/Unity/Launcher/xdgwatcher.hrenamed+17−17

This diff is too big to show here.

tests/plugins/Unity/Platform/CMakeLists.txtnew file+30

This diff is too big to show here.

tests/plugins/Unity/Platform/platformtest.cppnew file+142

This diff is too big to show here.

tests/plugins/Unity/Session/CMakeLists.txt+21−3

This diff is too big to show here.

tests/plugins/Unity/Session/LogindServer.cpp+10

This diff is too big to show here.

tests/plugins/Unity/Session/LogindServer.h+5

This diff is too big to show here.

tests/plugins/Unity/Session/interfaces.xml+12

This diff is too big to show here.

tests/plugins/Unity/Session/preload.cnew file+43

This diff is too big to show here.

tests/plugins/Unity/Session/sessionbackendtest.cpp+130−11

This diff is too big to show here.

tests/plugins/Utils/CMakeLists.txt+13−5

This diff is too big to show here.

tests/plugins/Utils/DeviceConfigParserTest.cpp+7−1

This diff is too big to show here.

tests/plugins/Utils/ModelTest.cpp+2−1

This diff is too big to show here.

tests/plugins/Utils/WindowInputMonitorTest.cpp+107−10

This diff is too big to show here.

tests/plugins/Utils/WindowStateStorageTest.cppnew file+121

This diff is too big to show here.

tests/plugins/WindowManager/CMakeLists.txtnew file+36

This diff is too big to show here.

tests/plugins/WindowManager/UnityApplicationMocks.hnew file+211

This diff is too big to show here.

tests/plugins/WindowManager/tst_TopLevelWindowModel.cppnew file+193

This diff is too big to show here.

tests/plugins/Wizard/CMakeLists.txt+8−2

This diff is too big to show here.

tests/qmltests/ApplicationMenuDataLoader.qmlnew file+99

This diff is too big to show here.

tests/qmltests/ApplicationMenus/tst_MenuBar.qmlnew file+437

This diff is too big to show here.

tests/qmltests/ApplicationMenus/tst_MenuPopup.qmlnew file+361

This diff is too big to show here.

tests/qmltests/CMakeLists.txt+28−53

This diff is too big to show here.

tests/qmltests/Components/CMakeLists.txt+26−7

This diff is too big to show here.

tests/qmltests/Components/tst_Carousel.qmldeleted−292

This diff is too big to show here.

tests/qmltests/Components/tst_Dialogs.qml−1

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle.cpp+23−4

This diff is too big to show here.

tests/qmltests/Components/tst_DraggingArea.qml+6−6

This diff is too big to show here.

tests/qmltests/Components/tst_EdgeDragEvaluator.cpp+4−2

This diff is too big to show here.

tests/qmltests/Components/tst_Lockscreen.qml+11−9

This diff is too big to show here.

tests/qmltests/Components/tst_MediaServices.qml+5−4

This diff is too big to show here.

tests/qmltests/Components/tst_PhysicalKeysMapper.qml+7−6

This diff is too big to show here.

tests/qmltests/Components/tst_Rating.qml+1−1

This diff is too big to show here.

tests/qmltests/Components/tst_ResponsiveGridView.qmldeleted−157

This diff is too big to show here.

tests/qmltests/Components/tst_ResponsiveVerticalJournal.qmldeleted−183

This diff is too big to show here.

tests/qmltests/Components/tst_SharingPicker.qml+3−3

This diff is too big to show here.

tests/qmltests/Components/tst_Showable.qml+13

This diff is too big to show here.

tests/qmltests/Components/tst_VirtualTouchPad.qml+55−1

This diff is too big to show here.

tests/qmltests/Components/tst_Wallpaper.qmlnew file+118

This diff is too big to show here.

tests/qmltests/Components/tst_WallpaperResolver.qml+104−18

This diff is too big to show here.

tests/qmltests/Components/tst_ZoomableImage.qml+1−1

This diff is too big to show here.

tests/qmltests/Dash/CardHelpers.jsdeleted−115

This diff is too big to show here.

tests/qmltests/Dash/Filters/tst_FilterExpandableWidget.qmldeleted−99

This diff is too big to show here.

tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qmldeleted−96

This diff is too big to show here.

tests/qmltests/Dash/Filters/tst_FilterRangeInput.qmldeleted−247

This diff is too big to show here.

tests/qmltests/Dash/Filters/tst_FilterValueSlider.qmldeleted−101

This diff is too big to show here.

tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qmldeleted−59

This diff is too big to show here.

tests/qmltests/Dash/Previews/MockPreviewWidget.qmldeleted−27

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_Preview.qmldeleted−151

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewActions.qmldeleted−262

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewAudioPlayback.qmldeleted−182

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewComment.qmldeleted−84

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewCommentInput.qmldeleted−113

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewExpandable.qmldeleted−358

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewHeader.qmldeleted−183

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewIconActions.qmldeleted−136

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qmldeleted−147

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewInlineVideo.qmldeleted−155

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewMediaToolbar.qmldeleted−69

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewPayments.qmldeleted−203

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewProgress.qmldeleted−100

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewRatingDisplay.qmldeleted−123

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewRatingDisplayCreationRanges.qmldeleted−131

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewRatingEdit.qmldeleted−87

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewRatingInput.qmldeleted−246

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewTable.qmldeleted−147

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewTextSummary.qmldeleted−87

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewVideoPlayback.qmldeleted−61

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qmldeleted−104

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qmldeleted−138

This diff is too big to show here.

tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingBoolean.qmldeleted−62

This diff is too big to show here.

tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingList.qmldeleted−84

This diff is too big to show here.

tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingNumber.qmldeleted−98

This diff is too big to show here.

tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingString.qmldeleted−88

This diff is too big to show here.

tests/qmltests/Dash/ScopeSettings/tst_ScopeSettingsWidgetFactory.qmldeleted−65

This diff is too big to show here.

tests/qmltests/Dash/artwork/avatar@12.pngdeleted

Binary file not shown.

tests/qmltests/Dash/artwork/background.pngdeleted

Binary file not shown.

tests/qmltests/Dash/artwork/checkers.pngdeleted

Binary file not shown.

tests/qmltests/Dash/artwork/emblem.pngdeleted

Binary file not shown.

tests/qmltests/Dash/artwork/music-player-design.pngdeleted

Binary file not shown.

tests/qmltests/Dash/tst_Card.qmldeleted−682

This diff is too big to show here.

tests/qmltests/Dash/tst_CardBenchmark.qmldeleted−95

This diff is too big to show here.

tests/qmltests/Dash/tst_CardCarousel.qmldeleted−111

This diff is too big to show here.

tests/qmltests/Dash/tst_CardTool.qmldeleted−379

This diff is too big to show here.

tests/qmltests/Dash/tst_Dash.qmldeleted−693

This diff is too big to show here.

tests/qmltests/Dash/tst_DashContent.qmldeleted−730

This diff is too big to show here.

tests/qmltests/Dash/tst_DashPageHeader.qmldeleted−357

This diff is too big to show here.

tests/qmltests/Dash/tst_DashShell.qmldeleted−145

This diff is too big to show here.

tests/qmltests/Dash/tst_GenericScopeView.qmldeleted−696

This diff is too big to show here.

tests/qmltests/Dash/tst_PageHeader/logo-ubuntu-orange.svgdeleted−60

This diff is too big to show here.

tests/qmltests/Dash/tst_PreviewView.qmldeleted−122

This diff is too big to show here.

tests/qmltests/Greeter/TestView.qml+16−22

This diff is too big to show here.

tests/qmltests/Greeter/tst_Greeter.qml+80−140

This diff is too big to show here.

tests/qmltests/Greeter/tst_GreeterView.qmlnew file+1366

This diff is too big to show here.

tests/qmltests/Greeter/tst_Infographics.qml+1−11

This diff is too big to show here.

tests/qmltests/Greeter/tst_NarrowView.qmldeleted−589

This diff is too big to show here.

tests/qmltests/Greeter/tst_WideView.qmldeleted−757

This diff is too big to show here.

tests/qmltests/Launcher/tst_Drawer.qmlnew file+520

This diff is too big to show here.

tests/qmltests/Launcher/tst_Launcher.qml+346−83

This diff is too big to show here.

tests/qmltests/Notifications/tst_Notifications.qml+27−21

This diff is too big to show here.

tests/qmltests/Notifications/tst_OptionToggle.qml+12−6

This diff is too big to show here.

tests/qmltests/Notifications/tst_SwipeToAct.qml+15−8

This diff is too big to show here.

tests/qmltests/Notifications/tst_VisualSnapDecisionsQueue.qml+3−3

This diff is too big to show here.

tests/qmltests/Panel/tst_IndicatorItem.qml → tests/qmltests/Panel/Indicators/tst_IndicatorItem.qmlrenamed+1−1

This diff is too big to show here.

tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml → tests/qmltests/Panel/Indicators/tst_IndicatorMenuItemFactory.qmlrenamed+41−82

This diff is too big to show here.

tests/qmltests/Panel/Indicators/tst_IndicatorsLight.qml+168−8

This diff is too big to show here.

tests/qmltests/Panel/IndicatorTest.qml → tests/qmltests/Panel/PanelTest.qmlrenamed−5

This diff is too big to show here.

tests/qmltests/Panel/PanelUI.qmlnew file+286

This diff is too big to show here.

tests/qmltests/Panel/tst_ActiveCallHint.qml+29−10

This diff is too big to show here.

tests/qmltests/Panel/tst_MenuContent.qml+16−4

This diff is too big to show here.

tests/qmltests/Panel/tst_Panel.qml+419−235

This diff is too big to show here.

tests/qmltests/Panel/tst_IndicatorsBar.qml → tests/qmltests/Panel/tst_PanelBar.qmlrenamed+24−33

This diff is too big to show here.

tests/qmltests/Panel/tst_IndicatorItemRow.qml → tests/qmltests/Panel/tst_PanelItemRow.qmlrenamed+38−34

This diff is too big to show here.

tests/qmltests/Panel/tst_IndicatorsMenu.qml → tests/qmltests/Panel/tst_PanelMenu.qmlrenamed+108−34

This diff is too big to show here.

tests/qmltests/Panel/tst_IndicatorPage.qml → tests/qmltests/Panel/tst_PanelMenuPage.qmlrenamed+53−47

This diff is too big to show here.

tests/qmltests/Panel/tst_PanelSmallScreen.qmlnew file+133

This diff is too big to show here.

tests/qmltests/Stage/ApplicationCheckBox.qml+8−8

This diff is too big to show here.

tests/qmltests/Stage/tst_ApplicationWindow.qml+15−32

This diff is too big to show here.

tests/qmltests/Stage/tst_DecoratedWindow.qml+40−5

This diff is too big to show here.

tests/qmltests/Stage/tst_DesktopStage.qml+433−141

This diff is too big to show here.

tests/qmltests/Stage/tst_PhoneStage.qml+118−102

This diff is too big to show here.

tests/qmltests/Stage/tst_QMLTopLevelWindowModel.qmlnew file+191

This diff is too big to show here.

tests/qmltests/Stage/tst_SpreadDelegate.qmldeleted−423

This diff is too big to show here.

tests/qmltests/Stage/tst_SurfaceContainer.qml+13−8

This diff is too big to show here.

tests/qmltests/Stage/tst_TabletStage.qml+151−141

This diff is too big to show here.

tests/qmltests/Stage/tst_WindowDecoration.qmlnew file+244

This diff is too big to show here.

tests/qmltests/Stage/tst_WindowResizeArea.qml+22−78

This diff is too big to show here.

tests/qmltests/Tutorial/tst_Tutorial.qml+205−184

This diff is too big to show here.

tests/qmltests/Wizard/tst_Wizard.qml+100−142

This diff is too big to show here.

tests/qmltests/tst_DisabledScreenNotice.qml+23−9

This diff is too big to show here.

tests/qmltests/tst_OrientedShell.qml+410−274

This diff is too big to show here.

tests/qmltests/tst_Shell.qml+1371−772

This diff is too big to show here.

tests/qmltests/tst_ShellWithPin.qml+122−190

This diff is too big to show here.

tests/scripts/alert-launcher-icon.sh+16

This diff is too big to show here.

tests/scripts/get-progress.sh+16

This diff is too big to show here.

tests/scripts/list-launcher-icons.sh+16

This diff is too big to show here.

tests/scripts/set-count-visible.sh+16

This diff is too big to show here.

tests/scripts/set-count.sh+16

This diff is too big to show here.

tests/scripts/set-progress.sh+16

This diff is too big to show here.

tests/uqmlscene/CMakeLists.txt+14−2

This diff is too big to show here.

tests/uqmlscene/main.cpp+7−3

This diff is too big to show here.

tests/utils/modules/Unity/Test/CMakeLists.txt+5−5

This diff is too big to show here.

tests/utils/modules/Unity/Test/MouseTouchEmulationCheckbox.qml+1−1

This diff is too big to show here.

tests/utils/modules/Unity/Test/StageTestCase.qmlnew file+64

This diff is too big to show here.

tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.cpp+10−1

This diff is too big to show here.

tests/utils/modules/Unity/Test/UnityTestCase.qml+10−20

This diff is too big to show here.

tests/utils/modules/Unity/Test/qmldir+2−1

This diff is too big to show here.

tests/utils/modules/Unity/Test/testutil.cpp+1−1

This diff is too big to show here.

tools/CMakeLists.txt+2−12

This diff is too big to show here.

src/Panel/Indicators/client/CMakeLists.txt → tools/indicatorsclient/CMakeLists.txtrenamed+1−1

This diff is too big to show here.

src/Panel/Indicators/client/indicatorsclient.cpp → tools/indicatorsclient/indicatorsclient.cpprenamed

The content is the same.

src/Panel/Indicators/client/indicatorsclient.h → tools/indicatorsclient/indicatorsclient.hrenamed

The content is the same.

src/Panel/Indicators/client/main.cpp → tools/indicatorsclient/main.cpprenamed

The content is the same.

tools/menutool/CMakeLists.txtnew file+5

This diff is too big to show here.

tools/menutool/READMEnew file+11

This diff is too big to show here.

tools/menutool/menutool.cppnew file+62

This diff is too big to show here.

tools/menutool/menutool.qmlnew file+118

This diff is too big to show here.

tools/registry-tracker.cppdeleted−146

This diff is too big to show here.

tools/registry-tracker.hdeleted−49

This diff is too big to show here.

tools/scopetool.cppdeleted−116

This diff is too big to show here.

tools/unlock-device+16

This diff is too big to show here.