Merge lp:~gary-lasker/software-center/launcher-integration-for-p into lp:software-center

Proposed by Gary Lasker on 2011-11-30
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
Reviewer Review Type Date Requested Status
Michael Vogt 2011-11-30 Pending
Review via email: mp+83953@code.launchpad.net

Description of the change

This branch implements the Software Center side of mpt's updated design for launcher integration functionality for Precise (https://wiki.ubuntu.com/SoftwareCenter#Learning_how_to_launch_an_application). It removes the explicit slide-in prompt in the details view for adding an item to the launcher, and instead always adds the item (or not) depending on the state of the new menu item "View->Add Applications in Launcher". A nice side-effect of the new design is that it now supports adding items to the launcher when they are installed from the details view.

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.

To post a comment you must log in.
Gary Lasker (gary-lasker) wrote :

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.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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/" +

Subscribers

People subscribed via source and target branches