pitivi r1124 - in branches/SOC_2008_BLEWIS: . pitivi/ui
- From: blewis svn gnome org
- To: svn-commits-list gnome org
- Subject: pitivi r1124 - in branches/SOC_2008_BLEWIS: . pitivi/ui
- Date: Sat, 31 May 2008 01:48:39 +0000 (UTC)
Author: blewis
Date: Sat May 31 01:48:38 2008
New Revision: 1124
URL: http://svn.gnome.org/viewvc/pitivi?rev=1124&view=rev
Log:
* pitivi/ui/test.py:
Test code...will later delete
* pitivi/ui/timelineobjects.py:
Added new class, but everything is broken
* pitivi/ui/util.py:
Convenience stuff for goocanvas. Similar to goocanvashelper in gst-editor
but with a much shorter name =P
Added:
branches/SOC_2008_BLEWIS/pitivi/ui/test.py
branches/SOC_2008_BLEWIS/pitivi/ui/util.py
Modified:
branches/SOC_2008_BLEWIS/ChangeLog
branches/SOC_2008_BLEWIS/pitivi/ui/timelineobjects.py
Added: branches/SOC_2008_BLEWIS/pitivi/ui/test.py
==============================================================================
--- (empty file)
+++ branches/SOC_2008_BLEWIS/pitivi/ui/test.py Sat May 31 01:48:38 2008
@@ -0,0 +1,73 @@
+import gobject
+gobject.threads_init()
+import pygtk
+pygtk.require("2.0")
+import gtk
+import goocanvas
+import timelineobjects
+from itertools import cycle
+from util import make_item, set_pos, center, Text, group
+
+
+LABELS = "one two three four five six seven".split()
+
+box_a = (
+ goocanvas.Rect,
+ {
+ "width" : 50,
+ "height" : 30,
+ "stroke_color" : "black",
+ "fill_color_rgba" : 0x556633FF
+ },
+ {}
+)
+box_b = (
+ goocanvas.Rect,
+ {
+ "width" : 75,
+ "height" : 30,
+ "stroke_color" : "black",
+ "fill_color_rgba" : 0x663333FF,
+ },
+ {}
+)
+
+box = cycle((box_a, box_b))
+
+label = (
+ Text,
+ {
+ "font" : "Sans 9",
+ "text" : "will be replaced",
+ "fill_color_rgba" : 0x66AA66FF,
+ "anchor" : gtk.ANCHOR_CENTER
+ },
+ {}
+)
+
+def make_box(text):
+ b = make_item(box.next())
+ t = make_item(label)
+ t.props.text = text
+ set_pos(t, center(b))
+ return group(b, t)
+
+def make_widget(text):
+ b = gtk.Button(text)
+ return goocanvas.Widget(widget=b, width=75,
+ height=50)
+
+t = timeline.SimpleTimeline()
+for word in LABELS:
+ t.add(make_box(word))
+
+s = gtk.ScrolledWindow()
+s.set_policy(gtk.POLICY_ALWAYS, gtk.POLICY_NEVER)
+s.add(t)
+w = gtk.Window()
+w.add(s)
+w.show_all()
+w.connect("destroy", gtk.main_quit)
+gtk.main()
+
+
Modified: branches/SOC_2008_BLEWIS/pitivi/ui/timelineobjects.py
==============================================================================
--- branches/SOC_2008_BLEWIS/pitivi/ui/timelineobjects.py (original)
+++ branches/SOC_2008_BLEWIS/pitivi/ui/timelineobjects.py Sat May 31 01:48:38 2008
@@ -42,6 +42,9 @@
from sourcefactories import beautify_length
from gettext import gettext as _
+import goocanvas
+from util import *
+
# Default width / height ratio for simple elements
DEFAULT_SIMPLE_SIZE_RATIO = 1.50 # default height / width ratio
@@ -69,458 +72,554 @@
hours = mins / 60
return "%02d:%02d:%02d.%03d" % (hours, mins, sec, ms)
-class SimpleTimeline(gtk.Layout):
- """ Simple Timeline representation """
-
- def __init__(self, **kw):
- gobject.GObject.__init__(self, **kw)
-
- self.hadjustment = self.get_property("hadjustment")
-
- # timeline and top level compositions
- self.timeline = instance.PiTiVi.current.timeline
- self.condensed = self.timeline.videocomp.condensed
-
- # TODO : connect signals for when the timeline changes
-
- # widgets correspondance dictionnary
- # MAPPING timelineobject => widget
- self.widgets = {}
-
- # edit-mode
- # True when in editing mode
- self._editingMode = False
- self.editingWidget = SimpleEditingWidget()
- self.editingWidget.connect("hide-me", self._editingWidgetHideMeCb)
-
- # Connect to timeline. We must remove and reset the callbacks when
- # changing project.
- self.project_signals = SignalGroup()
- # FIXME: do we need this? or will the newproject sginal implicitly
- # handle this???
- self._connectToTimeline(instance.PiTiVi.current.timeline)
- instance.PiTiVi.connect("new-project-loaded",
- self._newProjectLoadedCb)
- instance.PiTiVi.connect("project-closed", self._projectClosedCb)
- instance.PiTiVi.connect("new-project-loading",
- self._newProjectLoadingCb)
- instance.PiTiVi.connect("new-project-failed",
- self._newProjectFailedCb)
-
- # size
- self.width = int(DEFAULT_WIDTH)
- self.height = int(DEFAULT_HEIGHT)
- self.realWidth = 0 # displayed width of the layout
- self.childheight = int(DEFAULT_SIMPLE_ELEMENT_HEIGHT)
- self.childwidth = int(DEFAULT_SIMPLE_ELEMENT_WIDTH)
- self.set_size_request(int(MINIMUM_WIDTH), int(MINIMUM_HEIGHT))
- self.set_property("width", int(DEFAULT_WIDTH))
- self.set_property("height", int(DEFAULT_HEIGHT))
-
- # event callbacks
- self.connect("expose-event", self._exposeEventCb)
- self.connect("notify::width", self._widthChangedCb)
- self.connect("size-allocate", self._sizeAllocateCb)
- self.connect("realize", self._realizeCb)
-
- # drag and drop
- self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
- [dnd.FILESOURCE_TUPLE],
- gtk.gdk.ACTION_COPY)
- self.connect("drag-data-received", self._dragDataReceivedCb)
- self.connect("drag-leave", self._dragLeaveCb)
- self.connect("drag-motion", self._dragMotionCb)
- self.slotposition = -1
-
- self.draggedelement = None
-
- self.show_all()
+class SimpleTimeline(goocanvas.Canvas):
+ __gtype_name__ = 'SimpleTimeline'
+ def __init__(self, *args, **kwargs):
+ goocanvas.Canvas.__init__(self, *args, **kwargs)
+ self.props.automatic_bounds = False
+
+ self.root = self.get_root_item()
+ self.items = HList()
+ self.root.add_child(self.items)
+
+ self.left = None
+ self.l_thresh = None
+ self.right = None
+ self.r_thresh = None
+ self.drag_sigid = None
+
+ self.connect("size_allocate", self._size_allocate)
+ self.items.connect("notify::width", self._request_size)
+ self.items.connect("notify::height", self._request_size)
+
+ self.signals = {}
+
+ self.scale = 1.0
+
+ def _request_size(self, item, prop):
+ self.set_size_request(int(self.items.width), int(
+ self.items.height))
+ self.set_bounds(0, 0, self.items.width, self.items.height)
+ return True
- ## Project callbacks
-
- def _connectToTimeline(self, timeline):
- self.timeline = timeline
- self.condensed = self.timeline.videocomp.condensed
- self.project_signals.connect(self.timeline.videocomp,
- "condensed-list-changed",
- None, self._condensedListChangedCb)
-
- def _newProjectLoadingCb(self, unused_inst, unused_project):
- gst.log("...")
-
- def _newProjectLoadedCb(self, unused_inst, project):
- gst.log("...")
- assert(instance.PiTiVi.current == project)
- # now we connect to the new project, so we can receive any
- # signals that might be emitted while the project is loading
- self._connectToTimeline(project.timeline)
- # TODO: display final state of project now that loading has
- # completed. this callback doesn't do do much else
-
- # LOAD THE TIMELINE !!!
- self._condensedListChangedCb(None, self.timeline.videocomp.condensed)
-
- def _newProjectFailedCb(self, unused_inst, unused_reason, unused_uri):
- # oops the project failed to load
- self._clearTimeline()
-
- def _clearTimeline(self):
- self.switchToNormalMode()
- self.project_signals.disconnectAll()
- for widget in self.widgets.itervalues():
- self.remove(widget)
- self.widgets = {}
-
- def _projectClosedCb(self, unused_pitivi, unused_project):
- self._clearTimeline()
-
- ## Timeline callbacks
-
- def _condensedListChangedCb(self, unused_videocomp, clist):
- """ add/remove the widgets """
- gst.debug("condensed list changed in videocomp")
-
- current = self.widgets.keys()
- self.condensed = clist
-
- new = [x for x in clist if not x in current]
- removed = [x for x in current if not x in clist]
-
- # new elements
- for element in new:
- # add the widget to self.widget
- gst.debug("Adding new element to the layout")
- if isinstance(element, TimelineFileSource):
- widget = SimpleSourceWidget(element)
- widget.connect("delete-me", self._sourceDeleteMeCb, element)
- widget.connect("edit-me", self._sourceEditMeCb, element)
- widget.connect("drag-begin", self._sourceDragBeginCb, element)
- widget.connect("drag-end", self._sourceDragEndCb, element)
- else:
- widget = SimpleTransitionWidget(element)
- self.widgets[element] = widget
- self.put(widget, 0, 0)
- widget.show()
-
- # removed elements
- for element in removed:
- self.remove(self.widgets[element])
- del self.widgets[element]
-
- self._resizeChildrens()
-
-
- ## Utility methods
-
- def _getNearestSourceSlot(self, x):
- """
- returns the nearest file slot position available for the given position
- Returns the value in condensed list position
- Returns n , the element before which it should go
- Return -1 if it's meant to go last
- """
- if not self.condensed or x < 0:
- return 0
- if x > self.width - DEFAULT_SIMPLE_SPACING:
- return -1
-
- pos = DEFAULT_SIMPLE_SPACING
- order = 0
- # TODO Need to avoid getting position between source and transition
- for source in self.condensed:
- if isinstance(source, TimelineSource):
- spacing = self.childwidth
- elif isinstance(source, TimelineTransition):
- spacing = self.childwidth / 2
- else:
- # this shouldn't happen !! The condensed list only contains
- # sources and/or transitions
- pass
- if x <= pos + spacing / 2:
- return order
- pos = pos + spacing + DEFAULT_SIMPLE_SPACING
- order = order + 1
- return -1
-
- def _getNearestSourceSlotPixels(self, x):
- """
- returns the nearest file slot position available for the given position
- Returns the value in pixels
- """
- if not self.condensed or x < 0:
- return DEFAULT_SIMPLE_SPACING
- if x > self.width - DEFAULT_SIMPLE_SPACING:
- return self.width - 2 * DEFAULT_SIMPLE_SPACING
-
- pos = DEFAULT_SIMPLE_SPACING
- # TODO Need to avoid getting position between source and transition
- for source in self.condensed:
- if isinstance(source, TimelineSource):
- spacing = self.childwidth
- elif isinstance(source, TimelineTransition):
- spacing = self.childwidth / 2
- else:
- # this shouldn't happen !! The condensed list only contains
- # sources and/or transitions
- pass
- if x <= pos + spacing / 2:
- return pos
- pos = pos + spacing + DEFAULT_SIMPLE_SPACING
- return pos
-
-
- ## Drawing
-
- def _drawDragSlot(self):
- if self.slotposition == -1:
- return
- self.bin_window.draw_rectangle(self.style.black_gc, True,
- self.slotposition, DEFAULT_SIMPLE_SPACING,
- DEFAULT_SIMPLE_SPACING, self.childheight)
-
- def _eraseDragSlot(self):
- if self.slotposition == -1:
- return
- self.bin_window.draw_rectangle(self.style.white_gc, True,
- self.slotposition, DEFAULT_SIMPLE_SPACING,
- DEFAULT_SIMPLE_SPACING, self.childheight)
-
- def _gotFileFactory(self, filefactory, x, unused_y):
- """ got a filefactory at the given position """
- # remove the slot
- self._eraseDragSlot()
- self.slotposition = -1
- if not filefactory or not filefactory.is_video:
- return
- pos = self._getNearestSourceSlot(x)
-
- gst.debug("_got_filefactory pos : %d" % pos)
-
- # we just add it here, the drawing will be done in the condensed_list
- # callback
- source = TimelineFileSource(factory=filefactory,
- media_type=MEDIA_TYPE_VIDEO,
- name=filefactory.name)
-
- # ONLY FOR SIMPLE TIMELINE : if video-only, we link a blank audio object
- if not filefactory.is_audio:
- audiobrother = TimelineBlankSource(factory=filefactory,
- media_type=MEDIA_TYPE_AUDIO,
- name=filefactory.name)
- source.setBrother(audiobrother)
-
- if pos == -1:
- self.timeline.videocomp.appendSource(source)
- elif pos:
- self.timeline.videocomp.insertSourceAfter(source, self.condensed[pos - 1])
- else:
- self.timeline.videocomp.prependSource(source)
-
- def _moveElement(self, element, x):
- gst.debug("TimelineSource, move %s to x:%d" % (element, x))
- # remove the slot
- self._eraseDragSlot()
- self.slotposition = -1
- pos = self._getNearestSourceSlot(x)
-
- self.timeline.videocomp.moveSource(element, pos)
-
- def _widthChangedCb(self, unused_layout, property):
- if not property.name == "width":
- return
- self.width = self.get_property("width")
-
- def _motionNotifyEventCb(self, layout, event):
- pass
-
-
- ## Drag and Drop callbacks
-
- def _dragMotionCb(self, unused_layout, unused_context, x, unused_y,
- unused_timestamp):
- # TODO show where the dragged item would go
- pos = self._getNearestSourceSlotPixels(x + (self.hadjustment.get_value()))
- rpos = self._getNearestSourceSlot(x + self.hadjustment.get_value())
- gst.log("SimpleTimeline x:%d , source would go at %d" % (x, rpos))
- if not pos == self.slotposition:
- if not self.slotposition == -1:
- # erase previous slot position
- self._eraseDragSlot()
- # draw new slot position
- self.slotposition = pos
- self._drawDragSlot()
-
- def _dragLeaveCb(self, unused_layout, unused_context, unused_timestamp):
- gst.log("SimpleTimeline")
- self._eraseDragSlot()
- self.slotposition = -1
- # TODO remove the drag emplacement
-
- def _dragDataReceivedCb(self, unused_layout, context, x, y, selection,
- targetType, timestamp):
- gst.log("SimpleTimeline, targetType:%d, selection.data:%s" % (targetType, selection.data))
- if targetType == dnd.TYPE_PITIVI_FILESOURCE:
- uri = selection.data
- else:
- context.finish(False, False, timestamp)
- x = x + int(self.hadjustment.get_value())
- if self.draggedelement:
- self._moveElement(self.draggedelement, x)
- else:
- self._gotFileFactory(instance.PiTiVi.current.sources[uri], x, y)
- context.finish(True, False, timestamp)
- instance.PiTiVi.playground.switchToTimeline()
-
-
- ## Drawing
-
- def _realizeCb(self, unused_layout):
- self.modify_bg(gtk.STATE_NORMAL, self.style.white)
-
- def _areaIntersect(self, x, y, w, h, x2, y2, w2, h2):
- """ returns True if the area intersects, else False """
- # is zone to the left of zone2
- z1 = gtk.gdk.Rectangle(x, y, w, h)
- z2 = gtk.gdk.Rectangle(x2, y2, w2, h2)
- r = z1.intersect(z2)
- a, b, c, d = r
- if a or b or c or d:
- return True
- return False
-
- def _exposeEventCb(self, unused_layout, event):
- x, y, w, h = event.area
- # redraw the slot rectangle if there's one
- if not self.slotposition == -1:
- if self._areaIntersect(x, y, w, h,
- self.slotposition, DEFAULT_SIMPLE_SPACING,
- DEFAULT_SIMPLE_SPACING, self.childheight):
- self.bin_window.draw_rectangle(self.style.black_gc, True,
- self.slotposition, DEFAULT_SIMPLE_SPACING,
- DEFAULT_SIMPLE_SPACING, self.childheight)
-
- return False
+ def _size_allocate(self, unused_layout, allocation):
+ x1, y1, x2, y2 = self.get_bounds()
+ height = y2 - y1
+
+ if height > 0:
+ self.scale = allocation.height / height
+ self.set_scale(self.scale)
+ return True
- def _sizeAllocateCb(self, unused_layout, allocation):
- if not self.height == allocation.height:
- self.height = allocation.height
- self.childheight = self.height - 2 * DEFAULT_SIMPLE_SPACING
- self.childwidth = int(self.height / DEFAULT_SIMPLE_SIZE_RATIO)
- self._resizeChildrens()
- self.realWidth = allocation.width
- if self._editingMode:
- self.editingWidget.set_size_request(self.realWidth - 20,
- self.height - 20)
-
- def _resizeChildrens(self):
- # resize the childrens to self.height
- # also need to move them to their correct position
- # TODO : check if there already at the given position
- # TODO : check if they already have the good size
- if self._editingMode:
- return
- pos = 2 * DEFAULT_SIMPLE_SPACING
- for source in self.condensed:
- widget = self.widgets[source]
- if isinstance(source, TimelineFileSource):
- widget.set_size_request(self.childwidth, self.childheight)
- self.move(widget, pos, DEFAULT_SIMPLE_SPACING)
- pos = pos + self.childwidth + DEFAULT_SIMPLE_SPACING
- elif isinstance(source, SimpleTransitionWidget):
- widget.set_size_request(self.childheight / 2, self.childheight)
- self.move(widget, pos, DEFAULT_SIMPLE_SPACING)
- pos = pos + self.childwidth + DEFAULT_SIMPLE_SPACING
- newwidth = pos + DEFAULT_SIMPLE_SPACING
- self.set_property("width", newwidth)
-
- ## Child callbacks
-
- def _sourceDeleteMeCb(self, unused_widget, element):
- # remove this element from the timeline
- self.timeline.videocomp.removeSource(element, collapse_neighbours=True)
-
- def _sourceEditMeCb(self, unused_widget, element):
- self.switchToEditingMode(element)
-
- def _sourceDragBeginCb(self, unused_widget, unused_context, element):
- gst.log("Timeline drag beginning on %s" % element)
- if self.draggedelement:
- gst.error("We were already doing a DnD ???")
- self.draggedelement = element
- # this element is starting to be dragged
-
- def _sourceDragEndCb(self, unused_widget, unused_context, element):
- gst.log("Timeline drag ending on %s" % element)
- if not self.draggedelement == element:
- gst.error("The DnD that ended is not the one that started before ???")
- self.draggedelement = None
- # this element is no longer dragged
-
- def _editingWidgetHideMeCb(self, unused_widget):
- self.switchToNormalMode()
- # switch back to timeline in playground !
- instance.PiTiVi.playground.switchToTimeline()
-
-
-
- ## Editing mode
-
- def _switchEditingMode(self, source, mode=True):
- """ Switch editing mode for the given TimelineSource """
- gst.log("source:%s , mode:%s" % (source, mode))
-
- if self._editingMode == mode:
- gst.warning("We were already in the correct editing mode : %s" % mode)
- return
-
- if mode and not source:
- gst.warning("You need to specify a valid TimelineSource")
- return
-
- if mode:
- # switching TO editing mode
- gst.log("Switching TO editing mode")
-
- # 1. Hide all sources
- for widget in self.widgets.itervalues():
- widget.hide()
- self.remove(widget)
-
- self._editingMode = mode
-
- # 2. Show editing widget
- self.editingWidget.setSource(source)
- self.put(self.editingWidget, 10, 10)
- self.props.width = self.realWidth
- self.editingWidget.set_size_request(self.realWidth - 20, self.height - 20)
- self.editingWidget.show()
+ def _child_drag_start(self, child, unused, event):
+ child.raise_(None)
+ self.drag_sigid = child.connect("motion_notify_event",
+ self._child_drag)
+ x, y = event_coords(self, event)
+ self.pen_down = child.props.x - (x * self.scale)
+ self._set_drag_thresholds(child)
+ return True
- else:
- gst.log("Switching back to normal mode")
- # switching FROM editing mode
+ def _set_drag_thresholds(self, item):
+ index = self.items.index(item)
+ if index > 0:
+ self.left = self.items.item_at(index - 1)
+ self.l_thresh = (self.left.props.x -
+ self.left.props.width / 2)
+ if index < len(self.items):
+ self.right = self.items.item_at(index + 1)
+ self.r_thresh = (self.right.props.x +
+ self.right.props.width / 2 - item.props.width)
+
+ def _child_drag_end(self, child, target, event):
+ self.items.tidy()
+ if self.drag_sigid:
+ child.disconnect(self.drag_sigid)
+ self.drag_sigid = None
+ self.left = None
+ self.right = None
+ return True
- # 1. Hide editing widget
- self.editingWidget.hide()
- self.remove(self.editingWidget)
-
- self._editingMode = mode
-
- # 2. Show all sources
- for widget in self.widgets.itervalues():
- self.put(widget, 0, 0)
- widget.show()
- self._resizeChildrens()
-
- def switchToEditingMode(self, source):
- """ Switch to Editing mode for the given TimelineSource """
- self._switchEditingMode(source)
-
- def switchToNormalMode(self):
- """ Switch back to normal timeline mode """
- self._switchEditingMode(None, False)
+ def _child_drag(self, child, unused, event):
+ x, y = event_coords(self, event)
+ x = (min(self.items.width, max(0, self.scale * x +
+ self.pen_down)))
+ child.props.x = x
+ if self.left:
+ if x <= self.l_thresh:
+ self.items.swap(child, self.left)
+ self._set_drag_thresholds(child)
+ if self.right:
+ if x >= self.r_thresh:
+ self.items.swap(child, self.right)
+ self._set_drag_thresholds(child)
+ return True
+ def add(self, child):
+ self.items.add_child(child)
+ dn = child.connect("button_press_event", self._child_drag_start)
+ up = child.connect("button_release_event", self._child_drag_end)
+ self.signals[child] = (up, dn)
+
+ def remove(self, child):
+ self.items.remove(child)
+ for sig in self.signals[child]:
+ child.disconnect(sig)
+
+#class SimpleTimeline(gtk.Layout):
+# """ Simple Timeline representation """
+#
+# def __init__(self, **kw):
+# gobject.GObject.__init__(self, **kw)
+#
+# self.hadjustment = self.get_property("hadjustment")
+#
+# # timeline and top level compositions
+# self.timeline = instance.PiTiVi.current.timeline
+# self.condensed = self.timeline.videocomp.condensed
+#
+# # TODO : connect signals for when the timeline changes
+#
+# # widgets correspondance dictionnary
+# # MAPPING timelineobject => widget
+# self.widgets = {}
+#
+# # edit-mode
+# # True when in editing mode
+# self._editingMode = False
+# self.editingWidget = SimpleEditingWidget()
+# self.editingWidget.connect("hide-me", self._editingWidgetHideMeCb)
+#
+# # Connect to timeline. We must remove and reset the callbacks when
+# # changing project.
+# self.project_signals = SignalGroup()
+# # FIXME: do we need this? or will the newproject sginal implicitly
+# # handle this???
+# self._connectToTimeline(instance.PiTiVi.current.timeline)
+# instance.PiTiVi.connect("new-project-loaded",
+# self._newProjectLoadedCb)
+# instance.PiTiVi.connect("project-closed", self._projectClosedCb)
+# instance.PiTiVi.connect("new-project-loading",
+# self._newProjectLoadingCb)
+# instance.PiTiVi.connect("new-project-failed",
+# self._newProjectFailedCb)
+#
+# # size
+# self.width = int(DEFAULT_WIDTH)
+# self.height = int(DEFAULT_HEIGHT)
+# self.realWidth = 0 # displayed width of the layout
+# self.childheight = int(DEFAULT_SIMPLE_ELEMENT_HEIGHT)
+# self.childwidth = int(DEFAULT_SIMPLE_ELEMENT_WIDTH)
+# self.set_size_request(int(MINIMUM_WIDTH), int(MINIMUM_HEIGHT))
+# self.set_property("width", int(DEFAULT_WIDTH))
+# self.set_property("height", int(DEFAULT_HEIGHT))
+#
+# # event callbacks
+# self.connect("expose-event", self._exposeEventCb)
+# self.connect("notify::width", self._widthChangedCb)
+# self.connect("size-allocate", self._sizeAllocateCb)
+# self.connect("realize", self._realizeCb)
+#
+# # drag and drop
+# self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
+# [dnd.FILESOURCE_TUPLE],
+# gtk.gdk.ACTION_COPY)
+# self.connect("drag-data-received", self._dragDataReceivedCb)
+# self.connect("drag-leave", self._dragLeaveCb)
+# self.connect("drag-motion", self._dragMotionCb)
+# self.slotposition = -1
+#
+# self.draggedelement = None
+#
+# self.show_all()
+#
+#
+# ## Project callbacks
+#
+# def _connectToTimeline(self, timeline):
+# self.timeline = timeline
+# self.condensed = self.timeline.videocomp.condensed
+# self.project_signals.connect(self.timeline.videocomp,
+# "condensed-list-changed",
+# None, self._condensedListChangedCb)
+#
+# def _newProjectLoadingCb(self, unused_inst, unused_project):
+# gst.log("...")
+#
+# def _newProjectLoadedCb(self, unused_inst, project):
+# gst.log("...")
+# assert(instance.PiTiVi.current == project)
+# # now we connect to the new project, so we can receive any
+# # signals that might be emitted while the project is loading
+# self._connectToTimeline(project.timeline)
+# # TODO: display final state of project now that loading has
+# # completed. this callback doesn't do do much else
+#
+# # LOAD THE TIMELINE !!!
+# self._condensedListChangedCb(None, self.timeline.videocomp.condensed)
+#
+# def _newProjectFailedCb(self, unused_inst, unused_reason, unused_uri):
+# # oops the project failed to load
+# self._clearTimeline()
+#
+# def _clearTimeline(self):
+# self.switchToNormalMode()
+# self.project_signals.disconnectAll()
+# for widget in self.widgets.itervalues():
+# self.remove(widget)
+# self.widgets = {}
+#
+# def _projectClosedCb(self, unused_pitivi, unused_project):
+# self._clearTimeline()
+#
+# ## Timeline callbacks
+#
+# def _condensedListChangedCb(self, unused_videocomp, clist):
+# """ add/remove the widgets """
+# gst.debug("condensed list changed in videocomp")
+#
+# current = self.widgets.keys()
+# self.condensed = clist
+#
+# new = [x for x in clist if not x in current]
+# removed = [x for x in current if not x in clist]
+#
+# # new elements
+# for element in new:
+# # add the widget to self.widget
+# gst.debug("Adding new element to the layout")
+# if isinstance(element, TimelineFileSource):
+# widget = SimpleSourceWidget(element)
+# widget.connect("delete-me", self._sourceDeleteMeCb, element)
+# widget.connect("edit-me", self._sourceEditMeCb, element)
+# widget.connect("drag-begin", self._sourceDragBeginCb, element)
+# widget.connect("drag-end", self._sourceDragEndCb, element)
+# else:
+# widget = SimpleTransitionWidget(element)
+# self.widgets[element] = widget
+# self.put(widget, 0, 0)
+# widget.show()
+#
+# # removed elements
+# for element in removed:
+# self.remove(self.widgets[element])
+# del self.widgets[element]
+#
+# self._resizeChildrens()
+#
+#
+# ## Utility methods
+#
+# def _getNearestSourceSlot(self, x):
+# """
+# returns the nearest file slot position available for the given position
+# Returns the value in condensed list position
+# Returns n , the element before which it should go
+# Return -1 if it's meant to go last
+# """
+# if not self.condensed or x < 0:
+# return 0
+# if x > self.width - DEFAULT_SIMPLE_SPACING:
+# return -1
+#
+# pos = DEFAULT_SIMPLE_SPACING
+# order = 0
+# # TODO Need to avoid getting position between source and transition
+# for source in self.condensed:
+# if isinstance(source, TimelineSource):
+# spacing = self.childwidth
+# elif isinstance(source, TimelineTransition):
+# spacing = self.childwidth / 2
+# else:
+# # this shouldn't happen !! The condensed list only contains
+# # sources and/or transitions
+# pass
+# if x <= pos + spacing / 2:
+# return order
+# pos = pos + spacing + DEFAULT_SIMPLE_SPACING
+# order = order + 1
+# return -1
+#
+# def _getNearestSourceSlotPixels(self, x):
+# """
+# returns the nearest file slot position available for the given position
+# Returns the value in pixels
+# """
+# if not self.condensed or x < 0:
+# return DEFAULT_SIMPLE_SPACING
+# if x > self.width - DEFAULT_SIMPLE_SPACING:
+# return self.width - 2 * DEFAULT_SIMPLE_SPACING
+#
+# pos = DEFAULT_SIMPLE_SPACING
+# # TODO Need to avoid getting position between source and transition
+# for source in self.condensed:
+# if isinstance(source, TimelineSource):
+# spacing = self.childwidth
+# elif isinstance(source, TimelineTransition):
+# spacing = self.childwidth / 2
+# else:
+# # this shouldn't happen !! The condensed list only contains
+# # sources and/or transitions
+# pass
+# if x <= pos + spacing / 2:
+# return pos
+# pos = pos + spacing + DEFAULT_SIMPLE_SPACING
+# return pos
+#
+#
+# ## Drawing
+#
+# def _drawDragSlot(self):
+# if self.slotposition == -1:
+# return
+# self.bin_window.draw_rectangle(self.style.black_gc, True,
+# self.slotposition, DEFAULT_SIMPLE_SPACING,
+# DEFAULT_SIMPLE_SPACING, self.childheight)
+#
+# def _eraseDragSlot(self):
+# if self.slotposition == -1:
+# return
+# self.bin_window.draw_rectangle(self.style.white_gc, True,
+# self.slotposition, DEFAULT_SIMPLE_SPACING,
+# DEFAULT_SIMPLE_SPACING, self.childheight)
+#
+# def _gotFileFactory(self, filefactory, x, unused_y):
+# """ got a filefactory at the given position """
+# # remove the slot
+# self._eraseDragSlot()
+# self.slotposition = -1
+# if not filefactory or not filefactory.is_video:
+# return
+# pos = self._getNearestSourceSlot(x)
+#
+# gst.debug("_got_filefactory pos : %d" % pos)
+#
+# # we just add it here, the drawing will be done in the condensed_list
+# # callback
+# source = TimelineFileSource(factory=filefactory,
+# media_type=MEDIA_TYPE_VIDEO,
+# name=filefactory.name)
+#
+# # ONLY FOR SIMPLE TIMELINE : if video-only, we link a blank audio object
+# if not filefactory.is_audio:
+# audiobrother = TimelineBlankSource(factory=filefactory,
+# media_type=MEDIA_TYPE_AUDIO,
+# name=filefactory.name)
+# source.setBrother(audiobrother)
+#
+# if pos == -1:
+# self.timeline.videocomp.appendSource(source)
+# elif pos:
+# self.timeline.videocomp.insertSourceAfter(source, self.condensed[pos - 1])
+# else:
+# self.timeline.videocomp.prependSource(source)
+#
+# def _moveElement(self, element, x):
+# gst.debug("TimelineSource, move %s to x:%d" % (element, x))
+# # remove the slot
+# self._eraseDragSlot()
+# self.slotposition = -1
+# pos = self._getNearestSourceSlot(x)
+#
+# self.timeline.videocomp.moveSource(element, pos)
+#
+# def _widthChangedCb(self, unused_layout, property):
+# if not property.name == "width":
+# return
+# self.width = self.get_property("width")
+#
+# def _motionNotifyEventCb(self, layout, event):
+# pass
+#
+#
+# ## Drag and Drop callbacks
+#
+# def _dragMotionCb(self, unused_layout, unused_context, x, unused_y,
+# unused_timestamp):
+# # TODO show where the dragged item would go
+# pos = self._getNearestSourceSlotPixels(x + (self.hadjustment.get_value()))
+# rpos = self._getNearestSourceSlot(x + self.hadjustment.get_value())
+# gst.log("SimpleTimeline x:%d , source would go at %d" % (x, rpos))
+# if not pos == self.slotposition:
+# if not self.slotposition == -1:
+# # erase previous slot position
+# self._eraseDragSlot()
+# # draw new slot position
+# self.slotposition = pos
+# self._drawDragSlot()
+#
+# def _dragLeaveCb(self, unused_layout, unused_context, unused_timestamp):
+# gst.log("SimpleTimeline")
+# self._eraseDragSlot()
+# self.slotposition = -1
+# # TODO remove the drag emplacement
+#
+# def _dragDataReceivedCb(self, unused_layout, context, x, y, selection,
+# targetType, timestamp):
+# gst.log("SimpleTimeline, targetType:%d, selection.data:%s" % (targetType, selection.data))
+# if targetType == dnd.TYPE_PITIVI_FILESOURCE:
+# uri = selection.data
+# else:
+# context.finish(False, False, timestamp)
+# x = x + int(self.hadjustment.get_value())
+# if self.draggedelement:
+# self._moveElement(self.draggedelement, x)
+# else:
+# self._gotFileFactory(instance.PiTiVi.current.sources[uri], x, y)
+# context.finish(True, False, timestamp)
+# instance.PiTiVi.playground.switchToTimeline()
+#
+#
+# ## Drawing
+#
+# def _realizeCb(self, unused_layout):
+# self.modify_bg(gtk.STATE_NORMAL, self.style.white)
+#
+# def _areaIntersect(self, x, y, w, h, x2, y2, w2, h2):
+# """ returns True if the area intersects, else False """
+# # is zone to the left of zone2
+# z1 = gtk.gdk.Rectangle(x, y, w, h)
+# z2 = gtk.gdk.Rectangle(x2, y2, w2, h2)
+# r = z1.intersect(z2)
+# a, b, c, d = r
+# if a or b or c or d:
+# return True
+# return False
+#
+# def _exposeEventCb(self, unused_layout, event):
+# x, y, w, h = event.area
+# # redraw the slot rectangle if there's one
+# if not self.slotposition == -1:
+# if self._areaIntersect(x, y, w, h,
+# self.slotposition, DEFAULT_SIMPLE_SPACING,
+# DEFAULT_SIMPLE_SPACING, self.childheight):
+# self.bin_window.draw_rectangle(self.style.black_gc, True,
+# self.slotposition, DEFAULT_SIMPLE_SPACING,
+# DEFAULT_SIMPLE_SPACING, self.childheight)
+#
+# return False
+#
+# def _sizeAllocateCb(self, unused_layout, allocation):
+# if not self.height == allocation.height:
+# self.height = allocation.height
+# self.childheight = self.height - 2 * DEFAULT_SIMPLE_SPACING
+# self.childwidth = int(self.height / DEFAULT_SIMPLE_SIZE_RATIO)
+# self._resizeChildrens()
+# self.realWidth = allocation.width
+# if self._editingMode:
+# self.editingWidget.set_size_request(self.realWidth - 20,
+# self.height - 20)
+#
+# def _resizeChildrens(self):
+# # resize the childrens to self.height
+# # also need to move them to their correct position
+# # TODO : check if there already at the given position
+# # TODO : check if they already have the good size
+# if self._editingMode:
+# return
+# pos = 2 * DEFAULT_SIMPLE_SPACING
+# for source in self.condensed:
+# widget = self.widgets[source]
+# if isinstance(source, TimelineFileSource):
+# widget.set_size_request(self.childwidth, self.childheight)
+# self.move(widget, pos, DEFAULT_SIMPLE_SPACING)
+# pos = pos + self.childwidth + DEFAULT_SIMPLE_SPACING
+# elif isinstance(source, SimpleTransitionWidget):
+# widget.set_size_request(self.childheight / 2, self.childheight)
+# self.move(widget, pos, DEFAULT_SIMPLE_SPACING)
+# pos = pos + self.childwidth + DEFAULT_SIMPLE_SPACING
+# newwidth = pos + DEFAULT_SIMPLE_SPACING
+# self.set_property("width", newwidth)
+#
+# ## Child callbacks
+#
+# def _sourceDeleteMeCb(self, unused_widget, element):
+# # remove this element from the timeline
+# self.timeline.videocomp.removeSource(element, collapse_neighbours=True)
+#
+# def _sourceEditMeCb(self, unused_widget, element):
+# self.switchToEditingMode(element)
+#
+# def _sourceDragBeginCb(self, unused_widget, unused_context, element):
+# gst.log("Timeline drag beginning on %s" % element)
+# if self.draggedelement:
+# gst.error("We were already doing a DnD ???")
+# self.draggedelement = element
+# # this element is starting to be dragged
+#
+# def _sourceDragEndCb(self, unused_widget, unused_context, element):
+# gst.log("Timeline drag ending on %s" % element)
+# if not self.draggedelement == element:
+# gst.error("The DnD that ended is not the one that started before ???")
+# self.draggedelement = None
+# # this element is no longer dragged
+#
+# def _editingWidgetHideMeCb(self, unused_widget):
+# self.switchToNormalMode()
+# # switch back to timeline in playground !
+# instance.PiTiVi.playground.switchToTimeline()
+#
+#
+#
+# ## Editing mode
+#
+# def _switchEditingMode(self, source, mode=True):
+# """ Switch editing mode for the given TimelineSource """
+# gst.log("source:%s , mode:%s" % (source, mode))
+#
+# if self._editingMode == mode:
+# gst.warning("We were already in the correct editing mode : %s" % mode)
+# return
+#
+# if mode and not source:
+# gst.warning("You need to specify a valid TimelineSource")
+# return
+#
+# if mode:
+# # switching TO editing mode
+# gst.log("Switching TO editing mode")
+#
+# # 1. Hide all sources
+# for widget in self.widgets.itervalues():
+# widget.hide()
+# self.remove(widget)
+#
+# self._editingMode = mode
+#
+# # 2. Show editing widget
+# self.editingWidget.setSource(source)
+# self.put(self.editingWidget, 10, 10)
+# self.props.width = self.realWidth
+# self.editingWidget.set_size_request(self.realWidth - 20, self.height - 20)
+# self.editingWidget.show()
+#
+# else:
+# gst.log("Switching back to normal mode")
+# # switching FROM editing mode
+#
+# # 1. Hide editing widget
+# self.editingWidget.hide()
+# self.remove(self.editingWidget)
+#
+# self._editingMode = mode
+#
+# # 2. Show all sources
+# for widget in self.widgets.itervalues():
+# self.put(widget, 0, 0)
+# widget.show()
+# self._resizeChildrens()
+#
+# def switchToEditingMode(self, source):
+# """ Switch to Editing mode for the given TimelineSource """
+# self._switchEditingMode(source)
+#
+# def switchToNormalMode(self):
+# """ Switch back to normal timeline mode """
+# self._switchEditingMode(None, False)
+#
+#
class SimpleEditingWidget(gtk.EventBox):
"""
Widget for editing a source in the SimpleTimeline
Added: branches/SOC_2008_BLEWIS/pitivi/ui/util.py
==============================================================================
--- (empty file)
+++ branches/SOC_2008_BLEWIS/pitivi/ui/util.py Sat May 31 01:48:38 2008
@@ -0,0 +1,339 @@
+#Copyright (C) 2008 Brandon J. Lewis
+#
+#License:
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This package 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this package; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+#On Debian systems, the complete text of the GNU Lesser General
+#Public License can be found in `/usr/share/common-licenses/LGPL'.
+
+import gobject
+import goocanvas
+import gtk
+
+## GooCanvas Convenience Functions
+
+def event_coords(canvas, event):
+ """returns the coordinates of an event"""
+ return canvas.convert_from_pixels(event.x, event.y)
+
+def point_difference(p1, p2):
+ """Returns the 2-dvector difference p1 - p2"""
+ p1_x, p1_y = p1
+ p2_x, p2_y = p2
+ return (p1_x - p2_x, p1_y - p2_y)
+
+def point_sum(p1, p2):
+ """Returns the 2d vector sum p1 + p2"""
+ p1_x, p1_y = p1
+ p2_x, p2_y = p2
+ return (p1_x + p2_x, p1_y + p2_y)
+
+def point_mul(factor, point):
+ """Returns a scalar multiple factor * point"""
+ return tuple(factor * v for v in point)
+
+def pos(item):
+ """Returns a tuple x, y representing the position of the
+ supplied goocanvas Item"""
+ return item.props.x, item.props.y
+
+def pos_change_cb(item, prop, callback, data):
+ """Used internally, don't call this function"""
+ callback(pos(item), item, *data)
+
+def size_change_cb(item, prop, callback):
+ """Used internally, don't call this function"""
+ callback(size(item))
+
+def pos_change(item, callback, *data):
+ """Connects the callback to the x and y property notificaitons.
+ Do not call this function again without calling unlink_pos_change()
+ first"""
+ item.set_data("x_sig_hdl", item.connect("notify::x", pos_change_cb,
+ callback, data))
+ item.set_data("y_sig_hdl", item.connect("notify::y", pos_change_cb,
+ callback, data))
+
+def unlink_pos_change(item):
+ """Disconnects signal handlers after calling pos_change()"""
+ item.disconnect(item.get_data("x_sig_hdl"))
+ item.disconnect(item.get_data("y_sig_hdl"))
+
+def size(item):
+ """Returns the tuple (<width>, <height>) of item"""
+ return item.props.width, item.props.height
+
+def size_change(item, callback):
+ """Connects the callback to the width, height property notifications.
+ """
+ item.set_data("w_sig_hdl", item.connect("notify::width",
+ size_change_cb, callback))
+ item.set_data("h_sig_hdl", item.connect("notify::height",
+ size_change_cb, callback))
+
+def unlink_size_change(item):
+ item.disconnect(item.get_data("w_sig_hdl"))
+ item.disconnect(item.get_data("h_sig_hdl"))
+
+def set_pos(item, pos):
+ """Sets the position of item given pos, a tuple of (<x>, <y>)"""
+ item.props.x, item.props.y = pos
+
+def set_size(item, size):
+ """Sets the size of the item given size, a tuple of
+ (<width>, <height>)"""
+ item.props.width, item.props.height = size
+
+def width(item):
+ return item.props.width
+
+def height(item):
+ return item.props.height
+
+def center(item):
+ return point_sum(pos(item), point_mul(0.5, size(item)))
+
+def make_item(factory):
+ """Create a new goocanvas item given factory, a tuple of
+ * <class> - the class to create
+ * <properties> - initial properties to set, such as color
+ * <data> - initial data to set
+ """
+ klass, properties, data = factory
+ ret = klass(**properties)
+ for key, value in data.items():
+ ret.set_data(key, value)
+ return ret
+
+def group(*items):
+ """Wrap all the canvas items in items in a smartgroup and return the
+ resulting smartgroup. The item's current position is the offset
+ within the smartgroup"""
+ ret = SmartGroup()
+
+ for item in items:
+ ret.add_child(item, pos(item))
+
+ return ret
+
+# these are callbacks for implementing "dragable object features
+def drag_start(item, target, event, canvas, start_cb, transform):
+ """A callback which starts the drag operation of a dragable
+ object"""
+ item.set_data("dragging", True)
+ if transform:
+ coords = transform(event_coords(canvas, event))
+ print coords
+ else:
+ coords = event_coords(canvas, event)
+ item.set_data("pendown", point_difference(pos(item), coords))
+ if start_cb:
+ start_cb(item)
+ return True
+
+def drag_end(item, target, event, end_cb):
+ """A callback which ends the drag operation of a dragable object"""
+ item.set_data("dragging", False)
+ if end_cb:
+ end_cb(item)
+ return True
+
+def translate_item_group(item, target, event, canvas, transform):
+ """A callback which handles updating the position during a drag
+ operation"""
+ if item.get_data("dragging"):
+ pos = point_sum(item.get_data("pendown"),
+ event_coords(canvas, event))
+ if transform:
+ set_pos(item, transform(pos))
+ return True
+ set_pos(item, pos)
+ return True
+ return False
+
+def make_dragable(canvas, item, transform=None, start=None, end=None):
+ """Make item dragable with respect to the canvas. Call this
+ after make_selectable, or it will prevent the latter from working.
+ """
+ item.set_data("dragging", False)
+ item.connect("button_press_event", drag_start, canvas, start, transform)
+ item.connect("button_release_event", drag_end, end)
+ item.connect("motion_notify_event", translate_item_group, canvas,
+ transform)
+
+class Text(goocanvas.Text):
+ '''adds the "missing" height property to goocanvas.Text'''
+ #TODO: width/height are dumy props in this class...they
+ #should ideally be read-only values calculated from the layout
+ #parameters and text.
+ __gtype_name__ = 'SmartText'
+
+ height = gobject.property(type=float, default=0)
+ width = gobject.property(type=float, default=0)
+
+ def __init__(self, *args, **kwargs):
+ goocanvas.Text.__init__(self, *args, **kwargs)
+
+class SmartGroup(goocanvas.Group):
+ """Extends goocanvas.Group() with
+ through gobject properties x, y, and width/height"""
+ __gtype_name__ = 'SmartGroup'
+
+ x = gobject.property(type=float, default=0)
+ y = gobject.property(type=float, default=0)
+ width = gobject.property(type=float, default=0)
+ height = gobject.property(type=float, default=0)
+
+ def __init__(self, *args, **kwargs):
+ goocanvas.Group.__init__(self, *args, **kwargs)
+ self.children = {}
+ self.signals = {}
+ self.connect("notify::x", self.move_x_children)
+ self.connect("notify::y", self.move_y_children)
+
+ def move_x_children(self, object, prop):
+ for child, (x, y) in self.children.items():
+ child.set_property('x', self.x + x)
+
+ def move_y_children(self, object, prop):
+ for child, (x, y) in self.children.items():
+ child.set_property('y', self.y + y)
+
+ def update_width(self, obj, prop):
+ def compute(c, p):
+ return (c.get_property('width') + p[0])
+ widths = (compute(c, p) for c, p in self.children.items())
+ self.width = max(widths) if len(self.children) else float(0)
+
+ def update_height(self, obj, prop):
+ def compute(c, p):
+ return (c.get_property('height') + p[1])
+ heights = (compute(c, p) for c, p in self.children.items())
+ self.height = max(heights) if len(self.children) else float(0)
+
+ def set_child_pos(self, child, pos_):
+ set_pos(child, point_sum(pos(self), pos_))
+ self.children[child] = pos_
+
+ def add_child(self, child, p=None):
+ goocanvas.Group.add_child(self, child)
+ cw = child.connect("notify::width", self.update_width)
+ ch = child.connect("notify::height", self.update_height)
+ self.signals[child] = (cw, ch)
+ if not p:
+ self.children[child] = pos(child)
+ else:
+ self.set_child_pos(child, p)
+ self.update_width(None, None)
+ self.update_height(None, None)
+
+ def remove_child(self, child):
+ goocanvas.Group.remove_child(self, child)
+ for s in self.signals[child]:
+ child.disconnect(s)
+ del self.children[child]
+ self.update_width(None, None)
+ self.update_height(None, None)
+
+class List(SmartGroup):
+ __gytpe_name__ = 'List'
+
+ #TODO: changing this property should force update of list
+ spacing = gobject.property(type=float)
+
+ def __len__(self):
+ return len(self.order)
+
+ def __init__(self, spacing=5.0, *args, **kwargs):
+ SmartGroup.__init__(self, *args, **kwargs)
+ self.cur_pos = 0
+ self.spacing = spacing
+ self.order = []
+
+ def tidy(self):
+ cur = 0
+ i = 0
+ for child in self.order:
+ self.set_child_pos(child, self.cur(cur))
+ child.set_data("index", i)
+ cur += self.spacing + self.dimension(child)
+ i += 1
+ self.cur_pos = cur
+
+ def item_at(self, index):
+ return self.order[index]
+
+ def index(self, child):
+ return child.get_data("index")
+
+ def reorder(self, new_order):
+ order = []
+ for index in new_order:
+ order.append(self.order[index])
+ self.order = order
+ self.tidy()
+
+ def swap(self, a, b):
+ a_index = a.get_data("index")
+ b_index = b.get_data("index")
+ self.order[a_index] = b
+ self.order[b_index] = a
+
+
+ def remove_child(self, child):
+ SmartGroup.remove_child(self, child)
+ self.order.remove(child)
+ self.tidy()
+
+ def add_child(self, child):
+ SmartGroup.add_child(self, child, self.cur(self.cur_pos))
+ self.cur_pos += self.spacing + self.dimension(child)
+ self.order.append(child)
+ child.set_data("index", len(self.order))
+
+ def add(self, child):
+ self.add_child(child)
+
+ def insert_child(self, child, index):
+ SmartGroup.add_child(self, child, self.cur(self.cur_pos))
+ self.order.insert(child, index)
+ self.tidy()
+
+class VList(List):
+ __gtype_name__ = 'VList'
+
+ def __init__(self, *args, **kwargs):
+ List.__init__(self, *args, **kwargs)
+
+ def cur(self, value):
+ return (0, value)
+
+ def dimension(self, child):
+ return height(child)
+
+class HList(List):
+ __gtype_name__ = 'HList'
+
+ def __init__(self, *args, **kwargs):
+ List.__init__(self, *args, **kwargs)
+
+ def cur(self, value):
+ return (value, 0)
+
+ def dimension(self, child):
+ return width(child)
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]