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