552 lines
15 KiB
C++
552 lines
15 KiB
C++
/**********************************************************************
|
|
|
|
Audacity - A Digital Audio Editor
|
|
Copyright 1999-2009 Audacity Team
|
|
License: GPL v2 - see LICENSE.txt
|
|
|
|
Dominic Mazzoni
|
|
Dan Horgan
|
|
|
|
******************************************************************//**
|
|
|
|
\class ScreenshotCommand
|
|
\brief Implements a command for capturing various areas of the screen or
|
|
project window.
|
|
|
|
*//*******************************************************************/
|
|
|
|
#include "ScreenshotCommand.h"
|
|
#include "CommandTargets.h"
|
|
#include "../AudacityApp.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 "../Track.h"
|
|
#include "../TrackPanel.h"
|
|
#include "../toolbars/ToolManager.h"
|
|
#include "../toolbars/ToolBar.h"
|
|
#include "../toolbars/ControlToolBar.h"
|
|
#include "../toolbars/DeviceToolBar.h"
|
|
#include "../toolbars/EditToolBar.h"
|
|
#include "../toolbars/MeterToolBar.h"
|
|
#include "../toolbars/MixerToolBar.h"
|
|
#include "../toolbars/SelectionBar.h"
|
|
#include "../toolbars/ToolsToolBar.h"
|
|
#include "../toolbars/TranscriptionToolBar.h"
|
|
#include "../widgets/Ruler.h"
|
|
|
|
#if defined(__WXMAC__) && !wxCHECK_VERSION(3, 0, 0)
|
|
//
|
|
// This is a temporary solution for capturing screenshots on
|
|
// OS X 10.6 or greater. This needs to go away once we move
|
|
// to wx3.
|
|
//
|
|
// (This was copied from wx3.1.0 source and hacked up a bit.)
|
|
//
|
|
#include <wx/mac/private.h>
|
|
#include <dlfcn.h>
|
|
|
|
typedef CGImageRef (*CGDisplayCreateImageFunc)(CGDirectDisplayID displayID);
|
|
|
|
static wxBitmap DoGetAsBitmap(const wxRect *subrect)
|
|
{
|
|
CGRect cgbounds = CGDisplayBounds(CGMainDisplayID());
|
|
|
|
wxRect rect = subrect ? *subrect : wxRect(0, 0, cgbounds.size.width, cgbounds.size.height);
|
|
|
|
wxBitmap bmp(rect.GetSize().GetWidth(), rect.GetSize().GetHeight(), 32);
|
|
|
|
CGDisplayCreateImageFunc createImage =
|
|
(CGDisplayCreateImageFunc) dlsym(RTLD_NEXT, "CGDisplayCreateImage");
|
|
if (createImage == NULL)
|
|
{
|
|
return bmp;
|
|
}
|
|
|
|
CGRect srcRect = CGRectMake(rect.x, rect.y, rect.width, rect.height);
|
|
|
|
CGContextRef context = (CGContextRef)bmp.GetHBITMAP();
|
|
|
|
CGContextSaveGState(context);
|
|
|
|
CGContextTranslateCTM( context, 0, cgbounds.size.height );
|
|
CGContextScaleCTM( context, 1, -1 );
|
|
|
|
if ( subrect )
|
|
srcRect = CGRectOffset( srcRect, -subrect->x, -subrect->y ) ;
|
|
|
|
CGImageRef image = NULL;
|
|
|
|
image = createImage(kCGDirectMainDisplay);
|
|
|
|
wxASSERT_MSG(image, wxT("wxScreenDC::GetAsBitmap - unable to get screenshot."));
|
|
|
|
CGContextDrawImage(context, srcRect, image);
|
|
|
|
CGImageRelease(image);
|
|
|
|
CGContextRestoreGState(context);
|
|
|
|
return bmp;
|
|
}
|
|
#endif
|
|
|
|
wxTopLevelWindow *ScreenshotCommand::GetFrontWindow(AudacityProject *project)
|
|
{
|
|
wxWindow *front = NULL;
|
|
wxWindow *proj = wxGetTopLevelParent(project);
|
|
|
|
// 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) {
|
|
front = win;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 && !wxGetApp().Yield(true); cnt--) {
|
|
wxMilliSleep(10);
|
|
}
|
|
wxMilliSleep(200);
|
|
for (cnt = 10; cnt && !wxGetApp().Yield(true); cnt--) {
|
|
wxMilliSleep(10);
|
|
}
|
|
}
|
|
|
|
void ScreenshotCommand::Capture(wxString filename,
|
|
wxWindow *window,
|
|
int x, int y, int width, int height,
|
|
bool bg)
|
|
{
|
|
if (window) {
|
|
if (window->IsTopLevel()) {
|
|
window->Raise();
|
|
}
|
|
else {
|
|
wxGetTopLevelParent(window)->Raise();
|
|
}
|
|
}
|
|
|
|
Yield();
|
|
|
|
int screenW, screenH;
|
|
wxDisplaySize(&screenW, &screenH);
|
|
wxBitmap full(screenW, screenH);
|
|
|
|
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);
|
|
|
|
// Ensure within bounds (x/y are negative on Windows when maximized)
|
|
r.Intersect(wxRect(0, 0, screenW, screenH));
|
|
|
|
// Convert to screen coordinates if needed
|
|
if (window && window->GetParent() && !window->IsTopLevel()) {
|
|
r.SetPosition(window->GetParent()->ClientToScreen(r.GetPosition()));
|
|
}
|
|
|
|
// 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, wxSOLID));
|
|
fullDC.Clear();
|
|
|
|
fullDC.DrawBitmap(part, b.x, b.y);
|
|
fullDC.SelectObject(wxNullBitmap);
|
|
|
|
part = back;
|
|
}
|
|
|
|
// Save the final image
|
|
wxImage image = part.ConvertToImage();
|
|
if (image.SaveFile(filename)) {
|
|
mOutput->Status(_("Saved ") + filename);
|
|
}
|
|
else {
|
|
mOutput->Error(_("Error trying to save file: ") + filename);
|
|
}
|
|
|
|
::wxBell();
|
|
}
|
|
|
|
void ScreenshotCommand::CaptureToolbar(ToolManager *man, int type, 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);
|
|
|
|
Capture(name, w, x, y, width, height);
|
|
|
|
if (!visible) {
|
|
man->ShowHide(type);
|
|
if (mIgnore)
|
|
mIgnore->Raise();
|
|
}
|
|
}
|
|
|
|
void ScreenshotCommand::CaptureDock(wxWindow *win, wxString fileName)
|
|
{
|
|
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);
|
|
}
|
|
|
|
wxString ScreenshotCommandType::BuildName()
|
|
{
|
|
return wxT("Screenshot");
|
|
}
|
|
|
|
void ScreenshotCommandType::BuildSignature(CommandSignature &signature)
|
|
{
|
|
OptionValidator *captureModeValidator = new OptionValidator();
|
|
captureModeValidator->AddOption(wxT("window"));
|
|
captureModeValidator->AddOption(wxT("fullwindow"));
|
|
captureModeValidator->AddOption(wxT("windowplus"));
|
|
captureModeValidator->AddOption(wxT("fullscreen"));
|
|
captureModeValidator->AddOption(wxT("toolbars"));
|
|
captureModeValidator->AddOption(wxT("selectionbar"));
|
|
captureModeValidator->AddOption(wxT("tools"));
|
|
captureModeValidator->AddOption(wxT("transport"));
|
|
captureModeValidator->AddOption(wxT("mixer"));
|
|
captureModeValidator->AddOption(wxT("meter"));
|
|
captureModeValidator->AddOption(wxT("edit"));
|
|
captureModeValidator->AddOption(wxT("device"));
|
|
captureModeValidator->AddOption(wxT("transcription"));
|
|
captureModeValidator->AddOption(wxT("trackpanel"));
|
|
captureModeValidator->AddOption(wxT("ruler"));
|
|
captureModeValidator->AddOption(wxT("tracks"));
|
|
captureModeValidator->AddOption(wxT("firsttrack"));
|
|
captureModeValidator->AddOption(wxT("secondtrack"));
|
|
|
|
OptionValidator *backgroundValidator = new OptionValidator();
|
|
backgroundValidator->AddOption(wxT("Blue"));
|
|
backgroundValidator->AddOption(wxT("White"));
|
|
backgroundValidator->AddOption(wxT("None"));
|
|
|
|
Validator *filePathValidator = new Validator();
|
|
|
|
signature.AddParameter(wxT("CaptureMode"),
|
|
wxT("fullscreen"),
|
|
captureModeValidator);
|
|
signature.AddParameter(wxT("Background"),
|
|
wxT("None"),
|
|
backgroundValidator);
|
|
signature.AddParameter(wxT("FilePath"), wxT(""), filePathValidator);
|
|
}
|
|
|
|
Command *ScreenshotCommandType::Create(CommandOutputTarget *target)
|
|
{
|
|
return new ScreenshotCommand(*this, target);
|
|
}
|
|
|
|
wxString ScreenshotCommand::MakeFileName(wxString path, 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.c_str(), basename.c_str(), i);
|
|
i++;
|
|
} while (::wxFileExists(filename));
|
|
|
|
return filename;
|
|
}
|
|
|
|
bool ScreenshotCommand::Apply(CommandExecutionContext context)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
// Reset the toolbars to a known state
|
|
context.GetProject()->mToolManager->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()->mToolManager->GetTopDock(), fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("selectionbar")))
|
|
{
|
|
CaptureDock(context.GetProject()->mToolManager->GetBotDock(), fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("tools")))
|
|
{
|
|
CaptureToolbar(context.GetProject()->mToolManager, ToolsBarID, fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("transport")))
|
|
{
|
|
CaptureToolbar(context.GetProject()->mToolManager, TransportBarID, fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("mixer")))
|
|
{
|
|
CaptureToolbar(context.GetProject()->mToolManager, MixerBarID, fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("meter")))
|
|
{
|
|
CaptureToolbar(context.GetProject()->mToolManager, MeterBarID, fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("edit")))
|
|
{
|
|
CaptureToolbar(context.GetProject()->mToolManager, EditBarID, fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("device")))
|
|
{
|
|
CaptureToolbar(context.GetProject()->mToolManager, DeviceBarID, fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("transcription")))
|
|
{
|
|
CaptureToolbar(context.GetProject()->mToolManager, TranscriptionBarID, fileName);
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("trackpanel")))
|
|
{
|
|
TrackPanel *panel = context.GetProject()->mTrackPanel;
|
|
//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()->mTrackPanel;
|
|
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()->mTrackPanel;
|
|
|
|
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()->mTrackPanel;
|
|
TrackListIterator iter(context.GetProject()->GetTracks());
|
|
Track * t = iter.First();
|
|
if (!t) {
|
|
return false;
|
|
}
|
|
wxRect r = panel->FindTrackRect(t, true);
|
|
|
|
int x = 0, y = r.y - 3;
|
|
int width, height;
|
|
|
|
panel->ClientToScreen(&x, &y);
|
|
panel->GetParent()->ScreenToClient(&x, &y);
|
|
panel->GetClientSize(&width, &height);
|
|
|
|
Capture(fileName, panel, x, y, width, r.height + 6);
|
|
|
|
}
|
|
else if (captureMode.IsSameAs(wxT("secondtrack")))
|
|
{
|
|
TrackPanel *panel = context.GetProject()->mTrackPanel;
|
|
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 = panel->FindTrackRect(t, true);
|
|
|
|
int x = 0, y = r.y - 3;
|
|
int width, height;
|
|
|
|
panel->ClientToScreen(&x, &y);
|
|
panel->GetParent()->ScreenToClient(&x, &y);
|
|
panel->GetClientSize(&width, &height);
|
|
|
|
Capture(fileName, panel, x, y, width, r.height + 6);
|
|
}
|
|
else
|
|
{
|
|
// Invalid capture mode!
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|