1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
5 // Modified by: 2008-10-31 Vadim Zeitlin: big clean up
7 // Copyright: (c) 1997 Julian Smart
8 // (c) 2008 Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx/wx.h".
21 #include "wx/wxprec.h"
32 #include "wx/toolbar.h"
34 #ifndef wxHAS_IMAGES_IN_RESOURCES
35 #include "../sample.xpm"
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"
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)
51 #include "wx/generic/mdig.h"
52 #define wxMDIParentFrame wxGenericMDIParentFrame
53 #define wxMDIChildFrame wxGenericMDIChildFrame
54 #define wxMDIClientWindow wxGenericMDIClientWindow
61 // ---------------------------------------------------------------------------
63 // ---------------------------------------------------------------------------
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
)
71 EVT_MENU(wxID_CLOSE_ALL
, MyFrame::OnCloseAll
)
73 EVT_CLOSE(MyFrame::OnClose
)
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
)
87 EVT_MENU(wxID_PASTE
, MyChild::OnPaste
)
88 EVT_UPDATE_UI(wxID_PASTE
, MyChild::OnUpdatePaste
)
89 #endif // wxUSE_CLIPBOARD
91 EVT_SIZE(MyChild::OnSize
)
92 EVT_MOVE(MyChild::OnMove
)
94 EVT_CLOSE(MyChild::OnCloseWindow
)
97 BEGIN_EVENT_TABLE(MyCanvas
, wxScrolledWindow
)
98 EVT_MOUSE_EVENTS(MyCanvas::OnEvent
)
101 BEGIN_EVENT_TABLE(MyChild::EventHandler
, wxEvtHandler
)
102 EVT_MENU(MDI_REFRESH
, MyChild::EventHandler::OnRefresh
)
105 // ===========================================================================
107 // ===========================================================================
109 // ---------------------------------------------------------------------------
111 // ---------------------------------------------------------------------------
113 // Initialise this in OnInit, not statically
116 if ( !wxApp::OnInit() )
119 // Create the main frame window
121 MyFrame
*frame
= new MyFrame
;
128 // ---------------------------------------------------------------------------
130 // ---------------------------------------------------------------------------
132 // Define my frame constructor
134 : wxMDIParentFrame(NULL
, wxID_ANY
, "wxWidgets MDI Sample",
135 wxDefaultPosition
, wxSize(500, 400))
137 SetIcon(wxICON(sample
));
141 // Associate the menu bar with the frame
142 SetMenuBar(CreateMainMenubar());
145 // This shows that the standard window menu may be customized:
146 wxMenu
* const windowMenu
= GetWindowMenu();
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");
156 // we can also change the help string
157 windowMenu
->SetHelpString(wxID_MDI_WINDOW_CASCADE
,
158 "Arrange windows in cascade");
160 // we can remove some items
161 windowMenu
->Delete(wxID_MDI_WINDOW_ARRANGE_ICONS
);
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");
169 SetWindowMenu(windowMenu
);
171 #endif // wxUSE_MENUS
175 #endif // wxUSE_STATUSBAR
178 m_textWindow
= new wxTextCtrl(this, wxID_ANY
, "A help window",
179 wxDefaultPosition
, wxDefaultSize
,
180 wxTE_MULTILINE
| wxSUNKEN_BORDER
);
183 CreateToolBar(wxNO_BORDER
| wxTB_FLAT
| wxTB_HORIZONTAL
);
184 InitToolBar(GetToolBar());
185 #endif // wxUSE_TOOLBAR
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
197 // connect it only now, after creating m_textWindow
198 Connect(wxEVT_SIZE
, wxSizeEventHandler(MyFrame::OnSize
));
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
));
210 wxMenuBar
*MyFrame::CreateMainMenubar()
212 wxMenu
*menuFile
= new wxMenu
;
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");
218 wxMenu
*menuHelp
= new wxMenu
;
219 menuHelp
->Append(wxID_ABOUT
, "&About\tF1");
221 wxMenuBar
*mbar
= new wxMenuBar
;
222 mbar
->Append(menuFile
, "&File");
223 mbar
->Append(menuHelp
, "&Help");
227 #endif // wxUSE_MENUS
229 void MyFrame::OnClose(wxCloseEvent
& event
)
231 unsigned numChildren
= MyChild::GetChildrenCount();
232 if ( event
.CanVeto() && (numChildren
> 0) )
235 msg
.Printf("%d windows still open, close anyhow?", numChildren
);
236 if ( wxMessageBox(msg
, "Please confirm",
237 wxICON_QUESTION
| wxYES_NO
) != wxYES
)
248 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
253 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
) )
255 (void)wxMessageBox("wxWidgets 2.0 MDI Demo\n"
256 "Author: Julian Smart (c) 1997\n"
257 "Usage: mdi.exe", "About MDI Demo");
260 void MyFrame::OnNewWindow(wxCommandEvent
& WXUNUSED(event
) )
262 // create and show another child frame
263 MyChild
*subframe
= new MyChild(this);
264 subframe
->Show(true);
267 void MyFrame::OnFullScreen(wxCommandEvent
& event
)
269 ShowFullScreen(event
.IsChecked());
272 void MyFrame::OnCloseAll(wxCommandEvent
& WXUNUSED(event
))
274 for ( wxWindowList::const_iterator i
= GetChildren().begin();
275 i
!= GetChildren().end();
278 if ( wxDynamicCast(*i
, wxMDIChildFrame
) )
283 void MyFrame::OnSize(wxSizeEvent
& event
)
286 GetClientSize(&w
, &h
);
288 m_textWindow
->SetSize(0, 0, 200, h
);
289 GetClientWindow()->SetSize(200, 0, w
- 200, h
);
291 // FIXME: On wxX11, we need the MDI frame to process this
292 // event, but on other platforms this should not
294 #ifdef __WXUNIVERSAL__
302 void MyFrame::InitToolBar(wxToolBar
* toolBar
)
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
);
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");
329 #endif // wxUSE_TOOLBAR
331 // ---------------------------------------------------------------------------
333 // ---------------------------------------------------------------------------
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
,
339 wxNO_FULL_REPAINT_ON_RESIZE
|
340 wxVSCROLL
| wxHSCROLL
)
342 SetBackgroundColour(*wxWHITE
);
343 SetCursor(wxCursor(wxCURSOR_PENCIL
));
345 SetScrollbars(20, 20, 50, 50);
350 // Define the repainting behaviour
351 void MyCanvas::OnDraw(wxDC
& dc
)
353 if ( !m_text
.empty() )
354 dc
.DrawText(m_text
, 10, 10);
356 dc
.SetFont(*wxSWISS_FONT
);
357 dc
.SetPen(*wxGREEN_PEN
);
358 dc
.DrawLine(0, 0, 200, 200);
359 dc
.DrawLine(200, 0, 0, 200);
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);
366 dc
.DrawEllipse(250, 250, 100, 50);
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);
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;
378 dc
.DrawPolygon(3, points
);
381 // This implements a tiny doodling program! Drag the mouse using the left
383 void MyCanvas::OnEvent(wxMouseEvent
& event
)
388 wxPoint
pt(event
.GetLogicalPosition(dc
));
390 static long xpos
= -1;
391 static long ypos
= -1;
393 if (xpos
> -1 && ypos
> -1 && event
.Dragging())
395 dc
.SetPen(*wxBLACK_PEN
);
396 dc
.DrawLine(xpos
, ypos
, pt
.x
, pt
.y
);
405 // ---------------------------------------------------------------------------
407 // ---------------------------------------------------------------------------
409 unsigned MyChild::ms_numChildren
= 0;
411 MyChild::MyChild(wxMDIParentFrame
*parent
)
416 wxString::Format("Child %u", ++ms_numChildren
)
419 m_canvas
= new MyCanvas(this, wxPoint(0, 0), GetClientSize());
421 SetIcon(wxICON(chart
));
423 const bool canBeResized
= !IsAlwaysMaximized();
425 // create our menu bar: it will be shown instead of the main frame one when
428 wxMenuBar
*mbar
= MyFrame::CreateMainMenubar();
429 mbar
->GetMenu(0)->Insert(1, wxID_CLOSE
, "&Close child\tCtrl-W",
430 "Close this window");
432 wxMenu
*menuChild
= new wxMenu
;
434 menuChild
->Append(MDI_REFRESH
, "&Refresh picture");
435 menuChild
->Append(MDI_CHANGE_TITLE
, "Change &title...\tCtrl-T");
438 menuChild
->AppendSeparator();
439 menuChild
->Append(MDI_CHANGE_POSITION
, "Move frame\tCtrl-M");
440 menuChild
->Append(MDI_CHANGE_SIZE
, "Resize frame\tCtrl-S");
443 menuChild
->AppendSeparator();
444 menuChild
->Append(wxID_PASTE
, "Copy text from clipboard\tCtrl-V");
445 #endif // wxUSE_CLIPBOARD
447 mbar
->Insert(1, menuChild
, "&Child");
449 // Associate the menu bar with the frame
451 #endif // wxUSE_MENUS
453 // this should work for MDI frames as well as for normal ones, provided
454 // they can be resized at all
456 SetSizeHints(100, 100);
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
));
465 PopEventHandler(true);
470 void MyChild::OnClose(wxCommandEvent
& WXUNUSED(event
))
475 void MyChild::OnRefresh(wxCommandEvent
& WXUNUSED(event
))
481 void MyChild::OnChangePosition(wxCommandEvent
& WXUNUSED(event
))
486 void MyChild::OnChangeSize(wxCommandEvent
& WXUNUSED(event
))
488 SetClientSize(100, 100);
491 void MyChild::OnChangeTitle(wxCommandEvent
& WXUNUSED(event
))
494 static wxString s_title
= "Canvas Frame";
496 wxString title
= wxGetTextFromUser("Enter the new title for MDI child",
497 "MDI sample question",
499 GetParent()->GetParent());
505 #endif // wxUSE_TEXTDLG
508 void MyChild::OnActivate(wxActivateEvent
& event
)
510 if ( event
.GetActive() && m_canvas
)
511 m_canvas
->SetFocus();
514 void MyChild::OnMove(wxMoveEvent
& event
)
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
);
527 void MyChild::OnSize(wxSizeEvent
& event
)
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(),
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
);
541 void MyChild::OnCloseWindow(wxCloseEvent
& event
)
543 if ( m_canvas
&& m_canvas
->IsDirty() )
545 if ( wxMessageBox("Really close?", "Please confirm",
546 wxICON_QUESTION
| wxYES_NO
) != wxYES
)
559 #include "wx/clipbrd.h"
561 void MyChild::OnPaste(wxCommandEvent
& WXUNUSED(event
))
563 wxClipboardLocker lock
;
564 wxTextDataObject data
;
565 m_canvas
->SetText(wxTheClipboard
->GetData(data
)
567 : wxString("No text on clipboard"));
570 void MyChild::OnUpdatePaste(wxUpdateUIEvent
& event
)
572 wxClipboardLocker lock
;
573 event
.Enable( wxTheClipboard
->IsSupported(wxDF_TEXT
) );
576 #endif // wxUSE_CLIPBOARD