Merge lp:~gary-lasker/software-center/launcher-integration-for-p into lp:software-center
- launcher-integration-for-p
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 2575 |
Proposed branch: | lp:~gary-lasker/software-center/launcher-integration-for-p |
Merge into: | lp:software-center |
Diff against target: |
748 lines (+266/-243) 6 files modified
data/ui/gtk3/SoftwareCenter.ui (+20/-4) softwarecenter/backend/unitylauncher.py (+98/-0) softwarecenter/ui/gtk3/app.py (+35/-4) softwarecenter/ui/gtk3/panes/availablepane.py (+91/-2) softwarecenter/ui/gtk3/panes/softwarepane.py (+2/-193) test/gtk3/test_unity_launcher_integration.py (+20/-40) |
To merge this branch: | bzr merge lp:~gary-lasker/software-center/launcher-integration-for-p |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Vogt | Pending | ||
Review via email: mp+83953@code.launchpad.net |
Commit message
Description of the change
This branch implements the Software Center side of mpt's updated design for launcher integration functionality for Precise (https:/
There is also some refactoring to clean things up a bit and to set the stage for an easy update when the full Unity side implementation is ready. There's a new UnityLauncher class and you'll notice that I migrated the UI-side launcher integration code out of SoftwarePane and down into AvailablePane (there's no longer a reason for it to be in the superclass since we no longer use the ChannelPane).
The current implementation in the branch is designed to work using the Unity launcher dbus capabilities as they exist now. As such, it adds the application to the launcher once it has been installed, and does not yet display the install progress in the launcher. That functionality will come after the Unity side implementation is completed.
Finally, the supporting unit tests are updated and working.
Gary Lasker (gary-lasker) wrote : | # |
Preview Diff
1 | === modified file 'data/ui/gtk3/SoftwareCenter.ui' |
2 | --- data/ui/gtk3/SoftwareCenter.ui 2011-11-07 09:41:23 +0000 |
3 | +++ data/ui/gtk3/SoftwareCenter.ui 2011-11-30 14:34:40 +0000 |
4 | @@ -381,7 +381,7 @@ |
5 | <property name="use_underline">True</property> |
6 | <signal name="activate" handler="on_menu_view_activate" swapped="no"/> |
7 | <child type="submenu"> |
8 | - <object class="GtkMenu" id="menu4"> |
9 | + <object class="GtkMenu" id="menu_view"> |
10 | <property name="visible">True</property> |
11 | <property name="can_focus">False</property> |
12 | <child> |
13 | @@ -420,9 +420,9 @@ |
14 | <property name="visible">True</property> |
15 | <property name="can_focus">False</property> |
16 | <property name="related_action">navhistory_back_action</property> |
17 | + <property name="use_action_appearance">False</property> |
18 | <property name="use_underline">True</property> |
19 | <property name="use_stock">False</property> |
20 | - <property name="use_action_appearance">False</property> |
21 | <accelerator key="bracketleft" signal="activate" modifiers="GDK_CONTROL_MASK"/> |
22 | </object> |
23 | </child> |
24 | @@ -432,12 +432,28 @@ |
25 | <property name="visible">True</property> |
26 | <property name="can_focus">False</property> |
27 | <property name="related_action">navhistory_forward_action</property> |
28 | + <property name="use_action_appearance">False</property> |
29 | <property name="use_underline">True</property> |
30 | <property name="use_stock">False</property> |
31 | - <property name="use_action_appearance">False</property> |
32 | <accelerator key="bracketright" signal="activate" modifiers="GDK_CONTROL_MASK"/> |
33 | </object> |
34 | </child> |
35 | + <child> |
36 | + <object class="GtkSeparatorMenuItem" id="add_to_launcher_separator"> |
37 | + <property name="visible">True</property> |
38 | + <property name="can_focus">False</property> |
39 | + </object> |
40 | + </child> |
41 | + <child> |
42 | + <object class="GtkCheckMenuItem" id="menuitem_add_to_launcher"> |
43 | + <property name="visible">True</property> |
44 | + <property name="can_focus">False</property> |
45 | + <property name="use_action_appearance">False</property> |
46 | + <property name="label" translatable="yes">_New Applications in Launcher</property> |
47 | + <property name="use_underline">True</property> |
48 | + <signal name="toggled" handler="on_menuitem_add_to_launcher_toggled" swapped="no"/> |
49 | + </object> |
50 | + </child> |
51 | </object> |
52 | </child> |
53 | </object> |
54 | @@ -476,7 +492,7 @@ |
55 | <property name="visible">True</property> |
56 | <property name="can_focus">False</property> |
57 | <property name="use_action_appearance">False</property> |
58 | - <property name="use_underline">False</property> |
59 | + <property name="use_stock">False</property> |
60 | <signal name="activate" handler="on_menuitem_developer_activate" swapped="no"/> |
61 | </object> |
62 | </child> |
63 | |
64 | === added file 'softwarecenter/backend/unitylauncher.py' |
65 | --- softwarecenter/backend/unitylauncher.py 1970-01-01 00:00:00 +0000 |
66 | +++ softwarecenter/backend/unitylauncher.py 2011-11-30 14:34:40 +0000 |
67 | @@ -0,0 +1,98 @@ |
68 | +# Copyright (C) 2011 Canonical |
69 | +# |
70 | +# Authors: |
71 | +# Gary Lasker |
72 | +# |
73 | +# This program is free software; you can redistribute it and/or modify it under |
74 | +# the terms of the GNU General Public License as published by the Free Software |
75 | +# Foundation; version 3. |
76 | +# |
77 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
78 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
79 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
80 | +# details. |
81 | +# |
82 | +# You should have received a copy of the GNU General Public License along with |
83 | +# this program; if not, write to the Free Software Foundation, Inc., |
84 | +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
85 | + |
86 | +import dbus |
87 | +import logging |
88 | + |
89 | +LOG = logging.getLogger(__name__) |
90 | + |
91 | +class UnityLauncherInfo(object): |
92 | + """ Simple class to keep track of application details needed for |
93 | + Unity launcher integration |
94 | + """ |
95 | + def __init__(self, |
96 | + name, |
97 | + icon_name, |
98 | + icon_file_path, |
99 | + icon_x, |
100 | + icon_y, |
101 | + icon_size, |
102 | + app_install_desktop_file_path, |
103 | + installed_desktop_file_path, |
104 | + trans_id): |
105 | + self.name = name |
106 | + self.icon_name = icon_name |
107 | + self.icon_file_path = icon_file_path |
108 | + self.icon_x = icon_x |
109 | + self.icon_y = icon_y |
110 | + self.icon_size = icon_size |
111 | + self.app_install_desktop_file_path = app_install_desktop_file_path |
112 | + self.installed_desktop_file_path = installed_desktop_file_path |
113 | + self.trans_id = trans_id |
114 | + |
115 | +class UnityLauncher(object): |
116 | + """ Implements the integration between Software Center and the Unity |
117 | + launcher |
118 | + """ |
119 | + def __init__(self): |
120 | + # keep track of applications that are candidates for adding |
121 | + # to the Unity launcher |
122 | + self.launcher_queue = {} |
123 | + |
124 | + def add_to_launcher_queue(self, pkgname, launcher_info): |
125 | + """ add an application and its associated info to the set of candidates |
126 | + for adding to the Unity launcher |
127 | + """ |
128 | + self.launcher_queue[pkgname] = launcher_info |
129 | + |
130 | + def remove_from_launcher_queue(self, pkgname): |
131 | + """ remove an application and its associated info from the set of |
132 | + candidates for adding to the Unity launcher |
133 | + """ |
134 | + if pkgname in self.launcher_queue: |
135 | + return self.launcher_queue.pop(pkgname) |
136 | + |
137 | + def send_application_to_launcher(self, pkgname, launcher_info): |
138 | + LOG.debug("sending dbus signal to Unity launcher for application: ", |
139 | + launcher_info.name) |
140 | + LOG.debug(" launcher_info.icon_file_path: ", |
141 | + launcher_info.icon_file_path) |
142 | + LOG.debug(" launcher_info.installed_desktop_file_path: ", |
143 | + launcher_info.installed_desktop_file_path) |
144 | + LOG.debug(" launcher_info.trans_id: ", launcher_info.trans_id) |
145 | + # the application is being added to the launcher, so clear it from the |
146 | + # list of candidates in the launcher queue |
147 | + self.remove_from_launcher_queue(pkgname) |
148 | + # add the application by sending a dbus signal to the Unity launcher |
149 | + try: |
150 | + bus = dbus.SessionBus() |
151 | + launcher_obj = bus.get_object('com.canonical.Unity.Launcher', |
152 | + '/com/canonical/Unity/Launcher') |
153 | + launcher_iface = dbus.Interface(launcher_obj, |
154 | + 'com.canonical.Unity.Launcher') |
155 | + launcher_iface.AddLauncherItemFromPosition( |
156 | + launcher_info.name, |
157 | + launcher_info.icon_file_path, |
158 | + launcher_info.icon_x, |
159 | + launcher_info.icon_y, |
160 | + launcher_info.icon_size, |
161 | + launcher_info.installed_desktop_file_path, |
162 | + launcher_info.trans_id) |
163 | + except Exception as e: |
164 | + LOG.warn("could not send dbus signal to the Unity launcher: (%s)", |
165 | + e) |
166 | |
167 | === modified file 'softwarecenter/ui/gtk3/app.py' |
168 | --- softwarecenter/ui/gtk3/app.py 2011-11-24 14:48:07 +0000 |
169 | +++ softwarecenter/ui/gtk3/app.py 2011-11-30 14:34:40 +0000 |
170 | @@ -61,7 +61,8 @@ |
171 | MOUSE_EVENT_BACK_BUTTON) |
172 | from softwarecenter.utils import (clear_token_from_ubuntu_sso, |
173 | get_http_proxy_string_from_gsettings, |
174 | - wait_for_apt_cache_ready) |
175 | + wait_for_apt_cache_ready, |
176 | + is_unity_running) |
177 | from softwarecenter.ui.gtk3.utils import (get_sc_icon_theme, |
178 | init_sc_css_provider) |
179 | from softwarecenter.version import VERSION |
180 | @@ -350,7 +351,7 @@ |
181 | supported_menuitem = self.builder.get_object("menuitem_view_supported_only") |
182 | supported_menuitem.set_label(self.distro.get_supported_filter_name()) |
183 | file_menu = self.builder.get_object("menu1") |
184 | - |
185 | + |
186 | if not self.distro.DEVELOPER_URL: |
187 | help_menu = self.builder.get_object("menu_help") |
188 | developer_separator = self.builder.get_object("separator_developer") |
189 | @@ -362,6 +363,20 @@ |
190 | och = is_oneconf_available() |
191 | if not och: |
192 | file_menu.remove(self.builder.get_object("menuitem_sync_between_computers")) |
193 | + |
194 | + # restore the state of the add to launcher menu item, or remove the menu |
195 | + # item if Unity is not currently running |
196 | + add_to_launcher_menuitem = self.builder.get_object( |
197 | + "menuitem_add_to_launcher") |
198 | + if is_unity_running(): |
199 | + add_to_launcher_menuitem.set_active( |
200 | + self.available_pane.add_to_launcher_enabled) |
201 | + else: |
202 | + view_menu = self.builder.get_object("menu_view") |
203 | + add_to_launcher_separator = self.builder.get_object( |
204 | + "add_to_launcher_separator") |
205 | + view_menu.remove(add_to_launcher_separator) |
206 | + view_menu.remove(add_to_launcher_menuitem) |
207 | |
208 | # run s-c-agent update |
209 | if options.disable_buy or not self.distro.PURCHASE_APP_URL: |
210 | @@ -963,6 +978,9 @@ |
211 | def on_navhistory_forward_action_activate(self, navhistory_forward_action=None): |
212 | vm = get_viewmanager() |
213 | vm.nav_forward() |
214 | + |
215 | + def on_menuitem_add_to_launcher_toggled(self, menu_item): |
216 | + self.available_pane.add_to_launcher_enabled = menu_item.get_active() |
217 | |
218 | # Help Menu |
219 | def on_menuitem_about_activate(self, widget): |
220 | @@ -1154,11 +1172,20 @@ |
221 | # in case of a crazy-huge monitor) |
222 | screen_height = Gdk.Screen.height() |
223 | screen_width = Gdk.Screen.width() |
224 | - self.window_main.set_default_size(min(int(.85 * screen_width), 1200), |
225 | - min(int(.85 * screen_height), 800)) |
226 | + self.window_main.set_default_size( |
227 | + min(int(.85 * screen_width), 1200), |
228 | + min(int(.85 * screen_height), 800)) |
229 | if (self.config.has_option("general", "maximized") and |
230 | self.config.getboolean("general", "maximized")): |
231 | self.window_main.maximize() |
232 | + if self.config.has_option("general", "add_to_launcher"): |
233 | + self.available_pane.add_to_launcher_enabled = ( |
234 | + self.config.getboolean( |
235 | + "general", |
236 | + "add_to_launcher")) |
237 | + else: |
238 | + # initial default state is to add to launcher, per spec |
239 | + self.available_pane.add_to_launcher_enabled = True |
240 | |
241 | def save_state(self): |
242 | LOG.debug("save_state") |
243 | @@ -1176,6 +1203,10 @@ |
244 | # size only matters when non-maximized |
245 | size = self.window_main.get_size() |
246 | self.config.set("general","size", "%s, %s" % (size[0], size[1])) |
247 | + if self.available_pane.add_to_launcher_enabled: |
248 | + self.config.set("general", "add_to_launcher", "True") |
249 | + else: |
250 | + self.config.set("general", "add_to_launcher", "False") |
251 | self.config.write() |
252 | |
253 | def run(self, args): |
254 | |
255 | === modified file 'softwarecenter/ui/gtk3/panes/availablepane.py' |
256 | --- softwarecenter/ui/gtk3/panes/availablepane.py 2011-11-17 00:28:02 +0000 |
257 | +++ softwarecenter/ui/gtk3/panes/availablepane.py 2011-11-30 14:34:40 +0000 |
258 | @@ -21,7 +21,9 @@ |
259 | from gi.repository import Gtk |
260 | import logging |
261 | import xapian |
262 | +import os |
263 | |
264 | +import softwarecenter.utils |
265 | import softwarecenter.ui.gtk3.dialogs as dialogs |
266 | from softwarecenter.ui.gtk3.models.appstore2 import AppListStore |
267 | |
268 | @@ -30,9 +32,13 @@ |
269 | from softwarecenter.enums import (ActionButtons, |
270 | NavButtons, |
271 | NonAppVisibility, |
272 | - DEFAULT_SEARCH_LIMIT) |
273 | + DEFAULT_SEARCH_LIMIT, |
274 | + TransactionTypes) |
275 | from softwarecenter.paths import APP_INSTALL_PATH |
276 | -from softwarecenter.utils import wait_for_apt_cache_ready |
277 | +from softwarecenter.utils import (wait_for_apt_cache_ready, |
278 | + is_no_display_desktop_file, |
279 | + convert_desktop_file_to_installed_location, |
280 | + get_file_path_from_iconname) |
281 | from softwarecenter.db.appfilter import AppFilter |
282 | from softwarecenter.db.database import Application |
283 | from softwarecenter.ui.gtk3.views.purchaseview import PurchaseView |
284 | @@ -42,6 +48,8 @@ |
285 | from softwarepane import SoftwarePane |
286 | from softwarecenter.ui.gtk3.session.viewmanager import get_viewmanager |
287 | from softwarecenter.ui.gtk3.session.appmanager import get_appmanager |
288 | +from softwarecenter.backend.unitylauncher import (UnityLauncher, |
289 | + UnityLauncherInfo) |
290 | |
291 | LOG = logging.getLogger(__name__) |
292 | |
293 | @@ -91,6 +99,14 @@ |
294 | # views to be created in init_view |
295 | self.cat_view = None |
296 | self.subcategories_view = None |
297 | + |
298 | + # integrate with the Unity launcher |
299 | + self.unity_launcher = UnityLauncher() |
300 | + |
301 | + # flag to indicate whether applications should be added to the |
302 | + # unity launcher when installed (this value is initialized by |
303 | + # the config load in app.py) |
304 | + self.add_to_launcher_enabled = True |
305 | |
306 | def init_view(self): |
307 | if self.view_initialized: |
308 | @@ -179,6 +195,10 @@ |
309 | |
310 | # install backend |
311 | self.backend.connect("transactions-changed", self._on_transactions_changed) |
312 | + self.backend.connect("transaction-started", self.on_transaction_started) |
313 | + self.backend.connect("transaction-finished", self.on_transaction_finished) |
314 | + self.backend.connect("transaction-stopped", self.on_transaction_stopped) |
315 | + |
316 | # now we are initialized |
317 | self.searchentry.set_sensitive(True) |
318 | self.emit("available-pane-created") |
319 | @@ -322,6 +342,75 @@ |
320 | """ |
321 | if self._is_custom_list_search(self.state.search_term): |
322 | self._update_action_bar() |
323 | + |
324 | + def on_transaction_started(self, backend, pkgname, appname, trans_id, |
325 | + trans_type): |
326 | + self._register_unity_launcher_transaction_started( |
327 | + backend, pkgname, appname, trans_id, trans_type) |
328 | + |
329 | + def _register_unity_launcher_transaction_started(self, backend, pkgname, |
330 | + appname, trans_id, |
331 | + trans_type): |
332 | + if not self.add_to_launcher_enabled: |
333 | + return |
334 | + # mvo: use use softwarecenter.utils explictly so that we can monkey |
335 | + # patch it in the test |
336 | + if not softwarecenter.utils.is_unity_running(): |
337 | + return |
338 | + # we only care about getting the launcher information on an install |
339 | + if not trans_type == TransactionTypes.INSTALL: |
340 | + return |
341 | + # gather details for this transaction and create the launcher_info object |
342 | + app = Application(pkgname=pkgname, appname=appname) |
343 | + appdetails = app.get_details(self.db) |
344 | + # we only add items to the launcher that have a desktop file |
345 | + if not appdetails.desktop_file: |
346 | + return |
347 | + # do not add apps without a exec line (like wine, see #848437) |
348 | + if (os.path.exists(appdetails.desktop_file) and |
349 | + is_no_display_desktop_file(appdetails.desktop_file)): |
350 | + return |
351 | + |
352 | + (icon_size, icon_x, icon_y) = self._get_onscreen_icon_details_for_launcher_service(app) |
353 | + launcher_info = UnityLauncherInfo(app.name, |
354 | + appdetails.icon, |
355 | + "", # we set the icon_file_path value *after* install |
356 | + icon_x, |
357 | + icon_y, |
358 | + icon_size, |
359 | + appdetails.desktop_file, |
360 | + "", # we set the installed_desktop_file_path *after* install |
361 | + trans_id) |
362 | + self.unity_launcher.add_to_launcher_queue(app.pkgname, launcher_info) |
363 | + |
364 | + def _get_onscreen_icon_details_for_launcher_service(self, app): |
365 | + if self.is_app_details_view_showing(): |
366 | + return self.app_details_view.get_app_icon_details() |
367 | + else: |
368 | + # TODO: implement the app list view case once it has been specified |
369 | + return (0, 0, 0) |
370 | + |
371 | + def on_transaction_finished(self, backend, result): |
372 | + self._check_unity_launcher_transaction_finished(result) |
373 | + |
374 | + def _check_unity_launcher_transaction_finished(self, result): |
375 | + # add the completed transaction details to the corresponding |
376 | + # launcher_item |
377 | + if result.pkgname in self.unity_launcher.launcher_queue: |
378 | + launcher_info = self.unity_launcher.launcher_queue[result.pkgname] |
379 | + launcher_info.icon_file_path = get_file_path_from_iconname( |
380 | + self.icons, launcher_info.icon_name) |
381 | + installed_path = convert_desktop_file_to_installed_location( |
382 | + launcher_info.app_install_desktop_file_path, result.pkgname) |
383 | + launcher_info.installed_desktop_file_path = installed_path |
384 | + if result.success: |
385 | + self.unity_launcher.send_application_to_launcher( |
386 | + result.pkgname, launcher_info) |
387 | + else: |
388 | + self.unity_launcher.remove_from_launcher_queue(result.pkgname) |
389 | + |
390 | + def on_transaction_stopped(self, backend, result): |
391 | + self.unity_launcher.remove_from_launcher_queue(result.pkgname) |
392 | |
393 | def on_app_list_changed(self, pane, length): |
394 | """internal helper that keeps the status text and the action |
395 | |
396 | === modified file 'softwarecenter/ui/gtk3/panes/softwarepane.py' |
397 | --- softwarecenter/ui/gtk3/panes/softwarepane.py 2011-10-25 18:38:08 +0000 |
398 | +++ softwarecenter/ui/gtk3/panes/softwarepane.py 2011-11-30 14:34:40 +0000 |
399 | @@ -17,7 +17,6 @@ |
400 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
401 | |
402 | from gi.repository import Atk |
403 | -import dbus |
404 | import gettext |
405 | from gi.repository import GObject |
406 | from gi.repository import Gtk, Gdk |
407 | @@ -26,21 +25,14 @@ |
408 | import os |
409 | import xapian |
410 | |
411 | -from gettext import gettext as _ |
412 | - |
413 | -import softwarecenter.utils |
414 | from softwarecenter.backend import get_install_backend |
415 | from softwarecenter.db.database import Application |
416 | from softwarecenter.db.enquire import AppEnquire |
417 | -from softwarecenter.enums import (ActionButtons, |
418 | - SortMethods, |
419 | - TransactionTypes, |
420 | +from softwarecenter.enums import (SortMethods, |
421 | DEFAULT_SEARCH_LIMIT, |
422 | NonAppVisibility) |
423 | |
424 | from softwarecenter.utils import (ExecutionTime, |
425 | - convert_desktop_file_to_installed_location, |
426 | - get_file_path_from_iconname, |
427 | wait_for_apt_cache_ready, |
428 | utf8 |
429 | ) |
430 | @@ -55,39 +47,10 @@ |
431 | AppDetailsViewGtk as |
432 | AppDetailsView) |
433 | |
434 | -from softwarecenter.utils import is_no_display_desktop_file |
435 | - |
436 | from basepane import BasePane |
437 | |
438 | LOG = logging.getLogger(__name__) |
439 | |
440 | - |
441 | -class UnityLauncherInfo(object): |
442 | - """ Simple class to keep track of application details needed for |
443 | - Unity launcher integration |
444 | - """ |
445 | - def __init__(self, |
446 | - name, |
447 | - icon_name, |
448 | - icon_file_path, |
449 | - icon_x, |
450 | - icon_y, |
451 | - icon_size, |
452 | - app_install_desktop_file_path, |
453 | - installed_desktop_file_path, |
454 | - trans_id): |
455 | - self.name = name |
456 | - self.icon_name = icon_name |
457 | - self.icon_file_path = icon_file_path |
458 | - self.icon_x = icon_x |
459 | - self.icon_y = icon_y |
460 | - self.icon_size = icon_size |
461 | - self.app_install_desktop_file_path = app_install_desktop_file_path |
462 | - self.installed_desktop_file_path = installed_desktop_file_path |
463 | - self.trans_id = trans_id |
464 | - self.add_to_launcher_requested = False |
465 | - |
466 | - |
467 | # for DisplayState attribute type-checking |
468 | from softwarecenter.db.categories import Category |
469 | from softwarecenter.backend.channel import SoftwareChannel |
470 | @@ -202,9 +165,6 @@ |
471 | # request (e.g. people click on ubuntu channel, get impatient, click |
472 | # on partner channel) |
473 | self.refresh_seq_nr = 0 |
474 | - # keep track of applications that are candidates to be added |
475 | - # to the Unity launcher |
476 | - self.unity_launcher_items = {} |
477 | # this should be initialized |
478 | self.apps_search_term = "" |
479 | # Create the basic frame for the common view |
480 | @@ -282,14 +242,9 @@ |
481 | # when the cache changes, refresh the app list |
482 | self.cache.connect("cache-ready", self.on_cache_ready) |
483 | |
484 | - # aptdaemon |
485 | - self.backend.connect("transaction-started", self.on_transaction_started) |
486 | - self.backend.connect("transaction-finished", self.on_transaction_finished) |
487 | - self.backend.connect("transaction-stopped", self.on_transaction_stopped) |
488 | - |
489 | # connect signals |
490 | self.connect("app-list-changed", self.on_app_list_changed) |
491 | - |
492 | + |
493 | # db reopen |
494 | if self.db: |
495 | self.db.connect("reopen", self.on_db_reopen) |
496 | @@ -335,81 +290,6 @@ |
497 | vm = get_viewmanager() |
498 | vm.nav_forward() |
499 | |
500 | - def on_transaction_started(self, backend, pkgname, appname, trans_id, |
501 | - trans_type): |
502 | - self._register_unity_launcher_transaction_started( |
503 | - backend, pkgname, appname, trans_id, trans_type) |
504 | - |
505 | - |
506 | - def _get_onscreen_icon_details_for_launcher_service(self, app): |
507 | - if self.is_app_details_view_showing(): |
508 | - return self.app_details_view.get_app_icon_details() |
509 | - else: |
510 | - # TODO: implement the app list view case once it has been specified |
511 | - return (0, 0, 0) |
512 | - |
513 | - def _register_unity_launcher_transaction_started(self, backend, pkgname, |
514 | - appname, trans_id, |
515 | - trans_type): |
516 | - # mvo: use use softwarecenter.utils explictely so that we can monkey |
517 | - # patch it in the test |
518 | - if not softwarecenter.utils.is_unity_running(): |
519 | - return |
520 | - # add to launcher only applies in the details view currently |
521 | - if not self.is_app_details_view_showing(): |
522 | - return |
523 | - # we only care about getting the launcher information on an install |
524 | - if not trans_type == TransactionTypes.INSTALL: |
525 | - if pkgname in self.unity_launcher_items: |
526 | - self.unity_launcher_items.pop(pkgname) |
527 | - self.action_bar.clear() |
528 | - return |
529 | - # gather details for this transaction and create the launcher_info object |
530 | - app = Application(pkgname=pkgname, appname=appname) |
531 | - appdetails = app.get_details(self.db) |
532 | - (icon_size, icon_x, icon_y) = self._get_onscreen_icon_details_for_launcher_service(app) |
533 | - launcher_info = UnityLauncherInfo(app.name, |
534 | - appdetails.icon, |
535 | - "", # we set the icon_file_path value *after* install |
536 | - icon_x, |
537 | - icon_y, |
538 | - icon_size, |
539 | - appdetails.desktop_file, |
540 | - "", # we set the installed_desktop_file_path *after* install |
541 | - trans_id) |
542 | - self.unity_launcher_items[app.pkgname] = launcher_info |
543 | - self.show_add_to_launcher_panel(backend, pkgname, appname, app, appdetails, trans_id, trans_type) |
544 | - |
545 | - def show_add_to_launcher_panel(self, backend, pkgname, appname, app, appdetails, trans_id, trans_type): |
546 | - """ |
547 | - if Unity is currently running, display a panel to allow the user |
548 | - the choose whether to add a newly-installed application to the |
549 | - launcher |
550 | - """ |
551 | - # TODO: handle local deb install case |
552 | - # TODO: implement the list view case (once it is specified) |
553 | - # only show the panel if unity is running and this is a package install |
554 | - # |
555 | - # we only show the prompt for apps with a desktop file |
556 | - if not appdetails.desktop_file: |
557 | - return |
558 | - # do not add apps without a exec line (like wine, see #848437) |
559 | - if (os.path.exists(appdetails.desktop_file) and |
560 | - is_no_display_desktop_file(appdetails.desktop_file)): |
561 | - return |
562 | - self.action_bar.add_button(ActionButtons.CANCEL_ADD_TO_LAUNCHER, |
563 | - _("Not Now"), |
564 | - self.on_cancel_add_to_launcher, |
565 | - pkgname) |
566 | - self.action_bar.add_button(ActionButtons.ADD_TO_LAUNCHER, |
567 | - _("Add to Launcher"), |
568 | - self.on_add_to_launcher, |
569 | - pkgname, |
570 | - app, |
571 | - appdetails, |
572 | - trans_id) |
573 | - self.action_bar.set_label(utf8(_("Add %s to the launcher?")) % utf8(app.name)) |
574 | - |
575 | def on_query_complete(self, enquirer): |
576 | self.emit("app-list-changed", len(enquirer.matches)) |
577 | self.app_view.display_matches(enquirer.matches, |
578 | @@ -427,81 +307,10 @@ |
579 | self._refresh_apps_with_apt_cache(query) |
580 | return |
581 | |
582 | - def on_add_to_launcher(self, pkgname, app, appdetails, trans_id): |
583 | - """ |
584 | - callback indicating the user has chosen to add the indicated application |
585 | - to the launcher |
586 | - """ |
587 | - if pkgname in self.unity_launcher_items: |
588 | - launcher_info = self.unity_launcher_items[pkgname] |
589 | - if launcher_info.installed_desktop_file_path: |
590 | - # package install is complete, we can add to the launcher immediately |
591 | - self.unity_launcher_items.pop(pkgname) |
592 | - self.action_bar.clear() |
593 | - self._send_dbus_signal_to_unity_launcher(launcher_info) |
594 | - else: |
595 | - # package is not yet installed, it will be added to the launcher |
596 | - # once the installation is complete |
597 | - LOG.debug("the application '%s' will be added to the Unity launcher when installation is complete" % app.name) |
598 | - launcher_info.add_to_launcher_requested = True |
599 | - self.action_bar.set_label(_("%s will be added to the launcher when installation completes.") % app.name) |
600 | - self.action_bar.remove_button(ActionButtons.CANCEL_ADD_TO_LAUNCHER) |
601 | - self.action_bar.remove_button(ActionButtons.ADD_TO_LAUNCHER) |
602 | - |
603 | - def on_cancel_add_to_launcher(self, pkgname): |
604 | - if pkgname in self.unity_launcher_items: |
605 | - self.unity_launcher_items.pop(pkgname) |
606 | - self.action_bar.clear() |
607 | - |
608 | - def on_transaction_finished(self, backend, result): |
609 | - self._check_unity_launcher_transaction_finished(result) |
610 | - |
611 | def _is_in_search_mode(self): |
612 | return (self.state.search_term and |
613 | len(self.state.search_term) >= 2) |
614 | |
615 | - def _check_unity_launcher_transaction_finished(self, result): |
616 | - # add the completed transaction details to the corresponding |
617 | - # launcher_item |
618 | - if result.pkgname in self.unity_launcher_items: |
619 | - launcher_info = self.unity_launcher_items[result.pkgname] |
620 | - launcher_info.icon_file_path = get_file_path_from_iconname( |
621 | - self.icons, launcher_info.icon_name) |
622 | - installed_path = convert_desktop_file_to_installed_location( |
623 | - launcher_info.app_install_desktop_file_path, result.pkgname) |
624 | - launcher_info.installed_desktop_file_path = installed_path |
625 | - # if the request to add to launcher has already been made, do it now |
626 | - if launcher_info.add_to_launcher_requested: |
627 | - if result.success: |
628 | - self._send_dbus_signal_to_unity_launcher(launcher_info) |
629 | - self.unity_launcher_items.pop(result.pkgname) |
630 | - self.action_bar.clear() |
631 | - |
632 | - def _send_dbus_signal_to_unity_launcher(self, launcher_info): |
633 | - LOG.debug("sending dbus signal to Unity launcher for application: ", launcher_info.name) |
634 | - LOG.debug(" launcher_info.icon_file_path: ", launcher_info.icon_file_path) |
635 | - LOG.debug(" launcher_info.installed_desktop_file_path: ", launcher_info.installed_desktop_file_path) |
636 | - LOG.debug(" launcher_info.trans_id: ", launcher_info.trans_id) |
637 | - try: |
638 | - bus = dbus.SessionBus() |
639 | - launcher_obj = bus.get_object('com.canonical.Unity.Launcher', |
640 | - '/com/canonical/Unity/Launcher') |
641 | - launcher_iface = dbus.Interface(launcher_obj, 'com.canonical.Unity.Launcher') |
642 | - launcher_iface.AddLauncherItemFromPosition(launcher_info.name, |
643 | - launcher_info.icon_file_path, |
644 | - launcher_info.icon_x, |
645 | - launcher_info.icon_y, |
646 | - launcher_info.icon_size, |
647 | - launcher_info.installed_desktop_file_path, |
648 | - launcher_info.trans_id) |
649 | - except Exception as e: |
650 | - LOG.warn("could not send dbus signal to the Unity launcher: (%s)", e) |
651 | - |
652 | - def on_transaction_stopped(self, backend, result): |
653 | - if result.pkgname in self.unity_launcher_items: |
654 | - self.unity_launcher_items.pop(result.pkgname) |
655 | - self.action_bar.clear() |
656 | - |
657 | def show_appview_spinner(self): |
658 | """ display the spinner in the appview panel """ |
659 | if not self.state.search_term: |
660 | |
661 | === modified file 'test/gtk3/test_unity_launcher_integration.py' |
662 | --- test/gtk3/test_unity_launcher_integration.py 2011-11-17 03:17:37 +0000 |
663 | +++ test/gtk3/test_unity_launcher_integration.py 2011-11-30 14:34:40 +0000 |
664 | @@ -54,36 +54,10 @@ |
665 | TransactionTypes.INSTALL) |
666 | # wait a wee bit |
667 | self._zzz() |
668 | - |
669 | - def test_unity_launcher_stays_after_install_finished(self): |
670 | - test_pkgname = "gl-117" |
671 | - mock_result = Mock() |
672 | - mock_result.pkgname = test_pkgname |
673 | - mock_result.success = True |
674 | - # now pretend |
675 | - # now pretend |
676 | - self._navigate_to_appdetails_and_install(test_pkgname) |
677 | - # pretend we are done |
678 | - available_pane.backend.emit("transaction-finished", mock_result) |
679 | - # this is normally set in the transaction-finished call but our |
680 | - # app is not really installed so we need to mock it here |
681 | - available_pane.unity_launcher_items[test_pkgname].installed_desktop_file_path = "/some/path" |
682 | - # wait a wee bit |
683 | - self._zzz() |
684 | - # ensure we still have the button |
685 | - button = available_pane.action_bar.get_button( |
686 | - ActionButtons.ADD_TO_LAUNCHER) |
687 | - self.assertNotEqual(button, None) |
688 | - self.assertTrue(button.get_property("visible")) |
689 | - # now click it even though the transaction is over |
690 | - button.clicked() |
691 | - self._zzz() |
692 | - # ensure the add to launcher button is now hidden |
693 | - button = available_pane.action_bar.get_button( |
694 | - ActionButtons.ADD_TO_LAUNCHER) |
695 | - self.assertEqual(button, None) |
696 | |
697 | def test_unity_launcher_integration(self): |
698 | + # test the automatic add to launcher enabled functionality |
699 | + available_pane.add_to_launcher_enabled = True |
700 | test_pkgname = "lincity-ng" |
701 | mock_result = Mock() |
702 | mock_result.pkgname = test_pkgname |
703 | @@ -91,18 +65,10 @@ |
704 | # now pretend |
705 | self._navigate_to_appdetails_and_install(test_pkgname) |
706 | |
707 | - # verify that the panel is shown offering to add the app to the launcher |
708 | - self.assertTrue(available_pane.action_bar.get_property("visible")) |
709 | - button = available_pane.action_bar.get_button( |
710 | - ActionButtons.ADD_TO_LAUNCHER) |
711 | - self.assertTrue(button is not None) |
712 | - # click the button |
713 | - button.clicked() |
714 | - |
715 | # check that a correct UnityLauncherInfo object has been created and |
716 | # added to the queue |
717 | - self.assertTrue(test_pkgname in available_pane.unity_launcher_items) |
718 | - launcher_info = available_pane.unity_launcher_items.pop(test_pkgname) |
719 | + self.assertTrue(test_pkgname in available_pane.unity_launcher.launcher_queue) |
720 | + launcher_info = available_pane.unity_launcher.remove_from_launcher_queue(test_pkgname) |
721 | # check the UnityLauncherInfo values themselves |
722 | self.assertEqual(launcher_info.name, "lincity-ng") |
723 | self.assertEqual(launcher_info.icon_name, "lincity-ng") |
724 | @@ -114,8 +80,22 @@ |
725 | self.assertEqual(launcher_info.trans_id, "testid101") |
726 | # finally, make sure the the app has been removed from the launcher |
727 | # queue |
728 | - self.assertFalse(test_pkgname in available_pane.unity_launcher_items) |
729 | - |
730 | + self.assertFalse(test_pkgname in available_pane.unity_launcher.launcher_queue) |
731 | + |
732 | + def test_unity_launcher_integration_disabled(self): |
733 | + # test the case where automatic add to launcher is disabled |
734 | + available_pane.add_to_launcher_enabled = False |
735 | + test_pkgname = "lincity-ng" |
736 | + mock_result = Mock() |
737 | + mock_result.pkgname = test_pkgname |
738 | + mock_result.success = True |
739 | + # now pretend |
740 | + self._navigate_to_appdetails_and_install(test_pkgname) |
741 | + |
742 | + # check that no corresponding unity_launcher info object has been added |
743 | + # to the queue |
744 | + self.assertFalse(test_pkgname in available_pane.unity_launcher.launcher_queue) |
745 | + |
746 | def test_desktop_file_path_conversion(self): |
747 | # test 'normal' case |
748 | app_install_desktop_path = ("./data/app-install/desktop/" + |
Btw, the code in availablepane simplifies *a lot* once we get the full Unity implementation. Most of that code is just to queue up simultaneous installs to be able to do the desktop file and icon location updates as required for the current Unity dbus implementation.