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