Esempio n. 1
0
    def __init__(self):
        Gtk.Window.__init__(self, title=cn.App.application_name)

        self.hbar = hb.Headerbar(self)
        self.set_titlebar(self.hbar)

        HGtk = hl.HGtk()
        HGtk.set_dark_mode(1)

        self.stack = sk.Stack(self)
        self.add(self.stack)

        self.screen = Gdk.Screen.get_default()
        self.css_provider = Gtk.CssProvider()

        try:
            # development css
            self.css_provider.load_from_path('../data/style.css')
        except GLib.Error:
            # production css
            self.css_provider.load_from_path(
                '/usr/share/com.github.mirkobrombin.bottles/bottles/style.css')
        except GLib.Error:
            # different python version css
            self.css_provider.load_from_path('/usr/bin/bottles/style.css')
        except GLib.Error:
            print('Couldn\'t load style.css')
            exit(1)

        self.context = Gtk.StyleContext()
        self.context.add_provider_for_screen(self.screen, self.css_provider,
                                             Gtk.STYLE_PROVIDER_PRIORITY_USER)
Esempio n. 2
0
    def __init__(self, parent):
        Gtk.HeaderBar.__init__(self)
        self.parent = parent
        self.wine = w.Wine(self)
        self.HGtk = hl.HGtk()

        locale_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locale')

        try:
            current_locale, encoding = locale.getdefaultlocale()
            translate = gettext.translation ('bottles', locale_path, [current_locale] )
            _ = translate.gettext
        except FileNotFoundError:
            _ = str

        self.set_show_close_button(True)
        self.props.title = cn.App.application_name

        # help button
        # self.help = Gtk.Button.new_from_icon_name("help-contents", Gtk.IconSize.LARGE_TOOLBAR)
        # self.help.connect("clicked", self.on_help_clicked)
        # self.pack_end(self.help)

        # trash button
        self.trash = Gtk.Button()
        self.trash = Gtk.Button.new_from_icon_name("edit-delete", Gtk.IconSize.LARGE_TOOLBAR)
        self.trash.connect("clicked", self.on_trash_clicked)
        self.pack_end(self.trash)

        # properties button
        self.properties = Gtk.Button()
        self.properties = Gtk.Button.new_from_icon_name("document-properties", Gtk.IconSize.LARGE_TOOLBAR)
        self.properties.connect("clicked", self.on_properties_clicked)
        self.pack_end(self.properties)

        # save button
        self.save = Gtk.Button(_('Save'))
        self.save.connect("clicked", self.on_save_clicked)
        self.HGtk.add_class(self.save, "suggested-action")
        self.pack_end(self.save)

        # spinner button
        self.spinner = Gtk.Spinner()
        self.pack_end(self.spinner)

        # back button
        self.back = Gtk.Button(_('Return'))
        self.back.connect("clicked", self.on_back_clicked)
        Gtk.StyleContext.add_class(self.back.get_style_context(), "back-button")
        self.pack_start(self.back)
Esempio n. 3
0
    def __init__(self):
        Gtk.Window.__init__(self, title=cn.App.application_name)

        self.hbar = hb.Headerbar(self)
        self.set_titlebar(self.hbar)

        HGtk = hl.HGtk()
        HGtk.set_dark_mode(1)

        self.stack = sk.Stack(self)
        self.add(self.stack)

        self.screen = Gdk.Screen.get_default()
        self.css_provider = Gtk.CssProvider()
        try:
            self.css_provider.load_from_path('../data/style.css')
        except GLib.Error:
            self.css_provider.load_from_path(
                '/usr/local/bin/bottles/style.css')
        self.context = Gtk.StyleContext()
        self.context.add_provider_for_screen(self.screen, self.css_provider,
                                             Gtk.STYLE_PROVIDER_PRIORITY_USER)
Esempio n. 4
0
class Wine:
    HGtk = hl.HGtk()
    working_dir = str(Path.home()) + "/.Bottles/"
    working_dir_link = str(Path.home()) + "/My Bottles"
    POL_working_dir = str(Path.home()) + "/.PlayOnLinux/wineprefix/"

    def __init__(self, parent):
        self.parent = parent

    def get_wine_version():
        version = subprocess.check_output(["wine", "--version"])
        return str(version).replace("b'wine-", "").replace("\\n'", "")

    def check_work_dir(self):
        HLog.info(
            _('Checking for .Bottles directory on: %s' % self.working_dir))
        if not os.path.exists(self.working_dir):
            HLog.warning(
                '[NO_BOTTLES_DIR] ' +
                _('I did not find the Bottles directory, creating in: %s' %
                  self.working_dir))
            os.mkdir(self.working_dir)
            HLog.info(
                _('Directory created, now creating the symlink: %s' %
                  self.working_dir_link))
            os.symlink(self.working_dir, self.working_dir_link)
        else:
            HLog.success(_('Bottles directory founded!'))

    def run_winecfg(self, working_dir):
        T_Winecfg(working_dir).start()

    def run_winetricks(self, working_dir):
        T_Wintricks(working_dir).start()

    def run_console(self, working_dir):
        T_Console(working_dir).start()

    def run_monitor(self, working_dir):
        T_Monitor(working_dir).start()

    def run_control(self, working_dir):
        T_Control(working_dir).start()

    def run_regedit(self, working_dir):
        T_Regedit(working_dir).start()

    def run_uninstaller(self, working_dir):
        T_Uninstaller(working_dir).start()

    def run_wineboot(self, working_dir):
        T_Wineboot(working_dir).start()

    def run_debug(self, working_dir):
        T_Debug(working_dir).start()

    def run_clone(self, working_dir):
        self.parent.parent.parent.hbar.spinner.set_tooltip_text(
            _('Cloning the bottle..'))
        self.parent.parent.parent.hbar.spinner.start()
        T_Clone(working_dir, self.parent).start()

    def run_software(self, working_dir, file_src):
        T_Software(working_dir, file_src).start()

    def check_special_chars(self, string):
        if not re.match(r'^\w+$', string):
            HLog.error('[BOTTLE_NAME_ERROR] ' +
                       _('Bottle name can not contain special characters'))
            message_dialog = Gtk.MessageDialog.new_with_image_from_icon_name(
                "BOTTLE_NAME_ERROR",
                _('Bottle name can not contain special characters'),
                "dialog-warning", Gtk.ButtonsType.CANCEL)
            message_dialog.set_transient_for(self.parent.parent)
            message_dialog.set_flags = Gtk.DialogFlags.MODAL
            message_dialog.connect("response", self.md_ok)
            message_dialog.show_all()
            return False
        else:
            return True

    def create_bottle(self, name, arch):
        if self.check_special_chars(name):
            # create dir
            self.working_prefix_dir = self.working_dir + "prefix_" + name
            if not os.path.exists(self.working_prefix_dir):
                # create new bottle
                HLog.info(_('Creating new bottle with arch: %s' % arch))
                if arch == "32 Bit":
                    w_arch = "win32"
                else:
                    w_arch = "win64"
                subprocess.call("WINEPREFIX=" + self.working_prefix_dir +
                                " WINEARCH=" + w_arch + " wine wineboot",
                                shell=True)
                # create version file
                version_bottle = self.working_prefix_dir + "/version.bottle"
                with open(version_bottle, "w") as f:
                    f.write(arch)
            else:
                HLog.warning('[BOTTLE_ALREADY_EXISTS] ' + _(
                    'There is already a bottle named: %s, redirecting to it.' %
                    name))
            self.detail_bottle(name)

            # re-fill list
            lt = self.parent.parent.stack.list_all
            lt.generate_entries(True)

    def list_bottles(self):
        HLog.info(_('Generating the bottles list..'))
        bottles = []
        try:
            walk = next(os.walk(self.working_dir))[1]
            for w in walk:
                # Arch
                try:
                    with open(self.working_dir + w + "/version.bottle",
                              'r') as arch_f:
                        arch = arch_f.read().replace('\n', '')
                except FileNotFoundError:
                    HLog.warning('[NO_VERSION_FILE] ' + _(
                        'I can not find the version file for %s, I assume it is a 32 Bit.'
                        % w))
                    arch = "32 Bit"
                # Size
                size = subprocess.run(['du', '-sh', self.working_dir + w],
                                      stdout=subprocess.PIPE)
                size = str(size.stdout).split('\\t', 1)[0].replace("b'", '')
                bottles.append([w, arch, size])
        except StopIteration:
            pass
        return bottles

    def list_POLs(self):
        HLog.info(_('Generating the POL wineprefix list..'))
        POLs = []
        try:
            walk = next(os.walk(self.POL_working_dir))[1]
            for w in walk:
                try:
                    with open(self.POL_working_dir + w + "/playonlinux.cfg",
                              'r') as f:
                        rows = f.readlines()
                        # Arch
                        if rows[0].startswith("ARCH="):
                            arch = rows[0]
                        elif rows[1].startswith("ARCH="):
                            arch = rows[1]
                        else:
                            arch = rows[2]
                        arch = arch.replace('\n', '').replace('ARCH=', '')
                        arch = '32 Bit' if arch == 'x86' else '64 Bit'
                        # Version
                        if rows[0].startswith("VERSION="):
                            version = rows[0]
                        elif rows[1].startswith("VERSION="):
                            version = rows[1]
                        else:
                            version = rows[2]
                        version = version.replace('\n',
                                                  '').replace('VERSION=', '')
                        # Size
                        size = subprocess.run(
                            ['du', '-sh', self.POL_working_dir + w],
                            stdout=subprocess.PIPE)
                        size = str(size.stdout).split('\\t',
                                                      1)[0].replace("b'", '')
                    POLs.append([w, arch, version, size])
                except FileNotFoundError:
                    HLog.warning('[POL_WITHOUT_CONFIG] ' + _(
                        'The POL wineprefix: %s does not contain the configuration file. Ignoring'
                        % w))
                    pass
        except StopIteration:
            pass
        return POLs

    def remove_bottle(self, bottle_name):
        HLog.info(_('Removing bottle: %s' % bottle_name))
        if self.check_special_chars(bottle_name):
            shutil.rmtree(self.working_dir + bottle_name, ignore_errors=True)

            # re-fill list
            lt = self.parent.parent.stack.list_all
            lt.generate_entries(True)

    def convert_POL(self, POL_name, arch):
        message_dialog = Granite.MessageDialog.new_with_image_from_icon_name(
            "POL_TO_BOTTLE_DISCLAIMER",
            _('This process converts the POL wineprefix into a bottle. \n\nNote that Bottles uses the Wine version installed on the system. After the conversion, the programs installed in the bottle may not work properly.\n\nThe original version of the POL wineprefix will remain intact.'
              ), "dialog-warning", Gtk.ButtonsType.CANCEL)
        message_dialog.set_transient_for(self.parent.parent)
        message_dialog.set_flags = Gtk.DialogFlags.MODAL
        message_dialog.connect("response", self.md_ok)
        message_dialog.show_all()
        self.parent.spinner.set_tooltip_text(_('Converting POL to bottle..'))
        self.parent.spinner.start()
        T_POL_Convert(self.working_dir, self.POL_working_dir, POL_name, arch,
                      self.parent).start()

    def detail_bottle(self, name):
        HLog.info(_('Loading details for bottle: %s' % name))
        # get detail data
        dt = self.parent.parent.stack.detail
        self.parent.properties.hide()
        self.parent.trash.hide()
        if name.find("prefix_") == -1:
            dt.working_dir = self.working_dir + "prefix_" + name
        else:
            dt.working_dir = self.working_dir + name
        with open(dt.working_dir + "/version.bottle", 'r') as arch_f:
            arch = arch_f.read().replace('\n', '')
        HLog.info(_('This is a %s bottle' % arch))
        if arch == "32 Bit":
            version = subprocess.check_output(["wine", "--version"])
        else:
            version = subprocess.check_output(["wine64", "--version"])
        version = str(version)
        version = version.replace("b'", "")
        version = version.replace("\\n'", "")

        # remove prefix_ from bottle name
        name = name.replace("prefix_", "")

        # set detail title and description
        self.parent.props.title = name + " " + version + " (" + arch + ")"

        # change stack to detail
        self.parent.save.hide()
        self.parent.parent.stack.stack.set_visible_child_name("detail")

    '''
    MessageDialog responses
    '''

    def md_ok(self, widget, response_id):
        widget.destroy()
Esempio n. 5
0
class Search:
    HGtk = hl.HGtk()
    HUser = hl.HUser()
    HPath = hl.HPath()
    HString = hl.HString()
    '''
    Define the names of the lists we use for indexing search results
    '''
    index_data = [
    ]  # contains the list of windows and applications (id, str, str)
    index_windows_act = []  # contains xid for windows (id, str)
    index_apps_act = []  # contains all commands for applications (id, str)

    search_entry = None
    i = 0

    # Params
    item_height = 26  # This is the height size for each treeview row
    trim_limit = 50  # Limit characters per line

    # Load Translate
    try:
        current_locale, encoding = locale.getdefaultlocale()
        locale_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                   'locale')
        translate = gettext.translation(cn.App.application_shortname,
                                        locale_path, [current_locale])
        _ = translate.gettext
    except FileNotFoundError:
        _ = str

    def __init__(self, parent):
        self.parent = parent  # I use this to reach the window and widgets
        self.search_entry = self.parent.search_entry

    '''
    Here we create the application index (all apps from /usr/share/applications)
    '''

    def make_index_apps(self):
        for appinfo in Gio.AppInfo.get_all():
            # <https://developer.gnome.org/pygobject/stable/class-gioappinfo.html>
            if appinfo.supports_files() or appinfo.supports_uris():
                appname = appinfo.get_name()
                appcmd = appinfo.get_commandline()
                self.index_data.append([
                    self.HString.trim(appname, self.trim_limit), self.i,
                    "[app]"
                ])
                self.index_apps_act.append([self.index_data[-1][1], appcmd])
                self.i = self.i + 1

    '''
    Here we create the windows index (all open windows)
    '''

    def make_index_windows(self):
        windows = Wnck.Screen.get_default().get_windows()
        for w in windows:
            self.index_data.append([
                self.HString.trim(w.get_name(), self.trim_limit), self.i,
                "[win]"
            ])
            self.index_windows_act.append(
                [self.index_data[-1][0], w.get_xid()])
            self.i = self.i + 1

    '''
    Create index_data and index_final
    '''

    def index(self):
        self.i = 0

        # To avoid duplicate results, empty indexes
        self.index_apps_act = []
        self.index_windows_act = []
        self.index_data = []

        # Generate indexes
        self.make_index_apps()
        self.make_index_windows()

    '''
    Shows a window from its xid
    '''

    def show_window(self, xid):
        print(Wnck.Window.get(xid).activate(time.time()))

    '''
    Search start from here.
    Here I filter the indexes and get the "filter" list
    '''

    def find(self, search_text):
        # Get widgets
        self.results = self.parent.stack.results

        # Check for empty
        if search_text == "":
            self.results.resize(0)
        else:
            self.index_data = []
            self.index()
            found = []
            if search_text.startswith("!calc"):
                clean_data = search_text.replace("!calc ", "")
                found.append(b_calc.run(clean_data))
            else:
                # Get data
                for f, i, t in self.index_data:
                    if search_text in f:
                        found.append([f, i, t])
            if len(found) == 0:
                found.append([self._('I did not find anything'), 0, "[none]"])

            # Resize results box (calculate the number of results not > 250)
            estimated_results_size = len(found) * self.item_height
            if estimated_results_size > 250:
                self.results.resize(250)
            else:
                self.results.resize(estimated_results_size)

            # Return results
            self.results.generate_items(found, True)

    '''
    Check result type and call action
    '''

    def do(self, result):
        if result[2] == "[none]" or result[2] == "[bang]":
            pass
        elif result[2] == "[app]":  # Application detected
            for i, a in self.index_apps_act:
                if result[1] == i:
                    os.system(a)
        elif result[2] == "[win]":  # Window detected
            for i, x in self.index_windows_act:
                if result[1] == i:
                    self.show_window(x)
Esempio n. 6
0
class Window(Gtk.Dialog):
    HGtk = hl.HGtk()

    def __init__(self):
        Gtk.Dialog.__init__(self,
                            title=cn.App.application_name,
                            width_request=550,
                            resizable=False,
                            icon_name=cn.App.application_id)
        # Load Translate
        try:
            current_locale, encoding = locale.getdefaultlocale()
            locale_path = os.path.join(
                os.path.abspath(os.path.dirname(__file__)), 'locale')
            translate = gettext.translation(cn.App.application_shortname,
                                            locale_path, [current_locale])
            _ = translate.gettext
        except FileNotFoundError:
            _ = str

        # Dialog params
        self.HGtk.add_class(self, Gtk.STYLE_CLASS_FLAT)
        self.set_deletable(False)
        self.set_border_width(0)

        # Content params
        self.get_action_area().destroy()
        self.main_content = self.get_content_area()
        self.main_content.set_spacing(0)
        self.main_content.set_border_width(0)

        # Search Box
        self.search_box = Gtk.Frame()
        self.HGtk.add_class(self.search_box, "SearchBox")
        self.main_content.add(self.search_box)

        # Search Entry
        self.search_entry = Gtk.SearchEntry()
        self.HGtk.add_class(self.search_entry, "SearchEntry")
        self.search_entry.set_placeholder_text(_('Search..'))
        self.search_entry.connect("key-release-event",
                                  self.on_entry_key_release)
        self.search_box.add(self.search_entry)

        # Stack
        self.stack = sk.Stack(self)
        self.main_content.add(self.stack)

        # Search
        self.Search = sh.Search(self)

        # Style
        self.screen = Gdk.Screen.get_default()
        self.css_provider = Gtk.CssProvider()
        try:
            self.css_provider.load_from_path('../data/style.css')
        except GLib.Error:
            self.css_provider.load_from_path(
                '/usr/local/bin/kangaroo/style.css')
        except GLib.Error:
            self.css_provider.load_from_path('/usr/bin/kangaroo/style.css')
        except GLib.Error:
            print('Couldn\'t load style.css')
            exit(1)
        self.context = Gtk.StyleContext()
        self.context.add_provider_for_screen(self.screen, self.css_provider,
                                             Gtk.STYLE_PROVIDER_PRIORITY_USER)

    def on_entry_key_release(self, entry, ev, data=None):
        # If Escape pressed, close Kangaroo
        if ev.keyval == Gdk.KEY_Escape:
            exit()
        search_text = entry.get_text()
        self.Search.find(search_text)
Esempio n. 7
0
    def __init__(self, parent):
        Gtk.HeaderBar.__init__(self)
        self.parent = parent
        self.wine = w.Wine(self)
        self.HGtk = hl.HGtk()
        self.set_name("WineHeaderbar")

        locale_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locale')

        try:
            current_locale, encoding = locale.getdefaultlocale()
            translate = gettext.translation (cn.App.application_shortname, locale_path, [current_locale] )
            _ = translate.gettext
        except FileNotFoundError:
            _ = str

        self.set_show_close_button(True)
        self.props.title = cn.App.application_name

        # help button
        # self.help = Gtk.Button.new_from_icon_name("help-contents", Gtk.IconSize.LARGE_TOOLBAR)
        # self.help.connect("clicked", self.on_help_clicked)
        # self.pack_end(self.help)

        # trash button
        self.trash = Gtk.Button()
        self.trash = Gtk.Button.new_from_icon_name("user-trash-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
        self.trash.connect("clicked", self.on_trash_clicked)
        self.pack_end(self.trash)

        # properties button
        self.properties = Gtk.Button()
        self.properties = Gtk.Button.new_from_icon_name("document-properties-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
        self.properties.connect("clicked", self.on_properties_clicked)
        self.pack_end(self.properties)

        # save button
        self.save = Gtk.Button(_('Save'))
        self.save.connect("clicked", self.on_save_clicked)
        self.HGtk.add_class(self.save, "suggested-action")
        self.pack_end(self.save)

        # start button
        # [INFO] This button shows the applications installed in the selected bottle
        # self.start = Gtk.Button()
        # self.start = Gtk.Button.new_from_icon_name("applications-other", Gtk.IconSize.LARGE_TOOLBAR)
        # self.start.connect("clicked", self.on_start_clicked)
        # self.pack_end(self.start)

        # convert button
        self.convert = Gtk.Button()
        self.convert = Gtk.Button.new_from_icon_name("rotate-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
        self.convert.connect("clicked", self.on_convert_clicked)
        self.pack_end(self.convert)

        # refresh button
        self.refresh = Gtk.Button()
        self.refresh = Gtk.Button.new_from_icon_name("view-refresh-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
        self.refresh.connect("clicked", self.on_refresh_clicked)
        self.pack_end(self.refresh)

        # spinner button
        self.spinner = Gtk.Spinner()
        self.pack_end(self.spinner)

        # back button
        self.back = Gtk.Button.new_from_icon_name("go-previous-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
        self.back.connect("clicked", self.on_back_clicked)
        Gtk.StyleContext.add_class(self.back.get_style_context(), "back-button")
        self.pack_start(self.back)
Esempio n. 8
0
class Wine:
    HGtk = hl.HGtk()
    working_dir = str(Path.home()) + "/.Bottles/"
    working_dir_link = str(Path.home()) + "/My Bottles"

    try:
        current_locale, encoding = locale.getdefaultlocale()
        locale_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                   'locale')
        translate = gettext.translation('bottles', locale_path,
                                        [current_locale])
        _ = translate.gettext
    except FileNotFoundError:
        _ = str

    def __init__(self, parent):
        self.parent = parent

    def check_work_dir(self):
        if not os.path.exists(self.working_dir):
            os.mkdir(self.working_dir)
            os.symlink(self.working_dir, self.working_dir_link)

    def run_winecfg(self, working_dir):
        T_Winecfg(working_dir).start()

    def run_winetricks(self, working_dir):
        T_Wintricks(working_dir).start()

    def run_console(self, working_dir):
        T_Console(working_dir).start()

    def run_monitor(self, working_dir):
        T_Monitor(working_dir).start()

    def run_control(self, working_dir):
        T_Control(working_dir).start()

    def run_regedit(self, working_dir):
        T_Regedit(working_dir).start()

    def run_uninstaller(self, working_dir):
        T_Uninstaller(working_dir).start()

    def run_wineboot(self, working_dir):
        T_Wineboot(working_dir).start()

    def run_debug(self, working_dir):
        T_Debug(working_dir).start()

    def run_clone(self, working_dir):
        self.parent.parent.parent.hbar.spinner.start()
        T_Clone(working_dir, self.parent).start()

    def run_software(self, working_dir, file_src):
        T_Software(working_dir, file_src).start()

    def check_special_chars(self, string):
        if not re.match(r'^\w+$', string):
            alert = al.Alert(
                self.parent.parent, "BOTTLE_NAME_ERROR: " +
                self._('Bottle name can not contain special characters'), 600,
                90)
            response = alert.run()
            if response == Gtk.ResponseType.OK:
                alert.destroy()
            return False
        else:
            return True

    def create_bottle(self, name, arch):
        if self.check_special_chars(name):
            print(
                self._('Creating a bottle with name:') + name + " and arch: " +
                arch)

            # create dir
            self.working_prefix_dir = self.working_dir + "prefix_" + name
            if not os.path.exists(self.working_prefix_dir):
                os.mkdir(self.working_prefix_dir)
                version_bottle = self.working_prefix_dir + "/version.bottle"
                with open(version_bottle, "w") as f:
                    f.write(arch)

                # start winecfg
                self.run_winecfg(self.working_prefix_dir)

            self.detail_bottle(name)

            # re-fill list
            lt = self.parent.parent.stack.list_all
            lt.generate_entries(True)

    def list_bottles(self):
        bottles = []
        try:
            walk = next(os.walk(self.working_dir))[1]
            for w in walk:
                bottles.append(w)
        except StopIteration:
            pass
        return bottles

    def remove_bottle(self, bottle_name):
        if self.check_special_chars(bottle_name):
            shutil.rmtree(self.working_dir + bottle_name, ignore_errors=True)

            # re-fill list
            lt = self.parent.parent.stack.list_all
            lt.generate_entries(True)

    def detail_bottle(self, name):
        # get detail data
        dt = self.parent.parent.stack.detail
        self.parent.properties.hide()
        self.parent.trash.hide()
        if name.find("prefix_") == -1:
            dt.working_dir = self.working_dir + "prefix_" + name
        else:
            dt.working_dir = self.working_dir + name
        with open(dt.working_dir + "/version.bottle", 'r') as arch_f:
            arch = arch_f.read().replace('\n', '')
        if arch == "32 Bit":
            version = subprocess.check_output(["wine", "--version"])
        else:
            version = subprocess.check_output(["wine64", "--version"])
        version = str(version)
        version = version.replace("b'", "")
        version = version.replace("\\n'", "")

        # remove prefix_ from bottle name
        name = name.replace("prefix_", "")

        # set detail title and description
        self.parent.props.title = name + " " + version + " (" + arch + ")"

        # change stack to detail
        self.parent.save.hide()
        self.parent.parent.stack.stack.set_visible_child_name("detail")