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