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