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