using scan-line polygon conversion for constructing wxregion
[wxWidgets.git] / utils / screenshotgen / src / autocapture.cpp
index 8263e38b7ec0e68acacdad41e6eb5196518547d3..1ad9723dbb736b765bbd6043334d89c29c651c27 100644 (file)
 // Purpose:     Implement wxCtrlMaskOut class
 // Author:      Utensil Candel (UtensilCandel@@gmail.com)
 // RCS-ID:      $Id$
-// Licence:     wxWindows license
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 // For compilers that support precompilation, includes "wx/wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-    #pragma hdrstop
+#pragma hdrstop
 #endif
 
-// for all others, include the necessary headers
+#include "autocapture.h"
+
 #ifndef WX_PRECOMP
-    #include "wx/wx.h"
+#include "wx/wx.h"
 #endif
 
-#include <wx/filename.h>
+#include "wx/bitmap.h"
+#include "wx/filename.h"
+#include "wx/notebook.h"
 
-#include "autocapture.h"
+#include <ctime>
+
+#ifdef __WXMAC__
+#include <cstring>
+#endif
+
+
+// ----------------------------------------------------------------------------
+// AutoCaptureMechanism
+// ----------------------------------------------------------------------------
 
+AutoCaptureMechanism::AutoCaptureMechanism(wxNotebook *notebook,
+                                           int flag, int margin)
+: m_notebook(notebook),
+  m_flag(flag),
+  m_margin(margin),
+  m_grid(NULL)
+{
+}
 
-wxBitmap Capture(int x, int y, int width, int height)
+/* static */
+wxString AutoCaptureMechanism::default_dir = wxT("screenshots");
+
+/* static */
+wxString AutoCaptureMechanism::GetDefaultDirectoryAbsPath()
 {
-    //Somehow wxScreenDC.Blit() doesn't work under Mac for now. Here is a trick.
-    #ifdef __WXMAC__
+    wxFileName output = wxFileName::DirName(GetDefaultDirectory());
+    output.MakeAbsolute();
+    return output.GetFullPath();
+}
 
-    //wxExecute(_T("screencapture -x ") + tempfile, wxEXEC_SYNC);
+/* static */
+void AutoCaptureMechanism::Delay(int seconds)
+{
+    // TODO: Switch this to use wxTimer.
 
-    system("screencapture -x /tmp/wx_screen_capture.png");
+    // Wait for 3 seconds
+    clock_t start = clock();
+    while ( clock() - start < (clock_t)CLOCKS_PER_SEC * seconds)
+        wxYieldIfNeeded();
+}
 
-    wxBitmap fullscreen;
+/* static */
+bool AutoCaptureMechanism::Capture(wxBitmap* bitmap, int x, int y,
+                                   int width, int height, int delay)
+{
+    // Somehow wxScreenDC.Blit() doesn't work under Mac for now. Here is a trick.
+#ifdef __WXMAC__
 
+    // wxExecute(wxT("screencapture -x ") + tempfile, wxEXEC_SYNC);
+
+    char captureCommand[80] =""; // a reasonable max size is 80
+    sprintf(captureCommand, "sleep %d;%s", delay, "screencapture -x /tmp/wx_screen_capture.png");
+    system(captureCommand);
+
+    if(delay) Delay(delay);
+
+    wxBitmap fullscreen;
     do
     {
-        fullscreen = wxBitmap(_T("/tmp/wx_screen_capture.png"), wxBITMAP_TYPE_PNG);
+        fullscreen = wxBitmap(wxT("/tmp/wx_screen_capture.png"), wxBITMAP_TYPE_PNG);
     }
     while(!fullscreen.IsOk());
 
-    wxBitmap screenshot = fullscreen.GetSubBitmap(wxRect(x,y,width,height));
+    *bitmap = fullscreen.GetSubBitmap(wxRect(x, y, width, height));
+
+    // to prevent loading the old screenshot next time
+    system("rm /tmp/wx_screen_capture.png");
 
-    #else //Under other paltforms, take a real screenshot
+    return true;
 
-    //Create a DC for the whole screen area
+#else // Under other paltforms, take a real screenshot
+
+    if(delay) Delay(delay);
+
+    // Create a DC for the whole screen area
     wxScreenDC dcScreen;
 
-    //Create a Bitmap that will later on hold the screenshot image
-    //Note that the Bitmap must have a size big enough to hold the screenshot
-    //-1 means using the current default colour depth
-    wxBitmap screenshot(width, height, -1);
+    bitmap->Create(width, height);
 
-    //Create a memory DC that will be used for actually taking the screenshot
+    // Create a memory DC that will be used for actually taking the screenshot
     wxMemoryDC memDC;
-    //Tell the memory DC to use our Bitmap
-    //all drawing action on the memory DC will go to the Bitmap now
-    memDC.SelectObject(screenshot);
-    //Blit (in this case copy) the actual screen on the memory DC
-    //and thus the Bitmap
-    memDC.Blit( 0, //Copy to this X coordinate
-                0, //Copy to this Y coordinate
-                width, //Copy this width
-                height, //Copy this height
-                &dcScreen, //From where do we copy?
-                x, //What's the X offset in the original DC?
-                y  //What's the Y offset in the original DC?
-                    );
-    //Select the Bitmap out of the memory DC by selecting a new
-    //uninitialized Bitmap
+    memDC.SelectObject((*bitmap));
+    memDC.Clear();
+
+    // Blit (in this case copy) the actual screen on the memory DC
+    // and thus the Bitmap
+    memDC.Blit( 0, // Copy to this X coordinate
+                0, // Copy to this Y coordinate
+                width, // Copy this width
+                height, // Copy this height
+                &dcScreen, // From where do we copy?
+                x, // What's the X offset in the original DC?
+                y  // What's the Y offset in the original DC?
+              );
+
+    // Select the Bitmap out of the memory DC by selecting a new
+    // uninitialized Bitmap
     memDC.SelectObject(wxNullBitmap);
-    #endif //#ifdef __WXMAC__
-
-//    wxMessageBox(_(""),_(""));
-
-    return screenshot;
+#endif // #ifdef __WXMAC__
 
+    return true;
 }
 
-wxBitmap Capture(wxRect rect)
+/* static */
+bool AutoCaptureMechanism::Capture(wxBitmap* bitmap, wxRect rect, int delay)
 {
     wxPoint origin = rect.GetPosition();
-    return Capture(origin.x, origin.y, rect.GetWidth(), rect.GetHeight());
+    return Capture(bitmap, origin.x, origin.y, rect.GetWidth(), rect.GetHeight(), delay);
 }
 
+/* static */
+void AutoCaptureMechanism::Save(wxBitmap* screenshot, const wxString& fileName)
+{
+    // make sure default_dir exists
+    if (!wxDirExists(default_dir))
+        wxMkdir(default_dir);
 
-// ----------------------------------------------------------------------------
-// AutoCaptureMechanism
-// ----------------------------------------------------------------------------
+    wxFileName fullFileName(default_dir, "appear-" + fileName +
+        "-" + wxPlatformInfo::Get().GetPortIdShortName() + ".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
     m_notebook->SetSelection(0);
     wxYield();
 
-    for(ControlList::iterator it = m_controlList.begin();
-        it != m_controlList.end();
-        ++it)
+    for (ControlList::iterator it = m_controlList.begin();
+         it != m_controlList.end();
+         ++it)
     {
-        Control & ctrl = *it;
+        Control &ctrl = *it;
 
-        if(ctrl.flag == AJ_TurnPage) // Turn to next page
+        if (ctrl.flag == AJ_TurnPage)    // Turn to next page
         {
             m_notebook->SetSelection(m_notebook->GetSelection() + 1);
             wxYield();
             continue;
         }
 
-        wxBitmap screenshot = Capture(ctrl);
+        // create the screenshot
+        wxBitmap screenshot(1, 1);
+        Capture(&screenshot, ctrl);
 
         if(ctrl.flag & AJ_Union)
         {
-            screenshot = Union(screenshot, Capture(*(++it)));
+            // union screenshots until AJ_UnionEnd
+            do
+            {
+                ++it;
+                it->name = ctrl.name; //preserving the name
+                wxBitmap screenshot2(1, 1);
+                Capture(&screenshot2, *it);
+                wxBitmap combined(1, 1);
+                Union(&screenshot, &screenshot2, &combined);
+                screenshot = combined;
+            }
+            while(!(it->flag & AJ_UnionEnd));
         }
 
-        Save(screenshot, ctrl.name);
+        // and save it
+        Save(&screenshot, ctrl.name);
     }
 }
 
-wxBitmap AutoCaptureMechanism::Capture(Control & ctrl)
+bool AutoCaptureMechanism::Capture(wxBitmap* bitmap, Control& ctrl)
 {
-    if(ctrl.name == wxT("")) //no mannual specification for the control name
+    // no manual specification for the control name
+    // or name adjustment is disabled globally
+    if (ctrl.name == wxT("") || m_flag & AJ_DisableNameAdjust)
     {
-        //Get name from wxRTTI
+        // Get its name from wxRTTI
         ctrl.name = ctrl.ctrl->GetClassInfo()->GetClassName();
     }
 
     int choice = wxNO;
 
-    if(ctrl.flag & AJ_Dropdown)
+    wxRect rect = GetRect(ctrl.ctrl, ctrl.flag);
+
+    if (ctrl.flag & AJ_Dropdown && !(m_flag & AJ_DisableDropdown))
     {
-        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.");
+        // for drop-down controls we need the help of the user
+        wxString caption = _("Drop-down screenshot...");
+        wxString msg =
+            wxString::Format(_("Do you wish to capture the drop-down list of '%s' ?\n\n If YES, please drop down the list of '%s' in 5 seconds after closing this message box.\n If NO, the screenshot for this control won't contain its drop-down list."),
+                             ctrl.name, ctrl.name);
 
-        choice = wxMessageBox(notice, caption, wxYES_NO, m_notebook);
+        choice = wxMessageBox(msg, caption, wxYES_NO, m_notebook);
 
-        if(choice == wxYES)
+        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();
-            }
+            //A little hint
+            ctrl.ctrl->SetCursor(wxCursor(wxCURSOR_HAND));
+
+            // Do some rect adjust so it can include the dropdown list
+            // This adjust isn't pretty, but it works fine on all three paltforms.
+            // Looking forward to a better solution
+            int h = rect.GetHeight();
+            rect.SetHeight(h * 4);
         }
     }
 
-    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.
+    // cut off "wx" and change the name into lowercase.
     // e.g. wxButton will have a name of "button" at the end
-    ctrl.name.StartsWith(_T("wx"), &(ctrl.name));
+    ctrl.name.StartsWith(wxT("wx"), &(ctrl.name));
     ctrl.name.MakeLower();
 
-    wxBitmap screenshot = ::Capture(rect);
+    // take the screenshot
+    Capture(bitmap, rect, (choice == wxYES)?5:0);
 
-    if(ctrl.flag & AJ_RegionAdjust)
-    {
+    if (choice == wxYES) ctrl.ctrl->SetCursor(wxNullCursor);
+
+    if (ctrl.flag & AJ_RegionAdjust)
         PutBack(ctrl.ctrl);
-    }
 
-    return screenshot;
+    return true;
 }
 
-//if AJ_RegionAdjust is specified, the following line will use the label trick to adjust
-//the region position and size
-wxRect GetRect(wxWindow* ctrl, int flag);
-//put the control back after the label trick(Using reparent/resizer approach)
-void PutBack(wxWindow * ctrl);
-
-wxBitmap AutoCaptureMechanism::Union(wxBitmap pic1, wxBitmap pic2)
+/* static */
+bool AutoCaptureMechanism::Union(wxBitmap* top, wxBitmap* bottom, wxBitmap* result)
 {
     int w1, w2, h1, h2, w, h;
-    w1 = pic1.GetWidth();
-    w2 = pic2.GetWidth();
-    h1 = pic1.GetHeight();
-    h2 = pic2.GetHeight();
+    w1 = top->GetWidth();
+    w2 = bottom->GetWidth();
+    h1 = top->GetHeight();
+    h2 = bottom->GetHeight();
 
     const int gap_between = 20;
 
     w = (w1 >= w2) ? w1 : w2;
     h = h1 + h2 + gap_between;
 
-    wxBitmap result(w, h, -1);
+    result->Create(w, h);
 
     wxMemoryDC dstDC;
-    dstDC.SelectObject(result);
+    dstDC.SelectObject((*result));
 
-    dstDC.DrawBitmap(pic1, 0, 0, false);
-    dstDC.DrawBitmap(pic2, 0, h1 + gap_between, false);
+    dstDC.SetBrush(*wxWHITE_BRUSH);
+    dstDC.Clear();
+    dstDC.DrawBitmap((*top), 0, 0);
+    dstDC.DrawBitmap((*bottom), 0, h1 + gap_between);
 
     dstDC.SelectObject(wxNullBitmap);
 
-    wxMemoryDC maskDC;
-    wxBitmap mask(w, h, 1);
-    maskDC.SelectObject(mask);
-
-    maskDC.SetPen(*wxTRANSPARENT_PEN);
-    maskDC.SetBrush(*wxBLACK_BRUSH);
-    maskDC.DrawRectangle(0, 0, w + 1, h + 1);
-
-    maskDC.SetBrush(*wxWHITE_BRUSH);
-    maskDC.DrawRectangle(0, 0, w1, h1);
-    maskDC.DrawRectangle(0, h1 + gap_between, w2, h2);
-    maskDC.SelectObject(wxNullBitmap);
-
-    result.SetMask(new wxMask(mask));
-
-    return result;
-}
-
-void AutoCaptureMechanism::Save(wxBitmap screenshot, wxString fileName)
-{
-    //Check if m_defaultDir already existed
-    if(!wxDirExists(m_dir))
-        wxMkdir(m_dir);
-
-    wxString fullFileName = m_dir + wxFileName::GetPathSeparator() + fileName;
-
-    //to prvent overwritten
-    while(wxFileName::FileExists(fullFileName + _T(".png"))) fullFileName += _T("_");
-
-    //Our Bitmap now has the screenshot, so let's save it as an png
-    //The filename itself is without extension.
-    screenshot.SaveFile(fullFileName + _T(".png"), wxBITMAP_TYPE_PNG);
+    return true;
 }
 
 wxRect AutoCaptureMechanism::GetRect(wxWindow* ctrl, int flag)
 {
-    if(flag & AJ_RegionAdjust)
+    if( (!(m_flag & AJ_DisableRegionAdjust) && (flag & AJ_RegionAdjust))
+        || (m_flag & AJ_AlwaysRegionAdjust) )
     {
         wxWindow * parent = ctrl->GetParent();
         wxSizer * sizer = parent->GetSizer();
 
-        if(sizer)
-        {
-            sizer->Detach(ctrl);
-
-            /*
-            +---------+-----------+---------+
-            |    0    |   label   |    1    |
-            +---------+-----------+---------+
-            |  label  |    ctrl   |  label  |
-            +---------+-----------+---------+
-            |    2    |   label   |    3    |
-            +---------+-----------+---------+
-           */
-
-            m_grid = new wxFlexGridSizer(3, 3, m_border, m_border);
-
-            wxStaticText* l[4];
-
-            for(int i = 0; i < 4; ++i)
-                l[i] = new wxStaticText(parent, wxID_ANY, wxT(" "));
-
-            m_grid->Add(l[0]);
-            m_grid->Add(new wxStaticText(parent, wxID_ANY, wxT(" ")));
-            m_grid->Add(l[1]);
-            m_grid->Add(new wxStaticText(parent, wxID_ANY, wxT(" ")));
-            m_grid->Add(ctrl);
-            m_grid->Add(new wxStaticText(parent, wxID_ANY, wxT(" ")));
-            m_grid->Add(l[2]);
-            m_grid->Add(new wxStaticText(parent, wxID_ANY, wxT(" ")));
-            m_grid->Add(l[3]);
-
-            sizer->Add(m_grid);
-            parent->SetSizer(sizer);
-            parent->Layout();
-
-            parent->Refresh();
-            wxYield();
-
-            return wxRect(l[0]->GetScreenRect().GetBottomRight(),
-                    l[3]->GetScreenRect().GetTopLeft());
-
-        }
-        else  //Actually it won't get here working with the current guiframe.h/guiframe.cpp
-        {
-            return ctrl->GetScreenRect().Inflate(m_border);
-        }
+        //The assertion won't fail if controls are still managed by wxSizer, and it's unlikely to
+        //change in the future.
+        wxASSERT_MSG(sizer,
+        "The GUI that AutoCaptureMechanism working with doesn't manage controls with wxSizer");
+
+        sizer->Detach(ctrl);
+
+        /*
+        +---------+-----------+---------+
+        |    0    |   label   |    1    |
+        +---------+-----------+---------+
+        |  label  |    ctrl   |  label  |
+        +---------+-----------+---------+
+        |    2    |   label   |    3    |
+        +---------+-----------+---------+
+       */
+
+        m_grid = new wxFlexGridSizer(3, 3, m_margin, m_margin);
+
+        wxStaticText* l[4];
+
+        for (int i = 0; i < 4; ++i)
+            l[i] = new wxStaticText(parent, wxID_ANY, wxT(" "));
+
+        m_grid->Add(l[0]);
+        m_grid->Add(new wxStaticText(parent, wxID_ANY, wxT(" ")));
+        m_grid->Add(l[1]);
+        m_grid->Add(new wxStaticText(parent, wxID_ANY, wxT(" ")));
+        m_grid->Add(ctrl, 1, wxEXPAND);
+        m_grid->Add(new wxStaticText(parent, wxID_ANY, wxT(" ")));
+        m_grid->Add(l[2]);
+        m_grid->Add(new wxStaticText(parent, wxID_ANY, wxT(" ")));
+        m_grid->Add(l[3]);
+
+        sizer->Add(m_grid);
+        parent->SetSizer(sizer);
+        parent->Layout();
+
+        parent->Refresh();
+        wxYield();
+
+        return wxRect(l[0]->GetScreenRect().GetBottomRight(),
+                l[3]->GetScreenRect().GetTopLeft());
     }
     else
     {
-       return ctrl->GetScreenRect().Inflate(m_border);
+       return ctrl->GetScreenRect().Inflate(m_margin);
     }
 }
 
 void AutoCaptureMechanism::PutBack(wxWindow * ctrl)
 {
+    if(!m_grid) return;
+
     m_grid->Detach(ctrl);
 
     wxSizerItemList children = m_grid->GetChildren();
 
-    for(wxSizerItemList::iterator it = children.begin(); it != children.end(); ++it)
+    for (wxSizerItemList::iterator it = children.begin(); it != children.end(); ++it)
     {
         wxSizerItem* item = *it;
-        if(item->IsWindow()) delete (*it)->GetWindow();
+        if (item->IsWindow()) delete (*it)->GetWindow();
     }
 
     wxSizer * sizer = ctrl->GetParent()->GetSizer();
+
+    //The assertion won't fail if controls are still managed by wxSizer, and it's unlikely to
+    //change in the future.
+    wxASSERT_MSG(sizer,
+    "The GUI that AutoCaptureMechanism working with doesn't manage controls with wxSizer");
+
     sizer->Detach(m_grid);
     delete m_grid;
+    m_grid = NULL;
+
     sizer->Add(ctrl);
 }