]>
git.saurik.com Git - wxWidgets.git/blob - demos/life/life.cpp
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: The game of life, created by J. H. Conway
4 // Author: Guillermo Rodriguez Garcia, <guille@iies.es>
8 // Copyright: (c) 2000, Guillermo Rodriguez Garcia
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ==========================================================================
14 // ==========================================================================
16 // --------------------------------------------------------------------------
18 // --------------------------------------------------------------------------
21 #pragma implementation "life.h"
24 // for compilers that support precompilation, includes "wx/wx.h"
25 #include "wx/wxprec.h"
31 // for all others, include the necessary headers
36 #include "wx/statline.h"
42 // --------------------------------------------------------------------------
44 // --------------------------------------------------------------------------
46 #if defined(__WXGTK__) || defined(__WXMOTIF__)
47 // the application icon
48 #include "mondrian.xpm"
50 // bitmap buttons for the toolbar
51 #include "bitmaps/reset.xpm"
52 #include "bitmaps/play.xpm"
53 #include "bitmaps/stop.xpm"
56 // --------------------------------------------------------------------------
58 // --------------------------------------------------------------------------
60 // IDs for the controls and the menu commands
63 // menu items and toolbar buttons
74 // speed selection slider
78 // --------------------------------------------------------------------------
79 // event tables and other macros for wxWindows
80 // --------------------------------------------------------------------------
83 BEGIN_EVENT_TABLE(LifeFrame
, wxFrame
)
84 EVT_MENU (ID_NEWGAME
, LifeFrame::OnNewGame
)
85 EVT_MENU (ID_SAMPLES
, LifeFrame::OnSamples
)
86 EVT_MENU (ID_ABOUT
, LifeFrame::OnMenu
)
87 EVT_MENU (ID_EXIT
, LifeFrame::OnMenu
)
88 EVT_MENU (ID_CLEAR
, LifeFrame::OnMenu
)
89 EVT_MENU (ID_START
, LifeFrame::OnMenu
)
90 EVT_MENU (ID_STEP
, LifeFrame::OnMenu
)
91 EVT_MENU (ID_STOP
, LifeFrame::OnMenu
)
92 EVT_MENU (ID_WRAP
, LifeFrame::OnMenu
)
93 EVT_COMMAND_SCROLL (ID_SLIDER
, LifeFrame::OnSlider
)
96 BEGIN_EVENT_TABLE(LifeCanvas
, wxScrolledWindow
)
97 EVT_PAINT ( LifeCanvas::OnPaint
)
98 EVT_SIZE ( LifeCanvas::OnSize
)
99 EVT_MOUSE_EVENTS ( LifeCanvas::OnMouse
)
103 // Create a new application object
104 IMPLEMENT_APP(LifeApp
)
106 // ==========================================================================
108 // ==========================================================================
111 #define ADD_TOOL(a, b, c, d) \
112 toolBar->AddTool(a, b, wxNullBitmap, FALSE, -1, -1, (wxObject *)0, c, d)
114 #define GET_FRAME() \
115 ((LifeFrame *) wxGetApp().GetTopWindow())
117 // --------------------------------------------------------------------------
119 // --------------------------------------------------------------------------
121 // `Main program' equivalent: the program execution "starts" here
122 bool LifeApp::OnInit()
124 // create the main application window
125 LifeFrame
*frame
= new LifeFrame();
127 // show it and tell the application that it's our main window
131 // enter the main message loop and run the app
135 // --------------------------------------------------------------------------
137 // --------------------------------------------------------------------------
140 LifeFrame::LifeFrame() : wxFrame((wxFrame
*)0, -1, _("Life!"), wxPoint(50, 50))
143 SetIcon(wxICON(mondrian
));
146 wxMenu
*menuFile
= new wxMenu("", wxMENU_TEAROFF
);
147 wxMenu
*menuGame
= new wxMenu("", wxMENU_TEAROFF
);
149 menuFile
->Append(ID_NEWGAME
, _("New game..."), _("Start a new game"));
150 menuFile
->Append(ID_SAMPLES
, _("Sample game..."), _("Select a sample configuration"));
151 menuFile
->AppendSeparator();
152 menuFile
->Append(ID_ABOUT
, _("&About...\tCtrl-A"), _("Show about dialog"));
153 menuFile
->AppendSeparator();
154 menuFile
->Append(ID_EXIT
, _("E&xit\tAlt-X"), _("Quit this program"));
156 menuGame
->Append(ID_CLEAR
, _("&Clear\tCtrl-C"), _("Clear game field"));
157 menuGame
->Append(ID_START
, _("&Start\tCtrl-S"), _("Start"));
158 menuGame
->Append(ID_STEP
, _("&Next\tCtrl-N"), _("Single step"));
159 menuGame
->Append(ID_STOP
, _("S&top\tCtrl-T"), _("Stop"));
160 menuGame
->Enable(ID_STOP
, FALSE
);
161 menuGame
->AppendSeparator();
162 menuGame
->Append(ID_WRAP
, _("&Wraparound\tCtrl-W"), _("Wrap around borders"), TRUE
);
163 menuGame
->Check (ID_WRAP
, TRUE
);
165 wxMenuBar
*menuBar
= new wxMenuBar();
166 menuBar
->Append(menuFile
, _("&File"));
167 menuBar
->Append(menuGame
, _("&Game"));
171 wxBitmap tbBitmaps
[3];
173 tbBitmaps
[0] = wxBITMAP(reset
);
174 tbBitmaps
[1] = wxBITMAP(play
);
175 tbBitmaps
[2] = wxBITMAP(stop
);
177 wxToolBar
*toolBar
= CreateToolBar();
178 toolBar
->SetMargins(5, 5);
179 toolBar
->SetToolBitmapSize(wxSize(16, 16));
180 ADD_TOOL(ID_CLEAR
, tbBitmaps
[0], _("Clear"), _("Clear game board"));
181 ADD_TOOL(ID_START
, tbBitmaps
[1], _("Start"), _("Start"));
182 ADD_TOOL(ID_STOP
, tbBitmaps
[2], _("Stop"), _("Stop"));
183 toolBar
->EnableTool(ID_STOP
, FALSE
);
188 SetStatusText(_("Welcome to Life!"));
191 wxPanel
*panel
= new wxPanel(this, -1);
192 m_life
= new Life(20, 20);
193 m_canvas
= new LifeCanvas(panel
, m_life
);
194 m_timer
= new LifeTimer();
197 m_text
= new wxStaticText(panel
, -1, "");
201 wxSlider
*slider
= new wxSlider(panel
, ID_SLIDER
,
205 wxSL_HORIZONTAL
| wxSL_AUTOTICKS
);
208 wxBoxSizer
*sizer
= new wxBoxSizer(wxVERTICAL
);
209 sizer
->Add(new wxStaticLine(panel
, -1), 0, wxGROW
| wxCENTRE
);
210 sizer
->Add(m_canvas
, 1, wxGROW
| wxCENTRE
| wxALL
, 5);
211 sizer
->Add(new wxStaticLine(panel
, -1), 0, wxGROW
| wxCENTRE
);
212 sizer
->Add(m_text
, 0, wxCENTRE
| wxTOP
, 5);
213 sizer
->Add(slider
, 0, wxCENTRE
| wxALL
, 5);
214 panel
->SetSizer(sizer
);
215 panel
->SetAutoLayout(TRUE
);
217 sizer
->SetSizeHints(this);
220 LifeFrame::~LifeFrame()
226 void LifeFrame::UpdateInfoText()
230 msg
.Printf(_("Generation: %u, Interval: %u ms"), m_tics
, m_interval
);
231 m_text
->SetLabel(msg
);
235 void LifeFrame::OnMenu(wxCommandEvent
& event
)
237 switch (event
.GetId())
239 case ID_START
: OnStart(); break;
240 case ID_STEP
: OnTimer(); break;
241 case ID_STOP
: OnStop(); break;
244 bool checked
= GetMenuBar()->GetMenu(1)->IsChecked(ID_WRAP
);
245 m_life
->SetBorderWrap(checked
);
252 m_canvas
->DrawEverything(TRUE
);
253 m_canvas
->Refresh(FALSE
);
261 _("This is the about dialog of the Life! sample.\n"
262 "(c) 2000 Guillermo Rodriguez Garcia"),
264 wxOK
| wxICON_INFORMATION
,
270 // TRUE is to force the frame to close
277 void LifeFrame::OnNewGame(wxCommandEvent
& WXUNUSED(event
))
279 int w
= m_life
->GetWidth();
280 int h
= m_life
->GetHeight();
282 // stop if it was running
286 LifeNewGameDialog
dialog(this, &w
, &h
);
289 if (dialog
.ShowModal() == wxID_OK
)
292 if (w
>= LIFE_MIN
&& w
<= LIFE_MAX
&&
293 h
>= LIFE_MIN
&& h
<= LIFE_MAX
)
297 m_life
->Create(w
, h
);
308 msg
.Printf(_("Both dimensions must be within %u and %u.\n"),
310 wxMessageBox(msg
, _("Error!"), wxOK
| wxICON_EXCLAMATION
, this);
315 void LifeFrame::OnSamples(wxCommandEvent
& WXUNUSED(event
))
317 // stop if it was running
321 LifeSamplesDialog
dialog(this);
324 if (dialog
.ShowModal() == wxID_OK
)
326 int result
= dialog
.GetValue();
331 int gw
= g_shapes
[result
].m_fieldWidth
;
332 int gh
= g_shapes
[result
].m_fieldHeight
;
333 int wrap
= g_shapes
[result
].m_wrap
;
335 // set wraparound (don't ask the user)
336 m_life
->SetBorderWrap(wrap
);
337 GetMenuBar()->GetMenu(1)->Check(ID_WRAP
, wrap
);
339 // need to resize the game field?
340 if (gw
> m_life
->GetWidth() || gh
> m_life
->GetHeight())
343 s
.Printf(_("Your game field is too small for this configuration.\n"
344 "It is recommended to resize it to %u x %u. Proceed?\n"),
347 if (wxMessageBox(s
, _("Question"), wxYES_NO
| wxICON_QUESTION
, this) == wxYES
)
350 m_life
->Create(gw
, gh
);
355 m_life
->SetShape(g_shapes
[result
]);
357 // tell the canvas about the change
365 void LifeFrame::OnStart()
369 GetToolBar()->EnableTool(ID_START
, FALSE
);
370 GetToolBar()->EnableTool(ID_STOP
, TRUE
);
371 GetMenuBar()->GetMenu(1)->Enable(ID_START
, FALSE
);
372 GetMenuBar()->GetMenu(1)->Enable(ID_STEP
, FALSE
);
373 GetMenuBar()->GetMenu(1)->Enable(ID_STOP
, TRUE
);
375 m_timer
->Start(m_interval
);
380 void LifeFrame::OnStop()
384 GetToolBar()->EnableTool(ID_START
, TRUE
);
385 GetToolBar()->EnableTool(ID_STOP
, FALSE
);
386 GetMenuBar()->GetMenu(1)->Enable(ID_START
, TRUE
);
387 GetMenuBar()->GetMenu(1)->Enable(ID_STEP
, TRUE
);
388 GetMenuBar()->GetMenu(1)->Enable(ID_STOP
, FALSE
);
395 void LifeFrame::OnTimer()
397 if (m_life
->NextTic())
403 m_canvas
->DrawEverything();
404 m_canvas
->Refresh(FALSE
);
407 void LifeFrame::OnSlider(wxScrollEvent
& event
)
409 m_interval
= event
.GetPosition() * 100;
411 // restart timer if running, to set the new interval
415 m_timer
->Start(m_interval
);
421 // --------------------------------------------------------------------------
423 // --------------------------------------------------------------------------
425 void LifeTimer::Notify()
427 GET_FRAME()->OnTimer();
430 // --------------------------------------------------------------------------
432 // --------------------------------------------------------------------------
434 // canvas constructor
435 LifeCanvas::LifeCanvas(wxWindow
*parent
, Life
*life
, bool interactive
)
436 : wxScrolledWindow(parent
, -1, wxPoint(0, 0), wxSize(100, 100))
439 m_interactive
= interactive
;
445 LifeCanvas::~LifeCanvas()
450 void LifeCanvas::Reset()
455 m_status
= MOUSE_NOACTION
;
456 m_width
= CellToCoord(m_life
->GetWidth()) + 1;
457 m_height
= CellToCoord(m_life
->GetHeight()) + 1;
458 m_bmp
= new wxBitmap(m_width
, m_height
);
459 wxCoord w
= GetClientSize().GetX();
460 wxCoord h
= GetClientSize().GetY();
461 m_xoffset
= (w
> m_width
)? ((w
- m_width
) / 2) : 0;
462 m_yoffset
= (h
> m_height
)? ((h
- m_height
) / 2) : 0;
465 DrawEverything(TRUE
);
466 SetScrollbars(10, 10, (m_width
+ 9) / 10, (m_height
+ 9) / 10);
469 void LifeCanvas::DrawEverything(bool force
)
473 dc
.SelectObject(*m_bmp
);
477 const CellArray
*cells
=
478 force
? m_life
->GetCells() : m_life
->GetChangedCells();
480 for (unsigned i
= 0; i
< cells
->GetCount(); i
++)
481 DrawCell(cells
->Item(i
), dc
);
483 // bounding rectangle (always drawn - better than clipping region)
484 dc
.SetPen(*wxBLACK_PEN
);
485 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
486 dc
.DrawRectangle(0, 0, m_width
, m_height
);
489 dc
.SelectObject(wxNullBitmap
);
492 void LifeCanvas::DrawCell(Cell c
)
496 dc
.SelectObject(*m_bmp
);
499 dc
.SetClippingRegion(1, 1, m_width
- 2, m_height
- 2);
503 dc
.SelectObject(wxNullBitmap
);
506 void LifeCanvas::DrawCell(Cell c
, wxDC
&dc
)
508 if (m_life
->IsAlive(c
))
510 dc
.SetPen(*wxBLACK_PEN
);
511 dc
.SetBrush(*wxBLACK_BRUSH
);
512 dc
.DrawRectangle(CellToCoord( m_life
->GetX(c
) ),
513 CellToCoord( m_life
->GetY(c
) ),
519 dc
.SetPen(*wxLIGHT_GREY_PEN
);
520 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
521 dc
.DrawRectangle(CellToCoord( m_life
->GetX(c
) ),
522 CellToCoord( m_life
->GetY(c
) ),
525 dc
.SetPen(*wxWHITE_PEN
);
526 dc
.SetBrush(*wxWHITE_BRUSH
);
527 dc
.DrawRectangle(CellToCoord( m_life
->GetX(c
) ) + 1,
528 CellToCoord( m_life
->GetY(c
) ) + 1,
535 void LifeCanvas::OnPaint(wxPaintEvent
& event
)
540 wxRegionIterator
upd(GetUpdateRegion());
541 wxCoord x
, y
, w
, h
, xx
, yy
;
544 memdc
.SelectObject(*m_bmp
);
552 CalcUnscrolledPosition(x
, y
, &xx
, &yy
);
554 dc
.Blit(x
, y
, w
, h
, &memdc
, xx
- m_xoffset
, yy
- m_yoffset
);
558 memdc
.SelectObject(wxNullBitmap
);
562 void LifeCanvas::OnMouse(wxMouseEvent
& event
)
567 int x
, y
, xx
, yy
, i
, j
;
569 // which cell are we pointing at?
572 CalcUnscrolledPosition(x
, y
, &xx
, &yy
);
573 i
= CoordToCell( xx
- m_xoffset
);
574 j
= CoordToCell( yy
- m_yoffset
);
576 // adjust x, y to point to the upper left corner of the cell
577 CalcScrolledPosition( CellToCoord(i
) + m_xoffset
,
578 CellToCoord(j
) + m_yoffset
,
581 // set cursor shape and statusbar text
582 if (i
< 0 || i
>= m_life
->GetWidth() ||
583 j
< 0 || j
>= m_life
->GetHeight())
585 GET_FRAME()->SetStatusText(wxEmptyString
, 1);
586 SetCursor(*wxSTANDARD_CURSOR
);
591 msg
.Printf(_("Cell: (%u, %u)"), i
, j
);
592 GET_FRAME()->SetStatusText(msg
, 1);
593 SetCursor(*wxCROSS_CURSOR
);
597 if (!event
.LeftIsDown())
599 m_status
= MOUSE_NOACTION
;
601 else if (i
>= 0 && i
< m_life
->GetWidth() &&
602 j
>= 0 && j
< m_life
->GetHeight())
604 bool alive
= m_life
->IsAlive(i
, j
);
606 // if just pressed, update status
607 if (m_status
== MOUSE_NOACTION
)
608 m_status
= (alive
? MOUSE_ERASING
: MOUSE_DRAWING
);
610 // toggle cell and refresh if needed
611 if (((m_status
== MOUSE_ERASING
) && alive
) ||
612 ((m_status
== MOUSE_DRAWING
) && !alive
))
614 wxRect
rect(x
, y
, m_cellsize
+ 1, m_cellsize
+ 1);
615 DrawCell( m_life
->SetCell(i
, j
, !alive
) );
616 Refresh(FALSE
, &rect
);
621 void LifeCanvas::OnSize(wxSizeEvent
& event
)
623 wxCoord w
= event
.GetSize().GetX();
624 wxCoord h
= event
.GetSize().GetY();
625 m_xoffset
= (w
> m_width
)? ((w
- m_width
) / 2) : 0;
626 m_yoffset
= (h
> m_height
)? ((h
- m_height
) / 2) : 0;
628 // allow default processing