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