audacia/src/commands/ScreenshotCommand.cpp

911 lines
27 KiB
C++

/**********************************************************************
Audacity - A Digital Audio Editor
Copyright 1999-2018 Audacity Team
License: GPL v2 - see LICENSE.txt
Dominic Mazzoni
Dan Horgan
James Crook
******************************************************************//**
\class ScreenshotCommand
\brief Implements a command for capturing various areas of the screen or
project window. It's one big if-elseif switch statement with lots of
small calculations of rectangles.
*//*******************************************************************/
#include "../Audacity.h"
#include "ScreenshotCommand.h"
#include <mutex>
#include "LoadCommands.h"
#include "../Project.h"
#include <wx/toplevel.h>
#include <wx/dcscreen.h>
#include <wx/dcmemory.h>
#include <wx/settings.h>
#include <wx/bitmap.h>
#include <wx/valgen.h>
#include "../AdornedRulerPanel.h"
#include "../BatchCommands.h"
#include "../TrackPanel.h"
#include "../effects/Effect.h"
#include "../toolbars/ToolManager.h"
#include "../Prefs.h"
#include "../ProjectWindow.h"
#include "../Shuttle.h"
#include "../ShuttleGui.h"
#include "../Track.h"
#include "CommandContext.h"
#include "CommandManager.h"
const ComponentInterfaceSymbol ScreenshotCommand::Symbol
{ XO("Screenshot") };
namespace{ BuiltinCommandsModule::Registration< ScreenshotCommand > reg; }
static const EnumValueSymbol
kCaptureWhatStrings[ ScreenshotCommand::nCaptureWhats ] =
{
{ XO("Window") },
{ wxT("FullWindow"), XO("Full Window") },
{ wxT("WindowPlus"), XO("Window Plus") },
{ XO("Fullscreen") },
{ XO("Toolbars") },
{ XO("Effects") },
{ XO("Scriptables") },
{ XO("Preferences") },
{ XO("Selectionbar") },
{ wxT("SpectralSelection"), XO("Spectral Selection") },
{ XO("Timer") },
{ XO("Tools") },
{ XO("Transport") },
{ XO("Mixer") },
{ XO("Meter") },
{ wxT("PlayMeter"), XO("Play Meter") },
{ wxT("RecordMeter"), XO("Record Meter") },
{ XO("Edit") },
{ XO("Device") },
{ XO("Scrub") },
{ XO("Play-at-Speed") },
{ XO("Trackpanel") },
{ XO("Ruler") },
{ XO("Tracks") },
{ wxT("FirstTrack"), XO("First Track") },
{ wxT("FirstTwoTracks"), XO("First Two Tracks") },
{ wxT("FirstThreeTracks"), XO("First Three Tracks") },
{ wxT("FirstFourTracks"), XO("First Four Tracks") },
{ wxT("SecondTrack"), XO("Second Track") },
{ wxT("TracksPlus"), XO("Tracks Plus") },
{ wxT("FirstTrackPlus"), XO("First Track Plus") },
{ wxT("AllTracks"), XO("All Tracks") },
{ wxT("AllTracksPlus"), XO("All Tracks Plus") },
};
static const EnumValueSymbol
kBackgroundStrings[ ScreenshotCommand::nBackgrounds ] =
{
// These are acceptable dual purpose internal/visible names
{ XO("Blue") },
/* i18n-hint: This really means the color, not as in "white noise" */
{ XC("White", "color") },
{ XO("None") },
};
ScreenshotCommand::ScreenshotCommand()
{
mbBringToTop=true;
mIgnore=NULL;
static std::once_flag flag;
std::call_once( flag, []{
AudacityCommand::SetVetoDialogHook( MayCapture );
Effect::SetVetoDialogHook( MayCapture );
});
}
bool ScreenshotCommand::DefineParams( ShuttleParams & S ){
S.Define( mPath, wxT("Path"), wxT(""));
S.DefineEnum( mWhat, wxT("CaptureWhat"), kwindow,kCaptureWhatStrings, nCaptureWhats );
S.DefineEnum( mBack, wxT("Background"), kNone, kBackgroundStrings, nBackgrounds );
S.Define( mbBringToTop, wxT("ToTop"), true );
return true;
};
void ScreenshotCommand::PopulateOrExchange(ShuttleGui & S)
{
S.AddSpace(0, 5);
S.StartMultiColumn(2, wxALIGN_CENTER);
{
S.TieTextBox( XXO("Path:"), mPath);
S.TieChoice( XXO("Capture What:"),
mWhat, Msgids(kCaptureWhatStrings, nCaptureWhats));
S.TieChoice( XXO("Background:"),
mBack, Msgids(kBackgroundStrings, nBackgrounds));
S.TieCheckBox( XXO("Bring To Top"), mbBringToTop);
}
S.EndMultiColumn();
}
// static member variable.
void (*ScreenshotCommand::mIdleHandler)(wxIdleEvent& event) = NULL;
static AudacityProject *pIdleHandlerProject = nullptr;
// This static variable is used to get from an idle event to the screenshot
// command that caused the idle event interception to be set up.
ScreenshotCommand * ScreenshotCommand::mpShooter=NULL;
// IdleHandler is expected to be called from EVT_IDLE when a dialog has been
// fully created. Usually the dialog will have been created by invoking
// an effects gui.
void IdleHandler(wxIdleEvent& event){
event.Skip();
wxWindow * pWin = dynamic_cast<wxWindow*>(event.GetEventObject());
wxASSERT( pWin );
pWin->Unbind(wxEVT_IDLE, IdleHandler);
CommandContext context( *pIdleHandlerProject );
// We have the relevant window, so go and capture it.
if( ScreenshotCommand::mpShooter )
ScreenshotCommand::mpShooter->CaptureWindowOnIdle( context, pWin );
}
void ScreenshotCommand::SetIdleHandler( AudacityProject &project )
{
mIdleHandler = IdleHandler;
pIdleHandlerProject = &project;
}
wxTopLevelWindow *ScreenshotCommand::GetFrontWindow(AudacityProject *project)
{
wxWindow *front = NULL;
wxWindow *proj = wxGetTopLevelParent( ProjectWindow::Find( project ) );
// JKC: The code below is no longer such a good idea.
// We now have options to directly capture toolbars, effects, preferences.
// We now also may have more than one dialog open, so who is to say
// which one we want to capture? Additionally, as currently written,
// it may capture the screenshot dialog itself (on Linux)
// IF we still keep this code in future, it needs a rethink.
// Possibly as well as the kWindow options, we should offer kDialog options,
// which attempt to do what this once did.
#if 0
// This is kind of an odd hack. There's no method to enumerate all
// possible windows, so we search the whole screen for any windows
// that are not this one and not the given Audacity project and
// if we find anything, we assume that's the dialog the user wants
// to capture.
int width, height, x, y;
wxDisplaySize(&width, &height);
for (x = 0; x < width; x += 50) {
for (y = 0; y < height; y += 50) {
wxWindow *win = wxFindWindowAtPoint(wxPoint(x, y));
if (win) {
win = wxGetTopLevelParent(win);
if (win != mIgnore && win != proj && win->IsShown()) {
front = win;
break;
}
}
}
}
#endif
if (!front || !front->IsTopLevel()) {
return (wxTopLevelWindow *)proj;
}
return (wxTopLevelWindow *)front;
}
wxRect ScreenshotCommand::GetBackgroundRect()
{
wxRect r;
r.x = 16;
r.y = 16;
r.width = r.x * 2;
r.height = r.y * 2;
return r;
}
static void Yield()
{
int cnt;
for (cnt = 10; cnt && !wxTheApp->Yield(true); cnt--) {
wxMilliSleep(10);
}
wxMilliSleep(200);
for (cnt = 10; cnt && !wxTheApp->Yield(true); cnt--) {
wxMilliSleep(10);
}
}
bool ScreenshotCommand::Capture(
const CommandContext & context,
const wxString &filename,
wxWindow *window, wxRect r,
bool bg)
{
int width = r.width;
int height = r.height;
if( r.width == 0 )
return false;
if (window ) {
wxWindow * win = window;
wxTopLevelWindow * top_win= nullptr;
if( !window->IsTopLevel())
win = wxGetTopLevelParent(window);
top_win = dynamic_cast<wxTopLevelWindow*>( win );
if( (!bHasBringToTop || mbBringToTop) && (!top_win || !top_win->IsActive()) ){
win->Raise();
Yield();
}
}
int screenW, screenH;
wxDisplaySize(&screenW, &screenH);
// Bug 1378 workaround.
// wx 3.0.2 has a bug in Blit from ScreenDC where in default mode
// much is drawn transparent - including for example black text!
// Forcing 24 bit here is a workaround.
wxBitmap full(screenW, screenH, 24);
wxScreenDC screenDC;
wxMemoryDC fullDC;
#if defined(__WXMAC__) && !wxCHECK_VERSION(3, 0, 0)
full = DoGetAsBitmap(NULL);
#else
// We grab the whole screen image since there seems to be a problem with
// using non-zero source coordinates on OSX. (as of wx2.8.9)
fullDC.SelectObject(full);
fullDC.Blit(0, 0, screenW, screenH, &screenDC, 0, 0);
fullDC.SelectObject(wxNullBitmap);
#endif
//wxRect r(x, y, width, height);
// Convert to screen coordinates if needed
if (window && window->GetParent() && !window->IsTopLevel()) {
r.SetPosition(window->GetParent()->ClientToScreen(r.GetPosition()));
}
// Ensure within bounds (x/y are negative on Windows when maximized)
r.Intersect(wxRect(0, 0, screenW, screenH));
// Extract the actual image
wxBitmap part = full.GetSubBitmap(r);
// Add a background
if (bg && mBackground) {
wxRect b = GetBackgroundRect();
wxBitmap back(width + b.width, height + b.height);
fullDC.SelectObject(back);
fullDC.SetBackground(wxBrush(mBackColor, wxBRUSHSTYLE_SOLID));
fullDC.Clear();
fullDC.DrawBitmap(part, b.x, b.y);
fullDC.SelectObject(wxNullBitmap);
part = back;
}
// Save the final image
wxImage image = part.ConvertToImage();
::wxBell();
if (image.SaveFile(filename)) {
// flush
context.Status( wxString::Format( _("Saved %s"), filename ), true );
}
else {
context.Error(
wxString::Format( _("Error trying to save file: %s"), filename ) );
return false;
}
return true;
}
bool ScreenshotCommand::CaptureToolbar(
const CommandContext & context,
ToolManager *man, int type, const wxString &name)
{
bool visible = man->IsVisible(type);
if (!visible) {
man->ShowHide(type);
Yield();
}
wxWindow *w = man->GetToolBar(type);
int x = 0, y = 0;
int width, height;
w->ClientToScreen(&x, &y);
w->GetParent()->ScreenToClient(&x, &y);
w->GetClientSize(&width, &height);
bool result = Capture(context, name, w, wxRect(x, y, width, height));
if (!visible) {
man->ShowHide(type);
if (mIgnore)
mIgnore->Raise();
}
return result;
}
bool ScreenshotCommand::CaptureDock(
const CommandContext & context,
wxWindow *win, const wxString &FileName)
{
int x = 0, y = 0;
int width, height;
win->ClientToScreen(&x, &y);
win->GetParent()->ScreenToClient(&x, &y);
win->GetClientSize(&width, &height);
return Capture(context, FileName, win, wxRect(x, y, width, height));
}
// 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(
const CommandContext & context,
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 characters.
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( 400 );
// 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( context, Name, pDlg, wxRect((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(
const CommandContext & context,
AudacityProject * pProject, const wxString &FileName ){
(void)&FileName;//compiler food.
(void)&context;
CommandManager &commandManager = CommandManager::Get( *pProject );
// Yucky static variables. Is there a better way? The problem is that we need the
// idle callback to know more about what to do.
#ifdef __WXMSW__
mDirToWriteTo = FileName.BeforeLast('\\') + "\\";
#else
mDirToWriteTo = FileName.BeforeLast('/') + "/";
#endif
mpShooter = this;
const int nPrefsPages = 19;
for( int i=0;i<nPrefsPages;i++){
// The handler is cleared each time it is used.
SetIdleHandler( context.project );
gPrefs->Write(wxT("/Prefs/PrefsCategory"), (long)i);
gPrefs->Flush();
CommandID Command{ wxT("Preferences") };
const CommandContext projectContext( *pProject );
if( !MacroCommands::HandleTextualCommand( commandManager,
Command, projectContext, AlwaysEnabledFlag, true ) )
{
// using GET in a log message for devs' eyes only
wxLogDebug("Command %s not found", Command.GET() );
}
// This sleep is not needed, but gives user a chance to see the
// dialogs as they whizz by.
wxMilliSleep( 200 );
}
}
void ScreenshotCommand::CaptureEffects(
const CommandContext & context,
AudacityProject * pProject, const wxString &FileName )
{
(void)pProject;
(void)&FileName;//compiler food.
(void)&context;
#define TRICKY_CAPTURE
#define CAPTURE_NYQUIST_TOO
// Commented out the effects that don't have dialogs.
// Also any problematic ones,
CaptureCommands( context, {
#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...",
"Filter Curve EQ...",
"Graphic EQ...",
//"Fade In",
//"Fade Out",
//"Invert",
"Normalize...",
"Paulstretch...",
"Phaser...",
//"Repair",
"Repeat...",
"Reverb...",
//"Reverse",
"Sliding Stretch...",
"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 Effects 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...",
"Label Sounds...",
"Regular Interval Labels...",
"Sample Data Export...",
#endif
} );
}
void ScreenshotCommand::CaptureScriptables(
const CommandContext & context,
AudacityProject * pProject, const wxString &FileName )
{
(void)pProject;
(void)&FileName;//compiler food.
(void)&context;
CaptureCommands( context, {
"SelectTime",
"SelectFrequencies",
"SelectTracks",
"SetTrackStatus",
"SetTrackAudio",
"SetTrackVisuals",
"GetPreference",
"SetPreference",
"SetClip",
"SetEnvelope",
"SetLabel",
"SetProject",
"Select",
"SetTrack",
"GetInfo",
"Message",
"Help", // Help on individual commands
"Import2",
"Export2",
"OpenProject2",
"SaveProject2",
"Drag",
"CompareAudio",
"Screenshot",
} );
}
void ScreenshotCommand::CaptureCommands(
const CommandContext & context, const wxArrayStringEx & Commands ){
AudacityProject * pProject = &context.project;
CommandManager &manager = CommandManager::Get( *pProject );
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.
#ifdef __WXMSW__
mDirToWriteTo = mFileName.BeforeLast('\\') + "\\";
#else
mDirToWriteTo = mFileName.BeforeLast('/') + "/";
#endif
mpShooter = this;
for( size_t i=0;i<Commands.size();i++){
// The handler is cleared each time it is used.
SetIdleHandler( context.project );
Str = Commands[i];
const CommandContext projectContext( *pProject );
if( !manager.HandleTextualCommand( Str, projectContext, AlwaysEnabledFlag, true ) )
{
wxLogDebug("Command %s not found", Str);
}
// This particular sleep is not needed, but gives user a chance to see the
// dialogs as they whizz by.
wxMilliSleep( 200 );
}
}
wxString ScreenshotCommand::MakeFileName(const wxString &path, const wxString &basename)
{
// If the path is a full file name, then use it.
if( path.EndsWith( ".png" ) )
return path;
// Otherwise make up a file name that has not been used already.
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;
}
void ScreenshotCommand::GetDerivedParams()
{
// Read the parameters that were passed in
mFilePath = mPath;
mCaptureMode = mWhat;
// Build a suitable filename
mFileName = MakeFileName(mFilePath,
kCaptureWhatStrings[ mCaptureMode ].Translation() );
if (mBack == kBlue)
{
mBackground = true;
mBackColor = wxColour(51, 102, 153);
}
else if (mBack == kWhite)
{
mBackground = true;
mBackColor = wxColour(255, 255, 255);
}
else
{
mBackground = false;
}
}
wxRect ScreenshotCommand::GetWindowRect(wxTopLevelWindow *w){
int x = 0, y = 0;
int width, height;
w->ClientToScreen(&x, &y);
w->GetClientSize(&width, &height);
return wxRect( x,y,width,height);
}
wxRect ScreenshotCommand::GetFullWindowRect(wxTopLevelWindow *w){
wxRect r = w->GetRect();
r.SetPosition(w->GetScreenPosition());
r = w->GetScreenRect();
#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 && mCaptureMode == kwindowplus )
{
// 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;
}
return r;
}
wxRect ScreenshotCommand::GetScreenRect(){
int width, height;
wxDisplaySize(&width, &height);
return wxRect( 0,0,width,height);
}
wxRect ScreenshotCommand::GetPanelRect(TrackPanel * panel){
//AdornedRulerPanel *ruler = panel->mRuler;
int h = panel->GetRuler()->GetRulerHeight();
int x = 0, y = -h;
int width, height;
panel->ClientToScreen(&x, &y);
panel->GetParent()->ScreenToClient(&x, &y);
panel->GetClientSize(&width, &height);
return wxRect(x, y, width, height + h);
}
wxRect ScreenshotCommand::GetRulerRect(AdornedRulerPanel *ruler){
int x = 0, y = 0;
int width, height;
ruler->ClientToScreen(&x, &y);
ruler->GetParent()->ScreenToClient(&x, &y);
ruler->GetClientSize(&width, &height);
height = ruler->GetRulerHeight();
return wxRect( x, y, width, height);
}
wxRect ScreenshotCommand::GetTracksRect(TrackPanel * panel){
int x = 0, y = 0;
int width, height;
panel->ClientToScreen(&x, &y);
panel->GetParent()->ScreenToClient(&x, &y);
panel->GetClientSize(&width, &height);
return wxRect( x, y, width, height);
}
wxRect ScreenshotCommand::GetTrackRect( AudacityProject * pProj, TrackPanel * panel, int n){
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 );
// 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 TrackView::GetY() and
// TrackView::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;
};
int count = 0;
for (auto t : TrackList::Get( *pProj ).Leaders()) {
count += 1;
if( count > n )
{
wxRect r = FindRectangle( *panel, *t );
return r;
}
}
return wxRect( 0,0,0,0);
}
wxString ScreenshotCommand::WindowFileName(AudacityProject * proj, wxTopLevelWindow *w){
if (w != ProjectWindow::Find( proj ) && !w->GetTitle().empty()) {
mFileName = MakeFileName(mFilePath,
kCaptureWhatStrings[ mCaptureMode ].Translation() +
(wxT("-") + w->GetTitle() + wxT("-")));
}
return mFileName;
}
bool ScreenshotCommand::Apply(const CommandContext & context)
{
GetDerivedParams();
//Don't reset the toolbars to a known state.
//We will be capturing variations of them.
//ToolManager::Get( context.project ).Reset();
wxTopLevelWindow *w = GetFrontWindow(&context.project);
if (!w)
return false;
TrackPanel *panel = &TrackPanel::Get( context.project );
AdornedRulerPanel *ruler = panel->GetRuler();
int nTracks = TrackList::Get( context.project ).size();
int x1,y1,x2,y2;
w->ClientToScreen(&x1, &y1);
panel->ClientToScreen(&x2, &y2);
wxPoint p( x2-x1, y2-y1);
auto &toolManager = ToolManager::Get( context.project );
switch (mCaptureMode) {
case kwindow:
return Capture(context, WindowFileName( &context.project, w ) , w, GetWindowRect(w));
case kfullwindow:
case kwindowplus:
return Capture(context, WindowFileName( &context.project, w ) , w, GetFullWindowRect(w));
case kfullscreen:
return Capture(context, mFileName, w,GetScreenRect());
case ktoolbars:
return CaptureDock(context, toolManager.GetTopDock(), mFileName);
case kscriptables:
CaptureScriptables(context, &context.project, mFileName);
break;
case keffects:
CaptureEffects(context, &context.project, mFileName);
break;
case kpreferences:
CapturePreferences(context, &context.project, mFileName);
break;
case kselectionbar:
return CaptureToolbar(context, &toolManager, SelectionBarID, mFileName);
case kspectralselection:
return CaptureToolbar(context, &toolManager, SpectralSelectionBarID, mFileName);
case ktimer:
return CaptureToolbar(context, &toolManager, TimeBarID, mFileName);
case ktools:
return CaptureToolbar(context, &toolManager, ToolsBarID, mFileName);
case ktransport:
return CaptureToolbar(context, &toolManager, TransportBarID, mFileName);
case kmixer:
return CaptureToolbar(context, &toolManager, MixerBarID, mFileName);
case kmeter:
return CaptureToolbar(context, &toolManager, MeterBarID, mFileName);
case krecordmeter:
return CaptureToolbar(context, &toolManager, RecordMeterBarID, mFileName);
case kplaymeter:
return CaptureToolbar(context, &toolManager, PlayMeterBarID, mFileName);
case kedit:
return CaptureToolbar(context, &toolManager, EditBarID, mFileName);
case kdevice:
return CaptureToolbar(context, &toolManager, DeviceBarID, mFileName);
case ktranscription:
return CaptureToolbar(context, &toolManager, TranscriptionBarID, mFileName);
case kscrub:
return CaptureToolbar(context, &toolManager, ScrubbingBarID, mFileName);
case ktrackpanel:
return Capture(context, mFileName, panel, GetPanelRect(panel));
case kruler:
return Capture(context, mFileName, ruler, GetRulerRect(ruler) );
case ktracks:
return Capture(context, mFileName, panel, GetTracksRect(panel));
case kfirsttrack:
return Capture(context, mFileName, panel, GetTrackRect( &context.project, panel, 0 ) );
case ksecondtrack:
return Capture(context, mFileName, panel, GetTrackRect( &context.project, panel, 1 ) );
case ktracksplus:
{ wxRect r = GetTracksRect(panel);
r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
return Capture(context, mFileName, panel, r);
}
case kfirsttrackplus:
{ wxRect r = GetTrackRect(&context.project, panel, 0 );
r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
return Capture(context, mFileName, panel, r );
}
case kfirsttwotracks:
{ wxRect r = GetTrackRect( &context.project, panel, 0 );
r = r.Union( GetTrackRect( &context.project, panel, 1 ));
return Capture(context, mFileName, panel, r );
}
case kfirstthreetracks:
{ wxRect r = GetTrackRect( &context.project, panel, 0 );
r = r.Union( GetTrackRect( &context.project, panel, 2 ));
return Capture(context, mFileName, panel, r );
}
case kfirstfourtracks:
{ wxRect r = GetTrackRect( &context.project, panel, 0 );
r = r.Union( GetTrackRect( &context.project, panel, 3 ));
return Capture(context, mFileName, panel, r );
}
case kalltracks:
{ wxRect r = GetTrackRect( &context.project, panel, 0 );
r = r.Union( GetTrackRect( &context.project, panel, nTracks-1 ));
return Capture(context, mFileName, panel, r );
}
case kalltracksplus:
{ wxRect r = GetTrackRect( &context.project, panel, 0 );
r.SetTop( r.GetTop() - ruler->GetRulerHeight() );
r.SetHeight( r.GetHeight() + ruler->GetRulerHeight() );
r = r.Union( GetTrackRect( &context.project, panel, nTracks-1 ));
return Capture(context, mFileName, panel, r );
}
default:
return false;
}
return true;
}