diff options
Diffstat (limited to 'Edit/TrackInterface.py')
-rw-r--r-- | Edit/TrackInterface.py | 750 |
1 files changed, 750 insertions, 0 deletions
diff --git a/Edit/TrackInterface.py b/Edit/TrackInterface.py new file mode 100644 index 0000000..2490da7 --- /dev/null +++ b/Edit/TrackInterface.py @@ -0,0 +1,750 @@ +import pygtk +pygtk.require( '2.0' ) +import gtk + +from math import floor + +from Framework.Constants import Constants +from GUI.GUIConstants import GUIConstants +from GUI.Core.NoteInterface import NoteInterface +from GUI.Core.MainWindow import ModKeys +#from GUI.Core.NoteParametersWindow import NoteParametersWindow + +from Framework.Core.Profiler import TP + +class SELECTNOTES: + ALL = -1 + NONE = 0 + ADD = 1 + REMOVE = 2 + FLIP = 3 + EXCLUSIVE = 4 + +class INTERFACEMODE: + DEFAULT = 0 + DRAW = 1 + PASTE = 2 + +class TrackInterface( gtk.EventBox ): + + def __init__( self, onNoteDrag ): + gtk.EventBox.__init__( self ) + + 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.fullWidth = 1 # store the maximum allowed width + self.width = 1 + self.height = 1 + + self.interfaceMode = INTERFACEMODE.DRAW + + self.note = {} # list of pages, tracks, and notes: self.note[pageId][trackId][noteId] + self.pageBeatCount = {} # keep track of the beat count for each page + self.pageNoteCount = {} # keep track of how many notes are on a page (so we can get rid of them when they're empty) + self.noteMap = {} # maps note ids to self.note[p][t][i]s + + self.curPage = -1 # this isn't a real page at all! + self.beatCount = 4 + + self.trackSelected = [] + self.selectedNotes = [] + for i in range(0,Constants.NUMBER_OF_TRACKS): + self.trackSelected.insert( 0, False ) + self.selectedNotes.insert( 0, [] ) + + self.curAction = False # stores the current mouse action + self.curActionObject = False # stores the object that in handling the action + + self.buttonPressCount = 1 # used on release events to indicate double/triple releases + self.clickLoc = [0,0] # location of the last click + self.marqueeLoc = False # current drag location of the marquee + self.marqueeRect = [[0,0],[0,0]] + + self.playheadX = 0 + + self.cursor = { \ + "default": None, \ + "drag-onset": gtk.gdk.Cursor(gtk.gdk.SB_RIGHT_ARROW), \ + "drag-pitch": gtk.gdk.Cursor(gtk.gdk.SB_V_DOUBLE_ARROW), \ + "drag-duration": gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW), \ + "drag-playhead": gtk.gdk.Cursor(gtk.gdk.LEFT_SIDE), \ + "pencil": gtk.gdk.Cursor(gtk.gdk.PENCIL), \ + "error": None } + + self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK) + + self.connect( "size-allocate", self.size_allocate ) + + 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 ) + + self.onNoteDrag = onNoteDrag + + #======================================================= + # Module Interface + + def addNotes( self, noteParams, noteCount ): + at = {} + + for i in range(noteCount): + p = noteParams["page"][i] + t = noteParams["track"][i] + if p not in at: + at[p] = [0] * Constants.NUMBER_OF_TRACKS + #at[p] = [] + #for j in range(Constants.NUMBER_OF_TRACKS): at[p].append(0) + if p not in self.note: + self.note[p] = map(lambda x:[], range(Constants.NUMBER_OF_TRACKS)) + #self.note[p] = [] + #for j in range(Constants.NUMBER_OF_TRACKS): + #self.note[p].append( [] ) + self.pageBeatCount[p] = noteParams["beatCount"][i] + self.pageNoteCount[p] = 0 + csnote = noteParams["csnote"][i] + note = NoteInterface( self, p, noteParams["track"][i], noteParams["note"][i], \ + csnote["pitch"], csnote["onset"], csnote["duration"], csnote["amplitude"] ) + while at[p][t] > 0: + if self.note[p][t][at[p][t]-1].getStartTick() < csnote["onset"]: break + at[p][t] -= 1 + last = len(self.note[p][t]) + while at[p][t] < last: + if self.note[p][t][at[p][t]].getStartTick() > csnote["onset"]: break + at[p][t] += 1 + self.note[p][t].insert( at[p][t], note ) + self.pageNoteCount[p] += 1 + at[p][t] += 1 # assume the next note will fall after this one + + for page in at: + self.updateNoteMap( page ) + + def updateNotes( self, noteParams, noteCount ): + map( lambda page, track, id, csnote: \ + self.note[page][track][self.noteMap[page][id]].updateParams( csnote["pitch"], csnote["onset"], csnote["duration"], csnote["amplitude"] ), \ + noteParams["page"], noteParams["track"], noteParams["note"], noteParams["csnote"] ) + # assume that the note order will not have changed! + + # noteParams: { "page":pagelist, "track":tracklist, "note":noteIDlist } + def deleteNotes( self, noteParams, noteCount ): + modified = {} + for i in range(noteCount): + p = noteParams["page"][i] + t = noteParams["track"][i] + id = noteParams["note"][i] + if not p in modified: modified[p] = True + if p == self.curPage and self.note[p][t][self.noteMap[p][id]].getSelected(): + self.deselectNotes( { t: [ self.note[p][t][self.noteMap[p][id]] ] } ) + self.note[p][t][self.noteMap[p][id]].destroy() + self.note[p][t][self.noteMap[p][id]] = None # flag for removal + self.pageNoteCount[p] -= 1 + if self.pageNoteCount[p] == 0: + del self.note[p] + del self.pageNoteCount[p] + del self.noteMap[p] + del modified[p] + + for page in modified: + for i in range(Constants.NUMBER_OF_TRACKS): + j = len(self.note[page][i])-1 + while j >= 0: + if self.note[page][i][j] == None: del self.note[page][i][j] + j -= 1 + self.updateNoteMap( page ) + + def displayPage( self, page, beatCount ): + if page == self.curPage and self.beatCount == beatCount: return + + if self.curPage >= 0 and self.curPage != page: self.clearSelectedNotes() + + self.curPage = page + + if page not in self.note: # create a blank page if the page doesn't already exist + self.note[page] = [] + for i in range(Constants.NUMBER_OF_TRACKS): + self.note[page].append( [] ) + self.pageBeatCount[page] = beatCount + self.pageNoteCount[page] = 0 + + self.updateBeatCount( beatCount ) + + def updateBeatCount( self, beatCount ): + self.beatCount = beatCount + + # make sure this matches the calculation in size_allocate + self.beatSpacing = (self.fullWidth - GUIConstants.BORDER_SIZE_MUL2 + GUIConstants.BEAT_LINE_SIZE)/self.beatCount + self.width = self.beatSpacing * self.beatCount + GUIConstants.BORDER_SIZE_MUL2 + self.ticksPerPixel = float(self.beatCount * Constants.TICKS_PER_BEAT) / (self.width-2*GUIConstants.BORDER_SIZE) + self.pixelsPerTick = 1/self.ticksPerPixel + + if self.pageBeatCount[self.curPage] != beatCount: + self.pageBeatCount[self.curPage] = beatCount + for i in range(Constants.NUMBER_OF_TRACKS): + track = self.note[self.curPage][i] + map( lambda note:note.updateTransform( True ), track ) + + if self.drawingArea.window != None: + self.invalidate_rect( 0, 0, self.fullWidth, self.height ) + + def setPlayhead( self, ticks ): + self.invalidate_rect( self.playheadX, 0, GUIConstants.PLAYHEAD_SIZE, self.height ) + self.playheadX = self.ticksToPixels( ticks ) + GUIConstants.BORDER_SIZE + self.invalidate_rect( self.playheadX, 0, GUIConstants.PLAYHEAD_SIZE, self.height ) + + def getSelectedTracks( self ): + r = [] + for i in range( len(self.trackSelected) ): + if self.trackSelected[i]: r.append( i ) + return r + + # private + def updateNoteMap( self, page ): + self.noteMap[page] = {} + for i in range(Constants.NUMBER_OF_TRACKS): + for j in range(len(self.note[page][i])): + self.noteMap[page][self.note[page][i][j].getId()] = j + + #======================================================= + # Event Callbacks + + def size_allocate( self, widget, allocation ): + width = allocation.width + height = allocation.height + + self.drawingArea.set_size_request( width, height ) + + self.trackHeight = (height - (Constants.NUMBER_OF_TRACKS-1)*GUIConstants.TRACK_SPACING) / Constants.NUMBER_OF_TRACKS + self.height = self.trackHeight*Constants.NUMBER_OF_TRACKS + GUIConstants.TRACK_SPACING*(Constants.NUMBER_OF_TRACKS-1) + self.trackLimits = [] + self.trackOrigin = [] + for i in range(Constants.NUMBER_OF_TRACKS): + start = i*(self.trackHeight+GUIConstants.TRACK_SPACING) + self.trackLimits.insert( i, (start,start+self.trackHeight) ) + self.trackOrigin.insert( i, (GUIConstants.BORDER_SIZE,start+GUIConstants.BORDER_SIZE) ) + + self.fullWidth = width - 2 # cut off 2 pixels cause otherwise we try to draw on an area that gets cut off!? + + # make sure this matches the calculations in updateBeatCount + self.beatSpacing = (self.fullWidth - GUIConstants.BORDER_SIZE_MUL2 + GUIConstants.BEAT_LINE_SIZE)/self.beatCount + self.width = self.beatSpacing * self.beatCount + GUIConstants.BORDER_SIZE_MUL2 + self.ticksPerPixel = float(self.beatCount * Constants.TICKS_PER_BEAT) / (self.width-2*GUIConstants.BORDER_SIZE) + self.pixelsPerTick = 1/self.ticksPerPixel + + self.pitchPerPixel = float(Constants.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.trackHeight-2*GUIConstants.BORDER_SIZE-GUIConstants.NOTE_HEIGHT) + self.pixelsPerPitch = float(self.trackHeight-2*GUIConstants.BORDER_SIZE-GUIConstants.NOTE_HEIGHT)/(Constants.MAXIMUM_PITCH - Constants.MINIMUM_PITCH) + + # this could potentially take a loooong time, make sure they don't resize the window very often + for page in self.note: + for i in range(Constants.NUMBER_OF_TRACKS): + track = self.note[page][i] + map( lambda note:note.updateTransform( False ), track ) + + if self.drawingArea.window != None: + self.invalidate_rect( 0, 0, width, height ) + + def handleButtonPress( self, widget, event ): + + TP.ProfileBegin( "TI::handleButtonPress" ) + + if event.type == gtk.gdk._2BUTTON_PRESS: self.buttonPressCount = 2 + elif event.type == gtk.gdk._3BUTTON_PRESS: self.buttonPressCount = 3 + else: self.buttonPressCount = 1 + + self.clickLoc = [ event.x, event.y ] + + # check if we clicked on the playhead + if event.x >= self.playheadX and event.x <= self.playheadX + GUIConstants.PLAYHEAD_SIZE: + self.setCurrentAction( "playhead-drag", self ) + TP.ProfileEnd( "TI::handleButtonPress" ) + return + + for i in range(Constants.NUMBER_OF_TRACKS): + if self.trackLimits[i][0] > event.y: break + if self.trackLimits[i][1] < event.y: continue + + handled = 0 + notes = self.note[self.curPage][i] + for n in range(len(notes)): + handled = notes[n].handleButtonPress( self, event ) + if handled == 0: + continue + elif handled == 1: + if not self.curAction: self.curAction = True # it was handled but no action was declared, set curAction to True anyway + TP.ProfileEnd( "TI::handleButtonPress" ) + return + else: # all other options mean we can stop looking + break + + if self.interfaceMode == INTERFACEMODE.DRAW: + if handled == -1: # event occured before this note and didn't overlap with the previous note, so we can draw + print "draw a note" + + if event.button == 3: + print "Should bring up some note parameters or something!" + #self.noteParameters = NoteParametersWindow( self.trackDictionary, self.getNoteParameters ) + #self.setCurrentAction( "noteParameters", False ) + + TP.ProfileEnd( "TI::handleButtonPress" ) + + + def handleButtonRelease( self, widget, event ): + TP.ProfileBegin( "TI::handleButtonRelease" ) + + if not self.curAction: #do track selection stuff here so that we can also handle marquee selection + for i in range(Constants.NUMBER_OF_TRACKS): + if self.trackLimits[i][0] > event.y: break + if self.trackLimits[i][1] < event.y: continue + if event.button == 1: + if self.buttonPressCount == 1: self.toggleTrack( i, False ) + else: self.toggleTrack( i, True ) + break + + TP.ProfileEnd( "TI::handleButtonRelease" ) + return + + if not self.curActionObject: # there was no real action to carry out + self.curAction = False + TP.ProfileEnd( "TI::handleButtonRelease" ) + return + + if self.curActionObject != self: + self.curActionObject.handleButtonRelease( self, event, self.buttonPressCount ) + self.updateTooltip( event ) + else: + # we're doing the action ourselves + if self.curAction == "marquee": self.doneMarquee( event ) + elif self.curAction == "playhead-drag": self.donePlayhead( event ) + self.updateTooltip( event ) + + + TP.ProfileEnd( "TI::handleButtonRelease" ) + return + + def handleMotion( self, widget, event ): + TP.ProfileBegin( "TI::handleMotion::Common" ) + + + if event.is_hint: + x, y, state = self.window.get_pointer() + event.x = float(x) + event.y = float(y) + event.state = state + + TP.ProfileEnd( "TI::handleMotion::Common" ) + + if event.state & gtk.gdk.BUTTON1_MASK: + TP.ProfileBegin( "TI::handleMotion::Drag" ) + + if not self.curAction: # no action is in progress yet we're dragging, start a marquee + self.setCurrentAction( "marquee", self ) + + if self.curAction == "note-drag-onset": + self.noteDragOnset( event ) + + elif self.curAction == "note-drag-duration": + self.noteDragDuration( event ) + + elif self.curAction == "note-drag-pitch": + self.noteDragPitch( event ) + + elif self.curAction == "marquee": + self.updateMarquee( event ) + + elif self.curAction == "playhead-drag": + self.updatePlayhead( event ) + + TP.ProfileEnd( "TI::handleMotion::Drag" ) + else: + TP.ProfileBegin( "TI::handleMotion::Hover" ) + self.updateTooltip( event ) + TP.ProfileEnd( "TI::handleMotion::Hover" ) + + return + + #======================================================= + # Actions + + def setCurrentAction( self, action, obj ): + if self.curAction: + print "BackgroundView - Action already in progress!" + + self.curAction = action + self.curActionObject = obj + + if action == "note-drag-onset": self.updateDragLimits() + elif action == "note-drag-duration": self.updateDragLimits() + elif action == "note-drag-pitch": self.updateDragLimits() + + def doneCurrentAction( self ): + if self.curAction == "note-drag-onset": self.doneNoteDrag() + elif self.curAction == "note-drag-duration": self.doneNoteDrag() + elif self.curAction == "note-drag-pitch": self.doneNoteDrag() + + self.curAction = False + self.curActionObject = False + + def toggleTrack( self, trackN, exclusive ): + if exclusive: + for i in range(Constants.NUMBER_OF_TRACKS): + self.trackSelected[i] = False + self.trackSelected[trackN] = True + self.invalidate_rect( 0, 0, self.width, self.height ) + else: + self.trackSelected[trackN] = not self.trackSelected[trackN] + self.invalidate_rect( 0, self.trackLimits[trackN][0], self.width, self.trackLimits[trackN][1]-self.trackLimits[trackN][0] ) + + def selectionChanged( self ): + if self.curAction == "note-drag-onset": self.updateDragLimits() + elif self.curAction == "note-drag-duration": self.updateDragLimits() + elif self.curAction == "note-drag-pitch": self.updateDragLimits() + + def applyNoteSelection( self, mode, trackN, which ): + if mode == SELECTNOTES.ALL: + track = self.note[self.curPage][trackN] + map( lambda note:note.setSelected( True ), track ) + self.selectedNotes[trackN] = [] + map( lambda note:self.selectedNotes[trackN].append(note), track ) + elif mode == SELECTNOTES.NONE: + if self.note.has_key(self.curPage): + track = self.note[self.curPage][trackN] + map( lambda note:note.setSelected( False ), track ) + self.selectedNotes[trackN] = [] + elif mode == SELECTNOTES.ADD: + for note in which: + if note.setSelected( True ): + self.selectedNotes[trackN].append( note ) + elif mode == SELECTNOTES.REMOVE: + for note in which: + if note.setSelected( False ): + self.selectedNotes[trackN].remove( note ) + elif mode == SELECTNOTES.FLIP: + for note in which: + if note.getSelected(): + note.setSelected( False ) + self.selectedNotes[trackN].remove( note ) + else: + note.setSelected( True ) + self.selectedNotes[trackN].append( note ) + elif mode == SELECTNOTES.EXCLUSIVE: + notes = self.note[self.curPage][trackN] + for n in range(len(notes)): + if notes[n] in which: + if notes[n].setSelected( True ): + self.selectedNotes[trackN].append( notes[n] ) + else: + if notes[n].setSelected( False ): + self.selectedNotes[trackN].remove( notes[n] ) + + def selectNotesByBar( self, trackN, start, stop ): + for i in range(Constants.NUMBER_OF_TRACKS): + if i == trackN: + notes = [] + track = self.note[self.curPage][trackN] + for n in range(len(track)): + if track[n].testOnset( start, stop ): notes.append(track[n]) + if not ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, trackN, notes ) + else: self.applyNoteSelection( SELECTNOTES.ADD, trackN, notes ) + else: + if not ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.NONE, i, [] ) + self.selectionChanged() + + def selectNotesByTrack( self, trackN ): + if ModKeys.ctrlDown: + self.applyNoteSelection( SELECTNOTES.ALL, trackN, [] ) + else: + for i in range(Constants.NUMBER_OF_TRACKS): + if i == trackN: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [] ) + else: self.applyNoteSelection( SELECTNOTES.NONE, i, [] ) + self.selectionChanged() + + def selectNotes( self, noteDic ): + if ModKeys.ctrlDown: + for i in noteDic: + self.applyNoteSelection( SELECTNOTES.FLIP, i, noteDic[i] ) + else: + for i in range(Constants.NUMBER_OF_TRACKS): + if i in noteDic: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, i, noteDic[i] ) + else: self.applyNoteSelection( SELECTNOTES.NONE, i, [] ) + self.selectionChanged() + + def deselectNotes( self, noteDic ): + for i in noteDic: + self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i] ) + self.selectionChanged() + + def clearSelectedNotes( self ): + for i in range(Constants.NUMBER_OF_TRACKS): + self.applyNoteSelection( SELECTNOTES.NONE, i, [] ) + self.selectionChanged() + + def updateDragLimits( self ): + self.dragLimits = [ [-9999,9999], [-9999,9999], [-9999,9999] ] # initialize to big numbers! + maxRightBound = self.beatCount * Constants.TICKS_PER_BEAT + + for i in range(Constants.NUMBER_OF_TRACKS): + if not len(self.selectedNotes[i]): continue # no selected notes here + + track = self.note[self.curPage][i] + leftBound = 0 + skip = True # skip the first note + for n in range(len(track)): + if skip: + skip = False + thisNote = track[n] + continue + nextNote = track[n] + if not thisNote.getSelected(): + leftBound = thisNote.getEndTick() + else: + if not nextNote.getSelected(): + rightBound = min( nextNote.getStartTick(), maxRightBound ) + widthBound = rightBound + else: + rightBound = maxRightBound + widthBound = min( nextNote.getStartTick(), maxRightBound ) + thisNote.updateDragLimits( self.dragLimits, leftBound, rightBound, widthBound ) + thisNote = nextNote + # do the last note + if thisNote.getSelected(): + thisNote.updateDragLimits( self.dragLimits, leftBound, maxRightBound, maxRightBound ) + + def noteDragOnset( self, event ): + do = self.pixelsToTicks( event.x - self.clickLoc[0] ) + do = min( self.dragLimits[0][1], max( self.dragLimits[0][0], do ) ) + dp = 0 + dd = 0 + + for i in range(Constants.NUMBER_OF_TRACKS): + self.onNoteDrag( [ note.noteDrag(self, do, dp, dd) for note in self.selectedNotes[i] ] ) + + def noteDragDuration( self, event ): + do = 0 + dp = 0 + dd = self.pixelsToTicks( event.x - self.clickLoc[0] ) + dd = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dd ) ) + + for i in range(Constants.NUMBER_OF_TRACKS): + self.onNoteDrag( [ note.noteDrag(self, do, dp, dd) for note in self.selectedNotes[i] ] ) + + def noteDragPitch( self, event ): + do = 0 + dp = self.pixelsToPitch( event.y - self.clickLoc[1] ) + dp = min( self.dragLimits[1][1], max( self.dragLimits[1][0], dp ) ) + dd = 0 + + for i in range(Constants.NUMBER_OF_TRACKS): + self.onNoteDrag( [ note.noteDrag(self, do, dp, dd) for note in self.selectedNotes[i] ] ) + + def doneNoteDrag( self ): + for i in range(Constants.NUMBER_OF_TRACKS): + for note in self.selectedNotes[i]: + note.doneNoteDrag( self ) + + def updateMarquee( self, event ): + if self.marqueeLoc: + oldX = self.marqueeRect[0][0] + oldEndX = self.marqueeRect[0][0] + self.marqueeRect[1][0] + oldY = self.marqueeRect[0][1] + oldEndY = self.marqueeRect[0][1] + self.marqueeRect[1][1] + else: + oldX = oldEndX = self.clickLoc[0] + oldY = oldEndY = self.clickLoc[1] + + self.marqueeLoc = [ event.x, event.y ] + if self.marqueeLoc[0] < 0: self.marqueeLoc[0] = 0 + elif self.marqueeLoc[0] > self.width: self.marqueeLoc[0] = self.width + if self.marqueeLoc[1] < 0: self.marqueeLoc[1] = 0 + elif self.marqueeLoc[1] > self.height: self.marqueeLoc[1] = self.height + + if self.marqueeLoc[0] > self.clickLoc[0]: + self.marqueeRect[0][0] = self.clickLoc[0] + self.marqueeRect[1][0] = self.marqueeLoc[0] - self.clickLoc[0] + else: + self.marqueeRect[0][0] = self.marqueeLoc[0] + self.marqueeRect[1][0] = self.clickLoc[0] - self.marqueeLoc[0] + if self.marqueeLoc[1] > self.clickLoc[1]: + self.marqueeRect[0][1] = self.clickLoc[1] + self.marqueeRect[1][1] = self.marqueeLoc[1] - self.clickLoc[1] + else: + self.marqueeRect[0][1] = self.marqueeLoc[1] + self.marqueeRect[1][1] = self.clickLoc[1] - self.marqueeLoc[1] + + x = min( self.marqueeRect[0][0], oldX ) + width = max( self.marqueeRect[0][0] + self.marqueeRect[1][0], oldEndX ) - x + y = min( self.marqueeRect[0][1], oldY ) + height = max( self.marqueeRect[0][1] + self.marqueeRect[1][1], oldEndY ) - y + self.invalidate_rect( x-1, y-1, width+2, height+2 ) # increase by 1 to handle switching quadrants + + def doneMarquee( self, event ): + if self.marqueeLoc: + stop = [ self.marqueeRect[0][0] + self.marqueeRect[1][0], self.marqueeRect[0][1] + self.marqueeRect[1][1] ] + + select = {} + + for i in range(Constants.NUMBER_OF_TRACKS): + intersectionY = [ max(self.marqueeRect[0][1],self.trackLimits[i][0]), min(stop[1],self.trackLimits[i][1]) ] + if intersectionY[0] > intersectionY[1]: + continue + + notes = [] + track = self.note[self.curPage][i] + for n in range(len(track)): + hit = track[n].handleMarqueeSelect( self, + [ self.marqueeRect[0][0], intersectionY[0] ], \ + [ stop[0], intersectionY[1] ] ) + if hit: notes.append(track[n]) + + if len(notes): select[i] = notes + + self.selectNotes( select ) + + self.marqueeLoc = False + self.doneCurrentAction() + + self.invalidate_rect( self.marqueeRect[0][0]-1, self.marqueeRect[0][1]-1, self.marqueeRect[1][0]+2, self.marqueeRect[1][1]+2 ) + + def updatePlayhead( self, event ): + x = min( self.width - GUIConstants.BORDER_SIZE_MUL2 - self.pixelsPerTick, max( GUIConstants.BORDER_SIZE, event.x ) ) + self.setPlayhead( self.pixelsToTicks( x ) ) + + + def donePlayhead( self, event ): + x = min( self.width - GUIConstants.BORDER_SIZE_MUL2, max( GUIConstants.BORDER_SIZE, event.x ) ) + ticks = self.pixelsToTicks( x ) + print "set playhead to %d ticks" % (ticks) + + def updateTooltip( self, event ): + + # check clicked the playhead + if event.x >= self.playheadX and event.x <= self.playheadX + GUIConstants.PLAYHEAD_SIZE: + self.setCursor("drag-playhead") + return + + for i in range(Constants.NUMBER_OF_TRACKS): + if self.trackLimits[i][0] > event.y: break + if self.trackLimits[i][1] < event.y: continue + + notes = self.note[self.curPage][i] + handled = 0 + for n in range(len(notes)): + handled = notes[n].updateTooltip( self, event ) + if handled == 0: continue + elif handled == 1: return # event was handled + else: break + + # note wasn't handled, could potentially draw a note + if self.interfaceMode == INTERFACEMODE.DRAW: + if handled == -2: # event X overlapped with a note + self.setCursor("default") + return + + self.setCursor("pencil") + return + + break + + self.setCursor("default") + + def setCursor( self, cursor ): + self.window.set_cursor(self.cursor[cursor]) + + #======================================================= + # Drawing + + def draw( self, drawingArea, event ): + TP.ProfileBegin( "TrackInterface::draw" ) + + startX = event.area.x + startY = event.area.y + stopX = event.area.x + event.area.width + stopY = event.area.y + event.area.height + + context = drawingArea.window.cairo_create() + context.set_antialias(0) # I don't know what to set this to to turn it off, and it doesn't seem to work anyway!? + + for i in range( Constants.NUMBER_OF_TRACKS): + if startY > self.trackLimits[i][1]: continue + if stopY < self.trackLimits[i][0]: break + + if False: + context.set_line_width( GUIConstants.BORDER_SIZE ) + + context.move_to( GUIConstants.BORDER_SIZE_DIV2, self.trackLimits[i][0] + GUIConstants.BORDER_SIZE_DIV2 ) + context.rel_line_to( self.width - GUIConstants.BORDER_SIZE, 0 ) + context.rel_line_to( 0, self.trackHeight - GUIConstants.BORDER_SIZE ) + context.rel_line_to( -self.width + GUIConstants.BORDER_SIZE, 0 ) + context.close_path() + + #draw background + context.set_source_rgb( 0.75, 0.75, 0.75 ) + context.fill_preserve() + + else: + context.rectangle(GUIConstants.BORDER_SIZE_DIV2, self.trackLimits[i][0] + GUIConstants.BORDER_SIZE_DIV2 , self.width, self.height) + context.set_source_rgb( 0.75, 0.75, 0.75 ) + context.fill() + + # draw border + if self.trackSelected[i]: context.set_source_rgb( 1, 1, 1 ) + else: context.set_source_rgb( 0, 0, 0 ) + context.stroke() + + # draw beat lines + context.set_line_width( GUIConstants.BEAT_LINE_SIZE ) + beatStart = GUIConstants.BORDER_SIZE + GUIConstants.BEAT_LINE_SIZE_DIV2 + context.set_source_rgb( 0.4, 0.4, 0.4 ) + for j in range(1,self.beatCount): + context.move_to( beatStart + j*self.beatSpacing, self.trackLimits[i][0] + GUIConstants.BORDER_SIZE ) + context.rel_line_to( 0, self.trackHeight - GUIConstants.BORDER_SIZE_MUL2 ) + context.stroke() + + # draw notes + notes = self.note[self.curPage][i] + for n in range(len(notes)): + if not notes[n].draw( context, startX, stopX ): break + + # draw playhead + context.set_line_width( GUIConstants.PLAYHEAD_SIZE ) + context.move_to( self.playheadX + GUIConstants.PLAYHEAD_SIZE_DIV2, 0 ) + # do some fancy shit here to grey out muted tracks!? + context.rel_line_to( 0, self.height ) + context.set_source_rgb( 0, 0, 0 ) + context.stroke() + + if self.marqueeLoc: # draw the selection rect + context.set_line_width( 1 ) + context.move_to( self.marqueeRect[0][0] + 0.5, self.marqueeRect[0][1] + 0.5 ) + context.rel_line_to( self.marqueeRect[1][0] - 1, 0 ) + context.rel_line_to( 0, self.marqueeRect[1][1] - 1 ) + context.rel_line_to( -self.marqueeRect[1][0] + 1, 0 ) + context.close_path() + context.set_source_rgb( 1, 1, 1 ) + context.stroke() + + self.drawingAreaDirty = False + + TP.ProfileEnd( "TrackInterface::draw" ) + + def invalidate_rect( self, x, y, width, height ): + self.dirtyRectToAdd.x = x + self.dirtyRectToAdd.y = y + self.dirtyRectToAdd.width = width + self.dirtyRectToAdd.height = height + self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True ) + self.drawingAreaDirty = True + #self.queue_draw() + + def getTrackOrigin( self, track ): + return self.trackOrigin[track] + + def ticksToPixels( self, ticks ): + return int(round( ticks * self.pixelsPerTick )) + def pixelsToTicks( self, pixels ): + return int(round( pixels * self.ticksPerPixel )) + def pitchToPixels( self, pitch ): + return int(round( ( Constants.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch )) + def pixelsToPitch( self, pixels ): + return int(round(-pixels*self.pitchPerPixel)) |