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