~ubuntu-branches/ubuntu/precise/lxpanel/precise

« back to all changes in this revision

Viewing changes to .pc/fix_position.patch/src/plugins/menu.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2011-10-06 00:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20111006005602-jl06qq45fcxdfr20
Tags: 0.5.8-1ubuntu3
* debian/patches/fix_position.patch:
 - Fix position of the menu (LP: #818869).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software Foundation,
 
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
17
 */
 
18
 
 
19
#include <stdlib.h>
 
20
#include <string.h>
 
21
 
 
22
#include <gdk-pixbuf/gdk-pixbuf.h>
 
23
#include <glib.h>
 
24
#include <glib/gi18n.h>
 
25
 
 
26
#include <menu-cache.h>
 
27
 
 
28
#include <sys/types.h>
 
29
#include <sys/stat.h>
 
30
#include <unistd.h>
 
31
#include <fcntl.h>
 
32
 
 
33
#include "panel.h"
 
34
#include "misc.h"
 
35
#include "plugin.h"
 
36
#include "bg.h"
 
37
#include "menu-policy.h"
 
38
 
 
39
#include "dbg.h"
 
40
 
 
41
#define DEFAULT_MENU_ICON PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png"
 
42
/*
 
43
 * SuxPanel version 0.1
 
44
 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
 
45
 */
 
46
 
 
47
/*
 
48
 * menu style code was taken from suxpanel
 
49
 */
 
50
 
 
51
typedef struct {
 
52
    GtkWidget *menu, *box, *img, *label;
 
53
    char *fname, *caption;
 
54
    gulong handler_id;
 
55
    int iconsize, paneliconsize;
 
56
    GSList *files;
 
57
    gboolean has_system_menu;
 
58
    char* config_data;
 
59
    int sysmenu_pos;
 
60
    char *config_start, *config_end;
 
61
 
 
62
    MenuCache* menu_cache;
 
63
    guint visibility_flags;
 
64
    gpointer reload_notify;
 
65
} menup;
 
66
 
 
67
static guint idle_loader = 0;
 
68
 
 
69
GQuark SYS_MENU_ITEM_ID = 0;
 
70
 
 
71
/* a single-linked list storing all panels */
 
72
extern GSList* all_panels;
 
73
 
 
74
 
 
75
static void
 
76
menu_destructor(Plugin *p)
 
77
{
 
78
    menup *m = (menup *)p->priv;
 
79
 
 
80
    if( G_UNLIKELY( idle_loader ) )
 
81
    {
 
82
        g_source_remove( idle_loader );
 
83
        idle_loader = 0;
 
84
    }
 
85
 
 
86
    if( m->has_system_menu )
 
87
        p->panel->system_menus = g_slist_remove( p->panel->system_menus, p );
 
88
 
 
89
    g_signal_handler_disconnect(G_OBJECT(m->img), m->handler_id);
 
90
    gtk_widget_destroy(m->menu);
 
91
 
 
92
    if( m->menu_cache )
 
93
    {
 
94
        menu_cache_remove_reload_notify(m->menu_cache, m->reload_notify);
 
95
        menu_cache_unref( m->menu_cache );
 
96
    }
 
97
 
 
98
    g_free(m->fname);
 
99
    g_free(m->caption);
 
100
    g_free(m);
 
101
    RET();
 
102
}
 
103
 
 
104
static void
 
105
spawn_app(GtkWidget *widget, gpointer data)
 
106
{
 
107
    GError *error = NULL;
 
108
 
 
109
    ENTER;
 
110
    if (data) {
 
111
        if (! g_spawn_command_line_async(data, &error) ) {
 
112
            ERR("can't spawn %s\nError is %s\n", (char *)data, error->message);
 
113
            g_error_free (error);
 
114
        }
 
115
    }
 
116
    RET();
 
117
}
 
118
 
 
119
 
 
120
static void
 
121
run_command(GtkWidget *widget, void (*cmd)(void))
 
122
{
 
123
    ENTER;
 
124
    cmd();
 
125
    RET();
 
126
}
 
127
 
 
128
static void
 
129
menu_pos(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, GtkWidget *widget)
 
130
{
 
131
    int ox, oy, w, h;
 
132
    Plugin *p;
 
133
#if GTK_CHECK_VERSION(2,18,0)
 
134
    GtkAllocation allocation;
 
135
    gtk_widget_set_allocation(widget, &allocation);
 
136
#endif
 
137
    ENTER;
 
138
    p = g_object_get_data(G_OBJECT(widget), "plugin");
 
139
#if GTK_CHECK_VERSION(2,14,0)
 
140
    gdk_window_get_origin(gtk_widget_get_window(widget), &ox, &oy);
 
141
#else
 
142
    gdk_window_get_origin(widget->window, &ox, &oy);
 
143
#endif
 
144
#if GTK_CHECK_VERSION(2,20,0)
 
145
    GtkRequisition requisition;
 
146
    gtk_widget_get_requisition(GTK_WIDGET(menu), &requisition);
 
147
    w = requisition.width;
 
148
    h = requisition.height;
 
149
 
 
150
#else
 
151
    w = GTK_WIDGET(menu)->requisition.width;
 
152
    h = GTK_WIDGET(menu)->requisition.height;
 
153
#endif
 
154
    if (p->panel->orientation == ORIENT_HORIZ) {
 
155
        *x = ox;
 
156
        if (*x + w > gdk_screen_width())
 
157
#if GTK_CHECK_VERSION(2,18,0)
 
158
            *x = ox + allocation.width - w;
 
159
#else
 
160
            *x = ox + widget->allocation.width - w;
 
161
#endif
 
162
        *y = oy - h;
 
163
        if (*y < 0)
 
164
#if GTK_CHECK_VERSION(2,18,0)
 
165
            *y = oy + allocation.height;
 
166
#else
 
167
            *y = oy + widget->allocation.height;
 
168
#endif
 
169
    } else {
 
170
#if GTK_CHECK_VERSION(2,18,0)
 
171
        *x = ox + allocation.width;
 
172
#else
 
173
        *x = ox + widget->allocation.width;
 
174
#endif
 
175
        if (*x > gdk_screen_width())
 
176
            *x = ox - w;
 
177
        *y = oy;
 
178
        if (*y + h >  gdk_screen_height())
 
179
#if GTK_CHECK_VERSION(2,18,0)
 
180
            *y = oy + allocation.height - h;
 
181
#else
 
182
            *y = oy + widget->allocation.height - h;
 
183
#endif
 
184
    }
 
185
    DBG("widget: x,y=%d,%d  w,h=%d,%d\n", ox, oy,
 
186
#if GTK_CHECK_VERSION(2,18,0)
 
187
          allocation.width, allocation.height );
 
188
#else
 
189
          widget->allocation.width, widget->allocation.height );
 
190
#endif
 
191
    DBG("w-h %d %d\n", w, h);
 
192
    *push_in = TRUE;
 
193
    RET();
 
194
}
 
195
 
 
196
static void on_menu_item( GtkMenuItem* mi, MenuCacheItem* item )
 
197
{
 
198
    lxpanel_launch_app( menu_cache_app_get_exec(MENU_CACHE_APP(item)),
 
199
            NULL, menu_cache_app_get_use_terminal(MENU_CACHE_APP(item)));
 
200
}
 
201
 
 
202
/* load icon when mapping the menu item to speed up */
 
203
static void on_menu_item_map(GtkWidget* mi, MenuCacheItem* item)
 
204
{
 
205
  GtkImage* img = GTK_IMAGE(gtk_image_menu_item_get_image(GTK_IMAGE_MENU_ITEM(mi)));
 
206
    if( img )
 
207
    {
 
208
        if( gtk_image_get_storage_type(img) == GTK_IMAGE_EMPTY )
 
209
        {
 
210
            GdkPixbuf* icon;
 
211
            int w, h;
 
212
            /* FIXME: this is inefficient */
 
213
            gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &w, &h);
 
214
            item = g_object_get_qdata(G_OBJECT(mi), SYS_MENU_ITEM_ID);
 
215
            icon = lxpanel_load_icon(menu_cache_item_get_icon(item), w, h, TRUE);
 
216
            if (icon)
 
217
            {
 
218
                gtk_image_set_from_pixbuf(img, icon);
 
219
                g_object_unref(icon);
 
220
            }
 
221
        }
 
222
    }
 
223
}
 
224
 
 
225
static void on_menu_item_style_set(GtkWidget* mi, GtkStyle* prev, MenuCacheItem* item)
 
226
{
 
227
    /* reload icon */
 
228
    on_menu_item_map(mi, item);
 
229
}
 
230
 
 
231
static void on_add_menu_item_to_desktop(GtkMenuItem* item, MenuCacheApp* app)
 
232
{
 
233
    char* dest;
 
234
    char* src;
 
235
    const char* desktop = g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP);
 
236
    int dir_len = strlen(desktop);
 
237
    int basename_len = strlen(menu_cache_item_get_id(MENU_CACHE_ITEM(app)));
 
238
    int dest_fd;
 
239
 
 
240
    dest = g_malloc( dir_len + basename_len + 6 + 1 + 1 );
 
241
    memcpy(dest, desktop, dir_len);
 
242
    dest[dir_len] = '/';
 
243
    memcpy(dest + dir_len + 1, menu_cache_item_get_id(MENU_CACHE_ITEM(app)), basename_len + 1);
 
244
 
 
245
    /* if the destination file already exists, make a unique name. */
 
246
    if( g_file_test( dest, G_FILE_TEST_EXISTS ) )
 
247
    {
 
248
        memcpy( dest + dir_len + 1 + basename_len - 8 /* .desktop */, "XXXXXX.desktop", 15 );
 
249
        dest_fd = g_mkstemp(dest);
 
250
        if( dest_fd >= 0 )
 
251
            chmod(dest, 0600);
 
252
    }
 
253
    else
 
254
    {
 
255
        dest_fd = creat(dest, 0600);
 
256
    }
 
257
 
 
258
    if( dest_fd >=0 )
 
259
    {
 
260
        char* data;
 
261
        gsize len;
 
262
        src = menu_cache_item_get_file_path(MENU_CACHE_ITEM(app));
 
263
        if( g_file_get_contents(src, &data, &len, NULL) )
 
264
        {
 
265
            write( dest_fd, data, len );
 
266
            g_free(data);
 
267
        }
 
268
        close(dest_fd);
 
269
        g_free(src);
 
270
    }
 
271
    g_free(dest);
 
272
}
 
273
 
 
274
/* TODO: add menu item to panel */
 
275
#if 0
 
276
static void on_add_menu_item_to_panel(GtkMenuItem* item, MenuCacheApp* app)
 
277
{
 
278
    /* Find a penel containing launchbar applet.
 
279
     * The launchbar with most buttons will be choosen if
 
280
     * there are several launchbar applets loaded.
 
281
     */
 
282
    GSList* l;
 
283
    Plugin* lb = NULL;
 
284
    int n_btns = -1;
 
285
 
 
286
    for(l = all_panels; !lb && l; l = l->next)
 
287
    {
 
288
        Panel* panel = (Panel*)l->data;
 
289
        GList* pl;
 
290
        for(pl=panel->plugins; pl; pl = pl->next)
 
291
        {
 
292
            Plugin* plugin = (Plugin*)pl;
 
293
            if( strcmp(plugin->class->type, "launchbar") == 0 )
 
294
            {
 
295
                /* FIXME: should we let the users choose which launcherbar to add the btn? */
 
296
                break;
 
297
#if 0
 
298
                int n = launchbar_get_n_btns(plugin);
 
299
                if( n > n_btns )
 
300
                {
 
301
                    lb = plugin;
 
302
                    n_btns = n;
 
303
                }
 
304
#endif
 
305
            }
 
306
        }
 
307
    }
 
308
 
 
309
    if( ! lb ) /* launchbar is not currently in use */
 
310
    {
 
311
        /* FIXME: add a launchbar plugin to the panel which has a menu, too. */
 
312
    }
 
313
 
 
314
    if( lb )
 
315
    {
 
316
 
 
317
    }
 
318
}
 
319
#endif
 
320
 
 
321
static void on_menu_item_properties(GtkMenuItem* item, MenuCacheApp* app)
 
322
{
 
323
    /* FIXME: if the source desktop is in AppDir other then default
 
324
     * applications dirs, where should we store the user-specific file?
 
325
    */
 
326
    char* ifile = menu_cache_item_get_file_path(MENU_CACHE_ITEM(app));
 
327
    char* ofile = g_build_filename(g_get_user_data_dir(), "applications",
 
328
                                   menu_cache_item_get_file_basename(MENU_CACHE_ITEM(app)), NULL);
 
329
    char* argv[] = {
 
330
        "lxshortcut",
 
331
        "-i",
 
332
        NULL,
 
333
        "-o",
 
334
        NULL,
 
335
        NULL};
 
336
    argv[2] = ifile;
 
337
    argv[4] = ofile;
 
338
    g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL );
 
339
    g_free( ifile );
 
340
    g_free( ofile );
 
341
}
 
342
 
 
343
/* This following function restore_grabs is taken from menu.c of
 
344
 * gnome-panel.
 
345
 */
 
346
/*most of this function stolen from the real gtk_menu_popup*/
 
347
static void restore_grabs(GtkWidget *w, gpointer data)
 
348
{
 
349
    GtkWidget *menu_item = data;
 
350
    GtkMenu *menu = GTK_MENU(gtk_widget_get_parent(menu_item));
 
351
    GtkWidget *xgrab_shell;
 
352
    GtkWidget *parent;
 
353
 
 
354
    /* Find the last viewable ancestor, and make an X grab on it
 
355
    */
 
356
    parent = GTK_WIDGET (menu);
 
357
    xgrab_shell = NULL;
 
358
    while (parent)
 
359
    {
 
360
        gboolean viewable = TRUE;
 
361
        GtkWidget *tmp = parent;
 
362
 
 
363
        while (tmp)
 
364
        {
 
365
            if (!GTK_WIDGET_MAPPED (tmp))
 
366
            {
 
367
                viewable = FALSE;
 
368
                break;
 
369
            }
 
370
            tmp = gtk_widget_get_parent(tmp);
 
371
        }
 
372
 
 
373
        if (viewable)
 
374
            xgrab_shell = parent;
 
375
 
 
376
        parent = gtk_widget_get_parent(parent);
 
377
    }
 
378
 
 
379
    /*only grab if this HAD a grab before*/
 
380
#if GTK_CHECK_VERSION(2,18,0)
 
381
    if (xgrab_shell && (gtk_widget_has_focus(xgrab_shell)))
 
382
#else
 
383
    if (xgrab_shell && (GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
 
384
#endif
 
385
     {
 
386
#if GTK_CHECK_VERSION(2,14,0)
 
387
        if (gdk_pointer_grab (gtk_widget_get_window(xgrab_shell), TRUE,
 
388
#else
 
389
        if (gdk_pointer_grab (xgrab_shell->window, TRUE,
 
390
#endif
 
391
                    GDK_BUTTON_PRESS_MASK |
 
392
                    GDK_BUTTON_RELEASE_MASK |
 
393
                    GDK_ENTER_NOTIFY_MASK |
 
394
                    GDK_LEAVE_NOTIFY_MASK,
 
395
                    NULL, NULL, 0) == 0)
 
396
        {
 
397
#if GTK_CHECK_VERSION(2,14,0)
 
398
            if (gdk_keyboard_grab (gtk_widget_get_window(xgrab_shell), TRUE,
 
399
#else
 
400
            if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
 
401
#endif
 
402
                    GDK_CURRENT_TIME) == 0)
 
403
#if GTK_CHECK_VERSION(2,18,0)
 
404
                gtk_widget_grab_focus (xgrab_shell);
 
405
#else
 
406
                GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
 
407
#endif
 
408
            else
 
409
                gdk_pointer_ungrab (GDK_CURRENT_TIME);
 
410
        }
 
411
    }
 
412
    gtk_grab_add (GTK_WIDGET (menu));
 
413
}
 
414
 
 
415
static gboolean on_menu_button_press(GtkWidget* mi, GdkEventButton* evt, MenuCacheItem* data)
 
416
{
 
417
    if( evt->button == 3 )  /* right */
 
418
    {
 
419
        char* tmp;
 
420
        GtkWidget* item;
 
421
        GtkMenu* p = GTK_MENU(gtk_menu_new());
 
422
 
 
423
        item = gtk_menu_item_new_with_label(_("Add to desktop"));
 
424
        g_signal_connect(item, "activate", G_CALLBACK(on_add_menu_item_to_desktop), data);
 
425
        gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
 
426
 
 
427
        tmp = g_find_program_in_path("lxshortcut");
 
428
        if( tmp )
 
429
        {
 
430
            item = gtk_separator_menu_item_new();
 
431
            gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
 
432
 
 
433
            item = gtk_menu_item_new_with_label(_("Properties"));
 
434
            g_signal_connect(item, "activate", G_CALLBACK(on_menu_item_properties), data);
 
435
            gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
 
436
            g_free(tmp);
 
437
        }
 
438
        g_signal_connect(p, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL);
 
439
        g_signal_connect(p, "deactivate", G_CALLBACK(restore_grabs), mi);
 
440
 
 
441
        gtk_widget_show_all(GTK_WIDGET(p));
 
442
        gtk_menu_popup(p, NULL, NULL, NULL, NULL, 0, evt->time);
 
443
        return TRUE;
 
444
    }
 
445
    return FALSE;
 
446
}
 
447
 
 
448
static GtkWidget* create_item( MenuCacheItem* item )
 
449
{
 
450
    GtkWidget* mi;
 
451
    if( menu_cache_item_get_type(item) == MENU_CACHE_TYPE_SEP )
 
452
        mi = gtk_separator_menu_item_new();
 
453
    else
 
454
    {
 
455
        GtkWidget* img;
 
456
        mi = gtk_image_menu_item_new_with_label( menu_cache_item_get_name(item) );
 
457
        img = gtk_image_new();
 
458
        gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(mi), img );
 
459
        if( menu_cache_item_get_type(item) == MENU_CACHE_TYPE_APP )
 
460
        {
 
461
            gtk_widget_set_tooltip_text( mi, menu_cache_item_get_comment(item) );
 
462
            g_signal_connect( mi, "activate", G_CALLBACK(on_menu_item), item );
 
463
        }
 
464
        g_signal_connect(mi, "map", G_CALLBACK(on_menu_item_map), item);
 
465
        g_signal_connect(mi, "style-set", G_CALLBACK(on_menu_item_style_set), item);
 
466
        g_signal_connect(mi, "button-press-event", G_CALLBACK(on_menu_button_press), item);
 
467
    }
 
468
    gtk_widget_show( mi );
 
469
    g_object_set_qdata_full( G_OBJECT(mi), SYS_MENU_ITEM_ID, menu_cache_item_ref(item), (GDestroyNotify) menu_cache_item_unref );
 
470
    return mi;
 
471
}
 
472
 
 
473
static int load_menu(menup* m, MenuCacheDir* dir, GtkWidget* menu, int pos )
 
474
{
 
475
    GSList * l;
 
476
    /* number of visible entries */
 
477
    gint count = 0;             
 
478
    for( l = menu_cache_dir_get_children(dir); l; l = l->next )
 
479
    {
 
480
        MenuCacheItem* item = MENU_CACHE_ITEM(l->data);
 
481
        
 
482
        gboolean is_visible = ((menu_cache_item_get_type(item) != MENU_CACHE_TYPE_APP) || 
 
483
                               (panel_menu_item_evaluate_visibility(item, m->visibility_flags)));
 
484
        
 
485
        if (is_visible) 
 
486
        {
 
487
            GtkWidget * mi = create_item(item);
 
488
            count++;
 
489
            if (mi != NULL)
 
490
                gtk_menu_shell_insert( (GtkMenuShell*)menu, mi, pos );
 
491
                if( pos >= 0 )
 
492
                    ++pos;
 
493
                /* process subentries */
 
494
                if (menu_cache_item_get_type(item) == MENU_CACHE_TYPE_DIR) 
 
495
                {
 
496
                    GtkWidget* sub = gtk_menu_new();
 
497
                    /*  always pass -1 for position */
 
498
                    gint s_count = load_menu( m, MENU_CACHE_DIR(item), sub, -1 );    
 
499
                    if (s_count) 
 
500
                        gtk_menu_item_set_submenu( GTK_MENU_ITEM(mi), sub );        
 
501
                    else 
 
502
                    {
 
503
                        /* don't keep empty submenus */
 
504
                        gtk_widget_destroy( sub );
 
505
                        gtk_widget_destroy( mi );
 
506
                        if (pos > 0)
 
507
                            pos--;
 
508
                    }
 
509
                }
 
510
        }
 
511
    }
 
512
    return count;
 
513
}
 
514
 
 
515
 
 
516
 
 
517
static gboolean sys_menu_item_has_data( GtkMenuItem* item )
 
518
{
 
519
   return (g_object_get_qdata( G_OBJECT(item), SYS_MENU_ITEM_ID ) != NULL);
 
520
}
 
521
 
 
522
static void unload_old_icons(GtkMenu* menu, GtkIconTheme* theme)
 
523
{
 
524
    GList *children, *child;
 
525
    GtkMenuItem* item;
 
526
    GtkWidget* sub_menu=NULL;
 
527
 
 
528
    children = gtk_container_get_children( GTK_CONTAINER(menu) );
 
529
    for( child = children; child; child = child->next )
 
530
    {
 
531
        item = GTK_MENU_ITEM( child->data );
 
532
        if( sys_menu_item_has_data( item ) )
 
533
        {
 
534
            GtkImage* img;
 
535
            item = GTK_MENU_ITEM( child->data );
 
536
            if( GTK_IS_IMAGE_MENU_ITEM(item) )
 
537
            {
 
538
                img = GTK_IMAGE(gtk_image_menu_item_get_image(GTK_IMAGE_MENU_ITEM(item)));
 
539
                gtk_image_clear(img);
 
540
                if( GTK_WIDGET_MAPPED(img) )
 
541
                    on_menu_item_map(GTK_WIDGET(item),
 
542
                        (MenuCacheItem*)g_object_get_qdata(G_OBJECT(item), SYS_MENU_ITEM_ID) );
 
543
            }
 
544
        }
 
545
        else if( ( sub_menu = gtk_menu_item_get_submenu( item ) ) )
 
546
        {
 
547
            unload_old_icons( GTK_MENU(sub_menu), theme );
 
548
        }
 
549
    }
 
550
    g_list_free( children );
 
551
}
 
552
 
 
553
static void remove_change_handler(gpointer id, GObject* menu)
 
554
{
 
555
    g_signal_handler_disconnect(gtk_icon_theme_get_default(), GPOINTER_TO_INT(id));
 
556
}
 
557
 
 
558
/*
 
559
 * Insert application menus into specified menu
 
560
 * menu: The parent menu to which the items should be inserted
 
561
 * pisition: Position to insert items.
 
562
             Passing -1 in this parameter means append all items
 
563
             at the end of menu.
 
564
 */
 
565
static void sys_menu_insert_items( menup* m, GtkMenu* menu, int position )
 
566
{
 
567
    MenuCacheDir* dir;
 
568
    guint change_handler;
 
569
 
 
570
    if( G_UNLIKELY( SYS_MENU_ITEM_ID == 0 ) )
 
571
        SYS_MENU_ITEM_ID = g_quark_from_static_string( "SysMenuItem" );
 
572
 
 
573
    dir = menu_cache_get_root_dir( m->menu_cache );
 
574
    if(dir)
 
575
        load_menu( m, dir, GTK_WIDGET(menu), position );
 
576
    else /* menu content is empty */
 
577
    {
 
578
        /* add a place holder */
 
579
        GtkWidget* mi = gtk_menu_item_new();
 
580
        g_object_set_qdata( G_OBJECT(mi), SYS_MENU_ITEM_ID, GINT_TO_POINTER(1) );
 
581
        gtk_menu_shell_insert(menu, mi, position);
 
582
    }
 
583
 
 
584
    change_handler = g_signal_connect_swapped( gtk_icon_theme_get_default(), "changed", G_CALLBACK(unload_old_icons), menu );
 
585
    g_object_weak_ref( G_OBJECT(menu), remove_change_handler, GINT_TO_POINTER(change_handler) );
 
586
}
 
587
 
 
588
 
 
589
static void
 
590
reload_system_menu( menup* m, GtkMenu* menu )
 
591
{
 
592
    GList *children, *child;
 
593
    GtkMenuItem* item;
 
594
    GtkWidget* sub_menu;
 
595
    gint idx;
 
596
    gboolean found = FALSE;
 
597
 
 
598
    children = gtk_container_get_children( GTK_CONTAINER(menu) );
 
599
    for( child = children, idx = 0; child; child = child->next, ++idx )
 
600
    {
 
601
        item = GTK_MENU_ITEM( child->data );
 
602
        if( sys_menu_item_has_data( item ) )
 
603
        {
 
604
            do
 
605
            {
 
606
                item = GTK_MENU_ITEM( child->data );
 
607
                child = child->next;
 
608
                gtk_widget_destroy( GTK_WIDGET(item) );
 
609
            }while( child && sys_menu_item_has_data( child->data ) );
 
610
            sys_menu_insert_items( m, menu, idx );
 
611
            if( ! child )
 
612
                break;
 
613
            found = TRUE;
 
614
        }
 
615
        else if( ( sub_menu = gtk_menu_item_get_submenu( item ) ) )
 
616
        {
 
617
            reload_system_menu( m, GTK_MENU(sub_menu) );
 
618
        }
 
619
    }
 
620
    g_list_free( children );
 
621
}
 
622
 
 
623
static void show_menu( GtkWidget* widget, Plugin* p, int btn, guint32 time )
 
624
{
 
625
    menup* m = (menup*)p->priv;
 
626
    gtk_menu_popup(GTK_MENU(m->menu),
 
627
                   NULL, NULL,
 
628
                   (GtkMenuPositionFunc)menu_pos, widget,
 
629
                   btn, time);
 
630
}
 
631
 
 
632
static gboolean
 
633
my_button_pressed(GtkWidget *widget, GdkEventButton *event, Plugin* plugin)
 
634
{
 
635
    ENTER;
 
636
#if GTK_CHECK_VERSION(2,18,0)
 
637
    GtkAllocation allocation;
 
638
    gtk_widget_get_allocation(widget, &allocation);
 
639
#endif
 
640
 
 
641
    /* Standard right-click handling. */
 
642
    if (plugin_button_press_event(widget, event, plugin))
 
643
        return TRUE;
 
644
 
 
645
    if ((event->type == GDK_BUTTON_PRESS)
 
646
#if GTK_CHECK_VERSION(2,18,0)
 
647
          && (event->x >=0 && event->x < allocation.width)
 
648
          && (event->y >=0 && event->y < allocation.height)) {
 
649
#else
 
650
          && (event->x >=0 && event->x < widget->allocation.width)
 
651
          && (event->y >=0 && event->y < widget->allocation.height)) {
 
652
#endif
 
653
        show_menu( widget, plugin, event->button, event->time );
 
654
    }
 
655
    RET(TRUE);
 
656
}
 
657
 
 
658
gboolean show_system_menu( gpointer system_menu )
 
659
{
 
660
    Plugin* p = (Plugin*)system_menu;
 
661
    menup* m = (menup*)p->priv;
 
662
    show_menu( m->img, p, 0, GDK_CURRENT_TIME );
 
663
    return FALSE;
 
664
}
 
665
 
 
666
static GtkWidget *
 
667
make_button(Plugin *p, gchar *fname, gchar *name, GdkColor* tint, GtkWidget *menu)
 
668
{
 
669
    char* title = NULL;
 
670
    menup *m;
 
671
 
 
672
    ENTER;
 
673
    m = (menup *)p->priv;
 
674
    m->menu = menu;
 
675
 
 
676
    if( name )
 
677
    {
 
678
        /* load the name from *.directory file if needed */
 
679
        if( g_str_has_suffix( name, ".directory" ) )
 
680
        {
 
681
            GKeyFile* kf = g_key_file_new();
 
682
            char* dir_file = g_build_filename( "desktop-directories", name, NULL );
 
683
            if( g_key_file_load_from_data_dirs( kf, dir_file, NULL, 0, NULL ) )
 
684
            {
 
685
                title = g_key_file_get_locale_string( kf, "Desktop Entry", "Name", NULL, NULL );
 
686
            }
 
687
            g_free( dir_file );
 
688
            g_key_file_free( kf );
 
689
        }
 
690
        else
 
691
            title = name;
 
692
 
 
693
        m->img = fb_button_new_from_file_with_label(fname, -1, p->panel->icon_size, gcolor2rgb24(tint), TRUE, p->panel, title);
 
694
 
 
695
        if( title != name )
 
696
            g_free( title );
 
697
    }
 
698
    else
 
699
    {
 
700
        m->img = fb_button_new_from_file(fname, -1, p->panel->icon_size, gcolor2rgb24(tint), TRUE);
 
701
    }
 
702
 
 
703
    gtk_widget_show(m->img);
 
704
    gtk_box_pack_start(GTK_BOX(m->box), m->img, FALSE, FALSE, 0);
 
705
 
 
706
    m->handler_id = g_signal_connect (G_OBJECT (m->img), "button-press-event",
 
707
          G_CALLBACK (my_button_pressed), p);
 
708
    g_object_set_data(G_OBJECT(m->img), "plugin", p);
 
709
 
 
710
    RET(m->img);
 
711
}
 
712
 
 
713
 
 
714
static GtkWidget *
 
715
read_item(Plugin *p, char** fp)
 
716
{
 
717
    line s;
 
718
    gchar *name, *fname, *action;
 
719
    GtkWidget *item;
 
720
    menup *m = (menup *)p->priv;
 
721
    Command *cmd_entry = NULL;
 
722
 
 
723
    ENTER;
 
724
    s.len = 256;
 
725
    name = fname = action = NULL;
 
726
 
 
727
    if( fp )
 
728
    {
 
729
        while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
 
730
            if (s.type == LINE_VAR) {
 
731
                if (!g_ascii_strcasecmp(s.t[0], "image"))
 
732
                    fname = expand_tilda(s.t[1]);
 
733
                else if (!g_ascii_strcasecmp(s.t[0], "name"))
 
734
                    name = g_strdup(s.t[1]);
 
735
                else if (!g_ascii_strcasecmp(s.t[0], "action"))
 
736
                    action = g_strdup(s.t[1]);
 
737
                else if (!g_ascii_strcasecmp(s.t[0], "command")) {
 
738
                    Command *tmp;
 
739
 
 
740
                    for (tmp = commands; tmp->name; tmp++) {
 
741
                        if (!g_ascii_strcasecmp(s.t[1], tmp->name)) {
 
742
                            cmd_entry = tmp;
 
743
                            break;
 
744
                        }
 
745
                    }
 
746
                } else {
 
747
                    ERR( "menu/item: unknown var %s\n", s.t[0]);
 
748
                    goto error;
 
749
                }
 
750
            }
 
751
        }
 
752
    }
 
753
    /* menu button */
 
754
    if( cmd_entry ) /* built-in commands */
 
755
    {
 
756
        item = gtk_image_menu_item_new_with_label( _(cmd_entry->disp_name) );
 
757
        g_signal_connect(G_OBJECT(item), "activate", (GCallback)run_command, cmd_entry->cmd);
 
758
    }
 
759
    else
 
760
    {
 
761
        item = gtk_image_menu_item_new_with_label(name ? name : "");
 
762
        if (action) {
 
763
            g_signal_connect(G_OBJECT(item), "activate", (GCallback)spawn_app, action);
 
764
        }
 
765
    }
 
766
    gtk_container_set_border_width(GTK_CONTAINER(item), 0);
 
767
    g_free(name);
 
768
    if (fname) {
 
769
        GtkWidget *img;
 
770
 
 
771
        img = _gtk_image_new_from_file_scaled(fname, m->iconsize, m->iconsize, TRUE);
 
772
        gtk_widget_show(img);
 
773
        gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
 
774
        g_free(fname);
 
775
    }
 
776
    RET(item);
 
777
 
 
778
 error:
 
779
    g_free(fname);
 
780
    g_free(name);
 
781
    g_free(action);
 
782
    RET(NULL);
 
783
}
 
784
 
 
785
static GtkWidget *
 
786
read_separator(Plugin *p, char **fp)
 
787
{
 
788
    line s;
 
789
 
 
790
    ENTER;
 
791
    s.len = 256;
 
792
    if( fp )
 
793
    {
 
794
        while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
 
795
            ERR("menu: error - separator can not have paramteres\n");
 
796
            RET(NULL);
 
797
        }
 
798
    }
 
799
    RET(gtk_separator_menu_item_new());
 
800
}
 
801
 
 
802
static void on_reload_menu( MenuCache* cache, menup* m )
 
803
{
 
804
    /* g_debug("reload system menu!!"); */
 
805
    reload_system_menu( m, GTK_MENU(m->menu) );
 
806
}
 
807
 
 
808
static void
 
809
read_system_menu(GtkMenu* menu, Plugin *p, char** fp)
 
810
{
 
811
    line s;
 
812
    menup *m = (menup *)p->priv;
 
813
 
 
814
    if (m->menu_cache == NULL)
 
815
    {
 
816
        guint32 flags;
 
817
        m->menu_cache = panel_menu_cache_new(&flags);
 
818
        if (m->menu_cache == NULL)
 
819
        {
 
820
            ERR("error loading applications menu");
 
821
            return;
 
822
        }
 
823
        m->visibility_flags = flags;
 
824
        m->reload_notify = menu_cache_add_reload_notify(m->menu_cache, (GFunc) on_reload_menu, m);
 
825
    }
 
826
 
 
827
    s.len = 256;
 
828
    if( fp )
 
829
    {
 
830
        while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
 
831
            ERR("menu: error - system can not have paramteres\n");
 
832
            return;
 
833
        }
 
834
    }
 
835
 
 
836
    sys_menu_insert_items( m, menu, -1 );
 
837
    m->has_system_menu = TRUE;
 
838
 
 
839
    p->panel->system_menus = g_slist_append( p->panel->system_menus, p );
 
840
}
 
841
 
 
842
static void
 
843
read_include(Plugin *p, char **fp)
 
844
{
 
845
    ENTER;
 
846
#if 0
 
847
    gchar *name;
 
848
    line s;
 
849
    menup *m = (menup *)p->priv;
 
850
    /* FIXME: this is disabled */
 
851
    ENTER;
 
852
    s.len = 256;
 
853
    name = NULL;
 
854
    if( fp )
 
855
    {
 
856
        while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
 
857
            if (s.type == LINE_VAR) {
 
858
                if (!g_ascii_strcasecmp(s.t[0], "name"))
 
859
                    name = expand_tilda(s.t[1]);
 
860
                else  {
 
861
                    ERR( "menu/include: unknown var %s\n", s.t[0]);
 
862
                    RET();
 
863
                }
 
864
            }
 
865
        }
 
866
    }
 
867
    if ((fp = fopen(name, "r"))) {
 
868
        LOG(LOG_INFO, "Including %s\n", name);
 
869
        m->files = g_slist_prepend(m->files, fp);
 
870
        p->fp = fp;
 
871
    } else {
 
872
        ERR("Can't include %s\n", name);
 
873
    }
 
874
    if (name) g_free(name);
 
875
#endif
 
876
    RET();
 
877
}
 
878
 
 
879
static GtkWidget *
 
880
read_submenu(Plugin *p, char** fp, gboolean as_item)
 
881
{
 
882
    line s;
 
883
    GtkWidget *mi, *menu;
 
884
    gchar *name, *fname;
 
885
    menup *m = (menup *)p->priv;
 
886
    GdkColor color={0, 0, 36 * 0xffff / 0xff, 96 * 0xffff / 0xff};
 
887
 
 
888
    ENTER;
 
889
 
 
890
    s.len = 256;
 
891
    menu = gtk_menu_new ();
 
892
    gtk_container_set_border_width(GTK_CONTAINER(menu), 0);
 
893
 
 
894
    fname = NULL;
 
895
    name = NULL;
 
896
    while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
 
897
        if (s.type == LINE_BLOCK_START) {
 
898
            mi = NULL;
 
899
            if (!g_ascii_strcasecmp(s.t[0], "item")) {
 
900
                mi = read_item(p, fp);
 
901
            } else if (!g_ascii_strcasecmp(s.t[0], "separator")) {
 
902
                mi = read_separator(p, fp);
 
903
            } else if (!g_ascii_strcasecmp(s.t[0], "system")) {
 
904
                read_system_menu(GTK_MENU(menu), p, fp); /* add system menu items */
 
905
                continue;
 
906
            } else if (!g_ascii_strcasecmp(s.t[0], "menu")) {
 
907
                mi = read_submenu(p, fp, TRUE);
 
908
            } else if (!g_ascii_strcasecmp(s.t[0], "include")) {
 
909
                read_include(p, fp);
 
910
                continue;
 
911
            } else {
 
912
                ERR("menu: unknown block %s\n", s.t[0]);
 
913
                goto error;
 
914
            }
 
915
            if (!mi) {
 
916
                ERR("menu: can't create menu item\n");
 
917
                goto error;
 
918
            }
 
919
            gtk_widget_show(mi);
 
920
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
 
921
        } else if (s.type == LINE_VAR) {
 
922
            m->config_start = *fp;
 
923
            if (!g_ascii_strcasecmp(s.t[0], "image"))
 
924
                fname = expand_tilda(s.t[1]);
 
925
            else if (!g_ascii_strcasecmp(s.t[0], "name"))
 
926
                name = g_strdup(s.t[1]);
 
927
        /* FIXME: tintcolor will not be saved.  */
 
928
            else if (!g_ascii_strcasecmp(s.t[0], "tintcolor"))
 
929
                gdk_color_parse( s.t[1], &color);
 
930
            else {
 
931
                ERR("menu: unknown var %s\n", s.t[0]);
 
932
            }
 
933
        } else if (s.type == LINE_NONE) {
 
934
            if (m->files) {
 
935
                /*
 
936
                  fclose(p->fp);
 
937
                  p->fp = m->files->data;
 
938
                */
 
939
                m->files = g_slist_delete_link(m->files, m->files);
 
940
            }
 
941
        }  else {
 
942
            ERR("menu: illegal in this context %s\n", s.str);
 
943
            goto error;
 
944
        }
 
945
    }
 
946
    if (as_item) {
 
947
        mi = gtk_image_menu_item_new_with_label(name);
 
948
        if (fname) {
 
949
            GtkWidget *img;
 
950
            img = _gtk_image_new_from_file_scaled(fname, m->iconsize, m->iconsize, TRUE);
 
951
            gtk_widget_show(img);
 
952
            gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
 
953
        }
 
954
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
 
955
    } else {
 
956
        m->fname = fname ? g_strdup(fname) : g_strdup( DEFAULT_MENU_ICON );
 
957
        m->caption = name ? g_strdup(name) : NULL;
 
958
        mi = make_button(p, fname, name, &color, menu);
 
959
        RET(mi);
 
960
    }
 
961
 
 
962
    g_free(fname);
 
963
    g_free(name);
 
964
    RET(mi);
 
965
 
 
966
 error:
 
967
    // FIXME: we need to recursivly destroy all child menus and their items
 
968
    gtk_widget_destroy(menu);
 
969
    g_free(fname);
 
970
    g_free(name);
 
971
    RET(NULL);
 
972
}
 
973
 
 
974
static int
 
975
menu_constructor(Plugin *p, char **fp)
 
976
{
 
977
    char *start;
 
978
    menup *m;
 
979
    static char default_config[] =
 
980
        "system {\n"
 
981
        "}\n"
 
982
        "separator {\n"
 
983
        "}\n"
 
984
        "item {\n"
 
985
            "command=run\n"
 
986
        "}\n"
 
987
        "separator {\n"
 
988
        "}\n"
 
989
        "item {\n"
 
990
            "image=gnome-logout\n"
 
991
            "command=logout\n"
 
992
        "}\n"
 
993
        "image=" DEFAULT_MENU_ICON "\n"
 
994
        "}\n";
 
995
    char *config_default = default_config;
 
996
    int iw, ih;
 
997
 
 
998
    m = g_new0(menup, 1);
 
999
    g_return_val_if_fail(m != NULL, 0);
 
1000
    m->fname = NULL;
 
1001
    m->caption = NULL;
 
1002
 
 
1003
    p->priv = m;
 
1004
 
 
1005
    gtk_icon_size_lookup( GTK_ICON_SIZE_MENU, &iw, &ih );
 
1006
    m->iconsize = MAX(iw, ih);
 
1007
 
 
1008
    m->box = gtk_hbox_new(FALSE, 0);
 
1009
    gtk_container_set_border_width(GTK_CONTAINER(m->box), 0);
 
1010
 
 
1011
    if( ! fp )
 
1012
        fp = &config_default;
 
1013
 
 
1014
    m->config_start = start = *fp;
 
1015
    if (!read_submenu(p, fp, FALSE)) {
 
1016
        ERR("menu: plugin init failed\n");
 
1017
        return 0;
 
1018
    }
 
1019
    m->config_end = *fp - 1;
 
1020
    while( *m->config_end != '}' && m->config_end > m->config_start ) {
 
1021
        --m->config_end;
 
1022
    }
 
1023
    if( *m->config_end == '}' )
 
1024
        --m->config_end;
 
1025
 
 
1026
    m->config_data = g_strndup( start, (m->config_end - start) );
 
1027
 
 
1028
    p->pwid = m->box;
 
1029
 
 
1030
    RET(1);
 
1031
 
 
1032
}
 
1033
 
 
1034
static void save_config( Plugin* p, FILE* fp )
 
1035
{
 
1036
    menup* menu = (menup*)p->priv;
 
1037
    int level = 0;
 
1038
    lxpanel_put_str( fp, "name", menu->caption );
 
1039
    lxpanel_put_str( fp, "image", menu->fname );
 
1040
    if( menu->config_data ) {
 
1041
        char** lines = g_strsplit( menu->config_data, "\n", 0 );
 
1042
        char** line;
 
1043
        for( line = lines; *line; ++line ) {
 
1044
            g_strstrip( *line );
 
1045
            if( **line )
 
1046
            {
 
1047
                if( level == 0 )
 
1048
                {
 
1049
                    /* skip image and caption since we already save these two items */
 
1050
                    if( g_str_has_prefix(*line, "image") || g_str_has_prefix(*line, "caption") )
 
1051
                        continue;
 
1052
                }
 
1053
                g_strchomp(*line); /* remove trailing spaces */
 
1054
                if( g_str_has_suffix( *line, "{" ) )
 
1055
                    ++level;
 
1056
                else if( g_str_has_suffix( *line, "}" ) )
 
1057
                    --level;
 
1058
                lxpanel_put_line( fp, *line );
 
1059
            }
 
1060
        }
 
1061
        g_strfreev( lines );
 
1062
    }
 
1063
}
 
1064
 
 
1065
static void apply_config(Plugin* p)
 
1066
{
 
1067
    menup* m = (menup*)p->priv;
 
1068
    if( m->fname )
 
1069
        fb_button_set_from_file( m->img, m->fname, -1, p->panel->icon_size, TRUE );
 
1070
}
 
1071
 
 
1072
static void menu_config( Plugin *p, GtkWindow* parent )
 
1073
{
 
1074
    GtkWidget* dlg;
 
1075
    menup* menu = (menup*)p->priv;
 
1076
    dlg = create_generic_config_dlg( _(p->class->name),
 
1077
                                     GTK_WIDGET(parent),
 
1078
                                    (GSourceFunc) apply_config, (gpointer) p,
 
1079
                                     _("Icon"), &menu->fname, CONF_TYPE_FILE_ENTRY,
 
1080
                                     /* _("Caption"), &menu->caption, CONF_TYPE_STR, */
 
1081
                                     NULL );
 
1082
    gtk_window_present( GTK_WINDOW(dlg) );
 
1083
}
 
1084
 
 
1085
/* Callback when panel configuration changes. */
 
1086
static void menu_panel_configuration_changed(Plugin * p)
 
1087
{
 
1088
    apply_config(p);
 
1089
}
 
1090
 
 
1091
PluginClass menu_plugin_class = {
 
1092
 
 
1093
    PLUGINCLASS_VERSIONING,
 
1094
 
 
1095
    type : "menu",
 
1096
    name : N_("Menu"),
 
1097
    version: "2.0",
 
1098
    description : N_("Application Menu"),
 
1099
 
 
1100
    constructor : menu_constructor,
 
1101
    destructor  : menu_destructor,
 
1102
    config : menu_config,
 
1103
    save : save_config,
 
1104
    panel_configuration_changed : menu_panel_configuration_changed
 
1105
};
 
1106