Allow -Wl,-z,defs to work - create a liblxpanel.so.0.0.0
[lxde/lxpanel.git] / src / configurator.c
1 /**
2 *
3 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #define __LXPANEL_INTERNALS__
25
26 #include "private.h"
27 #include "misc.h"
28 #include "bg.h"
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <glib/gi18n.h>
35 #include <libfm/fm-gtk.h>
36
37 #include "dbg.h"
38
39 enum{
40 COL_NAME,
41 COL_EXPAND,
42 COL_DATA,
43 N_COLS
44 };
45
46 static void save_global_config();
47
48 static char* logout_cmd = NULL;
49
50 /* macros to update config */
51 #define UPDATE_GLOBAL_INT(panel,name,val) do { \
52 config_setting_t *_s = config_setting_add(config_setting_get_elem(config_setting_get_member(config_root_setting(panel->config),""),\
53 0),\
54 name,PANEL_CONF_TYPE_INT);\
55 if (_s) config_setting_set_int(_s,val); } while(0)
56
57 #define UPDATE_GLOBAL_STRING(panel,name,val) do { \
58 config_setting_t *_s = config_setting_add(config_setting_get_elem(config_setting_get_member(config_root_setting(panel->config),""),\
59 0),\
60 name,PANEL_CONF_TYPE_STRING);\
61 if (_s) config_setting_set_string(_s,val); } while(0)
62
63 #define UPDATE_GLOBAL_COLOR(panel,name,val) do { \
64 config_setting_t *_s = config_setting_add(config_setting_get_elem(config_setting_get_member(config_root_setting(panel->config),""),\
65 0),\
66 name,PANEL_CONF_TYPE_STRING);\
67 if (_s) { \
68 char _c[8];\
69 snprintf(_c, sizeof(_c), "#%06x",val);\
70 config_setting_set_string(_s,_c); } } while(0)
71
72 /* GtkColotButton expects a number between 0 and 65535, but p->alpha has range
73 * 0 to 255, and (2^(2n) - 1) / (2^n - 1) = 2^n + 1 = 257, with n = 8. */
74 static guint16 const alpha_scale_factor = 257;
75
76 void panel_config_save(Panel *p);
77
78 static void update_opt_menu(GtkWidget *w, int ind);
79 static void update_toggle_button(GtkWidget *w, gboolean n);
80 static void modify_plugin( GtkTreeView* view );
81 static gboolean on_entry_focus_out_old( GtkWidget* edit, GdkEventFocus *evt, gpointer user_data );
82 static gboolean on_entry_focus_out( GtkWidget* edit, GdkEventFocus *evt, gpointer user_data );
83 static gboolean _on_entry_focus_out_do_work(GtkWidget* edit, gpointer user_data);
84
85 static void
86 response_event(GtkDialog *widget, gint arg1, Panel* panel )
87 {
88 switch (arg1) {
89 /* FIXME: what will happen if the user exit lxpanel without
90 close this config dialog?
91 Then the config won't be save, I guess. */
92 case GTK_RESPONSE_DELETE_EVENT:
93 case GTK_RESPONSE_CLOSE:
94 case GTK_RESPONSE_NONE:
95 panel_config_save( panel );
96 /* NOTE: NO BREAK HERE*/
97 gtk_widget_destroy(GTK_WIDGET(widget));
98 break;
99 }
100 return;
101 }
102
103 static void
104 update_panel_geometry( LXPanel* p )
105 {
106 /* Guard against being called early in panel creation. */
107 _calculate_position(p);
108 gtk_widget_set_size_request(GTK_WIDGET(p), p->priv->aw, p->priv->ah);
109 gdk_window_move(gtk_widget_get_window(GTK_WIDGET(p)), p->priv->ax, p->priv->ay);
110 _panel_queue_update_background(p);
111 _panel_establish_autohide(p);
112 _panel_set_wm_strut(p);
113 }
114
115 static gboolean edge_selector(Panel* p, int edge)
116 {
117 return (p->edge == edge);
118 }
119
120 /* If there is a panel on this edge and it is not the panel being configured, set the edge unavailable. */
121 gboolean panel_edge_available(Panel* p, int edge, gint monitor)
122 {
123 GSList* l;
124 for (l = all_panels; l != NULL; l = l->next)
125 {
126 LXPanel* pl = (LXPanel*) l->data;
127 if ((pl->priv != p) && (pl->priv->edge == edge) && (pl->priv->monitor == monitor))
128 return FALSE;
129 }
130 return TRUE;
131 }
132
133 static void set_edge(LXPanel* panel, int edge)
134 {
135 Panel *p = panel->priv;
136
137 p->edge = edge;
138 update_panel_geometry(panel);
139 _panel_set_panel_configuration_changed(panel);
140 UPDATE_GLOBAL_STRING(p, "edge", num2str(edge_pair, edge, "none"));
141 }
142
143 static void edge_bottom_toggle(GtkToggleButton *widget, LXPanel *p)
144 {
145 if (gtk_toggle_button_get_active(widget))
146 set_edge(p, EDGE_BOTTOM);
147 }
148
149 static void edge_top_toggle(GtkToggleButton *widget, LXPanel *p)
150 {
151 if (gtk_toggle_button_get_active(widget))
152 set_edge(p, EDGE_TOP);
153 }
154
155 static void edge_left_toggle(GtkToggleButton *widget, LXPanel *p)
156 {
157 if (gtk_toggle_button_get_active(widget))
158 set_edge(p, EDGE_LEFT);
159 }
160
161 static void edge_right_toggle(GtkToggleButton *widget, LXPanel *p)
162 {
163 if (gtk_toggle_button_get_active(widget))
164 set_edge(p, EDGE_RIGHT);
165 }
166
167 static void set_monitor(GtkSpinButton *widget, LXPanel *panel)
168 {
169 Panel *p = panel->priv;
170
171 p->monitor = gtk_spin_button_get_value_as_int(widget) - 1;
172 update_panel_geometry(panel);
173 _panel_set_panel_configuration_changed(panel);
174 UPDATE_GLOBAL_INT(p, "monitor", p->monitor);
175 }
176
177 static void set_alignment(LXPanel* panel, int align)
178 {
179 Panel *p = panel->priv;
180
181 if (p->margin_control)
182 gtk_widget_set_sensitive(p->margin_control, (align != ALLIGN_CENTER));
183 p->allign = align;
184 update_panel_geometry(panel);
185 UPDATE_GLOBAL_STRING(p, "allign", num2str(allign_pair, align, "none"));
186 }
187
188 static void align_left_toggle(GtkToggleButton *widget, LXPanel *p)
189 {
190 if (gtk_toggle_button_get_active(widget))
191 set_alignment(p, ALLIGN_LEFT);
192 }
193
194 static void align_center_toggle(GtkToggleButton *widget, LXPanel *p)
195 {
196 if (gtk_toggle_button_get_active(widget))
197 set_alignment(p, ALLIGN_CENTER);
198 }
199
200 static void align_right_toggle(GtkToggleButton *widget, LXPanel *p)
201 {
202 if (gtk_toggle_button_get_active(widget))
203 set_alignment(p, ALLIGN_RIGHT);
204 }
205
206 static void
207 set_margin(GtkSpinButton* spin, LXPanel* panel)
208 {
209 Panel *p = panel->priv;
210
211 p->margin = (int)gtk_spin_button_get_value(spin);
212 update_panel_geometry(panel);
213 UPDATE_GLOBAL_INT(p, "margin", p->margin);
214 }
215
216 static void
217 set_width(GtkSpinButton* spin, LXPanel* panel)
218 {
219 Panel *p = panel->priv;
220
221 p->width = (int)gtk_spin_button_get_value(spin);
222 update_panel_geometry(panel);
223 UPDATE_GLOBAL_INT(p, "width", p->width);
224 }
225
226 static void
227 set_height(GtkSpinButton* spin, LXPanel* panel)
228 {
229 Panel *p = panel->priv;
230
231 p->height = (int)gtk_spin_button_get_value(spin);
232 update_panel_geometry(panel);
233 UPDATE_GLOBAL_INT(p, "height", p->height);
234 }
235
236 static void set_width_type( GtkWidget *item, LXPanel* panel )
237 {
238 GtkWidget* spin;
239 Panel *p = panel->priv;
240 int widthtype;
241 gboolean t;
242
243 widthtype = gtk_combo_box_get_active(GTK_COMBO_BOX(item)) + 1;
244 if (p->widthtype == widthtype) /* not changed */
245 return;
246
247 p->widthtype = widthtype;
248
249 spin = (GtkWidget*)g_object_get_data(G_OBJECT(item), "width_spin" );
250 t = (widthtype != WIDTH_REQUEST);
251 gtk_widget_set_sensitive( spin, t );
252 switch (widthtype)
253 {
254 case WIDTH_PERCENT:
255 gtk_spin_button_set_range( GTK_SPIN_BUTTON(spin), 0, 100 );
256 gtk_spin_button_set_value( GTK_SPIN_BUTTON(spin), 100 );
257 break;
258 case WIDTH_PIXEL:
259 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
260 {
261 gtk_spin_button_set_range( GTK_SPIN_BUTTON(spin), 0, gdk_screen_width() );
262 gtk_spin_button_set_value( GTK_SPIN_BUTTON(spin), gdk_screen_width() );
263 }
264 else
265 {
266 gtk_spin_button_set_range( GTK_SPIN_BUTTON(spin), 0, gdk_screen_height() );
267 gtk_spin_button_set_value( GTK_SPIN_BUTTON(spin), gdk_screen_height() );
268 }
269 break;
270 case WIDTH_REQUEST:
271 break;
272 default: ;
273 }
274
275 update_panel_geometry(panel);
276 UPDATE_GLOBAL_STRING(p, "widthtype", num2str(width_pair, widthtype, "none"));
277 }
278
279 /* FIXME: heighttype and spacing and RoundCorners */
280
281 static void transparency_toggle( GtkWidget *b, Panel* p)
282 {
283 GtkWidget* tr = (GtkWidget*)g_object_get_data(G_OBJECT(b), "tint_clr");
284 gboolean t;
285
286 ENTER;
287
288 t = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b));
289 gtk_widget_set_sensitive(tr, t);
290 /*
291 gtk_widget_set_sensitive(tr_colorl, t);
292 gtk_widget_set_sensitive(tr_colorb, t);
293 */
294 /* Update background immediately. */
295 if (t&&!p->transparent) {
296 p->transparent = 1;
297 p->background = 0;
298 panel_update_background( p );
299 UPDATE_GLOBAL_INT(p, "transparent", p->transparent);
300 UPDATE_GLOBAL_INT(p, "background", p->background);
301 }
302 RET();
303 }
304
305 static void background_file_helper(Panel * p, GtkWidget * toggle, GtkFileChooser * file_chooser)
306 {
307 char * file = g_strdup(gtk_file_chooser_get_filename(file_chooser));
308 if (file != NULL)
309 {
310 g_free(p->background_file);
311 p->background_file = file;
312 UPDATE_GLOBAL_STRING(p, "backgroundfile", p->background_file);
313 }
314
315 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
316 {
317 if ( ! p->background)
318 {
319 p->transparent = FALSE;
320 p->background = TRUE;
321 panel_update_background(p);
322 UPDATE_GLOBAL_INT(p, "transparent", p->transparent);
323 UPDATE_GLOBAL_INT(p, "background", p->background);
324 }
325 }
326 }
327
328 static void background_toggle( GtkWidget *b, Panel* p)
329 {
330 GtkWidget * fc = (GtkWidget*) g_object_get_data(G_OBJECT(b), "img_file");
331 gtk_widget_set_sensitive(fc, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b)));
332 background_file_helper(p, b, GTK_FILE_CHOOSER(fc));
333 }
334
335 static void background_changed(GtkFileChooser *file_chooser, Panel* p )
336 {
337 GtkWidget * btn = GTK_WIDGET(g_object_get_data(G_OBJECT(file_chooser), "bg_image"));
338 background_file_helper(p, btn, file_chooser);
339 }
340
341 static void
342 background_disable_toggle( GtkWidget *b, Panel* p )
343 {
344 ENTER;
345 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b))) {
346 if (p->background!=0||p->transparent!=0) {
347 p->background = 0;
348 p->transparent = 0;
349 /* Update background immediately. */
350 panel_update_background( p );
351 UPDATE_GLOBAL_INT(p, "transparent", p->transparent);
352 UPDATE_GLOBAL_INT(p, "background", p->background);
353 }
354 }
355
356 RET();
357 }
358
359 static void
360 on_font_color_set( GtkColorButton* clr, Panel* p )
361 {
362 gtk_color_button_get_color( clr, &p->gfontcolor );
363 panel_set_panel_configuration_changed(p);
364 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
365 UPDATE_GLOBAL_COLOR(p, "fontcolor", p->fontcolor);
366 }
367
368 static void
369 on_tint_color_set( GtkColorButton* clr, Panel* p )
370 {
371 gtk_color_button_get_color( clr, &p->gtintcolor );
372 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
373 p->alpha = gtk_color_button_get_alpha( clr ) / alpha_scale_factor;
374 panel_update_background( p );
375 UPDATE_GLOBAL_COLOR(p, "tintcolor", p->tintcolor);
376 UPDATE_GLOBAL_INT(p, "alpha", p->alpha);
377 }
378
379 static void
380 on_use_font_color_toggled( GtkToggleButton* btn, Panel* p )
381 {
382 GtkWidget* clr = (GtkWidget*)g_object_get_data( G_OBJECT(btn), "clr" );
383 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn)))
384 gtk_widget_set_sensitive( clr, TRUE );
385 else
386 gtk_widget_set_sensitive( clr, FALSE );
387 p->usefontcolor = gtk_toggle_button_get_active( btn );
388 panel_set_panel_configuration_changed(p);
389 UPDATE_GLOBAL_INT(p, "usefontcolor", p->usefontcolor);
390 }
391
392 static void
393 on_font_size_set( GtkSpinButton* spin, Panel* p )
394 {
395 p->fontsize = (int)gtk_spin_button_get_value(spin);
396 panel_set_panel_configuration_changed(p);
397 UPDATE_GLOBAL_INT(p, "fontsize", p->fontsize);
398 }
399
400 static void
401 on_use_font_size_toggled( GtkToggleButton* btn, Panel* p )
402 {
403 GtkWidget* clr = (GtkWidget*)g_object_get_data( G_OBJECT(btn), "clr" );
404 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn)))
405 gtk_widget_set_sensitive( clr, TRUE );
406 else
407 gtk_widget_set_sensitive( clr, FALSE );
408 p->usefontsize = gtk_toggle_button_get_active( btn );
409 panel_set_panel_configuration_changed(p);
410 UPDATE_GLOBAL_INT(p, "usefontsize", p->usefontsize);
411 }
412
413
414 static void
415 set_dock_type(GtkToggleButton* toggle, LXPanel* panel)
416 {
417 Panel *p = panel->priv;
418
419 p->setdocktype = gtk_toggle_button_get_active(toggle) ? 1 : 0;
420 panel_set_dock_type( p );
421 update_panel_geometry(panel);
422 UPDATE_GLOBAL_INT(p, "setdocktype", p->setdocktype);
423 }
424
425 static void
426 set_strut(GtkToggleButton* toggle, LXPanel* panel)
427 {
428 Panel *p = panel->priv;
429
430 p->setstrut = gtk_toggle_button_get_active(toggle) ? 1 : 0;
431 update_panel_geometry(panel);
432 UPDATE_GLOBAL_INT(p, "setpartialstrut", p->setstrut);
433 }
434
435 static void
436 set_autohide(GtkToggleButton* toggle, LXPanel* panel)
437 {
438 Panel *p = panel->priv;
439
440 p->autohide = gtk_toggle_button_get_active(toggle) ? 1 : 0;
441 update_panel_geometry(panel);
442 UPDATE_GLOBAL_INT(p, "autohide", p->autohide);
443 }
444
445 static void
446 set_height_when_minimized(GtkSpinButton* spin, LXPanel* panel)
447 {
448 Panel *p = panel->priv;
449
450 p->height_when_hidden = (int)gtk_spin_button_get_value(spin);
451 update_panel_geometry(panel);
452 UPDATE_GLOBAL_INT(p, "heightwhenhidden", p->height_when_hidden);
453 }
454
455 static void
456 set_icon_size( GtkSpinButton* spin, Panel* p )
457 {
458 p->icon_size = (int)gtk_spin_button_get_value(spin);
459 panel_set_panel_configuration_changed(p);
460 UPDATE_GLOBAL_INT(p, "iconsize", p->icon_size);
461 }
462
463 static void
464 on_sel_plugin_changed( GtkTreeSelection* tree_sel, GtkWidget* label )
465 {
466 GtkTreeIter it;
467 GtkTreeModel* model;
468 GtkWidget* pl;
469
470 if( gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
471 {
472 GtkTreeView* view = gtk_tree_selection_get_tree_view( tree_sel );
473 GtkWidget *edit_btn = GTK_WIDGET(g_object_get_data( G_OBJECT(view), "edit_btn" ));
474 const LXPanelPluginInit *init;
475 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
476 init = PLUGIN_CLASS(pl);
477 gtk_label_set_text( GTK_LABEL(label), _(init->description) );
478 gtk_widget_set_sensitive( edit_btn, init->config != NULL );
479 }
480 }
481
482 static void
483 on_plugin_expand_toggled(GtkCellRendererToggle* render, char* path, GtkTreeView* view)
484 {
485 GtkTreeModel* model;
486 GtkTreeIter it;
487 GtkTreePath* tp = gtk_tree_path_new_from_string( path );
488 model = gtk_tree_view_get_model( view );
489 if( gtk_tree_model_get_iter( model, &it, tp ) )
490 {
491 GtkWidget* pl;
492 gboolean old_expand, expand, fill;
493 guint padding;
494 GtkPackType pack_type;
495 const LXPanelPluginInit *init;
496 LXPanel *panel;
497
498 gtk_tree_model_get( model, &it, COL_DATA, &pl, COL_EXPAND, &expand, -1 );
499 init = PLUGIN_CLASS(pl);
500 panel = PLUGIN_PANEL(pl);
501
502 if (init->expand_available)
503 {
504 config_setting_t *s = g_object_get_qdata(G_OBJECT(pl), lxpanel_plugin_qconf);
505 GtkBox *box = GTK_BOX(panel->priv->box);
506 /* Only honor "stretch" if allowed by the plugin. */
507 expand = ! expand;
508 gtk_list_store_set( GTK_LIST_STORE(model), &it, COL_EXPAND, expand, -1 );
509
510 /* Query the old packing of the plugin widget.
511 * Apply the new packing with only "expand" modified. */
512 gtk_box_query_child_packing( box, pl, &old_expand, &fill, &padding, &pack_type );
513 gtk_box_set_child_packing( box, pl, expand, fill, padding, pack_type );
514 if (expand)
515 config_group_set_int(s, "expand", 1);
516 else
517 config_setting_remove(s, "expand");
518 }
519 }
520 gtk_tree_path_free( tp );
521 }
522
523 static void on_stretch_render(GtkTreeViewColumn * column, GtkCellRenderer * renderer, GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
524 {
525 /* Set the control visible depending on whether stretch is available for the plugin.
526 * The g_object_set method is touchy about its parameter, so we can't pass the boolean directly. */
527 GtkWidget * pl;
528 gtk_tree_model_get(model, iter, COL_DATA, &pl, -1);
529 g_object_set(renderer,
530 "visible", ((PLUGIN_CLASS(pl)->expand_available) ? TRUE : FALSE),
531 NULL);
532 }
533
534 static void init_plugin_list( LXPanel* p, GtkTreeView* view, GtkWidget* label )
535 {
536 GtkListStore* list;
537 GtkTreeViewColumn* col;
538 GtkCellRenderer* render;
539 GtkTreeSelection* tree_sel;
540 GList *plugins, *l;
541 GtkTreeIter it;
542
543 g_object_set_data( G_OBJECT(view), "panel", p );
544
545 render = gtk_cell_renderer_text_new();
546 col = gtk_tree_view_column_new_with_attributes(
547 _("Currently loaded plugins"),
548 render, "text", COL_NAME, NULL );
549 gtk_tree_view_column_set_expand( col, TRUE );
550 gtk_tree_view_append_column( view, col );
551
552 render = gtk_cell_renderer_toggle_new();
553 g_object_set( render, "activatable", TRUE, NULL );
554 g_signal_connect( render, "toggled", G_CALLBACK( on_plugin_expand_toggled ), view );
555 col = gtk_tree_view_column_new_with_attributes(
556 _("Stretch"),
557 render, "active", COL_EXPAND, NULL );
558 gtk_tree_view_column_set_expand( col, FALSE );
559 gtk_tree_view_column_set_cell_data_func(col, render, on_stretch_render, NULL, NULL);
560 gtk_tree_view_append_column( view, col );
561
562 list = gtk_list_store_new( N_COLS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER );
563 plugins = p->priv->box ? gtk_container_get_children(GTK_CONTAINER(p->priv->box)) : NULL;
564 for( l = plugins; l; l = l->next )
565 {
566 GtkTreeIter it;
567 gboolean expand;
568 GtkWidget *w = (GtkWidget*)l->data;
569 gtk_container_child_get(GTK_CONTAINER(p->priv->box), w, "expand", &expand, NULL);
570 gtk_list_store_append( list, &it );
571 gtk_list_store_set( list, &it,
572 COL_NAME, _(PLUGIN_CLASS(w)->name),
573 COL_EXPAND, expand,
574 COL_DATA, w,
575 -1);
576 }
577 g_list_free(plugins);
578 gtk_tree_view_set_model( view, GTK_TREE_MODEL( list ) );
579 g_signal_connect( view, "row-activated",
580 G_CALLBACK(modify_plugin), NULL );
581 tree_sel = gtk_tree_view_get_selection( view );
582 gtk_tree_selection_set_mode( tree_sel, GTK_SELECTION_BROWSE );
583 g_signal_connect( tree_sel, "changed",
584 G_CALLBACK(on_sel_plugin_changed), label);
585 if( gtk_tree_model_get_iter_first( GTK_TREE_MODEL(list), &it ) )
586 gtk_tree_selection_select_iter( tree_sel, &it );
587 }
588
589 static void on_add_plugin_row_activated( GtkTreeView *tree_view,
590 GtkTreePath *path,
591 GtkTreeViewColumn *col,
592 gpointer user_data)
593 {
594 GtkWidget *dlg;
595
596 dlg = (GtkWidget *) user_data;
597
598 (void) tree_view;
599 (void) path;
600 (void) col;
601
602 /* Emitting the "response" signal ourselves. */
603 gtk_dialog_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
604 }
605
606 static void on_add_plugin_response( GtkDialog* dlg,
607 int response,
608 GtkTreeView* _view )
609 {
610 LXPanel* p = (LXPanel*) g_object_get_data( G_OBJECT(_view), "panel" );
611 if( response == GTK_RESPONSE_OK )
612 {
613 GtkTreeView* view;
614 GtkTreeSelection* tree_sel;
615 GtkTreeIter it;
616 GtkTreeModel* model;
617
618 view = (GtkTreeView*)g_object_get_data( G_OBJECT(dlg), "avail-plugins" );
619 tree_sel = gtk_tree_view_get_selection( view );
620 if( gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
621 {
622 char* type = NULL;
623 GtkWidget *pl;
624 config_setting_t *cfg;
625
626 cfg = config_group_add_subgroup(config_root_setting(p->priv->config),
627 "Plugin");
628 gtk_tree_model_get( model, &it, 1, &type, -1 );
629 config_group_set_string(cfg, "type", type);
630 if ((pl = lxpanel_add_plugin(p, type, cfg, -1)))
631 {
632 GtkTreePath* tree_path;
633 gboolean expand;
634
635 panel_config_save(p->priv);
636
637 plugin_widget_set_background(pl, p);
638 gtk_container_child_get(GTK_CONTAINER(p->priv->box), pl, "expand", &expand, NULL);
639 model = gtk_tree_view_get_model( _view );
640 gtk_list_store_append( GTK_LIST_STORE(model), &it );
641 gtk_list_store_set( GTK_LIST_STORE(model), &it,
642 COL_NAME, _(PLUGIN_CLASS(pl)->name),
643 COL_EXPAND, expand,
644 COL_DATA, pl, -1 );
645 tree_sel = gtk_tree_view_get_selection( _view );
646 gtk_tree_selection_select_iter( tree_sel, &it );
647 if ((tree_path = gtk_tree_model_get_path(model, &it)) != NULL)
648 {
649 gtk_tree_view_scroll_to_cell( _view, tree_path, NULL, FALSE, 0, 0 );
650 gtk_tree_path_free( tree_path );
651 }
652 }
653 else /* free unused setting */
654 config_setting_destroy(cfg);
655 g_free( type );
656 }
657 }
658 gtk_widget_destroy( GTK_WIDGET(dlg) );
659 }
660
661 static void on_add_plugin( GtkButton* btn, GtkTreeView* _view )
662 {
663 GtkWidget* dlg, *parent_win, *scroll;
664 GHashTable *classes;
665 GtkTreeViewColumn* col;
666 GtkCellRenderer* render;
667 GtkTreeView* view;
668 GtkListStore* list;
669 GtkTreeSelection* tree_sel;
670 GHashTableIter iter;
671 gpointer key, val;
672
673 LXPanel* p = (LXPanel*) g_object_get_data( G_OBJECT(_view), "panel" );
674
675 classes = lxpanel_get_all_types();
676
677 parent_win = gtk_widget_get_toplevel( GTK_WIDGET(_view) );
678 dlg = gtk_dialog_new_with_buttons( _("Add plugin to panel"),
679 GTK_WINDOW(parent_win), 0,
680 GTK_STOCK_CANCEL,
681 GTK_RESPONSE_CANCEL,
682 GTK_STOCK_ADD,
683 GTK_RESPONSE_OK, NULL );
684 panel_apply_icon(GTK_WINDOW(dlg));
685
686 /* fix background */
687 if (p->priv->background)
688 gtk_widget_set_style(dlg, p->priv->defstyle);
689
690 /* gtk_widget_set_sensitive( parent_win, FALSE ); */
691 scroll = gtk_scrolled_window_new( NULL, NULL );
692 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(scroll),
693 GTK_SHADOW_IN );
694 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
695 GTK_POLICY_AUTOMATIC,
696 GTK_POLICY_AUTOMATIC );
697 gtk_box_pack_start( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))),
698 scroll, TRUE, TRUE, 4 );
699 view = GTK_TREE_VIEW(gtk_tree_view_new());
700 gtk_container_add( GTK_CONTAINER(scroll), GTK_WIDGET(view) );
701 tree_sel = gtk_tree_view_get_selection( view );
702 gtk_tree_selection_set_mode( tree_sel, GTK_SELECTION_BROWSE );
703
704 render = gtk_cell_renderer_text_new();
705 col = gtk_tree_view_column_new_with_attributes(
706 _("Available plugins"),
707 render, "text", 0, NULL );
708 gtk_tree_view_append_column( view, col );
709
710 list = gtk_list_store_new( 2,
711 G_TYPE_STRING,
712 G_TYPE_STRING );
713
714 /* Populate list of available plugins.
715 * Omit plugins that can only exist once per system if it is already configured. */
716 g_hash_table_iter_init(&iter, classes);
717 while(g_hash_table_iter_next(&iter, &key, &val))
718 {
719 register const LXPanelPluginInit *init = val;
720 if (init->superseded)
721 continue;
722 if (!init->one_per_system || !_class_is_present(init))
723 {
724 GtkTreeIter it;
725 gtk_list_store_append( list, &it );
726 /* it is safe to put classes data here - they will be valid until restart */
727 gtk_list_store_set( list, &it,
728 0, _(init->name),
729 1, key,
730 -1 );
731 /* g_debug( "%s (%s)", pc->type, _(pc->name) ); */
732 }
733 }
734
735 gtk_tree_view_set_model( view, GTK_TREE_MODEL(list) );
736 g_object_unref( list );
737
738 /*
739 * The user can add a plugin either by clicking the "Add" button, or by
740 * double-clicking the plugin.
741 */
742 g_signal_connect( dlg, "response",
743 G_CALLBACK(on_add_plugin_response), _view );
744 g_signal_connect( view, "row-activated",
745 G_CALLBACK(on_add_plugin_row_activated), (gpointer) dlg);
746
747 g_object_set_data( G_OBJECT(dlg), "avail-plugins", view );
748
749 gtk_window_set_default_size( GTK_WINDOW(dlg), 320, 400 );
750 gtk_widget_show_all( dlg );
751 }
752
753 static void on_remove_plugin( GtkButton* btn, GtkTreeView* view )
754 {
755 GtkTreeIter it;
756 GtkTreePath* tree_path;
757 GtkTreeModel* model;
758 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection( view );
759 GtkWidget* pl;
760
761 LXPanel* p = (LXPanel*) g_object_get_data( G_OBJECT(view), "panel" );
762
763 if( gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
764 {
765 tree_path = gtk_tree_model_get_path( model, &it );
766 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
767 if( gtk_tree_path_get_indices(tree_path)[0] >= gtk_tree_model_iter_n_children( model, NULL ) )
768 gtk_tree_path_prev( tree_path );
769 gtk_list_store_remove( GTK_LIST_STORE(model), &it );
770 gtk_tree_selection_select_path( tree_sel, tree_path );
771 gtk_tree_path_free( tree_path );
772
773 config_setting_destroy(g_object_get_qdata(G_OBJECT(pl), lxpanel_plugin_qconf));
774 /* reset conf pointer because the widget still may be referenced by configurator */
775 g_object_set_qdata(G_OBJECT(pl), lxpanel_plugin_qconf, NULL);
776 gtk_widget_destroy(pl);
777 panel_config_save(p->priv);
778 }
779 }
780
781 static void modify_plugin( GtkTreeView* view )
782 {
783 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection( view );
784 GtkTreeModel* model;
785 GtkTreeIter it;
786 GtkWidget* pl;
787 const LXPanelPluginInit *init;
788
789 if( ! gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
790 return;
791
792 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
793 init = PLUGIN_CLASS(pl);
794 if (init->config)
795 {
796 GtkWidget *dlg;
797 LXPanel *panel = PLUGIN_PANEL(pl);
798 dlg = init->config(panel, pl);
799 if (dlg)
800 _panel_show_config_dialog(panel, pl, dlg);
801 }
802 }
803
804 typedef struct
805 {
806 GtkWidget *pl;
807 int cur;
808 int idx;
809 } WidgetIndexData;
810
811 static void get_widget_index_cb(GtkWidget *widget, gpointer data)
812 {
813 if (((WidgetIndexData *)data)->pl == widget)
814 ((WidgetIndexData *)data)->idx = ((WidgetIndexData *)data)->cur;
815 ((WidgetIndexData *)data)->cur++;
816 }
817
818 static int get_widget_index(LXPanel* p, GtkWidget* pl)
819 {
820 WidgetIndexData data;
821
822 data.pl = pl;
823 data.idx = -1;
824 data.cur = 0;
825 gtk_container_foreach(GTK_CONTAINER(p->priv->box), get_widget_index_cb, &data);
826 return data.idx;
827 }
828
829 static void on_moveup_plugin( GtkButton* btn, GtkTreeView* view )
830 {
831 GtkTreeIter it, prev;
832 GtkTreeModel* model = gtk_tree_view_get_model( view );
833 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection( view );
834 int i;
835
836 LXPanel* panel = (LXPanel*) g_object_get_data( G_OBJECT(view), "panel" );
837
838 if( ! gtk_tree_model_get_iter_first( model, &it ) )
839 return;
840 if( gtk_tree_selection_iter_is_selected( tree_sel, &it ) )
841 return;
842 do{
843 if( gtk_tree_selection_iter_is_selected(tree_sel, &it) )
844 {
845 GtkWidget* pl;
846 config_setting_t *s;
847 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
848 gtk_list_store_move_before( GTK_LIST_STORE( model ),
849 &it, &prev );
850
851 i = get_widget_index(panel, pl);
852 s = g_object_get_qdata(G_OBJECT(pl), lxpanel_plugin_qconf);
853 /* reorder in config, 0 is Global */
854 if (i == 0)
855 i = 1;
856 config_setting_move_elem(s, config_setting_get_parent(s), i);
857 /* reorder in panel */
858 gtk_box_reorder_child(GTK_BOX(panel->priv->box), pl, i - 1);
859 panel_config_save(panel->priv);
860 return;
861 }
862 prev = it;
863 }while( gtk_tree_model_iter_next( model, &it ) );
864 }
865
866 static void on_movedown_plugin( GtkButton* btn, GtkTreeView* view )
867 {
868 GtkTreeIter it, next;
869 GtkTreeModel* model;
870 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection( view );
871 GtkWidget* pl;
872 config_setting_t *s;
873 int i;
874
875 LXPanel* panel = (LXPanel*) g_object_get_data( G_OBJECT(view), "panel" );
876
877 if( ! gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
878 return;
879 next = it;
880
881 if( ! gtk_tree_model_iter_next( model, &next) )
882 return;
883
884 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
885
886 gtk_list_store_move_after( GTK_LIST_STORE( model ), &it, &next );
887
888 i = get_widget_index(panel, pl) + 1;
889 s = g_object_get_qdata(G_OBJECT(pl), lxpanel_plugin_qconf);
890 /* reorder in config, 0 is Global */
891 config_setting_move_elem(s, config_setting_get_parent(s), i + 1);
892 /* reorder in panel */
893 gtk_box_reorder_child(GTK_BOX(panel->priv->box), pl, i);
894 panel_config_save(panel->priv);
895 }
896
897 static void
898 update_opt_menu(GtkWidget *w, int ind)
899 {
900 int i;
901
902 ENTER;
903 /* this trick will trigger "changed" signal even if active entry is
904 * not actually changing */
905 i = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
906 if (i == ind) {
907 i = i ? 0 : 1;
908 gtk_combo_box_set_active(GTK_COMBO_BOX(w), i);
909 }
910 gtk_combo_box_set_active(GTK_COMBO_BOX(w), ind);
911 RET();
912 }
913
914 static void
915 update_toggle_button(GtkWidget *w, gboolean n)
916 {
917 gboolean c;
918
919 ENTER;
920 /* this trick will trigger "changed" signal even if active entry is
921 * not actually changing */
922 c = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
923 if (c == n) {
924 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), !n);
925 }
926 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n);
927 RET();
928 }
929
930 static void on_app_chooser_destroy(GtkComboBox *fm, gpointer _unused)
931 {
932 gboolean is_changed;
933 GAppInfo *app = fm_app_chooser_combo_box_dup_selected_app(fm, &is_changed);
934 if(app)
935 {
936 if(is_changed)
937 g_app_info_set_as_default_for_type(app, "inode/directory", NULL);
938 g_object_unref(app);
939 }
940 }
941
942 void panel_configure( LXPanel* panel, int sel_page )
943 {
944 Panel *p = panel->priv;
945 GtkBuilder* builder;
946 GtkWidget *w, *w2, *tint_clr;
947 FmMimeType *mt;
948 GtkComboBox *fm;
949 GdkScreen *screen;
950 gint monitors;
951
952 if( p->pref_dialog )
953 {
954 panel_adjust_geometry_terminology(p);
955 gtk_window_present(GTK_WINDOW(p->pref_dialog));
956 return;
957 }
958
959 builder = gtk_builder_new();
960 if( !gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/panel-pref.ui", NULL) )
961 {
962 g_object_unref(builder);
963 return;
964 }
965
966 p->pref_dialog = (GtkWidget*)gtk_builder_get_object( builder, "panel_pref" );
967 gtk_window_set_transient_for(GTK_WINDOW(p->pref_dialog), GTK_WINDOW(panel));
968 g_signal_connect(p->pref_dialog, "response", G_CALLBACK(response_event), p);
969 g_object_add_weak_pointer( G_OBJECT(p->pref_dialog), (gpointer) &p->pref_dialog );
970 gtk_window_set_position( GTK_WINDOW(p->pref_dialog), GTK_WIN_POS_CENTER );
971 panel_apply_icon(GTK_WINDOW(p->pref_dialog));
972
973 /* position */
974 w = (GtkWidget*)gtk_builder_get_object( builder, "edge_bottom" );
975 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), edge_selector(p, EDGE_BOTTOM));
976 g_signal_connect(w, "toggled", G_CALLBACK(edge_bottom_toggle), panel);
977 w = (GtkWidget*)gtk_builder_get_object( builder, "edge_top" );
978 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), edge_selector(p, EDGE_TOP));
979 g_signal_connect(w, "toggled", G_CALLBACK(edge_top_toggle), panel);
980 w = (GtkWidget*)gtk_builder_get_object( builder, "edge_left" );
981 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), edge_selector(p, EDGE_LEFT));
982 g_signal_connect(w, "toggled", G_CALLBACK(edge_left_toggle), panel);
983 w = (GtkWidget*)gtk_builder_get_object( builder, "edge_right" );
984 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), edge_selector(p, EDGE_RIGHT));
985 g_signal_connect(w, "toggled", G_CALLBACK(edge_right_toggle), panel);
986
987 /* monitor */
988 monitors = 1;
989 screen = gdk_screen_get_default();
990 if(screen) monitors = gdk_screen_get_n_monitors(screen);
991 g_assert(monitors >= 1);
992 w = (GtkWidget*)gtk_builder_get_object( builder, "monitor" );
993 gtk_spin_button_set_range(GTK_SPIN_BUTTON(w), 1, monitors);
994 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), p->monitor + 1);
995 gtk_widget_set_sensitive(w, monitors > 1);
996 g_signal_connect(w, "value-changed", G_CALLBACK(set_monitor), panel);
997
998 /* alignment */
999 p->alignment_left_label = w = (GtkWidget*)gtk_builder_get_object( builder, "alignment_left" );
1000 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), (p->allign == ALLIGN_LEFT));
1001 g_signal_connect(w, "toggled", G_CALLBACK(align_left_toggle), panel);
1002 w = (GtkWidget*)gtk_builder_get_object( builder, "alignment_center" );
1003 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), (p->allign == ALLIGN_CENTER));
1004 g_signal_connect(w, "toggled", G_CALLBACK(align_center_toggle), panel);
1005 p->alignment_right_label = w = (GtkWidget*)gtk_builder_get_object( builder, "alignment_right" );
1006 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), (p->allign == ALLIGN_RIGHT));
1007 g_signal_connect(w, "toggled", G_CALLBACK(align_right_toggle), panel);
1008
1009 /* margin */
1010 p->margin_control = w = (GtkWidget*)gtk_builder_get_object( builder, "margin" );
1011 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), p->margin);
1012 gtk_widget_set_sensitive(p->margin_control, (p->allign != ALLIGN_CENTER));
1013 g_signal_connect( w, "value-changed",
1014 G_CALLBACK(set_margin), panel);
1015
1016 /* size */
1017 p->width_label = (GtkWidget*)gtk_builder_get_object( builder, "width_label");
1018 p->width_control = w = (GtkWidget*)gtk_builder_get_object( builder, "width" );
1019 gtk_widget_set_sensitive( w, p->widthtype != WIDTH_REQUEST );
1020 gint upper = 0;
1021 if( p->widthtype == WIDTH_PERCENT)
1022 upper = 100;
1023 else if( p->widthtype == WIDTH_PIXEL)
1024 upper = (((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM)) ? gdk_screen_width() : gdk_screen_height());
1025 gtk_spin_button_set_range( GTK_SPIN_BUTTON(w), 0, upper );
1026 gtk_spin_button_set_value( GTK_SPIN_BUTTON(w), p->width );
1027 g_signal_connect( w, "value-changed", G_CALLBACK(set_width), panel );
1028
1029 w = (GtkWidget*)gtk_builder_get_object( builder, "width_unit" );
1030 update_opt_menu( w, p->widthtype - 1 );
1031 g_object_set_data(G_OBJECT(w), "width_spin", p->width_control );
1032 g_signal_connect( w, "changed",
1033 G_CALLBACK(set_width_type), panel);
1034
1035 p->height_label = (GtkWidget*)gtk_builder_get_object( builder, "height_label");
1036 p->height_control = w = (GtkWidget*)gtk_builder_get_object( builder, "height" );
1037 gtk_spin_button_set_range( GTK_SPIN_BUTTON(w), PANEL_HEIGHT_MIN, PANEL_HEIGHT_MAX );
1038 gtk_spin_button_set_value( GTK_SPIN_BUTTON(w), p->height );
1039 g_signal_connect( w, "value-changed", G_CALLBACK(set_height), panel );
1040
1041 w = (GtkWidget*)gtk_builder_get_object( builder, "height_unit" );
1042 update_opt_menu( w, HEIGHT_PIXEL - 1);
1043
1044 w = (GtkWidget*)gtk_builder_get_object( builder, "icon_size" );
1045 gtk_spin_button_set_range( GTK_SPIN_BUTTON(w), PANEL_HEIGHT_MIN, PANEL_HEIGHT_MAX );
1046 gtk_spin_button_set_value( GTK_SPIN_BUTTON(w), p->icon_size );
1047 g_signal_connect( w, "value-changed", G_CALLBACK(set_icon_size), p );
1048
1049 /* properties */
1050
1051 /* Explaination from Ruediger Arp <ruediger@gmx.net>:
1052 "Set Dock Type", it is referring to the behaviour of
1053 dockable applications such as those found in WindowMaker (e.g.
1054 http://www.cs.mun.ca/~gstarkes/wmaker/dockapps ) and other
1055 lightweight window managers. These dockapps are probably being
1056 treated in some special way.
1057 */
1058 w = (GtkWidget*)gtk_builder_get_object( builder, "as_dock" );
1059 update_toggle_button( w, p->setdocktype );
1060 g_signal_connect( w, "toggled",
1061 G_CALLBACK(set_dock_type), panel );
1062
1063 /* Explaination from Ruediger Arp <ruediger@gmx.net>:
1064 "Set Strut": Reserve panel's space so that it will not be
1065 covered by maximazied windows.
1066 This is clearly an option to avoid the panel being
1067 covered/hidden by other applications so that it always is
1068 accessible. The panel "steals" some screen estate which cannot
1069 be accessed by other applications.
1070 GNOME Panel acts this way, too.
1071 */
1072 w = (GtkWidget*)gtk_builder_get_object( builder, "reserve_space" );
1073 update_toggle_button( w, p->setstrut );
1074 g_signal_connect( w, "toggled",
1075 G_CALLBACK(set_strut), panel );
1076
1077 w = (GtkWidget*)gtk_builder_get_object( builder, "autohide" );
1078 update_toggle_button( w, p->autohide );
1079 g_signal_connect( w, "toggled",
1080 G_CALLBACK(set_autohide), panel );
1081
1082 w = (GtkWidget*)gtk_builder_get_object( builder, "height_when_minimized" );
1083 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), p->height_when_hidden);
1084 g_signal_connect( w, "value-changed",
1085 G_CALLBACK(set_height_when_minimized), panel);
1086
1087 /* transparancy */
1088 tint_clr = w = (GtkWidget*)gtk_builder_get_object( builder, "tint_clr" );
1089 gtk_color_button_set_color(GTK_COLOR_BUTTON(w), &p->gtintcolor);
1090 gtk_color_button_set_alpha(GTK_COLOR_BUTTON(w), alpha_scale_factor * p->alpha);
1091 if ( ! p->transparent )
1092 gtk_widget_set_sensitive( w, FALSE );
1093 g_signal_connect( w, "color-set", G_CALLBACK( on_tint_color_set ), p );
1094
1095 /* background */
1096 {
1097 GtkWidget* none, *trans, *img;
1098 GtkIconInfo* info;
1099 none = (GtkWidget*)gtk_builder_get_object( builder, "bg_none" );
1100 trans = (GtkWidget*)gtk_builder_get_object( builder, "bg_transparency" );
1101 img = (GtkWidget*)gtk_builder_get_object( builder, "bg_image" );
1102
1103 g_object_set_data(G_OBJECT(trans), "tint_clr", tint_clr);
1104
1105 if (p->background)
1106 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(img), TRUE);
1107 else if (p->transparent)
1108 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(trans), TRUE);
1109 else
1110 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(none), TRUE);
1111
1112 g_signal_connect(none, "toggled", G_CALLBACK(background_disable_toggle), p);
1113 g_signal_connect(trans, "toggled", G_CALLBACK(transparency_toggle), p);
1114 g_signal_connect(img, "toggled", G_CALLBACK(background_toggle), p);
1115
1116 w = (GtkWidget*)gtk_builder_get_object( builder, "img_file" );
1117 g_object_set_data(G_OBJECT(img), "img_file", w);
1118 if (p->background_file != NULL)
1119 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), p->background_file);
1120 else if ((info = gtk_icon_theme_lookup_icon(p->icon_theme, "lxpanel-background", 0, 0)))
1121 {
1122 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), gtk_icon_info_get_filename(info));
1123 gtk_icon_info_free(info);
1124 }
1125
1126 if (!p->background)
1127 gtk_widget_set_sensitive( w, FALSE);
1128 g_object_set_data( G_OBJECT(w), "bg_image", img );
1129 g_signal_connect( w, "file-set", G_CALLBACK (background_changed), p);
1130 }
1131
1132 /* font color */
1133 w = (GtkWidget*)gtk_builder_get_object( builder, "font_clr" );
1134 gtk_color_button_set_color( GTK_COLOR_BUTTON(w), &p->gfontcolor );
1135 g_signal_connect( w, "color-set", G_CALLBACK( on_font_color_set ), p );
1136
1137 w2 = (GtkWidget*)gtk_builder_get_object( builder, "use_font_clr" );
1138 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(w2), p->usefontcolor );
1139 g_object_set_data( G_OBJECT(w2), "clr", w );
1140 g_signal_connect(w2, "toggled", G_CALLBACK(on_use_font_color_toggled), p);
1141 if( ! p->usefontcolor )
1142 gtk_widget_set_sensitive( w, FALSE );
1143
1144 /* font size */
1145 w = (GtkWidget*)gtk_builder_get_object( builder, "font_size" );
1146 gtk_spin_button_set_value( GTK_SPIN_BUTTON(w), p->fontsize );
1147 g_signal_connect( w, "value-changed",
1148 G_CALLBACK(on_font_size_set), p);
1149
1150 w2 = (GtkWidget*)gtk_builder_get_object( builder, "use_font_size" );
1151 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(w2), p->usefontsize );
1152 g_object_set_data( G_OBJECT(w2), "clr", w );
1153 g_signal_connect(w2, "toggled", G_CALLBACK(on_use_font_size_toggled), p);
1154 if( ! p->usefontsize )
1155 gtk_widget_set_sensitive( w, FALSE );
1156
1157 /* plugin list */
1158 {
1159 GtkWidget* plugin_list = (GtkWidget*)gtk_builder_get_object( builder, "plugin_list" );
1160
1161 /* buttons used to edit plugin list */
1162 w = (GtkWidget*)gtk_builder_get_object( builder, "add_btn" );
1163 g_signal_connect( w, "clicked", G_CALLBACK(on_add_plugin), plugin_list );
1164
1165 w = (GtkWidget*)gtk_builder_get_object( builder, "edit_btn" );
1166 g_signal_connect_swapped( w, "clicked", G_CALLBACK(modify_plugin), plugin_list );
1167 g_object_set_data( G_OBJECT(plugin_list), "edit_btn", w );
1168
1169 w = (GtkWidget*)gtk_builder_get_object( builder, "remove_btn" );
1170 g_signal_connect( w, "clicked", G_CALLBACK(on_remove_plugin), plugin_list );
1171 w = (GtkWidget*)gtk_builder_get_object( builder, "moveup_btn" );
1172 g_signal_connect( w, "clicked", G_CALLBACK(on_moveup_plugin), plugin_list );
1173 w = (GtkWidget*)gtk_builder_get_object( builder, "movedown_btn" );
1174 g_signal_connect( w, "clicked", G_CALLBACK(on_movedown_plugin), plugin_list );
1175
1176 w = (GtkWidget*)gtk_builder_get_object( builder, "plugin_desc" );
1177 init_plugin_list( panel, GTK_TREE_VIEW(plugin_list), w );
1178 }
1179 /* advanced, applications */
1180 mt = fm_mime_type_from_name("inode/directory");
1181 fm = GTK_COMBO_BOX(gtk_builder_get_object(builder, "fm_combobox"));
1182 fm_app_chooser_combo_box_setup_for_mime_type(fm, mt);
1183 fm_mime_type_unref(mt);
1184 g_signal_connect(fm, "destroy", G_CALLBACK(on_app_chooser_destroy), NULL);
1185
1186 w = (GtkWidget*)gtk_builder_get_object( builder, "term" );
1187 if (fm_config->terminal)
1188 gtk_entry_set_text( GTK_ENTRY(w), fm_config->terminal );
1189 g_signal_connect( w, "focus-out-event",
1190 G_CALLBACK(on_entry_focus_out),
1191 &fm_config->terminal);
1192
1193 /* If we are under LXSession, setting logout command is not necessary. */
1194 w = (GtkWidget*)gtk_builder_get_object( builder, "logout" );
1195 if( getenv("_LXSESSION_PID") ) {
1196 gtk_widget_hide( w );
1197 w = (GtkWidget*)gtk_builder_get_object( builder, "logout_label" );
1198 gtk_widget_hide( w );
1199 }
1200 else {
1201 if(logout_cmd)
1202 gtk_entry_set_text( GTK_ENTRY(w), logout_cmd );
1203 g_signal_connect( w, "focus-out-event",
1204 G_CALLBACK(on_entry_focus_out_old),
1205 &logout_cmd);
1206 }
1207
1208 panel_adjust_geometry_terminology(p);
1209 gtk_widget_show(GTK_WIDGET(p->pref_dialog));
1210 w = (GtkWidget*)gtk_builder_get_object( builder, "notebook" );
1211 gtk_notebook_set_current_page( GTK_NOTEBOOK(w), sel_page );
1212
1213 g_object_unref(builder);
1214 }
1215
1216 void panel_config_save( Panel* p )
1217 {
1218 gchar *fname;
1219
1220 fname = _user_config_file_name("panels", p->name);
1221 /* existance of 'panels' dir ensured in main() */
1222
1223 if (!config_write_file(p->config, fname)) {
1224 g_warning("can't open for write %s:", fname);
1225 g_free( fname );
1226 return;
1227 }
1228 g_free( fname );
1229
1230 /* save the global config file */
1231 save_global_config();
1232 p->config_changed = 0;
1233 }
1234
1235 void lxpanel_config_save(LXPanel *p)
1236 {
1237 panel_config_save(p->priv);
1238 }
1239
1240 void logout(void)
1241 {
1242 const char* l_logout_cmd = logout_cmd;
1243 /* If LXSession is running, _LXSESSION_PID will be set */
1244 if( ! l_logout_cmd && getenv("_LXSESSION_PID") )
1245 l_logout_cmd = "lxsession-logout";
1246
1247 if( l_logout_cmd )
1248 fm_launch_command_simple(NULL, NULL, 0, l_logout_cmd, NULL);
1249 else
1250 fm_show_error(NULL, NULL, _("Logout command is not set"));
1251 }
1252
1253 static void notify_apply_config( GtkWidget* widget )
1254 {
1255 GSourceFunc apply_func;
1256 GtkWidget* dlg;
1257
1258 dlg = gtk_widget_get_toplevel( widget );
1259 apply_func = g_object_get_data( G_OBJECT(dlg), "apply_func" );
1260 if( apply_func )
1261 (*apply_func)( g_object_get_data(G_OBJECT(dlg), "apply_func_data") );
1262 }
1263
1264 static gboolean _on_entry_focus_out_do_work(GtkWidget* edit, gpointer user_data)
1265 {
1266 char** val = (char**)user_data;
1267 const char *new_val;
1268 new_val = gtk_entry_get_text(GTK_ENTRY(edit));
1269 if (g_strcmp0(*val, new_val) == 0) /* not changed */
1270 return FALSE;
1271 g_free( *val );
1272 *val = (new_val && *new_val) ? g_strdup( new_val ) : NULL;
1273 return TRUE;
1274 }
1275
1276 static gboolean on_entry_focus_out_old( GtkWidget* edit, GdkEventFocus *evt, gpointer user_data )
1277 {
1278 if (_on_entry_focus_out_do_work(edit, user_data))
1279 notify_apply_config( edit );
1280 return FALSE;
1281 }
1282
1283 /* the same but affects fm_config instead of panel config */
1284 static gboolean on_entry_focus_out( GtkWidget* edit, GdkEventFocus *evt, gpointer user_data )
1285 {
1286 if (_on_entry_focus_out_do_work(edit, user_data))
1287 fm_config_save(fm_config, NULL);
1288 return FALSE;
1289 }
1290
1291 static void on_spin_changed( GtkSpinButton* spin, gpointer user_data )
1292 {
1293 int* val = (int*)user_data;
1294 *val = (int)gtk_spin_button_get_value( spin );
1295 notify_apply_config( GTK_WIDGET(spin) );
1296 }
1297
1298 static void on_toggle_changed( GtkToggleButton* btn, gpointer user_data )
1299 {
1300 gboolean* val = (gboolean*)user_data;
1301 *val = gtk_toggle_button_get_active( btn );
1302 notify_apply_config( GTK_WIDGET(btn) );
1303 }
1304
1305 static void on_file_chooser_btn_file_set(GtkFileChooser* btn, char** val)
1306 {
1307 g_free( *val );
1308 *val = gtk_file_chooser_get_filename(btn);
1309 notify_apply_config( GTK_WIDGET(btn) );
1310 }
1311
1312 static void on_browse_btn_clicked(GtkButton* btn, GtkEntry* entry)
1313 {
1314 char* file;
1315 GtkFileChooserAction action = (GtkFileChooserAction) g_object_get_data(G_OBJECT(btn), "chooser-action");
1316 GtkWidget* dlg = GTK_WIDGET(g_object_get_data(G_OBJECT(btn), "dlg"));
1317 GtkWidget* fc = gtk_file_chooser_dialog_new(
1318 (action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ? _("Select a directory") : _("Select a file"),
1319 GTK_WINDOW(dlg),
1320 action,
1321 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1322 GTK_STOCK_OK, GTK_RESPONSE_OK,
1323 NULL);
1324 gtk_dialog_set_alternative_button_order(GTK_DIALOG(fc), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
1325 file = (char*)gtk_entry_get_text(entry);
1326 if( file && *file )
1327 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(fc), file );
1328 if( gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_OK )
1329 {
1330 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1331 gtk_entry_set_text(entry, file);
1332 on_entry_focus_out_old(GTK_WIDGET(entry), NULL, g_object_get_data(G_OBJECT(btn), "file-val"));
1333 g_free(file);
1334 }
1335 gtk_widget_destroy(fc);
1336 }
1337
1338 /* if the plugin was destroyed then destroy the dialog opened for it */
1339 static void on_plugin_destroy(GtkWidget *plugin, GtkDialog *dlg)
1340 {
1341 gtk_dialog_response(dlg, GTK_RESPONSE_CLOSE);
1342 }
1343
1344 /* Handler for "response" signal from standard configuration dialog. */
1345 static void generic_config_dlg_response(GtkWidget * dlg, int response, Panel * panel)
1346 {
1347 gpointer plugin = g_object_get_data(G_OBJECT(dlg), "generic-config-plugin");
1348 if (plugin)
1349 g_signal_handlers_disconnect_by_func(plugin, on_plugin_destroy, dlg);
1350 g_object_set_data(G_OBJECT(dlg), "generic-config-plugin", NULL);
1351 panel->plugin_pref_dialog = NULL;
1352 gtk_widget_destroy(dlg);
1353 panel_config_save(panel);
1354 }
1355
1356 void _panel_show_config_dialog(LXPanel *panel, GtkWidget *p, GtkWidget *dlg)
1357 {
1358 gint x, y;
1359
1360 /* If there is already a plugin configuration dialog open, close it.
1361 * Then record this one in case the panel or plugin is deleted. */
1362 if (panel->priv->plugin_pref_dialog != NULL)
1363 gtk_dialog_response(GTK_DIALOG(panel->priv->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
1364 panel->priv->plugin_pref_dialog = dlg;
1365
1366 /* add some handlers to destroy the dialog on responce or widget destroy */
1367 g_signal_connect(dlg, "response", G_CALLBACK(generic_config_dlg_response), panel->priv);
1368 g_signal_connect(p, "destroy", G_CALLBACK(on_plugin_destroy), dlg);
1369 g_object_set_data(G_OBJECT(dlg), "generic-config-plugin", p);
1370
1371 /* adjust config dialog window position to be near plugin */
1372 gtk_window_set_transient_for(GTK_WINDOW(dlg), GTK_WINDOW(panel));
1373 // gtk_window_iconify(GTK_WINDOW(dlg));
1374 gtk_widget_show(dlg);
1375 lxpanel_plugin_popup_set_position_helper(panel, p, dlg, &x, &y);
1376 gdk_window_move(gtk_widget_get_window(dlg), x, y);
1377
1378 gtk_window_present(GTK_WINDOW(dlg));
1379 }
1380
1381 /* Parameters: const char* name, gpointer ret_value, GType type, ....NULL */
1382 static GtkWidget *_lxpanel_generic_config_dlg(const char *title, Panel *p,
1383 GSourceFunc apply_func,
1384 gpointer plugin,
1385 const char *name, va_list args)
1386 {
1387 GtkWidget* dlg = gtk_dialog_new_with_buttons( title, NULL, 0,
1388 GTK_STOCK_CLOSE,
1389 GTK_RESPONSE_CLOSE,
1390 NULL );
1391 GtkBox *dlg_vbox = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg)));
1392
1393 panel_apply_icon(GTK_WINDOW(dlg));
1394
1395 if( apply_func )
1396 g_object_set_data( G_OBJECT(dlg), "apply_func", apply_func );
1397 g_object_set_data( G_OBJECT(dlg), "apply_func_data", plugin );
1398
1399 gtk_box_set_spacing( dlg_vbox, 4 );
1400
1401 while( name )
1402 {
1403 GtkWidget* label = gtk_label_new( name );
1404 GtkWidget* entry = NULL;
1405 gpointer val = va_arg( args, gpointer );
1406 PluginConfType type = va_arg( args, PluginConfType );
1407 switch( type )
1408 {
1409 case CONF_TYPE_STR:
1410 case CONF_TYPE_FILE_ENTRY: /* entry with a button to browse for files. */
1411 case CONF_TYPE_DIRECTORY_ENTRY: /* entry with a button to browse for directories. */
1412 entry = gtk_entry_new();
1413 if( *(char**)val )
1414 gtk_entry_set_text( GTK_ENTRY(entry), *(char**)val );
1415 gtk_entry_set_width_chars(GTK_ENTRY(entry), 40);
1416 g_signal_connect( entry, "focus-out-event",
1417 G_CALLBACK(on_entry_focus_out_old), val );
1418 break;
1419 case CONF_TYPE_INT:
1420 {
1421 /* FIXME: the range shouldn't be hardcoded */
1422 entry = gtk_spin_button_new_with_range( 0, 1000, 1 );
1423 gtk_spin_button_set_value( GTK_SPIN_BUTTON(entry), *(int*)val );
1424 g_signal_connect( entry, "value-changed",
1425 G_CALLBACK(on_spin_changed), val );
1426 break;
1427 }
1428 case CONF_TYPE_BOOL:
1429 entry = gtk_check_button_new();
1430 gtk_container_add( GTK_CONTAINER(entry), label );
1431 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(entry), *(gboolean*)val );
1432 g_signal_connect( entry, "toggled",
1433 G_CALLBACK(on_toggle_changed), val );
1434 break;
1435 case CONF_TYPE_FILE:
1436 entry = gtk_file_chooser_button_new(_("Select a file"), GTK_FILE_CHOOSER_ACTION_OPEN);
1437 if( *(char**)val )
1438 gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(entry), *(char**)val );
1439 g_signal_connect( entry, "file-set",
1440 G_CALLBACK(on_file_chooser_btn_file_set), val );
1441 break;
1442 case CONF_TYPE_TRIM:
1443 {
1444 entry = gtk_label_new(NULL);
1445 char *markup = g_markup_printf_escaped ("<span style=\"italic\">%s</span>", name );
1446 gtk_label_set_markup (GTK_LABEL (entry), markup);
1447 g_free (markup);
1448 }
1449 break;
1450 }
1451 if( entry )
1452 {
1453 if(( type == CONF_TYPE_BOOL ) || ( type == CONF_TYPE_TRIM ))
1454 gtk_box_pack_start( dlg_vbox, entry, FALSE, FALSE, 2 );
1455 else
1456 {
1457 GtkWidget* hbox = gtk_hbox_new( FALSE, 2 );
1458 gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, FALSE, 2 );
1459 gtk_box_pack_start( GTK_BOX(hbox), entry, TRUE, TRUE, 2 );
1460 gtk_box_pack_start( dlg_vbox, hbox, FALSE, FALSE, 2 );
1461 if ((type == CONF_TYPE_FILE_ENTRY) || (type == CONF_TYPE_DIRECTORY_ENTRY))
1462 {
1463 GtkWidget* browse = gtk_button_new_with_mnemonic(_("_Browse"));
1464 gtk_box_pack_start( GTK_BOX(hbox), browse, TRUE, TRUE, 2 );
1465 g_object_set_data(G_OBJECT(browse), "file-val", val);
1466 g_object_set_data(G_OBJECT(browse), "dlg", dlg);
1467
1468 GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
1469 if (type == CONF_TYPE_DIRECTORY_ENTRY)
1470 {
1471 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1472 }
1473
1474 g_object_set_data(G_OBJECT(browse), "chooser-action", GINT_TO_POINTER(action));
1475 g_signal_connect( browse, "clicked", G_CALLBACK(on_browse_btn_clicked), entry );
1476 }
1477 }
1478 }
1479 name = va_arg( args, const char* );
1480 }
1481
1482 gtk_container_set_border_width( GTK_CONTAINER(dlg), 8 );
1483
1484 gtk_widget_show_all(GTK_WIDGET(dlg_vbox));
1485
1486 return dlg;
1487 }
1488
1489 /* new plugins API -- apply_func() gets GtkWidget* */
1490 GtkWidget *lxpanel_generic_config_dlg(const char *title, LXPanel *panel,
1491 GSourceFunc apply_func, GtkWidget *plugin,
1492 const char *name, ...)
1493 {
1494 GtkWidget *dlg;
1495 va_list args;
1496
1497 if (plugin == NULL)
1498 return NULL;
1499 va_start(args, name);
1500 dlg = _lxpanel_generic_config_dlg(title, panel->priv, apply_func, plugin, name, args);
1501 va_end(args);
1502 return dlg;
1503 }
1504
1505 /* for old plugins compatibility -- apply_func() gets Plugin* */
1506 GtkWidget* create_generic_config_dlg( const char* title, GtkWidget* parent,
1507 GSourceFunc apply_func, Plugin * plugin,
1508 const char* name, ... )
1509 {
1510 GtkWidget *dlg;
1511 va_list args;
1512
1513 if (plugin == NULL)
1514 return NULL;
1515 va_start(args, name);
1516 dlg = _lxpanel_generic_config_dlg(title, plugin->panel, apply_func, plugin, name, args);
1517 va_end(args);
1518 _panel_show_config_dialog(plugin->panel->topgwin, plugin->pwid, dlg);
1519 return dlg;
1520 }
1521
1522 #define COMMAND_GROUP "Command"
1523
1524 void load_global_config()
1525 {
1526 GKeyFile* kf = g_key_file_new();
1527 char* file = NULL;
1528 gboolean loaded = FALSE;
1529 const gchar * const * dir = g_get_system_config_dirs();
1530
1531 /* try to load system config file first */
1532 if (dir) while (dir[0] && !loaded)
1533 {
1534 g_free(file);
1535 file = _system_config_file_name(dir[0], "config");
1536 if (g_key_file_load_from_file(kf, file, 0, NULL))
1537 loaded = TRUE;
1538 dir++;
1539 }
1540 if (!loaded) /* fallback to old config place for backward compatibility */
1541 {
1542 g_free(file);
1543 file = _old_system_config_file_name("config");
1544 if (g_key_file_load_from_file(kf, file, 0, NULL))
1545 loaded = TRUE;
1546 }
1547 /* now try to load user config file */
1548 g_free(file);
1549 file = _user_config_file_name("config", NULL);
1550 if (g_key_file_load_from_file(kf, file, 0, NULL))
1551 loaded = TRUE;
1552 g_free(file);
1553
1554 if( loaded )
1555 {
1556 char *fm, *tmp;
1557 GList *apps, *l;
1558
1559 logout_cmd = g_key_file_get_string( kf, COMMAND_GROUP, "Logout", NULL );
1560 /* check for terminal setting on upgrade */
1561 if (fm_config->terminal == NULL)
1562 {
1563 fm_config->terminal = g_key_file_get_string(kf, COMMAND_GROUP,
1564 "Terminal", NULL);
1565 if (fm_config->terminal != NULL) /* setting changed, save it */
1566 fm_config_save(fm_config, NULL);
1567 }
1568 /* this is heavy but fortunately it will be ran only once: on upgrade */
1569 fm = g_key_file_get_string(kf, COMMAND_GROUP, "FileManager", NULL);
1570 if (fm)
1571 {
1572 tmp = strchr(fm, ' '); /* chop params */
1573 if (tmp)
1574 *tmp = '\0';
1575 tmp = strrchr(fm, '/'); /* use only basename */
1576 if (tmp)
1577 tmp++;
1578 else
1579 tmp = fm;
1580 tmp = g_strdup_printf("%s.desktop", tmp); /* generate desktop id */
1581 g_free(fm);
1582 apps = g_app_info_get_all_for_type("inode/directory");
1583 for (l = apps; l; l = l->next) /* scan all known applications */
1584 if (strcmp(tmp, g_app_info_get_id(l->data)) == 0)
1585 break;
1586 if (l != NULL) /* found */
1587 g_app_info_set_as_default_for_type(l->data, "inode/directory",
1588 NULL);
1589 else
1590 g_warning("the %s is not valid desktop id of file manager", tmp);
1591 for (l = apps; l; l = l->next) /* free retrieved data */
1592 g_object_unref(l->data);
1593 g_list_free(apps);
1594 g_free(tmp);
1595 save_global_config();
1596 }
1597 }
1598 g_key_file_free( kf );
1599 }
1600
1601 static void save_global_config()
1602 {
1603 char* file = _user_config_file_name("config", NULL);
1604 FILE* f = fopen( file, "w" );
1605 if( f )
1606 {
1607 fprintf( f, "[" COMMAND_GROUP "]\n");
1608 if( logout_cmd )
1609 fprintf( f, "Logout=%s\n", logout_cmd );
1610 fclose( f );
1611 }
1612 g_free(file);
1613 }
1614
1615 void free_global_config()
1616 {
1617 g_free( logout_cmd );
1618 }
1619
1620 /* this is dirty and should be removed later */
1621 const char*
1622 lxpanel_get_file_manager()
1623 {
1624 GAppInfo *app = g_app_info_get_default_for_type("inode/directory", TRUE);
1625 static char *exec = NULL;
1626 const char *c, *x;
1627
1628 if (!app)
1629 return "pcmanfm %s";
1630 c = g_app_info_get_commandline(app);
1631 x = strchr(c, ' '); /* skip all arguments */
1632 g_free(exec);
1633 if (x)
1634 exec = g_strndup(c, x - c);
1635 else
1636 exec = g_strdup(c);
1637 return exec;
1638 }
1639
1640 /* vim: set sw=4 et sts=4 : */