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