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 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 #include "config.h"
26 
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 
31 #include <gio/gio.h>
32 
33 #include "gsm-properties-dialog.h"
34 #include "gsm-app-dialog.h"
35 #include "gsm-util.h"
36 #include "gsp-app.h"
37 #include "gsp-app-manager.h"
38 
39 #define GTKBUILDER_FILE "session-properties.ui"
40 
41 #define CAPPLET_TREEVIEW_WIDGET_NAME      "session_properties_treeview"
42 #define CAPPLET_ADD_WIDGET_NAME           "session_properties_add_button"
43 #define CAPPLET_DELETE_WIDGET_NAME        "session_properties_delete_button"
44 #define CAPPLET_EDIT_WIDGET_NAME          "session_properties_edit_button"
45 #define CAPPLET_SAVE_WIDGET_NAME          "session_properties_save_button"
46 #define CAPPLET_REMEMBER_WIDGET_NAME      "session_properties_remember_toggle"
47 #define CAPPLET_SHOW_HIDDEN_WIDGET_NAME   "session_properties_show_hidden_toggle"
48 
49 #define STARTUP_APP_ICON     "system-run"
50 
51 #define SPC_CONFIG_SCHEMA   "org.mate.session"
52 #define SPC_AUTOSAVE_KEY    "auto-save-session"
53 #define SPC_SHOW_HIDDEN_KEY "show-hidden-apps"
54 
55 struct _GsmPropertiesDialog
56 {
57         GtkDialog          parent;
58         GtkBuilder        *xml;
59         GtkListStore      *list_store;
60         GtkTreeModel      *tree_filter;
61 
62         GtkTreeView       *treeview;
63         GtkWidget         *add_button;
64         GtkWidget         *delete_button;
65         GtkWidget         *edit_button;
66 
67         GspAppManager     *manager;
68 
69         GSettings         *settings;
70 };
71 
72 enum {
73         STORE_COL_ENABLED = 0,
74         STORE_COL_GICON,
75         STORE_COL_DESCRIPTION,
76         STORE_COL_APP,
77         STORE_COL_SEARCH,
78         NUMBER_OF_COLUMNS
79 };
80 
81 static void     gsm_properties_dialog_finalize    (GObject                  *object);
82 
G_DEFINE_TYPE(GsmPropertiesDialog,gsm_properties_dialog,GTK_TYPE_DIALOG)83 G_DEFINE_TYPE (GsmPropertiesDialog, gsm_properties_dialog, GTK_TYPE_DIALOG)
84 
85 static gboolean
86 find_by_app (GtkTreeModel *model,
87              GtkTreeIter  *iter,
88              GspApp       *app)
89 {
90         GspApp *iter_app = NULL;
91 
92         if (!gtk_tree_model_get_iter_first (model, iter)) {
93                 return FALSE;
94         }
95 
96         do {
97                 gtk_tree_model_get (model, iter,
98                                     STORE_COL_APP, &iter_app,
99                                     -1);
100 
101                 if (iter_app == app) {
102                         g_object_unref (iter_app);
103                         return TRUE;
104                 }
105 
106                 g_object_unref (iter_app);
107         } while (gtk_tree_model_iter_next (model, iter));
108 
109         return FALSE;
110 }
111 
112 static void
_fill_iter_from_app(GtkListStore * list_store,GtkTreeIter * iter,GspApp * app)113 _fill_iter_from_app (GtkListStore *list_store,
114                      GtkTreeIter  *iter,
115                      GspApp       *app)
116 {
117         gboolean    enabled;
118         GIcon      *icon;
119         const char *description;
120         const char *app_name;
121 
122         enabled     = !gsp_app_get_hidden (app);
123         icon        = gsp_app_get_icon (app);
124         description = gsp_app_get_description (app);
125         app_name    = gsp_app_get_name (app);
126 
127         if (G_IS_THEMED_ICON (icon)) {
128                 GtkIconTheme       *theme;
129                 const char * const *icon_names;
130 
131                 theme = gtk_icon_theme_get_default ();
132                 icon_names = g_themed_icon_get_names (G_THEMED_ICON (icon));
133                 if (icon_names[0] == NULL ||
134                     !gtk_icon_theme_has_icon (theme, icon_names[0])) {
135                         g_object_unref (icon);
136                         icon = NULL;
137                 }
138         } else if (G_IS_FILE_ICON (icon)) {
139                 GFile *iconfile;
140 
141                 iconfile = g_file_icon_get_file (G_FILE_ICON (icon));
142                 if (!g_file_query_exists (iconfile, NULL)) {
143                         g_object_unref (icon);
144                         icon = NULL;
145                 }
146         }
147 
148         if (icon == NULL) {
149                 icon = g_themed_icon_new (STARTUP_APP_ICON);
150         }
151 
152         gtk_list_store_set (list_store, iter,
153                             STORE_COL_ENABLED, enabled,
154                             STORE_COL_GICON, icon,
155                             STORE_COL_DESCRIPTION, description,
156                             STORE_COL_APP, app,
157                             STORE_COL_SEARCH, app_name,
158                             -1);
159         g_object_unref (icon);
160 }
161 
162 static void
_app_changed(GsmPropertiesDialog * dialog,GspApp * app)163 _app_changed (GsmPropertiesDialog *dialog,
164               GspApp              *app)
165 {
166         GtkTreeIter iter;
167 
168         if (!find_by_app (GTK_TREE_MODEL (dialog->list_store),
169                           &iter, app)) {
170                 return;
171         }
172 
173         _fill_iter_from_app (dialog->list_store, &iter, app);
174 }
175 
176 static void
append_app(GsmPropertiesDialog * dialog,GspApp * app)177 append_app (GsmPropertiesDialog *dialog,
178             GspApp              *app)
179 {
180         GtkTreeIter   iter;
181         if (find_by_app (GTK_TREE_MODEL (dialog->list_store),
182                          &iter, app)) {
183                 return;
184         }
185 
186         gtk_list_store_append (dialog->list_store, &iter);
187         _fill_iter_from_app (dialog->list_store, &iter, app);
188 
189         g_signal_connect_swapped (app, "changed",
190                                   G_CALLBACK (_app_changed), dialog);
191 }
192 
193 static void
_app_added(GsmPropertiesDialog * dialog,GspApp * app,GspAppManager * manager)194 _app_added (GsmPropertiesDialog *dialog,
195             GspApp              *app,
196             GspAppManager       *manager)
197 {
198         (void) manager;
199         append_app (dialog, app);
200 }
201 
202 static void
remove_app(GsmPropertiesDialog * dialog,GspApp * app)203 remove_app (GsmPropertiesDialog *dialog,
204             GspApp              *app)
205 {
206         GtkTreeIter iter;
207 
208         if (!find_by_app (GTK_TREE_MODEL (dialog->list_store),
209                           &iter, app)) {
210                 return;
211         }
212 
213         g_signal_handlers_disconnect_by_func (app,
214                                               _app_changed,
215                                               dialog);
216         gtk_list_store_remove (dialog->list_store, &iter);
217 }
218 
219 static void
_app_removed(GsmPropertiesDialog * dialog,GspApp * app,GspAppManager * manager)220 _app_removed (GsmPropertiesDialog *dialog,
221               GspApp              *app,
222               GspAppManager       *manager)
223 {
224         (void) manager;
225         remove_app (dialog, app);
226 }
227 
228 static void
populate_model(GsmPropertiesDialog * dialog)229 populate_model (GsmPropertiesDialog *dialog)
230 {
231         GSList *apps;
232         GSList *l;
233 
234         apps = gsp_app_manager_get_apps (dialog->manager);
235         for (l = apps; l != NULL; l = l->next) {
236                 append_app (dialog, GSP_APP (l->data));
237         }
238         g_slist_free (apps);
239 }
240 
241 static void
on_selection_changed(GtkTreeSelection * selection,GsmPropertiesDialog * dialog)242 on_selection_changed (GtkTreeSelection    *selection,
243                       GsmPropertiesDialog *dialog)
244 {
245         gboolean sel;
246 
247         sel = gtk_tree_selection_get_selected (selection, NULL, NULL);
248 
249         gtk_widget_set_sensitive (dialog->edit_button, sel);
250         gtk_widget_set_sensitive (dialog->delete_button, sel);
251 }
252 
253 static void
on_startup_enabled_toggled(GtkCellRendererToggle * cell_renderer,char * path,GsmPropertiesDialog * dialog)254 on_startup_enabled_toggled (GtkCellRendererToggle *cell_renderer,
255                             char                  *path,
256                             GsmPropertiesDialog   *dialog)
257 {
258         GtkTreeIter iter;
259         GspApp     *app;
260         gboolean    active;
261 
262         if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->tree_filter),
263                                                   &iter, path)) {
264                 return;
265         }
266 
267         app = NULL;
268         gtk_tree_model_get (GTK_TREE_MODEL (dialog->tree_filter),
269                             &iter,
270                             STORE_COL_APP, &app,
271                             -1);
272 
273         active = gtk_cell_renderer_toggle_get_active (cell_renderer);
274 
275         if (app) {
276                 gsp_app_set_hidden (app, active);
277                 g_object_unref (app);
278         }
279 }
280 
281 static void
on_drag_data_received(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint time,GsmPropertiesDialog * dialog)282 on_drag_data_received (GtkWidget           *widget,
283                        GdkDragContext      *drag_context,
284                        gint                 x,
285                        gint                 y,
286                        GtkSelectionData    *data,
287                        guint                info,
288                        guint                time,
289                        GsmPropertiesDialog *dialog)
290 {
291         gboolean dnd_success;
292 
293         dnd_success = FALSE;
294 
295         if (data != NULL) {
296                 char **filenames;
297                 int    i;
298 
299                 filenames = gtk_selection_data_get_uris (data);
300 
301                 for (i = 0; filenames[i] && filenames[i][0]; i++) {
302                         /* Return success if at least one file succeeded */
303                         gboolean file_success;
304                         file_success = gsp_app_copy_desktop_file (filenames[i]);
305                         dnd_success = dnd_success || file_success;
306                 }
307 
308                 g_strfreev (filenames);
309         }
310 
311         gtk_drag_finish (drag_context, dnd_success, FALSE, time);
312         g_signal_stop_emission_by_name (widget, "drag_data_received");
313 }
314 
315 static void
on_drag_begin(GtkWidget * widget,GdkDragContext * context,GsmPropertiesDialog * dialog)316 on_drag_begin (GtkWidget           *widget,
317                GdkDragContext      *context,
318                GsmPropertiesDialog *dialog)
319 {
320         GtkTreePath *path;
321         GtkTreeIter  iter;
322         GspApp      *app;
323 
324         gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &path, NULL);
325         gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->tree_filter),
326                                  &iter, path);
327         gtk_tree_path_free (path);
328 
329         gtk_tree_model_get (GTK_TREE_MODEL (dialog->tree_filter),
330                             &iter,
331                             STORE_COL_APP, &app,
332                             -1);
333 
334         if (app) {
335                 g_object_set_data_full (G_OBJECT (context), "gsp-app",
336                                         g_object_ref (app), g_object_unref);
337                 g_object_unref (app);
338         }
339 
340 }
341 
342 static void
on_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time,GsmPropertiesDialog * dialog)343 on_drag_data_get (GtkWidget           *widget,
344                   GdkDragContext      *context,
345                   GtkSelectionData    *selection_data,
346                   guint                info,
347                   guint                time,
348                   GsmPropertiesDialog *dialog)
349 {
350         GspApp *app;
351 
352         app = g_object_get_data (G_OBJECT (context), "gsp-app");
353         if (app) {
354                 const char *uris[2];
355                 char       *uri;
356 
357                 uri = g_filename_to_uri (gsp_app_get_path (app), NULL, NULL);
358 
359                 uris[0] = uri;
360                 uris[1] = NULL;
361                 gtk_selection_data_set_uris (selection_data, (char **) uris);
362 
363                 g_free (uri);
364         }
365 }
366 
367 static void
on_add_app_clicked(GtkWidget * widget,GsmPropertiesDialog * dialog)368 on_add_app_clicked (GtkWidget           *widget,
369                     GsmPropertiesDialog *dialog)
370 {
371         GtkWidget  *add_dialog;
372         char       *name;
373         char       *exec;
374         char       *comment;
375         guint       delay;
376 
377         add_dialog = gsm_app_dialog_new (NULL, NULL, NULL, 0);
378         gtk_window_set_transient_for (GTK_WINDOW (add_dialog),
379                                       GTK_WINDOW (dialog));
380 
381         if (gsm_app_dialog_run (GSM_APP_DIALOG (add_dialog),
382                                 &name, &exec, &comment, &delay)) {
383                 gsp_app_create (name, comment, exec, delay);
384                 g_free (name);
385                 g_free (exec);
386                 g_free (comment);
387         }
388 }
389 
390 static void
on_delete_app_clicked(GtkWidget * widget,GsmPropertiesDialog * dialog)391 on_delete_app_clicked (GtkWidget           *widget,
392                        GsmPropertiesDialog *dialog)
393 {
394         GtkTreeSelection *selection;
395         GtkTreeIter       iter;
396         GspApp           *app;
397 
398         selection = gtk_tree_view_get_selection (dialog->treeview);
399 
400         if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
401                 return;
402         }
403 
404         app = NULL;
405         gtk_tree_model_get (GTK_TREE_MODEL (dialog->tree_filter),
406                             &iter,
407                             STORE_COL_APP, &app,
408                             -1);
409 
410         if (app) {
411                 gsp_app_delete (app);
412                 g_object_unref (app);
413         }
414 }
415 
416 static void
on_edit_app_clicked(GtkWidget * widget,GsmPropertiesDialog * dialog)417 on_edit_app_clicked (GtkWidget           *widget,
418                      GsmPropertiesDialog *dialog)
419 {
420         GtkTreeSelection *selection;
421         GtkTreeIter       iter;
422         GspApp           *app;
423 
424         selection = gtk_tree_view_get_selection (dialog->treeview);
425 
426         if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
427                 return;
428         }
429 
430         app = NULL;
431         gtk_tree_model_get (GTK_TREE_MODEL (dialog->tree_filter),
432                             &iter,
433                             STORE_COL_APP, &app,
434                             -1);
435 
436         if (app) {
437                 GtkWidget  *edit_dialog;
438                 char       *name;
439                 char       *exec;
440                 char       *comment;
441                 guint       delay;
442 
443                 edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app),
444                                                   gsp_app_get_exec (app),
445                                                   gsp_app_get_comment (app),
446                                                   gsp_app_get_delay (app));
447                 gtk_window_set_transient_for (GTK_WINDOW (edit_dialog),
448                                               GTK_WINDOW (dialog));
449 
450                 if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog),
451                                         &name, &exec, &comment, &delay)) {
452                         gsp_app_update (app, name, comment, exec, delay);
453                         g_free (name);
454                         g_free (exec);
455                         g_free (comment);
456                 }
457 
458                 g_object_unref (app);
459         }
460 }
461 
462 static void
on_row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GsmPropertiesDialog * dialog)463 on_row_activated (GtkTreeView         *tree_view,
464                   GtkTreePath         *path,
465                   GtkTreeViewColumn   *column,
466                   GsmPropertiesDialog *dialog)
467 {
468         on_edit_app_clicked (NULL, dialog);
469 }
470 
471 static void
on_show_hidden_toggled(GtkToggleButton * togglebutton,GsmPropertiesDialog * dialog)472 on_show_hidden_toggled (GtkToggleButton     *togglebutton,
473                         GsmPropertiesDialog *dialog)
474 {
475         (void) togglebutton;
476         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (dialog->tree_filter));
477 }
478 
479 static void
on_save_session_clicked(GtkWidget * widget,GsmPropertiesDialog * dialog)480 on_save_session_clicked (GtkWidget           *widget,
481                          GsmPropertiesDialog *dialog)
482 {
483         g_debug ("Session saving is not implemented yet!");
484 }
485 
486 static gboolean
on_main_notebook_scroll_event(GtkWidget * widget,GdkEventScroll * event)487 on_main_notebook_scroll_event (GtkWidget        *widget,
488                                GdkEventScroll   *event)
489 {
490         GtkNotebook *notebook = GTK_NOTEBOOK (widget);
491         GtkWidget *child, *event_widget, *action_widget;
492 
493         child = gtk_notebook_get_nth_page (notebook, gtk_notebook_get_current_page (notebook));
494         if (child == NULL)
495                 return FALSE;
496 
497         event_widget = gtk_get_event_widget ((GdkEvent*) event);
498 
499         /* Ignore scroll events from the content of the page */
500         if (event_widget == NULL || event_widget == child || gtk_widget_is_ancestor (event_widget, child))
501                 return FALSE;
502 
503         /* And also from the action widgets */
504         action_widget = gtk_notebook_get_action_widget (notebook, GTK_PACK_START);
505         if (event_widget == action_widget || (action_widget != NULL && gtk_widget_is_ancestor (event_widget, action_widget)))
506                 return FALSE;
507 
508         action_widget = gtk_notebook_get_action_widget (notebook, GTK_PACK_END);
509         if (event_widget == action_widget || (action_widget != NULL && gtk_widget_is_ancestor (event_widget, action_widget)))
510                 return FALSE;
511 
512         switch (event->direction) {
513                 case GDK_SCROLL_RIGHT:
514                 case GDK_SCROLL_DOWN:
515                         gtk_notebook_next_page (notebook);
516                         break;
517                 case GDK_SCROLL_LEFT:
518                 case GDK_SCROLL_UP:
519                         gtk_notebook_prev_page (notebook);
520                         break;
521                 case GDK_SCROLL_SMOOTH:
522                         switch (gtk_notebook_get_tab_pos (notebook)) {
523                                 case GTK_POS_LEFT:
524                                 case GTK_POS_RIGHT:
525                                         if (event->delta_y > 0)
526                                                 gtk_notebook_next_page (notebook);
527                                         else if (event->delta_y < 0)
528                                                 gtk_notebook_prev_page (notebook);
529                                         break;
530                                 case GTK_POS_TOP:
531                                 case GTK_POS_BOTTOM:
532                                         if (event->delta_x > 0)
533                                                 gtk_notebook_next_page (notebook);
534                                         else if (event->delta_x < 0)
535                                                 gtk_notebook_prev_page (notebook);
536                                         break;
537                         }
538                         break;
539         }
540 
541         return TRUE;
542 }
543 
544 static gboolean
visible_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)545 visible_func (GtkTreeModel *model,
546               GtkTreeIter  *iter,
547               gpointer      data)
548 {
549         gboolean           visible = FALSE;
550         GtkToggleButton   *toggle_button = data;;
551         GspApp            *app;
552         gboolean           show_hidden;
553 
554         show_hidden = gtk_toggle_button_get_active (toggle_button);
555 
556         gtk_tree_model_get (model, iter,
557                             STORE_COL_APP, &app,
558                             -1);
559 
560         if (app) {
561                 gboolean nodisplay;
562 
563                 nodisplay = gsp_app_get_nodisplay (app);
564                 visible = show_hidden || !nodisplay;
565                 g_object_unref (app);
566         } else {
567                 visible = show_hidden;
568         }
569 
570         return visible;
571 }
572 
573 static void
setup_dialog(GsmPropertiesDialog * dialog)574 setup_dialog (GsmPropertiesDialog *dialog)
575 {
576         GtkTreeView       *treeview;
577         GtkWidget         *button;
578         GtkToggleButton   *toggle_button;
579         GtkTreeModel      *tree_filter;
580         GtkTreeViewColumn *column;
581         GtkCellRenderer   *renderer;
582         GtkTreeSelection  *selection;
583         GtkTargetList     *targetlist;
584 
585         gsm_util_dialog_add_button (GTK_DIALOG (dialog),
586                                     _("_Help"), "help-browser",
587                                     GTK_RESPONSE_HELP);
588 
589         gsm_util_dialog_add_button (GTK_DIALOG (dialog),
590                                     _("_Close"), "window-close",
591                                     GTK_RESPONSE_CLOSE);
592 
593         dialog->settings = g_settings_new (SPC_CONFIG_SCHEMA);
594 
595         toggle_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (dialog->xml,
596                                                                    CAPPLET_SHOW_HIDDEN_WIDGET_NAME));
597 
598         g_settings_bind (dialog->settings, SPC_SHOW_HIDDEN_KEY,
599                          toggle_button, "active", G_SETTINGS_BIND_DEFAULT);
600 
601         g_signal_connect (toggle_button, "toggled",
602                           G_CALLBACK (on_show_hidden_toggled),
603                           dialog);
604 
605         dialog->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
606                                                  G_TYPE_BOOLEAN,
607                                                  G_TYPE_ICON,
608                                                  G_TYPE_STRING,
609                                                  G_TYPE_OBJECT,
610                                                  G_TYPE_STRING);
611         tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->list_store),
612                                                  NULL);
613         g_object_unref (dialog->list_store);
614         dialog->tree_filter = tree_filter;
615 
616         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (tree_filter), visible_func, toggle_button, NULL);
617 
618         treeview = GTK_TREE_VIEW (gtk_builder_get_object (dialog->xml,
619                                                           CAPPLET_TREEVIEW_WIDGET_NAME));
620         dialog->treeview = treeview;
621 
622         gtk_tree_view_set_model (treeview, tree_filter);
623         g_object_unref (tree_filter);
624 
625         gtk_tree_view_set_headers_visible (treeview, FALSE);
626         g_signal_connect (treeview,
627                           "row-activated",
628                           G_CALLBACK (on_row_activated),
629                           dialog);
630 
631         selection = gtk_tree_view_get_selection (treeview);
632         gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
633         g_signal_connect (selection,
634                           "changed",
635                           G_CALLBACK (on_selection_changed),
636                           dialog);
637 
638         /* CHECKBOX COLUMN */
639         renderer = gtk_cell_renderer_toggle_new ();
640         column = gtk_tree_view_column_new_with_attributes (_("Enabled"),
641                                                            renderer,
642                                                            "active", STORE_COL_ENABLED,
643                                                            NULL);
644         gtk_tree_view_append_column (treeview, column);
645         g_signal_connect (renderer,
646                           "toggled",
647                           G_CALLBACK (on_startup_enabled_toggled),
648                           dialog);
649 
650         /* ICON COLUMN */
651         renderer = gtk_cell_renderer_pixbuf_new ();
652         column = gtk_tree_view_column_new_with_attributes (_("Icon"),
653                                                            renderer,
654                                                            "gicon", STORE_COL_GICON,
655                                                            "sensitive", STORE_COL_ENABLED,
656                                                            NULL);
657         g_object_set (renderer,
658                       "stock-size", GSM_PROPERTIES_ICON_SIZE,
659                       NULL);
660         gtk_tree_view_append_column (treeview, column);
661 
662         /* NAME COLUMN */
663         renderer = gtk_cell_renderer_text_new ();
664         column = gtk_tree_view_column_new_with_attributes (_("Program"),
665                                                            renderer,
666                                                            "markup", STORE_COL_DESCRIPTION,
667                                                            "sensitive", STORE_COL_ENABLED,
668                                                            NULL);
669         g_object_set (renderer,
670                       "ellipsize", PANGO_ELLIPSIZE_END,
671                       NULL);
672         gtk_tree_view_append_column (treeview, column);
673 
674 
675         gtk_tree_view_column_set_sort_column_id (column, STORE_COL_DESCRIPTION);
676         gtk_tree_view_set_search_column (treeview, STORE_COL_SEARCH);
677 
678         gtk_tree_view_enable_model_drag_source (treeview,
679                                                 GDK_BUTTON1_MASK|GDK_BUTTON2_MASK,
680                                                 NULL, 0,
681                                                 GDK_ACTION_COPY);
682         gtk_drag_source_add_uri_targets (GTK_WIDGET (treeview));
683 
684         gtk_drag_dest_set (GTK_WIDGET (treeview),
685                            GTK_DEST_DEFAULT_ALL,
686                            NULL, 0,
687                            GDK_ACTION_COPY);
688         gtk_drag_dest_add_uri_targets (GTK_WIDGET (treeview));
689 
690         /* we don't want to accept drags coming from this widget */
691         targetlist = gtk_drag_dest_get_target_list (GTK_WIDGET (treeview));
692         if (targetlist != NULL) {
693                 GtkTargetEntry *targets;
694                 gint n_targets;
695                 gint i;
696                 targets = gtk_target_table_new_from_list (targetlist, &n_targets);
697                 for (i = 0; i < n_targets; i++)
698                         targets[i].flags = GTK_TARGET_OTHER_WIDGET;
699                 targetlist = gtk_target_list_new (targets, n_targets);
700                 gtk_drag_dest_set_target_list (GTK_WIDGET (treeview), targetlist);
701                 gtk_target_list_unref (targetlist);
702                 gtk_target_table_free (targets, n_targets);
703         }
704 
705         g_signal_connect (treeview, "drag_begin",
706                           G_CALLBACK (on_drag_begin),
707                           dialog);
708         g_signal_connect (treeview, "drag_data_get",
709                           G_CALLBACK (on_drag_data_get),
710                           dialog);
711         g_signal_connect (treeview, "drag_data_received",
712                           G_CALLBACK (on_drag_data_received),
713                           dialog);
714 
715         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->list_store),
716                                               STORE_COL_DESCRIPTION,
717                                               GTK_SORT_ASCENDING);
718 
719 
720         button = GTK_WIDGET (gtk_builder_get_object (dialog->xml,
721                                                      CAPPLET_ADD_WIDGET_NAME));
722         dialog->add_button = button;
723         g_signal_connect (button,
724                           "clicked",
725                           G_CALLBACK (on_add_app_clicked),
726                           dialog);
727 
728         button = GTK_WIDGET (gtk_builder_get_object (dialog->xml,
729                                                      CAPPLET_DELETE_WIDGET_NAME));
730         dialog->delete_button = button;
731         g_signal_connect (button,
732                           "clicked",
733                           G_CALLBACK (on_delete_app_clicked),
734                           dialog);
735 
736         button = GTK_WIDGET (gtk_builder_get_object (dialog->xml,
737                                                      CAPPLET_EDIT_WIDGET_NAME));
738         dialog->edit_button = button;
739         g_signal_connect (button,
740                           "clicked",
741                           G_CALLBACK (on_edit_app_clicked),
742                           dialog);
743 
744 
745         button = GTK_WIDGET (gtk_builder_get_object (dialog->xml,
746                                                      CAPPLET_REMEMBER_WIDGET_NAME));
747 
748         g_settings_bind (dialog->settings, SPC_AUTOSAVE_KEY,
749                          button, "active", G_SETTINGS_BIND_DEFAULT);
750 
751         button = GTK_WIDGET (gtk_builder_get_object (dialog->xml,
752                                                      CAPPLET_SAVE_WIDGET_NAME));
753         g_signal_connect (button,
754                           "clicked",
755                           G_CALLBACK (on_save_session_clicked),
756                           dialog);
757 
758         dialog->manager = gsp_app_manager_get ();
759         gsp_app_manager_fill (dialog->manager);
760         g_signal_connect_swapped (dialog->manager, "added",
761                                   G_CALLBACK (_app_added), dialog);
762         g_signal_connect_swapped (dialog->manager, "removed",
763                                   G_CALLBACK (_app_removed), dialog);
764 
765         populate_model (dialog);
766         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (dialog->tree_filter));
767 }
768 
769 static GObject *
gsm_properties_dialog_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)770 gsm_properties_dialog_constructor (GType                  type,
771                                 guint                  n_construct_properties,
772                                 GObjectConstructParam *construct_properties)
773 {
774         GsmPropertiesDialog *dialog;
775 
776         dialog = GSM_PROPERTIES_DIALOG (G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->constructor (type,
777                                                                                                           n_construct_properties,
778                                                                                                           construct_properties));
779 
780         setup_dialog (dialog);
781 
782         return G_OBJECT (dialog);
783 }
784 
785 static void
gsm_properties_dialog_dispose(GObject * object)786 gsm_properties_dialog_dispose (GObject *object)
787 {
788         GsmPropertiesDialog *dialog;
789 
790         g_return_if_fail (object != NULL);
791         g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object));
792 
793         dialog = GSM_PROPERTIES_DIALOG (object);
794 
795         if (dialog->xml != NULL) {
796                 g_object_unref (dialog->xml);
797                 dialog->xml = NULL;
798         }
799 
800         if (dialog->settings != NULL) {
801                 g_object_unref (dialog->settings);
802                 dialog->settings = NULL;
803         }
804 
805         G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->dispose (object);
806 
807         /* it's important to do this after chaining to the parent dispose
808          * method because we want to make sure the treeview has been disposed
809          * and removed all its references to GspApp objects */
810         if (dialog->manager != NULL) {
811                 g_object_unref (dialog->manager);
812                 dialog->manager = NULL;
813         }
814 }
815 
816 static void
gsm_properties_dialog_class_init(GsmPropertiesDialogClass * klass)817 gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass)
818 {
819         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
820 
821         object_class->constructor = gsm_properties_dialog_constructor;
822         object_class->dispose = gsm_properties_dialog_dispose;
823         object_class->finalize = gsm_properties_dialog_finalize;
824 }
825 
826 static void
gsm_properties_dialog_init(GsmPropertiesDialog * dialog)827 gsm_properties_dialog_init (GsmPropertiesDialog *dialog)
828 {
829         GtkWidget   *content_area;
830         GtkWidget   *widget;
831         GError      *error;
832 
833         dialog->xml = gtk_builder_new ();
834         gtk_builder_set_translation_domain (dialog->xml, GETTEXT_PACKAGE);
835 
836         error = NULL;
837         if (!gtk_builder_add_from_file (dialog->xml,
838                                         GTKBUILDER_DIR "/" GTKBUILDER_FILE,
839                                         &error)) {
840                 if (error) {
841                         g_warning ("Could not load capplet UI file: %s",
842                                    error->message);
843                         g_error_free (error);
844                 } else {
845                         g_warning ("Could not load capplet UI file.");
846                 }
847         }
848 
849         content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
850         widget = GTK_WIDGET (gtk_builder_get_object (dialog->xml,
851                                                      "main-notebook"));
852 
853         gtk_widget_add_events (widget, GDK_SCROLL_MASK);
854         g_signal_connect (widget,
855                           "scroll-event",
856                           G_CALLBACK (on_main_notebook_scroll_event),
857                           NULL);
858 
859         gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
860 
861         gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
862         gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
863         gtk_box_set_spacing (GTK_BOX (content_area), 2);
864         gtk_window_set_icon_name (GTK_WINDOW (dialog), "mate-session-properties");
865         gtk_window_set_title (GTK_WINDOW (dialog), _("Startup Applications Preferences"));
866 }
867 
868 static void
gsm_properties_dialog_finalize(GObject * object)869 gsm_properties_dialog_finalize (GObject *object)
870 {
871         g_return_if_fail (object != NULL);
872         g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object));
873 
874         G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->finalize (object);
875 }
876 
877 GtkWidget *
gsm_properties_dialog_new(void)878 gsm_properties_dialog_new (void)
879 {
880         GObject *object;
881 
882         object = g_object_new (GSM_TYPE_PROPERTIES_DIALOG,
883                                NULL);
884 
885         return GTK_WIDGET (object);
886 }
887