adding overlay implementation for core graphics
[wxWidgets.git] / src / generic / dragimgg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/dragimgg.cpp
3 // Purpose: Generic wxDragImage implementation
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 29/2/2000
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_DRAGIMAGE
28
29 #ifndef WX_PRECOMP
30 #include <stdio.h>
31 #include "wx/window.h"
32 #include "wx/frame.h"
33 #include "wx/dcclient.h"
34 #include "wx/dcscreen.h"
35 #include "wx/dcmemory.h"
36 #include "wx/settings.h"
37 #include "wx/intl.h"
38 #include "wx/log.h"
39 #include "wx/image.h"
40 #endif
41
42 #define wxUSE_IMAGE_IN_DRAGIMAGE 1
43
44 #include "wx/generic/dragimgg.h"
45
46 // ----------------------------------------------------------------------------
47 // macros
48 // ----------------------------------------------------------------------------
49
50 IMPLEMENT_DYNAMIC_CLASS(wxGenericDragImage, wxObject)
51
52 // ============================================================================
53 // implementation
54 // ============================================================================
55
56 // ----------------------------------------------------------------------------
57 // wxGenericDragImage ctors/dtor
58 // ----------------------------------------------------------------------------
59
60 wxGenericDragImage::~wxGenericDragImage()
61 {
62 if (m_windowDC)
63 {
64 delete m_windowDC;
65 }
66 }
67
68 void wxGenericDragImage::Init()
69 {
70 m_isDirty = false;
71 m_isShown = false;
72 m_windowDC = (wxDC*) NULL;
73 m_window = (wxWindow*) NULL;
74 m_fullScreen = false;
75 #if defined(wxMAC_USE_CORE_GRAPHICS) && wxMAC_USE_CORE_GRAPHICS
76 m_dcOverlay = NULL;
77 #else
78 m_pBackingBitmap = (wxBitmap*) NULL;
79 #endif
80 }
81
82 #if WXWIN_COMPATIBILITY_2_6
83 wxGenericDragImage::wxGenericDragImage(const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
84 {
85 Init();
86 Create(cursor);
87 }
88
89 wxGenericDragImage::wxGenericDragImage(const wxBitmap& image, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
90 {
91 Init();
92
93 Create(image, cursor);
94 }
95
96 wxGenericDragImage::wxGenericDragImage(const wxIcon& image, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
97 {
98 Init();
99
100 Create(image, cursor);
101 }
102
103 wxGenericDragImage::wxGenericDragImage(const wxString& str, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
104 {
105 Init();
106
107 Create(str, cursor);
108 }
109
110 bool wxGenericDragImage::Create(const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
111 {
112 return Create(cursor);
113 }
114
115 bool wxGenericDragImage::Create(const wxBitmap& image, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
116 {
117 return Create(image, cursor);
118 }
119
120 bool wxGenericDragImage::Create(const wxIcon& image, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
121 {
122 return Create(image, cursor);
123 }
124
125 bool wxGenericDragImage::Create(const wxString& str, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
126 {
127 return Create(str, cursor);
128 }
129 #endif // WXWIN_COMPATIBILITY_2_6
130
131 // Attributes
132 ////////////////////////////////////////////////////////////////////////////
133
134
135 // Operations
136 ////////////////////////////////////////////////////////////////////////////
137
138 // Create a drag image with a virtual image (need to override DoDrawImage, GetImageRect)
139 bool wxGenericDragImage::Create(const wxCursor& cursor)
140 {
141 m_cursor = cursor;
142
143 return true;
144 }
145
146 // Create a drag image from a bitmap and optional cursor
147 bool wxGenericDragImage::Create(const wxBitmap& image, const wxCursor& cursor)
148 {
149 // We don't have to combine the cursor explicitly since we simply show the cursor
150 // as we drag. This currently will only work within one window.
151
152 m_cursor = cursor;
153 m_bitmap = image;
154
155 return true ;
156 }
157
158 // Create a drag image from an icon and optional cursor
159 bool wxGenericDragImage::Create(const wxIcon& image, const wxCursor& cursor)
160 {
161 // We don't have to combine the cursor explicitly since we simply show the cursor
162 // as we drag. This currently will only work within one window.
163
164 m_cursor = cursor;
165 m_icon = image;
166
167 return true ;
168 }
169
170 // Create a drag image from a string and optional cursor
171 bool wxGenericDragImage::Create(const wxString& str, const wxCursor& cursor)
172 {
173 wxFont font(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
174
175 long w = 0, h = 0;
176 wxScreenDC dc;
177 dc.SetFont(font);
178 dc.GetTextExtent(str, & w, & h);
179 dc.SetFont(wxNullFont);
180
181 wxMemoryDC dc2;
182
183 // Sometimes GetTextExtent isn't accurate enough, so make it longer
184 wxBitmap bitmap((int) ((w+2) * 1.5), (int) h+2);
185 dc2.SelectObject(bitmap);
186
187 dc2.SetFont(font);
188 dc2.SetBackground(* wxWHITE_BRUSH);
189 dc2.Clear();
190 dc2.SetBackgroundMode(wxTRANSPARENT);
191 dc2.SetTextForeground(* wxLIGHT_GREY);
192 dc2.DrawText(str, 0, 0);
193 dc2.DrawText(str, 1, 0);
194 dc2.DrawText(str, 2, 0);
195 dc2.DrawText(str, 1, 1);
196 dc2.DrawText(str, 2, 1);
197 dc2.DrawText(str, 1, 2);
198 dc2.DrawText(str, 2, 2);
199
200 dc2.SetTextForeground(* wxBLACK);
201 dc2.DrawText(str, 1, 1);
202
203 dc2.SelectObject(wxNullBitmap);
204
205 #if wxUSE_IMAGE_IN_DRAGIMAGE && (!defined(__WXMSW__) || wxUSE_WXDIB)
206 // Make the bitmap masked
207 wxImage image = bitmap.ConvertToImage();
208 image.SetMaskColour(255, 255, 255);
209 bitmap = wxBitmap(image);
210 #endif
211
212 return Create(bitmap, cursor);
213 }
214
215 #if wxUSE_TREECTRL
216 // Create a drag image for the given tree control item
217 bool wxGenericDragImage::Create(const wxTreeCtrl& treeCtrl, wxTreeItemId& id)
218 {
219 wxString str = treeCtrl.GetItemText(id);
220 return Create(str);
221 }
222 #endif
223
224 #if wxUSE_LISTCTRL
225 // Create a drag image for the given list control item
226 bool wxGenericDragImage::Create(const wxListCtrl& listCtrl, long id)
227 {
228 wxString str = listCtrl.GetItemText(id);
229 return Create(str);
230 }
231 #endif
232
233 // Begin drag
234 bool wxGenericDragImage::BeginDrag(const wxPoint& hotspot,
235 wxWindow* window,
236 bool fullScreen,
237 wxRect* rect)
238 {
239 wxASSERT_MSG( (window != 0), wxT("Window must not be null in BeginDrag."));
240
241 // The image should be offset by this amount
242 m_offset = hotspot;
243 m_window = window;
244 m_fullScreen = fullScreen;
245
246 if (rect)
247 m_boundingRect = * rect;
248
249 m_isDirty = false;
250 m_isDirty = false;
251
252 if (window)
253 {
254 window->CaptureMouse();
255
256 if (m_cursor.Ok())
257 {
258 m_oldCursor = window->GetCursor();
259 window->SetCursor(m_cursor);
260 }
261 }
262
263 // Make a copy of the window so we can repair damage done as the image is
264 // dragged.
265
266 wxSize clientSize;
267 wxPoint pt;
268 if (!m_fullScreen)
269 {
270 clientSize = window->GetClientSize();
271 m_boundingRect.x = 0; m_boundingRect.y = 0;
272 m_boundingRect.width = clientSize.x; m_boundingRect.height = clientSize.y;
273 }
274 else
275 {
276 int w, h;
277 wxDisplaySize(& w, & h);
278 clientSize.x = w; clientSize.y = h;
279 if (rect)
280 {
281 pt.x = m_boundingRect.x; pt.y = m_boundingRect.y;
282 clientSize.x = m_boundingRect.width; clientSize.y = m_boundingRect.height;
283 }
284 else
285 {
286 m_boundingRect.x = 0; m_boundingRect.y = 0;
287 m_boundingRect.width = w; m_boundingRect.height = h;
288 }
289 }
290
291 #if defined(wxMAC_USE_CORE_GRAPHICS) && wxMAC_USE_CORE_GRAPHICS
292 // nothing to setup here
293 #else
294 wxBitmap* backing = (m_pBackingBitmap ? m_pBackingBitmap : (wxBitmap*) & m_backingBitmap);
295
296 if (!backing->Ok() || (backing->GetWidth() < clientSize.x || backing->GetHeight() < clientSize.y))
297 (*backing) = wxBitmap(clientSize.x, clientSize.y);
298 #endif
299
300 if (!m_fullScreen)
301 {
302 m_windowDC = new wxClientDC(window);
303 }
304 else
305 {
306 m_windowDC = new wxScreenDC;
307
308 #if 0
309 // Use m_boundingRect to limit the area considered.
310 ((wxScreenDC*) m_windowDC)->StartDrawingOnTop(rect);
311 #endif
312
313 m_windowDC->SetClippingRegion(m_boundingRect.x, m_boundingRect.y,
314 m_boundingRect.width, m_boundingRect.height);
315 }
316
317 return true;
318 }
319
320 // Begin drag. hotspot is the location of the drag position relative to the upper-left
321 // corner of the image. This is full screen only. fullScreenRect gives the
322 // position of the window on the screen, to restrict the drag to.
323 bool wxGenericDragImage::BeginDrag(const wxPoint& hotspot, wxWindow* window, wxWindow* fullScreenRect)
324 {
325 wxRect rect;
326
327 int x = fullScreenRect->GetPosition().x;
328 int y = fullScreenRect->GetPosition().y;
329
330 wxSize sz = fullScreenRect->GetSize();
331
332 if (fullScreenRect->GetParent() && !fullScreenRect->IsKindOf(CLASSINFO(wxFrame)))
333 fullScreenRect->GetParent()->ClientToScreen(& x, & y);
334
335 rect.x = x; rect.y = y;
336 rect.width = sz.x; rect.height = sz.y;
337
338 return BeginDrag(hotspot, window, true, & rect);
339 }
340
341 // End drag
342 bool wxGenericDragImage::EndDrag()
343 {
344 if (m_window)
345 {
346 #ifdef __WXMSW__
347 // Under Windows we can be pretty sure this test will give
348 // the correct results
349 if (wxWindow::GetCapture() == m_window)
350 #endif
351 m_window->ReleaseMouse();
352
353 if (m_cursor.Ok() && m_oldCursor.Ok())
354 {
355 m_window->SetCursor(m_oldCursor);
356 }
357 }
358
359 if (m_windowDC)
360 {
361 #if defined(wxMAC_USE_CORE_GRAPHICS) && wxMAC_USE_CORE_GRAPHICS
362 m_overlay.Reset();
363 #else
364 m_windowDC->DestroyClippingRegion();
365 #endif
366 delete m_windowDC;
367 m_windowDC = (wxDC*) NULL;
368 }
369
370 #if defined(wxMAC_USE_CORE_GRAPHICS) && wxMAC_USE_CORE_GRAPHICS
371 // nothing to do for overlays
372 #else
373 m_repairBitmap = wxNullBitmap;
374 #endif
375
376 return true;
377 }
378
379 // Move the image: call from OnMouseMove. Pt is in window client coordinates if window
380 // is non-NULL, or in screen coordinates if NULL.
381 bool wxGenericDragImage::Move(const wxPoint& pt)
382 {
383 wxASSERT_MSG( (m_windowDC != (wxDC*) NULL), wxT("No window DC in wxGenericDragImage::Move()") );
384
385 wxPoint pt2(pt);
386 if (m_fullScreen)
387 pt2 = m_window->ClientToScreen(pt);
388
389 // Erase at old position, then show at the current position
390 wxPoint oldPos = m_position;
391
392 bool eraseOldImage = (m_isDirty && m_isShown);
393
394 if (m_isShown)
395 RedrawImage(oldPos - m_offset, pt2 - m_offset, eraseOldImage, true);
396
397 m_position = pt2;
398
399 if (m_isShown)
400 m_isDirty = true;
401
402 return true;
403 }
404
405 bool wxGenericDragImage::Show()
406 {
407 wxASSERT_MSG( (m_windowDC != (wxDC*) NULL), wxT("No window DC in wxGenericDragImage::Show()") );
408
409 // Show at the current position
410
411 if (!m_isShown)
412 {
413 // This is where we restore the backing bitmap, in case
414 // something has changed on the window.
415
416 #if defined(wxMAC_USE_CORE_GRAPHICS) && wxMAC_USE_CORE_GRAPHICS
417 // overlay will be set up in the drawing routine
418 #else
419 wxBitmap* backing = (m_pBackingBitmap ? m_pBackingBitmap : (wxBitmap*) & m_backingBitmap);
420 wxMemoryDC memDC;
421 memDC.SelectObject(* backing);
422
423 UpdateBackingFromWindow(* m_windowDC, memDC, m_boundingRect, wxRect(0, 0, m_boundingRect.width, m_boundingRect.height));
424
425 //memDC.Blit(0, 0, m_boundingRect.width, m_boundingRect.height, m_windowDC, m_boundingRect.x, m_boundingRect.y);
426 memDC.SelectObject(wxNullBitmap);
427 #endif
428
429 RedrawImage(m_position - m_offset, m_position - m_offset, false, true);
430 }
431
432 m_isShown = true;
433 m_isDirty = true;
434
435 return true;
436 }
437
438 bool wxGenericDragImage::UpdateBackingFromWindow(wxDC& windowDC, wxMemoryDC& destDC,
439 const wxRect& sourceRect, const wxRect& destRect) const
440 {
441 return destDC.Blit(destRect.x, destRect.y, destRect.width, destRect.height, & windowDC,
442 sourceRect.x, sourceRect.y);
443 }
444
445 bool wxGenericDragImage::Hide()
446 {
447 wxASSERT_MSG( (m_windowDC != (wxDC*) NULL), wxT("No window DC in wxGenericDragImage::Hide()") );
448
449 // Repair the old position
450
451 if (m_isShown && m_isDirty)
452 {
453 RedrawImage(m_position - m_offset, m_position - m_offset, true, false);
454 }
455
456 m_isShown = false;
457 m_isDirty = false;
458
459 return true;
460 }
461
462 // More efficient: erase and redraw simultaneously if possible
463 bool wxGenericDragImage::RedrawImage(const wxPoint& oldPos, const wxPoint& newPos,
464 bool eraseOld, bool drawNew)
465 {
466 if (!m_windowDC)
467 return false;
468
469 #if defined(wxMAC_USE_CORE_GRAPHICS) && wxMAC_USE_CORE_GRAPHICS
470 wxDCOverlay dcoverlay( m_overlay, (wxWindowDC*) m_windowDC ) ;
471 if ( eraseOld )
472 dcoverlay.Clear() ;
473 if (drawNew)
474 DoDrawImage(*m_windowDC, newPos);
475 #else
476 wxBitmap* backing = (m_pBackingBitmap ? m_pBackingBitmap : (wxBitmap*) & m_backingBitmap);
477 if (!backing->Ok())
478 return false;
479
480 wxRect oldRect(GetImageRect(oldPos));
481 wxRect newRect(GetImageRect(newPos));
482
483 wxRect fullRect;
484
485 // Full rect: the combination of both rects
486 if (eraseOld && drawNew)
487 {
488 int oldRight = oldRect.GetRight();
489 int oldBottom = oldRect.GetBottom();
490 int newRight = newRect.GetRight();
491 int newBottom = newRect.GetBottom();
492
493 wxPoint topLeft = wxPoint(wxMin(oldPos.x, newPos.x), wxMin(oldPos.y, newPos.y));
494 wxPoint bottomRight = wxPoint(wxMax(oldRight, newRight), wxMax(oldBottom, newBottom));
495
496 fullRect.x = topLeft.x; fullRect.y = topLeft.y;
497 fullRect.SetRight(bottomRight.x);
498 fullRect.SetBottom(bottomRight.y);
499 }
500 else if (eraseOld)
501 fullRect = oldRect;
502 else if (drawNew)
503 fullRect = newRect;
504
505 // Make the bitmap bigger than it need be, so we don't
506 // keep reallocating all the time.
507 int excess = 50;
508
509 if (!m_repairBitmap.Ok() || (m_repairBitmap.GetWidth() < fullRect.GetWidth() || m_repairBitmap.GetHeight() < fullRect.GetHeight()))
510 {
511 m_repairBitmap = wxBitmap(fullRect.GetWidth() + excess, fullRect.GetHeight() + excess);
512 }
513
514 wxMemoryDC memDC;
515 memDC.SelectObject(* backing);
516
517 wxMemoryDC memDCTemp;
518 memDCTemp.SelectObject(m_repairBitmap);
519
520 // Draw the backing bitmap onto the repair bitmap.
521 // If full-screen, we may have specified the rect on the
522 // screen that we're using for our backing bitmap.
523 // So subtract this when we're blitting from the backing bitmap
524 // (translate from screen to backing-bitmap coords).
525
526 memDCTemp.Blit(0, 0, fullRect.GetWidth(), fullRect.GetHeight(), & memDC, fullRect.x - m_boundingRect.x, fullRect.y - m_boundingRect.y);
527
528 // If drawing, draw the image onto the mem DC
529 if (drawNew)
530 {
531 wxPoint pos(newPos.x - fullRect.x, newPos.y - fullRect.y) ;
532 DoDrawImage(memDCTemp, pos);
533 }
534
535 // Now blit to the window
536 // Finally, blit the temp mem DC to the window.
537 m_windowDC->Blit(fullRect.x, fullRect.y, fullRect.width, fullRect.height, & memDCTemp, 0, 0);
538
539 memDCTemp.SelectObject(wxNullBitmap);
540 memDC.SelectObject(wxNullBitmap);
541 #endif
542 return true;
543 }
544
545 // Override this if you are using a virtual image (drawing your own image)
546 bool wxGenericDragImage::DoDrawImage(wxDC& dc, const wxPoint& pos) const
547 {
548 if (m_bitmap.Ok())
549 {
550 dc.DrawBitmap(m_bitmap, pos.x, pos.y, (m_bitmap.GetMask() != 0));
551 return true;
552 }
553 else if (m_icon.Ok())
554 {
555 dc.DrawIcon(m_icon, pos.x, pos.y);
556 return true;
557 }
558 else
559 return false;
560 }
561
562 // Override this if you are using a virtual image (drawing your own image)
563 wxRect wxGenericDragImage::GetImageRect(const wxPoint& pos) const
564 {
565 if (m_bitmap.Ok())
566 {
567 return wxRect(pos.x, pos.y, m_bitmap.GetWidth(), m_bitmap.GetHeight());
568 }
569 else if (m_icon.Ok())
570 {
571 return wxRect(pos.x, pos.y, m_icon.GetWidth(), m_icon.GetHeight());
572 }
573 else
574 {
575 return wxRect(pos.x, pos.y, 0, 0);
576 }
577 }
578
579 #endif // wxUSE_DRAGIMAGE