1. wxDropTarget::OnData() returns wxDragResult now, not bool
[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, wxICON(mondrian));
875
876 const char *pc;
877
878 switch ( source.DoDragDrop(TRUE) )
879 {
880 case wxDragError: pc = "Error!"; break;
881 case wxDragNone: pc = "Nothing"; break;
882 case wxDragCopy: pc = "Copied"; break;
883 case wxDragMove: pc = "Moved"; break;
884 case wxDragCancel: pc = "Cancelled"; break;
885 default: pc = "Huh?"; break;
886 }
887
888 SetStatusText(wxString("Drag result: ") + pc);
889 }
890 }
891
892 void DnDFrame::OnRightDown(wxMouseEvent &event )
893 {
894 wxMenu *menu = new wxMenu;
895
896 menu->Append(Menu_Drag, "&Test drag...");
897 menu->Append(Menu_About, "&About");
898 menu->Append(Menu_Quit, "E&xit");
899 menu->Append(Menu_ToBeDeleted, "To be deleted");
900 menu->Append(Menu_ToBeGreyed, "To be greyed");
901
902 menu->Delete( Menu_ToBeDeleted );
903 menu->Enable( Menu_ToBeGreyed, FALSE );
904
905 PopupMenu( menu, event.GetX(), event.GetY() );
906 }
907
908 DnDFrame::~DnDFrame()
909 {
910 if ( m_pLog != NULL ) {
911 if ( wxLog::SetActiveTarget(m_pLogPrev) == m_pLog )
912 delete m_pLog;
913 }
914 }
915
916 // ---------------------------------------------------------------------------
917 // bitmap clipboard
918 // ---------------------------------------------------------------------------
919
920 void DnDFrame::OnCopyBitmap(wxCommandEvent& WXUNUSED(event))
921 {
922 // PNG support is not always compiled in under Windows, so use BMP there
923 #ifdef __WXMSW__
924 wxFileDialog dialog(this, "Open a BMP file", "", "", "BMP files (*.bmp)|*.bmp", 0);
925 #else
926 wxFileDialog dialog(this, "Open a PNG file", "", "", "PNG files (*.png)|*.png", 0);
927 #endif
928
929 if (dialog.ShowModal() != wxID_OK)
930 {
931 wxLogMessage( _T("Aborted file open") );
932 return;
933 }
934
935 if (dialog.GetPath().IsEmpty())
936 {
937 wxLogMessage( _T("Returned empty string.") );
938 return;
939 }
940
941 if (!wxFileExists(dialog.GetPath()))
942 {
943 wxLogMessage( _T("File doesn't exist.") );
944 return;
945 }
946
947 wxImage image;
948 image.LoadFile( dialog.GetPath(),
949 #ifdef __WXMSW__
950 wxBITMAP_TYPE_BMP
951 #else
952 wxBITMAP_TYPE_PNG
953 #endif
954 );
955 if (!image.Ok())
956 {
957 wxLogError( _T("Invalid image file...") );
958 return;
959 }
960
961 wxLogStatus( _T("Decoding image file...") );
962 wxYield();
963
964 wxBitmap bitmap( image.ConvertToBitmap() );
965
966 if ( !wxTheClipboard->Open() )
967 {
968 wxLogError(_T("Can't open clipboard."));
969
970 return;
971 }
972
973 wxLogMessage( _T("Creating wxBitmapDataObject...") );
974 wxYield();
975
976 if ( !wxTheClipboard->AddData(new wxBitmapDataObject(bitmap)) )
977 {
978 wxLogError(_T("Can't copy image to the clipboard."));
979 }
980 else
981 {
982 wxLogMessage(_T("Image has been put on the clipboard.") );
983 wxLogMessage(_T("You can paste it now and look at it.") );
984 }
985
986 wxTheClipboard->Close();
987 }
988
989 void DnDFrame::OnPasteBitmap(wxCommandEvent& WXUNUSED(event))
990 {
991 if ( !wxTheClipboard->Open() )
992 {
993 wxLogError(_T("Can't open clipboard."));
994
995 return;
996 }
997
998 if ( !wxTheClipboard->IsSupported(wxDF_BITMAP) )
999 {
1000 wxLogWarning(_T("No bitmap on clipboard"));
1001
1002 wxTheClipboard->Close();
1003 return;
1004 }
1005
1006 wxBitmapDataObject data;
1007 if ( !wxTheClipboard->GetData(data) )
1008 {
1009 wxLogError(_T("Can't paste bitmap from the clipboard"));
1010 }
1011 else
1012 {
1013 wxLogMessage(_T("Bitmap pasted from the clipboard") );
1014 m_bitmap = data.GetBitmap();
1015 Refresh();
1016 }
1017
1018 wxTheClipboard->Close();
1019 }
1020
1021 // ---------------------------------------------------------------------------
1022 // text clipboard
1023 // ---------------------------------------------------------------------------
1024
1025 void DnDFrame::OnCopy(wxCommandEvent& WXUNUSED(event))
1026 {
1027 if ( !wxTheClipboard->Open() )
1028 {
1029 wxLogError(_T("Can't open clipboard."));
1030
1031 return;
1032 }
1033
1034 if ( !wxTheClipboard->AddData(new wxTextDataObject(m_strText)) )
1035 {
1036 wxLogError(_T("Can't copy data to the clipboard"));
1037 }
1038 else
1039 {
1040 wxLogMessage(_T("Text '%s' put on the clipboard"), m_strText.c_str());
1041 }
1042
1043 wxTheClipboard->Close();
1044 }
1045
1046 void DnDFrame::OnPaste(wxCommandEvent& WXUNUSED(event))
1047 {
1048 if ( !wxTheClipboard->Open() )
1049 {
1050 wxLogError(_T("Can't open clipboard."));
1051
1052 return;
1053 }
1054
1055 if ( !wxTheClipboard->IsSupported(wxDF_TEXT) )
1056 {
1057 wxLogWarning(_T("No text data on clipboard"));
1058
1059 wxTheClipboard->Close();
1060 return;
1061 }
1062
1063 wxTextDataObject text;
1064 if ( !wxTheClipboard->GetData(text) )
1065 {
1066 wxLogError(_T("Can't paste data from the clipboard"));
1067 }
1068 else
1069 {
1070 wxLogMessage(_T("Text '%s' pasted from the clipboard"),
1071 text.GetText().c_str());
1072 }
1073
1074 wxTheClipboard->Close();
1075 }
1076
1077 // ----------------------------------------------------------------------------
1078 // Notifications called by the base class
1079 // ----------------------------------------------------------------------------
1080
1081 bool DnDText::OnDropText(wxCoord, wxCoord, const wxString& text)
1082 {
1083 m_pOwner->Append(text);
1084
1085 return TRUE;
1086 }
1087
1088 bool DnDFile::OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames)
1089 {
1090 size_t nFiles = filenames.GetCount();
1091 wxString str;
1092 str.Printf( _T("%d files dropped"), nFiles);
1093 m_pOwner->Append(str);
1094 for ( size_t n = 0; n < nFiles; n++ ) {
1095 m_pOwner->Append(filenames[n]);
1096 }
1097
1098 return TRUE;
1099 }
1100
1101 // ----------------------------------------------------------------------------
1102 // DnDShapeDialog
1103 // ----------------------------------------------------------------------------
1104
1105 DnDShapeDialog::DnDShapeDialog(wxFrame *parent, DnDShape *shape)
1106 {
1107 m_shape = shape;
1108
1109 LoadFromResource(parent, "dialogShape");
1110
1111 m_textX = (wxTextCtrl *)wxFindWindowByName("textX", this);
1112 m_textY = (wxTextCtrl *)wxFindWindowByName("textY", this);
1113 m_textW = (wxTextCtrl *)wxFindWindowByName("textW", this);
1114 m_textH = (wxTextCtrl *)wxFindWindowByName("textH", this);
1115
1116 m_radio = (wxRadioBox *)wxFindWindowByName("radio", this);
1117 }
1118
1119 DnDShape *DnDShapeDialog::GetShape() const
1120 {
1121 switch ( m_shapeKind )
1122 {
1123 default:
1124 case DnDShape::None: return NULL;
1125 case DnDShape::Triangle: return new DnDTriangularShape(m_pos, m_size, m_col);
1126 case DnDShape::Rectangle: return new DnDRectangularShape(m_pos, m_size, m_col);
1127 case DnDShape::Ellipse: return new DnDEllipticShape(m_pos, m_size, m_col);
1128 }
1129 }
1130
1131 bool DnDShapeDialog::TransferDataToWindow()
1132 {
1133
1134 if ( m_shape )
1135 {
1136 m_radio->SetSelection(m_shape->GetKind());
1137 m_pos = m_shape->GetPosition();
1138 m_size = m_shape->GetSize();
1139 m_col = m_shape->GetColour();
1140 }
1141 else
1142 {
1143 m_radio->SetSelection(DnDShape::None);
1144 m_pos = wxPoint(1, 1);
1145 m_size = wxSize(100, 100);
1146 }
1147
1148 m_textX->SetValue(wxString() << m_pos.x);
1149 m_textY->SetValue(wxString() << m_pos.y);
1150 m_textW->SetValue(wxString() << m_size.x);
1151 m_textH->SetValue(wxString() << m_size.y);
1152
1153 return TRUE;
1154 }
1155
1156 bool DnDShapeDialog::TransferDataFromWindow()
1157 {
1158 m_shapeKind = (DnDShape::Kind)m_radio->GetSelection();
1159
1160 m_pos.x = atoi(m_textX->GetValue());
1161 m_pos.y = atoi(m_textY->GetValue());
1162 m_size.x = atoi(m_textW->GetValue());
1163 m_size.y = atoi(m_textH->GetValue());
1164
1165 if ( !m_pos.x || !m_pos.y || !m_size.x || !m_size.y )
1166 {
1167 wxMessageBox("All sizes and positions should be non null!",
1168 "Invalid shape", wxICON_HAND | wxOK, this);
1169
1170 return FALSE;
1171 }
1172
1173 return TRUE;
1174 }
1175
1176 void DnDShapeDialog::OnColour(wxCommandEvent& WXUNUSED(event))
1177 {
1178 wxColourData data;
1179 data.SetChooseFull(TRUE);
1180 for (int i = 0; i < 16; i++)
1181 {
1182 wxColour colour(i*16, i*16, i*16);
1183 data.SetCustomColour(i, colour);
1184 }
1185
1186 wxColourDialog dialog(this, &data);
1187 if ( dialog.ShowModal() == wxID_OK )
1188 {
1189 m_col = dialog.GetColourData().GetColour();
1190 }
1191 }
1192
1193 // ----------------------------------------------------------------------------
1194 // DnDShapeFrame
1195 // ----------------------------------------------------------------------------
1196
1197 DnDShapeFrame *DnDShapeFrame::ms_lastDropTarget = NULL;
1198
1199 DnDShapeFrame::DnDShapeFrame(wxFrame *parent)
1200 : wxFrame(parent, -1, "Shape Frame",
1201 wxDefaultPosition, wxSize(250, 150))
1202 {
1203 CreateStatusBar();
1204
1205 wxMenu *menuShape = new wxMenu;
1206 menuShape->Append(Menu_Shape_New, "&New default shape\tCtrl-S");
1207 menuShape->Append(Menu_Shape_Edit, "&Edit shape\tCtrl-E");
1208 menuShape->AppendSeparator();
1209 menuShape->Append(Menu_Shape_Clear, "&Clear shape\tDel");
1210
1211 wxMenu *menuClipboard = new wxMenu;
1212 menuClipboard->Append(Menu_ShapeClipboard_Copy, "&Copy\tCtrl-C");
1213 menuClipboard->Append(Menu_ShapeClipboard_Paste, "&Paste\tCtrl-V");
1214
1215 wxMenuBar *menubar = new wxMenuBar;
1216 menubar->Append(menuShape, "&Shape");
1217 menubar->Append(menuClipboard, "&Clipboard");
1218
1219 SetMenuBar(menubar);
1220
1221 SetStatusText("Press Ctrl-S to create a new shape");
1222
1223 SetDropTarget(new DnDShapeDropTarget(this));
1224
1225 m_shape = NULL;
1226
1227 SetBackgroundColour(*wxWHITE);
1228 }
1229
1230 DnDShapeFrame::~DnDShapeFrame()
1231 {
1232 if (m_shape)
1233 delete m_shape;
1234 }
1235
1236 void DnDShapeFrame::SetShape(DnDShape *shape)
1237 {
1238 if (m_shape)
1239 delete m_shape;
1240 m_shape = shape;
1241 Refresh();
1242 }
1243
1244 // callbacks
1245 void DnDShapeFrame::OnDrag(wxMouseEvent& event)
1246 {
1247 if ( !m_shape )
1248 {
1249 event.Skip();
1250
1251 return;
1252 }
1253
1254 // start drag operation
1255 DnDShapeDataObject shapeData(m_shape);
1256 wxDropSource source(shapeData, this, wxICON(mondrian));
1257
1258 const char *pc = NULL;
1259 switch ( source.DoDragDrop(TRUE) )
1260 {
1261 default:
1262 case wxDragError:
1263 wxLogError("An error occured during drag and drop operation");
1264 break;
1265
1266 case wxDragNone:
1267 SetStatusText("Nothing happened");
1268 break;
1269
1270 case wxDragCopy:
1271 pc = "copied";
1272 break;
1273
1274 case wxDragMove:
1275 pc = "moved";
1276 if ( ms_lastDropTarget != this )
1277 {
1278 // don't delete the shape if we dropped it on ourselves!
1279 SetShape(NULL);
1280 }
1281 break;
1282
1283 case wxDragCancel:
1284 SetStatusText("Drag and drop operation cancelled");
1285 break;
1286 }
1287
1288 if ( pc )
1289 {
1290 SetStatusText(wxString("Shape successfully ") + pc);
1291 }
1292 //else: status text already set
1293 }
1294
1295 void DnDShapeFrame::OnDrop(wxCoord x, wxCoord y, DnDShape *shape)
1296 {
1297 ms_lastDropTarget = this;
1298
1299 wxPoint pt(x, y);
1300
1301 wxString s;
1302 s.Printf("Shape dropped at (%ld, %ld)", pt.x, pt.y);
1303 SetStatusText(s);
1304
1305 shape->Move(pt);
1306 SetShape(shape);
1307 }
1308
1309 void DnDShapeFrame::OnEditShape(wxCommandEvent& event)
1310 {
1311 DnDShapeDialog dlg(this, m_shape);
1312 if ( dlg.ShowModal() == wxID_OK )
1313 {
1314 SetShape(dlg.GetShape());
1315
1316 if ( m_shape )
1317 {
1318 SetStatusText("You can now drag the shape to another frame");
1319 }
1320 }
1321 }
1322
1323 void DnDShapeFrame::OnNewShape(wxCommandEvent& event)
1324 {
1325 SetShape(new DnDEllipticShape(wxPoint(10, 10), wxSize(80, 60), *wxRED));
1326
1327 SetStatusText("You can now drag the shape to another frame");
1328 }
1329
1330 void DnDShapeFrame::OnClearShape(wxCommandEvent& event)
1331 {
1332 SetShape(NULL);
1333 }
1334
1335 void DnDShapeFrame::OnCopyShape(wxCommandEvent& event)
1336 {
1337 if ( m_shape )
1338 {
1339 wxClipboardLocker clipLocker;
1340 if ( !clipLocker )
1341 {
1342 wxLogError("Can't open the clipboard");
1343
1344 return;
1345 }
1346
1347 wxTheClipboard->AddData(new DnDShapeDataObject(m_shape));
1348 }
1349 }
1350
1351 void DnDShapeFrame::OnPasteShape(wxCommandEvent& event)
1352 {
1353 wxClipboardLocker clipLocker;
1354 if ( !clipLocker )
1355 {
1356 wxLogError("Can't open the clipboard");
1357
1358 return;
1359 }
1360
1361 DnDShapeDataObject shapeDataObject(NULL);
1362 if ( wxTheClipboard->GetData(shapeDataObject) )
1363 {
1364 SetShape(shapeDataObject.GetShape());
1365 }
1366 else
1367 {
1368 wxLogStatus("No shape on the clipboard");
1369 }
1370 }
1371
1372 void DnDShapeFrame::OnUpdateUICopy(wxUpdateUIEvent& event)
1373 {
1374 event.Enable( m_shape != NULL );
1375 }
1376
1377 void DnDShapeFrame::OnUpdateUIPaste(wxUpdateUIEvent& event)
1378 {
1379 event.Enable( wxTheClipboard->IsSupported(wxDataFormat(shapeFormatId)) );
1380 }
1381
1382 void DnDShapeFrame::OnPaint(wxPaintEvent& event)
1383 {
1384 if ( m_shape )
1385 {
1386 wxPaintDC dc(this);
1387
1388 m_shape->Draw(dc);
1389 }
1390 else
1391 {
1392 event.Skip();
1393 }
1394 }
1395
1396 // ----------------------------------------------------------------------------
1397 // DnDShape
1398 // ----------------------------------------------------------------------------
1399
1400 DnDShape *DnDShape::New(const void *buf)
1401 {
1402 const ShapeDump& dump = *(const ShapeDump *)buf;
1403 switch ( dump.k )
1404 {
1405 case Triangle:
1406 return new DnDTriangularShape(wxPoint(dump.x, dump.y),
1407 wxSize(dump.w, dump.h),
1408 wxColour(dump.r, dump.g, dump.b));
1409
1410 case Rectangle:
1411 return new DnDRectangularShape(wxPoint(dump.x, dump.y),
1412 wxSize(dump.w, dump.h),
1413 wxColour(dump.r, dump.g, dump.b));
1414
1415 case Ellipse:
1416 return new DnDEllipticShape(wxPoint(dump.x, dump.y),
1417 wxSize(dump.w, dump.h),
1418 wxColour(dump.r, dump.g, dump.b));
1419
1420 default:
1421 wxFAIL_MSG("invalid shape!");
1422 return NULL;
1423 }
1424 }
1425
1426 // ----------------------------------------------------------------------------
1427 // DnDShapeDataObject
1428 // ----------------------------------------------------------------------------
1429
1430 void DnDShapeDataObject::CreateBitmap() const
1431 {
1432 wxPoint pos = m_shape->GetPosition();
1433 wxSize size = m_shape->GetSize();
1434 int x = pos.x + size.x,
1435 y = pos.y + size.y;
1436 wxBitmap bitmap(x, y);
1437 wxMemoryDC dc;
1438 dc.SelectObject(bitmap);
1439 dc.SetBrush(wxBrush("white", wxSOLID));
1440 dc.Clear();
1441 m_shape->Draw(dc);
1442 dc.SelectObject(wxNullBitmap);
1443
1444 DnDShapeDataObject *self = (DnDShapeDataObject *)this; // const_cast
1445 self->m_dataobj.SetBitmap(bitmap);
1446 self->m_hasBitmap = TRUE;
1447 }
1448