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