Merge lp:~indicator-applet-developers/indicator-application/karmic into lp:~ubuntu-desktop/indicator-application/ubuntu
- karmic
- Merge into ubuntu
Proposed by
Ted Gould
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~indicator-applet-developers/indicator-application/karmic |
Merge into: | lp:~ubuntu-desktop/indicator-application/ubuntu |
Diff against target: |
1185 lines (+838/-31) 12 files modified
configure.ac (+6/-3) debian/changelog (+13/-0) debian/control (+2/-1) src/Makefile.am (+2/-0) src/application-service-appstore.c (+123/-20) src/application-service-appstore.h (+2/-0) src/application-service-lru-file.c (+473/-0) src/application-service-lru-file.h (+59/-0) src/application-service.c (+8/-1) src/application-service.xml (+1/-1) src/indicator-application.c (+145/-5) src/libappindicator/app-indicator.c (+4/-0) |
To merge this branch: | bzr merge lp:~indicator-applet-developers/indicator-application/karmic |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Indicator Applet Developers | Pending | ||
Review via email: mp+17846@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Ted Gould (ted) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'configure.ac' |
2 | --- configure.ac 2010-01-14 16:36:02 +0000 |
3 | +++ configure.ac 2010-01-21 20:27:14 +0000 |
4 | @@ -1,11 +1,11 @@ |
5 | |
6 | -AC_INIT(indicator-application, 0.0.9, ted@canonical.com) |
7 | +AC_INIT(indicator-application, 0.0.10, ted@canonical.com) |
8 | AC_COPYRIGHT([Copyright 2009, 2010 Canonical]) |
9 | |
10 | AC_PREREQ(2.53) |
11 | |
12 | AM_CONFIG_HEADER(config.h) |
13 | -AM_INIT_AUTOMAKE(indicator-application, 0.0.9) |
14 | +AM_INIT_AUTOMAKE(indicator-application, 0.0.10) |
15 | |
16 | AM_MAINTAINER_MODE |
17 | |
18 | @@ -38,11 +38,13 @@ |
19 | ########################### |
20 | |
21 | GTK_REQUIRED_VERSION=2.12 |
22 | -INDICATOR_REQUIRED_VERSION=0.3.0 |
23 | +INDICATOR_REQUIRED_VERSION=0.3.1 |
24 | DBUSMENUGTK_REQUIRED_VERSION=0.1.1 |
25 | +JSON_GLIB_REQUIRED_VERSION=0.7.6 |
26 | |
27 | PKG_CHECK_MODULES(INDICATOR, gtk+-2.0 >= $GTK_REQUIRED_VERSION |
28 | indicator >= $INDICATOR_REQUIRED_VERSION |
29 | + json-glib-1.0 >= $JSON_GLIB_REQUIRED_VERSION |
30 | dbusmenu-gtk >= $DBUSMENUGTK_REQUIRED_VERSION) |
31 | |
32 | AC_SUBST(INDICATOR_CFLAGS) |
33 | @@ -51,6 +53,7 @@ |
34 | ########################### |
35 | # Check for Mono support |
36 | ########################### |
37 | + |
38 | MONO_REQUIRED_VERSION=1.0 |
39 | PKG_CHECK_MODULES(MONO_DEPENDENCY, mono >= $MONO_REQUIRED_VERSION, has_mono=true, has_mono=false) |
40 | |
41 | |
42 | === modified file 'debian/changelog' |
43 | --- debian/changelog 2010-01-19 21:57:32 +0000 |
44 | +++ debian/changelog 2010-01-21 20:27:14 +0000 |
45 | @@ -1,3 +1,16 @@ |
46 | +indicator-application (0.0.10-0ubuntu1~ppa1) karmic; urgency=low |
47 | + |
48 | + * Upstream release 0.0.10 |
49 | + * Adding in a file to position the indicators |
50 | + * Ref counting theme directories as they get added and removed |
51 | + * Use the right callback function for fallback (LP: #507975) |
52 | + * Support getting the app list from a running service. |
53 | + * debian/control: Adding dependency for json-glib to build. |
54 | + * debian/control: Adjusted back changes in 0.0.9-0ubuntu3 as this |
55 | + branch is still on Karmic. Changes are in Lucid. |
56 | + |
57 | + -- Ted Gould <ted@ubuntu.com> Thu, 21 Jan 2010 14:21:00 -0600 |
58 | + |
59 | indicator-application (0.0.9-0ubuntu3) lucid; urgency=low |
60 | |
61 | * debian/control: |
62 | |
63 | === modified file 'debian/control' |
64 | --- debian/control 2010-01-19 21:57:32 +0000 |
65 | +++ debian/control 2010-01-21 20:27:14 +0000 |
66 | @@ -8,6 +8,7 @@ |
67 | python-central (>= 0.6), |
68 | libgtk2.0-dev (>= 2.12.0), |
69 | libdbus-glib-1-dev, |
70 | + libjson-glib-dev, |
71 | gnome-doc-utils, |
72 | gtk-doc-tools, |
73 | intltool, |
74 | @@ -23,7 +24,7 @@ |
75 | gtk-sharp2-gapi, |
76 | libmono-dev, |
77 | libnunit2.4-cil, |
78 | - libgtk2.0-cil-dev |
79 | + libgtk2.0-cil |
80 | Standards-Version: 3.8.3 |
81 | Homepage: https://launchpad.net/indicator-application |
82 | Vcs-Bzr: http://bazaar.launchpad.net/~ubuntu-desktop/indicator-application/ubuntu |
83 | |
84 | === modified file 'src/Makefile.am' |
85 | --- src/Makefile.am 2010-01-08 02:53:46 +0000 |
86 | +++ src/Makefile.am 2010-01-21 20:27:14 +0000 |
87 | @@ -35,6 +35,8 @@ |
88 | application-service.c \ |
89 | application-service-appstore.h \ |
90 | application-service-appstore.c \ |
91 | + application-service-lru-file.h \ |
92 | + application-service-lru-file.c \ |
93 | application-service-marshal.h \ |
94 | application-service-marshal.c \ |
95 | application-service-server.h \ |
96 | |
97 | === modified file 'src/application-service-appstore.c' |
98 | --- src/application-service-appstore.c 2010-01-08 20:10:45 +0000 |
99 | +++ src/application-service-appstore.c 2010-01-21 20:27:14 +0000 |
100 | @@ -31,7 +31,7 @@ |
101 | #include "dbus-shared.h" |
102 | |
103 | /* DBus Prototypes */ |
104 | -static gboolean _application_service_server_get_applications (ApplicationServiceAppstore * appstore, GArray ** apps); |
105 | +static gboolean _application_service_server_get_applications (ApplicationServiceAppstore * appstore, GPtrArray ** apps, GError ** error); |
106 | |
107 | #include "application-service-server.h" |
108 | |
109 | @@ -52,6 +52,7 @@ |
110 | struct _ApplicationServiceAppstorePrivate { |
111 | DBusGConnection * bus; |
112 | GList * applications; |
113 | + AppLruFile * lrufile; |
114 | }; |
115 | |
116 | #define APP_STATUS_PASSIVE_STR "passive" |
117 | @@ -67,6 +68,8 @@ |
118 | |
119 | typedef struct _Application Application; |
120 | struct _Application { |
121 | + gchar * id; |
122 | + gchar * category; |
123 | gchar * dbus_name; |
124 | gchar * dbus_object; |
125 | ApplicationServiceAppstore * appstore; /* not ref'd */ |
126 | @@ -148,6 +151,7 @@ |
127 | ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(self); |
128 | |
129 | priv->applications = NULL; |
130 | + priv->lrufile = NULL; |
131 | |
132 | GError * error = NULL; |
133 | priv->bus = dbus_g_bus_get(DBUS_BUS_STARTER, &error); |
134 | @@ -201,6 +205,8 @@ |
135 | Application * app = (Application *)data; |
136 | |
137 | if (g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_MENU) == NULL || |
138 | + g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ID) == NULL || |
139 | + g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_CATEGORY) == NULL || |
140 | g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_STATUS) == NULL || |
141 | g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME) == NULL) { |
142 | g_warning("Notification Item on object %s of %s doesn't have enough properties.", app->dbus_object, app->dbus_name); |
143 | @@ -210,6 +216,11 @@ |
144 | |
145 | app->validated = TRUE; |
146 | |
147 | + app->id = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ID)); |
148 | + app->category = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_CATEGORY)); |
149 | + ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(app->appstore); |
150 | + app_lru_file_touch(priv->lrufile, app->id, app->category); |
151 | + |
152 | app->icon = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME)); |
153 | app->menu = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_MENU)); |
154 | if (g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_AICON_NAME) != NULL) { |
155 | @@ -273,6 +284,12 @@ |
156 | g_object_unref(app->prop_proxy); |
157 | } |
158 | |
159 | + if (app->id != NULL) { |
160 | + g_free(app->id); |
161 | + } |
162 | + if (app->category != NULL) { |
163 | + g_free(app->category); |
164 | + } |
165 | if (app->dbus_name != NULL) { |
166 | g_free(app->dbus_name); |
167 | } |
168 | @@ -311,6 +328,40 @@ |
169 | return; |
170 | } |
171 | |
172 | +static gboolean |
173 | +can_add_application (GList *applications, Application *app) |
174 | +{ |
175 | + if (applications) |
176 | + { |
177 | + GList *l = NULL; |
178 | + |
179 | + for (l = applications; l != NULL; l = g_list_next (l)) |
180 | + { |
181 | + Application *tmp_app = (Application *)l->data; |
182 | + |
183 | + if (g_strcmp0 (tmp_app->dbus_name, app->dbus_name) == 0 && |
184 | + g_strcmp0 (tmp_app->dbus_object, app->dbus_object) == 0) |
185 | + { |
186 | + return FALSE; |
187 | + } |
188 | + } |
189 | + } |
190 | + |
191 | + return TRUE; |
192 | +} |
193 | + |
194 | +/* This function takes two Application structure |
195 | + pointers and uses the lrufile to compare them. */ |
196 | +static gint |
197 | +app_sort_func (gconstpointer a, gconstpointer b, gpointer userdata) |
198 | +{ |
199 | + Application * appa = (Application *)a; |
200 | + Application * appb = (Application *)b; |
201 | + AppLruFile * lrufile = (AppLruFile *)userdata; |
202 | + |
203 | + return app_lru_file_sort(lrufile, appa->id, appb->id); |
204 | +} |
205 | + |
206 | /* Change the status of the application. If we're going passive |
207 | it removes it from the panel. If we're coming online, then |
208 | it add it to the panel. Otherwise it changes the icon. */ |
209 | @@ -333,8 +384,7 @@ |
210 | g_signal_emit(G_OBJECT(appstore), |
211 | signals[APPLICATION_REMOVED], 0, |
212 | position, TRUE); |
213 | - |
214 | - priv->applications = g_list_remove(priv->applications, app); |
215 | + priv->applications = g_list_remove(priv->applications, app); |
216 | } else { |
217 | /* Figure out which icon we should be using */ |
218 | gchar * newicon = app->icon; |
219 | @@ -344,21 +394,19 @@ |
220 | |
221 | /* Determine whether we're already shown or not */ |
222 | if (app->status == APP_STATUS_PASSIVE) { |
223 | - /* Put on panel */ |
224 | - priv->applications = g_list_prepend(priv->applications, app); |
225 | - |
226 | - /* TODO: We need to have the position determined better. This |
227 | - would involve looking at the name and category and sorting |
228 | - it with the other entries. */ |
229 | + if (can_add_application (priv->applications, app)) { |
230 | + /* Put on panel */ |
231 | + priv->applications = g_list_insert_sorted_with_data (priv->applications, app, app_sort_func, priv->lrufile); |
232 | |
233 | - g_signal_emit(G_OBJECT(app->appstore), |
234 | - signals[APPLICATION_ADDED], 0, |
235 | - newicon, |
236 | - 0, /* Position */ |
237 | - app->dbus_name, |
238 | - app->menu, |
239 | - app->icon_path, |
240 | - TRUE); |
241 | + g_signal_emit(G_OBJECT(app->appstore), |
242 | + signals[APPLICATION_ADDED], 0, |
243 | + newicon, |
244 | + g_list_index(priv->applications, app), /* Position */ |
245 | + app->dbus_name, |
246 | + app->menu, |
247 | + app->icon_path, |
248 | + TRUE); |
249 | + } |
250 | } else { |
251 | /* Icon update */ |
252 | gint position = get_position(app); |
253 | @@ -622,11 +670,66 @@ |
254 | return; |
255 | } |
256 | |
257 | +/* Creates a basic appstore object and attaches the |
258 | + LRU file object to it. */ |
259 | +ApplicationServiceAppstore * |
260 | +application_service_appstore_new (AppLruFile * lrufile) |
261 | +{ |
262 | + g_return_val_if_fail(IS_APP_LRU_FILE(lrufile), NULL); |
263 | + ApplicationServiceAppstore * appstore = APPLICATION_SERVICE_APPSTORE(g_object_new(APPLICATION_SERVICE_APPSTORE_TYPE, NULL)); |
264 | + ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore); |
265 | + priv->lrufile = lrufile; |
266 | + return appstore; |
267 | +} |
268 | + |
269 | /* DBus Interface */ |
270 | static gboolean |
271 | -_application_service_server_get_applications (ApplicationServiceAppstore * appstore, GArray ** apps) |
272 | +_application_service_server_get_applications (ApplicationServiceAppstore * appstore, GPtrArray ** apps, GError ** error) |
273 | { |
274 | - |
275 | - return FALSE; |
276 | + ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore); |
277 | + |
278 | + *apps = g_ptr_array_new(); |
279 | + GList * listpntr; |
280 | + gint position = 0; |
281 | + |
282 | + for (listpntr = priv->applications; listpntr != NULL; listpntr = g_list_next(listpntr)) { |
283 | + GValueArray * values = g_value_array_new(5); |
284 | + |
285 | + GValue value = {0}; |
286 | + |
287 | + /* Icon name */ |
288 | + g_value_init(&value, G_TYPE_STRING); |
289 | + g_value_set_string(&value, ((Application *)listpntr->data)->icon); |
290 | + g_value_array_append(values, &value); |
291 | + g_value_unset(&value); |
292 | + |
293 | + /* Position */ |
294 | + g_value_init(&value, G_TYPE_INT); |
295 | + g_value_set_int(&value, position++); |
296 | + g_value_array_append(values, &value); |
297 | + g_value_unset(&value); |
298 | + |
299 | + /* DBus Address */ |
300 | + g_value_init(&value, G_TYPE_STRING); |
301 | + g_value_set_string(&value, ((Application *)listpntr->data)->dbus_name); |
302 | + g_value_array_append(values, &value); |
303 | + g_value_unset(&value); |
304 | + |
305 | + /* DBus Object */ |
306 | + g_value_init(&value, DBUS_TYPE_G_OBJECT_PATH); |
307 | + g_value_set_static_boxed(&value, ((Application *)listpntr->data)->menu); |
308 | + g_value_array_append(values, &value); |
309 | + g_value_unset(&value); |
310 | + |
311 | + /* Icon path */ |
312 | + g_value_init(&value, G_TYPE_STRING); |
313 | + g_value_set_string(&value, ((Application *)listpntr->data)->icon_path); |
314 | + g_value_array_append(values, &value); |
315 | + g_value_unset(&value); |
316 | + |
317 | + g_ptr_array_add(*apps, values); |
318 | + } |
319 | + |
320 | + return TRUE; |
321 | } |
322 | |
323 | |
324 | === modified file 'src/application-service-appstore.h' |
325 | --- src/application-service-appstore.h 2010-01-08 20:10:45 +0000 |
326 | +++ src/application-service-appstore.h 2010-01-21 20:27:14 +0000 |
327 | @@ -25,6 +25,7 @@ |
328 | |
329 | #include <glib.h> |
330 | #include <glib-object.h> |
331 | +#include "application-service-lru-file.h" |
332 | |
333 | G_BEGIN_DECLS |
334 | |
335 | @@ -50,6 +51,7 @@ |
336 | GObject parent; |
337 | }; |
338 | |
339 | +ApplicationServiceAppstore * application_service_appstore_new (AppLruFile * lrufile); |
340 | GType application_service_appstore_get_type (void); |
341 | void application_service_appstore_application_add (ApplicationServiceAppstore * appstore, |
342 | const gchar * dbus_name, |
343 | |
344 | === added file 'src/application-service-lru-file.c' |
345 | --- src/application-service-lru-file.c 1970-01-01 00:00:00 +0000 |
346 | +++ src/application-service-lru-file.c 2010-01-21 20:27:14 +0000 |
347 | @@ -0,0 +1,473 @@ |
348 | +/* |
349 | +This object manages the database of when the entires were touched |
350 | +and loved. And writes that out to disk occationally as well. |
351 | + |
352 | +Copyright 2010 Canonical Ltd. |
353 | + |
354 | +Authors: |
355 | + Ted Gould <ted@canonical.com> |
356 | + |
357 | +This program is free software: you can redistribute it and/or modify it |
358 | +under the terms of the GNU General Public License version 3, as published |
359 | +by the Free Software Foundation. |
360 | + |
361 | +This program is distributed in the hope that it will be useful, but |
362 | +WITHOUT ANY WARRANTY; without even the implied warranties of |
363 | +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
364 | +PURPOSE. See the GNU General Public License for more details. |
365 | + |
366 | +You should have received a copy of the GNU General Public License along |
367 | +with this program. If not, see <http://www.gnu.org/licenses/>. |
368 | +*/ |
369 | + |
370 | +#ifdef HAVE_CONFIG_H |
371 | +#include "config.h" |
372 | +#endif |
373 | + |
374 | +#include <string.h> |
375 | +#include <gio/gio.h> |
376 | +#include <json-glib/json-glib.h> |
377 | +#include "application-service-lru-file.h" |
378 | + |
379 | +#define ENTRY_CATEGORY "category" |
380 | +#define ENTRY_FIRST_TIME "first-time" |
381 | +#define ENTRY_LAST_TIME "last-time" |
382 | +#define ENTRY_VERSION "version" |
383 | + |
384 | +#define CONFIG_DIR ("indicators" G_DIR_SEPARATOR_S "application") |
385 | +#define CONFIG_FILE "lru-file.json" |
386 | + |
387 | +typedef struct _AppLruFilePrivate AppLruFilePrivate; |
388 | +struct _AppLruFilePrivate { |
389 | + GHashTable * apps; |
390 | + gboolean dirty; |
391 | + guint timer; |
392 | + gchar * filename; |
393 | +}; |
394 | + |
395 | +typedef struct _AppData AppData; |
396 | +struct _AppData { |
397 | + gchar * category; |
398 | + GTimeVal last_touched; |
399 | + GTimeVal first_touched; |
400 | +}; |
401 | + |
402 | +#define APP_LRU_FILE_GET_PRIVATE(o) \ |
403 | + (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_LRU_FILE_TYPE, AppLruFilePrivate)) |
404 | + |
405 | +static void app_lru_file_class_init (AppLruFileClass *klass); |
406 | +static void app_lru_file_init (AppLruFile *self); |
407 | +static void app_lru_file_dispose (GObject *object); |
408 | +static void app_lru_file_finalize (GObject *object); |
409 | +static void app_data_free (gpointer data); |
410 | +static void get_dirty (AppLruFile * lrufile); |
411 | +static gboolean load_from_file (gpointer data); |
412 | +static void load_file_object_cb (JsonObject * obj, const gchar * key, JsonNode * value, gpointer data); |
413 | +static void clean_off_hash_cb (gpointer key, gpointer value, gpointer data); |
414 | +static void clean_off_write_end_cb (GObject * obj, GAsyncResult * res, gpointer data); |
415 | + |
416 | +G_DEFINE_TYPE (AppLruFile, app_lru_file, G_TYPE_OBJECT); |
417 | + |
418 | +/* Set up the class variable stuff */ |
419 | +static void |
420 | +app_lru_file_class_init (AppLruFileClass *klass) |
421 | +{ |
422 | + GObjectClass *object_class = G_OBJECT_CLASS (klass); |
423 | + |
424 | + g_type_class_add_private (klass, sizeof (AppLruFilePrivate)); |
425 | + |
426 | + object_class->dispose = app_lru_file_dispose; |
427 | + object_class->finalize = app_lru_file_finalize; |
428 | + |
429 | + return; |
430 | +} |
431 | + |
432 | +/* Set all the data of the per-instance variables */ |
433 | +static void |
434 | +app_lru_file_init (AppLruFile *self) |
435 | +{ |
436 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(self); |
437 | + |
438 | + /* Default values */ |
439 | + priv->apps = NULL; |
440 | + priv->dirty = FALSE; |
441 | + priv->timer = 0; |
442 | + priv->filename = NULL; |
443 | + |
444 | + /* Now let's build some stuff */ |
445 | + priv->apps = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, app_data_free); |
446 | + priv->filename = g_build_filename(g_get_user_config_dir(), CONFIG_DIR, CONFIG_FILE, NULL); |
447 | + |
448 | + /* No reason to delay other stuff for this, we'll |
449 | + merge any values that get touched. */ |
450 | + g_idle_add(load_from_file, self); |
451 | + |
452 | + return; |
453 | +} |
454 | + |
455 | +/* Get rid of objects and other big things */ |
456 | +static void |
457 | +app_lru_file_dispose (GObject *object) |
458 | +{ |
459 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(object); |
460 | + |
461 | + if (priv->timer != 0) { |
462 | + g_source_remove(priv->timer); |
463 | + priv->timer = 0; |
464 | + } |
465 | + |
466 | + if (priv->apps != NULL) { |
467 | + /* Cleans up the keys and entries itself */ |
468 | + g_hash_table_destroy(priv->apps); |
469 | + priv->apps = NULL; |
470 | + } |
471 | + |
472 | + G_OBJECT_CLASS (app_lru_file_parent_class)->dispose (object); |
473 | + return; |
474 | +} |
475 | + |
476 | +/* Deallocate memory */ |
477 | +static void |
478 | +app_lru_file_finalize (GObject *object) |
479 | +{ |
480 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(object); |
481 | + |
482 | + if (priv->filename != NULL) { |
483 | + g_free(priv->filename); |
484 | + priv->filename = NULL; |
485 | + } |
486 | + |
487 | + G_OBJECT_CLASS (app_lru_file_parent_class)->finalize (object); |
488 | + return; |
489 | +} |
490 | + |
491 | +/* Takes an AppData structure and free's the |
492 | + memory from it. */ |
493 | +static void |
494 | +app_data_free (gpointer data) |
495 | +{ |
496 | + AppData * appdata = (AppData *)data; |
497 | + |
498 | + if (appdata->category != NULL) { |
499 | + g_free(appdata->category); |
500 | + } |
501 | + |
502 | + g_free(appdata); |
503 | + |
504 | + return; |
505 | +} |
506 | + |
507 | +/* Loads all of the data out of a json file */ |
508 | +static gboolean |
509 | +load_from_file (gpointer data) |
510 | +{ |
511 | + AppLruFile * lrufile = (AppLruFile *)data; |
512 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); |
513 | + |
514 | + if (!g_file_test(priv->filename, G_FILE_TEST_EXISTS)) { |
515 | + return FALSE; |
516 | + } |
517 | + |
518 | + JsonParser * parser = json_parser_new(); |
519 | + |
520 | + if (!json_parser_load_from_file(parser, priv->filename, NULL)) { |
521 | + g_warning("Unable to parse JSON file '%s'", priv->filename); |
522 | + g_object_unref(parser); |
523 | + return FALSE; |
524 | + } |
525 | + |
526 | + JsonNode * root = json_parser_get_root(parser); |
527 | + JsonObject * rootobj = json_node_get_object(root); |
528 | + if (rootobj == NULL) { |
529 | + g_warning("Malformed LRU file. The root node is not an object."); |
530 | + g_object_unref(parser); |
531 | + return FALSE; |
532 | + } |
533 | + |
534 | + json_object_foreach_member(rootobj, load_file_object_cb, lrufile); |
535 | + |
536 | + g_object_unref(parser); |
537 | + return FALSE; |
538 | +} |
539 | + |
540 | +/* Looks at the various things that we need on a node, makes |
541 | + sure that we have them, and then copies them into the |
542 | + application hash table of love. */ |
543 | +static void |
544 | +load_file_object_cb (JsonObject * rootobj, const gchar * key, JsonNode * value, gpointer data) |
545 | +{ |
546 | + AppLruFile * lrufile = (AppLruFile *)data; |
547 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); |
548 | + |
549 | + /* We're not looking at this today. */ |
550 | + if (!g_strcmp0(key, ENTRY_VERSION)) { |
551 | + return; |
552 | + } |
553 | + |
554 | + JsonObject * obj = json_node_get_object(value); |
555 | + if (obj == NULL) { |
556 | + g_warning("Data for node '%s' is not an object.", key); |
557 | + return; |
558 | + } |
559 | + |
560 | + const gchar * obj_category = json_object_get_string_member(obj, ENTRY_CATEGORY); |
561 | + const gchar * obj_first = json_object_get_string_member(obj, ENTRY_FIRST_TIME); |
562 | + const gchar * obj_last = json_object_get_string_member(obj, ENTRY_LAST_TIME); |
563 | + |
564 | + if (obj_category == NULL || obj_first == NULL || obj_last == NULL) { |
565 | + g_warning("Node '%s' is missing data. Got: ('%s', '%s', '%s')", key, obj_category, obj_first, obj_last); |
566 | + get_dirty(lrufile); |
567 | + return; |
568 | + } |
569 | + |
570 | + /* Check to see how old this entry is. If it hasn't been |
571 | + used in the last year, we remove the cruft. */ |
572 | + GTimeVal currenttime; |
573 | + g_get_current_time(¤ttime); |
574 | + GDate * currentdate = g_date_new(); |
575 | + g_date_set_time_val(currentdate, ¤ttime); |
576 | + |
577 | + GTimeVal lasttouch; |
578 | + g_time_val_from_iso8601(obj_last, &lasttouch); |
579 | + GDate * lastdate = g_date_new(); |
580 | + g_date_set_time_val(lastdate, &lasttouch); |
581 | + |
582 | + gint spread = g_date_days_between(lastdate, currentdate); |
583 | + |
584 | + g_date_free(currentdate); |
585 | + g_date_free(lastdate); |
586 | + |
587 | + if (spread > 365) { |
588 | + g_debug("Removing node '%s' as it's %d days old.", key, spread); |
589 | + get_dirty(lrufile); |
590 | + return; |
591 | + } |
592 | + |
593 | + /* See if we already have one of these. It's a little bit |
594 | + unlikely, but since we're async, we need to check */ |
595 | + gpointer datapntr = g_hash_table_lookup(priv->apps, key); |
596 | + if (datapntr == NULL) { |
597 | + /* Build a new node */ |
598 | + AppData * appdata = g_new0(AppData, 1); |
599 | + appdata->category = g_strdup(obj_category); |
600 | + g_time_val_from_iso8601(obj_first, &appdata->first_touched); |
601 | + g_time_val_from_iso8601(obj_last, &appdata->last_touched); |
602 | + |
603 | + g_hash_table_insert(priv->apps, g_strdup(key), appdata); |
604 | + } else { |
605 | + /* Merge nodes */ |
606 | + AppData * appdata = (AppData *)datapntr; |
607 | + GTimeVal temptime; |
608 | + g_time_val_from_iso8601(obj_first, &temptime); |
609 | + |
610 | + if (temptime.tv_sec < appdata->first_touched.tv_sec) { |
611 | + g_time_val_from_iso8601(obj_first, &appdata->first_touched); |
612 | + } |
613 | + } |
614 | + |
615 | + return; |
616 | +} |
617 | + |
618 | +/* Write out our cache to a file so that we can unmark the dirty |
619 | + bit and be happy. */ |
620 | +static gboolean |
621 | +clean_off (gpointer data) |
622 | +{ |
623 | + AppLruFile * lrufile = (AppLruFile *)data; |
624 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); |
625 | + priv->timer = 0; |
626 | + |
627 | + GError * error = NULL; |
628 | + |
629 | + /* Check to see if our directory exists. Build it if not. */ |
630 | + gchar * dirname = g_build_filename(g_get_user_config_dir(), CONFIG_DIR, NULL); |
631 | + if (!g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { |
632 | + GFile * dirfile = g_file_new_for_path(dirname); |
633 | + g_file_make_directory_with_parents(dirfile, NULL, NULL); |
634 | + g_object_unref(dirfile); |
635 | + } |
636 | + g_free(dirname); |
637 | + |
638 | + GFile * file = g_file_new_for_path(priv->filename); |
639 | + GFileOutputStream * ostream = g_file_replace(file, |
640 | + NULL, /* etag */ |
641 | + TRUE, /* backup */ |
642 | + G_FILE_CREATE_NONE, /* flags */ |
643 | + NULL, /* cancelable */ |
644 | + &error); |
645 | + if (error != NULL) { |
646 | + g_warning("Unable to open a file to store LRU file: %s", error->message); |
647 | + return FALSE; |
648 | + } |
649 | + |
650 | + /* This is how the file will start */ |
651 | + GString * filestring = g_string_new("{\n \"" ENTRY_VERSION "\": 1"); |
652 | + |
653 | + /* Put the middle in. */ |
654 | + g_hash_table_foreach (priv->apps, clean_off_hash_cb, filestring); |
655 | + |
656 | + /* And then tack on the end. */ |
657 | + g_string_append(filestring, "\n}\n"); |
658 | + gchar * filedata = g_string_free(filestring, FALSE); |
659 | + g_output_stream_write_async(G_OUTPUT_STREAM(ostream), |
660 | + filedata, |
661 | + strlen(filedata), |
662 | + G_PRIORITY_DEFAULT_IDLE, |
663 | + NULL, |
664 | + clean_off_write_end_cb, |
665 | + filedata); |
666 | + |
667 | + return FALSE; /* drop the timer */ |
668 | +} |
669 | + |
670 | +/* Looks at every value in the applications hash table and |
671 | + turns it into a string for writing out. */ |
672 | +static void |
673 | +clean_off_hash_cb (gpointer key, gpointer value, gpointer data) |
674 | +{ |
675 | + /* Mega-cast */ |
676 | + gchar * id = (gchar *)key; |
677 | + AppData * appdata = (AppData *)value; |
678 | + GString * string = (GString *)data; |
679 | + |
680 | + gchar * firsttime = g_time_val_to_iso8601(&appdata->first_touched); |
681 | + gchar * lasttime = g_time_val_to_iso8601(&appdata->last_touched); |
682 | + |
683 | + g_string_append_printf(string, ",\n \"%s\": { \"" ENTRY_FIRST_TIME "\": \"%s\", \"" ENTRY_LAST_TIME "\": \"%s\", \"" ENTRY_CATEGORY "\": \"%s\"}", id, firsttime, lasttime, appdata->category); |
684 | + |
685 | + g_free(lasttime); |
686 | + g_free(firsttime); |
687 | + |
688 | + return; |
689 | +} |
690 | + |
691 | +/* Very much like clean_off_write_cb except that it is the |
692 | + last actor on this Output Stream so it closes it. */ |
693 | +static void |
694 | +clean_off_write_end_cb (GObject * obj, GAsyncResult * res, gpointer data) |
695 | +{ |
696 | + g_free(data); |
697 | + |
698 | + GError * error = NULL; |
699 | + g_output_stream_close(G_OUTPUT_STREAM(obj), NULL, &error); |
700 | + |
701 | + if (error != NULL) { |
702 | + g_warning("Unable to close LRU File: %s", error->message); |
703 | + g_error_free(error); |
704 | + } |
705 | + |
706 | + return; |
707 | +} |
708 | + |
709 | +/* Sets the dirty bit if not already set and makes sure that |
710 | + we have a timer to fix that at some point. */ |
711 | +static void |
712 | +get_dirty (AppLruFile * lrufile) |
713 | +{ |
714 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); |
715 | + |
716 | + priv->dirty = TRUE; |
717 | + |
718 | + if (priv->timer == 0) { |
719 | + priv->timer = g_timeout_add_seconds(60, clean_off, lrufile); |
720 | + } |
721 | + |
722 | + return; |
723 | +} |
724 | + |
725 | +/* API */ |
726 | + |
727 | +/* Simple helper to create a new object */ |
728 | +AppLruFile * |
729 | +app_lru_file_new (void) |
730 | +{ |
731 | + return APP_LRU_FILE(g_object_new(APP_LRU_FILE_TYPE, NULL)); |
732 | +} |
733 | + |
734 | +/* This updates the timestamp for a particular |
735 | + entry in the database. It also queues up a |
736 | + write out of the database. But that'll happen |
737 | + later. */ |
738 | +void |
739 | +app_lru_file_touch (AppLruFile * lrufile, const gchar * id, const gchar * category) |
740 | +{ |
741 | + g_return_if_fail(IS_APP_LRU_FILE(lrufile)); |
742 | + g_return_if_fail(id != NULL && id[0] != '\0'); |
743 | + g_return_if_fail(category != NULL && category[0] != '\0'); |
744 | + |
745 | + AppData * appdata = NULL; |
746 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); |
747 | + gpointer data = g_hash_table_lookup(priv->apps, id); |
748 | + |
749 | + if (data == NULL) { |
750 | + /* Oh, we don't have one, let's build it and put it |
751 | + into the hash table ourselves */ |
752 | + appdata = g_new0(AppData, 1); |
753 | + |
754 | + appdata->category = g_strdup(category); |
755 | + g_get_current_time(&(appdata->first_touched)); |
756 | + /* NOTE: last touched set below */ |
757 | + |
758 | + g_hash_table_insert(priv->apps, g_strdup(id), appdata); |
759 | + } else { |
760 | + /* Boring, we've got this one already */ |
761 | + appdata = (AppData *)data; |
762 | + } |
763 | + |
764 | + /* Touch it and mark the DB as dirty */ |
765 | + g_get_current_time(&(appdata->last_touched)); |
766 | + get_dirty(lrufile); |
767 | + return; |
768 | +} |
769 | + |
770 | +/* Used to sort or compare different applications. */ |
771 | +gint |
772 | +app_lru_file_sort (AppLruFile * lrufile, const gchar * id_a, const gchar * id_b) |
773 | +{ |
774 | + g_return_val_if_fail(IS_APP_LRU_FILE(lrufile), -1); |
775 | + |
776 | + /* Let's first look to see if the IDs are the same, this |
777 | + really shouldn't happen, but it'll be confusing if we |
778 | + don't get it out of the way to start. */ |
779 | + if (g_strcmp0(id_a, id_b) == 0) { |
780 | + return 0; |
781 | + } |
782 | + |
783 | + AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); |
784 | + |
785 | + /* Now make sure we have app data for both of these. If |
786 | + not we'll assume that the one without is newer? */ |
787 | + gpointer data_a = g_hash_table_lookup(priv->apps, id_a); |
788 | + if (data_a == NULL) { |
789 | + return -1; |
790 | + } |
791 | + |
792 | + gpointer data_b = g_hash_table_lookup(priv->apps, id_b); |
793 | + if (data_b == NULL) { |
794 | + return 1; |
795 | + } |
796 | + |
797 | + /* Ugly casting */ |
798 | + AppData * appdata_a = (AppData *)data_a; |
799 | + AppData * appdata_b = (AppData *)data_b; |
800 | + |
801 | + /* Look at categories, we'll put the categories in alpha |
802 | + order if they're not the same. */ |
803 | + gint catcompare = g_strcmp0(appdata_a->category, appdata_b->category); |
804 | + if (catcompare != 0) { |
805 | + return catcompare; |
806 | + } |
807 | + |
808 | + /* Now we're looking at the first time that these two were |
809 | + seen in this account. Only using seconds. I mean, seriously. */ |
810 | + if (appdata_a->first_touched.tv_sec < appdata_b->first_touched.tv_sec) { |
811 | + return -1; |
812 | + } |
813 | + if (appdata_b->first_touched.tv_sec < appdata_a->first_touched.tv_sec) { |
814 | + return 1; |
815 | + } |
816 | + |
817 | + /* Eh, this seems roughly impossible. But if we have to choose, |
818 | + I like A better. */ |
819 | + return 1; |
820 | +} |
821 | |
822 | === added file 'src/application-service-lru-file.h' |
823 | --- src/application-service-lru-file.h 1970-01-01 00:00:00 +0000 |
824 | +++ src/application-service-lru-file.h 2010-01-21 20:27:14 +0000 |
825 | @@ -0,0 +1,59 @@ |
826 | +/* |
827 | +This object manages the database of when the entires were touched |
828 | +and loved. And writes that out to disk occationally as well. |
829 | + |
830 | +Copyright 2010 Canonical Ltd. |
831 | + |
832 | +Authors: |
833 | + Ted Gould <ted@canonical.com> |
834 | + |
835 | +This program is free software: you can redistribute it and/or modify it |
836 | +under the terms of the GNU General Public License version 3, as published |
837 | +by the Free Software Foundation. |
838 | + |
839 | +This program is distributed in the hope that it will be useful, but |
840 | +WITHOUT ANY WARRANTY; without even the implied warranties of |
841 | +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
842 | +PURPOSE. See the GNU General Public License for more details. |
843 | + |
844 | +You should have received a copy of the GNU General Public License along |
845 | +with this program. If not, see <http://www.gnu.org/licenses/>. |
846 | +*/ |
847 | + |
848 | +#ifndef __APP_LRU_FILE_H__ |
849 | +#define __APP_LRU_FILE_H__ |
850 | + |
851 | +#include <glib.h> |
852 | +#include <glib-object.h> |
853 | + |
854 | +G_BEGIN_DECLS |
855 | + |
856 | +#define APP_LRU_FILE_TYPE (app_lru_file_get_type ()) |
857 | +#define APP_LRU_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), APP_LRU_FILE_TYPE, AppLruFile)) |
858 | +#define APP_LRU_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), APP_LRU_FILE_TYPE, AppLruFileClass)) |
859 | +#define IS_APP_LRU_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APP_LRU_FILE_TYPE)) |
860 | +#define IS_APP_LRU_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APP_LRU_FILE_TYPE)) |
861 | +#define APP_LRU_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), APP_LRU_FILE_TYPE, AppLruFileClass)) |
862 | + |
863 | +typedef struct _AppLruFile AppLruFile; |
864 | +typedef struct _AppLruFileClass AppLruFileClass; |
865 | + |
866 | +struct _AppLruFileClass { |
867 | + GObjectClass parent_class; |
868 | + |
869 | +}; |
870 | + |
871 | +struct _AppLruFile { |
872 | + GObject parent; |
873 | + |
874 | +}; |
875 | + |
876 | +GType app_lru_file_get_type (void); |
877 | + |
878 | +AppLruFile * app_lru_file_new (void); |
879 | +void app_lru_file_touch (AppLruFile * lrufile, const gchar * id, const gchar * category); |
880 | +gint app_lru_file_sort (AppLruFile * lrufile, const gchar * id_a, const gchar * id_b); |
881 | + |
882 | +G_END_DECLS |
883 | + |
884 | +#endif |
885 | |
886 | === modified file 'src/application-service.c' |
887 | --- src/application-service.c 2010-01-08 21:02:29 +0000 |
888 | +++ src/application-service.c 2010-01-21 20:27:14 +0000 |
889 | @@ -25,6 +25,7 @@ |
890 | #include "notification-item-client.h" |
891 | #include "application-service-appstore.h" |
892 | #include "application-service-watcher.h" |
893 | +#include "application-service-lru-file.h" |
894 | #include "dbus-shared.h" |
895 | |
896 | /* The base main loop */ |
897 | @@ -35,6 +36,8 @@ |
898 | static ApplicationServiceWatcher * watcher = NULL; |
899 | /* The service management interface */ |
900 | static IndicatorService * service = NULL; |
901 | +/* The LRU file interface */ |
902 | +static AppLruFile * lrufile = NULL; |
903 | |
904 | /* Recieves the disonnection signal from the service |
905 | object and closes the mainloop. */ |
906 | @@ -59,8 +62,11 @@ |
907 | service = indicator_service_new(INDICATOR_APPLICATION_DBUS_ADDR); |
908 | g_signal_connect(G_OBJECT(service), INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_disconnected), NULL); |
909 | |
910 | + /* Start up the LRU file reading */ |
911 | + lrufile = app_lru_file_new(); |
912 | + |
913 | /* Building our app store */ |
914 | - appstore = APPLICATION_SERVICE_APPSTORE(g_object_new(APPLICATION_SERVICE_APPSTORE_TYPE, NULL)); |
915 | + appstore = application_service_appstore_new(lrufile); |
916 | |
917 | /* Adding a watcher for the Apps coming up */ |
918 | watcher = application_service_watcher_new(appstore); |
919 | @@ -73,6 +79,7 @@ |
920 | g_object_unref(G_OBJECT(watcher)); |
921 | g_object_unref(G_OBJECT(appstore)); |
922 | g_object_unref(G_OBJECT(service)); |
923 | + g_object_unref(G_OBJECT(lrufile)); |
924 | |
925 | return 0; |
926 | } |
927 | |
928 | === modified file 'src/application-service.xml' |
929 | --- src/application-service.xml 2010-01-07 23:06:38 +0000 |
930 | +++ src/application-service.xml 2010-01-21 20:27:14 +0000 |
931 | @@ -26,7 +26,7 @@ |
932 | |
933 | <!-- Methods --> |
934 | <method name="GetApplications"> |
935 | - <arg type="a(siso)" name="applications" direction="out" /> |
936 | + <arg type="a(sisos)" name="applications" direction="out" /> |
937 | </method> |
938 | |
939 | <!-- Signals --> |
940 | |
941 | === modified file 'src/indicator-application.c' |
942 | --- src/indicator-application.c 2010-01-07 23:06:38 +0000 |
943 | +++ src/indicator-application.c 2010-01-21 20:27:14 +0000 |
944 | @@ -75,6 +75,7 @@ |
945 | DBusGConnection * bus; |
946 | DBusGProxy * service_proxy; |
947 | GList * applications; |
948 | + GHashTable * theme_dirs; |
949 | }; |
950 | |
951 | typedef struct _ApplicationEntry ApplicationEntry; |
952 | @@ -91,11 +92,15 @@ |
953 | static void indicator_application_dispose (GObject *object); |
954 | static void indicator_application_finalize (GObject *object); |
955 | static GList * get_entries (IndicatorObject * io); |
956 | +static guint get_location (IndicatorObject * io, IndicatorObjectEntry * entry); |
957 | static void connected (IndicatorServiceManager * sm, gboolean connected, IndicatorApplication * application); |
958 | static void application_added (DBusGProxy * proxy, const gchar * iconname, gint position, const gchar * dbusaddress, const gchar * dbusobject, const gchar * icon_path, IndicatorApplication * application); |
959 | static void application_removed (DBusGProxy * proxy, gint position , IndicatorApplication * application); |
960 | static void application_icon_changed (DBusGProxy * proxy, gint position, const gchar * iconname, IndicatorApplication * application); |
961 | static void get_applications (DBusGProxy *proxy, GPtrArray *OUT_applications, GError *error, gpointer userdata); |
962 | +static void get_applications_helper (gpointer data, gpointer user_data); |
963 | +static void theme_dir_unref(IndicatorApplication * ia, const gchar * dir); |
964 | +static void theme_dir_ref(IndicatorApplication * ia, const gchar * dir); |
965 | |
966 | G_DEFINE_TYPE (IndicatorApplication, indicator_application, INDICATOR_OBJECT_TYPE); |
967 | |
968 | @@ -112,6 +117,7 @@ |
969 | IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass); |
970 | |
971 | io_class->get_entries = get_entries; |
972 | + io_class->get_location = get_location; |
973 | |
974 | dbus_g_object_register_marshaller(_application_service_marshal_VOID__STRING_INT_STRING_STRING_STRING, |
975 | G_TYPE_NONE, |
976 | @@ -138,12 +144,15 @@ |
977 | /* These are built in the connection phase */ |
978 | priv->bus = NULL; |
979 | priv->service_proxy = NULL; |
980 | + priv->theme_dirs = NULL; |
981 | |
982 | priv->sm = indicator_service_manager_new(INDICATOR_APPLICATION_DBUS_ADDR); |
983 | g_signal_connect(G_OBJECT(priv->sm), INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, G_CALLBACK(connected), self); |
984 | |
985 | priv->applications = NULL; |
986 | |
987 | + priv->theme_dirs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
988 | + |
989 | return; |
990 | } |
991 | |
992 | @@ -173,6 +182,15 @@ |
993 | priv->service_proxy = NULL; |
994 | } |
995 | |
996 | + if (priv->theme_dirs != NULL) { |
997 | + while (g_hash_table_size(priv->theme_dirs)) { |
998 | + GList * keys = g_hash_table_get_keys(priv->theme_dirs); |
999 | + theme_dir_unref(INDICATOR_APPLICATION(object), (gchar *)keys->data); |
1000 | + } |
1001 | + g_hash_table_destroy(priv->theme_dirs); |
1002 | + priv->theme_dirs = NULL; |
1003 | + } |
1004 | + |
1005 | G_OBJECT_CLASS (indicator_application_parent_class)->dispose (object); |
1006 | return; |
1007 | } |
1008 | @@ -282,6 +300,15 @@ |
1009 | return retval; |
1010 | } |
1011 | |
1012 | +/* Finds the location of a specific entry */ |
1013 | +static guint |
1014 | +get_location (IndicatorObject * io, IndicatorObjectEntry * entry) |
1015 | +{ |
1016 | + g_return_val_if_fail(IS_INDICATOR_APPLICATION(io), 0); |
1017 | + IndicatorApplicationPrivate * priv = INDICATOR_APPLICATION_GET_PRIVATE(io); |
1018 | + return g_list_index(priv->applications, entry); |
1019 | +} |
1020 | + |
1021 | /* Here we respond to new applications by building up the |
1022 | ApplicationEntry and signaling the indicator host that |
1023 | we've got a new indicator. */ |
1024 | @@ -295,14 +322,17 @@ |
1025 | app->icon_path = NULL; |
1026 | if (icon_path != NULL && icon_path[0] != '\0') { |
1027 | app->icon_path = g_strdup(icon_path); |
1028 | - g_debug("\tAppending search path: %s", app->icon_path); |
1029 | - gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), app->icon_path); |
1030 | + theme_dir_ref(application, icon_path); |
1031 | } |
1032 | |
1033 | app->entry.image = GTK_IMAGE(gtk_image_new_from_icon_name(iconname, GTK_ICON_SIZE_MENU)); |
1034 | app->entry.label = NULL; |
1035 | app->entry.menu = GTK_MENU(dbusmenu_gtkmenu_new((gchar *)dbusaddress, (gchar *)dbusobject)); |
1036 | |
1037 | + /* Keep copies of these for ourself, just in case. */ |
1038 | + g_object_ref(app->entry.image); |
1039 | + g_object_ref(app->entry.menu); |
1040 | + |
1041 | gtk_widget_show(GTK_WIDGET(app->entry.image)); |
1042 | |
1043 | priv->applications = g_list_insert(priv->applications, app, position); |
1044 | @@ -329,6 +359,7 @@ |
1045 | g_signal_emit(G_OBJECT(application), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED_ID, 0, &(app->entry), TRUE); |
1046 | |
1047 | if (app->icon_path != NULL) { |
1048 | + theme_dir_unref(application, app->icon_path); |
1049 | g_free(app->icon_path); |
1050 | } |
1051 | if (app->entry.image != NULL) { |
1052 | @@ -368,6 +399,115 @@ |
1053 | static void |
1054 | get_applications (DBusGProxy *proxy, GPtrArray *OUT_applications, GError *error, gpointer userdata) |
1055 | { |
1056 | - |
1057 | - return; |
1058 | -} |
1059 | + if (error != NULL) { |
1060 | + g_warning("Unable to get application list: %s", error->message); |
1061 | + return; |
1062 | + } |
1063 | + g_ptr_array_foreach(OUT_applications, get_applications_helper, userdata); |
1064 | + |
1065 | + return; |
1066 | +} |
1067 | + |
1068 | +/* A little helper that takes apart the DBus structure and calls |
1069 | + application_added on every entry in the list. */ |
1070 | +static void |
1071 | +get_applications_helper (gpointer data, gpointer user_data) |
1072 | +{ |
1073 | + GValueArray * array = (GValueArray *)data; |
1074 | + |
1075 | + g_return_if_fail(array->n_values == 5); |
1076 | + |
1077 | + const gchar * icon_name = g_value_get_string(g_value_array_get_nth(array, 0)); |
1078 | + gint position = g_value_get_int(g_value_array_get_nth(array, 1)); |
1079 | + const gchar * dbus_address = g_value_get_string(g_value_array_get_nth(array, 2)); |
1080 | + const gchar * dbus_object = g_value_get_boxed(g_value_array_get_nth(array, 3)); |
1081 | + const gchar * icon_path = g_value_get_string(g_value_array_get_nth(array, 4)); |
1082 | + |
1083 | + return application_added(NULL, icon_name, position, dbus_address, dbus_object, icon_path, user_data); |
1084 | +} |
1085 | + |
1086 | +/* Refs a theme directory, and it may add it to the search |
1087 | + path */ |
1088 | +static void |
1089 | +theme_dir_unref(IndicatorApplication * ia, const gchar * dir) |
1090 | +{ |
1091 | + IndicatorApplicationPrivate * priv = INDICATOR_APPLICATION_GET_PRIVATE(ia); |
1092 | + |
1093 | + /* Grab the count for this dir */ |
1094 | + int count = GPOINTER_TO_INT(g_hash_table_lookup(priv->theme_dirs, dir)); |
1095 | + |
1096 | + /* Is this a simple deprecation, if so, we can just lower the |
1097 | + number and move on. */ |
1098 | + if (count > 1) { |
1099 | + count--; |
1100 | + g_hash_table_insert(priv->theme_dirs, g_strdup(dir), GINT_TO_POINTER(count)); |
1101 | + return; |
1102 | + } |
1103 | + |
1104 | + /* Try to remove it from the hash table, this makes sure |
1105 | + that it existed */ |
1106 | + if (!g_hash_table_remove(priv->theme_dirs, dir)) { |
1107 | + g_warning("Unref'd a directory that wasn't in the theme dir hash table."); |
1108 | + return; |
1109 | + } |
1110 | + |
1111 | + GtkIconTheme * icon_theme = gtk_icon_theme_get_default(); |
1112 | + gchar ** paths; |
1113 | + gint path_count; |
1114 | + |
1115 | + gtk_icon_theme_get_search_path(icon_theme, &paths, &path_count); |
1116 | + |
1117 | + gint i; |
1118 | + gboolean found = FALSE; |
1119 | + for (i = 0; i < path_count; i++) { |
1120 | + if (found) { |
1121 | + /* If we've already found the right entry */ |
1122 | + paths[i - 1] = paths[i]; |
1123 | + } else { |
1124 | + /* We're still looking, is this the one? */ |
1125 | + if (!g_strcmp0(paths[i], dir)) { |
1126 | + found = TRUE; |
1127 | + /* We're freeing this here as it won't be captured by the |
1128 | + g_strfreev() below as it's out of the array. */ |
1129 | + g_free(paths[i]); |
1130 | + } |
1131 | + } |
1132 | + } |
1133 | + |
1134 | + /* If we found one we need to reset the path to |
1135 | + accomidate the changes */ |
1136 | + if (found) { |
1137 | + paths[path_count - 1] = NULL; /* Clear the last one */ |
1138 | + gtk_icon_theme_set_search_path(icon_theme, (const gchar **)paths, path_count - 1); |
1139 | + } |
1140 | + |
1141 | + g_strfreev(paths); |
1142 | + |
1143 | + return; |
1144 | +} |
1145 | + |
1146 | +/* Unrefs a theme directory. This may involve removing it from |
1147 | + the search path. */ |
1148 | +static void |
1149 | +theme_dir_ref(IndicatorApplication * ia, const gchar * dir) |
1150 | +{ |
1151 | + IndicatorApplicationPrivate * priv = INDICATOR_APPLICATION_GET_PRIVATE(ia); |
1152 | + |
1153 | + int count = 0; |
1154 | + if ((count = GPOINTER_TO_INT(g_hash_table_lookup(priv->theme_dirs, dir))) != 0) { |
1155 | + /* It exists so what we need to do is increase the ref |
1156 | + count of this dir. */ |
1157 | + count++; |
1158 | + } else { |
1159 | + /* It doesn't exist, so we need to add it to the table |
1160 | + and to the search path. */ |
1161 | + gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir); |
1162 | + g_debug("\tAppending search path: %s", dir); |
1163 | + count = 1; |
1164 | + } |
1165 | + |
1166 | + g_hash_table_insert(priv->theme_dirs, g_strdup(dir), GINT_TO_POINTER(count)); |
1167 | + |
1168 | + return; |
1169 | +} |
1170 | + |
1171 | |
1172 | === modified file 'src/libappindicator/app-indicator.c' |
1173 | --- src/libappindicator/app-indicator.c 2010-01-19 19:53:16 +0000 |
1174 | +++ src/libappindicator/app-indicator.c 2010-01-21 20:27:14 +0000 |
1175 | @@ -370,6 +370,10 @@ |
1176 | priv->menu = NULL; |
1177 | } |
1178 | |
1179 | + if (priv->menuservice != NULL) { |
1180 | + g_object_unref (priv->menuservice); |
1181 | + } |
1182 | + |
1183 | if (priv->dbus_proxy != NULL) { |
1184 | g_object_unref(G_OBJECT(priv->dbus_proxy)); |
1185 | priv->dbus_proxy = NULL; |
0.0.10