de2cad630c2baa2fb8128867831a8caba186e65f
[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 configurator.c */
229 void panel_configure(Panel* p, int sel_page );
230 gboolean panel_edge_available(Panel* p, int edge, gint monitor);
231
232 /* built-in commands, defined in configurator.c */
233 void restart(void);
234 void gtk_run(void);
235 void panel_destroy(Panel *p);
236
237 static void process_client_msg ( XClientMessageEvent* ev )
238 {
239 int cmd = ev->data.b[0];
240 switch( cmd )
241 {
242 #ifndef DISABLE_MENU
243 case LXPANEL_CMD_SYS_MENU:
244 {
245 GSList* l;
246 for( l = all_panels; l; l = l->next )
247 {
248 Panel* p = (Panel*)l->data;
249 GList *plugins, *pl;
250
251 plugins = gtk_container_get_children(GTK_CONTAINER(p->box));
252 for (pl = plugins; pl; pl = pl->next)
253 {
254 LXPanelPluginInit *init = PLUGIN_CLASS(pl->data);
255 if (init->show_system_menu)
256 /* queue to show system menu */
257 init->show_system_menu(pl->data);
258 }
259 g_list_free(plugins);
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->topgwin )
1390 {
1391 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
1392 gtk_widget_destroy(p->topgwin);
1393 }
1394 g_free(p->workarea);
1395 g_free( p->background_file );
1396 g_slist_free( p->system_menus );
1397 gdk_flush();
1398 XFlush(GDK_DISPLAY());
1399 XSync(GDK_DISPLAY(), True);
1400
1401 g_free( p->name );
1402 g_free(p);
1403 RET();
1404 }
1405
1406 Panel* panel_new( const char* config_file, const char* config_name )
1407 {
1408 Panel* panel = NULL;
1409
1410 if (G_LIKELY(config_file))
1411 {
1412 panel = panel_allocate();
1413 panel->name = g_strdup(config_name);
1414 g_debug("starting panel from file %s",config_file);
1415 if (!config_read_file(panel->config, config_file) ||
1416 !panel_start(panel))
1417 {
1418 ERR( "lxpanel: can't start panel\n");
1419 panel_destroy( panel );
1420 panel = NULL;
1421 }
1422 }
1423 return panel;
1424 }
1425
1426 static void
1427 usage()
1428 {
1429 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1430 g_print(_("Command line options:\n"));
1431 g_print(_(" --help -- print this help and exit\n"));
1432 g_print(_(" --version -- print version and exit\n"));
1433 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
1434 // g_print(_(" --configure -- launch configuration utility\n"));
1435 g_print(_(" --profile name -- use specified profile\n"));
1436 g_print("\n");
1437 g_print(_(" -h -- same as --help\n"));
1438 g_print(_(" -p -- same as --profile\n"));
1439 g_print(_(" -v -- same as --version\n"));
1440 // g_print(_(" -C -- same as --configure\n"));
1441 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
1442 }
1443
1444 int panel_handle_x_error(Display * d, XErrorEvent * ev)
1445 {
1446 char buf[256];
1447
1448 if (log_level >= LOG_WARN) {
1449 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1450 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1451 }
1452 return 0; /* Ignored */
1453 }
1454
1455 int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1456 {
1457 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1458 panel_handle_x_error(d, ev);
1459 return 0; /* Ignored */
1460 }
1461
1462 /* Lightweight lock related functions - X clipboard hacks */
1463
1464 #define CLIPBOARD_NAME "LXPANEL_SELECTION"
1465
1466 /*
1467 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1468 */
1469 static void
1470 clipboard_get_func(
1471 GtkClipboard *clipboard G_GNUC_UNUSED,
1472 GtkSelectionData *selection_data G_GNUC_UNUSED,
1473 guint info G_GNUC_UNUSED,
1474 gpointer user_data_or_owner G_GNUC_UNUSED)
1475 {
1476 }
1477
1478 /*
1479 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1480 */
1481 static void clipboard_clear_func(
1482 GtkClipboard *clipboard G_GNUC_UNUSED,
1483 gpointer user_data_or_owner G_GNUC_UNUSED)
1484 {
1485 }
1486
1487 /*
1488 * Lightweight version for checking single instance.
1489 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1490 *
1491 * Returns TRUE if successfully retrieved and FALSE otherwise.
1492 */
1493 static gboolean check_main_lock()
1494 {
1495 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1496 gboolean retval = FALSE;
1497 GtkClipboard *clipboard;
1498 Atom atom;
1499
1500 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1501
1502 XGrabServer(GDK_DISPLAY());
1503
1504 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1505 goto out;
1506
1507 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1508
1509 if (gtk_clipboard_set_with_data(clipboard, targets,
1510 G_N_ELEMENTS (targets),
1511 clipboard_get_func,
1512 clipboard_clear_func, NULL))
1513 retval = TRUE;
1514
1515 out:
1516 XUngrabServer (GDK_DISPLAY ());
1517 gdk_flush ();
1518
1519 return retval;
1520 }
1521 #undef CLIPBOARD_NAME
1522
1523 static gboolean start_all_panels( )
1524 {
1525 gboolean is_global;
1526 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
1527 {
1528 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1529 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1530 const gchar* name;
1531
1532 if( ! dir )
1533 {
1534 g_free( panel_dir );
1535 continue;
1536 }
1537
1538 while((name = g_dir_read_name(dir)) != NULL)
1539 {
1540 char* panel_config = g_build_filename( panel_dir, name, NULL );
1541 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1542 {
1543 Panel* panel = panel_new( panel_config, name );
1544 if( panel )
1545 all_panels = g_slist_prepend( all_panels, panel );
1546 }
1547 g_free( panel_config );
1548 }
1549 g_dir_close( dir );
1550 g_free( panel_dir );
1551 }
1552 return all_panels != NULL;
1553 }
1554
1555 void load_global_config();
1556 void free_global_config();
1557
1558 int main(int argc, char *argv[], char *env[])
1559 {
1560 int i;
1561 const char* desktop_name;
1562
1563 setlocale(LC_CTYPE, "");
1564
1565 g_thread_init(NULL);
1566 /* gdk_threads_init();
1567 gdk_threads_enter(); */
1568
1569 gtk_init(&argc, &argv);
1570
1571 #ifdef ENABLE_NLS
1572 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1573 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1574 textdomain ( GETTEXT_PACKAGE );
1575 #endif
1576
1577 XSetLocaleModifiers("");
1578 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
1579
1580 resolve_atoms();
1581
1582 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1583 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
1584
1585 for (i = 1; i < argc; i++) {
1586 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1587 usage();
1588 exit(0);
1589 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1590 printf("lxpanel %s\n", version);
1591 exit(0);
1592 } else if (!strcmp(argv[i], "--log")) {
1593 i++;
1594 if (i == argc) {
1595 ERR( "lxpanel: missing log level\n");
1596 usage();
1597 exit(1);
1598 } else {
1599 log_level = atoi(argv[i]);
1600 log_level_set_on_commandline = true;
1601 }
1602 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1603 config = 1;
1604 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1605 i++;
1606 if (i == argc) {
1607 ERR( "lxpanel: missing profile name\n");
1608 usage();
1609 exit(1);
1610 } else {
1611 cprofile = g_strdup(argv[i]);
1612 }
1613 } else {
1614 printf("lxpanel: unknown option - %s\n", argv[i]);
1615 usage();
1616 exit(1);
1617 }
1618 }
1619
1620 /* Check for duplicated lxpanel instances */
1621 if (!check_main_lock() && !config) {
1622 printf("There is already an instance of LXPanel. Now to exit\n");
1623 exit(1);
1624 }
1625
1626 /* Add our own icons to the search path of icon theme */
1627 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/lxpanel/images" );
1628
1629 fbev = fb_ev_new();
1630 win_grp = gtk_window_group_new();
1631
1632 restart:
1633 is_restarting = FALSE;
1634
1635 /* init LibFM */
1636 fm_gtk_init(NULL);
1637
1638 /* prepare modules data */
1639 _prepare_modules();
1640
1641 load_global_config();
1642
1643 /* NOTE: StructureNotifyMask is required by XRandR
1644 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1645 */
1646 gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
1647 GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
1648 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1649
1650 if( G_UNLIKELY( ! start_all_panels() ) )
1651 g_warning( "Config files are not found.\n" );
1652 /*
1653 * FIXME: configure??
1654 if (config)
1655 configure();
1656 */
1657 gtk_main();
1658
1659 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1660 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1661
1662 /* destroy all panels */
1663 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
1664 g_slist_free( all_panels );
1665 all_panels = NULL;
1666 g_free( cfgfile );
1667
1668 free_global_config();
1669
1670 _unload_modules();
1671 fm_gtk_finalize();
1672
1673 if( is_restarting )
1674 goto restart;
1675
1676 /* gdk_threads_leave(); */
1677
1678 g_object_unref(win_grp);
1679 g_object_unref(fbev);
1680
1681 return 0;
1682 }
1683
1684 extern GtkOrientation panel_get_orientation(Panel *panel)
1685 {
1686 return panel->orientation;
1687 }
1688
1689 extern gint panel_get_icon_size(Panel *panel)
1690 {
1691 return panel->icon_size;
1692 }
1693
1694 extern gint panel_get_height(Panel *panel)
1695 {
1696 return panel->height;
1697 }
1698
1699 extern GtkWindow *panel_get_toplevel_window(Panel *panel)
1700 {
1701 return GTK_WINDOW(panel->topgwin);
1702 }
1703
1704 extern GtkStyle *panel_get_defstyle(Panel *panel)
1705 {
1706 return panel->defstyle;
1707 }
1708
1709 extern GtkIconTheme *panel_get_icon_theme(Panel *panel)
1710 {
1711 return panel->icon_theme;
1712 }
1713
1714 extern GtkWidget *panel_box_new(Panel *panel, gboolean homogeneous, gint spacing)
1715 {
1716 return panel->my_box_new(homogeneous, spacing);
1717 }
1718
1719 extern GtkWidget *panel_separator_new(Panel *panel)
1720 {
1721 return panel->my_separator_new();
1722 }
1723
1724 gboolean _class_is_present(LXPanelPluginInit *init)
1725 {
1726 GSList *sl;
1727
1728 for (sl = all_panels; sl; sl = sl->next )
1729 {
1730 Panel *panel = (Panel*)sl->data;
1731 GList *plugins, *p;
1732
1733 plugins = gtk_container_get_children(GTK_CONTAINER(panel->box));
1734 for (p = plugins; p; p = p->next)
1735 if (PLUGIN_CLASS(p->data) == init)
1736 {
1737 g_list_free(plugins);
1738 return TRUE;
1739 }
1740 g_list_free(plugins);
1741 }
1742 return FALSE;
1743 }