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