Audacity 3.2.0
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 or later - see LICENSE.txt
6
7 Dominic Mazzoni
8 Dan Horgan
9 James Crook
10
11******************************************************************//*******************************************************************/
19
20
21#include "ScreenshotCommand.h"
22
23#include <mutex>
24#include <thread>
25
26#include "LoadCommands.h"
27#include "Project.h"
28#include <wx/app.h>
29#include <wx/toplevel.h>
30#include <wx/dcscreen.h>
31#include <wx/dcmemory.h>
32#include <wx/settings.h>
33#include <wx/bitmap.h>
34#include <wx/valgen.h>
35
36#include "ActiveProject.h"
37#include "../AdornedRulerPanel.h"
38#include "../TrackPanel.h"
39#include "../toolbars/ToolManager.h"
40#include "Prefs.h"
41#include "ProjectWindows.h"
42#include "SettingsVisitor.h"
43#include "ShuttleGui.h"
44#include "Track.h"
45#include "../widgets/VetoDialogHook.h"
46#include "CommandContext.h"
47#include "CommandManager.h"
48#include "CommandDispatch.h"
49#include "../CommonCommandFlags.h"
50
52{ XO("Screenshot") };
53
55
56
58{
59 static EnumValueSymbol symbols[]{
60 { XO("Window") },
61 { wxT("FullWindow"), XO("Full Window") },
62 { wxT("WindowPlus"), XO("Window Plus") },
63 { XO("Fullscreen") },
64 { XO("Toolbars") },
65 { XO("Effects") },
66 { XO("Scriptables") },
67 { XO("Preferences") },
68 { XO("Trackpanel") },
69 { XO("Ruler") },
70 { XO("Tracks") },
71 { wxT("FirstTrack"), XO("First Track") },
72 { wxT("FirstTwoTracks"), XO("First Two Tracks") },
73 { wxT("FirstThreeTracks"), XO("First Three Tracks") },
74 { wxT("FirstFourTracks"), XO("First Four Tracks") },
75 { wxT("SecondTrack"), XO("Second Track") },
76 { wxT("TracksPlus"), XO("Tracks Plus") },
77 { wxT("FirstTrackPlus"), XO("First Track Plus") },
78 { wxT("AllTracks"), XO("All Tracks") },
79 { wxT("AllTracksPlus"), XO("All Tracks Plus") },
80 };
81
82 if (mSymbols.empty()) {
83 // Compute the table of strings once
84
85 // Some fixed choices
86 copy(std::begin(symbols), std::end(symbols), back_inserter(mSymbols));
87 auto nFixed = mSymbols.size();
88
89 // Discover the set of toolbars -- don't hard-code it here
90 /*
91 There is no context passed in for a project.
92 So this need to use the active project is unfortunate, but the set of
93 toolbars and their identifying strings should not vary among projects
94 */
95 auto pProject = ::GetActiveProject().lock();
96 if ( pProject ) {
97 // Toolbars will be sorted by textual ID.
98 // Macro programmers do use these English ids no matter what their
99 // preferred language is.
100 ToolManager::Get(*pProject).ForEach([&](ToolBar *pBar){
101 mSymbols.emplace_back( pBar->GetSection(), pBar->GetLabel() );
102 });
103 }
104 }
105
106 return mSymbols;
107};
108
109
110static const EnumValueSymbol
112{
113 // These are acceptable dual purpose internal/visible names
114 { XO("Blue") },
115 /* i18n-hint: This really means the color, not as in "white noise" */
116 { XC("White", "color") },
117 { XO("None") },
118};
119
120
122{
123 mbBringToTop=true;
124 mIgnore=NULL;
125
127}
128
129template<bool Const>
131 auto strings = kCaptureWhatStrings();
132 S.Define( mPath, wxT("Path"), wxString{});
133 S.DefineEnum( mWhat, wxT("CaptureWhat"), kwindow, strings.data(), strings.size() );
134 S.DefineEnum( mBack, wxT("Background"), kNone, kBackgroundStrings, nBackgrounds );
135 S.Define( mbBringToTop, wxT("ToTop"), true );
136 return true;
137};
138
140 { return VisitSettings<false>(S); }
141
143 { return VisitSettings<true>(S); }
144
146{
147 auto strings = kCaptureWhatStrings();
148 S.AddSpace(0, 5);
149
150 S.StartMultiColumn(2, wxALIGN_CENTER);
151 {
152 S.TieTextBox( XXO("Path:"), mPath);
153 S.TieChoice( XXO("Capture What:"),
154 mWhat, Msgids(strings.data(), strings.size()));
155 S.TieChoice( XXO("Background:"),
157 S.TieCheckBox( XXO("Bring To Top"), mbBringToTop);
158 }
159 S.EndMultiColumn();
160}
161
162// static member variable.
163void (*ScreenshotCommand::mIdleHandler)(wxIdleEvent& event) = NULL;
165// This static variable is used to get from an idle event to the screenshot
166// command that caused the idle event interception to be set up.
168
169// IdleHandler is expected to be called from EVT_IDLE when a dialog has been
170// fully created. Usually the dialog will have been created by invoking
171// an effects gui.
172void IdleHandler(wxIdleEvent& event){
173 event.Skip();
174 wxWindow * pWin = dynamic_cast<wxWindow*>(event.GetEventObject());
175 wxASSERT( pWin );
176 pWin->Unbind(wxEVT_IDLE, IdleHandler);
178 // We have the relevant window, so go and capture it.
181}
182
184{
187}
188
190{
191 wxWindow *front = NULL;
192 wxWindow *proj = wxGetTopLevelParent(FindProjectFrame(project));
193
194 for (auto & win : wxTopLevelWindows)
195 {
196 win = wxGetTopLevelParent(win);
197 if (win != mIgnore && win != proj && win->IsShown()) {
198 front = win;
199 break;
200 }
201 }
202
203 if (!front || !front->IsTopLevel()) {
204 return (wxTopLevelWindow *)proj;
205 }
206
207 return (wxTopLevelWindow *)front;
208}
209
211{
212 wxRect r;
213
214 r.x = 16;
215 r.y = 16;
216 r.width = r.x * 2;
217 r.height = r.y * 2;
218
219 return r;
220}
221
222static void Yield()
223{
224 using namespace std::chrono;
225 int cnt;
226 for (cnt = 10; cnt && !wxTheApp->Yield(true); cnt--)
227 std::this_thread::sleep_for(10ms);
228 std::this_thread::sleep_for(200ms);
229 for (cnt = 10; cnt && !wxTheApp->Yield(true); cnt--)
230 std::this_thread::sleep_for(10ms);
231}
232
234 const CommandContext & context,
235 const wxString &filename,
236 wxWindow *window, wxRect r,
237 bool bg)
238{
239 int width = r.width;
240 int height = r.height;
241 if( r.width == 0 )
242 return false;
243 if (window ) {
244 wxWindow * win = window;
245 wxTopLevelWindow * top_win= nullptr;
246 if( !window->IsTopLevel())
247 win = wxGetTopLevelParent(window);
248 top_win = dynamic_cast<wxTopLevelWindow*>( win );
249 if( (!bHasBringToTop || mbBringToTop) && (!top_win || !top_win->IsActive()) ){
250 win->Raise();
251 Yield();
252 }
253 }
254
255
256 int screenW, screenH;
257 wxDisplaySize(&screenW, &screenH);
258 // Bug 1378 workaround.
259 // wx 3.0.2 has a bug in Blit from ScreenDC where in default mode
260 // much is drawn transparent - including for example black text!
261 // Forcing 24 bit here is a workaround.
262 wxBitmap full(screenW, screenH, 24);
263
264 wxScreenDC screenDC;
265 wxMemoryDC fullDC;
266
267#if defined(__WXMAC__) && !wxCHECK_VERSION(3, 0, 0)
268 full = DoGetAsBitmap(NULL);
269#else
270 // We grab the whole screen image since there seems to be a problem with
271 // using non-zero source coordinates on OSX. (as of wx2.8.9)
272 fullDC.SelectObject(full);
273 fullDC.Blit(0, 0, screenW, screenH, &screenDC, 0, 0);
274 fullDC.SelectObject(wxNullBitmap);
275#endif
276
277 //wxRect r(x, y, width, height);
278
279
280 // Convert to screen coordinates if needed
281 if (window && window->GetParent() && !window->IsTopLevel()) {
282 r.SetPosition(window->GetParent()->ClientToScreen(r.GetPosition()));
283 }
284
285 // Ensure within bounds (x/y are negative on Windows when maximized)
286 r.Intersect(wxRect(0, 0, screenW, screenH));
287
288 // Extract the actual image
289 wxBitmap part = full.GetSubBitmap(r);
290
291 // Add a background
292 if (bg && mBackground) {
293 wxRect b = GetBackgroundRect();
294
295 wxBitmap back(width + b.width, height + b.height);
296 fullDC.SelectObject(back);
297
298 fullDC.SetBackground(wxBrush(mBackColor, wxBRUSHSTYLE_SOLID));
299 fullDC.Clear();
300
301 fullDC.DrawBitmap(part, b.x, b.y);
302 fullDC.SelectObject(wxNullBitmap);
303
304 part = back;
305 }
306
307 // Save the final image
308 wxImage image = part.ConvertToImage();
309 ::wxBell();
310
311 if (image.SaveFile(filename)) {
312 // flush
313 context.Status( wxString::Format( _("Saved %s"), filename ), true );
314 }
315 else {
316 context.Error(
317 wxString::Format( _("Error trying to save file: %s"), filename ) );
318 return false;
319 }
320 return true;
321}
322
324 const CommandContext & context,
325 ToolManager *man, Identifier type, const wxString &name)
326{
327 bool visible = man->IsVisible(type);
328 if (!visible) {
329 man->ShowHide(type);
330 Yield();
331 }
332
333 wxWindow *w = man->GetToolBar(type);
334 if (!w)
335 return false;
336
337 int x = 0, y = 0;
338 int width, height;
339
340 w->ClientToScreen(&x, &y);
341 w->GetParent()->ScreenToClient(&x, &y);
342 w->GetClientSize(&width, &height);
343
344 bool result = Capture(context, name, w, wxRect(x, y, width, height));
345
346 if (!visible) {
347 man->ShowHide(type);
348 if (mIgnore)
349 mIgnore->Raise();
350 }
351 return result;
352}
353
355 const CommandContext & context,
356 wxWindow *win, const wxString &FileName)
357{
358 int x = 0, y = 0;
359 int width, height;
360
361 win->ClientToScreen(&x, &y);
362 win->GetParent()->ScreenToClient(&x, &y);
363 win->GetClientSize(&width, &height);
364
365 return Capture(context, FileName, win, wxRect(x, y, width, height));
366}
367
368// Handed a dialog, which it is given the option to capture.
369bool ScreenshotCommand::MayCapture( wxDialog * pDlg )
370{
371 if( mIdleHandler == NULL )
372 return false;
373 pDlg->Bind( wxEVT_IDLE, mIdleHandler );
374 mIdleHandler = NULL;
375 pDlg->ShowModal();
376 return true;
377}
378
380 const CommandContext & context,
381 wxWindow * pWin )
382{
383 using namespace std::chrono;
384 wxDialog * pDlg = dynamic_cast<wxDialog*>(pWin);
385 if( !pDlg ){
386 wxLogDebug("Event from bogus dlg" );
387 return;
388 }
389
390 wxPoint Pos = pDlg->GetScreenPosition();
391 wxSize Siz = pDlg->GetSize();
392 wxString Title = pDlg->GetTitle();
393
394 // Remove '/' from "Sliding Time Scale/Pitch Shift..."
395 // and any other effects that have illegal filename characters.
396 Title.Replace( "/", "" );
397 Title.Replace( ":", "" );
398 wxString Name = mDirToWriteTo + Title + ".png";
399
400 wxLogDebug("Taking screenshot of window %s (%i,%i,%i,%i)", Name,
401 Pos.x, Pos.y, Siz.x, Siz.y );
402 // This delay is needed, as dialogs take a moment or two to fade in.
403 std::this_thread::sleep_for(400ms);
404 // JKC: The border of 7 pixels was determined from a trial capture and then measuring
405 // in the GIMP. I'm unsure where the border comes from.
406 Capture( context, Name, pDlg, wxRect((int)Pos.x+7, (int)Pos.y, (int)Siz.x-14, (int)Siz.y-7) );
407
408 // We've captured the dialog, so now dismiss the dialog.
409 wxCommandEvent Evt( wxEVT_BUTTON, wxID_CANCEL );
410 pDlg->GetEventHandler()->AddPendingEvent( Evt );
411}
412
414 const CommandContext & context,
415 AudacityProject * pProject, const wxString &FileName )
416{
417 using namespace std::chrono;
418 (void)&FileName;//compiler food.
419 (void)&context;
420
421 // Yucky static variables. Is there a better way? The problem is that we need the
422 // idle callback to know more about what to do.
423#ifdef __WXMSW__
424 mDirToWriteTo = FileName.BeforeLast('\\') + "\\";
425#else
426 mDirToWriteTo = FileName.BeforeLast('/') + "/";
427#endif
428 mpShooter = this;
429 const int nPrefsPages = 19;
430
431 for( int i=0;i<nPrefsPages;i++){
432 // The handler is cleared each time it is used.
433 SetIdleHandler( context.project );
434 gPrefs->Write(wxT("/Prefs/PrefsCategory"), (long)i);
435 gPrefs->Flush();
436 CommandID Command{ wxT("Preferences") };
437 const CommandContext projectContext( *pProject );
439 Command, projectContext, AlwaysEnabledFlag, true ) )
440 {
441 // using GET in a log message for devs' eyes only
442 wxLogDebug("Command %s not found", Command.GET() );
443 }
444 // This sleep is not needed, but gives user a chance to see the
445 // dialogs as they whizz by.
446 std::this_thread::sleep_for(200ms);
447 }
448}
449
451 const CommandContext & context,
452 AudacityProject * pProject, const wxString &FileName )
453{
454 (void)pProject;
455 (void)&FileName;//compiler food.
456 (void)&context;
457#define TRICKY_CAPTURE
458#define CAPTURE_NYQUIST_TOO
459 // Commented out the effects that don't have dialogs.
460 // Also any problematic ones,
461 CaptureCommands( context, {
462#ifdef TRICKY_CAPTURE
463 //"Contrast...", // renamed
464 "ContrastAnalyser",
465 //"Plot Spectrum...", // renamed
466 "PlotSpectrum",
467
468 "Auto Duck...", // needs a track below.
469 //"Spectral Edit Multi Tool",
470 "Spectral Edit Parametric EQ...", // Needs a spectral selection.
471 "Spectral Edit Shelves...",
472
473 //"Noise Reduction...", // Exits twice...
474 //"SC4...", //Has 'Close' rather than 'Cancel'.
475#endif
476 "Amplify...",
477 "Bass and Treble...",
478 "Change Pitch...",
479 "Change Speed...",
480 "Change Tempo...",
481 "Click Removal...",
482 "Compressor...",
483 "Distortion...",
484 "Echo...",
485 //"Equalization...",
486 "Filter Curve EQ...",
487 "Graphic EQ...",
488 //"Fade In",
489 //"Fade Out",
490 //"Invert",
491 "Normalize...",
492 "Paulstretch...",
493 "Phaser...",
494 //"Repair",
495 "Repeat...",
496 "Reverb...",
497 //"Reverse",
498 "Sliding Stretch...",
499 "Truncate Silence...",
500 "Wahwah...",
501 // Sole LADSPA effect...
502#ifdef CAPTURE_NYQUIST_TOO
503 "Adjustable Fade...",
504 "Clip Fix...",
505 //"Crossfade Clips",
506 "Crossfade Tracks...",
507 "Delay...",
508 "High Pass Filter...",
509 "Limiter...",
510 "Low Pass Filter...",
511 "Notch Filter...",
512 "Nyquist Effects Prompt...",
513 //"Studio Fade Out",
514 "Tremolo...",
515 "Vocal Reduction and Isolation...",
516 "Vocal Remover...",
517 "Vocoder...",
518#endif
519 // Generators.....
520 "Chirp...",
521 "DTMF Tones...",
522 "Noise...",
523 "Silence...",
524 "Tone...",
525#ifdef CAPTURE_NYQUIST_TOO
526 "Pluck...",
527 "Rhythm Track...",
528 "Risset Drum...",
529 "Sample Data Import...",
530#endif
531 // Analyzers...
532 "Find Clipping...",
533#ifdef CAPTURE_NYQUIST_TOO
534 "Beat Finder...",
535 "Label Sounds...",
536 "Regular Interval Labels...",
537 "Sample Data Export...",
538#endif
539 } );
540}
541
543 const CommandContext & context,
544 AudacityProject * pProject, const wxString &FileName )
545{
546 (void)pProject;
547 (void)&FileName;//compiler food.
548 (void)&context;
549
550 CaptureCommands( context, {
551 "SelectTime",
552 "SelectFrequencies",
553 "SelectTracks",
554 "SetTrackStatus",
555 "SetTrackAudio",
556 "SetTrackVisuals",
557 "GetPreference",
558 "SetPreference",
559 "SetClip",
560 "SetEnvelope",
561 "SetLabel",
562 "SetProject",
563
564 "Select",
565 "SetTrack",
566 "GetInfo",
567 "Message",
568 "Help", // Help on individual commands
569 "Import2",
570 "Export2",
571 "OpenProject2",
572 "SaveProject2",
573 "Drag",
574 "CompareAudio",
575 "Screenshot",
576 } );
577
578}
579
580
582 const CommandContext & context, const wxArrayStringEx & Commands )
583{
584 using namespace std::chrono;
585 AudacityProject * pProject = &context.project;
587 wxString Str;
588 // Yucky static variables. Is there a better way? The problem is that we need the
589 // idle callback to know more about what to do.
590#ifdef __WXMSW__
591 mDirToWriteTo = mFileName.BeforeLast('\\') + "\\";
592#else
593 mDirToWriteTo = mFileName.BeforeLast('/') + "/";
594#endif
595 mpShooter = this;
596
597 for( size_t i=0;i<Commands.size();i++){
598 // The handler is cleared each time it is used.
599 SetIdleHandler( context.project );
600 Str = Commands[i];
601 const CommandContext projectContext( *pProject );
602 if( !manager.HandleTextualCommand( Str, projectContext, AlwaysEnabledFlag, true ) )
603 {
604 wxLogDebug("Command %s not found", Str);
605 }
606 // This particular sleep is not needed, but gives user a chance to see the
607 // dialogs as they whizz by.
608 std::this_thread::sleep_for(200ms);
609 }
610}
611
612wxString ScreenshotCommand::MakeFileName(const wxString &path, const wxString &basename)
613{
614 // If the path is a full file name, then use it.
615 if( path.EndsWith( ".png" ) )
616 return path;
617
618 // Otherwise make up a file name that has not been used already.
619 wxFileName prefixPath;
620 prefixPath.AssignDir(path);
621 wxString prefix = prefixPath.GetPath
622 (wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR);
623
624 wxString filename;
625 int i = 0;
626 do {
627 filename.Printf(wxT("%s%s%03d.png"),
628 prefix, basename, i);
629 i++;
630 } while (::wxFileExists(filename));
631
632 return filename;
633}
634
636{
637 // Read the parameters that were passed in
640
641 // Build a suitable filename
643 kCaptureWhatStrings()[ mCaptureMode ].Translation() );
644
645 if (mBack == kBlue)
646 {
647 mBackground = true;
648 mBackColor = wxColour(51, 102, 153);
649 }
650 else if (mBack == kWhite)
651 {
652 mBackground = true;
653 mBackColor = wxColour(255, 255, 255);
654 }
655 else
656 {
657 mBackground = false;
658 }
659
660}
661
662wxRect ScreenshotCommand::GetWindowRect(wxTopLevelWindow *w){
663 int x = 0, y = 0;
664 int width, height;
665
666 w->ClientToScreen(&x, &y);
667 w->GetClientSize(&width, &height);
668 return wxRect( x,y,width,height);
669}
670
671wxRect ScreenshotCommand::GetFullWindowRect(wxTopLevelWindow *w){
672
673 wxRect r = w->GetRect();
674 r.SetPosition(w->GetScreenPosition());
675 r = w->GetScreenRect();
676
677#if defined(__WXGTK__)
678 // In wxGTK, we need to include decoration sizes
679 r.width += (wxSystemSettings::GetMetric(wxSYS_BORDER_X, w) * 2);
680 r.height += wxSystemSettings::GetMetric(wxSYS_CAPTION_Y, w) +
681 wxSystemSettings::GetMetric(wxSYS_BORDER_Y, w);
682#endif
684 {
685 // background colour not selected but we want a background
686 wxRect b = GetBackgroundRect();
687 r.x = (r.x - b.x) >= 0 ? (r.x - b.x): 0;
688 r.y = (r.y - b.y) >= 0 ? (r.y - b.y): 0;
689 r.width += b.width;
690 r.height += b.height;
691 }
692
693 return r;
694}
695
697 int width, height;
698 wxDisplaySize(&width, &height);
699
700 return wxRect( 0,0,width,height);
701}
702
704 //AdornedRulerPanel *ruler = panel->mRuler;
705
706 int h = panel->GetRuler()->GetRulerHeight();
707 int x = 0, y = -h;
708 int width, height;
709
710 panel->ClientToScreen(&x, &y);
711 panel->GetParent()->ScreenToClient(&x, &y);
712 panel->GetClientSize(&width, &height);
713 return wxRect(x, y, width, height + h);
714}
715
717 int x = 0, y = 0;
718 int width, height;
719
720 ruler->ClientToScreen(&x, &y);
721 ruler->GetParent()->ScreenToClient(&x, &y);
722 ruler->GetClientSize(&width, &height);
723 height = ruler->GetRulerHeight();
724 return wxRect( x, y, width, height);
725}
726
728 int x = 0, y = 0;
729 int width, height;
730
731 panel->ClientToScreen(&x, &y);
732 panel->GetParent()->ScreenToClient(&x, &y);
733 panel->GetClientSize(&width, &height);
734
735 return wxRect( x, y, width, height);
736}
737
739 auto FindRectangle = []( TrackPanel &panel, Track &t )
740 {
741 wxRect rect = panel.FindFocusedTrackRect( &t );
742
743 // Reposition it relative to parent of panel
744 rect.SetPosition(
745 panel.GetParent()->ScreenToClient(
746 panel.ClientToScreen(
747 rect.GetPosition())));
748
749 return rect;
750 };
751
752 int count = 0;
753 for (auto t : TrackList::Get( *pProj )) {
754 count += 1;
755 if( count > n )
756 {
757 wxRect r = FindRectangle( *panel, *t );
758 return r;
759 }
760 }
761 return wxRect( 0,0,0,0);
762}
763
764wxString ScreenshotCommand::WindowFileName(AudacityProject * proj, wxTopLevelWindow *w){
765 if (w != FindProjectFrame(proj) && !w->GetTitle().empty()) {
767 kCaptureWhatStrings()[ mCaptureMode ].Translation() +
768 (wxT("-") + w->GetTitle() + wxT("-")));
769 }
770 return mFileName;
771}
772
774{
776 //Don't reset the toolbars to a known state.
777 //We will be capturing variations of them.
778 //ToolManager::Get( context.project ).Reset();
779
780 wxTopLevelWindow *w = GetFrontWindow(&context.project);
781 if (!w)
782 return false;
783
784 TrackPanel *panel = &TrackPanel::Get( context.project );
785 AdornedRulerPanel *ruler = panel->GetRuler();
786
787 int nTracks = TrackList::Get(context.project).Size();
788
789 int x1,y1,x2,y2;
790 w->ClientToScreen(&x1, &y1);
791 panel->ClientToScreen(&x2, &y2);
792
793 wxPoint p( x2-x1, y2-y1);
794
795 auto &toolManager = ToolManager::Get( context.project );
796
798 switch (mCaptureMode) {
799 case kwindow:
800 return Capture(context, WindowFileName( &context.project, w ) , w, GetWindowRect(w));
801 case kfullwindow:
802 case kwindowplus:
803 return Capture(context, WindowFileName( &context.project, w ) , w, GetFullWindowRect(w));
804 case kfullscreen:
805 return Capture(context, mFileName, w,GetScreenRect());
806 case ktoolbars:
807 return CaptureDock(context, toolManager.GetTopDock(), mFileName);
808 case kscriptables:
809 CaptureScriptables(context, &context.project, mFileName);
810 return true;
811 case keffects:
812 CaptureEffects(context, &context.project, mFileName);
813 return true;
814 case kpreferences:
815 CapturePreferences(context, &context.project, mFileName);
816 return true;
817 case ktrackpanel:
818 return Capture(context, mFileName, panel, GetPanelRect(panel));
819 case kruler:
820 return Capture(context, mFileName, ruler, GetRulerRect(ruler) );
821 case ktracks:
822 return Capture(context, mFileName, panel, GetTracksRect(panel));
823 case kfirsttrack:
824 return Capture(context, mFileName, panel, GetTrackRect( &context.project, panel, 0 ) );
825 case ksecondtrack:
826 return Capture(context, mFileName, panel, GetTrackRect( &context.project, panel, 1 ) );
827 case ktracksplus:
828 { wxRect r = GetTracksRect(panel);
829 r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
830 r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
831 return Capture(context, mFileName, panel, r);
832 }
833 case kfirsttrackplus:
834 { wxRect r = GetTrackRect(&context.project, panel, 0 );
835 r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
836 r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
837 return Capture(context, mFileName, panel, r );
838 }
839 case kfirsttwotracks:
840 { wxRect r = GetTrackRect( &context.project, panel, 0 );
841 r = r.Union( GetTrackRect( &context.project, panel, 1 ));
842 return Capture(context, mFileName, panel, r );
843 }
845 { wxRect r = GetTrackRect( &context.project, panel, 0 );
846 r = r.Union( GetTrackRect( &context.project, panel, 2 ));
847 return Capture(context, mFileName, panel, r );
848 }
849 case kfirstfourtracks:
850 { wxRect r = GetTrackRect( &context.project, panel, 0 );
851 r = r.Union( GetTrackRect( &context.project, panel, 3 ));
852 return Capture(context, mFileName, panel, r );
853 }
854 case kalltracks:
855 { wxRect r = GetTrackRect( &context.project, panel, 0 );
856 r = r.Union( GetTrackRect( &context.project, panel, nTracks-1 ));
857 return Capture(context, mFileName, panel, r );
858 }
859 case kalltracksplus:
860 { wxRect r = GetTrackRect( &context.project, panel, 0 );
861 r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
862 r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
863 r = r.Union( GetTrackRect( &context.project, panel, nTracks-1 ));
864 return Capture(context, mFileName, panel, r );
865 }
866 default:
867 return false;
868 }
869 }
870 else if (mCaptureMode < nCaptureWhats + toolManager.CountBars()) {
871 auto id = kCaptureWhatStrings()[mCaptureMode].Internal();
872 return CaptureToolbar(context, &toolManager, id, mFileName);
873 }
874 else
875 return false;
876}
877
878namespace {
879using namespace MenuRegistry;
880
881// Register menu items
882
884 // Note that the PLUGIN_SYMBOL must have a space between words,
885 // whereas the short-form used here must not.
886 // (So if you did write "Compare Audio" for the PLUGIN_SYMBOL name, then
887 // you would have to use "CompareAudio" here.)
888 // i18n-hint: Screenshot in the help menu has a much bigger dialog.
889 Command( wxT("Screenshot"), XXO("Screenshot (short format)..."),
891 wxT("Optional/Extra/Part2/Scriptables2")
892};
893}
AUDACITY_DLL_API std::weak_ptr< AudacityProject > GetActiveProject()
Handle changing of active project and keep global project pointer.
wxImage(22, 22)
wxT("CloseDown"))
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:34
const ReservedCommandFlag & AudioIONotBusyFlag()
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define XC(s, c)
Definition: Internat.h:37
#define _(s)
Definition: Internat.h:73
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
accessors for certain important windows associated with each project
static const AttachedProjectObjects::RegisteredFactory manager
static AudacityProject * pIdleHandlerProject
static const EnumValueSymbol kBackgroundStrings[ScreenshotCommand::nBackgrounds]
void IdleHandler(wxIdleEvent &event)
static void Yield()
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Convenience function often useful when adding choice controls.
const auto project
#define S(N)
Definition: ToChars.cpp:64
declares abstract base class Track, TrackList, and iterators over TrackList
This is an Audacity Specific ruler panel which additionally has border, selection markers,...
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
virtual void Error(const wxString &message) const
virtual void Status(const wxString &message, bool bFlush=false) const
AudacityProject & project
CommandManager implements a system for organizing all user-callable commands.
static CommandManager & Get(AudacityProject &project)
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
typename GlobalVariable< VetoDialogHook, const std::function< bool(wxDialog *) >, nullptr, Options... >::Scope Scope
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
Generates classes whose instances register items at construction.
Definition: Registry.h:388
Implements a command for capturing various areas of the screen or project window. It's one big if-els...
bool CaptureToolbar(const CommandContext &Context, ToolManager *man, Identifier type, const wxString &name)
static ScreenshotCommand * mpShooter
static bool MayCapture(wxDialog *pDlg)
wxRect GetTracksRect(TrackPanel *panel)
void CaptureScriptables(const CommandContext &Context, AudacityProject *pProject, const wxString &fileName)
void CaptureWindowOnIdle(const CommandContext &context, wxWindow *pWin)
static void SetIdleHandler(AudacityProject &project)
static void(* mIdleHandler)(wxIdleEvent &event)
wxRect GetRulerRect(AdornedRulerPanel *ruler)
bool VisitSettings(SettingsVisitorBase< Const > &S)
void PopulateOrExchange(ShuttleGui &S) override
EnumValueSymbols kCaptureWhatStrings()
bool Capture(const CommandContext &Context, const wxString &basename, wxWindow *window, wxRect rect, bool bg=false)
wxString WindowFileName(AudacityProject *proj, wxTopLevelWindow *w)
wxString MakeFileName(const wxString &path, const wxString &basename)
wxTopLevelWindow * GetFrontWindow(AudacityProject *project)
wxRect GetPanelRect(TrackPanel *panel)
wxRect GetTrackRect(AudacityProject *pProj, TrackPanel *panel, int n)
bool CaptureDock(const CommandContext &Context, wxWindow *win, const wxString &fileName)
void CaptureEffects(const CommandContext &Context, AudacityProject *pProject, const wxString &fileName)
EnumValueSymbols mSymbols
wxRect GetWindowRect(wxTopLevelWindow *w)
void CapturePreferences(const CommandContext &Context, AudacityProject *pProject, const wxString &fileName)
wxRect GetFullWindowRect(wxTopLevelWindow *w)
bool Apply(const CommandContext &context) override
static const ComponentInterfaceSymbol Symbol
void CaptureCommands(const CommandContext &Context, const wxArrayStringEx &Commands)
Visitor of effect or command parameters. This is a base class with lots of virtual functions that do ...
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:630
Works with ToolManager and ToolDock to provide a dockable window in which buttons can be placed.
Definition: ToolBar.h:74
class ToolManager
Definition: ToolManager.h:55
static ToolManager & Get(AudacityProject &project)
void ShowHide(Identifier type)
void ForEach(F &&fun)
Visit bars, lexicographically by their textual ids.
Definition: ToolManager.h:102
ToolBar * GetToolBar(const Identifier &type) const
bool IsVisible(Identifier type) const
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:122
size_t Size() const
Definition: Track.h:1241
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
The TrackPanel class coordinates updates and operations on the main part of the screen which contains...
Definition: TrackPanel.h:63
AdornedRulerPanel * GetRuler()
Definition: TrackPanel.h:169
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:233
wxRect FindFocusedTrackRect(const Track *target)
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
AUDACITY_DLL_API bool HandleTextualCommand(const CommandID &Str, const CommandContext &context, CommandFlag flags, bool alwaysEnabled)
AUDACITY_DLL_API void OnAudacityCommand(const CommandContext &ctx)
constexpr auto Command
Definition: MenuRegistry.h:456
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
BuiltinCommandsModule::Registration< ScreenshotCommand > reg
static CommandContext::TargetFactory::SubstituteInUnique< InteractiveOutputTargets > scope
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40