From ee0904a24c1b7d6c2f66578607d5a4e7a8e81ab8 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 17 Feb 2007 03:45:23 +0000 Subject: merging --- (limited to 'Edit') diff --git a/Edit/HitInterface.py b/Edit/HitInterface.py index 1301a33..1b12b1f 100644 --- a/Edit/HitInterface.py +++ b/Edit/HitInterface.py @@ -18,10 +18,14 @@ class HitInterface( NoteInterface ): self.updateTransform() def updateTransform( self ): - if self.note.page == self.owner.curPage and not self.firstTransform: - oldX = self.imgX - oldY = self.imgY - oldEndX = self.imgX + self.imgWidth + if self.note.page in self.owner.getActivePages(): + if not self.firstTransform: + oldX = self.imgX + oldY = self.imgY + oldEndX = self.imgX + self.imgWidth + dirty = True + else: + dirty = False if self.note.cs.onset != self.oldOnset: self.x = self.owner.ticksToPixels( self.noteDB.getPage(self.note.page).beats, self.note.cs.onset ) @@ -33,7 +37,7 @@ class HitInterface( NoteInterface ): self.imgY = self.y - Config.NOTE_IMAGE_PADDING self.oldPitch = self.note.cs.pitch - if self.note.page == self.owner.curPage: + if dirty: if self.firstTransform: self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page ) self.firstTransform = False @@ -107,19 +111,42 @@ class HitInterface( NoteInterface ): return 1 - def noteDrag( self, emitter, do, dp, dd ): + def noteDragPitch( self, dp, stream ): self.potentialDeselect = False - - if do != self.lastDragO: - self.lastDragO = do - self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.ONSET, self.baseOnset + do ) - self.end = self.note.cs.onset + self.note.cs.duration - if dp != self.lastDragP and not dp%2: self.lastDragP = dp - newPitch = self.basePitch + dp - self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.PITCH, newPitch ) - self.updateSampleNote( newPitch ) + stream += [ self.note.id, self.basePitch + dp ] + + def noteDragDuration( self, dd, stream ): + return + + def noteDecOnset( self, step, leftBound, stream ): + if self.selected: + if leftBound < self.note.cs.onset: + onset = max( self.note.cs.onset+step, leftBound ) + stream += [ self.note.id, onset ] + return leftBound + + def noteIncOnset( self, step, rightBound, stream ): + if self.selected: + if rightBound > self.end: + onset = min( self.end+step, rightBound ) - self.note.cs.duration + stream += [ self.note.id, onset ] + return rightBound + + def noteDecPitch( self, step, stream ): + if self.note.cs.pitch > Config.MINIMUM_PITCH_DRUM: + stream += [ self.note.id, max( self.note.cs.pitch+2*step, Config.MINIMUM_PITCH_DRUM ) ] + + def noteIncPitch( self, step, stream ): + if self.note.cs.pitch < Config.MAXIMUM_PITCH_DRUM: + stream += [ self.note.id, min( self.note.cs.pitch+2*step, Config.MAXIMUM_PITCH_DRUM ) ] + + def noteDecDuration( self, step, stream ): + return + + def noteIncDuration( self, step, rightBound, stream ): + return # updateTooltip returns: # -1, event occurs before us so don't bother checking any later notes diff --git a/Edit/MainWindow.py b/Edit/MainWindow.py index 2a8465b..4514f3e 100644 --- a/Edit/MainWindow.py +++ b/Edit/MainWindow.py @@ -207,17 +207,17 @@ class MainWindow( gtk.EventBox ): self.GUI["2toolPanel"].set_size_request( -1, 75 ) # + + tool box self.GUI["2toolBox"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2toolBox"].set_size_request( 146, -1 ) + self.GUI["2toolBox"].set_size_request( 154, -1 ) self.GUI["2toolPointerButton"] = ImageRadioButton( None, Config.IMAGE_ROOT+"pointer.png", Config.IMAGE_ROOT+"pointerDown.png", backgroundFill = "#6C9790" ) - self.GUI["2toolPointerButton"].connect( "clicked", self.handleToolClick , "Default" ) + self.GUI["2toolPointerButton"].connect( "clicked", self.handleToolClick , "default" ) self.GUI["2toolBox"].pack_start( self.GUI["2toolPointerButton"] ) self.GUI["2toolPencilButton"] = ImageRadioButton( self.GUI["2toolPointerButton"], Config.IMAGE_ROOT+"pencil.png", Config.IMAGE_ROOT+"pencilDown.png", backgroundFill = "#6C9790" ) - self.GUI["2toolPencilButton"].connect( "clicked", self.handleToolClick , "Draw" ) + self.GUI["2toolPencilButton"].connect( "clicked", self.handleToolClick , "draw" ) self.GUI["2toolBox"].pack_start( self.GUI["2toolPencilButton"] ) self.GUI["2toolPanel"].pack_start( self.GUI["2toolBox"], False, False ) self.GUI["2rightPanel"].pack_start( self.GUI["2toolPanel"], False ) # + + context box (for context sensitive buttons, nothing to do with CAIRO) - contextWidth = 592 + contextWidth = 674 self.GUI["2contextBox"] = formatRoundBox( RoundFixed(), "#6C9790" ) self.GUI["2contextBox"].set_size_request( contextWidth, -1 ) self.GUI["2contextPrevButton"] = gtk.Button("<") @@ -229,48 +229,92 @@ class MainWindow( gtk.EventBox ): # + + + page box self.GUI["2pageBox"] = gtk.HBox() self.GUI["2pageBox"].set_size_request( contextWidth-50, -1 ) + self.GUI["2pageGenerateButton"] = gtk.Button("Gen") + self.GUI["2pageGenerateButton"].connect( "clicked", lambda a1:self.pageGenerate() ) + self.GUI["2pageBox"].pack_start( self.GUI["2pageGenerateButton"] ) + self.GUI["2pagePropertiesButton"] = gtk.Button("Prop") + self.GUI["2pagePropertiesButton"].connect( "clicked", lambda a1:self.pageProperties() ) + self.GUI["2pageBox"].pack_start( self.GUI["2pagePropertiesButton"] ) self.GUI["2pageDeleteButton"] = gtk.Button("Delete") - self.GUI["2pageDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) + self.GUI["2pageDeleteButton"].connect( "clicked", lambda a1:self.pageDelete() ) self.GUI["2pageBox"].pack_start( self.GUI["2pageDeleteButton"] ) - self.GUI["2pageNewButton"] = gtk.Button("New") - self.GUI["2pageNewButton"].connect( "clicked", lambda a1:self.addPage() ) - self.GUI["2pageBox"].pack_start( self.GUI["2pageNewButton"] ) self.GUI["2pageDuplicateButton"] = gtk.Button("Duplicate") - self.GUI["2pageDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) + self.GUI["2pageDuplicateButton"].connect( "clicked", lambda a1:self.pageDuplicate() ) self.GUI["2pageBox"].pack_start( self.GUI["2pageDuplicateButton"] ) + self.GUI["2pageNewButton"] = gtk.Button("New") + self.GUI["2pageNewButton"].connect( "clicked", lambda a1:self.pageAdd() ) + self.GUI["2pageBox"].pack_start( self.GUI["2pageNewButton"] ) + self.GUI["2pageBeatsButton"] = gtk.Button("Beats") + self.GUI["2pageBeatsButton"].connect( "clicked", lambda a1:self.pageBeats() ) + self.GUI["2pageBox"].pack_start( self.GUI["2pageBeatsButton"] ) self.GUI["2contextBox"].put( self.GUI["2pageBox"], 25, 0 ) # + + + track box self.GUI["2trackBox"] = gtk.HBox() self.GUI["2trackBox"].set_size_request( contextWidth-50, -1 ) + self.GUI["2trackGenerateButton"] = gtk.Button("tGen") + self.GUI["2trackGenerateButton"].connect( "clicked", lambda a1:self.trackGenerate() ) + self.GUI["2trackBox"].pack_start( self.GUI["2trackGenerateButton"] ) + self.GUI["2trackPropertiesButton"] = gtk.Button("tProp") + self.GUI["2trackPropertiesButton"].connect( "clicked", lambda a1:self.trackProperties() ) + self.GUI["2trackBox"].pack_start( self.GUI["2trackPropertiesButton"] ) self.GUI["2trackDeleteButton"] = gtk.Button("tDelete") - self.GUI["2trackDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) + self.GUI["2trackDeleteButton"].connect( "clicked", lambda a1:self.trackDelete() ) self.GUI["2trackBox"].pack_start( self.GUI["2trackDeleteButton"] ) - self.GUI["2trackNewButton"] = gtk.Button("tNew") - self.GUI["2trackNewButton"].connect( "clicked", lambda a1:self.addPage() ) - self.GUI["2trackBox"].pack_start( self.GUI["2trackNewButton"] ) - self.GUI["2trackDuplicateButton"] = gtk.Button("tDuplicate") - self.GUI["2trackDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) + self.GUI["2trackDuplicateButton"] = gtk.ToggleButton("tDuplicate") + self.GUI["2trackDuplicateButton"].connect( "toggled", lambda a1:self.trackDuplicate() ) self.GUI["2trackBox"].pack_start( self.GUI["2trackDuplicateButton"] ) self.GUI["2contextBox"].put( self.GUI["2trackBox"], 25, 0 ) # + + + note box self.GUI["2noteBox"] = gtk.HBox() self.GUI["2noteBox"].set_size_request( contextWidth-50, -1 ) + self.GUI["2noteGenerateButton"] = gtk.Button("nGen") + self.GUI["2noteGenerateButton"].connect( "clicked", lambda a1:self.noteGenerate() ) + self.GUI["2noteBox"].pack_start( self.GUI["2noteGenerateButton"] ) + self.GUI["2notePropertiesButton"] = gtk.Button("nProp") + self.GUI["2notePropertiesButton"].connect( "clicked", lambda a1:self.noteProperties() ) + self.GUI["2noteBox"].pack_start( self.GUI["2notePropertiesButton"] ) self.GUI["2noteDeleteButton"] = gtk.Button("nDelete") - self.GUI["2noteDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) + self.GUI["2noteDeleteButton"].connect( "clicked", lambda a1:self.noteDelete() ) self.GUI["2noteBox"].pack_start( self.GUI["2noteDeleteButton"] ) - self.GUI["2noteNewButton"] = gtk.Button("nNew") - self.GUI["2noteNewButton"].connect( "clicked", lambda a1:self.addPage() ) - self.GUI["2noteBox"].pack_start( self.GUI["2noteNewButton"] ) - self.GUI["2noteDuplicateButton"] = gtk.Button("nDuplicate") - self.GUI["2noteDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) + self.GUI["2noteDuplicateButton"] = gtk.ToggleButton("nDuplicate") + self.GUI["2noteDuplicateButton"].connect( "toggled", self.noteDuplicateWidget ) self.GUI["2noteBox"].pack_start( self.GUI["2noteDuplicateButton"] ) + self.GUI["2noteOnsetBox"] = gtk.HBox() + self.GUI["2noteOnsetMinusButton"] = gtk.Button("<") + self.GUI["2noteOnsetMinusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepOnset(-1) ) + self.GUI["2noteOnsetBox"].pack_start( self.GUI["2noteOnsetMinusButton"] ) + self.GUI["2noteOnsetPlusButton"] = gtk.Button(">") + self.GUI["2noteOnsetPlusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepOnset(1) ) + self.GUI["2noteOnsetBox"].pack_start( self.GUI["2noteOnsetPlusButton"] ) + self.GUI["2noteBox"].pack_start( self.GUI["2noteOnsetBox"] ) + self.GUI["2notePitchBox"] = gtk.VBox() + self.GUI["2notePitchPlusButton"] = gtk.Button("^") + self.GUI["2notePitchPlusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepPitch(1) ) + self.GUI["2notePitchBox"].pack_start( self.GUI["2notePitchPlusButton"] ) + self.GUI["2notePitchMinusButton"] = gtk.Button("v") + self.GUI["2notePitchMinusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepPitch(-1) ) + self.GUI["2notePitchBox"].pack_start( self.GUI["2notePitchMinusButton"] ) + self.GUI["2noteBox"].pack_start( self.GUI["2notePitchBox"] ) + self.GUI["2noteDurationBox"] = gtk.HBox() + self.GUI["2noteDurationMinusButton"] = gtk.Button("<") + self.GUI["2noteDurationMinusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepDuration(-1) ) + self.GUI["2noteDurationBox"].pack_start( self.GUI["2noteDurationMinusButton"] ) + self.GUI["2noteDurationPlusButton"] = gtk.Button(">") + self.GUI["2noteDurationPlusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepDuration(1) ) + self.GUI["2noteDurationBox"].pack_start( self.GUI["2noteDurationPlusButton"] ) + self.GUI["2noteBox"].pack_start( self.GUI["2noteDurationBox"] ) + self.GUI["2noteVolumeBox"] = gtk.VBox() + self.GUI["2noteVolumePlusButton"] = gtk.Button("^") + self.GUI["2noteVolumePlusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepVolume(0.05) ) + self.GUI["2noteVolumeBox"].pack_start( self.GUI["2noteVolumePlusButton"] ) + self.GUI["2noteVolumeMinusButton"] = gtk.Button("v") + self.GUI["2noteVolumeMinusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepVolume(-0.05) ) + self.GUI["2noteVolumeBox"].pack_start( self.GUI["2noteVolumeMinusButton"] ) + self.GUI["2noteBox"].pack_start( self.GUI["2noteVolumeBox"] ) self.GUI["2contextBox"].put( self.GUI["2noteBox"], 25, 0 ) self.GUI["2toolPanel"].pack_start( self.GUI["2contextBox"], False ) # + + transport box self.GUI["2transportBox"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2generateButton"] = gtk.Button("G") - self.GUI["2generateButton"].connect( "clicked", self.handleGenerate, None ) - self.GUI["2transportBox"].pack_start( self.GUI["2generateButton"] ) self.GUI["2recordButton"] = gtk.ToggleButton("R") self.GUI["2transportBox"].pack_start( self.GUI["2recordButton"] ) self.GUI["2playButton"] = gtk.ToggleButton("P") @@ -311,6 +355,10 @@ class MainWindow( gtk.EventBox ): self.currentpageId = 0 self.playingTuneIdx = 0 + # timers + self.predrawTimeout = False + self.playbackTimeout = False + # FPS stuff self.fpsTotalTime = 0 self.fpsFrameCount = 0 @@ -323,7 +371,7 @@ class MainWindow( gtk.EventBox ): init_data() #above init_GUI() #above - + self.csnd.setMasterVolume( self.getVolume() ) for tid in range(Config.NUMBER_OF_TRACKS): @@ -376,7 +424,10 @@ class MainWindow( gtk.EventBox ): notes += self.noteDB.getCSNotesByTrack( page, track ) self.playing = True - self.playbackTimeout = gobject.timeout_add( 100, self.onTimeout ) + if self.predrawTimeout: + gobject.source_remove( self.predrawTimeout ) + self.predrawTimeout = False + self.playbackTimeout = gobject.timeout_add( 50, self.onTimeout ) if len(self.pages_playing) > 1: self.displayPage( self.pages_playing[0], self.pages_playing[1] ) @@ -407,6 +458,7 @@ class MainWindow( gtk.EventBox ): else: #stop gobject.source_remove( self.playbackTimeout ) + self.playbackTimeout = False if False: #This is causing csound to stop working... # reimplement this with real CSoundNotes and it should be ok. @@ -426,10 +478,22 @@ class MainWindow( gtk.EventBox ): curtick = self.csnd.loopGetTick() curIdx = curtick / ( 4 * Config.TICKS_PER_BEAT) #TODO handle each pages_playing length - if curIdx + 1 < len(self.pages_playing): predraw = self.pages_playing[curIdx+1] - else: predraw = self.pages_playing[0] - self.displayPage( self.pages_playing[curIdx], predraw ) + # TODO update playhead + + if self.pages_playing[curIdx] != self.displayedPage: + if curIdx + 1 < len(self.pages_playing): predraw = self.pages_playing[curIdx+1] + else: predraw = self.pages_playing[0] + self.displayPage( self.pages_playing[curIdx], predraw ) + else: + self.trackInterface.predrawPage( time.time() + 0.020 ) # 20 ms time limit + + return True + + def onPredrawTimeout( self ): + if self.trackInterface.predrawPage( time.time() + 0.020 ): # 20 ms time limit + self.predrawTimeout = False + return False return True def onMuteTrack( self, widget, trackId ): @@ -478,6 +542,10 @@ class MainWindow( gtk.EventBox ): def handleToolClick( self, widget, mode ): if widget.get_active(): self.trackInterface.setInterfaceMode( mode ) + def getTool( self ): + if self.GUI["2toolPointerButton"].get_active(): return "default" + else: return "draw" + def onKeyboardButton( self, widget, data ): self.kb_active = widget.get_active() @@ -488,11 +556,6 @@ class MainWindow( gtk.EventBox ): #----------------------------------- # generation functions #----------------------------------- - def handleGenerate( self, widget, data ): - #if widget.get_active(): - self.generationParametersWindow.show_all() - #else: - # self.handleCloseGeneratonParametersWindow() def handleCloseGenerationParametersWindow( self, widget = None, data = None ): self.generationParametersWindow.hide_all() @@ -507,11 +570,19 @@ class MainWindow( gtk.EventBox ): for p in range(Config.NUMBER_OF_PAGES): dict[t][p] = [] - if self.trackSelected == [ 0 for i in range(Config.NUMBER_OF_TRACKS) ]: + if self.generateMode == "note": + # unsupported!? + self.handleCloseGenerationParametersWindow( None, None ) + return + elif self.generateMode == "track": + if self.trackSelected == [ 0 for i in range(Config.NUMBER_OF_TRACKS) ]: + newtracks = set(range(Config.NUMBER_OF_TRACKS)) + else: + newtracks = set( [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] ) + newpages = self.tuneInterface.getSelectedIds() + else: # page mode newtracks = set(range(Config.NUMBER_OF_TRACKS)) - else: - newtracks = set( [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] ) - newpages = self.tuneInterface.getSelectedIds() + newpages = self.tuneInterface.getSelectedIds() algo( params, @@ -532,17 +603,21 @@ class MainWindow( gtk.EventBox ): note.pageId = page note.trackId = track - # add the new notes + # prepare the new notes newnotes = [] for tid in dict: for pid in dict[tid]: newnotes += dict[tid][pid] # delete the notes and add the new + if self.generateMode == "note": + print "TODO generateMode == note" + else: # track or page mode + self.noteDB.deleteNotesByTrack( newpages, newtracks ) + stream = [] for page in newpages: for track in newtracks: - self.noteDB.deleteNotesByTrack( page, track ) stream += [ page, track, len(dict[track][page]) ] stream += dict[track][page] stream += [-1] @@ -557,14 +632,96 @@ class MainWindow( gtk.EventBox ): self.recompose( variate, params) #======================================================= + # Clipboard Functions + + def getClipboardArea( self, page = -1 ): + if page == -1: page = self.displayedPage + ids = self.tuneInterface.getSelectedIds() + return self.noteDB.getClipboardArea( ids.index(page) ) + + def pasteClipboard( self, offset, trackMap ): + pages = self.tuneInterface.getSelectedIds() + return self.noteDB.pasteClipboard( pages, offset, trackMap ) + + def cleanupClipboard( self ): + if self.GUI["2noteDuplicateButton"].get_active(): + self.GUI["2noteDuplicateButton"].set_active(False) + if self.GUI["2trackDuplicateButton"].get_active(): + self.GUI["2trackDuplicateButton"].set_active(False) + self.trackInterface.donePaste() + + + #======================================================= + # Note Functions + + def noteGenerate( self ): + self.generateMode = "note" + self.generationParametersWindow.show_all() + return + + def noteProperties( self ): + # TODO + return + + def noteDelete( self ): + ids = self.trackInterface.getSelectedNotes() + stream = [] + for t in range(Config.NUMBER_OF_TRACKS): + N = len(ids[t]) + if not N: continue + stream += [ self.displayedPage, t, N ] + ids[t] + if len(stream): + self.noteDB.deleteNotes( stream + [-1] ) + + def noteDuplicate( self ): + ids = self.trackInterface.getSelectedNotes() + stream = [] + for t in range(Config.NUMBER_OF_TRACKS): + N = len(ids[t]) + if not N: continue + stream += [ self.displayedPage, t, N ] + ids[t] + if len(stream): + if self.GUI["2trackDuplicateButton"].get_active(): + self.GUI["2trackDuplicateButton"].set_active( False ) + self.noteDB.notesToClipboard( stream + [-1] ) + self.trackInterface.setInterfaceMode("paste_notes") + return True + return False + + def noteDuplicateWidget( self, widget ): + if widget.get_active(): + if self.noteDuplicate(): # duplicate succeeded + return + # cancel duplicate + self.trackInterface.setInterfaceMode("tool") + widget.set_active(False) + + def noteOnset( self, step ): + self.trackInterface.noteStepOnset( step ) + + def notePitch( self, step ): + # TODO + return + + def noteDuration( self, step ): + # TODO + return + + def noteVolume( self, step ): + # TODO + return + + #======================================================= # Track Functions def toggleTrack( self, trackN, exclusive ): if exclusive: for i in range(Config.NUMBER_OF_TRACKS): - self.trackSelected[i] = False + if self.trackSelected[i]: + self.trackSelected[i] = False + self.trackInterface.trackToggled( i ) self.trackSelected[trackN] = True - self.trackInterface.trackToggled() # invalidate whole page + self.trackInterface.trackToggled( trackN ) self.setContextState( CONTEXT.TRACK, True ) self.setContext( CONTEXT.TRACK ) else: @@ -577,27 +734,60 @@ class MainWindow( gtk.EventBox ): return self.setContextState( CONTEXT.TRACK, False ) + def setTrack( self, trackN, state ): + if self.trackSelected[trackN] != state: + self.trackSelected[trackN] = state + self.trackInterface.trackToggled( trackN ) + + def clearTracks( self ): + for i in range(Config.NUMBER_OF_TRACKS): + if self.trackSelected[i]: + self.trackSelected[i]= False + self.trackInterface.trackToggled( i ) + + self.setContextState( CONTEXT.TRACK, False ) + def getTrackSelected( self, trackN ): return self.trackSelected[trackN] - #======================================================= - # NoteDB notifications + def trackGenerate( self ): + self.generateMode = "track" + self.generationParametersWindow.show_all() - def notifyPageAdd( self, id, at ): - self.displayPage( id ) + def trackProperties( self, trackIds = -1 ): + # TODO + return - def notifyPageDelete( self, which, safe ): - if self.displayedPage in which: - self.displayPage( safe ) + def trackDelete( self, pageIds = -1, trackIds = -1 ): - def notifyPageDuplicate( self, new, at ): - self.displayPage( new[self.displayedPage] ) + if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds() + if trackIds == -1: trackIds = [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] - def notifyPageMove( self, which, low, high ): - return + self.noteDB.deleteNotesByTrack( pageIds, trackIds ) + + def trackDuplicate( self, pageIds = -1, trackIds = -1 ): + + if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds() + if trackIds == -1: trackIds = [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] + + if len(trackIds): + if self.GUI["2noteDuplicateButton"].get_active(): + self.GUI["2noteDuplicateButton"].set_active( False ) + self.noteDB.tracksToClipboard( pageIds, trackIds ) + self.trackInterface.setInterfaceMode("paste_tracks") + return True + return False + + def trackDuplicateWidget( self, widget ): + if widget.get_active(): + if self.trackDuplicate(): # duplicate succeeded + return + # cancel duplicate + self.trackInterface.setInterfaceMode("tool") + widget.set_active(False) #----------------------------------- - # tune functions + # tune/page functions #----------------------------------- def scrollTune( self, scroll ): @@ -614,25 +804,63 @@ class MainWindow( gtk.EventBox ): self.trackInterface.displayPage( pageId, nextId ) - def addPage( self, after = -1, beats = False ): + def predrawPage( self, pageId ): + if self.playbackTimeout: return # we're playing, predrawing is already handled + if self.trackInterface.setPredrawPage( pageId ): # page needs to be drawn + if not self.predrawTimeout: + self.predrawTimeout = gobject.timeout_add( 50, self.onPredrawTimeout ) - if after == -1: after = self.tuneInterface.getLastSelected() - if not beats: beats = self.noteDB.getPage( self.displayedPage ).beats + def pageGenerate( self ): + self.generateMode = "page" + self.generationParametersWindow.show_all() - self.noteDB.addPage( beats, after ) + def pageProperties( self, pageIds = -1 ): - def duplicatePages( self, after = -1, pageIds = False ): + if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds() + + # TODO show properties or something + + def pageDelete( self, pageIds = -1 ): + + if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds() + + self.noteDB.deletePages( pageIds ) + + def pageDuplicate( self, after = -1, pageIds = False ): if after == -1: after = self.tuneInterface.getLastSelected() if not pageIds: pageIds = self.tuneInterface.getSelectedIds() self.noteDB.duplicatePages( pageIds, after ) - def removePages( self, pageIds = -1 ): + def pageAdd( self, after = -1, beats = False ): + + if after == -1: after = self.tuneInterface.getLastSelected() + if not beats: beats = self.noteDB.getPage( self.displayedPage ).beats + + self.noteDB.addPage( beats, after ) + + def pageBeats( self, pageIds = -1 ): if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds() - self.noteDB.deletePages( pageIds ) + # TODO change the beats + + #======================================================= + # NoteDB notifications + + def notifyPageAdd( self, id, at ): + self.displayPage( id ) + + def notifyPageDelete( self, which, safe ): + if self.displayedPage in which: + self.displayPage( safe ) + + def notifyPageDuplicate( self, new, at ): + self.displayPage( new[self.displayedPage] ) + + def notifyPageMove( self, which, low, high ): + return #----------------------------------- # load and save functions diff --git a/Edit/NoteInterface.py b/Edit/NoteInterface.py index 9dc9e43..25a160d 100644 --- a/Edit/NoteInterface.py +++ b/Edit/NoteInterface.py @@ -40,7 +40,11 @@ class NoteInterface: self.updateParameter( None, None ) def destroy( self ): - self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True ) + if self.selected: + print "destroy", self.note.id + self.owner.deselectNotes( { self.note.track: [self] } ) + else: # if we were deselected above the rect has already been invalidated + self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True ) def updateParameter( self, parameter, value ): self.end = self.note.cs.onset + self.note.cs.duration @@ -70,10 +74,14 @@ class NoteInterface: return self.note.cs.pitch def updateTransform( self ): - if self.note.page == self.owner.curPage and not self.firstTransform: - oldX = self.imgX - oldY = self.imgY - oldEndX = self.imgX + self.imgWidth + if self.note.page in self.owner.getActivePages(): + if not self.firstTransform: + oldX = self.imgX + oldY = self.imgY + oldEndX = self.imgX + self.imgWidth + dirty = True + else: + dirty = False if self.note.cs.onset != self.oldOnset: self.x = self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.onset ) @@ -89,15 +97,16 @@ class NoteInterface: self.imgY = self.y - Config.NOTE_IMAGE_PADDING self.oldPitch = self.note.cs.pitch - if self.firstTransform: - self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True ) - self.firstTransform = False - else: - x = min( self.imgX, oldX ) - y = min( self.imgY, oldY ) - endx = max( self.imgX + self.imgWidth, oldEndX ) - endy = max( self.imgY, oldY ) + self.imgHeight - self.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page, True ) + if dirty: + if self.firstTransform: + self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True ) + self.firstTransform = False + else: + x = min( self.imgX, oldX ) + y = min( self.imgY, oldY ) + endx = max( self.imgX + self.imgWidth, oldEndX ) + endy = max( self.imgY, oldY ) + self.imgHeight + self.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page, True ) def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound, maxRightBound ): left = leftBound - self.note.cs.onset @@ -187,24 +196,23 @@ class NoteInterface: return True - def noteDrag( self, emitter, do, dp, dd ): - self.potentialDeselect = False - + def noteDragOnset( self, do, stream ): + self. potentialDeselect = False if do != self.lastDragO: self.lastDragO = do - self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.ONSET, self.baseOnset + do ) - self.end = self.note.cs.onset + self.note.cs.duration + stream += [ self.note.id, self.baseOnset + do ] + def noteDragPitch( self, dp, stream ): + self.potentialDeselect = False if dp != self.lastDragP: self.lastDragP = dp - newPitch = self.basePitch + dp - self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.PITCH, newPitch ) - self.updateSampleNote( newPitch ) + stream += [ self.note.id, self.basePitch + dp ] + def noteDragDuration( self, dd, stream ): + self.potentialDeselect = False if dd != self.lastDragD: self.lastDragD = dd - self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.DURATION, self.baseDuration + dd ) - self.end = self.note.cs.onset + self.note.cs.duration + stream += [ self.note.id, self.baseDuration + dd ] def doneNoteDrag( self, emitter ): self.baseOnset = self.note.cs.onset @@ -217,6 +225,47 @@ class NoteInterface: self.clearSampleNote() + def noteDecOnset( self, step, leftBound, stream ): + if self.selected: + if leftBound < self.note.cs.onset: + onset = max( self.note.cs.onset+step, leftBound ) + stream += [ self.note.id, onset ] + return onset + self.note.cs.duration + return self.end + + def noteIncOnset( self, step, rightBound, stream ): + if self.selected: + if rightBound > self.end: + onset = min( self.end+step, rightBound ) - self.note.cs.duration + stream += [ self.note.id, onset ] + return onset + return self.note.cs.onset + + def noteDecPitch( self, step, stream ): + if self.note.cs.pitch > Config.MINIMUM_PITCH: + stream += [ self.note.id, max( self.note.cs.pitch+step, Config.MINIMUM_PITCH ) ] + + def noteIncPitch( self, step, stream ): + if self.note.cs.pitch < Config.MAXIMUM_PITCH: + stream += [ self.note.id, min( self.note.cs.pitch+step, Config.MAXIMUM_PITCH ) ] + + def noteDecDuration( self, step, stream ): + if self.note.cs.duration > Config.MINIMUM_NOTE_DURATION: + stream += [ self.note.id, max( self.note.cs.duration+step, Config.MINIMUM_NOTE_DURATION ) ] + + def noteIncDuration( self, step, rightBound, stream ): + if self.selected: + if self.end < rightBound: + stream += [ self.note.id, min( self.end+step, rightBound ) - self.note.cs.onset ] + + def noteDecVolume( self, step, stream ): + if self.note.cs.amplitude > 0: + stream += [ self.note.id, max( self.note.cs.amplitude+step, 0 ) ] + + def noteIncVolume( self, step, stream ): + if self.note.cs.amplitude < 1: + stream += [ self.note.id, min( self.note.cs.amplitude+step, 1 ) ] + def handleMarqueeSelect( self, emitter, start, stop ): intersectionY = [ max(start[1],self.y), min(stop[1],self.y+self.height) ] if intersectionY[0] > intersectionY[1]: diff --git a/Edit/TrackInterface.py b/Edit/TrackInterface.py index a3b3986..a5b7593 100644 --- a/Edit/TrackInterface.py +++ b/Edit/TrackInterface.py @@ -3,12 +3,15 @@ pygtk.require( '2.0' ) import gtk 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.Profiler import TP class SELECTNOTES: @@ -22,7 +25,8 @@ class SELECTNOTES: class INTERFACEMODE: DEFAULT = 0 DRAW = 1 - PASTE = 2 + PASTE_NOTES = 2 + PASTE_TRACKS = 3 class TrackInterfaceParasite: def __init__( self, noteDB, owner, note ): @@ -66,15 +70,20 @@ class TrackInterface( gtk.EventBox ): 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.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.SB_V_DOUBLE_ARROW), \ - "drag-duration": gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW), \ - "drag-playhead": gtk.gdk.Cursor(gtk.gdk.LEFT_SIDE), \ + "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) @@ -164,6 +173,16 @@ class TrackInterface( gtk.EventBox ): self.curScreen = 0 self.preScreen = 1 + #-- 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] + #======================================================= # Module Interface @@ -173,18 +192,32 @@ class TrackInterface( gtk.EventBox ): else: return ( self.image["note"], self.image["noteSelected"], self.drawingArea.get_colormap(), self.trackColors[track] ) - def predrawPage( self, page ): + 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, timeout ): + return self.draw( self.preScreen, False, timeout ) def displayPage( self, page, predraw = -1 ): - if page == self.curPage: return + 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 @@ -202,23 +235,43 @@ class TrackInterface( gtk.EventBox ): self.screenBufPage[self.preScreen] = predraw self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats self.invalidate_rect( 0, 0, self.width, self.height, predraw ) + elif self.screenBufPage[self.preScreen] == -1: # make sure predraw is assigned to a valid page at least + self.screenBufPage[self.preScreen] = self.screenBufPage[self.curScreen] if clearNotes: # clear the notes now that we've sorted out the screen buffers - self.clearSelectedNotes() + self.clearSelectedNotes( oldPage ) + + if self.curAction == "paste": + self._updateClipboardArea() def setPlayhead( self, ticks ): self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False ) - self.playheadX = self.ticksToPixels( ticks, self.screenBufBeats[self.curScreen] ) + Config.TRACK_SPACING_DIV2 + 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 ) def setInterfaceMode( self, mode ): - if mode == "Draw": + self.doneCurrentAction() + + if mode == "tool": + mode = self.owner.getTool() + + if mode == "draw": self.interfaceMode = INTERFACEMODE.DRAW - elif mode == "Paste": - self.interfaceMode = INTERFACEMODE.PASTE + 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 @@ -226,7 +279,7 @@ class TrackInterface( gtk.EventBox ): self.alloc = allocation width = allocation.width height = allocation.height - + self.drawingArea.set_size_request( width, height ) if self.window != None: @@ -242,6 +295,13 @@ class TrackInterface( gtk.EventBox ): 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 ) @@ -289,8 +349,9 @@ class TrackInterface( gtk.EventBox ): 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 ) - else: self.owner.toggleTrack( i, True ) + 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" ) @@ -317,7 +378,6 @@ class TrackInterface( gtk.EventBox ): 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) @@ -326,7 +386,17 @@ class TrackInterface( gtk.EventBox ): TP.ProfileEnd( "TI::handleMotion::Common" ) - if event.state & gtk.gdk.BUTTON1_MASK: + 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.pixelsToTicks( 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 @@ -361,9 +431,9 @@ class TrackInterface( gtk.EventBox ): #======================================================= # Actions - def setCurrentAction( self, action, obj ): + def setCurrentAction( self, action, obj = None ): if self.curAction: - print "BackgroundView - Action already in progress!" + self.doneCurrentAction() self.curAction = action self.curActionObject = obj @@ -372,19 +442,25 @@ class TrackInterface( gtk.EventBox ): 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 self.curAction == "note-drag-onset": self.doneNoteDrag() - elif self.curAction == "note-drag-duration": self.doneNoteDrag() - elif self.curAction == "note-drag-pitch": self.doneNoteDrag() - elif self.curAction == "note-drag-pitch-drum": self.doneNoteDrag() - + if not self.curAction: return + action = self.curAction self.curAction = False - self.curActionObject = False + + if action == "note-drag-onset": self.doneNoteDrag() + elif action == "note-drag-duration": self.doneNoteDrag() + elif action == "note-drag-pitch": self.doneNoteDrag() + elif action == "note-drag-pitch-drum": self.doneNoteDrag() + elif action == "paste": + self.owner.cleanupClipboard() def trackToggled( self, trackN = -1 ): - if trackN == -1: self.invalidate_rect( 0, 0, self.width, self.height, self.curPage ) - else: self.invalidate_rect( 0, self.trackLimits[trackN][0], self.width, self.trackLimits[trackN][1]-self.trackLimits[trackN][0], self.curPage ) + 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() @@ -398,14 +474,15 @@ class TrackInterface( gtk.EventBox ): return self.owner.setContextState( CONTEXT.NOTE, False ) - def applyNoteSelection( self, mode, trackN, which ): + def applyNoteSelection( self, mode, trackN, which, page = -1 ): + if page == -1: page = self.curPage if mode == SELECTNOTES.ALL: - track = self.noteDB.getNotesByTrack( self.curPage, trackN, self ) + 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.noteDB.getNotesByTrack( self.curPage, trackN, self ) + track = self.noteDB.getNotesByTrack( page, trackN, self ) map( lambda note:note.setSelected( False ), track ) self.selectedNotes[trackN] = [] elif mode == SELECTNOTES.ADD: @@ -425,7 +502,7 @@ class TrackInterface( gtk.EventBox ): note.setSelected( True ) self.selectedNotes[trackN].append( note ) elif mode == SELECTNOTES.EXCLUSIVE: - notes = self.noteDB.getNotesByTrack( self.curPage, trackN, self ) + notes = self.noteDB.getNotesByTrack( page, trackN, self ) for n in range(len(notes)): if notes[n] in which: if notes[n].setSelected( True ): @@ -434,51 +511,51 @@ class TrackInterface( gtk.EventBox ): if notes[n].setSelected( False ): self.selectedNotes[trackN].remove( notes[n] ) - def selectNotesByBar( self, trackN, start, stop ): + 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 ) - else: self.applyNoteSelection( SELECTNOTES.ADD, trackN, notes ) + 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, [] ) + if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page ) self.selectionChanged() - def selectNotesByTrack( self, trackN ): + def selectNotesByTrack( self, trackN, page = -1 ): if Config.ModKeys.ctrlDown: - self.applyNoteSelection( SELECTNOTES.ALL, trackN, [] ) + self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page ) else: for i in range(Config.NUMBER_OF_TRACKS): - if i == trackN: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [] ) - else: self.applyNoteSelection( SELECTNOTES.NONE, i, [] ) + if i == trackN: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page ) + else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page ) self.selectionChanged() - def selectNotes( self, noteDic ): - if Config.ModKeys.ctrlDown: + 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] ) + 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] ) - else: self.applyNoteSelection( SELECTNOTES.NONE, i, [] ) + 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 ): + def deselectNotes( self, noteDic, page = -1 ): for i in noteDic: - self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i] ) + self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i], page ) self.selectionChanged() - def clearSelectedNotes( self ): + def clearSelectedNotes( self, page = -1 ): for i in range(Config.NUMBER_OF_TRACKS): - self.applyNoteSelection( SELECTNOTES.NONE, i, [] ) + 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.curBeats * Config.TICKS_PER_BEAT + 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 @@ -510,39 +587,133 @@ class TrackInterface( gtk.EventBox ): 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 ) ) - dp = 0 - dd = 0 + stream = [] for i in range(Config.NUMBER_OF_TRACKS): + tstream = [] for note in self.selectedNotes[i]: - note.noteDrag(self, do, dp, dd) + 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 ): - do = 0 - dp = 0 dd = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] ) dd = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dd ) ) + stream = [] for i in range(Config.NUMBER_OF_TRACKS): + tstream = [] for note in self.selectedNotes[i]: - note.noteDrag(self, do, dp, dd) + 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 ): - do = 0 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 ) ) - dd = 0 + stream = [] for i in range(Config.NUMBER_OF_TRACKS): + tstream = [] for note in self.selectedNotes[i]: - note.noteDrag(self, do, dp, dd) + 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] ) def doneNoteDrag( self ): 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] @@ -616,6 +787,79 @@ class TrackInterface( gtk.EventBox ): 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 @@ -654,8 +898,10 @@ class TrackInterface( gtk.EventBox ): #======================================================= # Drawing - def predraw( self, buf, noescape = True ): - TP.ProfileBegin( "TrackInterface::predraw" ) + 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 @@ -675,7 +921,7 @@ class TrackInterface( gtk.EventBox ): 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[0] == 0: + if resume[1] == 0: if startY > self.trackLimits[i][1]: continue if stopY < self.trackLimits[i][0]: break @@ -691,28 +937,30 @@ class TrackInterface( gtk.EventBox ): x = beatStart + j*beatSpacing 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.curPage, i, self ) - for n in range( resume[1], len(notes) ): + notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], i, self ) + for n in range( resume[2], len(notes) ): # check escape - if 0: + if not noescape and time.time() > timeout: resume[0] = i - resume[1] = n - TP.ProfilePause( "TrackInterface::predraw" ) + 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[0] = 0 resume[1] = 0 + resume[2] = 0 # drum track if stopY > self.trackLimits[self.drumIndex][0]: - if resume[0] == 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 ) @@ -725,27 +973,29 @@ class TrackInterface( gtk.EventBox ): x = beatStart + j*beatSpacing 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.curPage, self.drumIndex, self ) - for n in range( resume[1], len(notes) ): + notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], self.drumIndex, self ) + for n in range( resume[2], len(notes) ): # check escape - if 0: + if not noescape and time.time() > timeout: resume[0] = i - resume[1] = n - TP.ProfilePause( "TrackInterface::predraw" ) + 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::predraw" ) + TP.ProfileEnd( "TrackInterface::draw" ) return True def expose( self, DA, event ): if self.screenBufDirty[self.curScreen]: - self.predraw( self.curScreen ) + self.draw( self.curScreen ) TP.ProfileBegin( "TrackInterface::expose" ) @@ -771,11 +1021,23 @@ class TrackInterface( gtk.EventBox ): 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, base = True ): + 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 @@ -783,7 +1045,7 @@ class TrackInterface( gtk.EventBox ): self.dirtyRectToAdd.height = height #print "dirty %d %d %d %d %d %d" % (x, y, width, height, x+width, y+height) - if page == self.curPage: + 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 @@ -792,13 +1054,13 @@ class TrackInterface( gtk.EventBox ): self.screenBufDirtyRect[self.curScreen].height = height else: self.screenBufDirtyRect[self.curScreen] = self.screenBufDirtyRect[self.curScreen].union( self.dirtyRectToAdd ) - self.screenBufResume[self.curScreen] = [0,0] + 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 - elif page == self.screenBufPage[self.preScreen]: + 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 @@ -806,7 +1068,7 @@ class TrackInterface( gtk.EventBox ): self.screenBufDirtyRect[self.preScreen].height = height else: self.screenBufDirtyRect[self.preScreen] = self.screenBufDirtyRect[self.preScreen].union( self.dirtyRectToAdd ) - self.screenBufResume[self.preScreen] = [0,0] + self.screenBufResume[self.preScreen] = [0,0,0] self.screenBufDirty[self.preScreen] = True #self.queue_draw() diff --git a/Edit/TuneInterface.py b/Edit/TuneInterface.py index e6790b9..d1d5481 100644 --- a/Edit/TuneInterface.py +++ b/Edit/TuneInterface.py @@ -58,6 +58,8 @@ class TuneInterface( gtk.EventBox ): self.dragMode = None self.dropAt = -1 + self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK) + self.connect( "size-allocate", self.size_allocated ) self.drawingArea.connect( "expose-event", self.draw ) self.connect( "button-press-event", self.handleButtonPress ) @@ -90,15 +92,23 @@ class TuneInterface( gtk.EventBox ): def handleButtonPress( self, widget, event ): ind = int(event.x-self.pageOffset)//self.pageSpacing if ind >= self.noteDB.getPageCount(): - self.dragMode = self.DRAG_BLOCK + if self.dragMode != self.DRAG_MOVE: + self.dragMode = self.DRAG_BLOCK return if ind < 0: ind = 0 - + self.clickX = event.x id = self.noteDB.getPageByIndex( ind ) - - if event.type == gtk.gdk._2BUTTON_PRESS: # double click -> exclusive select + + if event.state & gtk.gdk.BUTTON2_MASK: + # bring up properties or something + return + + if event.type == gtk.gdk._3BUTTON_PRESS: # triple click -> select all + self.selectAll() + self.owner.displayPage( id ) + elif event.type == gtk.gdk._2BUTTON_PRESS: # double click -> exclusive select self.selectPage( id ) self.owner.displayPage( id ) else: @@ -120,38 +130,54 @@ class TuneInterface( gtk.EventBox ): self.owner.setContext( CONTEXT.PAGE ) def handleButtonRelease( self, widget, event ): - - if self.dragMode == self.DRAG_MOVE: + if self.dragMode == self.DRAG_MOVE \ + and event.button == 1: self.invalidate_rect( 0, 0, self.width, self.height ) # drop head if self.dropAt > 0: after = self.noteDB.getPageByIndex( self.dropAt-1 ) else: after = False self.noteDB.movePages( self.selectedIds, after ) + self.dropAt = -1 - self.dragMode = None + self.dragMode = None def handleMotion( self, widget, event ): - if Config.ModKeys.ctrlDown and (self.dragMode == None or self.dragMode == self.DRAG_MOVE): - self.dropAt = -1 - self.dragMode = self.DRAG_SELECT - - if self.dragMode == self.DRAG_SELECT: # select on drag - ind = int(event.x-self.pageOffset)//self.pageSpacing - if ind < self.noteDB.getPageCount(): self.selectPage( self.noteDB.getPageByIndex(ind), False ) - elif self.dragMode == self.DRAG_DESELECT: # deselect on drag + if event.is_hint: + x, y, state = self.window.get_pointer() + event.x = float(x) + event.y = float(y) + event.state = state + + if event.state & gtk.gdk.BUTTON1_MASK: # clicking + if Config.ModKeys.ctrlDown and (self.dragMode == None or self.dragMode == self.DRAG_MOVE): + self.dropAt = -1 + self.dragMode = self.DRAG_SELECT + + if self.dragMode == self.DRAG_SELECT: # select on drag + ind = int(event.x-self.pageOffset)//self.pageSpacing + if ind < self.noteDB.getPageCount(): self.selectPage( self.noteDB.getPageByIndex(ind), False ) + elif self.dragMode == self.DRAG_DESELECT: # deselect on drag + ind = int(event.x-self.pageOffset)//self.pageSpacing + if ind < self.noteDB.getPageCount(): self.deselectPage( self.noteDB.getPageByIndex(ind) ) + elif self.dragMode == None and abs(self.clickX-event.x) > 20: # drag and drop + self.dragMode = self.DRAG_MOVE + + if self.dragMode == self.DRAG_MOVE: + self.dropAt = int(event.x-self.pageOffset+Config.PAGE_THUMBNAIL_WIDTH_DIV2)//self.pageSpacing + c = self.noteDB.getPageCount() + if self.dropAt > c: self.dropAt = c + self.invalidate_rect( 0, 0, self.width, self.height ) + + else: # hovering ind = int(event.x-self.pageOffset)//self.pageSpacing - if ind < self.noteDB.getPageCount(): self.deselectPage( self.noteDB.getPageByIndex(ind) ) - elif self.dragMode == None and abs(self.clickX-event.x) > 20: # drag and drop - self.dragMode = self.DRAG_MOVE + if 0 <= ind < self.noteDB.getPageCount(): + id = self.noteDB.getPageByIndex(ind) + if id != self.displayedPage: + self.owner.predrawPage( id ) - if self.dragMode == self.DRAG_MOVE: - self.dropAt = int(event.x-self.pageOffset+Config.PAGE_THUMBNAIL_WIDTH_DIV2)//self.pageSpacing - c = self.noteDB.getPageCount() - if self.dropAt > c: self.dropAt = c - self.invalidate_rect( 0, 0, self.width, self.height ) def displayPage( self, id, scroll ): if self.displayedPage == id: return -1 @@ -160,12 +186,12 @@ class TuneInterface( gtk.EventBox ): if id not in self.selectedIds: self.selectPage( id ) - + ind = self.noteDB.getPageIndex( id ) - + startX = self.pageOffset + ind*self.pageSpacing stopX = startX + self.pageSpacing - + self.invalidate_rect( 0, 0, self.width, self.height ) if scroll > startX: scroll = startX @@ -176,7 +202,7 @@ class TuneInterface( gtk.EventBox ): return -1 else: scroll = stopX - self.baseWidth - + return scroll def selectPage( self, id, exclusive = True ): @@ -213,11 +239,14 @@ class TuneInterface( gtk.EventBox ): return True # page removed from the selection + def selectAll( self ): + self.selectedIds = self.noteDB.getTune()[:] + self.invalidate_rect( 0, 0, self.width, self.height ) + def clearSelection( self ): self.selectedIds = [] - self.invalidate_rect( 0, 0, self.width, self.height ) - + def getSelectedIds( self ): return self.selectedIds @@ -249,7 +278,7 @@ class TuneInterface( gtk.EventBox ): # Drawing def draw( self, drawingArea, event ): - + startX = event.area.x startY = event.area.y stopX = event.area.x + event.area.width -- cgit v0.9.1