wxDropSource now has 3 custom cursors for copy/move/nothing
[wxWidgets.git] / samples / dnd / dnd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dnd.cpp
3 // Purpose: Drag and drop sample
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright:
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #ifndef WX_PRECOMP
19 #include "wx/wx.h"
20 #endif
21
22 #if !wxUSE_DRAG_AND_DROP
23 #error This sample requires drag and drop support in the library
24 #endif
25
26 #include "wx/intl.h"
27 #include "wx/log.h"
28
29 #include "wx/dnd.h"
30 #include "wx/dirdlg.h"
31 #include "wx/filedlg.h"
32 #include "wx/image.h"
33 #include "wx/clipbrd.h"
34 #include "wx/colordlg.h"
35 #include "wx/resource.h"
36
37 #if defined(__WXGTK__) || defined(__WXMOTIF__)
38 #include "mondrian.xpm"
39 #endif
40
41 // ----------------------------------------------------------------------------
42 // Derive two simple classes which just put in the listbox the strings (text or
43 // file names) we drop on them
44 // ----------------------------------------------------------------------------
45
46 class DnDText : public wxTextDropTarget
47 {
48 public:
49 DnDText(wxListBox *pOwner) { m_pOwner = pOwner; }
50
51 virtual bool OnDropText(wxCoord x, wxCoord y, const wxString& text);
52
53 private:
54 wxListBox *m_pOwner;
55 };
56
57 class DnDFile : public wxFileDropTarget
58 {
59 public:
60 DnDFile(wxListBox *pOwner) { m_pOwner = pOwner; }
61
62 virtual bool OnDropFiles(wxCoord x, wxCoord y,
63 const wxArrayString& filenames);
64
65 private:
66 wxListBox *m_pOwner;
67 };
68
69 // ----------------------------------------------------------------------------
70 // Define a new application type
71 // ----------------------------------------------------------------------------
72
73 class DnDApp : public wxApp
74 {
75 public:
76 virtual bool OnInit();
77 };
78
79 IMPLEMENT_APP(DnDApp);
80
81 // ----------------------------------------------------------------------------
82 // Define a new frame type for the main frame
83 // ----------------------------------------------------------------------------
84
85 class DnDFrame : public wxFrame
86 {
87 public:
88 DnDFrame(wxFrame *frame, char *title, int x, int y, int w, int h);
89 ~DnDFrame();
90
91 void OnPaint(wxPaintEvent& event);
92 void OnQuit (wxCommandEvent& event);
93 void OnAbout(wxCommandEvent& event);
94 void OnDrag (wxCommandEvent& event);
95 void OnNewFrame(wxCommandEvent& event);
96 void OnHelp (wxCommandEvent& event);
97 void OnLogClear(wxCommandEvent& event);
98 void OnCopy(wxCommandEvent& event);
99 void OnPaste(wxCommandEvent& event);
100 void OnCopyBitmap(wxCommandEvent& event);
101 void OnPasteBitmap(wxCommandEvent& event);
102
103 void OnLeftDown(wxMouseEvent& event);
104 void OnRightDown(wxMouseEvent& event);
105
106 void OnUpdateUIPasteText(wxUpdateUIEvent& event);
107 void OnUpdateUIPasteBitmap(wxUpdateUIEvent& event);
108
109 DECLARE_EVENT_TABLE()
110
111 private:
112 wxListBox *m_ctrlFile,
113 *m_ctrlText;
114 wxTextCtrl *m_ctrlLog;
115
116 wxLog *m_pLog,
117 *m_pLogPrev;
118
119 wxString m_strText;
120 wxBitmap m_bitmap;
121 };
122
123 // ----------------------------------------------------------------------------
124 // A shape is an example of application-specific data which may be transported
125 // via drag-and-drop or clipboard: in our case, we have different geometric
126 // shapes, each one with its own colour and position
127 // ----------------------------------------------------------------------------
128
129 class DnDShape
130 {
131 public:
132 enum Kind
133 {
134 None,
135 Triangle,
136 Rectangle,
137 Ellipse
138 };
139
140 DnDShape(const wxPoint& pos,
141 const wxSize& size,
142 const wxColour& col)
143 : m_pos(pos), m_size(size), m_col(col)
144 {
145 }
146
147 // this is for debugging - lets us see when exactly an object is freed
148 // (this may be later than you think if it's on the clipboard, for example)
149 virtual ~DnDShape() { }
150
151 // the functions used for drag-and-drop: they dump and restore a shape into
152 // some bitwise-copiable data (might use streams too...)
153 // ------------------------------------------------------------------------
154
155 // restore from buffer
156 static DnDShape *New(const void *buf);
157
158 virtual size_t GetDataSize() const
159 {
160 return sizeof(ShapeDump);
161 }
162
163 virtual void GetDataHere(void *buf) const
164 {
165 ShapeDump& dump = *(ShapeDump *)buf;
166 dump.x = m_pos.x;
167 dump.y = m_pos.y;
168 dump.w = m_size.x;
169 dump.h = m_size.y;
170 dump.r = m_col.Red();
171 dump.g = m_col.Green();
172 dump.b = m_col.Blue();
173 dump.k = GetKind();
174 }
175
176 // accessors
177 const wxPoint& GetPosition() const { return m_pos; }
178 const wxColour& GetColour() const { return m_col; }
179 const wxSize& GetSize() const { return m_size; }
180
181 void Move(const wxPoint& pos) { m_pos = pos; }
182
183 // to implement in derived classes
184 virtual Kind GetKind() const = 0;
185
186 virtual void Draw(wxDC& dc)
187 {
188 dc.SetPen(wxPen(m_col, 1, wxSOLID));
189 }
190
191 protected:
192 wxPoint GetCentre() const
193 { return wxPoint(m_pos.x + m_size.x / 2, m_pos.y + m_size.y / 2); }
194
195 struct ShapeDump
196 {
197 int x, y, // position
198 w, h, // size
199 r, g, b, // colour
200 k; // kind
201 };
202
203 wxPoint m_pos;
204 wxSize m_size;
205 wxColour m_col;
206 };
207
208 class DnDTriangularShape : public DnDShape
209 {
210 public:
211 DnDTriangularShape(const wxPoint& pos,
212 const wxSize& size,
213 const wxColour& col)
214 : DnDShape(pos, size, col)
215 {
216 wxLogMessage("DnDTriangularShape is being created");
217 }
218
219 virtual ~DnDTriangularShape()
220 {
221 wxLogMessage("DnDTriangularShape is being deleted");
222 }
223
224 virtual Kind GetKind() const { return Triangle; }
225 virtual void Draw(wxDC& dc)
226 {
227 DnDShape::Draw(dc);
228
229 // well, it's a bit difficult to describe a triangle by position and
230 // size, but we're not doing geometry here, do we? ;-)
231 wxPoint p1(m_pos);
232 wxPoint p2(m_pos.x + m_size.x, m_pos.y);
233 wxPoint p3(m_pos.x, m_pos.y + m_size.y);
234
235 dc.DrawLine(p1, p2);
236 dc.DrawLine(p2, p3);
237 dc.DrawLine(p3, p1);
238
239 #ifdef __WXMSW__
240 dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
241 #endif
242 }
243 };
244
245 class DnDRectangularShape : public DnDShape
246 {
247 public:
248 DnDRectangularShape(const wxPoint& pos,
249 const wxSize& size,
250 const wxColour& col)
251 : DnDShape(pos, size, col)
252 {
253 wxLogMessage("DnDRectangularShape is being created");
254 }
255
256 virtual ~DnDRectangularShape()
257 {
258 wxLogMessage("DnDRectangularShape is being deleted");
259 }
260
261 virtual Kind GetKind() const { return Rectangle; }
262 virtual void Draw(wxDC& dc)
263 {
264 DnDShape::Draw(dc);
265
266 wxPoint p1(m_pos);
267 wxPoint p2(p1.x + m_size.x, p1.y);
268 wxPoint p3(p2.x, p2.y + m_size.y);
269 wxPoint p4(p1.x, p3.y);
270
271 dc.DrawLine(p1, p2);
272 dc.DrawLine(p2, p3);
273 dc.DrawLine(p3, p4);
274 dc.DrawLine(p4, p1);
275
276 #ifdef __WXMSW__
277 dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
278 #endif
279 }
280 };
281
282 class DnDEllipticShape : public DnDShape
283 {
284 public:
285 DnDEllipticShape(const wxPoint& pos,
286 const wxSize& size,
287 const wxColour& col)
288 : DnDShape(pos, size, col)
289 {
290 wxLogMessage("DnDEllipticShape is being created");
291 }
292
293 virtual ~DnDEllipticShape()
294 {
295 wxLogMessage("DnDEllipticShape is being deleted");
296 }
297
298 virtual Kind GetKind() const { return Ellipse; }
299 virtual void Draw(wxDC& dc)
300 {
301 DnDShape::Draw(dc);
302
303 dc.DrawEllipse(m_pos, m_size);
304
305 #ifdef __WXMSW__
306 dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
307 #endif
308 }
309 };
310
311 // ----------------------------------------------------------------------------
312 // A wxDataObject specialisation for the application-specific data
313 // ----------------------------------------------------------------------------
314
315 static const char *shapeFormatId = "wxShape";
316
317 class DnDShapeDataObject : public wxDataObject
318 {
319 public:
320 // ctor doesn't copy the pointer, so it shouldn't go away while this object
321 // is alive
322 DnDShapeDataObject(DnDShape *shape = (DnDShape *)NULL)
323 {
324 if ( shape )
325 {
326 // we need to copy the shape because the one we're handled may be
327 // deleted while it's still on the clipboard (for example) - and we
328 // reuse the serialisation methods here to copy it
329 void *buf = malloc(shape->DnDShape::GetDataSize());
330 shape->GetDataHere(buf);
331 m_shape = DnDShape::New(buf);
332
333 free(buf);
334 }
335 else
336 {
337 // nothing to copy
338 m_shape = NULL;
339 }
340
341 // this string should uniquely identify our format, but is otherwise
342 // arbitrary
343 m_formatShape.SetId(shapeFormatId);
344
345 // we don't draw the shape to a bitmap until it's really needed (i.e.
346 // we're asked to do so)
347 m_hasBitmap = FALSE;
348 }
349
350 virtual ~DnDShapeDataObject() { delete m_shape; }
351
352 // after a call to this function, the shape is owned by the caller and it
353 // is responsible for deleting it!
354 //
355 // NB: a better solution would be to make DnDShapes ref counted and this
356 // is what should probably be done in a real life program, otherwise
357 // the ownership problems become too complicated really fast
358 DnDShape *GetShape()
359 {
360 DnDShape *shape = m_shape;
361
362 m_shape = (DnDShape *)NULL;
363 m_hasBitmap = FALSE;
364
365 return shape;
366 }
367
368 // implement base class pure virtuals
369 // ----------------------------------
370
371 virtual wxDataFormat GetPreferredFormat(Direction WXUNUSED(dir)) const
372 {
373 return m_formatShape;
374 }
375
376 virtual size_t GetFormatCount(Direction dir) const
377 {
378 // our custom format is supported by both GetData() and SetData()
379 size_t nFormats = 1;
380 if ( dir == Get )
381 {
382 // but the bitmap format(s) are only supported for output
383 nFormats += m_dataobj.GetFormatCount(dir);
384 }
385
386 return nFormats;
387 }
388
389 virtual void GetAllFormats(wxDataFormat *formats, Direction dir) const
390 {
391 formats[0] = m_formatShape;
392 if ( dir == Get )
393 {
394 m_dataobj.GetAllFormats(&formats[1], dir);
395 }
396 }
397
398 virtual size_t GetDataSize(const wxDataFormat& format) const
399 {
400 if ( format == m_formatShape )
401 {
402 return m_shape->GetDataSize();
403 }
404 else
405 {
406 if ( !m_hasBitmap )
407 CreateBitmap();
408
409 return m_dataobj.GetDataSize();
410 }
411 }
412
413 virtual bool GetDataHere(const wxDataFormat& format, void *pBuf) const
414 {
415 if ( format == m_formatShape )
416 {
417 m_shape->GetDataHere(pBuf);
418
419 return TRUE;
420 }
421 else
422 {
423 if ( !m_hasBitmap )
424 CreateBitmap();
425
426 return m_dataobj.GetDataHere(pBuf);
427 }
428 }
429
430 virtual bool SetData(const wxDataFormat& format,
431 size_t len, const void *buf)
432 {
433 wxCHECK_MSG( format == m_formatShape, FALSE, "unsupported format" );
434
435 delete m_shape;
436 m_shape = DnDShape::New(buf);
437
438 // the shape has changed
439 m_hasBitmap = FALSE;
440
441 return TRUE;
442 }
443
444 private:
445 // creates a bitmap and assigns it to m_dataobj (also sets m_hasBitmap)
446 void CreateBitmap() const;
447
448 wxDataFormat m_formatShape; // our custom format
449
450 wxBitmapDataObject m_dataobj; // it handles bitmaps
451 bool m_hasBitmap; // true if m_dataobj has valid bitmap
452
453 DnDShape *m_shape; // our data
454 };
455
456 // ----------------------------------------------------------------------------
457 // A dialog to edit shape properties
458 // ----------------------------------------------------------------------------
459
460 class DnDShapeDialog : public wxDialog
461 {
462 public:
463 DnDShapeDialog(wxFrame *parent, DnDShape *shape);
464
465 DnDShape *GetShape() const;
466
467 virtual bool TransferDataToWindow();
468 virtual bool TransferDataFromWindow();
469
470 void OnColour(wxCommandEvent& event);
471
472 private:
473 // input
474 DnDShape *m_shape;
475
476 // output
477 DnDShape::Kind m_shapeKind;
478 wxPoint m_pos;
479 wxSize m_size;
480 wxColour m_col;
481
482 // controls
483 wxRadioBox *m_radio;
484 wxTextCtrl *m_textX,
485 *m_textY,
486 *m_textW,
487 *m_textH;
488
489 DECLARE_EVENT_TABLE()
490 };
491
492 // ----------------------------------------------------------------------------
493 // A frame for the shapes which can be drag-and-dropped between frames
494 // ----------------------------------------------------------------------------
495
496 class DnDShapeFrame : public wxFrame
497 {
498 public:
499 DnDShapeFrame(wxFrame *parent);
500 ~DnDShapeFrame();
501
502 void SetShape(DnDShape *shape);
503
504 // callbacks
505 void OnNewShape(wxCommandEvent& event);
506 void OnEditShape(wxCommandEvent& event);
507 void OnClearShape(wxCommandEvent& event);
508
509 void OnCopyShape(wxCommandEvent& event);
510 void OnPasteShape(wxCommandEvent& event);
511
512 void OnUpdateUICopy(wxUpdateUIEvent& event);
513 void OnUpdateUIPaste(wxUpdateUIEvent& event);
514
515 void OnDrag(wxMouseEvent& event);
516 void OnPaint(wxPaintEvent& event);
517 void OnDrop(wxCoord x, wxCoord y, DnDShape *shape);
518
519 private:
520 DnDShape *m_shape;
521
522 static DnDShapeFrame *ms_lastDropTarget;
523
524 DECLARE_EVENT_TABLE()
525 };
526
527 // ----------------------------------------------------------------------------
528 // wxDropTarget derivation for DnDShapes
529 // ----------------------------------------------------------------------------
530
531 class DnDShapeDropTarget : public wxDropTarget
532 {
533 public:
534 DnDShapeDropTarget(DnDShapeFrame *frame)
535 : wxDropTarget(new DnDShapeDataObject)
536 {
537 m_frame = frame;
538 }
539
540 // override base class (pure) virtuals
541 virtual void OnEnter()
542 { m_frame->SetStatusText("Mouse entered the frame"); }
543 virtual void OnLeave()
544 { m_frame->SetStatusText("Mouse left the frame"); }
545 virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
546 {
547 if ( !GetData() )
548 {
549 wxLogError("Failed to get drag and drop data");
550
551 return wxDragNone;
552 }
553
554 m_frame->OnDrop(x, y,
555 ((DnDShapeDataObject *)GetDataObject())->GetShape());
556
557 return def;
558 }
559
560 private:
561 DnDShapeFrame *m_frame;
562 };
563
564 // ----------------------------------------------------------------------------
565 // IDs for the menu commands
566 // ----------------------------------------------------------------------------
567
568 enum
569 {
570 Menu_Quit = 1,
571 Menu_Drag,
572 Menu_NewFrame,
573 Menu_About = 101,
574 Menu_Help,
575 Menu_Clear,
576 Menu_Copy,
577 Menu_Paste,
578 Menu_CopyBitmap,
579 Menu_PasteBitmap,
580 Menu_ToBeGreyed, /* for testing */
581 Menu_ToBeDeleted, /* for testing */
582 Menu_Shape_New = 500,
583 Menu_Shape_Edit,
584 Menu_Shape_Clear,
585 Menu_ShapeClipboard_Copy,
586 Menu_ShapeClipboard_Paste,
587 Button_Colour = 1001
588 };
589
590 BEGIN_EVENT_TABLE(DnDFrame, wxFrame)
591 EVT_MENU(Menu_Quit, DnDFrame::OnQuit)
592 EVT_MENU(Menu_About, DnDFrame::OnAbout)
593 EVT_MENU(Menu_Drag, DnDFrame::OnDrag)
594 EVT_MENU(Menu_NewFrame, DnDFrame::OnNewFrame)
595 EVT_MENU(Menu_Help, DnDFrame::OnHelp)
596 EVT_MENU(Menu_Clear, DnDFrame::OnLogClear)
597 EVT_MENU(Menu_Copy, DnDFrame::OnCopy)
598 EVT_MENU(Menu_Paste, DnDFrame::OnPaste)
599 EVT_MENU(Menu_CopyBitmap, DnDFrame::OnCopyBitmap)
600 EVT_MENU(Menu_PasteBitmap,DnDFrame::OnPasteBitmap)
601
602 EVT_UPDATE_UI(Menu_Paste, DnDFrame::OnUpdateUIPasteText)
603 EVT_UPDATE_UI(Menu_PasteBitmap, DnDFrame::OnUpdateUIPasteBitmap)
604
605 EVT_LEFT_DOWN( DnDFrame::OnLeftDown)
606 EVT_RIGHT_DOWN( DnDFrame::OnRightDown)
607 EVT_PAINT( DnDFrame::OnPaint)
608 END_EVENT_TABLE()
609
610 BEGIN_EVENT_TABLE(DnDShapeFrame, wxFrame)
611 EVT_MENU(Menu_Shape_New, DnDShapeFrame::OnNewShape)
612 EVT_MENU(Menu_Shape_Edit, DnDShapeFrame::OnEditShape)
613 EVT_MENU(Menu_Shape_Clear, DnDShapeFrame::OnClearShape)
614
615 EVT_MENU(Menu_ShapeClipboard_Copy, DnDShapeFrame::OnCopyShape)
616 EVT_MENU(Menu_ShapeClipboard_Paste, DnDShapeFrame::OnPasteShape)
617
618 EVT_UPDATE_UI(Menu_ShapeClipboard_Copy, DnDShapeFrame::OnUpdateUICopy)
619 EVT_UPDATE_UI(Menu_ShapeClipboard_Paste, DnDShapeFrame::OnUpdateUIPaste)
620
621 EVT_LEFT_DOWN(DnDShapeFrame::OnDrag)
622
623 EVT_PAINT(DnDShapeFrame::OnPaint)
624 END_EVENT_TABLE()
625
626 BEGIN_EVENT_TABLE(DnDShapeDialog, wxDialog)
627 EVT_BUTTON(Button_Colour, DnDShapeDialog::OnColour)
628 END_EVENT_TABLE()
629
630 // ============================================================================
631 // implementation
632 // ============================================================================
633
634 // `Main program' equivalent, creating windows and returning main app frame
635 bool DnDApp::OnInit()
636 {
637 // load our ressources
638 wxPathList pathList;
639 pathList.Add(".");
640 #ifdef __WXMSW__
641 pathList.Add("./Debug");
642 pathList.Add("./Release");
643 #endif // wxMSW
644
645 wxString path = pathList.FindValidPath("dnd.wxr");
646 if ( !path )
647 {
648 wxLogError("Can't find the resource file dnd.wxr in the current "
649 "directory, aborting.");
650
651 return FALSE;
652 }
653
654 wxDefaultResourceTable->ParseResourceFile(path);
655
656 #if wxUSE_LIBPNG
657 wxImage::AddHandler( new wxPNGHandler );
658 #endif
659
660 // create the main frame window
661 DnDFrame *frame = new DnDFrame((wxFrame *) NULL,
662 "Drag-and-Drop/Clipboard wxWindows Sample",
663 50, 50, 450, 340);
664
665 // activate it
666 frame->Show(TRUE);
667
668 SetTopWindow(frame);
669
670 return TRUE;
671 }
672
673 DnDFrame::DnDFrame(wxFrame *frame, char *title, int x, int y, int w, int h)
674 : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h)),
675 m_strText("wxWindows drag & drop works :-)")
676
677 {
678 // frame icon and status bar
679 SetIcon(wxICON(mondrian));
680
681 CreateStatusBar();
682
683 // construct menu
684 wxMenu *file_menu = new wxMenu;
685 file_menu->Append(Menu_Drag, "&Test drag...");
686 file_menu->AppendSeparator();
687 file_menu->Append(Menu_NewFrame, "&New frame\tCtrl-N");
688 file_menu->AppendSeparator();
689 file_menu->Append(Menu_Quit, "E&xit");
690
691 wxMenu *log_menu = new wxMenu;
692 log_menu->Append(Menu_Clear, "Clear\tDel");
693
694 wxMenu *help_menu = new wxMenu;
695 help_menu->Append(Menu_Help, "&Help...");
696 help_menu->AppendSeparator();
697 help_menu->Append(Menu_About, "&About");
698
699 wxMenu *clip_menu = new wxMenu;
700 clip_menu->Append(Menu_Copy, "&Copy text\tCtrl+C");
701 clip_menu->Append(Menu_Paste, "&Paste text\tCtrl+V");
702 clip_menu->AppendSeparator();
703 clip_menu->Append(Menu_CopyBitmap, "&Copy bitmap\tAlt+C");
704 clip_menu->Append(Menu_PasteBitmap, "&Paste bitmap\tAlt+V");
705
706 wxMenuBar *menu_bar = new wxMenuBar;
707 menu_bar->Append(file_menu, "&File");
708 menu_bar->Append(log_menu, "&Log");
709 menu_bar->Append(clip_menu, "&Clipboard");
710 menu_bar->Append(help_menu, "&Help");
711
712 SetMenuBar(menu_bar);
713
714 // make a panel with 3 subwindows
715 wxPoint pos(0, 0);
716 wxSize size(400, 200);
717
718 wxString strFile("Drop files here!"), strText("Drop text on me");
719
720 m_ctrlFile = new wxListBox(this, -1, pos, size, 1, &strFile,
721 wxLB_HSCROLL | wxLB_ALWAYS_SB );
722 m_ctrlText = new wxListBox(this, -1, pos, size, 1, &strText,
723 wxLB_HSCROLL | wxLB_ALWAYS_SB );
724
725 m_ctrlLog = new wxTextCtrl(this, -1, "", pos, size,
726 wxTE_MULTILINE | wxTE_READONLY |
727 wxSUNKEN_BORDER );
728
729 #ifdef __WXMSW__
730 // redirect log messages to the text window and switch on OLE messages
731 // logging
732 wxLog::AddTraceMask(wxTRACE_OleCalls);
733 #endif
734 m_pLog = new wxLogTextCtrl(m_ctrlLog);
735 m_pLogPrev = wxLog::SetActiveTarget(m_pLog);
736
737 // associate drop targets with 2 text controls
738 m_ctrlFile->SetDropTarget(new DnDFile(m_ctrlFile));
739 m_ctrlText->SetDropTarget(new DnDText(m_ctrlText));
740
741 wxLayoutConstraints *c;
742
743 // Top-left listbox
744 c = new wxLayoutConstraints;
745 c->left.SameAs(this, wxLeft);
746 c->top.SameAs(this, wxTop);
747 c->right.PercentOf(this, wxRight, 50);
748 c->height.PercentOf(this, wxHeight, 30);
749 m_ctrlFile->SetConstraints(c);
750
751 // Top-right listbox
752 c = new wxLayoutConstraints;
753 c->left.SameAs (m_ctrlFile, wxRight);
754 c->top.SameAs (this, wxTop);
755 c->right.SameAs (this, wxRight);
756 c->height.PercentOf(this, wxHeight, 30);
757 m_ctrlText->SetConstraints(c);
758
759 // Lower text control
760 c = new wxLayoutConstraints;
761 c->left.SameAs (this, wxLeft);
762 c->right.SameAs (this, wxRight);
763 c->height.PercentOf(this, wxHeight, 50);
764 c->top.SameAs(m_ctrlText, wxBottom);
765 m_ctrlLog->SetConstraints(c);
766
767 SetAutoLayout(TRUE);
768 }
769
770 void DnDFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
771 {
772 Close(TRUE);
773 }
774
775 void DnDFrame::OnPaint(wxPaintEvent& WXUNUSED(event))
776 {
777 int w = 0;
778 int h = 0;
779 GetClientSize( &w, &h );
780
781 wxPaintDC dc(this);
782 dc.SetFont( wxFont( 24, wxDECORATIVE, wxNORMAL, wxNORMAL, FALSE, "charter" ) );
783 dc.DrawText( "Drag text from here!", 20, h-50 );
784
785 if ( m_bitmap.Ok() )
786 {
787 // 4/5 is 80% taken by other windows, 20 is arbitrary margin
788 dc.DrawBitmap(m_bitmap,
789 w - m_bitmap.GetWidth() - 20,
790 (4*h)/5 + 20);
791 }
792 }
793
794 void DnDFrame::OnUpdateUIPasteText(wxUpdateUIEvent& event)
795 {
796 event.Enable( wxTheClipboard->IsSupported(wxDF_TEXT) );
797 }
798
799 void DnDFrame::OnUpdateUIPasteBitmap(wxUpdateUIEvent& event)
800 {
801 event.Enable( wxTheClipboard->IsSupported(wxDF_BITMAP) );
802 }
803
804 void DnDFrame::OnNewFrame(wxCommandEvent& WXUNUSED(event))
805 {
806 (new DnDShapeFrame(this))->Show(TRUE);
807
808 wxLogStatus(this, "Double click the new frame to select a shape for it");
809 }
810
811 void DnDFrame::OnDrag(wxCommandEvent& WXUNUSED(event))
812 {
813 wxString strText = wxGetTextFromUser
814 (
815 "After you enter text in this dialog, press any mouse\n"
816 "button in the bottom (empty) part of the frame and \n"
817 "drag it anywhere - you will be in fact dragging the\n"
818 "text object containing this text",
819 "Please enter some text", m_strText, this
820 );
821
822 m_strText = strText;
823 }
824
825 void DnDFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
826 {
827 wxMessageBox("Drag-&-Drop Demo\n"
828 "Please see \"Help|Help...\" for details\n"
829 "Copyright (c) 1998 Vadim Zeitlin",
830 "About wxDnD",
831 wxICON_INFORMATION | wxOK,
832 this);
833 }
834
835 void DnDFrame::OnHelp(wxCommandEvent& /* event */)
836 {
837 wxMessageDialog dialog(this,
838 "This small program demonstrates drag & drop support in wxWindows. The program window\n"
839 "consists of 3 parts: the bottom pane is for debug messages, so that you can see what's\n"
840 "going on inside. The top part is split into 2 listboxes, the left one accepts files\n"
841 "and the right one accepts text.\n"
842 "\n"
843 "To test wxDropTarget: open wordpad (write.exe), select some text in it and drag it to\n"
844 "the right listbox (you'll notice the usual visual feedback, i.e. the cursor will change).\n"
845 "Also, try dragging some files (you can select several at once) from Windows Explorer (or \n"
846 "File Manager) to the left pane. Hold down Ctrl/Shift keys when you drop text (doesn't \n"
847 "work with files) and see what changes.\n"
848 "\n"
849 "To test wxDropSource: just press any mouse button on the empty zone of the window and drag\n"
850 "it to wordpad or any other droptarget accepting text (and of course you can just drag it\n"
851 "to the right pane). Due to a lot of trace messages, the cursor might take some time to \n"
852 "change, don't release the mouse button until it does. You can change the string being\n"
853 "dragged in in \"File|Test drag...\" dialog.\n"
854 "\n"
855 "\n"
856 "Please send all questions/bug reports/suggestions &c to \n"
857 "Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>",
858 "wxDnD Help");
859
860 dialog.ShowModal();
861 }
862
863 void DnDFrame::OnLogClear(wxCommandEvent& /* event */ )
864 {
865 m_ctrlLog->Clear();
866 }
867
868 void DnDFrame::OnLeftDown(wxMouseEvent &WXUNUSED(event) )
869 {
870 if ( !m_strText.IsEmpty() )
871 {
872 // start drag operation
873 wxTextDataObject textData(m_strText);
874 wxDropSource source(textData, this,
875 wxCURSOR_PENCIL, // for copy
876 wxCURSOR_SPRAYCAN, // for move
877 wxCURSOR_QUESTION_ARROW); // for nothing
878
879 const char *pc;
880
881 switch ( source.DoDragDrop(TRUE) )
882 {
883 case wxDragError: pc = "Error!"; break;
884 case wxDragNone: pc = "Nothing"; break;
885 case wxDragCopy: pc = "Copied"; break;
886 case wxDragMove: pc = "Moved"; break;
887 case wxDragCancel: pc = "Cancelled"; break;
888 default: pc = "Huh?"; break;
889 }
890
891 SetStatusText(wxString("Drag result: ") + pc);
892 }
893 }
894
895 void DnDFrame::OnRightDown(wxMouseEvent &event )
896 {
897 wxMenu *menu = new wxMenu;
898
899 menu->Append(Menu_Drag, "&Test drag...");
900 menu->Append(Menu_About, "&About");
901 menu->Append(Menu_Quit, "E&xit");
902 menu->Append(Menu_ToBeDeleted, "To be deleted");
903 menu->Append(Menu_ToBeGreyed, "To be greyed");
904
905 menu->Delete( Menu_ToBeDeleted );
906 menu->Enable( Menu_ToBeGreyed, FALSE );
907
908 PopupMenu( menu, event.GetX(), event.GetY() );
909 }
910
911 DnDFrame::~DnDFrame()
912 {
913 if ( m_pLog != NULL ) {
914 if ( wxLog::SetActiveTarget(m_pLogPrev) == m_pLog )
915 delete m_pLog;
916 }
917 }
918
919 // ---------------------------------------------------------------------------
920 // bitmap clipboard
921 // ---------------------------------------------------------------------------
922
923 void DnDFrame::OnCopyBitmap(wxCommandEvent& WXUNUSED(event))
924 {
925 // PNG support is not always compiled in under Windows, so use BMP there
926 #ifdef __WXMSW__
927 wxFileDialog dialog(this, "Open a BMP file", "", "", "BMP files (*.bmp)|*.bmp", 0);
928 #else
929 wxFileDialog dialog(this, "Open a PNG file", "", "", "PNG files (*.png)|*.png", 0);
930 #endif
931
932 if (dialog.ShowModal() != wxID_OK)
933 {
934 wxLogMessage( _T("Aborted file open") );
935 return;
936 }
937
938 if (dialog.GetPath().IsEmpty())
939 {
940 wxLogMessage( _T("Returned empty string.") );
941 return;
942 }
943
944 if (!wxFileExists(dialog.GetPath()))
945 {
946 wxLogMessage( _T("File doesn't exist.") );
947 return;
948 }
949
950 wxImage image;
951 image.LoadFile( dialog.GetPath(),
952 #ifdef __WXMSW__
953 wxBITMAP_TYPE_BMP
954 #else
955 wxBITMAP_TYPE_PNG
956 #endif
957 );
958 if (!image.Ok())
959 {
960 wxLogError( _T("Invalid image file...") );
961 return;
962 }
963
964 wxLogStatus( _T("Decoding image file...") );
965 wxYield();
966
967 wxBitmap bitmap( image.ConvertToBitmap() );
968
969 if ( !wxTheClipboard->Open() )
970 {
971 wxLogError(_T("Can't open clipboard."));
972
973 return;
974 }
975
976 wxLogMessage( _T("Creating wxBitmapDataObject...") );
977 wxYield();
978
979 if ( !wxTheClipboard->AddData(new wxBitmapDataObject(bitmap)) )
980 {
981 wxLogError(_T("Can't copy image to the clipboard."));
982 }
983 else
984 {
985 wxLogMessage(_T("Image has been put on the clipboard.") );
986 wxLogMessage(_T("You can paste it now and look at it.") );
987 }
988
989 wxTheClipboard->Close();
990 }
991
992 void DnDFrame::OnPasteBitmap(wxCommandEvent& WXUNUSED(event))
993 {
994 if ( !wxTheClipboard->Open() )
995 {
996 wxLogError(_T("Can't open clipboard."));
997
998 return;
999 }
1000
1001 if ( !wxTheClipboard->IsSupported(wxDF_BITMAP) )
1002 {
1003 wxLogWarning(_T("No bitmap on clipboard"));
1004
1005 wxTheClipboard->Close();
1006 return;
1007 }
1008
1009 wxBitmapDataObject data;
1010 if ( !wxTheClipboard->GetData(data) )
1011 {
1012 wxLogError(_T("Can't paste bitmap from the clipboard"));
1013 }
1014 else
1015 {
1016 wxLogMessage(_T("Bitmap pasted from the clipboard") );
1017 m_bitmap = data.GetBitmap();
1018 Refresh();
1019 }
1020
1021 wxTheClipboard->Close();
1022 }
1023
1024 // ---------------------------------------------------------------------------
1025 // text clipboard
1026 // ---------------------------------------------------------------------------
1027
1028 void DnDFrame::OnCopy(wxCommandEvent& WXUNUSED(event))
1029 {
1030 if ( !wxTheClipboard->Open() )
1031 {
1032 wxLogError(_T("Can't open clipboard."));
1033
1034 return;
1035 }
1036
1037 if ( !wxTheClipboard->AddData(new wxTextDataObject(m_strText)) )
1038 {
1039 wxLogError(_T("Can't copy data to the clipboard"));
1040 }
1041 else
1042 {
1043 wxLogMessage(_T("Text '%s' put on the clipboard"), m_strText.c_str());
1044 }
1045
1046 wxTheClipboard->Close();
1047 }
1048
1049 void DnDFrame::OnPaste(wxCommandEvent& WXUNUSED(event))
1050 {
1051 if ( !wxTheClipboard->Open() )
1052 {
1053 wxLogError(_T("Can't open clipboard."));
1054
1055 return;
1056 }
1057
1058 if ( !wxTheClipboard->IsSupported(wxDF_TEXT) )
1059 {
1060 wxLogWarning(_T("No text data on clipboard"));
1061
1062 wxTheClipboard->Close();
1063 return;
1064 }
1065
1066 wxTextDataObject text;
1067 if ( !wxTheClipboard->GetData(text) )
1068 {
1069 wxLogError(_T("Can't paste data from the clipboard"));
1070 }
1071 else
1072 {
1073 wxLogMessage(_T("Text '%s' pasted from the clipboard"),
1074 text.GetText().c_str());
1075 }
1076
1077 wxTheClipboard->Close();
1078 }
1079
1080 // ----------------------------------------------------------------------------
1081 // Notifications called by the base class
1082 // ----------------------------------------------------------------------------
1083
1084 bool DnDText::OnDropText(wxCoord, wxCoord, const wxString& text)
1085 {
1086 m_pOwner->Append(text);
1087
1088 return TRUE;
1089 }
1090
1091 bool DnDFile::OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames)
1092 {
1093 size_t nFiles = filenames.GetCount();
1094 wxString str;
1095 str.Printf( _T("%d files dropped"), nFiles);
1096 m_pOwner->Append(str);
1097 for ( size_t n = 0; n < nFiles; n++ ) {
1098 m_pOwner->Append(filenames[n]);
1099 }
1100
1101 return TRUE;
1102 }
1103
1104 // ----------------------------------------------------------------------------
1105 // DnDShapeDialog
1106 // ----------------------------------------------------------------------------
1107
1108 DnDShapeDialog::DnDShapeDialog(wxFrame *parent, DnDShape *shape)
1109 {
1110 m_shape = shape;
1111
1112 LoadFromResource(parent, "dialogShape");
1113
1114 m_textX = (wxTextCtrl *)wxFindWindowByName("textX", this);
1115 m_textY = (wxTextCtrl *)wxFindWindowByName("textY", this);
1116 m_textW = (wxTextCtrl *)wxFindWindowByName("textW", this);
1117 m_textH = (wxTextCtrl *)wxFindWindowByName("textH", this);
1118
1119 m_radio = (wxRadioBox *)wxFindWindowByName("radio", this);
1120 }
1121
1122 DnDShape *DnDShapeDialog::GetShape() const
1123 {
1124 switch ( m_shapeKind )
1125 {
1126 default:
1127 case DnDShape::None: return NULL;
1128 case DnDShape::Triangle: return new DnDTriangularShape(m_pos, m_size, m_col);
1129 case DnDShape::Rectangle: return new DnDRectangularShape(m_pos, m_size, m_col);
1130 case DnDShape::Ellipse: return new DnDEllipticShape(m_pos, m_size, m_col);
1131 }
1132 }
1133
1134 bool DnDShapeDialog::TransferDataToWindow()
1135 {
1136
1137 if ( m_shape )
1138 {
1139 m_radio->SetSelection(m_shape->GetKind());
1140 m_pos = m_shape->GetPosition();
1141 m_size = m_shape->GetSize();
1142 m_col = m_shape->GetColour();
1143 }
1144 else
1145 {
1146 m_radio->SetSelection(DnDShape::None);
1147 m_pos = wxPoint(1, 1);
1148 m_size = wxSize(100, 100);
1149 }
1150
1151 m_textX->SetValue(wxString() << m_pos.x);
1152 m_textY->SetValue(wxString() << m_pos.y);
1153 m_textW->SetValue(wxString() << m_size.x);
1154 m_textH->SetValue(wxString() << m_size.y);
1155
1156 return TRUE;
1157 }
1158
1159 bool DnDShapeDialog::TransferDataFromWindow()
1160 {
1161 m_shapeKind = (DnDShape::Kind)m_radio->GetSelection();
1162
1163 m_pos.x = atoi(m_textX->GetValue());
1164 m_pos.y = atoi(m_textY->GetValue());
1165 m_size.x = atoi(m_textW->GetValue());
1166 m_size.y = atoi(m_textH->GetValue());
1167
1168 if ( !m_pos.x || !m_pos.y || !m_size.x || !m_size.y )
1169 {
1170 wxMessageBox("All sizes and positions should be non null!",
1171 "Invalid shape", wxICON_HAND | wxOK, this);
1172
1173 return FALSE;
1174 }
1175
1176 return TRUE;
1177 }
1178
1179 void DnDShapeDialog::OnColour(wxCommandEvent& WXUNUSED(event))
1180 {
1181 wxColourData data;
1182 data.SetChooseFull(TRUE);
1183 for (int i = 0; i < 16; i++)
1184 {
1185 wxColour colour(i*16, i*16, i*16);
1186 data.SetCustomColour(i, colour);
1187 }
1188
1189 wxColourDialog dialog(this, &data);
1190 if ( dialog.ShowModal() == wxID_OK )
1191 {
1192 m_col = dialog.GetColourData().GetColour();
1193 }
1194 }
1195
1196 // ----------------------------------------------------------------------------
1197 // DnDShapeFrame
1198 // ----------------------------------------------------------------------------
1199
1200 DnDShapeFrame *DnDShapeFrame::ms_lastDropTarget = NULL;
1201
1202 DnDShapeFrame::DnDShapeFrame(wxFrame *parent)
1203 : wxFrame(parent, -1, "Shape Frame",
1204 wxDefaultPosition, wxSize(250, 150))
1205 {
1206 CreateStatusBar();
1207
1208 wxMenu *menuShape = new wxMenu;
1209 menuShape->Append(Menu_Shape_New, "&New default shape\tCtrl-S");
1210 menuShape->Append(Menu_Shape_Edit, "&Edit shape\tCtrl-E");
1211 menuShape->AppendSeparator();
1212 menuShape->Append(Menu_Shape_Clear, "&Clear shape\tDel");
1213
1214 wxMenu *menuClipboard = new wxMenu;
1215 menuClipboard->Append(Menu_ShapeClipboard_Copy, "&Copy\tCtrl-C");
1216 menuClipboard->Append(Menu_ShapeClipboard_Paste, "&Paste\tCtrl-V");
1217
1218 wxMenuBar *menubar = new wxMenuBar;
1219 menubar->Append(menuShape, "&Shape");
1220 menubar->Append(menuClipboard, "&Clipboard");
1221
1222 SetMenuBar(menubar);
1223
1224 SetStatusText("Press Ctrl-S to create a new shape");
1225
1226 SetDropTarget(new DnDShapeDropTarget(this));
1227
1228 m_shape = NULL;
1229
1230 SetBackgroundColour(*wxWHITE);
1231 }
1232
1233 DnDShapeFrame::~DnDShapeFrame()
1234 {
1235 if (m_shape)
1236 delete m_shape;
1237 }
1238
1239 void DnDShapeFrame::SetShape(DnDShape *shape)
1240 {
1241 if (m_shape)
1242 delete m_shape;
1243 m_shape = shape;
1244 Refresh();
1245 }
1246
1247 // callbacks
1248 void DnDShapeFrame::OnDrag(wxMouseEvent& event)
1249 {
1250 if ( !m_shape )
1251 {
1252 event.Skip();
1253
1254 return;
1255 }
1256
1257 // start drag operation
1258 DnDShapeDataObject shapeData(m_shape);
1259 wxDropSource source(shapeData, this);
1260
1261 const char *pc = NULL;
1262 switch ( source.DoDragDrop(TRUE) )
1263 {
1264 default:
1265 case wxDragError:
1266 wxLogError("An error occured during drag and drop operation");
1267 break;
1268
1269 case wxDragNone:
1270 SetStatusText("Nothing happened");
1271 break;
1272
1273 case wxDragCopy:
1274 pc = "copied";
1275 break;
1276
1277 case wxDragMove:
1278 pc = "moved";
1279 if ( ms_lastDropTarget != this )
1280 {
1281 // don't delete the shape if we dropped it on ourselves!
1282 SetShape(NULL);
1283 }
1284 break;
1285
1286 case wxDragCancel:
1287 SetStatusText("Drag and drop operation cancelled");
1288 break;
1289 }
1290
1291 if ( pc )
1292 {
1293 SetStatusText(wxString("Shape successfully ") + pc);
1294 }
1295 //else: status text already set
1296 }
1297
1298 void DnDShapeFrame::OnDrop(wxCoord x, wxCoord y, DnDShape *shape)
1299 {
1300 ms_lastDropTarget = this;
1301
1302 wxPoint pt(x, y);
1303
1304 wxString s;
1305 s.Printf("Shape dropped at (%ld, %ld)", pt.x, pt.y);
1306 SetStatusText(s);
1307
1308 shape->Move(pt);
1309 SetShape(shape);
1310 }
1311
1312 void DnDShapeFrame::OnEditShape(wxCommandEvent& event)
1313 {
1314 DnDShapeDialog dlg(this, m_shape);
1315 if ( dlg.ShowModal() == wxID_OK )
1316 {
1317 SetShape(dlg.GetShape());
1318
1319 if ( m_shape )
1320 {
1321 SetStatusText("You can now drag the shape to another frame");
1322 }
1323 }
1324 }
1325
1326 void DnDShapeFrame::OnNewShape(wxCommandEvent& event)
1327 {
1328 SetShape(new DnDEllipticShape(wxPoint(10, 10), wxSize(80, 60), *wxRED));
1329
1330 SetStatusText("You can now drag the shape to another frame");
1331 }
1332
1333 void DnDShapeFrame::OnClearShape(wxCommandEvent& event)
1334 {
1335 SetShape(NULL);
1336 }
1337
1338 void DnDShapeFrame::OnCopyShape(wxCommandEvent& event)
1339 {
1340 if ( m_shape )
1341 {
1342 wxClipboardLocker clipLocker;
1343 if ( !clipLocker )
1344 {
1345 wxLogError("Can't open the clipboard");
1346
1347 return;
1348 }
1349
1350 wxTheClipboard->AddData(new DnDShapeDataObject(m_shape));
1351 }
1352 }
1353
1354 void DnDShapeFrame::OnPasteShape(wxCommandEvent& event)
1355 {
1356 wxClipboardLocker clipLocker;
1357 if ( !clipLocker )
1358 {
1359 wxLogError("Can't open the clipboard");
1360
1361 return;
1362 }
1363
1364 DnDShapeDataObject shapeDataObject(NULL);
1365 if ( wxTheClipboard->GetData(shapeDataObject) )
1366 {
1367 SetShape(shapeDataObject.GetShape());
1368 }
1369 else
1370 {
1371 wxLogStatus("No shape on the clipboard");
1372 }
1373 }
1374
1375 void DnDShapeFrame::OnUpdateUICopy(wxUpdateUIEvent& event)
1376 {
1377 event.Enable( m_shape != NULL );
1378 }
1379
1380 void DnDShapeFrame::OnUpdateUIPaste(wxUpdateUIEvent& event)
1381 {
1382 event.Enable( wxTheClipboard->IsSupported(wxDataFormat(shapeFormatId)) );
1383 }
1384
1385 void DnDShapeFrame::OnPaint(wxPaintEvent& event)
1386 {
1387 if ( m_shape )
1388 {
1389 wxPaintDC dc(this);
1390
1391 m_shape->Draw(dc);
1392 }
1393 else
1394 {
1395 event.Skip();
1396 }
1397 }
1398
1399 // ----------------------------------------------------------------------------
1400 // DnDShape
1401 // ----------------------------------------------------------------------------
1402
1403 DnDShape *DnDShape::New(const void *buf)
1404 {
1405 const ShapeDump& dump = *(const ShapeDump *)buf;
1406 switch ( dump.k )
1407 {
1408 case Triangle:
1409 return new DnDTriangularShape(wxPoint(dump.x, dump.y),
1410 wxSize(dump.w, dump.h),
1411 wxColour(dump.r, dump.g, dump.b));
1412
1413 case Rectangle:
1414 return new DnDRectangularShape(wxPoint(dump.x, dump.y),
1415 wxSize(dump.w, dump.h),
1416 wxColour(dump.r, dump.g, dump.b));
1417
1418 case Ellipse:
1419 return new DnDEllipticShape(wxPoint(dump.x, dump.y),
1420 wxSize(dump.w, dump.h),
1421 wxColour(dump.r, dump.g, dump.b));
1422
1423 default:
1424 wxFAIL_MSG("invalid shape!");
1425 return NULL;
1426 }
1427 }
1428
1429 // ----------------------------------------------------------------------------
1430 // DnDShapeDataObject
1431 // ----------------------------------------------------------------------------
1432
1433 void DnDShapeDataObject::CreateBitmap() const
1434 {
1435 wxPoint pos = m_shape->GetPosition();
1436 wxSize size = m_shape->GetSize();
1437 int x = pos.x + size.x,
1438 y = pos.y + size.y;
1439 wxBitmap bitmap(x, y);
1440 wxMemoryDC dc;
1441 dc.SelectObject(bitmap);
1442 dc.SetBrush(wxBrush("white", wxSOLID));
1443 dc.Clear();
1444 m_shape->Draw(dc);
1445 dc.SelectObject(wxNullBitmap);
1446
1447 DnDShapeDataObject *self = (DnDShapeDataObject *)this; // const_cast
1448 self->m_dataobj.SetBitmap(bitmap);
1449 self->m_hasBitmap = TRUE;
1450 }
1451