mdi.cpp 16 KB

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