diff options
Diffstat (limited to 'build_tools/larch8/larch0/gui/front')
| -rw-r--r-- | build_tools/larch8/larch0/gui/front/docviewer.py | 68 | ||||
| -rw-r--r-- | build_tools/larch8/larch0/gui/front/editor.py | 115 | ||||
| -rw-r--r-- | build_tools/larch8/larch0/gui/front/logview.py | 94 | ||||
| -rw-r--r-- | build_tools/larch8/larch0/gui/front/mainwindow.py | 205 | ||||
| -rw-r--r-- | build_tools/larch8/larch0/gui/front/page_installation.py | 193 | ||||
| -rw-r--r-- | build_tools/larch8/larch0/gui/front/page_larchify.py | 296 | ||||
| -rw-r--r-- | build_tools/larch8/larch0/gui/front/page_medium.py | 441 | ||||
| -rw-r--r-- | build_tools/larch8/larch0/gui/front/page_project.py | 241 | 
8 files changed, 1653 insertions, 0 deletions
diff --git a/build_tools/larch8/larch0/gui/front/docviewer.py b/build_tools/larch8/larch0/gui/front/docviewer.py new file mode 100644 index 0000000..de51605 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/docviewer.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python2 +# +# docviewer.py +# +# (c) Copyright 2009-2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +#    larch is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    larch is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with larch; if not, write to the Free Software Foundation, Inc., +#    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +# +#---------------------------------------------------------------------------- +# 2010.06.24 + + +import os + + +class DocViewer: +    def __init__(self): +        self.index = self._getPage('index.html') +        self.homepath = None +        ui.widgetlist(fss('fetch_layout', 'docviewer.uim')) + +        ui.connectlist( +                ('doc:hide*clicked', self._hide), +                ('doc:back*clicked', self._back), +                ('doc:forward*clicked', self._forward), +                ('doc:home*clicked', self.gohome), +                ('doc:parent*clicked', self.goto), +                (':docs*clicked', self._show), +            ) + +    def _show(self): +        ui.runningtab(3) + +    def _hide(self): +        ui.runningtab() + +    def _back(self): +        ui.command('doc:content.prev') + +    def _forward(self): +        ui.command('doc:content.next') + +    def _getPage(self, page): +        return fss('get_docs_url', page) + +    def gohome(self, home=None): +        if home: +            self.homepath = self._getPage(home) +        self.goto(self.homepath) + +    def goto(self, path=None): +        if not path: +            path = self.index +        ui.command('doc:content.setUrl', path) diff --git a/build_tools/larch8/larch0/gui/front/editor.py b/build_tools/larch8/larch0/gui/front/editor.py new file mode 100644 index 0000000..202f236 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/editor.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python2 +# +# editor.py +# +# (c) Copyright 2009-2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +#    larch is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    larch is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with larch; if not, write to the Free Software Foundation, Inc., +#    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +# +#---------------------------------------------------------------------------- +# 2010.06.24 + +class Editor: +    def __init__(self): +        ui.widgetlist(fss('fetch_layout', 'editor.uim')) +        ui.connectlist( +                ('edit:ok*clicked', self.ok), +                ('edit:cancel*clicked', self.cancel), +                ('edit:revert*clicked', self.dorevert), +                ('edit:copy*clicked', self.copy), +                ('edit:cut*clicked', self.cut), +                ('edit:paste*clicked', self.paste), +                ('edit:undo*clicked', self.undo), +                ('edit:redo*clicked', self.redo), +            ) + +    def start(self, title, endcall, text='', revert=None): +        ui.command('edit:title.markup', ['h3', title]) +        self.endcall = endcall +        self.revert = revert +        try: +            self.text0 = revert() if text == None else text +        except: +            run_error("BUG: Editor - no revert function?") +        ui.command('edit:content.text', self.text0) +        ui.runningtab(4) + +    def ok(self): +        self.endcall(ui.command('edit:content.get')) +        ui.runningtab() + +    def cancel(self): +        ui.runningtab() + +    def dorevert(self): +        if self.revert: +            self.text0 = self.revert() +        ui.command('edit:content.text', self.text0) + +    def copy(self): +        ui.command('edit:content.copy') + +    def cut(self): +        ui.command('edit:content.cut') + +    def paste(self): +        ui.command('edit:content.paste') + +    def undo(self): +        ui.command('edit:content.undo') + +    def redo(self): +        ui.command('edit:content.redo') + +    def edit(self, fname, source=None, label=None, filter=None): +        """Files (<fname> and <source>) can be either an absolute path or else +        relative to the profile directory, the application base directory +        or the working directory. Relative paths are determined by the +        prefixes 'profile:', 'base:' or 'working:'. +        If the file <fname> already exists its contents will be taken as the +        starting point, otherwise the file <source>, which may also be an +        empty string, will be read in. +        Whichever file is available its contents can be filtered by an +        optional 'filter' function, which takes the file contents as a +        string as argument and returns the transformed contents as another +        string. +        """ +        def revert(): +            """If a file is addressed by 'source' revert to its contents, +            if source is "", clear the contents, otherwise revert to the +            contents as they were before entering the editor. +            """ +            return textsrc if source != None else text0 + +        def endfile(text): +            t = text.encode("utf8") +            if t and (t[-1] != "\n"): +                t += "\n" +            fss('savefile', fname, text) + +        if source != None: +            textsrc = "" if source == "" else fss('readfile', source, +                    filter=filter) +        # Read the file, if it exists, else return None +        text0 = fss('readfile', fname, filter=filter, trap=False) +        if text0 == None: +            assert source != None   # The file must be present +            text0 = textsrc +        if not label: +            label = ui.command('editor_data.get', 'msg_dflt') % fname +        self.start(label, endfile, text0, revert) + diff --git a/build_tools/larch8/larch0/gui/front/logview.py b/build_tools/larch8/larch0/gui/front/logview.py new file mode 100644 index 0000000..7e506a5 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/logview.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python2 +# +# logview.py +# +# (c) Copyright 2009-2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +#    larch is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    larch is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with larch; if not, write to the Free Software Foundation, Inc., +#    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +# +#---------------------------------------------------------------------------- +# 2010.08.10 + +import locale +# Try to work around problems when the system encoding is not utf8 +encoding = locale.getdefaultlocale()[1] + +#TODO: progress bar? +class Progress: +    def __init__(self): +        self.active = False +        ui.widgetlist(fss('fetch_layout', 'progress.uim')) +        ui.connect('progress:done*clicked', self._done) + +    def _done(self): +        self.active = False +        ui.runningtab(0) + +    def start(self): +        # Set busy cursor on the progress area +        ui.command('progress:page.busycursor', True) +        # Initialize widgets +        ui.command("progress:text.text") +        ui.command("progress:progress.text") +        ui.command("progress:done.enable", False) +        ui.command("progress:cancel.enable", True) +        self.active = True +        ui.runningtab(1) + +    def end(self, auto=False): +        ui.command("progress:cancel.enable", False) +        ui.command("progress:done.enable", True) +        # Clear busy cursor on the progress area +        ui.command('progress:page.busycursor', True) +        ui.command('progress:page.busycursor', False) +        if auto: +            self._done() + +    def addLine(self, line): +        # Try to work around problems when the system encoding is not utf8 +        if isinstance(line, str): +            line = line.decode(encoding, 'replace') +        ui.command("progress:text.append_and_scroll", line) +        logger.addLine(line) + +    def set(self, text=""): +        ui.command("progress:progress.text", text) + + +class Logger: +    def __init__(self): +        ui.widgetlist(fss('fetch_layout', 'logger.uim')) +        ui.connectlist( +                ('log:clear*clicked', self.clear), +                ('log:hide*clicked', self._hide), +                (':showlog*clicked', self._show), +            ) + +    def clear(self): +        ui.command('log:text.text') + +    def addLine(self, line): +        # Try to work around problems when the system encoding is not utf8 +        if isinstance(line, str): +            line = line.decode(encoding, 'replace') +        ui.command('log:text.append_and_scroll', line) + +    def _show(self): +        ui.runningtab(2) + +    def _hide(self): +        ui.runningtab() diff --git a/build_tools/larch8/larch0/gui/front/mainwindow.py b/build_tools/larch8/larch0/gui/front/mainwindow.py new file mode 100644 index 0000000..8bf2a57 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/mainwindow.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python2 +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +#    larch is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    larch is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with larch; if not, write to the Free Software Foundation, Inc., +#    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +# +#---------------------------------------------------------------------------- +#2010.10.12 + +import __builtin__ +from liblarch.suim import Suim, debug +__builtin__.debug = debug + + + +def larchcall(script, *args): +    if script[0] != '*': +        progress.start()            # initialize progress widget +        ui.setcb(None) +    else: +        # This script won't switch to the progress widget, but accepts +        # a callback to handle the resulting output +        script = script[1:] +        ui.setcb(args[0]) +        args = args[1:] +    fss('larchscript', script, *args) + +__builtin__.larchcall = larchcall + + + +class Ui(Suim): +    def __init__(self): +        self.setcb(None) +        Suim.__init__(self, 'larch', busywidgets=[':larch']) + +        self.connect('$$$uiclose$$$', self.quit) +        self.connect('$$$uiquit$$$', self.quit) +        self.connect('$$$cancel$$$', self.interrupt) +        self.progressing = False    # used for managing progress display + + +    def sigin(self, signal, *args): +        self.idle_add(getattr(self, 'sig_' + signal), *args) + + +    def setcb(self, cb): +        """Set a callback to run when a 'larchscript' sends an output line, +        or completes. +        """ +        self._read_cb = cb + + +    def isbusy(self): +        return self._read_cb != None + + +    def runningtab(self, i=None): +        if (i == None): +            i = 1 if progress.active else 0 +        if (i == 0) and hasattr(stage, 'reenter'): +            stage.reenter() +        self.command(':tabs.set', i) + +    def fileDialog(self, message, startdir=None, create=False, +            dirsonly=False, file=None, filter=None): +# Actually this should access the file-system via 'fss' ... +# (that would require a new, custom widget) +        if dirsonly: +            return self.command('fileDialog_getdir', message, not create, startdir) +        if file and startdir: +            startdir = startdir + '/' + file +        if create: +            return self.command('fileDialog_save', message, startdir, filter) +        return self.command('fileDialog_open', message, startdir, filter) + +    def enable_installation_page(self, on): +        self.command(':notebook.enableTab', 1, on) + +    def interrupt(self): +        fss('larchscript', 'interrupt') + +    def quit(self): +        """Do any tidying up which may be necessary. +        """ +        fss('larchscript', 'close') +        Suim.quit(self) + +    def data(self, key): +        return self.command('main_page_data.get', key) + + +    def sig_get_password(self, message): +        """This is a callback, triggered by signal 'get_password' +        to ask the user to input the password. +        """ +        fss('larchscript', 'sendpassword', *ui.command('textLineDialog', +                message, "larch: pw", "", True)) + + +    def sig_line_cb(self, message): +        """A line has been received from the 'larchscript' (this is a callback, +        in the main thread). +        The input lines are filtered for pacman, mksquashfs and mkisofs +        progress output so that appropriate progress reports can be given. +        """ +        if message.startswith('>-'): +            if message.startswith('>-_$$_'): +                # Informing us of the pid +                fss('larchscript', 'pid', int(message.rsplit('_', 1)[1])) +            else: +                # It is a progress report +                progress.set(message[2:]) +                self.progressing = True +                return + +        else: +            if self.progressing: +                progress.set() +                self.progressing = False + +            progress.addLine(message) +            if message.startswith('?>'): +                # a query (yes/no): pop up the query +                fss('larchscript', 'reply', '??YES' if ui.command( +                        'confirmDialog', message[2:]) else '??NO') +            elif self._read_cb: +                self._read_cb(message) + + +    def sig_end_cb(self, ok): +        """A callback for the end of a 'larchscript'. +        The completion code, ok, is ignored. +        """ +        if self._read_cb: +            self._read_cb(None) +            self._read_cb = None +            self.busy(False) +        else: +            progress.end() + + +    def sig_log(self, line): +        logger.addLine(line) + + + + +def tab_changed(index): +    __builtin__.stage = pages[index] +    stage.enter() + +from page_project import ProjectSettings +from page_installation import Installation +from page_larchify import Larchify +from page_medium import Medium + +from docviewer import DocViewer +from editor import Editor +from logview import Logger, Progress + + +def run_error(message, title=None): +    ui.command('warningDialog', message, title) +__builtin__.run_error = run_error + + +pages = [] # Must be initialized before init() because of calls to tab_changed + +def init(): +    pages.append(ProjectSettings()) +    pages.append(Installation()) +    pages.append(Larchify()) +    pages.append(Medium()) + +    __builtin__.docviewer = DocViewer() +    __builtin__.edit = Editor().edit + +    __builtin__.progress = Progress() +    __builtin__.logger = Logger() + +    MainWindow = fss('fetch_layout', 'page_main.uim') +    ui.widgetlist(MainWindow) + +    ui.connect(':notebook*changed', tab_changed) + +    ui.command(':larch.pack') +    # Set up the first gui page (project settings) +    pages[0].setup() +    ui.command(':larch.show') + diff --git a/build_tools/larch8/larch0/gui/front/page_installation.py b/build_tools/larch8/larch0/gui/front/page_installation.py new file mode 100644 index 0000000..82d57c5 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/page_installation.py @@ -0,0 +1,193 @@ +# page_installation.py - Handler for the installation page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +#    larch is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    larch is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with larch; if not, write to the Free Software Foundation, Inc., +#    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +# +#---------------------------------------------------------------------------- +# 2010.08.12 + +class Installation: +    def __init__(self): +        ui.widgetlist(fss('fetch_layout', 'page_installation.uim')) + +        ui.connectlist( +                (':addedpacks*clicked', self.edit_addedpacks), +                (':vetopacks*clicked', self.edit_vetopacks), +                (':pacmanconf*clicked', self.edit_pacmanconf), +                (':repos*clicked', self.edit_repos), +                (':editmirrorlist*clicked', self.edit_mirrorlist), +                (':cache_change*clicked', self.change_cache), +                (':editrepolist*clicked', self.edit_repolist), +                (':install*clicked', self.install), +                (':sync*clicked', self.dosync), +                (':updateall*clicked', self.doupdateall), +                (':update*clicked', self.doupdate), +                (':add*clicked', self.doadd), +                (':remove*clicked', self.doremove), +                (':installrepos*toggled', self.installrepo), +            ) + + +    def enter(self): +        """This is called when the page is entered/selected/shown. +        It performs initializations which depend on the state. +        """ +        docviewer.gohome('gui_installation.html') +        # Set package cache display +        ui.command(':cache_show.text', fss('getitem', 'pacman_cache')) +        ui.command(':installrepos.opton', fss('getbool', 'installrepo')) + + +    def data(self, key): +        return ui.command('install_page_data.get', key) + + +    def edit_addedpacks(self): +        edit('profile:addedpacks')              # In profile dir + + +    def edit_vetopacks(self): +        # If there is no vetopacks file, start an empty one +        edit('profile:vetopacks', "")           # In profile dir + + +    def edit_pacmanconf(self): +        edit('profile:pacman.conf.options',     # In profile dir +                'base:data/pacman.conf',        # Relative to base_dir +                label=self.data('edit_pc'), +                filter=pacmanoptions) + + +    def edit_repos(self): +        """This edits the repository list file for the live system. +        It will be used to construct the /etc/pacman.conf file. +        If the option to specify a different file for the installation +        stage is not enabled (the default), this file will also be used +        for the installation. +        """ +        edit('profile:pacman.conf.repos',       # In profile dir +                'base:data/pacman.conf.repos',  # Relative to base_dir +                label=self.data('edit_pr')) + + +    def edit_mirrorlist(self): +        ml = '/etc/pacman.d/mirrorlist' +        if not fss('isfile', ml): +            ml = 'base:data/mirrorlist'         # Relative to base_dir +        edit('working:mirrorlist', ml, +                label=self.data('edit_mli')) + + +    def change_cache(self): +        # Is anything more necessary? Do I need to test the path? +        # Would a directory browser be better? +        ok, path = ui.command('textLineDialog', +                self.data('prompt_ncp'), +                None, fss('getitem', 'pacman_cache')) +        if ok: +            self.set_pacman_cache(path) + + +    def set_pacman_cache(self, path): +            path = path.strip().rstrip('/') +            fss('setitem', 'pacman_cache', path) +            ui.command(':cache_show.text', path) + + +    def edit_repolist(self): +        """This edits the repository list file used for installation, +        if the corresponding option is enabled. +        """ +        # Should it be based on the default or on the profile? +        rf = 'profile:pacman.conf.repos' +        if not fss('isfile', rf): +            rf = 'base:data/pacman.conf.repos'  # Relative to base_dir +        edit('working:pacman.conf.repos', rf, +                label=self.data('edit_pri')) + + +    def installrepo(self, on): +        fss('setbool', 'installrepo', on) + + +    def install(self): +        """Start the installation. +        """ +        self.archin('install') + + +    def dosync(self): +        self.archin('refresh') + + +    def doupdateall(self): +        self.archin('updateall') + + +    def doupdate(self): +        f = ui.fileDialog(message=self.data('msg_pu'), +                filter=(self.data('filter_pu'), '*.pkg.tar.*')) +        if f: +            self.archin('update ' + f) + + +    def doadd(self): +        ok, plist = ui.command('textLineDialog', +                self.data('prompt_pi'), +                'pacman -S') +        if ok: +            self.archin('sync ' + plist.strip()) + + +    def doremove(self): +        ok, plist = ui.command('textLineDialog', +                self.data('prompt_pr'), +                'pacman -Rs') +        if ok: +            self.archin('remove ' + plist.strip()) + + +    def archin(self, cmd): +        """This runs the 'archin' script (as root). It doesn't wait for +        completion because the output must be collected and displayed +        while it is running. The display switches the view to the +        progress reporting page. It should probably activate the busy +        cursor too. +        """ +        larchcall('archin', cmd, ui.command(':installrepos.active')) + + + +def pacmanoptions(text): +    """A filter for pacman.conf to remove the repository info. +    """ +    texto = "" +    block = "" +    for line in text.splitlines(): +        block += line + "\n" +        if line.startswith("#["): +            break +        if line.startswith("[") and not line.startswith("[options]"): +            break +        if not line.strip(): +            texto += block +            block = "" +    return texto + + + diff --git a/build_tools/larch8/larch0/gui/front/page_larchify.py b/build_tools/larch8/larch0/gui/front/page_larchify.py new file mode 100644 index 0000000..a63ecff --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/page_larchify.py @@ -0,0 +1,296 @@ +# page_larchify.py - Handler for the project settings page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +#    larch is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    larch is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with larch; if not, write to the Free Software Foundation, Inc., +#    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +# +#---------------------------------------------------------------------------- +# 2010.10.07 + +USERINFO = ['pw', 'maingroup', 'uid', 'skel', 'xgroups', 'expert'] + +class Larchify: +    def __init__(self): +        ui.widgetlist(fss('fetch_layout', 'page_larchify.uim')) + +        ui.connectlist( +                (':build*clicked', self.build), +                (':ssh*toggled', self.sshtoggle), +                (':locales*clicked', self.locales), +                (':rcconf*clicked', self.rcconf), +                (':initcpio*clicked', self.initcpio), +                (':overlay*clicked', self.overlay), +                (':utable*clicked', self.uedit), +                (':useradd*clicked', self.useradd), +                (':userdel*clicked', self.userdel), +                (':rootpwb*clicked', self.rootpw), +                (':kernelb*clicked', self.kernelfile), +                (':kernelmkib*clicked', self.kernelpreset), +            ) + +        self.userheaders = self.data('uheaders') + + +    def data(self, key): +        return ui.command('larchify_page_data.get', key) + + +    def enter(self): +        """This is called when the page is entered/selected/shown. +        It performs initializations which depend on the state. +        """ +        docviewer.gohome('gui_larchify.html') + +        # Check that it could possibly be an Arch installation +        idir = fss('get_installation_dir') +        couldBeArch = fss('isdir', ':var/lib/pacman/local') +        ui.command(':build.enable', couldBeArch) +        if not couldBeArch: +            run_error(_("No Arch installation at %s") % idir) + +        # ssh keys +        sshon = fss('isfile', ':usr/bin/ssh-keygen') +        ui.command(':ssh.enable', sshon) + +        if fss('isfile', 'profile:nosshkeys'): +            sshon = False +        ui.command(":ssh.set", sshon) + +        # users table +        idir_normal = idir != '/' +        ui.command(':users.enable', idir_normal) +        if idir_normal: +            # Fetch users information +            fss('newUserinfo') +            self.readuserinfo() + +        # Root password +        self.showrootpw() + +        # Kernel info +        self.showkernel() + +        self.reenter() + + +    def reenter(self): +        """These also need resetting after a build run. +        """ +        # Whether there is an old system.sqf to reuse? +        ossqf = fss('oldsqf') +        if not ossqf: +            ui.command(':oldsquash.set', False) +        ui.command(':oldsquash.enable', ossqf) + +        # Whether there is a set of old glibc locales to reuse? +        olcl = fss('oldlocales') +        if not olcl: +            ui.command(':oldlocales.set', False) +        ui.command(':oldlocales.enable', olcl) + +#TODO: Remove hack if the underlying bug gets fixed +        ui.command(":larchify_advanced.enable_hack") + + +    def readuserinfo(self, select=None): +        """'select' should be a username, defaulting to the first entry. +        """ +        self.usersel = 0 +        self.userlist = [] +        i = 0 +        for u in fss('allusers'): +            self.userlist.append(self.userinfolist(u)) +            if u == select: +                self.usersel = i +            i += 1 +        ui.command(':utable.set', self.userlist, self.usersel) + + +    def userinfolist(self, user): +        return [user] + fss('getuserinfo', user, USERINFO) + + +    def uedit(self, row, column): +        if self.usersel == row: +            uname = self.userlist[row][0] +            ulcell = self.userlist[row][column] +            if column == 4: +                ok, text = self.select_skel(ulcell) +            else: +                ok, text = ui.command('textLineDialog', +                        self.userheaders[column] + ':', 'larchify', ulcell) +            text = text.strip() +            if ok: +                try: +                    if (column == 0) and (text != ''): +                        # Rename the user, by adding a new one and deleting +                        # the old +                        uname = text +                        fss('newuser', uname) +                        i = 0 +                        for f in USERINFO: +                            i += 1 +                            fss('userset', uname, f, self.userlist[row][i]) +                        if not fss('deluser', ulcell): +                            run_error(self.data('rn_error')) + +                    else: +                        fss('userset', uname, USERINFO[column-1], text) +                        fss('saveusers') + +                except: +                    run_error(self.data('ud_error')) +                self.readuserinfo(uname) + +        else: +            self.usersel = row + + +    def select_skel(self, current): +        # Present a list of available 'skel' folders +        self.skellist = [self.data('def_skel')] +        for f in fss('listskels'): +            self.skellist.append(f.rsplit('/skel_', 1)[1]) +        try: +            i = self.skellist.index(current) +        except: +            i = 0 +        ok, skeli = ui.command('listDialog', self.data('skel_lbl'), +                self.data('skel_ttl'), self.skellist, i) +        if ok: +            return (True, '' if skeli == self.skellist[0] +                    else skeli.split()[0]) +        return (False, '') + + +    def useradd(self): +        ok, name = ui.command('textLineDialog', self.data('newlogin')) +        if ok: +            name = name.strip() +            if name != '' and fss('newuser', name): +                self.userlist.append(self.userinfolist(name)) +                self.usersel = len(self.userlist) -1 +                ui.command(':utable.set', self.userlist, self.usersel) + + +    def userdel(self): +        if self.usersel >= 0: +            user = self.userlist[self.usersel][0] +            if fss('deluser', user): +                del(self.userlist[self.usersel]) +                lu = len(self.userlist) +                if lu: +                    if lu <= self.usersel: +                        self.usersel -= 1 +                ui.command(':utable.set', self.userlist, self.usersel) + + +    def showrootpw(self): +        self.rootpw = fss('readfile', 'profile:rootpw', trap=False) +        if self.rootpw == None: +            self.rootpw = "" +        ui.command(':rootpwe.text', self.rootpw) + + +    def rootpw(self): +        ok, pw = ui.command('textLineDialog', self.data('newrootpw'), +                "larchify", self.rootpw) +        if ok: +            pw = pw.strip() +            if pw: +                fss('savefile', 'profile:rootpw', pw) +            else: +                fss('rm_rf', 'profile:rootpw') +            self.showrootpw() + + +    def sshtoggle(self, on): +        """Whether the system ssh keys are pregenerated +        depends on the presence of the profile file 'nosshkeys' +        (and of course on openssh being installed). +        """ +        sshoff = fss('isfile', 'profile:nosshkeys') +        if on: +            if sshoff: +                fss('rm_rf', 'profile:nosshkeys') +        elif not sshoff: +            fss('savefile', 'profile:nosshkeys', "Don't pregenerate ssh keys") + + +    def locales(self): +        edit('profile:rootoverlay/etc/locale.gen', 'install:etc/locale.gen') + + +    def rcconf(self): +        edit('profile:rootoverlay/etc/rc.conf', 'install:etc/rc.conf') + + +    def initcpio(self): +        edit('profile:rootoverlay/etc/mkinitcpio.conf.larch', +                'install:etc/mkinitcpio.conf.larch') + + +    def overlay(self): +        fss('browse', 'profile:rootoverlay') + + +    def showkernel(self): +        if fss('isfile', 'profile:kernel'): +            ki = fss('readfile', 'profile:kernel') +        else: +            ki = fss('readfile', 'base:data/kernel') +        self.kernel, self.kernelpreset = ki.split() +        ui.command(':kernele.text', self.kernel) +        ui.command(':kernelmkie.text', self.kernelpreset) + + +    def kernelfile(self): +        ok, kf = ui.command('textLineDialog', self.data('kernelf'), +                "larchify", self.kernel) +        if ok: +            kf = kf.strip() +            if not kf: +                fss('rm_rf', 'profile:kernel') + +            elif (' ' in kf) or not fss('isfile', 'install:boot/' + kf): +                run_error(_("Invalid kernel binary: %s") % kf) +                return +            else: +                fss('savefile', 'profile:kernel', kf + ' ' + self.kernelpreset) +            self.showkernel() + + +    def kernelpreset(self): +        ok, kp = ui.command('textLineDialog', self.data('kernelp'), +                "larchify", self.kernelpreset) +        if ok: +            kp = kp.strip() +            if not kp: +                fss('rm_rf', 'profile:kernel') + +            elif (' ' in kp) or not fss('isfile', +                    'install:etc/mkinitcpio.d/%s.preset' % kp): +                run_error(_("Invalid kernel mkinitcpio preset: %s") % kp) +                return +            else: +                fss('savefile', 'profile:kernel', self.kernel + ' ' + kp) +            self.showkernel() + + +    def build(self): +        larchcall('larchify', ui.command(':oldsquash.active'), +                ui.command(':oldlocales.active')) diff --git a/build_tools/larch8/larch0/gui/front/page_medium.py b/build_tools/larch8/larch0/gui/front/page_medium.py new file mode 100644 index 0000000..e63ce13 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/page_medium.py @@ -0,0 +1,441 @@ +# page_medium.py - Handler for the project settings page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +#    larch is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    larch is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with larch; if not, write to the Free Software Foundation, Inc., +#    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +# +#---------------------------------------------------------------------------- +# 2010.11.28 + +"""This page takes a directory processed by larchify. It produces a bootable +larch medium, or, in the case of CD/DVD, an iso image. +It also handles creation of a boot-iso for an existing larch medium (partition) +and can copy larch media to different devices. +""" + +import os +from config import detection_methods, OKFS + + +class Medium: +    def __init__(self): +        self.medium = None +        self.srcmedium = None +        self.detectionmodes = detection_methods.split('|') +        ui.widgetlist(fss('fetch_layout', 'page_medium.uim')) + +        ui.connectlist( +                (':vlabelb*clicked', self.newlabel), +                (':selectpart*clicked', self.choosedest), +                (':selectsrc*clicked', self.choosesrc), +                (':persist*toggled', self.persistence), +                (':ovl_journal*toggled', self.journal), +                (':mediumtype*changed', self.mediumtype), +                (':srctype*changed', self.srctype), +                (':make_medium*clicked', self.make), +                (':nolarchboot*toggled', self.bootnosearch), +                (':detection*changed', self.mediumsearch), +                (':pformat*toggled', self.setpformat), +                (':bootlines*clicked', self.editbootlines), +                (':syslinuxtemplate*clicked', self.editsyslin), +                (':cdroot*clicked', self.browsecdroot), +            ) + + +    def enter(self): +        """This is called when the page is entered/selected/shown. +        It performs initialisations which depend on the state. +        """ +        self.destinationpath = '' +        self.fsok = None +        self.source, ok = fss('testlarchify')   # check the larchified installation +        if not ok: +            run_error(self.data('msg_med') % self.source) +            self.source = '' +        self.mediumtype(None) +        if self.medium != 'medium-boot': +            self.srctype(None) +        detect = fss('getitem', 'medium_search') +        ui.command(':detection.set', +                [self.data('detectionmodes')[l] for l in self.detectionmodes], +                self.detectionmodes.index(detect)) +        self.nlbenable(detect) +        ui.command(':nolarchboot.set', fss('getbool', 'boot_nosearch')) +        ui.command(':vlabele.text', fss('get_mediumlabel')) +        ui.command(':ovl_journal.set', fss('getbool', 'journal')) +        fmt = fss('getbool', 'do_format') +        ui.command(':pformat.set', fmt ) +        ui.command(':ovl_journal.enable', fmt) +        ui.command(':persist.set', fss('getbool', 'do_persist')) + +        ui.command(':larchpart.text')       # clear the destination +        docviewer.gohome('gui_medium.html') + + +    def data(self, key): +        return ui.command('medium_page_data.get', key) + + +    def mediumtype(self, index): +        if (index == None): +            if self.medium == None: +                self.medium = self.data('media')[0] +        else: +            self.medium = self.data('media')[index] + +        if self.medium == 'medium-boot': +            label = fss('get_bootisolabel') +            ui.command(':srctype.setindex', self.data('sources').index('device')) +            ui.command(':srctype.enable', False) +            ifile = fss('getbootisofile') + +        else: +            label = fss('get_mediumlabel') +            ui.command(':srctype.enable', True) + +            if self.medium == 'medium-iso': +                ifile = fss('getisofile') + +        self.showlabel(label) + +        if self.medium == 'medium-w': +            ui.command(':mediumopts.enable', True) +            self.setdestinationpath('') + +        else: +            ui.command(':mediumopts.enable', False) +            idir =  fss('getisosavedir') +            if not fss('isdir', idir): +                fss('setitem', 'isosavedir', '') +                idir =  fss('getisosavedir') +            self.setdestinationpath(os.path.join(idir, ifile)) + +        if index != None: +            self.enableprofile() +            self.enablemake() + + +    def srctype(self, index): +        if (index == None): +            if self.srcmedium == None: +                self.srcmedium = self.data('sources')[0] +        else: +            self.srcmedium = self.data('sources')[index] + +        if self.srcmedium == 'larchified': +            self.setsourcepath(self.source) +            ui.command(':selectsrc.enable', False) + +        elif self.srcmedium == 'device': +            self.setsourcepath('')  # clear source, it must be selected +            ui.command(':selectsrc.enable', True) + +        elif self.srcmedium == 'isofile': +            isof = fss('getisofile') +            isod = fss('getisosavedir') +            self.pendingpath = os.path.join(isod, isof) +            ui.command(':selectsrc.enable', True) +            self.setsourcepath('') +            self.checklarchsource() + + +    def setsourcepath(self, path): +        self.sourcepath = path +        ui.command(':srclocation.text', path) +        self.enableprofile() +        self.enablemake() + + +    def choosesrc(self): +        # 'larchified' should not be possible - it is set on the project page +        if self.srcmedium == 'device': +            self.devices = [] +            larchcall('*rootfn', self._cd_line, 'blkid -c dev/null -o list') + +        elif self.srcmedium == 'isofile': +            # Pop up a file browser +            self.pendingpath = self.isopath(mode='source') +            self.checklarchsource() + +        else: +            debug('page_medium: Medium.choosesrc / ' + self.srcmedium) + +    def _cd_line(self, line): +        if line == None: +            # Completed - pop up device chooser +            ok, choice = ui.command('listDialog', +                    self.data('parts_src'), +                    self.data('parts_t'), +                    self.devices, len(self.devices) - 1) +            if ok: +                self.pendingpath = choice.split()[0] +                ui.idle_add(self.checklarchsource) + +        else: +            l = line.strip() +            if l.startswith('/dev/'): +                i = l.find('(not mounted)') +                if i > 0: +                    ls = l[:i].split(None, 2) +                    if (ls[0] != self.destinationpath) and (len(ls) == 3): +                        self.devices.append('%-10s %s' % (ls[0], ls[2])) + + +    def checklarchsource(self): +        if self.pendingpath: +            # check it is really a larch medium +            self.larchok = False +            larchcall('*larchmedium', self._cs_line, self.pendingpath) + +    def _cs_line(self, line): +        if line == None: +            # Completed +            if self.larchok: +                self.setsourcepath(self.pendingpath) +        else: +            l = line.strip() +            if l.startswith('##--'): +                if l.endswith('ok'): +                    self.larchok = True + + +    def enableprofile(self): +        """Set the enabled state of the medium profile frame, +        according to the state of the choices. +        """ +        ui.command(':mediumprofile.enable', (self.medium != 'medium-boot') +                and (self.srcmedium == 'larchified')) + + +    def enablemake(self): +        on = bool(self.destinationpath) and bool(self.sourcepath) +        if self.medium == 'medium-w': +            formatting = fss('getbool', 'do_format') +            self.enablepersist(formatting or (self.fsok in OKFS)) +            if (not formatting): +                if (self.fsok not in OKFS) and (self.fsok != 'vfat'): +                    on = False +        ui.command(':make_medium.enable', on) + + +    def enablepersist(self, on): +        ui.command(':persist.enable', on) +        self.persist_enabled = on + + +    def persistence(self, on): +        fss('setbool', 'do_persist', on) + + +    def journal(self, on): +        fss('setbool', 'journal', on) + + +    def mediumsearch(self, option): +        choice = self.detectionmodes[option] +        fss('setitem', 'medium_search', choice) +        self.nlbenable(choice) + + +    def nlbenable(self, choice): +        ui.command(':nolarchboot.enable', choice != 'search') + + +    def bootnosearch(self, on): +        fss('setbool', 'boot_nosearch', on) + + +    def setpformat(self, on): +        fss('setbool', 'do_format', on) +        ui.command(':ovl_journal.enable', on) +        self.enablemake() + + +    def editbootlines(self): +        f0 = 'profile:cd-root/boot0/bootlines' +        if not fss('isfile', f0): +            f0 = 'base:cd-root/boot0/bootlines' +        edit('profile:cd-root/boot/bootlines', 'base:cd-root/boot0/bootlines') + + +    def editsyslin(self): +        f0 = 'profile:cd-root/boot0/isolinux/isolinux.cfg' +        if not fss('isfile', f0): +            f0 = 'base:cd-root/boot0/isolinux/isolinux.cfg' +        edit('profile:cd-root/boot/isolinux/isolinux.cfg', f0) + + +    def browsecdroot(self): +        fss('browse', 'profile:cd-root') + + +    def newlabel(self): +        labelsrc = 'bootiso' if self.medium == 'medium-boot' else 'medium' +        ok, l = ui.command('textLineDialog', +                self.data('prompt_label'), +                None, fss('get_%slabel' % labelsrc)) +        if ok: +            self.showlabel(fss('set_%slabel' % labelsrc, l)) + + +    def showlabel(self, l): +        ui.command(':vlabele.text', l) + + +    def choosedest(self): +        if self.medium == 'medium-w': +            # Present a list of unmounted partitions +            self.devices = [] +            larchcall('*rootfn', self._sd_line, 'blkid -c /dev/null -o list') + +        elif self.medium == 'medium-iso': +            # Pop up a file browser +            path = self.isopath(mode='source') +            if path: +                self.setdestinationpath(path) + +        elif self.medium == 'medium-boot': +            # Pop up a file browser +            path = self.isopath(mode='bootiso') +            if path: +                self.setdestinationpath(path) + +    def _sd_line(self, line): +        if line == None: +            nmdevices = [] +            for part in fss('get_partitions'):  # ->(dev, size in MiB(int)) +                if part[0] == self.sourcepath: +                    continue +                found = False +                for partinfo in self.devices: +                    if partinfo[0] == part[0]: +                        if partinfo[2]: +                            nmdevices.append('%-12s %8d MiB    %-10s  %s' +                                    % (part[0], part[1], partinfo[1], partinfo[2])) +                        found = True +                        break +                if not found: +                    nmdevices.append('%-12s %-12d MiB' % (part[0], part[1])) + +            # Completed - pop up device chooser +            ok, choice = ui.command('listDialog', +                    self.data('parts_dst'), +                    self.data('parts_t'), +                    nmdevices, len(nmdevices) - 1) +            if ok: +                ui.idle_add(self.setdestinationpath, choice.split()[0]) + +        else: +            l = line.strip() +            if l.startswith('/dev/'): +                i = l.find('(not mounted)') +                if i > 0: +                    # Try to get label for unmounted devices only +                    ls = l[:i].split(None, 2) +                    if len(ls) < 3: +                        ls.append('-')  # signifies 'no label' +                else: +                    ls = l.split(None, 2) +                    ls[2] = None    # mark the partition as mounted +                self.devices.append(ls) + + +    def setdestinationpath(self, path): +        ui.command(':larchpart.text', path) +        self.destinationpath = path +        if path.startswith('/dev/'): +            # Check the file-system +            self.fsok = None +            larchcall('*rootfn', self._em_line, +                    'blkid -c /dev/null -o value -s TYPE %s' % path) +        else: +            self.enablemake() + +    def _em_line(self, line): +        if line == None: +            # Completed +            self.enablemake() +        else: +            line = line.strip() +            if line: +                self.fsok = line + + +    def isopath(self, mode='dest'): +        sdir = fss('getisosavedir') +        ifname = fss('getbootisofile' if mode=='bootiso' else 'getisofile') +        path = ui.fileDialog(self.data('isoget' if mode=='source' else 'isopath'), +            startdir=sdir, create=(mode!='source'), +            file=ifname, filter=(self.data('iso_type'), '*.iso')) +        if path: +            f = os.path.basename(path) +            d = os.path.dirname(path) +            if d != sdir: +                fss('setitem', 'isosavedir', d) +            if f != ifname: +                fss('setitem', 'bootisofile' if mode=='bootiso' else 'isofile', f) +            return path + +        return None + + +    def make(self): +        """Write the larch medium. +        """ +        if self.srcmedium == 'larchified': +            source = None +        else: +            source = self.sourcepath +            if not source: +                debug("page_medium: make / null source") +                return + +        args = ['-l', ui.command(':vlabele.get')] + +        if self.medium == 'medium-boot': +            # Write a boot iso file +            args += ['-b', '-o', self.destinationpath] +            larchcall('writemedium', source, args) + +        elif self.medium == 'medium-iso': +            # Write an 'iso' file +            args += ['-o', self.destinationpath] +            larchcall('writemedium', source, args) + +        else: +            # Write to partition +            # Medium detection options +            detect = fss('getitem', 'medium_search') +            args += ['-d', detect] +            if (detect != 'search') and ui.command(':nolarchboot.active'): +                args.append('-n') +            # Formatting +            if fss('getbool', 'do_format'): +                # Journalling? +                if not fss('getbool', 'journal'): +                    args.append('-j') +            else: +                args.append('-F') +            # Set master boot record? +            if ui.command(':nombr.active'): +                args.append('-m') +            # Persistence +            if fss('getbool', 'do_persist') and self.persist_enabled: +                args.append('-P') +            # Add the medium to the argument list +            args.append(self.destinationpath) +            larchcall('writemedium', source, args) diff --git a/build_tools/larch8/larch0/gui/front/page_project.py b/build_tools/larch8/larch0/gui/front/page_project.py new file mode 100644 index 0000000..cbbda66 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/page_project.py @@ -0,0 +1,241 @@ +# page_project.py - Handler for the project settings page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +#    larch is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    larch is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with larch; if not, write to the Free Software Foundation, Inc., +#    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +# +#---------------------------------------------------------------------------- +# 2010.08.15 + +import os + +class ProjectSettings: +    def __init__(self): +        ui.widgetlist(fss('fetch_layout', 'page_project.uim')) +        ui.widgetlist(fss('fetch_layout', 'profile_browse.uim')) +        ui.command('dialog:profile_browser.pack') + +        ui.connectlist( +                (':choose_profile_combo*changed', self.switch_profile), +                (':profile_rename*clicked', self.rename_profile), +                (':profile_browse*clicked', self.browse_profile), +                (':profile_delete*clicked', self.delete_profile), +                (':profile_save*clicked', self.copy_profile), +                (':profile_clone*clicked', self.clone_profile), +                (':installation_path_change*clicked', self.new_build_path), +                (':choose_project_combo*changed', self.switch_project), +                (':new_project*clicked', self.get_new_project_name), +                (':project_delete*clicked', self.delete_project), +                ('dpb:example_list*changed', self.dpb_example), +                ('dpb:browse*clicked', self.dpb_browse), +                ('dpb:name_s*clicked', self.dpb_source), +            ) + + +    def setup(self): +        # Initialize project combobox +        self.projects = fss('get_projects') +        self.project_name = fss('get_project') +        try: +            pix = self.projects.index(self.project_name) +        except: +            self.switch_project(0) +            return +        ui.command(':choose_project_combo.set', self.projects, pix) +        # Initialize profile combobox +        self.profiles = fss('get_profiles') +        self.profile_name = fss('get_profile') +        try: +            pfix = self.profiles.index(self.profile_name) +        except: +            self.switch_profile(0) +            pfix = 0 +        ui.command(':choose_profile_combo.set', self.profiles, pfix) +        # Initialize installation_dir display +        self.set_build_dir(fss('get_installation_dir')) + + +    def enter(self): +        """This is called when the page is entered/selected/shown. +        It performs initializations which depend on the state. +        """ +        docviewer.gohome('gui_project_settings.html') + + +    def data(self, key): +        return ui.command('project_page_data.get', key) + + +    def set_build_dir(self, path): +        self.build_dir = path +        ui.command(':installation_path_show.text', self.build_dir) +        ui.enable_installation_page(self.build_dir != '/') + + +    def switch_profile(self, index): +        """This has no effect on the display! +        It is assumed that the display is already updated, or will be +        updated later, and that the index is valid, so that the operation +        cannot fail. +        """ +        self.profile_name =  self.profiles[index] +        fss('set_profile', self.profile_name) + + +    def browse_profile(self): +        self.example_profile_dir, self.dpb_example_list = fss('get_example_profiles') +        ui.command('dpb:example_list.set', self.dpb_example_list, -1) +        if ui.command('dialog:profile_browser.showmodal'): +            source = ui.command('dpb:source.get') +            name = ui.command('dpb:name.get') +            if name in self.profiles: +                if not ui.command('confirmDialog', self.data('prompt_pr')): +                    return +            if fss('get_new_profile', source, name): +                self.setup() +            else: +                run_error(self.data('msg_npd') % source) + + +    def dpb_example(self, index): +        name = self.dpb_example_list[index] +        ui.command('dpb:source.text', self.example_profile_dir + '/' + name) +        ui.command('dpb:name.text', name) + + +    def dpb_browse(self): +        source = ui.fileDialog(self.data('file_ps'), dirsonly=True, +                startdir=fss('getitem', 'profile_browse_dir')) +        if source: +            fss('setitem', 'profile_browse_dir', os.path.dirname(source)) +            ui.command('dpb:source.text', source) +            ui.command('dpb:name.text', os.path.basename(source)) +            ui.command('dpb:example_list.set', self.dpb_example_list, -1) + + +    def dpb_source(self): +        ok, name = ui.command('textLineDialog', _("Name for new profile:"), None, +                os.path.basename(ui.command('dpb:source.get'))) +        if ok: +            ui.command('dpb:name.text', name) + + +    def rename_profile(self): +        if fss('can_rename_profile'): +            ok, new = ui.command('textLineDialog', +                    self.data('prompt_pn'), +                    None, self.profile_name) +            if ok: +                new = new.strip() +                if new in self.profiles: +                    ui.command('warningDialog', self.data('prompt_pe') % new) +                else: +                    fss('rename_profile', new) +                    self.setup() +        else: +            ui.command('infoDialog', self.data('msg_pu')) + + +    def clone_profile(self): +        ok, name = ui.command('textLineDialog', self.data('prompt_clone'), self.profile_name) +        if ok: +            name = name.strip() +            self.save_profile(name) +            fss('set_profile', name) +            self.setup() + +    def copy_profile(self): +        startdir = fss('getitem', 'profile_browse_dir') +        path = ui.fileDialog(self.data('file_sp'), +                create=True, dirsonly=True, file=self.profile_name, +                startdir=startdir) +        if path: +            fss('set_profile_browse_dir', path) +            self.save_profile(path) + +    def save_profile(self, path): +        ok = fss('save_profile', path, False) +        if ok == False: +            if ui.command('confirmDialog', self.data('prompt_dr')): +                # Force overwrite +                fss('save_profile', path, True) +        elif ok == None: +            run_error(self.data('msg_piu')) +        else: +            self.setup() + + +    def delete_profile(self): +        plist = fss('list_free_profiles') +        if plist: +            ok, item = ui.command('listDialog', self.data('prompt_dp'), +                    self.data('delprof'), plist) +            if ok: +                if fss('delete_profile', item): +                    self.setup() +                else: +                    ui.command('infoDialog', self.data('msg_dpff') % item) +        else: +            ui.command('infoDialog', self.data('msg_npf')) + + +    def new_build_path(self): +        # Is anything more necessary? Do I need to test or create the path? +        # I don't think so, the installation code does that. +        # If the path is "/", the installation page should be inhibited, +        # but that is handled by 'setup'. +        ok, path = ui.command('textLineDialog', +                self.data('prompt_ip'), +                None, self.build_dir) +        if ok: +            path = fss('set_installation_dir', path) +            if path: +                self.set_build_dir(path) + + +    def switch_project(self, index): +        fss('set_project', self.projects[index]) +        self.setup() + + +    def get_new_project_name(self): +        ok, name = ui.command('textLineDialog', +                self.data('prompt_np'), +                None, self.project_name) +        if ok: +            if name in self.projects: +                run_error(self.data('msg_pe') % name) +            else: +                fss('set_project', name) +                self.setup() + + +    def delete_project(self): +        """Pop up a list of eligible project names, the selected one +        will be deleted. +        """ +        plist = fss('list_free_projects') +        if plist: +            ok, item = ui.command('listDialog', self.data('prompt_pd'), +                    self.data('delproj'), plist) +            if ok: +                fss('delete_project', item) +                self.setup() +        else: +            ui.command('infoDialog', self.data('msg_np')) + +  | 
