def add_installed_games(self): """Syncs installed Steam games with Lutris""" installed_appids = [] for steamapps_path in self.steamapps_paths: for appmanifest_file in get_appmanifests(steamapps_path): app_manifest_path = os.path.join(steamapps_path, appmanifest_file) app_manifest = AppManifest(app_manifest_path) installed_appids.append(app_manifest.steamid) self.install_from_steam(app_manifest) db_games = get_games(filters={"runner": "steam"}) for db_game in db_games: steam_game = Game(db_game["id"]) appid = steam_game.config.game_level["game"]["appid"] if appid not in installed_appids: steam_game.remove(no_signal=True) db_appids = defaultdict(list) db_games = get_games(filters={"service": "steam"}) for db_game in db_games: db_appids[db_game["service_id"]].append(db_game["id"]) for appid in db_appids: game_ids = db_appids[appid] if len(game_ids) == 1: continue for game_id in game_ids: steam_game = Game(game_id) if not steam_game.playtime: steam_game.remove(no_signal=True) steam_game.delete()
def test_can_add_game(self): dlg = config_dialogs.AddGameDialog(None) name_entry = dlg.vbox.get_children()[0].get_children()[1] runner_dropdown = dlg.vbox.get_children()[1] name_entry.set_text("Test game") runner_dropdown.set_active(1) notebook = dlg.vbox.get_children()[2] scrolled_window = notebook.get_children()[0] viewport = scrolled_window.get_children()[0] game_box = viewport.get_children()[0] exe_box = game_box.get_children()[0].get_children()[0] exe_label = exe_box.get_children()[0] self.assertEqual(exe_label.get_text(), "Executable") test_exe = os.path.abspath(__file__) exe_field = exe_box.get_children()[1] exe_field.set_file(Gio.File.new_for_path(test_exe)) exe_field.emit('file-set') self.assertEqual(exe_field.get_filename(), test_exe) add_button = dlg.vbox.get_children()[3].get_children()[1] add_button.clicked() game = Game('test-game') self.assertEqual(game.name, 'Test game') game.remove(from_library=True)
def scan_directory(dirname): files = os.listdir(dirname) folder_extentions = {os.path.splitext(filename)[1] for filename in files} core_matches = {} for core in RECOMMENDED_CORES: for ext in RECOMMENDED_CORES[core].get("extensions", []): if ext in folder_extentions: core_matches[ext] = core added_games = [] for filename in files: name, ext = os.path.splitext(filename) if ext not in core_matches: continue for flag in ROM_FLAGS: name = name.replace(" (%s)" % flag, "") for flag in EXTRA_FLAGS: name = name.replace("[%s]" % flag, "") if ", The" in name: name = "The %s" % name.replace(", The", "") name = name.strip() print("Importing '%s'" % name) slug = slugify(name) core = core_matches[ext] config = { "game": { "core": core_matches[ext], "main_file": os.path.join(dirname, filename) } } installer_slug = "%s-libretro-%s" % (slug, core) existing_game = get_games(filters={ "installer_slug": installer_slug, "installed": "1" }) if existing_game: game = Game(existing_game[0]["id"]) game.remove() configpath = write_game_config(slug, config) game_id = add_game(name=name, runner="libretro", slug=slug, directory=dirname, installed=1, installer_slug=installer_slug, configpath=configpath) print("Imported %s" % name) added_games.append(game_id) return added_games
def on_apply_button_clicked(self, widget): widget.set_sensitive(False) remove_from_library_button = self.builder.get_object( 'remove_from_library_button' ) remove_from_library = remove_from_library_button.get_active() remove_contents_button = self.builder.get_object( 'remove_contents_button' ) remove_contents = remove_contents_button.get_active() game = Game(self.game_slug) game.remove(remove_from_library, remove_contents) self.callback(self.game_slug, remove_from_library) self.on_close()
def test_can_add_game(self): name_entry = self.dlg.name_entry name_entry.set_text("Test game") self.dlg.runner_dropdown.set_active(1) game_box = self.get_game_box() exe_box = game_box.get_children()[0].get_children()[0] exe_label = exe_box.get_children()[0] self.assertEqual(exe_label.get_text(), "Executable") test_exe = os.path.abspath(__file__) exe_field = exe_box.get_children()[1] exe_field.set_file(Gio.File.new_for_path(test_exe)) exe_field.emit('file-set') self.assertEqual(exe_field.get_filename(), test_exe) add_button = self.get_buttons().get_children()[1] add_button.clicked() game = Game('test-game') self.assertEqual(game.name, 'Test game') game.remove(from_library=True)
def test_can_add_game(self): name_entry = self.dlg.name_entry name_entry.set_text("Test game") self.dlg.runner_dropdown.set_active_id('linux') game_box = self.get_game_box() exe_box = game_box.get_children()[0].get_children()[0] exe_label = exe_box.get_children()[0] self.assertEqual(exe_label.get_text(), "Executable") test_exe = os.path.abspath(__file__) exe_field = exe_box.get_children()[1] exe_field.entry.set_text(test_exe) self.assertEqual(exe_field.get_text(), test_exe) add_button = self.get_buttons().get_children()[1] add_button.clicked() pga_game = pga.get_game_by_field('test-game', 'slug') self.assertTrue(pga_game) game = Game(pga_game['id']) self.assertEqual(game.name, 'Test game') game.remove(from_library=True)
def scan_directory(dirname): """Add a directory of ROMs as Lutris games""" files = os.listdir(dirname) folder_extentions = {os.path.splitext(filename)[1] for filename in files} core_matches = {} for core in RECOMMENDED_CORES: for ext in RECOMMENDED_CORES[core].get("extensions", []): if ext in folder_extentions: core_matches[ext] = core added_games = [] for filename in files: name, ext = os.path.splitext(filename) if ext not in core_matches: continue logger.info("Importing '%s'", name) slug = slugify(name) core = core_matches[ext] config = { "game": { "core": core_matches[ext], "main_file": os.path.join(dirname, filename) } } installer_slug = "%s-libretro-%s" % (slug, core) existing_game = get_games(filters={"installer_slug": installer_slug}) if existing_game: game = Game(existing_game[0]["id"]) game.remove() configpath = write_game_config(slug, config) game_id = add_game(name=name, runner="libretro", slug=slug, directory=dirname, installed=1, installer_slug=installer_slug, configpath=configpath) added_games.append(game_id) return added_games
class UninstallGameDialog(GtkBuilderDialog): glade_file = 'dialog-uninstall-game.ui' dialog_object = 'uninstall-game-dialog' def substitute_label(self, widget, name, replacement): if hasattr(widget, 'get_text'): get_text = widget.get_text set_text = widget.set_text elif hasattr(widget, 'get_label'): get_text = widget.get_label set_text = widget.set_label else: raise TypeError("Unsupported type %s" % type(widget)) replacement = replacement.replace('&', '&') set_text(get_text().replace("{%s}" % name, replacement)) def initialize(self, slug=None, callback=None): self.game = Game(slug) self.callback = callback runner = self.game.runner self.substitute_label(self.builder.get_object('description_label'), 'game', self.game.name) self.substitute_label( self.builder.get_object('remove_from_library_button'), 'game', self.game.name ) remove_contents_button = self.builder.get_object( 'remove_contents_button' ) if self.game.is_installed: if hasattr(runner, 'own_game_remove_method'): remove_contents_button.set_label(runner.own_game_remove_method) else: try: default_path = runner.default_path except AttributeError: default_path = "/" if not is_removeable(runner.game_path, excludes=[default_path]): remove_contents_button.set_sensitive(False) path = self.game.directory or 'disk' self.substitute_label(remove_contents_button, 'path', path) remove_contents_button.get_children()[0].set_use_markup(True) else: remove_contents_button.hide() cancel_button = self.builder.get_object('cancel_button') cancel_button.connect('clicked', self.on_close) apply_button = self.builder.get_object('apply_button') apply_button.connect('clicked', self.on_apply_button_clicked) def on_apply_button_clicked(self, widget): widget.set_sensitive(False) remove_from_library_button = self.builder.get_object( 'remove_from_library_button' ) remove_from_library = remove_from_library_button.get_active() remove_contents_button = self.builder.get_object( 'remove_contents_button' ) remove_contents = remove_contents_button.get_active() if remove_contents and not hasattr(self.game.runner, 'no_game_remove_warning'): game_dir = self.game.directory.replace('&', '&') dlg = QuestionDialog({ 'question': "Are you sure you want to delete EVERYTHING under " "\n<b>%s</b>?\n (This can't be undone)" % game_dir, 'title': "CONFIRM DANGEROUS OPERATION" }) if dlg.result != Gtk.ResponseType.YES: widget.set_sensitive(True) return self.game.remove(remove_from_library, remove_contents) self.callback(self.game.slug, remove_from_library) self.on_close()
class UninstallGameDialog(Dialog): 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 folder_size_cb(self, folder_size, error): if error: logger.error(error) return self.delete_files = True self.delete_button.set_sensitive(True) self.folder_label.hide() self.confirm_delete_button.show() self.confirm_delete_button.set_label( "Delete %s (%s)" % (reverse_expanduser(self.game.directory), human_size(folder_size))) def on_close(self, _button): self.destroy() def on_delete_clicked(self, button): button.set_sensitive(False) if not self.confirm_delete_button.get_active(): self.delete_files = 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...") self.game.remove(self.delete_files) self.destroy()
class UninstallGameDialog(GtkBuilderDialog): glade_file = "dialog-uninstall-game.ui" dialog_object = "uninstall-game-dialog" @staticmethod def substitute_label(widget, name, replacement): if hasattr(widget, "get_text"): get_text = widget.get_text set_text = widget.set_text elif hasattr(widget, "get_label"): get_text = widget.get_label set_text = widget.set_label else: raise TypeError("Unsupported type %s" % type(widget)) set_text(get_text().replace("{%s}" % name, replacement)) def initialize(self, game_id=None, callback=None): self.game = Game(game_id) self.callback = callback runner = self.game.runner self.substitute_label(self.builder.get_object("description_label"), "game", self.game.name) self.substitute_label( self.builder.get_object("remove_from_library_button"), "game", self.game.name, ) remove_contents_button = self.builder.get_object( "remove_contents_button") if self.game.is_installed: path = self.game.directory or "" if hasattr(runner, "own_game_remove_method"): remove_contents_button.set_label(runner.own_game_remove_method) remove_contents_button.set_active(True) else: try: default_path = runner.default_path except AttributeError: default_path = "/" if is_removeable(path, excludes=[default_path]): remove_contents_button.set_active(True) else: remove_contents_button.set_sensitive(False) path = _("No game folder") path = reverse_expanduser(path) self.substitute_label(remove_contents_button, "path", path) label = remove_contents_button.get_child() label.set_use_markup(True) label.set_line_wrap(True) label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR) else: remove_contents_button.hide() cancel_button = self.builder.get_object("cancel_button") cancel_button.connect("clicked", self.on_close) apply_button = self.builder.get_object("apply_button") apply_button.connect("clicked", self.on_apply_button_clicked) def on_apply_button_clicked(self, widget): widget.set_sensitive(False) remove_from_library_button = self.builder.get_object( "remove_from_library_button") remove_from_library = remove_from_library_button.get_active() remove_contents_button = self.builder.get_object( "remove_contents_button") remove_contents = remove_contents_button.get_active() if remove_contents and not hasattr(self.game.runner, "no_game_remove_warning"): game_dir = self.game.directory.replace("&", "&") dlg = QuestionDialog({ "question": _("Are you sure you want to delete EVERYTHING under " "\n<b>%s</b>?\n (This can't be undone)") % game_dir, "title": _("CONFIRM DANGEROUS OPERATION"), }) if dlg.result != Gtk.ResponseType.YES: widget.set_sensitive(True) return remove_from_library = self.game.remove(remove_from_library, remove_contents) self.callback(self.game.id, remove_from_library) self.on_close()
class GameDialogCommon: """Mixin for config dialogs""" no_runner_label = "Select a runner in the Game Info tab" def __init__(self): self.notebook = None self.vbox = None self.name_entry = None self.runner_box = None @staticmethod def build_scrolled_window(widget): scrolled_window = Gtk.ScrolledWindow() scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) scrolled_window.add(widget) return scrolled_window def build_notebook(self): self.notebook = Gtk.Notebook() self.vbox.pack_start(self.notebook, True, True, 10) def build_tabs(self, config_level): if config_level == "game": self._build_info_tab() self._build_game_tab() self._build_runner_tab(config_level) self._build_system_tab(config_level) def _build_info_tab(self): info_box = VBox() if self.game: info_box.pack_start(self._get_banner_box(), False, False, 5) # Banner info_box.pack_start(self._get_name_box(), False, False, 5) # Game name if self.game: info_box.pack_start(self._get_slug_box(), False, False, 5) # Game id self.runner_box = self._get_runner_box() info_box.pack_start(self.runner_box, False, False, 5) # Runner info_box.pack_start(self._get_year_box(), False, False, 5) # Year info_sw = self.build_scrolled_window(info_box) self._add_notebook_tab(info_sw, "Game info") def _get_name_box(self): box = Gtk.Box(spacing=12, margin_right=12, margin_left=12) label = Label("Name") box.pack_start(label, False, False, 0) self.name_entry = Gtk.Entry() if self.game: self.name_entry.set_text(self.game.name) box.pack_start(self.name_entry, True, True, 0) return box def _get_slug_box(self): slug_box = Gtk.Box(spacing=12, margin_right=12, margin_left=12) label = Label("Identifier") slug_box.pack_start(label, False, False, 0) self.slug_entry = SlugEntry() self.slug_entry.set_text(self.game.slug) self.slug_entry.set_sensitive(False) self.slug_entry.connect("activate", self.on_slug_entry_activate) slug_box.pack_start(self.slug_entry, True, True, 0) self.slug_change_button = Gtk.Button("Change") self.slug_change_button.connect("clicked", self.on_slug_change_clicked) slug_box.pack_start(self.slug_change_button, False, False, 0) return slug_box def _get_runner_box(self): runner_box = Gtk.Box(spacing=12, margin_right=12, margin_left=12) runner_label = Label("Runner") runner_box.pack_start(runner_label, False, False, 0) self.runner_dropdown = self._get_runner_dropdown() runner_box.pack_start(self.runner_dropdown, True, True, 0) install_runners_btn = Gtk.Button("Install runners") install_runners_btn.connect("clicked", self.on_install_runners_clicked) runner_box.pack_start(install_runners_btn, True, True, 0) return runner_box def _get_banner_box(self): banner_box = Gtk.Box(spacing=12, margin_right=12, margin_left=12) label = Label("") banner_box.pack_start(label, False, False, 0) self.banner_button = Gtk.Button() self._set_image("banner") self.banner_button.connect("clicked", self.on_custom_image_select, "banner") banner_box.pack_start(self.banner_button, False, False, 0) reset_banner_button = Gtk.Button.new_from_icon_name( "edit-clear", Gtk.IconSize.MENU ) reset_banner_button.set_relief(Gtk.ReliefStyle.NONE) reset_banner_button.set_tooltip_text("Remove custom banner") reset_banner_button.connect( "clicked", self.on_custom_image_reset_clicked, "banner" ) banner_box.pack_start(reset_banner_button, False, False, 0) self.icon_button = Gtk.Button() self._set_image("icon") self.icon_button.connect("clicked", self.on_custom_image_select, "icon") banner_box.pack_start(self.icon_button, False, False, 0) reset_icon_button = Gtk.Button.new_from_icon_name( "edit-clear", Gtk.IconSize.MENU ) reset_icon_button.set_relief(Gtk.ReliefStyle.NONE) reset_icon_button.set_tooltip_text("Remove custom icon") reset_icon_button.connect("clicked", self.on_custom_image_reset_clicked, "icon") banner_box.pack_start(reset_icon_button, False, False, 0) return banner_box def _get_year_box(self): box = Gtk.Box(spacing=12, margin_right=12, margin_left=12) label = Label("Release year") box.pack_start(label, False, False, 0) self.year_entry = NumberEntry() if self.game: self.year_entry.set_text(str(self.game.year or "")) box.pack_start(self.year_entry, True, True, 0) return box def _set_image(self, image_format): assert image_format in ("banner", "icon") image = Gtk.Image() game_slug = self.game.slug if self.game else "" image.set_from_pixbuf(get_pixbuf_for_game(game_slug, image_format)) if image_format == "banner": self.banner_button.set_image(image) else: self.icon_button.set_image(image) def _set_icon_image(self): image = Gtk.Image() game_slug = self.game.slug if self.game else "" image.set_from_pixbuf(get_pixbuf_for_game(game_slug, "banner")) self.banner_button.set_image(image) def _get_runner_dropdown(self): runner_liststore = self._get_runner_liststore() runner_dropdown = Gtk.ComboBox.new_with_model(runner_liststore) runner_dropdown.set_id_column(1) runner_index = 0 if self.runner_name: for runner in runner_liststore: if self.runner_name == str(runner[1]): break runner_index += 1 runner_dropdown.set_active(runner_index) runner_dropdown.connect("changed", self.on_runner_changed) cell = Gtk.CellRendererText() cell.props.ellipsize = Pango.EllipsizeMode.END runner_dropdown.pack_start(cell, True) runner_dropdown.add_attribute(cell, "text", 0) return runner_dropdown @staticmethod def _get_runner_liststore(): """Build a ListStore with available runners.""" runner_liststore = Gtk.ListStore(str, str) runner_liststore.append(("Select a runner from the list", "")) for runner in runners.get_installed(): description = runner.description runner_liststore.append( ("%s (%s)" % (runner.human_name, description), runner.name) ) return runner_liststore def on_slug_change_clicked(self, widget): if self.slug_entry.get_sensitive() is False: widget.set_label("Apply") self.slug_entry.set_sensitive(True) else: self.change_game_slug() def on_slug_entry_activate(self, widget): self.change_game_slug() def change_game_slug(self): self.slug = self.slug_entry.get_text() self.slug_entry.set_sensitive(False) self.slug_change_button.set_label("Change") def on_install_runners_clicked(self, _button): runners_dialog = gui.runnersdialog.RunnersDialog() runners_dialog.connect("runner-installed", self._update_runner_dropdown) def _update_runner_dropdown(self, _widget): active_id = self.runner_dropdown.get_active_id() self.runner_dropdown.set_model(self._get_runner_liststore()) self.runner_dropdown.set_active_id(active_id) def _build_game_tab(self): if self.game and self.runner_name: self.game.runner_name = self.runner_name try: self.game.runner = runners.import_runner(self.runner_name)() except runners.InvalidRunner: pass self.game_box = GameBox(self.lutris_config, self.game) game_sw = self.build_scrolled_window(self.game_box) elif self.runner_name: game = Game(None) game.runner_name = self.runner_name self.game_box = GameBox(self.lutris_config, game) game_sw = self.build_scrolled_window(self.game_box) else: game_sw = Gtk.Label(label=self.no_runner_label) self._add_notebook_tab(game_sw, "Game options") def _build_runner_tab(self, config_level): if self.runner_name: self.runner_box = RunnerBox(self.lutris_config) runner_sw = self.build_scrolled_window(self.runner_box) else: runner_sw = Gtk.Label(label=self.no_runner_label) self._add_notebook_tab(runner_sw, "Runner options") def _build_system_tab(self, config_level): self.system_box = SystemBox(self.lutris_config) self.system_sw = self.build_scrolled_window(self.system_box) self._add_notebook_tab(self.system_sw, "System options") def _add_notebook_tab(self, widget, label): self.notebook.append_page(widget, Gtk.Label(label=label)) def build_action_area(self, button_callback, callback2=None): self.action_area.set_layout(Gtk.ButtonBoxStyle.EDGE) # Advanced settings checkbox checkbox = Gtk.CheckButton(label="Show advanced options") value = settings.read_setting("show_advanced_options") if value == "True": checkbox.set_active(value) checkbox.connect("toggled", self.on_show_advanced_options_toggled) self.action_area.pack_start(checkbox, False, False, 5) # Buttons hbox = Gtk.Box() cancel_button = Gtk.Button(label="Cancel") cancel_button.connect("clicked", self.on_cancel_clicked) hbox.pack_start(cancel_button, True, True, 10) save_button = Gtk.Button(label="Save") if callback2: save_button.connect("clicked", button_callback, callback2) else: save_button.connect("clicked", button_callback) hbox.pack_start(save_button, True, True, 0) self.action_area.pack_start(hbox, True, True, 0) def on_show_advanced_options_toggled(self, checkbox): value = True if checkbox.get_active() else False settings.write_setting("show_advanced_options", value) self._set_advanced_options_visible(value) def _set_advanced_options_visible(self, value): """Change visibility of advanced options across all config tabs.""" widgets = self.system_box.get_children() if self.runner_name: widgets += self.runner_box.get_children() if self.game: widgets += self.game_box.get_children() for widget in widgets: if widget.get_style_context().has_class("advanced"): widget.set_visible(value) if value: widget.set_no_show_all(not value) widget.show_all() def on_runner_changed(self, widget): """Action called when runner drop down is changed.""" runner_index = widget.get_active() current_page = self.notebook.get_current_page() if runner_index == 0: self.runner_name = None self.lutris_config = LutrisConfig() else: self.runner_name = widget.get_model()[runner_index][1] self.lutris_config = LutrisConfig( runner_slug=self.runner_name, game_config_id=self.game_config_id, level="game", ) self._rebuild_tabs() self.notebook.set_current_page(current_page) def _rebuild_tabs(self): for i in range(self.notebook.get_n_pages(), 1, -1): self.notebook.remove_page(i - 1) self._build_game_tab() self._build_runner_tab("game") self._build_system_tab("game") self.show_all() def on_cancel_clicked(self, _widget=None, _event=None): """Dialog destroy callback.""" if self.game: self.game.load_config() self.destroy() def is_valid(self): name = self.name_entry.get_text() if not self.runner_name: ErrorDialog("Runner not provided") return False if not name: ErrorDialog("Please fill in the name") return False if ( self.runner_name in ("steam", "winesteam") and self.lutris_config.game_config.get("appid") is None ): ErrorDialog("Steam AppId not provided") return False return True def on_save(self, _button, callback=None): """Save game info and destroy widget. Return True if success.""" if not self.is_valid(): return False name = self.name_entry.get_text() if not self.slug: self.slug = slugify(name) if not self.game: self.game = Game() year = None if self.year_entry.get_text(): year = int(self.year_entry.get_text()) if self.lutris_config.game_config_id == TEMP_CONFIG: self.lutris_config.game_config_id = self.get_config_id() # Delete the old copy of the game if the runner changes runner_class = runners.import_runner(self.runner_name) runner = runner_class(self.lutris_config) if self.game.id and self.game.platform != runner.get_platform(): self.game.remove() self.game.runner_name = self.runner_name self.game.name = name self.game.slug = self.slug self.game.year = year self.game.config = self.lutris_config fps_limit = self.game.config.system_config.get("fps_limit", None) if fps_limit: try: int(fps_limit) except ValueError: ErrorDialog("Fps limit only accept numbers") return self.game.directory = runner.game_path self.game.is_installed = True if self.runner_name in ("steam", "winesteam"): self.game.steamid = self.lutris_config.game_config["appid"] self.game.set_platform_from_runner() self.game.save() self.destroy() self.saved = True if callback: callback() def on_custom_image_select(self, widget, image_type): dialog = Gtk.FileChooserDialog( "Please choose a custom image", self, Gtk.FileChooserAction.OPEN, ( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK, ), ) image_filter = Gtk.FileFilter() image_filter.set_name("Images") image_filter.add_pixbuf_formats() dialog.add_filter(image_filter) response = dialog.run() if response == Gtk.ResponseType.OK: image_path = dialog.get_filename() if image_type == "banner": self.game.has_custom_banner = True dest_path = datapath.get_banner_path(self.game.slug) size = BANNER_SIZE file_format = "jpeg" else: self.game.has_custom_icon = True dest_path = datapath.get_icon_path(self.game.slug) size = ICON_SIZE file_format = "png" pixbuf = get_pixbuf(image_path, size) pixbuf.savev(dest_path, file_format, [], []) self._set_image(image_type) if image_type == "icon": resources.udpate_desktop_icons() dialog.destroy() def on_custom_image_reset_clicked(self, widget, image_type): if image_type == "banner": self.game.has_custom_banner = False dest_path = datapath.get_banner_path(self.game.slug) elif image_type == "icon": self.game.has_custom_icon = False dest_path = datapath.get_icon_path(self.game.slug) else: raise ValueError("Unsupported image type %s", image_type) os.remove(dest_path) self._set_image(image_type)
name = name.replace("[%s]" % flag, "") if ", The" in name: name = "The %s" % name.replace(", The", "") name = name.strip() print("Importing '%s'" % name) slug = slugify(name) core = core_matches[ext] config = { "game": { "core": core_matches[ext], "main_file": os.path.join(dirname, filename) } } installer_slug = "%s-libretro-%s" % (slug, core) existing_game = get_games(filters={ "installer_slug": installer_slug, "installed": "1" }) if existing_game: game = Game(existing_game[0]["id"]) game.remove() configpath = write_game_config(slug, config) game_id = add_game(name=name, runner="libretro", slug=slug, directory=dirname, installed=1, installer_slug=installer_slug, configpath=configpath) print("Imported %s" % name)
class UninstallGameDialog(GtkBuilderDialog): glade_file = "dialog-uninstall-game.ui" dialog_object = "uninstall-game-dialog" @staticmethod def substitute_label(widget, name, replacement): if hasattr(widget, "get_text"): get_text = widget.get_text set_text = widget.set_text elif hasattr(widget, "get_label"): get_text = widget.get_label set_text = widget.set_label else: raise TypeError("Unsupported type %s" % type(widget)) set_text(get_text().replace("{%s}" % name, replacement)) def initialize(self, game_id=None, callback=None): self.game = Game(game_id) self.callback = callback runner = self.game.runner self.substitute_label( self.builder.get_object("description_label"), "game", self.game.name ) self.substitute_label( self.builder.get_object("remove_from_library_button"), "game", self.game.name, ) remove_contents_button = self.builder.get_object("remove_contents_button") if self.game.is_installed: path = self.game.directory or "" if hasattr(runner, "own_game_remove_method"): remove_contents_button.set_label(runner.own_game_remove_method) remove_contents_button.set_active(True) else: try: default_path = runner.default_path except AttributeError: default_path = "/" if is_removeable(path, excludes=[default_path]): remove_contents_button.set_active(True) else: remove_contents_button.set_sensitive(False) path = "No game folder" path = reverse_expanduser(path) self.substitute_label(remove_contents_button, "path", path) label = remove_contents_button.get_child() label.set_use_markup(True) label.set_line_wrap(True) label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR) else: remove_contents_button.hide() cancel_button = self.builder.get_object("cancel_button") cancel_button.connect("clicked", self.on_close) apply_button = self.builder.get_object("apply_button") apply_button.connect("clicked", self.on_apply_button_clicked) def on_apply_button_clicked(self, widget): widget.set_sensitive(False) remove_from_library_button = self.builder.get_object( "remove_from_library_button" ) remove_from_library = remove_from_library_button.get_active() remove_contents_button = self.builder.get_object("remove_contents_button") remove_contents = remove_contents_button.get_active() if remove_contents and not hasattr(self.game.runner, "no_game_remove_warning"): game_dir = self.game.directory.replace("&", "&") dlg = QuestionDialog( { "question": "Are you sure you want to delete EVERYTHING under " "\n<b>%s</b>?\n (This can't be undone)" % game_dir, "title": "CONFIRM DANGEROUS OPERATION", } ) if dlg.result != Gtk.ResponseType.YES: widget.set_sensitive(True) return remove_from_library = self.game.remove(remove_from_library, remove_contents) self.callback(self.game.id, remove_from_library) self.on_close()