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