Merge lp:~keirangtp/cardapio/web-plugins into lp:cardapio
- web-plugins
- Merge into cardapio
Status: | Merged |
---|---|
Merged at revision: | 363 |
Proposed branch: | lp:~keirangtp/cardapio/web-plugins |
Merge into: | lp:cardapio |
Diff against target: |
2568 lines (+1158/-317) 16 files modified
src/cardapio.py (+131/-137) src/plugins/amazon.py (+268/-0) src/plugins/bing.py (+7/-19) src/plugins/ebay.py (+251/-0) src/plugins/google.py (+9/-20) src/plugins/google_localized.py (+9/-21) src/plugins/pidgin.py (+218/-0) src/plugins/software_center.py (+3/-11) src/plugins/tomboy.py (+206/-0) src/plugins/tracker.py (+3/-11) src/plugins/tracker_fts.py (+10/-18) src/plugins/virtualbox.py (+3/-3) src/plugins/wikipedia.py (+15/-19) src/plugins/yahoo.py (+9/-23) src/plugins/you_tube.py (+7/-17) src/plugins/zg_recent_documents.py (+9/-18) |
To merge this branch: | bzr merge lp:~keirangtp/cardapio/web-plugins |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Thiago Teixeira | Approve | ||
Review via email: mp+31681@code.launchpad.net |
Commit message
Description of the change
Amazon plugin
Tomboy plugin
- 336. By Pawel Bara
-
updated Tomboys category icon
- 337. By Pawel Bara
-
found real search method in Tomboys DBus API (d-feet for the rescue) ;)
- 338. By Pawel Bara
-
bugfix: keyword searches not working in Bing
- 339. By Pawel Bara
-
merging trunk
- 340. By Pawel Bara
-
initial eBay plugin (localization on is its way)
- 341. By Pawel Bara
-
merged trunk and localized (finished) eBay plugin
- 342. By Pawel Bara
-
deleted tomboy plugin for merging purpose (will work on it some more)
Paweł Bara (keirangtp) wrote : | # |
- 343. By Pawel Bara
-
fallback search more item in eBay when the API call totally fails + finally got Tomboy plugin working well!
- 344. By Pawel Bara
-
tomboy: return immediately after passing empty search results to Cardapio
- 345. By Pawel Bara
-
minor documentation upgrades in Tomboys plugin
- 346. By Pawel Bara
-
new finished plugin: talk to your pidgin buddies!
- 347. By Pawel Bara
-
fixes bug 614710 - a little change in API of plugins: Cardapio passes result limit (count) to them
- 348. By Pawel Bara
-
minor corrections finalizing bug 614710
Thiago Teixeira (tvst) wrote : | # |
This will be in the bzr trunk and PPA later tonight. Thanks for the help!
Preview Diff
1 | === modified file 'src/cardapio.py' |
2 | --- src/cardapio.py 2010-08-05 21:09:45 +0000 |
3 | +++ src/cardapio.py 2010-08-12 17:20:35 +0000 |
4 | @@ -63,7 +63,7 @@ |
5 | |
6 | except: |
7 | # assume that if gnomeapplet is not found then the user is running Cardapio |
8 | - # without Gnome (maybe with './cardapio show', for example). |
9 | + # without Gnome (maybe with './cardapio show', for example). |
10 | print('Info: gnomeapplet Python module not present') |
11 | |
12 | try: |
13 | @@ -136,14 +136,14 @@ |
14 | version = '0.9.136' |
15 | |
16 | core_plugins = [ |
17 | - 'applications', |
18 | - 'google', |
19 | - 'google_localized', |
20 | + 'applications', |
21 | + 'google', |
22 | + 'google_localized', |
23 | 'pinned', |
24 | - 'places', |
25 | + 'places', |
26 | 'software_center', |
27 | - 'tracker', |
28 | - 'tracker_fts', |
29 | + 'tracker', |
30 | + 'tracker_fts', |
31 | 'zg_recent_documents', |
32 | ] |
33 | |
34 | @@ -216,20 +216,20 @@ |
35 | self.setup_dbus() |
36 | self.setup_base_ui() # must be the first ui-related method to be called |
37 | self.setup_plugins() |
38 | - self.build_ui() |
39 | - self.setup_ui_from_all_settings() |
40 | + self.build_ui() |
41 | + self.setup_ui_from_all_settings() |
42 | |
43 | self.schedule_search_with_all_plugins('') |
44 | |
45 | if not hidden: self.show() |
46 | |
47 | - # this is useful so that the user can edit the config file on first-run |
48 | + # this is useful so that the user can edit the config file on first-run |
49 | # without need to quit cardapio first: |
50 | self.save_config_file() |
51 | |
52 | if gnome_program_init is not None: |
53 | gnome_program_init('', self.version) # Prints a warning to the screen. Ignore it. |
54 | - client = gnome_ui_master_client() |
55 | + client = gnome_ui_master_client() |
56 | client.connect('save-yourself', self.quit) |
57 | |
58 | |
59 | @@ -262,9 +262,9 @@ |
60 | self.bus = dbus.SessionBus() |
61 | dbus.service.Object.__init__(self, self.bus, Cardapio.bus_obj_str) |
62 | |
63 | - |
64 | + |
65 | def get_plugin_class(self, basename): |
66 | - """ |
67 | + """ |
68 | Returns the CardapioPlugin class from the plugin at plugins/basename.py. |
69 | If it fails, it returns a string decribing the error. |
70 | """ |
71 | @@ -277,7 +277,7 @@ |
72 | |
73 | plugin_class = plugin_module.CardapioPlugin |
74 | |
75 | - if plugin_class.plugin_api_version != CardapioPluginInterface.plugin_api_version: |
76 | + if plugin_class.plugin_api_version != CardapioPluginInterface.plugin_api_version: |
77 | return 'Incorrect API version' |
78 | |
79 | return plugin_class |
80 | @@ -325,7 +325,7 @@ |
81 | } |
82 | |
83 | plugin_dirs = [ |
84 | - os.path.join(cardapio_path, 'plugins'), |
85 | + os.path.join(cardapio_path, 'plugins'), |
86 | os.path.join(DesktopEntry.xdg_config_home, 'Cardapio', 'plugins') |
87 | ] |
88 | |
89 | @@ -374,7 +374,7 @@ |
90 | basename = str(basename) |
91 | plugin_class = self.get_plugin_class(basename) |
92 | |
93 | - if type(plugin_class) is str: |
94 | + if type(plugin_class) is str: |
95 | logging.error('[plugin: %s] %s' % (basename, plugin_class)) |
96 | self.settings['active plugins'].remove(basename) |
97 | continue |
98 | @@ -425,7 +425,7 @@ |
99 | levels of messages can be used by setting one of is_debug, is_warning, is_error: |
100 | |
101 | debug - Used for any debugging message, including any messages that may |
102 | - be privacy sensitive. Messages set with the flag is_debug=True |
103 | + be privacy sensitive. Messages set with the flag is_debug=True |
104 | will *not* be logged unless the user enters debug mode. |
105 | |
106 | info - This is the default level when you don't set any of the flags. |
107 | @@ -438,13 +438,13 @@ |
108 | allow the plugin to function at all. |
109 | """ |
110 | |
111 | - if is_error: |
112 | + if is_error: |
113 | write = logging.error |
114 | |
115 | - elif is_warning: |
116 | + elif is_warning: |
117 | write = logging.warning |
118 | |
119 | - elif is_debug: |
120 | + elif is_debug: |
121 | write = logging.debug |
122 | |
123 | else: |
124 | @@ -494,7 +494,7 @@ |
125 | |
126 | self.config_folder_path = os.path.join(DesktopEntry.xdg_config_home, 'Cardapio') |
127 | |
128 | - if not os.path.exists(self.config_folder_path): |
129 | + if not os.path.exists(self.config_folder_path): |
130 | os.mkdir(self.config_folder_path) |
131 | |
132 | elif not os.path.isdir(self.config_folder_path): |
133 | @@ -537,14 +537,14 @@ |
134 | self.settings = {} |
135 | s = {} |
136 | |
137 | - try: |
138 | + try: |
139 | s = json.load(config_file) |
140 | |
141 | except Exception, exception: |
142 | logging.error('Could not read config file:') |
143 | logging.error(exception) |
144 | |
145 | - finally: |
146 | + finally: |
147 | config_file.close() |
148 | |
149 | default_side_pane_items = [] |
150 | @@ -552,20 +552,20 @@ |
151 | if os.path.exists(path): |
152 | default_side_pane_items.append( |
153 | { |
154 | - 'name' : _('Ubuntu Software Center'), |
155 | + 'name' : _('Ubuntu Software Center'), |
156 | 'icon name' : 'softwarecenter', |
157 | - 'tooltip' : _('Lets you choose from thousands of free applications available for Ubuntu'), |
158 | + 'tooltip' : _('Lets you choose from thousands of free applications available for Ubuntu'), |
159 | 'type' : 'raw', |
160 | - 'command' : 'software-center', |
161 | + 'command' : 'software-center', |
162 | }) |
163 | |
164 | default_side_pane_items.append( |
165 | { |
166 | - 'name' : _('Help and Support'), |
167 | + 'name' : _('Help and Support'), |
168 | 'icon name' : 'help-contents', |
169 | - 'tooltip' : _('Get help with %(distro_name)s') % {'distro_name':Cardapio.distro_name}, |
170 | + 'tooltip' : _('Get help with %(distro_name)s') % {'distro_name':Cardapio.distro_name}, |
171 | 'type' : 'raw', |
172 | - 'command' : 'gnome-help', |
173 | + 'command' : 'gnome-help', |
174 | }) |
175 | |
176 | self.read_config_option(s, 'window size' , None ) # format: [px, px] |
177 | @@ -585,10 +585,10 @@ |
178 | self.read_config_option(s, 'keybinding' , '<Super>space' ) # the user should use gtk.accelerator_parse('<Super>space') to see if the string is correct! |
179 | self.read_config_option(s, 'applet label' , Cardapio.distro_name ) # string |
180 | self.read_config_option(s, 'applet icon' , 'start-here' , override_empty_str = True) # string (either a path to the icon, or an icon name) |
181 | - self.read_config_option(s, 'pinned items' , [] ) |
182 | + self.read_config_option(s, 'pinned items' , [] ) |
183 | self.read_config_option(s, 'side pane items' , default_side_pane_items ) |
184 | - self.read_config_option(s, 'active plugins' , ['pinned', 'places', 'applications', 'tracker', 'google', 'software_center']) |
185 | - self.read_config_option(s, 'plugin settings' , {} ) |
186 | + self.read_config_option(s, 'active plugins' , ['pinned', 'places', 'applications', 'tracker', 'google', 'software_center']) |
187 | + self.read_config_option(s, 'plugin settings' , {} ) |
188 | |
189 | # these are a bit of a hack: |
190 | self.read_config_option(s, 'handler for ftp paths' , r"nautilus '%s'" ) # a command line using %s |
191 | @@ -635,7 +635,7 @@ |
192 | self.settings[key] = val |
193 | else: |
194 | self.settings[key] = user_settings[key] |
195 | - else: |
196 | + else: |
197 | self.settings[key] = val |
198 | |
199 | if force_update_from_version is not None: |
200 | @@ -709,12 +709,12 @@ |
201 | self.plugin_checkbox_column = self.get_object('PluginCheckboxColumn') |
202 | self.view_mode_button = self.get_object('ViewModeButton') |
203 | |
204 | - # HACK: fix names of widgets to allow theming |
205 | + # HACK: fix names of widgets to allow theming |
206 | # (glade doesn't seem to properly add names to widgets anymore...) |
207 | for widget in self.builder.get_objects(): |
208 | |
209 | # skip the about dialog or the app name will be overwritten! |
210 | - if widget == self.about_dialog: continue |
211 | + if widget == self.about_dialog: continue |
212 | |
213 | if 'set_name' in dir(widget): |
214 | widget.set_name(gtk.Buildable.get_name(widget)) |
215 | @@ -767,10 +767,10 @@ |
216 | <menuitem name="Item 5" verb="AboutDistro" label="%s" pixtype="none"/> |
217 | </popup> |
218 | ''' % ( |
219 | - _('_Properties'), |
220 | - _('_Edit Menus'), |
221 | - _('_About Cardapio'), |
222 | - _('_About Gnome'), |
223 | + _('_Properties'), |
224 | + _('_Edit Menus'), |
225 | + _('_About Cardapio'), |
226 | + _('_About Gnome'), |
227 | _('_About %(distro_name)s') % {'distro_name' : Cardapio.distro_name} |
228 | ) |
229 | |
230 | @@ -799,7 +799,7 @@ |
231 | self.safe_cardapio_proxy.handle_search_error = self.plugin_handle_search_error |
232 | self.safe_cardapio_proxy.ask_for_reload_permission = self.plugin_ask_for_reload_permission |
233 | |
234 | - self.build_plugin_database() |
235 | + self.build_plugin_database() |
236 | self.activate_plugins_from_settings() |
237 | |
238 | |
239 | @@ -810,7 +810,7 @@ |
240 | |
241 | panel = self.panel_button.get_toplevel().window |
242 | |
243 | - if panel is None: |
244 | + if panel is None: |
245 | return gtk.icon_size_lookup(gtk.ICON_SIZE_LARGE_TOOLBAR)[0] |
246 | |
247 | panel_width, panel_height = panel.get_size() |
248 | @@ -836,7 +836,7 @@ |
249 | |
250 | def setup_panel_button(self): |
251 | """ |
252 | - Sets up the look and feel of the Cardapio applet button |
253 | + Sets up the look and feel of the Cardapio applet button |
254 | """ |
255 | |
256 | self.panel_button.set_label(self.settings['applet label']) |
257 | @@ -923,13 +923,13 @@ |
258 | |
259 | button = self.add_button(_('All'), None, self.category_pane, tooltip = _('Show all categories'), button_type = Cardapio.CATEGORY_BUTTON) |
260 | button.connect('clicked', self.on_all_sections_sidebar_button_clicked) |
261 | - self.all_sections_sidebar_button = button |
262 | + self.all_sections_sidebar_button = button |
263 | self.set_sidebar_button_active(button, True) |
264 | self.all_sections_sidebar_button.set_sensitive(False) |
265 | |
266 | button = self.add_button(_('All'), None, self.system_category_pane, tooltip = _('Show all categories'), button_type = Cardapio.CATEGORY_BUTTON) |
267 | button.connect('clicked', self.on_all_sections_sidebar_button_clicked) |
268 | - self.all_system_sections_sidebar_button = button |
269 | + self.all_system_sections_sidebar_button = button |
270 | self.set_sidebar_button_active(button, True) |
271 | self.all_system_sections_sidebar_button.set_sensitive(False) |
272 | |
273 | @@ -1076,7 +1076,7 @@ |
274 | is_core = (basename in self.core_plugins) |
275 | is_required = (basename in self.required_plugins) |
276 | |
277 | - if is_required : title = '<b>%s</b>' % name |
278 | + if is_required : title = '<b>%s</b>' % name |
279 | |
280 | icon_pixbuf = self.get_icon_pixbuf(plugin_info['category icon'], icon_size, 'package-x-generic') |
281 | |
282 | @@ -1085,7 +1085,7 @@ |
283 | self.update_plugin_description() |
284 | self.options_dialog.show() |
285 | |
286 | - |
287 | + |
288 | def close_options_dialog(self, *dummy): |
289 | """ |
290 | Hides the Options Dialog |
291 | @@ -1118,7 +1118,7 @@ |
292 | |
293 | model, iter_ = self.get_object('PluginTreeView').get_selection().get_selected() |
294 | |
295 | - if iter_ is None: |
296 | + if iter_ is None: |
297 | is_core = True |
298 | plugin_info = {'name': '', 'version': '', 'author': '', 'description': ''} |
299 | |
300 | @@ -1137,7 +1137,7 @@ |
301 | |
302 | # make sure the label doesn't resize the window! |
303 | if width > 1: |
304 | - label.set_size_request(width - self.scrollbar_width - 20, -1) |
305 | + label.set_size_request(width - self.scrollbar_width - 20, -1) |
306 | |
307 | # The -20 is a hack because some themes add extra padding that I need to |
308 | # account for. Since I don't know where that padding is comming from, I |
309 | @@ -1181,13 +1181,13 @@ |
310 | |
311 | pthinfo = treeview.get_path_at_pos(int(event.x), int(event.y)) |
312 | |
313 | - if pthinfo is None: |
314 | + if pthinfo is None: |
315 | treeview.window.set_cursor(None) |
316 | return |
317 | |
318 | path, col, cellx, celly = pthinfo |
319 | |
320 | - if col == self.plugin_checkbox_column: |
321 | + if col == self.plugin_checkbox_column: |
322 | treeview.window.set_cursor(None) |
323 | else: |
324 | treeview.window.set_cursor(self.drag_allowed_cursor) |
325 | @@ -1205,7 +1205,7 @@ |
326 | |
327 | if basename in self.required_plugins: return |
328 | |
329 | - self.plugin_tree_model.set_value(iter_, 3, not cell.get_active()) |
330 | + self.plugin_tree_model.set_value(iter_, 3, not cell.get_active()) |
331 | self.apply_plugins_from_option_window() |
332 | |
333 | |
334 | @@ -1231,7 +1231,7 @@ |
335 | y = event.y_root - window_y |
336 | window_width, window_height = self.window.get_size() |
337 | resize_margin = 10 |
338 | - |
339 | + |
340 | if x < resize_margin: |
341 | |
342 | if y < resize_margin: |
343 | @@ -1260,7 +1260,7 @@ |
344 | |
345 | else: |
346 | edge = gtk.gdk.WINDOW_EDGE_SOUTH |
347 | - |
348 | + |
349 | x = int(event.x_root) |
350 | y = int(event.y_root) |
351 | |
352 | @@ -1336,7 +1336,7 @@ |
353 | # So by ignoring this focus-out we actually make sure that Cardapio |
354 | # will be hidden after all. Silly. |
355 | |
356 | - if self.panel_applet is not None and (0 <= cursor_x <= applet_w and 0 <= cursor_y <= applet_h): |
357 | + if self.panel_applet is not None and (0 <= cursor_x <= applet_w and 0 <= cursor_y <= applet_h): |
358 | return |
359 | |
360 | # If the last app was opened in the background, make sure Cardapio |
361 | @@ -1364,7 +1364,7 @@ |
362 | |
363 | def on_mainwindow_delete_event(self, widget, event): |
364 | """ |
365 | - What happens when the user presses Alt-F4? If in panel mode, |
366 | + What happens when the user presses Alt-F4? If in panel mode, |
367 | nothing. If in launcher mode, this terminates Cardapio. |
368 | """ |
369 | |
370 | @@ -1591,7 +1591,7 @@ |
371 | button = self.add_app_button(filename, icon_name, self.subfolders_section_contents, 'xdg', command, tooltip = command, app_list = None) |
372 | |
373 | if count: |
374 | - self.subfolders_section_slab.show() |
375 | + self.subfolders_section_slab.show() |
376 | self.set_section_has_entries(self.subfolders_section_slab) |
377 | self.no_results_to_show = False |
378 | |
379 | @@ -1628,7 +1628,7 @@ |
380 | |
381 | for plugin_keyword in self.keyword_to_plugin_mapping: |
382 | if plugin_keyword.find(keyword) == 0: |
383 | - keyword_exists = True |
384 | + keyword_exists = True |
385 | keyword = plugin_keyword |
386 | break |
387 | |
388 | @@ -1695,7 +1695,7 @@ |
389 | try: |
390 | # TODO: make plugins run in a separate thread |
391 | self.show_plugin_loading_text(plugin) |
392 | - plugin.search(text, long_search = True) |
393 | + plugin.search(text, self.settings['long search results limit']) |
394 | |
395 | except Exception, exception: |
396 | self.plugin_write_to_log(plugin, 'Plugin search query failed to execute', is_error = True) |
397 | @@ -1705,7 +1705,7 @@ |
398 | |
399 | for plugin in self.active_plugin_instances: |
400 | |
401 | - if plugin.search_delay_type != delay_type or plugin.show_only_with_keyword: |
402 | + if plugin.search_delay_type != delay_type or plugin.show_only_with_keyword: |
403 | continue |
404 | |
405 | if plugin.hide_from_sidebar and len(text) < self.settings['min search string length']: |
406 | @@ -1716,7 +1716,7 @@ |
407 | try: |
408 | # TODO: make plugins run in a separate thread |
409 | self.show_plugin_loading_text(plugin) |
410 | - plugin.search(text) |
411 | + plugin.search(text, self.settings['search results limit']) |
412 | |
413 | except Exception, exception: |
414 | self.plugin_write_to_log(plugin, 'Plugin search query failed to execute', is_error = True) |
415 | @@ -1944,7 +1944,7 @@ |
416 | |
417 | if self.is_search_entry_empty(): |
418 | self.hide_all_transitory_sections() |
419 | - return |
420 | + return |
421 | |
422 | first_app_widget = self.get_first_visible_app() |
423 | if first_app_widget is not None: |
424 | @@ -1973,9 +1973,9 @@ |
425 | |
426 | else: |
427 | first_app_widget = self.get_first_visible_app() |
428 | - if first_app_widget is not None: |
429 | + if first_app_widget is not None: |
430 | self.window.set_focus(first_app_widget) |
431 | - |
432 | + |
433 | |
434 | elif event.keyval == gtk.gdk.keyval_from_name('Escape'): |
435 | |
436 | @@ -2095,7 +2095,7 @@ |
437 | if orientation == gnomeapplet.ORIENT_DOWN: |
438 | window_x = panel_x + applet_x |
439 | window_y = panel_y + applet_y + applet_height |
440 | - |
441 | + |
442 | # bottom |
443 | elif orientation == gnomeapplet.ORIENT_UP: |
444 | window_x = panel_x + applet_x |
445 | @@ -2105,7 +2105,7 @@ |
446 | elif orientation == gnomeapplet.ORIENT_RIGHT: |
447 | window_x = panel_x + applet_x + applet_width |
448 | window_y = panel_y + applet_y |
449 | - |
450 | + |
451 | # right |
452 | elif orientation == gnomeapplet.ORIENT_LEFT: |
453 | window_x = panel_x + applet_x - window_width |
454 | @@ -2131,7 +2131,7 @@ |
455 | Resize Cardapio according to the user preferences |
456 | """ |
457 | |
458 | - if self.settings['window size'] is not None: |
459 | + if self.settings['window size'] is not None: |
460 | self.window.resize(*self.settings['window size']) |
461 | |
462 | if self.settings['splitter position'] > 0: |
463 | @@ -2282,7 +2282,7 @@ |
464 | window.present_with_time(int(time.time())) |
465 | |
466 | # for metacity, this is required!! |
467 | - window.window.focus() |
468 | + window.window.focus() |
469 | |
470 | |
471 | def on_panel_button_pressed(self, widget, event): |
472 | @@ -2504,13 +2504,13 @@ |
473 | |
474 | def on_volume_monitor_changed(self, drive): |
475 | """ |
476 | - Handler for when volumes are mounted or ejected |
477 | + Handler for when volumes are mounted or ejected |
478 | """ |
479 | |
480 | self.clear_pane(self.places_section_contents) |
481 | self.build_places_list() |
482 | |
483 | - |
484 | + |
485 | def get_folder_name_and_path(self, folder_path): |
486 | """ |
487 | Returns a folder's name and path from its full filename |
488 | @@ -2519,12 +2519,12 @@ |
489 | path = folder_path.strip(' \n\r\t') |
490 | |
491 | res = folder_path.split(os.path.sep) |
492 | - if res: |
493 | + if res: |
494 | name = res[-1].strip(' \n\r\t').replace('%20', ' ') |
495 | if name: return name, path |
496 | |
497 | # TODO: handle remote folders like nautilus does (i.e. '/home on ftp.myserver.net') |
498 | - name = path.replace('%20', ' ') |
499 | + name = path.replace('%20', ' ') |
500 | return name, path |
501 | |
502 | |
503 | @@ -2535,7 +2535,7 @@ |
504 | """ |
505 | |
506 | res = folder_path.split(' ') |
507 | - if len(res) > 1: |
508 | + if len(res) > 1: |
509 | name = ' '.join(res[1:]).strip(' \n\r\t') |
510 | path = res[0] |
511 | return name, path |
512 | @@ -2568,8 +2568,8 @@ |
513 | |
514 | text = self.search_entry.get_text().strip().lower() |
515 | |
516 | - no_results = True |
517 | - |
518 | + no_results = True |
519 | + |
520 | for app in self.settings[list_name]: |
521 | |
522 | # fixing a misspelling from the old config files... |
523 | @@ -2626,7 +2626,7 @@ |
524 | [ |
525 | _('Log Out...'), |
526 | _('Log out of this session to log in as a different user'), |
527 | - 'system-log-out', |
528 | + 'system-log-out', |
529 | 'gnome-session-save --logout-dialog', |
530 | self.right_session_pane, |
531 | ], |
532 | @@ -2688,18 +2688,18 @@ |
533 | sidebar_button.hide() |
534 | section_slab.hide() |
535 | self.section_list[section_slab] = { |
536 | - 'has entries': False, |
537 | - 'category': sidebar_button, |
538 | - 'contents': section_contents, |
539 | + 'has entries': False, |
540 | + 'category': sidebar_button, |
541 | + 'contents': section_contents, |
542 | 'name': title_str, |
543 | 'is system section': system_menu, |
544 | } |
545 | |
546 | else: |
547 | self.section_list[section_slab] = { |
548 | - 'has entries': True, |
549 | - 'category': sidebar_button, |
550 | - 'contents': section_contents, |
551 | + 'has entries': True, |
552 | + 'category': sidebar_button, |
553 | + 'contents': section_contents, |
554 | 'name': title_str, |
555 | 'is system section': system_menu, |
556 | } |
557 | @@ -2810,7 +2810,7 @@ |
558 | self.add_session_slab() |
559 | self.add_system_slab() |
560 | #self.build_system_list() # TODO: use gnomecc.menu |
561 | - |
562 | + |
563 | elif basename == 'places': |
564 | self.add_places_slab() |
565 | |
566 | @@ -2863,15 +2863,15 @@ |
567 | |
568 | if command_type != 'callback' and command_type != 'raw': |
569 | |
570 | - if command_type == 'app': |
571 | + if command_type == 'app': |
572 | button.drag_source_set( |
573 | - gtk.gdk.BUTTON1_MASK, |
574 | - [('text/uri-list', 0, 0)], |
575 | + gtk.gdk.BUTTON1_MASK, |
576 | + [('text/uri-list', 0, 0)], |
577 | gtk.gdk.ACTION_COPY) |
578 | - else: |
579 | + else: |
580 | button.drag_source_set( |
581 | - gtk.gdk.BUTTON1_MASK, |
582 | - [('text/uri-list', 0, 0)], |
583 | + gtk.gdk.BUTTON1_MASK, |
584 | + [('text/uri-list', 0, 0)], |
585 | gtk.gdk.ACTION_LINK) |
586 | |
587 | button.connect('drag-begin', self.on_app_button_drag_begin) |
588 | @@ -2934,7 +2934,7 @@ |
589 | align = gtk.Alignment(0, 0.5) |
590 | align.add(hbox) |
591 | |
592 | - if tooltip: |
593 | + if tooltip: |
594 | tooltip = self.unescape(tooltip) |
595 | button.set_tooltip_text(tooltip) |
596 | |
597 | @@ -2988,7 +2988,7 @@ |
598 | |
599 | # TODO: speed this up! |
600 | |
601 | - if not icon_value: |
602 | + if not icon_value: |
603 | icon_value = fallback_icon |
604 | |
605 | icon_pixbuf = None |
606 | @@ -3037,7 +3037,7 @@ |
607 | # try generic mimetype |
608 | gen_type = icon_name.split('-')[0] |
609 | icon_name = gen_type + '-x-generic' |
610 | - if self.icon_theme.has_icon(icon_name): |
611 | + if self.icon_theme.has_icon(icon_name): |
612 | return icon_name |
613 | |
614 | return None |
615 | @@ -3064,7 +3064,7 @@ |
616 | for icon_name in icons: |
617 | if self.icon_theme.has_icon(icon_name): |
618 | return icon_name |
619 | - |
620 | + |
621 | return None |
622 | |
623 | |
624 | @@ -3087,7 +3087,7 @@ |
625 | |
626 | def read_gtk_theme_info(self): |
627 | """ |
628 | - Reads colors and other info from the GTK theme so that the app better |
629 | + Reads colors and other info from the GTK theme so that the app better |
630 | adapt to any custom theme |
631 | """ |
632 | |
633 | @@ -3201,8 +3201,8 @@ |
634 | """ |
635 | |
636 | self.clicked_app = widget.app_info |
637 | - |
638 | - if widget.app_info['type'] == 'callback': |
639 | + |
640 | + if widget.app_info['type'] == 'callback': |
641 | self.pin_menuitem.hide() |
642 | self.unpin_menuitem.hide() |
643 | self.add_side_pane_menuitem.hide() |
644 | @@ -3217,12 +3217,12 @@ |
645 | self.app_menu_separator.show() |
646 | |
647 | for command in [app['command'] for app in self.settings['pinned items']]: |
648 | - if command == widget.app_info['command']: |
649 | + if command == widget.app_info['command']: |
650 | already_pinned = True |
651 | break |
652 | |
653 | for command in [app['command'] for app in self.settings['side pane items']]: |
654 | - if command == widget.app_info['command']: |
655 | + if command == widget.app_info['command']: |
656 | already_on_side_pane = True |
657 | break |
658 | |
659 | @@ -3288,7 +3288,7 @@ |
660 | |
661 | i = 0 |
662 | |
663 | - for item_info in self.clicked_app['context menu']: |
664 | + for item_info in self.clicked_app['context menu']: |
665 | |
666 | menu_item = gtk.ImageMenuItem(item_info['name'], True) |
667 | menu_item.set_tooltip_text(item_info['tooltip']) |
668 | @@ -3322,7 +3322,7 @@ |
669 | elif alloc.y + alloc.height > scroller_position + page_size: |
670 | self.scroll_adjustment.set_value(alloc.y + alloc.height - page_size) |
671 | |
672 | - |
673 | + |
674 | def on_app_button_drag_begin(self, button, drag_context): |
675 | """ |
676 | Set up drag action (not much goes on here...) |
677 | @@ -3335,7 +3335,7 @@ |
678 | def on_app_button_data_get(self, button, drag_context, selection_data, info, time): |
679 | """ |
680 | Prepare the data that will be sent to the other app when the drag-and-drop |
681 | - operation is done |
682 | + operation is done |
683 | """ |
684 | |
685 | command = button.app_info['command'] |
686 | @@ -3344,7 +3344,7 @@ |
687 | if command_type == 'app': |
688 | command = 'file://' + command |
689 | |
690 | - elif command_type == 'xdg': |
691 | + elif command_type == 'xdg': |
692 | |
693 | path_type, dummy = urllib2.splittype(command) |
694 | if path_type is None: command = 'file://' + command |
695 | @@ -3400,7 +3400,7 @@ |
696 | path = DesktopEntry.DesktopEntry(command).getExec() |
697 | |
698 | # Strip parts of the path that contain %<a-Z> |
699 | - |
700 | + |
701 | path_parts = path.split() |
702 | |
703 | for i in xrange(len(path_parts)): |
704 | @@ -3439,11 +3439,11 @@ |
705 | response = self.show_executable_file_dialog(path) |
706 | |
707 | # if "Run in Terminal" |
708 | - if response == 1: |
709 | + if response == 1: |
710 | return self.launch_raw_in_terminal(path, hide) |
711 | |
712 | # if "Display" |
713 | - elif response == 2: |
714 | + elif response == 2: |
715 | pass |
716 | |
717 | # if "Run" |
718 | @@ -3451,7 +3451,7 @@ |
719 | return self.launch_raw(path, hide) |
720 | |
721 | # if "Cancel" |
722 | - else: |
723 | + else: |
724 | return |
725 | |
726 | elif path_type in ['ftp', 'sftp', 'smb']: |
727 | @@ -3540,7 +3540,7 @@ |
728 | else: |
729 | widget = self.all_sections_sidebar_button |
730 | |
731 | - self.set_sidebar_button_active(widget, True) |
732 | + self.set_sidebar_button_active(widget, True) |
733 | |
734 | if self.is_search_entry_empty(): |
735 | widget.set_sensitive(False) |
736 | @@ -3604,11 +3604,11 @@ |
737 | if self.plugins_still_searching > 0: |
738 | return |
739 | |
740 | - if self.no_results_to_show: |
741 | + if self.no_results_to_show: |
742 | self.show_no_results_text() |
743 | |
744 | - return |
745 | - |
746 | + return |
747 | + |
748 | if self.section_list[self.selected_section]['has entries']: |
749 | self.selected_section.show() |
750 | self.hide_no_results_text() |
751 | @@ -3629,7 +3629,7 @@ |
752 | self.hide_section(self.system_section_slab, fully_hide) |
753 | self.hide_section(self.sidepane_section_slab, fully_hide) |
754 | self.hide_section(self.uncategorized_section_slab, fully_hide) |
755 | - |
756 | + |
757 | self.hide_transitory_plugin_sections(fully_hide) |
758 | |
759 | |
760 | @@ -3713,7 +3713,7 @@ |
761 | |
762 | |
763 | class CardapioPluginInterface: |
764 | - # for documentation, see: https://answers.launchpad.net/cardapio/+faq/1172 |
765 | + # for documentation, see: https://answers.launchpad.net/cardapio/+faq/1172 |
766 | |
767 | author = '' |
768 | name = '' |
769 | @@ -3724,7 +3724,7 @@ |
770 | help_text = '' |
771 | version = '' |
772 | |
773 | - plugin_api_version = 1.37 |
774 | + plugin_api_version = 1.38 |
775 | |
776 | search_delay_type = 'local search update delay' |
777 | |
778 | @@ -3749,7 +3749,7 @@ |
779 | For example, the Tracker plugin sets self.loaded to False if Tracker is not |
780 | installed in the system. |
781 | |
782 | - The constructor is given a single parameter, which is an object used to |
783 | + The constructor is given a single parameter, which is an object used to |
784 | communicate with Cardapio. This object has the following members: |
785 | |
786 | - settings - this is a dict containing the same things that you will |
787 | @@ -3759,17 +3759,17 @@ |
788 | log file, like this: write_to_log(self, 'hi there') |
789 | |
790 | - handle_search_result - a function to which you should pass the |
791 | - search results when you have them (see more info below, in the |
792 | + search results when you have them (see more info below, in the |
793 | search() method) |
794 | |
795 | - handle_search_error - a function to which you should pass an error |
796 | - message if the search fails (see more info below, in the |
797 | + message if the search fails (see more info below, in the |
798 | search() method) |
799 | |
800 | - ask_for_reload_permission - a function that should be used whenever |
801 | the plugin wants to reload its database. Not all plugins have |
802 | internal databases, though, so this is not always applicable. This |
803 | - is used, for example, with the software_center plugin. (see |
804 | + is used, for example, with the software_center plugin. (see |
805 | on_reload_permission_granted below for more info) |
806 | |
807 | Note: DO NOT WRITE ANYTHING IN THE settings DICT!! |
808 | @@ -3787,44 +3787,38 @@ |
809 | pass |
810 | |
811 | |
812 | - def search(self, text, long_search = False): |
813 | + def search(self, text, result_limit): |
814 | """ |
815 | REQUIRED |
816 | |
817 | This method gets called when a new text string is entered in the search |
818 | - field. It also takes an argument indicating whether this is a "long search" or |
819 | - not. This indicates the amount of items that the plugin should return to |
820 | - Cardapio: |
821 | + field. It also takes an argument indicating the maximum number of |
822 | + results Cardapio's expecting. The plugin should always provide as many |
823 | + results as it can but their number cannot exceed the given limit! |
824 | |
825 | - * if long_search == True: |
826 | - --> # of items = cardapio_proxy.settings['search results limit'] |
827 | - |
828 | - * if long_search == False: |
829 | - --> # of items = cardapio_proxy.settings['long search results limit'] |
830 | - |
831 | One of the following functions should be called from this method |
832 | (of from a thread spawned by this method): |
833 | |
834 | * if all goes well: |
835 | - --> handle_search_result(plugin, results, original_query) |
836 | + --> handle_search_result(plugin, results, original_query) |
837 | |
838 | * if there is an error |
839 | - --> handle_search_error(plugin, text) |
840 | + --> handle_search_error(plugin, text) |
841 | |
842 | The arguments to these functions are: |
843 | |
844 | - * plugin - this plugin instance (that is, it should always |
845 | + * plugin - this plugin instance (that is, it should always |
846 | be "self", without quotes) |
847 | * text - some text to be inserted in Cardapio's log. |
848 | * results - an array of dict items as described below. |
849 | * original_query - the search query that this corresponds to. The |
850 | - plugin should save the query received by the |
851 | + plugin should save the query received by the |
852 | search() method and pass it back to Cardapio. |
853 | |
854 | item = { |
855 | 'name' : _('Music'), |
856 | 'tooltip' : _('Show your Music folder'), |
857 | - 'icon name' : 'text-x-generic', |
858 | + 'icon name' : 'text-x-generic', |
859 | 'type' : 'xdg', |
860 | 'command' : '~/Music', |
861 | 'context menu' : None |
862 | @@ -3860,9 +3854,9 @@ |
863 | def on_reload_permission_granted(self): |
864 | """ |
865 | NOT REQUIRED |
866 | - |
867 | + |
868 | Whenever a plugin wishes to rebuild some sort of internal database, |
869 | - if this takes more than a couple of milliseconds it is advisable to |
870 | + if this takes more than a couple of milliseconds it is advisable to |
871 | first ask Cardapio for permission. This is how this works: |
872 | |
873 | 1) Plugin calls cardapio_proxy.ask_for_reload_permission(self) |
874 | @@ -3870,11 +3864,11 @@ |
875 | Cardapio then decides at what time it is best to give the plugin the |
876 | reload permission. Usually this can take up to 10s, to allow several |
877 | plugins to reload at the same time. Then, Cardapio shows the "Data has |
878 | - changed" window. |
879 | - |
880 | + changed" window. |
881 | + |
882 | 2) Cardapio calls on_reload_permission_granted to tell the plugin that |
883 | - it can reload its database |
884 | - |
885 | + it can reload its database |
886 | + |
887 | When done, the "Data has changed" window is hidden. |
888 | """ |
889 | pass |
890 | |
891 | === added file 'src/plugins/amazon.py' |
892 | --- src/plugins/amazon.py 1970-01-01 00:00:00 +0000 |
893 | +++ src/plugins/amazon.py 2010-08-12 17:20:35 +0000 |
894 | @@ -0,0 +1,268 @@ |
895 | +import json |
896 | +import gio |
897 | +import urllib |
898 | + |
899 | +import base64 |
900 | +import hashlib |
901 | +import hmac |
902 | +import time |
903 | + |
904 | +from xml.etree import ElementTree |
905 | + |
906 | +from locale import getdefaultlocale |
907 | + |
908 | +class CardapioPlugin(CardapioPluginInterface): |
909 | + |
910 | + """ |
911 | + Amazon plugin based on it's Product Advertising API. We provide |
912 | + results from all of the shop's categories. |
913 | + |
914 | + API's documentation can be found at: |
915 | + http://docs.amazonwebservices.com/AWSECommerceService/2009-11-01/DG/ |
916 | + |
917 | + If there's a localized API version, the plugin will use it. Amazon includes |
918 | + CA, GB, JP, FR and DE versions. |
919 | + As a fallback strategy, we use the United States version. |
920 | + |
921 | + The Cardapio's result limit is not fully respected. Amazon always |
922 | + returns paginated results with 10 elements per page. Because of the |
923 | + asynchronous nature of Cardapio's searching, we cannot use the |
924 | + pagination feature. We need to stick to the first page of results |
925 | + and that's why this plugin will always return at most 10 items. |
926 | + |
927 | + All of the plugin's web requests are asynchronous and cancellable. |
928 | + """ |
929 | + |
930 | + # Cardapio's variables |
931 | + author = 'Pawel Bara' |
932 | + name = _('Amazon') |
933 | + description = _('Search for results in Amazon') |
934 | + version = '0.9b' |
935 | + |
936 | + url = '' |
937 | + help_text = '' |
938 | + |
939 | + default_keyword = 'amazon' |
940 | + |
941 | + plugin_api_version = 1.38 |
942 | + |
943 | + search_delay_type = 'remote search update delay' |
944 | + |
945 | + category_name = _('Amazon Results') |
946 | + category_tooltip = _('Results found in Amazon') |
947 | + |
948 | + category_icon = 'system-search' |
949 | + fallback_icon = '' |
950 | + |
951 | + hide_from_sidebar = True |
952 | + |
953 | + def __init__(self, cardapio_proxy): |
954 | + cardapio_proxy.write_to_log(self, 'initializing Amazon plugin') |
955 | + |
956 | + self.cardapio = cardapio_proxy |
957 | + |
958 | + self.cancellable = gio.Cancellable() |
959 | + |
960 | + # my API keys |
961 | + self.aws_access_key = 'AKIAIW35CYEJ653CJJHQ' |
962 | + self.aws_secret_access_key = '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' |
963 | + |
964 | + # basic API's arguments (search in all categories) |
965 | + self.api_base_args = { |
966 | + 'Service' : 'AWSECommerceService', |
967 | + 'Version' : '2009-11-01', |
968 | + 'Operation' : 'ItemSearch', |
969 | + 'SearchIndex' : 'All', |
970 | + 'ResponseGroup' : 'Small', |
971 | + 'AWSAccessKeyId': self.aws_access_key |
972 | + } |
973 | + |
974 | + # try to get a locale specific URL for Amazon |
975 | + self.locale_url = self.get_locale_url() |
976 | + |
977 | + # Amazon's base URLs (search and search more variations) |
978 | + self.api_base_url = 'http://' + self.locale_url + '/onca/xml?{0}' |
979 | + self.web_base_url = 'http://www.amazon.com/s?url=search-alias%3Daps&{0}' |
980 | + |
981 | + self.loaded = True |
982 | + |
983 | + def get_locale_url(self): |
984 | + """ |
985 | + Tries to get a locale specific base URL for Amazon's API according to |
986 | + http://docs.amazonwebservices.com/AWSECommerceService/2009-11-01/DG/ |
987 | + |
988 | + If there's none, uses ".com" as a fallback strategy. |
989 | + """ |
990 | + |
991 | + locale_dict = { |
992 | + 'ca' : 'ecs.amazonaws.ca', |
993 | + 'de' : 'ecs.amazonaws.de', |
994 | + 'fr' : 'ecs.amazonaws.fr', |
995 | + 'jp' : 'ecs.amazonaws.jp', |
996 | + 'uk' : 'ecs.amazonaws.co.uk' |
997 | + } |
998 | + |
999 | + default = 'ecs.amazonaws.com' |
1000 | + |
1001 | + # get and parse the language code |
1002 | + lang_code = getdefaultlocale()[0] |
1003 | + |
1004 | + if lang_code is None: |
1005 | + return default |
1006 | + |
1007 | + lang = lang_code[:2].lower() |
1008 | + dialect = lang_code[3:].lower() |
1009 | + |
1010 | + # try to find a mapping... |
1011 | + key = None |
1012 | + if lang == 'en': |
1013 | + if dialect == 'gb': |
1014 | + key = 'uk' |
1015 | + elif dialect == 'ca': |
1016 | + key = 'ca' |
1017 | + elif lang == 'fr': |
1018 | + key = 'fr' |
1019 | + elif lang == 'de': |
1020 | + key = 'de' |
1021 | + elif lang == 'ja': |
1022 | + key = 'jp' |
1023 | + |
1024 | + return locale_dict.get(key, default) |
1025 | + |
1026 | + def search(self, text, result_limit): |
1027 | + if len(text) == 0: |
1028 | + return |
1029 | + |
1030 | + self.cardapio.write_to_log(self, 'searching for {0} using Amazon'.format(text), is_debug = True) |
1031 | + |
1032 | + self.cancellable.reset() |
1033 | + |
1034 | + # prepare final API URL |
1035 | + final_url = self.api_base_url.format(self.prepare_amazon_rest_url(text)) |
1036 | + |
1037 | + self.cardapio.write_to_log(self, 'final API URL: {0}'.format(final_url), is_debug = True) |
1038 | + |
1039 | + # asynchronous and cancellable IO call |
1040 | + self.current_stream = gio.File(final_url) |
1041 | + self.current_stream.load_contents_async(self.show_search_results, |
1042 | + cancellable = self.cancellable, |
1043 | + user_data = (text, result_limit)) |
1044 | + |
1045 | + def prepare_amazon_rest_url(self, text): |
1046 | + """ |
1047 | + Prepares a RESTful URL according to Amazon's strict querying policies. |
1048 | + Deals with the variable part of the URL only (the one after the '?'). |
1049 | + """ |
1050 | + |
1051 | + # additional required API arguments |
1052 | + copy_args = self.api_base_args.copy() |
1053 | + copy_args['Keywords'] = text |
1054 | + copy_args['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) |
1055 | + |
1056 | + # turn the argument map into a list of encoded request parameter strings |
1057 | + query_list = map( |
1058 | + lambda (k, v): (k + "=" + urllib.quote(v)), |
1059 | + copy_args.items() |
1060 | + ) |
1061 | + |
1062 | + # sort the list (by parameter name) |
1063 | + query_list.sort() |
1064 | + |
1065 | + # turn the list into a partial URL string |
1066 | + query_string = "&".join(query_list) |
1067 | + |
1068 | + # prepare a string on which we will base the AWS signature |
1069 | + string_to_sign = """GET |
1070 | +{0} |
1071 | +/onca/xml |
1072 | +{1}""".format(self.locale_url, query_string) |
1073 | + |
1074 | + # create HMAC for the string (using SHA-256 and our secret API key) |
1075 | + hm = hmac.new(key = self.aws_secret_access_key, |
1076 | + msg = string_to_sign, |
1077 | + digestmod = hashlib.sha256) |
1078 | + # final step... convert the HMAC to base64, then encode it |
1079 | + signature = urllib.quote(base64.b64encode(hm.digest())) |
1080 | + |
1081 | + return query_string + '&Signature=' + signature |
1082 | + |
1083 | + def show_search_results(self, gdaemonfile, result, user_data): |
1084 | + """ |
1085 | + Callback to asynchronous IO (Amazon's API call). |
1086 | + """ |
1087 | + |
1088 | + text = user_data[0] |
1089 | + result_limit = user_data[1] |
1090 | + |
1091 | + # watch out for connection problems |
1092 | + try: |
1093 | + xml_body = self.current_stream.load_contents_finish(result)[0] |
1094 | + |
1095 | + # watch out for empty input |
1096 | + if len(xml_body) == 0: |
1097 | + return |
1098 | + |
1099 | + root = ElementTree.fromstring(xml_body) |
1100 | + |
1101 | + # strip the namespaces from all the parsed items |
1102 | + for el in root.getiterator(): |
1103 | + ns_pos = el.tag.find('}') |
1104 | + if ns_pos != -1: |
1105 | + el.tag = el.tag[(ns_pos + 1):] |
1106 | + except Exception as ex: |
1107 | + self.cardapio.handle_search_error(self, 'error while obtaining data: {0}'.format(str(ex))) |
1108 | + return |
1109 | + |
1110 | + # decode the result |
1111 | + try: |
1112 | + items = [] |
1113 | + |
1114 | + is_valid = root.find('Items/Request/IsValid') |
1115 | + total_results = root.find('Items/TotalResults') |
1116 | + |
1117 | + # if we have a valid response with any results... |
1118 | + if (not is_valid is None) and is_valid != 'False' and (not total_results is None) and total_results != '0': |
1119 | + # remember them all |
1120 | + for i, item in enumerate(root.findall('Items/Item')): |
1121 | + |
1122 | + # the number of results cannot be limited using Amazon's API... |
1123 | + if i == result_limit: |
1124 | + break |
1125 | + |
1126 | + i_attributes = item.find('ItemAttributes') |
1127 | + url = item.find('DetailPageURL').text |
1128 | + |
1129 | + items.append({ |
1130 | + 'name' : i_attributes.find('Title').text + ' [' + i_attributes.find('ProductGroup').text + ']', |
1131 | + 'tooltip' : url, |
1132 | + 'icon name' : 'text-html', |
1133 | + 'type' : 'xdg', |
1134 | + 'command' : url, |
1135 | + 'context menu' : None |
1136 | + }) |
1137 | + |
1138 | + # always add 'Search more...' item |
1139 | + search_more_args = { 'field-keywords' : text } |
1140 | + |
1141 | + items.append({ |
1142 | + 'name' : _('Show additional results'), |
1143 | + 'tooltip' : _('Show additional search results in your web browser'), |
1144 | + 'icon name' : 'system-search', |
1145 | + 'type' : 'xdg', |
1146 | + # TODO: cardapio later unquotes this and then quotes it again; |
1147 | + # it's screwing my quotation |
1148 | + 'command' : self.web_base_url.format(urllib.urlencode(search_more_args)), |
1149 | + 'context menu' : None |
1150 | + }) |
1151 | + |
1152 | + # pass the results to Cardapio |
1153 | + self.cardapio.handle_search_result(self, items, text) |
1154 | + |
1155 | + except KeyError: |
1156 | + self.cardapio.handle_search_error(self, "Incorrect Amazon's JSON structure") |
1157 | + |
1158 | + def cancel(self): |
1159 | + self.cardapio.write_to_log(self, 'cancelling a recent Amazon search (if any)', is_debug = True) |
1160 | + |
1161 | + if not self.cancellable.is_cancelled(): |
1162 | + self.cancellable.cancel() |
1163 | |
1164 | === modified file 'src/plugins/bing.py' |
1165 | --- src/plugins/bing.py 2010-08-04 20:24:22 +0000 |
1166 | +++ src/plugins/bing.py 2010-08-12 17:20:35 +0000 |
1167 | @@ -22,7 +22,7 @@ |
1168 | url = '' |
1169 | help_text = '' |
1170 | |
1171 | - plugin_api_version = 1.37 |
1172 | + plugin_api_version = 1.38 |
1173 | |
1174 | search_delay_type = 'remote search update delay' |
1175 | |
1176 | @@ -43,18 +43,10 @@ |
1177 | |
1178 | self.cancellable = gio.Cancellable() |
1179 | |
1180 | - # Bing's API arguments (my AppID and a request for a web search with |
1181 | - # maximum four results) |
1182 | + # Bing's API arguments (my AppID and a request for a web search) |
1183 | self.api_base_args = { |
1184 | 'Appid' : '237CBC82BB8C3F7F5F19F6A77B0D38A59E8F8C2C', |
1185 | - 'sources' : 'web', |
1186 | - 'web.count': self.cardapio.settings['search results limit'], |
1187 | - } |
1188 | - |
1189 | - self.api_base_args_long = { |
1190 | - 'Appid' : '237CBC82BB8C3F7F5F19F6A77B0D38A59E8F8C2C', |
1191 | - 'sources' : 'web', |
1192 | - 'web.count': self.cardapio.settings['long search results limit'], |
1193 | + 'sources' : 'web' |
1194 | } |
1195 | |
1196 | # Bing's base URLs (search and search more variations) |
1197 | @@ -63,22 +55,18 @@ |
1198 | |
1199 | self.loaded = True |
1200 | |
1201 | - def search(self, text, long_search = False): |
1202 | + def search(self, text, result_limit): |
1203 | |
1204 | if len(text) == 0: |
1205 | return |
1206 | |
1207 | - self.current_query = text |
1208 | - |
1209 | self.cardapio.write_to_log(self, 'searching for {0} using Bing'.format(text), is_debug = True) |
1210 | |
1211 | self.cancellable.reset() |
1212 | |
1213 | # prepare final API URL |
1214 | - if long_search: |
1215 | - current_args = self.api_base_args_long.copy() |
1216 | - else: |
1217 | - current_args = self.api_base_args.copy() |
1218 | + current_args = self.api_base_args.copy() |
1219 | + current_args['web.count'] = result_limit |
1220 | |
1221 | current_args['query'] = text |
1222 | final_url = self.api_base_url.format(urllib.urlencode(current_args)) |
1223 | @@ -143,7 +131,7 @@ |
1224 | }) |
1225 | |
1226 | # pass the results to Cardapio |
1227 | - self.cardapio.handle_search_result(self, items, self.current_query) |
1228 | + self.cardapio.handle_search_result(self, items, text) |
1229 | |
1230 | except KeyError: |
1231 | self.cardapio.handle_search_error(self, "Incorrect Bing's JSON structure") |
1232 | |
1233 | === added file 'src/plugins/ebay.py' |
1234 | --- src/plugins/ebay.py 1970-01-01 00:00:00 +0000 |
1235 | +++ src/plugins/ebay.py 2010-08-12 17:20:35 +0000 |
1236 | @@ -0,0 +1,251 @@ |
1237 | +import json |
1238 | +import gio |
1239 | +import urllib |
1240 | + |
1241 | +from glib import GError |
1242 | +from locale import getdefaultlocale |
1243 | + |
1244 | +class CardapioPlugin(CardapioPluginInterface): |
1245 | + """ |
1246 | + eBay search plugin based on it's Finding API documented at: |
1247 | + http://developer.ebay.com/products/finding/ |
1248 | + |
1249 | + Please note, that this API limits the number of calls to 5000 per IP |
1250 | + and day. |
1251 | + |
1252 | + All calls are localized, meaning that they are using eBay version local |
1253 | + to the user. The specific version being used is derived from the user's |
1254 | + computer locale. |
1255 | + |
1256 | + For a list of locale supported by eBay check: |
1257 | + http://developer.ebay.com/DevZone/finding/Concepts/SiteIDToGlobalID.html |
1258 | + The listing there means that the plugin is localized for more than 20 |
1259 | + countries. :) Nevertheless, we must have a fallback strategy and we use |
1260 | + the US version in this role. |
1261 | + |
1262 | + All of the plugin's web requests are asynchronous and cancellable. |
1263 | + """ |
1264 | + |
1265 | + # Cardapio's variables |
1266 | + author = 'Pawel Bara' |
1267 | + name = _('eBay') |
1268 | + description = _('Search for items on eBay') |
1269 | + version = '0.9b' |
1270 | + |
1271 | + url = '' |
1272 | + help_text = '' |
1273 | + |
1274 | + plugin_api_version = 1.38 |
1275 | + |
1276 | + search_delay_type = 'remote search update delay' |
1277 | + |
1278 | + default_keyword = 'ebay' |
1279 | + |
1280 | + category_name = _('eBay Results') |
1281 | + category_tooltip = _('Items found on eBay') |
1282 | + |
1283 | + category_icon = 'system-search' |
1284 | + fallback_icon = '' |
1285 | + |
1286 | + hide_from_sidebar = True |
1287 | + |
1288 | + def __init__(self, cardapio_proxy): |
1289 | + cardapio_proxy.write_to_log(self, 'initializing eBay plugin') |
1290 | + |
1291 | + self.cardapio = cardapio_proxy |
1292 | + |
1293 | + self.cancellable = gio.Cancellable() |
1294 | + |
1295 | + # eBay's API arguments (my API key, 'find' operation, JSON response format, |
1296 | + # and locale information) |
1297 | + self.api_base_args = { |
1298 | + 'SECURITY-APPNAME' : 'Cardapio-9704-40b3-8e17-cfad62dd6c45', |
1299 | + 'OPERATION-NAME' : 'findItemsByKeywords', |
1300 | + 'RESPONSE-DATA-FORMAT' : 'JSON', |
1301 | + 'GLOBAL-ID' : self.get_global_id() |
1302 | + } |
1303 | + |
1304 | + # eBay's base URLs (search and a fallback search more variations) |
1305 | + self.api_base_url = 'http://svcs.ebay.com/services/search/FindingService/v1?{0}' |
1306 | + self.web_base_url = 'http://shop.ebay.com/?{0}' |
1307 | + |
1308 | + self.loaded = True |
1309 | + |
1310 | + def get_global_id(self): |
1311 | + """ |
1312 | + Tries to get a locale specific GLOBAL-ID argument for eBay's API. |
1313 | + For more information check those two websites: |
1314 | + http://developer.ebay.com/DevZone/finding/Concepts/SiteIDToGlobalID.html |
1315 | + http://developer.ebay.com/DevZone/finding/CallRef/Enums/GlobalIdList.html |
1316 | + |
1317 | + We use 'EBAY-US' as a fallback strategy. |
1318 | + """ |
1319 | + |
1320 | + default = 'EBAY-US' |
1321 | + |
1322 | + # get and parse the language code |
1323 | + lang_code = getdefaultlocale()[0] |
1324 | + |
1325 | + if lang_code is None: |
1326 | + return default |
1327 | + |
1328 | + lang = lang_code[:2].lower() |
1329 | + dialect = lang_code[3:].lower() |
1330 | + |
1331 | + # try to find a mapping... |
1332 | + result = None |
1333 | + if lang == 'en': |
1334 | + if dialect == 'gb': |
1335 | + result = 'EBAY-GB' |
1336 | + elif dialect == 'ca': |
1337 | + result = 'EBAY-ENCA' |
1338 | + elif dialect == 'ie': |
1339 | + result = 'EBAY-IE' |
1340 | + elif dialect == 'in': |
1341 | + result = 'EBAY-IN' |
1342 | + elif dialect == 'my': |
1343 | + result = 'EBAY-MY' |
1344 | + elif dialect == 'ph': |
1345 | + result = 'EBAY-PH' |
1346 | + elif dialect == 'sg': |
1347 | + result = 'EBAY-SG' |
1348 | + elif dialect == 'au': |
1349 | + result = 'EBAY-AU' |
1350 | + elif lang == 'fr': |
1351 | + if dialect == 'be': |
1352 | + result = 'EBAY-FRBE' |
1353 | + elif dialect == 'ca': |
1354 | + result = 'EBAY-FRCA' |
1355 | + else: |
1356 | + result = 'EBAY-FR' |
1357 | + elif lang == 'de': |
1358 | + if dialect == 'at': |
1359 | + result = 'EBAY-AT' |
1360 | + elif dialect == 'ch': |
1361 | + result = 'EBAY-CH' |
1362 | + else: |
1363 | + result = 'EBAY-DE' |
1364 | + elif lang == 'it': |
1365 | + result = 'EBAY-IT' |
1366 | + elif lang == 'pl': |
1367 | + result = 'EBAY-PL' |
1368 | + elif lang == 'es': |
1369 | + result = 'EBAY-ES' |
1370 | + elif lang == 'nl': |
1371 | + if dialect == 'be': |
1372 | + result = 'EBAY-NLBE' |
1373 | + else: |
1374 | + result = 'EBAY-NL' |
1375 | + elif lang == 'zh': |
1376 | + result = 'EBAY-HK' |
1377 | + elif lang == 'sv': |
1378 | + result = 'EBAY-SE' |
1379 | + |
1380 | + return default if result is None else result |
1381 | + |
1382 | + def search(self, text, result_limit): |
1383 | + if len(text) == 0: |
1384 | + return |
1385 | + |
1386 | + self.cardapio.write_to_log(self, 'searching for {0} on eBay'.format(text), is_debug = True) |
1387 | + |
1388 | + self.cancellable.reset() |
1389 | + |
1390 | + # prepare final API URL (items per page and search keyword) |
1391 | + current_args = self.api_base_args.copy() |
1392 | + current_args['paginationInput.entriesPerPage'] = result_limit |
1393 | + current_args['keywords'] = text |
1394 | + |
1395 | + final_url = self.api_base_url.format(urllib.urlencode(current_args)) |
1396 | + |
1397 | + self.cardapio.write_to_log(self, 'final API URL: {0}'.format(final_url), is_debug = True) |
1398 | + |
1399 | + # asynchronous and cancellable IO call |
1400 | + self.current_stream = gio.File(final_url) |
1401 | + self.current_stream.load_contents_async(self.show_search_results, |
1402 | + cancellable = self.cancellable, |
1403 | + user_data = text) |
1404 | + |
1405 | + def show_search_results(self, gdaemonfile, result, text): |
1406 | + """ |
1407 | + Callback to asynchronous IO (eBay's API call). |
1408 | + """ |
1409 | + |
1410 | + # watch out for connection problems |
1411 | + try: |
1412 | + json_body = self.current_stream.load_contents_finish(result)[0] |
1413 | + |
1414 | + # watch out for empty input |
1415 | + if len(json_body) == 0: |
1416 | + return |
1417 | + |
1418 | + response = json.loads(json_body) |
1419 | + except (ValueError, GError) as ex: |
1420 | + self.cardapio.handle_search_error(self, 'error while obtaining data: {0}'.format(str(ex))) |
1421 | + return |
1422 | + |
1423 | + # decode the result |
1424 | + try: |
1425 | + items = [] |
1426 | + |
1427 | + response_body = response['findItemsByKeywordsResponse'][0] |
1428 | + |
1429 | + # if we made a successful call... |
1430 | + if response_body['ack'][0] == 'Success': |
1431 | + search_result = response_body['searchResult'][0] |
1432 | + |
1433 | + # and we have any results... |
1434 | + if int(search_result['@count']) > 0: |
1435 | + |
1436 | + # remember them all |
1437 | + for ebay_item in search_result['item']: |
1438 | + ebay_item_url = ebay_item['viewItemURL'][0] |
1439 | + |
1440 | + items.append({ |
1441 | + 'name' : ebay_item['title'][0], |
1442 | + 'tooltip' : ebay_item_url, |
1443 | + 'icon name' : 'text-html', |
1444 | + 'type' : 'xdg', |
1445 | + 'command' : ebay_item_url, |
1446 | + 'context menu' : None |
1447 | + }) |
1448 | + |
1449 | + # on a succesful call, add the 'Search more...' item (URL from the response) |
1450 | + items.append({ |
1451 | + 'name' : _('Show additional results'), |
1452 | + 'tooltip' : _('Show additional search results in your web browser'), |
1453 | + 'icon name' : 'system-search', |
1454 | + 'type' : 'xdg', |
1455 | + 'command' : response_body['itemSearchURL'][0], |
1456 | + 'context menu' : None |
1457 | + }) |
1458 | + |
1459 | + # if the API call failed, add the generic 'search more' item |
1460 | + if len(items) == 0: |
1461 | + search_more_args = { |
1462 | + '_nkw' : text, |
1463 | + '_sacat' : 'See-All-Categories' |
1464 | + } |
1465 | + |
1466 | + items.append({ |
1467 | + 'name' : _('Show additional results'), |
1468 | + 'tooltip' : _('Show additional search results in your web browser'), |
1469 | + 'icon name' : 'system-search', |
1470 | + 'type' : 'xdg', |
1471 | + # TODO: cardapio later unquotes this and then quotes it again; |
1472 | + # it's screwing my quotation |
1473 | + 'command' : self.web_base_url.format(urllib.urlencode(search_more_args)), |
1474 | + 'context menu' : None |
1475 | + }) |
1476 | + |
1477 | + # pass the results to Cardapio |
1478 | + self.cardapio.handle_search_result(self, items, text) |
1479 | + |
1480 | + except KeyError: |
1481 | + self.cardapio.handle_search_error(self, "Incorrect eBay's JSON structure") |
1482 | + |
1483 | + def cancel(self): |
1484 | + self.cardapio.write_to_log(self, 'cancelling a recent eBay search (if any)', is_debug = True) |
1485 | + |
1486 | + if not self.cancellable.is_cancelled(): |
1487 | + self.cancellable.cancel() |
1488 | |
1489 | === modified file 'src/plugins/google.py' |
1490 | --- src/plugins/google.py 2010-08-04 20:24:22 +0000 |
1491 | +++ src/plugins/google.py 2010-08-12 17:20:35 +0000 |
1492 | @@ -12,7 +12,7 @@ |
1493 | help_text = '' |
1494 | version = '1.37' |
1495 | |
1496 | - plugin_api_version = 1.37 |
1497 | + plugin_api_version = 1.38 |
1498 | |
1499 | search_delay_type = 'remote search update delay' |
1500 | |
1501 | @@ -42,20 +42,7 @@ |
1502 | if google_interface_language_format == 'zh-HK': |
1503 | google_interface_language_format = 'zh-CN' |
1504 | |
1505 | - # The google search API only supports two sizes for the result list, |
1506 | - # that is: small (4 results) or large (8 results). So this plugin |
1507 | - # chooses the most appropriate given the 'search results limit' user |
1508 | - # preference. |
1509 | - |
1510 | - if self.c.settings['search results limit'] >= 8: |
1511 | - self.query_url = r'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=large&q=%s' |
1512 | - else: |
1513 | - self.query_url = r'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=small&q=%s' |
1514 | - |
1515 | - if self.c.settings['long search results limit'] >= 8: |
1516 | - self.query_url_long = r'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=large&q=%s' |
1517 | - else: |
1518 | - self.query_url_long = r'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=small&q=%s' |
1519 | + self.query_url = r'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz={0}&q={1}' |
1520 | |
1521 | self.search_controller = gio.Cancellable() |
1522 | |
1523 | @@ -72,7 +59,7 @@ |
1524 | self.loaded = True |
1525 | |
1526 | |
1527 | - def search(self, text, long_search = False): |
1528 | + def search(self, text, result_limit): |
1529 | |
1530 | # TODO: I'm sure this is not the best way of doing remote procedure |
1531 | # calls, but I can't seem to find anything that is this easy to use and |
1532 | @@ -84,10 +71,12 @@ |
1533 | self.current_query = text |
1534 | text = urllib2.quote(text) |
1535 | |
1536 | - if long_search: |
1537 | - query = self.query_url_long % text |
1538 | - else: |
1539 | - query = self.query_url % text |
1540 | + # The google search API only supports two sizes for the result list, |
1541 | + # that is: small (4 results) or large (8 results). So this plugin |
1542 | + # chooses the most appropriate given the 'search results limit' user |
1543 | + # preference. |
1544 | + |
1545 | + query = self.query_url.format('large' if result_limit >= 8 else 'small', text) |
1546 | |
1547 | self.stream = gio.File(query) |
1548 | |
1549 | |
1550 | === modified file 'src/plugins/google_localized.py' |
1551 | --- src/plugins/google_localized.py 2010-08-04 20:24:22 +0000 |
1552 | +++ src/plugins/google_localized.py 2010-08-12 17:20:35 +0000 |
1553 | @@ -12,7 +12,7 @@ |
1554 | help_text = '' |
1555 | version = '1.37' |
1556 | |
1557 | - plugin_api_version = 1.37 |
1558 | + plugin_api_version = 1.38 |
1559 | |
1560 | search_delay_type = 'remote search update delay' |
1561 | |
1562 | @@ -46,21 +46,7 @@ |
1563 | if google_interface_language_format[:2] == 'zh': |
1564 | google_results_language_format = google_interface_language_format |
1565 | |
1566 | - |
1567 | - # The google search API only supports two sizes for the result list, |
1568 | - # that is: small (4 results) or large (8 results). So this plugin |
1569 | - # chooses the most appropriate given the 'search results limit' user |
1570 | - # preference. |
1571 | - |
1572 | - if self.c.settings['search results limit'] >= 8: |
1573 | - self.query_url = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=large&lr=lang_%s&q=%%s' % google_results_language_format |
1574 | - else: |
1575 | - self.query_url = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=small&lr=lang_%s&q=%%s' % google_results_language_format |
1576 | - |
1577 | - if self.c.settings['long search results limit'] >= 8: |
1578 | - self.query_url_long = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=large&lr=lang_%s&q=%%s' % google_results_language_format |
1579 | - else: |
1580 | - self.query_url_long = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=small&lr=lang_%s&q=%%s' % google_results_language_format |
1581 | + self.query_url = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz={0}&lr=lang_%s&q={1}' % google_results_language_format |
1582 | |
1583 | self.search_controller = gio.Cancellable() |
1584 | |
1585 | @@ -77,7 +63,7 @@ |
1586 | self.loaded = True |
1587 | |
1588 | |
1589 | - def search(self, text, long_search = False): |
1590 | + def search(self, text, result_limit): |
1591 | |
1592 | # TODO: I'm sure this is not the best way of doing remote procedure |
1593 | # calls, but I can't seem to find anything that is this easy to use and |
1594 | @@ -89,10 +75,12 @@ |
1595 | self.current_query = text |
1596 | text = urllib2.quote(text) |
1597 | |
1598 | - if long_search: |
1599 | - query = self.query_url_long % text |
1600 | - else: |
1601 | - query = self.query_url % text |
1602 | + # The google search API only supports two sizes for the result list, |
1603 | + # that is: small (4 results) or large (8 results). So this plugin |
1604 | + # chooses the most appropriate given the 'search results limit' user |
1605 | + # preference. |
1606 | + |
1607 | + query = self.query_url.format('large' if result_limit >= 8 else 'small', text) |
1608 | |
1609 | self.stream = gio.File(query) |
1610 | |
1611 | |
1612 | === added file 'src/plugins/pidgin.py' |
1613 | --- src/plugins/pidgin.py 1970-01-01 00:00:00 +0000 |
1614 | +++ src/plugins/pidgin.py 2010-08-12 17:20:35 +0000 |
1615 | @@ -0,0 +1,218 @@ |
1616 | +from dbus.exceptions import DBusException |
1617 | + |
1618 | +class CardapioPlugin(CardapioPluginInterface): |
1619 | + |
1620 | + """ |
1621 | + Pidgin plugin based on it's D-Bus interface. Documentation: |
1622 | + http://developer.pidgin.im/wiki/DbusHowto |
1623 | + |
1624 | + The plugin looks for online buddies and provides the user with |
1625 | + possibility to start a conversation with any of them. All active |
1626 | + accounts are considered. We match buddies by their alias (case |
1627 | + insensitive). |
1628 | + |
1629 | + Please note that the plugin only works when Pidgin is on. You don't |
1630 | + need to turn Pidgin on before starting Cardapio or before initializing |
1631 | + the plugin. You just need to turn it on before performing a Cardapio |
1632 | + search. |
1633 | + """ |
1634 | + |
1635 | + # Cardapio's variables |
1636 | + author = 'Pawel Bara' |
1637 | + name = _('Pidgin') |
1638 | + description = _('Search for online Pidgin buddies') |
1639 | + version = '0.9b' |
1640 | + |
1641 | + url = '' |
1642 | + help_text = '' |
1643 | + |
1644 | + default_keyword = 'pidgin' |
1645 | + |
1646 | + plugin_api_version = 1.38 |
1647 | + |
1648 | + search_delay_type = 'local search update delay' |
1649 | + |
1650 | + category_name = _('Pidgin Buddies') |
1651 | + category_tooltip = _('Your online Pidgin buddies') |
1652 | + |
1653 | + category_icon = 'pidgin' |
1654 | + fallback_icon = '' |
1655 | + |
1656 | + hide_from_sidebar = True |
1657 | + |
1658 | + def __init__(self, cardapio_proxy): |
1659 | + cardapio_proxy.write_to_log(self, 'initializing Pidgin plugin') |
1660 | + |
1661 | + self.cardapio = cardapio_proxy |
1662 | + |
1663 | + # Pidgin's D-Bus constants |
1664 | + self.dpidgin_bus_name = 'im.pidgin.purple.PurpleService' |
1665 | + self.dpidgin_object_path = '/im/pidgin/purple/PurpleObject' |
1666 | + self.dpidgin_iface_name = 'im.pidgin.purple.PurpleInterface' |
1667 | + |
1668 | + try: |
1669 | + self.bus = dbus.SessionBus() |
1670 | + # we track Pidgin's on / off status |
1671 | + self.bus.watch_name_owner(self.dpidgin_bus_name, self.on_dbus_name_change) |
1672 | + |
1673 | + self.loaded = True |
1674 | + |
1675 | + except DBusException as ex: |
1676 | + self.cardapio.write_to_log(self, 'Pidgin plugin initialization error: {0}'.format(str(ex)), is_error = True) |
1677 | + self.loaded = False |
1678 | + |
1679 | + def on_dbus_name_change(self, connection_name): |
1680 | + """ |
1681 | + This method effectively tracks down the events of Pidgin app starting |
1682 | + and shutting down. When the app shuts down, this callback nullifies our |
1683 | + Pidgin's proxy and when the app starts, this callback sets the valid |
1684 | + proxy again. |
1685 | + """ |
1686 | + |
1687 | + if len(connection_name) == 0: |
1688 | + self.pidgin = None |
1689 | + else: |
1690 | + bus_object = self.bus.get_object(connection_name, self.dpidgin_object_path) |
1691 | + self.pidgin = dbus.Interface(bus_object, self.dpidgin_iface_name) |
1692 | + |
1693 | + def search(self, text, result_limit): |
1694 | + if len(text) == 0: |
1695 | + return |
1696 | + |
1697 | + # send empty results to Cardapio if Pidgin's off |
1698 | + if self.pidgin is None: |
1699 | + self.cardapio.handle_search_result(self, [], text) |
1700 | + return |
1701 | + |
1702 | + self.cardapio.write_to_log(self, 'searching for Pidgin buddies with name like {0}'.format(text), is_debug = True) |
1703 | + |
1704 | + # prepare a parametrized callback that remembers the current search text and |
1705 | + # the result limit |
1706 | + callback = DBusGatherBuddiesCallback(self.pidgin, self.finalize_search, |
1707 | + text, result_limit) |
1708 | + |
1709 | + # let's start by getting all of the user's active accounts |
1710 | + self.pidgin.PurpleAccountsGetAllActive(reply_handler = callback.handle_search_result, |
1711 | + error_handler = self.handle_search_error) |
1712 | + |
1713 | + def finalize_search(self, buddies, text): |
1714 | + """ |
1715 | + DBusGatherBuddiesCallback invokes this when it finishes gathering |
1716 | + single search results. This method finalizes the search, then |
1717 | + passes the result to Cardapio. |
1718 | + """ |
1719 | + |
1720 | + items = [] |
1721 | + |
1722 | + # for every buddy... |
1723 | + for buddy in buddies: |
1724 | + |
1725 | + # 'start conversation' callback wrapper |
1726 | + conversation_callback = DBusTalkToBuddyCallback(self.pidgin, buddy[0], buddy[1]) |
1727 | + |
1728 | + # add 'talk to this buddy' item |
1729 | + items.append({ |
1730 | + 'name' : buddy[2] + ' ({0})'.format(buddy[1]), |
1731 | + 'tooltip' : _('Talk to this buddy'), |
1732 | + 'icon name' : 'pidgin', |
1733 | + 'type' : 'callback', |
1734 | + 'command' : conversation_callback.start_conversation, |
1735 | + 'context menu' : None |
1736 | + }) |
1737 | + |
1738 | + self.cardapio.handle_search_result(self, items, text) |
1739 | + |
1740 | + def handle_search_error(self, error): |
1741 | + """ |
1742 | + Error callback to asynchronous Pidgin's D-Bus call. |
1743 | + """ |
1744 | + |
1745 | + self.cardapio.handle_search_error(self, 'Pidgin search error: {0}'.format(str(error))) |
1746 | + |
1747 | +class DBusGatherBuddiesCallback: |
1748 | + """ |
1749 | + DBusGatherBuddiesCallback serves as a parametrized wrapper over |
1750 | + the asynchronous callback to Pidgin's PurpleAccountsGetAllActive |
1751 | + call. |
1752 | + """ |
1753 | + |
1754 | + def __init__(self, pidgin, result_callback, text, result_limit): |
1755 | + self.pidgin = pidgin |
1756 | + self.result_callback = result_callback |
1757 | + |
1758 | + self.text = text |
1759 | + self.result_limit = result_limit |
1760 | + |
1761 | + def handle_search_result(self, accounts): |
1762 | + """ |
1763 | + Callback to asynchronous Pidgin's PurpleAccountsGetAllActive |
1764 | + call. It gathers results and then passes those back to the |
1765 | + main plugin class through the result_callback method. |
1766 | + """ |
1767 | + |
1768 | + # gather all online buddies |
1769 | + self.result_callback(self.gather_buddies(accounts), self.text) |
1770 | + |
1771 | + def gather_buddies(self, accounts): |
1772 | + """ |
1773 | + Gathers all of the user's online Pidgin buddies in a form of list |
1774 | + containing tuples. |
1775 | + """ |
1776 | + |
1777 | + # return empty results if Pidgin's off |
1778 | + if self.pidgin is None: |
1779 | + return [] |
1780 | + |
1781 | + buddies = [] |
1782 | + |
1783 | + # for every active account... |
1784 | + for account in accounts: |
1785 | + |
1786 | + # and every buddy associated with this active account... |
1787 | + for buddy in self.pidgin.PurpleFindBuddies(account, ''): |
1788 | + |
1789 | + # obey the result limit! |
1790 | + if len(buddies) == self.result_limit: |
1791 | + return buddies |
1792 | + |
1793 | + # we remember only those buddies who are online now |
1794 | + if self.pidgin.PurpleBuddyIsOnline(buddy): |
1795 | + |
1796 | + buddy_alias = self.pidgin.PurpleBuddyGetAlias(buddy) |
1797 | + buddy_name = self.pidgin.PurpleBuddyGetName(buddy) |
1798 | + |
1799 | + # if buddies alias contains (case insensitive) the search |
1800 | + # keyword, add him to the result list |
1801 | + if buddy_alias.lower().count(self.text.lower()) > 0: |
1802 | + buddies.append((account, buddy_name, buddy_alias)) |
1803 | + |
1804 | + return buddies |
1805 | + |
1806 | + |
1807 | +class DBusTalkToBuddyCallback: |
1808 | + """ |
1809 | + DBusTalkToBuddyCallback serves as a parametrized wrapper over |
1810 | + the asynchronous callback to Pidgin's PurpleConversationNew |
1811 | + call. |
1812 | + """ |
1813 | + |
1814 | + def __init__(self, pidgin, account, buddy): |
1815 | + self.pidgin = pidgin |
1816 | + |
1817 | + self.account = account |
1818 | + self.buddy = buddy |
1819 | + |
1820 | + def start_conversation(self, search_text): |
1821 | + """ |
1822 | + Starts a conversation for the account and buddy name with which this |
1823 | + callback was created. Ignores the callback parameter (search_text) |
1824 | + from Cardapio. |
1825 | + """ |
1826 | + |
1827 | + # try to avoid errors if Pidgin's off |
1828 | + if self.pidgin is None: |
1829 | + return |
1830 | + |
1831 | + # starting a conversation... the number 1 means 'InstantMessage |
1832 | + # conversation' |
1833 | + self.pidgin.PurpleConversationNew(1, self.account, self.buddy) |
1834 | \ No newline at end of file |
1835 | |
1836 | === modified file 'src/plugins/software_center.py' |
1837 | --- src/plugins/software_center.py 2010-08-02 07:22:55 +0000 |
1838 | +++ src/plugins/software_center.py 2010-08-12 17:20:35 +0000 |
1839 | @@ -32,7 +32,7 @@ |
1840 | help_text = '' |
1841 | version = '1.12' |
1842 | |
1843 | - plugin_api_version = 1.37 |
1844 | + plugin_api_version = 1.38 |
1845 | |
1846 | search_delay_type = 'local search update delay' |
1847 | default_keyword = 'softwarecenter' |
1848 | @@ -53,9 +53,6 @@ |
1849 | |
1850 | self.c = cardapio_proxy |
1851 | self.loaded = False |
1852 | - |
1853 | - self.num_search_results = self.c.settings['search results limit'] |
1854 | - self.num_search_results_long = self.c.settings['long search results limit'] |
1855 | |
1856 | if import_error: |
1857 | self.c.write_to_log(self, 'Could not import certain modules', is_error = True) |
1858 | @@ -103,7 +100,7 @@ |
1859 | self.loaded = True # set to true if everything goes well |
1860 | |
1861 | |
1862 | - def search(self, text, long_search = False): |
1863 | + def search(self, text, result_limit): |
1864 | |
1865 | self.current_query = text |
1866 | |
1867 | @@ -116,14 +113,9 @@ |
1868 | enquire.set_sort_by_value_then_relevance(XAPIAN_VALUE_POPCON) |
1869 | matches = enquire.get_mset(0, len(self.db)) |
1870 | |
1871 | - if long_search: |
1872 | - num_search_results = self.num_search_results_long |
1873 | - else: |
1874 | - num_search_results = self.num_search_results |
1875 | - |
1876 | i = 0 |
1877 | for m in matches: |
1878 | - if not i < num_search_results : break |
1879 | + if not i < result_limit : break |
1880 | |
1881 | doc = m[xapian.MSET_DOCUMENT] |
1882 | pkgname = self.db.get_pkgname(doc) |
1883 | |
1884 | === added file 'src/plugins/tomboy.py' |
1885 | --- src/plugins/tomboy.py 1970-01-01 00:00:00 +0000 |
1886 | +++ src/plugins/tomboy.py 2010-08-12 17:20:35 +0000 |
1887 | @@ -0,0 +1,206 @@ |
1888 | +from dbus.exceptions import DBusException |
1889 | + |
1890 | +class CardapioPlugin(CardapioPluginInterface): |
1891 | + |
1892 | + """ |
1893 | + Tomboy plugin based on it's D-Bus interface. Documentation: |
1894 | + http://arstechnica.com/open-source/news/2007/09/using-the-tomboy-d-bus-interface.ars |
1895 | + |
1896 | + The plugin looks for notes with titles and contents similar to the search string. |
1897 | + If it can't find any, it provides user with the handy 'create a note with this |
1898 | + title' link. |
1899 | + |
1900 | + Please note that the plugin only works when Tomboy is on. You don't need to |
1901 | + turn Tomboy on before starting Cardapio or before initializing the plugin. |
1902 | + You just need to turn it on before performing a Cardapio search. |
1903 | + """ |
1904 | + |
1905 | + # Cardapio's variables |
1906 | + author = 'Pawel Bara' |
1907 | + name = _('Tomboy') |
1908 | + description = _('Search for Tomboy notes') |
1909 | + version = '0.9b' |
1910 | + |
1911 | + url = '' |
1912 | + help_text = '' |
1913 | + |
1914 | + default_keyword = 'tomboy' |
1915 | + |
1916 | + plugin_api_version = 1.38 |
1917 | + |
1918 | + search_delay_type = 'local search update delay' |
1919 | + |
1920 | + category_name = _('Tomboy Results') |
1921 | + category_tooltip = _('Your Tomboy notes') |
1922 | + |
1923 | + category_icon = 'tomboy' |
1924 | + fallback_icon = '' |
1925 | + |
1926 | + hide_from_sidebar = True |
1927 | + |
1928 | + def __init__(self, cardapio_proxy): |
1929 | + cardapio_proxy.write_to_log(self, 'initializing Tomboy plugin') |
1930 | + |
1931 | + self.cardapio = cardapio_proxy |
1932 | + |
1933 | + # Tomboy's D-Bus constants |
1934 | + self.dtomboy_bus_name = 'org.gnome.Tomboy' |
1935 | + self.dtomboy_object_path = '/org/gnome/Tomboy/RemoteControl' |
1936 | + self.dtomboy_iface_name = 'org.gnome.Tomboy.RemoteControl' |
1937 | + |
1938 | + try: |
1939 | + self.bus = dbus.SessionBus() |
1940 | + # we track Tomboy's on / off status |
1941 | + self.bus.watch_name_owner(self.dtomboy_bus_name, self.on_dbus_name_change) |
1942 | + |
1943 | + self.loaded = True |
1944 | + |
1945 | + except DBusException as ex: |
1946 | + self.cardapio.write_to_log(self, 'Tomboy plugin initialization error: {0}'.format(str(ex)), is_error = True) |
1947 | + self.loaded = False |
1948 | + |
1949 | + def on_dbus_name_change(self, connection_name): |
1950 | + """ |
1951 | + This method effectively tracks down the events of Tomboy app starting |
1952 | + and shutting down. When the app shuts down, this callback nullifies our |
1953 | + Tomboy's proxy and when the app starts, the callback sets the valid |
1954 | + proxy again. |
1955 | + """ |
1956 | + |
1957 | + if len(connection_name) == 0: |
1958 | + self.tomboy = None |
1959 | + else: |
1960 | + bus_object = self.bus.get_object(connection_name, self.dtomboy_object_path) |
1961 | + self.tomboy = dbus.Interface(bus_object, self.dtomboy_iface_name) |
1962 | + |
1963 | + def search(self, text, result_limit): |
1964 | + if len(text) == 0: |
1965 | + return |
1966 | + |
1967 | + # send empty results to Cardapio if Tomboy's off |
1968 | + if self.tomboy is None: |
1969 | + self.cardapio.handle_search_result(self, [], text) |
1970 | + return |
1971 | + |
1972 | + self.cardapio.write_to_log(self, 'searching for Tomboy notes with topic like {0}'.format(text), is_debug = True) |
1973 | + |
1974 | + # prepare a parametrized callback that remembers the current search text and |
1975 | + # the result limit |
1976 | + callback = DBusSearchNotesCallback(self.tomboy, self.finalize_search, |
1977 | + text, result_limit) |
1978 | + |
1979 | + # we ask for a case insensitive search |
1980 | + self.tomboy.SearchNotes(text.lower(), False, reply_handler = callback.handle_search_result, |
1981 | + error_handler = self.handle_search_error) |
1982 | + |
1983 | + def finalize_search(self, items, text): |
1984 | + """ |
1985 | + DBusSearchNotesCallback invokes this when it finishes gathering |
1986 | + single search results. This method finalizes the search, then |
1987 | + passes the results to Cardapio. |
1988 | + """ |
1989 | + |
1990 | + # if there are no results, we'll add the 'create a note with this |
1991 | + # title' link |
1992 | + if len(items) == 0: |
1993 | + items.append({ |
1994 | + 'name' : _('Create this note'), |
1995 | + 'tooltip' : _('Create a new note with this title in Tomboy'), |
1996 | + 'icon name' : 'tomboy', |
1997 | + 'type' : 'callback', |
1998 | + 'command' : self.tomboy_create_note, |
1999 | + 'context menu' : None |
2000 | + }) |
2001 | + |
2002 | + # the 'search more' option is always present |
2003 | + items.append({ |
2004 | + 'name' : _('Show additional notes'), |
2005 | + 'tooltip' : _('Show additional notes in Tomboy'), |
2006 | + 'icon name' : 'tomboy', |
2007 | + 'type' : 'callback', |
2008 | + 'command' : self.tomboy_find_more, |
2009 | + 'context menu' : None |
2010 | + }) |
2011 | + |
2012 | + self.cardapio.handle_search_result(self, items, text) |
2013 | + |
2014 | + def handle_search_error(self, error): |
2015 | + """ |
2016 | + Error callback to asynchronous Tomboy's D-Bus call. |
2017 | + """ |
2018 | + |
2019 | + self.cardapio.handle_search_error(self, 'Tomboy search error: {0}'.format(str(error))) |
2020 | + |
2021 | + def tomboy_create_note(self, text): |
2022 | + """ |
2023 | + Creates a new note with the given title, then displays it to |
2024 | + the user. |
2025 | + """ |
2026 | + |
2027 | + # try to avoid errors if Tomboy's off |
2028 | + if self.tomboy is None: |
2029 | + return |
2030 | + |
2031 | + new_note = self.tomboy.CreateNamedNote(text) |
2032 | + self.tomboy.DisplayNote(new_note) |
2033 | + |
2034 | + def tomboy_find_more(self, text): |
2035 | + """ |
2036 | + Opens Tomboy's 'Search more' window. |
2037 | + """ |
2038 | + |
2039 | + # try to avoid errors if Tomboy's off |
2040 | + if self.tomboy is None: |
2041 | + return |
2042 | + |
2043 | + self.tomboy.DisplaySearchWithText(text) |
2044 | + |
2045 | +class DBusSearchNotesCallback: |
2046 | + """ |
2047 | + DBusSearchNotesCallback serves as a parametrized wrapper over |
2048 | + the asynchronous callback to Tomboy's SearchNotes call. |
2049 | + """ |
2050 | + |
2051 | + def __init__(self, tomboy, result_callback, text, result_limit): |
2052 | + self.tomboy = tomboy |
2053 | + self.result_callback = result_callback |
2054 | + |
2055 | + self.text = text |
2056 | + self.result_limit = result_limit |
2057 | + |
2058 | + def handle_search_result(self, result): |
2059 | + """ |
2060 | + Callback to asynchronous Tomboy's SearchNotes call. It gathers |
2061 | + results and then passes those back to the main plugin class |
2062 | + through the result_callback method. |
2063 | + """ |
2064 | + |
2065 | + # pass empty results to the main class if Tomboy's off |
2066 | + if self.tomboy is None: |
2067 | + self.result_callback([], self.text) |
2068 | + return |
2069 | + |
2070 | + items = [] |
2071 | + |
2072 | + # if we have any results... |
2073 | + i = 0 |
2074 | + for note in result: |
2075 | + |
2076 | + # exit after gathering enough results |
2077 | + if i == self.result_limit: |
2078 | + break |
2079 | + i += 1 |
2080 | + |
2081 | + # add 'open this note' item |
2082 | + items.append({ |
2083 | + 'name' : self.tomboy.GetNoteTitle(note), |
2084 | + 'tooltip' : _('Open this note'), |
2085 | + 'icon name' : 'tomboy', |
2086 | + 'type' : 'xdg', |
2087 | + 'command' : note, |
2088 | + 'context menu' : None |
2089 | + }) |
2090 | + |
2091 | + # pass all of the gathered items and the current search |
2092 | + # text to the main plugin class |
2093 | + self.result_callback(items, self.text) |
2094 | |
2095 | === modified file 'src/plugins/tracker.py' |
2096 | --- src/plugins/tracker.py 2010-08-02 07:22:55 +0000 |
2097 | +++ src/plugins/tracker.py 2010-08-12 17:20:35 +0000 |
2098 | @@ -10,7 +10,7 @@ |
2099 | help_text = '' |
2100 | version = '1.37' |
2101 | |
2102 | - plugin_api_version = 1.37 |
2103 | + plugin_api_version = 1.38 |
2104 | |
2105 | search_delay_type = 'local search update delay' |
2106 | |
2107 | @@ -37,9 +37,6 @@ |
2108 | self.loaded = False |
2109 | return |
2110 | |
2111 | - self.search_results_limit = self.c.settings['search results limit'] |
2112 | - self.search_results_limit_long = self.c.settings['long search results limit'] |
2113 | - |
2114 | self.action_command = r"tracker-search-tool '%s'" |
2115 | self.action = { |
2116 | 'name' : _('Show additional results'), |
2117 | @@ -53,16 +50,11 @@ |
2118 | self.loaded = True |
2119 | |
2120 | |
2121 | - def search(self, text, long_search = False): |
2122 | + def search(self, text, result_limit): |
2123 | |
2124 | self.current_query = text |
2125 | text = urllib2.quote(text).lower() |
2126 | |
2127 | - if long_search: |
2128 | - search_results_limit = self.search_results_limit_long |
2129 | - else: |
2130 | - search_results_limit = self.search_results_limit |
2131 | - |
2132 | self.tracker.SparqlQuery( |
2133 | """ |
2134 | SELECT ?uri ?mime |
2135 | @@ -76,7 +68,7 @@ |
2136 | ORDER BY ASC(?uri) |
2137 | LIMIT %d |
2138 | """ |
2139 | - % (text, search_results_limit), |
2140 | + % (text, result_limit), |
2141 | dbus_interface='org.freedesktop.Tracker1.Resources', |
2142 | reply_handler=self.prepare_and_handle_search_result, |
2143 | error_handler=self.handle_search_error |
2144 | |
2145 | === modified file 'src/plugins/tracker_fts.py' |
2146 | --- src/plugins/tracker_fts.py 2010-08-04 17:04:13 +0000 |
2147 | +++ src/plugins/tracker_fts.py 2010-08-12 17:20:35 +0000 |
2148 | @@ -10,7 +10,7 @@ |
2149 | help_text = '' |
2150 | version = '1.371' |
2151 | |
2152 | - plugin_api_version = 1.37 |
2153 | + plugin_api_version = 1.38 |
2154 | |
2155 | search_delay_type = 'local search update delay' |
2156 | |
2157 | @@ -22,7 +22,7 @@ |
2158 | hide_from_sidebar = True |
2159 | |
2160 | |
2161 | - def __init__(self, cardapio_proxy): |
2162 | + def __init__(self, cardapio_proxy): |
2163 | |
2164 | self.c = cardapio_proxy |
2165 | |
2166 | @@ -31,13 +31,10 @@ |
2167 | |
2168 | if bus.request_name('org.freedesktop.Tracker1') == dbus.bus.REQUEST_NAME_REPLY_IN_QUEUE: |
2169 | tracker_object = bus.get_object('org.freedesktop.Tracker1', '/org/freedesktop/Tracker1/Resources') |
2170 | - self.tracker = dbus.Interface(tracker_object, 'org.freedesktop.Tracker1.Resources') |
2171 | + self.tracker = dbus.Interface(tracker_object, 'org.freedesktop.Tracker1.Resources') |
2172 | else: |
2173 | self.loaded = False |
2174 | - return |
2175 | - |
2176 | - self.search_results_limit = self.c.settings['search results limit'] |
2177 | - self.search_results_limit_long = self.c.settings['long search results limit'] |
2178 | + return |
2179 | |
2180 | self.action_command = r'tracker-search-tool %s' |
2181 | self.action = { |
2182 | @@ -52,20 +49,15 @@ |
2183 | self.loaded = True |
2184 | |
2185 | |
2186 | - def search(self, text, long_search = False): |
2187 | + def search(self, text, result_limit): |
2188 | |
2189 | self.current_query = text |
2190 | text = urllib2.quote(text).lower() |
2191 | |
2192 | - if long_search: |
2193 | - search_results_limit = self.search_results_limit_long |
2194 | - else: |
2195 | - search_results_limit = self.search_results_limit |
2196 | - |
2197 | self.tracker.SparqlQuery( |
2198 | """ |
2199 | SELECT ?uri ?mime |
2200 | - WHERE { |
2201 | + WHERE { |
2202 | ?item a nie:InformationElement; |
2203 | fts:match "%s"; |
2204 | nie:url ?uri; |
2205 | @@ -73,8 +65,8 @@ |
2206 | tracker:available true. |
2207 | } |
2208 | LIMIT %d |
2209 | - """ |
2210 | - % (text, search_results_limit), |
2211 | + """ |
2212 | + % (text, result_limit), |
2213 | dbus_interface='org.freedesktop.Tracker1.Resources', |
2214 | reply_handler=self.prepare_and_handle_search_result, |
2215 | error_handler=self.handle_search_error |
2216 | @@ -90,10 +82,10 @@ |
2217 | |
2218 | def prepare_and_handle_search_result(self, results): |
2219 | |
2220 | - formatted_results = [] |
2221 | + formatted_results = [] |
2222 | |
2223 | for result in results: |
2224 | - |
2225 | + |
2226 | dummy, canonical_path = urllib2.splittype(result[0]) |
2227 | parent_name, child_name = os.path.split(canonical_path) |
2228 | icon_name = result[1] |
2229 | |
2230 | === modified file 'src/plugins/virtualbox.py' |
2231 | --- src/plugins/virtualbox.py 2010-08-02 07:22:55 +0000 |
2232 | +++ src/plugins/virtualbox.py 2010-08-12 17:20:35 +0000 |
2233 | @@ -19,7 +19,7 @@ |
2234 | help_text = '' |
2235 | version = '1.22' |
2236 | |
2237 | - plugin_api_version = 1.37 |
2238 | + plugin_api_version = 1.38 |
2239 | |
2240 | search_delay_type = None |
2241 | |
2242 | @@ -70,8 +70,8 @@ |
2243 | |
2244 | self.loaded = True |
2245 | |
2246 | - |
2247 | - def search(self, text, long_search = False): |
2248 | + # TODO: DOES NOT RESPECT result_limit's AUTHORITA'! |
2249 | + def search(self, text, result_limit): |
2250 | |
2251 | self.current_query = text |
2252 | |
2253 | |
2254 | === modified file 'src/plugins/wikipedia.py' |
2255 | --- src/plugins/wikipedia.py 2010-08-02 07:22:55 +0000 |
2256 | +++ src/plugins/wikipedia.py 2010-08-12 17:20:35 +0000 |
2257 | @@ -29,7 +29,7 @@ |
2258 | url = '' |
2259 | help_text = '' |
2260 | |
2261 | - plugin_api_version = 1.37 |
2262 | + plugin_api_version = 1.38 |
2263 | |
2264 | search_delay_type = 'remote search update delay' |
2265 | |
2266 | @@ -54,13 +54,7 @@ |
2267 | # maximum four results, formatted as json) |
2268 | self.api_base_args = { |
2269 | 'action': 'opensearch', |
2270 | - 'format': 'json', |
2271 | - 'limit' : str(self.cardapio.settings['search results limit']), |
2272 | - } |
2273 | - self.api_base_args_long = { |
2274 | - 'action': 'opensearch', |
2275 | - 'format': 'json', |
2276 | - 'limit' : str(self.cardapio.settings['long search results limit']), |
2277 | + 'format': 'json' |
2278 | } |
2279 | |
2280 | # Wikipedia's base URLs (search and show details variations) |
2281 | @@ -69,23 +63,19 @@ |
2282 | |
2283 | self.loaded = True |
2284 | |
2285 | - def search(self, text, long_search = False): |
2286 | + def search(self, text, result_limit): |
2287 | if len(text) == 0: |
2288 | return |
2289 | |
2290 | - self.current_query = text |
2291 | - |
2292 | self.cardapio.write_to_log(self, 'searching for {0} in Wikipedia'.format(text), is_debug = True) |
2293 | |
2294 | self.cancellable.reset() |
2295 | |
2296 | # prepare final API URL |
2297 | - if long_search: |
2298 | - current_args = self.api_base_args_long.copy() |
2299 | - else: |
2300 | - current_args = self.api_base_args.copy() |
2301 | - |
2302 | + current_args = self.api_base_args.copy() |
2303 | + current_args['limit'] = result_limit |
2304 | current_args['search'] = text |
2305 | + |
2306 | final_url = self.api_base_url.format(urllib.urlencode(current_args)) |
2307 | |
2308 | self.cardapio.write_to_log(self, 'final API URL: {0}'.format(final_url), is_debug = True) |
2309 | @@ -93,9 +83,10 @@ |
2310 | # asynchronous and cancellable IO call |
2311 | self.current_stream = gio.File(final_url) |
2312 | self.current_stream.load_contents_async(self.show_search_results, |
2313 | - cancellable = self.cancellable) |
2314 | + cancellable = self.cancellable, |
2315 | + user_data = text) |
2316 | |
2317 | - def show_search_results(self, gdaemonfile, result): |
2318 | + def show_search_results(self, gdaemonfile, result, text): |
2319 | """ |
2320 | Callback to asynchronous IO (Wikipedia's API call). |
2321 | """ |
2322 | @@ -120,6 +111,11 @@ |
2323 | # response[1] because the response looks like: [text, [result_list]] |
2324 | # append results (if any) |
2325 | for item in response[1]: |
2326 | + # TODO: wikipedia sometimes returns item names encoded in unicode (try |
2327 | + # searching for 'aaaaaaaaaaaaa' for example); we use those names as part |
2328 | + # of a URL so we need to encode the special characters; unfortunately, |
2329 | + # Python's 2.* urllib.quote throws an exception when it's given unicode |
2330 | + # argument - what now? |
2331 | item_url = self.web_base_url.format(urllib.quote(item)) |
2332 | items.append({ |
2333 | 'name' : item, |
2334 | @@ -131,7 +127,7 @@ |
2335 | }) |
2336 | |
2337 | # pass the results to Cardapio |
2338 | - self.cardapio.handle_search_result(self, items, self.current_query) |
2339 | + self.cardapio.handle_search_result(self, items, text) |
2340 | |
2341 | except KeyError: |
2342 | self.cardapio.handle_search_error(self, "Incorrect Wikipedia's JSON structure") |
2343 | |
2344 | === modified file 'src/plugins/yahoo.py' |
2345 | --- src/plugins/yahoo.py 2010-08-02 07:22:55 +0000 |
2346 | +++ src/plugins/yahoo.py 2010-08-12 17:20:35 +0000 |
2347 | @@ -31,7 +31,7 @@ |
2348 | url = '' |
2349 | help_text = '' |
2350 | |
2351 | - plugin_api_version = 1.37 |
2352 | + plugin_api_version = 1.38 |
2353 | |
2354 | search_delay_type = 'remote search update delay' |
2355 | |
2356 | @@ -66,44 +66,30 @@ |
2357 | 'appid' : 'TuNKmOzV34GRC9mrBNZMgr.vY1xPMLMH9U3PsOYkg8WvYnFawnB5gKd4GsrUbqluzg--', |
2358 | 'format' : 'json', |
2359 | 'style' : 'raw', |
2360 | - 'count' : self.cardapio.settings['search results limit'], |
2361 | - 'lang' : language, |
2362 | - 'region' : region |
2363 | - } |
2364 | - |
2365 | - self.api_base_args_long = { |
2366 | - 'appid' : 'TuNKmOzV34GRC9mrBNZMgr.vY1xPMLMH9U3PsOYkg8WvYnFawnB5gKd4GsrUbqluzg--', |
2367 | - 'format' : 'json', |
2368 | - 'style' : 'raw', |
2369 | - 'count' : self.cardapio.settings['long search results limit'], |
2370 | 'lang' : language, |
2371 | 'region' : region |
2372 | } |
2373 | |
2374 | # Yahoo's base URLs (search and search more variations) |
2375 | - self.api_base_url = 'http://boss.yahooapis.com/ysearch/web/v1/{0}?' + urllib.urlencode(self.api_base_args) |
2376 | - self.api_base_url_long = 'http://boss.yahooapis.com/ysearch/web/v1/{0}?' + urllib.urlencode(self.api_base_args_long) |
2377 | + self.api_base_url = 'http://boss.yahooapis.com/ysearch/web/v1/{0}?{1}' |
2378 | self.web_base_url = 'http://search.yahoo.com/search?{0}' |
2379 | |
2380 | self.loaded = True |
2381 | |
2382 | - def search(self, text, long_search = False): |
2383 | + def search(self, text, result_limit): |
2384 | if len(text) == 0: |
2385 | return |
2386 | |
2387 | - self.current_query = text |
2388 | - |
2389 | self.cardapio.write_to_log(self, 'searching for {0} using Yahoo'.format(text), is_debug = True) |
2390 | |
2391 | self.cancellable.reset() |
2392 | |
2393 | # prepare final API URL |
2394 | - if long_search: |
2395 | - api_base_url = self.api_base_url_long |
2396 | - else: |
2397 | - api_base_url = self.api_base_url |
2398 | - |
2399 | - final_url = api_base_url.format(urllib.quote(text, '')) |
2400 | + current_args = self.api_base_args.copy() |
2401 | + current_args['count'] = result_limit |
2402 | + |
2403 | + final_url = self.api_base_url.format(urllib.quote(text, ''), urllib.urlencode(current_args)) |
2404 | + |
2405 | self.cardapio.write_to_log(self, 'final API URL: {0}'.format(final_url), is_debug = True) |
2406 | |
2407 | # asynchronous and cancellable IO call |
2408 | @@ -164,7 +150,7 @@ |
2409 | }) |
2410 | |
2411 | # pass the results to Cardapio |
2412 | - self.cardapio.handle_search_result(self, items, self.current_query) |
2413 | + self.cardapio.handle_search_result(self, items, text) |
2414 | |
2415 | except KeyError: |
2416 | self.cardapio.handle_search_error(self, "Incorrect Yahoo's JSON structure") |
2417 | |
2418 | === modified file 'src/plugins/you_tube.py' |
2419 | --- src/plugins/you_tube.py 2010-08-02 07:22:55 +0000 |
2420 | +++ src/plugins/you_tube.py 2010-08-12 17:20:35 +0000 |
2421 | @@ -22,7 +22,7 @@ |
2422 | url = '' |
2423 | help_text = '' |
2424 | |
2425 | - plugin_api_version = 1.37 |
2426 | + plugin_api_version = 1.38 |
2427 | |
2428 | search_delay_type = 'remote search update delay' |
2429 | |
2430 | @@ -45,12 +45,7 @@ |
2431 | |
2432 | # YouTube's API arguments (format and maximum result count) |
2433 | self.api_base_args = { |
2434 | - 'alt' : 'json', |
2435 | - 'max-results': self.cardapio.settings['search results limit'], |
2436 | - } |
2437 | - self.api_base_args_long = { |
2438 | - 'alt' : 'json', |
2439 | - 'max-results': self.cardapio.settings['long search results limit'], |
2440 | + 'alt' : 'json' |
2441 | } |
2442 | |
2443 | # YouTube's base URLs (search and search more variations) |
2444 | @@ -59,25 +54,20 @@ |
2445 | |
2446 | self.loaded = True |
2447 | |
2448 | - def search(self, text, long_search = False): |
2449 | + def search(self, text, result_limit): |
2450 | |
2451 | if len(text) == 0: |
2452 | return |
2453 | |
2454 | - self.current_query = text |
2455 | - |
2456 | self.cardapio.write_to_log(self, 'searching for {0} using YouTube'.format(text), is_debug = True) |
2457 | |
2458 | self.cancellable.reset() |
2459 | |
2460 | # prepare final API URL |
2461 | - if long_search: |
2462 | - api_base_args = self.api_base_args_long |
2463 | - else: |
2464 | - api_base_args = self.api_base_args |
2465 | - |
2466 | - current_args = api_base_args.copy() |
2467 | + current_args = self.api_base_args.copy() |
2468 | + current_args['max-results'] = result_limit |
2469 | current_args['q'] = text |
2470 | + |
2471 | final_url = self.api_base_url.format(urllib.urlencode(current_args)) |
2472 | |
2473 | self.cardapio.write_to_log(self, 'final API URL: {0}'.format(final_url), is_debug = True) |
2474 | @@ -150,7 +140,7 @@ |
2475 | }) |
2476 | |
2477 | # pass the results to Cardapio |
2478 | - self.cardapio.handle_search_result(self, items, self.current_query) |
2479 | + self.cardapio.handle_search_result(self, items, text) |
2480 | |
2481 | except KeyError: |
2482 | self.cardapio.handle_search_error(self, "Incorrect YouTube's JSON structure") |
2483 | |
2484 | === modified file 'src/plugins/zg_recent_documents.py' |
2485 | --- src/plugins/zg_recent_documents.py 2010-08-02 07:22:55 +0000 |
2486 | +++ src/plugins/zg_recent_documents.py 2010-08-12 17:20:35 +0000 |
2487 | @@ -21,7 +21,7 @@ |
2488 | help_text = '' |
2489 | version = '0.97b' |
2490 | |
2491 | - plugin_api_version = 1.37 |
2492 | + plugin_api_version = 1.38 |
2493 | |
2494 | search_delay_type = 'local search update delay' |
2495 | |
2496 | @@ -40,8 +40,6 @@ |
2497 | self.c = cardapio_proxy |
2498 | |
2499 | self.loaded = False |
2500 | - self.num_search_results = self.c.settings['search results limit'] |
2501 | - self.num_search_results_long = self.c.settings['long search results limit'] |
2502 | |
2503 | if 'ZeitgeistClient' not in globals(): |
2504 | self.c.write_to_log(self, 'Could not import Zeitgeist', is_error = True) |
2505 | @@ -89,33 +87,26 @@ |
2506 | self.loaded = True |
2507 | |
2508 | |
2509 | - def __del__(self): |
2510 | - |
2511 | - pass |
2512 | - |
2513 | - |
2514 | - def search(self, text, long_search = False): |
2515 | + def search(self, text, result_limit): |
2516 | |
2517 | self.current_query = text |
2518 | |
2519 | text = text.lower() |
2520 | self.search_query = text |
2521 | + # TODO: this is thread unsafe. correct this using a wrapper like |
2522 | + # for example Tomboy's plugin does |
2523 | + self.result_limit = result_limit |
2524 | |
2525 | if text: |
2526 | self.event_template.actor = 'application://' + text + '*' |
2527 | else: |
2528 | self.event_template.actor = '' |
2529 | |
2530 | - if long_search: |
2531 | - num_search_results = self.num_search_results_long |
2532 | - else: |
2533 | - num_search_results = self.num_search_results |
2534 | - |
2535 | self.zg.find_events_for_templates( |
2536 | [self.event_template], |
2537 | self.handle_search_result, |
2538 | timerange = self.time_range, |
2539 | - num_events = num_search_results, |
2540 | + num_events = result_limit, |
2541 | result_type = datamodel.ResultType.MostRecentSubjects |
2542 | ) |
2543 | |
2544 | @@ -133,7 +124,7 @@ |
2545 | fts_results, count = self.fts.Search( |
2546 | self.search_query + '*', |
2547 | self.time_range, |
2548 | - [], 0, self.num_search_results, 2) |
2549 | + [], 0, self.result_limit, 2) |
2550 | |
2551 | except Exception, exception: |
2552 | print exception |
2553 | @@ -148,14 +139,14 @@ |
2554 | |
2555 | for event in all_events: |
2556 | |
2557 | - if len(urls_seen) >= self.num_search_results: break |
2558 | + if len(urls_seen) >= self.result_limit: break |
2559 | |
2560 | for subject in event.get_subjects(): |
2561 | |
2562 | dummy, canonical_path = urllib2.splittype(subject.uri) |
2563 | parent_name, child_name = os.path.split(canonical_path) |
2564 | |
2565 | - if len(urls_seen) >= self.num_search_results: break |
2566 | + if len(urls_seen) >= self.result_limit: break |
2567 | if canonical_path in urls_seen: continue |
2568 | urls_seen.add(canonical_path) |
2569 |
new Amazon plugin, new eBay plugin and a thread-safe version of passing current search string to Cardapio in Bing, Yahoo, Wikipedia and YouTube plugins