qubes-installer-qubes-os/anaconda/iw/osbootwidget.py
2011-03-31 13:40:54 +02:00

398 lines
14 KiB
Python

#
# osbootwidget.py: gui bootloader list of operating systems to boot
#
# Copyright (C) 2001, 2002 Red Hat, Inc. All rights reserved.
#
# This program 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.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author(s): Jeremy Katz <katzj@redhat.com>
#
import gtk
import gobject
import iutil
import parted
import gui
import datacombo
from constants import *
from storage.devices import devicePathToName
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
class OSBootWidget:
"""Widget to display OSes to boot and allow adding new ones."""
def __init__(self, anaconda, parent, blname = None):
self.bl = anaconda.bootloader
self.storage = anaconda.storage
self.parent = parent
self.intf = anaconda.intf
if blname is not None:
self.blname = blname
else:
self.blname = "GRUB"
self.setIllegalChars()
self.vbox = gtk.VBox(False, 5)
label = gtk.Label("<b>" + _("Boot loader operating system list") + "</b>")
label.set_alignment(0.0, 0.0)
label.set_property("use-markup", True)
self.vbox.pack_start(label, False)
box = gtk.HBox (False, 5)
sw = gtk.ScrolledWindow()
sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
sw.set_size_request(300, 100)
box.pack_start(sw, True)
self.osStore = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING,
gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
self.osTreeView = gtk.TreeView(self.osStore)
theColumns = [ _("Default"), _("Label"), _("Device") ]
self.checkboxrenderer = gtk.CellRendererToggle()
column = gtk.TreeViewColumn(theColumns[0], self.checkboxrenderer,
active = 0)
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
self.checkboxrenderer.connect("toggled", self.toggledDefault)
self.checkboxrenderer.set_radio(True)
self.osTreeView.append_column(column)
for columnTitle in theColumns[1:]:
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn(columnTitle, renderer,
text = theColumns.index(columnTitle))
column.set_clickable(False)
self.osTreeView.append_column(column)
self.osTreeView.set_headers_visible(True)
self.osTreeView.columns_autosize()
self.osTreeView.set_size_request(100, 100)
sw.add(self.osTreeView)
self.osTreeView.connect('row-activated', self.osTreeActivateCb)
self.imagelist = self.bl.images.getImages()
self.defaultDev = self.bl.images.getDefault()
self.fillOSList()
buttonbar = gtk.VButtonBox()
buttonbar.set_layout(gtk.BUTTONBOX_START)
buttonbar.set_border_width(5)
add = gtk.Button(_("_Add"))
buttonbar.pack_start(add, False)
add.connect("clicked", self.addEntry)
edit = gtk.Button(_("_Edit"))
buttonbar.pack_start(edit, False)
edit.connect("clicked", self.editEntry)
delete = gtk.Button(_("_Delete"))
buttonbar.pack_start(delete, False)
delete.connect("clicked", self.deleteEntry)
box.pack_start(buttonbar, False)
self.vbox.pack_start(box, False)
self.widget = self.vbox
def setIllegalChars(self):
# illegal characters for boot loader labels
if self.blname == "GRUB":
self.illegalChars = [ "$", "=" ]
else:
self.illegalChars = [ "$", "=", " " ]
def changeBootLoader(self, blname):
if blname is not None:
self.blname = blname
else:
self.blname = "GRUB"
self.setIllegalChars()
self.fillOSList()
# adds/edits a new "other" os to the boot loader config
def editOther(self, oldDevice, oldLabel, isDefault, isRoot = 0):
dialog = gtk.Dialog(_("Image"), self.parent)
dialog.add_button('gtk-cancel', 2)
dialog.add_button('gtk-ok', 1)
dialog.set_position(gtk.WIN_POS_CENTER)
gui.addFrame(dialog)
dialog.vbox.pack_start(gui.WrappingLabel(
_("Enter a label for the boot loader menu to display. The "
"device (or hard drive and partition number) is the device "
"from which it boots.")))
table = gtk.Table(2, 5)
table.set_row_spacings(5)
table.set_col_spacings(5)
label = gui.MnemonicLabel(_("_Label"))
table.attach(label, 0, 1, 1, 2, gtk.FILL, 0, 10)
labelEntry = gtk.Entry(32)
label.set_mnemonic_widget(labelEntry)
table.attach(labelEntry, 1, 2, 1, 2, gtk.FILL, 0, 10)
if oldLabel:
labelEntry.set_text(oldLabel)
label = gui.MnemonicLabel(_("_Device"))
table.attach(label, 0, 1, 2, 3, gtk.FILL, 0, 10)
if not isRoot:
parts = []
for part in self.storage.partitions:
if part.partedPartition.getFlag(parted.PARTITION_LVM) or \
part.partedPartition.getFlag(parted.PARTITION_RAID) or \
not part.partedPartition.active:
continue
parts.append(part)
deviceCombo = datacombo.DataComboBox()
defindex = 0
i = 0
for part in parts:
deviceCombo.append(part.path, part.name)
if oldDevice and oldDevice == part.name:
defindex = i
i = i + 1
deviceCombo.set_active(defindex)
table.attach(deviceCombo, 1, 2, 2, 3, gtk.FILL, 0, 10)
label.set_mnemonic_widget(deviceCombo)
else:
table.attach(gtk.Label(oldDevice), 1, 2, 2, 3, gtk.FILL, 0, 10)
default = gtk.CheckButton(_("Default Boot _Target"))
table.attach(default, 0, 2, 3, 4, gtk.FILL, 0, 10)
if isDefault != 0:
default.set_active(True)
if self.numentries == 1 and oldDevice != None:
default.set_sensitive(False)
else:
default.set_sensitive(True)
dialog.vbox.pack_start(table)
dialog.show_all()
while 1:
rc = dialog.run()
# cancel
if rc in [2, gtk.RESPONSE_DELETE_EVENT]:
break
label = labelEntry.get_text()
if not isRoot:
dev = deviceCombo.get_active_value()
else:
dev = oldDevice
if not label:
self.intf.messageWindow(_("Error"),
_("You must specify a label for the "
"entry"),
type="warning")
continue
foundBad = 0
for char in self.illegalChars:
if char in label:
self.intf.messageWindow(_("Error"),
_("Boot label contains illegal "
"characters"),
type="warning")
foundBad = 1
break
if foundBad:
continue
# verify that the label hasn't been used
foundBad = 0
for key in self.imagelist.keys():
if dev == key:
continue
if self.blname == "GRUB":
thisLabel = self.imagelist[key][1]
else:
thisLabel = self.imagelist[key][0]
# if the label is the same as it used to be, they must
# have changed the device which is fine
if thisLabel == oldLabel:
continue
if thisLabel == label:
self.intf.messageWindow(_("Duplicate Label"),
_("This label is already in "
"use for another boot entry."),
type="warning")
foundBad = 1
break
if foundBad:
continue
# XXX need to do some sort of validation of the device?
# they could be duplicating a device, which we don't handle
if dev in self.imagelist.keys() and (not oldDevice or
dev != oldDevice):
self.intf.messageWindow(_("Duplicate Device"),
_("This device is already being "
"used for another boot entry."),
type="warning")
continue
# if we're editing a previous, get what the old info was for
# labels. otherwise, make it something safe for grub and the
# device name for lilo for lack of any better ideas
if oldDevice:
(oldshort, oldlong, oldisroot) = self.imagelist[oldDevice]
else:
(oldshort, oldlong, oldisroot) = (dev, label, None)
# if we're editing and the device has changed, delete the old
if oldDevice and dev != oldDevice:
del self.imagelist[oldDevice]
# go ahead and add it
if self.blname == "GRUB":
self.imagelist[dev] = (oldshort, label, isRoot)
else:
self.imagelist[dev] = (label, oldlong, isRoot)
if default.get_active():
self.defaultDev = dev
# refill the os list store
self.fillOSList()
break
dialog.destroy()
def getSelected(self):
selection = self.osTreeView.get_selection()
(model, iter) = selection.get_selected()
if not iter:
return None
dev = devicePathToName(model.get_value(iter, 2))
label = model.get_value(iter, 1)
isRoot = model.get_value(iter, 3)
isDefault = model.get_value(iter, 0)
return (dev, label, isDefault, isRoot)
def addEntry(self, widget, *args):
self.editOther(None, None, 0)
def deleteEntry(self, widget, *args):
rc = self.getSelected()
if not rc:
return
(dev, label, isDefault, isRoot) = rc
if not isRoot:
del self.imagelist[dev]
if isDefault:
keys = self.imagelist.keys()
keys.sort()
self.defaultDev = keys[0]
self.fillOSList()
else:
self.intf.messageWindow(_("Cannot Delete"),
_("This boot target cannot be deleted "
"because it is for the %s "
"system you are about to install.")
%(productName,),
type="warning")
def editEntry(self, widget, *args):
rc = self.getSelected()
if not rc:
return
(dev, label, isDefault, isRoot) = rc
self.editOther(dev, label, isDefault, isRoot)
# the default os was changed in the treeview
def toggledDefault(self, data, row):
iter = self.osStore.get_iter((int(row),))
dev = self.osStore.get_value(iter, 2)
self.defaultDev = devicePathToName(dev)
self.fillOSList()
# fill in the os list tree view
def fillOSList(self):
self.osStore.clear()
keys = self.imagelist.keys()
keys.sort()
for dev in keys:
(label, longlabel, fstype) = self.imagelist[dev]
device = self.storage.devicetree.getDeviceByName(dev)
if self.blname == "GRUB":
theLabel = longlabel
else:
theLabel = label
# if the label is empty, remove from the image list and don't
# worry about it
if not theLabel:
del self.imagelist[dev]
continue
isRoot = 0
rootDev = self.storage.rootDevice
if rootDev and rootDev.name == dev:
isRoot = 1
devPath = getattr(device, "path", "/dev/%s" % dev)
iter = self.osStore.append()
self.osStore.set_value(iter, 1, theLabel)
self.osStore.set_value(iter, 2, devPath)
self.osStore.set_value(iter, 3, isRoot)
if self.defaultDev == dev:
self.osStore.set_value(iter, 0, True)
else:
self.osStore.set_value(iter, 0, False)
self.numentries = len(keys)
def osTreeActivateCb(self, view, path, col):
self.editEntry(view)
def getWidget(self):
return self.widget
# FIXME: I really shouldn't have such intimate knowledge of
# the bootloader object
def setBootloaderImages(self):
"Apply the changes from our list into the self.bl object"
# make a copy of our image list to shove into the bl struct
self.bl.images.images = {}
for key in self.imagelist.keys():
self.bl.images.images[key] = self.imagelist[key]
self.bl.images.setDefault(self.defaultDev)