From fa49d945305be0b6d12630635db5a0fce8bc02b1 Mon Sep 17 00:00:00 2001 From: James Crook Date: Sat, 17 Feb 2018 16:08:38 +0000 Subject: [PATCH] Script work Add Envelope script Add Clips and Boundaries Scripts. Add docimages_oddments.py for the weirder cases. Add make html script. Add more spectral images Add Dark versions of spectrograms (paul's comment) Clean up oddments script. - This commit also adds an option NOT to bring Audacity to the top when screenshotting. -- That's because QuickPlay will disappear if you do -because you lose mouse capture. - Also allow negative window IDs in the drag command. - Registration names of align commands sorted. They had &, : and / in them. - Fixed bug in SetTrackInfo where bFocused was being ignored - Fixed bug where command manager ignored multi-commands. - Commit docimages_oddments.py again, to fix line endings. --- scripts/piped-work/docimages_all.py | 42 ++- scripts/piped-work/docimages_arrange.py | 100 ++++++ .../piped-work/docimages_clip_boundaries.py | 69 ++++ scripts/piped-work/docimages_core.py | 15 + scripts/piped-work/docimages_cut_n_paste.py | 109 +++++++ scripts/piped-work/docimages_envelopes.py | 45 +++ scripts/piped-work/docimages_oddments.py | 50 +++ scripts/piped-work/docimages_spectro.py | 128 +++++++- scripts/piped-work/make_html.py | 17 + scripts/piped-work/pipeclient.py | 296 ++++++++++++++++++ src/commands/CommandManager.cpp | 31 +- src/commands/DragCommand.cpp | 2 +- src/commands/ScreenshotCommand.cpp | 30 +- src/commands/ScreenshotCommand.h | 3 + src/commands/SetEnvelopeCommand.cpp | 2 +- src/commands/SetTrackInfoCommand.cpp | 30 +- src/commands/SetTrackInfoCommand.h | 2 + src/toolbars/ControlToolBar.h | 6 +- src/toolbars/EditToolBar.cpp | 8 +- src/toolbars/EditToolBar.h | 2 + src/toolbars/ToolsToolBar.cpp | 8 +- src/toolbars/ToolsToolBar.h | 4 +- 22 files changed, 951 insertions(+), 48 deletions(-) create mode 100644 scripts/piped-work/docimages_arrange.py create mode 100644 scripts/piped-work/docimages_clip_boundaries.py create mode 100644 scripts/piped-work/docimages_cut_n_paste.py create mode 100644 scripts/piped-work/docimages_envelopes.py create mode 100644 scripts/piped-work/docimages_oddments.py create mode 100644 scripts/piped-work/make_html.py create mode 100644 scripts/piped-work/pipeclient.py diff --git a/scripts/piped-work/docimages_all.py b/scripts/piped-work/docimages_all.py index 1178c2b1b..d41338d3e 100644 --- a/scripts/piped-work/docimages_all.py +++ b/scripts/piped-work/docimages_all.py @@ -5,8 +5,42 @@ # Make sure Audacity is running first and that mod-script-pipe is enabled # before running this script. +import time + + +# records time, name of file and returns contents. +def inner( name ) : + global old_name + global start_time + global results + result = old_name + ' took ' + str( time.time() - start_time ) + results.append( result ) + print( result ) + start_time = time.time() + if not name : + return "" + old_name = name + return open("docimages_" + name + ".py" ).read() + +#initialise timing +start_time = time.time() +old_name = 'startup' +results = [] + +#do the different files... +exec( inner( 'tracks' ) ) +exec( inner( 'labels' ) ) +exec( inner( 'spectro' ) ) +exec( inner( 'after' ) ) +exec( inner( 'envelopes' ) ) +exec( inner( 'cut_n_paste' ) ) +exec( inner( 'clip_boundaries' ) ) +exec( inner( 'oddments' ) ) + +#report on timing. +inner( "" ) + +print( "\n\nSummary:" ) +print( "\n".join( results ) ) + -exec( open("docimages_tracks.py" ).read() ) -exec( open("docimages_labels.py" ).read() ) -exec( open("docimages_spectro.py" ).read() ) -exec( open("docimages_after.py" ).read() ) diff --git a/scripts/piped-work/docimages_arrange.py b/scripts/piped-work/docimages_arrange.py new file mode 100644 index 000000000..dc1f8f539 --- /dev/null +++ b/scripts/piped-work/docimages_arrange.py @@ -0,0 +1,100 @@ +# docimages_arrange.py +# Sends commands to get images for the manual. +# These ones arrange tracks and do alignment. + +# Make sure Audacity is running first and that mod-script-pipe is enabled +# before running this script. + +#load and run the common core. +exec( open("docimages_core.py" ).read() ) + +import time + + +def loadFourColours() : + loadMonoTracks( 4 ) + do( 'SetTrack: Track=0 Name="Claire" Height=60 Color=Color0') + do( 'SetTrack: Track=1 Name="Ann" Height=60 Color=Color1') + do( 'SetTrack: Track=2 Name="Bob" Height=60 Color=Color2') + do( 'SetTrack: Track=3 Name="David" Height=60 Color=Color3') + do( 'SetClip: Track=0 At=1 Start=25') + do( 'SetClip: Track=1 At=1 Start=15') + do( 'SetClip: Track=2 At=1 Start=20') + do( 'SetClip: Track=3 At=1 Start=10') + do( 'Select: First=0 Last=100 Mode=Remove' ) + +def loadFourColoursSelected() : + loadFourColours() + do( 'ZoomOut' ) + do( 'Select: Start=90 End=135 First=0 Last=100' ) + +def blockMoves( name ): + # These are the align commands that move tracks 'en block'. + loadFourColoursSelected() + capture( name + '001.png', 'All_Tracks_Plus' ) + do( 'Align_StarttoZero' ) + capture( name + '002.png', 'All_Tracks_Plus' ) + loadFourColoursSelected() + do( 'Align_StarttoCursorSelectionStart' ) + capture( name + '003.png', 'All_Tracks_Plus' ) + loadFourColoursSelected() + do( 'Align_StarttoSelectionEnd' ) + capture( name + '004.png', 'All_Tracks_Plus' ) + loadFourColoursSelected() + do( 'Align_EndtoCursorSelectionStart' ) + capture( name + '005.png', 'All_Tracks_Plus' ) + loadFourColoursSelected() + do( 'Align_EndtoSelectionEnd' ) + capture( name + '006.png', 'All_Tracks_Plus' ) + +def track_moves( type ) : + loadFourColours() + # Sorting tracks into order + do( 'SetTrack: Track=1 Focused=1') + capture( 'TrackOrder002.png', 'All_Tracks' ) + +def arrange_imagesA() : + loadFourColours() + # Moving tracks up and down. + capture( 'TrackOrder001.png', 'All_Tracks' ) + do( 'SetTrack: Track=1 Focused=1') + # ToTop=0 to show the focus... + capture( 'TrackOrder002.png', 'All_Tracks ToTop=0' ) + do( 'TrackMoveUp' ) + capture( 'TrackUp.png', 'All_Tracks ToTop=0' ) + do( 'TrackMoveDown' ) # undo + do( 'TrackMoveDown' ) + capture( 'TrackDown.png', 'All_Tracks ToTop=0' ) + do( 'TrackMoveTop' ) + capture( 'TrackTop.png', 'All_Tracks ToTop=0' ) + do( 'TrackMoveBottom' ) + capture( 'TrackBottom.png', 'All_Tracks ToTop=0' ) + # Sorting tracks into order + do( 'SortByName') + capture( 'TrackOrder003.png', 'All_Tracks' ) + do( 'SortByTime') + capture( 'TrackOrder004.png', 'All_Tracks' ) + # Aligning tracks + do( 'Select: First=0 Last=100 From=0 To=0') + do( 'Align_AlignTogether' ) + capture( 'TrackAlign001.png', 'All_Tracks' ) + do( 'Align_AlignEndtoEnd' ) + do( 'FitInWindow' ) + capture( 'TrackAlign002.png', 'All_Tracks' ) + + +def arrange_imagesB() : + blockMoves( 'BlockMoves' ) + do( 'MoveSelectionWithTracks') + blockMoves( 'BlockAndCursorMoves' ) + do( 'MoveSelectionWithTracks') + + + +#quickTest() + +arrange_imagesA() +#arrange_imagesB() + + + diff --git a/scripts/piped-work/docimages_clip_boundaries.py b/scripts/piped-work/docimages_clip_boundaries.py new file mode 100644 index 000000000..2f855199f --- /dev/null +++ b/scripts/piped-work/docimages_clip_boundaries.py @@ -0,0 +1,69 @@ +# docimages_clip_boundariess.py +# Sends commands to get images for the manual. +# Images for clip boundary manipulation, per that chapter in the manual +# Make sure Audacity is running first and that mod-script-pipe is enabled +# before running this script. + +#load and run the common core. +exec( open("docimages_core.py" ).read() ) + +def gappyTrack2() : + loadMonoTracks(1) + # A mono track + do( 'Select: Start=0 End=10') + do( 'SplitCut' ) + do( 'Select: Start=30 End=45') + do( 'SplitCut' ) + do( 'Select: Start=90 End=100') + do( 'SplitCut' ) + do( 'Select: Start=120 End=135') + do( 'SplitCut' ) + do( 'Select: Start=0 End=0') + +def clipb_imagesA(): + gappyTrack2() + # clip bound left + capture( 'ClipBounds001.png', 'All_Tracks' ) + do( 'Select: Start=60 End=60') + capture( 'ClipBounds002.png', 'All_Tracks' ) + do( 'SelPrevClipBoundaryToCursor' ) + capture( 'ClipBounds003.png', 'All_Tracks' ) + do( 'SelPrevClipBoundaryToCursor' ) + capture( 'ClipBounds004.png', 'All_Tracks' ) + do( 'SelPrevClipBoundaryToCursor' ) + capture( 'ClipBounds005.png', 'All_Tracks' ) + # clip bound right + gappyTrack2() + capture( 'ClipBounds006.png', 'All_Tracks' ) + do( 'Select: Start=60 End=60') + capture( 'ClipBounds007.png', 'All_Tracks' ) + do( 'SelCursorToNextClipBoundary' ) + capture( 'ClipBounds008.png', 'All_Tracks' ) + do( 'SelCursorToNextClipBoundary' ) + capture( 'ClipBounds009.png', 'All_Tracks' ) + do( 'SelCursorToNextClipBoundary' ) + capture( 'ClipBounds010.png', 'All_Tracks' ) + # clip left + gappyTrack2() + capture( 'ClipBounds011.png', 'All_Tracks' ) + do( 'Select: Start=60 End=60') + capture( 'ClipBounds012.png', 'All_Tracks' ) + do( 'SelPrevClip' ) + capture( 'ClipBounds013.png', 'All_Tracks' ) + do( 'SelPrevClip' ) + capture( 'ClipBounds014.png', 'All_Tracks' ) + # clip right + gappyTrack2() + capture( 'ClipBounds015.png', 'All_Tracks' ) + do( 'Select: Start=60 End=60') + capture( 'ClipBounds016.png', 'All_Tracks' ) + do( 'SelNextClip' ) + capture( 'ClipBounds017.png', 'All_Tracks' ) + do( 'SelNextClip' ) + capture( 'ClipBounds018.png', 'All_Tracks' ) + + + + + +clipb_imagesA() diff --git a/scripts/piped-work/docimages_core.py b/scripts/piped-work/docimages_core.py index d4c389fa8..991a9b509 100644 --- a/scripts/piped-work/docimages_core.py +++ b/scripts/piped-work/docimages_core.py @@ -72,9 +72,13 @@ def quickTest() : def setup() : global path + global sample_path global sample global sample2 + global postfix + postfix = '' path = 'C:\\Users\\James Crook\\' + sample_path ='C:\\Users\\James Crook\\Music\\' sample ='C:\\Users\\James Crook\\Music\\The Poodle Podcast.wav' sample2 ='C:\\Users\\James Crook\\Music\\PoodlePodStereo.wav' startPipes() @@ -89,8 +93,17 @@ def makeWayForTracks( ) : def capture( name, what ) : global path + global postfix + name = name.split( '.png' )[0] + postfix + '.png' do( 'Screenshot: Path="'+path+name+'" CaptureWhat=' + what ) +def loadExample( name ): + global sample_path + makeWayForTracks( ) + do( 'Import2: Filename="'+sample_path+name+'"' ) + do( 'Select: First=0 Last=0 Start=0 End=0') + do( 'FitInWindow' ) + def loadMonoTrack(): global sample makeWayForTracks( ) @@ -112,6 +125,7 @@ def loadMonoTracks( num ) : loadMonoTrack() do( 'SetTrack: Track=0 Name="Foxy Lady"') for i in range( 0, num-1 ): + do( 'Select: First=0 Last=0') do( 'Duplicate' ) do( 'FitInWindow' ) do( 'Select: Start=55 End=70') @@ -121,6 +135,7 @@ def loadStereoTracks( num ) : loadStereoTrack() do( 'SetTrack: Track=0 Name="Foxy Lady"') for i in range( 0, num-1 ): + do( 'Select: First=0 Last=0') do( 'Duplicate' ) do( 'FitInWindow' ) do( 'Select: Start=55 End=70 First=0 Last=' + str(num*2-1) ) diff --git a/scripts/piped-work/docimages_cut_n_paste.py b/scripts/piped-work/docimages_cut_n_paste.py new file mode 100644 index 000000000..722958d64 --- /dev/null +++ b/scripts/piped-work/docimages_cut_n_paste.py @@ -0,0 +1,109 @@ +# docimages_cut_n_pastes.py +# Sends commands to get images for the manual. +# Images for cut_n_paste manipulation. + +# Make sure Audacity is running first and that mod-script-pipe is enabled +# before running this script. + +#load and run the common core. +exec( open("docimages_core.py" ).read() ) + +def gappyTrack() : + loadMonoTracks(1) + # A mono track + do( 'Select: Start=0 End=10') + do( 'SplitCut' ) + do( 'Select: Start=60 End=100') + do( 'SplitCut' ) + do( 'Select: Start=30 End=50') + +def cut_n_paste_imagesA() : + # Split and move + gappyTrack() + capture( 'CutAndPaste001.png', 'All_Tracks' ) + do( 'Split' ) + capture( 'CutAndPaste002.png', 'All_Tracks' ) + do( 'SetClip: At=55 Start=60') + capture( 'CutAndPaste003.png', 'All_Tracks' ) + do( 'Select: Start=0 End=0') + capture( 'CutAndPaste004.png', 'All_Tracks' ) + +def cut_n_paste_imagesB() : + # SplitNew + gappyTrack() + capture( 'CutAndPaste005.png', 'All_Tracks' ) + do( 'SplitNew' ) + capture( 'CutAndPaste006.png', 'All_Tracks' ) + do( 'Select: Start=0 End=0') + capture( 'CutAndPaste007.png', 'All_Tracks' ) + +def cut_n_paste_imagesC() : + # Join + gappyTrack() + do( 'Select: Start=45 End=75') + do( 'Split' ) + do( 'Select: Start=39 End=129') + capture( 'CutAndPaste008.png', 'All_Tracks' ) + do( 'Join' ) + capture( 'CutAndPaste009.png', 'All_Tracks' ) + do( 'Select: Start=0 End=0') + capture( 'CutAndPaste010.png', 'All_Tracks' ) + # Detach at silences + do( 'Select: Start=0 End=150') + do( 'Join' ) + capture( 'CutAndPaste011.png', 'All_Tracks' ) + do( 'Select: Start=0 End=150') + do( 'Disjoin' ) + capture( 'CutAndPaste012.png', 'All_Tracks' ) + do( 'Select: Start=0 End=0') + capture( 'CutAndPaste013.png', 'All_Tracks' ) + +def cut_n_paste_imagesD() : + #Copy and Paste + gappyTrack() + do( 'Select: Start=15 End=20') + do( 'Copy' ) + capture( 'CutAndPaste014.png', 'All_Tracks' ) + #Pasting into + do( 'Select: Start=45 End=45') + capture( 'CutAndPaste015.png', 'All_Tracks' ) + do( 'Paste' ) + capture( 'CutAndPaste016.png', 'All_Tracks' ) + do( 'Select: Start=0 End=0') + capture( 'CutAndPaste017.png', 'All_Tracks' ) + + gappyTrack() + do( 'Select: Start=15 End=20') + do( 'Copy' ) + #Pasting before + do( 'Select: Start=5 End=5') + capture( 'CutAndPaste018.png', 'All_Tracks' ) + do( 'Paste' ) + capture( 'CutAndPaste019.png', 'All_Tracks' ) + do( 'Select: Start=0 End=0') + capture( 'CutAndPaste030.png', 'All_Tracks' ) + + #pasting before with no movement (cheat) + do( 'Select: Start=11 End=16') + do( 'Cut' ) + do( 'Select: Start=0 End=0') + capture( 'CutAndPaste031.png', 'All_Tracks' ) + do( 'Select: Start=5 End=10') + capture( 'CutAndPaste032.png', 'All_Tracks' ) + do( 'Select: Start=0 End=0') + +def cut_n_paste_imagesE() : + # Duplicate + gappyTrack() + capture( 'CutAndPaste033.png', 'All_Tracks' ) + do( 'Duplicate' ) + capture( 'CutAndPaste034.png', 'All_Tracks' ) + do( 'Select: Start=0 End=0') + capture( 'CutAndPaste035.png', 'All_Tracks' ) + + +cut_n_paste_imagesA() +cut_n_paste_imagesB() +cut_n_paste_imagesC() +cut_n_paste_imagesD() +cut_n_paste_imagesE() diff --git a/scripts/piped-work/docimages_envelopes.py b/scripts/piped-work/docimages_envelopes.py new file mode 100644 index 000000000..41963f30e --- /dev/null +++ b/scripts/piped-work/docimages_envelopes.py @@ -0,0 +1,45 @@ +# docimages_envelopes.py +# Sends commands to get images for the manual. +# Images for envelope manipulation. + +# Make sure Audacity is running first and that mod-script-pipe is enabled +# before running this script. + +#load and run the common core. +exec( open("docimages_core.py" ).read() ) + + + +def env_images() : + loadMonoTracks(1) + do( 'Select: Start=0 End=0') + # A mono track + capture( 'Envelope001.png', 'All_Tracks' ) + do( 'EnvelopeTool' ) + # As spectrogram. + capture( 'Envelope002.png', 'All_Tracks' ) + do( 'SetEnvelope: Time=55 Value=0.9'); + capture( 'Envelope003.png', 'All_Tracks' ) + do( 'SetEnvelope: Time=120 Value=0.4'); + capture( 'Envelope004.png', 'All_Tracks' ) + do( 'SetEnvelope: Time=125 Value=0.9'); + capture( 'Envelope005.png', 'All_Tracks' ) + do( 'SetEnvelope: Time=45 Value=0.85'); + capture( 'Envelope006.png', 'All_Tracks' ) + do( 'SetEnvelope: Time=25 Value=1.85'); + capture( 'Envelope007.png', 'All_Tracks' ) + do( 'SetEnvelope: Time=0 Value=0.85'); + capture( 'Envelope008.png', 'All_Tracks' ) + do( 'SetTrack: VZoom=Times2' ) + capture( 'Envelope009.png', 'All_Tracks' ) + do( 'SetTrack: VZoom=HalfWave' ) + capture( 'Envelope010.png', 'All_Tracks' ) + do( 'SelectTool' ) + capture( 'Envelope011.png', 'All_Tracks' ) + do( 'SetTrack: VZoom=Reset' ) + capture( 'Envelope012.png', 'All_Tracks' ) + + + +env_images() + diff --git a/scripts/piped-work/docimages_oddments.py b/scripts/piped-work/docimages_oddments.py new file mode 100644 index 000000000..341f032c7 --- /dev/null +++ b/scripts/piped-work/docimages_oddments.py @@ -0,0 +1,50 @@ +# docimages_oddments.py +# Sends commands to get images for the manual. +# Image oddments that don't fit the other categories. + +# Make sure Audacity is running first and that mod-script-pipe is enabled +# before running this script. +# Historically this file has had peculiar problems with line endings. + +#load and run the common core. +exec( open("docimages_core.py" ).read() ) + +import time + +def oddments_imagesA(): + for name in ["Select","Envelope","Draw","Zoom","TimeShift","Multi"] : + do( name + "Tool" ) + capture( name + "Tool.png" , 'Tools' ); + #A track is needed for the buttons to be active. + loadMonoTracks(1) + do( 'SetPreference: Name="/GUI/Theme" Value="high-contrast"') + for id in range( 11000, 11006 ): + do( "Drag: Id="+str( id) + " FromX=10 FromY=10" ) + capture( "Button" + str(id) +"Hover.png", "Transport" ) + do( "Drag: Id="+str( id) + " FromX=1000 FromY=10" ) + for id in range( 11200, 11206 ): + do( "Drag: Id="+str( id) + " FromX=10 FromY=10" ) + capture( "Button" + str(id) +"Hover.png", "Tools" ) + do( "Drag: Id="+str( id) + " FromX=1000 FromY=10" ) + for id in range( 11300, 11312 ): + do( "Drag: Id="+str( id) + " FromX=10 FromY=10" ) + capture( "Button" + str(id) +"Hover.png", "Edit" ) + do( "Drag: Id="+str( id) + " FromX=1000 FromY=10" ) + do( 'SetPreference: Name="/GUI/Theme" Value="light"') + + +def oddments_imagesB(): + loadMonoTracks(1) + #Bring window to top now + capture( "Dummy.png", "Ruler" ) + #We hope nothing else gets focus before the next capture, so + #that we actually get to see something! + do( "Drag: Window=Timeline FromX=200 FromY=10 ToX=600 ToY=10" ) + time.sleep(3.0) + #Disable bringing to top, so as not to destroy quick play. + capture( "QuikPlay001.png", "First_Track_Plus ToTop=0" ) + +oddments_imagesA() +oddments_imagesB() + + diff --git a/scripts/piped-work/docimages_spectro.py b/scripts/piped-work/docimages_spectro.py index d24df4e19..7d734c8cf 100644 --- a/scripts/piped-work/docimages_spectro.py +++ b/scripts/piped-work/docimages_spectro.py @@ -8,6 +8,8 @@ #load and run the common core. exec( open("docimages_core.py" ).read() ) +import math +import time # 11 2KHz tones, of decreasing amplitude. @@ -32,29 +34,139 @@ def makeStepper(): do( 'Select: Start=0 End=0') -def spectro_image1and2() : +def spectro_imagesA() : loadStereoTracks(1) # A stereo track capture( 'Spectral001.png', 'First_Track' ) # As spectrogram. do( 'SetTrack: Track=0 Display=Spectrogram') do( 'Select: Start=55 End=70 First=0 Last=1') - capture( 'Spectral002.png', 'First_Track' ) + capture( 'Spectral002.png', 'All_Tracks' ) # Half spectrogram, half wave. do( 'SetTrack: Channel=1 Display=Waveform') - capture( 'MixedMode.png', 'First_Track' ) + capture( 'MixedMode.png', 'All_Tracks' ) -def spectro_image3and4(): +def spectro_imagesB(): makeStepper(); # Stepper tone, viewed in dB. do( 'SetTrack: Scale=dB') - capture( 'Spectral003.png', 'First_Two_Tracks' ) + capture( 'Spectral003.png', 'All_Tracks' ) # As spectrogram. do( 'SetTrack: Display=Spectrogram') - capture( 'Spectral004.png', 'First_Two_Tracks' ) + capture( 'Spectral004.png', 'All_Tracks' ) + +def spectro_imagesC(): + # A chirp and the word 'Audacity' + loadExample( 'AudacitySpectral.wav' ) + capture( 'Spectral005.png', 'All_Tracks' ) + do( 'SetTrack: Scale=dB') + capture( 'Spectral006.png', 'All_Tracks' ) + do( 'SetTrack: Display=Spectrogram') + capture( 'Spectral007.png', 'All_Tracks' ) + do( 'Select: Start=1.5 End=2.1 Low=3000 High=6000') + capture( 'Spectral008.png', 'All_Tracks' ) + do( 'Select: Start=1.1 End=2.5' ) + do( 'ZoomSel' ) + do( 'Select: Start=1.5 End=2.1 Low=3000 High=6000') + do( 'SetTrack: Height=400' ) + multiWindow( "SpectralVocal" ) + +def setWindow( name, value ): + do( 'SetTrack: SpecPrefs=1 Name="Window Size '+value+'"' ) + do( 'SetPreference: Name="/Spectrum/FFTSize" Reload=1 Value='+value ) + do( 'SetTrack: Track=0 Display=Spectrogram' ) + capture( name + postfix + value + '.png', 'All_Tracks' ) + + +def multiWindow( name ) : + setWindow( name, "256" ) + setWindow( name, "512" ) + setWindow( name, "2048" ) + setWindow( name, "4096" ) + setWindow( name, "8192" ) + setWindow( name, "1024" ) # done last so we restore the default. + +def spectro_imagesD(): + makeWayForTracks() + do( 'NewMonoTrack' ) + do( 'Select: Start=0.7 End=1.3') + do( 'Silence' ) #Just so we can watch. + do( 'Select: Start=0.8 End=1.2') + do( 'ZoomSel') + do( 'Select: Start=0.7 End=1.3') + do( 'Tone: Frequency=3000 Amplitude=0.8' ) + do( 'Select: Start=0.99 End=0.99005' ) + do( 'Tone: Frequency=12000 Amplitude=0.9' ) + do( 'Select: Start=1.01 End=1.01005' ) + do( 'Tone: Frequency=12000 Amplitude=0.9' ) + do( 'Select: Start=0 End=0' ) + multiWindow( 'SpectralAt' ) + +def spectro_imagesE(): + makeWayForTracks() + do( 'NewMonoTrack' ) + do( 'Select: Start=0 End=1.2') + do( 'ZoomSel') + do( 'Pluck' ) + do( 'Select: Start=0.1 End=1.0') + do( 'ZoomSel') + multiWindow( 'SpectralNote' ) + +def makeScale( start, end, count ) : + a = math.exp( math.log( end/start) /(count-1 )) + makeWayForTracks() + do( 'NewMonoTrack' ) + do( 'Select: Start=0 End=' + str( count/10 )) + do( 'Silence' ) #To see it happen... + do( 'SetTrack: Track=0 SpecPrefs=1 Display=Spectrogram' ) + do( 'ZoomSel' ) + #do( 'SetTrack: Track=0 Display=Spectrogram' ) + for i in range( 0 , count , 2 ): + note = start * ( a ** i ) + #print( note ) + do( 'Select: Start=' + str( i / 10) + ' End=' + str( (i+1)/10 )) + do( 'Tone: Frequency=' +str( note ) ) + do( 'Select: Start=' + str( i / 10) + ' End=' + str( ( i / 10) +0.05)) + do( 'FadeIn' ) + do( 'Select: Start=' + str( (i+1) / 10 -0.05) + ' End=' + str( (i+1) / 10 )) + do( 'FadeOut' ) + do( 'FitInWindow' ) + #do( 'Select: Start=0 End=' + str( count/10 )) + #do( 'Join' ) + do( 'Select: Start=0 End=0') + + +def spectro_imagesF(): + makeScale( 200, 4000, 100 ) + do( 'SetTrack: Track=0 Display=Spectrogram' ) + capture( 'ScaleLin.png', 'All_Tracks' ) + do( 'SetPreference: Name=/Spectrum/ScaleType Value=1 Reload=1') + capture( 'ScaleLog.png', 'All_Tracks' ) + do( 'SetPreference: Name=/Spectrum/ScaleType Value=0 Reload=1') + + #quickTest() -spectro_image1and2() -spectro_image3and4() +do( 'SetPreference: Name="/GUI/Theme" Value=light Reload=1' ) + +postfix = '' +spectro_imagesA() +spectro_imagesB() +spectro_imagesC() +spectro_imagesD() +spectro_imagesE() +spectro_imagesF() + +do( 'SetPreference: Name="/GUI/Theme" Value=dark Reload=1' ) + +postfix = 'Dark' +spectro_imagesA() +spectro_imagesB() +spectro_imagesC() +spectro_imagesD() +spectro_imagesE() +spectro_imagesF() + +do( 'SetPreference: Name="/GUI/Theme" Value=light Reload=1' ) diff --git a/scripts/piped-work/make_html.py b/scripts/piped-work/make_html.py new file mode 100644 index 000000000..2fca833d7 --- /dev/null +++ b/scripts/piped-work/make_html.py @@ -0,0 +1,17 @@ +# Takes an image file directory and makes the web page that lists them. +# They are listed in creation date/time order. + +import glob +import os + +def getFiles() : + files = glob.glob("C:\\OpenSourceGit\\AudacityTeamTools\\wit-html\\auto_images\\*.png") + files.sort(key=os.path.getmtime) + return [ os.path.basename( name ) for name in files ] + #print("\n".join(files)) + +def oneItem( name ) : + return "
"+name+"
" + +files = getFiles() +print( "\n".join( [ oneItem(name) for name in files ] ) ) diff --git a/scripts/piped-work/pipeclient.py b/scripts/piped-work/pipeclient.py new file mode 100644 index 000000000..3cf939501 --- /dev/null +++ b/scripts/piped-work/pipeclient.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Automate Audacity via mod-script-pipe. + +Pipe Client may be used as a command-line script to send commands to +Audacity via the mod-script-pipe interface, or loaded as a module. +Requires Python 2.7 or later. Python 3 recommended. + +====================== +Command Line Interface +====================== + + usage: pipeclient.py [-h] [-t] [-s ] [-d] + +Arguments +--------- + -h,--help: optional + show short help and exit + -t, --timeout: float, optional + timeout for reply in seconds (default: 10) + -s, --show-time: bool, optional + show command execution time (default: True) + -d, --docs: optional + show this documentation and exit + +Example +------- + $ python3 pipeclient.py -t 20 -s False + + Launches command line interface with 20 second time-out for + returned message, and don't show the execution time. + + When prompted, enter the command to send (not quoted), or 'Q' to quit. + + $ Enter command or 'Q' to quit: GetInfo: Type=Tracks Format=LISP + +============ +Module Usage +============ + +Note that on a critical error (such as broken pipe), the module just exits. +If a more graceful shutdown is required, replace the sys.exit()'s with +exceptions. + +Example +------- + + # Import the module: + >>> import pipeclient + + # Create a client instance: + >>> client = pipeclient.PipeClient() + + # Send a command: + >>> client.write("Command", timer=True) + + # Read the last reply: + >>> print(client.read()) + +See Also +-------- +PipeClient.write : Write a command to _write_pipe. +PipeClient.read : Read Audacity's reply from pipe. + +Copyright Steve Daulton 2018 +Released under terms of the GNU General Public License version 2: + + +""" + +import os +import sys +import threading +import time +import errno +import argparse + + +if sys.version_info[0] < 3 and sys.version_info[1] < 7: + sys.exit('PipeClient Error: Python 2.7 or later required') + +# Platform specific constants +if sys.platform == 'win32': + WRITE_NAME = '\\\\.\\pipe\\ToSrvPipe' + READ_NAME = '\\\\.\\pipe\\FromSrvPipe' + EOL = '\r\n\0' +else: + # Linux or Mac + PIPE_BASE = '/tmp/audacity_script_pipe.' + WRITE_NAME = PIPE_BASE + 'to.' + str(os.getuid()) + READ_NAME = PIPE_BASE + 'from.' + str(os.getuid()) + EOL = '\n' + + +class PipeClient(object): + """Write / read client access to Audacity via named pipes. + + Normally there should be just one instance of this class. If + more instances are created, they all share the same state. + + __init__ calls _write_thread_start() and _read_thread_start() on + first instantiation. + + Parameters + ---------- + None + + Attributes + ---------- + reader_pipe_broken : event object + Set if pipe reader fails. Audacity may have crashed + reply_ready : event object + flag cleared when command sent and set when response received + timer : bool + When true, time the command execution (default False) + reply : string + message received when Audacity completes the command + + See Also + -------- + write : Write a command to _write_pipe. + read : Read Audacity's reply from pipe. + + """ + + reader_pipe_broken = threading.Event() + reply_ready = threading.Event() + + _shared_state = {} + + def __new__(cls, *p, **k): + self = object.__new__(cls, *p, **k) + self.__dict__ = cls._shared_state + return self + + def __init__(self): + self.timer = False + self._start_time = 0 + self._write_pipe = None + self.reply = '' + if not self._write_pipe: + self._write_thread_start() + self._read_thread_start() + + def _write_thread_start(self): + """Start _write_pipe thread""" + # Pipe is opened in a new thread so that we don't + # freeze if Audacity is not running. + write_thread = threading.Thread(target=self._write_pipe_open) + write_thread.daemon = True + write_thread.start() + # Allow a little time for connection to be made. + time.sleep(0.1) + if not self._write_pipe: + sys.exit('PipeClientError: Write pipe cannot be opened.') + + def _write_pipe_open(self): + """Open _write_pipe.""" + self._write_pipe = open(WRITE_NAME, 'w') + + def _read_thread_start(self): + """Start read_pipe thread.""" + read_thread = threading.Thread(target=self._reader) + read_thread.daemon = True + read_thread.start() + + def write(self, command, timer=False): + """Write a command to _write_pipe. + + Parameters + ---------- + command : string + The command to send to Audacity + timer : bool, optional + If true, time the execution of the command + + Example + ------- + write("GetInfo: Type=Labels", timer=True): + + """ + self.timer = timer + print('Sending command:', command) + self._write_pipe.write(command + EOL) + # Check that read pipe is alive + if PipeClient.reader_pipe_broken.isSet(): + sys.exit('PipeClient: Read-pipe error.') + try: + self._write_pipe.flush() + if self.timer: + self._start_time = time.time() + self.reply = '' + PipeClient.reply_ready.clear() + except IOError as err: + if err.errno == errno.EPIPE: + sys.exit('PipeClient: Write-pipe error.') + else: + raise + + def _reader(self): + """Read FIFO in worker thread.""" + # Thread will wait at this read until it connects. + # Connection should occur as soon as _write_pipe has connected. + read_pipe = open(READ_NAME, 'r') + message = '' + while True: + line = read_pipe.readline() + # Stop timer as soon as we get first line of response. + stop_time = time.time() + while line != EOL: + message += line + line = read_pipe.readline() + if line == '': + # No data in read_pipe indicates that the pipe is broken + # (Audacity may have crashed). + PipeClient.reader_pipe_broken.set() + if self.timer: + xtime = (stop_time - self._start_time) * 1000 + message += 'Execution time: {0:.2f}ms'.format(xtime) + self.reply = message + PipeClient.reply_ready.set() + message = '' + read_pipe.close() + + def read(self): + """Read Audacity's reply from pipe. + + Returns + ------- + string + The reply from the last command sent to Audacity, or null string + if reply not received. Null string usually indicates that Audacity + is still processing the last command. + + """ + if not PipeClient.reply_ready.isSet(): + return '' + else: + return self.reply + + +def bool_from_string(strval): + """Return boolean value from string""" + if strval.lower() in ('true', 't', '1', 'yes', 'y'): + return True + elif strval.lower() in ('false', 'f', '0', 'no', 'n'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + +def main(): + """Interactive command-line for PipeClient""" + + parser = argparse.ArgumentParser() + parser.add_argument('-t', '--timeout', type=float, metavar='', default=10, + help="timeout for reply in seconds (default: 10") + parser.add_argument('-s', '--show-time', metavar='True/False', + nargs='?', type=bool_from_string, + const='t', default='t', dest='show', + help='show command execution time (default: True)') + parser.add_argument('-d', '--docs', action='store_true', + help='show documentation and exit') + args = parser.parse_args() + + if args.docs: + print(__doc__) + sys.exit(0) + + client = PipeClient() + while True: + reply = '' + if sys.version_info[0] < 3: + #pylint: disable=undefined-variable + message = raw_input("\nEnter command or 'Q' to quit: ") + else: + message = input( #pylint: disable=bad-builtin + "\nEnter command or 'Q' to quit: ") + start = time.time() + if message.upper() == 'Q': + sys.exit(0) + elif message == '': + pass + else: + client.write(message, timer=args.show) + while reply == '': + time.sleep(0.1) # allow time for reply + if time.time() - start > args.timeout: + reply = 'PipeClient: Reply timed-out.' + else: + reply = client.read() + print(reply) + + +if __name__ == '__main__': + main() diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 9d1bb8797..6e833f97a 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -947,7 +947,7 @@ CommandListEntry *CommandManager::NewIdentifier(const wxString & name, count, {}); } -CommandListEntry *CommandManager::NewIdentifier(const wxString & name, +CommandListEntry *CommandManager::NewIdentifier(const wxString & nameIn, const wxString & label, const wxString & accel, wxMenu *menu, @@ -958,6 +958,8 @@ CommandListEntry *CommandManager::NewIdentifier(const wxString & name, int count, const CommandParameter ¶meter) { + wxString name = nameIn; + // If we have the identifier already, reuse it. CommandListEntry *prev = mCommandNameHash[name]; if (!prev); @@ -975,6 +977,18 @@ CommandListEntry *CommandManager::NewIdentifier(const wxString & name, labelPrefix = mSubMenuList.back()->name; } + // For key bindings for commands with a list, such as align, + // the name in prefs is the category name plus the effect name. + // This feature is not used for built-in effects. + if (multi) { + // The name needs to be clean for use by automation. + wxString cleanedName = wxString::Format(wxT("%s_%s"), name, label); + cleanedName.Replace( "/", "" ); + cleanedName.Replace( "&", "" ); + cleanedName.Replace( " ", "" ); + name = cleanedName; + } + // wxMac 2.5 and higher will do special things with the // Preferences, Exit (Quit), and About menu items, // if we give them the right IDs. @@ -1024,13 +1038,6 @@ CommandListEntry *CommandManager::NewIdentifier(const wxString & name, if( mMaxListOnly.Index( entry->key ) !=-1) entry->key = wxT(""); - - // For key bindings for commands with a list, such as effects, - // the name in prefs is the category name plus the effect name. - if (multi) { - entry->name = wxString::Format(wxT("%s:%s"), name, label); - } - // Key from preferences overridse the default key given gPrefs->SetPath(wxT("/NewKeys")); if (gPrefs->HasEntry(entry->name)) { @@ -1551,6 +1558,14 @@ bool CommandManager::HandleTextualCommand(const wxString & Str, const CommandCon return HandleCommandEntry( entry.get(), flags, mask); } } + else + { + // Handle multis too... + if( Str.IsSameAs( entry->name, false ) ) + { + return HandleCommandEntry( entry.get(), flags, mask); + } + } } // Not one of the singleton commands. // We could/should try all the list-style commands. diff --git a/src/commands/DragCommand.cpp b/src/commands/DragCommand.cpp index cfa39c8fe..15d223ea1 100644 --- a/src/commands/DragCommand.cpp +++ b/src/commands/DragCommand.cpp @@ -49,7 +49,7 @@ static const wxString kCoordTypeStrings[nCoordTypes] = bool DragCommand::DefineParams( ShuttleParams & S ){ wxArrayString coords( nCoordTypes, kCoordTypeStrings ); - S.OptionalN( bHasId ).Define( mId, wxT("Id"), 0.0, 11000.0, 1000000.0); + S.OptionalN( bHasId ).Define( mId, wxT("Id"), 11000.0, -100000.0, 1000000.0); S.OptionalY( bHasWinName ).Define( mWinName, wxT("Window"), "Timeline"); S.OptionalY( bHasFromX ).Define( mFromX, wxT("FromX"), 200.0, 0.0, 1000000.0); S.OptionalY( bHasFromY ).Define( mFromY, wxT("FromY"), 10.0, 0.0, 1000000.0); diff --git a/src/commands/ScreenshotCommand.cpp b/src/commands/ScreenshotCommand.cpp index dcbd95263..a8ff7c6f1 100644 --- a/src/commands/ScreenshotCommand.cpp +++ b/src/commands/ScreenshotCommand.cpp @@ -139,9 +139,10 @@ static const wxString kBackgroundStrings[nBackgrounds] = bool ScreenshotCommand::DefineParams( ShuttleParams & S ){ wxArrayString whats(nCaptureWhats, kCaptureWhatStrings); wxArrayString backs(nBackgrounds, kBackgroundStrings); - S.Define( mPath, wxT("Path"), wxT("")); - S.DefineEnum( mWhat, wxT("CaptureWhat"), wxT("Window"), whats ); - S.OptionalN(bHasBackground).DefineEnum( mBack, wxT("Background"), wxT("None"), backs ); + S.Define( mPath, wxT("Path"), wxT("")); + S.DefineEnum( mWhat, wxT("CaptureWhat"), wxT("Window"), whats ); + S.OptionalN(bHasBackground).DefineEnum( mBack, wxT("Background"), wxT("None"), backs ); + S.OptionalN(bHasBringToTop).Define( mbBringToTop, wxT("ToTop"), true ); return true; }; @@ -153,9 +154,10 @@ void ScreenshotCommand::PopulateOrExchange(ShuttleGui & S) S.StartMultiColumn(2, wxALIGN_CENTER); { - S.TieTextBox( _("Path:"), mPath); - S.TieChoice( _("Capture What:"), mWhat, &whats); - S.TieChoice( _("Background:"), mBack, &backs); + S.TieTextBox( _("Path:"), mPath); + S.TieChoice( _("Capture What:"), mWhat, &whats); + S.TieChoice( _("Background:"), mBack, &backs); + S.TieCheckBox( _("Bring To Top:"), mbBringToTop); } S.EndMultiColumn(); } @@ -247,16 +249,18 @@ bool ScreenshotCommand::Capture( int height = r.height; if( r.width == 0 ) return false; - if (window) { - if (window->IsTopLevel()) { - window->Raise(); - } - else { - wxGetTopLevelParent(window)->Raise(); + if (window ) { + wxWindow * win = window; + wxTopLevelWindow * top_win= nullptr; + if( !window->IsTopLevel()) + win = wxGetTopLevelParent(window); + top_win = dynamic_cast( win ); + if( (!bHasBringToTop || mbBringToTop) && (!top_win || !top_win->IsActive()) ){ + win->Raise(); + Yield(); } } - Yield(); int screenW, screenH; wxDisplaySize(&screenW, &screenH); diff --git a/src/commands/ScreenshotCommand.h b/src/commands/ScreenshotCommand.h index 53ad6f41f..c481a4c57 100644 --- a/src/commands/ScreenshotCommand.h +++ b/src/commands/ScreenshotCommand.h @@ -33,6 +33,7 @@ class CommandContext; class ScreenshotCommand : public AudacityCommand { public: + ScreenshotCommand(){ mbBringToTop=true;}; // CommandDefinitionInterface overrides wxString GetSymbol() override {return SCREENSHOT_PLUGIN_SYMBOL;}; wxString GetDescription() override {return _("Takes screenshots.");}; @@ -46,7 +47,9 @@ private: wxString mWhat; wxString mBack; wxString mPath; + bool mbBringToTop; bool bHasBackground; + bool bHasBringToTop; friend class ScreenshotCommand; friend class ScreenFrame; diff --git a/src/commands/SetEnvelopeCommand.cpp b/src/commands/SetEnvelopeCommand.cpp index 0cdc20ca0..56854fd49 100644 --- a/src/commands/SetEnvelopeCommand.cpp +++ b/src/commands/SetEnvelopeCommand.cpp @@ -35,7 +35,7 @@ bool SetEnvelopeCommand::DefineParams( ShuttleParams & S ){ S.OptionalY( bHasTrackIndex ).Define( mTrackIndex, wxT("Track"), 0, 0, 100 ); S.OptionalN( bHasChannelIndex ).Define( mChannelIndex, wxT("Channel"), 0, 0, 100 ); S.OptionalY( bHasT ).Define( mT, wxT("Time"), 0.0, 0.0, 100000.0); - S.OptionalY( bHasV ).Define( mV, wxT("Value"), 0.0, 0.0, 2.0); + S.OptionalY( bHasV ).Define( mV, wxT("Value"), 1.0, 0.0, 2.0); S.OptionalN( bHasDelete ).Define( mbDelete, wxT("Delete"), false ); return true; }; diff --git a/src/commands/SetTrackInfoCommand.cpp b/src/commands/SetTrackInfoCommand.cpp index b92276aa2..acd20167b 100644 --- a/src/commands/SetTrackInfoCommand.cpp +++ b/src/commands/SetTrackInfoCommand.cpp @@ -77,10 +77,27 @@ static const wxString kScaleTypeStrings[nScaleTypes] = }; +enum kZoomTypes +{ + kReset, + kTimes2, + kHalfWave, + nZoomTypes +}; + +static const wxString kZoomTypeStrings[nZoomTypes] = +{ + XO("Reset"), + XO("Times2"), + XO("HalfWave"), +}; + + bool SetTrackCommand::DefineParams( ShuttleParams & S ){ wxArrayString colours( nColours, kColourStrings ); wxArrayString displays( nDisplayTypes, kDisplayTypeStrings ); wxArrayString scales( nScaleTypes, kScaleTypeStrings ); + wxArrayString vzooms( nZoomTypes, kZoomTypeStrings ); S.OptionalY( bHasTrackIndex ).Define( mTrackIndex, wxT("Track"), 0, 0, 100 ); S.OptionalN( bHasChannelIndex ).Define( mChannelIndex, wxT("Channel"), 0, 0, 100 ); @@ -92,6 +109,7 @@ bool SetTrackCommand::DefineParams( ShuttleParams & S ){ S.OptionalN( bHasScaleType ).DefineEnum( mScaleType, wxT("Scale"), kLinear, scales ); S.OptionalN( bHasColour ).DefineEnum( mColour, wxT("Color"), kColour0, colours ); S.OptionalN( bHasUseSpecPrefs ).Define( bUseSpecPrefs, wxT("SpecPrefs"), false ); + S.OptionalN( bHasVZoom ).DefineEnum( mVZoom, wxT("VZoom"), kReset, vzooms ); S.OptionalN( bHasSpectralSelect ).Define( bSpectralSelect, wxT("SpectralSel"),true ); S.OptionalN( bHasGrayScale ).Define( bGrayScale, wxT("GrayScale"), false ); @@ -108,6 +126,7 @@ void SetTrackCommand::PopulateOrExchange(ShuttleGui & S) wxArrayString colours( nColours, kColourStrings ); wxArrayString displays( nDisplayTypes, kDisplayTypeStrings ); wxArrayString scales( nScaleTypes, kScaleTypeStrings ); + wxArrayString vzooms( nZoomTypes, kZoomTypeStrings ); S.AddSpace(0, 5); @@ -122,6 +141,7 @@ void SetTrackCommand::PopulateOrExchange(ShuttleGui & S) S.Optional( bHasColour ).TieChoice( _("Colour:"), mColour, &colours ); S.Optional( bHasDisplayType ).TieChoice( _("Display:"), mDisplayType, &displays ); S.Optional( bHasScaleType ).TieChoice( _("Scale:"), mScaleType, &scales ); + S.Optional( bHasVZoom ).TieChoice( _("VZoom:"), mVZoom, &vzooms ); } S.EndMultiColumn(); S.StartMultiColumn(2, wxALIGN_CENTER); @@ -188,11 +208,19 @@ bool SetTrackCommand::Apply(const CommandContext & context) wt->GetSpectrogramSettings().spectralSelection = bSpectralSelect; if( wt && bHasGrayScale ) wt->GetSpectrogramSettings().isGrayscale = bGrayScale; + if( wt && bHasVZoom ){ + switch( mVZoom ){ + default: + case kReset: wt->SetDisplayBounds(-1,1); break; + case kTimes2: wt->SetDisplayBounds(-2,2); break; + case kHalfWave: wt->SetDisplayBounds(0,1); break; + } + } // These ones don't make sense on the second channel of a stereo track. if( !bIsSecondChannel ){ if( bHasSelected ) t->SetSelected(bSelected); - if( bHasFocused ) + if( bHasFocused && bFocused) { TrackPanel *panel = context.GetProject()->GetTrackPanel(); panel->SetFocusedTrack( t ); diff --git a/src/commands/SetTrackInfoCommand.h b/src/commands/SetTrackInfoCommand.h index 6bffc26ad..d673d588b 100644 --- a/src/commands/SetTrackInfoCommand.h +++ b/src/commands/SetTrackInfoCommand.h @@ -47,6 +47,7 @@ public: int mHeight; int mDisplayType; int mScaleType; + int mVZoom; bool bUseSpecPrefs; bool bSpectralSelect; bool bGrayScale; @@ -65,6 +66,7 @@ public: bool bHasHeight; bool bHasDisplayType; bool bHasScaleType; + bool bHasVZoom; bool bHasUseSpecPrefs; bool bHasSpectralSelect; bool bHasGrayScale; diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index 897871ee1..059bc6b0e 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -141,12 +141,12 @@ class ControlToolBar final : public ToolBar { enum { - ID_PLAY_BUTTON = 11000, - ID_RECORD_BUTTON, - ID_PAUSE_BUTTON, + ID_PAUSE_BUTTON = 11000, + ID_PLAY_BUTTON, ID_STOP_BUTTON, ID_FF_BUTTON, ID_REW_BUTTON, + ID_RECORD_BUTTON, BUTTON_COUNT, }; diff --git a/src/toolbars/EditToolBar.cpp b/src/toolbars/EditToolBar.cpp index a2ae1b27a..0c0024965 100644 --- a/src/toolbars/EditToolBar.cpp +++ b/src/toolbars/EditToolBar.cpp @@ -69,8 +69,8 @@ const int SEPARATOR_WIDTH = 14; //////////////////////////////////////////////////////////// BEGIN_EVENT_TABLE( EditToolBar, ToolBar ) - EVT_COMMAND_RANGE( ETBCutID, - ETBCutID + ETBNumButtons - 1, + EVT_COMMAND_RANGE( ETBCutID+first_ETB_ID, + ETBCutID+first_ETB_ID + ETBNumButtons - 1, wxEVT_COMMAND_BUTTON_CLICKED, EditToolBar::OnButton ) END_EVENT_TABLE() @@ -110,7 +110,7 @@ AButton *EditToolBar::AddButton( r = ToolBar::MakeButton(pBar, bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredUpHiliteSmall, bmpRecoloredHiliteSmall, eEnabledUp, eEnabledDown, eDisabled, - wxWindowID(id), + wxWindowID(id+first_ETB_ID), wxDefaultPosition, toggle, theTheme.ImageSize( bmpRecoloredUpSmall )); @@ -291,7 +291,7 @@ void EditToolBar::ForAllButtons(int Action) void EditToolBar::OnButton(wxCommandEvent &event) { - int id = event.GetId(); + int id = event.GetId()-first_ETB_ID; // Be sure the pop-up happens even if there are exceptions, except for buttons which toggle. auto cleanup = finally( [&] { mButtons[id]->InteractionOver();}); diff --git a/src/toolbars/EditToolBar.h b/src/toolbars/EditToolBar.h index 8eba5a155..55dc854be 100644 --- a/src/toolbars/EditToolBar.h +++ b/src/toolbars/EditToolBar.h @@ -62,6 +62,8 @@ enum { ETBNumButtons }; +const int first_ETB_ID = 11300; + // flags so 1,2,4,8 etc. enum { ETBActTooltips = 1, diff --git a/src/toolbars/ToolsToolBar.cpp b/src/toolbars/ToolsToolBar.cpp index 9de3061fc..f59e73a5e 100644 --- a/src/toolbars/ToolsToolBar.cpp +++ b/src/toolbars/ToolsToolBar.cpp @@ -67,8 +67,8 @@ IMPLEMENT_CLASS(ToolsToolBar, ToolBar); //////////////////////////////////////////////////////////// BEGIN_EVENT_TABLE(ToolsToolBar, ToolBar) - EVT_COMMAND_RANGE(firstTool, - lastTool, + EVT_COMMAND_RANGE(firstTool+FirstToolID, + lastTool+FirstToolID, wxEVT_COMMAND_BUTTON_CLICKED, ToolsToolBar::OnTool) END_EVENT_TABLE() @@ -165,7 +165,7 @@ AButton * ToolsToolBar::MakeTool( bmpRecoloredUpHiliteSmall, bmpRecoloredDownSmall, // Not bmpRecoloredHiliteSmall as down is inactive. eTool, eTool, eTool, - wxWindowID(id), + wxWindowID(id + FirstToolID), wxDefaultPosition, true, theTheme.ImageSize( bmpRecoloredUpSmall )); button->SetLabel( label ); @@ -250,7 +250,7 @@ int ToolsToolBar::GetDownTool() void ToolsToolBar::OnTool(wxCommandEvent & evt) { - mCurrentTool = evt.GetId() - firstTool; + mCurrentTool = evt.GetId() - firstTool - FirstToolID; for (int i = 0; i < numTools; i++) if (i == mCurrentTool) mTool[i]->PushDown(); diff --git a/src/toolbars/ToolsToolBar.h b/src/toolbars/ToolsToolBar.h index 3a5a2b2dc..a50cab94a 100644 --- a/src/toolbars/ToolsToolBar.h +++ b/src/toolbars/ToolsToolBar.h @@ -41,9 +41,11 @@ enum { numTools, firstTool = selectTool, - lastTool = multiTool + lastTool = multiTool, }; +const int FirstToolID = 11200; + class ToolsToolBar final : public ToolBar { public: