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