Merge lp:~victored/pantheon-files/contractor-plugin into lp:~elementary-apps/pantheon-files/trunk

Proposed by Victor Martinez on 2013-04-23
Status: Merged
Merged at revision: 1157
Proposed branch: lp:~victored/pantheon-files/contractor-plugin
Merge into: lp:~elementary-apps/pantheon-files/trunk
Diff against target: 555 lines (+143/-311)
7 files modified
libcore/PluginManager.vala (+34/-2)
plugins/CMakeLists.txt (+0/-1)
plugins/contractor/CMakeLists.txt (+3/-1)
plugins/contractor/plugin.vala (+106/-120)
plugins/extended-actions/CMakeLists.txt (+0/-28)
plugins/extended-actions/extended-actions.plug (+0/-3)
plugins/extended-actions/plugin.vala (+0/-156)
To merge this branch: bzr merge lp:~victored/pantheon-files/contractor-plugin
Reviewer Review Type Date Requested Status
Sergey "Shnatsel" Davidoff (community) 2013-04-23 Approve on 2013-04-24
Review via email: mp+160377@code.launchpad.net

Description of the change

- Rewrite contractor plugin to use new API. This talks to Contractor by using Granite's Proxy API.
- Drop extended-actions plugin. Same API as previous Contractor.

To post a comment you must log in.
1160. By Victor Martinez on 2013-04-23

Destroy plugin menu items properly.

This implied some minor changes to the core library. Special care was taken to avoid breaking the existing plugin ABI.

I'm afraid the recent commit didn't help resolve the bug mentioned in https://code.launchpad.net/~victored/granite/contractor-wrapper/+merge/159948/comments/353537 at all

Also, the right-click menu should list contract *names* and show descriptions only as tooltips on them.

review: Needs Fixing

The old implementation showed descriptions only because names were purely technical, the only human-readable thing was description.

And it gets worse! This is where it may end up: http://pix.toile-libre.org/upload/original/1366754094.png

1161. By Victor Martinez on 2013-04-24

Fix bug involving duplicate menu entries.

The code was assuming that the menu passed to the plugin was the same the plugins were added to initially, which is incorrect.

Victor Martinez (victored) wrote :

Hey Sergey, thank you for testing!

I'm no longer able to reproduce the bug here.

Works properly for me too.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'libcore/PluginManager.vala'
--- libcore/PluginManager.vala 2012-11-06 19:52:04 +0000
+++ libcore/PluginManager.vala 2013-04-24 17:03:26 +0000
@@ -30,7 +30,11 @@
30 string plugin_dir;30 string plugin_dir;
31 Gee.List<string> names;31 Gee.List<string> names;
32 bool in_available = false;32 bool in_available = false;
33 public GLib.List<Gtk.Widget>? menus;33
34 [Deprecated (replacement = "Marlin.PluginManager.menuitem_references")]
35 public GLib.List<Gtk.Widget>? menus; // this doesn't manage GObject references properly
36
37 public Gee.List<Gtk.Widget> menuitem_references { get; private set; }
3438
35 public PluginManager(Settings settings, string field, string plugin_dir)39 public PluginManager(Settings settings, string field, string plugin_dir)
36 {40 {
@@ -39,6 +43,8 @@
39 this.plugin_dir = plugin_dir;43 this.plugin_dir = plugin_dir;
40 plugin_hash = new Gee.HashMap<string,Plugins.Base>();44 plugin_hash = new Gee.HashMap<string,Plugins.Base>();
41 names = new Gee.ArrayList<string>();45 names = new Gee.ArrayList<string>();
46
47 menuitem_references = new Gee.LinkedList<Gtk.Widget> ();
42 }48 }
43 49
44 public void load_plugins()50 public void load_plugins()
@@ -147,10 +153,36 @@
147 153
148 public void hook_context_menu(Gtk.Widget menu, List<GOF.File> files)154 public void hook_context_menu(Gtk.Widget menu, List<GOF.File> files)
149 {155 {
156 drop_menu_references (menu);
157
158 if (menu is Gtk.Menu)
159 drop_plugin_menuitems (menu as Gtk.Menu);
160
161
162 foreach (var plugin in plugin_hash.values)
163 plugin.context_menu (menu, files);
164 }
165
166 private void drop_plugin_menuitems (Gtk.Menu menu) {
167 var plugin_menu = menu as Gtk.Menu;
168
169 assert (plugin_menu != null);
170
171 foreach (var menu_item in menuitem_references)
172 menu_item.parent.remove (menu_item);
173
174 menuitem_references.clear ();
175 }
176
177 [Deprecated (replacement = "Marlin.PluginManager.drop_plugin_menuitems")]
178 private void drop_menu_references (Gtk.Widget menu) {
179 if (menus == null)
180 return;
181
150 foreach (var item in menus)182 foreach (var item in menus)
151 item.destroy ();183 item.destroy ();
184
152 menus = null;185 menus = null;
153 foreach(var plugin in plugin_hash.values) plugin.context_menu (menu, files);
154 }186 }
155 187
156 public void ui(Gtk.UIManager data)188 public void ui(Gtk.UIManager data)
157189
=== modified file 'plugins/CMakeLists.txt'
--- plugins/CMakeLists.txt 2012-06-01 21:34:30 +0000
+++ plugins/CMakeLists.txt 2013-04-24 17:03:26 +0000
@@ -1,4 +1,3 @@
1add_subdirectory(extended-actions)
2add_subdirectory(contractor)1add_subdirectory(contractor)
3add_subdirectory(marlin-trash)2add_subdirectory(marlin-trash)
4add_subdirectory(marlin-ctags)3add_subdirectory(marlin-ctags)
54
=== modified file 'plugins/contractor/CMakeLists.txt'
--- plugins/contractor/CMakeLists.txt 2013-04-22 07:45:04 +0000
+++ plugins/contractor/CMakeLists.txt 2013-04-24 17:03:26 +0000
@@ -2,7 +2,8 @@
2pkg_check_modules(DEPS REQUIRED2pkg_check_modules(DEPS REQUIRED
3 gtk+-3.03 gtk+-3.0
4 gee-1.04 gee-1.0
5 glib-2.0)5 glib-2.0
6 granite)
6set(CFLAGS7set(CFLAGS
7 ${DEPS_CFLAGS} ${DEPS_CFLAGS_OTHER}8 ${DEPS_CFLAGS} ${DEPS_CFLAGS_OTHER}
8)9)
@@ -19,6 +20,7 @@
19PACKAGES20PACKAGES
20 gtk+-3.021 gtk+-3.0
21 gee-1.022 gee-1.0
23 granite
22OPTIONS24OPTIONS
23 --thread)25 --thread)
24add_library(marlin-contractor SHARED26add_library(marlin-contractor SHARED
2527
=== modified file 'plugins/contractor/plugin.vala'
--- plugins/contractor/plugin.vala 2012-06-02 15:49:15 +0000
+++ plugins/contractor/plugin.vala 2013-04-24 17:03:26 +0000
@@ -2,8 +2,10 @@
2 * Authors:2 * Authors:
3 * Lucas Baudin <xapantu@gmail.com>3 * Lucas Baudin <xapantu@gmail.com>
4 * ammonkey <am.monkeyd@gmail.com>4 * ammonkey <am.monkeyd@gmail.com>
5 * 5 * Victor Martinez <victoreduardm@gmail.com>
6 *
6 * Copyright (C) Lucas Baudin 2011 <xapantu@gmail.com>7 * Copyright (C) Lucas Baudin 2011 <xapantu@gmail.com>
8 * Copyright (C) 2013 elementary
7 * 9 *
8 * Marlin is free software: you can redistribute it and/or modify it10 * Marlin is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the11 * under the terms of the GNU General Public License as published by the
@@ -19,138 +21,122 @@
19 * with this program. If not, see <http://www.gnu.org/licenses/>.21 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */22 */
2123
22using Gtk;24public class Marlin.Plugins.ContractMenuItem : Gtk.MenuItem {
23using Gee;25 private Granite.Services.Contract contract;
2426 private File[] files;
25[DBus (name = "org.elementary.contractor")]27
26public interface ContractorService : Object28 public ContractMenuItem (Granite.Services.Contract contract, File[] files) {
27{29 this.contract = contract;
28 //public abstract GLib.HashTable<string,string>[] GetServicesByMime (string mime) throws IOError;30 this.files = files;
29 public abstract GLib.HashTable<string,string>[] GetServicesByLocation (string strlocation, string? file_mime="") throws IOError;31
30 public abstract GLib.HashTable<string,string>[] GetServicesByLocationsList (GLib.HashTable<string, string>[] locations) throws IOError;32 label = contract.get_display_name ();
33 tooltip_text = contract.get_description ();
34 }
35
36 public override void activate () {
37 try {
38 if (files.length == 1)
39 contract.execute_with_file (files[0]);
40 else
41 contract.execute_with_files (files);
42 } catch (Error err) {
43 warning (err.message);
44 }
45 }
31}46}
3247
33public class Marlin.Plugins.Contractor : Marlin.Plugins.Base48public class Marlin.Plugins.Contractor : Marlin.Plugins.Base {
34{49 private Gtk.UIManager ui_manager;
35 UIManager ui_manager;50 private Gtk.Menu menu;
36 Gtk.Menu menu;51 private GOF.File current_directory = null;
37 GOF.File current_directory = null;52
38 unowned GLib.List<GOF.File> selection;53 public Contractor () {
39 GLib.HashTable<string,string>[] services = null;54 }
40 55
41 private ContractorService service_eactions;56 public override void context_menu (Gtk.Widget? widget, List<GOF.File> gof_files) {
42
43 public Contractor ()
44 {
45 try {
46 service_eactions = Bus.get_proxy_sync (BusType.SESSION,
47 "org.elementary.contractor",
48 "/org/elementary/contractor");
49 } catch (IOError e) {
50 stderr.printf ("%s\n", e.message);
51 }
52 }
53
54 private string get_app_display_name (GLib.HashTable<string,string> app__)
55 {
56 return app__.lookup ("Description");
57 }
58
59 private GLib.HashTable<string,string> add_location_entry (GOF.File file)
60 {
61 GLib.HashTable<string,string> entry;
62
63 entry = new GLib.HashTable<string,string> (str_hash, str_equal);
64 entry.insert ("uri", file.uri);
65 var ftype = file.get_ftype ();
66 if (ftype == "application/octet-stream" || ftype == null)
67 entry.insert ("mimetype", "");
68 else
69 entry.insert ("mimetype", ftype);
70
71 return entry;
72 }
73
74 private GLib.HashTable<string, string>[] build_hash_from_list_selection ()
75 {
76 GLib.HashTable<string,string>[] locations = null;
77
78 foreach (GOF.File file in selection) {
79 if (file != null)
80 locations += add_location_entry (file);
81 //message ("file %s", file.name);
82 }
83 if (selection == null && current_directory != null) {
84 locations += add_location_entry (current_directory);
85 }
86
87 return locations;
88 }
89
90 public void action_activated ()
91 {
92 Gtk.MenuItem menuitem;
93 GLib.HashTable<string,string> app__;
94
95 menuitem = (Gtk.MenuItem) menu.get_active ();
96 app__ = menuitem.get_data<GLib.HashTable<string,string>> ("app");
97
98 if (app__ != null) {
99 var cmd = app__.lookup ("Exec");
100 //message ("test exec %s", cmd);
101 try {
102 GLib.Process.spawn_command_line_async (cmd);
103 } catch (SpawnError e) {
104 stderr.printf ("error spawn command line %s: %s", cmd, e.message);
105 }
106 }
107 }
108
109 public override void context_menu (Gtk.Widget? widget, GLib.List<GOF.File> files)
110 {
111 menu = widget as Gtk.Menu;57 menu = widget as Gtk.Menu;
112 58 return_if_fail (menu != null);
113 selection = files;59
60 File[] files = null;
61 Gee.List<Granite.Services.Contract> contracts = null;
62
114 try {63 try {
115 services = service_eactions.GetServicesByLocationsList (build_hash_from_list_selection ());64 if (gof_files == null) {
116 65 if (current_directory == null)
117 uint i = 0;66 return;
118 foreach(var app__ in services)67
119 {68 files = new File[0];
120 /* insert separator if we got at least 1 action */69 files += current_directory.location;
70
71 string? mimetype = current_directory.get_ftype ();
72
73 if (mimetype == null)
74 return;
75
76 contracts = Granite.Services.ContractorProxy.get_contracts_by_mime (mimetype);
77 } else {
78 files = get_file_array (gof_files);
79 var mimetypes = get_mimetypes (gof_files);
80 contracts = Granite.Services.ContractorProxy.get_contracts_by_mimelist (mimetypes);
81 }
82
83 assert (files != null);
84 assert (contracts != null);
85
86 for (int i = 0; i < contracts.size; i++) {
87 var contract = contracts.get (i);
88 Gtk.MenuItem menu_item;
89
90 // insert separator if we got at least 1 contract
121 if (i == 0) {91 if (i == 0) {
122 var item = new Gtk.SeparatorMenuItem ();92 menu_item = new Gtk.SeparatorMenuItem ();
123 menu.append (item);93 add_menuitem (menu, menu_item);
124 item.show ();
125 plugins.menus.prepend (item);
126 }94 }
127 var menuitem = new Gtk.MenuItem.with_label(get_app_display_name(app__));95
128 menu.append (menuitem);96 menu_item = new ContractMenuItem (contract, files);
129 menuitem.set_data<GLib.HashTable<string,string>> ("app", app__);97 add_menuitem (menu, menu_item);
130 menuitem.show ();
131 menuitem.activate.connect (action_activated);
132 plugins.menus.prepend (menuitem);
133 i++;
134 }98 }
135 } catch (IOError e) {99 } catch (Error e) {
136 stderr.printf ("%s\n", e.message);100 warning (e.message);
137 }101 }
138 }102 }
139103
140 public override void ui (Gtk.UIManager? widget)104 public override void ui (Gtk.UIManager? widget) {
141 {
142 ui_manager = widget;105 ui_manager = widget;
143 menu = (Gtk.Menu)ui_manager.get_widget("/selection");106 menu = (Gtk.Menu) ui_manager.get_widget ("/selection");
144 }107 }
145 108
146 public override void directory_loaded (void* user_data)109 public override void directory_loaded (void* user_data) {
147 {110 current_directory = ((Object[]) user_data)[2] as GOF.File;
148 current_directory = ((Object[])user_data)[2] as GOF.File;111 }
112
113 private void add_menuitem (Gtk.Menu menu, Gtk.MenuItem menu_item) {
114 menu.append (menu_item);
115 menu_item.show ();
116 plugins.menuitem_references.add (menu_item);
117 }
118
119 private static string[] get_mimetypes (List<GOF.File> files) {
120 string[] mimetypes = new string[files.length ()];
121
122 foreach (var file in files)
123 mimetypes += file.get_ftype () ?? "";
124
125 return mimetypes;
126 }
127
128 private static File[] get_file_array (List<GOF.File> files) {
129 File[] file_array = new File[0];
130
131 foreach (var file in files) {
132 if (file.location != null)
133 file_array += file.location;
134 }
135
136 return file_array;
149 }137 }
150}138}
151139
152public Marlin.Plugins.Base module_init ()140public Marlin.Plugins.Base module_init () {
153{
154 return new Marlin.Plugins.Contractor ();141 return new Marlin.Plugins.Contractor ();
155}142}
156
157143
=== removed directory 'plugins/extended-actions'
=== removed file 'plugins/extended-actions/CMakeLists.txt'
--- plugins/extended-actions/CMakeLists.txt 2013-04-22 07:45:04 +0000
+++ plugins/extended-actions/CMakeLists.txt 1970-01-01 00:00:00 +0000
@@ -1,28 +0,0 @@
1find_package(PkgConfig)
2pkg_check_modules(DEPS REQUIRED
3 gtk+-3.0
4 gee-1.0
5 glib-2.0)
6set(CFLAGS
7 ${DEPS_CFLAGS} ${DEPS_CFLAGS_OTHER}
8)
9include_directories(${CMAKE_BINARY_DIR}/libcore/)
10include_directories(${CMAKE_SOURCE_DIR}/libcore/)
11add_definitions(${CFLAGS})
12link_directories(${LIB_PATHS})
13vala_precompile(VALA_C marlin-extended-actions
14 plugin.vala
15CUSTOM_VAPIS
16 ${CMAKE_BINARY_DIR}/libcore/marlincore.vapi
17 ${CMAKE_SOURCE_DIR}/libcore/marlincore-C.vapi
18 ${CMAKE_BINARY_DIR}/libwidgets/marlinwidgets.vapi
19PACKAGES
20 gtk+-3.0
21 gee-1.0
22OPTIONS
23 --thread)
24add_library(marlin-extended-actions SHARED
25 ${VALA_C})
26target_link_libraries(marlin-extended-actions marlincore marlinwidgets)
27install(TARGETS marlin-extended-actions DESTINATION lib/marlin/plugins/core/)
28install(FILES extended-actions.plug DESTINATION lib/marlin/plugins/core/)
290
=== removed file 'plugins/extended-actions/extended-actions.plug'
--- plugins/extended-actions/extended-actions.plug 2011-12-04 12:05:59 +0000
+++ plugins/extended-actions/extended-actions.plug 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
1[Plugin]
2Name=Extended Actions
3File=libmarlin-extended-actions.so
40
=== removed file 'plugins/extended-actions/plugin.vala'
--- plugins/extended-actions/plugin.vala 2012-06-01 21:34:30 +0000
+++ plugins/extended-actions/plugin.vala 1970-01-01 00:00:00 +0000
@@ -1,156 +0,0 @@
1/*
2 * Authors:
3 * Lucas Baudin <xapantu@gmail.com>
4 * ammonkey <am.monkeyd@gmail.com>
5 *
6 * Copyright (C) Lucas Baudin 2011 <xapantu@gmail.com>
7 *
8 * Marlin is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Marlin is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22using Gtk;
23using Gee;
24
25[DBus (name = "org.magma.ExtendedActions")]
26public interface ExtendedActionsService : Object
27{
28 //public abstract GLib.HashTable<string,string>[] GetServicesByMime (string mime) throws IOError;
29 public abstract GLib.HashTable<string,string>[] GetServicesByLocation (string strlocation, string? file_mime="") throws IOError;
30 public abstract GLib.HashTable<string,string>[] GetServicesByLocationsList (GLib.HashTable<string, string>[] locations) throws IOError;
31}
32
33public class Marlin.Plugins.ExtendedActions : Marlin.Plugins.Base
34{
35 UIManager ui_manager;
36 Gtk.Menu menu;
37 GOF.File current_directory = null;
38 unowned GLib.List<GOF.File> selection;
39 GLib.HashTable<string,string>[] services = null;
40
41 private ExtendedActionsService service_eactions;
42
43 public ExtendedActions ()
44 {
45 try {
46 service_eactions = Bus.get_proxy_sync (BusType.SESSION,
47 "org.magma.ExtendedActions",
48 "/org/magma/ExtendedActions");
49 } catch (IOError e) {
50 stderr.printf ("%s\n", e.message);
51 }
52 }
53
54 private string get_app_display_name (GLib.HashTable<string,string> app__)
55 {
56 return app__.lookup ("Description");
57 }
58
59 private GLib.HashTable<string,string> add_location_entry (GOF.File file)
60 {
61 GLib.HashTable<string,string> entry;
62
63 entry = new GLib.HashTable<string,string> (str_hash, str_equal);
64 entry.insert ("uri", file.uri);
65 var ftype = file.get_ftype ();
66 if (ftype == "application/octet-stream" || ftype == null)
67 entry.insert ("mimetype", "");
68 else
69 entry.insert ("mimetype", ftype);
70
71 return entry;
72 }
73
74 private GLib.HashTable<string, string>[] build_hash_from_list_selection ()
75 {
76 GLib.HashTable<string,string>[] locations = null;
77
78 foreach (GOF.File file in selection) {
79 if (file != null)
80 locations += add_location_entry (file);
81 //message ("file %s", file.name);
82 }
83 if (selection == null && current_directory != null) {
84 locations += add_location_entry (current_directory);
85 }
86
87 return locations;
88 }
89
90 public void action_activated ()
91 {
92 Gtk.MenuItem menuitem;
93 GLib.HashTable<string,string> app__;
94
95 menuitem = (Gtk.MenuItem) menu.get_active ();
96 app__ = menuitem.get_data<GLib.HashTable<string,string>> ("app");
97
98 if (app__ != null) {
99 var cmd = app__.lookup ("Exec");
100 //message ("test exec %s", cmd);
101 try {
102 GLib.Process.spawn_command_line_async (cmd);
103 } catch (SpawnError e) {
104 stderr.printf ("error spawn command line %s: %s", cmd, e.message);
105 }
106 }
107 }
108
109 public override void context_menu (Gtk.Widget? widget, GLib.List<GOF.File> files)
110 {
111 menu = widget as Gtk.Menu;
112
113 selection = files;
114 try {
115 services = service_eactions.GetServicesByLocationsList (build_hash_from_list_selection ());
116
117 uint i = 0;
118 foreach(var app__ in services)
119 {
120 /* insert separator if we got at least 1 action */
121 if (i == 0) {
122 var item = new Gtk.SeparatorMenuItem ();
123 menu.append (item);
124 item.show ();
125 plugins.menus.prepend (item);
126 }
127 var menuitem = new Gtk.MenuItem.with_label(get_app_display_name(app__));
128 menu.append (menuitem);
129 menuitem.set_data<GLib.HashTable<string,string>> ("app", app__);
130 menuitem.show ();
131 menuitem.activate.connect (action_activated);
132 plugins.menus.prepend (menuitem);
133 i++;
134 }
135 } catch (IOError e) {
136 stderr.printf ("%s\n", e.message);
137 }
138 }
139
140 public override void ui (Gtk.UIManager? widget)
141 {
142 ui_manager = widget;
143 menu = (Gtk.Menu)ui_manager.get_widget("/selection");
144 }
145
146 public override void directory_loaded (void* user_data)
147 {
148 current_directory = ((Object[])user_data)[2] as GOF.File;
149 }
150}
151
152public Marlin.Plugins.Base module_init ()
153{
154 return new Marlin.Plugins.ExtendedActions ();
155}
156

Subscribers

People subscribed via source and target branches

to all changes: