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