X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/29b07a3821efad89112a5a250fee09f2577658fe..170acdc90e9f92f8b8120fa6c04acdbf45c89582:/demos/life/life.cpp diff --git a/demos/life/life.cpp b/demos/life/life.cpp index 91dc7f236b..f6295f5981 100644 --- a/demos/life/life.cpp +++ b/demos/life/life.cpp @@ -13,10 +13,6 @@ // headers, declarations, constants // ========================================================================== -#ifdef __GNUG__ - #pragma implementation "life.h" -#endif - // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" @@ -29,25 +25,31 @@ #endif #include "wx/statline.h" +#include "wx/wfstream.h" +#include "wx/filedlg.h" +#include "wx/stockitem.h" #include "life.h" #include "game.h" #include "dialogs.h" +#include "reader.h" // -------------------------------------------------------------------------- // resources // -------------------------------------------------------------------------- -#if defined(__WXGTK__) || defined(__WXMOTIF__) +#if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__) || defined(__WXX11__) // application icon #include "mondrian.xpm" // bitmap buttons for the toolbar #include "bitmaps/reset.xpm" + #include "bitmaps/open.xpm" #include "bitmaps/play.xpm" #include "bitmaps/stop.xpm" #include "bitmaps/zoomin.xpm" #include "bitmaps/zoomout.xpm" + #include "bitmaps/info.xpm" // navigator #include "bitmaps/north.xpm" @@ -61,28 +63,17 @@ // constants // -------------------------------------------------------------------------- -// IDs for the controls and the menu commands +// IDs for the controls and the menu commands. Exluding those already defined +// by wxWidgets, such as wxID_NEW. enum { // timer - ID_TIMER = 1001, + ID_TIMER = wxID_HIGHEST, - // menu items and toolbar buttons - ID_RESET, + // file menu ID_SAMPLES, - ID_ABOUT, - ID_EXIT, - ID_START, - ID_STEP, - ID_STOP, - ID_ZOOMIN, - ID_ZOOMOUT, - ID_TOPSPEED, - - // speed selection slider - ID_SLIDER, - // navigation + // view menu ID_SHOWNAV, ID_ORIGIN, ID_CENTER, @@ -90,34 +81,47 @@ enum ID_SOUTH, ID_EAST, ID_WEST, + ID_INFO, + + // game menu + ID_START, + ID_STEP, + ID_TOPSPEED, + + // speed selection slider + ID_SLIDER }; // -------------------------------------------------------------------------- -// event tables and other macros for wxWindows +// event tables and other macros for wxWidgets // -------------------------------------------------------------------------- // Event tables BEGIN_EVENT_TABLE(LifeFrame, wxFrame) - EVT_MENU (ID_SAMPLES, LifeFrame::OnSamples) - EVT_MENU (ID_RESET, LifeFrame::OnMenu) - EVT_MENU (ID_ABOUT, LifeFrame::OnMenu) - EVT_MENU (ID_EXIT, LifeFrame::OnMenu) - EVT_MENU (ID_START, LifeFrame::OnMenu) - EVT_MENU (ID_STEP, LifeFrame::OnMenu) - EVT_MENU (ID_STOP, LifeFrame::OnMenu) - EVT_MENU (ID_TOPSPEED, LifeFrame::OnMenu) - EVT_MENU (ID_SHOWNAV, LifeFrame::OnMenu) - EVT_MENU (ID_ORIGIN, LifeFrame::OnNavigate) - EVT_BUTTON (ID_CENTER, LifeFrame::OnNavigate) - EVT_BUTTON (ID_NORTH, LifeFrame::OnNavigate) - EVT_BUTTON (ID_SOUTH, LifeFrame::OnNavigate) - EVT_BUTTON (ID_EAST, LifeFrame::OnNavigate) - EVT_BUTTON (ID_WEST, LifeFrame::OnNavigate) - EVT_MENU (ID_ZOOMIN, LifeFrame::OnZoom) - EVT_MENU (ID_ZOOMOUT, LifeFrame::OnZoom) - EVT_COMMAND_SCROLL (ID_SLIDER, LifeFrame::OnSlider) - EVT_TIMER (ID_TIMER, LifeFrame::OnTimer) - EVT_CLOSE ( LifeFrame::OnClose) + EVT_MENU (wxID_NEW, LifeFrame::OnMenu) +#if wxUSE_FILEDLG + EVT_MENU (wxID_OPEN, LifeFrame::OnOpen) +#endif + EVT_MENU (ID_SAMPLES, LifeFrame::OnSamples) + EVT_MENU (wxID_ABOUT, LifeFrame::OnMenu) + EVT_MENU (wxID_EXIT, LifeFrame::OnMenu) + EVT_MENU (ID_SHOWNAV, LifeFrame::OnMenu) + EVT_MENU (ID_ORIGIN, LifeFrame::OnNavigate) + EVT_BUTTON (ID_CENTER, LifeFrame::OnNavigate) + EVT_BUTTON (ID_NORTH, LifeFrame::OnNavigate) + EVT_BUTTON (ID_SOUTH, LifeFrame::OnNavigate) + EVT_BUTTON (ID_EAST, LifeFrame::OnNavigate) + EVT_BUTTON (ID_WEST, LifeFrame::OnNavigate) + EVT_MENU (wxID_ZOOM_IN, LifeFrame::OnZoom) + EVT_MENU (wxID_ZOOM_OUT,LifeFrame::OnZoom) + EVT_MENU (ID_INFO, LifeFrame::OnMenu) + EVT_MENU (ID_START, LifeFrame::OnMenu) + EVT_MENU (ID_STEP, LifeFrame::OnMenu) + EVT_MENU (wxID_STOP, LifeFrame::OnMenu) + EVT_MENU (ID_TOPSPEED, LifeFrame::OnMenu) + EVT_COMMAND_SCROLL (ID_SLIDER, LifeFrame::OnSlider) + EVT_TIMER (ID_TIMER, LifeFrame::OnTimer) + EVT_CLOSE ( LifeFrame::OnClose) END_EVENT_TABLE() BEGIN_EVENT_TABLE(LifeNavigator, wxMiniFrame) @@ -128,7 +132,10 @@ BEGIN_EVENT_TABLE(LifeCanvas, wxWindow) EVT_PAINT ( LifeCanvas::OnPaint) EVT_SCROLLWIN ( LifeCanvas::OnScroll) EVT_SIZE ( LifeCanvas::OnSize) - EVT_MOUSE_EVENTS ( LifeCanvas::OnMouse) + EVT_MOTION ( LifeCanvas::OnMouse) + EVT_LEFT_DOWN ( LifeCanvas::OnMouse) + EVT_LEFT_UP ( LifeCanvas::OnMouse) + EVT_LEFT_DCLICK ( LifeCanvas::OnMouse) EVT_ERASE_BACKGROUND( LifeCanvas::OnEraseBackground) END_EVENT_TABLE() @@ -142,8 +149,8 @@ IMPLEMENT_APP(LifeApp) // ========================================================================== // some shortcuts -#define ADD_TOOL(id, bmp, tooltip, help) \ - toolBar->AddTool(id, bmp, wxNullBitmap, FALSE, -1, -1, (wxObject *)0, tooltip, help) +#define ADD_TOOL(id, bmp, tooltip, help) \ + toolBar->AddTool(id, bmp, wxNullBitmap, false, wxDefaultCoord, wxDefaultCoord, (wxObject *)NULL, tooltip, help) // -------------------------------------------------------------------------- @@ -157,11 +164,16 @@ bool LifeApp::OnInit() LifeFrame *frame = new LifeFrame(); // show it and tell the application that it's our main window - frame->Show(TRUE); + frame->Show(true); SetTopWindow(frame); + // just for Motif +#ifdef __WXMOTIF__ + frame->UpdateInfoText(); +#endif + // enter the main message loop and run the app - return TRUE; + return true; } // -------------------------------------------------------------------------- @@ -169,79 +181,104 @@ bool LifeApp::OnInit() // -------------------------------------------------------------------------- // frame constructor -LifeFrame::LifeFrame() : wxFrame((wxFrame *)0, -1, _("Life!"), wxPoint(200, 200)) +LifeFrame::LifeFrame() : + wxFrame( (wxFrame *) NULL, wxID_ANY, _("Life!"), wxDefaultPosition ), + m_navigator(NULL) { // frame icon SetIcon(wxICON(mondrian)); // menu bar - wxMenu *menuFile = new wxMenu("", wxMENU_TEAROFF); - wxMenu *menuView = new wxMenu("", wxMENU_TEAROFF); - wxMenu *menuGame = new wxMenu("", wxMENU_TEAROFF); - - menuFile->Append(ID_RESET, _("Reset"), _("Start a new game")); - menuFile->Append(ID_SAMPLES, _("Sample game..."), _("Select a sample configuration")); - menuFile->AppendSeparator(); - menuFile->Append(ID_ABOUT, _("&About...\tCtrl-A"), _("Show about dialog")); + wxMenu *menuFile = new wxMenu(wxMENU_TEAROFF); + wxMenu *menuView = new wxMenu(wxMENU_TEAROFF); + wxMenu *menuGame = new wxMenu(wxMENU_TEAROFF); + wxMenu *menuHelp = new wxMenu(wxMENU_TEAROFF); + + menuFile->Append(wxID_NEW, wxEmptyString, _("Start a new game")); +#if wxUSE_FILEDLG + menuFile->Append(wxID_OPEN, wxEmptyString, _("Open an existing Life pattern")); +#endif + menuFile->Append(ID_SAMPLES, _("&Sample game..."), _("Select a sample configuration")); +#if ! (defined(__SMARTPHONE__) || defined(__POCKETPC__)) menuFile->AppendSeparator(); - menuFile->Append(ID_EXIT, _("E&xit\tAlt-X"), _("Quit this program")); + menuFile->Append(wxID_EXIT); - menuView->Append(ID_SHOWNAV, _("Navigation toolbox"), _("Show or hide toolbox"), TRUE); - menuView->Check (ID_SHOWNAV, TRUE); + menuView->Append(ID_SHOWNAV, _("Navigation &toolbox"), _("Show or hide toolbox"), wxITEM_CHECK); + menuView->Check(ID_SHOWNAV, true); menuView->AppendSeparator(); - menuView->Append(ID_ORIGIN, _("Absolute origin"), _("Go to (0, 0)")); - menuView->Append(ID_CENTER, _("Center of mass"), _("Find center of mass")); - menuView->Append(ID_NORTH, _("North"), _("Find northernmost cell")); - menuView->Append(ID_SOUTH, _("South"), _("Find southernmost cell")); - menuView->Append(ID_EAST, _("East"), _("Find easternmost cell")); - menuView->Append(ID_WEST, _("West"), _("Find westernmost cell")); +#endif + + menuView->Append(ID_ORIGIN, _("&Absolute origin"), _("Go to (0, 0)")); + menuView->Append(ID_CENTER, _("&Center of mass"), _("Find center of mass")); + menuView->Append(ID_NORTH, _("&North"), _("Find northernmost cell")); + menuView->Append(ID_SOUTH, _("&South"), _("Find southernmost cell")); + menuView->Append(ID_EAST, _("&East"), _("Find easternmost cell")); + menuView->Append(ID_WEST, _("&West"), _("Find westernmost cell")); menuView->AppendSeparator(); - menuView->Append(ID_ZOOMIN, _("Zoom &in\tCtrl-I")); - menuView->Append(ID_ZOOMOUT, _("Zoom &out\tCtrl-O")); + menuView->Append(wxID_ZOOM_IN, wxEmptyString, _("Zoom in")); + menuView->Append(wxID_ZOOM_OUT, wxEmptyString, _("Zoom out")); + menuView->Append(ID_INFO, _("&Description\tCtrl-D"), _("View pattern description")); menuGame->Append(ID_START, _("&Start\tCtrl-S"), _("Start")); menuGame->Append(ID_STEP, _("&Next\tCtrl-N"), _("Single step")); - menuGame->Append(ID_STOP, _("S&top\tCtrl-T"), _("Stop")); - menuGame->Enable(ID_STOP, FALSE); + menuGame->Append(wxID_STOP, wxEmptyString, _("Stop")); + menuGame->Enable(wxID_STOP, false); menuGame->AppendSeparator(); - menuGame->Append(ID_TOPSPEED, _("Top speed!"), _("Go as fast as possible")); + menuGame->Append(ID_TOPSPEED, _("T&op speed!"), _("Go as fast as possible")); + + menuHelp->Append(wxID_ABOUT, _("&About\tCtrl-A"), _("Show about dialog")); wxMenuBar *menuBar = new wxMenuBar(); menuBar->Append(menuFile, _("&File")); menuBar->Append(menuView, _("&View")); menuBar->Append(menuGame, _("&Game")); + menuBar->Append(menuHelp, _("&Help")); SetMenuBar(menuBar); // tool bar - wxBitmap tbBitmaps[5]; + wxBitmap tbBitmaps[7]; tbBitmaps[0] = wxBITMAP(reset); - tbBitmaps[1] = wxBITMAP(play); - tbBitmaps[2] = wxBITMAP(stop); - tbBitmaps[3] = wxBITMAP(zoomin); - tbBitmaps[4] = wxBITMAP(zoomout); + tbBitmaps[1] = wxBITMAP(open); + tbBitmaps[2] = wxBITMAP(zoomin); + tbBitmaps[3] = wxBITMAP(zoomout); + tbBitmaps[4] = wxBITMAP(info); + tbBitmaps[5] = wxBITMAP(play); + tbBitmaps[6] = wxBITMAP(stop); wxToolBar *toolBar = CreateToolBar(); toolBar->SetMargins(5, 5); toolBar->SetToolBitmapSize(wxSize(16, 16)); - ADD_TOOL(ID_RESET, tbBitmaps[0], _("Reset"), _("Start a new game")); - ADD_TOOL(ID_START, tbBitmaps[1], _("Start"), _("Start")); - ADD_TOOL(ID_STOP, tbBitmaps[2], _("Stop"), _("Stop")); + + ADD_TOOL(wxID_NEW, tbBitmaps[0], wxGetStockLabel(wxID_NEW, wxSTOCK_NOFLAGS), _("Start a new game")); +#ifndef __POCKETPC__ +#if wxUSE_FILEDLG + ADD_TOOL(wxID_OPEN, tbBitmaps[1], wxGetStockLabel(wxID_OPEN, wxSTOCK_NOFLAGS), _("Open an existing Life pattern")); +#endif // wxUSE_FILEDLG + + toolBar->AddSeparator(); + ADD_TOOL(wxID_ZOOM_IN, tbBitmaps[2], wxGetStockLabel(wxID_ZOOM_IN, wxSTOCK_NOFLAGS), _("Zoom in")); + ADD_TOOL(wxID_ZOOM_OUT, tbBitmaps[3], wxGetStockLabel(wxID_ZOOM_OUT, wxSTOCK_NOFLAGS), _("Zoom out")); + ADD_TOOL(ID_INFO, tbBitmaps[4], _("Description"), _("Show description")); toolBar->AddSeparator(); - ADD_TOOL(ID_ZOOMIN, tbBitmaps[3], _("Zoom in"), _("Zoom in")); - ADD_TOOL(ID_ZOOMOUT, tbBitmaps[4], _("Zoom out"), _("Zoom out")); +#endif // __POCKETPC__ + ADD_TOOL(ID_START, tbBitmaps[5], _("Start"), _("Start")); + ADD_TOOL(wxID_STOP, tbBitmaps[6], _("Stop"), _("Stop")); + toolBar->Realize(); - toolBar->EnableTool(ID_STOP, FALSE); // must be after Realize() ! + toolBar->EnableTool(wxID_STOP, false); // must be after Realize() ! +#if wxUSE_STATUSBAR // status bar CreateStatusBar(2); SetStatusText(_("Welcome to Life!")); +#endif // wxUSE_STATUSBAR // game and timer m_life = new Life(); m_timer = new wxTimer(this, ID_TIMER); - m_running = FALSE; - m_topspeed = FALSE; + m_running = false; + m_topspeed = false; m_interval = 500; m_tics = 0; @@ -250,14 +287,14 @@ LifeFrame::LifeFrame() : wxFrame((wxFrame *)0, -1, _("Life!"), wxPoint(200, 200) // and thus updating the text would result in a refresh of the canvas // if they belong to the same parent. - wxPanel *panel1 = new wxPanel(this, -1); - wxPanel *panel2 = new wxPanel(this, -1); + wxPanel *panel1 = new wxPanel(this, wxID_ANY); + wxPanel *panel2 = new wxPanel(this, wxID_ANY); // canvas m_canvas = new LifeCanvas(panel1, m_life); // info panel - m_text = new wxStaticText(panel2, -1, + m_text = new wxStaticText(panel2, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, @@ -266,7 +303,7 @@ LifeFrame::LifeFrame() : wxFrame((wxFrame *)0, -1, _("Life!"), wxPoint(200, 200) wxSlider *slider = new wxSlider(panel2, ID_SLIDER, 5, 1, 10, wxDefaultPosition, - wxSize(200, -1), + wxSize(200, wxDefaultCoord), wxSL_HORIZONTAL | wxSL_AUTOTICKS); UpdateInfoText(); @@ -276,30 +313,36 @@ LifeFrame::LifeFrame() : wxFrame((wxFrame *)0, -1, _("Life!"), wxPoint(200, 200) wxBoxSizer *sizer2 = new wxBoxSizer(wxVERTICAL); wxBoxSizer *sizer3 = new wxBoxSizer(wxVERTICAL); - sizer1->Add( new wxStaticLine(panel1, -1), 0, wxGROW ); +#if wxUSE_STATLINE + sizer1->Add( new wxStaticLine(panel1, wxID_ANY), 0, wxGROW ); +#endif // wxUSE_STATLINE sizer1->Add( m_canvas, 1, wxGROW | wxALL, 2 ); - sizer1->Add( new wxStaticLine(panel1, -1), 0, wxGROW ); +#if wxUSE_STATLINE + sizer1->Add( new wxStaticLine(panel1, wxID_ANY), 0, wxGROW ); +#endif // wxUSE_STATLINE panel1->SetSizer( sizer1 ); - panel1->SetAutoLayout( TRUE ); sizer1->Fit( panel1 ); sizer2->Add( m_text, 0, wxGROW | wxTOP, 4 ); sizer2->Add( slider, 0, wxCENTRE | wxALL, 4 ); + panel2->SetSizer( sizer2 ); - panel2->SetAutoLayout( TRUE ); sizer2->Fit( panel2 ); sizer3->Add( panel1, 1, wxGROW ); sizer3->Add( panel2, 0, wxGROW ); SetSizer( sizer3 ); - SetAutoLayout( TRUE ); + +#ifndef __WXWINCE__ sizer3->Fit( this ); // set minimum frame size sizer3->SetSizeHints( this ); - // navigator frame + // navigator frame - not appropriate for small devices m_navigator = new LifeNavigator(this); +#endif + } LifeFrame::~LifeFrame() @@ -325,46 +368,28 @@ void LifeFrame::UpdateUI() { // start / stop GetToolBar()->EnableTool(ID_START, !m_running); - GetToolBar()->EnableTool(ID_STOP, m_running); - GetMenuBar()->GetMenu(2)->Enable(ID_START, !m_running); - GetMenuBar()->GetMenu(2)->Enable(ID_STEP, !m_running); - GetMenuBar()->GetMenu(2)->Enable(ID_STOP, m_running); + GetToolBar()->EnableTool(wxID_STOP, m_running); + GetMenuBar()->Enable(ID_START, !m_running); + GetMenuBar()->Enable(ID_STEP, !m_running); + GetMenuBar()->Enable(wxID_STOP, m_running); + GetMenuBar()->Enable(ID_TOPSPEED, !m_topspeed); // zooming int cellsize = m_canvas->GetCellSize(); - GetToolBar()->EnableTool(ID_ZOOMIN, cellsize < 32); - GetToolBar()->EnableTool(ID_ZOOMOUT, cellsize > 1); - GetMenuBar()->GetMenu(1)->Enable(ID_ZOOMIN, cellsize < 32); - GetMenuBar()->GetMenu(1)->Enable(ID_ZOOMOUT, cellsize > 1); + GetToolBar()->EnableTool(wxID_ZOOM_IN, cellsize < 32); + GetToolBar()->EnableTool(wxID_ZOOM_OUT, cellsize > 1); + GetMenuBar()->Enable(wxID_ZOOM_IN, cellsize < 32); + GetMenuBar()->Enable(wxID_ZOOM_OUT, cellsize > 1); } -// event handlers +// Event handlers ----------------------------------------------------------- + +// OnMenu handles all events which don't have their own event handler void LifeFrame::OnMenu(wxCommandEvent& event) { switch (event.GetId()) { - case ID_START : OnStart(); break; - case ID_STEP : OnStep(); break; - case ID_STOP : OnStop(); break; - case ID_SHOWNAV : - { - bool checked = GetMenuBar()->GetMenu(1)->IsChecked(ID_SHOWNAV); - m_navigator->Show(checked); - break; - } - case ID_TOPSPEED: - { - m_running = TRUE; - m_topspeed = TRUE; - UpdateUI(); - while (m_running && m_topspeed) - { - OnStep(); - wxYield(); - } - break; - } - case ID_RESET: + case wxID_NEW: { // stop if it was running OnStop(); @@ -374,30 +399,86 @@ void LifeFrame::OnMenu(wxCommandEvent& event) UpdateInfoText(); break; } - case ID_ABOUT: + case wxID_ABOUT: { LifeAboutDialog dialog(this); dialog.ShowModal(); break; } - case ID_EXIT : + case wxID_EXIT: { - // TRUE is to force the frame to close - Close(TRUE); + // true is to force the frame to close + Close(true); + break; + } + case ID_SHOWNAV: + { + bool checked = GetMenuBar()->GetMenu(1)->IsChecked(ID_SHOWNAV); + if (m_navigator) + m_navigator->Show(checked); + break; + } + case ID_INFO: + { + wxString desc = m_life->GetDescription(); + + if ( desc.empty() ) + desc = _("Not available"); + + // should we make the description editable here? + wxMessageBox(desc, _("Description"), wxOK | wxICON_INFORMATION); + + break; + } + case ID_START : OnStart(); break; + case ID_STEP : OnStep(); break; + case wxID_STOP : OnStop(); break; + case ID_TOPSPEED: + { + m_running = true; + m_topspeed = true; + UpdateUI(); + while (m_running && m_topspeed) + { + OnStep(); + wxYield(); + } break; } } } -void LifeFrame::OnClose(wxCloseEvent& WXUNUSED(event)) +#if wxUSE_FILEDLG +void LifeFrame::OnOpen(wxCommandEvent& WXUNUSED(event)) { - // Stop if it was running; this is absolutely needed because - // the frame won't be actually destroyed until there are no - // more pending events, and this in turn won't ever happen - // if the timer is running faster than the window can redraw. - OnStop(); - Destroy(); + wxFileDialog filedlg(this, + _("Choose a file to open"), + wxEmptyString, + wxEmptyString, + _("Life patterns (*.lif)|*.lif|All files (*.*)|*.*"), + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if (filedlg.ShowModal() == wxID_OK) + { + wxFileInputStream stream(filedlg.GetPath()); + LifeReader reader(stream); + + // the reader handles errors itself, no need to do anything here + if (reader.IsOk()) + { + // stop if running and put the pattern + OnStop(); + m_life->Clear(); + m_life->SetPattern(reader.GetPattern()); + + // recenter canvas + m_canvas->Recenter(0, 0); + m_tics = 0; + UpdateInfoText(); + } + } } +#endif void LifeFrame::OnSamples(wxCommandEvent& WXUNUSED(event)) { @@ -409,11 +490,11 @@ void LifeFrame::OnSamples(wxCommandEvent& WXUNUSED(event)) if (dialog.ShowModal() == wxID_OK) { - const LifeShape shape = dialog.GetShape(); + const LifePattern pattern = dialog.GetPattern(); - // put the shape + // put the pattern m_life->Clear(); - m_life->SetShape(shape); + m_life->SetPattern(pattern); // recenter canvas m_canvas->Recenter(0, 0); @@ -426,12 +507,12 @@ void LifeFrame::OnZoom(wxCommandEvent& event) { int cellsize = m_canvas->GetCellSize(); - if ((event.GetId() == ID_ZOOMIN) && cellsize < 32) + if ((event.GetId() == wxID_ZOOM_IN) && cellsize < 32) { m_canvas->SetCellSize(cellsize * 2); UpdateUI(); } - else if ((event.GetId() == ID_ZOOMOUT) && cellsize > 1) + else if ((event.GetId() == wxID_ZOOM_OUT) && cellsize > 1) { m_canvas->SetCellSize(cellsize / 2); UpdateUI(); @@ -440,7 +521,7 @@ void LifeFrame::OnZoom(wxCommandEvent& event) void LifeFrame::OnNavigate(wxCommandEvent& event) { - Cell c; + LifeCell c; switch (event.GetId()) { @@ -449,6 +530,9 @@ void LifeFrame::OnNavigate(wxCommandEvent& event) case ID_WEST: c = m_life->FindWest(); break; case ID_EAST: c = m_life->FindEast(); break; case ID_CENTER: c = m_life->FindCenter(); break; + default : + wxFAIL; + // Fall through! case ID_ORIGIN: c.i = c.j = 0; break; } @@ -464,7 +548,7 @@ void LifeFrame::OnSlider(wxScrollEvent& event) OnStop(); OnStart(); } - + UpdateInfoText(); } @@ -473,12 +557,22 @@ void LifeFrame::OnTimer(wxTimerEvent& WXUNUSED(event)) OnStep(); } +void LifeFrame::OnClose(wxCloseEvent& WXUNUSED(event)) +{ + // Stop if it was running; this is absolutely needed because + // the frame won't be actually destroyed until there are no + // more pending events, and this in turn won't ever happen + // if the timer is running faster than the window can redraw. + OnStop(); + Destroy(); +} + void LifeFrame::OnStart() { if (!m_running) { m_timer->Start(m_interval); - m_running = TRUE; + m_running = true; UpdateUI(); } } @@ -488,8 +582,8 @@ void LifeFrame::OnStop() if (m_running) { m_timer->Stop(); - m_running = FALSE; - m_topspeed = FALSE; + m_running = false; + m_topspeed = false; UpdateUI(); } } @@ -511,29 +605,29 @@ void LifeFrame::OnStep() // -------------------------------------------------------------------------- LifeNavigator::LifeNavigator(wxWindow *parent) - : wxMiniFrame(parent, -1, + : wxMiniFrame(parent, wxID_ANY, _("Navigation"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxSIMPLE_BORDER) { - wxPanel *panel = new wxPanel(this, -1); + wxPanel *panel = new wxPanel(this, wxID_ANY); wxBoxSizer *sizer1 = new wxBoxSizer(wxVERTICAL); wxBoxSizer *sizer2 = new wxBoxSizer(wxHORIZONTAL); // create bitmaps and masks for the buttons wxBitmap bmpn = wxBITMAP(north), - bmpw = wxBITMAP(west), + bmpw = wxBITMAP(west), bmpc = wxBITMAP(center), - bmpe = wxBITMAP(east), + bmpe = wxBITMAP(east), bmps = wxBITMAP(south); -#ifdef __WXMSW__ +#if !defined(__WXGTK__) && !defined(__WXMOTIF__) && !defined(__WXMAC__) bmpn.SetMask(new wxMask(bmpn, *wxLIGHT_GREY)); bmpw.SetMask(new wxMask(bmpw, *wxLIGHT_GREY)); bmpc.SetMask(new wxMask(bmpc, *wxLIGHT_GREY)); - bmpe.SetMask(new wxMask(bmpe, *wxLIGHT_GREY)); + bmpe.SetMask(new wxMask(bmpe, *wxLIGHT_GREY)); bmps.SetMask(new wxMask(bmps, *wxLIGHT_GREY)); #endif @@ -545,11 +639,13 @@ LifeNavigator::LifeNavigator(wxWindow *parent) *be = new wxBitmapButton(panel, ID_EAST , bmpe), *bs = new wxBitmapButton(panel, ID_SOUTH, bmps); +#if wxUSE_TOOLTIPS bn->SetToolTip(_("Find northernmost cell")); bw->SetToolTip(_("Find westernmost cell")); bc->SetToolTip(_("Find center of mass")); be->SetToolTip(_("Find easternmost cell")); bs->SetToolTip(_("Find southernmost cell")); +#endif // add buttons to sizers sizer2->Add( bw, 0, wxCENTRE | wxWEST, 4 ); @@ -559,11 +655,13 @@ LifeNavigator::LifeNavigator(wxWindow *parent) sizer1->Add( sizer2 ); sizer1->Add( bs, 0, wxCENTRE | wxSOUTH, 4 ); - // set the miniframe size + // set the panel and miniframe size panel->SetSizer(sizer1); - panel->SetAutoLayout(TRUE); - sizer1->Fit(this); - sizer1->SetSizeHints(this); + + sizer1->Fit(panel); + SetClientSize(panel->GetSize()); + wxSize sz = GetSize(); + SetSizeHints(sz.x, sz.y, sz.x, sz.y); // move it to a sensible position wxRect parentRect = parent->GetRect(); @@ -575,11 +673,12 @@ LifeNavigator::LifeNavigator(wxWindow *parent) Move(x, y); // done - Show(TRUE); + Show(true); } void LifeNavigator::OnClose(wxCloseEvent& event) { + // avoid if we can if (event.CanVeto()) event.Veto(); else @@ -593,8 +692,14 @@ void LifeNavigator::OnClose(wxCloseEvent& event) // canvas constructor LifeCanvas::LifeCanvas(wxWindow *parent, Life *life, bool interactive) - : wxWindow(parent, -1, wxPoint(0, 0), wxSize(100, 100), - wxSUNKEN_BORDER) + : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(100, 100), + wxFULL_REPAINT_ON_RESIZE +#if !defined(__SMARTPHONE__) && !defined(__POCKETPC__) + |wxSUNKEN_BORDER +#else + |wxSIMPLE_BORDER +#endif + ) { m_life = life; m_interactive = interactive; @@ -609,12 +714,12 @@ LifeCanvas::LifeCanvas(wxWindow *parent, Life *life, bool interactive) SetCursor(*wxCROSS_CURSOR); // reduce flicker if wxEVT_ERASE_BACKGROUND is not available - SetBackgroundColour(*wxWHITE); + SetBackgroundColour(*wxWHITE); } LifeCanvas::~LifeCanvas() { - delete m_life; + delete m_life; } // recenter at the given position @@ -624,7 +729,7 @@ void LifeCanvas::Recenter(wxInt32 i, wxInt32 j) m_viewportY = j - m_viewportH / 2; // redraw everything - Refresh(FALSE); + Refresh(false); } // set the cell size and refresh display @@ -641,7 +746,7 @@ void LifeCanvas::SetCellSize(int cellsize) GetClientSize(&w, &h); m_viewportW = (w + m_cellsize - 1) / m_cellsize; m_viewportH = (h + m_cellsize - 1) / m_cellsize; - + // recenter m_viewportX = cx - m_viewportW / 2; m_viewportY = cy - m_viewportH / 2; @@ -654,8 +759,8 @@ void LifeCanvas::SetCellSize(int cellsize) m_thumbX = m_viewportW; m_thumbY = m_viewportH; } - - Refresh(FALSE); + + Refresh(false); } // draw a cell @@ -666,9 +771,7 @@ void LifeCanvas::DrawCell(wxInt32 i, wxInt32 j, bool alive) dc.SetPen(alive? *wxBLACK_PEN : *wxWHITE_PEN); dc.SetBrush(alive? *wxBLACK_BRUSH : *wxWHITE_BRUSH); - dc.BeginDrawing(); DrawCell(i, j, dc); - dc.EndDrawing(); } void LifeCanvas::DrawCell(wxInt32 i, wxInt32 j, wxDC &dc) @@ -696,16 +799,14 @@ void LifeCanvas::DrawChanged() wxClientDC dc(this); size_t ncells; - Cell *cells; - bool done = FALSE; + LifeCell *cells; + bool done = false; m_life->BeginFind(m_viewportX, m_viewportY, m_viewportX + m_viewportW, m_viewportY + m_viewportH, - TRUE); - - dc.BeginDrawing(); + true); if (m_cellsize == 1) { @@ -725,11 +826,10 @@ void LifeCanvas::DrawChanged() for (size_t m = 0; m < ncells; m++) DrawCell(cells[m].i, cells[m].j, dc); } - dc.EndDrawing(); } // event handlers -void LifeCanvas::OnPaint(wxPaintEvent& event) +void LifeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); wxRect rect = GetUpdateRegion().GetBox(); @@ -748,14 +848,12 @@ void LifeCanvas::OnPaint(wxPaintEvent& event) j1 = YToCell(y + h - 1); size_t ncells; - Cell *cells; - bool done = FALSE; + LifeCell *cells; - m_life->BeginFind(i0, j0, i1, j1, FALSE); - done = m_life->FindMore(&cells, &ncells); + m_life->BeginFind(i0, j0, i1, j1, false); + bool done = m_life->FindMore(&cells, &ncells); // erase all damaged cells and draw the grid - dc.BeginDrawing(); dc.SetBrush(*wxWHITE_BRUSH); if (m_cellsize <= 2) @@ -793,8 +891,6 @@ void LifeCanvas::OnPaint(wxPaintEvent& event) // last set for (size_t m = 0; m < ncells; m++) DrawCell(cells[m].i, cells[m].j, dc); - - dc.EndDrawing(); } void LifeCanvas::OnMouse(wxMouseEvent& event) @@ -806,24 +902,33 @@ void LifeCanvas::OnMouse(wxMouseEvent& event) wxInt32 i = XToCell( event.GetX() ); wxInt32 j = YToCell( event.GetY() ); +#if wxUSE_STATUSBAR // set statusbar text wxString msg; msg.Printf(_("Cell: (%d, %d)"), i, j); ((LifeFrame *) wxGetApp().GetTopWindow())->SetStatusText(msg, 1); +#endif // wxUSE_STATUSBAR + + // NOTE that wxMouseEvent::LeftDown() and wxMouseEvent::LeftIsDown() + // have different semantics. The first one is used to signal that the + // button was just pressed (i.e., in "button down" events); the second + // one just describes the current status of the button, independently + // of the mouse event type. LeftIsDown is typically used in "mouse + // move" events, to test if the button is _still_ pressed. - // button pressed? + // is the button down? if (!event.LeftIsDown()) { m_status = MOUSE_NOACTION; return; } - // button just pressed? - if (m_status == MOUSE_NOACTION) + // was it pressed just now? + if (event.LeftDown()) { - // yes, update status and toggle this cell + // yes: start a new action and toggle this cell m_status = (m_life->IsAlive(i, j)? MOUSE_ERASING : MOUSE_DRAWING); - + m_mi = i; m_mj = j; m_life->SetCell(i, j, m_status == MOUSE_DRAWING); @@ -831,13 +936,13 @@ void LifeCanvas::OnMouse(wxMouseEvent& event) } else if ((m_mi != i) || (m_mj != j)) { + // no: continue ongoing action bool alive = (m_status == MOUSE_DRAWING); // prepare DC and pen + brush to optimize drawing wxClientDC dc(this); dc.SetPen(alive? *wxBLACK_PEN : *wxWHITE_PEN); dc.SetBrush(alive? *wxBLACK_BRUSH : *wxWHITE_BRUSH); - dc.BeginDrawing(); // draw a line of cells using Bresenham's algorithm wxInt32 d, ii, jj, di, ai, si, dj, aj, sj; @@ -850,12 +955,12 @@ void LifeCanvas::OnMouse(wxMouseEvent& event) ii = m_mi; jj = m_mj; - + if (ai > aj) { // iterate over i - d = aj - (ai >> 1); - + d = aj - (ai >> 1); + while (ii != i) { m_life->SetCell(ii, jj, alive); @@ -863,7 +968,7 @@ void LifeCanvas::OnMouse(wxMouseEvent& event) if (d >= 0) { jj += sj; - d -= ai; + d -= ai; } ii += si; d += aj; @@ -881,7 +986,7 @@ void LifeCanvas::OnMouse(wxMouseEvent& event) if (d >= 0) { ii += si; - d -= aj; + d -= aj; } jj += sj; d += ai; @@ -893,8 +998,6 @@ void LifeCanvas::OnMouse(wxMouseEvent& event) DrawCell(ii, jj, dc); m_mi = ii; m_mj = jj; - - dc.EndDrawing(); } ((LifeFrame *) wxGetApp().GetTopWindow())->UpdateInfoText(); @@ -931,57 +1034,71 @@ void LifeCanvas::OnSize(wxSizeEvent& event) void LifeCanvas::OnScroll(wxScrollWinEvent& event) { - WXTYPE type = event.GetEventType(); + WXTYPE type = (WXTYPE)event.GetEventType(); int pos = event.GetPosition(); int orient = event.GetOrientation(); // calculate scroll increment int scrollinc = 0; - switch (type) + if (type == wxEVT_SCROLLWIN_TOP) { - case wxEVT_SCROLLWIN_TOP: - { - if (orient == wxHORIZONTAL) - scrollinc = -m_viewportW; - else - scrollinc = -m_viewportH; - break; - } - case wxEVT_SCROLLWIN_BOTTOM: - { - if (orient == wxHORIZONTAL) - scrollinc = m_viewportW; - else - scrollinc = m_viewportH; - break; - } - case wxEVT_SCROLLWIN_LINEUP: scrollinc = -1; break; - case wxEVT_SCROLLWIN_LINEDOWN: scrollinc = +1; break; - case wxEVT_SCROLLWIN_PAGEUP: scrollinc = -10; break; - case wxEVT_SCROLLWIN_PAGEDOWN: scrollinc = +10; break; - case wxEVT_SCROLLWIN_THUMBTRACK: + if (orient == wxHORIZONTAL) + scrollinc = -m_viewportW; + else + scrollinc = -m_viewportH; + } + else + if (type == wxEVT_SCROLLWIN_BOTTOM) + { + if (orient == wxHORIZONTAL) + scrollinc = m_viewportW; + else + scrollinc = m_viewportH; + } + else + if (type == wxEVT_SCROLLWIN_LINEUP) + { + scrollinc = -1; + } + else + if (type == wxEVT_SCROLLWIN_LINEDOWN) + { + scrollinc = +1; + } + else + if (type == wxEVT_SCROLLWIN_PAGEUP) + { + scrollinc = -10; + } + else + if (type == wxEVT_SCROLLWIN_PAGEDOWN) + { + scrollinc = +10; + } + else + if (type == wxEVT_SCROLLWIN_THUMBTRACK) + { + if (orient == wxHORIZONTAL) { - if (orient == wxHORIZONTAL) - { - scrollinc = pos - m_thumbX; - m_thumbX = pos; - } - else - { - scrollinc = pos - m_thumbY; - m_thumbY = pos; - } - break; + scrollinc = pos - m_thumbX; + m_thumbX = pos; } - case wxEVT_SCROLLWIN_THUMBRELEASE: + else { - m_thumbX = m_viewportW; - m_thumbY = m_viewportH; + scrollinc = pos - m_thumbY; + m_thumbY = pos; } } + else + if (type == wxEVT_SCROLLWIN_THUMBRELEASE) + { + m_thumbX = m_viewportW; + m_thumbY = m_viewportH; + } -#ifdef __WXGTK__ // what about Motif? - // wxGTK updates the thumb automatically (wxMSW doesn't); reset it back +#if defined(__WXGTK__) || defined(__WXMOTIF__) + // wxGTK and wxMotif update the thumb automatically (wxMSW doesn't); + // so reset it back as we always want it to be in the same position. if (type != wxEVT_SCROLLWIN_THUMBTRACK) { SetScrollbar(wxHORIZONTAL, m_viewportW, m_viewportW, 3 * m_viewportW); @@ -990,7 +1107,7 @@ void LifeCanvas::OnScroll(wxScrollWinEvent& event) #endif if (scrollinc == 0) return; - + // scroll the window and adjust the viewport if (orient == wxHORIZONTAL) { @@ -999,7 +1116,7 @@ void LifeCanvas::OnScroll(wxScrollWinEvent& event) } else { - m_viewportY += scrollinc; + m_viewportY += scrollinc; ScrollWindow( 0, -m_cellsize * scrollinc, (const wxRect *) NULL); } } @@ -1008,5 +1125,3 @@ void LifeCanvas::OnEraseBackground(wxEraseEvent& WXUNUSED(event)) { // do nothing. I just don't want the background to be erased, you know. } - -