Added wxCountingStream for measuring the size of streamed data
[wxWidgets.git] / samples / dnd / dnd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dnd.cpp
3 // Purpose: Drag and drop sample
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright:
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #ifndef WX_PRECOMP
19 #include "wx/wx.h"
20 #endif
21
22 #ifdef __WXMOTIF__
23 #error Sorry, drag and drop is not yet implemented on wxMotif.
24 #endif
25
26 #include "wx/intl.h"
27 #include "wx/log.h"
28
29 #include "wx/dnd.h"
30 #include "wx/dirdlg.h"
31 #include "wx/filedlg.h"
32 #include "wx/image.h"
33 #include "wx/clipbrd.h"
34
35 #if defined(__WXGTK__) || defined(__WXMOTIF__)
36 #include "mondrian.xpm"
37 #endif
38
39 // ----------------------------------------------------------------------------
40 // Derive two simple classes which just put in the listbox the strings (text or
41 // file names) we drop on them
42 // ----------------------------------------------------------------------------
43
44 typedef long wxDropPointCoord;
45
46 class DnDText : public wxTextDropTarget
47 {
48 public:
49 DnDText(wxListBox *pOwner) { m_pOwner = pOwner; }
50
51 virtual bool OnDropText(wxDropPointCoord x, wxDropPointCoord y,
52 const wxChar* psz);
53
54 private:
55 wxListBox *m_pOwner;
56 };
57
58 class DnDFile : public wxFileDropTarget
59 {
60 public:
61 DnDFile(wxListBox *pOwner) { m_pOwner = pOwner; }
62
63 virtual bool OnDropFiles(wxDropPointCoord x, wxDropPointCoord y,
64 size_t nFiles, const wxChar* const aszFiles[] );
65
66 private:
67 wxListBox *m_pOwner;
68 };
69
70 // ----------------------------------------------------------------------------
71 // Define a new application type
72 // ----------------------------------------------------------------------------
73
74 class DnDApp : public wxApp
75 {
76 public:
77 bool OnInit();
78 };
79
80 IMPLEMENT_APP(DnDApp);
81
82 // ----------------------------------------------------------------------------
83 // Define a new frame type
84 // ----------------------------------------------------------------------------
85 class DnDFrame : public wxFrame
86 {
87 public:
88 DnDFrame(wxFrame *frame, char *title, int x, int y, int w, int h);
89 ~DnDFrame();
90
91 void OnPaint(wxPaintEvent& event);
92 void OnQuit (wxCommandEvent& event);
93 void OnAbout(wxCommandEvent& event);
94 void OnDrag (wxCommandEvent& event);
95 void OnHelp (wxCommandEvent& event);
96 void OnLogClear(wxCommandEvent& event);
97 void OnCopy(wxCommandEvent& event);
98 void OnPaste(wxCommandEvent& event);
99 void OnCopyBitmap(wxCommandEvent& event);
100 void OnPasteBitmap(wxCommandEvent& event);
101 void OnClipboardHasText(wxCommandEvent& event);
102 void OnClipboardHasBitmap(wxCommandEvent& event);
103
104 void OnLeftDown(wxMouseEvent& event);
105 void OnRightDown(wxMouseEvent& event);
106
107 DECLARE_EVENT_TABLE()
108
109 private:
110 wxListBox *m_ctrlFile,
111 *m_ctrlText;
112 wxTextCtrl *m_ctrlLog;
113
114 wxLog *m_pLog, *m_pLogPrev;
115
116 wxString m_strText;
117 wxBitmap m_bitmap;
118 };
119
120 // ----------------------------------------------------------------------------
121 // IDs for the menu commands
122 // ----------------------------------------------------------------------------
123
124 enum
125 {
126 Menu_Quit = 1,
127 Menu_Drag,
128 Menu_About = 101,
129 Menu_Help,
130 Menu_Clear,
131 Menu_Copy,
132 Menu_Paste,
133 Menu_CopyBitmap,
134 Menu_PasteBitmap,
135 Menu_HasText,
136 Menu_HasBitmap
137 };
138
139 BEGIN_EVENT_TABLE(DnDFrame, wxFrame)
140 EVT_MENU(Menu_Quit, DnDFrame::OnQuit)
141 EVT_MENU(Menu_About, DnDFrame::OnAbout)
142 EVT_MENU(Menu_Drag, DnDFrame::OnDrag)
143 EVT_MENU(Menu_Help, DnDFrame::OnHelp)
144 EVT_MENU(Menu_Clear, DnDFrame::OnLogClear)
145 EVT_MENU(Menu_Copy, DnDFrame::OnCopy)
146 EVT_MENU(Menu_Paste, DnDFrame::OnPaste)
147 EVT_MENU(Menu_CopyBitmap, DnDFrame::OnCopyBitmap)
148 EVT_MENU(Menu_PasteBitmap,DnDFrame::OnPasteBitmap)
149 EVT_MENU(Menu_HasText, DnDFrame::OnClipboardHasText)
150 EVT_MENU(Menu_HasBitmap, DnDFrame::OnClipboardHasBitmap)
151
152 EVT_LEFT_DOWN( DnDFrame::OnLeftDown)
153 EVT_RIGHT_DOWN( DnDFrame::OnRightDown)
154 EVT_PAINT( DnDFrame::OnPaint)
155 END_EVENT_TABLE()
156
157 // `Main program' equivalent, creating windows and returning main app frame
158 bool DnDApp::OnInit()
159 {
160 #if wxUSE_LIBPNG
161 wxImage::AddHandler( new wxPNGHandler );
162 #endif
163
164 // create the main frame window
165 DnDFrame *frame = new DnDFrame((wxFrame *) NULL,
166 "Drag-and-Drop/Clipboard wxWindows Sample",
167 50, 50, 450, 340);
168
169 // activate it
170 frame->Show(TRUE);
171
172 SetTopWindow(frame);
173
174 return TRUE;
175 }
176
177 DnDFrame::DnDFrame(wxFrame *frame, char *title, int x, int y, int w, int h)
178 : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h)),
179 m_strText("wxWindows drag & drop works :-)")
180
181 {
182 // frame icon and status bar
183 SetIcon(wxICON(mondrian));
184
185 CreateStatusBar();
186
187 // construct menu
188 wxMenu *file_menu = new wxMenu;
189 file_menu->Append(Menu_Drag, "&Test drag...");
190 file_menu->AppendSeparator();
191 file_menu->Append(Menu_Quit, "E&xit");
192
193 wxMenu *log_menu = new wxMenu;
194 log_menu->Append(Menu_Clear, "Clear");
195
196 wxMenu *help_menu = new wxMenu;
197 help_menu->Append(Menu_Help, "&Help...");
198 help_menu->AppendSeparator();
199 help_menu->Append(Menu_About, "&About");
200
201 wxMenu *clip_menu = new wxMenu;
202 clip_menu->Append(Menu_Copy, "&Copy text\tCtrl+C");
203 clip_menu->Append(Menu_Paste, "&Paste text\tCtrl+V");
204 clip_menu->AppendSeparator();
205 clip_menu->Append(Menu_CopyBitmap, "&Copy bitmap");
206 clip_menu->Append(Menu_PasteBitmap, "&Paste bitmap");
207 clip_menu->AppendSeparator();
208 clip_menu->Append(Menu_HasText, "Clipboard has &text\tCtrl+T");
209 clip_menu->Append(Menu_HasBitmap, "Clipboard has a &bitmap\tCtrl+B");
210
211 wxMenuBar *menu_bar = new wxMenuBar;
212 menu_bar->Append(file_menu, "&File");
213 menu_bar->Append(log_menu, "&Log");
214 menu_bar->Append(clip_menu, "&Clipboard");
215 menu_bar->Append(help_menu, "&Help");
216
217 SetMenuBar(menu_bar);
218
219 // make a panel with 3 subwindows
220 wxPoint pos(0, 0);
221 wxSize size(400, 200);
222
223 wxString strFile("Drop files here!"), strText("Drop text on me");
224
225 m_ctrlFile = new wxListBox(this, -1, pos, size, 1, &strFile, wxLB_HSCROLL | wxLB_ALWAYS_SB );
226 m_ctrlText = new wxListBox(this, -1, pos, size, 1, &strText, wxLB_HSCROLL | wxLB_ALWAYS_SB );
227
228 m_ctrlLog = new wxTextCtrl(this, -1, "", pos, size,
229 wxTE_MULTILINE | wxTE_READONLY |
230 wxSUNKEN_BORDER );
231 // redirect log messages to the text window (don't forget to delete it!)
232 m_pLog = new wxLogTextCtrl(m_ctrlLog);
233 m_pLogPrev = wxLog::SetActiveTarget(m_pLog);
234
235 // associate drop targets with 2 text controls
236 m_ctrlFile->SetDropTarget(new DnDFile(m_ctrlFile));
237 m_ctrlText->SetDropTarget(new DnDText(m_ctrlText));
238
239 wxLayoutConstraints *c;
240
241 // Top-left listbox
242 c = new wxLayoutConstraints;
243 c->left.SameAs(this, wxLeft);
244 c->top.SameAs(this, wxTop);
245 c->right.PercentOf(this, wxRight, 50);
246 c->height.PercentOf(this, wxHeight, 30);
247 m_ctrlFile->SetConstraints(c);
248
249 // Top-right listbox
250 c = new wxLayoutConstraints;
251 c->left.SameAs (m_ctrlFile, wxRight);
252 c->top.SameAs (this, wxTop);
253 c->right.SameAs (this, wxRight);
254 c->height.PercentOf(this, wxHeight, 30);
255 m_ctrlText->SetConstraints(c);
256
257 // Lower text control
258 c = new wxLayoutConstraints;
259 c->left.SameAs (this, wxLeft);
260 c->right.SameAs (this, wxRight);
261 c->height.PercentOf(this, wxHeight, 30);
262 c->top.SameAs(m_ctrlText, wxBottom);
263 m_ctrlLog->SetConstraints(c);
264
265 SetAutoLayout(TRUE);
266 }
267
268 void DnDFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
269 {
270 Close(TRUE);
271 }
272
273 void DnDFrame::OnPaint(wxPaintEvent& WXUNUSED(event))
274 {
275 int w = 0;
276 int h = 0;
277 GetClientSize( &w, &h );
278
279 wxPaintDC dc(this);
280 dc.SetFont( wxFont( 24, wxDECORATIVE, wxNORMAL, wxNORMAL, FALSE, "charter" ) );
281 dc.DrawText( "Drag text from here!", 20, h-50 );
282
283 if (m_bitmap.Ok())
284 dc.DrawBitmap( m_bitmap, 280, h-120, TRUE );
285 }
286
287 void DnDFrame::OnClipboardHasText(wxCommandEvent& WXUNUSED(event))
288 {
289 if ( !wxTheClipboard->Open() )
290 {
291 wxLogError( _T("Can't open clipboard.") );
292
293 return;
294 }
295
296 if ( !wxTheClipboard->IsSupported( wxDF_TEXT ) )
297 {
298 wxLogMessage( _T("No text is on the clipboard") );
299 }
300 else
301 {
302 wxLogMessage( _T("There is text is on the clipboard") );
303 }
304
305 wxTheClipboard->Close();
306 }
307
308 void DnDFrame::OnClipboardHasBitmap(wxCommandEvent& WXUNUSED(event))
309 {
310 if ( !wxTheClipboard->Open() )
311 {
312 wxLogError( _T("Can't open clipboard.") );
313
314 return;
315 }
316
317 if ( !wxTheClipboard->IsSupported( wxDF_BITMAP ) )
318 {
319 wxLogMessage( _T("No bitmap is on the clipboard") );
320 }
321 else
322 {
323 wxLogMessage( _T("A bitmap is on the clipboard") );
324 }
325
326 wxTheClipboard->Close();
327 }
328
329 void DnDFrame::OnDrag(wxCommandEvent& WXUNUSED(event))
330 {
331 wxString strText = wxGetTextFromUser
332 (
333 "After you enter text in this dialog, press any mouse\n"
334 "button in the bottom (empty) part of the frame and \n"
335 "drag it anywhere - you will be in fact dragging the\n"
336 "text object containing this text",
337 "Please enter some text", m_strText, this
338 );
339
340 m_strText = strText;
341 }
342
343 void DnDFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
344 {
345 wxMessageBox("Drag-&-Drop Demo\n"
346 "Please see \"Help|Help...\" for details\n"
347 "Copyright (c) 1998 Vadim Zeitlin",
348 "About wxDnD",
349 wxICON_INFORMATION | wxOK,
350 this);
351 }
352
353 void DnDFrame::OnHelp(wxCommandEvent& /* event */)
354 {
355 wxMessageDialog dialog(this,
356 "This small program demonstrates drag & drop support in wxWindows. The program window\n"
357 "consists of 3 parts: the bottom pane is for debug messages, so that you can see what's\n"
358 "going on inside. The top part is split into 2 listboxes, the left one accepts files\n"
359 "and the right one accepts text.\n"
360 "\n"
361 "To test wxDropTarget: open wordpad (write.exe), select some text in it and drag it to\n"
362 "the right listbox (you'll notice the usual visual feedback, i.e. the cursor will change).\n"
363 "Also, try dragging some files (you can select several at once) from Windows Explorer (or \n"
364 "File Manager) to the left pane. Hold down Ctrl/Shift keys when you drop text (doesn't \n"
365 "work with files) and see what changes.\n"
366 "\n"
367 "To test wxDropSource: just press any mouse button on the empty zone of the window and drag\n"
368 "it to wordpad or any other droptarget accepting text (and of course you can just drag it\n"
369 "to the right pane). Due to a lot of trace messages, the cursor might take some time to \n"
370 "change, don't release the mouse button until it does. You can change the string being\n"
371 "dragged in in \"File|Test drag...\" dialog.\n"
372 "\n"
373 "\n"
374 "Please send all questions/bug reports/suggestions &c to \n"
375 "Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>",
376 "wxDnD Help");
377
378 dialog.ShowModal();
379 }
380
381 void DnDFrame::OnLogClear(wxCommandEvent& /* event */ )
382 {
383 m_ctrlLog->Clear();
384 }
385
386 void DnDFrame::OnLeftDown(wxMouseEvent &WXUNUSED(event) )
387 {
388 if ( !m_strText.IsEmpty() )
389 {
390 // start drag operation
391 #ifdef __WXMSW__
392 wxTextDataObject textData(m_strText);
393 wxDropSource dragSource( textData, this );
394 #else
395 wxDropSource dragSource( new wxTextDataObject (m_strText), this, wxIcon(mondrian_xpm) );
396 #endif
397 const char *pc;
398
399 switch ( dragSource.DoDragDrop(TRUE) )
400 {
401 case wxDragError: pc = "Error!"; break;
402 case wxDragNone: pc = "Nothing"; break;
403 case wxDragCopy: pc = "Copied"; break;
404 case wxDragMove: pc = "Moved"; break;
405 case wxDragCancel: pc = "Cancelled"; break;
406 default: pc = "Huh?"; break;
407 }
408
409 SetStatusText(wxString("Drag result: ") + pc);
410 }
411 }
412
413 void DnDFrame::OnRightDown(wxMouseEvent &event )
414 {
415 wxMenu *menu = new wxMenu;
416
417 menu->Append(Menu_Drag, "&Test drag...");
418 menu->Append(Menu_About, "&About");
419 menu->Append(Menu_Quit, "E&xit");
420
421 PopupMenu( menu, event.GetX(), event.GetY() );
422 }
423
424 DnDFrame::~DnDFrame()
425 {
426 if ( m_pLog != NULL ) {
427 if ( wxLog::SetActiveTarget(m_pLogPrev) == m_pLog )
428 delete m_pLog;
429 }
430 }
431
432 // ---------------------------------------------------------------------------
433 // bitmap clipboard
434 // ---------------------------------------------------------------------------
435
436 void DnDFrame::OnCopyBitmap(wxCommandEvent& WXUNUSED(event))
437 {
438 wxFileDialog dialog(this, "Open a PNG file", "", "", "PNG files (*.png)|*.png", 0);
439
440 if (dialog.ShowModal() != wxID_OK)
441 {
442 wxLogMessage( _T("Aborted file open") );
443 return;
444 }
445
446 if (dialog.GetPath().IsEmpty())
447 {
448 wxLogMessage( _T("Returned empty string.") );
449 return;
450 }
451
452 if (!wxFileExists(dialog.GetPath()))
453 {
454 wxLogMessage( _T("File doesn't exist.") );
455 return;
456 }
457
458 wxImage image;
459 image.LoadFile( dialog.GetPath(), wxBITMAP_TYPE_PNG );
460 if (!image.Ok())
461 {
462 wxLogMessage( _T("Invalid image file...") );
463 return;
464 }
465
466 wxLogMessage( _T("Decoding image file...") );
467 wxYield();
468
469 wxBitmap bitmap( image.ConvertToBitmap() );
470
471 if ( !wxTheClipboard->Open() )
472 {
473 wxLogError(_T("Can't open clipboard."));
474
475 return;
476 }
477
478 wxLogMessage( _T("Creating wxBitmapDataObject...") );
479 wxYield();
480
481 if ( !wxTheClipboard->AddData(new wxBitmapDataObject(bitmap)) )
482 {
483 wxLogError(_T("Can't copy image to the clipboard."));
484 }
485 else
486 {
487 wxLogMessage(_T("Image has been put on the clipboard.") );
488 wxLogMessage(_T("You can paste it now and look at it.") );
489 }
490
491 wxTheClipboard->Close();
492 }
493
494 void DnDFrame::OnPasteBitmap(wxCommandEvent& WXUNUSED(event))
495 {
496 if ( !wxTheClipboard->Open() )
497 {
498 wxLogError(_T("Can't open clipboard."));
499
500 return;
501 }
502
503 if ( !wxTheClipboard->IsSupported(wxDF_BITMAP) )
504 {
505 wxLogWarning(_T("No bitmap on clipboard"));
506
507 wxTheClipboard->Close();
508 return;
509 }
510
511 wxBitmapDataObject data;
512 if ( !wxTheClipboard->GetData(&data) )
513 {
514 wxLogError(_T("Can't paste bitmap from the clipboard"));
515 }
516 else
517 {
518 wxLogMessage(_T("Bitmap pasted from the clipboard") );
519 m_bitmap = data.GetBitmap();
520 Refresh();
521 }
522
523 wxTheClipboard->Close();
524 }
525
526 // ---------------------------------------------------------------------------
527 // text clipboard
528 // ---------------------------------------------------------------------------
529
530 void DnDFrame::OnCopy(wxCommandEvent& WXUNUSED(event))
531 {
532 if ( !wxTheClipboard->Open() )
533 {
534 wxLogError(_T("Can't open clipboard."));
535
536 return;
537 }
538
539 if ( !wxTheClipboard->AddData(new wxTextDataObject(m_strText)) )
540 {
541 wxLogError(_T("Can't copy data to the clipboard"));
542 }
543 else
544 {
545 wxLogMessage(_T("Text '%s' put on the clipboard"), m_strText.c_str());
546 }
547
548 wxTheClipboard->Close();
549 }
550
551 void DnDFrame::OnPaste(wxCommandEvent& WXUNUSED(event))
552 {
553 if ( !wxTheClipboard->Open() )
554 {
555 wxLogError(_T("Can't open clipboard."));
556
557 return;
558 }
559
560 if ( !wxTheClipboard->IsSupported(wxDF_TEXT) )
561 {
562 wxLogWarning(_T("No text data on clipboard"));
563
564 wxTheClipboard->Close();
565 return;
566 }
567
568 wxTextDataObject text;
569 if ( !wxTheClipboard->GetData(&text) )
570 {
571 wxLogError(_T("Can't paste data from the clipboard"));
572 }
573 else
574 {
575 wxLogMessage(_T("Text '%s' pasted from the clipboard"),
576 text.GetText().c_str());
577 }
578
579 wxTheClipboard->Close();
580 }
581
582 // ----------------------------------------------------------------------------
583 // Notifications called by the base class
584 // ----------------------------------------------------------------------------
585
586 bool DnDText::OnDropText( wxDropPointCoord, wxDropPointCoord, const wxChar *psz )
587 {
588 m_pOwner->Append(psz);
589
590 return TRUE;
591 }
592
593 bool DnDFile::OnDropFiles( wxDropPointCoord, wxDropPointCoord, size_t nFiles,
594 const wxChar* const aszFiles[])
595 {
596 wxString str;
597 str.Printf( _T("%d files dropped"), nFiles);
598 m_pOwner->Append(str);
599 for ( size_t n = 0; n < nFiles; n++ ) {
600 m_pOwner->Append(aszFiles[n]);
601 }
602
603 return TRUE;
604 }