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