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