--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// Name: ctrlmaskout.cpp
+// Purpose: Implement wxCtrlMaskOut class
+// Author: Utensil Candel (UtensilCandel@@gmail.com)
+// RCS-ID: $Id$
+// Licence: wxWindows license
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+// for all others, include the necessary headers
+#ifndef WX_PRECOMP
+ #include "wx/wx.h"
+#endif
+
+#ifdef __WXMAC__ // See CreateMask()
+ #include <wx/minifram.h>
+ #include <cstdlib>
+#endif
+
+#include <wx/filename.h>
+#include <wx/hashset.h>
+
+#include "ctrlmaskout.h"
+
+
+// It's copied from src/aui/framemanager.cpp and modified, a failed attempt to
+// visualize the process of taking the screenshot when wxTopLevelWindow::CanSetTransparent
+// returns false. see wxCtrlMaskOut::CreateMask for more info
+// now it shows nothing and does nothing
+/////////////////////////////////////////////////////////////////////////////
+
+class wxPseudoTransparentFrame : public wxFrame
+{
+public:
+ wxPseudoTransparentFrame(wxWindow* parent = NULL,
+ wxWindowID id = wxID_ANY,
+ const wxString& title = wxEmptyString,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize,
+ long style = wxDEFAULT_FRAME_STYLE,
+ const wxString &name = wxT("frame"))
+ : wxFrame(parent, id, title, pos, size, style | wxFRAME_SHAPED, name)
+ {
+ m_lastWidth=0;
+ m_lastHeight=0;
+ }
+
+ virtual bool SetTransparent(wxByte alpha)
+ {
+ return true;
+ }
+
+ void OnPaint(wxPaintEvent& WXUNUSED(event)){}
+
+ virtual void SetSize(const wxRect& rect){}
+ virtual void SetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO){}
+ virtual void SetSize(int width, int height){}
+ virtual void SetSize(const wxSize& size){}
+
+ virtual bool Show(bool show = true){ return true;}
+
+ void OnSize(wxSizeEvent& event)
+ {
+// // We sometimes get surplus size events
+// if ((event.GetSize().GetWidth() == m_lastWidth) &&
+// (event.GetSize().GetHeight() == m_lastHeight))
+// {
+// event.Skip();
+// return;
+// }
+//
+// Show(false);
+//
+// GetParent()->Update();
+//
+// Show(true);
+//
+//
+// m_lastWidth = event.GetSize().GetWidth();
+// m_lastHeight = event.GetSize().GetHeight();
+ }
+
+private:
+ int m_lastWidth,m_lastHeight;
+
+ DECLARE_DYNAMIC_CLASS(wxPseudoTransparentFrame)
+ DECLARE_EVENT_TABLE()
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxPseudoTransparentFrame, wxFrame)
+
+BEGIN_EVENT_TABLE(wxPseudoTransparentFrame, wxFrame)
+ EVT_PAINT(wxPseudoTransparentFrame::OnPaint)
+ EVT_SIZE(wxPseudoTransparentFrame::OnSize)
+END_EVENT_TABLE()
+
+
+
+// ----------------------------------------------------------------------------
+// wxCtrlMaskOut
+// ----------------------------------------------------------------------------
+
+wxCtrlMaskOut::wxCtrlMaskOut()
+ : m_defaultDir(_T("screenshots")),
+ m_controlName(_T("")),
+ m_currentRect(0, 0, 0, 0),
+ m_inflateBorder(5),
+ m_mask(NULL),
+ m_isTiming(false)
+{
+}
+
+wxCtrlMaskOut::~wxCtrlMaskOut()
+{
+ if (m_mask != NULL)
+ {
+ m_mask->Destroy();
+ m_mask = NULL;
+ }
+}
+
+void wxCtrlMaskOut::OnLeftButtonDown(wxMouseEvent& event)
+{
+ if (m_isTiming) event.Skip();
+
+ // Start draging at the left-top corner.
+ wxWindow * thePanel = (wxWindow *)event.GetEventObject();
+ m_currentRect.SetPosition(thePanel->ClientToScreen(event.GetPosition()));
+ m_currentRect.SetSize(wxSize(1, 1));
+
+ // Create a transparent mask to visulize the process of specifying a rect region..
+ CreateMask(thePanel);
+}
+
+void wxCtrlMaskOut::OnMouseMoving(wxMouseEvent& event)
+{
+ if (!event.Dragging())
+ {
+ event.Skip();
+ return;
+ }
+
+ wxWindow * thePanel = (wxWindow *)event.GetEventObject();
+
+ // Determine the current rect region
+ m_currentRect.SetBottomRight(thePanel->ClientToScreen(event.GetPosition()) - wxPoint(5, 5));
+ // /Because Mac can't get the left btn up event fired if we don't minus wxPoint(5, 5),
+ // /May fix it by making the panel handle this event later
+ // /This is the current trick
+ // /See also line 200 in CreateMask
+
+ // Visualize the current rect region
+ m_mask->SetSize(m_currentRect);
+}
+
+void wxCtrlMaskOut::OnLeftButtonUp(wxMouseEvent& event)
+{
+ if (m_mask == NULL)// Which means it's not after specifying a rect region
+ {
+ event.Skip();
+ return;
+ }
+
+ DestroyMask();
+
+ // End dragging at the right-bottom corner.
+ wxWindow * thePanel = (wxWindow *)event.GetEventObject();
+ m_currentRect.SetRightBottom(thePanel->ClientToScreen(event.GetPosition()));
+
+ if (event.ControlDown())
+ {
+ m_isTiming = true;
+ (new wxTimer(this))->Start(3000, wxTIMER_ONE_SHOT);
+ this->Connect(wxEVT_TIMER, wxTimerEventHandler( wxCtrlMaskOut::OnTimingFinished ), NULL, this);
+ }
+ else
+ {
+ // The final rect region is determined.
+ DetermineCtrlNameAndRect();
+
+ Capture(m_currentRect, m_controlName);
+ }
+}
+
+void wxCtrlMaskOut::OnTimingFinished(wxTimerEvent& event)
+{
+ // The final rect region is determined.
+ DetermineCtrlNameAndRect();
+
+ Capture(m_currentRect, m_controlName);
+
+ m_isTiming = false;
+ this->Disconnect(wxEVT_TIMER, wxTimerEventHandler( wxCtrlMaskOut::OnTimingFinished ), NULL, this);
+}
+
+void wxCtrlMaskOut::Capture(int x, int y, int width, int height, wxString fileName)
+{
+ // Somehow wxScreenDC.Blit() doesn't work under Mac for now. Here is a trick.
+#ifdef __WXMAC__
+
+ // wxExecute(_T("screencapture -x ") + tempfile, wxEXEC_SYNC);
+
+ system("screencapture -x /tmp/wx_screen_capture.png");
+
+ wxBitmap fullscreen;
+
+ do
+ {
+ fullscreen = wxBitmap(_T("/tmp/wx_screen_capture.png"), wxBITMAP_TYPE_PNG);
+ }
+ while(!fullscreen.IsOk());
+
+ wxBitmap screenshot = fullscreen.GetSubBitmap(wxRect(x,y,width,height));
+
+ #else // Under other paltforms, take a real screenshot
+
+ // 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);
+
+ // 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(wxNullBitmap);
+
+#endif // #ifdef __WXMAC__
+
+ // Check if m_defaultDir already existed
+ if (!wxDirExists(m_defaultDir))
+ wxMkdir(m_defaultDir);
+
+ // Our Bitmap now has the screenshot, so let's save it as an png
+ // The filename itself is without extension.
+ screenshot.SaveFile(m_defaultDir + wxFileName::GetPathSeparator() +
+ fileName + _T(".png"), wxBITMAP_TYPE_PNG);
+}
+
+void wxCtrlMaskOut::Capture(wxRect rect, wxString fileName)
+{
+ wxPoint origin = rect.GetPosition();
+ Capture(origin.x, origin.y, rect.GetWidth(), rect.GetHeight(), fileName);
+}
+
+void wxCtrlMaskOut::DetermineCtrlNameAndRect()
+{
+ // Detect windows using (n-1)*(n-1) points
+ const int n = 5;
+
+ wxPoint pt0 = m_currentRect.GetPosition();
+
+ int dx = m_currentRect.GetWidth() / n;
+ int dy = m_currentRect.GetHeight() / n;
+
+ wxPoint pt;
+ wxWindow * ctrl;
+ wxString ctrlName;
+ wxRect ctrlSize;
+
+ // use a set to make sure the same control won't be inserted twice
+ WX_DECLARE_HASH_SET(wxWindow*, wxPointerHash, wxPointerEqual, CtrlSet);
+
+ CtrlSet ctrls;
+
+ int i, j;
+ for (i = 1; i <= n - 1; ++i)
+ {
+ for (j = 1; j <= n - 1; ++j)
+ {
+ pt = wxPoint(pt0.x + i * dx, pt0.y + j * dy);
+ ctrls.insert(wxFindWindowAtPoint(pt));
+ }
+ }
+
+ // Store the manual specified rect region, we might need it
+ wxRect backup = m_currentRect;
+
+ // Reset the control region and control name
+ m_currentRect = wxRect(0, 0, 0, 0);
+ m_controlName = _T("");
+
+ for (CtrlSet::iterator it = ctrls.begin(); it != ctrls.end(); ++it )
+ {
+ ctrl = *it;
+ if (ctrl == NULL)
+ // which means no control was detected.
+ // This seldom happens, but without dealing with it, it will CRASH the program
+ {
+ static int count = 0;
+ ++count;
+ m_controlName = _T("nothing_");
+ m_controlName << count;
+ m_currentRect = backup;
+ return;
+ }
+ ctrlName = ctrl->GetClassInfo()->GetClassName();
+
+ if (ctrlName != _T("wxPanel")) // Avoid detecting "wxPanel", which is only the backgroud
+ {
+ // Get a rect region to contain every window detected by unioning one by one.
+ m_currentRect.Union(ctrl->GetScreenRect());
+
+ // Get the name of the control, cut off "wx" and change them into lowercase.
+ // e.g. wxButton will have a filename of "button" and be saved as "button.png"
+ ctrlName.StartsWith(_T("wx"), &ctrlName);
+ ctrlName.MakeLower();
+
+ if (m_controlName.IsEmpty() || m_controlName == ctrlName)
+ {
+ m_controlName = ctrlName;
+ }
+ else // more than one types of controls are in the region selected
+ {
+ m_controlName += _T("_");
+ m_controlName += ctrlName;
+ }
+ }
+ }
+
+ if (m_controlName.IsEmpty() && backup.GetHeight() > 15 && backup.GetWidth() > 15)
+ // which means no control other than the wxPanel was detected.
+ // if the size of the specified rect(backup) is smaller than 15x15,
+ // then it could be just an accident
+ {
+ static int count = 0;
+ ++count;
+ m_controlName = _T("unknown_");
+ m_controlName << count;
+ m_currentRect = backup;
+ return;
+ }
+
+ // Increase its border so that it looks nicer.
+ m_currentRect.Inflate(m_inflateBorder);
+}
+
+void wxCtrlMaskOut::CreateMask(wxWindow* parent)
+{
+ if (m_mask != NULL)
+ m_mask->Destroy();
+
+ // Make a frame to visualize the process of specifying the rect region.
+#if defined(__WXMSW__) || defined(__WXGTK__)
+ m_mask = new wxFrame(parent, wxID_ANY, wxEmptyString,
+ m_currentRect.GetPosition(), m_currentRect.GetSize(),
+ wxFRAME_TOOL_WINDOW |
+ wxFRAME_FLOAT_ON_PARENT |
+ wxFRAME_NO_TASKBAR);
+// wxNO_BORDER);
+// with the border, it looks better under windows
+
+ m_mask->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION));
+
+#elif defined(__WXMAC__)
+ // Using a miniframe with float and tool styles keeps the parent
+ // frame activated and highlighted as such...
+ m_mask = new wxMiniFrame(parent, wxID_ANY, wxEmptyString,
+ m_currentRect.GetPosition(), m_currentRect.GetSize(),
+ wxFRAME_FLOAT_ON_PARENT
+ | wxFRAME_TOOL_WINDOW );
+
+ // Can't set the bg colour of a Frame in wxMac
+ wxPanel* p = new wxPanel(m_mask);
+
+ // The default wxSYS_COLOUR_ACTIVECAPTION colour is a light silver
+ // color that is really hard to see, especially transparent.
+ // Until a better system color is decided upon we'll just use
+ // blue.
+ p->SetBackgroundColour(*wxBLUE);
+
+ // So that even if the cursor run into the mask, the events are still correctly processed.
+ p->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonDown ), NULL, this);
+ p->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonUp ), NULL, this);
+ p->Connect( wxEVT_MOTION, wxMouseEventHandler( wxCtrlMaskOut::OnMouseMoving ), NULL, this);
+#endif
+
+ // If the platform doesn't support SetTransparent()
+ // we will use a dummy mask(which does and shows nothing)
+ // /this is the current trick, shall make it visible later
+ if (!m_mask->CanSetTransparent())
+ {
+ m_mask->Destroy();
+
+ m_mask = new wxPseudoTransparentFrame(parent,
+ wxID_ANY,
+ wxEmptyString,
+ wxDefaultPosition,
+ wxSize(1,1),
+ wxFRAME_TOOL_WINDOW |
+ wxFRAME_FLOAT_ON_PARENT |
+ wxFRAME_NO_TASKBAR
+ );
+
+ }
+
+ m_mask->SetTransparent(100); // Full value is 255
+ m_mask->Show(true);
+
+ // So that even if the cursor run into the mask, the events are still correctly processed.
+ m_mask->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonDown ), NULL, this);
+ m_mask->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonUp ), NULL, this);
+ m_mask->Connect( wxEVT_MOTION, wxMouseEventHandler( wxCtrlMaskOut::OnMouseMoving ), NULL, this);
+}
+
+void wxCtrlMaskOut::DestroyMask()
+{
+ m_mask->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonDown ), NULL, this);
+ m_mask->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonUp ), NULL, this);
+ m_mask->Disconnect( wxEVT_MOTION, wxMouseEventHandler( wxCtrlMaskOut::OnMouseMoving ), NULL, this);
+ wxWindow * parent = m_mask->GetParent();
+// m_mask->Destroy();
+ delete m_mask;
+
+ parent->Update();
+ m_mask = NULL;
+}
+