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