Remove all lines containing cvs/svn "$Id$" keyword.
[wxWidgets.git] / samples / mdi / mdi.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mdi.cpp
3 // Purpose: MDI sample
4 // Author: Julian Smart
5 // Modified by: 2008-10-31 Vadim Zeitlin: big clean up
6 // Created: 04/01/98
7 // Copyright: (c) 1997 Julian Smart
8 // (c) 2008 Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx/wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/wx.h"
29 #include "wx/mdi.h"
30 #endif
31
32 #include "wx/toolbar.h"
33
34 #ifndef wxHAS_IMAGES_IN_RESOURCES
35 #include "../sample.xpm"
36 #include "chart.xpm"
37 #endif
38
39 #include "bitmaps/new.xpm"
40 #include "bitmaps/open.xpm"
41 #include "bitmaps/save.xpm"
42 #include "bitmaps/copy.xpm"
43 #include "bitmaps/cut.xpm"
44 #include "bitmaps/paste.xpm"
45 #include "bitmaps/print.xpm"
46 #include "bitmaps/help.xpm"
47
48 // replace this 0 with 1 to build the sample using the generic MDI classes (you
49 // may also need to add src/generic/mdig.cpp to the build)
50 #if 0
51 #include "wx/generic/mdig.h"
52 #define wxMDIParentFrame wxGenericMDIParentFrame
53 #define wxMDIChildFrame wxGenericMDIChildFrame
54 #define wxMDIClientWindow wxGenericMDIClientWindow
55 #endif
56
57 #include "mdi.h"
58
59 IMPLEMENT_APP(MyApp)
60
61 // ---------------------------------------------------------------------------
62 // event tables
63 // ---------------------------------------------------------------------------
64
65 BEGIN_EVENT_TABLE(MyFrame, wxMDIParentFrame)
66 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
67 EVT_MENU(wxID_NEW, MyFrame::OnNewWindow)
68 EVT_MENU(MDI_FULLSCREEN, MyFrame::OnFullScreen)
69 EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
70
71 EVT_MENU(wxID_CLOSE_ALL, MyFrame::OnCloseAll)
72
73 EVT_CLOSE(MyFrame::OnClose)
74 END_EVENT_TABLE()
75
76 // Note that wxID_NEW and wxID_ABOUT commands get passed
77 // to the parent window for processing, so no need to
78 // duplicate event handlers here.
79 BEGIN_EVENT_TABLE(MyChild, wxMDIChildFrame)
80 EVT_MENU(wxID_CLOSE, MyChild::OnClose)
81 EVT_MENU(MDI_REFRESH, MyChild::OnRefresh)
82 EVT_MENU(MDI_CHANGE_TITLE, MyChild::OnChangeTitle)
83 EVT_MENU(MDI_CHANGE_POSITION, MyChild::OnChangePosition)
84 EVT_MENU(MDI_CHANGE_SIZE, MyChild::OnChangeSize)
85
86 #if wxUSE_CLIPBOARD
87 EVT_MENU(wxID_PASTE, MyChild::OnPaste)
88 EVT_UPDATE_UI(wxID_PASTE, MyChild::OnUpdatePaste)
89 #endif // wxUSE_CLIPBOARD
90
91 EVT_SIZE(MyChild::OnSize)
92 EVT_MOVE(MyChild::OnMove)
93
94 EVT_CLOSE(MyChild::OnCloseWindow)
95 END_EVENT_TABLE()
96
97 BEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)
98 EVT_MOUSE_EVENTS(MyCanvas::OnEvent)
99 END_EVENT_TABLE()
100
101 BEGIN_EVENT_TABLE(MyChild::EventHandler, wxEvtHandler)
102 EVT_MENU(MDI_REFRESH, MyChild::EventHandler::OnRefresh)
103 END_EVENT_TABLE()
104
105 // ===========================================================================
106 // implementation
107 // ===========================================================================
108
109 // ---------------------------------------------------------------------------
110 // MyApp
111 // ---------------------------------------------------------------------------
112
113 // Initialise this in OnInit, not statically
114 bool MyApp::OnInit()
115 {
116 if ( !wxApp::OnInit() )
117 return false;
118
119 // Create the main frame window
120
121 MyFrame *frame = new MyFrame;
122
123 frame->Show(true);
124
125 return true;
126 }
127
128 // ---------------------------------------------------------------------------
129 // MyFrame
130 // ---------------------------------------------------------------------------
131
132 // Define my frame constructor
133 MyFrame::MyFrame()
134 : wxMDIParentFrame(NULL, wxID_ANY, "wxWidgets MDI Sample",
135 wxDefaultPosition, wxSize(500, 400))
136 {
137 SetIcon(wxICON(sample));
138
139 // Make a menubar
140 #if wxUSE_MENUS
141 // Associate the menu bar with the frame
142 SetMenuBar(CreateMainMenubar());
143
144
145 // This shows that the standard window menu may be customized:
146 wxMenu * const windowMenu = GetWindowMenu();
147 if ( windowMenu )
148 {
149 // we can change the labels of standard items (which also means we can
150 // set up accelerators for them as they're part of the label)
151 windowMenu->SetLabel(wxID_MDI_WINDOW_TILE_HORZ,
152 "&Tile horizontally\tCtrl-Shift-H");
153 windowMenu->SetLabel(wxID_MDI_WINDOW_TILE_VERT,
154 "&Tile vertically\tCtrl-Shift-V");
155
156 // we can also change the help string
157 windowMenu->SetHelpString(wxID_MDI_WINDOW_CASCADE,
158 "Arrange windows in cascade");
159
160 // we can remove some items
161 windowMenu->Delete(wxID_MDI_WINDOW_ARRANGE_ICONS);
162
163 // and we can add completely custom commands -- but then we must handle
164 // them ourselves, see OnCloseAll()
165 windowMenu->AppendSeparator();
166 windowMenu->Append(wxID_CLOSE_ALL, "&Close all windows\tCtrl-Shift-C",
167 "Close all open windows");
168
169 SetWindowMenu(windowMenu);
170 }
171 #endif // wxUSE_MENUS
172
173 #if wxUSE_STATUSBAR
174 CreateStatusBar();
175 #endif // wxUSE_STATUSBAR
176
177
178 m_textWindow = new wxTextCtrl(this, wxID_ANY, "A help window",
179 wxDefaultPosition, wxDefaultSize,
180 wxTE_MULTILINE | wxSUNKEN_BORDER);
181
182 #if wxUSE_TOOLBAR
183 CreateToolBar(wxNO_BORDER | wxTB_FLAT | wxTB_HORIZONTAL);
184 InitToolBar(GetToolBar());
185 #endif // wxUSE_TOOLBAR
186
187 #if wxUSE_ACCEL
188 // Accelerators
189 wxAcceleratorEntry entries[3];
190 entries[0].Set(wxACCEL_CTRL, (int) 'N', wxID_NEW);
191 entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_EXIT);
192 entries[2].Set(wxACCEL_CTRL, (int) 'A', wxID_ABOUT);
193 wxAcceleratorTable accel(3, entries);
194 SetAcceleratorTable(accel);
195 #endif // wxUSE_ACCEL
196
197 // connect it only now, after creating m_textWindow
198 Connect(wxEVT_SIZE, wxSizeEventHandler(MyFrame::OnSize));
199 }
200
201 MyFrame::~MyFrame()
202 {
203 // and disconnect it to prevent accessing already deleted m_textWindow in
204 // the size event handler if it's called during destruction
205 Disconnect(wxEVT_SIZE, wxSizeEventHandler(MyFrame::OnSize));
206 }
207
208 #if wxUSE_MENUS
209 /* static */
210 wxMenuBar *MyFrame::CreateMainMenubar()
211 {
212 wxMenu *menuFile = new wxMenu;
213
214 menuFile->Append(wxID_NEW, "&New window\tCtrl-N", "Create a new child window");
215 menuFile->AppendCheckItem(MDI_FULLSCREEN, "Show &full screen\tCtrl-F");
216 menuFile->Append(wxID_EXIT, "&Exit\tAlt-X", "Quit the program");
217
218 wxMenu *menuHelp = new wxMenu;
219 menuHelp->Append(wxID_ABOUT, "&About\tF1");
220
221 wxMenuBar *mbar = new wxMenuBar;
222 mbar->Append(menuFile, "&File");
223 mbar->Append(menuHelp, "&Help");
224
225 return mbar;
226 }
227 #endif // wxUSE_MENUS
228
229 void MyFrame::OnClose(wxCloseEvent& event)
230 {
231 unsigned numChildren = MyChild::GetChildrenCount();
232 if ( event.CanVeto() && (numChildren > 0) )
233 {
234 wxString msg;
235 msg.Printf("%d windows still open, close anyhow?", numChildren);
236 if ( wxMessageBox(msg, "Please confirm",
237 wxICON_QUESTION | wxYES_NO) != wxYES )
238 {
239 event.Veto();
240
241 return;
242 }
243 }
244
245 event.Skip();
246 }
247
248 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
249 {
250 Close();
251 }
252
253 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
254 {
255 (void)wxMessageBox("wxWidgets 2.0 MDI Demo\n"
256 "Author: Julian Smart (c) 1997\n"
257 "Usage: mdi.exe", "About MDI Demo");
258 }
259
260 void MyFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event) )
261 {
262 // create and show another child frame
263 MyChild *subframe = new MyChild(this);
264 subframe->Show(true);
265 }
266
267 void MyFrame::OnFullScreen(wxCommandEvent& event)
268 {
269 ShowFullScreen(event.IsChecked());
270 }
271
272 void MyFrame::OnCloseAll(wxCommandEvent& WXUNUSED(event))
273 {
274 for ( wxWindowList::const_iterator i = GetChildren().begin();
275 i != GetChildren().end();
276 ++i )
277 {
278 if ( wxDynamicCast(*i, wxMDIChildFrame) )
279 (*i)->Close();
280 }
281 }
282
283 void MyFrame::OnSize(wxSizeEvent& event)
284 {
285 int w, h;
286 GetClientSize(&w, &h);
287
288 m_textWindow->SetSize(0, 0, 200, h);
289 GetClientWindow()->SetSize(200, 0, w - 200, h);
290
291 // FIXME: On wxX11, we need the MDI frame to process this
292 // event, but on other platforms this should not
293 // be done.
294 #ifdef __WXUNIVERSAL__
295 event.Skip();
296 #else
297 wxUnusedVar(event);
298 #endif
299 }
300
301 #if wxUSE_TOOLBAR
302 void MyFrame::InitToolBar(wxToolBar* toolBar)
303 {
304 wxBitmap bitmaps[8];
305
306 bitmaps[0] = wxBitmap( new_xpm );
307 bitmaps[1] = wxBitmap( open_xpm );
308 bitmaps[2] = wxBitmap( save_xpm );
309 bitmaps[3] = wxBitmap( copy_xpm );
310 bitmaps[4] = wxBitmap( cut_xpm );
311 bitmaps[5] = wxBitmap( paste_xpm );
312 bitmaps[6] = wxBitmap( print_xpm );
313 bitmaps[7] = wxBitmap( help_xpm );
314
315 toolBar->AddTool(wxID_NEW, "New", bitmaps[0], "New file");
316 toolBar->AddTool(1, "Open", bitmaps[1], "Open file");
317 toolBar->AddTool(2, "Save", bitmaps[2], "Save file");
318 toolBar->AddSeparator();
319 toolBar->AddTool(3, "Copy", bitmaps[3], "Copy");
320 toolBar->AddTool(4, "Cut", bitmaps[4], "Cut");
321 toolBar->AddTool(5, "Paste", bitmaps[5], "Paste");
322 toolBar->AddSeparator();
323 toolBar->AddTool(6, "Print", bitmaps[6], "Print");
324 toolBar->AddSeparator();
325 toolBar->AddTool(wxID_ABOUT, "About", bitmaps[7], "Help");
326
327 toolBar->Realize();
328 }
329 #endif // wxUSE_TOOLBAR
330
331 // ---------------------------------------------------------------------------
332 // MyCanvas
333 // ---------------------------------------------------------------------------
334
335 // Define a constructor for my canvas
336 MyCanvas::MyCanvas(wxWindow *parent, const wxPoint& pos, const wxSize& size)
337 : wxScrolledWindow(parent, wxID_ANY, pos, size,
338 wxSUNKEN_BORDER |
339 wxNO_FULL_REPAINT_ON_RESIZE |
340 wxVSCROLL | wxHSCROLL)
341 {
342 SetBackgroundColour(*wxWHITE);
343 SetCursor(wxCursor(wxCURSOR_PENCIL));
344
345 SetScrollbars(20, 20, 50, 50);
346
347 m_dirty = false;
348 }
349
350 // Define the repainting behaviour
351 void MyCanvas::OnDraw(wxDC& dc)
352 {
353 if ( !m_text.empty() )
354 dc.DrawText(m_text, 10, 10);
355
356 dc.SetFont(*wxSWISS_FONT);
357 dc.SetPen(*wxGREEN_PEN);
358 dc.DrawLine(0, 0, 200, 200);
359 dc.DrawLine(200, 0, 0, 200);
360
361 dc.SetBrush(*wxCYAN_BRUSH);
362 dc.SetPen(*wxRED_PEN);
363 dc.DrawRectangle(100, 100, 100, 50);
364 dc.DrawRoundedRectangle(150, 150, 100, 50, 20);
365
366 dc.DrawEllipse(250, 250, 100, 50);
367 #if wxUSE_SPLINES
368 dc.DrawSpline(50, 200, 50, 100, 200, 10);
369 #endif // wxUSE_SPLINES
370 dc.DrawLine(50, 230, 200, 230);
371 dc.DrawText("This is a test string", 50, 230);
372
373 wxPoint points[3];
374 points[0].x = 200; points[0].y = 300;
375 points[1].x = 100; points[1].y = 400;
376 points[2].x = 300; points[2].y = 400;
377
378 dc.DrawPolygon(3, points);
379 }
380
381 // This implements a tiny doodling program! Drag the mouse using the left
382 // button.
383 void MyCanvas::OnEvent(wxMouseEvent& event)
384 {
385 wxClientDC dc(this);
386 PrepareDC(dc);
387
388 wxPoint pt(event.GetLogicalPosition(dc));
389
390 static long xpos = -1;
391 static long ypos = -1;
392
393 if (xpos > -1 && ypos > -1 && event.Dragging())
394 {
395 dc.SetPen(*wxBLACK_PEN);
396 dc.DrawLine(xpos, ypos, pt.x, pt.y);
397
398 m_dirty = true;
399 }
400
401 xpos = pt.x;
402 ypos = pt.y;
403 }
404
405 // ---------------------------------------------------------------------------
406 // MyChild
407 // ---------------------------------------------------------------------------
408
409 unsigned MyChild::ms_numChildren = 0;
410
411 MyChild::MyChild(wxMDIParentFrame *parent)
412 : wxMDIChildFrame
413 (
414 parent,
415 wxID_ANY,
416 wxString::Format("Child %u", ++ms_numChildren)
417 )
418 {
419 m_canvas = new MyCanvas(this, wxPoint(0, 0), GetClientSize());
420
421 SetIcon(wxICON(chart));
422
423 const bool canBeResized = !IsAlwaysMaximized();
424
425 // create our menu bar: it will be shown instead of the main frame one when
426 // we're active
427 #if wxUSE_MENUS
428 wxMenuBar *mbar = MyFrame::CreateMainMenubar();
429 mbar->GetMenu(0)->Insert(1, wxID_CLOSE, "&Close child\tCtrl-W",
430 "Close this window");
431
432 wxMenu *menuChild = new wxMenu;
433
434 menuChild->Append(MDI_REFRESH, "&Refresh picture");
435 menuChild->Append(MDI_CHANGE_TITLE, "Change &title...\tCtrl-T");
436 if ( canBeResized )
437 {
438 menuChild->AppendSeparator();
439 menuChild->Append(MDI_CHANGE_POSITION, "Move frame\tCtrl-M");
440 menuChild->Append(MDI_CHANGE_SIZE, "Resize frame\tCtrl-S");
441 }
442 #if wxUSE_CLIPBOARD
443 menuChild->AppendSeparator();
444 menuChild->Append(wxID_PASTE, "Copy text from clipboard\tCtrl-V");
445 #endif // wxUSE_CLIPBOARD
446
447 mbar->Insert(1, menuChild, "&Child");
448
449 // Associate the menu bar with the frame
450 SetMenuBar(mbar);
451 #endif // wxUSE_MENUS
452
453 // this should work for MDI frames as well as for normal ones, provided
454 // they can be resized at all
455 if ( canBeResized )
456 SetSizeHints(100, 100);
457
458 // test that event handlers pushed on top of MDI children do work (this
459 // used to be broken, see #11225)
460 PushEventHandler(new EventHandler(ms_numChildren));
461 }
462
463 MyChild::~MyChild()
464 {
465 PopEventHandler(true);
466
467 ms_numChildren--;
468 }
469
470 void MyChild::OnClose(wxCommandEvent& WXUNUSED(event))
471 {
472 Close(true);
473 }
474
475 void MyChild::OnRefresh(wxCommandEvent& WXUNUSED(event))
476 {
477 if ( m_canvas )
478 m_canvas->Refresh();
479 }
480
481 void MyChild::OnChangePosition(wxCommandEvent& WXUNUSED(event))
482 {
483 Move(10, 10);
484 }
485
486 void MyChild::OnChangeSize(wxCommandEvent& WXUNUSED(event))
487 {
488 SetClientSize(100, 100);
489 }
490
491 void MyChild::OnChangeTitle(wxCommandEvent& WXUNUSED(event))
492 {
493 #if wxUSE_TEXTDLG
494 static wxString s_title = "Canvas Frame";
495
496 wxString title = wxGetTextFromUser("Enter the new title for MDI child",
497 "MDI sample question",
498 s_title,
499 GetParent()->GetParent());
500 if ( !title )
501 return;
502
503 s_title = title;
504 SetTitle(s_title);
505 #endif // wxUSE_TEXTDLG
506 }
507
508 void MyChild::OnActivate(wxActivateEvent& event)
509 {
510 if ( event.GetActive() && m_canvas )
511 m_canvas->SetFocus();
512 }
513
514 void MyChild::OnMove(wxMoveEvent& event)
515 {
516 // VZ: here everything is totally wrong under MSW, the positions are
517 // different and both wrong (pos2 is off by 2 pixels for me which seems
518 // to be the width of the MDI canvas border)
519 wxPoint pos1 = event.GetPosition(),
520 pos2 = GetPosition();
521 wxLogStatus("position from event: (%d, %d), from frame (%d, %d)",
522 pos1.x, pos1.y, pos2.x, pos2.y);
523
524 event.Skip();
525 }
526
527 void MyChild::OnSize(wxSizeEvent& event)
528 {
529 // VZ: under MSW the size event carries the client size (quite
530 // unexpectedly) *except* for the very first one which has the full
531 // size... what should it really be? TODO: check under wxGTK
532 wxSize size1 = event.GetSize(),
533 size2 = GetSize(),
534 size3 = GetClientSize();
535 wxLogStatus("size from event: %dx%d, from frame %dx%d, client %dx%d",
536 size1.x, size1.y, size2.x, size2.y, size3.x, size3.y);
537
538 event.Skip();
539 }
540
541 void MyChild::OnCloseWindow(wxCloseEvent& event)
542 {
543 if ( m_canvas && m_canvas->IsDirty() )
544 {
545 if ( wxMessageBox("Really close?", "Please confirm",
546 wxICON_QUESTION | wxYES_NO) != wxYES )
547 {
548 event.Veto();
549
550 return;
551 }
552 }
553
554 event.Skip();
555 }
556
557 #if wxUSE_CLIPBOARD
558
559 #include "wx/clipbrd.h"
560
561 void MyChild::OnPaste(wxCommandEvent& WXUNUSED(event))
562 {
563 wxClipboardLocker lock;
564 wxTextDataObject data;
565 m_canvas->SetText(wxTheClipboard->GetData(data)
566 ? data.GetText()
567 : wxString("No text on clipboard"));
568 }
569
570 void MyChild::OnUpdatePaste(wxUpdateUIEvent& event)
571 {
572 wxClipboardLocker lock;
573 event.Enable( wxTheClipboard->IsSupported(wxDF_TEXT) );
574 }
575
576 #endif // wxUSE_CLIPBOARD