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: dragimgg.cpp 42397 2006-10-25 12:12:56Z VS $
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 
IMPLEMENT_DYNAMIC_CLASS(wxGenericDragImage,wxObject)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 
Init()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 #ifdef wxHAS_NATIVE_OVERLAY
76     m_dcOverlay = NULL;
77 #else
78     m_pBackingBitmap = (wxBitmap*) NULL;
79 #endif
80 }
81 
82 #if WXWIN_COMPATIBILITY_2_6
wxGenericDragImage(const wxCursor & cursor,const wxPoint & WXUNUSED (cursorHotspot))83 wxGenericDragImage::wxGenericDragImage(const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
84 {
85     Init();
86     Create(cursor);
87 }
88 
wxGenericDragImage(const wxBitmap & image,const wxCursor & cursor,const wxPoint & WXUNUSED (cursorHotspot))89 wxGenericDragImage::wxGenericDragImage(const wxBitmap& image, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
90 {
91     Init();
92 
93     Create(image, cursor);
94 }
95 
wxGenericDragImage(const wxIcon & image,const wxCursor & cursor,const wxPoint & WXUNUSED (cursorHotspot))96 wxGenericDragImage::wxGenericDragImage(const wxIcon& image, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
97 {
98     Init();
99 
100     Create(image, cursor);
101 }
102 
wxGenericDragImage(const wxString & str,const wxCursor & cursor,const wxPoint & WXUNUSED (cursorHotspot))103 wxGenericDragImage::wxGenericDragImage(const wxString& str, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
104 {
105     Init();
106 
107     Create(str, cursor);
108 }
109 
Create(const wxCursor & cursor,const wxPoint & WXUNUSED (cursorHotspot))110 bool wxGenericDragImage::Create(const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
111 {
112     return Create(cursor);
113 }
114 
Create(const wxBitmap & image,const wxCursor & cursor,const wxPoint & WXUNUSED (cursorHotspot))115 bool wxGenericDragImage::Create(const wxBitmap& image, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
116 {
117     return Create(image, cursor);
118 }
119 
Create(const wxIcon & image,const wxCursor & cursor,const wxPoint & WXUNUSED (cursorHotspot))120 bool wxGenericDragImage::Create(const wxIcon& image, const wxCursor& cursor, const wxPoint& WXUNUSED(cursorHotspot))
121 {
122     return Create(image, cursor);
123 }
124 
Create(const wxString & str,const wxCursor & cursor,const wxPoint & WXUNUSED (cursorHotspot))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)
Create(const wxCursor & cursor)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
Create(const wxBitmap & image,const wxCursor & 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
Create(const wxIcon & image,const wxCursor & 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
Create(const wxString & str,const wxCursor & 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
Create(const wxTreeCtrl & treeCtrl,wxTreeItemId & id)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
Create(const wxListCtrl & listCtrl,long id)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
BeginDrag(const wxPoint & hotspot,wxWindow * window,bool fullScreen,wxRect * rect)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 #ifndef wxHAS_NATIVE_OVERLAY
292     wxBitmap* backing = (m_pBackingBitmap ? m_pBackingBitmap : (wxBitmap*) & m_backingBitmap);
293 
294     if (!backing->Ok() || (backing->GetWidth() < clientSize.x || backing->GetHeight() < clientSize.y))
295         (*backing) = wxBitmap(clientSize.x, clientSize.y);
296 #endif // !wxHAS_NATIVE_OVERLAY
297 
298     if (!m_fullScreen)
299     {
300         m_windowDC = new wxClientDC(window);
301     }
302     else
303     {
304         m_windowDC = new wxScreenDC;
305 
306 #if 0
307         // Use m_boundingRect to limit the area considered.
308         ((wxScreenDC*) m_windowDC)->StartDrawingOnTop(rect);
309 #endif
310 
311         m_windowDC->SetClippingRegion(m_boundingRect.x, m_boundingRect.y,
312             m_boundingRect.width, m_boundingRect.height);
313     }
314 
315     return true;
316 }
317 
318 // Begin drag. hotspot is the location of the drag position relative to the upper-left
319 // corner of the image. This is full screen only. fullScreenRect gives the
320 // position of the window on the screen, to restrict the drag to.
BeginDrag(const wxPoint & hotspot,wxWindow * window,wxWindow * fullScreenRect)321 bool wxGenericDragImage::BeginDrag(const wxPoint& hotspot, wxWindow* window, wxWindow* fullScreenRect)
322 {
323     wxRect rect;
324 
325     int x = fullScreenRect->GetPosition().x;
326     int y = fullScreenRect->GetPosition().y;
327 
328     wxSize sz = fullScreenRect->GetSize();
329 
330     if (fullScreenRect->GetParent() && !fullScreenRect->IsKindOf(CLASSINFO(wxFrame)))
331         fullScreenRect->GetParent()->ClientToScreen(& x, & y);
332 
333     rect.x = x; rect.y = y;
334     rect.width = sz.x; rect.height = sz.y;
335 
336     return BeginDrag(hotspot, window, true, & rect);
337 }
338 
339 // End drag
EndDrag()340 bool wxGenericDragImage::EndDrag()
341 {
342     if (m_window)
343     {
344 #ifdef __WXMSW__
345         // Under Windows we can be pretty sure this test will give
346         // the correct results
347         if (wxWindow::GetCapture() == m_window)
348 #endif
349             m_window->ReleaseMouse();
350 
351         if (m_cursor.Ok() && m_oldCursor.Ok())
352         {
353             m_window->SetCursor(m_oldCursor);
354         }
355     }
356 
357     if (m_windowDC)
358     {
359 #ifdef wxHAS_NATIVE_OVERLAY
360         m_overlay.Reset();
361 #else
362         m_windowDC->DestroyClippingRegion();
363 #endif
364         delete m_windowDC;
365         m_windowDC = (wxDC*) NULL;
366     }
367 
368 #ifndef wxHAS_NATIVE_OVERLAY
369     m_repairBitmap = wxNullBitmap;
370 #endif
371 
372     return true;
373 }
374 
375 // Move the image: call from OnMouseMove. Pt is in window client coordinates if window
376 // is non-NULL, or in screen coordinates if NULL.
Move(const wxPoint & pt)377 bool wxGenericDragImage::Move(const wxPoint& pt)
378 {
379     wxASSERT_MSG( (m_windowDC != (wxDC*) NULL), wxT("No window DC in wxGenericDragImage::Move()") );
380 
381     wxPoint pt2(pt);
382     if (m_fullScreen)
383         pt2 = m_window->ClientToScreen(pt);
384 
385     // Erase at old position, then show at the current position
386     wxPoint oldPos = m_position;
387 
388     bool eraseOldImage = (m_isDirty && m_isShown);
389 
390     if (m_isShown)
391         RedrawImage(oldPos - m_offset, pt2 - m_offset, eraseOldImage, true);
392 
393     m_position = pt2;
394 
395     if (m_isShown)
396         m_isDirty = true;
397 
398     return true;
399 }
400 
Show()401 bool wxGenericDragImage::Show()
402 {
403     wxASSERT_MSG( (m_windowDC != (wxDC*) NULL), wxT("No window DC in wxGenericDragImage::Show()") );
404 
405     // Show at the current position
406 
407     if (!m_isShown)
408     {
409         // This is where we restore the backing bitmap, in case
410         // something has changed on the window.
411 
412 #ifndef wxHAS_NATIVE_OVERLAY
413         wxBitmap* backing = (m_pBackingBitmap ? m_pBackingBitmap : (wxBitmap*) & m_backingBitmap);
414         wxMemoryDC memDC;
415         memDC.SelectObject(* backing);
416 
417         UpdateBackingFromWindow(* m_windowDC, memDC, m_boundingRect, wxRect(0, 0, m_boundingRect.width, m_boundingRect.height));
418 
419         //memDC.Blit(0, 0, m_boundingRect.width, m_boundingRect.height, m_windowDC, m_boundingRect.x, m_boundingRect.y);
420         memDC.SelectObject(wxNullBitmap);
421 #endif // !wxHAS_NATIVE_OVERLAY
422 
423         RedrawImage(m_position - m_offset, m_position - m_offset, false, true);
424     }
425 
426     m_isShown = true;
427     m_isDirty = true;
428 
429     return true;
430 }
431 
UpdateBackingFromWindow(wxDC & windowDC,wxMemoryDC & destDC,const wxRect & sourceRect,const wxRect & destRect) const432 bool wxGenericDragImage::UpdateBackingFromWindow(wxDC& windowDC, wxMemoryDC& destDC,
433     const wxRect& sourceRect, const wxRect& destRect) const
434 {
435     return destDC.Blit(destRect.x, destRect.y, destRect.width, destRect.height, & windowDC,
436         sourceRect.x, sourceRect.y);
437 }
438 
Hide()439 bool wxGenericDragImage::Hide()
440 {
441     wxASSERT_MSG( (m_windowDC != (wxDC*) NULL), wxT("No window DC in wxGenericDragImage::Hide()") );
442 
443     // Repair the old position
444 
445     if (m_isShown && m_isDirty)
446     {
447         RedrawImage(m_position - m_offset, m_position - m_offset, true, false);
448     }
449 
450     m_isShown = false;
451     m_isDirty = false;
452 
453     return true;
454 }
455 
456 // More efficient: erase and redraw simultaneously if possible
RedrawImage(const wxPoint & oldPos,const wxPoint & newPos,bool eraseOld,bool drawNew)457 bool wxGenericDragImage::RedrawImage(const wxPoint& oldPos, const wxPoint& newPos,
458                                      bool eraseOld, bool drawNew)
459 {
460     if (!m_windowDC)
461         return false;
462 
463 #ifdef wxHAS_NATIVE_OVERLAY
464     wxDCOverlay dcoverlay( m_overlay, (wxWindowDC*) m_windowDC ) ;
465     if ( eraseOld )
466         dcoverlay.Clear() ;
467     if (drawNew)
468         DoDrawImage(*m_windowDC, newPos);
469 #else // !wxHAS_NATIVE_OVERLAY
470     wxBitmap* backing = (m_pBackingBitmap ? m_pBackingBitmap : (wxBitmap*) & m_backingBitmap);
471     if (!backing->Ok())
472         return false;
473 
474     wxRect oldRect(GetImageRect(oldPos));
475     wxRect newRect(GetImageRect(newPos));
476 
477     wxRect fullRect;
478 
479     // Full rect: the combination of both rects
480     if (eraseOld && drawNew)
481     {
482         int oldRight = oldRect.GetRight();
483         int oldBottom = oldRect.GetBottom();
484         int newRight = newRect.GetRight();
485         int newBottom = newRect.GetBottom();
486 
487         wxPoint topLeft = wxPoint(wxMin(oldPos.x, newPos.x), wxMin(oldPos.y, newPos.y));
488         wxPoint bottomRight = wxPoint(wxMax(oldRight, newRight), wxMax(oldBottom, newBottom));
489 
490         fullRect.x = topLeft.x; fullRect.y = topLeft.y;
491         fullRect.SetRight(bottomRight.x);
492         fullRect.SetBottom(bottomRight.y);
493     }
494     else if (eraseOld)
495         fullRect = oldRect;
496     else if (drawNew)
497         fullRect = newRect;
498 
499     // Make the bitmap bigger than it need be, so we don't
500     // keep reallocating all the time.
501     int excess = 50;
502 
503     if (!m_repairBitmap.Ok() || (m_repairBitmap.GetWidth() < fullRect.GetWidth() || m_repairBitmap.GetHeight() < fullRect.GetHeight()))
504     {
505         m_repairBitmap = wxBitmap(fullRect.GetWidth() + excess, fullRect.GetHeight() + excess);
506     }
507 
508     wxMemoryDC memDC;
509     memDC.SelectObject(* backing);
510 
511     wxMemoryDC memDCTemp;
512     memDCTemp.SelectObject(m_repairBitmap);
513 
514     // Draw the backing bitmap onto the repair bitmap.
515     // If full-screen, we may have specified the rect on the
516     // screen that we're using for our backing bitmap.
517     // So subtract this when we're blitting from the backing bitmap
518     // (translate from screen to backing-bitmap coords).
519 
520     memDCTemp.Blit(0, 0, fullRect.GetWidth(), fullRect.GetHeight(), & memDC, fullRect.x - m_boundingRect.x, fullRect.y - m_boundingRect.y);
521 
522     // If drawing, draw the image onto the mem DC
523     if (drawNew)
524     {
525         wxPoint pos(newPos.x - fullRect.x, newPos.y - fullRect.y) ;
526         DoDrawImage(memDCTemp, pos);
527     }
528 
529     // Now blit to the window
530     // Finally, blit the temp mem DC to the window.
531     m_windowDC->Blit(fullRect.x, fullRect.y, fullRect.width, fullRect.height, & memDCTemp, 0, 0);
532 
533     memDCTemp.SelectObject(wxNullBitmap);
534     memDC.SelectObject(wxNullBitmap);
535 #endif // wxHAS_NATIVE_OVERLAY/!wxHAS_NATIVE_OVERLAY
536 
537     return true;
538 }
539 
540 // Override this if you are using a virtual image (drawing your own image)
DoDrawImage(wxDC & dc,const wxPoint & pos) const541 bool wxGenericDragImage::DoDrawImage(wxDC& dc, const wxPoint& pos) const
542 {
543     if (m_bitmap.Ok())
544     {
545         dc.DrawBitmap(m_bitmap, pos.x, pos.y, (m_bitmap.GetMask() != 0));
546         return true;
547     }
548     else if (m_icon.Ok())
549     {
550         dc.DrawIcon(m_icon, pos.x, pos.y);
551         return true;
552     }
553     else
554         return false;
555 }
556 
557 // Override this if you are using a virtual image (drawing your own image)
GetImageRect(const wxPoint & pos) const558 wxRect wxGenericDragImage::GetImageRect(const wxPoint& pos) const
559 {
560     if (m_bitmap.Ok())
561     {
562         return wxRect(pos.x, pos.y, m_bitmap.GetWidth(), m_bitmap.GetHeight());
563     }
564     else if (m_icon.Ok())
565     {
566         return wxRect(pos.x, pos.y, m_icon.GetWidth(), m_icon.GetHeight());
567     }
568     else
569     {
570         return wxRect(pos.x, pos.y, 0, 0);
571     }
572 }
573 
574 #endif // wxUSE_DRAGIMAGE
575