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