7
import pitivi.configure as configure
8
from urllib import unquote
9
from gettext import gettext as _
10
from pitivi.receiver import receiver, handler
13
from zoominterface import Zoomable
14
from pitivi.timeline.track import TrackError
15
from pitivi.timeline.timeline import SELECT, SELECT_ADD, UNSELECT, \
16
SELECT_BETWEEN, MoveContext, TrimStartContext, TrimEndContext
17
from preview import Preview
18
from pitivi.ui.curve import Curve
20
from common import LAYER_HEIGHT_EXPANDED, LAYER_HEIGHT_COLLAPSED
21
from common import LAYER_SPACING, unpack_cairo_pattern, unpack_cairo_gradient
22
from pitivi.ui.point import Point
23
from pitivi.ui.prefs import PreferencesDialog
24
from pitivi.settings import GlobalSettings
25
from pitivi.stream import AudioStream, VideoStream
27
LEFT_SIDE = gtk.gdk.Cursor(gtk.gdk.LEFT_SIDE)
28
RIGHT_SIDE = gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE)
29
ARROW = gtk.gdk.Cursor(gtk.gdk.ARROW)
30
TRIMBAR_PIXBUF = gtk.gdk.pixbuf_new_from_file(
31
os.path.join(configure.get_pixmap_dir(), "trimbar-normal.png"))
32
TRIMBAR_PIXBUF_FOCUS = gtk.gdk.pixbuf_new_from_file(
33
os.path.join(configure.get_pixmap_dir(), "trimbar-focused.png"))
37
NAME_PADDING2X = 2 * NAME_PADDING
41
GlobalSettings.addConfigOption('videoClipBg',
42
section='user-interface',
43
key='videoclip-background',
47
PreferencesDialog.addColorPreference('videoClipBg',
48
section=_("Appearance"),
49
label=_("Color for video clips"),
50
description=_("The background color for clips in video tracks."))
52
GlobalSettings.addConfigOption('audioClipBg',
53
section='user-interface',
54
key='audioclip-background',
58
PreferencesDialog.addColorPreference('audioClipBg',
59
section=_("Appearance"),
60
label=_("Color for audio clips"),
61
description=_("The background color for clips in audio tracks."))
63
GlobalSettings.addConfigOption('selectedColor',
64
section='user-interface',
69
PreferencesDialog.addColorPreference('selectedColor',
70
section=_("Appearance"),
71
label=_("Selection color"),
72
description=_("Selected clips will be tinted with this color."))
74
GlobalSettings.addConfigOption('clipFontDesc',
75
section='user-interface',
80
PreferencesDialog.addFontPreference('clipFontDesc',
81
section=_('Appearance'),
83
description=_("The font to use for clip titles"))
85
GlobalSettings.addConfigOption('clipFontColor',
86
section='user-interface',
87
key='clip-font-color',
93
ink, logical = text.get_natural_extents()
94
x1, y1, x2, y2 = [pango.PIXELS(x) for x in logical]
95
return x2 - x1, y2 - y1
98
class TimelineController(controller.Controller):
102
_handle_enter_leave = False
104
def enter(self, unused, unused2):
107
def leave(self, unused, unused2):
110
def drag_start(self, item, target, event):
111
if not self._view.element.selected:
112
self._view.timeline.selection.setToObj(self._view.element, SELECT)
113
tx = self._view.props.parent.get_transform()
114
# store y offset for later priority calculation
115
self._y_offset = tx[5]
116
# zero y component of mousdown coordiante
117
self._mousedown = Point(self._mousedown[0], 0)
119
def drag_end(self, item, target, event):
120
self._context.finish()
122
self._view.app.action_log.commit()
124
def set_pos(self, item, pos):
126
position = Zoomable.pixelToNs(x + self._hadj.get_value())
127
priority = int((y - self._y_offset + self._vadj.get_value()) //
128
(LAYER_HEIGHT_EXPANDED + LAYER_SPACING))
129
self._context.setMode(self._getMode())
130
self._context.editTo(position, priority)
134
return self._context.RIPPLE
135
elif self._control_down:
136
return self._context.ROLL
137
return self._context.DEFAULT
139
def key_press(self, keyval):
141
self._context.setMode(self._getMode())
143
def key_release(self, keyval):
145
self._context.setMode(self._getMode())
148
class TrimHandle(View, goocanvas.Image, Zoomable):
150
"""A component of a TrackObject which manage's the source's edit
155
def __init__(self, instance, element, timeline, **kwargs):
157
self.element = element
158
self.timeline = timeline
159
goocanvas.Image.__init__(self,
160
pixbuf=TRIMBAR_PIXBUF,
162
pointer_events=goocanvas.EVENTS_FILL,
166
Zoomable.__init__(self)
169
self.props.pixbuf = TRIMBAR_PIXBUF_FOCUS
172
self.props.pixbuf = TRIMBAR_PIXBUF
175
class StartHandle(TrimHandle):
177
"""Subclass of TrimHandle wich sets the object's start time"""
179
class Controller(TimelineController):
183
def drag_start(self, item, target, event):
184
TimelineController.drag_start(self, item, target, event)
185
self._context = TrimStartContext(self._view.timeline,
187
self._view.timeline.selection.getSelectedTrackObjs())
188
self._view.app.action_log.begin("trim object")
191
class EndHandle(TrimHandle):
193
"""Subclass of TrimHandle which sets the objects's end time"""
195
class Controller(TimelineController):
199
def drag_start(self, item, target, event):
200
TimelineController.drag_start(self, item, target, event)
201
self._context = TrimEndContext(self._view.timeline,
203
self._view.timeline.selection.getSelectedTrackObjs())
204
self._view.app.action_log.begin("trim object")
207
class TrackObject(View, goocanvas.Group, Zoomable):
209
class Controller(TimelineController):
211
_handle_enter_leave = True
213
def drag_start(self, item, target, event):
214
TimelineController.drag_start(self, item, target, event)
215
self._context = MoveContext(self._view.timeline,
217
self._view.timeline.selection.getSelectedTrackObjs())
218
self._view.app.action_log.begin("move object")
222
return self._context.RIPPLE
223
return self._context.DEFAULT
225
def click(self, pos):
226
timeline = self._view.timeline
227
element = self._view.element
228
element_end = element.start + element.duration
229
if self._last_event.get_state() & gtk.gdk.SHIFT_MASK:
230
timeline.setSelectionToObj(element, SELECT_BETWEEN)
231
elif self._last_event.get_state() & gtk.gdk.CONTROL_MASK:
236
timeline.setSelectionToObj(element, mode)
239
x += self._hadj.get_value()
240
self._view.app.current.seeker.seek(Zoomable.pixelToNs(x))
241
timeline.setSelectionToObj(element, SELECT)
243
def __init__(self, instance, element, track, timeline):
244
goocanvas.Group.__init__(self)
246
Zoomable.__init__(self)
249
self.timeline = timeline
253
self.bg = goocanvas.Rect(
257
self.content = Preview(self.app, element)
259
self.name = goocanvas.Text(
260
x=NAME_HOFFSET + NAME_PADDING,
261
y=NAME_VOFFSET + NAME_PADDING,
262
operator=cairo.OPERATOR_ADD,
263
alignment=pango.ALIGN_LEFT)
264
self.namebg = goocanvas.Rect(
271
self.start_handle = StartHandle(self.app, element, timeline,
273
self.end_handle = EndHandle(self.app, element, timeline,
276
self.selection_indicator = goocanvas.Rect(
277
visibility=goocanvas.ITEM_INVISIBLE,
281
for thing in (self.bg, self.content, self.selection_indicator,
282
self.start_handle, self.end_handle, self.namebg, self.name):
283
self.add_child(thing)
285
for prop, interpolator in element.getInterpolators().itervalues():
286
self.add_child(Curve(instance, element, interpolator))
288
self.element = element
289
self.settings = instance.settings
294
_height = LAYER_HEIGHT_EXPANDED
296
def setHeight(self, height):
297
self._height = height
298
self.start_handle.props.height = height
299
self.end_handle.props.height = height
305
height = property(getHeight, setHeight)
309
def setExpanded(self, expanded):
310
self._expanded = expanded
311
if not self._expanded:
312
self.height = LAYER_HEIGHT_COLLAPSED
313
self.content.props.visibility = goocanvas.ITEM_INVISIBLE
314
self.namebg.props.visibility = goocanvas.ITEM_INVISIBLE
315
self.bg.props.height = LAYER_HEIGHT_COLLAPSED
316
self.name.props.y = 0
318
self.height = LAYER_HEIGHT_EXPANDED
319
self.content.props.visibility = goocanvas.ITEM_VISIBLE
320
self.namebg.props.visibility = goocanvas.ITEM_VISIBLE
321
self.bg.props.height = LAYER_HEIGHT_EXPANDED
322
self.height = LAYER_HEIGHT_EXPANDED
323
self.name.props.y = NAME_VOFFSET + NAME_PADDING
325
def getExpanded(self):
326
return self._expanded
328
expanded = property(getExpanded, setExpanded)
333
self.start_handle.props.visibility = goocanvas.ITEM_VISIBLE
334
self.end_handle.props.visibility = goocanvas.ITEM_VISIBLE
338
self.start_handle.props.visibility = goocanvas.ITEM_INVISIBLE
339
self.end_handle.props.visibility = goocanvas.ITEM_INVISIBLE
341
def zoomChanged(self):
346
def _setSettings(self):
348
self.clipAppearanceSettingsChanged()
350
settings = receiver(_setSettings)
352
@handler(settings, "audioClipBgChanged")
353
@handler(settings, "videoClipBgChanged")
354
@handler(settings, "selectedColorChanged")
355
@handler(settings, "clipFontDescChanged")
356
def clipAppearanceSettingsChanged(self, *args):
357
if isinstance(self.element.stream, VideoStream):
358
color = self.settings.videoClipBg
359
elif isinstance(self.element.stream, AudioStream):
360
color = self.settings.audioClipBg
361
pattern = unpack_cairo_gradient(color)
362
self.bg.props.fill_pattern = pattern
364
self.namebg.props.fill_pattern = pattern
366
self.selection_indicator.props.fill_pattern = unpack_cairo_pattern(
367
self.settings.selectedColor)
369
self.name.props.font = self.settings.clipFontDesc
370
self.name.props.fill_pattern = unpack_cairo_pattern(
371
self.settings.clipFontColor)
372
twidth, theight = text_size(self.name)
373
self.namewidth = twidth
374
self.nameheight = theight
379
def _setElement(self):
381
self.name.props.text = os.path.basename(unquote(
382
self.element.factory.name))
383
twidth, theight = text_size(self.name)
384
self.namewidth = twidth
385
self.nameheight = theight
388
element = receiver(_setElement)
390
@handler(element, "start-changed")
391
@handler(element, "duration-changed")
392
def startChangedCb(self, track_object, start):
395
@handler(element, "selected-changed")
396
def selected_changed(self, element, state):
398
self.selection_indicator.props.visibility = goocanvas.ITEM_VISIBLE
400
self.selection_indicator.props.visibility = \
401
goocanvas.ITEM_INVISIBLE
403
@handler(element, "priority-changed")
404
def priority_changed(self, element, priority):
409
x = self.nsToPixel(self.element.start)
411
print self.element.start
413
y = (self.height + LAYER_SPACING) * self.element.priority
414
self.set_simple_transform(x, y, 1, 0)
415
width = self.nsToPixel(self.element.duration)
416
min_width = self.start_handle.props.width * 2
417
if width < min_width:
419
w = width - self.end_handle.props.width
420
self.name.props.clip_path = "M%g,%g h%g v%g h-%g z" % (
421
0, 0, w, self.height, w)
422
self.bg.props.width = width
423
self.selection_indicator.props.width = width
424
self.end_handle.props.x = w
426
if w - NAME_HOFFSET > 0:
427
self.namebg.props.height = self.nameheight + NAME_PADDING2X
428
self.namebg.props.width = min(w - NAME_HOFFSET,
429
self.namewidth + NAME_PADDING2X)
430
self.namebg.props.visibility = goocanvas.ITEM_VISIBLE
432
self.namebg.props.visibility = goocanvas.ITEM_INVISIBLE