Urdu done!
[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>
a52c2257
HJYP
33
34#include "plugin.h"
35#include "panel.h"
36#include "misc.h"
37#include "bg.h"
a52c2257 38
77886b88 39#include "lxpanelctl.h"
cf701cb7 40#include "dbg.h"
77886b88 41
a52c2257
HJYP
42static gchar *cfgfile = NULL;
43static gchar version[] = VERSION;
44gchar *cprofile = "default";
45
e2957bd2
HJYP
46static GtkWindowGroup* win_grp; /* window group used to limit the scope of model dialog. */
47
e68b47dc 48static int config = 0;
22242ed4 49FbEv *fbev = NULL;
a52c2257 50
cf701cb7 51GSList* all_panels = NULL; /* a single-linked list storing all panels */
a52c2257 52
f7cb330e
HJYP
53gboolean is_restarting = FALSE;
54
3e7b8eb7 55static int panel_start( Panel *p, char **fp );
9dd114c4 56static void panel_start_gui(Panel *p);
4b93d81e
HJYP
57void panel_config_save(Panel* panel); /* defined in configurator.c */
58
95095259
HJYP
59gboolean is_in_lxde = FALSE;
60
9dd114c4 61/* Allocate and initialize new Panel structure. */
62static Panel* panel_allocate(void)
63{
2918994e 64 Panel* p = g_new0(Panel, 1);
9dd114c4 65 p->allign = ALLIGN_CENTER;
66 p->edge = EDGE_NONE;
67 p->widthtype = WIDTH_PERCENT;
68 p->width = 100;
69 p->heighttype = HEIGHT_PIXEL;
70 p->height = PANEL_HEIGHT_DEFAULT;
71 p->setdocktype = 1;
72 p->setstrut = 1;
73 p->round_corners = 0;
74 p->autohide = 0;
75 p->visible = TRUE;
76 p->height_when_hidden = 2;
77 p->transparent = 0;
78 p->alpha = 127;
79 p->tintcolor = 0xFFFFFFFF;
80 p->usefontcolor = 0;
81 p->fontcolor = 0x00000000;
82 p->spacing = 0;
8f9e6256 83 p->icon_size = PANEL_ICON_SIZE;
9dd114c4 84 return p;
85}
86
87/* Normalize panel configuration after load from file or reconfiguration. */
88static void panel_normalize_configuration(Panel* p)
89{
2918994e 90 panel_set_panel_configuration_changed( p );
9dd114c4 91 if (p->width < 0)
92 p->width = 100;
93 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
94 p->width = 100;
95 p->heighttype = HEIGHT_PIXEL;
96 if (p->heighttype == HEIGHT_PIXEL) {
97 if (p->height < PANEL_HEIGHT_MIN)
98 p->height = PANEL_HEIGHT_MIN;
99 else if (p->height > PANEL_HEIGHT_MAX)
100 p->height = PANEL_HEIGHT_MAX;
101 }
102 if (p->background)
103 p->transparent = 0;
104}
105
a52c2257
HJYP
106/****************************************************
107 * panel's handlers for WM events *
108 ****************************************************/
a52c2257 109
22242ed4 110void panel_set_wm_strut(Panel *p)
a52c2257 111{
d1d43629 112 int index;
113 gulong strut_size;
114 gulong strut_lower;
115 gulong strut_upper;
a52c2257 116
d1d43629 117 /* Dispatch on edge to set up strut parameters. */
118 switch (p->edge)
bee4c26e 119 {
d1d43629 120 case EDGE_LEFT:
121 index = 0;
122 strut_size = p->aw;
123 strut_lower = p->ay;
124 strut_upper = p->ay + p->ah;
125 break;
126 case EDGE_RIGHT:
127 index = 1;
128 strut_size = p->aw;
129 strut_lower = p->ay;
130 strut_upper = p->ay + p->ah;
131 break;
132 case EDGE_TOP:
133 index = 2;
134 strut_size = p->ah;
135 strut_lower = p->ax;
136 strut_upper = p->ax + p->aw;
137 break;
138 case EDGE_BOTTOM:
139 index = 3;
140 strut_size = p->ah;
141 strut_lower = p->ax;
142 strut_upper = p->ax + p->aw;
143 break;
144 default:
145 return;
bee4c26e
HJYP
146 }
147
176fb687 148 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
149 if ( ! p->visible)
150 strut_size = p->height_when_hidden;
151
d1d43629 152 /* Set up strut value in property format. */
153 gulong desired_strut[12];
154 memset(desired_strut, 0, sizeof(desired_strut));
155 if (p->setstrut)
156 {
157 desired_strut[index] = strut_size;
158 desired_strut[4 + index * 2] = strut_lower;
159 desired_strut[5 + index * 2] = strut_upper;
160 }
161 else
162 {
163 strut_size = 0;
164 strut_lower = 0;
165 strut_upper = 0;
bee4c26e 166 }
bee4c26e 167
d1d43629 168 /* If strut value changed, set the property value on the panel window.
169 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
170 if ((GTK_WIDGET_MAPPED(p->topgwin))
547ece89 171 && ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge)))
d1d43629 172 {
173 p->strut_size = strut_size;
174 p->strut_lower = strut_lower;
175 p->strut_upper = strut_upper;
547ece89 176 p->strut_edge = p->edge;
a52c2257 177
d1d43629 178 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
179 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
180 if (strut_size != 0)
181 {
182 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
183 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
184 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
185 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
186 }
187 else
188 {
189 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
190 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
191 }
192 }
a52c2257
HJYP
193}
194
e996608e 195/* defined in plugins/menu.c */
8c44345a 196gboolean show_system_menu( gpointer system_menu );
e996608e 197
3e7b8eb7
HJYP
198/* defined in configurator.c */
199void panel_configure(Panel* p, int sel_page );
9dd114c4 200gboolean panel_edge_available(Panel* p, int edge);
3e7b8eb7 201
e996608e 202/* built-in commands, defined in configurator.c */
77886b88
HJYP
203void restart(void);
204void gtk_run(void);
e3b89f43 205void panel_destroy(Panel *p);
77886b88 206
d18c9409 207static void process_client_msg ( XClientMessageEvent* ev )
77886b88 208{
8c44345a 209 int cmd = ev->data.b[0];
77886b88
HJYP
210 switch( cmd )
211 {
ace2a572 212#ifndef DISABLE_MENU
77886b88 213 case LXPANEL_CMD_SYS_MENU:
d18c9409
HJYP
214 {
215 GSList* l;
216 for( l = all_panels; l; l = l->next )
8c44345a 217 {
d18c9409
HJYP
218 Panel* p = (Panel*)l->data;
219 if( p->system_menus )
220 {
221 /* show_system_menu( p->system_menus->data ); */
222 /* FIXME: I've no idea why this doesn't work without timeout
223 under some WMs, like icewm. */
224 g_timeout_add( 200, (GSourceFunc)show_system_menu,
225 p->system_menus->data );
226 }
8c44345a 227 }
77886b88 228 break;
d18c9409 229 }
ace2a572
JH
230#endif
231#ifndef DISABLE_MENU
77886b88
HJYP
232 case LXPANEL_CMD_RUN:
233 gtk_run();
234 break;
ace2a572 235#endif
77886b88 236 case LXPANEL_CMD_CONFIG:
8110399f 237 //FIXME: configure();
77886b88
HJYP
238 break;
239 case LXPANEL_CMD_RESTART:
240 restart();
241 break;
242 case LXPANEL_CMD_EXIT:
243 gtk_main_quit();
244 break;
245 }
246}
247
a52c2257 248static GdkFilterReturn
cf701cb7 249panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
a52c2257
HJYP
250{
251 Atom at;
252 Window win;
253 XEvent *ev = (XEvent *) xevent;
254
255 ENTER;
256 DBG("win = 0x%x\n", ev->xproperty.window);
77886b88
HJYP
257 if (ev->type != PropertyNotify ) {
258 /* private client message from lxpanelctl */
259 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
260 {
d18c9409 261 process_client_msg( (XClientMessageEvent*)ev );
77886b88 262 }
22242ed4
HJYP
263 else if( ev->type == DestroyNotify )
264 {
cf701cb7 265 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
22242ed4 266 }
a52c2257 267 RET(GDK_FILTER_CONTINUE);
24053345 268 }
77886b88 269
a52c2257
HJYP
270 at = ev->xproperty.atom;
271 win = ev->xproperty.window;
a52c2257 272 if (win == GDK_ROOT_WINDOW()) {
cf701cb7
HJYP
273 if (at == a_NET_CLIENT_LIST) {
274 fb_ev_emit(fbev, EV_CLIENT_LIST);
275 } else if (at == a_NET_CURRENT_DESKTOP) {
276 GSList* l;
277 for( l = all_panels; l; l = l->next )
278 ((Panel*)l->data)->curdesk = get_net_current_desktop();
22242ed4 279 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
cf701cb7
HJYP
280 } else if (at == a_NET_NUMBER_OF_DESKTOPS) {
281 GSList* l;
282 for( l = all_panels; l; l = l->next )
283 ((Panel*)l->data)->desknum = get_net_number_of_desktops();
22242ed4 284 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
cf701cb7 285 } else if (at == a_NET_DESKTOP_NAMES) {
22242ed4 286 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
a52c2257 287 } else if (at == a_NET_ACTIVE_WINDOW) {
22242ed4 288 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
cf701cb7 289 } else if (at == a_NET_CLIENT_LIST_STACKING) {
22242ed4 290 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
a52c2257 291 } else if (at == a_XROOTPMAP_ID) {
cf701cb7
HJYP
292 GSList* l;
293 for( l = all_panels; l; l = l->next )
294 {
295 Panel* p = (Panel*)l->data;
296 if (p->transparent) {
297 fb_bg_notify_changed_bg(p->bg);
298 }
299 }
300 } else if (at == a_NET_WORKAREA) {
301 GSList* l;
302 for( l = all_panels; l; l = l->next )
303 {
304 Panel* p = (Panel*)l->data;
305 g_free( p->workarea );
306 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1d9dc649 307 /* print_wmdata(p); */
a52c2257 308 }
a52c2257 309 } else
cf701cb7
HJYP
310 return GDK_FILTER_CONTINUE;
311
312 return GDK_FILTER_REMOVE;
a52c2257 313 }
cf701cb7 314 return GDK_FILTER_CONTINUE;
a52c2257
HJYP
315}
316
317/****************************************************
318 * panel's handlers for GTK events *
319 ****************************************************/
320
bee4c26e 321
a52c2257
HJYP
322static gint
323panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
324{
325 ENTER;
326 RET(FALSE);
327}
328
329static gint
330panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
331{
22242ed4 332 //Panel *p = (Panel *) data;
a52c2257
HJYP
333 //if (!p->self_destroy)
334 gtk_main_quit();
335 RET(FALSE);
336}
337
4542c20d 338static void
22242ed4 339on_root_bg_changed(FbBg *bg, Panel* p)
4542c20d
HJYP
340{
341 panel_update_background( p );
342}
343
2918994e 344void panel_determine_background_pixmap(Panel * p, GtkWidget * widget, GdkWindow * window)
4542c20d 345{
2918994e 346 GdkPixmap * pixmap = NULL;
4542c20d 347
2918994e 348 /* Free p->bg if it is not going to be used. */
349 if (( ! p->transparent) && (p->bg != NULL))
350 {
351 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
352 g_object_unref(p->bg);
353 p->bg = NULL;
354 }
4542c20d 355
2918994e 356 if (p->background)
357 {
358 /* User specified background pixmap. */
359 if (p->background_file != NULL)
360 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
4542c20d 361 }
2918994e 362
363 else if (p->transparent)
4542c20d 364 {
2918994e 365 /* Transparent. Determine the appropriate value from the root pixmap. */
366 if (p->bg == NULL)
4542c20d 367 {
2918994e 368 p->bg = fb_bg_get_for_display();
369 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), p);
4542c20d 370 }
2918994e 371 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
372 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
373 fb_bg_composite(pixmap, widget->style->black_gc, p->tintcolor, p->alpha);
4542c20d
HJYP
374 }
375
2918994e 376 if (pixmap != NULL)
4542c20d 377 {
2918994e 378 gtk_widget_set_app_paintable(widget, TRUE );
379 gdk_window_set_back_pixmap(window, pixmap, FALSE);
380 g_object_unref(pixmap);
4542c20d
HJYP
381 }
382 else
2918994e 383 gtk_widget_set_app_paintable(widget, FALSE);
384}
4542c20d 385
2918994e 386/* Update the background of the entire panel.
387 * This function should only be called after the panel has been realized. */
388void panel_update_background(Panel * p)
389{
390 /* Redraw the top level widget. */
391 panel_determine_background_pixmap(p, p->topgwin, p->topgwin->window);
392 gdk_window_clear(p->topgwin->window);
393 gtk_widget_queue_draw(p->topgwin);
394 if (gtk_events_pending()) gtk_main_iteration();
395
396 /* Loop over all plugins redrawing each plugin. */
397 GList * l;
398 for (l = p->plugins; l != NULL; l = l->next)
4542c20d 399 {
2918994e 400 Plugin * pl = (Plugin *) l->data;
401 plugin_set_background(pl, p);
4542c20d
HJYP
402 }
403
4542c20d 404}
a52c2257 405
84fc1d55
HJYP
406static gboolean delay_update_background( Panel* p )
407{
6589616e
JH
408 /* Panel could be destroyed while background update scheduled */
409 if ( p->topgwin && GTK_WIDGET_REALIZED ( p->topgwin ) ) {
410 gdk_display_sync( gtk_widget_get_display(p->topgwin) );
411 panel_update_background( p );
412 }
413
cf701cb7 414 return FALSE;
84fc1d55
HJYP
415}
416
417static void
f2d54481
HJYP
418panel_realize(GtkWidget *widget, Panel *p)
419{
e3b89f43 420 g_idle_add_full( G_PRIORITY_LOW,
421 (GSourceFunc)delay_update_background, p, NULL );
f2d54481
HJYP
422}
423
424static void
84fc1d55
HJYP
425panel_style_set(GtkWidget *widget, GtkStyle* prev, Panel *p)
426{
0987f648 427 /* FIXME: This dirty hack is used to fix the background of systray... */
cf701cb7 428 if( GTK_WIDGET_REALIZED( widget ) )
e3b89f43 429 g_idle_add_full( G_PRIORITY_LOW,
430 (GSourceFunc)delay_update_background, p, NULL );
84fc1d55 431}
4542c20d 432
a52c2257 433static gint
22242ed4 434panel_size_req(GtkWidget *widget, GtkRequisition *req, Panel *p)
a52c2257
HJYP
435{
436 ENTER;
4542c20d 437
a52c2257
HJYP
438 if (p->widthtype == WIDTH_REQUEST)
439 p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height;
440 if (p->heighttype == HEIGHT_REQUEST)
441 p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width;
442 calculate_position(p);
443 req->width = p->aw;
444 req->height = p->ah;
4542c20d 445
a52c2257
HJYP
446 RET( TRUE );
447}
448
449static gint
22242ed4 450panel_size_alloc(GtkWidget *widget, GtkAllocation *a, Panel *p)
a52c2257
HJYP
451{
452 ENTER;
a52c2257
HJYP
453 if (p->widthtype == WIDTH_REQUEST)
454 p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height;
455 if (p->heighttype == HEIGHT_REQUEST)
456 p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width;
457 calculate_position(p);
4542c20d 458
a52c2257 459 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
a52c2257
HJYP
460 RET(TRUE);
461 }
462
463 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
bee4c26e 464 panel_set_wm_strut(p);
a52c2257
HJYP
465 RET(TRUE);
466}
467
a52c2257 468static gboolean
22242ed4 469panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, Panel *p)
a52c2257
HJYP
470{
471 ENTER;
472 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
473 RET(TRUE);
474 p->cw = e->width;
475 p->ch = e->height;
476 p->cx = e->x;
477 p->cy = e->y;
4542c20d 478
a52c2257
HJYP
479 if (p->transparent)
480 fb_bg_notify_changed_bg(p->bg);
bee4c26e 481
4542c20d 482 RET(FALSE);
a52c2257
HJYP
483}
484
fddae119
FC
485static gint
486panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
487{
4b93d81e
HJYP
488 panel_configure( (Panel*)user_data, 0 );
489 return TRUE;
fddae119
FC
490}
491
9dd114c4 492/* Handler for "button_press_event" signal with Panel as parameter. */
493static gboolean panel_button_press_event_with_panel(GtkWidget *widget, GdkEventButton *event, Panel *panel)
fddae119 494{
9dd114c4 495 if (event->button == 3) /* right button */
496 {
497 GtkMenu* popup = (GtkMenu*) lxpanel_get_panel_menu(panel, NULL, FALSE);
498 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
499 return TRUE;
500 }
fddae119
FC
501 return FALSE;
502}
503
cf701cb7
HJYP
504static void panel_popupmenu_config_plugin( GtkMenuItem* item, Plugin* plugin )
505{
e3b89f43 506 plugin->class->config( plugin, GTK_WINDOW(plugin->panel->topgwin) );
930af9fd
HJYP
507
508 /* FIXME: this should be more elegant */
509 plugin->panel->config_changed = TRUE;
cf701cb7
HJYP
510}
511
4b93d81e
HJYP
512static void panel_popupmenu_add_item( GtkMenuItem* item, Panel* panel )
513{
514 /* panel_add_plugin( panel, panel->topgwin ); */
8d8b66da 515 panel_configure( panel, 2 );
cf701cb7
HJYP
516}
517
518static void panel_popupmenu_remove_item( GtkMenuItem* item, Plugin* plugin )
519{
520 Panel* panel = plugin->panel;
521 panel->plugins = g_list_remove( panel->plugins, plugin );
2918994e 522 plugin_delete(plugin);
4b93d81e 523 panel_config_save( plugin->panel );
cf701cb7
HJYP
524}
525
3e7b8eb7
HJYP
526/* FIXME: Potentially we can support multiple panels at the same edge,
527 * but currently this cannot be done due to some positioning problems. */
528static char* gen_panel_name( int edge )
529{
530 const char* edge_str = num2str( edge_pair, edge, "" );
531 char* name = NULL;
532 char* dir = get_config_file( cprofile, "panels", FALSE );
533 int i;
534 for( i = 0; i < G_MAXINT; ++i )
535 {
536 char* f;
537 if( G_LIKELY( i > 0 ) )
538 name = g_strdup_printf( "%s%d", edge_str, i );
539 else
540 name = g_strdup( edge_str );
541 f = g_build_filename( dir, name, NULL );
542 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
543 {
544 g_free( f );
545 break;
546 }
547 g_free( name );
548 g_free( f );
549 }
550 g_free( dir );
551 return name;
552}
553
9dd114c4 554static void try_allocate_edge(Panel* p, int edge)
555{
556 if ((p->edge == EDGE_NONE) && (panel_edge_available(p, edge)))
557 p->edge = edge;
558}
559
3e7b8eb7
HJYP
560/* FIXME: Potentially we can support multiple panels at the same edge,
561 * but currently this cannot be done due to some positioning problems. */
cf701cb7
HJYP
562static void panel_popupmenu_create_panel( GtkMenuItem* item, Panel* panel )
563{
9dd114c4 564 Panel* new_panel = panel_allocate();
565
566 /* Allocate the edge. */
567 try_allocate_edge(new_panel, EDGE_BOTTOM);
568 try_allocate_edge(new_panel, EDGE_TOP);
569 try_allocate_edge(new_panel, EDGE_LEFT);
570 try_allocate_edge(new_panel, EDGE_RIGHT);
571 new_panel->name = gen_panel_name(new_panel->edge);
572
573 panel_configure(new_panel, 0);
574 panel_normalize_configuration(new_panel);
575 panel_start_gui(new_panel);
576 gtk_widget_show_all(new_panel->topgwin);
577
578 panel_config_save(new_panel);
579 all_panels = g_slist_prepend(all_panels, new_panel);
cf701cb7
HJYP
580}
581
582static void panel_popupmenu_delete_panel( GtkMenuItem* item, Panel* panel )
583{
584 GtkWidget* dlg;
585 gboolean ok;
e3b89f43 586 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel->topgwin),
cf701cb7
HJYP
587 GTK_DIALOG_MODAL,
588 GTK_MESSAGE_QUESTION,
589 GTK_BUTTONS_OK_CANCEL,
590 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
d1d43629 591 panel_apply_icon(GTK_WINDOW(dlg));
cf701cb7
HJYP
592 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
593 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
594 gtk_widget_destroy( dlg );
595 if( ok )
596 {
4b93d81e 597 gchar *fname, *dir;
cf701cb7 598 all_panels = g_slist_remove( all_panels, panel );
4b93d81e
HJYP
599
600 /* delete the config file of this panel */
601 dir = get_config_file( cprofile, "panels", FALSE );
602 fname = g_build_filename( dir, panel->name, NULL );
603 g_free( dir );
604 g_unlink( fname );
930af9fd 605 panel->config_changed = 0;
cf701cb7
HJYP
606 panel_destroy( panel );
607 }
608}
609
e7a42ecf 610static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
611{
612 GtkWidget *about;
613 const gchar* authors[] = {
614 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
615 "Jim Huang <jserv.tw@gmail.com>",
616 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
617 "Fred Chien <cfsghost@gmail.com>",
618 "Daniel Kesler <kesler.daniel@gmail.com>",
619 "Juergen Hoetzel <juergen@archlinux.org>",
2918994e 620 "Marty Jack <martyj19@comcast.net>",
e7a42ecf 621 NULL
622 };
623 /* TRANSLATORS: Replace this string with your names, one name per line. */
624 gchar *translators = _( "translator-credits" );
625
626 about = gtk_about_dialog_new();
627 panel_apply_icon(GTK_WINDOW(about));
628 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
629 gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
630 gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL));
631 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2009"));
632 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
633 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.");
634 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
635 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
636 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
637 gtk_dialog_run(GTK_DIALOG(about));
638 gtk_widget_destroy(about);
639}
640
641void panel_apply_icon( GtkWindow *w )
642{
643 gtk_window_set_icon_from_file(w, PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL);
644}
645
9dd114c4 646GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
cf701cb7 647{
e3b89f43 648 GtkWidget *menu_item, *img;
649 GtkMenu *ret,*menu;
650
cf701cb7 651 char* tmp;
e3b89f43 652 ret = menu = GTK_MENU(gtk_menu_new());
cf701cb7 653
4b93d81e
HJYP
654 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
655 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
656 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
657 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
658 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
659
660 if( plugin )
661 {
662 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
663 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
664 menu_item = gtk_image_menu_item_new_with_label( tmp );
665 g_free( tmp );
666 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
667 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
668 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
669 }
670
671 menu_item = gtk_separator_menu_item_new();
672 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
673
4b93d81e
HJYP
674 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
675 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
676 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
677 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
678 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
679
cf701cb7
HJYP
680 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
681 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
682 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
683 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
684 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
3e7b8eb7
HJYP
685 /* FIXME: Potentially we can support multiple panels at the same edge,
686 * but currently this cannot be done due to some positioning problems. */
687 /* currently, disable the option when there are already four panels */
688 if( g_slist_length( all_panels ) >= 4 )
689 gtk_widget_set_sensitive( menu_item, FALSE );
cf701cb7
HJYP
690
691 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
692 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
693 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
694 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
695 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
696 if( ! all_panels->next ) /* if this is the only panel */
697 gtk_widget_set_sensitive( menu_item, FALSE );
698
e7a42ecf 699 menu_item = gtk_separator_menu_item_new();
700 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
701
702 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
703 menu_item = gtk_image_menu_item_new_with_label(_("About"));
704 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
705 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
706 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel );
707
cf701cb7
HJYP
708 if( use_sub_menu )
709 {
e3b89f43 710 ret = GTK_MENU(gtk_menu_new());
cf701cb7
HJYP
711 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
712 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
e3b89f43 713 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
cf701cb7 714
e3b89f43 715 gtk_widget_show_all(GTK_WIDGET(ret));
cf701cb7
HJYP
716 }
717
3e7b8eb7
HJYP
718 if( plugin )
719 {
720 menu_item = gtk_separator_menu_item_new();
721 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
722
723 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
724 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
725 menu_item = gtk_image_menu_item_new_with_label( tmp );
726 g_free( tmp );
727 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
728 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
729 if( plugin->class->config )
730 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
731 else
732 gtk_widget_set_sensitive( menu_item, FALSE );
733 }
734
e3b89f43 735 gtk_widget_show_all(GTK_WIDGET(menu));
3e7b8eb7 736
cf701cb7
HJYP
737 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
738 return ret;
739}
740
a52c2257
HJYP
741/****************************************************
742 * panel creation *
743 ****************************************************/
176fb687 744
a52c2257 745static void
22242ed4 746make_round_corners(Panel *p)
a52c2257 747{
a97d06a6 748 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 749 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
750}
751
22242ed4 752void panel_set_dock_type(Panel *p)
bee4c26e
HJYP
753{
754 if (p->setdocktype) {
755 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
756 XChangeProperty(GDK_DISPLAY(), p->topxwin,
757 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
758 PropModeReplace, (unsigned char *) &state, 1);
759 }
760 else {
761 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
762 }
a52c2257
HJYP
763}
764
176fb687 765static void panel_set_visibility(Panel *p, gboolean visible)
766{
767 if ( ! visible) gtk_widget_hide(p->box);
768 p->visible = visible;
769 calculate_position(p);
770 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
771 gdk_window_move(p->topgwin->window, p->ax, p->ay);
772 if (visible) gtk_widget_show(p->box);
773 panel_set_wm_strut(p);
774}
775
776static gboolean panel_leave_real(Panel *p)
777{
778 if (gdk_display_pointer_is_grabbed(p->display))
779 return TRUE;
780
781 gint x, y;
782 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
783 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
784 return TRUE;
785
786 if ((p->autohide) && (p->visible))
787 panel_set_visibility(p, FALSE);
788
789 p->hide_timeout = 0;
790 return FALSE;
791}
792
793static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
794{
795 if (p->hide_timeout)
796 return FALSE;
797
798 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
799
800 panel_set_visibility(p, TRUE);
801 return TRUE;
802}
803
804static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
805 gint y, guint time, Panel *p)
806{
807 panel_enter(NULL, NULL, p);
808 return TRUE;
809}
810
811void panel_establish_autohide(Panel *p)
812{
813 if (p->autohide)
814 {
815 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
816 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
817 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
818 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
819 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
820 panel_enter(NULL, NULL, p);
821 }
822 else if ( ! p->visible)
823 {
824 gtk_widget_show(p->box);
825 p->visible = TRUE;
826 }
827}
828
8f9e6256 829/* Set an image from a file with scaling to the panel icon size. */
830void panel_image_set_from_file(Panel * p, GtkWidget * image, char * file)
831{
832 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
833 if (pixbuf != NULL)
834 {
835 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
836 g_object_unref(pixbuf);
837 }
838}
839
0defe4b9 840static void
22242ed4 841panel_start_gui(Panel *p)
a52c2257
HJYP
842{
843 Atom state[3];
844 XWMHints wmhints;
845 guint32 val;
6db11841 846
a52c2257
HJYP
847 ENTER;
848
9dd114c4 849 p->curdesk = get_net_current_desktop();
850 p->desknum = get_net_number_of_desktops();
851 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
852
a52c2257
HJYP
853 // main toplevel window
854 p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
176fb687 855 p->display = gdk_display_get_default();
a52c2257
HJYP
856 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
857 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
858 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
859 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
860 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
861 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
77886b88 862
e2957bd2
HJYP
863 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
864
a52c2257
HJYP
865 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
866 G_CALLBACK(panel_delete_event), p);
867 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
868 G_CALLBACK(panel_destroy_event), p);
869 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
870 (GCallback) panel_size_req, p);
871 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
872 (GCallback) panel_size_alloc, p);
873 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
874 (GCallback) panel_configure_event, p);
3e7b8eb7
HJYP
875
876 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
fddae119 877 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
9dd114c4 878 (GCallback) panel_button_press_event_with_panel, p);
f2d54481 879
a52c2257
HJYP
880 g_signal_connect (G_OBJECT (p->topgwin), "realize",
881 (GCallback) panel_realize, p);
f2d54481 882
84fc1d55
HJYP
883 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
884 (GCallback)panel_style_set, p);
a52c2257
HJYP
885 gtk_widget_realize(p->topgwin);
886 //gdk_window_set_decorations(p->topgwin->window, 0);
2de71c90 887
4542c20d 888 // main layout manager as a single child of panel
a97d06a6 889 p->box = p->my_box_new(FALSE, 0);
a52c2257 890 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
4542c20d
HJYP
891// gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
892 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
a52c2257 893 gtk_widget_show(p->box);
a97d06a6
HJYP
894 if (p->round_corners)
895 make_round_corners(p);
6db11841 896
a52c2257
HJYP
897 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
898 DBG("topxwin = %x\n", p->topxwin);
899
900 /* the settings that should be done before window is mapped */
901 wmhints.flags = InputHint;
902 wmhints.input = 0;
bee4c26e 903 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
24053345 904#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257
HJYP
905 val = WIN_HINTS_SKIP_FOCUS;
906 XChangeProperty(GDK_DISPLAY(), p->topxwin,
907 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
908 PropModeReplace, (unsigned char *) &val, 1);
909
bee4c26e 910 panel_set_dock_type(p);
a52c2257
HJYP
911
912 /* window mapping point */
913 gtk_widget_show_all(p->topgwin);
914
915 /* the settings that should be done after window is mapped */
176fb687 916 panel_establish_autohide(p);
a52c2257
HJYP
917
918 /* send it to running wm */
919 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
920 /* and assign it ourself just for case when wm is not running */
921 val = 0xFFFFFFFF;
922 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
923 PropModeReplace, (unsigned char *) &val, 1);
924
925 state[0] = a_NET_WM_STATE_SKIP_PAGER;
926 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
927 state[2] = a_NET_WM_STATE_STICKY;
928 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
929 32, PropModeReplace, (unsigned char *) state, 3);
930
a52c2257
HJYP
931 calculate_position(p);
932 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
bee4c26e 933 panel_set_wm_strut(p);
77886b88 934
a52c2257
HJYP
935 RET();
936}
937
2918994e 938/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
939void panel_adjust_geometry_terminology(Panel * p)
9dd114c4 940{
941 if ((p->height_label != NULL) && (p->width_label != NULL))
942 {
943 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
944 {
945 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
946 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
2918994e 947 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
948 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
9dd114c4 949 }
950 else
951 {
952 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
953 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
2918994e 954 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
955 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
9dd114c4 956 }
957 }
958}
959
2918994e 960/* Draw text into a label, with the user preference color and optionally bold. */
961void panel_draw_label_text(Panel * p, GtkWidget * label, char * text, gboolean bold)
962{
963 char buffer[512];
964
965 if (text == NULL)
966 {
967 /* Null string. */
968 gtk_label_set_text(GTK_LABEL(label), NULL);
969 }
970
f669ce5e 971 else
2918994e 972 {
f669ce5e 973 /* Compute an appropriate size so the font will scale with the panel's icon size. */
974 int font_desc;
975 if (p->icon_size < 20)
976 font_desc = 9;
977 else if (p->icon_size >= 20 && p->icon_size < 26)
978 font_desc = 10;
f669ce5e 979 else
ee4695be 980 font_desc = 12;
2918994e 981
982 /* Check the string for characters that need to be escaped.
983 * If any are found, create the properly escaped string and use it instead. */
f669ce5e 984 char * valid_markup = text;
985 char * escaped_text = NULL;
2918994e 986 char * q;
987 for (q = text; *q != '\0'; q += 1)
988 {
989 if ((*q == '<') || (*q == '>') || (*q == '&'))
990 {
991 escaped_text = g_markup_escape_text(text, -1);
992 valid_markup = escaped_text;
993 break;
994 }
995 }
996
997 if (p->usefontcolor)
998 {
999 /* Color, optionally bold. */
f669ce5e 1000 g_snprintf(buffer, sizeof(buffer), "<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
1001 font_desc,
2918994e 1002 gcolor2rgb24(&p->gfontcolor),
1003 ((bold) ? "<b>" : ""),
1004 valid_markup,
1005 ((bold) ? "</b>" : ""));
1006 gtk_label_set_markup(GTK_LABEL(label), buffer);
1007 }
f669ce5e 1008 else
2918994e 1009 {
f669ce5e 1010 /* No color, optionally bold. */
1011 g_snprintf(buffer, sizeof(buffer), "<span font_desc=\"%d\">%s%s%s</span>",
1012 font_desc,
1013 ((bold) ? "<b>" : ""),
1014 valid_markup,
1015 ((bold) ? "</b>" : ""));
2918994e 1016 gtk_label_set_markup(GTK_LABEL(label), buffer);
1017 }
1018 g_free(escaped_text);
1019 }
2918994e 1020}
1021
1022void panel_set_panel_configuration_changed(Panel *p)
a97d06a6
HJYP
1023{
1024 GList* l;
9dd114c4 1025
1026 int previous_orientation = p->orientation;
a97d06a6
HJYP
1027 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1028 ? ORIENT_HORIZ : ORIENT_VERT;
9dd114c4 1029
1030 if (previous_orientation != p->orientation)
1031 {
1032 panel_adjust_geometry_terminology(p);
1033 p->height = ((p->orientation == ORIENT_HORIZ) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1034 if (p->height_control != NULL)
1035 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1036 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1037 {
1038 int value = ((p->orientation == ORIENT_HORIZ) ? gdk_screen_width() : gdk_screen_height());
1039 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1040 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1041 }
1042
9dd114c4 1043 }
1044
a97d06a6
HJYP
1045 if (p->orientation == ORIENT_HORIZ) {
1046 p->my_box_new = gtk_hbox_new;
1047 p->my_separator_new = gtk_vseparator_new;
1048 } else {
1049 p->my_box_new = gtk_vbox_new;
1050 p->my_separator_new = gtk_hseparator_new;
1051 }
1052
1053 /* recreate the main layout box */
1054 if( p->box ) {
5a343ad5
JH
1055 GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
1056 if( GTK_WIDGET(newbox) != p->box ) {
1057 p->box = GTK_WIDGET(newbox);
4542c20d 1058 gtk_container_add( GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox) );
a97d06a6
HJYP
1059 }
1060 }
9dd114c4 1061
a97d06a6
HJYP
1062 /* NOTE: This loop won't be executed when panel started since
1063 plugins are not loaded at that time.
1064 This is used when the orientation of the panel is changed
1065 from the config dialog, and plugins should be re-layout.
1066 */
1067 for( l = p->plugins; l; l = l->next ) {
22242ed4 1068 Plugin* pl = (Plugin*)l->data;
2918994e 1069 if( pl->class->panel_configuration_changed ) {
1070 pl->class->panel_configuration_changed( pl );
a97d06a6
HJYP
1071 }
1072 }
1073}
1074
a52c2257 1075static int
22242ed4 1076panel_parse_global(Panel *p, char **fp)
a52c2257
HJYP
1077{
1078 line s;
1079 s.len = 256;
6db11841 1080
3e7b8eb7
HJYP
1081 if( G_LIKELY( fp ) )
1082 {
1083 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1084 if (s.type == LINE_VAR) {
1085 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
1086 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
1087 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
1088 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
1089 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
1090 p->margin = atoi(s.t[1]);
1091 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
1092 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
1093 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
1094 p->width = atoi(s.t[1]);
1095 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
1096 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
1097 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
1098 p->height = atoi(s.t[1]);
1099 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1100 p->spacing = atoi(s.t[1]);
1101 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
1102 p->setdocktype = str2num(bool_pair, s.t[1], 0);
1103 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
1104 p->setstrut = str2num(bool_pair, s.t[1], 0);
1105 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
1106 p->round_corners = str2num(bool_pair, s.t[1], 0);
1107 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
1108 p->transparent = str2num(bool_pair, s.t[1], 0);
1109 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
1110 p->alpha = atoi(s.t[1]);
1111 if (p->alpha > 255)
1112 p->alpha = 255;
176fb687 1113 } else if (!g_ascii_strcasecmp(s.t[0], "AutoHide")) {
1114 p->autohide = str2num(bool_pair, s.t[1], 0);
1115 } else if (!g_ascii_strcasecmp(s.t[0], "HeightWhenHidden")) {
1116 p->height_when_hidden = atoi(s.t[1]);
3e7b8eb7
HJYP
1117 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
1118 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
1119 gdk_color_parse ("white", &p->gtintcolor);
1120 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1121 DBG("tintcolor=%x\n", p->tintcolor);
1122 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
1123 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
1124 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
1125 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1126 gdk_color_parse ("black", &p->gfontcolor);
1127 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1128 DBG("fontcolor=%x\n", p->fontcolor);
1129 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1130 p->background = str2num(bool_pair, s.t[1], 0);
1131 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1132 p->background_file = g_strdup( s.t[1] );
8f9e6256 1133 } else if (!g_ascii_strcasecmp(s.t[0], "IconSize")) {
1134 p->icon_size = atoi(s.t[1]);
3e7b8eb7
HJYP
1135 } else {
1136 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
3e7b8eb7
HJYP
1137 }
1138 } else if (s.type == LINE_BLOCK_END) {
1139 break;
a52c2257 1140 } else {
3e7b8eb7 1141 ERR( "lxpanel: illegal in this context %s\n", s.str);
a52c2257
HJYP
1142 RET(0);
1143 }
a52c2257
HJYP
1144 }
1145 }
4542c20d 1146
9dd114c4 1147 panel_normalize_configuration(p);
239cb032 1148
9dd114c4 1149 return 1;
a52c2257
HJYP
1150}
1151
1152static int
22242ed4 1153panel_parse_plugin(Panel *p, char **fp)
a52c2257
HJYP
1154{
1155 line s;
22242ed4 1156 Plugin *plug = NULL;
a52c2257 1157 gchar *type = NULL;
a52c2257 1158 int expand , padding, border;
db449f6e
HJYP
1159 char* pconfig = NULL;
1160
a52c2257
HJYP
1161 ENTER;
1162 s.len = 256;
a52c2257 1163 border = expand = padding = 0;
c69ac68e 1164 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
1165 if (s.type == LINE_NONE) {
1166 ERR( "lxpanel: bad line %s\n", s.str);
1167 goto error;
1168 }
1169 if (s.type == LINE_VAR) {
1170 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1171 type = g_strdup(s.t[1]);
1172 DBG("plug %s\n", type);
1173 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1174 expand = str2num(bool_pair, s.t[1], 0);
1175 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1176 padding = atoi(s.t[1]);
1177 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1178 border = atoi(s.t[1]);
1179 else {
1180 ERR( "lxpanel: unknown var %s\n", s.t[0]);
a52c2257
HJYP
1181 }
1182 } else if (s.type == LINE_BLOCK_START) {
1183 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 1184 pconfig = *fp;
a52c2257
HJYP
1185 int pno = 1;
1186 while (pno) {
1187 get_line_as_is(fp, &s);
1188 if (s.type == LINE_NONE) {
1189 ERR( "lxpanel: unexpected eof\n");
1190 goto error;
1191 } else if (s.type == LINE_BLOCK_START) {
1192 pno++;
1193 } else if (s.type == LINE_BLOCK_END) {
1194 pno--;
bee4c26e 1195 }
db449f6e 1196 }
a52c2257
HJYP
1197 } else {
1198 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1199 goto error;
1200 }
1201 } else {
1202 ERR( "lxpanel: illegal in this context %s\n", s.str);
1203 goto error;
1204 }
1205 }
db449f6e 1206
a52c2257
HJYP
1207 if (!type || !(plug = plugin_load(type))) {
1208 ERR( "lxpanel: can't load %s plugin\n", type);
1209 goto error;
1210 }
db449f6e 1211
a52c2257 1212 plug->panel = p;
2918994e 1213 if (plug->class->expand_available) plug->expand = expand;
a52c2257
HJYP
1214 plug->padding = padding;
1215 plug->border = border;
a52c2257 1216 DBG("starting\n");
db449f6e 1217 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
1218 ERR( "lxpanel: can't start plugin %s\n", type);
1219 goto error;
1220 }
1221 DBG("plug %s\n", type);
1222 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
1223
1224 g_free( type );
a52c2257 1225 RET(1);
db449f6e 1226
a52c2257 1227 error:
2918994e 1228 if (plug != NULL)
1229 plugin_unload(plug);
a52c2257 1230 g_free(type);
a52c2257 1231 RET(0);
a52c2257
HJYP
1232}
1233
3e7b8eb7 1234int panel_start( Panel *p, char **fp )
a52c2257
HJYP
1235{
1236 line s;
db449f6e 1237
a52c2257
HJYP
1238 /* parse global section */
1239 ENTER;
1240 s.len = 256;
8110399f 1241
c69ac68e 1242 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
1243 ERR( "lxpanel: config file must start from Global section\n");
1244 RET(0);
1245 }
1246 if (!panel_parse_global(p, fp))
1247 RET(0);
1248
9dd114c4 1249 panel_start_gui(p);
1250
c69ac68e 1251 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
1252 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1253 ERR( "lxpanel: expecting Plugin section\n");
1254 RET(0);
1255 }
9939506e 1256 panel_parse_plugin(p, fp);
a52c2257 1257 }
4542c20d
HJYP
1258
1259 /* update backgrond of panel and all plugins */
1260 panel_update_background( p );
9dd114c4 1261 return 1;
a52c2257
HJYP
1262}
1263
1264static void
1265delete_plugin(gpointer data, gpointer udata)
1266{
2918994e 1267 plugin_delete((Plugin *)data);
a52c2257
HJYP
1268}
1269
8110399f 1270void panel_destroy(Panel *p)
a52c2257
HJYP
1271{
1272 ENTER;
1273
2918994e 1274 if (p->pref_dialog != NULL)
1275 gtk_widget_destroy(p->pref_dialog);
1276 if (p->plugin_pref_dialog != NULL)
1277 {
1278 gtk_widget_destroy(p->plugin_pref_dialog);
1279 p->plugin_pref_dialog = NULL;
1280 }
1281
1282 if (p->bg != NULL)
1283 {
1284 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1285 g_object_unref(p->bg);
1286 }
1287
930af9fd
HJYP
1288 if( p->config_changed )
1289 panel_config_save( p );
1290
a52c2257
HJYP
1291 g_list_foreach(p->plugins, delete_plugin, NULL);
1292 g_list_free(p->plugins);
1293 p->plugins = NULL;
8c44345a 1294
5297da29 1295 if( p->system_menus ){
8c44345a 1296 do{
5297da29 1297 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
1298 }
1299
16fbda14 1300 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
e2957bd2 1301
4b93d81e
HJYP
1302 if( p->topgwin )
1303 gtk_widget_destroy(p->topgwin);
a52c2257 1304 g_free(p->workarea);
2de71c90 1305 g_free( p->background_file );
5297da29 1306 g_slist_free( p->system_menus );
a52c2257
HJYP
1307 gdk_flush();
1308 XFlush(GDK_DISPLAY());
1309 XSync(GDK_DISPLAY(), True);
8110399f
HJYP
1310
1311 g_free( p->name );
2918994e 1312 g_free(p);
a52c2257
HJYP
1313 RET();
1314}
1315
8110399f
HJYP
1316Panel* panel_new( const char* config_file, const char* config_name )
1317{
1318 char *fp, *pfp; /* point to current position of profile data in memory */
cf701cb7 1319 Panel* panel = NULL;
16fbda14 1320
3e7b8eb7 1321 if( G_LIKELY(config_file) )
cf701cb7 1322 {
3e7b8eb7
HJYP
1323 g_file_get_contents( config_file, &fp, NULL, NULL );
1324 if( fp )
1325 {
9dd114c4 1326 panel = panel_allocate();
3e7b8eb7
HJYP
1327 panel->name = g_strdup( config_name );
1328 pfp = fp;
1329
1330 if (! panel_start( panel, &pfp )) {
1331 ERR( "lxpanel: can't start panel\n");
1332 panel_destroy( panel );
1333 panel = NULL;
1334 }
cf701cb7 1335
3e7b8eb7 1336 g_free( fp );
cf701cb7 1337 }
cf701cb7
HJYP
1338 }
1339 return panel;
8110399f 1340}
a52c2257 1341
0defe4b9 1342static void
a52c2257
HJYP
1343usage()
1344{
e7cb732b
HJYP
1345 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1346 g_print(_("Command line options:\n"));
1347 g_print(_(" --help -- print this help and exit\n"));
1348 g_print(_(" --version -- print version and exit\n"));
1349 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1350// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1351 g_print(_(" --profile name -- use specified profile\n"));
1352 g_print("\n");
1353 g_print(_(" -h -- same as --help\n"));
1354 g_print(_(" -p -- same as --profile\n"));
1355 g_print(_(" -v -- same as --version\n"));
2918994e 1356 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1357 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1358}
1359
2918994e 1360int panel_handle_x_error(Display * d, XErrorEvent * ev)
a52c2257
HJYP
1361{
1362 char buf[256];
1363
a52c2257
HJYP
1364 if (log_level >= LOG_WARN) {
1365 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1366 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1367 }
2918994e 1368 return 0; /* Ignored */
1369}
1370
1371int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1372{
1373 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1374 panel_handle_x_error(d, ev);
1375 return 0; /* Ignored */
a52c2257
HJYP
1376}
1377
e68b47dc
JH
1378/* Lightweight lock related functions - X clipboard hacks */
1379
1380#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1381
1382/*
1383 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1384 */
1385static void
1386clipboard_get_func(
1387 GtkClipboard *clipboard G_GNUC_UNUSED,
1388 GtkSelectionData *selection_data G_GNUC_UNUSED,
1389 guint info G_GNUC_UNUSED,
1390 gpointer user_data_or_owner G_GNUC_UNUSED)
1391{
1392}
1393
1394/*
1395 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1396 */
1397static void clipboard_clear_func(
1398 GtkClipboard *clipboard G_GNUC_UNUSED,
1399 gpointer user_data_or_owner G_GNUC_UNUSED)
1400{
1401}
1402
1403/*
1404 * Lightweight version for checking single instance.
1405 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1406 *
1407 * Returns TRUE if successfully retrieved and FALSE otherwise.
1408 */
16fb8c2e 1409static gboolean check_main_lock()
e68b47dc
JH
1410{
1411 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1412 gboolean retval = FALSE;
1413 GtkClipboard *clipboard;
1414 Atom atom;
1415
1416 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1417
1418 XGrabServer(GDK_DISPLAY());
1419
1420 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1421 goto out;
1422
1423 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1424
1425 if (gtk_clipboard_set_with_data(clipboard, targets,
1426 G_N_ELEMENTS (targets),
1427 clipboard_get_func,
1428 clipboard_clear_func, NULL))
1429 retval = TRUE;
1430
1431out:
1432 XUngrabServer (GDK_DISPLAY ());
1433 gdk_flush ();
1434
1435 return retval;
1436}
1437#undef CLIPBOARD_NAME
1438
8110399f
HJYP
1439static gboolean start_all_panels( )
1440{
cf701cb7 1441 gboolean is_global;
4b93d81e 1442 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
cf701cb7
HJYP
1443 {
1444 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1445 GDir* dir = g_dir_open( panel_dir, 0, NULL );
e3b89f43 1446 const gchar* name;
cf701cb7
HJYP
1447
1448 if( ! dir )
1449 {
1450 g_free( panel_dir );
1451 continue;
1452 }
1453
547ece89 1454 while((name = g_dir_read_name(dir)) != NULL)
cf701cb7
HJYP
1455 {
1456 char* panel_config = g_build_filename( panel_dir, name, NULL );
2918994e 1457 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1458 {
1459 Panel* panel = panel_new( panel_config, name );
1460 if( panel )
1461 all_panels = g_slist_prepend( all_panels, panel );
1462 }
cf701cb7
HJYP
1463 g_free( panel_config );
1464 }
1465 g_dir_close( dir );
1466 g_free( panel_dir );
1467 }
1468 return all_panels != NULL;
8110399f
HJYP
1469}
1470
cf701cb7
HJYP
1471void load_global_config();
1472void free_global_config();
1473
8110399f 1474int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1475{
1476 int i;
16fbda14 1477 const char* session_name;
f277dbb7 1478
a52c2257 1479 setlocale(LC_CTYPE, "");
f277dbb7 1480
a52c2257
HJYP
1481 gtk_init(&argc, &argv);
1482
1483#ifdef ENABLE_NLS
1484 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1485 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1486 textdomain ( GETTEXT_PACKAGE );
1487#endif
1488
1489 XSetLocaleModifiers("");
2918994e 1490 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1491
a52c2257 1492 resolve_atoms();
8110399f 1493
95095259
HJYP
1494 session_name = g_getenv("DESKTOP_SESSION");
1495 is_in_lxde = session_name && (0 == strcmp(session_name, "LXDE"));
1496
a52c2257
HJYP
1497 for (i = 1; i < argc; i++) {
1498 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1499 usage();
1500 exit(0);
1501 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1502 printf("lxpanel %s\n", version);
1503 exit(0);
1504 } else if (!strcmp(argv[i], "--log")) {
1505 i++;
1506 if (i == argc) {
1507 ERR( "lxpanel: missing log level\n");
1508 usage();
1509 exit(1);
1510 } else {
1511 log_level = atoi(argv[i]);
1512 }
1513 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1514 config = 1;
1515 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1516 i++;
1517 if (i == argc) {
1518 ERR( "lxpanel: missing profile name\n");
1519 usage();
1520 exit(1);
1521 } else {
1522 cprofile = g_strdup(argv[i]);
1523 }
1524 } else {
1525 printf("lxpanel: unknown option - %s\n", argv[i]);
1526 usage();
1527 exit(1);
1528 }
1529 }
f277dbb7 1530
8110399f 1531 /* Check for duplicated lxpanel instances */
e68b47dc 1532 if (!check_main_lock() && !config) {
e7a42ecf 1533 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1534 exit(1);
1535 }
1536
f277dbb7
HJYP
1537 /* Add our own icons to the search path of icon theme */
1538 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
1539 PACKAGE_DATA_DIR "/lxpanel/images" );
1540
cf701cb7 1541 fbev = fb_ev_new();
e2957bd2 1542 win_grp = gtk_window_group_new();
22242ed4 1543
f7cb330e
HJYP
1544restart:
1545 is_restarting = FALSE;
1546
cf701cb7
HJYP
1547 load_global_config();
1548
1d9dc649
HJYP
1549 /* NOTE: StructureNotifyMask is required by XRandR
1550 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1551 */
1552 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
cf701cb7 1553 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1554
cf701cb7
HJYP
1555 if( G_UNLIKELY( ! start_all_panels() ) )
1556 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1557/*
1558 * FIXME: configure??
6a6ad54e
HJYP
1559 if (config)
1560 configure();
8110399f 1561*/
6a6ad54e 1562 gtk_main();
8110399f 1563
cf701cb7
HJYP
1564 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1565 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1566
8110399f
HJYP
1567 /* destroy all panels */
1568 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
2a26c0d8
HJYP
1569 g_slist_free( all_panels );
1570 all_panels = NULL;
6a6ad54e 1571 g_free( cfgfile );
5541b8d2 1572
cf701cb7
HJYP
1573 free_global_config();
1574
f7cb330e
HJYP
1575 if( is_restarting )
1576 goto restart;
1577
e2957bd2 1578 g_object_unref(win_grp);
22242ed4
HJYP
1579 g_object_unref(fbev);
1580
5541b8d2 1581 return 0;
a52c2257 1582}