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