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