1 /////////////////////////////////////////////////////////////////////////////
2 // Name: ctrlmaskout.cpp
3 // Purpose: Implement wxCtrlMaskOut 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
21 #ifdef __WXMAC__ // See CreateMask()
22 #include <wx/minifram.h>
26 #include <wx/filename.h>
27 #include <wx/hashset.h>
29 #include "ctrlmaskout.h"
32 // It's copied from src/aui/framemanager.cpp and modified, a failed attempt to
33 // visualize the process of taking the screenshot when wxTopLevelWindow::CanSetTransparent
34 // returns false. see wxCtrlMaskOut::CreateMask for more info
35 // now it shows nothing and does nothing
36 /////////////////////////////////////////////////////////////////////////////
38 class wxPseudoTransparentFrame
: public wxFrame
41 wxPseudoTransparentFrame(wxWindow
* parent
= NULL
,
42 wxWindowID id
= wxID_ANY
,
43 const wxString
& title
= wxEmptyString
,
44 const wxPoint
& pos
= wxDefaultPosition
,
45 const wxSize
& size
= wxDefaultSize
,
46 long style
= wxDEFAULT_FRAME_STYLE
,
47 const wxString
&name
= wxT("frame"))
48 : wxFrame(parent
, id
, title
, pos
, size
, style
| wxFRAME_SHAPED
, name
)
54 virtual bool SetTransparent(wxByte alpha
)
59 void OnPaint(wxPaintEvent
& WXUNUSED(event
)){}
61 virtual void SetSize(const wxRect
& rect
){}
62 virtual void SetSize(int x
, int y
, int width
, int height
, int sizeFlags
= wxSIZE_AUTO
){}
63 virtual void SetSize(int width
, int height
){}
64 virtual void SetSize(const wxSize
& size
){}
66 virtual bool Show(bool show
= true){ return true;}
68 void OnSize(wxSizeEvent
& event
)
70 // // We sometimes get surplus size events
71 // if ((event.GetSize().GetWidth() == m_lastWidth) &&
72 // (event.GetSize().GetHeight() == m_lastHeight))
80 // GetParent()->Update();
85 // m_lastWidth = event.GetSize().GetWidth();
86 // m_lastHeight = event.GetSize().GetHeight();
90 int m_lastWidth
,m_lastHeight
;
92 DECLARE_DYNAMIC_CLASS(wxPseudoTransparentFrame
)
96 IMPLEMENT_DYNAMIC_CLASS(wxPseudoTransparentFrame
, wxFrame
)
98 BEGIN_EVENT_TABLE(wxPseudoTransparentFrame
, wxFrame
)
99 EVT_PAINT(wxPseudoTransparentFrame::OnPaint
)
100 EVT_SIZE(wxPseudoTransparentFrame::OnSize
)
105 // ----------------------------------------------------------------------------
107 // ----------------------------------------------------------------------------
109 wxCtrlMaskOut::wxCtrlMaskOut()
110 : m_defaultDir(_T("screenshots")),
111 m_controlName(_T("")),
112 m_currentRect(0, 0, 0, 0),
119 wxCtrlMaskOut::~wxCtrlMaskOut()
128 void wxCtrlMaskOut::OnLeftButtonDown(wxMouseEvent
& event
)
130 if (m_isTiming
) event
.Skip();
132 // Start draging at the left-top corner.
133 wxWindow
* thePanel
= (wxWindow
*)event
.GetEventObject();
134 m_currentRect
.SetPosition(thePanel
->ClientToScreen(event
.GetPosition()));
135 m_currentRect
.SetSize(wxSize(1, 1));
137 // Create a transparent mask to visulize the process of specifying a rect region..
138 CreateMask(thePanel
);
141 void wxCtrlMaskOut::OnMouseMoving(wxMouseEvent
& event
)
143 if (!event
.Dragging())
149 wxWindow
* thePanel
= (wxWindow
*)event
.GetEventObject();
151 // Determine the current rect region
152 m_currentRect
.SetBottomRight(thePanel
->ClientToScreen(event
.GetPosition()) - wxPoint(5, 5));
153 // /Because Mac can't get the left btn up event fired if we don't minus wxPoint(5, 5),
154 // /May fix it by making the panel handle this event later
155 // /This is the current trick
156 // /See also line 200 in CreateMask
158 // Visualize the current rect region
159 m_mask
->SetSize(m_currentRect
);
162 void wxCtrlMaskOut::OnLeftButtonUp(wxMouseEvent
& event
)
164 if (m_mask
== NULL
)// Which means it's not after specifying a rect region
172 // End dragging at the right-bottom corner.
173 wxWindow
* thePanel
= (wxWindow
*)event
.GetEventObject();
174 m_currentRect
.SetRightBottom(thePanel
->ClientToScreen(event
.GetPosition()));
176 if (event
.ControlDown())
179 (new wxTimer(this))->Start(3000, wxTIMER_ONE_SHOT
);
180 this->Connect(wxEVT_TIMER
, wxTimerEventHandler( wxCtrlMaskOut::OnTimingFinished
), NULL
, this);
184 // The final rect region is determined.
185 DetermineCtrlNameAndRect();
187 Capture(m_currentRect
, m_controlName
);
191 void wxCtrlMaskOut::OnTimingFinished(wxTimerEvent
& event
)
193 // The final rect region is determined.
194 DetermineCtrlNameAndRect();
196 Capture(m_currentRect
, m_controlName
);
199 this->Disconnect(wxEVT_TIMER
, wxTimerEventHandler( wxCtrlMaskOut::OnTimingFinished
), NULL
, this);
202 void wxCtrlMaskOut::Capture(int x
, int y
, int width
, int height
, wxString fileName
)
204 // Somehow wxScreenDC.Blit() doesn't work under Mac for now. Here is a trick.
207 // wxExecute(_T("screencapture -x ") + tempfile, wxEXEC_SYNC);
209 system("screencapture -x /tmp/wx_screen_capture.png");
215 fullscreen
= wxBitmap(_T("/tmp/wx_screen_capture.png"), wxBITMAP_TYPE_PNG
);
217 while(!fullscreen
.IsOk());
219 wxBitmap screenshot
= fullscreen
.GetSubBitmap(wxRect(x
,y
,width
,height
));
221 #else // Under other paltforms, take a real screenshot
223 // Create a DC for the whole screen area
226 // Create a Bitmap that will later on hold the screenshot image
227 // Note that the Bitmap must have a size big enough to hold the screenshot
228 // -1 means using the current default colour depth
229 wxBitmap
screenshot(width
, height
, -1);
231 // Create a memory DC that will be used for actually taking the screenshot
233 // Tell the memory DC to use our Bitmap
234 // all drawing action on the memory DC will go to the Bitmap now
235 memDC
.SelectObject(screenshot
);
236 // Blit (in this case copy) the actual screen on the memory DC
237 // and thus the Bitmap
238 memDC
.Blit( 0, // Copy to this X coordinate
239 0, // Copy to this Y coordinate
240 width
, // Copy this width
241 height
, // Copy this height
242 &dcScreen
, // From where do we copy?
243 x
, // What's the X offset in the original DC?
244 y
// What's the Y offset in the original DC?
246 // Select the Bitmap out of the memory DC by selecting a new
247 // uninitialized Bitmap
248 memDC
.SelectObject(wxNullBitmap
);
250 #endif // #ifdef __WXMAC__
252 // Check if m_defaultDir already existed
253 if (!wxDirExists(m_defaultDir
))
254 wxMkdir(m_defaultDir
);
256 // Our Bitmap now has the screenshot, so let's save it as an png
257 // The filename itself is without extension.
258 screenshot
.SaveFile(m_defaultDir
+ wxFileName::GetPathSeparator() +
259 fileName
+ _T(".png"), wxBITMAP_TYPE_PNG
);
262 void wxCtrlMaskOut::Capture(wxRect rect
, wxString fileName
)
264 wxPoint origin
= rect
.GetPosition();
265 Capture(origin
.x
, origin
.y
, rect
.GetWidth(), rect
.GetHeight(), fileName
);
268 void wxCtrlMaskOut::DetermineCtrlNameAndRect()
270 // Detect windows using (n-1)*(n-1) points
273 wxPoint pt0
= m_currentRect
.GetPosition();
275 int dx
= m_currentRect
.GetWidth() / n
;
276 int dy
= m_currentRect
.GetHeight() / n
;
283 // use a set to make sure the same control won't be inserted twice
284 WX_DECLARE_HASH_SET(wxWindow
*, wxPointerHash
, wxPointerEqual
, CtrlSet
);
289 for (i
= 1; i
<= n
- 1; ++i
)
291 for (j
= 1; j
<= n
- 1; ++j
)
293 pt
= wxPoint(pt0
.x
+ i
* dx
, pt0
.y
+ j
* dy
);
294 ctrls
.insert(wxFindWindowAtPoint(pt
));
298 // Store the manual specified rect region, we might need it
299 wxRect backup
= m_currentRect
;
301 // Reset the control region and control name
302 m_currentRect
= wxRect(0, 0, 0, 0);
303 m_controlName
= _T("");
305 for (CtrlSet::iterator it
= ctrls
.begin(); it
!= ctrls
.end(); ++it
)
309 // which means no control was detected.
310 // This seldom happens, but without dealing with it, it will CRASH the program
312 static int count
= 0;
314 m_controlName
= _T("nothing_");
315 m_controlName
<< count
;
316 m_currentRect
= backup
;
319 ctrlName
= ctrl
->GetClassInfo()->GetClassName();
321 if (ctrlName
!= _T("wxPanel")) // Avoid detecting "wxPanel", which is only the backgroud
323 // Get a rect region to contain every window detected by unioning one by one.
324 m_currentRect
.Union(ctrl
->GetScreenRect());
326 // Get the name of the control, cut off "wx" and change them into lowercase.
327 // e.g. wxButton will have a filename of "button" and be saved as "button.png"
328 ctrlName
.StartsWith(_T("wx"), &ctrlName
);
329 ctrlName
.MakeLower();
331 if (m_controlName
.IsEmpty() || m_controlName
== ctrlName
)
333 m_controlName
= ctrlName
;
335 else // more than one types of controls are in the region selected
337 m_controlName
+= _T("_");
338 m_controlName
+= ctrlName
;
343 if (m_controlName
.IsEmpty() && backup
.GetHeight() > 15 && backup
.GetWidth() > 15)
344 // which means no control other than the wxPanel was detected.
345 // if the size of the specified rect(backup) is smaller than 15x15,
346 // then it could be just an accident
348 static int count
= 0;
350 m_controlName
= _T("unknown_");
351 m_controlName
<< count
;
352 m_currentRect
= backup
;
356 // Increase its border so that it looks nicer.
357 m_currentRect
.Inflate(m_inflateBorder
);
360 void wxCtrlMaskOut::CreateMask(wxWindow
* parent
)
365 // Make a frame to visualize the process of specifying the rect region.
366 #if defined(__WXMSW__) || defined(__WXGTK__)
367 m_mask
= new wxFrame(parent
, wxID_ANY
, wxEmptyString
,
368 m_currentRect
.GetPosition(), m_currentRect
.GetSize(),
369 wxFRAME_TOOL_WINDOW
|
370 wxFRAME_FLOAT_ON_PARENT
|
373 // with the border, it looks better under windows
375 m_mask
->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION
));
377 #elif defined(__WXMAC__)
378 // Using a miniframe with float and tool styles keeps the parent
379 // frame activated and highlighted as such...
380 m_mask
= new wxMiniFrame(parent
, wxID_ANY
, wxEmptyString
,
381 m_currentRect
.GetPosition(), m_currentRect
.GetSize(),
382 wxFRAME_FLOAT_ON_PARENT
383 | wxFRAME_TOOL_WINDOW
);
385 // Can't set the bg colour of a Frame in wxMac
386 wxPanel
* p
= new wxPanel(m_mask
);
388 // The default wxSYS_COLOUR_ACTIVECAPTION colour is a light silver
389 // color that is really hard to see, especially transparent.
390 // Until a better system color is decided upon we'll just use
392 p
->SetBackgroundColour(*wxBLUE
);
394 // So that even if the cursor run into the mask, the events are still correctly processed.
395 p
->Connect( wxEVT_LEFT_DOWN
, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonDown
), NULL
, this);
396 p
->Connect( wxEVT_LEFT_UP
, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonUp
), NULL
, this);
397 p
->Connect( wxEVT_MOTION
, wxMouseEventHandler( wxCtrlMaskOut::OnMouseMoving
), NULL
, this);
400 // If the platform doesn't support SetTransparent()
401 // we will use a dummy mask(which does and shows nothing)
402 // /this is the current trick, shall make it visible later
403 if (!m_mask
->CanSetTransparent())
407 m_mask
= new wxPseudoTransparentFrame(parent
,
412 wxFRAME_TOOL_WINDOW
|
413 wxFRAME_FLOAT_ON_PARENT
|
419 m_mask
->SetTransparent(100); // Full value is 255
422 // So that even if the cursor run into the mask, the events are still correctly processed.
423 m_mask
->Connect( wxEVT_LEFT_DOWN
, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonDown
), NULL
, this);
424 m_mask
->Connect( wxEVT_LEFT_UP
, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonUp
), NULL
, this);
425 m_mask
->Connect( wxEVT_MOTION
, wxMouseEventHandler( wxCtrlMaskOut::OnMouseMoving
), NULL
, this);
428 void wxCtrlMaskOut::DestroyMask()
430 m_mask
->Disconnect( wxEVT_LEFT_DOWN
, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonDown
), NULL
, this);
431 m_mask
->Disconnect( wxEVT_LEFT_UP
, wxMouseEventHandler( wxCtrlMaskOut::OnLeftButtonUp
), NULL
, this);
432 m_mask
->Disconnect( wxEVT_MOTION
, wxMouseEventHandler( wxCtrlMaskOut::OnMouseMoving
), NULL
, this);
433 wxWindow
* parent
= m_mask
->GetParent();
434 // m_mask->Destroy();