diff options
Diffstat (limited to 'Edit/TuneInterface.py')
-rw-r--r-- | Edit/TuneInterface.py | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/Edit/TuneInterface.py b/Edit/TuneInterface.py new file mode 100644 index 0000000..f8bc4e7 --- /dev/null +++ b/Edit/TuneInterface.py @@ -0,0 +1,646 @@ +import pygtk +pygtk.require( '2.0' ) +import gtk + +import common.Config as Config + +from common.Util.Profiler import TP +from Edit.MainWindow import CONTEXT + +from common.Util.NoteDB import PARAMETER + +class TuneInterfaceParasite: + + def __init__( self, noteDB, owner, note ): + self.noteDB = noteDB + self.owner = owner + self.note = note + + self.x = self.y = self.width = -1 + + def attach( self ): + self.updateParameter( None, None ) + return self + + def destroy( self ): + self.owner.invalidate_thumbnail( self.note.page, self.x, self.y, self.width, 1 ) + + def updateParameter( self, parameter, value ): + if parameter == PARAMETER.AMPLITUDE: return + x = 2 + Config.THUMBNAIL_TRACK_RECT[self.note.track][0] + self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.onset ) + if self.note.track == Config.NUMBER_OF_TRACKS-1: # drum track + y = Config.THUMBNAIL_TRACK_RECT[self.note.track][1] + self.owner.pitchToPixelsDrum( self.note.cs.pitch ) + if x != self.x or y != self.y: + if parameter != None: # not the first update + xx = min( self.x, x ) + yy = min( self.y, y ) + endxx = max( self.endx, x + 1 ) + endyy = max( self.y, y ) + 1 + self.x = x + self.endx = x + 1 + self.y = y + self.owner.invalidate_thumbnail( self.note.page, xx, yy, endxx-xx, endyy-yy ) + else: + self.x = x + self.endx = x + 1 + self.y = y + self.owner.invalidate_thumbnail( self.note.page, x, y, 1, 1 ) + else: + y = Config.THUMBNAIL_TRACK_RECT[self.note.track][1] + self.owner.pitchToPixels( self.note.cs.pitch ) + width = max( 1, self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.duration ) ) + if x != self.x or y != self.y or width != self.width: + if parameter != None: # not the first update + xx = min( self.x, x ) + yy = min( self.y, y ) + endxx = max( self.endx, x + width ) + endyy = max( self.y, y ) + 1 + self.x = x + self.endx = x + width + self.y = y + self.width = width + self.owner.invalidate_thumbnail( self.note.page, xx, yy, endxx-xx, endyy-yy ) + else: + self.x = x + self.endx = x + width + self.y = y + self.width = width + self.owner.invalidate_thumbnail( self.note.page, x, y, width, 1 ) + + def draw( self, win, gc, startX, stopX ): + if stopX < self.x: return False # we don't need to draw and no one after us will draw + if startX > self.endx: return True # we don't need to draw, but maybe a later note does + + win.draw_line( gc, self.x, self.y, self.endx, self.y ) + + return True # we drew something + + +class TuneInterface( gtk.EventBox ): + + DRAG_BLOCK = -1 # block other drag events + DRAG_SELECT = 1 + DRAG_DESELECT = 2 + DRAG_MOVE = 3 + + def __init__( self, noteDB, owner, adjustment ): + gtk.EventBox.__init__( self ) + + self.noteDB = noteDB + self.owner = owner + self.adjustment = adjustment + #adjustment.connect( "changed", self.adjustmentChanged ) + adjustment.connect( "value-changed", self.adjustmentValue ) + + self.drawingArea = gtk.DrawingArea() + self.drawingAreaDirty = False # is the drawingArea waiting to draw? + self.add( self.drawingArea ) + self.dirtyRectToAdd = gtk.gdk.Rectangle() # used by the invalidate_rect function + + self.selectedIds = [] + self.displayedPage = -1 + + self.drumIndex = Config.NUMBER_OF_TRACKS-1 + + self.trackRect = Config.THUMBNAIL_TRACK_RECT + self.thumbnail = {} + self.thumbnailDirty = {} + self.thumbnailDirtyRect = {} + self.defaultwin = gtk.gdk.get_default_root_window() # used when creating pixmaps + self.gc = gtk.gdk.GC( self.defaultwin ) + colormap = self.drawingArea.get_colormap() + self.bgColor = colormap.alloc_color( Config.TOOLBAR_BCK_COLOR, True, True ) + self.lineColor = colormap.alloc_color( Config.THUMBNAIL_DRAG_COLOR, True, True ) + self.displayedColor = colormap.alloc_color( Config.THUMBNAIL_DISPLAYED_COLOR, True, True ) + self.selectedColor = colormap.alloc_color( Config.THUMBNAIL_SELECTED_COLOR, True, True ) + + # prepare thumbnail + self.thumbnailBG = [] + self.gc.foreground = self.bgColor + for i in range(4): + pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+"pageThumbnailBG%d.png"%i ) + self.thumbnailBG.append( gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) ) + self.thumbnailBG[i].draw_rectangle( self.gc, True, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + self.thumbnailBG[i].draw_pixbuf( self.gc, pix, 0, 0, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT, gtk.gdk.RGB_DITHER_NONE ) + + # load clipmask + pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'pageThumbnailMask.png') + pixels = pix.get_pixels() + stride = pix.get_rowstride() + channels = pix.get_n_channels() + bitmap = "" + byte = 0 + shift = 0 + for j in range(pix.get_height()): + offset = stride*j + for i in range(pix.get_width()): + if pixels[i*channels+offset] != "\0": + byte += 1 << shift + shift += 1 + if shift > 7: + bitmap += "%c" % byte + byte = 0 + shift = 0 + if shift: + bitmap += "%c" % byte + byte = 0 + shift = 0 + self.clipMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() ) + self.clearMask = gtk.gdk.Rectangle( 0, 0, 1200, 800 ) + + self.pageOffset = 5 # offset the first page by this + self.dropWidth = 5 # line thickness of the drop head + self.dropWidthDIV2 = self.dropWidth//2 + + self.pixelsPerPitch = float(self.trackRect[0][3]-1)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH) + self.pixelsPerPitchDrum = float(self.trackRect[self.drumIndex][3]-1)/(Config.MAXIMUM_PITCH_DRUM - Config.MINIMUM_PITCH_DRUM ) + self.pixelsPerTick = [0] + [ float(self.trackRect[0][2]-4)/(i*Config.TICKS_PER_BEAT) for i in range(1,Config.MAXIMUM_BEATS+1) ] + + self.alloced = False + self.width = self.baseWidth = self.height = -1 + self.waitingForAlloc = True + self.scrollTo = None + self.clickX = -1 + + self.set_size_request( self.width, self.height ) + + self.button1Down = False + self.dragMode = None + self.dropAt = -1 + self.dropAtX = 0 + + self.visibleX = 0 + self.visibleEndX = 0 + + self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK) + + self.connect( "size-allocate", self.size_allocated ) + self.drawingArea.connect( "expose-event", self.draw ) + self.connect( "button-press-event", self.handleButtonPress ) + self.connect( "button-release-event", self.handleButtonRelease ) + self.connect( "motion-notify-event", self.handleMotion ) + + def size_allocated( self, widget, allocation ): + if not self.alloced: + self.baseWidth = allocation.width + self.visibleEndX = self.baseWidth + self.baseHeight = allocation.height + self.alloced = True + self.updateSize() + self.width = allocation.width + self.height = allocation.height + self.drawingArea.set_size_request( self.width, self.height ) + self.clearMask.height = self.height + self.clearMask.width = self.width + + self.pageY = 2 + (self.height-Config.PAGE_THUMBNAIL_HEIGHT)//2 + + if self.scrollTo != None: + if self.scrollTo >= 0: self.adjustment.set_value( self.scrollTo ) + else: self.adjustment.set_value( self.width - self.baseWidth ) + self.scrollTo = None + + self.waitingForAlloc = False + + def adjustmentValue( self, adj ): + self.visibleX = int(adj.value) + self.visibleEndX = self.visibleX + self.baseWidth + + def updateSize( self ): + width = self.noteDB.getPageCount()*Config.PAGE_THUMBNAIL_WIDTH + 5 # add extra 5 for the first page + self.waitingForAlloc = True + if width < self.baseWidth: + self.pageOffset = ( self.baseWidth - width ) // 2 + 5 + else: + self.pageOffset = 5 + + if self.alloced: + self.set_size_request( max( self.baseWidth, width), -1 ) + self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height ) + + def handleButtonPress( self, widget, event ): + if event.button != 1: + # bring up properties or something + return + + self.button1Down = True + + self.owner.abortPredrawPage() + + ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH + if ind >= self.noteDB.getPageCount(): + if self.dragMode != self.DRAG_MOVE: + self.dragMode = self.DRAG_BLOCK + return + if ind < 0: ind = 0 + + self.clickX = event.x + + id = self.noteDB.getPageByIndex( ind ) + + if event.type == gtk.gdk._3BUTTON_PRESS: # triple click -> select all + self.owner.displayPage( id ) + self.selectAll() + elif event.type == gtk.gdk._2BUTTON_PRESS: # double click -> exclusive select + self.owner.displayPage( id ) + self.selectPage( id ) + else: + if Config.ModKeys.ctrlDown: + if id in self.selectedIds: # ctrl click, selected page -> remove page from selection + if self.deselectPage( id ): + self.dragMode = self.DRAG_DESELECT + self.dragLastInd = ind + else: + self.dragMode = self.DRAG_SELECT # special case, they clicked on the last selected page and it wasn't deselected + self.dragLastInd = ind + else: # ctrl click, unselected page -> add page to selection (but don't display it) + self.selectPage( id, False ) + self.dragMode = self.DRAG_SELECT + self.dragLastInd = ind + elif id in self.selectedIds: # click, selected page -> display this page but don't change the selection + self.owner.displayPage( id ) + else: # click, unselected page -> exclusive select + self.owner.displayPage( id ) + self.selectPage( id ) + + + self.owner.setContext( CONTEXT.PAGE ) + + def handleButtonRelease( self, widget, event ): + if event.button != 1: + return + + self.button1Down = False + + if self.dragMode == self.DRAG_MOVE: + self.invalidate_rect( self.dropAtX - self.dropWidthDIV2, 0, self.dropWidth, self.height ) # drop head + + if self.dropAt > 0: after = self.noteDB.getPageByIndex( self.dropAt-1 ) + else: after = False + + self.noteDB.movePages( self.selectedIds, after ) + + self.dropAt = -1 + + self.dragMode = None + + def handleMotion( self, widget, event ): + + if event.is_hint: + x, y, state = self.window.get_pointer() + event.x = float(x) + event.y = float(y) + event.state = state + + if self.button1Down: # clicking + if Config.ModKeys.ctrlDown and (self.dragMode == None or self.dragMode == self.DRAG_MOVE): + self.dropAt = -1 + self.dragMode = self.DRAG_SELECT + if event.x >= self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH + else: ind = 0 + self.dragLastInd = ind + + if self.dragMode == self.DRAG_SELECT: # select on drag + if event.x > self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH + else: ind = 0 + pageCount = self.noteDB.getPageCount() + if ind >= pageCount: ind = pageCount-1 + for i in range( min(ind,self.dragLastInd), max(ind,self.dragLastInd)+1): + self.selectPage( self.noteDB.getPageByIndex(i), False ) + self.dragLastInd = ind + elif self.dragMode == self.DRAG_DESELECT: # deselect on drag + if event.x > self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH + else: ind = 0 + pageCount = self.noteDB.getPageCount() + if ind >= pageCount: ind = pageCount-1 + for i in range( min(ind,self.dragLastInd), max(ind,self.dragLastInd)+1): + self.deselectPage( self.noteDB.getPageByIndex(i) ) + self.dragLastInd = ind + elif self.dragMode == None and abs(self.clickX-event.x) > 20: # drag and drop + self.dragMode = self.DRAG_MOVE + + if self.dragMode == self.DRAG_MOVE: + if self.dropAt >= 0: lastX = self.dropAtX + else: lastX = -1 + if event.x > self.pageOffset: self.dropAt = int(event.x-self.pageOffset+Config.PAGE_THUMBNAIL_WIDTH_DIV2)//Config.PAGE_THUMBNAIL_WIDTH + else: self.dropAt = 0 + c = self.noteDB.getPageCount() + if self.dropAt > c: self.dropAt = c + self.dropAtX = self.pageOffset + self.dropAt*Config.PAGE_THUMBNAIL_WIDTH - self.dropWidthDIV2 - 1 + if lastX >= 0 and lastX != self.dropAtX: + if lastX < self.dropAtX: + x = lastX - self.dropWidthDIV2 + w = self.dropAtX - lastX + self.dropWidth + else: + x = self.dropAtX - self.dropWidthDIV2 + w = lastX - self.dropAtX + self.dropWidth + self.invalidate_rect( x, 0, w, self.height ) + elif lastX == -1: + self.invalidate_rect( self.dropAtX-self.dropWidthDIV2, 0, self.dropWidth, self.height ) + + else: # hovering + ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH + if ind != self.lastPredrawInd and 0 <= ind < self.noteDB.getPageCount(): + id = self.noteDB.getPageByIndex(ind) + if id != self.displayedPage: + self.owner.predrawPage( id ) + self.lastPredrawInd = ind + + + def trackToggled( self, i ): + self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height ) + + def displayPage( self, id ): + if self.displayedPage == id: return -1 + + self.lastPredrawInd = -1 + + if self.displayedPage != -1: + ind = self.noteDB.getPageIndex( self.displayedPage ) + self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height ) + + if not self.thumbnail.has_key( id ): + # premptive add + self.thumbnail[id] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + self.thumbnailDirtyRect[id] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + self.thumbnailDirty[id] = True + self.selectPage( id ) + self.updateSize() + + self.displayedPage = id + + if id not in self.selectedIds: + self.selectPage( id ) + + ind = self.noteDB.getPageIndex( id ) + + startX = self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH + stopX = startX + Config.PAGE_THUMBNAIL_WIDTH + + if self.adjustment.value > startX: + scroll = startX + Config.PAGE_THUMBNAIL_WIDTH + Config.PAGE_THUMBNAIL_WIDTH_DIV2 - self.baseWidth + if scroll < 0: scroll = 0 + self.adjustment.set_value( scroll ) + elif self.adjustment.value + self.baseWidth < stopX: + scroll = startX - Config.PAGE_THUMBNAIL_WIDTH_DIV2 + if scroll + self.baseWidth > self.width: + if self.waitingForAlloc: + self.scrollTo = -1 + else: + self.adjustment.set_value( self.width - self.baseWidth ) + else: + if self.waitingForAlloc: + self.scrollTo = scroll + else: + self.adjustment.set_value( scroll ) + + self.invalidate_rect( startX, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height ) + + def selectPage( self, id, exclusive = True ): + if exclusive: + self._clearSelection() + + if id in self.selectedIds: return False # no change + + ind = self.noteDB.getPageIndex( id ) + l = len(self.selectedIds) + i = 0 # in case len(self.selectedIds) == 0 + while i < l: + if self.noteDB.getPageIndex( self.selectedIds[i] ) > ind: break + i += 1 + + self.selectedIds.insert( i, id ) + + self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height ) + + self.owner.updatePageSelection( self.selectedIds ) + + return True # page added to selection + + def deselectPage( self, id, force = False, skip_redraw = False, noUpdate = False ): + if not id in self.selectedIds: return False # page isn't selected + + if not force: + if len(self.selectedIds) <= 1: return False # don't deselect the last page + + if self.displayedPage == id: + i = self.selectedIds.index(id) + if i == 0: self.owner.displayPage( self.selectedIds[1] ) + else: self.owner.displayPage( self.selectedIds[i-1] ) + + self.selectedIds.remove( id ) + if not skip_redraw: + ind = self.noteDB.getPageIndex( id ) + self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height ) + + if not noUpdate: + self.owner.updatePageSelection( self.selectedIds ) + + return True # page removed from the selection + + def selectPages( self, which ): + self._clearSelection() + self.selectedIds += which + + self.owner.updatePageSelection( self.selectedIds ) + + def selectAll( self ): + self.selectedIds = self.noteDB.getTune()[:] + self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height ) + + self.owner.updatePageSelection( self.selectedIds ) + + def _clearSelection( self ): + self.selectedIds = [] + self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height ) + + def getSelectedIds( self ): + return self.selectedIds + + def getDisplayedIndex( self ): + return self.selectedIds.index( self.displayedPage ) + + def getFirstSelected( self ): + return self.selectedIds[0] + + def getLastSelected( self ): + return self.selectedIds[-1] + + #======================================================= + # NoteDB notifications + + def notifyPageAdd( self, id, at ): + if not self.thumbnail.has_key(id): + self.thumbnail[id] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + self.thumbnailDirtyRect[id] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + self.thumbnailDirty[id] = True + self.selectPage( id ) + self.updateSize() + + def notifyPageDelete( self, which, safe ): + if self.displayedPage in which: + noUpdate = True + else: + noUpdate = False + for id in self.selectedIds: + if id in which: + self.deselectPage( id, True, True, noUpdate ) + for id in which: + del self.thumbnail[id] + del self.thumbnailDirtyRect[id] + del self.thumbnailDirty[id] + if self.displayedPage in which: + self.displayedPage = -1 + self.updateSize() + + def notifyPageDuplicate( self, new, at ): + for id in new: + self.thumbnail[new[id]] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + self.thumbnailDirtyRect[new[id]] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + self.thumbnailDirty[new[id]] = True + self.updateSize() + + def notifyPageMove( self, which, low, high ): + self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height ) + + def notifyPageUpdate( self, page, parameter, value ): + if parameter == PARAMETER.PAGE_BEATS: + notes = self.noteDB.getNotesByPage( page, self ) + for note in notes: + note.updateParameter( -1, -1 ) # force update transform + + elif parameter == PARAMETER.PAGE_COLOR: + self.invalidate_thumbnail( page, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + + #======================================================= + # Drawing + + def drawThumbnail( self, id, pixmap, rect ): + startX = rect.x + startY = rect.y + stopX = rect.x + rect.width + stopY = rect.y + rect.height + + # draw background + pixmap.draw_drawable( self.gc, self.thumbnailBG[self.noteDB.getPage(id).color], startX, startY, startX, startY, rect.width, rect.height+1 ) + + # draw regular tracks + self.gc.foreground = self.lineColor + self.gc.set_line_attributes( 1, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER ) + for i in range(self.drumIndex): + if startY >= self.trackRect[i+1][1]: continue + if stopY < self.trackRect[i][1]: break + + # draw notes + notes = self.noteDB.getNotesByTrack( id, i, self ) + for n in range( len(notes) ): + if not notes[n].draw( pixmap, self.gc, startX, stopX ): break + # drum track + if stopY > self.trackRect[self.drumIndex][0]: + # draw notes + notes = self.noteDB.getNotesByTrack( id, self.drumIndex, self ) + for n in range( len(notes) ): + if not notes[n].draw( pixmap, self.gc, startX, stopX ): break + + self.thumbnailDirty[id] = False + + + def draw( self, drawingArea, event ): + + startX = event.area.x + startY = event.area.y + stopX = event.area.x + event.area.width + stopY = event.area.y + event.area.height + + self.gc.set_clip_rectangle( self.clearMask ) + + # draw background + self.gc.foreground = self.bgColor + drawingArea.window.draw_rectangle( self.gc, True, startX, startY, event.area.width, event.area.height ) + + tracks = [ self.owner.getTrackSelected(i) for i in range(Config.NUMBER_OF_TRACKS) ] + + # draw pages + self.gc.set_clip_mask( self.clipMask ) + + x = self.pageOffset + endx = x + Config.PAGE_THUMBNAIL_WIDTH + for pageId in self.noteDB.getTune(): + if endx < startX: + x = endx + endx += Config.PAGE_THUMBNAIL_WIDTH + continue + if x > stopX: break + + # draw thumbnail + if self.thumbnailDirty[pageId]: + self.gc.set_clip_origin( 0, 0 ) + self.drawThumbnail( pageId, self.thumbnail[pageId], self.thumbnailDirtyRect[pageId] ) + self.gc.set_clip_origin( x, self.pageY ) + drawingArea.window.draw_drawable( self.gc, self.thumbnail[pageId], 0, 0, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + + # draw border if necessary + if pageId == self.displayedPage: # displayed page border + self.gc.set_function( gtk.gdk.INVERT ) + for i in range(Config.NUMBER_OF_TRACKS): + if tracks[i]: + drawingArea.window.draw_rectangle( self.gc, True, x + self.trackRect[i][0], self.pageY + self.trackRect[i][1], self.trackRect[i][2], self.trackRect[i][3] ) + self.gc.set_function( gtk.gdk.COPY ) + self.gc.foreground = self.displayedColor + self.gc.set_clip_origin( x - Config.PAGE_THUMBNAIL_WIDTH, self.pageY ) + drawingArea.window.draw_rectangle( self.gc, True, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + elif pageId in self.selectedIds: # selected page border + self.gc.set_function( gtk.gdk.INVERT ) + for i in range(Config.NUMBER_OF_TRACKS): + if tracks[i]: + drawingArea.window.draw_rectangle( self.gc, True, x + self.trackRect[i][0], self.pageY + self.trackRect[i][1], self.trackRect[i][2], self.trackRect[i][3] ) + self.gc.set_function( gtk.gdk.COPY ) + self.gc.foreground = self.selectedColor + self.gc.set_clip_origin( x - Config.PAGE_THUMBNAIL_WIDTH, self.pageY ) + drawingArea.window.draw_rectangle( self.gc, True, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) + + x += Config.PAGE_THUMBNAIL_WIDTH + + # draw drop marker + if self.dropAt >= 0: + self.gc.set_clip_rectangle( self.clearMask ) + self.gc.set_line_attributes( self.dropWidth, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER ) + self.gc.foreground = self.lineColor + drawingArea.window.draw_line( self.gc, self.dropAtX, self.pageY+2, self.dropAtX, self.pageY+Config.PAGE_THUMBNAIL_HEIGHT-4 ) + + def invalidate_rect( self, x, y, width, height ): + if self.alloced == False: return + if x < self.visibleX: x = self.visibleX + if x + width > self.visibleEndX: width = self.visibleEndX - x + if width <= 0: return + + self.dirtyRectToAdd.x = x + self.dirtyRectToAdd.y = y + self.dirtyRectToAdd.width = width + self.dirtyRectToAdd.height = height + if self.drawingArea.window: + self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True ) + self.drawingAreaDirty = True + + def invalidate_thumbnail( self, id, x, y, width, height ): + if not self.thumbnailDirty[id]: + self.thumbnailDirtyRect[id].x = x + self.thumbnailDirtyRect[id].y = y + self.thumbnailDirtyRect[id].width = width + self.thumbnailDirtyRect[id].height = height + self.thumbnailDirty[id] = True + else: + self.dirtyRectToAdd.x = x + self.dirtyRectToAdd.y = y + self.dirtyRectToAdd.width = width + self.dirtyRectToAdd.height = height + self.thumbnailDirtyRect[id] = self.thumbnailDirtyRect[id].union( self.dirtyRectToAdd ) + + ind = self.noteDB.getPageIndex( id ) + self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height ) + + def ticksToPixels( self, beats, ticks ): + return int(round( ticks * self.pixelsPerTick[beats] )) + def pitchToPixels( self, pitch ): + return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch )) + def pitchToPixelsDrum( self, pitch ): + return int(round( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum )) |