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