/********************************************************************** 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 #include #include #include #include #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" 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; // 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); 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("control")); 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.proj->mToolManager->Reset(); wxTopLevelWindow *w = GetFrontWindow(context.proj); 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.proj && 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.proj && 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.proj->mToolManager->GetTopDock(), fileName); } else if (captureMode.IsSameAs(wxT("selectionbar"))) { CaptureDock(context.proj->mToolManager->GetBotDock(), fileName); } else if (captureMode.IsSameAs(wxT("tools"))) { CaptureToolbar(context.proj->mToolManager, ToolsBarID, fileName); } else if (captureMode.IsSameAs(wxT("control"))) { CaptureToolbar(context.proj->mToolManager, ControlBarID, fileName); } else if (captureMode.IsSameAs(wxT("mixer"))) { CaptureToolbar(context.proj->mToolManager, MixerBarID, fileName); } else if (captureMode.IsSameAs(wxT("meter"))) { CaptureToolbar(context.proj->mToolManager, MeterBarID, fileName); } else if (captureMode.IsSameAs(wxT("edit"))) { CaptureToolbar(context.proj->mToolManager, EditBarID, fileName); } else if (captureMode.IsSameAs(wxT("device"))) { CaptureToolbar(context.proj->mToolManager, DeviceBarID, fileName); } else if (captureMode.IsSameAs(wxT("transcription"))) { CaptureToolbar(context.proj->mToolManager, TranscriptionBarID, fileName); } else if (captureMode.IsSameAs(wxT("trackpanel"))) { TrackPanel *panel = context.proj->mTrackPanel; AdornedRulerPanel *ruler = panel->mRuler; int h = ruler->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.proj->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.proj->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.proj->mTrackPanel; TrackListIterator iter(context.proj->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.proj->mTrackPanel; TrackListIterator iter(context.proj->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; } // Indentation settings for Vim and Emacs. // Please do not modify past this point. // // Local Variables: // c-basic-offset: 3 // indent-tabs-mode: nil // End: // // vim: et sts=3 sw=3