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