Merge lp:~charlesk/indicator-display/add-rotation-lock into lp:indicator-display/14.10

Proposed by Charles Kerr on 2014-08-21
Status: Merged
Approved by: Ted Gould on 2014-08-22
Approved revision: 16
Merged at revision: 2
Proposed branch: lp:~charlesk/indicator-display/add-rotation-lock
Merge into: lp:indicator-display/14.10
Diff against target: 1576 lines (+1426/-0)
29 files modified
CMakeLists.txt (+72/-0)
cmake/FindGMock.cmake (+10/-0)
cmake/GCov.cmake (+51/-0)
cmake/GdbusCodegen.cmake (+36/-0)
cmake/Translations.cmake (+37/-0)
data/CMakeLists.txt (+74/-0)
data/com.canonical.indicator.rotation_lock (+11/-0)
data/indicator-display.conf.in (+9/-0)
data/indicator-display.desktop.in (+9/-0)
data/indicator-display.upstart.desktop.in (+9/-0)
debian/changelog (+6/-0)
debian/compat (+1/-0)
debian/control (+34/-0)
debian/copyright (+21/-0)
debian/rules (+10/-0)
po/CMakeLists.txt (+3/-0)
po/POTFILES.in (+1/-0)
src/CMakeLists.txt (+29/-0)
src/exporter.cpp (+217/-0)
src/exporter.h (+40/-0)
src/indicator.h (+88/-0)
src/main.cpp (+62/-0)
src/rotation-lock.cpp (+197/-0)
src/rotation-lock.h (+42/-0)
tests/CMakeLists.txt (+35/-0)
tests/glib-fixture.h (+143/-0)
tests/gtestdbus-fixture.h (+102/-0)
tests/manual (+16/-0)
tests/test-rotation-lock.cpp (+61/-0)
To merge this branch: bzr merge lp:~charlesk/indicator-display/add-rotation-lock
Reviewer Review Type Date Requested Status
Ted Gould (community) 2014-08-21 Approve on 2014-08-22
PS Jenkins bot (community) continuous-integration Approve on 2014-08-22
Review via email: mp+231663@code.launchpad.net

Commit message

Add a rotation lock indicator.

Description of the change

Add a rotation lock indicator.

Note, for testing on the phone without unity-system-settings, one toggle the orientation lock on and off from the command line:

$ gsettings set com.ubuntu.touch.system orientation-lock 'PrimaryOrientation'

$ gsettings set com.ubuntu.touch.system orientation-lock 'none'

Enabling/disabling lock with this method should have the same results as in the indicator-datetime/rotation-indicator manual test.

To post a comment you must log in.
9. By Charles Kerr on 2014-08-21

remove url-dispatcher dependency.

10. By Charles Kerr on 2014-08-21

add cmake/ directory

Charles Kerr (charlesk) wrote :

aha, --split is the bzr-bd argument I was looking for to generate a local tarball since there's not one uploaded yet :)

output of dpkg -c:

 ./
 ./usr/
 ./usr/share/
 ./usr/share/upstart/
 ./usr/share/upstart/xdg/
 ./usr/share/upstart/xdg/autostart/
 ./usr/share/upstart/xdg/autostart/indicator-display.desktop
 ./usr/share/upstart/sessions/
 ./usr/share/upstart/sessions/indicator-display.conf
 ./usr/share/doc/
 ./usr/share/doc/indicator-display/
 ./usr/share/doc/indicator-display/copyright
 ./usr/share/doc/indicator-display/changelog.Debian.gz
 ./usr/share/unity/
 ./usr/share/unity/indicators/
 ./usr/share/unity/indicators/com.canonical.indicator.rotation_lock
 ./usr/lib/
 ./usr/lib/i386-linux-gnu/
 ./usr/lib/i386-linux-gnu/indicator-display/
 ./usr/lib/i386-linux-gnu/indicator-display/indicator-display-service
 ./etc/
 ./etc/xdg/
 ./etc/xdg/autostart/
 ./etc/xdg/autostart/indicator-display.desktop

11. By Charles Kerr on 2014-08-21

unit tests passing on desktop and phone, but not in jenkins -- adding gsettings-ubuntu-schemas to build-depends.

Ted Gould (ted) wrote :

A few cleanup things.

Charles Kerr (charlesk) wrote :

Responded inline

12. By Charles Kerr on 2014-08-21

in debian/control, make the Description more informative

13. By Charles Kerr on 2014-08-21

fix copy-paste error

14. By Charles Kerr on 2014-08-21

in src/exporter.cpp, use G_DBUS_NAME_OWNER_FLAGS_NONE when owning the busname

Ted Gould (ted) wrote :

So, I swear that I'm not trying to be a jerk, but the description is too long now :-) From Lintian:

W: indicator-display: description-too-long
E: indicator-display: extended-description-is-empty

Also if you add a default.conf to your .bzr-builddeb directory to enable split mode it'll build with "bzr bd" right out of the box.

15. By Charles Kerr on 2014-08-21

in control/debian, split Description into a shorter description plus an extended description.

16. By Charles Kerr on 2014-08-22

in debian/control, bump Standards-Version from 3.9.4 to 3.9.5 to make lintian happy

Charles Kerr (charlesk) wrote :

:-)

I've revised the description and extended-description in r15, and since lintian suggested it, bumped the Standards-Version from 3.9.4 to 3.9.5 in r16.

Ted Gould (ted) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'CMakeLists.txt'
2--- CMakeLists.txt 1970-01-01 00:00:00 +0000
3+++ CMakeLists.txt 2014-08-22 00:07:37 +0000
4@@ -0,0 +1,72 @@
5+project (indicator-display)
6+cmake_minimum_required (VERSION 2.8.9)
7+
8+list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
9+
10+set (PROJECT_VERSION "14.10.0")
11+set (PACKAGE ${CMAKE_PROJECT_NAME})
12+set (GETTEXT_PACKAGE indicator-display)
13+add_definitions (-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}"
14+ -DGNOMELOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}")
15+
16+set (SERVICE_LIB ${PACKAGE})
17+set (SERVICE_EXEC "${PACKAGE}-service")
18+
19+option (enable_tests "Build the package's automatic tests." ON)
20+option (enable_lcov "Generate lcov code coverage reports." ON)
21+
22+##
23+## GNU standard paths
24+##
25+include (GNUInstallDirs)
26+if (EXISTS "/etc/debian_version") # Workaround for libexecdir on debian
27+ set (CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}")
28+ set (CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_FULL_LIBDIR}")
29+endif ()
30+set (CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
31+set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
32+
33+##
34+## Check for prerequisites
35+##
36+
37+find_package (PkgConfig REQUIRED)
38+
39+include (FindPkgConfig)
40+pkg_check_modules (SERVICE_DEPS REQUIRED
41+ gio-unix-2.0>=2.36
42+ glib-2.0>=2.36)
43+include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS})
44+
45+##
46+##
47+##
48+
49+set (CMAKE_INCLUDE_CURRENT_DIR OFF)
50+include_directories (${CMAKE_CURRENT_SOURCE_DIR})
51+
52+# set the compiler warnings
53+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
54+ set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Weverything -Wno-c++98-compat")
55+else()
56+ set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wall -Wextra -Wpedantic")
57+endif()
58+set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-missing-field-initializers") # GActionEntry
59+
60+# testing & coverage
61+if (${enable_tests})
62+ set (GTEST_SOURCE_DIR /usr/src/gtest/src)
63+ set (GTEST_INCLUDE_DIR ${GTEST_SOURCE_DIR}/..)
64+ set (GTEST_LIBS -lpthread)
65+ enable_testing ()
66+ if (${enable_lcov})
67+ include(GCov)
68+ endif ()
69+endif ()
70+
71+add_subdirectory (src)
72+add_subdirectory (data)
73+add_subdirectory (po)
74+if (${enable_tests})
75+ add_subdirectory (tests)
76+endif ()
77
78=== added directory 'cmake'
79=== added file 'cmake/FindGMock.cmake'
80--- cmake/FindGMock.cmake 1970-01-01 00:00:00 +0000
81+++ cmake/FindGMock.cmake 2014-08-22 00:07:37 +0000
82@@ -0,0 +1,10 @@
83+# Build with system gmock and embedded gtest
84+set (GMOCK_INCLUDE_DIRS "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
85+set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
86+set (GTEST_INCLUDE_DIRS "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory")
87+
88+add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock")
89+
90+set(GTEST_LIBRARIES gtest)
91+set(GTEST_MAIN_LIBRARIES gtest_main)
92+set(GMOCK_LIBRARIES gmock gmock_main)
93
94=== added file 'cmake/GCov.cmake'
95--- cmake/GCov.cmake 1970-01-01 00:00:00 +0000
96+++ cmake/GCov.cmake 2014-08-22 00:07:37 +0000
97@@ -0,0 +1,51 @@
98+if (CMAKE_BUILD_TYPE MATCHES coverage)
99+ set(GCOV_FLAGS "${GCOV_FLAGS} --coverage")
100+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCOV_FLAGS}")
101+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${GCOV_FLAGS}")
102+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GCOV_FLAGS}")
103+ set(GCOV_LIBS ${GCOV_LIBS} gcov)
104+
105+ find_program(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin")
106+ if (NOT GCOVR_EXECUTABLE)
107+ message(STATUS "Gcovr binary was not found, can not generate XML coverage info.")
108+ else ()
109+ message(STATUS "Gcovr found, can generate XML coverage info.")
110+ add_custom_target (coverage-xml
111+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
112+ COMMAND "${GCOVR_EXECUTABLE}" --exclude="test.*" -x -r "${CMAKE_SOURCE_DIR}"
113+ --object-directory=${CMAKE_BINARY_DIR} -o coverage.xml)
114+ endif()
115+
116+ find_program(LCOV_EXECUTABLE lcov HINTS ${LCOV_ROOT} "${GCOVR_ROOT}/bin")
117+ find_program(GENHTML_EXECUTABLE genhtml HINTS ${GENHTML_ROOT})
118+ if (NOT LCOV_EXECUTABLE)
119+ message(STATUS "Lcov binary was not found, can not generate HTML coverage info.")
120+ else ()
121+ if(NOT GENHTML_EXECUTABLE)
122+ message(STATUS "Genthml binary not found, can not generate HTML coverage info.")
123+ else()
124+ message(STATUS "Lcov and genhtml found, can generate HTML coverage info.")
125+ add_custom_target (coverage-html
126+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
127+ COMMAND "${CMAKE_CTEST_COMMAND}" --force-new-ctest-process --verbose
128+ COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture | ${CMAKE_SOURCE_DIR}/trim-lcov.py > dconf-lcov.info
129+ COMMAND "${LCOV_EXECUTABLE}" -r dconf-lcov.info /usr/include/\\* -o nosys-lcov.info
130+ COMMAND LANG=C "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory lcov-html --legend --show-details nosys-lcov.info
131+ COMMAND ${CMAKE_COMMAND} -E echo ""
132+ COMMAND ${CMAKE_COMMAND} -E echo "file://${CMAKE_BINARY_DIR}/lcov-html/index.html"
133+ COMMAND ${CMAKE_COMMAND} -E echo "")
134+ #COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture --output-file coverage.info --no-checksum
135+ #COMMAND "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info
136+ #COMMAND ${CMAKE_COMMAND} -E echo "\\#define foo \\\"bar\\\""
137+ #)
138+ endif()
139+ endif()
140+endif()
141+
142+
143+ #$(MAKE) $(AM_MAKEFLAGS) check
144+ #lcov --directory $(top_builddir) --capture --test-name dconf | $(top_srcdir)/trim-lcov.py > dconf-lcov.info
145+ #LANG=C genhtml --prefix $(top_builddir) --output-directory lcov-html --legend --show-details dconf-lcov.info
146+ #@echo
147+ #@echo " file://$(abs_top_builddir)/lcov-html/index.html"
148+ #@echo
149
150=== added file 'cmake/GdbusCodegen.cmake'
151--- cmake/GdbusCodegen.cmake 1970-01-01 00:00:00 +0000
152+++ cmake/GdbusCodegen.cmake 2014-08-22 00:07:37 +0000
153@@ -0,0 +1,36 @@
154+cmake_minimum_required(VERSION 2.6)
155+if(POLICY CMP0011)
156+ cmake_policy(SET CMP0011 NEW)
157+endif(POLICY CMP0011)
158+
159+find_program(GDBUS_CODEGEN NAMES gdbus-codegen DOC "gdbus-codegen executable")
160+if(NOT GDBUS_CODEGEN)
161+ message(FATAL_ERROR "Excutable gdbus-codegen not found")
162+endif()
163+
164+macro(add_gdbus_codegen outfiles name prefix service_xml)
165+ add_custom_command(
166+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
167+ COMMAND "${GDBUS_CODEGEN}"
168+ --interface-prefix "${prefix}"
169+ --generate-c-code "${name}"
170+ "${service_xml}"
171+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
172+ DEPENDS ${ARGN} "${service_xml}"
173+ )
174+ list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
175+endmacro(add_gdbus_codegen)
176+
177+macro(add_gdbus_codegen_with_namespace outfiles name prefix namespace service_xml)
178+ add_custom_command(
179+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
180+ COMMAND "${GDBUS_CODEGEN}"
181+ --interface-prefix "${prefix}"
182+ --generate-c-code "${name}"
183+ --c-namespace "${namespace}"
184+ "${service_xml}"
185+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
186+ DEPENDS ${ARGN} "${service_xml}"
187+ )
188+ list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
189+endmacro(add_gdbus_codegen_with_namespace)
190
191=== added file 'cmake/Translations.cmake'
192--- cmake/Translations.cmake 1970-01-01 00:00:00 +0000
193+++ cmake/Translations.cmake 2014-08-22 00:07:37 +0000
194@@ -0,0 +1,37 @@
195+# Translations.cmake, CMake macros written for Marlin, feel free to re-use them
196+
197+macro(add_translations_directory NLS_PACKAGE)
198+ add_custom_target (i18n ALL)
199+ find_program (MSGFMT_EXECUTABLE msgfmt)
200+ file (GLOB PO_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.po)
201+ foreach (PO_INPUT ${PO_FILES})
202+ get_filename_component (PO_INPUT_BASE ${PO_INPUT} NAME_WE)
203+ set (MO_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PO_INPUT_BASE}.mo)
204+ add_custom_command (TARGET i18n COMMAND ${MSGFMT_EXECUTABLE} -o ${MO_OUTPUT} ${PO_INPUT})
205+
206+ install (FILES ${MO_OUTPUT} DESTINATION
207+ ${CMAKE_INSTALL_LOCALEDIR}/${PO_INPUT_BASE}/LC_MESSAGES
208+ RENAME ${NLS_PACKAGE}.mo)
209+ endforeach (PO_INPUT ${PO_FILES})
210+endmacro(add_translations_directory)
211+
212+
213+macro(add_translations_catalog NLS_PACKAGE)
214+ add_custom_target (pot COMMENT “Building translation catalog.”)
215+ find_program (XGETTEXT_EXECUTABLE xgettext)
216+
217+ # init this list, which will hold all the sources across all dirs
218+ set(SOURCES "")
219+
220+ # add each directory's sources to the overall sources list
221+ foreach(FILES_INPUT ${ARGN})
222+ set (DIR ${CMAKE_CURRENT_SOURCE_DIR}/${FILES_INPUT})
223+ file (GLOB_RECURSE DIR_SOURCES ${DIR}/*.c ${DIR}/*.cc ${DIR}/*.cpp ${DIR}/*.cxx ${DIR}/*.vala)
224+ set (SOURCES ${SOURCES} ${DIR_SOURCES})
225+ endforeach()
226+
227+ add_custom_command (TARGET pot COMMAND
228+ ${XGETTEXT_EXECUTABLE} -d ${NLS_PACKAGE} -o ${CMAKE_CURRENT_SOURCE_DIR}/${NLS_PACKAGE}.pot
229+ ${SOURCES} --keyword="_" --keyword="N_" --from-code=UTF-8
230+ )
231+endmacro()
232
233=== added directory 'data'
234=== added file 'data/CMakeLists.txt'
235--- data/CMakeLists.txt 1970-01-01 00:00:00 +0000
236+++ data/CMakeLists.txt 2014-08-22 00:07:37 +0000
237@@ -0,0 +1,74 @@
238+##
239+## Upstart Job File
240+##
241+
242+# where to install
243+set (UPSTART_JOBS_DIR "${CMAKE_INSTALL_FULL_DATADIR}/upstart/sessions")
244+message (STATUS "${UPSTART_JOBS_DIR} is the Upstart Jobs File install dir")
245+
246+set (UPSTART_JOB_NAME "${CMAKE_PROJECT_NAME}.conf")
247+set (UPSTART_JOB_FILE "${CMAKE_CURRENT_BINARY_DIR}/${UPSTART_JOB_NAME}")
248+set (UPSTART_JOB_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${UPSTART_JOB_NAME}.in")
249+
250+# build it
251+set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
252+configure_file ("${UPSTART_JOB_FILE_IN}" "${UPSTART_JOB_FILE}")
253+
254+# install it
255+install (FILES "${UPSTART_JOB_FILE}"
256+ DESTINATION "${UPSTART_JOBS_DIR}")
257+
258+##
259+## XDG Autostart File
260+##
261+
262+# where to install
263+set (XDG_AUTOSTART_DIR "/etc/xdg/autostart")
264+message (STATUS "${XDG_AUTOSTART_DIR} is the XDG Autostart install dir")
265+
266+set (XDG_AUTOSTART_NAME "${CMAKE_PROJECT_NAME}.desktop")
267+set (XDG_AUTOSTART_FILE "${CMAKE_CURRENT_BINARY_DIR}/${XDG_AUTOSTART_NAME}")
268+set (XDG_AUTOSTART_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${XDG_AUTOSTART_NAME}.in")
269+
270+# build it
271+set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
272+configure_file ("${XDG_AUTOSTART_FILE_IN}" "${XDG_AUTOSTART_FILE}")
273+
274+# install it
275+install (FILES "${XDG_AUTOSTART_FILE}"
276+ DESTINATION "${XDG_AUTOSTART_DIR}")
277+
278+##
279+## Upstart XDG Autostart Override
280+##
281+
282+# where to install
283+set (UPSTART_XDG_AUTOSTART_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/upstart/xdg/autostart")
284+message (STATUS "${UPSTART_XDG_AUTOSTART_DIR} is the Upstart XDG autostart override dir")
285+
286+set (UPSTART_XDG_AUTOSTART_NAME "${CMAKE_PROJECT_NAME}.upstart.desktop")
287+set (UPSTART_XDG_AUTOSTART_FILE "${CMAKE_CURRENT_BINARY_DIR}/${UPSTART_XDG_AUTOSTART_NAME}")
288+set (UPSTART_XDG_AUTOSTART_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${UPSTART_XDG_AUTOSTART_NAME}.in")
289+
290+# build it
291+set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
292+configure_file ("${UPSTART_XDG_AUTOSTART_FILE_IN}" "${UPSTART_XDG_AUTOSTART_FILE}")
293+
294+# install it
295+install (FILES "${UPSTART_XDG_AUTOSTART_FILE}"
296+ DESTINATION "${UPSTART_XDG_AUTOSTART_DIR}"
297+ RENAME "${XDG_AUTOSTART_NAME}")
298+
299+##
300+## Unity Indicator File
301+##
302+
303+# where to install
304+set (UNITY_INDICATOR_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/unity/indicators")
305+message (STATUS "${UNITY_INDICATOR_DIR} is the Unity Indicator install dir")
306+
307+set (UNITY_INDICATOR_NAME "com.canonical.indicator.rotation_lock")
308+set (UNITY_INDICATOR_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${UNITY_INDICATOR_NAME}")
309+
310+install (FILES "${UNITY_INDICATOR_FILE}"
311+ DESTINATION "${UNITY_INDICATOR_DIR}")
312
313=== added file 'data/com.canonical.indicator.rotation_lock'
314--- data/com.canonical.indicator.rotation_lock 1970-01-01 00:00:00 +0000
315+++ data/com.canonical.indicator.rotation_lock 2014-08-22 00:07:37 +0000
316@@ -0,0 +1,11 @@
317+[Indicator Service]
318+Name=indicator-rotation-lock
319+ObjectPath=/com/canonical/indicator/rotation_lock
320+Position=90
321+
322+[phone]
323+ObjectPath=/com/canonical/indicator/rotation_lock/phone
324+
325+[phone_greeter]
326+ObjectPath=/com/canonical/indicator/rotation_lock/phone
327+
328
329=== added file 'data/indicator-display.conf.in'
330--- data/indicator-display.conf.in 1970-01-01 00:00:00 +0000
331+++ data/indicator-display.conf.in 2014-08-22 00:07:37 +0000
332@@ -0,0 +1,9 @@
333+description "Indicator Display Backend"
334+
335+start on indicator-services-start
336+stop on desktop-end or indicator-services-end
337+
338+respawn
339+respawn limit 2 10
340+
341+exec @pkglibexecdir@/indicator-display-service
342
343=== added file 'data/indicator-display.desktop.in'
344--- data/indicator-display.desktop.in 1970-01-01 00:00:00 +0000
345+++ data/indicator-display.desktop.in 2014-08-22 00:07:37 +0000
346@@ -0,0 +1,9 @@
347+[Desktop Entry]
348+Type=Application
349+Name=Indicator Display
350+Exec=@pkglibexecdir@/indicator-display-service
351+OnlyShowIn=Unity;GNOME;
352+NoDisplay=true
353+StartupNotify=false
354+Terminal=false
355+AutostartCondition=GNOME3 unless-session gnome
356
357=== added file 'data/indicator-display.upstart.desktop.in'
358--- data/indicator-display.upstart.desktop.in 1970-01-01 00:00:00 +0000
359+++ data/indicator-display.upstart.desktop.in 2014-08-22 00:07:37 +0000
360@@ -0,0 +1,9 @@
361+[Desktop Entry]
362+Type=Application
363+Name=Indicator Display
364+Exec=@pkglibexecdir@/indicator-display-service
365+OnlyShowIn=Unity;
366+NoDisplay=true
367+StartupNotify=false
368+Terminal=false
369+Hidden=true
370
371=== added directory 'debian'
372=== added file 'debian/changelog'
373--- debian/changelog 1970-01-01 00:00:00 +0000
374+++ debian/changelog 2014-08-22 00:07:37 +0000
375@@ -0,0 +1,6 @@
376+indicator-display (0.1-0ubuntu1) utopic; urgency=medium
377+
378+ * Initial release.
379+
380+ -- Charles Kerr <charles.kerr@canonical.com> Wed, 20 Aug 2014 15:29:27 -0500
381+
382
383=== added file 'debian/compat'
384--- debian/compat 1970-01-01 00:00:00 +0000
385+++ debian/compat 2014-08-22 00:07:37 +0000
386@@ -0,0 +1,1 @@
387+9
388
389=== added file 'debian/control'
390--- debian/control 1970-01-01 00:00:00 +0000
391+++ debian/control 2014-08-22 00:07:37 +0000
392@@ -0,0 +1,34 @@
393+Source: indicator-display
394+Section: misc
395+Priority: optional
396+Maintainer: Charles Kerr <charles.kerr@canonical.com>
397+Build-Depends: cmake,
398+ dbus,
399+# make g++ version explicit for ABI safety <https://wiki.ubuntu.com/cpp-11>
400+ g++-4.9,
401+ libglib2.0-dev (>= 2.36),
402+ libproperties-cpp-dev,
403+# for coverage reports
404+ lcov,
405+# for tests
406+ cppcheck,
407+ libgtest-dev,
408+ google-mock (>= 1.6.0+svn437),
409+ gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140813),
410+# for packaging
411+ debhelper (>= 9),
412+ dh-translations,
413+Standards-Version: 3.9.5
414+Homepage: http://launchpad.net/indicator-display/
415+# If you aren't a member of ~indicator-applet-developers but need to upload
416+# packaging changes, just go ahead. ~indicator-applet-developers will notice and
417+# sync up the code again.
418+Vcs-Bzr: https://code.launchpad.net/~indicator-applet-developers/indicator-display/trunk.14.10
419+
420+Package: indicator-display
421+Architecture: any
422+Depends: ${shlibs:Depends},
423+ ${misc:Depends},
424+ gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140813),
425+Description: Collection of small indicators
426+ Indicators too small to merit separate codebases, such as Rotation Lock
427
428=== added file 'debian/copyright'
429--- debian/copyright 1970-01-01 00:00:00 +0000
430+++ debian/copyright 2014-08-22 00:07:37 +0000
431@@ -0,0 +1,21 @@
432+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
433+Upstream-Name: indicator-display
434+Source: http://launchpad.net/indicator-display
435+
436+Files: *
437+Copyright: 2014, Canonical Ltd.
438+License: GPL-3
439+ This package is free software; you can redistribute it and/or modify
440+ it under the terms of the GNU General Public License as published by
441+ the Free Software Foundation; version 3 of the License.
442+ .
443+ This package is distributed in the hope that it will be useful,
444+ but WITHOUT ANY WARRANTY; without even the implied warranty of
445+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
446+ GNU General Public License for more details.
447+ .
448+ You should have received a copy of the GNU General Public License
449+ along with this program. If not, see <http://www.gnu.org/licenses/>
450+ .
451+ On Debian systems, the complete text of the GNU General
452+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
453
454=== added file 'debian/rules'
455--- debian/rules 1970-01-01 00:00:00 +0000
456+++ debian/rules 2014-08-22 00:07:37 +0000
457@@ -0,0 +1,10 @@
458+#!/usr/bin/make -f
459+
460+# Explicitly selecting a G{CC,++}-version here to avoid ABI breaks
461+# introduced by toolchain updates. <https://wiki.ubuntu.com/cpp-11>
462+export CC=$(DEB_HOST_GNU_TYPE)-gcc-4.9
463+export CXX=$(DEB_HOST_GNU_TYPE)-g++-4.9
464+
465+%:
466+ dh $@ --with translations
467+
468
469=== added directory 'po'
470=== added file 'po/CMakeLists.txt'
471--- po/CMakeLists.txt 1970-01-01 00:00:00 +0000
472+++ po/CMakeLists.txt 2014-08-22 00:07:37 +0000
473@@ -0,0 +1,3 @@
474+include (Translations)
475+add_translations_directory("${GETTEXT_PACKAGE}")
476+add_translations_catalog("${GETTEXT_PACKAGE}" ../src/)
477
478=== added file 'po/POTFILES.in'
479--- po/POTFILES.in 1970-01-01 00:00:00 +0000
480+++ po/POTFILES.in 2014-08-22 00:07:37 +0000
481@@ -0,0 +1,1 @@
482+src/rotation-lock.cpp
483
484=== added directory 'src'
485=== added file 'src/CMakeLists.txt'
486--- src/CMakeLists.txt 1970-01-01 00:00:00 +0000
487+++ src/CMakeLists.txt 2014-08-22 00:07:37 +0000
488@@ -0,0 +1,29 @@
489+set (SERVICE_LIB "indicatordisplayservice")
490+set (SERVICE_EXEC "indicator-display-service")
491+
492+add_definitions (-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}")
493+
494+# handwritten source code...
495+set (SERVICE_LIB_HANDWRITTEN_SOURCES
496+ exporter.cpp
497+ rotation-lock.cpp)
498+
499+add_library (${SERVICE_LIB} STATIC
500+ ${SERVICE_LIB_HANDWRITTEN_SOURCES})
501+
502+# add the bin dir to the include path so that
503+# the compiler can find the generated header files
504+include_directories (${CMAKE_CURRENT_BINARY_DIR})
505+
506+link_directories (${SERVICE_DEPS_LIBRARY_DIRS})
507+
508+set (SERVICE_EXEC_HANDWRITTEN_SOURCES main.cpp)
509+add_executable (${SERVICE_EXEC} ${SERVICE_EXEC_HANDWRITTEN_SOURCES})
510+target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS})
511+install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR})
512+
513+# add warnings/coverage info on handwritten files
514+# but not the generated ones...
515+set_property (SOURCE ${SERVICE_LIB_HANDWRITTEN_SOURCES} ${SERVICE_EXEC_HANDWRITTEN_SOURCES}
516+ APPEND_STRING PROPERTY COMPILE_FLAGS " -std=c++11 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}")
517+
518
519=== added file 'src/exporter.cpp'
520--- src/exporter.cpp 1970-01-01 00:00:00 +0000
521+++ src/exporter.cpp 2014-08-22 00:07:37 +0000
522@@ -0,0 +1,217 @@
523+/*
524+ * Copyright 2014 Canonical Ltd.
525+ *
526+ * This program is free software: you can redistribute it and/or modify it
527+ * under the terms of the GNU General Public License version 3, as published
528+ * by the Free Software Foundation.
529+ *
530+ * This program is distributed in the hope that it will be useful, but
531+ * WITHOUT ANY WARRANTY; without even the implied warranties of
532+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
533+ * PURPOSE. See the GNU General Public License for more details.
534+ *
535+ * You should have received a copy of the GNU General Public License along
536+ * with this program. If not, see <http://www.gnu.org/licenses/>.
537+ *
538+ * Authors:
539+ * Charles Kerr <charles.kerr@canonical.com>
540+ */
541+
542+#include <src/exporter.h>
543+
544+class Exporter::Impl
545+{
546+public:
547+
548+ Impl(const std::shared_ptr<Indicator>& indicator):
549+ m_indicator(indicator)
550+ {
551+ auto bus_name = g_strdup_printf("com.canonical.indicator.%s", indicator->name());
552+ m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION,
553+ bus_name,
554+ G_BUS_NAME_OWNER_FLAGS_NONE,
555+ on_bus_acquired,
556+ nullptr,
557+ on_name_lost,
558+ this,
559+ nullptr);
560+
561+ g_free(bus_name);
562+ }
563+
564+ ~Impl()
565+ {
566+ if (m_bus != nullptr)
567+ {
568+ for(const auto& id : m_exported_menu_ids)
569+ g_dbus_connection_unexport_menu_model(m_bus, id);
570+
571+ if (m_exported_actions_id)
572+ g_dbus_connection_unexport_action_group(m_bus, m_exported_actions_id);
573+ }
574+
575+ if (m_own_id)
576+ g_bus_unown_name(m_own_id);
577+
578+ g_clear_object(&m_bus);
579+ }
580+
581+ core::Signal<std::string>& name_lost()
582+ {
583+ return m_name_lost;
584+ }
585+
586+private:
587+
588+ void emit_name_lost(const char* bus_name)
589+ {
590+ m_name_lost(bus_name);
591+ }
592+
593+ static void on_bus_acquired(GDBusConnection * connection,
594+ const gchar * name,
595+ gpointer gself)
596+ {
597+ static_cast<Impl*>(gself)->on_bus_acquired(connection, name);
598+ }
599+
600+ void on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/)
601+ {
602+ m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(connection)));
603+
604+ export_actions(m_indicator);
605+
606+ for (auto& profile : m_indicator->profiles())
607+ export_profile(m_indicator, profile);
608+ }
609+
610+ void export_actions(const std::shared_ptr<Indicator>& indicator)
611+ {
612+ GError* error;
613+ char* object_path;
614+ guint id;
615+
616+ // export the actions
617+
618+ error = nullptr;
619+ object_path = g_strdup_printf("/com/canonical/indicator/%s", indicator->name());
620+ id = g_dbus_connection_export_action_group(m_bus,
621+ object_path,
622+ G_ACTION_GROUP(indicator->action_group()),
623+ &error);
624+ if (id)
625+ m_exported_actions_id = id;
626+ else
627+ g_warning("Can't export action group to '%s': %s", object_path, error->message);
628+
629+ g_clear_error(&error);
630+ g_free(object_path);
631+ }
632+
633+ static GVariant* create_header_state(const Header& h)
634+ {
635+ GVariantBuilder b;
636+ g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT);
637+
638+ g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(h.is_visible));
639+
640+ if (!h.title.empty())
641+ g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string(h.title.c_str()));
642+
643+ if (!h.label.empty())
644+ g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string(h.label.c_str()));
645+
646+ if (!h.title.empty() || !h.label.empty())
647+ g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_string(!h.label.empty() ? h.label.c_str() : h.title.c_str()));
648+
649+ if (h.icon)
650+ g_variant_builder_add(&b, "{sv}", "icon", g_icon_serialize(h.icon.get()));
651+
652+ return g_variant_builder_end (&b);
653+ }
654+
655+ void export_profile(const std::shared_ptr<Indicator>& indicator,
656+ const std::shared_ptr<Profile>& profile)
657+ {
658+ // build the header action
659+ auto action_group = indicator->action_group();
660+ std::string action_name = profile->name() + "-header";
661+ auto a = g_simple_action_new_stateful(action_name.c_str(), nullptr, create_header_state(profile->header()));
662+ g_action_map_add_action(G_ACTION_MAP(action_group), G_ACTION(a));
663+ profile->header().changed().connect([action_group,action_name](const Header& header){
664+ auto state = create_header_state(header);
665+ auto tmp = g_variant_print(state, true);
666+ g_message("header changed; updating action state to '%s'", tmp);
667+ g_action_group_change_action_state(G_ACTION_GROUP(action_group),
668+ action_name.c_str(),
669+ create_header_state(header));
670+ g_free(tmp);
671+ });
672+
673+ // build the header menu
674+ auto detailed_action = g_strdup_printf("indicator.%s", action_name.c_str());
675+ GMenuItem* header = g_menu_item_new(nullptr, detailed_action);
676+ g_menu_item_set_attribute(header, "x-canonical-type", "s", "com.canonical.indicator.root");
677+ g_menu_item_set_submenu(header, profile->menu_model().get());
678+ g_free(detailed_action);
679+
680+ // build the menu
681+ auto menu = g_menu_new();
682+ g_menu_append_item(menu, header);
683+ g_object_unref(header);
684+
685+ // export the menu
686+ auto object_path = g_strdup_printf("/com/canonical/indicator/%s/%s",
687+ indicator->name(),
688+ profile->name().c_str());
689+ GError* error = nullptr;
690+ auto id = g_dbus_connection_export_menu_model(m_bus, object_path, G_MENU_MODEL(menu), &error);
691+ if (id)
692+ m_exported_menu_ids.insert(id);
693+ else if (error != nullptr)
694+ g_warning("cannot export '%s': %s", object_path, error->message);
695+
696+ g_free(object_path);
697+ g_clear_error(&error);
698+ //g_object_unref(menu);
699+ }
700+
701+ static void on_name_lost(GDBusConnection * /*connection*/,
702+ const gchar * name,
703+ gpointer gthis)
704+ {
705+ static_cast<Impl*>(gthis)->emit_name_lost(name);
706+ }
707+
708+ const std::string m_bus_name;
709+ core::Signal<std::string> m_name_lost;
710+ std::shared_ptr<Indicator> m_indicator;
711+ std::set<guint> m_exported_menu_ids;
712+ guint m_own_id = 0;
713+ guint m_exported_actions_id = 0;
714+ GDBusConnection * m_bus = nullptr;
715+};
716+
717+/***
718+****
719+***/
720+
721+Exporter::Exporter(const std::shared_ptr<Indicator>& indicator):
722+ impl(new Impl(indicator))
723+{
724+}
725+
726+Exporter::~Exporter()
727+{
728+}
729+
730+core::Signal<std::string>&
731+Exporter::name_lost()
732+{
733+ return impl->name_lost();
734+}
735+
736+/***
737+****
738+***/
739+
740
741=== added file 'src/exporter.h'
742--- src/exporter.h 1970-01-01 00:00:00 +0000
743+++ src/exporter.h 2014-08-22 00:07:37 +0000
744@@ -0,0 +1,40 @@
745+/*
746+ * Copyright 2014 Canonical Ltd.
747+ *
748+ * This program is free software: you can redistribute it and/or modify it
749+ * under the terms of the GNU General Public License version 3, as published
750+ * by the Free Software Foundation.
751+ *
752+ * This program is distributed in the hope that it will be useful, but
753+ * WITHOUT ANY WARRANTY; without even the implied warranties of
754+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
755+ * PURPOSE. See the GNU General Public License for more details.
756+ *
757+ * You should have received a copy of the GNU General Public License along
758+ * with this program. If not, see <http://www.gnu.org/licenses/>.
759+ *
760+ * Authors:
761+ * Charles Kerr <charles.kerr@canonical.com>
762+ */
763+
764+#include <src/indicator.h>
765+
766+#include <core/signal.h>
767+
768+#include <memory>
769+
770+class Exporter
771+{
772+public:
773+ Exporter(const std::shared_ptr<Indicator>& indicator);
774+ ~Exporter();
775+ core::Signal<std::string>& name_lost();
776+
777+private:
778+ class Impl;
779+ std::unique_ptr<Impl> impl;
780+
781+ Exporter(const Exporter&) =delete;
782+ Exporter& operator=(const Exporter&) =delete;
783+};
784+
785
786=== added file 'src/indicator.h'
787--- src/indicator.h 1970-01-01 00:00:00 +0000
788+++ src/indicator.h 2014-08-22 00:07:37 +0000
789@@ -0,0 +1,88 @@
790+/*
791+ * Copyright 2014 Canonical Ltd.
792+ *
793+ * This program is free software: you can redistribute it and/or modify it
794+ * under the terms of the GNU General Public License version 3, as published
795+ * by the Free Software Foundation.
796+ *
797+ * This program is distributed in the hope that it will be useful, but
798+ * WITHOUT ANY WARRANTY; without even the implied warranties of
799+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
800+ * PURPOSE. See the GNU General Public License for more details.
801+ *
802+ * You should have received a copy of the GNU General Public License along
803+ * with this program. If not, see <http://www.gnu.org/licenses/>.
804+ *
805+ * Authors:
806+ * Charles Kerr <charles.kerr@canonical.com>
807+ */
808+
809+#ifndef INDICATOR_DISPLAY_INDICATOR_H
810+#define INDICATOR_DISPLAY_INDICATOR_H
811+
812+#include <core/property.h>
813+
814+#include <gio/gio.h> // GIcon
815+
816+#include <string>
817+#include <vector>
818+
819+struct Header
820+{
821+ bool is_visible = false;
822+ std::string title;
823+ std::string label;
824+ std::string a11y;
825+ std::shared_ptr<GIcon> icon;
826+
827+ bool operator== (const Header& that) const {
828+ return (is_visible == that.is_visible) &&
829+ (title == that.title) &&
830+ (label == that.label) &&
831+ (a11y == that.a11y) &&
832+ (icon == that.icon);
833+ }
834+ bool operator!= (const Header& that) const { return !(*this == that);}
835+};
836+
837+
838+class Profile
839+{
840+public:
841+ virtual std::string name() const =0;
842+ virtual const core::Property<Header>& header() const =0;
843+ virtual std::shared_ptr<GMenuModel> menu_model() const =0;
844+
845+protected:
846+ Profile() =default;
847+};
848+
849+
850+class SimpleProfile: public Profile
851+{
852+public:
853+ SimpleProfile(const char* name, const std::shared_ptr<GMenuModel>& menu): m_name(name), m_menu(menu) {}
854+
855+ std::string name() const {return m_name;}
856+ core::Property<Header>& header() {return m_header;}
857+ const core::Property<Header>& header() const {return m_header;}
858+ std::shared_ptr<GMenuModel> menu_model() const {return m_menu;}
859+
860+protected:
861+ const std::string m_name;
862+ core::Property<Header> m_header;
863+ std::shared_ptr<GMenuModel> m_menu;
864+};
865+
866+
867+class Indicator
868+{
869+public:
870+ virtual ~Indicator() =default;
871+
872+ virtual const char* name() const =0;
873+ virtual GSimpleActionGroup* action_group() const =0;
874+ virtual std::vector<std::shared_ptr<Profile>> profiles() const =0;
875+};
876+
877+#endif
878
879=== added file 'src/main.cpp'
880--- src/main.cpp 1970-01-01 00:00:00 +0000
881+++ src/main.cpp 2014-08-22 00:07:37 +0000
882@@ -0,0 +1,62 @@
883+/*
884+ * Copyright 2014 Canonical Ltd.
885+ *
886+ * This program is free software: you can redistribute it and/or modify it
887+ * under the terms of the GNU General Public License version 3, as published
888+ * by the Free Software Foundation.
889+ *
890+ * This program is distributed in the hope that it will be useful, but
891+ * WITHOUT ANY WARRANTY; without even the implied warranties of
892+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
893+ * PURPOSE. See the GNU General Public License for more details.
894+ *
895+ * You should have received a copy of the GNU General Public License along
896+ * with this program. If not, see <http://www.gnu.org/licenses/>.
897+ *
898+ * Authors:
899+ * Charles Kerr <charles.kerr@canonical.com>
900+ */
901+
902+#include <src/exporter.h>
903+#include <src/rotation-lock.h>
904+
905+#include <glib/gi18n.h> // bindtextdomain()
906+#include <gio/gio.h>
907+
908+#include <locale.h>
909+
910+int
911+main(int /*argc*/, char** /*argv*/)
912+{
913+ // Work around a deadlock in glib's type initialization.
914+ // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed.
915+ g_type_ensure(G_TYPE_DBUS_CONNECTION);
916+
917+ // boilerplate i18n
918+ setlocale(LC_ALL, "");
919+ bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR);
920+ textdomain(GETTEXT_PACKAGE);
921+
922+ auto loop = g_main_loop_new(nullptr, false);
923+ auto on_name_lost = [loop](const std::string& name){
924+ g_warning("busname lost: '%s'", name.c_str());
925+ g_main_loop_quit(loop);
926+ };
927+
928+ // build all our indicators.
929+ // Right now we've only got one -- rotation lock -- but hey, we can dream.
930+ std::vector<std::shared_ptr<Indicator>> indicators;
931+ std::vector<std::shared_ptr<Exporter>> exporters;
932+ indicators.push_back(std::make_shared<RotationLockIndicator>());
933+ for (auto& indicator : indicators) {
934+ auto exporter = std::make_shared<Exporter>(indicator);
935+ exporter->name_lost().connect(on_name_lost);
936+ exporters.push_back(exporter);
937+ }
938+
939+ g_main_loop_run(loop);
940+
941+ // cleanup
942+ g_main_loop_unref(loop);
943+ return 0;
944+}
945
946=== added file 'src/rotation-lock.cpp'
947--- src/rotation-lock.cpp 1970-01-01 00:00:00 +0000
948+++ src/rotation-lock.cpp 2014-08-22 00:07:37 +0000
949@@ -0,0 +1,197 @@
950+/*
951+ * Copyright 2014 Canonical Ltd.
952+ *
953+ * This program is free software; you can redistribute it and/or modify
954+ * it under the terms of the GNU General Public License as published by
955+ * the Free Software Foundation; version 3.
956+ *
957+ * This program is distributed in the hope that it will be useful,
958+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
959+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
960+ * GNU Lesser General Public License for more details.
961+ *
962+ * You should have received a copy of the GNU Lesser General Public License
963+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
964+ *
965+ * Authors:
966+ * Charles Kerr <charles.kerr@canonical.com>
967+ */
968+
969+#include <src/rotation-lock.h>
970+
971+#include <glib/gi18n.h>
972+
973+class RotationLockIndicator::Impl
974+{
975+public:
976+
977+ Impl():
978+ m_settings(g_settings_new(m_schema_name)),
979+ m_action_group(create_action_group())
980+ {
981+ // build the rotation lock icon
982+ auto icon = g_themed_icon_new_with_default_fallbacks("orientation-lock");
983+ auto icon_deleter = [](GIcon* o){g_object_unref(G_OBJECT(o));};
984+ m_icon.reset(icon, icon_deleter);
985+
986+ // build the phone profile
987+ auto menu_model_deleter = [](GMenuModel* o){g_object_unref(G_OBJECT(o));};
988+ std::shared_ptr<GMenuModel> phone_menu (create_phone_menu(), menu_model_deleter);
989+ m_phone = std::make_shared<SimpleProfile>("phone", phone_menu);
990+ update_phone_header();
991+ }
992+
993+ ~Impl()
994+ {
995+ g_clear_object(&m_action_group);
996+ g_clear_object(&m_settings);
997+ }
998+
999+ GSimpleActionGroup* action_group() const
1000+ {
1001+ return m_action_group;
1002+ }
1003+
1004+ std::vector<std::shared_ptr<Profile>> profiles()
1005+ {
1006+ std::vector<std::shared_ptr<Profile>> ret;
1007+ ret.push_back(m_phone);
1008+ return ret;
1009+ }
1010+
1011+private:
1012+
1013+ /***
1014+ **** Actions
1015+ ***/
1016+
1017+ static gboolean settings_to_action_state(GValue *value,
1018+ GVariant *variant,
1019+ gpointer /*unused*/)
1020+ {
1021+ bool is_locked = g_strcmp0(g_variant_get_string(variant, nullptr), "none");
1022+ g_value_set_variant(value, g_variant_new_boolean(is_locked));
1023+ return TRUE;
1024+ }
1025+
1026+ static GVariant* action_state_to_settings(const GValue *value,
1027+ const GVariantType * /*expected_type*/,
1028+ gpointer /*unused*/)
1029+ {
1030+ // Toggling to 'on' *should* lock to the screen's current orientation.
1031+ // We don't have any way of knowing Screen.orientation in this service,
1032+ // so just pick one at random to satisfy the binding's needs.
1033+ //
1034+ // In practice this doesn't matter since the indicator isn't visible
1035+ // when the lock mode is 'none' so the end user won't ever be able
1036+ // to toggle the menuitem from None to anything else.
1037+
1038+ auto state_is_true = g_variant_get_boolean(g_value_get_variant(value));
1039+ return g_variant_new_string(state_is_true ? "PrimaryOrientation" : "none");
1040+ }
1041+
1042+ GSimpleActionGroup* create_action_group()
1043+ {
1044+ GSimpleActionGroup* group;
1045+ GSimpleAction* action;
1046+
1047+ group = g_simple_action_group_new();
1048+ action = g_simple_action_new_stateful("rotation-lock",
1049+ nullptr,
1050+ g_variant_new_boolean(false));
1051+ g_settings_bind_with_mapping(m_settings, "orientation-lock",
1052+ action, "state",
1053+ G_SETTINGS_BIND_DEFAULT,
1054+ settings_to_action_state,
1055+ action_state_to_settings,
1056+ nullptr,
1057+ nullptr);
1058+ g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(action));
1059+ g_object_unref(G_OBJECT(action));
1060+ g_signal_connect_swapped(m_settings, "changed::orientation-lock",
1061+ G_CALLBACK(on_orientation_lock_setting_changed), this);
1062+
1063+ return group;
1064+ }
1065+
1066+ /***
1067+ **** Phone profile
1068+ ***/
1069+
1070+ static void on_orientation_lock_setting_changed (gpointer gself)
1071+ {
1072+ static_cast<Impl*>(gself)->update_phone_header();
1073+ }
1074+
1075+ GMenuModel* create_phone_menu()
1076+ {
1077+ GMenu* menu;
1078+ GMenuItem* menu_item;
1079+
1080+ menu = g_menu_new();
1081+
1082+ menu_item = g_menu_item_new(_("Rotation Lock"), "indicator.rotation-lock");
1083+ g_menu_item_set_attribute(menu_item, "x-canonical-type", "s", "com.canonical.indicator.switch");
1084+ g_menu_append_item(menu, menu_item);
1085+ g_object_unref(menu_item);
1086+
1087+ return G_MENU_MODEL(menu);
1088+ }
1089+
1090+ void update_phone_header()
1091+ {
1092+ Header h;
1093+ h.title = _("Rotation lock");
1094+ h.a11y = h.title;
1095+ h.is_visible = g_settings_get_enum(m_settings, "orientation-lock") != 0;
1096+ h.icon = m_icon;
1097+ m_phone->header().set(h);
1098+ }
1099+
1100+ /***
1101+ ****
1102+ ***/
1103+
1104+ static constexpr char const * m_schema_name {"com.ubuntu.touch.system"};
1105+ static constexpr char const * m_orientation_lock_icon_name {"orientation-lock"};
1106+ GSettings* m_settings = nullptr;
1107+ GSimpleActionGroup* m_action_group = nullptr;
1108+ std::shared_ptr<SimpleProfile> m_phone;
1109+ std::shared_ptr<GIcon> m_icon;
1110+};
1111+
1112+/***
1113+****
1114+***/
1115+
1116+RotationLockIndicator::RotationLockIndicator():
1117+ impl(new Impl())
1118+{
1119+}
1120+
1121+RotationLockIndicator::~RotationLockIndicator()
1122+{
1123+}
1124+
1125+std::vector<std::shared_ptr<Profile>>
1126+RotationLockIndicator::profiles() const
1127+{
1128+ return impl->profiles();
1129+}
1130+
1131+GSimpleActionGroup*
1132+RotationLockIndicator::action_group() const
1133+{
1134+ return impl->action_group();
1135+}
1136+
1137+const char*
1138+RotationLockIndicator::name() const
1139+{
1140+ return "rotation_lock";
1141+}
1142+
1143+/***
1144+****
1145+***/
1146+
1147
1148=== added file 'src/rotation-lock.h'
1149--- src/rotation-lock.h 1970-01-01 00:00:00 +0000
1150+++ src/rotation-lock.h 2014-08-22 00:07:37 +0000
1151@@ -0,0 +1,42 @@
1152+/*
1153+ * Copyright 2014 Canonical Ltd.
1154+ *
1155+ * This program is free software: you can redistribute it and/or modify it
1156+ * under the terms of the GNU General Public License version 3, as published
1157+ * by the Free Software Foundation.
1158+ *
1159+ * This program is distributed in the hope that it will be useful, but
1160+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1161+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1162+ * PURPOSE. See the GNU General Public License for more details.
1163+ *
1164+ * You should have received a copy of the GNU General Public License along
1165+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1166+ *
1167+ * Authors:
1168+ * Charles Kerr <charles.kerr@canonical.com>
1169+ */
1170+
1171+#ifndef INDICATOR_DISPLAY_ROTATION_LOCK_H
1172+#define INDICATOR_DISPLAY_ROTATION_LOCK_H
1173+
1174+#include <src/indicator.h>
1175+
1176+#include <memory> // std::unique_ptr
1177+
1178+class RotationLockIndicator: public Indicator
1179+{
1180+public:
1181+ RotationLockIndicator();
1182+ ~RotationLockIndicator();
1183+
1184+ const char* name() const;
1185+ GSimpleActionGroup* action_group() const;
1186+ std::vector<std::shared_ptr<Profile>> profiles() const;
1187+
1188+protected:
1189+ class Impl;
1190+ std::unique_ptr<Impl> impl;
1191+};
1192+
1193+#endif
1194
1195=== added directory 'tests'
1196=== added file 'tests/CMakeLists.txt'
1197--- tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
1198+++ tests/CMakeLists.txt 2014-08-22 00:07:37 +0000
1199@@ -0,0 +1,35 @@
1200+include(FindGMock)
1201+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
1202+include_directories(${GMOCK_INCLUDE_DIRS})
1203+include_directories(${GTEST_INCLUDE_DIRS})
1204+
1205+# build libgtest
1206+#add_library (gtest STATIC
1207+# ${GTEST_SOURCE_DIR}/gtest-all.cc
1208+# ${GTEST_SOURCE_DIR}/gtest_main.cc)
1209+#set_target_properties (gtest PROPERTIES INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR})
1210+#set_target_properties (gtest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -w)
1211+
1212+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
1213+ # turn off the warnings that break Google Test
1214+ set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-global-constructors -Wno-weak-vtables -Wno-undef -Wno-c++98-compat-pedantic -Wno-missing-noreturn -Wno-used-but-marked-unused -Wno-padded -Wno-deprecated -Wno-sign-compare -Wno-shift-sign-overflow")
1215+endif()
1216+
1217+SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}")
1218+
1219+# look for headers in our src dir, and also in the directories where we autogenerate files...
1220+include_directories (${CMAKE_SOURCE_DIR}/src)
1221+include_directories (${CMAKE_CURRENT_BINARY_DIR})
1222+include_directories (${DBUSTEST_INCLUDE_DIRS})
1223+
1224+function(add_test_by_name name)
1225+ set (TEST_NAME ${name})
1226+ add_executable (${TEST_NAME} ${TEST_NAME}.cpp)
1227+ add_test (${TEST_NAME} ${TEST_NAME})
1228+ add_dependencies (${TEST_NAME} libindicatordisplayservice)
1229+ target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES})
1230+endfunction()
1231+add_test_by_name(test-rotation-lock)
1232+
1233+add_test (cppcheck cppcheck --enable=all -q --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests)
1234+
1235
1236=== added file 'tests/glib-fixture.h'
1237--- tests/glib-fixture.h 1970-01-01 00:00:00 +0000
1238+++ tests/glib-fixture.h 2014-08-22 00:07:37 +0000
1239@@ -0,0 +1,143 @@
1240+/*
1241+ * Copyright 2014 Canonical Ltd.
1242+ *
1243+ * This program is free software: you can redistribute it and/or modify it
1244+ * under the terms of the GNU General Public License version 3, as published
1245+ * by the Free Software Foundation.
1246+ *
1247+ * This program is distributed in the hope that it will be useful, but
1248+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1249+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1250+ * PURPOSE. See the GNU General Public License for more details.
1251+ *
1252+ * You should have received a copy of the GNU General Public License along
1253+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1254+ *
1255+ * Authors:
1256+ * Charles Kerr <charles.kerr@canonical.com>
1257+ */
1258+
1259+#include <map>
1260+
1261+#include <glib.h>
1262+#include <glib/gstdio.h>
1263+#include <gio/gio.h>
1264+
1265+#include <gtest/gtest.h>
1266+
1267+#include <locale.h> // setlocale()
1268+
1269+class GlibFixture : public ::testing::Test
1270+{
1271+ private:
1272+
1273+ GLogFunc realLogHandler;
1274+
1275+ std::map<GLogLevelFlags,size_t> expected_log;
1276+ std::map<GLogLevelFlags,std::vector<std::string>> log;
1277+
1278+ void test_log_counts()
1279+ {
1280+ const GLogLevelFlags levels_to_test[] = { G_LOG_LEVEL_ERROR,
1281+ G_LOG_LEVEL_CRITICAL,
1282+ G_LOG_LEVEL_MESSAGE,
1283+ G_LOG_LEVEL_WARNING };
1284+
1285+ for(const auto& level : levels_to_test)
1286+ {
1287+ const auto& v = log[level];
1288+ const auto n = v.size();
1289+
1290+ EXPECT_EQ(expected_log[level], n);
1291+
1292+ if (expected_log[level] != n)
1293+ for (size_t i=0; i<n; ++i)
1294+ g_message("%d %s", (n+1), v[i].c_str());
1295+ }
1296+
1297+ expected_log.clear();
1298+ log.clear();
1299+ }
1300+
1301+ static void default_log_handler(const gchar * log_domain,
1302+ GLogLevelFlags log_level,
1303+ const gchar * message,
1304+ gpointer self)
1305+ {
1306+ auto tmp = g_strdup_printf ("%s:%d \"%s\"", log_domain, (int)log_level, message);
1307+ static_cast<GlibFixture*>(self)->log[log_level].push_back(tmp);
1308+ g_free(tmp);
1309+ }
1310+
1311+ protected:
1312+
1313+ void increment_expected_errors(GLogLevelFlags level, size_t n=1)
1314+ {
1315+ expected_log[level] += n;
1316+ }
1317+
1318+ virtual void SetUp()
1319+ {
1320+ setlocale(LC_ALL, "C.UTF-8");
1321+
1322+ loop = g_main_loop_new(nullptr, false);
1323+
1324+ g_log_set_default_handler(default_log_handler, this);
1325+
1326+ g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true));
1327+
1328+ g_unsetenv("DISPLAY");
1329+ }
1330+
1331+ virtual void TearDown()
1332+ {
1333+ test_log_counts();
1334+
1335+ g_log_set_default_handler(realLogHandler, this);
1336+
1337+ g_clear_pointer(&loop, g_main_loop_unref);
1338+ }
1339+
1340+ private:
1341+
1342+ static gboolean
1343+ wait_for_signal__timeout(gpointer name)
1344+ {
1345+ g_error("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name);
1346+ return G_SOURCE_REMOVE;
1347+ }
1348+
1349+ static gboolean
1350+ wait_msec__timeout(gpointer loop)
1351+ {
1352+ g_main_loop_quit(static_cast<GMainLoop*>(loop));
1353+ return G_SOURCE_CONTINUE;
1354+ }
1355+
1356+ protected:
1357+
1358+ /* convenience func to loop while waiting for a GObject's signal */
1359+ void wait_for_signal(gpointer o, const gchar * signal, const guint timeout_seconds=5)
1360+ {
1361+ // wait for the signal or for timeout, whichever comes first
1362+ const auto handler_id = g_signal_connect_swapped(o, signal,
1363+ G_CALLBACK(g_main_loop_quit),
1364+ loop);
1365+ const auto timeout_id = g_timeout_add_seconds(timeout_seconds,
1366+ wait_for_signal__timeout,
1367+ loop);
1368+ g_main_loop_run(loop);
1369+ g_source_remove(timeout_id);
1370+ g_signal_handler_disconnect(o, handler_id);
1371+ }
1372+
1373+ /* convenience func to loop for N msec */
1374+ void wait_msec(guint msec=50)
1375+ {
1376+ const auto id = g_timeout_add(msec, wait_msec__timeout, loop);
1377+ g_main_loop_run(loop);
1378+ g_source_remove(id);
1379+ }
1380+
1381+ GMainLoop * loop;
1382+};
1383
1384=== added file 'tests/gtestdbus-fixture.h'
1385--- tests/gtestdbus-fixture.h 1970-01-01 00:00:00 +0000
1386+++ tests/gtestdbus-fixture.h 2014-08-22 00:07:37 +0000
1387@@ -0,0 +1,102 @@
1388+/*
1389+ * Copyright 2013 Canonical Ltd.
1390+ *
1391+ * This program is free software: you can redistribute it and/or modify it
1392+ * under the terms of the GNU General Public License version 3, as published
1393+ * by the Free Software Foundation.
1394+ *
1395+ * This program is distributed in the hope that it will be useful, but
1396+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1397+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1398+ * PURPOSE. See the GNU General Public License for more details.
1399+ *
1400+ * You should have received a copy of the GNU General Public License along
1401+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1402+ *
1403+ * Authors:
1404+ * Charles Kerr <charles.kerr@canonical.com>
1405+ */
1406+
1407+#include "glib-fixture.h"
1408+
1409+/***
1410+****
1411+***/
1412+
1413+class GTestDBusFixture: public GlibFixture
1414+{
1415+ public:
1416+
1417+ GTestDBusFixture() {}
1418+
1419+ GTestDBusFixture(const std::vector<std::string>& service_dirs_in): service_dirs(service_dirs_in) {}
1420+
1421+ private:
1422+
1423+ typedef GlibFixture super;
1424+
1425+ static void
1426+ on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself)
1427+ {
1428+ auto self = static_cast<GTestDBusFixture*>(gself);
1429+
1430+ GError * err = 0;
1431+ self->bus = g_bus_get_finish (res, &err);
1432+ g_assert_no_error (err);
1433+
1434+ g_main_loop_quit (self->loop);
1435+ }
1436+
1437+ static void
1438+ on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself)
1439+ {
1440+ auto self = static_cast<GTestDBusFixture*>(gself);
1441+
1442+ GError * err = 0;
1443+ g_dbus_connection_close_finish (self->bus, res, &err);
1444+ g_assert_no_error (err);
1445+
1446+ g_main_loop_quit (self->loop);
1447+ }
1448+
1449+ protected:
1450+
1451+ GTestDBus * test_dbus = nullptr;
1452+ GDBusConnection * bus = nullptr;
1453+ const std::vector<std::string> service_dirs;
1454+
1455+ virtual void SetUp ()
1456+ {
1457+ super::SetUp ();
1458+
1459+ // pull up a test dbus
1460+ test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
1461+ for (const auto& dir : service_dirs)
1462+ g_test_dbus_add_service_dir (test_dbus, dir.c_str());
1463+ g_test_dbus_up (test_dbus);
1464+ const char * address = g_test_dbus_get_bus_address (test_dbus);
1465+ g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, true);
1466+ g_setenv ("DBUS_SESSION_BUS_ADDRESS", address, true);
1467+ g_debug ("test_dbus's address is %s", address);
1468+
1469+ // wait for the GDBusConnection before returning
1470+ g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this);
1471+ g_main_loop_run (loop);
1472+ }
1473+
1474+ virtual void TearDown ()
1475+ {
1476+ wait_msec();
1477+
1478+ // close the system bus
1479+ g_dbus_connection_close(bus, nullptr, on_bus_closed, this);
1480+ g_main_loop_run(loop);
1481+ g_clear_object(&bus);
1482+
1483+ // tear down the test dbus
1484+ g_test_dbus_down(test_dbus);
1485+ g_clear_object(&test_dbus);
1486+
1487+ super::TearDown();
1488+ }
1489+};
1490
1491=== added file 'tests/manual'
1492--- tests/manual 1970-01-01 00:00:00 +0000
1493+++ tests/manual 2014-08-22 00:07:37 +0000
1494@@ -0,0 +1,16 @@
1495+
1496+Test-case indicator-display/rotation-indicator
1497+<dl>
1498+ <dt>On the phone, enable the orientation lock in ubuntu-system-settings.</dt>
1499+ <dd>The rotation lock indicator should appear, and its switch menuitem should be set to 'true'.</dd>
1500+
1501+ <dt>With the orientation locked, click on the indicator's switch menuitem to toggle from locked to unlocked.</dd>
1502+ <dd>The rotation lock indicator should disappear</dd>
1503+ <dd>In ubuntu-system-settings, the orientation lock control should change to 'none'.</dd>
1504+</dl>
1505+
1506+
1507+<strong>
1508+ If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result.
1509+ If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result</strong>.
1510+
1511
1512=== added file 'tests/test-rotation-lock.cpp'
1513--- tests/test-rotation-lock.cpp 1970-01-01 00:00:00 +0000
1514+++ tests/test-rotation-lock.cpp 2014-08-22 00:07:37 +0000
1515@@ -0,0 +1,61 @@
1516+/*
1517+ * Copyright 2014 Canonical Ltd.
1518+ *
1519+ * This program is free software: you can redistribute it and/or modify it
1520+ * under the terms of the GNU General Public License version 3, as published
1521+ * by the Free Software Foundation.
1522+ *
1523+ * This program is distributed in the hope that it will be useful, but
1524+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1525+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1526+ * PURPOSE. See the GNU General Public License for more details.
1527+ *
1528+ * You should have received a copy of the GNU General Public License along
1529+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1530+ *
1531+ * Authors:
1532+ * Charles Kerr <charles.kerr@canonical.com>
1533+ */
1534+
1535+#include "gtestdbus-fixture.h"
1536+
1537+#include <src/rotation-lock.h>
1538+
1539+class RotationLockFixture: public GTestDBusFixture
1540+{
1541+private:
1542+ typedef GTestDBusFixture super;
1543+
1544+protected:
1545+
1546+ void SetUp()
1547+ {
1548+ super::SetUp();
1549+ }
1550+
1551+ void TearDown()
1552+ {
1553+ super::TearDown();
1554+ }
1555+};
1556+
1557+/***
1558+****
1559+***/
1560+
1561+TEST_F(RotationLockFixture, CheckIndicator)
1562+{
1563+ RotationLockIndicator indicator;
1564+
1565+ ASSERT_STREQ("rotation_lock", indicator.name());
1566+ auto actions = indicator.action_group();
1567+ ASSERT_TRUE(actions != nullptr);
1568+ ASSERT_TRUE(g_action_group_has_action(G_ACTION_GROUP(actions), "rotation-lock"));
1569+
1570+ std::vector<std::shared_ptr<Profile>> profiles = indicator.profiles();
1571+ ASSERT_EQ(1, profiles.size());
1572+ std::shared_ptr<Profile> phone = profiles[0];
1573+ ASSERT_EQ(std::string("phone"), phone->name());
1574+ ASSERT_FALSE(phone->header()->is_visible);
1575+}
1576+

Subscribers

People subscribed via source and target branches