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