Modification/amélioration menu contextuel gtk (C++?)

Modification/amélioration menu contextuel gtk (C++?) - C++ - Programmation

Marsh Posté le 11-04-2011 à 14:10:56    

Bonjour,
Je connais un peu le code gtkdialog3, qui me permet modestement de créer/modifier des scripts simples pour le développement d'une distribution LINUX à laquelle je collabore: Toutou LINUX FAT version RELOADED. Parmi les utilitaires, il en est un, nommé "fbxkb", qui permet d'afficher et changer la disposition du clavier, d'un simple clic gauche sur une icône-drapeau située dans la barre des tâches. Un clic droit sur cette icône affiche un menu contextuel donnant accès à une fenêtre d'information (genre A propos) ainsi qu'à un bouton pour quitter l'application.
 
http://www.murga-linux.com/puppy/viewtopic.php?mode=attach&id=39739
 
Je trouverais bien pratique qu'une entrée supplémentaire de ce menu donne directement accès à une application, (en l'occurrence, au Gestionnaire de configuration avancée du clavier Xkb Config Manager, dont fbxkb est une sorte d'extension), sans avoir donc à passer par un raccourci dans le menu ou sur le bureau.
 
Ce que j'aimerais obtenir:
 
http://www.murga-linux.com/puppy/viewtopic.php?mode=attach&id=39756
 
Le problème c'est que je ne sais pas du tout comment y parvenir. Pour un habitué de ce langage (gtk C, C++?), je suppose que ce n'est pas bien sorcier, voire basique et évident (?) de modifier le code ci-dessous pour ajouter l'entrée voulue au menu contextuel de l'icône de la barre des taches:

Code :
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <ctype.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <unistd.h>
  7. #include <errno.h>
  8. #include <locale.h>
  9. #include <string.h>
  10. #include <signal.h>
  11. #include <X11/Xlib.h>
  12. #include <gtk/gtk.h>
  13. #include <gdk/gdk.h>
  14. #include <X11/XKBlib.h>
  15. #include "config.h"
  16. #include "eggtrayicon.h"
  17. #include "version.h"
  18. static gchar version[] = VERSION;
  19. //#define DEBUG
  20. #include "dbg.h"
  21. /******************************************************************
  22. * TYPEDEFS                                                        *
  23. ******************************************************************/
  24. typedef struct _kbd_info {
  25.     gchar *sym;
  26.     gchar *name;
  27.     GdkPixbuf *flag;
  28. } kbd_info;
  29. #define IMGPREFIX PREFIX "/share/fbxkb/images/"
  30. /******************************************************************
  31. * GLOBAL VARSIABLES                                              *
  32. ******************************************************************/
  33. /* X11 common stuff */
  34. static Atom a_XKB_RULES_NAMES;
  35. static Display *dpy;
  36. static int xkb_event_type;
  37. /* internal state mashine */
  38. static int cur_group;
  39. static int ngroups;
  40. static GHashTable *sym2pix;
  41. static kbd_info group2info[XkbNumKbdGroups];
  42. static GdkPixbuf *zzflag;
  43. static int active;
  44. /* gtk gui */
  45. static GtkWidget *flag_menu;
  46. static GtkWidget *app_menu;
  47. static GtkWidget *docklet;
  48. static GtkWidget *image;
  49. static GtkWidget *about_dialog = NULL;
  50. /******************************************************************
  51. * DECLARATION                                                    *
  52. ******************************************************************/
  53. static int init();
  54. static void read_kbd_description();
  55. static void update_flag(int no);
  56. static GdkFilterReturn filter( XEvent *xev, GdkEvent *event, gpointer data);
  57. static void Xerror_handler(Display * d, XErrorEvent * ev);
  58. static GdkPixbuf *sym2flag(char *sym);
  59. static void flag_menu_create();
  60. static void flag_menu_destroy();
  61. static void flag_menu_activated(GtkWidget *widget, gpointer data);
  62. static void app_menu_create();
  63. static void app_menu_about(GtkWidget *widget, gpointer data);
  64. static void app_menu_exit(GtkWidget *widget, gpointer data);
  65. static int docklet_create();
  66. static int create_all();
  67. /******************************************************************
  68. * CODE                                                           *
  69. ******************************************************************/
  70. /******************************************************************
  71. * gtk gui                                                        *
  72. ******************************************************************/
  73. static void
  74. flag_menu_create()
  75. {
  76.     int i;
  77.     GdkPixbuf *flag;
  78.     GtkWidget *mi, *img;
  79.     //static GString *s = NULL;;
  80.    
  81.     ENTER;
  82.     flag_menu =  gtk_menu_new();
  83.     for (i = 0; i < ngroups; i++) {
  84.         mi = gtk_image_menu_item_new_with_label(
  85.             group2info.name ? group2info[i].name : group2info[i].sym);
  86.         g_signal_connect(G_OBJECT(mi), "activate", (GCallback)flag_menu_activated, GINT_TO_POINTER(i));
  87.         gtk_menu_shell_append (GTK_MENU_SHELL (flag_menu), mi);
  88.         gtk_widget_show (mi);
  89.         flag = sym2flag(group2info[i].sym);
  90.         img = gtk_image_new_from_pixbuf(flag);
  91.         gtk_widget_show(img);
  92.         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
  93.     }
  94.     RET();
  95. }
  96. static void
  97. flag_menu_destroy()
  98. {
  99.     if (flag_menu) {
  100.         gtk_widget_destroy(flag_menu);
  101.         flag_menu = NULL;
  102.     }
  103. }
  104. static void
  105. flag_menu_activated(GtkWidget *widget, gpointer data)
  106. {
  107.     int i;
  108.     ENTER;   
  109.     i = GPOINTER_TO_INT(data);
  110.     DBG("asking %d group\n", i);
  111.     XkbLockGroup(dpy, XkbUseCoreKbd, i);
  112.     RET();
  113. }
  114. static void
  115. app_menu_create()
  116. {
  117.     GtkWidget *mi;
  118.    
  119.     ENTER;
  120.     app_menu =  gtk_menu_new();
  121.     mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
  122.     g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_about, NULL);
  123.     gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
  124.     gtk_widget_show (mi);
  125.    
  126.     mi = gtk_menu_item_new ();
  127.     gtk_widget_show (mi);
  128.     gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
  129.     gtk_widget_set_sensitive (mi, FALSE);
  130.     mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
  131.     g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_exit, NULL);
  132.     gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
  133.     gtk_widget_show (mi);
  134.     RET();
  135. }
  136. static void
  137. app_menu_about(GtkWidget *widget, gpointer data)
  138. {
  139.     ENTER;
  140.     if (!about_dialog) {
  141.         about_dialog = gtk_message_dialog_new (NULL,
  142.               GTK_DIALOG_DESTROY_WITH_PARENT,
  143.               GTK_MESSAGE_INFO,
  144.               GTK_BUTTONS_CLOSE,
  145.               "fbxkb %s\nX11 Keyboard switcher\nAuthor: Anatoly Asviyan <aanatoly@users.sf.net>", version);
  146.         /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
  147.         g_signal_connect_swapped (about_dialog, "response",
  148.               G_CALLBACK (gtk_widget_hide),
  149.               about_dialog);
  150.     }
  151.     gtk_widget_show (about_dialog);
  152.     RET();
  153. }
  154. static void
  155. app_menu_exit(GtkWidget *widget, gpointer data)
  156. {
  157.     ENTER;   
  158.     exit(0);
  159.     RET();
  160. }
  161. static void docklet_embedded(GtkWidget *widget, void *data)
  162. {
  163.     ENTER;
  164.     RET();
  165. }
  166. static void docklet_destroyed(GtkWidget *widget, void *data)
  167. {
  168.     ENTER;
  169.     //g_object_unref(G_OBJECT(docklet));
  170.     docklet = NULL;
  171.     g_idle_add(create_all, NULL);
  172.     RET();
  173. }
  174. void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data)
  175. {
  176.     //GtkWidget *menu;
  177.     ENTER;
  178.     if (event->type != GDK_BUTTON_PRESS)
  179.         RET();
  180.     if (event->button == 1) {
  181.         int no;
  182.         no =  (cur_group + 1) % ngroups;
  183.         DBG("no=%d\n", no);
  184.         XkbLockGroup(dpy, XkbUseCoreKbd, no);
  185.     } else if (event->button == 2) {
  186.         gtk_menu_popup(GTK_MENU(flag_menu), NULL, NULL, NULL, NULL, event->button, event->time);
  187.     } else if (event->button == 3) {
  188.         gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, event->button, event->time);
  189.     }
  190.     RET();
  191. }
  192. static int
  193. docklet_create()
  194. {
  195.     GtkWidget *box;
  196.    
  197.     ENTER;
  198.     docklet = (GtkWidget*)egg_tray_icon_new("fbxkb" );
  199.     box     = gtk_event_box_new();
  200.     image   = gtk_image_new();
  201.     //image   = gtk_image_new();
  202.     g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
  203.     g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
  204.     g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL);
  205.     gtk_container_set_border_width(GTK_CONTAINER(box), 0);
  206.    
  207.     gtk_container_add(GTK_CONTAINER(box), image);
  208.     gtk_container_add(GTK_CONTAINER(docklet), box);
  209.     gtk_widget_show_all(GTK_WIDGET(docklet));
  210.    
  211.     RET(1);
  212. }
  213. /******************************************************************
  214. * internal state machine                                         *
  215. ******************************************************************/
  216. static gboolean
  217. my_str_equal (gchar *a, gchar *b)
  218. {
  219.     return  (a[0] == b[0] && a[1] == b[1]);
  220. }
  221.    
  222. static GdkPixbuf *
  223. sym2flag(char *sym)
  224. {
  225.     GdkPixbuf *flag;
  226.     static GString *s = NULL;
  227.     char tmp[3];
  228.    
  229.     ENTER;
  230.     g_assert(sym != NULL && strlen(sym) > 1);
  231.     flag = g_hash_table_lookup(sym2pix, sym);
  232.     if (flag)
  233.         RET(flag);
  234.     if (!s)
  235.         s = g_string_new(IMGPREFIX "tt.png" );
  236.     s->str[s->len-6] = sym[0];
  237.     s->str[s->len-5] = sym[1];
  238.     flag = gdk_pixbuf_new_from_file_at_size(s->str, 24, 24, NULL);
  239.     if (!flag)
  240.         RET(zzflag);
  241.     tmp[0] = sym[0];
  242.     tmp[1] = sym[1];
  243.     tmp[2] = 0;
  244.     g_hash_table_insert(sym2pix, tmp, flag);
  245.     RET(flag);
  246. }
  247. static void
  248. read_kbd_description()
  249. {
  250.     unsigned int mask;
  251.     XkbDescRec *kbd_desc_ptr;
  252.     XkbStateRec xkb_state;
  253.     Atom sym_name_atom;
  254.     int i;
  255.     ENTER;
  256.     // clean up
  257.     cur_group = ngroups = 0;
  258.     for (i = 0; i < XkbNumKbdGroups; i++) {
  259.         g_free(group2info[i].sym);
  260.         g_free(group2info[i].name);
  261.         /*
  262.           if (group2info[i].flag)
  263.             g_object_unref(G_OBJECT(group2info[i].flag));
  264.         */
  265.     }
  266.     bzero(group2info, sizeof(group2info));
  267.     // get kbd info
  268.     mask = XkbControlsMask | XkbServerMapMask;
  269.     kbd_desc_ptr = XkbAllocKeyboard();
  270.     if (!kbd_desc_ptr) {
  271.         ERR("can't alloc kbd info\n" );
  272.         goto out_us;
  273.     }
  274.     kbd_desc_ptr->dpy = dpy;
  275.     if (XkbGetControls(dpy, XkbAllControlsMask, kbd_desc_ptr) != Success) {
  276.         ERR("can't get Xkb controls\n" );
  277.         goto out;
  278.     }
  279.     ngroups = kbd_desc_ptr->ctrls->num_groups;
  280.     if (ngroups < 1)
  281.         goto out;
  282.     if (XkbGetState(dpy, XkbUseCoreKbd, &xkb_state) != Success) {
  283.         ERR("can't get Xkb state\n" );
  284.         goto out;
  285.     }
  286.     cur_group = xkb_state.group;
  287.     DBG("cur_group = %d ngroups = %d\n", cur_group, ngroups);
  288.     g_assert(cur_group < ngroups);
  289.    
  290.     if (XkbGetNames(dpy, XkbSymbolsNameMask, kbd_desc_ptr) != Success) {
  291.         ERR("can't get Xkb symbol description\n" );
  292.         goto out;
  293.     }
  294.     if (XkbGetNames(dpy, XkbGroupNamesMask, kbd_desc_ptr) != Success)
  295.         ERR("Failed to get keyboard description\n" );
  296.     g_assert(kbd_desc_ptr->names);
  297.     sym_name_atom = kbd_desc_ptr->names->symbols;
  298.     // parse kbd info
  299.     if (sym_name_atom != None) {
  300.         char *sym_name, *tmp, *tok;
  301.         int no;
  302.        
  303.         sym_name = XGetAtomName(dpy, sym_name_atom);
  304.         if (!sym_name)
  305.             goto out;
  306.         /* to know how sym_name might look like do this:
  307.          *    % xlsatoms | grep pc
  308.          *    150 pc/pc(pc101)+pc/us+pc/ru(phonetic):2+group(shift_toggle)
  309.          *    470 pc(pc105)+us+ru(phonetic):2+il(phonetic):3+group(shifts_toggle)+group(switch)
  310.          */
  311.         DBG("sym_name=%s\n", sym_name);
  312.         for (tok = strtok(sym_name, "+" ); tok; tok = strtok(NULL, "+" )) {
  313.             DBG("tok=%s\n", tok);
  314.             tmp = strchr(tok, ':');
  315.             if (tmp) {
  316.                 if (sscanf(tmp+1, "%d", &no) != 1)
  317.                     ERR("can't read kbd number\n" );
  318.                 no--;
  319.                 *tmp = 0;
  320.             } else {
  321.                 no = 0;
  322.             }
  323.             for (tmp = tok; isalpha(*tmp); tmp++);
  324.             *tmp = 0;
  325.             DBG("map=%s no=%d\n", tok, no);
  326.             if (!strcmp(tok, "pc" ) || !strcmp(tok, "group" ))
  327.                 continue;
  328.          
  329.             g_assert((no >= 0) && (no < ngroups));
  330.             if (group2info[no].sym != NULL) {
  331.                 ERR("xkb group #%d is already defined\n", no);
  332.             }
  333.             group2info[no].sym = g_strdup(tok);
  334.             group2info[no].flag = sym2flag(tok);
  335.             group2info[no].name = XGetAtomName(dpy, kbd_desc_ptr->names->groups[no]);         
  336.         }
  337.         XFree(sym_name);
  338.     }
  339. out:
  340.     XkbFreeKeyboard(kbd_desc_ptr, 0, True);
  341.     // sanity check: group numbering must be continous
  342.     for (i = 0; (i < XkbNumKbdGroups) && (group2info[i].sym != NULL); i++);
  343.     if (i != ngroups) {
  344.         ERR("kbd group numbering is not continous\n" );
  345.         ERR("run 'xlsatoms | grep pc' to know what hapends\n" );
  346.         exit(1);
  347.     }
  348. out_us:
  349.     //if no groups were defined just add default 'us' kbd group
  350.     if (!ngroups) {
  351.         ngroups = 1;
  352.         cur_group = 0;
  353.         group2info[0].sym = g_strdup("us" );
  354.         group2info[0].flag = sym2flag("us" );
  355.         group2info[0].name = NULL;
  356.         ERR("no kbd groups defined. adding default 'us' group\n" );
  357.     }
  358.     RET();
  359. }
  360. static void update_flag(int no)
  361. {
  362.     kbd_info *k = &group2info[no];
  363.     ENTER;
  364.     g_assert(k != NULL);
  365.     DBG("k->sym=%s\n", k->sym);
  366.     gtk_image_set_from_pixbuf(GTK_IMAGE(image), k->flag);
  367.     RET();
  368. }
  369. static GdkFilterReturn
  370. filter( XEvent *xev, GdkEvent *event, gpointer data)
  371. {
  372.     ENTER;
  373.     if (!active)
  374.         RET(GDK_FILTER_CONTINUE);
  375.  
  376.     if (xev->type ==  xkb_event_type) {
  377.         XkbEvent *xkbev = (XkbEvent *) xev;
  378.         DBG("XkbTypeEvent %d \n", xkbev->any.xkb_type);
  379.         if (xkbev->any.xkb_type == XkbStateNotify) {
  380.             DBG("XkbStateNotify: %d\n", xkbev->state.group);
  381.             cur_group = xkbev->state.group;
  382.             if (cur_group < ngroups)
  383.                 update_flag(cur_group);
  384.         } else if (xkbev->any.xkb_type == XkbNewKeyboardNotify) {       
  385.             DBG("XkbNewKeyboardNotify\n" );
  386.             read_kbd_description();
  387.             //cur_group = 0;
  388.             update_flag(cur_group);
  389.             flag_menu_destroy();
  390.             flag_menu_create(); 
  391.         }
  392.         RET(GDK_FILTER_REMOVE);
  393.     }
  394.     RET(GDK_FILTER_CONTINUE);
  395. }
  396. static int
  397. init()
  398. {
  399.     int dummy;
  400.     ENTER;
  401.     sym2pix  = g_hash_table_new(g_str_hash, (GEqualFunc) my_str_equal);
  402.     dpy = GDK_DISPLAY();
  403.     a_XKB_RULES_NAMES = XInternAtom(dpy, "_XKB_RULES_NAMES", False);
  404.     if (a_XKB_RULES_NAMES == None)
  405.         ERR("_XKB_RULES_NAMES - can't get this atom\n" );
  406.     if (!XkbQueryExtension(dpy, &dummy, &xkb_event_type, &dummy, &dummy, &dummy))
  407.         RET(0);
  408.     DBG("xkb_event_type=%d\n", xkb_event_type);
  409.     XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
  410.           XkbAllStateComponentsMask, XkbGroupStateMask);
  411.     gdk_window_add_filter(NULL, (GdkFilterFunc)filter, NULL);
  412.     zzflag = gdk_pixbuf_new_from_file_at_size(IMGPREFIX "zz.png", 24, 24, NULL);
  413.     RET(1);
  414. }
  415. #if 0
  416. static void
  417. app_menu_destroy()
  418. {
  419.     ENTER;
  420.     if (app_menu) {
  421.         gtk_widget_destroy(app_menu);
  422.         app_menu = NULL;
  423.     }
  424.     RET();
  425. }
  426. static void
  427. destroy_all()
  428. {
  429.     active = 0;
  430.     gdk_window_remove_filter(NULL, (GdkFilterFunc)filter, NULL);
  431.     XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
  432.           XkbAllStateComponentsMask, 0UL);
  433.     flag_menu_destroy();
  434.     app_menu_destroy();
  435. }
  436. #endif
  437. static int
  438. create_all()
  439. {
  440.     ENTER;
  441.     read_kbd_description();
  442.     docklet_create();
  443.     flag_menu_create();
  444.     app_menu_create();
  445.     update_flag(cur_group);
  446.     active = 1;
  447.     RET(FALSE);// FALSE will remove us from idle func
  448. }
  449. int
  450. main(int argc, char *argv[], char *env[])
  451. {
  452.     ENTER;
  453.     setlocale(LC_CTYPE, "" );
  454.     gtk_set_locale();
  455.     gtk_init(&argc, &argv);
  456.     XSetLocaleModifiers("" );
  457.     XSetErrorHandler((XErrorHandler) Xerror_handler);
  458.     if (!init())
  459.         ERR("can't init. exiting\n" );
  460.     create_all();
  461.     gtk_main ();
  462.     RET(0);
  463. }
  464. /********************************************************************/
  465. void
  466. Xerror_handler(Display * d, XErrorEvent * ev)
  467. {
  468.     char buf[256];
  469.     ENTER;
  470.     XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
  471.     ERR( "fbxkb : X error: %s\n", buf);
  472.     RET();
  473. }


L'application a lancer est:
 

Code :
  1. XkbConfigMngr


Pas ce chemin à préciser, vu qu'il peut varier d'un système à l'autre et qu'un raccourci vers cette application se trouve dans  le dossier  /usr/bin.
J'en demande beaucoup et sous estime peut-être le travail de codage que ça demande?
... Mais qui ne risque rien n'a rien!
 
Note: Le nom de l'auteur de la modification apparaitrait évidemment, de droit, dans la fenêtre d'information où figure déjà celui de l'auteur du script original[i] (Anatoly Asviyan)
 
Merci de votre aide éventuelle!
 
Cordialement.

Reply

Marsh Posté le 11-04-2011 à 14:10:56   

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed