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