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