Some work on tabbing and menu accels,
[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 if ( !m_hasBitmap )
424 CreateBitmap();
425
426 return m_dataobj.GetDataHere(pBuf);
427 }
428 }
429
430 virtual bool SetData(const wxDataFormat& format,
431 size_t len, const void *buf)
432 {
433 wxCHECK_MSG( format == m_formatShape, FALSE, "unsupported format" );
434
435 delete m_shape;
436 m_shape = DnDShape::New(buf);
437
438 // the shape has changed
439 m_hasBitmap = FALSE;
440
441 return TRUE;
442 }
443
444 private:
445 // creates a bitmap and assigns it to m_dataobj (also sets m_hasBitmap)
446 void CreateBitmap() const;
447
448 wxDataFormat m_formatShape; // our custom format
449
450 wxBitmapDataObject m_dataobj; // it handles bitmaps
451 bool m_hasBitmap; // true if m_dataobj has valid bitmap
452
453 DnDShape *m_shape; // our data
454 };
455
456 // ----------------------------------------------------------------------------
457 // A dialog to edit shape properties
458 // ----------------------------------------------------------------------------
459
460 class DnDShapeDialog : public wxDialog
461 {
462 public:
463 DnDShapeDialog(wxFrame *parent, DnDShape *shape);
464
465 DnDShape *GetShape() const;
466
467 virtual bool TransferDataToWindow();
468 virtual bool TransferDataFromWindow();
469
470 void OnColour(wxCommandEvent& event);
471
472 private:
473 // input
474 DnDShape *m_shape;
475
476 // output
477 DnDShape::Kind m_shapeKind;
478 wxPoint m_pos;
479 wxSize m_size;
480 wxColour m_col;
481
482 // controls
483 wxRadioBox *m_radio;
484 wxTextCtrl *m_textX,
485 *m_textY,
486 *m_textW,
487 *m_textH;
488
489 DECLARE_EVENT_TABLE()
490 };
491
492 // ----------------------------------------------------------------------------
493 // A frame for the shapes which can be drag-and-dropped between frames
494 // ----------------------------------------------------------------------------
495
496 class DnDShapeFrame : public wxFrame
497 {
498 public:
499 DnDShapeFrame(wxFrame *parent);
500 ~DnDShapeFrame();
501
502 void SetShape(DnDShape *shape);
503
504 // callbacks
505 void OnNewShape(wxCommandEvent& event);
506 void OnEditShape(wxCommandEvent& event);
507 void OnClearShape(wxCommandEvent& event);
508
509 void OnCopyShape(wxCommandEvent& event);
510 void OnPasteShape(wxCommandEvent& event);
511
512 void OnUpdateUICopy(wxUpdateUIEvent& event);
513 void OnUpdateUIPaste(wxUpdateUIEvent& event);
514
515 void OnDrag(wxMouseEvent& event);
516 void OnPaint(wxPaintEvent& event);
517 void OnDrop(wxCoord x, wxCoord y, DnDShape *shape);
518
519 private:
520 DnDShape *m_shape;
521
522 static DnDShapeFrame *ms_lastDropTarget;
523
524 DECLARE_EVENT_TABLE()
525 };
526
527 // ----------------------------------------------------------------------------
528 // wxDropTarget derivation for DnDShapes
529 // ----------------------------------------------------------------------------
530
531 class DnDShapeDropTarget : public wxDropTarget
532 {
533 public:
534 DnDShapeDropTarget(DnDShapeFrame *frame)
535 : wxDropTarget(new DnDShapeDataObject)
536 {
537 m_frame = frame;
538 }
539
540 // override base class (pure) virtuals
541 virtual void OnEnter()
542 { m_frame->SetStatusText("Mouse entered the frame"); }
543 virtual void OnLeave()
544 { m_frame->SetStatusText("Mouse left the frame"); }
545 virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
546 {
547 if ( !GetData() )
548 {
549 wxLogError("Failed to get drag and drop data");
550
551 return wxDragNone;
552 }
553
554 m_frame->OnDrop(x, y,
555 ((DnDShapeDataObject *)GetDataObject())->GetShape());
556
557 return def;
558 }
559
560 private:
561 DnDShapeFrame *m_frame;
562 };
563
564 // ----------------------------------------------------------------------------
565 // IDs for the menu commands
566 // ----------------------------------------------------------------------------
567
568 enum
569 {
570 Menu_Quit = 1,
571 Menu_Drag,
572 Menu_NewFrame,
573 Menu_About = 101,
574 Menu_Help,
575 Menu_Clear,
576 Menu_Copy,
577 Menu_Paste,
578 Menu_CopyBitmap,
579 Menu_PasteBitmap,
580 Menu_ToBeGreyed, /* for testing */
581 Menu_ToBeDeleted, /* for testing */
582 Menu_Shape_New = 500,
583 Menu_Shape_Edit,
584 Menu_Shape_Clear,
585 Menu_ShapeClipboard_Copy,
586 Menu_ShapeClipboard_Paste,
587 Button_Colour = 1001
588 };
589
590 BEGIN_EVENT_TABLE(DnDFrame, wxFrame)
591 EVT_MENU(Menu_Quit, DnDFrame::OnQuit)
592 EVT_MENU(Menu_About, DnDFrame::OnAbout)
593 EVT_MENU(Menu_Drag, DnDFrame::OnDrag)
594 EVT_MENU(Menu_NewFrame, DnDFrame::OnNewFrame)
595 EVT_MENU(Menu_Help, DnDFrame::OnHelp)
596 EVT_MENU(Menu_Clear, DnDFrame::OnLogClear)
597 EVT_MENU(Menu_Copy, DnDFrame::OnCopy)
598 EVT_MENU(Menu_Paste, DnDFrame::OnPaste)
599 EVT_MENU(Menu_CopyBitmap, DnDFrame::OnCopyBitmap)
600 EVT_MENU(Menu_PasteBitmap,DnDFrame::OnPasteBitmap)
601
602 EVT_UPDATE_UI(Menu_Paste, DnDFrame::OnUpdateUIPasteText)
603 EVT_UPDATE_UI(Menu_PasteBitmap, DnDFrame::OnUpdateUIPasteBitmap)
604
605 EVT_LEFT_DOWN( DnDFrame::OnLeftDown)
606 EVT_RIGHT_DOWN( DnDFrame::OnRightDown)
607 EVT_PAINT( DnDFrame::OnPaint)
608 END_EVENT_TABLE()
609
610 BEGIN_EVENT_TABLE(DnDShapeFrame, wxFrame)
611 EVT_MENU(Menu_Shape_New, DnDShapeFrame::OnNewShape)
612 EVT_MENU(Menu_Shape_Edit, DnDShapeFrame::OnEditShape)
613 EVT_MENU(Menu_Shape_Clear, DnDShapeFrame::OnClearShape)
614
615 EVT_MENU(Menu_ShapeClipboard_Copy, DnDShapeFrame::OnCopyShape)
616 EVT_MENU(Menu_ShapeClipboard_Paste, DnDShapeFrame::OnPasteShape)
617
618 EVT_UPDATE_UI(Menu_ShapeClipboard_Copy, DnDShapeFrame::OnUpdateUICopy)
619 EVT_UPDATE_UI(Menu_ShapeClipboard_Paste, DnDShapeFrame::OnUpdateUIPaste)
620
621 EVT_LEFT_DOWN(DnDShapeFrame::OnDrag)
622
623 EVT_PAINT(DnDShapeFrame::OnPaint)
624 END_EVENT_TABLE()
625
626 BEGIN_EVENT_TABLE(DnDShapeDialog, wxDialog)
627 EVT_BUTTON(Button_Colour, DnDShapeDialog::OnColour)
628 END_EVENT_TABLE()
629
630 // ============================================================================
631 // implementation
632 // ============================================================================
633
634 // `Main program' equivalent, creating windows and returning main app frame
635 bool DnDApp::OnInit()
636 {
637 // load our ressources
638 wxPathList pathList;
639 pathList.Add(".");
640 #ifdef __WXMSW__
641 pathList.Add("./Debug");
642 pathList.Add("./Release");
643 #endif // wxMSW
644
645 wxString path = pathList.FindValidPath("dnd.wxr");
646 if ( !path )
647 {
648 wxLogError("Can't find the resource file dnd.wxr in the current "
649 "directory, aborting.");
650
651 return FALSE;
652 }
653
654 wxDefaultResourceTable->ParseResourceFile(path);
655
656 #if wxUSE_LIBPNG
657 wxImage::AddHandler( new wxPNGHandler );
658 #endif
659
660 // create the main frame window
661 DnDFrame *frame = new DnDFrame((wxFrame *) NULL,
662 "Drag-and-Drop/Clipboard wxWindows Sample",
663 50, 50, 450, 340);
664
665 // activate it
666 frame->Show(TRUE);
667
668 SetTopWindow(frame);
669
670 return TRUE;
671 }
672
673 DnDFrame::DnDFrame(wxFrame *frame, char *title, int x, int y, int w, int h)
674 : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h)),
675 m_strText("wxWindows drag & drop works :-)")
676
677 {
678 // frame icon and status bar
679 SetIcon(wxICON(mondrian));
680
681 CreateStatusBar();
682
683 // construct menu
684 wxMenu *file_menu = new wxMenu;
685 file_menu->Append(Menu_Drag, "&Test drag...");
686 file_menu->AppendSeparator();
687 file_menu->Append(Menu_NewFrame, "&New frame\tCtrl-N");
688 file_menu->AppendSeparator();
689 file_menu->Append(Menu_Quit, "E&xit");
690
691 wxMenu *log_menu = new wxMenu;
692 log_menu->Append(Menu_Clear, "Clear\tDel");
693
694 wxMenu *help_menu = new wxMenu;
695 help_menu->Append(Menu_Help, "&Help...");
696 help_menu->AppendSeparator();
697 help_menu->Append(Menu_About, "&About");
698
699 wxMenu *clip_menu = new wxMenu;
700 clip_menu->Append(Menu_Copy, "&Copy text\tCtrl+C");
701 clip_menu->Append(Menu_Paste, "&Paste text\tCtrl+V");
702 clip_menu->AppendSeparator();
703 clip_menu->Append(Menu_CopyBitmap, "&Copy bitmap\tAlt+C");
704 clip_menu->Append(Menu_PasteBitmap, "&Paste bitmap\tAlt+V");
705
706 wxMenuBar *menu_bar = new wxMenuBar;
707 menu_bar->Append(file_menu, "&File");
708 menu_bar->Append(log_menu, "&Log");
709 menu_bar->Append(clip_menu, "&Clipboard");
710 menu_bar->Append(help_menu, "&Help");
711
712 SetMenuBar(menu_bar);
713
714 // make a panel with 3 subwindows
715 wxPoint pos(0, 0);
716 wxSize size(400, 200);
717
718 wxString strFile("Drop files here!"), strText("Drop text on me");
719
720 m_ctrlFile = new wxListBox(this, -1, pos, size, 1, &strFile,
721 wxLB_HSCROLL | wxLB_ALWAYS_SB );
722 m_ctrlText = new wxListBox(this, -1, pos, size, 1, &strText,
723 wxLB_HSCROLL | wxLB_ALWAYS_SB );
724
725 m_ctrlLog = new wxTextCtrl(this, -1, "", pos, size,
726 wxTE_MULTILINE | wxTE_READONLY |
727 wxSUNKEN_BORDER );
728
729 #ifdef __WXMSW__
730 // redirect log messages to the text window and switch on OLE messages
731 // logging
732 wxLog::AddTraceMask(wxTRACE_OleCalls);
733 #endif
734 m_pLog = new wxLogTextCtrl(m_ctrlLog);
735 m_pLogPrev = wxLog::SetActiveTarget(m_pLog);
736
737 // associate drop targets with 2 text controls
738 m_ctrlFile->SetDropTarget(new DnDFile(m_ctrlFile));
739 m_ctrlText->SetDropTarget(new DnDText(m_ctrlText));
740
741 wxLayoutConstraints *c;
742
743 // Top-left listbox
744 c = new wxLayoutConstraints;
745 c->left.SameAs(this, wxLeft);
746 c->top.SameAs(this, wxTop);
747 c->right.PercentOf(this, wxRight, 50);
748 c->height.PercentOf(this, wxHeight, 30);
749 m_ctrlFile->SetConstraints(c);
750
751 // Top-right listbox
752 c = new wxLayoutConstraints;
753 c->left.SameAs (m_ctrlFile, wxRight);
754 c->top.SameAs (this, wxTop);
755 c->right.SameAs (this, wxRight);
756 c->height.PercentOf(this, wxHeight, 30);
757 m_ctrlText->SetConstraints(c);
758
759 // Lower text control
760 c = new wxLayoutConstraints;
761 c->left.SameAs (this, wxLeft);
762 c->right.SameAs (this, wxRight);
763 c->height.PercentOf(this, wxHeight, 50);
764 c->top.SameAs(m_ctrlText, wxBottom);
765 m_ctrlLog->SetConstraints(c);
766
767 SetAutoLayout(TRUE);
768 }
769
770 void DnDFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
771 {
772 Close(TRUE);
773 }
774
775 void DnDFrame::OnPaint(wxPaintEvent& WXUNUSED(event))
776 {
777 int w = 0;
778 int h = 0;
779 GetClientSize( &w, &h );
780
781 wxPaintDC dc(this);
782 dc.SetFont( wxFont( 24, wxDECORATIVE, wxNORMAL, wxNORMAL, FALSE, "charter" ) );
783 dc.DrawText( "Drag text from here!", 20, h-50 );
784
785 if ( m_bitmap.Ok() )
786 {
787 // 4/5 is 80% taken by other windows, 20 is arbitrary margin
788 dc.DrawBitmap(m_bitmap,
789 w - m_bitmap.GetWidth() - 20,
790 (4*h)/5 + 20);
791 }
792 }
793
794 void DnDFrame::OnUpdateUIPasteText(wxUpdateUIEvent& event)
795 {
796 event.Enable( wxTheClipboard->IsSupported(wxDF_TEXT) );
797 }
798
799 void DnDFrame::OnUpdateUIPasteBitmap(wxUpdateUIEvent& event)
800 {
801 event.Enable( wxTheClipboard->IsSupported(wxDF_BITMAP) );
802 }
803
804 void DnDFrame::OnNewFrame(wxCommandEvent& WXUNUSED(event))
805 {
806 (new DnDShapeFrame(this))->Show(TRUE);
807
808 wxLogStatus(this, "Double click the new frame to select a shape for it");
809 }
810
811 void DnDFrame::OnDrag(wxCommandEvent& WXUNUSED(event))
812 {
813 wxString strText = wxGetTextFromUser
814 (
815 "After you enter text in this dialog, press any mouse\n"
816 "button in the bottom (empty) part of the frame and \n"
817 "drag it anywhere - you will be in fact dragging the\n"
818 "text object containing this text",
819 "Please enter some text", m_strText, this
820 );
821
822 m_strText = strText;
823 }
824
825 void DnDFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
826 {
827 wxMessageBox("Drag-&-Drop Demo\n"
828 "Please see \"Help|Help...\" for details\n"
829 "Copyright (c) 1998 Vadim Zeitlin",
830 "About wxDnD",
831 wxICON_INFORMATION | wxOK,
832 this);
833 }
834
835 void DnDFrame::OnHelp(wxCommandEvent& /* event */)
836 {
837 wxMessageDialog dialog(this,
838 "This small program demonstrates drag & drop support in wxWindows. The program window\n"
839 "consists of 3 parts: the bottom pane is for debug messages, so that you can see what's\n"
840 "going on inside. The top part is split into 2 listboxes, the left one accepts files\n"
841 "and the right one accepts text.\n"
842 "\n"
843 "To test wxDropTarget: open wordpad (write.exe), select some text in it and drag it to\n"
844 "the right listbox (you'll notice the usual visual feedback, i.e. the cursor will change).\n"
845 "Also, try dragging some files (you can select several at once) from Windows Explorer (or \n"
846 "File Manager) to the left pane. Hold down Ctrl/Shift keys when you drop text (doesn't \n"
847 "work with files) and see what changes.\n"
848 "\n"
849 "To test wxDropSource: just press any mouse button on the empty zone of the window and drag\n"
850 "it to wordpad or any other droptarget accepting text (and of course you can just drag it\n"
851 "to the right pane). Due to a lot of trace messages, the cursor might take some time to \n"
852 "change, don't release the mouse button until it does. You can change the string being\n"
853 "dragged in in \"File|Test drag...\" dialog.\n"
854 "\n"
855 "\n"
856 "Please send all questions/bug reports/suggestions &c to \n"
857 "Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>",
858 "wxDnD Help");
859
860 dialog.ShowModal();
861 }
862
863 void DnDFrame::OnLogClear(wxCommandEvent& /* event */ )
864 {
865 m_ctrlLog->Clear();
866 }
867
868 void DnDFrame::OnLeftDown(wxMouseEvent &WXUNUSED(event) )
869 {
870 if ( !m_strText.IsEmpty() )
871 {
872 // start drag operation
873 wxTextDataObject textData(m_strText);
874 wxDropSource source(textData, this
875 #ifdef __WXMSW__
876 ,wxCURSOR_PENCIL, // for copy
877 wxCURSOR_SPRAYCAN, // for move
878 wxCURSOR_QUESTION_ARROW // for nothing
879 #endif
880 );
881
882 const char *pc;
883
884 switch ( source.DoDragDrop(TRUE) )
885 {
886 case wxDragError: pc = "Error!"; break;
887 case wxDragNone: pc = "Nothing"; break;
888 case wxDragCopy: pc = "Copied"; break;
889 case wxDragMove: pc = "Moved"; break;
890 case wxDragCancel: pc = "Cancelled"; break;
891 default: pc = "Huh?"; break;
892 }
893
894 SetStatusText(wxString("Drag result: ") + pc);
895 }
896 }
897
898 void DnDFrame::OnRightDown(wxMouseEvent &event )
899 {
900 wxMenu *menu = new wxMenu;
901
902 menu->Append(Menu_Drag, "&Test drag...");
903 menu->Append(Menu_About, "&About");
904 menu->Append(Menu_Quit, "E&xit");
905 menu->Append(Menu_ToBeDeleted, "To be deleted");
906 menu->Append(Menu_ToBeGreyed, "To be greyed");
907
908 menu->Delete( Menu_ToBeDeleted );
909 menu->Enable( Menu_ToBeGreyed, FALSE );
910
911 PopupMenu( menu, event.GetX(), event.GetY() );
912 }
913
914 DnDFrame::~DnDFrame()
915 {
916 if ( m_pLog != NULL ) {
917 if ( wxLog::SetActiveTarget(m_pLogPrev) == m_pLog )
918 delete m_pLog;
919 }
920 }
921
922 // ---------------------------------------------------------------------------
923 // bitmap clipboard
924 // ---------------------------------------------------------------------------
925
926 void DnDFrame::OnCopyBitmap(wxCommandEvent& WXUNUSED(event))
927 {
928 // PNG support is not always compiled in under Windows, so use BMP there
929 #ifdef __WXMSW__
930 wxFileDialog dialog(this, "Open a BMP file", "", "", "BMP files (*.bmp)|*.bmp", 0);
931 #else
932 wxFileDialog dialog(this, "Open a PNG file", "", "", "PNG files (*.png)|*.png", 0);
933 #endif
934
935 if (dialog.ShowModal() != wxID_OK)
936 {
937 wxLogMessage( _T("Aborted file open") );
938 return;
939 }
940
941 if (dialog.GetPath().IsEmpty())
942 {
943 wxLogMessage( _T("Returned empty string.") );
944 return;
945 }
946
947 if (!wxFileExists(dialog.GetPath()))
948 {
949 wxLogMessage( _T("File doesn't exist.") );
950 return;
951 }
952
953 wxImage image;
954 image.LoadFile( dialog.GetPath(),
955 #ifdef __WXMSW__
956 wxBITMAP_TYPE_BMP
957 #else
958 wxBITMAP_TYPE_PNG
959 #endif
960 );
961 if (!image.Ok())
962 {
963 wxLogError( _T("Invalid image file...") );
964 return;
965 }
966
967 wxLogStatus( _T("Decoding image file...") );
968 wxYield();
969
970 wxBitmap bitmap( image.ConvertToBitmap() );
971
972 if ( !wxTheClipboard->Open() )
973 {
974 wxLogError(_T("Can't open clipboard."));
975
976 return;
977 }
978
979 wxLogMessage( _T("Creating wxBitmapDataObject...") );
980 wxYield();
981
982 if ( !wxTheClipboard->AddData(new wxBitmapDataObject(bitmap)) )
983 {
984 wxLogError(_T("Can't copy image to the clipboard."));
985 }
986 else
987 {
988 wxLogMessage(_T("Image has been put on the clipboard.") );
989 wxLogMessage(_T("You can paste it now and look at it.") );
990 }
991
992 wxTheClipboard->Close();
993 }
994
995 void DnDFrame::OnPasteBitmap(wxCommandEvent& WXUNUSED(event))
996 {
997 if ( !wxTheClipboard->Open() )
998 {
999 wxLogError(_T("Can't open clipboard."));
1000
1001 return;
1002 }
1003
1004 if ( !wxTheClipboard->IsSupported(wxDF_BITMAP) )
1005 {
1006 wxLogWarning(_T("No bitmap on clipboard"));
1007
1008 wxTheClipboard->Close();
1009 return;
1010 }
1011
1012 wxBitmapDataObject data;
1013 if ( !wxTheClipboard->GetData(data) )
1014 {
1015 wxLogError(_T("Can't paste bitmap from the clipboard"));
1016 }
1017 else
1018 {
1019 wxLogMessage(_T("Bitmap pasted from the clipboard") );
1020 m_bitmap = data.GetBitmap();
1021 Refresh();
1022 }
1023
1024 wxTheClipboard->Close();
1025 }
1026
1027 // ---------------------------------------------------------------------------
1028 // text clipboard
1029 // ---------------------------------------------------------------------------
1030
1031 void DnDFrame::OnCopy(wxCommandEvent& WXUNUSED(event))
1032 {
1033 if ( !wxTheClipboard->Open() )
1034 {
1035 wxLogError(_T("Can't open clipboard."));
1036
1037 return;
1038 }
1039
1040 if ( !wxTheClipboard->AddData(new wxTextDataObject(m_strText)) )
1041 {
1042 wxLogError(_T("Can't copy data to the clipboard"));
1043 }
1044 else
1045 {
1046 wxLogMessage(_T("Text '%s' put on the clipboard"), m_strText.c_str());
1047 }
1048
1049 wxTheClipboard->Close();
1050 }
1051
1052 void DnDFrame::OnPaste(wxCommandEvent& WXUNUSED(event))
1053 {
1054 if ( !wxTheClipboard->Open() )
1055 {
1056 wxLogError(_T("Can't open clipboard."));
1057
1058 return;
1059 }
1060
1061 if ( !wxTheClipboard->IsSupported(wxDF_TEXT) )
1062 {
1063 wxLogWarning(_T("No text data on clipboard"));
1064
1065 wxTheClipboard->Close();
1066 return;
1067 }
1068
1069 wxTextDataObject text;
1070 if ( !wxTheClipboard->GetData(text) )
1071 {
1072 wxLogError(_T("Can't paste data from the clipboard"));
1073 }
1074 else
1075 {
1076 wxLogMessage(_T("Text '%s' pasted from the clipboard"),
1077 text.GetText().c_str());
1078 }
1079
1080 wxTheClipboard->Close();
1081 }
1082
1083 // ----------------------------------------------------------------------------
1084 // Notifications called by the base class
1085 // ----------------------------------------------------------------------------
1086
1087 bool DnDText::OnDropText(wxCoord, wxCoord, const wxString& text)
1088 {
1089 m_pOwner->Append(text);
1090
1091 return TRUE;
1092 }
1093
1094 bool DnDFile::OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames)
1095 {
1096 size_t nFiles = filenames.GetCount();
1097 wxString str;
1098 str.Printf( _T("%d files dropped"), nFiles);
1099 m_pOwner->Append(str);
1100 for ( size_t n = 0; n < nFiles; n++ ) {
1101 m_pOwner->Append(filenames[n]);
1102 }
1103
1104 return TRUE;
1105 }
1106
1107 // ----------------------------------------------------------------------------
1108 // DnDShapeDialog
1109 // ----------------------------------------------------------------------------
1110
1111 DnDShapeDialog::DnDShapeDialog(wxFrame *parent, DnDShape *shape)
1112 {
1113 m_shape = shape;
1114
1115 LoadFromResource(parent, "dialogShape");
1116
1117 m_textX = (wxTextCtrl *)wxFindWindowByName("textX", this);
1118 m_textY = (wxTextCtrl *)wxFindWindowByName("textY", this);
1119 m_textW = (wxTextCtrl *)wxFindWindowByName("textW", this);
1120 m_textH = (wxTextCtrl *)wxFindWindowByName("textH", this);
1121
1122 m_radio = (wxRadioBox *)wxFindWindowByName("radio", this);
1123 }
1124
1125 DnDShape *DnDShapeDialog::GetShape() const
1126 {
1127 switch ( m_shapeKind )
1128 {
1129 default:
1130 case DnDShape::None: return NULL;
1131 case DnDShape::Triangle: return new DnDTriangularShape(m_pos, m_size, m_col);
1132 case DnDShape::Rectangle: return new DnDRectangularShape(m_pos, m_size, m_col);
1133 case DnDShape::Ellipse: return new DnDEllipticShape(m_pos, m_size, m_col);
1134 }
1135 }
1136
1137 bool DnDShapeDialog::TransferDataToWindow()
1138 {
1139
1140 if ( m_shape )
1141 {
1142 m_radio->SetSelection(m_shape->GetKind());
1143 m_pos = m_shape->GetPosition();
1144 m_size = m_shape->GetSize();
1145 m_col = m_shape->GetColour();
1146 }
1147 else
1148 {
1149 m_radio->SetSelection(DnDShape::None);
1150 m_pos = wxPoint(1, 1);
1151 m_size = wxSize(100, 100);
1152 }
1153
1154 m_textX->SetValue(wxString() << m_pos.x);
1155 m_textY->SetValue(wxString() << m_pos.y);
1156 m_textW->SetValue(wxString() << m_size.x);
1157 m_textH->SetValue(wxString() << m_size.y);
1158
1159 return TRUE;
1160 }
1161
1162 bool DnDShapeDialog::TransferDataFromWindow()
1163 {
1164 m_shapeKind = (DnDShape::Kind)m_radio->GetSelection();
1165
1166 m_pos.x = atoi(m_textX->GetValue());
1167 m_pos.y = atoi(m_textY->GetValue());
1168 m_size.x = atoi(m_textW->GetValue());
1169 m_size.y = atoi(m_textH->GetValue());
1170
1171 if ( !m_pos.x || !m_pos.y || !m_size.x || !m_size.y )
1172 {
1173 wxMessageBox("All sizes and positions should be non null!",
1174 "Invalid shape", wxICON_HAND | wxOK, this);
1175
1176 return FALSE;
1177 }
1178
1179 return TRUE;
1180 }
1181
1182 void DnDShapeDialog::OnColour(wxCommandEvent& WXUNUSED(event))
1183 {
1184 wxColourData data;
1185 data.SetChooseFull(TRUE);
1186 for (int i = 0; i < 16; i++)
1187 {
1188 wxColour colour(i*16, i*16, i*16);
1189 data.SetCustomColour(i, colour);
1190 }
1191
1192 wxColourDialog dialog(this, &data);
1193 if ( dialog.ShowModal() == wxID_OK )
1194 {
1195 m_col = dialog.GetColourData().GetColour();
1196 }
1197 }
1198
1199 // ----------------------------------------------------------------------------
1200 // DnDShapeFrame
1201 // ----------------------------------------------------------------------------
1202
1203 DnDShapeFrame *DnDShapeFrame::ms_lastDropTarget = NULL;
1204
1205 DnDShapeFrame::DnDShapeFrame(wxFrame *parent)
1206 : wxFrame(parent, -1, "Shape Frame",
1207 wxDefaultPosition, wxSize(250, 150))
1208 {
1209 CreateStatusBar();
1210
1211 wxMenu *menuShape = new wxMenu;
1212 menuShape->Append(Menu_Shape_New, "&New default shape\tCtrl-S");
1213 menuShape->Append(Menu_Shape_Edit, "&Edit shape\tCtrl-E");
1214 menuShape->AppendSeparator();
1215 menuShape->Append(Menu_Shape_Clear, "&Clear shape\tDel");
1216
1217 wxMenu *menuClipboard = new wxMenu;
1218 menuClipboard->Append(Menu_ShapeClipboard_Copy, "&Copy\tCtrl-C");
1219 menuClipboard->Append(Menu_ShapeClipboard_Paste, "&Paste\tCtrl-V");
1220
1221 wxMenuBar *menubar = new wxMenuBar;
1222 menubar->Append(menuShape, "&Shape");
1223 menubar->Append(menuClipboard, "&Clipboard");
1224
1225 SetMenuBar(menubar);
1226
1227 SetStatusText("Press Ctrl-S to create a new shape");
1228
1229 SetDropTarget(new DnDShapeDropTarget(this));
1230
1231 m_shape = NULL;
1232
1233 SetBackgroundColour(*wxWHITE);
1234 }
1235
1236 DnDShapeFrame::~DnDShapeFrame()
1237 {
1238 if (m_shape)
1239 delete m_shape;
1240 }
1241
1242 void DnDShapeFrame::SetShape(DnDShape *shape)
1243 {
1244 if (m_shape)
1245 delete m_shape;
1246 m_shape = shape;
1247 Refresh();
1248 }
1249
1250 // callbacks
1251 void DnDShapeFrame::OnDrag(wxMouseEvent& event)
1252 {
1253 if ( !m_shape )
1254 {
1255 event.Skip();
1256
1257 return;
1258 }
1259
1260 // start drag operation
1261 DnDShapeDataObject shapeData(m_shape);
1262 wxDropSource source(shapeData, this);
1263
1264 const char *pc = NULL;
1265 switch ( source.DoDragDrop(TRUE) )
1266 {
1267 default:
1268 case wxDragError:
1269 wxLogError("An error occured during drag and drop operation");
1270 break;
1271
1272 case wxDragNone:
1273 SetStatusText("Nothing happened");
1274 break;
1275
1276 case wxDragCopy:
1277 pc = "copied";
1278 break;
1279
1280 case wxDragMove:
1281 pc = "moved";
1282 if ( ms_lastDropTarget != this )
1283 {
1284 // don't delete the shape if we dropped it on ourselves!
1285 SetShape(NULL);
1286 }
1287 break;
1288
1289 case wxDragCancel:
1290 SetStatusText("Drag and drop operation cancelled");
1291 break;
1292 }
1293
1294 if ( pc )
1295 {
1296 SetStatusText(wxString("Shape successfully ") + pc);
1297 }
1298 //else: status text already set
1299 }
1300
1301 void DnDShapeFrame::OnDrop(wxCoord x, wxCoord y, DnDShape *shape)
1302 {
1303 ms_lastDropTarget = this;
1304
1305 wxPoint pt(x, y);
1306
1307 wxString s;
1308 s.Printf("Shape dropped at (%ld, %ld)", pt.x, pt.y);
1309 SetStatusText(s);
1310
1311 shape->Move(pt);
1312 SetShape(shape);
1313 }
1314
1315 void DnDShapeFrame::OnEditShape(wxCommandEvent& event)
1316 {
1317 DnDShapeDialog dlg(this, m_shape);
1318 if ( dlg.ShowModal() == wxID_OK )
1319 {
1320 SetShape(dlg.GetShape());
1321
1322 if ( m_shape )
1323 {
1324 SetStatusText("You can now drag the shape to another frame");
1325 }
1326 }
1327 }
1328
1329 void DnDShapeFrame::OnNewShape(wxCommandEvent& event)
1330 {
1331 SetShape(new DnDEllipticShape(wxPoint(10, 10), wxSize(80, 60), *wxRED));
1332
1333 SetStatusText("You can now drag the shape to another frame");
1334 }
1335
1336 void DnDShapeFrame::OnClearShape(wxCommandEvent& event)
1337 {
1338 SetShape(NULL);
1339 }
1340
1341 void DnDShapeFrame::OnCopyShape(wxCommandEvent& event)
1342 {
1343 if ( m_shape )
1344 {
1345 wxClipboardLocker clipLocker;
1346 if ( !clipLocker )
1347 {
1348 wxLogError("Can't open the clipboard");
1349
1350 return;
1351 }
1352
1353 wxTheClipboard->AddData(new DnDShapeDataObject(m_shape));
1354 }
1355 }
1356
1357 void DnDShapeFrame::OnPasteShape(wxCommandEvent& event)
1358 {
1359 wxClipboardLocker clipLocker;
1360 if ( !clipLocker )
1361 {
1362 wxLogError("Can't open the clipboard");
1363
1364 return;
1365 }
1366
1367 DnDShapeDataObject shapeDataObject(NULL);
1368 if ( wxTheClipboard->GetData(shapeDataObject) )
1369 {
1370 SetShape(shapeDataObject.GetShape());
1371 }
1372 else
1373 {
1374 wxLogStatus("No shape on the clipboard");
1375 }
1376 }
1377
1378 void DnDShapeFrame::OnUpdateUICopy(wxUpdateUIEvent& event)
1379 {
1380 event.Enable( m_shape != NULL );
1381 }
1382
1383 void DnDShapeFrame::OnUpdateUIPaste(wxUpdateUIEvent& event)
1384 {
1385 event.Enable( wxTheClipboard->IsSupported(wxDataFormat(shapeFormatId)) );
1386 }
1387
1388 void DnDShapeFrame::OnPaint(wxPaintEvent& event)
1389 {
1390 if ( m_shape )
1391 {
1392 wxPaintDC dc(this);
1393
1394 m_shape->Draw(dc);
1395 }
1396 else
1397 {
1398 event.Skip();
1399 }
1400 }
1401
1402 // ----------------------------------------------------------------------------
1403 // DnDShape
1404 // ----------------------------------------------------------------------------
1405
1406 DnDShape *DnDShape::New(const void *buf)
1407 {
1408 const ShapeDump& dump = *(const ShapeDump *)buf;
1409 switch ( dump.k )
1410 {
1411 case Triangle:
1412 return new DnDTriangularShape(wxPoint(dump.x, dump.y),
1413 wxSize(dump.w, dump.h),
1414 wxColour(dump.r, dump.g, dump.b));
1415
1416 case Rectangle:
1417 return new DnDRectangularShape(wxPoint(dump.x, dump.y),
1418 wxSize(dump.w, dump.h),
1419 wxColour(dump.r, dump.g, dump.b));
1420
1421 case Ellipse:
1422 return new DnDEllipticShape(wxPoint(dump.x, dump.y),
1423 wxSize(dump.w, dump.h),
1424 wxColour(dump.r, dump.g, dump.b));
1425
1426 default:
1427 wxFAIL_MSG("invalid shape!");
1428 return NULL;
1429 }
1430 }
1431
1432 // ----------------------------------------------------------------------------
1433 // DnDShapeDataObject
1434 // ----------------------------------------------------------------------------
1435
1436 void DnDShapeDataObject::CreateBitmap() const
1437 {
1438 wxPoint pos = m_shape->GetPosition();
1439 wxSize size = m_shape->GetSize();
1440 int x = pos.x + size.x,
1441 y = pos.y + size.y;
1442 wxBitmap bitmap(x, y);
1443 wxMemoryDC dc;
1444 dc.SelectObject(bitmap);
1445 dc.SetBrush(wxBrush("white", wxSOLID));
1446 dc.Clear();
1447 m_shape->Draw(dc);
1448 dc.SelectObject(wxNullBitmap);
1449
1450 DnDShapeDataObject *self = (DnDShapeDataObject *)this; // const_cast
1451 self->m_dataobj.SetBitmap(bitmap);
1452 self->m_hasBitmap = TRUE;
1453 }
1454