]> git.saurik.com Git - wxWidgets.git/blob - samples/dnd/dnd.cpp
accels for menu commands
[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 if ( m_shape )
1068 {
1069 m_radio->SetSelection(m_shape->GetKind());
1070 m_pos = m_shape->GetPosition();
1071 m_size = m_shape->GetSize();
1072 m_col = m_shape->GetColour();
1073 }
1074 else
1075 {
1076 m_radio->SetSelection(DnDShape::None);
1077 m_pos = wxPoint(1, 1);
1078 m_size = wxSize(100, 100);
1079 }
1080
1081 m_textX->SetValue(wxString() << m_pos.x);
1082 m_textY->SetValue(wxString() << m_pos.y);
1083 m_textW->SetValue(wxString() << m_size.x);
1084 m_textH->SetValue(wxString() << m_size.y);
1085
1086 return TRUE;
1087 }
1088
1089 bool DnDShapeDialog::TransferDataFromWindow()
1090 {
1091 m_shapeKind = (DnDShape::Kind)m_radio->GetSelection();
1092
1093 m_pos.x = atoi(m_textX->GetValue());
1094 m_pos.y = atoi(m_textY->GetValue());
1095 m_size.x = atoi(m_textW->GetValue());
1096 m_size.y = atoi(m_textH->GetValue());
1097
1098 if ( !m_pos.x || !m_pos.y || !m_size.x || !m_size.y )
1099 {
1100 wxMessageBox("All sizes and positions should be non null!",
1101 "Invalid shape", wxICON_HAND | wxOK, this);
1102
1103 return FALSE;
1104 }
1105
1106 return TRUE;
1107 }
1108
1109 void DnDShapeDialog::OnColour(wxCommandEvent& WXUNUSED(event))
1110 {
1111 wxColourData data;
1112 data.SetChooseFull(TRUE);
1113 for (int i = 0; i < 16; i++)
1114 {
1115 wxColour colour(i*16, i*16, i*16);
1116 data.SetCustomColour(i, colour);
1117 }
1118
1119 wxColourDialog dialog(this, &data);
1120 if ( dialog.ShowModal() == wxID_OK )
1121 {
1122 m_col = dialog.GetColourData().GetColour();
1123 }
1124 }
1125
1126 // ----------------------------------------------------------------------------
1127 // DnDShapeFrame
1128 // ----------------------------------------------------------------------------
1129
1130 DnDShapeFrame *DnDShapeFrame::ms_lastDropTarget = NULL;
1131
1132 DnDShapeFrame::DnDShapeFrame(wxFrame *parent)
1133 : wxFrame(parent, -1, "Shape Frame",
1134 wxDefaultPosition, wxSize(250, 150))
1135 {
1136 SetBackgroundColour(*wxWHITE);
1137
1138 CreateStatusBar();
1139
1140 wxMenu *menuShape = new wxMenu;
1141 menuShape->Append(Menu_Shape_New, "&New default shape\tCtrl-S");
1142 menuShape->Append(Menu_Shape_Edit, "&Edit shape\tCtrl-E");
1143 menuShape->AppendSeparator();
1144 menuShape->Append(Menu_Shape_Clear, "&Clear shape\tDel");
1145
1146 wxMenu *menuClipboard = new wxMenu;
1147 menuClipboard->Append(Menu_ShapeClipboard_Copy, "&Copy\tCtrl-C");
1148 menuClipboard->Append(Menu_ShapeClipboard_Paste, "&Paste\tCtrl-V");
1149
1150 wxMenuBar *menubar = new wxMenuBar;
1151 menubar->Append(menuShape, "&Shape");
1152 menubar->Append(menuClipboard, "&Clipboard");
1153
1154 SetMenuBar(menubar);
1155
1156 SetStatusText("Press Ctrl-S to create a new shape");
1157
1158 SetDropTarget(new DnDShapeDropTarget(this));
1159
1160 m_shape = NULL;
1161 }
1162
1163 DnDShapeFrame::~DnDShapeFrame()
1164 {
1165 delete m_shape;
1166 }
1167
1168 void DnDShapeFrame::SetShape(DnDShape *shape)
1169 {
1170 delete m_shape;
1171 m_shape = shape;
1172 Refresh();
1173 }
1174
1175 // callbacks
1176 void DnDShapeFrame::OnDrag(wxMouseEvent& event)
1177 {
1178 if ( !m_shape )
1179 {
1180 event.Skip();
1181
1182 return;
1183 }
1184
1185 // start drag operation
1186 DnDShapeDataObject shapeData(m_shape);
1187 wxDropSource source(shapeData, this, wxICON(mondrian));
1188
1189 const char *pc = NULL;
1190 switch ( source.DoDragDrop(TRUE) )
1191 {
1192 default:
1193 case wxDragError:
1194 wxLogError("An error occured during drag and drop operation");
1195 break;
1196
1197 case wxDragNone:
1198 SetStatusText("Nothing happened");
1199 break;
1200
1201 case wxDragCopy:
1202 pc = "copied";
1203 break;
1204
1205 case wxDragMove:
1206 pc = "moved";
1207 if ( ms_lastDropTarget != this )
1208 {
1209 // don't delete the shape if we dropped it on ourselves!
1210 SetShape(NULL);
1211 }
1212 break;
1213
1214 case wxDragCancel:
1215 SetStatusText("Drag and drop operation cancelled");
1216 break;
1217 }
1218
1219 if ( pc )
1220 {
1221 SetStatusText(wxString("Shape successfully ") + pc);
1222 }
1223 //else: status text already set
1224 }
1225
1226 void DnDShapeFrame::OnEditShape(wxCommandEvent& event)
1227 {
1228 DnDShapeDialog dlg(this, m_shape);
1229 if ( dlg.ShowModal() == wxID_OK )
1230 {
1231 SetShape(dlg.GetShape());
1232
1233 if ( m_shape )
1234 {
1235 SetStatusText("You can now drag the shape to another frame");
1236 }
1237 }
1238 }
1239
1240 void DnDShapeFrame::OnNewShape(wxCommandEvent& event)
1241 {
1242 SetShape(new DnDEllipticShape(wxPoint(10, 10), wxSize(80, 60), *wxRED));
1243
1244 SetStatusText("You can now drag the shape to another frame");
1245 }
1246
1247 void DnDShapeFrame::OnClearShape(wxCommandEvent& event)
1248 {
1249 SetShape(NULL);
1250 }
1251
1252 void DnDShapeFrame::OnCopyShape(wxCommandEvent& event)
1253 {
1254 if ( m_shape )
1255 wxTheClipboard->AddData(new DnDShapeDataObject(m_shape));
1256 }
1257
1258 void DnDShapeFrame::OnPasteShape(wxCommandEvent& event)
1259 {
1260 DnDShapeDataObject shapeDataObject(NULL);
1261 if ( wxTheClipboard->GetData(&shapeDataObject) )
1262 {
1263 SetShape(shapeDataObject.GetShape());
1264 }
1265 else
1266 {
1267 wxLogStatus("No shape on the clipboard");
1268 }
1269 }
1270
1271 void DnDShapeFrame::OnUpdateUICopy(wxUpdateUIEvent& event)
1272 {
1273 event.Enable( m_shape != NULL );
1274 }
1275
1276 void DnDShapeFrame::OnUpdateUIPaste(wxUpdateUIEvent& event)
1277 {
1278 event.Enable( wxTheClipboard->IsSupported(wxDataFormat(shapeFormatId)) );
1279 }
1280
1281 void DnDShapeFrame::OnPaint(wxPaintEvent& event)
1282 {
1283 if ( m_shape )
1284 m_shape->Draw(wxPaintDC(this));
1285 else
1286 event.Skip();
1287 }
1288
1289 void DnDShapeFrame::OnDrop(long x, long y, DnDShape *shape)
1290 {
1291 ms_lastDropTarget = this;
1292
1293 wxString s;
1294 s.Printf("Shape dropped at (%ld, %ld)", x, y);
1295 SetStatusText(s);
1296
1297 shape->Move(ScreenToClient(wxPoint(x, y)));
1298 SetShape(shape);
1299 }
1300
1301 // ----------------------------------------------------------------------------
1302 // DnDShape
1303 // ----------------------------------------------------------------------------
1304
1305 DnDShape *DnDShape::New(const void *buf)
1306 {
1307 const ShapeDump& dump = *(const ShapeDump *)buf;
1308 switch ( dump.k )
1309 {
1310 case Triangle:
1311 return new DnDTriangularShape(wxPoint(dump.x, dump.y),
1312 wxSize(dump.w, dump.h),
1313 wxColour(dump.r, dump.g, dump.b));
1314
1315 case Rectangle:
1316 return new DnDRectangularShape(wxPoint(dump.x, dump.y),
1317 wxSize(dump.w, dump.h),
1318 wxColour(dump.r, dump.g, dump.b));
1319
1320 case Ellipse:
1321 return new DnDEllipticShape(wxPoint(dump.x, dump.y),
1322 wxSize(dump.w, dump.h),
1323 wxColour(dump.r, dump.g, dump.b));
1324
1325 default:
1326 wxFAIL_MSG("invalid shape!");
1327 return NULL;
1328 }
1329 }
1330
1331 // ----------------------------------------------------------------------------
1332 // DnDShapeDataObject
1333 // ----------------------------------------------------------------------------
1334
1335 void DnDShapeDataObject::CreateBitmap() const
1336 {
1337 wxPoint pos = m_shape->GetPosition();
1338 wxSize size = m_shape->GetSize();
1339 int x = pos.x + size.x,
1340 y = pos.y + size.y;
1341 wxBitmap bitmap(x, y);
1342 wxMemoryDC dc;
1343 dc.SelectObject(bitmap);
1344 dc.SetBrush(wxBrush("white", wxSOLID));
1345 dc.Clear();
1346 m_shape->Draw(dc);
1347 dc.SelectObject(wxNullBitmap);
1348
1349 DnDShapeDataObject *self = (DnDShapeDataObject *)this; // const_cast
1350 self->m_dataobj.SetBitmap(bitmap);
1351 self->m_hasBitmap = TRUE;
1352 }
1353