Support desktop entry in launchbar.
authorHong Jen Yee (PCMan) <pcman.tw@gmail.com>
Fri, 6 Oct 2006 11:07:51 +0000 (11:07 +0000)
committerHong Jen Yee (PCMan) <pcman.tw@gmail.com>
Fri, 6 Oct 2006 11:07:51 +0000 (11:07 +0000)
Add translation to built-in commands.
Add run dialog to input commands.

14 files changed:
intltool-merge
po/POTFILES.in
po/zh_TW.po
src/Makefile.am
src/Makefile.in
src/configurator.c
src/gtk-run.c [new file with mode: 0644]
src/misc.c
src/misc.h
src/panel.h
src/plugins/launchbar.c
src/plugins/menu.c
src/plugins/ptk-app-menu.c [new file with mode: 0644]
src/plugins/ptk_app_menu.c [deleted file]

index 01345b0..cf9e08e 100755 (executable)
@@ -257,7 +257,7 @@ sub gather_po_files
 sub get_local_charset
 {
     my ($encoding) = @_;
-    my $alias_file = $ENV{"G_CHARSET_ALIAS"} || "/usr/lib/charset.alias";
+    my $alias_file = $ENV{"G_CHARSET_ALIAS"} || "/lib/charset.alias";
 
     # seek character encoding aliases in charset.alias (glib)
 
index 31bb1c9..d4b4948 100644 (file)
@@ -6,11 +6,12 @@ src/gtkbar.c
 src/gtkbgbox.c
 src/panel.c
 src/plugin.c
+src/gtk-run.c
 
 src/plugins/cpu.c
 src/plugins/deskno.c
 src/plugins/launchbar.c
-src/plugins/ptk_app_menu.c
+src/plugins/ptk-app-menu.c
 src/plugins/taskbar.c
 src/plugins/dclock.c
 src/plugins/icons.c
index afd2645..c0a5fc8 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: lxpanel-0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-10-06 01:38+0800\n"
-"PO-Revision-Date: 2006-10-06 01:46+0800\n"
+"POT-Creation-Date: 2006-10-06 19:03+0800\n"
+"PO-Revision-Date: 2006-10-06 19:04+0800\n"
 "Last-Translator: 洪任諭 <pcman.tw@gmail.com>\n"
 "Language-Team: zh_TW <LL@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -18,113 +18,126 @@ msgstr ""
 "X-Poedit-Country: TAIWAN\n"
 "X-Poedit-SourceCharset: iso-8859-1\n"
 
-#: ../src/configurator.c:165
+#: ../src/configurator.c:25
+msgid "Preferences"
+msgstr "偏好設定"
+
+#: ../src/configurator.c:26
+#: ../src/gtk-run.c:108
+msgid "Run"
+msgstr "執行"
+
+#: ../src/configurator.c:27
+msgid "Restart"
+msgstr "重新啟動"
+
+#: ../src/configurator.c:167
 msgid "<b>Position</b>"
 msgstr "<b>位置</b>"
 
 #. Edge
-#: ../src/configurator.c:182
+#: ../src/configurator.c:184
 msgid "Edge:"
 msgstr "吸附邊緣:"
 
-#: ../src/configurator.c:189
-#: ../src/configurator.c:211
+#: ../src/configurator.c:191
+#: ../src/configurator.c:213
 msgid "Left"
 msgstr "左"
 
-#: ../src/configurator.c:190
-#: ../src/configurator.c:213
+#: ../src/configurator.c:192
+#: ../src/configurator.c:215
 msgid "Right"
 msgstr "右"
 
-#: ../src/configurator.c:191
+#: ../src/configurator.c:193
 msgid "Top"
 msgstr "上"
 
-#: ../src/configurator.c:192
+#: ../src/configurator.c:194
 msgid "Bottom"
 msgstr "下"
 
 #. Alignment
-#: ../src/configurator.c:203
+#: ../src/configurator.c:205
 msgid "Alignment:"
 msgstr "排列方式:"
 
-#: ../src/configurator.c:212
+#: ../src/configurator.c:214
 msgid "Center"
 msgstr "置中"
 
 #. Margin
-#: ../src/configurator.c:224
+#: ../src/configurator.c:226
 msgid "Margin:"
 msgstr "留空:"
 
-#: ../src/configurator.c:279
+#: ../src/configurator.c:281
 msgid "<b>Size</b>"
 msgstr "<b>大小</b>"
 
 #. width
-#: ../src/configurator.c:296
+#: ../src/configurator.c:298
 msgid "Width:"
 msgstr "寬度:"
 
-#: ../src/configurator.c:308
+#: ../src/configurator.c:310
 msgid "dynamic"
 msgstr "動態"
 
-#: ../src/configurator.c:309
-#: ../src/configurator.c:334
+#: ../src/configurator.c:311
+#: ../src/configurator.c:336
 msgid "pixels"
 msgstr "像素 (pixels)"
 
-#: ../src/configurator.c:310
+#: ../src/configurator.c:312
 #, c-format
 msgid "% of edge"
 msgstr "% 百分比"
 
 #. height
-#: ../src/configurator.c:322
+#: ../src/configurator.c:324
 msgid "Height:"
 msgstr "高度:"
 
-#: ../src/configurator.c:368
+#: ../src/configurator.c:370
 msgid "<b>Transparency</b>"
 msgstr "<b>透明度</b>"
 
-#: ../src/configurator.c:382
+#: ../src/configurator.c:384
 msgid "Enable Transparency"
 msgstr " 啟用透明效果"
 
 #. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tr_checkb), FALSE);
-#: ../src/configurator.c:388
+#: ../src/configurator.c:390
 msgid "Tint color:"
 msgstr "色彩:"
 
-#: ../src/configurator.c:413
+#: ../src/configurator.c:415
 msgid "<b>Properties</b>"
 msgstr "<b>屬性</b>"
 
-#: ../src/configurator.c:427
+#: ../src/configurator.c:429
 msgid "Set Dock Type"
 msgstr "設定停駐型態"
 
-#: ../src/configurator.c:430
+#: ../src/configurator.c:432
 msgid "Set Strut"
 msgstr ""
 
-#: ../src/configurator.c:461
+#: ../src/configurator.c:463
 msgid "Right-click to get context menu. Drag & Drop to change order."
 msgstr "可以按右鍵使用選單,用拖曳改變順序"
 
-#: ../src/configurator.c:517
+#: ../src/configurator.c:519
 msgid "lxpanel configurator"
 msgstr "lxpanel 偏好設定"
 
-#: ../src/configurator.c:540
+#: ../src/configurator.c:542
 msgid "General"
 msgstr "一般"
 
-#: ../src/configurator.c:545
+#: ../src/configurator.c:547
 msgid "Plugins"
 msgstr "外掛"
 
@@ -183,6 +196,10 @@ msgstr ""
 "詳細資訊請參觀 http://lxpanel.sourceforge.net/\n"
 "\n"
 
+#: ../src/gtk-run.c:118
+msgid "Enter the command you want to execute:"
+msgstr "輸入你要想執行的指令:"
+
 #: ../src/plugins/cpu.c:210
 msgid "Display cpu usage"
 msgstr ""
@@ -192,51 +209,51 @@ msgstr ""
 msgid "Display workspace number, by cmeury@users.sf.net"
 msgstr ""
 
-#: ../src/plugins/launchbar.c:327
+#: ../src/plugins/launchbar.c:364
 msgid "Bar with buttons to launch application"
 msgstr ""
 
-#: ../src/plugins/ptk_app_menu.c:143
+#: ../src/plugins/ptk-app-menu.c:145
 msgid "Other"
 msgstr "其他"
 
-#: ../src/plugins/ptk_app_menu.c:144
+#: ../src/plugins/ptk-app-menu.c:146
 msgid "Game"
 msgstr "遊戲"
 
-#: ../src/plugins/ptk_app_menu.c:145
+#: ../src/plugins/ptk-app-menu.c:147
 msgid "Education"
 msgstr "教育"
 
-#: ../src/plugins/ptk_app_menu.c:146
+#: ../src/plugins/ptk-app-menu.c:148
 msgid "Development"
 msgstr "軟體開發"
 
-#: ../src/plugins/ptk_app_menu.c:147
+#: ../src/plugins/ptk-app-menu.c:149
 msgid "Audio & Video"
 msgstr "影音多媒體"
 
-#: ../src/plugins/ptk_app_menu.c:148
+#: ../src/plugins/ptk-app-menu.c:150
 msgid "Graphics"
 msgstr "繪圖"
 
-#: ../src/plugins/ptk_app_menu.c:149
+#: ../src/plugins/ptk-app-menu.c:151
 msgid "Settings"
 msgstr "設定"
 
-#: ../src/plugins/ptk_app_menu.c:150
+#: ../src/plugins/ptk-app-menu.c:152
 msgid "System Tools"
 msgstr "系統工具"
 
-#: ../src/plugins/ptk_app_menu.c:151
+#: ../src/plugins/ptk-app-menu.c:153
 msgid "Network"
 msgstr "網路"
 
-#: ../src/plugins/ptk_app_menu.c:152
+#: ../src/plugins/ptk-app-menu.c:154
 msgid "Office"
 msgstr "辦公軟體"
 
-#: ../src/plugins/ptk_app_menu.c:153
+#: ../src/plugins/ptk-app-menu.c:155
 msgid "Accessories"
 msgstr "附屬應用程式"
 
@@ -283,7 +300,7 @@ msgstr ""
 
 #: ../src/plugins/wincmd.c:221
 msgid "Left click to iconify all windows. Middle click to shade them"
-msgstr "按左鍵最小化所有視窗 / 鍵折疊所有視窗"
+msgstr "按左鍵最小化所有視窗 / 鍵折疊所有視窗"
 
 #: ../src/plugins/wincmd.c:239
 msgid ""
index 643aae0..2698a7c 100644 (file)
@@ -13,7 +13,7 @@ PLUGINS_SOURCES= \
        plugins/cpu.c \
        plugins/deskno.c \
        plugins/launchbar.c \
-       plugins/ptk_app_menu.c \
+       plugins/ptk-app-menu.c \
        plugins/taskbar.c \
        plugins/dclock.c \
        plugins/icons.c \
@@ -42,7 +42,8 @@ lxpanel_SOURCES = \
        gtkbar.h gtkbar.c \
        gtkbgbox.h gtkbgbox.c \
        panel.c panel.h \
-       plugin.c plugin.h
+       plugin.c plugin.h \
+       gtk-run.c
 
 lxpanel_LDADD = $(PACKAGE_LIBS) $(INTLLIBS)
 
index 028e465..047c66d 100644 (file)
@@ -50,7 +50,7 @@ am__installdirs = "$(DESTDIR)$(bindir)"
 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
 PROGRAMS = $(bin_PROGRAMS)
 am__objects_1 = cpu.$(OBJEXT) deskno.$(OBJEXT) launchbar.$(OBJEXT) \
-       ptk_app_menu.$(OBJEXT) taskbar.$(OBJEXT) dclock.$(OBJEXT) \
+       ptk-app-menu.$(OBJEXT) taskbar.$(OBJEXT) dclock.$(OBJEXT) \
        icons.$(OBJEXT) menu.$(OBJEXT) separator.$(OBJEXT) \
        deskno2.$(OBJEXT) image.$(OBJEXT) pager.$(OBJEXT) \
        space.$(OBJEXT) wincmd.$(OBJEXT)
@@ -59,7 +59,7 @@ am__objects_2 = eggmarshalers.$(OBJEXT) eggtraymanager.$(OBJEXT) \
 am_lxpanel_OBJECTS = misc.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
        bg.$(OBJEXT) configurator.$(OBJEXT) dbg.$(OBJEXT) ev.$(OBJEXT) \
        gtkbar.$(OBJEXT) gtkbgbox.$(OBJEXT) panel.$(OBJEXT) \
-       plugin.$(OBJEXT)
+       plugin.$(OBJEXT) gtk-run.$(OBJEXT)
 lxpanel_OBJECTS = $(am_lxpanel_OBJECTS)
 am__DEPENDENCIES_1 =
 lxpanel_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
@@ -226,7 +226,7 @@ PLUGINS_SOURCES = \
        plugins/cpu.c \
        plugins/deskno.c \
        plugins/launchbar.c \
-       plugins/ptk_app_menu.c \
+       plugins/ptk-app-menu.c \
        plugins/taskbar.c \
        plugins/dclock.c \
        plugins/icons.c \
@@ -255,7 +255,8 @@ lxpanel_SOURCES = \
        gtkbar.h gtkbar.c \
        gtkbgbox.h gtkbgbox.c \
        panel.c panel.h \
-       plugin.c plugin.h
+       plugin.c plugin.h \
+       gtk-run.c
 
 lxpanel_LDADD = $(PACKAGE_LIBS) $(INTLLIBS)
 EXTRA_DIST = \
@@ -338,6 +339,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eggtraymanager.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ev.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fixedtip.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-run.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtkbar.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtkbgbox.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icons.Po@am__quote@
@@ -348,7 +350,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pager.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/panel.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptk_app_menu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptk-app-menu.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/separator.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/space.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/taskbar.Po@am__quote@
@@ -411,19 +413,19 @@ launchbar.obj: plugins/launchbar.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o launchbar.obj `if test -f 'plugins/launchbar.c'; then $(CYGPATH_W) 'plugins/launchbar.c'; else $(CYGPATH_W) '$(srcdir)/plugins/launchbar.c'; fi`
 
-ptk_app_menu.o: plugins/ptk_app_menu.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ptk_app_menu.o -MD -MP -MF "$(DEPDIR)/ptk_app_menu.Tpo" -c -o ptk_app_menu.o `test -f 'plugins/ptk_app_menu.c' || echo '$(srcdir)/'`plugins/ptk_app_menu.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/ptk_app_menu.Tpo" "$(DEPDIR)/ptk_app_menu.Po"; else rm -f "$(DEPDIR)/ptk_app_menu.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='plugins/ptk_app_menu.c' object='ptk_app_menu.o' libtool=no @AMDEPBACKSLASH@
+ptk-app-menu.o: plugins/ptk-app-menu.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ptk-app-menu.o -MD -MP -MF "$(DEPDIR)/ptk-app-menu.Tpo" -c -o ptk-app-menu.o `test -f 'plugins/ptk-app-menu.c' || echo '$(srcdir)/'`plugins/ptk-app-menu.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/ptk-app-menu.Tpo" "$(DEPDIR)/ptk-app-menu.Po"; else rm -f "$(DEPDIR)/ptk-app-menu.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='plugins/ptk-app-menu.c' object='ptk-app-menu.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ptk_app_menu.o `test -f 'plugins/ptk_app_menu.c' || echo '$(srcdir)/'`plugins/ptk_app_menu.c
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ptk-app-menu.o `test -f 'plugins/ptk-app-menu.c' || echo '$(srcdir)/'`plugins/ptk-app-menu.c
 
-ptk_app_menu.obj: plugins/ptk_app_menu.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ptk_app_menu.obj -MD -MP -MF "$(DEPDIR)/ptk_app_menu.Tpo" -c -o ptk_app_menu.obj `if test -f 'plugins/ptk_app_menu.c'; then $(CYGPATH_W) 'plugins/ptk_app_menu.c'; else $(CYGPATH_W) '$(srcdir)/plugins/ptk_app_menu.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/ptk_app_menu.Tpo" "$(DEPDIR)/ptk_app_menu.Po"; else rm -f "$(DEPDIR)/ptk_app_menu.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='plugins/ptk_app_menu.c' object='ptk_app_menu.obj' libtool=no @AMDEPBACKSLASH@
+ptk-app-menu.obj: plugins/ptk-app-menu.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ptk-app-menu.obj -MD -MP -MF "$(DEPDIR)/ptk-app-menu.Tpo" -c -o ptk-app-menu.obj `if test -f 'plugins/ptk-app-menu.c'; then $(CYGPATH_W) 'plugins/ptk-app-menu.c'; else $(CYGPATH_W) '$(srcdir)/plugins/ptk-app-menu.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/ptk-app-menu.Tpo" "$(DEPDIR)/ptk-app-menu.Po"; else rm -f "$(DEPDIR)/ptk-app-menu.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='plugins/ptk-app-menu.c' object='ptk-app-menu.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ptk_app_menu.obj `if test -f 'plugins/ptk_app_menu.c'; then $(CYGPATH_W) 'plugins/ptk_app_menu.c'; else $(CYGPATH_W) '$(srcdir)/plugins/ptk_app_menu.c'; fi`
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ptk-app-menu.obj `if test -f 'plugins/ptk-app-menu.c'; then $(CYGPATH_W) 'plugins/ptk-app-menu.c'; else $(CYGPATH_W) '$(srcdir)/plugins/ptk-app-menu.c'; fi`
 
 taskbar.o: plugins/taskbar.c
 @am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT taskbar.o -MD -MP -MF "$(DEPDIR)/taskbar.Tpo" -c -o taskbar.o `test -f 'plugins/taskbar.c' || echo '$(srcdir)/'`plugins/taskbar.c; \
index ee2b2fb..fc7e85d 100644 (file)
 
 void configure(void);
 void restart(void);
+void gtk_run(void);
 
 command commands[] = {
-    { "configure", configure },
-    { "restart", restart },
+    { "configure", N_("Preferences"), configure },
+    { "run", N_("Run"), gtk_run },
+    { "restart", N_("Restart"), restart },
     { NULL, NULL },
 };
 
diff --git a/src/gtk-run.c b/src/gtk-run.c
new file mode 100644 (file)
index 0000000..34cdcaa
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * gtk-run.c: Little application launcher
+ * Copyright (C) 2006 Hong Jen Tee (PCMan) pcman.tw(AT)gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Compile this program with following command line:
+ * gcc `pkg-config gtk+-2.0 --cflags --libs` gtk-run.c -o gtk-run
+ *
+ */
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+static gboolean setup_auto_complete( gpointer entry )
+{
+    GtkListStore* store;
+    GList *list = NULL, *l;
+    gchar **dirname;
+    gchar **dirnames = g_strsplit( g_getenv("PATH"), ":", 0 );
+    GtkEntryCompletion* comp = gtk_entry_completion_new();
+    gtk_entry_completion_set_minimum_key_length( comp, 2 );
+    gtk_entry_completion_set_inline_completion( comp, TRUE );
+    gtk_entry_completion_set_popup_set_width( comp, TRUE );
+    gtk_entry_completion_set_popup_single_match( comp, FALSE );
+     store = gtk_list_store_new( 1, G_TYPE_STRING );
+
+    for( dirname = dirnames; *dirname; ++dirname )
+    {
+        GDir *dir = g_dir_open( *dirname, 0, NULL );
+        const char *name;
+        int i = 0;
+        if( ! dir )
+            continue;
+        while( name = g_dir_read_name( dir ) )
+        {
+            char* filename = g_build_filename( *dirname, name, NULL );
+            if( g_file_test( filename, G_FILE_TEST_IS_EXECUTABLE ) )
+            {
+                if( !g_list_find_custom( list, name, (GCompareFunc)strcmp ) )
+                    list = g_list_prepend( list, g_strdup( name ) );
+            }
+            g_free( filename );
+        }
+    }
+    g_strfreev( dirnames );
+
+    for( l = list; l; l = l->next )
+    {
+        GtkTreeIter it;
+        gtk_list_store_append( store, &it );
+        gtk_list_store_set( store, &it, 0, l->data, -1 );
+        g_free( l->data );
+    }
+    g_list_free( list );
+
+    gtk_entry_completion_set_model( comp, (GtkTreeModel*)store );
+    gtk_entry_completion_set_text_column( comp, 0 );
+    gtk_entry_set_completion( (GtkEntry*)entry, comp );
+    return FALSE;
+}
+
+static void show_error( GtkWindow* parent_win, const char* msg )
+{
+    GtkWidget* dlg = gtk_message_dialog_new( parent_win,
+                                             GTK_DIALOG_MODAL,
+                                             GTK_MESSAGE_ERROR,
+                                             GTK_BUTTONS_OK, msg );
+    gtk_dialog_run( (GtkDialog*)dlg );
+    gtk_widget_destroy( dlg );
+}
+
+static void on_response( GtkDialog* dlg, gint response, gpointer user_data )
+{
+    GtkEntry* entry = (GtkEntry*)user_data;
+    if( G_LIKELY(response == GTK_RESPONSE_OK) )
+    {
+        GError* err = NULL;
+        if( !g_spawn_command_line_async( gtk_entry_get_text(entry), &err ) )
+        {
+            show_error( (GtkWindow*)dlg, err->message );
+            g_error_free( err );
+            g_signal_stop_emission_by_name( dlg, "response" );
+            return;
+        }
+    }
+    g_source_remove_by_user_data( entry ); /* remove timeout */
+    gtk_widget_destroy( (GtkWidget*)dlg );
+}
+
+void gtk_run()
+{
+    GtkWidget *win, *entry, *hbox;
+
+    win = gtk_dialog_new_with_buttons( _("Run"),
+                                       NULL,
+                                       GTK_DIALOG_NO_SEPARATOR,
+                                       GTK_STOCK_OK, GTK_RESPONSE_OK,
+                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                       NULL );
+    gtk_dialog_set_default_response( (GtkDialog*)win, GTK_RESPONSE_OK );
+    entry = gtk_entry_new();
+    gtk_entry_set_activates_default( (GtkEntry*)entry, TRUE );
+    gtk_box_pack_start( (GtkBox*)((GtkDialog*)win)->vbox,
+                         gtk_label_new(_("Enter the command you want to execute:")),
+                         FALSE, FALSE, 8 );
+    hbox = gtk_hbox_new( FALSE, 2 );
+    gtk_box_pack_start( (GtkBox*)hbox,
+                         gtk_image_new_from_stock( GTK_STOCK_EXECUTE, GTK_ICON_SIZE_DIALOG ),
+                         FALSE, FALSE, 4 );
+    gtk_box_pack_start( (GtkBox*)hbox, entry, TRUE, TRUE, 4 );
+    gtk_box_pack_start( (GtkBox*)((GtkDialog*)win)->vbox,
+                         hbox, FALSE, FALSE, 8 );
+    g_signal_connect( win, "response", G_CALLBACK(on_response), entry );
+    gtk_window_set_position( (GtkWindow*)win, GTK_WIN_POS_CENTER );
+    gtk_window_set_default_size( (GtkWindow*)win, 360, -1 );
+    gtk_widget_show_all( win );
+    g_timeout_add( 500, setup_auto_complete, entry );
+    gtk_widget_show( (GtkDialog*)win );
+}
+
index 370dea8..2053ba2 100644 (file)
@@ -774,14 +774,30 @@ gtk_image_new_from_file_scaled(const gchar *file, gint width,
     GtkWidget *img;
     GdkPixbuf *pb, *pb_scaled;
     gfloat w, h, rw, rh;
+    GtkIconInfo *inf = NULL;
 
     ENTER;
+
+
     if (!g_file_test(file, G_FILE_TEST_EXISTS)) 
-        goto err;
+    {
+        /* FIXME: should reload icon when theme gets changed */
+        inf = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(),
+                                         file, MAX(width, height), 0);
+        if( ! inf )
+            goto err;
+        file = gtk_icon_info_get_filename(inf);
+    }
 
+#if GTK_CHECK_VERSION( 2, 6, 0 )
+    pb_scaled = gdk_pixbuf_new_from_file_at_scale( file, width, height,
+                                                   keep_ratio, NULL );
+    if( !pb_scaled )
+        goto err;
+#else
     if (!(pb = gdk_pixbuf_new_from_file(file, NULL)))
         goto err;
-    
+
     if (keep_ratio) {
         w = gdk_pixbuf_get_width(pb);
         h = gdk_pixbuf_get_height(pb);
@@ -794,9 +810,14 @@ gtk_image_new_from_file_scaled(const gchar *file, gint width,
     }
     pb_scaled = gdk_pixbuf_scale_simple(pb, width, height,
                                         GDK_INTERP_BILINEAR);
-    img = gtk_image_new_from_pixbuf(pb_scaled);                        
     g_object_unref(pb);
+#endif
+    img = gtk_image_new_from_pixbuf(pb_scaled);
     g_object_unref(pb_scaled);
+
+    if( inf )
+        gtk_icon_info_free ( inf );
+
     RET(img);
 
  err:
@@ -913,11 +934,11 @@ GtkWidget *
 fb_button_new_from_file(gchar *fname, int width, int height, gulong hicolor, gboolean keep_ratio)
 {
     GtkWidget *b, *image;
-    
     ENTER;
     b = gtk_bgbox_new();
     gtk_container_set_border_width(GTK_CONTAINER(b), 0);
     GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
+
     image = gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
     gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
     g_object_set_data(G_OBJECT(image), "hicolor", (gpointer)hicolor);
@@ -970,3 +991,45 @@ fb_button_new_from_file_with_label(gchar *fname, int width, int height,
     gtk_widget_show_all(b);
     RET(b);
 }
+
+char* translate_exec_to_cmd( const char* exec, const char* icon,
+                             const char* title, const char* fpath )
+{
+    GString* cmd = g_string_sized_new( 256 );
+    for( ; *exec; ++exec )
+    {
+        if( G_UNLIKELY(*exec == '%') )
+        {
+            ++exec;
+            if( !*exec )
+                break;
+            switch( *exec )
+            {
+                case 'c':
+                    g_string_append( cmd, title );
+                    break;
+                case 'i':
+                    if( icon )
+                    {
+                        g_string_append( cmd, "--icon " );
+                        g_string_append( cmd, icon );
+                    }
+                    break;
+                case 'k':
+                {
+                    char* uri = g_filename_to_uri( fpath, NULL, NULL );
+                    g_string_append( cmd, uri );
+                    g_free( uri );
+                    break;
+                }
+                case '%':
+                    g_string_append_c( cmd, '%' );
+                    break;
+            }
+        }
+        else
+            g_string_append_c( cmd, *exec );
+    }
+    return g_string_free( cmd, FALSE );
+}
+
index 1c9a976..d184cac 100644 (file)
@@ -63,4 +63,7 @@ GtkWidget *fb_button_new_from_file(gchar *fname, int width, int height, gulong h
       gboolean keep_ratio);
 GtkWidget *fb_button_new_from_file_with_label(gchar *fname, int width, int height,
       gulong hicolor, gboolean keep_ratio, gchar *label);
+
+char* translate_exec_to_cmd( const char* exec, const char* icon,
+                             const char* title, const char* fpath );
 #endif
index f4dd45f..a1853e4 100644 (file)
@@ -91,6 +91,7 @@ typedef struct {
 
 typedef struct {
     char *name;
+    char *disp_name;
     void (*cmd)(void);
 } command;
 
index 26d8b3e..30c2f21 100644 (file)
@@ -41,6 +41,8 @@ static const GtkTargetEntry target_table[] = {
     { "STRING",        0, 0 }
 };
 
+static const char desktop_ent[] = "Desktop Entry";
+
 typedef struct btn {
     //GtkWidget *button, *pixmap;
     gchar *action;
@@ -91,7 +93,7 @@ launchbar_destructor(plugin *p)
     ENTER;
     gtk_widget_destroy(lb->box);
     for (i = 0; i < lb->btn_num; i++) {
-        g_free(lb->btns[i].action);     
+        g_free(lb->btns[i].action);
     }
     g_free(lb);
     RET();
@@ -151,7 +153,7 @@ static int
 read_button(plugin *p)
 {
     launchbar *lb = (launchbar *)p->priv;
-    gchar *fname, *tooltip, *action;
+    gchar *fname, *tooltip, *action, *desktop_id;
     //GdkPixbuf *gp, *gps;
     GtkWidget *button;
     line s;
@@ -166,14 +168,16 @@ read_button(plugin *p)
         RET(0);
     }
 
-    tooltip = fname = action = 0;
+    tooltip = fname = action = desktop_id = NULL;
     while (get_line(p->fp, &s) != LINE_BLOCK_END) {
         if (s.type == LINE_NONE) {
             ERR( "launchbar: illegal token %s\n", s.str);
             RET(0);
         }
         if (s.type == LINE_VAR) {
-            if (!g_ascii_strcasecmp(s.t[0], "image")) 
+            if( !g_ascii_strcasecmp(s.t[0], "id") )
+                desktop_id = g_strdup(s.t[1]);
+            else if (!g_ascii_strcasecmp(s.t[0], "image"))
                 fname = expand_tilda(s.t[1]);
             else if (!g_ascii_strcasecmp(s.t[0], "tooltip"))
                 tooltip = g_strdup(s.t[1]);
@@ -190,6 +194,43 @@ read_button(plugin *p)
     }
     DBG("action=%s\n", action);
 
+    if( desktop_id ) {
+        gchar *desktop_file = NULL;
+        gchar *full_id = NULL;
+        GKeyFile* desktop = g_key_file_new();
+        full_id = g_strconcat( "applications/", desktop_id, NULL );
+        if( g_key_file_load_from_data_dirs( desktop, full_id, &desktop_file,
+                                            G_KEY_FILE_NONE, NULL ) )
+        {
+            gchar *icon = NULL, *title = NULL;
+            icon = g_key_file_get_string( desktop, desktop_ent, "Icon", NULL);
+            title = g_key_file_get_locale_string( desktop, desktop_ent,
+                                                "Name", NULL, NULL);
+            if( !fname && icon ){
+                gchar* sep = strchr( icon, '.' );
+                if( sep )
+                    fname = g_strndup( icon, (sep - icon) );
+                else
+                    fname = icon;
+            }
+            if( ! action ) {
+                gchar* exec;
+                exec = g_key_file_get_string( desktop, desktop_ent, "Exec", NULL);
+                action = translate_exec_to_cmd( exec, icon, title, desktop_file );
+                g_free( exec );
+            }
+            if( ! tooltip )
+                tooltip = title;
+            if( fname != icon )
+                g_free( icon );
+            if( tooltip != title )
+                g_free( title );
+        }
+        g_free( full_id );
+        g_free( desktop_file );
+        g_key_file_free( desktop );
+    }
+
     // button
     if (p->panel->orientation == ORIENT_HORIZ) {
         w = 10000;
@@ -208,26 +249,22 @@ read_button(plugin *p)
           G_CALLBACK (my_button_pressed), (gpointer) &lb->btns[lb->btn_num]);
 
 
-
-    
     GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
     // DnD support
     gtk_drag_dest_set (GTK_WIDGET(button),
           GTK_DEST_DEFAULT_ALL, //GTK_DEST_DEFAULT_HIGHLIGHT,
           target_table, G_N_ELEMENTS (target_table),
-          GDK_ACTION_COPY);    
+          GDK_ACTION_COPY);
     g_signal_connect (G_OBJECT(button), "drag_data_received",
           G_CALLBACK (drag_data_received_cb),  (gpointer) &lb->btns[lb->btn_num]);
 
-
     gtk_box_pack_start(GTK_BOX(lb->box), button, FALSE, FALSE, 0);
     gtk_widget_show(button);
-    //gtk_bgbox_set_background(button, BG_ROOT, 0xFFFFFF, 20);        
+    //gtk_bgbox_set_background(button, BG_ROOT, 0xFFFFFF, 20);
 
-    if (p->panel->transparent) 
+    if (p->panel->transparent)
         gtk_bgbox_set_background(button, BG_ROOT, p->panel->tintcolor, p->panel->alpha);
-    
+
     g_free(fname);
     // tooltip
     if (tooltip) {
@@ -251,7 +288,7 @@ read_button(plugin *p)
 static int
 launchbar_constructor(plugin *p)
 {
-    launchbar *lb; 
+    launchbar *lb;
     line s;
     GtkRequisition req;
     static gchar *launchbar_rc = "style 'launchbar-style'\n"
@@ -277,11 +314,11 @@ launchbar_constructor(plugin *p)
     gtk_widget_show(lb->box);
     lb->tips = gtk_tooltips_new();
     
-    if  (p->panel->orientation == ORIENT_HORIZ) 
+    if  (p->panel->orientation == ORIENT_HORIZ)
         lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.height;
     else
         lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.width;
-    DBG("button: req width=%d height=%d\n", req.width, req.height);            
+    DBG("button: req width=%d height=%d\n", req.width, req.height);
     DBG("iconsize=%d\n", lb->iconsize);
     
     s.len = 256;
index 65e1ad5..4696af6 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <glib.h>
+#include <glib/gi18n.h>
 
 #include "panel.h"
 #include "misc.h"
@@ -13,8 +14,6 @@
 //#define DEBUG
 #include "dbg.h"
 
-// #include "ptk_app_menu.c"
-
 /*
  * SuxPanel version 0.1
  * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
@@ -189,12 +188,12 @@ read_item(plugin *p)
     gchar *name, *fname, *action;
     GtkWidget *item;
     menup *m = (menup *)p->priv;
-    void (*cmd)(void);
+    command *cmd_entry = NULL;
 
     ENTER;
     s.len = 256;
-    name = fname = action = 0;
-    cmd = NULL;
+    name = fname = action = NULL;
+
     while (get_line(p->fp, &s) != LINE_BLOCK_END) {
         if (s.type == LINE_VAR) {
             if (!g_ascii_strcasecmp(s.t[0], "image"))
@@ -208,7 +207,7 @@ read_item(plugin *p)
 
                 for (tmp = commands; tmp->name; tmp++) {
                     if (!g_ascii_strcasecmp(s.t[1], tmp->name)) {
-                        cmd = tmp->cmd;
+                        cmd_entry = tmp;
                         break;
                     }
                 }
@@ -219,10 +218,20 @@ read_item(plugin *p)
         }
     }
     /* menu button */
-    item = gtk_image_menu_item_new_with_label(name ? name : "");
+    if( cmd_entry ) /* built-in commands */
+    {
+        item = gtk_image_menu_item_new_with_label( _(cmd_entry->disp_name) );
+        g_signal_connect(G_OBJECT(item), "activate", (GCallback)run_command, cmd_entry->cmd);
+    }
+    else
+    {
+        item = gtk_image_menu_item_new_with_label(name ? name : "");
+        if (action) {
+            g_signal_connect(G_OBJECT(item), "activate", (GCallback)spawn_app, action);
+        }
+    }
     gtk_container_set_border_width(GTK_CONTAINER(item), 0);
-    if (name)
-        g_free(name);
+    g_free(name);
     if (fname) {
         GtkWidget *img;
 
@@ -231,11 +240,6 @@ read_item(plugin *p)
         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
         g_free(fname);
     }
-    if (cmd) {
-        g_signal_connect(G_OBJECT(item), "activate", (GCallback)run_command, cmd);
-    } else if (action) {
-        g_signal_connect(G_OBJECT(item), "activate", (GCallback)spawn_app, action);
-    }
     RET(item);
 
  error:
diff --git a/src/plugins/ptk-app-menu.c b/src/plugins/ptk-app-menu.c
new file mode 100644 (file)
index 0000000..3ca413b
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+* ptk-app-menu.c
+*
+* Description: Generate menu from desktop files according to the spec on freedesktop.org
+*
+*
+* Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2006
+*
+* Copyright: GNU Lesser General Public License Version 2
+*
+*/
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+
+/* Compatibility macros for older versions of glib */
+#if ! GLIB_CHECK_VERSION(2, 10, 0)
+/* older versions of glib don't provde g_slice API */
+#define g_slice_alloc(size)         g_malloc(size)
+#define g_slice_alloc0(size)        g_malloc0(size)
+#define g_slice_new(type)           g_new(type, 1)
+#define g_slice_new0(type)          g_new0(type, 1)
+#define g_slice_free(type, mem)     g_free(mem)
+#define g_slice_free1(size, mem)    g_free(mem)
+#endif
+
+#include "misc.h" /* Misc functions for lxpanel */
+
+#define ICON_SIZE        24
+
+GtkWidget* ptk_app_menu_new();
+
+const char desktop_ent[] = "Desktop Entry";
+const char app_dir_name[] = "applications";
+static time_t* times = NULL;
+static int n_ref = 0;
+
+typedef struct _CatInfo
+{
+    char* title;
+    char* icon;
+    char** sub_cats;
+}CatInfo;
+
+typedef struct _PtkAppMenuItem
+{
+    char* name;
+    char* icon;
+    char* exec;
+}PtkAppMenuItem;
+
+static guint data_id = 0;
+
+const char* development_cats[]={
+   "Development",
+   "Translation",
+   "Building","Debugger",
+   "IDE",
+   "GUIDesigner",
+   "Profiling",
+   "RevisionControl",
+   "WebDevelopment",
+   NULL
+};
+const char* office_cats[] = {
+   "Office",
+   "Dictionary",
+   "Chart",
+   "Calendar",
+   "ContactManagement",
+   "Database",
+   NULL
+};
+const char* graphics_cats[] = {
+   "Graphics",
+   "2DGraphics",
+   "3DGraphics",
+   "VectorGraphics",
+   "RasterGraphics",
+   "Viewer",
+   NULL
+};
+const char* network_cats[] = {
+   "Network",
+   "Dialup",
+   "Email",
+   "WebBrowser",
+   "InstantMessaging",
+   "IRCClient",
+   "FileTransfer",
+   "News",
+   "P2P",
+   "RemoteAccess",
+   "Telephony",
+   NULL
+};
+const char* settings_cats[] = {
+   "Settings",
+   "DesktopSettings",
+   "HardwareSettings",
+   "Accessibility",
+   NULL
+};
+const char* system_cats[] = {
+   "System",
+   "Core",
+   "Security",
+   "PackageManager",
+   NULL
+};
+const char* audiovideo_cats[] ={
+   "AudioVideo",
+   "Audio",
+   "Video",
+   "Mixer",
+   "Sequencer",
+   "Tuner",
+   "TV",
+   "AudioVideoEditing",
+   "Player",
+   "Recorder",
+   "DiscBurning",
+   "Music",
+   NULL
+};
+const char* game_cats[] = {
+   "Game",
+   "Amusement",
+   NULL
+};
+const char* education_cats[] = {
+   "Education",
+   NULL
+};
+const char* utility_cats[] = {
+   "Utility",
+   NULL
+};
+
+const CatInfo known_cats[]=
+{
+    {N_("Other"), "gnome-other", NULL},
+    {N_("Game"), "gnome-joystick", game_cats},
+    {N_("Education"), "gnome-amusements", education_cats},
+    {N_("Development"), "gnome-devel", development_cats},
+    {N_("Audio & Video"), "gnome-multimedia", audiovideo_cats},
+    {N_("Graphics"), "gnome-graphics", graphics_cats},
+    {N_("Settings"), "gnome-settings", settings_cats},
+    {N_("System Tools"), "gnome-system", system_cats},
+    {N_("Network"), "gnome-globe", network_cats},
+    {N_("Office"), "gnome-applications", office_cats},
+    {N_("Accessories"), "gnome-util", utility_cats}
+};
+
+int find_cat( char** cats )
+{
+    char** cat;
+    for( cat = cats; *cat; ++cat )
+    {
+        int i;
+        /* Skip other */
+        for( i = 1; i < G_N_ELEMENTS(known_cats); ++i )
+        {
+            char** sub_cats = known_cats[i].sub_cats;
+            while( *sub_cats )
+            {
+                if( 0 == strncmp(*cat, "X-", 2) ) /*  Desktop specific*/
+                    return -1;
+                if( 0 == strcmp( *sub_cats, *cat ) )
+                    return i;
+                ++sub_cats;
+            }
+        }
+    }
+    return -1;
+}
+
+static void app_dirs_foreach( GFunc func, gpointer user_data );
+
+static int compare_menu_item_titles( gpointer a, gpointer b )
+{
+    char* title_a = gtk_label_get_text( gtk_bin_get_child(GTK_BIN(a)) );
+    char* title_b = gtk_label_get_text( gtk_bin_get_child(GTK_BIN(b)) );
+    return g_ascii_strcasecmp(title_a, title_b);
+}
+
+static int find_menu_item_by_name( gpointer a, gpointer b )
+{
+    PtkAppMenuItem* data = g_object_get_qdata( G_OBJECT(a), data_id );
+    const char* name = (char*)b;
+    return strcmp(data->name, b);
+}
+
+/* Moved to misc.c of lxpanel to be used in other plugins */
+#if 0
+static char* translate_exec( const char* exec, const char* icon,
+                             const char* title, const char* fpath )
+{
+    GString* cmd = g_string_sized_new( 256 );
+    for( ; *exec; ++exec )
+    {
+        if( G_UNLIKELY(*exec == '%') )
+        {
+            ++exec;
+            if( !*exec )
+                break;
+            switch( *exec )
+            {
+            case 'c':
+                g_string_append( cmd, title );
+                break;
+            case 'i':
+                if( icon )
+                {
+                    g_string_append( cmd, "--icon " );
+                    g_string_append( cmd, icon );
+                }
+                break;
+            case 'k':
+                {
+                    char* uri = g_filename_to_uri( fpath, NULL, NULL );
+                    g_string_append( cmd, uri );
+                    g_free( uri );
+                    break;
+                }
+            case '%':
+                g_string_append_c( cmd, '%' );
+                break;
+            }
+        }
+        else
+            g_string_append_c( cmd, *exec );
+    }
+    return g_string_free( cmd, FALSE );
+}
+#endif
+
+void  unload_old_icons( GtkWidget* menu )
+{
+    GList* items = gtk_container_get_children( GTK_CONTAINER(menu) );
+    GList* l;
+    for( l = items; l; l = l->next )
+    {
+        GtkWidget* sub_menu = gtk_menu_item_get_submenu( GTK_MENU_ITEM(l->data) );
+        GtkWidget* img = gtk_image_menu_item_get_image( GTK_IMAGE_MENU_ITEM(l->data) );
+        if( ! g_object_get_qdata( G_OBJECT(l->data), data_id ) )
+            continue;
+        if( img )
+            gtk_widget_destroy( img );
+        if( sub_menu )
+            unload_old_icons( sub_menu );
+    }
+    g_list_free( items );
+}
+
+static void on_menu_item_size_request( GtkWidget* item,
+                                       GtkRequisition* req,
+                                       gpointer user_data )
+{
+    if( req->height < ICON_SIZE )
+        req->height = ICON_SIZE;
+    if( req->width < ICON_SIZE )
+       req->width = ICON_SIZE;
+}
+
+static gboolean on_menu_item_expose( GtkWidget* item,
+                                     GdkEventExpose* evt,
+                                     gpointer user_data )
+{
+    GtkWidget* img;
+    GdkPixbuf* pix;
+    PtkAppMenuItem* data = (PtkAppMenuItem*)user_data;
+    if( !data )
+        return FALSE;
+    img = gtk_image_menu_item_get_image(item);
+    if( img )
+        return FALSE;
+    if( G_UNLIKELY(!data) || G_UNLIKELY(!data->icon) )
+        return FALSE;
+    pix = NULL;
+    if( data->icon[0] == '/' )
+    {
+        pix = gdk_pixbuf_new_from_file_at_size(data->icon, ICON_SIZE, ICON_SIZE, NULL);
+    }
+    else
+    {
+        GtkIconInfo* inf;
+        inf = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), data->icon, ICON_SIZE, 0);
+        if( inf )
+        {
+            pix = gdk_pixbuf_new_from_file_at_size( gtk_icon_info_get_filename(inf), ICON_SIZE, ICON_SIZE, NULL);
+            gtk_icon_info_free ( inf );
+        }
+    }
+    if( G_LIKELY(pix) )
+    {
+        img = gtk_image_new_from_pixbuf( pix );
+        if( G_LIKELY(pix) )
+            g_object_unref( pix );
+    }
+    else
+    {
+        img = gtk_image_new();
+        gtk_image_set_pixel_size( img, ICON_SIZE );
+    }
+    gtk_image_menu_item_set_image( item, img );
+    return FALSE;
+}
+
+static void on_app_menu_item_activate( GtkMenuItem* item, PtkAppMenuItem* data )
+{
+    GError* err = NULL;
+    /* FIXME: support startup notification */
+    g_debug("run command: %s", data->exec);
+    if( !g_spawn_command_line_async( data->exec, &err ) )
+    {
+        /* FIXME: show error message */
+        g_error_free( err );
+    }
+}
+
+static void ptk_app_menu_item_free( PtkAppMenuItem* data )
+{
+    g_free( data->name );
+    g_free( data->icon );
+    g_free( data->exec );
+    g_slice_free( PtkAppMenuItem, data );
+}
+
+static void do_load_dir( int prefix_len,
+                         const char* path,
+                         GList** sub_menus )
+{
+    GDir* dir = g_dir_open( path, 0, NULL );
+    const char* name;
+    if( G_UNLIKELY( ! dir ) )
+        return;
+    while( name = g_dir_read_name( dir ) )
+    {
+        char* fpath;
+        GKeyFile*  file;
+        char **cats, **cat;
+        char **only_show_in;
+
+        if( name[0] =='.' )
+            continue;
+        fpath = g_build_filename( path, name, NULL );
+        if( g_file_test(fpath, G_FILE_TEST_IS_DIR) )
+        {
+            do_load_dir( prefix_len, fpath, sub_menus );
+            continue;
+        }
+        if( ! g_str_has_suffix( name, ".desktop" ) )
+            continue;
+        file = g_key_file_new();
+        g_key_file_load_from_file( file, fpath, 0, NULL );
+        if( g_key_file_get_boolean( file, desktop_ent, "NoDisplay", NULL ) )
+        {
+            g_key_file_free( file );
+            continue;
+        }
+        only_show_in = g_key_file_get_string_list( file, desktop_ent, "OnlyShowIn", NULL, NULL );
+        if( only_show_in )
+        {
+            g_key_file_free( file );
+            g_strfreev( only_show_in );
+            continue;
+        }
+        cats = g_key_file_get_string_list( file, desktop_ent, "Categories", NULL, NULL );
+        if( cats )
+        {
+            int i = find_cat( cats );
+            if( i >= 0 )
+            {
+                GtkWidget* menu_item;
+                char *title, *exec, *icon;
+                /* FIXME: processing duplicated items */
+                exec = g_key_file_get_string( file, desktop_ent, "Exec", NULL);
+                if( exec )
+                {
+                    title = g_key_file_get_locale_string( file, desktop_ent, "Name", NULL, NULL);
+                    if( title )
+                    {
+                        PtkAppMenuItem* data;
+                        GList* prev;
+                        prev =g_list_find_custom( sub_menus[i], (fpath + prefix_len),
+                                                                                                            find_menu_item_by_name );
+                        if( ! prev )
+                        {
+                            menu_item = gtk_image_menu_item_new_with_label( title );
+                            data = g_slice_new0(PtkAppMenuItem);
+                        }
+                        else
+                        {
+                            GtkLabel* label;
+                            menu_item = GTK_WIDGET(prev->data);
+                            label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(menu_item)));
+                            data = (PtkAppMenuItem*)g_object_get_qdata( menu_item, data_id );
+                            gtk_label_set_text( label, title );
+                            g_free( data->name );
+                            g_free( data->exec );
+                            g_free( data->icon );
+                        }
+                        data->name = g_strdup( fpath + prefix_len );
+                        data->exec = exec ? translate_exec_to_cmd( exec, data->icon, title, fpath ) : NULL;
+                        g_free( title );
+                        g_signal_connect( menu_item, "expose-event", on_menu_item_expose, data );
+                        g_signal_connect( menu_item, "size-request", on_menu_item_size_request, data );
+                        icon = g_strdup( g_key_file_get_string( file, desktop_ent, "Icon", NULL) );
+                        if( icon )
+                        {
+                            char* dot = strchr( icon, '.' );
+                            if( icon[0] !='/' && dot )
+                                *dot = '\0';
+                        }
+                        data->icon = icon;
+                        if( !prev )
+                        {
+                            g_signal_connect( menu_item, "activate", on_app_menu_item_activate, data );
+                            g_object_set_qdata_full( menu_item, data_id, data, ptk_app_menu_item_free );
+                            sub_menus[i] = g_list_insert_sorted( sub_menus[i], menu_item,compare_menu_item_titles );
+                        }
+                    } /* if( title ) */
+                    g_free( exec );
+                } /* if( exec ) */
+            }
+            g_strfreev(cats);
+        }
+        g_key_file_free( file );
+        g_free( fpath );
+    }
+    g_dir_close( dir );
+}
+
+static void load_dir( const char* path, GList** sub_menus )
+{
+    do_load_dir( strlen( path ) + 1, path, sub_menus );
+}
+
+static GtkWidget* app_menu = NULL;
+static void on_menu( GtkWidget* btn, gpointer user_data )
+{
+    if( ptk_app_menu_need_reload() )
+    {
+        if( app_menu )
+            gtk_widget_destroy( app_menu );
+        app_menu = ptk_app_menu_new();
+    }
+    else if( !app_menu )
+        app_menu = ptk_app_menu_new();
+    gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, 0, 0 );
+}
+
+void on_app_menu_destroy( gpointer user_data, GObject* menu )
+{
+    g_signal_handler_disconnect( gtk_icon_theme_get_default(),
+                                 GPOINTER_TO_INT(user_data) );
+    --n_ref;
+    if( n_ref == 0 )
+    {
+        g_free( times );
+        times = NULL;
+    }
+}
+
+gboolean ptk_app_menu_item_has_data( GtkMenuItem* item )
+{
+   return (g_object_get_qdata( item, data_id ) != NULL);
+}
+
+/*
+ * Insert application menus into specified menu
+ * menu: The parent menu to which the items should be inserted
+ * pisition: Position to insert items.
+             Passing -1 in this parameter means append all items
+             at the end of menu.
+ */
+void ptk_app_menu_insert_items( GtkMenu* menu, int position )
+{
+   GList* sub_menus[ G_N_ELEMENTS(known_cats) ] = {0};
+   int i;
+   GList *sub_items, *l;
+   guint change_handler;
+
+   if( ! data_id )
+      data_id = g_quark_from_static_string("PtkAppMenuItem");
+   app_dirs_foreach( load_dir, sub_menus );
+   for( i = 0; i < G_N_ELEMENTS(known_cats); ++i )
+   {
+      GtkMenu* sub_menu;
+      GtkWidget* menu_item;
+      GtkWidget* img;
+      PtkAppMenuItem* data;
+      if( ! (sub_items = sub_menus[i]) )
+         continue;
+      sub_menu = gtk_menu_new();
+
+      for( l = sub_items; l; l = l->next )
+         gtk_menu_shell_append( sub_menu, GTK_WIDGET(l->data) );
+      g_list_free( sub_items );
+      menu_item = gtk_image_menu_item_new_with_label( _(known_cats[i].title) );
+      data = g_slice_new0( PtkAppMenuItem );
+      data->icon = g_strdup(known_cats[i].icon);
+      g_object_set_qdata_full( menu_item, data_id, data, ptk_app_menu_item_free );
+      g_signal_connect( menu_item, "expose-event", on_menu_item_expose, data );
+      g_signal_connect( menu_item, "size-request", on_menu_item_size_request, data );
+      on_menu_item_expose( menu_item, NULL, data );
+      gtk_menu_item_set_submenu( menu_item, sub_menu );
+      if( position == -1 )
+         gtk_menu_shell_append( menu, menu_item );
+      else
+      {
+         gtk_menu_shell_insert( menu, menu_item, position );
+         ++position;
+      }
+   }
+   gtk_widget_show_all(menu);
+   change_handler = g_signal_connect_swapped( gtk_icon_theme_get_default(), "changed", unload_old_icons, menu );
+   g_object_weak_ref( menu, on_app_menu_destroy, GINT_TO_POINTER(change_handler) );
+   ++n_ref;
+}
+
+GtkWidget* ptk_app_menu_new()
+{
+    GtkWidget* menu;
+    menu = gtk_menu_new();
+    ptk_app_menu_insert_items( menu, -1 );
+    return menu;
+}
+
+void app_dirs_foreach( GFunc func, gpointer user_data )
+{
+    const char** sys_dirs = (const char**)g_get_system_data_dirs();
+    char* path;
+    int i, len;
+    struct stat dir_stat;
+
+    len = g_strv_length(sys_dirs);
+    if( !times )
+        times = g_new0( time_t, len + 2 );
+    for( i = 0; i < len; ++i )
+    {
+        path = g_build_filename( sys_dirs[i], app_dir_name, NULL );
+        if( stat( path, &dir_stat) == 0 )
+        {
+            times[i] = dir_stat.st_mtime;
+            func( path, user_data );
+        }
+        g_free( path );
+    }
+    path = g_build_filename( g_get_user_data_dir(), app_dir_name, NULL );
+    times[i] = dir_stat.st_mtime;
+    if( stat( path, &dir_stat) == 0 )
+    {
+        times[i] = dir_stat.st_mtime;
+        func( path, user_data );
+    }
+    g_free( path );
+}
+
+#if defined( PTK_APP_MENU_DEMO )
+int main( int argc, char** argv )
+{
+    gtk_init(&argc, &argv);
+    GtkWidget* window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+    gtk_window_set_title(GTK_WINDOW( window ), "Show Applications" );
+    GtkWidget* button = gtk_button_new_with_label("Application Menu");
+    g_signal_connect(button, "clicked", G_CALLBACK(on_menu), NULL );
+    gtk_container_add( GTK_CONTAINER(window), button );
+    g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL );
+    gtk_widget_show_all(window);
+    if( app_menu )
+        gtk_widget_destroy( app_menu );
+    gtk_main();
+    return 0;
+}
+#endif
+
+gboolean ptk_app_menu_need_reload()
+{
+    const char** sys_dirs = (const char**)g_get_system_data_dirs();
+    char* path;
+    int i, len;
+    struct stat dir_stat;
+
+    if( !times )
+        return TRUE;
+    len = g_strv_length(sys_dirs);
+    for( i = 0; i < len; ++i )
+    {
+        path = g_build_filename( sys_dirs[i], app_dir_name, NULL );
+        if( stat( path, &dir_stat) == 0 )
+        {
+            if( times[i] != dir_stat.st_mtime )
+            {
+                g_free( path );
+                return TRUE;
+            }
+        }
+        g_free( path );
+    }
+    path = g_build_filename( g_get_user_data_dir(), app_dir_name, NULL );
+    if( stat( path, &dir_stat) == 0 )
+    {
+        if( times[i] != dir_stat.st_mtime )
+        {
+            g_free( path );
+            return TRUE;
+        }
+    }
+    g_free( path );
+    return FALSE;
+}
+
diff --git a/src/plugins/ptk_app_menu.c b/src/plugins/ptk_app_menu.c
deleted file mode 100644 (file)
index 1175657..0000000
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
-* ptk-app-menu.c
-*
-* Description: Generate menu from desktop files according to the spec on freedesktop.org
-*
-*
-* Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2006
-*
-* Copyright: GNU Lesser General Public License Version 2
-*
-*/
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <string.h>
-
-/* Compatibility macros for older versions of glib */
-#if ! GLIB_CHECK_VERSION(2, 10, 0)
-/* older versions of glib don't provde g_slice API */
-#define g_slice_alloc(size)         g_malloc(size)
-#define g_slice_alloc0(size)        g_malloc0(size)
-#define g_slice_new(type)           g_new(type, 1)
-#define g_slice_new0(type)          g_new0(type, 1)
-#define g_slice_free(type, mem)     g_free(mem)
-#define g_slice_free1(size, mem)    g_free(mem)
-#endif
-
-#define ICON_SIZE        24
-
-GtkWidget* ptk_app_menu_new();
-
-const char desktop_ent[] = "Desktop Entry";
-const char app_dir_name[] = "applications";
-static time_t* times = NULL;
-static int n_ref = 0;
-
-typedef struct _CatInfo
-{
-    char* title;
-    char* icon;
-    char** sub_cats;
-}CatInfo;
-
-typedef struct _PtkAppMenuItem
-{
-    char* name;
-    char* icon;
-    char* exec;
-}PtkAppMenuItem;
-
-static guint data_id = 0;
-
-const char* development_cats[]={
-   "Development",
-   "Translation",
-   "Building","Debugger",
-   "IDE",
-   "GUIDesigner",
-   "Profiling",
-   "RevisionControl",
-   "WebDevelopment",
-   NULL
-};
-const char* office_cats[] = {
-   "Office",
-   "Dictionary",
-   "Chart",
-   "Calendar",
-   "ContactManagement",
-   "Database",
-   NULL
-};
-const char* graphics_cats[] = {
-   "Graphics",
-   "2DGraphics",
-   "3DGraphics",
-   "VectorGraphics",
-   "RasterGraphics",
-   "Viewer",
-   NULL
-};
-const char* network_cats[] = {
-   "Network",
-   "Dialup",
-   "Email",
-   "WebBrowser",
-   "InstantMessaging",
-   "IRCClient",
-   "FileTransfer",
-   "News",
-   "P2P",
-   "RemoteAccess",
-   "Telephony",
-   NULL
-};
-const char* settings_cats[] = {
-   "Settings",
-   "DesktopSettings",
-   "HardwareSettings",
-   "Accessibility",
-   NULL
-};
-const char* system_cats[] = {
-   "System",
-   "Core",
-   "Security",
-   "PackageManager",
-   NULL
-};
-const char* audiovideo_cats[] ={
-   "AudioVideo",
-   "Audio",
-   "Video",
-   "Mixer",
-   "Sequencer",
-   "Tuner",
-   "TV",
-   "AudioVideoEditing",
-   "Player",
-   "Recorder",
-   "DiscBurning",
-   "Music",
-   NULL
-};
-const char* game_cats[] = {
-   "Game",
-   "Amusement",
-   NULL
-};
-const char* education_cats[] = {
-   "Education",
-   NULL
-};
-const char* utility_cats[] = {
-   "Utility",
-   NULL
-};
-
-const CatInfo known_cats[]=
-{
-    {N_("Other"), "gnome-other", NULL},
-    {N_("Game"), "gnome-joystick", game_cats},
-    {N_("Education"), "gnome-amusements", education_cats},
-    {N_("Development"), "gnome-devel", development_cats},
-    {N_("Audio & Video"), "gnome-multimedia", audiovideo_cats},
-    {N_("Graphics"), "gnome-graphics", graphics_cats},
-    {N_("Settings"), "gnome-settings", settings_cats},
-    {N_("System Tools"), "gnome-system", system_cats},
-    {N_("Network"), "gnome-globe", network_cats},
-    {N_("Office"), "gnome-applications", office_cats},
-    {N_("Accessories"), "gnome-util", utility_cats}
-};
-
-int find_cat( char** cats )
-{
-    char** cat;
-    for( cat = cats; *cat; ++cat )
-    {
-        int i;
-        /* Skip other */
-        for( i = 1; i < G_N_ELEMENTS(known_cats); ++i )
-        {
-            char** sub_cats = known_cats[i].sub_cats;
-            while( *sub_cats )
-            {
-                if( 0 == strncmp(*cat, "X-", 2) ) /*  Desktop specific*/
-                    return -1;
-                if( 0 == strcmp( *sub_cats, *cat ) )
-                    return i;
-                ++sub_cats;
-            }
-        }
-    }
-    return -1;
-}
-
-static void app_dirs_foreach( GFunc func, gpointer user_data );
-
-static int compare_menu_item_titles( gpointer a, gpointer b )
-{
-    char* title_a = gtk_label_get_text( gtk_bin_get_child(GTK_BIN(a)) );
-    char* title_b = gtk_label_get_text( gtk_bin_get_child(GTK_BIN(b)) );
-    return g_ascii_strcasecmp(title_a, title_b);
-}
-
-static int find_menu_item_by_name( gpointer a, gpointer b )
-{
-    PtkAppMenuItem* data = g_object_get_qdata( G_OBJECT(a), data_id );
-    const char* name = (char*)b;
-    return strcmp(data->name, b);
-}
-
-static char* translate_exec( const char* exec, PtkAppMenuItem* data,
-                             const char* title, const char* fpath )
-{
-    GString* cmd = g_string_sized_new( 256 );
-    for( ; *exec; ++exec )
-    {
-        if( G_UNLIKELY(*exec == '%') )
-        {
-            ++exec;
-            if( !*exec )
-                break;
-            switch( *exec )
-            {
-            case 'c':
-                g_string_append( cmd, title );
-                break;
-            case 'i':
-                if( data->icon )
-                {
-                    g_string_append( cmd, "--icon " );
-                    g_string_append( cmd, data->icon );
-                }
-                break;
-            case 'k':
-                {
-                    char* uri = g_filename_to_uri( fpath, NULL, NULL );
-                    g_string_append( cmd, uri );
-                    g_free( uri );
-                    break;
-                }
-            case '%':
-                g_string_append_c( cmd, '%' );
-                break;
-            }
-        }
-        else
-            g_string_append_c( cmd, *exec );
-    }
-    return g_string_free( cmd, FALSE );
-}
-
-void  unload_old_icons( GtkWidget* menu )
-{
-    GList* items = gtk_container_get_children( GTK_CONTAINER(menu) );
-    GList* l;
-    for( l = items; l; l = l->next )
-    {
-        GtkWidget* sub_menu = gtk_menu_item_get_submenu( GTK_MENU_ITEM(l->data) );
-        GtkWidget* img = gtk_image_menu_item_get_image( GTK_IMAGE_MENU_ITEM(l->data) );
-        if( ! g_object_get_qdata( G_OBJECT(l->data), data_id ) )
-            continue;
-        if( img )
-            gtk_widget_destroy( img );
-        if( sub_menu )
-            unload_old_icons( sub_menu );
-    }
-    g_list_free( items );
-}
-
-static void on_menu_item_size_request( GtkWidget* item,
-                                       GtkRequisition* req,
-                                       gpointer user_data )
-{
-    if( req->height < ICON_SIZE )
-        req->height = ICON_SIZE;
-    if( req->width < ICON_SIZE )
-       req->width = ICON_SIZE;
-}
-
-static gboolean on_menu_item_expose( GtkWidget* item,
-                                     GdkEventExpose* evt,
-                                     gpointer user_data )
-{
-    GtkWidget* img;
-    GdkPixbuf* pix;
-    PtkAppMenuItem* data = (PtkAppMenuItem*)user_data;
-    if( !data )
-        return FALSE;
-    img = gtk_image_menu_item_get_image(item);
-    if( img )
-        return FALSE;
-    if( G_UNLIKELY(!data) || G_UNLIKELY(!data->icon) )
-        return FALSE;
-    pix = NULL;
-    if( data->icon[0] == '/' )
-    {
-        pix = gdk_pixbuf_new_from_file_at_size(data->icon, ICON_SIZE, ICON_SIZE, NULL);
-    }
-    else
-    {
-        GtkIconInfo* inf;
-        inf = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), data->icon, ICON_SIZE, 0);
-        if( inf )
-        {
-            pix = gdk_pixbuf_new_from_file_at_size( gtk_icon_info_get_filename(inf), ICON_SIZE, ICON_SIZE, NULL);
-            gtk_icon_info_free ( inf );
-        }
-    }
-    if( G_LIKELY(pix) )
-    {
-        img = gtk_image_new_from_pixbuf( pix );
-        if( G_LIKELY(pix) )
-            g_object_unref( pix );
-    }
-    else
-    {
-        img = gtk_image_new();
-        gtk_image_set_pixel_size( img, ICON_SIZE );
-    }
-    gtk_image_menu_item_set_image( item, img );
-    return FALSE;
-}
-
-static void on_app_menu_item_activate( GtkMenuItem* item, PtkAppMenuItem* data )
-{
-    GError* err = NULL;
-    /* FIXME: support startup notification */
-    g_debug("run command: %s", data->exec);
-    if( !g_spawn_command_line_async( data->exec, &err ) )
-    {
-        /* FIXME: show error message */
-        g_error_free( err );
-    }
-}
-
-static void ptk_app_menu_item_free( PtkAppMenuItem* data )
-{
-    g_free( data->name );
-    g_free( data->icon );
-    g_free( data->exec );
-    g_slice_free( PtkAppMenuItem, data );
-}
-
-static void do_load_dir( int prefix_len,
-                         const char* path,
-                         GList** sub_menus )
-{
-    GDir* dir = g_dir_open( path, 0, NULL );
-    const char* name;
-    if( G_UNLIKELY( ! dir ) )
-        return;
-    while( name = g_dir_read_name( dir ) )
-    {
-        char* fpath;
-        GKeyFile*  file;
-        char **cats, **cat;
-        char **only_show_in;
-
-        if( name[0] =='.' )
-            continue;
-        fpath = g_build_filename( path, name, NULL );
-        if( g_file_test(fpath, G_FILE_TEST_IS_DIR) )
-        {
-            do_load_dir( prefix_len, fpath, sub_menus );
-            continue;
-        }
-        if( ! g_str_has_suffix( name, ".desktop" ) )
-            continue;
-        file = g_key_file_new();
-        g_key_file_load_from_file( file, fpath, 0, NULL );
-        if( g_key_file_get_boolean( file, desktop_ent, "NoDisplay", NULL ) )
-        {
-            g_key_file_free( file );
-            continue;
-        }
-        only_show_in = g_key_file_get_string_list( file, desktop_ent, "OnlyShowIn", NULL, NULL );
-        if( only_show_in )
-        {
-            g_key_file_free( file );
-            g_strfreev( only_show_in );
-            continue;
-        }
-        cats = g_key_file_get_string_list( file, desktop_ent, "Categories", NULL, NULL );
-        if( cats )
-        {
-            int i = find_cat( cats );
-            if( i >= 0 )
-            {
-                GtkWidget* menu_item;
-                char *title, *exec, *icon;
-                /* FIXME: processing duplicated items */
-                exec = g_key_file_get_string( file, desktop_ent, "Exec", NULL);
-                if( exec )
-                {
-                    title = g_key_file_get_locale_string( file, desktop_ent, "Name", NULL, NULL);
-                    if( title )
-                    {
-                        PtkAppMenuItem* data;
-                        GList* prev;
-                        prev =g_list_find_custom( sub_menus[i], (fpath + prefix_len),
-                                                                                                            find_menu_item_by_name );
-                        if( ! prev )
-                        {
-                            menu_item = gtk_image_menu_item_new_with_label( title );
-                            data = g_slice_new0(PtkAppMenuItem);
-                        }
-                        else
-                        {
-                            GtkLabel* label;
-                            menu_item = GTK_WIDGET(prev->data);
-                            label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(menu_item)));
-                            data = (PtkAppMenuItem*)g_object_get_qdata( menu_item, data_id );
-                            gtk_label_set_text( label, title );
-                            g_free( data->name );
-                            g_free( data->exec );
-                            g_free( data->icon );
-                        }
-                        data->name = g_strdup( fpath + prefix_len );
-                        data->exec = exec ? translate_exec( exec, data, title, fpath ) : NULL;
-                        g_free( title );
-                        g_signal_connect( menu_item, "expose-event", on_menu_item_expose, data );
-                        g_signal_connect( menu_item, "size-request", on_menu_item_size_request, data );
-                        icon = g_strdup( g_key_file_get_string( file, desktop_ent, "Icon", NULL) );
-                        if( icon )
-                        {
-                            char* dot = strchr( icon, '.' );
-                            if( icon[0] !='/' && dot )
-                                *dot = '\0';
-                        }
-                        data->icon = icon;
-                        if( !prev )
-                        {
-                            g_signal_connect( menu_item, "activate", on_app_menu_item_activate, data );
-                            g_object_set_qdata_full( menu_item, data_id, data, ptk_app_menu_item_free );
-                            sub_menus[i] = g_list_insert_sorted( sub_menus[i], menu_item,compare_menu_item_titles );
-                        }
-                    } /* if( title ) */
-                    g_free( exec );
-                } /* if( exec ) */
-            }
-            g_strfreev(cats);
-        }
-        g_key_file_free( file );
-        g_free( fpath );
-    }
-    g_dir_close( dir );
-}
-
-static void load_dir( const char* path, GList** sub_menus )
-{
-    do_load_dir( strlen( path ) + 1, path, sub_menus );
-}
-
-static GtkWidget* app_menu = NULL;
-static void on_menu( GtkWidget* btn, gpointer user_data )
-{
-    if( ptk_app_menu_need_reload() )
-    {
-        if( app_menu )
-            gtk_widget_destroy( app_menu );
-        app_menu = ptk_app_menu_new();
-    }
-    else if( !app_menu )
-        app_menu = ptk_app_menu_new();
-    gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, 0, 0 );
-}
-
-void on_app_menu_destroy( gpointer user_data, GObject* menu )
-{
-    g_signal_handler_disconnect( gtk_icon_theme_get_default(),
-                                 GPOINTER_TO_INT(user_data) );
-    --n_ref;
-    if( n_ref == 0 )
-    {
-        g_free( times );
-        times = NULL;
-    }
-}
-
-gboolean ptk_app_menu_item_has_data( GtkMenuItem* item )
-{
-   return (g_object_get_qdata( item, data_id ) != NULL);
-}
-
-/*
- * Insert application menus into specified menu
- * menu: The parent menu to which the items should be inserted
- * pisition: Position to insert items.
-             Passing -1 in this parameter means append all items
-             at the end of menu.
- */
-void ptk_app_menu_insert_items( GtkMenu* menu, int position )
-{
-   GList* sub_menus[ G_N_ELEMENTS(known_cats) ] = {0};
-   int i;
-   GList *sub_items, *l;
-   guint change_handler;
-
-   if( ! data_id )
-      data_id = g_quark_from_static_string("PtkAppMenuItem");
-   app_dirs_foreach( load_dir, sub_menus );
-   for( i = 0; i < G_N_ELEMENTS(known_cats); ++i )
-   {
-      GtkMenu* sub_menu;
-      GtkWidget* menu_item;
-      GtkWidget* img;
-      PtkAppMenuItem* data;
-      if( ! (sub_items = sub_menus[i]) )
-         continue;
-      sub_menu = gtk_menu_new();
-
-      for( l = sub_items; l; l = l->next )
-         gtk_menu_shell_append( sub_menu, GTK_WIDGET(l->data) );
-      g_list_free( sub_items );
-      menu_item = gtk_image_menu_item_new_with_label( _(known_cats[i].title) );
-      data = g_slice_new0( PtkAppMenuItem );
-      data->icon = g_strdup(known_cats[i].icon);
-      g_object_set_qdata_full( menu_item, data_id, data, ptk_app_menu_item_free );
-      g_signal_connect( menu_item, "expose-event", on_menu_item_expose, data );
-      g_signal_connect( menu_item, "size-request", on_menu_item_size_request, data );
-      on_menu_item_expose( menu_item, NULL, data );
-      gtk_menu_item_set_submenu( menu_item, sub_menu );
-      if( position == -1 )
-         gtk_menu_shell_append( menu, menu_item );
-      else
-      {
-         gtk_menu_shell_insert( menu, menu_item, position );
-         ++position;
-      }
-   }
-   gtk_widget_show_all(menu);
-   change_handler = g_signal_connect_swapped( gtk_icon_theme_get_default(), "changed", unload_old_icons, menu );
-   g_object_weak_ref( menu, on_app_menu_destroy, GINT_TO_POINTER(change_handler) );
-   ++n_ref;
-}
-
-GtkWidget* ptk_app_menu_new()
-{
-    GtkWidget* menu;
-    menu = gtk_menu_new();
-    ptk_app_menu_insert_items( menu, -1 );
-    return menu;
-}
-
-void app_dirs_foreach( GFunc func, gpointer user_data )
-{
-    const char** sys_dirs = (const char**)g_get_system_data_dirs();
-    char* path;
-    int i, len;
-    struct stat dir_stat;
-
-    len = g_strv_length(sys_dirs);
-    if( !times )
-        times = g_new0( time_t, len + 2 );
-    for( i = 0; i < len; ++i )
-    {
-        path = g_build_filename( sys_dirs[i], app_dir_name, NULL );
-        if( stat( path, &dir_stat) == 0 )
-        {
-            times[i] = dir_stat.st_mtime;
-            func( path, user_data );
-        }
-        g_free( path );
-    }
-    path = g_build_filename( g_get_user_data_dir(), app_dir_name, NULL );
-    times[i] = dir_stat.st_mtime;
-    if( stat( path, &dir_stat) == 0 )
-    {
-        times[i] = dir_stat.st_mtime;
-        func( path, user_data );
-    }
-    g_free( path );
-}
-
-#if defined( PTK_APP_MENU_DEMO )
-int main( int argc, char** argv )
-{
-    gtk_init(&argc, &argv);
-    GtkWidget* window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
-    gtk_window_set_title(GTK_WINDOW( window ), "Show Applications" );
-    GtkWidget* button = gtk_button_new_with_label("Application Menu");
-    g_signal_connect(button, "clicked", G_CALLBACK(on_menu), NULL );
-    gtk_container_add( GTK_CONTAINER(window), button );
-    g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL );
-    gtk_widget_show_all(window);
-    if( app_menu )
-        gtk_widget_destroy( app_menu );
-    gtk_main();
-    return 0;
-}
-#endif
-
-gboolean ptk_app_menu_need_reload()
-{
-    const char** sys_dirs = (const char**)g_get_system_data_dirs();
-    char* path;
-    int i, len;
-    struct stat dir_stat;
-
-    if( !times )
-        return TRUE;
-    len = g_strv_length(sys_dirs);
-    for( i = 0; i < len; ++i )
-    {
-        path = g_build_filename( sys_dirs[i], app_dir_name, NULL );
-        if( stat( path, &dir_stat) == 0 )
-        {
-            if( times[i] != dir_stat.st_mtime )
-            {
-                g_free( path );
-                return TRUE;
-            }
-        }
-        g_free( path );
-    }
-    path = g_build_filename( g_get_user_data_dir(), app_dir_name, NULL );
-    if( stat( path, &dir_stat) == 0 )
-    {
-        if( times[i] != dir_stat.st_mtime )
-        {
-            g_free( path );
-            return TRUE;
-        }
-    }
-    g_free( path );
-    return FALSE;
-}
-