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