from gi.repository import ( GObject, Gdk, Gtk, Pango, ) from ubiquity import misc def refresh(): while Gtk.events_pending(): Gtk.main_iteration() def draw_round_rect(c, r, x, y, w, h): c.move_to(x + r, y) c.line_to(x + w - r, y) c.curve_to(x + w, y, x + w, y, x + w, y + r) c.line_to(x + w, y + h - r) c.curve_to(x + w, y + h, x + w, y + h, x + w - r, y + h) c.line_to(x + r, y + h) c.curve_to(x, y + h, x, y + h, x, y + h - r) c.line_to(x, y + r) c.curve_to(x, y, x, y, x + r, y) c.close_path() def gtk_to_cairo_color(c): color = Gdk.color_parse(c) s = 1.0 / 65535.0 r = color.red * s g = color.green * s b = color.blue * s return r, g, b class StylizedFrame(Gtk.Alignment): __gtype_name__ = 'StylizedFrame' __gproperties__ = { 'radius': ( GObject.TYPE_INT, 'Radius', 'The radius of the rounded corners.', 0, GObject.G_MAXINT, 10, GObject.PARAM_READWRITE), 'width': ( GObject.TYPE_INT, 'Width', 'The width of the outline.', 0, GObject.G_MAXINT, 1, GObject.PARAM_READWRITE), } def __init__(self): Gtk.Alignment.__init__(self) self.radius = 10 self.width = 1 def do_get_property(self, prop): if prop.name in ('radius', 'width'): return getattr(self, prop.name) else: return Gtk.Alignment.do_get_property(self, prop) def do_set_property(self, prop, value): if prop.name in ('radius', 'width'): setattr(self, prop.name, value) self.queue_draw() else: Gtk.Alignment.do_set_property(self, prop, value) def paint_background(self, c): c.set_source_rgb(*gtk_to_cairo_color('#fbfbfb')) alloc = self.get_allocation() draw_round_rect(c, self.radius, self.width / 2, self.width / 2, alloc.width - self.width, alloc.height - self.width) c.fill_preserve() def do_draw(self, c): # Background self.paint_background(c) # Edge c.set_source_rgb(*gtk_to_cairo_color('#c7c7c6')) c.set_line_width(self.width) c.stroke() if self.get_child(): top, bottom, left, right = self.get_padding() c.translate(left, top) self.get_child().draw(c) GObject.type_register(StylizedFrame) class ResizeWidget(Gtk.Paned): __gtype_name__ = 'ResizeWidget' __gproperties__ = { 'part_size': ( GObject.TYPE_UINT64, 'Partition size', 'The size of the partition being resized', 1, GObject.G_MAXUINT64, 100, GObject.PARAM_READWRITE), 'min_size': ( GObject.TYPE_UINT64, 'Minimum size', 'The minimum size that the existing partition can be resized to', 0, GObject.G_MAXUINT64, 0, GObject.PARAM_READWRITE), 'max_size': ( GObject.TYPE_UINT64, 'Maximum size', 'The maximum size that the existing partition can be resized to', 1, GObject.G_MAXUINT64, 100, GObject.PARAM_READWRITE) } def do_get_property(self, prop): return getattr(self, prop.name.replace('-', '_')) def do_set_property(self, prop, value): setattr(self, prop.name.replace('-', '_'), value) def __init__(self, part_size=100, min_size=0, max_size=100, existing_part=None, new_part=None): Gtk.Paned.__init__(self) self.set_orientation(Gtk.Orientation.HORIZONTAL) assert min_size <= max_size <= part_size assert part_size > 0 # The size (b) of the existing partition. self.part_size = part_size # The min size (b) that the existing partition can be resized to. self.min_size = min_size # The max size (b) that the existing partition can be resized to. self.max_size = max_size # Use test window to figure out highlight color test_window = Gtk.Window() test_label = Gtk.Label() test_window.add(test_label) style = test_label.get_style_context() self.highlight_color = style.get_background_color( Gtk.StateFlags.SELECTED) self.highlight_color.alpha = 0.5 self.existing_part = existing_part or PartitionBox() frame = Gtk.Frame.new() frame.set_shadow_type(Gtk.ShadowType.ETCHED_IN) frame.modify_bg(Gtk.StateFlags.NORMAL, self.highlight_color.to_color()) frame.add(self.existing_part) self.pack1(frame, resize=False, shrink=False) self.new_part = new_part or PartitionBox() frame = Gtk.Frame.new() frame.set_shadow_type(Gtk.ShadowType.ETCHED_IN) frame.modify_bg(Gtk.StateFlags.NORMAL, self.highlight_color.to_color()) frame.add(self.new_part) self.pack2(frame, resize=False, shrink=False) self.show_all() # FIXME hideous, but do_realize fails inexplicably. self.connect('realize', self.realize) def realize(self, w): # TEST: Make sure the value of the minimum size and maximum size # equal the value of the widget when pushed to the min/max. total = (self.new_part.get_allocation().width + self.existing_part.get_allocation().width) tmp = float(self.min_size) / self.part_size pixels = int(tmp * total) self.existing_part.set_size_request(pixels, -1) tmp = ((float(self.part_size) - self.max_size) / self.part_size) pixels = int(tmp * total) self.new_part.set_size_request(pixels, -1) def do_draw(self, cr): Gtk.Paned.do_draw(self, cr) s1 = self.existing_part.get_allocation().width s2 = self.new_part.get_allocation().width total = s1 + s2 percent = (float(s1) / float(total)) self.existing_part.set_size(percent * self.part_size) percent = (float(s2) / float(total)) self.new_part.set_size(percent * self.part_size) def set_pref_size(self, size): s1 = self.existing_part.get_allocation().width s2 = self.new_part.get_allocation().width total = s1 + s2 percent = (float(size) / float(self.part_size)) val = percent * total self.set_position(int(val)) def get_size(self): '''Returns the size of the old partition, clipped to the minimum and maximum sizes. ''' s1 = self.existing_part.get_allocation().width s2 = self.new_part.get_allocation().width totalwidth = s1 + s2 size = int(float(s1) * self.part_size / float(totalwidth)) if size < self.min_size: return self.min_size elif size > self.max_size: return self.max_size else: return size GObject.type_register(ResizeWidget) class DiskBox(Gtk.Box): __gtype_name__ = 'DiskBox' def add(self, partition, size): Gtk.Box.add(self, partition, expand=False) partition.set_size_request(size, -1) def clear(self): self.forall(lambda x: self.remove(x)) GObject.type_register(DiskBox) class PartitionBox(Gtk.Alignment): __gtype_name__ = 'PartitionBox' __gproperties__ = { 'title': ( GObject.TYPE_STRING, 'Title', None, 'Title', GObject.PARAM_READWRITE), 'icon-name': ( GObject.TYPE_STRING, 'Icon Name', None, 'distributor-logo', GObject.PARAM_READWRITE), 'extra': ( GObject.TYPE_STRING, 'Extra Text', None, '', GObject.PARAM_READWRITE), } def do_get_property(self, prop): if prop.name == 'title': return self.ostitle.get_text() elif prop.name == 'icon-name': return self.logo.get_icon_name() elif prop.name == 'extra': return self.extra.get_text() return getattr(self, prop.name) def do_set_property(self, prop, value): if prop.name == 'title': self.ostitle.set_markup('%s' % value) return elif prop.name == 'icon-name': self.logo.set_from_icon_name(value, Gtk.IconSize.DIALOG) return elif prop.name == 'extra': self.extra.set_markup('%s' % (value and value or ' ')) return setattr(self, prop.name, value) def __init__(self, title='', extra='', icon_name='distributor-logo'): # 10 px above the topmost element # 6 px between the icon and the title # 4 px between the title and the extra heading # 5 px between the extra heading and the size # 12 px below the bottom-most element Gtk.Alignment.__init__(self) vbox = Gtk.Box() vbox.set_orientation(Gtk.Orientation.VERTICAL) self.logo = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DIALOG) align = Gtk.Alignment.new(0.5, 0.5, 0.5, 0.5) align.set_padding(10, 0, 0, 0) align.add(self.logo) vbox.pack_start(align, False, True, 0) self.ostitle = Gtk.Label() self.ostitle.set_ellipsize(Pango.EllipsizeMode.END) align = Gtk.Alignment.new(0.5, 0.5, 0.5, 0.5) align.set_padding(6, 0, 0, 0) align.add(self.ostitle) vbox.pack_start(align, False, True, 0) self.extra = Gtk.Label() self.extra.set_ellipsize(Pango.EllipsizeMode.END) align = Gtk.Alignment.new(0.5, 0.5, 0.5, 0.5) align.set_padding(4, 0, 0, 0) align.add(self.extra) vbox.pack_start(align, False, True, 0) self.size = Gtk.Label() self.size.set_ellipsize(Pango.EllipsizeMode.END) align = Gtk.Alignment.new(0.5, 0.5, 0.5, 0.5) align.set_padding(5, 12, 0, 0) align.add(self.size) vbox.pack_start(align, False, True, 0) self.add(vbox) self.ostitle.set_markup('%s' % title) # Take up the space that would otherwise be used to create symmetry. self.extra.set_markup('%s' % extra and extra or ' ') self.show_all() def set_size(self, size): size = misc.format_size(size) self.size.set_markup('%s' % size) GObject.type_register(PartitionBox) class StateBox(StylizedFrame): __gtype_name__ = 'StateBox' __gproperties__ = { 'label': ( GObject.TYPE_STRING, 'Label', None, 'label', GObject.PARAM_READWRITE), } def do_get_property(self, prop): if prop.name == 'label': return self.label.get_text() return getattr(self, prop.name) def do_set_property(self, prop, value): if prop.name == 'label': self.label.set_text(value) return setattr(self, prop.name, value) def __init__(self, text=''): StylizedFrame.__init__(self) alignment = Gtk.Alignment() alignment.set_padding(7, 7, 15, 15) hbox = Gtk.Box() hbox.set_spacing(10) self.image = Gtk.Image() self.image.set_from_stock(Gtk.STOCK_YES, Gtk.IconSize.LARGE_TOOLBAR) self.label = Gtk.Label(label=text) self.label.set_alignment(0, 0.5) hbox.pack_start(self.image, False, True, 0) hbox.pack_start(self.label, True, True, 0) alignment.add(hbox) self.add(alignment) self.show_all() self.status = True def set_state(self, state): self.status = state if state: self.image.set_from_stock(Gtk.STOCK_YES, Gtk.IconSize.LARGE_TOOLBAR) else: self.image.set_from_stock(Gtk.STOCK_NO, Gtk.IconSize.LARGE_TOOLBAR) def get_state(self): return self.status GObject.type_register(StateBox) # GtkBuilder should have .get_object_ids() method class Builder(Gtk.Builder): def __init__(self): self._widget_ids = set() super().__init__() def add_from_file(self, filename): import xml.etree.cElementTree as ET tree = ET.parse(filename) root = tree.getroot() for widgets in root.iter('object'): self._widget_ids.add(widgets.attrib['id']) return super().add_from_file(filename) def get_object_ids(self): return self._widget_ids