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