Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/TamTamEdit.activity/Edit/TrackInterface.py
diff options
context:
space:
mode:
authorNat <natcl@hotmail.com>2007-09-13 15:55:52 (GMT)
committer Nat <natcl@hotmail.com>2007-09-13 15:55:52 (GMT)
commite12dbff4dda5aafbaac98f75f0467ef00dc06c32 (patch)
tree52f74f5a699ca1a2827b333e76a7225b7d768256 /TamTamEdit.activity/Edit/TrackInterface.py
parentb94ccdfd2329ed2d1128a4392e2f67b1e6b704da (diff)
Activity split
Diffstat (limited to 'TamTamEdit.activity/Edit/TrackInterface.py')
-rw-r--r--TamTamEdit.activity/Edit/TrackInterface.py1364
1 files changed, 1364 insertions, 0 deletions
diff --git a/TamTamEdit.activity/Edit/TrackInterface.py b/TamTamEdit.activity/Edit/TrackInterface.py
new file mode 100644
index 0000000..b8a3a27
--- /dev/null
+++ b/TamTamEdit.activity/Edit/TrackInterface.py
@@ -0,0 +1,1364 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import gobject
+
+from math import floor
+import time
+
+import Config
+from Edit.NoteInterface import NoteInterface
+from Edit.HitInterface import HitInterface
+from Edit.MainWindow import CONTEXT
+
+from Util.NoteDB import PARAMETER
+from Util.CSoundNote import CSoundNote
+from Generation.GenerationConstants import GenerationConstants
+from Util.Profiler import TP
+
+class SELECTNOTES:
+ ALL = -1
+ NONE = 0
+ ADD = 1
+ REMOVE = 2
+ FLIP = 3
+ EXCLUSIVE = 4
+
+class INTERFACEMODE:
+ DEFAULT = 0
+ DRAW = 1
+ PASTE_NOTES = 2
+ PASTE_TRACKS = 3
+ PAINT = 4
+
+class TrackInterfaceParasite:
+ def __init__( self, noteDB, owner, note ):
+ if note.track == Config.NUMBER_OF_TRACKS-1: # drum track
+ self.parasite = HitInterface( noteDB, owner, note )
+ else:
+ self.parasite = NoteInterface( noteDB, owner, note )
+
+ def attach( self ):
+ return self.parasite
+
+class TrackInterface( gtk.EventBox ):
+
+ def __init__( self, noteDB, owner, getScaleFunction ):
+ gtk.EventBox.__init__( self )
+
+ self.noteDB = noteDB
+ self.owner = owner
+ self.getScale = getScaleFunction
+
+ self.drawingArea = gtk.DrawingArea()
+ self.drawingAreaDirty = False # are we 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.DEFAULT
+
+ self.curPage = -1 # this isn't a real page at all!
+ self.curBeats = 4
+ self.painting = False
+ self.pointerGrid = 1
+ self.drawGrid = Config.DEFAULT_GRID
+ self.paintGrid = Config.DEFAULT_GRID
+ self.paintNoteDur = Config.DEFAULT_GRID
+
+ self.selectedNotes = [ [] for i in range(Config.NUMBER_OF_TRACKS) ]
+
+ self.curAction = False # stores the current mouse action
+ self.curActionObject = False # stores the object that in handling the action
+
+ self.lastDO = self.lastDP = self.lastDrumDP = self.lastDD = None
+
+ self.clickButton = 0 # used in release and motion events to make sure we where actually the widget originally clicked. (hack for popup windows)
+ 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.pasteTick = -1
+ self.pasteTrack = -1
+ self.pasteRect = False
+
+ self.playheadT = 0
+ self.playheadX = Config.TRACK_SPACING_DIV2
+
+ self.cursor = { \
+ "default": None, \
+ "drag-onset": gtk.gdk.Cursor(gtk.gdk.SB_RIGHT_ARROW), \
+ "drag-pitch": gtk.gdk.Cursor(gtk.gdk.BOTTOM_SIDE), \
+ "drag-duration": gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE), \
+ "drag-playhead": gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW), \
+ "pencil": gtk.gdk.Cursor(gtk.gdk.PENCIL), \
+ "paste": gtk.gdk.Cursor(gtk.gdk.CENTER_PTR), \
+ "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.expose )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "button-release-event", self.handleButtonRelease )
+ self.connect( "motion-notify-event", self.handleMotion )
+
+ # prepare drawing stuff
+ hexToInt = { "0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "A":10, "B":11, "C":12, "D":13, "E":14, "F":15, "a":10, "b":11, "c":12, "d":13, "e":14, "f":15 }
+ self.trackColors = []
+ for i in Config.TRACK_COLORS:
+ low = ( 256*(hexToInt[i[0][1]]*16+hexToInt[i[0][2]]), 256*(hexToInt[i[0][3]]*16+hexToInt[i[0][4]]), 256*(hexToInt[i[0][5]]*16+hexToInt[i[0][6]]) )
+ high = ( 256*(hexToInt[i[1][1]]*16+hexToInt[i[1][2]]), 256*(hexToInt[i[1][3]]*16+hexToInt[i[1][4]]), 256*(hexToInt[i[1][5]]*16+hexToInt[i[1][6]]) )
+ delta = ( high[0]-low[0], high[1]-low[1], high[2]-low[2] )
+ self.trackColors.append( (low, delta) )
+
+ colormap = self.drawingArea.get_colormap()
+ self.beatColor = colormap.alloc_color( Config.BEAT_COLOR, True, True )
+ self.playheadColor = colormap.alloc_color( Config.PLAYHEAD_COLOR, True, True )
+ self.marqueeColor = colormap.alloc_color( Config.MARQUEE_COLOR, True, True )
+
+ self.image = {}
+ win = gtk.gdk.get_default_root_window()
+ self.gc = gtk.gdk.GC( win )
+
+ def prepareDrawable( name ):
+ pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+name+".png" )
+ self.image[name] = gtk.gdk.Pixmap( win, pix.get_width(), pix.get_height() )
+ self.image[name].draw_pixbuf( self.gc, pix, 0, 0, 0, 0, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
+ def preparePixbuf( name ):
+ self.image[name] = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+name+".png" )
+
+ prepareDrawable( "trackBG" )
+ prepareDrawable( "trackBGSelected" )
+ prepareDrawable( "trackBGDrum" )
+ prepareDrawable( "trackBGDrumSelected" )
+ preparePixbuf( "note" )
+ preparePixbuf( "noteSelected" )
+ preparePixbuf( "hit" )
+ preparePixbuf( "hitSelected" )
+
+ # define dimensions
+ self.width = self.trackFullWidth = self.image["trackBG"].get_size()[0]
+ self.trackWidth = self.width - Config.TRACK_SPACING
+ self.trackFullHeight = self.image["trackBG"].get_size()[1]
+ self.trackHeight = self.trackFullHeight - Config.TRACK_SPACING
+ self.trackFullHeightDrum = self.image["trackBGDrum"].get_size()[1]
+ self.trackHeightDrum = self.trackFullHeightDrum - Config.TRACK_SPACING
+ self.height = self.trackHeight*(Config.NUMBER_OF_TRACKS-1) + self.trackHeightDrum + Config.TRACK_SPACING*Config.NUMBER_OF_TRACKS
+ self.trackLimits = []
+ self.trackRect = []
+ self.drumIndex = Config.NUMBER_OF_TRACKS-1
+ for i in range(self.drumIndex):
+ start = i*(self.trackFullHeight)
+ self.trackLimits.append( (start,start+self.trackFullHeight) )
+ self.trackRect.append( gtk.gdk.Rectangle(Config.TRACK_SPACING_DIV2,start+Config.TRACK_SPACING_DIV2, self.trackWidth, self.trackHeight ) )
+ self.trackLimits.append( ( self.height - self.trackFullHeightDrum, self.height ) )
+ self.trackRect.append( gtk.gdk.Rectangle( Config.TRACK_SPACING_DIV2, self.height - self.trackFullHeightDrum + Config.TRACK_SPACING_DIV2, self.trackWidth, self.trackHeightDrum ) )
+
+ self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.trackHeight - Config.NOTE_HEIGHT)
+ self.pixelsPerPitch = float(self.trackHeight-Config.NOTE_HEIGHT)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ self.pitchPerPixelDrum = float(Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM / (self.trackHeightDrum - Config.HIT_HEIGHT)
+ self.pixelsPerPitchDrum = float(self.trackHeightDrum-Config.HIT_HEIGHT)/(Config.MAXIMUM_PITCH_DRUM - Config.MINIMUM_PITCH_DRUM )
+
+ self.pixelsPerTick = [0] + [ self.trackWidth/float(i*Config.TICKS_PER_BEAT) for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.ticksPerPixel = [0] + [ 1.0/self.pixelsPerTick[i] for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.beatSpacing = [[0]]
+ for i in range(1,Config.MAXIMUM_BEATS+1):
+ self.beatSpacing.append( [ self.ticksToPixels( i, Config.TICKS_PER_BEAT*j ) for j in range(i) ] )
+
+ # screen buffers
+ self.screenBuf = [ gtk.gdk.Pixmap( win, self.width, self.height ), \
+ gtk.gdk.Pixmap( win, self.width, self.height ) ]
+ self.screenBufPage = [ -1, -1 ]
+ self.screenBufBeats = [ -1, -1 ]
+ self.screenBufDirtyRect = [ gtk.gdk.Rectangle(), gtk.gdk.Rectangle() ]
+ self.screenBufDirty = [ False, False ]
+ self.screenBufResume = [ [0,0], [0,0] ] # allows for stopping and restarting in the middle of a draw
+ self.curScreen = 0
+ self.preScreen = 1
+ self.predrawTimeout = False
+
+ #-- private --------------------------------------------
+
+ def _updateClipboardArea( self ):
+ self.clipboardArea = self.owner.getClipboardArea( self.curPage )
+ self.clipboardTrackTop = 0
+ for t in range(self.drumIndex):
+ if self.clipboardArea["tracks"][t]: break
+ self.clipboardTrackTop += 1
+ self.clipboardDrumTrack = self.clipboardArea["tracks"][self.drumIndex]
+
+ #=======================================================
+ # NoteDB notifications
+
+ def notifyPageAdd( self, id, at ):
+ return
+
+ def notifyPageDelete( self, which, safe ):
+ if self.screenBufPage[self.preScreen] in which:
+ self.screenBufPage[self.preScreen] = -1
+
+ def notifyPageDuplicate( self, new, at ):
+ return
+
+ def notifyPageMove( self, which, low, high ):
+ return
+
+ def notifyPageUpdate( self, page, parameter, value ):
+ if parameter == PARAMETER.PAGE_BEATS:
+ notes = self.noteDB.getNotesByPage( page, self )
+ for note in notes:
+ note.updateTransform()
+
+ if page == self.screenBufPage[self.curScreen]:
+ self.screenBufBeats[self.curScreen] = value
+ self.curBeats = value
+ if self.playheadT >= value*Config.TICKS_PER_BEAT:
+ self.playheadT = value*Config.TICKS_PER_BEAT - 1
+ self.playheadX = self.ticksToPixels( self.curBeats, self.playheadT ) + Config.TRACK_SPACING_DIV2
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ if page == self.screenBufPage[self.preScreen]:
+ self.screenBufBeats[self.preScreen] = value
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ self.predrawPage()
+
+
+ #=======================================================
+ # Module Interface
+
+ def getDrawingPackage( self, track ):
+ if track == self.drumIndex:
+ return ( self.image["hit"], self.image["hitSelected"], self.drawingArea.get_colormap(), self.trackColors[track] )
+ else:
+ return ( self.image["note"], self.image["noteSelected"], self.drawingArea.get_colormap(), self.trackColors[track] )
+
+ def getActivePages( self ):
+ return self.screenBufPage
+
+ def setPredrawPage( self, page ):
+ if self.screenBufPage[self.preScreen] != page:
+ self.screenBufPage[self.preScreen] = page
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(page).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ return True
+ return False
+
+ def predrawPage( self ):
+ if self.screenBufPage[self.preScreen] == -1: return True # no page to predraw
+ if not self.predrawTimeout:
+ self.predrawTimeout = gobject.timeout_add( 50, self._predrawTimeout )
+
+ def abortPredrawPage( self ):
+ if self.predrawTimeout:
+ gobject.source_remove( self.predrawTimeout )
+ self.predrawTimeout = False
+
+ def _predrawTimeout( self ):
+ if self.preScreen == -1: return False # no page to predraw
+ if self.draw( self.preScreen, False, time.time() + 0.020 ): # 20 ms time limit
+ self.predrawTimeout = False
+ return False
+ return True
+
+
+
+ def displayPage( self, page, predraw = -1 ):
+ if page == self.curPage:
+ if predraw >= 0 and self.screenBufPage[self.preScreen] != predraw:
+ self.screenBufPage[self.preScreen] = predraw
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, predraw )
+ return
+
+ if self.curPage >= 0 and self.curPage != page: clearNotes = True
+ else: clearNotes = False
+
+ oldPage = self.curPage
+ self.curPage = page
+ self.curBeats = self.noteDB.getPage(page).beats
+
+ if self.screenBufPage[self.preScreen] == self.curPage: # we predrew this page, so smart!
+ t = self.preScreen
+ self.preScreen = self.curScreen
+ self.curScreen = t
+ self.invalidate_rect( 0, 0, self.width, self.height, self.curPage, False )
+ else: # we need to draw this page from scratch
+ self.screenBufPage[self.curScreen] = self.curPage
+ self.screenBufBeats[self.curScreen] = self.curBeats
+ self.invalidate_rect( 0, 0, self.width, self.height, self.curPage )
+
+ if predraw >= 0 and self.screenBufPage[self.preScreen] != predraw:
+ self.screenBufPage[self.preScreen] = predraw
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, predraw )
+
+ if clearNotes: # clear the notes now that we've sorted out the screen buffers
+ self.clearSelectedNotes( oldPage )
+
+ if self.curAction == "paste":
+ self._updateClipboardArea()
+
+ def getPlayhead( self ):
+ return self.playheadT
+
+ def setPlayhead( self, ticks ):
+ if self.playheadT != ticks:
+ self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False )
+ self.playheadX = self.ticksToPixels( self.curBeats, ticks ) + Config.TRACK_SPACING_DIV2
+ self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False )
+ self.playheadT = ticks
+
+ def setInterfaceMode( self, mode ):
+ self.doneCurrentAction()
+
+ if mode == "tool":
+ mode = self.owner.getTool()
+
+ if mode == "draw":
+ self.interfaceMode = INTERFACEMODE.DRAW
+ elif mode == "paint":
+ self.interfaceMode = INTERFACEMODE.PAINT
+ elif mode == "paste_notes":
+ self.interfaceMode = INTERFACEMODE.PASTE_NOTES
+ self.setCurrentAction("paste", self)
+ elif mode == "paste_tracks":
+ self.interfaceMode = INTERFACEMODE.PASTE_TRACKS
+ self.setCurrentAction("paste", self )
+ else:
+ self.interfaceMode = INTERFACEMODE.DEFAULT
+
+ def getSelectedNotes( self ):
+ ids = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ ids.append( [ n.note.id for n in self.selectedNotes[t] ] )
+ return ids
+
+ #=======================================================
+ # Event Callbacks
+
+ def size_allocate( self, widget, allocation ):
+ self.alloc = allocation
+ width = allocation.width
+ height = allocation.height
+
+ self.drawingArea.set_size_request( width, height )
+
+ if self.window != None:
+ self.invalidate_rect( 0, 0, width, height, self.curPage, False )
+
+ def setPointerGrid(self, value):
+ self.pointerGrid = value
+
+ def setDrawGrid(self, value):
+ self.drawGrid = value
+
+ def setPaintGrid(self, value):
+ self.paintGrid = value
+
+ def setPaintNoteDur(self, value):
+ self.paintNoteDur = value
+
+ def handleButtonPress( self, widget, event ):
+
+ TP.ProfileBegin( "TI::handleButtonPress" )
+
+ self.clickButton = event.button
+
+ 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 = [ int(event.x), int(event.y) ]
+
+
+ if self.curAction == "paste":
+ self.doPaste()
+ self.setCurrentAction("block-track-select")
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+
+
+ # check if we clicked on the playhead
+ if event.x >= self.playheadX and event.x <= self.playheadX + Config.PLAYHEAD_SIZE:
+ self.setCurrentAction( "playhead-drag", self )
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+
+ handled = 0
+ notes = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ last = len(notes)-1
+ for n in range(last+1):
+ if i == self.drumIndex and n < last: # check to see if the next hit overlaps this one
+ if notes[n].getStartTick() == notes[n+1].getStartTick() and notes[n].getPitch() == notes[n+1].getPitch():
+ continue
+ 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 not handled or handled == -1: # event didn't overlap any notes, so we can draw
+ if i == self.drumIndex: pitch = min( self.pixelsToPitchDrumFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ else: pitch = min( self.pixelsToPitchFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ onset = self.pixelsToTicksFloor( self.curBeats, self.clickLoc[0] - self.trackRect[i].x)
+ snapOnset = self.drawGrid * int(onset / float(self.drawGrid) + 0.5)
+ cs = CSoundNote( snapOnset,
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ for note in noteS:
+ if note.cs.onset < snapOnset and (note.cs.onset + note.cs.duration) > snapOnset:
+ self.noteDB.updateNote(self.curPage, i, note.id, PARAMETER.DURATION, snapOnset - note.cs.onset)
+
+ if i != self.drumIndex: # switch to drag duration
+ self.updateDragLimits()
+ self.clickLoc[0] += self.ticksToPixels( self.curBeats, 1 )
+ self.setCurrentAction( "note-drag-duration", n )
+ self.setCursor("drag-duration")
+ else:
+ self.curAction = True # we handled this, but there's no real action
+
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+ elif self.interfaceMode == INTERFACEMODE.PAINT:
+ self.scale = self.getScale()
+ self.painting = True
+ self.paintTrack = i
+ if i == self.drumIndex:
+ pitch = min( self.pixelsToPitchDrumFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ else:
+ pitch = min( self.pixelsToPitchFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+
+ minDiff = 100
+ for pit in GenerationConstants.SCALES[self.scale]:
+ diff = abs(pitch-(pit+36))
+ if diff < minDiff:
+ minDiff = diff
+ nearestPit = pit
+ pitch = nearestPit+36
+
+ onset = self.pixelsToTicksFloor( self.curBeats, self.clickLoc[0] - self.trackRect[i].x )
+ onset = self.paintGrid * int(onset / self.paintGrid + 0.5)
+ self.pLastPos = onset
+ if i != self.drumIndex:
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ ids = []
+ stream = []
+ for n in noteS:
+ if n.cs.onset >= onset and n.cs.onset < (onset + self.paintNoteDur):
+ ids.append(n.id)
+ if onset > n.cs.onset and onset < (n.cs.onset + n.cs.duration):
+ ids.append(n.id)
+ if len(ids):
+ stream += [self.curPage, i, len(ids)] + ids
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ cs = CSoundNote( int(onset),
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ self.noteDB.updateNote(self.curPage, i, id, PARAMETER.DURATION, self.paintNoteDur)
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+ self.curAction = True
+
+ TP.ProfileEnd( "TI::handleButtonPress" )
+
+
+ def handleButtonRelease( self, widget, event ):
+ if not self.clickButton: return # we recieved this event but were never clicked! (probably a popup window was open)
+ self.clickButton = 0
+ self.painting = False
+
+ TP.ProfileBegin( "TI::handleButtonRelease" )
+
+ if event.button != 1:
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ return
+
+ if not self.curAction: #do track selection stuff here so that we can also handle marquee selection
+ for i in range(Config.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.owner.toggleTrack( i, False )
+ elif self.buttonPressCount == 2: self.owner.toggleTrack( i, True )
+ else: self.owner.clearTracks()
+ 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
+
+ if self.painting:
+ i = self.paintTrack
+ curPos = self.pixelsToTicksFloor(self.curBeats, event.x - self.trackRect[i].x)
+ gridPos = self.paintGrid * int(curPos / self.paintGrid + 0.5)
+ if gridPos >= self.curBeats * Config.TICKS_PER_BEAT:
+ return
+ if gridPos != self.pLastPos:
+ self.pLastPos = gridPos
+ if i == self.drumIndex:
+ pitch = min( self.pixelsToPitchDrumFloor( int(event.y) - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ else:
+ pitch = min( self.pixelsToPitchFloor( int(event.y) - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ minDiff = 100
+ for pit in GenerationConstants.SCALES[self.scale]:
+ diff = abs(pitch-(pit+36))
+ if diff < minDiff:
+ minDiff = diff
+ nearestPit = pit
+ pitch = nearestPit+36
+
+ onset = gridPos
+ if i != self.drumIndex:
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ ids = []
+ stream = []
+ for n in noteS:
+ if n.cs.onset >= onset and n.cs.onset < (onset + self.paintNoteDur):
+ ids.append(n.id)
+ if onset > n.cs.onset and onset < (n.cs.onset + n.cs.duration):
+ ids.append(n.id)
+ if len(ids):
+ stream += [self.curPage, i, len(ids)] + ids
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ cs = CSoundNote( int(onset),
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ self.noteDB.updateNote(self.curPage, i, id, PARAMETER.DURATION, self.paintNoteDur)
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+ self.curAction = True
+
+
+ TP.ProfileEnd( "TI::handleMotion::Common" )
+
+ if not self.clickButton and self.curAction != "paste": # we recieved this event but were never clicked! (probably a popup window was open)
+ TP.ProfileBegin( "TI::handleMotion::Hover" )
+ self.updateTooltip( event )
+ TP.ProfileEnd( "TI::handleMotion::Hover" )
+ return
+
+ if self.curAction == "paste":
+ TP.ProfileBegin( "TI::handleMotion::Paste" )
+ top = Config.NUMBER_OF_TRACKS
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+ top = i
+ break
+ self.updatePaste( self.pixelsToTicksFloor( self.curBeats, event.x ), top )
+ TP.ProfileEnd( "TI::handleMotion::Paste" )
+ elif 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 == "note-drag-pitch-drum":
+ self.noteDragPitch( event, True )
+
+ 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 = None ):
+ if self.curAction:
+ self.doneCurrentAction()
+
+ 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()
+ elif action == "note-drag-pitch-drum": self.updateDragLimits()
+ elif action == "paste":
+ self._updateClipboardArea()
+ self.setCursor("paste")
+
+ def doneCurrentAction( self ):
+ if not self.curAction: return
+ action = self.curAction
+ self.curAction = False
+
+ if action == "note-drag-onset": self.doneNoteDrag( action )
+ elif action == "note-drag-duration": self.doneNoteDrag( action )
+ elif action == "note-drag-pitch": self.doneNoteDrag( action )
+ elif action == "note-drag-pitch-drum": self.doneNoteDrag( action )
+ elif action == "paste":
+ self.owner.cleanupClipboard()
+
+ def trackToggled( self, trackN = -1 ):
+ if trackN == -1: self.invalidate_rect( 0, 0, self.width, self.height )
+ else: 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()
+ elif self.curAction == "note-drag-pitch-drum": self.updateDragLimits()
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if len(self.selectedNotes[i]):
+ self.owner.setContextState( CONTEXT.NOTE, True )
+ self.owner.setContext( CONTEXT.NOTE )
+ return
+ self.owner.setContextState( CONTEXT.NOTE, False )
+
+ def applyNoteSelection( self, mode, trackN, which, page = -1 ):
+ if page == -1: page = self.curPage
+ if mode == SELECTNOTES.ALL:
+ track = self.noteDB.getNotesByTrack( page, trackN, self )
+ map( lambda note:note.setSelected( True ), track )
+ self.selectedNotes[trackN] = []
+ map( lambda note:self.selectedNotes[trackN].append(note), track )
+ elif mode == SELECTNOTES.NONE:
+ track = self.selectedNotes[trackN] #self.noteDB.getNotesByTrack( page, trackN, self )
+ 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.noteDB.getNotesByTrack( page, trackN, self )
+ 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, page = -1 ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == trackN:
+ notes = []
+ track = self.noteDB.getNotesByTrack( self.curPage, trackN, self )
+ for n in range(len(track)):
+ if track[n].testOnset( start, stop ): notes.append(track[n])
+ if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, trackN, notes, page )
+ else: self.applyNoteSelection( SELECTNOTES.ADD, trackN, notes, page )
+ else:
+ if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def selectNotesByTrack( self, trackN, page = -1 ):
+ if Config.ModKeys.ctrlDown:
+ self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page )
+ else:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == trackN: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page )
+ else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def selectNotes( self, noteDic, ignoreCtrl = False, page = -1 ):
+ if Config.ModKeys.ctrlDown and not ignoreCtrl:
+ for i in noteDic:
+ self.applyNoteSelection( SELECTNOTES.FLIP, i, noteDic[i], page )
+ else:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i in noteDic: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, i, noteDic[i], page )
+ else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def deselectNotes( self, noteDic, page = -1 ):
+ for i in noteDic:
+ self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i], page )
+ self.selectionChanged()
+
+ def clearSelectedNotes( self, page = -1 ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def updateDragLimits( self ):
+ self.dragLimits = [ [-9999,9999], [-9999,9999], [-9999,9999] ] # initialize to big numbers!
+ maxRightBound = self.noteDB.getPage(self.curPage).ticks
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ 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, maxRightBound )
+ thisNote = nextNote
+ # do the last note
+ if thisNote.getSelected():
+ thisNote.updateDragLimits( self.dragLimits, leftBound, maxRightBound, maxRightBound, maxRightBound )
+
+ def noteDragOnset( self, event ):
+ do = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] )
+ do = min( self.dragLimits[0][1], max( self.dragLimits[0][0], do ) )
+ do = self.pointerGrid * int(do / self.pointerGrid)
+
+ if do != self.lastDO:
+ self.lastDO = do
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragOnset( do, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.ONSET, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteDragDuration( self, event ):
+ dd = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] )
+ dd = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dd ) )
+
+ if dd != self.lastDD:
+ self.lastDD = dd
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragDuration( dd, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.DURATION, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteDragPitch( self, event, drum = False ):
+ if not drum: dp = self.pixelsToPitch( event.y - self.clickLoc[1] )
+ else: dp = self.pixelsToPitchDrum( event.y - self.clickLoc[1] )
+ dp = min( self.dragLimits[1][1], max( self.dragLimits[1][0], dp ) )
+
+ if dp != self.lastDP:
+ self.lastDP = dp
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragPitch( dp, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.PITCH, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ if self.curActionObject.note.track != self.drumIndex:
+ self.curActionObject.playSampleNote( True )
+ elif dp != self.lastDrumDP and not dp%2: # only play of "full" drum pitches
+ self.lastDrumDP = dp
+ self.curActionObject.playSampleNote( False )
+
+ def doneNoteDrag( self, action ):
+ # if action == "note-drag-pitch" or action == "note-drag-pitch-drum":
+ # self.curActionObject.playSampleNote()
+
+ self.lastDO = self.lastDP = self.lastDrumDP = self.lastDD = None
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ for note in self.selectedNotes[i]:
+ note.doneNoteDrag( self )
+
+ def noteStepOnset( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ if step < 0: # moving to the left, iterate forwards
+ leftBound = 0
+ for n in range(len(track)):
+ leftBound = track[n].noteDecOnset( step, leftBound, tstream )
+ else: # moving to the right, iterate backwards
+ rightBound = self.noteDB.getPage(self.curPage).ticks
+ for n in range(len(track)-1, -1, -1 ):
+ rightBound = track[n].noteIncOnset( step, rightBound, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.ONSET, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepPitch( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecPitch( step, tstream )
+ else:
+ for n in self.selectedNotes[i]:
+ n.noteIncPitch( step, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.PITCH, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepDuration( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecDuration( step, tstream )
+ else:
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ for j in range(len(track)-1):
+ track[j].noteIncDuration( step, track[j+1].getStartTick(), tstream )
+ track[len(track)-1].noteIncDuration( step, self.noteDB.getPage(self.curPage).ticks, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.DURATION, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepVolume( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecVolume( step, tstream )
+ else:
+ for n in self.selectedNotes[i]:
+ n.noteIncVolume( step, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.AMPLITUDE, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+
+ 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 = [ int(event.x), int(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, self.curPage, False )
+
+ 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(Config.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.noteDB.getNotesByTrack( self.curPage, i, self )
+ 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, self.curPage, False )
+
+ def updatePlayhead( self, event ):
+ x = min( self.trackWidth - self.pixelsPerTick[self.curBeats], max( Config.TRACK_SPACING_DIV2, event.x ) )
+ self.setPlayhead( self.pixelsToTicks( self.curBeats, x ) )
+
+ def donePlayhead( self, event ):
+ x = min( self.trackWidth - self.pixelsPerTick[self.curBeats], max( Config.TRACK_SPACING_DIV2, event.x ) )
+ ticks = self.pixelsToTicks( self.curBeats, x )
+ print "set playhead to %d ticks" % (ticks)
+ self.doneCurrentAction()
+
+ def updatePaste( self, tick, track ):
+ if self.interfaceMode == INTERFACEMODE.PASTE_TRACKS: tick = 0
+ if self.pasteTick == tick and self.pasteTrack == track: return
+ if self.noteDB.getPage(self.curPage).ticks < tick < 0 \
+ or track > self.drumIndex \
+ or ( track == self.drumIndex and not self.clipboardDrumTrack ):
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ self.pasteTick = self.pasteTrack = -1
+ self.pasteRect = False
+ return
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ if self.clipboardDrumTrack:
+ bottom = self.drumIndex
+ else:
+ bottom = self.drumIndex - 1
+ for t in range(self.drumIndex-1,self.clipboardTrackTop-1,-1):
+ if self.clipboardArea["tracks"][t]: break
+ bottom -= 1
+ end = -tick + min( self.noteDB.getPage(self.curPage).ticks, tick + self.clipboardArea["limit"][1]-self.clipboardArea["limit"][0] )
+ self.pasteTick = tick
+ self.pasteTrack = track
+ self.pasteRect = [ [ self.ticksToPixels( self.curBeats, tick ), \
+ self.trackLimits[track][0] ], \
+ [ self.ticksToPixels( self.curBeats, end), \
+ self.trackLimits[bottom][1] ] ]
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+
+ def doPaste( self ):
+ if self.pasteTrack == -1:
+ self.doneCurrentAction()
+ return
+
+ trackMap = {}
+ for t in range(self.pasteTrack,self.drumIndex):
+ ind = t+self.clipboardTrackTop-self.pasteTrack
+ if ind >= self.drumIndex: break
+ if not self.clipboardArea["tracks"][ind]:
+ continue
+ trackMap[t] = ind
+ if self.clipboardDrumTrack:
+ trackMap[self.drumIndex] = self.drumIndex
+ new = self.owner.pasteClipboard( self.pasteTick - self.clipboardArea["limit"][0], trackMap )
+ if self.interfaceMode == INTERFACEMODE.PASTE_NOTES and self.curPage in new:
+ noteDic = {}
+ for t in range(Config.NUMBER_OF_TRACKS):
+ if len(new[self.curPage][t]):
+ noteDic[t] = [ self.noteDB.getNote( self.curPage, t, n, self ) for n in new[self.curPage][t] ]
+ self.selectNotes(noteDic)
+ elif self.interfaceMode == INTERFACEMODE.PASTE_TRACKS:
+ for t in range(self.drumIndex):
+ ind = t + self.clipboardTrackTop - self.pasteTrack
+ if ind >= self.drumIndex or ind < 0: self.owner.setTrack( t, False )
+ else: self.owner.setTrack( t, self.clipboardArea["tracks"][ind] )
+ self.owner.setTrack( self.drumIndex, self.clipboardDrumTrack )
+
+ self.doneCurrentAction()
+
+ def donePaste( self ):
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ self.pasteTick = self.pasteTrack = -1
+ self.pasteRect = False
+ self.setInterfaceMode("tool")
+ # make a fake event for updateTooltip
+ event = gtk.gdk.Event(gtk.gdk.MOTION_NOTIFY)
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+ self.updateTooltip( event )
+
+ def updateTooltip( self, event ):
+
+ # check clicked the playhead
+ if event.x >= self.playheadX and event.x <= self.playheadX + Config.PLAYHEAD_SIZE:
+ self.setCursor("drag-playhead")
+ return
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+
+ notes = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ 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, buf, noescape = True, timeout = 0 ):
+ if not self.screenBufDirty[buf]: return True
+
+ TP.ProfileBegin( "TrackInterface::draw" )
+
+ startX = self.screenBufDirtyRect[buf].x
+ startY = self.screenBufDirtyRect[buf].y
+ stopX = self.screenBufDirtyRect[buf].x + self.screenBufDirtyRect[buf].width
+ stopY = self.screenBufDirtyRect[buf].y + self.screenBufDirtyRect[buf].height
+
+ beatStart = Config.TRACK_SPACING_DIV2
+ beats = self.screenBufBeats[buf]
+
+ pixmap = self.screenBuf[buf]
+
+ resume = self.screenBufResume[buf]
+
+ self.gc.set_clip_rectangle( self.screenBufDirtyRect[buf] )
+
+ self.gc.set_line_attributes( Config.BEAT_LINE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ # regular tracks
+ for i in range( resume[0], self.drumIndex ):
+ if resume[1] == 0:
+ if startY > self.trackLimits[i][1]: continue
+ if stopY < self.trackLimits[i][0]: break
+
+ # draw background
+ if self.owner.getTrackSelected( i ):
+ pixmap.draw_drawable( self.gc, self.image["trackBGSelected"], 0, 0, 0, self.trackLimits[i][0], self.trackFullWidth, self.trackFullHeight )
+ else:
+ pixmap.draw_drawable( self.gc, self.image["trackBG"], 0, 0, 0, self.trackLimits[i][0], self.trackFullWidth, self.trackFullHeight )
+
+ # draw beat lines
+ self.gc.foreground = self.beatColor
+ for j in range(1,self.screenBufBeats[buf]):
+ x = beatStart + self.beatSpacing[beats][j]
+ pixmap.draw_line( self.gc, x, self.trackRect[i].y, x, self.trackRect[i].y+self.trackRect[i].height )
+
+ resume[1] = 1 # background drawn
+
+ # draw notes
+ TP.ProfileBegin("TI::draw notes")
+ notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], i, self )
+ for n in range( resume[2], len(notes) ):
+ # check escape
+ if not noescape and time.time() > timeout:
+ resume[0] = i
+ resume[2] = n
+ TP.ProfilePause( "TrackInterface::draw" )
+ return False
+
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+ TP.ProfileEnd("TI::draw notes")
+
+ # finished a track, reset the resume values for the next one
+ resume[1] = 0
+ resume[2] = 0
+
+ # drum track
+ if stopY > self.trackLimits[self.drumIndex][0]:
+
+ if resume[1] == 0:
+ # draw background
+ if self.owner.getTrackSelected( self.drumIndex ):
+ pixmap.draw_drawable( self.gc, self.image["trackBGDrumSelected"], 0, 0, 0, self.trackLimits[self.drumIndex][0], self.trackFullWidth, self.trackFullHeightDrum )
+ else:
+ pixmap.draw_drawable( self.gc, self.image["trackBGDrum"], 0, 0, 0, self.trackLimits[self.drumIndex][0], self.trackFullWidth, self.trackFullHeightDrum )
+
+ # draw beat lines
+ self.gc.foreground = self.beatColor
+ for j in range(1,self.screenBufBeats[buf]):
+ x = beatStart + self.beatSpacing[beats][j]
+ pixmap.draw_line( self.gc, x, self.trackRect[self.drumIndex].y, x, self.trackRect[self.drumIndex].y+self.trackRect[self.drumIndex].height )
+
+ resume[1] = 1 # background drawn
+
+ # draw notes
+ notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], self.drumIndex, self )
+ for n in range( resume[2], len(notes) ):
+ # check escape
+ if not noescape and time.time() > timeout:
+ resume[0] = i
+ resume[2] = n
+ TP.ProfilePause( "TrackInterface::draw" )
+ return False
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+
+ self.screenBufDirty[buf] = False
+
+ TP.ProfileEnd( "TrackInterface::draw" )
+
+ return True
+
+ def expose( self, DA, event ):
+
+ if self.screenBufDirty[self.curScreen]:
+ self.draw( self.curScreen )
+
+ TP.ProfileBegin( "TrackInterface::expose" )
+
+ startX = event.area.x
+ startY = event.area.y
+ stopX = event.area.x + event.area.width
+ stopY = event.area.y + event.area.height
+
+ #print "%d %d %d %d" % (startX,startY,stopX,stopY)
+
+ self.gc.set_clip_rectangle( event.area )
+
+ # draw base
+ DA.window.draw_drawable( self.gc, self.screenBuf[self.curScreen], startX, startY, startX, startY, event.area.width, event.area.height )
+
+ # draw playhead
+ self.gc.set_line_attributes( Config.PLAYHEAD_SIZE, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.playheadColor
+ DA.window.draw_line( self.gc, self.playheadX, startY, self.playheadX, stopY )
+
+ if self.marqueeLoc: # draw the selection rect
+ self.gc.set_line_attributes( Config.MARQUEE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.marqueeColor
+ DA.window.draw_rectangle( self.gc, False, self.marqueeRect[0][0], self.marqueeRect[0][1], self.marqueeRect[1][0], self.marqueeRect[1][1] )
+
+ if self.pasteRect: # draw the paste highlight
+ self.gc.set_function( gtk.gdk.INVERT )
+ for t in range(self.pasteTrack,self.drumIndex):
+ ind = t+self.clipboardTrackTop-self.pasteTrack
+ if ind >= self.drumIndex: break
+ if not self.clipboardArea["tracks"][ind]:
+ continue
+ DA.window.draw_rectangle( self.gc, True, self.pasteRect[0][0], self.trackLimits[t][0] + Config.TRACK_SPACING_DIV2, self.pasteRect[1][0], self.trackHeight )
+ if self.clipboardDrumTrack:
+ DA.window.draw_rectangle( self.gc, True, self.pasteRect[0][0], self.trackLimits[self.drumIndex][0] + Config.TRACK_SPACING_DIV2, self.pasteRect[1][0], self.trackHeightDrum )
+ self.gc.set_function( gtk.gdk.COPY )
+
+ self.drawingAreaDirty = False
+
+ TP.ProfileEnd( "TrackInterface::expose" )
+
+ def invalidate_rect( self, x, y, width, height, page = -1, base = True ):
+ #print "%d %d %d %d Page %d CurPage %d" % (x,y,width,height,page,self.curPage)
+ self.dirtyRectToAdd.x = x
+ self.dirtyRectToAdd.y = y
+ self.dirtyRectToAdd.width = width
+ self.dirtyRectToAdd.height = height
+
+ #print "dirty %d %d %d %d %d %d" % (x, y, width, height, x+width, y+height)
+ if page == self.curPage or page == -1:
+ if base: # the base image has been dirtied
+ if not self.screenBufDirty[self.curScreen]:
+ self.screenBufDirtyRect[self.curScreen].x = x
+ self.screenBufDirtyRect[self.curScreen].y = y
+ self.screenBufDirtyRect[self.curScreen].width = width
+ self.screenBufDirtyRect[self.curScreen].height = height
+ else:
+ self.screenBufDirtyRect[self.curScreen] = self.screenBufDirtyRect[self.curScreen].union( self.dirtyRectToAdd )
+ self.screenBufResume[self.curScreen] = [0,0,0]
+ self.screenBufDirty[self.curScreen] = True
+ if self.drawingArea.window != None:
+ self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True )
+ self.drawingAreaDirty = True
+
+ if page == self.screenBufPage[self.preScreen] or page == -1:
+ if not self.screenBufDirty[self.preScreen]:
+ self.screenBufDirtyRect[self.preScreen].x = x
+ self.screenBufDirtyRect[self.preScreen].y = y
+ self.screenBufDirtyRect[self.preScreen].width = width
+ self.screenBufDirtyRect[self.preScreen].height = height
+ else:
+ self.screenBufDirtyRect[self.preScreen] = self.screenBufDirtyRect[self.preScreen].union( self.dirtyRectToAdd )
+ self.screenBufResume[self.preScreen] = [0,0,0]
+ self.screenBufDirty[self.preScreen] = True
+
+ #self.queue_draw()
+
+ def getTrackOrigin( self, track ):
+ return ( self.trackRect[track].x, self.trackRect[track].y )
+
+ def ticksToPixels( self, beats, ticks ):
+ return int(round( ticks * self.pixelsPerTick[beats] ))
+ def pixelsToTicks( self, beats, pixels ):
+ return int(round( pixels * self.ticksPerPixel[beats] ))
+ def ticksToPixelsFloor( self, beats, ticks ):
+ return int( ticks * self.pixelsPerTick[beats] )
+ def pixelsToTicksFloor( self, beats, pixels ):
+ return int( pixels * self.ticksPerPixel[beats] )
+ def pitchToPixels( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+ def pixelsToPitch( self, pixels ):
+ return int(round(-pixels*self.pitchPerPixel))
+ def pitchToPixelsFloor( self, pitch ):
+ return int(( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch )
+ def pixelsToPitchFloor( self, pixels ):
+ return int(-pixels*self.pitchPerPixel)
+ def pitchToPixelsDrum( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum ))
+ def pixelsToPitchDrum( self, pixels ):
+ return int(round(-pixels*self.pitchPerPixelDrum))
+ def pitchToPixelsDrumFloor( self, pitch ):
+ return int( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum )
+ def pixelsToPitchDrumFloor( self, pixels ):
+ return int(-pixels*self.pitchPerPixelDrum)