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