Audacity  2.2.2
ScreenshotCommand.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity - A Digital Audio Editor
4  Copyright 1999-2009 Audacity Team
5  License: GPL v2 - see LICENSE.txt
6 
7  Dominic Mazzoni
8  Dan Horgan
9 
10 ******************************************************************//*******************************************************************/
17 
18 /* TODO: JKC: The screenshot code is very verbose and should be made
19  much shorter. It could work from a single list of function
20  names and functors.
21  */
22 
23 #include "../Audacity.h"
24 #include "ScreenshotCommand.h"
25 #include "CommandTargets.h"
26 #include "../Project.h"
27 #include <wx/toplevel.h>
28 #include <wx/dcscreen.h>
29 #include <wx/dcmemory.h>
30 #include <wx/settings.h>
31 #include <wx/bitmap.h>
32 
33 #include "../Track.h"
34 #include "../TrackPanel.h"
35 #include "../toolbars/ToolManager.h"
36 #include "../toolbars/ToolBar.h"
37 #include "../toolbars/ControlToolBar.h"
38 #include "../toolbars/DeviceToolBar.h"
39 #include "../toolbars/EditToolBar.h"
40 #include "../toolbars/MeterToolBar.h"
41 #include "../toolbars/MixerToolBar.h"
42 #include "../toolbars/SelectionBar.h"
43 #include "../toolbars/ToolsToolBar.h"
44 #include "../toolbars/TranscriptionToolBar.h"
45 #include "../widgets/Ruler.h"
46 #include "../Prefs.h"
47 
48 #if defined(__WXMAC__) && !wxCHECK_VERSION(3, 0, 0)
49 //
50 // This is a temporary solution for capturing screenshots on
51 // OS X 10.6 or greater. This needs to go away once we move
52 // to wx3.
53 //
54 // (This was copied from wx3.1.0 source and hacked up a bit.)
55 //
56 #include <wx/mac/private.h>
57 #include <dlfcn.h>
58 
59 typedef CGImageRef (*CGDisplayCreateImageFunc)(CGDirectDisplayID displayID);
60 
61 static wxBitmap DoGetAsBitmap(const wxRect *subrect)
62 {
63  CGRect cgbounds = CGDisplayBounds(CGMainDisplayID());
64 
65  wxRect rect = subrect ? *subrect : wxRect(0, 0, cgbounds.size.width, cgbounds.size.height);
66 
67  wxBitmap bmp(rect.GetSize().GetWidth(), rect.GetSize().GetHeight(), 32);
68 
69  CGDisplayCreateImageFunc createImage =
70  (CGDisplayCreateImageFunc) dlsym(RTLD_NEXT, "CGDisplayCreateImage");
71  if (createImage == NULL)
72  {
73  return bmp;
74  }
75 
76  CGRect srcRect = CGRectMake(rect.x, rect.y, rect.width, rect.height);
77 
78  CGContextRef context = (CGContextRef)bmp.GetHBITMAP();
79 
80  CGContextSaveGState(context);
81 
82  CGContextTranslateCTM( context, 0, cgbounds.size.height );
83  CGContextScaleCTM( context, 1, -1 );
84 
85  if ( subrect )
86  srcRect = CGRectOffset( srcRect, -subrect->x, -subrect->y ) ;
87 
88  CGImageRef image = NULL;
89 
90  image = createImage(kCGDirectMainDisplay);
91 
92  wxASSERT_MSG(image, wxT("wxScreenDC::GetAsBitmap - unable to get screenshot."));
93 
94  CGContextDrawImage(context, srcRect, image);
95 
96  CGImageRelease(image);
97 
98  CGContextRestoreGState(context);
99 
100  return bmp;
101 }
102 #endif
103 
104 // static member variable.
105 void (*ScreenshotCommand::mIdleHandler)(wxIdleEvent& event) = NULL;
106 // This static variable is used to get from an idle event to the screenshot
107 // command that caused the idle event interception to be set up.
109 
110 // IdleHandler is expected to be called from EVT_IDLE when a dialog has been
111 // fully created. Usually the dialog will have been created by invoking
112 // an effects gui.
113 void IdleHandler(wxIdleEvent& event){
114  wxWindow * pWin = dynamic_cast<wxWindow*>(event.GetEventObject());
115  wxASSERT( pWin );
116  pWin->Unbind(wxEVT_IDLE, IdleHandler);
117  // We have the relevant window, so go and capture it.
118  if( ScreenshotCommand::mpShooter )
119  ScreenshotCommand::mpShooter->CaptureWindowOnIdle( pWin );
120 }
121 
122 
124 {
125  wxWindow *front = NULL;
126  wxWindow *proj = wxGetTopLevelParent(project);
127 
128  // This is kind of an odd hack. There's no method to enumerate all
129  // possible windows, so we search the whole screen for any windows
130  // that are not this one and not the given Audacity project and
131  // if we find anything, we assume that's the dialog the user wants
132  // to capture.
133 
134  int width, height, x, y;
135  wxDisplaySize(&width, &height);
136  for (x = 0; x < width; x += 50) {
137  for (y = 0; y < height; y += 50) {
138  wxWindow *win = wxFindWindowAtPoint(wxPoint(x, y));
139  if (win) {
140  win = wxGetTopLevelParent(win);
141  if (win != mIgnore && win != proj) {
142  front = win;
143  break;
144  }
145  }
146  }
147  }
148 
149  if (!front || !front->IsTopLevel()) {
150  return (wxTopLevelWindow *)proj;
151  }
152 
153  return (wxTopLevelWindow *)front;
154 }
155 
157 {
158  wxRect r;
159 
160  r.x = 16;
161  r.y = 16;
162  r.width = r.x * 2;
163  r.height = r.y * 2;
164 
165  return r;
166 }
167 
168 static void Yield()
169 {
170  int cnt;
171  for (cnt = 10; cnt && !wxTheApp->Yield(true); cnt--) {
172  wxMilliSleep(10);
173  }
174  wxMilliSleep(200);
175  for (cnt = 10; cnt && !wxTheApp->Yield(true); cnt--) {
176  wxMilliSleep(10);
177  }
178 }
179 
180 void ScreenshotCommand::Capture(const wxString &filename,
181  wxWindow *window,
182  int x, int y, int width, int height,
183  bool bg)
184 {
185  if (window) {
186  if (window->IsTopLevel()) {
187  window->Raise();
188  }
189  else {
190  wxGetTopLevelParent(window)->Raise();
191  }
192  }
193 
194  Yield();
195 
196  int screenW, screenH;
197  wxDisplaySize(&screenW, &screenH);
198  // Bug 1378 workaround.
199  // wx 3.0.2 has a bug in Blit from ScreenDC where in default mode
200  // much is drawn transparent - including for example black text!
201  // Forcing 24 bit here is a workaround.
202  wxBitmap full(screenW, screenH, 24);
203 
204  wxScreenDC screenDC;
205  wxMemoryDC fullDC;
206 
207 #if defined(__WXMAC__) && !wxCHECK_VERSION(3, 0, 0)
208  full = DoGetAsBitmap(NULL);
209 #else
210  // We grab the whole screen image since there seems to be a problem with
211  // using non-zero source coordinates on OSX. (as of wx2.8.9)
212  fullDC.SelectObject(full);
213  fullDC.Blit(0, 0, screenW, screenH, &screenDC, 0, 0);
214  fullDC.SelectObject(wxNullBitmap);
215 #endif
216 
217  wxRect r(x, y, width, height);
218 
219  // Ensure within bounds (x/y are negative on Windows when maximized)
220  r.Intersect(wxRect(0, 0, screenW, screenH));
221 
222  // Convert to screen coordinates if needed
223  if (window && window->GetParent() && !window->IsTopLevel()) {
224  r.SetPosition(window->GetParent()->ClientToScreen(r.GetPosition()));
225  }
226 
227  // Extract the actual image
228  wxBitmap part = full.GetSubBitmap(r);
229 
230  // Add a background
231  if (bg && mBackground) {
232  wxRect b = GetBackgroundRect();
233 
234  wxBitmap back(width + b.width, height + b.height);
235  fullDC.SelectObject(back);
236 
237  fullDC.SetBackground(wxBrush(mBackColor, wxSOLID));
238  fullDC.Clear();
239 
240  fullDC.DrawBitmap(part, b.x, b.y);
241  fullDC.SelectObject(wxNullBitmap);
242 
243  part = back;
244  }
245 
246  // Save the final image
247  wxImage image = part.ConvertToImage();
248  if (image.SaveFile(filename)) {
249  mOutput->Status( wxString::Format( _("Saved %s"), filename ) );
250  }
251  else {
252  mOutput->Error(
253  wxString::Format( _("Error trying to save file: %s"), filename ) );
254  }
255 
256  ::wxBell();
257 }
258 
259 void ScreenshotCommand::CaptureToolbar(ToolManager *man, int type, const wxString &name)
260 {
261  bool visible = man->IsVisible(type);
262  if (!visible) {
263  man->ShowHide(type);
264  Yield();
265  }
266 
267  wxWindow *w = man->GetToolBar(type);
268  int x = 0, y = 0;
269  int width, height;
270 
271  w->ClientToScreen(&x, &y);
272  w->GetParent()->ScreenToClient(&x, &y);
273  w->GetClientSize(&width, &height);
274 
275  Capture(name, w, x, y, width, height);
276 
277  if (!visible) {
278  man->ShowHide(type);
279  if (mIgnore)
280  mIgnore->Raise();
281  }
282 }
283 
284 void ScreenshotCommand::CaptureDock(wxWindow *win, const wxString &fileName)
285 {
286  int x = 0, y = 0;
287  int width, height;
288 
289  win->ClientToScreen(&x, &y);
290  win->GetParent()->ScreenToClient(&x, &y);
291  win->GetClientSize(&width, &height);
292 
293  Capture(fileName, win, x, y, width, height);
294 }
295 
296 void ExploreMenu( wxMenu * pMenu, int Id, int depth ){
297  Id;//compiler food.
298  if( !pMenu )
299  return;
300 
301  wxMenuItemList list = pMenu->GetMenuItems();
302  size_t lcnt = list.GetCount();
303  wxMenuItem * item;
304  wxString Label;
305  wxString Accel;
306 
307  for (size_t lndx = 0; lndx < lcnt; lndx++) {
308  item = list.Item(lndx)->GetData();
309  Label = item->GetItemLabelText();
310  Accel = item->GetItemLabel();
311  if( Accel.Contains("\t") )
312  Accel = Accel.AfterLast('\t');
313  else
314  Accel = "";
315  if( item->IsSeparator() )
316  Label = "----";
317  int flags = 0;
318  if (item->IsSubMenu())
319  flags +=1;
320  if (item->IsCheck() && item->IsChecked())
321  flags +=2;
322 
323  wxLogDebug("T.Add( %2i, %2i, 0, \"%s%s\" );", depth, flags, Label,Accel );
324  if (item->IsSubMenu()) {
325  pMenu = item->GetSubMenu();
326  ExploreMenu( pMenu, item->GetId(), depth+1 );
327  }
328  }
329 }
330 
331 void ScreenshotCommand::CaptureMenus(wxMenuBar*pBar, const wxString &fileName)
332 {
333  fileName;//compiler food.
334  if(!pBar ){
335  wxLogDebug("No menus");
336  return;
337  }
338 
339  size_t cnt = pBar->GetMenuCount();
340  size_t i;
341  wxString Label;
342  for(i=0;i<cnt;i++)
343  {
344  Label = pBar->GetMenuLabelText( i );
345  wxLogDebug( "\nT.Add( 0, 0, 0, \"%s" );", Label); ExploreMenu( pBar->GetMenu( i ), pBar->GetId(), 1 ); } #if 0 int x = 0, y = 0; int width, height; win->ClientToScreen(&x, &y); win->GetParent()->ScreenToClient(&x, &y); win->GetClientSize(&width, &height); Capture(fileName, win, x, y, width, height); #endif } // Handed a dialog, which it is given the option to capture. bool ScreenshotCommand::MayCapture( wxDialog * pDlg ) { if( mIdleHandler == NULL ) return false; pDlg->Bind( wxEVT_IDLE, mIdleHandler ); mIdleHandler = NULL; pDlg->ShowModal(); return true; } void ScreenshotCommand::CaptureWindowOnIdle( wxWindow * pWin ) { wxDialog * pDlg = dynamic_cast<wxDialog*>(pWin); if( !pDlg ){ wxLogDebug("Event from bogus dlg" ); return; } wxPoint Pos = pDlg->GetScreenPosition(); wxSize Siz = pDlg->GetSize(); wxString Title = pDlg->GetTitle(); // Remove '/' from "Sliding Time Scale/Pitch Shift..." // and any other effects that have illegal filename chanracters. Title.Replace( "/", "" ); Title.Replace( ":", "" ); wxString Name = mDirToWriteTo + Title + ".png"; wxLogDebug("Taking screenshot of window %s (%i,%i,%i,%i)", Name, Pos.x, Pos.y, Siz.x, Siz.y ); // This delay is needed, as dialogs take a moment or two to fade in. wxMilliSleep( 200 ); // JKC: The border of 7 pixels was determined from a trial capture and then measuring // in the GIMP. I'm unsure where the border comes from. Capture( Name, pDlg, (int)Pos.x+7, (int)Pos.y, (int)Siz.x-14, (int)Siz.y-7 ); // We've captured the dialog, so now dismiss the dialog. wxCommandEvent Evt( wxEVT_BUTTON, wxID_CANCEL ); pDlg->GetEventHandler()->AddPendingEvent( Evt ); } void ScreenshotCommand::CapturePreferences( AudacityProject * pProject, const wxString &fileName ){ fileName;//compiler food. CommandManager * pMan = pProject->GetCommandManager(); // Yucky static variables. Is there a better way? The problem is that we need the // idle callback to know more about what to do. mDirToWriteTo = fileName.BeforeLast('\\') + "\\"; mpShooter = this; const int nPrefsPages = 19; for( int i=0;i<nPrefsPages;i++){ // The handler is cleared each time it is used. SetIdleHandler( IdleHandler ); gPrefs->Write(wxT("/Prefs/PrefsCategory"), (long)i); gPrefs->Flush(); wxString Command = "Preferences"; if( !pMan->HandleTextualCommand( Command, AlwaysEnabledFlag, AlwaysEnabledFlag ) ) { wxLogDebug("Command %s not found", Command ); } // This sleep is not needed, but gives user a chance to see the // dialogs as they whizz by. wxMilliSleep( 200 ); } } void ScreenshotCommand::CaptureEffects( AudacityProject * pProject, const wxString &fileName ){ fileName;//compiler food. CommandManager * pMan = pProject->GetCommandManager(); wxString Str; // Yucky static variables. Is there a better way? The problem is that we need the // idle callback to know more about what to do. mDirToWriteTo = fileName.BeforeLast('\\') + "\\"; mpShooter = this; #define TRICKY_CAPTURE #define CAPTURE_NYQUIST_TOO // Commented out the effects that don't have dialogs. // Also any problematic ones, const wxString EffectNames[] = { #ifdef TRICKY_CAPTURE //"Contrast...", // renamed "ContrastAnalyser", //"Plot Spectrum...", // renamed "PlotSpectrum", "Auto Duck...", // needs a track below. //"Spectral edit multi tool", "Spectral edit parametric EQ...", // Needs a spectral selection. "Spectral edit shelves...", //"Noise Reduction...", // Exits twice... //"SC4...", //Has 'Close' rather than 'Cancel'. #endif "Amplify...", "Bass and Treble...", "Change Pitch...", "Change Speed...", "Change Tempo...", "Click Removal...", "Compressor...", "Distortion...", "Echo...", "Equalization...", //"Fade In", //"Fade Out", //"Invert", "Normalize...", "Paulstretch...", "Phaser...", //"Repair", "Repeat...", "Reverb...", //"Reverse", "Sliding Time Scale/Pitch Shift...", "Truncate Silence...", "Wahwah...", // Sole LADSPA effect... #ifdef CAPTURE_NYQUIST_TOO "Adjustable Fade...", "Clip Fix...", //"Crossfade Clips", "Crossfade Tracks...", "Delay...", "High Pass Filter...", "Limiter...", "Low Pass Filter...", "Notch Filter...", "Nyquist Prompt...", //"Studio Fade Out", "Tremolo...", "Vocal Reduction and Isolation...", "Vocal Remover...", "Vocoder...", #endif // Generators..... "Chirp...", "DTMF Tones...", "Noise...", "Silence...", "Tone...", #ifdef CAPTURE_NYQUIST_TOO "Pluck...", "Rhythm Track...", "Risset Drum...", "Sample Data Import...", #endif // Analyzers... "Find Clipping...", #ifdef CAPTURE_NYQUIST_TOO "Beat Finder...", "Regular Interval Labels...", "Sample Data Export...", "Silence Finder...", "Sound Finder...", #endif }; for( int i=0;i<sizeof(EffectNames)/sizeof(EffectNames[0]);i++){ // The handler is cleared each time it is used. SetIdleHandler( IdleHandler ); Str = EffectNames[i]; if( !pMan->HandleTextualCommand( Str, AlwaysEnabledFlag, AlwaysEnabledFlag ) ) { wxLogDebug("Command %s not found", Str); } // This sleep is not needed, but gives user a chance to see the // dialogs as they whizz by. wxMilliSleep( 200 ); } } wxString ScreenshotCommandType::BuildName() { return wxT("Screenshot"); } void ScreenshotCommandType::BuildSignature(CommandSignature &signature) { auto captureModeValidator = make_movable<OptionValidator>(); captureModeValidator->AddOption(wxT("window")); captureModeValidator->AddOption(wxT("fullwindow")); captureModeValidator->AddOption(wxT("windowplus")); captureModeValidator->AddOption(wxT("fullscreen")); captureModeValidator->AddOption(wxT("toolbars")); captureModeValidator->AddOption(wxT("menus")); captureModeValidator->AddOption(wxT("effects")); captureModeValidator->AddOption(wxT("preferences")); captureModeValidator->AddOption(wxT("selectionbar")); captureModeValidator->AddOption(wxT("spectralselection")); captureModeValidator->AddOption(wxT("tools")); captureModeValidator->AddOption(wxT("transport")); captureModeValidator->AddOption(wxT("mixer")); captureModeValidator->AddOption(wxT("meter")); captureModeValidator->AddOption(wxT("playmeter")); captureModeValidator->AddOption(wxT("recordmeter")); captureModeValidator->AddOption(wxT("edit")); captureModeValidator->AddOption(wxT("device")); captureModeValidator->AddOption(wxT("scrub")); captureModeValidator->AddOption(wxT("transcription")); captureModeValidator->AddOption(wxT("trackpanel")); captureModeValidator->AddOption(wxT("ruler")); captureModeValidator->AddOption(wxT("tracks")); captureModeValidator->AddOption(wxT("firsttrack")); captureModeValidator->AddOption(wxT("secondtrack")); auto backgroundValidator = make_movable<OptionValidator>(); backgroundValidator->AddOption(wxT("Blue")); backgroundValidator->AddOption(wxT("White")); backgroundValidator->AddOption(wxT("None")); auto filePathValidator = make_movable<DefaultValidator>(); signature.AddParameter(wxT("CaptureMode"), wxT("fullscreen"), std::move(captureModeValidator)); signature.AddParameter(wxT("Background"), wxT("None"), std::move(backgroundValidator)); signature.AddParameter(wxT("FilePath"), wxT(""), std::move(filePathValidator)); } CommandHolder ScreenshotCommandType::Create(std::unique_ptr<CommandOutputTarget> &&target) { return std::make_shared<ScreenshotCommand>(*this, std::move(target)); } wxString ScreenshotCommand::MakeFileName(const wxString &path, const wxString &basename) { wxFileName prefixPath; prefixPath.AssignDir(path); wxString prefix = prefixPath.GetPath (wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR); wxString filename; int i = 0; do { filename.Printf(wxT("%s%s%03d.png"), prefix, basename, i); i++; } while (::wxFileExists(filename)); return filename; } bool ScreenshotCommand::Apply(CommandExecutionContext context) { auto FindRectangle = []( TrackPanel &panel, Track &t ) { // This rectangle omits the focus ring about the track, and // also within that, a narrow black border with a "shadow" below and // to the right wxRect rect = panel.FindTrackRect( &t, false ); // Enlarge horizontally. // PRL: perhaps it's one pixel too much each side, including some gray // beyond the yellow? rect.x = 0; panel.GetClientSize(&rect.width, nullptr); // Enlarge vertically, enough to enclose the yellow focus border pixels // Omit the outermost ring of gray pixels // (Note that TrackPanel paints its focus over the "top margin" of the // rectangle allotted to the track, according to Track::GetY() and // Track::GetHeight(), but also over the margin of the next track.) rect.height += kBottomMargin; int dy = kTopMargin - 1; rect.Inflate( 0, dy ); // Reposition it relative to parent of panel rect.SetPosition( panel.GetParent()->ScreenToClient( panel.ClientToScreen( rect.GetPosition()))); return rect; }; // Read the parameters that were passed in wxString filePath = GetString(wxT("FilePath")); wxString captureMode = GetString(wxT("CaptureMode")); wxString background = GetString(wxT("Background")); // Build a suitable filename wxString fileName = MakeFileName(filePath, captureMode); if (background.IsSameAs(wxT("Blue"))) { mBackground = true; mBackColor = wxColour(51, 102, 153); } else if (background.IsSameAs(wxT("White"))) { mBackground = true; mBackColor = wxColour(255, 255, 255); } else { mBackground = false; } //Don't reset the toolbars to a known state. //We wil lbe capturing variations of them. //context.GetProject()->GetToolManager()->Reset(); wxTopLevelWindow *w = GetFrontWindow(context.GetProject()); if (!w) { return false; } if (captureMode.IsSameAs(wxT("window"))) { int x = 0, y = 0; int width, height; w->ClientToScreen(&x, &y); w->GetClientSize(&width, &height); if (w != context.GetProject() && w->GetTitle() != wxT("")) { fileName = MakeFileName(filePath, captureMode + (wxT("-") + w->GetTitle() + wxT("-"))); } Capture(fileName, w, x, y, width, height); } else if (captureMode.IsSameAs(wxT("fullwindow")) || captureMode.IsSameAs(wxT("windowplus"))) { wxRect r = w->GetRect(); r.SetPosition(w->GetScreenPosition()); r = w->GetScreenRect(); if (w != context.GetProject() && w->GetTitle() != wxT("")) { fileName = MakeFileName(filePath, captureMode + (wxT("-") + w->GetTitle() + wxT("-"))); } #if defined(__WXGTK__) // In wxGTK, we need to include decoration sizes r.width += (wxSystemSettings::GetMetric(wxSYS_BORDER_X, w) * 2); r.height += wxSystemSettings::GetMetric(wxSYS_CAPTION_Y, w) + wxSystemSettings::GetMetric(wxSYS_BORDER_Y, w); #endif if (!mBackground && captureMode.IsSameAs(wxT("windowplus"))) { // background colour not selected but we want a background wxRect b = GetBackgroundRect(); r.x = (r.x - b.x) >= 0 ? (r.x - b.x): 0; r.y = (r.y - b.y) >= 0 ? (r.y - b.y): 0; r.width += b.width; r.height += b.height; } Capture(fileName, w, r.x, r.y, r.width, r.height, true); } else if (captureMode.IsSameAs(wxT("fullscreen"))) { int width, height; wxDisplaySize(&width, &height); Capture(fileName, w, 0, 0, width, height); } else if (captureMode.IsSameAs(wxT("toolbars"))) { CaptureDock(context.GetProject()->GetToolManager()->GetTopDock(), fileName); } else if (captureMode.IsSameAs(wxT("menus"))) { CaptureMenus(context.GetProject()->GetMenuBar(), fileName); } else if (captureMode.IsSameAs(wxT("effects"))) { CaptureEffects(context.GetProject(), fileName); } else if (captureMode.IsSameAs(wxT("preferences"))) { CapturePreferences(context.GetProject(), fileName); } else if (captureMode.IsSameAs(wxT("selectionbar"))) { CaptureToolbar(context.GetProject()->GetToolManager(), SelectionBarID, fileName); } else if (captureMode.IsSameAs(wxT("spectralselection"))) { CaptureToolbar(context.GetProject()->GetToolManager(), SpectralSelectionBarID, fileName); } else if (captureMode.IsSameAs(wxT("tools"))) { CaptureToolbar(context.GetProject()->GetToolManager(), ToolsBarID, fileName); } else if (captureMode.IsSameAs(wxT("transport"))) { CaptureToolbar(context.GetProject()->GetToolManager(), TransportBarID, fileName); } else if (captureMode.IsSameAs(wxT("mixer"))) { CaptureToolbar(context.GetProject()->GetToolManager(), MixerBarID, fileName); } else if (captureMode.IsSameAs(wxT("meter"))) { CaptureToolbar(context.GetProject()->GetToolManager(), MeterBarID, fileName); } else if (captureMode.IsSameAs(wxT("recordmeter"))) { CaptureToolbar(context.GetProject()->GetToolManager(), RecordMeterBarID, fileName); } else if (captureMode.IsSameAs(wxT("playmeter"))) { CaptureToolbar(context.GetProject()->GetToolManager(), PlayMeterBarID, fileName); } else if (captureMode.IsSameAs(wxT("edit"))) { CaptureToolbar(context.GetProject()->GetToolManager(), EditBarID, fileName); } else if (captureMode.IsSameAs(wxT("device"))) { CaptureToolbar(context.GetProject()->GetToolManager(), DeviceBarID, fileName); } else if (captureMode.IsSameAs(wxT("transcription"))) { CaptureToolbar(context.GetProject()->GetToolManager(), TranscriptionBarID, fileName); } else if (captureMode.IsSameAs(wxT("scrub"))) { CaptureToolbar(context.GetProject()->GetToolManager(), ScrubbingBarID, fileName); } else if (captureMode.IsSameAs(wxT("trackpanel"))) { TrackPanel *panel = context.GetProject()->GetTrackPanel(); //AdornedRulerPanel *ruler = panel->mRuler; int h = panel->mRuler->GetRulerHeight(); int x = 0, y = -h; int width, height; panel->ClientToScreen(&x, &y); panel->GetParent()->ScreenToClient(&x, &y); panel->GetClientSize(&width, &height); Capture(fileName, panel, x, y, width, height + h); } else if (captureMode.IsSameAs(wxT("ruler"))) { TrackPanel *panel = context.GetProject()->GetTrackPanel(); AdornedRulerPanel *ruler = panel->mRuler; int x = 0, y = 0; int width, height; ruler->ClientToScreen(&x, &y); ruler->GetParent()->ScreenToClient(&x, &y); ruler->GetClientSize(&width, &height); height = ruler->GetRulerHeight(); Capture(fileName, ruler, x, y, width, height); } else if (captureMode.IsSameAs(wxT("tracks"))) { TrackPanel *panel = context.GetProject()->GetTrackPanel(); int x = 0, y = 0; int width, height; panel->ClientToScreen(&x, &y); panel->GetParent()->ScreenToClient(&x, &y); panel->GetClientSize(&width, &height); Capture(fileName, panel, x, y, width, height); } else if (captureMode.IsSameAs(wxT("firsttrack"))) { TrackPanel *panel = context.GetProject()->GetTrackPanel(); TrackListIterator iter(context.GetProject()->GetTracks()); Track * t = iter.First(); if (!t) { return false; } wxRect r = FindRectangle( *panel, *t ); Capture(fileName, panel, r.x, r.y, r.width, r.height); } else if (captureMode.IsSameAs(wxT("secondtrack"))) { TrackPanel *panel = context.GetProject()->GetTrackPanel(); TrackListIterator iter(context.GetProject()->GetTracks()); Track * t = iter.First(); if (!t) { return false; } if (t->GetLinked()) { t = iter.Next(); } t = iter.Next(); if (!t) { return false; } wxRect r = FindRectangle( *panel, *t ); Capture(fileName, panel, r.x, r.y, r.width, r.height); } else { // Invalid capture mode! return false; } return true; } \" );", Label);
346  ExploreMenu( pBar->GetMenu( i ), pBar->GetId(), 1 );
347  }
348 
349 #if 0
350  int x = 0, y = 0;
351  int width, height;
352 
353  win->ClientToScreen(&x, &y);
354  win->GetParent()->ScreenToClient(&x, &y);
355  win->GetClientSize(&width, &height);
356 
357  Capture(fileName, win, x, y, width, height);
358 #endif
359 }
360 
361 // Handed a dialog, which it is given the option to capture.
362 bool ScreenshotCommand::MayCapture( wxDialog * pDlg )
363 {
364  if( mIdleHandler == NULL )
365  return false;
366  pDlg->Bind( wxEVT_IDLE, mIdleHandler );
367  mIdleHandler = NULL;
368  pDlg->ShowModal();
369  return true;
370 }
371 
373 {
374  wxDialog * pDlg = dynamic_cast<wxDialog*>(pWin);
375  if( !pDlg ){
376  wxLogDebug("Event from bogus dlg" );
377  return;
378  }
379 
380  wxPoint Pos = pDlg->GetScreenPosition();
381  wxSize Siz = pDlg->GetSize();
382  wxString Title = pDlg->GetTitle();
383 
384  // Remove '/' from "Sliding Time Scale/Pitch Shift..."
385  // and any other effects that have illegal filename chanracters.
386  Title.Replace( "/", "" );
387  Title.Replace( ":", "" );
388  wxString Name = mDirToWriteTo + Title + ".png";
389 
390  wxLogDebug("Taking screenshot of window %s (%i,%i,%i,%i)", Name,
391  Pos.x, Pos.y, Siz.x, Siz.y );
392  // This delay is needed, as dialogs take a moment or two to fade in.
393  wxMilliSleep( 200 );
394  // JKC: The border of 7 pixels was determined from a trial capture and then measuring
395  // in the GIMP. I'm unsure where the border comes from.
396  Capture( Name, pDlg, (int)Pos.x+7, (int)Pos.y, (int)Siz.x-14, (int)Siz.y-7 );
397 
398  // We've captured the dialog, so now dismiss the dialog.
399  wxCommandEvent Evt( wxEVT_BUTTON, wxID_CANCEL );
400  pDlg->GetEventHandler()->AddPendingEvent( Evt );
401 }
402 
403 void ScreenshotCommand::CapturePreferences( AudacityProject * pProject, const wxString &fileName ){
404  fileName;//compiler food.
405  CommandManager * pMan = pProject->GetCommandManager();
406 
407  // Yucky static variables. Is there a better way? The problem is that we need the
408  // idle callback to know more about what to do.
409  mDirToWriteTo = fileName.BeforeLast('\\') + "\\";
410  mpShooter = this;
411  const int nPrefsPages = 19;
412 
413  for( int i=0;i<nPrefsPages;i++){
414  // The handler is cleared each time it is used.
416  gPrefs->Write(wxT("/Prefs/PrefsCategory"), (long)i);
417  gPrefs->Flush();
418  wxString Command = "Preferences";
420  {
421  wxLogDebug("Command %s not found", Command );
422  }
423  // This sleep is not needed, but gives user a chance to see the
424  // dialogs as they whizz by.
425  wxMilliSleep( 200 );
426  }
427 }
428 
429 void ScreenshotCommand::CaptureEffects( AudacityProject * pProject, const wxString &fileName ){
430  fileName;//compiler food.
431  CommandManager * pMan = pProject->GetCommandManager();
432  wxString Str;
433  // Yucky static variables. Is there a better way? The problem is that we need the
434  // idle callback to know more about what to do.
435  mDirToWriteTo = fileName.BeforeLast('\\') + "\\";
436  mpShooter = this;
437 
438 #define TRICKY_CAPTURE
439 #define CAPTURE_NYQUIST_TOO
440  // Commented out the effects that don't have dialogs.
441  // Also any problematic ones,
442  const wxString EffectNames[] = {
443 
444 #ifdef TRICKY_CAPTURE
445  //"Contrast...", // renamed
446  "ContrastAnalyser",
447  //"Plot Spectrum...", // renamed
448  "PlotSpectrum",
449 
450  "Auto Duck...", // needs a track below.
451  //"Spectral edit multi tool",
452  "Spectral edit parametric EQ...", // Needs a spectral selection.
453  "Spectral edit shelves...",
454 
455  //"Noise Reduction...", // Exits twice...
456  //"SC4...", //Has 'Close' rather than 'Cancel'.
457 #endif
458  "Amplify...",
459  "Bass and Treble...",
460  "Change Pitch...",
461  "Change Speed...",
462  "Change Tempo...",
463  "Click Removal...",
464  "Compressor...",
465  "Distortion...",
466  "Echo...",
467  "Equalization...",
468  //"Fade In",
469  //"Fade Out",
470  //"Invert",
471  "Normalize...",
472  "Paulstretch...",
473  "Phaser...",
474  //"Repair",
475  "Repeat...",
476  "Reverb...",
477  //"Reverse",
478  "Sliding Time Scale/Pitch Shift...",
479  "Truncate Silence...",
480  "Wahwah...",
481  // Sole LADSPA effect...
482 #ifdef CAPTURE_NYQUIST_TOO
483  "Adjustable Fade...",
484  "Clip Fix...",
485  //"Crossfade Clips",
486  "Crossfade Tracks...",
487  "Delay...",
488  "High Pass Filter...",
489  "Limiter...",
490  "Low Pass Filter...",
491  "Notch Filter...",
492  "Nyquist Prompt...",
493  //"Studio Fade Out",
494  "Tremolo...",
495  "Vocal Reduction and Isolation...",
496  "Vocal Remover...",
497  "Vocoder...",
498 #endif
499  // Generators.....
500  "Chirp...",
501  "DTMF Tones...",
502  "Noise...",
503  "Silence...",
504  "Tone...",
505 #ifdef CAPTURE_NYQUIST_TOO
506  "Pluck...",
507  "Rhythm Track...",
508  "Risset Drum...",
509  "Sample Data Import...",
510 #endif
511  // Analyzers...
512  "Find Clipping...",
513 #ifdef CAPTURE_NYQUIST_TOO
514  "Beat Finder...",
515  "Regular Interval Labels...",
516  "Sample Data Export...",
517  "Silence Finder...",
518  "Sound Finder...",
519 #endif
520  };
521 
522  for( int i=0;i<sizeof(EffectNames)/sizeof(EffectNames[0]);i++){
523  // The handler is cleared each time it is used.
525  Str = EffectNames[i];
527  {
528  wxLogDebug("Command %s not found", Str);
529  }
530  // This sleep is not needed, but gives user a chance to see the
531  // dialogs as they whizz by.
532  wxMilliSleep( 200 );
533  }
534 }
535 
536 
538 {
539  return wxT("Screenshot");
540 }
541 
543 {
544  auto captureModeValidator = make_movable<OptionValidator>();
545  captureModeValidator->AddOption(wxT("window"));
546  captureModeValidator->AddOption(wxT("fullwindow"));
547  captureModeValidator->AddOption(wxT("windowplus"));
548  captureModeValidator->AddOption(wxT("fullscreen"));
549  captureModeValidator->AddOption(wxT("toolbars"));
550  captureModeValidator->AddOption(wxT("menus"));
551  captureModeValidator->AddOption(wxT("effects"));
552  captureModeValidator->AddOption(wxT("preferences"));
553  captureModeValidator->AddOption(wxT("selectionbar"));
554  captureModeValidator->AddOption(wxT("spectralselection"));
555  captureModeValidator->AddOption(wxT("tools"));
556  captureModeValidator->AddOption(wxT("transport"));
557  captureModeValidator->AddOption(wxT("mixer"));
558  captureModeValidator->AddOption(wxT("meter"));
559  captureModeValidator->AddOption(wxT("playmeter"));
560  captureModeValidator->AddOption(wxT("recordmeter"));
561  captureModeValidator->AddOption(wxT("edit"));
562  captureModeValidator->AddOption(wxT("device"));
563  captureModeValidator->AddOption(wxT("scrub"));
564  captureModeValidator->AddOption(wxT("transcription"));
565  captureModeValidator->AddOption(wxT("trackpanel"));
566  captureModeValidator->AddOption(wxT("ruler"));
567  captureModeValidator->AddOption(wxT("tracks"));
568  captureModeValidator->AddOption(wxT("firsttrack"));
569  captureModeValidator->AddOption(wxT("secondtrack"));
570 
571  auto backgroundValidator = make_movable<OptionValidator>();
572  backgroundValidator->AddOption(wxT("Blue"));
573  backgroundValidator->AddOption(wxT("White"));
574  backgroundValidator->AddOption(wxT("None"));
575 
576  auto filePathValidator = make_movable<DefaultValidator>();
577 
578  signature.AddParameter(wxT("CaptureMode"),
579  wxT("fullscreen"),
580  std::move(captureModeValidator));
581  signature.AddParameter(wxT("Background"),
582  wxT("None"),
583  std::move(backgroundValidator));
584  signature.AddParameter(wxT("FilePath"), wxT(""), std::move(filePathValidator));
585 }
586 
587 CommandHolder ScreenshotCommandType::Create(std::unique_ptr<CommandOutputTarget> &&target)
588 {
589  return std::make_shared<ScreenshotCommand>(*this, std::move(target));
590 }
591 
592 wxString ScreenshotCommand::MakeFileName(const wxString &path, const wxString &basename)
593 {
594  wxFileName prefixPath;
595  prefixPath.AssignDir(path);
596  wxString prefix = prefixPath.GetPath
597  (wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR);
598 
599  wxString filename;
600  int i = 0;
601  do {
602  filename.Printf(wxT("%s%s%03d.png"),
603  prefix, basename, i);
604  i++;
605  } while (::wxFileExists(filename));
606 
607  return filename;
608 }
609 
611 {
612  auto FindRectangle = []( TrackPanel &panel, Track &t )
613  {
614  // This rectangle omits the focus ring about the track, and
615  // also within that, a narrow black border with a "shadow" below and
616  // to the right
617  wxRect rect = panel.FindTrackRect( &t, false );
618 
619  // Enlarge horizontally.
620  // PRL: perhaps it's one pixel too much each side, including some gray
621  // beyond the yellow?
622  rect.x = 0;
623  panel.GetClientSize(&rect.width, nullptr);
624 
625  // Enlarge vertically, enough to enclose the yellow focus border pixels
626  // Omit the outermost ring of gray pixels
627 
628  // (Note that TrackPanel paints its focus over the "top margin" of the
629  // rectangle allotted to the track, according to Track::GetY() and
630  // Track::GetHeight(), but also over the margin of the next track.)
631 
632  rect.height += kBottomMargin;
633  int dy = kTopMargin - 1;
634  rect.Inflate( 0, dy );
635 
636  // Reposition it relative to parent of panel
637  rect.SetPosition(
638  panel.GetParent()->ScreenToClient(
639  panel.ClientToScreen(
640  rect.GetPosition())));
641 
642  return rect;
643  };
644 
645  // Read the parameters that were passed in
646  wxString filePath = GetString(wxT("FilePath"));
647  wxString captureMode = GetString(wxT("CaptureMode"));
648  wxString background = GetString(wxT("Background"));
649 
650  // Build a suitable filename
651  wxString fileName = MakeFileName(filePath, captureMode);
652 
653  if (background.IsSameAs(wxT("Blue")))
654  {
655  mBackground = true;
656  mBackColor = wxColour(51, 102, 153);
657  }
658  else if (background.IsSameAs(wxT("White")))
659  {
660  mBackground = true;
661  mBackColor = wxColour(255, 255, 255);
662  }
663  else
664  {
665  mBackground = false;
666  }
667 
668  //Don't reset the toolbars to a known state.
669  //We wil lbe capturing variations of them.
670  //context.GetProject()->GetToolManager()->Reset();
671 
672  wxTopLevelWindow *w = GetFrontWindow(context.GetProject());
673  if (!w)
674  {
675  return false;
676  }
677 
678  if (captureMode.IsSameAs(wxT("window")))
679  {
680  int x = 0, y = 0;
681  int width, height;
682 
683  w->ClientToScreen(&x, &y);
684  w->GetClientSize(&width, &height);
685 
686  if (w != context.GetProject() && w->GetTitle() != wxT("")) {
687  fileName = MakeFileName(filePath,
688  captureMode + (wxT("-") + w->GetTitle() + wxT("-")));
689  }
690 
691  Capture(fileName, w, x, y, width, height);
692  }
693  else if (captureMode.IsSameAs(wxT("fullwindow"))
694  || captureMode.IsSameAs(wxT("windowplus")))
695  {
696 
697  wxRect r = w->GetRect();
698  r.SetPosition(w->GetScreenPosition());
699  r = w->GetScreenRect();
700 
701  if (w != context.GetProject() && w->GetTitle() != wxT("")) {
702  fileName = MakeFileName(filePath,
703  captureMode + (wxT("-") + w->GetTitle() + wxT("-")));
704  }
705 
706 #if defined(__WXGTK__)
707  // In wxGTK, we need to include decoration sizes
708  r.width += (wxSystemSettings::GetMetric(wxSYS_BORDER_X, w) * 2);
709  r.height += wxSystemSettings::GetMetric(wxSYS_CAPTION_Y, w) +
710  wxSystemSettings::GetMetric(wxSYS_BORDER_Y, w);
711 #endif
712  if (!mBackground && captureMode.IsSameAs(wxT("windowplus")))
713  {
714  // background colour not selected but we want a background
715  wxRect b = GetBackgroundRect();
716  r.x = (r.x - b.x) >= 0 ? (r.x - b.x): 0;
717  r.y = (r.y - b.y) >= 0 ? (r.y - b.y): 0;
718  r.width += b.width;
719  r.height += b.height;
720  }
721 
722  Capture(fileName, w, r.x, r.y, r.width, r.height, true);
723  }
724  else if (captureMode.IsSameAs(wxT("fullscreen")))
725  {
726  int width, height;
727  wxDisplaySize(&width, &height);
728 
729  Capture(fileName, w, 0, 0, width, height);
730  }
731  else if (captureMode.IsSameAs(wxT("toolbars")))
732  {
733  CaptureDock(context.GetProject()->GetToolManager()->GetTopDock(), fileName);
734  }
735  else if (captureMode.IsSameAs(wxT("menus")))
736  {
737  CaptureMenus(context.GetProject()->GetMenuBar(), fileName);
738  }
739  else if (captureMode.IsSameAs(wxT("effects")))
740  {
741  CaptureEffects(context.GetProject(), fileName);
742  }
743  else if (captureMode.IsSameAs(wxT("preferences")))
744  {
745  CapturePreferences(context.GetProject(), fileName);
746  }
747  else if (captureMode.IsSameAs(wxT("selectionbar")))
748  {
749  CaptureToolbar(context.GetProject()->GetToolManager(), SelectionBarID, fileName);
750  }
751  else if (captureMode.IsSameAs(wxT("spectralselection")))
752  {
754  }
755  else if (captureMode.IsSameAs(wxT("tools")))
756  {
757  CaptureToolbar(context.GetProject()->GetToolManager(), ToolsBarID, fileName);
758  }
759  else if (captureMode.IsSameAs(wxT("transport")))
760  {
761  CaptureToolbar(context.GetProject()->GetToolManager(), TransportBarID, fileName);
762  }
763  else if (captureMode.IsSameAs(wxT("mixer")))
764  {
765  CaptureToolbar(context.GetProject()->GetToolManager(), MixerBarID, fileName);
766  }
767  else if (captureMode.IsSameAs(wxT("meter")))
768  {
769  CaptureToolbar(context.GetProject()->GetToolManager(), MeterBarID, fileName);
770  }
771  else if (captureMode.IsSameAs(wxT("recordmeter")))
772  {
773  CaptureToolbar(context.GetProject()->GetToolManager(), RecordMeterBarID, fileName);
774  }
775  else if (captureMode.IsSameAs(wxT("playmeter")))
776  {
777  CaptureToolbar(context.GetProject()->GetToolManager(), PlayMeterBarID, fileName);
778  }
779  else if (captureMode.IsSameAs(wxT("edit")))
780  {
781  CaptureToolbar(context.GetProject()->GetToolManager(), EditBarID, fileName);
782  }
783  else if (captureMode.IsSameAs(wxT("device")))
784  {
785  CaptureToolbar(context.GetProject()->GetToolManager(), DeviceBarID, fileName);
786  }
787  else if (captureMode.IsSameAs(wxT("transcription")))
788  {
790  }
791  else if (captureMode.IsSameAs(wxT("scrub")))
792  {
793  CaptureToolbar(context.GetProject()->GetToolManager(), ScrubbingBarID, fileName);
794  }
795  else if (captureMode.IsSameAs(wxT("trackpanel")))
796  {
797  TrackPanel *panel = context.GetProject()->GetTrackPanel();
798  //AdornedRulerPanel *ruler = panel->mRuler;
799 
800  int h = panel->mRuler->GetRulerHeight();
801  int x = 0, y = -h;
802  int width, height;
803 
804  panel->ClientToScreen(&x, &y);
805  panel->GetParent()->ScreenToClient(&x, &y);
806  panel->GetClientSize(&width, &height);
807 
808  Capture(fileName, panel, x, y, width, height + h);
809  }
810  else if (captureMode.IsSameAs(wxT("ruler")))
811  {
812  TrackPanel *panel = context.GetProject()->GetTrackPanel();
813  AdornedRulerPanel *ruler = panel->mRuler;
814 
815  int x = 0, y = 0;
816  int width, height;
817 
818  ruler->ClientToScreen(&x, &y);
819  ruler->GetParent()->ScreenToClient(&x, &y);
820  ruler->GetClientSize(&width, &height);
821  height = ruler->GetRulerHeight();
822 
823  Capture(fileName, ruler, x, y, width, height);
824  }
825  else if (captureMode.IsSameAs(wxT("tracks")))
826  {
827  TrackPanel *panel = context.GetProject()->GetTrackPanel();
828 
829  int x = 0, y = 0;
830  int width, height;
831 
832  panel->ClientToScreen(&x, &y);
833  panel->GetParent()->ScreenToClient(&x, &y);
834  panel->GetClientSize(&width, &height);
835 
836  Capture(fileName, panel, x, y, width, height);
837  }
838  else if (captureMode.IsSameAs(wxT("firsttrack")))
839  {
840  TrackPanel *panel = context.GetProject()->GetTrackPanel();
841  TrackListIterator iter(context.GetProject()->GetTracks());
842  Track * t = iter.First();
843  if (!t) {
844  return false;
845  }
846  wxRect r = FindRectangle( *panel, *t );
847  Capture(fileName, panel, r.x, r.y, r.width, r.height);
848  }
849  else if (captureMode.IsSameAs(wxT("secondtrack")))
850  {
851  TrackPanel *panel = context.GetProject()->GetTrackPanel();
852  TrackListIterator iter(context.GetProject()->GetTracks());
853  Track * t = iter.First();
854  if (!t) {
855  return false;
856  }
857  if (t->GetLinked()) {
858  t = iter.Next();
859  }
860  t = iter.Next();
861  if (!t) {
862  return false;
863  }
864  wxRect r = FindRectangle( *panel, *t );
865  Capture(fileName, panel, r.x, r.y, r.width, r.height);
866  }
867  else
868  {
869  // Invalid capture mode!
870  return false;
871  }
872 
873  return true;
874 }
ToolDock * GetTopDock()
void CaptureToolbar(ToolManager *man, int type, const wxString &name)
std::shared_ptr< Command > CommandHolder
wxRect FindTrackRect(const Track *target, bool label)
void AddParameter(const wxString &name, const wxVariant &dft, movable_ptr< Validator > &&valid)
wxString GetString(const wxString &paramName)
Definition: Command.cpp:165
wxString BuildName() override
CommandHolder Create(std::unique_ptr< CommandOutputTarget > &&target) override
void BuildSignature(CommandSignature &signature) override
Postcondition: signature is a 'signature' map containing parameter.
void CaptureDock(wxWindow *win, const wxString &fileName)
int GetRulerHeight()
Definition: Ruler.h:331
CommandManager * GetCommandManager()
Definition: Project.h:328
Abstract base class for command interface. It implements Command::SetParameter() and defers all other...
Definition: Command.h:70
void ShowHide(int type)
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:158
std::unique_ptr< CommandOutputTarget > mOutput
Definition: Command.h:130
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
static ScreenshotCommand * mpShooter
The TrackPanel class coordinates updates and operations on the main part of the screen which contains...
Definition: TrackPanel.h:245
bool HandleTextualCommand(const wxString &Str, CommandFlag flags, CommandMask mask)
ToolManager * GetToolManager()
Definition: Project.h:688
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:94
CommandManager implements a system for organizing all user-callable commands.
bool IsVisible(int type)
Implements a command for capturing various areas of the screen or project window. ...
void CaptureEffects(AudacityProject *pProject, const wxString &fileName)
void ExploreMenu(wxMenu *pMenu, int Id, int depth)
An iterator for a TrackList.
Definition: Track.h:394
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
void CaptureMenus(wxMenuBar *pBar, const wxString &fileName)
void CapturePreferences(AudacityProject *pProject, const wxString &fileName)
const wxChar * name
Definition: Distortion.cpp:94
Represents a context to which a command may be applied.
Definition: Command.h:50
void IdleHandler(wxIdleEvent &event)
TrackPanel * GetTrackPanel()
Definition: Project.h:289
This is an Audacity Specific ruler panel which additionally has border, selection markers...
Definition: Ruler.h:314
static bool MayCapture(wxDialog *pDlg)
TrackList * GetTracks()
Definition: Project.h:174
static void Yield()
AudacityProject * GetProject() const
Definition: Command.h:60
AdornedRulerPanel * mRuler
Definition: TrackPanel.h:453
class ToolManager
Definition: ToolManager.h:45
static void(* mIdleHandler)(wxIdleEvent &event)
static void SetIdleHandler(void(*pHandler)(wxIdleEvent &event))
Class that maps parameter names to default values and validators.
wxTopLevelWindow * GetFrontWindow(AudacityProject *project)
ToolBar * GetToolBar(int type) const
void CaptureWindowOnIdle(wxWindow *pWin)
void Capture(const wxString &basename, wxWindow *window, int x, int y, int width, int height, bool bg=false)
bool Apply(CommandExecutionContext context)
wxString MakeFileName(const wxString &path, const wxString &basename)