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