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 canvas class to show a bitmap
93 // ----------------------------------------------------------------------------
95 class DnDCanvasBitmap
: public wxScrolledWindow
98 DnDCanvasBitmap(wxWindow
*parent
) : wxScrolledWindow(parent
) { }
100 void SetBitmap(const wxBitmap
& bitmap
)
104 SetScrollbars(10, 10,
105 m_bitmap
.GetWidth() / 10, m_bitmap
.GetHeight() / 10);
110 void OnPaint(wxPaintEvent
& event
)
118 dc
.DrawBitmap(m_bitmap
, 0, 0);
125 DECLARE_EVENT_TABLE()
130 // and the same thing fo metafiles
131 class DnDCanvasMetafile
: public wxScrolledWindow
134 DnDCanvasMetafile(wxWindow
*parent
) : wxScrolledWindow(parent
) { }
136 void SetMetafile(const wxMetafile
& metafile
)
138 m_metafile
= metafile
;
140 SetScrollbars(10, 10,
141 m_metafile
.GetWidth() / 10, m_metafile
.GetHeight() / 10);
146 void OnPaint(wxPaintEvent
& event
)
150 if ( m_metafile
.Ok() )
154 m_metafile
.Play(&dc
);
159 wxMetafile m_metafile
;
161 DECLARE_EVENT_TABLE()
164 #endif // USE_METAFILES
166 // ----------------------------------------------------------------------------
167 // Define a new frame type for the main frame
168 // ----------------------------------------------------------------------------
170 class DnDFrame
: public wxFrame
173 DnDFrame(wxFrame
*frame
, char *title
, int x
, int y
, int w
, int h
);
176 void OnPaint(wxPaintEvent
& event
);
177 void OnQuit (wxCommandEvent
& event
);
178 void OnAbout(wxCommandEvent
& event
);
179 void OnDrag (wxCommandEvent
& event
);
180 void OnNewFrame(wxCommandEvent
& event
);
181 void OnHelp (wxCommandEvent
& event
);
182 void OnLogClear(wxCommandEvent
& event
);
184 void OnCopy(wxCommandEvent
& event
);
185 void OnPaste(wxCommandEvent
& event
);
187 void OnCopyBitmap(wxCommandEvent
& event
);
188 void OnPasteBitmap(wxCommandEvent
& event
);
191 void OnPasteMetafile(wxCommandEvent
& event
);
192 #endif // USE_METAFILES
194 void OnCopyFiles(wxCommandEvent
& event
);
196 void OnLeftDown(wxMouseEvent
& event
);
197 void OnRightDown(wxMouseEvent
& event
);
199 void OnUpdateUIPasteText(wxUpdateUIEvent
& event
);
200 void OnUpdateUIPasteBitmap(wxUpdateUIEvent
& event
);
202 DECLARE_EVENT_TABLE()
205 wxListBox
*m_ctrlFile
,
207 wxTextCtrl
*m_ctrlLog
;
215 // ----------------------------------------------------------------------------
216 // A shape is an example of application-specific data which may be transported
217 // via drag-and-drop or clipboard: in our case, we have different geometric
218 // shapes, each one with its own colour and position
219 // ----------------------------------------------------------------------------
232 DnDShape(const wxPoint
& pos
,
235 : m_pos(pos
), m_size(size
), m_col(col
)
239 // this is for debugging - lets us see when exactly an object is freed
240 // (this may be later than you think if it's on the clipboard, for example)
241 virtual ~DnDShape() { }
243 // the functions used for drag-and-drop: they dump and restore a shape into
244 // some bitwise-copiable data (might use streams too...)
245 // ------------------------------------------------------------------------
247 // restore from buffer
248 static DnDShape
*New(const void *buf
);
250 virtual size_t GetDataSize() const
252 return sizeof(ShapeDump
);
255 virtual void GetDataHere(void *buf
) const
257 ShapeDump
& dump
= *(ShapeDump
*)buf
;
262 dump
.r
= m_col
.Red();
263 dump
.g
= m_col
.Green();
264 dump
.b
= m_col
.Blue();
269 const wxPoint
& GetPosition() const { return m_pos
; }
270 const wxColour
& GetColour() const { return m_col
; }
271 const wxSize
& GetSize() const { return m_size
; }
273 void Move(const wxPoint
& pos
) { m_pos
= pos
; }
275 // to implement in derived classes
276 virtual Kind
GetKind() const = 0;
278 virtual void Draw(wxDC
& dc
)
280 dc
.SetPen(wxPen(m_col
, 1, wxSOLID
));
284 wxPoint
GetCentre() const
285 { return wxPoint(m_pos
.x
+ m_size
.x
/ 2, m_pos
.y
+ m_size
.y
/ 2); }
289 int x
, y
, // position
300 class DnDTriangularShape
: public DnDShape
303 DnDTriangularShape(const wxPoint
& pos
,
306 : DnDShape(pos
, size
, col
)
308 wxLogMessage("DnDTriangularShape is being created");
311 virtual ~DnDTriangularShape()
313 wxLogMessage("DnDTriangularShape is being deleted");
316 virtual Kind
GetKind() const { return Triangle
; }
317 virtual void Draw(wxDC
& dc
)
321 // well, it's a bit difficult to describe a triangle by position and
322 // size, but we're not doing geometry here, do we? ;-)
324 wxPoint
p2(m_pos
.x
+ m_size
.x
, m_pos
.y
);
325 wxPoint
p3(m_pos
.x
, m_pos
.y
+ m_size
.y
);
332 dc
.FloodFill(GetCentre(), m_col
, wxFLOOD_BORDER
);
337 class DnDRectangularShape
: public DnDShape
340 DnDRectangularShape(const wxPoint
& pos
,
343 : DnDShape(pos
, size
, col
)
345 wxLogMessage("DnDRectangularShape is being created");
348 virtual ~DnDRectangularShape()
350 wxLogMessage("DnDRectangularShape is being deleted");
353 virtual Kind
GetKind() const { return Rectangle
; }
354 virtual void Draw(wxDC
& dc
)
359 wxPoint
p2(p1
.x
+ m_size
.x
, p1
.y
);
360 wxPoint
p3(p2
.x
, p2
.y
+ m_size
.y
);
361 wxPoint
p4(p1
.x
, p3
.y
);
369 dc
.FloodFill(GetCentre(), m_col
, wxFLOOD_BORDER
);
374 class DnDEllipticShape
: public DnDShape
377 DnDEllipticShape(const wxPoint
& pos
,
380 : DnDShape(pos
, size
, col
)
382 wxLogMessage("DnDEllipticShape is being created");
385 virtual ~DnDEllipticShape()
387 wxLogMessage("DnDEllipticShape is being deleted");
390 virtual Kind
GetKind() const { return Ellipse
; }
391 virtual void Draw(wxDC
& dc
)
395 dc
.DrawEllipse(m_pos
, m_size
);
398 dc
.FloodFill(GetCentre(), m_col
, wxFLOOD_BORDER
);
403 // ----------------------------------------------------------------------------
404 // A wxDataObject specialisation for the application-specific data
405 // ----------------------------------------------------------------------------
407 static const char *shapeFormatId
= "wxShape";
409 class DnDShapeDataObject
: public wxDataObject
412 // ctor doesn't copy the pointer, so it shouldn't go away while this object
414 DnDShapeDataObject(DnDShape
*shape
= (DnDShape
*)NULL
)
418 // we need to copy the shape because the one we're handled may be
419 // deleted while it's still on the clipboard (for example) - and we
420 // reuse the serialisation methods here to copy it
421 void *buf
= malloc(shape
->DnDShape::GetDataSize());
422 shape
->GetDataHere(buf
);
423 m_shape
= DnDShape::New(buf
);
433 // this string should uniquely identify our format, but is otherwise
435 m_formatShape
.SetId(shapeFormatId
);
437 // we don't draw the shape to a bitmap until it's really needed (i.e.
438 // we're asked to do so)
441 m_hasMetaFile
= FALSE
;
445 virtual ~DnDShapeDataObject() { delete m_shape
; }
447 // after a call to this function, the shape is owned by the caller and it
448 // is responsible for deleting it!
450 // NB: a better solution would be to make DnDShapes ref counted and this
451 // is what should probably be done in a real life program, otherwise
452 // the ownership problems become too complicated really fast
455 DnDShape
*shape
= m_shape
;
457 m_shape
= (DnDShape
*)NULL
;
460 m_hasMetaFile
= FALSE
;
466 // implement base class pure virtuals
467 // ----------------------------------
469 virtual wxDataFormat
GetPreferredFormat(Direction
WXUNUSED(dir
)) const
471 return m_formatShape
;
474 virtual size_t GetFormatCount(Direction dir
) const
476 // our custom format is supported by both GetData() and SetData()
480 // but the bitmap format(s) are only supported for output
481 nFormats
+= m_dobjBitmap
.GetFormatCount(dir
);
484 nFormats
+= m_dobjMetaFile
.GetFormatCount(dir
);
491 virtual void GetAllFormats(wxDataFormat
*formats
, Direction dir
) const
493 formats
[0] = m_formatShape
;
496 // in Get direction we additionally support bitmaps and metafiles
498 m_dobjBitmap
.GetAllFormats(&formats
[1], dir
);
501 // don't assume that m_dobjBitmap has only 1 format
502 m_dobjMetaFile
.GetAllFormats(&formats
[1 +
503 m_dobjBitmap
.GetFormatCount(dir
)], dir
);
508 virtual size_t GetDataSize(const wxDataFormat
& format
) const
510 if ( format
== m_formatShape
)
512 return m_shape
->GetDataSize();
515 else if ( m_dobjMetaFile
.IsSupported(format
) )
517 if ( !m_hasMetaFile
)
520 return m_dobjMetaFile
.GetDataSize(format
);
525 wxASSERT_MSG( m_dobjBitmap
.IsSupported(format
),
526 "unexpected format" );
531 return m_dobjBitmap
.GetDataSize();
535 virtual bool GetDataHere(const wxDataFormat
& format
, void *pBuf
) const
537 if ( format
== m_formatShape
)
539 m_shape
->GetDataHere(pBuf
);
544 else if ( m_dobjMetaFile
.IsSupported(format
) )
546 if ( !m_hasMetaFile
)
549 return m_dobjMetaFile
.GetDataHere(format
, pBuf
);
554 wxASSERT_MSG( m_dobjBitmap
.IsSupported(format
),
555 "unexpected format" );
560 return m_dobjBitmap
.GetDataHere(pBuf
);
564 virtual bool SetData(const wxDataFormat
& format
,
565 size_t len
, const void *buf
)
567 wxCHECK_MSG( format
== m_formatShape
, FALSE
, "unsupported format" );
570 m_shape
= DnDShape::New(buf
);
572 // the shape has changed
576 m_hasMetaFile
= FALSE
;
583 // creates a bitmap and assigns it to m_dobjBitmap (also sets m_hasBitmap)
584 void CreateBitmap() const;
586 void CreateMetaFile() const;
589 wxDataFormat m_formatShape
; // our custom format
591 wxBitmapDataObject m_dobjBitmap
; // it handles bitmaps
592 bool m_hasBitmap
; // true if m_dobjBitmap has valid bitmap
595 wxMetaFileDataObject m_dobjMetaFile
;// handles metafiles
596 bool m_hasMetaFile
; // true if we have valid metafile
599 DnDShape
*m_shape
; // our data
602 // ----------------------------------------------------------------------------
603 // A dialog to edit shape properties
604 // ----------------------------------------------------------------------------
606 class DnDShapeDialog
: public wxDialog
609 DnDShapeDialog(wxFrame
*parent
, DnDShape
*shape
);
611 DnDShape
*GetShape() const;
613 virtual bool TransferDataToWindow();
614 virtual bool TransferDataFromWindow();
616 void OnColour(wxCommandEvent
& event
);
623 DnDShape::Kind m_shapeKind
;
635 DECLARE_EVENT_TABLE()
638 // ----------------------------------------------------------------------------
639 // A frame for the shapes which can be drag-and-dropped between frames
640 // ----------------------------------------------------------------------------
642 class DnDShapeFrame
: public wxFrame
645 DnDShapeFrame(wxFrame
*parent
);
648 void SetShape(DnDShape
*shape
);
651 void OnNewShape(wxCommandEvent
& event
);
652 void OnEditShape(wxCommandEvent
& event
);
653 void OnClearShape(wxCommandEvent
& event
);
655 void OnCopyShape(wxCommandEvent
& event
);
656 void OnPasteShape(wxCommandEvent
& event
);
658 void OnUpdateUICopy(wxUpdateUIEvent
& event
);
659 void OnUpdateUIPaste(wxUpdateUIEvent
& event
);
661 void OnDrag(wxMouseEvent
& event
);
662 void OnPaint(wxPaintEvent
& event
);
663 void OnDrop(wxCoord x
, wxCoord y
, DnDShape
*shape
);
668 static DnDShapeFrame
*ms_lastDropTarget
;
670 DECLARE_EVENT_TABLE()
673 // ----------------------------------------------------------------------------
674 // wxDropTarget derivation for DnDShapes
675 // ----------------------------------------------------------------------------
677 class DnDShapeDropTarget
: public wxDropTarget
680 DnDShapeDropTarget(DnDShapeFrame
*frame
)
681 : wxDropTarget(new DnDShapeDataObject
)
686 // override base class (pure) virtuals
687 virtual wxDragResult
OnEnter(wxCoord x
, wxCoord y
, wxDragResult def
)
688 { m_frame
->SetStatusText("Mouse entered the frame"); return OnDragOver(x
, y
, def
); }
689 virtual void OnLeave()
690 { m_frame
->SetStatusText("Mouse left the frame"); }
691 virtual wxDragResult
OnData(wxCoord x
, wxCoord y
, wxDragResult def
)
695 wxLogError("Failed to get drag and drop data");
700 m_frame
->OnDrop(x
, y
,
701 ((DnDShapeDataObject
*)GetDataObject())->GetShape());
707 DnDShapeFrame
*m_frame
;
710 // ----------------------------------------------------------------------------
711 // functions prototypes
712 // ----------------------------------------------------------------------------
714 static void ShowBitmap(const wxBitmap
& bitmap
);
717 static void ShowMetaFile(const wxMetaFile
& metafile
);
718 #endif // USE_METAFILES
720 // ----------------------------------------------------------------------------
721 // IDs for the menu commands
722 // ----------------------------------------------------------------------------
738 Menu_Shape_New
= 500,
741 Menu_ShapeClipboard_Copy
,
742 Menu_ShapeClipboard_Paste
,
746 BEGIN_EVENT_TABLE(DnDFrame
, wxFrame
)
747 EVT_MENU(Menu_Quit
, DnDFrame::OnQuit
)
748 EVT_MENU(Menu_About
, DnDFrame::OnAbout
)
749 EVT_MENU(Menu_Drag
, DnDFrame::OnDrag
)
750 EVT_MENU(Menu_NewFrame
, DnDFrame::OnNewFrame
)
751 EVT_MENU(Menu_Help
, DnDFrame::OnHelp
)
752 EVT_MENU(Menu_Clear
, DnDFrame::OnLogClear
)
753 EVT_MENU(Menu_Copy
, DnDFrame::OnCopy
)
754 EVT_MENU(Menu_Paste
, DnDFrame::OnPaste
)
755 EVT_MENU(Menu_CopyBitmap
, DnDFrame::OnCopyBitmap
)
756 EVT_MENU(Menu_PasteBitmap
,DnDFrame::OnPasteBitmap
)
758 EVT_MENU(Menu_PasteMFile
, DnDFrame::OnPasteMetafile
)
759 #endif // USE_METAFILES
760 EVT_MENU(Menu_CopyFiles
, DnDFrame::OnCopyFiles
)
762 EVT_UPDATE_UI(Menu_Paste
, DnDFrame::OnUpdateUIPasteText
)
763 EVT_UPDATE_UI(Menu_PasteBitmap
, DnDFrame::OnUpdateUIPasteBitmap
)
765 EVT_LEFT_DOWN( DnDFrame::OnLeftDown
)
766 EVT_RIGHT_DOWN( DnDFrame::OnRightDown
)
767 EVT_PAINT( DnDFrame::OnPaint
)
770 BEGIN_EVENT_TABLE(DnDShapeFrame
, wxFrame
)
771 EVT_MENU(Menu_Shape_New
, DnDShapeFrame::OnNewShape
)
772 EVT_MENU(Menu_Shape_Edit
, DnDShapeFrame::OnEditShape
)
773 EVT_MENU(Menu_Shape_Clear
, DnDShapeFrame::OnClearShape
)
775 EVT_MENU(Menu_ShapeClipboard_Copy
, DnDShapeFrame::OnCopyShape
)
776 EVT_MENU(Menu_ShapeClipboard_Paste
, DnDShapeFrame::OnPasteShape
)
778 EVT_UPDATE_UI(Menu_ShapeClipboard_Copy
, DnDShapeFrame::OnUpdateUICopy
)
779 EVT_UPDATE_UI(Menu_ShapeClipboard_Paste
, DnDShapeFrame::OnUpdateUIPaste
)
781 EVT_LEFT_DOWN(DnDShapeFrame::OnDrag
)
783 EVT_PAINT(DnDShapeFrame::OnPaint
)
786 BEGIN_EVENT_TABLE(DnDShapeDialog
, wxDialog
)
787 EVT_BUTTON(Button_Colour
, DnDShapeDialog::OnColour
)
790 BEGIN_EVENT_TABLE(DnDCanvasBitmap
, wxScrolledWindow
)
791 EVT_PAINT(DnDCanvasBitmap::OnPaint
)
795 BEGIN_EVENT_TABLE(DnDCanvasMetafile
, wxScrolledWindow
)
796 EVT_PAINT(DnDCanvasMetafile::OnPaint
)
798 #endif // USE_METAFILES
800 // ============================================================================
802 // ============================================================================
804 // `Main program' equivalent, creating windows and returning main app frame
805 bool DnDApp::OnInit()
807 // load our ressources
811 pathList
.Add("./Debug");
812 pathList
.Add("./Release");
815 wxString path
= pathList
.FindValidPath("dnd.wxr");
818 wxLogError("Can't find the resource file dnd.wxr in the current "
819 "directory, aborting.");
824 wxDefaultResourceTable
->ParseResourceFile(path
);
826 // switch on trace messages
827 #if defined(__WXGTK__)
828 wxLog::AddTraceMask(_T("clipboard"));
829 #elif defined(__WXMSW__)
830 wxLog::AddTraceMask(wxTRACE_OleCalls
);
834 wxImage::AddHandler( new wxPNGHandler
);
837 // under X we usually want to use the primary selection by default (which
838 // is shared with other apps)
839 wxTheClipboard
->UsePrimarySelection();
841 // create the main frame window
842 DnDFrame
*frame
= new DnDFrame((wxFrame
*) NULL
,
843 "Drag-and-Drop/Clipboard wxWindows Sample",
854 DnDFrame::DnDFrame(wxFrame
*frame
, char *title
, int x
, int y
, int w
, int h
)
855 : wxFrame(frame
, -1, title
, wxPoint(x
, y
), wxSize(w
, h
)),
856 m_strText("wxWindows drag & drop works :-)")
859 // frame icon and status bar
860 SetIcon(wxICON(mondrian
));
865 wxMenu
*file_menu
= new wxMenu
;
866 file_menu
->Append(Menu_Drag
, "&Test drag...");
867 file_menu
->AppendSeparator();
868 file_menu
->Append(Menu_NewFrame
, "&New frame\tCtrl-N");
869 file_menu
->AppendSeparator();
870 file_menu
->Append(Menu_Quit
, "E&xit");
872 wxMenu
*log_menu
= new wxMenu
;
873 log_menu
->Append(Menu_Clear
, "Clear\tCtrl-L");
875 wxMenu
*help_menu
= new wxMenu
;
876 help_menu
->Append(Menu_Help
, "&Help...");
877 help_menu
->AppendSeparator();
878 help_menu
->Append(Menu_About
, "&About");
880 wxMenu
*clip_menu
= new wxMenu
;
881 clip_menu
->Append(Menu_Copy
, "&Copy text\tCtrl+C");
882 clip_menu
->Append(Menu_Paste
, "&Paste text\tCtrl+V");
883 clip_menu
->AppendSeparator();
884 clip_menu
->Append(Menu_CopyBitmap
, "Copy &bitmap\tAlt+C");
885 clip_menu
->Append(Menu_PasteBitmap
, "Paste b&itmap\tAlt+V");
887 clip_menu
->AppendSeparator();
888 clip_menu
->Append(Menu_PasteMFile
, "Paste &metafile\tCtrl-M");
889 #endif // USE_METAFILES
890 clip_menu
->AppendSeparator();
891 clip_menu
->Append(Menu_CopyFiles
, "Copy &files\tCtrl+F");
893 wxMenuBar
*menu_bar
= new wxMenuBar
;
894 menu_bar
->Append(file_menu
, "&File");
895 menu_bar
->Append(log_menu
, "&Log");
896 menu_bar
->Append(clip_menu
, "&Clipboard");
897 menu_bar
->Append(help_menu
, "&Help");
899 SetMenuBar(menu_bar
);
901 // make a panel with 3 subwindows
903 wxSize
size(400, 200);
905 wxString
strFile("Drop files here!"), strText("Drop text on me");
907 m_ctrlFile
= new wxListBox(this, -1, pos
, size
, 1, &strFile
,
908 wxLB_HSCROLL
| wxLB_ALWAYS_SB
);
909 m_ctrlText
= new wxListBox(this, -1, pos
, size
, 1, &strText
,
910 wxLB_HSCROLL
| wxLB_ALWAYS_SB
);
912 m_ctrlLog
= new wxTextCtrl(this, -1, "", pos
, size
,
913 wxTE_MULTILINE
| wxTE_READONLY
|
916 // redirect log messages to the text window
917 m_pLog
= new wxLogTextCtrl(m_ctrlLog
);
918 m_pLogPrev
= wxLog::SetActiveTarget(m_pLog
);
920 // associate drop targets with 2 text controls
921 m_ctrlFile
->SetDropTarget(new DnDFile(m_ctrlFile
));
922 m_ctrlText
->SetDropTarget(new DnDText(m_ctrlText
));
924 wxLayoutConstraints
*c
;
927 c
= new wxLayoutConstraints
;
928 c
->left
.SameAs(this, wxLeft
);
929 c
->top
.SameAs(this, wxTop
);
930 c
->right
.PercentOf(this, wxRight
, 50);
931 c
->height
.PercentOf(this, wxHeight
, 30);
932 m_ctrlFile
->SetConstraints(c
);
935 c
= new wxLayoutConstraints
;
936 c
->left
.SameAs (m_ctrlFile
, wxRight
);
937 c
->top
.SameAs (this, wxTop
);
938 c
->right
.SameAs (this, wxRight
);
939 c
->height
.PercentOf(this, wxHeight
, 30);
940 m_ctrlText
->SetConstraints(c
);
942 // Lower text control
943 c
= new wxLayoutConstraints
;
944 c
->left
.SameAs (this, wxLeft
);
945 c
->right
.SameAs (this, wxRight
);
946 c
->height
.PercentOf(this, wxHeight
, 50);
947 c
->top
.SameAs(m_ctrlText
, wxBottom
);
948 m_ctrlLog
->SetConstraints(c
);
953 void DnDFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
958 void DnDFrame::OnPaint(wxPaintEvent
& WXUNUSED(event
))
962 GetClientSize( &w
, &h
);
965 dc
.SetFont( wxFont( 24, wxDECORATIVE
, wxNORMAL
, wxNORMAL
, FALSE
, "charter" ) );
966 dc
.DrawText( "Drag text from here!", 100, h
-50 );
969 void DnDFrame::OnUpdateUIPasteText(wxUpdateUIEvent
& event
)
972 // too many trace messages if we don't do it - this function is called
977 event
.Enable( wxTheClipboard
->IsSupported(wxDF_TEXT
) );
980 void DnDFrame::OnUpdateUIPasteBitmap(wxUpdateUIEvent
& event
)
983 // too many trace messages if we don't do it - this function is called
988 event
.Enable( wxTheClipboard
->IsSupported(wxDF_BITMAP
) );
991 void DnDFrame::OnNewFrame(wxCommandEvent
& WXUNUSED(event
))
993 (new DnDShapeFrame(this))->Show(TRUE
);
995 wxLogStatus(this, "Double click the new frame to select a shape for it");
998 void DnDFrame::OnDrag(wxCommandEvent
& WXUNUSED(event
))
1000 wxString strText
= wxGetTextFromUser
1002 "After you enter text in this dialog, press any mouse\n"
1003 "button in the bottom (empty) part of the frame and \n"
1004 "drag it anywhere - you will be in fact dragging the\n"
1005 "text object containing this text",
1006 "Please enter some text", m_strText
, this
1009 m_strText
= strText
;
1012 void DnDFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
1014 wxMessageBox("Drag-&-Drop Demo\n"
1015 "Please see \"Help|Help...\" for details\n"
1016 "Copyright (c) 1998 Vadim Zeitlin",
1018 wxICON_INFORMATION
| wxOK
,
1022 void DnDFrame::OnHelp(wxCommandEvent
& /* event */)
1024 wxMessageDialog
dialog(this,
1025 "This small program demonstrates drag & drop support in wxWindows. The program window\n"
1026 "consists of 3 parts: the bottom pane is for debug messages, so that you can see what's\n"
1027 "going on inside. The top part is split into 2 listboxes, the left one accepts files\n"
1028 "and the right one accepts text.\n"
1030 "To test wxDropTarget: open wordpad (write.exe), select some text in it and drag it to\n"
1031 "the right listbox (you'll notice the usual visual feedback, i.e. the cursor will change).\n"
1032 "Also, try dragging some files (you can select several at once) from Windows Explorer (or \n"
1033 "File Manager) to the left pane. Hold down Ctrl/Shift keys when you drop text (doesn't \n"
1034 "work with files) and see what changes.\n"
1036 "To test wxDropSource: just press any mouse button on the empty zone of the window and drag\n"
1037 "it to wordpad or any other droptarget accepting text (and of course you can just drag it\n"
1038 "to the right pane). Due to a lot of trace messages, the cursor might take some time to \n"
1039 "change, don't release the mouse button until it does. You can change the string being\n"
1040 "dragged in in \"File|Test drag...\" dialog.\n"
1043 "Please send all questions/bug reports/suggestions &c to \n"
1044 "Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>",
1050 void DnDFrame::OnLogClear(wxCommandEvent
& /* event */ )
1053 m_ctrlText
->Clear();
1054 m_ctrlFile
->Clear();
1057 void DnDFrame::OnLeftDown(wxMouseEvent
&WXUNUSED(event
) )
1059 if ( !m_strText
.IsEmpty() )
1061 // start drag operation
1062 wxTextDataObject
textData(m_strText
);
1064 wxFileDataObject textData;
1065 textData.AddFile( "/file1.txt" );
1066 textData.AddFile( "/file2.txt" );
1068 wxDropSource
source(textData
, this
1071 ,wxCURSOR_PENCIL
, // for copy
1072 wxCURSOR_SPRAYCAN
, // for move
1073 wxCURSOR_QUESTION_ARROW
// for nothing
1079 switch ( source
.DoDragDrop(TRUE
) )
1081 case wxDragError
: pc
= "Error!"; break;
1082 case wxDragNone
: pc
= "Nothing"; break;
1083 case wxDragCopy
: pc
= "Copied"; break;
1084 case wxDragMove
: pc
= "Moved"; break;
1085 case wxDragCancel
: pc
= "Cancelled"; break;
1086 default: pc
= "Huh?"; break;
1089 SetStatusText(wxString("Drag result: ") + pc
);
1093 void DnDFrame::OnRightDown(wxMouseEvent
&event
)
1095 wxMenu
menu("Dnd sample menu");
1097 menu
.Append(Menu_Drag
, "&Test drag...");
1098 menu
.AppendSeparator();
1099 menu
.Append(Menu_About
, "&About");
1101 PopupMenu( &menu
, event
.GetX(), event
.GetY() );
1104 DnDFrame::~DnDFrame()
1106 if ( m_pLog
!= NULL
) {
1107 if ( wxLog::SetActiveTarget(m_pLogPrev
) == m_pLog
)
1112 // ---------------------------------------------------------------------------
1114 // ---------------------------------------------------------------------------
1116 void DnDFrame::OnCopyBitmap(wxCommandEvent
& WXUNUSED(event
))
1118 // PNG support is not always compiled in under Windows, so use BMP there
1120 wxFileDialog
dialog(this, "Open a BMP file", "", "", "BMP files (*.bmp)|*.bmp", 0);
1122 wxFileDialog
dialog(this, "Open a PNG file", "", "", "PNG files (*.png)|*.png", 0);
1125 if (dialog
.ShowModal() != wxID_OK
)
1127 wxLogMessage( _T("Aborted file open") );
1131 if (dialog
.GetPath().IsEmpty())
1133 wxLogMessage( _T("Returned empty string.") );
1137 if (!wxFileExists(dialog
.GetPath()))
1139 wxLogMessage( _T("File doesn't exist.") );
1144 image
.LoadFile( dialog
.GetPath(),
1153 wxLogError( _T("Invalid image file...") );
1157 wxLogStatus( _T("Decoding image file...") );
1160 wxBitmap
bitmap( image
.ConvertToBitmap() );
1162 if ( !wxTheClipboard
->Open() )
1164 wxLogError(_T("Can't open clipboard."));
1169 wxLogMessage( _T("Creating wxBitmapDataObject...") );
1172 if ( !wxTheClipboard
->AddData(new wxBitmapDataObject(bitmap
)) )
1174 wxLogError(_T("Can't copy image to the clipboard."));
1178 wxLogMessage(_T("Image has been put on the clipboard.") );
1179 wxLogMessage(_T("You can paste it now and look at it.") );
1182 wxTheClipboard
->Close();
1185 void DnDFrame::OnPasteBitmap(wxCommandEvent
& WXUNUSED(event
))
1187 if ( !wxTheClipboard
->Open() )
1189 wxLogError(_T("Can't open clipboard."));
1194 if ( !wxTheClipboard
->IsSupported(wxDF_BITMAP
) )
1196 wxLogWarning(_T("No bitmap on clipboard"));
1198 wxTheClipboard
->Close();
1202 wxBitmapDataObject data
;
1203 if ( !wxTheClipboard
->GetData(data
) )
1205 wxLogError(_T("Can't paste bitmap from the clipboard"));
1209 const wxBitmap
& bmp
= data
.GetBitmap();
1211 wxLogMessage(_T("Bitmap %dx%d pasted from the clipboard"),
1212 bmp
.GetWidth(), bmp
.GetHeight());
1216 wxTheClipboard
->Close();
1219 #ifdef USE_METAFILES
1221 void DnDFrame::OnPasteMetafile(wxCommandEvent
& WXUNUSED(event
))
1223 if ( !wxTheClipboard
->Open() )
1225 wxLogError(_T("Can't open clipboard."));
1230 if ( !wxTheClipboard
->IsSupported(wxDF_METAFILE
) )
1232 wxLogWarning(_T("No metafile on clipboard"));
1236 wxMetaFileDataObject data
;
1237 if ( !wxTheClipboard
->GetData(data
) )
1239 wxLogError(_T("Can't paste metafile from the clipboard"));
1243 const wxMetaFile
& mf
= data
.GetMetafile();
1245 wxLogMessage(_T("Metafile %dx%d pasted from the clipboard"),
1246 mf
.GetWidth(), mf
.GetHeight());
1252 wxTheClipboard
->Close();
1255 #endif // USE_METAFILES
1257 // ----------------------------------------------------------------------------
1259 // ----------------------------------------------------------------------------
1261 void DnDFrame::OnCopyFiles(wxCommandEvent
& WXUNUSED(event
))
1264 wxFileDialog
dialog(this, "Select a file to copy", "", "",
1265 "All files (*.*)|*.*", 0);
1267 wxArrayString filenames
;
1268 while ( dialog
.ShowModal() == wxID_OK
)
1270 filenames
.Add(dialog
.GetPath());
1273 if ( !filenames
.IsEmpty() )
1275 wxFileDataObject
*dobj
= new wxFileDataObject
;
1276 size_t count
= filenames
.GetCount();
1277 for ( size_t n
= 0; n
< count
; n
++ )
1279 dobj
->AddFile(filenames
[n
]);
1282 wxClipboardLocker locker
;
1285 wxLogError("Can't open clipboard");
1289 if ( !wxTheClipboard
->AddData(dobj
) )
1291 wxLogError("Can't copy file(s) to the clipboard");
1295 wxLogStatus(this, "%d file%s copied to the clipboard",
1296 count
, count
== 1 ? "" : "s");
1302 wxLogStatus(this, "Aborted");
1305 wxLogError("Sorry, not implemented");
1309 // ---------------------------------------------------------------------------
1311 // ---------------------------------------------------------------------------
1313 void DnDFrame::OnCopy(wxCommandEvent
& WXUNUSED(event
))
1315 if ( !wxTheClipboard
->Open() )
1317 wxLogError(_T("Can't open clipboard."));
1322 if ( !wxTheClipboard
->AddData(new wxTextDataObject(m_strText
)) )
1324 wxLogError(_T("Can't copy data to the clipboard"));
1328 wxLogMessage(_T("Text '%s' put on the clipboard"), m_strText
.c_str());
1331 wxTheClipboard
->Close();
1334 void DnDFrame::OnPaste(wxCommandEvent
& WXUNUSED(event
))
1336 if ( !wxTheClipboard
->Open() )
1338 wxLogError(_T("Can't open clipboard."));
1343 if ( !wxTheClipboard
->IsSupported(wxDF_TEXT
) )
1345 wxLogWarning(_T("No text data on clipboard"));
1347 wxTheClipboard
->Close();
1351 wxTextDataObject text
;
1352 if ( !wxTheClipboard
->GetData(text
) )
1354 wxLogError(_T("Can't paste data from the clipboard"));
1358 wxLogMessage(_T("Text '%s' pasted from the clipboard"),
1359 text
.GetText().c_str());
1362 wxTheClipboard
->Close();
1365 // ----------------------------------------------------------------------------
1366 // Notifications called by the base class
1367 // ----------------------------------------------------------------------------
1369 bool DnDText::OnDropText(wxCoord
, wxCoord
, const wxString
& text
)
1371 m_pOwner
->Append(text
);
1376 bool DnDFile::OnDropFiles(wxCoord
, wxCoord
, const wxArrayString
& filenames
)
1378 size_t nFiles
= filenames
.GetCount();
1380 str
.Printf( _T("%d files dropped"), nFiles
);
1381 m_pOwner
->Append(str
);
1382 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
1383 m_pOwner
->Append(filenames
[n
]);
1389 // ----------------------------------------------------------------------------
1391 // ----------------------------------------------------------------------------
1393 DnDShapeDialog::DnDShapeDialog(wxFrame
*parent
, DnDShape
*shape
)
1397 LoadFromResource(parent
, "dialogShape");
1399 m_textX
= (wxTextCtrl
*)wxFindWindowByName("textX", this);
1400 m_textY
= (wxTextCtrl
*)wxFindWindowByName("textY", this);
1401 m_textW
= (wxTextCtrl
*)wxFindWindowByName("textW", this);
1402 m_textH
= (wxTextCtrl
*)wxFindWindowByName("textH", this);
1404 m_radio
= (wxRadioBox
*)wxFindWindowByName("radio", this);
1407 DnDShape
*DnDShapeDialog::GetShape() const
1409 switch ( m_shapeKind
)
1412 case DnDShape::None
: return NULL
;
1413 case DnDShape::Triangle
: return new DnDTriangularShape(m_pos
, m_size
, m_col
);
1414 case DnDShape::Rectangle
: return new DnDRectangularShape(m_pos
, m_size
, m_col
);
1415 case DnDShape::Ellipse
: return new DnDEllipticShape(m_pos
, m_size
, m_col
);
1419 bool DnDShapeDialog::TransferDataToWindow()
1424 m_radio
->SetSelection(m_shape
->GetKind());
1425 m_pos
= m_shape
->GetPosition();
1426 m_size
= m_shape
->GetSize();
1427 m_col
= m_shape
->GetColour();
1431 m_radio
->SetSelection(DnDShape::None
);
1432 m_pos
= wxPoint(1, 1);
1433 m_size
= wxSize(100, 100);
1436 m_textX
->SetValue(wxString() << m_pos
.x
);
1437 m_textY
->SetValue(wxString() << m_pos
.y
);
1438 m_textW
->SetValue(wxString() << m_size
.x
);
1439 m_textH
->SetValue(wxString() << m_size
.y
);
1444 bool DnDShapeDialog::TransferDataFromWindow()
1446 m_shapeKind
= (DnDShape::Kind
)m_radio
->GetSelection();
1448 m_pos
.x
= atoi(m_textX
->GetValue());
1449 m_pos
.y
= atoi(m_textY
->GetValue());
1450 m_size
.x
= atoi(m_textW
->GetValue());
1451 m_size
.y
= atoi(m_textH
->GetValue());
1453 if ( !m_pos
.x
|| !m_pos
.y
|| !m_size
.x
|| !m_size
.y
)
1455 wxMessageBox("All sizes and positions should be non null!",
1456 "Invalid shape", wxICON_HAND
| wxOK
, this);
1464 void DnDShapeDialog::OnColour(wxCommandEvent
& WXUNUSED(event
))
1467 data
.SetChooseFull(TRUE
);
1468 for (int i
= 0; i
< 16; i
++)
1470 wxColour
colour(i
*16, i
*16, i
*16);
1471 data
.SetCustomColour(i
, colour
);
1474 wxColourDialog
dialog(this, &data
);
1475 if ( dialog
.ShowModal() == wxID_OK
)
1477 m_col
= dialog
.GetColourData().GetColour();
1481 // ----------------------------------------------------------------------------
1483 // ----------------------------------------------------------------------------
1485 DnDShapeFrame
*DnDShapeFrame::ms_lastDropTarget
= NULL
;
1487 DnDShapeFrame::DnDShapeFrame(wxFrame
*parent
)
1488 : wxFrame(parent
, -1, "Shape Frame",
1489 wxDefaultPosition
, wxSize(250, 150))
1493 wxMenu
*menuShape
= new wxMenu
;
1494 menuShape
->Append(Menu_Shape_New
, "&New default shape\tCtrl-S");
1495 menuShape
->Append(Menu_Shape_Edit
, "&Edit shape\tCtrl-E");
1496 menuShape
->AppendSeparator();
1497 menuShape
->Append(Menu_Shape_Clear
, "&Clear shape\tCtrl-L");
1499 wxMenu
*menuClipboard
= new wxMenu
;
1500 menuClipboard
->Append(Menu_ShapeClipboard_Copy
, "&Copy\tCtrl-C");
1501 menuClipboard
->Append(Menu_ShapeClipboard_Paste
, "&Paste\tCtrl-V");
1503 wxMenuBar
*menubar
= new wxMenuBar
;
1504 menubar
->Append(menuShape
, "&Shape");
1505 menubar
->Append(menuClipboard
, "&Clipboard");
1507 SetMenuBar(menubar
);
1509 SetStatusText("Press Ctrl-S to create a new shape");
1511 SetDropTarget(new DnDShapeDropTarget(this));
1515 SetBackgroundColour(*wxWHITE
);
1518 DnDShapeFrame::~DnDShapeFrame()
1524 void DnDShapeFrame::SetShape(DnDShape
*shape
)
1533 void DnDShapeFrame::OnDrag(wxMouseEvent
& event
)
1542 // start drag operation
1543 DnDShapeDataObject
shapeData(m_shape
);
1544 wxDropSource
source(shapeData
, this);
1546 const char *pc
= NULL
;
1547 switch ( source
.DoDragDrop(TRUE
) )
1551 wxLogError("An error occured during drag and drop operation");
1555 SetStatusText("Nothing happened");
1564 if ( ms_lastDropTarget
!= this )
1566 // don't delete the shape if we dropped it on ourselves!
1572 SetStatusText("Drag and drop operation cancelled");
1578 SetStatusText(wxString("Shape successfully ") + pc
);
1580 //else: status text already set
1583 void DnDShapeFrame::OnDrop(wxCoord x
, wxCoord y
, DnDShape
*shape
)
1585 ms_lastDropTarget
= this;
1590 s
.Printf("Shape dropped at (%ld, %ld)", pt
.x
, pt
.y
);
1597 void DnDShapeFrame::OnEditShape(wxCommandEvent
& event
)
1599 DnDShapeDialog
dlg(this, m_shape
);
1600 if ( dlg
.ShowModal() == wxID_OK
)
1602 SetShape(dlg
.GetShape());
1606 SetStatusText("You can now drag the shape to another frame");
1611 void DnDShapeFrame::OnNewShape(wxCommandEvent
& event
)
1613 SetShape(new DnDEllipticShape(wxPoint(10, 10), wxSize(80, 60), *wxRED
));
1615 SetStatusText("You can now drag the shape to another frame");
1618 void DnDShapeFrame::OnClearShape(wxCommandEvent
& event
)
1623 void DnDShapeFrame::OnCopyShape(wxCommandEvent
& event
)
1627 wxClipboardLocker clipLocker
;
1630 wxLogError("Can't open the clipboard");
1635 wxTheClipboard
->AddData(new DnDShapeDataObject(m_shape
));
1639 void DnDShapeFrame::OnPasteShape(wxCommandEvent
& event
)
1641 wxClipboardLocker clipLocker
;
1644 wxLogError("Can't open the clipboard");
1649 DnDShapeDataObject
shapeDataObject(NULL
);
1650 if ( wxTheClipboard
->GetData(shapeDataObject
) )
1652 SetShape(shapeDataObject
.GetShape());
1656 wxLogStatus("No shape on the clipboard");
1660 void DnDShapeFrame::OnUpdateUICopy(wxUpdateUIEvent
& event
)
1662 event
.Enable( m_shape
!= NULL
);
1665 void DnDShapeFrame::OnUpdateUIPaste(wxUpdateUIEvent
& event
)
1667 event
.Enable( wxTheClipboard
->IsSupported(wxDataFormat(shapeFormatId
)) );
1670 void DnDShapeFrame::OnPaint(wxPaintEvent
& event
)
1684 // ----------------------------------------------------------------------------
1686 // ----------------------------------------------------------------------------
1688 DnDShape
*DnDShape::New(const void *buf
)
1690 const ShapeDump
& dump
= *(const ShapeDump
*)buf
;
1694 return new DnDTriangularShape(wxPoint(dump
.x
, dump
.y
),
1695 wxSize(dump
.w
, dump
.h
),
1696 wxColour(dump
.r
, dump
.g
, dump
.b
));
1699 return new DnDRectangularShape(wxPoint(dump
.x
, dump
.y
),
1700 wxSize(dump
.w
, dump
.h
),
1701 wxColour(dump
.r
, dump
.g
, dump
.b
));
1704 return new DnDEllipticShape(wxPoint(dump
.x
, dump
.y
),
1705 wxSize(dump
.w
, dump
.h
),
1706 wxColour(dump
.r
, dump
.g
, dump
.b
));
1709 wxFAIL_MSG("invalid shape!");
1714 // ----------------------------------------------------------------------------
1715 // DnDShapeDataObject
1716 // ----------------------------------------------------------------------------
1718 #ifdef USE_METAFILES
1720 void DnDShapeDataObject::CreateMetaFile() const
1722 wxPoint pos
= m_shape
->GetPosition();
1723 wxSize size
= m_shape
->GetSize();
1725 wxMetaFileDC
dcMF(wxEmptyString
, pos
.x
+ size
.x
, pos
.y
+ size
.y
);
1727 m_shape
->Draw(dcMF
);
1729 wxMetafile
*mf
= dcMF
.Close();
1731 DnDShapeDataObject
*self
= (DnDShapeDataObject
*)this; // const_cast
1732 self
->m_dobjMetaFile
.SetMetafile(*mf
);
1733 self
->m_hasMetaFile
= TRUE
;
1740 void DnDShapeDataObject::CreateBitmap() const
1742 wxPoint pos
= m_shape
->GetPosition();
1743 wxSize size
= m_shape
->GetSize();
1744 int x
= pos
.x
+ size
.x
,
1746 wxBitmap
bitmap(x
, y
);
1748 dc
.SelectObject(bitmap
);
1749 dc
.SetBrush(wxBrush("white", wxSOLID
));
1752 dc
.SelectObject(wxNullBitmap
);
1754 DnDShapeDataObject
*self
= (DnDShapeDataObject
*)this; // const_cast
1755 self
->m_dobjBitmap
.SetBitmap(bitmap
);
1756 self
->m_hasBitmap
= TRUE
;
1759 // ----------------------------------------------------------------------------
1761 // ----------------------------------------------------------------------------
1763 static void ShowBitmap(const wxBitmap
& bitmap
)
1765 wxFrame
*frame
= new wxFrame(NULL
, -1, _T("Bitmap view"));
1766 frame
->CreateStatusBar();
1767 DnDCanvasBitmap
*canvas
= new DnDCanvasBitmap(frame
);
1768 canvas
->SetBitmap(bitmap
);
1770 int w
= bitmap
.GetWidth(),
1771 h
= bitmap
.GetHeight();
1772 frame
->SetStatusText(wxString::Format(_T("%dx%d"), w
, h
));
1774 frame
->SetClientSize(w
> 100 ? 100 : w
, h
> 100 ? 100 : h
);
1778 #ifdef USE_METAFILES
1780 static void ShowMetaFile(const wxMetaFile
& metafile
)
1782 wxFrame
*frame
= new wxFrame(NULL
, -1, _T("Metafile view"));
1783 frame
->CreateStatusBar();
1784 DnDCanvasMetafile
*canvas
= new DnDCanvasMetafile(frame
);
1785 canvas
->SetMetafile(metafile
);
1787 wxSize size
= metafile
.GetSize();
1788 frame
->SetStatusText(wxString::Format(_T("%dx%d"), size
.x
, size
.y
));
1790 frame
->SetClientSize(size
.x
> 100 ? 100 : size
.x
,
1791 size
.y
> 100 ? 100 : size
.y
);
1795 #endif // USE_METAFILES