~ubuntu-branches/ubuntu/natty/lxpanel/natty

« back to all changes in this revision

Viewing changes to src/plugins/ptk-app-menu.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Lee
  • Date: 2009-05-30 00:48:18 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20090530004818-7cmv8ncb2aloio9o
Tags: 0.4.1+svn20090524-1
* New upstream release (Closes: #516603, #511001, #493242, #486284, 
  #515633, #484606, #495924, #517858)
  - A whole new application menu generated according to 
    freedesktop.org menu spec. (faster and more standard-compliant. 
    has tooltips for menu items)
  - Enhanced "Run" dialog.
  - Auto resize of application launcher buttons when panel size gets 
    changed.
  - The problematic netstat plugin is now turned off by default. It 
    will be moved to a separate project later.
  - Building dynamic panel plugins outside the source tree of lxpanel 
    is now possible.
  - Fix icon display problems under 64-bit systems.
  - Fixes to work with minimization animation provided by window 
    manager.
  - Fix RTL problems of the task list.
  - Launch console applications in terminal properly
  - Add new temperature monitor plugin by Daniel Kesler
  - Size of icons now can automatically be automatically adjusted 
    according to the height of the panel.
* debian/control:
  - Build-depends on libmenu-cache0
  - lxpanel-netstat-plugin become a dummy package to help transition 
    to wicd or network-manage package.
  - Update descriptions. Thanks Phil Miller <phildebian@mailinator.com>
    (Closes: #513009)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
* ptk-app-menu.c
3
 
*
4
 
* Description: Generate menu from desktop files according to the spec on freedesktop.org
5
 
*
6
 
*
7
 
* Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2006
8
 
*
9
 
* Copyright: GNU Lesser General Public License Version 2
10
 
*
11
 
*/
12
 
 
13
 
#include <gtk/gtk.h>
14
 
#include <glib/gi18n.h>
15
 
#include <stdio.h>
16
 
#include <sys/stat.h>
17
 
#include <string.h>
18
 
#include "ptk-app-menu.h"
19
 
 
20
 
/* Compatibility macros for older versions of glib */
21
 
#if ! GLIB_CHECK_VERSION(2, 10, 0)
22
 
/* older versions of glib don't provde g_slice API */
23
 
#define g_slice_alloc(size)         g_malloc(size)
24
 
#define g_slice_alloc0(size)        g_malloc0(size)
25
 
#define g_slice_new(type)           g_new(type, 1)
26
 
#define g_slice_new0(type)          g_new0(type, 1)
27
 
#define g_slice_free(type, mem)     g_free(mem)
28
 
#define g_slice_free1(size, mem)    g_free(mem)
29
 
#endif
30
 
 
31
 
#include "misc.h" /* Misc functions for lxpanel */
32
 
 
33
 
#define ICON_SIZE        18
34
 
 
35
 
GQuark PTK_APP_MENU_ITEM_ID = 0;
36
 
 
37
 
GtkWidget* ptk_app_menu_new();
38
 
 
39
 
static const char desktop_ent[] = "Desktop Entry";
40
 
static const char app_dir_name[] = "applications";
41
 
 
42
 
static time_t* times = NULL;
43
 
 
44
 
 
45
 
typedef struct _CatInfo
46
 
{
47
 
    char* title;
48
 
    char* directory_file;
49
 
    char* icon;
50
 
    const char** sub_cats;
51
 
}CatInfo;
52
 
 
53
 
typedef struct _PtkAppMenuItem
54
 
{
55
 
    char* name;
56
 
    char* icon;
57
 
    char* exec;
58
 
}PtkAppMenuItem;
59
 
 
60
 
static const char* development_cats[]={
61
 
   "Development",
62
 
   "Translation",
63
 
   "Building","Debugger",
64
 
   "IDE",
65
 
   "GUIDesigner",
66
 
   "Profiling",
67
 
   "RevisionControl",
68
 
   "WebDevelopment",
69
 
   NULL
70
 
};
71
 
 
72
 
static const char* office_cats[] = {
73
 
   "Office",
74
 
   "Dictionary",
75
 
   "Chart",
76
 
   "Calendar",
77
 
   "ContactManagement",
78
 
   "Database",
79
 
   NULL
80
 
};
81
 
 
82
 
static const char* graphics_cats[] = {
83
 
   "Graphics",
84
 
   "2DGraphics",
85
 
   "3DGraphics",
86
 
   "VectorGraphics",
87
 
   "RasterGraphics",
88
 
   "Viewer",
89
 
   NULL
90
 
};
91
 
 
92
 
static const char* network_cats[] = {
93
 
   "Network",
94
 
   "Dialup",
95
 
   "Email",
96
 
   "WebBrowser",
97
 
   "InstantMessaging",
98
 
   "IRCClient",
99
 
   "FileTransfer",
100
 
   "News",
101
 
   "P2P",
102
 
   "RemoteAccess",
103
 
   "Telephony",
104
 
   NULL
105
 
};
106
 
 
107
 
static const char* settings_cats[] = {
108
 
   "Settings",
109
 
   "DesktopSettings",
110
 
   "HardwareSettings",
111
 
   "Accessibility",
112
 
   NULL
113
 
};
114
 
 
115
 
static const char* system_cats[] = {
116
 
   "System",
117
 
   "Core",
118
 
   "Security",
119
 
   "PackageManager",
120
 
   NULL
121
 
};
122
 
 
123
 
static const char* audiovideo_cats[] ={
124
 
   "AudioVideo",
125
 
   "Audio",
126
 
   "Video",
127
 
   "Mixer",
128
 
   "Sequencer",
129
 
   "Tuner",
130
 
   "TV",
131
 
   "AudioVideoEditing",
132
 
   "Player",
133
 
   "Recorder",
134
 
   "DiscBurning",
135
 
   "Music",
136
 
   NULL
137
 
};
138
 
 
139
 
static const char* game_cats[] = {
140
 
   "Game",
141
 
   "Amusement",
142
 
   NULL
143
 
};
144
 
 
145
 
static const char* education_cats[] = {
146
 
   "Education",
147
 
   NULL
148
 
};
149
 
 
150
 
static const char* utility_cats[] = {
151
 
   "Utility",
152
 
   NULL
153
 
};
154
 
 
155
 
static const CatInfo known_cats[]=
156
 
{
157
 
    {N_("Other"), "Other", "gnome-other", NULL},
158
 
    {N_("Game"), "Games", "gnome-joystick", game_cats},
159
 
    {N_("Education"), "Education", "gnome-amusements", education_cats},
160
 
    {N_("Development"), "Development", "gnome-devel", development_cats},
161
 
    {N_("Audio & Video"), "Multimedia", "gnome-multimedia", audiovideo_cats},
162
 
    {N_("Graphics"), "Graphics", "gnome-graphics", graphics_cats},
163
 
    {N_("Settings"), "Settings", "gnome-settings", settings_cats},
164
 
    {N_("System Tools"), "System-Tools", "gnome-system", system_cats},
165
 
    {N_("Network"), "Internet", "gnome-globe", network_cats},
166
 
    {N_("Office"), "Office", "gnome-applications", office_cats},
167
 
    {N_("Accessories"), "Accessories", "gnome-util", utility_cats}
168
 
};
169
 
 
170
 
static int find_cat( char** cats )
171
 
{
172
 
    char** cat;
173
 
    for( cat = cats; *cat; ++cat )
174
 
    {
175
 
        int i;
176
 
        /* Skip other */
177
 
        for( i = 1; i < G_N_ELEMENTS(known_cats); ++i )
178
 
        {
179
 
            const char** sub_cats = known_cats[i].sub_cats;
180
 
            while( *sub_cats )
181
 
            {
182
 
                if( 0 == strncmp(*cat, "X-", 2) ) /*  Desktop specific*/
183
 
                    return -1;
184
 
                if( 0 == strcmp( *sub_cats, *cat ) )
185
 
                    return i;
186
 
                ++sub_cats;
187
 
            }
188
 
        }
189
 
    }
190
 
    return -1;
191
 
}
192
 
 
193
 
static void app_dirs_foreach( GFunc func, gpointer user_data );
194
 
 
195
 
static int compare_menu_item_titles( gpointer a, gpointer b )
196
 
{
197
 
    const gchar *title_a, *title_b;
198
 
    title_a = gtk_label_get_text( GTK_LABEL(gtk_bin_get_child(GTK_BIN(a))) );
199
 
    title_b = gtk_label_get_text( GTK_LABEL(gtk_bin_get_child(GTK_BIN(b))) );
200
 
    return g_ascii_strcasecmp(title_a, title_b);
201
 
}
202
 
 
203
 
static int find_menu_item_by_name( gpointer a, gpointer b )
204
 
{
205
 
    PtkAppMenuItem* data = g_object_get_qdata( G_OBJECT(a), PTK_APP_MENU_ITEM_ID );
206
 
    const char* name = (char*)b;
207
 
    return strcmp(data->name, name);
208
 
}
209
 
 
210
 
/* Moved to misc.c of lxpanel to be used in other plugins */
211
 
#if 0
212
 
static char* translate_exec( const char* exec, const char* icon,
213
 
                             const char* title, const char* fpath )
214
 
{
215
 
    GString* cmd = g_string_sized_new( 256 );
216
 
    for( ; *exec; ++exec )
217
 
    {
218
 
        if( G_UNLIKELY(*exec == '%') )
219
 
        {
220
 
            ++exec;
221
 
            if( !*exec )
222
 
                break;
223
 
            switch( *exec )
224
 
            {
225
 
            case 'c':
226
 
                g_string_append( cmd, title );
227
 
                break;
228
 
            case 'i':
229
 
                if( icon )
230
 
                {
231
 
                    g_string_append( cmd, "--icon " );
232
 
                    g_string_append( cmd, icon );
233
 
                }
234
 
                break;
235
 
            case 'k':
236
 
                {
237
 
                    char* uri = g_filename_to_uri( fpath, NULL, NULL );
238
 
                    g_string_append( cmd, uri );
239
 
                    g_free( uri );
240
 
                    break;
241
 
                }
242
 
            case '%':
243
 
                g_string_append_c( cmd, '%' );
244
 
                break;
245
 
            }
246
 
        }
247
 
        else
248
 
            g_string_append_c( cmd, *exec );
249
 
    }
250
 
    return g_string_free( cmd, FALSE );
251
 
}
252
 
#endif
253
 
 
254
 
static char* load_cat_title( GKeyFile* kf, CatInfo* inf )
255
 
{
256
 
    char* ret = NULL;
257
 
    char* fn = g_strconcat( "desktop-directories/", inf->directory_file, ".directory", NULL );
258
 
    if( g_key_file_load_from_data_dirs( kf, fn, NULL, 0, NULL ) )
259
 
        ret = g_key_file_get_locale_string( kf, desktop_ent, "Name", NULL, NULL );
260
 
    g_free( fn );
261
 
    return ret;
262
 
}
263
 
 
264
 
static void  unload_old_icons( GtkWidget* menu )
265
 
{
266
 
    GList* items = gtk_container_get_children( GTK_CONTAINER(menu) );
267
 
    GList* l;
268
 
    for( l = items; l; l = l->next )
269
 
    {
270
 
        GtkWidget* sub_menu = gtk_menu_item_get_submenu( GTK_MENU_ITEM(l->data) );
271
 
        GtkWidget* img;
272
 
        if( ! GTK_IS_IMAGE_MENU_ITEM(l->data) )
273
 
            continue;
274
 
        img = gtk_image_menu_item_get_image( (GtkImageMenuItem*)l->data );
275
 
        if( ! g_object_get_qdata( G_OBJECT(l->data), PTK_APP_MENU_ITEM_ID ) )
276
 
            continue;
277
 
        if( img )
278
 
            gtk_widget_destroy( img );
279
 
        if( sub_menu )
280
 
            unload_old_icons( sub_menu );
281
 
    }
282
 
    g_list_free( items );
283
 
}
284
 
 
285
 
static void on_menu_item_size_request( GtkWidget* item,
286
 
                                       GtkRequisition* req,
287
 
                                       gpointer user_data )
288
 
{
289
 
    int min_height = ICON_SIZE + (GTK_CONTAINER(item)->border_width +
290
 
             item->style->ythickness) * 2;
291
 
    if( req->height < min_height ) {
292
 
        req->height = min_height;
293
 
    }
294
 
    if( req->width < ICON_SIZE )
295
 
       req->width = ICON_SIZE;
296
 
}
297
 
 
298
 
static gboolean on_menu_item_expose( GtkWidget* item,
299
 
                                     GdkEventExpose* evt,
300
 
                                     gpointer user_data )
301
 
{
302
 
    GtkWidget* img;
303
 
    GdkPixbuf* pix;
304
 
    PtkAppMenuItem* data = (PtkAppMenuItem*)user_data;
305
 
    if( !data )
306
 
        return FALSE;
307
 
    if( !GTK_IS_IMAGE_MENU_ITEM(item) )
308
 
    return FALSE;
309
 
    img = GTK_WIDGET(gtk_image_menu_item_get_image((GtkImageMenuItem *) item));
310
 
    if( img )
311
 
        return FALSE;
312
 
    if( G_UNLIKELY(!data) || G_UNLIKELY(!data->icon) )
313
 
        return FALSE;
314
 
    pix = NULL;
315
 
    if( data->icon[0] == '/' )
316
 
    {
317
 
        pix = gdk_pixbuf_new_from_file_at_size(data->icon, ICON_SIZE, ICON_SIZE, NULL);
318
 
    }
319
 
    else
320
 
    {
321
 
        GtkIconInfo* inf;
322
 
        inf = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), data->icon, ICON_SIZE, 0);
323
 
        if( inf )
324
 
        {
325
 
            pix = gdk_pixbuf_new_from_file_at_size( gtk_icon_info_get_filename(inf), ICON_SIZE, ICON_SIZE, NULL);
326
 
            gtk_icon_info_free ( inf );
327
 
        }
328
 
    }
329
 
    if( G_LIKELY(pix) )
330
 
    {
331
 
        img = gtk_image_new_from_pixbuf( pix );
332
 
        if( G_LIKELY(pix) )
333
 
            g_object_unref( pix );
334
 
    }
335
 
    else
336
 
    {
337
 
        img = gtk_image_new();
338
 
        gtk_image_set_pixel_size( GTK_IMAGE(img), ICON_SIZE );
339
 
    }
340
 
    gtk_image_menu_item_set_image( (GtkImageMenuItem *) item, img );
341
 
    return FALSE;
342
 
}
343
 
 
344
 
static void on_app_menu_item_activate( GtkMenuItem* item, PtkAppMenuItem* data )
345
 
{
346
 
    GError* err = NULL;
347
 
    /* FIXME: support startup notification */
348
 
    g_debug("run command: %s", data->exec);
349
 
    if( !g_spawn_command_line_async( data->exec, &err ) )
350
 
    {
351
 
        /* FIXME: show error message */
352
 
        g_error_free( err );
353
 
    }
354
 
}
355
 
 
356
 
static void ptk_app_menu_item_free( PtkAppMenuItem* data )
357
 
{
358
 
    g_free( data->name );
359
 
    g_free( data->icon );
360
 
    g_free( data->exec );
361
 
    g_slice_free( PtkAppMenuItem, data );
362
 
}
363
 
 
364
 
static void do_load_dir( int prefix_len,
365
 
                         const char* path,
366
 
                         GList** sub_menus )
367
 
{
368
 
    GDir* dir = g_dir_open( path, 0, NULL );
369
 
    const char* name;
370
 
    GKeyFile*  file;
371
 
 
372
 
    if( G_UNLIKELY( ! dir ) )
373
 
        return;
374
 
 
375
 
    file = g_key_file_new();
376
 
 
377
 
    while( (name = g_dir_read_name( dir )) )
378
 
    {
379
 
        char* fpath;
380
 
        char **cats;
381
 
        char **only_show_in;
382
 
 
383
 
        if( name[0] =='.' )
384
 
            continue;
385
 
        fpath = g_build_filename( path, name, NULL );
386
 
        if( g_file_test(fpath, G_FILE_TEST_IS_DIR) )
387
 
        {
388
 
            do_load_dir( prefix_len, fpath, sub_menus );
389
 
            g_free( fpath );
390
 
            continue;
391
 
        }
392
 
        if( ! g_str_has_suffix( name, ".desktop" ) )
393
 
        {
394
 
            g_free( fpath );
395
 
            continue;
396
 
        }
397
 
        if( ! g_key_file_load_from_file( file, fpath, 0, NULL ) )
398
 
        {
399
 
            g_free( fpath );
400
 
            continue;
401
 
        }
402
 
        if( g_key_file_get_boolean( file, desktop_ent, "NoDisplay", NULL ) )
403
 
        {
404
 
            g_free( fpath );
405
 
            continue;
406
 
        }
407
 
        only_show_in = g_key_file_get_string_list( file, desktop_ent, "OnlyShowIn", NULL, NULL );
408
 
        if( only_show_in )
409
 
        {
410
 
            g_free( fpath );
411
 
            g_strfreev( only_show_in );
412
 
            continue;
413
 
        }
414
 
        cats = g_key_file_get_string_list( file, desktop_ent, "Categories", NULL, NULL );
415
 
        if( cats )
416
 
        {
417
 
            int i = find_cat( cats );
418
 
            if( i >= 0 )
419
 
            {
420
 
                GtkWidget* menu_item;
421
 
                char *title, *exec, *icon;
422
 
                /* FIXME: processing duplicated items */
423
 
                exec = g_key_file_get_string( file, desktop_ent, "Exec", NULL);
424
 
                if( exec )
425
 
                {
426
 
                    title = g_key_file_get_locale_string( file, desktop_ent, "Name", NULL, NULL);
427
 
                    if( title )
428
 
                    {
429
 
                        PtkAppMenuItem* data;
430
 
                        GList* prev;
431
 
                        prev =g_list_find_custom( sub_menus[i], (fpath + prefix_len),
432
 
                              (GCompareFunc) find_menu_item_by_name );
433
 
                        if( ! prev )
434
 
                        {
435
 
                            menu_item = gtk_image_menu_item_new_with_label( title );
436
 
                            data = g_slice_new0(PtkAppMenuItem);
437
 
                        }
438
 
                        else
439
 
                        {
440
 
                            GtkLabel* label;
441
 
                            menu_item = GTK_WIDGET(prev->data);
442
 
                            label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(menu_item)));
443
 
                            data = (PtkAppMenuItem*)g_object_get_qdata( G_OBJECT(menu_item), PTK_APP_MENU_ITEM_ID );
444
 
                            gtk_label_set_text( label, title );
445
 
                            g_free( data->name );
446
 
                            g_free( data->exec );
447
 
                            g_free( data->icon );
448
 
                        }
449
 
                        data->name = g_strdup( fpath + prefix_len );
450
 
                        data->exec = exec ? translate_exec_to_cmd( exec, data->icon, title, fpath ) : NULL;
451
 
                        g_free( title );
452
 
                        g_signal_connect( menu_item, "expose-event",
453
 
                      G_CALLBACK(on_menu_item_expose), data );
454
 
                        g_signal_connect( menu_item,
455
 
                                          "size-request",
456
 
                      G_CALLBACK(on_menu_item_size_request), data );
457
 
                        icon = g_strdup( g_key_file_get_string( file, desktop_ent, "Icon", NULL) );
458
 
                        if( icon )
459
 
                        {
460
 
                            char* dot = strchr( icon, '.' );
461
 
                            if( icon[0] !='/' && dot )
462
 
                                *dot = '\0';
463
 
                        }
464
 
                        data->icon = icon;
465
 
                        if( !prev )
466
 
                        {
467
 
                            g_signal_connect( menu_item, "activate",
468
 
                          G_CALLBACK(on_app_menu_item_activate), data );
469
 
                            g_object_set_qdata_full( G_OBJECT(menu_item), PTK_APP_MENU_ITEM_ID, data,
470
 
                             (GDestroyNotify) ptk_app_menu_item_free );
471
 
                            sub_menus[i] = g_list_insert_sorted( sub_menus[i],
472
 
                                 (gpointer) menu_item,
473
 
                                 (GCompareFunc) compare_menu_item_titles );
474
 
                        }
475
 
                    } /* if( title ) */
476
 
                    g_free( exec );
477
 
                } /* if( exec ) */
478
 
            }
479
 
            g_strfreev(cats);
480
 
        }
481
 
        g_free( fpath );
482
 
    }
483
 
    g_key_file_free( file );
484
 
    g_dir_close( dir );
485
 
}
486
 
 
487
 
static void load_dir( const char* path, GList** sub_menus )
488
 
{
489
 
    do_load_dir( strlen( path ) + 1, path, sub_menus );
490
 
}
491
 
 
492
 
#if defined( PTK_APP_MENU_DEMO )
493
 
static GtkWidget* app_menu = NULL;
494
 
static void on_menu( GtkWidget* btn, gpointer user_data )
495
 
{
496
 
    if( ptk_app_menu_need_reload() )
497
 
    {
498
 
        if( app_menu )
499
 
            gtk_widget_destroy( app_menu );
500
 
        app_menu = ptk_app_menu_new();
501
 
    }
502
 
    else if( !app_menu )
503
 
        app_menu = ptk_app_menu_new();
504
 
    gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, 0, 0 );
505
 
}
506
 
#endif
507
 
 
508
 
static void on_app_menu_destroy( gpointer user_data, GObject* menu )
509
 
{
510
 
    g_signal_handler_disconnect( gtk_icon_theme_get_default(),
511
 
                                 GPOINTER_TO_INT(user_data) );
512
 
}
513
 
 
514
 
gboolean ptk_app_menu_item_has_data( GtkMenuItem* item )
515
 
{
516
 
   return (g_object_get_qdata( G_OBJECT(item), PTK_APP_MENU_ITEM_ID ) != NULL);
517
 
}
518
 
 
519
 
/*
520
 
 * Insert application menus into specified menu
521
 
 * menu: The parent menu to which the items should be inserted
522
 
 * pisition: Position to insert items.
523
 
             Passing -1 in this parameter means append all items
524
 
             at the end of menu.
525
 
 */
526
 
void ptk_app_menu_insert_items( GtkMenu* menu, int position )
527
 
{
528
 
   GList* sub_menus[ G_N_ELEMENTS(known_cats) ] = {0};
529
 
   int i;
530
 
   GList *sub_items, *l;
531
 
   guint change_handler;
532
 
  GKeyFile* kf;
533
 
 
534
 
    if( G_UNLIKELY( PTK_APP_MENU_ITEM_ID == 0 ) )
535
 
        PTK_APP_MENU_ITEM_ID = g_quark_from_static_string( "PtkAppMenuItem" );
536
 
 
537
 
   app_dirs_foreach( (GFunc) load_dir, sub_menus );
538
 
 
539
 
    kf = g_key_file_new();
540
 
 
541
 
   for( i = 0; i < G_N_ELEMENTS(known_cats); ++i )
542
 
   {
543
 
      GtkMenu* sub_menu;
544
 
      GtkWidget* menu_item;
545
 
      PtkAppMenuItem* data;
546
 
      char* title;
547
 
 
548
 
      if( ! (sub_items = sub_menus[i]) )
549
 
         continue;
550
 
      sub_menu = GTK_MENU(gtk_menu_new());
551
 
 
552
 
      for( l = sub_items; l; l = l->next )
553
 
         gtk_menu_shell_append( GTK_MENU_SHELL(sub_menu), GTK_WIDGET(l->data) );
554
 
      g_list_free( sub_items );
555
 
 
556
 
      title = load_cat_title( kf, &known_cats[i] );
557
 
      menu_item = gtk_image_menu_item_new_with_label( title ? title : _(known_cats[i].title) );
558
 
      g_free( title );
559
 
 
560
 
      data = g_slice_new0( PtkAppMenuItem );
561
 
      data->icon = g_strdup(known_cats[i].icon);
562
 
      g_object_set_qdata_full( G_OBJECT(menu_item), PTK_APP_MENU_ITEM_ID, data, (GDestroyNotify) ptk_app_menu_item_free );
563
 
      g_signal_connect( menu_item, "expose-event", G_CALLBACK(on_menu_item_expose), data );
564
 
      g_signal_connect( menu_item, "size-request", G_CALLBACK(on_menu_item_size_request), data );
565
 
      on_menu_item_expose( menu_item, NULL, data );
566
 
      gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu_item), GTK_WIDGET(sub_menu) );
567
 
 
568
 
      if( position == -1 )
569
 
         gtk_menu_shell_append( GTK_MENU_SHELL(menu), menu_item );
570
 
      else
571
 
      {
572
 
         gtk_menu_shell_insert( GTK_MENU_SHELL(menu), menu_item, position );
573
 
         ++position;
574
 
      }
575
 
   }
576
 
 
577
 
   g_key_file_free( kf );
578
 
 
579
 
   gtk_widget_show_all(GTK_WIDGET(menu));
580
 
   change_handler = g_signal_connect_swapped( gtk_icon_theme_get_default(), "changed", G_CALLBACK(unload_old_icons), menu );
581
 
   g_object_weak_ref( G_OBJECT(menu), on_app_menu_destroy, GINT_TO_POINTER(change_handler) );
582
 
}
583
 
 
584
 
GtkWidget* ptk_app_menu_new()
585
 
{
586
 
    GtkWidget* menu;
587
 
    menu = gtk_menu_new();
588
 
    ptk_app_menu_insert_items( GTK_MENU(menu), -1 );
589
 
    return menu;
590
 
}
591
 
 
592
 
static void app_dirs_foreach( GFunc func, gpointer user_data )
593
 
{
594
 
    const char** sys_dirs = (const char**)g_get_system_data_dirs();
595
 
    char* path;
596
 
    int i, len;
597
 
    struct stat dir_stat;
598
 
 
599
 
    len = g_strv_length((gchar **) sys_dirs);
600
 
    if( !times )
601
 
        times = g_new0( time_t, len + 2 );
602
 
    for( i = 0; i < len; ++i )
603
 
    {
604
 
        path = g_build_filename( sys_dirs[i], app_dir_name, NULL );
605
 
        if( stat( path, &dir_stat) == 0 )
606
 
        {
607
 
            times[i] = dir_stat.st_mtime;
608
 
            func( path, user_data );
609
 
        }
610
 
        g_free( path );
611
 
    }
612
 
    path = g_build_filename( g_get_user_data_dir(), app_dir_name, NULL );
613
 
    times[i] = dir_stat.st_mtime;
614
 
    if( stat( path, &dir_stat) == 0 )
615
 
    {
616
 
        times[i] = dir_stat.st_mtime;
617
 
        func( path, user_data );
618
 
    }
619
 
    g_free( path );
620
 
}
621
 
 
622
 
#if defined( PTK_APP_MENU_DEMO )
623
 
int main( int argc, char** argv )
624
 
{
625
 
    gtk_init(&argc, &argv);
626
 
    GtkWidget* window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
627
 
    gtk_window_set_title(GTK_WINDOW( window ), "Show Applications" );
628
 
    GtkWidget* button = gtk_button_new_with_label("Application Menu");
629
 
    g_signal_connect(button, "clicked", G_CALLBACK(on_menu), NULL );
630
 
    gtk_container_add( GTK_CONTAINER(window), button );
631
 
    g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL );
632
 
    gtk_widget_show_all(window);
633
 
    if( app_menu )
634
 
        gtk_widget_destroy( app_menu );
635
 
    gtk_main();
636
 
    return 0;
637
 
}
638
 
#endif
639
 
 
640
 
gboolean ptk_app_menu_need_reload( GtkWidget* menu )
641
 
{
642
 
    const char** sys_dirs = (const char**)g_get_system_data_dirs();
643
 
    char* path;
644
 
    int i, len;
645
 
    struct stat dir_stat;
646
 
 
647
 
    if( !times )
648
 
        return TRUE;
649
 
    len = g_strv_length((gchar **) sys_dirs);
650
 
    for( i = 0; i < len; ++i )
651
 
    {
652
 
        path = g_build_filename( sys_dirs[i], app_dir_name, NULL );
653
 
        if( stat( path, &dir_stat) == 0 )
654
 
        {
655
 
            if( times[i] != dir_stat.st_mtime )
656
 
            {
657
 
                g_free( path );
658
 
                return TRUE;
659
 
            }
660
 
        }
661
 
        g_free( path );
662
 
    }
663
 
    path = g_build_filename( g_get_user_data_dir(), app_dir_name, NULL );
664
 
    if( stat( path, &dir_stat) == 0 )
665
 
    {
666
 
        if( times[i] != dir_stat.st_mtime )
667
 
        {
668
 
            g_free( path );
669
 
            return TRUE;
670
 
        }
671
 
    }
672
 
    g_free( path );
673
 
    return FALSE;
674
 
}
675