391f056398d9cd74828e55ae6150ff88daac3fef
[lxde/lxpanel.git] / src / panel.c
1 /*
2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <glib/gi18n.h>
24 #include <stdlib.h>
25 #include <glib/gstdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <locale.h>
31 #include <string.h>
32 #include <gdk/gdkx.h>
33 #include <libfm/fm-gtk.h>
34
35 #define __LXPANEL_INTERNALS__
36
37 #include "private.h"
38 #include "misc.h"
39 #include "bg.h"
40
41 #include "lxpanelctl.h"
42 #include "dbg.h"
43 #include "gtk-compat.h"
44
45 gchar *cprofile = "default";
46
47 GSList* all_panels = NULL; /* a single-linked list storing all panels */
48
49 gboolean is_in_lxde = FALSE;
50
51 static GtkWindowGroup* win_grp = NULL; /* window group used to limit the scope of model dialog. */
52
53 static gulong monitors_handler = 0;
54
55 static void panel_start_gui(LXPanel *p, config_setting_t *list);
56 static void ah_start(LXPanel *p);
57 static void ah_stop(LXPanel *p);
58 static void on_root_bg_changed(FbBg *bg, LXPanel* p);
59 static void _panel_update_background(LXPanel * p);
60
61 enum
62 {
63 ICON_SIZE_CHANGED,
64 N_SIGNALS
65 };
66
67 static guint signals[N_SIGNALS];
68
69 G_DEFINE_TYPE(PanelToplevel, lxpanel, GTK_TYPE_WINDOW);
70
71 static void lxpanel_finalize(GObject *object)
72 {
73 LXPanel *self = LXPANEL(object);
74 Panel *p = self->priv;
75
76 if( p->config_changed )
77 lxpanel_config_save( self );
78 config_destroy(p->config);
79
80 XFree(p->workarea);
81 g_free( p->background_file );
82 g_slist_free( p->system_menus );
83
84 g_free( p->name );
85 g_free(p);
86
87 G_OBJECT_CLASS(lxpanel_parent_class)->finalize(object);
88 }
89
90 static void panel_stop_gui(LXPanel *self)
91 {
92 Panel *p = self->priv;
93 Display *xdisplay;
94
95 g_debug("panel_stop_gui on '%s'", p->name);
96 if (p->autohide)
97 ah_stop(self);
98
99 if (p->pref_dialog != NULL)
100 gtk_widget_destroy(p->pref_dialog);
101 p->pref_dialog = NULL;
102
103 if (p->plugin_pref_dialog != NULL)
104 /* just close the dialog, it will do all required cleanup */
105 gtk_dialog_response(GTK_DIALOG(p->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
106
107 if (p->bg != NULL)
108 {
109 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, self);
110 g_object_unref(p->bg);
111 p->bg = NULL;
112 }
113
114 if (p->initialized)
115 {
116 gtk_window_group_remove_window(win_grp, GTK_WINDOW(self));
117 xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
118 gdk_flush();
119 XFlush(xdisplay);
120 XSync(xdisplay, True);
121 p->initialized = FALSE;
122 }
123
124 if (p->background_update_queued)
125 {
126 g_source_remove(p->background_update_queued);
127 p->background_update_queued = 0;
128 }
129
130 if (gtk_bin_get_child(GTK_BIN(self)))
131 {
132 gtk_widget_destroy(p->box);
133 p->box = NULL;
134 }
135 }
136
137 static void lxpanel_destroy(GtkObject *object)
138 {
139 LXPanel *self = LXPANEL(object);
140
141 panel_stop_gui(self);
142
143 GTK_OBJECT_CLASS(lxpanel_parent_class)->destroy(object);
144 }
145
146 static gboolean idle_update_background(gpointer p)
147 {
148 LXPanel *panel = LXPANEL(p);
149
150 if (g_source_is_destroyed(g_main_current_source()))
151 return FALSE;
152
153 /* Panel could be destroyed while background update scheduled */
154 if (gtk_widget_get_realized(p))
155 {
156 gdk_display_sync( gtk_widget_get_display(p) );
157 _panel_update_background(panel);
158 }
159 panel->priv->background_update_queued = 0;
160
161 return FALSE;
162 }
163
164 void _panel_queue_update_background(LXPanel *panel)
165 {
166 if (panel->priv->background_update_queued)
167 return;
168 panel->priv->background_update_queued = g_idle_add_full(G_PRIORITY_HIGH,
169 idle_update_background,
170 panel, NULL);
171 }
172
173 static void lxpanel_realize(GtkWidget *widget)
174 {
175 GTK_WIDGET_CLASS(lxpanel_parent_class)->realize(widget);
176
177 _panel_queue_update_background(LXPANEL(widget));
178 }
179
180 static void lxpanel_style_set(GtkWidget *widget, GtkStyle* prev)
181 {
182 GTK_WIDGET_CLASS(lxpanel_parent_class)->style_set(widget, prev);
183
184 /* FIXME: This dirty hack is used to fix the background of systray... */
185 _panel_queue_update_background(LXPANEL(widget));
186 }
187
188 static void lxpanel_size_request(GtkWidget *widget, GtkRequisition *req)
189 {
190 LXPanel *panel = LXPANEL(widget);
191 Panel *p = panel->priv;
192 GdkRectangle rect;
193
194 GTK_WIDGET_CLASS(lxpanel_parent_class)->size_request(widget, req);
195
196 if (!p->visible)
197 /* When the panel is in invisible state, the content box also got hidden, thus always
198 * report 0 size. Ask the content box instead for its size. */
199 gtk_widget_size_request(p->box, req);
200
201 rect.width = req->width;
202 rect.height = req->height;
203 _calculate_position(panel, &rect);
204 req->width = rect.width;
205 req->height = rect.height;
206 }
207
208 static void lxpanel_size_allocate(GtkWidget *widget, GtkAllocation *a)
209 {
210 LXPanel *panel = LXPANEL(widget);
211 Panel *p = panel->priv;
212 GdkRectangle rect;
213 gint x, y;
214
215 GTK_WIDGET_CLASS(lxpanel_parent_class)->size_allocate(widget, a);
216
217 if (p->widthtype == WIDTH_REQUEST)
218 p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? a->width : a->height;
219 if (p->heighttype == HEIGHT_REQUEST)
220 p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? a->height : a->width;
221
222 if (!gtk_widget_get_realized(widget))
223 return;
224
225 rect = *a;
226 /* get real coords since a contains 0, 0 */
227 gdk_window_get_origin(gtk_widget_get_window(widget), &x, &y);
228 _calculate_position(panel, &rect);
229 p->ax = rect.x;
230 p->ay = rect.y;
231
232 if (a->width != p->aw || a->height != p->ah || x != p->ax || y != p->ay)
233 {
234 p->aw = a->width;
235 p->ah = a->height;
236 /* FIXME: should we "correct" requested sizes? */
237 gtk_window_move(GTK_WINDOW(widget), p->ax, p->ay);
238 _panel_set_wm_strut(panel);
239 _panel_queue_update_background(panel);
240 }
241 if (p->background_update_queued)
242 {
243 g_source_remove(p->background_update_queued);
244 p->background_update_queued = 0;
245 _panel_update_background(LXPANEL(widget));
246 }
247 }
248
249 static gboolean lxpanel_configure_event (GtkWidget *widget, GdkEventConfigure *e)
250 {
251 Panel *p = LXPANEL(widget)->priv;
252
253 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
254 goto ok;
255 p->cw = e->width;
256 p->ch = e->height;
257 p->cx = e->x;
258 p->cy = e->y;
259
260 if (p->transparent)
261 fb_bg_notify_changed_bg(p->bg);
262 ok:
263 return GTK_WIDGET_CLASS(lxpanel_parent_class)->configure_event(widget, e);
264 }
265
266 static gboolean lxpanel_map_event(GtkWidget *widget, GdkEventAny *event)
267 {
268 Panel *p = PLUGIN_PANEL(widget)->priv;
269
270 if (p->autohide)
271 ah_start(LXPANEL(widget));
272 return GTK_WIDGET_CLASS(lxpanel_parent_class)->map_event(widget, event);
273 }
274
275 /* Handler for "button_press_event" signal with Panel as parameter. */
276 static gboolean lxpanel_button_press(GtkWidget *widget, GdkEventButton *event)
277 {
278 if (event->button == 3) /* right button */
279 {
280 GtkMenu* popup = (GtkMenu*) lxpanel_get_plugin_menu(LXPANEL(widget), NULL, FALSE);
281 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
282 return TRUE;
283 }
284 return FALSE;
285 }
286
287 static void lxpanel_class_init(PanelToplevelClass *klass)
288 {
289 GObjectClass *gobject_class = (GObjectClass *)klass;
290 GtkObjectClass *gtk_object_class = (GtkObjectClass *)klass;
291 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
292
293 gobject_class->finalize = lxpanel_finalize;
294 gtk_object_class->destroy = lxpanel_destroy;
295 widget_class->realize = lxpanel_realize;
296 widget_class->size_request = lxpanel_size_request;
297 widget_class->size_allocate = lxpanel_size_allocate;
298 widget_class->configure_event = lxpanel_configure_event;
299 widget_class->style_set = lxpanel_style_set;
300 widget_class->map_event = lxpanel_map_event;
301 widget_class->button_press_event = lxpanel_button_press;
302
303 signals[ICON_SIZE_CHANGED] =
304 g_signal_new("icon-size-changed",
305 G_TYPE_FROM_CLASS(klass),
306 G_SIGNAL_RUN_LAST,
307 G_STRUCT_OFFSET(PanelToplevelClass, icon_size_changed),
308 NULL, NULL,
309 g_cclosure_marshal_VOID__VOID,
310 G_TYPE_NONE, 0, G_TYPE_NONE);
311 }
312
313 static void lxpanel_init(PanelToplevel *self)
314 {
315 Panel *p = g_new0(Panel, 1);
316
317 self->priv = p;
318 p->topgwin = self;
319 p->allign = ALLIGN_CENTER;
320 p->edge = EDGE_NONE;
321 p->widthtype = WIDTH_PERCENT;
322 p->width = 100;
323 p->heighttype = HEIGHT_PIXEL;
324 p->height = PANEL_HEIGHT_DEFAULT;
325 p->monitor = 0;
326 p->setdocktype = 1;
327 p->setstrut = 1;
328 p->round_corners = 0;
329 p->autohide = 0;
330 p->visible = TRUE;
331 p->height_when_hidden = 2;
332 p->transparent = 0;
333 p->alpha = 255;
334 gdk_color_parse("white", &p->gtintcolor);
335 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
336 p->usefontcolor = 0;
337 p->fontcolor = 0x00000000;
338 p->usefontsize = 0;
339 p->fontsize = 10;
340 p->spacing = 0;
341 p->icon_size = PANEL_ICON_SIZE;
342 p->icon_theme = gtk_icon_theme_get_default();
343 p->config = config_new();
344 p->defstyle = gtk_widget_get_default_style();
345 gtk_window_set_type_hint(GTK_WINDOW(self), GDK_WINDOW_TYPE_HINT_DOCK);
346 }
347
348 /* Allocate and initialize new Panel structure. */
349 static LXPanel* panel_allocate(void)
350 {
351 return g_object_new(LX_TYPE_PANEL, NULL);
352 }
353
354 void _panel_emit_icon_size_changed(LXPanel *p)
355 {
356 g_signal_emit(p, signals[ICON_SIZE_CHANGED], 0);
357 }
358
359 /* Normalize panel configuration after load from file or reconfiguration. */
360 static void panel_normalize_configuration(Panel* p)
361 {
362 panel_set_panel_configuration_changed( p );
363 if (p->width < 0)
364 p->width = 100;
365 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
366 p->width = 100;
367 p->heighttype = HEIGHT_PIXEL;
368 if (p->heighttype == HEIGHT_PIXEL) {
369 if (p->height < PANEL_HEIGHT_MIN)
370 p->height = PANEL_HEIGHT_MIN;
371 else if (p->height > PANEL_HEIGHT_MAX)
372 p->height = PANEL_HEIGHT_MAX;
373 }
374 if (p->monitor < 0)
375 p->monitor = -1;
376 if (p->background)
377 p->transparent = 0;
378 }
379
380 gboolean _panel_edge_can_strut(LXPanel *panel, int edge, gint monitor, gulong *size)
381 {
382 Panel *p;
383 GdkScreen *screen;
384 GdkRectangle rect;
385 GdkRectangle rect2;
386 gint n, i;
387 gulong s;
388
389 if (!gtk_widget_get_mapped(GTK_WIDGET(panel)))
390 return FALSE;
391
392 p = panel->priv;
393 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
394 if (p->autohide)
395 s = p->height_when_hidden;
396 else switch (edge)
397 {
398 case EDGE_LEFT:
399 case EDGE_RIGHT:
400 s = p->aw;
401 break;
402 case EDGE_TOP:
403 case EDGE_BOTTOM:
404 s = p->ah;
405 break;
406 default: /* error! */
407 return FALSE;
408 }
409
410 if (monitor < 0) /* screen span */
411 {
412 if (G_LIKELY(size))
413 *size = s;
414 return TRUE;
415 }
416
417 screen = gtk_widget_get_screen(GTK_WIDGET(panel));
418 n = gdk_screen_get_n_monitors(screen);
419 if (monitor >= n) /* hidden now */
420 return FALSE;
421 gdk_screen_get_monitor_geometry(screen, monitor, &rect);
422 switch (edge)
423 {
424 case EDGE_LEFT:
425 rect.width = rect.x;
426 rect.x = 0;
427 s += rect.width;
428 break;
429 case EDGE_RIGHT:
430 rect.x += rect.width;
431 rect.width = gdk_screen_get_width(screen) - rect.x;
432 s += rect.width;
433 break;
434 case EDGE_TOP:
435 rect.height = rect.y;
436 rect.y = 0;
437 s += rect.height;
438 break;
439 case EDGE_BOTTOM:
440 rect.y += rect.height;
441 rect.height = gdk_screen_get_height(screen) - rect.y;
442 s += rect.height;
443 break;
444 default: ;
445 }
446 if (rect.height == 0 || rect.width == 0) ; /* on a border of monitor */
447 else
448 {
449 for (i = 0; i < n; i++)
450 {
451 if (i == monitor)
452 continue;
453 gdk_screen_get_monitor_geometry(screen, i, &rect2);
454 if (gdk_rectangle_intersect(&rect, &rect2, NULL))
455 /* that monitor lies over the edge */
456 return FALSE;
457 }
458 }
459 if (G_LIKELY(size))
460 *size = s;
461 return TRUE;
462 }
463
464 /****************************************************
465 * panel's handlers for WM events *
466 ****************************************************/
467
468 void panel_set_wm_strut(Panel *p)
469 {
470 _panel_set_wm_strut(p->topgwin);
471 }
472
473 void _panel_set_wm_strut(LXPanel *panel)
474 {
475 int index;
476 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
477 Panel *p = panel->priv;
478 gulong strut_size;
479 gulong strut_lower;
480 gulong strut_upper;
481
482 if (!gtk_widget_get_mapped(GTK_WIDGET(panel)))
483 return;
484 /* most wm's tend to ignore struts of unmapped windows, and that's how
485 * lxpanel hides itself. so no reason to set it. */
486 if (p->autohide && p->height_when_hidden <= 0)
487 return;
488
489 /* Dispatch on edge to set up strut parameters. */
490 switch (p->edge)
491 {
492 case EDGE_LEFT:
493 index = 0;
494 strut_lower = p->ay;
495 strut_upper = p->ay + p->ah;
496 break;
497 case EDGE_RIGHT:
498 index = 1;
499 strut_lower = p->ay;
500 strut_upper = p->ay + p->ah;
501 break;
502 case EDGE_TOP:
503 index = 2;
504 strut_lower = p->ax;
505 strut_upper = p->ax + p->aw;
506 break;
507 case EDGE_BOTTOM:
508 index = 3;
509 strut_lower = p->ax;
510 strut_upper = p->ax + p->aw;
511 break;
512 default:
513 return;
514 }
515
516 /* Set up strut value in property format. */
517 gulong desired_strut[12];
518 memset(desired_strut, 0, sizeof(desired_strut));
519 if (p->setstrut &&
520 _panel_edge_can_strut(panel, p->edge, p->monitor, &strut_size))
521 {
522 desired_strut[index] = strut_size;
523 desired_strut[4 + index * 2] = strut_lower;
524 desired_strut[5 + index * 2] = strut_upper;
525 }
526 else
527 {
528 strut_size = 0;
529 strut_lower = 0;
530 strut_upper = 0;
531 }
532
533 /* If strut value changed, set the property value on the panel window.
534 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
535 if ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge))
536 {
537 p->strut_size = strut_size;
538 p->strut_lower = strut_lower;
539 p->strut_upper = strut_upper;
540 p->strut_edge = p->edge;
541
542 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
543 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
544 if (strut_size != 0)
545 {
546 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL,
547 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
548 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT,
549 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
550 }
551 else
552 {
553 XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT);
554 XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL);
555 }
556 }
557 }
558
559 /****************************************************
560 * panel's handlers for GTK events *
561 ****************************************************/
562
563
564 static void
565 on_root_bg_changed(FbBg *bg, LXPanel* p)
566 {
567 _panel_update_background( p );
568 }
569
570 void panel_determine_background_pixmap(Panel * panel, GtkWidget * widget, GdkWindow * window)
571 {
572 _panel_determine_background_pixmap(panel->topgwin, widget);
573 }
574
575 void _panel_determine_background_pixmap(LXPanel * panel, GtkWidget * widget)
576 {
577 GdkPixmap * pixmap = NULL;
578 GdkWindow * window = gtk_widget_get_window(widget);
579 Panel * p = panel->priv;
580
581 /* Free p->bg if it is not going to be used. */
582 if (( ! p->transparent) && (p->bg != NULL))
583 {
584 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, panel);
585 g_object_unref(p->bg);
586 p->bg = NULL;
587 }
588
589 if (p->background)
590 {
591 /* User specified background pixmap. */
592 if (p->background_file != NULL)
593 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
594 }
595
596 else if (p->transparent)
597 {
598 /* Transparent. Determine the appropriate value from the root pixmap. */
599 if (p->bg == NULL)
600 {
601 p->bg = fb_bg_get_for_display();
602 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), panel);
603 }
604 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
605 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
606 fb_bg_composite(pixmap, &p->gtintcolor, p->alpha);
607 }
608
609 if (pixmap != NULL)
610 {
611 gtk_widget_set_app_paintable(widget, TRUE );
612 gdk_window_set_back_pixmap(window, pixmap, FALSE);
613 g_object_unref(pixmap);
614 }
615 else
616 gtk_widget_set_app_paintable(widget, FALSE);
617 }
618
619 /* Update the background of the entire panel.
620 * This function should only be called after the panel has been realized. */
621 void panel_update_background(Panel * p)
622 {
623 _panel_update_background(p->topgwin);
624 }
625
626 static void _panel_update_background(LXPanel * p)
627 {
628 GtkWidget *w = GTK_WIDGET(p);
629 GList *plugins = NULL, *l;
630
631 /* Redraw the top level widget. */
632 _panel_determine_background_pixmap(p, w);
633 gdk_window_clear(gtk_widget_get_window(w));
634 gtk_widget_queue_draw(w);
635
636 /* Loop over all plugins redrawing each plugin. */
637 if (p->priv->box != NULL)
638 plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
639 for (l = plugins; l != NULL; l = l->next)
640 plugin_widget_set_background(l->data, p);
641 g_list_free(plugins);
642 }
643
644 /****************************************************
645 * autohide : borrowed from fbpanel *
646 ****************************************************/
647
648 /* Autohide is behaviour when panel hides itself when mouse is "far enough"
649 * and pops up again when mouse comes "close enough".
650 * Formally, it's a state machine with 3 states that driven by mouse
651 * coordinates and timer:
652 * 1. VISIBLE - ensures that panel is visible. When/if mouse goes "far enough"
653 * switches to WAITING state
654 * 2. WAITING - starts timer. If mouse comes "close enough", stops timer and
655 * switches to VISIBLE. If timer expires, switches to HIDDEN
656 * 3. HIDDEN - hides panel. When mouse comes "close enough" switches to VISIBLE
657 *
658 * Note 1
659 * Mouse coordinates are queried every PERIOD milisec
660 *
661 * Note 2
662 * If mouse is less then GAP pixels to panel it's considered to be close,
663 * otherwise it's far
664 */
665
666 #define GAP 2
667 #define PERIOD 300
668
669 typedef enum
670 {
671 AH_STATE_VISIBLE,
672 AH_STATE_WAITING,
673 AH_STATE_HIDDEN
674 } PanelAHState;
675
676 static void ah_state_set(LXPanel *p, PanelAHState ah_state);
677
678 static gboolean
679 mouse_watch(LXPanel *panel)
680 {
681 Panel *p = panel->priv;
682 gint x, y;
683
684 if (g_source_is_destroyed(g_main_current_source()))
685 return FALSE;
686
687 ENTER;
688 gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
689
690 /* Reduce sensitivity area
691 p->ah_far = ((x < p->cx - GAP) || (x > p->cx + p->cw + GAP)
692 || (y < p->cy - GAP) || (y > p->cy + p->ch + GAP));
693 */
694
695 gint cx, cy, cw, ch, gap;
696
697 cx = p->ax;
698 cy = p->ay;
699 cw = p->cw;
700 ch = p->ch;
701
702 if (cw == 1) cw = 0;
703 if (ch == 1) ch = 0;
704 /* reduce area which will raise panel so it does not interfere with apps */
705 if (p->ah_state == AH_STATE_HIDDEN) {
706 gap = MAX(p->height_when_hidden, GAP);
707 switch (p->edge) {
708 case EDGE_LEFT:
709 cw = gap;
710 break;
711 case EDGE_RIGHT:
712 cx = cx + cw - gap;
713 cw = gap;
714 break;
715 case EDGE_TOP:
716 ch = gap;
717 break;
718 case EDGE_BOTTOM:
719 cy = cy + ch - gap;
720 ch = gap;
721 break;
722 }
723 }
724 p->ah_far = ((x < cx) || (x > cx + cw) || (y < cy) || (y > cy + ch));
725
726 ah_state_set(panel, p->ah_state);
727 RET(TRUE);
728 }
729
730 static gboolean ah_state_hide_timeout(gpointer p)
731 {
732 if (!g_source_is_destroyed(g_main_current_source()))
733 {
734 ah_state_set(p, AH_STATE_HIDDEN);
735 ((LXPanel *)p)->priv->hide_timeout = 0;
736 }
737 return FALSE;
738 }
739
740 static void ah_state_set(LXPanel *panel, PanelAHState ah_state)
741 {
742 Panel *p = panel->priv;
743
744 ENTER;
745 if (p->ah_state != ah_state) {
746 p->ah_state = ah_state;
747 switch (ah_state) {
748 case AH_STATE_VISIBLE:
749 gtk_widget_show(GTK_WIDGET(panel));
750 gtk_widget_show(p->box);
751 gtk_widget_queue_resize(GTK_WIDGET(panel));
752 gtk_window_stick(GTK_WINDOW(panel));
753 p->visible = TRUE;
754 break;
755 case AH_STATE_WAITING:
756 if (p->hide_timeout)
757 g_source_remove(p->hide_timeout);
758 p->hide_timeout = g_timeout_add(2 * PERIOD, ah_state_hide_timeout, panel);
759 break;
760 case AH_STATE_HIDDEN:
761 if (p->height_when_hidden > 0)
762 gtk_widget_hide(p->box);
763 else
764 gtk_widget_hide(GTK_WIDGET(panel));
765 p->visible = FALSE;
766 }
767 } else if (p->autohide && p->ah_far) {
768 switch (ah_state) {
769 case AH_STATE_VISIBLE:
770 ah_state_set(panel, AH_STATE_WAITING);
771 break;
772 case AH_STATE_WAITING:
773 break;
774 case AH_STATE_HIDDEN:
775 /* configurator might change height_when_hidden value */
776 if (p->height_when_hidden > 0)
777 {
778 if (gtk_widget_get_visible(p->box))
779 {
780 gtk_widget_hide(p->box);
781 gtk_widget_show(GTK_WIDGET(panel));
782 }
783 }
784 else
785 if (gtk_widget_get_visible(GTK_WIDGET(panel)))
786 {
787 gtk_widget_hide(GTK_WIDGET(panel));
788 gtk_widget_show(p->box);
789 }
790 }
791 } else {
792 switch (ah_state) {
793 case AH_STATE_VISIBLE:
794 break;
795 case AH_STATE_WAITING:
796 if (p->hide_timeout)
797 g_source_remove(p->hide_timeout);
798 p->hide_timeout = 0;
799 /* continue with setting visible */
800 case AH_STATE_HIDDEN:
801 ah_state_set(panel, AH_STATE_VISIBLE);
802 }
803 }
804 RET();
805 }
806
807 /* starts autohide behaviour */
808 static void ah_start(LXPanel *p)
809 {
810 ENTER;
811 if (!p->priv->mouse_timeout)
812 p->priv->mouse_timeout = g_timeout_add(PERIOD, (GSourceFunc) mouse_watch, p);
813 RET();
814 }
815
816 /* stops autohide */
817 static void ah_stop(LXPanel *p)
818 {
819 ENTER;
820 if (p->priv->mouse_timeout) {
821 g_source_remove(p->priv->mouse_timeout);
822 p->priv->mouse_timeout = 0;
823 }
824 if (p->priv->hide_timeout) {
825 g_source_remove(p->priv->hide_timeout);
826 p->priv->hide_timeout = 0;
827 }
828 RET();
829 }
830 /* end of the autohide code
831 * ------------------------------------------------------------- */
832
833 static gint
834 panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
835 {
836 panel_configure( (LXPanel*)user_data, 0 );
837 return TRUE;
838 }
839
840 static void panel_popupmenu_config_plugin( GtkMenuItem* item, GtkWidget* plugin )
841 {
842 Panel *panel = PLUGIN_PANEL(plugin)->priv;
843
844 lxpanel_plugin_show_config_dialog(plugin);
845
846 /* FIXME: this should be more elegant */
847 panel->config_changed = TRUE;
848 }
849
850 static void panel_popupmenu_add_item( GtkMenuItem* item, LXPanel* panel )
851 {
852 /* panel_add_plugin( panel, panel->topgwin ); */
853 panel_configure( panel, 2 );
854 }
855
856 static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
857 {
858 Panel* panel = PLUGIN_PANEL(plugin)->priv;
859
860 /* If the configuration dialog is open, there will certainly be a crash if the
861 * user manipulates the Configured Plugins list, after we remove this entry.
862 * Close the configuration dialog if it is open. */
863 if (panel->pref_dialog != NULL)
864 {
865 gtk_widget_destroy(panel->pref_dialog);
866 panel->pref_dialog = NULL;
867 }
868 config_setting_destroy(g_object_get_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf));
869 /* reset conf pointer because the widget still may be referenced by configurator */
870 g_object_set_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf, NULL);
871
872 lxpanel_config_save(PLUGIN_PANEL(plugin));
873 gtk_widget_destroy(plugin);
874 }
875
876 /* FIXME: Potentially we can support multiple panels at the same edge,
877 * but currently this cannot be done due to some positioning problems. */
878 static char* gen_panel_name( int edge, gint monitor )
879 {
880 const char* edge_str = num2str( edge_pair, edge, "" );
881 char* name = NULL;
882 char* dir = _user_config_file_name("panels", NULL);
883 int i;
884 for( i = 0; i < G_MAXINT; ++i )
885 {
886 char* f;
887 if(monitor != 0)
888 name = g_strdup_printf( "%s-m%d-%d", edge_str, monitor, i );
889 else if( G_LIKELY( i > 0 ) )
890 name = g_strdup_printf( "%s%d", edge_str, i );
891 else
892 name = g_strdup( edge_str );
893
894 f = g_build_filename( dir, name, NULL );
895 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
896 {
897 g_free( f );
898 break;
899 }
900 g_free( name );
901 g_free( f );
902 }
903 g_free( dir );
904 return name;
905 }
906
907 /* FIXME: Potentially we can support multiple panels at the same edge,
908 * but currently this cannot be done due to some positioning problems. */
909 static void panel_popupmenu_create_panel( GtkMenuItem* item, LXPanel* panel )
910 {
911 gint m, e, monitors;
912 GdkScreen *screen;
913 LXPanel *new_panel = panel_allocate();
914 Panel *p = new_panel->priv;
915 config_setting_t *global;
916
917 /* Allocate the edge. */
918 screen = gtk_widget_get_screen(GTK_WIDGET(panel));
919 g_assert(screen);
920 monitors = gdk_screen_get_n_monitors(screen);
921 /* try to allocate edge on current monitor first */
922 m = panel->priv->monitor;
923 if (m < 0)
924 {
925 /* panel is spanned over the screen, guess from pointer now */
926 gint x, y;
927 gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
928 m = gdk_screen_get_monitor_at_point(screen, x, y);
929 }
930 for (e = 1; e < 5; ++e)
931 {
932 if (panel_edge_available(p, e, m))
933 {
934 p->edge = e;
935 p->monitor = m;
936 goto found_edge;
937 }
938 }
939 /* try all monitors */
940 for(m=0; m<monitors; ++m)
941 {
942 /* try each of the four edges */
943 for(e=1; e<5; ++e)
944 {
945 if(panel_edge_available(p,e,m)) {
946 p->edge = e;
947 p->monitor = m;
948 goto found_edge;
949 }
950 }
951 }
952
953 gtk_widget_destroy(GTK_WIDGET(new_panel));
954 g_warning("Error adding panel: There is no room for another panel. All the edges are taken.");
955 fm_show_error(NULL, NULL, _("There is no room for another panel. All the edges are taken."));
956 return;
957
958 found_edge:
959 p->name = gen_panel_name(p->edge, p->monitor);
960
961 /* create new config with first group "Global" */
962 global = config_group_add_subgroup(config_root_setting(p->config), "Global");
963 config_group_set_string(global, "edge", num2str(edge_pair, p->edge, "none"));
964 config_group_set_int(global, "monitor", p->monitor);
965 panel_configure(new_panel, 0);
966 panel_normalize_configuration(p);
967 panel_start_gui(new_panel, NULL);
968
969 lxpanel_config_save(new_panel);
970 all_panels = g_slist_prepend(all_panels, new_panel);
971 }
972
973 static void panel_popupmenu_delete_panel( GtkMenuItem* item, LXPanel* panel )
974 {
975 GtkWidget* dlg;
976 gboolean ok;
977 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel),
978 GTK_DIALOG_MODAL,
979 GTK_MESSAGE_QUESTION,
980 GTK_BUTTONS_OK_CANCEL,
981 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
982 panel_apply_icon(GTK_WINDOW(dlg));
983 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
984 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
985 gtk_widget_destroy( dlg );
986 if( ok )
987 {
988 gchar *fname;
989 all_panels = g_slist_remove( all_panels, panel );
990
991 /* delete the config file of this panel */
992 fname = _user_config_file_name("panels", panel->priv->name);
993 g_unlink( fname );
994 g_free(fname);
995 panel->priv->config_changed = 0;
996 gtk_widget_destroy(GTK_WIDGET(panel));
997 }
998 }
999
1000 static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
1001 {
1002 GtkWidget *about;
1003 const gchar* authors[] = {
1004 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
1005 "Jim Huang <jserv.tw@gmail.com>",
1006 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
1007 "Fred Chien <cfsghost@gmail.com>",
1008 "Daniel Kesler <kesler.daniel@gmail.com>",
1009 "Juergen Hoetzel <juergen@archlinux.org>",
1010 "Marty Jack <martyj19@comcast.net>",
1011 "Martin Bagge <brother@bsnet.se>",
1012 "Andriy Grytsenko <andrej@rep.kiev.ua>",
1013 "Giuseppe Penone <giuspen@gmail.com>",
1014 "Piotr Sipika <piotr.sipika@gmail.com>",
1015 NULL
1016 };
1017 /* TRANSLATORS: Replace this string with your names, one name per line. */
1018 gchar *translators = _( "translator-credits" );
1019
1020 about = gtk_about_dialog_new();
1021 panel_apply_icon(GTK_WINDOW(about));
1022 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
1023 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
1024
1025 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
1026 {
1027 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1028 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
1029 }
1030 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
1031 {
1032 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1033 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
1034 }
1035 else
1036 {
1037 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1038 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL));
1039 }
1040
1041 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2014"));
1042 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
1043 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.");
1044 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
1045 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
1046 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
1047 gtk_dialog_run(GTK_DIALOG(about));
1048 gtk_widget_destroy(about);
1049 }
1050
1051 void panel_apply_icon( GtkWindow *w )
1052 {
1053 GdkPixbuf* window_icon;
1054
1055 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
1056 {
1057 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
1058 }
1059 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
1060 {
1061 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
1062 }
1063 else
1064 {
1065 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL);
1066 }
1067 gtk_window_set_icon(w, window_icon);
1068 }
1069
1070 GtkMenu* lxpanel_get_plugin_menu( LXPanel* panel, GtkWidget* plugin, gboolean use_sub_menu )
1071 {
1072 GtkWidget *menu_item, *img;
1073 GtkMenu *ret,*menu;
1074 const LXPanelPluginInit *init;
1075 char* tmp;
1076
1077 ret = menu = GTK_MENU(gtk_menu_new());
1078
1079 if (plugin)
1080 {
1081 init = PLUGIN_CLASS(plugin);
1082 /* create single item - plugin instance settings */
1083 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1084 tmp = g_strdup_printf( _("\"%s\" Settings"), _(init->name) );
1085 menu_item = gtk_image_menu_item_new_with_label( tmp );
1086 g_free( tmp );
1087 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1088 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
1089 if( init->config )
1090 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
1091 else
1092 gtk_widget_set_sensitive( menu_item, FALSE );
1093 /* add custom items by plugin if requested */
1094 if (init->update_context_menu != NULL)
1095 use_sub_menu = init->update_context_menu(plugin, ret);
1096 /* append a separator */
1097 menu_item = gtk_separator_menu_item_new();
1098 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
1099 }
1100 if (use_sub_menu)
1101 menu = GTK_MENU(gtk_menu_new());
1102
1103 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
1104 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
1105 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1106 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1107 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
1108
1109 if( plugin )
1110 {
1111 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
1112 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(init->name) );
1113 menu_item = gtk_image_menu_item_new_with_label( tmp );
1114 g_free( tmp );
1115 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1116 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1117 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
1118 }
1119
1120 menu_item = gtk_separator_menu_item_new();
1121 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1122
1123 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1124 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
1125 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1126 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1127 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
1128
1129 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
1130 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
1131 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1132 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1133 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
1134
1135 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
1136 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
1137 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1138 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1139 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
1140 if( ! all_panels->next ) /* if this is the only panel */
1141 gtk_widget_set_sensitive( menu_item, FALSE );
1142
1143 menu_item = gtk_separator_menu_item_new();
1144 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1145
1146 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
1147 menu_item = gtk_image_menu_item_new_with_label(_("About"));
1148 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1149 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1150 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel->priv );
1151
1152 if( use_sub_menu )
1153 {
1154 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
1155 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
1156 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
1157 }
1158
1159 gtk_widget_show_all(GTK_WIDGET(ret));
1160
1161 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1162 return ret;
1163 }
1164
1165 /* for old plugins compatibility */
1166 GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
1167 {
1168 return lxpanel_get_plugin_menu(panel->topgwin, plugin->pwid, use_sub_menu);
1169 }
1170
1171 /****************************************************
1172 * panel creation *
1173 ****************************************************/
1174
1175 static void
1176 make_round_corners(Panel *p)
1177 {
1178 /* FIXME: This should be re-written with shape extension of X11 */
1179 /* gdk_window_shape_combine_mask() can be used */
1180 }
1181
1182 void panel_set_dock_type(Panel *p)
1183 {
1184 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1185
1186 if (p->setdocktype) {
1187 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
1188 XChangeProperty(xdisplay, p->topxwin,
1189 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
1190 PropModeReplace, (unsigned char *) &state, 1);
1191 }
1192 else {
1193 XDeleteProperty( xdisplay, p->topxwin, a_NET_WM_WINDOW_TYPE );
1194 }
1195 }
1196
1197 void panel_establish_autohide(Panel *p)
1198 {
1199 _panel_establish_autohide(p->topgwin);
1200 }
1201
1202 void _panel_establish_autohide(LXPanel *p)
1203 {
1204 if (p->priv->autohide)
1205 ah_start(p);
1206 else
1207 {
1208 ah_stop(p);
1209 ah_state_set(p, AH_STATE_VISIBLE);
1210 }
1211 }
1212
1213 /* Set an image from a file with scaling to the panel icon size. */
1214 void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
1215 {
1216 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
1217 if (pixbuf != NULL)
1218 {
1219 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1220 g_object_unref(pixbuf);
1221 }
1222 }
1223
1224 void lxpanel_image_set_from_file(LXPanel * p, GtkWidget * image, const char * file)
1225 {
1226 panel_image_set_from_file(p->priv, image, file);
1227 }
1228
1229 /* Set an image from a icon theme with scaling to the panel icon size. */
1230 gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
1231 {
1232 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
1233 {
1234 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
1235 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1236 g_object_unref(pixbuf);
1237 return TRUE;
1238 }
1239 return FALSE;
1240 }
1241
1242 gboolean lxpanel_image_set_icon_theme(LXPanel * p, GtkWidget * image, const gchar * icon)
1243 {
1244 return panel_image_set_icon_theme(p->priv, image, icon);
1245 }
1246
1247 static int
1248 panel_parse_plugin(LXPanel *p, config_setting_t *cfg)
1249 {
1250 const char *type = NULL;
1251
1252 ENTER;
1253 config_setting_lookup_string(cfg, "type", &type);
1254 DBG("plug %s\n", type);
1255
1256 if (!type || lxpanel_add_plugin(p, type, cfg, -1) == NULL) {
1257 g_warning( "lxpanel: can't load %s plugin", type);
1258 goto error;
1259 }
1260 RET(1);
1261
1262 error:
1263 RET(0);
1264 }
1265
1266 static void
1267 panel_start_gui(LXPanel *panel, config_setting_t *list)
1268 {
1269 Atom state[3];
1270 XWMHints wmhints;
1271 gulong val;
1272 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1273 Panel *p = panel->priv;
1274 GtkWidget *w = GTK_WIDGET(panel);
1275 config_setting_t *s;
1276 int i;
1277
1278 ENTER;
1279
1280 g_debug("panel_start_gui on '%s'", p->name);
1281 p->curdesk = get_net_current_desktop();
1282 p->desknum = get_net_number_of_desktops();
1283 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1284 p->ax = p->ay = p->aw = p->ah = 0;
1285
1286 /* main toplevel window */
1287 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
1288 gtk_widget_set_name(w, "PanelToplevel");
1289 p->display = gdk_display_get_default();
1290 gtk_container_set_border_width(GTK_CONTAINER(panel), 0);
1291 gtk_window_set_resizable(GTK_WINDOW(panel), FALSE);
1292 gtk_window_set_wmclass(GTK_WINDOW(panel), "panel", "lxpanel");
1293 gtk_window_set_title(GTK_WINDOW(panel), "panel");
1294 gtk_window_set_position(GTK_WINDOW(panel), GTK_WIN_POS_NONE);
1295 gtk_window_set_decorated(GTK_WINDOW(panel), FALSE);
1296
1297 if (G_UNLIKELY(win_grp == NULL))
1298 {
1299 win_grp = gtk_window_group_new();
1300 g_object_add_weak_pointer(G_OBJECT(win_grp), (gpointer *)&win_grp);
1301 gtk_window_group_add_window(win_grp, (GtkWindow*)panel);
1302 g_object_unref(win_grp);
1303 }
1304 else
1305 gtk_window_group_add_window(win_grp, (GtkWindow*)panel);
1306
1307 gtk_widget_add_events( w, GDK_BUTTON_PRESS_MASK );
1308
1309 gtk_widget_realize(w);
1310 //gdk_window_set_decorations(gtk_widget_get_window(p->topgwin), 0);
1311
1312 // main layout manager as a single child of panel
1313 p->box = panel_box_new(panel, FALSE, 0);
1314 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
1315 gtk_container_add(GTK_CONTAINER(panel), p->box);
1316 gtk_widget_show(p->box);
1317 if (p->round_corners)
1318 make_round_corners(p);
1319
1320 p->topxwin = GDK_WINDOW_XWINDOW(gtk_widget_get_window(w));
1321 DBG("topxwin = %x\n", p->topxwin);
1322
1323 /* the settings that should be done before window is mapped */
1324 wmhints.flags = InputHint;
1325 wmhints.input = 0;
1326 XSetWMHints (xdisplay, p->topxwin, &wmhints);
1327 #define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
1328 val = WIN_HINTS_SKIP_FOCUS;
1329 XChangeProperty(xdisplay, p->topxwin,
1330 XInternAtom(xdisplay, "_WIN_HINTS", False), XA_CARDINAL, 32,
1331 PropModeReplace, (unsigned char *) &val, 1);
1332
1333 panel_set_dock_type(p);
1334
1335 /* window mapping point */
1336 gtk_widget_show_all(w);
1337
1338 /* the settings that should be done after window is mapped */
1339 _panel_establish_autohide(panel);
1340
1341 /* send it to running wm */
1342 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, G_MAXULONG, 0, 0, 0, 0);
1343 /* and assign it ourself just for case when wm is not running */
1344 val = G_MAXULONG;
1345 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
1346 PropModeReplace, (unsigned char *) &val, 1);
1347
1348 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1349 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1350 state[2] = a_NET_WM_STATE_STICKY;
1351 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STATE, XA_ATOM,
1352 32, PropModeReplace, (unsigned char *) state, 3);
1353
1354 p->initialized = TRUE;
1355
1356 if (list) for (i = 1; (s = config_setting_get_elem(list, i)) != NULL; )
1357 if (strcmp(config_setting_get_name(s), "Plugin") == 0 &&
1358 panel_parse_plugin(panel, s)) /* success on plugin start */
1359 i++;
1360 else /* remove invalid data from config */
1361 config_setting_remove_elem(list, i);
1362
1363 RET();
1364 }
1365
1366 /* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1367 void panel_adjust_geometry_terminology(Panel * p)
1368 {
1369 if ((p->height_label != NULL) && (p->width_label != NULL)
1370 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
1371 {
1372 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1373 {
1374 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1375 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
1376 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1377 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
1378 }
1379 else
1380 {
1381 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1382 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
1383 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1384 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
1385 }
1386 }
1387 }
1388
1389 /* Draw text into a label, with the user preference color and optionally bold. */
1390 void panel_draw_label_text(Panel * p, GtkWidget * label, const char * text,
1391 gboolean bold, float custom_size_factor,
1392 gboolean custom_color)
1393 {
1394 if (text == NULL)
1395 {
1396 /* Null string. */
1397 gtk_label_set_text(GTK_LABEL(label), NULL);
1398 return;
1399 }
1400
1401 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1402 int font_desc;
1403 if (p->usefontsize)
1404 font_desc = p->fontsize;
1405 else
1406 {
1407 GtkStyle *style = gtk_widget_get_style(label);
1408 font_desc = pango_font_description_get_size(style->font_desc) / PANGO_SCALE;
1409 }
1410 font_desc *= custom_size_factor;
1411
1412 /* Check the string for characters that need to be escaped.
1413 * If any are found, create the properly escaped string and use it instead. */
1414 const char * valid_markup = text;
1415 char * escaped_text = NULL;
1416 const char * q;
1417 for (q = text; *q != '\0'; q += 1)
1418 {
1419 if ((*q == '<') || (*q == '>') || (*q == '&'))
1420 {
1421 escaped_text = g_markup_escape_text(text, -1);
1422 valid_markup = escaped_text;
1423 break;
1424 }
1425 }
1426
1427 gchar * formatted_text;
1428 if ((custom_color) && (p->usefontcolor))
1429 {
1430 /* Color, optionally bold. */
1431 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
1432 font_desc,
1433 gcolor2rgb24(&p->gfontcolor),
1434 ((bold) ? "<b>" : ""),
1435 valid_markup,
1436 ((bold) ? "</b>" : ""));
1437 }
1438 else
1439 {
1440 /* No color, optionally bold. */
1441 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
1442 font_desc,
1443 ((bold) ? "<b>" : ""),
1444 valid_markup,
1445 ((bold) ? "</b>" : ""));
1446 }
1447
1448 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1449 g_free(formatted_text);
1450 g_free(escaped_text);
1451 }
1452
1453 void lxpanel_draw_label_text(LXPanel * p, GtkWidget * label, const char * text,
1454 gboolean bold, float custom_size_factor,
1455 gboolean custom_color)
1456 {
1457 panel_draw_label_text(p->priv, label, text, bold, custom_size_factor, custom_color);
1458 }
1459
1460 void panel_set_panel_configuration_changed(Panel *p)
1461 {
1462 _panel_set_panel_configuration_changed(p->topgwin);
1463 }
1464
1465 void _panel_set_panel_configuration_changed(LXPanel *panel)
1466 {
1467 Panel *p = panel->priv;
1468 GList *plugins, *l;
1469
1470 GtkOrientation previous_orientation = p->orientation;
1471 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1472 ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1473
1474 /* either first run or orientation was changed */
1475 if (!p->initialized || previous_orientation != p->orientation)
1476 {
1477 panel_adjust_geometry_terminology(p);
1478 if (p->initialized)
1479 p->height = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1480 if (p->height_control != NULL)
1481 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
1482 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1483 {
1484 int value = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? gdk_screen_width() : gdk_screen_height());
1485 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1486 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1487 }
1488 }
1489
1490 /* FIXME: it's deprecated, kept for binary compatibility */
1491 if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
1492 p->my_box_new = gtk_hbox_new;
1493 p->my_separator_new = gtk_vseparator_new;
1494 } else {
1495 p->my_box_new = gtk_vbox_new;
1496 p->my_separator_new = gtk_hseparator_new;
1497 }
1498
1499 /* recreate the main layout box */
1500 if (p->box != NULL)
1501 {
1502 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), p->orientation);
1503 }
1504
1505 /* NOTE: This loop won't be executed when panel started since
1506 plugins are not loaded at that time.
1507 This is used when the orientation of the panel is changed
1508 from the config dialog, and plugins should be re-layout.
1509 */
1510 plugins = p->box ? gtk_container_get_children(GTK_CONTAINER(p->box)) : NULL;
1511 for( l = plugins; l; l = l->next ) {
1512 GtkWidget *w = (GtkWidget*)l->data;
1513 const LXPanelPluginInit *init = PLUGIN_CLASS(w);
1514 if (init->reconfigure)
1515 init->reconfigure(panel, w);
1516 }
1517 g_list_free(plugins);
1518 /* panel geometry changed? update panel background then */
1519 _panel_queue_update_background(panel);
1520 }
1521
1522 static int
1523 panel_parse_global(Panel *p, config_setting_t *cfg)
1524 {
1525 const char *str;
1526 gint i;
1527
1528 /* check Global config */
1529 if (!cfg || strcmp(config_setting_get_name(cfg), "Global") != 0)
1530 {
1531 g_warning( "lxpanel: Global section not found");
1532 RET(0);
1533 }
1534 if (config_setting_lookup_string(cfg, "edge", &str))
1535 p->edge = str2num(edge_pair, str, EDGE_NONE);
1536 if (config_setting_lookup_string(cfg, "allign", &str))
1537 p->allign = str2num(allign_pair, str, ALLIGN_NONE);
1538 config_setting_lookup_int(cfg, "monitor", &p->monitor);
1539 config_setting_lookup_int(cfg, "margin", &p->margin);
1540 if (config_setting_lookup_string(cfg, "widthtype", &str))
1541 p->widthtype = str2num(width_pair, str, WIDTH_NONE);
1542 config_setting_lookup_int(cfg, "width", &p->width);
1543 if (config_setting_lookup_string(cfg, "heighttype", &str))
1544 p->heighttype = str2num(height_pair, str, HEIGHT_NONE);
1545 config_setting_lookup_int(cfg, "height", &p->height);
1546 if (config_setting_lookup_int(cfg, "spacing", &i) && i > 0)
1547 p->spacing = i;
1548 if (config_setting_lookup_int(cfg, "setdocktype", &i))
1549 p->setdocktype = i != 0;
1550 if (config_setting_lookup_int(cfg, "setpartialstrut", &i))
1551 p->setstrut = i != 0;
1552 if (config_setting_lookup_int(cfg, "RoundCorners", &i))
1553 p->round_corners = i != 0;
1554 if (config_setting_lookup_int(cfg, "transparent", &i))
1555 p->transparent = i != 0;
1556 if (config_setting_lookup_int(cfg, "alpha", &p->alpha))
1557 {
1558 if (p->alpha > 255)
1559 p->alpha = 255;
1560 }
1561 if (config_setting_lookup_int(cfg, "autohide", &i))
1562 p->autohide = i != 0;
1563 if (config_setting_lookup_int(cfg, "heightwhenhidden", &i))
1564 p->height_when_hidden = MAX(0, i);
1565 if (config_setting_lookup_string(cfg, "tintcolor", &str))
1566 {
1567 if (!gdk_color_parse (str, &p->gtintcolor))
1568 gdk_color_parse ("white", &p->gtintcolor);
1569 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1570 DBG("tintcolor=%x\n", p->tintcolor);
1571 }
1572 if (config_setting_lookup_int(cfg, "usefontcolor", &i))
1573 p->usefontcolor = i != 0;
1574 if (config_setting_lookup_string(cfg, "fontcolor", &str))
1575 {
1576 if (!gdk_color_parse (str, &p->gfontcolor))
1577 gdk_color_parse ("black", &p->gfontcolor);
1578 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1579 DBG("fontcolor=%x\n", p->fontcolor);
1580 }
1581 if (config_setting_lookup_int(cfg, "usefontsize", &i))
1582 p->usefontsize = i != 0;
1583 if (config_setting_lookup_int(cfg, "fontsize", &i) && i > 0)
1584 p->fontsize = i;
1585 if (config_setting_lookup_int(cfg, "background", &i))
1586 p->background = i != 0;
1587 if (config_setting_lookup_string(cfg, "backgroundfile", &str))
1588 p->background_file = g_strdup(str);
1589 config_setting_lookup_int(cfg, "iconsize", &p->icon_size);
1590
1591 panel_normalize_configuration(p);
1592
1593 return 1;
1594 }
1595
1596 static void on_monitors_changed(GdkScreen* screen, gpointer unused)
1597 {
1598 GSList *pl;
1599 int monitors = gdk_screen_get_n_monitors(screen);
1600
1601 for (pl = all_panels; pl; pl = pl->next)
1602 {
1603 LXPanel *p = pl->data;
1604
1605 /* handle connecting and disconnecting monitors now */
1606 if (p->priv->monitor < monitors && !p->priv->initialized)
1607 panel_start_gui(p, config_setting_get_member(config_root_setting(p->priv->config), ""));
1608 else if (p->priv->monitor >= monitors && p->priv->initialized)
1609 panel_stop_gui(p);
1610 /* resize panel if appropriate monitor changed its size or position */
1611 else
1612 {
1613 /* SF bug #666: after screen resize panel cannot establish
1614 right size since cannot be moved while is hidden */
1615 ah_state_set(p, AH_STATE_VISIBLE);
1616 gtk_widget_queue_resize(GTK_WIDGET(p));
1617 }
1618 }
1619 }
1620
1621 static int panel_start(LXPanel *p)
1622 {
1623 config_setting_t *list;
1624 GdkScreen *screen = gdk_screen_get_default();
1625
1626 /* parse global section */
1627 ENTER;
1628
1629 list = config_setting_get_member(config_root_setting(p->priv->config), "");
1630 if (!list || !panel_parse_global(p->priv, config_setting_get_elem(list, 0)))
1631 RET(0);
1632
1633 if (p->priv->monitor < gdk_screen_get_n_monitors(screen))
1634 panel_start_gui(p, list);
1635 if (monitors_handler == 0)
1636 monitors_handler = g_signal_connect(screen, "monitors-changed",
1637 G_CALLBACK(on_monitors_changed), NULL);
1638 return 1;
1639 }
1640
1641 void panel_destroy(Panel *p)
1642 {
1643 gtk_widget_destroy(GTK_WIDGET(p->topgwin));
1644 }
1645
1646 LXPanel* panel_new( const char* config_file, const char* config_name )
1647 {
1648 LXPanel* panel = NULL;
1649
1650 if (G_LIKELY(config_file))
1651 {
1652 panel = panel_allocate();
1653 panel->priv->name = g_strdup(config_name);
1654 g_debug("starting panel from file %s",config_file);
1655 if (!config_read_file(panel->priv->config, config_file) ||
1656 !panel_start(panel))
1657 {
1658 g_warning( "lxpanel: can't start panel");
1659 gtk_widget_destroy(GTK_WIDGET(panel));
1660 panel = NULL;
1661 }
1662 }
1663 return panel;
1664 }
1665
1666
1667 GtkOrientation panel_get_orientation(LXPanel *panel)
1668 {
1669 return panel->priv->orientation;
1670 }
1671
1672 gint panel_get_icon_size(LXPanel *panel)
1673 {
1674 return panel->priv->icon_size;
1675 }
1676
1677 gint panel_get_height(LXPanel *panel)
1678 {
1679 return panel->priv->height;
1680 }
1681
1682 Window panel_get_xwindow(LXPanel *panel)
1683 {
1684 return panel->priv->topxwin;
1685 }
1686
1687 gint panel_get_monitor(LXPanel *panel)
1688 {
1689 return panel->priv->monitor;
1690 }
1691
1692 GtkStyle *panel_get_defstyle(LXPanel *panel)
1693 {
1694 return panel->priv->defstyle;
1695 }
1696
1697 GtkIconTheme *panel_get_icon_theme(LXPanel *panel)
1698 {
1699 return panel->priv->icon_theme;
1700 }
1701
1702 gboolean panel_is_at_bottom(LXPanel *panel)
1703 {
1704 return panel->priv->edge == EDGE_BOTTOM;
1705 }
1706
1707 gboolean panel_is_dynamic(LXPanel *panel)
1708 {
1709 return panel->priv->widthtype == WIDTH_REQUEST;
1710 }
1711
1712 GtkWidget *panel_box_new(LXPanel *panel, gboolean homogeneous, gint spacing)
1713 {
1714 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1715 return gtk_hbox_new(homogeneous, spacing);
1716 return gtk_vbox_new(homogeneous, spacing);
1717 }
1718
1719 GtkWidget *panel_separator_new(LXPanel *panel)
1720 {
1721 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1722 return gtk_vseparator_new();
1723 return gtk_hseparator_new();
1724 }
1725
1726 gboolean _class_is_present(const LXPanelPluginInit *init)
1727 {
1728 GSList *sl;
1729
1730 for (sl = all_panels; sl; sl = sl->next )
1731 {
1732 LXPanel *panel = (LXPanel*)sl->data;
1733 GList *plugins, *p;
1734
1735 if (panel->priv->box == NULL)
1736 continue;
1737 plugins = gtk_container_get_children(GTK_CONTAINER(panel->priv->box));
1738 for (p = plugins; p; p = p->next)
1739 if (PLUGIN_CLASS(p->data) == init)
1740 {
1741 g_list_free(plugins);
1742 return TRUE;
1743 }
1744 g_list_free(plugins);
1745 }
1746 return FALSE;
1747 }