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