Merge lp:~ci-train-bot/unity8/unity8-ubuntu-xenial-landing-022 into lp:unity8

Proposed by Michał Sawicz on 2015-10-26
Status: Merged
Merged at revision: 2096
Proposed branch: lp:~ci-train-bot/unity8/unity8-ubuntu-xenial-landing-022
Merge into: lp:unity8
Diff against target: 5795 lines (+3025/-1212)
103 files modified
CMakeLists.txt (+4/-1)
data/unity8.conf (+10/-0)
debian/changelog (+36/-0)
debian/control (+9/-7)
debian/copyright (+21/-0)
debian/unity8-private.install (+1/-0)
debian/unity8.install (+1/-0)
plugins/CMakeLists.txt (+1/-0)
plugins/Cursor/3rd_party/CMakeLists.txt (+1/-0)
plugins/Cursor/3rd_party/xcursor/CMakeLists.txt (+15/-0)
plugins/Cursor/3rd_party/xcursor/xcursor.c (+968/-0)
plugins/Cursor/3rd_party/xcursor/xcursor.h (+65/-0)
plugins/Cursor/CMakeLists.txt (+28/-0)
plugins/Cursor/Cursor.qml (+12/-0)
plugins/Cursor/CursorImageProvider.cpp (+191/-0)
plugins/Cursor/CursorImageProvider.h (+76/-0)
plugins/Cursor/MousePointer.cpp (+125/-0)
plugins/Cursor/MousePointer.h (+59/-0)
plugins/Cursor/plugin.cpp (+39/-0)
plugins/Cursor/plugin.h (+33/-0)
plugins/Cursor/qmldir (+3/-0)
plugins/IntegratedLightDM/UsersModel.cpp (+7/-6)
plugins/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp (+2/-1)
plugins/ScreenGrabber/screengrabber.cpp (+2/-2)
plugins/ScreenGrabber/screengrabber.h (+1/-1)
plugins/Unity/CMakeLists.txt (+1/-0)
plugins/Unity/Launcher/desktopfilehandler.cpp (+3/-2)
plugins/Unity/Platform/CMakeLists.txt (+9/-0)
plugins/Unity/Platform/platform.cpp (+43/-0)
plugins/Unity/Platform/platform.h (+58/-0)
plugins/Unity/Platform/plugin.cpp (+27/-0)
plugins/Unity/Platform/plugin.h (+32/-0)
plugins/Unity/Platform/qmldir (+2/-0)
plugins/Unity/Session/dbusunitysessionservice.cpp (+2/-1)
plugins/Utils/CMakeLists.txt (+0/-2)
plugins/Utils/plugin.cpp (+0/-5)
plugins/Utils/relativetimeformatter.cpp (+0/-260)
plugins/Utils/relativetimeformatter.h (+0/-34)
plugins/Utils/timeformatter.cpp (+0/-206)
plugins/Utils/timeformatter.h (+0/-68)
plugins/Utils/timezoneFormatter.cpp (+1/-1)
qml/Components/Dialogs.qml (+3/-2)
qml/Components/ScreenGrabber.qml (+4/-1)
qml/Components/WallpaperResolver.qml (+63/-0)
qml/DeviceConfiguration.qml (+1/-0)
qml/DisabledScreenNotice.qml (+49/-0)
qml/Greeter/Greeter.qml (+1/-1)
qml/Launcher/Launcher.qml (+1/-0)
qml/Notifications/Notification.qml (+2/-0)
qml/Notifications/NotificationMenuItemFactory.qml (+2/-1)
qml/Notifications/Notifications.qml (+2/-0)
qml/OrientedShell.qml (+10/-1)
qml/Panel/Indicators/MenuItemFactory.qml (+19/-15)
qml/Panel/Indicators/MessageMenuItemFactory.qml (+35/-26)
qml/Panel/Panel.qml (+1/-0)
qml/Rotation/RotationStates.qml (+2/-2)
qml/Shell.qml (+24/-40)
qml/Stages/ApplicationWindow.qml (+2/-0)
qml/Stages/DecoratedWindow.qml (+7/-4)
qml/Stages/DesktopStage.qml (+5/-8)
qml/Stages/SurfaceContainer.qml (+1/-0)
qml/Stages/WindowDecoration.qml (+32/-4)
qml/Stages/WindowResizeArea.qml (+135/-62)
src/ApplicationArguments.h (+10/-2)
src/CMakeLists.txt (+3/-0)
src/SecondaryWindow.cpp (+31/-0)
src/SecondaryWindow.h (+30/-0)
src/ShellApplication.cpp (+197/-0)
src/ShellApplication.h (+55/-0)
src/ShellView.cpp (+59/-0)
src/ShellView.h (+34/-0)
src/main.cpp (+4/-101)
tests/autopilot/unity8/fixture_setup.py (+1/-1)
tests/autopilot/unity8/greeter/tests/__init__.py (+1/-1)
tests/autopilot/unity8/launcher.py (+1/-1)
tests/autopilot/unity8/settings_wizard/__init__.py (+7/-2)
tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py (+10/-5)
tests/autopilot/unity8/shell/__init__.py (+1/-1)
tests/autopilot/unity8/shell/tests/__init__.py (+1/-1)
tests/mocks/CMakeLists.txt (+1/-0)
tests/mocks/Cursor/CMakeLists.txt (+1/-0)
tests/mocks/Cursor/Cursor.qml (+20/-0)
tests/mocks/Cursor/qmldir (+2/-0)
tests/mocks/Unity/Launcher/MockQuickListModel.cpp (+1/-1)
tests/mocks/Unity/fake_resultsmodel.cpp (+1/-1)
tests/mocks/Unity/fake_scopesoverview.cpp (+1/-1)
tests/mocks/Utils/CMakeLists.txt (+0/-2)
tests/mocks/Utils/Utils.qmltypes (+31/-28)
tests/mocks/Utils/plugin.cpp (+0/-13)
tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp (+11/-0)
tests/plugins/ScreenGrabber/grabber.qml (+1/-1)
tests/plugins/Unity/Indicators/UnityMenuModelStackTest.cpp (+2/-9)
tests/plugins/Utils/CMakeLists.txt (+0/-1)
tests/plugins/Utils/TimeFormatterTest.cpp (+0/-155)
tests/plugins/Wizard/tst_pagelist.cpp (+5/-5)
tests/qmltests/CMakeLists.txt (+3/-1)
tests/qmltests/Components/tst_WallpaperResolver.qml (+89/-0)
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml (+6/-16)
tests/qmltests/Panel/Indicators/tst_MessageMenuItemFactory.qml (+6/-13)
tests/qmltests/Stages/tst_WindowResizeArea.qml (+18/-61)
tests/qmltests/tst_DisabledScreenNotice.qml (+37/-0)
tests/qmltests/tst_OrientedShell.qml (+55/-1)
tests/qmltests/tst_Shell.qml (+0/-24)
To merge this branch: bzr merge lp:~ci-train-bot/unity8/unity8-ubuntu-xenial-landing-022
Reviewer Review Type Date Requested Status
Unity Team 2015-10-26 Pending
Review via email: mp+275672@code.launchpad.net

Description of the Change

Only use this to base new silos on this one.

To post a comment you must log in.
2018. By CI Train Bot Account on 2015-10-26

Resync trunk.

2019. By Michał Sawicz on 2015-10-26

Add missing copyright to Cursor.qml

Approved by: Albert Astals Cid

2020. By Launchpad Translations on behalf of unity-team on 2015-10-28

Launchpad automatic translations update.

2021. By Launchpad Translations on behalf of unity-team on 2015-10-30

Launchpad automatic translations update.

2022. By Daniel d'Andrada on 2015-11-04

Use SDK 1.3 across all files Fixes: #1449628, #1503498, #1508363
Approved by: PS Jenkins bot

2023. By Andrea Cimitan on 2015-11-04

Updated all 1.3 UbuntuShape to use new APIs where possible
Approved by: Albert Astals Cid

2024. By Andrea Cimitan on 2015-11-04

Make zoomable and video playback widgets edge to edge
Approved by: Albert Astals Cid

2025. By Andrea Cimitan on 2015-11-04

Add sharing widget to zoomable image and video playback
Approved by: Albert Astals Cid, Pawel Stolowski

2026. By Albert Astals Cid on 2015-11-04

Make the scope settings list scroll to text visible area
 Fixes: #1499084
Approved by: Michael Terry

2027. By Albert Astals Cid on 2015-11-04

Move to importing QtQuick 2.4 & friends

This means we obviously require Qt 5.4 or greater, we had for a while
but make it official now
Approved by: Michael Terry

2028. By Albert Astals Cid on 2015-11-04

Add an Item that proxies for old and new audio roles Fixes: #1493851

2029. By Michael Zanetti on 2015-11-04

Fixes for the panel buttons

* Hide panel buttons when switching to staged mode
* properly save/restore window states, not just geometry Fixes: #1431566, #1443319, #1504269, #1510360
Approved by: Lukáš Tinkl

2030. By Michał Sawicz on 2015-11-04

Enable support for overriding application orientations based on device type Fixes: #1478637

2031. By Michael Terry on 2015-11-04

Handle lifecycle policy exceptions ourselves, instead of letting qtmir do it for us and allow non-Touch apps to opt-out of the Touch lifecycle.

This requires using the new isTouchApp unity-api property to ApplicationInfoInterface.

Now that qtmir won't decide policy for suspending exemptions anymore, we take over the interpretation of the lifecycleException GSettings key. Since the GSettings key for that was registered under the qtmir namespace (and there's no technical reason to migrate settings), I left the schema in qtmir itself. We merely consume it.

2032. By Michał Sawicz on 2015-11-04

Support server->client visibility change to stop rendering (lp:#1475678) Fixes: #1475678
Approved by: Daniel d'Andrada

2033. By Christopher Lee on 2015-11-04

Quick spelling fix in process control output.
Approved by: Albert Astals Cid

2034. By Albert Astals Cid on 2015-11-04

CroppedImageMinimumSourceSize: Fix 'Binding loop detected for property "imageAspectRatio"'

Approved by: Gerry Boland, Michał Sawicz

2035. By Michael Terry on 2015-11-04

Avoid showing the shutdown dialog when turning on the screen if your device is under heavy load.

Specifically, we actually watch the timestamp of input events as they come in to determine how long it's been. This means that if for whatever reason, processing of events get delayed, we don't misinterpret user input.

To test this, try running the following command and then turning the screen on and off again:
sudo cpulimit -l 1 -c 1 -p `ps ax | grep dbus-daemon | head -n 1 | awk '{print $1;}'`

Without this branch, you'll notice that at some point, you see the shutdown dialog in error. Because unity8 couldn't keep up with events and thought 2s passed between power-pressed and power-released events.

But if we watch the timestamps, we can avoid that particular fate. Fixes: #1508563
Approved by: Albert Astals Cid, Michael Zanetti

2036. By CI Train Bot Account on 2015-11-04

Releasing 8.11+16.04.20151104-0ubuntu1

2037. By Launchpad Translations on behalf of unity-team on 2015-11-07

Launchpad automatic translations update.

2038. By Launchpad Translations on behalf of unity-team on 2015-11-08

Launchpad automatic translations update.

2039. By Launchpad Translations on behalf of unity-team on 2015-11-11

Launchpad automatic translations update.

2040. By Michael Terry on 2015-11-12

Make a few DBus calls asynchronous, for a smoother UX.

The calls I've changed here are neither hugely important nor frequent calls. But every little bit helps.

- Made a few sync calls async.

- Made a few sync calls more obviously sync. We had several instances of asyncCall()'s return value -- a QDBusPendingReply -- being assigned to a QDBusReply variable, which makes it wait for the call to finish. In cases where it didn't make sense to rewrite logic to be async, I've merely changed the method to be call() instead of asyncCall() for clarity.

- I removed the API in our AccountsService plugin for even making sync calls. This won't stop future developers from from writing sync code, but might give them pause.

- I removed some test mocks for our AccountsService plugin interface and changed those tests to use our actual AS plugin against a mock AS server. In truth, this change was because I had a devil of a time crafting a fake reply that included a custom complex type. But it's a good change anyway. Exercises more of our code in tests and reduces duplicated interfaces.
Approved by: Albert Astals Cid

2041. By Daniel d'Andrada on 2015-11-12

Cursor: properly initialize hotspot position Fixes: #1510407
Approved by: Gerry Boland

2042. By Albert Astals Cid on 2015-11-12

 Warn we're using only the cache when not connected to the interwebs

Approved by: Michael Terry

2043. By Michael Zanetti on 2015-11-12

added icon for the dash Fixes: #1488146
Approved by: Albert Astals Cid, Lukáš Tinkl

2044. By Daniel d'Andrada on 2015-11-12

Update GSettings mock in tst_OrientedShell

Gets rid of the following warning:
OrientedShell.qml:76: Error: Cannot assign to non-existent property "disableHeight"
Approved by: Albert Astals Cid

2045. By Andrea Cimitan on 2015-11-12

Add sharing widget to zoomable image and video playback
Approved by: Albert Astals Cid, Pawel Stolowski

2046. By Lukáš Tinkl on 2015-11-12

Restore windows when activating from the spread, maintain a focus stack

Stop displaying the "grabbing" icon when we merely click to focus the app's decoration.

Provide keyboard shortcuts for common window operations
Approved by: Michael Zanetti

2047. By Michael Zanetti on 2015-11-12

prevent windows to be moved under the panel

If they happen to have a saved state which is under the panel
(might happen on alt+drag later), they will be moved below the panel
on close and reopen. This is how unity7 behaves in this regard. Fixes: #1438465
Approved by: Lukáš Tinkl

2048. By Michael Zanetti on 2015-11-12

use UbuntuNumberAnimations instead of linear ones for window state transitions Fixes: #1497097
Approved by: Lukáš Tinkl

2049. By Albert Astals Cid on 2015-11-12

Make cardWidth and cardHeight real

If they are var the properties depending on them get reevaluated even if they don't change
Approved by: Michael Zanetti

2050. By Michael Zanetti on 2015-11-12

update inputinfo api to the latest upstream snapshot

* Import latest update of api from gerrit.
* drop all the old mock code
* instead of mocking the whole api, add mocks for the backend only. This should simplify future updates and moving to upstream implementation a lot.
* update tests to use new mocking system
Approved by: Lukáš Tinkl

2051. By Michael Zanetti on 2015-11-12

Add a warning dialog when disconnecting the external monitor.

In case there are still legacy apps running, the dialog forces the user to reconnect the external monitor, or close all the legacy apps.
Approved by: Daniel d'Andrada

2052. By Albert Astals Cid on 2015-11-12

Reset instead of qFatal when removing things from the middle

This can be optimized but let's see how this behaves, at least we won't qFatal anymore :D Fixes: #1238979
Approved by: Andrea Cimitan

2053. By CI Train Bot Account on 2015-11-12

Releasing 8.11+16.04.20151112.1-0ubuntu1

2054. By CI Train Bot Account on 2015-11-20

Resync trunk.

2055. By Launchpad Translations on behalf of unity-team on 2015-11-21

Launchpad automatic translations update.

2056. By Launchpad Translations on behalf of unity-team on 2015-11-23

Launchpad automatic translations update.

2057. By Josh Arenson on 2015-11-26

Don't let IntegratedLightDM muck with the username Fixes: #1497081
Approved by: Lukáš Tinkl

2058. By Michael Zanetti on 2015-11-26

move screenshots out of the Dash dir

This was consuming 2.5 megs of installed size while they really are
only used for testing.
Approved by: Albert Astals Cid

2059. By Daniel d'Andrada on 2015-11-26

Don't stretch application surfaces when resizing Fixes: #1497083
Approved by: Michael Zanetti

2060. By Lukáš Tinkl on 2015-11-26

Implement new visuals for panel and window decorations Fixes: #1493066, #1497095, #1511020
Approved by: Daniel d'Andrada

2061. By Daniel d'Andrada on 2015-11-26

Mouse has to push against edges to show launcher or apps spread

edge-barrier-sensitivity is the property that should show up in the System Settings GUI.

edge-barrier-min-push and edge-barrier-max-push are exposed in GSettings merely as a convenience so you can tweak those values without having to restart unity8. But they should NOT show up in system settings. Fixes: #1510969
Approved by: Michael Zanetti

2062. By Daniel d'Andrada on 2015-11-26

DesktopStage: swiping from right edge shows window spread
Approved by: Lukáš Tinkl

2063. By Daniel d'Andrada on 2015-11-26

Cursor: Add more fallback names, a blank and custom cursor support
Approved by: Lukáš Tinkl

2064. By Albert Astals Cid on 2015-11-26

clazy improvements

Pass small (<=16 bytes) and trivially-copyable[1] and trivially-destructible [2] type by value [3]
Don't call QList::operator[]() on temporary
Fix QString(const char*) being called
Use qEnvironmentVariableIsEmpty() instead of qgetenv().isEmpty()
Use the multiarg arg()
use Q_FOREACH for non const qt containers
delete copy constructor and assignment operator for non copyable class
remove assignment operator that does the same as the default one

[1] http://en.cppreference.com/w/cpp/concept/TriviallyCopyable
[2] http://www.cplusplus.com/reference/type_traits/is_trivially_destructible/
[3] http://www.macieira.org/blog/2012/02/the-value-of-passing-by-value/

2065. By Lukáš Tinkl on 2015-11-26

Make saving screenshots really async
Approved by: Michael Zanetti

2066. By Albert Astals Cid on 2015-11-26

Initialize the image to be transparent

Otherwise since we're not painting all the pixels we get noise Fixes: #1517128
Approved by: Daniel d'Andrada

2067. By Daniel d'Andrada on 2015-11-26

Cursor plugin: add scroll wheel handling Fixes: #1497091
Approved by: Lukáš Tinkl

2068. By Albert Astals Cid on 2015-11-26

Update pot file

Approved by: Lukáš Tinkl, Michael Terry

2069. By Michael Zanetti on 2015-11-26

detect touchpads too, not only mice Fixes: #1518395
Approved by: Lukáš Tinkl, Daniel d'Andrada

2070. By Lukáš Tinkl on 2015-11-26

Let systemd/logind handle the lid-close action
Approved by: Michael Zanetti

2071. By Andrea Cimitan on 2015-11-26

Shadows in launcher and dash using UbuntuShapeIcon
Approved by: Michael Zanetti, Albert Astals Cid

2072. By CI Train Bot Account on 2015-11-26

Releasing 8.11+16.04.20151126-0ubuntu1

2073. By CI Train Bot Account on 2015-11-29

Resync trunk.

2074. By Launchpad Translations on behalf of unity-team on 2015-11-30

Launchpad automatic translations update.

2075. By Launchpad Translations on behalf of unity-team on 2015-12-01

Launchpad automatic translations update.

2076. By Timo Jyrinki on 2015-12-01

Rebuild against Qt 5.5.1.

2077. By Launchpad Translations on behalf of unity-team on 2015-12-02

Launchpad automatic translations update.

2078. By Launchpad Translations on behalf of unity-team on 2015-12-05

Launchpad automatic translations update.

2079. By Launchpad Translations on behalf of unity-team on 2015-12-07

Launchpad automatic translations update.

2080. By Daniel d'Andrada on 2015-12-08

MirSurfaceItem got a new property: fillMode

And put it to use in SurfaceContainer. That's the final piece to finally get rid of stretched windows Fixes: #1497083

2081. By Daniel d'Andrada on 2015-12-08

Session can have multiple surfaces now

As a first step, we just display the latest surface for each application.

2082. By Michael Terry on 2015-12-08

Let qtmir know which apps are exempt from the lifecycle management.
This way, it can manage its own wakelocks better (and stop preventing the system from deep sleeping).

2083. By Albert Astals Cid on 2015-12-08

Make sure that unfavoriting a scope gives us the next one

This is currently broken due to a regression in the Qt Quick ListView
Approved by: Michael Zanetti

2084. By Albert Astals Cid on 2015-12-08

Create ratings on demand instead of all at the same time
 Fixes: #1492214, #1519898
Approved by: Michael Zanetti

2085. By Andrea Cimitan on 2015-12-08

Add shadows to ubuntu store icon
Approved by: Albert Astals Cid

2086. By Albert Astals Cid on 2015-12-08

Move images only used in tests to tests folder
Approved by: Michael Zanetti

2087. By Lukáš Tinkl on 2015-12-08

Indicators convergence: use the "phone" profile everywhere Fixes: #1520492
Approved by: Albert Astals Cid

2088. By Albert Astals Cid on 2015-12-08

LVWPH: Process correctly section changes Fixes: #1519893
Approved by: Michael Zanetti

2089. By Daniel d'Andrada on 2015-12-08

plugins/Cursor: Do not force loading a specific cursor size Fixes: #1517878
Approved by: Lukáš Tinkl

2090. By Michael Zanetti on 2015-12-08

drop the ignoredMice hack again

the new inputinfo backend seems to be clever enough to not
wrongly detect the hardware in there as real a real mouse Fixes: #1521580
Approved by: Daniel d'Andrada

2091. By Michael Zanetti on 2015-12-08

add some debug prints to the uinput backend

this should make debugging bug reports easier
Approved by: Michael Terry

2092. By Michał Sawicz on 2015-12-08

Update .pot file in debian/clean when in train
Approved by: Didier Roche, Robert Bruce Park

2093. By Albert Astals Cid on 2015-12-08

Allow dragging launcher items with the quicklist open
 Fixes: #1250861
Approved by: Michael Zanetti

2094. By Michael Zanetti on 2015-12-08

Use proper z ordering instead of app index for occlusion detection
Approved by: Nick Dedekind

2095. By CI Train Bot Account on 2015-12-08

Releasing 8.11+16.04.20151208.1-0ubuntu1

2096. By CI Train Bot Account on 2015-12-08

Update translation template

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-09-30 16:49:52 +0000
3+++ CMakeLists.txt 2015-10-26 09:06:21 +0000
4@@ -57,7 +57,7 @@
5 find_package(Qt5Concurrent 5.2 REQUIRED)
6 find_package(Qt5Sql 5.2 REQUIRED)
7
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application>=8)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=9)
10
11 # Standard install paths
12 include(GNUInstallDirs)
13@@ -101,6 +101,9 @@
14 # Save a few container detach and conversions
15 add_definitions(-DQT_STRICT_ITERATORS)
16
17+# Use the fast string builder
18+add_definitions(-DQT_USE_QSTRINGBUILDER)
19+
20 # Autopilot tests
21 include(autopilot)
22 declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
23
24=== modified file 'data/unity8.conf'
25--- data/unity8.conf 2015-09-01 12:41:57 +0000
26+++ data/unity8.conf 2015-10-26 09:06:21 +0000
27@@ -46,6 +46,16 @@
28 initctl set-env --global MIR_SERVER_PROMPT_FILE=1
29
30 initctl emit --no-wait indicator-services-start
31+
32+ # Disable Qt's stuttering 'touch compression' to fix scrolling smoothness
33+ # issues (LP: #1486341). As a bonus, this eliminates most of the
34+ # lag seen in the indicator panel pull-down (LP: #1488327) and also
35+ # reduces lag seen in apps:
36+ initctl set-env --global QML_NO_TOUCH_COMPRESSION=1
37+
38+ # For twice the fun and half the latency, try this (Warning: not all
39+ # devices are fast enough to keep up smoothly yet)...
40+ # initctl set-env MIR_SERVER_NBUFFERS=2
41 end script
42
43 exec ${BINARY:-unity8} $ARGS
44
45=== modified file 'debian/changelog'
46--- debian/changelog 2015-10-09 09:13:03 +0000
47+++ debian/changelog 2015-10-26 09:06:21 +0000
48@@ -1,3 +1,39 @@
49+unity8 (8.11+15.10.20151021-0ubuntu1) wily; urgency=medium
50+
51+ [ Albert Astals Cid ]
52+ * Clazy fixes
53+ * Enable Efficient String Construction by default
54+
55+ [ CI Train Bot ]
56+ * New rebuild forced.
57+
58+ [ Daniel d'Andrada ]
59+ * Have unity8 drawing its own cursor (LP: #1488417)
60+ * Initial multi-monitor support
61+
62+ [ Daniel van Vugt ]
63+ * Disable Qt's stuttering 'touch compression' to fix scrolling
64+ smoothness (LP: #1486341, #1488327)
65+
66+ [ Lukáš Tinkl ]
67+ * Fix autopilot wizard test skipping the reporting page
68+ * Implement Unity.Platform plugin wrapping org.freedesktop.hostname1
69+ (LP: #1504318)
70+ * React to window title (aka surface name) changes (LP: #1497092)
71+ * Rotate the screenshots according to the actual orientation
72+
73+ [ Michał Sawicz ]
74+ * Fix application API dependency
75+ * Have unity8 drawing its own cursor (LP: #1488417)
76+ * Initial multi-monitor support
77+ * Rotate the screenshots according to the actual orientation
78+
79+ [ Nick Dedekind ]
80+ * Fixed leak in UnityMenuModelStackTest
81+ * Moved time translation to SDK (LP: #1372061)
82+
83+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 21 Oct 2015 11:51:53 +0000
84+
85 unity8 (8.11+15.10.20151009-0ubuntu1) wily; urgency=medium
86
87 [ CI Train Bot ]
88
89=== modified file 'debian/control'
90--- debian/control 2015-09-23 18:15:40 +0000
91+++ debian/control 2015-10-26 09:06:21 +0000
92@@ -25,10 +25,11 @@
93 libpay2-dev,
94 libpulse-dev,
95 libqmenumodel-dev (>= 0.2.9),
96+ libqt5svg5-dev,
97 libqt5xmlpatterns5-dev,
98 libsystemsettings-dev,
99 libudev-dev,
100- libunity-api-dev (>= 7.100),
101+ libunity-api-dev (>= 7.101),
102 libusermetricsoutput1-dev,
103 libxcb1-dev,
104 pkg-config,
105@@ -49,7 +50,7 @@
106 qtdeclarative5-private-dev (>= 5.2.1),
107 qtdeclarative5-qtmultimedia-plugin,
108 qtdeclarative5-ubuntu-settings-components (>= 0.6),
109- qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
110+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1627) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1627),
111 qtdeclarative5-ubuntu-web-plugin,
112 ttf-ubuntu-font-family,
113 Standards-Version: 3.9.4
114@@ -64,7 +65,7 @@
115 Package: indicators-client
116 Architecture: amd64 armhf i386
117 Depends: qmenumodel-qml (>= 0.2.9),
118- qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
119+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1627) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1627),
120 unity8 (= ${binary:Version}),
121 ${misc:Depends},
122 ${shlibs:Depends},
123@@ -87,7 +88,8 @@
124 Package: unity8
125 Architecture: any
126 Provides: indicator-renderer,
127-Depends: gsettings-desktop-schemas,
128+Depends: dmz-cursor-theme,
129+ gsettings-desktop-schemas,
130 libcap2-bin,
131 libglib2.0-bin,
132 qmenumodel-qml (>= 0.2.9),
133@@ -122,10 +124,10 @@
134 Depends: qml-module-qtquick-layouts,
135 qtdeclarative5-ubuntu-settings-components (>= 0.6),
136 qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
137- qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
138+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1627) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1627),
139 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
140 ubuntu-thumbnailer-impl-0,
141- unity-application-impl-8,
142+ unity-application-impl-9,
143 unity-notifications-impl-3,
144 unity-plugin-scopes | unity-scopes-impl,
145 unity-scopes-impl-7,
146@@ -171,7 +173,7 @@
147 Depends: ${misc:Depends},
148 ${shlibs:Depends},
149 Provides: unity-application-impl,
150- unity-application-impl-8,
151+ unity-application-impl-9,
152 Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
153 Description: Fake environment for running Unity 8 shell
154 Provides fake implementations of some QML modules used by Unity 8 shell
155
156=== modified file 'debian/copyright'
157--- debian/copyright 2014-06-11 15:36:51 +0000
158+++ debian/copyright 2015-10-26 09:06:21 +0000
159@@ -60,3 +60,24 @@
160 packaging of this file. Please review the following information to
161 ensure the GNU General Public License version 3.0 requirements will be
162 met: http://www.gnu.org/copyleft/gpl.html.
163+
164+Files: plugins/Cursor/3rd_party/xcursor/xcursor.*
165+Copyright: 2002 Keith Packard
166+License: Keith Packard
167+ Permission to use, copy, modify, distribute, and sell this software and its
168+ documentation for any purpose is hereby granted without fee, provided that
169+ the above copyright notice appear in all copies and that both that
170+ copyright notice and this permission notice appear in supporting
171+ documentation, and that the name of Keith Packard not be used in
172+ advertising or publicity pertaining to distribution of the software without
173+ specific, written prior permission. Keith Packard makes no
174+ representations about the suitability of this software for any purpose. It
175+ is provided "as is" without express or implied warranty.
176+ .
177+ KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
178+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
179+ EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
180+ CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
181+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
182+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
183+ PERFORMANCE OF THIS SOFTWARE.
184
185=== modified file 'debian/unity8-private.install'
186--- debian/unity8-private.install 2015-09-02 09:30:32 +0000
187+++ debian/unity8-private.install 2015-10-26 09:06:21 +0000
188@@ -1,6 +1,7 @@
189 usr/lib/*/libunity8-private.*
190 usr/lib/*/unity8/libUbuntuGestures*
191 usr/lib/*/unity8/qml/AccountsService
192+usr/lib/*/unity8/qml/Cursor
193 usr/lib/*/unity8/qml/Dash
194 usr/lib/*/unity8/qml/GlobalShortcut
195 usr/lib/*/unity8/qml/Greeter
196
197=== modified file 'debian/unity8.install'
198--- debian/unity8.install 2015-10-01 13:10:32 +0000
199+++ debian/unity8.install 2015-10-26 09:06:21 +0000
200@@ -12,6 +12,7 @@
201 usr/share/unity8/Rotation
202 usr/share/unity8/DeviceConfiguration.qml
203 usr/share/unity8/OrientedShell.qml
204+usr/share/unity8/DisabledScreenNotice.qml
205 usr/share/unity8/Shell.qml
206 usr/share/unity8/Stages
207 usr/share/unity8/Tutorial
208
209=== modified file 'plugins/CMakeLists.txt'
210--- plugins/CMakeLists.txt 2015-09-02 09:30:32 +0000
211+++ plugins/CMakeLists.txt 2015-10-26 09:06:21 +0000
212@@ -12,6 +12,7 @@
213 endmacro()
214
215 add_subdirectory(AccountsService)
216+add_subdirectory(Cursor)
217 add_subdirectory(GlobalShortcut)
218 add_subdirectory(Greeter)
219 add_subdirectory(IntegratedLightDM)
220
221=== added directory 'plugins/Cursor'
222=== added directory 'plugins/Cursor/3rd_party'
223=== added file 'plugins/Cursor/3rd_party/CMakeLists.txt'
224--- plugins/Cursor/3rd_party/CMakeLists.txt 1970-01-01 00:00:00 +0000
225+++ plugins/Cursor/3rd_party/CMakeLists.txt 2015-10-26 09:06:21 +0000
226@@ -0,0 +1,1 @@
227+add_subdirectory(xcursor)
228
229=== added directory 'plugins/Cursor/3rd_party/xcursor'
230=== added file 'plugins/Cursor/3rd_party/xcursor/CMakeLists.txt'
231--- plugins/Cursor/3rd_party/xcursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
232+++ plugins/Cursor/3rd_party/xcursor/CMakeLists.txt 2015-10-26 09:06:21 +0000
233@@ -0,0 +1,15 @@
234+add_definitions(-D_DEFAULT_SOURCE=1)
235+
236+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
237+
238+set(
239+ XCURSOR_SOURCES
240+
241+ xcursor.c
242+)
243+
244+add_library(
245+ xcursorloader-static STATIC
246+
247+ ${XCURSOR_SOURCES}
248+)
249
250=== added file 'plugins/Cursor/3rd_party/xcursor/xcursor.c'
251--- plugins/Cursor/3rd_party/xcursor/xcursor.c 1970-01-01 00:00:00 +0000
252+++ plugins/Cursor/3rd_party/xcursor/xcursor.c 2015-10-26 09:06:21 +0000
253@@ -0,0 +1,968 @@
254+/*
255+ * Copyright © 2002 Keith Packard
256+ *
257+ * Permission to use, copy, modify, distribute, and sell this software and its
258+ * documentation for any purpose is hereby granted without fee, provided that
259+ * the above copyright notice appear in all copies and that both that
260+ * copyright notice and this permission notice appear in supporting
261+ * documentation, and that the name of Keith Packard not be used in
262+ * advertising or publicity pertaining to distribution of the software without
263+ * specific, written prior permission. Keith Packard makes no
264+ * representations about the suitability of this software for any purpose. It
265+ * is provided "as is" without express or implied warranty.
266+ *
267+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
268+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
269+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
270+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
271+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
272+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
273+ * PERFORMANCE OF THIS SOFTWARE.
274+ */
275+
276+#include "xcursor.h"
277+#include <stdio.h>
278+#include <stdlib.h>
279+#include <string.h>
280+#include <dirent.h>
281+
282+/*
283+ * From libXcursor/include/X11/extensions/Xcursor.h
284+ */
285+
286+#define XcursorTrue 1
287+#define XcursorFalse 0
288+
289+/*
290+ * Cursor files start with a header. The header
291+ * contains a magic number, a version number and a
292+ * table of contents which has type and offset information
293+ * for the remaining tables in the file.
294+ *
295+ * File minor versions increment for compatible changes
296+ * File major versions increment for incompatible changes (never, we hope)
297+ *
298+ * Chunks of the same type are always upward compatible. Incompatible
299+ * changes are made with new chunk types; the old data can remain under
300+ * the old type. Upward compatible changes can add header data as the
301+ * header lengths are specified in the file.
302+ *
303+ * File:
304+ * FileHeader
305+ * LISTofChunk
306+ *
307+ * FileHeader:
308+ * CARD32 magic magic number
309+ * CARD32 header bytes in file header
310+ * CARD32 version file version
311+ * CARD32 ntoc number of toc entries
312+ * LISTofFileToc toc table of contents
313+ *
314+ * FileToc:
315+ * CARD32 type entry type
316+ * CARD32 subtype entry subtype (size for images)
317+ * CARD32 position absolute file position
318+ */
319+
320+#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */
321+
322+/*
323+ * Current Xcursor version number. Will be substituted by configure
324+ * from the version in the libXcursor configure.ac file.
325+ */
326+
327+#define XCURSOR_LIB_MAJOR 1
328+#define XCURSOR_LIB_MINOR 1
329+#define XCURSOR_LIB_REVISION 13
330+#define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \
331+ (XCURSOR_LIB_MINOR * 100) + \
332+ (XCURSOR_LIB_REVISION))
333+
334+/*
335+ * This version number is stored in cursor files; changes to the
336+ * file format require updating this version number
337+ */
338+#define XCURSOR_FILE_MAJOR 1
339+#define XCURSOR_FILE_MINOR 0
340+#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
341+#define XCURSOR_FILE_HEADER_LEN (4 * 4)
342+#define XCURSOR_FILE_TOC_LEN (3 * 4)
343+
344+typedef struct _XcursorFileToc {
345+ XcursorUInt type; /* chunk type */
346+ XcursorUInt subtype; /* subtype (size for images) */
347+ XcursorUInt position; /* absolute position in file */
348+} XcursorFileToc;
349+
350+typedef struct _XcursorFileHeader {
351+ XcursorUInt magic; /* magic number */
352+ XcursorUInt header; /* byte length of header */
353+ XcursorUInt version; /* file version number */
354+ XcursorUInt ntoc; /* number of toc entries */
355+ XcursorFileToc *tocs; /* table of contents */
356+} XcursorFileHeader;
357+
358+/*
359+ * The rest of the file is a list of chunks, each tagged by type
360+ * and version.
361+ *
362+ * Chunk:
363+ * ChunkHeader
364+ * <extra type-specific header fields>
365+ * <type-specific data>
366+ *
367+ * ChunkHeader:
368+ * CARD32 header bytes in chunk header + type header
369+ * CARD32 type chunk type
370+ * CARD32 subtype chunk subtype
371+ * CARD32 version chunk type version
372+ */
373+
374+#define XCURSOR_CHUNK_HEADER_LEN (4 * 4)
375+
376+typedef struct _XcursorChunkHeader {
377+ XcursorUInt header; /* bytes in chunk header */
378+ XcursorUInt type; /* chunk type */
379+ XcursorUInt subtype; /* chunk subtype (size for images) */
380+ XcursorUInt version; /* version of this type */
381+} XcursorChunkHeader;
382+
383+/*
384+ * Here's a list of the known chunk types
385+ */
386+
387+/*
388+ * Comments consist of a 4-byte length field followed by
389+ * UTF-8 encoded text
390+ *
391+ * Comment:
392+ * ChunkHeader header chunk header
393+ * CARD32 length bytes in text
394+ * LISTofCARD8 text UTF-8 encoded text
395+ */
396+
397+#define XCURSOR_COMMENT_TYPE 0xfffe0001
398+#define XCURSOR_COMMENT_VERSION 1
399+#define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
400+#define XCURSOR_COMMENT_COPYRIGHT 1
401+#define XCURSOR_COMMENT_LICENSE 2
402+#define XCURSOR_COMMENT_OTHER 3
403+#define XCURSOR_COMMENT_MAX_LEN 0x100000
404+
405+typedef struct _XcursorComment {
406+ XcursorUInt version;
407+ XcursorUInt comment_type;
408+ char *comment;
409+} XcursorComment;
410+
411+/*
412+ * Each cursor image occupies a separate image chunk.
413+ * The length of the image header follows the chunk header
414+ * so that future versions can extend the header without
415+ * breaking older applications
416+ *
417+ * Image:
418+ * ChunkHeader header chunk header
419+ * CARD32 width actual width
420+ * CARD32 height actual height
421+ * CARD32 xhot hot spot x
422+ * CARD32 yhot hot spot y
423+ * CARD32 delay animation delay
424+ * LISTofCARD32 pixels ARGB pixels
425+ */
426+
427+#define XCURSOR_IMAGE_TYPE 0xfffd0002
428+#define XCURSOR_IMAGE_VERSION 1
429+#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4))
430+#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
431+
432+typedef struct _XcursorFile XcursorFile;
433+
434+struct _XcursorFile {
435+ void *closure;
436+ int (*read) (XcursorFile *file, unsigned char *buf, int len);
437+ int (*write) (XcursorFile *file, unsigned char *buf, int len);
438+ int (*seek) (XcursorFile *file, long offset, int whence);
439+};
440+
441+typedef struct _XcursorComments {
442+ int ncomment; /* number of comments */
443+ XcursorComment **comments; /* array of XcursorComment pointers */
444+} XcursorComments;
445+
446+/*
447+ * From libXcursor/src/file.c
448+ */
449+
450+static XcursorImage *
451+XcursorImageCreate (int width, int height)
452+{
453+ XcursorImage *image;
454+
455+ image = malloc (sizeof (XcursorImage) +
456+ width * height * sizeof (XcursorPixel));
457+ if (!image)
458+ return NULL;
459+ image->version = XCURSOR_IMAGE_VERSION;
460+ image->pixels = (XcursorPixel *) (image + 1);
461+ image->size = width > height ? width : height;
462+ image->width = width;
463+ image->height = height;
464+ image->delay = 0;
465+ return image;
466+}
467+
468+static void
469+XcursorImageDestroy (XcursorImage *image)
470+{
471+ free (image);
472+}
473+
474+static XcursorImages *
475+XcursorImagesCreate (int size)
476+{
477+ XcursorImages *images;
478+
479+ images = malloc (sizeof (XcursorImages) +
480+ size * sizeof (XcursorImage *));
481+ if (!images)
482+ return NULL;
483+ images->nimage = 0;
484+ images->images = (XcursorImage **) (images + 1);
485+ images->name = NULL;
486+ return images;
487+}
488+
489+void
490+XcursorImagesDestroy (XcursorImages *images)
491+{
492+ int n;
493+
494+ if (!images)
495+ return;
496+
497+ for (n = 0; n < images->nimage; n++)
498+ XcursorImageDestroy (images->images[n]);
499+ if (images->name)
500+ free (images->name);
501+ free (images);
502+}
503+
504+static void
505+XcursorImagesSetName (XcursorImages *images, const char *name)
506+{
507+ char *new;
508+
509+ if (!images || !name)
510+ return;
511+
512+ new = malloc (strlen (name) + 1);
513+
514+ if (!new)
515+ return;
516+
517+ strcpy (new, name);
518+ if (images->name)
519+ free (images->name);
520+ images->name = new;
521+}
522+
523+static XcursorBool
524+_XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
525+{
526+ unsigned char bytes[4];
527+
528+ if (!file || !u)
529+ return XcursorFalse;
530+
531+ if ((*file->read) (file, bytes, 4) != 4)
532+ return XcursorFalse;
533+ *u = ((bytes[0] << 0) |
534+ (bytes[1] << 8) |
535+ (bytes[2] << 16) |
536+ (bytes[3] << 24));
537+ return XcursorTrue;
538+}
539+
540+static void
541+_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
542+{
543+ free (fileHeader);
544+}
545+
546+static XcursorFileHeader *
547+_XcursorFileHeaderCreate (int ntoc)
548+{
549+ XcursorFileHeader *fileHeader;
550+
551+ if (ntoc > 0x10000)
552+ return NULL;
553+ fileHeader = malloc (sizeof (XcursorFileHeader) +
554+ ntoc * sizeof (XcursorFileToc));
555+ if (!fileHeader)
556+ return NULL;
557+ fileHeader->magic = XCURSOR_MAGIC;
558+ fileHeader->header = XCURSOR_FILE_HEADER_LEN;
559+ fileHeader->version = XCURSOR_FILE_VERSION;
560+ fileHeader->ntoc = ntoc;
561+ fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
562+ return fileHeader;
563+}
564+
565+static XcursorFileHeader *
566+_XcursorReadFileHeader (XcursorFile *file)
567+{
568+ XcursorFileHeader head, *fileHeader;
569+ XcursorUInt skip;
570+ unsigned int n;
571+
572+ if (!file)
573+ return NULL;
574+
575+ if (!_XcursorReadUInt (file, &head.magic))
576+ return NULL;
577+ if (head.magic != XCURSOR_MAGIC)
578+ return NULL;
579+ if (!_XcursorReadUInt (file, &head.header))
580+ return NULL;
581+ if (!_XcursorReadUInt (file, &head.version))
582+ return NULL;
583+ if (!_XcursorReadUInt (file, &head.ntoc))
584+ return NULL;
585+ skip = head.header - XCURSOR_FILE_HEADER_LEN;
586+ if (skip)
587+ if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
588+ return NULL;
589+ fileHeader = _XcursorFileHeaderCreate (head.ntoc);
590+ if (!fileHeader)
591+ return NULL;
592+ fileHeader->magic = head.magic;
593+ fileHeader->header = head.header;
594+ fileHeader->version = head.version;
595+ fileHeader->ntoc = head.ntoc;
596+ for (n = 0; n < fileHeader->ntoc; n++)
597+ {
598+ if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
599+ break;
600+ if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
601+ break;
602+ if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
603+ break;
604+ }
605+ if (n != fileHeader->ntoc)
606+ {
607+ _XcursorFileHeaderDestroy (fileHeader);
608+ return NULL;
609+ }
610+ return fileHeader;
611+}
612+
613+static XcursorBool
614+_XcursorSeekToToc (XcursorFile *file,
615+ XcursorFileHeader *fileHeader,
616+ int toc)
617+{
618+ if (!file || !fileHeader || \
619+ (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
620+ return XcursorFalse;
621+ return XcursorTrue;
622+}
623+
624+static XcursorBool
625+_XcursorFileReadChunkHeader (XcursorFile *file,
626+ XcursorFileHeader *fileHeader,
627+ int toc,
628+ XcursorChunkHeader *chunkHeader)
629+{
630+ if (!file || !fileHeader || !chunkHeader)
631+ return XcursorFalse;
632+ if (!_XcursorSeekToToc (file, fileHeader, toc))
633+ return XcursorFalse;
634+ if (!_XcursorReadUInt (file, &chunkHeader->header))
635+ return XcursorFalse;
636+ if (!_XcursorReadUInt (file, &chunkHeader->type))
637+ return XcursorFalse;
638+ if (!_XcursorReadUInt (file, &chunkHeader->subtype))
639+ return XcursorFalse;
640+ if (!_XcursorReadUInt (file, &chunkHeader->version))
641+ return XcursorFalse;
642+ /* sanity check */
643+ if (chunkHeader->type != fileHeader->tocs[toc].type ||
644+ chunkHeader->subtype != fileHeader->tocs[toc].subtype)
645+ return XcursorFalse;
646+ return XcursorTrue;
647+}
648+
649+#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a))
650+
651+static XcursorDim
652+_XcursorFindBestSize (XcursorFileHeader *fileHeader,
653+ XcursorDim size,
654+ int *nsizesp)
655+{
656+ unsigned int n;
657+ int nsizes = 0;
658+ XcursorDim bestSize = 0;
659+ XcursorDim thisSize;
660+
661+ if (!fileHeader || !nsizesp)
662+ return 0;
663+
664+ for (n = 0; n < fileHeader->ntoc; n++)
665+ {
666+ if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
667+ continue;
668+ thisSize = fileHeader->tocs[n].subtype;
669+ if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
670+ {
671+ bestSize = thisSize;
672+ nsizes = 1;
673+ }
674+ else if (thisSize == bestSize)
675+ nsizes++;
676+ }
677+ *nsizesp = nsizes;
678+ return bestSize;
679+}
680+
681+static int
682+_XcursorFindImageToc (XcursorFileHeader *fileHeader,
683+ XcursorDim size,
684+ int count)
685+{
686+ unsigned int toc;
687+ XcursorDim thisSize;
688+
689+ if (!fileHeader)
690+ return 0;
691+
692+ for (toc = 0; toc < fileHeader->ntoc; toc++)
693+ {
694+ if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
695+ continue;
696+ thisSize = fileHeader->tocs[toc].subtype;
697+ if (thisSize != size)
698+ continue;
699+ if (!count)
700+ break;
701+ count--;
702+ }
703+ if (toc == fileHeader->ntoc)
704+ return -1;
705+ return toc;
706+}
707+
708+static XcursorImage *
709+_XcursorReadImage (XcursorFile *file,
710+ XcursorFileHeader *fileHeader,
711+ int toc)
712+{
713+ XcursorChunkHeader chunkHeader;
714+ XcursorImage head;
715+ XcursorImage *image;
716+ int n;
717+ XcursorPixel *p;
718+
719+ if (!file || !fileHeader)
720+ return NULL;
721+
722+ if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
723+ return NULL;
724+ if (!_XcursorReadUInt (file, &head.width))
725+ return NULL;
726+ if (!_XcursorReadUInt (file, &head.height))
727+ return NULL;
728+ if (!_XcursorReadUInt (file, &head.xhot))
729+ return NULL;
730+ if (!_XcursorReadUInt (file, &head.yhot))
731+ return NULL;
732+ if (!_XcursorReadUInt (file, &head.delay))
733+ return NULL;
734+ /* sanity check data */
735+ if (head.width >= 0x10000 || head.height > 0x10000)
736+ return NULL;
737+ if (head.width == 0 || head.height == 0)
738+ return NULL;
739+ if (head.xhot > head.width || head.yhot > head.height)
740+ return NULL;
741+
742+ /* Create the image and initialize it */
743+ image = XcursorImageCreate (head.width, head.height);
744+ if (image == NULL)
745+ return NULL;
746+ if (chunkHeader.version < image->version)
747+ image->version = chunkHeader.version;
748+ image->size = chunkHeader.subtype;
749+ image->xhot = head.xhot;
750+ image->yhot = head.yhot;
751+ image->delay = head.delay;
752+ n = image->width * image->height;
753+ p = image->pixels;
754+ while (n--)
755+ {
756+ if (!_XcursorReadUInt (file, p))
757+ {
758+ XcursorImageDestroy (image);
759+ return NULL;
760+ }
761+ p++;
762+ }
763+ return image;
764+}
765+
766+static XcursorImages *
767+XcursorXcFileLoadImages (XcursorFile *file, int size)
768+{
769+ XcursorFileHeader *fileHeader;
770+ XcursorDim bestSize;
771+ int nsize;
772+ XcursorImages *images;
773+ int n;
774+ int toc;
775+
776+ if (!file || size < 0)
777+ return NULL;
778+ fileHeader = _XcursorReadFileHeader (file);
779+ if (!fileHeader)
780+ return NULL;
781+ bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
782+ if (!bestSize)
783+ {
784+ _XcursorFileHeaderDestroy (fileHeader);
785+ return NULL;
786+ }
787+ images = XcursorImagesCreate (nsize);
788+ if (!images)
789+ {
790+ _XcursorFileHeaderDestroy (fileHeader);
791+ return NULL;
792+ }
793+ for (n = 0; n < nsize; n++)
794+ {
795+ toc = _XcursorFindImageToc (fileHeader, bestSize, n);
796+ if (toc < 0)
797+ break;
798+ images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
799+ toc);
800+ if (!images->images[images->nimage])
801+ break;
802+ images->nimage++;
803+ }
804+ _XcursorFileHeaderDestroy (fileHeader);
805+ if (images->nimage != nsize)
806+ {
807+ XcursorImagesDestroy (images);
808+ images = NULL;
809+ }
810+ return images;
811+}
812+
813+static int
814+_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
815+{
816+ FILE *f = file->closure;
817+ return fread (buf, 1, len, f);
818+}
819+
820+static int
821+_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
822+{
823+ FILE *f = file->closure;
824+ return fwrite (buf, 1, len, f);
825+}
826+
827+static int
828+_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
829+{
830+ FILE *f = file->closure;
831+ return fseek (f, offset, whence);
832+}
833+
834+static void
835+_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
836+{
837+ file->closure = stdfile;
838+ file->read = _XcursorStdioFileRead;
839+ file->write = _XcursorStdioFileWrite;
840+ file->seek = _XcursorStdioFileSeek;
841+}
842+
843+static XcursorImages *
844+XcursorFileLoadImages (FILE *file, int size)
845+{
846+ XcursorFile f;
847+
848+ if (!file)
849+ return NULL;
850+
851+ _XcursorStdioFileInitialize (file, &f);
852+ return XcursorXcFileLoadImages (&f, size);
853+}
854+
855+/*
856+ * From libXcursor/src/library.c
857+ */
858+
859+#ifndef ICONDIR
860+#define ICONDIR "/usr/X11R6/lib/X11/icons"
861+#endif
862+
863+#ifndef XCURSORPATH
864+#define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR
865+#endif
866+
867+static const char *
868+XcursorLibraryPath (void)
869+{
870+ static const char *path;
871+
872+ if (!path)
873+ {
874+ path = getenv ("XCURSOR_PATH");
875+ if (!path)
876+ path = XCURSORPATH;
877+ }
878+ return path;
879+}
880+
881+static void
882+_XcursorAddPathElt (char *path, const char *elt, int len)
883+{
884+ int pathlen = strlen (path);
885+
886+ /* append / if the path doesn't currently have one */
887+ if (path[0] == '\0' || path[pathlen - 1] != '/')
888+ {
889+ strcat (path, "/");
890+ pathlen++;
891+ }
892+ if (len == -1)
893+ len = strlen (elt);
894+ /* strip leading slashes */
895+ while (len && elt[0] == '/')
896+ {
897+ elt++;
898+ len--;
899+ }
900+ strncpy (path + pathlen, elt, len);
901+ path[pathlen + len] = '\0';
902+}
903+
904+static char *
905+_XcursorBuildThemeDir (const char *dir, const char *theme)
906+{
907+ const char *colon;
908+ const char *tcolon;
909+ char *full;
910+ char *home;
911+ int dirlen;
912+ int homelen;
913+ int themelen;
914+ int len;
915+
916+ if (!dir || !theme)
917+ return NULL;
918+
919+ colon = strchr (dir, ':');
920+ if (!colon)
921+ colon = dir + strlen (dir);
922+
923+ dirlen = colon - dir;
924+
925+ tcolon = strchr (theme, ':');
926+ if (!tcolon)
927+ tcolon = theme + strlen (theme);
928+
929+ themelen = tcolon - theme;
930+
931+ home = NULL;
932+ homelen = 0;
933+ if (*dir == '~')
934+ {
935+ home = getenv ("HOME");
936+ if (!home)
937+ return NULL;
938+ homelen = strlen (home);
939+ dir++;
940+ dirlen--;
941+ }
942+
943+ /*
944+ * add space for any needed directory separators, one per component,
945+ * and one for the trailing null
946+ */
947+ len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
948+
949+ full = malloc (len);
950+ if (!full)
951+ return NULL;
952+ full[0] = '\0';
953+
954+ if (home)
955+ _XcursorAddPathElt (full, home, -1);
956+ _XcursorAddPathElt (full, dir, dirlen);
957+ _XcursorAddPathElt (full, theme, themelen);
958+ return full;
959+}
960+
961+static char *
962+_XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
963+{
964+ char *full;
965+
966+ if (!dir || !subdir || !file)
967+ return NULL;
968+
969+ full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
970+ if (!full)
971+ return NULL;
972+ full[0] = '\0';
973+ _XcursorAddPathElt (full, dir, -1);
974+ _XcursorAddPathElt (full, subdir, -1);
975+ _XcursorAddPathElt (full, file, -1);
976+ return full;
977+}
978+
979+static const char *
980+_XcursorNextPath (const char *path)
981+{
982+ char *colon = strchr (path, ':');
983+
984+ if (!colon)
985+ return NULL;
986+ return colon + 1;
987+}
988+
989+#define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
990+#define XcursorSep(c) ((c) == ';' || (c) == ',')
991+
992+static char *
993+_XcursorThemeInherits (const char *full)
994+{
995+ char line[8192];
996+ char *result = NULL;
997+ FILE *f;
998+
999+ if (!full)
1000+ return NULL;
1001+
1002+ f = fopen (full, "r");
1003+ if (f)
1004+ {
1005+ while (fgets (line, sizeof (line), f))
1006+ {
1007+ if (!strncmp (line, "Inherits", 8))
1008+ {
1009+ char *l = line + 8;
1010+ char *r;
1011+ while (*l == ' ') l++;
1012+ if (*l != '=') continue;
1013+ l++;
1014+ while (*l == ' ') l++;
1015+ result = malloc (strlen (l) + 1);
1016+ if (result)
1017+ {
1018+ r = result;
1019+ while (*l)
1020+ {
1021+ while (XcursorSep(*l) || XcursorWhite (*l)) l++;
1022+ if (!*l)
1023+ break;
1024+ if (r != result)
1025+ *r++ = ':';
1026+ while (*l && !XcursorWhite(*l) &&
1027+ !XcursorSep(*l))
1028+ *r++ = *l++;
1029+ }
1030+ *r++ = '\0';
1031+ }
1032+ break;
1033+ }
1034+ }
1035+ fclose (f);
1036+ }
1037+ return result;
1038+}
1039+
1040+static FILE *
1041+XcursorScanTheme (const char *theme, const char *name)
1042+{
1043+ FILE *f = NULL;
1044+ char *full;
1045+ char *dir;
1046+ const char *path;
1047+ char *inherits = NULL;
1048+ const char *i;
1049+
1050+ if (!theme || !name)
1051+ return NULL;
1052+
1053+ /*
1054+ * Scan this theme
1055+ */
1056+ for (path = XcursorLibraryPath ();
1057+ path && f == NULL;
1058+ path = _XcursorNextPath (path))
1059+ {
1060+ dir = _XcursorBuildThemeDir (path, theme);
1061+ if (dir)
1062+ {
1063+ full = _XcursorBuildFullname (dir, "cursors", name);
1064+ if (full)
1065+ {
1066+ f = fopen (full, "r");
1067+ free (full);
1068+ }
1069+ if (!f && !inherits)
1070+ {
1071+ full = _XcursorBuildFullname (dir, "", "index.theme");
1072+ if (full)
1073+ {
1074+ inherits = _XcursorThemeInherits (full);
1075+ free (full);
1076+ }
1077+ }
1078+ free (dir);
1079+ }
1080+ }
1081+ /*
1082+ * Recurse to scan inherited themes
1083+ */
1084+ for (i = inherits; i && f == NULL; i = _XcursorNextPath (i))
1085+ f = XcursorScanTheme (i, name);
1086+ if (inherits != NULL)
1087+ free (inherits);
1088+ return f;
1089+}
1090+
1091+XcursorImages *
1092+XcursorLibraryLoadImages (const char *file, const char *theme, int size)
1093+{
1094+ FILE *f = NULL;
1095+ XcursorImages *images = NULL;
1096+
1097+ if (!file)
1098+ return NULL;
1099+
1100+ if (theme)
1101+ f = XcursorScanTheme (theme, file);
1102+ if (!f)
1103+ f = XcursorScanTheme ("default", file);
1104+ if (f)
1105+ {
1106+ images = XcursorFileLoadImages (f, size);
1107+ if (images)
1108+ XcursorImagesSetName (images, file);
1109+ fclose (f);
1110+ }
1111+ return images;
1112+}
1113+
1114+static void
1115+load_all_cursors_from_dir(const char *path, int size,
1116+ void (*load_callback)(XcursorImages *, void *),
1117+ void *user_data)
1118+{
1119+ FILE *f;
1120+ DIR *dir = opendir(path);
1121+ struct dirent *ent;
1122+ char *full;
1123+ XcursorImages *images;
1124+
1125+ if (!dir)
1126+ return;
1127+
1128+ for(ent = readdir(dir); ent; ent = readdir(dir)) {
1129+#ifdef _DIRENT_HAVE_D_TYPE
1130+ if (ent->d_type != DT_UNKNOWN &&
1131+ (ent->d_type != DT_REG && ent->d_type != DT_LNK))
1132+ continue;
1133+#endif
1134+
1135+ full = _XcursorBuildFullname(path, "", ent->d_name);
1136+ if (!full)
1137+ continue;
1138+
1139+ f = fopen(full, "r");
1140+ if (!f) {
1141+ free(full);
1142+ continue;
1143+ }
1144+
1145+ images = XcursorFileLoadImages(f, size);
1146+
1147+ if (images) {
1148+ XcursorImagesSetName(images, ent->d_name);
1149+ load_callback(images, user_data);
1150+ }
1151+
1152+ fclose (f);
1153+ free(full);
1154+ }
1155+
1156+ closedir(dir);
1157+}
1158+
1159+/** Load all the cursor of a theme
1160+ *
1161+ * This function loads all the cursor images of a given theme and its
1162+ * inherited themes. Each cursor is loaded into an XcursorImages object
1163+ * which is passed to the caller's load callback. If a cursor appears
1164+ * more than once across all the inherited themes, the load callback
1165+ * will be called multiple times, with possibly different XcursorImages
1166+ * object which have the same name. The user is expected to destroy the
1167+ * XcursorImages objects passed to the callback with
1168+ * XcursorImagesDestroy().
1169+ *
1170+ * \param theme The name of theme that should be loaded
1171+ * \param size The desired size of the cursor images
1172+ * \param load_callback A callback function that will be called
1173+ * for each cursor loaded. The first parameter is the XcursorImages
1174+ * object representing the loaded cursor and the second is a pointer
1175+ * to data provided by the user.
1176+ * \param user_data The data that should be passed to the load callback
1177+ */
1178+void
1179+xcursor_load_theme(const char *theme, int size,
1180+ void (*load_callback)(XcursorImages *, void *),
1181+ void *user_data)
1182+{
1183+ char *full, *dir;
1184+ char *inherits = NULL;
1185+ const char *path, *i;
1186+
1187+ if (!theme)
1188+ theme = "default";
1189+
1190+ for (path = XcursorLibraryPath();
1191+ path;
1192+ path = _XcursorNextPath(path)) {
1193+ dir = _XcursorBuildThemeDir(path, theme);
1194+ if (!dir)
1195+ continue;
1196+
1197+ full = _XcursorBuildFullname(dir, "cursors", "");
1198+
1199+ if (full) {
1200+ load_all_cursors_from_dir(full, size, load_callback,
1201+ user_data);
1202+ free(full);
1203+ }
1204+
1205+ if (!inherits) {
1206+ full = _XcursorBuildFullname(dir, "", "index.theme");
1207+ if (full) {
1208+ inherits = _XcursorThemeInherits(full);
1209+ free(full);
1210+ }
1211+ }
1212+
1213+ free(dir);
1214+ }
1215+
1216+ for (i = inherits; i; i = _XcursorNextPath(i))
1217+ xcursor_load_theme(i, size, load_callback, user_data);
1218+
1219+ if (inherits)
1220+ free(inherits);
1221+}
1222
1223=== added file 'plugins/Cursor/3rd_party/xcursor/xcursor.h'
1224--- plugins/Cursor/3rd_party/xcursor/xcursor.h 1970-01-01 00:00:00 +0000
1225+++ plugins/Cursor/3rd_party/xcursor/xcursor.h 2015-10-26 09:06:21 +0000
1226@@ -0,0 +1,65 @@
1227+/*
1228+ * Copyright © 2002 Keith Packard
1229+ *
1230+ * Permission to use, copy, modify, distribute, and sell this software and its
1231+ * documentation for any purpose is hereby granted without fee, provided that
1232+ * the above copyright notice appear in all copies and that both that
1233+ * copyright notice and this permission notice appear in supporting
1234+ * documentation, and that the name of Keith Packard not be used in
1235+ * advertising or publicity pertaining to distribution of the software without
1236+ * specific, written prior permission. Keith Packard makes no
1237+ * representations about the suitability of this software for any purpose. It
1238+ * is provided "as is" without express or implied warranty.
1239+ *
1240+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1241+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1242+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1243+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1244+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1245+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1246+ * PERFORMANCE OF THIS SOFTWARE.
1247+ */
1248+
1249+#ifndef XCURSOR_H
1250+#define XCURSOR_H
1251+
1252+#include <stdint.h>
1253+
1254+
1255+typedef int XcursorBool;
1256+typedef uint32_t XcursorUInt;
1257+
1258+typedef XcursorUInt XcursorDim;
1259+typedef XcursorUInt XcursorPixel;
1260+
1261+typedef struct _XcursorImage {
1262+ XcursorUInt version; /* version of the image data */
1263+ XcursorDim size; /* nominal size for matching */
1264+ XcursorDim width; /* actual width */
1265+ XcursorDim height; /* actual height */
1266+ XcursorDim xhot; /* hot spot x (must be inside image) */
1267+ XcursorDim yhot; /* hot spot y (must be inside image) */
1268+ XcursorUInt delay; /* animation delay to next frame (ms) */
1269+ XcursorPixel *pixels; /* pointer to pixels */
1270+} XcursorImage;
1271+
1272+/*
1273+ * Other data structures exposed by the library API
1274+ */
1275+typedef struct _XcursorImages {
1276+ int nimage; /* number of images */
1277+ XcursorImage **images; /* array of XcursorImage pointers */
1278+ char *name; /* name used to load images */
1279+} XcursorImages;
1280+
1281+XcursorImages *
1282+XcursorLibraryLoadImages (const char *file, const char *theme, int size);
1283+
1284+void
1285+XcursorImagesDestroy (XcursorImages *images);
1286+
1287+void
1288+xcursor_load_theme(const char *theme, int size,
1289+ void (*load_callback)(XcursorImages *, void *),
1290+ void *user_data);
1291+#endif
1292
1293=== added file 'plugins/Cursor/CMakeLists.txt'
1294--- plugins/Cursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
1295+++ plugins/Cursor/CMakeLists.txt 2015-10-26 09:06:21 +0000
1296@@ -0,0 +1,28 @@
1297+add_subdirectory(3rd_party)
1298+
1299+include_directories(
1300+ ${CMAKE_CURRENT_SOURCE_DIR}
1301+ ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/xcursor
1302+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
1303+)
1304+
1305+set(QMLPLUGIN_SRC
1306+ plugin.cpp
1307+ MousePointer.cpp
1308+ CursorImageProvider.cpp
1309+ # We need to run moc on this header
1310+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirMousePointerInterface.h
1311+ )
1312+
1313+add_library(Cursor-qml SHARED
1314+ ${QMLPLUGIN_SRC}
1315+ )
1316+
1317+target_link_libraries(Cursor-qml
1318+ xcursorloader-static
1319+ ${QT5PLATFORM_SUPPORT_LDFLAGS}
1320+)
1321+
1322+qt5_use_modules(Cursor-qml Qml Quick DBus Network Gui Sql Concurrent Svg)
1323+
1324+add_unity8_plugin(Cursor 1.0 Cursor TARGETS Cursor-qml)
1325
1326=== added file 'plugins/Cursor/Cursor.qml'
1327--- plugins/Cursor/Cursor.qml 1970-01-01 00:00:00 +0000
1328+++ plugins/Cursor/Cursor.qml 2015-10-26 09:06:21 +0000
1329@@ -0,0 +1,12 @@
1330+import QtQuick 2.4
1331+import Cursor 1.0 // For MousePointer
1332+
1333+MousePointer {
1334+ id: mousePointer
1335+
1336+ Image {
1337+ x: -mousePointer.hotspotX
1338+ y: -mousePointer.hotspotY
1339+ source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
1340+ }
1341+}
1342
1343=== added file 'plugins/Cursor/CursorImageProvider.cpp'
1344--- plugins/Cursor/CursorImageProvider.cpp 1970-01-01 00:00:00 +0000
1345+++ plugins/Cursor/CursorImageProvider.cpp 2015-10-26 09:06:21 +0000
1346@@ -0,0 +1,191 @@
1347+/*
1348+ * Copyright (C) 2015 Canonical, Ltd.
1349+ *
1350+ * This program is free software: you can redistribute it and/or modify it under
1351+ * the terms of the GNU Lesser General Public License version 3, as published by
1352+ * the Free Software Foundation.
1353+ *
1354+ * This program is distributed in the hope that it will be useful, but WITHOUT
1355+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1356+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1357+ * Lesser General Public License for more details.
1358+ *
1359+ * You should have received a copy of the GNU Lesser General Public License
1360+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1361+ */
1362+
1363+#include "CursorImageProvider.h"
1364+
1365+#include <QDebug>
1366+#include <QFile>
1367+#include <QPainter>
1368+#include <QSvgRenderer>
1369+
1370+CursorImageProvider *CursorImageProvider::m_instance = nullptr;
1371+
1372+/////
1373+// BuiltInCursorImage
1374+
1375+BuiltInCursorImage::BuiltInCursorImage()
1376+{
1377+ const char *svgString =
1378+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
1379+ "<svg"
1380+ " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
1381+ " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
1382+ " xmlns:svg=\"http://www.w3.org/2000/svg\""
1383+ " xmlns=\"http://www.w3.org/2000/svg\""
1384+ " version=\"1.1\">"
1385+ " <path"
1386+ " style=\"fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:40;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\""
1387+ " d=\"M 20.504,50.94931 460.42533,518.14486 266.47603,515.61948 366.48114,719.16522 274.05218,770.68296 172.53185,559.56112 20.504,716.13476 Z\" />"
1388+ "</svg>";
1389+
1390+ qimage = QImage(20, 32, QImage::Format_ARGB32);
1391+ QPainter imagePainter(&qimage);
1392+
1393+ QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
1394+ svgRenderer->render(&imagePainter);
1395+ delete svgRenderer;
1396+}
1397+
1398+/////
1399+// XCursorImage
1400+
1401+XCursorImage::XCursorImage(const QString &theme, const QString &file)
1402+ : xcursorImages(nullptr)
1403+{
1404+ xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme), 32);
1405+ if (!xcursorImages) {
1406+ return;
1407+ }
1408+
1409+ bool loaded = false;
1410+ for (int i = 0; i < xcursorImages->nimage && !loaded; ++i) {
1411+ XcursorImage *xcursorImage = xcursorImages->images[i];
1412+ if (xcursorImage->size == 32) {
1413+
1414+ qimage = QImage((uchar*)xcursorImage->pixels,
1415+ xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32);
1416+
1417+ hotspot.setX(xcursorImage->xhot);
1418+ hotspot.setY(xcursorImage->yhot);
1419+
1420+ loaded = true;
1421+ }
1422+ }
1423+}
1424+
1425+XCursorImage::~XCursorImage()
1426+{
1427+ XcursorImagesDestroy(xcursorImages);
1428+}
1429+
1430+/////
1431+// CursorImageProvider
1432+
1433+CursorImageProvider::CursorImageProvider()
1434+ : QQuickImageProvider(QQuickImageProvider::Image)
1435+{
1436+ if (m_instance) {
1437+ qFatal("Cannot have multiple CursorImageProvider instances");
1438+ }
1439+ m_instance = this;
1440+}
1441+
1442+CursorImageProvider::~CursorImageProvider()
1443+{
1444+ {
1445+ QList< QMap<QString, CursorImage*> > cursorList = m_cursors.values();
1446+
1447+ for (int i = 0; i < cursorList.count(); ++i) {
1448+ QList<CursorImage*> cursorImageList = cursorList[i].values();
1449+ for (int j = 0; j < cursorImageList.count(); ++j) {
1450+ delete cursorImageList[j];
1451+ }
1452+ }
1453+ }
1454+
1455+ m_cursors.clear();
1456+ m_instance = nullptr;
1457+}
1458+
1459+QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSize *size, const QSize & /*requestedSize*/)
1460+{
1461+ CursorImage *cursorImage = fetchCursor(cursorThemeAndName);
1462+ size->setWidth(cursorImage->qimage.width());
1463+ size->setHeight(cursorImage->qimage.height());
1464+
1465+ return cursorImage->qimage;
1466+}
1467+
1468+QPoint CursorImageProvider::hotspot(const QString &themeName, const QString &cursorName)
1469+{
1470+ CursorImage *cursorImage = fetchCursor(themeName, cursorName);
1471+ if (cursorImage) {
1472+ return cursorImage->hotspot;
1473+ } else {
1474+ return QPoint(0,0);
1475+ }
1476+}
1477+
1478+CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
1479+{
1480+ QString themeName;
1481+ QString cursorName;
1482+ {
1483+ QStringList themeAndNameList = cursorThemeAndName.split("/");
1484+ if (themeAndNameList.size() != 2) {
1485+ return nullptr;
1486+ }
1487+ themeName = themeAndNameList[0];
1488+ cursorName = themeAndNameList[1];
1489+ }
1490+
1491+ return fetchCursor(themeName, cursorName);
1492+}
1493+
1494+CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName)
1495+{
1496+ CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName);
1497+
1498+ // Try some fallbacks
1499+ if (cursorImage->qimage.isNull()) {
1500+ if (cursorName == "ibeam") {
1501+ qDebug() << "CursorImageProvider: \"ibeam\" not found, falling back to \"xterm\"";
1502+ cursorImage = fetchCursorHelper(themeName, "xterm");
1503+ } else if (cursorName == "xterm") {
1504+ qDebug() << "CursorImageProvider: \"xterm\" not found, falling back to \"ibeam\"";
1505+ cursorImage = fetchCursorHelper(themeName, "ibeam");
1506+ }
1507+ }
1508+
1509+ // if it all fails, there must be at least a left_ptr
1510+ if (cursorImage->qimage.isNull() && cursorName != "left_ptr") {
1511+ qDebug() << "CursorImageProvider:" << cursorName
1512+ << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
1513+ cursorImage = fetchCursorHelper(themeName, "left_ptr");
1514+ }
1515+
1516+ if (cursorImage->qimage.isNull()) {
1517+ // finally, go for the built-in cursor
1518+ qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
1519+ if (!m_builtInCursorImage) {
1520+ m_builtInCursorImage.reset(new BuiltInCursorImage);
1521+ }
1522+ cursorImage = m_builtInCursorImage.data();
1523+ }
1524+
1525+ return cursorImage;
1526+}
1527+
1528+CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName)
1529+{
1530+ QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
1531+
1532+ if (!themeCursors.contains(cursorName)) {
1533+ themeCursors[cursorName] = new XCursorImage(themeName, cursorName);
1534+ }
1535+
1536+ return themeCursors[cursorName];
1537+}
1538
1539=== added file 'plugins/Cursor/CursorImageProvider.h'
1540--- plugins/Cursor/CursorImageProvider.h 1970-01-01 00:00:00 +0000
1541+++ plugins/Cursor/CursorImageProvider.h 2015-10-26 09:06:21 +0000
1542@@ -0,0 +1,76 @@
1543+/*
1544+ * Copyright (C) 2015 Canonical, Ltd.
1545+ *
1546+ * This program is free software: you can redistribute it and/or modify it under
1547+ * the terms of the GNU Lesser General Public License version 3, as published by
1548+ * the Free Software Foundation.
1549+ *
1550+ * This program is distributed in the hope that it will be useful, but WITHOUT
1551+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1552+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553+ * Lesser General Public License for more details.
1554+ *
1555+ * You should have received a copy of the GNU Lesser General Public License
1556+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1557+ */
1558+
1559+#ifndef CURSORIMAGEPROVIDER_H
1560+#define CURSORIMAGEPROVIDER_H
1561+
1562+#include <QQuickImageProvider>
1563+#include <QScopedPointer>
1564+
1565+// xcursor static lib
1566+extern "C"
1567+{
1568+#include <xcursor.h>
1569+}
1570+
1571+class CursorImage {
1572+public:
1573+ virtual ~CursorImage() {}
1574+
1575+ QImage qimage;
1576+ QPoint hotspot;
1577+};
1578+
1579+class XCursorImage : public CursorImage {
1580+public:
1581+ XCursorImage(const QString &theme, const QString &file);
1582+ virtual ~XCursorImage();
1583+
1584+ XcursorImages *xcursorImages;
1585+};
1586+
1587+class BuiltInCursorImage : public CursorImage {
1588+public:
1589+ BuiltInCursorImage();
1590+};
1591+
1592+class CursorImageProvider : public QQuickImageProvider
1593+{
1594+public:
1595+ CursorImageProvider();
1596+ virtual ~CursorImageProvider();
1597+
1598+ static CursorImageProvider *instance() { return m_instance; }
1599+
1600+
1601+ QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override;
1602+
1603+ QPoint hotspot(const QString &themeName, const QString &cursorName);
1604+
1605+private:
1606+ CursorImage *fetchCursor(const QString &cursorThemeAndName);
1607+ CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
1608+ CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName);
1609+
1610+ // themeName -> (cursorName -> cursorImage)
1611+ QMap<QString, QMap<QString, CursorImage*> > m_cursors;
1612+
1613+ QScopedPointer<CursorImage> m_builtInCursorImage;
1614+
1615+ static CursorImageProvider *m_instance;
1616+};
1617+
1618+#endif // CURSORIMAGEPROVIDER_H
1619
1620=== added file 'plugins/Cursor/MousePointer.cpp'
1621--- plugins/Cursor/MousePointer.cpp 1970-01-01 00:00:00 +0000
1622+++ plugins/Cursor/MousePointer.cpp 2015-10-26 09:06:21 +0000
1623@@ -0,0 +1,125 @@
1624+/*
1625+ * Copyright (C) 2015 Canonical, Ltd.
1626+ *
1627+ * This program is free software: you can redistribute it and/or modify it under
1628+ * the terms of the GNU Lesser General Public License version 3, as published by
1629+ * the Free Software Foundation.
1630+ *
1631+ * This program is distributed in the hope that it will be useful, but WITHOUT
1632+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1633+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1634+ * Lesser General Public License for more details.
1635+ *
1636+ * You should have received a copy of the GNU Lesser General Public License
1637+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1638+ */
1639+
1640+#include "MousePointer.h"
1641+#include "CursorImageProvider.h"
1642+
1643+// Unity API
1644+#include <unity/shell/application/MirPlatformCursor.h>
1645+
1646+#include <QQuickWindow>
1647+#include <QGuiApplication>
1648+
1649+#include <qpa/qwindowsysteminterface.h>
1650+
1651+MousePointer::MousePointer(QQuickItem *parent)
1652+ : MirMousePointerInterface(parent)
1653+ , m_cursorName("left_ptr")
1654+ , m_themeName("default")
1655+ , m_hotspotX(0)
1656+ , m_hotspotY(0)
1657+{
1658+}
1659+
1660+void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
1661+ Qt::KeyboardModifiers modifiers)
1662+{
1663+ if (!parentItem()) {
1664+ return;
1665+ }
1666+
1667+ qreal newX = x() + movement.x();
1668+ if (newX < 0) {
1669+ newX = 0;
1670+ } else if (newX > parentItem()->width()) {
1671+ newX = parentItem()->width();
1672+ }
1673+ setX(newX);
1674+
1675+ qreal newY = y() + movement.y();
1676+ if (newY < 0) {
1677+ newY = 0;
1678+ } else if (newY > parentItem()->height()) {
1679+ newY = parentItem()->height();
1680+ }
1681+ setY(newY);
1682+
1683+ QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
1684+ QWindowSystemInterface::handleMouseEvent(window(), timestamp, scenePosition /*local*/, scenePosition /*global*/,
1685+ buttons, modifiers);
1686+}
1687+
1688+void MousePointer::itemChange(ItemChange change, const ItemChangeData &value)
1689+{
1690+ if (change == ItemSceneChange) {
1691+ registerWindow(value.window);
1692+ }
1693+}
1694+
1695+void MousePointer::registerWindow(QWindow *window)
1696+{
1697+ if (m_registeredWindow && window != m_registeredWindow) {
1698+ auto previousCursor = dynamic_cast<MirPlatformCursor*>(m_registeredWindow->screen()->handle()->cursor());
1699+ if (previousCursor) {
1700+ previousCursor->setMousePointer(nullptr);
1701+ } else {
1702+ qCritical("QPlatformCursor is not a MirPlatformCursor! Cursor module only works in a Mir server.");
1703+ }
1704+ }
1705+
1706+ m_registeredWindow = window;
1707+
1708+ if (m_registeredWindow) {
1709+ auto cursor = dynamic_cast<MirPlatformCursor*>(window->screen()->handle()->cursor());
1710+ if (cursor) {
1711+ cursor->setMousePointer(this);
1712+ } else {
1713+ qCritical("QPlaformCursor is not a MirPlatformCursor! Cursor module only works in Mir.");
1714+ }
1715+ }
1716+}
1717+
1718+void MousePointer::setCursorName(const QString &cursorName)
1719+{
1720+ if (cursorName != m_cursorName) {
1721+ m_cursorName = cursorName;
1722+ Q_EMIT cursorNameChanged(m_cursorName);
1723+ updateHotspot();
1724+ }
1725+}
1726+
1727+void MousePointer::updateHotspot()
1728+{
1729+ QPoint newHotspot = CursorImageProvider::instance()->hotspot(m_themeName, m_cursorName);
1730+
1731+ if (m_hotspotX != newHotspot.x()) {
1732+ m_hotspotX = newHotspot.x();
1733+ Q_EMIT hotspotXChanged(m_hotspotX);
1734+ }
1735+
1736+ if (m_hotspotY != newHotspot.y()) {
1737+ m_hotspotY = newHotspot.y();
1738+ Q_EMIT hotspotYChanged(m_hotspotY);
1739+ }
1740+}
1741+
1742+void MousePointer::setThemeName(const QString &themeName)
1743+{
1744+ if (m_themeName != themeName) {
1745+ m_themeName = themeName;
1746+ Q_EMIT themeNameChanged(m_themeName);
1747+ }
1748+}
1749
1750=== added file 'plugins/Cursor/MousePointer.h'
1751--- plugins/Cursor/MousePointer.h 1970-01-01 00:00:00 +0000
1752+++ plugins/Cursor/MousePointer.h 2015-10-26 09:06:21 +0000
1753@@ -0,0 +1,59 @@
1754+/*
1755+ * Copyright (C) 2015 Canonical, Ltd.
1756+ *
1757+ * This program is free software: you can redistribute it and/or modify it under
1758+ * the terms of the GNU Lesser General Public License version 3, as published by
1759+ * the Free Software Foundation.
1760+ *
1761+ * This program is distributed in the hope that it will be useful, but WITHOUT
1762+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1763+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1764+ * Lesser General Public License for more details.
1765+ *
1766+ * You should have received a copy of the GNU Lesser General Public License
1767+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1768+ */
1769+
1770+#ifndef MOUSEPOINTER_H
1771+#define MOUSEPOINTER_H
1772+
1773+// Qt
1774+#include <QPointer>
1775+#include <QWindow>
1776+
1777+// Unity API
1778+#include <unity/shell/application/MirMousePointerInterface.h>
1779+
1780+class MousePointer : public MirMousePointerInterface {
1781+ Q_OBJECT
1782+public:
1783+ MousePointer(QQuickItem *parent = nullptr);
1784+
1785+ void setCursorName(const QString &qtCursorName) override;
1786+ QString cursorName() const override { return m_cursorName; }
1787+
1788+ void setThemeName(const QString &themeName) override;
1789+ QString themeName() const override { return m_themeName; }
1790+
1791+ qreal hotspotX() const override { return m_hotspotX; }
1792+ qreal hotspotY() const override { return m_hotspotY; }
1793+
1794+public Q_SLOTS:
1795+ void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
1796+ Qt::KeyboardModifiers modifiers) override;
1797+
1798+protected:
1799+ void itemChange(ItemChange change, const ItemChangeData &value) override;
1800+
1801+private:
1802+ void registerWindow(QWindow *window);
1803+ void updateHotspot();
1804+
1805+ QPointer<QWindow> m_registeredWindow;
1806+ QString m_cursorName;
1807+ QString m_themeName;
1808+ int m_hotspotX;
1809+ int m_hotspotY;
1810+};
1811+
1812+#endif // MOUSEPOINTER_H
1813
1814=== added file 'plugins/Cursor/plugin.cpp'
1815--- plugins/Cursor/plugin.cpp 1970-01-01 00:00:00 +0000
1816+++ plugins/Cursor/plugin.cpp 2015-10-26 09:06:21 +0000
1817@@ -0,0 +1,39 @@
1818+/*
1819+ * Copyright (C) 2015 Canonical, Ltd.
1820+ *
1821+ * This program is free software; you can redistribute it and/or modify
1822+ * it under the terms of the GNU General Public License as published by
1823+ * the Free Software Foundation; version 3.
1824+ *
1825+ * This program is distributed in the hope that it will be useful,
1826+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1827+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1828+ * GNU General Public License for more details.
1829+ *
1830+ * You should have received a copy of the GNU General Public License
1831+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1832+ */
1833+
1834+// Qt
1835+#include <QtQml/qqml.h>
1836+#include <QQmlContext>
1837+
1838+// self
1839+#include "plugin.h"
1840+
1841+// local
1842+#include "CursorImageProvider.h"
1843+#include "MousePointer.h"
1844+
1845+void CursorPlugin::registerTypes(const char *uri)
1846+{
1847+ Q_ASSERT(uri == QLatin1String("Cursor"));
1848+ qmlRegisterType<MousePointer>(uri, 1, 0, "MousePointer");
1849+}
1850+
1851+void CursorPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
1852+{
1853+ QQmlExtensionPlugin::initializeEngine(engine, uri);
1854+
1855+ engine->addImageProvider(QLatin1String("cursor"), new CursorImageProvider());
1856+}
1857
1858=== added file 'plugins/Cursor/plugin.h'
1859--- plugins/Cursor/plugin.h 1970-01-01 00:00:00 +0000
1860+++ plugins/Cursor/plugin.h 2015-10-26 09:06:21 +0000
1861@@ -0,0 +1,33 @@
1862+/*
1863+ * Copyright (C) 2015 Canonical, Ltd.
1864+ *
1865+ * This program is free software; you can redistribute it and/or modify
1866+ * it under the terms of the GNU General Public License as published by
1867+ * the Free Software Foundation; version 3.
1868+ *
1869+ * This program is distributed in the hope that it will be useful,
1870+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1871+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1872+ * GNU General Public License for more details.
1873+ *
1874+ * You should have received a copy of the GNU General Public License
1875+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1876+ */
1877+
1878+#ifndef CURSOR_PLUGIN_H
1879+#define CURSOR_PLUGIN_H
1880+
1881+#include <QtQml/QQmlEngine>
1882+#include <QtQml/QQmlExtensionPlugin>
1883+
1884+class CursorPlugin : public QQmlExtensionPlugin
1885+{
1886+ Q_OBJECT
1887+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
1888+
1889+public:
1890+ void registerTypes(const char *uri) override;
1891+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
1892+};
1893+
1894+#endif // CURSOR_PLUGIN_H
1895
1896=== added file 'plugins/Cursor/qmldir'
1897--- plugins/Cursor/qmldir 1970-01-01 00:00:00 +0000
1898+++ plugins/Cursor/qmldir 2015-10-26 09:06:21 +0000
1899@@ -0,0 +1,3 @@
1900+module Cursor
1901+plugin Cursor-qml
1902+Cursor 1.0 Cursor.qml
1903
1904=== modified file 'plugins/IntegratedLightDM/UsersModel.cpp'
1905--- plugins/IntegratedLightDM/UsersModel.cpp 2015-04-30 09:31:51 +0000
1906+++ plugins/IntegratedLightDM/UsersModel.cpp 2015-10-26 09:06:21 +0000
1907@@ -43,16 +43,17 @@
1908
1909 QVariant MangleModel::data(const QModelIndex &index, int role) const
1910 {
1911- QVariant data = QSortFilterProxyModel::data(index, role);
1912+ QVariant variantData = QSortFilterProxyModel::data(index, role);
1913
1914 // If user's real name is empty, switch to unix name
1915- if (role == QLightDM::UsersModel::RealNameRole && data.toString().isEmpty()) {
1916- data = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
1917- } else if (role == QLightDM::UsersModel::BackgroundPathRole && data.toString().startsWith('#')) {
1918- data = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + data.toString() + "'/></svg>";
1919+ if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
1920+ variantData = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
1921+ } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
1922+ const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
1923+ variantData = stringData;
1924 }
1925
1926- return data;
1927+ return variantData;
1928 }
1929
1930 // **** Now we continue with actual UsersModel class ****
1931
1932=== modified file 'plugins/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp'
1933--- plugins/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp 2015-09-14 09:11:08 +0000
1934+++ plugins/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp 2015-10-26 09:06:21 +0000
1935@@ -34,7 +34,8 @@
1936 entries.reserve(users.count());
1937 Q_FOREACH(const QString &user, users)
1938 {
1939- QString name = settings.value(user + "/name", user[0].toUpper() + user.mid(1)).toString();
1940+ QVariant defaultValue = QString(user[0].toUpper() + user.mid(1));
1941+ QString name = settings.value(user + "/name", defaultValue).toString();
1942 entries.append({user, name, 0, 0, false, false, 0, 0});
1943 }
1944 }
1945
1946=== modified file 'plugins/ScreenGrabber/screengrabber.cpp'
1947--- plugins/ScreenGrabber/screengrabber.cpp 2015-09-23 15:14:01 +0000
1948+++ plugins/ScreenGrabber/screengrabber.cpp 2015-10-26 09:06:21 +0000
1949@@ -60,7 +60,7 @@
1950 }
1951 }
1952
1953-void ScreenGrabber::captureAndSave()
1954+void ScreenGrabber::captureAndSave(int angle)
1955 {
1956 if (fileNamePrefix.isEmpty())
1957 {
1958@@ -82,7 +82,7 @@
1959 return;
1960 }
1961
1962- const QImage screenshot = main_window->grabWindow();
1963+ const QImage screenshot = main_window->grabWindow().transformed(QTransform().rotate(angle));
1964 const QString filename = makeFileName();
1965 qDebug() << "Saving screenshot to" << filename;
1966 auto saveOp = QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality);
1967
1968=== modified file 'plugins/ScreenGrabber/screengrabber.h'
1969--- plugins/ScreenGrabber/screengrabber.h 2015-07-17 19:59:31 +0000
1970+++ plugins/ScreenGrabber/screengrabber.h 2015-10-26 09:06:21 +0000
1971@@ -29,7 +29,7 @@
1972 ~ScreenGrabber() = default;
1973
1974 public Q_SLOTS:
1975- void captureAndSave();
1976+ void captureAndSave(int angle = 0);
1977
1978 Q_SIGNALS:
1979 void screenshotSaved(const QString &filename);
1980
1981=== modified file 'plugins/Unity/CMakeLists.txt'
1982--- plugins/Unity/CMakeLists.txt 2015-05-05 11:19:15 +0000
1983+++ plugins/Unity/CMakeLists.txt 2015-10-26 09:06:21 +0000
1984@@ -4,3 +4,4 @@
1985 add_subdirectory(Session)
1986 add_subdirectory(DashCommunicator)
1987 add_subdirectory(InputInfo)
1988+add_subdirectory(Platform)
1989
1990=== modified file 'plugins/Unity/Launcher/desktopfilehandler.cpp'
1991--- plugins/Unity/Launcher/desktopfilehandler.cpp 2015-09-14 09:41:55 +0000
1992+++ plugins/Unity/Launcher/desktopfilehandler.cpp 2015-10-26 09:06:21 +0000
1993@@ -111,8 +111,9 @@
1994 settings.beginGroup(QStringLiteral("Desktop Entry"));
1995
1996 // First try to find Name[xx_YY] and Name[xx] in .desktop file
1997- QString locale = QLocale().name();
1998- QString shortLocale = locale.split('_').first();
1999+ const QString locale = QLocale().name();
2000+ const QStringList splitLocale = locale.split(QLatin1Char('_'));
2001+ const QString shortLocale = splitLocale.first();
2002
2003 if (locale != shortLocale && settings.contains(QStringLiteral("Name[%1]").arg(locale))) {
2004 return settings.value(QStringLiteral("Name[%1]").arg(locale)).toString();
2005
2006=== added directory 'plugins/Unity/Platform'
2007=== added file 'plugins/Unity/Platform/CMakeLists.txt'
2008--- plugins/Unity/Platform/CMakeLists.txt 1970-01-01 00:00:00 +0000
2009+++ plugins/Unity/Platform/CMakeLists.txt 2015-10-26 09:06:21 +0000
2010@@ -0,0 +1,9 @@
2011+set(platformplugin_SRCS
2012+ platform.cpp
2013+ plugin.cpp)
2014+
2015+add_library(Platform-qml SHARED ${platformplugin_SRCS})
2016+
2017+qt5_use_modules(Platform-qml DBus Qml)
2018+
2019+add_unity8_plugin(Unity.Platform 1.0 Unity/Platform TARGETS Platform-qml)
2020
2021=== added file 'plugins/Unity/Platform/platform.cpp'
2022--- plugins/Unity/Platform/platform.cpp 1970-01-01 00:00:00 +0000
2023+++ plugins/Unity/Platform/platform.cpp 2015-10-26 09:06:21 +0000
2024@@ -0,0 +1,43 @@
2025+/*
2026+ * Copyright (C) 2015 Canonical, Ltd.
2027+ *
2028+ * This program is free software; you can redistribute it and/or modify
2029+ * it under the terms of the GNU General Public License as published by
2030+ * the Free Software Foundation; version 3.
2031+ *
2032+ * This program is distributed in the hope that it will be useful,
2033+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2034+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2035+ * GNU General Public License for more details.
2036+ *
2037+ * You should have received a copy of the GNU General Public License
2038+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2039+ */
2040+
2041+#include "platform.h"
2042+
2043+#include <QDBusConnection>
2044+
2045+Platform::Platform(QObject *parent)
2046+ : QObject(parent)
2047+ , m_iface("org.freedesktop.hostname1", "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
2048+ QDBusConnection::systemBus(), this)
2049+{
2050+ QMetaObject::invokeMethod(this, "init");
2051+}
2052+
2053+void Platform::init()
2054+{
2055+ m_chassis = m_iface.property("Chassis").toString();
2056+ m_isPC = (m_chassis == "desktop" || m_chassis == "laptop" || m_chassis == "server");
2057+}
2058+
2059+QString Platform::chassis() const
2060+{
2061+ return m_chassis;
2062+}
2063+
2064+bool Platform::isPC() const
2065+{
2066+ return m_isPC;
2067+}
2068
2069=== added file 'plugins/Unity/Platform/platform.h'
2070--- plugins/Unity/Platform/platform.h 1970-01-01 00:00:00 +0000
2071+++ plugins/Unity/Platform/platform.h 2015-10-26 09:06:21 +0000
2072@@ -0,0 +1,58 @@
2073+/*
2074+ * Copyright (C) 2015 Canonical, Ltd.
2075+ *
2076+ * This program is free software; you can redistribute it and/or modify
2077+ * it under the terms of the GNU General Public License as published by
2078+ * the Free Software Foundation; version 3.
2079+ *
2080+ * This program is distributed in the hope that it will be useful,
2081+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2082+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2083+ * GNU General Public License for more details.
2084+ *
2085+ * You should have received a copy of the GNU General Public License
2086+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2087+ */
2088+
2089+#ifndef PLATFORM_H
2090+#define PLATFORM_H
2091+
2092+#include <QDBusInterface>
2093+
2094+/**
2095+ * @brief The Platform class
2096+ *
2097+ * Wrapper around platform detection support (org.freedesktop.hostname1)
2098+ */
2099+class Platform: public QObject
2100+{
2101+ Q_OBJECT
2102+ /**
2103+ * The chassis property
2104+ *
2105+ * Supported values include: "laptop", "computer", "handset" or "tablet"
2106+ * For full list see: http://www.freedesktop.org/wiki/Software/systemd/hostnamed/
2107+ */
2108+ Q_PROPERTY(QString chassis READ chassis CONSTANT)
2109+ /**
2110+ * Whether the machine is an ordinary PC (desktop, laptop or server)
2111+ */
2112+ Q_PROPERTY(bool isPC READ isPC CONSTANT)
2113+
2114+public:
2115+ Platform(QObject *parent = nullptr);
2116+ ~Platform() = default;
2117+
2118+ QString chassis() const;
2119+ bool isPC() const;
2120+
2121+private Q_SLOTS:
2122+ void init();
2123+
2124+private:
2125+ QDBusInterface m_iface;
2126+ QString m_chassis;
2127+ bool m_isPC;
2128+};
2129+
2130+#endif // PLATFORM_H
2131
2132=== added file 'plugins/Unity/Platform/plugin.cpp'
2133--- plugins/Unity/Platform/plugin.cpp 1970-01-01 00:00:00 +0000
2134+++ plugins/Unity/Platform/plugin.cpp 2015-10-26 09:06:21 +0000
2135@@ -0,0 +1,27 @@
2136+/*
2137+ * Copyright (C) 2015 Canonical, Ltd.
2138+ *
2139+ * This program is free software; you can redistribute it and/or modify
2140+ * it under the terms of the GNU General Public License as published by
2141+ * the Free Software Foundation; version 3.
2142+ *
2143+ * This program is distributed in the hope that it will be useful,
2144+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2145+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2146+ * GNU General Public License for more details.
2147+ *
2148+ * You should have received a copy of the GNU General Public License
2149+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2150+ */
2151+
2152+#include "plugin.h"
2153+#include "platform.h"
2154+
2155+#include <QtQml>
2156+
2157+void GlobalShortcutPlugin::registerTypes(const char *uri)
2158+{
2159+ Q_ASSERT(uri == QLatin1String("Unity.Platform"));
2160+
2161+ qmlRegisterSingletonType<Platform>(uri, 1, 0, "Platform", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Platform; });
2162+}
2163
2164=== added file 'plugins/Unity/Platform/plugin.h'
2165--- plugins/Unity/Platform/plugin.h 1970-01-01 00:00:00 +0000
2166+++ plugins/Unity/Platform/plugin.h 2015-10-26 09:06:21 +0000
2167@@ -0,0 +1,32 @@
2168+/*
2169+ * Copyright (C) 2015 Canonical, Ltd.
2170+ *
2171+ * This program is free software; you can redistribute it and/or modify
2172+ * it under the terms of the GNU General Public License as published by
2173+ * the Free Software Foundation; version 3.
2174+ *
2175+ * This program is distributed in the hope that it will be useful,
2176+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2177+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2178+ * GNU General Public License for more details.
2179+ *
2180+ * You should have received a copy of the GNU General Public License
2181+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2182+ */
2183+
2184+#ifndef PLATFORMPLUGIN_H
2185+#define PLATFORMPLUGIN_H
2186+
2187+#include <QQmlExtensionPlugin>
2188+
2189+class GlobalShortcutPlugin: public QQmlExtensionPlugin
2190+{
2191+ Q_OBJECT
2192+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
2193+
2194+public:
2195+ void registerTypes(const char *uri) override;
2196+};
2197+
2198+
2199+#endif // PLATFORMPLUGIN_H
2200
2201=== added file 'plugins/Unity/Platform/qmldir'
2202--- plugins/Unity/Platform/qmldir 1970-01-01 00:00:00 +0000
2203+++ plugins/Unity/Platform/qmldir 2015-10-26 09:06:21 +0000
2204@@ -0,0 +1,2 @@
2205+module Unity.Platform
2206+plugin Platform-qml
2207
2208=== modified file 'plugins/Unity/Session/dbusunitysessionservice.cpp'
2209--- plugins/Unity/Session/dbusunitysessionservice.cpp 2015-09-14 09:11:08 +0000
2210+++ plugins/Unity/Session/dbusunitysessionservice.cpp 2015-10-26 09:06:21 +0000
2211@@ -292,7 +292,8 @@
2212 if (p) {
2213 const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
2214 if (!gecos.isEmpty()) {
2215- return gecos.split(QLatin1Char(',')).first();
2216+ const QStringList splitGecos = gecos.split(QLatin1Char(','));
2217+ return splitGecos.first();
2218 }
2219 }
2220
2221
2222=== modified file 'plugins/Utils/CMakeLists.txt'
2223--- plugins/Utils/CMakeLists.txt 2015-09-25 12:13:13 +0000
2224+++ plugins/Utils/CMakeLists.txt 2015-10-26 09:06:21 +0000
2225@@ -15,8 +15,6 @@
2226 inputwatcher.cpp
2227 qlimitproxymodelqml.cpp
2228 unitysortfilterproxymodelqml.cpp
2229- relativetimeformatter.cpp
2230- timeformatter.cpp
2231 Timer.cpp
2232 unitymenumodelpaths.cpp
2233 windowkeysfilter.cpp
2234
2235=== modified file 'plugins/Utils/plugin.cpp'
2236--- plugins/Utils/plugin.cpp 2015-09-25 12:13:13 +0000
2237+++ plugins/Utils/plugin.cpp 2015-10-26 09:06:21 +0000
2238@@ -29,8 +29,6 @@
2239 #include "inputwatcher.h"
2240 #include "qlimitproxymodelqml.h"
2241 #include "unitysortfilterproxymodelqml.h"
2242-#include "relativetimeformatter.h"
2243-#include "timeformatter.h"
2244 #include "unitymenumodelpaths.h"
2245 #include "windowkeysfilter.h"
2246 #include "windowscreenshotprovider.h"
2247@@ -60,11 +58,8 @@
2248 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
2249 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
2250 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
2251- qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
2252 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
2253- qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
2254 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
2255- qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
2256 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
2257 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
2258 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
2259
2260=== removed file 'plugins/Utils/relativetimeformatter.cpp'
2261--- plugins/Utils/relativetimeformatter.cpp 2015-09-21 13:17:30 +0000
2262+++ plugins/Utils/relativetimeformatter.cpp 1970-01-01 00:00:00 +0000
2263@@ -1,260 +0,0 @@
2264-/*
2265- * Copyright 2014 Canonical Ltd.
2266- *
2267- * This program is free software; you can redistribute it and/or modify
2268- * it under the terms of the GNU General Public License as published by
2269- * the Free Software Foundation; version 3.
2270- *
2271- * This program is distributed in the hope that it will be useful,
2272- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2273- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2274- * GNU General Public License for more details.
2275- *
2276- * You should have received a copy of the GNU General Public License
2277- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2278- *
2279- */
2280-
2281-// Local
2282-#include "relativetimeformatter.h"
2283-
2284-// Qt
2285-#include <QDateTime>
2286-
2287-// Other
2288-#include <glib.h>
2289-#include <glib/gi18n.h>
2290-#include <locale.h>
2291-#include <langinfo.h>
2292-#include <string.h>
2293-
2294-RelativeTimeFormatter::RelativeTimeFormatter(QObject *parent)
2295- : GDateTimeFormatter(parent)
2296-{
2297-}
2298-
2299- /* Check the system locale setting to see if the format is 24-hour
2300- time or 12-hour time */
2301-gboolean
2302-is_locale_12h(void)
2303-{
2304- int i;
2305- static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", nullptr};
2306- const char* t_fmt = nl_langinfo(T_FMT);
2307-
2308- for (i=0; formats_24h[i]!=nullptr; i++)
2309- if (strstr(t_fmt, formats_24h[i]) != nullptr)
2310- return FALSE;
2311-
2312- return TRUE;
2313-}
2314-
2315-typedef enum
2316-{
2317- DATE_PROXIMITY_YESTERDAY,
2318- DATE_PROXIMITY_TODAY,
2319- DATE_PROXIMITY_TOMORROW,
2320- DATE_PROXIMITY_LAST_WEEK,
2321- DATE_PROXIMITY_NEXT_WEEK,
2322- DATE_PROXIMITY_FAR
2323-} date_proximity_t;
2324-
2325-static date_proximity_t
2326-getDateProximity(GDateTime* now, GDateTime* time)
2327-{
2328- date_proximity_t prox = DATE_PROXIMITY_FAR;
2329- gint now_year, now_month, now_day;
2330- gint time_year, time_month, time_day;
2331-
2332- // does it happen today?
2333- g_date_time_get_ymd(now, &now_year, &now_month, &now_day);
2334- g_date_time_get_ymd(time, &time_year, &time_month, &time_day);
2335- if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) {
2336- return DATE_PROXIMITY_TODAY;
2337- }
2338-
2339- // did it happen yesterday?
2340- GDateTime* yesterday = g_date_time_add_days(now, -1);
2341- gint tom_year, tom_month, tom_day;
2342- g_date_time_get_ymd(yesterday, &tom_year, &tom_month, &tom_day);
2343- g_date_time_unref(yesterday);
2344- if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) {
2345- return DATE_PROXIMITY_YESTERDAY;
2346- }
2347-
2348- // does it happen tomorrow?
2349- GDateTime* tomorrow = g_date_time_add_days(now, 1);
2350- g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day);
2351- g_date_time_unref(tomorrow);
2352- if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) {
2353- return DATE_PROXIMITY_TOMORROW;
2354- }
2355-
2356- // does it happen this week?
2357- if (g_date_time_compare(time, now) < 0) {
2358- GDateTime* last_week = g_date_time_add_days(now, -6);
2359- GDateTime* last_week_bound = g_date_time_new_local(g_date_time_get_year(last_week),
2360- g_date_time_get_month(last_week),
2361- g_date_time_get_day_of_month(last_week),
2362- 0, 0, 0);
2363- if (g_date_time_compare(time, last_week_bound) >= 0)
2364- prox = DATE_PROXIMITY_LAST_WEEK;
2365-
2366- g_date_time_unref(last_week);
2367- g_date_time_unref(last_week_bound);
2368- } else {
2369- GDateTime* next_week = g_date_time_add_days(now, 6);
2370- GDateTime* next_week_bound = g_date_time_new_local(g_date_time_get_year(next_week),
2371- g_date_time_get_month(next_week),
2372- g_date_time_get_day_of_month(next_week),
2373- 23, 59, 59.9);
2374- if (g_date_time_compare(time, next_week_bound) <= 0)
2375- prox = DATE_PROXIMITY_NEXT_WEEK;
2376-
2377- g_date_time_unref(next_week);
2378- g_date_time_unref(next_week_bound);
2379- }
2380-
2381- return prox;
2382-}
2383-
2384-const char*
2385-dgettext_datetime(const char *text)
2386-{
2387- return dgettext("unity8", text);
2388-}
2389-
2390-/**
2391- * _ a time yesterday should be shown as (e.g. “Yesterday 3:55 PM”)
2392- * _ a time today should be shown as just the time (e.g. “3:55 PM”)
2393- * _ a time tomorrow should be shown as(e.g. “Tomorrow 3:55 PM”)
2394- * _ a time any other day this week should be shown as the short version of the
2395- * day and time (e.g. “Wed 3:55 PM”)
2396- * weekday (e.g. “Friday”)
2397- * _ a time after this week should be shown as the short version of the day,
2398- * date, and time (e.g. “Wed 21 Apr 3:55 PM”)
2399- * _ in addition, when presenting the times of upcoming events, the time should
2400- * be followed by the timezone if it is different from the one the computer
2401- * is currently set to. For example, “Wed 3:55 PM UTC−5”.
2402- *
2403- * TODO - keep inline with indicator-datetime
2404- */
2405-char* generate_full_format_string_at_time (GDateTime* now,
2406- GDateTime* then)
2407-{
2408- GString* ret = g_string_new (nullptr);
2409-
2410- if (then != nullptr) {
2411- const date_proximity_t prox = getDateProximity(now, then);
2412-
2413- if (is_locale_12h()) {
2414- switch (prox) {
2415- case DATE_PROXIMITY_YESTERDAY:
2416- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2417- This format string is used for showing, on a 12-hour clock, times that happen yesterday.
2418- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2419- en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM" */
2420- g_string_assign (ret, dgettext_datetime("Yesterday\u2003%l:%M %p"));
2421- break;
2422-
2423- case DATE_PROXIMITY_TODAY:
2424- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2425- This format string is used for showing, on a 12-hour clock, times that happened today.
2426- en_US example: "%l:%M %p" --> "1:00 PM" */
2427- g_string_assign (ret, dgettext_datetime("%l:%M %p"));
2428- break;
2429-
2430- case DATE_PROXIMITY_TOMORROW:
2431- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2432- This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
2433- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2434- en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM" */
2435- g_string_assign (ret, dgettext_datetime("Tomorrow\u2003%l:%M %p"));
2436- break;
2437-
2438- case DATE_PROXIMITY_LAST_WEEK:
2439- case DATE_PROXIMITY_NEXT_WEEK:
2440- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2441- This format string is used for showing, on a 12-hour clock, times that happened in the last week.
2442- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2443- en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM" */
2444- g_string_assign (ret, dgettext_datetime("%a\u2003%l:%M %p"));
2445- break;
2446-
2447- case DATE_PROXIMITY_FAR:
2448- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2449- This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
2450- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2451- en_US example: "%a %b %d\u2003%l:%M %p" --> "Fri Oct 31 1:00 PM"
2452- en_GB example: "%a %d %b\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM" */
2453- g_string_assign (ret, dgettext_datetime("%a %d %b\u2003%l:%M %p"));
2454- break;
2455- }
2456- } else {
2457- switch (prox) {
2458-
2459- case DATE_PROXIMITY_YESTERDAY:
2460- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2461- This format string is used for showing, on a 24-hour clock, times that happen yesterday.
2462- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2463- en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00" */
2464- g_string_assign (ret, dgettext_datetime("Yesterday\u2003%H:%M"));
2465- break;
2466-
2467- case DATE_PROXIMITY_TODAY:
2468- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2469- This format string is used for showing, on a 24-hour clock, times that happened today.
2470- en_US example: "%H:%M" --> "13:00" */
2471- g_string_assign (ret, dgettext_datetime("%H:%M"));
2472- break;
2473-
2474- case DATE_PROXIMITY_TOMORROW:
2475- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2476- This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
2477- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2478- en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00" */
2479- g_string_assign (ret, dgettext_datetime("Tomorrow\u2003%H:%M"));
2480- break;
2481-
2482- case DATE_PROXIMITY_LAST_WEEK:
2483- case DATE_PROXIMITY_NEXT_WEEK:
2484- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2485- This format string is used for showing, on a 24-hour clock, times that happened in the last week.
2486- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2487- en_US example: "%a\u2003%H:%M" --> "Fri 13:00" */
2488- g_string_assign (ret, dgettext_datetime("%a\u2003%H:%M"));
2489- break;
2490-
2491- case DATE_PROXIMITY_FAR:
2492- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2493- This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
2494- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2495- en_US example: "%a %b %d\u2003%H:%M" --> "Fri Oct 31 13:00"
2496- en_GB example: "%a %d %b\u2003%H:%M" --> "Fri 31 Oct 13:00" */
2497- g_string_assign (ret, dgettext_datetime("%a %d %b\u2003%H:%M"));
2498- break;
2499- }
2500- }
2501- }
2502-
2503- return g_string_free (ret, FALSE);
2504-}
2505-
2506-QString RelativeTimeFormatter::format() const
2507-{
2508- GDateTime* now = g_date_time_new_now_local();
2509- if (!now) { return QString(); }
2510-
2511- GDateTime* then = g_date_time_new_from_unix_local(time());
2512- if (!then) { return QString(); }
2513-
2514- char* time_format = generate_full_format_string_at_time(now, then);
2515-
2516- QString str(QString::fromUtf8(time_format));
2517- g_free(time_format);
2518-
2519- g_date_time_unref(now);
2520- g_date_time_unref(then);
2521-
2522- return str;
2523-}
2524
2525=== removed file 'plugins/Utils/relativetimeformatter.h'
2526--- plugins/Utils/relativetimeformatter.h 2014-09-19 14:46:53 +0000
2527+++ plugins/Utils/relativetimeformatter.h 1970-01-01 00:00:00 +0000
2528@@ -1,34 +0,0 @@
2529-/*
2530- * Copyright 2014 Canonical Ltd.
2531- *
2532- * This program is free software; you can redistribute it and/or modify
2533- * it under the terms of the GNU General Public License as published by
2534- * the Free Software Foundation; version 3.
2535- *
2536- * This program is distributed in the hope that it will be useful,
2537- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2538- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2539- * GNU General Public License for more details.
2540- *
2541- * You should have received a copy of the GNU General Public License
2542- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2543- *
2544- */
2545-
2546-#ifndef RELATIVETIMEFORMATTER_H
2547-#define RELATIVETIMEFORMATTER_H
2548-
2549-#include "timeformatter.h"
2550-
2551-// TODO - move this to the sdk
2552-// https://blueprints.launchpad.net/ubuntu-ui-toolkit/+spec/time-formatter
2553-class RelativeTimeFormatter : public GDateTimeFormatter
2554-{
2555- Q_OBJECT
2556-public:
2557- RelativeTimeFormatter(QObject *parent = 0);
2558-
2559- QString format() const override;
2560-};
2561-
2562-#endif // RELATIVETIMEFORMATTER_H
2563
2564=== removed file 'plugins/Utils/timeformatter.cpp'
2565--- plugins/Utils/timeformatter.cpp 2015-09-24 14:02:25 +0000
2566+++ plugins/Utils/timeformatter.cpp 1970-01-01 00:00:00 +0000
2567@@ -1,206 +0,0 @@
2568-/*
2569- * Copyright 2013 Canonical Ltd.
2570- *
2571- * This program is free software; you can redistribute it and/or modify
2572- * it under the terms of the GNU General Public License as published by
2573- * the Free Software Foundation; version 3.
2574- *
2575- * This program is distributed in the hope that it will be useful,
2576- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2577- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2578- * GNU General Public License for more details.
2579- *
2580- * You should have received a copy of the GNU General Public License
2581- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2582- *
2583- * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
2584- */
2585-
2586-#include "timeformatter.h"
2587-
2588-#include <gio/gio.h>
2589-#include <QDateTime>
2590-
2591-struct TimeFormatterPrivate
2592-{
2593- TimeFormatter *formatter;
2594-
2595- QString format;
2596- QString timeString;
2597- qint64 time;
2598-
2599- GDBusConnection *system_bus;
2600- guint subscription_id;
2601- GCancellable *cancellable;
2602-};
2603-
2604-static void
2605-timedate1_properties_changed (GDBusConnection *connection,
2606- const gchar *sender_name,
2607- const gchar *object_path,
2608- const gchar *interface_name,
2609- const gchar *signal_name,
2610- GVariant *parameters,
2611- gpointer user_data)
2612-{
2613- Q_UNUSED(connection);
2614- Q_UNUSED(sender_name);
2615- Q_UNUSED(object_path);
2616- Q_UNUSED(interface_name);
2617- Q_UNUSED(signal_name);
2618-
2619- TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
2620- GVariant *changed;
2621- GVariantIter *iter;
2622- const gchar *name;
2623-
2624- if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
2625- return;
2626-
2627- g_variant_get (parameters, "(s@a{sv}as)", nullptr, &changed, &iter);
2628-
2629- if (g_variant_lookup (changed, "Timezone", "s", nullptr)) {
2630- priv->formatter->update();
2631- }
2632- else {
2633- while (g_variant_iter_next (iter, "&s", &name)) {
2634- if (g_str_equal (name, "Timezone")) {
2635- priv->formatter->update();
2636- break;
2637- }
2638- }
2639- }
2640-
2641- g_variant_unref (changed);
2642- g_variant_iter_free (iter);
2643-}
2644-
2645-static void
2646-got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
2647-{
2648- Q_UNUSED(object);
2649-
2650- TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
2651- GError *error = nullptr;
2652-
2653- priv->system_bus = g_bus_get_finish (result, &error);
2654- if (priv->system_bus == nullptr) {
2655- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
2656- qWarning("TimeFormatter: cannot connect to the system bus: %s", error->message);
2657- g_error_free (error);
2658- return;
2659- }
2660-
2661- /* Listen to the PropertiesChanged on the org.freedesktop.timedate1
2662- * interface from any sender. In practice, this signal will only be sent
2663- * from timedated (we can trust other processes on the system bus to behave
2664- * nicely). That way, we don't have to watch timedated's well-known name
2665- * and keep the process alive.
2666- */
2667- priv->subscription_id = g_dbus_connection_signal_subscribe (priv->system_bus,
2668- nullptr, /* sender */
2669- "org.freedesktop.DBus.Properties",
2670- "PropertiesChanged",
2671- nullptr,
2672- "org.freedesktop.timedate1",
2673- G_DBUS_SIGNAL_FLAGS_NONE,
2674- timedate1_properties_changed,
2675- priv, nullptr);
2676-}
2677-
2678-TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
2679-{
2680- priv = new TimeFormatterPrivate;
2681- priv->formatter = this;
2682- priv->time = 0;
2683- priv->format = QStringLiteral("yyyy-MM-dd hh:mm");
2684- priv->system_bus = nullptr;
2685- priv->subscription_id = 0;
2686- priv->cancellable = g_cancellable_new ();
2687-
2688- g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
2689-}
2690-
2691-TimeFormatter::TimeFormatter(const QString &initialFormat, QObject *parent): TimeFormatter(parent)
2692-{
2693- priv->format = initialFormat;
2694-}
2695-
2696-TimeFormatter::~TimeFormatter()
2697-{
2698- if (priv->system_bus) {
2699- g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->subscription_id);
2700- g_object_unref (priv->system_bus);
2701- }
2702-
2703- g_cancellable_cancel (priv->cancellable);
2704- g_object_unref (priv->cancellable);
2705-}
2706-
2707-QString TimeFormatter::format() const
2708-{
2709- return priv->format;
2710-}
2711-
2712-QString TimeFormatter::timeString() const
2713-{
2714- return priv->timeString;
2715-}
2716-
2717-qint64 TimeFormatter::time() const
2718-{
2719- return priv->time;
2720-}
2721-
2722-void TimeFormatter::setFormat(const QString &format)
2723-{
2724- if (priv->format != format) {
2725- priv->format = format;
2726- Q_EMIT formatChanged(priv->format);
2727- update();
2728- }
2729-}
2730-
2731-void TimeFormatter::setTime(qint64 time)
2732-{
2733- if (priv->time != time) {
2734- priv->time = time;
2735- Q_EMIT timeChanged(priv->time);
2736- update();
2737- }
2738-}
2739-
2740-void TimeFormatter::update()
2741-{
2742- priv->timeString = formatTime();
2743- Q_EMIT timeStringChanged(priv->timeString);
2744-}
2745-
2746-QString TimeFormatter::formatTime() const
2747-{
2748- return QDateTime::fromMSecsSinceEpoch(time() / 1000).toString(format());
2749-}
2750-
2751-GDateTimeFormatter::GDateTimeFormatter(QObject* parent)
2752-: TimeFormatter(QStringLiteral("%d-%m-%Y %I:%M%p"), parent)
2753-{
2754-}
2755-
2756-QString GDateTimeFormatter::formatTime() const
2757-{
2758- gchar* time_string;
2759- GDateTime* datetime;
2760- QByteArray formatBytes = format().toUtf8();
2761-
2762- datetime = g_date_time_new_from_unix_local(time());
2763- if (!datetime) {
2764- return QLatin1String("");
2765- }
2766-
2767- time_string = g_date_time_format(datetime, formatBytes.constData());
2768- QString formattedTime(QString::fromUtf8(time_string));
2769-
2770- g_free(time_string);
2771- g_date_time_unref(datetime);
2772- return formattedTime;
2773-}
2774
2775=== removed file 'plugins/Utils/timeformatter.h'
2776--- plugins/Utils/timeformatter.h 2015-09-17 13:42:15 +0000
2777+++ plugins/Utils/timeformatter.h 1970-01-01 00:00:00 +0000
2778@@ -1,68 +0,0 @@
2779-/*
2780- * Copyright 2013 Canonical Ltd.
2781- *
2782- * This program is free software; you can redistribute it and/or modify
2783- * it under the terms of the GNU General Public License as published by
2784- * the Free Software Foundation; version 3.
2785- *
2786- * This program is distributed in the hope that it will be useful,
2787- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2788- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2789- * GNU General Public License for more details.
2790- *
2791- * You should have received a copy of the GNU General Public License
2792- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2793- *
2794- * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
2795- */
2796-
2797-#ifndef TIME_FORMATTER_H
2798-#define TIME_FORMATTER_H
2799-
2800-#include <QObject>
2801-
2802-// TODO - bug #1260728
2803-class TimeFormatter : public QObject
2804-{
2805- Q_OBJECT
2806- Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged)
2807- Q_PROPERTY(QString timeString READ timeString NOTIFY timeStringChanged)
2808- Q_PROPERTY(qint64 time READ time WRITE setTime NOTIFY timeChanged)
2809-
2810-public:
2811- TimeFormatter(QObject *parent = 0);
2812- virtual ~TimeFormatter();
2813-
2814- virtual QString format() const;
2815- QString timeString() const;
2816- qint64 time() const;
2817-
2818- void setFormat(const QString &format);
2819- void setTime(qint64 time);
2820-
2821- void update();
2822-
2823-Q_SIGNALS:
2824- void formatChanged(const QString &format);
2825- void timeStringChanged(const QString &timeString);
2826- void timeChanged(qint64 time);
2827-
2828-protected:
2829- TimeFormatter(const QString &initialFormat, QObject *parent = 0);
2830-
2831- virtual QString formatTime() const;
2832-
2833-private:
2834- struct TimeFormatterPrivate *priv;
2835-};
2836-
2837-class GDateTimeFormatter : public TimeFormatter
2838-{
2839-public:
2840- GDateTimeFormatter(QObject *parent = 0);
2841-
2842-protected:
2843- QString formatTime() const override;
2844-};
2845-
2846-#endif
2847
2848=== modified file 'plugins/Utils/timezoneFormatter.cpp'
2849--- plugins/Utils/timezoneFormatter.cpp 2015-09-17 13:42:15 +0000
2850+++ plugins/Utils/timezoneFormatter.cpp 2015-10-26 09:06:21 +0000
2851@@ -29,7 +29,7 @@
2852 if (tz.isValid()) {
2853 const QDateTime now = QDateTime::currentDateTime().toTimeZone(tz);
2854 // return locale-aware string in the form "day, hh:mm", e.g. "Mon 14:30" or "Mon 1:30 pm"
2855- return QStringLiteral("%1 %2").arg(now.toString("ddd")).arg(now.time().toString(Qt::DefaultLocaleShortDate));
2856+ return QStringLiteral("%1 %2").arg(now.toString(QStringLiteral("ddd"))).arg(now.time().toString(Qt::DefaultLocaleShortDate));
2857 }
2858 return QString();
2859 }
2860
2861=== modified file 'qml/Components/Dialogs.qml'
2862--- qml/Components/Dialogs.qml 2015-09-02 09:30:32 +0000
2863+++ qml/Components/Dialogs.qml 2015-10-26 09:06:21 +0000
2864@@ -20,6 +20,7 @@
2865 import Unity.Session 0.1
2866 import Ubuntu.Components 1.1
2867 import GlobalShortcut 1.0
2868+import Unity.Platform 1.0
2869 import "../Greeter"
2870
2871 Item {
2872@@ -46,13 +47,13 @@
2873
2874 GlobalShortcut { // reboot/shutdown dialog
2875 shortcut: Qt.Key_PowerDown
2876- active: root.usageScenario === "desktop"
2877+ active: Platform.isPC
2878 onTriggered: root.unitySessionService.RequestShutdown()
2879 }
2880
2881 GlobalShortcut { // reboot/shutdown dialog
2882 shortcut: Qt.Key_PowerOff
2883- active: root.usageScenario === "desktop"
2884+ active: Platform.isPC
2885 onTriggered: root.unitySessionService.RequestShutdown()
2886 }
2887
2888
2889=== modified file 'qml/Components/ScreenGrabber.qml'
2890--- qml/Components/ScreenGrabber.qml 2015-08-24 15:39:53 +0000
2891+++ qml/Components/ScreenGrabber.qml 2015-10-26 09:06:21 +0000
2892@@ -26,6 +26,9 @@
2893 anchors.fill: parent
2894 opacity: 0.0
2895
2896+ // to be set from outside
2897+ property int rotationAngle: 0
2898+
2899 ScreenGrabber {
2900 id: screenGrabber
2901 objectName: "screenGrabber"
2902@@ -67,7 +70,7 @@
2903 to: 0.0
2904 onStopped: {
2905 if (visible) {
2906- screenGrabber.captureAndSave();
2907+ screenGrabber.captureAndSave(root.rotationAngle);
2908 visible = false;
2909 }
2910 }
2911
2912=== added file 'qml/Components/WallpaperResolver.qml'
2913--- qml/Components/WallpaperResolver.qml 1970-01-01 00:00:00 +0000
2914+++ qml/Components/WallpaperResolver.qml 2015-10-26 09:06:21 +0000
2915@@ -0,0 +1,63 @@
2916+/*
2917+ * Copyright (C) 2015 Canonical, Ltd.
2918+ *
2919+ * This program is free software; you can redistribute it and/or modify
2920+ * it under the terms of the GNU General Public License as published by
2921+ * the Free Software Foundation; version 3.
2922+ *
2923+ * This program is distributed in the hope that it will be useful,
2924+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2925+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2926+ * GNU General Public License for more details.
2927+ *
2928+ * You should have received a copy of the GNU General Public License
2929+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2930+ */
2931+
2932+import QtQuick 2.4
2933+import AccountsService 0.1
2934+import GSettings 1.0
2935+import Ubuntu.Components 1.3
2936+
2937+/*
2938+ Defines the background URL based on several factors, such as:
2939+ - default, fallback, background
2940+ - Background set in AccountSettings, if any
2941+ - Background set in GSettings, if any
2942+ */
2943+QtObject {
2944+ // Users should set their UI width here.
2945+ property real width
2946+
2947+ property url defaultBackground: Qt.resolvedUrl(width >= units.gu(60) ? "../graphics/tablet_background.jpg"
2948+ : "../graphics/phone_background.jpg")
2949+
2950+ // That's the property users of this component are going to consume.
2951+ readonly property url background: asImageTester.status == Image.Ready ? asImageTester.source
2952+ : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
2953+
2954+ // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
2955+ property var _asImageTester: Image {
2956+ id: asImageTester
2957+ source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
2958+ height: 0
2959+ width: 0
2960+ sourceSize.height: 0
2961+ sourceSize.width: 0
2962+ }
2963+
2964+ // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
2965+ property var _gsImageTester: Image {
2966+ id: gsImageTester
2967+ source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
2968+ height: 0
2969+ width: 0
2970+ sourceSize.height: 0
2971+ sourceSize.width: 0
2972+ }
2973+
2974+ property var _gsettings: GSettings {
2975+ id: backgroundSettings
2976+ schema.id: "org.gnome.desktop.background"
2977+ }
2978+}
2979
2980=== modified file 'qml/DeviceConfiguration.qml'
2981--- qml/DeviceConfiguration.qml 2015-07-01 17:52:34 +0000
2982+++ qml/DeviceConfiguration.qml 2015-10-26 09:06:21 +0000
2983@@ -86,6 +86,7 @@
2984 PropertyChanges {
2985 target: root
2986 category: "desktop"
2987+ supportedOrientations: root.useNativeOrientation
2988 }
2989 }
2990 ]
2991
2992=== added file 'qml/DisabledScreenNotice.qml'
2993--- qml/DisabledScreenNotice.qml 1970-01-01 00:00:00 +0000
2994+++ qml/DisabledScreenNotice.qml 2015-10-26 09:06:21 +0000
2995@@ -0,0 +1,49 @@
2996+/*
2997+ * Copyright (C) 2015 Canonical, Ltd.
2998+ *
2999+ * This program is free software; you can redistribute it and/or modify
3000+ * it under the terms of the GNU General Public License as published by
3001+ * the Free Software Foundation; version 3.
3002+ *
3003+ * This program is distributed in the hope that it will be useful,
3004+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3005+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3006+ * GNU General Public License for more details.
3007+ *
3008+ * You should have received a copy of the GNU General Public License
3009+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3010+ */
3011+
3012+import QtQuick 2.4
3013+import Ubuntu.Components 1.3
3014+import "Components"
3015+
3016+Image {
3017+ id: root
3018+
3019+ WallpaperResolver {
3020+ width: root.width
3021+ id: wallpaperResolver
3022+ }
3023+
3024+ source: wallpaperResolver.background
3025+
3026+ UbuntuShape {
3027+ anchors.fill: text
3028+ anchors.margins: -units.gu(2)
3029+ backgroundColor: "black"
3030+ opacity: 0.4
3031+ }
3032+
3033+ Label {
3034+ id: text
3035+ anchors.centerIn: parent
3036+ width: parent.width / 2
3037+ text: i18n.tr("Your device is now connected to an external display.")
3038+ color: "white"
3039+ horizontalAlignment: Text.AlignHCenter
3040+ verticalAlignment: Text.AlignVCenter
3041+ fontSize: "x-large"
3042+ wrapMode: Text.Wrap
3043+ }
3044+}
3045
3046=== modified file 'qml/Greeter/Greeter.qml'
3047--- qml/Greeter/Greeter.qml 2015-09-02 13:06:56 +0000
3048+++ qml/Greeter/Greeter.qml 2015-10-26 09:06:21 +0000
3049@@ -251,7 +251,7 @@
3050
3051 // event eater
3052 // Nothing should leak to items behind the greeter
3053- MouseArea { anchors.fill: parent }
3054+ MouseArea { anchors.fill: parent; hoverEnabled: true }
3055
3056 Loader {
3057 id: loader
3058
3059=== modified file 'qml/Launcher/Launcher.qml'
3060--- qml/Launcher/Launcher.qml 2015-09-02 14:18:40 +0000
3061+++ qml/Launcher/Launcher.qml 2015-10-26 09:06:21 +0000
3062@@ -188,6 +188,7 @@
3063 bottom: parent.bottom
3064 }
3065 enabled: root.shadeBackground && root.state == "visible"
3066+ visible: enabled // otherwise it will get in the way of cursor selection for some reason
3067 onPressed: {
3068 root.state = ""
3069 }
3070
3071=== modified file 'qml/Notifications/Notification.qml'
3072--- qml/Notifications/Notification.qml 2015-10-08 19:30:59 +0000
3073+++ qml/Notifications/Notification.qml 2015-10-26 09:06:21 +0000
3074@@ -50,6 +50,7 @@
3075 readonly property real contentSpacing: units.gu(2)
3076 readonly property bool canBeClosed: type === Notification.Ephemeral
3077 property bool hasMouse
3078+ property url background: ""
3079
3080 objectName: "background"
3081 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
3082@@ -401,6 +402,7 @@
3083 menuData: model
3084 menuIndex: index
3085 maxHeight: notification.maxHeight
3086+ background: notification.background
3087
3088 onLoaded: {
3089 notification.fullscreen = Qt.binding(function() { return fullscreen; });
3090
3091=== modified file 'qml/Notifications/NotificationMenuItemFactory.qml'
3092--- qml/Notifications/NotificationMenuItemFactory.qml 2014-11-17 13:46:56 +0000
3093+++ qml/Notifications/NotificationMenuItemFactory.qml 2015-10-26 09:06:21 +0000
3094@@ -30,6 +30,7 @@
3095 property int menuIndex : -1
3096 property int maxHeight
3097 readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
3098+ property url background: ""
3099
3100 signal accepted()
3101
3102@@ -149,7 +150,7 @@
3103 infoText: notification.summary
3104 errorText: errorAction.valid ? errorAction.state : ""
3105 retryText: notification.body
3106- background: shell.background
3107+ background: menuFactory.background
3108 darkenBackground: 0.4
3109
3110 onEntered: {
3111
3112=== modified file 'qml/Notifications/Notifications.qml'
3113--- qml/Notifications/Notifications.qml 2015-09-22 14:23:44 +0000
3114+++ qml/Notifications/Notifications.qml 2015-10-26 09:06:21 +0000
3115@@ -29,6 +29,7 @@
3116 property real margin
3117 property bool useModal: snapDecisionProxyModel.count > 0
3118 property bool hasMouse
3119+ property url background: ""
3120
3121 UnitySortFilterProxyModel {
3122 id: snapDecisionProxyModel
3123@@ -60,6 +61,7 @@
3124 maxHeight: notificationList.height
3125 margins: notificationList.margin
3126 hasMouse: notificationList.hasMouse
3127+ background: notificationList.background
3128
3129 // make sure there's no opacity-difference between the several
3130 // elements in a notification
3131
3132=== modified file 'qml/OrientedShell.qml'
3133--- qml/OrientedShell.qml 2015-09-22 14:23:44 +0000
3134+++ qml/OrientedShell.qml 2015-10-26 09:06:21 +0000
3135@@ -76,6 +76,12 @@
3136 oskSettings.disableHeight = shell.usageScenario == "desktop"
3137 }
3138
3139+ // we must rotate to a supported orientation regardless of shell's preference
3140+ property bool orientationChangesEnabled:
3141+ (orientation & supportedOrientations) === 0 ? true
3142+ : shell.orientationChangesEnabled
3143+
3144+
3145 Binding {
3146 target: oskSettings
3147 property: "stayHidden"
3148@@ -89,7 +95,10 @@
3149 }
3150
3151 readonly property int supportedOrientations: shell.supportedOrientations
3152- & deviceConfiguration.supportedOrientations
3153+ & (deviceConfiguration.supportedOrientations == deviceConfiguration.useNativeOrientation
3154+ ? nativeOrientation
3155+ : deviceConfiguration.supportedOrientations)
3156+
3157 property int acceptedOrientationAngle: {
3158 if (orientation & supportedOrientations) {
3159 return Screen.angleBetween(nativeOrientation, orientation);
3160
3161=== modified file 'qml/Panel/Indicators/MenuItemFactory.qml'
3162--- qml/Panel/Indicators/MenuItemFactory.qml 2015-09-17 18:02:22 +0000
3163+++ qml/Panel/Indicators/MenuItemFactory.qml 2015-10-26 09:06:21 +0000
3164@@ -20,7 +20,7 @@
3165 import QMenuModel 0.1
3166 import Utils 0.1 as Utils
3167 import Ubuntu.Components.ListItems 0.1 as ListItems
3168-import Ubuntu.Components 1.2
3169+import Ubuntu.Components 1.3
3170 import Unity.Session 0.1
3171
3172 Item {
3173@@ -350,20 +350,23 @@
3174 id: alarmMenu;
3175
3176 Menus.EventMenu {
3177+ id: alarmItem
3178 objectName: "alarmMenu"
3179 property QtObject menuData: null
3180 property var menuModel: menuFactory.menuModel
3181 property int menuIndex: -1
3182 property var extendedData: menuData && menuData.ext || undefined
3183- // TODO - bug #1260728
3184- property var timeFormatter: Utils.GDateTimeFormatter {
3185- time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
3186- format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
3187+
3188+ property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
3189+ LiveTimer {
3190+ frequency: LiveTimer.Relative
3191+ relativeTime: alarmItem.serverTime
3192+ onTrigger: alarmItem.serverTime = new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
3193 }
3194
3195 text: menuData && menuData.label || ""
3196 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
3197- time: timeFormatter.timeString
3198+ time: i18n.relativeDateTime(serverTime)
3199 enabled: menuData && menuData.sensitive || false
3200 highlightWhenPressed: false
3201
3202@@ -379,8 +382,7 @@
3203
3204 function loadAttributes() {
3205 if (!menuModel || menuIndex == -1) return;
3206- menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
3207- 'x-canonical-time-format': 'string'});
3208+ menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64'});
3209 }
3210 }
3211 }
3212@@ -389,20 +391,23 @@
3213 id: appointmentMenu;
3214
3215 Menus.EventMenu {
3216+ id: appointmentItem
3217 objectName: "appointmentMenu"
3218 property QtObject menuData: null
3219 property var menuModel: menuFactory.menuModel
3220 property int menuIndex: -1
3221 property var extendedData: menuData && menuData.ext || undefined
3222- // TODO - bug #1260728
3223- property var timeFormatter: Utils.GDateTimeFormatter {
3224- time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
3225- format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
3226+
3227+ property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
3228+ LiveTimer {
3229+ frequency: LiveTimer.Relative
3230+ relativeTime: appointmentItem.serverTime
3231+ onTrigger: appointmentItem.serverTime = new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
3232 }
3233
3234 text: menuData && menuData.label || ""
3235 iconSource: menuData && menuData.icon || "image://theme/calendar"
3236- time: timeFormatter.timeString
3237+ time: i18n.relativeDateTime(serverTime)
3238 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
3239 enabled: menuData && menuData.sensitive || false
3240 highlightWhenPressed: false
3241@@ -420,8 +425,7 @@
3242 function loadAttributes() {
3243 if (!menuModel || menuIndex == -1) return;
3244 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
3245- 'x-canonical-time': 'int64',
3246- 'x-canonical-time-format': 'string'});
3247+ 'x-canonical-time': 'int64'});
3248 }
3249 }
3250 }
3251
3252=== modified file 'qml/Panel/Indicators/MessageMenuItemFactory.qml'
3253--- qml/Panel/Indicators/MessageMenuItemFactory.qml 2015-09-15 22:50:16 +0000
3254+++ qml/Panel/Indicators/MessageMenuItemFactory.qml 2015-10-26 09:06:21 +0000
3255@@ -19,7 +19,7 @@
3256 */
3257
3258 import QtQuick 2.0
3259-import Ubuntu.Components 0.1
3260+import Ubuntu.Components 1.3
3261 import Ubuntu.Settings.Menus 0.1 as Menus
3262 import QMenuModel 0.1 as QMenuModel
3263 import Utils 0.1 as Utils
3264@@ -35,12 +35,17 @@
3265 signal menuSelected
3266 signal menuDeselected
3267
3268- property var extendedData: menuData && menuData.ext || undefined
3269- property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
3270-
3271- // TODO - bug #1260728
3272- property var timeFormatter: Utils.RelativeTimeFormatter {
3273- time: getExtendedProperty(extendedData, "xCanonicalTime", 0) / 1000000
3274+ QtObject {
3275+ id: priv
3276+ property var extendedData: menuData && menuData.ext || undefined
3277+ property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
3278+ property date time: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) / 1000)
3279+ property string timeString: i18n.relativeDateTime(time)
3280+ }
3281+ LiveTimer {
3282+ frequency: LiveTimer.Relative
3283+ relativeTime: priv.time
3284+ onTrigger: priv.timeString = Qt.binding(function() { return i18n.relativeDateTime(priv.time); })
3285 }
3286
3287 onMenuModelChanged: {
3288@@ -50,7 +55,7 @@
3289 loadAttributes();
3290 }
3291
3292- sourceComponent: loadMessage(actionsDescription)
3293+ sourceComponent: loadMessage(priv.actionsDescription)
3294
3295 function loadMessage(actions)
3296 {
3297@@ -100,11 +105,11 @@
3298 objectName: "simpleTextMessage"
3299 // text
3300 title: menuData && menuData.label || ""
3301- time: timeFormatter.timeString
3302- body: getExtendedProperty(extendedData, "xCanonicalText", "")
3303+ time: priv.timeString
3304+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
3305 // icons
3306- avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
3307- icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
3308+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
3309+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/message")
3310 // actions
3311 enabled: menuData && menuData.sensitive || false
3312 removable: !selected
3313@@ -133,7 +138,9 @@
3314 Menus.TextMessageMenu {
3315 id: message
3316 objectName: "textMessage"
3317- property var replyActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
3318+ property var replyActionDescription: priv.actionsDescription && priv.actionsDescription.length > 0 ?
3319+ priv.actionsDescription[0] :
3320+ undefined
3321
3322 property var replyAction: QMenuModel.UnityMenuAction {
3323 model: menuModel
3324@@ -143,13 +150,13 @@
3325
3326 // text
3327 title: menuData && menuData.label || ""
3328- time: timeFormatter.timeString
3329- body: getExtendedProperty(extendedData, "xCanonicalText", "")
3330- replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.tr("Send"))
3331+ time: priv.timeString
3332+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
3333+ replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.ctr("Button: Send a reply message", "Send"))
3334 replyHintText: i18n.ctr("Label: Hint in message indicator line edit", "Reply")
3335 // icons
3336- avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
3337- icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
3338+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
3339+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/message")
3340 // actions
3341 replyEnabled: replyAction.valid && replyAction.enabled
3342 enabled: menuData && menuData.sensitive || false
3343@@ -183,8 +190,10 @@
3344 Menus.SnapDecisionMenu {
3345 id: message
3346 objectName: "snapDecision"
3347- property var activateActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
3348- property var replyActionDescription: actionsDescription && actionsDescription.length > 1 ? actionsDescription[1] : undefined
3349+ property var activateActionDescription: priv.actionsDescription && priv.actionsDescription.length > 0 ?
3350+ priv.actionsDescription[0] : undefined
3351+ property var replyActionDescription: priv.actionsDescription && priv.actionsDescription.length > 1 ?
3352+ priv.actionsDescription[1] : undefined
3353
3354 property var activateAction: QMenuModel.UnityMenuAction {
3355 model: menuModel
3356@@ -199,13 +208,13 @@
3357
3358 // text
3359 title: menuData && menuData.label || ""
3360- time: timeFormatter.timeString
3361- body: getExtendedProperty(extendedData, "xCanonicalText", "")
3362- actionButtonText: getExtendedProperty(activateActionDescription, "label", i18n.tr("Call back"))
3363- replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.tr("Send"))
3364+ time: priv.timeString
3365+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
3366+ actionButtonText: getExtendedProperty(activateActionDescription, "label", i18n.ctr("Button: Call back on phone", "Call back"))
3367+ replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.ctr("Button: Send a reply message", "Send"))
3368 // icons
3369- avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
3370- icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
3371+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
3372+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
3373 // actions
3374 actionEnabled: activateAction.valid && activateAction.enabled
3375 replyEnabled: replyAction.valid && replyAction.enabled
3376
3377=== modified file 'qml/Panel/Panel.qml'
3378--- qml/Panel/Panel.qml 2015-06-29 03:58:22 +0000
3379+++ qml/Panel/Panel.qml 2015-10-26 09:06:21 +0000
3380@@ -48,6 +48,7 @@
3381 MouseArea {
3382 anchors.fill: parent
3383 onClicked: if (indicators.fullyOpened) indicators.hide();
3384+ hoverEnabled: true // should also eat hover events, otherwise they will pass through
3385 }
3386 }
3387
3388
3389=== modified file 'qml/Rotation/RotationStates.qml'
3390--- qml/Rotation/RotationStates.qml 2015-05-11 14:36:03 +0000
3391+++ qml/Rotation/RotationStates.qml 2015-10-26 09:06:21 +0000
3392@@ -83,7 +83,7 @@
3393 }
3394
3395 function tryUpdateState() {
3396- if (d.transitioning || (!d.startingUp && !root.shell.orientationChangesEnabled)) {
3397+ if (d.transitioning || (!d.startingUp && !root.orientedShell.orientationChangesEnabled)) {
3398 return;
3399 }
3400
3401@@ -95,7 +95,7 @@
3402 }
3403
3404 property Connections shellConnections: Connections {
3405- target: root.shell
3406+ target: root.orientedShell
3407 onOrientationChangesEnabledChanged: {
3408 d.tryUpdateState();
3409 }
3410
3411=== modified file 'qml/Shell.qml'
3412--- qml/Shell.qml 2015-09-22 14:23:44 +0000
3413+++ qml/Shell.qml 2015-10-26 09:06:21 +0000
3414@@ -17,7 +17,6 @@
3415 import QtQuick 2.0
3416 import QtQuick.Window 2.0
3417 import AccountsService 0.1
3418-import GSettings 1.0
3419 import Unity.Application 0.1
3420 import Ubuntu.Components 0.1
3421 import Ubuntu.Components.Popups 1.0
3422@@ -41,6 +40,7 @@
3423 import Unity.Session 0.1
3424 import Unity.DashCommunicator 0.1
3425 import Unity.Indicators 0.1 as Indicators
3426+import Cursor 1.0
3427
3428
3429 Item {
3430@@ -63,7 +63,7 @@
3431 function updateFocusedAppOrientationAnimated() {
3432 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
3433 }
3434- property bool hasMouse
3435+ property bool hasMouse: false
3436
3437 // to be read from outside
3438 readonly property int mainAppWindowOrientationAngle:
3439@@ -107,9 +107,11 @@
3440 enabled: greeter && !greeter.waiting
3441
3442 property real edgeSize: units.gu(2)
3443- property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
3444- property url background: asImageTester.status == Image.Ready ? asImageTester.source
3445- : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
3446+
3447+ WallpaperResolver {
3448+ id: wallpaperResolver
3449+ width: shell.width
3450+ }
3451
3452 readonly property alias greeter: greeterLoader.item
3453
3454@@ -130,31 +132,6 @@
3455 shell.activateApplication(app);
3456 }
3457
3458- // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
3459- Image {
3460- id: asImageTester
3461- source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
3462- height: 0
3463- width: 0
3464- sourceSize.height: 0
3465- sourceSize.width: 0
3466- }
3467-
3468- GSettings {
3469- id: backgroundSettings
3470- schema.id: "org.gnome.desktop.background"
3471- }
3472-
3473- // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
3474- Image {
3475- id: gsImageTester
3476- source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
3477- height: 0
3478- width: 0
3479- sourceSize.height: 0
3480- sourceSize.width: 0
3481- }
3482-
3483 Binding {
3484 target: LauncherModel
3485 property: "applicationManager"
3486@@ -190,11 +167,6 @@
3487 onScreenshotTriggered: screenGrabber.capture();
3488 }
3489
3490- ScreenGrabber {
3491- id: screenGrabber
3492- z: dialogs.z + 10
3493- }
3494-
3495 GlobalShortcut {
3496 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter
3497 }
3498@@ -327,7 +299,7 @@
3499 Binding {
3500 target: applicationsDisplayLoader.item
3501 property: "background"
3502- value: shell.background
3503+ value: wallpaperResolver.background
3504 }
3505 Binding {
3506 target: applicationsDisplayLoader.item
3507@@ -432,7 +404,7 @@
3508 tabletMode: shell.usageScenario != "phone"
3509 launcherOffset: launcher.progress
3510 forcedUnlock: tutorial.running
3511- background: shell.background
3512+ background: wallpaperResolver.background
3513
3514 // avoid overlapping with Launcher's edge drag area
3515 // FIXME: Fix TouchRegistry & friends and remove this workaround
3516@@ -607,7 +579,7 @@
3517 id: wizard
3518 objectName: "wizard"
3519 anchors.fill: parent
3520- background: shell.background
3521+ background: wallpaperResolver.background
3522
3523 function unlockWhenDoneWithWizard() {
3524 if (!active) {
3525@@ -638,6 +610,7 @@
3526 model: NotificationBackend.Model
3527 margin: units.gu(1)
3528 hasMouse: shell.hasMouse
3529+ background: wallpaperResolver.background
3530
3531 y: topmostIsFullscreen ? 0 : panel.panelHeight
3532 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
3533@@ -684,9 +657,21 @@
3534 onShowHome: showHome()
3535 }
3536
3537+ ScreenGrabber {
3538+ id: screenGrabber
3539+ rotationAngle: -shell.orientationAngle
3540+ z: dialogs.z + 10
3541+ }
3542+
3543+ Cursor {
3544+ id: cursor
3545+ visible: shell.hasMouse
3546+ z: screenGrabber.z + 1
3547+ }
3548+
3549 Rectangle {
3550 id: shutdownFadeOutRectangle
3551- z: screenGrabber.z + 10
3552+ z: cursor.z + 1
3553 enabled: false
3554 visible: false
3555 color: "black"
3556@@ -703,5 +688,4 @@
3557 }
3558 }
3559 }
3560-
3561 }
3562
3563=== modified file 'qml/Stages/ApplicationWindow.qml'
3564--- qml/Stages/ApplicationWindow.qml 2015-09-17 12:25:29 +0000
3565+++ qml/Stages/ApplicationWindow.qml 2015-10-26 09:06:21 +0000
3566@@ -27,6 +27,8 @@
3567 readonly property bool fullscreen: application ? application.fullscreen : false
3568 property alias interactive: sessionContainer.interactive
3569 property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
3570+ readonly property string title: sessionContainer.surface && sessionContainer.surface.name !== "" ?
3571+ sessionContainer.surface.name : d.name
3572
3573 // to be set from outside
3574 property QtObject application
3575
3576=== modified file 'qml/Stages/DecoratedWindow.qml'
3577--- qml/Stages/DecoratedWindow.qml 2015-09-08 10:32:28 +0000
3578+++ qml/Stages/DecoratedWindow.qml 2015-10-26 09:06:21 +0000
3579@@ -31,9 +31,10 @@
3580 property bool highlightShown: false
3581 property real shadowOpacity: 1
3582
3583- signal close();
3584- signal maximize();
3585- signal minimize();
3586+ signal close()
3587+ signal maximize()
3588+ signal minimize()
3589+ signal decorationPressed()
3590
3591 BorderImage {
3592 anchors {
3593@@ -61,13 +62,15 @@
3594
3595 WindowDecoration {
3596 id: decoration
3597+ target: root.parent
3598 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
3599 anchors { left: parent.left; top: parent.top; right: parent.right }
3600 height: units.gu(3)
3601- title: model.name
3602+ title: window.title !== "" ? window.title : model.name
3603 onClose: root.close();
3604 onMaximize: root.maximize();
3605 onMinimize: root.minimize();
3606+ onPressed: root.decorationPressed();
3607 visible: decorationShown
3608 }
3609
3610
3611=== modified file 'qml/Stages/DesktopStage.qml'
3612--- qml/Stages/DesktopStage.qml 2015-09-18 15:28:07 +0000
3613+++ qml/Stages/DesktopStage.qml 2015-10-26 09:06:21 +0000
3614@@ -138,9 +138,6 @@
3615 height: units.gu(50)
3616 focus: model.appId === priv.focusedAppId
3617
3618- readonly property int minWidth: units.gu(10)
3619- readonly property int minHeight: units.gu(10)
3620-
3621 property bool maximized: false
3622 property bool minimized: false
3623
3624@@ -215,12 +212,11 @@
3625 when: index == spread.highlightedIndex && blurLayer.ready
3626 }
3627
3628- WindowMoveResizeArea {
3629- id: windowMoveResizeArea
3630+ WindowResizeArea {
3631 target: appDelegate
3632- minWidth: appDelegate.minWidth
3633- minHeight: appDelegate.minHeight
3634- resizeHandleWidth: units.gu(2)
3635+ minWidth: units.gu(10)
3636+ minHeight: units.gu(10)
3637+ borderThickness: units.gu(2)
3638 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
3639
3640 onPressed: { ApplicationManager.focusApplication(model.appId) }
3641@@ -240,6 +236,7 @@
3642 onClose: ApplicationManager.stopApplication(model.appId)
3643 onMaximize: appDelegate.maximize()
3644 onMinimize: appDelegate.minimize()
3645+ onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
3646 }
3647 }
3648 }
3649
3650=== modified file 'qml/Stages/SurfaceContainer.qml'
3651--- qml/Stages/SurfaceContainer.qml 2015-09-09 13:44:12 +0000
3652+++ qml/Stages/SurfaceContainer.qml 2015-10-26 09:06:21 +0000
3653@@ -28,6 +28,7 @@
3654 property bool hadSurface: false
3655 property bool interactive
3656 property int surfaceOrientationAngle: 0
3657+ property string name: surface ? surface.name : ""
3658 property bool resizeSurface: true
3659
3660 onSurfaceChanged: {
3661
3662=== modified file 'qml/Stages/WindowDecoration.qml'
3663--- qml/Stages/WindowDecoration.qml 2015-03-13 19:18:35 +0000
3664+++ qml/Stages/WindowDecoration.qml 2015-10-26 09:06:21 +0000
3665@@ -1,5 +1,5 @@
3666 /*
3667- * Copyright (C) 2014 Canonical, Ltd.
3668+ * Copyright (C) 2014-2015 Canonical, Ltd.
3669 *
3670 * This program is free software; you can redistribute it and/or modify
3671 * it under the terms of the GNU General Public License as published by
3672@@ -12,25 +12,53 @@
3673 *
3674 * You should have received a copy of the GNU General Public License
3675 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3676- *
3677- * Authors: Michael Zanetti <michael.zanetti@canonical.com>
3678 */
3679
3680 import QtQuick 2.3
3681+import Unity.Application 0.1 // For Mir singleton
3682 import Ubuntu.Components 1.1
3683 import "../Components"
3684
3685-Item {
3686+MouseArea {
3687 id: root
3688 clip: true
3689
3690+ property Item target
3691 property alias title: titleLabel.text
3692 property bool active: false
3693+ hoverEnabled: true
3694
3695 signal close()
3696 signal minimize()
3697 signal maximize()
3698
3699+ QtObject {
3700+ id: priv
3701+ property real distanceX
3702+ property real distanceY
3703+ property bool dragging
3704+ }
3705+
3706+ onPressedChanged: {
3707+ if (pressed) {
3708+ var pos = mapToItem(root.target, mouseX, mouseY);
3709+ priv.distanceX = pos.x;
3710+ priv.distanceY = pos.y;
3711+ priv.dragging = true;
3712+ Mir.cursorName = "grabbing";
3713+ } else {
3714+ priv.dragging = false;
3715+ Mir.cursorName = "";
3716+ }
3717+ }
3718+ onPositionChanged: {
3719+ if (priv.dragging) {
3720+ var pos = mapToItem(root.target.parent, mouseX, mouseY);
3721+ root.target.x = pos.x - priv.distanceX;
3722+ root.target.y = pos.y - priv.distanceY;
3723+ }
3724+ }
3725+
3726 Rectangle {
3727 anchors.fill: parent
3728 anchors.bottomMargin: -radius
3729
3730=== renamed file 'qml/Stages/WindowMoveResizeArea.qml' => 'qml/Stages/WindowResizeArea.qml'
3731--- qml/Stages/WindowMoveResizeArea.qml 2015-09-08 10:32:28 +0000
3732+++ qml/Stages/WindowResizeArea.qml 2015-10-26 09:06:21 +0000
3733@@ -12,18 +12,19 @@
3734 *
3735 * You should have received a copy of the GNU General Public License
3736 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3737- *
3738- * Authors: Michael Zanetti <michael.zanetti@canonical.com>
3739 */
3740
3741 import QtQuick 2.3
3742 import Ubuntu.Components 1.1
3743 import Utils 0.1
3744+import Unity.Application 0.1 // for Mir.cursorName
3745
3746 MouseArea {
3747 id: root
3748 anchors.fill: target
3749- anchors.margins: -resizeHandleWidth
3750+ anchors.margins: -borderThickness
3751+
3752+ hoverEnabled: true
3753
3754 property var windowStateStorage: WindowStateStorage
3755
3756@@ -31,24 +32,10 @@
3757 // The area will anchor to it and manage move and resize events
3758 property Item target: null
3759 property string windowId: ""
3760- property int resizeHandleWidth: 0
3761+ property int borderThickness: 0
3762 property int minWidth: 0
3763 property int minHeight: 0
3764
3765- QtObject {
3766- id: priv
3767- readonly property int windowWidth: root.width - root.resizeHandleWidth * 2
3768- readonly property int windowHeight: root.height - resizeHandleWidth * 2
3769-
3770- property var startPoint
3771-
3772- property bool resizeTop: false
3773- property bool resizeBottom: false
3774- property bool resizeLeft: false
3775- property bool resizeRight: false
3776-
3777- }
3778-
3779 Component.onCompleted: {
3780 var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
3781 if (windowState !== undefined) {
3782@@ -59,51 +46,137 @@
3783 }
3784 }
3785
3786- onPressed: {
3787- priv.startPoint = Qt.point(mouse.x, mouse.y);
3788- priv.resizeTop = mouseY < root.resizeHandleWidth;
3789- priv.resizeBottom = mouseY > (root.height - root.resizeHandleWidth);
3790- priv.resizeLeft = mouseX < root.resizeHandleWidth;
3791- priv.resizeRight = mouseX > (root.width - root.resizeHandleWidth);
3792- }
3793-
3794- onPositionChanged: {
3795- var currentPoint = Qt.point(mouse.x, mouse.y);
3796- var mouseDiff = Qt.point(currentPoint.x - priv.startPoint.x, currentPoint.y - priv.startPoint.y);
3797- var moveDiff = Qt.point(0, 0);
3798- var sizeDiff = Qt.point(0, 0);
3799- var maxSizeDiff = Qt.point(root.minWidth - root.target.width, root.minHeight - root.target.height)
3800-
3801- if (priv.resizeTop || priv.resizeBottom || priv.resizeLeft || priv.resizeRight) {
3802- if (priv.resizeTop) {
3803- sizeDiff.y = Math.max(maxSizeDiff.y, -currentPoint.y + priv.startPoint.y)
3804- moveDiff.y = -sizeDiff.y
3805- }
3806- if (priv.resizeBottom) {
3807- sizeDiff.y = Math.max(maxSizeDiff.y, currentPoint.y - priv.startPoint.y)
3808- priv.startPoint.y += sizeDiff.y
3809- }
3810- if (priv.resizeLeft) {
3811- sizeDiff.x = Math.max(maxSizeDiff.x, -currentPoint.x + priv.startPoint.x)
3812- moveDiff.x = -sizeDiff.x
3813- }
3814- if (priv.resizeRight) {
3815- sizeDiff.x = Math.max(maxSizeDiff.x, currentPoint.x - priv.startPoint.x)
3816- priv.startPoint.x += sizeDiff.x
3817- }
3818-
3819- target.x += moveDiff.x;
3820- target.y += moveDiff.y;
3821- target.width += sizeDiff.x;
3822- target.height += sizeDiff.y;
3823- } else {
3824- target.x += mouseDiff.x;
3825- target.y += mouseDiff.y;
3826- }
3827-
3828- }
3829-
3830 Component.onDestruction: {
3831 windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
3832 }
3833+
3834+ QtObject {
3835+ id: d
3836+ property bool leftBorder: false
3837+ property bool rightBorder: false
3838+ property bool topBorder: false
3839+ property bool bottomBorder: false
3840+
3841+ property bool dragging: false
3842+ property real startMousePosX
3843+ property real startMousePosY
3844+ property real startX
3845+ property real startY
3846+ property real startWidth
3847+ property real startHeight
3848+
3849+ property string cursorName: {
3850+ if (root.containsMouse || root.pressed) {
3851+ if (leftBorder && !topBorder && !bottomBorder) {
3852+ return "left_side";
3853+ } else if (rightBorder && !topBorder && !bottomBorder) {
3854+ return "right_side";
3855+ } else if (topBorder && !leftBorder && !rightBorder) {
3856+ return "top_side";
3857+ } else if (bottomBorder && !leftBorder && !rightBorder) {
3858+ return "bottom_side";
3859+ } else if (leftBorder && topBorder) {
3860+ return "top_left_corner";
3861+ } else if (leftBorder && bottomBorder) {
3862+ return "bottom_left_corner";
3863+ } else if (rightBorder && topBorder) {
3864+ return "top_right_corner";
3865+ } else if (rightBorder && bottomBorder) {
3866+ return "bottom_right_corner";
3867+ } else {
3868+ return "";
3869+ }
3870+ } else {
3871+ return "";
3872+ }
3873+ }
3874+ onCursorNameChanged: {
3875+ Mir.cursorName = cursorName;
3876+ }
3877+
3878+ function updateBorders() {
3879+ leftBorder = mouseX <= borderThickness;
3880+ rightBorder = mouseX >= width - borderThickness;
3881+ topBorder = mouseY <= borderThickness;
3882+ bottomBorder = mouseY >= height - borderThickness;
3883+ }
3884+ }
3885+
3886+ onPressedChanged: {
3887+ var pos = mapToItem(target.parent, mouseX, mouseY);
3888+
3889+ if (pressed) {
3890+ d.updateBorders();
3891+ var pos = mapToItem(root.target.parent, mouseX, mouseY);
3892+ d.startMousePosX = pos.x;
3893+ d.startMousePosY = pos.y;
3894+ d.startX = target.x;
3895+ d.startY = target.y;
3896+ d.startWidth = target.width;
3897+ d.startHeight = target.height;
3898+ d.dragging = true;
3899+ } else {
3900+ d.dragging = false;
3901+ if (containsMouse) {
3902+ d.updateBorders();
3903+ }
3904+ }
3905+ }
3906+
3907+ onEntered: {
3908+ if (!pressed) {
3909+ d.updateBorders();
3910+ }
3911+ }
3912+
3913+ onPositionChanged: {
3914+ if (!pressed) {
3915+ d.updateBorders();
3916+ }
3917+
3918+ if (!d.dragging) {
3919+ return;
3920+ }
3921+
3922+ var pos = mapToItem(target.parent, mouse.x, mouse.y);
3923+
3924+ var deltaX = pos.x - d.startMousePosX;
3925+ var deltaY = pos.y - d.startMousePosY;
3926+
3927+ if (d.leftBorder) {
3928+ var newTargetX = d.startX + deltaX;
3929+ if (target.x + target.width > newTargetX + minWidth) {
3930+ target.width = target.x + target.width - newTargetX;
3931+ target.x = newTargetX;
3932+ } else {
3933+ target.x = target.x + target.width - minWidth;
3934+ target.width = minWidth;
3935+ }
3936+
3937+ } else if (d.rightBorder) {
3938+ if (d.startWidth + deltaX >= minWidth) {
3939+ target.width = d.startWidth + deltaX;
3940+ } else {
3941+ target.width = minWidth;
3942+ }
3943+ }
3944+
3945+ if (d.topBorder) {
3946+ var newTargetY = d.startY + deltaY;
3947+ if (target.y + target.height > newTargetY + minHeight) {
3948+ target.height = target.y + target.height - newTargetY;
3949+ target.y = newTargetY;
3950+ } else {
3951+ target.y = target.y + target.height - minHeight;
3952+ target.height = minHeight;
3953+ }
3954+
3955+ } else if (d.bottomBorder) {
3956+ if (d.startHeight + deltaY >= minHeight) {
3957+ target.height = d.startHeight + deltaY;
3958+ } else {
3959+ target.height = minHeight;
3960+ }
3961+ }
3962+ }
3963 }
3964
3965=== modified file 'src/ApplicationArguments.h'
3966--- src/ApplicationArguments.h 2015-09-14 09:11:08 +0000
3967+++ src/ApplicationArguments.h 2015-10-26 09:06:21 +0000
3968@@ -26,17 +26,25 @@
3969 class ApplicationArguments : public QObject
3970 {
3971 Q_OBJECT
3972- Q_PROPERTY(QString deviceName READ deviceName CONSTANT)
3973+ Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
3974 Q_PROPERTY(QString mode READ mode CONSTANT)
3975 public:
3976 ApplicationArguments(QObject *parent = nullptr);
3977
3978- void setDeviceName(const QString &deviceName) { m_deviceName = deviceName; }
3979+ void setDeviceName(const QString &deviceName) {
3980+ if (deviceName != m_deviceName) {
3981+ m_deviceName = deviceName;
3982+ Q_EMIT deviceNameChanged(m_deviceName);
3983+ }
3984+ }
3985 QString deviceName() const { return m_deviceName; }
3986
3987 void setMode(const QString &mode) { m_mode = mode; }
3988 QString mode() const { return m_mode; }
3989
3990+Q_SIGNALS:
3991+ void deviceNameChanged(const QString&);
3992+
3993 private:
3994 QString m_deviceName;
3995 QString m_mode;
3996
3997=== modified file 'src/CMakeLists.txt'
3998--- src/CMakeLists.txt 2015-06-24 11:41:09 +0000
3999+++ src/CMakeLists.txt 2015-10-26 09:06:21 +0000
4000@@ -21,6 +21,9 @@
4001 main.cpp
4002 MouseTouchAdaptor.cpp
4003 CachingNetworkManagerFactory.cpp
4004+ SecondaryWindow.cpp
4005+ ShellApplication.cpp
4006+ ShellView.cpp
4007 UnityCommandLineParser.cpp
4008 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
4009 )
4010
4011=== added file 'src/SecondaryWindow.cpp'
4012--- src/SecondaryWindow.cpp 1970-01-01 00:00:00 +0000
4013+++ src/SecondaryWindow.cpp 2015-10-26 09:06:21 +0000
4014@@ -0,0 +1,31 @@
4015+/*
4016+ * Copyright (C) 2015 Canonical, Ltd.
4017+ *
4018+ * This program is free software; you can redistribute it and/or modify
4019+ * it under the terms of the GNU General Public License as published by
4020+ * the Free Software Foundation; version 3.
4021+ *
4022+ * This program is distributed in the hope that it will be useful,
4023+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4024+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4025+ * GNU General Public License for more details.
4026+ *
4027+ * You should have received a copy of the GNU General Public License
4028+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4029+ */
4030+
4031+#include "SecondaryWindow.h"
4032+
4033+// local
4034+#include <paths.h>
4035+
4036+SecondaryWindow::SecondaryWindow(QQmlEngine *engine)
4037+ : QQuickView(engine, nullptr)
4038+{
4039+ setResizeMode(QQuickView::SizeRootObjectToView);
4040+ setColor("black");
4041+ setTitle(QStringLiteral("Unity8 Shell - Secondary Screen"));
4042+
4043+ QUrl source(::qmlDirectory() + "/DisabledScreenNotice.qml");
4044+ setSource(source);
4045+}
4046
4047=== added file 'src/SecondaryWindow.h'
4048--- src/SecondaryWindow.h 1970-01-01 00:00:00 +0000
4049+++ src/SecondaryWindow.h 2015-10-26 09:06:21 +0000
4050@@ -0,0 +1,30 @@
4051+/*
4052+ * Copyright (C) 2015 Canonical, Ltd.
4053+ *
4054+ * This program is free software; you can redistribute it and/or modify
4055+ * it under the terms of the GNU General Public License as published by
4056+ * the Free Software Foundation; version 3.
4057+ *
4058+ * This program is distributed in the hope that it will be useful,
4059+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4060+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4061+ * GNU General Public License for more details.
4062+ *
4063+ * You should have received a copy of the GNU General Public License
4064+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4065+ */
4066+
4067+#ifndef UNITY_SECONDARY_WINDOW_H
4068+#define UNITY_SECONDARY_WINDOW_H
4069+
4070+#include <QQuickView>
4071+
4072+class SecondaryWindow : public QQuickView
4073+{
4074+ Q_OBJECT
4075+
4076+public:
4077+ SecondaryWindow(QQmlEngine *engine);
4078+};
4079+
4080+#endif // UNITY_SECONDARY_WINDOW_H
4081
4082=== added file 'src/ShellApplication.cpp'
4083--- src/ShellApplication.cpp 1970-01-01 00:00:00 +0000
4084+++ src/ShellApplication.cpp 2015-10-26 09:06:21 +0000
4085@@ -0,0 +1,197 @@
4086+/*
4087+ * Copyright (C) 2015 Canonical, Ltd.
4088+ *
4089+ * This program is free software; you can redistribute it and/or modify
4090+ * it under the terms of the GNU General Public License as published by
4091+ * the Free Software Foundation; version 3.
4092+ *
4093+ * This program is distributed in the hope that it will be useful,
4094+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4095+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4096+ * GNU General Public License for more details.
4097+ *
4098+ * You should have received a copy of the GNU General Public License
4099+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4100+ */
4101+
4102+#include "ShellApplication.h"
4103+
4104+// Qt
4105+#include <QLibrary>
4106+#include <QScreen>
4107+
4108+#include <libintl.h>
4109+
4110+// libandroid-properties
4111+#include <hybris/properties/properties.h>
4112+
4113+// local
4114+#include <paths.h>
4115+#include "CachingNetworkManagerFactory.h"
4116+#include "MouseTouchAdaptor.h"
4117+#include "UnityCommandLineParser.h"
4118+
4119+ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
4120+ : QGuiApplication(argc, argv)
4121+ , m_shellView(nullptr)
4122+ , m_secondaryWindow(nullptr)
4123+ , m_mouseTouchAdaptor(nullptr)
4124+ , m_qmlEngine(nullptr)
4125+{
4126+
4127+ setApplicationName(QStringLiteral("unity8"));
4128+
4129+ connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
4130+
4131+ setupQmlEngine(isMirServer);
4132+
4133+ UnityCommandLineParser parser(*this);
4134+
4135+ if (!parser.deviceName().isEmpty()) {
4136+ m_deviceName = parser.deviceName();
4137+ } else {
4138+ char buffer[200];
4139+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
4140+ m_deviceName = QString(buffer);
4141+ }
4142+ m_qmlArgs.setDeviceName(m_deviceName);
4143+
4144+ m_qmlArgs.setMode(parser.mode());
4145+
4146+ // The testability driver is only loaded by QApplication but not by QGuiApplication.
4147+ // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
4148+ if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
4149+ QLibrary testLib(QStringLiteral("qttestability"));
4150+ if (testLib.load()) {
4151+ typedef void (*TasInitialize)(void);
4152+ TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
4153+ if (initFunction) {
4154+ initFunction();
4155+ } else {
4156+ qCritical("Library qttestability resolve failed!");
4157+ }
4158+ } else {
4159+ qCritical("Library qttestability load failed!");
4160+ }
4161+ }
4162+
4163+ bindtextdomain("unity8", translationDirectory().toUtf8().data());
4164+ textdomain("unity8");
4165+
4166+ m_shellView = new ShellView(m_qmlEngine, &m_qmlArgs);
4167+
4168+ if (parser.windowGeometry().isValid()) {
4169+ m_shellView->setWidth(parser.windowGeometry().width());
4170+ m_shellView->setHeight(parser.windowGeometry().height());
4171+ }
4172+
4173+ if (parser.hasFrameless()) {
4174+ m_shellView->setFlags(Qt::FramelessWindowHint);
4175+ }
4176+
4177+ // You will need this if you want to interact with touch-only components using a mouse
4178+ // Needed only when manually testing on a desktop.
4179+ if (parser.hasMouseToTouch()) {
4180+ m_mouseTouchAdaptor = MouseTouchAdaptor::instance();
4181+ }
4182+
4183+
4184+ // Some hard-coded policy for now.
4185+ // NB: We don't support more than two screens at the moment
4186+ //
4187+ // TODO: Support an arbitrary number of screens and different policies
4188+ // (eg cloned desktop, several desktops, etc)
4189+ if (isMirServer && screens().count() == 2) {
4190+ m_shellView->setScreen(screens().at(1));
4191+ m_qmlArgs.setDeviceName("desktop");
4192+
4193+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
4194+ m_secondaryWindow->setScreen(screens().at(0));
4195+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
4196+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
4197+ m_secondaryWindow->setVisible(true);
4198+ }
4199+
4200+ if (isMirServer || parser.hasFullscreen()) {
4201+ m_shellView->showFullScreen();
4202+ } else {
4203+ m_shellView->show();
4204+ }
4205+}
4206+
4207+ShellApplication::~ShellApplication()
4208+{
4209+ destroyResources();
4210+}
4211+
4212+void ShellApplication::destroyResources()
4213+{
4214+ // Deletion order is important. Don't use QScopedPointers and the like
4215+ // Otherwise the process will hang on shutdown (bug somewhere I guess).
4216+ delete m_shellView;
4217+ m_shellView = nullptr;
4218+
4219+ delete m_secondaryWindow;
4220+ m_secondaryWindow = nullptr;
4221+
4222+ delete m_mouseTouchAdaptor;
4223+ m_mouseTouchAdaptor = nullptr;
4224+
4225+ delete m_qmlEngine;
4226+ m_qmlEngine = nullptr;
4227+}
4228+
4229+void ShellApplication::setupQmlEngine(bool isMirServer)
4230+{
4231+ m_qmlEngine = new QQmlEngine(this);
4232+
4233+ m_qmlEngine->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
4234+
4235+ prependImportPaths(m_qmlEngine, ::overrideImportPaths());
4236+ if (!isMirServer) {
4237+ prependImportPaths(m_qmlEngine, ::nonMirImportPaths());
4238+ }
4239+ appendImportPaths(m_qmlEngine, ::fallbackImportPaths());
4240+
4241+ m_qmlEngine->setNetworkAccessManagerFactory(new CachingNetworkManagerFactory);
4242+
4243+ QObject::connect(m_qmlEngine, &QQmlEngine::quit, this, &QGuiApplication::quit);
4244+}
4245+
4246+void ShellApplication::onScreenAdded(QScreen * /*screen*/)
4247+{
4248+ // TODO: Support an arbitrary number of screens and different policies
4249+ // (eg cloned desktop, several desktops, etc)
4250+ if (screens().count() == 2) {
4251+ m_shellView->setScreen(screens().at(1));
4252+ m_qmlArgs.setDeviceName("desktop");
4253+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
4254+ // its backing QPlatformWindow recreated). So lets refocus it.
4255+ m_shellView->requestActivate();
4256+
4257+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
4258+ m_secondaryWindow->setScreen(screens().at(0));
4259+
4260+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
4261+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
4262+ m_secondaryWindow->setVisible(true);
4263+ }
4264+}
4265+
4266+void ShellApplication::onScreenAboutToBeRemoved(QScreen *screen)
4267+{
4268+ // TODO: Support an arbitrary number of screens and different policies
4269+ // (eg cloned desktop, several desktops, etc)
4270+ if (screen == m_shellView->screen()) {
4271+ Q_ASSERT(screens().count() > 1);
4272+ Q_ASSERT(screens().at(0) != screen);
4273+ Q_ASSERT(m_secondaryWindow);
4274+ delete m_secondaryWindow;
4275+ m_secondaryWindow = nullptr;
4276+ m_shellView->setScreen(screens().first());
4277+ m_qmlArgs.setDeviceName(m_deviceName);
4278+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
4279+ // its backing QPlatformWindow recreated). So lets refocus it.
4280+ m_shellView->requestActivate();
4281+ }
4282+}
4283
4284=== added file 'src/ShellApplication.h'
4285--- src/ShellApplication.h 1970-01-01 00:00:00 +0000
4286+++ src/ShellApplication.h 2015-10-26 09:06:21 +0000
4287@@ -0,0 +1,55 @@
4288+/*
4289+ * Copyright (C) 2015 Canonical, Ltd.
4290+ *
4291+ * This program is free software; you can redistribute it and/or modify
4292+ * it under the terms of the GNU General Public License as published by
4293+ * the Free Software Foundation; version 3.
4294+ *
4295+ * This program is distributed in the hope that it will be useful,
4296+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4297+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4298+ * GNU General Public License for more details.
4299+ *
4300+ * You should have received a copy of the GNU General Public License
4301+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4302+ */
4303+
4304+#ifndef SHELLAPPLICATION_H
4305+#define SHELLAPPLICATION_H
4306+
4307+#include <QGuiApplication>
4308+#include <QQmlEngine>
4309+#include <QQuickView>
4310+#include <QScopedPointer>
4311+
4312+#include "ApplicationArguments.h"
4313+#include "MouseTouchAdaptor.h"
4314+#include "SecondaryWindow.h"
4315+#include "ShellView.h"
4316+
4317+class ShellApplication : public QGuiApplication
4318+{
4319+ Q_OBJECT
4320+public:
4321+ ShellApplication(int & argc, char ** argv, bool isMirServer);
4322+ virtual ~ShellApplication();
4323+
4324+ void destroyResources();
4325+public Q_SLOTS:
4326+ // called by qtmir
4327+ void onScreenAboutToBeRemoved(QScreen *screen);
4328+
4329+private Q_SLOTS:
4330+ void onScreenAdded(QScreen*);
4331+
4332+private:
4333+ void setupQmlEngine(bool isMirServer);
4334+ QString m_deviceName;
4335+ ApplicationArguments m_qmlArgs;
4336+ ShellView *m_shellView;
4337+ SecondaryWindow *m_secondaryWindow;
4338+ MouseTouchAdaptor *m_mouseTouchAdaptor;
4339+ QQmlEngine *m_qmlEngine;
4340+};
4341+
4342+#endif // SHELLAPPLICATION_H
4343
4344=== added file 'src/ShellView.cpp'
4345--- src/ShellView.cpp 1970-01-01 00:00:00 +0000
4346+++ src/ShellView.cpp 2015-10-26 09:06:21 +0000
4347@@ -0,0 +1,59 @@
4348+/*
4349+ * Copyright (C) 2015 Canonical, Ltd.
4350+ *
4351+ * This program is free software; you can redistribute it and/or modify
4352+ * it under the terms of the GNU General Public License as published by
4353+ * the Free Software Foundation; version 3.
4354+ *
4355+ * This program is distributed in the hope that it will be useful,
4356+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4357+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4358+ * GNU General Public License for more details.
4359+ *
4360+ * You should have received a copy of the GNU General Public License
4361+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4362+ */
4363+
4364+#include "ShellView.h"
4365+
4366+// Qt
4367+#include <QQmlContext>
4368+#include <QQuickItem>
4369+
4370+// local
4371+#include <paths.h>
4372+
4373+ShellView::ShellView(QQmlEngine *engine, QObject *qmlArgs)
4374+ : QQuickView(engine, nullptr)
4375+{
4376+ setResizeMode(QQuickView::SizeRootObjectToView);
4377+ setColor("black");
4378+ setTitle(QStringLiteral("Unity8"));
4379+
4380+ rootContext()->setContextProperty(QStringLiteral("applicationArguments"), qmlArgs);
4381+
4382+ QUrl source(::qmlDirectory() + "/OrientedShell.qml");
4383+ setSource(source);
4384+
4385+ connect(this, &QWindow::widthChanged, this, &ShellView::onWidthChanged);
4386+ connect(this, &QWindow::heightChanged, this, &ShellView::onHeightChanged);
4387+}
4388+
4389+void ShellView::onWidthChanged(int w)
4390+{
4391+ // For good measure in case SizeRootObjectToView doesn't fulfill its promise.
4392+ //
4393+ // There's at least one situation that's know to leave the root object with an outdated size.
4394+ // (really looks like Qt bug)
4395+ // Happens when starting unity8 with an external monitor already connected.
4396+ // The QResizeEvent we get still has the size of the first screen and since the resize move is triggered
4397+ // from the resize event handler, the root item doesn't get resized.
4398+ // TODO: Confirm the Qt bug and submit a patch upstream
4399+ rootObject()->setWidth(w);
4400+}
4401+
4402+void ShellView::onHeightChanged(int h)
4403+{
4404+ // See comment in ShellView::onWidthChanged()
4405+ rootObject()->setHeight(h);
4406+}
4407
4408=== added file 'src/ShellView.h'
4409--- src/ShellView.h 1970-01-01 00:00:00 +0000
4410+++ src/ShellView.h 2015-10-26 09:06:21 +0000
4411@@ -0,0 +1,34 @@
4412+/*
4413+ * Copyright (C) 2015 Canonical, Ltd.
4414+ *
4415+ * This program is free software; you can redistribute it and/or modify
4416+ * it under the terms of the GNU General Public License as published by
4417+ * the Free Software Foundation; version 3.
4418+ *
4419+ * This program is distributed in the hope that it will be useful,
4420+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4421+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4422+ * GNU General Public License for more details.
4423+ *
4424+ * You should have received a copy of the GNU General Public License
4425+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4426+ */
4427+
4428+#ifndef UNITY_SHELL_VIEW_H
4429+#define UNITY_SHELL_VIEW_H
4430+
4431+#include <QQuickView>
4432+
4433+class ShellView : public QQuickView
4434+{
4435+ Q_OBJECT
4436+
4437+public:
4438+ ShellView(QQmlEngine *engine, QObject *qmlArgs);
4439+
4440+private Q_SLOTS:
4441+ void onWidthChanged(int);
4442+ void onHeightChanged(int);
4443+};
4444+
4445+#endif // UNITY_SHELL_VIEW_H
4446
4447=== modified file 'src/main.cpp'
4448--- src/main.cpp 2015-09-23 15:14:01 +0000
4449+++ src/main.cpp 2015-10-26 09:06:21 +0000
4450@@ -14,26 +14,8 @@
4451 * along with this program. If not, see <http://www.gnu.org/licenses/>.
4452 */
4453
4454-// Qt
4455-#include <QCommandLineParser>
4456-#include <QtQuick/QQuickView>
4457-#include <QtGui/QGuiApplication>
4458-#include <QtQml/QQmlEngine>
4459-#include <QtQml/QQmlContext>
4460-#include <QLibrary>
4461-#include <QDebug>
4462-#include <csignal>
4463-#include <libintl.h>
4464-
4465-// libandroid-properties
4466-#include <hybris/properties/properties.h>
4467-
4468 // local
4469-#include <paths.h>
4470-#include "MouseTouchAdaptor.h"
4471-#include "ApplicationArguments.h"
4472-#include "CachingNetworkManagerFactory.h"
4473-#include "UnityCommandLineParser.h"
4474+#include "ShellApplication.h"
4475
4476 int main(int argc, const char *argv[])
4477 {
4478@@ -43,91 +25,12 @@
4479 isMirServer = true;
4480 }
4481
4482- QGuiApplication::setApplicationName(QStringLiteral("unity8"));
4483- QGuiApplication *application;
4484-
4485- application = new QGuiApplication(argc, (char**)argv);
4486-
4487- UnityCommandLineParser parser(*application);
4488-
4489- ApplicationArguments qmlArgs;
4490-
4491- if (!parser.deviceName().isEmpty()) {
4492- qmlArgs.setDeviceName(parser.deviceName());
4493- } else {
4494- char buffer[200];
4495- property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
4496- qmlArgs.setDeviceName(QString(buffer));
4497- }
4498-
4499- qmlArgs.setMode(parser.mode());
4500-
4501- // The testability driver is only loaded by QApplication but not by QGuiApplication.
4502- // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
4503- if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
4504- QLibrary testLib(QStringLiteral("qttestability"));
4505- if (testLib.load()) {
4506- typedef void (*TasInitialize)(void);
4507- TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
4508- if (initFunction) {
4509- initFunction();
4510- } else {
4511- qCritical("Library qttestability resolve failed!");
4512- }
4513- } else {
4514- qCritical("Library qttestability load failed!");
4515- }
4516- }
4517-
4518- bindtextdomain("unity8", translationDirectory().toUtf8().data());
4519- textdomain("unity8");
4520-
4521- QQuickView* view = new QQuickView();
4522- view->setResizeMode(QQuickView::SizeRootObjectToView);
4523- view->setColor("black");
4524- view->setTitle(QStringLiteral("Unity8 Shell"));
4525-
4526- if (parser.windowGeometry().isValid()) {
4527- view->setWidth(parser.windowGeometry().width());
4528- view->setHeight(parser.windowGeometry().height());
4529- }
4530-
4531- view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
4532- view->rootContext()->setContextProperty(QStringLiteral("applicationArguments"), &qmlArgs);
4533- if (parser.hasFrameless()) {
4534- view->setFlags(Qt::FramelessWindowHint);
4535- }
4536-
4537- // You will need this if you want to interact with touch-only components using a mouse
4538- // Needed only when manually testing on a desktop.
4539- MouseTouchAdaptor *mouseTouchAdaptor = 0;
4540- if (parser.hasMouseToTouch()) {
4541- mouseTouchAdaptor = MouseTouchAdaptor::instance();
4542- }
4543-
4544- QUrl source(::qmlDirectory() + "/OrientedShell.qml");
4545- prependImportPaths(view->engine(), ::overrideImportPaths());
4546- if (!isMirServer) {
4547- prependImportPaths(view->engine(), ::nonMirImportPaths());
4548- }
4549- appendImportPaths(view->engine(), ::fallbackImportPaths());
4550-
4551- CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
4552- view->engine()->setNetworkAccessManagerFactory(managerFactory);
4553-
4554- view->setSource(source);
4555- QObject::connect(view->engine(), &QQmlEngine::quit, application, &QGuiApplication::quit);
4556-
4557- if (isMirServer || parser.hasFullscreen()) {
4558- view->showFullScreen();
4559- } else {
4560- view->show();
4561- }
4562+ ShellApplication *application = new ShellApplication(argc, (char**)argv, isMirServer);
4563
4564 int result = application->exec();
4565
4566- delete view;
4567- delete mouseTouchAdaptor;
4568+ application->destroyResources();
4569+
4570 delete application;
4571
4572 return result;
4573
4574=== modified file 'tests/autopilot/unity8/fixture_setup.py'
4575--- tests/autopilot/unity8/fixture_setup.py 2015-09-21 12:50:10 +0000
4576+++ tests/autopilot/unity8/fixture_setup.py 2015-10-26 09:06:21 +0000
4577@@ -133,7 +133,7 @@
4578 args = [binary_arg] + env_args
4579 self.unity_proxy = process_helpers.restart_unity_with_testability(
4580 *args)
4581- self.main_win = self.unity_proxy.select_single(shell.QQuickView)
4582+ self.main_win = self.unity_proxy.select_single(shell.ShellView)
4583
4584 def _create_sensors(self):
4585 # Wait for unity to start running.
4586
4587=== modified file 'tests/autopilot/unity8/greeter/tests/__init__.py'
4588--- tests/autopilot/unity8/greeter/tests/__init__.py 2015-04-29 19:21:18 +0000
4589+++ tests/autopilot/unity8/greeter/tests/__init__.py 2015-10-26 09:06:21 +0000
4590@@ -24,5 +24,5 @@
4591 class GreeterTestCase(UnityTestCase):
4592 def get_shell(self, unity_proxy):
4593 main_window = (
4594- unity_proxy.select_single(shell.QQuickView))
4595+ unity_proxy.select_single(shell.ShellView))
4596 return main_window.select_single('Shell')
4597
4598=== modified file 'tests/autopilot/unity8/launcher.py'
4599--- tests/autopilot/unity8/launcher.py 2015-04-29 19:21:18 +0000
4600+++ tests/autopilot/unity8/launcher.py 2015-10-26 09:06:21 +0000
4601@@ -42,7 +42,7 @@
4602 logger.debug('The launcher is already opened.')
4603
4604 def _swipe_to_show_launcher(self):
4605- view = self.get_root_instance().select_single('QQuickView')
4606+ view = self.get_root_instance().select_single('ShellView')
4607 start_y = stop_y = view.y + view.height // 2
4608
4609 start_x = view.x + 1
4610
4611=== modified file 'tests/autopilot/unity8/settings_wizard/__init__.py'
4612--- tests/autopilot/unity8/settings_wizard/__init__.py 2015-05-21 16:52:05 +0000
4613+++ tests/autopilot/unity8/settings_wizard/__init__.py 2015-10-26 09:06:21 +0000
4614@@ -375,12 +375,17 @@
4615 wizard = get_wizard(self)
4616 next_page = wizard.get_current_page()
4617 locationPageEnabled = True
4618+ reportingPageEnabled = True
4619 if next_page.objectName == 'locationPage':
4620 next_page = wizard.get_location_page()
4621 else:
4622 locationPageEnabled = False
4623- next_page = wizard.get_reporting_page()
4624- return locationPageEnabled, next_page
4625+ if next_page.objectName == 'reportingPage':
4626+ next_page = wizard.get_reporting_page()
4627+ else:
4628+ reportingPageEnabled = False
4629+ next_page = wizard.get_finished_page()
4630+ return locationPageEnabled, reportingPageEnabled, next_page
4631
4632 def _get_notification(self, unity):
4633 logger.info('Waiting longer for notification object')
4634
4635=== modified file 'tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py'
4636--- tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py 2015-05-21 16:37:36 +0000
4637+++ tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py 2015-10-26 09:06:21 +0000
4638@@ -80,14 +80,19 @@
4639 password_page = next_page
4640 wifi_connect_page = self._test_password_page(password_page)
4641
4642- locationPageEnabled, next_page = self._test_wifi_connect_page(
4643- wifi_connect_page)
4644+ reporting_page = None
4645+ locationPageEnabled, reportingPageEnabled, next_page = self._test_wifi_connect_page(wifi_connect_page)
4646 if locationPageEnabled:
4647 location_page = next_page
4648- reporting_page = self._test_location_page(location_page)
4649+ if reportingPageEnabled:
4650+ reporting_page = self._test_location_page(location_page)
4651+ else:
4652+ finish_page = next_page
4653+
4654+ if reporting_page is not None:
4655+ finish_page = self._test_reporting_page(reporting_page)
4656 else:
4657- reporting_page = next_page
4658- finish_page = self._test_reporting_page(reporting_page)
4659+ finish_page = next_page
4660
4661 finish_page.finish()
4662 self.assertFalse(
4663
4664=== modified file 'tests/autopilot/unity8/shell/__init__.py'
4665--- tests/autopilot/unity8/shell/__init__.py 2015-06-22 15:47:34 +0000
4666+++ tests/autopilot/unity8/shell/__init__.py 2015-10-26 09:06:21 +0000
4667@@ -93,7 +93,7 @@
4668 return _urgency_enums.get(urgency.upper())
4669
4670
4671-class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
4672+class ShellView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
4673 """An helper class that makes it easy to interact with the shell"""
4674
4675 def get_greeter(self):
4676
4677=== modified file 'tests/autopilot/unity8/shell/tests/__init__.py'
4678--- tests/autopilot/unity8/shell/tests/__init__.py 2015-09-21 12:50:10 +0000
4679+++ tests/autopilot/unity8/shell/tests/__init__.py 2015-10-26 09:06:21 +0000
4680@@ -297,7 +297,7 @@
4681
4682 @property
4683 def main_window(self):
4684- return self._proxy.select_single(shell.QQuickView)
4685+ return self._proxy.select_single(shell.ShellView)
4686
4687
4688 class DashBaseTestCase(AutopilotTestCase):
4689
4690=== modified file 'tests/mocks/CMakeLists.txt'
4691--- tests/mocks/CMakeLists.txt 2015-08-06 22:45:56 +0000
4692+++ tests/mocks/CMakeLists.txt 2015-10-26 09:06:21 +0000
4693@@ -29,6 +29,7 @@
4694 endmacro()
4695
4696 add_subdirectory(AccountsService)
4697+add_subdirectory(Cursor)
4698 add_subdirectory(GSettings.1.0)
4699 add_subdirectory(indicator-service)
4700 add_subdirectory(libusermetrics)
4701
4702=== added directory 'tests/mocks/Cursor'
4703=== added file 'tests/mocks/Cursor/CMakeLists.txt'
4704--- tests/mocks/Cursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
4705+++ tests/mocks/Cursor/CMakeLists.txt 2015-10-26 09:06:21 +0000
4706@@ -0,0 +1,1 @@
4707+add_unity8_mock(Cursor 1.0 Cursor PREFIX mocks)
4708
4709=== added file 'tests/mocks/Cursor/Cursor.qml'
4710--- tests/mocks/Cursor/Cursor.qml 1970-01-01 00:00:00 +0000
4711+++ tests/mocks/Cursor/Cursor.qml 2015-10-26 09:06:21 +0000
4712@@ -0,0 +1,20 @@
4713+/*
4714+ * Copyright (C) 2015 Canonical, Ltd.
4715+ *
4716+ * This program is free software; you can redistribute it and/or modify
4717+ * it under the terms of the GNU General Public License as published by
4718+ * the Free Software Foundation; version 3.
4719+ *
4720+ * This program is distributed in the hope that it will be useful,
4721+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4722+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4723+ * GNU General Public License for more details.
4724+ *
4725+ * You should have received a copy of the GNU General Public License
4726+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4727+ */
4728+
4729+import QtQuick 2.4
4730+
4731+Item {
4732+}
4733
4734=== added file 'tests/mocks/Cursor/qmldir'
4735--- tests/mocks/Cursor/qmldir 1970-01-01 00:00:00 +0000
4736+++ tests/mocks/Cursor/qmldir 2015-10-26 09:06:21 +0000
4737@@ -0,0 +1,2 @@
4738+module Cursor
4739+Cursor 1.0 Cursor.qml
4740
4741=== modified file 'tests/mocks/Unity/Launcher/MockQuickListModel.cpp'
4742--- tests/mocks/Unity/Launcher/MockQuickListModel.cpp 2013-08-19 15:10:58 +0000
4743+++ tests/mocks/Unity/Launcher/MockQuickListModel.cpp 2015-10-26 09:06:21 +0000
4744@@ -32,7 +32,7 @@
4745 switch (role)
4746 {
4747 case RoleLabel:
4748- return QLatin1String("test menu entry ") + QString::number(index.row());
4749+ return QString(QLatin1String("test menu entry ") + QString::number(index.row()));
4750 case RoleIcon:
4751 return QLatin1String("copy.png");
4752 case RoleClickable:
4753
4754=== modified file 'tests/mocks/Unity/fake_resultsmodel.cpp'
4755--- tests/mocks/Unity/fake_resultsmodel.cpp 2015-09-22 10:44:21 +0000
4756+++ tests/mocks/Unity/fake_resultsmodel.cpp 2015-10-26 09:06:21 +0000
4757@@ -78,7 +78,7 @@
4758 case RoleTitle:
4759 return QString("Title.%1.%2").arg(m_categoryId).arg(index.row());
4760 case RoleArt:
4761- return qmlDirectory() + "/graphics/applicationIcons/dash.png";
4762+ return QString(qmlDirectory() + "/graphics/applicationIcons/dash.png");
4763 case RoleSubtitle:
4764 return QString("Subtitle.%1.%2").arg(m_categoryId).arg(index.row());
4765 case RoleMascot:
4766
4767=== modified file 'tests/mocks/Unity/fake_scopesoverview.cpp'
4768--- tests/mocks/Unity/fake_scopesoverview.cpp 2015-09-22 10:44:21 +0000
4769+++ tests/mocks/Unity/fake_scopesoverview.cpp 2015-10-26 09:06:21 +0000
4770@@ -308,7 +308,7 @@
4771 case RoleSubtitle:
4772 return scope && scope->name() == "Videos this is long ab cd ef gh ij kl" ? "tube, movies, cinema, pictures, art, moving images, magic in a box" : QString();
4773 case RoleArt:
4774- return qmlDirectory() + "/graphics/applicationIcons/dash.png";
4775+ return QString(qmlDirectory() + "/graphics/applicationIcons/dash.png");
4776 case RoleMascot:
4777 case RoleEmblem:
4778 case RoleSummary:
4779
4780=== modified file 'tests/mocks/Utils/CMakeLists.txt'
4781--- tests/mocks/Utils/CMakeLists.txt 2015-09-17 12:25:29 +0000
4782+++ tests/mocks/Utils/CMakeLists.txt 2015-10-26 09:06:21 +0000
4783@@ -13,8 +13,6 @@
4784 ${CMAKE_SOURCE_DIR}/plugins/Utils/activefocuslogger.cpp
4785 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
4786 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
4787- ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
4788- ${CMAKE_SOURCE_DIR}/plugins/Utils/timeformatter.cpp
4789 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
4790 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
4791 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
4792
4793=== modified file 'tests/mocks/Utils/Utils.qmltypes'
4794--- tests/mocks/Utils/Utils.qmltypes 2015-03-04 13:04:58 +0000
4795+++ tests/mocks/Utils/Utils.qmltypes 2015-10-26 09:06:21 +0000
4796@@ -4,10 +4,19 @@
4797 // It is used for QML tooling purposes only.
4798 //
4799 // This file was auto-generated by:
4800-// 'qmlplugindump -notrelocatable Utils 0.1 plugins'
4801+// 'qmlplugindump -notrelocatable Utils 0.1 tests/mocks'
4802
4803 Module {
4804 Component {
4805+ name: "Constants"
4806+ prototype: "QObject"
4807+ exports: ["Utils/Constants 0.1"]
4808+ isCreatable: false
4809+ isSingleton: true
4810+ exportMetaObjectRevisions: [0]
4811+ Property { name: "indicatorValueTimeout"; type: "int"; isReadonly: true }
4812+ }
4813+ Component {
4814 name: "EasingCurve"
4815 prototype: "QObject"
4816 exports: ["Utils/EasingCurve 0.1"]
4817@@ -18,6 +27,22 @@
4818 Property { name: "value"; type: "double"; isReadonly: true }
4819 }
4820 Component {
4821+ name: "InputWatcher"
4822+ prototype: "QObject"
4823+ exports: ["Utils/InputWatcher 0.1"]
4824+ exportMetaObjectRevisions: [0]
4825+ Property { name: "target"; type: "QObject"; isPointer: true }
4826+ Property { name: "targetPressed"; type: "bool"; isReadonly: true }
4827+ Signal {
4828+ name: "targetChanged"
4829+ Parameter { name: "value"; type: "QObject"; isPointer: true }
4830+ }
4831+ Signal {
4832+ name: "targetPressedChanged"
4833+ Parameter { name: "value"; type: "bool" }
4834+ }
4835+ }
4836+ Component {
4837 name: "QAbstractProxyModel"
4838 prototype: "QAbstractItemModel"
4839 Property { name: "sourceModel"; type: "QAbstractItemModel"; isPointer: true }
4840@@ -60,33 +85,6 @@
4841 Method { name: "invalidate" }
4842 }
4843 Component {
4844- name: "RelativeTimeFormatter"
4845- prototype: "TimeFormatter"
4846- exports: ["Utils/RelativeTimeFormatter 0.1"]
4847- exportMetaObjectRevisions: [0]
4848- }
4849- Component {
4850- name: "TimeFormatter"
4851- prototype: "QObject"
4852- exports: ["Utils/GDateTimeFormatter 0.1", "Utils/TimeFormatter 0.1"]
4853- exportMetaObjectRevisions: [0, 0]
4854- Property { name: "format"; type: "string" }
4855- Property { name: "timeString"; type: "string"; isReadonly: true }
4856- Property { name: "time"; type: "qlonglong" }
4857- Signal {
4858- name: "formatChanged"
4859- Parameter { name: "format"; type: "string" }
4860- }
4861- Signal {
4862- name: "timeStringChanged"
4863- Parameter { name: "timeString"; type: "string" }
4864- }
4865- Signal {
4866- name: "timeChanged"
4867- Parameter { name: "time"; type: "qlonglong" }
4868- }
4869- }
4870- Component {
4871 name: "UnityMenuModelPaths"
4872 prototype: "QObject"
4873 exports: ["Utils/UnityMenuModelPaths 0.1"]
4874@@ -160,6 +158,11 @@
4875 isCreatable: false
4876 isSingleton: true
4877 exportMetaObjectRevisions: [0]
4878+ Property { name: "geometry"; type: "QVariantMap" }
4879+ Signal {
4880+ name: "geometryChanged"
4881+ Parameter { name: "geometry"; type: "QVariantMap" }
4882+ }
4883 Method {
4884 name: "saveGeometry"
4885 Parameter { name: "windowId"; type: "string" }
4886
4887=== modified file 'tests/mocks/Utils/plugin.cpp'
4888--- tests/mocks/Utils/plugin.cpp 2015-09-17 12:25:29 +0000
4889+++ tests/mocks/Utils/plugin.cpp 2015-10-26 09:06:21 +0000
4890@@ -23,14 +23,6 @@
4891
4892 // local
4893 #include "plugin.h"
4894-#include "inputwatcher.h"
4895-#include "qlimitproxymodelqml.h"
4896-#include "unitysortfilterproxymodelqml.h"
4897-#include "relativetimeformatter.h"
4898-#include "timeformatter.h"
4899-#include "unitymenumodelpaths.h"
4900-#include "windowkeysfilter.h"
4901-#include "easingcurve.h"
4902 #include "windowstatestorage.h"
4903 #include "constants.h"
4904
4905@@ -39,8 +31,6 @@
4906 #include <inputwatcher.h>
4907 #include <qlimitproxymodelqml.h>
4908 #include <unitysortfilterproxymodelqml.h>
4909-#include <relativetimeformatter.h>
4910-#include <timeformatter.h>
4911 #include <unitymenumodelpaths.h>
4912 #include <windowkeysfilter.h>
4913 #include <windowscreenshotprovider.h>
4914@@ -67,11 +57,8 @@
4915 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
4916 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
4917 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
4918- qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
4919 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
4920- qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
4921 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
4922- qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
4923 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
4924 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
4925 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
4926
4927=== modified file 'tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp'
4928--- tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp 2015-07-20 15:06:46 +0000
4929+++ tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp 2015-10-26 09:06:21 +0000
4930@@ -55,6 +55,17 @@
4931 QVERIFY(!args.first().toString().isEmpty());
4932 }
4933
4934+ void testRotatedScreenshot()
4935+ {
4936+ QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
4937+ m_grabber->captureAndSave(90); // rotate by 90°
4938+ const QVariantList args = grabberSpy.takeFirst();
4939+ const QString filename = args.first().toString();
4940+ QVERIFY(!filename.isEmpty());
4941+ QImage img(filename);
4942+ QVERIFY(img.height() > img.width()); // verify that the image got rotated by 90° (height > width)
4943+ }
4944+
4945 private:
4946 QQuickView *m_view;
4947 ScreenGrabber *m_grabber = nullptr;
4948
4949=== modified file 'tests/plugins/ScreenGrabber/grabber.qml'
4950--- tests/plugins/ScreenGrabber/grabber.qml 2015-07-17 19:59:31 +0000
4951+++ tests/plugins/ScreenGrabber/grabber.qml 2015-10-26 09:06:21 +0000
4952@@ -20,7 +20,7 @@
4953 Rectangle {
4954 property var grabber: screenGrabber
4955
4956- width: 100
4957+ width: 101 // width intentionally bigger than height to test rotation
4958 height: 100
4959 color: "green"
4960
4961
4962=== modified file 'tests/plugins/Unity/Indicators/UnityMenuModelStackTest.cpp'
4963--- tests/plugins/Unity/Indicators/UnityMenuModelStackTest.cpp 2015-08-19 13:56:21 +0000
4964+++ tests/plugins/Unity/Indicators/UnityMenuModelStackTest.cpp 2015-10-26 09:06:21 +0000
4965@@ -43,6 +43,8 @@
4966 {
4967 delete m_model;
4968 m_model = nullptr;
4969+ // send deleteLaters to avoid leaks.
4970+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4971 }
4972
4973 void testHeadOnSetHead()
4974@@ -146,19 +148,10 @@
4975
4976 m_model->removeRow(removeIndex);
4977
4978- waitFor([&stack, resultCount]() { return stack.count() == resultCount; }, 1000);
4979 QCOMPARE(stack.count(), resultCount);
4980 }
4981
4982 private:
4983- bool waitFor(std::function<bool()> functor, int ms) {
4984-
4985- QElapsedTimer timer;
4986- timer.start();
4987- while(!functor() && timer.elapsed() < ms) { QTest::qWait(10); }
4988- return functor();
4989- }
4990-
4991 QVariant recuseAddMenu(int subMenuCount, int depth_remaining)
4992 {
4993 QVariantList rows;
4994
4995=== modified file 'tests/plugins/Utils/CMakeLists.txt'
4996--- tests/plugins/Utils/CMakeLists.txt 2015-04-30 07:56:43 +0000
4997+++ tests/plugins/Utils/CMakeLists.txt 2015-10-26 09:06:21 +0000
4998@@ -7,7 +7,6 @@
4999 foreach(util_test
5000 QLimitProxyModel
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches