// AutoCaptureMechanism
// ----------------------------------------------------------------------------
+/* static */
+wxString AutoCaptureMechanism::default_dir = _T("screenshots");
+
/* static */
void AutoCaptureMechanism::Delay(int seconds)
{
#else // Under other paltforms, take a real screenshot
+ wxUnusedVar(delay);
+
// Create a DC for the whole screen area
wxScreenDC dcScreen;
return Capture(origin.x, origin.y, rect.GetWidth(), rect.GetHeight(), delay);
}
+/* static */
+void AutoCaptureMechanism::Save(wxBitmap screenshot, wxString fileName)
+{
+ // make sure default_dir exists
+ if (!wxDirExists(default_dir))
+ wxMkdir(default_dir);
+
+ wxFileName fullFileName(default_dir, fileName + ".png");
+
+ // do not overwrite already existing files with this name
+ while (fullFileName.FileExists())
+ fullFileName.SetName(fullFileName.GetName() + "_");
+
+ // save the screenshot as a PNG
+ screenshot.SaveFile(fullFileName.GetFullPath(), wxBITMAP_TYPE_PNG);
+}
+
void AutoCaptureMechanism::CaptureAll()
{
// start from the first page
continue;
}
+// // create the screenshot
+// wxBitmap screenshot = Capture(ctrl);
+// if (ctrl.flag & AJ_Union)
+// screenshot = Union(screenshot, Capture(*(++it)));
+//
+// // and save it
+// Save(screenshot, ctrl.name);
// create the screenshot
wxBitmap screenshot = Capture(ctrl);
- if (ctrl.flag & AJ_Union)
- screenshot = Union(screenshot, Capture(*(++it)));
+
+ if(ctrl.flag & AJ_Union)
+ {
+ do
+ {
+ ctrl = *(++it);
+ screenshot = Union(screenshot, Capture(ctrl));
+ }
+ while(!(ctrl.flag & AJ_UnionEnd));
+ }
// and save it
Save(screenshot, ctrl.name);
wxBitmap result(w, h, -1);
- wxMemoryDC dstDC;
- dstDC.SelectObject(result);
-
- dstDC.DrawBitmap(pic1, 0, 0, false);
- dstDC.DrawBitmap(pic2, 0, h1 + gap_between, false);
-
- dstDC.SelectObject(wxNullBitmap);
-
+#if 0
+ //Mask the bitmap "result"
wxMemoryDC maskDC;
wxBitmap mask(w, h, 1);
maskDC.SelectObject(mask);
maskDC.SelectObject(wxNullBitmap);
result.SetMask(new wxMask(mask));
+#endif
- return result;
-}
-
-void AutoCaptureMechanism::Save(wxBitmap screenshot, wxString fileName)
-{
- // make sure m_dir exists
- if (!wxDirExists(m_dir))
- wxMkdir(m_dir);
+ wxMemoryDC dstDC;
+ dstDC.SelectObject(result);
- wxFileName fullFileName(m_dir, fileName + ".png");
+ dstDC.SetPen(*wxTRANSPARENT_PEN);
+ dstDC.SetBrush(*wxWHITE_BRUSH);
+ dstDC.DrawRectangle(-1, -1, w + 1, h + 1);
+ dstDC.DrawBitmap(pic1, 0, 0, false);
+ dstDC.DrawBitmap(pic2, 0, h1 + gap_between, false);
- // do not overwrite already existing files with this name
- while (fullFileName.FileExists())
- fullFileName.SetName(fullFileName.GetName() + "_");
+ dstDC.SelectObject(wxNullBitmap);
- // save the screenshot as a PNG
- screenshot.SaveFile(fullFileName.GetFullPath(), wxBITMAP_TYPE_PNG);
+ return result;
}
wxRect AutoCaptureMechanism::GetRect(wxWindow* ctrl, int flag)
+---------+-----------+---------+
*/
- m_grid = new wxFlexGridSizer(3, 3, m_border, m_border);
+ m_grid = new wxFlexGridSizer(3, 3, m_margin, m_margin);
wxStaticText* l[4];
}
else // Actually it won't get here working with the current guiframe.h/guiframe.cpp
{
- return ctrl->GetScreenRect().Inflate(m_border);
+ return ctrl->GetScreenRect().Inflate(m_margin);
}
}
else
{
- return ctrl->GetScreenRect().Inflate(m_border);
+ return ctrl->GetScreenRect().Inflate(m_margin);
}
}
#include <wx/notebook.h>
+/**
+ GlobalAdjustFlags works with AutoCaptureMechanism's constructor, to disbale/enable
+ some auto-adjustment for all controls.
-// TODO: document what these flags mean
+ They are used to make AutoCaptureMechanism more configurable and provide a fallback
+ to detect the bugs that the adjustments intended to avoid.
+
+ @see AdjustFlags
+*/
+enum GlobalAdjustFlags
+{
+ /**
+ This is the default. All adjustments instructed in
+ AutoCaptureMechanism::RegisterControl() will be performed.
+ */
+ AJ_NormalAll = 0,
+
+ /**
+ Disable region adjustment for all controls.
+ */
+ AJ_DisableRegionAdjust = 1 << 0,
+
+ /**
+ Enable region adjustment for all controls.
+ */
+ AJ_AlwaysRegionAdjust = 1 << 1,
+
+ /**
+ Disable name adjustment for all controls.
+ */
+ AJ_DisableNameAdjust = 1 << 2,
+
+ /**
+ For all the "Drop-down Controls", e.g. wxChoice, do not prompt the user about whether
+ to capture their drop-down state, and always capture only its non-drop-down state.
+ */
+ AJ_DisableDropdown = 1 << 3
+};
+
+/**
+ AdjustFlags works with AutoCaptureMechanism::RegisterControl() to specify how to
+ adjust the screenshot of the current control.
+
+ They are used to avoid bugs, look better or interact with user etc.
+
+ @see GlobalAdjustFlags
+*/
enum AdjustFlags
{
+ /**
+ This is the default. Perform no adjustment for this control.
+ */
AJ_Normal = 0,
+
+ /**
+ Perform region adjustment for this control.
+
+ On some platforms and for some controls, wxWindow::GetScreenRect() will return
+ a smaller or deflected region. In these cases, the screenshots we get are incomplete.
+ It's recommended for everyone to fix the controls' code, yet this flag provides a
+ workaround to get a guaranteed correct region without using wxWindow::GetScreenRect().
+
+ This workaround("label trick") is inspired by (or say stolen from) Auria's work.
+ */
AJ_RegionAdjust = 1 << 0,
+
+ /**
+ This flag provides a way to capture the drop-down state of "Drop-down Controls",
+ e.g. wxChoice.
+
+ For all the "Drop-down Controls", prompt the user about whether to capture their
+ drop-down state, if the user chooses YES, he should drop down the control in about
+ 3 seconds and wait util it's captured in that state.
+ */
AJ_Dropdown = 1 << 1,
+
+ /**
+ This flag is used internally by RegisterPageTurn(). Don't use it directly unless you
+ know what you are doing.
+ */
AJ_TurnPage = 1 << 2,
+
+ /**
+ This flag provides a functionality to union screenshots of different controls into
+ one image.
+
+ It's especially useful to demonstrate different modes/states of a control,
+ e.g. the single-line/multi-line modes of a wxTextCtrl.
+
+ For a series of controls to be unioned, you should specify AJ_Union for the first,
+ and AJ_UnionEnd for the last. For the controls between them, you can either specify
+ AJ_Union or not.
+ */
AJ_Union = 1 << 3,
+
+ /**
+ @see AJ_Union.
+ */
AJ_UnionEnd = 1 << 4
};
+/**
+ @class AutoCaptureMechanism
+
+ AutoCaptureMechanism provides an easy-to-use and adjustable facility to take the screenshots
+ for all controls fully automaticly and correctly. It also provides an advanced feature to
+ union screenshots of different states/modes of a control.
+
+ @section tag_filename_convention Screenshot File Name Convention
+
+ All screenshots are generated as PNG files. For a control named wxName, its screenshot file
+ name would be "name.png", e.g. "button.png" for wxButton. This is the protocol with the
+ doxygen document of wxWidgets.
+
+ By default, screenshots are generated under the subdirectory "screenshots" of current working
+ directory. During updating or adding new screenshots, first make sure screenshots are generated
+ correctly, and then copy them to the following subdirectory of docs/doxygen/images:
+
+ "wxmsw" for MS Windows, "wxgtk" for Linux and "wxmac" for Mac OS.
+
+ @section tag_gui_assumption The Assumption of GUI
+
+ Unfortunately, this class have an assumption about the structure of GUI:
+ It must have the follwoing top-down structure:
+
+ wxNotebook->wxPanel->wxSizer->wxControl
+
+ That means, in the wxNotebook associated with this class, controls that needs to be
+ taken screenshots are placed on different panels(for grouping) and layed out by wxSizers.
+
+ @section tag_tutorial Tutorial
+
+ In the contruction, you should associate a wxNotebook with this class, in that wxNotebook,
+ controls that needs to be captured are placed on different panels(for grouping).
+
+ When you register controls, you should do it in order: Register the controls on the first
+ panel(using RegisterControl()), and then register a page turn(using RegisterPageTurn()),
+ so this class can turn a page of the wxNotebook to present the second page. And then
+ you register the controls on the second panel, then a page turn, and so on.
+
+ When you are done, simply call CaptureAll(), then screenshots of all controls will be
+ automaticly generated.
+
+ @section tag_autoadjust Make Use of Auto Adjustments
-// ----------------------------------------------------------------------------
-// class AutoCaptureMechanism
-// ----------------------------------------------------------------------------
+ First take a look at the document of RegisterControl(), enum AdjustFlags and
+ GlobalAdjustFlags.
+ And then, ScreenshotFrame::OnCaptureAllControls() is a good example of making use of
+ auto adjustment. Taking a look at it will get you started.
+
+ @section tag_developer_note Notes for Developers
+
+ @subsection tag_cnc CaptureAll() and Capture()
+
+ The implementation of Auto Adjustments is in CaptureAll() and Capture(), the code is
+ short, quite readable and well commented, please read the codes before any modification.
+
+ If you need the class to do something sepcial for you, consider introducing a new flag
+ and implement it in them. For an operation performed on multiple controls, implemente
+ its logic in CaptureAll(), otherwise in the private member Capture().
+
+ @subsection tag_yield_issue wxYield Issues
+
+ Not quite a good habit, but this class made a lot of use of wxYield()/wxYieldIfNeeded().
+ They are used to ensure the update of GUI(e.g. the page turn of wxNotebook) is done
+ before any further screenshot-taking, or to do the timing(in Delay()). Without their use,
+ there would be subtle bugs.
+
+ I've read documents about wxYield() and understand the down side of it before using it.
+ But I didn't find a better approach to do those things, and I used them carefully. So
+ please DO NOT remove any of these wxYield()s unless you're sure that it won't cause problems
+ on all of MS Windows XP/Vista, Linux(Ubuntu/Fedora), Mac OS Tiger/Leopard. And please
+ help me to find a better approach, thank you :)
+*/
class AutoCaptureMechanism
{
public:
+ /**
+ Constructor.
+
+ @param notebook
+ The wxNotebook associated with this class.Please see @ref tag_gui_assumption
+ and @ref tag_tutorial.
+
+ @param flag
+ It's one of or a combination of GlobalAdjustFlags, to disbale/enable some auto-adjustment
+ for all controls.
+
+ @param margin
+ It's the margin around every control in the sreenshots.
+ */
AutoCaptureMechanism(wxNotebook *notebook,
- wxString directory = wxT("screenshots"),
- int border = 5)
- : m_notebook(notebook), m_dir(directory), m_border(border) {}
+ int flag = AJ_NormalAll,
+ int margin = 5)
+ : m_notebook(notebook), m_flag(flag),
+ m_margin(margin), m_grid(NULL) {}
~AutoCaptureMechanism(){}
- /*
- If wxRTTI can't get the name correctly, specify name;
- If wxWindow::GetScreenRect doesn't get the rect correctly, set flag to AJ_RegionAdjust
+ /**
+ Register a control and perform specifid auto adjustments.
+
+ @param ctrl
+ The pointer to the control to be taken a screenshot.
+
+ @param name
+ If you find out that the screenshot for this control was generated under an incorrect
+ file name, specify @a name. e.g. for wxButton, "wxButton" or "button" are both OK.
+
+ @param flag
+
+ If you end up with an a smaller or deflected screenshot, use AJ_RegionAdjust.
+
+ If you want to caputure the "drop-down" state of a "drop-down" control, use AJ_Dropdown.
+
+ If you want to present different states of a control in one screenshot, use AJ_Union
+ and AJ_UnionEnd.
+
+ Please read the document of enum AdjustFlags, and notice that this flag could be enabled/
+ disabled by global flag GlobalAdjustFlags.
*/
- void RegisterControl(wxWindow * ctrl, wxString name = wxT(""),
- int flag = AJ_Normal)
+ void RegisterControl(wxWindow * ctrl, wxString name = _T(""), int flag = AJ_Normal)
{
m_controlList.push_back(Control(ctrl, name, flag));
}
+ /**
+ Register a control and perform specifid auto adjustments.
+
+ This is the same as RegisterControl(wxWindow * ctrl, wxString name, int flag),
+ But with it, you won't have to specify the name if you only want to auto-adjust something
+ other than name adjustment.
+ */
void RegisterControl(wxWindow * ctrl, int flag)
{
- RegisterControl(ctrl, wxT(""), flag);
+ RegisterControl(ctrl, _T(""), flag);
}
+ /**
+ Register a page turn.
+
+ When you finished registering the controls on a panel, remember to call it to turn the
+ wxNotebook to the next panel.
+ */
void RegisterPageTurn()
{
- m_controlList.push_back(Control(0, wxT(""), AJ_TurnPage));
+ m_controlList.push_back(Control(0, _T(""), AJ_TurnPage));
}
- // capture all controls of the associated notebook
+ /**
+ Capture all registered controls of the associated wxNotebook.
+ */
void CaptureAll();
- // take a screenshot only of the given rect
- // delay is only useful for Mac, for fixing a delay bug
+ /*
+ Static Members
+ */
+ /**
+ Take a screenshot for the given region.
+
+ @param rect is the given rectangular region.
+
+ @param delay is only useful for Mac, for fixing a delay bug. It seems that it didn't
+ fix the bug, so it might be removed soon.
+ */
static wxBitmap Capture(wxRect rect, int delay = 0);
+
+ /**
+ Take a screenshot for the given region.
+
+ @see Capture(wxRect rect, int delay)
+ */
static wxBitmap Capture(int x, int y, int width, int height, int delay = 0);
- static void Delay(int seconds);
+ /**
+ Save the screenshot as the name of @a fileName in the default directory.
+
+ @a fileName should be without ".png".
+ */
+ static void Save(wxBitmap screenshot, wxString fileName);
+
+ /**
+ Set the default directory where the screenshots will be generated.
+ */
+ static void SetDefaultDirectory(wxString dir) { default_dir = dir; }
+
+ /**
+ Get the default directory where the screenshots will be generated.
+ */
+ static wxString GetDefaultDirectory() { return default_dir; }
+
+ /**
+ Get the absolute path of the default directory where the screenshots will be generated.
+ */
+ static wxString GetDefaultDirectoryAbsPath()
+ {
+ wxFileName output = wxFileName::DirName(GetDefaultDirectory());
+ output.MakeAbsolute();
+ return output.GetFullPath();
+ }
+private:
-private: // internal utils
+ /*
+ Internal Data Structures
+
+ They might go public in future to provide reuse of ControlList.
+ */
struct Control
{
Control() {}
int flag;
};
+ typedef std::vector<Control> ControlList;
+
+ /*
+ Internal Functions
+
+ They are only used to clearify the logic of some public functions and it's nonsense
+ to call them elsewhere.
+ */
+
+ /*
+ Capture and auto adjust the control. Used by CaptureAll().
+ */
wxBitmap Capture(Control & ctrl);
- // if AJ_RegionAdjust is specified, the following line will use the label
- // trick to adjust the region position and size
+ /*
+ Get the correct rectangular region that the control occupies. Used by
+ Capture(Control & ctrl).
+
+ If AJ_RegionAdjust is specified, it will use the "label trick" to perform
+ region auto adjustment.
+
+ The "label trick" is to reattach the control to a wxFlexGridSizer m_grid,
+ surround the control with labels and get the control's region by label's positions.
+ Just like this:
+
+ +---------+-----------+---------+
+ | 0 | label | 1 |
+ +---------+-----------+---------+
+ | label | ctrl | label |
+ +---------+-----------+---------+
+ | 2 | label | 3 |
+ +---------+-----------+---------+
+
+ So, there will be a side effect: the control is moved to a new position. So after taking the
+ screenshot, Capture(Control & ctrl) should call PutBack(wxWindow * ctrl) to put it back.
+
+ If AJ_RegionAdjust isn't specified, it will simply call wxWindow::GetScreenRect().
+ */
wxRect GetRect(wxWindow* ctrl, int flag);
- // put the control back after the label trick(Using reparent/resizer approach)
+ /*
+ Put the control back after the label trick used in GetRect(). Used by
+ Capture(Control & ctrl).
+ */
void PutBack(wxWindow * ctrl);
- wxBitmap Union(wxBitmap pic1, wxBitmap pic2);
+ /*
+ Union two screenshots in the vertical direction, and leave a gap between the
+ screenshots. Used by CaptureAll().
+
+ The gap is 20 pixels by default. Currently it isn't configurable.
+ */
+ static wxBitmap Union(wxBitmap pic1, wxBitmap pic2);
- void Save(wxBitmap screenshot, wxString fileName);
+ static void Delay(int seconds);
- typedef std::vector<Control> ControlList;
+ /*
+ Data Members
+ */
ControlList m_controlList;
- // here we introduce the dependency on wxNotebook.
- // The assumption of this whole class is that the gui has the following top-down structure
- // wxNotebook wxPanel wxSizer wxControls
wxNotebook* m_notebook;
+ int m_flag;
+
+ int m_margin;
+
wxFlexGridSizer* m_grid;
- wxString m_dir;
- int m_border;
+ static wxString default_dir;
};
#endif // _AUTOCAPTURE_H_