Merge lp:~saviq/unity8/add-cursor-copyright into lp:unity8

Proposed by Michał Sawicz
Status: Merged
Approved by: Albert Astals Cid
Approved revision: 2008
Merged at revision: 2019
Proposed branch: lp:~saviq/unity8/add-cursor-copyright
Merge into: lp:unity8
Diff against target: 5811 lines (+3041/-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 (+28/-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:~saviq/unity8/add-cursor-copyright
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Albert Astals Cid (community) Approve
Review via email: mp+275692@code.launchpad.net

Commit message

Add missing copyright to Cursor.qml

To post a comment you must log in.
Revision history for this message
Albert Astals Cid (aacid) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
No, it's just copyright addition

 * Did CI run pass? If not, please explain why.
Doesn't matter since it's just copyright addition

 * Did you make sure that the branch does not contain spurious tags?
Yes

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +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 12:04:03 +0000
1329@@ -0,0 +1,28 @@
1330+/*
1331+ * Copyright (C) 2015 Canonical, Ltd.
1332+ *
1333+ * This program is free software; you can redistribute it and/or modify
1334+ * it under the terms of the GNU General Public License as published by
1335+ * the Free Software Foundation; version 3.
1336+ *
1337+ * This program is distributed in the hope that it will be useful,
1338+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1339+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1340+ * GNU General Public License for more details.
1341+ *
1342+ * You should have received a copy of the GNU General Public License
1343+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1344+ */
1345+
1346+import QtQuick 2.4
1347+import Cursor 1.0 // For MousePointer
1348+
1349+MousePointer {
1350+ id: mousePointer
1351+
1352+ Image {
1353+ x: -mousePointer.hotspotX
1354+ y: -mousePointer.hotspotY
1355+ source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
1356+ }
1357+}
1358
1359=== added file 'plugins/Cursor/CursorImageProvider.cpp'
1360--- plugins/Cursor/CursorImageProvider.cpp 1970-01-01 00:00:00 +0000
1361+++ plugins/Cursor/CursorImageProvider.cpp 2015-10-26 12:04:03 +0000
1362@@ -0,0 +1,191 @@
1363+/*
1364+ * Copyright (C) 2015 Canonical, Ltd.
1365+ *
1366+ * This program is free software: you can redistribute it and/or modify it under
1367+ * the terms of the GNU Lesser General Public License version 3, as published by
1368+ * the Free Software Foundation.
1369+ *
1370+ * This program is distributed in the hope that it will be useful, but WITHOUT
1371+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1372+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1373+ * Lesser General Public License for more details.
1374+ *
1375+ * You should have received a copy of the GNU Lesser General Public License
1376+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1377+ */
1378+
1379+#include "CursorImageProvider.h"
1380+
1381+#include <QDebug>
1382+#include <QFile>
1383+#include <QPainter>
1384+#include <QSvgRenderer>
1385+
1386+CursorImageProvider *CursorImageProvider::m_instance = nullptr;
1387+
1388+/////
1389+// BuiltInCursorImage
1390+
1391+BuiltInCursorImage::BuiltInCursorImage()
1392+{
1393+ const char *svgString =
1394+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
1395+ "<svg"
1396+ " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
1397+ " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
1398+ " xmlns:svg=\"http://www.w3.org/2000/svg\""
1399+ " xmlns=\"http://www.w3.org/2000/svg\""
1400+ " version=\"1.1\">"
1401+ " <path"
1402+ " 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\""
1403+ " 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\" />"
1404+ "</svg>";
1405+
1406+ qimage = QImage(20, 32, QImage::Format_ARGB32);
1407+ QPainter imagePainter(&qimage);
1408+
1409+ QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
1410+ svgRenderer->render(&imagePainter);
1411+ delete svgRenderer;
1412+}
1413+
1414+/////
1415+// XCursorImage
1416+
1417+XCursorImage::XCursorImage(const QString &theme, const QString &file)
1418+ : xcursorImages(nullptr)
1419+{
1420+ xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme), 32);
1421+ if (!xcursorImages) {
1422+ return;
1423+ }
1424+
1425+ bool loaded = false;
1426+ for (int i = 0; i < xcursorImages->nimage && !loaded; ++i) {
1427+ XcursorImage *xcursorImage = xcursorImages->images[i];
1428+ if (xcursorImage->size == 32) {
1429+
1430+ qimage = QImage((uchar*)xcursorImage->pixels,
1431+ xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32);
1432+
1433+ hotspot.setX(xcursorImage->xhot);
1434+ hotspot.setY(xcursorImage->yhot);
1435+
1436+ loaded = true;
1437+ }
1438+ }
1439+}
1440+
1441+XCursorImage::~XCursorImage()
1442+{
1443+ XcursorImagesDestroy(xcursorImages);
1444+}
1445+
1446+/////
1447+// CursorImageProvider
1448+
1449+CursorImageProvider::CursorImageProvider()
1450+ : QQuickImageProvider(QQuickImageProvider::Image)
1451+{
1452+ if (m_instance) {
1453+ qFatal("Cannot have multiple CursorImageProvider instances");
1454+ }
1455+ m_instance = this;
1456+}
1457+
1458+CursorImageProvider::~CursorImageProvider()
1459+{
1460+ {
1461+ QList< QMap<QString, CursorImage*> > cursorList = m_cursors.values();
1462+
1463+ for (int i = 0; i < cursorList.count(); ++i) {
1464+ QList<CursorImage*> cursorImageList = cursorList[i].values();
1465+ for (int j = 0; j < cursorImageList.count(); ++j) {
1466+ delete cursorImageList[j];
1467+ }
1468+ }
1469+ }
1470+
1471+ m_cursors.clear();
1472+ m_instance = nullptr;
1473+}
1474+
1475+QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSize *size, const QSize & /*requestedSize*/)
1476+{
1477+ CursorImage *cursorImage = fetchCursor(cursorThemeAndName);
1478+ size->setWidth(cursorImage->qimage.width());
1479+ size->setHeight(cursorImage->qimage.height());
1480+
1481+ return cursorImage->qimage;
1482+}
1483+
1484+QPoint CursorImageProvider::hotspot(const QString &themeName, const QString &cursorName)
1485+{
1486+ CursorImage *cursorImage = fetchCursor(themeName, cursorName);
1487+ if (cursorImage) {
1488+ return cursorImage->hotspot;
1489+ } else {
1490+ return QPoint(0,0);
1491+ }
1492+}
1493+
1494+CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
1495+{
1496+ QString themeName;
1497+ QString cursorName;
1498+ {
1499+ QStringList themeAndNameList = cursorThemeAndName.split("/");
1500+ if (themeAndNameList.size() != 2) {
1501+ return nullptr;
1502+ }
1503+ themeName = themeAndNameList[0];
1504+ cursorName = themeAndNameList[1];
1505+ }
1506+
1507+ return fetchCursor(themeName, cursorName);
1508+}
1509+
1510+CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName)
1511+{
1512+ CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName);
1513+
1514+ // Try some fallbacks
1515+ if (cursorImage->qimage.isNull()) {
1516+ if (cursorName == "ibeam") {
1517+ qDebug() << "CursorImageProvider: \"ibeam\" not found, falling back to \"xterm\"";
1518+ cursorImage = fetchCursorHelper(themeName, "xterm");
1519+ } else if (cursorName == "xterm") {
1520+ qDebug() << "CursorImageProvider: \"xterm\" not found, falling back to \"ibeam\"";
1521+ cursorImage = fetchCursorHelper(themeName, "ibeam");
1522+ }
1523+ }
1524+
1525+ // if it all fails, there must be at least a left_ptr
1526+ if (cursorImage->qimage.isNull() && cursorName != "left_ptr") {
1527+ qDebug() << "CursorImageProvider:" << cursorName
1528+ << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
1529+ cursorImage = fetchCursorHelper(themeName, "left_ptr");
1530+ }
1531+
1532+ if (cursorImage->qimage.isNull()) {
1533+ // finally, go for the built-in cursor
1534+ qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
1535+ if (!m_builtInCursorImage) {
1536+ m_builtInCursorImage.reset(new BuiltInCursorImage);
1537+ }
1538+ cursorImage = m_builtInCursorImage.data();
1539+ }
1540+
1541+ return cursorImage;
1542+}
1543+
1544+CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName)
1545+{
1546+ QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
1547+
1548+ if (!themeCursors.contains(cursorName)) {
1549+ themeCursors[cursorName] = new XCursorImage(themeName, cursorName);
1550+ }
1551+
1552+ return themeCursors[cursorName];
1553+}
1554
1555=== added file 'plugins/Cursor/CursorImageProvider.h'
1556--- plugins/Cursor/CursorImageProvider.h 1970-01-01 00:00:00 +0000
1557+++ plugins/Cursor/CursorImageProvider.h 2015-10-26 12:04:03 +0000
1558@@ -0,0 +1,76 @@
1559+/*
1560+ * Copyright (C) 2015 Canonical, Ltd.
1561+ *
1562+ * This program is free software: you can redistribute it and/or modify it under
1563+ * the terms of the GNU Lesser General Public License version 3, as published by
1564+ * the Free Software Foundation.
1565+ *
1566+ * This program is distributed in the hope that it will be useful, but WITHOUT
1567+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1568+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1569+ * Lesser General Public License for more details.
1570+ *
1571+ * You should have received a copy of the GNU Lesser General Public License
1572+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1573+ */
1574+
1575+#ifndef CURSORIMAGEPROVIDER_H
1576+#define CURSORIMAGEPROVIDER_H
1577+
1578+#include <QQuickImageProvider>
1579+#include <QScopedPointer>
1580+
1581+// xcursor static lib
1582+extern "C"
1583+{
1584+#include <xcursor.h>
1585+}
1586+
1587+class CursorImage {
1588+public:
1589+ virtual ~CursorImage() {}
1590+
1591+ QImage qimage;
1592+ QPoint hotspot;
1593+};
1594+
1595+class XCursorImage : public CursorImage {
1596+public:
1597+ XCursorImage(const QString &theme, const QString &file);
1598+ virtual ~XCursorImage();
1599+
1600+ XcursorImages *xcursorImages;
1601+};
1602+
1603+class BuiltInCursorImage : public CursorImage {
1604+public:
1605+ BuiltInCursorImage();
1606+};
1607+
1608+class CursorImageProvider : public QQuickImageProvider
1609+{
1610+public:
1611+ CursorImageProvider();
1612+ virtual ~CursorImageProvider();
1613+
1614+ static CursorImageProvider *instance() { return m_instance; }
1615+
1616+
1617+ QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override;
1618+
1619+ QPoint hotspot(const QString &themeName, const QString &cursorName);
1620+
1621+private:
1622+ CursorImage *fetchCursor(const QString &cursorThemeAndName);
1623+ CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
1624+ CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName);
1625+
1626+ // themeName -> (cursorName -> cursorImage)
1627+ QMap<QString, QMap<QString, CursorImage*> > m_cursors;
1628+
1629+ QScopedPointer<CursorImage> m_builtInCursorImage;
1630+
1631+ static CursorImageProvider *m_instance;
1632+};
1633+
1634+#endif // CURSORIMAGEPROVIDER_H
1635
1636=== added file 'plugins/Cursor/MousePointer.cpp'
1637--- plugins/Cursor/MousePointer.cpp 1970-01-01 00:00:00 +0000
1638+++ plugins/Cursor/MousePointer.cpp 2015-10-26 12:04:03 +0000
1639@@ -0,0 +1,125 @@
1640+/*
1641+ * Copyright (C) 2015 Canonical, Ltd.
1642+ *
1643+ * This program is free software: you can redistribute it and/or modify it under
1644+ * the terms of the GNU Lesser General Public License version 3, as published by
1645+ * the Free Software Foundation.
1646+ *
1647+ * This program is distributed in the hope that it will be useful, but WITHOUT
1648+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1649+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1650+ * Lesser General Public License for more details.
1651+ *
1652+ * You should have received a copy of the GNU Lesser General Public License
1653+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1654+ */
1655+
1656+#include "MousePointer.h"
1657+#include "CursorImageProvider.h"
1658+
1659+// Unity API
1660+#include <unity/shell/application/MirPlatformCursor.h>
1661+
1662+#include <QQuickWindow>
1663+#include <QGuiApplication>
1664+
1665+#include <qpa/qwindowsysteminterface.h>
1666+
1667+MousePointer::MousePointer(QQuickItem *parent)
1668+ : MirMousePointerInterface(parent)
1669+ , m_cursorName("left_ptr")
1670+ , m_themeName("default")
1671+ , m_hotspotX(0)
1672+ , m_hotspotY(0)
1673+{
1674+}
1675+
1676+void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
1677+ Qt::KeyboardModifiers modifiers)
1678+{
1679+ if (!parentItem()) {
1680+ return;
1681+ }
1682+
1683+ qreal newX = x() + movement.x();
1684+ if (newX < 0) {
1685+ newX = 0;
1686+ } else if (newX > parentItem()->width()) {
1687+ newX = parentItem()->width();
1688+ }
1689+ setX(newX);
1690+
1691+ qreal newY = y() + movement.y();
1692+ if (newY < 0) {
1693+ newY = 0;
1694+ } else if (newY > parentItem()->height()) {
1695+ newY = parentItem()->height();
1696+ }
1697+ setY(newY);
1698+
1699+ QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
1700+ QWindowSystemInterface::handleMouseEvent(window(), timestamp, scenePosition /*local*/, scenePosition /*global*/,
1701+ buttons, modifiers);
1702+}
1703+
1704+void MousePointer::itemChange(ItemChange change, const ItemChangeData &value)
1705+{
1706+ if (change == ItemSceneChange) {
1707+ registerWindow(value.window);
1708+ }
1709+}
1710+
1711+void MousePointer::registerWindow(QWindow *window)
1712+{
1713+ if (m_registeredWindow && window != m_registeredWindow) {
1714+ auto previousCursor = dynamic_cast<MirPlatformCursor*>(m_registeredWindow->screen()->handle()->cursor());
1715+ if (previousCursor) {
1716+ previousCursor->setMousePointer(nullptr);
1717+ } else {
1718+ qCritical("QPlatformCursor is not a MirPlatformCursor! Cursor module only works in a Mir server.");
1719+ }
1720+ }
1721+
1722+ m_registeredWindow = window;
1723+
1724+ if (m_registeredWindow) {
1725+ auto cursor = dynamic_cast<MirPlatformCursor*>(window->screen()->handle()->cursor());
1726+ if (cursor) {
1727+ cursor->setMousePointer(this);
1728+ } else {
1729+ qCritical("QPlaformCursor is not a MirPlatformCursor! Cursor module only works in Mir.");
1730+ }
1731+ }
1732+}
1733+
1734+void MousePointer::setCursorName(const QString &cursorName)
1735+{
1736+ if (cursorName != m_cursorName) {
1737+ m_cursorName = cursorName;
1738+ Q_EMIT cursorNameChanged(m_cursorName);
1739+ updateHotspot();
1740+ }
1741+}
1742+
1743+void MousePointer::updateHotspot()
1744+{
1745+ QPoint newHotspot = CursorImageProvider::instance()->hotspot(m_themeName, m_cursorName);
1746+
1747+ if (m_hotspotX != newHotspot.x()) {
1748+ m_hotspotX = newHotspot.x();
1749+ Q_EMIT hotspotXChanged(m_hotspotX);
1750+ }
1751+
1752+ if (m_hotspotY != newHotspot.y()) {
1753+ m_hotspotY = newHotspot.y();
1754+ Q_EMIT hotspotYChanged(m_hotspotY);
1755+ }
1756+}
1757+
1758+void MousePointer::setThemeName(const QString &themeName)
1759+{
1760+ if (m_themeName != themeName) {
1761+ m_themeName = themeName;
1762+ Q_EMIT themeNameChanged(m_themeName);
1763+ }
1764+}
1765
1766=== added file 'plugins/Cursor/MousePointer.h'
1767--- plugins/Cursor/MousePointer.h 1970-01-01 00:00:00 +0000
1768+++ plugins/Cursor/MousePointer.h 2015-10-26 12:04:03 +0000
1769@@ -0,0 +1,59 @@
1770+/*
1771+ * Copyright (C) 2015 Canonical, Ltd.
1772+ *
1773+ * This program is free software: you can redistribute it and/or modify it under
1774+ * the terms of the GNU Lesser General Public License version 3, as published by
1775+ * the Free Software Foundation.
1776+ *
1777+ * This program is distributed in the hope that it will be useful, but WITHOUT
1778+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1779+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1780+ * Lesser General Public License for more details.
1781+ *
1782+ * You should have received a copy of the GNU Lesser General Public License
1783+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1784+ */
1785+
1786+#ifndef MOUSEPOINTER_H
1787+#define MOUSEPOINTER_H
1788+
1789+// Qt
1790+#include <QPointer>
1791+#include <QWindow>
1792+
1793+// Unity API
1794+#include <unity/shell/application/MirMousePointerInterface.h>
1795+
1796+class MousePointer : public MirMousePointerInterface {
1797+ Q_OBJECT
1798+public:
1799+ MousePointer(QQuickItem *parent = nullptr);
1800+
1801+ void setCursorName(const QString &qtCursorName) override;
1802+ QString cursorName() const override { return m_cursorName; }
1803+
1804+ void setThemeName(const QString &themeName) override;
1805+ QString themeName() const override { return m_themeName; }
1806+
1807+ qreal hotspotX() const override { return m_hotspotX; }
1808+ qreal hotspotY() const override { return m_hotspotY; }
1809+
1810+public Q_SLOTS:
1811+ void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
1812+ Qt::KeyboardModifiers modifiers) override;
1813+
1814+protected:
1815+ void itemChange(ItemChange change, const ItemChangeData &value) override;
1816+
1817+private:
1818+ void registerWindow(QWindow *window);
1819+ void updateHotspot();
1820+
1821+ QPointer<QWindow> m_registeredWindow;
1822+ QString m_cursorName;
1823+ QString m_themeName;
1824+ int m_hotspotX;
1825+ int m_hotspotY;
1826+};
1827+
1828+#endif // MOUSEPOINTER_H
1829
1830=== added file 'plugins/Cursor/plugin.cpp'
1831--- plugins/Cursor/plugin.cpp 1970-01-01 00:00:00 +0000
1832+++ plugins/Cursor/plugin.cpp 2015-10-26 12:04:03 +0000
1833@@ -0,0 +1,39 @@
1834+/*
1835+ * Copyright (C) 2015 Canonical, Ltd.
1836+ *
1837+ * This program is free software; you can redistribute it and/or modify
1838+ * it under the terms of the GNU General Public License as published by
1839+ * the Free Software Foundation; version 3.
1840+ *
1841+ * This program is distributed in the hope that it will be useful,
1842+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1843+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1844+ * GNU General Public License for more details.
1845+ *
1846+ * You should have received a copy of the GNU General Public License
1847+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1848+ */
1849+
1850+// Qt
1851+#include <QtQml/qqml.h>
1852+#include <QQmlContext>
1853+
1854+// self
1855+#include "plugin.h"
1856+
1857+// local
1858+#include "CursorImageProvider.h"
1859+#include "MousePointer.h"
1860+
1861+void CursorPlugin::registerTypes(const char *uri)
1862+{
1863+ Q_ASSERT(uri == QLatin1String("Cursor"));
1864+ qmlRegisterType<MousePointer>(uri, 1, 0, "MousePointer");
1865+}
1866+
1867+void CursorPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
1868+{
1869+ QQmlExtensionPlugin::initializeEngine(engine, uri);
1870+
1871+ engine->addImageProvider(QLatin1String("cursor"), new CursorImageProvider());
1872+}
1873
1874=== added file 'plugins/Cursor/plugin.h'
1875--- plugins/Cursor/plugin.h 1970-01-01 00:00:00 +0000
1876+++ plugins/Cursor/plugin.h 2015-10-26 12:04:03 +0000
1877@@ -0,0 +1,33 @@
1878+/*
1879+ * Copyright (C) 2015 Canonical, Ltd.
1880+ *
1881+ * This program is free software; you can redistribute it and/or modify
1882+ * it under the terms of the GNU General Public License as published by
1883+ * the Free Software Foundation; version 3.
1884+ *
1885+ * This program is distributed in the hope that it will be useful,
1886+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1887+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1888+ * GNU General Public License for more details.
1889+ *
1890+ * You should have received a copy of the GNU General Public License
1891+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1892+ */
1893+
1894+#ifndef CURSOR_PLUGIN_H
1895+#define CURSOR_PLUGIN_H
1896+
1897+#include <QtQml/QQmlEngine>
1898+#include <QtQml/QQmlExtensionPlugin>
1899+
1900+class CursorPlugin : public QQmlExtensionPlugin
1901+{
1902+ Q_OBJECT
1903+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
1904+
1905+public:
1906+ void registerTypes(const char *uri) override;
1907+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
1908+};
1909+
1910+#endif // CURSOR_PLUGIN_H
1911
1912=== added file 'plugins/Cursor/qmldir'
1913--- plugins/Cursor/qmldir 1970-01-01 00:00:00 +0000
1914+++ plugins/Cursor/qmldir 2015-10-26 12:04:03 +0000
1915@@ -0,0 +1,3 @@
1916+module Cursor
1917+plugin Cursor-qml
1918+Cursor 1.0 Cursor.qml
1919
1920=== modified file 'plugins/IntegratedLightDM/UsersModel.cpp'
1921--- plugins/IntegratedLightDM/UsersModel.cpp 2015-04-30 09:31:51 +0000
1922+++ plugins/IntegratedLightDM/UsersModel.cpp 2015-10-26 12:04:03 +0000
1923@@ -43,16 +43,17 @@
1924
1925 QVariant MangleModel::data(const QModelIndex &index, int role) const
1926 {
1927- QVariant data = QSortFilterProxyModel::data(index, role);
1928+ QVariant variantData = QSortFilterProxyModel::data(index, role);
1929
1930 // If user's real name is empty, switch to unix name
1931- if (role == QLightDM::UsersModel::RealNameRole && data.toString().isEmpty()) {
1932- data = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
1933- } else if (role == QLightDM::UsersModel::BackgroundPathRole && data.toString().startsWith('#')) {
1934- data = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + data.toString() + "'/></svg>";
1935+ if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
1936+ variantData = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
1937+ } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
1938+ const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
1939+ variantData = stringData;
1940 }
1941
1942- return data;
1943+ return variantData;
1944 }
1945
1946 // **** Now we continue with actual UsersModel class ****
1947
1948=== modified file 'plugins/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp'
1949--- plugins/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp 2015-09-14 09:11:08 +0000
1950+++ plugins/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp 2015-10-26 12:04:03 +0000
1951@@ -34,7 +34,8 @@
1952 entries.reserve(users.count());
1953 Q_FOREACH(const QString &user, users)
1954 {
1955- QString name = settings.value(user + "/name", user[0].toUpper() + user.mid(1)).toString();
1956+ QVariant defaultValue = QString(user[0].toUpper() + user.mid(1));
1957+ QString name = settings.value(user + "/name", defaultValue).toString();
1958 entries.append({user, name, 0, 0, false, false, 0, 0});
1959 }
1960 }
1961
1962=== modified file 'plugins/ScreenGrabber/screengrabber.cpp'
1963--- plugins/ScreenGrabber/screengrabber.cpp 2015-09-23 15:14:01 +0000
1964+++ plugins/ScreenGrabber/screengrabber.cpp 2015-10-26 12:04:03 +0000
1965@@ -60,7 +60,7 @@
1966 }
1967 }
1968
1969-void ScreenGrabber::captureAndSave()
1970+void ScreenGrabber::captureAndSave(int angle)
1971 {
1972 if (fileNamePrefix.isEmpty())
1973 {
1974@@ -82,7 +82,7 @@
1975 return;
1976 }
1977
1978- const QImage screenshot = main_window->grabWindow();
1979+ const QImage screenshot = main_window->grabWindow().transformed(QTransform().rotate(angle));
1980 const QString filename = makeFileName();
1981 qDebug() << "Saving screenshot to" << filename;
1982 auto saveOp = QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality);
1983
1984=== modified file 'plugins/ScreenGrabber/screengrabber.h'
1985--- plugins/ScreenGrabber/screengrabber.h 2015-07-17 19:59:31 +0000
1986+++ plugins/ScreenGrabber/screengrabber.h 2015-10-26 12:04:03 +0000
1987@@ -29,7 +29,7 @@
1988 ~ScreenGrabber() = default;
1989
1990 public Q_SLOTS:
1991- void captureAndSave();
1992+ void captureAndSave(int angle = 0);
1993
1994 Q_SIGNALS:
1995 void screenshotSaved(const QString &filename);
1996
1997=== modified file 'plugins/Unity/CMakeLists.txt'
1998--- plugins/Unity/CMakeLists.txt 2015-05-05 11:19:15 +0000
1999+++ plugins/Unity/CMakeLists.txt 2015-10-26 12:04:03 +0000
2000@@ -4,3 +4,4 @@
2001 add_subdirectory(Session)
2002 add_subdirectory(DashCommunicator)
2003 add_subdirectory(InputInfo)
2004+add_subdirectory(Platform)
2005
2006=== modified file 'plugins/Unity/Launcher/desktopfilehandler.cpp'
2007--- plugins/Unity/Launcher/desktopfilehandler.cpp 2015-09-14 09:41:55 +0000
2008+++ plugins/Unity/Launcher/desktopfilehandler.cpp 2015-10-26 12:04:03 +0000
2009@@ -111,8 +111,9 @@
2010 settings.beginGroup(QStringLiteral("Desktop Entry"));
2011
2012 // First try to find Name[xx_YY] and Name[xx] in .desktop file
2013- QString locale = QLocale().name();
2014- QString shortLocale = locale.split('_').first();
2015+ const QString locale = QLocale().name();
2016+ const QStringList splitLocale = locale.split(QLatin1Char('_'));
2017+ const QString shortLocale = splitLocale.first();
2018
2019 if (locale != shortLocale && settings.contains(QStringLiteral("Name[%1]").arg(locale))) {
2020 return settings.value(QStringLiteral("Name[%1]").arg(locale)).toString();
2021
2022=== added directory 'plugins/Unity/Platform'
2023=== added file 'plugins/Unity/Platform/CMakeLists.txt'
2024--- plugins/Unity/Platform/CMakeLists.txt 1970-01-01 00:00:00 +0000
2025+++ plugins/Unity/Platform/CMakeLists.txt 2015-10-26 12:04:03 +0000
2026@@ -0,0 +1,9 @@
2027+set(platformplugin_SRCS
2028+ platform.cpp
2029+ plugin.cpp)
2030+
2031+add_library(Platform-qml SHARED ${platformplugin_SRCS})
2032+
2033+qt5_use_modules(Platform-qml DBus Qml)
2034+
2035+add_unity8_plugin(Unity.Platform 1.0 Unity/Platform TARGETS Platform-qml)
2036
2037=== added file 'plugins/Unity/Platform/platform.cpp'
2038--- plugins/Unity/Platform/platform.cpp 1970-01-01 00:00:00 +0000
2039+++ plugins/Unity/Platform/platform.cpp 2015-10-26 12:04:03 +0000
2040@@ -0,0 +1,43 @@
2041+/*
2042+ * Copyright (C) 2015 Canonical, Ltd.
2043+ *
2044+ * This program is free software; you can redistribute it and/or modify
2045+ * it under the terms of the GNU General Public License as published by
2046+ * the Free Software Foundation; version 3.
2047+ *
2048+ * This program is distributed in the hope that it will be useful,
2049+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2050+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2051+ * GNU General Public License for more details.
2052+ *
2053+ * You should have received a copy of the GNU General Public License
2054+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2055+ */
2056+
2057+#include "platform.h"
2058+
2059+#include <QDBusConnection>
2060+
2061+Platform::Platform(QObject *parent)
2062+ : QObject(parent)
2063+ , m_iface("org.freedesktop.hostname1", "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
2064+ QDBusConnection::systemBus(), this)
2065+{
2066+ QMetaObject::invokeMethod(this, "init");
2067+}
2068+
2069+void Platform::init()
2070+{
2071+ m_chassis = m_iface.property("Chassis").toString();
2072+ m_isPC = (m_chassis == "desktop" || m_chassis == "laptop" || m_chassis == "server");
2073+}
2074+
2075+QString Platform::chassis() const
2076+{
2077+ return m_chassis;
2078+}
2079+
2080+bool Platform::isPC() const
2081+{
2082+ return m_isPC;
2083+}
2084
2085=== added file 'plugins/Unity/Platform/platform.h'
2086--- plugins/Unity/Platform/platform.h 1970-01-01 00:00:00 +0000
2087+++ plugins/Unity/Platform/platform.h 2015-10-26 12:04:03 +0000
2088@@ -0,0 +1,58 @@
2089+/*
2090+ * Copyright (C) 2015 Canonical, Ltd.
2091+ *
2092+ * This program is free software; you can redistribute it and/or modify
2093+ * it under the terms of the GNU General Public License as published by
2094+ * the Free Software Foundation; version 3.
2095+ *
2096+ * This program is distributed in the hope that it will be useful,
2097+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2098+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2099+ * GNU General Public License for more details.
2100+ *
2101+ * You should have received a copy of the GNU General Public License
2102+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2103+ */
2104+
2105+#ifndef PLATFORM_H
2106+#define PLATFORM_H
2107+
2108+#include <QDBusInterface>
2109+
2110+/**
2111+ * @brief The Platform class
2112+ *
2113+ * Wrapper around platform detection support (org.freedesktop.hostname1)
2114+ */
2115+class Platform: public QObject
2116+{
2117+ Q_OBJECT
2118+ /**
2119+ * The chassis property
2120+ *
2121+ * Supported values include: "laptop", "computer", "handset" or "tablet"
2122+ * For full list see: http://www.freedesktop.org/wiki/Software/systemd/hostnamed/
2123+ */
2124+ Q_PROPERTY(QString chassis READ chassis CONSTANT)
2125+ /**
2126+ * Whether the machine is an ordinary PC (desktop, laptop or server)
2127+ */
2128+ Q_PROPERTY(bool isPC READ isPC CONSTANT)
2129+
2130+public:
2131+ Platform(QObject *parent = nullptr);
2132+ ~Platform() = default;
2133+
2134+ QString chassis() const;
2135+ bool isPC() const;
2136+
2137+private Q_SLOTS:
2138+ void init();
2139+
2140+private:
2141+ QDBusInterface m_iface;
2142+ QString m_chassis;
2143+ bool m_isPC;
2144+};
2145+
2146+#endif // PLATFORM_H
2147
2148=== added file 'plugins/Unity/Platform/plugin.cpp'
2149--- plugins/Unity/Platform/plugin.cpp 1970-01-01 00:00:00 +0000
2150+++ plugins/Unity/Platform/plugin.cpp 2015-10-26 12:04:03 +0000
2151@@ -0,0 +1,27 @@
2152+/*
2153+ * Copyright (C) 2015 Canonical, Ltd.
2154+ *
2155+ * This program is free software; you can redistribute it and/or modify
2156+ * it under the terms of the GNU General Public License as published by
2157+ * the Free Software Foundation; version 3.
2158+ *
2159+ * This program is distributed in the hope that it will be useful,
2160+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2161+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2162+ * GNU General Public License for more details.
2163+ *
2164+ * You should have received a copy of the GNU General Public License
2165+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2166+ */
2167+
2168+#include "plugin.h"
2169+#include "platform.h"
2170+
2171+#include <QtQml>
2172+
2173+void GlobalShortcutPlugin::registerTypes(const char *uri)
2174+{
2175+ Q_ASSERT(uri == QLatin1String("Unity.Platform"));
2176+
2177+ qmlRegisterSingletonType<Platform>(uri, 1, 0, "Platform", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Platform; });
2178+}
2179
2180=== added file 'plugins/Unity/Platform/plugin.h'
2181--- plugins/Unity/Platform/plugin.h 1970-01-01 00:00:00 +0000
2182+++ plugins/Unity/Platform/plugin.h 2015-10-26 12:04:03 +0000
2183@@ -0,0 +1,32 @@
2184+/*
2185+ * Copyright (C) 2015 Canonical, Ltd.
2186+ *
2187+ * This program is free software; you can redistribute it and/or modify
2188+ * it under the terms of the GNU General Public License as published by
2189+ * the Free Software Foundation; version 3.
2190+ *
2191+ * This program is distributed in the hope that it will be useful,
2192+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2193+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2194+ * GNU General Public License for more details.
2195+ *
2196+ * You should have received a copy of the GNU General Public License
2197+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2198+ */
2199+
2200+#ifndef PLATFORMPLUGIN_H
2201+#define PLATFORMPLUGIN_H
2202+
2203+#include <QQmlExtensionPlugin>
2204+
2205+class GlobalShortcutPlugin: public QQmlExtensionPlugin
2206+{
2207+ Q_OBJECT
2208+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
2209+
2210+public:
2211+ void registerTypes(const char *uri) override;
2212+};
2213+
2214+
2215+#endif // PLATFORMPLUGIN_H
2216
2217=== added file 'plugins/Unity/Platform/qmldir'
2218--- plugins/Unity/Platform/qmldir 1970-01-01 00:00:00 +0000
2219+++ plugins/Unity/Platform/qmldir 2015-10-26 12:04:03 +0000
2220@@ -0,0 +1,2 @@
2221+module Unity.Platform
2222+plugin Platform-qml
2223
2224=== modified file 'plugins/Unity/Session/dbusunitysessionservice.cpp'
2225--- plugins/Unity/Session/dbusunitysessionservice.cpp 2015-09-14 09:11:08 +0000
2226+++ plugins/Unity/Session/dbusunitysessionservice.cpp 2015-10-26 12:04:03 +0000
2227@@ -292,7 +292,8 @@
2228 if (p) {
2229 const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
2230 if (!gecos.isEmpty()) {
2231- return gecos.split(QLatin1Char(',')).first();
2232+ const QStringList splitGecos = gecos.split(QLatin1Char(','));
2233+ return splitGecos.first();
2234 }
2235 }
2236
2237
2238=== modified file 'plugins/Utils/CMakeLists.txt'
2239--- plugins/Utils/CMakeLists.txt 2015-09-25 12:13:13 +0000
2240+++ plugins/Utils/CMakeLists.txt 2015-10-26 12:04:03 +0000
2241@@ -15,8 +15,6 @@
2242 inputwatcher.cpp
2243 qlimitproxymodelqml.cpp
2244 unitysortfilterproxymodelqml.cpp
2245- relativetimeformatter.cpp
2246- timeformatter.cpp
2247 Timer.cpp
2248 unitymenumodelpaths.cpp
2249 windowkeysfilter.cpp
2250
2251=== modified file 'plugins/Utils/plugin.cpp'
2252--- plugins/Utils/plugin.cpp 2015-09-25 12:13:13 +0000
2253+++ plugins/Utils/plugin.cpp 2015-10-26 12:04:03 +0000
2254@@ -29,8 +29,6 @@
2255 #include "inputwatcher.h"
2256 #include "qlimitproxymodelqml.h"
2257 #include "unitysortfilterproxymodelqml.h"
2258-#include "relativetimeformatter.h"
2259-#include "timeformatter.h"
2260 #include "unitymenumodelpaths.h"
2261 #include "windowkeysfilter.h"
2262 #include "windowscreenshotprovider.h"
2263@@ -60,11 +58,8 @@
2264 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
2265 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
2266 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
2267- qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
2268 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
2269- qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
2270 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
2271- qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
2272 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
2273 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
2274 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
2275
2276=== removed file 'plugins/Utils/relativetimeformatter.cpp'
2277--- plugins/Utils/relativetimeformatter.cpp 2015-09-21 13:17:30 +0000
2278+++ plugins/Utils/relativetimeformatter.cpp 1970-01-01 00:00:00 +0000
2279@@ -1,260 +0,0 @@
2280-/*
2281- * Copyright 2014 Canonical Ltd.
2282- *
2283- * This program is free software; you can redistribute it and/or modify
2284- * it under the terms of the GNU General Public License as published by
2285- * the Free Software Foundation; version 3.
2286- *
2287- * This program is distributed in the hope that it will be useful,
2288- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2289- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2290- * GNU General Public License for more details.
2291- *
2292- * You should have received a copy of the GNU General Public License
2293- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2294- *
2295- */
2296-
2297-// Local
2298-#include "relativetimeformatter.h"
2299-
2300-// Qt
2301-#include <QDateTime>
2302-
2303-// Other
2304-#include <glib.h>
2305-#include <glib/gi18n.h>
2306-#include <locale.h>
2307-#include <langinfo.h>
2308-#include <string.h>
2309-
2310-RelativeTimeFormatter::RelativeTimeFormatter(QObject *parent)
2311- : GDateTimeFormatter(parent)
2312-{
2313-}
2314-
2315- /* Check the system locale setting to see if the format is 24-hour
2316- time or 12-hour time */
2317-gboolean
2318-is_locale_12h(void)
2319-{
2320- int i;
2321- static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", nullptr};
2322- const char* t_fmt = nl_langinfo(T_FMT);
2323-
2324- for (i=0; formats_24h[i]!=nullptr; i++)
2325- if (strstr(t_fmt, formats_24h[i]) != nullptr)
2326- return FALSE;
2327-
2328- return TRUE;
2329-}
2330-
2331-typedef enum
2332-{
2333- DATE_PROXIMITY_YESTERDAY,
2334- DATE_PROXIMITY_TODAY,
2335- DATE_PROXIMITY_TOMORROW,
2336- DATE_PROXIMITY_LAST_WEEK,
2337- DATE_PROXIMITY_NEXT_WEEK,
2338- DATE_PROXIMITY_FAR
2339-} date_proximity_t;
2340-
2341-static date_proximity_t
2342-getDateProximity(GDateTime* now, GDateTime* time)
2343-{
2344- date_proximity_t prox = DATE_PROXIMITY_FAR;
2345- gint now_year, now_month, now_day;
2346- gint time_year, time_month, time_day;
2347-
2348- // does it happen today?
2349- g_date_time_get_ymd(now, &now_year, &now_month, &now_day);
2350- g_date_time_get_ymd(time, &time_year, &time_month, &time_day);
2351- if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) {
2352- return DATE_PROXIMITY_TODAY;
2353- }
2354-
2355- // did it happen yesterday?
2356- GDateTime* yesterday = g_date_time_add_days(now, -1);
2357- gint tom_year, tom_month, tom_day;
2358- g_date_time_get_ymd(yesterday, &tom_year, &tom_month, &tom_day);
2359- g_date_time_unref(yesterday);
2360- if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) {
2361- return DATE_PROXIMITY_YESTERDAY;
2362- }
2363-
2364- // does it happen tomorrow?
2365- GDateTime* tomorrow = g_date_time_add_days(now, 1);
2366- g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day);
2367- g_date_time_unref(tomorrow);
2368- if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) {
2369- return DATE_PROXIMITY_TOMORROW;
2370- }
2371-
2372- // does it happen this week?
2373- if (g_date_time_compare(time, now) < 0) {
2374- GDateTime* last_week = g_date_time_add_days(now, -6);
2375- GDateTime* last_week_bound = g_date_time_new_local(g_date_time_get_year(last_week),
2376- g_date_time_get_month(last_week),
2377- g_date_time_get_day_of_month(last_week),
2378- 0, 0, 0);
2379- if (g_date_time_compare(time, last_week_bound) >= 0)
2380- prox = DATE_PROXIMITY_LAST_WEEK;
2381-
2382- g_date_time_unref(last_week);
2383- g_date_time_unref(last_week_bound);
2384- } else {
2385- GDateTime* next_week = g_date_time_add_days(now, 6);
2386- GDateTime* next_week_bound = g_date_time_new_local(g_date_time_get_year(next_week),
2387- g_date_time_get_month(next_week),
2388- g_date_time_get_day_of_month(next_week),
2389- 23, 59, 59.9);
2390- if (g_date_time_compare(time, next_week_bound) <= 0)
2391- prox = DATE_PROXIMITY_NEXT_WEEK;
2392-
2393- g_date_time_unref(next_week);
2394- g_date_time_unref(next_week_bound);
2395- }
2396-
2397- return prox;
2398-}
2399-
2400-const char*
2401-dgettext_datetime(const char *text)
2402-{
2403- return dgettext("unity8", text);
2404-}
2405-
2406-/**
2407- * _ a time yesterday should be shown as (e.g. “Yesterday 3:55 PM”)
2408- * _ a time today should be shown as just the time (e.g. “3:55 PM”)
2409- * _ a time tomorrow should be shown as(e.g. “Tomorrow 3:55 PM”)
2410- * _ a time any other day this week should be shown as the short version of the
2411- * day and time (e.g. “Wed 3:55 PM”)
2412- * weekday (e.g. “Friday”)
2413- * _ a time after this week should be shown as the short version of the day,
2414- * date, and time (e.g. “Wed 21 Apr 3:55 PM”)
2415- * _ in addition, when presenting the times of upcoming events, the time should
2416- * be followed by the timezone if it is different from the one the computer
2417- * is currently set to. For example, “Wed 3:55 PM UTC−5”.
2418- *
2419- * TODO - keep inline with indicator-datetime
2420- */
2421-char* generate_full_format_string_at_time (GDateTime* now,
2422- GDateTime* then)
2423-{
2424- GString* ret = g_string_new (nullptr);
2425-
2426- if (then != nullptr) {
2427- const date_proximity_t prox = getDateProximity(now, then);
2428-
2429- if (is_locale_12h()) {
2430- switch (prox) {
2431- case DATE_PROXIMITY_YESTERDAY:
2432- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2433- This format string is used for showing, on a 12-hour clock, times that happen yesterday.
2434- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2435- en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM" */
2436- g_string_assign (ret, dgettext_datetime("Yesterday\u2003%l:%M %p"));
2437- break;
2438-
2439- case DATE_PROXIMITY_TODAY:
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 today.
2442- en_US example: "%l:%M %p" --> "1:00 PM" */
2443- g_string_assign (ret, dgettext_datetime("%l:%M %p"));
2444- break;
2445-
2446- case DATE_PROXIMITY_TOMORROW:
2447- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2448- This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
2449- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2450- en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM" */
2451- g_string_assign (ret, dgettext_datetime("Tomorrow\u2003%l:%M %p"));
2452- break;
2453-
2454- case DATE_PROXIMITY_LAST_WEEK:
2455- case DATE_PROXIMITY_NEXT_WEEK:
2456- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2457- This format string is used for showing, on a 12-hour clock, times that happened in the last week.
2458- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2459- en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM" */
2460- g_string_assign (ret, dgettext_datetime("%a\u2003%l:%M %p"));
2461- break;
2462-
2463- case DATE_PROXIMITY_FAR:
2464- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2465- This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
2466- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2467- en_US example: "%a %b %d\u2003%l:%M %p" --> "Fri Oct 31 1:00 PM"
2468- en_GB example: "%a %d %b\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM" */
2469- g_string_assign (ret, dgettext_datetime("%a %d %b\u2003%l:%M %p"));
2470- break;
2471- }
2472- } else {
2473- switch (prox) {
2474-
2475- case DATE_PROXIMITY_YESTERDAY:
2476- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2477- This format string is used for showing, on a 24-hour clock, times that happen yesterday.
2478- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2479- en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00" */
2480- g_string_assign (ret, dgettext_datetime("Yesterday\u2003%H:%M"));
2481- break;
2482-
2483- case DATE_PROXIMITY_TODAY:
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 today.
2486- en_US example: "%H:%M" --> "13:00" */
2487- g_string_assign (ret, dgettext_datetime("%H:%M"));
2488- break;
2489-
2490- case DATE_PROXIMITY_TOMORROW:
2491- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2492- This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
2493- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2494- en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00" */
2495- g_string_assign (ret, dgettext_datetime("Tomorrow\u2003%H:%M"));
2496- break;
2497-
2498- case DATE_PROXIMITY_LAST_WEEK:
2499- case DATE_PROXIMITY_NEXT_WEEK:
2500- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2501- This format string is used for showing, on a 24-hour clock, times that happened in the last week.
2502- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2503- en_US example: "%a\u2003%H:%M" --> "Fri 13:00" */
2504- g_string_assign (ret, dgettext_datetime("%a\u2003%H:%M"));
2505- break;
2506-
2507- case DATE_PROXIMITY_FAR:
2508- /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
2509- This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
2510- (\u2003 is a unicode em space which is slightly wider than a normal space.)
2511- en_US example: "%a %b %d\u2003%H:%M" --> "Fri Oct 31 13:00"
2512- en_GB example: "%a %d %b\u2003%H:%M" --> "Fri 31 Oct 13:00" */
2513- g_string_assign (ret, dgettext_datetime("%a %d %b\u2003%H:%M"));
2514- break;
2515- }
2516- }
2517- }
2518-
2519- return g_string_free (ret, FALSE);
2520-}
2521-
2522-QString RelativeTimeFormatter::format() const
2523-{
2524- GDateTime* now = g_date_time_new_now_local();
2525- if (!now) { return QString(); }
2526-
2527- GDateTime* then = g_date_time_new_from_unix_local(time());
2528- if (!then) { return QString(); }
2529-
2530- char* time_format = generate_full_format_string_at_time(now, then);
2531-
2532- QString str(QString::fromUtf8(time_format));
2533- g_free(time_format);
2534-
2535- g_date_time_unref(now);
2536- g_date_time_unref(then);
2537-
2538- return str;
2539-}
2540
2541=== removed file 'plugins/Utils/relativetimeformatter.h'
2542--- plugins/Utils/relativetimeformatter.h 2014-09-19 14:46:53 +0000
2543+++ plugins/Utils/relativetimeformatter.h 1970-01-01 00:00:00 +0000
2544@@ -1,34 +0,0 @@
2545-/*
2546- * Copyright 2014 Canonical Ltd.
2547- *
2548- * This program is free software; you can redistribute it and/or modify
2549- * it under the terms of the GNU General Public License as published by
2550- * the Free Software Foundation; version 3.
2551- *
2552- * This program is distributed in the hope that it will be useful,
2553- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2554- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2555- * GNU General Public License for more details.
2556- *
2557- * You should have received a copy of the GNU General Public License
2558- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2559- *
2560- */
2561-
2562-#ifndef RELATIVETIMEFORMATTER_H
2563-#define RELATIVETIMEFORMATTER_H
2564-
2565-#include "timeformatter.h"
2566-
2567-// TODO - move this to the sdk
2568-// https://blueprints.launchpad.net/ubuntu-ui-toolkit/+spec/time-formatter
2569-class RelativeTimeFormatter : public GDateTimeFormatter
2570-{
2571- Q_OBJECT
2572-public:
2573- RelativeTimeFormatter(QObject *parent = 0);
2574-
2575- QString format() const override;
2576-};
2577-
2578-#endif // RELATIVETIMEFORMATTER_H
2579
2580=== removed file 'plugins/Utils/timeformatter.cpp'
2581--- plugins/Utils/timeformatter.cpp 2015-09-24 14:02:25 +0000
2582+++ plugins/Utils/timeformatter.cpp 1970-01-01 00:00:00 +0000
2583@@ -1,206 +0,0 @@
2584-/*
2585- * Copyright 2013 Canonical Ltd.
2586- *
2587- * This program is free software; you can redistribute it and/or modify
2588- * it under the terms of the GNU General Public License as published by
2589- * the Free Software Foundation; version 3.
2590- *
2591- * This program is distributed in the hope that it will be useful,
2592- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2593- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2594- * GNU General Public License for more details.
2595- *
2596- * You should have received a copy of the GNU General Public License
2597- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2598- *
2599- * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
2600- */
2601-
2602-#include "timeformatter.h"
2603-
2604-#include <gio/gio.h>
2605-#include <QDateTime>
2606-
2607-struct TimeFormatterPrivate
2608-{
2609- TimeFormatter *formatter;
2610-
2611- QString format;
2612- QString timeString;
2613- qint64 time;
2614-
2615- GDBusConnection *system_bus;
2616- guint subscription_id;
2617- GCancellable *cancellable;
2618-};
2619-
2620-static void
2621-timedate1_properties_changed (GDBusConnection *connection,
2622- const gchar *sender_name,
2623- const gchar *object_path,
2624- const gchar *interface_name,
2625- const gchar *signal_name,
2626- GVariant *parameters,
2627- gpointer user_data)
2628-{
2629- Q_UNUSED(connection);
2630- Q_UNUSED(sender_name);
2631- Q_UNUSED(object_path);
2632- Q_UNUSED(interface_name);
2633- Q_UNUSED(signal_name);
2634-
2635- TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
2636- GVariant *changed;
2637- GVariantIter *iter;
2638- const gchar *name;
2639-
2640- if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
2641- return;
2642-
2643- g_variant_get (parameters, "(s@a{sv}as)", nullptr, &changed, &iter);
2644-
2645- if (g_variant_lookup (changed, "Timezone", "s", nullptr)) {
2646- priv->formatter->update();
2647- }
2648- else {
2649- while (g_variant_iter_next (iter, "&s", &name)) {
2650- if (g_str_equal (name, "Timezone")) {
2651- priv->formatter->update();
2652- break;
2653- }
2654- }
2655- }
2656-
2657- g_variant_unref (changed);
2658- g_variant_iter_free (iter);
2659-}
2660-
2661-static void
2662-got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
2663-{
2664- Q_UNUSED(object);
2665-
2666- TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
2667- GError *error = nullptr;
2668-
2669- priv->system_bus = g_bus_get_finish (result, &error);
2670- if (priv->system_bus == nullptr) {
2671- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
2672- qWarning("TimeFormatter: cannot connect to the system bus: %s", error->message);
2673- g_error_free (error);
2674- return;
2675- }
2676-
2677- /* Listen to the PropertiesChanged on the org.freedesktop.timedate1
2678- * interface from any sender. In practice, this signal will only be sent
2679- * from timedated (we can trust other processes on the system bus to behave
2680- * nicely). That way, we don't have to watch timedated's well-known name
2681- * and keep the process alive.
2682- */
2683- priv->subscription_id = g_dbus_connection_signal_subscribe (priv->system_bus,
2684- nullptr, /* sender */
2685- "org.freedesktop.DBus.Properties",
2686- "PropertiesChanged",
2687- nullptr,
2688- "org.freedesktop.timedate1",
2689- G_DBUS_SIGNAL_FLAGS_NONE,
2690- timedate1_properties_changed,
2691- priv, nullptr);
2692-}
2693-
2694-TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
2695-{
2696- priv = new TimeFormatterPrivate;
2697- priv->formatter = this;
2698- priv->time = 0;
2699- priv->format = QStringLiteral("yyyy-MM-dd hh:mm");
2700- priv->system_bus = nullptr;
2701- priv->subscription_id = 0;
2702- priv->cancellable = g_cancellable_new ();
2703-
2704- g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
2705-}
2706-
2707-TimeFormatter::TimeFormatter(const QString &initialFormat, QObject *parent): TimeFormatter(parent)
2708-{
2709- priv->format = initialFormat;
2710-}
2711-
2712-TimeFormatter::~TimeFormatter()
2713-{
2714- if (priv->system_bus) {
2715- g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->subscription_id);
2716- g_object_unref (priv->system_bus);
2717- }
2718-
2719- g_cancellable_cancel (priv->cancellable);
2720- g_object_unref (priv->cancellable);
2721-}
2722-
2723-QString TimeFormatter::format() const
2724-{
2725- return priv->format;
2726-}
2727-
2728-QString TimeFormatter::timeString() const
2729-{
2730- return priv->timeString;
2731-}
2732-
2733-qint64 TimeFormatter::time() const
2734-{
2735- return priv->time;
2736-}
2737-
2738-void TimeFormatter::setFormat(const QString &format)
2739-{
2740- if (priv->format != format) {
2741- priv->format = format;
2742- Q_EMIT formatChanged(priv->format);
2743- update();
2744- }
2745-}
2746-
2747-void TimeFormatter::setTime(qint64 time)
2748-{
2749- if (priv->time != time) {
2750- priv->time = time;
2751- Q_EMIT timeChanged(priv->time);
2752- update();
2753- }
2754-}
2755-
2756-void TimeFormatter::update()
2757-{
2758- priv->timeString = formatTime();
2759- Q_EMIT timeStringChanged(priv->timeString);
2760-}
2761-
2762-QString TimeFormatter::formatTime() const
2763-{
2764- return QDateTime::fromMSecsSinceEpoch(time() / 1000).toString(format());
2765-}
2766-
2767-GDateTimeFormatter::GDateTimeFormatter(QObject* parent)
2768-: TimeFormatter(QStringLiteral("%d-%m-%Y %I:%M%p"), parent)
2769-{
2770-}
2771-
2772-QString GDateTimeFormatter::formatTime() const
2773-{
2774- gchar* time_string;
2775- GDateTime* datetime;
2776- QByteArray formatBytes = format().toUtf8();
2777-
2778- datetime = g_date_time_new_from_unix_local(time());
2779- if (!datetime) {
2780- return QLatin1String("");
2781- }
2782-
2783- time_string = g_date_time_format(datetime, formatBytes.constData());
2784- QString formattedTime(QString::fromUtf8(time_string));
2785-
2786- g_free(time_string);
2787- g_date_time_unref(datetime);
2788- return formattedTime;
2789-}
2790
2791=== removed file 'plugins/Utils/timeformatter.h'
2792--- plugins/Utils/timeformatter.h 2015-09-17 13:42:15 +0000
2793+++ plugins/Utils/timeformatter.h 1970-01-01 00:00:00 +0000
2794@@ -1,68 +0,0 @@
2795-/*
2796- * Copyright 2013 Canonical Ltd.
2797- *
2798- * This program is free software; you can redistribute it and/or modify
2799- * it under the terms of the GNU General Public License as published by
2800- * the Free Software Foundation; version 3.
2801- *
2802- * This program is distributed in the hope that it will be useful,
2803- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2804- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2805- * GNU General Public License for more details.
2806- *
2807- * You should have received a copy of the GNU General Public License
2808- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2809- *
2810- * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
2811- */
2812-
2813-#ifndef TIME_FORMATTER_H
2814-#define TIME_FORMATTER_H
2815-
2816-#include <QObject>
2817-
2818-// TODO - bug #1260728
2819-class TimeFormatter : public QObject
2820-{
2821- Q_OBJECT
2822- Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged)
2823- Q_PROPERTY(QString timeString READ timeString NOTIFY timeStringChanged)
2824- Q_PROPERTY(qint64 time READ time WRITE setTime NOTIFY timeChanged)
2825-
2826-public:
2827- TimeFormatter(QObject *parent = 0);
2828- virtual ~TimeFormatter();
2829-
2830- virtual QString format() const;
2831- QString timeString() const;
2832- qint64 time() const;
2833-
2834- void setFormat(const QString &format);
2835- void setTime(qint64 time);
2836-
2837- void update();
2838-
2839-Q_SIGNALS:
2840- void formatChanged(const QString &format);
2841- void timeStringChanged(const QString &timeString);
2842- void timeChanged(qint64 time);
2843-
2844-protected:
2845- TimeFormatter(const QString &initialFormat, QObject *parent = 0);
2846-
2847- virtual QString formatTime() const;
2848-
2849-private:
2850- struct TimeFormatterPrivate *priv;
2851-};
2852-
2853-class GDateTimeFormatter : public TimeFormatter
2854-{
2855-public:
2856- GDateTimeFormatter(QObject *parent = 0);
2857-
2858-protected:
2859- QString formatTime() const override;
2860-};
2861-
2862-#endif
2863
2864=== modified file 'plugins/Utils/timezoneFormatter.cpp'
2865--- plugins/Utils/timezoneFormatter.cpp 2015-09-17 13:42:15 +0000
2866+++ plugins/Utils/timezoneFormatter.cpp 2015-10-26 12:04:03 +0000
2867@@ -29,7 +29,7 @@
2868 if (tz.isValid()) {
2869 const QDateTime now = QDateTime::currentDateTime().toTimeZone(tz);
2870 // return locale-aware string in the form "day, hh:mm", e.g. "Mon 14:30" or "Mon 1:30 pm"
2871- return QStringLiteral("%1 %2").arg(now.toString("ddd")).arg(now.time().toString(Qt::DefaultLocaleShortDate));
2872+ return QStringLiteral("%1 %2").arg(now.toString(QStringLiteral("ddd"))).arg(now.time().toString(Qt::DefaultLocaleShortDate));
2873 }
2874 return QString();
2875 }
2876
2877=== modified file 'qml/Components/Dialogs.qml'
2878--- qml/Components/Dialogs.qml 2015-09-02 09:30:32 +0000
2879+++ qml/Components/Dialogs.qml 2015-10-26 12:04:03 +0000
2880@@ -20,6 +20,7 @@
2881 import Unity.Session 0.1
2882 import Ubuntu.Components 1.1
2883 import GlobalShortcut 1.0
2884+import Unity.Platform 1.0
2885 import "../Greeter"
2886
2887 Item {
2888@@ -46,13 +47,13 @@
2889
2890 GlobalShortcut { // reboot/shutdown dialog
2891 shortcut: Qt.Key_PowerDown
2892- active: root.usageScenario === "desktop"
2893+ active: Platform.isPC
2894 onTriggered: root.unitySessionService.RequestShutdown()
2895 }
2896
2897 GlobalShortcut { // reboot/shutdown dialog
2898 shortcut: Qt.Key_PowerOff
2899- active: root.usageScenario === "desktop"
2900+ active: Platform.isPC
2901 onTriggered: root.unitySessionService.RequestShutdown()
2902 }
2903
2904
2905=== modified file 'qml/Components/ScreenGrabber.qml'
2906--- qml/Components/ScreenGrabber.qml 2015-08-24 15:39:53 +0000
2907+++ qml/Components/ScreenGrabber.qml 2015-10-26 12:04:03 +0000
2908@@ -26,6 +26,9 @@
2909 anchors.fill: parent
2910 opacity: 0.0
2911
2912+ // to be set from outside
2913+ property int rotationAngle: 0
2914+
2915 ScreenGrabber {
2916 id: screenGrabber
2917 objectName: "screenGrabber"
2918@@ -67,7 +70,7 @@
2919 to: 0.0
2920 onStopped: {
2921 if (visible) {
2922- screenGrabber.captureAndSave();
2923+ screenGrabber.captureAndSave(root.rotationAngle);
2924 visible = false;
2925 }
2926 }
2927
2928=== added file 'qml/Components/WallpaperResolver.qml'
2929--- qml/Components/WallpaperResolver.qml 1970-01-01 00:00:00 +0000
2930+++ qml/Components/WallpaperResolver.qml 2015-10-26 12:04:03 +0000
2931@@ -0,0 +1,63 @@
2932+/*
2933+ * Copyright (C) 2015 Canonical, Ltd.
2934+ *
2935+ * This program is free software; you can redistribute it and/or modify
2936+ * it under the terms of the GNU General Public License as published by
2937+ * the Free Software Foundation; version 3.
2938+ *
2939+ * This program is distributed in the hope that it will be useful,
2940+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2941+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2942+ * GNU General Public License for more details.
2943+ *
2944+ * You should have received a copy of the GNU General Public License
2945+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2946+ */
2947+
2948+import QtQuick 2.4
2949+import AccountsService 0.1
2950+import GSettings 1.0
2951+import Ubuntu.Components 1.3
2952+
2953+/*
2954+ Defines the background URL based on several factors, such as:
2955+ - default, fallback, background
2956+ - Background set in AccountSettings, if any
2957+ - Background set in GSettings, if any
2958+ */
2959+QtObject {
2960+ // Users should set their UI width here.
2961+ property real width
2962+
2963+ property url defaultBackground: Qt.resolvedUrl(width >= units.gu(60) ? "../graphics/tablet_background.jpg"
2964+ : "../graphics/phone_background.jpg")
2965+
2966+ // That's the property users of this component are going to consume.
2967+ readonly property url background: asImageTester.status == Image.Ready ? asImageTester.source
2968+ : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
2969+
2970+ // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
2971+ property var _asImageTester: Image {
2972+ id: asImageTester
2973+ source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
2974+ height: 0
2975+ width: 0
2976+ sourceSize.height: 0
2977+ sourceSize.width: 0
2978+ }
2979+
2980+ // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
2981+ property var _gsImageTester: Image {
2982+ id: gsImageTester
2983+ source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
2984+ height: 0
2985+ width: 0
2986+ sourceSize.height: 0
2987+ sourceSize.width: 0
2988+ }
2989+
2990+ property var _gsettings: GSettings {
2991+ id: backgroundSettings
2992+ schema.id: "org.gnome.desktop.background"
2993+ }
2994+}
2995
2996=== modified file 'qml/DeviceConfiguration.qml'
2997--- qml/DeviceConfiguration.qml 2015-07-01 17:52:34 +0000
2998+++ qml/DeviceConfiguration.qml 2015-10-26 12:04:03 +0000
2999@@ -86,6 +86,7 @@
3000 PropertyChanges {
3001 target: root
3002 category: "desktop"
3003+ supportedOrientations: root.useNativeOrientation
3004 }
3005 }
3006 ]
3007
3008=== added file 'qml/DisabledScreenNotice.qml'
3009--- qml/DisabledScreenNotice.qml 1970-01-01 00:00:00 +0000
3010+++ qml/DisabledScreenNotice.qml 2015-10-26 12:04:03 +0000
3011@@ -0,0 +1,49 @@
3012+/*
3013+ * Copyright (C) 2015 Canonical, Ltd.
3014+ *
3015+ * This program is free software; you can redistribute it and/or modify
3016+ * it under the terms of the GNU General Public License as published by
3017+ * the Free Software Foundation; version 3.
3018+ *
3019+ * This program is distributed in the hope that it will be useful,
3020+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3021+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3022+ * GNU General Public License for more details.
3023+ *
3024+ * You should have received a copy of the GNU General Public License
3025+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3026+ */
3027+
3028+import QtQuick 2.4
3029+import Ubuntu.Components 1.3
3030+import "Components"
3031+
3032+Image {
3033+ id: root
3034+
3035+ WallpaperResolver {
3036+ width: root.width
3037+ id: wallpaperResolver
3038+ }
3039+
3040+ source: wallpaperResolver.background
3041+
3042+ UbuntuShape {
3043+ anchors.fill: text
3044+ anchors.margins: -units.gu(2)
3045+ backgroundColor: "black"
3046+ opacity: 0.4
3047+ }
3048+
3049+ Label {
3050+ id: text
3051+ anchors.centerIn: parent
3052+ width: parent.width / 2
3053+ text: i18n.tr("Your device is now connected to an external display.")
3054+ color: "white"
3055+ horizontalAlignment: Text.AlignHCenter
3056+ verticalAlignment: Text.AlignVCenter
3057+ fontSize: "x-large"
3058+ wrapMode: Text.Wrap
3059+ }
3060+}
3061
3062=== modified file 'qml/Greeter/Greeter.qml'
3063--- qml/Greeter/Greeter.qml 2015-09-02 13:06:56 +0000
3064+++ qml/Greeter/Greeter.qml 2015-10-26 12:04:03 +0000
3065@@ -251,7 +251,7 @@
3066
3067 // event eater
3068 // Nothing should leak to items behind the greeter
3069- MouseArea { anchors.fill: parent }
3070+ MouseArea { anchors.fill: parent; hoverEnabled: true }
3071
3072 Loader {
3073 id: loader
3074
3075=== modified file 'qml/Launcher/Launcher.qml'
3076--- qml/Launcher/Launcher.qml 2015-09-02 14:18:40 +0000
3077+++ qml/Launcher/Launcher.qml 2015-10-26 12:04:03 +0000
3078@@ -188,6 +188,7 @@
3079 bottom: parent.bottom
3080 }
3081 enabled: root.shadeBackground && root.state == "visible"
3082+ visible: enabled // otherwise it will get in the way of cursor selection for some reason
3083 onPressed: {
3084 root.state = ""
3085 }
3086
3087=== modified file 'qml/Notifications/Notification.qml'
3088--- qml/Notifications/Notification.qml 2015-10-08 19:30:59 +0000
3089+++ qml/Notifications/Notification.qml 2015-10-26 12:04:03 +0000
3090@@ -50,6 +50,7 @@
3091 readonly property real contentSpacing: units.gu(2)
3092 readonly property bool canBeClosed: type === Notification.Ephemeral
3093 property bool hasMouse
3094+ property url background: ""
3095
3096 objectName: "background"
3097 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
3098@@ -401,6 +402,7 @@
3099 menuData: model
3100 menuIndex: index
3101 maxHeight: notification.maxHeight
3102+ background: notification.background
3103
3104 onLoaded: {
3105 notification.fullscreen = Qt.binding(function() { return fullscreen; });
3106
3107=== modified file 'qml/Notifications/NotificationMenuItemFactory.qml'
3108--- qml/Notifications/NotificationMenuItemFactory.qml 2014-11-17 13:46:56 +0000
3109+++ qml/Notifications/NotificationMenuItemFactory.qml 2015-10-26 12:04:03 +0000
3110@@ -30,6 +30,7 @@
3111 property int menuIndex : -1
3112 property int maxHeight
3113 readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
3114+ property url background: ""
3115
3116 signal accepted()
3117
3118@@ -149,7 +150,7 @@
3119 infoText: notification.summary
3120 errorText: errorAction.valid ? errorAction.state : ""
3121 retryText: notification.body
3122- background: shell.background
3123+ background: menuFactory.background
3124 darkenBackground: 0.4
3125
3126 onEntered: {
3127
3128=== modified file 'qml/Notifications/Notifications.qml'
3129--- qml/Notifications/Notifications.qml 2015-09-22 14:23:44 +0000
3130+++ qml/Notifications/Notifications.qml 2015-10-26 12:04:03 +0000
3131@@ -29,6 +29,7 @@
3132 property real margin
3133 property bool useModal: snapDecisionProxyModel.count > 0
3134 property bool hasMouse
3135+ property url background: ""
3136
3137 UnitySortFilterProxyModel {
3138 id: snapDecisionProxyModel
3139@@ -60,6 +61,7 @@
3140 maxHeight: notificationList.height
3141 margins: notificationList.margin
3142 hasMouse: notificationList.hasMouse
3143+ background: notificationList.background
3144
3145 // make sure there's no opacity-difference between the several
3146 // elements in a notification
3147
3148=== modified file 'qml/OrientedShell.qml'
3149--- qml/OrientedShell.qml 2015-09-22 14:23:44 +0000
3150+++ qml/OrientedShell.qml 2015-10-26 12:04:03 +0000
3151@@ -76,6 +76,12 @@
3152 oskSettings.disableHeight = shell.usageScenario == "desktop"
3153 }
3154
3155+ // we must rotate to a supported orientation regardless of shell's preference
3156+ property bool orientationChangesEnabled:
3157+ (orientation & supportedOrientations) === 0 ? true
3158+ : shell.orientationChangesEnabled
3159+
3160+
3161 Binding {
3162 target: oskSettings
3163 property: "stayHidden"
3164@@ -89,7 +95,10 @@
3165 }
3166
3167 readonly property int supportedOrientations: shell.supportedOrientations
3168- & deviceConfiguration.supportedOrientations
3169+ & (deviceConfiguration.supportedOrientations == deviceConfiguration.useNativeOrientation
3170+ ? nativeOrientation
3171+ : deviceConfiguration.supportedOrientations)
3172+
3173 property int acceptedOrientationAngle: {
3174 if (orientation & supportedOrientations) {
3175 return Screen.angleBetween(nativeOrientation, orientation);
3176
3177=== modified file 'qml/Panel/Indicators/MenuItemFactory.qml'
3178--- qml/Panel/Indicators/MenuItemFactory.qml 2015-09-17 18:02:22 +0000
3179+++ qml/Panel/Indicators/MenuItemFactory.qml 2015-10-26 12:04:03 +0000
3180@@ -20,7 +20,7 @@
3181 import QMenuModel 0.1
3182 import Utils 0.1 as Utils
3183 import Ubuntu.Components.ListItems 0.1 as ListItems
3184-import Ubuntu.Components 1.2
3185+import Ubuntu.Components 1.3
3186 import Unity.Session 0.1
3187
3188 Item {
3189@@ -350,20 +350,23 @@
3190 id: alarmMenu;
3191
3192 Menus.EventMenu {
3193+ id: alarmItem
3194 objectName: "alarmMenu"
3195 property QtObject menuData: null
3196 property var menuModel: menuFactory.menuModel
3197 property int menuIndex: -1
3198 property var extendedData: menuData && menuData.ext || undefined
3199- // TODO - bug #1260728
3200- property var timeFormatter: Utils.GDateTimeFormatter {
3201- time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
3202- format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
3203+
3204+ property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
3205+ LiveTimer {
3206+ frequency: LiveTimer.Relative
3207+ relativeTime: alarmItem.serverTime
3208+ onTrigger: alarmItem.serverTime = new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
3209 }
3210
3211 text: menuData && menuData.label || ""
3212 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
3213- time: timeFormatter.timeString
3214+ time: i18n.relativeDateTime(serverTime)
3215 enabled: menuData && menuData.sensitive || false
3216 highlightWhenPressed: false
3217
3218@@ -379,8 +382,7 @@
3219
3220 function loadAttributes() {
3221 if (!menuModel || menuIndex == -1) return;
3222- menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
3223- 'x-canonical-time-format': 'string'});
3224+ menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64'});
3225 }
3226 }
3227 }
3228@@ -389,20 +391,23 @@
3229 id: appointmentMenu;
3230
3231 Menus.EventMenu {
3232+ id: appointmentItem
3233 objectName: "appointmentMenu"
3234 property QtObject menuData: null
3235 property var menuModel: menuFactory.menuModel
3236 property int menuIndex: -1
3237 property var extendedData: menuData && menuData.ext || undefined
3238- // TODO - bug #1260728
3239- property var timeFormatter: Utils.GDateTimeFormatter {
3240- time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
3241- format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
3242+
3243+ property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
3244+ LiveTimer {
3245+ frequency: LiveTimer.Relative
3246+ relativeTime: appointmentItem.serverTime
3247+ onTrigger: appointmentItem.serverTime = new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
3248 }
3249
3250 text: menuData && menuData.label || ""
3251 iconSource: menuData && menuData.icon || "image://theme/calendar"
3252- time: timeFormatter.timeString
3253+ time: i18n.relativeDateTime(serverTime)
3254 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
3255 enabled: menuData && menuData.sensitive || false
3256 highlightWhenPressed: false
3257@@ -420,8 +425,7 @@
3258 function loadAttributes() {
3259 if (!menuModel || menuIndex == -1) return;
3260 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
3261- 'x-canonical-time': 'int64',
3262- 'x-canonical-time-format': 'string'});
3263+ 'x-canonical-time': 'int64'});
3264 }
3265 }
3266 }
3267
3268=== modified file 'qml/Panel/Indicators/MessageMenuItemFactory.qml'
3269--- qml/Panel/Indicators/MessageMenuItemFactory.qml 2015-09-15 22:50:16 +0000
3270+++ qml/Panel/Indicators/MessageMenuItemFactory.qml 2015-10-26 12:04:03 +0000
3271@@ -19,7 +19,7 @@
3272 */
3273
3274 import QtQuick 2.0
3275-import Ubuntu.Components 0.1
3276+import Ubuntu.Components 1.3
3277 import Ubuntu.Settings.Menus 0.1 as Menus
3278 import QMenuModel 0.1 as QMenuModel
3279 import Utils 0.1 as Utils
3280@@ -35,12 +35,17 @@
3281 signal menuSelected
3282 signal menuDeselected
3283
3284- property var extendedData: menuData && menuData.ext || undefined
3285- property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
3286-
3287- // TODO - bug #1260728
3288- property var timeFormatter: Utils.RelativeTimeFormatter {
3289- time: getExtendedProperty(extendedData, "xCanonicalTime", 0) / 1000000
3290+ QtObject {
3291+ id: priv
3292+ property var extendedData: menuData && menuData.ext || undefined
3293+ property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
3294+ property date time: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) / 1000)
3295+ property string timeString: i18n.relativeDateTime(time)
3296+ }
3297+ LiveTimer {
3298+ frequency: LiveTimer.Relative
3299+ relativeTime: priv.time
3300+ onTrigger: priv.timeString = Qt.binding(function() { return i18n.relativeDateTime(priv.time); })
3301 }
3302
3303 onMenuModelChanged: {
3304@@ -50,7 +55,7 @@
3305 loadAttributes();
3306 }
3307
3308- sourceComponent: loadMessage(actionsDescription)
3309+ sourceComponent: loadMessage(priv.actionsDescription)
3310
3311 function loadMessage(actions)
3312 {
3313@@ -100,11 +105,11 @@
3314 objectName: "simpleTextMessage"
3315 // text
3316 title: menuData && menuData.label || ""
3317- time: timeFormatter.timeString
3318- body: getExtendedProperty(extendedData, "xCanonicalText", "")
3319+ time: priv.timeString
3320+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
3321 // icons
3322- avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
3323- icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
3324+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
3325+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/message")
3326 // actions
3327 enabled: menuData && menuData.sensitive || false
3328 removable: !selected
3329@@ -133,7 +138,9 @@
3330 Menus.TextMessageMenu {
3331 id: message
3332 objectName: "textMessage"
3333- property var replyActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
3334+ property var replyActionDescription: priv.actionsDescription && priv.actionsDescription.length > 0 ?
3335+ priv.actionsDescription[0] :
3336+ undefined
3337
3338 property var replyAction: QMenuModel.UnityMenuAction {
3339 model: menuModel
3340@@ -143,13 +150,13 @@
3341
3342 // text
3343 title: menuData && menuData.label || ""
3344- time: timeFormatter.timeString
3345- body: getExtendedProperty(extendedData, "xCanonicalText", "")
3346- replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.tr("Send"))
3347+ time: priv.timeString
3348+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
3349+ replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.ctr("Button: Send a reply message", "Send"))
3350 replyHintText: i18n.ctr("Label: Hint in message indicator line edit", "Reply")
3351 // icons
3352- avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
3353- icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
3354+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
3355+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/message")
3356 // actions
3357 replyEnabled: replyAction.valid && replyAction.enabled
3358 enabled: menuData && menuData.sensitive || false
3359@@ -183,8 +190,10 @@
3360 Menus.SnapDecisionMenu {
3361 id: message
3362 objectName: "snapDecision"
3363- property var activateActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
3364- property var replyActionDescription: actionsDescription && actionsDescription.length > 1 ? actionsDescription[1] : undefined
3365+ property var activateActionDescription: priv.actionsDescription && priv.actionsDescription.length > 0 ?
3366+ priv.actionsDescription[0] : undefined
3367+ property var replyActionDescription: priv.actionsDescription && priv.actionsDescription.length > 1 ?
3368+ priv.actionsDescription[1] : undefined
3369
3370 property var activateAction: QMenuModel.UnityMenuAction {
3371 model: menuModel
3372@@ -199,13 +208,13 @@
3373
3374 // text
3375 title: menuData && menuData.label || ""
3376- time: timeFormatter.timeString
3377- body: getExtendedProperty(extendedData, "xCanonicalText", "")
3378- actionButtonText: getExtendedProperty(activateActionDescription, "label", i18n.tr("Call back"))
3379- replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.tr("Send"))
3380+ time: priv.timeString
3381+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
3382+ actionButtonText: getExtendedProperty(activateActionDescription, "label", i18n.ctr("Button: Call back on phone", "Call back"))
3383+ replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.ctr("Button: Send a reply message", "Send"))
3384 // icons
3385- avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
3386- icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
3387+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
3388+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
3389 // actions
3390 actionEnabled: activateAction.valid && activateAction.enabled
3391 replyEnabled: replyAction.valid && replyAction.enabled
3392
3393=== modified file 'qml/Panel/Panel.qml'
3394--- qml/Panel/Panel.qml 2015-06-29 03:58:22 +0000
3395+++ qml/Panel/Panel.qml 2015-10-26 12:04:03 +0000
3396@@ -48,6 +48,7 @@
3397 MouseArea {
3398 anchors.fill: parent
3399 onClicked: if (indicators.fullyOpened) indicators.hide();
3400+ hoverEnabled: true // should also eat hover events, otherwise they will pass through
3401 }
3402 }
3403
3404
3405=== modified file 'qml/Rotation/RotationStates.qml'
3406--- qml/Rotation/RotationStates.qml 2015-05-11 14:36:03 +0000
3407+++ qml/Rotation/RotationStates.qml 2015-10-26 12:04:03 +0000
3408@@ -83,7 +83,7 @@
3409 }
3410
3411 function tryUpdateState() {
3412- if (d.transitioning || (!d.startingUp && !root.shell.orientationChangesEnabled)) {
3413+ if (d.transitioning || (!d.startingUp && !root.orientedShell.orientationChangesEnabled)) {
3414 return;
3415 }
3416
3417@@ -95,7 +95,7 @@
3418 }
3419
3420 property Connections shellConnections: Connections {
3421- target: root.shell
3422+ target: root.orientedShell
3423 onOrientationChangesEnabledChanged: {
3424 d.tryUpdateState();
3425 }
3426
3427=== modified file 'qml/Shell.qml'
3428--- qml/Shell.qml 2015-09-22 14:23:44 +0000
3429+++ qml/Shell.qml 2015-10-26 12:04:03 +0000
3430@@ -17,7 +17,6 @@
3431 import QtQuick 2.0
3432 import QtQuick.Window 2.0
3433 import AccountsService 0.1
3434-import GSettings 1.0
3435 import Unity.Application 0.1
3436 import Ubuntu.Components 0.1
3437 import Ubuntu.Components.Popups 1.0
3438@@ -41,6 +40,7 @@
3439 import Unity.Session 0.1
3440 import Unity.DashCommunicator 0.1
3441 import Unity.Indicators 0.1 as Indicators
3442+import Cursor 1.0
3443
3444
3445 Item {
3446@@ -63,7 +63,7 @@
3447 function updateFocusedAppOrientationAnimated() {
3448 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
3449 }
3450- property bool hasMouse
3451+ property bool hasMouse: false
3452
3453 // to be read from outside
3454 readonly property int mainAppWindowOrientationAngle:
3455@@ -107,9 +107,11 @@
3456 enabled: greeter && !greeter.waiting
3457
3458 property real edgeSize: units.gu(2)
3459- property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
3460- property url background: asImageTester.status == Image.Ready ? asImageTester.source
3461- : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
3462+
3463+ WallpaperResolver {
3464+ id: wallpaperResolver
3465+ width: shell.width
3466+ }
3467
3468 readonly property alias greeter: greeterLoader.item
3469
3470@@ -130,31 +132,6 @@
3471 shell.activateApplication(app);
3472 }
3473
3474- // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
3475- Image {
3476- id: asImageTester
3477- source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
3478- height: 0
3479- width: 0
3480- sourceSize.height: 0
3481- sourceSize.width: 0
3482- }
3483-
3484- GSettings {
3485- id: backgroundSettings
3486- schema.id: "org.gnome.desktop.background"
3487- }
3488-
3489- // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
3490- Image {
3491- id: gsImageTester
3492- source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
3493- height: 0
3494- width: 0
3495- sourceSize.height: 0
3496- sourceSize.width: 0
3497- }
3498-
3499 Binding {
3500 target: LauncherModel
3501 property: "applicationManager"
3502@@ -190,11 +167,6 @@
3503 onScreenshotTriggered: screenGrabber.capture();
3504 }
3505
3506- ScreenGrabber {
3507- id: screenGrabber
3508- z: dialogs.z + 10
3509- }
3510-
3511 GlobalShortcut {
3512 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter
3513 }
3514@@ -327,7 +299,7 @@
3515 Binding {
3516 target: applicationsDisplayLoader.item
3517 property: "background"
3518- value: shell.background
3519+ value: wallpaperResolver.background
3520 }
3521 Binding {
3522 target: applicationsDisplayLoader.item
3523@@ -432,7 +404,7 @@
3524 tabletMode: shell.usageScenario != "phone"
3525 launcherOffset: launcher.progress
3526 forcedUnlock: tutorial.running
3527- background: shell.background
3528+ background: wallpaperResolver.background
3529
3530 // avoid overlapping with Launcher's edge drag area
3531 // FIXME: Fix TouchRegistry & friends and remove this workaround
3532@@ -607,7 +579,7 @@
3533 id: wizard
3534 objectName: "wizard"
3535 anchors.fill: parent
3536- background: shell.background
3537+ background: wallpaperResolver.background
3538
3539 function unlockWhenDoneWithWizard() {
3540 if (!active) {
3541@@ -638,6 +610,7 @@
3542 model: NotificationBackend.Model
3543 margin: units.gu(1)
3544 hasMouse: shell.hasMouse
3545+ background: wallpaperResolver.background
3546
3547 y: topmostIsFullscreen ? 0 : panel.panelHeight
3548 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
3549@@ -684,9 +657,21 @@
3550 onShowHome: showHome()
3551 }
3552
3553+ ScreenGrabber {
3554+ id: screenGrabber
3555+ rotationAngle: -shell.orientationAngle
3556+ z: dialogs.z + 10
3557+ }
3558+
3559+ Cursor {
3560+ id: cursor
3561+ visible: shell.hasMouse
3562+ z: screenGrabber.z + 1
3563+ }
3564+
3565 Rectangle {
3566 id: shutdownFadeOutRectangle
3567- z: screenGrabber.z + 10
3568+ z: cursor.z + 1
3569 enabled: false
3570 visible: false
3571 color: "black"
3572@@ -703,5 +688,4 @@
3573 }
3574 }
3575 }
3576-
3577 }
3578
3579=== modified file 'qml/Stages/ApplicationWindow.qml'
3580--- qml/Stages/ApplicationWindow.qml 2015-09-17 12:25:29 +0000
3581+++ qml/Stages/ApplicationWindow.qml 2015-10-26 12:04:03 +0000
3582@@ -27,6 +27,8 @@
3583 readonly property bool fullscreen: application ? application.fullscreen : false
3584 property alias interactive: sessionContainer.interactive
3585 property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
3586+ readonly property string title: sessionContainer.surface && sessionContainer.surface.name !== "" ?
3587+ sessionContainer.surface.name : d.name
3588
3589 // to be set from outside
3590 property QtObject application
3591
3592=== modified file 'qml/Stages/DecoratedWindow.qml'
3593--- qml/Stages/DecoratedWindow.qml 2015-09-08 10:32:28 +0000
3594+++ qml/Stages/DecoratedWindow.qml 2015-10-26 12:04:03 +0000
3595@@ -31,9 +31,10 @@
3596 property bool highlightShown: false
3597 property real shadowOpacity: 1
3598
3599- signal close();
3600- signal maximize();
3601- signal minimize();
3602+ signal close()
3603+ signal maximize()
3604+ signal minimize()
3605+ signal decorationPressed()
3606
3607 BorderImage {
3608 anchors {
3609@@ -61,13 +62,15 @@
3610
3611 WindowDecoration {
3612 id: decoration
3613+ target: root.parent
3614 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
3615 anchors { left: parent.left; top: parent.top; right: parent.right }
3616 height: units.gu(3)
3617- title: model.name
3618+ title: window.title !== "" ? window.title : model.name
3619 onClose: root.close();
3620 onMaximize: root.maximize();
3621 onMinimize: root.minimize();
3622+ onPressed: root.decorationPressed();
3623 visible: decorationShown
3624 }
3625
3626
3627=== modified file 'qml/Stages/DesktopStage.qml'
3628--- qml/Stages/DesktopStage.qml 2015-09-18 15:28:07 +0000
3629+++ qml/Stages/DesktopStage.qml 2015-10-26 12:04:03 +0000
3630@@ -138,9 +138,6 @@
3631 height: units.gu(50)
3632 focus: model.appId === priv.focusedAppId
3633
3634- readonly property int minWidth: units.gu(10)
3635- readonly property int minHeight: units.gu(10)
3636-
3637 property bool maximized: false
3638 property bool minimized: false
3639
3640@@ -215,12 +212,11 @@
3641 when: index == spread.highlightedIndex && blurLayer.ready
3642 }
3643
3644- WindowMoveResizeArea {
3645- id: windowMoveResizeArea
3646+ WindowResizeArea {
3647 target: appDelegate
3648- minWidth: appDelegate.minWidth
3649- minHeight: appDelegate.minHeight
3650- resizeHandleWidth: units.gu(2)
3651+ minWidth: units.gu(10)
3652+ minHeight: units.gu(10)
3653+ borderThickness: units.gu(2)
3654 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
3655
3656 onPressed: { ApplicationManager.focusApplication(model.appId) }
3657@@ -240,6 +236,7 @@
3658 onClose: ApplicationManager.stopApplication(model.appId)
3659 onMaximize: appDelegate.maximize()
3660 onMinimize: appDelegate.minimize()
3661+ onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
3662 }
3663 }
3664 }
3665
3666=== modified file 'qml/Stages/SurfaceContainer.qml'
3667--- qml/Stages/SurfaceContainer.qml 2015-09-09 13:44:12 +0000
3668+++ qml/Stages/SurfaceContainer.qml 2015-10-26 12:04:03 +0000
3669@@ -28,6 +28,7 @@
3670 property bool hadSurface: false
3671 property bool interactive
3672 property int surfaceOrientationAngle: 0
3673+ property string name: surface ? surface.name : ""
3674 property bool resizeSurface: true
3675
3676 onSurfaceChanged: {
3677
3678=== modified file 'qml/Stages/WindowDecoration.qml'
3679--- qml/Stages/WindowDecoration.qml 2015-03-13 19:18:35 +0000
3680+++ qml/Stages/WindowDecoration.qml 2015-10-26 12:04:03 +0000
3681@@ -1,5 +1,5 @@
3682 /*
3683- * Copyright (C) 2014 Canonical, Ltd.
3684+ * Copyright (C) 2014-2015 Canonical, Ltd.
3685 *
3686 * This program is free software; you can redistribute it and/or modify
3687 * it under the terms of the GNU General Public License as published by
3688@@ -12,25 +12,53 @@
3689 *
3690 * You should have received a copy of the GNU General Public License
3691 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3692- *
3693- * Authors: Michael Zanetti <michael.zanetti@canonical.com>
3694 */
3695
3696 import QtQuick 2.3
3697+import Unity.Application 0.1 // For Mir singleton
3698 import Ubuntu.Components 1.1
3699 import "../Components"
3700
3701-Item {
3702+MouseArea {
3703 id: root
3704 clip: true
3705
3706+ property Item target
3707 property alias title: titleLabel.text
3708 property bool active: false
3709+ hoverEnabled: true
3710
3711 signal close()
3712 signal minimize()
3713 signal maximize()
3714
3715+ QtObject {
3716+ id: priv
3717+ property real distanceX
3718+ property real distanceY
3719+ property bool dragging
3720+ }
3721+
3722+ onPressedChanged: {
3723+ if (pressed) {
3724+ var pos = mapToItem(root.target, mouseX, mouseY);
3725+ priv.distanceX = pos.x;
3726+ priv.distanceY = pos.y;
3727+ priv.dragging = true;
3728+ Mir.cursorName = "grabbing";
3729+ } else {
3730+ priv.dragging = false;
3731+ Mir.cursorName = "";
3732+ }
3733+ }
3734+ onPositionChanged: {
3735+ if (priv.dragging) {
3736+ var pos = mapToItem(root.target.parent, mouseX, mouseY);
3737+ root.target.x = pos.x - priv.distanceX;
3738+ root.target.y = pos.y - priv.distanceY;
3739+ }
3740+ }
3741+
3742 Rectangle {
3743 anchors.fill: parent
3744 anchors.bottomMargin: -radius
3745
3746=== renamed file 'qml/Stages/WindowMoveResizeArea.qml' => 'qml/Stages/WindowResizeArea.qml'
3747--- qml/Stages/WindowMoveResizeArea.qml 2015-09-08 10:32:28 +0000
3748+++ qml/Stages/WindowResizeArea.qml 2015-10-26 12:04:03 +0000
3749@@ -12,18 +12,19 @@
3750 *
3751 * You should have received a copy of the GNU General Public License
3752 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3753- *
3754- * Authors: Michael Zanetti <michael.zanetti@canonical.com>
3755 */
3756
3757 import QtQuick 2.3
3758 import Ubuntu.Components 1.1
3759 import Utils 0.1
3760+import Unity.Application 0.1 // for Mir.cursorName
3761
3762 MouseArea {
3763 id: root
3764 anchors.fill: target
3765- anchors.margins: -resizeHandleWidth
3766+ anchors.margins: -borderThickness
3767+
3768+ hoverEnabled: true
3769
3770 property var windowStateStorage: WindowStateStorage
3771
3772@@ -31,24 +32,10 @@
3773 // The area will anchor to it and manage move and resize events
3774 property Item target: null
3775 property string windowId: ""
3776- property int resizeHandleWidth: 0
3777+ property int borderThickness: 0
3778 property int minWidth: 0
3779 property int minHeight: 0
3780
3781- QtObject {
3782- id: priv
3783- readonly property int windowWidth: root.width - root.resizeHandleWidth * 2
3784- readonly property int windowHeight: root.height - resizeHandleWidth * 2
3785-
3786- property var startPoint
3787-
3788- property bool resizeTop: false
3789- property bool resizeBottom: false
3790- property bool resizeLeft: false
3791- property bool resizeRight: false
3792-
3793- }
3794-
3795 Component.onCompleted: {
3796 var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
3797 if (windowState !== undefined) {
3798@@ -59,51 +46,137 @@
3799 }
3800 }
3801
3802- onPressed: {
3803- priv.startPoint = Qt.point(mouse.x, mouse.y);
3804- priv.resizeTop = mouseY < root.resizeHandleWidth;
3805- priv.resizeBottom = mouseY > (root.height - root.resizeHandleWidth);
3806- priv.resizeLeft = mouseX < root.resizeHandleWidth;
3807- priv.resizeRight = mouseX > (root.width - root.resizeHandleWidth);
3808- }
3809-
3810- onPositionChanged: {
3811- var currentPoint = Qt.point(mouse.x, mouse.y);
3812- var mouseDiff = Qt.point(currentPoint.x - priv.startPoint.x, currentPoint.y - priv.startPoint.y);
3813- var moveDiff = Qt.point(0, 0);
3814- var sizeDiff = Qt.point(0, 0);
3815- var maxSizeDiff = Qt.point(root.minWidth - root.target.width, root.minHeight - root.target.height)
3816-
3817- if (priv.resizeTop || priv.resizeBottom || priv.resizeLeft || priv.resizeRight) {
3818- if (priv.resizeTop) {
3819- sizeDiff.y = Math.max(maxSizeDiff.y, -currentPoint.y + priv.startPoint.y)
3820- moveDiff.y = -sizeDiff.y
3821- }
3822- if (priv.resizeBottom) {
3823- sizeDiff.y = Math.max(maxSizeDiff.y, currentPoint.y - priv.startPoint.y)
3824- priv.startPoint.y += sizeDiff.y
3825- }
3826- if (priv.resizeLeft) {
3827- sizeDiff.x = Math.max(maxSizeDiff.x, -currentPoint.x + priv.startPoint.x)
3828- moveDiff.x = -sizeDiff.x
3829- }
3830- if (priv.resizeRight) {
3831- sizeDiff.x = Math.max(maxSizeDiff.x, currentPoint.x - priv.startPoint.x)
3832- priv.startPoint.x += sizeDiff.x
3833- }
3834-
3835- target.x += moveDiff.x;
3836- target.y += moveDiff.y;
3837- target.width += sizeDiff.x;
3838- target.height += sizeDiff.y;
3839- } else {
3840- target.x += mouseDiff.x;
3841- target.y += mouseDiff.y;
3842- }
3843-
3844- }
3845-
3846 Component.onDestruction: {
3847 windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
3848 }
3849+
3850+ QtObject {
3851+ id: d
3852+ property bool leftBorder: false
3853+ property bool rightBorder: false
3854+ property bool topBorder: false
3855+ property bool bottomBorder: false
3856+
3857+ property bool dragging: false
3858+ property real startMousePosX
3859+ property real startMousePosY
3860+ property real startX
3861+ property real startY
3862+ property real startWidth
3863+ property real startHeight
3864+
3865+ property string cursorName: {
3866+ if (root.containsMouse || root.pressed) {
3867+ if (leftBorder && !topBorder && !bottomBorder) {
3868+ return "left_side";
3869+ } else if (rightBorder && !topBorder && !bottomBorder) {
3870+ return "right_side";
3871+ } else if (topBorder && !leftBorder && !rightBorder) {
3872+ return "top_side";
3873+ } else if (bottomBorder && !leftBorder && !rightBorder) {
3874+ return "bottom_side";
3875+ } else if (leftBorder && topBorder) {
3876+ return "top_left_corner";
3877+ } else if (leftBorder && bottomBorder) {
3878+ return "bottom_left_corner";
3879+ } else if (rightBorder && topBorder) {
3880+ return "top_right_corner";
3881+ } else if (rightBorder && bottomBorder) {
3882+ return "bottom_right_corner";
3883+ } else {
3884+ return "";
3885+ }
3886+ } else {
3887+ return "";
3888+ }
3889+ }
3890+ onCursorNameChanged: {
3891+ Mir.cursorName = cursorName;
3892+ }
3893+
3894+ function updateBorders() {
3895+ leftBorder = mouseX <= borderThickness;
3896+ rightBorder = mouseX >= width - borderThickness;
3897+ topBorder = mouseY <= borderThickness;
3898+ bottomBorder = mouseY >= height - borderThickness;
3899+ }
3900+ }
3901+
3902+ onPressedChanged: {
3903+ var pos = mapToItem(target.parent, mouseX, mouseY);
3904+
3905+ if (pressed) {
3906+ d.updateBorders();
3907+ var pos = mapToItem(root.target.parent, mouseX, mouseY);
3908+ d.startMousePosX = pos.x;
3909+ d.startMousePosY = pos.y;
3910+ d.startX = target.x;
3911+ d.startY = target.y;
3912+ d.startWidth = target.width;
3913+ d.startHeight = target.height;
3914+ d.dragging = true;
3915+ } else {
3916+ d.dragging = false;
3917+ if (containsMouse) {
3918+ d.updateBorders();
3919+ }
3920+ }
3921+ }
3922+
3923+ onEntered: {
3924+ if (!pressed) {
3925+ d.updateBorders();
3926+ }
3927+ }
3928+
3929+ onPositionChanged: {
3930+ if (!pressed) {
3931+ d.updateBorders();
3932+ }
3933+
3934+ if (!d.dragging) {
3935+ return;
3936+ }
3937+
3938+ var pos = mapToItem(target.parent, mouse.x, mouse.y);
3939+
3940+ var deltaX = pos.x - d.startMousePosX;
3941+ var deltaY = pos.y - d.startMousePosY;
3942+
3943+ if (d.leftBorder) {
3944+ var newTargetX = d.startX + deltaX;
3945+ if (target.x + target.width > newTargetX + minWidth) {
3946+ target.width = target.x + target.width - newTargetX;
3947+ target.x = newTargetX;
3948+ } else {
3949+ target.x = target.x + target.width - minWidth;
3950+ target.width = minWidth;
3951+ }
3952+
3953+ } else if (d.rightBorder) {
3954+ if (d.startWidth + deltaX >= minWidth) {
3955+ target.width = d.startWidth + deltaX;
3956+ } else {
3957+ target.width = minWidth;
3958+ }
3959+ }
3960+
3961+ if (d.topBorder) {
3962+ var newTargetY = d.startY + deltaY;
3963+ if (target.y + target.height > newTargetY + minHeight) {
3964+ target.height = target.y + target.height - newTargetY;
3965+ target.y = newTargetY;
3966+ } else {
3967+ target.y = target.y + target.height - minHeight;
3968+ target.height = minHeight;
3969+ }
3970+
3971+ } else if (d.bottomBorder) {
3972+ if (d.startHeight + deltaY >= minHeight) {
3973+ target.height = d.startHeight + deltaY;
3974+ } else {
3975+ target.height = minHeight;
3976+ }
3977+ }
3978+ }
3979 }
3980
3981=== modified file 'src/ApplicationArguments.h'
3982--- src/ApplicationArguments.h 2015-09-14 09:11:08 +0000
3983+++ src/ApplicationArguments.h 2015-10-26 12:04:03 +0000
3984@@ -26,17 +26,25 @@
3985 class ApplicationArguments : public QObject
3986 {
3987 Q_OBJECT
3988- Q_PROPERTY(QString deviceName READ deviceName CONSTANT)
3989+ Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
3990 Q_PROPERTY(QString mode READ mode CONSTANT)
3991 public:
3992 ApplicationArguments(QObject *parent = nullptr);
3993
3994- void setDeviceName(const QString &deviceName) { m_deviceName = deviceName; }
3995+ void setDeviceName(const QString &deviceName) {
3996+ if (deviceName != m_deviceName) {
3997+ m_deviceName = deviceName;
3998+ Q_EMIT deviceNameChanged(m_deviceName);
3999+ }
4000+ }
4001 QString deviceName() const { return m_deviceName; }
4002
4003 void setMode(const QString &mode) { m_mode = mode; }
4004 QString mode() const { return m_mode; }
4005
4006+Q_SIGNALS:
4007+ void deviceNameChanged(const QString&);
4008+
4009 private:
4010 QString m_deviceName;
4011 QString m_mode;
4012
4013=== modified file 'src/CMakeLists.txt'
4014--- src/CMakeLists.txt 2015-06-24 11:41:09 +0000
4015+++ src/CMakeLists.txt 2015-10-26 12:04:03 +0000
4016@@ -21,6 +21,9 @@
4017 main.cpp
4018 MouseTouchAdaptor.cpp
4019 CachingNetworkManagerFactory.cpp
4020+ SecondaryWindow.cpp
4021+ ShellApplication.cpp
4022+ ShellView.cpp
4023 UnityCommandLineParser.cpp
4024 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
4025 )
4026
4027=== added file 'src/SecondaryWindow.cpp'
4028--- src/SecondaryWindow.cpp 1970-01-01 00:00:00 +0000
4029+++ src/SecondaryWindow.cpp 2015-10-26 12:04:03 +0000
4030@@ -0,0 +1,31 @@
4031+/*
4032+ * Copyright (C) 2015 Canonical, Ltd.
4033+ *
4034+ * This program is free software; you can redistribute it and/or modify
4035+ * it under the terms of the GNU General Public License as published by
4036+ * the Free Software Foundation; version 3.
4037+ *
4038+ * This program is distributed in the hope that it will be useful,
4039+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4040+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4041+ * GNU General Public License for more details.
4042+ *
4043+ * You should have received a copy of the GNU General Public License
4044+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4045+ */
4046+
4047+#include "SecondaryWindow.h"
4048+
4049+// local
4050+#include <paths.h>
4051+
4052+SecondaryWindow::SecondaryWindow(QQmlEngine *engine)
4053+ : QQuickView(engine, nullptr)
4054+{
4055+ setResizeMode(QQuickView::SizeRootObjectToView);
4056+ setColor("black");
4057+ setTitle(QStringLiteral("Unity8 Shell - Secondary Screen"));
4058+
4059+ QUrl source(::qmlDirectory() + "/DisabledScreenNotice.qml");
4060+ setSource(source);
4061+}
4062
4063=== added file 'src/SecondaryWindow.h'
4064--- src/SecondaryWindow.h 1970-01-01 00:00:00 +0000
4065+++ src/SecondaryWindow.h 2015-10-26 12:04:03 +0000
4066@@ -0,0 +1,30 @@
4067+/*
4068+ * Copyright (C) 2015 Canonical, Ltd.
4069+ *
4070+ * This program is free software; you can redistribute it and/or modify
4071+ * it under the terms of the GNU General Public License as published by
4072+ * the Free Software Foundation; version 3.
4073+ *
4074+ * This program is distributed in the hope that it will be useful,
4075+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4076+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4077+ * GNU General Public License for more details.
4078+ *
4079+ * You should have received a copy of the GNU General Public License
4080+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4081+ */
4082+
4083+#ifndef UNITY_SECONDARY_WINDOW_H
4084+#define UNITY_SECONDARY_WINDOW_H
4085+
4086+#include <QQuickView>
4087+
4088+class SecondaryWindow : public QQuickView
4089+{
4090+ Q_OBJECT
4091+
4092+public:
4093+ SecondaryWindow(QQmlEngine *engine);
4094+};
4095+
4096+#endif // UNITY_SECONDARY_WINDOW_H
4097
4098=== added file 'src/ShellApplication.cpp'
4099--- src/ShellApplication.cpp 1970-01-01 00:00:00 +0000
4100+++ src/ShellApplication.cpp 2015-10-26 12:04:03 +0000
4101@@ -0,0 +1,197 @@
4102+/*
4103+ * Copyright (C) 2015 Canonical, Ltd.
4104+ *
4105+ * This program is free software; you can redistribute it and/or modify
4106+ * it under the terms of the GNU General Public License as published by
4107+ * the Free Software Foundation; version 3.
4108+ *
4109+ * This program is distributed in the hope that it will be useful,
4110+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4111+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4112+ * GNU General Public License for more details.
4113+ *
4114+ * You should have received a copy of the GNU General Public License
4115+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4116+ */
4117+
4118+#include "ShellApplication.h"
4119+
4120+// Qt
4121+#include <QLibrary>
4122+#include <QScreen>
4123+
4124+#include <libintl.h>
4125+
4126+// libandroid-properties
4127+#include <hybris/properties/properties.h>
4128+
4129+// local
4130+#include <paths.h>
4131+#include "CachingNetworkManagerFactory.h"
4132+#include "MouseTouchAdaptor.h"
4133+#include "UnityCommandLineParser.h"
4134+
4135+ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
4136+ : QGuiApplication(argc, argv)
4137+ , m_shellView(nullptr)
4138+ , m_secondaryWindow(nullptr)
4139+ , m_mouseTouchAdaptor(nullptr)
4140+ , m_qmlEngine(nullptr)
4141+{
4142+
4143+ setApplicationName(QStringLiteral("unity8"));
4144+
4145+ connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
4146+
4147+ setupQmlEngine(isMirServer);
4148+
4149+ UnityCommandLineParser parser(*this);
4150+
4151+ if (!parser.deviceName().isEmpty()) {
4152+ m_deviceName = parser.deviceName();
4153+ } else {
4154+ char buffer[200];
4155+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
4156+ m_deviceName = QString(buffer);
4157+ }
4158+ m_qmlArgs.setDeviceName(m_deviceName);
4159+
4160+ m_qmlArgs.setMode(parser.mode());
4161+
4162+ // The testability driver is only loaded by QApplication but not by QGuiApplication.
4163+ // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
4164+ if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
4165+ QLibrary testLib(QStringLiteral("qttestability"));
4166+ if (testLib.load()) {
4167+ typedef void (*TasInitialize)(void);
4168+ TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
4169+ if (initFunction) {
4170+ initFunction();
4171+ } else {
4172+ qCritical("Library qttestability resolve failed!");
4173+ }
4174+ } else {
4175+ qCritical("Library qttestability load failed!");
4176+ }
4177+ }
4178+
4179+ bindtextdomain("unity8", translationDirectory().toUtf8().data());
4180+ textdomain("unity8");
4181+
4182+ m_shellView = new ShellView(m_qmlEngine, &m_qmlArgs);
4183+
4184+ if (parser.windowGeometry().isValid()) {
4185+ m_shellView->setWidth(parser.windowGeometry().width());
4186+ m_shellView->setHeight(parser.windowGeometry().height());
4187+ }
4188+
4189+ if (parser.hasFrameless()) {
4190+ m_shellView->setFlags(Qt::FramelessWindowHint);
4191+ }
4192+
4193+ // You will need this if you want to interact with touch-only components using a mouse
4194+ // Needed only when manually testing on a desktop.
4195+ if (parser.hasMouseToTouch()) {
4196+ m_mouseTouchAdaptor = MouseTouchAdaptor::instance();
4197+ }
4198+
4199+
4200+ // Some hard-coded policy for now.
4201+ // NB: We don't support more than two screens at the moment
4202+ //
4203+ // TODO: Support an arbitrary number of screens and different policies
4204+ // (eg cloned desktop, several desktops, etc)
4205+ if (isMirServer && screens().count() == 2) {
4206+ m_shellView->setScreen(screens().at(1));
4207+ m_qmlArgs.setDeviceName("desktop");
4208+
4209+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
4210+ m_secondaryWindow->setScreen(screens().at(0));
4211+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
4212+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
4213+ m_secondaryWindow->setVisible(true);
4214+ }
4215+
4216+ if (isMirServer || parser.hasFullscreen()) {
4217+ m_shellView->showFullScreen();
4218+ } else {
4219+ m_shellView->show();
4220+ }
4221+}
4222+
4223+ShellApplication::~ShellApplication()
4224+{
4225+ destroyResources();
4226+}
4227+
4228+void ShellApplication::destroyResources()
4229+{
4230+ // Deletion order is important. Don't use QScopedPointers and the like
4231+ // Otherwise the process will hang on shutdown (bug somewhere I guess).
4232+ delete m_shellView;
4233+ m_shellView = nullptr;
4234+
4235+ delete m_secondaryWindow;
4236+ m_secondaryWindow = nullptr;
4237+
4238+ delete m_mouseTouchAdaptor;
4239+ m_mouseTouchAdaptor = nullptr;
4240+
4241+ delete m_qmlEngine;
4242+ m_qmlEngine = nullptr;
4243+}
4244+
4245+void ShellApplication::setupQmlEngine(bool isMirServer)
4246+{
4247+ m_qmlEngine = new QQmlEngine(this);
4248+
4249+ m_qmlEngine->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
4250+
4251+ prependImportPaths(m_qmlEngine, ::overrideImportPaths());
4252+ if (!isMirServer) {
4253+ prependImportPaths(m_qmlEngine, ::nonMirImportPaths());
4254+ }
4255+ appendImportPaths(m_qmlEngine, ::fallbackImportPaths());
4256+
4257+ m_qmlEngine->setNetworkAccessManagerFactory(new CachingNetworkManagerFactory);
4258+
4259+ QObject::connect(m_qmlEngine, &QQmlEngine::quit, this, &QGuiApplication::quit);
4260+}
4261+
4262+void ShellApplication::onScreenAdded(QScreen * /*screen*/)
4263+{
4264+ // TODO: Support an arbitrary number of screens and different policies
4265+ // (eg cloned desktop, several desktops, etc)
4266+ if (screens().count() == 2) {
4267+ m_shellView->setScreen(screens().at(1));
4268+ m_qmlArgs.setDeviceName("desktop");
4269+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
4270+ // its backing QPlatformWindow recreated). So lets refocus it.
4271+ m_shellView->requestActivate();
4272+
4273+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
4274+ m_secondaryWindow->setScreen(screens().at(0));
4275+
4276+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
4277+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
4278+ m_secondaryWindow->setVisible(true);
4279+ }
4280+}
4281+
4282+void ShellApplication::onScreenAboutToBeRemoved(QScreen *screen)
4283+{
4284+ // TODO: Support an arbitrary number of screens and different policies
4285+ // (eg cloned desktop, several desktops, etc)
4286+ if (screen == m_shellView->screen()) {
4287+ Q_ASSERT(screens().count() > 1);
4288+ Q_ASSERT(screens().at(0) != screen);
4289+ Q_ASSERT(m_secondaryWindow);
4290+ delete m_secondaryWindow;
4291+ m_secondaryWindow = nullptr;
4292+ m_shellView->setScreen(screens().first());
4293+ m_qmlArgs.setDeviceName(m_deviceName);
4294+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
4295+ // its backing QPlatformWindow recreated). So lets refocus it.
4296+ m_shellView->requestActivate();
4297+ }
4298+}
4299
4300=== added file 'src/ShellApplication.h'
4301--- src/ShellApplication.h 1970-01-01 00:00:00 +0000
4302+++ src/ShellApplication.h 2015-10-26 12:04:03 +0000
4303@@ -0,0 +1,55 @@
4304+/*
4305+ * Copyright (C) 2015 Canonical, Ltd.
4306+ *
4307+ * This program is free software; you can redistribute it and/or modify
4308+ * it under the terms of the GNU General Public License as published by
4309+ * the Free Software Foundation; version 3.
4310+ *
4311+ * This program is distributed in the hope that it will be useful,
4312+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4313+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4314+ * GNU General Public License for more details.
4315+ *
4316+ * You should have received a copy of the GNU General Public License
4317+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4318+ */
4319+
4320+#ifndef SHELLAPPLICATION_H
4321+#define SHELLAPPLICATION_H
4322+
4323+#include <QGuiApplication>
4324+#include <QQmlEngine>
4325+#include <QQuickView>
4326+#include <QScopedPointer>
4327+
4328+#include "ApplicationArguments.h"
4329+#include "MouseTouchAdaptor.h"
4330+#include "SecondaryWindow.h"
4331+#include "ShellView.h"
4332+
4333+class ShellApplication : public QGuiApplication
4334+{
4335+ Q_OBJECT
4336+public:
4337+ ShellApplication(int & argc, char ** argv, bool isMirServer);
4338+ virtual ~ShellApplication();
4339+
4340+ void destroyResources();
4341+public Q_SLOTS:
4342+ // called by qtmir
4343+ void onScreenAboutToBeRemoved(QScreen *screen);
4344+
4345+private Q_SLOTS:
4346+ void onScreenAdded(QScreen*);
4347+
4348+private:
4349+ void setupQmlEngine(bool isMirServer);
4350+ QString m_deviceName;
4351+ ApplicationArguments m_qmlArgs;
4352+ ShellView *m_shellView;
4353+ SecondaryWindow *m_secondaryWindow;
4354+ MouseTouchAdaptor *m_mouseTouchAdaptor;
4355+ QQmlEngine *m_qmlEngine;
4356+};
4357+
4358+#endif // SHELLAPPLICATION_H
4359
4360=== added file 'src/ShellView.cpp'
4361--- src/ShellView.cpp 1970-01-01 00:00:00 +0000
4362+++ src/ShellView.cpp 2015-10-26 12:04:03 +0000
4363@@ -0,0 +1,59 @@
4364+/*
4365+ * Copyright (C) 2015 Canonical, Ltd.
4366+ *
4367+ * This program is free software; you can redistribute it and/or modify
4368+ * it under the terms of the GNU General Public License as published by
4369+ * the Free Software Foundation; version 3.
4370+ *
4371+ * This program is distributed in the hope that it will be useful,
4372+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4373+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4374+ * GNU General Public License for more details.
4375+ *
4376+ * You should have received a copy of the GNU General Public License
4377+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4378+ */
4379+
4380+#include "ShellView.h"
4381+
4382+// Qt
4383+#include <QQmlContext>
4384+#include <QQuickItem>
4385+
4386+// local
4387+#include <paths.h>
4388+
4389+ShellView::ShellView(QQmlEngine *engine, QObject *qmlArgs)
4390+ : QQuickView(engine, nullptr)
4391+{
4392+ setResizeMode(QQuickView::SizeRootObjectToView);
4393+ setColor("black");
4394+ setTitle(QStringLiteral("Unity8"));
4395+
4396+ rootContext()->setContextProperty(QStringLiteral("applicationArguments"), qmlArgs);
4397+
4398+ QUrl source(::qmlDirectory() + "/OrientedShell.qml");
4399+ setSource(source);
4400+
4401+ connect(this, &QWindow::widthChanged, this, &ShellView::onWidthChanged);
4402+ connect(this, &QWindow::heightChanged, this, &ShellView::onHeightChanged);
4403+}
4404+
4405+void ShellView::onWidthChanged(int w)
4406+{
4407+ // For good measure in case SizeRootObjectToView doesn't fulfill its promise.
4408+ //
4409+ // There's at least one situation that's know to leave the root object with an outdated size.
4410+ // (really looks like Qt bug)
4411+ // Happens when starting unity8 with an external monitor already connected.
4412+ // The QResizeEvent we get still has the size of the first screen and since the resize move is triggered
4413+ // from the resize event handler, the root item doesn't get resized.
4414+ // TODO: Confirm the Qt bug and submit a patch upstream
4415+ rootObject()->setWidth(w);
4416+}
4417+
4418+void ShellView::onHeightChanged(int h)
4419+{
4420+ // See comment in ShellView::onWidthChanged()
4421+ rootObject()->setHeight(h);
4422+}
4423
4424=== added file 'src/ShellView.h'
4425--- src/ShellView.h 1970-01-01 00:00:00 +0000
4426+++ src/ShellView.h 2015-10-26 12:04:03 +0000
4427@@ -0,0 +1,34 @@
4428+/*
4429+ * Copyright (C) 2015 Canonical, Ltd.
4430+ *
4431+ * This program is free software; you can redistribute it and/or modify
4432+ * it under the terms of the GNU General Public License as published by
4433+ * the Free Software Foundation; version 3.
4434+ *
4435+ * This program is distributed in the hope that it will be useful,
4436+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4437+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4438+ * GNU General Public License for more details.
4439+ *
4440+ * You should have received a copy of the GNU General Public License
4441+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4442+ */
4443+
4444+#ifndef UNITY_SHELL_VIEW_H
4445+#define UNITY_SHELL_VIEW_H
4446+
4447+#include <QQuickView>
4448+
4449+class ShellView : public QQuickView
4450+{
4451+ Q_OBJECT
4452+
4453+public:
4454+ ShellView(QQmlEngine *engine, QObject *qmlArgs);
4455+
4456+private Q_SLOTS:
4457+ void onWidthChanged(int);
4458+ void onHeightChanged(int);
4459+};
4460+
4461+#endif // UNITY_SHELL_VIEW_H
4462
4463=== modified file 'src/main.cpp'
4464--- src/main.cpp 2015-09-23 15:14:01 +0000
4465+++ src/main.cpp 2015-10-26 12:04:03 +0000
4466@@ -14,26 +14,8 @@
4467 * along with this program. If not, see <http://www.gnu.org/licenses/>.
4468 */
4469
4470-// Qt
4471-#include <QCommandLineParser>
4472-#include <QtQuick/QQuickView>
4473-#include <QtGui/QGuiApplication>
4474-#include <QtQml/QQmlEngine>
4475-#include <QtQml/QQmlContext>
4476-#include <QLibrary>
4477-#include <QDebug>
4478-#include <csignal>
4479-#include <libintl.h>
4480-
4481-// libandroid-properties
4482-#include <hybris/properties/properties.h>
4483-
4484 // local
4485-#include <paths.h>
4486-#include "MouseTouchAdaptor.h"
4487-#include "ApplicationArguments.h"
4488-#include "CachingNetworkManagerFactory.h"
4489-#include "UnityCommandLineParser.h"
4490+#include "ShellApplication.h"
4491
4492 int main(int argc, const char *argv[])
4493 {
4494@@ -43,91 +25,12 @@
4495 isMirServer = true;
4496 }
4497
4498- QGuiApplication::setApplicationName(QStringLiteral("unity8"));
4499- QGuiApplication *application;
4500-
4501- application = new QGuiApplication(argc, (char**)argv);
4502-
4503- UnityCommandLineParser parser(*application);
4504-
4505- ApplicationArguments qmlArgs;
4506-
4507- if (!parser.deviceName().isEmpty()) {
4508- qmlArgs.setDeviceName(parser.deviceName());
4509- } else {
4510- char buffer[200];
4511- property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
4512- qmlArgs.setDeviceName(QString(buffer));
4513- }
4514-
4515- qmlArgs.setMode(parser.mode());
4516-
4517- // The testability driver is only loaded by QApplication but not by QGuiApplication.
4518- // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
4519- if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
4520- QLibrary testLib(QStringLiteral("qttestability"));
4521- if (testLib.load()) {
4522- typedef void (*TasInitialize)(void);
4523- TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
4524- if (initFunction) {
4525- initFunction();
4526- } else {
4527- qCritical("Library qttestability resolve failed!");
4528- }
4529- } else {
4530- qCritical("Library qttestability load failed!");
4531- }
4532- }
4533-
4534- bindtextdomain("unity8", translationDirectory().toUtf8().data());
4535- textdomain("unity8");
4536-
4537- QQuickView* view = new QQuickView();
4538- view->setResizeMode(QQuickView::SizeRootObjectToView);
4539- view->setColor("black");
4540- view->setTitle(QStringLiteral("Unity8 Shell"));
4541-
4542- if (parser.windowGeometry().isValid()) {
4543- view->setWidth(parser.windowGeometry().width());
4544- view->setHeight(parser.windowGeometry().height());
4545- }
4546-
4547- view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
4548- view->rootContext()->setContextProperty(QStringLiteral("applicationArguments"), &qmlArgs);
4549- if (parser.hasFrameless()) {
4550- view->setFlags(Qt::FramelessWindowHint);
4551- }
4552-
4553- // You will need this if you want to interact with touch-only components using a mouse
4554- // Needed only when manually testing on a desktop.
4555- MouseTouchAdaptor *mouseTouchAdaptor = 0;
4556- if (parser.hasMouseToTouch()) {
4557- mouseTouchAdaptor = MouseTouchAdaptor::instance();
4558- }
4559-
4560- QUrl source(::qmlDirectory() + "/OrientedShell.qml");
4561- prependImportPaths(view->engine(), ::overrideImportPaths());
4562- if (!isMirServer) {
4563- prependImportPaths(view->engine(), ::nonMirImportPaths());
4564- }
4565- appendImportPaths(view->engine(), ::fallbackImportPaths());
4566-
4567- CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
4568- view->engine()->setNetworkAccessManagerFactory(managerFactory);
4569-
4570- view->setSource(source);
4571- QObject::connect(view->engine(), &QQmlEngine::quit, application, &QGuiApplication::quit);
4572-
4573- if (isMirServer || parser.hasFullscreen()) {
4574- view->showFullScreen();
4575- } else {
4576- view->show();
4577- }
4578+ ShellApplication *application = new ShellApplication(argc, (char**)argv, isMirServer);
4579
4580 int result = application->exec();
4581
4582- delete view;
4583- delete mouseTouchAdaptor;
4584+ application->destroyResources();
4585+
4586 delete application;
4587
4588 return result;
4589
4590=== modified file 'tests/autopilot/unity8/fixture_setup.py'
4591--- tests/autopilot/unity8/fixture_setup.py 2015-09-21 12:50:10 +0000
4592+++ tests/autopilot/unity8/fixture_setup.py 2015-10-26 12:04:03 +0000
4593@@ -133,7 +133,7 @@
4594 args = [binary_arg] + env_args
4595 self.unity_proxy = process_helpers.restart_unity_with_testability(
4596 *args)
4597- self.main_win = self.unity_proxy.select_single(shell.QQuickView)
4598+ self.main_win = self.unity_proxy.select_single(shell.ShellView)
4599
4600 def _create_sensors(self):
4601 # Wait for unity to start running.
4602
4603=== modified file 'tests/autopilot/unity8/greeter/tests/__init__.py'
4604--- tests/autopilot/unity8/greeter/tests/__init__.py 2015-04-29 19:21:18 +0000
4605+++ tests/autopilot/unity8/greeter/tests/__init__.py 2015-10-26 12:04:03 +0000
4606@@ -24,5 +24,5 @@
4607 class GreeterTestCase(UnityTestCase):
4608 def get_shell(self, unity_proxy):
4609 main_window = (
4610- unity_proxy.select_single(shell.QQuickView))
4611+ unity_proxy.select_single(shell.ShellView))
4612 return main_window.select_single('Shell')
4613
4614=== modified file 'tests/autopilot/unity8/launcher.py'
4615--- tests/autopilot/unity8/launcher.py 2015-04-29 19:21:18 +0000
4616+++ tests/autopilot/unity8/launcher.py 2015-10-26 12:04:03 +0000
4617@@ -42,7 +42,7 @@
4618 logger.debug('The launcher is already opened.')
4619
4620 def _swipe_to_show_launcher(self):
4621- view = self.get_root_instance().select_single('QQuickView')
4622+ view = self.get_root_instance().select_single('ShellView')
4623 start_y = stop_y = view.y + view.height // 2
4624
4625 start_x = view.x + 1
4626
4627=== modified file 'tests/autopilot/unity8/settings_wizard/__init__.py'
4628--- tests/autopilot/unity8/settings_wizard/__init__.py 2015-05-21 16:52:05 +0000
4629+++ tests/autopilot/unity8/settings_wizard/__init__.py 2015-10-26 12:04:03 +0000
4630@@ -375,12 +375,17 @@
4631 wizard = get_wizard(self)
4632 next_page = wizard.get_current_page()
4633 locationPageEnabled = True
4634+ reportingPageEnabled = True
4635 if next_page.objectName == 'locationPage':
4636 next_page = wizard.get_location_page()
4637 else:
4638 locationPageEnabled = False
4639- next_page = wizard.get_reporting_page()
4640- return locationPageEnabled, next_page
4641+ if next_page.objectName == 'reportingPage':
4642+ next_page = wizard.get_reporting_page()
4643+ else:
4644+ reportingPageEnabled = False
4645+ next_page = wizard.get_finished_page()
4646+ return locationPageEnabled, reportingPageEnabled, next_page
4647
4648 def _get_notification(self, unity):
4649 logger.info('Waiting longer for notification object')
4650
4651=== modified file 'tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py'
4652--- tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py 2015-05-21 16:37:36 +0000
4653+++ tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py 2015-10-26 12:04:03 +0000
4654@@ -80,14 +80,19 @@
4655 password_page = next_page
4656 wifi_connect_page = self._test_password_page(password_page)
4657
4658- locationPageEnabled, next_page = self._test_wifi_connect_page(
4659- wifi_connect_page)
4660+ reporting_page = None
4661+ locationPageEnabled, reportingPageEnabled, next_page = self._test_wifi_connect_page(wifi_connect_page)
4662 if locationPageEnabled:
4663 location_page = next_page
4664- reporting_page = self._test_location_page(location_page)
4665+ if reportingPageEnabled:
4666+ reporting_page = self._test_location_page(location_page)
4667+ else:
4668+ finish_page = next_page
4669+
4670+ if reporting_page is not None:
4671+ finish_page = self._test_reporting_page(reporting_page)
4672 else:
4673- reporting_page = next_page
4674- finish_page = self._test_reporting_page(reporting_page)
4675+ finish_page = next_page
4676
4677 finish_page.finish()
4678 self.assertFalse(
4679
4680=== modified file 'tests/autopilot/unity8/shell/__init__.py'
4681--- tests/autopilot/unity8/shell/__init__.py 2015-06-22 15:47:34 +0000
4682+++ tests/autopilot/unity8/shell/__init__.py 2015-10-26 12:04:03 +0000
4683@@ -93,7 +93,7 @@
4684 return _urgency_enums.get(urgency.upper())
4685
4686
4687-class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
4688+class ShellView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
4689 """An helper class that makes it easy to interact with the shell"""
4690
4691 def get_greeter(self):
4692
4693=== modified file 'tests/autopilot/unity8/shell/tests/__init__.py'
4694--- tests/autopilot/unity8/shell/tests/__init__.py 2015-09-21 12:50:10 +0000
4695+++ tests/autopilot/unity8/shell/tests/__init__.py 2015-10-26 12:04:03 +0000
4696@@ -297,7 +297,7 @@
4697
4698 @property
4699 def main_window(self):
4700- return self._proxy.select_single(shell.QQuickView)
4701+ return self._proxy.select_single(shell.ShellView)
4702
4703
4704 class DashBaseTestCase(AutopilotTestCase):
4705
4706=== modified file 'tests/mocks/CMakeLists.txt'
4707--- tests/mocks/CMakeLists.txt 2015-08-06 22:45:56 +0000
4708+++ tests/mocks/CMakeLists.txt 2015-10-26 12:04:03 +0000
4709@@ -29,6 +29,7 @@
4710 endmacro()
4711
4712 add_subdirectory(AccountsService)
4713+add_subdirectory(Cursor)
4714 add_subdirectory(GSettings.1.0)
4715 add_subdirectory(indicator-service)
4716 add_subdirectory(libusermetrics)
4717
4718=== added directory 'tests/mocks/Cursor'
4719=== added file 'tests/mocks/Cursor/CMakeLists.txt'
4720--- tests/mocks/Cursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
4721+++ tests/mocks/Cursor/CMakeLists.txt 2015-10-26 12:04:03 +0000
4722@@ -0,0 +1,1 @@
4723+add_unity8_mock(Cursor 1.0 Cursor PREFIX mocks)
4724
4725=== added file 'tests/mocks/Cursor/Cursor.qml'
4726--- tests/mocks/Cursor/Cursor.qml 1970-01-01 00:00:00 +0000
4727+++ tests/mocks/Cursor/Cursor.qml 2015-10-26 12:04:03 +0000
4728@@ -0,0 +1,20 @@
4729+/*
4730+ * Copyright (C) 2015 Canonical, Ltd.
4731+ *
4732+ * This program is free software; you can redistribute it and/or modify
4733+ * it under the terms of the GNU General Public License as published by
4734+ * the Free Software Foundation; version 3.
4735+ *
4736+ * This program is distributed in the hope that it will be useful,
4737+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4738+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4739+ * GNU General Public License for more details.
4740+ *
4741+ * You should have received a copy of the GNU General Public License
4742+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4743+ */
4744+
4745+import QtQuick 2.4
4746+
4747+Item {
4748+}
4749
4750=== added file 'tests/mocks/Cursor/qmldir'
4751--- tests/mocks/Cursor/qmldir 1970-01-01 00:00:00 +0000
4752+++ tests/mocks/Cursor/qmldir 2015-10-26 12:04:03 +0000
4753@@ -0,0 +1,2 @@
4754+module Cursor
4755+Cursor 1.0 Cursor.qml
4756
4757=== modified file 'tests/mocks/Unity/Launcher/MockQuickListModel.cpp'
4758--- tests/mocks/Unity/Launcher/MockQuickListModel.cpp 2013-08-19 15:10:58 +0000
4759+++ tests/mocks/Unity/Launcher/MockQuickListModel.cpp 2015-10-26 12:04:03 +0000
4760@@ -32,7 +32,7 @@
4761 switch (role)
4762 {
4763 case RoleLabel:
4764- return QLatin1String("test menu entry ") + QString::number(index.row());
4765+ return QString(QLatin1String("test menu entry ") + QString::number(index.row()));
4766 case RoleIcon:
4767 return QLatin1String("copy.png");
4768 case RoleClickable:
4769
4770=== modified file 'tests/mocks/Unity/fake_resultsmodel.cpp'
4771--- tests/mocks/Unity/fake_resultsmodel.cpp 2015-09-22 10:44:21 +0000
4772+++ tests/mocks/Unity/fake_resultsmodel.cpp 2015-10-26 12:04:03 +0000
4773@@ -78,7 +78,7 @@
4774 case RoleTitle:
4775 return QString("Title.%1.%2").arg(m_categoryId).arg(index.row());
4776 case RoleArt:
4777- return qmlDirectory() + "/graphics/applicationIcons/dash.png";
4778+ return QString(qmlDirectory() + "/graphics/applicationIcons/dash.png");
4779 case RoleSubtitle:
4780 return QString("Subtitle.%1.%2").arg(m_categoryId).arg(index.row());
4781 case RoleMascot:
4782
4783=== modified file 'tests/mocks/Unity/fake_scopesoverview.cpp'
4784--- tests/mocks/Unity/fake_scopesoverview.cpp 2015-09-22 10:44:21 +0000
4785+++ tests/mocks/Unity/fake_scopesoverview.cpp 2015-10-26 12:04:03 +0000
4786@@ -308,7 +308,7 @@
4787 case RoleSubtitle:
4788 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();
4789 case RoleArt:
4790- return qmlDirectory() + "/graphics/applicationIcons/dash.png";
4791+ return QString(qmlDirectory() + "/graphics/applicationIcons/dash.png");
4792 case RoleMascot:
4793 case RoleEmblem:
4794 case RoleSummary:
4795
4796=== modified file 'tests/mocks/Utils/CMakeLists.txt'
4797--- tests/mocks/Utils/CMakeLists.txt 2015-09-17 12:25:29 +0000
4798+++ tests/mocks/Utils/CMakeLists.txt 2015-10-26 12:04:03 +0000
4799@@ -13,8 +13,6 @@
4800 ${CMAKE_SOURCE_DIR}/plugins/Utils/activefocuslogger.cpp
4801 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
4802 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
4803- ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
4804- ${CMAKE_SOURCE_DIR}/plugins/Utils/timeformatter.cpp
4805 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
4806 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
4807 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
4808
4809=== modified file 'tests/mocks/Utils/Utils.qmltypes'
4810--- tests/mocks/Utils/Utils.qmltypes 2015-03-04 13:04:58 +0000
4811+++ tests/mocks/Utils/Utils.qmltypes 2015-10-26 12:04:03 +0000
4812@@ -4,10 +4,19 @@
4813 // It is used for QML tooling purposes only.
4814 //
4815 // This file was auto-generated by:
4816-// 'qmlplugindump -notrelocatable Utils 0.1 plugins'
4817+// 'qmlplugindump -notrelocatable Utils 0.1 tests/mocks'
4818
4819 Module {
4820 Component {
4821+ name: "Constants"
4822+ prototype: "QObject"
4823+ exports: ["Utils/Constants 0.1"]
4824+ isCreatable: false
4825+ isSingleton: true
4826+ exportMetaObjectRevisions: [0]
4827+ Property { name: "indicatorValueTimeout"; type: "int"; isReadonly: true }
4828+ }
4829+ Component {
4830 name: "EasingCurve"
4831 prototype: "QObject"
4832 exports: ["Utils/EasingCurve 0.1"]
4833@@ -18,6 +27,22 @@
4834 Property { name: "value"; type: "double"; isReadonly: true }
4835 }
4836 Component {
4837+ name: "InputWatcher"
4838+ prototype: "QObject"
4839+ exports: ["Utils/InputWatcher 0.1"]
4840+ exportMetaObjectRevisions: [0]
4841+ Property { name: "target"; type: "QObject"; isPointer: true }
4842+ Property { name: "targetPressed"; type: "bool"; isReadonly: true }
4843+ Signal {
4844+ name: "targetChanged"
4845+ Parameter { name: "value"; type: "QObject"; isPointer: true }
4846+ }
4847+ Signal {
4848+ name: "targetPressedChanged"
4849+ Parameter { name: "value"; type: "bool" }
4850+ }
4851+ }
4852+ Component {
4853 name: "QAbstractProxyModel"
4854 prototype: "QAbstractItemModel"
4855 Property { name: "sourceModel"; type: "QAbstractItemModel"; isPointer: true }
4856@@ -60,33 +85,6 @@
4857 Method { name: "invalidate" }
4858 }
4859 Component {
4860- name: "RelativeTimeFormatter"
4861- prototype: "TimeFormatter"
4862- exports: ["Utils/RelativeTimeFormatter 0.1"]
4863- exportMetaObjectRevisions: [0]
4864- }
4865- Component {
4866- name: "TimeFormatter"
4867- prototype: "QObject"
4868- exports: ["Utils/GDateTimeFormatter 0.1", "Utils/TimeFormatter 0.1"]
4869- exportMetaObjectRevisions: [0, 0]
4870- Property { name: "format"; type: "string" }
4871- Property { name: "timeString"; type: "string"; isReadonly: true }
4872- Property { name: "time"; type: "qlonglong" }
4873- Signal {
4874- name: "formatChanged"
4875- Parameter { name: "format"; type: "string" }
4876- }
4877- Signal {
4878- name: "timeStringChanged"
4879- Parameter { name: "timeString"; type: "string" }
4880- }
4881- Signal {
4882- name: "timeChanged"
4883- Parameter { name: "time"; type: "qlonglong" }
4884- }
4885- }
4886- Component {
4887 name: "UnityMenuModelPaths"
4888 prototype: "QObject"
4889 exports: ["Utils/UnityMenuModelPaths 0.1"]
4890@@ -160,6 +158,11 @@
4891 isCreatable: false
4892 isSingleton: true
4893 exportMetaObjectRevisions: [0]
4894+ Property { name: "geometry"; type: "QVariantMap" }
4895+ Signal {
4896+ name: "geometryChanged"
4897+ Parameter { name: "geometry"; type: "QVariantMap" }
4898+ }
4899 Method {
4900 name: "saveGeometry"
4901 Parameter { name: "windowId"; type: "string" }
4902
4903=== modified file 'tests/mocks/Utils/plugin.cpp'
4904--- tests/mocks/Utils/plugin.cpp 2015-09-17 12:25:29 +0000
4905+++ tests/mocks/Utils/plugin.cpp 2015-10-26 12:04:03 +0000
4906@@ -23,14 +23,6 @@
4907
4908 // local
4909 #include "plugin.h"
4910-#include "inputwatcher.h"
4911-#include "qlimitproxymodelqml.h"
4912-#include "unitysortfilterproxymodelqml.h"
4913-#include "relativetimeformatter.h"
4914-#include "timeformatter.h"
4915-#include "unitymenumodelpaths.h"
4916-#include "windowkeysfilter.h"
4917-#include "easingcurve.h"
4918 #include "windowstatestorage.h"
4919 #include "constants.h"
4920
4921@@ -39,8 +31,6 @@
4922 #include <inputwatcher.h>
4923 #include <qlimitproxymodelqml.h>
4924 #include <unitysortfilterproxymodelqml.h>
4925-#include <relativetimeformatter.h>
4926-#include <timeformatter.h>
4927 #include <unitymenumodelpaths.h>
4928 #include <windowkeysfilter.h>
4929 #include <windowscreenshotprovider.h>
4930@@ -67,11 +57,8 @@
4931 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
4932 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
4933 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
4934- qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
4935 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
4936- qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
4937 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
4938- qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
4939 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
4940 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
4941 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
4942
4943=== modified file 'tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp'
4944--- tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp 2015-07-20 15:06:46 +0000
4945+++ tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp 2015-10-26 12:04:03 +0000
4946@@ -55,6 +55,17 @@
4947 QVERIFY(!args.first().toString().isEmpty());
4948 }
4949
4950+ void testRotatedScreenshot()
4951+ {
4952+ QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
4953+ m_grabber->captureAndSave(90); // rotate by 90°
4954+ const QVariantList args = grabberSpy.takeFirst();
4955+ const QString filename = args.first().toString();
4956+ QVERIFY(!filename.isEmpty());
4957+ QImage img(filename);
4958+ QVERIFY(img.height() > img.width()); // verify that the image got rotated by 90° (height > width)
4959+ }
4960+
4961 private:
4962 QQuickView *m_view;
4963 ScreenGrabber *m_grabber = nullptr;
4964
4965=== modified file 'tests/plugins/ScreenGrabber/grabber.qml'
4966--- tests/plugins/ScreenGrabber/grabber.qml 2015-07-17 19:59:31 +0000
4967+++ tests/plugins/ScreenGrabber/grabber.qml 2015-10-26 12:04:03 +0000
4968@@ -20,7 +20,7 @@
4969 Rectangle {
4970 property var grabber: screenGrabber
4971
4972- width: 100
4973+ width: 101 // width intentionally bigger than height to test rotation
4974 height: 100
4975 color: "green"
4976
4977
4978=== modified file 'tests/plugins/Unity/Indicators/UnityMenuModelStackTest.cpp'
4979--- tests/plugins/Unity/Indicators/UnityMenuModelStackTest.cpp 2015-08-19 13:56:21 +0000
4980+++ tests/plugins/Unity/Indicators/UnityMenuModelStackTest.cpp 2015-10-26 12:04:03 +0000
4981@@ -43,6 +43,8 @@
4982 {
4983 delete m_model;
4984 m_model = nullptr;
4985+ // send deleteLaters to avoid leaks.
4986+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4987 }
4988
4989 void testHeadOnSetHead()
4990@@ -146,19 +148,10 @@
4991
4992 m_model->removeRow(removeIndex);
4993
4994- waitFor([&stack, resultCount]() { return stack.count() == resultCount; }, 1000);
4995 QCOMPARE(stack.count(), resultCount);
4996 }
4997
4998 private:
4999- bool waitFor(std::function<bool()> functor, int ms) {
5000-
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches