diff options
Diffstat (limited to 'build_tools/larch8/larch0/gui')
21 files changed, 3798 insertions, 0 deletions
diff --git a/build_tools/larch8/larch0/gui/controller.py b/build_tools/larch8/larch0/gui/controller.py new file mode 100644 index 0000000..493d9c4 --- /dev/null +++ b/build_tools/larch8/larch0/gui/controller.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python2 +# +# controller.py - Manages file-system access and calling of larch scripts +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +#2010.10.25 + +import sys, os, __builtin__ +from liblarch.rootrun import init_rootrun +import threading +from glob import glob +from subprocess import call +try: + import json as serialize +except: + import simplejson as serialize + +from config import * + +from liblarch.translation import i18n_module, lang, i18nurl +__builtin__._ = i18n_module(base_dir, 'larch') +__builtin__.lang = lang + + +exports = {} +def add_exports(elist): + for key, method in elist: + exports[key] = method + +__builtin__.add_exports = add_exports + + +def error0(message): + sys.stderr.write('>>ERROR>>' + message + '\n') + sys.stderr.flush() +__builtin__.error0 = error0 + + +class Fs: + """Collect file system access methods in one class. + """ + def __init__(self): + add_exports( ( + ('fetch_layout', self.fetch_layout), + ('isfile', self.isfile), + ('isdir', self.isdir), + ('rm_rf', self.rm_rf), + ('get_partitions', self.get_partitions), + ('readfile', self.readfile), + ('savefile', self.savefile), + ('get_docs_url', self.get_docs_url), + ('oldsqf', self.oldsqf), + ('oldlocales', self.oldlocales), + ('browse', self.browse)) + ) + + def fetch_layout(self, layout_file): + fh = open(base_dir + '/gui/layouts/' + layout_file) + r = fh.read() + fh.close() + return (True, eval(r)) + + def rm_rf(self, path): + call(['rm', '-rf', self._getpath(path)]) + return (True, None) + + def oldsqf(self): + return (True, + os.path.isfile(self._getpath('install:' + CHROOT_SYSTEMSQF)) + or os.path.isfile(self._getpath('install:' + CHROOT_DIR_MEDIUM + + '/larch/system.sqf'))) + + def oldlocales(self): + return (True, os.path.isdir(self._getpath('install:%s/locale' + % CHROOT_DIR_BUILD))) + + def isfile(self, path): + return (True, os.path.isfile(self._getpath(path))) + + def isdir(self, path): + return (True, os.path.isdir(self._getpath(path))) + + + def browse(self, path): + fpath = self._getpath(path) + if call(['mkdir', '-p', fpath]) == 0: + # Start file browser at fpath + call(project_manager.appget('filebrowser').replace('$', fpath) + + ' &', shell=True) + return (True, None) + else: + return (False, None) + + def readfile(self, f): + f = self._getpath(f) + try: + fh = open(f) + r = fh.read() + fh.close() + return (True, r) + except: + return (False, _("Couldn't read file '%s'") % f) + + def savefile(self, f, d): + f = self._getpath(f) + dir = os.path.dirname(f) + if not os.path.isdir(dir): + os.makedirs(dir) + try: + fh = open(f, "w") + fh.write(d) + fh.close() + return (True, None) + except: + return (False, _("Couldn't save file '%s'") % f) + + def _getpath(self, f): + if f[0] != "/": + base, f = f.split(':') + f = '/' + f + if base == 'base': + f = base_dir + f + elif base == 'profile': + f = project_manager.profile_path + f + elif base == 'working': + f = project_manager.project_dir + f + else: + f = project_manager.get_ipath()[1] + f + return f + + def get_docs_url(self, page): + return (True, i18nurl(base_dir + '/docs/html/' + page)) + + + def get_partitions(self): + """Get a list of available (not too small) partitions. + """ + partlist = [] + with open('/proc/partitions') as fh: + for l in fh: + fields = l.split() + if len(fields) == 4: + dev = fields[3] + if dev.startswith('sd') and (dev[-1] in '0123456789'): + size = (int(fields[2]) + 512) / 1024 + if (size > 200): + # Keep a tuple (partition, size in MiB) + partlist.append(('/dev/' + dev, size)) + return (True, partlist) + + + +class LarchScripts: + """This class deals with calling the larch scripts. + As they must be run as root, the rootrun module is used, which uses + pexpect to start the subprocesses with su or sudo. + Callbacks are used to fetch the password and return output lines + (as they become available) and also to signal completion. + While reading output from the subprocess the gui must remain responsive, + and capable of updates (e.g. for logging, or cancelling of the process). + To achieve this a separate thread is used for reading input from the + subprocess (this is also taken care of by the rootrun module). + The output lines and completion information are passed to the gui + via signals/messages. The gui is responsible for handling them in the + correct thread, e.g. by adding 'idle' calls. + """ + def __init__(self): + """Initialize the mechanism for making root calls. + """ + self.rootfunc = init_rootrun(self._pwget) + + + def do(self, cmdname, *args): + """This is the larchscripts dispatcher. + The first argument is the name of the function, the remaining ones + are the arguments to the function. + """ + return getattr(self, 'l_' + cmdname)(*args) + + + def _pwget(self, cb, prompt): + """Callback to fetch password, running in rootrun thread. + """ + self.pw_event = threading.Event() + ui_signal('get_password', prompt) + self.pw_event.wait() + self.rootfunc.cb_done(cb, *self.pw_returned) + + + def l_sendpassword(self, ok, pw): + """The gui passes in the password via this call. + The result needs to be passed to the waiting self._pwget method + (which is running in a different thread). + """ + self.pw_returned = (ok, pw) + self.pw_event.set() + return None + + + def _line_cb(self, cb, line): + try: + dline = serialize.loads(line) + except: + dline = line + ui_signal('line_cb', dline) + + + def _end_cb(self, cb, ok, res): + # As the output is passed line-by-line there is no need to pass + # it again. + ui_signal('end_cb', ok) + + + def call(self, cmd, arg=[]): + """Start a process running as root. + """ + args = ' '.join(['"%s"' % i for i in arg]) + cmdx = '%s/cli/%s.py -s %s' % (base_dir, cmd, args) + self.l_rootfn(cmdx) + return None + + + def l_rootfn(self, cmd): + """Run a command as root. + """ + ui_signal('log', '++' + cmd) + self.rootfunc.run(cmd, self._end_cb, self._line_cb, project_manager.project_dir) + return None + + + def l_archin(self, cmd, installrepos): + args = ['-p', project_manager.profile_path, + '-i', project_manager.get_ipath()[1], + '-c', project_manager.get('pacman_cache')] + rf = project_manager.project_dir + '/pacman.conf.repos' + if installrepos and os.path.isfile(rf): + args += ['-r', rf] + return self.call('archin', args + cmd.split()) + + + def l_larchify(self, oldsyssqf, oldlocales): + args = ['-p', project_manager.profile_path, + '-i', project_manager.get_ipath()[1],] + if oldsyssqf: + args.append('-o') + if oldlocales: + args.append('-l') + return self.call('larchify', args) + + + def l_larchmedium(self, dev): + return self.call('medium', ['-T', '-S', dev,]) + + + def l_writemedium(self, source, args): + if source: + args += ['-S', source] + else: + args += ['-p', project_manager.profile_path] + args += ['-i', project_manager.get_ipath()[1]] + return self.call('medium', args) + + + def l_pid(self, pid): + self.rootfunc.setpid(pid) + return None + + + def l_interrupt(self): + ui_signal('log', '--Terminate--') + self.rootfunc.interrupt() + return None + + + def l_reply(self, reply): + self.rootfunc.send(reply) + return None + + + def l_close(self): + # if something is running, stop it ... + try: + self.rootfunc.interrupt() + except: + pass + +fs = Fs() + +import project +project_manager.init() + +larchscripts = LarchScripts() +add_exports(( ('larchscript', larchscripts.do), + )) + + diff --git a/build_tools/larch8/larch0/gui/front/docviewer.py b/build_tools/larch8/larch0/gui/front/docviewer.py new file mode 100644 index 0000000..de51605 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/docviewer.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python2 +# +# docviewer.py +# +# (c) Copyright 2009-2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.06.24 + + +import os + + +class DocViewer: + def __init__(self): + self.index = self._getPage('index.html') + self.homepath = None + ui.widgetlist(fss('fetch_layout', 'docviewer.uim')) + + ui.connectlist( + ('doc:hide*clicked', self._hide), + ('doc:back*clicked', self._back), + ('doc:forward*clicked', self._forward), + ('doc:home*clicked', self.gohome), + ('doc:parent*clicked', self.goto), + (':docs*clicked', self._show), + ) + + def _show(self): + ui.runningtab(3) + + def _hide(self): + ui.runningtab() + + def _back(self): + ui.command('doc:content.prev') + + def _forward(self): + ui.command('doc:content.next') + + def _getPage(self, page): + return fss('get_docs_url', page) + + def gohome(self, home=None): + if home: + self.homepath = self._getPage(home) + self.goto(self.homepath) + + def goto(self, path=None): + if not path: + path = self.index + ui.command('doc:content.setUrl', path) diff --git a/build_tools/larch8/larch0/gui/front/editor.py b/build_tools/larch8/larch0/gui/front/editor.py new file mode 100644 index 0000000..202f236 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/editor.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python2 +# +# editor.py +# +# (c) Copyright 2009-2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.06.24 + +class Editor: + def __init__(self): + ui.widgetlist(fss('fetch_layout', 'editor.uim')) + ui.connectlist( + ('edit:ok*clicked', self.ok), + ('edit:cancel*clicked', self.cancel), + ('edit:revert*clicked', self.dorevert), + ('edit:copy*clicked', self.copy), + ('edit:cut*clicked', self.cut), + ('edit:paste*clicked', self.paste), + ('edit:undo*clicked', self.undo), + ('edit:redo*clicked', self.redo), + ) + + def start(self, title, endcall, text='', revert=None): + ui.command('edit:title.markup', ['h3', title]) + self.endcall = endcall + self.revert = revert + try: + self.text0 = revert() if text == None else text + except: + run_error("BUG: Editor - no revert function?") + ui.command('edit:content.text', self.text0) + ui.runningtab(4) + + def ok(self): + self.endcall(ui.command('edit:content.get')) + ui.runningtab() + + def cancel(self): + ui.runningtab() + + def dorevert(self): + if self.revert: + self.text0 = self.revert() + ui.command('edit:content.text', self.text0) + + def copy(self): + ui.command('edit:content.copy') + + def cut(self): + ui.command('edit:content.cut') + + def paste(self): + ui.command('edit:content.paste') + + def undo(self): + ui.command('edit:content.undo') + + def redo(self): + ui.command('edit:content.redo') + + def edit(self, fname, source=None, label=None, filter=None): + """Files (<fname> and <source>) can be either an absolute path or else + relative to the profile directory, the application base directory + or the working directory. Relative paths are determined by the + prefixes 'profile:', 'base:' or 'working:'. + If the file <fname> already exists its contents will be taken as the + starting point, otherwise the file <source>, which may also be an + empty string, will be read in. + Whichever file is available its contents can be filtered by an + optional 'filter' function, which takes the file contents as a + string as argument and returns the transformed contents as another + string. + """ + def revert(): + """If a file is addressed by 'source' revert to its contents, + if source is "", clear the contents, otherwise revert to the + contents as they were before entering the editor. + """ + return textsrc if source != None else text0 + + def endfile(text): + t = text.encode("utf8") + if t and (t[-1] != "\n"): + t += "\n" + fss('savefile', fname, text) + + if source != None: + textsrc = "" if source == "" else fss('readfile', source, + filter=filter) + # Read the file, if it exists, else return None + text0 = fss('readfile', fname, filter=filter, trap=False) + if text0 == None: + assert source != None # The file must be present + text0 = textsrc + if not label: + label = ui.command('editor_data.get', 'msg_dflt') % fname + self.start(label, endfile, text0, revert) + diff --git a/build_tools/larch8/larch0/gui/front/logview.py b/build_tools/larch8/larch0/gui/front/logview.py new file mode 100644 index 0000000..7e506a5 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/logview.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python2 +# +# logview.py +# +# (c) Copyright 2009-2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.08.10 + +import locale +# Try to work around problems when the system encoding is not utf8 +encoding = locale.getdefaultlocale()[1] + +#TODO: progress bar? +class Progress: + def __init__(self): + self.active = False + ui.widgetlist(fss('fetch_layout', 'progress.uim')) + ui.connect('progress:done*clicked', self._done) + + def _done(self): + self.active = False + ui.runningtab(0) + + def start(self): + # Set busy cursor on the progress area + ui.command('progress:page.busycursor', True) + # Initialize widgets + ui.command("progress:text.text") + ui.command("progress:progress.text") + ui.command("progress:done.enable", False) + ui.command("progress:cancel.enable", True) + self.active = True + ui.runningtab(1) + + def end(self, auto=False): + ui.command("progress:cancel.enable", False) + ui.command("progress:done.enable", True) + # Clear busy cursor on the progress area + ui.command('progress:page.busycursor', True) + ui.command('progress:page.busycursor', False) + if auto: + self._done() + + def addLine(self, line): + # Try to work around problems when the system encoding is not utf8 + if isinstance(line, str): + line = line.decode(encoding, 'replace') + ui.command("progress:text.append_and_scroll", line) + logger.addLine(line) + + def set(self, text=""): + ui.command("progress:progress.text", text) + + +class Logger: + def __init__(self): + ui.widgetlist(fss('fetch_layout', 'logger.uim')) + ui.connectlist( + ('log:clear*clicked', self.clear), + ('log:hide*clicked', self._hide), + (':showlog*clicked', self._show), + ) + + def clear(self): + ui.command('log:text.text') + + def addLine(self, line): + # Try to work around problems when the system encoding is not utf8 + if isinstance(line, str): + line = line.decode(encoding, 'replace') + ui.command('log:text.append_and_scroll', line) + + def _show(self): + ui.runningtab(2) + + def _hide(self): + ui.runningtab() diff --git a/build_tools/larch8/larch0/gui/front/mainwindow.py b/build_tools/larch8/larch0/gui/front/mainwindow.py new file mode 100644 index 0000000..8bf2a57 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/mainwindow.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python2 +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +#2010.10.12 + +import __builtin__ +from liblarch.suim import Suim, debug +__builtin__.debug = debug + + + +def larchcall(script, *args): + if script[0] != '*': + progress.start() # initialize progress widget + ui.setcb(None) + else: + # This script won't switch to the progress widget, but accepts + # a callback to handle the resulting output + script = script[1:] + ui.setcb(args[0]) + args = args[1:] + fss('larchscript', script, *args) + +__builtin__.larchcall = larchcall + + + +class Ui(Suim): + def __init__(self): + self.setcb(None) + Suim.__init__(self, 'larch', busywidgets=[':larch']) + + self.connect('$$$uiclose$$$', self.quit) + self.connect('$$$uiquit$$$', self.quit) + self.connect('$$$cancel$$$', self.interrupt) + self.progressing = False # used for managing progress display + + + def sigin(self, signal, *args): + self.idle_add(getattr(self, 'sig_' + signal), *args) + + + def setcb(self, cb): + """Set a callback to run when a 'larchscript' sends an output line, + or completes. + """ + self._read_cb = cb + + + def isbusy(self): + return self._read_cb != None + + + def runningtab(self, i=None): + if (i == None): + i = 1 if progress.active else 0 + if (i == 0) and hasattr(stage, 'reenter'): + stage.reenter() + self.command(':tabs.set', i) + + def fileDialog(self, message, startdir=None, create=False, + dirsonly=False, file=None, filter=None): +# Actually this should access the file-system via 'fss' ... +# (that would require a new, custom widget) + if dirsonly: + return self.command('fileDialog_getdir', message, not create, startdir) + if file and startdir: + startdir = startdir + '/' + file + if create: + return self.command('fileDialog_save', message, startdir, filter) + return self.command('fileDialog_open', message, startdir, filter) + + def enable_installation_page(self, on): + self.command(':notebook.enableTab', 1, on) + + def interrupt(self): + fss('larchscript', 'interrupt') + + def quit(self): + """Do any tidying up which may be necessary. + """ + fss('larchscript', 'close') + Suim.quit(self) + + def data(self, key): + return self.command('main_page_data.get', key) + + + def sig_get_password(self, message): + """This is a callback, triggered by signal 'get_password' + to ask the user to input the password. + """ + fss('larchscript', 'sendpassword', *ui.command('textLineDialog', + message, "larch: pw", "", True)) + + + def sig_line_cb(self, message): + """A line has been received from the 'larchscript' (this is a callback, + in the main thread). + The input lines are filtered for pacman, mksquashfs and mkisofs + progress output so that appropriate progress reports can be given. + """ + if message.startswith('>-'): + if message.startswith('>-_$$_'): + # Informing us of the pid + fss('larchscript', 'pid', int(message.rsplit('_', 1)[1])) + else: + # It is a progress report + progress.set(message[2:]) + self.progressing = True + return + + else: + if self.progressing: + progress.set() + self.progressing = False + + progress.addLine(message) + if message.startswith('?>'): + # a query (yes/no): pop up the query + fss('larchscript', 'reply', '??YES' if ui.command( + 'confirmDialog', message[2:]) else '??NO') + elif self._read_cb: + self._read_cb(message) + + + def sig_end_cb(self, ok): + """A callback for the end of a 'larchscript'. + The completion code, ok, is ignored. + """ + if self._read_cb: + self._read_cb(None) + self._read_cb = None + self.busy(False) + else: + progress.end() + + + def sig_log(self, line): + logger.addLine(line) + + + + +def tab_changed(index): + __builtin__.stage = pages[index] + stage.enter() + +from page_project import ProjectSettings +from page_installation import Installation +from page_larchify import Larchify +from page_medium import Medium + +from docviewer import DocViewer +from editor import Editor +from logview import Logger, Progress + + +def run_error(message, title=None): + ui.command('warningDialog', message, title) +__builtin__.run_error = run_error + + +pages = [] # Must be initialized before init() because of calls to tab_changed + +def init(): + pages.append(ProjectSettings()) + pages.append(Installation()) + pages.append(Larchify()) + pages.append(Medium()) + + __builtin__.docviewer = DocViewer() + __builtin__.edit = Editor().edit + + __builtin__.progress = Progress() + __builtin__.logger = Logger() + + MainWindow = fss('fetch_layout', 'page_main.uim') + ui.widgetlist(MainWindow) + + ui.connect(':notebook*changed', tab_changed) + + ui.command(':larch.pack') + # Set up the first gui page (project settings) + pages[0].setup() + ui.command(':larch.show') + diff --git a/build_tools/larch8/larch0/gui/front/page_installation.py b/build_tools/larch8/larch0/gui/front/page_installation.py new file mode 100644 index 0000000..82d57c5 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/page_installation.py @@ -0,0 +1,193 @@ +# page_installation.py - Handler for the installation page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.08.12 + +class Installation: + def __init__(self): + ui.widgetlist(fss('fetch_layout', 'page_installation.uim')) + + ui.connectlist( + (':addedpacks*clicked', self.edit_addedpacks), + (':vetopacks*clicked', self.edit_vetopacks), + (':pacmanconf*clicked', self.edit_pacmanconf), + (':repos*clicked', self.edit_repos), + (':editmirrorlist*clicked', self.edit_mirrorlist), + (':cache_change*clicked', self.change_cache), + (':editrepolist*clicked', self.edit_repolist), + (':install*clicked', self.install), + (':sync*clicked', self.dosync), + (':updateall*clicked', self.doupdateall), + (':update*clicked', self.doupdate), + (':add*clicked', self.doadd), + (':remove*clicked', self.doremove), + (':installrepos*toggled', self.installrepo), + ) + + + def enter(self): + """This is called when the page is entered/selected/shown. + It performs initializations which depend on the state. + """ + docviewer.gohome('gui_installation.html') + # Set package cache display + ui.command(':cache_show.text', fss('getitem', 'pacman_cache')) + ui.command(':installrepos.opton', fss('getbool', 'installrepo')) + + + def data(self, key): + return ui.command('install_page_data.get', key) + + + def edit_addedpacks(self): + edit('profile:addedpacks') # In profile dir + + + def edit_vetopacks(self): + # If there is no vetopacks file, start an empty one + edit('profile:vetopacks', "") # In profile dir + + + def edit_pacmanconf(self): + edit('profile:pacman.conf.options', # In profile dir + 'base:data/pacman.conf', # Relative to base_dir + label=self.data('edit_pc'), + filter=pacmanoptions) + + + def edit_repos(self): + """This edits the repository list file for the live system. + It will be used to construct the /etc/pacman.conf file. + If the option to specify a different file for the installation + stage is not enabled (the default), this file will also be used + for the installation. + """ + edit('profile:pacman.conf.repos', # In profile dir + 'base:data/pacman.conf.repos', # Relative to base_dir + label=self.data('edit_pr')) + + + def edit_mirrorlist(self): + ml = '/etc/pacman.d/mirrorlist' + if not fss('isfile', ml): + ml = 'base:data/mirrorlist' # Relative to base_dir + edit('working:mirrorlist', ml, + label=self.data('edit_mli')) + + + def change_cache(self): + # Is anything more necessary? Do I need to test the path? + # Would a directory browser be better? + ok, path = ui.command('textLineDialog', + self.data('prompt_ncp'), + None, fss('getitem', 'pacman_cache')) + if ok: + self.set_pacman_cache(path) + + + def set_pacman_cache(self, path): + path = path.strip().rstrip('/') + fss('setitem', 'pacman_cache', path) + ui.command(':cache_show.text', path) + + + def edit_repolist(self): + """This edits the repository list file used for installation, + if the corresponding option is enabled. + """ + # Should it be based on the default or on the profile? + rf = 'profile:pacman.conf.repos' + if not fss('isfile', rf): + rf = 'base:data/pacman.conf.repos' # Relative to base_dir + edit('working:pacman.conf.repos', rf, + label=self.data('edit_pri')) + + + def installrepo(self, on): + fss('setbool', 'installrepo', on) + + + def install(self): + """Start the installation. + """ + self.archin('install') + + + def dosync(self): + self.archin('refresh') + + + def doupdateall(self): + self.archin('updateall') + + + def doupdate(self): + f = ui.fileDialog(message=self.data('msg_pu'), + filter=(self.data('filter_pu'), '*.pkg.tar.*')) + if f: + self.archin('update ' + f) + + + def doadd(self): + ok, plist = ui.command('textLineDialog', + self.data('prompt_pi'), + 'pacman -S') + if ok: + self.archin('sync ' + plist.strip()) + + + def doremove(self): + ok, plist = ui.command('textLineDialog', + self.data('prompt_pr'), + 'pacman -Rs') + if ok: + self.archin('remove ' + plist.strip()) + + + def archin(self, cmd): + """This runs the 'archin' script (as root). It doesn't wait for + completion because the output must be collected and displayed + while it is running. The display switches the view to the + progress reporting page. It should probably activate the busy + cursor too. + """ + larchcall('archin', cmd, ui.command(':installrepos.active')) + + + +def pacmanoptions(text): + """A filter for pacman.conf to remove the repository info. + """ + texto = "" + block = "" + for line in text.splitlines(): + block += line + "\n" + if line.startswith("#["): + break + if line.startswith("[") and not line.startswith("[options]"): + break + if not line.strip(): + texto += block + block = "" + return texto + + + diff --git a/build_tools/larch8/larch0/gui/front/page_larchify.py b/build_tools/larch8/larch0/gui/front/page_larchify.py new file mode 100644 index 0000000..a63ecff --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/page_larchify.py @@ -0,0 +1,296 @@ +# page_larchify.py - Handler for the project settings page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.10.07 + +USERINFO = ['pw', 'maingroup', 'uid', 'skel', 'xgroups', 'expert'] + +class Larchify: + def __init__(self): + ui.widgetlist(fss('fetch_layout', 'page_larchify.uim')) + + ui.connectlist( + (':build*clicked', self.build), + (':ssh*toggled', self.sshtoggle), + (':locales*clicked', self.locales), + (':rcconf*clicked', self.rcconf), + (':initcpio*clicked', self.initcpio), + (':overlay*clicked', self.overlay), + (':utable*clicked', self.uedit), + (':useradd*clicked', self.useradd), + (':userdel*clicked', self.userdel), + (':rootpwb*clicked', self.rootpw), + (':kernelb*clicked', self.kernelfile), + (':kernelmkib*clicked', self.kernelpreset), + ) + + self.userheaders = self.data('uheaders') + + + def data(self, key): + return ui.command('larchify_page_data.get', key) + + + def enter(self): + """This is called when the page is entered/selected/shown. + It performs initializations which depend on the state. + """ + docviewer.gohome('gui_larchify.html') + + # Check that it could possibly be an Arch installation + idir = fss('get_installation_dir') + couldBeArch = fss('isdir', ':var/lib/pacman/local') + ui.command(':build.enable', couldBeArch) + if not couldBeArch: + run_error(_("No Arch installation at %s") % idir) + + # ssh keys + sshon = fss('isfile', ':usr/bin/ssh-keygen') + ui.command(':ssh.enable', sshon) + + if fss('isfile', 'profile:nosshkeys'): + sshon = False + ui.command(":ssh.set", sshon) + + # users table + idir_normal = idir != '/' + ui.command(':users.enable', idir_normal) + if idir_normal: + # Fetch users information + fss('newUserinfo') + self.readuserinfo() + + # Root password + self.showrootpw() + + # Kernel info + self.showkernel() + + self.reenter() + + + def reenter(self): + """These also need resetting after a build run. + """ + # Whether there is an old system.sqf to reuse? + ossqf = fss('oldsqf') + if not ossqf: + ui.command(':oldsquash.set', False) + ui.command(':oldsquash.enable', ossqf) + + # Whether there is a set of old glibc locales to reuse? + olcl = fss('oldlocales') + if not olcl: + ui.command(':oldlocales.set', False) + ui.command(':oldlocales.enable', olcl) + +#TODO: Remove hack if the underlying bug gets fixed + ui.command(":larchify_advanced.enable_hack") + + + def readuserinfo(self, select=None): + """'select' should be a username, defaulting to the first entry. + """ + self.usersel = 0 + self.userlist = [] + i = 0 + for u in fss('allusers'): + self.userlist.append(self.userinfolist(u)) + if u == select: + self.usersel = i + i += 1 + ui.command(':utable.set', self.userlist, self.usersel) + + + def userinfolist(self, user): + return [user] + fss('getuserinfo', user, USERINFO) + + + def uedit(self, row, column): + if self.usersel == row: + uname = self.userlist[row][0] + ulcell = self.userlist[row][column] + if column == 4: + ok, text = self.select_skel(ulcell) + else: + ok, text = ui.command('textLineDialog', + self.userheaders[column] + ':', 'larchify', ulcell) + text = text.strip() + if ok: + try: + if (column == 0) and (text != ''): + # Rename the user, by adding a new one and deleting + # the old + uname = text + fss('newuser', uname) + i = 0 + for f in USERINFO: + i += 1 + fss('userset', uname, f, self.userlist[row][i]) + if not fss('deluser', ulcell): + run_error(self.data('rn_error')) + + else: + fss('userset', uname, USERINFO[column-1], text) + fss('saveusers') + + except: + run_error(self.data('ud_error')) + self.readuserinfo(uname) + + else: + self.usersel = row + + + def select_skel(self, current): + # Present a list of available 'skel' folders + self.skellist = [self.data('def_skel')] + for f in fss('listskels'): + self.skellist.append(f.rsplit('/skel_', 1)[1]) + try: + i = self.skellist.index(current) + except: + i = 0 + ok, skeli = ui.command('listDialog', self.data('skel_lbl'), + self.data('skel_ttl'), self.skellist, i) + if ok: + return (True, '' if skeli == self.skellist[0] + else skeli.split()[0]) + return (False, '') + + + def useradd(self): + ok, name = ui.command('textLineDialog', self.data('newlogin')) + if ok: + name = name.strip() + if name != '' and fss('newuser', name): + self.userlist.append(self.userinfolist(name)) + self.usersel = len(self.userlist) -1 + ui.command(':utable.set', self.userlist, self.usersel) + + + def userdel(self): + if self.usersel >= 0: + user = self.userlist[self.usersel][0] + if fss('deluser', user): + del(self.userlist[self.usersel]) + lu = len(self.userlist) + if lu: + if lu <= self.usersel: + self.usersel -= 1 + ui.command(':utable.set', self.userlist, self.usersel) + + + def showrootpw(self): + self.rootpw = fss('readfile', 'profile:rootpw', trap=False) + if self.rootpw == None: + self.rootpw = "" + ui.command(':rootpwe.text', self.rootpw) + + + def rootpw(self): + ok, pw = ui.command('textLineDialog', self.data('newrootpw'), + "larchify", self.rootpw) + if ok: + pw = pw.strip() + if pw: + fss('savefile', 'profile:rootpw', pw) + else: + fss('rm_rf', 'profile:rootpw') + self.showrootpw() + + + def sshtoggle(self, on): + """Whether the system ssh keys are pregenerated + depends on the presence of the profile file 'nosshkeys' + (and of course on openssh being installed). + """ + sshoff = fss('isfile', 'profile:nosshkeys') + if on: + if sshoff: + fss('rm_rf', 'profile:nosshkeys') + elif not sshoff: + fss('savefile', 'profile:nosshkeys', "Don't pregenerate ssh keys") + + + def locales(self): + edit('profile:rootoverlay/etc/locale.gen', 'install:etc/locale.gen') + + + def rcconf(self): + edit('profile:rootoverlay/etc/rc.conf', 'install:etc/rc.conf') + + + def initcpio(self): + edit('profile:rootoverlay/etc/mkinitcpio.conf.larch', + 'install:etc/mkinitcpio.conf.larch') + + + def overlay(self): + fss('browse', 'profile:rootoverlay') + + + def showkernel(self): + if fss('isfile', 'profile:kernel'): + ki = fss('readfile', 'profile:kernel') + else: + ki = fss('readfile', 'base:data/kernel') + self.kernel, self.kernelpreset = ki.split() + ui.command(':kernele.text', self.kernel) + ui.command(':kernelmkie.text', self.kernelpreset) + + + def kernelfile(self): + ok, kf = ui.command('textLineDialog', self.data('kernelf'), + "larchify", self.kernel) + if ok: + kf = kf.strip() + if not kf: + fss('rm_rf', 'profile:kernel') + + elif (' ' in kf) or not fss('isfile', 'install:boot/' + kf): + run_error(_("Invalid kernel binary: %s") % kf) + return + else: + fss('savefile', 'profile:kernel', kf + ' ' + self.kernelpreset) + self.showkernel() + + + def kernelpreset(self): + ok, kp = ui.command('textLineDialog', self.data('kernelp'), + "larchify", self.kernelpreset) + if ok: + kp = kp.strip() + if not kp: + fss('rm_rf', 'profile:kernel') + + elif (' ' in kp) or not fss('isfile', + 'install:etc/mkinitcpio.d/%s.preset' % kp): + run_error(_("Invalid kernel mkinitcpio preset: %s") % kp) + return + else: + fss('savefile', 'profile:kernel', self.kernel + ' ' + kp) + self.showkernel() + + + def build(self): + larchcall('larchify', ui.command(':oldsquash.active'), + ui.command(':oldlocales.active')) diff --git a/build_tools/larch8/larch0/gui/front/page_medium.py b/build_tools/larch8/larch0/gui/front/page_medium.py new file mode 100644 index 0000000..e63ce13 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/page_medium.py @@ -0,0 +1,441 @@ +# page_medium.py - Handler for the project settings page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.11.28 + +"""This page takes a directory processed by larchify. It produces a bootable +larch medium, or, in the case of CD/DVD, an iso image. +It also handles creation of a boot-iso for an existing larch medium (partition) +and can copy larch media to different devices. +""" + +import os +from config import detection_methods, OKFS + + +class Medium: + def __init__(self): + self.medium = None + self.srcmedium = None + self.detectionmodes = detection_methods.split('|') + ui.widgetlist(fss('fetch_layout', 'page_medium.uim')) + + ui.connectlist( + (':vlabelb*clicked', self.newlabel), + (':selectpart*clicked', self.choosedest), + (':selectsrc*clicked', self.choosesrc), + (':persist*toggled', self.persistence), + (':ovl_journal*toggled', self.journal), + (':mediumtype*changed', self.mediumtype), + (':srctype*changed', self.srctype), + (':make_medium*clicked', self.make), + (':nolarchboot*toggled', self.bootnosearch), + (':detection*changed', self.mediumsearch), + (':pformat*toggled', self.setpformat), + (':bootlines*clicked', self.editbootlines), + (':syslinuxtemplate*clicked', self.editsyslin), + (':cdroot*clicked', self.browsecdroot), + ) + + + def enter(self): + """This is called when the page is entered/selected/shown. + It performs initialisations which depend on the state. + """ + self.destinationpath = '' + self.fsok = None + self.source, ok = fss('testlarchify') # check the larchified installation + if not ok: + run_error(self.data('msg_med') % self.source) + self.source = '' + self.mediumtype(None) + if self.medium != 'medium-boot': + self.srctype(None) + detect = fss('getitem', 'medium_search') + ui.command(':detection.set', + [self.data('detectionmodes')[l] for l in self.detectionmodes], + self.detectionmodes.index(detect)) + self.nlbenable(detect) + ui.command(':nolarchboot.set', fss('getbool', 'boot_nosearch')) + ui.command(':vlabele.text', fss('get_mediumlabel')) + ui.command(':ovl_journal.set', fss('getbool', 'journal')) + fmt = fss('getbool', 'do_format') + ui.command(':pformat.set', fmt ) + ui.command(':ovl_journal.enable', fmt) + ui.command(':persist.set', fss('getbool', 'do_persist')) + + ui.command(':larchpart.text') # clear the destination + docviewer.gohome('gui_medium.html') + + + def data(self, key): + return ui.command('medium_page_data.get', key) + + + def mediumtype(self, index): + if (index == None): + if self.medium == None: + self.medium = self.data('media')[0] + else: + self.medium = self.data('media')[index] + + if self.medium == 'medium-boot': + label = fss('get_bootisolabel') + ui.command(':srctype.setindex', self.data('sources').index('device')) + ui.command(':srctype.enable', False) + ifile = fss('getbootisofile') + + else: + label = fss('get_mediumlabel') + ui.command(':srctype.enable', True) + + if self.medium == 'medium-iso': + ifile = fss('getisofile') + + self.showlabel(label) + + if self.medium == 'medium-w': + ui.command(':mediumopts.enable', True) + self.setdestinationpath('') + + else: + ui.command(':mediumopts.enable', False) + idir = fss('getisosavedir') + if not fss('isdir', idir): + fss('setitem', 'isosavedir', '') + idir = fss('getisosavedir') + self.setdestinationpath(os.path.join(idir, ifile)) + + if index != None: + self.enableprofile() + self.enablemake() + + + def srctype(self, index): + if (index == None): + if self.srcmedium == None: + self.srcmedium = self.data('sources')[0] + else: + self.srcmedium = self.data('sources')[index] + + if self.srcmedium == 'larchified': + self.setsourcepath(self.source) + ui.command(':selectsrc.enable', False) + + elif self.srcmedium == 'device': + self.setsourcepath('') # clear source, it must be selected + ui.command(':selectsrc.enable', True) + + elif self.srcmedium == 'isofile': + isof = fss('getisofile') + isod = fss('getisosavedir') + self.pendingpath = os.path.join(isod, isof) + ui.command(':selectsrc.enable', True) + self.setsourcepath('') + self.checklarchsource() + + + def setsourcepath(self, path): + self.sourcepath = path + ui.command(':srclocation.text', path) + self.enableprofile() + self.enablemake() + + + def choosesrc(self): + # 'larchified' should not be possible - it is set on the project page + if self.srcmedium == 'device': + self.devices = [] + larchcall('*rootfn', self._cd_line, 'blkid -c dev/null -o list') + + elif self.srcmedium == 'isofile': + # Pop up a file browser + self.pendingpath = self.isopath(mode='source') + self.checklarchsource() + + else: + debug('page_medium: Medium.choosesrc / ' + self.srcmedium) + + def _cd_line(self, line): + if line == None: + # Completed - pop up device chooser + ok, choice = ui.command('listDialog', + self.data('parts_src'), + self.data('parts_t'), + self.devices, len(self.devices) - 1) + if ok: + self.pendingpath = choice.split()[0] + ui.idle_add(self.checklarchsource) + + else: + l = line.strip() + if l.startswith('/dev/'): + i = l.find('(not mounted)') + if i > 0: + ls = l[:i].split(None, 2) + if (ls[0] != self.destinationpath) and (len(ls) == 3): + self.devices.append('%-10s %s' % (ls[0], ls[2])) + + + def checklarchsource(self): + if self.pendingpath: + # check it is really a larch medium + self.larchok = False + larchcall('*larchmedium', self._cs_line, self.pendingpath) + + def _cs_line(self, line): + if line == None: + # Completed + if self.larchok: + self.setsourcepath(self.pendingpath) + else: + l = line.strip() + if l.startswith('##--'): + if l.endswith('ok'): + self.larchok = True + + + def enableprofile(self): + """Set the enabled state of the medium profile frame, + according to the state of the choices. + """ + ui.command(':mediumprofile.enable', (self.medium != 'medium-boot') + and (self.srcmedium == 'larchified')) + + + def enablemake(self): + on = bool(self.destinationpath) and bool(self.sourcepath) + if self.medium == 'medium-w': + formatting = fss('getbool', 'do_format') + self.enablepersist(formatting or (self.fsok in OKFS)) + if (not formatting): + if (self.fsok not in OKFS) and (self.fsok != 'vfat'): + on = False + ui.command(':make_medium.enable', on) + + + def enablepersist(self, on): + ui.command(':persist.enable', on) + self.persist_enabled = on + + + def persistence(self, on): + fss('setbool', 'do_persist', on) + + + def journal(self, on): + fss('setbool', 'journal', on) + + + def mediumsearch(self, option): + choice = self.detectionmodes[option] + fss('setitem', 'medium_search', choice) + self.nlbenable(choice) + + + def nlbenable(self, choice): + ui.command(':nolarchboot.enable', choice != 'search') + + + def bootnosearch(self, on): + fss('setbool', 'boot_nosearch', on) + + + def setpformat(self, on): + fss('setbool', 'do_format', on) + ui.command(':ovl_journal.enable', on) + self.enablemake() + + + def editbootlines(self): + f0 = 'profile:cd-root/boot0/bootlines' + if not fss('isfile', f0): + f0 = 'base:cd-root/boot0/bootlines' + edit('profile:cd-root/boot/bootlines', 'base:cd-root/boot0/bootlines') + + + def editsyslin(self): + f0 = 'profile:cd-root/boot0/isolinux/isolinux.cfg' + if not fss('isfile', f0): + f0 = 'base:cd-root/boot0/isolinux/isolinux.cfg' + edit('profile:cd-root/boot/isolinux/isolinux.cfg', f0) + + + def browsecdroot(self): + fss('browse', 'profile:cd-root') + + + def newlabel(self): + labelsrc = 'bootiso' if self.medium == 'medium-boot' else 'medium' + ok, l = ui.command('textLineDialog', + self.data('prompt_label'), + None, fss('get_%slabel' % labelsrc)) + if ok: + self.showlabel(fss('set_%slabel' % labelsrc, l)) + + + def showlabel(self, l): + ui.command(':vlabele.text', l) + + + def choosedest(self): + if self.medium == 'medium-w': + # Present a list of unmounted partitions + self.devices = [] + larchcall('*rootfn', self._sd_line, 'blkid -c /dev/null -o list') + + elif self.medium == 'medium-iso': + # Pop up a file browser + path = self.isopath(mode='source') + if path: + self.setdestinationpath(path) + + elif self.medium == 'medium-boot': + # Pop up a file browser + path = self.isopath(mode='bootiso') + if path: + self.setdestinationpath(path) + + def _sd_line(self, line): + if line == None: + nmdevices = [] + for part in fss('get_partitions'): # ->(dev, size in MiB(int)) + if part[0] == self.sourcepath: + continue + found = False + for partinfo in self.devices: + if partinfo[0] == part[0]: + if partinfo[2]: + nmdevices.append('%-12s %8d MiB %-10s %s' + % (part[0], part[1], partinfo[1], partinfo[2])) + found = True + break + if not found: + nmdevices.append('%-12s %-12d MiB' % (part[0], part[1])) + + # Completed - pop up device chooser + ok, choice = ui.command('listDialog', + self.data('parts_dst'), + self.data('parts_t'), + nmdevices, len(nmdevices) - 1) + if ok: + ui.idle_add(self.setdestinationpath, choice.split()[0]) + + else: + l = line.strip() + if l.startswith('/dev/'): + i = l.find('(not mounted)') + if i > 0: + # Try to get label for unmounted devices only + ls = l[:i].split(None, 2) + if len(ls) < 3: + ls.append('-') # signifies 'no label' + else: + ls = l.split(None, 2) + ls[2] = None # mark the partition as mounted + self.devices.append(ls) + + + def setdestinationpath(self, path): + ui.command(':larchpart.text', path) + self.destinationpath = path + if path.startswith('/dev/'): + # Check the file-system + self.fsok = None + larchcall('*rootfn', self._em_line, + 'blkid -c /dev/null -o value -s TYPE %s' % path) + else: + self.enablemake() + + def _em_line(self, line): + if line == None: + # Completed + self.enablemake() + else: + line = line.strip() + if line: + self.fsok = line + + + def isopath(self, mode='dest'): + sdir = fss('getisosavedir') + ifname = fss('getbootisofile' if mode=='bootiso' else 'getisofile') + path = ui.fileDialog(self.data('isoget' if mode=='source' else 'isopath'), + startdir=sdir, create=(mode!='source'), + file=ifname, filter=(self.data('iso_type'), '*.iso')) + if path: + f = os.path.basename(path) + d = os.path.dirname(path) + if d != sdir: + fss('setitem', 'isosavedir', d) + if f != ifname: + fss('setitem', 'bootisofile' if mode=='bootiso' else 'isofile', f) + return path + + return None + + + def make(self): + """Write the larch medium. + """ + if self.srcmedium == 'larchified': + source = None + else: + source = self.sourcepath + if not source: + debug("page_medium: make / null source") + return + + args = ['-l', ui.command(':vlabele.get')] + + if self.medium == 'medium-boot': + # Write a boot iso file + args += ['-b', '-o', self.destinationpath] + larchcall('writemedium', source, args) + + elif self.medium == 'medium-iso': + # Write an 'iso' file + args += ['-o', self.destinationpath] + larchcall('writemedium', source, args) + + else: + # Write to partition + # Medium detection options + detect = fss('getitem', 'medium_search') + args += ['-d', detect] + if (detect != 'search') and ui.command(':nolarchboot.active'): + args.append('-n') + # Formatting + if fss('getbool', 'do_format'): + # Journalling? + if not fss('getbool', 'journal'): + args.append('-j') + else: + args.append('-F') + # Set master boot record? + if ui.command(':nombr.active'): + args.append('-m') + # Persistence + if fss('getbool', 'do_persist') and self.persist_enabled: + args.append('-P') + # Add the medium to the argument list + args.append(self.destinationpath) + larchcall('writemedium', source, args) diff --git a/build_tools/larch8/larch0/gui/front/page_project.py b/build_tools/larch8/larch0/gui/front/page_project.py new file mode 100644 index 0000000..cbbda66 --- /dev/null +++ b/build_tools/larch8/larch0/gui/front/page_project.py @@ -0,0 +1,241 @@ +# page_project.py - Handler for the project settings page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.08.15 + +import os + +class ProjectSettings: + def __init__(self): + ui.widgetlist(fss('fetch_layout', 'page_project.uim')) + ui.widgetlist(fss('fetch_layout', 'profile_browse.uim')) + ui.command('dialog:profile_browser.pack') + + ui.connectlist( + (':choose_profile_combo*changed', self.switch_profile), + (':profile_rename*clicked', self.rename_profile), + (':profile_browse*clicked', self.browse_profile), + (':profile_delete*clicked', self.delete_profile), + (':profile_save*clicked', self.copy_profile), + (':profile_clone*clicked', self.clone_profile), + (':installation_path_change*clicked', self.new_build_path), + (':choose_project_combo*changed', self.switch_project), + (':new_project*clicked', self.get_new_project_name), + (':project_delete*clicked', self.delete_project), + ('dpb:example_list*changed', self.dpb_example), + ('dpb:browse*clicked', self.dpb_browse), + ('dpb:name_s*clicked', self.dpb_source), + ) + + + def setup(self): + # Initialize project combobox + self.projects = fss('get_projects') + self.project_name = fss('get_project') + try: + pix = self.projects.index(self.project_name) + except: + self.switch_project(0) + return + ui.command(':choose_project_combo.set', self.projects, pix) + # Initialize profile combobox + self.profiles = fss('get_profiles') + self.profile_name = fss('get_profile') + try: + pfix = self.profiles.index(self.profile_name) + except: + self.switch_profile(0) + pfix = 0 + ui.command(':choose_profile_combo.set', self.profiles, pfix) + # Initialize installation_dir display + self.set_build_dir(fss('get_installation_dir')) + + + def enter(self): + """This is called when the page is entered/selected/shown. + It performs initializations which depend on the state. + """ + docviewer.gohome('gui_project_settings.html') + + + def data(self, key): + return ui.command('project_page_data.get', key) + + + def set_build_dir(self, path): + self.build_dir = path + ui.command(':installation_path_show.text', self.build_dir) + ui.enable_installation_page(self.build_dir != '/') + + + def switch_profile(self, index): + """This has no effect on the display! + It is assumed that the display is already updated, or will be + updated later, and that the index is valid, so that the operation + cannot fail. + """ + self.profile_name = self.profiles[index] + fss('set_profile', self.profile_name) + + + def browse_profile(self): + self.example_profile_dir, self.dpb_example_list = fss('get_example_profiles') + ui.command('dpb:example_list.set', self.dpb_example_list, -1) + if ui.command('dialog:profile_browser.showmodal'): + source = ui.command('dpb:source.get') + name = ui.command('dpb:name.get') + if name in self.profiles: + if not ui.command('confirmDialog', self.data('prompt_pr')): + return + if fss('get_new_profile', source, name): + self.setup() + else: + run_error(self.data('msg_npd') % source) + + + def dpb_example(self, index): + name = self.dpb_example_list[index] + ui.command('dpb:source.text', self.example_profile_dir + '/' + name) + ui.command('dpb:name.text', name) + + + def dpb_browse(self): + source = ui.fileDialog(self.data('file_ps'), dirsonly=True, + startdir=fss('getitem', 'profile_browse_dir')) + if source: + fss('setitem', 'profile_browse_dir', os.path.dirname(source)) + ui.command('dpb:source.text', source) + ui.command('dpb:name.text', os.path.basename(source)) + ui.command('dpb:example_list.set', self.dpb_example_list, -1) + + + def dpb_source(self): + ok, name = ui.command('textLineDialog', _("Name for new profile:"), None, + os.path.basename(ui.command('dpb:source.get'))) + if ok: + ui.command('dpb:name.text', name) + + + def rename_profile(self): + if fss('can_rename_profile'): + ok, new = ui.command('textLineDialog', + self.data('prompt_pn'), + None, self.profile_name) + if ok: + new = new.strip() + if new in self.profiles: + ui.command('warningDialog', self.data('prompt_pe') % new) + else: + fss('rename_profile', new) + self.setup() + else: + ui.command('infoDialog', self.data('msg_pu')) + + + def clone_profile(self): + ok, name = ui.command('textLineDialog', self.data('prompt_clone'), self.profile_name) + if ok: + name = name.strip() + self.save_profile(name) + fss('set_profile', name) + self.setup() + + def copy_profile(self): + startdir = fss('getitem', 'profile_browse_dir') + path = ui.fileDialog(self.data('file_sp'), + create=True, dirsonly=True, file=self.profile_name, + startdir=startdir) + if path: + fss('set_profile_browse_dir', path) + self.save_profile(path) + + def save_profile(self, path): + ok = fss('save_profile', path, False) + if ok == False: + if ui.command('confirmDialog', self.data('prompt_dr')): + # Force overwrite + fss('save_profile', path, True) + elif ok == None: + run_error(self.data('msg_piu')) + else: + self.setup() + + + def delete_profile(self): + plist = fss('list_free_profiles') + if plist: + ok, item = ui.command('listDialog', self.data('prompt_dp'), + self.data('delprof'), plist) + if ok: + if fss('delete_profile', item): + self.setup() + else: + ui.command('infoDialog', self.data('msg_dpff') % item) + else: + ui.command('infoDialog', self.data('msg_npf')) + + + def new_build_path(self): + # Is anything more necessary? Do I need to test or create the path? + # I don't think so, the installation code does that. + # If the path is "/", the installation page should be inhibited, + # but that is handled by 'setup'. + ok, path = ui.command('textLineDialog', + self.data('prompt_ip'), + None, self.build_dir) + if ok: + path = fss('set_installation_dir', path) + if path: + self.set_build_dir(path) + + + def switch_project(self, index): + fss('set_project', self.projects[index]) + self.setup() + + + def get_new_project_name(self): + ok, name = ui.command('textLineDialog', + self.data('prompt_np'), + None, self.project_name) + if ok: + if name in self.projects: + run_error(self.data('msg_pe') % name) + else: + fss('set_project', name) + self.setup() + + + def delete_project(self): + """Pop up a list of eligible project names, the selected one + will be deleted. + """ + plist = fss('list_free_projects') + if plist: + ok, item = ui.command('listDialog', self.data('prompt_pd'), + self.data('delproj'), plist) + if ok: + fss('delete_project', item) + self.setup() + else: + ui.command('infoDialog', self.data('msg_np')) + + diff --git a/build_tools/larch8/larch0/gui/larch.py b/build_tools/larch8/larch0/gui/larch.py new file mode 100755 index 0000000..cda420b --- /dev/null +++ b/build_tools/larch8/larch0/gui/larch.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 +# +# larch.py - GUI for the larch scripts +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +#2010.10.12 + +import sys, os, __builtin__ +dirpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(dirpath + '/front') +basedir = os.path.dirname(dirpath) +sys.path.append(basedir + '/cli') +sys.path.append(os.path.dirname(basedir)) + +from controller import base_dir, exports +# Note that the gui module must have a reference point for relative +# paths, so the current directory must be set to the larch base directory +# before starting the gui: +os.chdir(base_dir) + +import mainwindow + +_running = False +def fss(key, *args, **kargs): + while True: + if _running: + ui.busy(True) + res = exports[key](*args) + if _running and not ui.isbusy(): + ui.busy(False) + if res: + result = res[1] + if res[0]: + filter = kargs.get('filter') + return filter(result) if filter else result + + if kargs.get('trap', True): + ui.command('errorDialog', result) + # That might return, but the gui should quit (soon?). + return None + +__builtin__.fss = fss +__builtin__.ui = mainwindow.Ui() +__builtin__.ui_signal = ui.sigin + +mainwindow.init() +_running = True +ui.run() diff --git a/build_tools/larch8/larch0/gui/layouts/docviewer.uim b/build_tools/larch8/larch0/gui/layouts/docviewer.uim new file mode 100644 index 0000000..8e85a0f --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/docviewer.uim @@ -0,0 +1,72 @@ +# docviewer.uim - The layout for the documentation viewer widget +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.05.21 + +[ + ['Frame', 'doc:page', + { 'layout': + ['VBOX', + ['HBOX', 'doc:header', '*', 'doc:back', 'doc:forward', + 'doc:home', 'doc:parent', 'doc:hide'], + 'doc:content' + ] + } + ], + ['Label', 'doc:header', + { 'markup': ['h2', _("Documentation")] + } + ], + ['HtmlView', 'doc:content', {}], + ['Button', 'doc:hide', + { 'text': _("Hide"), + 'tt': _("Return to the larch controls"), + 'clicked': '' + }, + ], + ['Button','doc:back', + { 'icon': 'left', + 'tt': _("Go back in the viewing history"), + 'clicked': '' + }, + ], + ['Button','doc:forward', + { 'icon': 'right', + 'tt': _("Go forward in the viewing history"), + 'clicked': '' + }, + ], + + ['Button','doc:home', + { 'icon': 'reload', + 'tt': _("Reload the documentation for the current larch tab"), + 'clicked': '' + }, + ], + + ['Button','doc:parent', + { 'icon': 'up', + 'tt': _("Go to the general larch documentation index"), + 'clicked': '' + }, + ], + +] diff --git a/build_tools/larch8/larch0/gui/layouts/editor.uim b/build_tools/larch8/larch0/gui/layouts/editor.uim new file mode 100644 index 0000000..2338f6e --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/editor.uim @@ -0,0 +1,92 @@ +# editor.uim - The layout for the editor widget +# +# (c) Copyright 2009-2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.05.21 + +[ + ['Frame', 'edit:page', + { 'layout': + ['VBOX', + ['HBOX', 'edit:header', '*', 'edit:title'], + ['HBOX', 'edit:content', + ['VBOX', 'edit:copy', 'edit:cut', 'edit:paste', + 'edit:undo', 'edit:redo', 'edit:revert', + '*', 'edit:cancel', 'edit:ok' + ] + ] + ] + } + ], + ['Label', 'edit:header', + { 'markup': ['h2', _("Editor")] + } + ], + ['Label', 'edit:title', {}], + ['TextEdit', 'edit:content', {}], + ['Button', 'edit:ok', + { 'text': _('OK'), + 'clicked': '' + } + ], + ['Button', 'edit:cancel', + { 'text': _('Cancel'), + 'clicked': '' + } + ], + ['Button', 'edit:revert', + { 'text': _('Revert'), + 'tt': _('Restore the text to its initial/default state'), + 'clicked': '' + } + ], + ['Button', 'edit:copy', + { 'text': _('Copy'), + 'clicked': '' + } + ], + ['Button', 'edit:cut', + { 'text': _('Cut'), + 'clicked': '' + } + ], + ['Button', 'edit:paste', + { 'text': _('Paste'), + 'clicked': '' + } + ], + ['Button', 'edit:undo', + { 'text': _('Undo'), + 'clicked': '' + } + ], + ['Button', 'edit:redo', + { 'text': _('Redo'), + 'clicked': '' + } + ], + + ['DATA', 'editor_data', + { 'messages': + { 'msg_dflt': _("Editing '%s'") + } + }, + ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/logger.uim b/build_tools/larch8/larch0/gui/layouts/logger.uim new file mode 100644 index 0000000..0ecb7bf --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/logger.uim @@ -0,0 +1,57 @@ +# logger.uim - The layout for the logging widget +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.05.22 + +[ + ['Frame', 'log:page', + { 'layout': + ['VBOX', + 'log:header', + ['HBOX', + 'log:text', + ['VBOX', 'log:clear', '*', 'log:hide'] + ] + ] + } + ], + ['Label', 'log:header', + { 'markup': ['', ['h2', _("Low-level Command Logging")], + ['p', _("Here you can follow the detailed, low-level" + " progress of the commands.")]] + } + ], + ['TextEdit', 'log:text', + { 'ro': True + } + ], + ['Button', 'log:clear', + { 'text': _("Clear"), + 'clicked': '' + } + ], + ['Button', 'log:hide', + { 'text': _("Hide"), + 'tt': _("Go back to the larch controls"), + 'clicked': '' + } + ] +] diff --git a/build_tools/larch8/larch0/gui/layouts/page_installation.uim b/build_tools/larch8/larch0/gui/layouts/page_installation.uim new file mode 100644 index 0000000..db86059 --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_installation.uim @@ -0,0 +1,190 @@ +# page_installation.uim - The layout for the installation page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.10.10 + +[ + ['Page', ':page_installation', + { 'layout': + ['VBOX', + ['HBOX', + ['VBOX', ':editmirrorlist', ':edit_profile'], + 'VLINE,20', + ':pacmanops' + ], + '*', + ':settings_advanced', + 'HLINE', + ['HBOX', '*', ':install'] + ] + } + ], + # - - - - The profile editing frame + ['Frame', ':edit_profile', + { 'text': _("Edit Profile"), + 'layout': + ['VBOX', + ':addedpacks', + ':vetopacks', + ':pacmanconf', + ':repos' + ] + } + ], + ['Button', ':addedpacks', + { 'text': _("Edit 'addedpacks'"), + 'tt': _("Edit the list of packages to be installed") + }, + 'clicked' + ], + ['Button', ':vetopacks', + { 'text': _("Edit 'vetopacks'"), + 'tt': _("Edit the list of packages NOT to install") + }, + 'clicked' + ], + ['Button', ':pacmanconf', + { 'text': _("Edit pacman.conf options"), + 'tt': _("Edit pacman.conf options - not the repositories") + }, + 'clicked' + ], + ['Button', ':repos', + { 'text': _("Edit pacman.conf repositories"), + 'tt': _("Edit the repository entries for pacman.conf") + }, + 'clicked' + ], + + # - - - - The installed package tweaking frame + ['OptionalFrame', ':pacmanops', + { 'text': _("Tweak Installed Packages"), + 'layout': + ['VBOX', ':sync', ':updateall', ':update', ':add', ':remove'] + } + ], + ['Button', ':sync', + { 'text': _("Synchronize db [-Sy]"), + 'tt': _("Synchronize the pacman db on the target (pacman -Sy)") + }, + 'clicked' + ], + ['Button', ':updateall', + { 'text': _("Update all packages [-Su]"), + 'tt': _("Update all installed packages for which a newer version is available\n" + "(you will normally need to synchronize the pacman db first)") + }, + 'clicked' + ], + ['Button', ':update', + { 'text': _("Update / Add package [-U]"), + 'tt': _("Update / Add a package from a package file" + " using pacman -U") + }, + 'clicked' + ], + ['Button', ':add', + { 'text': _("Add package(s) [-S]"), + 'tt': _("Add one or more packages (space separated)" + " using pacman -S") + }, + 'clicked' + ], + ['Button', ':remove', + { 'text': _("Remove package(s) [-Rs]"), + 'tt': _("Remove one or more packages (space separated)" + " using pacman -Rs") + }, + 'clicked' + ], + + # - - - - The advanced installation options frame + ['OptionalFrame', ':settings_advanced', + { 'text': _("Advanced Installation Options"), + 'layout': ['HBOX', ':installrepos', 'VLINE,3', ':cache'] + } + ], + + ['OptionalFrame', ':installrepos', + { 'text': _("Use project repository list"), + 'tt': _("Enables use of an alternative pacman.conf" + " for installation only"), + 'layout': + ['HBOX', ':editrepolist'] + }, + 'toggled' + ], + ['Button', ':editrepolist', + { 'text': _("Edit repository list"), + 'tt': _("Edit repository list file used for installation") + }, + 'clicked' + ], + ['Button', ':editmirrorlist', + { 'text': _("Edit mirror list"), + 'tt': _("Edit the pacman mirror list for the live system\n" + "(not for the build process)") + }, + 'clicked' + ], + + ['Frame', ':cache', + { 'text': _("Package Cache"), + 'layout': + ['HBOX', ':cache_show', ':cache_change'] + } + ], + ['LineEdit', ':cache_show', + { 'ro': True, + 'tt': _("The path to the (host's) package cache") + } + ], + ['Button', ':cache_change', + { 'text': _("Change"), + 'tt': _("Change the package cache path") + }, + 'clicked' + ], + + ['Button', ':install', + { 'text': _("Install"), + 'tt': _("This will start the installation to the set path") + }, + 'clicked' + ], + + ['DATA', 'install_page_data', + { 'messages': + { 'edit_pc': _("Editing pacman.conf options only"), + 'edit_pr': _("Editing pacman repositories"), + 'edit_mli': _("Editing mirror list for installation"), + 'prompt_ncp': _("Enter new package cache path:"), + 'edit_pri': _("Editing pacman repositories for installation"), + 'msg_pu': _("Package to add/update"), + 'filter_pu': _("Packages"), + 'prompt_pi': _("Enter the names of packages to install -" + "\n separated by spaces:"), + 'prompt_pr': _("Enter the names of packages to remove -" + "\n separated by spaces:"), + } + }, + ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/page_larchify.uim b/build_tools/larch8/larch0/gui/layouts/page_larchify.uim new file mode 100644 index 0000000..34c4e66 --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_larchify.uim @@ -0,0 +1,216 @@ +# page_larchify.uim - The layout for the larchify page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.10.10 + +(lambda USERHEADERS: +[ + ['Page', ':page_larchify', + { 'layout': + ['VBOX', +# ':larchify', + ':users', + 'HLINE', + ['HBOX', + ['VBOX', ':overlay', ':locales', ':rcconf'], + 'VLINE', + ['VBOX', ':kernelfile', '*', ':kernelpack'], + 'VLINE', + ':larchify_advanced' + ], + 'HLINE', + ['HBOX', ':oldsquash', '*', ':build'] + ] + } + ], + # - - - - The profile editing frame + ['Button', ':locales', + { 'text': _("Supported locales"), + 'tt': _("Edit the /etc/locale.gen file to select" + " supported glibc locales") + }, + 'clicked' + ], + ['Button', ':rcconf', + { 'text': _("Edit /etc/rc.conf"), + 'tt': _("Edit the general system configuration file for the" + " live system") + }, + 'clicked' + ], + ['Button', ':overlay', + { 'text': _("Edit overlay"), + 'tt': _("Open a file browser on the profile's 'rootoverlay'") + }, + 'clicked' + ], + + # - - - - The kernel selection frame + ['Frame', ':kernelfile', + { 'text': _("Live kernel filename"), + 'tt': _("The name of the kernel binary file (in /boot)"), + 'layout': + ['HBOX', ':kernele', ':kernelb'] + } + ], + ['LineEdit', ':kernele', + { 'width': 100, + 'ro': True, + } + ], + ['Button', ':kernelb', + { 'text': _("Change"), + 'tt': _("Change the name of the kernel binary file (in /boot)") + }, + 'clicked' + ], + ['Frame', ':kernelpack', + { 'text': _("Live kernel package"), + 'tt': _("The name of the kernel for mkinitcpio (the preset file)"), + 'layout': + ['HBOX', ':kernelmkie', ':kernelmkib'] + } + ], + ['LineEdit', ':kernelmkie', + { 'width': 100, + 'ro': True, + } + ], + ['Button', ':kernelmkib', + { 'text': _("Change"), + 'tt': _("Change the name of the kernel preset file (for mkinitcpio)") + }, + 'clicked' + ], + + + ['OptionalFrame', ':larchify_advanced', + { 'text': _("Advanced Options"), + 'layout': + ['VBOX', ':initcpio', ':oldlocales', ':ssh'], + } + ], + ['Button', ':initcpio', + { 'text': _("Edit mkinitcpio.conf"), + 'tt': _("Edit the configuration file for generating" + " the initramfs via mkinitcpio") + }, + 'clicked' + ], + ['CheckBox', ':ssh', + { 'text': _("Generate ssh keys"), + 'tt': _("The ssh host keys will be pre-generated") + }, + 'toggled' + ], + ['CheckBox', ':oldlocales', + { 'text': _("Reuse existing locales"), + 'tt': _("To save time it may be possible to reuse glibc" + " locales from a previous run") + }, +# 'toggled' + ], + + ['CheckBox', ':oldsquash', + { 'text': _("Reuse existing system.sqf"), + 'tt': _("Reuse existing system.sqf, to save time if the" + " base system hasn't changed") + }, +# 'toggled' + ], + ['Button', ':build', + { 'text': _("Larchify"), + 'tt': _("Build the main components of the larch system") + }, + 'clicked' + ], + +#Note that this should be disabled if installation directory is '/' + ['Frame', ':users', + { 'text': _("User accounts"), + 'layout': + ['VBOX', + ':utable', + ['HBOX', ':useradd', ':userdel', '*', + ':rootpwl', ':rootpwe', ':rootpwb' + ] + ] + } + ], + ['List', ':utable', + { 'selectionmode': 'Single', + 'headers': USERHEADERS, + 'compact': True, + 'tt': _("Click on a row to select, click on a selected" + " cell to edit") + }, +# 'select', + 'clicked' + ], + ['Button', ':useradd', + { 'text': _("Add user"), + 'tt': _("Create a new user-name") + }, + 'clicked' + ], + ['Button', ':userdel', + { 'text': _("Delete user"), + 'tt': _("Remove the selected user-name") + }, + 'clicked' + ], + ['Label', ':rootpwl', + { 'text': _("Root password:") + } + ], + ['LineEdit', ':rootpwe', + { 'ro': True, + 'tt': _("The unencrypted root password for the live system") + } + ], + ['Button', ':rootpwb', + { 'text': _("Change"), + 'tt': _("Enter a new password for the 'root' user") + }, + 'clicked' + ], + + + ['DATA', 'larchify_page_data', + { 'messages': + { 'uheaders': USERHEADERS, + 'rn_error': _("Renaming failed, see log"), + 'ud_error': _("Couldn't adjust user definition"), + 'def_skel': _("Default (/etc/skel)"), + 'skel_lbl': _("This folder will be copied\n" + "to build the user's home folder:"), + 'skel_ttl': _("Choose 'skel' Folder"), + 'newlogin': _("Enter login-name for new user:"), + 'newrootpw': _("Enter root password for live system:"), + 'kernelf': _("Name of kernel binary:"), + 'kernelp': _("Name of kernel mkinitcpio preset:"), + } + }, + ], +] +)([_("User-Name"), _("Password"), _("Group"), + "UID", _("'skel' directory"), + _("Additional Groups"), _("Expert options")]) diff --git a/build_tools/larch8/larch0/gui/layouts/page_main.uim b/build_tools/larch8/larch0/gui/layouts/page_main.uim new file mode 100644 index 0000000..7d79c20 --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_main.uim @@ -0,0 +1,99 @@ +# page_main.uim - The layout for the main window +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.10.10 +[ + ['Window', ':larch', + { 'title': 'larch', 'size': '600_400', + 'icon': 'images/larchicon.png', + 'closesignal': '$$$uiclose$$$', + 'layout': + ['VBOX', + ['HBOX', + ':image', + ['VBOX', + ['HBOX', ':header', '*'], + ['HBOX', ':showlog', ':docs', '*', ':quit'], + ] + ], + ':tabs' + ] + + } + ], + + # - Header + ['Label', ':image', + { 'image': 'images/larch80.png' + } + ], + ['Label', ':header', + { 'markup': ['h1', ['color', '#c55500', ['em', 'larch '], + _("Live Arch Linux Construction Kit")]] + } + ], + ['Button', ':showlog', + { 'text': _("View Log"), + 'tt': _("This button switches to the log viewer"), + }, + 'clicked' + ], + ['Button', ':docs', + { 'text': _("Help"), + 'tt': _("This button switches to the documentation viewer"), + }, + 'clicked' + ], + ['Button', ':quit', + { 'text': _("Quit"), + 'tt': _("Stop the current action and quit the program"), + 'clicked': '$$$uiquit$$$' + }, + ], + +#TODO + # - Main widget + ['Stack', ':tabs', + { 'pages': [':notebook', 'progress:page', 'log:page', + 'doc:page', 'edit:page'] + } + ], + + # - - The main page of the Stack + ['Notebook', ':notebook', + { 'tabs': [ + [':page_settings', _("Project Settings")], + [':page_installation', _("Installation")], + [':page_larchify', _("Larchify")], + [':page_medium', _("Make Medium")], + ] + }, + 'changed' + ], + + ['DATA', 'main_page_data', + { 'messages': + { 'authfail': _("Authentication failure"), + 'getpw': _("Enter the password to run as administrator:"), + } + }, + ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/page_medium.uim b/build_tools/larch8/larch0/gui/layouts/page_medium.uim new file mode 100644 index 0000000..8f081bb --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_medium.uim @@ -0,0 +1,247 @@ +# page_medium.uim - The layout for the medium building page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.10.24 + +(lambda MEDIA, SRC: +[ + ['Page', ':page_medium', + { 'layout': + ['VBOX', '*', + ['HBOX', ':mtlabel', 'VLINE', ':srcframe', 'VLINE', ':vlabel',], + '*', 'HLINE', '*', + ['HBOX', ':mediumprofile', 'VLINE', ':mediumopts'], + 'HLINE', + ['HBOX', '*', ':make_medium'] + ] + } + ], + + ['Frame', ':srcframe', + { 'text': _("Source of the larch system"), + 'tt': _("Normally the larchified Arch install,\n" + "but it could also be an existing larch medium"), + 'layout': + ['VBOX', ['HBOX', ':srctype', ':selectsrc'], ':srclocation'] + }, + ], + ['ComboBox', ':srctype', + { 'width': 180, + 'set': [m[1] for m in SRC], + 'tt': _("Select where the system to put on the medium comes from") + }, + 'changed' + ], + ['LineEdit', ':srclocation', + { 'ro': True, + #'width': 250, + 'tt': _("The larch data comes from here") + } + ], + ['Button', ':selectsrc', + { 'text': _("Choose"), + 'tt': _("Select the source of the larch system") + }, + 'clicked' + ], + + ['Frame', ':mtlabel', + { 'text': _("Type of medium to build"), + 'tt': _("A writeable medium could be a USB-stick or a hard drive partition, for example"), + 'layout': + ['VBOX', ['HBOX', ':mediumtype', ':selectpart'], ':larchpart'] + }, + ], + ['ComboBox', ':mediumtype', + { 'width': 180, + 'set': [m[1] for m in MEDIA], + 'tt': _("Select the larch source.") + }, + 'changed' + ], + + ['Frame', ':mediumprofile', + { 'text': _("Medium profile"), + 'tt': _("Settings which are saved in the profile"), + 'layout': + ['VBOX', ':bootlines', ':syslinuxtemplate', ':cdroot'] + }, + ], + ['Button', ':bootlines', + { 'text': _("Edit boot entries"), + 'tt': _("Edit the file determining the boot entries") + }, + 'clicked' + ], + ['Button', ':syslinuxtemplate', + { 'text': _("Edit bootloader template"), + 'tt': _("Edit the syslinux/extlinux/isolinux configuration file\n" + "(the larch boot entries are handled separately)") + }, + 'clicked' + ], + ['Button', ':cdroot', + { 'text': _("Edit medium files"), + 'tt': _("Open a file browser on the profile's 'cd-root' folder") + }, + 'clicked' + ], + +#++++ + ['LineEdit', ':larchpart', + { 'ro': True, + #'width': 250, + 'tt': _("The partition or iso-file to receive the larch system") + } + ], + ['Button', ':selectpart', + { 'text': _("Choose"), + 'tt': _("Select the destination medium or file") + }, + 'clicked' + ], + + ['Frame', ':mediumopts', + { 'text': _("Medium options"), + 'tt': _("Optional settings for larch partitions"), + 'layout': + ['VBOX', + ['GRID', ['+', ':detection_l', ':detection', '-'], + ['+', ':persist', 'VLINE,3', '*'], + ['+', ':ovl_journal', '|', ':nombr'], + ['+', ':pformat', '|', ':nolarchboot'], + ], + ], + }, + 'toggled' + ], + ['CheckBox', ':pformat', + { 'text': _("Format medium"), + 'tt': _("Normally the medium should be formatted before writing, but IF YOU\n" + "KNOW WHAT YOU ARE DOING it might be useful to skip the formatting") + }, + 'toggled' + ], + ['CheckBox', ':nombr', + { 'text': _("Don't set MBR"), + 'tt': _("The bootloader will be installed to the partition only," + " leaving the mbr untouched\n" + "(you'll need to provide some other means of booting)") + }, +# 'toggled' + ], + ['CheckBox', ':nolarchboot', + { 'text': _("Not bootable via search"), + 'tt': _("Don't create the file 'larch/larchboot':\n" + " the medium will only be bootable by uuid, label" + " or partition name") + }, + 'toggled' + ], + ['CheckBox', ':persist', + { 'text': _("Data persistence"), + 'tt': _("Support data persistence by using the medium as a writeable overlay"), + }, + 'toggled' + ], + ['CheckBox', ':ovl_journal', + { 'text': _("Use journalling"), + 'tt': _("The file-system containing the overlay can use journalling\n" + "to aid data integrity") + }, + 'toggled' + ], + + ['Label', ':detection_l', + { 'text': _("Medium Detection:") + } + ], + ['ComboBox', ':detection', + { 'tt': _("Choose how the boot scripts determine where to" + " look for the larch system"), + }, + 'changed' + ], +#---- + +#+ + ['Frame', ':vlabel', + { 'text': _("Volume Label:"), + 'tt': _("The 'label' given to the created medium"), + 'layout': ['VBOX', ':vlabele', ':vlabelb'] + } + ], + ['LineEdit', ':vlabele', + { 'ro': True, + 'width': 120, + 'tt': _("The length may not exceed 16 bytes," + " 11 for vfat") + } + ], + ['Button', ':vlabelb', + { 'text': _("Change"), + 'tt': _("Enter a new label for the volume, empty to use default") + }, + 'clicked' + ], +#- + + ['Button', ':make_medium', + { 'text': _("Write the larch medium"), + 'tt': _("The larch image will be written to the 'iso' file" + " or to the partition, as selected") + }, + 'clicked' + ], + + ['DATA', 'medium_page_data', + { 'messages': + { 'parts_t': _("Choose unmounted partition"), + 'parts_src': _("Select larch source partition"), + 'parts_dst': _("Device to receive larch system\n" + "WARNING: Be very careful in choosing here,\n" + "if you choose the wrong one you might\n" + "seriously damage your system!"), + 'msg_med': _("Invalid larch medium folder: %s"), + 'prompt_label': _("Volume label (clear to use default):"), + 'isopath': _("Save 'iso' to ..."), + 'isoget': _("Read 'iso' file"), + 'media': [m[0] for m in MEDIA], + 'sources': [m[0] for m in SRC], + 'detectionmodes': + { 'label': "LABEL", + 'uuid': "UUID", + 'device': _("Device (e.g. /dev/sdb1)"), + 'search': _("Search (for 'larchboot' file)") + }, + } + }, + ], +] +)( [ ('medium-w', _("writeable medium")), + ('medium-iso', "iso file"), + ('medium-boot', _("boot iso")), + ], + [ ('larchified', _("larchify output")), + ('device', _("other medium")), + ('isofile', _("iso file")), + ], +) diff --git a/build_tools/larch8/larch0/gui/layouts/page_project.uim b/build_tools/larch8/larch0/gui/layouts/page_project.uim new file mode 100644 index 0000000..d48295e --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/page_project.uim @@ -0,0 +1,188 @@ +# page_project.uim - The layout for the project settings page +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.08.15 + +[ + ['Page', ':page_settings', + { 'layout': + ['VBOX', + ':settings_profile', + '*', + ':options_advanced', + '*' + ] + } + ], + # - - - - The profile selection frame + ['Frame', ':settings_profile', + { 'text': _("Profile"), + 'layout': + ['VBOX', + ['HBOX', ':choose_profile', ':choose_profile_combo', + '*', ':profile_browse'], + ['HBOX', ':profile_rename', ':profile_clone', + ':profile_delete', ':profile_save'] + ] + } + ], + ['Label', ':choose_profile', + { 'text': _("Select:"), + 'align': 'right' + } + ], + ['ComboBox', ':choose_profile_combo', + { 'width': 200, + 'tt': _("Choose a profile from those already in your" + " larch working folder") + }, + 'changed' + ], + ['Button', ':profile_browse', + { 'text': _("Browse for Profile"), + 'tt': _("Fetch a profile from the file-system") + }, + 'clicked' + ], + ['Button', ':profile_clone', + { 'text': _("Clone"), + 'tt': _("Make a copy of the current profile under a new name") + }, + 'clicked' + ], + ['Button', ':profile_rename', + { 'text': _("Rename"), + 'tt': _("Rename the current profile") + }, + 'clicked' + ], + ['Button', ':profile_delete', + { 'text': _("Delete"), + 'tt': _("Delete an unused profile") + }, + 'clicked' + ], + ['Button', ':profile_save', + { 'text': _("Copy to ..."), + 'tt': _("Copy the current profile to somehere else") + }, + 'clicked' + ], + + # - - - - Advanced project options + ['OptionalFrame', ':options_advanced', + { 'text': _("Advanced Project Options"), + 'layout': + ['HBOX', + #['HBOX', '*', ':lplat', ':platform'], + ['GRID', + ['+', ':choose_project', ':choose_project_combo'], + ['+', ':new_project', ':project_delete'] + ], + 'VLINE,3', + ':installation_path' + ] + } + ], +# Pending better support in Arch/pacman +# ['Label', '::lplat', +# { 'text': _("Platform (processor architecture):") +# } +# ], +# ['ComboBox', ':platform', +# { 'tt': _("Which processor architecture?") +# }, +# 'changed' +# ], + ['Label', ':choose_project', + { 'text': _("Choose Existing Project:") + } + ], + ['ComboBox', ':choose_project_combo', + { 'tt': _("Choose a project from those already defined"), + 'width': 120 + }, + 'changed' + ], + ['Button', ':new_project', + { 'text': _("New Project"), + 'tt': _("Create a new project") + }, + 'clicked' + ], + ['Button', ':project_delete', + { 'text': _("Delete"), + 'tt': _("Delete a project") + }, + 'clicked' + ], + ['Frame', ':installation_path', + { 'text': _("Installation Path"), + 'layout': + ['HBOX', ':installation_path_show', + ':installation_path_change'] + } + ], + ['LineEdit', ':installation_path_show', + { 'ro': True, + 'tt': _("The root directory of the Arch installation" + " to larchify") + } + ], + ['Button', ':installation_path_change', + { 'text': _("Change"), + 'tt': _("Change the root directory of the Arch installation") + }, + 'clicked' + ], + + ['DATA', 'project_page_data', + { 'messages': + { 'file_ps': _("Select profile source folder"), + 'prompt_pr': _("Destination profile exists - replace it?"), + 'prompt_pn': _("Enter new name for current profile:"), + 'prompt_pe': _("Profile '%s' exists already"), + 'msg_pu': _("Can't rename the profile," + " it is in use by other projects"), + 'file_sp': _("Save profile folder"), + 'prompt_dr': _("Destination exists - replace it?"), + 'prompt_dp': _("Select the profile for deletion"), + 'delprof': _("Remove Profile"), + 'msg_npf': _("There are no profiles which can" + " be deleted - all are in use"), + 'msg_dpff': _("Couldn't delete profile '%s' -" + " check permissions"), + 'prompt_ip': _("An empty path here will reset to the default.\n" + " WARNING: Double check your path -\n" + " If you make a mistake here it could destroy your system!" + "\n\nEnter new installation path:"), + 'prompt_np': _("Enter name for new project:"), + 'msg_pe': _("Project '%s' already exists"), + 'prompt_pd': _("Select the project for deletion"), + 'delproj': _("Remove Project"), + 'msg_np': _("There are no projects which can be deleted"), + 'msg_npd': _("'%s' is not a profile folder"), + 'msg_piu': _("The path '%s' is already in use, not saving"), + 'prompt_clone': _("Enter the name for the new profile:"), + } + }, + ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/profile_browse.uim b/build_tools/larch8/larch0/gui/layouts/profile_browse.uim new file mode 100644 index 0000000..1d5912b --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/profile_browse.uim @@ -0,0 +1,94 @@ +# profile_browse.uim - The layout for the profile browser widget +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.08.15 + +[ + ['Dialog', 'dialog:profile_browser', + { 'title': None, + 'layout': + ['VBOX', + 'dpb:label_f', '*,5', + ['HBOX', 'dpb:browse', 'VLINE,10', 'dpb:examples'], + ['HBOX', 'dpb:source_l', 'dpb:source'], + ['HBOX', 'dpb:name_l', 'dpb:name', 'dpb:name_s'], + 'HLINE', + 'dpb:dbuttons' + ], + } + ], + ['Frame', 'dpb:label_f', + { 'layout': ['VBOX', 'dpb:label'], + } + ], + ['Label', 'dpb:label', + { 'text': _("You can browse the file-system for the source profile" + " directory,\nor else choose one of the examples"), + } + ], + ['Frame', 'dpb:examples', + { 'text': _("Example Profiles"), + 'tt': _("Here you can select one of the example profiles to use as a starting point"), + 'layout': ['VBOX', 'dpb:example_list'], + } + ], + ['ComboBox', 'dpb:example_list', + { + }, + 'changed' + + ], + ['Button', 'dpb:browse', + { 'text': _("Browse file-system"), + 'tt': _("Open a file dialog to search for the profile"), + }, + 'clicked' + ], + ['Label', 'dpb:source_l', + { 'text': _("Source:"), + } + ], + ['LineEdit', 'dpb:source', + { 'ro': True, + 'tt': _("The path from which the profile directory will be copied"), + } + ], + ['Label', 'dpb:name_l', + { 'text': _("New name:") + } + ], + ['LineEdit', 'dpb:name', + { 'ro': True, + 'tt': _("The name the profile will be given in the work area"), + } + ], + ['Button', 'dpb:name_s', + { 'text': _("Change"), + 'tt': _("Open a dialog to change the new profile's name"), + }, + 'clicked' + ], + ['DialogButtons', 'dpb:dbuttons', + { 'buttons': ('Ok', 'Cancel'), + 'dialog': 'dialog:profile_browser', + }, + ], +] diff --git a/build_tools/larch8/larch0/gui/layouts/progress.uim b/build_tools/larch8/larch0/gui/layouts/progress.uim new file mode 100644 index 0000000..62ae9e2 --- /dev/null +++ b/build_tools/larch8/larch0/gui/layouts/progress.uim @@ -0,0 +1,64 @@ +# progress.uim - The layout for the progress widget +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.05.22 + +[ + ['Frame', 'progress:page', + { 'layout': + ['VBOX', + 'progress:header', + ['HBOX', + 'progress:text', + ['VBOX', 'progress:cancel', '*', 'progress:done'] + ], + 'progress:progress' + ] + } + ], + ['Label', 'progress:header', + { 'markup': ['', ['h2', _("Processing ...")], + ['p', _("Here you can follow the detailed, low-level" + " progress of the commands.")]] + } + ], + ['TextEdit', 'progress:text', + { 'ro': True + } + ], + ['LineEdit', 'progress:progress', + { 'ro': True, + 'tt': _("An indication of the progress of the current" + " operation, if possible") + } + ], + ['Button', 'progress:cancel', + { 'text': _("Cancel"), + 'tt': _("Stop the current action"), + 'clicked': '$$$cancel$$$' + } + ], + ['Button', 'progress:done', + { 'text': _("Done"), + 'clicked': '' + } + ], +] diff --git a/build_tools/larch8/larch0/gui/project.py b/build_tools/larch8/larch0/gui/project.py new file mode 100644 index 0000000..6dc0000 --- /dev/null +++ b/build_tools/larch8/larch0/gui/project.py @@ -0,0 +1,446 @@ +# project.py - Project management +# +# (c) Copyright 2010 Michael Towers (larch42 at googlemail dot com) +# +# This file is part of the larch project. +# +# larch is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# larch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with larch; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +#---------------------------------------------------------------------------- +# 2010.10.24 + +from config import * +import os, shutil, pwd +from glob import glob +from subprocess import call +from userinfo import Userinfo + +CONFIG_DIR = '.config/larch' # within the user's home directory +APP_CONF = 'app.conf' # within larch config directory +PROJECT_CONF = 'project.conf' # within project directory +PROJECT0 = 'larch-0' # default project +PROFILE0 = 'default' # default profile + +# Some default values for the project config file +DEFAULTS = { 'installation_dir' : '', + 'pacman_cache' : '/var/cache/pacman/pkg', + 'profile' : PROFILE0, + 'profile_browse_dir': '', # => use default + 'installrepo' : '', + 'medium_iso' : '', # 'Yes' | '' + 'medium_search' : 'label', # 'search' | 'uuid' | 'label' | 'device' + 'boot_nosearch' : '', # 'Yes' | '' + 'do_persist' : 'Yes', # 'Yes' | '' + 'journal' : 'Yes', # 'Yes' | '' + 'medium_label' : '', # => fetch default + 'isosavedir' : '', + 'isofile' : '', + 'bootisofile' : '', + 'bootisolabel' : '', + 'do_format' : 'Yes', # 'Yes' | '' + } + + +# Default values for the application config file +APP_DEFAULTS = { + 'project' : PROJECT0, + 'filebrowser' : 'xdg-open $', + } + + + +class ProjectManager: + def __init__(self): + add_exports( ( + ('getitem', self.getitem), + ('getbool', self.getbool), + ('setitem', self.setitem), + ('setbool', self.setbool), + ('get_projects', self.list_projects), + ('get_profiles', self.list_profiles), + ('get_installation_dir', self.get_ipath), + ('set_installation_dir', self.set_ipath), + ('testlarchify', self.testlarchify), + ('set_project', self.set_projectname), + ('get_project', self.get_projectname), + ('delete_project', self.delete_project), + ('delete_profile', self.delete_profile), + ('list_free_projects', self.list_free_projects), + ('list_free_profiles', self.list_free_profiles), + ('get_new_profile', self.get_new_profile), + ('rename_profile', self.rename_profile), + ('can_rename_profile', self.can_rename_profile), + ('save_profile', self.save_profile), + ('get_profile', self.get_profile), + ('set_profile', self.set_profile), + ('get_example_profiles', self.get_example_profiles), + ('set_profile_browse_dir', self.set_profile_browse_dir), + ('get_mediumlabel', self.get_mediumlabel), + ('set_mediumlabel', self.set_mediumlabel), + ('getisosavedir', self.getisosavedir), + ('getisofile', self.getisofile), + ('getbootisofile', self.getbootisofile), + ('get_bootisolabel', self.get_bootisolabel), + ('set_bootisolabel', self.set_bootisolabel), + ('newUserinfo', self.newUserinfo), + ('allusers', self.allusers), + ('getuserinfo', self.getuserinfo), + ('newuser', self.newuser), + ('userset', self.userset), + ('deluser', self.deluser), + ('listskels', self.listskels), + ('saveusers', self.saveusers)) + ) + + + def init(self): + self.projects_base = os.path.join(os.environ['HOME'], CONFIG_DIR) + self.profiles_dir = os.path.join(self.projects_base, 'myprofiles') + # Ensure the presence of the larch default project folder + dpf = '%s/p_%s' % (self.projects_base, PROJECT0) + if not os.path.isdir(dpf): + os.makedirs(dpf) + # Ensure the presence of the profiles folder and the 'default' profile + if not os.path.isdir(self.profiles_dir): + os.mkdir(self.profiles_dir) + self.default_profile_dir = os.path.join(self.profiles_dir, PROFILE0) + if not os.path.isdir(self.default_profile_dir): + call(['cp', '-a', base_dir + '/profiles/'+ PROFILE0, + self.profiles_dir]) + + # The application configs + self.aconfig_file = os.path.join(self.projects_base, APP_CONF) + self.aconfig = self.getconfig(self.aconfig_file) + + # The project-specific configs + self.set_projectname(self.appget('project')) + + + def get_projectname(self): + return (True, self.project_name) + + def set_projectname(self, name): + self.project_dir = os.path.join(self.projects_base, 'p_' + name) + plist = self.list_projects()[1] + if name not in plist: + os.mkdir(self.project_dir) + + self.pconfig_file = os.path.join(self.project_dir, PROJECT_CONF) + self.pconfig = self.getconfig(self.pconfig_file) + self.profile_name = self.get('profile') + + self.profile_path = os.path.join(self.profiles_dir, self.profile_name) + self.appset('project', name) + self.project_name = name + return (True, None) + + def delete_project(self, name): + # This should probably be run as root, in case the build directory + # is inside it ... cross that bridge when we come to it! + r = call(['rm', '-r', '--interactive=never', + os.path.join(self.projects_base, 'p_' + name)]) + return (True, r == 0) + + + def delete_profile(self, name): + r = call(['rm', '-r', '--interactive=never', + os.path.join(self.profiles_dir, name)]) + return (True, r == 0) + + + def get_profile(self): + return (True, self.profile_name) + + def set_profile(self, name): + self.set('profile', name) + self.profile_name = name + self.profile_path = os.path.join(self.profiles_dir, self.profile_name) + return (True, None) + + + def rename_profile(self, name): + os.rename(self.profile_path, os.path.join(self.profiles_dir, name)) + self.set_profile(name) + return (True, None) + + + def get_new_profile(self, src, name): + if not os.path.isfile(src + '/addedpacks'): + return (True, False) + dst = os.path.join(self.profiles_dir, name) + call(['rm', '-rf', dst]) + shutil.copytree(src, dst, symlinks=True) + self.set_profile(name) + return (True, True) + + + def get_example_profiles(self): + pd = base_dir + '/profiles' + return (True, (pd, os.listdir(pd))) + +# location of working profiles +# self.projects_base + '/myprofiles', + + +# What about not allowing changes to the default profile? +# That would mean also no renaming? +# One would have to copy a profile into the project before going +# any further ... +# Is it right to share profiles between projects? (Probably) + +#+++++++++++++++++++++++++++++++++++++++++++++++ +### A very simple configuration file handler + def getconfig(self, filepath): + cfg = {} + if os.path.isfile(filepath): + fh = open(filepath) + for line in fh: + ls = line.split('=', 1) + if len(ls) > 1: + cfg[ls[0].strip()] = ls[1].strip() + return cfg + + + def saveconfig(self, filepath, config): + fh = open(filepath, 'w') + ci = config.items() + ci.sort() + for kv in ci: + fh.write('%s = %s\n' % kv) + fh.close() +### +#----------------------------------------------- + + def list_projects(self): + projects = [p[2:] for p in os.listdir(self.projects_base) + if p.startswith('p_')] + projects.sort() + return (True, projects) + + + def list_free_projects(self): + """This returns a list of projects which are free for (e.g.) deletion. + """ + plist = self.list_projects()[1] + plist.remove(PROJECT0) # this one is not 'free' + if self.project_name in plist: + plist.remove(self.project_name) + return (True, plist) + + + def list_profiles(self): + profiles = [d for d in os.listdir(self.profiles_dir) if + os.path.isfile(os.path.join(self.profiles_dir, d, 'addedpacks'))] + profiles.sort() + return (True, profiles) + + + def list_free_profiles(self): + """This returns a list of profiles which are not in use by any project. + """ + plist = self.list_profiles()[1] + plist.remove(PROFILE0) # this one is not 'free' + for project in self.list_projects()[1]: + cfg = self.getconfig(os.path.join(self.projects_base, + 'p_' + project, PROJECT_CONF)) + p = cfg.get('profile') + if p in plist: + plist.remove(p) + return (True, plist) + + + def can_rename_profile(self): + if self.profile_name == PROFILE0: + return (True, False) + for project in self.list_projects()[1]: + if project != self.project_name: + cfg = self.getconfig(os.path.join(self.projects_base, + 'p_' + project, PROJECT_CONF)) + if self.profile_name == cfg.get('profile'): + return (True, False) + return (True, True) + + + def save_profile(self, path, force): + if path[0] != '/': + # cloning, only the profile name is passed + path = os.path.join(self.profiles_dir, path) + else: + # copying, the destination will have the same name + if os.path.basename(path) != self.profile_name: + path = os.path.join(path, self.profile_name) + if os.path.exists(path): + if force: + call(['rm', '-rf', path]) + elif os.path.isfile(os.path.join(path, 'addedpacks')): + # This is an existing profile + return (True, False) + else: + # This location is otherwise in use + return (True, None) + shutil.copytree(self.profile_path, path, symlinks=True) + return (True, True) + + + def set_profile_browse_dir(self, path): + if os.path.isfile(os.path.join(path, 'addedpacks')): + # Don't set the profile browse path to a profile directory, + # but rather tp the containing directory + path = os.path.dirname(path) + self.set('profile_browse_dir', path) + + + def appget(self, item): + """Read an entry in the application configuration. + """ + v = self.aconfig.get(item) + if v: + return v + elif APP_DEFAULTS.has_key(item): + return APP_DEFAULTS[item] + debug("Unknown application configuration option: %s" % item) + assert False + + def appset(self, item, value): + """Set an entry in the application configuration. + """ + self.aconfig[item] = value.strip() + self.saveconfig(self.aconfig_file, self.aconfig) + + + def getitem(self, item): + return (True, self.get(item)) + + def getbool(self, item): + return (True, self.get(item) == 'Yes') + + def setitem(self, item, value): + self.set(item, value) + return (True, None) + + def setbool(self, item, on): + self.set(item, 'Yes' if on else 'No') + return (True, None) + + + def get(self, item): + """Read an entry in the project configuration. + """ + v = self.pconfig.get(item) + if v: + return v + elif DEFAULTS.has_key(item): + return DEFAULTS[item] + debug("Unknown configuration option: %s" % item) + assert False + + def set(self, item, value): + """Set an entry in the project configuration. + """ + self.pconfig[item] = value.strip() + self.saveconfig(self.pconfig_file, self.pconfig) +# return True + + + def get_ipath(self): + ip = self.get('installation_dir') + if not ip: + ip = self.set_ipath('')[1] + return (True, ip) + + def set_ipath(self, path): + path = path.strip() + if path: + path = '/' + path.strip('/') + else: + path = os.environ['HOME'] + '/larch_build' + self.set('installation_dir', path) + return (True, path) + + + def testlarchify(self): + ipath = self.get_ipath()[1] + path = ipath + CHROOT_DIR_MEDIUM + bl = [] + nosave = False + ok = (os.path.isfile(path + '/larch/system.sqf') + and os.path.isfile(ipath + SYSLINUXDIR + '/isolinux.bin')) + return (True, (path, ok)) + + + def newUserinfo(self): + self.userInfo = Userinfo(self.profile_path) + return (True, None) + + def allusers(self): + return (True, self.userInfo.allusers()) + + def getuserinfo(self, user, fields): + return (True, self.userInfo.userinfo(user, fields)) + + def newuser(self, user): + return (True, self.userInfo.newuser(user)) + + def saveusers(self): + return (True, self.userInfo.saveusers()) + + def userset(self, uname, field, text): + self.userInfo.userset(uname, field, text) + return (True, None) + + def deluser(self, user): + return (True, self.userInfo.deluser(user)) + + def listskels(self): + return (True, glob(self.profile_path + '/skel_*')) + + + def get_mediumlabel(self): + l = self.get('medium_label') + return (True, l if l else LABEL) + + def set_mediumlabel(self, l): + if len(l) > 16: + l = l[:16] + self.set('medium_label', l) + return self.get_mediumlabel() + + def set_bootisolabel(self, l): + if len(l) > 32: + l = l[:32] + self.set('bootisolabel', l) + return self.get_mediumlabel() + + + def getisosavedir(self): + d = self.get('isosavedir') + return (True, d if d else os.environ['HOME']) + + def getisofile(self): + f = self.get('isofile') + return (True, f if f else ISOFILE) + + def getbootisofile(self): + f = self.get('bootisofile') + return (True, f if f else BOOTISO) + + def get_bootisolabel(self): + l = self.get('bootisolabel') + return (True, l if l else BOOTISOLABEL) + + + +import __builtin__ +__builtin__.project_manager = ProjectManager() |