debian: set build dependency as libfm-gtk-dev | libfm-dev for non-PPA compatibility
[lxde/lxpanel.git] / src / panel.c
CommitLineData
50928868 1/*
b840f7cc 2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
e68b47dc
JH
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
a52c2257 19#ifdef HAVE_CONFIG_H
cf701cb7 20#include <config.h>
a52c2257
HJYP
21#endif
22
cf701cb7 23#include <glib/gi18n.h>
a52c2257 24#include <stdlib.h>
16fbda14 25#include <glib/gstdio.h>
a52c2257
HJYP
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>
e68b47dc 32#include <gdk/gdkx.h>
4f5769fc 33#include <libfm/fm-gtk.h>
a52c2257 34
1f4bc3aa
AG
35#define __LXPANEL_INTERNALS__
36
b31cb1d2 37#include "private.h"
a52c2257
HJYP
38#include "misc.h"
39#include "bg.h"
a52c2257 40
77886b88 41#include "lxpanelctl.h"
cf701cb7 42#include "dbg.h"
77886b88 43
a52c2257
HJYP
44static gchar *cfgfile = NULL;
45static gchar version[] = VERSION;
46gchar *cprofile = "default";
47
e2957bd2
HJYP
48static GtkWindowGroup* win_grp; /* window group used to limit the scope of model dialog. */
49
e68b47dc 50static int config = 0;
22242ed4 51FbEv *fbev = NULL;
a52c2257 52
cf701cb7 53GSList* all_panels = NULL; /* a single-linked list storing all panels */
a52c2257 54
f7cb330e
HJYP
55gboolean is_restarting = FALSE;
56
78b42e90
AG
57gboolean is_in_lxde = FALSE;
58
a7bd16a4 59static void panel_start_gui(LXPanel *p);
78b42e90 60static void ah_start(LXPanel *p);
a7bd16a4
AG
61static void ah_stop(LXPanel *p);
62static void on_root_bg_changed(FbBg *bg, LXPanel* p);
50928868 63static void _panel_update_background(LXPanel * p);
424b1f06 64
a7bd16a4 65G_DEFINE_TYPE(PanelToplevel, lxpanel, GTK_TYPE_WINDOW);
cb22c87c 66
a7bd16a4 67static void lxpanel_finalize(GObject *object)
cb22c87c 68{
a7bd16a4
AG
69 LXPanel *self = LXPANEL(object);
70 Panel *p = self->priv;
71
72 if( p->config_changed )
73 lxpanel_config_save( self );
74 config_destroy(p->config);
75
0bfcc6c9 76 XFree(p->workarea);
a7bd16a4
AG
77 g_free( p->background_file );
78 g_slist_free( p->system_menus );
79
80 g_free( p->name );
81 g_free(p);
82
83 G_OBJECT_CLASS(lxpanel_parent_class)->finalize(object);
84}
85
86static void lxpanel_destroy(GtkObject *object)
cb22c87c 87{
a7bd16a4
AG
88 LXPanel *self = LXPANEL(object);
89 Panel *p = self->priv;
90 Display *xdisplay;
91
92 if (p->autohide)
93 ah_stop(self);
94
95 if (p->pref_dialog != NULL)
96 gtk_widget_destroy(p->pref_dialog);
97 p->pref_dialog = NULL;
98
99 if (p->plugin_pref_dialog != NULL)
100 /* just close the dialog, it will do all required cleanup */
101 gtk_dialog_response(GTK_DIALOG(p->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
102
103 if (p->bg != NULL)
104 {
105 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, self);
106 g_object_unref(p->bg);
107 p->bg = NULL;
108 }
109
110 if (p->initialized)
111 {
112 gtk_window_group_remove_window(win_grp, GTK_WINDOW(self));
113 xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
114 gdk_flush();
115 XFlush(xdisplay);
116 XSync(xdisplay, True);
117 p->initialized = FALSE;
118 }
119
50928868
AG
120 if (p->background_update_queued)
121 {
122 g_source_remove(p->background_update_queued);
123 p->background_update_queued = 0;
124 }
125
a7bd16a4 126 GTK_OBJECT_CLASS(lxpanel_parent_class)->destroy(object);
cb22c87c 127}
a7bd16a4 128
50928868 129static gboolean idle_update_background(gpointer p)
78b42e90 130{
50928868
AG
131 LXPanel *panel = LXPANEL(p);
132
133 if (g_source_is_destroyed(g_main_current_source()))
134 return FALSE;
135
78b42e90
AG
136 /* Panel could be destroyed while background update scheduled */
137#if GTK_CHECK_VERSION(2, 20, 0)
138 if (gtk_widget_get_realized(p))
139#else
140 if (GTK_WIDGET_REALIZED(p))
141#endif
142 {
143 gdk_display_sync( gtk_widget_get_display(p) );
50928868 144 _panel_update_background(panel);
78b42e90 145 }
50928868 146 panel->priv->background_update_queued = 0;
78b42e90
AG
147
148 return FALSE;
149}
150
50928868
AG
151void _panel_queue_update_background(LXPanel *panel)
152{
153 if (panel->priv->background_update_queued)
154 return;
155 panel->priv->background_update_queued = g_idle_add_full(G_PRIORITY_HIGH,
156 idle_update_background,
157 panel, NULL);
158}
159
78b42e90
AG
160static void lxpanel_realize(GtkWidget *widget)
161{
162 GTK_WIDGET_CLASS(lxpanel_parent_class)->realize(widget);
163
50928868 164 _panel_queue_update_background(LXPANEL(widget));
78b42e90
AG
165}
166
167static void lxpanel_style_set(GtkWidget *widget, GtkStyle* prev)
168{
169 GTK_WIDGET_CLASS(lxpanel_parent_class)->style_set(widget, prev);
170
171 /* FIXME: This dirty hack is used to fix the background of systray... */
50928868 172 _panel_queue_update_background(LXPANEL(widget));
78b42e90
AG
173}
174
175static void lxpanel_size_request(GtkWidget *widget, GtkRequisition *req)
176{
177 Panel *p = LXPANEL(widget)->priv;
178
179 GTK_WIDGET_CLASS(lxpanel_parent_class)->size_request(widget, req);
180
181 if (!p->visible)
182 /* When the panel is in invisible state, the content box also got hidden, thus always
183 * report 0 size. Ask the content box instead for its size. */
184 gtk_widget_size_request(p->box, req);
185
186 /* FIXME: is this ever required? */
187 if (p->widthtype == WIDTH_REQUEST)
188 p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? req->width : req->height;
189 if (p->heighttype == HEIGHT_REQUEST)
190 p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? req->height : req->width;
191 calculate_position(p);
192
193 gtk_widget_set_size_request( widget, p->aw, p->ah );
194}
195
196static void lxpanel_size_allocate(GtkWidget *widget, GtkAllocation *a)
197{
198 Panel *p = LXPANEL(widget)->priv;
199
200 GTK_WIDGET_CLASS(lxpanel_parent_class)->size_allocate(widget, a);
201
202 if (p->widthtype == WIDTH_REQUEST)
203 p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? a->width : a->height;
204 if (p->heighttype == HEIGHT_REQUEST)
205 p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? a->height : a->width;
206 calculate_position(p);
207
208 if (a->width != p->aw || a->height != p->ah || a->x != p->ax || a->y != p->ay)
209 {
210 gtk_window_move(GTK_WINDOW(widget), p->ax, p->ay);
211 _panel_set_wm_strut(LXPANEL(widget));
212 }
50928868
AG
213 else if (p->background_update_queued)
214 {
215 g_source_remove(p->background_update_queued);
216 p->background_update_queued = 0;
217#if GTK_CHECK_VERSION(2, 20, 0)
218 if (gtk_widget_get_realized(widget))
219#else
220 if (GTK_WIDGET_REALIZED(widget))
221#endif
222 _panel_update_background(LXPANEL(widget));
223 }
78b42e90
AG
224}
225
226static gboolean lxpanel_configure_event (GtkWidget *widget, GdkEventConfigure *e)
227{
228 Panel *p = LXPANEL(widget)->priv;
229
230 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
231 goto ok;
232 p->cw = e->width;
233 p->ch = e->height;
234 p->cx = e->x;
235 p->cy = e->y;
236
237 if (p->transparent)
238 fb_bg_notify_changed_bg(p->bg);
239ok:
240 return GTK_WIDGET_CLASS(lxpanel_parent_class)->configure_event(widget, e);
241}
242
243static gboolean lxpanel_map_event(GtkWidget *widget, GdkEventAny *event)
244{
245 Panel *p = PLUGIN_PANEL(widget)->priv;
246
247 if (p->autohide)
248 ah_start(LXPANEL(widget));
249 return GTK_WIDGET_CLASS(lxpanel_parent_class)->map_event(widget, event);
250}
251
252/* Handler for "button_press_event" signal with Panel as parameter. */
253static gboolean lxpanel_button_press(GtkWidget *widget, GdkEventButton *event)
254{
255 if (event->button == 3) /* right button */
256 {
257 GtkMenu* popup = (GtkMenu*) lxpanel_get_plugin_menu(LXPANEL(widget), NULL, FALSE);
258 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
259 return TRUE;
260 }
d4943c80 261 return FALSE;
78b42e90
AG
262}
263
a7bd16a4 264static void lxpanel_class_init(PanelToplevelClass *klass)
cb22c87c 265{
a7bd16a4
AG
266 GObjectClass *gobject_class = (GObjectClass *)klass;
267 GtkObjectClass *gtk_object_class = (GtkObjectClass *)klass;
424b1f06 268 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
a7bd16a4
AG
269
270 gobject_class->finalize = lxpanel_finalize;
271 gtk_object_class->destroy = lxpanel_destroy;
78b42e90 272 widget_class->realize = lxpanel_realize;
424b1f06
AG
273 widget_class->size_request = lxpanel_size_request;
274 widget_class->size_allocate = lxpanel_size_allocate;
424b1f06 275 widget_class->configure_event = lxpanel_configure_event;
424b1f06 276 widget_class->style_set = lxpanel_style_set;
78b42e90
AG
277 widget_class->map_event = lxpanel_map_event;
278 widget_class->button_press_event = lxpanel_button_press;
cb22c87c
HJYP
279}
280
a7bd16a4 281static void lxpanel_init(PanelToplevel *self)
9dd114c4 282{
a7bd16a4
AG
283 Panel *p = g_new0(Panel, 1);
284
285 self->priv = p;
286 p->topgwin = self;
9dd114c4 287 p->allign = ALLIGN_CENTER;
288 p->edge = EDGE_NONE;
289 p->widthtype = WIDTH_PERCENT;
290 p->width = 100;
291 p->heighttype = HEIGHT_PIXEL;
292 p->height = PANEL_HEIGHT_DEFAULT;
64afc832 293 p->monitor = 0;
9dd114c4 294 p->setdocktype = 1;
295 p->setstrut = 1;
296 p->round_corners = 0;
297 p->autohide = 0;
298 p->visible = TRUE;
299 p->height_when_hidden = 2;
300 p->transparent = 0;
bf2140f8
MJ
301 p->alpha = 255;
302 gdk_color_parse("white", &p->gtintcolor);
303 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
9dd114c4 304 p->usefontcolor = 0;
305 p->fontcolor = 0x00000000;
1869ef90
LK
306 p->usefontsize = 0;
307 p->fontsize = 10;
9dd114c4 308 p->spacing = 0;
8f9e6256 309 p->icon_size = PANEL_ICON_SIZE;
21f91705 310 p->icon_theme = gtk_icon_theme_get_default();
17fab6e5 311 p->config = config_new();
761e0dae 312 p->defstyle = gtk_widget_get_default_style();
c1910c15 313 gtk_window_set_type_hint(GTK_WINDOW(self), GDK_WINDOW_TYPE_HINT_DOCK);
a7bd16a4
AG
314}
315
316/* Allocate and initialize new Panel structure. */
317static LXPanel* panel_allocate(void)
318{
319 return g_object_new(LX_TYPE_PANEL, NULL);
9dd114c4 320}
321
322/* Normalize panel configuration after load from file or reconfiguration. */
323static void panel_normalize_configuration(Panel* p)
324{
2918994e 325 panel_set_panel_configuration_changed( p );
9dd114c4 326 if (p->width < 0)
327 p->width = 100;
328 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
329 p->width = 100;
330 p->heighttype = HEIGHT_PIXEL;
331 if (p->heighttype == HEIGHT_PIXEL) {
332 if (p->height < PANEL_HEIGHT_MIN)
333 p->height = PANEL_HEIGHT_MIN;
334 else if (p->height > PANEL_HEIGHT_MAX)
335 p->height = PANEL_HEIGHT_MAX;
336 }
64afc832
R
337 if (p->monitor < 0)
338 p->monitor = 0;
9dd114c4 339 if (p->background)
340 p->transparent = 0;
341}
342
a52c2257
HJYP
343/****************************************************
344 * panel's handlers for WM events *
345 ****************************************************/
a52c2257 346
22242ed4 347void panel_set_wm_strut(Panel *p)
a52c2257 348{
a7bd16a4
AG
349 _panel_set_wm_strut(p->topgwin);
350}
351
352void _panel_set_wm_strut(LXPanel *panel)
353{
d1d43629 354 int index;
09fa171b 355 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
a7bd16a4 356 Panel *p = panel->priv;
d1d43629 357 gulong strut_size;
358 gulong strut_lower;
359 gulong strut_upper;
a52c2257 360
9a2c13d0 361#if GTK_CHECK_VERSION(2, 20, 0)
a7bd16a4 362 if (!gtk_widget_get_mapped(GTK_WIDGET(panel)))
9a2c13d0 363#else
a7bd16a4 364 if (!GTK_WIDGET_MAPPED(panel))
9a2c13d0
AG
365#endif
366 return;
367 /* most wm's tend to ignore struts of unmapped windows, and that's how
368 * lxpanel hides itself. so no reason to set it. */
369 if (p->autohide && p->height_when_hidden <= 0)
370 return;
371
d1d43629 372 /* Dispatch on edge to set up strut parameters. */
373 switch (p->edge)
bee4c26e 374 {
d1d43629 375 case EDGE_LEFT:
376 index = 0;
377 strut_size = p->aw;
378 strut_lower = p->ay;
379 strut_upper = p->ay + p->ah;
380 break;
381 case EDGE_RIGHT:
382 index = 1;
383 strut_size = p->aw;
384 strut_lower = p->ay;
385 strut_upper = p->ay + p->ah;
386 break;
387 case EDGE_TOP:
388 index = 2;
389 strut_size = p->ah;
390 strut_lower = p->ax;
391 strut_upper = p->ax + p->aw;
392 break;
393 case EDGE_BOTTOM:
394 index = 3;
395 strut_size = p->ah;
396 strut_lower = p->ax;
397 strut_upper = p->ax + p->aw;
398 break;
399 default:
400 return;
bee4c26e
HJYP
401 }
402
176fb687 403 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
92955ae5 404 if (p->autohide)
176fb687 405 strut_size = p->height_when_hidden;
406
d1d43629 407 /* Set up strut value in property format. */
0bfcc6c9 408 gulong desired_strut[12];
d1d43629 409 memset(desired_strut, 0, sizeof(desired_strut));
410 if (p->setstrut)
411 {
412 desired_strut[index] = strut_size;
413 desired_strut[4 + index * 2] = strut_lower;
414 desired_strut[5 + index * 2] = strut_upper;
415 }
416 else
417 {
418 strut_size = 0;
419 strut_lower = 0;
420 strut_upper = 0;
bee4c26e 421 }
bee4c26e 422
d1d43629 423 /* If strut value changed, set the property value on the panel window.
424 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
9a2c13d0 425 if ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge))
d1d43629 426 {
427 p->strut_size = strut_size;
428 p->strut_lower = strut_lower;
429 p->strut_upper = strut_upper;
547ece89 430 p->strut_edge = p->edge;
a52c2257 431
d1d43629 432 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
433 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
434 if (strut_size != 0)
435 {
09fa171b 436 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL,
d1d43629 437 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
09fa171b 438 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT,
d1d43629 439 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
440 }
441 else
442 {
09fa171b
AG
443 XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT);
444 XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL);
d1d43629 445 }
446 }
a52c2257
HJYP
447}
448
d18c9409 449static void process_client_msg ( XClientMessageEvent* ev )
77886b88 450{
8c44345a 451 int cmd = ev->data.b[0];
77886b88
HJYP
452 switch( cmd )
453 {
ace2a572 454#ifndef DISABLE_MENU
77886b88 455 case LXPANEL_CMD_SYS_MENU:
d18c9409
HJYP
456 {
457 GSList* l;
458 for( l = all_panels; l; l = l->next )
8c44345a 459 {
a7bd16a4 460 LXPanel* p = (LXPanel*)l->data;
dd731fde
AG
461 GList *plugins, *pl;
462
a7bd16a4 463 plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
dd731fde 464 for (pl = plugins; pl; pl = pl->next)
d18c9409 465 {
191694fb 466 const LXPanelPluginInit *init = PLUGIN_CLASS(pl->data);
dd731fde
AG
467 if (init->show_system_menu)
468 /* queue to show system menu */
469 init->show_system_menu(pl->data);
d18c9409 470 }
dd731fde 471 g_list_free(plugins);
8c44345a 472 }
77886b88 473 break;
d18c9409 474 }
ace2a572 475#endif
77886b88
HJYP
476 case LXPANEL_CMD_RUN:
477 gtk_run();
478 break;
479 case LXPANEL_CMD_CONFIG:
0c5f948f 480 {
a7bd16a4 481 LXPanel * p = ((all_panels != NULL) ? all_panels->data : NULL);
0c5f948f
MJ
482 if (p != NULL)
483 panel_configure(p, 0);
484 }
77886b88
HJYP
485 break;
486 case LXPANEL_CMD_RESTART:
487 restart();
488 break;
489 case LXPANEL_CMD_EXIT:
490 gtk_main_quit();
491 break;
492 }
493}
494
a52c2257 495static GdkFilterReturn
cf701cb7 496panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
a52c2257
HJYP
497{
498 Atom at;
499 Window win;
500 XEvent *ev = (XEvent *) xevent;
501
502 ENTER;
503 DBG("win = 0x%x\n", ev->xproperty.window);
53b37ef2
HJYP
504 if (ev->type != PropertyNotify )
505 {
77886b88
HJYP
506 /* private client message from lxpanelctl */
507 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
508 {
d18c9409 509 process_client_msg( (XClientMessageEvent*)ev );
77886b88 510 }
22242ed4
HJYP
511 else if( ev->type == DestroyNotify )
512 {
cf701cb7 513 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
22242ed4 514 }
a52c2257 515 RET(GDK_FILTER_CONTINUE);
24053345 516 }
77886b88 517
a52c2257
HJYP
518 at = ev->xproperty.atom;
519 win = ev->xproperty.window;
53b37ef2
HJYP
520 if (win == GDK_ROOT_WINDOW())
521 {
522 if (at == a_NET_CLIENT_LIST)
523 {
524 fb_ev_emit(fbev, EV_CLIENT_LIST);
525 }
526 else if (at == a_NET_CURRENT_DESKTOP)
527 {
cf701cb7
HJYP
528 GSList* l;
529 for( l = all_panels; l; l = l->next )
a7bd16a4 530 ((LXPanel*)l->data)->priv->curdesk = get_net_current_desktop();
22242ed4 531 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
53b37ef2
HJYP
532 }
533 else if (at == a_NET_NUMBER_OF_DESKTOPS)
534 {
cf701cb7
HJYP
535 GSList* l;
536 for( l = all_panels; l; l = l->next )
a7bd16a4 537 ((LXPanel*)l->data)->priv->desknum = get_net_number_of_desktops();
22242ed4 538 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
53b37ef2
HJYP
539 }
540 else if (at == a_NET_DESKTOP_NAMES)
541 {
22242ed4 542 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
53b37ef2
HJYP
543 }
544 else if (at == a_NET_ACTIVE_WINDOW)
545 {
22242ed4 546 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
53b37ef2
HJYP
547 }
548 else if (at == a_NET_CLIENT_LIST_STACKING)
549 {
22242ed4 550 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
53b37ef2
HJYP
551 }
552 else if (at == a_XROOTPMAP_ID)
553 {
cf701cb7
HJYP
554 GSList* l;
555 for( l = all_panels; l; l = l->next )
556 {
a7bd16a4
AG
557 LXPanel* p = (LXPanel*)l->data;
558 if (p->priv->transparent) {
559 fb_bg_notify_changed_bg(p->priv->bg);
cf701cb7
HJYP
560 }
561 }
53b37ef2
HJYP
562 }
563 else if (at == a_NET_WORKAREA)
564 {
cf701cb7
HJYP
565 GSList* l;
566 for( l = all_panels; l; l = l->next )
567 {
a7bd16a4 568 LXPanel* p = (LXPanel*)l->data;
0bfcc6c9 569 XFree( p->priv->workarea );
a7bd16a4 570 p->priv->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->priv->wa_len);
1d9dc649 571 /* print_wmdata(p); */
a52c2257 572 }
53b37ef2
HJYP
573 }
574 else
cf701cb7
HJYP
575 return GDK_FILTER_CONTINUE;
576
577 return GDK_FILTER_REMOVE;
a52c2257 578 }
cf701cb7 579 return GDK_FILTER_CONTINUE;
a52c2257
HJYP
580}
581
582/****************************************************
583 * panel's handlers for GTK events *
584 ****************************************************/
585
bee4c26e 586
4542c20d 587static void
a7bd16a4 588on_root_bg_changed(FbBg *bg, LXPanel* p)
4542c20d 589{
a7bd16a4 590 _panel_update_background( p );
4542c20d
HJYP
591}
592
a7bd16a4
AG
593void panel_determine_background_pixmap(Panel * panel, GtkWidget * widget, GdkWindow * window)
594{
595 _panel_determine_background_pixmap(panel->topgwin, widget);
596}
597
598void _panel_determine_background_pixmap(LXPanel * panel, GtkWidget * widget)
4542c20d 599{
2918994e 600 GdkPixmap * pixmap = NULL;
a7bd16a4
AG
601 GdkWindow * window = gtk_widget_get_window(widget);
602 Panel * p = panel->priv;
4542c20d 603
2918994e 604 /* Free p->bg if it is not going to be used. */
605 if (( ! p->transparent) && (p->bg != NULL))
606 {
a7bd16a4 607 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, panel);
2918994e 608 g_object_unref(p->bg);
609 p->bg = NULL;
610 }
4542c20d 611
2918994e 612 if (p->background)
613 {
614 /* User specified background pixmap. */
615 if (p->background_file != NULL)
616 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
4542c20d 617 }
2918994e 618
619 else if (p->transparent)
4542c20d 620 {
2918994e 621 /* Transparent. Determine the appropriate value from the root pixmap. */
622 if (p->bg == NULL)
4542c20d 623 {
2918994e 624 p->bg = fb_bg_get_for_display();
a7bd16a4 625 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), panel);
4542c20d 626 }
2918994e 627 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
628 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
938806f9 629 fb_bg_composite(pixmap, &p->gtintcolor, p->alpha);
4542c20d
HJYP
630 }
631
2918994e 632 if (pixmap != NULL)
4542c20d 633 {
2918994e 634 gtk_widget_set_app_paintable(widget, TRUE );
635 gdk_window_set_back_pixmap(window, pixmap, FALSE);
636 g_object_unref(pixmap);
4542c20d
HJYP
637 }
638 else
2918994e 639 gtk_widget_set_app_paintable(widget, FALSE);
640}
4542c20d 641
2918994e 642/* Update the background of the entire panel.
643 * This function should only be called after the panel has been realized. */
644void panel_update_background(Panel * p)
645{
a7bd16a4
AG
646 _panel_update_background(p->topgwin);
647}
648
50928868 649static void _panel_update_background(LXPanel * p)
a7bd16a4
AG
650{
651 GtkWidget *w = GTK_WIDGET(p);
17fab6e5
AG
652 GList *plugins, *l;
653
2918994e 654 /* Redraw the top level widget. */
a7bd16a4
AG
655 _panel_determine_background_pixmap(p, w);
656 gdk_window_clear(gtk_widget_get_window(w));
657 gtk_widget_queue_draw(w);
2918994e 658
659 /* Loop over all plugins redrawing each plugin. */
a7bd16a4 660 plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
17fab6e5
AG
661 for (l = plugins; l != NULL; l = l->next)
662 plugin_widget_set_background(l->data, p);
663 g_list_free(plugins);
4542c20d 664}
a52c2257 665
9a2c13d0
AG
666/****************************************************
667 * autohide : borrowed from fbpanel *
668 ****************************************************/
669
670/* Autohide is behaviour when panel hides itself when mouse is "far enough"
671 * and pops up again when mouse comes "close enough".
672 * Formally, it's a state machine with 3 states that driven by mouse
673 * coordinates and timer:
674 * 1. VISIBLE - ensures that panel is visible. When/if mouse goes "far enough"
675 * switches to WAITING state
676 * 2. WAITING - starts timer. If mouse comes "close enough", stops timer and
677 * switches to VISIBLE. If timer expires, switches to HIDDEN
678 * 3. HIDDEN - hides panel. When mouse comes "close enough" switches to VISIBLE
679 *
680 * Note 1
681 * Mouse coordinates are queried every PERIOD milisec
682 *
683 * Note 2
684 * If mouse is less then GAP pixels to panel it's considered to be close,
685 * otherwise it's far
686 */
687
688#define GAP 2
689#define PERIOD 300
690
691typedef enum
692{
693 AH_STATE_VISIBLE,
694 AH_STATE_WAITING,
695 AH_STATE_HIDDEN
696} PanelAHState;
697
a7bd16a4 698static void ah_state_set(LXPanel *p, PanelAHState ah_state);
9a2c13d0
AG
699
700static gboolean
a7bd16a4 701mouse_watch(LXPanel *panel)
9a2c13d0 702{
a7bd16a4 703 Panel *p = panel->priv;
9a2c13d0
AG
704 gint x, y;
705
706 if (g_source_is_destroyed(g_main_current_source()))
707 return FALSE;
708
709 ENTER;
710 gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
711
712/* Reduce sensitivity area
713 p->ah_far = ((x < p->cx - GAP) || (x > p->cx + p->cw + GAP)
714 || (y < p->cy - GAP) || (y > p->cy + p->ch + GAP));
715*/
716
717 gint cx, cy, cw, ch, gap;
718
9a762e63
AG
719 cx = p->ax;
720 cy = p->ay;
c6c6d2e9
AG
721 cw = p->cw;
722 ch = p->ch;
9a2c13d0 723
9a762e63
AG
724 if (cw == 1) cw = 0;
725 if (ch == 1) ch = 0;
9a2c13d0
AG
726 /* reduce area which will raise panel so it does not interfere with apps */
727 if (p->ah_state == AH_STATE_HIDDEN) {
728 gap = MAX(p->height_when_hidden, GAP);
729 switch (p->edge) {
730 case EDGE_LEFT:
731 cw = gap;
732 break;
733 case EDGE_RIGHT:
734 cx = cx + cw - gap;
735 cw = gap;
736 break;
737 case EDGE_TOP:
738 ch = gap;
739 break;
740 case EDGE_BOTTOM:
741 cy = cy + ch - gap;
742 ch = gap;
743 break;
744 }
745 }
746 p->ah_far = ((x < cx) || (x > cx + cw) || (y < cy) || (y > cy + ch));
747
a7bd16a4 748 ah_state_set(panel, p->ah_state);
9a2c13d0
AG
749 RET(TRUE);
750}
751
752static gboolean ah_state_hide_timeout(gpointer p)
753{
754 if (!g_source_is_destroyed(g_main_current_source()))
755 {
756 ah_state_set(p, AH_STATE_HIDDEN);
a7bd16a4 757 ((LXPanel *)p)->priv->hide_timeout = 0;
9a2c13d0
AG
758 }
759 return FALSE;
760}
761
a7bd16a4 762static void ah_state_set(LXPanel *panel, PanelAHState ah_state)
9a2c13d0 763{
a7bd16a4
AG
764 Panel *p = panel->priv;
765
9a2c13d0
AG
766 ENTER;
767 if (p->ah_state != ah_state) {
768 p->ah_state = ah_state;
769 switch (ah_state) {
770 case AH_STATE_VISIBLE:
a7bd16a4 771 gtk_widget_show(GTK_WIDGET(panel));
9a2c13d0 772 gtk_widget_show(p->box);
9a762e63 773 gtk_widget_queue_resize(GTK_WIDGET(panel));
a7bd16a4 774 gtk_window_stick(GTK_WINDOW(panel));
9a2c13d0
AG
775 p->visible = TRUE;
776 break;
777 case AH_STATE_WAITING:
9a762e63
AG
778 if (p->hide_timeout)
779 g_source_remove(p->hide_timeout);
a7bd16a4 780 p->hide_timeout = g_timeout_add(2 * PERIOD, ah_state_hide_timeout, panel);
9a2c13d0
AG
781 break;
782 case AH_STATE_HIDDEN:
783 if (p->height_when_hidden > 0)
784 gtk_widget_hide(p->box);
785 else
a7bd16a4 786 gtk_widget_hide(GTK_WIDGET(panel));
9a2c13d0
AG
787 p->visible = FALSE;
788 }
789 } else if (p->autohide && p->ah_far) {
e1fd11ee
AG
790 switch (ah_state) {
791 case AH_STATE_VISIBLE:
a7bd16a4 792 ah_state_set(panel, AH_STATE_WAITING);
e1fd11ee
AG
793 break;
794 case AH_STATE_WAITING:
795 break;
796 case AH_STATE_HIDDEN:
797 /* configurator might change height_when_hidden value */
798 if (p->height_when_hidden > 0)
799 {
800 if (gtk_widget_get_visible(p->box))
801 {
802 gtk_widget_hide(p->box);
a7bd16a4 803 gtk_widget_show(GTK_WIDGET(panel));
e1fd11ee
AG
804 }
805 }
806 else
a7bd16a4 807 if (gtk_widget_get_visible(GTK_WIDGET(panel)))
e1fd11ee 808 {
a7bd16a4 809 gtk_widget_hide(GTK_WIDGET(panel));
e1fd11ee
AG
810 gtk_widget_show(p->box);
811 }
812 }
9a2c13d0
AG
813 } else {
814 switch (ah_state) {
815 case AH_STATE_VISIBLE:
816 break;
817 case AH_STATE_WAITING:
818 if (p->hide_timeout)
819 g_source_remove(p->hide_timeout);
820 p->hide_timeout = 0;
821 /* continue with setting visible */
822 case AH_STATE_HIDDEN:
a7bd16a4 823 ah_state_set(panel, AH_STATE_VISIBLE);
9a2c13d0
AG
824 }
825 }
826 RET();
827}
828
829/* starts autohide behaviour */
a7bd16a4 830static void ah_start(LXPanel *p)
9a2c13d0
AG
831{
832 ENTER;
a7bd16a4
AG
833 if (!p->priv->mouse_timeout)
834 p->priv->mouse_timeout = g_timeout_add(PERIOD, (GSourceFunc) mouse_watch, p);
9a2c13d0
AG
835 RET();
836}
837
838/* stops autohide */
a7bd16a4 839static void ah_stop(LXPanel *p)
9a2c13d0
AG
840{
841 ENTER;
a7bd16a4
AG
842 if (p->priv->mouse_timeout) {
843 g_source_remove(p->priv->mouse_timeout);
844 p->priv->mouse_timeout = 0;
9a2c13d0 845 }
a7bd16a4
AG
846 if (p->priv->hide_timeout) {
847 g_source_remove(p->priv->hide_timeout);
848 p->priv->hide_timeout = 0;
9a2c13d0
AG
849 }
850 RET();
851}
9a2c13d0
AG
852/* end of the autohide code
853 * ------------------------------------------------------------- */
854
fddae119
FC
855static gint
856panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
857{
a7bd16a4 858 panel_configure( (LXPanel*)user_data, 0 );
4b93d81e 859 return TRUE;
fddae119
FC
860}
861
17fab6e5 862static void panel_popupmenu_config_plugin( GtkMenuItem* item, GtkWidget* plugin )
cf701cb7 863{
a7bd16a4 864 Panel *panel = PLUGIN_PANEL(plugin)->priv;
17fab6e5 865
2f1173de 866 lxpanel_plugin_show_config_dialog(plugin);
930af9fd
HJYP
867
868 /* FIXME: this should be more elegant */
17fab6e5 869 panel->config_changed = TRUE;
cf701cb7
HJYP
870}
871
a7bd16a4 872static void panel_popupmenu_add_item( GtkMenuItem* item, LXPanel* panel )
4b93d81e
HJYP
873{
874 /* panel_add_plugin( panel, panel->topgwin ); */
8d8b66da 875 panel_configure( panel, 2 );
cf701cb7
HJYP
876}
877
17fab6e5 878static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
cf701cb7 879{
a7bd16a4 880 Panel* panel = PLUGIN_PANEL(plugin)->priv;
b7e8ad02
MJ
881
882 /* If the configuration dialog is open, there will certainly be a crash if the
883 * user manipulates the Configured Plugins list, after we remove this entry.
884 * Close the configuration dialog if it is open. */
885 if (panel->pref_dialog != NULL)
886 {
887 gtk_widget_destroy(panel->pref_dialog);
888 panel->pref_dialog = NULL;
889 }
17fab6e5
AG
890 config_setting_destroy(g_object_get_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf));
891 /* reset conf pointer because the widget still may be referenced by configurator */
892 g_object_set_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf, NULL);
b7e8ad02 893
a7bd16a4 894 lxpanel_config_save(PLUGIN_PANEL(plugin));
17fab6e5 895 gtk_widget_destroy(plugin);
cf701cb7
HJYP
896}
897
3e7b8eb7
HJYP
898/* FIXME: Potentially we can support multiple panels at the same edge,
899 * but currently this cannot be done due to some positioning problems. */
64afc832 900static char* gen_panel_name( int edge, gint monitor )
3e7b8eb7
HJYP
901{
902 const char* edge_str = num2str( edge_pair, edge, "" );
903 char* name = NULL;
1f4bc3aa 904 char* dir = _user_config_file_name("panels", NULL);
3e7b8eb7
HJYP
905 int i;
906 for( i = 0; i < G_MAXINT; ++i )
907 {
908 char* f;
64afc832
R
909 if(monitor != 0)
910 name = g_strdup_printf( "%s-m%d-%d", edge_str, monitor, i );
911 else if( G_LIKELY( i > 0 ) )
3e7b8eb7
HJYP
912 name = g_strdup_printf( "%s%d", edge_str, i );
913 else
914 name = g_strdup( edge_str );
64afc832 915
3e7b8eb7
HJYP
916 f = g_build_filename( dir, name, NULL );
917 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
918 {
919 g_free( f );
920 break;
921 }
922 g_free( name );
923 g_free( f );
924 }
925 g_free( dir );
926 return name;
927}
928
929/* FIXME: Potentially we can support multiple panels at the same edge,
930 * but currently this cannot be done due to some positioning problems. */
a7bd16a4 931static void panel_popupmenu_create_panel( GtkMenuItem* item, LXPanel* panel )
cf701cb7 932{
64afc832
R
933 gint m, e, monitors;
934 GdkScreen *screen;
a7bd16a4
AG
935 LXPanel *new_panel = panel_allocate();
936 Panel *p = new_panel->priv;
9dd114c4 937
938 /* Allocate the edge. */
64afc832
R
939 screen = gdk_screen_get_default();
940 g_assert(screen);
941 monitors = gdk_screen_get_n_monitors(screen);
942 for(m=0; m<monitors; ++m)
943 {
944 /* try each of the four edges */
945 for(e=1; e<5; ++e)
946 {
a7bd16a4
AG
947 if(panel_edge_available(p,e,m)) {
948 p->edge = e;
949 p->monitor = m;
64afc832
R
950 goto found_edge;
951 }
952 }
953 }
954
a7bd16a4 955 gtk_widget_destroy(GTK_WIDGET(new_panel));
06e29ce1 956 g_warning("Error adding panel: There is no room for another panel. All the edges are taken.");
bb4cf963 957 fm_show_error(NULL, NULL, _("There is no room for another panel. All the edges are taken."));
64afc832
R
958 return;
959
960found_edge:
a7bd16a4 961 p->name = gen_panel_name(p->edge, p->monitor);
9dd114c4 962
17fab6e5 963 /* create new config with first group "Global" */
a7bd16a4 964 config_group_add_subgroup(config_root_setting(p->config), "Global");
9dd114c4 965 panel_configure(new_panel, 0);
a7bd16a4 966 panel_normalize_configuration(p);
9dd114c4 967 panel_start_gui(new_panel);
a7bd16a4 968 gtk_widget_show_all(GTK_WIDGET(new_panel));
9dd114c4 969
a7bd16a4 970 lxpanel_config_save(new_panel);
9dd114c4 971 all_panels = g_slist_prepend(all_panels, new_panel);
cf701cb7
HJYP
972}
973
a7bd16a4 974static void panel_popupmenu_delete_panel( GtkMenuItem* item, LXPanel* panel )
cf701cb7
HJYP
975{
976 GtkWidget* dlg;
977 gboolean ok;
a7bd16a4 978 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel),
cf701cb7
HJYP
979 GTK_DIALOG_MODAL,
980 GTK_MESSAGE_QUESTION,
981 GTK_BUTTONS_OK_CANCEL,
982 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
d1d43629 983 panel_apply_icon(GTK_WINDOW(dlg));
cf701cb7
HJYP
984 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
985 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
986 gtk_widget_destroy( dlg );
987 if( ok )
988 {
1f4bc3aa 989 gchar *fname;
cf701cb7 990 all_panels = g_slist_remove( all_panels, panel );
4b93d81e
HJYP
991
992 /* delete the config file of this panel */
a7bd16a4 993 fname = _user_config_file_name("panels", panel->priv->name);
4b93d81e 994 g_unlink( fname );
1f4bc3aa 995 g_free(fname);
a7bd16a4
AG
996 panel->priv->config_changed = 0;
997 gtk_widget_destroy(GTK_WIDGET(panel));
cf701cb7
HJYP
998 }
999}
1000
e7a42ecf 1001static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
1002{
1003 GtkWidget *about;
1004 const gchar* authors[] = {
1005 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
1006 "Jim Huang <jserv.tw@gmail.com>",
1007 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
1008 "Fred Chien <cfsghost@gmail.com>",
1009 "Daniel Kesler <kesler.daniel@gmail.com>",
1010 "Juergen Hoetzel <juergen@archlinux.org>",
2918994e 1011 "Marty Jack <martyj19@comcast.net>",
815e1027 1012 "Martin Bagge <brother@bsnet.se>",
bb4cf963
AG
1013 "Andriy Grytsenko <andrej@rep.kiev.ua>",
1014 "Giuseppe Penone <giuspen@gmail.com>",
1015 "Piotr Sipika <piotr.sipika@gmail.com>",
e7a42ecf 1016 NULL
1017 };
1018 /* TRANSLATORS: Replace this string with your names, one name per line. */
1019 gchar *translators = _( "translator-credits" );
1020
1021 about = gtk_about_dialog_new();
1022 panel_apply_icon(GTK_WINDOW(about));
1023 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
09fa171b 1024 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
4c91be3e
JL
1025
1026 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
1027 {
1028 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1029 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
1030 }
1031 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
1032 {
1033 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1034 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
1035 }
0bcf9045 1036 else
4c91be3e
JL
1037 {
1038 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
0b806437 1039 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL));
4c91be3e
JL
1040 }
1041
b840f7cc 1042 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2014"));
e7a42ecf 1043 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
1044 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.");
1045 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
1046 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
1047 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
1048 gtk_dialog_run(GTK_DIALOG(about));
0bcf9045 1049 gtk_widget_destroy(about);
e7a42ecf 1050}
1051
1052void panel_apply_icon( GtkWindow *w )
1053{
0bcf9045
AG
1054 GdkPixbuf* window_icon;
1055
1056 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
4c91be3e 1057 {
0bcf9045
AG
1058 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
1059 }
1060 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
4c91be3e 1061 {
0bcf9045
AG
1062 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
1063 }
89496346 1064 else
4c91be3e 1065 {
0bcf9045
AG
1066 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL);
1067 }
21f91705 1068 gtk_window_set_icon(w, window_icon);
e7a42ecf 1069}
1070
a7bd16a4 1071GtkMenu* lxpanel_get_plugin_menu( LXPanel* panel, GtkWidget* plugin, gboolean use_sub_menu )
cf701cb7 1072{
e3b89f43 1073 GtkWidget *menu_item, *img;
1074 GtkMenu *ret,*menu;
191694fb 1075 const LXPanelPluginInit *init;
cf701cb7 1076 char* tmp;
eb1762e5 1077
e3b89f43 1078 ret = menu = GTK_MENU(gtk_menu_new());
cf701cb7 1079
eb1762e5
AG
1080 if (plugin)
1081 {
1082 init = PLUGIN_CLASS(plugin);
1083 /* create single item - plugin instance settings */
1084 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1085 tmp = g_strdup_printf( _("\"%s\" Settings"), _(init->name) );
1086 menu_item = gtk_image_menu_item_new_with_label( tmp );
1087 g_free( tmp );
1088 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1089 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
1090 if( init->config )
1091 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
1092 else
1093 gtk_widget_set_sensitive( menu_item, FALSE );
1094 /* add custom items by plugin if requested */
1095 if (init->update_context_menu != NULL)
1096 use_sub_menu = init->update_context_menu(plugin, ret);
1097 /* append a separator */
1098 menu_item = gtk_separator_menu_item_new();
1099 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
1100 }
1101 if (use_sub_menu)
1102 menu = GTK_MENU(gtk_menu_new());
1103
4b93d81e
HJYP
1104 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
1105 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
1106 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1107 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1108 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
1109
1110 if( plugin )
1111 {
1112 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
17fab6e5 1113 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(init->name) );
cf701cb7
HJYP
1114 menu_item = gtk_image_menu_item_new_with_label( tmp );
1115 g_free( tmp );
1116 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1117 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1118 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
1119 }
1120
1121 menu_item = gtk_separator_menu_item_new();
1122 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1123
4b93d81e
HJYP
1124 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1125 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
1126 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1127 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1128 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
1129
cf701cb7
HJYP
1130 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
1131 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
1132 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1133 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1134 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
1135
1136 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
1137 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
1138 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1139 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1140 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
1141 if( ! all_panels->next ) /* if this is the only panel */
1142 gtk_widget_set_sensitive( menu_item, FALSE );
1143
e7a42ecf 1144 menu_item = gtk_separator_menu_item_new();
1145 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1146
1147 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
1148 menu_item = gtk_image_menu_item_new_with_label(_("About"));
1149 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1150 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
a7bd16a4 1151 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel->priv );
e7a42ecf 1152
cf701cb7
HJYP
1153 if( use_sub_menu )
1154 {
cf701cb7
HJYP
1155 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
1156 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
e3b89f43 1157 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
3e7b8eb7
HJYP
1158 }
1159
eb1762e5 1160 gtk_widget_show_all(GTK_WIDGET(ret));
3e7b8eb7 1161
cf701cb7
HJYP
1162 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1163 return ret;
1164}
1165
17fab6e5
AG
1166/* for old plugins compatibility */
1167GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
1168{
a7bd16a4 1169 return lxpanel_get_plugin_menu(panel->topgwin, plugin->pwid, use_sub_menu);
17fab6e5
AG
1170}
1171
a52c2257
HJYP
1172/****************************************************
1173 * panel creation *
1174 ****************************************************/
176fb687 1175
a52c2257 1176static void
22242ed4 1177make_round_corners(Panel *p)
a52c2257 1178{
a97d06a6 1179 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 1180 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
1181}
1182
22242ed4 1183void panel_set_dock_type(Panel *p)
bee4c26e 1184{
09fa171b
AG
1185 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1186
bee4c26e
HJYP
1187 if (p->setdocktype) {
1188 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
09fa171b 1189 XChangeProperty(xdisplay, p->topxwin,
bee4c26e
HJYP
1190 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
1191 PropModeReplace, (unsigned char *) &state, 1);
1192 }
1193 else {
09fa171b 1194 XDeleteProperty( xdisplay, p->topxwin, a_NET_WM_WINDOW_TYPE );
bee4c26e 1195 }
a52c2257
HJYP
1196}
1197
176fb687 1198void panel_establish_autohide(Panel *p)
1199{
a7bd16a4
AG
1200 _panel_establish_autohide(p->topgwin);
1201}
1202
1203void _panel_establish_autohide(LXPanel *p)
1204{
1205 if (p->priv->autohide)
9a2c13d0
AG
1206 ah_start(p);
1207 else
176fb687 1208 {
9a2c13d0
AG
1209 ah_stop(p);
1210 ah_state_set(p, AH_STATE_VISIBLE);
176fb687 1211 }
1212}
1213
8f9e6256 1214/* Set an image from a file with scaling to the panel icon size. */
fcb35553 1215void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
8f9e6256 1216{
1217 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
1218 if (pixbuf != NULL)
1219 {
1220 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1221 g_object_unref(pixbuf);
1222 }
1223}
1224
a7bd16a4
AG
1225void lxpanel_image_set_from_file(LXPanel * p, GtkWidget * image, const char * file)
1226{
1227 panel_image_set_from_file(p->priv, image, file);
1228}
1229
c14620f2
MJ
1230/* Set an image from a icon theme with scaling to the panel icon size. */
1231gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
1232{
1b532bce 1233 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
c14620f2 1234 {
1b532bce 1235 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
c14620f2
MJ
1236 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1237 g_object_unref(pixbuf);
1238 return TRUE;
1239 }
1240 return FALSE;
1241}
1242
a7bd16a4
AG
1243gboolean lxpanel_image_set_icon_theme(LXPanel * p, GtkWidget * image, const gchar * icon)
1244{
1245 return panel_image_set_icon_theme(p->priv, image, icon);
1246}
1247
0defe4b9 1248static void
a7bd16a4 1249panel_start_gui(LXPanel *panel)
a52c2257
HJYP
1250{
1251 Atom state[3];
1252 XWMHints wmhints;
0bfcc6c9 1253 gulong val;
09fa171b 1254 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
a7bd16a4
AG
1255 Panel *p = panel->priv;
1256 GtkWidget *w = GTK_WIDGET(panel);
6db11841 1257
a52c2257
HJYP
1258 ENTER;
1259
9dd114c4 1260 p->curdesk = get_net_current_desktop();
1261 p->desknum = get_net_number_of_desktops();
1262 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1263
cb22c87c
HJYP
1264 /* main toplevel window */
1265 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
a7bd16a4 1266 gtk_widget_set_name(w, "PanelToplevel");
176fb687 1267 p->display = gdk_display_get_default();
a7bd16a4
AG
1268 gtk_container_set_border_width(GTK_CONTAINER(panel), 0);
1269 gtk_window_set_resizable(GTK_WINDOW(panel), FALSE);
1270 gtk_window_set_wmclass(GTK_WINDOW(panel), "panel", "lxpanel");
1271 gtk_window_set_title(GTK_WINDOW(panel), "panel");
1272 gtk_window_set_position(GTK_WINDOW(panel), GTK_WIN_POS_NONE);
1273 gtk_window_set_decorated(GTK_WINDOW(panel), FALSE);
77886b88 1274
a7bd16a4 1275 gtk_window_group_add_window( win_grp, (GtkWindow*)panel );
e2957bd2 1276
a7bd16a4 1277 gtk_widget_add_events( w, GDK_BUTTON_PRESS_MASK );
f2d54481 1278
a7bd16a4 1279 gtk_widget_realize(w);
175f73d1 1280 //gdk_window_set_decorations(gtk_widget_get_window(p->topgwin), 0);
2de71c90 1281
4542c20d 1282 // main layout manager as a single child of panel
a7bd16a4 1283 p->box = panel_box_new(panel, FALSE, 0);
a52c2257 1284 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
a7bd16a4 1285 gtk_container_add(GTK_CONTAINER(panel), p->box);
a52c2257 1286 gtk_widget_show(p->box);
a97d06a6
HJYP
1287 if (p->round_corners)
1288 make_round_corners(p);
6db11841 1289
a7bd16a4 1290 p->topxwin = GDK_WINDOW_XWINDOW(gtk_widget_get_window(w));
a52c2257
HJYP
1291 DBG("topxwin = %x\n", p->topxwin);
1292
1293 /* the settings that should be done before window is mapped */
1294 wmhints.flags = InputHint;
1295 wmhints.input = 0;
09fa171b 1296 XSetWMHints (xdisplay, p->topxwin, &wmhints);
24053345 1297#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257 1298 val = WIN_HINTS_SKIP_FOCUS;
09fa171b
AG
1299 XChangeProperty(xdisplay, p->topxwin,
1300 XInternAtom(xdisplay, "_WIN_HINTS", False), XA_CARDINAL, 32,
a52c2257
HJYP
1301 PropModeReplace, (unsigned char *) &val, 1);
1302
bee4c26e 1303 panel_set_dock_type(p);
a52c2257
HJYP
1304
1305 /* window mapping point */
a7bd16a4 1306 gtk_widget_show_all(w);
a52c2257
HJYP
1307
1308 /* the settings that should be done after window is mapped */
a7bd16a4 1309 _panel_establish_autohide(panel);
a52c2257
HJYP
1310
1311 /* send it to running wm */
0bfcc6c9 1312 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, G_MAXULONG, 0, 0, 0, 0);
a52c2257 1313 /* and assign it ourself just for case when wm is not running */
0bfcc6c9 1314 val = G_MAXULONG;
09fa171b 1315 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
a52c2257
HJYP
1316 PropModeReplace, (unsigned char *) &val, 1);
1317
1318 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1319 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1320 state[2] = a_NET_WM_STATE_STICKY;
09fa171b 1321 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STATE, XA_ATOM,
a52c2257
HJYP
1322 32, PropModeReplace, (unsigned char *) state, 3);
1323
a7bd16a4
AG
1324 _calculate_position(panel);
1325 gdk_window_move_resize(gtk_widget_get_window(w), p->ax, p->ay, p->aw, p->ah);
1326 _panel_set_wm_strut(panel);
5eaabb1b 1327 p->initialized = TRUE;
77886b88 1328
a52c2257
HJYP
1329 RET();
1330}
1331
2918994e 1332/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1333void panel_adjust_geometry_terminology(Panel * p)
9dd114c4 1334{
8ed3b3d4 1335 if ((p->height_label != NULL) && (p->width_label != NULL)
1336 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
9dd114c4 1337 {
1338 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1339 {
1340 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1341 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
2918994e 1342 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1343 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
9dd114c4 1344 }
1345 else
1346 {
1347 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1348 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
2918994e 1349 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1350 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
9dd114c4 1351 }
1352 }
1353}
1354
2918994e 1355/* Draw text into a label, with the user preference color and optionally bold. */
38ac664c
AG
1356void panel_draw_label_text(Panel * p, GtkWidget * label, const char * text,
1357 gboolean bold, float custom_size_factor,
1358 gboolean custom_color)
2918994e 1359{
2918994e 1360 if (text == NULL)
1361 {
1362 /* Null string. */
1363 gtk_label_set_text(GTK_LABEL(label), NULL);
83d410c1 1364 return;
2918994e 1365 }
1366
83d410c1
HG
1367 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1368 int font_desc;
1369 if (p->usefontsize)
1370 font_desc = p->fontsize;
f669ce5e 1371 else
2918994e 1372 {
761e0dae
AG
1373 GtkStyle *style = gtk_widget_get_style(label);
1374 font_desc = pango_font_description_get_size(style->font_desc) / PANGO_SCALE;
83d410c1
HG
1375 }
1376 font_desc *= custom_size_factor;
1377
1378 /* Check the string for characters that need to be escaped.
1379 * If any are found, create the properly escaped string and use it instead. */
38ac664c 1380 const char * valid_markup = text;
83d410c1 1381 char * escaped_text = NULL;
38ac664c 1382 const char * q;
83d410c1
HG
1383 for (q = text; *q != '\0'; q += 1)
1384 {
1385 if ((*q == '<') || (*q == '>') || (*q == '&'))
2918994e 1386 {
83d410c1
HG
1387 escaped_text = g_markup_escape_text(text, -1);
1388 valid_markup = escaped_text;
1389 break;
2918994e 1390 }
83d410c1 1391 }
2918994e 1392
83d410c1
HG
1393 gchar * formatted_text;
1394 if ((custom_color) && (p->usefontcolor))
1395 {
1396 /* Color, optionally bold. */
1397 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
f669ce5e 1398 font_desc,
2918994e 1399 gcolor2rgb24(&p->gfontcolor),
1400 ((bold) ? "<b>" : ""),
1401 valid_markup,
1402 ((bold) ? "</b>" : ""));
83d410c1
HG
1403 }
1404 else
1405 {
1406 /* No color, optionally bold. */
1407 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
f669ce5e 1408 font_desc,
1409 ((bold) ? "<b>" : ""),
1410 valid_markup,
1411 ((bold) ? "</b>" : ""));
2918994e 1412 }
83d410c1
HG
1413
1414 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1415 g_free(formatted_text);
1416 g_free(escaped_text);
2918994e 1417}
1418
a7bd16a4
AG
1419void lxpanel_draw_label_text(LXPanel * p, GtkWidget * label, const char * text,
1420 gboolean bold, float custom_size_factor,
1421 gboolean custom_color)
1422{
1423 panel_draw_label_text(p->priv, label, text, bold, custom_size_factor, custom_color);
1424}
1425
2918994e 1426void panel_set_panel_configuration_changed(Panel *p)
a97d06a6 1427{
a7bd16a4
AG
1428 _panel_set_panel_configuration_changed(p->topgwin);
1429}
1430
1431void _panel_set_panel_configuration_changed(LXPanel *panel)
1432{
1433 Panel *p = panel->priv;
17fab6e5 1434 GList *plugins, *l;
9dd114c4 1435
cfde283a 1436 GtkOrientation previous_orientation = p->orientation;
a97d06a6 1437 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
cfde283a 1438 ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
9dd114c4 1439
cfde283a 1440 /* either first run or orientation was changed */
5eaabb1b 1441 if (!p->initialized || previous_orientation != p->orientation)
9dd114c4 1442 {
1443 panel_adjust_geometry_terminology(p);
5eaabb1b 1444 if (p->initialized)
f5c7784a 1445 p->height = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
9dd114c4 1446 if (p->height_control != NULL)
1447 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1448 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1449 {
cfde283a 1450 int value = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? gdk_screen_width() : gdk_screen_height());
2918994e 1451 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1452 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1453 }
9dd114c4 1454 }
1455
5eaabb1b 1456 /* FIXME: it's deprecated, kept for binary compatibility */
cfde283a 1457 if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
a97d06a6
HJYP
1458 p->my_box_new = gtk_hbox_new;
1459 p->my_separator_new = gtk_vseparator_new;
1460 } else {
1461 p->my_box_new = gtk_vbox_new;
1462 p->my_separator_new = gtk_hseparator_new;
1463 }
1464
1465 /* recreate the main layout box */
964b8b7e 1466 if (p->box != NULL)
1467 {
cfde283a 1468 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), p->orientation);
a97d06a6 1469 }
9dd114c4 1470
a97d06a6
HJYP
1471 /* NOTE: This loop won't be executed when panel started since
1472 plugins are not loaded at that time.
1473 This is used when the orientation of the panel is changed
1474 from the config dialog, and plugins should be re-layout.
1475 */
17fab6e5
AG
1476 plugins = p->box ? gtk_container_get_children(GTK_CONTAINER(p->box)) : NULL;
1477 for( l = plugins; l; l = l->next ) {
1478 GtkWidget *w = (GtkWidget*)l->data;
191694fb 1479 const LXPanelPluginInit *init = PLUGIN_CLASS(w);
17fab6e5 1480 if (init->reconfigure)
a7bd16a4 1481 init->reconfigure(panel, w);
a97d06a6 1482 }
17fab6e5 1483 g_list_free(plugins);
3154b4ef 1484 /* panel geometry changed? update panel background then */
50928868 1485 _panel_queue_update_background(panel);
a97d06a6
HJYP
1486}
1487
a52c2257 1488static int
17fab6e5 1489panel_parse_global(Panel *p, config_setting_t *cfg)
a52c2257 1490{
4bca3e51
AG
1491 const char *str;
1492 gint i;
6db11841 1493
17fab6e5
AG
1494 /* check Global config */
1495 if (!cfg || strcmp(config_setting_get_name(cfg), "Global") != 0)
3e7b8eb7 1496 {
06e29ce1 1497 g_warning( "lxpanel: Global section not found");
17fab6e5
AG
1498 RET(0);
1499 }
4bca3e51
AG
1500 if (config_setting_lookup_string(cfg, "edge", &str))
1501 p->edge = str2num(edge_pair, str, EDGE_NONE);
1502 if (config_setting_lookup_string(cfg, "allign", &str))
1503 p->allign = str2num(allign_pair, str, ALLIGN_NONE);
1504 config_setting_lookup_int(cfg, "monitor", &p->monitor);
1505 config_setting_lookup_int(cfg, "margin", &p->margin);
1506 if (config_setting_lookup_string(cfg, "widthtype", &str))
1507 p->widthtype = str2num(width_pair, str, WIDTH_NONE);
1508 config_setting_lookup_int(cfg, "width", &p->width);
1509 if (config_setting_lookup_string(cfg, "heighttype", &str))
1510 p->heighttype = str2num(height_pair, str, HEIGHT_NONE);
1511 config_setting_lookup_int(cfg, "height", &p->height);
1512 if (config_setting_lookup_int(cfg, "spacing", &i) && i > 0)
1513 p->spacing = i;
1514 if (config_setting_lookup_int(cfg, "setdocktype", &i))
1515 p->setdocktype = i != 0;
1516 if (config_setting_lookup_int(cfg, "setpartialstrut", &i))
1517 p->setstrut = i != 0;
1518 if (config_setting_lookup_int(cfg, "RoundCorners", &i))
1519 p->round_corners = i != 0;
1520 if (config_setting_lookup_int(cfg, "transparent", &i))
1521 p->transparent = i != 0;
1522 if (config_setting_lookup_int(cfg, "alpha", &p->alpha))
17fab6e5 1523 {
17fab6e5
AG
1524 if (p->alpha > 255)
1525 p->alpha = 255;
1526 }
4bca3e51
AG
1527 if (config_setting_lookup_int(cfg, "autohide", &i))
1528 p->autohide = i != 0;
9a2c13d0
AG
1529 if (config_setting_lookup_int(cfg, "heightwhenhidden", &i))
1530 p->height_when_hidden = MAX(0, i);
4bca3e51 1531 if (config_setting_lookup_string(cfg, "tintcolor", &str))
17fab6e5 1532 {
4bca3e51 1533 if (!gdk_color_parse (str, &p->gtintcolor))
17fab6e5
AG
1534 gdk_color_parse ("white", &p->gtintcolor);
1535 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1536 DBG("tintcolor=%x\n", p->tintcolor);
1537 }
4bca3e51
AG
1538 if (config_setting_lookup_int(cfg, "usefontcolor", &i))
1539 p->usefontcolor = i != 0;
1540 if (config_setting_lookup_string(cfg, "fontcolor", &str))
17fab6e5 1541 {
4bca3e51 1542 if (!gdk_color_parse (str, &p->gfontcolor))
17fab6e5
AG
1543 gdk_color_parse ("black", &p->gfontcolor);
1544 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1545 DBG("fontcolor=%x\n", p->fontcolor);
1546 }
4bca3e51
AG
1547 if (config_setting_lookup_int(cfg, "usefontsize", &i))
1548 p->usefontsize = i != 0;
1549 if (config_setting_lookup_int(cfg, "fontsize", &i) && i > 0)
1550 p->fontsize = i;
1551 if (config_setting_lookup_int(cfg, "background", &i))
1552 p->background = i != 0;
1553 if (config_setting_lookup_string(cfg, "backgroundfile", &str))
1554 p->background_file = g_strdup(str);
1555 config_setting_lookup_int(cfg, "iconsize", &p->icon_size);
4542c20d 1556
9dd114c4 1557 panel_normalize_configuration(p);
239cb032 1558
9dd114c4 1559 return 1;
a52c2257
HJYP
1560}
1561
1562static int
a7bd16a4 1563panel_parse_plugin(LXPanel *p, config_setting_t *cfg)
a52c2257 1564{
4bca3e51 1565 const char *type = NULL;
db449f6e 1566
a52c2257 1567 ENTER;
4bca3e51 1568 config_setting_lookup_string(cfg, "type", &type);
17fab6e5 1569 DBG("plug %s\n", type);
db449f6e 1570
17fab6e5 1571 if (!type || lxpanel_add_plugin(p, type, cfg, -1) == NULL) {
06e29ce1 1572 g_warning( "lxpanel: can't load %s plugin", type);
a52c2257
HJYP
1573 goto error;
1574 }
a52c2257 1575 RET(1);
db449f6e 1576
17fab6e5 1577error:
a52c2257 1578 RET(0);
a52c2257
HJYP
1579}
1580
adf42c84 1581static int panel_start( LXPanel *p )
a52c2257 1582{
17fab6e5
AG
1583 config_setting_t *list, *s;
1584 int i;
db449f6e 1585
a52c2257
HJYP
1586 /* parse global section */
1587 ENTER;
8110399f 1588
a7bd16a4
AG
1589 list = config_setting_get_member(config_root_setting(p->priv->config), "");
1590 if (!list || !panel_parse_global(p->priv, config_setting_get_elem(list, 0)))
a52c2257
HJYP
1591 RET(0);
1592
9dd114c4 1593 panel_start_gui(p);
1594
17fab6e5
AG
1595 for (i = 1; (s = config_setting_get_elem(list, i)) != NULL; )
1596 if (strcmp(config_setting_get_name(s), "Plugin") == 0 &&
1597 panel_parse_plugin(p, s)) /* success on plugin start */
1598 i++;
1599 else /* remove invalid data from config */
1600 config_setting_remove_elem(list, i);
4542c20d
HJYP
1601
1602 /* update backgrond of panel and all plugins */
a7bd16a4 1603 _panel_update_background(p);
9dd114c4 1604 return 1;
a52c2257
HJYP
1605}
1606
8110399f 1607void panel_destroy(Panel *p)
a52c2257 1608{
a7bd16a4 1609 gtk_widget_destroy(GTK_WIDGET(p->topgwin));
a52c2257
HJYP
1610}
1611
a7bd16a4 1612static LXPanel* panel_new( const char* config_file, const char* config_name )
8110399f 1613{
a7bd16a4 1614 LXPanel* panel = NULL;
16fbda14 1615
17fab6e5 1616 if (G_LIKELY(config_file))
cf701cb7 1617 {
17fab6e5 1618 panel = panel_allocate();
a7bd16a4 1619 panel->priv->name = g_strdup(config_name);
17fab6e5 1620 g_debug("starting panel from file %s",config_file);
a7bd16a4 1621 if (!config_read_file(panel->priv->config, config_file) ||
17fab6e5 1622 !panel_start(panel))
3e7b8eb7 1623 {
06e29ce1 1624 g_warning( "lxpanel: can't start panel");
a7bd16a4 1625 gtk_widget_destroy(GTK_WIDGET(panel));
17fab6e5 1626 panel = NULL;
cf701cb7 1627 }
cf701cb7
HJYP
1628 }
1629 return panel;
8110399f 1630}
a52c2257 1631
0defe4b9 1632static void
a52c2257
HJYP
1633usage()
1634{
e7cb732b
HJYP
1635 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1636 g_print(_("Command line options:\n"));
1637 g_print(_(" --help -- print this help and exit\n"));
1638 g_print(_(" --version -- print version and exit\n"));
06e29ce1 1639// g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1640// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1641 g_print(_(" --profile name -- use specified profile\n"));
1642 g_print("\n");
1643 g_print(_(" -h -- same as --help\n"));
1644 g_print(_(" -p -- same as --profile\n"));
1645 g_print(_(" -v -- same as --version\n"));
2918994e 1646 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1647 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1648}
1649
e68b47dc
JH
1650/* Lightweight lock related functions - X clipboard hacks */
1651
1652#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1653
1654/*
1655 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1656 */
1657static void
1658clipboard_get_func(
1659 GtkClipboard *clipboard G_GNUC_UNUSED,
1660 GtkSelectionData *selection_data G_GNUC_UNUSED,
1661 guint info G_GNUC_UNUSED,
1662 gpointer user_data_or_owner G_GNUC_UNUSED)
1663{
1664}
1665
1666/*
1667 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1668 */
1669static void clipboard_clear_func(
1670 GtkClipboard *clipboard G_GNUC_UNUSED,
1671 gpointer user_data_or_owner G_GNUC_UNUSED)
1672{
1673}
1674
1675/*
1676 * Lightweight version for checking single instance.
1677 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1678 *
1679 * Returns TRUE if successfully retrieved and FALSE otherwise.
1680 */
16fb8c2e 1681static gboolean check_main_lock()
e68b47dc
JH
1682{
1683 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1684 gboolean retval = FALSE;
1685 GtkClipboard *clipboard;
1686 Atom atom;
09fa171b 1687 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
e68b47dc
JH
1688
1689 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1690
09fa171b 1691 XGrabServer(xdisplay);
e68b47dc 1692
09fa171b 1693 if (XGetSelectionOwner(xdisplay, atom) != None)
e68b47dc
JH
1694 goto out;
1695
1696 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1697
1698 if (gtk_clipboard_set_with_data(clipboard, targets,
1699 G_N_ELEMENTS (targets),
1700 clipboard_get_func,
1701 clipboard_clear_func, NULL))
1702 retval = TRUE;
1703
1704out:
09fa171b 1705 XUngrabServer (xdisplay);
e68b47dc
JH
1706 gdk_flush ();
1707
1708 return retval;
1709}
1710#undef CLIPBOARD_NAME
1711
1f4bc3aa 1712static void _start_panels_from_dir(const char *panel_dir)
8110399f 1713{
1f4bc3aa
AG
1714 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1715 const gchar* name;
cf701cb7 1716
1f4bc3aa
AG
1717 if( ! dir )
1718 {
1719 return;
1720 }
cf701cb7 1721
1f4bc3aa
AG
1722 while((name = g_dir_read_name(dir)) != NULL)
1723 {
1724 char* panel_config = g_build_filename( panel_dir, name, NULL );
0bcf9045 1725 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
cf701cb7 1726 {
a7bd16a4 1727 LXPanel* panel = panel_new( panel_config, name );
1f4bc3aa
AG
1728 if( panel )
1729 all_panels = g_slist_prepend( all_panels, panel );
cf701cb7 1730 }
1f4bc3aa 1731 g_free( panel_config );
cf701cb7 1732 }
1f4bc3aa
AG
1733 g_dir_close( dir );
1734}
1735
1736static gboolean start_all_panels( )
1737{
1738 char *panel_dir;
8ea02629 1739 const gchar * const * dir;
1f4bc3aa
AG
1740
1741 /* try user panels */
1742 panel_dir = _user_config_file_name("panels", NULL);
1743 _start_panels_from_dir(panel_dir);
1744 g_free(panel_dir);
1745 if (all_panels != NULL)
1746 return TRUE;
8ea02629
AG
1747 /* else try XDG fallbacks */
1748 dir = g_get_system_config_dirs();
1749 if (dir) while (dir[0])
1750 {
1751 panel_dir = _system_config_file_name(dir[0], "panels");
1752 _start_panels_from_dir(panel_dir);
1753 g_free(panel_dir);
1754 if (all_panels != NULL)
1755 return TRUE;
1756 dir++;
1757 }
1f4bc3aa
AG
1758 /* last try at old fallback for compatibility reasons */
1759 panel_dir = _old_system_config_file_name("panels");
1760 _start_panels_from_dir(panel_dir);
1761 g_free(panel_dir);
cf701cb7 1762 return all_panels != NULL;
8110399f
HJYP
1763}
1764
cf701cb7
HJYP
1765void load_global_config();
1766void free_global_config();
1767
1f4bc3aa
AG
1768static void _ensure_user_config_dirs(void)
1769{
1770 char *dir = g_build_filename(g_get_user_config_dir(), "lxpanel", cprofile,
1771 "panels", NULL);
1772
1773 /* make sure the private profile and panels dir exists */
1774 g_mkdir_with_parents(dir, 0700);
1775 g_free(dir);
1776}
1777
8110399f 1778int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1779{
1780 int i;
9df6826f 1781 const char* desktop_name;
f3eacf81 1782 char *file;
f277dbb7 1783
a52c2257 1784 setlocale(LC_CTYPE, "");
f277dbb7 1785
0bcf9045
AG
1786 g_thread_init(NULL);
1787/* gdk_threads_init();
1788 gdk_threads_enter(); */
1d434622 1789
a52c2257
HJYP
1790 gtk_init(&argc, &argv);
1791
1792#ifdef ENABLE_NLS
1793 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1794 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1795 textdomain ( GETTEXT_PACKAGE );
1796#endif
1797
1798 XSetLocaleModifiers("");
2918994e 1799 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1800
a52c2257 1801 resolve_atoms();
8110399f 1802
9df6826f
HJYP
1803 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1804 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
95095259 1805
a52c2257
HJYP
1806 for (i = 1; i < argc; i++) {
1807 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1808 usage();
1809 exit(0);
1810 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1811 printf("lxpanel %s\n", version);
1812 exit(0);
1813 } else if (!strcmp(argv[i], "--log")) {
1814 i++;
1815 if (i == argc) {
06e29ce1 1816 g_critical( "lxpanel: missing log level");
a52c2257
HJYP
1817 usage();
1818 exit(1);
1819 } else {
06e29ce1 1820 /* deprecated */
a52c2257
HJYP
1821 }
1822 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1823 config = 1;
1824 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1825 i++;
1826 if (i == argc) {
06e29ce1 1827 g_critical( "lxpanel: missing profile name");
a52c2257
HJYP
1828 usage();
1829 exit(1);
1830 } else {
1831 cprofile = g_strdup(argv[i]);
1832 }
1833 } else {
1834 printf("lxpanel: unknown option - %s\n", argv[i]);
1835 usage();
1836 exit(1);
1837 }
1838 }
f277dbb7 1839
12eaee21
AG
1840 /* Add a gtkrc file to be parsed too. */
1841 file = _user_config_file_name("gtkrc", NULL);
1842 gtk_rc_parse(file);
1843 g_free(file);
1844
8110399f 1845 /* Check for duplicated lxpanel instances */
e68b47dc 1846 if (!check_main_lock() && !config) {
e7a42ecf 1847 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1848 exit(1);
1849 }
1850
1f4bc3aa
AG
1851 _ensure_user_config_dirs();
1852
730e3f7b 1853 /* Add our own icons to the search path of icon theme */
0b806437 1854 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/images" );
730e3f7b 1855
cf701cb7 1856 fbev = fb_ev_new();
e2957bd2 1857 win_grp = gtk_window_group_new();
22242ed4 1858
f7cb330e
HJYP
1859 is_restarting = FALSE;
1860
17fab6e5
AG
1861 /* init LibFM */
1862 fm_gtk_init(NULL);
1863
1864 /* prepare modules data */
1865 _prepare_modules();
1866
cf701cb7
HJYP
1867 load_global_config();
1868
0bcf9045
AG
1869 /* NOTE: StructureNotifyMask is required by XRandR
1870 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1871 */
0f83fb71 1872 gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
1873 GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
cf701cb7 1874 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1875
cf701cb7
HJYP
1876 if( G_UNLIKELY( ! start_all_panels() ) )
1877 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1878/*
1879 * FIXME: configure??
6a6ad54e
HJYP
1880 if (config)
1881 configure();
8110399f 1882*/
6a6ad54e 1883 gtk_main();
8110399f 1884
09fa171b 1885 XSelectInput (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), GDK_ROOT_WINDOW(), NoEventMask);
cf701cb7
HJYP
1886 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1887
8110399f 1888 /* destroy all panels */
a7bd16a4 1889 g_slist_foreach( all_panels, (GFunc) gtk_widget_destroy, NULL );
2a26c0d8
HJYP
1890 g_slist_free( all_panels );
1891 all_panels = NULL;
6a6ad54e 1892 g_free( cfgfile );
5541b8d2 1893
cf701cb7
HJYP
1894 free_global_config();
1895
17fab6e5
AG
1896 _unload_modules();
1897 fm_gtk_finalize();
1898
74bf5dbf 1899 /* gdk_threads_leave(); */
3e71f8af 1900
e2957bd2 1901 g_object_unref(win_grp);
22242ed4
HJYP
1902 g_object_unref(fbev);
1903
a7bd16a4
AG
1904 if (!is_restarting)
1905 return 0;
36f544e2 1906 if (strchr(argv[0], G_DIR_SEPARATOR))
a7bd16a4
AG
1907 execve(argv[0], argv, env);
1908 else
1909 execve(g_find_program_in_path(argv[0]), argv, env);
36f544e2 1910 return 1;
9c338caf
AG
1911}
1912
a7bd16a4 1913GtkOrientation panel_get_orientation(LXPanel *panel)
9c338caf 1914{
a7bd16a4 1915 return panel->priv->orientation;
9c338caf
AG
1916}
1917
a7bd16a4 1918gint panel_get_icon_size(LXPanel *panel)
9c338caf 1919{
a7bd16a4 1920 return panel->priv->icon_size;
9c338caf
AG
1921}
1922
a7bd16a4 1923gint panel_get_height(LXPanel *panel)
9c338caf 1924{
a7bd16a4 1925 return panel->priv->height;
9c338caf
AG
1926}
1927
a7bd16a4 1928Window panel_get_xwindow(LXPanel *panel)
4718ca02 1929{
a7bd16a4 1930 return panel->priv->topxwin;
4718ca02
AG
1931}
1932
a7bd16a4 1933gint panel_get_monitor(LXPanel *panel)
4718ca02 1934{
a7bd16a4 1935 return panel->priv->monitor;
4718ca02
AG
1936}
1937
a7bd16a4 1938GtkStyle *panel_get_defstyle(LXPanel *panel)
9c338caf 1939{
a7bd16a4 1940 return panel->priv->defstyle;
9c338caf
AG
1941}
1942
a7bd16a4 1943GtkIconTheme *panel_get_icon_theme(LXPanel *panel)
9c338caf 1944{
a7bd16a4 1945 return panel->priv->icon_theme;
9c338caf
AG
1946}
1947
a7bd16a4 1948gboolean panel_is_at_bottom(LXPanel *panel)
4718ca02 1949{
a7bd16a4 1950 return panel->priv->edge == EDGE_BOTTOM;
4718ca02
AG
1951}
1952
072944bf
AG
1953gboolean panel_is_dynamic(LXPanel *panel)
1954{
1955 return panel->priv->widthtype == WIDTH_REQUEST;
1956}
1957
a7bd16a4 1958GtkWidget *panel_box_new(LXPanel *panel, gboolean homogeneous, gint spacing)
9c338caf 1959{
a7bd16a4 1960 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
5eaabb1b
AG
1961 return gtk_hbox_new(homogeneous, spacing);
1962 return gtk_vbox_new(homogeneous, spacing);
9c338caf
AG
1963}
1964
a7bd16a4 1965GtkWidget *panel_separator_new(LXPanel *panel)
9c338caf 1966{
a7bd16a4 1967 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
5eaabb1b
AG
1968 return gtk_vseparator_new();
1969 return gtk_hseparator_new();
9c338caf 1970}
10d93053 1971
191694fb 1972gboolean _class_is_present(const LXPanelPluginInit *init)
10d93053
AG
1973{
1974 GSList *sl;
1975
1976 for (sl = all_panels; sl; sl = sl->next )
1977 {
a7bd16a4 1978 LXPanel *panel = (LXPanel*)sl->data;
10d93053
AG
1979 GList *plugins, *p;
1980
a7bd16a4 1981 plugins = gtk_container_get_children(GTK_CONTAINER(panel->priv->box));
10d93053
AG
1982 for (p = plugins; p; p = p->next)
1983 if (PLUGIN_CLASS(p->data) == init)
1984 {
1985 g_list_free(plugins);
1986 return TRUE;
1987 }
1988 g_list_free(plugins);
1989 }
1990 return FALSE;
1991}