]> git.saurik.com Git - wxWidgets.git/blobdiff - utils/screenshotgen/src/autocapture.h
Fix a crash in wxExecute() in wxMSW too.
[wxWidgets.git] / utils / screenshotgen / src / autocapture.h
index 6893cc498df2e51c9cac146d8659cf21e2aed3fe..36477c3a54c1642e972ca6c0c1b96a0ac46c37bd 100644 (file)
 // Purpose:     Defines the AutoCaptureMechanism class
 // Author:      Utensil Candel (UtensilCandel@@gmail.com)
 // RCS-ID:      $Id$
-// Licence:     wxWindows license
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-#ifndef AUTOCAP_H
-#define AUTOCAP_H
+#ifndef _AUTOCAPTURE_H_
+#define _AUTOCAPTURE_H_
 
-#include <wx/notebook.h>
-#include <wx/settings.h>
+#include "wx/beforestd.h"
 #include <vector>
-#include <ctime>
+#include "wx/afterstd.h"
 
-// Global helper functions. to take screenshot for a rect region
-wxBitmap Capture(wxRect rect);
-wxBitmap Capture(int x, int y, int width, int height);
+#include "wx/gdicmn.h"
 
+class wxBitmap;
+class wxFlexGridSizer;
+class wxWindow;
+class wxNotebook;
+
+/**
+    GlobalAdjustFlags works with AutoCaptureMechanism's constructor, to
+    configure auto-adjustments of all controls.
+
+    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.
+
+        If AJ_DisableRegionAdjust and AJ_AlwaysRegionAdjust are both specified,
+        current implemetation will ignore AJ_DisableRegionAdjust.
+    */
+    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
+        modes/states of a control into one image. 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.
+
+        The filename of the generated screenshot is the name of the first
+        control in the series.
+    */
     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 "docs/doxygen/images" folder directly.
+
+    @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
+
+    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:
-    AutoCaptureMechanism(wxNotebook * notebook,
-                         wxString directory = wxT("screenshots"),
-                         int border = 5)
-        : m_notebook(notebook), m_dir(directory), m_border(border) {}
-    ~AutoCaptureMechanism(){}
+    /**
+        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 disable /
+            enable some auto-adjustment for all controls.
+        @param margin
+            It's the margin around every control in the sreenshots.
+    */
+    AutoCaptureMechanism(wxNotebook *notebook,
+                         int flag = AJ_NormalAll,
+                         int margin = 5);
 
-    /*
-        If wxRTTI can't get the name correctly, specify name;
-        If wxWindow::GetScreenRect doesn't get the rect correctly, set flag to AJ_RegionAdjust
+    ~AutoCaptureMechanism() { }
+
+    /**
+        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(""),
+    void RegisterControl(wxWindow* ctrl,
+                         wxString name = wxT(""),
                          int flag = AJ_Normal)
     {
         m_controlList.push_back(Control(ctrl, name, flag));
     }
 
-    void RegisterControl(wxWindow * ctrl, int flag)
+    /**
+        Register a control and perform specifid auto adjustments.
+
+        This calls RegisterControl(wxWindow* ctrl, wxString name, int flag)
+        with an empty name. This is useful when you only want to auto-adjust
+        something other than name adjustment.
+    */
+    void RegisterControl(wxWindow* ctrl, int flag)
     {
         RegisterControl(ctrl, wxT(""), 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));
     }
 
-    void CaptureAll()
-    {
-        m_notebook->SetSelection(0);
-        wxYield();
-
-        for(ControlList::iterator it = m_controlList.begin();
-            it != m_controlList.end();
-            ++it)
-        {
-            Control & ctrl = *it;
-
-            if(ctrl.flag == AJ_TurnPage) // Turn to next page
-            {
-                m_notebook->SetSelection(m_notebook->GetSelection() + 1);
-                wxYield();
-                continue;
-            }
-
-            wxBitmap screenshot = Capture(ctrl);
-
-            if(ctrl.flag & AJ_Union)
-            {
-                screenshot = Union(screenshot, Capture(*(++it)));
-            }
-
-            Save(screenshot, ctrl.name);
-        }
-    }
+    /**
+        Capture all registered controls of the associated wxNotebook.
+    */
+    void CaptureAll();
+
+    /*
+        Static Members
+    */
+    /**
+        Take a screenshot for the given region.
+
+        @param bitmap
+            Bitmap to save the screenshot to.
+        @param rect
+            Given rectangular region.
+        @param delay
+            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 bool Capture(wxBitmap* bitmap, wxRect rect, int delay = 0);
+
+    /**
+        Take a screenshot for the given region.
+
+        @see Capture(wxBitmap*,wxRect,int)
+    */
+    static bool Capture(wxBitmap* bitmap,
+                        int x, int y, int width, int height,
+                        int delay = 0);
+
+    /**
+        Save the screenshot as the name of @a fileName in the default
+        directory.
+
+        @a fileName should be without ".png".
+    */
+    static void Save(wxBitmap* screenshot, const wxString& fileName);
+
+    /**
+        Set the default directory where the screenshots will be generated.
+    */
+    static void SetDefaultDirectory(const 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();
 
 private:
+
+    /*
+        Internal Data Structures
+
+        They might go public in future to provide reuse of ControlList.
+    */
     struct Control
     {
         Control() {}
@@ -100,129 +357,80 @@ private:
     };
 
     typedef std::vector<Control> ControlList;
-    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;
-
-    wxFlexGridSizer* m_grid;
-
-    wxString m_dir;
-    int m_border;
 
+    /*
+        Internal Functions
 
+        They are only used to clearify the logic of some public functions and
+        it's nonsense to call them elsewhere.
+    */
 
-    wxBitmap Capture(Control & ctrl)
-    {
-        if(ctrl.name == wxT("")) //no mannual specification for the control name
-        {
-            //Get name from wxRTTI
-            ctrl.name = ctrl.ctrl->GetClassInfo()->GetClassName();
-        }
-
-        int choice = wxNO;
-
-        if(ctrl.flag & AJ_Dropdown)
-        {
-            wxString caption = _("Do you wish to capture the dropdown list of ") + ctrl.name + _("?");
-            wxString notice = _("Click YES to capture it.\nAnd you MUST drop down the ") + ctrl.name + _(" in 3 seconds after close me.\n");
-            notice += _("Click NO otherwise.");
-
-            choice = wxMessageBox(notice, caption, wxYES_NO, m_notebook);
-
-            if(choice == wxYES)
-            {
-                //Wait for 3 seconds
-                using std::clock;
-                using std::clock_t;
-
-                clock_t start = clock();
-                while(clock() - start < CLOCKS_PER_SEC * 3)
-                {
-                    wxYieldIfNeeded();
-                }
-            }
-        }
-
-        wxRect rect = GetRect(ctrl.ctrl, ctrl.flag);
-
-        //Do some rect adjust so it can include the dropdown list
-        //Currently it only works well under MSW, not adjusted for Linux and Mac OS
-        if(ctrl.flag & AJ_Dropdown && choice == wxYES)
-        {
-//                #ifdef __WXMSW__
-            int h = rect.GetHeight();
-            rect.SetHeight(h * 4);
-//                #endif
-        }
-
-        //cut off "wx" and change them into lowercase.
-        // e.g. wxButton will have a name of "button" at the end
-        ctrl.name.StartsWith(_T("wx"), &(ctrl.name));
-        ctrl.name.MakeLower();
-
-        wxBitmap screenshot = ::Capture(rect);
-
-        if(ctrl.flag & AJ_RegionAdjust)
-        {
-            PutBack(ctrl.ctrl);
-        }
-
-        return screenshot;
-    }
+    /*
+        Capture and auto adjust the control. Used by CaptureAll().
+    */
+    bool Capture(wxBitmap* bitmap, 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)
-    void PutBack(wxWindow * ctrl);
-
-    wxBitmap Union(wxBitmap pic1, wxBitmap pic2)
-    {
-        int w1, w2, h1, h2, w, h;
-        w1 = pic1.GetWidth();
-        w2 = pic2.GetWidth();
-        h1 = pic1.GetHeight();
-        h2 = pic2.GetHeight();
-
-        const int gap_between = 20;
-
-        w = (w1 >= w2) ? w1 : w2;
-        h = h1 + h2 + gap_between;
 
-        wxBitmap result(w, h, -1);
+    /*
+        Put the control back after the label trick used in GetRect(). Used by
+        Capture(Control & ctrl).
+    */
+    void PutBack(wxWindow * ctrl);
 
-        wxMemoryDC dstDC;
-        dstDC.SelectObject(result);
+    /*
+        Union two screenshots in the vertical direction, and leave a gap
+        between the screenshots. Used by CaptureAll().
 
-        dstDC.DrawBitmap(pic1, 0, 0, false);
-        dstDC.DrawBitmap(pic2, 0, h1 + gap_between, false);
+        The gap is 20 pixels by default. Currently it isn't configurable.
+    */
+    static bool Union(wxBitmap* top, wxBitmap* bottom, wxBitmap* result);
 
-        dstDC.SelectObject(wxNullBitmap);
+    /*
+        Delay a few seconds without blocking GUI.
+    */
+    static void Delay(int seconds);
 
-        wxMemoryDC maskDC;
-        wxBitmap mask(w, h, 1);
-        maskDC.SelectObject(mask);
+    /*
+        Data Members
+    */
+    ControlList m_controlList;
 
-        maskDC.SetPen(*wxTRANSPARENT_PEN);
-        maskDC.SetBrush(*wxBLACK_BRUSH);
-        maskDC.DrawRectangle(0, 0, w + 1, h + 1);
+    wxNotebook* m_notebook;
 
-        maskDC.SetBrush(*wxWHITE_BRUSH);
-        maskDC.DrawRectangle(0, 0, w1, h1);
-        maskDC.DrawRectangle(0, h1 + gap_between, w2, h2);
-        maskDC.SelectObject(wxNullBitmap);
+    int m_flag;
 
-        result.SetMask(new wxMask(mask));
+    int m_margin;
 
-        return result;
-    }
+    wxFlexGridSizer* m_grid;
 
-    void Save(wxBitmap screenshot, wxString fileName);
+    static wxString default_dir;
 };
 
-#endif // AUTOCAP_H
-
-
+#endif // _AUTOCAPTURE_H_