Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Edit/TrackInterface.py
diff options
context:
space:
mode:
authorjaberg <james@localhost.localdomain>2007-01-07 21:24:04 (GMT)
committer jaberg <james@localhost.localdomain>2007-01-07 21:24:04 (GMT)
commit1becac24dfa6bd0cee4ae46f4c0b8630bfbb6269 (patch)
tree70d602342d546fd8c268143b54d46b8ca580a7ed /Edit/TrackInterface.py
parent2a940a71469b2b5964e8deab3ed6db0254424671 (diff)
unify_edit init
Diffstat (limited to 'Edit/TrackInterface.py')
-rw-r--r--Edit/TrackInterface.py750
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))