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