summaryrefslogtreecommitdiffstats
path: root/build_tools/larch8/larch0/gui
diff options
context:
space:
mode:
authorJames Meyer <james.meyer@operamail.com>2010-12-02 22:37:23 (GMT)
committerJames Meyer <james.meyer@operamail.com>2010-12-02 22:37:34 (GMT)
commit8b94d7f39c71234712bead363526a0283efeb9fa (patch)
tree23f1dbd6458dc39a2c1b08bcdd4cbf768a60d84d /build_tools/larch8/larch0/gui
parent338af567e74d08cbd357079941208e494463d61e (diff)
downloadlinhes_dev-8b94d7f39c71234712bead363526a0283efeb9fa.zip
larch8: first checkin, still needs some work
Diffstat (limited to 'build_tools/larch8/larch0/gui')
-rw-r--r--build_tools/larch8/larch0/gui/controller.py314
-rw-r--r--build_tools/larch8/larch0/gui/front/docviewer.py68
-rw-r--r--build_tools/larch8/larch0/gui/front/editor.py115
-rw-r--r--build_tools/larch8/larch0/gui/front/logview.py94
-rw-r--r--build_tools/larch8/larch0/gui/front/mainwindow.py205
-rw-r--r--build_tools/larch8/larch0/gui/front/page_installation.py193
-rw-r--r--build_tools/larch8/larch0/gui/front/page_larchify.py296
-rw-r--r--build_tools/larch8/larch0/gui/front/page_medium.py441
-rw-r--r--build_tools/larch8/larch0/gui/front/page_project.py241
-rwxr-xr-xbuild_tools/larch8/larch0/gui/larch.py66
-rw-r--r--build_tools/larch8/larch0/gui/layouts/docviewer.uim72
-rw-r--r--build_tools/larch8/larch0/gui/layouts/editor.uim92
-rw-r--r--build_tools/larch8/larch0/gui/layouts/logger.uim57
-rw-r--r--build_tools/larch8/larch0/gui/layouts/page_installation.uim190
-rw-r--r--build_tools/larch8/larch0/gui/layouts/page_larchify.uim216
-rw-r--r--build_tools/larch8/larch0/gui/layouts/page_main.uim99
-rw-r--r--build_tools/larch8/larch0/gui/layouts/page_medium.uim247
-rw-r--r--build_tools/larch8/larch0/gui/layouts/page_project.uim188
-rw-r--r--build_tools/larch8/larch0/gui/layouts/profile_browse.uim94
-rw-r--r--build_tools/larch8/larch0/gui/layouts/progress.uim64
-rw-r--r--build_tools/larch8/larch0/gui/project.py446
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()