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