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