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