~gary-lasker/software-center/tech-items-to-launcher-fix-lp1006483

« back to all changes in this revision

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

FINALLY

Show diffs side-by-side

added added

removed removed

Lines of Context:
130
130
                 object_path='/com/ubuntu/Softwarecenter'):
131
131
        dbus.service.Object.__init__(self, bus_name, object_path)
132
132
        self.parent = parent
 
133
        self.bus_name = bus_name
133
134
 
134
135
    def stop(self):
135
136
        """ stop the dbus controller and remove from the bus """
 
137
        self.bus_name.get_bus().release_name(self.bus_name.get_name())
136
138
        self.remove_from_connection()
137
139
 
138
140
    @dbus.service.method('com.ubuntu.SoftwarecenterIFace')
159
161
    APP_ICON_SIZE = Gtk.IconSize.DIALOG
160
162
 
161
163
    def __init__(self, datadir, xapian_base_path, options, args=None):
162
 
        # setup dbus and exit if there is another instance already
163
 
        # running
 
164
        self.dbusControler = None
 
165
        # setup dbus and exit if there is another instance already running
164
166
        self.setup_dbus_or_bring_other_instance_to_front(args)
165
167
 
166
168
        self.datadir = datadir
167
 
        SimpleGtkbuilderApp.__init__(self,
 
169
        super(SoftwareCenterAppGtk3, self).__init__(
168
170
                                     datadir + "/ui/gtk3/SoftwareCenter.ui",
169
171
                                     "software-center")
170
172
        gettext.bindtextdomain("software-center", "/usr/share/locale")
517
519
    def on_review_stats_loaded(self, reviews):
518
520
        LOG.debug("on_review_stats_loaded: '%s'" % len(reviews))
519
521
 
 
522
    def destroy(self):
 
523
        """Destroy this instance and every used resource."""
 
524
        self.window_main.destroy()
 
525
 
 
526
        # remove global instances of Managers
 
527
        self.app_manager.destroy()
 
528
        self.view_manager.destroy()
 
529
 
 
530
        if self.dbusControler is not None:
 
531
            # ensure that the dbus controller is really gone
 
532
            self.dbusControler.stop()
 
533
 
520
534
    def close_app(self):
521
535
        """ perform tasks like save-state etc when the application is
522
536
            exited
531
545
        if hasattr(self, "glaunchpad"):
532
546
            self.glaunchpad.shutdown()
533
547
        self.save_state()
 
548
        self.destroy()
 
549
 
534
550
        # this will not throw exceptions in pygi but "only" log via g_critical
535
551
        # to the terminal but it might in the future so we add a handler here
536
552
        try:
537
553
            Gtk.main_quit()
538
554
        except:
539
555
            LOG.exception("Gtk.main_quit failed")
540
 
        # ensure that the dbus controller is really gone, just for good
541
 
        # measure
542
 
        self.dbusControler.stop()
 
556
 
543
557
        # exit here explictely to ensure that no further gtk event loops or
544
558
        # threads run and cause havoc on exit (LP: #914393)
545
559
        sys.exit(0)
1209
1223
            bus_name = dbus.service.BusName('com.ubuntu.Softwarecenter', bus)
1210
1224
            self.dbusControler = SoftwarecenterDbusController(self, bus_name)
1211
1225
 
1212
 
    def show_available_packages(self, packages):
 
1226
    @wait_for_apt_cache_ready
 
1227
    def show_app(self, app):
 
1228
        """Show 'app' in the installed pane if is installed.
 
1229
 
 
1230
        If 'app' is not installed, show it in the available pane.
 
1231
 
 
1232
        """
 
1233
        print '\n\n\n=============================== show_available_packages (app installed?)', (app.pkgname in self.cache and self.cache[app.pkgname].installed)
 
1234
        if (app.pkgname in self.cache and self.cache[app.pkgname].installed):
 
1235
            with ExecutionTime("installed_pane.init_view()"):
 
1236
                self.installed_pane.init_view()
 
1237
            with ExecutionTime("installed_pane.show_app()"):
 
1238
                self.installed_pane.show_app(app)
 
1239
        else:
 
1240
            self.available_pane.init_view()
 
1241
            self.available_pane.show_app(app)
 
1242
 
 
1243
    def show_search_text(self, text):
 
1244
        """Set 'text' to be the search text in the available pane."""
 
1245
        self.available_pane.init_view()
 
1246
        self.available_pane.searchentry.set_text(search_text)
 
1247
 
 
1248
    def _show_available_packages(self, packages):
1213
1249
        """ Show packages given as arguments in the available_pane
1214
1250
            If the list of packages is only one element long show that,
1215
1251
            otherwise turn it into a comma seperated search
1220
1256
            return
1221
1257
 
1222
1258
        _packages = []  # make a copy
 
1259
        search_text = None
1223
1260
 
1224
1261
        # support both "pkg1 pkg" and "pkg1,pkg2" (and "pkg1,pkg2 pkg3")
1225
1262
        for arg in packages:
1235
1272
                _packages[0] = _packages[0].partition(prefix)[2]
1236
1273
 
1237
1274
            # allow s-c to be called with a search term
1238
 
            if packages[0].startswith("search:"):
1239
 
                packages[0] = packages[0].partition("search:")[2]
1240
 
                self.available_pane.init_view()
1241
 
                self.available_pane.searchentry.set_text(" ".join(packages))
 
1275
            if _packages[0].startswith("search:"):
 
1276
                _packages[0] = _packages[0].partition("search:")[2]
 
1277
                self.show_search_text(" ".join(_packages))
1242
1278
                return
1243
1279
 
1244
1280
        print '\n-> show_available_packages len is', len(_packages)
1245
1281
        print '\n-> show_available_packages content is', repr(_packages)
1246
1282
 
 
1283
        app = None
1247
1284
        if len(_packages) == 1:
1248
1285
            request = _packages[0]
 
1286
            print '\n-> exactly one package! ', request
 
1287
 
 
1288
            # are we dealing with a path?
 
1289
            if os.path.exists(request) and not os.path.isdir(request):
 
1290
                if not request.startswith(os.path.sep):
 
1291
                    # we may have been given a relative path
 
1292
                    request = os.path.join(os.getcwd(), request)
 
1293
                app = DebFileApplication(request)
 
1294
            else:
 
1295
                # package from archive
 
1296
                # if there is a "/" in the string consider it as tuple
 
1297
                # of (pkgname, appname) for exact matching (used by
 
1298
                # e.g. unity
 
1299
                (pkgname, sep, appname) = request.partition("/")
 
1300
                if pkgname or appname:
 
1301
                    app = Application(appname, pkgname)
 
1302
                else:
 
1303
                    LOG.warning('show_available_packages: received %r but '
 
1304
                                'can\'t build an Application from it.', request)
 
1305
        elif len(_packages) > 1:
 
1306
            print '\n-> more than one package! ', _packages
 
1307
            search_text = ",".join(_packages)
 
1308
            # turn multiple packages into a search with ","
 
1309
            self.show_search_text(",".join(_packages))
 
1310
            return
 
1311
 
 
1312
        print '\n-> app is None? ', app is None
 
1313
 
 
1314
        if app is not None:
 
1315
            self.show_app(app)
 
1316
            return
 
1317
 
 
1318
        print '\n-> setting self.view_manager.set_active_view to ', ViewPages.AVAILABLE
 
1319
 
 
1320
        # normal startup, show the lobby (it will have a spinner when
 
1321
        # its not ready yet) - it will also initialize the view
 
1322
        self.view_manager.set_active_view(ViewPages.AVAILABLE)
 
1323
 
 
1324
    def show_available_packages(self, packages):
 
1325
        """ Show packages given as arguments in the available_pane
 
1326
            If the list of packages is only one element long show that,
 
1327
            otherwise turn it into a comma seperated search
 
1328
        """
 
1329
 
 
1330
        print '\n\n\n=============================== show_available_packages', packages
 
1331
 
 
1332
        # strip away the apt: prefix
 
1333
        if packages and packages[0].startswith("apt:///"):
 
1334
            # this is for 'apt:pkgname' in alt+F2 in gnome
 
1335
            packages[0] = packages[0].partition("apt:///")[2]
 
1336
        elif packages and packages[0].startswith("apt://"):
 
1337
            packages[0] = packages[0].partition("apt://")[2]
 
1338
        elif packages and packages[0].startswith("apt:"):
 
1339
            packages[0] = packages[0].partition("apt:")[2]
 
1340
 
 
1341
        # allow s-c to be called with a search term
 
1342
        if packages and packages[0].startswith("search:"):
 
1343
            packages[0] = packages[0].partition("search:")[2]
 
1344
            self.available_pane.init_view()
 
1345
            self.available_pane.searchentry.set_text(" ".join(packages))
 
1346
            return
 
1347
 
 
1348
        if len(packages) == 1:
 
1349
            request = packages[0]
 
1350
            print '\n\n\n=============================== show_available_packages (request)', request
1249
1351
 
1250
1352
            # are we dealing with a path?
1251
1353
            if os.path.exists(request) and not os.path.isdir(request):
1258
1360
                # if there is a "/" in the string consider it as tuple
1259
1361
                # of (pkgname, appname) for exact matching (used by
1260
1362
                # e.g. unity
1261
 
 
1262
 
                # when passing [''] to bringToFront, this triggers:
1263
 
                # org.freedesktop.DBus.Python.ValueError: Traceback (most recent call last):
1264
 
                #   File "/usr/lib/python2.7/dist-packages/dbus/service.py", line 707, in _message_cb
1265
 
                #     retval = candidate_method(self, *args, **keywords)
1266
 
                #   File "/home/nessita/canonical/software-store/fix-977931/softwarecenter/ui/gtk3/app.py", line 144, in bringToFront
1267
 
                #     self.parent.show_available_packages(args)
1268
 
                #   File "/home/nessita/canonical/software-store/fix-977931/softwarecenter/ui/gtk3/app.py", line 1250, in show_available_packages
1269
 
                #     app = Application(appname, pkgname)
1270
 
                #   File "/home/nessita/canonical/software-store/fix-977931/softwarecenter/db/application.py", line 52, in __init__
1271
 
                #     raise ValueError("Need either appname or pkgname or request")
1272
 
                # ValueError: Need either appname or pkgname or request
1273
 
 
1274
 
                (pkgname, sep, appname) = _packages[0].partition("/")
1275
 
                # pkgname and/or appname can be ''
1276
 
                print '\n\n***** creating an Application with %r and %r' % (appname, pkgname)
 
1363
                (pkgname, sep, appname) = packages[0].partition("/")
1277
1364
                app = Application(appname, pkgname)
1278
1365
 
 
1366
            print '\n\n\n=============================== show_available_packages (app?)', repr(app)
 
1367
 
1279
1368
            @wait_for_apt_cache_ready
1280
1369
            def show_app(self, app):
1281
1370
                # if the pkg is installed, show it in the installed pane
1288
1377
                else:
1289
1378
                    self.available_pane.init_view()
1290
1379
                    self.available_pane.show_app(app)
1291
 
            show_app(self, app)
 
1380
 
 
1381
            self.show_app(app)
1292
1382
            return
1293
 
        elif len(_packages) > 1:
 
1383
        elif len(packages) > 1:
1294
1384
            # turn multiple packages into a search with ","
1295
1385
            self.available_pane.init_view()
1296
 
            self.available_pane.searchentry.set_text(",".join(_packages))
 
1386
            self.available_pane.searchentry.set_text(",".join(packages))
1297
1387
            return
1298
 
 
1299
 
        print '\n-> setting self.view_manager.set_active_view to ', ViewPages.AVAILABLE
1300
 
 
1301
1388
        # normal startup, show the lobby (it will have a spinner when
1302
1389
        # its not ready yet) - it will also initialize the view
1303
1390
        self.view_manager.set_active_view(ViewPages.AVAILABLE)
1327
1414
            # initial default state is to add to launcher, per spec
1328
1415
            self.available_pane.add_to_launcher_enabled = True
1329
1416
 
 
1417
    def restore_state(self):
 
1418
        if self.config.has_option("general", "size"):
 
1419
            (x, y) = self.config.get("general", "size").split(",")
 
1420
            self.window_main.set_default_size(int(x), int(y))
 
1421
        else:
 
1422
            # on first launch, specify the default window size to take
 
1423
            # advantage of the available screen real estate (but set a
 
1424
            # reasonable limit in case of a crazy-huge monitor)
 
1425
            screen_height = Gdk.Screen.height()
 
1426
            screen_width = Gdk.Screen.width()
 
1427
            self.window_main.set_default_size(
 
1428
                                        min(int(.85 * screen_width), 1200),
 
1429
                                        min(int(.85 * screen_height), 800))
 
1430
        if (self.config.has_option("general", "maximized") and
 
1431
            self.config.getboolean("general", "maximized")):
 
1432
            self.window_main.maximize()
 
1433
        if self.config.has_option("general", "add_to_launcher"):
 
1434
            self.available_pane.add_to_launcher_enabled = (
 
1435
                    self.config.getboolean(
 
1436
                    "general",
 
1437
                    "add_to_launcher"))
 
1438
        else:
 
1439
            # initial default state is to add to launcher, per spec
 
1440
            self.available_pane.add_to_launcher_enabled = True
 
1441
 
1330
1442
    def save_state(self):
1331
1443
        LOG.debug("save_state")
1332
1444
        # this happens on a delete event, we explicitely save_state() there
1361
1473
        # delay cache open
1362
1474
        GObject.timeout_add(1, self.cache.open)
1363
1475
 
 
1476
        # support both "pkg1 pkg" and "pkg1,pkg2" (and pkg1,pkg2 pkg3)
 
1477
        if args:
 
1478
            for (i, arg) in enumerate(args[:]):
 
1479
                if "," in arg:
 
1480
                    args.extend(arg.split(","))
 
1481
                    del args[i]
 
1482
 
1364
1483
        # FIXME: make this more predictable and less random
1365
1484
        # show args when the app is ready
1366
1485
        self.show_available_packages(args)