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