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