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