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