diff options
| author | James Meyer <james.meyer@operamail.com> | 2010-12-02 22:37:23 (GMT) | 
|---|---|---|
| committer | James Meyer <james.meyer@operamail.com> | 2010-12-02 22:37:34 (GMT) | 
| commit | 8b94d7f39c71234712bead363526a0283efeb9fa (patch) | |
| tree | 23f1dbd6458dc39a2c1b08bcdd4cbf768a60d84d /build_tools/larch8/larch0/gui | |
| parent | 338af567e74d08cbd357079941208e494463d61e (diff) | |
| download | linhes_dev-8b94d7f39c71234712bead363526a0283efeb9fa.zip | |
larch8: first checkin, still needs some work
Diffstat (limited to 'build_tools/larch8/larch0/gui')
21 files changed, 3798 insertions, 0 deletions
diff --git a/build_tools/larch8/larch0/gui/controller.py b/build_tools/larch8/larch0/gui/controller.py new file mode 100644 index 0000000..493d9c4 --- /dev/null +++ b/build_tools/larch8/larch0/gui/controller.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python2 +# +# controller.py - Manages file-system access and calling of larch scripts +# +# (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.25 + +import sys, os, __builtin__ +from liblarch.rootrun import init_rootrun +import threading +from glob import glob +from subprocess import call +try: +    import json as serialize +except: +    import simplejson as serialize + +from config import * + +from liblarch.translation import i18n_module, lang, i18nurl +__builtin__._ = i18n_module(base_dir, 'larch') +__builtin__.lang = lang + + +exports = {} +def add_exports(elist): +    for key, method in elist: +        exports[key] = method + +__builtin__.add_exports = add_exports + + +def error0(message): +    sys.stderr.write('>>ERROR>>' + message + '\n') +    sys.stderr.flush() +__builtin__.error0 = error0 + + +class Fs: +    """Collect file system access methods in one class. +    """ +    def __init__(self): +        add_exports(    ( +                ('fetch_layout', self.fetch_layout), +                ('isfile', self.isfile), +                ('isdir', self.isdir), +                ('rm_rf', self.rm_rf), +                ('get_partitions', self.get_partitions), +                ('readfile', self.readfile), +                ('savefile', self.savefile), +                ('get_docs_url', self.get_docs_url), +                ('oldsqf', self.oldsqf), +                ('oldlocales', self.oldlocales), +                ('browse', self.browse)) +        ) + +    def fetch_layout(self, layout_file): +        fh = open(base_dir + '/gui/layouts/' + layout_file) +        r = fh.read() +        fh.close() +        return (True, eval(r)) + +    def rm_rf(self, path): +        call(['rm', '-rf', self._getpath(path)]) +        return (True, None) + +    def oldsqf(self): +        return (True, +                os.path.isfile(self._getpath('install:' + CHROOT_SYSTEMSQF)) +                or os.path.isfile(self._getpath('install:' + CHROOT_DIR_MEDIUM +                        + '/larch/system.sqf'))) + +    def oldlocales(self): +        return (True, os.path.isdir(self._getpath('install:%s/locale' +                % CHROOT_DIR_BUILD))) + +    def isfile(self, path): +        return (True, os.path.isfile(self._getpath(path))) + +    def isdir(self, path): +        return (True, os.path.isdir(self._getpath(path))) + + +    def browse(self, path): +        fpath = self._getpath(path) +        if call(['mkdir', '-p', fpath]) == 0: +            # Start file browser at fpath +            call(project_manager.appget('filebrowser').replace('$', fpath) +                    + ' &', shell=True) +            return (True, None) +        else: +            return (False, None) + +    def readfile(self, f): +        f = self._getpath(f) +        try: +            fh = open(f) +            r = fh.read() +            fh.close() +            return (True, r) +        except: +            return (False, _("Couldn't read file '%s'") % f) + +    def savefile(self, f, d): +        f = self._getpath(f) +        dir = os.path.dirname(f) +        if not os.path.isdir(dir): +            os.makedirs(dir) +        try: +            fh = open(f, "w") +            fh.write(d) +            fh.close() +            return (True, None) +        except: +            return (False, _("Couldn't save file '%s'") % f) + +    def _getpath(self, f): +        if f[0] != "/": +            base, f = f.split(':') +            f = '/' + f +            if base == 'base': +                f = base_dir + f +            elif base == 'profile': +                f = project_manager.profile_path + f +            elif base == 'working': +                f = project_manager.project_dir + f +            else: +                f = project_manager.get_ipath()[1] + f +        return f + +    def get_docs_url(self, page): +        return (True, i18nurl(base_dir + '/docs/html/' + page)) + + +    def get_partitions(self): +        """Get a list of available (not too small) partitions. +        """ +        partlist = [] +        with open('/proc/partitions') as fh: +            for l in fh: +                fields = l.split() +                if len(fields) == 4: +                    dev = fields[3] +                    if dev.startswith('sd') and (dev[-1] in '0123456789'): +                        size = (int(fields[2]) + 512) / 1024 +                        if (size > 200): +                            # Keep a tuple (partition, size in MiB) +                            partlist.append(('/dev/' + dev, size)) +        return (True, partlist) + + + +class LarchScripts: +    """This class deals with calling the larch scripts. +    As they must be run as root, the rootrun module is used, which uses +    pexpect to start the subprocesses with su or sudo. +    Callbacks are used to fetch the password and return output lines +    (as they become available) and also to signal completion. +    While reading output from the subprocess the gui must remain responsive, +    and capable of updates (e.g. for logging, or cancelling of the process). +    To achieve this a separate thread is used for reading input from the +    subprocess (this is also taken care of by the rootrun module). +    The output lines and completion information are passed to the gui +    via signals/messages. The gui is responsible for handling them in the +    correct thread, e.g. by adding 'idle' calls. +    """ +    def __init__(self): +        """Initialize the mechanism for making root calls. +        """ +        self.rootfunc = init_rootrun(self._pwget) + + +    def do(self, cmdname, *args): +        """This is the larchscripts dispatcher. +        The first argument is the name of the function, the remaining ones +        are the arguments to the function. +        """ +        return getattr(self, 'l_' + cmdname)(*args) + + +    def _pwget(self, cb, prompt): +        """Callback to fetch password, running in rootrun thread. +        """ +        self.pw_event = threading.Event() +        ui_signal('get_password', prompt) +        self.pw_event.wait() +        self.rootfunc.cb_done(cb, *self.pw_returned) + + +    def l_sendpassword(self, ok, pw): +        """The gui passes in the password via this call. +        The result needs to be passed to the waiting self._pwget method +        (which is running in a different thread). +        """ +        self.pw_returned = (ok, pw) +        self.pw_event.set() +        return None + + +    def _line_cb(self, cb, line): +        try: +            dline = serialize.loads(line) +        except: +            dline = line +        ui_signal('line_cb', dline) + + +    def _end_cb(self, cb, ok, res): +        # As the output is passed line-by-line there is no need to pass +        # it again. +        ui_signal('end_cb', ok) + + +    def call(self, cmd, arg=[]): +        """Start a process running as root. +        """ +        args = ' '.join(['"%s"' % i for i in arg]) +        cmdx = '%s/cli/%s.py -s %s' % (base_dir, cmd, args) +        self.l_rootfn(cmdx) +        return None + + +    def l_rootfn(self, cmd): +        """Run a command as root. +        """ +        ui_signal('log', '++' + cmd) +        self.rootfunc.run(cmd, self._end_cb, self._line_cb, project_manager.project_dir) +        return None + + +    def l_archin(self, cmd, installrepos): +        args = ['-p', project_manager.profile_path, +                '-i', project_manager.get_ipath()[1], +                '-c', project_manager.get('pacman_cache')] +        rf = project_manager.project_dir + '/pacman.conf.repos' +        if installrepos and os.path.isfile(rf): +            args += ['-r', rf] +        return self.call('archin', args + cmd.split()) + + +    def l_larchify(self, oldsyssqf, oldlocales): +        args = ['-p', project_manager.profile_path, +                '-i', project_manager.get_ipath()[1],] +        if oldsyssqf: +            args.append('-o') +        if oldlocales: +            args.append('-l') +        return self.call('larchify', args) + + +    def l_larchmedium(self, dev): +        return self.call('medium', ['-T', '-S', dev,]) + + +    def l_writemedium(self, source, args): +        if source: +            args += ['-S', source] +        else: +            args += ['-p', project_manager.profile_path] +            args += ['-i', project_manager.get_ipath()[1]] +        return self.call('medium', args) + + +    def l_pid(self, pid): +        self.rootfunc.setpid(pid) +        return None + + +    def l_interrupt(self): +        ui_signal('log', '--Terminate--') +        self.rootfunc.interrupt() +        return None + + +    def l_reply(self, reply): +        self.rootfunc.send(reply) +        return None + + +    def l_close(self): +        # if something is running, stop it ... +        try: +            self.rootfunc.interrupt() +        except: +            pass + +fs = Fs() + +import project +project_manager.init() + +larchscripts = LarchScripts() +add_exports((   ('larchscript', larchscripts.do), +    )) + + 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')) + + diff --git a/build_tools/larch8/larch0/gui/larch.py b/build_tools/larch8/larch0/gui/larch.py new file mode 100755 index 0000000..cda420b --- /dev/null +++ b/build_tools/larch8/larch0/gui/larch.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 +# +# larch.py - GUI for the larch scripts +# +# (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 sys, os, __builtin__ +dirpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(dirpath + '/front') +basedir = os.path.dirname(dirpath) +sys.path.append(basedir + '/cli') +sys.path.append(os.path.dirname(basedir)) + +from controller import base_dir, exports +# Note that the gui module must have a reference point for relative +# paths, so the current directory must be set to the larch base directory +# before starting the gui: +os.chdir(base_dir) + +import mainwindow + +_running = False +def fss(key, *args, **kargs): +    while True: +        if _running: +            ui.busy(True) +        res = exports[key](*args) +        if _running and not ui.isbusy(): +            ui.busy(False) +        if res: +            result = res[1] +            if res[0]: +                filter = kargs.get('filter') +                return filter(result) if filter else result + +            if kargs.get('trap', True): +                ui.command('errorDialog', result) +                # That might return, but the gui should quit (soon?). +        return None + +__builtin__.fss = fss +__builtin__.ui = mainwindow.Ui() +__builtin__.ui_signal = ui.sigin + +mainwindow.init() +_running = True +ui.run() diff --git a/build_tools/larch8/larch0/gui/layouts/docviewer.uim b/build_tools/larch8/larch0/gui/layouts/docviewer.uim new file mode 100644 index 0000000..8e85a0f --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/docviewer.uim @@ -0,0 +1,72 @@ +# docviewer.uim - The layout for the documentation viewer widget +# +# (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.05.21 + +[ +    ['Frame', 'doc:page', +            {   'layout': +                    ['VBOX', +                        ['HBOX', 'doc:header', '*', 'doc:back', 'doc:forward', +                                'doc:home', 'doc:parent', 'doc:hide'], +                        'doc:content' +                    ] +            } +    ], +    ['Label', 'doc:header', +            {   'markup': ['h2', _("Documentation")] +            } +    ], +    ['HtmlView', 'doc:content', {}], +    ['Button', 'doc:hide', +            {   'text': _("Hide"), +                'tt': _("Return to the larch controls"), +                'clicked': '' +            }, +    ], +    ['Button','doc:back', +            {   'icon': 'left', +                'tt': _("Go back in the viewing history"), +                'clicked': '' +            }, +    ], +    ['Button','doc:forward', +            {   'icon': 'right', +                'tt': _("Go forward in the viewing history"), +                'clicked': '' +            }, +    ], + +    ['Button','doc:home', +            {   'icon': 'reload', +                'tt': _("Reload the documentation for the current larch tab"), +                'clicked': '' +            }, +    ], + +    ['Button','doc:parent', +            {   'icon': 'up', +                'tt': _("Go to the general larch documentation index"), +                'clicked': '' +            }, +    ], + +] diff --git a/build_tools/larch8/larch0/gui/layouts/editor.uim b/build_tools/larch8/larch0/gui/layouts/editor.uim new file mode 100644 index 0000000..2338f6e --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/editor.uim @@ -0,0 +1,92 @@ +# editor.uim - The layout for the editor widget +# +# (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.05.21 + +[ +    ['Frame', 'edit:page', +            {   'layout': +                    ['VBOX', +                        ['HBOX', 'edit:header', '*', 'edit:title'], +                        ['HBOX', 'edit:content', +                            ['VBOX', 'edit:copy', 'edit:cut', 'edit:paste', +                                    'edit:undo', 'edit:redo', 'edit:revert', +                                    '*', 'edit:cancel', 'edit:ok' +                            ] +                        ] +                    ] +            } +    ], +    ['Label', 'edit:header', +            {   'markup': ['h2', _("Editor")] +            } +    ], +    ['Label', 'edit:title', {}], +    ['TextEdit', 'edit:content', {}], +    ['Button', 'edit:ok', +            {   'text': _('OK'), +                'clicked': '' +            } +    ], +    ['Button', 'edit:cancel', +            {   'text': _('Cancel'), +                'clicked': '' +            } +    ], +    ['Button', 'edit:revert', +            {   'text': _('Revert'), +                'tt': _('Restore the text to its initial/default state'), +                'clicked': '' +            } +    ], +    ['Button', 'edit:copy', +            {   'text': _('Copy'), +                'clicked': '' +            } +    ], +    ['Button', 'edit:cut', +            {   'text': _('Cut'), +                'clicked': '' +            } +    ], +    ['Button', 'edit:paste', +            {   'text': _('Paste'), +                'clicked': '' +            } +    ], +    ['Button', 'edit:undo', +            {   'text': _('Undo'), +                'clicked': '' +            } +    ], +    ['Button', 'edit:redo', +            {   'text': _('Redo'), +                'clicked': '' +            } +    ], + +    ['DATA', 'editor_data', +            {   'messages': +                    {   'msg_dflt':  _("Editing '%s'") +                    } +            }, +    ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/logger.uim b/build_tools/larch8/larch0/gui/layouts/logger.uim new file mode 100644 index 0000000..0ecb7bf --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/logger.uim @@ -0,0 +1,57 @@ +# logger.uim - The layout for the logging widget +# +# (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.05.22 + +[ +    ['Frame', 'log:page', +            {   'layout': +                    ['VBOX', +                        'log:header', +                        ['HBOX', +                            'log:text', +                            ['VBOX', 'log:clear', '*', 'log:hide'] +                        ] +                    ] +             } +    ], +    ['Label', 'log:header', +            {   'markup': ['', ['h2', _("Low-level Command Logging")], +                        ['p', _("Here you can follow the detailed, low-level" +                        " progress of the commands.")]] +            } +    ], +    ['TextEdit', 'log:text', +            {   'ro': True +            } +    ], +    ['Button', 'log:clear', +            {   'text': _("Clear"), +                'clicked': '' +            } +    ], +    ['Button', 'log:hide', +            {   'text': _("Hide"), +                'tt': _("Go back to the larch controls"), +                'clicked': '' +            } +    ] +] diff --git a/build_tools/larch8/larch0/gui/layouts/page_installation.uim b/build_tools/larch8/larch0/gui/layouts/page_installation.uim new file mode 100644 index 0000000..db86059 --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_installation.uim @@ -0,0 +1,190 @@ +# page_installation.uim - The layout 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.10.10 + +[ +    ['Page', ':page_installation', +            {   'layout': +                    ['VBOX', +                        ['HBOX', +                            ['VBOX', ':editmirrorlist', ':edit_profile'], +                            'VLINE,20', +                            ':pacmanops' +                        ], +                        '*', +                        ':settings_advanced', +                        'HLINE', +                        ['HBOX', '*', ':install'] +                    ] +            } +    ], +    # - - - - The profile editing frame +    ['Frame', ':edit_profile', +            {   'text': _("Edit Profile"), +                'layout': +                    ['VBOX', +                        ':addedpacks', +                        ':vetopacks', +                        ':pacmanconf', +                        ':repos' +                    ] +            } +    ], +    ['Button', ':addedpacks', +            {   'text': _("Edit 'addedpacks'"), +                'tt': _("Edit the list of packages to be installed") +            }, +        'clicked' +    ], +    ['Button', ':vetopacks', +            {   'text': _("Edit 'vetopacks'"), +                'tt': _("Edit the list of packages NOT to install") +            }, +        'clicked' +    ], +    ['Button', ':pacmanconf', +            {   'text': _("Edit pacman.conf options"), +                'tt': _("Edit pacman.conf options - not the repositories") +            }, +        'clicked' +    ], +    ['Button', ':repos', +            {   'text': _("Edit pacman.conf repositories"), +                'tt': _("Edit the repository entries for pacman.conf") +            }, +        'clicked' +    ], + +    # - - - - The installed package tweaking frame +    ['OptionalFrame', ':pacmanops', +            {   'text': _("Tweak Installed Packages"), +                'layout': +                    ['VBOX', ':sync', ':updateall', ':update', ':add', ':remove'] +            } +    ], +    ['Button', ':sync', +            {   'text': _("Synchronize db    [-Sy]"), +                'tt': _("Synchronize the pacman db on the target (pacman -Sy)") +            }, +        'clicked' +    ], +    ['Button', ':updateall', +            {   'text': _("Update all packages    [-Su]"), +                'tt': _("Update all installed packages for which a newer version is available\n" +                        "(you will normally need to synchronize the pacman db first)") +            }, +        'clicked' +    ], +    ['Button', ':update', +            {   'text': _("Update / Add package    [-U]"), +                'tt': _("Update / Add a package from a package file" +                        " using pacman -U") +            }, +        'clicked' +    ], +    ['Button', ':add', +            {   'text': _("Add package(s)    [-S]"), +                'tt': _("Add one or more packages (space separated)" +                        " using pacman -S") +            }, +        'clicked' +    ], +    ['Button', ':remove', +            {   'text': _("Remove package(s)    [-Rs]"), +                'tt': _("Remove one or more packages (space separated)" +                        " using pacman -Rs") +            }, +        'clicked' +    ], + +    # - - - - The advanced installation options frame +    ['OptionalFrame', ':settings_advanced', +            {   'text': _("Advanced Installation Options"), +                'layout': ['HBOX', ':installrepos', 'VLINE,3', ':cache'] +            } +    ], + +    ['OptionalFrame', ':installrepos', +            {   'text': _("Use project repository list"), +                'tt': _("Enables use of an alternative pacman.conf" +                        " for installation only"), +                'layout': +                    ['HBOX', ':editrepolist'] +            }, +        'toggled' +    ], +    ['Button', ':editrepolist', +            {   'text': _("Edit repository list"), +                'tt': _("Edit repository list file used for installation") +            }, +        'clicked' +    ], +    ['Button', ':editmirrorlist', +            {   'text': _("Edit mirror list"), +                'tt': _("Edit the pacman mirror list for the live system\n" +                        "(not for the build process)") +            }, +        'clicked' +    ], + +    ['Frame', ':cache', +            {   'text': _("Package Cache"), +                'layout': +                    ['HBOX', ':cache_show', ':cache_change'] +            } +    ], +    ['LineEdit', ':cache_show', +            {   'ro': True, +                'tt': _("The path to the (host's) package cache") +            } +    ], +    ['Button', ':cache_change', +            {   'text': _("Change"), +                'tt': _("Change the package cache path") +            }, +        'clicked' +    ], + +    ['Button', ':install', +            {   'text': _("Install"), +                'tt': _("This will start the installation to the set path") +            }, +        'clicked' +    ], + +    ['DATA', 'install_page_data', +            {   'messages': +                    {   'edit_pc':  _("Editing pacman.conf options only"), +                        'edit_pr':  _("Editing pacman repositories"), +                        'edit_mli': _("Editing mirror list for installation"), +                        'prompt_ncp': _("Enter new package cache path:"), +                        'edit_pri': _("Editing pacman repositories for installation"), +                        'msg_pu':   _("Package to add/update"), +                        'filter_pu': _("Packages"), +                        'prompt_pi': _("Enter the names of packages to install -" +                                    "\n  separated by spaces:"), +                        'prompt_pr': _("Enter the names of packages to remove -" +                                    "\n  separated by spaces:"), +                    } +            }, +    ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/page_larchify.uim b/build_tools/larch8/larch0/gui/layouts/page_larchify.uim new file mode 100644 index 0000000..34c4e66 --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_larchify.uim @@ -0,0 +1,216 @@ +# page_larchify.uim - The layout for the larchify 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.10 + +(lambda USERHEADERS: +[ +    ['Page', ':page_larchify', +            {   'layout': +                    ['VBOX', +#                        ':larchify', +                        ':users', +                        'HLINE', +                        ['HBOX', +                            ['VBOX', ':overlay', ':locales', ':rcconf'], +                            'VLINE', +                            ['VBOX', ':kernelfile', '*', ':kernelpack'], +                            'VLINE', +                            ':larchify_advanced' +                        ], +                        'HLINE', +                        ['HBOX', ':oldsquash', '*', ':build'] +                    ] +            } +    ], +    # - - - - The profile editing frame +    ['Button', ':locales', +            {   'text': _("Supported locales"), +                'tt': _("Edit the /etc/locale.gen file to select" +                        " supported glibc locales") +            }, +        'clicked' +    ], +    ['Button', ':rcconf', +            {   'text': _("Edit /etc/rc.conf"), +                'tt': _("Edit the general system configuration file for the" +                        " live system") +            }, +        'clicked' +    ], +    ['Button', ':overlay', +            {   'text': _("Edit overlay"), +                'tt': _("Open a file browser on the profile's 'rootoverlay'") +            }, +        'clicked' +    ], + +    # - - - - The kernel selection frame +    ['Frame', ':kernelfile', +            {   'text': _("Live kernel filename"), +                'tt': _("The name of the kernel binary file (in /boot)"), +                'layout': +                    ['HBOX', ':kernele', ':kernelb'] +            } +    ], +    ['LineEdit', ':kernele', +            {   'width': 100, +                'ro': True, +            } +    ], +    ['Button', ':kernelb', +            {   'text': _("Change"), +                'tt': _("Change the name of the kernel binary file (in /boot)") +            }, +        'clicked' +    ], +    ['Frame', ':kernelpack', +            {   'text': _("Live kernel package"), +                'tt': _("The name of the kernel for mkinitcpio (the preset file)"), +                'layout': +                    ['HBOX', ':kernelmkie', ':kernelmkib'] +            } +    ], +    ['LineEdit', ':kernelmkie', +            {   'width': 100, +                'ro': True, +            } +    ], +    ['Button', ':kernelmkib', +            {   'text': _("Change"), +                'tt': _("Change the name of the kernel preset file (for mkinitcpio)") +            }, +        'clicked' +    ], + + +    ['OptionalFrame', ':larchify_advanced', +            {   'text': _("Advanced Options"), +                'layout': +                    ['VBOX', ':initcpio', ':oldlocales', ':ssh'], +            } +    ], +    ['Button', ':initcpio', +            {   'text': _("Edit mkinitcpio.conf"), +                'tt': _("Edit the configuration file for generating" +                        " the initramfs via mkinitcpio") +            }, +        'clicked' +    ], +    ['CheckBox', ':ssh', +            {   'text': _("Generate ssh keys"), +                'tt': _("The ssh host keys will be pre-generated") +            }, +        'toggled' +    ], +    ['CheckBox', ':oldlocales', +            {   'text': _("Reuse existing locales"), +                'tt': _("To save time it may be possible to reuse glibc" +                        " locales from a previous run") +            }, +#        'toggled' +    ], + +    ['CheckBox', ':oldsquash', +            {   'text': _("Reuse existing system.sqf"), +                'tt': _("Reuse existing system.sqf, to save time if the" +                        " base system hasn't changed") +            }, +#        'toggled' +    ], +    ['Button', ':build', +            {   'text': _("Larchify"), +                'tt': _("Build the main components of the larch system") +            }, +        'clicked' +    ], + +#Note that this should be disabled if installation directory is '/' +    ['Frame', ':users', +            {   'text': _("User accounts"), +                'layout': +                    ['VBOX', +                        ':utable', +                        ['HBOX', ':useradd', ':userdel', '*', +                            ':rootpwl', ':rootpwe', ':rootpwb' +                        ] +                    ] +            } +    ], +    ['List', ':utable', +            {   'selectionmode': 'Single', +                'headers': USERHEADERS, +                'compact': True, +                'tt': _("Click on a row to select, click on a selected" +                        " cell to edit") +            }, +#        'select', +        'clicked' +    ], +    ['Button', ':useradd', +            {   'text': _("Add user"), +                'tt': _("Create a new user-name") +            }, +        'clicked' +    ], +    ['Button', ':userdel', +            {   'text': _("Delete user"), +                'tt': _("Remove the selected user-name") +            }, +        'clicked' +    ], +    ['Label', ':rootpwl', +            {   'text': _("Root password:") +            } +    ], +    ['LineEdit', ':rootpwe', +            {   'ro': True, +                'tt': _("The unencrypted root password for the live system") +            } +    ], +    ['Button', ':rootpwb', +            {   'text': _("Change"), +                'tt': _("Enter a new password for the 'root' user") +            }, +        'clicked' +    ], + + +    ['DATA', 'larchify_page_data', +            {   'messages': +                    {   'uheaders': USERHEADERS, +                        'rn_error': _("Renaming failed, see log"), +                        'ud_error': _("Couldn't adjust user definition"), +                        'def_skel': _("Default (/etc/skel)"), +                        'skel_lbl': _("This folder will be copied\n" +                "to build the user's home folder:"), +                        'skel_ttl': _("Choose 'skel' Folder"), +                        'newlogin': _("Enter login-name for new user:"), +                        'newrootpw': _("Enter root password for live system:"), +                        'kernelf':  _("Name of kernel binary:"), +                        'kernelp':  _("Name of kernel mkinitcpio preset:"), +                    } +            }, +    ], +] +)([_("User-Name"), _("Password"), _("Group"), +                        "UID", _("'skel' directory"), +                        _("Additional Groups"), _("Expert options")]) diff --git a/build_tools/larch8/larch0/gui/layouts/page_main.uim b/build_tools/larch8/larch0/gui/layouts/page_main.uim new file mode 100644 index 0000000..7d79c20 --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_main.uim @@ -0,0 +1,99 @@ +# page_main.uim - The layout for the main window +# +# (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.10 +[ +    ['Window', ':larch', +            {   'title': 'larch', 'size': '600_400', +                'icon': 'images/larchicon.png', +                'closesignal': '$$$uiclose$$$', +                'layout': +                    ['VBOX', +                        ['HBOX', +                            ':image', +                            ['VBOX', +                                ['HBOX', ':header', '*'], +                                ['HBOX', ':showlog', ':docs', '*', ':quit'], +                            ] +                        ], +                        ':tabs' +                    ] + +            } +    ], + +    # - Header +    ['Label', ':image', +            {   'image': 'images/larch80.png' +            } +    ], +    ['Label', ':header', +            {   'markup': ['h1', ['color', '#c55500', ['em', 'larch '], +                        _("Live Arch Linux Construction Kit")]] +            } +    ], +    ['Button', ':showlog', +            {   'text': _("View Log"), +                'tt': _("This button switches to the log viewer"), +            }, +        'clicked' +    ], +    ['Button', ':docs', +            {   'text': _("Help"), +                'tt': _("This button switches to the documentation viewer"), +            }, +        'clicked' +    ], +    ['Button', ':quit', +            {   'text': _("Quit"), +                'tt': _("Stop the current action and quit the program"), +                'clicked': '$$$uiquit$$$' +            }, +    ], + +#TODO +    # - Main widget +    ['Stack', ':tabs', +            {   'pages': [':notebook', 'progress:page', 'log:page', +                        'doc:page', 'edit:page'] +            } +    ], + +    # - - The main page of the Stack +    ['Notebook', ':notebook', +            {   'tabs': [ +                            [':page_settings', _("Project Settings")], +                            [':page_installation', _("Installation")], +                            [':page_larchify', _("Larchify")], +                            [':page_medium', _("Make Medium")], +                        ] +            }, +        'changed' +    ], + +    ['DATA', 'main_page_data', +            {   'messages': +                    {   'authfail':  _("Authentication failure"), +                        'getpw': _("Enter the password to run as administrator:"), +                    } +            }, +    ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/page_medium.uim b/build_tools/larch8/larch0/gui/layouts/page_medium.uim new file mode 100644 index 0000000..8f081bb --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_medium.uim @@ -0,0 +1,247 @@ +# page_medium.uim - The layout for the medium building 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.24 + +(lambda MEDIA, SRC: +[ +    ['Page', ':page_medium', +            {   'layout': +                    ['VBOX', '*', +                        ['HBOX', ':mtlabel', 'VLINE', ':srcframe', 'VLINE', ':vlabel',], +                        '*', 'HLINE', '*', +                        ['HBOX', ':mediumprofile', 'VLINE', ':mediumopts'], +                        'HLINE', +                        ['HBOX', '*', ':make_medium'] +                    ] +            } +    ], + +    ['Frame', ':srcframe', +            {   'text': _("Source of the larch system"), +                'tt': _("Normally the larchified Arch install,\n" +                        "but it could also be an existing larch medium"), +                'layout': +                    ['VBOX', ['HBOX', ':srctype', ':selectsrc'], ':srclocation'] +            }, +    ], +    ['ComboBox', ':srctype', +            {   'width': 180, +                'set': [m[1] for m in SRC], +                'tt': _("Select where the system to put on the medium comes from") +            }, +        'changed' +    ], +    ['LineEdit', ':srclocation', +            {   'ro': True, +                #'width': 250, +                'tt': _("The larch data comes from here") +            } +    ], +    ['Button', ':selectsrc', +            {   'text': _("Choose"), +                'tt': _("Select the source of the larch system") +            }, +        'clicked' +    ], + +    ['Frame', ':mtlabel', +            {   'text': _("Type of medium to build"), +                'tt': _("A writeable medium could be a USB-stick or a hard drive partition, for example"), +                'layout': +                    ['VBOX', ['HBOX', ':mediumtype', ':selectpart'], ':larchpart'] +            }, +    ], +    ['ComboBox', ':mediumtype', +            {   'width': 180, +                'set': [m[1] for m in MEDIA], +                'tt': _("Select the larch source.") +            }, +        'changed' +    ], + +    ['Frame', ':mediumprofile', +            {   'text': _("Medium profile"), +                'tt': _("Settings which are saved in the profile"), +                'layout': +                    ['VBOX', ':bootlines', ':syslinuxtemplate', ':cdroot'] +            }, +    ], +    ['Button', ':bootlines', +            {   'text': _("Edit boot entries"), +                'tt': _("Edit the file determining the boot entries") +            }, +        'clicked' +    ], +    ['Button', ':syslinuxtemplate', +            {   'text': _("Edit bootloader template"), +                'tt': _("Edit the syslinux/extlinux/isolinux configuration file\n" +                        "(the larch boot entries are handled separately)") +            }, +        'clicked' +    ], +    ['Button', ':cdroot', +            {   'text': _("Edit medium files"), +                'tt': _("Open a file browser on the profile's 'cd-root' folder") +            }, +        'clicked' +    ], + +#++++ +    ['LineEdit', ':larchpart', +            {   'ro': True, +                #'width': 250, +                'tt': _("The partition or iso-file to receive the larch system") +            } +    ], +    ['Button', ':selectpart', +            {   'text': _("Choose"), +                'tt': _("Select the destination medium or file") +            }, +        'clicked' +    ], + +    ['Frame', ':mediumopts', +            {   'text': _("Medium options"), +                'tt': _("Optional settings for larch partitions"), +                'layout': +                    ['VBOX', +                        ['GRID',    ['+', ':detection_l', ':detection', '-'], +                                    ['+', ':persist',     'VLINE,3', '*'], +                                    ['+', ':ovl_journal', '|',      ':nombr'], +                                    ['+', ':pformat',     '|',      ':nolarchboot'], +                        ], +                    ], +            }, +        'toggled' +    ], +    ['CheckBox', ':pformat', +            {   'text': _("Format medium"), +                'tt': _("Normally the medium should be formatted before writing, but IF YOU\n" +                        "KNOW WHAT YOU ARE DOING it might be useful to skip the formatting") +            }, +        'toggled' +    ], +    ['CheckBox', ':nombr', +            {   'text': _("Don't set MBR"), +                'tt': _("The bootloader will be installed to the partition only," +                        " leaving the mbr untouched\n" +                        "(you'll need to provide some other means of booting)") +            }, +#        'toggled' +    ], +    ['CheckBox', ':nolarchboot', +            {   'text': _("Not bootable via search"), +                'tt': _("Don't create the file 'larch/larchboot':\n" +                        " the medium will only be bootable by uuid, label" +                        " or partition name") +            }, +        'toggled' +    ], +    ['CheckBox', ':persist', +            {   'text': _("Data persistence"), +                'tt': _("Support data persistence by using the medium as a writeable overlay"), +            }, +        'toggled' +    ], +    ['CheckBox', ':ovl_journal', +            {   'text': _("Use journalling"), +                'tt': _("The file-system containing the overlay can use journalling\n" +                        "to aid data integrity") +            }, +        'toggled' +    ], + +    ['Label', ':detection_l', +            {   'text': _("Medium Detection:") +            } +    ], +    ['ComboBox', ':detection', +            {   'tt': _("Choose how the boot scripts determine where to" +                        " look for the larch system"), +            }, +        'changed' +    ], +#---- + +#+ +    ['Frame', ':vlabel', +            {   'text': _("Volume Label:"), +                'tt': _("The 'label' given to the created medium"), +                'layout': ['VBOX', ':vlabele', ':vlabelb'] +            } +    ], +    ['LineEdit', ':vlabele', +            {   'ro': True, +                'width': 120, +                'tt': _("The length may not exceed 16 bytes," +                        " 11 for vfat") +            } +    ], +    ['Button', ':vlabelb', +            {   'text': _("Change"), +                'tt': _("Enter a new label for the volume, empty to use default") +            }, +        'clicked' +    ], +#- + +    ['Button', ':make_medium', +            {   'text': _("Write the larch medium"), +                'tt': _("The larch image will be written to the 'iso' file" +                        " or to the partition, as selected") +            }, +        'clicked' +    ], + +    ['DATA', 'medium_page_data', +            {   'messages': +                    {   'parts_t':      _("Choose unmounted partition"), +                        'parts_src':    _("Select larch source partition"), +                        'parts_dst':    _("Device to receive larch system\n" +                                "WARNING: Be very careful in choosing here,\n" +                                "if you choose the wrong one you might\n" +                                "seriously damage your system!"), +                        'msg_med':      _("Invalid larch medium folder: %s"), +                        'prompt_label': _("Volume label (clear to use default):"), +                        'isopath':      _("Save 'iso' to ..."), +                        'isoget':       _("Read 'iso' file"), +                        'media':        [m[0] for m in MEDIA], +                        'sources':      [m[0] for m in SRC], +                        'detectionmodes': +                            {   'label':   "LABEL", +                                'uuid':    "UUID", +                                'device':  _("Device (e.g. /dev/sdb1)"), +                                'search':  _("Search (for 'larchboot' file)") +                            }, +                    } +            }, +    ], +] +)(  [   ('medium-w', _("writeable medium")), +        ('medium-iso', "iso file"), +        ('medium-boot', _("boot iso")), +    ], +    [   ('larchified', _("larchify output")), +        ('device',  _("other medium")), +        ('isofile', _("iso file")), +    ], +) diff --git a/build_tools/larch8/larch0/gui/layouts/page_project.uim b/build_tools/larch8/larch0/gui/layouts/page_project.uim new file mode 100644 index 0000000..d48295e --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_project.uim @@ -0,0 +1,188 @@ +# page_project.uim - The layout 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 + +[ +    ['Page', ':page_settings', +            {   'layout': +                    ['VBOX', +                        ':settings_profile', +                        '*', +                        ':options_advanced', +                        '*' +                    ] +            } +    ], +    # - - - - The profile selection frame +    ['Frame', ':settings_profile', +            {   'text': _("Profile"), +                'layout': +                    ['VBOX', +                        ['HBOX', ':choose_profile', ':choose_profile_combo', +                                '*', ':profile_browse'], +                        ['HBOX', ':profile_rename', ':profile_clone', +                                ':profile_delete', ':profile_save'] +                    ] +            } +    ], +    ['Label', ':choose_profile', +            {   'text': _("Select:"), +                'align': 'right' +            } +    ], +    ['ComboBox', ':choose_profile_combo', +            {   'width': 200, +                'tt': _("Choose a profile from those already in your" +                        " larch working folder") +            }, +            'changed' +    ], +    ['Button', ':profile_browse', +            {   'text': _("Browse for Profile"), +                'tt': _("Fetch a profile from the file-system") +            }, +        'clicked' +    ], +    ['Button', ':profile_clone', +            {   'text': _("Clone"), +                'tt': _("Make a copy of the current profile under a new name") +            }, +        'clicked' +    ], +    ['Button', ':profile_rename', +            {   'text': _("Rename"), +                'tt': _("Rename the current profile") +            }, +        'clicked' +    ], +    ['Button', ':profile_delete', +            {   'text': _("Delete"), +                'tt': _("Delete an unused profile") +            }, +        'clicked' +    ], +    ['Button', ':profile_save', +            {   'text': _("Copy to ..."), +                'tt': _("Copy the current profile to somehere else") +            }, +        'clicked' +    ], + +    # - - - - Advanced project options +    ['OptionalFrame', ':options_advanced', +            {   'text': _("Advanced Project Options"), +                'layout': +                    ['HBOX', +                        #['HBOX', '*', ':lplat', ':platform'], +                        ['GRID', +                            ['+', ':choose_project', ':choose_project_combo'], +                            ['+', ':new_project', ':project_delete'] +                        ], +                        'VLINE,3', +                        ':installation_path' +                    ] +            } +    ], +# Pending better support in Arch/pacman +#    ['Label', '::lplat', +#            {   'text': _("Platform (processor architecture):") +#            } +#    ], +#    ['ComboBox', ':platform', +#            {   'tt': _("Which processor architecture?") +#            }, +#            'changed' +#    ], +    ['Label', ':choose_project', +            {   'text': _("Choose Existing Project:") +            } +    ], +    ['ComboBox', ':choose_project_combo', +            {   'tt': _("Choose a project from those already defined"), +                'width': 120 +            }, +            'changed' +    ], +    ['Button', ':new_project', +            {   'text': _("New Project"), +                'tt': _("Create a new project") +            }, +        'clicked' +    ], +    ['Button', ':project_delete', +            {   'text': _("Delete"), +                'tt': _("Delete a project") +            }, +        'clicked' +    ], +    ['Frame', ':installation_path', +            {   'text': _("Installation Path"), +                'layout': +                    ['HBOX', ':installation_path_show', +                            ':installation_path_change'] +            } +    ], +    ['LineEdit', ':installation_path_show', +            {   'ro': True, +                'tt': _("The root directory of the Arch installation" +                        " to larchify") +            } +    ], +    ['Button', ':installation_path_change', +            {   'text': _("Change"), +                'tt': _("Change the root directory of the Arch installation") +            }, +        'clicked' +    ], + +    ['DATA', 'project_page_data', +            {   'messages': +                    {   'file_ps':  _("Select profile source folder"), +                        'prompt_pr': _("Destination profile exists - replace it?"), +                        'prompt_pn': _("Enter new name for current profile:"), +                        'prompt_pe': _("Profile '%s' exists already"), +                        'msg_pu':   _("Can't rename the profile," +                                    " it is in use by other projects"), +                        'file_sp':  _("Save profile folder"), +                        'prompt_dr': _("Destination exists - replace it?"), +                        'prompt_dp': _("Select the profile for deletion"), +                        'delprof': _("Remove Profile"), +                        'msg_npf':   _("There are no profiles which can" +                                    " be deleted - all are in use"), +                        'msg_dpff':   _("Couldn't delete profile '%s' -" +                                    " check permissions"), +                        'prompt_ip': _("An empty path here will reset to the default.\n" +                                "  WARNING: Double check your path -\n" +                                "  If you make a mistake here it could destroy your system!" +                                "\n\nEnter new installation path:"), +                        'prompt_np': _("Enter name for new project:"), +                        'msg_pe':   _("Project '%s' already exists"), +                        'prompt_pd': _("Select the project for deletion"), +                        'delproj': _("Remove Project"), +                        'msg_np':   _("There are no projects which can be deleted"), +                        'msg_npd': _("'%s' is not a profile folder"), +                        'msg_piu': _("The path '%s' is already in use, not saving"), +                        'prompt_clone': _("Enter the name for the new profile:"), +                    } +            }, +    ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/profile_browse.uim b/build_tools/larch8/larch0/gui/layouts/profile_browse.uim new file mode 100644 index 0000000..1d5912b --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/profile_browse.uim @@ -0,0 +1,94 @@ +# profile_browse.uim - The layout for the profile browser widget +# +# (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 + +[ +    ['Dialog', 'dialog:profile_browser', +            {   'title': None, +                'layout': +                    ['VBOX', +                        'dpb:label_f', '*,5', +                        ['HBOX', 'dpb:browse',  'VLINE,10', 'dpb:examples'], +                        ['HBOX', 'dpb:source_l', 'dpb:source'], +                        ['HBOX', 'dpb:name_l', 'dpb:name', 'dpb:name_s'], +                        'HLINE', +                        'dpb:dbuttons' +                    ], +            } +    ], +    ['Frame', 'dpb:label_f', +            {   'layout':   ['VBOX', 'dpb:label'], +            } +    ], +    ['Label', 'dpb:label', +            {   'text': _("You can browse the file-system for the source profile" +                    " directory,\nor else choose one of the examples"), +            } +    ], +    ['Frame', 'dpb:examples', +            {   'text':     _("Example Profiles"), +                'tt':       _("Here you can select one of the example profiles to use as a starting point"), +                'layout':   ['VBOX', 'dpb:example_list'], +            } +    ], +    ['ComboBox', 'dpb:example_list', +            { +            }, +        'changed' + +    ], +    ['Button', 'dpb:browse', +            {   'text': _("Browse file-system"), +                'tt': _("Open a file dialog to search for the profile"), +            }, +        'clicked' +    ], +    ['Label', 'dpb:source_l', +            {   'text': _("Source:"), +            } +    ], +    ['LineEdit', 'dpb:source', +            {   'ro': True, +                'tt': _("The path from which the profile directory will be copied"), +            } +    ], +    ['Label', 'dpb:name_l', +            {   'text': _("New name:") +            } +    ], +    ['LineEdit', 'dpb:name', +            {   'ro': True, +                'tt': _("The name the profile will be given in the work area"), +            } +    ], +    ['Button', 'dpb:name_s', +            {   'text': _("Change"), +                'tt': _("Open a dialog to change the new profile's name"), +            }, +        'clicked' +    ], +    ['DialogButtons', 'dpb:dbuttons', +            {   'buttons':   ('Ok', 'Cancel'), +                'dialog':   'dialog:profile_browser', +            }, +    ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/progress.uim b/build_tools/larch8/larch0/gui/layouts/progress.uim new file mode 100644 index 0000000..62ae9e2 --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/progress.uim @@ -0,0 +1,64 @@ +# progress.uim - The layout for the progress widget +# +# (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.05.22 + +[ +    ['Frame', 'progress:page', +            {   'layout': +                    ['VBOX', +                        'progress:header', +                        ['HBOX', +                            'progress:text', +                            ['VBOX', 'progress:cancel', '*', 'progress:done'] +                        ], +                        'progress:progress' +                    ] +            } +    ], +    ['Label', 'progress:header', +            {   'markup': ['', ['h2', _("Processing ...")], +                        ['p', _("Here you can follow the detailed, low-level" +                        " progress of the commands.")]] +            } +    ], +    ['TextEdit', 'progress:text', +            {   'ro': True +            } +    ], +    ['LineEdit', 'progress:progress', +            {   'ro': True, +                'tt': _("An indication of the progress of the current" +                        " operation, if possible") +            } +    ], +    ['Button', 'progress:cancel', +            {   'text': _("Cancel"), +                'tt': _("Stop the current action"), +                'clicked': '$$$cancel$$$' +            } +    ], +    ['Button', 'progress:done', +            {   'text': _("Done"), +                'clicked': '' +            } +    ], +] diff --git a/build_tools/larch8/larch0/gui/project.py b/build_tools/larch8/larch0/gui/project.py new file mode 100644 index 0000000..6dc0000 --- /dev/null +++ b/build_tools/larch8/larch0/gui/project.py @@ -0,0 +1,446 @@ +# project.py - Project management +# +# (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.24 + +from config import * +import os, shutil, pwd +from glob import glob +from subprocess import call +from userinfo import Userinfo + +CONFIG_DIR = '.config/larch'        # within the user's home directory +APP_CONF = 'app.conf'               # within larch config directory +PROJECT_CONF = 'project.conf'       # within project directory +PROJECT0 = 'larch-0'                # default project +PROFILE0 = 'default'                # default profile + +# Some default values for the project config file +DEFAULTS = {    'installation_dir' : '', +                'pacman_cache'  : '/var/cache/pacman/pkg', +                'profile'       : PROFILE0, +                'profile_browse_dir': '',   # => use default +                'installrepo'   : '', +                'medium_iso'    : '',       # 'Yes' | '' +                'medium_search' : 'label',  # 'search' | 'uuid' | 'label' | 'device' +                'boot_nosearch' : '',       # 'Yes' | '' +                'do_persist'    : 'Yes',    # 'Yes' | '' +                'journal'       : 'Yes',    # 'Yes' | '' +                'medium_label'  : '',       # => fetch default +                'isosavedir'    : '', +                'isofile'       : '', +                'bootisofile'   : '', +                'bootisolabel'  : '', +                'do_format'     : 'Yes',    # 'Yes' | '' +    } + + +# Default values for the application config file +APP_DEFAULTS = { +                'project'       : PROJECT0, +                'filebrowser'   : 'xdg-open $', +    } + + + +class ProjectManager: +    def __init__(self): +        add_exports(    ( +                ('getitem', self.getitem), +                ('getbool', self.getbool), +                ('setitem', self.setitem), +                ('setbool', self.setbool), +                ('get_projects', self.list_projects), +                ('get_profiles', self.list_profiles), +                ('get_installation_dir', self.get_ipath), +                ('set_installation_dir', self.set_ipath), +                ('testlarchify', self.testlarchify), +                ('set_project', self.set_projectname), +                ('get_project', self.get_projectname), +                ('delete_project', self.delete_project), +                ('delete_profile', self.delete_profile), +                ('list_free_projects', self.list_free_projects), +                ('list_free_profiles', self.list_free_profiles), +                ('get_new_profile', self.get_new_profile), +                ('rename_profile', self.rename_profile), +                ('can_rename_profile', self.can_rename_profile), +                ('save_profile', self.save_profile), +                ('get_profile', self.get_profile), +                ('set_profile', self.set_profile), +                ('get_example_profiles', self.get_example_profiles), +                ('set_profile_browse_dir', self.set_profile_browse_dir), +                ('get_mediumlabel', self.get_mediumlabel), +                ('set_mediumlabel', self.set_mediumlabel), +                ('getisosavedir', self.getisosavedir), +                ('getisofile', self.getisofile), +                ('getbootisofile', self.getbootisofile), +                ('get_bootisolabel', self.get_bootisolabel), +                ('set_bootisolabel', self.set_bootisolabel), +                ('newUserinfo', self.newUserinfo), +                ('allusers', self.allusers), +                ('getuserinfo', self.getuserinfo), +                ('newuser', self.newuser), +                ('userset', self.userset), +                ('deluser', self.deluser), +                ('listskels', self.listskels), +                ('saveusers', self.saveusers)) +            ) + + +    def init(self): +        self.projects_base = os.path.join(os.environ['HOME'], CONFIG_DIR) +        self.profiles_dir = os.path.join(self.projects_base, 'myprofiles') +        # Ensure the presence of the larch default project folder +        dpf = '%s/p_%s' % (self.projects_base, PROJECT0) +        if not os.path.isdir(dpf): +            os.makedirs(dpf) +        # Ensure the presence of the profiles folder and the 'default' profile +        if not os.path.isdir(self.profiles_dir): +            os.mkdir(self.profiles_dir) +        self.default_profile_dir = os.path.join(self.profiles_dir, PROFILE0) +        if not os.path.isdir(self.default_profile_dir): +            call(['cp', '-a', base_dir + '/profiles/'+ PROFILE0, +                    self.profiles_dir]) + +        # The application configs +        self.aconfig_file = os.path.join(self.projects_base, APP_CONF) +        self.aconfig = self.getconfig(self.aconfig_file) + +        # The project-specific configs +        self.set_projectname(self.appget('project')) + + +    def get_projectname(self): +        return (True, self.project_name) + +    def set_projectname(self, name): +        self.project_dir = os.path.join(self.projects_base, 'p_' + name) +        plist = self.list_projects()[1] +        if name not in plist: +            os.mkdir(self.project_dir) + +        self.pconfig_file = os.path.join(self.project_dir, PROJECT_CONF) +        self.pconfig = self.getconfig(self.pconfig_file) +        self.profile_name = self.get('profile') + +        self.profile_path = os.path.join(self.profiles_dir, self.profile_name) +        self.appset('project', name) +        self.project_name = name +        return (True, None) + +    def delete_project(self, name): +        # This should probably be run as root, in case the build directory +        # is inside it ... cross that bridge when we come to it! +        r = call(['rm', '-r', '--interactive=never', +                os.path.join(self.projects_base, 'p_' + name)]) +        return (True, r == 0) + + +    def delete_profile(self, name): +        r = call(['rm', '-r', '--interactive=never', +                os.path.join(self.profiles_dir, name)]) +        return (True, r == 0) + + +    def get_profile(self): +        return (True, self.profile_name) + +    def set_profile(self, name): +        self.set('profile', name) +        self.profile_name = name +        self.profile_path = os.path.join(self.profiles_dir, self.profile_name) +        return (True, None) + + +    def rename_profile(self, name): +        os.rename(self.profile_path, os.path.join(self.profiles_dir, name)) +        self.set_profile(name) +        return (True, None) + + +    def get_new_profile(self, src, name): +        if not os.path.isfile(src + '/addedpacks'): +            return (True, False) +        dst = os.path.join(self.profiles_dir, name) +        call(['rm', '-rf', dst]) +        shutil.copytree(src, dst, symlinks=True) +        self.set_profile(name) +        return (True, True) + + +    def get_example_profiles(self): +        pd = base_dir + '/profiles' +        return (True, (pd, os.listdir(pd))) + +# location of working profiles +#        self.projects_base + '/myprofiles', + + +# What about not allowing changes to the default profile? +# That would mean also no renaming? +# One would have to copy a profile into the project before going +# any further ... +# Is it right to share profiles between projects? (Probably) + +#+++++++++++++++++++++++++++++++++++++++++++++++ +### A very simple configuration file handler +    def getconfig(self, filepath): +        cfg = {} +        if os.path.isfile(filepath): +            fh = open(filepath) +            for line in fh: +                ls = line.split('=', 1) +                if len(ls) > 1: +                    cfg[ls[0].strip()] = ls[1].strip() +        return cfg + + +    def saveconfig(self, filepath, config): +        fh = open(filepath, 'w') +        ci = config.items() +        ci.sort() +        for kv in ci: +            fh.write('%s = %s\n' % kv) +        fh.close() +### +#----------------------------------------------- + +    def list_projects(self): +        projects = [p[2:] for p in os.listdir(self.projects_base) +                if p.startswith('p_')] +        projects.sort() +        return (True, projects) + + +    def list_free_projects(self): +        """This returns a list of projects which are free for (e.g.) deletion. +        """ +        plist = self.list_projects()[1] +        plist.remove(PROJECT0)          # this one is not 'free' +        if self.project_name in plist: +            plist.remove(self.project_name) +        return (True, plist) + + +    def list_profiles(self): +        profiles = [d for d in os.listdir(self.profiles_dir) if +                os.path.isfile(os.path.join(self.profiles_dir, d, 'addedpacks'))] +        profiles.sort() +        return (True, profiles) + + +    def list_free_profiles(self): +        """This returns a list of profiles which are not in use by any project. +        """ +        plist = self.list_profiles()[1] +        plist.remove(PROFILE0)          # this one is not 'free' +        for project in self.list_projects()[1]: +            cfg = self.getconfig(os.path.join(self.projects_base, +                    'p_' + project, PROJECT_CONF)) +            p = cfg.get('profile') +            if p in plist: +                plist.remove(p) +        return (True, plist) + + +    def can_rename_profile(self): +        if self.profile_name == PROFILE0: +            return (True, False) +        for project in self.list_projects()[1]: +            if project != self.project_name: +                cfg = self.getconfig(os.path.join(self.projects_base, +                        'p_' + project, PROJECT_CONF)) +                if self.profile_name == cfg.get('profile'): +                    return (True, False) +        return (True, True) + + +    def save_profile(self, path, force): +        if path[0] != '/': +            # cloning, only the profile name is passed +            path = os.path.join(self.profiles_dir, path) +        else: +            # copying, the destination will have the same name +            if os.path.basename(path) != self.profile_name: +                path = os.path.join(path, self.profile_name) +        if os.path.exists(path): +            if force: +                call(['rm', '-rf', path]) +            elif os.path.isfile(os.path.join(path, 'addedpacks')): +                # This is an existing profile +                return (True, False) +            else: +                # This location is otherwise in use +                return (True, None) +        shutil.copytree(self.profile_path, path, symlinks=True) +        return (True, True) + + +    def set_profile_browse_dir(self, path): +        if os.path.isfile(os.path.join(path, 'addedpacks')): +            # Don't set the profile browse path to a profile directory, +            # but rather tp the containing directory +            path = os.path.dirname(path) +        self.set('profile_browse_dir', path) + + +    def appget(self, item): +        """Read an entry in the application configuration. +        """ +        v = self.aconfig.get(item) +        if v: +            return v +        elif APP_DEFAULTS.has_key(item): +            return APP_DEFAULTS[item] +        debug("Unknown application configuration option: %s" % item) +        assert False + +    def appset(self, item, value): +        """Set an entry in the application configuration. +        """ +        self.aconfig[item] = value.strip() +        self.saveconfig(self.aconfig_file, self.aconfig) + + +    def getitem(self, item): +        return (True, self.get(item)) + +    def getbool(self, item): +        return (True, self.get(item) == 'Yes') + +    def setitem(self, item, value): +        self.set(item, value) +        return (True, None) + +    def setbool(self, item, on): +        self.set(item, 'Yes' if on else 'No') +        return (True, None) + + +    def get(self, item): +        """Read an entry in the project configuration. +        """ +        v = self.pconfig.get(item) +        if v: +            return v +        elif DEFAULTS.has_key(item): +            return DEFAULTS[item] +        debug("Unknown configuration option: %s" % item) +        assert False + +    def set(self, item, value): +        """Set an entry in the project configuration. +        """ +        self.pconfig[item] = value.strip() +        self.saveconfig(self.pconfig_file, self.pconfig) +#        return True + + +    def get_ipath(self): +        ip = self.get('installation_dir') +        if not ip: +            ip = self.set_ipath('')[1] +        return (True, ip) + +    def set_ipath(self, path): +        path = path.strip() +        if path: +            path = '/' + path.strip('/') +        else: +            path = os.environ['HOME'] + '/larch_build' +        self.set('installation_dir', path) +        return (True, path) + + +    def testlarchify(self): +        ipath = self.get_ipath()[1] +        path = ipath + CHROOT_DIR_MEDIUM +        bl = [] +        nosave = False +        ok = (os.path.isfile(path + '/larch/system.sqf') +                and os.path.isfile(ipath + SYSLINUXDIR + '/isolinux.bin')) +        return (True, (path, ok)) + + +    def newUserinfo(self): +        self.userInfo = Userinfo(self.profile_path) +        return (True, None) + +    def allusers(self): +        return (True, self.userInfo.allusers()) + +    def getuserinfo(self, user, fields): +        return (True, self.userInfo.userinfo(user, fields)) + +    def newuser(self, user): +        return (True, self.userInfo.newuser(user)) + +    def saveusers(self): +        return (True, self.userInfo.saveusers()) + +    def userset(self, uname, field, text): +        self.userInfo.userset(uname, field, text) +        return (True, None) + +    def deluser(self, user): +        return (True, self.userInfo.deluser(user)) + +    def listskels(self): +        return (True, glob(self.profile_path + '/skel_*')) + + +    def get_mediumlabel(self): +        l = self.get('medium_label') +        return (True, l if l else LABEL) + +    def set_mediumlabel(self, l): +        if len(l) > 16: +            l = l[:16] +        self.set('medium_label', l) +        return self.get_mediumlabel() + +    def set_bootisolabel(self, l): +        if len(l) > 32: +            l = l[:32] +        self.set('bootisolabel', l) +        return self.get_mediumlabel() + + +    def getisosavedir(self): +        d = self.get('isosavedir') +        return (True, d if d else os.environ['HOME']) + +    def getisofile(self): +        f = self.get('isofile') +        return (True, f if f else ISOFILE) + +    def getbootisofile(self): +        f = self.get('bootisofile') +        return (True, f if f else BOOTISO) + +    def get_bootisolabel(self): +        l = self.get('bootisolabel') +        return (True, l if l else BOOTISOLABEL) + + + +import __builtin__ +__builtin__.project_manager = ProjectManager()  | 
