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