1 /////////////////////////////////////////////////////////////////////////////
2 // Name: ctrlmaskout.cpp
3 // Purpose: Implement CtrlMaskOut class
4 // Author: Utensil Candel (UtensilCandel@@gmail.com)
6 // Licence: wxWindows license
7 /////////////////////////////////////////////////////////////////////////////
9 // For compilers that support precompilation, includes "wx/wx.h".
10 #include "wx/wxprec.h"
16 // for all others, include the necessary headers wxWidgets headers)
21 #include "ctrlmaskout.h"
23 #ifdef __WXMAC__ // See CreateMask()
24 #include <wx/minifram.h>
28 // use a set to make sure the same control won't be inserted twice
29 #include "wx/hashset.h"
30 WX_DECLARE_HASH_SET(wxWindow
*, wxPointerHash
, wxPointerEqual
, CtrlSet
);
33 // It's copied from src/aui/framemanager.cpp and modified, a failed attempt to
34 // visualize the process of taking the screenshot when wxTopLevelWindow::CanSetTransparent
35 // returns false. see CtrlMaskOut::CreateMask for more info
36 // now it shows nothing and does nothing
37 /////////////////////////////////////////////////////////////////////////////
39 class wxPseudoTransparentFrame
: public wxFrame
42 wxPseudoTransparentFrame(wxWindow
* parent
= NULL
,
43 wxWindowID id
= wxID_ANY
,
44 const wxString
& title
= wxEmptyString
,
45 const wxPoint
& pos
= wxDefaultPosition
,
46 const wxSize
& size
= wxDefaultSize
,
47 long style
= wxDEFAULT_FRAME_STYLE
,
48 const wxString
&name
= wxT("frame"))
49 : wxFrame(parent
, id
, title
, pos
, size
, style
| wxFRAME_SHAPED
, name
)
55 virtual bool SetTransparent(wxByte alpha
)
60 void OnPaint(wxPaintEvent
& WXUNUSED(event
)){}
62 virtual void SetSize(const wxRect
& rect
){}
63 virtual void SetSize(int x
, int y
, int width
, int height
, int sizeFlags
= wxSIZE_AUTO
){}
64 virtual void SetSize(int width
, int height
){}
65 virtual void SetSize(const wxSize
& size
){}
67 virtual bool Show(bool show
= true){ return true;}
69 void OnSize(wxSizeEvent
& event
)
71 // // We sometimes get surplus size events
72 // if ((event.GetSize().GetWidth() == m_lastWidth) &&
73 // (event.GetSize().GetHeight() == m_lastHeight))
81 // GetParent()->Update();
86 // m_lastWidth = event.GetSize().GetWidth();
87 // m_lastHeight = event.GetSize().GetHeight();
91 int m_lastWidth
,m_lastHeight
;
93 DECLARE_DYNAMIC_CLASS(wxPseudoTransparentFrame
)
97 IMPLEMENT_DYNAMIC_CLASS(wxPseudoTransparentFrame
, wxFrame
)
99 BEGIN_EVENT_TABLE(wxPseudoTransparentFrame
, wxFrame
)
100 EVT_PAINT(wxPseudoTransparentFrame::OnPaint
)
101 EVT_SIZE(wxPseudoTransparentFrame::OnSize
)
106 // ----------------------------------------------------------------------------
108 // ----------------------------------------------------------------------------
110 CtrlMaskOut::CtrlMaskOut()
111 : m_defaultDir(_T("screenshots")),
112 m_controlName(_T("")),
113 m_currentRect(0, 0, 0, 0),
120 CtrlMaskOut::~CtrlMaskOut()
129 void CtrlMaskOut::OnLeftButtonDown(wxMouseEvent
& event
)
131 if (m_isTiming
) event
.Skip();
133 // Start draging at the left-top corner.
134 wxWindow
* thePanel
= (wxWindow
*)event
.GetEventObject();
135 m_currentRect
.SetPosition(thePanel
->ClientToScreen(event
.GetPosition()));
136 m_currentRect
.SetSize(wxSize(1, 1));
138 // Create a transparent mask to visulize the process of specifying a rect region..
139 CreateMask(thePanel
);
142 void CtrlMaskOut::OnMouseMoving(wxMouseEvent
& event
)
144 if (!event
.Dragging())
150 wxWindow
* thePanel
= (wxWindow
*)event
.GetEventObject();
152 // Determine the current rect region
153 m_currentRect
.SetBottomRight(thePanel
->ClientToScreen(event
.GetPosition()) - wxPoint(5, 5));
154 // /Because Mac can't get the left btn up event fired if we don't minus wxPoint(5, 5),
155 // /May fix it by making the panel handle this event later
156 // /This is the current trick
157 // /See also line 200 in CreateMask
159 // Visualize the current rect region
160 m_mask
->SetSize(m_currentRect
);
163 void CtrlMaskOut::OnLeftButtonUp(wxMouseEvent
& event
)
165 if (m_mask
== NULL
)// Which means it's not after specifying a rect region
173 // End dragging at the right-bottom corner.
174 wxWindow
* thePanel
= (wxWindow
*)event
.GetEventObject();
175 m_currentRect
.SetRightBottom(thePanel
->ClientToScreen(event
.GetPosition()));
177 if (event
.ControlDown())
180 (new wxTimer(this))->Start(3000, wxTIMER_ONE_SHOT
);
181 this->Connect(wxEVT_TIMER
, wxTimerEventHandler( CtrlMaskOut::OnTimingFinished
), NULL
, this);
185 // The final rect region is determined.
186 DetermineCtrlNameAndRect();
188 Capture(m_currentRect
, m_controlName
);
192 void CtrlMaskOut::OnTimingFinished(wxTimerEvent
& event
)
194 // The final rect region is determined.
195 DetermineCtrlNameAndRect();
197 Capture(m_currentRect
, m_controlName
);
200 this->Disconnect(wxEVT_TIMER
, wxTimerEventHandler( CtrlMaskOut::OnTimingFinished
), NULL
, this);
203 void CtrlMaskOut::Capture(int x
, int y
, int width
, int height
, wxString fileName
)
205 // Somehow wxScreenDC.Blit() doesn't work under Mac for now. Here is a trick.
208 // wxExecute(_T("screencapture -x ") + tempfile, wxEXEC_SYNC);
210 system("screencapture -x /tmp/wx_screen_capture.png");
216 fullscreen
= wxBitmap(_T("/tmp/wx_screen_capture.png"), wxBITMAP_TYPE_PNG
);
218 while(!fullscreen
.IsOk());
220 wxBitmap screenshot
= fullscreen
.GetSubBitmap(wxRect(x
,y
,width
,height
));
222 #else // Under other paltforms, take a real screenshot
224 // Create a DC for the whole screen area
227 // Create a Bitmap that will later on hold the screenshot image
228 // Note that the Bitmap must have a size big enough to hold the screenshot
229 // -1 means using the current default colour depth
230 wxBitmap
screenshot(width
, height
, -1);
232 // Create a memory DC that will be used for actually taking the screenshot
234 // Tell the memory DC to use our Bitmap
235 // all drawing action on the memory DC will go to the Bitmap now
236 memDC
.SelectObject(screenshot
);
237 // Blit (in this case copy) the actual screen on the memory DC
238 // and thus the Bitmap
239 memDC
.Blit( 0, // Copy to this X coordinate
240 0, // Copy to this Y coordinate
241 width
, // Copy this width
242 height
, // Copy this height
243 &dcScreen
, // From where do we copy?
244 x
, // What's the X offset in the original DC?
245 y
// What's the Y offset in the original DC?
247 // Select the Bitmap out of the memory DC by selecting a new
248 // uninitialized Bitmap
249 memDC
.SelectObject(wxNullBitmap
);
251 #endif // #ifdef __WXMAC__
253 // Check if m_defaultDir already existed
254 if (!wxDirExists(m_defaultDir
))
255 wxMkdir(m_defaultDir
);
257 // Our Bitmap now has the screenshot, so let's save it as an png
258 // The filename itself is without extension.
259 screenshot
.SaveFile(m_defaultDir
+ wxFileName::GetPathSeparator() +
260 fileName
+ _T(".png"), wxBITMAP_TYPE_PNG
);
263 void CtrlMaskOut::Capture(wxRect rect
, wxString fileName
)
265 wxPoint origin
= rect
.GetPosition();
266 Capture(origin
.x
, origin
.y
, rect
.GetWidth(), rect
.GetHeight(), fileName
);
269 void CtrlMaskOut::DetermineCtrlNameAndRect()
271 // Detect windows using (n-1)*(n-1) points
274 wxPoint pt0
= m_currentRect
.GetPosition();
276 int dx
= m_currentRect
.GetWidth() / n
;
277 int dy
= m_currentRect
.GetHeight() / n
;
287 for (i
= 1; i
<= n
- 1; ++i
)
289 for (j
= 1; j
<= n
- 1; ++j
)
291 pt
= wxPoint(pt0
.x
+ i
* dx
, pt0
.y
+ j
* dy
);
292 ctrls
.insert(wxFindWindowAtPoint(pt
));
296 // Store the manual specified rect region, we might need it
297 wxRect backup
= m_currentRect
;
299 // Reset the control region and control name
300 m_currentRect
= wxRect(0, 0, 0, 0);
301 m_controlName
= _T("");
303 for (CtrlSet::iterator it
= ctrls
.begin(); it
!= ctrls
.end(); ++it
)
307 // which means no control was detected.
308 // This seldom happens, but without dealing with it, it will CRASH the program
310 static int count
= 0;
312 m_controlName
= _T("nothing_");
313 m_controlName
<< count
;
314 m_currentRect
= backup
;
317 ctrlName
= ctrl
->GetClassInfo()->GetClassName();
319 if (ctrlName
!= _T("wxPanel")) // Avoid detecting "wxPanel", which is only the backgroud
321 // Get a rect region to contain every window detected by unioning one by one.
322 m_currentRect
.Union(ctrl
->GetScreenRect());
324 // Get the name of the control, cut off "wx" and change them into lowercase.
325 // e.g. wxButton will have a filename of "button" and be saved as "button.png"
326 ctrlName
.StartsWith(_T("wx"), &ctrlName
);
327 ctrlName
.MakeLower();
329 if (m_controlName
.IsEmpty() || m_controlName
== ctrlName
)
331 m_controlName
= ctrlName
;
333 else // more than one types of controls are in the region selected
335 m_controlName
+= _T("_");
336 m_controlName
+= ctrlName
;
341 if (m_controlName
.IsEmpty() && backup
.GetHeight() > 15 && backup
.GetWidth() > 15)
342 // which means no control other than the wxPanel was detected.
343 // if the size of the specified rect(backup) is smaller than 15x15,
344 // then it could be just an accident
346 static int count
= 0;
348 m_controlName
= _T("unknown_");
349 m_controlName
<< count
;
350 m_currentRect
= backup
;
354 // Increase its border so that it looks nicer.
355 m_currentRect
.Inflate(m_inflateBorder
);
358 void CtrlMaskOut::CreateMask(wxWindow
* parent
)
363 // Make a frame to visualize the process of specifying the rect region.
364 #if defined(__WXMSW__) || defined(__WXGTK__)
365 m_mask
= new wxFrame(parent
, wxID_ANY
, wxEmptyString
,
366 m_currentRect
.GetPosition(), m_currentRect
.GetSize(),
367 wxFRAME_TOOL_WINDOW
|
368 wxFRAME_FLOAT_ON_PARENT
|
371 // with the border, it looks better under windows
373 m_mask
->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION
));
375 #elif defined(__WXMAC__)
376 // Using a miniframe with float and tool styles keeps the parent
377 // frame activated and highlighted as such...
378 m_mask
= new wxMiniFrame(parent
, wxID_ANY
, wxEmptyString
,
379 m_currentRect
.GetPosition(), m_currentRect
.GetSize(),
380 wxFRAME_FLOAT_ON_PARENT
381 | wxFRAME_TOOL_WINDOW
);
383 // Can't set the bg colour of a Frame in wxMac
384 wxPanel
* p
= new wxPanel(m_mask
);
386 // The default wxSYS_COLOUR_ACTIVECAPTION colour is a light silver
387 // color that is really hard to see, especially transparent.
388 // Until a better system color is decided upon we'll just use
390 p
->SetBackgroundColour(*wxBLUE
);
392 // So that even if the cursor run into the mask, the events are still correctly processed.
393 p
->Connect( wxEVT_LEFT_DOWN
, wxMouseEventHandler( CtrlMaskOut::OnLeftButtonDown
), NULL
, this);
394 p
->Connect( wxEVT_LEFT_UP
, wxMouseEventHandler( CtrlMaskOut::OnLeftButtonUp
), NULL
, this);
395 p
->Connect( wxEVT_MOTION
, wxMouseEventHandler( CtrlMaskOut::OnMouseMoving
), NULL
, this);
398 // If the platform doesn't support SetTransparent()
399 // we will use a dummy mask(which does and shows nothing)
400 // /this is the current trick, shall make it visible later
401 if (!m_mask
->CanSetTransparent())
405 m_mask
= new wxPseudoTransparentFrame(parent
,
410 wxFRAME_TOOL_WINDOW
|
411 wxFRAME_FLOAT_ON_PARENT
|
417 m_mask
->SetTransparent(100); // Full value is 255
420 // So that even if the cursor run into the mask, the events are still correctly processed.
421 m_mask
->Connect( wxEVT_LEFT_DOWN
, wxMouseEventHandler( CtrlMaskOut::OnLeftButtonDown
), NULL
, this);
422 m_mask
->Connect( wxEVT_LEFT_UP
, wxMouseEventHandler( CtrlMaskOut::OnLeftButtonUp
), NULL
, this);
423 m_mask
->Connect( wxEVT_MOTION
, wxMouseEventHandler( CtrlMaskOut::OnMouseMoving
), NULL
, this);
426 void CtrlMaskOut::DestroyMask()
428 m_mask
->Disconnect( wxEVT_LEFT_DOWN
, wxMouseEventHandler( CtrlMaskOut::OnLeftButtonDown
), NULL
, this);
429 m_mask
->Disconnect( wxEVT_LEFT_UP
, wxMouseEventHandler( CtrlMaskOut::OnLeftButtonUp
), NULL
, this);
430 m_mask
->Disconnect( wxEVT_MOTION
, wxMouseEventHandler( CtrlMaskOut::OnMouseMoving
), NULL
, this);
431 wxWindow
* parent
= m_mask
->GetParent();
432 // m_mask->Destroy();