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