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