1
# OpenShot Video Editor is a program that creates, modifies, and edits video files.
2
# Copyright (C) 2009 Jonathan Thomas
4
# This file is part of OpenShot Video Editor (http://launchpad.net/openshot/).
6
# OpenShot Video Editor is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
11
# OpenShot Video Editor is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with OpenShot Video Editor. If not, see <http://www.gnu.org/licenses/>.
24
########################################################################
26
"""This class represents a media clip on the timeline."""
28
#----------------------------------------------------------------------
29
def __init__(self, name, position_on_track, length, resource, parent, type="transition", mask_value=50.0):
32
# init variables for clip object
34
self.position_on_track = float(position_on_track) # the time in seconds where the transition starts on the timeline
35
self.length = float(length) # the length in seconds of this transition
36
self.resource = resource # Any grey-scale image, or leave empty for a disolve
37
self.softness = 0.3 # 0.0 = no softness. 1.0 = too soft.
39
self.unique_id = str(uuid.uuid1())
40
self.parent = parent # the sequence
43
self.type = type # transition or mask
44
self.mask_value = mask_value # 0.0 to 1.0
53
def Render(self, exiting_item=None, x_offset = 0):
56
pixels_per_second = self.parent.parent.get_pixels_per_second()
57
x = float(self.position_on_track * pixels_per_second) + x_offset
58
width = self.length * pixels_per_second
62
# get bottom y of track
63
y = self.parent.y_bottom - 20
68
# get a reference to the 2 main canvas objects & theme
69
theme = self.parent.parent.project.theme
71
# Get root group of the canvas
72
canvas_right = self.parent.parent.project.form.MyCanvas
73
root_right = canvas_right.get_root_item ()
75
if exiting_item == None:
76
# Create the Group (for the clip)
77
GroupTransition = goocanvas.Group (parent = root_right)
79
# set the unique ID of the group
80
GroupTransition.set_data ("id", self.unique_id)
82
# Add a translucent blue rectangle
83
rec2 = goocanvas.Rect (parent = GroupTransition,
89
stroke_color_rgba = 1401402500,
90
fill_color_rgba = 1401402500)
91
rec2.set_data ("id", "tran_rectangle")
93
# add text to the transition
94
text1 = goocanvas.Text (parent = GroupTransition,
95
text = "%s" % self.name,
102
text1.set_data ("id", "tran_text")
104
# hide text (if transition is too small)
106
text1.set_properties(visibility = 1)
108
text1.set_properties(visibility = 2)
112
if self.type == "transition":
116
imgTrans = gtk.image_new_from_file("%s/themes/%s/transition_down.png" % (self.parent.parent.project.form.openshot_path, theme))
119
imgTrans = gtk.image_new_from_file("%s/themes/%s/transition_up.png" % (self.parent.parent.project.form.openshot_path, theme))
120
elif self.type == "mask":
122
imgTrans = gtk.image_new_from_file("%s/themes/%s/transition_mask.png" % (self.parent.parent.project.form.openshot_path, theme))
124
# create canvas image object
125
canvasImageTrans = goocanvas.Image (parent = GroupTransition,
126
pixbuf = imgTrans.get_pixbuf(),
129
canvasImageTrans.set_data ("id", "trans_direction")
131
# hide up/down image (if transition is too small)
133
canvasImageTrans.set_properties(visibility = 1)
135
canvasImageTrans.set_properties(visibility = 2)
138
# connect drag n drop events to the new cavnas group
139
GroupTransition.connect ("motion_notify_event", self.on_motion_notify_x)
140
GroupTransition.connect ("button_press_event", self.on_button_press_x)
141
GroupTransition.connect ("button_release_event", self.on_button_release_x)
145
GroupTransition = exiting_item
147
# get existing object
148
rec2 = self.get_canvas_child(GroupTransition, "tran_rectangle")
149
text1 = self.get_canvas_child(GroupTransition, "tran_text")
150
canvasImageTrans = self.get_canvas_child(GroupTransition, "trans_direction")
153
rec2.set_properties(x = x)
154
text1.set_properties(x = x + 8)
155
canvasImageTrans.set_properties(x = x + 8)
156
rec2.set_properties(width = width)
157
text1.set_properties(width = width - 8)
159
# hide text (if transition is too small)
161
text1.set_properties(visibility = 1)
163
text1.set_properties(visibility = 2)
165
# hide up/down image (if transition is too small)
167
canvasImageTrans.set_properties(visibility = 1)
169
canvasImageTrans.set_properties(visibility = 2)
171
# return the gooCanvas transition object
172
return GroupTransition
176
def get_canvas_child(self, group, requested_child_id):
177
"""this method loops though the children objects of this group looking
178
for the item with a specfic id."""
180
for index in range(0, group.get_n_children()):
181
child = group.get_child(index)
182
child_id = child.get_data ("id")
184
if child_id == requested_child_id:
190
def on_button_press_x (self, item, target, event):
191
""" This method initializes some variables needed for dragging and dropping a clip """
192
# raise the group up to the top level
195
# set the x and y where the cursor started dragging from
196
self.drag_x = event.x
197
self.drag_y = event.y
199
# only respond to the first mouse button
200
if event.button == 1:
202
# determine what cursor mode is enable (arrow, razor, snap, etc...)
203
(isArrow, isRazor, isSnap, isResize) = self.parent.parent.project.form.get_toolbar_options()
208
# change the cursor for the drag n drop operation
209
fleur = gtk.gdk.Cursor (gtk.gdk.FLEUR)
210
canvas = item.get_canvas ()
211
canvas.pointer_grab (item, gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.BUTTON_RELEASE_MASK, fleur, event.time)
216
# remember the original length and position before resizing starts
217
self.original_length = self.length
218
self.original_start_pos = self.position_on_track
225
elif event.button == 3:
227
# show the track popup menu
228
self.parent.parent.project.form.mnuTransition1.showmnu(event, self, item)
234
def on_motion_notify_x (self, item, target, event):
235
"""this method allows the clip to be dragged and dropped on a track"""
237
# get the new x,y coordinates from the mouse
241
# get the pixels per second from the parent sequence
242
pixels_per_second = self.parent.parent.get_pixels_per_second()
244
# determine end pixel of sequence
245
end_of_timeline = self.parent.parent.length * pixels_per_second
247
# determine what cursor mode is enable (arrow, razor, snap, etc...)
248
(isArrow, isRazor, isSnap, isResize) = self.parent.parent.project.form.get_toolbar_options()
253
# Move the clip based on the x, y of the mouse
254
if (event.state & gtk.gdk.BUTTON1_MASK):
256
# don't allow the clip to slide past the beginning of the canvas
257
total_x_diff = new_x - self.drag_x
258
total_y_diff = event.y - self.drag_y
259
if (item.get_bounds().x1 + total_x_diff < 0):
260
total_x_diff = 0 - item.get_bounds().x1
261
elif (item.get_bounds().x2 + total_x_diff > end_of_timeline):
262
total_x_diff = end_of_timeline - item.get_bounds().x2
264
# get the track under this transition (the top track)
265
drop_track = self.get_valid_drop(item.get_bounds().x1, item.get_bounds().y1)
267
# mark project as modified
268
self.parent.parent.project.is_modified = True
271
item.translate (total_x_diff, total_y_diff)
279
self.parent.parent.project.form.current_cursor[1] = int(event.x_root)
280
self.parent.parent.project.form.current_cursor[2] = int(event.y_root)
281
self.parent.parent.project.form.current_cursor[3] = "clip"
283
if (event.state & gtk.gdk.BUTTON1_MASK):
285
# get the direction from the cursor object
286
direction = self.parent.parent.project.form.current_cursor[0]
289
# only calculate LEFT or RIGHT when the mouse is not clicked. Once the user starts resizing
290
# a clip, we want to just use the direction in the cursor object. In other words, we can't allow
291
# the direction to change while we are resizing.
292
# determine if user is resizing the LEFT or RIGHT side of the clip
293
length = float(self.length)
295
left_of_clip = self.position_on_track * pixels_per_second
296
center_of_clip = left_of_clip + ((length / 2.0) * pixels_per_second)
297
right_of_clip = left_of_clip + (length * pixels_per_second)
300
if event.x_root < center_of_clip:
305
# if right, update the cursor the "right grab" icon
306
if direction == "right":
307
# update cursor variable
308
if self.parent.parent.project.form.current_cursor[0] != "right":
310
# change cursor to "right bar"
311
self.parent.parent.project.form.current_cursor[0] = "right"
312
self.parent.parent.project.form.MyCanvas.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE))
314
# if left, update the cursor the "left grab" icon
315
if direction == "left":
316
# update cursor variable
317
if self.parent.parent.project.form.current_cursor[0] != "left":
319
# change cursor to "left bar"
320
self.parent.parent.project.form.current_cursor[0] = "left"
321
self.parent.parent.project.form.MyCanvas.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_SIDE))
324
# Move the clip based on the x, y of the mouse (if the mouse button is pressed)
325
if (event.state & gtk.gdk.BUTTON1_MASK):
327
# Get side of clip that is being re-sized
328
side = self.parent.parent.project.form.current_cursor[0] # left or right
330
# Get the x and y difference
331
original_end_pos = (self.original_start_pos * pixels_per_second) + (self.original_length * pixels_per_second)
332
x_offset = event.x - event.x_root
335
# calculate new start position and length
336
new_position = float(event.x_root) / float(pixels_per_second)
337
new_length = float(original_end_pos - event.x_root) / pixels_per_second
338
#new_start_time = self.end_time - new_length
340
# update the properties of this clip
341
if new_position >= 0 and new_length >= 0:
343
# mark project as modified
344
self.parent.parent.project.is_modified = True
346
self.position_on_track = new_position
347
self.length = new_length
348
#self.start_time = new_start_time
350
# adjust x for windows
352
x_offset = x_offset - 2
354
# re-render this clip to the canvas (passing the canvas group to the RenderClip method)
355
self.Render(item, x_offset)
359
# calculate the new clip length
360
new_length = (event.x_root - (self.position_on_track * pixels_per_second)) / pixels_per_second
361
self.length = new_length
363
# update the properties of this clip
366
# mark project as modified
367
self.parent.parent.project.is_modified = True
369
# re-render this clip to the canvas (passing the canvas group to the RenderClip method)
370
self.Render(item, x_offset)
376
def on_button_release_x (self, item, target, event):
377
""" This method drops a clip, and snaps the clip to the nearest valid track """
379
# raise the play head to the top
380
self.parent.parent.play_head.raise_(None)
382
# get reference to the canvas, and stop dragging the item
383
canvas = item.get_canvas()
384
canvas.pointer_ungrab (item, event.time)
386
# determine what cursor mode is enable (arrow, razor, snap, etc...)
387
(isArrow, isRazor, isSnap, isResize) = self.parent.parent.project.form.get_toolbar_options()
390
# get new parent track
391
drop_track = self.get_valid_drop(item.get_bounds().x1, item.get_bounds().y1)
394
if drop_track == None:
395
# keep old parent, if no track found
396
drop_track = self.parent
398
# get bottom y of track
399
bottom_y = drop_track.y_bottom - 20
401
# get the pixels per second from the parent sequence
402
pixels_per_second = self.parent.parent.get_pixels_per_second()
404
# update the position
405
self.position_on_track = float(item.get_bounds().x1) / pixels_per_second
407
# update the parent (the primary track)
408
self.update_parent(drop_track)
409
drop_track.reorder_transitions()
413
# move the clip object on the timeline to correct position (snapping to the y and x of the track)
414
item.translate (0, bottom_y - item.get_bounds().y1)
418
self.parent.parent.project.form.MyCanvas.window.set_cursor(None)
420
# mark project as modified
421
self.parent.parent.project.is_modified = True
424
def get_valid_drop(self, x1, y1):
425
""" A clip must be dropped on a track. This method returns the track
426
object that is under the clip's current position """
428
# loop through each track
431
for track in self.parent.parent.tracks:
432
# get the top y and bottom y of each track
434
y_bottom = track.y_bottom
436
# increment track counter
437
track_counter = track_counter + 1
439
# determine if middle of clip is contained inside this track
440
if y1 > y_top and y1 < y_bottom and track_counter != len(self.parent.parent.tracks):
443
# return false if no valid track found
447
def update_parent(self, parent_track):
448
""" This method updates the x and y settings of the clip in the object model """
450
# remove clip from current parent track (if new parent is different)
451
if (parent_track != self.parent):
453
self.parent.transitions.remove(self)
455
# add to new parent track
456
parent_track.transitions.append(self)
457
self.parent = parent_track
b'\\ No newline at end of file'