]>
Commit | Line | Data |
---|---|---|
0d5eda9c FM |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: ctrlmaskout.cpp | |
4bae10bd | 3 | // Purpose: Implement CtrlMaskOut class |
0d5eda9c FM |
4 | // Author: Utensil Candel (UtensilCandel@@gmail.com) |
5 | // RCS-ID: $Id$ | |
6 | // Licence: wxWindows license | |
7 | ///////////////////////////////////////////////////////////////////////////// | |
8 | ||
9 | // For compilers that support precompilation, includes "wx/wx.h". | |
10 | #include "wx/wxprec.h" | |
11 | ||
12 | #ifdef __BORLANDC__ | |
f978831f | 13 | #pragma hdrstop |
0d5eda9c FM |
14 | #endif |
15 | ||
f978831f | 16 | // for all others, include the necessary headers wxWidgets headers) |
0d5eda9c | 17 | #ifndef WX_PRECOMP |
f978831f | 18 | #include "wx/wx.h" |
0d5eda9c FM |
19 | #endif |
20 | ||
f978831f BP |
21 | #include "ctrlmaskout.h" |
22 | ||
0d5eda9c | 23 | #ifdef __WXMAC__ // See CreateMask() |
f978831f BP |
24 | #include <wx/minifram.h> |
25 | #include <cstdlib> | |
0d5eda9c FM |
26 | #endif |
27 | ||
f978831f BP |
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); | |
0d5eda9c FM |
31 | |
32 | ||
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 | |
4bae10bd | 35 | // returns false. see CtrlMaskOut::CreateMask for more info |
0d5eda9c FM |
36 | // now it shows nothing and does nothing |
37 | ///////////////////////////////////////////////////////////////////////////// | |
38 | ||
39 | class wxPseudoTransparentFrame : public wxFrame | |
40 | { | |
41 | public: | |
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) | |
50 | { | |
51 | m_lastWidth=0; | |
52 | m_lastHeight=0; | |
53 | } | |
54 | ||
55 | virtual bool SetTransparent(wxByte alpha) | |
56 | { | |
57 | return true; | |
58 | } | |
59 | ||
60 | void OnPaint(wxPaintEvent& WXUNUSED(event)){} | |
61 | ||
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){} | |
66 | ||
67 | virtual bool Show(bool show = true){ return true;} | |
68 | ||
69 | void OnSize(wxSizeEvent& event) | |
70 | { | |
71 | // // We sometimes get surplus size events | |
72 | // if ((event.GetSize().GetWidth() == m_lastWidth) && | |
73 | // (event.GetSize().GetHeight() == m_lastHeight)) | |
74 | // { | |
75 | // event.Skip(); | |
76 | // return; | |
77 | // } | |
78 | // | |
f978831f | 79 | // Show(false); |
0d5eda9c | 80 | // |
f978831f | 81 | // GetParent()->Update(); |
0d5eda9c | 82 | // |
f978831f | 83 | // Show(true); |
0d5eda9c FM |
84 | // |
85 | // | |
86 | // m_lastWidth = event.GetSize().GetWidth(); | |
87 | // m_lastHeight = event.GetSize().GetHeight(); | |
88 | } | |
89 | ||
90 | private: | |
91 | int m_lastWidth,m_lastHeight; | |
92 | ||
93 | DECLARE_DYNAMIC_CLASS(wxPseudoTransparentFrame) | |
94 | DECLARE_EVENT_TABLE() | |
95 | }; | |
96 | ||
97 | IMPLEMENT_DYNAMIC_CLASS(wxPseudoTransparentFrame, wxFrame) | |
98 | ||
99 | BEGIN_EVENT_TABLE(wxPseudoTransparentFrame, wxFrame) | |
100 | EVT_PAINT(wxPseudoTransparentFrame::OnPaint) | |
101 | EVT_SIZE(wxPseudoTransparentFrame::OnSize) | |
102 | END_EVENT_TABLE() | |
103 | ||
104 | ||
105 | ||
106 | // ---------------------------------------------------------------------------- | |
4bae10bd | 107 | // CtrlMaskOut |
0d5eda9c FM |
108 | // ---------------------------------------------------------------------------- |
109 | ||
4bae10bd | 110 | CtrlMaskOut::CtrlMaskOut() |
0d5eda9c FM |
111 | : m_defaultDir(_T("screenshots")), |
112 | m_controlName(_T("")), | |
113 | m_currentRect(0, 0, 0, 0), | |
114 | m_inflateBorder(5), | |
115 | m_mask(NULL), | |
116 | m_isTiming(false) | |
117 | { | |
118 | } | |
119 | ||
4bae10bd | 120 | CtrlMaskOut::~CtrlMaskOut() |
0d5eda9c FM |
121 | { |
122 | if (m_mask != NULL) | |
123 | { | |
124 | m_mask->Destroy(); | |
125 | m_mask = NULL; | |
126 | } | |
127 | } | |
128 | ||
4bae10bd | 129 | void CtrlMaskOut::OnLeftButtonDown(wxMouseEvent& event) |
0d5eda9c FM |
130 | { |
131 | if (m_isTiming) event.Skip(); | |
132 | ||
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)); | |
137 | ||
138 | // Create a transparent mask to visulize the process of specifying a rect region.. | |
139 | CreateMask(thePanel); | |
140 | } | |
141 | ||
4bae10bd | 142 | void CtrlMaskOut::OnMouseMoving(wxMouseEvent& event) |
0d5eda9c FM |
143 | { |
144 | if (!event.Dragging()) | |
145 | { | |
146 | event.Skip(); | |
147 | return; | |
148 | } | |
149 | ||
150 | wxWindow * thePanel = (wxWindow *)event.GetEventObject(); | |
151 | ||
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 | |
158 | ||
159 | // Visualize the current rect region | |
160 | m_mask->SetSize(m_currentRect); | |
161 | } | |
162 | ||
4bae10bd | 163 | void CtrlMaskOut::OnLeftButtonUp(wxMouseEvent& event) |
0d5eda9c FM |
164 | { |
165 | if (m_mask == NULL)// Which means it's not after specifying a rect region | |
166 | { | |
167 | event.Skip(); | |
168 | return; | |
169 | } | |
170 | ||
171 | DestroyMask(); | |
172 | ||
173 | // End dragging at the right-bottom corner. | |
174 | wxWindow * thePanel = (wxWindow *)event.GetEventObject(); | |
175 | m_currentRect.SetRightBottom(thePanel->ClientToScreen(event.GetPosition())); | |
176 | ||
177 | if (event.ControlDown()) | |
178 | { | |
179 | m_isTiming = true; | |
180 | (new wxTimer(this))->Start(3000, wxTIMER_ONE_SHOT); | |
4bae10bd | 181 | this->Connect(wxEVT_TIMER, wxTimerEventHandler( CtrlMaskOut::OnTimingFinished ), NULL, this); |
0d5eda9c FM |
182 | } |
183 | else | |
184 | { | |
185 | // The final rect region is determined. | |
186 | DetermineCtrlNameAndRect(); | |
187 | ||
188 | Capture(m_currentRect, m_controlName); | |
189 | } | |
190 | } | |
191 | ||
4bae10bd | 192 | void CtrlMaskOut::OnTimingFinished(wxTimerEvent& event) |
0d5eda9c FM |
193 | { |
194 | // The final rect region is determined. | |
195 | DetermineCtrlNameAndRect(); | |
196 | ||
197 | Capture(m_currentRect, m_controlName); | |
198 | ||
199 | m_isTiming = false; | |
4bae10bd | 200 | this->Disconnect(wxEVT_TIMER, wxTimerEventHandler( CtrlMaskOut::OnTimingFinished ), NULL, this); |
0d5eda9c FM |
201 | } |
202 | ||
4bae10bd | 203 | void CtrlMaskOut::Capture(int x, int y, int width, int height, wxString fileName) |
0d5eda9c FM |
204 | { |
205 | // Somehow wxScreenDC.Blit() doesn't work under Mac for now. Here is a trick. | |
206 | #ifdef __WXMAC__ | |
207 | ||
208 | // wxExecute(_T("screencapture -x ") + tempfile, wxEXEC_SYNC); | |
209 | ||
210 | system("screencapture -x /tmp/wx_screen_capture.png"); | |
211 | ||
212 | wxBitmap fullscreen; | |
213 | ||
214 | do | |
215 | { | |
216 | fullscreen = wxBitmap(_T("/tmp/wx_screen_capture.png"), wxBITMAP_TYPE_PNG); | |
217 | } | |
218 | while(!fullscreen.IsOk()); | |
219 | ||
220 | wxBitmap screenshot = fullscreen.GetSubBitmap(wxRect(x,y,width,height)); | |
221 | ||
222 | #else // Under other paltforms, take a real screenshot | |
223 | ||
224 | // Create a DC for the whole screen area | |
225 | wxScreenDC dcScreen; | |
226 | ||
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); | |
231 | ||
232 | // Create a memory DC that will be used for actually taking the screenshot | |
233 | wxMemoryDC memDC; | |
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? | |
246 | ); | |
247 | // Select the Bitmap out of the memory DC by selecting a new | |
248 | // uninitialized Bitmap | |
249 | memDC.SelectObject(wxNullBitmap); | |
250 | ||
251 | #endif // #ifdef __WXMAC__ | |
252 | ||
253 | // Check if m_defaultDir already existed | |
254 | if (!wxDirExists(m_defaultDir)) | |
255 | wxMkdir(m_defaultDir); | |
256 | ||
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); | |
261 | } | |
262 | ||
4bae10bd | 263 | void CtrlMaskOut::Capture(wxRect rect, wxString fileName) |
0d5eda9c FM |
264 | { |
265 | wxPoint origin = rect.GetPosition(); | |
266 | Capture(origin.x, origin.y, rect.GetWidth(), rect.GetHeight(), fileName); | |
267 | } | |
268 | ||
4bae10bd | 269 | void CtrlMaskOut::DetermineCtrlNameAndRect() |
0d5eda9c FM |
270 | { |
271 | // Detect windows using (n-1)*(n-1) points | |
272 | const int n = 5; | |
273 | ||
274 | wxPoint pt0 = m_currentRect.GetPosition(); | |
275 | ||
276 | int dx = m_currentRect.GetWidth() / n; | |
277 | int dy = m_currentRect.GetHeight() / n; | |
278 | ||
279 | wxPoint pt; | |
280 | wxWindow * ctrl; | |
281 | wxString ctrlName; | |
282 | wxRect ctrlSize; | |
283 | ||
0d5eda9c FM |
284 | CtrlSet ctrls; |
285 | ||
286 | int i, j; | |
287 | for (i = 1; i <= n - 1; ++i) | |
288 | { | |
289 | for (j = 1; j <= n - 1; ++j) | |
290 | { | |
291 | pt = wxPoint(pt0.x + i * dx, pt0.y + j * dy); | |
292 | ctrls.insert(wxFindWindowAtPoint(pt)); | |
293 | } | |
294 | } | |
295 | ||
296 | // Store the manual specified rect region, we might need it | |
297 | wxRect backup = m_currentRect; | |
298 | ||
299 | // Reset the control region and control name | |
300 | m_currentRect = wxRect(0, 0, 0, 0); | |
301 | m_controlName = _T(""); | |
302 | ||
303 | for (CtrlSet::iterator it = ctrls.begin(); it != ctrls.end(); ++it ) | |
304 | { | |
305 | ctrl = *it; | |
306 | if (ctrl == NULL) | |
307 | // which means no control was detected. | |
308 | // This seldom happens, but without dealing with it, it will CRASH the program | |
309 | { | |
310 | static int count = 0; | |
311 | ++count; | |
312 | m_controlName = _T("nothing_"); | |
313 | m_controlName << count; | |
314 | m_currentRect = backup; | |
315 | return; | |
316 | } | |
317 | ctrlName = ctrl->GetClassInfo()->GetClassName(); | |
318 | ||
319 | if (ctrlName != _T("wxPanel")) // Avoid detecting "wxPanel", which is only the backgroud | |
320 | { | |
321 | // Get a rect region to contain every window detected by unioning one by one. | |
322 | m_currentRect.Union(ctrl->GetScreenRect()); | |
323 | ||
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(); | |
328 | ||
329 | if (m_controlName.IsEmpty() || m_controlName == ctrlName) | |
330 | { | |
331 | m_controlName = ctrlName; | |
332 | } | |
333 | else // more than one types of controls are in the region selected | |
334 | { | |
335 | m_controlName += _T("_"); | |
336 | m_controlName += ctrlName; | |
337 | } | |
338 | } | |
339 | } | |
340 | ||
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 | |
345 | { | |
346 | static int count = 0; | |
347 | ++count; | |
348 | m_controlName = _T("unknown_"); | |
349 | m_controlName << count; | |
350 | m_currentRect = backup; | |
351 | return; | |
352 | } | |
353 | ||
354 | // Increase its border so that it looks nicer. | |
355 | m_currentRect.Inflate(m_inflateBorder); | |
356 | } | |
357 | ||
4bae10bd | 358 | void CtrlMaskOut::CreateMask(wxWindow* parent) |
0d5eda9c FM |
359 | { |
360 | if (m_mask != NULL) | |
361 | m_mask->Destroy(); | |
362 | ||
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 | | |
369 | wxFRAME_NO_TASKBAR); | |
370 | // wxNO_BORDER); | |
371 | // with the border, it looks better under windows | |
372 | ||
373 | m_mask->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION)); | |
374 | ||
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 ); | |
382 | ||
383 | // Can't set the bg colour of a Frame in wxMac | |
384 | wxPanel* p = new wxPanel(m_mask); | |
385 | ||
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 | |
389 | // blue. | |
390 | p->SetBackgroundColour(*wxBLUE); | |
391 | ||
392 | // So that even if the cursor run into the mask, the events are still correctly processed. | |
4bae10bd FM |
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); | |
0d5eda9c FM |
396 | #endif |
397 | ||
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()) | |
402 | { | |
403 | m_mask->Destroy(); | |
404 | ||
405 | m_mask = new wxPseudoTransparentFrame(parent, | |
406 | wxID_ANY, | |
407 | wxEmptyString, | |
408 | wxDefaultPosition, | |
409 | wxSize(1,1), | |
410 | wxFRAME_TOOL_WINDOW | | |
411 | wxFRAME_FLOAT_ON_PARENT | | |
412 | wxFRAME_NO_TASKBAR | |
413 | ); | |
414 | ||
415 | } | |
416 | ||
417 | m_mask->SetTransparent(100); // Full value is 255 | |
418 | m_mask->Show(true); | |
419 | ||
420 | // So that even if the cursor run into the mask, the events are still correctly processed. | |
4bae10bd FM |
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); | |
0d5eda9c FM |
424 | } |
425 | ||
4bae10bd | 426 | void CtrlMaskOut::DestroyMask() |
0d5eda9c | 427 | { |
4bae10bd FM |
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); | |
0d5eda9c FM |
431 | wxWindow * parent = m_mask->GetParent(); |
432 | // m_mask->Destroy(); | |
433 | delete m_mask; | |
434 | ||
435 | parent->Update(); | |
436 | m_mask = NULL; | |
437 | } | |
438 |