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