Merge lp:~charlesk/indicator-datetime/alarms into lp:indicator-datetime/14.04
- alarms
- Merge into trunk.14.04
Status: | Merged |
---|---|
Approved by: | Ted Gould |
Approved revision: | 405 |
Merged at revision: | 305 |
Proposed branch: | lp:~charlesk/indicator-datetime/alarms |
Merge into: | lp:indicator-datetime/14.04 |
Prerequisite: | lp:~charlesk/indicator-datetime/lp-1271484 |
Diff against target: |
1743 lines (+1011/-204) 25 files modified
CMakeLists.txt (+2/-2) debian/control (+1/-1) include/datetime/clock-watcher.h (+72/-0) include/datetime/date-time.h (+3/-0) include/datetime/planner-eds.h (+3/-2) include/datetime/snap.h (+51/-0) include/datetime/timezone-file.h (+2/-2) po/POTFILES.in (+2/-0) src/CMakeLists.txt (+2/-0) src/actions-live.cpp (+1/-1) src/clock-watcher.cpp (+71/-0) src/date-time.cpp (+18/-0) src/main.cpp (+22/-9) src/menu.cpp (+37/-66) src/planner-eds.cpp (+216/-105) src/snap.cpp (+256/-0) src/timezone-file.cpp (+4/-4) tests/CMakeLists.txt (+5/-0) tests/geoclue-fixture.h (+1/-1) tests/manual-test-snap.cpp (+63/-0) tests/test-clock-watcher.cpp (+166/-0) tests/test-clock.cpp (+2/-2) tests/test-planner.cpp (+8/-6) tests/test-settings.cpp (+2/-2) tests/test-utils.cpp (+1/-1) |
To merge this branch: | bzr merge lp:~charlesk/indicator-datetime/alarms |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ted Gould (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+204420@code.launchpad.net |
Commit message
support for ubuntu-clock-app's alarms
Description of the change
Alarm Support for indicator-datetime:
* add ECalClient view to monitor changes in the client's components
* watch both calendar and task-list because ubuntu-ui-tookit uses the latter
* add ClockWatcher + unit tests to decide when to show a snap decision
* restore Snap decision code + manual tests to display alarms on phone
* play a looping sound while the snap decision is being shown
* update the header state when alarms change s.t. we sync the icon
* use the ubuntu-mobile icon 'alarm-clock' for alarm + header icon
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:396
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 397. By Charles Kerr
-
bugfix: when closing the snap decision, ensure there's not a timeout waiting to loop the ringtone
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:397
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 398. By Charles Kerr
-
copyediting: make the Snap lambdas a little easier to read.
- 399. By Charles Kerr
-
add utils.c to POTFILES.in
- 400. By Charles Kerr
-
remove alarms from the menu once they've been shown in a snap decision.
- 401. By Charles Kerr
-
if an alarm doesn't have a URL associated with it, use 'appid:
//com.ubuntu. clock/clock/ current- user-version' as a fallback url.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:401
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 402. By Charles Kerr
-
revert r400; we can't block alarms by UID because that would hide recurring alarms
- 403. By Charles Kerr
-
remove upcoming events from the menu once they're no longer upcoming.
- 404. By Charles Kerr
-
when playing a sound in canberra, don't use CA_PROP_EVENT_ID if caching failed
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:403
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:404
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 405. By Charles Kerr
-
another pass at removing alarms from the menu once they're no longer upcoming. This version fixes the header's icon as well.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:405
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 406. By Charles Kerr
-
sync with trunk
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2014-02-10 22:59:02 +0000 | |||
3 | +++ CMakeLists.txt 2014-02-19 15:36:23 +0000 | |||
4 | @@ -38,10 +38,10 @@ | |||
5 | 38 | libical>=0.48 | 38 | libical>=0.48 |
6 | 39 | libecal-1.2>=3.5 | 39 | libecal-1.2>=3.5 |
7 | 40 | libedataserver-1.2>=3.5 | 40 | libedataserver-1.2>=3.5 |
8 | 41 | libcanberra>=0.12 | ||
9 | 41 | libnotify>=0.7.6 | 42 | libnotify>=0.7.6 |
10 | 42 | url-dispatcher-1>=1 | 43 | url-dispatcher-1>=1 |
13 | 43 | properties-cpp>=0.0.1 | 44 | properties-cpp>=0.0.1) |
12 | 44 | json-glib-1.0>=0.16.2) | ||
14 | 45 | include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) | 45 | include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) |
15 | 46 | 46 | ||
16 | 47 | ## | 47 | ## |
17 | 48 | 48 | ||
18 | === modified file 'debian/control' | |||
19 | --- debian/control 2014-02-11 20:06:28 +0000 | |||
20 | +++ debian/control 2014-02-19 15:36:23 +0000 | |||
21 | @@ -16,13 +16,13 @@ | |||
22 | 16 | libgtest-dev, | 16 | libgtest-dev, |
23 | 17 | libglib2.0-dev (>= 2.35.4), | 17 | libglib2.0-dev (>= 2.35.4), |
24 | 18 | libnotify-dev (>= 0.7.6), | 18 | libnotify-dev (>= 0.7.6), |
25 | 19 | libcanberra-dev, | ||
26 | 19 | libido3-0.1-dev (>= 0.2.90), | 20 | libido3-0.1-dev (>= 0.2.90), |
27 | 20 | libgeoclue-dev (>= 0.12.0), | 21 | libgeoclue-dev (>= 0.12.0), |
28 | 21 | libecal1.2-dev (>= 3.5), | 22 | libecal1.2-dev (>= 3.5), |
29 | 22 | libical-dev (>= 1.0), | 23 | libical-dev (>= 1.0), |
30 | 23 | libgtk-3-dev (>= 3.1.4), | 24 | libgtk-3-dev (>= 3.1.4), |
31 | 24 | libcairo2-dev (>= 1.10), | 25 | libcairo2-dev (>= 1.10), |
32 | 25 | libjson-glib-dev, | ||
33 | 26 | libpolkit-gobject-1-dev, | 26 | libpolkit-gobject-1-dev, |
34 | 27 | libedataserver1.2-dev (>= 3.5), | 27 | libedataserver1.2-dev (>= 3.5), |
35 | 28 | libgconf2-dev (>= 2.31), | 28 | libgconf2-dev (>= 2.31), |
36 | 29 | 29 | ||
37 | === added file 'include/datetime/clock-watcher.h' | |||
38 | --- include/datetime/clock-watcher.h 1970-01-01 00:00:00 +0000 | |||
39 | +++ include/datetime/clock-watcher.h 2014-02-19 15:36:23 +0000 | |||
40 | @@ -0,0 +1,72 @@ | |||
41 | 1 | /* | ||
42 | 2 | * Copyright 2014 Canonical Ltd. | ||
43 | 3 | * | ||
44 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
45 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
46 | 6 | * by the Free Software Foundation. | ||
47 | 7 | * | ||
48 | 8 | * This program is distributed in the hope that it will be useful, but | ||
49 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
50 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
51 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
52 | 12 | * | ||
53 | 13 | * You should have received a copy of the GNU General Public License along | ||
54 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
55 | 15 | * | ||
56 | 16 | * Authors: | ||
57 | 17 | * Charles Kerr <charles.kerr@canonical.com> | ||
58 | 18 | */ | ||
59 | 19 | |||
60 | 20 | #ifndef INDICATOR_DATETIME_CLOCK_WATCHER_H | ||
61 | 21 | #define INDICATOR_DATETIME_CLOCK_WATCHER_H | ||
62 | 22 | |||
63 | 23 | #include <datetime/state.h> | ||
64 | 24 | #include <datetime/appointment.h> | ||
65 | 25 | |||
66 | 26 | #include <core/signal.h> | ||
67 | 27 | |||
68 | 28 | #include <memory> | ||
69 | 29 | #include <set> | ||
70 | 30 | #include <string> | ||
71 | 31 | |||
72 | 32 | namespace unity { | ||
73 | 33 | namespace indicator { | ||
74 | 34 | namespace datetime { | ||
75 | 35 | |||
76 | 36 | |||
77 | 37 | /** | ||
78 | 38 | * \brief Watches the clock and appointments to notify when an | ||
79 | 39 | * appointment's time is reached. | ||
80 | 40 | */ | ||
81 | 41 | class ClockWatcher | ||
82 | 42 | { | ||
83 | 43 | public: | ||
84 | 44 | ClockWatcher() =default; | ||
85 | 45 | virtual ~ClockWatcher() =default; | ||
86 | 46 | virtual core::Signal<const Appointment&>& alarm_reached() = 0; | ||
87 | 47 | }; | ||
88 | 48 | |||
89 | 49 | |||
90 | 50 | /** | ||
91 | 51 | * \brief A #ClockWatcher implementation | ||
92 | 52 | */ | ||
93 | 53 | class ClockWatcherImpl: public ClockWatcher | ||
94 | 54 | { | ||
95 | 55 | public: | ||
96 | 56 | ClockWatcherImpl(const std::shared_ptr<const State>& state); | ||
97 | 57 | ~ClockWatcherImpl() =default; | ||
98 | 58 | core::Signal<const Appointment&>& alarm_reached(); | ||
99 | 59 | |||
100 | 60 | private: | ||
101 | 61 | void pulse(); | ||
102 | 62 | std::set<std::string> m_triggered; | ||
103 | 63 | std::shared_ptr<const State> m_state; | ||
104 | 64 | core::Signal<const Appointment&> m_alarm_reached; | ||
105 | 65 | }; | ||
106 | 66 | |||
107 | 67 | |||
108 | 68 | } // namespace datetime | ||
109 | 69 | } // namespace indicator | ||
110 | 70 | } // namespace unity | ||
111 | 71 | |||
112 | 72 | #endif // INDICATOR_DATETIME_CLOCK_WATCHER_H | ||
113 | 0 | 73 | ||
114 | === modified file 'include/datetime/date-time.h' | |||
115 | --- include/datetime/date-time.h 2014-01-30 19:00:22 +0000 | |||
116 | +++ include/datetime/date-time.h 2014-02-19 15:36:23 +0000 | |||
117 | @@ -41,6 +41,7 @@ | |||
118 | 41 | DateTime& operator=(GDateTime* in); | 41 | DateTime& operator=(GDateTime* in); |
119 | 42 | DateTime& operator=(const DateTime& in); | 42 | DateTime& operator=(const DateTime& in); |
120 | 43 | DateTime to_timezone(const std::string& zone) const; | 43 | DateTime to_timezone(const std::string& zone) const; |
121 | 44 | DateTime add_full(int years, int months, int days, int hours, int minutes, double seconds) const; | ||
122 | 44 | void reset(GDateTime* in=nullptr); | 45 | void reset(GDateTime* in=nullptr); |
123 | 45 | 46 | ||
124 | 46 | GDateTime* get() const; | 47 | GDateTime* get() const; |
125 | @@ -48,9 +49,11 @@ | |||
126 | 48 | 49 | ||
127 | 49 | std::string format(const std::string& fmt) const; | 50 | std::string format(const std::string& fmt) const; |
128 | 50 | int day_of_month() const; | 51 | int day_of_month() const; |
129 | 52 | double seconds() const; | ||
130 | 51 | int64_t to_unix() const; | 53 | int64_t to_unix() const; |
131 | 52 | 54 | ||
132 | 53 | bool operator<(const DateTime& that) const; | 55 | bool operator<(const DateTime& that) const; |
133 | 56 | bool operator<=(const DateTime& that) const; | ||
134 | 54 | bool operator!=(const DateTime& that) const; | 57 | bool operator!=(const DateTime& that) const; |
135 | 55 | bool operator==(const DateTime& that) const; | 58 | bool operator==(const DateTime& that) const; |
136 | 56 | 59 | ||
137 | 57 | 60 | ||
138 | === modified file 'include/datetime/planner-eds.h' | |||
139 | --- include/datetime/planner-eds.h 2014-01-15 05:07:10 +0000 | |||
140 | +++ include/datetime/planner-eds.h 2014-02-19 15:36:23 +0000 | |||
141 | @@ -20,9 +20,10 @@ | |||
142 | 20 | #ifndef INDICATOR_DATETIME_PLANNER_EDS_H | 20 | #ifndef INDICATOR_DATETIME_PLANNER_EDS_H |
143 | 21 | #define INDICATOR_DATETIME_PLANNER_EDS_H | 21 | #define INDICATOR_DATETIME_PLANNER_EDS_H |
144 | 22 | 22 | ||
145 | 23 | #include <datetime/clock.h> | ||
146 | 23 | #include <datetime/planner.h> | 24 | #include <datetime/planner.h> |
147 | 24 | 25 | ||
149 | 25 | #include <memory> // unique_ptr | 26 | #include <memory> // shared_ptr, unique_ptr |
150 | 26 | 27 | ||
151 | 27 | namespace unity { | 28 | namespace unity { |
152 | 28 | namespace indicator { | 29 | namespace indicator { |
153 | @@ -34,7 +35,7 @@ | |||
154 | 34 | class PlannerEds: public Planner | 35 | class PlannerEds: public Planner |
155 | 35 | { | 36 | { |
156 | 36 | public: | 37 | public: |
158 | 37 | PlannerEds(); | 38 | PlannerEds(const std::shared_ptr<Clock>& clock); |
159 | 38 | virtual ~PlannerEds(); | 39 | virtual ~PlannerEds(); |
160 | 39 | 40 | ||
161 | 40 | private: | 41 | private: |
162 | 41 | 42 | ||
163 | === added file 'include/datetime/snap.h' | |||
164 | --- include/datetime/snap.h 1970-01-01 00:00:00 +0000 | |||
165 | +++ include/datetime/snap.h 2014-02-19 15:36:23 +0000 | |||
166 | @@ -0,0 +1,51 @@ | |||
167 | 1 | /* | ||
168 | 2 | * Copyright 2014 Canonical Ltd. | ||
169 | 3 | * | ||
170 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
171 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
172 | 6 | * by the Free Software Foundation. | ||
173 | 7 | * | ||
174 | 8 | * This program is distributed in the hope that it will be useful, but | ||
175 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
176 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
177 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
178 | 12 | * | ||
179 | 13 | * You should have received a copy of the GNU General Public License along | ||
180 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
181 | 15 | * | ||
182 | 16 | * Authors: | ||
183 | 17 | * Charles Kerr <charles.kerr@canonical.com> | ||
184 | 18 | */ | ||
185 | 19 | |||
186 | 20 | #ifndef INDICATOR_DATETIME_SNAP_H | ||
187 | 21 | #define INDICATOR_DATETIME_SNAP_H | ||
188 | 22 | |||
189 | 23 | #include <datetime/appointment.h> | ||
190 | 24 | |||
191 | 25 | #include <memory> | ||
192 | 26 | #include <functional> | ||
193 | 27 | |||
194 | 28 | namespace unity { | ||
195 | 29 | namespace indicator { | ||
196 | 30 | namespace datetime { | ||
197 | 31 | |||
198 | 32 | /** | ||
199 | 33 | * \brief Pops up Snap Decisions for appointments | ||
200 | 34 | */ | ||
201 | 35 | class Snap | ||
202 | 36 | { | ||
203 | 37 | public: | ||
204 | 38 | Snap(); | ||
205 | 39 | virtual ~Snap(); | ||
206 | 40 | |||
207 | 41 | typedef std::function<void(const Appointment&)> appointment_func; | ||
208 | 42 | void operator()(const Appointment& appointment, | ||
209 | 43 | appointment_func show, | ||
210 | 44 | appointment_func dismiss); | ||
211 | 45 | }; | ||
212 | 46 | |||
213 | 47 | } // namespace datetime | ||
214 | 48 | } // namespace indicator | ||
215 | 49 | } // namespace unity | ||
216 | 50 | |||
217 | 51 | #endif // INDICATOR_DATETIME_SNAP_H | ||
218 | 0 | 52 | ||
219 | === modified file 'include/datetime/timezone-file.h' | |||
220 | --- include/datetime/timezone-file.h 2014-01-30 19:00:22 +0000 | |||
221 | +++ include/datetime/timezone-file.h 2014-02-19 15:36:23 +0000 | |||
222 | @@ -42,8 +42,8 @@ | |||
223 | 42 | ~FileTimezone(); | 42 | ~FileTimezone(); |
224 | 43 | 43 | ||
225 | 44 | private: | 44 | private: |
228 | 45 | void setFilename(const std::string& filename); | 45 | void set_filename(const std::string& filename); |
229 | 46 | static void onFileChanged(gpointer gself); | 46 | static void on_file_changed(gpointer gself); |
230 | 47 | void clear(); | 47 | void clear(); |
231 | 48 | void reload(); | 48 | void reload(); |
232 | 49 | 49 | ||
233 | 50 | 50 | ||
234 | === modified file 'po/POTFILES.in' | |||
235 | --- po/POTFILES.in 2014-02-10 22:59:02 +0000 | |||
236 | +++ po/POTFILES.in 2014-02-19 15:36:23 +0000 | |||
237 | @@ -1,3 +1,5 @@ | |||
238 | 1 | src/formatter.cpp | 1 | src/formatter.cpp |
239 | 2 | src/formatter-desktop.cpp | 2 | src/formatter-desktop.cpp |
240 | 3 | src/menu.cpp | 3 | src/menu.cpp |
241 | 4 | src/snap.cpp | ||
242 | 5 | src/utils.c | ||
243 | 4 | 6 | ||
244 | === modified file 'src/CMakeLists.txt' | |||
245 | --- src/CMakeLists.txt 2014-02-10 22:59:02 +0000 | |||
246 | +++ src/CMakeLists.txt 2014-02-19 15:36:23 +0000 | |||
247 | @@ -13,6 +13,7 @@ | |||
248 | 13 | appointment.cpp | 13 | appointment.cpp |
249 | 14 | clock.cpp | 14 | clock.cpp |
250 | 15 | clock-live.cpp | 15 | clock-live.cpp |
251 | 16 | clock-watcher.cpp | ||
252 | 16 | date-time.cpp | 17 | date-time.cpp |
253 | 17 | exporter.cpp | 18 | exporter.cpp |
254 | 18 | formatter.cpp | 19 | formatter.cpp |
255 | @@ -22,6 +23,7 @@ | |||
256 | 22 | menu.cpp | 23 | menu.cpp |
257 | 23 | planner-eds.cpp | 24 | planner-eds.cpp |
258 | 24 | settings-live.cpp | 25 | settings-live.cpp |
259 | 26 | snap.cpp | ||
260 | 25 | timezone-file.cpp | 27 | timezone-file.cpp |
261 | 26 | timezone-geoclue.cpp | 28 | timezone-geoclue.cpp |
262 | 27 | timezones-live.cpp | 29 | timezones-live.cpp |
263 | 28 | 30 | ||
264 | === modified file 'src/actions-live.cpp' | |||
265 | --- src/actions-live.cpp 2014-02-10 22:59:02 +0000 | |||
266 | +++ src/actions-live.cpp 2014-02-19 15:36:23 +0000 | |||
267 | @@ -156,7 +156,7 @@ | |||
268 | 156 | 156 | ||
269 | 157 | GError * err = nullptr; | 157 | GError * err = nullptr; |
270 | 158 | auto proxy = g_dbus_proxy_new_for_bus_finish(res, &err); | 158 | auto proxy = g_dbus_proxy_new_for_bus_finish(res, &err); |
272 | 159 | if (err != NULL) | 159 | if (err != nullptr) |
273 | 160 | { | 160 | { |
274 | 161 | if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) | 161 | if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
275 | 162 | g_warning("Could not grab DBus proxy for timedated: %s", err->message); | 162 | g_warning("Could not grab DBus proxy for timedated: %s", err->message); |
276 | 163 | 163 | ||
277 | === added file 'src/clock-watcher.cpp' | |||
278 | --- src/clock-watcher.cpp 1970-01-01 00:00:00 +0000 | |||
279 | +++ src/clock-watcher.cpp 2014-02-19 15:36:23 +0000 | |||
280 | @@ -0,0 +1,71 @@ | |||
281 | 1 | /* | ||
282 | 2 | * Copyright 2014 Canonical Ltd. | ||
283 | 3 | * | ||
284 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
285 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
286 | 6 | * by the Free Software Foundation. | ||
287 | 7 | * | ||
288 | 8 | * This program is distributed in the hope that it will be useful, but | ||
289 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
290 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
291 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
292 | 12 | * | ||
293 | 13 | * You should have received a copy of the GNU General Public License along | ||
294 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
295 | 15 | * | ||
296 | 16 | * Authors: | ||
297 | 17 | * Charles Kerr <charles.kerr@canonical.com> | ||
298 | 18 | */ | ||
299 | 19 | |||
300 | 20 | #include <datetime/clock-watcher.h> | ||
301 | 21 | |||
302 | 22 | namespace unity { | ||
303 | 23 | namespace indicator { | ||
304 | 24 | namespace datetime { | ||
305 | 25 | |||
306 | 26 | /*** | ||
307 | 27 | **** | ||
308 | 28 | ***/ | ||
309 | 29 | |||
310 | 30 | ClockWatcherImpl::ClockWatcherImpl(const std::shared_ptr<const State>& state): | ||
311 | 31 | m_state(state) | ||
312 | 32 | { | ||
313 | 33 | m_state->planner->upcoming.changed().connect([this](const std::vector<Appointment>&){ | ||
314 | 34 | g_debug("ClockWatcher pulse because upcoming appointments changed"); | ||
315 | 35 | pulse(); | ||
316 | 36 | }); | ||
317 | 37 | m_state->clock->minute_changed.connect([this](){ | ||
318 | 38 | g_debug("ClockWatcher pulse because clock minute_changed"); | ||
319 | 39 | pulse(); | ||
320 | 40 | }); | ||
321 | 41 | pulse(); | ||
322 | 42 | } | ||
323 | 43 | |||
324 | 44 | core::Signal<const Appointment&>& ClockWatcherImpl::alarm_reached() | ||
325 | 45 | { | ||
326 | 46 | return m_alarm_reached; | ||
327 | 47 | } | ||
328 | 48 | |||
329 | 49 | void ClockWatcherImpl::pulse() | ||
330 | 50 | { | ||
331 | 51 | const auto now = m_state->clock->localtime(); | ||
332 | 52 | |||
333 | 53 | for(const auto& appointment : m_state->planner->upcoming.get()) | ||
334 | 54 | { | ||
335 | 55 | if (m_triggered.count(appointment.uid)) | ||
336 | 56 | continue; | ||
337 | 57 | if (!DateTime::is_same_minute(now, appointment.begin)) | ||
338 | 58 | continue; | ||
339 | 59 | |||
340 | 60 | m_triggered.insert(appointment.uid); | ||
341 | 61 | m_alarm_reached(appointment); | ||
342 | 62 | } | ||
343 | 63 | } | ||
344 | 64 | |||
345 | 65 | /*** | ||
346 | 66 | **** | ||
347 | 67 | ***/ | ||
348 | 68 | |||
349 | 69 | } // namespace datetime | ||
350 | 70 | } // namespace indicator | ||
351 | 71 | } // namespace unity | ||
352 | 0 | 72 | ||
353 | === modified file 'src/date-time.cpp' | |||
354 | --- src/date-time.cpp 2014-01-30 19:00:22 +0000 | |||
355 | +++ src/date-time.cpp 2014-02-19 15:36:23 +0000 | |||
356 | @@ -69,6 +69,14 @@ | |||
357 | 69 | return dt; | 69 | return dt; |
358 | 70 | } | 70 | } |
359 | 71 | 71 | ||
360 | 72 | DateTime DateTime::add_full(int years, int months, int days, int hours, int minutes, double seconds) const | ||
361 | 73 | { | ||
362 | 74 | auto gdt = g_date_time_add_full(get(), years, months, days, hours, minutes, seconds); | ||
363 | 75 | DateTime dt(gdt); | ||
364 | 76 | g_date_time_unref(gdt); | ||
365 | 77 | return dt; | ||
366 | 78 | } | ||
367 | 79 | |||
368 | 72 | GDateTime* DateTime::get() const | 80 | GDateTime* DateTime::get() const |
369 | 73 | { | 81 | { |
370 | 74 | g_assert(m_dt); | 82 | g_assert(m_dt); |
371 | @@ -88,6 +96,11 @@ | |||
372 | 88 | return g_date_time_get_day_of_month(get()); | 96 | return g_date_time_get_day_of_month(get()); |
373 | 89 | } | 97 | } |
374 | 90 | 98 | ||
375 | 99 | double DateTime::seconds() const | ||
376 | 100 | { | ||
377 | 101 | return g_date_time_get_seconds(get()); | ||
378 | 102 | } | ||
379 | 103 | |||
380 | 91 | int64_t DateTime::to_unix() const | 104 | int64_t DateTime::to_unix() const |
381 | 92 | { | 105 | { |
382 | 93 | return g_date_time_to_unix(get()); | 106 | return g_date_time_to_unix(get()); |
383 | @@ -112,6 +125,11 @@ | |||
384 | 112 | return g_date_time_compare(get(), that.get()) < 0; | 125 | return g_date_time_compare(get(), that.get()) < 0; |
385 | 113 | } | 126 | } |
386 | 114 | 127 | ||
387 | 128 | bool DateTime::operator<=(const DateTime& that) const | ||
388 | 129 | { | ||
389 | 130 | return g_date_time_compare(get(), that.get()) <= 0; | ||
390 | 131 | } | ||
391 | 132 | |||
392 | 115 | bool DateTime::operator!=(const DateTime& that) const | 133 | bool DateTime::operator!=(const DateTime& that) const |
393 | 116 | { | 134 | { |
394 | 117 | // return true if this isn't set, or if it's not equal | 135 | // return true if this isn't set, or if it's not equal |
395 | 118 | 136 | ||
396 | === modified file 'src/main.cpp' | |||
397 | --- src/main.cpp 2014-01-30 19:06:33 +0000 | |||
398 | +++ src/main.cpp 2014-02-19 15:36:23 +0000 | |||
399 | @@ -17,24 +17,25 @@ | |||
400 | 17 | * with this program. If not, see <http://www.gnu.org/licenses/>. | 17 | * with this program. If not, see <http://www.gnu.org/licenses/>. |
401 | 18 | */ | 18 | */ |
402 | 19 | 19 | ||
403 | 20 | |||
404 | 21 | |||
405 | 22 | #include <datetime/actions-live.h> | 20 | #include <datetime/actions-live.h> |
406 | 23 | #include <datetime/clock.h> | 21 | #include <datetime/clock.h> |
407 | 22 | #include <datetime/clock-watcher.h> | ||
408 | 24 | #include <datetime/exporter.h> | 23 | #include <datetime/exporter.h> |
409 | 25 | #include <datetime/locations-settings.h> | 24 | #include <datetime/locations-settings.h> |
410 | 26 | #include <datetime/menu.h> | 25 | #include <datetime/menu.h> |
411 | 27 | #include <datetime/planner-eds.h> | 26 | #include <datetime/planner-eds.h> |
412 | 28 | #include <datetime/settings-live.h> | 27 | #include <datetime/settings-live.h> |
413 | 28 | #include <datetime/snap.h> | ||
414 | 29 | #include <datetime/state.h> | 29 | #include <datetime/state.h> |
415 | 30 | #include <datetime/timezones-live.h> | 30 | #include <datetime/timezones-live.h> |
416 | 31 | 31 | ||
417 | 32 | #include <glib/gi18n.h> // bindtextdomain() | 32 | #include <glib/gi18n.h> // bindtextdomain() |
418 | 33 | #include <gio/gio.h> | 33 | #include <gio/gio.h> |
420 | 34 | #include <libnotify/notify.h> | 34 | |
421 | 35 | #include <url-dispatcher.h> | ||
422 | 35 | 36 | ||
423 | 36 | #include <locale.h> | 37 | #include <locale.h> |
425 | 37 | #include <stdlib.h> // exit() | 38 | #include <cstdlib> // exit() |
426 | 38 | 39 | ||
427 | 39 | using namespace unity::indicator::datetime; | 40 | using namespace unity::indicator::datetime; |
428 | 40 | 41 | ||
429 | @@ -50,10 +51,6 @@ | |||
430 | 50 | bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); | 51 | bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); |
431 | 51 | textdomain(GETTEXT_PACKAGE); | 52 | textdomain(GETTEXT_PACKAGE); |
432 | 52 | 53 | ||
433 | 53 | // init libnotify | ||
434 | 54 | if(!notify_init("indicator-datetime-service")) | ||
435 | 55 | g_critical("libnotify initialization failed"); | ||
436 | 56 | |||
437 | 57 | // build the state, actions, and menufactory | 54 | // build the state, actions, and menufactory |
438 | 58 | std::shared_ptr<State> state(new State); | 55 | std::shared_ptr<State> state(new State); |
439 | 59 | std::shared_ptr<Settings> live_settings(new LiveSettings); | 56 | std::shared_ptr<Settings> live_settings(new LiveSettings); |
440 | @@ -62,11 +59,27 @@ | |||
441 | 62 | state->settings = live_settings; | 59 | state->settings = live_settings; |
442 | 63 | state->clock = live_clock; | 60 | state->clock = live_clock; |
443 | 64 | state->locations.reset(new SettingsLocations(live_settings, live_timezones)); | 61 | state->locations.reset(new SettingsLocations(live_settings, live_timezones)); |
445 | 65 | state->planner.reset(new PlannerEds); | 62 | state->planner.reset(new PlannerEds(live_clock)); |
446 | 66 | state->planner->time = live_clock->localtime(); | 63 | state->planner->time = live_clock->localtime(); |
447 | 67 | std::shared_ptr<Actions> actions(new LiveActions(state)); | 64 | std::shared_ptr<Actions> actions(new LiveActions(state)); |
448 | 68 | MenuFactory factory(actions, state); | 65 | MenuFactory factory(actions, state); |
449 | 69 | 66 | ||
450 | 67 | // snap decisions | ||
451 | 68 | ClockWatcherImpl clock_watcher(state); | ||
452 | 69 | Snap snap; | ||
453 | 70 | clock_watcher.alarm_reached().connect([&snap](const Appointment& appt){ | ||
454 | 71 | auto snap_show = [](const Appointment& a){ | ||
455 | 72 | const char* url; | ||
456 | 73 | if(!a.url.empty()) | ||
457 | 74 | url = a.url.c_str(); | ||
458 | 75 | else // alarm doesn't have a URl associated with it; use a fallback | ||
459 | 76 | url = "appid://com.ubuntu.clock/clock/current-user-version"; | ||
460 | 77 | url_dispatch_send(url, nullptr, nullptr); | ||
461 | 78 | }; | ||
462 | 79 | auto snap_dismiss = [](const Appointment&){}; | ||
463 | 80 | snap(appt, snap_show, snap_dismiss); | ||
464 | 81 | }); | ||
465 | 82 | |||
466 | 70 | // create the menus | 83 | // create the menus |
467 | 71 | std::vector<std::shared_ptr<Menu>> menus; | 84 | std::vector<std::shared_ptr<Menu>> menus; |
468 | 72 | for(int i=0, n=Menu::NUM_PROFILES; i<n; i++) | 85 | for(int i=0, n=Menu::NUM_PROFILES; i<n; i++) |
469 | 73 | 86 | ||
470 | === modified file 'src/menu.cpp' | |||
471 | --- src/menu.cpp 2014-02-05 18:22:42 +0000 | |||
472 | +++ src/menu.cpp 2014-02-19 15:36:23 +0000 | |||
473 | @@ -22,11 +22,11 @@ | |||
474 | 22 | #include <datetime/formatter.h> | 22 | #include <datetime/formatter.h> |
475 | 23 | #include <datetime/state.h> | 23 | #include <datetime/state.h> |
476 | 24 | 24 | ||
477 | 25 | #include <json-glib/json-glib.h> | ||
478 | 26 | |||
479 | 27 | #include <glib/gi18n.h> | 25 | #include <glib/gi18n.h> |
480 | 28 | #include <gio/gio.h> | 26 | #include <gio/gio.h> |
481 | 29 | 27 | ||
482 | 28 | #include <vector> | ||
483 | 29 | |||
484 | 30 | namespace unity { | 30 | namespace unity { |
485 | 31 | namespace indicator { | 31 | namespace indicator { |
486 | 32 | namespace datetime { | 32 | namespace datetime { |
487 | @@ -62,7 +62,7 @@ | |||
488 | 62 | ****/ | 62 | ****/ |
489 | 63 | 63 | ||
490 | 64 | 64 | ||
492 | 65 | #define FALLBACK_ALARM_CLOCK_ICON_NAME "clock" | 65 | #define ALARM_ICON_NAME "alarm-clock" |
493 | 66 | #define CALENDAR_ICON_NAME "calendar" | 66 | #define CALENDAR_ICON_NAME "calendar" |
494 | 67 | 67 | ||
495 | 68 | class MenuImpl: public Menu | 68 | class MenuImpl: public Menu |
496 | @@ -105,12 +105,15 @@ | |||
497 | 105 | update_section(Appointments); // showing events got toggled | 105 | update_section(Appointments); // showing events got toggled |
498 | 106 | }); | 106 | }); |
499 | 107 | m_state->planner->upcoming.changed().connect([this](const std::vector<Appointment>&){ | 107 | m_state->planner->upcoming.changed().connect([this](const std::vector<Appointment>&){ |
501 | 108 | update_section(Appointments); // "upcoming" is the list of Appointments we show | 108 | update_upcoming(); // our m_upcoming is planner->upcoming() filtered by time |
502 | 109 | }); | 109 | }); |
503 | 110 | m_state->clock->date_changed.connect([this](){ | 110 | m_state->clock->date_changed.connect([this](){ |
504 | 111 | update_section(Calendar); // need to update the Date menuitem | 111 | update_section(Calendar); // need to update the Date menuitem |
505 | 112 | update_section(Locations); // locations' relative time may have changed | 112 | update_section(Locations); // locations' relative time may have changed |
506 | 113 | }); | 113 | }); |
507 | 114 | m_state->clock->minute_changed.connect([this](){ | ||
508 | 115 | update_upcoming(); // our m_upcoming is planner->upcoming() filtered by time | ||
509 | 116 | }); | ||
510 | 114 | m_state->locations->locations.changed().connect([this](const std::vector<Location>&) { | 117 | m_state->locations->locations.changed().connect([this](const std::vector<Location>&) { |
511 | 115 | update_section(Locations); // "locations" is the list of Locations we show | 118 | update_section(Locations); // "locations" is the list of Locations we show |
512 | 116 | }); | 119 | }); |
513 | @@ -133,6 +136,24 @@ | |||
514 | 133 | g_action_group_change_action_state(action_group, action_name.c_str(), state); | 136 | g_action_group_change_action_state(action_group, action_name.c_str(), state); |
515 | 134 | } | 137 | } |
516 | 135 | 138 | ||
517 | 139 | void update_upcoming() | ||
518 | 140 | { | ||
519 | 141 | const auto now = m_state->clock->localtime(); | ||
520 | 142 | const auto next_minute = now.add_full(0,0,0,0,1,-now.seconds()); | ||
521 | 143 | |||
522 | 144 | std::vector<Appointment> upcoming; | ||
523 | 145 | for(const auto& a : m_state->planner->upcoming.get()) | ||
524 | 146 | if (next_minute <= a.begin) | ||
525 | 147 | upcoming.push_back(a); | ||
526 | 148 | |||
527 | 149 | if (m_upcoming != upcoming) | ||
528 | 150 | { | ||
529 | 151 | m_upcoming.swap(upcoming); | ||
530 | 152 | update_header(); // show an 'alarm' icon if there are upcoming alarms | ||
531 | 153 | update_section(Appointments); // "upcoming" is the list of Appointments we show | ||
532 | 154 | } | ||
533 | 155 | } | ||
534 | 156 | |||
535 | 136 | std::shared_ptr<const State> m_state; | 157 | std::shared_ptr<const State> m_state; |
536 | 137 | std::shared_ptr<Actions> m_actions; | 158 | std::shared_ptr<Actions> m_actions; |
537 | 138 | std::shared_ptr<const Formatter> m_formatter; | 159 | std::shared_ptr<const Formatter> m_formatter; |
538 | @@ -141,71 +162,19 @@ | |||
539 | 141 | GVariant* get_serialized_alarm_icon() | 162 | GVariant* get_serialized_alarm_icon() |
540 | 142 | { | 163 | { |
541 | 143 | if (G_UNLIKELY(m_serialized_alarm_icon == nullptr)) | 164 | if (G_UNLIKELY(m_serialized_alarm_icon == nullptr)) |
543 | 144 | m_serialized_alarm_icon = create_alarm_icon(); | 165 | { |
544 | 166 | auto i = g_themed_icon_new_with_default_fallbacks(ALARM_ICON_NAME); | ||
545 | 167 | m_serialized_alarm_icon = g_icon_serialize(i); | ||
546 | 168 | g_object_unref(i); | ||
547 | 169 | } | ||
548 | 145 | 170 | ||
549 | 146 | return m_serialized_alarm_icon; | 171 | return m_serialized_alarm_icon; |
550 | 147 | } | 172 | } |
551 | 148 | 173 | ||
552 | 174 | std::vector<Appointment> m_upcoming; | ||
553 | 175 | |||
554 | 149 | private: | 176 | private: |
555 | 150 | 177 | ||
556 | 151 | /* try to get the clock app's filename from click. (/$pkgdir/$icon) */ | ||
557 | 152 | static GVariant* create_alarm_icon() | ||
558 | 153 | { | ||
559 | 154 | GVariant* serialized = nullptr; | ||
560 | 155 | gchar* icon_filename = nullptr; | ||
561 | 156 | gchar* standard_error = nullptr; | ||
562 | 157 | gchar* pkgdir = nullptr; | ||
563 | 158 | |||
564 | 159 | g_spawn_command_line_sync("click pkgdir com.ubuntu.clock", &pkgdir, &standard_error, nullptr, nullptr); | ||
565 | 160 | g_clear_pointer(&standard_error, g_free); | ||
566 | 161 | if (pkgdir != nullptr) | ||
567 | 162 | { | ||
568 | 163 | gchar* manifest = nullptr; | ||
569 | 164 | g_strstrip(pkgdir); | ||
570 | 165 | g_spawn_command_line_sync("click info com.ubuntu.clock", &manifest, &standard_error, nullptr, nullptr); | ||
571 | 166 | g_clear_pointer(&standard_error, g_free); | ||
572 | 167 | if (manifest != nullptr) | ||
573 | 168 | { | ||
574 | 169 | JsonParser* parser = json_parser_new(); | ||
575 | 170 | if (json_parser_load_from_data(parser, manifest, -1, nullptr)) | ||
576 | 171 | { | ||
577 | 172 | JsonNode* root = json_parser_get_root(parser); /* transfer-none */ | ||
578 | 173 | if ((root != nullptr) && (JSON_NODE_TYPE(root) == JSON_NODE_OBJECT)) | ||
579 | 174 | { | ||
580 | 175 | JsonObject* o = json_node_get_object(root); /* transfer-none */ | ||
581 | 176 | const gchar* icon_name = json_object_get_string_member(o, "icon"); | ||
582 | 177 | if (icon_name != nullptr) | ||
583 | 178 | icon_filename = g_build_filename(pkgdir, icon_name, nullptr); | ||
584 | 179 | } | ||
585 | 180 | } | ||
586 | 181 | g_object_unref(parser); | ||
587 | 182 | g_free(manifest); | ||
588 | 183 | } | ||
589 | 184 | g_free(pkgdir); | ||
590 | 185 | } | ||
591 | 186 | |||
592 | 187 | if (icon_filename != nullptr) | ||
593 | 188 | { | ||
594 | 189 | GFile* file = g_file_new_for_path(icon_filename); | ||
595 | 190 | GIcon* icon = g_file_icon_new(file); | ||
596 | 191 | |||
597 | 192 | serialized = g_icon_serialize(icon); | ||
598 | 193 | |||
599 | 194 | g_object_unref(icon); | ||
600 | 195 | g_object_unref(file); | ||
601 | 196 | g_free(icon_filename); | ||
602 | 197 | } | ||
603 | 198 | |||
604 | 199 | if (serialized == nullptr) | ||
605 | 200 | { | ||
606 | 201 | auto i = g_themed_icon_new_with_default_fallbacks(FALLBACK_ALARM_CLOCK_ICON_NAME); | ||
607 | 202 | serialized = g_icon_serialize(i); | ||
608 | 203 | g_object_unref(i); | ||
609 | 204 | } | ||
610 | 205 | |||
611 | 206 | return serialized; | ||
612 | 207 | } | ||
613 | 208 | |||
614 | 209 | GVariant* get_serialized_calendar_icon() | 178 | GVariant* get_serialized_calendar_icon() |
615 | 210 | { | 179 | { |
616 | 211 | if (G_UNLIKELY(m_serialized_calendar_icon == nullptr)) | 180 | if (G_UNLIKELY(m_serialized_calendar_icon == nullptr)) |
617 | @@ -273,7 +242,7 @@ | |||
618 | 273 | // add calendar | 242 | // add calendar |
619 | 274 | if (show_calendar) | 243 | if (show_calendar) |
620 | 275 | { | 244 | { |
622 | 276 | item = g_menu_item_new ("[calendar]", NULL); | 245 | item = g_menu_item_new ("[calendar]", nullptr); |
623 | 277 | v = g_variant_new_int64(0); | 246 | v = g_variant_new_int64(0); |
624 | 278 | g_menu_item_set_action_and_target_value (item, "indicator.calendar", v); | 247 | g_menu_item_set_action_and_target_value (item, "indicator.calendar", v); |
625 | 279 | g_menu_item_set_attribute (item, "x-canonical-type", | 248 | g_menu_item_set_attribute (item, "x-canonical-type", |
626 | @@ -296,11 +265,13 @@ | |||
627 | 296 | const int MAX_APPTS = 5; | 265 | const int MAX_APPTS = 5; |
628 | 297 | std::set<std::string> added; | 266 | std::set<std::string> added; |
629 | 298 | 267 | ||
631 | 299 | for (const auto& appt : m_state->planner->upcoming.get()) | 268 | for (const auto& appt : m_upcoming) |
632 | 300 | { | 269 | { |
633 | 270 | // don't show too many | ||
634 | 301 | if (n++ >= MAX_APPTS) | 271 | if (n++ >= MAX_APPTS) |
635 | 302 | break; | 272 | break; |
636 | 303 | 273 | ||
637 | 274 | // don't show duplicates | ||
638 | 304 | if (added.count(appt.uid)) | 275 | if (added.count(appt.uid)) |
639 | 305 | continue; | 276 | continue; |
640 | 306 | 277 | ||
641 | @@ -508,7 +479,7 @@ | |||
642 | 508 | { | 479 | { |
643 | 509 | // are there alarms? | 480 | // are there alarms? |
644 | 510 | bool has_alarms = false; | 481 | bool has_alarms = false; |
646 | 511 | for(const auto& appointment : m_state->planner->upcoming.get()) | 482 | for(const auto& appointment : m_upcoming) |
647 | 512 | if((has_alarms = appointment.has_alarms)) | 483 | if((has_alarms = appointment.has_alarms)) |
648 | 513 | break; | 484 | break; |
649 | 514 | 485 | ||
650 | 515 | 486 | ||
651 | === modified file 'src/planner-eds.cpp' | |||
652 | --- src/planner-eds.cpp 2014-01-31 00:33:14 +0000 | |||
653 | +++ src/planner-eds.cpp 2014-02-19 15:36:23 +0000 | |||
654 | @@ -26,6 +26,9 @@ | |||
655 | 26 | #include <libecal/libecal.h> | 26 | #include <libecal/libecal.h> |
656 | 27 | #include <libedataserver/libedataserver.h> | 27 | #include <libedataserver/libedataserver.h> |
657 | 28 | 28 | ||
658 | 29 | #include <map> | ||
659 | 30 | #include <set> | ||
660 | 31 | |||
661 | 29 | namespace unity { | 32 | namespace unity { |
662 | 30 | namespace indicator { | 33 | namespace indicator { |
663 | 31 | namespace datetime { | 34 | namespace datetime { |
664 | @@ -34,25 +37,28 @@ | |||
665 | 34 | ***** | 37 | ***** |
666 | 35 | ****/ | 38 | ****/ |
667 | 36 | 39 | ||
668 | 37 | G_DEFINE_QUARK("source-client", source_client) | ||
669 | 38 | |||
670 | 39 | |||
671 | 40 | class PlannerEds::Impl | 40 | class PlannerEds::Impl |
672 | 41 | { | 41 | { |
673 | 42 | public: | 42 | public: |
674 | 43 | 43 | ||
676 | 44 | Impl(PlannerEds& owner): | 44 | Impl(PlannerEds& owner, const std::shared_ptr<Clock>& clock): |
677 | 45 | m_owner(owner), | 45 | m_owner(owner), |
678 | 46 | m_clock(clock), | ||
679 | 46 | m_cancellable(g_cancellable_new()) | 47 | m_cancellable(g_cancellable_new()) |
680 | 47 | { | 48 | { |
681 | 48 | e_source_registry_new(m_cancellable, on_source_registry_ready, this); | 49 | e_source_registry_new(m_cancellable, on_source_registry_ready, this); |
682 | 49 | 50 | ||
683 | 51 | m_clock->minute_changed.connect([this](){ | ||
684 | 52 | g_debug("rebuilding upcoming because the clock's minute_changed"); | ||
685 | 53 | rebuild_soon(UPCOMING); | ||
686 | 54 | }); | ||
687 | 55 | |||
688 | 50 | m_owner.time.changed().connect([this](const DateTime& dt) { | 56 | m_owner.time.changed().connect([this](const DateTime& dt) { |
691 | 51 | g_debug("planner's datetime property changed to %s; calling rebuildSoon()", dt.format("%F %T").c_str()); | 57 | g_debug("planner's datetime property changed to %s; calling rebuild_soon()", dt.format("%F %T").c_str()); |
692 | 52 | rebuildSoon(); | 58 | rebuild_soon(MONTH); |
693 | 53 | }); | 59 | }); |
694 | 54 | 60 | ||
696 | 55 | rebuildSoon(); | 61 | rebuild_soon(ALL); |
697 | 56 | } | 62 | } |
698 | 57 | 63 | ||
699 | 58 | ~Impl() | 64 | ~Impl() |
700 | @@ -60,6 +66,9 @@ | |||
701 | 60 | g_cancellable_cancel(m_cancellable); | 66 | g_cancellable_cancel(m_cancellable); |
702 | 61 | g_clear_object(&m_cancellable); | 67 | g_clear_object(&m_cancellable); |
703 | 62 | 68 | ||
704 | 69 | while(!m_sources.empty()) | ||
705 | 70 | remove_source(*m_sources.begin()); | ||
706 | 71 | |||
707 | 63 | if (m_rebuild_tag) | 72 | if (m_rebuild_tag) |
708 | 64 | g_source_remove(m_rebuild_tag); | 73 | g_source_remove(m_rebuild_tag); |
709 | 65 | 74 | ||
710 | @@ -83,26 +92,31 @@ | |||
711 | 83 | } | 92 | } |
712 | 84 | else | 93 | else |
713 | 85 | { | 94 | { |
714 | 95 | g_signal_connect(r, "source-added", G_CALLBACK(on_source_added), gself); | ||
715 | 96 | g_signal_connect(r, "source-removed", G_CALLBACK(on_source_removed), gself); | ||
716 | 97 | g_signal_connect(r, "source-changed", G_CALLBACK(on_source_changed), gself); | ||
717 | 98 | g_signal_connect(r, "source-disabled", G_CALLBACK(on_source_disabled), gself); | ||
718 | 99 | g_signal_connect(r, "source-enabled", G_CALLBACK(on_source_enabled), gself); | ||
719 | 100 | |||
720 | 86 | auto self = static_cast<Impl*>(gself); | 101 | auto self = static_cast<Impl*>(gself); |
721 | 87 | |||
722 | 88 | g_signal_connect(r, "source-added", G_CALLBACK(on_source_added), self); | ||
723 | 89 | g_signal_connect(r, "source-removed", G_CALLBACK(on_source_removed), self); | ||
724 | 90 | g_signal_connect(r, "source-changed", G_CALLBACK(on_source_changed), self); | ||
725 | 91 | g_signal_connect(r, "source-disabled", G_CALLBACK(on_source_disabled), self); | ||
726 | 92 | g_signal_connect(r, "source-enabled", G_CALLBACK(on_source_enabled), self); | ||
727 | 93 | |||
728 | 94 | self->m_source_registry = r; | 102 | self->m_source_registry = r; |
734 | 95 | 103 | self->add_sources_by_extension(E_SOURCE_EXTENSION_CALENDAR); | |
735 | 96 | GList* sources = e_source_registry_list_sources(r, E_SOURCE_EXTENSION_CALENDAR); | 104 | self->add_sources_by_extension(E_SOURCE_EXTENSION_TASK_LIST); |
731 | 97 | for (auto l=sources; l!=nullptr; l=l->next) | ||
732 | 98 | on_source_added(r, E_SOURCE(l->data), gself); | ||
733 | 99 | g_list_free_full(sources, g_object_unref); | ||
736 | 100 | } | 105 | } |
737 | 101 | } | 106 | } |
738 | 102 | 107 | ||
739 | 108 | void add_sources_by_extension(const char* extension) | ||
740 | 109 | { | ||
741 | 110 | auto& r = m_source_registry; | ||
742 | 111 | auto sources = e_source_registry_list_sources(r, extension); | ||
743 | 112 | for (auto l=sources; l!=nullptr; l=l->next) | ||
744 | 113 | on_source_added(r, E_SOURCE(l->data), this); | ||
745 | 114 | g_list_free_full(sources, g_object_unref); | ||
746 | 115 | } | ||
747 | 116 | |||
748 | 103 | static void on_source_added(ESourceRegistry* registry, ESource* source, gpointer gself) | 117 | static void on_source_added(ESourceRegistry* registry, ESource* source, gpointer gself) |
749 | 104 | { | 118 | { |
751 | 105 | auto self = static_cast<PlannerEds::Impl*>(gself); | 119 | auto self = static_cast<Impl*>(gself); |
752 | 106 | 120 | ||
753 | 107 | self->m_sources.insert(E_SOURCE(g_object_ref(source))); | 121 | self->m_sources.insert(E_SOURCE(g_object_ref(source))); |
754 | 108 | 122 | ||
755 | @@ -112,10 +126,19 @@ | |||
756 | 112 | 126 | ||
757 | 113 | static void on_source_enabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) | 127 | static void on_source_enabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) |
758 | 114 | { | 128 | { |
761 | 115 | auto self = static_cast<PlannerEds::Impl*>(gself); | 129 | auto self = static_cast<Impl*>(gself); |
762 | 116 | 130 | ECalClientSourceType source_type; | |
763 | 131 | |||
764 | 132 | if (e_source_has_extension(source, E_SOURCE_EXTENSION_CALENDAR)) | ||
765 | 133 | source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; | ||
766 | 134 | else if (e_source_has_extension(source, E_SOURCE_EXTENSION_TASK_LIST)) | ||
767 | 135 | source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; | ||
768 | 136 | else | ||
769 | 137 | g_assert_not_reached(); | ||
770 | 138 | |||
771 | 139 | g_debug("connecting a client to source %s", e_source_get_uid(source)); | ||
772 | 117 | e_cal_client_connect(source, | 140 | e_cal_client_connect(source, |
774 | 118 | E_CAL_CLIENT_SOURCE_TYPE_EVENTS, | 141 | source_type, |
775 | 119 | self->m_cancellable, | 142 | self->m_cancellable, |
776 | 120 | on_client_connected, | 143 | on_client_connected, |
777 | 121 | gself); | 144 | gself); |
778 | @@ -134,45 +157,118 @@ | |||
779 | 134 | } | 157 | } |
780 | 135 | else | 158 | else |
781 | 136 | { | 159 | { |
791 | 137 | // we've got a new connected ECalClient, so store it & notify clients | 160 | // add the client to our collection |
792 | 138 | g_object_set_qdata_full(G_OBJECT(e_client_get_source(client)), | 161 | auto self = static_cast<Impl*>(gself); |
793 | 139 | source_client_quark(), | 162 | g_debug("got a client for %s", e_cal_client_get_local_attachment_store(E_CAL_CLIENT(client))); |
794 | 140 | client, | 163 | self->m_clients[e_client_get_source(client)] = E_CAL_CLIENT(client); |
795 | 141 | g_object_unref); | 164 | |
796 | 142 | 165 | // now create a view for it so that we can listen for changes | |
797 | 143 | g_debug("client connected; calling rebuildSoon()"); | 166 | e_cal_client_get_view (E_CAL_CLIENT(client), |
798 | 144 | static_cast<Impl*>(gself)->rebuildSoon(); | 167 | "#t", // match all |
799 | 145 | } | 168 | self->m_cancellable, |
800 | 169 | on_client_view_ready, | ||
801 | 170 | self); | ||
802 | 171 | |||
803 | 172 | g_debug("client connected; calling rebuild_soon()"); | ||
804 | 173 | self->rebuild_soon(ALL); | ||
805 | 174 | } | ||
806 | 175 | } | ||
807 | 176 | |||
808 | 177 | static void on_client_view_ready (GObject* client, GAsyncResult* res, gpointer gself) | ||
809 | 178 | { | ||
810 | 179 | GError* error = nullptr; | ||
811 | 180 | ECalClientView* view = nullptr; | ||
812 | 181 | |||
813 | 182 | if (e_cal_client_get_view_finish (E_CAL_CLIENT(client), res, &view, &error)) | ||
814 | 183 | { | ||
815 | 184 | // add the view to our collection | ||
816 | 185 | e_cal_client_view_start(view, &error); | ||
817 | 186 | g_debug("got a view for %s", e_cal_client_get_local_attachment_store(E_CAL_CLIENT(client))); | ||
818 | 187 | auto self = static_cast<Impl*>(gself); | ||
819 | 188 | self->m_views[e_client_get_source(E_CLIENT(client))] = view; | ||
820 | 189 | |||
821 | 190 | g_signal_connect(view, "objects-added", G_CALLBACK(on_view_objects_added), self); | ||
822 | 191 | g_signal_connect(view, "objects-modified", G_CALLBACK(on_view_objects_modified), self); | ||
823 | 192 | g_signal_connect(view, "objects-removed", G_CALLBACK(on_view_objects_removed), self); | ||
824 | 193 | g_debug("view connected; calling rebuild_soon()"); | ||
825 | 194 | self->rebuild_soon(ALL); | ||
826 | 195 | } | ||
827 | 196 | else if(error != nullptr) | ||
828 | 197 | { | ||
829 | 198 | if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) | ||
830 | 199 | g_warning("indicator-datetime cannot get View to EDS client: %s", error->message); | ||
831 | 200 | |||
832 | 201 | g_error_free(error); | ||
833 | 202 | } | ||
834 | 203 | } | ||
835 | 204 | |||
836 | 205 | static void on_view_objects_added(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) | ||
837 | 206 | { | ||
838 | 207 | g_debug("%s", G_STRFUNC); | ||
839 | 208 | static_cast<Impl*>(gself)->rebuild_soon(ALL); | ||
840 | 209 | } | ||
841 | 210 | static void on_view_objects_modified(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) | ||
842 | 211 | { | ||
843 | 212 | g_debug("%s", G_STRFUNC); | ||
844 | 213 | static_cast<Impl*>(gself)->rebuild_soon(ALL); | ||
845 | 214 | } | ||
846 | 215 | static void on_view_objects_removed(ECalClientView* /*view*/, gpointer /*objects*/, gpointer gself) | ||
847 | 216 | { | ||
848 | 217 | g_debug("%s", G_STRFUNC); | ||
849 | 218 | static_cast<Impl*>(gself)->rebuild_soon(ALL); | ||
850 | 146 | } | 219 | } |
851 | 147 | 220 | ||
852 | 148 | static void on_source_disabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) | 221 | static void on_source_disabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) |
853 | 149 | { | 222 | { |
874 | 150 | gpointer e_cal_client; | 223 | static_cast<Impl*>(gself)->disable_source(source); |
875 | 151 | 224 | } | |
876 | 152 | // if this source has a connected ECalClient, remove it & notify clients | 225 | void disable_source(ESource* source) |
877 | 153 | if ((e_cal_client = g_object_steal_qdata(G_OBJECT(source), source_client_quark()))) | 226 | { |
878 | 154 | { | 227 | // if an ECalClientView is associated with this source, remove it |
879 | 155 | g_object_unref(e_cal_client); | 228 | auto vit = m_views.find(source); |
880 | 156 | 229 | if (vit != m_views.end()) | |
881 | 157 | g_debug("source disabled; calling rebuildSoon()"); | 230 | { |
882 | 158 | static_cast<Impl*>(gself)->rebuildSoon(); | 231 | auto& view = vit->second; |
883 | 159 | } | 232 | e_cal_client_view_stop(view, nullptr); |
884 | 160 | } | 233 | const auto n_disconnected = g_signal_handlers_disconnect_by_data(view, this); |
885 | 161 | 234 | g_warn_if_fail(n_disconnected == 3); | |
886 | 162 | static void on_source_removed(ESourceRegistry* registry, ESource* source, gpointer gself) | 235 | g_object_unref(view); |
887 | 163 | { | 236 | m_views.erase(vit); |
888 | 164 | auto self = static_cast<PlannerEds::Impl*>(gself); | 237 | rebuild_soon(ALL); |
889 | 165 | 238 | } | |
890 | 166 | on_source_disabled(registry, source, gself); | 239 | |
891 | 167 | 240 | // if an ECalClient is associated with this source, remove it | |
892 | 168 | self->m_sources.erase(source); | 241 | auto cit = m_clients.find(source); |
893 | 169 | g_object_unref(source); | 242 | if (cit != m_clients.end()) |
894 | 243 | { | ||
895 | 244 | auto& client = cit->second; | ||
896 | 245 | g_object_unref(client); | ||
897 | 246 | m_clients.erase(cit); | ||
898 | 247 | rebuild_soon(ALL); | ||
899 | 248 | } | ||
900 | 249 | } | ||
901 | 250 | |||
902 | 251 | static void on_source_removed(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) | ||
903 | 252 | { | ||
904 | 253 | static_cast<Impl*>(gself)->remove_source(source); | ||
905 | 254 | } | ||
906 | 255 | void remove_source(ESource* source) | ||
907 | 256 | { | ||
908 | 257 | disable_source(source); | ||
909 | 258 | |||
910 | 259 | auto sit = m_sources.find(source); | ||
911 | 260 | if (sit != m_sources.end()) | ||
912 | 261 | { | ||
913 | 262 | g_object_unref(*sit); | ||
914 | 263 | m_sources.erase(sit); | ||
915 | 264 | rebuild_soon(ALL); | ||
916 | 265 | } | ||
917 | 170 | } | 266 | } |
918 | 171 | 267 | ||
919 | 172 | static void on_source_changed(ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) | 268 | static void on_source_changed(ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) |
920 | 173 | { | 269 | { |
923 | 174 | g_debug("source changed; calling rebuildSoon()"); | 270 | g_debug("source changed; calling rebuild_soon()"); |
924 | 175 | static_cast<Impl*>(gself)->rebuildSoon(); | 271 | static_cast<Impl*>(gself)->rebuild_soon(ALL); |
925 | 176 | } | 272 | } |
926 | 177 | 273 | ||
927 | 178 | private: | 274 | private: |
928 | @@ -196,58 +292,72 @@ | |||
929 | 196 | task(task_in), client(client_in), color(color_in) {} | 292 | task(task_in), client(client_in), color(color_in) {} |
930 | 197 | }; | 293 | }; |
931 | 198 | 294 | ||
933 | 199 | void rebuildSoon() | 295 | void rebuild_soon(int rebuild_flags) |
934 | 200 | { | 296 | { |
936 | 201 | const static guint ARBITRARY_INTERVAL_SECS = 2; | 297 | static const guint ARBITRARY_INTERVAL_SECS = 2; |
937 | 298 | |||
938 | 299 | m_rebuild_flags |= rebuild_flags; | ||
939 | 202 | 300 | ||
940 | 203 | if (m_rebuild_tag == 0) | 301 | if (m_rebuild_tag == 0) |
942 | 204 | m_rebuild_tag = g_timeout_add_seconds(ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); | 302 | m_rebuild_tag = g_timeout_add_seconds(ARBITRARY_INTERVAL_SECS, rebuild_now_static, this); |
943 | 205 | } | 303 | } |
944 | 206 | 304 | ||
946 | 207 | static gboolean rebuildNowStatic(gpointer gself) | 305 | static gboolean rebuild_now_static(gpointer gself) |
947 | 208 | { | 306 | { |
948 | 209 | auto self = static_cast<Impl*>(gself); | 307 | auto self = static_cast<Impl*>(gself); |
949 | 308 | const auto flags = self->m_rebuild_flags; | ||
950 | 210 | self->m_rebuild_tag = 0; | 309 | self->m_rebuild_tag = 0; |
952 | 211 | self->rebuildNow(); | 310 | self->m_rebuild_flags = 0; |
953 | 311 | self->rebuild_now(flags); | ||
954 | 212 | return G_SOURCE_REMOVE; | 312 | return G_SOURCE_REMOVE; |
955 | 213 | } | 313 | } |
956 | 214 | 314 | ||
993 | 215 | void rebuildNow() | 315 | void rebuild_now(int rebuild_flags) |
994 | 216 | { | 316 | { |
995 | 217 | const auto calendar_date = m_owner.time.get().get(); | 317 | if (rebuild_flags & UPCOMING) |
996 | 218 | GDateTime* begin; | 318 | rebuild_upcoming(); |
997 | 219 | GDateTime* end; | 319 | |
998 | 220 | int y, m, d; | 320 | if (rebuild_flags & MONTH) |
999 | 221 | 321 | rebuild_month(); | |
1000 | 222 | // get all the appointments in the calendar month | 322 | } |
1001 | 223 | g_date_time_get_ymd(calendar_date, &y, &m, &d); | 323 | |
1002 | 224 | begin = g_date_time_new_local(y, m, 1, 0, 0, 0.1); | 324 | void rebuild_month() |
1003 | 225 | end = g_date_time_new_local(y, m, g_date_get_days_in_month(GDateMonth(m),GDateYear(y)), 23, 59, 59.9); | 325 | { |
1004 | 226 | if (begin && end) | 326 | const auto ref = m_owner.time.get().get(); |
1005 | 227 | { | 327 | auto month_begin = g_date_time_add_full(ref, |
1006 | 228 | getAppointments(begin, end, [this](const std::vector<Appointment>& appointments) { | 328 | 0, // subtract no years |
1007 | 229 | g_debug("got %d appointments in this calendar month", (int)appointments.size()); | 329 | 0, // subtract no months |
1008 | 230 | m_owner.this_month.set(appointments); | 330 | -(g_date_time_get_day_of_month(ref)-1), |
1009 | 231 | }); | 331 | -g_date_time_get_hour(ref), |
1010 | 232 | } | 332 | -g_date_time_get_minute(ref), |
1011 | 233 | g_clear_pointer(&begin, g_date_time_unref); | 333 | -g_date_time_get_seconds(ref)); |
1012 | 234 | g_clear_pointer(&end, g_date_time_unref); | 334 | auto month_end = g_date_time_add_full(month_begin, 0, 1, 0, 0, 0, -0.1); |
1013 | 235 | 335 | ||
1014 | 236 | // get the upcoming appointments | 336 | get_appointments(month_begin, month_end, [this](const std::vector<Appointment>& appointments) { |
1015 | 237 | begin = g_date_time_ref(calendar_date); | 337 | g_debug("got %d appointments in this calendar month", (int)appointments.size()); |
1016 | 238 | end = g_date_time_add_months(begin, 1); | 338 | m_owner.this_month.set(appointments); |
1017 | 239 | if (begin && end) | 339 | }); |
1018 | 240 | { | 340 | |
1019 | 241 | getAppointments(begin, end, [this](const std::vector<Appointment>& appointments) { | 341 | g_date_time_unref(month_end); |
1020 | 242 | g_debug("got %d upcoming appointments", (int)appointments.size()); | 342 | g_date_time_unref(month_begin); |
1021 | 243 | m_owner.upcoming.set(appointments); | 343 | } |
1022 | 244 | }); | 344 | |
1023 | 245 | } | 345 | void rebuild_upcoming() |
1024 | 246 | g_clear_pointer(&begin, g_date_time_unref); | 346 | { |
1025 | 247 | g_clear_pointer(&end, g_date_time_unref); | 347 | const auto ref = m_clock->localtime(); |
1026 | 248 | } | 348 | const auto begin = g_date_time_add_minutes(ref.get(),-10); |
1027 | 249 | 349 | const auto end = g_date_time_add_months(begin,1); | |
1028 | 250 | void getAppointments(GDateTime* begin_dt, GDateTime* end_dt, appointment_func func) | 350 | |
1029 | 351 | get_appointments(begin, end, [this](const std::vector<Appointment>& appointments) { | ||
1030 | 352 | g_debug("got %d upcoming appointments", (int)appointments.size()); | ||
1031 | 353 | m_owner.upcoming.set(appointments); | ||
1032 | 354 | }); | ||
1033 | 355 | |||
1034 | 356 | g_date_time_unref(end); | ||
1035 | 357 | g_date_time_unref(begin); | ||
1036 | 358 | } | ||
1037 | 359 | |||
1038 | 360 | void get_appointments(GDateTime* begin_dt, GDateTime* end_dt, appointment_func func) | ||
1039 | 251 | { | 361 | { |
1040 | 252 | const auto begin = g_date_time_to_unix(begin_dt); | 362 | const auto begin = g_date_time_to_unix(begin_dt); |
1041 | 253 | const auto end = g_date_time_to_unix(end_dt); | 363 | const auto end = g_date_time_to_unix(end_dt); |
1042 | @@ -286,16 +396,14 @@ | |||
1043 | 286 | delete task; | 396 | delete task; |
1044 | 287 | }); | 397 | }); |
1045 | 288 | 398 | ||
1047 | 289 | for (auto& source : m_sources) | 399 | for (auto& kv : m_clients) |
1048 | 290 | { | 400 | { |
1053 | 291 | auto client = E_CAL_CLIENT(g_object_get_qdata(G_OBJECT(source), source_client_quark())); | 401 | auto& client = kv.second; |
1050 | 292 | if (client == nullptr) | ||
1051 | 293 | continue; | ||
1052 | 294 | |||
1054 | 295 | if (default_timezone != nullptr) | 402 | if (default_timezone != nullptr) |
1055 | 296 | e_cal_client_set_default_timezone(client, default_timezone); | 403 | e_cal_client_set_default_timezone(client, default_timezone); |
1056 | 297 | 404 | ||
1057 | 298 | // start a new subtask to enumerate all the components in this client. | 405 | // start a new subtask to enumerate all the components in this client. |
1058 | 406 | auto& source = kv.first; | ||
1059 | 299 | auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); | 407 | auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); |
1060 | 300 | const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); | 408 | const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); |
1061 | 301 | g_debug("calling e_cal_client_generate_instances for %p", (void*)client); | 409 | g_debug("calling e_cal_client_generate_instances for %p", (void*)client); |
1062 | @@ -407,16 +515,19 @@ | |||
1063 | 407 | delete subtask; | 515 | delete subtask; |
1064 | 408 | } | 516 | } |
1065 | 409 | 517 | ||
1066 | 410 | private: | ||
1067 | 411 | |||
1068 | 412 | PlannerEds& m_owner; | 518 | PlannerEds& m_owner; |
1069 | 519 | std::shared_ptr<Clock> m_clock; | ||
1070 | 413 | std::set<ESource*> m_sources; | 520 | std::set<ESource*> m_sources; |
1073 | 414 | GCancellable * m_cancellable = nullptr; | 521 | std::map<ESource*,ECalClient*> m_clients; |
1074 | 415 | ESourceRegistry * m_source_registry = nullptr; | 522 | std::map<ESource*,ECalClientView*> m_views; |
1075 | 523 | GCancellable* m_cancellable = nullptr; | ||
1076 | 524 | ESourceRegistry* m_source_registry = nullptr; | ||
1077 | 416 | guint m_rebuild_tag = 0; | 525 | guint m_rebuild_tag = 0; |
1078 | 526 | guint m_rebuild_flags = 0; | ||
1079 | 527 | enum { UPCOMING=(1<<0), MONTH=(1<<1), ALL=UPCOMING|MONTH }; | ||
1080 | 417 | }; | 528 | }; |
1081 | 418 | 529 | ||
1083 | 419 | PlannerEds::PlannerEds(): p(new Impl(*this)) {} | 530 | PlannerEds::PlannerEds(const std::shared_ptr<Clock>& clock): p(new Impl(*this, clock)) {} |
1084 | 420 | 531 | ||
1085 | 421 | PlannerEds::~PlannerEds() =default; | 532 | PlannerEds::~PlannerEds() =default; |
1086 | 422 | 533 | ||
1087 | 423 | 534 | ||
1088 | === added file 'src/snap.cpp' | |||
1089 | --- src/snap.cpp 1970-01-01 00:00:00 +0000 | |||
1090 | +++ src/snap.cpp 2014-02-19 15:36:23 +0000 | |||
1091 | @@ -0,0 +1,256 @@ | |||
1092 | 1 | /* | ||
1093 | 2 | * Copyright 2014 Canonical Ltd. | ||
1094 | 3 | * | ||
1095 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1096 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
1097 | 6 | * by the Free Software Foundation. | ||
1098 | 7 | * | ||
1099 | 8 | * This program is distributed in the hope that it will be useful, but | ||
1100 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
1101 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
1102 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
1103 | 12 | * | ||
1104 | 13 | * You should have received a copy of the GNU General Public License along | ||
1105 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1106 | 15 | * | ||
1107 | 16 | * Authors: | ||
1108 | 17 | * Charles Kerr <charles.kerr@canonical.com> | ||
1109 | 18 | */ | ||
1110 | 19 | |||
1111 | 20 | #include <datetime/appointment.h> | ||
1112 | 21 | #include <datetime/formatter.h> | ||
1113 | 22 | #include <datetime/snap.h> | ||
1114 | 23 | |||
1115 | 24 | #include <canberra.h> | ||
1116 | 25 | #include <libnotify/notify.h> | ||
1117 | 26 | |||
1118 | 27 | #include <glib/gi18n.h> | ||
1119 | 28 | #include <glib.h> | ||
1120 | 29 | |||
1121 | 30 | #define ALARM_SOUND_FILENAME "/usr/share/sounds/ubuntu/stereo/phone-incoming-call.ogg" | ||
1122 | 31 | |||
1123 | 32 | namespace unity { | ||
1124 | 33 | namespace indicator { | ||
1125 | 34 | namespace datetime { | ||
1126 | 35 | |||
1127 | 36 | /*** | ||
1128 | 37 | **** | ||
1129 | 38 | ***/ | ||
1130 | 39 | |||
1131 | 40 | namespace | ||
1132 | 41 | { | ||
1133 | 42 | |||
1134 | 43 | /** | ||
1135 | 44 | *** libcanberra -- play sounds | ||
1136 | 45 | **/ | ||
1137 | 46 | |||
1138 | 47 | // arbitrary number, but we need a consistent id for play/cancel | ||
1139 | 48 | const int32_t alarm_ca_id = 1; | ||
1140 | 49 | |||
1141 | 50 | gboolean media_cached = FALSE; | ||
1142 | 51 | ca_context *c_context = nullptr; | ||
1143 | 52 | guint timeout_tag = 0; | ||
1144 | 53 | |||
1145 | 54 | ca_context* get_ca_context() | ||
1146 | 55 | { | ||
1147 | 56 | if (G_UNLIKELY(c_context == nullptr)) | ||
1148 | 57 | { | ||
1149 | 58 | int rv; | ||
1150 | 59 | |||
1151 | 60 | if ((rv = ca_context_create(&c_context)) != CA_SUCCESS) | ||
1152 | 61 | { | ||
1153 | 62 | g_warning("Failed to create canberra context: %s\n", ca_strerror(rv)); | ||
1154 | 63 | c_context = nullptr; | ||
1155 | 64 | } | ||
1156 | 65 | else | ||
1157 | 66 | { | ||
1158 | 67 | const char* filename = ALARM_SOUND_FILENAME; | ||
1159 | 68 | rv = ca_context_cache(c_context, | ||
1160 | 69 | CA_PROP_EVENT_ID, "alarm", | ||
1161 | 70 | CA_PROP_MEDIA_FILENAME, filename, | ||
1162 | 71 | CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", | ||
1163 | 72 | NULL); | ||
1164 | 73 | media_cached = rv == CA_SUCCESS; | ||
1165 | 74 | if (rv != CA_SUCCESS) | ||
1166 | 75 | g_warning("Couldn't add '%s' to canberra cache: %s", filename, ca_strerror(rv)); | ||
1167 | 76 | } | ||
1168 | 77 | } | ||
1169 | 78 | |||
1170 | 79 | return c_context; | ||
1171 | 80 | } | ||
1172 | 81 | |||
1173 | 82 | void play_alarm_sound(); | ||
1174 | 83 | |||
1175 | 84 | gboolean play_alarm_sound_idle (gpointer) | ||
1176 | 85 | { | ||
1177 | 86 | timeout_tag = 0; | ||
1178 | 87 | play_alarm_sound(); | ||
1179 | 88 | return G_SOURCE_REMOVE; | ||
1180 | 89 | } | ||
1181 | 90 | |||
1182 | 91 | void on_alarm_play_done (ca_context* /*context*/, uint32_t /*id*/, int rv, void* /*user_data*/) | ||
1183 | 92 | { | ||
1184 | 93 | // wait one second, then play it again | ||
1185 | 94 | if ((rv == CA_SUCCESS) && (timeout_tag == 0)) | ||
1186 | 95 | timeout_tag = g_timeout_add_seconds (1, play_alarm_sound_idle, nullptr); | ||
1187 | 96 | } | ||
1188 | 97 | |||
1189 | 98 | void play_alarm_sound() | ||
1190 | 99 | { | ||
1191 | 100 | const gchar* filename = ALARM_SOUND_FILENAME; | ||
1192 | 101 | auto context = get_ca_context(); | ||
1193 | 102 | g_return_if_fail(context != nullptr); | ||
1194 | 103 | |||
1195 | 104 | ca_proplist* props = nullptr; | ||
1196 | 105 | ca_proplist_create(&props); | ||
1197 | 106 | if (media_cached) | ||
1198 | 107 | ca_proplist_sets(props, CA_PROP_EVENT_ID, "alarm"); | ||
1199 | 108 | ca_proplist_sets(props, CA_PROP_MEDIA_FILENAME, filename); | ||
1200 | 109 | |||
1201 | 110 | const auto rv = ca_context_play_full(context, alarm_ca_id, props, on_alarm_play_done, nullptr); | ||
1202 | 111 | if (rv != CA_SUCCESS) | ||
1203 | 112 | g_warning("Failed to play file '%s': %s", filename, ca_strerror(rv)); | ||
1204 | 113 | |||
1205 | 114 | g_clear_pointer(&props, ca_proplist_destroy); | ||
1206 | 115 | } | ||
1207 | 116 | |||
1208 | 117 | void stop_alarm_sound() | ||
1209 | 118 | { | ||
1210 | 119 | auto context = get_ca_context(); | ||
1211 | 120 | if (context != nullptr) | ||
1212 | 121 | { | ||
1213 | 122 | const auto rv = ca_context_cancel(context, alarm_ca_id); | ||
1214 | 123 | if (rv != CA_SUCCESS) | ||
1215 | 124 | g_warning("Failed to cancel alarm sound: %s", ca_strerror(rv)); | ||
1216 | 125 | } | ||
1217 | 126 | |||
1218 | 127 | if (timeout_tag != 0) | ||
1219 | 128 | { | ||
1220 | 129 | g_source_remove(timeout_tag); | ||
1221 | 130 | timeout_tag = 0; | ||
1222 | 131 | } | ||
1223 | 132 | } | ||
1224 | 133 | |||
1225 | 134 | /** | ||
1226 | 135 | *** libnotify -- snap decisions | ||
1227 | 136 | **/ | ||
1228 | 137 | |||
1229 | 138 | void first_time_init() | ||
1230 | 139 | { | ||
1231 | 140 | static bool inited = false; | ||
1232 | 141 | |||
1233 | 142 | if (G_UNLIKELY(!inited)) | ||
1234 | 143 | { | ||
1235 | 144 | inited = true; | ||
1236 | 145 | |||
1237 | 146 | if(!notify_init("indicator-datetime-service")) | ||
1238 | 147 | g_critical("libnotify initialization failed"); | ||
1239 | 148 | } | ||
1240 | 149 | } | ||
1241 | 150 | |||
1242 | 151 | struct SnapData | ||
1243 | 152 | { | ||
1244 | 153 | Snap::appointment_func show; | ||
1245 | 154 | Snap::appointment_func dismiss; | ||
1246 | 155 | Appointment appointment; | ||
1247 | 156 | }; | ||
1248 | 157 | |||
1249 | 158 | void on_snap_show(NotifyNotification*, gchar* /*action*/, gpointer gdata) | ||
1250 | 159 | { | ||
1251 | 160 | stop_alarm_sound(); | ||
1252 | 161 | auto data = static_cast<SnapData*>(gdata); | ||
1253 | 162 | data->show(data->appointment); | ||
1254 | 163 | } | ||
1255 | 164 | |||
1256 | 165 | void on_snap_dismiss(NotifyNotification*, gchar* /*action*/, gpointer gdata) | ||
1257 | 166 | { | ||
1258 | 167 | stop_alarm_sound(); | ||
1259 | 168 | auto data = static_cast<SnapData*>(gdata); | ||
1260 | 169 | data->dismiss(data->appointment); | ||
1261 | 170 | } | ||
1262 | 171 | |||
1263 | 172 | void snap_data_destroy_notify(gpointer gdata) | ||
1264 | 173 | { | ||
1265 | 174 | delete static_cast<SnapData*>(gdata); | ||
1266 | 175 | } | ||
1267 | 176 | |||
1268 | 177 | void show_snap_decision(SnapData* data) | ||
1269 | 178 | { | ||
1270 | 179 | const Appointment& appointment = data->appointment; | ||
1271 | 180 | |||
1272 | 181 | const auto timestr = appointment.begin.format("%a, %X"); | ||
1273 | 182 | auto title = g_strdup_printf(_("Alarm %s"), timestr.c_str()); | ||
1274 | 183 | const auto body = appointment.summary; | ||
1275 | 184 | const gchar* icon_name = "alarm-clock"; | ||
1276 | 185 | |||
1277 | 186 | auto nn = notify_notification_new(title, body.c_str(), icon_name); | ||
1278 | 187 | notify_notification_set_hint_string(nn, "x-canonical-snap-decisions", "true"); | ||
1279 | 188 | notify_notification_set_hint_string(nn, "x-canonical-private-button-tint", "true"); | ||
1280 | 189 | notify_notification_add_action(nn, "show", _("Show"), on_snap_show, data, nullptr); | ||
1281 | 190 | notify_notification_add_action(nn, "dismiss", _("Dismiss"), on_snap_dismiss, data, nullptr); | ||
1282 | 191 | g_object_set_data_full(G_OBJECT(nn), "snap-data", data, snap_data_destroy_notify); | ||
1283 | 192 | |||
1284 | 193 | GError * error = nullptr; | ||
1285 | 194 | notify_notification_show(nn, &error); | ||
1286 | 195 | if (error != NULL) | ||
1287 | 196 | { | ||
1288 | 197 | g_warning("Unable to show snap decision for '%s': %s", body.c_str(), error->message); | ||
1289 | 198 | g_error_free(error); | ||
1290 | 199 | data->show(data->appointment); | ||
1291 | 200 | } | ||
1292 | 201 | |||
1293 | 202 | g_free(title); | ||
1294 | 203 | } | ||
1295 | 204 | |||
1296 | 205 | /** | ||
1297 | 206 | *** | ||
1298 | 207 | **/ | ||
1299 | 208 | |||
1300 | 209 | void notify(const Appointment& appointment, | ||
1301 | 210 | Snap::appointment_func show, | ||
1302 | 211 | Snap::appointment_func dismiss) | ||
1303 | 212 | { | ||
1304 | 213 | auto data = new SnapData; | ||
1305 | 214 | data->appointment = appointment; | ||
1306 | 215 | data->show = show; | ||
1307 | 216 | data->dismiss = dismiss; | ||
1308 | 217 | |||
1309 | 218 | play_alarm_sound(); | ||
1310 | 219 | show_snap_decision(data); | ||
1311 | 220 | } | ||
1312 | 221 | |||
1313 | 222 | } // unnamed namespace | ||
1314 | 223 | |||
1315 | 224 | |||
1316 | 225 | /*** | ||
1317 | 226 | **** | ||
1318 | 227 | ***/ | ||
1319 | 228 | |||
1320 | 229 | Snap::Snap() | ||
1321 | 230 | { | ||
1322 | 231 | first_time_init(); | ||
1323 | 232 | } | ||
1324 | 233 | |||
1325 | 234 | Snap::~Snap() | ||
1326 | 235 | { | ||
1327 | 236 | media_cached = false; | ||
1328 | 237 | g_clear_pointer(&c_context, ca_context_destroy); | ||
1329 | 238 | } | ||
1330 | 239 | |||
1331 | 240 | void Snap::operator()(const Appointment& appointment, | ||
1332 | 241 | appointment_func show, | ||
1333 | 242 | appointment_func dismiss) | ||
1334 | 243 | { | ||
1335 | 244 | if (appointment.has_alarms) | ||
1336 | 245 | notify(appointment, show, dismiss); | ||
1337 | 246 | else | ||
1338 | 247 | dismiss(appointment); | ||
1339 | 248 | } | ||
1340 | 249 | |||
1341 | 250 | /*** | ||
1342 | 251 | **** | ||
1343 | 252 | ***/ | ||
1344 | 253 | |||
1345 | 254 | } // namespace datetime | ||
1346 | 255 | } // namespace indicator | ||
1347 | 256 | } // namespace unity | ||
1348 | 0 | 257 | ||
1349 | === modified file 'src/timezone-file.cpp' | |||
1350 | --- src/timezone-file.cpp 2014-02-19 15:36:23 +0000 | |||
1351 | +++ src/timezone-file.cpp 2014-02-19 15:36:23 +0000 | |||
1352 | @@ -32,7 +32,7 @@ | |||
1353 | 32 | 32 | ||
1354 | 33 | FileTimezone::FileTimezone(const std::string& filename) | 33 | FileTimezone::FileTimezone(const std::string& filename) |
1355 | 34 | { | 34 | { |
1357 | 35 | setFilename(filename); | 35 | set_filename(filename); |
1358 | 36 | } | 36 | } |
1359 | 37 | 37 | ||
1360 | 38 | FileTimezone::~FileTimezone() | 38 | FileTimezone::~FileTimezone() |
1361 | @@ -52,7 +52,7 @@ | |||
1362 | 52 | } | 52 | } |
1363 | 53 | 53 | ||
1364 | 54 | void | 54 | void |
1366 | 55 | FileTimezone::setFilename(const std::string& filename) | 55 | FileTimezone::set_filename(const std::string& filename) |
1367 | 56 | { | 56 | { |
1368 | 57 | clear(); | 57 | clear(); |
1369 | 58 | 58 | ||
1370 | @@ -79,7 +79,7 @@ | |||
1371 | 79 | } | 79 | } |
1372 | 80 | else | 80 | else |
1373 | 81 | { | 81 | { |
1375 | 82 | m_monitor_handler_id = g_signal_connect_swapped(m_monitor, "changed", G_CALLBACK(onFileChanged), this); | 82 | m_monitor_handler_id = g_signal_connect_swapped(m_monitor, "changed", G_CALLBACK(on_file_changed), this); |
1376 | 83 | g_debug("%s Monitoring timezone file '%s'", G_STRLOC, m_filename.c_str()); | 83 | g_debug("%s Monitoring timezone file '%s'", G_STRLOC, m_filename.c_str()); |
1377 | 84 | } | 84 | } |
1378 | 85 | 85 | ||
1379 | @@ -87,7 +87,7 @@ | |||
1380 | 87 | } | 87 | } |
1381 | 88 | 88 | ||
1382 | 89 | void | 89 | void |
1384 | 90 | FileTimezone::onFileChanged(gpointer gself) | 90 | FileTimezone::on_file_changed(gpointer gself) |
1385 | 91 | { | 91 | { |
1386 | 92 | static_cast<FileTimezone*>(gself)->reload(); | 92 | static_cast<FileTimezone*>(gself)->reload(); |
1387 | 93 | } | 93 | } |
1388 | 94 | 94 | ||
1389 | === modified file 'tests/CMakeLists.txt' | |||
1390 | --- tests/CMakeLists.txt 2014-01-29 03:42:05 +0000 | |||
1391 | +++ tests/CMakeLists.txt 2014-02-19 15:36:23 +0000 | |||
1392 | @@ -42,6 +42,7 @@ | |||
1393 | 42 | endfunction() | 42 | endfunction() |
1394 | 43 | add_test_by_name(test-actions) | 43 | add_test_by_name(test-actions) |
1395 | 44 | add_test_by_name(test-clock) | 44 | add_test_by_name(test-clock) |
1396 | 45 | add_test_by_name(test-clock-watcher) | ||
1397 | 45 | add_test_by_name(test-exporter) | 46 | add_test_by_name(test-exporter) |
1398 | 46 | add_test_by_name(test-formatter) | 47 | add_test_by_name(test-formatter) |
1399 | 47 | add_test_by_name(test-live-actions) | 48 | add_test_by_name(test-live-actions) |
1400 | @@ -52,6 +53,10 @@ | |||
1401 | 52 | add_test_by_name(test-timezone-file) | 53 | add_test_by_name(test-timezone-file) |
1402 | 53 | add_test_by_name(test-utils) | 54 | add_test_by_name(test-utils) |
1403 | 54 | 55 | ||
1404 | 56 | set (TEST_NAME manual-test-snap) | ||
1405 | 57 | add_executable (${TEST_NAME} ${TEST_NAME}.cpp) | ||
1406 | 58 | add_dependencies (${TEST_NAME} libindicatordatetimeservice) | ||
1407 | 59 | target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) | ||
1408 | 55 | 60 | ||
1409 | 56 | # disabling the timezone unit tests because they require | 61 | # disabling the timezone unit tests because they require |
1410 | 57 | # https://code.launchpad.net/~ted/dbus-test-runner/multi-interface-test/+merge/199724 | 62 | # https://code.launchpad.net/~ted/dbus-test-runner/multi-interface-test/+merge/199724 |
1411 | 58 | 63 | ||
1412 | === modified file 'tests/geoclue-fixture.h' | |||
1413 | --- tests/geoclue-fixture.h 2014-01-17 03:23:57 +0000 | |||
1414 | +++ tests/geoclue-fixture.h 2014-02-19 15:36:23 +0000 | |||
1415 | @@ -95,7 +95,7 @@ | |||
1416 | 95 | 95 | ||
1417 | 96 | // I've looked and can't find where this extra ref is coming from. | 96 | // I've looked and can't find where this extra ref is coming from. |
1418 | 97 | // is there an unbalanced ref to the bus in the test harness?! | 97 | // is there an unbalanced ref to the bus in the test harness?! |
1420 | 98 | while (bus != NULL) | 98 | while (bus != nullptr) |
1421 | 99 | { | 99 | { |
1422 | 100 | g_object_unref (bus); | 100 | g_object_unref (bus); |
1423 | 101 | wait_msec (1000); | 101 | wait_msec (1000); |
1424 | 102 | 102 | ||
1425 | === added file 'tests/manual-test-snap.cpp' | |||
1426 | --- tests/manual-test-snap.cpp 1970-01-01 00:00:00 +0000 | |||
1427 | +++ tests/manual-test-snap.cpp 2014-02-19 15:36:23 +0000 | |||
1428 | @@ -0,0 +1,63 @@ | |||
1429 | 1 | |||
1430 | 2 | /* | ||
1431 | 3 | * Copyright 2013 Canonical Ltd. | ||
1432 | 4 | * | ||
1433 | 5 | * Authors: | ||
1434 | 6 | * Charles Kerr <charles.kerr@canonical.com> | ||
1435 | 7 | * | ||
1436 | 8 | * This program is free software: you can redistribute it and/or modify it | ||
1437 | 9 | * under the terms of the GNU General Public License version 3, as published | ||
1438 | 10 | * by the Free Software Foundation. | ||
1439 | 11 | * | ||
1440 | 12 | * This program is distributed in the hope that it will be useful, but | ||
1441 | 13 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
1442 | 14 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
1443 | 15 | * PURPOSE. See the GNU General Public License for more details. | ||
1444 | 16 | * | ||
1445 | 17 | * You should have received a copy of the GNU General Public License along | ||
1446 | 18 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1447 | 19 | */ | ||
1448 | 20 | |||
1449 | 21 | #include <datetime/appointment.h> | ||
1450 | 22 | #include <datetime/snap.h> | ||
1451 | 23 | |||
1452 | 24 | #include <glib.h> | ||
1453 | 25 | |||
1454 | 26 | using namespace unity::indicator::datetime; | ||
1455 | 27 | |||
1456 | 28 | /*** | ||
1457 | 29 | **** | ||
1458 | 30 | ***/ | ||
1459 | 31 | |||
1460 | 32 | int main() | ||
1461 | 33 | { | ||
1462 | 34 | Appointment a; | ||
1463 | 35 | a.color = "green"; | ||
1464 | 36 | a.summary = "Alarm"; | ||
1465 | 37 | a.url = "alarm:///hello-world"; | ||
1466 | 38 | a.uid = "D4B57D50247291478ED31DED17FF0A9838DED402"; | ||
1467 | 39 | a.is_event = false; | ||
1468 | 40 | a.is_daily = false; | ||
1469 | 41 | a.has_alarms = true; | ||
1470 | 42 | auto begin = g_date_time_new_local(2014,12,25,0,0,0); | ||
1471 | 43 | auto end = g_date_time_add_full(begin,0,0,1,0,0,-1); | ||
1472 | 44 | a.begin = begin; | ||
1473 | 45 | a.end = end; | ||
1474 | 46 | g_date_time_unref(end); | ||
1475 | 47 | g_date_time_unref(begin); | ||
1476 | 48 | |||
1477 | 49 | auto loop = g_main_loop_new(nullptr, false); | ||
1478 | 50 | auto show = [loop](const Appointment& appt){ | ||
1479 | 51 | g_message("You clicked 'show' for appt url '%s'", appt.url.c_str()); | ||
1480 | 52 | g_main_loop_quit(loop); | ||
1481 | 53 | }; | ||
1482 | 54 | auto dismiss = [loop](const Appointment&){ | ||
1483 | 55 | g_message("You clicked 'dismiss'"); | ||
1484 | 56 | g_main_loop_quit(loop); | ||
1485 | 57 | }; | ||
1486 | 58 | |||
1487 | 59 | Snap snap; | ||
1488 | 60 | snap(a, show, dismiss); | ||
1489 | 61 | g_main_loop_run(loop); | ||
1490 | 62 | return 0; | ||
1491 | 63 | } | ||
1492 | 0 | 64 | ||
1493 | === added file 'tests/test-clock-watcher.cpp' | |||
1494 | --- tests/test-clock-watcher.cpp 1970-01-01 00:00:00 +0000 | |||
1495 | +++ tests/test-clock-watcher.cpp 2014-02-19 15:36:23 +0000 | |||
1496 | @@ -0,0 +1,166 @@ | |||
1497 | 1 | /* | ||
1498 | 2 | * Copyright 2014 Canonical Ltd. | ||
1499 | 3 | * | ||
1500 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1501 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
1502 | 6 | * by the Free Software Foundation. | ||
1503 | 7 | * | ||
1504 | 8 | * This program is distributed in the hope that it will be useful, but | ||
1505 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
1506 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
1507 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
1508 | 12 | * | ||
1509 | 13 | * You should have received a copy of the GNU General Public License along | ||
1510 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1511 | 15 | * | ||
1512 | 16 | * Authors: | ||
1513 | 17 | * Charles Kerr <charles.kerr@canonical.com> | ||
1514 | 18 | */ | ||
1515 | 19 | |||
1516 | 20 | #include <datetime/clock-watcher.h> | ||
1517 | 21 | |||
1518 | 22 | #include <gtest/gtest.h> | ||
1519 | 23 | |||
1520 | 24 | #include "state-fixture.h" | ||
1521 | 25 | |||
1522 | 26 | using namespace unity::indicator::datetime; | ||
1523 | 27 | |||
1524 | 28 | class ClockWatcherFixture: public StateFixture | ||
1525 | 29 | { | ||
1526 | 30 | private: | ||
1527 | 31 | |||
1528 | 32 | typedef StateFixture super; | ||
1529 | 33 | |||
1530 | 34 | protected: | ||
1531 | 35 | |||
1532 | 36 | std::vector<std::string> m_triggered; | ||
1533 | 37 | std::unique_ptr<ClockWatcher> m_watcher; | ||
1534 | 38 | |||
1535 | 39 | void SetUp() | ||
1536 | 40 | { | ||
1537 | 41 | super::SetUp(); | ||
1538 | 42 | |||
1539 | 43 | m_watcher.reset(new ClockWatcherImpl(m_state)); | ||
1540 | 44 | m_watcher->alarm_reached().connect([this](const Appointment& appt){ | ||
1541 | 45 | m_triggered.push_back(appt.uid); | ||
1542 | 46 | }); | ||
1543 | 47 | |||
1544 | 48 | EXPECT_TRUE(m_triggered.empty()); | ||
1545 | 49 | } | ||
1546 | 50 | |||
1547 | 51 | void TearDown() | ||
1548 | 52 | { | ||
1549 | 53 | m_triggered.clear(); | ||
1550 | 54 | m_watcher.reset(); | ||
1551 | 55 | |||
1552 | 56 | super::TearDown(); | ||
1553 | 57 | } | ||
1554 | 58 | |||
1555 | 59 | std::vector<Appointment> build_some_appointments() | ||
1556 | 60 | { | ||
1557 | 61 | const auto now = m_state->clock->localtime(); | ||
1558 | 62 | auto tomorrow = g_date_time_add_days (now.get(), 1); | ||
1559 | 63 | auto tomorrow_begin = g_date_time_add_full (tomorrow, 0, 0, 0, | ||
1560 | 64 | -g_date_time_get_hour(tomorrow), | ||
1561 | 65 | -g_date_time_get_minute(tomorrow), | ||
1562 | 66 | -g_date_time_get_seconds(tomorrow)); | ||
1563 | 67 | auto tomorrow_end = g_date_time_add_full (tomorrow_begin, 0, 0, 1, 0, 0, -1); | ||
1564 | 68 | |||
1565 | 69 | Appointment a1; // an alarm clock appointment | ||
1566 | 70 | a1.color = "red"; | ||
1567 | 71 | a1.summary = "Alarm"; | ||
1568 | 72 | a1.summary = "http://www.example.com/"; | ||
1569 | 73 | a1.uid = "example"; | ||
1570 | 74 | a1.has_alarms = true; | ||
1571 | 75 | a1.begin = tomorrow_begin; | ||
1572 | 76 | a1.end = tomorrow_end; | ||
1573 | 77 | |||
1574 | 78 | auto ubermorgen_begin = g_date_time_add_days (tomorrow, 1); | ||
1575 | 79 | auto ubermorgen_end = g_date_time_add_full (tomorrow_begin, 0, 0, 1, 0, 0, -1); | ||
1576 | 80 | |||
1577 | 81 | Appointment a2; // a non-alarm appointment | ||
1578 | 82 | a2.color = "green"; | ||
1579 | 83 | a2.summary = "Other Text"; | ||
1580 | 84 | a2.summary = "http://www.monkey.com/"; | ||
1581 | 85 | a2.uid = "monkey"; | ||
1582 | 86 | a2.has_alarms = false; | ||
1583 | 87 | a2.begin = ubermorgen_begin; | ||
1584 | 88 | a2.end = ubermorgen_end; | ||
1585 | 89 | |||
1586 | 90 | // cleanup | ||
1587 | 91 | g_date_time_unref(ubermorgen_end); | ||
1588 | 92 | g_date_time_unref(ubermorgen_begin); | ||
1589 | 93 | g_date_time_unref(tomorrow_end); | ||
1590 | 94 | g_date_time_unref(tomorrow_begin); | ||
1591 | 95 | g_date_time_unref(tomorrow); | ||
1592 | 96 | |||
1593 | 97 | return std::vector<Appointment>({a1, a2}); | ||
1594 | 98 | } | ||
1595 | 99 | }; | ||
1596 | 100 | |||
1597 | 101 | /*** | ||
1598 | 102 | **** | ||
1599 | 103 | ***/ | ||
1600 | 104 | |||
1601 | 105 | TEST_F(ClockWatcherFixture, AppointmentsChanged) | ||
1602 | 106 | { | ||
1603 | 107 | // Add some appointments to the planner. | ||
1604 | 108 | // One of these matches our state's localtime, so that should get triggered. | ||
1605 | 109 | std::vector<Appointment> a = build_some_appointments(); | ||
1606 | 110 | a[0].begin = m_state->clock->localtime(); | ||
1607 | 111 | m_state->planner->upcoming.set(a); | ||
1608 | 112 | |||
1609 | 113 | // Confirm that it got fired | ||
1610 | 114 | EXPECT_EQ(1, m_triggered.size()); | ||
1611 | 115 | EXPECT_EQ(a[0].uid, m_triggered[0]); | ||
1612 | 116 | } | ||
1613 | 117 | |||
1614 | 118 | |||
1615 | 119 | TEST_F(ClockWatcherFixture, TimeChanged) | ||
1616 | 120 | { | ||
1617 | 121 | // Add some appointments to the planner. | ||
1618 | 122 | // Neither of these match the state's localtime, so nothing should be triggered. | ||
1619 | 123 | std::vector<Appointment> a = build_some_appointments(); | ||
1620 | 124 | m_state->planner->upcoming.set(a); | ||
1621 | 125 | EXPECT_TRUE(m_triggered.empty()); | ||
1622 | 126 | |||
1623 | 127 | // Set the state's clock to a time that matches one of the appointments. | ||
1624 | 128 | // That appointment should get triggered. | ||
1625 | 129 | m_mock_state->mock_clock->set_localtime(a[1].begin); | ||
1626 | 130 | EXPECT_EQ(1, m_triggered.size()); | ||
1627 | 131 | EXPECT_EQ(a[1].uid, m_triggered[0]); | ||
1628 | 132 | } | ||
1629 | 133 | |||
1630 | 134 | |||
1631 | 135 | TEST_F(ClockWatcherFixture, MoreThanOne) | ||
1632 | 136 | { | ||
1633 | 137 | const auto now = m_state->clock->localtime(); | ||
1634 | 138 | std::vector<Appointment> a = build_some_appointments(); | ||
1635 | 139 | a[0].begin = a[1].begin = now; | ||
1636 | 140 | m_state->planner->upcoming.set(a); | ||
1637 | 141 | |||
1638 | 142 | EXPECT_EQ(2, m_triggered.size()); | ||
1639 | 143 | EXPECT_EQ(a[0].uid, m_triggered[0]); | ||
1640 | 144 | EXPECT_EQ(a[1].uid, m_triggered[1]); | ||
1641 | 145 | } | ||
1642 | 146 | |||
1643 | 147 | |||
1644 | 148 | TEST_F(ClockWatcherFixture, NoDuplicates) | ||
1645 | 149 | { | ||
1646 | 150 | // Setup: add an appointment that gets triggered. | ||
1647 | 151 | const auto now = m_state->clock->localtime(); | ||
1648 | 152 | const std::vector<Appointment> appointments = build_some_appointments(); | ||
1649 | 153 | std::vector<Appointment> a; | ||
1650 | 154 | a.push_back(appointments[0]); | ||
1651 | 155 | a[0].begin = now; | ||
1652 | 156 | m_state->planner->upcoming.set(a); | ||
1653 | 157 | EXPECT_EQ(1, m_triggered.size()); | ||
1654 | 158 | EXPECT_EQ(a[0].uid, m_triggered[0]); | ||
1655 | 159 | |||
1656 | 160 | // Now change the appointment vector by adding one to it. | ||
1657 | 161 | // Confirm that the ClockWatcher doesn't re-trigger a[0] | ||
1658 | 162 | a.push_back(appointments[1]); | ||
1659 | 163 | m_state->planner->upcoming.set(a); | ||
1660 | 164 | EXPECT_EQ(1, m_triggered.size()); | ||
1661 | 165 | EXPECT_EQ(a[0].uid, m_triggered[0]); | ||
1662 | 166 | } | ||
1663 | 0 | 167 | ||
1664 | === modified file 'tests/test-clock.cpp' | |||
1665 | --- tests/test-clock.cpp 2014-01-31 00:33:14 +0000 | |||
1666 | +++ tests/test-clock.cpp 2014-02-19 15:36:23 +0000 | |||
1667 | @@ -37,12 +37,12 @@ | |||
1668 | 37 | void emitPrepareForSleep() | 37 | void emitPrepareForSleep() |
1669 | 38 | { | 38 | { |
1670 | 39 | g_dbus_connection_emit_signal(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), | 39 | g_dbus_connection_emit_signal(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), |
1672 | 40 | NULL, | 40 | nullptr, |
1673 | 41 | "/org/freedesktop/login1", // object path | 41 | "/org/freedesktop/login1", // object path |
1674 | 42 | "org.freedesktop.login1.Manager", // interface | 42 | "org.freedesktop.login1.Manager", // interface |
1675 | 43 | "PrepareForSleep", // signal name | 43 | "PrepareForSleep", // signal name |
1676 | 44 | g_variant_new("(b)", FALSE), | 44 | g_variant_new("(b)", FALSE), |
1678 | 45 | NULL); | 45 | nullptr); |
1679 | 46 | } | 46 | } |
1680 | 47 | }; | 47 | }; |
1681 | 48 | 48 | ||
1682 | 49 | 49 | ||
1683 | === modified file 'tests/test-planner.cpp' | |||
1684 | --- tests/test-planner.cpp 2014-01-31 00:33:14 +0000 | |||
1685 | +++ tests/test-planner.cpp 2014-02-19 15:36:23 +0000 | |||
1686 | @@ -28,9 +28,7 @@ | |||
1687 | 28 | #include <langinfo.h> | 28 | #include <langinfo.h> |
1688 | 29 | #include <locale.h> | 29 | #include <locale.h> |
1689 | 30 | 30 | ||
1693 | 31 | using unity::indicator::datetime::Appointment; | 31 | using namespace unity::indicator::datetime; |
1691 | 32 | using unity::indicator::datetime::DateTime; | ||
1692 | 33 | using unity::indicator::datetime::PlannerEds; | ||
1694 | 34 | 32 | ||
1695 | 35 | /*** | 33 | /*** |
1696 | 36 | **** | 34 | **** |
1697 | @@ -40,11 +38,15 @@ | |||
1698 | 40 | 38 | ||
1699 | 41 | TEST_F(PlannerFixture, EDS) | 39 | TEST_F(PlannerFixture, EDS) |
1700 | 42 | { | 40 | { |
1702 | 43 | PlannerEds planner; | 41 | auto tmp = g_date_time_new_now_local(); |
1703 | 42 | const auto now = DateTime(tmp); | ||
1704 | 43 | g_date_time_unref(tmp); | ||
1705 | 44 | |||
1706 | 45 | std::shared_ptr<Clock> clock(new MockClock(now)); | ||
1707 | 46 | PlannerEds planner(clock); | ||
1708 | 44 | wait_msec(100); | 47 | wait_msec(100); |
1709 | 45 | 48 | ||
1712 | 46 | auto now = g_date_time_new_now_local(); | 49 | planner.time.set(now); |
1711 | 47 | planner.time.set(DateTime(now)); | ||
1713 | 48 | wait_msec(2500); | 50 | wait_msec(2500); |
1714 | 49 | 51 | ||
1715 | 50 | std::vector<Appointment> this_month = planner.this_month.get(); | 52 | std::vector<Appointment> this_month = planner.this_month.get(); |
1716 | 51 | 53 | ||
1717 | === modified file 'tests/test-settings.cpp' | |||
1718 | --- tests/test-settings.cpp 2014-01-31 00:40:13 +0000 | |||
1719 | +++ tests/test-settings.cpp 2014-02-19 15:36:23 +0000 | |||
1720 | @@ -167,8 +167,8 @@ | |||
1721 | 167 | { | 167 | { |
1722 | 168 | const auto key = SETTINGS_LOCATIONS_S; | 168 | const auto key = SETTINGS_LOCATIONS_S; |
1723 | 169 | 169 | ||
1726 | 170 | const gchar* astrv[] = {"America/Los_Angeles Oakland", "America/Chicago Oklahoma City", "Europe/London London", NULL}; | 170 | const gchar* astrv[] = {"America/Los_Angeles Oakland", "America/Chicago Oklahoma City", "Europe/London London", nullptr}; |
1727 | 171 | const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", NULL}; | 171 | const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", nullptr}; |
1728 | 172 | const std::vector<std::string> av = strv_to_vector(astrv); | 172 | const std::vector<std::string> av = strv_to_vector(astrv); |
1729 | 173 | const std::vector<std::string> bv = strv_to_vector(bstrv); | 173 | const std::vector<std::string> bv = strv_to_vector(bstrv); |
1730 | 174 | 174 | ||
1731 | 175 | 175 | ||
1732 | === modified file 'tests/test-utils.cpp' | |||
1733 | --- tests/test-utils.cpp 2014-01-27 07:26:02 +0000 | |||
1734 | +++ tests/test-utils.cpp 2014-02-19 15:36:23 +0000 | |||
1735 | @@ -59,7 +59,7 @@ | |||
1736 | 59 | const char* location; | 59 | const char* location; |
1737 | 60 | const char* expected_name; | 60 | const char* expected_name; |
1738 | 61 | } beautify_timezone_test_cases[] = { | 61 | } beautify_timezone_test_cases[] = { |
1740 | 62 | { "America/Chicago", NULL, "Chicago" }, | 62 | { "America/Chicago", nullptr, "Chicago" }, |
1741 | 63 | { "America/Chicago", "America/Chicago", "Chicago" }, | 63 | { "America/Chicago", "America/Chicago", "Chicago" }, |
1742 | 64 | { "America/Chicago", "America/Chigago Chicago", "Chicago" }, | 64 | { "America/Chicago", "America/Chigago Chicago", "Chicago" }, |
1743 | 65 | { "America/Chicago", "America/Chicago Oklahoma City", "Oklahoma City" }, | 65 | { "America/Chicago", "America/Chicago Oklahoma City", "Oklahoma City" }, |
PASSED: Continuous integration, rev:388 jenkins. qa.ubuntu. com/job/ indicator- datetime- ci/173/ jenkins. qa.ubuntu. com/job/ indicator- datetime- trusty- amd64-ci/ 53 jenkins. qa.ubuntu. com/job/ indicator- datetime- trusty- armhf-ci/ 53 jenkins. qa.ubuntu. com/job/ indicator- datetime- trusty- armhf-ci/ 53/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/indicator- datetime- ci/173/ rebuild
http://