Merge lp:~julien-spautz/switchboard-plug-startup-applications/new-design into lp:switchboard-plug-startup-applications

Proposed by Julien Spautz
Status: Merged
Merged at revision: 50
Proposed branch: lp:~julien-spautz/switchboard-plug-startup-applications/new-design
Merge into: lp:switchboard-plug-startup-applications
Diff against target: 1312 lines (+430/-627)
8 files modified
src/Backend/KeyFile.vala (+83/-32)
src/Backend/KeyFileFactory.vala (+4/-23)
src/CMakeLists.txt (+7/-9)
src/Dialogs/AppChooser.vala (+142/-188)
src/Plug.vala (+14/-37)
src/Widgets/Editor.vala (+0/-102)
src/Widgets/List.vala (+180/-175)
src/Widgets/Toolbar.vala (+0/-61)
To merge this branch: bzr merge lp:~julien-spautz/switchboard-plug-startup-applications/new-design
Reviewer Review Type Date Requested Status
Julien Spautz Pending
Review via email: mp+214391@code.launchpad.net

Description of the change

Implemented new design, now using Gtk.Popover and Gtk.ListBox requiring Gtk+ 3.12

Simplified interface by removing the editor pane because it's kind of useless, apps can still be added and removed, custom commands also still work.

To post a comment you must log in.
47. By Julien Spautz

make label insensitive when app is inactive

48. By Julien Spautz

rewrote AppChooser dialog using gtk.listbox

49. By Julien Spautz

remove gobject-style construction

50. By Julien Spautz

simplify KeyFileFactory and hide popover instead of destroying and recreating every time

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/Backend/KeyFile.vala'
--- src/Backend/KeyFile.vala 2014-03-22 11:07:44 +0000
+++ src/Backend/KeyFile.vala 2014-04-07 13:03:25 +0000
@@ -18,13 +18,15 @@
18***/18***/
1919
20/**20/**
21 * Stores information about an app found in it's .desktop file 21 * Stores information about an app found in it's .desktop file
22 * and allows us to modify it.22 * and allows us to modify it.
23 * http://standards.freedesktop.org/desktop-entry-spec/latest/index.html
23 */24 */
24public class Pantheon.Startup.Backend.KeyFile : GLib.Object {25public class Pantheon.Startup.Backend.KeyFile : GLib.Object {
2526
26 const int ICON_SIZE = 48;27 const int ICON_SIZE = 48;
27 const string FALLBACK_ICON = "application-default-icon";28 const string FALLBACK_ICON = "application-default-icon";
29 const string CUSTOM_COMMAND_NAME = _("Custom Command");
28 30
29 const string KEY_NAME = KeyFileDesktop.KEY_NAME;31 const string KEY_NAME = KeyFileDesktop.KEY_NAME;
30 const string KEY_COMMAND = KeyFileDesktop.KEY_EXEC;32 const string KEY_COMMAND = KeyFileDesktop.KEY_EXEC;
@@ -33,7 +35,10 @@
33 const string KEY_ACTIVE = "X-GNOME-Autostart-enabled";35 const string KEY_ACTIVE = "X-GNOME-Autostart-enabled";
34 const string KEY_TYPE = KeyFileDesktop.KEY_TYPE;36 const string KEY_TYPE = KeyFileDesktop.KEY_TYPE;
35 const string KEY_NO_DISPLAY = KeyFileDesktop.KEY_NO_DISPLAY;37 const string KEY_NO_DISPLAY = KeyFileDesktop.KEY_NO_DISPLAY;
36 38 const string KEY_HIDDEN = KeyFileDesktop.KEY_HIDDEN;
39 const string KEY_NOT_SHOW_IN = KeyFileDesktop.KEY_NOT_SHOW_IN;
40 const string KEY_ONLY_SHOW_IN = KeyFileDesktop.KEY_ONLY_SHOW_IN;
41
37 public string name {42 public string name {
38 owned get { return get_key (KEY_NAME); }43 owned get { return get_key (KEY_NAME); }
39 set { set_key (KEY_NAME, value); }44 set { set_key (KEY_NAME, value); }
@@ -55,44 +60,54 @@
55 }60 }
5661
57 public bool active {62 public bool active {
58 get { return get_key (KEY_ACTIVE) != "false"; }63 get { return get_bool_key (KEY_ACTIVE); }
59 set { set_key (KEY_ACTIVE, value.to_string ()); }64 set { set_bool_key (KEY_ACTIVE, value); }
60 }65 }
61 66
62 public bool dont_show {67 public bool show {
63 get { return bool.parse (get_key (KEY_NO_DISPLAY)); }68 get {
69 if (get_bool_key (KEY_NO_DISPLAY))
70 return false;
71 if (get_bool_key (KEY_HIDDEN))
72 return false;
73 return show_in_environment ();
74 }
64 }75 }
6576
66 public string path { get; set; }77 public string path { get; set; }
6778
79 public bool is_custom_command {
80 get { return name == CUSTOM_COMMAND_NAME; }
81 }
82
68 GLib.KeyFile keyfile;83 GLib.KeyFile keyfile;
69 static string[] languages;84 static string[] languages;
70 static string preferred_language;85 static string preferred_language;
71 86
72 static construct {87 static construct {
73 languages = Intl.get_language_names ();88 languages = Intl.get_language_names ();
74 preferred_language = languages [0];89 preferred_language = languages [0];
75 }90 }
76 91
77 public KeyFile (string path) {92 public KeyFile (string path) {
78 GLib.Object (path: path);93 Object (path: path);
7994
80 keyfile = new GLib.KeyFile ();95 keyfile = new GLib.KeyFile ();
81 load_from_file ();96 load_from_file ();
82 }97 }
83 98
84 public KeyFile.from_command (string command) {99 public KeyFile.from_command (string command) {
85 keyfile = new GLib.KeyFile ();100 keyfile = new GLib.KeyFile ();
86 101
87 this.path = Utils.get_user_startup_dir () + command.split (" ")[0] + ".desktop";102 this.path = Utils.get_user_startup_dir () + command.split (" ")[0] + ".desktop";
88 this.name = _("Custom Command");103 this.name = CUSTOM_COMMAND_NAME;
89 this.comment = command;104 this.comment = command;
90 this.command = command;105 this.command = command;
91 this.icon = FALLBACK_ICON;106 this.icon = FALLBACK_ICON;
92 this.active = true;107 this.active = true;
93 108
94 set_key (KEY_TYPE, "Application");109 set_key (KEY_TYPE, "Application");
95 110
96 write_to_file ();111 write_to_file ();
97 }112 }
98113
@@ -106,7 +121,7 @@
106 } catch (Error e) {121 } catch (Error e) {
107 warning (e.message);122 warning (e.message);
108 }123 }
109 124
110 message ("-- Saving to %s --", path);125 message ("-- Saving to %s --", path);
111 message ("Name: %s", name);126 message ("Name: %s", name);
112 message ("Comment: %s", comment);127 message ("Comment: %s", comment);
@@ -124,31 +139,46 @@
124 }139 }
125 }140 }
126 141
142 void set_bool_key (string key, bool value) {
143 var as_string = value ? "true" : "false";
144 keyfile_set_string (key, as_string);
145 }
146
147 bool get_bool_key (string key) {
148 var as_string = keyfile_get_string (key);
149 return as_string == "true";
150 }
151
127 void set_key (string key, string value) {152 void set_key (string key, string value) {
128 if (key_is_localized (key))153 if (key_is_localized (key))
129 keyfile_set_locale_string (key, value);154 keyfile_set_locale_string (key, value);
130 else155 else
131 keyfile_set_string (key, value);156 keyfile_set_string (key, value);
132 }157 }
133 158
134 string get_key (string key) {159 string get_key (string key) {
135 if (key_is_localized (key))160 if (key_is_localized (key))
136 return keyfile_get_locale_string (key);161 return keyfile_get_locale_string (key);
137 else162 else
138 return keyfile_get_string (key);163 return keyfile_get_string (key);
139 }164 }
140 165
141 bool key_is_localized (string key) {166 bool key_is_localized (string key) {
142 switch (key) {167 switch (key) {
143 case KEY_NAME:168 case KEY_NAME:
144 case KEY_COMMENT:169 case KEY_COMMENT:
145 return true;170 return true;
146 171
147 case KEY_COMMAND:172 case KEY_COMMAND:
148 case KEY_ICON:173 case KEY_ICON:
149 case KEY_ACTIVE:174 case KEY_ACTIVE:
175 case KEY_NO_DISPLAY:
176 case KEY_TYPE:
177 case KEY_ONLY_SHOW_IN:
178 case KEY_NOT_SHOW_IN:
179 case KEY_HIDDEN:
150 return false;180 return false;
151 181
152 default:182 default:
153 warn_if_reached ();183 warn_if_reached ();
154 return false;184 return false;
@@ -166,9 +196,7 @@
166 string keyfile_get_string (string key) {196 string keyfile_get_string (string key) {
167 try {197 try {
168 return keyfile.get_string (KeyFileDesktop.GROUP, key);198 return keyfile.get_string (KeyFileDesktop.GROUP, key);
169 } catch (KeyFileError e) {199 } catch (KeyFileError e) { }
170 warning (e.message);
171 }
172200
173 return "";201 return "";
174 }202 }
@@ -177,28 +205,51 @@
177 foreach (string lang in languages) {205 foreach (string lang in languages) {
178 try {206 try {
179 return keyfile.get_locale_string (KeyFileDesktop.GROUP, key, lang);207 return keyfile.get_locale_string (KeyFileDesktop.GROUP, key, lang);
180 } catch (KeyFileError e) {208 } catch (KeyFileError e) { }
181 warning (e.message);
182 }
183 }209 }
184210
185 return "";211 return "";
186 }212 }
187 213
214 bool show_in_environment () {
215 var only_show_in = get_key (KEY_ONLY_SHOW_IN);
216 var not_show_in = get_key (KEY_NOT_SHOW_IN);
217
218 var session = Environment.get_variable ("DESKTOP_SESSION");
219
220 if (session in only_show_in)
221 return true;
222 if (session in not_show_in)
223 return false;
224
225 if (only_show_in == "")
226 return true;
227 return false;
228 }
229
188 public void copy_to_local () {230 public void copy_to_local () {
189 path = Utils.get_user_startup_dir () + name.down ().split (" ")[0] + ".desktop";231 path = Utils.get_user_startup_dir () + name.down ().split (" ")[0] + ".desktop";
190 write_to_file ();232 write_to_file ();
191 }233 }
192 234
193 public string create_markup () {235 public string create_markup () {
194 var markup = @"<span font_weight=\"bold\" size=\"large\">$name</span>\n$comment";236 var escaped_name = Markup.escape_text (name);
237 var escaped_comment = Markup.escape_text (comment);
238 var escaped_command = Markup.escape_text (command);
239 string markup;
240
241 if (is_custom_command)
242 markup = escaped_command;
243 else
244 markup = @"<span font_weight=\"bold\" size=\"large\">$escaped_name</span>\n$escaped_comment";
245
195 return markup;246 return markup;
196 }247 }
197 248
198 public Gdk.Pixbuf create_icon (int size = ICON_SIZE) {249 public Gdk.Pixbuf create_icon (int size = ICON_SIZE) {
199 var icon_theme = Gtk.IconTheme.get_default ();250 var icon_theme = Gtk.IconTheme.get_default ();
200 var lookup_flags = Gtk.IconLookupFlags.GENERIC_FALLBACK;251 var lookup_flags = Gtk.IconLookupFlags.GENERIC_FALLBACK;
201 252
202 try {253 try {
203 if (icon_theme.has_icon (icon))254 if (icon_theme.has_icon (icon))
204 return icon_theme.load_icon (icon, size, lookup_flags);255 return icon_theme.load_icon (icon, size, lookup_flags);
@@ -207,7 +258,7 @@
207 } catch (Error e) {258 } catch (Error e) {
208 warning (e.message);259 warning (e.message);
209 }260 }
210 261
211 return (Gdk.Pixbuf) null;262 return (Gdk.Pixbuf) null;
212 }263 }
213}264}
214\ No newline at end of file265\ No newline at end of file
215266
=== modified file 'src/Backend/KeyFileFactory.vala'
--- src/Backend/KeyFileFactory.vala 2014-03-22 11:07:44 +0000
+++ src/Backend/KeyFileFactory.vala 2014-04-07 13:03:25 +0000
@@ -24,29 +24,10 @@
24 public static void init () {24 public static void init () {
25 cache = new Gee.HashMap <string, KeyFile> ();25 cache = new Gee.HashMap <string, KeyFile> ();
26 }26 }
2727
28 public static KeyFile get_or_create (string path) {28 public static KeyFile get_or_create (string path) {
29 if (key_file_is_cached (path))29 if (cache [path] == null) {
30 return get_key_file_from_path (path);30 cache [path] = new KeyFile (path);
31 else31 return cache [path];
32 return create_key_file_from_path (path);
33 }
34
35 static KeyFile get_key_file_from_path (string path)
36 requires (key_file_is_cached (path)) {
37 return cache[path];
38 }
39
40 static KeyFile create_key_file_from_path (string path)
41 requires (key_file_is_cached (path) == false)
42 ensures (key_file_is_cached (path)) {
43
44 var key_file = new KeyFile (path);
45 cache.set (path, key_file);
46 return key_file;
47 }
48
49 static bool key_file_is_cached (string path) {
50 return cache.has_key (path);
51 }32 }
52}33}
53\ No newline at end of file34\ No newline at end of file
5435
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2014-03-22 11:07:44 +0000
+++ src/CMakeLists.txt 2014-04-07 13:03:25 +0000
@@ -1,7 +1,12 @@
1find_package (PkgConfig)1find_package (PkgConfig)
22
3# Add all your dependencies to the list below3# Add all your dependencies to the list below
4pkg_check_modules (DEPS REQUIRED gthread-2.0 gtk+-3.0 switchboard-2.0 granite)4pkg_check_modules (DEPS REQUIRED
5 gthread-2.0
6 gtk+-3.0>=3.12
7 switchboard-2.0
8 granite
9)
510
6add_definitions (${DEPS_CFLAGS})11add_definitions (${DEPS_CFLAGS})
7link_libraries (${DEPS_LIBRARIES})12link_libraries (${DEPS_LIBRARIES})
@@ -16,25 +21,18 @@
16vala_precompile (VALA_C21vala_precompile (VALA_C
17 Plug.vala22 Plug.vala
18 Utils.vala23 Utils.vala
19
20 Backend/KeyFile.vala24 Backend/KeyFile.vala
21 Backend/KeyFileFactory.vala25 Backend/KeyFileFactory.vala
22 Backend/DesktopFileEnumerator.vala26 Backend/DesktopFileEnumerator.vala
23
24 Widgets/List.vala27 Widgets/List.vala
25 Widgets/Editor.vala
26 Widgets/Toolbar.vala
27
28 Dialogs/AppChooser.vala28 Dialogs/AppChooser.vala
29
30 ${CMAKE_CURRENT_BINARY_DIR}/config.vala29 ${CMAKE_CURRENT_BINARY_DIR}/config.vala
31PACKAGES30PACKAGES
32 switchboard-2.031 switchboard-2.0
33 granite32 granite
34OPTIONS33OPTIONS
35 --thread34 --thread
36 --enable-experimental35 --fatal-warnings
37 #--fatal-warnings
38 --verbose36 --verbose
39)37)
4038
4139
=== modified file 'src/Dialogs/AppChooser.vala'
--- src/Dialogs/AppChooser.vala 2014-03-22 11:07:44 +0000
+++ src/Dialogs/AppChooser.vala 2014-04-07 13:03:25 +0000
@@ -1,204 +1,158 @@
1/***1/***
2 Copyright (C) 2013 Michael Langfermann2 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>
3 2013 Julien Spautz <spautz.julien@gmail.com>3
4
5 This program is free software: you can redistribute it and/or modify4 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by5 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or6 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.7 (at your option) any later version.
9 8
10 This program is distributed in the hope that it will be useful,9 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of10 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.12 GNU General Public License for more details.
14 13
15 You should have received a copy of the GNU General Public License14 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.15 along with this program. If not, see <http://www.gnu.org/licenses/>.
17***/16***/
1817
19public class Pantheon.Startup.Dialogs.AppChooser : Granite.Widgets.PopOver {18namespace Pantheon.Startup.Dialogs {
2019
21 private Gtk.ListStore list_store;20 public class AppRow : Gtk.Box {
22 private Gtk.TreeModelSort list_sort;21
23 private Gtk.TreeModelFilter list_filter;22 public Backend.KeyFile app { get; construct; }
24 private Gtk.TreeView list;23
25 private Gtk.Widget apply_button;24 public signal void deleted ();
26 private Granite.Widgets.SearchBar search;25
27 private Gtk.ScrolledWindow scroll;26 public AppRow (Backend.KeyFile app) {
28 private Gtk.CellRendererText cell;27 Object (app: app);
29 private Gtk.Entry command;28 setup ();
3029 }
31 private string search_text = "";30
3231 void setup () {
33 public AppInfo selected_app;32 orientation = Gtk.Orientation.HORIZONTAL;
3433
35 public signal void app_chosen (Backend.KeyFile key_file);34 var markup = app.create_markup ();
36 public signal void app_selected ();35 var icon = app.create_icon (32);
3736
38 public AppChooser () {37 margin = 6;
39 setup_gui ();38 spacing = 12;
40 list_update ();39
41 connect_signals ();40 var image = new Gtk.Image.from_pixbuf (icon);
42 }41 add (image);
4342
44 private void setup_gui () {43 var label = new Gtk.Label (markup);
45 title = _("Choose an Application...");44 label.use_markup = true;
46 border_width = 5;45 label.halign = Gtk.Align.START;
47 46 label.ellipsize = Pango.EllipsizeMode.END;
48 list_store = new Gtk.ListStore (3,47 add (label);
49 typeof (string),48
50 typeof (Gdk.Pixbuf),49 show_all ();
51 typeof (Backend.KeyFile));50 }
5251 }
53 list_filter = new Gtk.TreeModelFilter (list_store, null);52
54 list_filter.set_visible_func (filter_func);53 public class AppChooser : Gtk.Popover {
5554
56 list_sort = new Gtk.TreeModelSort.with_model (list_filter);55 Gtk.ListBox list;
57 list_sort.set_sort_column_id (0, Gtk.SortType.ASCENDING);56 Granite.Widgets.SearchBar search_bar;
5857 Gtk.Entry custom_bar;
59 list = new Gtk.TreeView.with_model (list_sort);58
60 list.expand = true;59 public signal void app_chosen (Backend.KeyFile key_file);
61 list.enable_search = false;60
62 list.headers_visible = false;61 public AppChooser (Gtk.Widget widget) {
6362 Object (relative_to: widget);
64 Gtk.CellRendererPixbuf pixbuf = new Gtk.CellRendererPixbuf ();63 setup_gui ();
65 list.insert_column_with_attributes (-1, null, pixbuf, "pixbuf", 1);64 connect_signals ();
6665 }
67 cell = new Gtk.CellRendererText ();66
68 cell.wrap_mode = Pango.WrapMode.WORD_CHAR;67 void setup_gui () {
69 list.insert_column_with_attributes (-1, null, cell, "markup", 0);68 var grid = new Gtk.Grid ();
7069 grid.margin = 12;
71 search = new Granite.Widgets.SearchBar (_("Search Applications..."));70 grid.row_spacing = 6;
7271
73 scroll = new Gtk.ScrolledWindow (null, null);72 search_bar = new Granite.Widgets.SearchBar (_("Search Applications"));
74 scroll.hscrollbar_policy = Gtk.PolicyType.NEVER;73
75 scroll.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;74 var scrolled = new Gtk.ScrolledWindow (null, null);
76 scroll.shadow_type = Gtk.ShadowType.IN;75 scrolled.height_request = 200;
77 scroll.expand = true;76 scrolled.width_request = 250;
78 scroll.height_request = 250;77 scrolled.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
79 scroll.width_request = 150;78 scrolled.shadow_type = Gtk.ShadowType.IN;
80 scroll.add (list);79
8180 list = new Gtk.ListBox ();
82 Gtk.Box content = get_content_area () as Gtk.Box;81 list.expand = true;
83 content.pack_start (search, false, true, 0);82 list.height_request = 200;
84 content.pack_start (scroll, false, true, 0);83 list.width_request = 150;
8584 list.set_sort_func (sort_function);
86 command = new Gtk.Entry();85 list.set_filter_func (filter_function);
87 command.placeholder_text = _("Type in a custom command");86 scrolled.add (list);
88 command.primary_icon_name = "document-properties-symbolic";87
89 command.primary_icon_activatable = false;88 custom_bar = new Gtk.Entry();
90 content.pack_start (command, false, true, 0);89 custom_bar.placeholder_text = _("Type in a custom command");
9190 custom_bar.primary_icon_name = "document-properties-symbolic";
92 content.spacing = 10;91 custom_bar.primary_icon_activatable = false;
9392
94 add_button (Gtk.Stock.CANCEL, Gtk.ResponseType.CLOSE);93 grid.attach (search_bar, 0, 0, 1, 1);
95 apply_button = add_button (Gtk.Stock.ADD, Gtk.ResponseType.APPLY);94 grid.attach (scrolled, 0, 1, 1, 1);
96 apply_button.sensitive = false;95 grid.attach (custom_bar, 0, 2, 1, 1);
97 }96
9897 add (grid);
99 void list_update () {98
100 var enumerator = new Backend.DesktopFileEnumerator ("/usr/share/applications/");99 list_update ();
101 var names = enumerator.get_desktop_files ();100 }
102 101
103 foreach (var file in names) {102 void list_update () {
104 var key_file = new Backend.KeyFile (file);103 var enumerator = new Backend.DesktopFileEnumerator ("/usr/share/applications/");
105 append_item_from_keyfile (key_file);104 var paths = enumerator.get_desktop_files ();
106 }105
107 }106 foreach (var path in paths) {
108 107 var key_file = Backend.KeyFileFactory.get_or_create (path);
109 void append_item_from_keyfile (Backend.KeyFile key_file) {108 if (key_file.show)
110 if (key_file.dont_show)109 append_item_from_keyfile (key_file);
111 return;
112
113 Gtk.TreeIter iter;
114 list_store.append (out iter);
115 list_store.set (iter, 0, key_file.name,
116 1, key_file.create_icon (32),
117 2, key_file);
118 }
119
120 void connect_signals () {
121 command.icon_press.connect (() => command.text = "");
122
123 command.changed.connect (() => {
124 var is_empty = (command.text == "");
125
126 scroll.sensitive = is_empty;
127 search.sensitive = is_empty;
128 command.secondary_icon_sensitive = !is_empty;
129 apply_button.sensitive = !is_empty;
130
131 if (is_empty) {
132 command.secondary_icon_name = null;
133 list_update ();
134 } else {
135 list_store.clear ();
136 command.secondary_icon_name = "edit-clear-symbolic";
137 }110 }
138 });111 }
139112
140 list.cursor_changed.connect (() => command.activate ());113 void append_item_from_keyfile (Backend.KeyFile key_file) {
141 response.connect (on_response);114 var app_row = new AppRow (key_file);
142115 list.prepend (app_row);
143 command.activate.connect (() => {116 }
144 app_chosen(get_app());117
145 destroy ();118 int sort_function (Gtk.ListBoxRow list_box_row_1,
146 });119 Gtk.ListBoxRow list_box_row_2) {
147120 var row_1 = list_box_row_1.get_child () as AppRow;
148 search.text_changed_pause.connect ((text) => {121 var row_2 = list_box_row_2.get_child () as AppRow;
149 search_text = text;122
150 list_filter.refilter ();123 var name_1 = row_1.app.name;
151 });124 var name_2 = row_2.app.name;
152125
153 size_allocate.connect ((allocation) =>126 return name_1.collate (name_2);
154 cell.wrap_width = allocation.width - (allocation.width / 3));127 }
155 }128
156129 bool filter_function (Gtk.ListBoxRow list_box_row) {
157 private void on_response (Gtk.Dialog source, int response_id) {130 var app_row = list_box_row.get_child () as AppRow;
158 if (response_id == Gtk.ResponseType.APPLY) {131 return search_bar.text.down () in app_row.app.name.down ();
159 app_chosen (get_app());132 }
160 }133
161 134 void connect_signals () {
162 destroy ();135 list.row_activated.connect (on_app_selected);
163 }136 search_bar.text_changed_pause.connect (apply_filter);
164137 custom_bar.activate.connect (on_custom_command_entered);
165 private Backend.KeyFile get_app () {138 }
166 Gtk.TreeIter iter;139
167 Gtk.TreePath path;140 void on_app_selected (Gtk.ListBoxRow list_box_row) {
168 Backend.KeyFile app = null;141 var app_row = list_box_row.get_child () as AppRow;
169142 app_row.app.copy_to_local ();
170 list.get_cursor (out path, null);143 app_row.app.active = true;
171 if (path != null) {144 app_chosen (app_row.app);
172 path = list_sort.convert_path_to_child_path (path);145 hide ();
173 path = list_filter.convert_path_to_child_path (path);146 }
174 list_store.get_iter (out iter, path);147
175 list_store.get (iter, 2, out app);148 void apply_filter () {
176 app.copy_to_local ();149 list.set_filter_func (filter_function);
177 app.active = true;150 }
178 return app;151
179 } else if (command.text != "" && command.text != null) {152 void on_custom_command_entered () {
180 return create_command ();153 var app = new Backend.KeyFile.from_command (custom_bar.text);
181 }154 app_chosen (app);
182 155 hide ();
183 return (Backend.KeyFile) null;156 }
184 }
185
186 private bool filter_func (Gtk.TreeModel model, Gtk.TreeIter iter) {
187 Backend.KeyFile key_file;
188 string name;
189
190 list_store.get (iter, 2, out key_file);
191 name = key_file.name + key_file.comment;
192
193 if (search_text == "")
194 return true;
195 else if (name != null)
196 return name.up ().contains (search_text.up ());
197 else
198 return false;
199 }
200
201 private Backend.KeyFile create_command () {
202 return new Backend.KeyFile.from_command (command.text);
203 }157 }
204}158}
205\ No newline at end of file159\ No newline at end of file
206160
=== modified file 'src/Plug.vala'
--- src/Plug.vala 2014-03-22 11:07:44 +0000
+++ src/Plug.vala 2014-04-07 13:03:25 +0000
@@ -19,67 +19,44 @@
1919
20public Switchboard.Plug get_plug (Module module) {20public Switchboard.Plug get_plug (Module module) {
21 debug ("Activating Startup Apps plug");21 debug ("Activating Startup Apps plug");
22 var plug = new Pantheon.Startup.Plug ();22 return new Pantheon.Startup.Plug ();
23 return plug;
24}23}
2524
26public class Pantheon.Startup.Plug : Switchboard.Plug {25public class Pantheon.Startup.Plug : Switchboard.Plug {
2726
28 Widgets.List list;27 Gtk.ScrolledWindow scrolled;
29 Widgets.Editor editor;28
30 Granite.Widgets.ThinPaned paned = null;
31
32 public Plug () {29 public Plug () {
33 Object (category: Category.PERSONAL,30 Object (category: Category.PERSONAL,
34 code_name: "personal-pantheon-startup",31 code_name: "personal-pantheon-startup",
35 display_name: _("Startup Apps"),32 display_name: _("Startup Apps"),
36 description: _("Shows Startup Applications Settings…"),33 description: _("Shows Startup Applications Settings…"),
37 icon: "preferences-system-session");34 icon: "preferences-system-session");
38 35
39 Backend.KeyFileFactory.init ();36 Backend.KeyFileFactory.init ();
40 }37 }
4138
42 public override Gtk.Widget get_widget () {39 public override Gtk.Widget get_widget () {
43 if (paned == null) {40 if (scrolled == null) {
44 setup_gui ();41 scrolled = new Gtk.ScrolledWindow (null, null);
45 connect_signals ();42 var list = new Widgets.List ();
46 initialize_state ();43 scrolled.add (list);
47 }44 }
48 45
49 return paned;46 return scrolled;
50 }47 }
51 48
52 public override void shown () {49 public override void shown () {
50 scrolled.show_all ();
53 }51 }
54 52
55 public override void hidden () {53 public override void hidden () {
56 }54 }
5755
58 public override void search_callback (string location) {56 public override void search_callback (string location) {
59 }57 }
6058
61 public override async Gee.TreeMap<string, string> search (string search) {59 public override async Gee.TreeMap <string, string> search (string search) {
62 return new Gee.TreeMap<string, string> (null, null);60 return new Gee.TreeMap <string, string> (null, null);
63 }
64
65 void setup_gui () {
66 this.list = new Widgets.List ();
67 this.editor = new Widgets.Editor ();
68
69 paned = new Granite.Widgets.ThinPaned ();
70 paned.pack1 (list, false, false);
71 paned.pack2 (editor, false, false);
72
73 paned.show_all ();
74 }
75
76 void connect_signals () {
77 list.selected.connect ((key_file) => {
78 editor.load_key_file (key_file);
79 });
80 }
81
82 void initialize_state () {
83 list.select_first_item ();
84 }61 }
85}62}
86\ No newline at end of file63\ No newline at end of file
8764
=== removed file 'src/Widgets/Editor.vala'
--- src/Widgets/Editor.vala 2014-03-22 11:07:44 +0000
+++ src/Widgets/Editor.vala 1970-01-01 00:00:00 +0000
@@ -1,102 +0,0 @@
1/***
2 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>
3
4 This program or library is free software; you can redistribute it
5 and/or modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library; if not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301 USA.
18***/
19
20public class Pantheon.Startup.Widgets.Editor : Gtk.Grid {
21
22 public string icon { get; set; }
23
24 public new string name {
25 get { return name_entry.text; }
26 set { name_entry.text = value; }
27 }
28
29 public string comment {
30 get { return comment_entry.text; }
31 set { comment_entry.text = value; }
32 }
33
34 public string command {
35 get { return command_entry.text; }
36 set { command_entry.text = value; }
37 }
38
39 public bool revive {
40 get; set;
41 }
42
43 Backend.KeyFile key_file;
44
45 Gtk.Entry name_entry;
46 Gtk.Entry comment_entry;
47 Gtk.Entry command_entry;
48
49 public Editor () {
50 var name_label = new Gtk.Label (_("Name:"));
51 name_label.halign = Gtk.Align.END;
52 name_entry = new Gtk.Entry ();
53 name_entry.placeholder_text = _("Application name");
54 name_entry.width_request = 300;
55 name_entry.valign = Gtk.Align.END;
56
57 var comment_label = new Gtk.Label (_("Description:"));
58 comment_label.halign = Gtk.Align.END;
59 comment_entry = new Gtk.Entry ();
60 comment_entry.placeholder_text = _("Short description");
61
62 var command_label = new Gtk.Label (_("Command:"));
63 command_label.halign = Gtk.Align.END;
64 command_entry = new Gtk.Entry ();
65 command_entry.placeholder_text = _("Command to execute");
66
67 this.attach (name_label, 0, 0, 1, 1);
68 this.attach (name_entry, 1, 0, 1, 1);
69 this.attach (comment_label, 0, 1, 1, 1);
70 this.attach (comment_entry, 1, 1, 1, 1);
71 this.attach (command_label, 0, 2, 1, 1);
72 this.attach (command_entry, 1, 2, 1, 1);
73
74 row_spacing = 6;
75 column_spacing = 6;
76 margin = 12;
77 halign = Gtk.Align.CENTER;
78
79 name_entry.notify["text"].connect (() => {
80 key_file.name = name_entry.text;
81 key_file.write_to_file ();
82 });
83
84 comment_entry.notify["text"].connect (() => {
85 key_file.comment = comment_entry.text;
86 key_file.write_to_file ();
87 });
88
89 command_entry.notify["text"].connect (() => {
90 key_file.command = command_entry.text;
91 key_file.write_to_file ();
92 });
93 }
94
95 public void load_key_file (Backend.KeyFile key_file) {
96 this.key_file = key_file;
97
98 name_entry.text = key_file.name;
99 comment_entry.text = key_file.comment;
100 command_entry.text = key_file.command;
101 }
102}
103\ No newline at end of file0\ No newline at end of file
1041
=== modified file 'src/Widgets/List.vala'
--- src/Widgets/List.vala 2014-03-22 11:07:44 +0000
+++ src/Widgets/List.vala 2014-04-07 13:03:25 +0000
@@ -17,187 +17,192 @@
17 Boston, MA 02110-1301 USA.17 Boston, MA 02110-1301 USA.
18***/18***/
1919
20class Pantheon.Startup.Widgets.List : Gtk.Grid {20class Pantheon.Startup.Widgets.AppRow : Gtk.Box {
2121
22 private Gtk.ListStore list_store;22 Gtk.Button delete_button;
23 private Gtk.TreeModelSort sorted_list;23 Gtk.Label label;
24 private Gtk.TreeView list;24 Gtk.Switch active_switch;
25 private Widgets.Toolbar toolbar;25 Gtk.Image image;
26 private Gtk.ScrolledWindow scroll;26
27 private Gtk.CellRendererToggle toggle;27 public Backend.KeyFile app { get; construct; }
2828
29 public signal void deleted ();
30
31 public AppRow (Backend.KeyFile app) {
32 Object (app: app);
33 setup ();
34 connect_signals ();
35 on_active_changed ();
36 }
37
38 void setup () {
39 orientation = Gtk.Orientation.HORIZONTAL;
40
41 var markup = app.create_markup ();
42 var icon = app.create_icon ();
43
44 margin = 6;
45 spacing = 12;
46
47 active_switch = new Gtk.Switch ();
48 active_switch.active = app.active;
49 add (active_switch);
50
51 image = new Gtk.Image.from_pixbuf (icon);
52 add (image);
53
54 label = new Gtk.Label (markup);
55 label.expand = true;
56 label.use_markup = true;
57 label.halign = Gtk.Align.START;
58 label.ellipsize = Pango.EllipsizeMode.END;
59 label.sensitive = app.active;
60 add (label);
61
62 delete_button = new Gtk.Button.with_label (_("Delete"));
63 delete_button.get_style_context ().add_class ("destructive-action");
64 delete_button.no_show_all = true;
65 delete_button.vexpand = false;
66 var button_box = new Gtk.ButtonBox (Gtk.Orientation.HORIZONTAL);
67 button_box.add (delete_button);
68 add (button_box);
69
70 show_all ();
71 }
72
73 void connect_signals () {
74 delete_button.clicked.connect (on_delete_clicked);
75 active_switch.notify["active"].connect (on_active_changed);
76 }
77
78 void on_delete_clicked () {
79 app.delete_file ();
80 deleted ();
81 }
82
83 void on_active_changed () {
84 var active = active_switch.active;
85 label.sensitive = active;
86 app.active = active;
87 app.write_to_file ();
88 }
89
90 public void show_delete (bool show) {
91 delete_button.no_show_all = !show;
92 delete_button.visible = show;
93 }
94}
95
96class Pantheon.Startup.Widgets.NewAppRow : Gtk.Box {
97
98 public signal void app_added (Backend.KeyFile app);
99
100 public NewAppRow () {
101 orientation = Gtk.Orientation.HORIZONTAL;
102
103 margin = 6;
104 spacing = 12;
105
106 var sw = new Gtk.Switch ();
107 sw.opacity = 0.0;
108 add (sw);
109
110 var add_button = new Gtk.Button.from_icon_name ("add", Gtk.IconSize.DIALOG);
111 add_button.relief = Gtk.ReliefStyle.NONE;
112 add (add_button);
113
114 var label = new Gtk.Label ("<i>" + _("Add Startup App") + "</i>");
115 label.expand = true;
116 label.use_markup = true;
117 label.halign = Gtk.Align.START;
118 label.ellipsize = Pango.EllipsizeMode.END;
119 add (label);
120
121 var app_chooser = new Dialogs.AppChooser (add_button);
122 app_chooser.modal = true;
123
124 app_chooser.app_chosen.connect ((key_file) => {
125 app_added (key_file);
126 });
127
128 add_button.clicked.connect (app_chooser.show_all);
129 }
130}
131
132class Pantheon.Startup.Widgets.List : Gtk.ListBox {
133
134 NewAppRow new_app_row;
135
136 public string search_string { get; set; }
137
29 public signal void selected (Backend.KeyFile app);138 public signal void selected (Backend.KeyFile app);
30139
31 enum Column {
32 ACTIVE,
33 ICON,
34 TEXT,
35 APP,
36 COUNT
37 }
38
39 public List () {140 public List () {
40 setup_scrolled ();141 setup_gui ();
41 setup_toolbar ();142 connect_signals ();
42143 }
43 attach (scroll, 0, 0, 1, 1);144
44 attach (toolbar, 0, 1, 1, 1);145 void setup_gui () {
45146 load_startup_apps ();
46 setup_signals ();147 add_new_app_row ();
47 148 }
48 load_apps ();149
49 }150 void connect_signals () {
50151 row_selected.connect (show_delete_button_on_select);
51 void setup_scrolled () {152 set_sort_func (sort_function);
52 list_store = new Gtk.ListStore (Column.COUNT,153 new_app_row.app_added.connect (add_app);
53 typeof (bool),154 }
54 typeof (Gdk.Pixbuf),155
55 typeof (string),156 void load_startup_apps () {
56 typeof (Backend.KeyFile)157 foreach (var path in get_auto_start_files ()) {
57 );158 var app = Backend.KeyFileFactory.get_or_create (path);
58159 add_app (app);
59 sorted_list = new Gtk.TreeModelSort.with_model (list_store);160 }
60 sorted_list.set_sort_column_id (Column.TEXT, Gtk.SortType.ASCENDING);161 }
61162
62 list = new Gtk.TreeView.with_model (sorted_list);163 void add_new_app_row () {
63 164 new_app_row = new NewAppRow ();
64 toggle = new Gtk.CellRendererToggle ();165 prepend (new_app_row);
65 list.insert_column_with_attributes (-1, null, toggle, "active", Column.ACTIVE);166 }
66167
67 var pixbuf = new Gtk.CellRendererPixbuf ();168 void show_delete_button_on_select (Gtk.ListBoxRow? selected_list_box_row) {
68 list.insert_column_with_attributes (-1, null, pixbuf, "pixbuf", Column.ICON);169 this.foreach ((row) => {
69 170 var list_box_row = row as Gtk.ListBoxRow;
70 var cell = new Gtk.CellRendererText ();171 var app_row = list_box_row.get_child () as AppRow;
71 list.insert_column_with_attributes (-1, null, cell, "markup", Column.TEXT);172 if (app_row != null)
72173 app_row.show_delete (false);
73 list.expand = true;174 });
74 list.enable_search = false;175
75 list.headers_visible = false;176 if (selected_list_box_row != null) {
76177 var selected_app_row = selected_list_box_row.get_child () as AppRow;
77 scroll = new Gtk.ScrolledWindow (null, null);178 selected_app_row.show_delete (true);
78 scroll.width_request = 300;179 }
79 scroll.add (list);180 }
80 }181
81182 int sort_function (Gtk.ListBoxRow list_box_row_1,
82 void setup_toolbar () {183 Gtk.ListBoxRow list_box_row_2) {
83 toolbar = new Widgets.Toolbar ();184 var row_1 = list_box_row_1.get_child ();
84 }185 var row_2 = list_box_row_2.get_child ();
85 186
86 void setup_signals () {187 if (row_1 is NewAppRow)
87 list.cursor_changed.connect (() => {188 return 1;
88 Gtk.TreeIter iter;189 if (row_2 is NewAppRow)
89 Gtk.TreePath tree_path;190 return -1;
90 GLib.Value app;191
91192 var name_1 = (row_1 as AppRow).app.name;
92 list.get_cursor (out tree_path, null);193 var name_2 = (row_2 as AppRow).app.name;
93 list.model.get_iter (out iter, tree_path);194 return name_1.collate (name_2);
94 list.model.get_value (iter, Column.APP, out app);195 }
95196
96 selected (app as Backend.KeyFile);
97 });
98
99 toggle.toggled.connect ((toggle, tree_path_string) => {
100 GLib.Value app;
101
102 Gtk.TreePath tree_path = new Gtk.TreePath.from_string (tree_path_string);
103 list.set_cursor (tree_path, null, false);
104 Gtk.TreeIter iter;
105 tree_path = sorted_list.convert_path_to_child_path (tree_path);
106
107 list_store.get_iter (out iter, tree_path);
108 list_store.set (iter, Column.ACTIVE, !toggle.active);
109 list_store.get_value (iter, Column.APP, out app);
110
111 (app as Backend.KeyFile).active = toggle.active;
112 (app as Backend.KeyFile).write_to_file ();
113 });
114
115 toolbar.clicked_remove_button.connect (() => {
116 remove_current_app ();
117 });
118
119 toolbar.clicked_add_button.connect ((key_file) => {
120 add_app (key_file.path);
121 });
122 }
123
124 void load_apps () {
125 foreach (var file in get_auto_start_files ())
126 add_app (file);
127 }
128
129 string[] get_auto_start_files () {197 string[] get_auto_start_files () {
130 var startup_dir = Utils.get_user_startup_dir ();198 var startup_dir = Utils.get_user_startup_dir ();
131 var enumerator = new Backend.DesktopFileEnumerator (startup_dir);199 var enumerator = new Backend.DesktopFileEnumerator (startup_dir);
132 return enumerator.get_desktop_files ();200 return enumerator.get_desktop_files ();
133 }201 }
134 202
135203 public void add_app (Backend.KeyFile app) {
136 public void select_first_item () {204 var row = new AppRow (app);
137 var path = new Gtk.TreePath.from_indices (0);205 prepend (row);
138 list.set_cursor (path, null, false);206 row.deleted.connect (() => remove (row.parent));
139 }207 }
140
141 public void remove_current_app () {
142 Gtk.TreeIter iter;
143 Gtk.TreePath path;
144 GLib.Value app;
145
146 list.get_cursor (out path, null);
147 path = sorted_list.convert_path_to_child_path (path);
148
149 list_store.get_iter (out iter, path);
150 list_store.get_value (iter, Column.APP, out app);
151 (app as Backend.KeyFile).delete_file ();
152
153 list_store.remove (iter);
154 }
155
156 public void add_app (string path) {
157 Gtk.TreeIter iter;
158
159 var app = Backend.KeyFileFactory.get_or_create (path);
160 var markup = app.create_markup ();
161 var icon = app.create_icon ();
162
163 list_store.append (out iter);
164 list_store.set (iter,
165 Column.ACTIVE, app.active,
166 Column.ICON, icon,
167 Column.TEXT, markup,
168 Column.APP, app
169 );
170
171 app.notify["name"].connect (() => {
172 update_list_item (iter, path);
173 });
174
175 app.notify["comment"].connect (() => {
176 update_list_item (iter, path);
177 });
178
179 app.notify["command"].connect (() => {
180 update_list_item (iter, path);
181 });
182 }
183
184 void update_list_item (Gtk.TreeIter iter, string path) {
185 var app = Backend.KeyFileFactory.get_or_create (path);
186 var markup = app.create_markup ();
187 var icon = app.create_icon ();
188
189 list_store.set (iter,
190 Column.ACTIVE, app.active,
191 Column.ICON, icon,
192 Column.TEXT, markup,
193 Column.APP, app
194 );
195 }
196
197 /*Gtk.TreeIter get_iter_from_index (int index) {
198 Gtk.TreeIter iter;
199 var path = new Gtk.TreePath.from_indices (0);
200 list_store.get_iter (out iter, path);
201 return iter;
202 }*/
203}208}
204\ No newline at end of file209\ No newline at end of file
205210
=== removed file 'src/Widgets/Toolbar.vala'
--- src/Widgets/Toolbar.vala 2014-03-22 11:07:44 +0000
+++ src/Widgets/Toolbar.vala 1970-01-01 00:00:00 +0000
@@ -1,61 +0,0 @@
1/***
2 Copyright (C) 2013 Julien Spautz <spautz.julien@gmail.com>
3
4 This program or library is free software; you can redistribute it
5 and/or modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library; if not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301 USA.
18***/
19
20class Pantheon.Startup.Widgets.Toolbar : Gtk.Toolbar {
21
22 public signal void clicked_add_button (Backend.KeyFile key_file);
23 public signal void clicked_remove_button ();
24
25 public Toolbar () {
26 set_style (Gtk.ToolbarStyle.ICONS);
27 set_icon_size (Gtk.IconSize.SMALL_TOOLBAR);
28 set_show_arrow (false);
29 get_style_context ().add_class (Gtk.STYLE_CLASS_INLINE_TOOLBAR);
30 get_style_context ().set_junction_sides (Gtk.JunctionSides.TOP);
31
32 var add_button = new Gtk.ToolButton (null, _("Add..."));
33 var remove_button = new Gtk.ToolButton (null, _("Remove"));
34
35 add_button.set_icon_name ("list-add-symbolic");
36 remove_button.set_icon_name ("list-remove-symbolic");
37
38 add_button.set_tooltip_text (_("Add..."));
39 remove_button.set_tooltip_text (_("Remove"));
40
41 insert (add_button, -1);
42 insert (remove_button, -1);
43
44 add_button.clicked.connect (() => {
45 var app_chooser = new Dialogs.AppChooser ();
46 app_chooser.modal = true;
47
48 app_chooser.app_chosen.connect ((key_file) => {
49 clicked_add_button (key_file);
50 });
51
52 app_chooser.move_to_widget (add_button);
53 app_chooser.show_all ();
54 app_chooser.present ();
55 app_chooser.run ();
56 app_chooser.destroy ();
57 });
58
59 remove_button.clicked.connect (() => clicked_remove_button ());
60 }
61}
62\ No newline at end of file0\ No newline at end of file

Subscribers

People subscribed via source and target branches

to all changes: