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