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