2
* Copyright (C) 2007 Intel
3
* Copyright (C) 2008 Canonical Ltd
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public
7
* License as published by the Free Software Foundation; either
8
* version 2 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU General Public
16
* License along with this library; if not, write to the
17
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
* Boston, MA 02111-1307, USA.
20
* Authored by Neil Jagdish Patel <njp@o-hand.com>
21
* Neil Jagdish Patel <neil.patel@canonical.com>
30
#define GMENU_I_KNOW_THIS_IS_UNSTABLE 1
31
#include <gmenu-tree.h>
33
#include <libwnck/window.h>
35
#include <clutter/clutter.h>
37
#include "launcher-defines.h"
38
#include "launcher-menu.h"
40
G_DEFINE_TYPE (LauncherMenu, launcher_menu, G_TYPE_OBJECT)
42
#define LAUNCHER_MENU_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE(obj, \
43
LAUNCHER_TYPE_MENU, LauncherMenuPrivate))
45
struct _LauncherMenuPrivate
59
struct _LauncherMenuCategory
69
struct _LauncherMenuApplication
71
LauncherMenuCategory *category;
92
static guint _menu_signals[LAST_SIGNAL] = { 0 };
95
static void tree_changed (GMenuTree *tree, LauncherMenu *menu);
97
/* Utility functions */
99
/* From matchbox-desktop */
101
strip_extension (const char *file)
105
stripped = g_strdup (file);
107
p = strrchr (stripped, '.');
109
(!strcmp (p, ".png") ||
110
!strcmp (p, ".svg") ||
111
!strcmp (p, ".xpm")))
117
/* Gets the pixbuf from a desktop file's icon name. Based on the same function
118
* from matchbox-desktop
121
get_icon (const gchar *name, guint size)
123
static GtkIconTheme *theme = NULL;
124
GdkPixbuf *pixbuf = NULL;
125
GError *error = NULL;
126
gchar *stripped = NULL;
131
theme = gtk_icon_theme_get_default ();
135
pixbuf = gtk_icon_theme_load_icon (theme, "application-x-executable",
140
if (g_path_is_absolute (name))
142
if (g_file_test (name, G_FILE_TEST_EXISTS))
144
pixbuf = gdk_pixbuf_new_from_file_at_scale (name, size, size,
148
/*g_warning ("Error loading icon: %s\n", error->message);*/
149
g_error_free (error);
156
stripped = strip_extension (name);
158
pixbuf = gtk_icon_theme_load_icon (theme,
164
/*g_warning ("Error loading icon: %s\n", error->message);*/
165
g_error_free (error);
169
/* Always try and send back something */
171
pixbuf = gtk_icon_theme_load_icon (theme, "application-x-executable",
174
width = gdk_pixbuf_get_width (pixbuf);
175
height = gdk_pixbuf_get_height (pixbuf);
177
if (width != size || height != size)
179
GdkPixbuf *temp = pixbuf;
180
pixbuf = gdk_pixbuf_scale_simple (temp,
184
g_object_unref (temp);
192
/* Public functions */
194
launcher_menu_get_categories (LauncherMenu *menu)
196
g_return_val_if_fail (LAUNCHER_IS_MENU (menu), NULL);
197
return menu->priv->categories;
201
launcher_menu_get_applications (LauncherMenu *menu)
203
g_return_val_if_fail (LAUNCHER_IS_MENU (menu), NULL);
204
return menu->priv->menu_apps;
208
launcher_menu_category_get_name (LauncherMenuCategory *category)
210
g_return_val_if_fail (category, NULL);
212
return category->name;
216
launcher_menu_category_get_comment (LauncherMenuCategory *category)
218
g_return_val_if_fail (category, NULL);
220
return category->comment;
224
launcher_menu_category_get_icon (LauncherMenuCategory *category)
226
g_return_val_if_fail (category, NULL);
228
return launcher_menu_category_get_pixbuf (category, CAT_ICON_SIZE);
232
launcher_menu_category_get_applications (LauncherMenuCategory *category)
234
g_return_val_if_fail (category, NULL);
236
return category->applications;
241
launcher_menu_category_get_pixbuf (LauncherMenuCategory *category,
246
g_return_val_if_fail (category, NULL);
248
name = category->icon;
250
if (!GDK_IS_PIXBUF (category->pixbuf))
251
category->pixbuf = get_icon (name, CAT_ICON_SIZE);
253
return category->pixbuf;
257
launcher_menu_application_get_name (LauncherMenuApplication *application)
259
g_return_val_if_fail (application, NULL);
261
return application->name;
266
launcher_menu_application_get_comment (LauncherMenuApplication *application)
268
g_return_val_if_fail (application, NULL);
270
return application->comment;
274
launcher_menu_application_get_icon (LauncherMenuApplication *application)
276
g_return_val_if_fail (application, NULL);
278
if (application->pixbuf)
279
return application->pixbuf;
281
return launcher_menu_application_get_pixbuf (application, APP_ICON_SIZE);
284
LauncherMenuCategory*
285
launcher_menu_application_get_category (LauncherMenuApplication *application)
287
g_return_val_if_fail (application, NULL);
289
return application->category;
293
launcher_menu_application_get_pixbuf (LauncherMenuApplication *application,
298
g_return_val_if_fail (application, NULL);
300
name = application->icon;
302
if (!GDK_IS_PIXBUF (application->pixbuf))
303
application->pixbuf = get_icon (name, APP_ICON_SIZE);
305
return application->pixbuf;
309
launcher_menu_application_get_actor (LauncherMenuApplication *application)
311
g_return_val_if_fail (application, NULL);
314
//application->actor = launcher_item_app_new (application);
316
return application->actor;
320
launcher_menu_application_get_exec (LauncherMenuApplication *application)
322
g_return_val_if_fail (application, NULL);
324
return application->exec;
328
launcher_menu_application_get_desktop_filename (LauncherMenuApplication *application)
330
g_return_val_if_fail (application, NULL);
332
return application->path;
336
launcher_menu_application_get_pid (LauncherMenuApplication *application)
338
g_return_val_if_fail (application, -1);
340
return application->pid;
344
launcher_menu_application_set_pid (LauncherMenuApplication *application,
347
g_return_if_fail (application);
349
application->pid = pid;
353
launcher_menu_application_get_window (LauncherMenuApplication *application)
355
g_return_val_if_fail (application, NULL);
357
return application->window;
361
launcher_menu_application_set_window (LauncherMenuApplication *application,
364
g_return_if_fail (application);
366
application->window = window;
371
static LauncherMenuCategory*
372
make_category (LauncherMenu *menu, GMenuTreeDirectory *dir)
374
LauncherMenuPrivate *priv = menu->priv;
375
LauncherMenuCategory *category = NULL;
377
/* if we are refreshing, try searching for the category */
381
const gchar *name = gmenu_tree_directory_get_name (dir);
382
for (l = priv->old_cats; l; l = l->next)
384
LauncherMenuCategory *cat = l->data;
385
if (strcmp (name, cat->name) == 0)
393
g_list_free (category->applications);
394
category->applications = NULL;
395
priv->categories = g_list_append (priv->categories, (gpointer)category);
397
priv->old_cats = g_list_remove (priv->old_cats, category);
402
category = g_slice_new0 (LauncherMenuCategory);
403
category->name = g_strdup (gmenu_tree_directory_get_name (dir));
404
category->comment = g_strdup (gmenu_tree_directory_get_comment (dir));
405
category->icon = g_strdup (gmenu_tree_directory_get_icon (dir));
406
category->applications = NULL;
408
priv->categories = g_list_append (priv->categories, (gpointer)category);
410
g_print ("+%s\n", category->name);
416
* Each application has a pointer to its category, and gets added to the
417
* categories application list and the main applicaton list
420
make_application (LauncherMenu *menu,
421
GMenuTreeEntry *entry,
422
LauncherMenuCategory *category)
424
LauncherMenuPrivate *priv = menu->priv;
425
LauncherMenuApplication *app = NULL;
427
/* if we are refreshing, try searching for the category */
431
const gchar *name = gmenu_tree_entry_get_name (entry);
433
LauncherMenuApplication *application;
434
for (a = priv->old_apps; a; a = a->next)
436
application = a->data;
437
if (strcmp (name, application->name) == 0)
445
app->category = category;
446
category->applications = g_list_append (category->applications, app);
447
priv->menu_apps = g_list_append (priv->menu_apps, app);
449
priv->old_apps = g_list_remove (priv->old_apps, app);
455
app = g_slice_new0 (LauncherMenuApplication);
456
app->name = g_strdup (gmenu_tree_entry_get_name (entry));
457
app->comment = g_strdup (gmenu_tree_entry_get_comment (entry));
458
app->icon = g_strdup (gmenu_tree_entry_get_icon (entry));
459
app->exec = g_strdup (gmenu_tree_entry_get_exec (entry));
460
app->path = g_strdup (gmenu_tree_entry_get_desktop_file_path (entry));
461
app->category = category;
465
category->applications = g_list_append (category->applications,
467
priv->menu_apps = g_list_append (priv->menu_apps, (gpointer)app);
469
g_print ("\t-%s\n", app->name);
473
* Traverse through the root tree, treating each directory as a category, and
474
* each entry as an application. We only want 1st tier categories, so we pass
475
* a 'category' variable to the function, which, if present, blocks the
476
* 2nd tier directory from becoming a new category, and uses it's parent as the
480
load_menu_from_directory (LauncherMenu *menu,
481
GMenuTreeDirectory *dir,
482
LauncherMenuCategory *category)
485
LauncherMenuCategory *root = NULL;
490
list = gmenu_tree_directory_get_contents (dir);
491
for (l = list; l; l = l->next)
493
GMenuTreeItem *item = (GMenuTreeItem*)l->data;
495
switch (gmenu_tree_item_get_type (item))
497
case GMENU_TREE_ITEM_DIRECTORY:
501
load_menu_from_directory (menu,
502
GMENU_TREE_DIRECTORY (item),
504
GMENU_TREE_DIRECTORY (item)));
508
load_menu_from_directory (menu,
509
GMENU_TREE_DIRECTORY (item), category);
511
case GMENU_TREE_ITEM_ENTRY:
513
make_application (menu, GMENU_TREE_ENTRY (item), category);
517
root = make_category (menu, dir);
518
make_application (menu, GMENU_TREE_ENTRY (item), root);
526
gmenu_tree_item_unref (item);
532
load_menu_from_tree (LauncherMenu *menu, const gchar *name)
534
LauncherMenuPrivate *priv;
535
GMenuTreeDirectory *root = NULL;
536
GMenuTree *tree = NULL;
538
g_return_val_if_fail (LAUNCHER_IS_MENU (menu), NULL);
541
tree = gmenu_tree_lookup (name, GMENU_TREE_FLAGS_NONE);
544
g_warning ("Unable to find %s", name);
547
root = gmenu_tree_get_root_directory (tree);
548
load_menu_from_directory (menu, root, NULL);
549
gmenu_tree_item_unref (root);
551
gmenu_tree_add_monitor (tree, (GMenuTreeChangedFunc)tree_changed, menu);
557
_poor_mans_garbage_collection (GList *pixbufs)
561
for (p = pixbufs; p; p = p->next)
563
while (G_IS_OBJECT (p->data))
565
g_debug ("PMGC: Swept one up");
566
g_object_unref (p->data);
569
g_list_free (pixbufs);
575
free_menu (LauncherMenu *menu)
577
LauncherMenuPrivate *priv;
579
GList *pixbufs = NULL;
581
g_return_if_fail (LAUNCHER_IS_MENU (menu));
584
for (l = priv->old_apps; l; l = l->next)
586
LauncherMenuApplication *app = l->data;
589
g_free (app->comment);
594
if (G_IS_OBJECT (app->pixbuf))
596
if (G_OBJECT (app->pixbuf)->ref_count >1)
597
g_object_unref (app->pixbuf);
599
pixbufs = g_list_append (pixbufs, app->pixbuf);
601
g_slice_free (LauncherMenuApplication, app);
604
g_list_free (priv->old_apps);
605
priv->old_apps = NULL;
607
for (l = priv->old_cats; l; l = l->next)
609
LauncherMenuCategory *cat = l->data;
612
g_free (cat->comment);
614
g_list_free (cat->applications);
616
if (G_IS_OBJECT (cat->pixbuf))
618
if (G_OBJECT (cat->pixbuf)->ref_count >1)
619
g_object_unref (cat->pixbuf);
621
pixbufs = g_list_append (pixbufs, cat->pixbuf);
623
g_slice_free (LauncherMenuCategory, cat);
626
g_list_free (priv->old_cats);
627
priv->old_cats = NULL;
629
g_timeout_add (1000, (GSourceFunc)_poor_mans_garbage_collection, pixbufs);
633
list_changed (LauncherMenu *menu)
635
LauncherMenuPrivate *priv;
636
GMenuTreeDirectory *root;
638
g_return_val_if_fail (LAUNCHER_IS_MENU (menu), FALSE);
641
g_debug ("menu changed\n");
643
priv->old_cats = priv->categories;
644
priv->old_apps = priv->menu_apps;
645
priv->categories = priv->menu_apps = NULL;
647
priv->refresh = TRUE;
649
root = gmenu_tree_get_root_directory (priv->app_tree);
650
load_menu_from_directory (menu, root, NULL);
651
gmenu_tree_item_unref (root);
653
priv->refresh = FALSE;
658
root = gmenu_tree_get_root_directory (priv->sys_tree);
659
load_menu_from_directory (menu, root, NULL);
660
gmenu_tree_item_unref (root);
662
g_signal_emit (menu, _menu_signals[MENU_CHANGED], 0);
669
* FIXME: The tree changing should not be destructive. Find a way to make it
670
* work without re-creating the entire menu again.
673
tree_changed (GMenuTree *tree, LauncherMenu *menu)
675
LauncherMenuPrivate *priv;
676
GMenuTreeDirectory *root;
678
g_return_if_fail (LAUNCHER_IS_MENU (menu));
681
root = gmenu_tree_get_root_directory (tree);
682
gmenu_tree_item_unref (root);
688
priv->tag = g_timeout_add (500, (GSourceFunc)list_changed, menu);
692
on_theme_changed (GtkIconTheme *theme, LauncherMenu *menu)
694
LauncherMenuPrivate *priv;
695
GMenuTreeDirectory *root;
697
g_return_if_fail (LAUNCHER_IS_MENU (menu));
700
root = gmenu_tree_get_root_directory (priv->app_tree);
701
gmenu_tree_item_unref (root);
707
priv->tag = g_timeout_add (500, (GSourceFunc)list_changed, menu);
710
/* GObject functions */
712
launcher_menu_dispose (GObject *object)
714
free_menu (LAUNCHER_MENU (object));
716
G_OBJECT_CLASS (launcher_menu_parent_class)->dispose (object);
720
launcher_menu_finalize (GObject *menu)
722
LauncherMenuPrivate *priv;
724
g_return_if_fail (LAUNCHER_IS_MENU (menu));
725
priv = LAUNCHER_MENU (menu)->priv;
727
G_OBJECT_CLASS (launcher_menu_parent_class)->finalize (menu);
732
launcher_menu_class_init (LauncherMenuClass *klass)
734
GObjectClass *obj_class = G_OBJECT_CLASS (klass);
736
obj_class->finalize = launcher_menu_finalize;
737
obj_class->dispose = launcher_menu_dispose;
739
_menu_signals[MENU_CHANGED] =
740
g_signal_new ("menu-changed",
741
G_OBJECT_CLASS_TYPE (obj_class),
743
G_STRUCT_OFFSET (LauncherMenuClass, menu_changed),
745
g_cclosure_marshal_VOID__VOID,
749
g_type_class_add_private (obj_class, sizeof (LauncherMenuPrivate));
753
launcher_menu_init (LauncherMenu *menu)
755
LauncherMenuPrivate *priv;
758
priv = menu->priv = LAUNCHER_MENU_GET_PRIVATE (menu);
760
priv->categories = NULL;
761
priv->menu_apps = NULL;
763
priv->refresh = FALSE;
765
/*priv->app_tree = load_menu_from_tree (menu, "applications.menu");*/
766
/*priv->sys_tree = load_menu_from_tree (menu, "settings.menu");*/
767
priv->app_tree = load_menu_from_tree (menu, PKGDATADIR"/applications.menu");
769
/* React to theme-changed signals */
770
theme = gtk_icon_theme_get_default ();
771
g_signal_connect (theme, "changed",
772
G_CALLBACK (on_theme_changed), menu);
776
launcher_menu_get_default (void)
778
static LauncherMenu *menu = NULL;
781
menu = g_object_new (LAUNCHER_TYPE_MENU,