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