~ubuntu-branches/ubuntu/karmic/indicator-messages/karmic

1.1.5 by Ted Gould
Import upstream version 0.2.0
1
/*
2
An indicator to show information that is in messaging applications
3
that the user is using.
4
5
Copyright 2009 Canonical Ltd.
6
7
Authors:
8
    Ted Gould <ted@canonical.com>
9
10
This program is free software: you can redistribute it and/or modify it 
11
under the terms of the GNU General Public License version 3, as published 
12
by the Free Software Foundation.
13
14
This program is distributed in the hope that it will be useful, but 
15
WITHOUT ANY WARRANTY; without even the implied warranties of 
16
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
17
PURPOSE.  See the GNU General Public License for more details.
18
19
You should have received a copy of the GNU General Public License along 
20
with this program.  If not, see <http://www.gnu.org/licenses/>.
21
*/
22
23
#include <string.h>
14 by Sebastien Bacher
src/messages-service.c: initialise the translations
24
#include <locale.h>
1.1.7 by Ted Gould
Import upstream version 0.2.4
25
#include <libintl.h>
26
#include <config.h>
1.1.5 by Ted Gould
Import upstream version 0.2.0
27
#include <pango/pango-utils.h>
28
#include <dbus/dbus-glib-bindings.h>
29
#include <libindicate/listener.h>
30
#include <gio/gio.h>
31
1.1.6 by Ted Gould
Import upstream version 0.2.2
32
#include <libdbusmenu-glib/client.h>
1.1.5 by Ted Gould
Import upstream version 0.2.0
33
#include <libdbusmenu-glib/server.h>
34
35
#include "im-menu-item.h"
36
#include "app-menu-item.h"
37
#include "launcher-menu-item.h"
38
#include "dbus-data.h"
39
#include "dirs.h"
40
#include "messages-service-dbus.h"
41
42
static IndicateListener * listener;
43
static GList * serverList = NULL;
44
static GList * launcherList = NULL;
45
46
static DbusmenuMenuitem * root_menuitem = NULL;
47
static GMainLoop * mainloop = NULL;
48
49
static MessageServiceDbus * dbus_interface = NULL;
50
51
52
static void server_count_changed (AppMenuItem * appitem, guint count, gpointer data);
53
static void server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data);
54
static void im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data);
55
static void resort_menu (DbusmenuMenuitem * menushell);
1.1.6 by Ted Gould
Import upstream version 0.2.2
56
static void indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data);
1.1.5 by Ted Gould
Import upstream version 0.2.0
57
static void check_eclipses (AppMenuItem * ai);
58
static void remove_eclipses (AppMenuItem * ai);
59
static gboolean build_launcher (gpointer data);
60
static gboolean build_launchers (gpointer data);
61
static gboolean blacklist_init (gpointer data);
62
static gboolean blacklist_add (gpointer data);
63
static gboolean blacklist_remove (gpointer data);
64
static void blacklist_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data);
65
static void app_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data);
66
static gboolean destroy_launcher (gpointer data);
67
static void check_hidden (void);
68
69
70
/*
71
 * Server List
72
 */
73
74
typedef struct _serverList_t serverList_t;
75
struct _serverList_t {
76
	IndicateListenerServer * server;
77
	AppMenuItem * menuitem;
1.1.6 by Ted Gould
Import upstream version 0.2.2
78
	DbusmenuMenuitem * separator;
79
	gboolean attention;
80
	guint count;
1.1.5 by Ted Gould
Import upstream version 0.2.0
81
	GList * imList;
82
};
83
84
static gint
85
serverList_equal (gconstpointer a, gconstpointer b)
86
{
87
	serverList_t * pa, * pb;
88
89
	pa = (serverList_t *)a;
90
	pb = (serverList_t *)b;
91
92
	const gchar * pas = INDICATE_LISTENER_SERVER_DBUS_NAME(pa->server);
93
	const gchar * pbs = INDICATE_LISTENER_SERVER_DBUS_NAME(pb->server);
94
95
	return g_strcmp0(pas, pbs);
96
}
97
98
static gint
99
serverList_sort (gconstpointer a, gconstpointer b)
100
{
101
	serverList_t * pa, * pb;
102
103
	pa = (serverList_t *)a;
104
	pb = (serverList_t *)b;
105
106
	const gchar * pan = app_menu_item_get_name(pa->menuitem);
107
	const gchar * pbn = app_menu_item_get_name(pb->menuitem);
108
109
	return g_strcmp0(pan, pbn);
110
}
111
112
/*
113
 * Item List
114
 */
115
116
typedef struct _imList_t imList_t;
117
struct _imList_t {
118
	IndicateListenerServer * server;
119
	IndicateListenerIndicator * indicator;
120
	DbusmenuMenuitem * menuitem;
121
	gulong timechange_cb;
1.1.6 by Ted Gould
Import upstream version 0.2.2
122
	gulong attentionchange_cb;
1.1.5 by Ted Gould
Import upstream version 0.2.0
123
};
124
125
static gboolean
126
imList_equal (gconstpointer a, gconstpointer b)
127
{
128
	imList_t * pa, * pb;
129
130
	pa = (imList_t *)a;
131
	pb = (imList_t *)b;
132
133
	const gchar * pas = INDICATE_LISTENER_SERVER_DBUS_NAME(pa->server);
134
	const gchar * pbs = INDICATE_LISTENER_SERVER_DBUS_NAME(pb->server);
135
136
	guint pai = INDICATE_LISTENER_INDICATOR_ID(pa->indicator);
137
	guint pbi = INDICATE_LISTENER_INDICATOR_ID(pb->indicator);
138
139
	g_debug("\tComparing (%s %d) to (%s %d)", pas, pai, pbs, pbi);
140
141
	return !((!g_strcmp0(pas, pbs)) && (pai == pbi));
142
}
143
144
static gint
145
imList_sort (gconstpointer a, gconstpointer b)
146
{
147
	imList_t * pa, * pb;
148
149
	pa = (imList_t *)a;
150
	pb = (imList_t *)b;
151
152
	return (gint)(im_menu_item_get_seconds(IM_MENU_ITEM(pb->menuitem)) - im_menu_item_get_seconds(IM_MENU_ITEM(pa->menuitem)));
153
}
154
155
/*
156
 * Launcher List
157
 */
158
159
typedef struct _launcherList_t launcherList_t;
160
struct _launcherList_t {
161
	LauncherMenuItem * menuitem;
1.1.6 by Ted Gould
Import upstream version 0.2.2
162
	DbusmenuMenuitem * separator;
1.1.5 by Ted Gould
Import upstream version 0.2.0
163
	GList * appdiritems;
164
};
165
166
static gint
167
launcherList_sort (gconstpointer a, gconstpointer b)
168
{
169
	launcherList_t * pa, * pb;
170
171
	pa = (launcherList_t *)a;
172
	pb = (launcherList_t *)b;
173
174
	const gchar * pan = launcher_menu_item_get_name(pa->menuitem);
175
	const gchar * pbn = launcher_menu_item_get_name(pb->menuitem);
176
177
	return g_strcmp0(pan, pbn);
178
}
179
180
static void
181
launcherList_count_helper (gpointer data, gpointer user_data)
182
{
183
	guint * count = (guint *)user_data;
184
	launcherList_t * li = (launcherList_t *)data;
185
186
	if (!launcher_menu_item_get_eclipsed(li->menuitem)) {
187
		*count = *count + 1;
188
	}
189
190
	return;
191
}
192
193
static guint
194
launcherList_count (void)
195
{
196
	guint count = 0;
197
198
	g_list_foreach(launcherList, launcherList_count_helper, &count);
199
200
	return count;
201
}
202
203
/*
204
 * Black List
205
 */
206
207
static GHashTable * blacklist = NULL;
208
static GFileMonitor * blacklistdirmon = NULL;
209
210
/* Initialize the black list and start to setup
211
   handlers for it. */
212
static gboolean
213
blacklist_init (gpointer data)
214
{
215
	blacklist = g_hash_table_new_full(g_str_hash, g_str_equal,
216
	                                  g_free, g_free);
217
218
	gchar * blacklistdir = g_build_filename(g_get_user_config_dir(), USER_BLACKLIST_DIR, NULL);
219
	g_debug("Looking at blacklist: %s", blacklistdir);
220
	if (!g_file_test(blacklistdir, G_FILE_TEST_IS_DIR)) {
221
		g_free(blacklistdir);
222
		return FALSE;
223
	}
224
225
	GFile * filedir = g_file_new_for_path(blacklistdir);
226
	blacklistdirmon = g_file_monitor_directory(filedir, G_FILE_MONITOR_NONE, NULL, NULL);
227
	if (blacklistdirmon != NULL) {
228
		g_signal_connect(G_OBJECT(blacklistdirmon), "changed", G_CALLBACK(blacklist_dir_changed), NULL);
229
	}
230
231
	GError * error = NULL;
232
	GDir * dir = g_dir_open(blacklistdir, 0, &error);
233
	if (dir == NULL) {
234
		g_warning("Unable to open blacklist directory (%s): %s", blacklistdir, error == NULL ? "No Message" : error->message);
235
		g_error_free(error);
236
		g_free(blacklistdir);
237
		return FALSE;
238
	}
239
240
	const gchar * filename = NULL;
241
	while ((filename = g_dir_read_name(dir)) != NULL) {
242
		g_debug("Found file: %s", filename);
243
		gchar * path = g_build_filename(blacklistdir, filename, NULL);
244
		g_idle_add(blacklist_add, path);
245
	}
246
247
	g_dir_close(dir);
248
	g_free(blacklistdir);
249
250
	return FALSE;
251
}
252
253
/* Add a definition file into the black list and eclipse
254
   and launchers that have the same file. */
255
static gboolean
256
blacklist_add (gpointer udata)
257
{
258
	gchar * definition_file = (gchar *)udata;
259
	/* Dump the file */
260
	gchar * desktop;
261
	g_file_get_contents(definition_file, &desktop, NULL, NULL);
262
	if (desktop == NULL) {
263
		g_warning("Couldn't get data out of: %s", definition_file);
264
		return FALSE;
265
	}
266
267
	/* Clean up the data */
268
	gchar * trimdesktop = pango_trim_string(desktop);
269
	g_free(desktop);
270
271
	/* Check for conflicts */
272
	gpointer data = g_hash_table_lookup(blacklist, trimdesktop);
273
	if (data != NULL) {
274
		gchar * oldfile = (gchar *)data;
275
		if (!g_strcmp0(oldfile, definition_file)) {
276
			g_warning("Already added file '%s'", oldfile);
277
		} else {
278
			g_warning("Already have desktop file '%s' in blacklist file '%s' not adding from '%s'", trimdesktop, oldfile, definition_file);
279
		}
280
281
		g_free(trimdesktop);
282
		g_free(definition_file);
283
		return FALSE;
284
	}
285
286
	/* Actually blacklist this thing */
287
	g_hash_table_insert(blacklist, trimdesktop, definition_file);
288
	g_debug("Adding Blacklist item '%s' for desktop '%s'", definition_file, trimdesktop);
289
290
	/* Go through and eclipse folks */
291
	GList * launcher;
292
	for (launcher = launcherList; launcher != NULL; launcher = launcher->next) {
293
		launcherList_t * item = (launcherList_t *)launcher->data;
294
		if (!g_strcmp0(trimdesktop, launcher_menu_item_get_desktop(item->menuitem))) {
295
			launcher_menu_item_set_eclipsed(item->menuitem, TRUE);
1.1.6 by Ted Gould
Import upstream version 0.2.2
296
			dbusmenu_menuitem_property_set(item->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "false");
1.1.5 by Ted Gould
Import upstream version 0.2.0
297
		}
298
	}
299
300
	check_hidden();
301
302
	return FALSE;
303
}
304
305
/* Remove a black list item based on the definition file
306
   and uneclipse those launchers blocked by it. */
307
static gboolean
308
blacklist_remove (gpointer data)
309
{
310
	gchar * definition_file = (gchar *)data;
311
	g_debug("Removing: %s", definition_file);
312
313
	GHashTableIter iter;
314
	gpointer key, value;
315
	gboolean found = FALSE;
316
317
	g_hash_table_iter_init(&iter, blacklist);
318
	while (g_hash_table_iter_next(&iter, &key, &value)) {
319
		if (!g_strcmp0((gchar *)value, definition_file)) {
320
			found = TRUE;
321
			break;
322
		}
323
	}
324
325
	if (!found) {
326
		g_debug("\tNot found!");
327
		return FALSE;
328
	}
329
330
	GList * launcheritem;
331
	for (launcheritem = launcherList; launcheritem != NULL; launcheritem = launcheritem->next) {
332
		launcherList_t * li = (launcherList_t *)launcheritem->data;
333
		if (!g_strcmp0(launcher_menu_item_get_desktop(li->menuitem), (gchar *)key)) {
334
			GList * serveritem;
335
			for (serveritem = serverList; serveritem != NULL; serveritem = serveritem->next) {
336
				serverList_t * si = (serverList_t *)serveritem->data;
337
				if (!g_strcmp0(app_menu_item_get_desktop(si->menuitem), (gchar *)key)) {
338
					break;
339
				}
340
			}
341
			if (serveritem == NULL) {
342
				launcher_menu_item_set_eclipsed(li->menuitem, FALSE);
1.1.6 by Ted Gould
Import upstream version 0.2.2
343
				dbusmenu_menuitem_property_set(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "true");
1.1.5 by Ted Gould
Import upstream version 0.2.0
344
			}
345
		}
346
	}
347
348
	if (!g_hash_table_remove(blacklist, key)) {
349
		g_warning("Unable to remove '%s' with value '%s'", definition_file, (gchar *)key);
350
	}
351
352
	check_hidden();
1.1.7 by Ted Gould
Import upstream version 0.2.4
353
	resort_menu(root_menuitem);
1.1.5 by Ted Gould
Import upstream version 0.2.0
354
355
	return FALSE;
356
}
357
358
/* Check to see if a particular desktop file is
359
   in the blacklist. */
360
static gboolean
361
blacklist_check (const gchar * desktop_file)
362
{
363
	g_debug("Checking blacklist for: %s", desktop_file);
364
	if (blacklist == NULL) return FALSE;
365
366
	if (g_hash_table_lookup(blacklist, desktop_file)) {
367
		g_debug("\tFound!");
368
		return TRUE;
369
	}
370
371
	return FALSE;
372
}
373
374
/* A callback everytime the blacklist directory changes
375
   in some way.  It needs to handle that. */
376
static void
377
blacklist_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data)
378
{
379
	g_debug("Blacklist directory changed!");
380
381
	switch (event_type) {
382
	case G_FILE_MONITOR_EVENT_DELETED: {
383
		gchar * path = g_file_get_path(file);
384
		g_debug("\tDelete: %s", path);
385
		g_idle_add(blacklist_remove, path);
386
		break;
387
	}
388
	case G_FILE_MONITOR_EVENT_CREATED: {
389
		gchar * path = g_file_get_path(file);
390
		g_debug("\tCreate: %s", path);
391
		g_idle_add(blacklist_add, path);
392
		break;
393
	}
394
	default:
395
		break;
396
	}
397
398
	return;
399
}
400
401
/*
402
 * More code
403
 */
404
1.1.6 by Ted Gould
Import upstream version 0.2.2
405
/* Goes through all the servers and sees if any of them
406
   want attention.  If they do, then well we'll give it
407
   to them.  If they don't, let's not bother the user
408
   any, shall we? */
409
static void
410
check_attention (void)
411
{
412
	GList * pointer;
413
	for (pointer = serverList; pointer != NULL; pointer = g_list_next(pointer)) {
414
		serverList_t * slt = (serverList_t *)pointer->data;
415
		if (slt->attention) {
416
			message_service_dbus_set_attention(dbus_interface, TRUE);
417
			return;
418
		}
419
	}
420
	message_service_dbus_set_attention(dbus_interface, FALSE);
421
	return;
422
}
423
424
/* This checks a server listing to see if it should
425
   have attention.  It can get attention through it's
426
   count or by having an indicator that is requestion
427
   attention. */
428
static void
429
server_attention (serverList_t * slt)
430
{
431
	/* Count, easy yes and out. */
432
	if (slt->count > 0) {
433
		slt->attention = TRUE;
434
		return;
435
	}
436
437
	/* Check to see if any of the indicators want attention */
438
	GList * pointer;
439
	for (pointer = slt->imList; pointer != NULL; pointer = g_list_next(pointer)) {
440
		imList_t * ilt = (imList_t *)pointer->data;
441
		if (im_menu_item_get_attention(IM_MENU_ITEM(ilt->menuitem))) {
442
			slt->attention = TRUE;
443
			return;
444
		}
445
	}
446
447
	/* Nope, no one */
448
	slt->attention = FALSE;
449
	return;
450
}
451
452
/* A new server has been created on the indicate bus.
453
   We need to check to see if we like it.  And build
454
   structures for it if so. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
455
static void 
456
server_added (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data)
457
{
458
	g_debug("Server Added '%s' of type '%s'.", INDICATE_LISTENER_SERVER_DBUS_NAME(server), type);
459
	if (type == NULL) {
460
		return;
461
	}
462
463
	if (type[0] == '\0') {
464
		return;
465
	}
466
467
	if (strncmp(type, "message", strlen("message"))) {
468
		g_debug("\tServer type '%s' is not a message based type.", type);
469
		return;
470
	}
471
472
	DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data);
473
	if (menushell == NULL) {
474
		g_error("\tData in callback is not a menushell");
475
		return;
476
	}
477
1.1.6 by Ted Gould
Import upstream version 0.2.2
478
	/* Build the Menu item */
1.1.5 by Ted Gould
Import upstream version 0.2.0
479
	AppMenuItem * menuitem = app_menu_item_new(listener, server);
480
1.1.6 by Ted Gould
Import upstream version 0.2.2
481
	/* Build a possible server structure */
1.1.5 by Ted Gould
Import upstream version 0.2.0
482
	serverList_t * sl_item = g_new0(serverList_t, 1);
483
	sl_item->server = server;
484
	sl_item->menuitem = menuitem;
485
	sl_item->imList = NULL;
1.1.6 by Ted Gould
Import upstream version 0.2.2
486
	sl_item->attention = FALSE;
487
	sl_item->count = 0;
488
489
	/* Build a separator */
490
	sl_item->separator = dbusmenu_menuitem_new();
491
	dbusmenu_menuitem_property_set(sl_item->separator, "type", DBUSMENU_CLIENT_TYPES_SEPARATOR);
1.1.5 by Ted Gould
Import upstream version 0.2.0
492
493
	/* Incase we got an indicator first */
494
	GList * alreadythere = g_list_find_custom(serverList, sl_item, serverList_equal);
495
	if (alreadythere != NULL) {
1.1.6 by Ted Gould
Import upstream version 0.2.2
496
		/* Use the one we already had */
1.1.5 by Ted Gould
Import upstream version 0.2.0
497
		g_free(sl_item);
498
		sl_item = (serverList_t *)alreadythere->data;
499
		sl_item->menuitem = menuitem;
500
		serverList = g_list_sort(serverList, serverList_sort);
501
	} else {
1.1.6 by Ted Gould
Import upstream version 0.2.2
502
		/* Insert the new one in the list */
1.1.5 by Ted Gould
Import upstream version 0.2.0
503
		serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort);
504
	}
505
1.1.6 by Ted Gould
Import upstream version 0.2.2
506
	/* Connect the signals up to the menu item */
507
	g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_COUNT_CHANGED, G_CALLBACK(server_count_changed), sl_item);
508
	g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_NAME_CHANGED,  G_CALLBACK(server_name_changed),  menushell);
509
510
	/* Put our new menu item in, with the separator behind it.
511
	   resort_menu will take care of whether it should be hidden
512
	   or not. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
513
	dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem));
1.1.6 by Ted Gould
Import upstream version 0.2.2
514
	dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(sl_item->separator));
1.1.5 by Ted Gould
Import upstream version 0.2.0
515
516
	resort_menu(menushell);
517
	check_hidden();
518
519
	return;
520
}
521
1.1.6 by Ted Gould
Import upstream version 0.2.2
522
/* The name of a server has changed, we probably
523
   need to reorder the menu to keep it in alphabetical
524
   order.  This happens often after we read the destkop
525
   file from disk. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
526
static void
527
server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data)
528
{
529
	serverList = g_list_sort(serverList, serverList_sort);
530
	check_eclipses(appitem);
531
	resort_menu(DBUSMENU_MENUITEM(data));
532
	return;
533
}
534
1.1.6 by Ted Gould
Import upstream version 0.2.2
535
/* If the count on the server changes, we need to know
536
   whether that should be grabbing attention or not.  If
537
   it is, we need to reevaluate whether the whole thing
538
   should be grabbing attention or not. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
539
static void
540
server_count_changed (AppMenuItem * appitem, guint count, gpointer data)
541
{
1.1.6 by Ted Gould
Import upstream version 0.2.2
542
	serverList_t * slt = (serverList_t *)data;
543
	slt->count = count;
544
545
	if (count == 0 && slt->attention) {
546
		/* Regen based on indicators if the count isn't going to cause it. */
547
		server_attention(slt);
548
		/* If we're dropping let's see if we're the last. */
549
		if (!slt->attention) {
550
			check_attention();
551
		}
552
	}
553
554
	if (count != 0 && !slt->attention) {
555
		slt->attention = TRUE;
556
		/* Let's tell everyone about us! */
1.1.5 by Ted Gould
Import upstream version 0.2.0
557
		message_service_dbus_set_attention(dbus_interface, TRUE);
558
	}
559
560
	return;
561
}
562
1.1.6 by Ted Gould
Import upstream version 0.2.2
563
/* Respond to the IM entrie's time changing
564
   which results in it needing to resort the list
565
   and rebuild the menu to match. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
566
static void
567
im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data)
568
{
569
	serverList_t * sl = (serverList_t *)data;
570
	sl->imList = g_list_sort(sl->imList, imList_sort);
571
	resort_menu(root_menuitem);
572
	return;
573
}
574
1.1.6 by Ted Gould
Import upstream version 0.2.2
575
/* The IM entrie's request for attention has changed
576
   so we need to pass that up the stack. */
577
static void
578
im_attention_changed (ImMenuItem * imitem, gboolean requestit, gpointer data)
579
{
580
	serverList_t * sl = (serverList_t *)data;
581
582
	if (requestit) {
583
		sl->attention = TRUE;
584
		message_service_dbus_set_attention(dbus_interface, TRUE);
585
	} else {
586
		server_attention(sl);
587
		if (!sl->attention) {
588
			check_attention();
589
		}
590
	}
591
592
	return;
593
}
594
595
/* Run when a server is removed from the indicator bus.  It figures
596
   out if we have it somewhere, and if so then we dump it out and
597
   clean up all of it's entries. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
598
static void 
599
server_removed (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data)
600
{
1.1.6 by Ted Gould
Import upstream version 0.2.2
601
	/* Look for the server */
1.1.5 by Ted Gould
Import upstream version 0.2.0
602
	g_debug("Removing server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server));
1.1.6 by Ted Gould
Import upstream version 0.2.2
603
	serverList_t slt = {0};
1.1.5 by Ted Gould
Import upstream version 0.2.0
604
	slt.server = server;
605
	GList * lookup = g_list_find_custom(serverList, &slt, serverList_equal);
606
1.1.6 by Ted Gould
Import upstream version 0.2.2
607
	/* If we don't have it, exit */
1.1.5 by Ted Gould
Import upstream version 0.2.0
608
	if (lookup == NULL) {
609
		g_debug("\tUnable to find server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server));
610
		return;
611
	}
612
613
	serverList_t * sltp = (serverList_t *)lookup->data;
614
1.1.6 by Ted Gould
Import upstream version 0.2.2
615
	/* Removing indicators from this server */
1.1.5 by Ted Gould
Import upstream version 0.2.0
616
	while (sltp->imList) {
617
		imList_t * imitem = (imList_t *)sltp->imList->data;
1.1.6 by Ted Gould
Import upstream version 0.2.2
618
		indicator_removed(listener, server, imitem->indicator, data);
1.1.5 by Ted Gould
Import upstream version 0.2.0
619
	}
620
1.1.6 by Ted Gould
Import upstream version 0.2.2
621
	/* Remove from the server list */
1.1.5 by Ted Gould
Import upstream version 0.2.0
622
	serverList = g_list_remove(serverList, sltp);
623
1.1.6 by Ted Gould
Import upstream version 0.2.2
624
	/* Remove launchers this could be eclipsing */
625
	remove_eclipses(sltp->menuitem);
626
627
	/* If there is a menu item, let's get rid of it. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
628
	if (sltp->menuitem != NULL) {
1.1.6 by Ted Gould
Import upstream version 0.2.2
629
		dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(sltp->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE, "false");
1.1.5 by Ted Gould
Import upstream version 0.2.0
630
		dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(sltp->menuitem));
631
		g_object_unref(G_OBJECT(sltp->menuitem));
632
	}
633
1.1.6 by Ted Gould
Import upstream version 0.2.2
634
	/* If there is a separator, let's get rid of it. */
635
	if (sltp->separator != NULL) {
636
		dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(sltp->separator), DBUSMENU_MENUITEM_PROP_VISIBLE, "false");
637
		dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(sltp->separator));
638
		g_object_unref(G_OBJECT(sltp->separator));
639
	}
640
641
	if (sltp->attention) {
642
		/* Check to see if this was the server causing the menu item to
643
		   be lit up. */
644
		check_attention();
645
	}
646
1.1.5 by Ted Gould
Import upstream version 0.2.0
647
	g_free(sltp);
648
649
	check_hidden();
650
651
	return;
652
}
653
654
typedef struct _menushell_location menushell_location_t;
655
struct _menushell_location {
656
	const IndicateListenerServer * server;
657
	gint position;
658
	gboolean found;
659
};
660
661
static void
662
menushell_foreach_cb (DbusmenuMenuitem * data_mi, gpointer data_ms) {
663
	menushell_location_t * msl = (menushell_location_t *)data_ms;
664
665
	if (msl->found) return;
666
667
	msl->position++;
668
669
	if (!IS_APP_MENU_ITEM(data_mi)) {
670
		return;
671
	}
672
673
	AppMenuItem * appmenu = APP_MENU_ITEM(data_mi);
674
	if (!g_strcmp0(INDICATE_LISTENER_SERVER_DBUS_NAME((IndicateListenerServer*)msl->server), INDICATE_LISTENER_SERVER_DBUS_NAME(app_menu_item_get_server(appmenu)))) {
675
		msl->found = TRUE;
676
	}
677
678
	return;
679
}
680
681
static void
682
check_hidden (void)
683
{
684
	g_debug("Checking Hidden...");
685
	gboolean hide = FALSE;
686
	if (launcherList_count() == 0) {
687
		g_debug("\tZero Launchers");
688
		/* If we don't have visible launchers we need to look more */
689
		if (g_list_length(serverList) == 0) {
690
			g_debug("\tZero Applications");
691
			hide = TRUE;	
692
		}
693
	}
694
695
	message_service_dbus_set_icon(dbus_interface, hide);
696
	return;
697
}
698
1.1.6 by Ted Gould
Import upstream version 0.2.2
699
/* This function takes care of putting the menu in the right order.
700
   It basically it rebuilds the order by looking through all the
701
   applications and launchers and puts them in the right place.  The
702
   menu functions will handle the cases where they don't move so this
703
   is a good way to ensure everything is right. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
704
static void
705
resort_menu (DbusmenuMenuitem * menushell)
706
{
707
	guint position = 0;
708
	GList * serverentry;
709
	GList * launcherentry = launcherList;
1.1.6 by Ted Gould
Import upstream version 0.2.2
710
	DbusmenuMenuitem * last_separator = NULL;
1.1.5 by Ted Gould
Import upstream version 0.2.0
711
712
	g_debug("Reordering Menu:");
713
714
	for (serverentry = serverList; serverentry != NULL; serverentry = serverentry->next) {
715
		serverList_t * si = (serverList_t *)serverentry->data;
716
		
1.1.6 by Ted Gould
Import upstream version 0.2.2
717
		/* Looking to see if there are any launchers we need to insert
718
		   into the menu structure.  We put as many as we need to. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
719
		if (launcherentry != NULL) {
720
			launcherList_t * li = (launcherList_t *)launcherentry->data;
721
			while (launcherentry != NULL && g_strcmp0(launcher_menu_item_get_name(li->menuitem), app_menu_item_get_name(si->menuitem)) < 0) {
1.1.6 by Ted Gould
Import upstream version 0.2.2
722
				/* Putting the launcher item in */
1.1.5 by Ted Gould
Import upstream version 0.2.0
723
				g_debug("\tMoving launcher '%s' to position %d", launcher_menu_item_get_name(li->menuitem), position);
724
				dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->menuitem), position);
1.1.6 by Ted Gould
Import upstream version 0.2.2
725
				position++;
726
727
				/* Putting the launcher separator in */
728
				g_debug("\tMoving launcher separator to position %d", position);
729
				dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->separator), position);
730
				if (!launcher_menu_item_get_eclipsed(li->menuitem)) {
731
					/* Only clear the visiblity if we're not eclipsed */
732
					dbusmenu_menuitem_property_set(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "true");
733
					last_separator = li->separator;
734
				}
735
				position++;
736
1.1.5 by Ted Gould
Import upstream version 0.2.0
737
				launcherentry = launcherentry->next;
738
				if (launcherentry != NULL) {
739
					li = (launcherList_t *)launcherentry->data;
740
				}
741
			}
742
		}
743
1.1.6 by Ted Gould
Import upstream version 0.2.2
744
		/* Putting the app menu item in */
1.1.5 by Ted Gould
Import upstream version 0.2.0
745
		if (si->menuitem != NULL) {
746
			g_debug("\tMoving app %s to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(si->server), position);
747
			dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(si->menuitem), position);
748
			position++;
749
		}
750
1.1.6 by Ted Gould
Import upstream version 0.2.2
751
		/* Putting all the indicators that are related to this application
752
		   after it. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
753
		GList * imentry;
754
		for (imentry = si->imList; imentry != NULL; imentry = imentry->next) {
755
			imList_t * imi = (imList_t *)imentry->data;
756
757
			if (imi->menuitem != NULL) {
758
				g_debug("\tMoving indicator on %s id %d to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(imi->server), INDICATE_LISTENER_INDICATOR_ID(imi->indicator), position);
759
				dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(imi->menuitem), position);
760
				position++;
761
			}
762
		}
1.1.6 by Ted Gould
Import upstream version 0.2.2
763
764
		/* Lastly putting the separator in */
765
		if (si->separator != NULL) {
766
			g_debug("\tMoving app %s separator to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(si->server), position);
767
			dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(si->separator), position);
768
			dbusmenu_menuitem_property_set(si->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "true");
769
			position++;
770
			last_separator = si->separator;
771
		}
1.1.5 by Ted Gould
Import upstream version 0.2.0
772
	}
773
1.1.6 by Ted Gould
Import upstream version 0.2.2
774
	/* Put any leftover launchers in at the end of the list. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
775
	while (launcherentry != NULL) {
776
		launcherList_t * li = (launcherList_t *)launcherentry->data;
1.1.6 by Ted Gould
Import upstream version 0.2.2
777
778
		/* Putting the launcher in */
1.1.5 by Ted Gould
Import upstream version 0.2.0
779
		g_debug("\tMoving launcher '%s' to position %d", launcher_menu_item_get_name(li->menuitem), position);
780
		dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->menuitem), position);
1.1.6 by Ted Gould
Import upstream version 0.2.2
781
		position++;
782
783
		/* Putting the launcher separator in */
784
		g_debug("\tMoving launcher separator to position %d", position);
785
		dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->separator), position);
786
		if (!launcher_menu_item_get_eclipsed(li->menuitem)) {
787
			/* Only clear the visiblity if we're not eclipsed */
788
			dbusmenu_menuitem_property_set(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "true");
789
			last_separator = li->separator;
790
		}
791
		position++;
792
1.1.5 by Ted Gould
Import upstream version 0.2.0
793
		launcherentry = launcherentry->next;
794
	}
795
1.1.6 by Ted Gould
Import upstream version 0.2.2
796
	if (last_separator != NULL) {
797
		dbusmenu_menuitem_property_set(last_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "false");
798
	} else {
799
		g_warning("No last separator on resort");
800
	}
801
1.1.5 by Ted Gould
Import upstream version 0.2.0
802
	return;
803
}
804
1.1.6 by Ted Gould
Import upstream version 0.2.2
805
/* Responding to a new indicator showing up on the bus.  We
806
   need to create a menuitem for it and start populating the
807
   internal structures to track it. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
808
static void
1.1.6 by Ted Gould
Import upstream version 0.2.2
809
indicator_added (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data)
1.1.5 by Ted Gould
Import upstream version 0.2.0
810
{
811
	DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data);
812
	if (menushell == NULL) {
813
		g_error("Data in callback is not a menushell");
814
		return;
815
	}
816
1.1.6 by Ted Gould
Import upstream version 0.2.2
817
	imList_t * listItem = g_new0(imList_t, 1);
818
	listItem->server = server;
819
	listItem->indicator = indicator;
820
821
	/* Building the IM Menu Item which is a subclass
822
	   of DBus Menuitem */
823
	ImMenuItem * menuitem = im_menu_item_new(listener, server, indicator);
824
	g_object_ref(G_OBJECT(menuitem));
825
	listItem->menuitem = DBUSMENU_MENUITEM(menuitem);
826
827
	/* Looking for a server entry to attach this indicator
828
	   to.  If we can't find one then we have to build one
829
	   and attach the indicator to it. */
830
	serverList_t sl_item_local = {0};
831
	serverList_t * sl_item = NULL;
832
	sl_item_local.server = server;
833
	GList * serverentry = g_list_find_custom(serverList, &sl_item_local, serverList_equal);
834
835
	if (serverentry == NULL) {
836
		/* This sucks, we got an indicator before the server.  I guess
837
		   that's the joy of being asynchronous */
838
		serverList_t * sl_item = g_new0(serverList_t, 1);
839
		sl_item->server = server;
840
		sl_item->menuitem = NULL;
841
		sl_item->imList = NULL;
842
		sl_item->attention = FALSE;
843
		sl_item->count = 0;
844
		sl_item->separator = NULL;
845
846
		serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort);
847
	} else {
848
		sl_item = (serverList_t *)serverentry->data;
849
	}
850
851
	/* Added a this entry into the IM list */
852
	sl_item->imList = g_list_insert_sorted(sl_item->imList, listItem, imList_sort);
853
	listItem->timechange_cb = g_signal_connect(G_OBJECT(menuitem), IM_MENU_ITEM_SIGNAL_TIME_CHANGED, G_CALLBACK(im_time_changed), sl_item);
854
	listItem->attentionchange_cb = g_signal_connect(G_OBJECT(menuitem), IM_MENU_ITEM_SIGNAL_ATTENTION_CHANGED, G_CALLBACK(im_attention_changed), sl_item);
855
856
	/* Check the length of the list.  If we've got more inidactors
857
	   than we allow.  Well.  Someone's gotta pay.  Sorry.  I didn't
858
	   want to do this, but you did it to yourself. */
859
	if (g_list_length(sl_item->imList) > MAX_NUMBER_OF_INDICATORS) {
860
		GList * indicatoritem;
861
		gint count;
862
		for (indicatoritem = sl_item->imList, count = 0; indicatoritem != NULL; indicatoritem = g_list_next(indicatoritem), count++) {
863
			imList_t * im = (imList_t *)indicatoritem->data;
864
			im_menu_item_show(IM_MENU_ITEM(im->menuitem), count < MAX_NUMBER_OF_INDICATORS);
865
		}
866
	}
867
868
	/* Placing the item into the shell.  Look to see if
869
	   we can find our server and slip in there.  Otherwise
870
	   we'll just append. */
871
	menushell_location_t msl;
872
	msl.found = FALSE;
873
	msl.position = 0;
874
	msl.server = server;
875
876
	dbusmenu_menuitem_foreach(DBUSMENU_MENUITEM(menushell), menushell_foreach_cb, &msl);
877
	if (msl.found) {
878
		dbusmenu_menuitem_child_add_position(menushell, DBUSMENU_MENUITEM(menuitem), msl.position);
879
	} else {
880
		g_warning("Unable to find server menu item");
881
		dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem));
882
	}
883
884
	return;
885
}
886
887
/* Process and indicator getting removed from the system.  We
888
   first need to ensure that it's one of ours and figure out
889
   where we put it.  When we find all that out we can go through
890
   the process of removing the effect it had on the system. */
891
static void
892
indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data)
1.1.5 by Ted Gould
Import upstream version 0.2.0
893
{
894
	g_debug("Removing %s %d", INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_INDICATOR_ID(indicator));
895
896
	gboolean removed = FALSE;
897
1.1.6 by Ted Gould
Import upstream version 0.2.2
898
	/* Find the server that was related to this item */
899
	serverList_t sl_item_local = {0};
1.1.5 by Ted Gould
Import upstream version 0.2.0
900
	serverList_t * sl_item = NULL;
901
	sl_item_local.server = server;
902
	GList * serverentry = g_list_find_custom(serverList, &sl_item_local, serverList_equal);
903
	if (serverentry == NULL) {
1.1.6 by Ted Gould
Import upstream version 0.2.2
904
		/* We didn't care about that server */
1.1.5 by Ted Gould
Import upstream version 0.2.0
905
		return;
906
	}
907
	sl_item = (serverList_t *)serverentry->data;
908
909
	/* Look in the IM Hash Table */
1.1.6 by Ted Gould
Import upstream version 0.2.2
910
	imList_t listData = {0};
1.1.5 by Ted Gould
Import upstream version 0.2.0
911
	listData.server = server;
912
	listData.indicator = indicator;
913
914
	GList * listItem = g_list_find_custom(sl_item->imList, &listData, imList_equal);
915
	DbusmenuMenuitem * menuitem = NULL;
916
	imList_t * ilt = NULL;
917
	if (listItem != NULL) {
918
		ilt = (imList_t *)listItem->data;
919
		menuitem = ilt->menuitem;
920
	}
921
1.1.6 by Ted Gould
Import upstream version 0.2.2
922
	/* If we found a menu item and an imList_t item then
923
	   we can go ahead and remove it.  Otherwise we can 
924
	   skip this and exit. */
1.1.5 by Ted Gould
Import upstream version 0.2.0
925
	if (!removed && menuitem != NULL) {
926
		sl_item->imList = g_list_remove(sl_item->imList, ilt);
927
		g_signal_handler_disconnect(menuitem, ilt->timechange_cb);
1.1.6 by Ted Gould
Import upstream version 0.2.2
928
		g_signal_handler_disconnect(menuitem, ilt->attentionchange_cb);
1.1.5 by Ted Gould
Import upstream version 0.2.0
929
		g_free(ilt);
930
1.1.6 by Ted Gould
Import upstream version 0.2.2
931
		if (im_menu_item_get_attention(IM_MENU_ITEM(menuitem)) && im_menu_item_shown(IM_MENU_ITEM(menuitem))) {
932
			/* If the removed indicator menu item was asking for
933
			   attention we need to see if this server should still
934
			   be asking for attention. */
935
			server_attention(sl_item);
936
			/* If the server is no longer asking for attention then
937
			   we need to check if the whole system should be. */
938
			if (!sl_item->attention) {
939
				check_attention();
940
			}
941
		}
942
943
		if (im_menu_item_shown(IM_MENU_ITEM(menuitem)) && g_list_length(sl_item->imList) >= MAX_NUMBER_OF_INDICATORS) {
944
			/* In this case we need to show a different indicator
945
			   becasue a shown one has left.  But we're going to be
946
			   easy and set all the values. */
947
			GList * indicatoritem;
948
			gint count;
949
			for (indicatoritem = sl_item->imList, count = 0; indicatoritem != NULL; indicatoritem = g_list_next(indicatoritem), count++) {
950
				imList_t * im = (imList_t *)indicatoritem->data;
951
				im_menu_item_show(IM_MENU_ITEM(im->menuitem), count < MAX_NUMBER_OF_INDICATORS);
952
			}
953
		}
954
955
		/* Hide the item immediately, and then remove it
956
		   which might take a little longer. */
957
		dbusmenu_menuitem_property_set(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE, "false");
1.1.5 by Ted Gould
Import upstream version 0.2.0
958
		dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), menuitem);
959
		removed = TRUE;
960
	}
961
962
	if (!removed) {
963
		g_warning("We were asked to remove %s %d but we didn't.", INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_INDICATOR_ID(indicator));
964
	}
965
966
	return;
967
}
968
969
static void
970
app_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data)
971
{
972
	gchar * directory = (gchar *)user_data;
973
	g_debug("Application directory changed: %s", directory);
974
975
	switch (event_type) {
976
	case G_FILE_MONITOR_EVENT_DELETED: {
977
		gchar * path = g_file_get_path(file);
978
		g_debug("\tDelete: %s", path);
979
		g_idle_add(destroy_launcher, path);
980
		break;
981
	}
982
	case G_FILE_MONITOR_EVENT_CREATED: {
983
		gchar * path = g_file_get_path(file);
984
		g_debug("\tCreate: %s", path);
985
		g_idle_add(build_launcher, path);
986
		break;
987
	}
988
	default:
989
		break;
990
	}
991
992
	return;
993
}
994
995
/* Check to see if a new desktop file causes
996
   any of the launchers to be eclipsed by a running
997
   process */
998
static void
999
check_eclipses (AppMenuItem * ai)
1000
{
1001
	g_debug("Checking eclipsing");
1002
	const gchar * aidesktop = app_menu_item_get_desktop(ai);
1003
	if (aidesktop == NULL) return;
1004
	g_debug("\tApp desktop: %s", aidesktop);
1005
1006
	GList * llitem;
1007
	for (llitem = launcherList; llitem != NULL; llitem = llitem->next) {
1008
		launcherList_t * ll = (launcherList_t *)llitem->data;
1009
		const gchar * lidesktop = launcher_menu_item_get_desktop(ll->menuitem);
1010
		g_debug("\tLauncher desktop: %s", lidesktop);
1011
1012
		if (!g_strcmp0(aidesktop, lidesktop)) {
1013
			launcher_menu_item_set_eclipsed(ll->menuitem, TRUE);
1.1.6 by Ted Gould
Import upstream version 0.2.2
1014
			dbusmenu_menuitem_property_set(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "false");
1.1.5 by Ted Gould
Import upstream version 0.2.0
1015
			break;
1016
		}
1017
	}
1018
1019
	return;
1020
}
1021
1022
/* Remove any eclipses that might have been caused
1023
   by this app item that is now retiring */
1024
static void
1025
remove_eclipses (AppMenuItem * ai)
1026
{
1027
	const gchar * aidesktop = app_menu_item_get_desktop(ai);
1028
	if (aidesktop == NULL) return;
1029
1030
	if (blacklist_check(aidesktop)) return;
1031
1032
	GList * llitem;
1033
	for (llitem = launcherList; llitem != NULL; llitem = llitem->next) {
1034
		launcherList_t * ll = (launcherList_t *)llitem->data;
1035
		const gchar * lidesktop = launcher_menu_item_get_desktop(ll->menuitem);
1036
1037
		if (!g_strcmp0(aidesktop, lidesktop)) {
1038
			launcher_menu_item_set_eclipsed(ll->menuitem, FALSE);
1.1.6 by Ted Gould
Import upstream version 0.2.2
1039
			dbusmenu_menuitem_property_set(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "true");
1.1.5 by Ted Gould
Import upstream version 0.2.0
1040
			break;
1041
		}
1042
	}
1043
1044
	return;
1045
}
1046
1047
/* Remove a launcher from the system.  We need to figure
1048
   out what it's up to! */
1049
static gboolean
1050
destroy_launcher (gpointer data)
1051
{
1052
	gchar * appdirentry = (gchar *)data;
1053
1054
	GList * listitem;
1055
	GList * direntry;
1056
	launcherList_t * li;
1057
	gchar * appdir;
1058
1059
	for (listitem = launcherList; listitem != NULL; listitem = listitem->next) {
1060
		li = (launcherList_t *)listitem->data;
1061
		for (direntry = li->appdiritems; direntry != NULL; direntry = direntry->next) {
1062
			appdir = (gchar *)direntry->data;
1063
			if (!g_strcmp0(appdir, appdirentry)) {
1064
				break;
1065
			}
1066
		}
1067
1068
		if (direntry != NULL) {
1069
			break;
1070
		}
1071
	}
1072
1073
	if (listitem == NULL) {
1074
		g_warning("Removed '%s' by the way of it not seeming to exist anywhere.", appdirentry);
1075
		return FALSE;
1076
	}
1077
1078
	if (g_list_length(li->appdiritems) > 1) {
1079
		/* Just remove this item, and we can move on */
1080
		g_debug("Just removing file entry: %s", appdir);
1081
		li->appdiritems = g_list_remove(li->appdiritems, appdir);
1082
		g_free(appdir);
1083
		return FALSE;
1084
	}
1085
1086
	/* Full Destroy */
1087
	g_free(appdir);
1088
	g_list_free(li->appdiritems);
1089
1090
	if (li->menuitem != NULL) {
1091
		dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(li->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE, "false");
1092
		dbusmenu_menuitem_child_delete(root_menuitem, DBUSMENU_MENUITEM(li->menuitem));
1093
		g_object_unref(G_OBJECT(li->menuitem));
1094
		li->menuitem = NULL;
1095
	}
1096
1097
	launcherList = g_list_remove(launcherList, li);
1098
	g_free(li);
1099
1100
	return FALSE;
1101
}
1102
1103
/* This function turns a specific file into a menu
1104
   item and registers it appropriately with everyone */
1105
static gboolean
1106
build_launcher (gpointer data)
1107
{
1108
	/* Read the file get the data */
1109
	gchar * path = (gchar *)data;
1110
	g_debug("\tpath: %s", path);
1111
	gchar * desktop = NULL;
1112
	
1113
	g_file_get_contents(path, &desktop, NULL, NULL);
1114
1115
	if (desktop == NULL) {
1116
		return FALSE;
1117
	}
1118
1119
	gchar * trimdesktop = pango_trim_string(desktop);
1120
	g_free(desktop);
1121
	g_debug("\tcontents: %s", trimdesktop);
1122
1123
	/* Check to see if we already have a launcher */
1124
	GList * listitem;
1125
	for (listitem = launcherList; listitem != NULL; listitem = listitem->next) {
1126
		launcherList_t * li = (launcherList_t *)listitem->data;
1127
		if (!g_strcmp0(launcher_menu_item_get_desktop(li->menuitem), trimdesktop)) {
1128
			break;
1129
		}
1130
	}
1131
1132
	if (listitem == NULL) {
1133
		/* If not */
1134
		/* Build the item */
1135
		launcherList_t * ll = g_new0(launcherList_t, 1);
1136
		ll->menuitem = launcher_menu_item_new(trimdesktop);
1137
		g_free(trimdesktop);
1138
		ll->appdiritems = g_list_append(NULL, path);
1139
1.1.6 by Ted Gould
Import upstream version 0.2.2
1140
		/* Build a separator */
1141
		ll->separator = dbusmenu_menuitem_new();
1142
		dbusmenu_menuitem_property_set(ll->separator, "type", DBUSMENU_CLIENT_TYPES_SEPARATOR);
1143
1.1.5 by Ted Gould
Import upstream version 0.2.0
1144
		/* Add it to the list */
1145
		launcherList = g_list_insert_sorted(launcherList, ll, launcherList_sort);
1146
1147
		/* Add it to the menu */
1148
		dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(ll->menuitem));
1.1.6 by Ted Gould
Import upstream version 0.2.2
1149
		dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(ll->separator));
1.1.5 by Ted Gould
Import upstream version 0.2.0
1150
1.1.7 by Ted Gould
Import upstream version 0.2.4
1151
		/* If we're in the black list or we've gotten eclipsed
1152
		   by something else, hide the item and the separator. */
1153
		if (blacklist_check(launcher_menu_item_get_desktop(ll->menuitem)) ||
1154
				launcher_menu_item_get_eclipsed(ll->menuitem)) {
1.1.5 by Ted Gould
Import upstream version 0.2.0
1155
			launcher_menu_item_set_eclipsed(ll->menuitem, TRUE);
1.1.6 by Ted Gould
Import upstream version 0.2.2
1156
			dbusmenu_menuitem_property_set(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, "false");
1.1.5 by Ted Gould
Import upstream version 0.2.0
1157
		}
1.1.8 by Ted Gould
Import upstream version 0.2.5
1158
1.1.9 by Ted Gould
Import upstream version 0.2.6
1159
		resort_menu(root_menuitem);
1.1.8 by Ted Gould
Import upstream version 0.2.5
1160
		check_hidden();
1.1.5 by Ted Gould
Import upstream version 0.2.0
1161
	} else {
1162
		/* If so add ourselves */
1163
		launcherList_t * ll = (launcherList_t *)listitem->data;
1164
		ll->appdiritems = g_list_append(ll->appdiritems, path);
1165
	}
1166
1167
	return FALSE;
1168
}
1169
1170
/* This function goes through all the launchers that we're
1171
   supposed to be grabbing and decides to show turn them
1172
   into menu items or not.  It doens't do the work, but it
1173
   makes the decision. */
1174
static gboolean
1175
build_launchers (gpointer data)
1176
{
1177
	gchar * directory = (gchar *)data;
1178
1179
	if (!g_file_test(directory, G_FILE_TEST_IS_DIR)) {
1180
		return FALSE;
1181
	}
1182
1183
	GFile * filedir = g_file_new_for_path(directory);
1184
	GFileMonitor * dirmon = g_file_monitor_directory(filedir, G_FILE_MONITOR_NONE, NULL, NULL);
1185
	if (dirmon != NULL) {
1186
		g_signal_connect(G_OBJECT(dirmon), "changed", G_CALLBACK(app_dir_changed), directory);
1187
	}
1188
1189
	GError * error = NULL;
1190
	GDir * dir = g_dir_open(directory, 0, &error);
1191
	if (dir == NULL) {
1192
		g_warning("Unable to open system apps directory: %s", error->message);
1193
		g_error_free(error);
1194
		return FALSE;
1195
	}
1196
1197
	const gchar * filename = NULL;
1198
	while ((filename = g_dir_read_name(dir)) != NULL) {
1199
		g_debug("Found file: %s", filename);
1200
		gchar * path = g_build_filename(directory, filename, NULL);
1201
		g_idle_add(build_launcher, path);
1202
	}
1203
1204
	g_dir_close(dir);
1205
	launcherList = g_list_sort(launcherList, launcherList_sort);
1206
	return FALSE;
1207
}
1208
1209
/* Oh, if you don't know what main() is for
1210
   we really shouldn't be talking. */
1211
int
1212
main (int argc, char ** argv)
1213
{
1214
	g_type_init();
1215
1216
	DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
1217
	DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
1218
	GError * error = NULL;
1219
	guint nameret = 0;
1220
1221
	if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_MESSAGES_DBUS_NAME, 0, &nameret, &error)) {
1222
		g_error("Unable to call to request name");
1223
		return 1;
1224
	}
1225
1226
	if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1227
		g_error("Unable to get name");
1228
		return 1;
1229
	}
1230
1.1.7 by Ted Gould
Import upstream version 0.2.4
1231
	/* Setting up i18n and gettext.  Apparently, we need
1232
	   all of these. */
1233
	setlocale (LC_ALL, "");
1234
	bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
1235
	textdomain (GETTEXT_PACKAGE);
14 by Sebastien Bacher
src/messages-service.c: initialise the translations
1236
1.1.5 by Ted Gould
Import upstream version 0.2.0
1237
	dbus_interface = message_service_dbus_new();
1238
1239
	listener = indicate_listener_ref_default();
1240
	serverList = NULL;
1241
1242
	root_menuitem = dbusmenu_menuitem_new();
1243
	DbusmenuServer * server = dbusmenu_server_new(INDICATOR_MESSAGES_DBUS_OBJECT);
1244
	dbusmenu_server_set_root(server, root_menuitem);
1245
1246
	g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_INDICATOR_ADDED, G_CALLBACK(indicator_added), root_menuitem);
1247
	g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_INDICATOR_REMOVED, G_CALLBACK(indicator_removed), root_menuitem);
1248
	g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_SERVER_ADDED, G_CALLBACK(server_added), root_menuitem);
1249
	g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_SERVER_REMOVED, G_CALLBACK(server_removed), root_menuitem);
1250
1251
	g_idle_add(blacklist_init, NULL);
1252
	g_idle_add(build_launchers, SYSTEM_APPS_DIR);
1.1.7 by Ted Gould
Import upstream version 0.2.4
1253
	g_idle_add(build_launchers, SYSTEM_APPS_DIR_OLD);
1.1.5 by Ted Gould
Import upstream version 0.2.0
1254
	gchar * userdir = g_build_filename(g_get_user_config_dir(), USER_APPS_DIR, NULL);
1255
	g_idle_add(build_launchers, userdir);
1256
1257
	mainloop = g_main_loop_new(NULL, FALSE);
1258
	g_main_loop_run(mainloop);
1259
1260
	g_free(userdir);
1261
1262
	return 0;
1263
}