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