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