# Copyright 1999-2005 Gentoo Foundation
# This source code is distributed under the terms of version 2 of the GNU
# General Public License as published by the Free Software Foundation, a copy
# of which can be found in the main directory of this project.
import gtk
from GLIScreen import *
import Partitioning
from GLIException import *
import re
import PartitionButton
import PartProperties
from gettext import gettext as _
class Panel(GLIScreen):
title = _("Partitioning")
part_buttons = {}
drives = []
devices = {}
active_device = ""
active_device_bytes_in_sector = 0
active_part_idx = -1
colors = { 'ext2': '#0af2fe', 'ext3': '#0af2fe', 'unalloc': '#a2a2a2', 'unknown': '#ed03e0', 'free': '#ffffff', 'ntfs': '#f20600', 'fat16': '#3d07f9', 'fat32': '#3d07f9', 'reiserfs': '#f0ff00', 'linux-swap': '#12ff09', 'xfs': '#006600', 'jfs': '#ffb400', 'hfs': '#fdb0ff', 'hfs+': '#fdb0ff', 'apple_bootstrap': '#fdb0ff' }
supported_filesystems = ["ext2", "ext3", "linux-swap", "xfs", "jfs", "reiserfs", "fat16", "fat32", "ntfs", "hfs", "hfs+"]
_helptext = """
Partitioning
The choices you make on this screen are very important. There are three ways \
to do your partitioning.
The first method is the Recommended Layout. If you really have no idea how you \
should lay out your partitions, this is probably the best option. You will need \
at least 4GB of disk space to use this option. If you have any existing \
partitions, they will be deleted. Three partitions will be created: \
/boot (100MB), swap (calculated based on physical memory, up to 512MB), and \
/ (remaining concurrent space).
The second method is the old fashioned one: doing it yourself. The partition \
edit is fairly straightforward (although, not that straightforward as \
this help would not need to exist).
The third method is doing it completely "by hand". You can open up a terminal \
and use fdisk, cfdisk, or some other partitioning program. You \
will need to create filesystems on all partitions that you create.
The currently active disk is represented by the bar near the top of the screen. \
If you have more than one disk present in your system, you can change the \
active disk by choosing another disk from the drop down box labeled 'Devices'. \
Along the bottom of the screen, there is a color key for the disk representation \
above.
To view an existing partition's properties, you must select it by clicking it in \
the bar above. You will get a brief overview of the partition below the bar. To \
delete it, click the 'Delete' button. You will be asked for confirmation before \
the partition is removed.
To create a new partition, select some unallocated space in the bar at the top. \
For a partition for use by Linux, you will want to use ext2, ext3, reiserfs, \
jfs, or xfs. Ext3 is the recommended type.
Keep in mind that all changes are committed to disk immediately. If you just \
click the Next button, your partition table will remain untouched.
"""
def __init__(self, controller):
GLIScreen.__init__(self, controller, show_title=True)
vert = gtk.VBox(False, 0)
vert.set_border_width(10)
container = gtk.HBox(False, 0)
detected_dev_label = gtk.Label(_("Devices:"))
container.pack_start(detected_dev_label, expand=False, fill=False, padding=0)
self.detected_dev_combo = gtk.combo_box_new_text()
self.detected_dev_combo.connect("changed", self.drive_changed)
container.pack_start(self.detected_dev_combo, expand=False, fill=False, padding=10)
self.part_button_recommended = gtk.Button(_(" Recommended layout "))
self.part_button_recommended.connect("clicked", self.part_button_recommended_clicked)
container.pack_end(self.part_button_recommended, expand=False, fill=False, padding=0)
self.part_button_clear = gtk.Button(_(" Clear partitions "))
self.part_button_clear.connect("clicked", self.part_button_clear_clicked)
container.pack_end(self.part_button_clear, expand=False, fill=False, padding=10)
vert.pack_start(container, expand=False, fill=False, padding=10)
# This builds the container for the "whole disk" display at the top
part_table_frame = gtk.Frame()
part_table_frame.set_shadow_type(gtk.SHADOW_IN)
self.part_table = gtk.Table(1, 1, False)
self.part_table.set_size_request(-1, 40)
part_table_frame.add(self.part_table)
vert.pack_start(part_table_frame, expand=False, fill=False, padding=5)
# This builds the partition info box
self.part_info_box = gtk.HBox(False, 0)
part_info_table = gtk.Table(6, 2, False)
part_info_table.set_col_spacings(10)
info_partition_label = gtk.Label(_("Partition:"))
info_partition_label.set_alignment(0.0, 0.5)
self.info_partition = gtk.Label()
self.info_partition.set_alignment(0.0, 0.5)
info_type_label = gtk.Label(_("Type:"))
info_type_label.set_alignment(0.0, 0.5)
self.info_type = gtk.Label()
self.info_type.set_alignment(0.0, 0.5)
info_filesystem_label = gtk.Label(_("Filesystem:"))
info_filesystem_label.set_alignment(0.0, 0.5)
self.info_filesystem = gtk.Label()
self.info_filesystem.set_alignment(0.0, 0.5)
info_size_label = gtk.Label(_("Size:"))
info_size_label.set_alignment(0.0, 0.5)
self.info_size = gtk.Label()
self.info_size.set_alignment(0.0, 0.5)
part_info_table.attach(info_partition_label, 0, 1, 0, 1)
part_info_table.attach(self.info_partition, 1, 2, 0, 1)
part_info_table.attach(info_type_label, 0, 1, 1, 2)
part_info_table.attach(self.info_type, 1, 2, 1, 2)
part_info_table.attach(info_filesystem_label, 0, 1, 2, 3)
part_info_table.attach(self.info_filesystem, 1, 2, 2, 3)
part_info_table.attach(info_size_label, 0, 1, 3, 4)
part_info_table.attach(self.info_size, 1, 2, 3, 4)
self.part_info_box.pack_start(part_info_table, expand=False, fill=False)
vert.pack_start(self.part_info_box, expand=False, fill=False, padding=10)
# This builds the row of buttons
self.part_button_box = gtk.HBox(False, 0)
self.part_button_delete = gtk.Button(_(" Delete "))
self.part_button_delete.connect("clicked", self.part_button_delete_clicked)
self.part_button_box.pack_start(self.part_button_delete, expand=False, fill=False, padding=0)
# self.part_button_properties = gtk.Button(_(" Properties "))
# self.part_button_properties.connect("clicked", self.part_button_properties_clicked)
# self.part_button_box.pack_start(self.part_button_properties, expand=False, fill=False, padding=10)
vert.pack_start(self.part_button_box, expand=False, fill=False, padding=10)
# This builds the color key at the bottom
color_codes = [ { 'label': "Swap", 'color': '#12ff09' },
{ 'label': "Ext2/3", 'color': '#0af2fe' },
{ 'label': "Reiserfs", 'color': '#f0ff00' },
{ 'label': "JFS", 'color': '#ffb400' },
{ 'label': "XFS", 'color': '#006600' },
{ 'label': "FAT", 'color': '#3d07f9' },
{ 'label': "NTFS", 'color': '#f20600' },
{ 'label': "HFS", 'color': '#fdb0ff' },
{ 'label': _("Other"), 'color': '#ed03e0' },
# { 'label': "Free space", 'color': '#ffffff' },
{ 'label': _("Unallocated"), 'color': '#a2a2a2' }
]
color_codes_table = gtk.Table(rows=2, columns=6)
color_codes_table.set_row_spacings(5)
color_codes_table.set_col_spacings(10)
vert.pack_end(color_codes_table, expand=False, fill=True, padding=2)
# color_codes_box = gtk.HBox(False, 0)
# vert.pack_end(color_codes_box, expand=False, fill=False, padding=2)
col = -1
row = 0
for color in color_codes:
col += 1
if col == 6:
col = 0
row += 1
temp_xpm = [ "12 12 2 1",
"B c #000000",
"C c " + color['color'],
"BBBBBBBBBBBB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BCCCCCCCCCCB",
"BBBBBBBBBBBB"
]
pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(self.controller.window.window, None, temp_xpm)
tmp_image = gtk.Image()
tmp_image.set_from_pixmap(pixmap, mask)
tmpbox = gtk.HBox(False, 0)
tmpbox.pack_start(tmp_image, expand=False, fill=False, padding=3)
tmpbox.pack_start(gtk.Label(color['label']), expand=False, fill=False, padding=3)
# color_codes_box.pack_start(tmpbox, expand=False, fill=False, padding=6)
color_codes_table.attach(tmpbox, col, col + 1, row, row + 1)
self.add_content(vert)
def drive_changed(self, combobox, data=None):
self.active_device = self.drives[self.detected_dev_combo.get_active()]
self.draw_part_box()
self.active_device_bytes_in_sector = self.devices[self.active_device].get_geometry()['sector_size']
self.info_partition.set_text("")
self.info_type.set_text("")
self.info_filesystem.set_text("")
self.info_size.set_text("")
self.part_info_box.hide_all()
self.part_button_box.hide_all()
# self.part_selected(None, self.drives[self.detected_dev_combo.get_active()], 0)
def part_selected(self, button, dev=None, idx=None):
tmppart = self.devices[dev][idx]
self.info_partition.set_text(tmppart['devnode'])
if tmppart.is_logical():
self.info_type.set_text(_("Logical"))
elif tmppart.get_type() == "extended":
self.info_type.set_text(_("Extended"))
else:
self.info_type.set_text(_("Primary"))
fstype = tmppart.get_type()
if fstype == "extended":
self.info_filesystem.set_text(_("N/A"))
else:
self.info_filesystem.set_text(fstype)
part_size = int(tmppart['mb'])
self.info_size.set_text(str(part_size) + _(" MB"))
self.active_part_idx = idx
self.part_button_delete.set_sensitive(True)
self.part_info_box.show_all()
self.part_button_box.show_all()
def unalloc_selected(self, button, idx=None):
tmppart = self.devices[self.active_device][idx]
props = PartProperties.PartProperties(self, tmppart)
props.run()
def part_button_delete_clicked(self, button, data=None):
msgdlg = gtk.MessageDialog(parent=self.controller.window, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format=_("Are you sure you want to delete ") + self.devices[self.active_device][self.active_part_idx]['devnode'])
resp = msgdlg.run()
msgdlg.destroy()
if resp == gtk.RESPONSE_YES:
self.devices[self.active_device].remove_partition(self.active_part_idx)
if self.devices[self.active_device]._labelinfo['extended']:
ext_idx = self.devices[self.active_device].get_extended_partition()
if ext_idx > -1 and not len([x for x in self.devices[self.active_device][ext_idx].get_logicals() if x['type'] != "free"]):
self.devices[self.active_device].remove_partition(ext_idx)
self.drive_changed(None)
def part_button_recommended_clicked(self, button):
msgdlg = gtk.MessageDialog(parent=self.controller.window, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format=_("This will clear your drive and apply a recommended partition layout. Are you sure you want to do this?"))
resp = msgdlg.run()
msgdlg.destroy()
if resp == gtk.RESPONSE_YES:
try:
self.devices[self.active_device].do_recommended()
except GLIException, error:
msgdlg = gtk.MessageDialog(parent=self.controller.window, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK, message_format=error.get_error_msg())
msgdlg.run()
msgdlg.destroy()
self.draw_part_box()
def part_button_clear_clicked(self, button):
msgdlg = gtk.MessageDialog(parent=self.controller.window, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format=_("Are you sure you wish to clear the partition table for " + self.active_device + "?"))
resp = msgdlg.run()
msgdlg.destroy()
if resp == gtk.RESPONSE_YES:
self.devices[self.active_device].clear_partitions()
self.drive_changed(self.detected_dev_combo)
def draw_part_box(self):
# partlist = self.devices[self.active_device].get_ordered_partition_list()
total_mb = self.devices[self.active_device].get_geometry()['total_mb']
for button in self.part_buttons.keys():
self.part_table.remove(self.part_buttons[button])
self.part_table.resize(1, 100)
self.part_buttons = {}
last_percent = 0
last_log_percent = 0
extended_part = 0
extended_table = None
for idx, tmppart in enumerate(self.devices[self.active_device]):
# tmppart = self.devices[self.active_device][part]
if tmppart.get_type() == "free":
partsize = tmppart.get_mb()
percent = (float(partsize) / float(total_mb)) * 100
if percent < 1: percent = 1
percent = int(percent)
# print "minor: " + str(part) + ", mb: " + str(partsize) + ", percent: " + str(percent) + ", last_percent: " + str(last_percent)
if tmppart.is_logical():
tmpbutton = PartitionButton.Partition(color1=self.colors['unalloc'], color2=self.colors['unalloc'], label="", division=0)
tmpbutton.connect("clicked", self.unalloc_selected, idx)
extended_table.attach(tmpbutton, last_log_percent, (last_log_percent + percent), 0, 1)
last_log_percent = last_log_percent + percent
else:
self.part_buttons['free_' + str(idx)] = PartitionButton.Partition(color1=self.colors['unalloc'], color2=self.colors['unalloc'], label="", division=0)
if self.devices[self.active_device]._labelinfo['extended'] and self.devices[self.active_device].get_partition_idx_from_minor(1) > -1 and self.devices[self.active_device].get_partition_idx_from_minor(2) > -1 and self.devices[self.active_device].get_partition_idx_from_minor(3) > -1 and self.devices[self.active_device].get_partition_idx_from_minor(4) > -1:
self.part_buttons['free_' + str(idx)].connect("clicked", self.show_no_more_primary_message)
else:
self.part_buttons['free_' + str(idx)].connect("clicked", self.unalloc_selected, idx)
self.part_table.attach(self.part_buttons['free_' + str(idx)], last_percent, (last_percent + percent), 0, 1)
last_percent = last_percent + percent
else:
partsize = tmppart.get_mb()
percent = (float(partsize) / float(total_mb)) * 100
if percent < 1: percent = 1
percent = int(percent)
tmpminor = int(tmppart.get_minor())
tmpdevice = self.active_device
# print "minor: " + str(tmpminor) + ", mb: " + str(partsize) + ", percent: " + str(percent) + ", last_percent: " + str(last_percent)
if tmppart.is_extended():
extended_table = gtk.Table(1, percent)
extended_table.set_border_width(3)
extended_part = tmpminor
self.part_buttons[tmpminor] = extended_table
self.part_table.attach(self.part_buttons[tmpminor], last_percent, (last_percent + percent), 0, 1)
last_percent = last_percent + percent
elif tmppart.is_logical():
tmpbutton = PartitionButton.Partition(color1=self.colors[tmppart.get_type()], color2=self.colors[tmppart.get_type()], label="", division=0)
if percent >= 15:
tmpbutton.set_label(tmppart['devnode'])
extended_table.attach(tmpbutton, last_log_percent, (last_log_percent + percent), 0, 1)
last_log_percent = last_log_percent + percent
tmpbutton.connect("clicked", self.part_selected, tmpdevice, idx)
else:
self.part_buttons[tmpminor] = PartitionButton.Partition(color1=self.colors[tmppart.get_type()], color2=self.colors[tmppart.get_type()], label="", division=0)
if percent >= 15:
self.part_buttons[tmpminor].set_label(tmppart['devnode'])
self.part_buttons[tmpminor].connect("clicked", self.part_selected, tmpdevice, idx)
self.part_table.attach(self.part_buttons[tmpminor], last_percent, (last_percent + percent), 0, 1)
last_percent = last_percent + percent
self.part_table.show_all()
def show_no_more_primary_message(self, button, data=None):
msgdlg = gtk.MessageDialog(parent=self.controller.window, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK, message_format=_("You cannot create more than 4 primary partitions. If you need more partitions, delete one or more and create logical partitions."))
msgdlg.run()
msgdlg.destroy()
def part_button_properties_clicked(self, widget, data=None):
tmppart = self.devices[self.active_device][self.active_part_idx]
props = PartProperties.PartProperties(self, tmppart)
props.run()
def activate(self):
self.controller.SHOW_BUTTON_BACK = False
self.controller.SHOW_BUTTON_FORWARD = True
part_load_error = 0
tmp_drives = Partitioning.detect_devices()
tmp_drives.sort()
for drive in tmp_drives:
try:
self.devices[drive] = Partitioning.Device(drive, self.controller.cc.get_arch(), self.controller.install_profile)
if self.devices[drive].has_mounted_partitions():
msgdlg = gtk.MessageDialog(parent=self.controller.window, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_OK, message_format="You have a filesystem mounted on %s. Please unmount before performing any operations on this device. Failure to do so could cause data loss. You have been warned." % drive)
msgdlg.run()
msgdlg.destroy()
self.detected_dev_combo.append_text(drive)
self.drives.append(drive)
except:
print _("Exception received while loading partitions from device " + drive)
if self.devices.has_key(drive): del self.devices[drive]
msgdlg = gtk.MessageDialog(parent=self.controller.window, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_OK, message_format="There was an error loading the partition table from device " + drive + ". It will not be displayed.")
msgdlg.run()
msgdlg.destroy()
if not self.drives:
tmp_drives = self.devices.keys()
tmp_drives.sort()
self.drives = tmp_drives
for drive in self.drives:
self.detected_dev_combo.append_text(drive)
if self.devices:
self.active_device = self.drives[0]
self.detected_dev_combo.set_active(0)
self.drive_changed(None)
def next(self):
self.controller.load_screen("LocalMounts")