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