Added wxMenu::Delete() and fixed some menu deleted memory
[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 Menu_ToBeGreyed, /* for testing */
138 Menu_ToBeDeleted /* for testing */
139 };
140
141 BEGIN_EVENT_TABLE(DnDFrame, wxFrame)
142 EVT_MENU(Menu_Quit, DnDFrame::OnQuit)
143 EVT_MENU(Menu_About, DnDFrame::OnAbout)
144 EVT_MENU(Menu_Drag, DnDFrame::OnDrag)
145 EVT_MENU(Menu_Help, DnDFrame::OnHelp)
146 EVT_MENU(Menu_Clear, DnDFrame::OnLogClear)
147 EVT_MENU(Menu_Copy, DnDFrame::OnCopy)
148 EVT_MENU(Menu_Paste, DnDFrame::OnPaste)
149 EVT_MENU(Menu_CopyBitmap, DnDFrame::OnCopyBitmap)
150 EVT_MENU(Menu_PasteBitmap,DnDFrame::OnPasteBitmap)
151 EVT_MENU(Menu_HasText, DnDFrame::OnClipboardHasText)
152 EVT_MENU(Menu_HasBitmap, DnDFrame::OnClipboardHasBitmap)
153
154 EVT_LEFT_DOWN( DnDFrame::OnLeftDown)
155 EVT_RIGHT_DOWN( DnDFrame::OnRightDown)
156 EVT_PAINT( DnDFrame::OnPaint)
157 END_EVENT_TABLE()
158
159 // `Main program' equivalent, creating windows and returning main app frame
160 bool DnDApp::OnInit()
161 {
162 #if wxUSE_LIBPNG
163 wxImage::AddHandler( new wxPNGHandler );
164 #endif
165
166 // create the main frame window
167 DnDFrame *frame = new DnDFrame((wxFrame *) NULL,
168 "Drag-and-Drop/Clipboard wxWindows Sample",
169 50, 50, 450, 340);
170
171 // activate it
172 frame->Show(TRUE);
173
174 SetTopWindow(frame);
175
176 return TRUE;
177 }
178
179 DnDFrame::DnDFrame(wxFrame *frame, char *title, int x, int y, int w, int h)
180 : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h)),
181 m_strText("wxWindows drag & drop works :-)")
182
183 {
184 // frame icon and status bar
185 SetIcon(wxICON(mondrian));
186
187 CreateStatusBar();
188
189 // construct sub menu for testing
190 wxMenu *sub_menu = new wxMenu;
191 sub_menu->Append(Menu_Quit, "E&xit");
192 sub_menu->Append(Menu_Quit, "E&xit");
193 sub_menu->Append(Menu_Quit, "E&xit");
194
195 // construct menu
196 wxMenu *file_menu = new wxMenu;
197 file_menu->Append(Menu_Drag, "&Test drag...");
198 file_menu->AppendSeparator();
199 file_menu->Append(Menu_Quit, "E&xit");
200 file_menu->AppendSeparator();
201 file_menu->Append( 0, "More exit menus", sub_menu);
202
203 wxMenu *log_menu = new wxMenu;
204 log_menu->Append(Menu_Clear, "Clear");
205
206 wxMenu *help_menu = new wxMenu;
207 help_menu->Append(Menu_Help, "&Help...");
208 help_menu->AppendSeparator();
209 help_menu->Append(Menu_About, "&About");
210
211 wxMenu *clip_menu = new wxMenu;
212 clip_menu->Append(Menu_Copy, "&Copy text\tCtrl+C");
213 clip_menu->Append(Menu_Paste, "&Paste text\tCtrl+V");
214 clip_menu->AppendSeparator();
215 clip_menu->Append(Menu_CopyBitmap, "&Copy bitmap");
216 clip_menu->Append(Menu_PasteBitmap, "&Paste bitmap");
217 clip_menu->AppendSeparator();
218 clip_menu->Append(Menu_HasText, "Clipboard has &text\tCtrl+T");
219 clip_menu->Append(Menu_HasBitmap, "Clipboard has a &bitmap\tCtrl+B");
220
221 wxMenuBar *menu_bar = new wxMenuBar;
222 menu_bar->Append(file_menu, "&File");
223 menu_bar->Append(log_menu, "&Log");
224 menu_bar->Append(clip_menu, "&Clipboard");
225 menu_bar->Append(help_menu, "&Help");
226
227 SetMenuBar(menu_bar);
228
229 // make a panel with 3 subwindows
230 wxPoint pos(0, 0);
231 wxSize size(400, 200);
232
233 wxString strFile("Drop files here!"), strText("Drop text on me");
234
235 m_ctrlFile = new wxListBox(this, -1, pos, size, 1, &strFile, wxLB_HSCROLL | wxLB_ALWAYS_SB );
236 m_ctrlText = new wxListBox(this, -1, pos, size, 1, &strText, wxLB_HSCROLL | wxLB_ALWAYS_SB );
237
238 m_ctrlLog = new wxTextCtrl(this, -1, "", pos, size,
239 wxTE_MULTILINE | wxTE_READONLY |
240 wxSUNKEN_BORDER );
241 // redirect log messages to the text window (don't forget to delete it!)
242 m_pLog = new wxLogTextCtrl(m_ctrlLog);
243 m_pLogPrev = wxLog::SetActiveTarget(m_pLog);
244
245 // associate drop targets with 2 text controls
246 m_ctrlFile->SetDropTarget(new DnDFile(m_ctrlFile));
247 m_ctrlText->SetDropTarget(new DnDText(m_ctrlText));
248
249 wxLayoutConstraints *c;
250
251 // Top-left listbox
252 c = new wxLayoutConstraints;
253 c->left.SameAs(this, wxLeft);
254 c->top.SameAs(this, wxTop);
255 c->right.PercentOf(this, wxRight, 50);
256 c->height.PercentOf(this, wxHeight, 30);
257 m_ctrlFile->SetConstraints(c);
258
259 // Top-right listbox
260 c = new wxLayoutConstraints;
261 c->left.SameAs (m_ctrlFile, wxRight);
262 c->top.SameAs (this, wxTop);
263 c->right.SameAs (this, wxRight);
264 c->height.PercentOf(this, wxHeight, 30);
265 m_ctrlText->SetConstraints(c);
266
267 // Lower text control
268 c = new wxLayoutConstraints;
269 c->left.SameAs (this, wxLeft);
270 c->right.SameAs (this, wxRight);
271 c->height.PercentOf(this, wxHeight, 30);
272 c->top.SameAs(m_ctrlText, wxBottom);
273 m_ctrlLog->SetConstraints(c);
274
275 SetAutoLayout(TRUE);
276 }
277
278 void DnDFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
279 {
280 Close(TRUE);
281 }
282
283 void DnDFrame::OnPaint(wxPaintEvent& WXUNUSED(event))
284 {
285 int w = 0;
286 int h = 0;
287 GetClientSize( &w, &h );
288
289 wxPaintDC dc(this);
290 dc.SetFont( wxFont( 24, wxDECORATIVE, wxNORMAL, wxNORMAL, FALSE, "charter" ) );
291 dc.DrawText( "Drag text from here!", 20, h-50 );
292
293 if (m_bitmap.Ok())
294 dc.DrawBitmap( m_bitmap, 280, h-120, TRUE );
295 }
296
297 void DnDFrame::OnClipboardHasText(wxCommandEvent& WXUNUSED(event))
298 {
299 if ( !wxTheClipboard->Open() )
300 {
301 wxLogError( _T("Can't open clipboard.") );
302
303 return;
304 }
305
306 if ( !wxTheClipboard->IsSupported( wxDF_TEXT ) )
307 {
308 wxLogMessage( _T("No text on the clipboard") );
309 }
310 else
311 {
312 wxLogMessage( _T("There is text on the clipboard") );
313 }
314
315 wxTheClipboard->Close();
316 }
317
318 void DnDFrame::OnClipboardHasBitmap(wxCommandEvent& WXUNUSED(event))
319 {
320 if ( !wxTheClipboard->Open() )
321 {
322 wxLogError( _T("Can't open clipboard.") );
323
324 return;
325 }
326
327 if ( !wxTheClipboard->IsSupported( wxDF_BITMAP ) )
328 {
329 wxLogMessage( _T("No bitmap on the clipboard") );
330 }
331 else
332 {
333 wxLogMessage( _T("There is a bitmap on the clipboard") );
334 }
335
336 wxTheClipboard->Close();
337 }
338
339 void DnDFrame::OnDrag(wxCommandEvent& WXUNUSED(event))
340 {
341 wxString strText = wxGetTextFromUser
342 (
343 "After you enter text in this dialog, press any mouse\n"
344 "button in the bottom (empty) part of the frame and \n"
345 "drag it anywhere - you will be in fact dragging the\n"
346 "text object containing this text",
347 "Please enter some text", m_strText, this
348 );
349
350 m_strText = strText;
351 }
352
353 void DnDFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
354 {
355 wxMessageBox("Drag-&-Drop Demo\n"
356 "Please see \"Help|Help...\" for details\n"
357 "Copyright (c) 1998 Vadim Zeitlin",
358 "About wxDnD",
359 wxICON_INFORMATION | wxOK,
360 this);
361 }
362
363 void DnDFrame::OnHelp(wxCommandEvent& /* event */)
364 {
365 wxMessageDialog dialog(this,
366 "This small program demonstrates drag & drop support in wxWindows. The program window\n"
367 "consists of 3 parts: the bottom pane is for debug messages, so that you can see what's\n"
368 "going on inside. The top part is split into 2 listboxes, the left one accepts files\n"
369 "and the right one accepts text.\n"
370 "\n"
371 "To test wxDropTarget: open wordpad (write.exe), select some text in it and drag it to\n"
372 "the right listbox (you'll notice the usual visual feedback, i.e. the cursor will change).\n"
373 "Also, try dragging some files (you can select several at once) from Windows Explorer (or \n"
374 "File Manager) to the left pane. Hold down Ctrl/Shift keys when you drop text (doesn't \n"
375 "work with files) and see what changes.\n"
376 "\n"
377 "To test wxDropSource: just press any mouse button on the empty zone of the window and drag\n"
378 "it to wordpad or any other droptarget accepting text (and of course you can just drag it\n"
379 "to the right pane). Due to a lot of trace messages, the cursor might take some time to \n"
380 "change, don't release the mouse button until it does. You can change the string being\n"
381 "dragged in in \"File|Test drag...\" dialog.\n"
382 "\n"
383 "\n"
384 "Please send all questions/bug reports/suggestions &c to \n"
385 "Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>",
386 "wxDnD Help");
387
388 dialog.ShowModal();
389 }
390
391 void DnDFrame::OnLogClear(wxCommandEvent& /* event */ )
392 {
393 m_ctrlLog->Clear();
394 }
395
396 void DnDFrame::OnLeftDown(wxMouseEvent &WXUNUSED(event) )
397 {
398 if ( !m_strText.IsEmpty() )
399 {
400 // start drag operation
401 #ifdef __WXMSW__
402 wxTextDataObject textData(m_strText);
403 wxDropSource dragSource( textData, this );
404 #else
405 wxDropSource dragSource( new wxTextDataObject (m_strText), this, wxIcon(mondrian_xpm) );
406 #endif
407 const char *pc;
408
409 switch ( dragSource.DoDragDrop(TRUE) )
410 {
411 case wxDragError: pc = "Error!"; break;
412 case wxDragNone: pc = "Nothing"; break;
413 case wxDragCopy: pc = "Copied"; break;
414 case wxDragMove: pc = "Moved"; break;
415 case wxDragCancel: pc = "Cancelled"; break;
416 default: pc = "Huh?"; break;
417 }
418
419 SetStatusText(wxString("Drag result: ") + pc);
420 }
421 }
422
423 void DnDFrame::OnRightDown(wxMouseEvent &event )
424 {
425 wxMenu *menu = new wxMenu;
426
427 menu->Append(Menu_Drag, "&Test drag...");
428 menu->Append(Menu_About, "&About");
429 menu->Append(Menu_Quit, "E&xit");
430 menu->Append(Menu_ToBeDeleted, "To be deleted");
431 menu->Append(Menu_ToBeGreyed, "To be greyed");
432
433 menu->Delete( Menu_ToBeDeleted );
434 menu->Enable( Menu_ToBeGreyed, FALSE );
435
436 PopupMenu( menu, event.GetX(), event.GetY() );
437 }
438
439 DnDFrame::~DnDFrame()
440 {
441 if ( m_pLog != NULL ) {
442 if ( wxLog::SetActiveTarget(m_pLogPrev) == m_pLog )
443 delete m_pLog;
444 }
445 }
446
447 // ---------------------------------------------------------------------------
448 // bitmap clipboard
449 // ---------------------------------------------------------------------------
450
451 void DnDFrame::OnCopyBitmap(wxCommandEvent& WXUNUSED(event))
452 {
453 // PNG support is not always compiled in under Windows, so use BMP there
454 #ifdef __WXMSW__
455 wxFileDialog dialog(this, "Open a BMP file", "", "", "BMP files (*.bmp)|*.bmp", 0);
456 #else
457 wxFileDialog dialog(this, "Open a PNG file", "", "", "PNG files (*.png)|*.png", 0);
458 #endif
459
460 if (dialog.ShowModal() != wxID_OK)
461 {
462 wxLogMessage( _T("Aborted file open") );
463 return;
464 }
465
466 if (dialog.GetPath().IsEmpty())
467 {
468 wxLogMessage( _T("Returned empty string.") );
469 return;
470 }
471
472 if (!wxFileExists(dialog.GetPath()))
473 {
474 wxLogMessage( _T("File doesn't exist.") );
475 return;
476 }
477
478 wxImage image;
479 image.LoadFile( dialog.GetPath(),
480 #ifdef __WXMSW__
481 wxBITMAP_TYPE_BMP
482 #else
483 wxBITMAP_TYPE_PNG
484 #endif
485 );
486 if (!image.Ok())
487 {
488 wxLogMessage( _T("Invalid image file...") );
489 return;
490 }
491
492 wxLogMessage( _T("Decoding image file...") );
493 wxYield();
494
495 wxBitmap bitmap( image.ConvertToBitmap() );
496
497 if ( !wxTheClipboard->Open() )
498 {
499 wxLogError(_T("Can't open clipboard."));
500
501 return;
502 }
503
504 wxLogMessage( _T("Creating wxBitmapDataObject...") );
505 wxYield();
506
507 if ( !wxTheClipboard->AddData(new wxBitmapDataObject(bitmap)) )
508 {
509 wxLogError(_T("Can't copy image to the clipboard."));
510 }
511 else
512 {
513 wxLogMessage(_T("Image has been put on the clipboard.") );
514 wxLogMessage(_T("You can paste it now and look at it.") );
515 }
516
517 wxTheClipboard->Close();
518 }
519
520 void DnDFrame::OnPasteBitmap(wxCommandEvent& WXUNUSED(event))
521 {
522 if ( !wxTheClipboard->Open() )
523 {
524 wxLogError(_T("Can't open clipboard."));
525
526 return;
527 }
528
529 if ( !wxTheClipboard->IsSupported(wxDF_BITMAP) )
530 {
531 wxLogWarning(_T("No bitmap on clipboard"));
532
533 wxTheClipboard->Close();
534 return;
535 }
536
537 wxBitmapDataObject data;
538 if ( !wxTheClipboard->GetData(&data) )
539 {
540 wxLogError(_T("Can't paste bitmap from the clipboard"));
541 }
542 else
543 {
544 wxLogMessage(_T("Bitmap pasted from the clipboard") );
545 m_bitmap = data.GetBitmap();
546 Refresh();
547 }
548
549 wxTheClipboard->Close();
550 }
551
552 // ---------------------------------------------------------------------------
553 // text clipboard
554 // ---------------------------------------------------------------------------
555
556 void DnDFrame::OnCopy(wxCommandEvent& WXUNUSED(event))
557 {
558 if ( !wxTheClipboard->Open() )
559 {
560 wxLogError(_T("Can't open clipboard."));
561
562 return;
563 }
564
565 if ( !wxTheClipboard->AddData(new wxTextDataObject(m_strText)) )
566 {
567 wxLogError(_T("Can't copy data to the clipboard"));
568 }
569 else
570 {
571 wxLogMessage(_T("Text '%s' put on the clipboard"), m_strText.c_str());
572 }
573
574 wxTheClipboard->Close();
575 }
576
577 void DnDFrame::OnPaste(wxCommandEvent& WXUNUSED(event))
578 {
579 if ( !wxTheClipboard->Open() )
580 {
581 wxLogError(_T("Can't open clipboard."));
582
583 return;
584 }
585
586 if ( !wxTheClipboard->IsSupported(wxDF_TEXT) )
587 {
588 wxLogWarning(_T("No text data on clipboard"));
589
590 wxTheClipboard->Close();
591 return;
592 }
593
594 wxTextDataObject text;
595 if ( !wxTheClipboard->GetData(&text) )
596 {
597 wxLogError(_T("Can't paste data from the clipboard"));
598 }
599 else
600 {
601 wxLogMessage(_T("Text '%s' pasted from the clipboard"),
602 text.GetText().c_str());
603 }
604
605 wxTheClipboard->Close();
606 }
607
608 // ----------------------------------------------------------------------------
609 // Notifications called by the base class
610 // ----------------------------------------------------------------------------
611
612 bool DnDText::OnDropText( wxDropPointCoord, wxDropPointCoord, const wxChar *psz )
613 {
614 m_pOwner->Append(psz);
615
616 return TRUE;
617 }
618
619 bool DnDFile::OnDropFiles( wxDropPointCoord, wxDropPointCoord, size_t nFiles,
620 const wxChar* const aszFiles[])
621 {
622 wxString str;
623 str.Printf( _T("%d files dropped"), nFiles);
624 m_pOwner->Append(str);
625 for ( size_t n = 0; n < nFiles; n++ ) {
626 m_pOwner->Append(aszFiles[n]);
627 }
628
629 return TRUE;
630 }