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