Updated copyright year to 2011 in preparation for next version
[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 "Martin Bagge <brother@bsnet.se>",
681 NULL
682 };
683 /* TRANSLATORS: Replace this string with your names, one name per line. */
684 gchar *translators = _( "translator-credits" );
685
686 about = gtk_about_dialog_new();
687 panel_apply_icon(GTK_WINDOW(about));
688 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
689 gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
690 gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL));
691 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2011"));
692 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
693 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.");
694 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
695 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
696 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
697 gtk_dialog_run(GTK_DIALOG(about));
698 gtk_widget_destroy(about);
699 }
700
701 void panel_apply_icon( GtkWindow *w )
702 {
703 gtk_window_set_icon_from_file(w, PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL);
704 }
705
706 GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
707 {
708 GtkWidget *menu_item, *img;
709 GtkMenu *ret,*menu;
710
711 char* tmp;
712 ret = menu = GTK_MENU(gtk_menu_new());
713
714 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
715 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
716 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
717 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
718 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
719
720 if( plugin )
721 {
722 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
723 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
724 menu_item = gtk_image_menu_item_new_with_label( tmp );
725 g_free( tmp );
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_remove_item), plugin );
729 }
730
731 menu_item = gtk_separator_menu_item_new();
732 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
733
734 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
735 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
736 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
737 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
738 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
739
740 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
741 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
742 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
743 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
744 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
745 /* FIXME: Potentially we can support multiple panels at the same edge,
746 * but currently this cannot be done due to some positioning problems. */
747 /* currently, disable the option when there are already four panels */
748 if( g_slist_length( all_panels ) >= 4 )
749 gtk_widget_set_sensitive( menu_item, FALSE );
750
751 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
752 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
753 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
754 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
755 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
756 if( ! all_panels->next ) /* if this is the only panel */
757 gtk_widget_set_sensitive( menu_item, FALSE );
758
759 menu_item = gtk_separator_menu_item_new();
760 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
761
762 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
763 menu_item = gtk_image_menu_item_new_with_label(_("About"));
764 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
765 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
766 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel );
767
768 if( use_sub_menu )
769 {
770 ret = GTK_MENU(gtk_menu_new());
771 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
772 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
773 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
774
775 gtk_widget_show_all(GTK_WIDGET(ret));
776 }
777
778 if( plugin )
779 {
780 menu_item = gtk_separator_menu_item_new();
781 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
782
783 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
784 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
785 menu_item = gtk_image_menu_item_new_with_label( tmp );
786 g_free( tmp );
787 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
788 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
789 if( plugin->class->config )
790 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
791 else
792 gtk_widget_set_sensitive( menu_item, FALSE );
793 }
794
795 gtk_widget_show_all(GTK_WIDGET(menu));
796
797 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
798 return ret;
799 }
800
801 /****************************************************
802 * panel creation *
803 ****************************************************/
804
805 static void
806 make_round_corners(Panel *p)
807 {
808 /* FIXME: This should be re-written with shape extension of X11 */
809 /* gdk_window_shape_combine_mask() can be used */
810 }
811
812 void panel_set_dock_type(Panel *p)
813 {
814 if (p->setdocktype) {
815 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
816 XChangeProperty(GDK_DISPLAY(), p->topxwin,
817 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
818 PropModeReplace, (unsigned char *) &state, 1);
819 }
820 else {
821 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
822 }
823 }
824
825 static void panel_set_visibility(Panel *p, gboolean visible)
826 {
827 if ( ! visible) gtk_widget_hide(p->box);
828 p->visible = visible;
829 calculate_position(p);
830 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
831 gdk_window_move(p->topgwin->window, p->ax, p->ay);
832 if (visible) gtk_widget_show(p->box);
833 panel_set_wm_strut(p);
834 }
835
836 static gboolean panel_leave_real(Panel *p)
837 {
838 /* If the pointer is grabbed by this application, leave the panel displayed.
839 * There is no way to determine if it is grabbed by another application, such as an application that has a systray icon. */
840 if (gdk_display_pointer_is_grabbed(p->display))
841 return TRUE;
842
843 /* If the pointer is inside the panel, leave the panel displayed. */
844 gint x, y;
845 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
846 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
847 return TRUE;
848
849 /* If the panel is configured to autohide and if it is visible, hide the panel. */
850 if ((p->autohide) && (p->visible))
851 panel_set_visibility(p, FALSE);
852
853 /* Clear the timer. */
854 p->hide_timeout = 0;
855 return FALSE;
856 }
857
858 static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
859 {
860 /* We may receive multiple enter-notify events when the pointer crosses into the panel.
861 * Do extra tests to make sure this does not cause misoperation such as blinking.
862 * If the pointer is inside the panel, unhide it. */
863 gint x, y;
864 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
865 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
866 {
867 /* If the pointer is inside the panel and we have not already unhidden it, do so and
868 * set a timer to recheck it in a half second. */
869 if (p->hide_timeout == 0)
870 {
871 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
872 panel_set_visibility(p, TRUE);
873 }
874 }
875 else
876 {
877 /* If the pointer is not inside the panel, simulate a timer expiration. */
878 panel_leave_real(p);
879 }
880 return TRUE;
881 }
882
883 static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
884 gint y, guint time, Panel *p)
885 {
886 panel_enter(NULL, NULL, p);
887 return TRUE;
888 }
889
890 void panel_establish_autohide(Panel *p)
891 {
892 if (p->autohide)
893 {
894 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
895 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
896 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
897 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
898 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
899 panel_enter(NULL, NULL, p);
900 }
901 else if ( ! p->visible)
902 {
903 gtk_widget_show(p->box);
904 p->visible = TRUE;
905 }
906 }
907
908 /* Set an image from a file with scaling to the panel icon size. */
909 void panel_image_set_from_file(Panel * p, GtkWidget * image, char * file)
910 {
911 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
912 if (pixbuf != NULL)
913 {
914 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
915 g_object_unref(pixbuf);
916 }
917 }
918
919 /* Set an image from a icon theme with scaling to the panel icon size. */
920 gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
921 {
922 if (gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), icon))
923 {
924 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), icon, p->icon_size, 0, NULL);
925 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
926 g_object_unref(pixbuf);
927 return TRUE;
928 }
929 return FALSE;
930 }
931
932 static void
933 panel_start_gui(Panel *p)
934 {
935 Atom state[3];
936 XWMHints wmhints;
937 guint32 val;
938
939 ENTER;
940
941 p->curdesk = get_net_current_desktop();
942 p->desknum = get_net_number_of_desktops();
943 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
944
945 /* main toplevel window */
946 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
947 p->topgwin = (GtkWidget*)g_object_new(PANEL_TOPLEVEL_TYPE, NULL);
948 gtk_widget_set_name(p->topgwin, "PanelToplevel");
949 p->display = gdk_display_get_default();
950 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
951 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
952 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
953 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
954 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
955 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
956
957 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
958
959 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
960 G_CALLBACK(panel_delete_event), p);
961 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
962 G_CALLBACK(panel_destroy_event), p);
963 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
964 (GCallback) panel_size_req, p);
965 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
966 (GCallback) panel_size_alloc, p);
967 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
968 (GCallback) panel_configure_event, p);
969
970 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
971 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
972 (GCallback) panel_button_press_event_with_panel, p);
973
974 g_signal_connect (G_OBJECT (p->topgwin), "realize",
975 (GCallback) panel_realize, p);
976
977 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
978 (GCallback)panel_style_set, p);
979 gtk_widget_realize(p->topgwin);
980 //gdk_window_set_decorations(p->topgwin->window, 0);
981
982 // main layout manager as a single child of panel
983 p->box = p->my_box_new(FALSE, 0);
984 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
985 // gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
986 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
987 gtk_widget_show(p->box);
988 if (p->round_corners)
989 make_round_corners(p);
990
991 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
992 DBG("topxwin = %x\n", p->topxwin);
993
994 /* the settings that should be done before window is mapped */
995 wmhints.flags = InputHint;
996 wmhints.input = 0;
997 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
998 #define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
999 val = WIN_HINTS_SKIP_FOCUS;
1000 XChangeProperty(GDK_DISPLAY(), p->topxwin,
1001 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
1002 PropModeReplace, (unsigned char *) &val, 1);
1003
1004 panel_set_dock_type(p);
1005
1006 /* window mapping point */
1007 gtk_widget_show_all(p->topgwin);
1008
1009 /* the settings that should be done after window is mapped */
1010 panel_establish_autohide(p);
1011
1012 /* send it to running wm */
1013 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
1014 /* and assign it ourself just for case when wm is not running */
1015 val = 0xFFFFFFFF;
1016 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
1017 PropModeReplace, (unsigned char *) &val, 1);
1018
1019 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1020 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1021 state[2] = a_NET_WM_STATE_STICKY;
1022 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
1023 32, PropModeReplace, (unsigned char *) state, 3);
1024
1025 calculate_position(p);
1026 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
1027 panel_set_wm_strut(p);
1028
1029 RET();
1030 }
1031
1032 /* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1033 void panel_adjust_geometry_terminology(Panel * p)
1034 {
1035 if ((p->height_label != NULL) && (p->width_label != NULL)
1036 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
1037 {
1038 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1039 {
1040 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1041 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
1042 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1043 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
1044 }
1045 else
1046 {
1047 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1048 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
1049 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1050 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
1051 }
1052 }
1053 }
1054
1055 /* Draw text into a label, with the user preference color and optionally bold. */
1056 void panel_draw_label_text(Panel * p, GtkWidget * label, char * text, gboolean bold, gboolean custom_color)
1057 {
1058 if (text == NULL)
1059 {
1060 /* Null string. */
1061 gtk_label_set_text(GTK_LABEL(label), NULL);
1062 }
1063
1064 else
1065 {
1066 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1067 int font_desc;
1068 if (p->icon_size < 20)
1069 font_desc = 9;
1070 else if (p->icon_size >= 20 && p->icon_size < 36)
1071 font_desc = 10;
1072 else
1073 font_desc = 12;
1074
1075 /* Check the string for characters that need to be escaped.
1076 * If any are found, create the properly escaped string and use it instead. */
1077 char * valid_markup = text;
1078 char * escaped_text = NULL;
1079 char * q;
1080 for (q = text; *q != '\0'; q += 1)
1081 {
1082 if ((*q == '<') || (*q == '>') || (*q == '&'))
1083 {
1084 escaped_text = g_markup_escape_text(text, -1);
1085 valid_markup = escaped_text;
1086 break;
1087 }
1088 }
1089
1090 if ((custom_color) && (p->usefontcolor))
1091 {
1092 /* Color, optionally bold. */
1093 gchar * text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
1094 font_desc,
1095 gcolor2rgb24(&p->gfontcolor),
1096 ((bold) ? "<b>" : ""),
1097 valid_markup,
1098 ((bold) ? "</b>" : ""));
1099 gtk_label_set_markup(GTK_LABEL(label), text);
1100 g_free(text);
1101 }
1102 else
1103 {
1104 /* No color, optionally bold. */
1105 gchar * text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
1106 font_desc,
1107 ((bold) ? "<b>" : ""),
1108 valid_markup,
1109 ((bold) ? "</b>" : ""));
1110 gtk_label_set_markup(GTK_LABEL(label), text);
1111 g_free(text);
1112 }
1113 g_free(escaped_text);
1114 }
1115 }
1116
1117 void panel_set_panel_configuration_changed(Panel *p)
1118 {
1119 GList* l;
1120
1121 int previous_orientation = p->orientation;
1122 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1123 ? ORIENT_HORIZ : ORIENT_VERT;
1124
1125 if (previous_orientation != p->orientation)
1126 {
1127 panel_adjust_geometry_terminology(p);
1128 if (previous_orientation != ORIENT_NONE)
1129 p->height = ((p->orientation == ORIENT_HORIZ) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1130 if (p->height_control != NULL)
1131 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
1132 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1133 {
1134 int value = ((p->orientation == ORIENT_HORIZ) ? gdk_screen_width() : gdk_screen_height());
1135 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1136 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1137 }
1138
1139 }
1140
1141 if (p->orientation == ORIENT_HORIZ) {
1142 p->my_box_new = gtk_hbox_new;
1143 p->my_separator_new = gtk_vseparator_new;
1144 } else {
1145 p->my_box_new = gtk_vbox_new;
1146 p->my_separator_new = gtk_hseparator_new;
1147 }
1148
1149 /* recreate the main layout box */
1150 if (p->box != NULL)
1151 {
1152 #if GTK_CHECK_VERSION(2,16,0)
1153 GtkOrientation bo = (p->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1154 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), bo);
1155 #else
1156 GtkBox * newbox = GTK_BOX(recreate_box(GTK_BOX(p->box), p->orientation));
1157 if (GTK_WIDGET(newbox) != p->box)
1158 {
1159 p->box = GTK_WIDGET(newbox);
1160 gtk_container_add(GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox));
1161 }
1162 #endif
1163 }
1164
1165 /* NOTE: This loop won't be executed when panel started since
1166 plugins are not loaded at that time.
1167 This is used when the orientation of the panel is changed
1168 from the config dialog, and plugins should be re-layout.
1169 */
1170 for( l = p->plugins; l; l = l->next ) {
1171 Plugin* pl = (Plugin*)l->data;
1172 if( pl->class->panel_configuration_changed ) {
1173 pl->class->panel_configuration_changed( pl );
1174 }
1175 }
1176 }
1177
1178 static int
1179 panel_parse_global(Panel *p, char **fp)
1180 {
1181 line s;
1182 s.len = 256;
1183
1184 if( G_LIKELY( fp ) )
1185 {
1186 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1187 if (s.type == LINE_VAR) {
1188 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
1189 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
1190 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
1191 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
1192 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
1193 p->margin = atoi(s.t[1]);
1194 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
1195 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
1196 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
1197 p->width = atoi(s.t[1]);
1198 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
1199 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
1200 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
1201 p->height = atoi(s.t[1]);
1202 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1203 p->spacing = atoi(s.t[1]);
1204 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
1205 p->setdocktype = str2num(bool_pair, s.t[1], 0);
1206 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
1207 p->setstrut = str2num(bool_pair, s.t[1], 0);
1208 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
1209 p->round_corners = str2num(bool_pair, s.t[1], 0);
1210 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
1211 p->transparent = str2num(bool_pair, s.t[1], 0);
1212 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
1213 p->alpha = atoi(s.t[1]);
1214 if (p->alpha > 255)
1215 p->alpha = 255;
1216 } else if (!g_ascii_strcasecmp(s.t[0], "AutoHide")) {
1217 p->autohide = str2num(bool_pair, s.t[1], 0);
1218 } else if (!g_ascii_strcasecmp(s.t[0], "HeightWhenHidden")) {
1219 p->height_when_hidden = atoi(s.t[1]);
1220 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
1221 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
1222 gdk_color_parse ("white", &p->gtintcolor);
1223 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1224 DBG("tintcolor=%x\n", p->tintcolor);
1225 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
1226 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
1227 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
1228 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1229 gdk_color_parse ("black", &p->gfontcolor);
1230 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1231 DBG("fontcolor=%x\n", p->fontcolor);
1232 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1233 p->background = str2num(bool_pair, s.t[1], 0);
1234 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1235 p->background_file = g_strdup( s.t[1] );
1236 } else if (!g_ascii_strcasecmp(s.t[0], "IconSize")) {
1237 p->icon_size = atoi(s.t[1]);
1238 } else {
1239 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
1240 }
1241 } else if (s.type == LINE_BLOCK_END) {
1242 break;
1243 } else {
1244 ERR( "lxpanel: illegal in this context %s\n", s.str);
1245 RET(0);
1246 }
1247 }
1248 }
1249
1250 panel_normalize_configuration(p);
1251
1252 return 1;
1253 }
1254
1255 static int
1256 panel_parse_plugin(Panel *p, char **fp)
1257 {
1258 line s;
1259 Plugin *plug = NULL;
1260 gchar *type = NULL;
1261 int expand , padding, border;
1262 char* pconfig = NULL;
1263
1264 ENTER;
1265 s.len = 256;
1266 border = expand = padding = 0;
1267 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
1268 if (s.type == LINE_NONE) {
1269 ERR( "lxpanel: bad line %s\n", s.str);
1270 goto error;
1271 }
1272 if (s.type == LINE_VAR) {
1273 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1274 type = g_strdup(s.t[1]);
1275 DBG("plug %s\n", type);
1276 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1277 expand = str2num(bool_pair, s.t[1], 0);
1278 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1279 padding = atoi(s.t[1]);
1280 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1281 border = atoi(s.t[1]);
1282 else {
1283 ERR( "lxpanel: unknown var %s\n", s.t[0]);
1284 }
1285 } else if (s.type == LINE_BLOCK_START) {
1286 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
1287 pconfig = *fp;
1288 int pno = 1;
1289 while (pno) {
1290 get_line_as_is(fp, &s);
1291 if (s.type == LINE_NONE) {
1292 ERR( "lxpanel: unexpected eof\n");
1293 goto error;
1294 } else if (s.type == LINE_BLOCK_START) {
1295 pno++;
1296 } else if (s.type == LINE_BLOCK_END) {
1297 pno--;
1298 }
1299 }
1300 } else {
1301 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1302 goto error;
1303 }
1304 } else {
1305 ERR( "lxpanel: illegal in this context %s\n", s.str);
1306 goto error;
1307 }
1308 }
1309
1310 if (!type || !(plug = plugin_load(type))) {
1311 ERR( "lxpanel: can't load %s plugin\n", type);
1312 goto error;
1313 }
1314
1315 plug->panel = p;
1316 if (plug->class->expand_available) plug->expand = expand;
1317 plug->padding = padding;
1318 plug->border = border;
1319 DBG("starting\n");
1320 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
1321 ERR( "lxpanel: can't start plugin %s\n", type);
1322 goto error;
1323 }
1324 DBG("plug %s\n", type);
1325 p->plugins = g_list_append(p->plugins, plug);
1326
1327 g_free( type );
1328 RET(1);
1329
1330 error:
1331 if (plug != NULL)
1332 plugin_unload(plug);
1333 g_free(type);
1334 RET(0);
1335 }
1336
1337 int panel_start( Panel *p, char **fp )
1338 {
1339 line s;
1340
1341 /* parse global section */
1342 ENTER;
1343 s.len = 256;
1344
1345 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
1346 ERR( "lxpanel: config file must start from Global section\n");
1347 RET(0);
1348 }
1349 if (!panel_parse_global(p, fp))
1350 RET(0);
1351
1352 panel_start_gui(p);
1353
1354 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1355 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1356 ERR( "lxpanel: expecting Plugin section\n");
1357 RET(0);
1358 }
1359 panel_parse_plugin(p, fp);
1360 }
1361
1362 /* update backgrond of panel and all plugins */
1363 panel_update_background( p );
1364 return 1;
1365 }
1366
1367 static void
1368 delete_plugin(gpointer data, gpointer udata)
1369 {
1370 plugin_delete((Plugin *)data);
1371 }
1372
1373 void panel_destroy(Panel *p)
1374 {
1375 ENTER;
1376
1377 if (p->pref_dialog != NULL)
1378 gtk_widget_destroy(p->pref_dialog);
1379 if (p->plugin_pref_dialog != NULL)
1380 {
1381 gtk_widget_destroy(p->plugin_pref_dialog);
1382 p->plugin_pref_dialog = NULL;
1383 }
1384
1385 if (p->bg != NULL)
1386 {
1387 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1388 g_object_unref(p->bg);
1389 }
1390
1391 if( p->config_changed )
1392 panel_config_save( p );
1393
1394 g_list_foreach(p->plugins, delete_plugin, NULL);
1395 g_list_free(p->plugins);
1396 p->plugins = NULL;
1397
1398 if( p->system_menus ){
1399 do{
1400 } while ( g_source_remove_by_user_data( p->system_menus ) );
1401 }
1402
1403 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
1404
1405 if( p->topgwin )
1406 gtk_widget_destroy(p->topgwin);
1407 g_free(p->workarea);
1408 g_free( p->background_file );
1409 g_slist_free( p->system_menus );
1410 gdk_flush();
1411 XFlush(GDK_DISPLAY());
1412 XSync(GDK_DISPLAY(), True);
1413
1414 g_free( p->name );
1415 g_free(p);
1416 RET();
1417 }
1418
1419 Panel* panel_new( const char* config_file, const char* config_name )
1420 {
1421 char *fp, *pfp; /* point to current position of profile data in memory */
1422 Panel* panel = NULL;
1423
1424 if( G_LIKELY(config_file) )
1425 {
1426 g_file_get_contents( config_file, &fp, NULL, NULL );
1427 if( fp )
1428 {
1429 panel = panel_allocate();
1430 panel->orientation = ORIENT_NONE;
1431 panel->name = g_strdup( config_name );
1432 pfp = fp;
1433
1434 if (! panel_start( panel, &pfp )) {
1435 ERR( "lxpanel: can't start panel\n");
1436 panel_destroy( panel );
1437 panel = NULL;
1438 }
1439
1440 g_free( fp );
1441 }
1442 }
1443 return panel;
1444 }
1445
1446 static void
1447 usage()
1448 {
1449 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1450 g_print(_("Command line options:\n"));
1451 g_print(_(" --help -- print this help and exit\n"));
1452 g_print(_(" --version -- print version and exit\n"));
1453 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
1454 // g_print(_(" --configure -- launch configuration utility\n"));
1455 g_print(_(" --profile name -- use specified profile\n"));
1456 g_print("\n");
1457 g_print(_(" -h -- same as --help\n"));
1458 g_print(_(" -p -- same as --profile\n"));
1459 g_print(_(" -v -- same as --version\n"));
1460 // g_print(_(" -C -- same as --configure\n"));
1461 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
1462 }
1463
1464 int panel_handle_x_error(Display * d, XErrorEvent * ev)
1465 {
1466 char buf[256];
1467
1468 if (log_level >= LOG_WARN) {
1469 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1470 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1471 }
1472 return 0; /* Ignored */
1473 }
1474
1475 int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1476 {
1477 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1478 panel_handle_x_error(d, ev);
1479 return 0; /* Ignored */
1480 }
1481
1482 /* Lightweight lock related functions - X clipboard hacks */
1483
1484 #define CLIPBOARD_NAME "LXPANEL_SELECTION"
1485
1486 /*
1487 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1488 */
1489 static void
1490 clipboard_get_func(
1491 GtkClipboard *clipboard G_GNUC_UNUSED,
1492 GtkSelectionData *selection_data G_GNUC_UNUSED,
1493 guint info G_GNUC_UNUSED,
1494 gpointer user_data_or_owner G_GNUC_UNUSED)
1495 {
1496 }
1497
1498 /*
1499 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1500 */
1501 static void clipboard_clear_func(
1502 GtkClipboard *clipboard G_GNUC_UNUSED,
1503 gpointer user_data_or_owner G_GNUC_UNUSED)
1504 {
1505 }
1506
1507 /*
1508 * Lightweight version for checking single instance.
1509 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1510 *
1511 * Returns TRUE if successfully retrieved and FALSE otherwise.
1512 */
1513 static gboolean check_main_lock()
1514 {
1515 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1516 gboolean retval = FALSE;
1517 GtkClipboard *clipboard;
1518 Atom atom;
1519
1520 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1521
1522 XGrabServer(GDK_DISPLAY());
1523
1524 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1525 goto out;
1526
1527 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1528
1529 if (gtk_clipboard_set_with_data(clipboard, targets,
1530 G_N_ELEMENTS (targets),
1531 clipboard_get_func,
1532 clipboard_clear_func, NULL))
1533 retval = TRUE;
1534
1535 out:
1536 XUngrabServer (GDK_DISPLAY ());
1537 gdk_flush ();
1538
1539 return retval;
1540 }
1541 #undef CLIPBOARD_NAME
1542
1543 static gboolean start_all_panels( )
1544 {
1545 gboolean is_global;
1546 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
1547 {
1548 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1549 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1550 const gchar* name;
1551
1552 if( ! dir )
1553 {
1554 g_free( panel_dir );
1555 continue;
1556 }
1557
1558 while((name = g_dir_read_name(dir)) != NULL)
1559 {
1560 char* panel_config = g_build_filename( panel_dir, name, NULL );
1561 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1562 {
1563 Panel* panel = panel_new( panel_config, name );
1564 if( panel )
1565 all_panels = g_slist_prepend( all_panels, panel );
1566 }
1567 g_free( panel_config );
1568 }
1569 g_dir_close( dir );
1570 g_free( panel_dir );
1571 }
1572 return all_panels != NULL;
1573 }
1574
1575 void load_global_config();
1576 void free_global_config();
1577
1578 int main(int argc, char *argv[], char *env[])
1579 {
1580 int i;
1581 const char* desktop_name;
1582
1583 setlocale(LC_CTYPE, "");
1584
1585 g_thread_init(NULL);
1586 gdk_threads_init();
1587
1588 gtk_init(&argc, &argv);
1589
1590 #ifdef ENABLE_NLS
1591 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1592 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1593 textdomain ( GETTEXT_PACKAGE );
1594 #endif
1595
1596 XSetLocaleModifiers("");
1597 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
1598
1599 resolve_atoms();
1600
1601 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1602 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
1603
1604 for (i = 1; i < argc; i++) {
1605 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1606 usage();
1607 exit(0);
1608 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1609 printf("lxpanel %s\n", version);
1610 exit(0);
1611 } else if (!strcmp(argv[i], "--log")) {
1612 i++;
1613 if (i == argc) {
1614 ERR( "lxpanel: missing log level\n");
1615 usage();
1616 exit(1);
1617 } else {
1618 log_level = atoi(argv[i]);
1619 }
1620 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1621 config = 1;
1622 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1623 i++;
1624 if (i == argc) {
1625 ERR( "lxpanel: missing profile name\n");
1626 usage();
1627 exit(1);
1628 } else {
1629 cprofile = g_strdup(argv[i]);
1630 }
1631 } else {
1632 printf("lxpanel: unknown option - %s\n", argv[i]);
1633 usage();
1634 exit(1);
1635 }
1636 }
1637
1638 /* Check for duplicated lxpanel instances */
1639 if (!check_main_lock() && !config) {
1640 printf("There is already an instance of LXPanel. Now to exit\n");
1641 exit(1);
1642 }
1643
1644 /* Add our own icons to the search path of icon theme */
1645 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
1646 PACKAGE_DATA_DIR "/lxpanel/images" );
1647
1648 fbev = fb_ev_new();
1649 win_grp = gtk_window_group_new();
1650
1651 restart:
1652 is_restarting = FALSE;
1653
1654 load_global_config();
1655
1656 /* NOTE: StructureNotifyMask is required by XRandR
1657 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1658 */
1659 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
1660 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1661
1662 if( G_UNLIKELY( ! start_all_panels() ) )
1663 g_warning( "Config files are not found.\n" );
1664 /*
1665 * FIXME: configure??
1666 if (config)
1667 configure();
1668 */
1669 gtk_main();
1670
1671 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1672 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1673
1674 /* destroy all panels */
1675 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
1676 g_slist_free( all_panels );
1677 all_panels = NULL;
1678 g_free( cfgfile );
1679
1680 free_global_config();
1681
1682 if( is_restarting )
1683 goto restart;
1684
1685 g_object_unref(win_grp);
1686 g_object_unref(fbev);
1687
1688 return 0;
1689 }