Support desktop entry in launchbar.
[lxde/lxpanel.git] / src / plugins / menu.c
CommitLineData
a52c2257
HJYP
1#include <stdlib.h>
2#include <string.h>
3
4#include <gdk-pixbuf/gdk-pixbuf.h>
5#include <glib.h>
08ea5341 6#include <glib/gi18n.h>
a52c2257
HJYP
7
8#include "panel.h"
9#include "misc.h"
10#include "plugin.h"
11#include "bg.h"
12#include "gtkbgbox.h"
13
14//#define DEBUG
15#include "dbg.h"
16
a52c2257
HJYP
17/*
18 * SuxPanel version 0.1
19 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
20 */
21
22/*
23 * menu style code was taken from suxpanel
24 */
25
26
27typedef struct {
28 GtkTooltips *tips;
29 GtkWidget *menu, *box, *bg, *label;
30 gulong handler_id;
31 int iconsize, paneliconsize;
32 GSList *files;
33 gboolean has_system_menu;
34} menup;
35
36static void
37menu_destructor(plugin *p)
38{
39 menup *m = (menup *)p->priv;
40
41 ENTER;
42 g_signal_handler_disconnect(G_OBJECT(m->bg), m->handler_id);
43 gtk_widget_destroy(m->menu);
44 gtk_widget_destroy(m->box);
45 g_free(m);
46 RET();
47}
48
49static void
50spawn_app(GtkWidget *widget, gpointer data)
51{
52 GError *error = NULL;
53
54 ENTER;
55 if (data) {
56 if (! g_spawn_command_line_async(data, &error) ) {
57 ERR("can't spawn %s\nError is %s\n", (char *)data, error->message);
58 g_error_free (error);
59 }
60 }
61 RET();
62}
63
64
65static void
66run_command(GtkWidget *widget, void (*cmd)(void))
67{
68 ENTER;
69 cmd();
70 RET();
71}
72
73static void
74menu_pos(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, GtkWidget *widget)
75{
76 int ox, oy, w, h;
77 plugin *p;
78
79 ENTER;
80 p = g_object_get_data(G_OBJECT(widget), "plugin");
81 gdk_window_get_origin(widget->window, &ox, &oy);
82 w = GTK_WIDGET(menu)->requisition.width;
83 h = GTK_WIDGET(menu)->requisition.height;
84 if (p->panel->orientation == ORIENT_HORIZ) {
85 *x = ox;
86 if (*x + w > gdk_screen_width())
87 *x = ox + widget->allocation.width - w;
88 *y = oy - h;
89 if (*y < 0)
90 *y = oy + widget->allocation.height;
91 } else {
92 *x = ox + widget->allocation.width;
93 if (*x > gdk_screen_width())
94 *x = ox - w;
95 *y = oy;
96 if (*y + h > gdk_screen_height())
97 *y = oy + widget->allocation.height - h;
98 }
99 DBG("widget: x,y=%d,%d w,h=%d,%d\n", ox, oy,
100 widget->allocation.width, widget->allocation.height );
101 DBG("w-h %d %d\n", w, h);
102 *push_in = TRUE;
103 RET();
104}
105
106static void
107reload_system_menu( GtkMenu* menu )
108{
109 GList *children, *child;
110 GtkMenuItem* item;
111 GtkWidget* sub_menu;
112 gint idx;
113 children = gtk_container_get_children( GTK_CONTAINER(menu) );
114 for( child = children, idx = 0; child; child = child->next, ++idx ) {
115 item = GTK_MENU_ITEM( child->data );
116 if( ptk_app_menu_item_has_data( item ) ) {
117 do {
118 item = GTK_MENU_ITEM( child->data );
119 child = child->next;
120 gtk_widget_destroy( GTK_WIDGET(item) );
121 }while( child && ptk_app_menu_item_has_data( child->data ) );
122 ptk_app_menu_insert_items( menu, idx );
123 if( ! child )
124 break;
125 }
126 else if( sub_menu = gtk_menu_item_get_submenu( item ) ) {
127 reload_system_menu( GTK_MENU(sub_menu) );
128 }
129 }
130 g_list_free( children );
131}
132
133static gboolean
134my_button_pressed(GtkWidget *widget, GdkEventButton *event, menup* m)
135{
136 ENTER;
137 if ((event->type == GDK_BUTTON_PRESS)
138 && (event->x >=0 && event->x < widget->allocation.width)
139 && (event->y >=0 && event->y < widget->allocation.height)) {
140 /* reload system menu items if needed */
141 if( m->has_system_menu && ptk_app_menu_need_reload() ) {
142 reload_system_menu( m->menu );
143 }
144 gtk_menu_popup(m->menu,
145 NULL, NULL, (GtkMenuPositionFunc)menu_pos, widget,
146 event->button, event->time);
147 }
148 RET(TRUE);
149}
150
151
152static GtkWidget *
153make_button(plugin *p, gchar *fname, gchar *name, GtkWidget *menu)
154{
155 int w, h;
156 menup *m;
157
158 ENTER;
159 m = (menup *)p->priv;
160 m->menu = menu;
161 if (p->panel->orientation == ORIENT_HORIZ) {
162 w = 10000;
163 h = p->panel->ah;
164 } else {
165 w = p->panel->aw;
166 h = 10000;
167 }
168 m->bg = fb_button_new_from_file_with_label(fname, w, h, 0xFF0000, TRUE,
169 (p->panel->orientation == ORIENT_HORIZ ? name : NULL));
170 gtk_widget_show(m->bg);
171 gtk_box_pack_start(GTK_BOX(m->box), m->bg, FALSE, FALSE, 0);
172 if (p->panel->transparent)
173 gtk_bgbox_set_background(m->bg, BG_ROOT, p->panel->tintcolor, p->panel->alpha);
174
175
176 m->handler_id = g_signal_connect (G_OBJECT (m->bg), "button-press-event",
177 G_CALLBACK (my_button_pressed), m);
178 g_object_set_data(G_OBJECT(m->bg), "plugin", p);
179
180 RET(m->bg);
181}
182
183
184static GtkWidget *
185read_item(plugin *p)
186{
187 line s;
188 gchar *name, *fname, *action;
189 GtkWidget *item;
190 menup *m = (menup *)p->priv;
08ea5341 191 command *cmd_entry = NULL;
a52c2257
HJYP
192
193 ENTER;
194 s.len = 256;
08ea5341
HJYP
195 name = fname = action = NULL;
196
a52c2257
HJYP
197 while (get_line(p->fp, &s) != LINE_BLOCK_END) {
198 if (s.type == LINE_VAR) {
199 if (!g_ascii_strcasecmp(s.t[0], "image"))
200 fname = expand_tilda(s.t[1]);
201 else if (!g_ascii_strcasecmp(s.t[0], "name"))
202 name = g_strdup(s.t[1]);
203 else if (!g_ascii_strcasecmp(s.t[0], "action"))
204 action = g_strdup(s.t[1]);
205 else if (!g_ascii_strcasecmp(s.t[0], "command")) {
206 command *tmp;
207
208 for (tmp = commands; tmp->name; tmp++) {
209 if (!g_ascii_strcasecmp(s.t[1], tmp->name)) {
08ea5341 210 cmd_entry = tmp;
a52c2257
HJYP
211 break;
212 }
213 }
214 } else {
215 ERR( "menu/item: unknown var %s\n", s.t[0]);
216 goto error;
217 }
218 }
219 }
220 /* menu button */
08ea5341
HJYP
221 if( cmd_entry ) /* built-in commands */
222 {
223 item = gtk_image_menu_item_new_with_label( _(cmd_entry->disp_name) );
224 g_signal_connect(G_OBJECT(item), "activate", (GCallback)run_command, cmd_entry->cmd);
225 }
226 else
227 {
228 item = gtk_image_menu_item_new_with_label(name ? name : "");
229 if (action) {
230 g_signal_connect(G_OBJECT(item), "activate", (GCallback)spawn_app, action);
231 }
232 }
a52c2257 233 gtk_container_set_border_width(GTK_CONTAINER(item), 0);
08ea5341 234 g_free(name);
a52c2257
HJYP
235 if (fname) {
236 GtkWidget *img;
237
238 img = gtk_image_new_from_file_scaled(fname, m->iconsize, m->iconsize, TRUE);
239 gtk_widget_show(img);
240 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
241 g_free(fname);
242 }
a52c2257
HJYP
243 RET(item);
244
245 error:
246 g_free(fname);
247 g_free(name);
248 g_free(action);
249 RET(NULL);
250}
251
252static GtkWidget *
253read_separator(plugin *p)
254{
255 line s;
256
257 ENTER;
258 s.len = 256;
259 while (get_line(p->fp, &s) != LINE_BLOCK_END) {
260 ERR("menu: error - separator can not have paramteres\n");
261 RET(NULL);
262 }
263 RET(gtk_separator_menu_item_new());
264}
265
266static void
267read_system_menu(GtkMenu* menu, plugin *p)
268{
269 line s;
270 menup *m = (menup *)p->priv;
271
272 ENTER;
273 s.len = 256;
274 while (get_line(p->fp, &s) != LINE_BLOCK_END) {
275 ERR("menu: error - system can not have paramteres\n");
276 RET();
277 }
278 if( m->has_system_menu ) {
279 ERR("menu: error - only one system is allowed");
280 }
281 ptk_app_menu_insert_items( menu, -1 );
282 m->has_system_menu = TRUE;
283 RET();
284}
285
286static void
287read_include(plugin *p)
288{
289 gchar *name;
290 line s;
291 menup *m = (menup *)p->priv;
292 FILE *fp;
293
294 ENTER;
295 s.len = 256;
296 name = NULL;
297 while (get_line(p->fp, &s) != LINE_BLOCK_END) {
298 if (s.type == LINE_VAR) {
299 if (!g_ascii_strcasecmp(s.t[0], "name"))
300 name = expand_tilda(s.t[1]);
301 else {
302 ERR( "menu/include: unknown var %s\n", s.t[0]);
303 RET();
304 }
305 }
306 }
307 if ((fp = fopen(name, "r"))) {
308 LOG(LOG_INFO, "Including %s\n", name);
309 m->files = g_slist_prepend(m->files, p->fp);
310 p->fp = fp;
311 } else {
312 ERR("Can't include %s\n", name);
313 }
314 if (name) g_free(name);
315 RET();
316}
317
318static GtkWidget *
319read_submenu(plugin *p, gboolean as_item)
320{
321 line s;
322 GtkWidget *mi, *menu;
323 gchar name[256], *fname;
324 menup *m = (menup *)p->priv;
325
326
327 ENTER;
328 s.len = 256;
329 menu = gtk_menu_new ();
330 gtk_container_set_border_width(GTK_CONTAINER(menu), 0);
331
332 fname = 0;
333 name[0] = 0;
334 while (get_line(p->fp, &s) != LINE_BLOCK_END) {
335 if (s.type == LINE_BLOCK_START) {
336 mi = NULL;
337 if (!g_ascii_strcasecmp(s.t[0], "item")) {
338 mi = read_item(p);
339 } else if (!g_ascii_strcasecmp(s.t[0], "separator")) {
340 mi = read_separator(p);
341 } else if (!g_ascii_strcasecmp(s.t[0], "system")) {
342 read_system_menu(menu, p); /* add system menu items */
343 continue;
344 } else if (!g_ascii_strcasecmp(s.t[0], "menu")) {
345 mi = read_submenu(p, TRUE);
346 } else if (!g_ascii_strcasecmp(s.t[0], "include")) {
347 read_include(p);
348 continue;
349 } else {
350 ERR("menu: unknown block %s\n", s.t[0]);
351 goto error;
352 }
353 if (!mi) {
354 ERR("menu: can't create menu item\n");
355 goto error;
356 }
357 gtk_widget_show(mi);
358 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
359 } else if (s.type == LINE_VAR) {
360 if (!g_ascii_strcasecmp(s.t[0], "image"))
361 fname = expand_tilda(s.t[1]);
362 else if (!g_ascii_strcasecmp(s.t[0], "name"))
363 strcpy(name, s.t[1]);
364 else {
365 ERR("menu: unknown var %s\n", s.t[0]);
366 goto error;
367 }
368 } else if (s.type == LINE_NONE) {
369 if (m->files) {
370 fclose(p->fp);
371 p->fp = m->files->data;
372 m->files = g_slist_delete_link(m->files, m->files);
373 }
374 } else {
375 ERR("menu: illegal in this context %s\n", s.str);
376 goto error;
377 }
378 }
379 if (as_item) {
380 mi = gtk_image_menu_item_new_with_label(name ? name : "");
381 if (fname) {
382 GtkWidget *img;
383 img = gtk_image_new_from_file_scaled(fname, m->iconsize, m->iconsize, TRUE);
384 gtk_widget_show(img);
385 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
386 g_free(fname);
387 }
388 gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu);
389 RET(mi);
390 } else {
391 mi = make_button(p, fname, name, menu);
392 if (fname)
393 g_free(fname);
394 RET(mi);
395 }
396
397 error:
398 // FIXME: we need to recursivly destroy all child menus and their items
399 gtk_widget_destroy(menu);
400 g_free(fname);
401 g_free(name);
402 RET(NULL);
403}
404
405
406
407
408static int
409menu_constructor(plugin *p)
410{
411 menup *m;
412
413 ENTER;
414 m = g_new0(menup, 1);
415 g_return_val_if_fail(m != NULL, 0);
416 p->priv = m;
417
418 //gtk_rc_parse_string(menu_rc);
419 if (p->panel->orientation == ORIENT_HORIZ)
420 m->paneliconsize = p->panel->ah
421 - 2* GTK_WIDGET(p->panel->box)->style->ythickness;
422 else
423 m->paneliconsize = p->panel->aw
424 - 2* GTK_WIDGET(p->panel->box)->style->xthickness;
425 m->iconsize = 22;
426
427 m->box = gtk_hbox_new(FALSE, 0);
428 gtk_container_set_border_width(GTK_CONTAINER(m->box), 0);
429 gtk_container_add(GTK_CONTAINER(p->pwid), m->box);
430
431 if (!read_submenu(p, FALSE)) {
432 ERR("menu: plugin init failed\n");
433 goto error;
434 }
435 RET(1);
436
437 error:
438 menu_destructor(p);
439 RET(0);
440}
441
442
443plugin_class menu_plugin_class = {
444 fname: NULL,
445 count: 0,
446
447 type : "menu",
448 name : "menu",
449 version: "1.0",
450 description : "Provide Menu",
451
452 constructor : menu_constructor,
453 destructor : menu_destructor,
454};
455
456
457/*
458if (level == 0) {
459
460 } else
461*/