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