def update_search_results_cb(self, api_games, error): if error: ErrorDialog(error) return self.search_spinner.stop() self.search_spinner.hide() total_count = api_games.get("count", 0) count = len(api_games.get('results', [])) if not count: self.result_label.set_markup(_("No results")) elif count == total_count: self.result_label.set_markup(_(f"Showing <b>{count}</b> results")) else: self.result_label.set_markup( _(f"<b>{total_count}</b> results, only displaying first {count}" )) for row in self.listbox.get_children(): row.destroy() for game in api_games.get("results", []): platforms = ",".join( gtk_safe(platform["name"]) for platform in game["platforms"]) year = game['year'] or "" if platforms and year: platforms = ", " + platforms row = self.build_row("", gtk_safe(game['name']), f"{year}{platforms}") row.api_info = game self.listbox.add(row) self.listbox.show()
def update(self, db_game): """Update game informations Return whether a row was updated """ store_item = StoreItem(db_game, self.service_media) row = self.get_row_by_id(store_item.id) if not row: row = self.get_row_by_id(db_game["service_id"]) if not row: return False row[COL_ID] = str(store_item.id) row[COL_SLUG] = store_item.slug row[COL_NAME] = gtk_safe(store_item.name) row[COL_ICON] = store_item.get_pixbuf() row[COL_YEAR] = store_item.year row[COL_RUNNER] = store_item.runner row[COL_RUNNER_HUMAN_NAME] = gtk_safe(store_item.runner_text) row[COL_PLATFORM] = gtk_safe(store_item.platform) row[COL_LASTPLAYED] = store_item.lastplayed row[COL_LASTPLAYED_TEXT] = store_item.lastplayed_text row[COL_INSTALLED] = store_item.installed row[COL_INSTALLED_AT] = store_item.installed_at row[COL_INSTALLED_AT_TEXT] = store_item.installed_at_text row[COL_PLAYTIME] = store_item.playtime row[COL_PLAYTIME_TEXT] = store_item.playtime_text return True
def _on_folder_scanned(self, result, error): if error: ErrorDialog(error) self.destroy() return for child in self.vbox.get_children(): child.destroy() installed, missing = result installed_label = self._get_label("Installed games") self.vbox.add(installed_label) installed_listbox = Gtk.ListBox(visible=True) installed_scroll = Gtk.ScrolledWindow(visible=True) installed_scroll.set_vexpand(True) installed_scroll.add(installed_listbox) self.vbox.add(installed_scroll) for folder in installed: installed_listbox.add(self.build_row("", gtk_safe(folder), "")) missing_label = self._get_label("No match found") self.vbox.add(missing_label) missing_listbox = Gtk.ListBox(visible=True) missing_scroll = Gtk.ScrolledWindow(visible=True) missing_scroll.set_vexpand(True) missing_scroll.add(missing_listbox) self.vbox.add(missing_scroll) for folder in missing: missing_listbox.add(self.build_row("", gtk_safe(folder), ""))
def get_game_name_label(self): """Return the label with the game's title""" title_label = Gtk.Label(visible=True) title_label.set_ellipsize(Pango.EllipsizeMode.END) title_label.set_markup("<span font_desc='16'><b>%s</b></span>" % gtk_safe(self.game.name)) return title_label
def on_installer_selected(self, _widget, installer_slug): """Sets the script interpreter to the correct script then proceed to install folder selection. If the installed game depends on another one and it's not installed, prompt the user to install it and quit this installer. """ self.clean_widgets() try: self.interpreter = interpreter.ScriptInterpreter( self.get_script_from_slug(installer_slug), self ) except MissingGameDependency as ex: dlg = QuestionDialog( { "question": _("This game requires %s. Do you want to install it?") % ex.slug, "title": _("Missing dependency"), } ) if dlg.result == Gtk.ResponseType.YES: InstallerWindow( installers=self.installers, service=self.service, appid=self.appid, application=self.application, ) self.destroy() return self.title_label.set_markup(_(u"<b>Installing {}</b>").format(gtk_safe(self.interpreter.installer.game_name))) self.select_install_folder()
def on_game_duplicate(self, _widget): confirm_dlg = QuestionDialog({ "parent": self.window, "question": _("Do you wish to duplicate %s?\nThe configuration will be duplicated, " "but the games files will <b>not be duplicated</b>.") % gtk_safe(self.game.name), "title": _("Duplicate game?"), }) if confirm_dlg.result != Gtk.ResponseType.YES: return assigned_name = get_unusued_game_name(self.game.name) old_config_id = self.game.game_config_id if old_config_id: new_config_id = duplicate_game_config(self.game.slug, old_config_id) else: new_config_id = None db_game = get_game_by_field(self.game.id, "id") db_game["name"] = assigned_name db_game["configpath"] = new_config_id db_game.pop("id") # Disconnect duplicate from service- there should be at most # 1 PGA game for a service game. db_game.pop("service", None) db_game.pop("service_id", None) game_id = add_game(**db_game) new_game = Game(game_id) new_game.save()
def get_runner_label(self): runner_label = Gtk.Label(visible=True) if len(self.game.platform) > 15: platform = self.game.platform[:15] + "…" else: platform = self.game.platform runner_label.set_markup("Platform:\n<b>%s</b>" % gtk_safe(platform)) return runner_label
def platform(self): """Platform""" _platform = self._game_data.get("platform") if not _platform and not self.service and self.installed: game_inst = Game(self._game_data["id"]) if game_inst.platform: _platform = game_inst.platform return gtk_safe(_platform)
def error_handler(error_type, value, traceback): """Intercept all possible exceptions and raise them as ScriptingErrors""" if error_type == ScriptingError: message = value.message if value.faulty_data: message += "\n<b>%s</b>" % gtk_safe(value.faulty_data) ErrorDialog(message) else: _excepthook(error_type, value, traceback)
def get_platform_label(self): platform_label = Gtk.Label(visible=True) platform_label.set_size_request(120, -1) platform_label.set_alignment(0, 0.5) platform = gtk_safe(self.game.platform) platform_label.set_tooltip_markup(platform) platform_label.set_markup(_("Platform:\n<b>%s</b>") % platform) platform_label.set_property("ellipsize", Pango.EllipsizeMode.END) return platform_label
def playtime_text(self): """Playtime duration in hours (textual representation)""" try: _playtime_text = get_formatted_playtime(self.playtime) except ValueError: logger.warning("Invalid playtime value %s for %s", self.playtime, self) _playtime_text = "" # Do not show erroneous values return gtk_safe(_playtime_text)
def update(self, db_game): """Update game informations.""" game = StoreItem(db_game, self.service_media) row = self.get_row_by_id(game.id) row[COL_ID] = game.id row[COL_SLUG] = game.slug row[COL_NAME] = gtk_safe(game.name) row[COL_ICON] = game.get_pixbuf() row[COL_YEAR] = game.year row[COL_RUNNER] = game.runner row[COL_RUNNER_HUMAN_NAME] = gtk_safe(game.runner_text) row[COL_PLATFORM] = gtk_safe(game.platform) row[COL_LASTPLAYED] = game.lastplayed row[COL_LASTPLAYED_TEXT] = game.lastplayed_text row[COL_INSTALLED] = game.installed row[COL_INSTALLED_AT] = game.installed_at row[COL_INSTALLED_AT_TEXT] = game.installed_at_text row[COL_PLAYTIME] = game.playtime row[COL_PLAYTIME_TEXT] = game.playtime_text
def get_title_label(self): """Return the label with the game's title""" title_label = Gtk.Label() title_label.set_markup("<span font_desc='16'>%s</span>" % gtk_safe(self.game.name)) title_label.set_ellipsize(Pango.EllipsizeMode.END) title_label.set_size_request(256, -1) title_label.set_alignment(0, 0.5) title_label.set_justify(Gtk.Justification.LEFT) title_label.show() return title_label
def platform(self): """Platform""" _platform = self._game_data.get("platform") if not _platform and not self.service and self.installed: game_inst = Game(self._game_data["id"]) if game_inst.platform: _platform = game_inst.platform else: logger.debug("Game %s has no platform", self) return gtk_safe(_platform)
def get_title_label(self): """Return the label with the game's title""" title_label = Gtk.Label() title_label.set_markup("<span font_desc='16'>%s</span>" % gtk_safe(self.game.name)) title_label.set_ellipsize(Pango.EllipsizeMode.END) title_label.set_size_request(226, -1) title_label.set_alignment(0, 0.5) title_label.set_justify(Gtk.Justification.LEFT) title_label.show() return title_label
def __init__(self, game_id, parent=None): super().__init__(parent=parent) self.set_size_request(640, 128) self.game = Game(game_id) self.delete_files = False container = Gtk.VBox(visible=True) self.get_content_area().add(container) title_label = Gtk.Label(visible=True) title_label.set_line_wrap(True) title_label.set_alignment(0, 0.5) title_label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR) title_label.set_markup( "<span font_desc='14'><b>Uninstall %s</b></span>" % gtk_safe(self.game.name)) container.pack_start(title_label, False, False, 4) self.folder_label = Gtk.Label(visible=True) self.folder_label.set_alignment(0, 0.5) self.delete_button = Gtk.Button(_("Uninstall"), visible=True) self.delete_button.connect("clicked", self.on_delete_clicked) if not self.game.directory: self.folder_label.set_markup("No file will be deleted") elif len(get_games(searches={"directory": self.game.directory})) > 1: self.folder_label.set_markup( "The folder %s is used by other games and will be kept." % self.game.directory) elif is_removeable(self.game.directory): self.delete_button.set_sensitive(False) self.folder_label.set_markup("<i>Calculating size…</i>") AsyncCall(get_disk_size, self.folder_size_cb, self.game.directory) else: self.folder_label.set_markup( "Content of %s are protected and will not be deleted." % reverse_expanduser(self.game.directory)) container.pack_start(self.folder_label, False, False, 4) self.confirm_delete_button = Gtk.CheckButton() self.confirm_delete_button.set_active(True) container.pack_start(self.confirm_delete_button, False, False, 4) button_box = Gtk.HBox(visible=True) button_box.set_margin_top(30) style_context = button_box.get_style_context() style_context.add_class("linked") cancel_button = Gtk.Button(_("Cancel"), visible=True) cancel_button.connect("clicked", self.on_close) button_box.add(cancel_button) button_box.add(self.delete_button) container.pack_end(button_box, False, False, 0) self.show()
def get_file_label(self): """Return a human readable label for installer files""" url = self.installer_file.url if url.startswith("http"): parsed = urlparse(url) label = "%s on %s" % (self.installer_file.filename, parsed.netloc) elif url.startswith("N/A"): label = url[3:].lstrip(":") else: label = url return add_url_tags(gtk_safe(label))
def get_infobox(self): """Return the central information box""" info_box = Gtk.VBox(spacing=6) title_box = Gtk.HBox(spacing=6) title_box.add(InstallerLabel("<b>%s</b>" % gtk_safe(self.script["version"]))) title_box.pack_start(InstallerLabel(""), True, True, 0) rating_label = InstallerLabel(self.get_rating()) rating_label.set_alignment(1, 0.5) title_box.pack_end(rating_label, False, False, 0) info_box.add(title_box) info_box.add(InstallerLabel(add_url_tags(self.script["description"]))) return info_box
def get_file_provider_widget(self): """Return the widget used to track progress of file""" box = Gtk.VBox(spacing=6) if self.provider == "download": download_progress = self.get_download_progress() box.pack_start(download_progress, False, False, 0) return box if self.provider == "pga": url_label = InstallerLabel("CACHED: %s" % gtk_safe(self.installer_file.human_url), wrap=False) box.pack_start(url_label, False, False, 6) return box if self.provider == "user": user_label = InstallerLabel(gtk_safe( self.installer_file.human_url)) box.pack_start(user_label, False, False, 0) if self.provider == "steam": steam_installer = SteamInstaller(self.installer_file.url, self.installer_file.id) steam_installer.connect("game-installed", self.on_download_complete) steam_installer.connect("state-changed", self.on_state_changed) self.start_func = steam_installer.install_steam_game self.stop_func = steam_installer.stop_func steam_box = Gtk.HBox(spacing=6) info_box = Gtk.VBox(spacing=6) steam_label = InstallerLabel( _("Steam game for {platform} (appid: <b>{appid}</b>)").format( platform=steam_installer.platform, appid=steam_installer.appid)) info_box.add(steam_label) self.state_label = InstallerLabel("") info_box.add(self.state_label) steam_box.add(info_box) return steam_box return Gtk.Label(gtk_safe(self.installer_file.url))
def get_runner_label(self): """Return the label containing the runner info""" runner_icon = Gtk.Image.new_from_icon_name( self.game.runner.name.lower().replace(" ", "") + "-symbolic", Gtk.IconSize.MENU ) runner_icon.show() runner_label = Gtk.Label() runner_label.show() runner_label.set_markup("<b>%s</b>" % gtk_safe(self.game.platform)) runner_box = Gtk.Box(spacing=6) runner_box.add(runner_icon) runner_box.add(runner_label) runner_box.show() return runner_box
def get_runner_label(self): """Return the label containing the runner info""" runner_icon = Gtk.Image.new_from_icon_name( self.game.runner.name.lower().replace(" ", "") + "-symbolic", Gtk.IconSize.MENU) runner_icon.show() runner_label = Gtk.Label() runner_label.show() runner_label.set_markup("<b>%s</b>" % gtk_safe(self.game.platform)) runner_box = Gtk.Box(spacing=6) runner_box.add(runner_icon) runner_box.add(runner_label) runner_box.show() return runner_box
def choose_installer(self): """Stage where we choose an install script.""" self.validate_scripts() base_script = self.installers[0] self.title_label.set_markup(_("<b>Install %s</b>") % gtk_safe(base_script["name"])) installer_picker = InstallerPicker(self.installers) installer_picker.connect("installer-selected", self.on_installer_selected) scrolledwindow = Gtk.ScrolledWindow( hexpand=True, vexpand=True, child=installer_picker, visible=True ) scrolledwindow.set_shadow_type(Gtk.ShadowType.ETCHED_IN) self.widget_box.pack_end(scrolledwindow, True, True, 10)
def on_installer_selected(self, _widget, installer_version): """Sets the script interpreter to the correct script then proceed to install folder selection. If the installed game depends on another one and it's not installed, prompt the user to install it and quit this installer. """ self.clean_widgets() try: script = None for _script in self.installers: if _script["version"] == installer_version: script = _script self.interpreter = interpreter.ScriptInterpreter(script, self) except MissingGameDependency as ex: dlg = QuestionDialog({ "question": _("This game requires %s. Do you want to install it?") % ex.slug, "title": _("Missing dependency"), }) if dlg.result == Gtk.ResponseType.YES: InstallerWindow( installers=self.installers, service=self.service, appid=self.appid, application=self.application, ) self.destroy() return self.title_label.set_markup( _("<b>Installing {}</b>").format( gtk_safe(self.interpreter.installer.game_name))) self.select_install_folder() desktop_shortcut_button = Gtk.CheckButton(_("Create desktop shortcut"), visible=True) desktop_shortcut_button.connect( "clicked", self.on_create_desktop_shortcut_clicked) self.widget_box.pack_start(desktop_shortcut_button, False, False, 5) menu_shortcut_button = Gtk.CheckButton( _("Create application menu shortcut"), visible=True) menu_shortcut_button.connect("clicked", self.on_create_menu_shortcut_clicked) self.widget_box.pack_start(menu_shortcut_button, False, False, 5)
def add_game(self, game): platform = "" runner_human_name = "" runner_info = self.get_runner_info(game) if runner_info: runner_human_name, platform = runner_info else: game["installed"] = False lastplayed_text = "" if game["lastplayed"]: lastplayed_text = time.strftime("%c", time.localtime(game["lastplayed"])) installed_at_text = "" if game["installed_at"]: installed_at_text = time.strftime( "%c", time.localtime(game["installed_at"])) pixbuf = get_pixbuf_for_game(game["slug"], self.icon_type, game["installed"]) try: playtime_text = get_formatted_playtime(game["playtime"]) except ValueError: # We're all screwed pga.unfuck_playtime(game) playtime_text = game["playtime"] + ":(" self.store.append(( game["id"], gtk_safe(game["slug"]), gtk_safe(game["name"]), pixbuf, gtk_safe(str(game["year"] or "")), gtk_safe(game["runner"]), gtk_safe(runner_human_name), gtk_safe(platform), game["lastplayed"], gtk_safe(lastplayed_text), game["installed"], game["installed_at"], gtk_safe(installed_at_text), game["playtime"], playtime_text, ))
def get_file_provider_label(self): """Return the label displayed before the download starts""" if self.provider != "user": return InstallerLabel(gtk_safe(self.installer_file.human_url), wrap=False) box = Gtk.VBox(spacing=6) location_entry = FileChooserEntry(self.installer_file.human_url, Gtk.FileChooserAction.OPEN, path=None) location_entry.entry.connect("changed", self.on_location_changed) location_entry.show() box.pack_start(location_entry, False, False, 0) if self.installer_file.uses_pga_cache(create=True): cache_option = Gtk.CheckButton( _("Cache file for future installations")) cache_option.set_active(self.cache_to_pga) cache_option.connect("toggled", self.on_user_file_cached) box.pack_start(cache_option, False, False, 0) return box
def add_game(self, db_game): """Add a PGA game to the store""" game = StoreItem(db_game, self.service_media) self.store.append(( str(game.id), game.slug, game.name, game.get_pixbuf(), game.year, game.runner, game.runner_text, gtk_safe(game.platform), game.lastplayed, game.lastplayed_text, game.installed, game.installed_at, game.installed_at_text, game.playtime, game.playtime_text, ))
def on_delete_clicked(self, button): button.set_sensitive(False) if self.delete_files and not hasattr(self.game.runner, "no_game_remove_warning"): dlg = QuestionDialog({ "question": _("Please confirm.\nEverything under <b>%s</b>\n" "will be deleted.") % gtk_safe(self.game.directory), "title": _("Permanently delete files?"), }) if dlg.result != Gtk.ResponseType.YES: button.set_sensitive(True) return if self.delete_files: self.folder_label.set_markup( "Uninstalling game and deleting files...") else: self.folder_label.set_markup("Uninstalling game...") AsyncCall(self.game.remove, self.delete_cb, self.delete_files)
def get_file_provider_widget(self): """Return the widget used to track progress of file""" box = Gtk.VBox(spacing=6) if self.provider == "download": download_progress = self.get_download_progress() self.start_func = download_progress.start self.stop_func = download_progress.on_cancel_clicked box.pack_start(download_progress, False, False, 0) return box if self.provider == "pga": url_label = InstallerLabel("In cache: %s" % self.get_file_label(), wrap=False) box.pack_start(url_label, False, False, 6) return box if self.provider == "user": user_label = InstallerLabel(gtk_safe( self.installer_file.human_url)) box.pack_start(user_label, False, False, 0) return box if self.provider == "steam": steam_installer = SteamInstaller(self.installer_file.url, self.installer_file.id) steam_installer.connect("steam-game-installed", self.on_download_complete) steam_installer.connect("steam-state-changed", self.on_state_changed) self.start_func = steam_installer.install_steam_game self.stop_func = steam_installer.stop_func steam_box = Gtk.HBox(spacing=6) info_box = Gtk.VBox(spacing=6) steam_label = InstallerLabel( _("Steam game <b>{appid}</b>").format( appid=steam_installer.appid)) info_box.add(steam_label) self.state_label = InstallerLabel("") info_box.add(self.state_label) steam_box.add(info_box) return steam_box raise ValueError("Invalid provider %s" % self.provider)
def __init__(self, game_id, parent=None): super().__init__(parent=parent) self.set_size_request(640, 128) self.game = Game(game_id) container = Gtk.VBox(visible=True) self.get_content_area().add(container) title_label = Gtk.Label(visible=True) title_label.set_line_wrap(True) title_label.set_alignment(0, 0.5) title_label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR) title_label.set_markup( _("<span font_desc='14'><b>Remove %s</b></span>") % gtk_safe(self.game.name)) container.pack_start(title_label, False, False, 4) self.delete_label = Gtk.Label(visible=True) self.delete_label.set_alignment(0, 0.5) self.delete_label.set_markup( _("Completely remove %s from the library?\nAll play time will be lost." ) % self.game) container.pack_start(self.delete_label, False, False, 4) button_box = Gtk.HBox(visible=True) button_box.set_margin_top(30) style_context = button_box.get_style_context() style_context.add_class("linked") cancel_button = Gtk.Button(_("Cancel"), visible=True) cancel_button.connect("clicked", self.on_close) button_box.add(cancel_button) self.remove_button = Gtk.Button(_("Remove"), visible=True) self.remove_button.connect("clicked", self.on_remove_clicked) button_box.add(self.remove_button) container.pack_end(button_box, False, False, 0) self.show()
def lastplayed_text(self): """Date of last play (textual representation)""" return gtk_safe( time.strftime("%X %x", time.localtime(self._pga_data["lastplayed"])) if self._pga_data["lastplayed"] else "" )
def runner_text(self): """Runner name""" return gtk_safe(self.runner_names.get(self.runner))
def slug(self): """Slug identifier""" return gtk_safe(self._pga_data["slug"])
def installed_at_text(self): """Date of install (textual representation)""" return gtk_safe( time.strftime("%X %x", time.localtime(self._pga_data["installed_at"])) if self._pga_data["installed_at"] else "" )
def name(self): """Name""" return gtk_safe(self._pga_data["name"])
def runner(self): """Runner slug""" return gtk_safe(self._pga_data["runner"])
def runner(self): """Runner slug""" return gtk_safe(self._game_data.get("runner")) or ""
def _set_text(self, text): markup = "<span size='10000'>{}</span>".format(gtk_safe(text)) self.progress_label.set_markup(markup)
def runner_text(self): """Runner name""" return gtk_safe(RUNNER_NAMES.get(self.runner))