]> git.saurik.com Git - wxWidgets.git/blob - utils/screenshotgen/src/ctrlmaskout.cpp
implement wxEventLoop::DispatchTimeout() for wxGTK (thanks Paul) and rewrote it to...
[wxWidgets.git] / utils / screenshotgen / src / ctrlmaskout.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: ctrlmaskout.cpp
3 // Purpose: Implement CtrlMaskOut class
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__
13 #pragma hdrstop
14 #endif
15
16 // for all others, include the necessary headers wxWidgets headers)
17 #ifndef WX_PRECOMP
18 #include "wx/wx.h"
19 #endif
20
21 #include "ctrlmaskout.h"
22
23 #ifdef __WXMAC__ // See CreateMask()
24 #include <wx/minifram.h>
25 #include <cstdlib>
26 #endif
27
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);
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
35 // returns false. see CtrlMaskOut::CreateMask for more info
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 //
79 // Show(false);
80 //
81 // GetParent()->Update();
82 //
83 // Show(true);
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 // ----------------------------------------------------------------------------
107 // CtrlMaskOut
108 // ----------------------------------------------------------------------------
109
110 CtrlMaskOut::CtrlMaskOut()
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
120 CtrlMaskOut::~CtrlMaskOut()
121 {
122 if (m_mask != NULL)
123 {
124 m_mask->Destroy();
125 m_mask = NULL;
126 }
127 }
128
129 void CtrlMaskOut::OnLeftButtonDown(wxMouseEvent& event)
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
142 void CtrlMaskOut::OnMouseMoving(wxMouseEvent& event)
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
163 void CtrlMaskOut::OnLeftButtonUp(wxMouseEvent& event)
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);
181 this->Connect(wxEVT_TIMER, wxTimerEventHandler( CtrlMaskOut::OnTimingFinished ), NULL, this);
182 }
183 else
184 {
185 // The final rect region is determined.
186 DetermineCtrlNameAndRect();
187
188 Capture(m_currentRect, m_controlName);
189 }
190 }
191
192 void CtrlMaskOut::OnTimingFinished(wxTimerEvent& event)
193 {
194 // The final rect region is determined.
195 DetermineCtrlNameAndRect();
196
197 Capture(m_currentRect, m_controlName);
198
199 m_isTiming = false;
200 this->Disconnect(wxEVT_TIMER, wxTimerEventHandler( CtrlMaskOut::OnTimingFinished ), NULL, this);
201 }
202
203 void CtrlMaskOut::Capture(int x, int y, int width, int height, wxString fileName)
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
263 void CtrlMaskOut::Capture(wxRect rect, wxString fileName)
264 {
265 wxPoint origin = rect.GetPosition();
266 Capture(origin.x, origin.y, rect.GetWidth(), rect.GetHeight(), fileName);
267 }
268
269 void CtrlMaskOut::DetermineCtrlNameAndRect()
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
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
358 void CtrlMaskOut::CreateMask(wxWindow* parent)
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.
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);
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.
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);
424 }
425
426 void CtrlMaskOut::DestroyMask()
427 {
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();
433 delete m_mask;
434
435 parent->Update();
436 m_mask = NULL;
437 }
438