Merge lp:~voldyman/granite/contractor-wid-dep-new-Contractor into lp:~elementary-pantheon/granite/granite

Proposed by Sergey "Shnatsel" Davidoff
Status: Merged
Merged at revision: 559
Proposed branch: lp:~voldyman/granite/contractor-wid-dep-new-Contractor
Merge into: lp:~elementary-pantheon/granite/granite
Diff against target: 448 lines (+232/-91)
4 files modified
demo/main.vala (+11/-37)
lib/Services/Contractor.vala (+219/-54)
lib/Widgets/ContractorMenu.vala (+1/-0)
lib/Widgets/ContractorView.vala (+1/-0)
To merge this branch: bzr merge lp:~voldyman/granite/contractor-wid-dep-new-Contractor
Reviewer Review Type Date Requested Status
Sergey "Shnatsel" Davidoff (community) Approve
elementary Apps team Pending
Review via email: mp+158161@code.launchpad.net

This proposal supersedes a proposal from 2013-04-10.

Description of the change

* Added Granite.Services.Contractor methods that work with the new Contractor API
* Deprecated the widgets that work with the old and abandoned API instead of including updated widgets, because Dan doesn't want any Contractor widgets in Granite

This merge does not cause an API/ABI break.

To post a comment you must log in.
Revision history for this message
Sergey "Shnatsel" Davidoff (shnatsel) wrote : Posted in a previous version of this proposal

This seems to break the ABI (according to Rico's check in deb-packaging) even thought it shouldn't.

review: Needs Fixing
557. By Akshay Shekher

Deprecated ContractorView and ContractorMenu. Added support for the new contractor API in services/Contractor.vala

558. By Akshay Shekher

Change the constructor of Contractor.vala to private

Revision history for this message
Victor Martinez (victored) wrote :

Wow, fantastic work here!

Since overall this is very cohesive and well done, I'll just focus on a couple of details to make it even better IMHO:

(1) Why isn't ensure() private?
(2) Make sure the interface ContractorDBus is internal to Granite.
(3) The names of execute_* () suggest URIs, but in the parameters they are named paths. What do these really expect?

(1) and (2) are easily fixable.

Regarding (3), since this is a wrapper to the DBus API, I could suggest receiving GLib.File objects instead, and calling get_path() or get_uri() on them depending on what Contractor is expecting. Make sure you rename the methods accordingly (..._with_files).

Regarding the API, it would be nice to have something like:
    Granite.Services.Contract contract;
    File file;
    // get some instances from somewhere...
    contract.execute_with_file (file);

Instead of:
    string contract_id;
    string file_uri;
    // get those strings from somewhere...
    Granite.Services.Contractor.execute_with_uri (contract_id, uri);

We'd have something like:

public interface Granite.Services.Contract : Object {
    public abstract void execute_with_file (File file);
    public abstract void execute_with_files (File[] files);
}

public class Granite.Services.Contractor : Object {
    private struct GenericContract { ... }

    // Private implementation of Contract
    private class ContractorContract : Object, Contract {
        private GenericContract data;
        private Con

        public ContractorContract (GenericContract data) {
            this.data = data;
        }

        public void execute_with_file (File file) {
            // Yes, we have access to the parent class's private static methods
            execute_with_uri (data.id, file.get_uri ());
        }

        [...]
    }

    [...]

    // Of course, this method could be removed and its contents may be moved to
    // Contract.execute_with_file() directly.
    private static void execute_with_uri (string id, string uri);

    [...]
}

Finally, please fix the coding style issues. A quick read through the code will show what I mean. Of course there are only a couple of them :)

559. By Akshay Shekher

Changed GenericContract to ContractorContract and renamed arguments

560. By Akshay Shekher

fixed coding style

561. By Akshay Shekher

More OO contractor interface

Revision history for this message
Victor Martinez (victored) wrote :

Thank you for your work Akshay!

Actually, my suggestion was not related to hiding contractor behind an interface, since it's only a facade. What I was suggesting was abstracting the Contracts themselves, so that no implementation details are made available to the client code.

Please take a look to rev. 562 of lp:~victored/granite/contractor-wrapper

Revision history for this message
Victor Martinez (victored) wrote :

For an overview of the API, you can go here: http://pastebin.ubuntu.com/5719759/ :)

Revision history for this message
Sergey "Shnatsel" Davidoff (shnatsel) wrote :

Looks good to me (but disregard my opinion because I'm OOP-impaired). In hindsight, using Glib.File instead of messing with paths and URIs is a good idea.

Revision history for this message
Sergey "Shnatsel" Davidoff (shnatsel) :
review: Approve
Revision history for this message
Sergey "Shnatsel" Davidoff (shnatsel) wrote :

Argh, Victor's updated branch is not yet merged into this one... but I don't have anything left to review here anyway, so "Approved" from me it is.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'demo/main.vala'
--- demo/main.vala 2013-03-20 15:42:25 +0000
+++ demo/main.vala 2013-04-18 12:53:23 +0000
@@ -13,7 +13,7 @@
13 but WITHOUT ANY WARRANTY; without even the implied warranty of13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.15 Lesser General Public License for more details.
16 16
17 You should have received a copy of the GNU Lesser General17 You should have received a copy of the GNU Lesser General
18 Public License along with this library; if not, write to the18 Public License along with this library; if not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
@@ -276,32 +276,6 @@
276 right_sep.set_expand (true);276 right_sep.set_expand (true);
277 main_toolbar.insert (right_sep, -1);277 main_toolbar.insert (right_sep, -1);
278278
279 // Contractor
280 var contractor_tab = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
281 var text_view = new Gtk.TextView ();
282
283 var hash_tables = Granite.Services.Contractor.get_contract ("/.zip", "application/zip");
284 foreach (var hash_table in hash_tables) {
285 text_view.buffer.text += hash_table.lookup ("Name")
286 + ": " + hash_table.lookup ("Description")
287 + " icon: " + hash_table.lookup ("Exec") + "\n";
288 }
289
290 contractor_tab.add (text_view);
291 contractor_tab.add (new Granite.Widgets.ContractorView ("file:///home/user/file.txt", "text/plain"));
292 var contractor_menu = new Granite.Widgets.ContractorMenu ("/home/user/file.txt", "text");
293 var contractor_button_image = new Gtk.Image.from_icon_name ("document-export",
294 Gtk.IconSize.LARGE_TOOLBAR);
295 var contractor_tool_item = new Granite.Widgets.ToolButtonWithMenu (contractor_button_image,
296 "Share", contractor_menu);
297 main_toolbar.insert (contractor_tool_item, -1);
298
299 contractor_tool_item.halign = contractor_tool_item.valign = Gtk.Align.CENTER;
300
301 var contractor_item = new SourceListItem ("Contractor");
302 contractor_item.page_num = page_switcher.append_page (contractor_tab, null);
303 services_category.add (contractor_item);
304
305 // Search Entry279 // Search Entry
306 var search_entry = new Granite.Widgets.SearchBar ("Search");280 var search_entry = new Granite.Widgets.SearchBar ("Search");
307 var search_item = new Gtk.ToolItem ();281 var search_item = new Gtk.ToolItem ();
@@ -343,42 +317,42 @@
343317
344 private void show_light_window () {318 private void show_light_window () {
345 var light_window = new Granite.Widgets.LightWindow ();319 var light_window = new Granite.Widgets.LightWindow ();
346 320
347 var light_window_notebook = new Granite.Widgets.StaticNotebook ();321 var light_window_notebook = new Granite.Widgets.StaticNotebook ();
348 var entry = new Gtk.Entry ();322 var entry = new Gtk.Entry ();
349 var open_drop = new Gtk.ComboBoxText ();323 var open_drop = new Gtk.ComboBoxText ();
350 var open_lbl = new LLabel ("Alwas Open Mpeg Video Files with Audience");324 var open_lbl = new LLabel ("Alwas Open Mpeg Video Files with Audience");
351 325
352 var grid = new Gtk.Grid ();326 var grid = new Gtk.Grid ();
353 grid.attach (new Gtk.Image.from_icon_name ("video-x-generic", Gtk.IconSize.DIALOG), 0, 0, 1, 2);327 grid.attach (new Gtk.Image.from_icon_name ("video-x-generic", Gtk.IconSize.DIALOG), 0, 0, 1, 2);
354 grid.attach (entry, 1, 0, 1, 1);328 grid.attach (entry, 1, 0, 1, 1);
355 grid.attach (new LLabel ("1.13 GB, Mpeg Video File"), 1, 1, 1, 1);329 grid.attach (new LLabel ("1.13 GB, Mpeg Video File"), 1, 1, 1, 1);
356 330
357 grid.attach (light_window_notebook, 0, 2, 2, 1);331 grid.attach (light_window_notebook, 0, 2, 2, 1);
358 332
359 var general = new Gtk.Grid ();333 var general = new Gtk.Grid ();
360 general.attach (new LLabel.markup ("<b>Info:</b>"), 0, 0, 2, 1);334 general.attach (new LLabel.markup ("<b>Info:</b>"), 0, 0, 2, 1);
361 335
362 general.attach (new LLabel.right ("Created:"), 0, 1, 1, 1);336 general.attach (new LLabel.right ("Created:"), 0, 1, 1, 1);
363 general.attach (new LLabel.right ("Modified:"), 0, 2, 1, 1);337 general.attach (new LLabel.right ("Modified:"), 0, 2, 1, 1);
364 general.attach (new LLabel.right ("Opened:"), 0, 3, 1, 1);338 general.attach (new LLabel.right ("Opened:"), 0, 3, 1, 1);
365 general.attach (new LLabel.right ("Mimetype:"), 0, 4, 1, 1);339 general.attach (new LLabel.right ("Mimetype:"), 0, 4, 1, 1);
366 general.attach (new LLabel.right ("Location:"), 0, 5, 1, 1);340 general.attach (new LLabel.right ("Location:"), 0, 5, 1, 1);
367 341
368 general.attach (new LLabel ("Today at 9:50 PM"), 1, 1, 1, 1);342 general.attach (new LLabel ("Today at 9:50 PM"), 1, 1, 1, 1);
369 general.attach (new LLabel ("Today at 9:50 PM"), 1, 2, 1, 1);343 general.attach (new LLabel ("Today at 9:50 PM"), 1, 2, 1, 1);
370 general.attach (new LLabel ("Today at 10:00 PM"), 1, 3, 1, 1);344 general.attach (new LLabel ("Today at 10:00 PM"), 1, 3, 1, 1);
371 general.attach (new LLabel ("video/mpeg"), 1, 4, 1, 1);345 general.attach (new LLabel ("video/mpeg"), 1, 4, 1, 1);
372 general.attach (new LLabel ("/home/daniel/Downloads"), 1, 5, 1, 1);346 general.attach (new LLabel ("/home/daniel/Downloads"), 1, 5, 1, 1);
373 347
374 general.attach (new LLabel.markup ("<b>Open with:</b>"), 0, 6, 2, 1);348 general.attach (new LLabel.markup ("<b>Open with:</b>"), 0, 6, 2, 1);
375 general.attach (open_drop, 0, 7, 2, 1);349 general.attach (open_drop, 0, 7, 2, 1);
376 general.attach (open_lbl, 0, 8, 2, 1);350 general.attach (open_lbl, 0, 8, 2, 1);
377 351
378 light_window_notebook.append_page (general, new Gtk.Label ("General"));352 light_window_notebook.append_page (general, new Gtk.Label ("General"));
379 light_window_notebook.append_page (new Gtk.Label ("More"), new Gtk.Label ("More"));353 light_window_notebook.append_page (new Gtk.Label ("More"), new Gtk.Label ("More"));
380 light_window_notebook.append_page (new Gtk.Label ("Sharing"), new Gtk.Label ("Sharing"));354 light_window_notebook.append_page (new Gtk.Label ("Sharing"), new Gtk.Label ("Sharing"));
381 355
382 open_lbl.margin_left = 24;356 open_lbl.margin_left = 24;
383 open_drop.margin_left = 12;357 open_drop.margin_left = 12;
384 open_drop.append ("audience", "Audience");358 open_drop.append ("audience", "Audience");
@@ -389,7 +363,7 @@
389 entry.text = "Cool Hand Luke";363 entry.text = "Cool Hand Luke";
390 general.column_spacing = 6;364 general.column_spacing = 6;
391 general.row_spacing = 6;365 general.row_spacing = 6;
392 366
393 light_window.add (grid);367 light_window.add (grid);
394 light_window.show_all ();368 light_window.show_all ();
395 }369 }
396370
=== modified file 'lib/Services/Contractor.vala'
--- lib/Services/Contractor.vala 2013-04-04 20:05:38 +0000
+++ lib/Services/Contractor.vala 2013-04-18 12:53:23 +0000
@@ -10,7 +10,7 @@
10 but WITHOUT ANY WARRANTY; without even the implied warranty of10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.12 Lesser General Public License for more details.
13 13
14 You should have received a copy of the GNU Lesser General14 You should have received a copy of the GNU Lesser General
15 Public License along with this library; if not, write to the15 Public License along with this library; if not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
@@ -19,86 +19,251 @@
1919
20namespace Granite.Services {20namespace Granite.Services {
2121
22 [DBus (name = "org.elementary.contractor")]22 public struct ContractorContract {
23 interface ContractorDBus : Object23 string id;
24 {24 string display_name;
25 string description;
26 string icon_path;
27
28 public int execute_uri (string uri) {
29 return Contractor.execute_with_uri (this.id, uri);
30 }
31 public int execute_uris (string[] uris) {
32 return Contractor.execute_with_uri_list (this.id, uris);
33 }
34 }
35
36 [DBus (name = "org.elementary.Contractor")]
37 internal interface ContractorDBus : Object {
38 [Deprecated]
25 public abstract GLib.HashTable<string,string>[] GetServicesByLocation (string strlocation, string? file_mime="") throws IOError;39 public abstract GLib.HashTable<string,string>[] GetServicesByLocation (string strlocation, string? file_mime="") throws IOError;
40 [Deprecated]
26 public abstract GLib.HashTable<string,string>[] GetServicesByLocationsList (GLib.HashTable<string,string>[] locations) throws IOError;41 public abstract GLib.HashTable<string,string>[] GetServicesByLocationsList (GLib.HashTable<string,string>[] locations) throws IOError;
42 public abstract ContractorContract[] list_all_contracts () throws Error;
43 public abstract ContractorContract[] get_contracts_by_mime (string mime_type) throws Error;
44 public abstract ContractorContract[] get_contracts_by_mimelist (string[] mime_types) throws Error;
45 public abstract int execute_with_uri (string id, string uri) throws Error;
46 public abstract int execute_with_uri_list (string id, string[] uri) throws Error;
47 }
48
49 /*
50 * interface that the contractor class must implement
51 */
52 public interface ContractorIface {
53 public abstract ContractorContract[] get_contracts () throws ContractError;
54 public abstract ContractorContract get_for_id (string id) throws ContractError;
27 }55 }
2856
29 /**57 /**
30 * A way to handle contractor, a way to communicate between apps.58 * A way to handle contractor, a way to communicate between apps.
31 * 59 *
32 * /!\ Highly unstable API
33 */60 */
34 public class Contractor : Object61 public class Contractor : Object, ContractorIface {
35 {62
63 private string? mime_type;
64 private string[]? mime_types;
65 private ContractorContract[]? contracts;
66
3667
37 internal ContractorDBus contract;68 internal ContractorDBus contract;
3869
39 internal static Contractor? contractor = null;70 internal static Contractor? contractor = null;
4071
41 /**72 /**
42 * This creates a new Contractor 73 * This creates a new Contractor
43 */74 */
44 public Contractor()75 private Contractor () {
45 {76 try {
46 try
47 {
48 contract = Bus.get_proxy_sync (BusType.SESSION,77 contract = Bus.get_proxy_sync (BusType.SESSION,
49 "org.elementary.contractor",78 "org.elementary.Contractor",
50 "/org/elementary/contractor");79 "/org/elementary/contractor");
51 }80 } catch (IOError e) {
52 catch (IOError e)81 stderr.printf ("%s\n", e.message);
53 {82 }
54 stderr.printf ("%s\n", e.message);83 }
55 }84
56 }85 internal static void ensure () {
5786 if (contractor == null) {
58 internal static void ensure ()87 contractor = new Contractor ();
59 {88 }
60 if(contractor == null) contractor = new Contractor ();89 }
61 }90
6291 /*
63 /**92 * Class constructor for single mime type
64 * This searches for available contracts of a particular file93 */
65 * 94 public Contractor.for_mime (string mime) {
95 this.mime_type = mime;
96 this.mime_types = null;
97 }
98
99 /*
100 * Class constructor for multiple mime types
101 */
102 public Contractor.for_mime_list (string[] mimes) {
103 this.mime_types = mimes;
104 this.mime_type = null;
105 }
106
107 /*
108 * searches for contracts that support the specified mime type(s)
109 * throws error if mime type is not set.
110 */
111 public ContractorContract[] get_contracts () throws ContractError {
112 if (this.mime_type != null) {
113 this.contracts = get_contracts_by_mime (this.mime_type);
114 return contracts;
115 } else if (this.mime_types != null) {
116 contracts = get_contracts_by_mimelist (this.mime_types);
117 return this.contracts;
118 }
119 throw new ContractError.OBJECT_ERROR ("data members are null");
120 }
121
122 /*
123 * Searches for the contract with with the id as specified
124 * if not found, throws NOT_FOUND error
125 */
126 public ContractorContract get_for_id (string id) throws ContractError {
127 if (this.contracts == null) {
128 throw new ContractError.NOT_FOUND ("contracts were not found");
129 }
130 foreach (var cont in this.contracts) {
131 if (cont.id == id)
132 return cont;
133 }
134 throw new ContractError.NOT_FOUND ("Contract of this ID was not found");
135 }
136
137 /**
138 * Lists all the contracts
139 *
140 * @return an array of struct ContractorContract
141 */
142 public static ContractorContract[] list_all_contracts () {
143 ensure ();
144 ContractorContract[] contracts = null;
145
146 try {
147 contracts = contractor.contract.list_all_contracts ();
148 } catch (Error e) {
149 stderr.printf ("%s\n", e.message);
150 }
151
152 return contracts;
153 }
154
155 /**
156 * This searches for available contracts of a particular file
157 *
158 * @param mime mime type of file
159 * @return an array of struct ContractorContract
160 */
161 public static ContractorContract[] get_contracts_by_mime (string mime_type) {
162 ensure ();
163 ContractorContract[] contracts = null;
164
165 try {
166 contracts = contractor.contract.get_contracts_by_mime (mime_type);
167 } catch (Error e) {
168 stderr.printf ("%s\n", e.message);
169 }
170
171 return contracts;
172 }
173
174 /**
175 * generate contracts for a list of mime types. the contracts which support
176 * all of them are returned
177 *
178 * @param locations Array of MimeTypes
179 * @return array of struct (ContractorContract)
180 */
181 public static ContractorContract[] get_contracts_by_mimelist (string[] mime_types) {
182 ensure ();
183 ContractorContract[] contracts = null;
184
185 try {
186 contracts = contractor.contract.get_contracts_by_mimelist (mime_types);
187 } catch (Error e) {
188 stderr.printf ("%s\n", e.message);
189 }
190
191 return contracts;
192 }
193
194 /**
195 * This executes the exec parameter provided by the contract of given id
196 * with the uri as arguments
197 *
198 * @param id id of the contract
199 * @param uris uri to execute
200 * @return int status of execution
201 */
202 public static int execute_with_uri (string id, string uri) {
203 ensure ();
204 int ret_val = 1;
205
206 try {
207 ret_val = contractor.contract.execute_with_uri (id, uri);
208 } catch (Error e) {
209 stderr.printf ("%s\n", e.message);
210 }
211 return ret_val;
212 }
213
214 /**
215 * This executes the exec parameter provided by the contract of given id
216 * with the uris as arguments
217 *
218 * @param id id of the contract
219 * @param uris array of uris to execute
220 * @return int status of execution
221 */
222 public static int execute_with_uri_list (string id, string[] uris) {
223 ensure ();
224 int ret_val = 1;
225
226 try {
227 ret_val = contractor.contract.execute_with_uri_list (id, uris);
228 } catch (Error e) {
229 stderr.printf ("%s\n", e.message);
230 }
231 return ret_val;
232 }
233
234 /**
235 * The functions below have been deprecated in order to support the
236 * the new contractor API.
237 */
238
239 /**
240 * This searches for available contracts of a particular file
241 *
66 * @param uri uri of file242 * @param uri uri of file
67 * @param mime mime type of file243 * @param mime mime type of file
68 * @return Hashtable of available contracts244 * @return Hashtable of available contracts
69 */245 */
70 public static GLib.HashTable<string,string>[] get_contract(string uri, string mime)246 [Deprecated]
71 {247 public static GLib.HashTable<string,string>[] get_contract(string uri, string mime) {
72 ensure ();248
73 GLib.HashTable<string,string>[] contracts = null;249 return { new GLib.HashTable<string,string> (null, null) };
74
75 try {
76 contracts = contractor.contract.GetServicesByLocation(uri, mime);
77 }catch (IOError e) {
78 stderr.printf ("%s\n", e.message);
79 }
80
81 return contracts;
82 }250 }
83251
84 /**252 /**
85 * generate contracts for rguments and filter them by common parent mimetype.253 * generate contracts for rguments and filter them by common parent mimetype.
86 * 254 *
87 * @param locations Hashtable of locations255 * @param locations Hashtable of locations
88 * @return Hashtable of available contracts256 * @return Hashtable of available contracts
89 */257 */
90 public static GLib.HashTable<string,string>[] get_selection_contracts (GLib.HashTable<string, string>[] locations)258 [Deprecated]
91 {259 public static GLib.HashTable<string,string>[] get_selection_contracts (GLib.HashTable<string, string>[] locations) {
92 ensure ();260
93 GLib.HashTable<string,string>[] contracts = null;261 return { new GLib.HashTable<string,string> (null, null) };
94
95 try {
96 contracts = contractor.contract.GetServicesByLocationsList (locations);
97 }catch (IOError e) {
98 stderr.printf ("%s\n", e.message);
99 }
100
101 return contracts;
102 }262 }
103 }263 }
264
265 public errordomain ContractError {
266 OBJECT_ERROR,
267 NOT_FOUND
268 }
104}269}
105270
=== modified file 'lib/Widgets/ContractorMenu.vala'
--- lib/Widgets/ContractorMenu.vala 2013-03-20 15:42:25 +0000
+++ lib/Widgets/ContractorMenu.vala 2013-04-18 12:53:23 +0000
@@ -20,6 +20,7 @@
20/**20/**
21 * This class provides a simple menu for managing Contractor.21 * This class provides a simple menu for managing Contractor.
22 */22 */
23[Deprecated]
23public class Granite.Widgets.ContractorMenu : Gtk.Menu {24public class Granite.Widgets.ContractorMenu : Gtk.Menu {
24 /**25 /**
25 * The Hashtable of available contracts26 * The Hashtable of available contracts
2627
=== modified file 'lib/Widgets/ContractorView.vala'
--- lib/Widgets/ContractorView.vala 2013-03-20 15:42:25 +0000
+++ lib/Widgets/ContractorView.vala 2013-04-18 12:53:23 +0000
@@ -22,6 +22,7 @@
22/**22/**
23 * This class provides a simple way to look at contracts from Contractor23 * This class provides a simple way to look at contracts from Contractor
24 */24 */
25[Deprecated]
25public class Granite.Widgets.ContractorView : TreeView {26public class Granite.Widgets.ContractorView : TreeView {
26 27
27 /**28 /**

Subscribers

People subscribed via source and target branches