40
41
"motion-notify-event":"override",
45
47
def __init__(self, hadj):
46
48
gst.log("Creating new ScaleRule")
47
49
gtk.Layout.__init__(self)
48
self.add_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
50
Zoomable.__init__(self)
51
self.add_events(gtk.gdk.POINTER_MOTION_MASK |
52
gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
49
53
self.set_hadjustment(hadj)
55
# double-buffering properties
57
# all values are in pixels
58
self.pixmap_offset = 0
59
self.pixmap_visible_width = 0
60
self.pixmap_allocated_width = 0
61
self.pixmap_old_allocated_width = -1
62
# This is the number of visible_width we allocate for the pixmap
63
self.pixmap_multiples = 2
51
65
# position is in nanoseconds
54
self.requested_time = long(0)
67
self.requested_time = gst.CLOCK_TIME_NONE
55
68
self.currentlySeeking = False
56
69
self.pressed = False
58
## ZoomableWidgetInterface methods are handled by the container (LayerStack)
59
## Except for ZoomChanged
71
## Zoomable interface override
61
73
def zoomChanged(self):
65
def getPixelWidth(self):
66
return ZoomableWidgetInterface.getPixelWidth(self) + 2 * self.border
69
## timeline position changed method
71
def timelinePositionChanged(self, value, unused_frame):
72
previous = self.position
78
## timeline position changed method
80
def timelinePositionChanged(self, value, unused_frame=None):
81
gst.debug("value : %r" % value)
82
ppos = max(self.nsToPixel(self.position) - 1, 0)
73
83
self.position = value
74
self.queue_draw_area(max(self.nsToPixel(min(value, previous)) - 5, 0),
76
self.nsToPixel(max(value, previous)) + 5,
77
self.get_allocation().height)
84
npos = max(self.nsToPixel(self.position) - 1, 0)
85
height = self.get_allocation().height
86
self.bin_window.invalidate_rect((ppos, 0, 2, height), True)
87
self.bin_window.invalidate_rect((npos, 0, 2, height), True)
79
## gtk.Widget overrides
89
## gtk.Widget overrides
81
91
def do_size_allocate(self, allocation):
82
92
gst.debug("ScaleRuler got %s" % list(allocation))
83
93
gtk.Layout.do_size_allocate(self, allocation)
84
94
width = max(self.getPixelWidth(), allocation.width)
85
gst.debug("Setting layout size to %d x %d" % (width, allocation.height))
95
gst.debug("Setting layout size to %d x %d"
96
% (width, allocation.height))
86
97
self.set_size(width, allocation.height)
87
98
# the size has changed, therefore we want to redo our pixmap
95
106
def do_expose_event(self, event):
96
107
gst.debug("exposing ScaleRuler %s" % list(event.area))
97
108
x, y, width, height = event.area
109
if (x < self.pixmap_offset) or (x+width > self.pixmap_offset + self.pixmap_allocated_width):
110
gst.debug("exposing outside boundaries !")
111
self.pixmap_offset = max(0, x + (width / 2) - (self.pixmap_allocated_width / 2))
112
gst.debug("offset is now %d" % self.pixmap_offset)
114
width = self.pixmap_allocated_width
98
116
# double buffering power !
99
self.bin_window.draw_drawable(self.style.fg_gc[gtk.STATE_NORMAL],
101
x, y, x, y, width, height)
117
self.bin_window.draw_drawable(
118
self.style.fg_gc[gtk.STATE_NORMAL],
120
x - self.pixmap_offset, y,
102
122
# draw the position
103
123
context = self.bin_window.cairo_create()
104
124
self.drawPosition(context, self.get_allocation())
126
148
self._doSeek(cur)
131
153
def _seekTimeoutCb(self):
132
154
gst.debug("timeout")
133
155
self.currentlySeeking = False
134
if not self.position == self.requested_time:
156
if (self.requested_time != gst.CLOCK_TIME_NONE) and (self.position != self.requested_time):
135
157
self._doSeek(self.requested_time)
137
160
def _doSeek(self, value, format=gst.FORMAT_TIME):
138
gst.debug("seeking to %s" % gst.TIME_ARGS (value))
161
gst.debug("seeking to %s / currentlySeeking %r" % (gst.TIME_ARGS (value),
162
self.currentlySeeking))
163
# clamping values within acceptable range
164
duration = self.getDuration()
165
if duration == gst.CLOCK_TIME_NONE:
139
171
if not self.currentlySeeking:
140
172
self.currentlySeeking = True
141
self.requested_time = value
142
gobject.timeout_add(80, self._seekTimeoutCb)
143
instance.PiTiVi.playground.seekInCurrent(value, format=format)
173
if instance.PiTiVi.playground.seekInCurrent(value, format=format):
174
self.requested_time = gst.CLOCK_TIME_NONE
175
gobject.timeout_add(80, self._seekTimeoutCb)
176
self.timelinePositionChanged(value)
178
self.currentlySeeking = False
144
179
elif format == gst.FORMAT_TIME:
145
180
self.requested_time = value
149
184
def doPixmap(self):
150
185
""" (re)create the buffered drawable for the Widget """
151
186
# we can't create the pixmap if we're not realized
152
187
if not self.flags() & gtk.REALIZED:
190
# We want to benefit from double-buffering (so as not to recreate the
191
# ruler graphics all the time) yet we don't want to allocate insanely
192
# big pixmaps (which would result in big memory usage, or even not being
193
# able to allocate such a big pixmap).
195
# We therefore create a pixmap with a width of 2 times the maximum viewable
196
# width (allocation.width)
154
198
allocation = self.get_allocation()
155
199
lwidth, lheight = self.get_size()
156
allocation.width = max(allocation.width, lwidth)
157
gst.debug("Creating pixmap(self.window, width:%d, height:%d)" % (allocation.width, allocation.height))
160
self.pixmap = gtk.gdk.Pixmap(self.bin_window, allocation.width, allocation.height)
201
self.pixmap_visible_width = allocation.width
202
self.pixmap_allocated_width = self.pixmap_visible_width * self.pixmap_multiples
203
allocation.width = self.pixmap_allocated_width
206
if (allocation.width != self.pixmap_old_allocated_width):
209
self.pixmap = gtk.gdk.Pixmap(self.bin_window, allocation.width,
211
self.pixmap_old_allocated_width = allocation.width
161
213
context = self.pixmap.cairo_create()
162
214
self.drawBackground(context, allocation)
163
215
self.drawRuler(context, allocation)
168
220
self.drawBackground(context, rect)
169
221
self.drawRuler(context, rect)
223
def getDuration(self):
224
if instance.PiTiVi.current:
225
return instance.PiTiVi.current.timeline.getDuration()
227
return gst.CLOCK_TIME_NONE
229
def getPixelWidth(self):
230
return self.nsToPixel(self.getDuration())
232
def getPixelPosition(self):
171
235
def drawBackground(self, context, allocation):
174
238
context.set_source_rgb(0.5, 0.5, 0.5)
175
context.rectangle(0, 0,
176
allocation.width, allocation.height)
239
context.rectangle(0, 0, allocation.width, allocation.height)
180
243
if self.getDuration() > 0:
181
244
context.set_source_rgb(0.8, 0.8, 0.8)
182
context.rectangle(0, 0,
183
self.getPixelWidth(), allocation.height)
245
context.rectangle(0, 0, self.getPixelWidth(), allocation.height)
187
249
context.restore()
251
def startDurationChanged(self):
252
gst.info("start/duration changed")
189
255
def drawRuler(self, context, allocation):
192
zoomRatio = self.getZoomRatio()
194
paintpos = float(self.border) + 0.5
201
# FIXME : this should be beautified (instead of all the if/elif/else)
203
#Smallest tic is 10 minutes
205
if zoomRatio < 0.006:
207
elif zoomRatio < 0.0125:
209
elif zoomRatio < 0.025:
213
elif zoomRatio < 0.5:
214
#Smallest tic is 1 minute
221
#Smallest tic is 10 seconds
228
#Smallest tic is 1 second
238
while paintpos < allocation.width:
256
# there are 4 lengths of tick mark:
257
# full height: largest increments, 1 minute
258
# 3/4 height: 10 seconds
259
# 1/2 height: 1 second
260
# 1/4 height: 1/10 second (might later be changed to 1 frame in
263
# At the highest level of magnification, all ticmarks are visible. At
264
# the lowest, only the full height tic marks are visible. The
265
# appearance of text is dependent on the spacing between tics: text
266
# only appears when there is enough space between tics for it to be
270
return context.text_extents(text)[2:4]
272
def drawTick(paintpos, height):
239
273
context.move_to(paintpos, 0)
241
if seconds % 600 == 0:
242
context.line_to(paintpos, allocation.height)
243
elif seconds % 60 == 0:
244
context.line_to(paintpos, allocation.height * 3 / 4)
245
elif seconds % 10 == 0:
246
context.line_to(paintpos, allocation.height / 2)
248
context.line_to(paintpos, allocation.height / 4)
251
# draw the text position
252
hours = seconds / 3600
253
mins = seconds % 3600 / 60
255
time = "%02d:%02d:%02d" % (hours, mins, secs)
256
txtwidth, txtheight = context.text_extents(time)[2:4]
257
context.move_to( paintpos - txtwidth / 2.0,
258
allocation.height - 2 )
259
context.show_text( time )
260
timeprint = ticspertime
263
paintpos += zoomRatio * secspertic
264
seconds += secspertic
266
#Since drawing is done in batch we can't use different styles
267
context.set_line_width(1)
268
context.set_source_rgb(0, 0, 0)
274
context.line_to(paintpos, allocation.height * height)
276
def drawText(paintpos, time, txtwidth, txtheight):
277
# draw the text position
278
time = time_to_string(time)
279
context.move_to( paintpos - txtwidth / 2.0,
280
allocation.height - 2 )
281
context.show_text( time )
283
def drawTicks(interval, height):
284
spacing = zoomRatio * interval
285
offset = self.pixmap_offset % spacing
286
paintpos = float(self.border) + 0.5
288
paintpos += spacing - offset
289
if spacing >= self.min_tick_spacing:
290
while paintpos < allocation.width:
291
drawTick(paintpos, height)
292
paintpos += zoomRatio * interval
294
def drawTimes(interval):
295
# figure out what the optimal offset is
296
spacing = zoomRatio * interval
297
offset = self.pixmap_offset % spacing
298
seconds = self.pixelToNs(self.pixmap_offset)
299
paintpos = float(self.border) + 0.5
301
seconds += self.pixelToNs(spacing - offset)
302
paintpos += spacing - offset
303
textwidth, textheight = textSize(time_to_string(0))
304
if spacing > textwidth:
305
while paintpos < allocation.width:
306
timevalue = long(seconds)
307
drawText(paintpos, timevalue, textwidth, textheight)
309
seconds += long(interval * gst.SECOND)
313
zoomRatio = self.zoomratio
314
# looks better largest tick doesn't run into the text label
315
interval_sizes = ((60, 0.80), (10, 0.75), (1, 0.5), (0.1, 0.25))
316
for interval, height in interval_sizes:
317
drawTicks(interval, height)
320
#set a slightly thicker line. This forces anti-aliasing, and gives the
322
context.set_line_width(1.1)
323
context.set_source_rgb(0.4, 0.4, 0.4)
271
325
context.restore()