1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 1999 Free Software Foundation, Inc.
4 * Copyright (C) 2007, 2009 Vincent Untz.
5 * Copyright (C) 2008 Lucas Rocha.
6 * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
7 * Copyright (C) 2012-2021 MATE Developers
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <sys/stat.h>
31
32 #include <glib/gi18n.h>
33 #include <glib/gstdio.h>
34
35 #include "gsm-app-dialog.h"
36 #include "gsm-properties-dialog.h"
37 #include "gsm-util.h"
38 #include "gsp-app-manager.h"
39 #include "gsp-keyfile.h"
40
41 #include "gsp-app.h"
42
43 #define GSP_APP_SAVE_DELAY 2
44
45 #define GSP_ASP_SAVE_MASK_HIDDEN 0x0001
46 #define GSP_ASP_SAVE_MASK_NAME 0x0002
47 #define GSP_ASP_SAVE_MASK_EXEC 0x0004
48 #define GSP_ASP_SAVE_MASK_COMMENT 0x0008
49 #define GSP_ASP_SAVE_MASK_DELAY 0x0010
50 #define GSP_ASP_SAVE_MASK_ALL 0xffff
51
52 typedef struct {
53 char *basename;
54 char *path;
55
56 gboolean hidden;
57 gboolean nodisplay;
58
59 char *name;
60 char *exec;
61 char *comment;
62 char *icon;
63 gint delay;
64
65 GIcon *gicon;
66 char *description;
67
68 /* position of the directory in the XDG environment variable */
69 unsigned int xdg_position;
70 /* position of the first system directory in the XDG env var containing
71 * this autostart app too (G_MAXUINT means none) */
72 unsigned int xdg_system_position;
73
74 unsigned int save_timeout;
75 /* mask of what has changed */
76 unsigned int save_mask;
77 /* path that contains the original file that needs to be saved */
78 char *old_system_path;
79 /* after writing to file, we skip the next file monitor event of type
80 * CHANGED */
81 gboolean skip_next_monitor_event;
82 } GspAppPrivate;
83
84 enum {
85 CHANGED,
86 REMOVED,
87 LAST_SIGNAL
88 };
89
90 static guint gsp_app_signals[LAST_SIGNAL] = { 0 };
91
92
93 G_DEFINE_TYPE_WITH_PRIVATE (GspApp, gsp_app, G_TYPE_OBJECT)
94
95 static void gsp_app_dispose (GObject *object);
96 static void gsp_app_finalize (GObject *object);
97 static gboolean _gsp_app_save (gpointer data);
98
99
100 static gboolean
_gsp_str_equal(const char * a,const char * b)101 _gsp_str_equal (const char *a,
102 const char *b)
103 {
104 if (g_strcmp0 (a, b) == 0) {
105 return TRUE;
106 }
107
108 if (a && !b && a[0] == '\0') {
109 return TRUE;
110 }
111
112 if (b && !a && b[0] == '\0') {
113 return TRUE;
114 }
115
116 return FALSE;
117 }
118
119
120 static void
gsp_app_class_init(GspAppClass * class)121 gsp_app_class_init (GspAppClass *class)
122 {
123 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
124
125 gobject_class->dispose = gsp_app_dispose;
126 gobject_class->finalize = gsp_app_finalize;
127
128 gsp_app_signals[CHANGED] =
129 g_signal_new ("changed",
130 G_TYPE_FROM_CLASS (gobject_class),
131 G_SIGNAL_RUN_LAST,
132 G_STRUCT_OFFSET (GspAppClass,
133 changed),
134 NULL,
135 NULL,
136 g_cclosure_marshal_VOID__VOID,
137 G_TYPE_NONE, 0);
138
139 gsp_app_signals[REMOVED] =
140 g_signal_new ("removed",
141 G_TYPE_FROM_CLASS (gobject_class),
142 G_SIGNAL_RUN_LAST,
143 G_STRUCT_OFFSET (GspAppClass,
144 removed),
145 NULL,
146 NULL,
147 g_cclosure_marshal_VOID__VOID,
148 G_TYPE_NONE, 0);
149
150 }
151
152 static void
gsp_app_init(GspApp * app)153 gsp_app_init (GspApp *app)
154 {
155 GspAppPrivate *priv;
156
157 priv = gsp_app_get_instance_private (app);
158
159 memset (priv, 0, sizeof (GspAppPrivate));
160 priv->xdg_position = G_MAXUINT;
161 priv->xdg_system_position = G_MAXUINT;
162 }
163
164 static void
_gsp_app_free_reusable_data(GspApp * app)165 _gsp_app_free_reusable_data (GspApp *app)
166 {
167 GspAppPrivate *priv;
168
169 priv = gsp_app_get_instance_private (app);
170 if (priv->path) {
171 g_free (priv->path);
172 priv->path = NULL;
173 }
174
175 if (priv->name) {
176 g_free (priv->name);
177 priv->name = NULL;
178 }
179
180 if (priv->exec) {
181 g_free (priv->exec);
182 priv->exec = NULL;
183 }
184
185 if (priv->comment) {
186 g_free (priv->comment);
187 priv->comment = NULL;
188 }
189
190 if (priv->icon) {
191 g_free (priv->icon);
192 priv->icon = NULL;
193 }
194
195 if (priv->gicon) {
196 g_object_unref (priv->gicon);
197 priv->gicon = NULL;
198 }
199
200 if (priv->description) {
201 g_free (priv->description);
202 priv->description = NULL;
203 }
204
205 if (priv->old_system_path) {
206 g_free (priv->old_system_path);
207 priv->old_system_path = NULL;
208 }
209 }
210
211 static void
gsp_app_dispose(GObject * object)212 gsp_app_dispose (GObject *object)
213 {
214 GspApp *app;
215 GspAppPrivate *priv;
216
217 g_return_if_fail (object != NULL);
218 g_return_if_fail (GSP_IS_APP (object));
219
220 app = GSP_APP (object);
221 priv = gsp_app_get_instance_private (app);
222
223 /* we save in dispose since we might need to reference GspAppManager */
224 if (priv->save_timeout) {
225 g_source_remove (priv->save_timeout);
226 priv->save_timeout = 0;
227
228 /* save now */
229 _gsp_app_save (app);
230 }
231
232 G_OBJECT_CLASS (gsp_app_parent_class)->dispose (object);
233 }
234
235 static void
gsp_app_finalize(GObject * object)236 gsp_app_finalize (GObject *object)
237 {
238 GspApp *app;
239 GspAppPrivate *priv;
240
241 g_return_if_fail (object != NULL);
242 g_return_if_fail (GSP_IS_APP (object));
243
244 app = GSP_APP (object);
245 priv = gsp_app_get_instance_private (app);
246
247 if (priv->basename) {
248 g_free (priv->basename);
249 priv->basename = NULL;
250 }
251
252 _gsp_app_free_reusable_data (app);
253
254 G_OBJECT_CLASS (gsp_app_parent_class)->finalize (object);
255 }
256
257 static void
_gsp_app_emit_changed(GspApp * app)258 _gsp_app_emit_changed (GspApp *app)
259 {
260 g_signal_emit (G_OBJECT (app), gsp_app_signals[CHANGED], 0);
261 }
262
263 static void
_gsp_app_emit_removed(GspApp * app)264 _gsp_app_emit_removed (GspApp *app)
265 {
266 g_signal_emit (G_OBJECT (app), gsp_app_signals[REMOVED], 0);
267 }
268
269 static void
_gsp_app_update_description(GspApp * app)270 _gsp_app_update_description (GspApp *app)
271 {
272 const char *primary;
273 const char *secondary;
274 GspAppPrivate *priv;
275
276 priv = gsp_app_get_instance_private (app);
277
278 if (!gsm_util_text_is_blank (priv->name)) {
279 primary = priv->name;
280 } else if (!gsm_util_text_is_blank (priv->exec)) {
281 primary = priv->exec;
282 } else {
283 primary = _("No name");
284 }
285
286 if (!gsm_util_text_is_blank (priv->comment)) {
287 secondary = priv->comment;
288 } else {
289 secondary = _("No description");
290 }
291
292 g_free (priv->description);
293 priv->description = g_markup_printf_escaped ("<b>%s</b>\n%s",
294 primary,
295 secondary);
296 }
297
298 /*
299 * Saving
300 */
301
302 static void
_gsp_ensure_user_autostart_dir(void)303 _gsp_ensure_user_autostart_dir (void)
304 {
305 char *dir;
306
307 dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
308 g_mkdir_with_parents (dir, S_IRWXU);
309
310 g_free (dir);
311 }
312
313 static gboolean
_gsp_app_user_equal_system(GspApp * app,char ** system_path)314 _gsp_app_user_equal_system (GspApp *app,
315 char **system_path)
316 {
317 GspAppManager *manager;
318 GspAppPrivate *priv;
319 const char *system_dir;
320 char *path;
321 char *str;
322 GKeyFile *keyfile;
323 guint delay;
324
325 manager = gsp_app_manager_get ();
326 priv = gsp_app_get_instance_private (app);
327 system_dir = gsp_app_manager_get_dir (manager,
328 priv->xdg_system_position);
329 g_object_unref (manager);
330 if (!system_dir) {
331 return FALSE;
332 }
333
334 path = g_build_filename (system_dir, priv->basename, NULL);
335
336 keyfile = g_key_file_new ();
337 if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
338 g_free (path);
339 g_key_file_free (keyfile);
340 return FALSE;
341 }
342
343 if (gsp_key_file_get_boolean (keyfile,
344 G_KEY_FILE_DESKTOP_KEY_HIDDEN,
345 FALSE) != priv->hidden) {
346 g_free (path);
347 g_key_file_free (keyfile);
348 return FALSE;
349 }
350
351 str = gsp_key_file_get_locale_string (keyfile,
352 G_KEY_FILE_DESKTOP_KEY_NAME);
353 if (!_gsp_str_equal (str, priv->name)) {
354 g_free (str);
355 g_free (path);
356 g_key_file_free (keyfile);
357 return FALSE;
358 }
359 g_free (str);
360
361 str = gsp_key_file_get_locale_string (keyfile,
362 G_KEY_FILE_DESKTOP_KEY_COMMENT);
363 if (!_gsp_str_equal (str, priv->comment)) {
364 g_free (str);
365 g_free (path);
366 g_key_file_free (keyfile);
367 return FALSE;
368 }
369 g_free (str);
370
371 str = gsp_key_file_get_string (keyfile,
372 G_KEY_FILE_DESKTOP_KEY_EXEC);
373 if (!_gsp_str_equal (str, priv->exec)) {
374 g_free (str);
375 g_free (path);
376 g_key_file_free (keyfile);
377 return FALSE;
378 }
379 g_free (str);
380
381 str = gsp_key_file_get_locale_string (keyfile,
382 G_KEY_FILE_DESKTOP_KEY_ICON);
383 if (!_gsp_str_equal (str, priv->icon)) {
384 g_free (str);
385 g_free (path);
386 g_key_file_free (keyfile);
387 return FALSE;
388 }
389 g_free (str);
390
391 delay = gsp_key_file_get_delay(keyfile);
392 if (delay != priv->delay) {
393 g_free (path);
394 g_key_file_free (keyfile);
395 return FALSE;
396 }
397
398 g_key_file_free (keyfile);
399
400 *system_path = path;
401
402 return TRUE;
403 }
404
405 static inline void
_gsp_app_save_done_success(GspApp * app)406 _gsp_app_save_done_success (GspApp *app)
407 {
408 GspAppPrivate *priv;
409
410 priv = gsp_app_get_instance_private (app);
411 priv->save_mask = 0;
412
413 if (priv->old_system_path) {
414 g_free (priv->old_system_path);
415 priv->old_system_path = NULL;
416 }
417 }
418
419 static gboolean
_gsp_app_save(gpointer data)420 _gsp_app_save (gpointer data)
421 {
422 GspApp *app;
423 char *use_path;
424 GKeyFile *keyfile;
425 GError *error;
426 GspAppPrivate *priv;
427
428 app = GSP_APP (data);
429 priv = gsp_app_get_instance_private (app);
430
431 /* first check if removing the data from the user dir and using the
432 * data from the system dir is enough -- this helps us keep clean the
433 * user config dir by removing unneeded files */
434 if (_gsp_app_user_equal_system (app, &use_path)) {
435 if (g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
436 g_remove (priv->path);
437 }
438
439 g_free (priv->path);
440 priv->path = use_path;
441
442 priv->xdg_position = priv->xdg_system_position;
443
444 _gsp_app_save_done_success (app);
445 return FALSE;
446 }
447
448 if (priv->old_system_path)
449 use_path = priv->old_system_path;
450 else
451 use_path = priv->path;
452
453 keyfile = g_key_file_new ();
454
455 error = NULL;
456 g_key_file_load_from_file (keyfile, use_path,
457 G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,
458 &error);
459
460 if (error) {
461 g_error_free (error);
462 gsp_key_file_populate (keyfile);
463 }
464
465 if (priv->save_mask & GSP_ASP_SAVE_MASK_HIDDEN) {
466 gsp_key_file_set_boolean (keyfile,
467 G_KEY_FILE_DESKTOP_KEY_HIDDEN,
468 priv->hidden);
469 }
470
471 if (priv->save_mask & GSP_ASP_SAVE_MASK_NAME) {
472 gsp_key_file_set_locale_string (keyfile,
473 G_KEY_FILE_DESKTOP_KEY_NAME,
474 priv->name);
475 gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME);
476 }
477
478 if (priv->save_mask & GSP_ASP_SAVE_MASK_COMMENT) {
479 gsp_key_file_set_locale_string (keyfile,
480 G_KEY_FILE_DESKTOP_KEY_COMMENT,
481 priv->comment);
482 gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT);
483 }
484
485 if (priv->save_mask & GSP_ASP_SAVE_MASK_EXEC) {
486 gsp_key_file_set_string (keyfile,
487 G_KEY_FILE_DESKTOP_KEY_EXEC,
488 priv->exec);
489 }
490
491 if (priv->save_mask & GSP_ASP_SAVE_MASK_DELAY) {
492 gsp_key_file_set_delay (keyfile, priv->delay);
493 }
494
495 _gsp_ensure_user_autostart_dir ();
496 if (gsp_key_file_to_file (keyfile, priv->path, NULL)) {
497 priv->skip_next_monitor_event = TRUE;
498 _gsp_app_save_done_success (app);
499 } else {
500 g_warning ("Could not save %s file", priv->path);
501 }
502
503 g_key_file_free (keyfile);
504
505 priv->save_timeout = 0;
506 return FALSE;
507 }
508
509 static void
_gsp_app_queue_save(GspApp * app)510 _gsp_app_queue_save (GspApp *app)
511 {
512 GspAppPrivate *priv;
513
514 priv = gsp_app_get_instance_private (app);
515 if (priv->save_timeout) {
516 g_source_remove (priv->save_timeout);
517 priv->save_timeout = 0;
518 }
519
520 /* if the file was not in the user directory, then we'll create a copy
521 * there */
522 if (priv->xdg_position != 0) {
523 priv->xdg_position = 0;
524
525 if (priv->old_system_path == NULL) {
526 priv->old_system_path = priv->path;
527 /* if old_system_path was not NULL, then it means we
528 * tried to save and we failed; in that case, we want
529 * to try again and use the old file as a basis again */
530 }
531
532 priv->path = g_build_filename (g_get_user_config_dir (),
533 "autostart",
534 priv->basename, NULL);
535 }
536
537 priv->save_timeout = g_timeout_add_seconds (GSP_APP_SAVE_DELAY,
538 _gsp_app_save,
539 app);
540 }
541
542 /*
543 * Accessors
544 */
545
546 const char *
gsp_app_get_basename(GspApp * app)547 gsp_app_get_basename (GspApp *app)
548 {
549 GspAppPrivate *priv;
550
551 g_return_val_if_fail (GSP_IS_APP (app), NULL);
552
553 priv = gsp_app_get_instance_private (app);
554
555 return priv->basename;
556 }
557
558 const char *
gsp_app_get_path(GspApp * app)559 gsp_app_get_path (GspApp *app)
560 {
561 GspAppPrivate *priv;
562
563 g_return_val_if_fail (GSP_IS_APP (app), NULL);
564
565 priv = gsp_app_get_instance_private (app);
566
567 return priv->path;
568 }
569
570 gboolean
gsp_app_get_hidden(GspApp * app)571 gsp_app_get_hidden (GspApp *app)
572 {
573 GspAppPrivate *priv;
574
575 g_return_val_if_fail (GSP_IS_APP (app), FALSE);
576
577 priv = gsp_app_get_instance_private (app);
578
579 return priv->hidden;
580 }
581
582 void
gsp_app_set_hidden(GspApp * app,gboolean hidden)583 gsp_app_set_hidden (GspApp *app,
584 gboolean hidden)
585 {
586 GspAppPrivate *priv;
587
588 g_return_if_fail (GSP_IS_APP (app));
589
590 priv = gsp_app_get_instance_private (app);
591
592 if (hidden == priv->hidden) {
593 return;
594 }
595
596 priv->hidden = hidden;
597 priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
598
599 _gsp_app_queue_save (app);
600 _gsp_app_emit_changed (app);
601 }
602
603 gboolean
gsp_app_get_nodisplay(GspApp * app)604 gsp_app_get_nodisplay (GspApp *app)
605 {
606 GspAppPrivate *priv;
607
608 g_return_val_if_fail (GSP_IS_APP (app), FALSE);
609
610 priv = gsp_app_get_instance_private (app);
611
612 return priv->nodisplay;
613 }
614
615 const char *
gsp_app_get_name(GspApp * app)616 gsp_app_get_name (GspApp *app)
617 {
618 GspAppPrivate *priv;
619
620 g_return_val_if_fail (GSP_IS_APP (app), NULL);
621
622 priv = gsp_app_get_instance_private (app);
623
624 return priv->name;
625 }
626
627 const char *
gsp_app_get_exec(GspApp * app)628 gsp_app_get_exec (GspApp *app)
629 {
630 GspAppPrivate *priv;
631
632 g_return_val_if_fail (GSP_IS_APP (app), NULL);
633
634 priv = gsp_app_get_instance_private (app);
635
636 return priv->exec;
637 }
638
639 const char *
gsp_app_get_comment(GspApp * app)640 gsp_app_get_comment (GspApp *app)
641 {
642 GspAppPrivate *priv;
643
644 g_return_val_if_fail (GSP_IS_APP (app), NULL);
645
646 priv = gsp_app_get_instance_private (app);
647
648 return priv->comment;
649 }
650
651 guint
gsp_app_get_delay(GspApp * app)652 gsp_app_get_delay (GspApp *app)
653 {
654 GspAppPrivate *priv;
655
656 g_return_val_if_fail (GSP_IS_APP (app), 0);
657 priv = gsp_app_get_instance_private (app);
658
659 return priv->delay;
660 }
661
662 GIcon *
gsp_app_get_icon(GspApp * app)663 gsp_app_get_icon (GspApp *app)
664 {
665 GspAppPrivate *priv;
666
667 g_return_val_if_fail (GSP_IS_APP (app), NULL);
668
669 priv = gsp_app_get_instance_private (app);
670
671 if (priv->gicon) {
672 return g_object_ref (priv->gicon);
673 } else {
674 return NULL;
675 }
676 }
677
678 unsigned int
gsp_app_get_xdg_position(GspApp * app)679 gsp_app_get_xdg_position (GspApp *app)
680 {
681 GspAppPrivate *priv;
682
683 g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
684
685 priv = gsp_app_get_instance_private (app);
686
687 return priv->xdg_position;
688 }
689
690 unsigned int
gsp_app_get_xdg_system_position(GspApp * app)691 gsp_app_get_xdg_system_position (GspApp *app)
692 {
693 GspAppPrivate *priv;
694
695 g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
696
697 priv = gsp_app_get_instance_private (app);
698
699 return priv->xdg_system_position;
700 }
701
702 void
gsp_app_set_xdg_system_position(GspApp * app,unsigned int position)703 gsp_app_set_xdg_system_position (GspApp *app,
704 unsigned int position)
705 {
706 GspAppPrivate *priv;
707
708 g_return_if_fail (GSP_IS_APP (app));
709
710 priv = gsp_app_get_instance_private (app);
711
712 priv->xdg_system_position = position;
713 }
714
715 const char *
gsp_app_get_description(GspApp * app)716 gsp_app_get_description (GspApp *app)
717 {
718 GspAppPrivate *priv;
719
720 g_return_val_if_fail (GSP_IS_APP (app), NULL);
721
722 priv = gsp_app_get_instance_private (app);
723
724 return priv->description;
725 }
726
727 /*
728 * High-level edition
729 */
730
731 void
gsp_app_update(GspApp * app,const char * name,const char * comment,const char * exec,guint delay)732 gsp_app_update (GspApp *app,
733 const char *name,
734 const char *comment,
735 const char *exec,
736 guint delay)
737 {
738 gboolean changed;
739 GspAppPrivate *priv;
740
741 g_return_if_fail (GSP_IS_APP (app));
742
743 changed = FALSE;
744 priv = gsp_app_get_instance_private (app);
745
746 if (!_gsp_str_equal (name, priv->name)) {
747 changed = TRUE;
748 g_free (priv->name);
749 priv->name = g_strdup (name);
750 priv->save_mask |= GSP_ASP_SAVE_MASK_NAME;
751 }
752
753 if (!_gsp_str_equal (comment, priv->comment)) {
754 changed = TRUE;
755 g_free (priv->comment);
756 priv->comment = g_strdup (comment);
757 priv->save_mask |= GSP_ASP_SAVE_MASK_COMMENT;
758 }
759
760 if (changed) {
761 _gsp_app_update_description (app);
762 }
763
764 if (!_gsp_str_equal (exec, priv->exec)) {
765 changed = TRUE;
766 g_free (priv->exec);
767 priv->exec = g_strdup (exec);
768 priv->save_mask |= GSP_ASP_SAVE_MASK_EXEC;
769 }
770
771 if ( delay != priv->delay) {
772 changed = TRUE;
773 priv->delay = delay;
774 priv->save_mask |= GSP_ASP_SAVE_MASK_DELAY;
775 }
776
777 if (changed) {
778 _gsp_app_queue_save (app);
779 _gsp_app_emit_changed (app);
780 }
781 }
782
783 void
gsp_app_delete(GspApp * app)784 gsp_app_delete (GspApp *app)
785 {
786 GspAppPrivate *priv;
787
788 g_return_if_fail (GSP_IS_APP (app));
789
790 priv = gsp_app_get_instance_private (app);
791 if (priv->xdg_position == 0 &&
792 priv->xdg_system_position == G_MAXUINT) {
793 /* exists in user directory only */
794 if (priv->save_timeout) {
795 g_source_remove (priv->save_timeout);
796 priv->save_timeout = 0;
797 }
798
799 if (g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
800 g_remove (priv->path);
801 }
802
803 /* for extra safety */
804 priv->hidden = TRUE;
805 priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
806
807 _gsp_app_emit_removed (app);
808 } else {
809 /* also exists in system directory, so we have to keep a file
810 * in the user directory */
811 priv->hidden = TRUE;
812 priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
813
814 _gsp_app_queue_save (app);
815 _gsp_app_emit_changed (app);
816 }
817 }
818
819 /*
820 * New autostart app
821 */
822
823 void
gsp_app_reload_at(GspApp * app,const char * path,unsigned int xdg_position)824 gsp_app_reload_at (GspApp *app,
825 const char *path,
826 unsigned int xdg_position)
827 {
828 GspAppPrivate *priv;
829
830 g_return_if_fail (GSP_IS_APP (app));
831
832 priv = gsp_app_get_instance_private (app);
833
834 priv->xdg_position = G_MAXUINT;
835 gsp_app_new (path, xdg_position);
836 }
837
838 static gboolean
gsp_app_can_launch(GKeyFile * keyfile)839 gsp_app_can_launch (GKeyFile *keyfile)
840 {
841 char **only_show_in, **not_show_in;
842 gboolean found;
843 int i;
844
845 only_show_in = g_key_file_get_string_list (keyfile,
846 G_KEY_FILE_DESKTOP_GROUP,
847 G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN,
848 NULL, NULL);
849 if (only_show_in) {
850 for (i = 0, found = FALSE; only_show_in[i] && !found; i++) {
851 if (!strcmp (only_show_in[i], "MATE"))
852 found = TRUE;
853 }
854 g_strfreev (only_show_in);
855 if (!found)
856 return FALSE;
857 }
858 not_show_in = g_key_file_get_string_list (keyfile,
859 G_KEY_FILE_DESKTOP_GROUP,
860 G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN,
861 NULL, NULL);
862 if (not_show_in) {
863 for (i = 0, found = FALSE; not_show_in[i] && !found; i++) {
864 if (!strcmp (not_show_in[i], "MATE"))
865 found = TRUE;
866 }
867 g_strfreev (not_show_in);
868 if (found)
869 return FALSE;
870 }
871 return TRUE;
872 }
873
874 GspApp *
gsp_app_new(const char * path,unsigned int xdg_position)875 gsp_app_new (const char *path,
876 unsigned int xdg_position)
877 {
878 GspAppManager *manager;
879 GspApp *app;
880 GKeyFile *keyfile;
881 char *basename;
882 gboolean new;
883 GspAppPrivate *priv;
884
885 basename = g_path_get_basename (path);
886
887 manager = gsp_app_manager_get ();
888 app = gsp_app_manager_find_app_with_basename (manager, basename);
889 priv = gsp_app_get_instance_private (app);
890 g_object_unref (manager);
891
892 new = (app == NULL);
893
894 if (!new) {
895 if (priv->xdg_position == xdg_position) {
896 if (priv->skip_next_monitor_event) {
897 priv->skip_next_monitor_event = FALSE;
898 return NULL;
899 }
900 /* else: the file got changed but not by us, we'll
901 * update our data from disk */
902 }
903
904 if (priv->xdg_position < xdg_position ||
905 priv->save_timeout != 0) {
906 /* we don't really care about this file, since we
907 * already have something with a higher priority, or
908 * we're going to write something in the user config
909 * anyway.
910 * Note: xdg_position >= 1 so it's a system dir */
911 priv->xdg_system_position = MIN (xdg_position,
912 priv->xdg_system_position);
913 return NULL;
914 }
915 }
916
917 keyfile = g_key_file_new ();
918 if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
919 g_key_file_free (keyfile);
920 g_free (basename);
921 return NULL;
922 }
923
924 if (!gsp_app_can_launch (keyfile)) {
925 g_key_file_free (keyfile);
926 g_free (basename);
927 return NULL;
928 }
929
930 if (new) {
931 app = g_object_new (GSP_TYPE_APP, NULL);
932 priv = gsp_app_get_instance_private (app);
933 priv->basename = basename;
934 } else {
935 g_free (basename);
936 _gsp_app_free_reusable_data (app);
937 }
938
939 priv->path = g_strdup (path);
940
941 priv->hidden = gsp_key_file_get_boolean (keyfile,
942 G_KEY_FILE_DESKTOP_KEY_HIDDEN,
943 FALSE);
944 priv->nodisplay = gsp_key_file_get_boolean (keyfile,
945 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
946 FALSE);
947 priv->name = gsp_key_file_get_locale_string (keyfile,
948 G_KEY_FILE_DESKTOP_KEY_NAME);
949 priv->exec = gsp_key_file_get_string (keyfile,
950 G_KEY_FILE_DESKTOP_KEY_EXEC);
951 priv->comment = gsp_key_file_get_locale_string (keyfile,
952 G_KEY_FILE_DESKTOP_KEY_COMMENT);
953 priv->delay = gsp_key_file_get_delay (keyfile);
954
955 if (gsm_util_text_is_blank (priv->name)) {
956 g_free (priv->name);
957 priv->name = g_strdup (priv->exec);
958 }
959
960 priv->icon = gsp_key_file_get_locale_string (keyfile,
961 G_KEY_FILE_DESKTOP_KEY_ICON);
962
963 if (priv->icon) {
964 /* look at icon and see if it's a themed icon or not */
965 if (g_path_is_absolute (priv->icon)) {
966 GFile *iconfile;
967
968 iconfile = g_file_new_for_path (priv->icon);
969 priv->gicon = g_file_icon_new (iconfile);
970 g_object_unref (iconfile);
971 } else {
972 priv->gicon = g_themed_icon_new (priv->icon);
973 }
974 } else {
975 priv->gicon = NULL;
976 }
977
978 g_key_file_free (keyfile);
979
980 _gsp_app_update_description (app);
981
982 if (xdg_position > 0) {
983 g_assert (xdg_position <= priv->xdg_system_position);
984 priv->xdg_system_position = xdg_position;
985 }
986 /* else we keep the old value (which is G_MAXUINT if it wasn't set) */
987 priv->xdg_position = xdg_position;
988
989 g_assert (!new || priv->save_timeout == 0);
990 priv->save_timeout = 0;
991 priv->old_system_path = NULL;
992 priv->skip_next_monitor_event = FALSE;
993
994 if (!new) {
995 _gsp_app_emit_changed (app);
996 }
997
998 return app;
999 }
1000
1001 static char *
_gsp_find_free_basename(const char * suggested_basename)1002 _gsp_find_free_basename (const char *suggested_basename)
1003 {
1004 GspAppManager *manager;
1005 char *base_path;
1006 char *filename;
1007 char *basename;
1008 int i;
1009
1010 if (g_str_has_suffix (suggested_basename, ".desktop")) {
1011 char *basename_no_ext;
1012
1013 basename_no_ext = g_strndup (suggested_basename,
1014 strlen (suggested_basename) - strlen (".desktop"));
1015 base_path = g_build_filename (g_get_user_config_dir (),
1016 "autostart",
1017 basename_no_ext, NULL);
1018 g_free (basename_no_ext);
1019 } else {
1020 base_path = g_build_filename (g_get_user_config_dir (),
1021 "autostart",
1022 suggested_basename, NULL);
1023 }
1024
1025 filename = g_strdup_printf ("%s.desktop", base_path);
1026 basename = g_path_get_basename (filename);
1027
1028 manager = gsp_app_manager_get ();
1029
1030 i = 1;
1031 #define _GSP_FIND_MAX_TRY 10000
1032 while (gsp_app_manager_find_app_with_basename (manager,
1033 basename) != NULL &&
1034 g_file_test (filename, G_FILE_TEST_EXISTS) &&
1035 i < _GSP_FIND_MAX_TRY) {
1036 g_free (filename);
1037 g_free (basename);
1038
1039 filename = g_strdup_printf ("%s-%d.desktop", base_path, i);
1040 basename = g_path_get_basename (filename);
1041
1042 i++;
1043 }
1044
1045 g_object_unref (manager);
1046
1047 g_free (base_path);
1048 g_free (filename);
1049
1050 if (i == _GSP_FIND_MAX_TRY) {
1051 g_free (basename);
1052 return NULL;
1053 }
1054
1055 return basename;
1056 }
1057
1058 void
gsp_app_create(const char * name,const char * comment,const char * exec,guint delay)1059 gsp_app_create (const char *name,
1060 const char *comment,
1061 const char *exec,
1062 guint delay)
1063 {
1064 GspAppManager *manager;
1065 GspAppPrivate *priv;
1066 GspApp *app;
1067 char *basename;
1068 char **argv;
1069 int argc;
1070
1071 g_return_if_fail (!gsm_util_text_is_blank (exec));
1072
1073 if (!g_shell_parse_argv (exec, &argc, &argv, NULL)) {
1074 return;
1075 }
1076
1077 basename = _gsp_find_free_basename (argv[0]);
1078 g_strfreev (argv);
1079 if (basename == NULL) {
1080 return;
1081 }
1082
1083 app = g_object_new (GSP_TYPE_APP, NULL);
1084 priv = gsp_app_get_instance_private (app);
1085
1086 priv->basename = basename;
1087 priv->path = g_build_filename (g_get_user_config_dir (),
1088 "autostart",
1089 priv->basename, NULL);
1090
1091 priv->hidden = FALSE;
1092 priv->nodisplay = FALSE;
1093
1094 if (!gsm_util_text_is_blank (name)) {
1095 priv->name = g_strdup (name);
1096 } else {
1097 priv->name = g_strdup (exec);
1098 }
1099 priv->exec = g_strdup (exec);
1100 priv->comment = g_strdup (comment);
1101 priv->delay = delay;
1102 priv->icon = NULL;
1103
1104 priv->gicon = NULL;
1105 _gsp_app_update_description (app);
1106
1107 /* by definition */
1108 priv->xdg_position = 0;
1109 priv->xdg_system_position = G_MAXUINT;
1110
1111 priv->save_timeout = 0;
1112 priv->save_mask |= GSP_ASP_SAVE_MASK_ALL;
1113 priv->old_system_path = NULL;
1114 priv->skip_next_monitor_event = FALSE;
1115
1116 _gsp_app_queue_save (app);
1117
1118 manager = gsp_app_manager_get ();
1119 gsp_app_manager_add (manager, app);
1120 g_object_unref (app);
1121 g_object_unref (manager);
1122 }
1123
1124 gboolean
gsp_app_copy_desktop_file(const char * uri)1125 gsp_app_copy_desktop_file (const char *uri)
1126 {
1127 GspAppManager *manager;
1128 GspAppPrivate *priv;
1129 GspApp *app;
1130 GFile *src_file;
1131 char *src_basename;
1132 char *dst_basename;
1133 char *dst_path;
1134 GFile *dst_file;
1135 gboolean changed;
1136
1137 g_return_val_if_fail (uri != NULL, FALSE);
1138
1139 src_file = g_file_new_for_uri (uri);
1140 src_basename = g_file_get_basename (src_file);
1141
1142 if (src_basename == NULL) {
1143 g_object_unref (src_file);
1144 return FALSE;
1145 }
1146
1147 dst_basename = _gsp_find_free_basename (src_basename);
1148 g_free (src_basename);
1149
1150 if (dst_basename == NULL) {
1151 g_object_unref (src_file);
1152 return FALSE;
1153 }
1154
1155 dst_path = g_build_filename (g_get_user_config_dir (),
1156 "autostart",
1157 dst_basename, NULL);
1158 g_free (dst_basename);
1159
1160 dst_file = g_file_new_for_path (dst_path);
1161
1162 _gsp_ensure_user_autostart_dir ();
1163 if (!g_file_copy (src_file, dst_file, G_FILE_COPY_NONE,
1164 NULL, NULL, NULL, NULL)) {
1165 g_object_unref (src_file);
1166 g_object_unref (dst_file);
1167 g_free (dst_path);
1168 return FALSE;
1169 }
1170
1171 g_object_unref (src_file);
1172 g_object_unref (dst_file);
1173
1174 app = gsp_app_new (dst_path, 0);
1175 priv = gsp_app_get_instance_private (app);
1176 if (!app) {
1177 g_remove (dst_path);
1178 g_free (dst_path);
1179 return FALSE;
1180 }
1181
1182 g_free (dst_path);
1183
1184 changed = FALSE;
1185 if (priv->hidden) {
1186 changed = TRUE;
1187 priv->hidden = FALSE;
1188 priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
1189 }
1190
1191 if (changed) {
1192 _gsp_app_queue_save (app);
1193 }
1194
1195 manager = gsp_app_manager_get ();
1196 gsp_app_manager_add (manager, app);
1197 g_object_unref (app);
1198 g_object_unref (manager);
1199
1200 return TRUE;
1201 }
1202