Exemple #1
0
class LightDMManager:
    def __init__(self):
        self.scriptDir = os.path.dirname(os.path.realpath(__file__))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            join(self.scriptDir,
                 '../../share/lightdm-manager/lightdm-manager.glade'))
        # Main window objects
        go = self.builder.get_object
        self.window = go('ldmWindow')
        self.swUsers = go('swUsers')
        self.tvUsers = go('tvUsers')
        self.btnSave = go('btnSave')
        self.imgBackground = go('imgBackground')
        self.btnUsers = go('btnUsers')
        self.btnAppearance = go('btnAppearance')
        self.chkHideUsers = go('chkHideUsers')
        self.ebFace = go('ebFace')
        self.imgFace = go('imgFace')
        self.nbLightDM = go('nbLightDM')
        self.cmbThemes = go('cmbThemes')

        # Read from config file
        self.cfg = Config('lightdm-manager.conf')
        self.lightdmConf = self.cfg.getValue('CONFIG', 'lightdmConf')
        self.desktopbaseDir = self.cfg.getValue('CONFIG', 'desktopbaseDir')
        gktGreeterConf = self.cfg.getValue('CONFIG', 'gtkGreeterConf')
        kdeGreeterConf = self.cfg.getValue('CONFIG', 'kdeGreeterConf')
        if exists(gktGreeterConf):
            self.greeterConf = gktGreeterConf
        else:
            self.greeterConf = kdeGreeterConf

        # Translations
        title = _("LightDM Manager")
        self.window.set_title(title)
        self.btnUsers.set_label("_{}".format(_("Users")))
        self.btnAppearance.set_label("_{}".format(_("Appearance")))
        go('lblBackground').set_label(_("Background"))
        go('lblTheme').set_label(_("Theme"))
        go('lblLightDmMenu').set_label(_("Menu"))
        self.chkHideUsers.set_label(_("Hide users"))
        go('lblUsersFace').set_label(_("User icon"))
        go('lblUsersAutologin').set_label(_("Auto-login"))

        # Get current background image
        self.cfgGreeter = Config(self.greeterConf)
        try:
            self.curBgPath = self.cfgGreeter.getValue('greeter', 'background')
            self.curTheme = self.cfgGreeter.getValue('greeter', 'theme-name')
        except:
            self.curBgPath = None
            self.curTheme = None

        # Get current auto-login user
        self.cfgLightdm = Config(self.lightdmConf)
        try:
            self.curAutoUser = self.cfgLightdm.getValue(
                'SeatDefaults', 'autologin-user').strip()
            self.curHideUsers = False
            ghu = self.cfgLightdm.getValue('SeatDefaults',
                                           'greeter-hide-users').strip()
            if 'true' in ghu:
                self.curHideUsers = True
        except:
            self.curAutoUser = None
            self.curHideUsers = False

        # Init
        self.usr = User()
        self.newbgImg = self.curBgPath
        self.newAutoUser = self.curAutoUser
        self.newTheme = self.curTheme
        self.themes = []
        self.selectedMenuItem = None
        self.debug = False
        self.logPath = ''
        self.prevPath = None
        self.tempFace = "/tmp/face"
        self.newFaces = []
        self.loggedUser = functions.getUserLoginName()
        self.curUser = self.loggedUser
        self.selectImg = join(self.scriptDir,
                              '../../share/lightdm-manager/select.png')

        # Handle arguments
        try:
            opts, args = getopt.getopt(sys.argv[1:], 'dl:', ['debug', 'log='])
        except getopt.GetoptError:
            print(("Arguments cannot be parsed: %s" % str(sys.argv[1:])))
            sys.exit(1)

        for opt, arg in opts:
            if opt in ('-d', '--debug'):
                self.debug = True
            elif opt in ('-l', '--log'):
                self.logPath = arg

        # Initialize logging
        if self.debug:
            if not self.logPath:
                self.logPath = 'lightdm-manager.log'
        self.log = Logger(self.logPath, 'debug', True, None, self.window)

        # Backup config files because ConfigParser does not preserve commented lines
        if not exists("%s.org" % self.greeterConf):
            copy(self.greeterConf, "%s.org" % self.greeterConf)
            self.log.write(
                "%(conf1)s copied to %(conf2)s.org" % {
                    "conf1": self.greeterConf,
                    "conf2": self.greeterConf
                }, 'LightDMManager.main', 'debug')
        if not exists("%s.org" % self.lightdmConf):
            copy(self.lightdmConf, "%s.org" % self.lightdmConf)
            self.log.write(
                "%(conf1)s copied to %(conf2)s.org" % {
                    "conf1": self.lightdmConf,
                    "conf2": self.lightdmConf
                }, 'LightDMManager.main', 'debug')

        # Initiate the treeview handler and connect the custom toggle event with usersCheckBoxToggled
        self.tvHandler = TreeViewHandler(self.tvUsers, self.log)
        self.tvHandler.connect('checkbox-toggled', self.usersCheckBoxToggled)

        # Get users
        self.users = self.usr.getUsers()
        self.fillUsers()
        self.tvHandler.selectValue(self.curUser, 1)
        self.setBackground(self.curBgPath)
        self.cmbHandlerThemes = ComboBoxHandler(self.cmbThemes)
        self.listThemes()
        self.chkHideUsers.set_active(self.curHideUsers)

        # Show users menu
        self.on_btnUsers_clicked(None)
        self.on_tvUsers_cursor_changed(None)

        self.version = functions.getPackageVersion('lightdm-manager')

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Menu section functions
    # ===============================================

    def on_btnUsers_clicked(self, widget, event=None):
        if self.selectedMenuItem != menuItems[0]:
            self.selectedMenuItem = menuItems[0]
            self.nbLightDM.set_current_page(0)

    def on_btnAppearance_clicked(self, widget, event=None):
        if self.selectedMenuItem != menuItems[1]:
            self.selectedMenuItem = menuItems[1]
            self.nbLightDM.set_current_page(1)

    # ===============================================
    # Functions
    # ===============================================

    def on_ebFace_enter_notify_event(self, widget, event):
        self.window.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebFace_leave_notify_event(self, widget, event):
        self.window.get_window().set_cursor(None)

    def on_ebFace_button_release_event(self, widget, event):
        home = self.usr.getUserHomeDir(self.curUser)
        primaryGroup = self.usr.getUserPrimaryGroupName(self.curUser)
        imagePath = SelectImageDialog(_('Select user image'), home,
                                      self.window).show()
        if imagePath is not None:
            tempUserImg = "%(tempFace)s.%(curUser)s" % {
                "tempFace": self.tempFace,
                "curUser": self.curUser
            }
            self.newFaces.append(
                [tempUserImg,
                 join(home, ".face"), self.curUser, primaryGroup])
            print((">>> self.newFaces = %s" % self.newFaces))
            ih = ImageHandler(imagePath)
            ih.makeFaceImage(tempUserImg)
            if exists(tempUserImg):
                self.imgFace.set_from_pixbuf(ih.pixbuf)
            else:
                # This should never happen
                self.imgFace.set_from_file(self.selectImg)

    def on_tvUsers_cursor_changed(self, widget):
        self.curUser = self.tvHandler.getSelectedValue(1)
        showFace = None
        if self.newFaces:
            for face in self.newFaces:
                if face[2] == self.curUser and exists(face[0]):
                    showFace = GdkPixbuf.Pixbuf.new_from_file(face[0])
        if showFace is None:
            showFace = self.usr.getUserFacePixbuf(self.curUser)
        if showFace is None:
            # Still no user icon found: show select image
            self.imgFace.set_from_file(self.selectImg)
        else:
            self.imgFace.set_from_pixbuf(showFace)

    def on_btnSave_clicked(self, widget):
        saved = False
        saveHideUsers = False
        saveAutoUser = False
        saveFaces = False
        saveBackground = False
        saveTheme = False
        if self.chkHideUsers.get_active() != self.curHideUsers:
            saveHideUsers = True
        if self.curAutoUser != self.newAutoUser:
            saveAutoUser = True
        if self.newFaces:
            saveFaces = True
        if self.curBgPath != self.newbgImg:
            saveBackground = True
        self.newTheme = self.cmbHandlerThemes.getValue()
        if self.curTheme != self.newTheme:
            saveTheme = True

        if saveHideUsers or saveAutoUser or saveFaces or saveBackground or saveTheme:
            qd = QuestionDialog(
                _("LightDM settings"),
                _("Settings have changed\n\nDo you want to save the new settings?"
                  ), self.window)
            answer = qd.show()
            if answer:
                if saveAutoUser:
                    if self.newAutoUser is not None:
                        # Save the auto-login user
                        self.cfgLightdm.setValue('SeatDefaults',
                                                 'autologin-user',
                                                 self.newAutoUser)
                        self.cfgLightdm.setValue('SeatDefaults',
                                                 'autologin-user-timeout', '0')
                        self.curAutoUser = self.newAutoUser
                        self.log.write(
                            "New auto-login user: %(usr)s" %
                            {"usr": self.curAutoUser},
                            'LightDMManager.saveSettings', 'debug')
                    else:
                        self.cfgLightdm.removeOption('SeatDefaults',
                                                     'autologin-user')
                        self.cfgLightdm.removeOption('SeatDefaults',
                                                     'autologin-user-timeout')
                        self.curAutoUser = None
                        self.log.write("Auto-login disabled",
                                       'LightDMManager.saveSettings', 'debug')
                if saveHideUsers:
                    hideUsers = str(self.chkHideUsers.get_active()).lower()
                    self.cfgLightdm.setValue('SeatDefaults',
                                             'greeter-hide-users', hideUsers)
                    self.log.write(
                        "Hide users saved: %(users)s" % {"users": hideUsers},
                        'LightDMManager.saveSettings', 'debug')
                if saveFaces:
                    for face in self.newFaces:
                        if exists(face[0]):
                            copy(face[0], face[1])
                            if exists(face[1]):
                                os.system(
                                    "chown %(owner)s:%(group)s %(path)s" % {
                                        "owner": face[2],
                                        "group": face[3],
                                        "path": face[1]
                                    })
                    self.log.write("User icons saved",
                                   'LightDMManager.saveSettings', 'debug')
                if saveTheme:
                    self.cfgGreeter.setValue('greeter', 'theme-name',
                                             self.newTheme)
                    self.curTheme = self.newTheme
                    self.log.write(
                        "Theme saved: %(theme)s" % {"theme": self.curTheme},
                        'LightDMManager.saveSettings', 'debug')
                if saveBackground:
                    if os.path.exists(self.newbgImg):
                        self.cfgGreeter.setValue('greeter', 'background',
                                                 self.newbgImg)
                        self.curBgPath = self.newbgImg
                        self.log.write(
                            "Background saved: %(background)s" %
                            {"background": self.curBgPath},
                            'LightDMManager.saveSettings', 'debug')
                saved = True
            else:
                if os.path.exists(self.curBgPath):
                    self.setBackground(self.curBgPath)
                    self.log.write(
                        "Current background: %(background)s" %
                        {"background": self.curBgPath},
                        'LightDMManager.saveSettings', 'debug')
                else:
                    self.imgBackground.set_from_file(
                        join(self.scriptDir,
                             '../../share/lightdm-manager/select.png'))
                    self.log.write("No background set",
                                   'LightDMManager.saveSettings', 'debug')
                self.fillUsers()

        if saved:
            self.curHideUsers = self.chkHideUsers.get_active()
            self.curAutoUser = self.newAutoUser
            self.newFaces = []
            self.curBgPath = self.newbgImg
            self.curTheme = self.newTheme
            MessageDialogSafe(_("Saved"),
                              _("LightDM settings saved successfully."),
                              Gtk.MessageType.INFO, self.window).show()

    def on_ebBackground_button_release_event(self, widget, event):
        self.newbgImg = SelectImageDialog(_("Choose background image"),
                                          self.desktopbaseDir,
                                          self.window).show()
        if exists(self.newbgImg) and self.newbgImg != self.curBgPath:
            self.setBackground(self.newbgImg)
            self.log.write(
                _("New background: %(bg)s") % {"bg": self.newbgImg},
                'LightDMManager.chooseFile', 'info')

    def on_ebBackground_enter_notify_event(self, widget, event):
        self.window.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebBackground_leave_notify_event(self, widget, event):
        self.window.get_window().set_cursor(None)

    # This method is fired by the TreeView.checkbox-toggled event
    def usersCheckBoxToggled(self, obj, path, colNr, toggleValue):
        path = int(path)
        model = self.tvUsers.get_model()
        itr = model.get_iter(path)
        user = model[itr][1]

        if self.prevPath != path or toggleValue:
            # Only one toggle box can be selected (or none)
            self.tvHandler.treeviewToggleAll([0], False, 1, user)
            # Save current path
            self.prevPath = path
            # Save selected user
            self.newAutoUser = user
            self.log.write(
                _("Auto-login user selected: %(usr)s") % {"usr": user},
                'LightDMManager.usersCheckBoxToggled', 'info')
        elif self.prevPath == path and not toggleValue:
            self.newAutoUser = None

    def listThemes(self):
        themeDir = '/usr/share/themes'
        themeDirLocal = '~/.local/share/themes'
        dirs = functions.locate('gtk-*', themeDir, True) + functions.locate(
            'gtk-*', themeDirLocal, True)
        for path in dirs:
            dirList = path.split('/')
            for d in dirList:
                if 'gtk-' in d:
                    break
                themeName = d
            if themeName not in self.themes:
                self.themes.append(themeName)
        if self.themes:
            self.cmbHandlerThemes.fillComboBox(self.themes)
            if self.curTheme in self.themes:
                self.cmbHandlerThemes.selectValue(self.curTheme)

    def fillUsers(self):
        selUsr = False
        contentList = []
        i = 0
        for usr in self.users:
            if usr == self.curAutoUser:
                selUsr = True
                self.prevPath = i
            else:
                selUsr = False
            contentList.append([selUsr, usr])
            i += 1

        # Fill treeview with users
        #fillTreeview(contentList, columnTypesList, columnHideList=[-1], setCursor=0, setCursorWeight=400, firstItemIsColName=False, appendToExisting=False, appendToTop=False)
        columnTypesList = ['bool', 'str']
        self.tvHandler.fillTreeview(contentList, columnTypesList)

    def setBackground(self, path):
        # Set Background
        if path is not None:
            if exists(path):
                ih = ImageHandler(path)
                ih.resizeImage(height=200)
                self.imgBackground.set_from_pixbuf(ih.pixbuf)
            else:
                self.imgBackground.set_from_file(self.selectImg)
        else:
            self.imgBackground.set_from_file(self.selectImg)

    # ===============================================
    # General functions
    # ===============================================

    def on_ldmWindow_destroy(self, widget, data=None):
        # Close the app
        self.on_btnSave_clicked(None)
        for tmp in self.newFaces:
            os.remove(tmp[0])
        Gtk.main_quit()
Exemple #2
0
class UpdateManager(object):

    def __init__(self):
        # Check if script is running
        self.scriptName = basename(__file__)
        self.umglobal = UmGlobal()
        self.user = self.umglobal.getLoginName()

        # Initiate logging
        self.logFile = join('/var/log', self.umglobal.settings['log'])
        print(("UM log = %s" % self.logFile))
        self.log = Logger(self.logFile, maxSizeKB=5120)

        # Remove scripts
        self.deleteScripts(self.umglobal.localUpdVersion)

        # Initialize
        self.ec = ExecCmd(loggerObject=self.log)
        self.apt = UmApt(self.umglobal)
        self.kernelVersion = self.umglobal.getKernelVersion()
        self.upgradables = []
        self.upgradableUM = []
        self.window = None

        # Handle arguments
        parser = argparse.ArgumentParser(description='SolydXK Update Manager')
        parser.add_argument('-q','--quick', action="store_true", help='Quick upgrade')
        parser.add_argument('-r','--reload', action="store_true", help='')
        args, extra = parser.parse_known_args()

        self.quickUpdate = False
        if args.quick and not self.umglobal.newUpd:
            self.quickUpdate = True
        if args.reload:
            pids = self.umglobal.getProcessPids("updatemanager.py")
            if len(pids) > 1:
                print(("updatemanager.py already running - kill pid {}".format(pids[0])))
                os.system("kill {}".format(pids[0]))

        # Set some global translations
        self.aptErrorText = _("Apt error")
        self.upgradablePackagesText = _("The following packages will be upgraded:")
        self.newPackagesText = _("The following NEW packages will be installed:")
        self.removedPackagesText = _("The following packages will be REMOVED:")
        self.heldbackPackagesText = _("The following packages have been kept back:")
        self.downgradePackagesText = _("The following packages are going to be downgraded:")

        # Cleanup first
        for fle in glob(join(self.umglobal.filesDir, '.um*')):
            remove(fle)

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.umglobal.shareDir, 'updatemanager.glade'))
        go = self.builder.get_object

        # Quick update
        if self.quickUpdate:
            # Refresh data
            self.refresh()
            self.umglobal.collectData()
            self.apt.createPackagesInfoList()
            self.apt.createPackageLists()
            self.fillTreeView()

            # Run upgrade
            nid = self.run_upgrade()

            if nid != "":
                self.on_command_done(None, 0, nid)

            sys.exit(2)

        # Make sure the files directory is set correctly
        self.checkFilesDir()

        # Main window objects
        self.window = go("windowMain")
        #self.window.set_icon_from_file(join(self.umglobal.iconsDir, self.umglobal.settings["icon-connected"]))
        self.tvPck = go("tvPck")
        self.swTerminal = go("swTerminal")
        self.statusbar = go("statusbar")
        self.btnInstall = go("btnInstall")
        self.btnRefresh = go("btnRefresh")
        self.btnPackages = go("btnPackages")
        self.btnOutput = go("btnOutput")
        self.btnInfo = go("btnInfo")
        self.btnPreferences = go("btnPreferences")
        self.nbMain = go("nbMain")
        self.swInfo = go("swInfo")
        self.btnMaintenance = go("btnMaintenance")
        self.lblMaintenance = go("lblMaintenance")
        self.tvMaintenance = go("tvMaintenance")
        self.btnMaintenanceExecute = go("btnMaintenanceExecute")
        self.chkMaintenanceSelectAll = go("chkMaintenanceSelectAll")
        self.radUnneeded = go("radUnneeded")
        self.radCleanCache = go("radCleanCache")
        self.radDowngradable = go("radDowngradable")
        self.radNotavailable = go("radNotavailable")
        self.radOldKernel = go("radOldKernel")
        self.lblMaintenanceHelp = go("lblMaintenanceHelp")

        # Translations
        self.window.set_title(_("SolydXK Update Manager"))
        self.btnInstall.set_label(_("Install"))
        self.btnRefresh.set_label(_("Refresh"))
        self.btnOutput.set_label(_("Output"))
        self.btnInfo.set_label(_("Information"))
        self.btnPreferences.set_label(_("Preferences"))
        self.btnMaintenance.set_label(_("Maintenance"))
        self.btnPackages.set_label(_("Packages"))
        self.uptodateText = self.umglobal.connectedText
        self.lblMaintenance.set_label(self.btnMaintenance.get_label())
        self.btnMaintenanceExecute.set_label(_("Execute"))
        self.chkMaintenanceSelectAll.set_label(_("Select all"))
        self.radCleanCache.set_label(_("Clean up the apt cache"))
        self.radUnneeded.set_label(_("Remove unneeded packages"))
        self.radNotavailable.set_label(_("Remove packages not available\nin the repositories"))
        self.radOldKernel.set_label(_("Remove old kernels"))
        self.radDowngradable.set_label(_("Downgrade packages with\nonly lower versions available"))
        self.lblMaintenanceHelp.set_label(_("Make sure you create\n"
                                             "a system image before you\n"
                                             "continue (e.g. Clonezilla)."))

        # VTE Terminal
        self.terminal = VirtualTerminal(userInputAllowed=self.umglobal.settings["allow-terminal-user-input"])
        self.swTerminal.add(self.terminal)
        self.terminal.set_vexpand(True)
        self.terminal.set_hexpand(True)
        self.terminal.connect('command-done', self.on_command_done)
        self.terminal.connect('line-added', self.on_line_added)
        palletList = ['#4A4A4A', '#BD1919', '#118011', '#CE6800', '#1919BC', '#8D138D', '#139494', '#A7A7A7']
        self.terminal.setTerminalColors("#000000", "#FFFFFF", palletList)
        self.swTerminal.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("#FFFFFF"))

        # Disable all buttons
        self.btnInfo.set_sensitive(False)
        self.btnPreferences.set_sensitive(False)
        self.btnOutput.set_sensitive(False)
        self.btnRefresh.set_sensitive(False)
        self.btnInstall.set_sensitive(False)
        self.btnPackages.set_sensitive(False)
        self.btnMaintenance.set_sensitive(False)

        # Hide tabs if needed
        for tab in self.umglobal.settings["hide-tabs"]:
            if tab == "packages":
                self.nbMain.get_nth_page(0).set_visible(False)
                self.btnPackages.set_visible(False)
            elif tab == "output":
                self.nbMain.get_nth_page(1).set_visible(False)
                self.btnOutput.set_visible(False)
            elif tab == "info":
                self.nbMain.get_nth_page(2).set_visible(False)
                self.btnInfo.set_visible(False)
            elif tab == "maintenance":
                self.nbMain.get_nth_page(3).set_visible(False)
                self.btnMaintenance.set_visible(False)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

        # Force the window to show
        while Gtk.events_pending():
            Gtk.main_iteration()

        # Just show something that we're busy
        msg = _("Gathering information...")
        self.terminal.executeCommand('echo "%s"' % msg, 'init')
        self.showOutput()

        # Treeview handlers
        self.tvHandler = TreeViewHandler(self.tvPck)
        self.tvMaintenanceHandler = TreeViewHandler(self.tvMaintenance)

        # Version information
        ver = _("Version")
        pckVer = self.apt.getPackageVersion('updatemanager')
        versionInfo = "%(ver)s: %(pckVer)s" % { "ver": ver, "pckVer": pckVer }
        if self.umglobal.localUpdVersion != "2000.01.01":
            versionInfo = "%(ver)s: %(pckVer)s" % { "ver": ver, "pckVer": pckVer }
        self.pushMessage(versionInfo)

        # Log basic information
        self.log.write("==============================================", "UM.init", "debug")
        self.log.write("UM version = %s" % versionInfo, "UM.init", "debug")
        self.log.write("==============================================", "UM.init", "debug")
        mirrorsList = join(self.umglobal.filesDir, basename(self.umglobal.settings["mirrors-list"]))
        if exists(mirrorsList):
            self.log.write("Mirrors list", "UM.init", "debug")
            with open(mirrorsList, 'r') as f:
                for line in f.readlines():
                    self.log.write(line, "UM.init", "debug")
            self.log.write("==============================================", "UM.init", "debug")

        # Refresh apt cache
        self.refresh()

        # Initialize maintenance screen
        self.fillTreeViewMaintenance()

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnInstall_clicked(self, widget):
        self.run_upgrade()

    def run_upgrade(self):
        nid = ""
        aptHasErrors = self.apt.aptHasErrors()
        if aptHasErrors is not None:
            MessageDialog(self.aptErrorText, aptHasErrors)
        elif self.upgradables:

            if self.apt.upgradablePackages:
                self.log.write("=================== upgradable pacages ====================", "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.upgradablePackages), "UM.run_upgrade", "debug")
            if self.apt.removedPackages:
                self.log.write("==================== removed packages =====================", "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.removedPackages), "UM.run_upgrade", "debug")
            if self.apt.newPackages:
                self.log.write("======================= new packages =======================", "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.newPackages), "UM.run_upgrade", "debug")
            if self.apt.heldbackPackages:
                self.log.write("=================== kept back packages =====================", "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.heldbackPackages), "UM.run_upgrade", "debug")

            if not self.quickUpdate:
                self.showOutput()
            contMsg = _("Continue installation?")
            if self.upgradableUM:
                cmd = "%s install updatemanager" % self.umglobal.settings['apt-get-string']
                cmd += "; %s install %s" % (self.umglobal.settings['apt-get-string'], " ".join(self.apt.getPackageDependencies('updatemanager')))
                nid = 'uminstallum'
                self.prepForCommand(nid)
                if self.quickUpdate:
                    self.ec.run(cmd)
                else:
                    self.terminal.executeCommand(cmd, nid)
                self.log.write("Execute command: %s (%s)" % (cmd, nid), "UM.on_btnInstall_clicked", "debug")
            else:
                msg = self.getDistUpgradeInfo()
                answer = True
                if msg != "":
                    answer = self.showConfirmationDlg(contMsg, msg)
                if answer:
                    cmd = "%s dist-upgrade" % self.umglobal.settings['apt-get-string']
                    #if self.umglobal.newUpd:
                    pre = join(self.umglobal.filesDir, self.umglobal.settings['pre-upd'].replace("[VERSION]", self.umglobal.serverUpdVersion))
                    post = join(self.umglobal.filesDir, self.umglobal.settings['post-upd'].replace("[VERSION]", self.umglobal.serverUpdVersion))
                    if exists(pre):
                        cmd = "/bin/bash %(pre)s; %(cmd)s" % { "pre": pre, "cmd": cmd }
                    if exists(post):
                        cmd = "%(cmd)s; /bin/bash %(post)s" % { "cmd": cmd, "post": post }
                    nid = 'umupd'
                    self.prepForCommand(nid)
                    if self.quickUpdate:
                        self.ec.run(cmd)
                    else:
                        self.terminal.executeCommand(cmd, nid)
                    self.log.write("Execute command: %s (%s)" % (cmd, nid), "UM.on_btnInstall_clicked", "debug")

        else:
            if not self.quickUpdate:
                MessageDialog(self.btnInstall.get_label(), self.uptodateText)

        return nid

    def on_btnRefresh_clicked(self, widget):
        self.refresh()

    def on_btnPackages_clicked(self, widget):
        self.showPackages()

    def on_btnOutput_clicked(self, widget):
        self.showOutput()

    def on_btnInfo_clicked(self, widget):
        self.showInfo()

    def on_btnPreferences_clicked(self, widget):
        # Run preferences in its own thread
        pref_thread = threading.Thread(target=self.openPreferences)
        pref_thread.setDaemon(True)
        pref_thread.start()

    def openPreferences(self):
        os.system("updatemanager -p")

    def on_btnMaintenance_clicked(self, widget):
        self.showMaintenance()

    def on_radCleanCache_toggled(self, widget):
        if widget.get_active():
            self.fillTreeViewMaintenance()

    def on_radUnneeded_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (_("You might need to run this several times."), self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radNotavailable_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (_("Removing not available packages may break your system!"), self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radOldKernel_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (_("Once removed you will not be able to boot these kernels!"), self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radDowngradable_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (_("Downgrading packages may break your system!"), self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_btnMaintenanceExecute_clicked(self, widget):
        self.executeMaintenance()

    def on_chkMaintenanceSelectAll_toggled(self, widget):
        self.tvMaintenanceHandler.treeviewToggleAll(toggleColNrList=[0], toggleValue=widget.get_active())

    # ===============================================
    # Maintenance functions
    # ===============================================

    def enableMaintenance(self, enable=True):
        self.btnMaintenanceExecute.set_sensitive(enable)
        self.radUnneeded.set_sensitive(enable)
        self.radCleanCache.set_sensitive(enable)
        self.radDowngradable.set_sensitive(enable)
        self.radOldKernel.set_sensitive(enable)
        self.radNotavailable.set_sensitive(enable)
        self.chkMaintenanceSelectAll.set_sensitive(enable)
        if enable:
            self.chkMaintenanceSelectAll.set_active(False)

    def fillTreeViewMaintenance(self):
        blnCleanCache = self.radCleanCache.get_active()
        blnUnneeded = self.radUnneeded.get_active()
        blnDowngradable = self.radDowngradable.get_active()
        blnNotavailable = self.radNotavailable.get_active()
        blnOldKernels = self.radOldKernel.get_active()

        self.enableMaintenance(False)

        columnTypesList = ['bool', 'str', 'str', 'str']
        packages = [["", _("Package"), _("Installed"), _("Available")]]
        # Clear the treeview first
        self.tvMaintenanceHandler.fillTreeview(packages, columnTypesList, 0, 400, True)

        msg = ""
        if blnCleanCache:
            msg = _("Hit the Execute button to clean the cache.")
            columnTypesList = ['str']
            packages.append([msg])
        elif blnUnneeded:
            msg = self.radUnneeded.get_label()
            self.apt.createPackageLists("apt-get autoremove")
            for pck in self.apt.removedPackages:
                packages.append([False, pck[0], pck[1], pck[2]])
            self.apt.createPackageLists()
            # Add orphaned files
            self.apt.fillOrphanedPackages()
            for pck in self.apt.orphanedPackages:
                packages.append([False, pck[0], pck[1], pck[2]])
        elif blnNotavailable:
            msg = self.radNotavailable.get_label()
            self.apt.fillNotAvailablePackages()
            for pck in self.apt.notavailablePackages:
                if pck[0][0:6] != "linux-":
                    packages.append([False, pck[0], pck[1], ""])
        elif blnOldKernels:
            msg = self.radOldKernel.get_label()
            self.apt.fillKernelPackages()
            for pck in self.apt.kernelPackages:
                if "headers-486" not in pck[0] \
                    and "headers-586" not in pck[0] \
                    and "headers-686" not in pck[0] \
                    and "headers-amd64" not in pck[0] \
                    and "image-486" not in pck[0] \
                    and "image-586" not in pck[0] \
                    and "image-686" not in pck[0] \
                    and "image-amd64" not in pck[0]:

                    checkVersion = self.kernelVersion
                    if "kbuild" in pck[0]:
                        indMinus = self.kernelVersion.index("-")
                        indZero = self.kernelVersion.index("0")
                        if indZero > 0 and indZero < indMinus:
                            ind = indZero - 1
                        else:
                            ind = indMinus
                        if ind > 0:
                            checkVersion = self.kernelVersion[0:ind]
                    if checkVersion not in pck[0]:
                        packages.append([False, pck[0], pck[1], ""])
        elif blnDowngradable:
            msg = self.radDowngradable.get_label()
            self.apt.fillDowngradablePackages()
            for pck in self.apt.downgradablePackages:
                packages.append([False, pck[0], pck[1], pck[2]])

        if len(packages) > 1:
            self.tvMaintenanceHandler.fillTreeview(packages, columnTypesList, 0, 400, True)
        else:
            if not blnCleanCache:
                msg = _("\"%s\"\n did not return any results.") % msg
                MessageDialog(self.btnMaintenance.get_label(), msg)

        self.enableMaintenance(True)

    def executeMaintenance(self):
        blnCleanCache = self.radCleanCache.get_active()
        blnDowngradable = self.radDowngradable.get_active()
        blnNotNeeded = self.radUnneeded.get_active()
        downgradeString = ""
        deleteString = ""
        updateGrub = False
        cmd = ""

        self.enableMaintenance(False)

        if blnCleanCache:
            safe = False
            msg = _("Do you want to completely clean the apt cache?\n\n"
                    "When No, only unavailable installation packages are removed.")
            answer = QuestionDialog(self.radCleanCache.get_label(), msg)
            if answer:
                safe = True
            self.apt.cleanCache(safe)
            msg = _("Apt cache has been cleaned.")
            MessageDialog(self.radCleanCache.get_label(), msg)
        else:
            # Get user selected packages
            model = self.tvMaintenance.get_model()
            itr = model.get_iter_first()
            while itr is not None:
                sel = model.get_value(itr, 0)
                if sel:
                    pck = model.get_value(itr, 1)
                    avVer = model.get_value(itr, 3)
                    if blnDowngradable:
                        downgradeString += " %(pck)s=%(avVer)s" % {"pck": pck, "avVer": avVer}
                    else:
                        deleteString += " %s" % pck
                        if "linux-image" in pck:
                            updateGrub = True
                itr = model.iter_next(itr)

            if downgradeString != "":
                cmd = "%s install %s" % (self.umglobal.settings['apt-get-string'], downgradeString)
            elif deleteString != "":
                cmd = "%s purge %s" % (self.umglobal.settings['apt-get-string'], deleteString)

        if cmd != "":
            self.apt.createPackageLists(cmd)
            msg = self.getDistUpgradeInfo()
            answer = True
            if msg != "":
                contMsg = _("Execute maintenance changes?")
                answer = self.showConfirmationDlg(contMsg, msg)
            if answer:
                if updateGrub:
                    cmd += "; update-grub"
                if blnNotNeeded:
                    cmd += "; %s purge $(COLUMNS=132 dpkg -l | grep ^rc | awk '{ print $2 }')" % self.umglobal.settings['apt-get-string']
                self.showOutput()
                nid = 'ummaintenance'
                self.prepForCommand(nid)
                self.terminal.executeCommand(cmd, nid)
                self.log.write("Execute command: %s (%s)" % (cmd, nid), "UM.executeMaintenance", "debug")

        self.enableMaintenance(True)

    # ===============================================
    # General functions
    # ===============================================

    def prepForCommand(self, nid):
        os.system("touch %s" % self.umglobal.umfiles[nid])
        if not self.quickUpdate:
            self.btnRefresh.set_sensitive(False)
            self.btnInstall.set_sensitive(False)
            self.btnMaintenance.set_sensitive(False)

    def on_line_added(self, terminal, line):
        if line.strip()[0:2].upper() == "E:":
            self.log.write(line, "UM.on_line_added", "error")
        else:
            self.log.write(line, "UM.on_line_added", "info")

    def on_command_done(self, terminal, pid, nid):
        if nid != "init":
            self.log.write("Command finished (pid=%s, nid=%s)" % (pid, nid), "UM.on_command_done", "info")
            if nid == "uminstallum":
                # Reload UM
                self.log.write("Updating UM: kill process of updatemanagerpref.py", "UM.on_command_done", "debug")
                self.umglobal.killScriptProcess("updatemanagerpref.py")
                # Reload tray as user
                self.log.write("Updating UM: kill process of updatemanagertray.py", "UM.on_command_done", "debug")
                self.umglobal.killScriptProcess("updatemanagertray.py")
                cmd = "sudo -u {} updatemanager -t -r".format(self.user)
                os.system(cmd)
                self.log.write("UM updated: reload tray as user {}".format(self.user), "UM.on_command_done", "debug")
                # Reload UM window
                cmd = join(self.umglobal.scriptDir, "updatemanager.py")
                if self.quickUpdate:
                    cmd = join(self.umglobal.scriptDir, "updatemanager.py -q")
                self.umglobal.reloadWindow(cmd, self.user)
                self.log.write("UM updated: reload {0} as user {1}".format(cmd, self.user), "UM.on_command_done", "debug")
            elif nid == "umrefresh":
                # Build installed packages info list
                self.apt.createPackagesInfoList()
                # Run post update when needed
                self.postUpdate()
            elif nid == "ummaintenance":
                self.enableMaintenance(True)
                self.fillTreeViewMaintenance()
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.showMaintenance()
            elif nid == 'umupd':
                # Save update version in hist file
                self.umglobal.saveHistVersion("upd", self.umglobal.serverUpdVersion)
                self.log.write("Save history upd=%s" % self.umglobal.serverUpdVersion, "UM.on_command_done", "debug")
                self.deleteScripts()

            # Refresh data after install or update
            self.umglobal.collectData()
            self.apt.createPackageLists()
            self.fillTreeView()

            # Enable the buttons and load the info page
            self.log.write("Re-initiate window after terminal command: %s" % str(not self.quickUpdate), "UM.on_command_done", "debug")
            if not self.quickUpdate:
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.btnInfo.set_sensitive(True)
                self.loadInfo()

                if self.umglobal.newUpd:
                    self.showInfo()
                else:
                    # This throws a lock file error in Plasma5
                    #aptHasErrors = self.apt.aptHasErrors()
                    #if aptHasErrors is not None:
                        #MessageDialog(self.aptErrorText, aptHasErrors)
                    #el
                    if self.upgradables:
                        self.showPackages()
                    else:
                        self.showInfo()
                        MessageDialog(self.btnInfo.get_label(), self.uptodateText)
                self.log.write("Re-initiate window complete", "UM.on_command_done", "debug")

            # Cleanup name file(s)
            for fle in glob(join(self.umglobal.filesDir, '.um*')):
                remove(fle)

    def createLogString(self, packagesList):
        lst = []
        for data in packagesList:
            lst.append(data[0])
        return ' '.join(lst)

    def refresh(self):
        # Refresh server info
        print((self.umglobal.hasInternet))
        self.umglobal.getServerInfo()
        print((self.umglobal.hasInternet))

        # Check of programs locking apt
        prog = self.apt.getAptCacheLockedProgram(self.umglobal.settings["apt-packages"])
        if prog is not None:
            msg = _("Another program is locking the apt cache\n\n"
                    "Please, close the program before refreshing:\n"
                    "* %s" % prog)
            MessageDialog(self.btnRefresh.get_label(), msg)
            self.log.write("%s is locking the apt cache" % prog, "UM.refresh", "warning")
        elif self.umglobal.hasInternet:
            if not self.quickUpdate:
                # Update the apt cache
                self.btnPreferences.set_sensitive(True)
                self.btnOutput.set_sensitive(True)
                self.btnRefresh.set_sensitive(False)
                self.btnInstall.set_sensitive(False)
                self.btnMaintenance.set_sensitive(False)
                self.btnPackages.set_sensitive(True)
                self.showOutput()
            cmd = "dpkg --configure -a; %s -f install; apt-get update" % self.umglobal.settings['apt-get-string']
            nid = 'umrefresh'
            self.prepForCommand(nid)
            if self.quickUpdate:
                self.ec.run(cmd)
            else:
                self.terminal.executeCommand(cmd, nid)
            self.apt.initAptShowVersions()
            self.log.write("Execute command: %s (%s)" % (cmd, nid), "UM.refresh", "debug")
        else:
            if not self.quickUpdate:
                # No internet connection
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.btnInfo.set_sensitive(True)
                self.btnOutput.set_sensitive(True)
                self.btnPreferences.set_sensitive(True)
                self.loadInfo()
                self.showInfo()

    def postUpdate(self):
        # Check for changed version information
        if self.umglobal.newUpd and self.umglobal.serverUpdVersion is not None:
            self.getScripts([self.umglobal.settings['pre-upd'].replace("[VERSION]", self.umglobal.serverUpdVersion),
                            self.umglobal.settings['post-upd'].replace("[VERSION]", self.umglobal.serverUpdVersion)])

    def fillTreeView(self):
        # First check if this application is upgradable
        self.upgradableUM = self.getUpgradablePackages(packageNames=["updatemanager"])
        if self.upgradableUM:
            self.upgradables = self.upgradableUM
        else:
            # Get a list of packages that can be upgraded
            self.upgradableUM = []
            self.upgradables = self.getUpgradablePackages()
            #if not self.upgradables:
                ## Check for black listed packages
                #cmd = "dpkg --get-selections | grep hold$ | awk '{print $1}'"
                #lst = self.ec.run(cmd, False)
                #for pck in lst:
                    #self.upgradables.append([pck.strip(), _("blacklisted"), ""])

        if not self.quickUpdate:
            contentList = [[_("Package"), _("Current version"), _("New version")]] + self.upgradables
            self.tvHandler.fillTreeview(contentList=contentList, columnTypesList=['str', 'str', 'str'], firstItemIsColName=True)

    def getUpgradablePackages(self, packageNames=[]):
        upckList = []
        if packageNames:
            upckList = []
            for packageName in packageNames:
                for upck in self.apt.upgradablePackages:
                    if upck[0] == packageName:
                        upckList.append(upck)
                        break
        else:
            upckList = self.apt.upgradablePackages
        return upckList

    def getDistUpgradeInfo(self, upgradablesOnly=False):
        info = ""
        if upgradablesOnly:
            if self.apt.upgradablePackages:
                info = "<strong>%s</strong><br>" % self.apt.upgradablePackagesText
                for pck in self.apt.upgradablePackages:
                    info += "%s " % pck[0]
        else:
            if self.apt.removedPackages:
                info = "<strong>%s</strong><br>" % self.removedPackagesText
                for pck in self.apt.removedPackages:
                    info += "%s " % pck[0]
            if self.apt.newPackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.newPackagesText
                for pck in self.apt.newPackages:
                    info += "%s " % pck[0]
            if self.apt.heldbackPackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.heldbackPackagesText
                for pck in self.apt.heldbackPackages:
                    info += "%s " % pck[0]
            if self.apt.downgradablePackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.downgradePackagesText
                for pck in self.apt.downgradablePackages:
                    info += "%s " % pck[0]
        return info

    def showPackages(self):
        self.nbMain.set_current_page(0)

    def showOutput(self):
        self.nbMain.set_current_page(1)
        self.terminal.grab_focus()

    def showInfo(self):
        self.nbMain.set_current_page(2)

    def showMaintenance(self):
        self.nbMain.set_current_page(3)

    def showConfirmationDlg(self, title, message):
        head = "<html><head><style>body { font-family: Arial, Helvetica, Verdana, Sans-serif; font-size: 12px; color: #555555; background: #ffffff; }</style></head><body>"
        end = "</body></html>"
        html = "%s%s%s" % (head, message, end)
        sw = Gtk.ScrolledWindow()
        sw.add(SimpleBrowser(html))
        return CustomQuestionDialog(title, sw, 550, 300, self.window).show()

    # Get pre-install script and post-install script from the server
    def getScripts(self, files):
        for fle in files:
            flePath = join(self.umglobal.filesDir, fle)
            if not exists(flePath):
                # Get the new scripts if they exist
                url = "%s/%s/%s" % (self.umglobal.settings['solydxk'], self.umglobal.settings["umfilesdir"], fle)
                try:
                    txt = urlopen(url).read().decode('utf-8')
                    if txt != '':
                        # Save to a file and make executable
                        self.log.write("Save script = %s" % flePath, "UM.getScripts", "debug")
                        with open(flePath, 'w') as f:
                            f.write(txt)
                        chmod(flePath, 0o755)
                except:
                    pass

    def loadInfo(self):
        languageDir = self.get_language_dir()
        url = join("file://%s" % languageDir, self.umglobal.settings['up-to-date'])
        self.btnInfo.set_icon_name("help-about")
        if self.upgradables:
            url = join("file://%s" % languageDir, self.umglobal.settings['updates'])
            if self.umglobal.newUpd:
                url = "%s/%s/%s" % (self.umglobal.settings['solydxk'], self.umglobal.settings["umfilesdir"], self.umglobal.settings['upd-info'])
        elif self.umglobal.serverUpdVersion is None:
            url = join("file://%s" % languageDir, self.umglobal.settings['not-found'])

        self.log.write("Load info url: %s" % url, "UM.loadInfo", "debug")

        children = self.swInfo.get_children()
        if children:
            children[0].openUrl(url)
        else:
            self.swInfo.add(SimpleBrowser(url))

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.umglobal.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.umglobal.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.umglobal.htmlDir, "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.umglobal.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = os.environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang

    def pushMessage(self, message):
        if message is not None:
            context = self.statusbar.get_context_id('message')
            self.statusbar.push(context, message)

    def checkFilesDir(self):
        if not exists(self.umglobal.filesDir):
            makedirs(self.umglobal.filesDir)
        oldFiles = glob(join(self.umglobal.scriptDir, 'pre-*')) + \
                   glob(join(self.umglobal.scriptDir, 'post-*')) + \
                   [join(self.umglobal.scriptDir, 'updatemanager.hist')] + \
                   [join(self.umglobal.scriptDir, 'mirrors.list')]
        for fle in oldFiles:
            if exists(fle):
                fleName = basename(fle)
                if not exists(join(self.umglobal.filesDir, fleName)):
                    move(fle, self.umglobal.filesDir)
                else:
                    remove(fle)
        chmod(self.umglobal.filesDir, 0o777)

    def deleteScripts(self, UpdVersion=None):
        UpdVersion = "*"
        if UpdVersion is not None:
            UpdVersion = "*%s" % UpdVersion

        oldFiles = glob(join(self.umglobal.filesDir, "pre-%s" % UpdVersion)) + glob(join(self.umglobal.filesDir, "post-%s" % UpdVersion))
        for fle in oldFiles:
            remove(fle)
            self.log.write("Cleanup file: %s" % fle, "UM.deleteScripts", "debug")

    # Close the gui
    def on_windowMain_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
class LightDMManager:

    def __init__(self):
        self.scriptDir = os.path.dirname(os.path.realpath(__file__))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.scriptDir, '../../share/lightdm-manager/lightdm-manager.glade'))
        # Main window objects
        go = self.builder.get_object
        self.window = go('ldmWindow')
        self.swUsers = go('swUsers')
        self.tvUsers = go('tvUsers')
        self.btnSave = go('btnSave')
        self.imgBackground = go('imgBackground')
        self.btnUsers = go('btnUsers')
        self.btnAppearance = go('btnAppearance')
        self.chkHideUsers = go('chkHideUsers')
        self.ebFace = go('ebFace')
        self.imgFace = go('imgFace')
        self.nbLightDM = go('nbLightDM')
        self.cmbThemes = go('cmbThemes')

        # Read from config file
        self.cfg = Config('lightdm-manager.conf')
        self.lightdmConf = self.cfg.getValue('CONFIG', 'lightdmConf')
        self.desktopbaseDir = self.cfg.getValue('CONFIG', 'desktopbaseDir')
        gktGreeterConf = self.cfg.getValue('CONFIG', 'gtkGreeterConf')
        kdeGreeterConf = self.cfg.getValue('CONFIG', 'kdeGreeterConf')
        if exists(gktGreeterConf):
            self.greeterConf = gktGreeterConf
        else:
            self.greeterConf = kdeGreeterConf

        # Translations
        title = _("LightDM Manager")
        self.window.set_title(title)
        self.btnUsers.set_label("_{}".format(_("Users")))
        self.btnAppearance.set_label("_{}".format(_("Appearance")))
        go('lblBackground').set_label(_("Background"))
        go('lblTheme').set_label(_("Theme"))
        go('lblLightDmMenu').set_label(_("Menu"))
        self.chkHideUsers.set_label(_("Hide users"))
        go('lblUsersFace').set_label(_("User icon"))
        go('lblUsersAutologin').set_label(_("Auto-login"))

        # Get current background image
        self.cfgGreeter = Config(self.greeterConf)
        try:
            self.curBgPath = self.cfgGreeter.getValue('greeter', 'background')
            self.curTheme = self.cfgGreeter.getValue('greeter', 'theme-name')
        except:
            self.curBgPath = None
            self.curTheme = None

        # Get current auto-login user
        self.cfgLightdm = Config(self.lightdmConf)
        try:
            self.curAutoUser = self.cfgLightdm.getValue('SeatDefaults', 'autologin-user').strip()
            self.curHideUsers = False
            ghu = self.cfgLightdm.getValue('SeatDefaults', 'greeter-hide-users').strip()
            if 'true' in ghu:
                self.curHideUsers = True
        except:
            self.curAutoUser = None
            self.curHideUsers = False

        # Init
        self.usr = User()
        self.newbgImg = self.curBgPath
        self.newAutoUser = self.curAutoUser
        self.newTheme = self.curTheme
        self.themes = []
        self.selectedMenuItem = None
        self.debug = False
        self.logPath = ''
        self.prevPath = None
        self.tempFace = "/tmp/face"
        self.newFaces = []
        self.loggedUser = functions.getUserLoginName()
        self.curUser = self.loggedUser
        self.selectImg = join(self.scriptDir, '../../share/lightdm-manager/select.png')

        # Handle arguments
        try:
            opts, args = getopt.getopt(sys.argv[1:], 'dl:', ['debug', 'log='])
        except getopt.GetoptError:
            print(("Arguments cannot be parsed: %s" % str(sys.argv[1:])))
            sys.exit(1)

        for opt, arg in opts:
            if opt in ('-d', '--debug'):
                self.debug = True
            elif opt in ('-l', '--log'):
                self.logPath = arg

        # Initialize logging
        if self.debug:
            if not self.logPath:
                self.logPath = 'lightdm-manager.log'
        self.log = Logger(self.logPath, 'debug', True, None, self.window)

        # Backup config files because ConfigParser does not preserve commented lines
        if not exists("%s.org" % self.greeterConf):
            copy(self.greeterConf, "%s.org" % self.greeterConf)
            self.log.write("%(conf1)s copied to %(conf2)s.org" % { "conf1": self.greeterConf, "conf2": self.greeterConf }, 'LightDMManager.main', 'debug')
        if not exists("%s.org" % self.lightdmConf):
            copy(self.lightdmConf, "%s.org" % self.lightdmConf)
            self.log.write("%(conf1)s copied to %(conf2)s.org" % { "conf1": self.lightdmConf, "conf2": self.lightdmConf }, 'LightDMManager.main', 'debug')

        # Initiate the treeview handler and connect the custom toggle event with usersCheckBoxToggled
        self.tvHandler = TreeViewHandler(self.tvUsers, self.log)
        self.tvHandler.connect('checkbox-toggled', self.usersCheckBoxToggled)

        # Get users
        self.users = self.usr.getUsers()
        self.fillUsers()
        self.tvHandler.selectValue(self.curUser, 1)
        self.setBackground(self.curBgPath)
        self.cmbHandlerThemes = ComboBoxHandler(self.cmbThemes)
        self.listThemes()
        self.chkHideUsers.set_active(self.curHideUsers)

        # Show users menu
        self.on_btnUsers_clicked(None)
        self.on_tvUsers_cursor_changed(None)

        self.version = functions.getPackageVersion('lightdm-manager')

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Menu section functions
    # ===============================================

    def on_btnUsers_clicked(self, widget, event=None):
        if self.selectedMenuItem != menuItems[0]:
            self.selectedMenuItem = menuItems[0]
            self.nbLightDM.set_current_page(0)

    def on_btnAppearance_clicked(self, widget, event=None):
        if self.selectedMenuItem != menuItems[1]:
            self.selectedMenuItem = menuItems[1]
            self.nbLightDM.set_current_page(1)

    # ===============================================
    # Functions
    # ===============================================

    def on_ebFace_enter_notify_event(self, widget, event):
        self.window.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebFace_leave_notify_event(self, widget, event):
        self.window.get_window().set_cursor(None)

    def on_ebFace_button_release_event(self, widget, event):
        home = self.usr.getUserHomeDir(self.curUser)
        primaryGroup = self.usr.getUserPrimaryGroupName(self.curUser)
        imagePath = SelectImageDialog(_('Select user image'), home, self.window).show()
        if imagePath is not None:
            tempUserImg = "%(tempFace)s.%(curUser)s" % {"tempFace": self.tempFace, "curUser": self.curUser}
            self.newFaces.append([tempUserImg, join(home, ".face"), self.curUser, primaryGroup])
            print((">>> self.newFaces = %s" % self.newFaces))
            ih = ImageHandler(imagePath)
            ih.makeFaceImage(tempUserImg)
            if exists(tempUserImg):
                self.imgFace.set_from_pixbuf(ih.pixbuf)
            else:
                # This should never happen
                self.imgFace.set_from_file(self.selectImg)

    def on_tvUsers_cursor_changed(self, widget):
        self.curUser = self.tvHandler.getSelectedValue(1)
        showFace = None
        if self.newFaces:
            for face in self.newFaces:
                if face[2] == self.curUser and exists(face[0]):
                    showFace = GdkPixbuf.Pixbuf.new_from_file(face[0])
        if showFace is None:
            showFace = self.usr.getUserFacePixbuf(self.curUser)
        if showFace is None:
            # Still no user icon found: show select image
            self.imgFace.set_from_file(self.selectImg)
        else:
            self.imgFace.set_from_pixbuf(showFace)

    def on_btnSave_clicked(self, widget):
        saved = False
        saveHideUsers = False
        saveAutoUser = False
        saveFaces = False
        saveBackground = False
        saveTheme = False
        if self.chkHideUsers.get_active() != self.curHideUsers:
            saveHideUsers = True
        if self.curAutoUser != self.newAutoUser:
            saveAutoUser = True
        if self.newFaces:
            saveFaces = True
        if self.curBgPath != self.newbgImg:
            saveBackground = True
        self.newTheme = self.cmbHandlerThemes.getValue()
        if self.curTheme != self.newTheme:
            saveTheme = True

        if saveHideUsers or saveAutoUser or saveFaces or saveBackground or saveTheme:
            qd = QuestionDialog(_("LightDM settings"), _("Settings have changed\n\nDo you want to save the new settings?"), self.window)
            answer = qd.show()
            if answer:
                if saveAutoUser:
                    if self.newAutoUser is not None:
                        # Save the auto-login user
                        self.cfgLightdm.setValue('SeatDefaults', 'autologin-user', self.newAutoUser)
                        self.cfgLightdm.setValue('SeatDefaults', 'autologin-user-timeout', '0')
                        self.curAutoUser = self.newAutoUser
                        self.log.write("New auto-login user: %(usr)s" % { "usr": self.curAutoUser }, 'LightDMManager.saveSettings', 'debug')
                    else:
                        self.cfgLightdm.removeOption('SeatDefaults', 'autologin-user')
                        self.cfgLightdm.removeOption('SeatDefaults', 'autologin-user-timeout')
                        self.curAutoUser = None
                        self.log.write("Auto-login disabled", 'LightDMManager.saveSettings', 'debug')
                if saveHideUsers:
                    hideUsers = str(self.chkHideUsers.get_active()).lower()
                    self.cfgLightdm.setValue('SeatDefaults', 'greeter-hide-users', hideUsers)
                    self.log.write("Hide users saved: %(users)s" % {"users": hideUsers}, 'LightDMManager.saveSettings', 'debug')
                if saveFaces:
                    for face in self.newFaces:
                        if exists(face[0]):
                            copy(face[0], face[1])
                            if exists(face[1]):
                                os.system("chown %(owner)s:%(group)s %(path)s" % {"owner": face[2], "group": face[3], "path": face[1]})
                    self.log.write("User icons saved", 'LightDMManager.saveSettings', 'debug')
                if saveTheme:
                    self.cfgGreeter.setValue('greeter', 'theme-name', self.newTheme)
                    self.curTheme = self.newTheme
                    self.log.write("Theme saved: %(theme)s" % { "theme": self.curTheme }, 'LightDMManager.saveSettings', 'debug')
                if saveBackground:
                    if os.path.exists(self.newbgImg):
                        self.cfgGreeter.setValue('greeter', 'background', self.newbgImg)
                        self.curBgPath = self.newbgImg
                        self.log.write("Background saved: %(background)s" % { "background": self.curBgPath }, 'LightDMManager.saveSettings', 'debug')
                saved = True
            else:
                if os.path.exists(self.curBgPath):
                    self.setBackground(self.curBgPath)
                    self.log.write("Current background: %(background)s" % { "background": self.curBgPath }, 'LightDMManager.saveSettings', 'debug')
                else:
                    self.imgBackground.set_from_file(join(self.scriptDir, '../../share/lightdm-manager/select.png'))
                    self.log.write("No background set", 'LightDMManager.saveSettings', 'debug')
                self.fillUsers()

        if saved:
            self.curHideUsers = self.chkHideUsers.get_active()
            self.curAutoUser = self.newAutoUser
            self.newFaces = []
            self.curBgPath = self.newbgImg
            self.curTheme = self.newTheme
            MessageDialogSafe(_("Saved"), _("LightDM settings saved successfully."), Gtk.MessageType.INFO, self.window).show()


    def on_ebBackground_button_release_event(self, widget, event):
        self.newbgImg = SelectImageDialog(_("Choose background image"), self.desktopbaseDir, self.window).show()
        if exists(self.newbgImg) and self.newbgImg != self.curBgPath:
            self.setBackground(self.newbgImg)
            self.log.write(_("New background: %(bg)s") % { "bg": self.newbgImg }, 'LightDMManager.chooseFile', 'info')

    def on_ebBackground_enter_notify_event(self, widget, event):
        self.window.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebBackground_leave_notify_event(self, widget, event):
        self.window.get_window().set_cursor(None)

    # This method is fired by the TreeView.checkbox-toggled event
    def usersCheckBoxToggled(self, obj, path, colNr, toggleValue):
        path = int(path)
        model = self.tvUsers.get_model()
        itr = model.get_iter(path)
        user = model[itr][1]

        if self.prevPath != path or toggleValue:
            # Only one toggle box can be selected (or none)
            self.tvHandler.treeviewToggleAll([0], False, 1, user)
            # Save current path
            self.prevPath = path
            # Save selected user
            self.newAutoUser = user
            self.log.write(_("Auto-login user selected: %(usr)s") % { "usr": user }, 'LightDMManager.usersCheckBoxToggled', 'info')
        elif self.prevPath == path and not toggleValue:
            self.newAutoUser = None

    def listThemes(self):
        themeDir = '/usr/share/themes'
        themeDirLocal = '~/.local/share/themes'
        dirs = functions.locate('gtk-*', themeDir, True) + functions.locate('gtk-*', themeDirLocal, True)
        for path in dirs:
            dirList = path.split('/')
            for d in dirList:
                if 'gtk-' in d:
                    break
                themeName = d
            if themeName not in self.themes:
                self.themes.append(themeName)
        if self.themes:
            self.cmbHandlerThemes.fillComboBox(self.themes)
            if self.curTheme in self.themes:
                self.cmbHandlerThemes.selectValue(self.curTheme)

    def fillUsers(self):
        selUsr = False
        contentList = []
        i = 0
        for usr in self.users:
            if usr == self.curAutoUser:
                selUsr = True
                self.prevPath = i
            else:
                selUsr = False
            contentList.append([selUsr, usr])
            i += 1

        # Fill treeview with users
        #fillTreeview(contentList, columnTypesList, columnHideList=[-1], setCursor=0, setCursorWeight=400, firstItemIsColName=False, appendToExisting=False, appendToTop=False)
        columnTypesList = ['bool', 'str']
        self.tvHandler.fillTreeview(contentList, columnTypesList)

    def setBackground(self, path):
        # Set Background
        if path is not None:
            if exists(path):
                ih = ImageHandler(path)
                ih.resizeImage(height=200)
                self.imgBackground.set_from_pixbuf(ih.pixbuf)
            else:
                self.imgBackground.set_from_file(self.selectImg)
        else:
            self.imgBackground.set_from_file(self.selectImg)

    # ===============================================
    # General functions
    # ===============================================

    def on_ldmWindow_destroy(self, widget, data=None):
        # Close the app
        self.on_btnSave_clicked(None)
        for tmp in self.newFaces:
            os.remove(tmp[0])
        Gtk.main_quit()
class Constructor(object):

    def __init__(self):
        self.scriptDir = abspath(dirname(__file__))
        self.shareDir = join(self.scriptDir, '../../../share/solydxk/constructor')
        self.userAppDir = join(get_user_home_dir(), ".constructor")
        self.distroFile = join(self.userAppDir, "distros.list")

        # Create the user's application directory if it doesn't exist
        if not isdir(self.userAppDir):
            user_name = getUserLoginName()
            makedirs(self.userAppDir)
            old_distro_file = join(self.scriptDir, "distros.list")
            if exists(old_distro_file):
                move(old_distro_file, self.distroFile)
            system("chown -R %s:%s %s" % (user_name, user_name, self.userAppDir))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.shareDir, 'constructor.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go('constructorWindow')
        self.tvDistros = go('tvDistros')
        self.lblOutput = go('lblOutput')
        self.statusbar = go('statusbar')
        self.btnAdd = go('btnAdd')
        self.chkSelectAll = go('chkSelectAll')
        self.btnRemove = go('btnRemove')
        self.btnEdit = go('btnEdit')
        self.btnUpgrade = go('btnUpgrade')
        self.btnLocalize = go('btnLocalize')
        self.btnBuildIso = go('btnBuildIso')

        # Add iso window objects
        self.windowAddDistro = go('addDistroWindow')
        self.txtIso = go('txtIso')
        self.txtDir = go('txtDir')
        self.btnDir = go('btnDir')
        self.btnSave = go('btnSave')
        self.btnHelp = go('btnHelp')
        self.lblIso = go('lblIso')
        self.boxIso = go('boxIso')
        self.lblDir = go('lblDir')
        self.chkFromIso = go('chkFromIso')

        # Main window translations
        self.window.set_title(_("SolydXK Constructor"))
        self.chkSelectAll.set_label(_("Select all"))
        self.btnAdd.set_label("_{}".format(_("Add")))
        self.btnRemove.set_label("_{}".format(_("Remove")))
        self.btnEdit.set_label("_{}".format(_("Edit")))
        self.btnUpgrade.set_label("_{}".format(_("Upgrade")))
        self.btnLocalize.set_label("_{}".format(_("Localize")))
        self.btnBuildIso.set_label("_{}".format(_("Build")))
        self.btnHelp.set_label("_{}".format(_("Help")))

        # Add iso window translations
        self.windowAddDistro.set_title(_("Add Distribution"))
        self.lblIso.set_text(_("ISO"))
        go('lblFromIso').set_label("Create from ISO")
        go('btnCancel').set_label("_{}".format(_("Cancel")))
  
        # Init
        self.ec = ExecCmd()
        self.ec.run("modprobe loop", False)
        self.queue = Queue()
        self.mountDir = "/mnt/constructor"
        self.distroAdded = False
        self.iso = None
        self.dir = None
        self.isoName = None
        self.doneWav = join(self.shareDir, 'done.wav')
        self.htmlDir = join(self.shareDir, "html")
        self.help = join(self.get_language_dir(), "help.html")
        self.chkFromIso.set_active(True)
        self.toggleGuiElements(False)

        # Treeviews
        self.tvHandlerDistros = TreeViewHandler(self.tvDistros)
        self.fillTreeViewDistros()

        # Version information
        ver = _("Version")
        self.version = "%s: %s" % (ver, getPackageVersion('solydxk-constructor'))
        self.showOutput(self.version)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Main Window Functions
    # ===============================================

    def on_btnAdd_clicked(self, widget):
        self.windowAddDistro.show()

    def on_btnRemove_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \
                                                              "(This will not remove the directory and its data)"), self.window)
            answer = qd.show()
            if answer:
                self.saveDistroFile(distroPath=path, addDistro=False)
        self.fillTreeViewDistros()

    def on_btnEdit_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            de = EditDistro(path)
            services = []
            if exists(join(path, 'root/etc/apache2/apache2.conf')):
                services.append("apache2")
            if exists(join(path, 'root/etc/mysql/debian.cnf')):
                services.append("mysql")
            if services:
                msg = "If you need to update packages that depend on these services,\n" \
                      "you will need to manually start them:\n"
                for service in services:
                    msg += "\nservice %s start" % service
                msg += "\n\nWhen done:\n"
                for service in services:
                    msg += "\nservice %s stop" % service
                self.showInfo(_("Services detected"), msg, self.window)
                repaintGui()
            de.openTerminal()

    def on_btnUpgrade_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        upgraded = False
        for path in selected:
            upgraded = True
            rootPath = "%s/root" % path
            force = get_apt_force(rootPath)
            de = EditDistro(path)
            de.openTerminal("apt-get update")
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 start")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql start")
            de.openTerminal("apt-get -y %s -o Dpkg::Options::=\"--force-confnew\" dist-upgrade" % force)
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 stop")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql stop")

            # Cleanup old kernel and headers
            script = "rmoldkernel.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                silent_remove(scriptTarget)

            # Download offline packages
            print(">> Start downloading offline packages")
            self.download_offline_packages(path)

        if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)

    def on_btnLocalize_clicked(self, widget):
        # Set locale
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            de = EditDistro(path)
            script = "setlocale.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                silent_remove(scriptTarget)
            
            # Copy Grub locale files to ISO boot directory and configure grub.cfg
            grub_path = "%s/boot/boot/grub/" % path
            grubcfg_path = "%s/grub.cfg" % grub_path
            locale_path = "%s/root/boot/grub/locale" % path
            default_path = "%s/root/etc/default" % path
            locale = self.ec.run("grep -oP '(?<=LANG=).*?(?=\.)' %s/locale" % default_path)[0]
            if exists(locale_path) and \
               exists(grubcfg_path) and \
               locale:
                self.ec.run("cp -rf %s %s" % (locale_path, grub_path))
                self.ec.run("sed -i 's/set lang=.*/set lang=%s/' %s" % (locale, grubcfg_path))

    def download_offline_packages(self, path):
        rootPath = "%s/root" % path
        arch = getGuestEfiArchitecture(rootPath)
        de = EditDistro(path)
        script = "offline.sh"
        scriptSource = join(self.scriptDir, "files/{}".format(script))
        scriptTarget = join(rootPath, script)
        offlineSource = join(rootPath, "offline")
        offlineTarget = join(rootPath, "../boot/offline")
        if exists(scriptSource):
            try:
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                # Run the script
                de.openTerminal("/bin/bash {} {}".format(script, arch))
                # Remove script
                silent_remove(scriptTarget)
                # Move offline directory to boot directory
                if exists(offlineSource):
                    print(("%s exists" % offlineSource))
                    if exists(offlineTarget):
                        print((">> Remove %s" % offlineTarget))
                        silent_remove(offlineTarget)
                    print((">> Move %s to %s" % (offlineSource, offlineTarget)))
                    move(offlineSource, offlineTarget)
                else:
                    print((">> Cannot find: %s" % offlineSource))
            except Exception as detail:
                self.showError("Error: getting offline packages", detail, self.window)
        else:
            print((">> Cannot find: %s" % scriptSource))


    def on_btnBuildIso_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        msg = ""
        for path in selected:
            self.toggleGuiElements(True)
            self.showOutput("Start building ISO in: %s" % path)
            repaintGui()

            # Start building the ISO in a thread
            t = BuildIso(path, self.queue)
            t.start()
            self.queue.join()

            # Thread is done
            # Get the data from the queue
            ret = self.queue.get()
            self.queue.task_done()

            if ret is not None:
                self.showOutput(ret)
                if "error" in ret.lower():
                    self.showError("Error", ret, self.window)
                else:
                    msg += "%s\n" % ret

        if msg != "":
            if exists("/usr/bin/aplay") and exists(self.doneWav):
                self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
            self.showInfo("", msg, self.window)
        self.toggleGuiElements(False)

    def on_chkSelectAll_toggled(self, widget):
        self.tvHandlerDistros.treeviewToggleAll(toggleColNrList=[0], toggleValue=widget.get_active())

    def on_tvDistros_row_activated(self, widget, path, column):
        self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0])

    def on_btnHelp_clicked(self, widget):
        system("open-as-user %s" % self.help)

    def on_btnOpenDir_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            system("open-as-user %s" % path)

    def fillTreeViewDistros(self, selectDistros=[]):
        contentList = [[_("Select"), _("Distribution"), _("Working directory")]]
        distros = self.getDistros()
        for distro in distros:
            select = False
            for selectDistro in selectDistros:
                if distro[0] == selectDistro:
                    select = True
            contentList.append([select, distro[0], distro[1]])
        self.tvHandlerDistros.fillTreeview(contentList=contentList, columnTypesList=['bool', 'str', 'str'], firstItemIsColName=True)

    def getDistros(self):
        distros = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                lines = f.readlines()
            for line in lines:
                line = line.strip().rstrip('/')
                print(line)
                if exists(line):
                    dg = DistroGeneral(line)
                    isoName = dg.description
                    distros.append([isoName, line])
            # Sort on iso name
            if distros:
                distros = sorted(distros, key=operator.itemgetter(0))
        return distros

    # ===============================================
    # Add ISO Window Functions
    # ===============================================

    def on_btnIso_clicked(self, widget):
        fleFilter = Gtk.FileFilter()
        fleFilter.set_name("ISO")
        fleFilter.add_mime_type("application/x-cd-image")
        fleFilter.add_pattern("*.iso")

        startDir = None
        if exists(dirname(self.txtIso.get_text())):
            startDir = dirname(self.txtIso.get_text())

        filePath = SelectFileDialog(title=_('Select SolydXK ISO'), start_directory=startDir, parent=self.window, gtkFileFilter=fleFilter).show()
        if filePath is not None:
            self.txtIso.set_text(filePath)

    def on_btnDir_clicked(self, widget):
        startDir = None
        if exists(self.txtDir.get_text()):
            startDir = self.txtDir.get_text()
        dirText = SelectDirectoryDialog(title=_('Select directory'), start_directory=startDir, parent=self.window).show()
        if dirText is not None:
            self.txtDir.set_text(dirText)

    def on_btnSave_clicked(self, widget):
        self.iso = ""
        if self.chkFromIso.get_active():
            self.iso = self.txtIso.get_text()
        self.dir = self.txtDir.get_text()

        title = _("Save existing working directory")
        if self.iso != "":
            title = _("Unpack ISO and save")

        if not exists(self.dir):
            makedirs(self.dir)

        if not exists(self.dir):
            self.showError(title, _("Could not create directory %(dir)s: exiting" % {"dir": self.dir}), self.window)
        else:
            self.windowAddDistro.hide()
            if self.iso != "":
                if not exists(self.iso):
                    self.showInfo(self.btnSave.get_label(), _("The path to the ISO file does not exist:\n{}".format(self.iso)), self.window)
                    return
                if listdir(self.dir):
                    qd = QuestionDialog(self.btnSave.get_label(),
                        _("The destination directory is not empty.\n"
                        "Are you sure you want to overwrite all data in {}?".format(self.dir)),
                        self.window)
                    answer = qd.show()
                    if not answer:
                        return

                self.showOutput("Start unpacking the ISO...")
                self.toggleGuiElements(True)
                t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue)
                t.start()
                self.queue.join()
                GObject.timeout_add(1000, self.checkThread, True)
            else:
                self.saveDistroFile(self.dir, True)
                self.fillTreeViewDistros()
                self.showOutput(_("Existing working directory added"))
                #self.toggleGuiElements(False)

    def on_btnCancel_clicked(self, widget):
        self.windowAddDistro.hide()

    def on_addDistroWindow_delete_event(self, widget, data=None):
        self.windowAddDistro.hide()
        return True

    def on_txtIso_changed(self, widget):
        path = self.txtIso.get_text()
        if exists(path):
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            if exists(self.txtDir.get_text()):
                self.btnSave.set_sensitive(True)
        else:
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)

    def on_txtDir_changed(self, widget):
        blnFromIso = self.chkFromIso.get_active()
        isoPath = self.txtIso.get_text()
        dirText = self.txtDir.get_text()
        self.btnSave.set_sensitive(False)
        if exists(dirText):
            if blnFromIso:
                if exists(isoPath):
                    self.btnSave.set_sensitive(True)
            else:
                self.btnSave.set_sensitive(True)

    def on_chkFromIso_toggled(self, widget):
        if widget.get_active():
            self.lblIso.set_visible(True)
            self.boxIso.set_visible(True)
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)
            self.lblDir.set_text(_("Unpack ISO to directory"))
            self.btnSave.set_label(_("Unpack & Save"))
        else:
            self.txtIso.set_text("")
            self.lblIso.set_visible(False)
            self.boxIso.set_visible(False)
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            self.btnSave.set_sensitive(True)
            self.lblDir.set_text(_("Work directory"))
            self.btnSave.set_label(_("Save"))

    # ===============================================
    # General functions
    # ===============================================

    def showInfo(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show()

    def showError(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show()

    def showOutput(self, message):
        print(message)
        pushMessage(self.statusbar, message)

    def checkThread(self, addDistro=None):
        #print 'Thread count = ' + str(threading.active_count())
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            return True

        # Thread is done
        # Get the data from the queuez
        ret = self.queue.get()
        self.queue.task_done()

        # Thread is done
        if addDistro is not None:
            self.saveDistroFile(self.dir, addDistro)
            self.fillTreeViewDistros(self.isoName)
        self.toggleGuiElements(False)
        if exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
        if ret is not None:
            self.showOutput(ret)
            if "error" in ret.lower():
                self.showError("Error", ret, self.window)
            else:
                self.showInfo("", ret, self.window)
        return False

    def toggleGuiElements(self, startThread):
        if startThread:
            self.chkSelectAll.set_sensitive(False)
            self.tvDistros.set_sensitive(False)
            self.btnAdd.set_sensitive(False)
            self.btnBuildIso.set_sensitive(False)
            self.btnEdit.set_sensitive(False)
            self.btnRemove.set_sensitive(False)
            self.btnUpgrade.set_sensitive(False)
            self.btnLocalize.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnHelp.set_sensitive(False)
        else:
            self.chkSelectAll.set_sensitive(True)
            self.tvDistros.set_sensitive(True)
            self.btnAdd.set_sensitive(True)
            self.btnBuildIso.set_sensitive(True)
            self.btnEdit.set_sensitive(True)
            self.btnRemove.set_sensitive(True)
            self.btnUpgrade.set_sensitive(True)
            self.btnLocalize.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            self.btnHelp.set_sensitive(True)

    def saveDistroFile(self, distroPath, addDistro=True):
        newCont = []
        dg = DistroGeneral(distroPath)
        self.isoName = dg.description

        cfg = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                cfg = f.readlines()
            for line in cfg:
                line = line.strip()
                if distroPath != line and exists(line):
                    newCont.append(line)

        if addDistro:
            newCont.append(distroPath)

        with open(self.distroFile, 'w') as f:
            f.write('\n'.join(newCont))

        self.iso = ""
        self.dir = ""

    # Close the gui
    def on_constructorWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
        
    # ===============================================
    # Language specific functions
    # ===============================================

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.htmlDir, "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang
Exemple #5
0
class UpdateManager(object):
    def __init__(self):
        # Check if script is running
        self.scriptName = basename(__file__)
        self.umglobal = UmGlobal()
        self.user = self.umglobal.getLoginName()

        # Initiate logging
        self.logFile = join('/var/log', self.umglobal.settings['log'])
        print(("UM log = %s" % self.logFile))
        self.log = Logger(self.logFile, maxSizeKB=5120)

        # Remove scripts
        self.deleteScripts(self.umglobal.localUpdVersion)

        # Initialize
        self.ec = ExecCmd(loggerObject=self.log)
        self.apt = UmApt(self.umglobal)
        self.kernelVersion = self.umglobal.getKernelVersion()
        self.upgradables = []
        self.upgradableUM = []
        self.window = None

        # Handle arguments
        parser = argparse.ArgumentParser(description='SolydXK Update Manager')
        parser.add_argument('-q',
                            '--quick',
                            action="store_true",
                            help='Quick upgrade')
        parser.add_argument('-r', '--reload', action="store_true", help='')
        args, extra = parser.parse_known_args()

        self.quickUpdate = False
        if args.quick and not self.umglobal.newUpd:
            self.quickUpdate = True
        if args.reload:
            pids = self.umglobal.getProcessPids("updatemanager.py")
            if len(pids) > 1:
                print(("updatemanager.py already running - kill pid {}".format(
                    pids[0])))
                os.system("kill {}".format(pids[0]))

        # Set some global translations
        self.aptErrorText = _("Apt error")
        self.upgradablePackagesText = _(
            "The following packages will be upgraded:")
        self.newPackagesText = _(
            "The following NEW packages will be installed:")
        self.removedPackagesText = _("The following packages will be REMOVED:")
        self.heldbackPackagesText = _(
            "The following packages have been kept back:")
        self.downgradePackagesText = _(
            "The following packages are going to be downgraded:")

        # Cleanup first
        for fle in glob(join(self.umglobal.filesDir, '.um*')):
            remove(fle)

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            join(self.umglobal.shareDir, 'updatemanager.glade'))
        go = self.builder.get_object

        # Quick update
        if self.quickUpdate:
            # Refresh data
            self.refresh()
            self.umglobal.collectData()
            self.apt.createPackagesInfoList()
            self.apt.createPackageLists()
            self.fillTreeView()

            # Run upgrade
            nid = self.run_upgrade()

            if nid != "":
                self.on_command_done(None, 0, nid)

            sys.exit(2)

        # Make sure the files directory is set correctly
        self.checkFilesDir()

        # Main window objects
        self.window = go("windowMain")
        #self.window.set_icon_from_file(join(self.umglobal.iconsDir, self.umglobal.settings["icon-connected"]))
        self.tvPck = go("tvPck")
        self.swTerminal = go("swTerminal")
        self.statusbar = go("statusbar")
        self.btnInstall = go("btnInstall")
        self.btnRefresh = go("btnRefresh")
        self.btnPackages = go("btnPackages")
        self.btnOutput = go("btnOutput")
        self.btnInfo = go("btnInfo")
        self.btnPreferences = go("btnPreferences")
        self.nbMain = go("nbMain")
        self.swInfo = go("swInfo")
        self.btnMaintenance = go("btnMaintenance")
        self.lblMaintenance = go("lblMaintenance")
        self.tvMaintenance = go("tvMaintenance")
        self.btnMaintenanceExecute = go("btnMaintenanceExecute")
        self.chkMaintenanceSelectAll = go("chkMaintenanceSelectAll")
        self.radUnneeded = go("radUnneeded")
        self.radCleanCache = go("radCleanCache")
        self.radDowngradable = go("radDowngradable")
        self.radNotavailable = go("radNotavailable")
        self.radOldKernel = go("radOldKernel")
        self.lblMaintenanceHelp = go("lblMaintenanceHelp")

        # Translations
        self.window.set_title(_("SolydXK Update Manager"))
        self.btnInstall.set_label(_("Install"))
        self.btnRefresh.set_label(_("Refresh"))
        self.btnOutput.set_label(_("Output"))
        self.btnInfo.set_label(_("Information"))
        self.btnPreferences.set_label(_("Preferences"))
        self.btnMaintenance.set_label(_("Maintenance"))
        self.btnPackages.set_label(_("Packages"))
        self.uptodateText = self.umglobal.connectedText
        self.lblMaintenance.set_label(self.btnMaintenance.get_label())
        self.btnMaintenanceExecute.set_label(_("Execute"))
        self.chkMaintenanceSelectAll.set_label(_("Select all"))
        self.radCleanCache.set_label(_("Clean up the apt cache"))
        self.radUnneeded.set_label(_("Remove unneeded packages"))
        self.radNotavailable.set_label(
            _("Remove packages not available\nin the repositories"))
        self.radOldKernel.set_label(_("Remove old kernels"))
        self.radDowngradable.set_label(
            _("Downgrade packages with\nonly lower versions available"))
        self.lblMaintenanceHelp.set_label(
            _("Make sure you create\n"
              "a system image before you\n"
              "continue (e.g. Clonezilla)."))

        # VTE Terminal
        self.terminal = VirtualTerminal(userInputAllowed=self.umglobal.
                                        settings["allow-terminal-user-input"])
        self.swTerminal.add(self.terminal)
        self.terminal.set_vexpand(True)
        self.terminal.set_hexpand(True)
        self.terminal.connect('command-done', self.on_command_done)
        self.terminal.connect('line-added', self.on_line_added)
        palletList = [
            '#4A4A4A', '#BD1919', '#118011', '#CE6800', '#1919BC', '#8D138D',
            '#139494', '#A7A7A7'
        ]
        self.terminal.setTerminalColors("#000000", "#FFFFFF", palletList)
        self.swTerminal.modify_bg(Gtk.StateType.NORMAL,
                                  Gdk.color_parse("#FFFFFF"))

        # Disable all buttons
        self.btnInfo.set_sensitive(False)
        self.btnPreferences.set_sensitive(False)
        self.btnOutput.set_sensitive(False)
        self.btnRefresh.set_sensitive(False)
        self.btnInstall.set_sensitive(False)
        self.btnPackages.set_sensitive(False)
        self.btnMaintenance.set_sensitive(False)

        # Hide tabs if needed
        for tab in self.umglobal.settings["hide-tabs"]:
            if tab == "packages":
                self.nbMain.get_nth_page(0).set_visible(False)
                self.btnPackages.set_visible(False)
            elif tab == "output":
                self.nbMain.get_nth_page(1).set_visible(False)
                self.btnOutput.set_visible(False)
            elif tab == "info":
                self.nbMain.get_nth_page(2).set_visible(False)
                self.btnInfo.set_visible(False)
            elif tab == "maintenance":
                self.nbMain.get_nth_page(3).set_visible(False)
                self.btnMaintenance.set_visible(False)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

        # Force the window to show
        while Gtk.events_pending():
            Gtk.main_iteration()

        # Just show something that we're busy
        msg = _("Gathering information...")
        self.terminal.executeCommand('echo "%s"' % msg, 'init')
        self.showOutput()

        # Treeview handlers
        self.tvHandler = TreeViewHandler(self.tvPck)
        self.tvMaintenanceHandler = TreeViewHandler(self.tvMaintenance)

        # Version information
        ver = _("Version")
        pckVer = self.apt.getPackageVersion('updatemanager')
        versionInfo = "%(ver)s: %(pckVer)s" % {"ver": ver, "pckVer": pckVer}
        if self.umglobal.localUpdVersion != "2000.01.01":
            versionInfo = "%(ver)s: %(pckVer)s" % {
                "ver": ver,
                "pckVer": pckVer
            }
        self.pushMessage(versionInfo)

        # Log basic information
        self.log.write("==============================================",
                       "UM.init", "debug")
        self.log.write("UM version = %s" % versionInfo, "UM.init", "debug")
        self.log.write("==============================================",
                       "UM.init", "debug")
        mirrorsList = join(self.umglobal.filesDir,
                           basename(self.umglobal.settings["mirrors-list"]))
        if exists(mirrorsList):
            self.log.write("Mirrors list", "UM.init", "debug")
            with open(mirrorsList, 'r') as f:
                for line in f.readlines():
                    self.log.write(line, "UM.init", "debug")
            self.log.write("==============================================",
                           "UM.init", "debug")

        # Refresh apt cache
        self.refresh()

        # Initialize maintenance screen
        self.fillTreeViewMaintenance()

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnInstall_clicked(self, widget):
        self.run_upgrade()

    def run_upgrade(self):
        nid = ""
        aptHasErrors = self.apt.aptHasErrors()
        if aptHasErrors is not None:
            MessageDialog(self.aptErrorText, aptHasErrors)
        elif self.upgradables:

            if self.apt.upgradablePackages:
                self.log.write(
                    "=================== upgradable pacages ====================",
                    "UM.run_upgrade", "debug")
                self.log.write(
                    self.createLogString(self.apt.upgradablePackages),
                    "UM.run_upgrade", "debug")
            if self.apt.removedPackages:
                self.log.write(
                    "==================== removed packages =====================",
                    "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.removedPackages),
                               "UM.run_upgrade", "debug")
            if self.apt.newPackages:
                self.log.write(
                    "======================= new packages =======================",
                    "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.newPackages),
                               "UM.run_upgrade", "debug")
            if self.apt.heldbackPackages:
                self.log.write(
                    "=================== kept back packages =====================",
                    "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.heldbackPackages),
                               "UM.run_upgrade", "debug")

            if not self.quickUpdate:
                self.showOutput()
            contMsg = _("Continue installation?")
            if self.upgradableUM:
                cmd = "%s install updatemanager" % self.umglobal.settings[
                    'apt-get-string']
                cmd += "; %s install %s" % (
                    self.umglobal.settings['apt-get-string'], " ".join(
                        self.apt.getPackageDependencies('updatemanager')))
                nid = 'uminstallum'
                self.prepForCommand(nid)
                if self.quickUpdate:
                    self.ec.run(cmd)
                else:
                    self.terminal.executeCommand(cmd, nid)
                self.log.write("Execute command: %s (%s)" % (cmd, nid),
                               "UM.on_btnInstall_clicked", "debug")
            else:
                msg = self.getDistUpgradeInfo()
                answer = True
                if msg != "":
                    answer = self.showConfirmationDlg(contMsg, msg)
                if answer:
                    cmd = "%s dist-upgrade" % self.umglobal.settings[
                        'apt-get-string']
                    #if self.umglobal.newUpd:
                    pre = join(
                        self.umglobal.filesDir,
                        self.umglobal.settings['pre-upd'].replace(
                            "[VERSION]", self.umglobal.serverUpdVersion))
                    post = join(
                        self.umglobal.filesDir,
                        self.umglobal.settings['post-upd'].replace(
                            "[VERSION]", self.umglobal.serverUpdVersion))
                    if exists(pre):
                        cmd = "/bin/bash %(pre)s; %(cmd)s" % {
                            "pre": pre,
                            "cmd": cmd
                        }
                    if exists(post):
                        cmd = "%(cmd)s; /bin/bash %(post)s" % {
                            "cmd": cmd,
                            "post": post
                        }
                    nid = 'umupd'
                    self.prepForCommand(nid)
                    if self.quickUpdate:
                        self.ec.run(cmd)
                    else:
                        self.terminal.executeCommand(cmd, nid)
                    self.log.write("Execute command: %s (%s)" % (cmd, nid),
                                   "UM.on_btnInstall_clicked", "debug")

        else:
            if not self.quickUpdate:
                MessageDialog(self.btnInstall.get_label(), self.uptodateText)

        return nid

    def on_btnRefresh_clicked(self, widget):
        self.refresh()

    def on_btnPackages_clicked(self, widget):
        self.showPackages()

    def on_btnOutput_clicked(self, widget):
        self.showOutput()

    def on_btnInfo_clicked(self, widget):
        self.showInfo()

    def on_btnPreferences_clicked(self, widget):
        # Run preferences in its own thread
        pref_thread = threading.Thread(target=self.openPreferences)
        pref_thread.setDaemon(True)
        pref_thread.start()

    def openPreferences(self):
        os.system("updatemanager -p")

    def on_btnMaintenance_clicked(self, widget):
        self.showMaintenance()

    def on_radCleanCache_toggled(self, widget):
        if widget.get_active():
            self.fillTreeViewMaintenance()

    def on_radUnneeded_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (
                _("You might need to run this several times."),
                self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radNotavailable_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (
                _("Removing not available packages may break your system!"),
                self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radOldKernel_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (
                _("Once removed you will not be able to boot these kernels!"),
                self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radDowngradable_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (
                _("Downgrading packages may break your system!"),
                self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_btnMaintenanceExecute_clicked(self, widget):
        self.executeMaintenance()

    def on_chkMaintenanceSelectAll_toggled(self, widget):
        self.tvMaintenanceHandler.treeviewToggleAll(
            toggleColNrList=[0], toggleValue=widget.get_active())

    # ===============================================
    # Maintenance functions
    # ===============================================

    def enableMaintenance(self, enable=True):
        self.btnMaintenanceExecute.set_sensitive(enable)
        self.radUnneeded.set_sensitive(enable)
        self.radCleanCache.set_sensitive(enable)
        self.radDowngradable.set_sensitive(enable)
        self.radOldKernel.set_sensitive(enable)
        self.radNotavailable.set_sensitive(enable)
        self.chkMaintenanceSelectAll.set_sensitive(enable)
        if enable:
            self.chkMaintenanceSelectAll.set_active(False)

    def fillTreeViewMaintenance(self):
        blnCleanCache = self.radCleanCache.get_active()
        blnUnneeded = self.radUnneeded.get_active()
        blnDowngradable = self.radDowngradable.get_active()
        blnNotavailable = self.radNotavailable.get_active()
        blnOldKernels = self.radOldKernel.get_active()

        self.enableMaintenance(False)

        columnTypesList = ['bool', 'str', 'str', 'str']
        packages = [["", _("Package"), _("Installed"), _("Available")]]
        # Clear the treeview first
        self.tvMaintenanceHandler.fillTreeview(packages, columnTypesList, 0,
                                               400, True)

        msg = ""
        if blnCleanCache:
            msg = _("Hit the Execute button to clean the cache.")
            columnTypesList = ['str']
            packages.append([msg])
        elif blnUnneeded:
            msg = self.radUnneeded.get_label()
            self.apt.createPackageLists("apt-get autoremove")
            for pck in self.apt.removedPackages:
                packages.append([False, pck[0], pck[1], pck[2]])
            self.apt.createPackageLists()
            # Add orphaned files
            self.apt.fillOrphanedPackages()
            for pck in self.apt.orphanedPackages:
                packages.append([False, pck[0], pck[1], pck[2]])
        elif blnNotavailable:
            msg = self.radNotavailable.get_label()
            self.apt.fillNotAvailablePackages()
            for pck in self.apt.notavailablePackages:
                if pck[0][0:6] != "linux-":
                    packages.append([False, pck[0], pck[1], ""])
        elif blnOldKernels:
            msg = self.radOldKernel.get_label()
            self.apt.fillKernelPackages()
            for pck in self.apt.kernelPackages:
                if "headers-486" not in pck[0] \
                    and "headers-586" not in pck[0] \
                    and "headers-686" not in pck[0] \
                    and "headers-amd64" not in pck[0] \
                    and "image-486" not in pck[0] \
                    and "image-586" not in pck[0] \
                    and "image-686" not in pck[0] \
                    and "image-amd64" not in pck[0]:

                    checkVersion = self.kernelVersion
                    if "kbuild" in pck[0]:
                        indMinus = self.kernelVersion.index("-")
                        indZero = self.kernelVersion.index("0")
                        if indZero > 0 and indZero < indMinus:
                            ind = indZero - 1
                        else:
                            ind = indMinus
                        if ind > 0:
                            checkVersion = self.kernelVersion[0:ind]
                    if checkVersion not in pck[0]:
                        packages.append([False, pck[0], pck[1], ""])
        elif blnDowngradable:
            msg = self.radDowngradable.get_label()
            self.apt.fillDowngradablePackages()
            for pck in self.apt.downgradablePackages:
                packages.append([False, pck[0], pck[1], pck[2]])

        if len(packages) > 1:
            self.tvMaintenanceHandler.fillTreeview(packages, columnTypesList,
                                                   0, 400, True)
        else:
            if not blnCleanCache:
                msg = _("\"%s\"\n did not return any results.") % msg
                MessageDialog(self.btnMaintenance.get_label(), msg)

        self.enableMaintenance(True)

    def executeMaintenance(self):
        blnCleanCache = self.radCleanCache.get_active()
        blnDowngradable = self.radDowngradable.get_active()
        blnNotNeeded = self.radUnneeded.get_active()
        downgradeString = ""
        deleteString = ""
        updateGrub = False
        cmd = ""

        self.enableMaintenance(False)

        if blnCleanCache:
            safe = False
            msg = _(
                "Do you want to completely clean the apt cache?\n\n"
                "When No, only unavailable installation packages are removed.")
            answer = QuestionDialog(self.radCleanCache.get_label(), msg)
            if answer:
                safe = True
            self.apt.cleanCache(safe)
            msg = _("Apt cache has been cleaned.")
            MessageDialog(self.radCleanCache.get_label(), msg)
        else:
            # Get user selected packages
            model = self.tvMaintenance.get_model()
            itr = model.get_iter_first()
            while itr is not None:
                sel = model.get_value(itr, 0)
                if sel:
                    pck = model.get_value(itr, 1)
                    avVer = model.get_value(itr, 3)
                    if blnDowngradable:
                        downgradeString += " %(pck)s=%(avVer)s" % {
                            "pck": pck,
                            "avVer": avVer
                        }
                    else:
                        deleteString += " %s" % pck
                        if "linux-image" in pck:
                            updateGrub = True
                itr = model.iter_next(itr)

            if downgradeString != "":
                cmd = "%s install %s" % (
                    self.umglobal.settings['apt-get-string'], downgradeString)
            elif deleteString != "":
                cmd = "%s purge %s" % (
                    self.umglobal.settings['apt-get-string'], deleteString)

        if cmd != "":
            self.apt.createPackageLists(cmd)
            msg = self.getDistUpgradeInfo()
            answer = True
            if msg != "":
                contMsg = _("Execute maintenance changes?")
                answer = self.showConfirmationDlg(contMsg, msg)
            if answer:
                if updateGrub:
                    cmd += "; update-grub"
                if blnNotNeeded:
                    cmd += "; %s purge $(COLUMNS=132 dpkg -l | grep ^rc | awk '{ print $2 }')" % self.umglobal.settings[
                        'apt-get-string']
                self.showOutput()
                nid = 'ummaintenance'
                self.prepForCommand(nid)
                self.terminal.executeCommand(cmd, nid)
                self.log.write("Execute command: %s (%s)" % (cmd, nid),
                               "UM.executeMaintenance", "debug")

        self.enableMaintenance(True)

    # ===============================================
    # General functions
    # ===============================================

    def prepForCommand(self, nid):
        os.system("touch %s" % self.umglobal.umfiles[nid])
        if not self.quickUpdate:
            self.btnRefresh.set_sensitive(False)
            self.btnInstall.set_sensitive(False)
            self.btnMaintenance.set_sensitive(False)

    def on_line_added(self, terminal, line):
        if line.strip()[0:2].upper() == "E:":
            self.log.write(line, "UM.on_line_added", "error")
        else:
            self.log.write(line, "UM.on_line_added", "info")

    def on_command_done(self, terminal, pid, nid):
        if nid != "init":
            self.log.write("Command finished (pid=%s, nid=%s)" % (pid, nid),
                           "UM.on_command_done", "info")
            if nid == "uminstallum":
                # Reload UM
                self.log.write(
                    "Updating UM: kill process of updatemanagerpref.py",
                    "UM.on_command_done", "debug")
                self.umglobal.killScriptProcess("updatemanagerpref.py")
                # Reload tray as user
                self.log.write(
                    "Updating UM: kill process of updatemanagertray.py",
                    "UM.on_command_done", "debug")
                self.umglobal.killScriptProcess("updatemanagertray.py")
                cmd = "sudo -u {} updatemanager -t -r".format(self.user)
                os.system(cmd)
                self.log.write(
                    "UM updated: reload tray as user {}".format(self.user),
                    "UM.on_command_done", "debug")
                # Reload UM window
                cmd = join(self.umglobal.scriptDir, "updatemanager.py")
                if self.quickUpdate:
                    cmd = join(self.umglobal.scriptDir, "updatemanager.py -q")
                self.umglobal.reloadWindow(cmd, self.user)
                self.log.write(
                    "UM updated: reload {0} as user {1}".format(
                        cmd, self.user), "UM.on_command_done", "debug")
            elif nid == "umrefresh":
                # Build installed packages info list
                self.apt.createPackagesInfoList()
                # Run post update when needed
                self.postUpdate()
            elif nid == "ummaintenance":
                self.enableMaintenance(True)
                self.fillTreeViewMaintenance()
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.showMaintenance()
            elif nid == 'umupd':
                # Save update version in hist file
                self.umglobal.saveHistVersion("upd",
                                              self.umglobal.serverUpdVersion)
                self.log.write(
                    "Save history upd=%s" % self.umglobal.serverUpdVersion,
                    "UM.on_command_done", "debug")
                self.deleteScripts()

            # Refresh data after install or update
            self.umglobal.collectData()
            self.apt.createPackageLists()
            self.fillTreeView()

            # Enable the buttons and load the info page
            self.log.write(
                "Re-initiate window after terminal command: %s" %
                str(not self.quickUpdate), "UM.on_command_done", "debug")
            if not self.quickUpdate:
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.btnInfo.set_sensitive(True)
                self.loadInfo()

                if self.umglobal.newUpd:
                    self.showInfo()
                else:
                    # This throws a lock file error in Plasma5
                    #aptHasErrors = self.apt.aptHasErrors()
                    #if aptHasErrors is not None:
                    #MessageDialog(self.aptErrorText, aptHasErrors)
                    #el
                    if self.upgradables:
                        self.showPackages()
                    else:
                        self.showInfo()
                        MessageDialog(self.btnInfo.get_label(),
                                      self.uptodateText)
                self.log.write("Re-initiate window complete",
                               "UM.on_command_done", "debug")

            # Cleanup name file(s)
            for fle in glob(join(self.umglobal.filesDir, '.um*')):
                remove(fle)

    def createLogString(self, packagesList):
        lst = []
        for data in packagesList:
            lst.append(data[0])
        return ' '.join(lst)

    def refresh(self):
        # Refresh server info
        print((self.umglobal.hasInternet))
        self.umglobal.getServerInfo()
        print((self.umglobal.hasInternet))

        # Check of programs locking apt
        prog = self.apt.getAptCacheLockedProgram(
            self.umglobal.settings["apt-packages"])
        if prog is not None:
            msg = _("Another program is locking the apt cache\n\n"
                    "Please, close the program before refreshing:\n"
                    "* %s" % prog)
            MessageDialog(self.btnRefresh.get_label(), msg)
            self.log.write("%s is locking the apt cache" % prog, "UM.refresh",
                           "warning")
        elif self.umglobal.hasInternet:
            if not self.quickUpdate:
                # Update the apt cache
                self.btnPreferences.set_sensitive(True)
                self.btnOutput.set_sensitive(True)
                self.btnRefresh.set_sensitive(False)
                self.btnInstall.set_sensitive(False)
                self.btnMaintenance.set_sensitive(False)
                self.btnPackages.set_sensitive(True)
                self.showOutput()
            cmd = "dpkg --configure -a; %s -f install; apt-get update" % self.umglobal.settings[
                'apt-get-string']
            nid = 'umrefresh'
            self.prepForCommand(nid)
            if self.quickUpdate:
                self.ec.run(cmd)
            else:
                self.terminal.executeCommand(cmd, nid)
            self.apt.initAptShowVersions()
            self.log.write("Execute command: %s (%s)" % (cmd, nid),
                           "UM.refresh", "debug")
        else:
            if not self.quickUpdate:
                # No internet connection
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.btnInfo.set_sensitive(True)
                self.btnOutput.set_sensitive(True)
                self.btnPreferences.set_sensitive(True)
                self.loadInfo()
                self.showInfo()

    def postUpdate(self):
        # Check for changed version information
        if self.umglobal.newUpd and self.umglobal.serverUpdVersion is not None:
            self.getScripts([
                self.umglobal.settings['pre-upd'].replace(
                    "[VERSION]", self.umglobal.serverUpdVersion),
                self.umglobal.settings['post-upd'].replace(
                    "[VERSION]", self.umglobal.serverUpdVersion)
            ])

    def fillTreeView(self):
        # First check if this application is upgradable
        self.upgradableUM = self.getUpgradablePackages(
            packageNames=["updatemanager"])
        if self.upgradableUM:
            self.upgradables = self.upgradableUM
        else:
            # Get a list of packages that can be upgraded
            self.upgradableUM = []
            self.upgradables = self.getUpgradablePackages()
            #if not self.upgradables:
            ## Check for black listed packages
            #cmd = "dpkg --get-selections | grep hold$ | awk '{print $1}'"
            #lst = self.ec.run(cmd, False)
            #for pck in lst:
            #self.upgradables.append([pck.strip(), _("blacklisted"), ""])

        if not self.quickUpdate:
            contentList = [[
                _("Package"),
                _("Current version"),
                _("New version")
            ]] + self.upgradables
            self.tvHandler.fillTreeview(contentList=contentList,
                                        columnTypesList=['str', 'str', 'str'],
                                        firstItemIsColName=True)

    def getUpgradablePackages(self, packageNames=[]):
        upckList = []
        if packageNames:
            upckList = []
            for packageName in packageNames:
                for upck in self.apt.upgradablePackages:
                    if upck[0] == packageName:
                        upckList.append(upck)
                        break
        else:
            upckList = self.apt.upgradablePackages
        return upckList

    def getDistUpgradeInfo(self, upgradablesOnly=False):
        info = ""
        if upgradablesOnly:
            if self.apt.upgradablePackages:
                info = "<strong>%s</strong><br>" % self.apt.upgradablePackagesText
                for pck in self.apt.upgradablePackages:
                    info += "%s " % pck[0]
        else:
            if self.apt.removedPackages:
                info = "<strong>%s</strong><br>" % self.removedPackagesText
                for pck in self.apt.removedPackages:
                    info += "%s " % pck[0]
            if self.apt.newPackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.newPackagesText
                for pck in self.apt.newPackages:
                    info += "%s " % pck[0]
            if self.apt.heldbackPackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.heldbackPackagesText
                for pck in self.apt.heldbackPackages:
                    info += "%s " % pck[0]
            if self.apt.downgradablePackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.downgradePackagesText
                for pck in self.apt.downgradablePackages:
                    info += "%s " % pck[0]
        return info

    def showPackages(self):
        self.nbMain.set_current_page(0)

    def showOutput(self):
        self.nbMain.set_current_page(1)
        self.terminal.grab_focus()

    def showInfo(self):
        self.nbMain.set_current_page(2)

    def showMaintenance(self):
        self.nbMain.set_current_page(3)

    def showConfirmationDlg(self, title, message):
        head = "<html><head><style>body { font-family: Arial, Helvetica, Verdana, Sans-serif; font-size: 12px; color: #555555; background: #ffffff; }</style></head><body>"
        end = "</body></html>"
        html = "%s%s%s" % (head, message, end)
        sw = Gtk.ScrolledWindow()
        sw.add(SimpleBrowser(html))
        return CustomQuestionDialog(title, sw, 550, 300, self.window).show()

    # Get pre-install script and post-install script from the server
    def getScripts(self, files):
        for fle in files:
            flePath = join(self.umglobal.filesDir, fle)
            if not exists(flePath):
                # Get the new scripts if they exist
                url = "%s/%s/%s" % (self.umglobal.settings['solydxk'],
                                    self.umglobal.settings["umfilesdir"], fle)
                try:
                    txt = urlopen(url).read().decode('utf-8')
                    if txt != '':
                        # Save to a file and make executable
                        self.log.write("Save script = %s" % flePath,
                                       "UM.getScripts", "debug")
                        with open(flePath, 'w') as f:
                            f.write(txt)
                        chmod(flePath, 0o755)
                except:
                    pass

    def loadInfo(self):
        languageDir = self.get_language_dir()
        url = join("file://%s" % languageDir,
                   self.umglobal.settings['up-to-date'])
        self.btnInfo.set_icon_name("help-about")
        if self.upgradables:
            url = join("file://%s" % languageDir,
                       self.umglobal.settings['updates'])
            if self.umglobal.newUpd:
                url = "%s/%s/%s" % (self.umglobal.settings['solydxk'],
                                    self.umglobal.settings["umfilesdir"],
                                    self.umglobal.settings['upd-info'])
        elif self.umglobal.serverUpdVersion is None:
            url = join("file://%s" % languageDir,
                       self.umglobal.settings['not-found'])

        self.log.write("Load info url: %s" % url, "UM.loadInfo", "debug")

        children = self.swInfo.get_children()
        if children:
            children[0].openUrl(url)
        else:
            self.swInfo.add(SimpleBrowser(url))

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.umglobal.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.umglobal.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.umglobal.htmlDir,
                            "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.umglobal.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = os.environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang

    def pushMessage(self, message):
        if message is not None:
            context = self.statusbar.get_context_id('message')
            self.statusbar.push(context, message)

    def checkFilesDir(self):
        if not exists(self.umglobal.filesDir):
            makedirs(self.umglobal.filesDir)
        oldFiles = glob(join(self.umglobal.scriptDir, 'pre-*')) + \
                   glob(join(self.umglobal.scriptDir, 'post-*')) + \
                   [join(self.umglobal.scriptDir, 'updatemanager.hist')] + \
                   [join(self.umglobal.scriptDir, 'mirrors.list')]
        for fle in oldFiles:
            if exists(fle):
                fleName = basename(fle)
                if not exists(join(self.umglobal.filesDir, fleName)):
                    move(fle, self.umglobal.filesDir)
                else:
                    remove(fle)
        chmod(self.umglobal.filesDir, 0o777)

    def deleteScripts(self, UpdVersion=None):
        UpdVersion = "*"
        if UpdVersion is not None:
            UpdVersion = "*%s" % UpdVersion

        oldFiles = glob(join(
            self.umglobal.filesDir, "pre-%s" % UpdVersion)) + glob(
                join(self.umglobal.filesDir, "post-%s" % UpdVersion))
        for fle in oldFiles:
            remove(fle)
            self.log.write("Cleanup file: %s" % fle, "UM.deleteScripts",
                           "debug")

    # Close the gui
    def on_windowMain_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
class Constructor(object):

    def __init__(self):
        self.scriptDir = abspath(dirname(__file__))
        self.shareDir = join(self.scriptDir, '../../../share/solydxk/constructor')

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.shareDir, 'constructor.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go('constructorWindow')
        self.tvDistros = go('tvDistros')
        self.lblOutput = go('lblOutput')
        self.statusbar = go('statusbar')
        self.btnAdd = go('btnAdd')
        self.chkSelectAll = go('chkSelectAll')
        self.btnRemove = go('btnRemove')
        self.btnEdit = go('btnEdit')
        self.btnUpgrade = go('btnUpgrade')
        self.btnLocalize = go('btnLocalize')
        self.btnBuildIso = go('btnBuildIso')

        # Add iso window objects
        self.windowAddDistro = go('addDistroWindow')
        self.txtIso = go('txtIso')
        self.txtDir = go('txtDir')
        self.btnDir = go('btnDir')
        self.btnSave = go('btnSave')
        self.btnHelp = go('btnHelp')
        self.lblIso = go('lblIso')
        self.boxIso = go('boxIso')
        self.lblDir = go('lblDir')
        self.chkFromIso = go('chkFromIso')

        # Main window translations
        self.window.set_title(_("SolydXK Constructor"))
        self.chkSelectAll.set_label(_("Select all"))
        self.btnAdd.set_label("_{}".format(_("Add")))
        self.btnRemove.set_label("_{}".format(_("Remove")))
        self.btnEdit.set_label("_{}".format(_("Edit")))
        self.btnUpgrade.set_label("_{}".format(_("Upgrade")))
        self.btnLocalize.set_label("_{}".format(_("Localize")))
        self.btnBuildIso.set_label("_{}".format(_("Build")))
        self.btnHelp.set_label("_{}".format(_("Help")))

        # Add iso window translations
        self.lblIso.set_text(_("ISO"))
        go('btnCancel').set_label("_{}".format(_("Cancel")))

        # Init
        self.ec = ExecCmd()
        self.ec.run("modprobe loop", False)
        self.queue = Queue()
        self.mountDir = "/mnt/constructor"
        self.distroFile = join(self.scriptDir, "distros.list")
        self.distroAdded = False
        self.iso = None
        self.dir = None
        self.isoName = None
        self.doneWav = join(self.shareDir, 'done.wav')
        self.help = join(self.shareDir, 'help.html')
        self.chkFromIso.set_active(True)
        self.toggleGuiElements(False)
        self.hostEfiArchitecture = functions.getHostEfiArchitecture()

        # Treeviews
        self.tvHandlerDistros = TreeViewHandler(self.tvDistros)
        self.fillTreeViewDistros()

        # Version information
        ver = _("Version")
        self.version = "%s: %s" % (ver, functions.getPackageVersion('solydxk-constructor'))
        self.showOutput(self.version)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Main Window Functions
    # ===============================================

    def on_btnAdd_clicked(self, widget):
        self.windowAddDistro.show()

    def on_btnRemove_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \
                                                              "(This will not remove the directory and its data)"), self.window)
            answer = qd.show()
            if answer:
                self.saveDistroFile(distroPath=path, addDistro=False)
        self.fillTreeViewDistros()

    def on_btnEdit_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            de = EditDistro(path)
            services = []
            if exists(join(path, 'root/etc/apache2/apache2.conf')):
                services.append("apache2")
            if exists(join(path, 'root/etc/mysql/debian.cnf')):
                services.append("mysql")
            if services:
                msg = "If you need to update packages that depend on these services,\n" \
                      "you will need to manually start them:\n"
                for service in services:
                    msg += "\nservice %s start" % service
                msg += "\n\nWhen done:\n"
                for service in services:
                    msg += "\nservice %s stop" % service
                self.showInfo(_("Services detected"), msg, self.window)
                functions.repaintGui()
            de.openTerminal()

    def on_btnUpgrade_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        upgraded = False
        for path in selected:
            upgraded = True
            rootPath = "%s/root" % path
            de = EditDistro(path)
            de.openTerminal("apt-get update")
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 start")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql start")
            de.openTerminal("apt-get -y --force-yes -o Dpkg::Options::=\"--force-confnew\" dist-upgrade")
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 stop")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql stop")

            # Cleanup old kernel and headers
            script = "rmoldkernel.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x %s" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                remove(scriptTarget)

            # Build EFI files
            if self.hostEfiArchitecture != "":
                print(">> Start building EFI files")
                self.build_efi_files()

            # Download offline packages
            print(">> Start downloading offline packages")
            self.download_offline_packages()

        if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)

    def on_btnLocalize_clicked(self, widget):
        # Set locale
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            de = EditDistro(path)
            script = "setlocale.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x %s" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                remove(scriptTarget)

    def build_efi_files(self):

        # TODO - also 32-bit installs (haven't tested this)

        modules = "part_gpt part_msdos ntfs ntfscomp hfsplus fat ext2 normal chain boot configfile linux " \
                "multiboot iso9660 gfxmenu gfxterm loadenv efi_gop efi_uga loadbios fixvideo png " \
                "ext2 ntfscomp loopback search minicmd cat cpuid appleldr elf usb videotest " \
                "halt help ls reboot echo test normal sleep memdisk tar font video_fb video " \
                "gettext true  video_bochs video_cirrus multiboot2 acpi gfxterm_background gfxterm_menu"

        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            bootPath = "{}/boot".format(path)
            arch = functions.getGuestEfiArchitecture(rootPath)

            grubEfiName = "bootx64"
            efiName = "x64"
            if arch != "x86_64":
                arch = "i386"
                grubEfiName = "bootia32"
                efiName = "ia32"

            try:
                if not exists("{}/efi/boot".format(bootPath)):
                    makedirs("{}/efi/boot".format(bootPath))

                self.ec.run("efi-image {}/~tmp {}-efi {}".format(bootPath, arch, efiName))
                if exists("{}/~tmp/efi.img".format(bootPath) and
                   exists("{}/~tmp/boot/grub/{}-efi".format(bootPath, arch))):
                    self.ec.run("rm -r {}/boot/grub/{}-efi".format(bootPath, arch))
                    self.ec.run("mv -vf {}/~tmp/boot/grub/{}-efi {}/boot/grub/".format(bootPath, arch, bootPath))
                    self.ec.run("mv -vf {}/~tmp/efi.img {}/boot/grub/".format(bootPath, bootPath))
                    self.ec.run("rm -r {}/~tmp".format(bootPath))

                self.ec.run("grub-mkimage -O {}-efi -d /usr/lib/grub/{}-efi "
                            "-o {}/efi/boot/{}.efi "
                            "-p \"/boot/grub\" {}".format(arch, arch, bootPath, grubEfiName, modules))

                print((">> Finished building EFI files"))

            except Exception as detail:
                self.showError("Error: build EFI files", detail, self.window)

    def download_offline_packages(self):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            arch = functions.getGuestEfiArchitecture(rootPath)
            de = EditDistro(path)
            script = "offline.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            offlineSource = join(rootPath, "offline")
            offlineTarget = join(rootPath, "../boot/offline")
            if exists(scriptSource):
                try:
                    copy(scriptSource, scriptTarget)
                    self.ec.run("chmod a+x %s" % scriptTarget)
                    # Run the script
                    de.openTerminal("/bin/bash {} {}".format(script, arch))
                    # Remove script
                    remove(scriptTarget)
                    # Move offline directory to boot directory
                    if exists(offlineSource):
                        print(("%s exists" % offlineSource))
                        if exists(offlineTarget):
                            print((">> Remove %s" % offlineTarget))
                            rmtree(offlineTarget)
                        print((">> Move %s to %s" % (offlineSource, offlineTarget)))
                        move(offlineSource, offlineTarget)
                    else:
                        print((">> Cannot find: %s" % offlineSource))
                except Exception as detail:
                    self.showError("Error: getting offline packages", detail, self.window)
            else:
                print((">> Cannot find: %s" % scriptSource))


    def on_btnBuildIso_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        msg = ""
        for path in selected:
            self.toggleGuiElements(True)
            self.showOutput("Start building ISO in: %s" % path)
            functions.repaintGui()

            # Start building the ISO in a thread
            t = BuildIso(path, self.queue)
            t.start()
            self.queue.join()

            # Thread is done
            # Get the data from the queue
            ret = self.queue.get()
            self.queue.task_done()

            if ret is not None:
                self.showOutput(ret)
                if "error" in ret.lower():
                    self.showError("Error", ret, self.window)
                else:
                    msg += "%s\n" % ret

        if msg != "":
            if exists("/usr/bin/aplay") and exists(self.doneWav):
                self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
            self.showInfo("", msg, self.window)
        self.toggleGuiElements(False)

    def on_chkSelectAll_toggled(self, widget):
        self.tvHandlerDistros.treeviewToggleAll(toggleColNrList=[0], toggleValue=widget.get_active())

    def on_tvDistros_row_activated(self, widget, path, column):
        self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0])

    def on_btnHelp_clicked(self, widget):
        if functions.isPackageInstalled("firefox"):
            system("firefox file://%s &" % self.help)
        else:
            system("xdg-open file://%s &" % self.help)

    def on_btnOpenDir_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            system("xdg-open %s &" % path)

    def fillTreeViewDistros(self, selectDistros=[]):
        contentList = [[_("Select"), _("Distribution"), _("Working directory")]]
        distros = self.getDistros()
        for distro in distros:
            select = False
            for selectDistro in selectDistros:
                if distro[0] == selectDistro:
                    select = True
            contentList.append([select, distro[0], distro[1]])
        self.tvHandlerDistros.fillTreeview(contentList=contentList, columnTypesList=['bool', 'str', 'str'], firstItemIsColName=True)

    def getDistros(self):
        distros = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                lines = f.readlines()
            for line in lines:
                line = line.strip().rstrip('/')
                print(line)
                if exists(line):
                    dg = DistroGeneral(line)
                    isoName = dg.description
                    distros.append([isoName, line])
            # Sort on iso name
            if distros:
                distros = sorted(distros, key=operator.itemgetter(0))
        return distros

    # ===============================================
    # Add ISO Window Functions
    # ===============================================

    def on_btnIso_clicked(self, widget):
        fleFilter = Gtk.FileFilter()
        fleFilter.set_name("ISO")
        fleFilter.add_mime_type("application/x-cd-image")
        fleFilter.add_pattern("*.iso")

        startDir = None
        if exists(dirname(self.txtIso.get_text())):
            startDir = dirname(self.txtIso.get_text())

        filePath = SelectFileDialog(title=_('Select SolydXK ISO'), start_directory=startDir, parent=self.window, gtkFileFilter=fleFilter).show()
        if filePath is not None:
            self.txtIso.set_text(filePath)

    def on_btnDir_clicked(self, widget):
        startDir = None
        if exists(self.txtDir.get_text()):
            startDir = self.txtDir.get_text()
        dirText = SelectDirectoryDialog(title=_('Select directory'), start_directory=startDir, parent=self.window).show()
        if dirText is not None:
            self.txtDir.set_text(dirText)

    def on_btnSave_clicked(self, widget):
        self.iso = ""
        if self.chkFromIso.get_active():
            self.iso = self.txtIso.get_text()
        self.dir = self.txtDir.get_text()

        title = _("Save existing working directory")
        if self.iso != "":
            title = _("Unpack ISO and save")

        if not exists(self.dir):
            makedirs(self.dir)

        if not exists(self.dir):
            self.showError(title, _("Could not create directory %(dir)s: exiting" % {"dir": self.dir}), self.window)
        else:
            self.windowAddDistro.hide()
            if self.iso != "":
                if not exists(self.iso):
                    self.showInfo(self.btnSave.get_label(), _("The path to the ISO file does not exist:\n{}".format(self.iso)), self.window)
                    return
                if listdir(self.dir):
                    qd = QuestionDialog(self.btnSave.get_label(),
                        _("The destination directory is not empty.\n"
                        "Are you sure you want to overwrite all data in {}?".format(self.dir)),
                        self.window)
                    answer = qd.show()
                    if not answer:
                        return

                self.showOutput("Start unpacking the ISO...")
                self.toggleGuiElements(True)
                t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue)
                t.start()
                self.queue.join()
                GObject.timeout_add(1000, self.checkThread, True)
            else:
                self.saveDistroFile(self.dir, True)
                self.fillTreeViewDistros()
                self.showOutput(_("Existing working directory added"))
                #self.toggleGuiElements(False)

    def on_btnCancel_clicked(self, widget):
        self.windowAddDistro.hide()

    def on_addDistroWindow_delete_event(self, widget, data=None):
        self.windowAddDistro.hide()
        return True

    def on_txtIso_changed(self, widget):
        path = self.txtIso.get_text()
        if exists(path):
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            if exists(self.txtDir.get_text()):
                self.btnSave.set_sensitive(True)
        else:
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)

    def on_txtDir_changed(self, widget):
        blnFromIso = self.chkFromIso.get_active()
        isoPath = self.txtIso.get_text()
        dirText = self.txtDir.get_text()
        self.btnSave.set_sensitive(False)
        if exists(dirText):
            if blnFromIso:
                if exists(isoPath):
                    self.btnSave.set_sensitive(True)
            else:
                self.btnSave.set_sensitive(True)

    def on_chkFromIso_toggled(self, widget):
        if widget.get_active():
            self.lblIso.set_visible(True)
            self.boxIso.set_visible(True)
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)
            self.lblDir.set_text(_("Unpack ISO to directory"))
            self.btnSave.set_label(_("Unpack & Save"))
        else:
            self.txtIso.set_text("")
            self.lblIso.set_visible(False)
            self.boxIso.set_visible(False)
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            self.btnSave.set_sensitive(True)
            self.lblDir.set_text(_("Work directory"))
            self.btnSave.set_label(_("Save"))

    # ===============================================
    # General functions
    # ===============================================

    def showInfo(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show()

    def showError(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show()

    def showOutput(self, message):
        print(message)
        functions.pushMessage(self.statusbar, message)

    def checkThread(self, addDistro=None):
        #print 'Thread count = ' + str(threading.active_count())
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            return True

        # Thread is done
        # Get the data from the queuez
        ret = self.queue.get()
        self.queue.task_done()

        # Thread is done
        if addDistro is not None:
            self.saveDistroFile(self.dir, addDistro)
            self.fillTreeViewDistros(self.isoName)
        self.toggleGuiElements(False)
        if exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
        if ret is not None:
            self.showOutput(ret)
            if "error" in ret.lower():
                self.showError("Error", ret, self.window)
            else:
                self.showInfo("", ret, self.window)
        return False

    def toggleGuiElements(self, startThread):
        if startThread:
            self.chkSelectAll.set_sensitive(False)
            self.tvDistros.set_sensitive(False)
            self.btnAdd.set_sensitive(False)
            self.btnBuildIso.set_sensitive(False)
            self.btnEdit.set_sensitive(False)
            self.btnRemove.set_sensitive(False)
            self.btnUpgrade.set_sensitive(False)
        else:
            self.chkSelectAll.set_sensitive(True)
            self.tvDistros.set_sensitive(True)
            self.btnAdd.set_sensitive(True)
            self.btnBuildIso.set_sensitive(True)
            self.btnEdit.set_sensitive(True)
            self.btnRemove.set_sensitive(True)
            self.btnUpgrade.set_sensitive(True)

    def saveDistroFile(self, distroPath, addDistro=True):
        newCont = []
        dg = DistroGeneral(distroPath)
        self.isoName = dg.description

        cfg = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                cfg = f.readlines()
            for line in cfg:
                line = line.strip()
                if distroPath not in line and exists(line):
                    newCont.append(line)

        if addDistro:
            newCont.append(distroPath)

        with open(self.distroFile, 'w') as f:
            f.write('\n'.join(newCont))

        self.iso = ""
        self.dir = ""

    # Close the gui
    def on_constructorWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
Exemple #7
0
class Constructor(object):
    def __init__(self):
        self.scriptDir = abspath(dirname(__file__))
        self.shareDir = join(self.scriptDir,
                             '../../../share/trail/constructor')
        self.userAppDir = join(get_user_home_dir(), ".constructor")
        self.distroFile = join(self.userAppDir, "distros.list")

        # Create the user's application directory if it doesn't exist
        if not isdir(self.userAppDir):
            user_name = getUserLoginName()
            makedirs(self.userAppDir)
            old_distro_file = join(self.scriptDir, "distros.list")
            if exists(old_distro_file):
                move(old_distro_file, self.distroFile)
            system("chown -R %s:%s %s" %
                   (user_name, user_name, self.userAppDir))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.shareDir, 'constructor.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go('constructorWindow')
        self.tvDistros = go('tvDistros')
        self.lblOutput = go('lblOutput')
        self.statusbar = go('statusbar')
        self.btnAdd = go('btnAdd')
        self.chkSelectAll = go('chkSelectAll')
        self.btnRemove = go('btnRemove')
        self.btnEdit = go('btnEdit')
        self.btnUpgrade = go('btnUpgrade')
        self.btnLocalize = go('btnLocalize')
        self.btnBuildIso = go('btnBuildIso')

        # Add iso window objects
        self.windowAddDistro = go('addDistroWindow')
        self.txtIso = go('txtIso')
        self.txtDir = go('txtDir')
        self.btnDir = go('btnDir')
        self.btnSave = go('btnSave')
        self.lblIso = go('lblIso')
        self.boxIso = go('boxIso')
        self.lblDir = go('lblDir')
        self.chkFromIso = go('chkFromIso')

        # Main window translations
        self.window.set_title(_("Constructor"))
        self.chkSelectAll.set_label(_("Select all"))
        self.btnAdd.set_label("_{}".format(_("Add")))
        self.btnRemove.set_label("_{}".format(_("Remove")))
        self.btnEdit.set_label("_{}".format(_("Edit")))
        self.btnUpgrade.set_label("_{}".format(_("Upgrade")))
        self.btnLocalize.set_label("_{}".format(_("Localize")))
        self.btnBuildIso.set_label("_{}".format(_("Build")))

        # Add iso window translations
        self.lblIso.set_text(_("ISO"))
        go('btnCancel').set_label("_{}".format(_("Cancel")))

        # Init
        self.ec = ExecCmd()
        self.ec.run("modprobe loop", False)
        self.queue = Queue()
        self.mountDir = "/mnt/constructor"
        self.distroAdded = False
        self.iso = None
        self.dir = None
        self.isoName = None
        self.chkFromIso.set_active(True)
        self.toggleGuiElements(False)

        # Treeviews
        self.tvHandlerDistros = TreeViewHandler(self.tvDistros)
        self.fillTreeViewDistros()

        # Version information
        ver = _("Version")
        self.version = "%s: %s" % (ver, getPackageVersion('constructor'))
        self.showOutput(self.version)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Main Window Functions
    # ===============================================

    def on_btnAdd_clicked(self, widget):
        self.windowAddDistro.show()

    def on_btnRemove_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \
                                                              "(This will not remove the directory and its data)"), self.window)
            answer = qd.show()
            if answer:
                self.saveDistroFile(distroPath=path, addDistro=False)
        self.fillTreeViewDistros()

    def on_btnEdit_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            de = EditDistro(path)
            services = []
            if exists(join(path, 'root/etc/apache2/apache2.conf')):
                services.append("apache2")
            if exists(join(path, 'root/etc/mysql/debian.cnf')):
                services.append("mysql")
            if services:
                msg = "If you need to update packages that depend on these services,\n" \
                      "you will need to manually start them:\n"
                for service in services:
                    msg += "\nservice %s start" % service
                msg += "\n\nWhen done:\n"
                for service in services:
                    msg += "\nservice %s stop" % service
                self.showInfo(_("Services detected"), msg, self.window)
                repaintGui()
            de.openTerminal()

    def on_btnUpgrade_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        upgraded = False
        for path in selected:
            upgraded = True
            rootPath = "%s/root" % path
            force = get_apt_force(rootPath)
            de = EditDistro(path)
            de.openTerminal("apt-get update")
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 start")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql start")
            de.openTerminal(
                "apt-get -y %s -o Dpkg::Options::=\"--force-confnew\" dist-upgrade"
                % force)
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 stop")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql stop")

            # Cleanup old kernel and headers
            script = "rmoldkernel.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                silent_remove(scriptTarget)

            # Download offline packages
            print(">> Start downloading offline packages")
            self.download_offline_packages()

        if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)

    def on_btnLocalize_clicked(self, widget):
        # Set locale
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            de = EditDistro(path)
            script = "setlocale.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                silent_remove(scriptTarget)

    def download_offline_packages(self):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            arch = getGuestEfiArchitecture(rootPath)
            de = EditDistro(path)
            script = "offline.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            offlineSource = join(rootPath, "offline")
            offlineTarget = join(rootPath, "../boot/offline")
            if exists(scriptSource):
                try:
                    copy(scriptSource, scriptTarget)
                    self.ec.run("chmod a+x '%s'" % scriptTarget)
                    # Run the script
                    de.openTerminal("/bin/bash {} {}".format(script, arch))
                    # Remove script
                    silent_remove(scriptTarget)
                    # Move offline directory to boot directory
                    if exists(offlineSource):
                        print(("%s exists" % offlineSource))
                        if exists(offlineTarget):
                            print((">> Remove %s" % offlineTarget))
                            silent_remove(offlineTarget)
                        print((">> Move %s to %s" %
                               (offlineSource, offlineTarget)))
                        move(offlineSource, offlineTarget)
                    else:
                        print((">> Cannot find: %s" % offlineSource))
                except Exception as detail:
                    self.showError("Error: getting offline packages", detail,
                                   self.window)
            else:
                print((">> Cannot find: %s" % scriptSource))

    def on_btnBuildIso_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        msg = ""
        for path in selected:
            self.toggleGuiElements(True)
            self.showOutput("Start building ISO in: %s" % path)
            repaintGui()

            # Start building the ISO in a thread
            t = BuildIso(path, self.queue)
            t.start()
            self.queue.join()

            # Thread is done
            # Get the data from the queue
            ret = self.queue.get()
            self.queue.task_done()

            if ret is not None:
                self.showOutput(ret)
                if "error" in ret.lower():
                    self.showError("Error", ret, self.window)
                else:
                    msg += "%s\n" % ret

        if msg != "":
            if exists("/usr/bin/aplay") and exists(self.doneWav):
                self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
            self.showInfo("", msg, self.window)
        self.toggleGuiElements(False)

    def on_chkSelectAll_toggled(self, widget):
        self.tvHandlerDistros.treeviewToggleAll(
            toggleColNrList=[0], toggleValue=widget.get_active())

    def on_tvDistros_row_activated(self, widget, path, column):
        self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0])

    def on_btnOpenDir_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            system("open-as-user %s root" % path)

    def fillTreeViewDistros(self, selectDistros=[]):
        contentList = [[
            _("Select"),
            _("Distribution"),
            _("Working directory")
        ]]
        distros = self.getDistros()
        for distro in distros:
            select = False
            for selectDistro in selectDistros:
                if distro[0] == selectDistro:
                    select = True
            contentList.append([select, distro[0], distro[1]])
        self.tvHandlerDistros.fillTreeview(
            contentList=contentList,
            columnTypesList=['bool', 'str', 'str'],
            firstItemIsColName=True)

    def getDistros(self):
        distros = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                lines = f.readlines()
            for line in lines:
                line = line.strip().rstrip('/')
                print(line)
                if exists(line):
                    dg = DistroGeneral(line)
                    isoName = dg.description
                    distros.append([isoName, line])
            # Sort on iso name
            if distros:
                distros = sorted(distros, key=operator.itemgetter(0))
        return distros

    # ===============================================
    # Add ISO Window Functions
    # ===============================================

    def on_btnIso_clicked(self, widget):
        fleFilter = Gtk.FileFilter()
        fleFilter.set_name("ISO")
        fleFilter.add_mime_type("application/x-cd-image")
        fleFilter.add_pattern("*.iso")

        startDir = None
        if exists(dirname(self.txtIso.get_text())):
            startDir = dirname(self.txtIso.get_text())

        filePath = SelectFileDialog(title=_('Select ISO'),
                                    start_directory=startDir,
                                    parent=self.window,
                                    gtkFileFilter=fleFilter).show()
        if filePath is not None:
            self.txtIso.set_text(filePath)

    def on_btnDir_clicked(self, widget):
        startDir = None
        if exists(self.txtDir.get_text()):
            startDir = self.txtDir.get_text()
        dirText = SelectDirectoryDialog(title=_('Select directory'),
                                        start_directory=startDir,
                                        parent=self.window).show()
        if dirText is not None:
            self.txtDir.set_text(dirText)

    def on_btnSave_clicked(self, widget):
        self.iso = ""
        if self.chkFromIso.get_active():
            self.iso = self.txtIso.get_text()
        self.dir = self.txtDir.get_text()

        title = _("Save existing working directory")
        if self.iso != "":
            title = _("Unpack ISO and save")

        if not exists(self.dir):
            makedirs(self.dir)

        if not exists(self.dir):
            self.showError(
                title,
                _("Could not create directory %(dir)s: exiting" %
                  {"dir": self.dir}), self.window)
        else:
            self.windowAddDistro.hide()
            if self.iso != "":
                if not exists(self.iso):
                    self.showInfo(
                        self.btnSave.get_label(),
                        _("The path to the ISO file does not exist:\n{}".
                          format(self.iso)), self.window)
                    return
                if listdir(self.dir):
                    qd = QuestionDialog(
                        self.btnSave.get_label(),
                        _("The destination directory is not empty.\n"
                          "Are you sure you want to overwrite all data in {}?".
                          format(self.dir)), self.window)
                    answer = qd.show()
                    if not answer:
                        return

                self.showOutput("Start unpacking the ISO...")
                self.toggleGuiElements(True)
                t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue)
                t.start()
                self.queue.join()
                GObject.timeout_add(1000, self.checkThread, True)
            else:
                self.saveDistroFile(self.dir, True)
                self.fillTreeViewDistros()
                self.showOutput(_("Existing working directory added"))
                #self.toggleGuiElements(False)

    def on_btnCancel_clicked(self, widget):
        self.windowAddDistro.hide()

    def on_addDistroWindow_delete_event(self, widget, data=None):
        self.windowAddDistro.hide()
        return True

    def on_txtIso_changed(self, widget):
        path = self.txtIso.get_text()
        if exists(path):
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            if exists(self.txtDir.get_text()):
                self.btnSave.set_sensitive(True)
        else:
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)

    def on_txtDir_changed(self, widget):
        blnFromIso = self.chkFromIso.get_active()
        isoPath = self.txtIso.get_text()
        dirText = self.txtDir.get_text()
        self.btnSave.set_sensitive(False)
        if exists(dirText):
            if blnFromIso:
                if exists(isoPath):
                    self.btnSave.set_sensitive(True)
            else:
                self.btnSave.set_sensitive(True)

    def on_chkFromIso_toggled(self, widget):
        if widget.get_active():
            self.lblIso.set_visible(True)
            self.boxIso.set_visible(True)
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)
            self.lblDir.set_text(_("Unpack ISO to directory"))
            self.btnSave.set_label(_("Unpack & Save"))
        else:
            self.txtIso.set_text("")
            self.lblIso.set_visible(False)
            self.boxIso.set_visible(False)
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            self.btnSave.set_sensitive(True)
            self.lblDir.set_text(_("Work directory"))
            self.btnSave.set_label(_("Save"))

    # ===============================================
    # General functions
    # ===============================================

    def showInfo(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show()

    def showError(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show()

    def showOutput(self, message):
        print(message)
        pushMessage(self.statusbar, message)

    def checkThread(self, addDistro=None):
        #print 'Thread count = ' + str(threading.active_count())
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            return True

        # Thread is done
        # Get the data from the queuez
        ret = self.queue.get()
        self.queue.task_done()

        # Thread is done
        if addDistro is not None:
            self.saveDistroFile(self.dir, addDistro)
            self.fillTreeViewDistros(self.isoName)
        self.toggleGuiElements(False)
        if exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
        if ret is not None:
            self.showOutput(ret)
            if "error" in ret.lower():
                self.showError("Error", ret, self.window)
            else:
                self.showInfo("", ret, self.window)
        return False

    def toggleGuiElements(self, startThread):
        if startThread:
            self.chkSelectAll.set_sensitive(False)
            self.tvDistros.set_sensitive(False)
            self.btnAdd.set_sensitive(False)
            self.btnBuildIso.set_sensitive(False)
            self.btnEdit.set_sensitive(False)
            self.btnRemove.set_sensitive(False)
            self.btnUpgrade.set_sensitive(False)
            self.btnLocalize.set_sensitive(False)
            self.btnDir.set_sensitive(False)
        else:
            self.chkSelectAll.set_sensitive(True)
            self.tvDistros.set_sensitive(True)
            self.btnAdd.set_sensitive(True)
            self.btnBuildIso.set_sensitive(True)
            self.btnEdit.set_sensitive(True)
            self.btnRemove.set_sensitive(True)
            self.btnUpgrade.set_sensitive(True)
            self.btnLocalize.set_sensitive(True)
            self.btnDir.set_sensitive(True)

    def saveDistroFile(self, distroPath, addDistro=True):
        newCont = []
        dg = DistroGeneral(distroPath)
        self.isoName = dg.description

        cfg = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                cfg = f.readlines()
            for line in cfg:
                line = line.strip()
                if distroPath not in line and exists(line):
                    newCont.append(line)

        if addDistro:
            newCont.append(distroPath)

        with open(self.distroFile, 'w') as f:
            f.write('\n'.join(newCont))

        self.iso = ""
        self.dir = ""

    # Close the gui
    def on_constructorWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()