~timo-jyrinki/ubuntu/trusty/pitivi/backport_utopic_fixes

« back to all changes in this revision

Viewing changes to .pc/10_canvas-fixes-lp_640630.patch/pitivi/ui/timelinecanvas.py

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2011-05-26 15:29:58 UTC
  • mfrom: (3.1.20 experimental)
  • Revision ID: james.westby@ubuntu.com-20110526152958-90je1myzzjly26vw
Tags: 0.13.9.90-1ubuntu1
* Resynchronize on Debian
* debian/control:
  - Depend on python-launchpad-integration
  - Drop hal from Recommends to Suggests. This version has the patch applied
    to not crash without hal.
* debian/patches/01_lpi.patch:
  - launchpad integration  
* debian/rules:
  - Use gnome.mk so a translation template is built

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# PiTiVi , Non-linear video editor
2
 
#
3
 
#       pitivi/ui/timelinecanvas.py
4
 
#
5
 
# Copyright (c) 2009, Brandon Lewis <brandon_lewis@berkeley.edu>
6
 
#
7
 
# This program is free software; you can redistribute it and/or
8
 
# modify it under the terms of the GNU Lesser General Public
9
 
# License as published by the Free Software Foundation; either
10
 
# version 2.1 of the License, or (at your option) any later version.
11
 
#
12
 
# This program is distributed in the hope that it will be useful,
13
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 
# Lesser General Public License for more details.
16
 
#
17
 
# You should have received a copy of the GNU Lesser General Public
18
 
# License along with this program; if not, write to the
19
 
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
 
# Boston, MA 02111-1307, USA.
21
 
 
22
 
import gtk
23
 
import goocanvas
24
 
from gettext import gettext as _
25
 
 
26
 
from pitivi.log.loggable import Loggable
27
 
from pitivi.receiver import receiver, handler
28
 
from pitivi.ui.track import Track
29
 
from pitivi.ui.trackobject import TrackObject
30
 
from pitivi.ui.point import Point
31
 
from pitivi.ui.zoominterface import Zoomable
32
 
from pitivi.settings import GlobalSettings
33
 
from pitivi.ui.prefs import PreferencesDialog
34
 
from pitivi.ui.common import TRACK_SPACING, unpack_cairo_pattern, \
35
 
        LAYER_HEIGHT_EXPANDED, LAYER_SPACING
36
 
from pitivi.ui.controller import Controller
37
 
from pitivi.ui.curve import KW_LABEL_Y_OVERFLOW
38
 
 
39
 
# cursors to be used for resizing objects
40
 
ARROW = gtk.gdk.Cursor(gtk.gdk.ARROW)
41
 
# TODO: replace this with custom cursor
42
 
PLAYHEAD_CURSOR = gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW)
43
 
 
44
 
GlobalSettings.addConfigOption('edgeSnapDeadband',
45
 
    section = "user-interface",
46
 
    key = "edge-snap-deadband",
47
 
    default = 5,
48
 
    notify = True)
49
 
 
50
 
PreferencesDialog.addNumericPreference('edgeSnapDeadband',
51
 
    section = _("Behavior"),
52
 
    label = _("Snap Distance (pixels)"),
53
 
    description = _("Threshold distance (in pixels) used for all snapping "
54
 
        "operations"),
55
 
    lower = 0)
56
 
 
57
 
class PlayheadController(Controller, Zoomable):
58
 
 
59
 
    _cursor = PLAYHEAD_CURSOR
60
 
 
61
 
 
62
 
    def __init__(self, *args, **kwargs):
63
 
        Controller.__init__(self, *args, **kwargs)
64
 
 
65
 
    def set_pos(self, item, pos):
66
 
        self._canvas.app.current.seeker.seek(
67
 
            Zoomable.pixelToNs(pos[0]))
68
 
 
69
 
class TimelineCanvas(goocanvas.Canvas, Zoomable, Loggable):
70
 
 
71
 
    __gtype_name__ = 'TimelineCanvas'
72
 
    __gsignals__ = {
73
 
        "scroll-event": "override",
74
 
        "expose-event" : "override",
75
 
    }
76
 
 
77
 
    _tracks = None
78
 
 
79
 
    def __init__(self, instance, timeline=None):
80
 
        goocanvas.Canvas.__init__(self)
81
 
        Zoomable.__init__(self)
82
 
        Loggable.__init__(self)
83
 
        self.app = instance
84
 
        self._selected_sources = []
85
 
        self._tracks = []
86
 
        self._height = 0
87
 
        self._position = 0
88
 
 
89
 
        self._block_size_request = False
90
 
        self.props.integer_layout = True
91
 
        self.props.automatic_bounds = False
92
 
        self.props.clear_background = False
93
 
        self.get_root_item().set_simple_transform(0, 2.0, 1.0, 0)
94
 
 
95
 
        self._createUI()
96
 
        self.timeline = timeline
97
 
        self.settings = instance.settings
98
 
 
99
 
    def _createUI(self):
100
 
        self._cursor = ARROW
101
 
        root = self.get_root_item()
102
 
        self.tracks = goocanvas.Group()
103
 
        self.tracks.set_simple_transform(0, KW_LABEL_Y_OVERFLOW, 1.0, 0)
104
 
        root.add_child(self.tracks)
105
 
        self._marquee = goocanvas.Rect(
106
 
            parent=root,
107
 
            stroke_pattern = unpack_cairo_pattern(0x33CCFF66),
108
 
            fill_pattern = unpack_cairo_pattern(0x33CCFF66),
109
 
            visibility = goocanvas.ITEM_INVISIBLE)
110
 
        self._playhead = goocanvas.Rect(
111
 
            y = -10,
112
 
            parent=root,
113
 
            line_width=1,
114
 
            fill_color_rgba=0x000000FF,
115
 
            stroke_color_rgba=0xFFFFFFFF,
116
 
            width=3)
117
 
        self._playhead_controller = PlayheadController(self._playhead)
118
 
        root.connect("motion-notify-event", self._selectionDrag)
119
 
        root.connect("button-press-event", self._selectionStart)
120
 
        root.connect("button-release-event", self._selectionEnd)
121
 
        height = (LAYER_HEIGHT_EXPANDED + TRACK_SPACING + LAYER_SPACING) * 2
122
 
        # add some padding for the horizontal scrollbar
123
 
        height += 21
124
 
        self.set_size_request(-1, height)
125
 
 
126
 
    def from_event(self, event):
127
 
        return Point(*self.convert_from_pixels(event.x, event.y))
128
 
 
129
 
    def setExpanded(self, track_object, expanded):
130
 
        track_ui = None
131
 
        for track in self._tracks:
132
 
            if track.track == track_object:
133
 
                track_ui = track
134
 
                break
135
 
 
136
 
        track_ui.setExpanded(expanded)
137
 
 
138
 
    def do_scroll_event(self, event):
139
 
        if event.state & gtk.gdk.SHIFT_MASK:
140
 
            # shift + scroll => vertical (up/down) scroll
141
 
            if event.direction == gtk.gdk.SCROLL_LEFT:
142
 
                event.direction = gtk.gdk.SCROLL_UP
143
 
            elif event.direction == gtk.gdk.SCROLL_RIGHT:
144
 
                event.direction = gtk.gdk.SCROLL_DOWN
145
 
            event.state &= ~gtk.gdk.SHIFT_MASK
146
 
        elif event.state & gtk.gdk.CONTROL_MASK:
147
 
            # zoom + scroll => zooming (up: zoom in)
148
 
            if event.direction == gtk.gdk.SCROLL_UP:
149
 
                Zoomable.zoomIn()
150
 
                return True
151
 
            elif event.direction == gtk.gdk.SCROLL_DOWN:
152
 
                Zoomable.zoomOut()
153
 
                return True
154
 
            return False
155
 
        else:
156
 
            if event.direction == gtk.gdk.SCROLL_UP:
157
 
                event.direction = gtk.gdk.SCROLL_LEFT
158
 
            elif event.direction == gtk.gdk.SCROLL_DOWN:
159
 
                event.direction = gtk.gdk.SCROLL_RIGHT
160
 
        return goocanvas.Canvas.do_scroll_event(self, event)
161
 
 
162
 
## sets the cursor as appropriate
163
 
 
164
 
    def _mouseEnterCb(self, unused_item, unused_target, event):
165
 
        event.window.set_cursor(self._cursor)
166
 
        return True
167
 
 
168
 
    def do_expose_event(self, event):
169
 
        allocation = self.get_allocation()
170
 
        width = allocation.width
171
 
        height = allocation.height
172
 
        # draw the canvas background
173
 
        # we must have props.clear_background set to False
174
 
 
175
 
        self.style.apply_default_background(event.window,
176
 
            True,
177
 
            gtk.STATE_ACTIVE,
178
 
            event.area,
179
 
            event.area.x, event.area.y,
180
 
            event.area.width, event.area.height)
181
 
 
182
 
        y = 0
183
 
        for track in self._tracks[:-1]:
184
 
            y += track.height
185
 
            self.style.paint_box(event.window,
186
 
                gtk.STATE_NORMAL,
187
 
                gtk.SHADOW_OUT,
188
 
                event.area,
189
 
                self,
190
 
                "",
191
 
                event.area.x - 5, y + 1,
192
 
                event.area.width + 10,  TRACK_SPACING - 2)
193
 
            y += TRACK_SPACING 
194
 
 
195
 
        goocanvas.Canvas.do_expose_event(self, event)
196
 
 
197
 
## implements selection marquee
198
 
 
199
 
    _selecting = False
200
 
    _mousedown = None
201
 
    _marquee = None
202
 
    _got_motion_notify = False
203
 
 
204
 
    def _normalize(self, p1, p2):
205
 
        w, h = p2 - p1
206
 
        x, y = p1
207
 
        if w < 0:
208
 
            w = abs(w)
209
 
            x -= w
210
 
        if h < 0:
211
 
            h = abs(h)
212
 
            y -= h
213
 
        return (x, y), (w, h)
214
 
 
215
 
    def _selectionDrag(self, item, target, event):
216
 
        if self._selecting:
217
 
            self._got_motion_notify = True
218
 
            cur = self.from_event(event)
219
 
            pos, size = self._normalize(self._mousedown, cur)
220
 
            self._marquee.props.x, self._marquee.props.y = pos
221
 
            self._marquee.props.width, self._marquee.props.height = size
222
 
            return True
223
 
        return False
224
 
 
225
 
    def _selectionStart(self, item, target, event):
226
 
        self._selecting = True
227
 
        self._marquee.props.visibility = goocanvas.ITEM_VISIBLE
228
 
        self._mousedown = self.from_event(event)
229
 
        self._marquee.props.width = 0
230
 
        self._marquee.props.height = 0
231
 
        self.pointer_grab(self.get_root_item(), gtk.gdk.POINTER_MOTION_MASK |
232
 
            gtk.gdk.BUTTON_RELEASE_MASK, self._cursor, event.time)
233
 
        return True
234
 
 
235
 
    def _selectionEnd(self, item, target, event):
236
 
        seeker = self.app.current.seeker
237
 
        self.pointer_ungrab(self.get_root_item(), event.time)
238
 
        self._selecting = False
239
 
        self._marquee.props.visibility = goocanvas.ITEM_INVISIBLE
240
 
        if not self._got_motion_notify:
241
 
            self.timeline.setSelectionTo(set(), 0)
242
 
            seeker.seek(Zoomable.pixelToNs(event.x))
243
 
        else:
244
 
            self._got_motion_notify = False
245
 
            mode = 0
246
 
            if event.get_state() & gtk.gdk.SHIFT_MASK:
247
 
                mode = 1
248
 
            if event.get_state() & gtk.gdk.CONTROL_MASK:
249
 
                mode = 2
250
 
            self.timeline.setSelectionTo(self._objectsUnderMarquee(), mode)
251
 
        return True
252
 
 
253
 
    def _objectsUnderMarquee(self):
254
 
        items = self.get_items_in_area(self._marquee.get_bounds(), True, True,
255
 
            True)
256
 
        if items:
257
 
            return set((item.element for item in items if isinstance(item,
258
 
                TrackObject) and item.bg in items))
259
 
        return set()
260
 
 
261
 
## playhead implementation
262
 
 
263
 
    position = 0
264
 
 
265
 
    def timelinePositionChanged(self, position):
266
 
        self.position = position
267
 
        self._playhead.props.x = self.nsToPixel(position)
268
 
 
269
 
    max_duration = 0
270
 
 
271
 
    def setMaxDuration(self, duration):
272
 
        self.max_duration = duration
273
 
        self._request_size()
274
 
 
275
 
    def _request_size(self):
276
 
        alloc = self.get_allocation()
277
 
        w = Zoomable.nsToPixel(self.max_duration)
278
 
        h = max(self._height, alloc.height)
279
 
        self.set_bounds(0, 0, w, h)
280
 
        self._playhead.props.height = h + 10
281
 
 
282
 
    def zoomChanged(self):
283
 
        if self.timeline:
284
 
            self.timeline.dead_band = self.pixelToNs(
285
 
                self.settings.edgeSnapDeadband)
286
 
            self._request_size()
287
 
            self.timelinePositionChanged(self.position)
288
 
 
289
 
## settings callbacks
290
 
 
291
 
    def _setSettings(self):
292
 
        self.zoomChanged()
293
 
 
294
 
    settings = receiver(_setSettings)
295
 
 
296
 
    @handler(settings, "edgeSnapDeadbandChanged")
297
 
    def _edgeSnapDeadbandChangedCb(self, settings):
298
 
        self.zoomChanged()
299
 
 
300
 
## Timeline callbacks
301
 
 
302
 
    def _set_timeline(self):
303
 
        while self._tracks:
304
 
            self._trackRemoved(None, 0)
305
 
        if self.timeline:
306
 
            for track in self.timeline.tracks:
307
 
                self._trackAdded(None, track)
308
 
        self.zoomChanged()
309
 
 
310
 
    timeline = receiver(_set_timeline)
311
 
 
312
 
    @handler(timeline, "track-added")
313
 
    def _trackAdded(self, timeline, track):
314
 
        track = Track(self.app, track, self.timeline)
315
 
        self._tracks.append(track)
316
 
        track.set_canvas(self)
317
 
        self.tracks.add_child(track)
318
 
        self.regroupTracks()
319
 
 
320
 
    @handler(timeline, "track-removed")
321
 
    def _trackRemoved(self, unused_timeline, position):
322
 
        track = self._tracks[position]
323
 
        del self._tracks[position]
324
 
        track.remove()
325
 
        self.regroupTracks()
326
 
 
327
 
    def regroupTracks(self):
328
 
        height = 0
329
 
        for i, track in enumerate(self._tracks):
330
 
            track.set_simple_transform(0, height, 1, 0)
331
 
            height += track.height + TRACK_SPACING
332
 
        self._height = height
333
 
        self._request_size()