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