Merge branch 'master' of ssh://git.code.sf.net/p/lxde/lxpanel
[lxde/lxpanel.git] / src / panel.c
1 /**
2 * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <glib/gi18n.h>
24 #include <stdlib.h>
25 #include <glib/gstdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <locale.h>
31 #include <string.h>
32 #include <gdk/gdkx.h>
33 #include <libfm/fm-gtk.h>
34
35 #define __LXPANEL_INTERNALS__
36
37 #include "private.h"
38 #include "misc.h"
39 #include "bg.h"
40
41 #include "lxpanelctl.h"
42 #include "dbg.h"
43
44 static gchar *cfgfile = NULL;
45 static gchar version[] = VERSION;
46 gchar *cprofile = "default";
47
48 static GtkWindowGroup* win_grp; /* window group used to limit the scope of model dialog. */
49
50 static int config = 0;
51 FbEv *fbev = NULL;
52
53 GSList* all_panels = NULL; /* a single-linked list storing all panels */
54
55 gboolean is_restarting = FALSE;
56
57 static int panel_start(Panel *p);
58 static void panel_start_gui(Panel *p);
59
60 gboolean is_in_lxde = FALSE;
61
62 /* A hack used to be compatible with Gnome panel for gtk+ themes.
63 * Some gtk+ themes define special styles for desktop panels.
64 * http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GnomePanel
65 * So we make a derived class from GtkWindow named PanelToplevel
66 * and create the panels with it to be compatible with Gnome themes.
67 */
68 #define PANEL_TOPLEVEL_TYPE (panel_toplevel_get_type())
69
70 typedef struct _PanelToplevel PanelToplevel;
71 typedef struct _PanelToplevelClass PanelToplevelClass;
72 struct _PanelToplevel
73 {
74 GtkWindow parent;
75 };
76 struct _PanelToplevelClass
77 {
78 GtkWindowClass parent_class;
79 };
80 G_DEFINE_TYPE(PanelToplevel, panel_toplevel, GTK_TYPE_WINDOW);
81 static void panel_toplevel_class_init(PanelToplevelClass *klass)
82 {
83 }
84 static void panel_toplevel_init(PanelToplevel *self)
85 {
86 }
87
88 /* Allocate and initialize new Panel structure. */
89 static Panel* panel_allocate(void)
90 {
91 Panel* p = g_new0(Panel, 1);
92 p->allign = ALLIGN_CENTER;
93 p->edge = EDGE_NONE;
94 p->widthtype = WIDTH_PERCENT;
95 p->width = 100;
96 p->heighttype = HEIGHT_PIXEL;
97 p->height = PANEL_HEIGHT_DEFAULT;
98 p->monitor = 0;
99 p->setdocktype = 1;
100 p->setstrut = 1;
101 p->round_corners = 0;
102 p->autohide = 0;
103 p->visible = TRUE;
104 p->height_when_hidden = 2;
105 p->transparent = 0;
106 p->alpha = 255;
107 gdk_color_parse("white", &p->gtintcolor);
108 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
109 p->usefontcolor = 0;
110 p->fontcolor = 0x00000000;
111 p->usefontsize = 0;
112 p->fontsize = 10;
113 p->spacing = 0;
114 p->icon_size = PANEL_ICON_SIZE;
115 p->icon_theme = gtk_icon_theme_get_default();
116 p->config = config_new();
117 return p;
118 }
119
120 /* Normalize panel configuration after load from file or reconfiguration. */
121 static void panel_normalize_configuration(Panel* p)
122 {
123 panel_set_panel_configuration_changed( p );
124 if (p->width < 0)
125 p->width = 100;
126 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
127 p->width = 100;
128 p->heighttype = HEIGHT_PIXEL;
129 if (p->heighttype == HEIGHT_PIXEL) {
130 if (p->height < PANEL_HEIGHT_MIN)
131 p->height = PANEL_HEIGHT_MIN;
132 else if (p->height > PANEL_HEIGHT_MAX)
133 p->height = PANEL_HEIGHT_MAX;
134 }
135 if (p->monitor < 0)
136 p->monitor = 0;
137 if (p->background)
138 p->transparent = 0;
139 }
140
141 /****************************************************
142 * panel's handlers for WM events *
143 ****************************************************/
144
145 void panel_set_wm_strut(Panel *p)
146 {
147 int index;
148 gulong strut_size;
149 gulong strut_lower;
150 gulong strut_upper;
151
152 /* Dispatch on edge to set up strut parameters. */
153 switch (p->edge)
154 {
155 case EDGE_LEFT:
156 index = 0;
157 strut_size = p->aw;
158 strut_lower = p->ay;
159 strut_upper = p->ay + p->ah;
160 break;
161 case EDGE_RIGHT:
162 index = 1;
163 strut_size = p->aw;
164 strut_lower = p->ay;
165 strut_upper = p->ay + p->ah;
166 break;
167 case EDGE_TOP:
168 index = 2;
169 strut_size = p->ah;
170 strut_lower = p->ax;
171 strut_upper = p->ax + p->aw;
172 break;
173 case EDGE_BOTTOM:
174 index = 3;
175 strut_size = p->ah;
176 strut_lower = p->ax;
177 strut_upper = p->ax + p->aw;
178 break;
179 default:
180 return;
181 }
182
183 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
184 if (p->autohide)
185 strut_size = p->height_when_hidden;
186
187 /* Set up strut value in property format. */
188 gulong desired_strut[12];
189 memset(desired_strut, 0, sizeof(desired_strut));
190 if (p->setstrut)
191 {
192 desired_strut[index] = strut_size;
193 desired_strut[4 + index * 2] = strut_lower;
194 desired_strut[5 + index * 2] = strut_upper;
195 }
196 else
197 {
198 strut_size = 0;
199 strut_lower = 0;
200 strut_upper = 0;
201 }
202
203 /* If strut value changed, set the property value on the panel window.
204 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
205 if ((GTK_WIDGET_MAPPED(p->topgwin))
206 && ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge)))
207 {
208 p->strut_size = strut_size;
209 p->strut_lower = strut_lower;
210 p->strut_upper = strut_upper;
211 p->strut_edge = p->edge;
212
213 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
214 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
215 if (strut_size != 0)
216 {
217 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
218 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
219 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
220 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
221 }
222 else
223 {
224 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
225 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
226 }
227 }
228 }
229
230 /* defined in configurator.c */
231 void panel_configure(Panel* p, int sel_page );
232 gboolean panel_edge_available(Panel* p, int edge, gint monitor);
233
234 /* built-in commands, defined in configurator.c */
235 void restart(void);
236 void gtk_run(void);
237 void panel_destroy(Panel *p);
238
239 static void process_client_msg ( XClientMessageEvent* ev )
240 {
241 int cmd = ev->data.b[0];
242 switch( cmd )
243 {
244 #ifndef DISABLE_MENU
245 case LXPANEL_CMD_SYS_MENU:
246 {
247 GSList* l;
248 for( l = all_panels; l; l = l->next )
249 {
250 Panel* p = (Panel*)l->data;
251 GList *plugins, *pl;
252
253 plugins = gtk_container_get_children(GTK_CONTAINER(p->box));
254 for (pl = plugins; pl; pl = pl->next)
255 {
256 LXPanelPluginInit *init = PLUGIN_CLASS(pl->data);
257 if (init->show_system_menu)
258 /* queue to show system menu */
259 init->show_system_menu(pl->data);
260 }
261 g_list_free(plugins);
262 }
263 break;
264 }
265 #endif
266 #ifndef DISABLE_MENU
267 case LXPANEL_CMD_RUN:
268 gtk_run();
269 break;
270 #endif
271 case LXPANEL_CMD_CONFIG:
272 {
273 Panel * p = ((all_panels != NULL) ? all_panels->data : NULL);
274 if (p != NULL)
275 panel_configure(p, 0);
276 }
277 break;
278 case LXPANEL_CMD_RESTART:
279 restart();
280 break;
281 case LXPANEL_CMD_EXIT:
282 gtk_main_quit();
283 break;
284 }
285 }
286
287 static GdkFilterReturn
288 panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
289 {
290 Atom at;
291 Window win;
292 XEvent *ev = (XEvent *) xevent;
293
294 ENTER;
295 DBG("win = 0x%x\n", ev->xproperty.window);
296 if (ev->type != PropertyNotify )
297 {
298 /* private client message from lxpanelctl */
299 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
300 {
301 process_client_msg( (XClientMessageEvent*)ev );
302 }
303 else if( ev->type == DestroyNotify )
304 {
305 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
306 }
307 RET(GDK_FILTER_CONTINUE);
308 }
309
310 at = ev->xproperty.atom;
311 win = ev->xproperty.window;
312 if (win == GDK_ROOT_WINDOW())
313 {
314 if (at == a_NET_CLIENT_LIST)
315 {
316 fb_ev_emit(fbev, EV_CLIENT_LIST);
317 }
318 else if (at == a_NET_CURRENT_DESKTOP)
319 {
320 GSList* l;
321 for( l = all_panels; l; l = l->next )
322 ((Panel*)l->data)->curdesk = get_net_current_desktop();
323 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
324 }
325 else if (at == a_NET_NUMBER_OF_DESKTOPS)
326 {
327 GSList* l;
328 for( l = all_panels; l; l = l->next )
329 ((Panel*)l->data)->desknum = get_net_number_of_desktops();
330 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
331 }
332 else if (at == a_NET_DESKTOP_NAMES)
333 {
334 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
335 }
336 else if (at == a_NET_ACTIVE_WINDOW)
337 {
338 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
339 }
340 else if (at == a_NET_CLIENT_LIST_STACKING)
341 {
342 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
343 }
344 else if (at == a_XROOTPMAP_ID)
345 {
346 GSList* l;
347 for( l = all_panels; l; l = l->next )
348 {
349 Panel* p = (Panel*)l->data;
350 if (p->transparent) {
351 fb_bg_notify_changed_bg(p->bg);
352 }
353 }
354 }
355 else if (at == a_NET_WORKAREA)
356 {
357 GSList* l;
358 for( l = all_panels; l; l = l->next )
359 {
360 Panel* p = (Panel*)l->data;
361 g_free( p->workarea );
362 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
363 /* print_wmdata(p); */
364 }
365 }
366 else
367 return GDK_FILTER_CONTINUE;
368
369 return GDK_FILTER_REMOVE;
370 }
371 return GDK_FILTER_CONTINUE;
372 }
373
374 /****************************************************
375 * panel's handlers for GTK events *
376 ****************************************************/
377
378
379 static gint
380 panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
381 {
382 ENTER;
383 RET(FALSE);
384 }
385
386 static gint
387 panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
388 {
389 //Panel *p = (Panel *) data;
390 //if (!p->self_destroy)
391 gtk_main_quit();
392 RET(FALSE);
393 }
394
395 static void
396 on_root_bg_changed(FbBg *bg, Panel* p)
397 {
398 panel_update_background( p );
399 }
400
401 void panel_determine_background_pixmap(Panel * p, GtkWidget * widget, GdkWindow * window)
402 {
403 GdkPixmap * pixmap = NULL;
404
405 /* Free p->bg if it is not going to be used. */
406 if (( ! p->transparent) && (p->bg != NULL))
407 {
408 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
409 g_object_unref(p->bg);
410 p->bg = NULL;
411 }
412
413 if (p->background)
414 {
415 /* User specified background pixmap. */
416 if (p->background_file != NULL)
417 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
418 }
419
420 else if (p->transparent)
421 {
422 /* Transparent. Determine the appropriate value from the root pixmap. */
423 if (p->bg == NULL)
424 {
425 p->bg = fb_bg_get_for_display();
426 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), p);
427 }
428 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
429 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
430 fb_bg_composite(pixmap, &p->gtintcolor, p->alpha);
431 }
432
433 if (pixmap != NULL)
434 {
435 gtk_widget_set_app_paintable(widget, TRUE );
436 gdk_window_set_back_pixmap(window, pixmap, FALSE);
437 g_object_unref(pixmap);
438 }
439 else
440 gtk_widget_set_app_paintable(widget, FALSE);
441 }
442
443 /* Update the background of the entire panel.
444 * This function should only be called after the panel has been realized. */
445 void panel_update_background(Panel * p)
446 {
447 GList *plugins, *l;
448
449 /* Redraw the top level widget. */
450 panel_determine_background_pixmap(p, p->topgwin, p->topgwin->window);
451 gdk_window_clear(p->topgwin->window);
452 gtk_widget_queue_draw(p->topgwin);
453
454 /* Loop over all plugins redrawing each plugin. */
455 plugins = gtk_container_get_children(GTK_CONTAINER(p->box));
456 for (l = plugins; l != NULL; l = l->next)
457 plugin_widget_set_background(l->data, p);
458 g_list_free(plugins);
459 }
460
461 static gboolean delay_update_background( Panel* p )
462 {
463 /* Panel could be destroyed while background update scheduled */
464 if ( p->topgwin && GTK_WIDGET_REALIZED ( p->topgwin ) ) {
465 gdk_display_sync( gtk_widget_get_display(p->topgwin) );
466 panel_update_background( p );
467 }
468
469 return FALSE;
470 }
471
472 static void
473 panel_realize(GtkWidget *widget, Panel *p)
474 {
475 g_idle_add_full( G_PRIORITY_LOW,
476 (GSourceFunc)delay_update_background, p, NULL );
477 }
478
479 static void
480 panel_style_set(GtkWidget *widget, GtkStyle* prev, Panel *p)
481 {
482 /* FIXME: This dirty hack is used to fix the background of systray... */
483 if( GTK_WIDGET_REALIZED( widget ) )
484 g_idle_add_full( G_PRIORITY_LOW,
485 (GSourceFunc)delay_update_background, p, NULL );
486 }
487
488 static gint
489 panel_size_req(GtkWidget *widget, GtkRequisition *req, Panel *p)
490 {
491 ENTER;
492
493 if (p->autohide && !p->visible)
494 /* When the panel is in invisible state, the content box also got hidden, thus always
495 * report 0 size. Ask the content box instead for its size. */
496 gtk_widget_size_request(p->box, req);
497
498 if (p->widthtype == WIDTH_REQUEST)
499 p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? req->width : req->height;
500 if (p->heighttype == HEIGHT_REQUEST)
501 p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? req->height : req->width;
502
503 calculate_position(p);
504
505 gtk_widget_set_size_request( widget, p->aw, p->ah );
506
507 RET( TRUE );
508 }
509
510 static gint
511 panel_size_alloc(GtkWidget *widget, GtkAllocation *a, Panel *p)
512 {
513 ENTER;
514 if (p->widthtype == WIDTH_REQUEST)
515 p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? a->width : a->height;
516 if (p->heighttype == HEIGHT_REQUEST)
517 p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? a->height : a->width;
518 calculate_position(p);
519
520 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
521 RET(TRUE);
522 }
523
524 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
525 panel_set_wm_strut(p);
526 RET(TRUE);
527 }
528
529 static gboolean
530 panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, Panel *p)
531 {
532 ENTER;
533 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
534 RET(TRUE);
535 p->cw = e->width;
536 p->ch = e->height;
537 p->cx = e->x;
538 p->cy = e->y;
539
540 if (p->transparent)
541 fb_bg_notify_changed_bg(p->bg);
542
543 RET(FALSE);
544 }
545
546 static gint
547 panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
548 {
549 panel_configure( (Panel*)user_data, 0 );
550 return TRUE;
551 }
552
553 /* Handler for "button_press_event" signal with Panel as parameter. */
554 static gboolean panel_button_press_event_with_panel(GtkWidget *widget, GdkEventButton *event, Panel *panel)
555 {
556 if (event->button == 3) /* right button */
557 {
558 GtkMenu* popup = (GtkMenu*) lxpanel_get_plugin_menu(panel, NULL, FALSE);
559 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
560 return TRUE;
561 }
562 return FALSE;
563 }
564
565 static void panel_popupmenu_config_plugin( GtkMenuItem* item, GtkWidget* plugin )
566 {
567 Panel *panel = PLUGIN_PANEL(plugin);
568
569 lxpanel_plugin_show_config_dialog(panel, plugin);
570
571 /* FIXME: this should be more elegant */
572 panel->config_changed = TRUE;
573 }
574
575 static void panel_popupmenu_add_item( GtkMenuItem* item, Panel* panel )
576 {
577 /* panel_add_plugin( panel, panel->topgwin ); */
578 panel_configure( panel, 2 );
579 }
580
581 static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
582 {
583 Panel* panel = PLUGIN_PANEL(plugin);
584
585 /* If the configuration dialog is open, there will certainly be a crash if the
586 * user manipulates the Configured Plugins list, after we remove this entry.
587 * Close the configuration dialog if it is open. */
588 if (panel->pref_dialog != NULL)
589 {
590 gtk_widget_destroy(panel->pref_dialog);
591 panel->pref_dialog = NULL;
592 }
593 config_setting_destroy(g_object_get_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf));
594 /* reset conf pointer because the widget still may be referenced by configurator */
595 g_object_set_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf, NULL);
596
597 panel_config_save(panel);
598 gtk_widget_destroy(plugin);
599 }
600
601 /* FIXME: Potentially we can support multiple panels at the same edge,
602 * but currently this cannot be done due to some positioning problems. */
603 static char* gen_panel_name( int edge, gint monitor )
604 {
605 const char* edge_str = num2str( edge_pair, edge, "" );
606 char* name = NULL;
607 char* dir = _user_config_file_name("panels", NULL);
608 int i;
609 for( i = 0; i < G_MAXINT; ++i )
610 {
611 char* f;
612 if(monitor != 0)
613 name = g_strdup_printf( "%s-m%d-%d", edge_str, monitor, i );
614 else if( G_LIKELY( i > 0 ) )
615 name = g_strdup_printf( "%s%d", edge_str, i );
616 else
617 name = g_strdup( edge_str );
618
619 f = g_build_filename( dir, name, NULL );
620 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
621 {
622 g_free( f );
623 break;
624 }
625 g_free( name );
626 g_free( f );
627 }
628 g_free( dir );
629 return name;
630 }
631
632 /* FIXME: Potentially we can support multiple panels at the same edge,
633 * but currently this cannot be done due to some positioning problems. */
634 static void panel_popupmenu_create_panel( GtkMenuItem* item, Panel* panel )
635 {
636 gint m, e, monitors;
637 GdkScreen *screen;
638 GtkWidget *err;
639 Panel* new_panel = panel_allocate();
640
641 /* Allocate the edge. */
642 screen = gdk_screen_get_default();
643 g_assert(screen);
644 monitors = gdk_screen_get_n_monitors(screen);
645 for(m=0; m<monitors; ++m)
646 {
647 /* try each of the four edges */
648 for(e=1; e<5; ++e)
649 {
650 if(panel_edge_available(new_panel,e,m)) {
651 new_panel->edge = e;
652 new_panel->monitor = m;
653 goto found_edge;
654 }
655 }
656 }
657
658 panel_destroy(new_panel);
659 ERR("Error adding panel: There is no room for another panel. All the edges are taken.\n");
660 err = gtk_message_dialog_new(NULL,0,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,_("There is no room for another panel. All the edges are taken."));
661 gtk_dialog_run(GTK_DIALOG(err));
662 gtk_widget_destroy(err);
663 return;
664
665 found_edge:
666 new_panel->name = gen_panel_name(new_panel->edge,new_panel->monitor);
667
668 /* create new config with first group "Global" */
669 config_group_add_subgroup(config_root_setting(new_panel->config), "Global");
670 panel_configure(new_panel, 0);
671 panel_normalize_configuration(new_panel);
672 panel_start_gui(new_panel);
673 gtk_widget_show_all(new_panel->topgwin);
674
675 panel_config_save(new_panel);
676 all_panels = g_slist_prepend(all_panels, new_panel);
677 }
678
679 static void panel_popupmenu_delete_panel( GtkMenuItem* item, Panel* panel )
680 {
681 GtkWidget* dlg;
682 gboolean ok;
683 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel->topgwin),
684 GTK_DIALOG_MODAL,
685 GTK_MESSAGE_QUESTION,
686 GTK_BUTTONS_OK_CANCEL,
687 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
688 panel_apply_icon(GTK_WINDOW(dlg));
689 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
690 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
691 gtk_widget_destroy( dlg );
692 if( ok )
693 {
694 gchar *fname;
695 all_panels = g_slist_remove( all_panels, panel );
696
697 /* delete the config file of this panel */
698 fname = _user_config_file_name("panels", panel->name);
699 g_unlink( fname );
700 g_free(fname);
701 panel->config_changed = 0;
702 panel_destroy( panel );
703 }
704 }
705
706 static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
707 {
708 GtkWidget *about;
709 const gchar* authors[] = {
710 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
711 "Jim Huang <jserv.tw@gmail.com>",
712 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
713 "Fred Chien <cfsghost@gmail.com>",
714 "Daniel Kesler <kesler.daniel@gmail.com>",
715 "Juergen Hoetzel <juergen@archlinux.org>",
716 "Marty Jack <martyj19@comcast.net>",
717 "Martin Bagge <brother@bsnet.se>",
718 NULL
719 };
720 /* TRANSLATORS: Replace this string with your names, one name per line. */
721 gchar *translators = _( "translator-credits" );
722
723 about = gtk_about_dialog_new();
724 panel_apply_icon(GTK_WINDOW(about));
725 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
726 gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
727
728 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
729 {
730 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
731 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
732 }
733 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
734 {
735 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
736 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
737 }
738 else
739 {
740 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
741 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL));
742 }
743
744 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2013"));
745 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
746 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.");
747 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
748 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
749 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
750 gtk_dialog_run(GTK_DIALOG(about));
751 gtk_widget_destroy(about);
752 }
753
754 void panel_apply_icon( GtkWindow *w )
755 {
756 GdkPixbuf* window_icon;
757
758 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
759 {
760 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
761 }
762 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
763 {
764 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
765 }
766 else
767 {
768 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL);
769 }
770 gtk_window_set_icon(w, window_icon);
771 }
772
773 GtkMenu* lxpanel_get_plugin_menu( Panel* panel, GtkWidget* plugin, gboolean use_sub_menu )
774 {
775 GtkWidget *menu_item, *img;
776 GtkMenu *ret,*menu;
777 LXPanelPluginInit *init;
778 char* tmp;
779 ret = menu = GTK_MENU(gtk_menu_new());
780
781 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
782 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
783 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
784 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
785 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
786
787 if( plugin )
788 {
789 init = PLUGIN_CLASS(plugin);
790 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
791 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(init->name) );
792 menu_item = gtk_image_menu_item_new_with_label( tmp );
793 g_free( tmp );
794 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
795 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
796 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
797 }
798
799 menu_item = gtk_separator_menu_item_new();
800 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
801
802 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
803 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
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(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
807
808 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
809 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
810 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
811 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
812 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
813
814 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
815 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
816 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
817 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
818 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
819 if( ! all_panels->next ) /* if this is the only panel */
820 gtk_widget_set_sensitive( menu_item, FALSE );
821
822 menu_item = gtk_separator_menu_item_new();
823 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
824
825 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
826 menu_item = gtk_image_menu_item_new_with_label(_("About"));
827 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
828 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
829 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel );
830
831 if( use_sub_menu )
832 {
833 ret = GTK_MENU(gtk_menu_new());
834 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
835 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
836 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
837
838 gtk_widget_show_all(GTK_WIDGET(ret));
839 }
840
841 if( plugin )
842 {
843 menu_item = gtk_separator_menu_item_new();
844 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
845
846 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
847 tmp = g_strdup_printf( _("\"%s\" Settings"), _(init->name) );
848 menu_item = gtk_image_menu_item_new_with_label( tmp );
849 g_free( tmp );
850 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
851 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
852 if( init->config )
853 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
854 else
855 gtk_widget_set_sensitive( menu_item, FALSE );
856 }
857
858 gtk_widget_show_all(GTK_WIDGET(menu));
859
860 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
861 return ret;
862 }
863
864 /* for old plugins compatibility */
865 GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
866 {
867 return lxpanel_get_plugin_menu(panel, plugin->pwid, use_sub_menu);
868 }
869
870 /****************************************************
871 * panel creation *
872 ****************************************************/
873
874 static void
875 make_round_corners(Panel *p)
876 {
877 /* FIXME: This should be re-written with shape extension of X11 */
878 /* gdk_window_shape_combine_mask() can be used */
879 }
880
881 void panel_set_dock_type(Panel *p)
882 {
883 if (p->setdocktype) {
884 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
885 XChangeProperty(GDK_DISPLAY(), p->topxwin,
886 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
887 PropModeReplace, (unsigned char *) &state, 1);
888 }
889 else {
890 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
891 }
892 }
893
894 static void panel_set_visibility(Panel *p, gboolean visible)
895 {
896 if ( ! visible) gtk_widget_hide(p->box);
897 p->visible = visible;
898 calculate_position(p);
899 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
900 gdk_window_move(p->topgwin->window, p->ax, p->ay);
901 if (visible) gtk_widget_show(p->box);
902 panel_set_wm_strut(p);
903 }
904
905 static gboolean panel_leave_real(Panel *p)
906 {
907 /* If the pointer is grabbed by this application, leave the panel displayed.
908 * There is no way to determine if it is grabbed by another application, such as an application that has a systray icon. */
909 if (gdk_display_pointer_is_grabbed(p->display))
910 return TRUE;
911
912 /* If the pointer is inside the panel, leave the panel displayed. */
913 gint x, y;
914 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
915 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
916 return TRUE;
917
918 /* If the panel is configured to autohide and if it is visible, hide the panel. */
919 if ((p->autohide) && (p->visible))
920 panel_set_visibility(p, FALSE);
921
922 /* Clear the timer. */
923 p->hide_timeout = 0;
924 return FALSE;
925 }
926
927 static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
928 {
929 /* We may receive multiple enter-notify events when the pointer crosses into the panel.
930 * Do extra tests to make sure this does not cause misoperation such as blinking.
931 * If the pointer is inside the panel, unhide it. */
932 gint x, y;
933 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
934 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
935 {
936 /* If the pointer is inside the panel and we have not already unhidden it, do so and
937 * set a timer to recheck it in a half second. */
938 if (p->hide_timeout == 0)
939 {
940 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
941 panel_set_visibility(p, TRUE);
942 }
943 }
944 else
945 {
946 /* If the pointer is not inside the panel, simulate a timer expiration. */
947 panel_leave_real(p);
948 }
949 return TRUE;
950 }
951
952 static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
953 gint y, guint time, Panel *p)
954 {
955 panel_enter(NULL, NULL, p);
956 return TRUE;
957 }
958
959 void panel_establish_autohide(Panel *p)
960 {
961 if (p->autohide)
962 {
963 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
964 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
965 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
966 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
967 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
968 panel_enter(NULL, NULL, p);
969 }
970 else if ( ! p->visible)
971 {
972 gtk_widget_show(p->box);
973 p->visible = TRUE;
974 }
975 }
976
977 /* Set an image from a file with scaling to the panel icon size. */
978 void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
979 {
980 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
981 if (pixbuf != NULL)
982 {
983 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
984 g_object_unref(pixbuf);
985 }
986 }
987
988 /* Set an image from a icon theme with scaling to the panel icon size. */
989 gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
990 {
991 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
992 {
993 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
994 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
995 g_object_unref(pixbuf);
996 return TRUE;
997 }
998 return FALSE;
999 }
1000
1001 static void
1002 panel_start_gui(Panel *p)
1003 {
1004 Atom state[3];
1005 XWMHints wmhints;
1006 guint32 val;
1007
1008 ENTER;
1009
1010 p->curdesk = get_net_current_desktop();
1011 p->desknum = get_net_number_of_desktops();
1012 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1013
1014 /* main toplevel window */
1015 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
1016 p->topgwin = (GtkWidget*)g_object_new(PANEL_TOPLEVEL_TYPE, NULL);
1017 gtk_widget_set_name(p->topgwin, "PanelToplevel");
1018 p->display = gdk_display_get_default();
1019 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
1020 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
1021 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
1022 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
1023 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
1024 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
1025
1026 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
1027
1028 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
1029 G_CALLBACK(panel_delete_event), p);
1030 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
1031 G_CALLBACK(panel_destroy_event), p);
1032 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
1033 (GCallback) panel_size_req, p);
1034 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
1035 (GCallback) panel_size_alloc, p);
1036 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
1037 (GCallback) panel_configure_event, p);
1038
1039 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
1040 g_signal_connect(G_OBJECT (p->topgwin), "button-press-event",
1041 (GCallback) panel_button_press_event_with_panel, p);
1042
1043 g_signal_connect (G_OBJECT (p->topgwin), "realize",
1044 (GCallback) panel_realize, p);
1045
1046 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
1047 (GCallback)panel_style_set, p);
1048 gtk_widget_realize(p->topgwin);
1049 //gdk_window_set_decorations(p->topgwin->window, 0);
1050
1051 // main layout manager as a single child of panel
1052 p->box = p->my_box_new(FALSE, 0);
1053 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
1054 // gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
1055 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
1056 gtk_widget_show(p->box);
1057 if (p->round_corners)
1058 make_round_corners(p);
1059
1060 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
1061 DBG("topxwin = %x\n", p->topxwin);
1062
1063 /* the settings that should be done before window is mapped */
1064 wmhints.flags = InputHint;
1065 wmhints.input = 0;
1066 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
1067 #define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
1068 val = WIN_HINTS_SKIP_FOCUS;
1069 XChangeProperty(GDK_DISPLAY(), p->topxwin,
1070 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
1071 PropModeReplace, (unsigned char *) &val, 1);
1072
1073 panel_set_dock_type(p);
1074
1075 /* window mapping point */
1076 gtk_widget_show_all(p->topgwin);
1077
1078 /* the settings that should be done after window is mapped */
1079 panel_establish_autohide(p);
1080
1081 /* send it to running wm */
1082 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
1083 /* and assign it ourself just for case when wm is not running */
1084 val = 0xFFFFFFFF;
1085 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
1086 PropModeReplace, (unsigned char *) &val, 1);
1087
1088 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1089 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1090 state[2] = a_NET_WM_STATE_STICKY;
1091 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
1092 32, PropModeReplace, (unsigned char *) state, 3);
1093
1094 calculate_position(p);
1095 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
1096 panel_set_wm_strut(p);
1097
1098 RET();
1099 }
1100
1101 /* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1102 void panel_adjust_geometry_terminology(Panel * p)
1103 {
1104 if ((p->height_label != NULL) && (p->width_label != NULL)
1105 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
1106 {
1107 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1108 {
1109 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1110 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
1111 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1112 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
1113 }
1114 else
1115 {
1116 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1117 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
1118 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1119 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
1120 }
1121 }
1122 }
1123
1124 /* Draw text into a label, with the user preference color and optionally bold. */
1125 void panel_draw_label_text(Panel * p, GtkWidget * label, const char * text,
1126 gboolean bold, float custom_size_factor,
1127 gboolean custom_color)
1128 {
1129 if (text == NULL)
1130 {
1131 /* Null string. */
1132 gtk_label_set_text(GTK_LABEL(label), NULL);
1133 return;
1134 }
1135
1136 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1137 int font_desc;
1138 if (p->usefontsize)
1139 font_desc = p->fontsize;
1140 else
1141 {
1142 if (p->icon_size < 20)
1143 font_desc = 9;
1144 else if (p->icon_size >= 20 && p->icon_size < 36)
1145 font_desc = 10;
1146 else
1147 font_desc = 12;
1148 }
1149 font_desc *= custom_size_factor;
1150
1151 /* Check the string for characters that need to be escaped.
1152 * If any are found, create the properly escaped string and use it instead. */
1153 const char * valid_markup = text;
1154 char * escaped_text = NULL;
1155 const char * q;
1156 for (q = text; *q != '\0'; q += 1)
1157 {
1158 if ((*q == '<') || (*q == '>') || (*q == '&'))
1159 {
1160 escaped_text = g_markup_escape_text(text, -1);
1161 valid_markup = escaped_text;
1162 break;
1163 }
1164 }
1165
1166 gchar * formatted_text;
1167 if ((custom_color) && (p->usefontcolor))
1168 {
1169 /* Color, optionally bold. */
1170 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
1171 font_desc,
1172 gcolor2rgb24(&p->gfontcolor),
1173 ((bold) ? "<b>" : ""),
1174 valid_markup,
1175 ((bold) ? "</b>" : ""));
1176 }
1177 else
1178 {
1179 /* No color, optionally bold. */
1180 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
1181 font_desc,
1182 ((bold) ? "<b>" : ""),
1183 valid_markup,
1184 ((bold) ? "</b>" : ""));
1185 }
1186
1187 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1188 g_free(formatted_text);
1189 g_free(escaped_text);
1190 }
1191
1192 void panel_set_panel_configuration_changed(Panel *p)
1193 {
1194 GList *plugins, *l;
1195
1196 GtkOrientation previous_orientation = p->orientation;
1197 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1198 ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1199
1200 /* either first run or orientation was changed */
1201 if (p->my_box_new == NULL || previous_orientation != p->orientation)
1202 {
1203 panel_adjust_geometry_terminology(p);
1204 if (p->my_box_new != NULL)
1205 p->height = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1206 if (p->height_control != NULL)
1207 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
1208 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1209 {
1210 int value = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? gdk_screen_width() : gdk_screen_height());
1211 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1212 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1213 }
1214 }
1215
1216 if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
1217 p->my_box_new = gtk_hbox_new;
1218 p->my_separator_new = gtk_vseparator_new;
1219 } else {
1220 p->my_box_new = gtk_vbox_new;
1221 p->my_separator_new = gtk_hseparator_new;
1222 }
1223
1224 /* recreate the main layout box */
1225 if (p->box != NULL)
1226 {
1227 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), p->orientation);
1228 }
1229
1230 /* NOTE: This loop won't be executed when panel started since
1231 plugins are not loaded at that time.
1232 This is used when the orientation of the panel is changed
1233 from the config dialog, and plugins should be re-layout.
1234 */
1235 plugins = p->box ? gtk_container_get_children(GTK_CONTAINER(p->box)) : NULL;
1236 for( l = plugins; l; l = l->next ) {
1237 GtkWidget *w = (GtkWidget*)l->data;
1238 LXPanelPluginInit *init = PLUGIN_CLASS(w);
1239 if (init->reconfigure)
1240 init->reconfigure(p, w);
1241 }
1242 g_list_free(plugins);
1243 }
1244
1245 static int
1246 panel_parse_global(Panel *p, config_setting_t *cfg)
1247 {
1248 const char *str;
1249 gint i;
1250
1251 /* check Global config */
1252 if (!cfg || strcmp(config_setting_get_name(cfg), "Global") != 0)
1253 {
1254 ERR( "lxpanel: Global section not found\n");
1255 RET(0);
1256 }
1257 if (config_setting_lookup_string(cfg, "edge", &str))
1258 p->edge = str2num(edge_pair, str, EDGE_NONE);
1259 if (config_setting_lookup_string(cfg, "allign", &str))
1260 p->allign = str2num(allign_pair, str, ALLIGN_NONE);
1261 config_setting_lookup_int(cfg, "monitor", &p->monitor);
1262 config_setting_lookup_int(cfg, "margin", &p->margin);
1263 if (config_setting_lookup_string(cfg, "widthtype", &str))
1264 p->widthtype = str2num(width_pair, str, WIDTH_NONE);
1265 config_setting_lookup_int(cfg, "width", &p->width);
1266 if (config_setting_lookup_string(cfg, "heighttype", &str))
1267 p->heighttype = str2num(height_pair, str, HEIGHT_NONE);
1268 config_setting_lookup_int(cfg, "height", &p->height);
1269 if (config_setting_lookup_int(cfg, "spacing", &i) && i > 0)
1270 p->spacing = i;
1271 if (config_setting_lookup_int(cfg, "setdocktype", &i))
1272 p->setdocktype = i != 0;
1273 if (config_setting_lookup_int(cfg, "setpartialstrut", &i))
1274 p->setstrut = i != 0;
1275 if (config_setting_lookup_int(cfg, "RoundCorners", &i))
1276 p->round_corners = i != 0;
1277 if (config_setting_lookup_int(cfg, "transparent", &i))
1278 p->transparent = i != 0;
1279 if (config_setting_lookup_int(cfg, "alpha", &p->alpha))
1280 {
1281 if (p->alpha > 255)
1282 p->alpha = 255;
1283 }
1284 if (config_setting_lookup_int(cfg, "autohide", &i))
1285 p->autohide = i != 0;
1286 config_setting_lookup_int(cfg, "heightwhenhidden", &p->height_when_hidden);
1287 if (config_setting_lookup_string(cfg, "tintcolor", &str))
1288 {
1289 if (!gdk_color_parse (str, &p->gtintcolor))
1290 gdk_color_parse ("white", &p->gtintcolor);
1291 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1292 DBG("tintcolor=%x\n", p->tintcolor);
1293 }
1294 if (config_setting_lookup_int(cfg, "usefontcolor", &i))
1295 p->usefontcolor = i != 0;
1296 if (config_setting_lookup_string(cfg, "fontcolor", &str))
1297 {
1298 if (!gdk_color_parse (str, &p->gfontcolor))
1299 gdk_color_parse ("black", &p->gfontcolor);
1300 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1301 DBG("fontcolor=%x\n", p->fontcolor);
1302 }
1303 if (config_setting_lookup_int(cfg, "usefontsize", &i))
1304 p->usefontsize = i != 0;
1305 if (config_setting_lookup_int(cfg, "fontsize", &i) && i > 0)
1306 p->fontsize = i;
1307 if (config_setting_lookup_int(cfg, "background", &i))
1308 p->background = i != 0;
1309 if (config_setting_lookup_string(cfg, "backgroundfile", &str))
1310 p->background_file = g_strdup(str);
1311 config_setting_lookup_int(cfg, "iconsize", &p->icon_size);
1312 if (config_setting_lookup_int(cfg, "loglevel", &configured_log_level))
1313 {
1314 if (!log_level_set_on_commandline)
1315 log_level = configured_log_level;
1316 }
1317
1318 panel_normalize_configuration(p);
1319
1320 return 1;
1321 }
1322
1323 static int
1324 panel_parse_plugin(Panel *p, config_setting_t *cfg)
1325 {
1326 const char *type = NULL;
1327
1328 ENTER;
1329 config_setting_lookup_string(cfg, "type", &type);
1330 DBG("plug %s\n", type);
1331
1332 if (!type || lxpanel_add_plugin(p, type, cfg, -1) == NULL) {
1333 ERR( "lxpanel: can't load %s plugin\n", type);
1334 goto error;
1335 }
1336 RET(1);
1337
1338 error:
1339 RET(0);
1340 }
1341
1342 int panel_start( Panel *p )
1343 {
1344 config_setting_t *list, *s;
1345 int i;
1346
1347 /* parse global section */
1348 ENTER;
1349
1350 list = config_setting_get_member(config_root_setting(p->config), "");
1351 if (!list || !panel_parse_global(p, config_setting_get_elem(list, 0)))
1352 RET(0);
1353
1354 panel_start_gui(p);
1355
1356 for (i = 1; (s = config_setting_get_elem(list, i)) != NULL; )
1357 if (strcmp(config_setting_get_name(s), "Plugin") == 0 &&
1358 panel_parse_plugin(p, s)) /* success on plugin start */
1359 i++;
1360 else /* remove invalid data from config */
1361 config_setting_remove_elem(list, i);
1362
1363 /* update backgrond of panel and all plugins */
1364 panel_update_background( p );
1365 return 1;
1366 }
1367
1368 void panel_destroy(Panel *p)
1369 {
1370 ENTER;
1371
1372 if (p->pref_dialog != NULL)
1373 gtk_widget_destroy(p->pref_dialog);
1374 if (p->plugin_pref_dialog != NULL)
1375 /* just close the dialog, it will do all required cleanup */
1376 gtk_dialog_response(GTK_DIALOG(p->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
1377
1378 if (p->bg != NULL)
1379 {
1380 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1381 g_object_unref(p->bg);
1382 }
1383
1384 if( p->config_changed )
1385 panel_config_save( p );
1386 config_destroy(p->config);
1387
1388 if( p->topgwin )
1389 {
1390 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
1391 gtk_widget_destroy(p->topgwin);
1392 }
1393 g_free(p->workarea);
1394 g_free( p->background_file );
1395 g_slist_free( p->system_menus );
1396 gdk_flush();
1397 XFlush(GDK_DISPLAY());
1398 XSync(GDK_DISPLAY(), True);
1399
1400 g_free( p->name );
1401 g_free(p);
1402 RET();
1403 }
1404
1405 Panel* panel_new( const char* config_file, const char* config_name )
1406 {
1407 Panel* panel = NULL;
1408
1409 if (G_LIKELY(config_file))
1410 {
1411 panel = panel_allocate();
1412 panel->name = g_strdup(config_name);
1413 g_debug("starting panel from file %s",config_file);
1414 if (!config_read_file(panel->config, config_file) ||
1415 !panel_start(panel))
1416 {
1417 ERR( "lxpanel: can't start panel\n");
1418 panel_destroy( panel );
1419 panel = NULL;
1420 }
1421 }
1422 return panel;
1423 }
1424
1425 static void
1426 usage()
1427 {
1428 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1429 g_print(_("Command line options:\n"));
1430 g_print(_(" --help -- print this help and exit\n"));
1431 g_print(_(" --version -- print version and exit\n"));
1432 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
1433 // g_print(_(" --configure -- launch configuration utility\n"));
1434 g_print(_(" --profile name -- use specified profile\n"));
1435 g_print("\n");
1436 g_print(_(" -h -- same as --help\n"));
1437 g_print(_(" -p -- same as --profile\n"));
1438 g_print(_(" -v -- same as --version\n"));
1439 // g_print(_(" -C -- same as --configure\n"));
1440 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
1441 }
1442
1443 int panel_handle_x_error(Display * d, XErrorEvent * ev)
1444 {
1445 char buf[256];
1446
1447 if (log_level >= LOG_WARN) {
1448 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1449 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1450 }
1451 return 0; /* Ignored */
1452 }
1453
1454 int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1455 {
1456 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1457 panel_handle_x_error(d, ev);
1458 return 0; /* Ignored */
1459 }
1460
1461 /* Lightweight lock related functions - X clipboard hacks */
1462
1463 #define CLIPBOARD_NAME "LXPANEL_SELECTION"
1464
1465 /*
1466 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1467 */
1468 static void
1469 clipboard_get_func(
1470 GtkClipboard *clipboard G_GNUC_UNUSED,
1471 GtkSelectionData *selection_data G_GNUC_UNUSED,
1472 guint info G_GNUC_UNUSED,
1473 gpointer user_data_or_owner G_GNUC_UNUSED)
1474 {
1475 }
1476
1477 /*
1478 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1479 */
1480 static void clipboard_clear_func(
1481 GtkClipboard *clipboard G_GNUC_UNUSED,
1482 gpointer user_data_or_owner G_GNUC_UNUSED)
1483 {
1484 }
1485
1486 /*
1487 * Lightweight version for checking single instance.
1488 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1489 *
1490 * Returns TRUE if successfully retrieved and FALSE otherwise.
1491 */
1492 static gboolean check_main_lock()
1493 {
1494 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1495 gboolean retval = FALSE;
1496 GtkClipboard *clipboard;
1497 Atom atom;
1498
1499 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1500
1501 XGrabServer(GDK_DISPLAY());
1502
1503 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1504 goto out;
1505
1506 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1507
1508 if (gtk_clipboard_set_with_data(clipboard, targets,
1509 G_N_ELEMENTS (targets),
1510 clipboard_get_func,
1511 clipboard_clear_func, NULL))
1512 retval = TRUE;
1513
1514 out:
1515 XUngrabServer (GDK_DISPLAY ());
1516 gdk_flush ();
1517
1518 return retval;
1519 }
1520 #undef CLIPBOARD_NAME
1521
1522 static void _start_panels_from_dir(const char *panel_dir)
1523 {
1524 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1525 const gchar* name;
1526
1527 if( ! dir )
1528 {
1529 return;
1530 }
1531
1532 while((name = g_dir_read_name(dir)) != NULL)
1533 {
1534 char* panel_config = g_build_filename( panel_dir, name, NULL );
1535 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1536 {
1537 Panel* panel = panel_new( panel_config, name );
1538 if( panel )
1539 all_panels = g_slist_prepend( all_panels, panel );
1540 }
1541 g_free( panel_config );
1542 }
1543 g_dir_close( dir );
1544 }
1545
1546 static gboolean start_all_panels( )
1547 {
1548 char *panel_dir;
1549
1550 /* try user panels */
1551 panel_dir = _user_config_file_name("panels", NULL);
1552 _start_panels_from_dir(panel_dir);
1553 g_free(panel_dir);
1554 if (all_panels != NULL)
1555 return TRUE;
1556 /* else try XDG fallback */
1557 panel_dir = _system_config_file_name("panels");
1558 _start_panels_from_dir(panel_dir);
1559 g_free(panel_dir);
1560 if (all_panels != NULL)
1561 return TRUE;
1562 /* last try at old fallback for compatibility reasons */
1563 panel_dir = _old_system_config_file_name("panels");
1564 _start_panels_from_dir(panel_dir);
1565 g_free(panel_dir);
1566 return all_panels != NULL;
1567 }
1568
1569 void load_global_config();
1570 void free_global_config();
1571
1572 static void _ensure_user_config_dirs(void)
1573 {
1574 char *dir = g_build_filename(g_get_user_config_dir(), "lxpanel", cprofile,
1575 "panels", NULL);
1576
1577 /* make sure the private profile and panels dir exists */
1578 g_mkdir_with_parents(dir, 0700);
1579 g_free(dir);
1580 }
1581
1582 int main(int argc, char *argv[], char *env[])
1583 {
1584 int i;
1585 const char* desktop_name;
1586
1587 setlocale(LC_CTYPE, "");
1588
1589 g_thread_init(NULL);
1590 /* gdk_threads_init();
1591 gdk_threads_enter(); */
1592
1593 gtk_init(&argc, &argv);
1594
1595 #ifdef ENABLE_NLS
1596 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1597 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1598 textdomain ( GETTEXT_PACKAGE );
1599 #endif
1600
1601 XSetLocaleModifiers("");
1602 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
1603
1604 resolve_atoms();
1605
1606 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1607 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
1608
1609 for (i = 1; i < argc; i++) {
1610 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1611 usage();
1612 exit(0);
1613 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1614 printf("lxpanel %s\n", version);
1615 exit(0);
1616 } else if (!strcmp(argv[i], "--log")) {
1617 i++;
1618 if (i == argc) {
1619 ERR( "lxpanel: missing log level\n");
1620 usage();
1621 exit(1);
1622 } else {
1623 log_level = atoi(argv[i]);
1624 log_level_set_on_commandline = true;
1625 }
1626 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1627 config = 1;
1628 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1629 i++;
1630 if (i == argc) {
1631 ERR( "lxpanel: missing profile name\n");
1632 usage();
1633 exit(1);
1634 } else {
1635 cprofile = g_strdup(argv[i]);
1636 }
1637 } else {
1638 printf("lxpanel: unknown option - %s\n", argv[i]);
1639 usage();
1640 exit(1);
1641 }
1642 }
1643
1644 /* Check for duplicated lxpanel instances */
1645 if (!check_main_lock() && !config) {
1646 printf("There is already an instance of LXPanel. Now to exit\n");
1647 exit(1);
1648 }
1649
1650 _ensure_user_config_dirs();
1651
1652 /* Add our own icons to the search path of icon theme */
1653 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/images" );
1654
1655 fbev = fb_ev_new();
1656 win_grp = gtk_window_group_new();
1657
1658 restart:
1659 is_restarting = FALSE;
1660
1661 /* init LibFM */
1662 fm_gtk_init(NULL);
1663
1664 /* prepare modules data */
1665 _prepare_modules();
1666
1667 load_global_config();
1668
1669 /* NOTE: StructureNotifyMask is required by XRandR
1670 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1671 */
1672 gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
1673 GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
1674 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1675
1676 if( G_UNLIKELY( ! start_all_panels() ) )
1677 g_warning( "Config files are not found.\n" );
1678 /*
1679 * FIXME: configure??
1680 if (config)
1681 configure();
1682 */
1683 gtk_main();
1684
1685 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1686 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1687
1688 /* destroy all panels */
1689 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
1690 g_slist_free( all_panels );
1691 all_panels = NULL;
1692 g_free( cfgfile );
1693
1694 free_global_config();
1695
1696 _unload_modules();
1697 fm_gtk_finalize();
1698
1699 if( is_restarting )
1700 goto restart;
1701
1702 /* gdk_threads_leave(); */
1703
1704 g_object_unref(win_grp);
1705 g_object_unref(fbev);
1706
1707 return 0;
1708 }
1709
1710 GtkOrientation panel_get_orientation(Panel *panel)
1711 {
1712 return panel->orientation;
1713 }
1714
1715 gint panel_get_icon_size(Panel *panel)
1716 {
1717 return panel->icon_size;
1718 }
1719
1720 gint panel_get_height(Panel *panel)
1721 {
1722 return panel->height;
1723 }
1724
1725 GtkWindow *panel_get_toplevel_window(Panel *panel)
1726 {
1727 return GTK_WINDOW(panel->topgwin);
1728 }
1729
1730 Window panel_get_xwindow(Panel *panel)
1731 {
1732 return panel->topxwin;
1733 }
1734
1735 gint panel_get_monitor(Panel *panel)
1736 {
1737 return panel->monitor;
1738 }
1739
1740 GtkStyle *panel_get_defstyle(Panel *panel)
1741 {
1742 return panel->defstyle;
1743 }
1744
1745 GtkIconTheme *panel_get_icon_theme(Panel *panel)
1746 {
1747 return panel->icon_theme;
1748 }
1749
1750 gboolean panel_is_at_bottom(Panel *panel)
1751 {
1752 return panel->edge == EDGE_BOTTOM;
1753 }
1754
1755 GtkWidget *panel_box_new(Panel *panel, gboolean homogeneous, gint spacing)
1756 {
1757 return panel->my_box_new(homogeneous, spacing);
1758 }
1759
1760 GtkWidget *panel_separator_new(Panel *panel)
1761 {
1762 return panel->my_separator_new();
1763 }
1764
1765 gboolean _class_is_present(LXPanelPluginInit *init)
1766 {
1767 GSList *sl;
1768
1769 for (sl = all_panels; sl; sl = sl->next )
1770 {
1771 Panel *panel = (Panel*)sl->data;
1772 GList *plugins, *p;
1773
1774 plugins = gtk_container_get_children(GTK_CONTAINER(panel->box));
1775 for (p = plugins; p; p = p->next)
1776 if (PLUGIN_CLASS(p->data) == init)
1777 {
1778 g_list_free(plugins);
1779 return TRUE;
1780 }
1781 g_list_free(plugins);
1782 }
1783 return FALSE;
1784 }