1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Drag and drop sample
4 // Author: Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
22 #if !wxUSE_DRAG_AND_DROP
23 #error This sample requires drag and drop support in the library
26 // under Windows we also support data transfer of metafiles as an extra bonus,
27 // but they're not available under other platforms
36 #include "wx/dirdlg.h"
37 #include "wx/filedlg.h"
39 #include "wx/clipbrd.h"
40 #include "wx/colordlg.h"
41 #include "wx/resource.h"
44 #include "wx/metafile.h"
47 #if defined(__WXGTK__) || defined(__WXMOTIF__)
48 #include "mondrian.xpm"
51 // ----------------------------------------------------------------------------
52 // Derive two simple classes which just put in the listbox the strings (text or
53 // file names) we drop on them
54 // ----------------------------------------------------------------------------
56 class DnDText
: public wxTextDropTarget
59 DnDText(wxListBox
*pOwner
) { m_pOwner
= pOwner
; }
61 virtual bool OnDropText(wxCoord x
, wxCoord y
, const wxString
& text
);
67 class DnDFile
: public wxFileDropTarget
70 DnDFile(wxListBox
*pOwner
) { m_pOwner
= pOwner
; }
72 virtual bool OnDropFiles(wxCoord x
, wxCoord y
,
73 const wxArrayString
& filenames
);
79 // ----------------------------------------------------------------------------
80 // Define a new application type
81 // ----------------------------------------------------------------------------
83 class DnDApp
: public wxApp
86 virtual bool OnInit();
89 IMPLEMENT_APP(DnDApp
);
91 // ----------------------------------------------------------------------------
92 // Define a new frame type for the main frame
93 // ----------------------------------------------------------------------------
95 class DnDFrame
: public wxFrame
98 DnDFrame(wxFrame
*frame
, char *title
, int x
, int y
, int w
, int h
);
101 void OnPaint(wxPaintEvent
& event
);
102 void OnQuit (wxCommandEvent
& event
);
103 void OnAbout(wxCommandEvent
& event
);
104 void OnDrag (wxCommandEvent
& event
);
105 void OnNewFrame(wxCommandEvent
& event
);
106 void OnHelp (wxCommandEvent
& event
);
107 void OnLogClear(wxCommandEvent
& event
);
109 void OnCopy(wxCommandEvent
& event
);
110 void OnPaste(wxCommandEvent
& event
);
112 void OnCopyBitmap(wxCommandEvent
& event
);
113 void OnPasteBitmap(wxCommandEvent
& event
);
116 void OnPasteMetafile(wxCommandEvent
& event
);
117 #endif // USE_METAFILES
119 void OnCopyFiles(wxCommandEvent
& event
);
121 void OnLeftDown(wxMouseEvent
& event
);
122 void OnRightDown(wxMouseEvent
& event
);
124 void OnUpdateUIPasteText(wxUpdateUIEvent
& event
);
125 void OnUpdateUIPasteBitmap(wxUpdateUIEvent
& event
);
127 DECLARE_EVENT_TABLE()
130 wxListBox
*m_ctrlFile
,
132 wxTextCtrl
*m_ctrlLog
;
141 // ----------------------------------------------------------------------------
142 // A shape is an example of application-specific data which may be transported
143 // via drag-and-drop or clipboard: in our case, we have different geometric
144 // shapes, each one with its own colour and position
145 // ----------------------------------------------------------------------------
158 DnDShape(const wxPoint
& pos
,
161 : m_pos(pos
), m_size(size
), m_col(col
)
165 // this is for debugging - lets us see when exactly an object is freed
166 // (this may be later than you think if it's on the clipboard, for example)
167 virtual ~DnDShape() { }
169 // the functions used for drag-and-drop: they dump and restore a shape into
170 // some bitwise-copiable data (might use streams too...)
171 // ------------------------------------------------------------------------
173 // restore from buffer
174 static DnDShape
*New(const void *buf
);
176 virtual size_t GetDataSize() const
178 return sizeof(ShapeDump
);
181 virtual void GetDataHere(void *buf
) const
183 ShapeDump
& dump
= *(ShapeDump
*)buf
;
188 dump
.r
= m_col
.Red();
189 dump
.g
= m_col
.Green();
190 dump
.b
= m_col
.Blue();
195 const wxPoint
& GetPosition() const { return m_pos
; }
196 const wxColour
& GetColour() const { return m_col
; }
197 const wxSize
& GetSize() const { return m_size
; }
199 void Move(const wxPoint
& pos
) { m_pos
= pos
; }
201 // to implement in derived classes
202 virtual Kind
GetKind() const = 0;
204 virtual void Draw(wxDC
& dc
)
206 dc
.SetPen(wxPen(m_col
, 1, wxSOLID
));
210 wxPoint
GetCentre() const
211 { return wxPoint(m_pos
.x
+ m_size
.x
/ 2, m_pos
.y
+ m_size
.y
/ 2); }
215 int x
, y
, // position
226 class DnDTriangularShape
: public DnDShape
229 DnDTriangularShape(const wxPoint
& pos
,
232 : DnDShape(pos
, size
, col
)
234 wxLogMessage("DnDTriangularShape is being created");
237 virtual ~DnDTriangularShape()
239 wxLogMessage("DnDTriangularShape is being deleted");
242 virtual Kind
GetKind() const { return Triangle
; }
243 virtual void Draw(wxDC
& dc
)
247 // well, it's a bit difficult to describe a triangle by position and
248 // size, but we're not doing geometry here, do we? ;-)
250 wxPoint
p2(m_pos
.x
+ m_size
.x
, m_pos
.y
);
251 wxPoint
p3(m_pos
.x
, m_pos
.y
+ m_size
.y
);
258 dc
.FloodFill(GetCentre(), m_col
, wxFLOOD_BORDER
);
263 class DnDRectangularShape
: public DnDShape
266 DnDRectangularShape(const wxPoint
& pos
,
269 : DnDShape(pos
, size
, col
)
271 wxLogMessage("DnDRectangularShape is being created");
274 virtual ~DnDRectangularShape()
276 wxLogMessage("DnDRectangularShape is being deleted");
279 virtual Kind
GetKind() const { return Rectangle
; }
280 virtual void Draw(wxDC
& dc
)
285 wxPoint
p2(p1
.x
+ m_size
.x
, p1
.y
);
286 wxPoint
p3(p2
.x
, p2
.y
+ m_size
.y
);
287 wxPoint
p4(p1
.x
, p3
.y
);
295 dc
.FloodFill(GetCentre(), m_col
, wxFLOOD_BORDER
);
300 class DnDEllipticShape
: public DnDShape
303 DnDEllipticShape(const wxPoint
& pos
,
306 : DnDShape(pos
, size
, col
)
308 wxLogMessage("DnDEllipticShape is being created");
311 virtual ~DnDEllipticShape()
313 wxLogMessage("DnDEllipticShape is being deleted");
316 virtual Kind
GetKind() const { return Ellipse
; }
317 virtual void Draw(wxDC
& dc
)
321 dc
.DrawEllipse(m_pos
, m_size
);
324 dc
.FloodFill(GetCentre(), m_col
, wxFLOOD_BORDER
);
329 // ----------------------------------------------------------------------------
330 // A wxDataObject specialisation for the application-specific data
331 // ----------------------------------------------------------------------------
333 static const char *shapeFormatId
= "wxShape";
335 class DnDShapeDataObject
: public wxDataObject
338 // ctor doesn't copy the pointer, so it shouldn't go away while this object
340 DnDShapeDataObject(DnDShape
*shape
= (DnDShape
*)NULL
)
344 // we need to copy the shape because the one we're handled may be
345 // deleted while it's still on the clipboard (for example) - and we
346 // reuse the serialisation methods here to copy it
347 void *buf
= malloc(shape
->DnDShape::GetDataSize());
348 shape
->GetDataHere(buf
);
349 m_shape
= DnDShape::New(buf
);
359 // this string should uniquely identify our format, but is otherwise
361 m_formatShape
.SetId(shapeFormatId
);
363 // we don't draw the shape to a bitmap until it's really needed (i.e.
364 // we're asked to do so)
367 m_hasMetaFile
= FALSE
;
371 virtual ~DnDShapeDataObject() { delete m_shape
; }
373 // after a call to this function, the shape is owned by the caller and it
374 // is responsible for deleting it!
376 // NB: a better solution would be to make DnDShapes ref counted and this
377 // is what should probably be done in a real life program, otherwise
378 // the ownership problems become too complicated really fast
381 DnDShape
*shape
= m_shape
;
383 m_shape
= (DnDShape
*)NULL
;
386 m_hasMetaFile
= FALSE
;
392 // implement base class pure virtuals
393 // ----------------------------------
395 virtual wxDataFormat
GetPreferredFormat(Direction
WXUNUSED(dir
)) const
397 return m_formatShape
;
400 virtual size_t GetFormatCount(Direction dir
) const
402 // our custom format is supported by both GetData() and SetData()
406 // but the bitmap format(s) are only supported for output
407 nFormats
+= m_dobjBitmap
.GetFormatCount(dir
);
410 nFormats
+= m_dobjMetaFile
.GetFormatCount(dir
);
417 virtual void GetAllFormats(wxDataFormat
*formats
, Direction dir
) const
419 formats
[0] = m_formatShape
;
422 // in Get direction we additionally support bitmaps and metafiles
424 m_dobjBitmap
.GetAllFormats(&formats
[1], dir
);
427 // don't assume that m_dobjBitmap has only 1 format
428 m_dobjMetaFile
.GetAllFormats(&formats
[1 +
429 m_dobjBitmap
.GetFormatCount(dir
)], dir
);
434 virtual size_t GetDataSize(const wxDataFormat
& format
) const
436 if ( format
== m_formatShape
)
438 return m_shape
->GetDataSize();
441 else if ( format
== wxDF_METAFILE
)
443 if ( !m_hasMetaFile
)
446 return m_dobjMetaFile
.GetDataSize();
454 return m_dobjBitmap
.GetDataSize();
458 virtual bool GetDataHere(const wxDataFormat
& format
, void *pBuf
) const
460 if ( format
== m_formatShape
)
462 m_shape
->GetDataHere(pBuf
);
467 else if ( format
== wxDF_METAFILE
)
469 if ( !m_hasMetaFile
)
472 return m_dobjMetaFile
.GetDataHere(pBuf
);
480 return m_dobjBitmap
.GetDataHere(pBuf
);
484 virtual bool SetData(const wxDataFormat
& format
,
485 size_t len
, const void *buf
)
487 wxCHECK_MSG( format
== m_formatShape
, FALSE
, "unsupported format" );
490 m_shape
= DnDShape::New(buf
);
492 // the shape has changed
496 m_hasMetaFile
= FALSE
;
503 // creates a bitmap and assigns it to m_dobjBitmap (also sets m_hasBitmap)
504 void CreateBitmap() const;
506 void CreateMetaFile() const;
509 wxDataFormat m_formatShape
; // our custom format
511 wxBitmapDataObject m_dobjBitmap
; // it handles bitmaps
512 bool m_hasBitmap
; // true if m_dobjBitmap has valid bitmap
515 wxMetaFileDataObject m_dobjMetaFile
;// handles metafiles
516 bool m_hasMetaFile
; // true if we have valid metafile
519 DnDShape
*m_shape
; // our data
522 // ----------------------------------------------------------------------------
523 // A dialog to edit shape properties
524 // ----------------------------------------------------------------------------
526 class DnDShapeDialog
: public wxDialog
529 DnDShapeDialog(wxFrame
*parent
, DnDShape
*shape
);
531 DnDShape
*GetShape() const;
533 virtual bool TransferDataToWindow();
534 virtual bool TransferDataFromWindow();
536 void OnColour(wxCommandEvent
& event
);
543 DnDShape::Kind m_shapeKind
;
555 DECLARE_EVENT_TABLE()
558 // ----------------------------------------------------------------------------
559 // A frame for the shapes which can be drag-and-dropped between frames
560 // ----------------------------------------------------------------------------
562 class DnDShapeFrame
: public wxFrame
565 DnDShapeFrame(wxFrame
*parent
);
568 void SetShape(DnDShape
*shape
);
571 void OnNewShape(wxCommandEvent
& event
);
572 void OnEditShape(wxCommandEvent
& event
);
573 void OnClearShape(wxCommandEvent
& event
);
575 void OnCopyShape(wxCommandEvent
& event
);
576 void OnPasteShape(wxCommandEvent
& event
);
578 void OnUpdateUICopy(wxUpdateUIEvent
& event
);
579 void OnUpdateUIPaste(wxUpdateUIEvent
& event
);
581 void OnDrag(wxMouseEvent
& event
);
582 void OnPaint(wxPaintEvent
& event
);
583 void OnDrop(wxCoord x
, wxCoord y
, DnDShape
*shape
);
588 static DnDShapeFrame
*ms_lastDropTarget
;
590 DECLARE_EVENT_TABLE()
593 // ----------------------------------------------------------------------------
594 // wxDropTarget derivation for DnDShapes
595 // ----------------------------------------------------------------------------
597 class DnDShapeDropTarget
: public wxDropTarget
600 DnDShapeDropTarget(DnDShapeFrame
*frame
)
601 : wxDropTarget(new DnDShapeDataObject
)
606 // override base class (pure) virtuals
607 virtual wxDragResult
OnEnter(wxCoord x
, wxCoord y
, wxDragResult def
)
608 { m_frame
->SetStatusText("Mouse entered the frame"); return OnDragOver(x
, y
, def
); }
609 virtual void OnLeave()
610 { m_frame
->SetStatusText("Mouse left the frame"); }
611 virtual wxDragResult
OnData(wxCoord x
, wxCoord y
, wxDragResult def
)
615 wxLogError("Failed to get drag and drop data");
620 m_frame
->OnDrop(x
, y
,
621 ((DnDShapeDataObject
*)GetDataObject())->GetShape());
627 DnDShapeFrame
*m_frame
;
630 // ----------------------------------------------------------------------------
631 // IDs for the menu commands
632 // ----------------------------------------------------------------------------
648 Menu_Shape_New
= 500,
651 Menu_ShapeClipboard_Copy
,
652 Menu_ShapeClipboard_Paste
,
656 BEGIN_EVENT_TABLE(DnDFrame
, wxFrame
)
657 EVT_MENU(Menu_Quit
, DnDFrame::OnQuit
)
658 EVT_MENU(Menu_About
, DnDFrame::OnAbout
)
659 EVT_MENU(Menu_Drag
, DnDFrame::OnDrag
)
660 EVT_MENU(Menu_NewFrame
, DnDFrame::OnNewFrame
)
661 EVT_MENU(Menu_Help
, DnDFrame::OnHelp
)
662 EVT_MENU(Menu_Clear
, DnDFrame::OnLogClear
)
663 EVT_MENU(Menu_Copy
, DnDFrame::OnCopy
)
664 EVT_MENU(Menu_Paste
, DnDFrame::OnPaste
)
665 EVT_MENU(Menu_CopyBitmap
, DnDFrame::OnCopyBitmap
)
666 EVT_MENU(Menu_PasteBitmap
,DnDFrame::OnPasteBitmap
)
668 EVT_MENU(Menu_PasteMFile
, DnDFrame::OnPasteMetafile
)
669 #endif // USE_METAFILES
670 EVT_MENU(Menu_CopyFiles
, DnDFrame::OnCopyFiles
)
672 EVT_UPDATE_UI(Menu_Paste
, DnDFrame::OnUpdateUIPasteText
)
673 EVT_UPDATE_UI(Menu_PasteBitmap
, DnDFrame::OnUpdateUIPasteBitmap
)
675 EVT_LEFT_DOWN( DnDFrame::OnLeftDown
)
676 EVT_RIGHT_DOWN( DnDFrame::OnRightDown
)
677 EVT_PAINT( DnDFrame::OnPaint
)
680 BEGIN_EVENT_TABLE(DnDShapeFrame
, wxFrame
)
681 EVT_MENU(Menu_Shape_New
, DnDShapeFrame::OnNewShape
)
682 EVT_MENU(Menu_Shape_Edit
, DnDShapeFrame::OnEditShape
)
683 EVT_MENU(Menu_Shape_Clear
, DnDShapeFrame::OnClearShape
)
685 EVT_MENU(Menu_ShapeClipboard_Copy
, DnDShapeFrame::OnCopyShape
)
686 EVT_MENU(Menu_ShapeClipboard_Paste
, DnDShapeFrame::OnPasteShape
)
688 EVT_UPDATE_UI(Menu_ShapeClipboard_Copy
, DnDShapeFrame::OnUpdateUICopy
)
689 EVT_UPDATE_UI(Menu_ShapeClipboard_Paste
, DnDShapeFrame::OnUpdateUIPaste
)
691 EVT_LEFT_DOWN(DnDShapeFrame::OnDrag
)
693 EVT_PAINT(DnDShapeFrame::OnPaint
)
696 BEGIN_EVENT_TABLE(DnDShapeDialog
, wxDialog
)
697 EVT_BUTTON(Button_Colour
, DnDShapeDialog::OnColour
)
700 // ============================================================================
702 // ============================================================================
704 // `Main program' equivalent, creating windows and returning main app frame
705 bool DnDApp::OnInit()
707 // load our ressources
711 pathList
.Add("./Debug");
712 pathList
.Add("./Release");
715 wxString path
= pathList
.FindValidPath("dnd.wxr");
718 wxLogError("Can't find the resource file dnd.wxr in the current "
719 "directory, aborting.");
724 wxDefaultResourceTable
->ParseResourceFile(path
);
726 // switch on trace messages
727 #if defined(__WXGTK__)
728 wxLog::AddTraceMask(_T("clipboard"));
729 #elif defined(__WXMSW__)
730 wxLog::AddTraceMask(wxTRACE_OleCalls
);
734 wxImage::AddHandler( new wxPNGHandler
);
737 // under X we usually want to use the primary selection by default (which
738 // is shared with other apps)
739 wxTheClipboard
->UsePrimarySelection();
741 // create the main frame window
742 DnDFrame
*frame
= new DnDFrame((wxFrame
*) NULL
,
743 "Drag-and-Drop/Clipboard wxWindows Sample",
754 DnDFrame::DnDFrame(wxFrame
*frame
, char *title
, int x
, int y
, int w
, int h
)
755 : wxFrame(frame
, -1, title
, wxPoint(x
, y
), wxSize(w
, h
)),
756 m_strText("wxWindows drag & drop works :-)")
759 // frame icon and status bar
760 SetIcon(wxICON(mondrian
));
765 wxMenu
*file_menu
= new wxMenu
;
766 file_menu
->Append(Menu_Drag
, "&Test drag...");
767 file_menu
->AppendSeparator();
768 file_menu
->Append(Menu_NewFrame
, "&New frame\tCtrl-N");
769 file_menu
->AppendSeparator();
770 file_menu
->Append(Menu_Quit
, "E&xit");
772 wxMenu
*log_menu
= new wxMenu
;
773 log_menu
->Append(Menu_Clear
, "Clear\tCtrl-L");
775 wxMenu
*help_menu
= new wxMenu
;
776 help_menu
->Append(Menu_Help
, "&Help...");
777 help_menu
->AppendSeparator();
778 help_menu
->Append(Menu_About
, "&About");
780 wxMenu
*clip_menu
= new wxMenu
;
781 clip_menu
->Append(Menu_Copy
, "&Copy text\tCtrl+C");
782 clip_menu
->Append(Menu_Paste
, "&Paste text\tCtrl+V");
783 clip_menu
->AppendSeparator();
784 clip_menu
->Append(Menu_CopyBitmap
, "Copy &bitmap\tAlt+C");
785 clip_menu
->Append(Menu_PasteBitmap
, "Paste b&itmap\tAlt+V");
787 clip_menu
->AppendSeparator();
788 clip_menu
->Append(Menu_PasteMFile
, "Paste &metafile\tCtrl-M");
789 #endif // USE_METAFILES
790 clip_menu
->AppendSeparator();
791 clip_menu
->Append(Menu_CopyFiles
, "Copy &files\tCtrl+F");
793 wxMenuBar
*menu_bar
= new wxMenuBar
;
794 menu_bar
->Append(file_menu
, "&File");
795 menu_bar
->Append(log_menu
, "&Log");
796 menu_bar
->Append(clip_menu
, "&Clipboard");
797 menu_bar
->Append(help_menu
, "&Help");
799 SetMenuBar(menu_bar
);
801 // make a panel with 3 subwindows
803 wxSize
size(400, 200);
805 wxString
strFile("Drop files here!"), strText("Drop text on me");
807 m_ctrlFile
= new wxListBox(this, -1, pos
, size
, 1, &strFile
,
808 wxLB_HSCROLL
| wxLB_ALWAYS_SB
);
809 m_ctrlText
= new wxListBox(this, -1, pos
, size
, 1, &strText
,
810 wxLB_HSCROLL
| wxLB_ALWAYS_SB
);
812 m_ctrlLog
= new wxTextCtrl(this, -1, "", pos
, size
,
813 wxTE_MULTILINE
| wxTE_READONLY
|
816 // redirect log messages to the text window
817 m_pLog
= new wxLogTextCtrl(m_ctrlLog
);
818 m_pLogPrev
= wxLog::SetActiveTarget(m_pLog
);
820 // associate drop targets with 2 text controls
821 m_ctrlFile
->SetDropTarget(new DnDFile(m_ctrlFile
));
822 m_ctrlText
->SetDropTarget(new DnDText(m_ctrlText
));
824 wxLayoutConstraints
*c
;
827 c
= new wxLayoutConstraints
;
828 c
->left
.SameAs(this, wxLeft
);
829 c
->top
.SameAs(this, wxTop
);
830 c
->right
.PercentOf(this, wxRight
, 50);
831 c
->height
.PercentOf(this, wxHeight
, 30);
832 m_ctrlFile
->SetConstraints(c
);
835 c
= new wxLayoutConstraints
;
836 c
->left
.SameAs (m_ctrlFile
, wxRight
);
837 c
->top
.SameAs (this, wxTop
);
838 c
->right
.SameAs (this, wxRight
);
839 c
->height
.PercentOf(this, wxHeight
, 30);
840 m_ctrlText
->SetConstraints(c
);
842 // Lower text control
843 c
= new wxLayoutConstraints
;
844 c
->left
.SameAs (this, wxLeft
);
845 c
->right
.SameAs (this, wxRight
);
846 c
->height
.PercentOf(this, wxHeight
, 50);
847 c
->top
.SameAs(m_ctrlText
, wxBottom
);
848 m_ctrlLog
->SetConstraints(c
);
853 void DnDFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
858 void DnDFrame::OnPaint(wxPaintEvent
& WXUNUSED(event
))
862 GetClientSize( &w
, &h
);
865 dc
.SetFont( wxFont( 24, wxDECORATIVE
, wxNORMAL
, wxNORMAL
, FALSE
, "charter" ) );
866 dc
.DrawText( "Drag text from here!", 20, h
-50 );
870 // 4/5 is 80% taken by other windows, 20 is arbitrary margin
871 dc
.DrawBitmap(m_bitmap
,
872 w
- m_bitmap
.GetWidth() - 20,
877 void DnDFrame::OnUpdateUIPasteText(wxUpdateUIEvent
& event
)
880 // too many trace messages if we don't do it - this function is called
885 event
.Enable( wxTheClipboard
->IsSupported(wxDF_TEXT
) );
888 void DnDFrame::OnUpdateUIPasteBitmap(wxUpdateUIEvent
& event
)
891 // too many trace messages if we don't do it - this function is called
896 event
.Enable( wxTheClipboard
->IsSupported(wxDF_BITMAP
) );
899 void DnDFrame::OnNewFrame(wxCommandEvent
& WXUNUSED(event
))
901 (new DnDShapeFrame(this))->Show(TRUE
);
903 wxLogStatus(this, "Double click the new frame to select a shape for it");
906 void DnDFrame::OnDrag(wxCommandEvent
& WXUNUSED(event
))
908 wxString strText
= wxGetTextFromUser
910 "After you enter text in this dialog, press any mouse\n"
911 "button in the bottom (empty) part of the frame and \n"
912 "drag it anywhere - you will be in fact dragging the\n"
913 "text object containing this text",
914 "Please enter some text", m_strText
, this
920 void DnDFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
922 wxMessageBox("Drag-&-Drop Demo\n"
923 "Please see \"Help|Help...\" for details\n"
924 "Copyright (c) 1998 Vadim Zeitlin",
926 wxICON_INFORMATION
| wxOK
,
930 void DnDFrame::OnHelp(wxCommandEvent
& /* event */)
932 wxMessageDialog
dialog(this,
933 "This small program demonstrates drag & drop support in wxWindows. The program window\n"
934 "consists of 3 parts: the bottom pane is for debug messages, so that you can see what's\n"
935 "going on inside. The top part is split into 2 listboxes, the left one accepts files\n"
936 "and the right one accepts text.\n"
938 "To test wxDropTarget: open wordpad (write.exe), select some text in it and drag it to\n"
939 "the right listbox (you'll notice the usual visual feedback, i.e. the cursor will change).\n"
940 "Also, try dragging some files (you can select several at once) from Windows Explorer (or \n"
941 "File Manager) to the left pane. Hold down Ctrl/Shift keys when you drop text (doesn't \n"
942 "work with files) and see what changes.\n"
944 "To test wxDropSource: just press any mouse button on the empty zone of the window and drag\n"
945 "it to wordpad or any other droptarget accepting text (and of course you can just drag it\n"
946 "to the right pane). Due to a lot of trace messages, the cursor might take some time to \n"
947 "change, don't release the mouse button until it does. You can change the string being\n"
948 "dragged in in \"File|Test drag...\" dialog.\n"
951 "Please send all questions/bug reports/suggestions &c to \n"
952 "Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>",
958 void DnDFrame::OnLogClear(wxCommandEvent
& /* event */ )
965 void DnDFrame::OnLeftDown(wxMouseEvent
&WXUNUSED(event
) )
967 if ( !m_strText
.IsEmpty() )
969 // start drag operation
970 wxTextDataObject
textData(m_strText
);
972 wxFileDataObject textData;
973 textData.AddFile( "/file1.txt" );
974 textData.AddFile( "/file2.txt" );
976 wxDropSource
source(textData
, this
979 ,wxCURSOR_PENCIL
, // for copy
980 wxCURSOR_SPRAYCAN
, // for move
981 wxCURSOR_QUESTION_ARROW
// for nothing
987 switch ( source
.DoDragDrop(TRUE
) )
989 case wxDragError
: pc
= "Error!"; break;
990 case wxDragNone
: pc
= "Nothing"; break;
991 case wxDragCopy
: pc
= "Copied"; break;
992 case wxDragMove
: pc
= "Moved"; break;
993 case wxDragCancel
: pc
= "Cancelled"; break;
994 default: pc
= "Huh?"; break;
997 SetStatusText(wxString("Drag result: ") + pc
);
1001 void DnDFrame::OnRightDown(wxMouseEvent
&event
)
1003 wxMenu
menu("Dnd sample menu");
1005 menu
.Append(Menu_Drag
, "&Test drag...");
1006 menu
.AppendSeparator();
1007 menu
.Append(Menu_About
, "&About");
1009 PopupMenu( &menu
, event
.GetX(), event
.GetY() );
1012 DnDFrame::~DnDFrame()
1014 if ( m_pLog
!= NULL
) {
1015 if ( wxLog::SetActiveTarget(m_pLogPrev
) == m_pLog
)
1020 // ---------------------------------------------------------------------------
1022 // ---------------------------------------------------------------------------
1024 void DnDFrame::OnCopyBitmap(wxCommandEvent
& WXUNUSED(event
))
1026 // PNG support is not always compiled in under Windows, so use BMP there
1028 wxFileDialog
dialog(this, "Open a BMP file", "", "", "BMP files (*.bmp)|*.bmp", 0);
1030 wxFileDialog
dialog(this, "Open a PNG file", "", "", "PNG files (*.png)|*.png", 0);
1033 if (dialog
.ShowModal() != wxID_OK
)
1035 wxLogMessage( _T("Aborted file open") );
1039 if (dialog
.GetPath().IsEmpty())
1041 wxLogMessage( _T("Returned empty string.") );
1045 if (!wxFileExists(dialog
.GetPath()))
1047 wxLogMessage( _T("File doesn't exist.") );
1052 image
.LoadFile( dialog
.GetPath(),
1061 wxLogError( _T("Invalid image file...") );
1065 wxLogStatus( _T("Decoding image file...") );
1068 wxBitmap
bitmap( image
.ConvertToBitmap() );
1070 if ( !wxTheClipboard
->Open() )
1072 wxLogError(_T("Can't open clipboard."));
1077 wxLogMessage( _T("Creating wxBitmapDataObject...") );
1080 if ( !wxTheClipboard
->AddData(new wxBitmapDataObject(bitmap
)) )
1082 wxLogError(_T("Can't copy image to the clipboard."));
1086 wxLogMessage(_T("Image has been put on the clipboard.") );
1087 wxLogMessage(_T("You can paste it now and look at it.") );
1090 wxTheClipboard
->Close();
1093 void DnDFrame::OnPasteBitmap(wxCommandEvent
& WXUNUSED(event
))
1095 if ( !wxTheClipboard
->Open() )
1097 wxLogError(_T("Can't open clipboard."));
1102 if ( !wxTheClipboard
->IsSupported(wxDF_BITMAP
) )
1104 wxLogWarning(_T("No bitmap on clipboard"));
1106 wxTheClipboard
->Close();
1110 wxBitmapDataObject data
;
1111 if ( !wxTheClipboard
->GetData(data
) )
1113 wxLogError(_T("Can't paste bitmap from the clipboard"));
1117 wxLogMessage(_T("Bitmap pasted from the clipboard") );
1118 m_bitmap
= data
.GetBitmap();
1122 wxTheClipboard
->Close();
1125 #ifdef USE_METAFILES
1127 void DnDFrame::OnPasteMetafile(wxCommandEvent
& WXUNUSED(event
))
1129 if ( !wxTheClipboard
->Open() )
1131 wxLogError(_T("Can't open clipboard."));
1136 if ( !wxTheClipboard
->IsSupported(wxDF_METAFILE
) )
1138 wxLogWarning(_T("No metafile on clipboard"));
1142 wxMetaFileDataObject data
;
1143 if ( !wxTheClipboard
->GetData(data
) )
1145 wxLogError(_T("Can't paste metafile from the clipboard"));
1149 wxLogMessage(_T("Metafile pasted from the clipboard"));
1151 // TODO: show it somewhere
1155 wxTheClipboard
->Close();
1158 #endif // USE_METAFILES
1160 // ----------------------------------------------------------------------------
1162 // ----------------------------------------------------------------------------
1164 void DnDFrame::OnCopyFiles(wxCommandEvent
& WXUNUSED(event
))
1167 wxFileDialog
dialog(this, "Select a file to copy", "", "",
1168 "All files (*.*)|*.*", 0);
1170 wxArrayString filenames
;
1171 while ( dialog
.ShowModal() == wxID_OK
)
1173 filenames
.Add(dialog
.GetPath());
1176 if ( !filenames
.IsEmpty() )
1178 wxFileDataObject
*dobj
= new wxFileDataObject
;
1179 size_t count
= filenames
.GetCount();
1180 for ( size_t n
= 0; n
< count
; n
++ )
1182 dobj
->AddFile(filenames
[n
]);
1185 wxClipboardLocker locker
;
1188 wxLogError("Can't open clipboard");
1192 if ( !wxTheClipboard
->AddData(dobj
) )
1194 wxLogError("Can't copy file(s) to the clipboard");
1198 wxLogStatus(this, "%d file%s copied to the clipboard",
1199 count
, count
== 1 ? "" : "s");
1205 wxLogStatus(this, "Aborted");
1208 wxLogError("Sorry, not implemented");
1212 // ---------------------------------------------------------------------------
1214 // ---------------------------------------------------------------------------
1216 void DnDFrame::OnCopy(wxCommandEvent
& WXUNUSED(event
))
1218 if ( !wxTheClipboard
->Open() )
1220 wxLogError(_T("Can't open clipboard."));
1225 if ( !wxTheClipboard
->AddData(new wxTextDataObject(m_strText
)) )
1227 wxLogError(_T("Can't copy data to the clipboard"));
1231 wxLogMessage(_T("Text '%s' put on the clipboard"), m_strText
.c_str());
1234 wxTheClipboard
->Close();
1237 void DnDFrame::OnPaste(wxCommandEvent
& WXUNUSED(event
))
1239 if ( !wxTheClipboard
->Open() )
1241 wxLogError(_T("Can't open clipboard."));
1246 if ( !wxTheClipboard
->IsSupported(wxDF_TEXT
) )
1248 wxLogWarning(_T("No text data on clipboard"));
1250 wxTheClipboard
->Close();
1254 wxTextDataObject text
;
1255 if ( !wxTheClipboard
->GetData(text
) )
1257 wxLogError(_T("Can't paste data from the clipboard"));
1261 wxLogMessage(_T("Text '%s' pasted from the clipboard"),
1262 text
.GetText().c_str());
1265 wxTheClipboard
->Close();
1268 // ----------------------------------------------------------------------------
1269 // Notifications called by the base class
1270 // ----------------------------------------------------------------------------
1272 bool DnDText::OnDropText(wxCoord
, wxCoord
, const wxString
& text
)
1274 m_pOwner
->Append(text
);
1279 bool DnDFile::OnDropFiles(wxCoord
, wxCoord
, const wxArrayString
& filenames
)
1281 size_t nFiles
= filenames
.GetCount();
1283 str
.Printf( _T("%d files dropped"), nFiles
);
1284 m_pOwner
->Append(str
);
1285 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
1286 m_pOwner
->Append(filenames
[n
]);
1292 // ----------------------------------------------------------------------------
1294 // ----------------------------------------------------------------------------
1296 DnDShapeDialog::DnDShapeDialog(wxFrame
*parent
, DnDShape
*shape
)
1300 LoadFromResource(parent
, "dialogShape");
1302 m_textX
= (wxTextCtrl
*)wxFindWindowByName("textX", this);
1303 m_textY
= (wxTextCtrl
*)wxFindWindowByName("textY", this);
1304 m_textW
= (wxTextCtrl
*)wxFindWindowByName("textW", this);
1305 m_textH
= (wxTextCtrl
*)wxFindWindowByName("textH", this);
1307 m_radio
= (wxRadioBox
*)wxFindWindowByName("radio", this);
1310 DnDShape
*DnDShapeDialog::GetShape() const
1312 switch ( m_shapeKind
)
1315 case DnDShape::None
: return NULL
;
1316 case DnDShape::Triangle
: return new DnDTriangularShape(m_pos
, m_size
, m_col
);
1317 case DnDShape::Rectangle
: return new DnDRectangularShape(m_pos
, m_size
, m_col
);
1318 case DnDShape::Ellipse
: return new DnDEllipticShape(m_pos
, m_size
, m_col
);
1322 bool DnDShapeDialog::TransferDataToWindow()
1327 m_radio
->SetSelection(m_shape
->GetKind());
1328 m_pos
= m_shape
->GetPosition();
1329 m_size
= m_shape
->GetSize();
1330 m_col
= m_shape
->GetColour();
1334 m_radio
->SetSelection(DnDShape::None
);
1335 m_pos
= wxPoint(1, 1);
1336 m_size
= wxSize(100, 100);
1339 m_textX
->SetValue(wxString() << m_pos
.x
);
1340 m_textY
->SetValue(wxString() << m_pos
.y
);
1341 m_textW
->SetValue(wxString() << m_size
.x
);
1342 m_textH
->SetValue(wxString() << m_size
.y
);
1347 bool DnDShapeDialog::TransferDataFromWindow()
1349 m_shapeKind
= (DnDShape::Kind
)m_radio
->GetSelection();
1351 m_pos
.x
= atoi(m_textX
->GetValue());
1352 m_pos
.y
= atoi(m_textY
->GetValue());
1353 m_size
.x
= atoi(m_textW
->GetValue());
1354 m_size
.y
= atoi(m_textH
->GetValue());
1356 if ( !m_pos
.x
|| !m_pos
.y
|| !m_size
.x
|| !m_size
.y
)
1358 wxMessageBox("All sizes and positions should be non null!",
1359 "Invalid shape", wxICON_HAND
| wxOK
, this);
1367 void DnDShapeDialog::OnColour(wxCommandEvent
& WXUNUSED(event
))
1370 data
.SetChooseFull(TRUE
);
1371 for (int i
= 0; i
< 16; i
++)
1373 wxColour
colour(i
*16, i
*16, i
*16);
1374 data
.SetCustomColour(i
, colour
);
1377 wxColourDialog
dialog(this, &data
);
1378 if ( dialog
.ShowModal() == wxID_OK
)
1380 m_col
= dialog
.GetColourData().GetColour();
1384 // ----------------------------------------------------------------------------
1386 // ----------------------------------------------------------------------------
1388 DnDShapeFrame
*DnDShapeFrame::ms_lastDropTarget
= NULL
;
1390 DnDShapeFrame::DnDShapeFrame(wxFrame
*parent
)
1391 : wxFrame(parent
, -1, "Shape Frame",
1392 wxDefaultPosition
, wxSize(250, 150))
1396 wxMenu
*menuShape
= new wxMenu
;
1397 menuShape
->Append(Menu_Shape_New
, "&New default shape\tCtrl-S");
1398 menuShape
->Append(Menu_Shape_Edit
, "&Edit shape\tCtrl-E");
1399 menuShape
->AppendSeparator();
1400 menuShape
->Append(Menu_Shape_Clear
, "&Clear shape\tCtrl-L");
1402 wxMenu
*menuClipboard
= new wxMenu
;
1403 menuClipboard
->Append(Menu_ShapeClipboard_Copy
, "&Copy\tCtrl-C");
1404 menuClipboard
->Append(Menu_ShapeClipboard_Paste
, "&Paste\tCtrl-V");
1406 wxMenuBar
*menubar
= new wxMenuBar
;
1407 menubar
->Append(menuShape
, "&Shape");
1408 menubar
->Append(menuClipboard
, "&Clipboard");
1410 SetMenuBar(menubar
);
1412 SetStatusText("Press Ctrl-S to create a new shape");
1414 SetDropTarget(new DnDShapeDropTarget(this));
1418 SetBackgroundColour(*wxWHITE
);
1421 DnDShapeFrame::~DnDShapeFrame()
1427 void DnDShapeFrame::SetShape(DnDShape
*shape
)
1436 void DnDShapeFrame::OnDrag(wxMouseEvent
& event
)
1445 // start drag operation
1446 DnDShapeDataObject
shapeData(m_shape
);
1447 wxDropSource
source(shapeData
, this);
1449 const char *pc
= NULL
;
1450 switch ( source
.DoDragDrop(TRUE
) )
1454 wxLogError("An error occured during drag and drop operation");
1458 SetStatusText("Nothing happened");
1467 if ( ms_lastDropTarget
!= this )
1469 // don't delete the shape if we dropped it on ourselves!
1475 SetStatusText("Drag and drop operation cancelled");
1481 SetStatusText(wxString("Shape successfully ") + pc
);
1483 //else: status text already set
1486 void DnDShapeFrame::OnDrop(wxCoord x
, wxCoord y
, DnDShape
*shape
)
1488 ms_lastDropTarget
= this;
1493 s
.Printf("Shape dropped at (%ld, %ld)", pt
.x
, pt
.y
);
1500 void DnDShapeFrame::OnEditShape(wxCommandEvent
& event
)
1502 DnDShapeDialog
dlg(this, m_shape
);
1503 if ( dlg
.ShowModal() == wxID_OK
)
1505 SetShape(dlg
.GetShape());
1509 SetStatusText("You can now drag the shape to another frame");
1514 void DnDShapeFrame::OnNewShape(wxCommandEvent
& event
)
1516 SetShape(new DnDEllipticShape(wxPoint(10, 10), wxSize(80, 60), *wxRED
));
1518 SetStatusText("You can now drag the shape to another frame");
1521 void DnDShapeFrame::OnClearShape(wxCommandEvent
& event
)
1526 void DnDShapeFrame::OnCopyShape(wxCommandEvent
& event
)
1531 wxClipboardLocker clipLocker
;
1534 wxLogError("Can't open the clipboard");
1539 wxTheClipboard
->AddData(new DnDShapeDataObject(m_shape
));
1541 // VZ: temp test code, will remove
1546 m_shape
->Draw(dcMF
);
1548 wxMetafile
*mf
= dcMF
.Close();
1550 wxPoint pos
= m_shape
->GetPosition();
1551 wxSize size
= m_shape
->GetSize();
1552 wxSetClipboardData(wxDF_METAFILE
, mf
, pos
.x
+ size
.x
, pos
.y
+ size
.y
);
1559 void DnDShapeFrame::OnPasteShape(wxCommandEvent
& event
)
1561 wxClipboardLocker clipLocker
;
1564 wxLogError("Can't open the clipboard");
1569 DnDShapeDataObject
shapeDataObject(NULL
);
1570 if ( wxTheClipboard
->GetData(shapeDataObject
) )
1572 SetShape(shapeDataObject
.GetShape());
1576 wxLogStatus("No shape on the clipboard");
1580 void DnDShapeFrame::OnUpdateUICopy(wxUpdateUIEvent
& event
)
1582 event
.Enable( m_shape
!= NULL
);
1585 void DnDShapeFrame::OnUpdateUIPaste(wxUpdateUIEvent
& event
)
1587 event
.Enable( wxTheClipboard
->IsSupported(wxDataFormat(shapeFormatId
)) );
1590 void DnDShapeFrame::OnPaint(wxPaintEvent
& event
)
1604 // ----------------------------------------------------------------------------
1606 // ----------------------------------------------------------------------------
1608 DnDShape
*DnDShape::New(const void *buf
)
1610 const ShapeDump
& dump
= *(const ShapeDump
*)buf
;
1614 return new DnDTriangularShape(wxPoint(dump
.x
, dump
.y
),
1615 wxSize(dump
.w
, dump
.h
),
1616 wxColour(dump
.r
, dump
.g
, dump
.b
));
1619 return new DnDRectangularShape(wxPoint(dump
.x
, dump
.y
),
1620 wxSize(dump
.w
, dump
.h
),
1621 wxColour(dump
.r
, dump
.g
, dump
.b
));
1624 return new DnDEllipticShape(wxPoint(dump
.x
, dump
.y
),
1625 wxSize(dump
.w
, dump
.h
),
1626 wxColour(dump
.r
, dump
.g
, dump
.b
));
1629 wxFAIL_MSG("invalid shape!");
1634 // ----------------------------------------------------------------------------
1635 // DnDShapeDataObject
1636 // ----------------------------------------------------------------------------
1638 #ifdef USE_METAFILES
1640 void DnDShapeDataObject::CreateMetaFile() const
1644 m_shape
->Draw(dcMF
);
1646 wxMetafile
*mf
= dcMF
.Close();
1648 wxPoint pos
= m_shape
->GetPosition();
1649 wxSize size
= m_shape
->GetSize();
1650 mf
->SetWidth(pos
.x
+ size
.x
);
1651 mf
->SetHeight(pos
.y
+ size
.y
);
1653 DnDShapeDataObject
*self
= (DnDShapeDataObject
*)this; // const_cast
1654 self
->m_dobjMetaFile
.SetMetafile(*mf
);
1655 self
->m_hasMetaFile
= TRUE
;
1660 void DnDShapeDataObject::CreateBitmap() const
1662 wxPoint pos
= m_shape
->GetPosition();
1663 wxSize size
= m_shape
->GetSize();
1664 int x
= pos
.x
+ size
.x
,
1666 wxBitmap
bitmap(x
, y
);
1668 dc
.SelectObject(bitmap
);
1669 dc
.SetBrush(wxBrush("white", wxSOLID
));
1672 dc
.SelectObject(wxNullBitmap
);
1674 DnDShapeDataObject
*self
= (DnDShapeDataObject
*)this; // const_cast
1675 self
->m_dobjBitmap
.SetBitmap(bitmap
);
1676 self
->m_hasBitmap
= TRUE
;