1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        dragimag.cpp
3 // Purpose:     wxDragImage sample
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     28/2/2000
7 // RCS-ID:      $Id: dragimag.cpp 41803 2006-10-09 15:15:13Z JS $
8 // Copyright:   (c) Julian Smart
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14 
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18 
19 #ifndef WX_PRECOMP
20 #include "wx/wx.h"
21 #endif
22 
23 #include "wx/image.h"
24 
25 // Under Windows, change this to 1
26 // to use wxGenericDragImage
27 
28 #define wxUSE_GENERIC_DRAGIMAGE 1
29 
30 #if wxUSE_GENERIC_DRAGIMAGE
31 #include "wx/generic/dragimgg.h"
32 #define wxDragImage wxGenericDragImage
33 #else
34 #include "wx/dragimag.h"
35 #endif
36 
37 #include "dragimag.h"
38 
39 #if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__) || defined(__WXX11__)
40 #include "mondrian.xpm"
41 #include "dragicon.xpm"
42 #endif
43 
44 // main program
45 
46 IMPLEMENT_APP(MyApp)
47 
48 // MyCanvas
49 
IMPLEMENT_CLASS(MyCanvas,wxScrolledWindow)50 IMPLEMENT_CLASS(MyCanvas, wxScrolledWindow)
51 
52 BEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)
53   EVT_PAINT(MyCanvas::OnPaint)
54   EVT_ERASE_BACKGROUND(MyCanvas::OnEraseBackground)
55   EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
56 END_EVENT_TABLE()
57 
58 MyCanvas::MyCanvas( wxWindow *parent, wxWindowID id,
59                     const wxPoint &pos, const wxSize &size )
60         : wxScrolledWindow( parent, id, pos, size, wxSUNKEN_BORDER )
61 {
62     SetBackgroundColour(* wxWHITE);
63 
64     SetCursor(wxCursor(wxCURSOR_ARROW));
65 
66     m_dragMode = TEST_DRAG_NONE;
67     m_draggedShape = (DragShape*) NULL;
68     m_dragImage = (wxDragImage*) NULL;
69     m_currentlyHighlighted = (DragShape*) NULL;
70 }
71 
~MyCanvas()72 MyCanvas::~MyCanvas()
73 {
74     ClearShapes();
75 
76     if (m_dragImage)
77         delete m_dragImage;
78 }
79 
OnPaint(wxPaintEvent & WXUNUSED (event))80 void MyCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) )
81 {
82     wxPaintDC dc( this );
83     PrepareDC( dc );
84 
85     DrawShapes(dc);
86 }
87 
OnEraseBackground(wxEraseEvent & event)88 void MyCanvas::OnEraseBackground(wxEraseEvent& event)
89 {
90     if (wxGetApp().GetBackgroundBitmap().Ok())
91     {
92         wxSize sz = GetClientSize();
93         wxRect rect(0, 0, sz.x, sz.y);
94 
95         if (event.GetDC())
96         {
97             wxGetApp().TileBitmap(rect, *(event.GetDC()), wxGetApp().GetBackgroundBitmap());
98         }
99         else
100         {
101             wxClientDC dc(this);
102             wxGetApp().TileBitmap(rect, dc, wxGetApp().GetBackgroundBitmap());
103         }
104     }
105     else
106         event.Skip(); // The official way of doing it
107 }
108 
OnMouseEvent(wxMouseEvent & event)109 void MyCanvas::OnMouseEvent(wxMouseEvent& event)
110 {
111     if (event.LeftDown())
112     {
113         DragShape* shape = FindShape(event.GetPosition());
114         if (shape)
115         {
116             // We tentatively start dragging, but wait for
117             // mouse movement before dragging properly.
118 
119             m_dragMode = TEST_DRAG_START;
120             m_dragStartPos = event.GetPosition();
121             m_draggedShape = shape;
122         }
123     }
124     else if (event.LeftUp() && m_dragMode != TEST_DRAG_NONE)
125     {
126         // Finish dragging
127 
128         m_dragMode = TEST_DRAG_NONE;
129 
130         if (!m_draggedShape || !m_dragImage)
131             return;
132 
133         m_draggedShape->SetPosition(m_draggedShape->GetPosition()
134                                     + event.GetPosition() - m_dragStartPos);
135 
136         m_dragImage->Hide();
137         m_dragImage->EndDrag();
138         delete m_dragImage;
139         m_dragImage = NULL;
140 
141         m_draggedShape->SetShow(true);
142 
143         m_currentlyHighlighted = (DragShape*) NULL;
144 
145         m_draggedShape = (DragShape*) NULL;
146 
147         Refresh(true);
148     }
149     else if (event.Dragging() && m_dragMode != TEST_DRAG_NONE)
150     {
151         if (m_dragMode == TEST_DRAG_START)
152         {
153             // We will start dragging if we've moved beyond a couple of pixels
154 
155             int tolerance = 2;
156             int dx = abs(event.GetPosition().x - m_dragStartPos.x);
157             int dy = abs(event.GetPosition().y - m_dragStartPos.y);
158             if (dx <= tolerance && dy <= tolerance)
159                 return;
160 
161             // Start the drag.
162             m_dragMode = TEST_DRAG_DRAGGING;
163 
164             if (m_dragImage)
165                 delete m_dragImage;
166 
167             // Erase the dragged shape from the canvas
168             m_draggedShape->SetShow(false);
169 
170             // redraw immediately
171             Refresh(true);
172             Update();
173 
174             switch (m_draggedShape->GetDragMethod())
175             {
176                 case SHAPE_DRAG_BITMAP:
177                 {
178                     m_dragImage = new MyDragImage(this, m_draggedShape->GetBitmap(), wxCursor(wxCURSOR_HAND));
179                     break;
180                 }
181                 case SHAPE_DRAG_TEXT:
182                 {
183                     m_dragImage = new MyDragImage(this, wxString(_T("Dragging some test text")), wxCursor(wxCURSOR_HAND));
184                     break;
185                 }
186                 case SHAPE_DRAG_ICON:
187                 {
188                     m_dragImage = new MyDragImage(this, wxICON(dragicon), wxCursor(wxCURSOR_HAND));
189                     break;
190                 }
191             }
192 
193             bool fullScreen = wxGetApp().GetUseScreen();
194 
195             // The offset between the top-left of the shape image and the current shape position
196             wxPoint beginDragHotSpot = m_dragStartPos - m_draggedShape->GetPosition();
197 
198             // Now we do this inside the implementation: always assume
199             // coordinates relative to the capture window (client coordinates)
200 
201             //if (fullScreen)
202             //    beginDragHotSpot -= ClientToScreen(wxPoint(0, 0));
203 
204             if (!m_dragImage->BeginDrag(beginDragHotSpot, this, fullScreen))
205             {
206                 delete m_dragImage;
207                 m_dragImage = (wxDragImage*) NULL;
208                 m_dragMode = TEST_DRAG_NONE;
209 
210             } else
211             {
212                 m_dragImage->Move(event.GetPosition());
213                 m_dragImage->Show();
214             }
215         }
216         else if (m_dragMode == TEST_DRAG_DRAGGING)
217         {
218             // We're currently dragging. See if we're over another shape.
219             DragShape* onShape = FindShape(event.GetPosition());
220 
221             bool mustUnhighlightOld = false;
222             bool mustHighlightNew = false;
223 
224             if (m_currentlyHighlighted)
225             {
226                 if ((onShape == (DragShape*) NULL) || (m_currentlyHighlighted != onShape))
227                     mustUnhighlightOld = true;
228             }
229 
230             if (onShape && (onShape != m_currentlyHighlighted) && onShape->IsShown())
231                 mustHighlightNew = true;
232 
233             if (mustUnhighlightOld || mustHighlightNew)
234                 m_dragImage->Hide();
235 
236             // Now with the drag image switched off, we can change the window contents.
237             if (mustUnhighlightOld)
238                 m_currentlyHighlighted = (DragShape*) NULL;
239 
240             if (mustHighlightNew)
241                 m_currentlyHighlighted = onShape;
242 
243             if (mustUnhighlightOld || mustHighlightNew)
244             {
245                 Refresh(mustUnhighlightOld);
246                 Update();
247             }
248 
249             // Move and show the image again
250             m_dragImage->Move(event.GetPosition());
251 
252             if (mustUnhighlightOld || mustHighlightNew)
253                  m_dragImage->Show();
254         }
255     }
256 }
257 
DrawShapes(wxDC & dc)258 void MyCanvas::DrawShapes(wxDC& dc)
259 {
260     wxList::compatibility_iterator node = m_displayList.GetFirst();
261     while (node)
262     {
263         DragShape* shape = (DragShape*) node->GetData();
264         if (shape->IsShown() && m_draggedShape != shape)
265         {
266             shape->Draw(dc, (m_currentlyHighlighted == shape));
267         }
268         node = node->GetNext();
269     }
270 }
271 
EraseShape(DragShape * shape,wxDC & dc)272 void MyCanvas::EraseShape(DragShape* shape, wxDC& dc)
273 {
274     wxSize sz = GetClientSize();
275     wxRect rect(0, 0, sz.x, sz.y);
276 
277     wxRect rect2(shape->GetRect());
278     dc.SetClippingRegion(rect2.x, rect2.y, rect2.width, rect2.height);
279 
280     wxGetApp().TileBitmap(rect, dc, wxGetApp().GetBackgroundBitmap());
281 
282     dc.DestroyClippingRegion();
283 }
284 
ClearShapes()285 void MyCanvas::ClearShapes()
286 {
287     wxList::compatibility_iterator node = m_displayList.GetFirst();
288     while (node)
289     {
290         DragShape* shape = (DragShape*) node->GetData();
291         delete shape;
292         node = node->GetNext();
293     }
294     m_displayList.Clear();
295 }
296 
FindShape(const wxPoint & pt) const297 DragShape* MyCanvas::FindShape(const wxPoint& pt) const
298 {
299     wxList::compatibility_iterator node = m_displayList.GetFirst();
300     while (node)
301     {
302         DragShape* shape = (DragShape*) node->GetData();
303         if (shape->HitTest(pt))
304             return shape;
305         node = node->GetNext();
306     }
307     return (DragShape*) NULL;
308 }
309 
310 // MyFrame
IMPLEMENT_DYNAMIC_CLASS(MyFrame,wxFrame)311 IMPLEMENT_DYNAMIC_CLASS( MyFrame, wxFrame )
312 
313 BEGIN_EVENT_TABLE(MyFrame,wxFrame)
314   EVT_MENU    (wxID_ABOUT, MyFrame::OnAbout)
315   EVT_MENU    (wxID_EXIT,  MyFrame::OnQuit)
316 END_EVENT_TABLE()
317 
318 MyFrame::MyFrame()
319 : wxFrame( (wxFrame *)NULL, wxID_ANY, _T("wxDragImage sample"),
320           wxPoint(20,20), wxSize(470,360) )
321 {
322     wxMenu *file_menu = new wxMenu();
323     file_menu->Append( wxID_ABOUT, _T("&About..."));
324     file_menu->AppendCheckItem( TEST_USE_SCREEN, _T("&Use whole screen for dragging"), _T("Use whole screen"));
325     file_menu->Append( wxID_EXIT, _T("E&xit"));
326 
327     wxMenuBar *menu_bar = new wxMenuBar();
328     menu_bar->Append(file_menu, _T("&File"));
329 
330     SetIcon(wxICON(mondrian));
331     SetMenuBar( menu_bar );
332 
333 #if wxUSE_STATUSBAR
334     CreateStatusBar(2);
335     int widths[] = { -1, 100 };
336     SetStatusWidths( 2, widths );
337 #endif // wxUSE_STATUSBAR
338 
339     m_canvas = new MyCanvas( this, wxID_ANY, wxPoint(0,0), wxSize(10,10) );
340 }
341 
OnQuit(wxCommandEvent & WXUNUSED (event))342 void MyFrame::OnQuit( wxCommandEvent &WXUNUSED(event) )
343 {
344     Close( true );
345 }
346 
OnAbout(wxCommandEvent & WXUNUSED (event))347 void MyFrame::OnAbout( wxCommandEvent &WXUNUSED(event) )
348 {
349     (void)wxMessageBox( _T("wxDragImage demo\n")
350         _T("Julian Smart (c) 2000"),
351         _T("About wxDragImage Demo"),
352         wxICON_INFORMATION | wxOK );
353 }
354 
355 //-----------------------------------------------------------------------------
356 // MyApp
357 //-----------------------------------------------------------------------------
358 
BEGIN_EVENT_TABLE(MyApp,wxApp)359 BEGIN_EVENT_TABLE(MyApp, wxApp)
360     EVT_MENU(TEST_USE_SCREEN, MyApp::OnUseScreen)
361 END_EVENT_TABLE()
362 
363 MyApp::MyApp()
364 {
365     // Drag across whole screen
366     m_useScreen = false;
367 }
368 
OnInit()369 bool MyApp::OnInit()
370 {
371 #if wxUSE_LIBPNG
372     wxImage::AddHandler( new wxPNGHandler );
373 #endif
374 
375     wxImage image;
376     if (image.LoadFile(_T("backgrnd.png"), wxBITMAP_TYPE_PNG))
377     {
378         m_background = wxBitmap(image);
379     }
380 
381     MyFrame *frame = new MyFrame();
382 
383     wxString rootName(_T("shape0"));
384 
385     int i;
386     for (i = 1; i < 4; i++)
387     {
388         wxString filename;
389         filename.Printf(wxT("%s%d.png"), (const wxChar*)rootName, i);
390     /* For some reason under wxX11, the 2nd LoadFile in this loop fails, with
391        a BadMatch inside CreateFromImage (inside ConvertToBitmap). This happens even if you copy
392        the first file over the second file. */
393         if (image.LoadFile(filename, wxBITMAP_TYPE_PNG))
394         {
395             DragShape* newShape = new DragShape(wxBitmap(image));
396             newShape->SetPosition(wxPoint(i*50, i*50));
397 
398             if (i == 2)
399                 newShape->SetDragMethod(SHAPE_DRAG_TEXT);
400             else if (i == 3)
401                 newShape->SetDragMethod(SHAPE_DRAG_ICON);
402             else
403                 newShape->SetDragMethod(SHAPE_DRAG_BITMAP);
404             frame->GetCanvas()->GetDisplayList().Append(newShape);
405         }
406     }
407 
408 #if 0
409     // Under Motif or GTK, this demonstrates that
410     // wxScreenDC only gets the root window content.
411     // We need to be able to copy the overall content
412     // for full-screen dragging to work.
413     int w, h;
414     wxDisplaySize(& w, & h);
415     wxBitmap bitmap(w, h);
416 
417     wxScreenDC dc;
418     wxMemoryDC memDC;
419     memDC.SelectObject(bitmap);
420     memDC.Blit(0, 0, w, h, & dc, 0, 0);
421     memDC.SelectObject(wxNullBitmap);
422     m_background = bitmap;
423 #endif
424 
425     frame->Show( true );
426 
427     return true;
428 }
429 
OnExit()430 int MyApp::OnExit()
431 {
432     return 0;
433 }
434 
TileBitmap(const wxRect & rect,wxDC & dc,wxBitmap & bitmap)435 bool MyApp::TileBitmap(const wxRect& rect, wxDC& dc, wxBitmap& bitmap)
436 {
437     int w = bitmap.GetWidth();
438     int h = bitmap.GetHeight();
439 
440     int i, j;
441     for (i = rect.x; i < rect.x + rect.width; i += w)
442     {
443         for (j = rect.y; j < rect.y + rect.height; j+= h)
444             dc.DrawBitmap(bitmap, i, j);
445     }
446     return true;
447 }
448 
OnUseScreen(wxCommandEvent & WXUNUSED (event))449 void MyApp::OnUseScreen(wxCommandEvent& WXUNUSED(event))
450 {
451     m_useScreen = !m_useScreen;
452 }
453 
454 // DragShape
455 
DragShape(const wxBitmap & bitmap)456 DragShape::DragShape(const wxBitmap& bitmap)
457 {
458     m_bitmap = bitmap;
459     m_pos.x = 0;
460     m_pos.y = 0;
461     m_dragMethod = SHAPE_DRAG_BITMAP;
462     m_show = true;
463 }
464 
HitTest(const wxPoint & pt) const465 bool DragShape::HitTest(const wxPoint& pt) const
466 {
467     wxRect rect(GetRect());
468     return rect.Contains(pt.x, pt.y);
469 }
470 
Draw(wxDC & dc,bool highlight)471 bool DragShape::Draw(wxDC& dc, bool highlight)
472 {
473     if (m_bitmap.Ok())
474     {
475         wxMemoryDC memDC;
476         memDC.SelectObject(m_bitmap);
477 
478         dc.Blit(m_pos.x, m_pos.y, m_bitmap.GetWidth(), m_bitmap.GetHeight(),
479             & memDC, 0, 0, wxCOPY, true);
480 
481         if (highlight)
482         {
483             dc.SetPen(*wxWHITE_PEN);
484             dc.SetBrush(*wxTRANSPARENT_BRUSH);
485             dc.DrawRectangle(m_pos.x, m_pos.y, m_bitmap.GetWidth(), m_bitmap.GetHeight());
486         }
487 
488         return true;
489     }
490     else
491         return false;
492 }
493 
494 // MyDragImage
495 
496 // On some platforms, notably Mac OS X with Core Graphics, we can't blit from
497 // a window, so we need to draw the background explicitly.
UpdateBackingFromWindow(wxDC & WXUNUSED (windowDC),wxMemoryDC & destDC,const wxRect & WXUNUSED (sourceRect),const wxRect & destRect) const498 bool MyDragImage::UpdateBackingFromWindow(wxDC& WXUNUSED(windowDC), wxMemoryDC& destDC, const wxRect& WXUNUSED(sourceRect),
499                     const wxRect& destRect) const
500 {
501     destDC.SetClippingRegion(destRect);
502 
503     if (wxGetApp().GetBackgroundBitmap().Ok())
504         wxGetApp().TileBitmap(destRect, destDC, wxGetApp().GetBackgroundBitmap());
505 
506     m_canvas->DrawShapes(destDC);
507     return true;
508 }
509 
510