def OnDraw(self, widget, event):
"""
Fires off the drawing operation.
Parameters:
widget -- reserved for GTK callbacks, don't use it explicitly.
event -- reserved for GTK callbacks, don't use it explicitly.
"""
cache = self.cachedDrawArea
self.area = event.area
#check if the expose area is within the already cached rectangle
# TODO: reenable this if
#if self.area.x < cache.x or (self.area.x + self.area.width > cache.x + cache.width):
self.DrawLine()
# Get a cairo surface for this drawing op
context = widget.window.cairo_create()
# Give it our waveform image as a source
context.set_source_surface(self.source, self.cachedDrawArea.x, self.cachedDrawArea.y)
# Blit our waveform across
context.paint()
#d = widget.window
#gc = d.new_gc()
# redraw area from saved gtk.gdk.Image
#d.draw_image(gc, self.savedLine,
# event.area.x, event.area.y,
# event.area.x, event.area.y,
# event.area.width, event.area.height)
# Draw play cursor position (add 1 so it lines up correctly)
x = int(round((self.project.transport.position - self.project.viewStart) * self.project.viewScale))+1
context.move_to(x, 0)
context.set_line_width(1)
context.line_to(x, self.allocation.height)
context.set_source_rgb(*self._PLAY_CURSOR_RGB)
context.stroke()
# Set the color
#col = gc.get_colormap().alloc_color("#FF0000")
#gc.set_foreground(col)
# And draw:
#x = int(round((self.project.transport.position - self.project.viewStart) * self.project.viewScale))
#d.draw_line(gc, x, 0, x, self.get_allocation().height)
return False
#_____________________________________________________________________
def DrawLine(self):
"""
Uses Cairo to draw the timeline onto a canvas in memory.
Must be called initially and to redraw the timeline
after moving the project start.
Parameters:
allocation -- the gtk.gdk.Rectangle allocated to the widget.
"""
allocArea = self.get_allocation()
rect = gtk.gdk.Rectangle(self.area.x - self.area.width, self.area.y,
self.area.width*3, self.area.height)
#Check if our area to cache is outside the allocated area
if rect.x < 0:
rect.x = 0
if rect.x + rect.width > allocArea.width:
rect.width = allocArea.width - rect.x
self.source = cairo.ImageSurface(cairo.FORMAT_ARGB32, rect.width, rect.height)
context = cairo.Context(self.source)
context.set_line_width(2)
context.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
# Draw white background
context.rectangle(0, 0, rect.width, rect.height)
context.set_source_rgb(*self._BACKGROUND_RGB)
context.fill()
# Draw the widget border
context.set_line_width(0.2)
context.rectangle(0, 0, rect.width, rect.height)
context.set_source_rgb(*self._BORDER_RGB)
context.stroke()
# Draw a gray frame around the background
#col = gc.get_colormap().alloc_color("#555555")
#gc.set_foreground(col)
#d.draw_rectangle( gc, False, # False = not filled
# 0,
# 0,
# self.get_allocation().width,
# self.get_allocation().height)
x = 0
transport = self.project.transport
if transport.mode == transport.MODE_BARS_BEATS:
print "bars"
# Calculate our scroll offset
# viewStart is in seconds. Seconds/60 = minutes. Minutes * Beat/Minute = beats (not an integer here)
pos = (self.project.viewStart / 60.) * self.project.bpm
# floor to an integer. beat = the last beat before viewStart
beat = int(pos)
# offset = part of a beat that has past since the last beat (offset < 1)
offset = pos - beat
if offset > 0.:
# beats * ( pixels/minute ) / ( beats/minute ) = pixels
# Set x to the position in pixels of the last beat
x -= offset * ((self.project.viewScale * 60.) / self.project.bpm)
# (pixels/minute) / ( beats/minute) * 1 beat = pixels
# Add the length of one beat, in pixels
x += (self.project.viewScale * 60.) / self.project.bpm
# x is now at the pixel-position of the first beat after the viewStart
beat += 1
while x < self.get_allocation().width:
# Draw the beat/bar divisions
ix = int(x)
if beat % self.project.meter_nom:
lineHeight = int(self.get_allocation().height/1.2)
else:
lineHeight = int(self.get_allocation().height/2)
context.move_to(ix, lineHeight)
context.line_to(ix, self.get_allocation().height)
context.set_source_rgb(*self._BEAT_BAR_RGB)
context.stroke()
# Draw the bar number
context.set_source_rgb(*self._TEXT_RGB)
context.move_to(ix, 15)
context.show_text(str((beat / self.project.meter_nom)+1))
#l = pango.Layout(self.create_pango_context())
#l.set_text(str((beat / self.project.meter_nom)+1))
#d.draw_layout(gc, ix, 5, l)
beat += 1
x += (60. / self.project.bpm ) * self.project.viewScale
else:
print "hours"
# Working in milliseconds here. Using seconds gives modulus problems because they're floats
viewScale = self.project.viewScale / 1000.
viewStart = int(self.project.viewStart * 1000)
factor, displayMilliseconds = self.GetZoomFactor(viewScale)
# Calculate our scroll offset
# sec : viewStart, truncated to 1000ms; the second that has past just before the beginning of our surface
msec = viewStart - (viewStart % 1000)
# sec : move to the last 'line' that wasn't drawn
if (msec % factor) != 0:
msec -= (msec % factor)
# offset: the amount of milliseconds since the last second before the timeline
offset = viewStart - msec
if offset > 0: # x = 0. atm, it should stay that way if offset == 0.
# offset : milliseconds
# viewScale : pixels / milliseconds
# offset * viewScale : offset in pixels
x -= offset * viewScale # return to the last 'active' second
x += viewScale * factor # positions the cursor at the first second to be drawn
msec += factor # cursor is at the first line to be drawn now
# Draw ticks up to the end of our display
while x < self.get_allocation().width:
ix = int(x)
if msec % (self._NUM_LINES * factor):
lineHeight = int(self.get_allocation().height/1.2)
else:
lineHeight = int(self.get_allocation().height/2)
context.move_to(ix, lineHeight)
context.line_to(ix, self.get_allocation().height)
context.set_source_rgb(*self._BEAT_BAR_RGB)
context.stroke()
# Draw the bar number
#l = pango.Layout(self.create_pango_context())
if displayMilliseconds:
#Should use transportmanager for this...
number = "%d:%02d:%03d"%((msec/1000) / 60, (msec/1000) % 60, msec%1000)
else:
number = "%d:%02d"%((msec/1000) / 60, (msec/1000) % 60)
context.set_source_rgb(*self._TEXT_RGB)
context.move_to(ix, 15)
context.show_text(number)
#d.draw_layout(gc, ix, 5, l)
msec += factor
x += viewScale * factor
#self.savedLine = d.get_image(0, 0, self.get_allocation().width, self.get_allocation().height)
#set area to record where the cached surface goes
self.cachedDrawArea = rect
self.redrawWaveform = False