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