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