Commits

Anonymous committed 55468ba

[tui] Added bootloaders ui. Not yet functional

  • Participants
  • Parent commits 59e64f9

Comments (0)

Files changed (5)

modules/support/vectorlinux/FSTAB.py

     """ return a list of all partition paths"""
     partitions = []
     for dev in parted.getAllDevices():
-        if not dev.readOnly:
+        if not dev.readOnly and dev.getSize() > 0.0:
            dsk = parted.Disk(dev)
            for part in dsk.partitions:
                # extended partitions are type 2 and have no fs

modules/support/vectorlinux/SKEL.py

         assert login not in ("", None), "You must provide a valid login name"
         self.login = login
 	self.skeldir = skeldir
-	self.targetdir = os.path.join("/home", self.login)
+        if login == 'root':
+            self.targetdir = '/root'
+        else:
+            self.targetdir = os.path.join("/home", self.login)
 
     def _get_skel_description(self, skel=""):
 	DEF={

modules/tui/tui_bootloaders.py

+#!/usr/bin/env python
+
+#    This file is part of VASM.
+#
+#    VASM is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License v3 as published by
+#    the Free Software Foundation.
+#
+#    VASM 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 VASM.  If not, see <http://www.gnu.org/licenses/>.
+
+""" tui_bootloaders.py
+    Text mode module to interact with available bootloaders in the system. """
+
+__author__ = "Moises Henriquez"
+__author_email__ = "moc.liamg@xnl.E0M"[::-1]
+
+import urwid
+import os
+import sys
+if os.getcwd() not in sys.path:
+    sys.path.append(os.getcwd())
+from modules.support.vectorlinux import BOOTLOADERS
+from modules.support.vectorlinux import FSTAB
+from modules.tuisupport import dialogs
+from modules.tuisupport import widgets
+from modules.tuisupport.tui_utils import _
+
+class BootloaderTui(dialogs.ModuleDialog):
+    def __init__(self, parent):
+        self.parent = parent
+        msg = (
+            _("Select which bootloader you would like to use."), "\n",
+            _("WARNING: this tool will over-write the target you selecct below."), "  ",
+            _("Selecting the wrong options here could render your system unbootable."),)
+
+        self.blpicker = widgets.HChoiceBox(
+            label = 'Select Bootloader:',
+            choices = ["LILO", "GRUB2"],
+            on_state_change = self.blpicker_event,
+            on_focus = self.refresh_module_status_text
+            )
+        self.targetpicker = widgets.LabeledPicker(
+            label = "Installation Target:",
+            picker_title = _("Select the installation target for your bootloader."),
+            options = ["Foo", "bar", "foobar", "barfoo"],
+            parent = self,
+            on_focus = self.refresh_module_status_text
+            )
+        self.videosizepicker = widgets.LabeledPicker(
+            label = _("Video Resolution:"),
+            picker_title = _("Select the video resolution for your boot screen."),
+            options = ["FOO", "BAR", "FOO"],
+            parent = self,
+            on_focus = self.refresh_module_status_text
+            )
+        self.videoqualitypicker = widgets.LabeledPicker(
+            label = _("Video Quality:"),
+            picker_title = _("Select your video quality for your boot screen."),
+            options = ["low", "med", "high"],
+            parent = self,
+            on_focus = self.refresh_module_status_text
+            )
+        self.timeoutspinner = widgets.LabeledSpinner(
+            label = "Timeout (seconds):",
+            default_value=5,
+            range_max = 60,
+            on_focus = self.refresh_module_status_text
+            )
+        actionlabel = urwid.Text(_("Select the action to be performed with your bootloader."))
+        self.actionpicker = widgets.HChoiceBox(
+            label = _("Action:"),
+            choices = [
+                _("Install and (re-)create boot menu"),
+                _("Refresh boot menu only")],
+                on_state_change = self.action_toggle_event,
+                on_focus = self.refresh_module_status_text
+            )
+        self.actionpicker.set_value(_("Refresh boot menu only"))
+        
+        _body = [self.blpicker,
+                 self.targetpicker,
+                 self.videosizepicker,
+                 self.videoqualitypicker,
+                 self.timeoutspinner,
+                 widgets.blank,
+                 actionlabel,
+                 self.actionpicker
+                 ]
+        dialogs.ModuleDialog.__init__(
+            self,
+            header = _("System Boot Menu"),
+            desc = [''.join(msg), urwid.Divider('_'),widgets.blank],
+            parent = self.parent,
+            body = _body,
+            buttons = [("Save", self.module_ok_event),
+                ("Cancel", self.parent.return_home)])
+        # Set the default values
+        bootsec = BOOTLOADERS.get_current_bootsector()
+        currentroot = bootsec[:len('/dev/sda'):]
+        mbr_read = BOOTLOADERS.MBR(currentroot).read_bootloader().upper() or "LILO"
+        self.blpicker.set_value(mbr_read)
+        self.populate_pickers(mbr_read)
+        # Move the focus to the body section of the module
+        self.set_focus('body')
+
+    def _check_selections(self):
+        # check if target has a value
+        assert self.targetpicker.get_value().startswith("/"),\
+            ' '.join((
+                _("Select a valid target from the options list."),
+                _("Access the options list by pressing Enter or Space on the selection area")))
+
+    def module_ok_event(self, widget=None):
+        """ Triggered when "Save" is pressed on this module """
+        try:
+            self._check_selections()
+        except AssertionError as e:
+            dia = dialogs.Error(
+                parent = self,
+                message = "%s"% e,
+                buttons = [("OK", self.return_to_module)]
+                )
+            return self.pop_dialog(dia)
+
+        bootloader = self.blpicker.get_value()
+        target = self.targetpicker.get_value()
+        vgasize = self.videosizepicker.get_value()
+        vgaquality = self.videoqualitypicker.get_value()
+        timeout = self.timeoutspinner.get_value()
+        action = self.actionpicker.get_value()        
+        dia = dialogs.Info(
+            parent = self,
+            message = ''.join(
+                ("Install %s to %s"% (bootloader, target),
+                "Timeout=%s"% timeout,
+                "Video size=%s"% vgasize,
+                "Quality=%s"% vgaquality,
+                "action=%s"% action,
+                _("Are you sure you want to continue?")),
+                ),
+            buttons = [("OK", self.return_to_module)]
+            )
+        return self.pop_dialog(dia)
+
+    def do_save_changes(self, widget=None):
+        pass
+
+    def refresh_module_status_text(self, widget=None):
+        if isinstance(widget, widgets.SensibleLabel):
+            return self.set_status_text('Press Enter/Space to activate')
+        elif isinstance(widget, widgets.TimeSpinner):
+            return self.set_status_text("Use +/- to increate / decrease value")
+        elif isinstance(widget, urwid.RadioButton):
+           return self.set_status_text("Press Enter/Space to select")            
+
+    def action_toggle_event(self, radio=None, new_state=None, user_data=None):
+        pass
+    
+    def populate_pickers(self, bootloader):
+        # populate the targets.
+        partitions = FSTAB.list_all_system_partitions()
+        drives = FSTAB.list_all_system_drives()
+        if bootloader == "LILO":
+            targets = partitions
+            targets.extend(drives)
+            targets.sort()
+            self.targetpicker.set_options(targets)
+            self.videosizepicker.set_options([
+                "640x480","800x600","1024x768"
+                ])
+            self.videoqualitypicker.set_options(["low", "med", "high"])
+        elif bootloader == "GRUB2":
+            self.targetpicker.set_options(drives)
+            self.videoqualitypicker.set_options(["med"])
+        self.videosizepicker.set_value("800x600")
+        self.videoqualitypicker.set_value("med")
+        # We want to force the target to reset because we dont want
+        # an invalid (ie, bootsector) value in the target when grub is selected
+        return self.targetpicker.reset_value()        
+
+    def blpicker_event(self, radio=None, new_state=None, user_data=None):
+        if new_state is True:
+            return self.populate_pickers(radio.get_label())
+            dia = dialogs.Info(radio.get_label(), parent=self.parent,
+                                   buttons = [("OK", self.return_to_module)])
+            return self.pop_dialog(dia)
+
+def __vasm_test__():
+    return os.geteuid() == 0
+
+def __run__(parent):
+    mod = BootloaderTui(parent)
+    parent.pop_module(mod)
+
+VASM_CATEGORY = "System"
+VASM_LABEL = "Sytem Boot Menu"

modules/tui/tui_skel.py

 if os.getcwd() not in sys.path:
     sys.path.append(os.getcwd())
 from modules.support.vectorlinux import SKEL
+from modules.support.vectorlinux import USERADMIN
 from modules.tuisupport import dialogs
 from modules.tuisupport import widgets
 from modules.tuisupport.tui_utils import _
         self.parent = parent
         msgx = _("Select which settings you would like to reset. "\
                  "WARNING: This cannot be undone.")
-        self.skelmodel = SKEL.UserSkel(os.getenv("USERNAME"))
+        acct = None
+        for member in USERADMIN.UserModel().listUsers():
+            if member.uid == os.geteuid():
+                acct = member
+                break
+        assert acct is not None, "Unable to get user account from system for current user."
+        self.skelmodel = SKEL.UserSkel(acct.login)
         settings = self.skelmodel.list_resettable_settings()
         self._settings_to_reset = []
         _body = []

modules/tuisupport/widgets.py

             if self.on_focus:
                 self.on_focus(self)
         return super(SensibleCheckBox, self).render(size, focus)
-        
+
+class SensibleRadioButton(urwid.RadioButton):
+    def __init__(self, label="", group=None, state=False, on_state_change=None,
+		 user_data=None, on_focus=None):
+	self.on_focus=on_focus
+	urwid.RadioButton.__init__(
+		self,
+		group,
+		label,
+		state,
+		on_state_change,
+		user_data
+		)
+    def selectable(self):
+	return True
+
+    def render(self, size, focus=False):
+	if focus:
+	    if self.on_focus:
+		self.on_focus(self)
+	return super(SensibleRadioButton, self).render(size, focus)
+	
 class CategoryLabel(urwid.Text):
     """ Selectable label used to represent settings categories
     Arguments:
 
 class TimeSpinner(urwid.Text):
     """ Selectable time spinner"""
-    def __init__(self, markup="", rangemax=24):
+    def __init__(self, markup="0", rangemax=24, on_focus=None):
         """ change rangemax to 59 for a minutes/seconds picker """
+	self.on_focus = on_focus
         urwid.Text.__init__(self, markup)
         self.range = range(0, rangemax)
     
         self.set_text(str(newval))
     
     def keypress(self, size, key):
-        if key in ("-", 'down'):
+        if key in ("-", 'page down'):
             return self._go_back()
-        elif key in ("+", 'up'):
+        elif key in ("+", 'page up'):
             return self._go_next()
         return key
 
         return int(self.text)
 
     def render(self, size, focus=False):
+        if self.on_focus:
+            self.on_focus(self)
         return super(TimeSpinner, self).render(size, focus)
 
+class LabeledSpinner(urwid.Columns):
+    def __init__(self, label="", default_value=0, range_max=60, on_focus=None):
+	self.label = urwid.Text(label)
+	self.spinner = TimeSpinner(markup=str(default_value),rangemax = range_max,
+				   on_focus=on_focus)
+	urwid.Columns.__init__(
+		self,[
+		('fixed', len(label) + 1, self.label),
+		('fixed', 4,
+		 urwid.AttrWrap(self.spinner, 'button', 'button sel'))])
+
+    def get_value(self):
+	return self.spinner.get_value()
+
+    def set_value(self, value):
+        return self.spinner.set_value(float(value))
+
+    def set_label(self, markup):
+	return self.label.set_text(markup)
+	
+
 class TimePicker(urwid.Pile):
     def __init__(self):
         now = datetime.datetime.now()
 
 
 class HChoiceBox(urwid.Columns):
-    def __init__(self, label="", choices = []):
+    def __init__(self, label="", choices = [], on_state_change=None,
+		 on_focus=None):
+	self.on_focus=on_focus
 	self.label = urwid.Text(label)
+	self.on_state_change = on_state_change
 	rbgroup = []
-	_widgets = [('fixed', 25, self.label)]
+	_widgets = [('fixed', len(label) + 1, self.label)]
+	rbw = 0
 	self._radios = []
 	for item in choices:
-	    rb = urwid.RadioButton(group=rbgroup, label=item, on_state_change=self.radio_toggle)
+	    rbw = max(rbw, len(item) + 1, 10)
+	    rb = SensibleRadioButton(group=rbgroup, label=item,
+			     on_state_change=self.radio_toggle,
+			     on_focus=on_focus)
+		    #rb = urwid.RadioButton(group=rbgroup, label=item, on_state_change=self.radio_toggle)
 	    self._radios.append(rb)
 	    wrap = urwid.AttrWrap(rb, 'button','button sel')
-	    _widgets.append(('fixed', 15, wrap))
+	    _widgets.append(('fixed', rbw, wrap))
 	urwid.Columns.__init__(self, _widgets)
 
     def radio_toggle(self, radio=None, new_value=None, user_data=None):
+	if self.on_state_change:
+	    self.on_state_change(radio, new_value, user_data)
 	if new_value:
 	    self._selection = radio.get_label()
 	    return
     def set_value(self, value):
 	for item in self._radios:
 	    if item.get_label().upper() == value.upper():
-		item.toggle_state()
-		return
+		return item.toggle_state()
 
-	
+class TuiModule(urwid.Frame):
+    """ Module dialog using a frame.
     
+    Arguments:
+        header: string or urwid widget to be used as a header.
+        desc:   List of strings or widgets to be packed below the header line,
+            but still into the header section.  This allows scrolling action in
+            the body section while keeping the header static.
+        body:   list of widets to be packed into the body of the module display.
+        buttons:    list of (button, callback) tuples.
+        parent:     parent container widget calling this class.
+    
+    """
+    def __init__(self, header="", desc=[], body=None,
+		 buttons=[], parent=None):
+        self.desc = desc
+        self.buttons = buttons
+        self.parent = parent
+        self.statusbar = urwid.Text("")
+        _header = []
+        if type(header) == str:
+            _header.append(urwid.AttrWrap(
+		    urwid.LineBox(urwid.Text(header)), 'header'))
+        else:
+            _header.append(header)
+        
+        # pack the description into the header too
+        for item in desc:
+            if type(item) == str:
+                _header.append(urwid.Text(item))
+            else:
+                _header.append(item)
+        self.header = urwid.Pile(_header)
+        self._body = []
+        if type(body) == urwid.Pile:
+            pile = body
+        else:
+            for item in body:
+                self._body.append(item)
+            _list = [
+                urwid.ListBox(self._body),
+            ]
+            pile = FrameBody(_list)
+        self.footer = self._get_footer()
+        urwid.Frame.__init__(
+            self,
+            body=pile,
+            header=self.header,
+            footer=self.footer, focus_part='footer')
+    
+    def pop_dialog(self, dialog, level=1):
+        """ Send a dialog to the parent so it can be popped up"""
+        return self.parent.pop_module_dialog(dialog, level)
+    
+    def _emulate_left_button(self, times=1):
+        """ emulate a left arrow hit """
+        ml = self.parent._mainloop
+        for x in range(0, times):
+            ml.process_input(['left'])
+            self.keypress((10, 2), 'left')
+
+    def return_to_module(self, caller_widget=None):
+        """ Close all dialogs and return to this module's home """
+        return self.parent.return_to_module(caller_widget)
+    
+    def close_module(self, caller_widget=None):
+        """ Close this module and return home """
+        return self.parent.return_home(caller_widget)
+
+    def release_focus_to_parent(self):
+        """ Return focus to the left column"""
+        return self._emulate_left_button(2)
+    
+    def _trigger_cancel_method(self, widget=None):
+        for bt, cb in self.buttons:
+            if bt in ("Cancel", "Done", "Exit", "Back", "back",
+                      "cancel", "done", "exit", "Home", "home"):
+                return cb(widget)
+        # Look for another way out.
+        return self.release_focus_to_parent()
+    
+    def keypress(self, size, key):
+        """ Handle the keys to change focus """
+        if key == 'tab':
+            if self.focus_part == 'body':   
+                self.set_focus('footer')
+            else:
+                self.set_focus('body')
+        elif key in ("esc", "b"):
+            return self._trigger_cancel_method()
+        
+        elif key in ("page up", "page down"):
+            # pass it to the body
+            if type(self.get_body()) == urwid.Pile:
+                return self.get_body().keypress(size, key)
+        
+        return super(TuiModule, self).keypress(size, key)
+    
+
+    def set_status_text(self, text):
+        """ Set the status text for this module """
+        return self.statusbar.set_text(text)
+    
+    def _get_footer(self):
+        """ Return a pile that can be  used for the window's footer"""
+        buttons = self._get_action_area()
+        statusline = [self.statusbar, buttons]
+        return urwid.Columns(statusline)
+    
+    def _get_action_area(self):
+        buttons = []
+        for but, cb in self.buttons:
+            buttons.append(urwid.LineBox(
+                urwid.AttrWrap(
+                    urwid.Button(but, cb), 'button', 'button sel'
+                ))
+            )
+        bar = urwid.GridFlow(buttons, 15, 0, 0, 'right')
+        return bar
+    
+    def add_button(self, button, callback):
+        """ Add a button to the action area """
+        self.buttons.append((button, callback))
+        self.footer = self._get_footer()
+
+class VChoicePopUp(TuiModule):
+    def __init__(self, title="", options=[], parent=None, on_select=None):
+        self.parent = parent
+        self.on_select = on_select
+
+	TuiModule.__init__(self,
+			   header = urwid.AttrWrap(urwid.Text(title),
+						   'column header'),
+		desc = [blank],
+		body = [blank],
+		buttons = [("OK", self.make_choice),
+                           ("Cancel", self.return_to_module)],
+		parent = parent)
+	self.set_options(options)
+        self.set_focus('body')
+        self._selection = None
+
+    def set_options(self, options):
+	_grp = []
+	_selectors = []
+	for item in options:
+		rb = urwid.RadioButton(group = _grp,
+				       label = item,
+			on_state_change = self._radio_toggle)
+		wrap = urwid.AttrWrap(rb, 'button', 'button sel')
+		_selectors.append(wrap)
+	self.choices = _selectors
+	self.set_body(urwid.ListBox(_selectors))
+
+    def _radio_toggle(self, radio=None, new_state = None, userdata=None):
+        if new_state:
+            self._selection = radio.get_label()
+
+    def _get_action_area(self):
+        buttons = []
+        for but, cb in self.buttons:
+            buttons.append(
+                urwid.AttrWrap(
+                    urwid.Button(but, cb), 'button', 'button sel'
+                    )
+                )
+
+        bar = urwid.GridFlow(buttons, 12, 1, 0, 'right')
+        return bar
+
+    def make_choice(self, widget=None):
+        if self.on_select:
+            self.on_select(self._selection)
+        return self.return_to_module()
+
+
+class LabeledPicker(urwid.Columns):
+    def __init__(self, label="",
+		 picker_title = "",
+		 options = [],
+		 parent=None, on_focus=None):
+	self._default_value = "[ Select ]"
+	self.on_focus = on_focus
+	self._options = options
+	self.parent = parent
+	self.title = picker_title
+	self._label = label
+	self.label = urwid.Text(label)
+	self.selector = SensibleLabel(
+		markup = self._default_value,
+		actuator = self._launch_picker,
+		on_focus = self.on_focus,
+		)
+	selwrap = urwid.AttrWrap(self.selector, 'button', 'button sel')
+	# the picker
+	self.picker = VChoicePopUp(
+		parent = self.parent,
+		title = self.title,
+		options = self._options,
+		on_select = self.set_value
+		)
+	# The width of the first column should be == len(self._label)
+	urwid.Columns.__init__(self,
+			       [('fixed', len(self._label) + 1, self.label),
+			       selwrap]
+			       )
+
+    def reset_value(self):
+	return self.selector.set_value(self._default_value)
+
+    def set_options(self, options):
+	""" Modify the provided options """
+	return self.picker.set_options(options)
+
+    def _launch_picker(self):
+	return self.parent.pop_dialog(self.picker)
+
+    def get_value(self):
+	return self.selector.get_text()[0]
+
+    def set_value(self, value):
+	return self.selector.set_text(value)