~ubuntu-branches/ubuntu/raring/software-center/raring-proposed

« back to all changes in this revision

Viewing changes to softwarecenter/ui/gtk3/app.py

  • Committer: Package Import Robot
  • Author(s): Michael Vogt
  • Date: 2012-10-11 15:33:05 UTC
  • mfrom: (195.1.18 quantal)
  • Revision ID: package-import@ubuntu.com-20121011153305-fm5ln7if3rpzts4n
Tags: 5.4.1.1
* lp:~mvo/software-center/reinstall-previous-purchase-token-fix:
  - fix reinstall previous purchases that have a system-wide
    license key LP: #1065481
* lp:~mvo/software-center/lp1060106:
  - Add missing gettext init for utils/update-software-center-agent
    (LP: #1060106)

Show diffs side-by-side

added added

removed removed

Lines of Context:
47
47
# make pyflakes shut up
48
48
softwarecenter.netstatus.NETWORK_STATE
49
49
 
 
50
from softwarecenter.backend.ubuntusso import UbuntuSSO
 
51
 
50
52
# db imports
51
53
from softwarecenter.db.application import Application
52
 
from softwarecenter.db import DebFileApplication, DebFileOpenError
 
54
from softwarecenter.db import (
 
55
    DebFileApplication,
 
56
    DebFileOpenError,
 
57
    )
 
58
from softwarecenter.db.utils import run_software_center_agent
53
59
from softwarecenter.i18n import init_locale
54
60
 
55
61
# misc imports
80
86
    init_sc_css_provider,
81
87
)
82
88
from softwarecenter.version import VERSION
83
 
from softwarecenter.db.database import StoreDatabase
 
89
from softwarecenter.db.database import (
 
90
    StoreDatabase,
 
91
    get_reinstall_previous_purchases_query,
 
92
    )
84
93
try:
85
94
    from aptd_gtk3 import InstallBackendUI
86
95
    InstallBackendUI  # pyflakes
92
101
import softwarecenter.ui.gtk3.dialogs as dialogs
93
102
 
94
103
from softwarecenter.ui.gtk3.SimpleGtkbuilderApp import SimpleGtkbuilderApp
95
 
from softwarecenter.ui.gtk3.panes.installedpane import InstalledPane
96
 
from softwarecenter.ui.gtk3.panes.availablepane import AvailablePane
 
104
with ExecutionTime("import InstalledPane"):
 
105
    from softwarecenter.ui.gtk3.panes.installedpane import InstalledPane
 
106
with ExecutionTime("import AvailablePane"):
 
107
    from softwarecenter.ui.gtk3.panes.availablepane import AvailablePane
97
108
from softwarecenter.ui.gtk3.panes.historypane import HistoryPane
98
109
from softwarecenter.ui.gtk3.panes.globalpane import GlobalPane
99
110
from softwarecenter.ui.gtk3.panes.pendingpane import PendingPane
109
120
    RecommendationsOptInDialog)
110
121
 
111
122
from softwarecenter.config import get_config
112
 
from softwarecenter.backend import get_install_backend
113
 
from softwarecenter.backend.login_sso import get_sso_backend
 
123
from softwarecenter.backend.installbackend import get_install_backend
 
124
from softwarecenter.backend.login import get_login_backend
114
125
from softwarecenter.backend.recagent import RecommenderAgent
115
126
 
116
127
from softwarecenter.backend.channel import AllInstalledChannel
217
228
        """ stop the dbus controller and remove from the bus """
218
229
        self.remove_from_connection()
219
230
 
220
 
    @dbus.service.method('com.ubuntu.SoftwarecenterIFace')
 
231
    @dbus.service.method('com.ubuntu.SoftwarecenterIFace', utf8_strings=True)
221
232
    def bringToFront(self, args):
222
233
        if args != 'nothing-to-show':
223
234
            self.parent.show_available_packages(args)
246
257
 
247
258
    START_DBUS = True
248
259
 
249
 
    def __init__(self, datadir, xapian_base_path, options, args=None):
 
260
    def __init__(self, options, args=None):
250
261
        self.dbusControler = None
251
262
        if self.START_DBUS:
252
263
            # setup dbus and exit if there is another instance already running
253
264
            self.setup_dbus_or_bring_other_instance_to_front(args)
254
265
 
255
 
        self.datadir = datadir
 
266
        self.datadir = softwarecenter.paths.datadir
256
267
        super(SoftwareCenterAppGtk3, self).__init__(
257
 
                                     datadir + "/ui/gtk3/SoftwareCenter.ui",
258
 
                                     "software-center")
 
268
            os.path.join(self.datadir, "ui", "gtk3", "SoftwareCenter.ui"),
 
269
            "software-center")
259
270
        gettext.bindtextdomain("software-center", "/usr/share/locale")
260
271
        gettext.textdomain("software-center")
261
272
 
280
291
            # cache is opened later in run()
281
292
            self.cache.connect("cache-broken", self._on_apt_cache_broken)
282
293
 
 
294
        xapian_base_path = softwarecenter.paths.XAPIAN_BASE_PATH
283
295
        with ExecutionTime("opening the xapiandb"):
284
296
            pathname = os.path.join(xapian_base_path, "xapian")
285
297
            self._use_axi = not options.disable_apt_xapian_index
303
315
                dialogs.error(None,
304
316
                              _("Sorry, can not open the software database"),
305
317
                              _("Please re-install the 'software-center' "
306
 
                                "package."))
 
318
                               "package."))
307
319
                # FIXME: force rebuild by providing a dbus service for this
308
320
                sys.exit(1)
309
321
 
310
322
        # additional icons come from app-install-data
311
323
        with ExecutionTime("building the icon cache"):
312
 
            self.icons = get_sc_icon_theme(self.datadir)
 
324
            self.icons = get_sc_icon_theme()
313
325
 
314
326
        # backend
315
327
        with ExecutionTime("creating the backend"):
330
342
        # for use when viewing previous purchases
331
343
        self.scagent = None
332
344
        self.sso = None
333
 
        self.available_for_me_query = None
 
345
        self.available_for_me_query = get_reinstall_previous_purchases_query()
334
346
 
335
347
        Gtk.Window.set_default_icon_name("softwarecenter")
336
348
 
341
353
        # initial css loading
342
354
        init_sc_css_provider(self.window_main,
343
355
                             settings,
344
 
                             Gdk.Screen.get_default(),
345
 
                             datadir)
 
356
                             Gdk.Screen.get_default())
346
357
 
347
358
        # wire up the css provider to reconfigure on theme-changes
348
359
        self.window_main.connect("style-updated",
349
360
                                 self._on_style_updated,
350
361
                                 init_sc_css_provider,
351
362
                                 settings,
352
 
                                 Gdk.Screen.get_default(),
353
 
                                 datadir)
 
363
                                 Gdk.Screen.get_default())
 
364
        # workaround broken engines (LP: #1021308)
 
365
        self.window_main.emit("style-updated")
354
366
 
355
367
        # register view manager and create view panes/widgets
356
368
        self.view_manager = ViewManager(self.notebook_view, options)
357
369
 
358
370
        with ExecutionTime("building panes"):
359
 
            self.global_pane = GlobalPane(self.view_manager, self.datadir,
360
 
                self.db, self.cache, self.icons)
 
371
            self.global_pane = GlobalPane(self.view_manager, self.db,
 
372
                                          self.cache, self.icons)
361
373
            self.vbox1.pack_start(self.global_pane, False, False, 0)
362
374
            self.vbox1.reorder_child(self.global_pane, 1)
363
375
 
370
382
                                                self.db,
371
383
                                                self.distro,
372
384
                                                self.icons,
373
 
                                                self.datadir,
374
385
                                                self.navhistory_back_action,
375
386
                                                self.navhistory_forward_action)
376
387
            self.available_pane.connect("available-pane-created",
382
393
            self.installed_pane = InstalledPane(self.cache,
383
394
                                                self.db,
384
395
                                                self.distro,
385
 
                                                self.icons,
386
 
                                                self.datadir)
 
396
                                                self.icons)
387
397
            self.installed_pane.connect("installed-pane-created",
388
398
                self.on_installed_pane_created)
389
399
            self.view_manager.register(self.installed_pane,
393
403
            self.history_pane = HistoryPane(self.cache,
394
404
                                            self.db,
395
405
                                            self.distro,
396
 
                                            self.icons,
397
 
                                            self.datadir)
 
406
                                            self.icons)
398
407
            self.view_manager.register(self.history_pane, ViewPages.HISTORY)
399
408
 
400
409
            # pending pane
412
421
        # reviews
413
422
        with ExecutionTime("create review loader"):
414
423
            self.review_loader = get_review_loader(self.cache, self.db)
415
 
            # FIXME: add some kind of throttle, I-M-S here
416
 
            self.review_loader.refresh_review_stats(
417
 
                self.on_review_stats_loaded)
 
424
            self.review_loader.connect(
 
425
                "refresh-review-stats-finished", self.on_review_stats_loaded)
 
426
            self.review_loader.refresh_review_stats()
418
427
            #load usefulness votes from server when app starts
419
428
            self.useful_cache = UsefulnessCache(True)
420
429
            self.setup_database_rebuilding_listener()
459
468
        # menu item if Unity is not currently running
460
469
        if is_unity_running():
461
470
            self.menuitem_add_to_launcher.set_active(
462
 
                                self.available_pane.add_to_launcher_enabled)
 
471
                self.config.add_to_unity_launcher)
463
472
        else:
464
473
            self.menu_view.remove(self.add_to_launcher_separator)
465
474
            self.menu_view.remove(self.menuitem_add_to_launcher)
467
476
        # run s-c-agent update
468
477
        if options.disable_buy or not self.distro.PURCHASE_APP_URL:
469
478
            self.menu_file.remove(self.menuitem_reinstall_purchases)
470
 
            if not (options.enable_lp or och):
471
 
                self.menu_file.remove(self.separator_login)
472
479
        else:
473
480
            # running the agent will trigger a db reload so we do it later
474
481
            GObject.timeout_add_seconds(3, self._run_software_center_agent)
498
505
    # helper
499
506
    def _run_software_center_agent(self):
500
507
        """ helper that triggers the update-software-center-agent helper """
501
 
        sc_agent_update = os.path.join(
502
 
            self.datadir, "update-software-center-agent")
503
 
        (pid, stdin, stdout, stderr) = GObject.spawn_async(
504
 
            [sc_agent_update, "--datadir", self.datadir],
505
 
            flags=GObject.SPAWN_DO_NOT_REAP_CHILD)
506
 
        GObject.child_watch_add(
507
 
            pid, self._on_update_software_center_agent_finished)
 
508
        run_software_center_agent(self.db)
508
509
 
509
510
    def _run_expunge_cache_helper(self):
510
511
        """ helper that expires the piston-mini-client cache """
597
598
            self._recommender_agent = RecommenderAgent()
598
599
        return self._recommender_agent
599
600
 
600
 
    def _on_update_software_center_agent_finished(self, pid, condition):
601
 
        LOG.info("software-center-agent finished with status %i" %
602
 
            os.WEXITSTATUS(condition))
603
 
        if os.WEXITSTATUS(condition) == 0:
604
 
            self.db.reopen()
605
 
 
606
 
    def on_review_stats_loaded(self, reviews):
 
601
    def on_review_stats_loaded(self, loader, reviews):
607
602
        LOG.debug("on_review_stats_loaded: '%s'" % len(reviews))
608
603
 
609
604
    def destroy(self):
628
623
        if Gtk.main_level() == 0:
629
624
            LOG.info("closing before the regular main loop was run")
630
625
            sys.exit(0)
631
 
        # this is the case when it regularly runs
632
 
        if hasattr(self, "glaunchpad"):
633
 
            self.glaunchpad.shutdown()
634
626
        self.save_state()
635
627
        self.destroy()
636
628
 
740
732
            if self.menuitem_go_forward.get_sensitive():
741
733
                self.menuitem_go_forward.activate()
742
734
 
743
 
    def _on_lp_login(self, lp, token):
744
 
        self._lp_login_successful = True
745
 
        private_archives = self.glaunchpad.get_subscribed_archives()
746
 
        channel_manager = self.view_switcher.get_model().channel_manager
747
 
        channel_manager.feed_in_private_sources_list_entries(
748
 
            private_archives)
749
 
 
750
 
    def _on_sso_login(self, sso, oauth_result):
751
 
        self._sso_login_successful = True
752
 
        # appmanager needs to know about the oauth token for the reinstall
753
 
        # previous purchases add_license_key call
754
 
        self.app_manager.oauth_token = oauth_result
755
 
        self.scagent.query_available_for_me()
756
 
 
757
735
    def _on_style_updated(self, widget, init_css_callback, *args):
758
736
        init_css_callback(widget, *args)
759
737
 
760
 
    def _available_for_me_result(self, scagent, result_list):
761
 
        #print "available_for_me_result", result_list
762
 
        from softwarecenter.db.update import (
763
 
            add_from_purchased_but_needs_reinstall_data)
764
 
        available = add_from_purchased_but_needs_reinstall_data(result_list,
765
 
            self.db, self.cache)
766
 
        self.available_for_me_query = available
767
 
        self.available_pane.on_previous_purchases_activated(available)
768
 
 
769
738
    def get_icon_filename(self, iconname, iconsize):
770
739
        iconinfo = self.icons.lookup_icon(iconname, iconsize, 0)
771
740
        if not iconinfo:
838
807
        # return False to ensure that a possible GObject.timeout_add ends
839
808
        return False
840
809
 
841
 
    def on_menuitem_launchpad_private_ppas_activate(self, menuitem):
842
 
        from backend.launchpad import GLaunchpad
843
 
        self.glaunchpad = GLaunchpad()
844
 
        self.glaunchpad.connect("login-successful", self._on_lp_login)
845
 
        from view.logindialog import LoginDialog
846
 
        d = LoginDialog(self.glaunchpad, self.datadir, parent=self.window_main)
847
 
        d.login()
848
 
 
849
 
    def _create_dbus_sso(self):
850
 
        # see bug #773214 for the rationale, do not translate the appname
851
 
        #appname = _("Ubuntu Software Center")
852
 
        appname = SOFTWARE_CENTER_NAME_KEYRING
853
 
        help_text = _("To reinstall previous purchases, sign in to the "
854
 
            "Ubuntu Single Sign-On account you used to pay for them.")
855
 
        #window = self.window_main.get_window()
856
 
        #xid = self.get_window().xid
857
 
        xid = 0
858
 
        self.sso = get_sso_backend(xid,
859
 
                                   appname,
860
 
                                   help_text)
861
 
        self.sso.connect("login-successful", self._on_sso_login)
862
 
 
863
 
    def _login_via_dbus_sso(self):
864
 
        self._create_dbus_sso()
865
 
        self.sso.login()
866
 
 
867
 
    def _create_scagent_if_needed(self):
868
 
        if not self.scagent:
869
 
            from softwarecenter.backend.scagent import SoftwareCenterAgent
870
 
            self.scagent = SoftwareCenterAgent()
871
 
            self.scagent.connect("available-for-me",
872
 
                                 self._available_for_me_result)
 
810
    def _on_reinstall_purchased_login(self, sso, oauth_result):
 
811
        self._sso_login_successful = True
 
812
        # appmanager needs to know about the oauth token for the reinstall
 
813
        # previous purchases add_license_key call
 
814
        self.app_manager.oauth_token = oauth_result
 
815
 
 
816
        # the software-center-agent will ensure the previous-purchases
 
817
        # get merged in
 
818
        self._run_software_center_agent()
 
819
 
 
820
        # show spinner as this may take some time, the spinner will
 
821
        # automatically go away when a DB refreshes
 
822
        self.available_pane.show_appview_spinner()
 
823
 
 
824
        # show previous purchased
 
825
        self.available_pane.on_previous_purchases_activated(
 
826
            self.available_for_me_query)
 
827
 
 
828
    def on_menuitem_reinstall_purchases_activate(self, menuitem):
 
829
        self.view_manager.set_active_view(ViewPages.AVAILABLE)
 
830
        self.view_manager.search_entry.clear_with_no_signal()
 
831
        # its ok to use the sync version here to get the token, this is
 
832
        # very quick
 
833
        helper = UbuntuSSO()
 
834
        token = helper.find_oauth_token_sync()
 
835
        if token:
 
836
            # trigger a software-center-agent run to ensure the merged in
 
837
            # purchases are fresh
 
838
            self._run_software_center_agent()
 
839
            # we already have the list of available items, so just show it
 
840
            # (no need for spinner here)
 
841
            self.available_pane.on_previous_purchases_activated(
 
842
                    self.available_for_me_query)
 
843
        else:
 
844
            # see bug #773214 for the rationale, do not translate the appname
 
845
            #appname = _("Ubuntu Software Center")
 
846
            appname = SOFTWARE_CENTER_NAME_KEYRING
 
847
            help_text = _(
 
848
                "To reinstall previous purchases, sign in to the "
 
849
                "Ubuntu Single Sign-On account you used to pay for them.")
 
850
            #window = self.window_main.get_window()
 
851
            #xid = self.get_window().xid
 
852
            xid = 0
 
853
            self.sso = get_login_backend(xid, appname, help_text)
 
854
            self.sso.connect(
 
855
                "login-successful", self._on_reinstall_purchased_login)
 
856
            self.sso.login()
873
857
 
874
858
    def on_menuitem_recommendations_activate(self, menu_item):
875
859
        rec_panel = self.available_pane.cat_view.recommended_for_you_panel
883
867
            if res == Gtk.ResponseType.YES:
884
868
                rec_panel.opt_in_to_recommendations_service()
885
869
 
886
 
    def on_menuitem_reinstall_purchases_activate(self, menuitem):
887
 
        self.view_manager.set_active_view(ViewPages.AVAILABLE)
888
 
        self.view_manager.search_entry.clear_with_no_signal()
889
 
        self.available_pane.show_appview_spinner()
890
 
        if self.available_for_me_query:
891
 
            # we already have the list of available items, so just show it
892
 
            self.available_pane.on_previous_purchases_activated(
893
 
                    self.available_for_me_query)
894
 
        else:
895
 
            # fetch the list of available items and show it
896
 
            self._create_scagent_if_needed()
897
 
            self._login_via_dbus_sso()
898
 
 
899
870
    def on_menuitem_deauthorize_computer_activate(self, menuitem):
900
871
 
901
872
        # FIXME: need Ubuntu SSO username here
1129
1100
        if self.menuitem_view.blocked:
1130
1101
            return
1131
1102
        from softwarecenter.db.appfilter import get_global_filter
1132
 
        if get_global_filter().supported_only == True:
 
1103
        if get_global_filter().supported_only is True:
1133
1104
            get_global_filter().supported_only = False
1134
1105
 
1135
1106
            self.available_pane.refresh_apps()
1142
1113
        if self.menuitem_view.blocked:
1143
1114
            return
1144
1115
        from softwarecenter.db.appfilter import get_global_filter
1145
 
        if get_global_filter().supported_only == False:
 
1116
        if get_global_filter().supported_only is False:
1146
1117
            get_global_filter().supported_only = True
1147
1118
 
1148
1119
            self.available_pane.refresh_apps()
1179
1150
        vm.nav_forward()
1180
1151
 
1181
1152
    def on_menuitem_add_to_launcher_toggled(self, menu_item):
1182
 
        self.available_pane.add_to_launcher_enabled = menu_item.get_active()
 
1153
        self.config.add_to_unity_launcher = menu_item.get_active()
1183
1154
 
1184
1155
# Help Menu
1185
1156
    def on_menuitem_about_activate(self, widget):
1200
1171
 
1201
1172
    def _ask_and_repair_broken_cache(self):
1202
1173
        # wait until the window window is available
1203
 
        if self.window_main.props.visible == False:
 
1174
        if self.window_main.props.visible is False:
1204
1175
            GObject.timeout_add_seconds(1, self._ask_and_repair_broken_cache)
1205
1176
            return
1206
1177
        if dialogs.confirm_repair_broken_cache(self.window_main,
1354
1325
            self.view_manager.set_active_view(ViewPages.AVAILABLE)
1355
1326
 
1356
1327
    def restore_state(self):
1357
 
        if self.config.has_option("general", "size"):
1358
 
            (x, y) = self.config.get("general", "size").split(",")
1359
 
            self.window_main.set_default_size(int(x), int(y))
 
1328
        (x, y) = self.config.app_window_size
 
1329
        if x > 0 and y > 0:
 
1330
            self.window_main.set_default_size(x, y)
1360
1331
        else:
1361
1332
            # on first launch, specify the default window size to take
1362
1333
            # advantage of the available screen real estate (but set a
1366
1337
            self.window_main.set_default_size(
1367
1338
                                        min(int(.85 * screen_width), 1200),
1368
1339
                                        min(int(.85 * screen_height), 800))
1369
 
        if (self.config.has_option("general", "maximized") and
1370
 
            self.config.getboolean("general", "maximized")):
 
1340
        if self.config.app_window_maximized:
1371
1341
            self.window_main.maximize()
1372
 
        if self.config.has_option("general", "add_to_launcher"):
1373
 
            self.available_pane.add_to_launcher_enabled = (
1374
 
                    self.config.getboolean(
1375
 
                    "general",
1376
 
                    "add_to_launcher"))
1377
 
        else:
1378
 
            # initial default state is to add to launcher, per spec
1379
 
            self.available_pane.add_to_launcher_enabled = True
1380
1342
 
1381
1343
    def save_state(self):
1382
1344
        LOG.debug("save_state")
1385
1347
        if window is None:
1386
1348
            return
1387
1349
        maximized = window.get_state() & Gdk.WindowState.MAXIMIZED
1388
 
        if maximized:
1389
 
            self.config.set("general", "maximized", "True")
1390
 
        else:
1391
 
            self.config.set("general", "maximized", "False")
 
1350
        self.config.app_window_maximized = maximized
 
1351
        if not maximized:
1392
1352
            # size only matters when non-maximized
1393
1353
            size = self.window_main.get_size()
1394
 
            self.config.set("general", "size", "%s, %s" % (size[0], size[1]))
1395
 
        if self.available_pane.add_to_launcher_enabled:
1396
 
            self.config.set("general", "add_to_launcher", "True")
1397
 
        else:
1398
 
            self.config.set("general", "add_to_launcher", "False")
1399
 
        # store the recommender values
1400
 
        self.config.set("general",
1401
 
                        "recommender_uuid",
1402
 
                        self._get_recommender_agent().recommender_uuid)
1403
 
        self.config.set("general",
1404
 
                        "recommender_profile_id",
1405
 
                        self._get_recommender_agent().recommender_profile_id)
 
1354
            self.config.app_window_size = [size[0], size[1]]
1406
1355
        self.config.write()
1407
1356
 
1408
1357
    def write_memory_dump(self, fname=None):