Merging upstream version 0.2.0.
[debian/lxsession-edit.git] / src / lxsession-edit.c
CommitLineData
b67758d2
DB
1/*
2 * lxsession-edit.c
3 *
4 * Copyright 2008 PCMan <pcman.tw@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <gtk/gtk.h>
27#include <glib/gi18n.h>
28#include <stdio.h>
29#include <string.h>
30
63c2f36c
DB
31#define CONFIG_FILE_NAME "desktop.conf"
32
b67758d2
DB
33enum {
34 COL_ENABLED,
35 COL_ICON,
36 COL_NAME,
37 COL_COMMENT,
38 COL_DESKTOP_ID,
39 COL_SRC_FILE,
40 COL_FLAGS,
41 N_COLS
42};
43
44enum {
45 NONE = 0,
46 NOT_SHOW_IN = 1 << 0,
47 ONLY_SHOW_IN = 1 << 1,
48 ORIGINALLY_ENABLED = 1 << 15
49};
50
63c2f36c 51static const char* session_name = NULL;
b67758d2
DB
52static GtkListStore* autostart_list = NULL;
53static const char grp[] = "Desktop Entry";
54
55
56static gboolean is_desktop_file_enabled(GKeyFile* kf, int *flags)
57{
58 char** not_show_in;
59 char** only_show_in;
60 gsize n, i;
61
62 *flags = 0;
63
64 not_show_in = g_key_file_get_string_list(kf, grp, "NotShowIn", &n, NULL);
65 if( not_show_in )
66 {
67 *flags |= NOT_SHOW_IN;
68 for( i = 0; i < n; ++i )
69 {
70 if(strcmp(not_show_in[i], session_name) == 0)
71 {
72 g_strfreev(not_show_in);
73 return FALSE;
74 }
75 }
76 g_strfreev(not_show_in);
77 }
78
79 only_show_in = g_key_file_get_string_list(kf, grp, "OnlyShowIn", &n, NULL);
80 if( only_show_in )
81 {
82 *flags |= ONLY_SHOW_IN;
83 for( i = 0; i < n; ++i )
84 if(strcmp(only_show_in[i], session_name) == 0)
85 break;
86 g_strfreev(only_show_in);
87 if( i >= n )
88 return FALSE;
89 }
90
91 return TRUE;
92}
93
94static gboolean is_desktop_file_valid(GKeyFile* kf)
95{
96 char* tmp;
97 if( g_key_file_get_boolean(kf, grp, "Hidden", NULL) )
98 return FALSE;
99
63c2f36c 100 if( (tmp = g_key_file_get_string(kf, grp, "Type", NULL)) != NULL )
b67758d2
DB
101 {
102 if( strcmp(tmp, "Application") )
103 {
104 g_free(tmp);
105 return FALSE;
106 }
107 g_free(tmp);
108 }
109
63c2f36c 110 if( (tmp = g_key_file_get_string(kf, grp, "TryExec", NULL)) != NULL )
b67758d2
DB
111 {
112 char* prg = g_find_program_in_path(tmp);
113 g_free(tmp);
114 if(!prg)
115 return FALSE;
116 g_free(prg);
117 }
118 return TRUE;
119}
120
121static void get_autostart_files_in_dir( GHashTable* hash, const char* session_name, const char* base_dir )
122{
123 char* dir_path = g_build_filename( base_dir, "autostart", NULL );
124 GDir* dir = g_dir_open( dir_path, 0, NULL );
125 if( dir )
126 {
127 char *path;
128 const char *name;
129
130 while( (name = g_dir_read_name( dir )) && g_str_has_suffix( name, ".desktop" ) )
131 {
132 path = g_build_filename( dir_path, name, NULL );
133 g_hash_table_replace( hash, g_strdup(name), path );
134 }
135 g_dir_close( dir );
136 }
137 g_free( dir_path );
138}
139
140static void add_autostart_file(char* desktop_id, char* file, GKeyFile* kf)
141{
142 GtkTreeIter it;
143 if( g_key_file_load_from_file( kf, file, 0, NULL ) )
144 {
145 if( is_desktop_file_valid(kf) )
146 {
147 char* name = g_key_file_get_locale_string(kf, grp, "Name", NULL, NULL);
148 char* icon = g_key_file_get_locale_string(kf, grp, "Icon", NULL, NULL);
149 char* comment = g_key_file_get_locale_string(kf, grp, "Comment", NULL, NULL);
150 int flags;
151 gboolean enabled = is_desktop_file_enabled(kf, &flags);
152 if( enabled )
153 flags |= ORIGINALLY_ENABLED;
154 gtk_list_store_append(autostart_list, &it);
155 gtk_list_store_set( autostart_list, &it,
156 COL_ENABLED, enabled,
157 COL_NAME, name,
158 /* COL_ICON, pix, */
159 COL_COMMENT, comment,
160 COL_DESKTOP_ID, desktop_id,
161 COL_SRC_FILE, file,
162 COL_FLAGS, flags,
163 -1 );
164 g_free(name);
165 g_free(icon);
166 g_free(comment);
167 }
168 }
169}
170
171static void load_autostart()
172{
173 const char* const *dirs = g_get_system_config_dirs();
174 const char* const *dir;
175 GHashTable* hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free );
176
177 /* get system-wide autostart files */
178 for( dir = dirs; *dir; ++dir )
179 get_autostart_files_in_dir( hash, session_name, *dir );
180
181 /* get user-specific autostart files */
182 get_autostart_files_in_dir( hash, session_name, g_get_user_config_dir() );
183
184 if( g_hash_table_size( hash ) > 0 )
185 {
186 GKeyFile* kf = g_key_file_new();
187 g_hash_table_foreach( hash, (GHFunc)add_autostart_file, kf );
188 g_key_file_free( kf );
189 }
190 g_hash_table_destroy( hash );
191}
192
193/* FIXME:
194 * If the system-wide desktop file can meet our needs,
195 * remove the user-specific one instead of changing its key values. */
196static void update_enable_state(GKeyFile* kf, gboolean enabled, int flags)
197{
198 if( flags & NOT_SHOW_IN ) /* the desktop file contains NotShowIn key */
199 {
200 gsize n, i;
201 char** list = g_key_file_get_string_list(kf, grp, "NotShowIn", &n, NULL);
202 if( enabled ) /* remove our DE from NotShowIn */
203 {
204 for( i = 0; i < n; ++i )
205 {
206 if( strcmp(list[i], session_name) == 0 )
207 {
208 g_free(list[i]);
209 memcpy( list + i, list + i + 1, (n-i) * sizeof(char*) );
210 --n;
211 break;
212 }
213 }
214 }
215 else /* add our DE to NotShowIn */
216 {
217 ++n;
218 if( list )
219 list = g_realloc( list, sizeof(char*) * (n + 1) );
220 else
221 list = g_new( char*, n + 1 );
222 list[n-1] = g_strdup(session_name);
223 list[n] = NULL;
224 }
225 if( n > 0 )
63c2f36c 226 g_key_file_set_string_list( kf, grp, "NotShowIn", (const gchar * const *) list, n );
b67758d2
DB
227 else
228 g_key_file_remove_key(kf, grp, "NotShowIn", NULL);
229 g_strfreev(list);
230 }
231 else if( flags & ONLY_SHOW_IN )
232 {
233 gsize n, i;
63c2f36c 234 char * * list = g_key_file_get_string_list(kf, grp, "OnlyShowIn", &n, NULL);
b67758d2
DB
235 if( enabled ) /* add our DE to OnlyShowIn */
236 {
237 ++n;
238 if( list )
239 list = g_realloc( list, sizeof(char*) * (n + 1) );
240 else
241 list = g_new( char*, n + 1 );
242 list[n-1] = g_strdup(session_name);
243 list[n] = NULL;
244 }
245 else /* remove our DE to OnlyShowIn */
246 {
247 for( i = 0; i < n; ++i )
248 {
249 if( strcmp(list[i], session_name) == 0 )
250 {
251 g_free(list[i]);
252 memcpy( list + i, list + i + 1, (n-i) * sizeof(char*) );
253 --n;
254 break;
255 }
256 }
257 }
258 if( n > 0 )
63c2f36c 259 g_key_file_set_string_list(kf, grp, "OnlyShowIn", (const gchar * const *) list, n );
b67758d2
DB
260 else
261 g_key_file_remove_key(kf, grp, "OnlyShowIn", NULL);
262 g_strfreev(list);
263 }
264 else
265 {
266 if( !enabled )
267 {
63c2f36c
DB
268 char* list[2];
269 list[0] = (char *) session_name;
b67758d2 270 list[1] = NULL;
63c2f36c 271 g_key_file_set_string_list( kf, grp, "NotShowIn", (const gchar * const *) list, 1);
b67758d2
DB
272 }
273 else
274 {
275 /* nothing to do */
276 }
277 }
278}
279
280static void save_autostart()
281{
282 GtkTreeIter it;
283 GKeyFile* kf;
284 char* dirname;
63c2f36c 285 if( ! gtk_tree_model_get_iter_first(GTK_TREE_MODEL(autostart_list), &it) )
b67758d2
DB
286 return;
287
288 /* create user autostart dir */
289 dirname = g_build_filename(g_get_user_config_dir(), "autostart", NULL);
290 g_mkdir_with_parents(dirname, 0700);
291 g_free(dirname);
292
293 /* update desktop files in autostart dir */
294 kf = g_key_file_new();
295 do
296 {
297 int flags;
298 gboolean enabled;
63c2f36c 299 gtk_tree_model_get( GTK_TREE_MODEL(autostart_list), &it,
b67758d2
DB
300 COL_ENABLED, &enabled,
301 COL_FLAGS, &flags, -1);
302
303 /* enabled state is changed */
304 if( enabled != !!(flags & ORIGINALLY_ENABLED) )
305 {
306 char* desktop_id, *src_file;
63c2f36c 307 gtk_tree_model_get( GTK_TREE_MODEL(autostart_list), &it,
b67758d2
DB
308 COL_DESKTOP_ID, &desktop_id,
309 COL_SRC_FILE, &src_file,
310 -1);
311
312 /* load the source desktop file */
313 if( g_key_file_load_from_file( kf, src_file, G_KEY_FILE_KEEP_TRANSLATIONS, NULL) )
314 {
315 char* file, *data;
316 gsize len;
317 /* update enabled state */
318 update_enable_state(kf, enabled, flags);
319 data = g_key_file_to_data(kf, &len, NULL);
320 file = g_build_filename( g_get_user_config_dir(), "autostart", desktop_id, NULL );
321 /* save it to user-specific autostart dir */
322 g_debug("src:%s, save to: %s", src_file, file);
323 g_file_set_contents(file, data, len, NULL);
324 g_free(file);
325 g_free(data);
326 }
327 g_free(desktop_id);
328 g_free(src_file);
329 }
63c2f36c 330 }while( gtk_tree_model_iter_next(GTK_TREE_MODEL(autostart_list), &it) );
b67758d2
DB
331 g_key_file_free(kf);
332}
333
334static void on_enable_toggled(GtkCellRendererToggle* render,
335 char* tp_str, gpointer user_data)
336{
337 GtkTreePath* tp = gtk_tree_path_new_from_string(tp_str);
338 GtkTreeIter it;
63c2f36c 339 if( gtk_tree_model_get_iter(GTK_TREE_MODEL(autostart_list), &it, tp) )
b67758d2
DB
340 {
341 gboolean enabled;
63c2f36c 342 gtk_tree_model_get(GTK_TREE_MODEL(autostart_list), &it, COL_ENABLED, &enabled, -1 );
b67758d2
DB
343 gtk_list_store_set(autostart_list, &it, COL_ENABLED, !enabled, -1 );
344 }
345 gtk_tree_path_free(tp);
346}
347
348static void init_list_view( GtkTreeView* view )
349{
350 GtkTreeViewColumn* col;
351 GtkCellRenderer* render;
352 autostart_list = gtk_list_store_new(N_COLS,
353 G_TYPE_BOOLEAN,
354 GDK_TYPE_PIXBUF,
355 G_TYPE_STRING,
356 G_TYPE_STRING,
357 G_TYPE_STRING,
358 G_TYPE_STRING,
359 G_TYPE_INT );
360
361 render = gtk_cell_renderer_toggle_new();
362 col = gtk_tree_view_column_new_with_attributes(_("Enabled"), render, "active", COL_ENABLED, NULL );
363 gtk_tree_view_append_column(view, col);
364 g_signal_connect(render, "toggled", G_CALLBACK(on_enable_toggled), NULL);
365
366 render = gtk_cell_renderer_pixbuf_new();
367 col = gtk_tree_view_column_new_with_attributes(_("Application"), render, "pixbuf", COL_ICON, NULL );
368 gtk_tree_view_append_column(view, col);
369
370 render = gtk_cell_renderer_text_new();
371 gtk_tree_view_column_pack_start( col, render, TRUE );
372 gtk_tree_view_column_set_attributes( col, render, "text", COL_NAME, NULL );
373
374 render = gtk_cell_renderer_text_new();
375 col = gtk_tree_view_column_new_with_attributes(_("Comment"), render, "text", COL_COMMENT, NULL );
376 gtk_tree_view_append_column(view, col);
377}
378
379int main(int argc, char** argv)
380{
381 GtkBuilder *builder;
382 GtkWidget *dlg, *autostarts, *wm, *adv_page;
383 GKeyFile* kf;
384 char *cfg, *wm_cmd = NULL;
385 gboolean loaded;
386
387#ifdef ENABLE_NLS
388 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
389 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
390 textdomain ( GETTEXT_PACKAGE );
391#endif
392
393 gtk_init( &argc, &argv );
394 if( argc > 1 )
395 session_name = argv[1];
63c2f36c
DB
396 else
397 session_name = g_getenv("XDG_CURRENT_DESKTOP");
398
399 if( G_UNLIKELY(!session_name) )
400 session_name = "LXDE";
b67758d2
DB
401
402 builder = gtk_builder_new();
403 if( !gtk_builder_add_from_file( builder, PACKAGE_DATA_DIR "/lxsession-edit/lxsession-edit.ui", NULL ) )
404 return 1;
405
2532e841
DB
406 dlg = (GtkWidget*) gtk_builder_get_object( builder, "dlg" );
407 autostarts = (GtkWidget*) gtk_builder_get_object( builder, "autostarts" );
408 adv_page = (GtkWidget*) gtk_builder_get_object( builder, "adv_page" );
409 wm = (GtkWidget*) gtk_builder_get_object( builder, "wm" );
b67758d2
DB
410 g_object_unref(builder);
411
2532e841 412 gtk_dialog_set_alternative_button_order((GtkDialog*)dlg, GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1);
b67758d2 413
63c2f36c
DB
414 /* Set icon name for main (dlg) window so it displays in the panel. */
415 gtk_window_set_icon_name(GTK_WINDOW(dlg), "xfwm4");
416
b67758d2 417 /* autostart list */
2532e841 418 init_list_view((GtkTreeView*)autostarts);
b67758d2 419 load_autostart();
2532e841 420 gtk_tree_view_set_model( (GtkTreeView*)autostarts, (GtkTreeModel*)autostart_list );
b67758d2
DB
421
422 /* if we are running under LXSession */
423 if( g_getenv("_LXSESSION_PID") )
424 {
425 /* wm settings (only show this when we are under lxsession) */
426 kf = g_key_file_new();
63c2f36c 427 cfg = g_build_filename( g_get_user_config_dir(), "lxsession", session_name, CONFIG_FILE_NAME, NULL );
b67758d2
DB
428 loaded = g_key_file_load_from_file(kf, cfg, 0, NULL);
429 if( !loaded )
430 {
431 const char* const *dirs = g_get_system_config_dirs();
432 const char* const *dir;
433 g_free(cfg);
434 for( dir = dirs; *dir; ++dir )
435 {
63c2f36c 436 cfg = g_build_filename( *dir, "lxsession", session_name, CONFIG_FILE_NAME, NULL );
b67758d2
DB
437 loaded = g_key_file_load_from_file(kf, cfg, 0, NULL);
438 g_free( cfg );
439 if( loaded )
440 break;
441 }
442 }
443 if( loaded )
444 wm_cmd = g_key_file_get_string(kf, "Session", "window_manager", NULL);
445
446 if( ! wm_cmd || !*wm_cmd )
447 {
448 g_free(wm_cmd);
449 /* If it's our favorite, LXDE */
63c2f36c 450 if( strcmp(session_name, "LXDE") == 0 )
b67758d2
DB
451 wm_cmd = g_strdup("openbox-lxde");
452 else
453 wm_cmd = g_strdup("openbox");
454 }
2532e841 455 gtk_entry_set_text((GtkEntry*)wm, wm_cmd);
b67758d2
DB
456 }
457 else
458 {
459 gtk_widget_destroy(adv_page);
460 wm = adv_page = NULL;
461 wm_cmd = NULL;
462 }
463
2532e841 464 if( gtk_dialog_run((GtkDialog*)dlg) == GTK_RESPONSE_OK )
b67758d2
DB
465 {
466 save_autostart();
467
63c2f36c 468 if( wm ) /* if wm settings is available. */
b67758d2
DB
469 {
470 char* dir;
471 dir = g_build_filename( g_get_user_config_dir(), "lxsession", session_name, NULL );
472 g_mkdir_with_parents( dir, 0700 );
63c2f36c 473 cfg = g_build_filename( dir, "desktop.conf", NULL );
b67758d2 474 g_free( dir );
2532e841 475 wm_cmd = (char*)gtk_entry_get_text((GtkEntry*)wm);
b67758d2
DB
476 if( wm_cmd )
477 {
478 char* data;
479 gsize len;
480 g_key_file_set_string( kf, "Session", "window_manager", wm_cmd );
481 data = g_key_file_to_data(kf, &len, NULL);
482 g_file_set_contents(cfg, data, len, NULL);
483 g_free( wm_cmd );
484 }
485 }
486 }
487 g_key_file_free(kf);
488
489 gtk_widget_destroy(dlg);
490 return 0;
491}