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 const char grp[]= "Desktop Entry";
50 GtkListStore* autostart_list = NULL;
51
is_desktop_file_enabled(GKeyFile * kf,int * flags,const char * session_name)52 gboolean is_desktop_file_enabled(GKeyFile* kf, int *flags, const char* session_name)
53 {
54 char** not_show_in;
55 char** only_show_in;
56 gsize n, i;
57
58 *flags = 0;
59
60 not_show_in = g_key_file_get_string_list(kf, grp, "NotShowIn", &n, NULL);
61 if( not_show_in )
62 {
63 *flags |= NOT_SHOW_IN;
64 for( i = 0; i < n; ++i )
65 {
66 if(strcmp(not_show_in[i], session_name) == 0)
67 {
68 g_strfreev(not_show_in);
69 return FALSE;
70 }
71 }
72 g_strfreev(not_show_in);
73 }
74
75 only_show_in = g_key_file_get_string_list(kf, grp, "OnlyShowIn", &n, NULL);
76 if( only_show_in )
77 {
78 *flags |= ONLY_SHOW_IN;
79 for( i = 0; i < n; ++i )
80 if(strcmp(only_show_in[i], session_name) == 0)
81 break;
82 g_strfreev(only_show_in);
83 if( i >= n )
84 return FALSE;
85 }
86
87 return TRUE;
88 }
89
is_desktop_file_valid(GKeyFile * kf)90 gboolean is_desktop_file_valid(GKeyFile* kf)
91 {
92 char* tmp;
93 if( g_key_file_get_boolean(kf, grp, "Hidden", NULL) )
94 return FALSE;
95
96 if( (tmp = g_key_file_get_string(kf, grp, "Type", NULL)) != NULL )
97 {
98 if( strcmp(tmp, "Application") )
99 {
100 g_free(tmp);
101 return FALSE;
102 }
103 g_free(tmp);
104 }
105
106 if( (tmp = g_key_file_get_string(kf, grp, "TryExec", NULL)) != NULL )
107 {
108 char* prg = g_find_program_in_path(tmp);
109 g_free(tmp);
110 if(!prg)
111 return FALSE;
112 g_free(prg);
113 }
114 return TRUE;
115 }
116
get_autostart_files_in_dir(GHashTable * hash,const char * session_name,const char * base_dir)117 void get_autostart_files_in_dir( GHashTable* hash, const char* session_name, const char* base_dir )
118 {
119 char* dir_path = g_build_filename( base_dir, "autostart", NULL );
120 GDir* dir = g_dir_open( dir_path, 0, NULL );
121 if( dir )
122 {
123 char *path;
124 const char *name;
125
126 while( name = g_dir_read_name(dir) )
127 {
128 if(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 }
134 g_dir_close( dir );
135 }
136 g_free( dir_path );
137 }
138
add_autostart_file(char * desktop_id,char * file,GKeyFile * kf)139 void add_autostart_file(char* desktop_id, char* file, GKeyFile* kf)
140 {
141 GtkTreeIter it;
142
143 const char* session_name_local = NULL;
144
145 session_name_local = g_getenv("XDG_CURRENT_DESKTOP");
146 if(!session_name_local)
147 {
148 session_name_local = g_getenv("DESKTOP_SESSION");
149 if( G_UNLIKELY(!session_name_local) )
150 session_name_local = "LXDE";
151 }
152
153 if( g_key_file_load_from_file( kf, file, 0, NULL ) )
154 {
155 if( is_desktop_file_valid(kf) )
156 {
157 char* name = g_key_file_get_locale_string(kf, grp, "Name", NULL, NULL);
158 char* icon = g_key_file_get_locale_string(kf, grp, "Icon", NULL, NULL);
159 char* comment = g_key_file_get_locale_string(kf, grp, "Comment", NULL, NULL);
160 int flags;
161 gboolean enabled = is_desktop_file_enabled(kf, &flags, session_name_local);
162 if( enabled )
163 flags |= ORIGINALLY_ENABLED;
164 gtk_list_store_append(autostart_list, &it);
165 gtk_list_store_set( autostart_list, &it,
166 COL_ENABLED, enabled,
167 COL_NAME, name,
168 /* COL_ICON, pix, */
169 COL_COMMENT, comment,
170 COL_DESKTOP_ID, desktop_id,
171 COL_SRC_FILE, file,
172 COL_FLAGS, flags,
173 -1 );
174 g_free(name);
175 g_free(icon);
176 g_free(comment);
177 }
178 }
179 }
180
load_autostart(const char * session_name)181 void load_autostart(const char* session_name)
182 {
183 const char* const *dirs = g_get_system_config_dirs();
184 const char* const *dir;
185 GHashTable* hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free );
186
187 /* get system-wide autostart files */
188 for( dir = dirs; *dir; ++dir )
189 get_autostart_files_in_dir( hash, session_name, *dir );
190
191 /* get user-specific autostart files */
192 get_autostart_files_in_dir( hash, session_name, g_get_user_config_dir() );
193
194 if( g_hash_table_size( hash ) > 0 )
195 {
196 GKeyFile* kf = g_key_file_new();
197 g_hash_table_foreach( hash, (GHFunc)add_autostart_file, kf );
198 g_key_file_free( kf );
199 }
200 g_hash_table_destroy( hash );
201 }
202
203 /* FIXME:
204 * If the system-wide desktop file can meet our needs,
205 * remove the user-specific one instead of changing its key values. */
update_enable_state(GKeyFile * kf,gboolean enabled,int flags,const char * session_name)206 void update_enable_state(GKeyFile* kf, gboolean enabled, int flags, const char* session_name)
207 {
208 if( flags & NOT_SHOW_IN ) /* the desktop file contains NotShowIn key */
209 {
210 gsize n, i;
211 char** list = g_key_file_get_string_list(kf, grp, "NotShowIn", &n, NULL);
212 if( enabled ) /* remove our DE from NotShowIn */
213 {
214 for( i = 0; i < n; ++i )
215 {
216 if( strcmp(list[i], session_name) == 0 )
217 {
218 g_free(list[i]);
219 memcpy( list + i, list + i + 1, (n-i) * sizeof(char*) );
220 --n;
221 break;
222 }
223 }
224 }
225 else /* add our DE to NotShowIn */
226 {
227 ++n;
228 if( list )
229 list = g_realloc( list, sizeof(char*) * (n + 1) );
230 else
231 list = g_new( char*, n + 1 );
232 list[n-1] = g_strdup(session_name);
233 list[n] = NULL;
234 }
235 if( n > 0 )
236 g_key_file_set_string_list( kf, grp, "NotShowIn", (const gchar * const *) list, n );
237 else
238 g_key_file_remove_key(kf, grp, "NotShowIn", NULL);
239 g_strfreev(list);
240 }
241 else if( flags & ONLY_SHOW_IN )
242 {
243 gsize n, i;
244 char * * list = g_key_file_get_string_list(kf, grp, "OnlyShowIn", &n, NULL);
245 if( enabled ) /* add our DE to OnlyShowIn */
246 {
247 ++n;
248 if( list )
249 list = g_realloc( list, sizeof(char*) * (n + 1) );
250 else
251 list = g_new( char*, n + 1 );
252 list[n-1] = g_strdup(session_name);
253 list[n] = NULL;
254 }
255 else /* remove our DE to OnlyShowIn */
256 {
257 for( i = 0; i < n; ++i )
258 {
259 if( strcmp(list[i], session_name) == 0 )
260 {
261 g_free(list[i]);
262 memcpy( list + i, list + i + 1, (n-i) * sizeof(char*) );
263 --n;
264 break;
265 }
266 }
267 }
268 if( n > 0 )
269 g_key_file_set_string_list(kf, grp, "OnlyShowIn", (const gchar * const *) list, n );
270 else
271 g_key_file_remove_key(kf, grp, "OnlyShowIn", NULL);
272 g_strfreev(list);
273 }
274 else
275 {
276 if( !enabled )
277 {
278 char* list[2];
279 list[0] = (char *) session_name;
280 list[1] = NULL;
281 g_key_file_set_string_list( kf, grp, "NotShowIn", (const gchar * const *) list, 1);
282 }
283 else
284 {
285 /* nothing to do */
286 }
287 }
288 }
289
save_autostart(const char * session_name)290 void save_autostart(const char* session_name)
291 {
292 GtkTreeIter it;
293 GKeyFile* kf;
294 char* dirname;
295 if( ! gtk_tree_model_get_iter_first(GTK_TREE_MODEL(autostart_list), &it) )
296 return;
297
298 /* create user autostart dir */
299 dirname = g_build_filename(g_get_user_config_dir(), "autostart", NULL);
300 g_mkdir_with_parents(dirname, 0700);
301 g_free(dirname);
302
303 /* update desktop files in autostart dir */
304 kf = g_key_file_new();
305 do
306 {
307 int flags;
308 gboolean enabled;
309 gtk_tree_model_get( GTK_TREE_MODEL(autostart_list), &it,
310 COL_ENABLED, &enabled,
311 COL_FLAGS, &flags, -1);
312
313 /* enabled state is changed */
314 if( enabled != !!(flags & ORIGINALLY_ENABLED) )
315 {
316 char* desktop_id, *src_file;
317 gtk_tree_model_get( GTK_TREE_MODEL(autostart_list), &it,
318 COL_DESKTOP_ID, &desktop_id,
319 COL_SRC_FILE, &src_file,
320 -1);
321
322 /* load the source desktop file */
323 if( g_key_file_load_from_file( kf, src_file, G_KEY_FILE_KEEP_TRANSLATIONS, NULL) )
324 {
325 char* file, *data;
326 gsize len;
327 /* update enabled state */
328 update_enable_state(kf, enabled, flags, session_name);
329 data = g_key_file_to_data(kf, &len, NULL);
330 file = g_build_filename( g_get_user_config_dir(), "autostart", desktop_id, NULL );
331 /* save it to user-specific autostart dir */
332 g_debug("src:%s, save to: %s", src_file, file);
333 g_file_set_contents(file, data, len, NULL);
334 g_free(file);
335 g_free(data);
336 }
337 g_free(desktop_id);
338 g_free(src_file);
339 }
340 }while( gtk_tree_model_iter_next(GTK_TREE_MODEL(autostart_list), &it) );
341 g_key_file_free(kf);
342 }
343
on_enable_toggled(GtkCellRendererToggle * render,char * tp_str,gpointer user_data)344 void on_enable_toggled(GtkCellRendererToggle* render,
345 char* tp_str, gpointer user_data)
346 {
347 GtkTreePath* tp = gtk_tree_path_new_from_string(tp_str);
348 GtkTreeIter it;
349 if( gtk_tree_model_get_iter(GTK_TREE_MODEL(autostart_list), &it, tp) )
350 {
351 gboolean enabled;
352 gtk_tree_model_get(GTK_TREE_MODEL(autostart_list), &it, COL_ENABLED, &enabled, -1 );
353 gtk_list_store_set(autostart_list, &it, COL_ENABLED, !enabled, -1 );
354 }
355
356 const char* session_name = NULL;
357 session_name = g_getenv("XDG_CURRENT_DESKTOP");
358 if(!session_name)
359 {
360 session_name = g_getenv("DESKTOP_SESSION");
361 if( G_UNLIKELY(!session_name) )
362 session_name = "LXDE";
363 }
364
365 save_autostart(session_name);
366
367 gtk_tree_path_free(tp);
368 }
369
init_list_view(GtkTreeView * view)370 void init_list_view( GtkTreeView* view )
371 {
372
373 GtkTreeViewColumn* col;
374 GtkCellRenderer* render;
375 autostart_list = gtk_list_store_new(N_COLS,
376 G_TYPE_BOOLEAN,
377 GDK_TYPE_PIXBUF,
378 G_TYPE_STRING,
379 G_TYPE_STRING,
380 G_TYPE_STRING,
381 G_TYPE_STRING,
382 G_TYPE_INT );
383
384 render = gtk_cell_renderer_toggle_new();
385 col = gtk_tree_view_column_new_with_attributes(_("Enabled"), render, "active", COL_ENABLED, NULL );
386 gtk_tree_view_append_column(view, col);
387 g_signal_connect(render, "toggled", G_CALLBACK(on_enable_toggled), NULL);
388
389 render = gtk_cell_renderer_pixbuf_new();
390 col = gtk_tree_view_column_new_with_attributes(_("Application"), render, "pixbuf", COL_ICON, NULL );
391 gtk_tree_view_append_column(view, col);
392
393 render = gtk_cell_renderer_text_new();
394 gtk_tree_view_column_pack_start( col, render, TRUE );
395 gtk_tree_view_column_set_attributes( col, render, "text", COL_NAME, NULL );
396 /*
397 Remove the comment, make the windows huge
398 render = gtk_cell_renderer_text_new();
399 col = gtk_tree_view_column_new_with_attributes(_("Comment"), render, "text", COL_COMMENT, NULL );
400 gtk_tree_view_append_column(view, col);
401 */
402 }
403
get_autostart_list()404 GtkListStore* get_autostart_list ()
405 {
406 return autostart_list;
407 }
408