2
* This file is a part of the Cairo-Dock project
4
* Copyright : (C) see the 'copyright' file.
5
* E-mail : see the 'copyright' file.
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License
9
* as published by the Free Software Foundation; either version 3
10
* of the License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20
/******************************************************************************
22
This file is a part of the cairo-dock program,
23
released under the terms of the GNU General Public License.
25
Adapted from the Gnome-panel for Cairo-Dock by Fabrice Rey (for any bug report, please mail me to fabounet@users.berlios.de)
27
******************************************************************************/
30
#include <cairo-dock.h>
32
#include "applet-struct.h"
33
#include "applet-menu-callbacks.h"
34
#include "applet-util.h"
35
#include "applet-menu.h"
37
static GSList *image_menu_items = NULL;
38
static GHashTable *loaded_icons = NULL;
41
GtkWidget * add_menu_separator (GtkWidget *menu)
45
menuitem = gtk_separator_menu_item_new ();
46
gtk_widget_set_sensitive (menuitem, FALSE);
47
gtk_widget_show (menuitem);
48
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
53
GtkWidget * create_fake_menu (GMenuTreeDirectory *directory)
58
menu = create_empty_menu ();
60
g_object_set_data_full (G_OBJECT (menu),
61
"panel-menu-tree-directory",
62
gmenu_tree_item_ref (directory),
63
(GDestroyNotify) gmenu_tree_item_unref);
65
g_object_set_data (G_OBJECT (menu),
66
"panel-menu-needs-loading",
67
GUINT_TO_POINTER (TRUE));
69
g_signal_connect (menu, "show",
70
G_CALLBACK (submenu_to_display), NULL);
72
idle_id = g_idle_add_full (G_PRIORITY_LOW,
73
submenu_to_display_in_idle,
76
if (myData.iSidFakeMenuIdle != 0)
77
g_source_remove (myData.iSidFakeMenuIdle);
78
myData.iSidFakeMenuIdle = idle_id;
79
g_object_set_data_full (G_OBJECT (menu),
81
GUINT_TO_POINTER (idle_id),
82
remove_submenu_to_display_idle);
84
g_signal_connect (menu, "button_press_event",
85
G_CALLBACK (menu_dummy_button_press_event), NULL);
90
void image_menu_destroy (GtkWidget *image, gpointer data)
92
image_menu_items = g_slist_remove (image_menu_items, image);
96
static void reload_image_menu_items (void)
100
for (l = image_menu_items; l; l = l->next) {
101
GtkWidget *image = l->data;
104
is_mapped = GTK_WIDGET_MAPPED (image);
107
gtk_widget_unmap (image);
109
gtk_image_set_from_pixbuf (GTK_IMAGE (image), NULL);
112
gtk_widget_map (image);
118
remove_pixmap_from_loaded (gpointer data, GObject *where_the_object_was)
122
if (loaded_icons != NULL)
123
g_hash_table_remove (loaded_icons, key);
127
GdkPixbuf * panel_make_menu_icon (GtkIconTheme *icon_theme,
129
const char *fallback,
131
gboolean *long_operation)
137
g_return_val_if_fail (size > 0, NULL);
141
file = panel_find_icon (icon_theme, icon, size);
142
if (file == NULL && fallback != NULL)
143
file = panel_find_icon (icon_theme, fallback, size);
148
if (long_operation != NULL)
149
*long_operation = TRUE;
155
key = g_strdup_printf ("%d:%s", size, file);
157
if (loaded_icons != NULL &&
158
(pb = g_hash_table_lookup (loaded_icons, key)) != NULL) {
160
g_object_ref (G_OBJECT (pb));
164
pb = gdk_pixbuf_new_from_file (file, NULL);
168
width = gdk_pixbuf_get_width (pb);
169
height = gdk_pixbuf_get_height (pb);
171
/* if we want 24 and we get 22, do nothing;
173
if (!(size - 2 <= width && width <= size &&
174
size - 2 <= height && height <= size)) {
177
tmp = gdk_pixbuf_scale_simple (pb, size, size,
178
GDK_INTERP_BILINEAR);
185
/* add icon to the hash table so we don't load it again */
196
(gdk_pixbuf_get_width (pb) != size &&
197
gdk_pixbuf_get_height (pb) != size)) {
204
width = gdk_pixbuf_get_width (pb);
205
height = gdk_pixbuf_get_height (pb);
207
if (height > width) {
208
dest_width = (size * width) / height;
212
dest_height = (size * height) / width;
215
pb2 = gdk_pixbuf_scale_simple (pb, dest_width, dest_height,
216
GDK_INTERP_BILINEAR);
217
g_object_unref (G_OBJECT (pb));
222
if (loaded_icons == NULL)
223
loaded_icons = g_hash_table_new_full
224
(g_str_hash, g_str_equal,
225
(GDestroyNotify) g_free,
226
(GDestroyNotify) g_object_unref);
227
g_hash_table_replace (loaded_icons,
229
g_object_ref (G_OBJECT (pb)));
230
g_object_weak_ref (G_OBJECT (pb),
231
(GWeakNotify) remove_pixmap_from_loaded,
234
/* we didn't load from disk */
235
if (long_operation != NULL)
236
*long_operation = FALSE;
245
void panel_load_menu_image_deferred (GtkWidget *image_menu_item,
246
GtkIconSize icon_size,
247
const char *stock_id,
249
const char *image_filename,
250
const char *fallback_image_filename)
254
int icon_height = PANEL_DEFAULT_MENU_ICON_SIZE;
256
icon = g_new (IconToLoad, 1);
258
gtk_icon_size_lookup (icon_size, NULL, &icon_height);
260
image = gtk_image_new ();
261
image->requisition.width = icon_height;
262
image->requisition.height = icon_height;
264
/* this takes over the floating ref */
265
icon->pixmap = g_object_ref (G_OBJECT (image));
266
gtk_object_sink (GTK_OBJECT (image));
268
icon->stock_id = stock_id;
270
icon->gicon = g_object_ref (gicon);
273
icon->image = g_strdup (image_filename);
274
icon->fallback_image = g_strdup (fallback_image_filename);
275
icon->icon_size = icon_size;
277
gtk_widget_show (image);
279
g_object_set_data_full (G_OBJECT (image_menu_item),
281
g_object_ref (image),
282
(GDestroyNotify) g_object_unref);
284
if (myConfig.bHasIcons)
285
gtk_image_menu_item_set_image (
286
GTK_IMAGE_MENU_ITEM (image_menu_item), image);
288
g_signal_connect_data (image, "map",
289
G_CALLBACK (image_menu_shown), icon,
290
(GClosureNotify) icon_to_load_free, 0);
292
g_signal_connect (image, "destroy",
293
G_CALLBACK (image_menu_destroy), NULL);
295
image_menu_items = g_slist_prepend (image_menu_items, image);
297
GtkWidget * create_submenu_entry (GtkWidget *menu,
298
GMenuTreeDirectory *directory)
302
menuitem = gtk_image_menu_item_new ();
304
panel_load_menu_image_deferred (menuitem,
305
32, //panel_menu_icon_get_size (),
307
gmenu_tree_directory_get_icon (directory),
310
setup_menuitem (menuitem,
311
32, //panel_menu_icon_get_size (),
313
gmenu_tree_directory_get_name (directory));
315
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
317
gtk_widget_show (menuitem);
322
void create_submenu (GtkWidget *menu,
323
GMenuTreeDirectory *directory,
324
GMenuTreeDirectory *alias_directory)
330
menuitem = create_submenu_entry (menu, alias_directory);
332
menuitem = create_submenu_entry (menu, directory);
334
submenu = create_fake_menu (directory);
336
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
339
void create_header (GtkWidget *menu,
340
GMenuTreeHeader *header)
342
GMenuTreeDirectory *directory;
345
directory = gmenu_tree_header_get_directory (header);
346
menuitem = create_submenu_entry (menu, directory);
347
gmenu_tree_item_unref (directory);
349
g_object_set_data_full (G_OBJECT (menuitem),
350
"panel-gmenu-tree.header",
351
gmenu_tree_item_ref (header),
352
(GDestroyNotify) gmenu_tree_item_unref);
354
g_signal_connect (menuitem, "activate",
355
G_CALLBACK (gtk_false), NULL);
358
void create_menuitem (GtkWidget *menu,
359
GMenuTreeEntry *entry,
360
GMenuTreeDirectory *alias_directory)
364
menuitem = gtk_image_menu_item_new ();
366
g_object_set_data_full (G_OBJECT (menuitem),
367
"panel-menu-tree-entry",
368
gmenu_tree_item_ref (entry),
369
(GDestroyNotify) gmenu_tree_item_unref);
372
//FIXME: we should probably use this data when we do dnd or
373
//context menu for this menu item
374
g_object_set_data_full (G_OBJECT (menuitem),
375
"panel-menu-tree-alias-directory",
376
gmenu_tree_item_ref (alias_directory),
377
(GDestroyNotify) gmenu_tree_item_unref);
379
panel_load_menu_image_deferred (menuitem,
380
PANEL_DEFAULT_MENU_ICON_SIZE, //panel_menu_icon_get_size (),
382
alias_directory ? gmenu_tree_directory_get_icon (alias_directory) :
383
gmenu_tree_entry_get_icon (entry),
386
setup_menuitem (menuitem,
387
PANEL_DEFAULT_MENU_ICON_SIZE, //panel_menu_icon_get_size (),
389
alias_directory ? gmenu_tree_directory_get_name (alias_directory) :
390
gmenu_tree_entry_get_name (entry));
392
if ((alias_directory &&
393
gmenu_tree_directory_get_comment (alias_directory)) ||
395
gmenu_tree_entry_get_comment (entry)))
396
panel_util_set_tooltip_text (menuitem,
398
gmenu_tree_directory_get_comment (alias_directory) :
399
gmenu_tree_entry_get_comment (entry));
401
/*g_signal_connect_after (menuitem, "button_press_event",
402
G_CALLBACK (menuitem_button_press_event), NULL);*/
404
//if (!panel_lockdown_get_locked_down ()) {
406
static GtkTargetEntry menu_item_targets[] = {
407
{ "text/uri-list", 0, 0 }
410
gtk_drag_source_set (menuitem,
411
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
412
menu_item_targets, 1,
415
if (gmenu_tree_entry_get_icon (entry) != NULL) {
419
icon = gmenu_tree_entry_get_icon (entry);
420
if (!g_path_is_absolute (icon)) {
421
icon_no_ext = panel_util_icon_remove_extension (icon);
422
gtk_drag_source_set_icon_name (menuitem,
424
g_free (icon_no_ext);
428
g_signal_connect (G_OBJECT (menuitem), "drag_begin",
429
G_CALLBACK (drag_begin_menu_cb), NULL);
430
g_signal_connect (menuitem, "drag_data_get",
431
G_CALLBACK (drag_data_get_menu_cb), entry);
432
g_signal_connect (menuitem, "drag_end",
433
G_CALLBACK (drag_end_menu_cb), NULL);
436
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
438
g_signal_connect (menuitem, "activate",
439
G_CALLBACK (activate_app_def), entry);
441
gtk_widget_show (menuitem);
444
void create_menuitem_from_alias (GtkWidget *menu,
445
GMenuTreeAlias *alias)
447
GMenuTreeItem *aliased_item;
449
aliased_item = gmenu_tree_alias_get_item (alias);
451
switch (gmenu_tree_item_get_type (aliased_item)) {
452
case GMENU_TREE_ITEM_DIRECTORY:
453
create_submenu (menu,
454
GMENU_TREE_DIRECTORY (aliased_item),
455
gmenu_tree_alias_get_directory (alias));
458
case GMENU_TREE_ITEM_ENTRY:
459
create_menuitem (menu,
460
GMENU_TREE_ENTRY (aliased_item),
461
gmenu_tree_alias_get_directory (alias));
468
gmenu_tree_item_unref (aliased_item);
473
image_menuitem_size_request (GtkWidget *menuitem,
474
GtkRequisition *requisition,
477
GtkIconSize icon_size = (GtkIconSize) GPOINTER_TO_INT (data);
481
if (!gtk_icon_size_lookup (icon_size, NULL, &icon_height))
484
/* If we don't have a pixmap for this menuitem
485
* at least make sure its the same height as
487
* This is a bit ugly, since we should keep this in sync with what's in
488
* gtk_menu_item_size_request()
490
req_height = icon_height;
491
req_height += (GTK_CONTAINER (menuitem)->border_width +
492
menuitem->style->ythickness) * 2;
493
requisition->height = MAX (requisition->height, req_height);
495
void setup_menuitem (GtkWidget *menuitem,
496
GtkIconSize icon_size,
504
/* this creates a label with an invisible mnemonic */
505
label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
506
_title = menu_escape_underscores_and_prepend (title);
507
gtk_label_set_text_with_mnemonic (GTK_LABEL (label), _title);
510
gtk_label_set_pattern (GTK_LABEL (label), "");
512
gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), menuitem);
514
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
515
gtk_widget_show (label);
517
gtk_container_add (GTK_CONTAINER (menuitem), label);
520
g_object_set_data_full (G_OBJECT (menuitem),
522
g_object_ref (image),
523
(GDestroyNotify) g_object_unref);
524
gtk_widget_show (image);
525
if (myConfig.bHasIcons)
526
gtk_image_menu_item_set_image (
527
GTK_IMAGE_MENU_ITEM (menuitem), image);
528
} else if (icon_size != GTK_ICON_SIZE_INVALID)
529
g_signal_connect (menuitem, "size_request",
530
G_CALLBACK (image_menuitem_size_request),
531
GINT_TO_POINTER (icon_size));
533
gtk_widget_show (menuitem);
536
GtkWidget * populate_menu_from_directory (GtkWidget *menu,
537
GMenuTreeDirectory *directory)
541
gboolean add_separator;
543
add_separator = (GTK_MENU_SHELL (menu)->children != NULL);
545
items = gmenu_tree_directory_get_contents (directory);
547
for (l = items; l; l = l->next) {
548
GMenuTreeItem *item = l->data;
551
gmenu_tree_item_get_type (item) == GMENU_TREE_ITEM_SEPARATOR) {
552
add_menu_separator (menu);
553
add_separator = FALSE;
556
switch (gmenu_tree_item_get_type (item)) {
557
case GMENU_TREE_ITEM_DIRECTORY:
558
create_submenu (menu, GMENU_TREE_DIRECTORY (item), NULL);
561
case GMENU_TREE_ITEM_ENTRY:
562
create_menuitem (menu, GMENU_TREE_ENTRY (item), NULL);
565
case GMENU_TREE_ITEM_SEPARATOR :
569
case GMENU_TREE_ITEM_ALIAS:
570
create_menuitem_from_alias (menu, GMENU_TREE_ALIAS (item));
573
case GMENU_TREE_ITEM_HEADER:
574
create_header (menu, GMENU_TREE_HEADER (item));
581
gmenu_tree_item_unref (item);
584
g_slist_free (items);
591
void icon_theme_changed (GtkIconTheme *icon_theme,
594
reload_image_menu_items ();
597
GtkWidget * panel_create_menu (void)
600
static gboolean registered_icon_theme_changer = FALSE;
602
if (!registered_icon_theme_changer) {
603
registered_icon_theme_changer = TRUE;
605
g_signal_connect (gtk_icon_theme_get_default (), "changed",
606
G_CALLBACK (icon_theme_changed), NULL);
609
retval = gtk_menu_new ();
611
/*panel_gconf_notify_add_while_alive ("/desktop/gnome/interface/menus_have_icons",
612
(GConfClientNotifyFunc) menus_have_icons_changed,
613
G_OBJECT (retval));*/
615
/*g_signal_connect (retval, "key_press_event",
616
G_CALLBACK (panel_menu_key_press_handler),
621
GtkWidget * create_empty_menu (void)
625
retval = panel_create_menu ();
627
//g_signal_connect (retval, "show", G_CALLBACK (setup_menu_panel), NULL);
629
/* intercept all right button clicks makes sure they don't
630
go to the object itself */
631
g_signal_connect (retval, "button_press_event",
632
G_CALLBACK (menu_dummy_button_press_event), NULL);
637
static void _on_remove_tree (GMenuTree *tree)
639
cd_message ("%s (%x)", __func__, tree);
640
//gmenu_tree_unref (tree);
643
GtkWidget * create_applications_menu (const char *menu_file,
644
const char *menu_path, GtkWidget *parent_menu)
650
menu = (parent_menu ? parent_menu : create_empty_menu ());
652
cd_message ("%s (%s)", __func__, menu_file);
653
tree = gmenu_tree_lookup (menu_file, GMENU_TREE_FLAGS_NONE);
654
cd_debug (" tree : %x", tree);
656
g_object_set_data_full (G_OBJECT (menu),
658
gmenu_tree_ref (tree),
659
(GDestroyNotify) _on_remove_tree);
661
g_object_set_data_full (G_OBJECT (menu),
662
"panel-menu-tree-path",
663
g_strdup (menu_path ? menu_path : "/"),
664
(GDestroyNotify) g_free);
666
g_object_set_data (G_OBJECT (menu),
667
"panel-menu-needs-loading",
668
GUINT_TO_POINTER (TRUE));
670
g_signal_connect (menu, "show",
671
G_CALLBACK (submenu_to_display), NULL);
673
idle_id = g_idle_add_full (G_PRIORITY_LOW,
674
submenu_to_display_in_idle,
677
if (myData.iSidCreateMenuIdle != 0)
678
g_source_remove (myData.iSidCreateMenuIdle);
679
myData.iSidCreateMenuIdle = idle_id;
680
g_object_set_data_full (G_OBJECT (menu),
681
"panel-menu-idle-id",
682
GUINT_TO_POINTER (idle_id),
683
remove_submenu_to_display_idle);
685
g_signal_connect (menu, "button_press_event",
686
G_CALLBACK (menu_dummy_button_press_event), NULL);
688
gmenu_tree_add_monitor (tree,
689
(GMenuTreeChangedFunc) handle_gmenu_tree_changed,
691
g_signal_connect (menu, "destroy",
692
G_CALLBACK (remove_gmenu_tree_monitor), tree);
694
gmenu_tree_unref (tree);
699
GtkWidget * create_main_menu (CairoDockModuleInstance *myApplet)
701
GtkWidget *main_menu;
703
main_menu = create_applications_menu ("applications.menu", NULL, NULL);
704
g_signal_connect (G_OBJECT (main_menu),
706
G_CALLBACK (cairo_dock_delete_menu),
709
g_object_set_data (G_OBJECT (main_menu),
710
"panel-menu-append-callback",
712
g_object_set_data (G_OBJECT (main_menu),
713
"panel-menu-append-callback-data",
716
myData.bIconsLoaded = myConfig.bHasIcons;