Rework the "Run" dialog to load all available commands with multi-threading to speed...
[lxde/lxpanel.git] / src / panel.c
1 /**
2 * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <glib/gi18n.h>
24 #include <stdlib.h>
25 #include <glib/gstdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <locale.h>
31 #include <string.h>
32 #include <gdk/gdkx.h>
33
34 #include "plugin.h"
35 #include "panel.h"
36 #include "misc.h"
37 #include "bg.h"
38
39 #include "lxpanelctl.h"
40 #include "dbg.h"
41
42 static gchar *cfgfile = NULL;
43 static gchar version[] = VERSION;
44 gchar *cprofile = "default";
45
46 static GtkWindowGroup* win_grp; /* window group used to limit the scope of model dialog. */
47
48 static int config = 0;
49 FbEv *fbev = NULL;
50
51 GSList* all_panels = NULL; /* a single-linked list storing all panels */
52
53 gboolean is_restarting = FALSE;
54
55 static int panel_start( Panel *p, char **fp );
56 static void panel_start_gui(Panel *p);
57 void panel_config_save(Panel* panel); /* defined in configurator.c */
58
59 gboolean is_in_lxde = FALSE;
60
61 /* Allocate and initialize new Panel structure. */
62 static Panel* panel_allocate(void)
63 {
64 Panel* p = g_new0(Panel, 1);
65 p->allign = ALLIGN_CENTER;
66 p->edge = EDGE_NONE;
67 p->widthtype = WIDTH_PERCENT;
68 p->width = 100;
69 p->heighttype = HEIGHT_PIXEL;
70 p->height = PANEL_HEIGHT_DEFAULT;
71 p->setdocktype = 1;
72 p->setstrut = 1;
73 p->round_corners = 0;
74 p->autohide = 0;
75 p->visible = TRUE;
76 p->height_when_hidden = 2;
77 p->transparent = 0;
78 p->alpha = 127;
79 p->tintcolor = 0xFFFFFFFF;
80 p->usefontcolor = 0;
81 p->fontcolor = 0x00000000;
82 p->spacing = 0;
83 p->icon_size = PANEL_ICON_SIZE;
84 return p;
85 }
86
87 /* Normalize panel configuration after load from file or reconfiguration. */
88 static void panel_normalize_configuration(Panel* p)
89 {
90 panel_set_panel_configuration_changed( p );
91 if (p->width < 0)
92 p->width = 100;
93 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
94 p->width = 100;
95 p->heighttype = HEIGHT_PIXEL;
96 if (p->heighttype == HEIGHT_PIXEL) {
97 if (p->height < PANEL_HEIGHT_MIN)
98 p->height = PANEL_HEIGHT_MIN;
99 else if (p->height > PANEL_HEIGHT_MAX)
100 p->height = PANEL_HEIGHT_MAX;
101 }
102 if (p->background)
103 p->transparent = 0;
104 }
105
106 /****************************************************
107 * panel's handlers for WM events *
108 ****************************************************/
109
110 void panel_set_wm_strut(Panel *p)
111 {
112 int index;
113 gulong strut_size;
114 gulong strut_lower;
115 gulong strut_upper;
116
117 /* Dispatch on edge to set up strut parameters. */
118 switch (p->edge)
119 {
120 case EDGE_LEFT:
121 index = 0;
122 strut_size = p->aw;
123 strut_lower = p->ay;
124 strut_upper = p->ay + p->ah;
125 break;
126 case EDGE_RIGHT:
127 index = 1;
128 strut_size = p->aw;
129 strut_lower = p->ay;
130 strut_upper = p->ay + p->ah;
131 break;
132 case EDGE_TOP:
133 index = 2;
134 strut_size = p->ah;
135 strut_lower = p->ax;
136 strut_upper = p->ax + p->aw;
137 break;
138 case EDGE_BOTTOM:
139 index = 3;
140 strut_size = p->ah;
141 strut_lower = p->ax;
142 strut_upper = p->ax + p->aw;
143 break;
144 default:
145 return;
146 }
147
148 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
149 if ( ! p->visible)
150 strut_size = p->height_when_hidden;
151
152 /* Set up strut value in property format. */
153 gulong desired_strut[12];
154 memset(desired_strut, 0, sizeof(desired_strut));
155 if (p->setstrut)
156 {
157 desired_strut[index] = strut_size;
158 desired_strut[4 + index * 2] = strut_lower;
159 desired_strut[5 + index * 2] = strut_upper;
160 }
161 else
162 {
163 strut_size = 0;
164 strut_lower = 0;
165 strut_upper = 0;
166 }
167
168 /* If strut value changed, set the property value on the panel window.
169 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
170 if ((GTK_WIDGET_MAPPED(p->topgwin))
171 && ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge)))
172 {
173 p->strut_size = strut_size;
174 p->strut_lower = strut_lower;
175 p->strut_upper = strut_upper;
176 p->strut_edge = p->edge;
177
178 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
179 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
180 if (strut_size != 0)
181 {
182 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
183 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
184 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
185 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
186 }
187 else
188 {
189 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
190 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
191 }
192 }
193 }
194
195 /* defined in plugins/menu.c */
196 gboolean show_system_menu( gpointer system_menu );
197
198 /* defined in configurator.c */
199 void panel_configure(Panel* p, int sel_page );
200 gboolean panel_edge_available(Panel* p, int edge);
201
202 /* built-in commands, defined in configurator.c */
203 void restart(void);
204 void gtk_run(void);
205 void panel_destroy(Panel *p);
206
207 static void process_client_msg ( XClientMessageEvent* ev )
208 {
209 int cmd = ev->data.b[0];
210 switch( cmd )
211 {
212 #ifndef DISABLE_MENU
213 case LXPANEL_CMD_SYS_MENU:
214 {
215 GSList* l;
216 for( l = all_panels; l; l = l->next )
217 {
218 Panel* p = (Panel*)l->data;
219 if( p->system_menus )
220 {
221 /* show_system_menu( p->system_menus->data ); */
222 /* FIXME: I've no idea why this doesn't work without timeout
223 under some WMs, like icewm. */
224 g_timeout_add( 200, (GSourceFunc)show_system_menu,
225 p->system_menus->data );
226 }
227 }
228 break;
229 }
230 #endif
231 #ifndef DISABLE_MENU
232 case LXPANEL_CMD_RUN:
233 gtk_run();
234 break;
235 #endif
236 case LXPANEL_CMD_CONFIG:
237 //FIXME: configure();
238 break;
239 case LXPANEL_CMD_RESTART:
240 restart();
241 break;
242 case LXPANEL_CMD_EXIT:
243 gtk_main_quit();
244 break;
245 }
246 }
247
248 static GdkFilterReturn
249 panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
250 {
251 Atom at;
252 Window win;
253 XEvent *ev = (XEvent *) xevent;
254
255 ENTER;
256 DBG("win = 0x%x\n", ev->xproperty.window);
257 if (ev->type != PropertyNotify ) {
258 /* private client message from lxpanelctl */
259 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
260 {
261 process_client_msg( (XClientMessageEvent*)ev );
262 }
263 else if( ev->type == DestroyNotify )
264 {
265 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
266 }
267 RET(GDK_FILTER_CONTINUE);
268 }
269
270 at = ev->xproperty.atom;
271 win = ev->xproperty.window;
272 if (win == GDK_ROOT_WINDOW()) {
273 if (at == a_NET_CLIENT_LIST) {
274 fb_ev_emit(fbev, EV_CLIENT_LIST);
275 } else if (at == a_NET_CURRENT_DESKTOP) {
276 GSList* l;
277 for( l = all_panels; l; l = l->next )
278 ((Panel*)l->data)->curdesk = get_net_current_desktop();
279 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
280 } else if (at == a_NET_NUMBER_OF_DESKTOPS) {
281 GSList* l;
282 for( l = all_panels; l; l = l->next )
283 ((Panel*)l->data)->desknum = get_net_number_of_desktops();
284 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
285 } else if (at == a_NET_DESKTOP_NAMES) {
286 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
287 } else if (at == a_NET_ACTIVE_WINDOW) {
288 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
289 } else if (at == a_NET_CLIENT_LIST_STACKING) {
290 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
291 } else if (at == a_XROOTPMAP_ID) {
292 GSList* l;
293 for( l = all_panels; l; l = l->next )
294 {
295 Panel* p = (Panel*)l->data;
296 if (p->transparent) {
297 fb_bg_notify_changed_bg(p->bg);
298 }
299 }
300 } else if (at == a_NET_WORKAREA) {
301 GSList* l;
302 for( l = all_panels; l; l = l->next )
303 {
304 Panel* p = (Panel*)l->data;
305 g_free( p->workarea );
306 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
307 /* print_wmdata(p); */
308 }
309 } else
310 return GDK_FILTER_CONTINUE;
311
312 return GDK_FILTER_REMOVE;
313 }
314 return GDK_FILTER_CONTINUE;
315 }
316
317 /****************************************************
318 * panel's handlers for GTK events *
319 ****************************************************/
320
321
322 static gint
323 panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
324 {
325 ENTER;
326 RET(FALSE);
327 }
328
329 static gint
330 panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
331 {
332 //Panel *p = (Panel *) data;
333 //if (!p->self_destroy)
334 gtk_main_quit();
335 RET(FALSE);
336 }
337
338 static void
339 on_root_bg_changed(FbBg *bg, Panel* p)
340 {
341 panel_update_background( p );
342 }
343
344 void panel_determine_background_pixmap(Panel * p, GtkWidget * widget, GdkWindow * window)
345 {
346 GdkPixmap * pixmap = NULL;
347
348 /* Free p->bg if it is not going to be used. */
349 if (( ! p->transparent) && (p->bg != NULL))
350 {
351 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
352 g_object_unref(p->bg);
353 p->bg = NULL;
354 }
355
356 if (p->background)
357 {
358 /* User specified background pixmap. */
359 if (p->background_file != NULL)
360 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
361 }
362
363 else if (p->transparent)
364 {
365 /* Transparent. Determine the appropriate value from the root pixmap. */
366 if (p->bg == NULL)
367 {
368 p->bg = fb_bg_get_for_display();
369 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), p);
370 }
371 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
372 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
373 fb_bg_composite(pixmap, widget->style->black_gc, p->tintcolor, p->alpha);
374 }
375
376 if (pixmap != NULL)
377 {
378 gtk_widget_set_app_paintable(widget, TRUE );
379 gdk_window_set_back_pixmap(window, pixmap, FALSE);
380 g_object_unref(pixmap);
381 }
382 else
383 gtk_widget_set_app_paintable(widget, FALSE);
384 }
385
386 /* Update the background of the entire panel.
387 * This function should only be called after the panel has been realized. */
388 void panel_update_background(Panel * p)
389 {
390 /* Redraw the top level widget. */
391 panel_determine_background_pixmap(p, p->topgwin, p->topgwin->window);
392 gdk_window_clear(p->topgwin->window);
393 gtk_widget_queue_draw(p->topgwin);
394
395 /* Loop over all plugins redrawing each plugin. */
396 GList * l;
397 for (l = p->plugins; l != NULL; l = l->next)
398 {
399 Plugin * pl = (Plugin *) l->data;
400 if (pl->pwid != NULL)
401 plugin_widget_set_background(pl->pwid, p);
402 }
403
404 }
405
406 static gboolean delay_update_background( Panel* p )
407 {
408 /* Panel could be destroyed while background update scheduled */
409 if ( p->topgwin && GTK_WIDGET_REALIZED ( p->topgwin ) ) {
410 gdk_display_sync( gtk_widget_get_display(p->topgwin) );
411 panel_update_background( p );
412 }
413
414 return FALSE;
415 }
416
417 static void
418 panel_realize(GtkWidget *widget, Panel *p)
419 {
420 g_idle_add_full( G_PRIORITY_LOW,
421 (GSourceFunc)delay_update_background, p, NULL );
422 }
423
424 static void
425 panel_style_set(GtkWidget *widget, GtkStyle* prev, Panel *p)
426 {
427 /* FIXME: This dirty hack is used to fix the background of systray... */
428 if( GTK_WIDGET_REALIZED( widget ) )
429 g_idle_add_full( G_PRIORITY_LOW,
430 (GSourceFunc)delay_update_background, p, NULL );
431 }
432
433 static gint
434 panel_size_req(GtkWidget *widget, GtkRequisition *req, Panel *p)
435 {
436 ENTER;
437
438 if (p->widthtype == WIDTH_REQUEST)
439 p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height;
440 if (p->heighttype == HEIGHT_REQUEST)
441 p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width;
442 calculate_position(p);
443 req->width = p->aw;
444 req->height = p->ah;
445
446 RET( TRUE );
447 }
448
449 static gint
450 panel_size_alloc(GtkWidget *widget, GtkAllocation *a, Panel *p)
451 {
452 ENTER;
453 if (p->widthtype == WIDTH_REQUEST)
454 p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height;
455 if (p->heighttype == HEIGHT_REQUEST)
456 p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width;
457 calculate_position(p);
458
459 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
460 RET(TRUE);
461 }
462
463 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
464 panel_set_wm_strut(p);
465 RET(TRUE);
466 }
467
468 static gboolean
469 panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, Panel *p)
470 {
471 ENTER;
472 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
473 RET(TRUE);
474 p->cw = e->width;
475 p->ch = e->height;
476 p->cx = e->x;
477 p->cy = e->y;
478
479 if (p->transparent)
480 fb_bg_notify_changed_bg(p->bg);
481
482 RET(FALSE);
483 }
484
485 static gint
486 panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
487 {
488 panel_configure( (Panel*)user_data, 0 );
489 return TRUE;
490 }
491
492 /* Handler for "button_press_event" signal with Panel as parameter. */
493 static gboolean panel_button_press_event_with_panel(GtkWidget *widget, GdkEventButton *event, Panel *panel)
494 {
495 if (event->button == 3) /* right button */
496 {
497 GtkMenu* popup = (GtkMenu*) lxpanel_get_panel_menu(panel, NULL, FALSE);
498 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
499 return TRUE;
500 }
501 return FALSE;
502 }
503
504 static void panel_popupmenu_config_plugin( GtkMenuItem* item, Plugin* plugin )
505 {
506 plugin->class->config( plugin, GTK_WINDOW(plugin->panel->topgwin) );
507
508 /* FIXME: this should be more elegant */
509 plugin->panel->config_changed = TRUE;
510 }
511
512 static void panel_popupmenu_add_item( GtkMenuItem* item, Panel* panel )
513 {
514 /* panel_add_plugin( panel, panel->topgwin ); */
515 panel_configure( panel, 2 );
516 }
517
518 static void panel_popupmenu_remove_item( GtkMenuItem* item, Plugin* plugin )
519 {
520 Panel* panel = plugin->panel;
521 panel->plugins = g_list_remove( panel->plugins, plugin );
522 plugin_delete(plugin);
523 panel_config_save( plugin->panel );
524 }
525
526 /* FIXME: Potentially we can support multiple panels at the same edge,
527 * but currently this cannot be done due to some positioning problems. */
528 static char* gen_panel_name( int edge )
529 {
530 const char* edge_str = num2str( edge_pair, edge, "" );
531 char* name = NULL;
532 char* dir = get_config_file( cprofile, "panels", FALSE );
533 int i;
534 for( i = 0; i < G_MAXINT; ++i )
535 {
536 char* f;
537 if( G_LIKELY( i > 0 ) )
538 name = g_strdup_printf( "%s%d", edge_str, i );
539 else
540 name = g_strdup( edge_str );
541 f = g_build_filename( dir, name, NULL );
542 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
543 {
544 g_free( f );
545 break;
546 }
547 g_free( name );
548 g_free( f );
549 }
550 g_free( dir );
551 return name;
552 }
553
554 static void try_allocate_edge(Panel* p, int edge)
555 {
556 if ((p->edge == EDGE_NONE) && (panel_edge_available(p, edge)))
557 p->edge = edge;
558 }
559
560 /* FIXME: Potentially we can support multiple panels at the same edge,
561 * but currently this cannot be done due to some positioning problems. */
562 static void panel_popupmenu_create_panel( GtkMenuItem* item, Panel* panel )
563 {
564 Panel* new_panel = panel_allocate();
565
566 /* Allocate the edge. */
567 try_allocate_edge(new_panel, EDGE_BOTTOM);
568 try_allocate_edge(new_panel, EDGE_TOP);
569 try_allocate_edge(new_panel, EDGE_LEFT);
570 try_allocate_edge(new_panel, EDGE_RIGHT);
571 new_panel->name = gen_panel_name(new_panel->edge);
572
573 panel_configure(new_panel, 0);
574 panel_normalize_configuration(new_panel);
575 panel_start_gui(new_panel);
576 gtk_widget_show_all(new_panel->topgwin);
577
578 panel_config_save(new_panel);
579 all_panels = g_slist_prepend(all_panels, new_panel);
580 }
581
582 static void panel_popupmenu_delete_panel( GtkMenuItem* item, Panel* panel )
583 {
584 GtkWidget* dlg;
585 gboolean ok;
586 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel->topgwin),
587 GTK_DIALOG_MODAL,
588 GTK_MESSAGE_QUESTION,
589 GTK_BUTTONS_OK_CANCEL,
590 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
591 panel_apply_icon(GTK_WINDOW(dlg));
592 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
593 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
594 gtk_widget_destroy( dlg );
595 if( ok )
596 {
597 gchar *fname, *dir;
598 all_panels = g_slist_remove( all_panels, panel );
599
600 /* delete the config file of this panel */
601 dir = get_config_file( cprofile, "panels", FALSE );
602 fname = g_build_filename( dir, panel->name, NULL );
603 g_free( dir );
604 g_unlink( fname );
605 panel->config_changed = 0;
606 panel_destroy( panel );
607 }
608 }
609
610 static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
611 {
612 GtkWidget *about;
613 const gchar* authors[] = {
614 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
615 "Jim Huang <jserv.tw@gmail.com>",
616 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
617 "Fred Chien <cfsghost@gmail.com>",
618 "Daniel Kesler <kesler.daniel@gmail.com>",
619 "Juergen Hoetzel <juergen@archlinux.org>",
620 "Marty Jack <martyj19@comcast.net>",
621 NULL
622 };
623 /* TRANSLATORS: Replace this string with your names, one name per line. */
624 gchar *translators = _( "translator-credits" );
625
626 about = gtk_about_dialog_new();
627 panel_apply_icon(GTK_WINDOW(about));
628 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
629 gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
630 gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL));
631 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2009"));
632 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
633 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about), "This program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.");
634 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
635 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
636 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
637 gtk_dialog_run(GTK_DIALOG(about));
638 gtk_widget_destroy(about);
639 }
640
641 void panel_apply_icon( GtkWindow *w )
642 {
643 gtk_window_set_icon_from_file(w, PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL);
644 }
645
646 GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
647 {
648 GtkWidget *menu_item, *img;
649 GtkMenu *ret,*menu;
650
651 char* tmp;
652 ret = menu = GTK_MENU(gtk_menu_new());
653
654 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
655 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
656 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
657 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
658 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
659
660 if( plugin )
661 {
662 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
663 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
664 menu_item = gtk_image_menu_item_new_with_label( tmp );
665 g_free( tmp );
666 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
667 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
668 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
669 }
670
671 menu_item = gtk_separator_menu_item_new();
672 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
673
674 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
675 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
676 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
677 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
678 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
679
680 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
681 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
682 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
683 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
684 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
685 /* FIXME: Potentially we can support multiple panels at the same edge,
686 * but currently this cannot be done due to some positioning problems. */
687 /* currently, disable the option when there are already four panels */
688 if( g_slist_length( all_panels ) >= 4 )
689 gtk_widget_set_sensitive( menu_item, FALSE );
690
691 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
692 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
693 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
694 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
695 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
696 if( ! all_panels->next ) /* if this is the only panel */
697 gtk_widget_set_sensitive( menu_item, FALSE );
698
699 menu_item = gtk_separator_menu_item_new();
700 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
701
702 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
703 menu_item = gtk_image_menu_item_new_with_label(_("About"));
704 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
705 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
706 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel );
707
708 if( use_sub_menu )
709 {
710 ret = GTK_MENU(gtk_menu_new());
711 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
712 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
713 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
714
715 gtk_widget_show_all(GTK_WIDGET(ret));
716 }
717
718 if( plugin )
719 {
720 menu_item = gtk_separator_menu_item_new();
721 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
722
723 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
724 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
725 menu_item = gtk_image_menu_item_new_with_label( tmp );
726 g_free( tmp );
727 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
728 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
729 if( plugin->class->config )
730 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
731 else
732 gtk_widget_set_sensitive( menu_item, FALSE );
733 }
734
735 gtk_widget_show_all(GTK_WIDGET(menu));
736
737 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
738 return ret;
739 }
740
741 /****************************************************
742 * panel creation *
743 ****************************************************/
744
745 static void
746 make_round_corners(Panel *p)
747 {
748 /* FIXME: This should be re-written with shape extension of X11 */
749 /* gdk_window_shape_combine_mask() can be used */
750 }
751
752 void panel_set_dock_type(Panel *p)
753 {
754 if (p->setdocktype) {
755 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
756 XChangeProperty(GDK_DISPLAY(), p->topxwin,
757 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
758 PropModeReplace, (unsigned char *) &state, 1);
759 }
760 else {
761 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
762 }
763 }
764
765 static void panel_set_visibility(Panel *p, gboolean visible)
766 {
767 if ( ! visible) gtk_widget_hide(p->box);
768 p->visible = visible;
769 calculate_position(p);
770 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
771 gdk_window_move(p->topgwin->window, p->ax, p->ay);
772 if (visible) gtk_widget_show(p->box);
773 panel_set_wm_strut(p);
774 }
775
776 static gboolean panel_leave_real(Panel *p)
777 {
778 if (gdk_display_pointer_is_grabbed(p->display))
779 return TRUE;
780
781 gint x, y;
782 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
783 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
784 return TRUE;
785
786 if ((p->autohide) && (p->visible))
787 panel_set_visibility(p, FALSE);
788
789 p->hide_timeout = 0;
790 return FALSE;
791 }
792
793 static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
794 {
795 if (p->hide_timeout)
796 return FALSE;
797
798 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
799
800 panel_set_visibility(p, TRUE);
801 return TRUE;
802 }
803
804 static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
805 gint y, guint time, Panel *p)
806 {
807 panel_enter(NULL, NULL, p);
808 return TRUE;
809 }
810
811 void panel_establish_autohide(Panel *p)
812 {
813 if (p->autohide)
814 {
815 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
816 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
817 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
818 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
819 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
820 panel_enter(NULL, NULL, p);
821 }
822 else if ( ! p->visible)
823 {
824 gtk_widget_show(p->box);
825 p->visible = TRUE;
826 }
827 }
828
829 /* Set an image from a file with scaling to the panel icon size. */
830 void panel_image_set_from_file(Panel * p, GtkWidget * image, char * file)
831 {
832 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
833 if (pixbuf != NULL)
834 {
835 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
836 g_object_unref(pixbuf);
837 }
838 }
839
840 static void
841 panel_start_gui(Panel *p)
842 {
843 Atom state[3];
844 XWMHints wmhints;
845 guint32 val;
846
847 ENTER;
848
849 p->curdesk = get_net_current_desktop();
850 p->desknum = get_net_number_of_desktops();
851 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
852
853 // main toplevel window
854 p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
855 p->display = gdk_display_get_default();
856 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
857 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
858 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
859 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
860 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
861 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
862
863 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
864
865 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
866 G_CALLBACK(panel_delete_event), p);
867 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
868 G_CALLBACK(panel_destroy_event), p);
869 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
870 (GCallback) panel_size_req, p);
871 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
872 (GCallback) panel_size_alloc, p);
873 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
874 (GCallback) panel_configure_event, p);
875
876 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
877 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
878 (GCallback) panel_button_press_event_with_panel, p);
879
880 g_signal_connect (G_OBJECT (p->topgwin), "realize",
881 (GCallback) panel_realize, p);
882
883 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
884 (GCallback)panel_style_set, p);
885 gtk_widget_realize(p->topgwin);
886 //gdk_window_set_decorations(p->topgwin->window, 0);
887
888 // main layout manager as a single child of panel
889 p->box = p->my_box_new(FALSE, 0);
890 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
891 // gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
892 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
893 gtk_widget_show(p->box);
894 if (p->round_corners)
895 make_round_corners(p);
896
897 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
898 DBG("topxwin = %x\n", p->topxwin);
899
900 /* the settings that should be done before window is mapped */
901 wmhints.flags = InputHint;
902 wmhints.input = 0;
903 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
904 #define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
905 val = WIN_HINTS_SKIP_FOCUS;
906 XChangeProperty(GDK_DISPLAY(), p->topxwin,
907 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
908 PropModeReplace, (unsigned char *) &val, 1);
909
910 panel_set_dock_type(p);
911
912 /* window mapping point */
913 gtk_widget_show_all(p->topgwin);
914
915 /* the settings that should be done after window is mapped */
916 panel_establish_autohide(p);
917
918 /* send it to running wm */
919 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
920 /* and assign it ourself just for case when wm is not running */
921 val = 0xFFFFFFFF;
922 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
923 PropModeReplace, (unsigned char *) &val, 1);
924
925 state[0] = a_NET_WM_STATE_SKIP_PAGER;
926 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
927 state[2] = a_NET_WM_STATE_STICKY;
928 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
929 32, PropModeReplace, (unsigned char *) state, 3);
930
931 calculate_position(p);
932 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
933 panel_set_wm_strut(p);
934
935 RET();
936 }
937
938 /* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
939 void panel_adjust_geometry_terminology(Panel * p)
940 {
941 if ((p->height_label != NULL) && (p->width_label != NULL))
942 {
943 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
944 {
945 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
946 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
947 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
948 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
949 }
950 else
951 {
952 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
953 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
954 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
955 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
956 }
957 }
958 }
959
960 /* Draw text into a label, with the user preference color and optionally bold. */
961 void panel_draw_label_text(Panel * p, GtkWidget * label, char * text, gboolean bold, gboolean custom_color)
962 {
963 char buffer[512];
964
965 if (text == NULL)
966 {
967 /* Null string. */
968 gtk_label_set_text(GTK_LABEL(label), NULL);
969 }
970
971 else
972 {
973 /* Compute an appropriate size so the font will scale with the panel's icon size. */
974 int font_desc;
975 if (p->icon_size < 20)
976 font_desc = 9;
977 else if (p->icon_size >= 20 && p->icon_size < 26)
978 font_desc = 10;
979 else
980 font_desc = 12;
981
982 /* Check the string for characters that need to be escaped.
983 * If any are found, create the properly escaped string and use it instead. */
984 char * valid_markup = text;
985 char * escaped_text = NULL;
986 char * q;
987 for (q = text; *q != '\0'; q += 1)
988 {
989 if ((*q == '<') || (*q == '>') || (*q == '&'))
990 {
991 escaped_text = g_markup_escape_text(text, -1);
992 valid_markup = escaped_text;
993 break;
994 }
995 }
996
997 if ((custom_color) && (p->usefontcolor))
998 {
999 /* Color, optionally bold. */
1000 g_snprintf(buffer, sizeof(buffer), "<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
1001 font_desc,
1002 gcolor2rgb24(&p->gfontcolor),
1003 ((bold) ? "<b>" : ""),
1004 valid_markup,
1005 ((bold) ? "</b>" : ""));
1006 gtk_label_set_markup(GTK_LABEL(label), buffer);
1007 }
1008 else
1009 {
1010 /* No color, optionally bold. */
1011 g_snprintf(buffer, sizeof(buffer), "<span font_desc=\"%d\">%s%s%s</span>",
1012 font_desc,
1013 ((bold) ? "<b>" : ""),
1014 valid_markup,
1015 ((bold) ? "</b>" : ""));
1016 gtk_label_set_markup(GTK_LABEL(label), buffer);
1017 }
1018 g_free(escaped_text);
1019 }
1020 }
1021
1022 void panel_set_panel_configuration_changed(Panel *p)
1023 {
1024 GList* l;
1025
1026 int previous_orientation = p->orientation;
1027 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1028 ? ORIENT_HORIZ : ORIENT_VERT;
1029
1030 if (previous_orientation != p->orientation)
1031 {
1032 panel_adjust_geometry_terminology(p);
1033 p->height = ((p->orientation == ORIENT_HORIZ) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1034 if (p->height_control != NULL)
1035 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
1036 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1037 {
1038 int value = ((p->orientation == ORIENT_HORIZ) ? gdk_screen_width() : gdk_screen_height());
1039 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1040 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1041 }
1042
1043 }
1044
1045 if (p->orientation == ORIENT_HORIZ) {
1046 p->my_box_new = gtk_hbox_new;
1047 p->my_separator_new = gtk_vseparator_new;
1048 } else {
1049 p->my_box_new = gtk_vbox_new;
1050 p->my_separator_new = gtk_hseparator_new;
1051 }
1052
1053 /* recreate the main layout box */
1054 if (p->box != NULL)
1055 {
1056 #if GTK_CHECK_VERSION(2,16,0)
1057 GtkOrientation bo = (p->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1058 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), bo);
1059 #else
1060 GtkBox * newbox = GTK_BOX(recreate_box(GTK_BOX(p->box), p->orientation));
1061 if (GTK_WIDGET(newbox) != p->box)
1062 {
1063 p->box = GTK_WIDGET(newbox);
1064 gtk_container_add(GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox));
1065 }
1066 #endif
1067 }
1068
1069 /* NOTE: This loop won't be executed when panel started since
1070 plugins are not loaded at that time.
1071 This is used when the orientation of the panel is changed
1072 from the config dialog, and plugins should be re-layout.
1073 */
1074 for( l = p->plugins; l; l = l->next ) {
1075 Plugin* pl = (Plugin*)l->data;
1076 if( pl->class->panel_configuration_changed ) {
1077 pl->class->panel_configuration_changed( pl );
1078 }
1079 }
1080 }
1081
1082 static int
1083 panel_parse_global(Panel *p, char **fp)
1084 {
1085 line s;
1086 s.len = 256;
1087
1088 if( G_LIKELY( fp ) )
1089 {
1090 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1091 if (s.type == LINE_VAR) {
1092 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
1093 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
1094 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
1095 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
1096 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
1097 p->margin = atoi(s.t[1]);
1098 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
1099 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
1100 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
1101 p->width = atoi(s.t[1]);
1102 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
1103 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
1104 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
1105 p->height = atoi(s.t[1]);
1106 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1107 p->spacing = atoi(s.t[1]);
1108 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
1109 p->setdocktype = str2num(bool_pair, s.t[1], 0);
1110 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
1111 p->setstrut = str2num(bool_pair, s.t[1], 0);
1112 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
1113 p->round_corners = str2num(bool_pair, s.t[1], 0);
1114 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
1115 p->transparent = str2num(bool_pair, s.t[1], 0);
1116 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
1117 p->alpha = atoi(s.t[1]);
1118 if (p->alpha > 255)
1119 p->alpha = 255;
1120 } else if (!g_ascii_strcasecmp(s.t[0], "AutoHide")) {
1121 p->autohide = str2num(bool_pair, s.t[1], 0);
1122 } else if (!g_ascii_strcasecmp(s.t[0], "HeightWhenHidden")) {
1123 p->height_when_hidden = atoi(s.t[1]);
1124 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
1125 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
1126 gdk_color_parse ("white", &p->gtintcolor);
1127 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1128 DBG("tintcolor=%x\n", p->tintcolor);
1129 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
1130 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
1131 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
1132 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1133 gdk_color_parse ("black", &p->gfontcolor);
1134 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1135 DBG("fontcolor=%x\n", p->fontcolor);
1136 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1137 p->background = str2num(bool_pair, s.t[1], 0);
1138 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1139 p->background_file = g_strdup( s.t[1] );
1140 } else if (!g_ascii_strcasecmp(s.t[0], "IconSize")) {
1141 p->icon_size = atoi(s.t[1]);
1142 } else {
1143 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
1144 }
1145 } else if (s.type == LINE_BLOCK_END) {
1146 break;
1147 } else {
1148 ERR( "lxpanel: illegal in this context %s\n", s.str);
1149 RET(0);
1150 }
1151 }
1152 }
1153
1154 panel_normalize_configuration(p);
1155
1156 return 1;
1157 }
1158
1159 static int
1160 panel_parse_plugin(Panel *p, char **fp)
1161 {
1162 line s;
1163 Plugin *plug = NULL;
1164 gchar *type = NULL;
1165 int expand , padding, border;
1166 char* pconfig = NULL;
1167
1168 ENTER;
1169 s.len = 256;
1170 border = expand = padding = 0;
1171 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
1172 if (s.type == LINE_NONE) {
1173 ERR( "lxpanel: bad line %s\n", s.str);
1174 goto error;
1175 }
1176 if (s.type == LINE_VAR) {
1177 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1178 type = g_strdup(s.t[1]);
1179 DBG("plug %s\n", type);
1180 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1181 expand = str2num(bool_pair, s.t[1], 0);
1182 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1183 padding = atoi(s.t[1]);
1184 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1185 border = atoi(s.t[1]);
1186 else {
1187 ERR( "lxpanel: unknown var %s\n", s.t[0]);
1188 }
1189 } else if (s.type == LINE_BLOCK_START) {
1190 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
1191 pconfig = *fp;
1192 int pno = 1;
1193 while (pno) {
1194 get_line_as_is(fp, &s);
1195 if (s.type == LINE_NONE) {
1196 ERR( "lxpanel: unexpected eof\n");
1197 goto error;
1198 } else if (s.type == LINE_BLOCK_START) {
1199 pno++;
1200 } else if (s.type == LINE_BLOCK_END) {
1201 pno--;
1202 }
1203 }
1204 } else {
1205 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1206 goto error;
1207 }
1208 } else {
1209 ERR( "lxpanel: illegal in this context %s\n", s.str);
1210 goto error;
1211 }
1212 }
1213
1214 if (!type || !(plug = plugin_load(type))) {
1215 ERR( "lxpanel: can't load %s plugin\n", type);
1216 goto error;
1217 }
1218
1219 plug->panel = p;
1220 if (plug->class->expand_available) plug->expand = expand;
1221 plug->padding = padding;
1222 plug->border = border;
1223 DBG("starting\n");
1224 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
1225 ERR( "lxpanel: can't start plugin %s\n", type);
1226 goto error;
1227 }
1228 DBG("plug %s\n", type);
1229 p->plugins = g_list_append(p->plugins, plug);
1230
1231 g_free( type );
1232 RET(1);
1233
1234 error:
1235 if (plug != NULL)
1236 plugin_unload(plug);
1237 g_free(type);
1238 RET(0);
1239 }
1240
1241 int panel_start( Panel *p, char **fp )
1242 {
1243 line s;
1244
1245 /* parse global section */
1246 ENTER;
1247 s.len = 256;
1248
1249 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
1250 ERR( "lxpanel: config file must start from Global section\n");
1251 RET(0);
1252 }
1253 if (!panel_parse_global(p, fp))
1254 RET(0);
1255
1256 panel_start_gui(p);
1257
1258 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1259 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1260 ERR( "lxpanel: expecting Plugin section\n");
1261 RET(0);
1262 }
1263 panel_parse_plugin(p, fp);
1264 }
1265
1266 /* update backgrond of panel and all plugins */
1267 panel_update_background( p );
1268 return 1;
1269 }
1270
1271 static void
1272 delete_plugin(gpointer data, gpointer udata)
1273 {
1274 plugin_delete((Plugin *)data);
1275 }
1276
1277 void panel_destroy(Panel *p)
1278 {
1279 ENTER;
1280
1281 if (p->pref_dialog != NULL)
1282 gtk_widget_destroy(p->pref_dialog);
1283 if (p->plugin_pref_dialog != NULL)
1284 {
1285 gtk_widget_destroy(p->plugin_pref_dialog);
1286 p->plugin_pref_dialog = NULL;
1287 }
1288
1289 if (p->bg != NULL)
1290 {
1291 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1292 g_object_unref(p->bg);
1293 }
1294
1295 if( p->config_changed )
1296 panel_config_save( p );
1297
1298 g_list_foreach(p->plugins, delete_plugin, NULL);
1299 g_list_free(p->plugins);
1300 p->plugins = NULL;
1301
1302 if( p->system_menus ){
1303 do{
1304 } while ( g_source_remove_by_user_data( p->system_menus ) );
1305 }
1306
1307 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
1308
1309 if( p->topgwin )
1310 gtk_widget_destroy(p->topgwin);
1311 g_free(p->workarea);
1312 g_free( p->background_file );
1313 g_slist_free( p->system_menus );
1314 gdk_flush();
1315 XFlush(GDK_DISPLAY());
1316 XSync(GDK_DISPLAY(), True);
1317
1318 g_free( p->name );
1319 g_free(p);
1320 RET();
1321 }
1322
1323 Panel* panel_new( const char* config_file, const char* config_name )
1324 {
1325 char *fp, *pfp; /* point to current position of profile data in memory */
1326 Panel* panel = NULL;
1327
1328 if( G_LIKELY(config_file) )
1329 {
1330 g_file_get_contents( config_file, &fp, NULL, NULL );
1331 if( fp )
1332 {
1333 panel = panel_allocate();
1334 panel->name = g_strdup( config_name );
1335 pfp = fp;
1336
1337 if (! panel_start( panel, &pfp )) {
1338 ERR( "lxpanel: can't start panel\n");
1339 panel_destroy( panel );
1340 panel = NULL;
1341 }
1342
1343 g_free( fp );
1344 }
1345 }
1346 return panel;
1347 }
1348
1349 static void
1350 usage()
1351 {
1352 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1353 g_print(_("Command line options:\n"));
1354 g_print(_(" --help -- print this help and exit\n"));
1355 g_print(_(" --version -- print version and exit\n"));
1356 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
1357 // g_print(_(" --configure -- launch configuration utility\n"));
1358 g_print(_(" --profile name -- use specified profile\n"));
1359 g_print("\n");
1360 g_print(_(" -h -- same as --help\n"));
1361 g_print(_(" -p -- same as --profile\n"));
1362 g_print(_(" -v -- same as --version\n"));
1363 // g_print(_(" -C -- same as --configure\n"));
1364 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
1365 }
1366
1367 int panel_handle_x_error(Display * d, XErrorEvent * ev)
1368 {
1369 char buf[256];
1370
1371 if (log_level >= LOG_WARN) {
1372 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1373 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1374 }
1375 return 0; /* Ignored */
1376 }
1377
1378 int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1379 {
1380 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1381 panel_handle_x_error(d, ev);
1382 return 0; /* Ignored */
1383 }
1384
1385 /* Lightweight lock related functions - X clipboard hacks */
1386
1387 #define CLIPBOARD_NAME "LXPANEL_SELECTION"
1388
1389 /*
1390 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1391 */
1392 static void
1393 clipboard_get_func(
1394 GtkClipboard *clipboard G_GNUC_UNUSED,
1395 GtkSelectionData *selection_data G_GNUC_UNUSED,
1396 guint info G_GNUC_UNUSED,
1397 gpointer user_data_or_owner G_GNUC_UNUSED)
1398 {
1399 }
1400
1401 /*
1402 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1403 */
1404 static void clipboard_clear_func(
1405 GtkClipboard *clipboard G_GNUC_UNUSED,
1406 gpointer user_data_or_owner G_GNUC_UNUSED)
1407 {
1408 }
1409
1410 /*
1411 * Lightweight version for checking single instance.
1412 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1413 *
1414 * Returns TRUE if successfully retrieved and FALSE otherwise.
1415 */
1416 static gboolean check_main_lock()
1417 {
1418 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1419 gboolean retval = FALSE;
1420 GtkClipboard *clipboard;
1421 Atom atom;
1422
1423 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1424
1425 XGrabServer(GDK_DISPLAY());
1426
1427 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1428 goto out;
1429
1430 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1431
1432 if (gtk_clipboard_set_with_data(clipboard, targets,
1433 G_N_ELEMENTS (targets),
1434 clipboard_get_func,
1435 clipboard_clear_func, NULL))
1436 retval = TRUE;
1437
1438 out:
1439 XUngrabServer (GDK_DISPLAY ());
1440 gdk_flush ();
1441
1442 return retval;
1443 }
1444 #undef CLIPBOARD_NAME
1445
1446 static gboolean start_all_panels( )
1447 {
1448 gboolean is_global;
1449 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
1450 {
1451 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1452 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1453 const gchar* name;
1454
1455 if( ! dir )
1456 {
1457 g_free( panel_dir );
1458 continue;
1459 }
1460
1461 while((name = g_dir_read_name(dir)) != NULL)
1462 {
1463 char* panel_config = g_build_filename( panel_dir, name, NULL );
1464 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1465 {
1466 Panel* panel = panel_new( panel_config, name );
1467 if( panel )
1468 all_panels = g_slist_prepend( all_panels, panel );
1469 }
1470 g_free( panel_config );
1471 }
1472 g_dir_close( dir );
1473 g_free( panel_dir );
1474 }
1475 return all_panels != NULL;
1476 }
1477
1478 void load_global_config();
1479 void free_global_config();
1480
1481 int main(int argc, char *argv[], char *env[])
1482 {
1483 int i;
1484 const char* session_name;
1485
1486 setlocale(LC_CTYPE, "");
1487
1488 g_thread_init(NULL);
1489 gdk_threads_init();
1490
1491 gtk_init(&argc, &argv);
1492
1493 #ifdef ENABLE_NLS
1494 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1495 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1496 textdomain ( GETTEXT_PACKAGE );
1497 #endif
1498
1499 XSetLocaleModifiers("");
1500 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
1501
1502 resolve_atoms();
1503
1504 session_name = g_getenv("DESKTOP_SESSION");
1505 is_in_lxde = session_name && (0 == strcmp(session_name, "LXDE"));
1506
1507 for (i = 1; i < argc; i++) {
1508 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1509 usage();
1510 exit(0);
1511 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1512 printf("lxpanel %s\n", version);
1513 exit(0);
1514 } else if (!strcmp(argv[i], "--log")) {
1515 i++;
1516 if (i == argc) {
1517 ERR( "lxpanel: missing log level\n");
1518 usage();
1519 exit(1);
1520 } else {
1521 log_level = atoi(argv[i]);
1522 }
1523 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1524 config = 1;
1525 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1526 i++;
1527 if (i == argc) {
1528 ERR( "lxpanel: missing profile name\n");
1529 usage();
1530 exit(1);
1531 } else {
1532 cprofile = g_strdup(argv[i]);
1533 }
1534 } else {
1535 printf("lxpanel: unknown option - %s\n", argv[i]);
1536 usage();
1537 exit(1);
1538 }
1539 }
1540
1541 /* Check for duplicated lxpanel instances */
1542 if (!check_main_lock() && !config) {
1543 printf("There is already an instance of LXPanel. Now to exit\n");
1544 exit(1);
1545 }
1546
1547 /* Add our own icons to the search path of icon theme */
1548 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
1549 PACKAGE_DATA_DIR "/lxpanel/images" );
1550
1551 fbev = fb_ev_new();
1552 win_grp = gtk_window_group_new();
1553
1554 restart:
1555 is_restarting = FALSE;
1556
1557 load_global_config();
1558
1559 /* NOTE: StructureNotifyMask is required by XRandR
1560 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1561 */
1562 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
1563 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1564
1565 if( G_UNLIKELY( ! start_all_panels() ) )
1566 g_warning( "Config files are not found.\n" );
1567 /*
1568 * FIXME: configure??
1569 if (config)
1570 configure();
1571 */
1572 gtk_main();
1573
1574 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1575 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1576
1577 /* destroy all panels */
1578 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
1579 g_slist_free( all_panels );
1580 all_panels = NULL;
1581 g_free( cfgfile );
1582
1583 free_global_config();
1584
1585 if( is_restarting )
1586 goto restart;
1587
1588 g_object_unref(win_grp);
1589 g_object_unref(fbev);
1590
1591 return 0;
1592 }