Audacity  2.2.2
ScreenshotCommand.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity - A Digital Audio Editor
4  Copyright 1999-2018 Audacity Team
5  License: GPL v2 - see LICENSE.txt
6 
7  Dominic Mazzoni
8  Dan Horgan
9  James Crook
10 
11 ******************************************************************//*******************************************************************/
19 
20 #include "../Audacity.h"
21 #include "ScreenshotCommand.h"
22 #include "CommandTargets.h"
23 #include "../Project.h"
24 #include <wx/toplevel.h>
25 #include <wx/dcscreen.h>
26 #include <wx/dcmemory.h>
27 #include <wx/settings.h>
28 #include <wx/bitmap.h>
29 #include <wx/valgen.h>
30 
31 #include "../Track.h"
32 #include "../TrackPanel.h"
33 #include "../toolbars/ToolManager.h"
34 #include "../toolbars/ToolBar.h"
35 #include "../toolbars/ControlToolBar.h"
36 #include "../toolbars/DeviceToolBar.h"
37 #include "../toolbars/EditToolBar.h"
38 #include "../toolbars/MeterToolBar.h"
39 #include "../toolbars/MixerToolBar.h"
40 #include "../toolbars/SelectionBar.h"
41 #include "../toolbars/ToolsToolBar.h"
42 #include "../toolbars/TranscriptionToolBar.h"
43 #include "../widgets/Ruler.h"
44 #include "../Prefs.h"
45 #include "../ShuttleGui.h"
46 #include "CommandContext.h"
47 
48 
49 static const IdentInterfaceSymbol
51 {
52  { XO("Window") },
53  { wxT("FullWindow"), XO("Full Window") },
54  { wxT("WindowPlus"), XO("Window Plus") },
55  { XO("Fullscreen") },
56  { XO("Toolbars") },
57  { XO("Effects") },
58  { XO("Scriptables") },
59  { XO("Preferences") },
60  { XO("Selectionbar") },
61  { wxT("SpectralSelection"), XO("Spectral Selection") },
62  { XO("Tools") },
63  { XO("Transport") },
64  { XO("Mixer") },
65  { XO("Meter") },
66  { wxT("PlayMeter"), XO("Play Meter") },
67  { wxT("RecordMeter"), XO("Record Meter") },
68  { XO("Edit") },
69  { XO("Device") },
70  { XO("Scrub") },
71  { XO("Transcription") },
72  { XO("Trackpanel") },
73  { XO("Ruler") },
74  { XO("Tracks") },
75  { wxT("FirstTrack"), XO("First Track") },
76  { wxT("FirstTwoTracks"), XO("First Two Tracks") },
77  { wxT("FirstThreeTracks"), XO("First Three Tracks") },
78  { wxT("FirstFourTracks"), XO("First Four Tracks") },
79  { wxT("SecondTrack"), XO("Second Track") },
80  { wxT("TracksPlus"), XO("Tracks Plus") },
81  { wxT("FirstTrackPlus"), XO("First Track Plus") },
82  { wxT("AllTracks"), XO("All Tracks") },
83  { wxT("AllTracksPlus"), XO("All Tracks Plus") },
84 };
85 
86 
87 static const IdentInterfaceSymbol
89 {
90  // These are acceptable dual purpose internal/visible names
91  { XO("Blue") },
92  { XO("White") },
93  { XO("None") },
94 };
95 
96 
98  S.Define( mPath, wxT("Path"), wxT(""));
99  S.DefineEnum( mWhat, wxT("CaptureWhat"), kwindow,kCaptureWhatStrings, nCaptureWhats );
101  S.OptionalN(bHasBringToTop).Define( mbBringToTop, wxT("ToTop"), true );
102  return true;
103 };
104 
106 {
109  S.AddSpace(0, 5);
110 
111  S.StartMultiColumn(2, wxALIGN_CENTER);
112  {
113  S.TieTextBox( _("Path:"), mPath);
114  S.TieChoice( _("Capture What:"), mWhat, &whats);
115  S.TieChoice( _("Background:"), mBack, &backs);
116  S.TieCheckBox( _("Bring To Top:"), mbBringToTop);
117  }
118  S.EndMultiColumn();
119 }
120 
121 // static member variable.
122 void (*ScreenshotCommand::mIdleHandler)(wxIdleEvent& event) = NULL;
123 // This static variable is used to get from an idle event to the screenshot
124 // command that caused the idle event interception to be set up.
126 
127 // IdleHandler is expected to be called from EVT_IDLE when a dialog has been
128 // fully created. Usually the dialog will have been created by invoking
129 // an effects gui.
130 void IdleHandler(wxIdleEvent& event){
131  wxWindow * pWin = dynamic_cast<wxWindow*>(event.GetEventObject());
132  wxASSERT( pWin );
133  pWin->Unbind(wxEVT_IDLE, IdleHandler);
134  CommandContext context( *GetActiveProject() );
135  // We have the relevant window, so go and capture it.
136  if( ScreenshotCommand::mpShooter )
137  ScreenshotCommand::mpShooter->CaptureWindowOnIdle( context, pWin );
138 }
139 
140 
142 {
143  wxWindow *front = NULL;
144  wxWindow *proj = wxGetTopLevelParent(project);
145 
146  // This is kind of an odd hack. There's no method to enumerate all
147  // possible windows, so we search the whole screen for any windows
148  // that are not this one and not the given Audacity project and
149  // if we find anything, we assume that's the dialog the user wants
150  // to capture.
151 
152  int width, height, x, y;
153  wxDisplaySize(&width, &height);
154  for (x = 0; x < width; x += 50) {
155  for (y = 0; y < height; y += 50) {
156  wxWindow *win = wxFindWindowAtPoint(wxPoint(x, y));
157  if (win) {
158  win = wxGetTopLevelParent(win);
159  if (win != mIgnore && win != proj) {
160  front = win;
161  break;
162  }
163  }
164  }
165  }
166 
167  if (!front || !front->IsTopLevel()) {
168  return (wxTopLevelWindow *)proj;
169  }
170 
171  return (wxTopLevelWindow *)front;
172 }
173 
175 {
176  wxRect r;
177 
178  r.x = 16;
179  r.y = 16;
180  r.width = r.x * 2;
181  r.height = r.y * 2;
182 
183  return r;
184 }
185 
186 static void Yield()
187 {
188  int cnt;
189  for (cnt = 10; cnt && !wxTheApp->Yield(true); cnt--) {
190  wxMilliSleep(10);
191  }
192  wxMilliSleep(200);
193  for (cnt = 10; cnt && !wxTheApp->Yield(true); cnt--) {
194  wxMilliSleep(10);
195  }
196 }
197 
199  const CommandContext & context,
200  const wxString &filename,
201  wxWindow *window, wxRect r,
202  bool bg)
203 {
204  int width = r.width;
205  int height = r.height;
206  if( r.width == 0 )
207  return false;
208  if (window ) {
209  wxWindow * win = window;
210  wxTopLevelWindow * top_win= nullptr;
211  if( !window->IsTopLevel())
212  win = wxGetTopLevelParent(window);
213  top_win = dynamic_cast<wxTopLevelWindow*>( win );
214  if( (!bHasBringToTop || mbBringToTop) && (!top_win || !top_win->IsActive()) ){
215  win->Raise();
216  Yield();
217  }
218  }
219 
220 
221  int screenW, screenH;
222  wxDisplaySize(&screenW, &screenH);
223  // Bug 1378 workaround.
224  // wx 3.0.2 has a bug in Blit from ScreenDC where in default mode
225  // much is drawn transparent - including for example black text!
226  // Forcing 24 bit here is a workaround.
227  wxBitmap full(screenW, screenH, 24);
228 
229  wxScreenDC screenDC;
230  wxMemoryDC fullDC;
231 
232 #if defined(__WXMAC__) && !wxCHECK_VERSION(3, 0, 0)
233  full = DoGetAsBitmap(NULL);
234 #else
235  // We grab the whole screen image since there seems to be a problem with
236  // using non-zero source coordinates on OSX. (as of wx2.8.9)
237  fullDC.SelectObject(full);
238  fullDC.Blit(0, 0, screenW, screenH, &screenDC, 0, 0);
239  fullDC.SelectObject(wxNullBitmap);
240 #endif
241 
242  //wxRect r(x, y, width, height);
243 
244 
245  // Convert to screen coordinates if needed
246  if (window && window->GetParent() && !window->IsTopLevel()) {
247  r.SetPosition(window->GetParent()->ClientToScreen(r.GetPosition()));
248  }
249 
250  // Ensure within bounds (x/y are negative on Windows when maximized)
251  r.Intersect(wxRect(0, 0, screenW, screenH));
252 
253  // Extract the actual image
254  wxBitmap part = full.GetSubBitmap(r);
255 
256  // Add a background
257  if (bg && mBackground) {
258  wxRect b = GetBackgroundRect();
259 
260  wxBitmap back(width + b.width, height + b.height);
261  fullDC.SelectObject(back);
262 
263  fullDC.SetBackground(wxBrush(mBackColor, wxSOLID));
264  fullDC.Clear();
265 
266  fullDC.DrawBitmap(part, b.x, b.y);
267  fullDC.SelectObject(wxNullBitmap);
268 
269  part = back;
270  }
271 
272  // Save the final image
273  wxImage image = part.ConvertToImage();
274  ::wxBell();
275 
276  if (image.SaveFile(filename)) {
277  // flush
278  context.Status( wxString::Format( _("Saved %s"), filename ), true );
279  }
280  else {
281  context.Error(
282  wxString::Format( _("Error trying to save file: %s"), filename ) );
283  return false;
284  }
285  return true;
286 }
287 
289  const CommandContext & context,
290  ToolManager *man, int type, const wxString &name)
291 {
292  bool visible = man->IsVisible(type);
293  if (!visible) {
294  man->ShowHide(type);
295  Yield();
296  }
297 
298  wxWindow *w = man->GetToolBar(type);
299  int x = 0, y = 0;
300  int width, height;
301 
302  w->ClientToScreen(&x, &y);
303  w->GetParent()->ScreenToClient(&x, &y);
304  w->GetClientSize(&width, &height);
305 
306  bool result = Capture(context, name, w, wxRect(x, y, width, height));
307 
308  if (!visible) {
309  man->ShowHide(type);
310  if (mIgnore)
311  mIgnore->Raise();
312  }
313  return result;
314 }
315 
317  const CommandContext & context,
318  wxWindow *win, const wxString &mFileName)
319 {
320  int x = 0, y = 0;
321  int width, height;
322 
323  win->ClientToScreen(&x, &y);
324  win->GetParent()->ScreenToClient(&x, &y);
325  win->GetClientSize(&width, &height);
326 
327  return Capture(context, mFileName, win, wxRect(x, y, width, height));
328 }
329 
331  const CommandContext & context,
332  wxMenu * pMenu, int Id, int depth ){
333  static_cast<void>(Id);//compiler food.
334 
335  if( !pMenu )
336  return;
337 
338  wxMenuItemList list = pMenu->GetMenuItems();
339  size_t lcnt = list.GetCount();
340  wxMenuItem * item;
341  wxString Label;
342  wxString Accel;
343 
344  for (size_t lndx = 0; lndx < lcnt; lndx++) {
345  item = list.Item(lndx)->GetData();
346  Label = item->GetItemLabelText();
347  Accel = item->GetItemLabel();
348  if( Accel.Contains("\t") )
349  Accel = Accel.AfterLast('\t');
350  else
351  Accel = "";
352  if( item->IsSeparator() )
353  Label = "----";
354  int flags = 0;
355  if (item->IsSubMenu())
356  flags +=1;
357  if (item->IsCheck() && item->IsChecked())
358  flags +=2;
359 
360  if (item->IsSubMenu()) {
361  pMenu = item->GetSubMenu();
362  ExploreMenu( context, pMenu, item->GetId(), depth+1 );
363  }
364  }
365 }
366 
367 // Handed a dialog, which it is given the option to capture.
368 bool ScreenshotCommand::MayCapture( wxDialog * pDlg )
369 {
370  if( mIdleHandler == NULL )
371  return false;
372  pDlg->Bind( wxEVT_IDLE, mIdleHandler );
373  mIdleHandler = NULL;
374  pDlg->ShowModal();
375  return true;
376 }
377 
379  const CommandContext & context,
380  wxWindow * pWin )
381 {
382  wxDialog * pDlg = dynamic_cast<wxDialog*>(pWin);
383  if( !pDlg ){
384  wxLogDebug("Event from bogus dlg" );
385  return;
386  }
387 
388  wxPoint Pos = pDlg->GetScreenPosition();
389  wxSize Siz = pDlg->GetSize();
390  wxString Title = pDlg->GetTitle();
391 
392  // Remove '/' from "Sliding Time Scale/Pitch Shift..."
393  // and any other effects that have illegal filename chanracters.
394  Title.Replace( "/", "" );
395  Title.Replace( ":", "" );
396  wxString Name = mDirToWriteTo + Title + ".png";
397 
398  wxLogDebug("Taking screenshot of window %s (%i,%i,%i,%i)", Name,
399  Pos.x, Pos.y, Siz.x, Siz.y );
400  // This delay is needed, as dialogs take a moment or two to fade in.
401  wxMilliSleep( 400 );
402  // JKC: The border of 7 pixels was determined from a trial capture and then measuring
403  // in the GIMP. I'm unsure where the border comes from.
404  Capture( context, Name, pDlg, wxRect((int)Pos.x+7, (int)Pos.y, (int)Siz.x-14, (int)Siz.y-7) );
405 
406  // We've captured the dialog, so now dismiss the dialog.
407  wxCommandEvent Evt( wxEVT_BUTTON, wxID_CANCEL );
408  pDlg->GetEventHandler()->AddPendingEvent( Evt );
409 }
410 
412  const CommandContext & context,
413  AudacityProject * pProject, const wxString &mFileName ){
414  (void)&mFileName;//compiler food.
415  (void)&context;
416  CommandManager * pMan = pProject->GetCommandManager();
417 
418  // Yucky static variables. Is there a better way? The problem is that we need the
419  // idle callback to know more about what to do.
420 #ifdef __WXMSW__
421  mDirToWriteTo = mFileName.BeforeLast('\\') + "\\";
422 #else
423  mDirToWriteTo = mFileName.BeforeLast('/') + "/";
424 #endif
425  mpShooter = this;
426  const int nPrefsPages = 19;
427 
428  for( int i=0;i<nPrefsPages;i++){
429  // The handler is cleared each time it is used.
431  gPrefs->Write(wxT("/Prefs/PrefsCategory"), (long)i);
432  gPrefs->Flush();
433  wxString Command = "Preferences";
434  const CommandContext context( *pProject );
435  if( !pMan->HandleTextualCommand( Command, context, AlwaysEnabledFlag, AlwaysEnabledFlag ) )
436  {
437  wxLogDebug("Command %s not found", Command );
438  }
439  // This sleep is not needed, but gives user a chance to see the
440  // dialogs as they whizz by.
441  wxMilliSleep( 200 );
442  }
443 }
444 
446  const CommandContext & context,
447  AudacityProject * pProject, const wxString &mFileName )
448 {
449  (void)pProject;
450  (void)&mFileName;//compiler food.
451  (void)&context;
452 #define TRICKY_CAPTURE
453 #define CAPTURE_NYQUIST_TOO
454  // Commented out the effects that don't have dialogs.
455  // Also any problematic ones,
456  const wxString EffectNames[] = {
457 #ifdef TRICKY_CAPTURE
458  //"Contrast...", // renamed
459  "ContrastAnalyser",
460  //"Plot Spectrum...", // renamed
461  "PlotSpectrum",
462 
463  "Auto Duck...", // needs a track below.
464  //"Spectral edit multi tool",
465  "Spectral edit parametric EQ...", // Needs a spectral selection.
466  "Spectral edit shelves...",
467 
468  //"Noise Reduction...", // Exits twice...
469  //"SC4...", //Has 'Close' rather than 'Cancel'.
470 #endif
471  "Amplify...",
472  "Bass and Treble...",
473  "Change Pitch...",
474  "Change Speed...",
475  "Change Tempo...",
476  "Click Removal...",
477  "Compressor...",
478  "Distortion...",
479  "Echo...",
480  "Equalization...",
481  //"Fade In",
482  //"Fade Out",
483  //"Invert",
484  "Normalize...",
485  "Paulstretch...",
486  "Phaser...",
487  //"Repair",
488  "Repeat...",
489  "Reverb...",
490  //"Reverse",
491  "Sliding Stretch...",
492  "Truncate Silence...",
493  "Wahwah...",
494  // Sole LADSPA effect...
495 #ifdef CAPTURE_NYQUIST_TOO
496  "Adjustable Fade...",
497  "Clip Fix...",
498  //"Crossfade Clips",
499  "Crossfade Tracks...",
500  "Delay...",
501  "High Pass Filter...",
502  "Limiter...",
503  "Low Pass Filter...",
504  "Notch Filter...",
505  "Nyquist Prompt...",
506  //"Studio Fade Out",
507  "Tremolo...",
508  "Vocal Reduction and Isolation...",
509  "Vocal Remover...",
510  "Vocoder...",
511 #endif
512  // Generators.....
513  "Chirp...",
514  "DTMF Tones...",
515  "Noise...",
516  "Silence...",
517  "Tone...",
518 #ifdef CAPTURE_NYQUIST_TOO
519  "Pluck...",
520  "Rhythm Track...",
521  "Risset Drum...",
522  "Sample Data Import...",
523 #endif
524  // Analyzers...
525  "Find Clipping...",
526 #ifdef CAPTURE_NYQUIST_TOO
527  "Beat Finder...",
528  "Regular Interval Labels...",
529  "Sample Data Export...",
530  "Silence Finder...",
531  "Sound Finder...",
532 #endif
533  };
534  wxArrayString Commands( sizeof(EffectNames)/sizeof(EffectNames[0]), EffectNames );
535  CaptureCommands( context, Commands );
536 }
537 
539  const CommandContext & context,
540  AudacityProject * pProject, const wxString &mFileName )
541 {
542  (void)pProject;
543  (void)&mFileName;//compiler food.
544  (void)&context;
545 
546  const wxString ScriptablesNames[] = {
547  "SelectTime",
548  "SelectFrequencies",
549  "SelectTracks",
550  "SetTrackStatus",
551  "SetTrackAudio",
552  "SetTrackVisuals",
553  "GetPreference",
554  "SetPreference",
555  "SetClip",
556  "SetEnvelope",
557  "SetLabel",
558  "SetProject",
559 
560  "Select",
561  "SetTrack",
562  "GetInfo",
563  "Message",
564  "Help", // Help on individual commands
565  "Import2",
566  "Export2",
567  "OpenProject2",
568  "SaveProject2",
569  "Drag",
570  "CompareAudio",
571  "Screenshot",
572  };
573 
574  wxArrayString Commands( sizeof(ScriptablesNames)/sizeof(ScriptablesNames[0]), ScriptablesNames );
575  CaptureCommands( context, Commands );
576 
577 }
578 
579 
581  const CommandContext & context, wxArrayString & Commands ){
582  AudacityProject * pProject = context.GetProject();
583  CommandManager * pMan = pProject->GetCommandManager();
584  wxString Str;
585  // Yucky static variables. Is there a better way? The problem is that we need the
586  // idle callback to know more about what to do.
587 #ifdef __WXMSW__
588  mDirToWriteTo = mFileName.BeforeLast('\\') + "\\";
589 #else
590  mDirToWriteTo = mFileName.BeforeLast('/') + "/";
591 #endif
592  mpShooter = this;
593 
594  for( size_t i=0;i<Commands.GetCount();i++){
595  // The handler is cleared each time it is used.
597  Str = Commands[i];
598  const CommandContext context( *pProject );
599  if( !pMan->HandleTextualCommand( Str, context, AlwaysEnabledFlag, AlwaysEnabledFlag ) )
600  {
601  wxLogDebug("Command %s not found", Str);
602  }
603  // This particular sleep is not needed, but gives user a chance to see the
604  // dialogs as they whizz by.
605  wxMilliSleep( 200 );
606  }
607 }
608 
609 wxString ScreenshotCommand::MakeFileName(const wxString &path, const wxString &basename)
610 {
611  // If the path is a full file name, then use it.
612  if( path.EndsWith( ".png" ) )
613  return path;
614 
615  // Otherwise make up a file name that has not been used already.
616  wxFileName prefixPath;
617  prefixPath.AssignDir(path);
618  wxString prefix = prefixPath.GetPath
619  (wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR);
620 
621  wxString filename;
622  int i = 0;
623  do {
624  filename.Printf(wxT("%s%s%03d.png"),
625  prefix, basename, i);
626  i++;
627  } while (::wxFileExists(filename));
628 
629  return filename;
630 }
631 
633 {
634  // Read the parameters that were passed in
635  mFilePath = mPath;
637 
638  // Build a suitable filename
640  kCaptureWhatStrings[ mCaptureMode ].Translation() );
641 
642  if (mBack == kBlue)
643  {
644  mBackground = true;
645  mBackColor = wxColour(51, 102, 153);
646  }
647  else if (mBack == kWhite)
648  {
649  mBackground = true;
650  mBackColor = wxColour(255, 255, 255);
651  }
652  else
653  {
654  mBackground = false;
655  }
656 
657 }
658 
659 wxRect ScreenshotCommand::GetWindowRect(wxTopLevelWindow *w){
660  int x = 0, y = 0;
661  int width, height;
662 
663  w->ClientToScreen(&x, &y);
664  w->GetClientSize(&width, &height);
665  return wxRect( x,y,width,height);
666 }
667 
668 wxRect ScreenshotCommand::GetFullWindowRect(wxTopLevelWindow *w){
669 
670  wxRect r = w->GetRect();
671  r.SetPosition(w->GetScreenPosition());
672  r = w->GetScreenRect();
673 
674 #if defined(__WXGTK__)
675  // In wxGTK, we need to include decoration sizes
676  r.width += (wxSystemSettings::GetMetric(wxSYS_BORDER_X, w) * 2);
677  r.height += wxSystemSettings::GetMetric(wxSYS_CAPTION_Y, w) +
678  wxSystemSettings::GetMetric(wxSYS_BORDER_Y, w);
679 #endif
681  {
682  // background colour not selected but we want a background
683  wxRect b = GetBackgroundRect();
684  r.x = (r.x - b.x) >= 0 ? (r.x - b.x): 0;
685  r.y = (r.y - b.y) >= 0 ? (r.y - b.y): 0;
686  r.width += b.width;
687  r.height += b.height;
688  }
689 
690  return r;
691 }
692 
694  int width, height;
695  wxDisplaySize(&width, &height);
696 
697  return wxRect( 0,0,width,height);
698 }
699 
701  //AdornedRulerPanel *ruler = panel->mRuler;
702 
703  int h = panel->mRuler->GetRulerHeight();
704  int x = 0, y = -h;
705  int width, height;
706 
707  panel->ClientToScreen(&x, &y);
708  panel->GetParent()->ScreenToClient(&x, &y);
709  panel->GetClientSize(&width, &height);
710  return wxRect(x, y, width, height + h);
711 }
712 
714  int x = 0, y = 0;
715  int width, height;
716 
717  ruler->ClientToScreen(&x, &y);
718  ruler->GetParent()->ScreenToClient(&x, &y);
719  ruler->GetClientSize(&width, &height);
720  height = ruler->GetRulerHeight();
721  return wxRect( x, y, width, height);
722 }
723 
725  int x = 0, y = 0;
726  int width, height;
727 
728  panel->ClientToScreen(&x, &y);
729  panel->GetParent()->ScreenToClient(&x, &y);
730  panel->GetClientSize(&width, &height);
731 
732  return wxRect( x, y, width, height);
733 }
734 
736  auto FindRectangle = []( TrackPanel &panel, Track &t )
737  {
738  // This rectangle omits the focus ring about the track, and
739  // also within that, a narrow black border with a "shadow" below and
740  // to the right
741  wxRect rect = panel.FindTrackRect( &t, false );
742 
743  // Enlarge horizontally.
744  // PRL: perhaps it's one pixel too much each side, including some gray
745  // beyond the yellow?
746  rect.x = 0;
747  panel.GetClientSize(&rect.width, nullptr);
748 
749  // Enlarge vertically, enough to enclose the yellow focus border pixels
750  // Omit the outermost ring of gray pixels
751 
752  // (Note that TrackPanel paints its focus over the "top margin" of the
753  // rectangle allotted to the track, according to Track::GetY() and
754  // Track::GetHeight(), but also over the margin of the next track.)
755 
756  rect.height += kBottomMargin;
757  int dy = kTopMargin - 1;
758  rect.Inflate( 0, dy );
759 
760  // Reposition it relative to parent of panel
761  rect.SetPosition(
762  panel.GetParent()->ScreenToClient(
763  panel.ClientToScreen(
764  rect.GetPosition())));
765 
766  return rect;
767  };
768 
769  TrackListIterator iter(pProj->GetTracks());
770  int count = 0;
771  for (auto t = iter.First(); t; t = iter.Next()) {
772  count += 1;
773  if( count > n )
774  {
775  wxRect r = FindRectangle( *panel, *t );
776  return r;
777  }
778  if( t->GetLinked() ){
779  t = iter.Next();
780  if( !t )
781  break;
782  }
783  }
784  return wxRect( 0,0,0,0);
785 }
786 
787 wxString ScreenshotCommand::WindowFileName(AudacityProject * proj, wxTopLevelWindow *w){
788  if (w != proj && w->GetTitle() != wxT("")) {
790  kCaptureWhatStrings[ mCaptureMode ].Translation() +
791  (wxT("-") + w->GetTitle() + wxT("-")));
792  }
793  return mFileName;
794 }
795 
797 {
799  //Don't reset the toolbars to a known state.
800  //We will be capturing variations of them.
801  //context.GetProject()->GetToolManager()->Reset();
802 
803  wxTopLevelWindow *w = GetFrontWindow(context.GetProject());
804  if (!w)
805  return false;
806 
807  TrackPanel *panel = context.GetProject()->GetTrackPanel();
808  AdornedRulerPanel *ruler = panel->mRuler;
809 
810  int nTracks = context.GetProject()->GetTracks()->size();
811 
812  int x1,y1,x2,y2;
813  w->ClientToScreen(&x1, &y1);
814  panel->ClientToScreen(&x2, &y2);
815 
816  wxPoint p( x2-x1, y2-y1);
817 
818  switch (mCaptureMode) {
819  case kwindow:
820  return Capture(context, WindowFileName( context.GetProject(), w ) , w, GetWindowRect(w));
821  case kfullwindow:
822  case kwindowplus:
823  return Capture(context, WindowFileName( context.GetProject(), w ) , w, GetFullWindowRect(w));
824  case kfullscreen:
825  return Capture(context, mFileName, w,GetScreenRect());
826  case ktoolbars:
827  return CaptureDock(context, context.GetProject()->GetToolManager()->GetTopDock(), mFileName);
828  case kscriptables:
829  CaptureScriptables(context, context.GetProject(), mFileName);
830  break;
831  case keffects:
832  CaptureEffects(context, context.GetProject(), mFileName);
833  break;
834  case kpreferences:
835  CapturePreferences(context, context.GetProject(), mFileName);
836  break;
837  case kselectionbar:
838  return CaptureToolbar(context, context.GetProject()->GetToolManager(), SelectionBarID, mFileName);
839  case kspectralselection:
841  case ktools:
842  return CaptureToolbar(context, context.GetProject()->GetToolManager(), ToolsBarID, mFileName);
843  case ktransport:
844  return CaptureToolbar(context, context.GetProject()->GetToolManager(), TransportBarID, mFileName);
845  case kmixer:
846  return CaptureToolbar(context, context.GetProject()->GetToolManager(), MixerBarID, mFileName);
847  case kmeter:
848  return CaptureToolbar(context, context.GetProject()->GetToolManager(), MeterBarID, mFileName);
849  case krecordmeter:
850  return CaptureToolbar(context, context.GetProject()->GetToolManager(), RecordMeterBarID, mFileName);
851  case kplaymeter:
852  return CaptureToolbar(context, context.GetProject()->GetToolManager(), PlayMeterBarID, mFileName);
853  case kedit:
854  return CaptureToolbar(context, context.GetProject()->GetToolManager(), EditBarID, mFileName);
855  case kdevice:
856  return CaptureToolbar(context, context.GetProject()->GetToolManager(), DeviceBarID, mFileName);
857  case ktranscription:
858  return CaptureToolbar(context, context.GetProject()->GetToolManager(), TranscriptionBarID, mFileName);
859  case kscrub:
860  return CaptureToolbar(context, context.GetProject()->GetToolManager(), ScrubbingBarID, mFileName);
861  case ktrackpanel:
862  return Capture(context, mFileName, panel, GetPanelRect(panel));
863  case kruler:
864  return Capture(context, mFileName, ruler, GetRulerRect(ruler) );
865  case ktracks:
866  return Capture(context, mFileName, panel, GetTracksRect(panel));
867  case kfirsttrack:
868  return Capture(context, mFileName, panel, GetTrackRect( context.GetProject(), panel, 0 ) );
869  case ksecondtrack:
870  return Capture(context, mFileName, panel, GetTrackRect( context.GetProject(), panel, 1 ) );
871  case ktracksplus:
872  { wxRect r = GetTracksRect(panel);
873  r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
874  r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
875  return Capture(context, mFileName, panel, r);
876  }
877  case kfirsttrackplus:
878  { wxRect r = GetTrackRect(context.GetProject(), panel, 0 );
879  r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
880  r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
881  return Capture(context, mFileName, panel, r );
882  }
883  case kfirsttwotracks:
884  { wxRect r = GetTrackRect( context.GetProject(), panel, 0 );
885  r = r.Union( GetTrackRect( context.GetProject(), panel, 1 ));
886  return Capture(context, mFileName, panel, r );
887  }
888  case kfirstthreetracks:
889  { wxRect r = GetTrackRect( context.GetProject(), panel, 0 );
890  r = r.Union( GetTrackRect( context.GetProject(), panel, 2 ));
891  return Capture(context, mFileName, panel, r );
892  }
893  case kfirstfourtracks:
894  { wxRect r = GetTrackRect( context.GetProject(), panel, 0 );
895  r = r.Union( GetTrackRect( context.GetProject(), panel, 3 ));
896  return Capture(context, mFileName, panel, r );
897  }
898  case kalltracks:
899  { wxRect r = GetTrackRect( context.GetProject(), panel, 0 );
900  r = r.Union( GetTrackRect( context.GetProject(), panel, nTracks-1 ));
901  return Capture(context, mFileName, panel, r );
902  }
903  case kalltracksplus:
904  { wxRect r = GetTrackRect( context.GetProject(), panel, 0 );
905  r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
906  r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
907  r = r.Union( GetTrackRect( context.GetProject(), panel, nTracks-1 ));
908  return Capture(context, mFileName, panel, r );
909  }
910  default:
911  return false;
912  }
913 
914  return true;
915 }
wxChoice * TieChoice(const wxString &Prompt, WrappedType &WrappedRef, const wxArrayString *pChoices)
ToolDock * GetTopDock()
virtual ShuttleParams & OptionalN(bool &var)
Definition: Shuttle.h:72
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
AudacityProject * GetProject() const
bool DefineParams(ShuttleParams &S) override
bool CaptureDock(const CommandContext &Context, wxWindow *win, const wxString &fileName)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
wxArrayString LocalizedStrings(const IdentInterfaceSymbol strings[], size_t nStrings)
Definition: Internat.cpp:303
wxRect FindTrackRect(const Track *target, bool label)
bool CaptureToolbar(const CommandContext &Context, ToolManager *man, int type, const wxString &name)
void EndMultiColumn()
virtual void Status(const wxString &message, bool bFlush=false) const
bool HandleTextualCommand(const wxString &Str, const CommandContext &context, CommandFlag flags, CommandMask mask)
void CaptureWindowOnIdle(const CommandContext &context, wxWindow *pWin)
#define XO(s)
Definition: Internat.h:33
void ExploreMenu(const CommandContext &context, wxMenu *pMenu, int Id, int depth)
void CaptureScriptables(const CommandContext &Context, AudacityProject *pProject, const wxString &fileName)
wxRect GetWindowRect(wxTopLevelWindow *w)
int GetRulerHeight()
Definition: Ruler.h:333
CommandContext provides addiitonal information to an 'Apply()' command. It provides the project...
wxRect GetTrackRect(AudacityProject *pProj, TrackPanel *panel, int n)
Shuttle that deals with parameters. This is a base class with lots of virtual functions that do nothi...
Definition: Shuttle.h:60
CommandManager * GetCommandManager()
Definition: Project.h:346
void PopulateOrExchange(ShuttleGui &S) override
void ShowHide(int type)
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
size_t size() const
Definition: Track.cpp:1234
virtual void DefineEnum(int &var, const wxChar *key, const int vdefault, const IdentInterfaceSymbol strings[], size_t nStrings)
Definition: Shuttle.cpp:346
static ScreenshotCommand * mpShooter
The TrackPanel class coordinates updates and operations on the main part of the screen which contains...
Definition: TrackPanel.h:245
static const IdentInterfaceSymbol kBackgroundStrings[ScreenshotCommand::nBackgrounds]
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
void CaptureEffects(const CommandContext &Context, AudacityProject *pProject, const wxString &fileName)
ToolManager * GetToolManager()
Definition: Project.h:704
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:101
CommandManager implements a system for organizing all user-callable commands.
virtual void Define(bool &var, const wxChar *key, const bool vdefault, const bool vmin=false, const bool vmax=false, const bool vscl=false)
Definition: Shuttle.cpp:339
wxString WindowFileName(AudacityProject *proj, wxTopLevelWindow *w)
wxRect GetTracksRect(TrackPanel *panel)
bool Capture(const CommandContext &Context, const wxString &basename, wxWindow *window, wxRect rect, bool bg=false)
IdentInterfaceSymbol pairs a persistent string identifier used internally with an optional...
bool IsVisible(int type)
void CapturePreferences(const CommandContext &Context, AudacityProject *pProject, const wxString &fileName)
Implements a command for capturing various areas of the screen or project window. It's one big if-els...
static const IdentInterfaceSymbol kCaptureWhatStrings[ScreenshotCommand::nCaptureWhats]
An iterator for a TrackList.
Definition: Track.h:401
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
wxCheckBox * TieCheckBox(const wxString &Prompt, WrappedType &WrappedRef)
const wxChar * name
Definition: Distortion.cpp:94
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
void IdleHandler(wxIdleEvent &event)
virtual void Error(const wxString &message) const
TrackPanel * GetTrackPanel()
Definition: Project.h:307
This is an Audacity Specific ruler panel which additionally has border, selection markers...
Definition: Ruler.h:316
static bool MayCapture(wxDialog *pDlg)
wxSizerItem * AddSpace(int width, int height)
wxRect GetRulerRect(AdornedRulerPanel *ruler)
TrackList * GetTracks()
Definition: Project.h:192
static void Yield()
AdornedRulerPanel * mRuler
Definition: TrackPanel.h:453
class ToolManager
Definition: ToolManager.h:45
static void(* mIdleHandler)(wxIdleEvent &event)
static void SetIdleHandler(void(*pHandler)(wxIdleEvent &event))
wxRect GetFullWindowRect(wxTopLevelWindow *w)
wxTopLevelWindow * GetFrontWindow(AudacityProject *project)
ToolBar * GetToolBar(int type) const
wxTextCtrl * TieTextBox(const wxString &Prompt, WrappedType &WrappedRef, const int nChars)
wxRect GetPanelRect(TrackPanel *panel)
virtual bool Apply()
wxString MakeFileName(const wxString &path, const wxString &basename)
void CaptureCommands(const CommandContext &Context, wxArrayString &Commands)