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