~ubuntu-branches/ubuntu/trusty/pitivi/trusty

« back to all changes in this revision

Viewing changes to pitivi/ui/trackobject.py

* New upstream pre-release:
  + debian/control:
    - Update dependencies.
* debian/control:
  + Update Standards-Version to 3.8.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
import cairo
7
7
import pitivi.configure as configure
8
8
from urllib import unquote
 
9
from gettext import gettext as _
9
10
from pitivi.receiver import receiver, handler
10
11
from view import View
11
12
import controller
12
13
from zoominterface import Zoomable
13
14
from pitivi.timeline.track import TrackError
14
 
from pitivi.timeline.timeline import SELECT, SELECT_ADD, UNSELECT
 
15
from pitivi.timeline.timeline import SELECT, SELECT_ADD, UNSELECT, \
 
16
    MoveContext, TrimStartContext, TrimEndContext
15
17
from preview import Preview
 
18
from pitivi.ui.curve import Curve
16
19
import gst
17
20
from common import LAYER_HEIGHT_EXPANDED, LAYER_HEIGHT_COLLAPSED
18
21
from common import LAYER_SPACING, unpack_cairo_pattern, unpack_cairo_gradient
28
31
    os.path.join(configure.get_pixmap_dir(), "trimbar-normal.png"))
29
32
TRIMBAR_PIXBUF_FOCUS = gtk.gdk.pixbuf_new_from_file(
30
33
    os.path.join(configure.get_pixmap_dir(), "trimbar-focused.png"))
 
34
NAME_HOFFSET = 10
 
35
NAME_VOFFSET = 5
 
36
NAME_PADDING = 2
 
37
NAME_PADDING2X = 2 * NAME_PADDING
31
38
 
32
39
import gst
33
40
 
38
45
    notify = True)
39
46
 
40
47
PreferencesDialog.addColorPreference('videoClipBg',
41
 
    section = "Appearance",
42
 
    label = "Clip Background (Video)",
43
 
    description = "The background color for clips in video tracks.")
 
48
    section = _("Appearance"),
 
49
    label = _("Clip Background (Video)"),
 
50
    description = _("The background color for clips in video tracks."))
44
51
 
45
52
GlobalSettings.addConfigOption('audioClipBg',
46
53
    section = 'user-interface',
49
56
    notify = True)
50
57
 
51
58
PreferencesDialog.addColorPreference('audioClipBg',
52
 
    section = "Appearance",
53
 
    label = "Clip Background (Audio)",
54
 
    description = "The background color for clips in audio tracks.")
 
59
    section = _("Appearance"),
 
60
    label = _("Clip Background (Audio)"),
 
61
    description = _("The background color for clips in audio tracks."))
55
62
 
56
63
GlobalSettings.addConfigOption('selectedColor',
57
64
    section = 'user-interface',
60
67
    notify = True)
61
68
 
62
69
PreferencesDialog.addColorPreference('selectedColor',
63
 
    section = "Appearance",
64
 
    label = "Selection Color",
65
 
    description = "Selected clips will be tinted with this color.")
 
70
    section = _("Appearance"),
 
71
    label = _("Selection Color"),
 
72
    description = _("Selected clips will be tinted with this color."))
66
73
 
67
74
GlobalSettings.addConfigOption('clipFontDesc',
68
75
    section = 'user-interface',
71
78
    notify = True)
72
79
 
73
80
PreferencesDialog.addFontPreference('clipFontDesc',
74
 
    section = 'Appearance',
75
 
    description = "The font to use for clip titles",
76
 
    label = "Clip Font")
 
81
    section = _('Appearance'),
 
82
    label = _("Clip Font"),
 
83
    description = _("The font to use for clip titles"))
77
84
 
78
85
GlobalSettings.addConfigOption('clipFontColor',
79
86
    section = 'user-interface',
89
96
class TimelineController(controller.Controller):
90
97
 
91
98
    _cursor = ARROW
 
99
    _context = None
92
100
 
93
101
    def enter(self, unused, unused2):
94
102
        self._view.focus()
96
104
    def leave(self, unused, unused2):
97
105
        self._view.unfocus()
98
106
 
99
 
    def drag_start(self):
100
 
        pass
101
 
 
102
 
    def drag_end(self):
103
 
        self._view.timeline.rebuildEdges()
 
107
    def drag_start(self, item, target, event):
 
108
        if not self._view.element.selected:
 
109
            self._view.timeline.selection.setToObj(self._view.element, SELECT)
 
110
        tx = self._view.props.parent.get_transform()
 
111
        # store y offset for later priority calculation
 
112
        self._y_offset = tx[5]
 
113
        # zero y component of mousdown coordiante
 
114
        self._mousedown = Point(self._mousedown[0], 0)
 
115
 
 
116
    def drag_end(self, item, target, event):
 
117
        self._context.finish()
 
118
        self._context = None
 
119
        self._view.app.action_log.commit()
 
120
 
 
121
    def set_pos(self, item, pos):
 
122
        x, y = pos
 
123
        position = Zoomable.pixelToNs(x)
 
124
        priority = int((y - self._y_offset) // (LAYER_HEIGHT_EXPANDED + LAYER_SPACING))
 
125
        self._context.setMode(self._getMode())
 
126
        self._context.editTo(position, priority)
 
127
 
 
128
    def _getMode(self):
 
129
        if self._shift_down:
 
130
            return self._context.RIPPLE
 
131
        elif self._control_down:
 
132
            return self._context.ROLL
 
133
        return self._context.DEFAULT
 
134
 
 
135
    def key_press(self, keyval):
 
136
        if self._context:
 
137
            self._context.setMode(self._getMode())
 
138
 
 
139
    def key_release(self, keyval):
 
140
        if self._context:
 
141
            self._context.setMode(self._getMode())
104
142
 
105
143
class TrimHandle(View, goocanvas.Image, Zoomable):
106
144
 
109
147
 
110
148
    element = receiver()
111
149
 
112
 
    def __init__(self, element, timeline, **kwargs):
 
150
    def __init__(self, instance, element, timeline, **kwargs):
 
151
        self.app = instance
113
152
        self.element = element
114
153
        self.timeline = timeline
115
154
        goocanvas.Image.__init__(self,
134
173
 
135
174
        _cursor = LEFT_SIDE
136
175
 
137
 
        def set_pos(self, obj, pos):
138
 
            new_start = max(self._view.pixelToNs(pos[0]), 0)
139
 
            self._view.element.trimStart(new_start, snap=True)
 
176
        def drag_start(self, item, target, event):
 
177
            TimelineController.drag_start(self, item, target, event)
 
178
            self._context = TrimStartContext(self._view.timeline,
 
179
                self._view.element,
 
180
                self._view.timeline.selection.getSelectedTrackObjs())
 
181
            self._view.app.action_log.begin("trim object")
140
182
 
141
183
class EndHandle(TrimHandle):
142
184
 
146
188
 
147
189
        _cursor = RIGHT_SIDE
148
190
 
149
 
        def set_pos(self, obj, pos):
150
 
            start = self._view.element.start
151
 
            abs_pos = self._view.pixelToNs(pos[0])
152
 
            duration = max(abs_pos - start, 0)
153
 
            self._view.element.setDuration(duration, snap=True)
 
191
        def drag_start(self, item, target, event):
 
192
            TimelineController.drag_start(self, item, target, event)
 
193
            self._context = TrimEndContext(self._view.timeline,
 
194
                self._view.element,
 
195
                self._view.timeline.selection.getSelectedTrackObjs())
 
196
            self._view.app.action_log.begin("trim object")
154
197
 
155
198
class TrackObject(View, goocanvas.Group, Zoomable):
156
199
 
157
200
 
158
201
    class Controller(TimelineController):
159
202
 
160
 
        def drag_start(self):
161
 
            TimelineController.drag_start(self)
162
 
            self._view.timeline.disableUpdates()
 
203
        def drag_start(self, item, target, event):
 
204
            TimelineController.drag_start(self, item, target, event)
163
205
            self._view.raise_(None)
164
 
            tx = self._view.props.parent.get_transform()
165
 
            self._y_offset = tx[5]
166
 
            self._mousedown = Point(self._mousedown[0], 0)
167
 
            element = self._view.element
168
 
            timeline = self._view.timeline
169
 
            self._offsets = {}
170
 
            self._min_start = 0
171
 
            self._min_pri = 0
172
 
            # calculate offsets to selected clips
173
 
            if element.selected:
174
 
                for obj in timeline.selection.getSelectedTrackObjs():
175
 
                    start = obj.start - element.start
176
 
                    if start < 0:
177
 
                        self._min_start = max(self._min_start, -start)
178
 
                    priority = obj.priority - element.priority
179
 
                    if priority < 0:
180
 
                        self._min_pri = max(self._min_pri, -priority)
181
 
                    self._offsets[obj] = start, priority
182
 
                del self._offsets[element]
 
206
            self._context = MoveContext(self._view.timeline,
 
207
                self._view.element,
 
208
                self._view.timeline.selection.getSelectedTrackObjs())
 
209
            self._view.app.action_log.begin("move object")
183
210
 
184
 
        def drag_end(self):
185
 
            TimelineController.drag_end(self)
186
 
            self._view.timeline.enableUpdates()
 
211
        def _getMode(self):
 
212
            if self._shift_down:
 
213
                return self._context.RIPPLE
 
214
            return self._context.DEFAULT
187
215
 
188
216
        def click(self, pos):
189
217
            mode = SELECT
194
222
            self._view.timeline.setSelectionToObj(
195
223
                self._view.element, mode)
196
224
 
197
 
        def set_pos(self, item, pos):
198
 
            x, y = pos
199
 
            self._view.element.setStart(max(self._view.pixelToNs(x),
200
 
                self._min_start), snap=True)
201
 
            start = self._view.element.start
202
 
            priority = int(max(self._min_pri, (y - self._y_offset) // 
203
 
                (LAYER_HEIGHT_EXPANDED + LAYER_SPACING)))
204
 
            self._view.element.setObjectPriority(priority)
205
 
            for obj, (s, p) in self._offsets.iteritems():
206
 
                obj.setStart(start + s)
207
 
                obj.setPriority(priority + p)
208
 
 
209
225
    def __init__(self, instance, element, track, timeline):
210
226
        goocanvas.Group.__init__(self)
211
227
        View.__init__(self)
214
230
        self.track = track
215
231
        self.timeline = timeline
216
232
        self.namewidth = 0
 
233
        self.nameheight = 0
217
234
 
218
235
        self.bg = goocanvas.Rect(
219
236
            height=self.height, 
222
239
        self.content = Preview(element)
223
240
 
224
241
        self.name = goocanvas.Text(
225
 
            x=10,
226
 
            y=5,
227
 
            
 
242
            x= NAME_HOFFSET + NAME_PADDING,
 
243
            y= NAME_VOFFSET + NAME_PADDING,
228
244
            operator = cairo.OPERATOR_ADD,
229
245
            alignment=pango.ALIGN_LEFT)
230
246
        self.namebg = goocanvas.Rect(
231
247
            radius_x = 2,
232
248
            radius_y = 2,
233
 
            x = 8,
234
 
            y = 3,
 
249
            x = NAME_HOFFSET,
 
250
            y = NAME_VOFFSET,
235
251
            line_width = 0)
236
252
 
237
 
        self.start_handle = StartHandle(element, timeline,
 
253
        self.start_handle = StartHandle(self.app, element, timeline,
238
254
            height=self.height)
239
 
        self.end_handle = EndHandle(element, timeline,
 
255
        self.end_handle = EndHandle(self.app, element, timeline,
240
256
            height=self.height)
241
257
 
242
258
        self.selection_indicator = goocanvas.Rect(
248
264
            self.start_handle, self.end_handle, self.namebg, self.name):
249
265
            self.add_child(thing)
250
266
 
 
267
        for prop, interpolator in element.getInterpolators().itervalues():
 
268
            self.add_child(Curve(instance, element, interpolator, 50))
 
269
 
251
270
        self.element = element
252
271
        self.settings = instance.settings
253
272
        self.normal()
283
302
            self.namebg.props.visibility = goocanvas.ITEM_VISIBLE
284
303
            self.bg.props.height = LAYER_HEIGHT_EXPANDED
285
304
            self.height = LAYER_HEIGHT_EXPANDED
286
 
            self.name.props.y = 5
 
305
            self.name.props.y = NAME_VOFFSET + NAME_PADDING
287
306
 
288
307
    def getExpanded(self):
289
308
        return self._expanded
331
350
        self.name.props.font = self.settings.clipFontDesc
332
351
        self.name.props.fill_pattern = unpack_cairo_pattern(
333
352
            self.settings.clipFontColor)
 
353
        twidth, theight = text_size(self.name)
 
354
        self.namewidth = twidth
 
355
        self.nameheight = theight
 
356
        self._update()
334
357
 
335
358
## element signals
336
359
 
340
363
                self.element.factory.name))
341
364
            twidth, theight = text_size(self.name)
342
365
            self.namewidth = twidth
343
 
            self.namebg.props.width = twidth + 6.0
344
 
            self.namebg.props.height = theight + 4.0
 
366
            self.nameheight = theight
345
367
            self._update()
346
368
 
347
369
    element = receiver(_setElement)
364
386
        self._update()
365
387
 
366
388
    def _update(self):
367
 
        x = self.nsToPixel(self.element.start)
 
389
        try:
 
390
            x = self.nsToPixel(self.element.start)
 
391
        except Exception, e:
 
392
            print self.element.start
 
393
            raise Exception(e)
368
394
        y = (self.height + LAYER_SPACING) * self.element.priority
369
395
        self.set_simple_transform(x, y, 1, 0)
370
396
        width = self.nsToPixel(self.element.duration)
375
401
        self.selection_indicator.props.width = width
376
402
        self.end_handle.props.x = w
377
403
        if self.expanded:
378
 
            if w - 10 > 0:
379
 
                self.namebg.props.width = min(w - 8, self.namewidth)
 
404
            if w - NAME_HOFFSET > 0:
 
405
                self.namebg.props.height = self.nameheight + NAME_PADDING2X
 
406
                self.namebg.props.width = min(w - NAME_HOFFSET, 
 
407
                    self.namewidth + NAME_PADDING2X)
380
408
                self.namebg.props.visibility = goocanvas.ITEM_VISIBLE
381
409
            else:
382
410
                self.namebg.props.visibility = goocanvas.ITEM_INVISIBLE