Exemple #1
0
    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()
Exemple #2
0
    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)
Exemple #3
0
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()
Exemple #5
0
    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)
Exemple #6
0
    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)
Exemple #7
0
    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)
Exemple #8
0
    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)
Exemple #9
0
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
Exemple #10
0
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()
Exemple #11
0
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("&", "&amp;")
            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()
Exemple #13
0
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)
Exemple #14
0
        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)
Exemple #15
0
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("&", "&amp;")
            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()