Apr
28
2008
In the process of setting up MySQL 5.0.32 on Debian Etch recently I ran into a frustrating problem. The server was installed correctly. The users were set up correctly, with the right established roles. I could connect to the server fine from the machine it was installed on, but not from any other machine on the LAN. I had run through the configuration in the MySQL Administrator three or four times to make sure I wasn’t missing anything, principally to keep squinting at the “Disable networking” checkbox to make sure it was clear. It was clear. It WAS CLEAR, dammit. But no matter: I could connect from the local system, but not from the Windows box sitting next to it. Trying from the MySQL Administrator program yielded the following…
From the command line it looked like this…
ERROR 2003 (HY000): Can’t connect to MySQL server on ‘192.168.0.105′ (111)
I Googled every combination of terms I could think of. At one point I ran across a discussion where someone seemed to be having the same problem, and about halfway down the page someone else suggested editing something in a conf file. Figuring I didn’t want to mess with conf files just yet (there MUST be a checkbox I missed SOMEWHERE) I kept butting my head against the problem. Finally, in a flash of what you wouldn’t call inspiration, given that I had been making no progress for thirty minutes, I tried the actual IP address rather than ‘localhost’ from the local host. No go. Aha… so it certainly acts like networking is disabled.
I went back and reread that discussion, and that’s when I learned about the variable bind-address in /etc/mysql/my.cnf. Here’s what it looks like after a default install…
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
bind-address = 127.0.0.1
What it means is that in the default install mysqld will only bind to localhost (127.0.0.1). That interface has no communication with the outside world. The solution is just to comment it out, and suddenly everything works. You would think that the default install would also show “Disable networking” as “true” in the administrator when networking is… uhm… disabled, but I’m probably just not getting it yet.
Apr
27
2008
using System ;
using System.ComponentModel ;
using System.IO ;
using System.Net ;
using System.Text ;
using System.Runtime.Serialization.Formatters.Binary ;
using System.Collections.Generic ;
using System.Windows ;
using System.Windows.Media ;
using System.Windows.Documents ;
using System.Windows.Controls ;
using System.Windows.Input ;
using System.Windows.Shapes ;
namespace AvalonLife
{
/// <summary>
/// UIStateChanges
///
/// This enum provides indicators for the user interface states
/// that the program can be in. Used to call UIStateChange().
/// </summary>
enum UIStateChanges
{
ModelCreated,
ModelSaved,
ModelSavedAs,
ModelLoadedFromFile,
ModelLoadedFromNet,
ModelCellEdited,
ModelRun,
ModelPaused,
ModelHalted,
ModelReset,
ModelPropertiesEdited
}
/// <summary>
/// Used to indicate the current type of cell brush used to pain
/// the grid
/// </summary>
enum CellBrushType
{
Radial,
Linear,
Solid
}
enum ALFileType
{
None,
AVL,
Cells
}
public partial class ALMainWin : System .Windows .Window
{
#region ReticleAdorner class
/// <summary>
/// ReticleAdorner
///
/// This class is derived from Adorner, and renders the reticle (crosshairs)
/// over the main game grid.
/// </summary>
public class ReticleAdorner : Adorner
{
public ReticleAdorner( UIElement target )
: base ( target)
{
_reticleColor = ALSettings.Default .ReticleColor ;
_reticlePen = new Pen( new SolidColorBrush( _reticleColor) , 1 ) ;
}
protected override void OnRender( DrawingContext dctxt )
{
double height = ( ( Grid) ( this .AdornedElement ) ) .ActualHeight ;
double width = ( ( Grid) ( this .AdornedElement ) ) .ActualWidth ;
Point start = new Point( 0 , height / 2 ) ;
Point end = new Point( width, height / 2 ) ;
dctxt.DrawLine ( _reticlePen, start, end ) ;
start = new Point( width / 2 , 0 ) ;
end = new Point( width / 2 , height ) ;
dctxt.DrawLine ( _reticlePen, start, end ) ;
}
/// <summary>
/// Set this property to change the color of the reticle
/// </summary>
private Color _reticleColor;
public Color ReticleColor
{
get
{
return _reticleColor;
}
set
{
_reticleColor = value;
ALSettings.Default .ReticleColor = _reticleColor;
_reticlePen = new Pen( new SolidColorBrush( _reticleColor) , 1 ) ;
}
}
private Pen _reticlePen;
}
#endregion
public ALMainWin( )
{
InitializeComponent( ) ;
}
/// <summary>
/// PrepMessage( string, int )
///
/// A simple function to insert line breaks into a string destined for a messagebox.
/// MessageBox.Show() seems to work differently on XP and Vista. On Vista a long
/// message is sensibly wrapped at word boundaries. On XP it is not. If you insert
/// line breaks to look right in XP, they don't look right in Vista. So this is an
/// attempt to get some consistency.
/// </summary>
/// <param name="msg"></param>
/// <param name="lineLen"></param>
/// <returns></returns>
public string PrepMessage( string msg, int lineLen)
{
string strout = "" ;
int j = 0 ;
for ( int i = 0 ; i < msg.Length ; i++ )
{
if ( ( j >= lineLen && msg[ i] == ' ' ) || i == msg.Length - 1 )
{
strout += msg.Substring ( i - j, j) ;
strout += "\n " ;
j = 0 ;
}
else j++;
}
return strout;
}
#region menu bar event handlers
/// <summary>
/// Menu_OnGameNew(Object, RoutedEventArgs)
///
/// Handles the click event for the game menu, new command. Prompts the user and
/// on yes rebuilds the simulation and life models and resets the grid.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnGameNew( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
LifeGrid.Cursor = Cursors.Wait ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
if ( ExecNew( false ) )
oldCursor = LifeGrid.Cursor ;
else
_ls.IsPaused = paused;
LifeGrid.Cursor = oldCursor;
}
/// <summary>
/// Menu_OnGameReset(Object, RoutedEventArgs)
///
/// Handles a click event on the Game menu, Reset item. Prompts the user, and on 'Yes'
/// rebuilds the model and simulation controller, and resets various parameters.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnGameReset( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
LifeGrid.Cursor = Cursors.Wait ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
if ( MessageBox.Show ( this , Properties.Resources .UI_MB_PromptText2 , Properties.Resources .UI_MB_CaptionText2 ,
MessageBoxButton.YesNo , MessageBoxImage.Question ) == MessageBoxResult.Yes )
{
_ls.ResetSim ( ) ;
UIStateChange( UIStateChanges.ModelReset ) ;
oldCursor = LifeGrid.Cursor ;
}
else
_ls.IsPaused = paused;
LifeGrid.Cursor = oldCursor;
}
/// <summary>
/// Menu_OnGameSave(Object, RoutedEventArgs)
///
/// Handles the click event for the game menu, save command. Pauses the sim if
/// it is not already, and then calls ExecSave()
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnGameSave( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
LifeGrid.Cursor = Cursors.Wait ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
ExecSave( false ) ;
_ls.IsPaused = paused;
LifeGrid.Cursor = oldCursor;
}
/// <summary>
/// Menu_OnGameSaveAs(Object, RoutedEventArgs)
///
/// Handles a click on the "save as" command in the Game menu. The only difference
/// from the function above is that this one calls ExecSave with true, causing
/// a new filename to be chosen.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnGameSaveAs( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
LifeGrid.Cursor = Cursors.Wait ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
ExecSave( true ) ;
_ls.IsPaused = paused;
LifeGrid.Cursor = oldCursor;
}
/// <summary>
/// Menu_OnGameLoad(Object, RoutedEventArgs)
///
/// Handles the click event for the game menu, load command. Pauses the sim and
/// then checks to see if the current game is dirty. If so it pops a save dialog
/// and calls ExecSave() if the user answers affirm. It then calls ExecLoad(). If
/// load fails (ExecLoad returns false) the function unpauses the game (if it was
/// paused on entry).
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnGameLoad( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
LifeGrid.Cursor = Cursors.Wait ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
bool final = false;
System .Windows .Forms .OpenFileDialog opendlg =
new System .Windows .Forms .OpenFileDialog ( ) ;
opendlg.DefaultExt = ".avl" ;
opendlg.Filter = "AvalonLife Saved Games (.avl)|*.avl|Life Lexicon Cells (.cells)|*.cells" ;
opendlg.Title = "Load Saved Model" ;
if ( opendlg.ShowDialog ( ) == System .Windows .Forms .DialogResult .OK )
{
if ( ExecLoadFile( opendlg.FileName ) )
{
final = true;
}
}
if ( ! final )
_ls.IsPaused = paused;
LifeGrid.Cursor = oldCursor;
}
/// <summary>
/// Menu_OnGameExit(Object, RoutedEventArgs)
///
/// Handles a click on the game menu, exit command, and shuts down the application
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnGameExit( Object sender, RoutedEventArgs e)
{
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
if ( CheckSave( ) == true )
Application.Current .Shutdown ( ) ;
_ls.IsPaused = paused;
}
/// <summary>
/// Menu_OnSettingsGridLines(Object, RoutedEventArgs)
///
/// Handles a click on the settings menu, show grid command, and turns the
/// grid lines on/off accordingly.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsGridLines( Object sender, RoutedEventArgs e)
{
if ( MenuSettingsGridLines.IsChecked == false )
{
ALSettings.Default .GridOn = true;
LifeGrid.ShowGridLines = true;
MenuSettingsGridLines.IsChecked = true;
}
else
{
ALSettings.Default .GridOn = false;
LifeGrid.ShowGridLines = false;
MenuSettingsGridLines.IsChecked = false;
}
}
/// <summary>
/// Menu_OnSettingsReticle(Object, RoutedEventArgs)
///
/// Handles a click on the settings menu, show reticle command, and turns the
/// reticle (crosshairs) on/off accordingly.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsReticle( Object sender, RoutedEventArgs e)
{
if ( MenuSettingsReticle.IsChecked == false )
{
ALSettings.Default .ReticleOn = true;
_gridAdornerLayer.GetAdorners ( LifeGrid) [ 0 ] .Visibility = Visibility.Visible ;
MenuSettingsReticle.IsChecked = true;
}
else
{
ALSettings.Default .ReticleOn = false;
_gridAdornerLayer.GetAdorners ( LifeGrid) [ 0 ] .Visibility = Visibility.Hidden ;
MenuSettingsReticle.IsChecked = false;
}
}
/// <summary>
/// Menu_OnSettingsHaltStable(Object, RoutedEventArgs)
///
/// Handles a click on the Settings | Halt Stable Model command. Flips the
/// state of this property in ALSettings. Controls whether the simulation will
/// halt when a model ceases evolving.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsHaltStable( Object sender, RoutedEventArgs e)
{
if ( MenuSettingsHaltStable.IsChecked == false )
{
MenuSettingsHaltStable.IsChecked = true;
ALSettings.Default .HaltOnStability = true;
}
else
{
MenuSettingsHaltStable.IsChecked = false;
ALSettings.Default .HaltOnStability = false;
}
}
/// <summary>
/// Menu_OnSettingsGridBackground(Object, RoutedEventArgs)
///
/// Handles a click on the settings menu, grid background command. Opens a color picker
/// dialog to let the user choose the grid background color. I'm sure there must be an easier
/// way to deal with the collision between System.Windows.Media.xxx, which is used by
/// the WPF shapes and brushes, and System.Drawing.xxx which is used by the standard color
/// dialog in System.Windows.Forms, but I haven't taken the time to research it yet. So
/// for the moment this function is full of ugly conversions. Works, though.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsGridBackground( Object sender, RoutedEventArgs e)
{
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
System .Windows .Forms .ColorDialog clrDlg = new System .Windows .Forms .ColorDialog ( ) ;
clrDlg.AllowFullOpen = true;
clrDlg.SolidColorOnly = true;
System.Drawing .Color origColor = System.Drawing .Color .FromArgb (
( ( SolidColorBrush) LifeGrid.Background ) .Color .A ,
( ( SolidColorBrush) LifeGrid.Background ) .Color .R ,
( ( SolidColorBrush) LifeGrid.Background ) .Color .G ,
( ( SolidColorBrush) LifeGrid.Background ) .Color .B ) ;
clrDlg.Color = origColor;
if ( clrDlg.ShowDialog ( ) == System .Windows .Forms .DialogResult .OK )
{
Color newColor = Color.FromArgb ( clrDlg.Color .A , clrDlg.Color .R , clrDlg.Color .G , clrDlg.Color .B ) ;
ALSettings.Default .GridBackground = newColor;
SolidColorBrush gridBkgBrush = new SolidColorBrush( newColor ) ;
LifeGrid.Background = gridBkgBrush;
}
_ls.IsPaused = paused;
}
/// <summary>
/// Menu_OnSettingsReticleColor(Object, RoutedEventArgs)
///
/// Handles a click on the settings menu, reticle color command. Opens a color
/// picker and allows the user to choose a new color for drawing the reticle.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsReticleColor( Object sender, RoutedEventArgs e)
{
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
ReticleAdorner ad = _gridAdornerLayer.GetAdorners ( LifeGrid) [ 0 ] as ReticleAdorner;
System .Windows .Forms .ColorDialog clrDlg = new System .Windows .Forms .ColorDialog ( ) ;
clrDlg.AllowFullOpen = true;
clrDlg.SolidColorOnly = true;
if ( clrDlg.ShowDialog ( ) == System .Windows .Forms .DialogResult .OK )
{
Color newColor = Color.FromArgb ( clrDlg.Color .A , clrDlg.Color .R , clrDlg.Color .G , clrDlg.Color .B ) ;
ad.ReticleColor = newColor;
ALSettings.Default .ReticleColor = newColor;
ad.InvalidateVisual ( ) ;
}
_ls.IsPaused = paused;
}
/// <summary>
/// Menu_OnSettingsCellBrush(Object, RoutedEventArgs)
///
/// Handles a click on the Settings | Cell Brush command, and pops the brush definer
/// dialog. Calls ApplyCellBrush on return if the user accepted the dialog.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsCellBrush( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
ALBrushDlg dlg = new ALBrushDlg( ) ;
dlg.Owner = this;
if ( dlg.ShowDialog ( ) == true )
{
LifeGrid.Cursor = Cursors.Wait ;
ApplyRectStyle( ) ;
LifeGrid.Cursor = oldCursor;
}
_ls.IsPaused = paused;
}
/// <summary>
/// Menu_OnSettingsGridType(Object, RoutedEventArgs)
///
/// Handles a click on one of the grid type checkable menuitems in Settings | Grid Type.
/// The Tag property of these controls has been set to the appropriate value from
/// the GridType enumeration, so the tag can just be cast and passed through to
/// SetGridType.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsGridType( Object sender, RoutedEventArgs e)
{
GridType gridType = ( GridType) ( ( MenuItem) sender) .Tag ;
if ( _ls.Model .LifeGridType != gridType)
{
_ls.Model .LifeGridType = gridType;
SetGridType( gridType) ;
UIStateChange( UIStateChanges.ModelPropertiesEdited ) ;
}
}
/// <summary>
/// Menu_OnSettingsGridSize(Object, RoutedEventArgs)
///
/// Handles a click on one of the pre-defined grid sizes in the Settings | Grid Size
/// | [predefined grid size] menu. Since the predefined model sizes are square we can
/// just store one dimensionin the object tag at startup, grab it here, and use it to
/// figure out what size was requested.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsGridSize( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
LifeGrid.Cursor = Cursors.Wait ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
int size = ( int ) ( ( ( MenuItem) sender) .Tag ) ;
if ( _ls.Model .ResizeModel ( size, size) )
{
InitUIState( ) ;
UIStateChange( UIStateChanges.ModelPropertiesEdited ) ;
}
else
MessageBox.Show ( this , Properties.Resources .UI_MB_ResizeFailedMsg , Properties.Resources .UI_MB_ResizeFailedCaption ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
_ls.IsPaused = paused;
LifeGrid.Cursor = oldCursor;
}
/// <summary>
/// Menu_OnSettingsGridSizeCustom(Object, RoutedEventArgs)
///
/// Handles a click on the Settings | Grid Size | Custom menu. The dialog enforces type
/// constraints on the edit, so we know we're getting ints, but we need to check how
/// large a model the user has asked for. You can ask the sim to make a 1000 x 1000 grid.
/// I did, and by the time I killed the process five minutes later it had a 1.7 gig
/// working set. A grid that size requires the system to create about 3 million objects,
/// plus or minus a couple hundred thousand. So we check to see if the aggregate grid
/// size is greater than 10k cells, and pop a warning dialog. If the user wants to go
/// ahead we let them. I've done a 200 x 200 grid and it loads relatively quickly and will
/// actually run, but not smoothly.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsGridSizeCustom( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
LifeGrid.Cursor = Cursors.Wait ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
ALModelSizeDlg dlg = new ALModelSizeDlg( ) ;
dlg.Owner = this;
dlg.Tag = _ls.Model ;
dlg.Resize = true;
if ( dlg.ShowDialog ( ) == true )
{
int rows = dlg.Rows ;
int cols = dlg.Columns ;
if ( rows * cols > 10000 )
{
string msg = PrepMessage( Properties.Resources .UI_MB_LargeModelMsg , 80 ) ;
msg += "\n \n Requested model size will cause the program to create approximately "
+ Convert.ToString ( ( rows * cols) * 3 ) + " objects." ;
if ( MessageBox.Show ( this , msg, Properties.Resources .UI_MB_LargeModelCaption ,
MessageBoxButton.OKCancel , MessageBoxImage.Question ) == MessageBoxResult.Cancel )
{
LifeGrid.Cursor = oldCursor;
return;
}
}
if ( _ls.Model .ResizeModel ( rows, cols) )
{
InitUIState( ) ;
UIStateChange( UIStateChanges.ModelPropertiesEdited ) ;
}
else
MessageBox.Show ( this , Properties.Resources .UI_MB_ResizeFailedMsg , Properties.Resources .UI_MB_ResizeFailedCaption ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
}
_ls.IsPaused = paused;
LifeGrid.Cursor = oldCursor;
}
/// <summary>
/// Menu_OnSettingsGridSizeShrink(Object, RoutedEventArgs)
///
/// Handles a click on the Settings | Grid Size | Shrink to Model menu item. Calls
/// the model to resize the grid to the model size. See LifeModel.ResizeModel() for
/// failure conditions.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsGridSizeShrink( Object sender, RoutedEventArgs e)
{
Cursor oldCursor = LifeGrid.Cursor ;
LifeGrid.Cursor = Cursors.Wait ;
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
if ( _ls.Model .ResizeModel ( 0 , 0 ) )
{
InitUIState( ) ;
UIStateChange( UIStateChanges.ModelPropertiesEdited ) ;
}
else
MessageBox.Show ( this , Properties.Resources .UI_MB_ResizeFailedMsg , Properties.Resources .UI_MB_ResizeFailedCaption ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
_ls.IsPaused = paused;
LifeGrid.Cursor = oldCursor;
}
/// <summary>
/// Menu_OnSettingsGridSettings(Object, RoutedEventArgs)
///
/// Handles a click on the grid settings menuitem in Settings | Grid Settings. Pops
/// the grid settings dialog window.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsGridSettings( Object sender, RoutedEventArgs e)
{
ALGridDlg dlg = new ALGridDlg( ) ;
dlg.Owner = this;
dlg.ShowDialog ( ) ;
}
/// <summary>
/// Menu_OnSettingsModelName(Object, RoutedEventArgs)
///
/// Handles a click on the model name menuitem in Settings. Pops the model
/// name dialog window. Tracks changes to the name of the model so it can
/// update the window title bar after the dialog closes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnSettingsModelName( Object sender, RoutedEventArgs e)
{
ALModelNameDlg dlg = new ALModelNameDlg( ) ;
dlg.Owner = this;
dlg.Tag = _ls;
if ( dlg.ShowDialog ( ) == true )
UIStateChange( UIStateChanges.ModelPropertiesEdited ) ;
}
/// <summary>
/// Menu_OnHelpHowTo(Object, RoutedEventArgs)
///
/// Handles a click on the help menu, How to Play command.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnHelpHowTo( Object sender, RoutedEventArgs e)
{
System .Windows .Forms .HelpNavigator nav =
System .Windows .Forms .HelpNavigator .TopicId ;
System .Windows .Forms .Help .ShowHelp ( null , @"avalonlife.chm" , nav, "1020" ) ;
}
/// <summary>
/// Menu_OnHelpAbout(Object, RoutedEventArgs)
///
/// Handles a click on the help menu, about AvalonLife command.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnHelpAbout( Object sender, RoutedEventArgs e)
{
System .Windows .Forms .HelpNavigator nav =
System .Windows .Forms .HelpNavigator .TopicId ;
System .Windows .Forms .Help .ShowHelp ( null , @"avalonlife.chm" , nav, "1000" ) ;
}
/// <summary>
/// Menu_OnHelpAboutLife(Object, RoutedEventArgs)
///
/// Handles a click on the help menu, about the Game of Life command.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Menu_OnHelpAboutLife( Object sender, RoutedEventArgs e)
{
System .Windows .Forms .HelpNavigator nav =
System .Windows .Forms .HelpNavigator .TopicId ;
System .Windows .Forms .Help .ShowHelp ( null , @"avalonlife.chm" , nav, "1010" ) ;
}
#endregion
#region other ui event handlers
/// <summary>
/// ALMainWin_OnLoaded()
///
/// Handles the loaded event for the main window. Initializes the simulation
/// model, controller, and display grid.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void ALMainWin_OnLoaded( Object sender, RoutedEventArgs e)
{
ExecNew( true ) ;
RunSpeedSlider.ToolTip = Properties.Resources .UI_RunSpeedSlider_ToolTip ;
StatusGenCount.ToolTip = Properties.Resources .UI_StatusGenCount_ToolTip ;
CellBirthCount.ToolTip = Properties.Resources .UI_CellBirthCount_ToolTip ;
CellDeathCount.ToolTip = Properties.Resources .UI_CellDeathCount_ToolTip ;
PopulationCount.ToolTip = Properties.Resources .UI_PopulationCount_ToolTip ;
PeakPopulationCount.ToolTip = Properties.Resources .UI_PeakPopulationCount_ToolTip ;
MenuGridSizeText.ToolTip = Properties.Resources .UI_GridSize_ToolTip ;
_gridAdornerLayer = AdornerLayer.GetAdornerLayer ( LifeGrid) ;
ReticleAdorner ad = new ReticleAdorner( LifeGrid) ;
_gridAdornerLayer.Add ( ad) ;
MenuSettingsReticle.IsChecked = ALSettings.Default .ReticleOn ;
if ( MenuSettingsReticle.IsChecked )
ad.Visibility = Visibility.Visible ;
else
ad.Visibility = Visibility.Hidden ;
LifeGrid.Drop += new DragEventHandler( Grid_OnDrop ) ;
LifeGrid.MouseUp += new MouseButtonEventHandler( Grid_OnMouseUp ) ;
this .MouseLeave += new MouseEventHandler( Window_OnMouseLeave ) ;
this .Closing += new CancelEventHandler( ALMainWin_OnClosing ) ;
Application.Current .SessionEnding += new SessionEndingCancelEventHandler( ALMainWin_OnClosing ) ;
LifeGrid.Background = new SolidColorBrush( ALSettings.Default .GridBackground ) ;
LifeGrid.ShowGridLines = ALSettings.Default .GridOn ;
MenuSettingsGridLines.IsChecked = LifeGrid.ShowGridLines ;
MenuSettingsGridTorus.Tag = GridType.Torus ;
MenuSettingsGridXCyl.Tag = GridType.XCylinder ;
MenuSettingsGridYCyl.Tag = GridType.YCylinder ;
MenuSettingsGridFinite.Tag = GridType.Finite ;
MenuSettingsGrid40x40.Tag = 40 ;
MenuSettingsGrid50x50.Tag = 50 ;
MenuSettingsGrid60x60.Tag = 60 ;
MenuSettingsGrid70x70.Tag = 70 ;
if ( ALSettings.Default .HaltOnStability )
MenuSettingsHaltStable.IsChecked = true;
else
MenuSettingsHaltStable.IsChecked = false;
}
/// <summary>
/// ALMainWin_OnClosing(Object, CancelEventArgs)
///
/// This handles the closing event for the main window. If the game is not
/// dirty or the user saves it using 'Ok' in CheckSave, then this handler
/// will commit any settings changes and allow the close to continue. Other
/// wise if the user clicks Cancel in CheckSave it will cancel.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void ALMainWin_OnClosing( Object sender, CancelEventArgs e )
{
bool paused = _ls.IsPaused ;
_ls.IsPaused = true;
if ( CheckSave( ) == true )
{
if ( ALSettings.Default .Changed )
ALSettings.Default .Save ( ) ;
}
else
{
e.Cancel = true;
}
_ls.IsPaused = paused;
}
/// <summary>
/// RunButton_OnClick()
///
/// Handles the click event for the run button on the main window. The button
/// changes state when it is clicked. The behavior of the sim drives off of
/// the IsPaused property of _ls (LifeSim). When the sim is paused the UI allows
/// editing of the grid cells.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void RunButton_OnClick( Object sender, RoutedEventArgs e)
{
if ( _ls.IsPaused )
{
_ls.IsPaused = false;
UIStateChange( UIStateChanges.ModelRun ) ;
}
else
{
_ls.IsPaused = true;
UIStateChange( UIStateChanges.ModelPaused ) ;
}
}
/// <summary>
/// Rect_OnMouseDown(Object, MouseButtonEventArgs)
///
/// Handles the mouse down event on a Rectangle in the grid, and if the game is
/// in the paused state (editable) flips the cell state. Since we allow dragging
/// _lastMouseCell is used to avoid the effects of repeated MouseEnter events
/// getting fired.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Rect_OnMouseDown( Object sender, MouseButtonEventArgs e)
{
if ( _ls.IsPaused && ( _ls.Generation == 0 ) )
{
LifeCell lc = ( ( Rectangle) sender) .DataContext as LifeCell;
if ( lc != null )
{
_startEdit = true;
_lastMouseCell = lc;
lc.IsAlive = ! lc.IsAlive ;
UIStateChange( UIStateChanges.ModelCellEdited ) ;
}
else throw ( new System .InvalidOperationException ( "Rect_OnMouseDown" ) ) ;
}
}
/// <summary>
/// Rect_OnMouseEnter(Object, MouseEventArgs)
///
/// Handles the mouseover event for an Ellipse. If the game is paused and the mouse
/// is entering a new cell, flip that cell's state.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Rect_OnMouseEnter( Object sender, MouseEventArgs e)
{
if ( _startEdit )
{
LifeCell lc = ( ( Rectangle) sender) .DataContext as LifeCell;
if ( lc != null && lc != _lastMouseCell )
{
_lastMouseCell = lc;
lc.IsAlive = ! lc.IsAlive ;
}
else if ( lc == null )
throw ( new System .InvalidOperationException ( "Rect_OnMouseEnter" ) ) ;
}
}
/// <summary>
/// Grid_OnMouseUp(Object, MouseEventArgs)
///
/// Handles the mouse left button up event for the grid. We use it to check
/// the grid state and enable/disable menu items accordingly.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Grid_OnMouseUp( Object sender, MouseButtonEventArgs e)
{
if ( _startEdit )
{
_startEdit = false;
if ( _ls.Model .IsEmpty ( ) )
{
UIStateChange( UIStateChanges.ModelCreated ) ;
}
}
}
/// <summary>
/// Window_OnMouseLeave(Object, MouseEventArgs)
///
/// This function makes sure we handle things correctly if the user drags
/// the mouse out of the window while drawing cells. Ordinarily you would
/// capture the mouse and not release it until mouse up, but that's
/// cumbersome here, because we're getting events at the rectangle level
/// but can't capture the mouse there. If we capture it at the grid the
/// rectangles stop getting events. So this is the next best alternative.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Window_OnMouseLeave( Object sender, MouseEventArgs e)
{
if ( _startEdit )
{
_startEdit = false;
if ( _ls.Model .IsEmpty ( ) )
{
UIStateChange( UIStateChanges.ModelCreated ) ;
}
}
}
/// <summary>
/// Grid_OnDrop(Object, DragEventArgs)
///
/// This function gets called when an object is dropped on the grid. It handles drops
/// of .cells data from the Life Lexicon website, as well as .avl and .cells saved files.
/// The first half of the function detects a drop of a link from the Lexicon, and calls
/// ExecLoadWeb to get the data from the net. The second half detects file drops, and hands
/// off to ExecFileLoad.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Grid_OnDrop( Object sender, DragEventArgs e)
{
Cursor old = this .Cursor ;
bool force = this .ForceCursor ;
this .ForceCursor = true;
this .Cursor = Cursors.Wait ;
if ( e.Data .GetDataPresent ( DataFormats.Text ) )
{
if ( ( e.AllowedEffects & DragDropEffects.Link ) == DragDropEffects.Link )
{
string url = e.Data .GetData ( DataFormats.Text ) as string;
if ( Uri.IsWellFormedUriString ( url, UriKind.RelativeOrAbsolute ) )
{
ExecLoadWeb( url ) ;
}
}
}
else if ( e.Data .GetDataPresent ( DataFormats.FileDrop ) )
{
string [ ] files = e.Data .GetData ( DataFormats.FileDrop ) as string [ ] ;
ExecLoadFile( files[ files.Length - 1 ] ) ;
}
this .Cursor = old;
this .ForceCursor = force;
}
/// <summary>
/// SimStatusCallback()
///
/// This function handles a callback from the simulation controller when it detects
/// that model evolution has halted. Will only be called if ALSettings.HaltOnStability
/// is true.
/// </summary>
/// <returns></returns>
public void SimStatusCallback( )
{
string msg = null;
if ( _ls.Model .EvolutionHalted )
{
msg = Properties.Resources .UI_SimStatus_HaltMsg ;
msg += " " + _ls.Generation .ToString ( ) ;
msg += "\n \n Cell births: " + _ls.Model .CellBirths .ToString ( ) ;
msg += "\n Cell deaths: " + _ls.Model .CellDeaths .ToString ( ) ;
msg += "\n Population: " + _ls.Model .Population .ToString ( ) ;
msg += "\n Peak population: " + _ls.Model .PeakPopulation .ToString ( ) ;
msg += "\n \n The simulation has been halted." ;
MessageBox.Show ( this , msg, "Simulation Status" , MessageBoxButton.OK , MessageBoxImage.Information ) ;
UIStateChange( UIStateChanges.ModelHalted ) ;
}
}
#endregion
#region ALMainWin private methods
/// <summary>
/// UIStateChange(UIStateChanges)
///
/// This function wraps up all the UI state changes that the program goes through.
/// The possible states are defined in the AvalonLIfe.UIStateChanges enum. Each
/// case of the switch statement handles setting the UI controls and some variables
/// to appropriate settings for a given state.
/// </summary>
/// <param name="uis"></param>
private void UIStateChange( UIStateChanges uis)
{
switch ( uis)
{
case UIStateChanges.ModelCreated :
RunButton.Content = Properties.Resources .UI_RunButton_Content1 ;
RunButton.ToolTip = Properties.Resources .UI_RunButton_ToolTip1 ;
LifeGrid.ToolTip = Properties.Resources .UI_LifeGrid_ToolTip ;
LifeGrid.Cursor = Cursors.Hand ;
RunButton.IsEnabled = false;
MenuGameSave.IsEnabled = false;
MenuGameSaveAs.IsEnabled = false;
MenuGameReset.IsEnabled = false;
SetGameDirty( false ) ;
_currentModelFileBase = null;
break;
case UIStateChanges.ModelSaved :
case UIStateChanges.ModelSavedAs :
MenuGameSave.IsEnabled = false;
SetGameDirty( false ) ;
break;
case UIStateChanges.ModelLoadedFromFile :
RunButton.Content = Properties.Resources .UI_RunButton_Content1 ;
RunButton.ToolTip = Properties.Resources .UI_RunButton_ToolTip1 ;
RunButton.IsEnabled = true;
LifeGrid.ToolTip = Properties.Resources .UI_LifeGrid_ToolTip ;
LifeGrid.Cursor = Cursors.Hand ;
MenuGameSave.IsEnabled = false;
MenuGameSaveAs.IsEnabled = true;
SetGameDirty( false ) ;
break;
case UIStateChanges.ModelLoadedFromNet :
RunButton.Content = Properties.Resources .UI_RunButton_Content1 ;
RunButton.ToolTip = Properties.Resources .UI_RunButton_ToolTip1 ;
RunButton.IsEnabled = true;
LifeGrid.ToolTip = Properties.Resources .UI_LifeGrid_ToolTip ;
LifeGrid.Cursor = Cursors.Hand ;
MenuGameSave.IsEnabled = true;
MenuGameSaveAs.IsEnabled = true;
SetGameDirty( true ) ;
break;
case UIStateChanges.ModelCellEdited :
RunButton.IsEnabled = true;
MenuGameSave.IsEnabled = true;
MenuGameSaveAs.IsEnabled = true;
SetGameDirty( true ) ;
break;
case UIStateChanges.ModelRun :
RunButton.Content = Properties.Resources .UI_RunButton_Content2 ;
RunButton.ToolTip = Properties.Resources .UI_RunButton_ToolTip2 ;
LifeGrid.Cursor = Cursors.Arrow ;
LifeGrid.ToolTip = null;
MenuGameReset.IsEnabled = true;
break;
case UIStateChanges.ModelPaused :
RunButton.Content = Properties.Resources .UI_RunButton_Content1 ;
RunButton.ToolTip = Properties.Resources .UI_RunButton_ToolTip1 ;
break;
case UIStateChanges.ModelHalted :
RunButton.Content = Properties.Resources .UI_RunButton_Content1 ;
RunButton.ToolTip = Properties.Resources .UI_RunButton_ToolTip1 ;
RunButton.IsEnabled = false;
break;
case UIStateChanges.ModelReset :
RunButton.Content = Properties.Resources .UI_RunButton_Content1 ;
RunButton.ToolTip = Properties.Resources .UI_RunButton_ToolTip1 ;
RunButton.IsEnabled = true;
LifeGrid.ToolTip = Properties.Resources .UI_LifeGrid_ToolTip ;
LifeGrid.Cursor = Cursors.Hand ;
MenuGameReset.IsEnabled = false;
break;
case UIStateChanges.ModelPropertiesEdited :
MenuGameSave.IsEnabled = true;
SetGameDirty( true ) ;
break;
}
}
/// <summary>
/// UpdateWrapInidicators(bool, bool, bool, bool)
///
/// This method is called from SetGridType() to enable/disable the wrap indicator
/// bars that border the grid.
/// </summary>
/// <param name="left">True if the left bar is on, else false</param>
/// <param name="top">True if the top bar is on, else false</param>
/// <param name="right">True if the right bar is on, else false</param>
/// <param name="bottom">True if the bottom bar is on, else false</param>
private void UpdateWrapIndicators( bool left, bool top, bool right, bool bottom )
{
if ( left )
LeftWrapIndicator.Width = 4 ;
else
LeftWrapIndicator.Width = 0 ;
if ( top)
TopWrapIndicator.Height = 5 ;
else
TopWrapIndicator.Height = 0 ;
if ( right)
RightWrapIndicator.Width = 4 ;
else
RightWrapIndicator.Width = 0 ;
if ( bottom)
BottomWrapIndicator.Height = 5 ;
else
BottomWrapIndicator.Height = 0 ;
}
/// <summary>
/// SetGridType( GridType )
///
/// This method is called from ExecLoad or Menu_OnGridXXX to update the menu and UI to
/// correspond with the grid type of the loaded model.
/// </summary>
/// <param name="gridType"></param>
private void SetGridType( GridType gridType )
{
MenuSettingsGridTorus.IsChecked = false;
MenuSettingsGridXCyl.IsChecked = false;
MenuSettingsGridYCyl.IsChecked = false;
MenuSettingsGridFinite.IsChecked = false;
switch ( gridType )
{
case GridType.Torus :
MenuSettingsGridTorus.IsChecked = true;
UpdateWrapIndicators( false , false , false , false ) ;
break;
case GridType.XCylinder :
MenuSettingsGridXCyl.IsChecked = true;
UpdateWrapIndicators( false , true , false , true ) ;
break;
case GridType.YCylinder :
MenuSettingsGridYCyl.IsChecked = true;
UpdateWrapIndicators( true , false , true , false ) ;
break;
case GridType.Finite :
MenuSettingsGridFinite.IsChecked = true;
UpdateWrapIndicators( true , true , true , true ) ;
break;
}
}
/// <summary>
/// SetWindowTitle()
///
/// Called from one or two spots to set the proper window title.
/// </summary>
private void SetWindowTitle( )
{
this .Title = _winTitleBase;
if ( _ls.Model .ModelName != null && _ls.Model .ModelName .Length > 0 )
this .Title += " [" + _ls.Model .ModelName ;
else
this .Title += " [Untitled" ;
if ( _gameIsDirty)
this .Title += "*" ;
this .Title += "]" ;
}
/// <summary>
/// SetGameDirty(bool)
///
/// Called from various places where the current game becomes "dirty", or in need
/// of saving to disk. Called with true if the game has become dirty. Updates
/// the _gameIsDirty flag and window title on a state change.
/// </summary>
/// <param name="dirty"></param>
private void SetGameDirty( bool dirty )
{
_gameIsDirty = dirty;
SetWindowTitle( ) ;
}
/// <summary>
/// SetGridSizeMenu()
///
/// Called from InitUIState to update the state of the grid size menu
/// to reflect the size of a model.
/// </summary>
private void SetGridSizeMenu( )
{
MenuSettingsGrid40x40.IsChecked = false;
MenuSettingsGrid50x50.IsChecked = false;
MenuSettingsGrid60x60.IsChecked = false;
MenuSettingsGrid70x70.IsChecked = false;
MenuSettingsGridCustom.IsChecked = false;
if ( ( _ls.Model .Columns != _ls.Model .Rows ) ||
( _ls.Model .Columns != 40 && _ls.Model .Columns != 50 &&
_ls.Model .Columns != 60 && _ls.Model .Columns != 70 ) )
{
MenuSettingsGridCustom.IsChecked = true;
}
else
{
switch ( _ls.Model .Rows )
{
case 40 :
MenuSettingsGrid40x40.IsChecked = true;
break;
case 50 :
MenuSettingsGrid50x50.IsChecked = true;
break;
case 60 :
MenuSettingsGrid60x60.IsChecked = true;
break;
case 70 :
MenuSettingsGrid70x70.IsChecked = true;
break;
}
}
MenuGridSizeText.Text = "r:" + _ls.Model .Rows .ToString ( ) + " c:" + _ls.Model .Columns .ToString ( ) ;
}
/// <summary>
/// InitUIState()
///
/// This function peforms common setup work when a game is created or loaded.
/// Initializes the grid and populates it, sets up some data contexts, and sets
/// the window title.
/// </summary>
private void InitUIState( )
{
InitGrid( ) ;
PopulateGrid( ) ;
ApplyRectStyle( ) ;
SetGridSizeMenu( ) ;
SetWindowTitle( ) ;
SetGridType( _ls.Model .LifeGridType ) ;
StatusGenCount.DataContext = _ls;
RunSpeedSlider.DataContext = _ls;
CellBirthCount.DataContext = _ls.Model ;
CellDeathCount.DataContext = _ls.Model ;
PopulationCount.DataContext = _ls.Model ;
PeakPopulationCount.DataContext = _ls.Model ;
}
/// <summary>
/// ExecNew()
///
/// Does the grunt work of initializing a new game with an empty grid. Initializes
/// the model and controller, populates the grid, and wires up some UI fields by setting
/// data contexts for items with property bindings.
/// </summary>
private bool ExecNew( bool defaults)
{
bool result = false;
if ( CheckSave( ) == true )
{
if ( ! defaults)
{
ALModelSizeDlg dlg = new ALModelSizeDlg( ) ;
dlg.Owner = this;
dlg.Tag = _ls.Model ;
if ( dlg.ShowDialog ( ) == true )
{
int rows = dlg.Rows ;
int cols = dlg.Columns ;
if ( rows * cols > 10000 )
{
string msg = PrepMessage( Properties.Resources .UI_MB_LargeModelMsg , 80 ) ;
msg += "\n \n Requested model size will cause the program to create approximately "
+ Convert.ToString ( ( rows * cols) * 3 ) + " objects." ;
if ( MessageBox.Show ( this , msg, Properties.Resources .UI_MB_LargeModelCaption ,
MessageBoxButton.OKCancel , MessageBoxImage.Question ) == MessageBoxResult.Cancel )
{
return result;
}
}
if ( _ls == null )
{
_ls = new LifeSim( rows, cols) ;
_ls.UICallback = SimStatusCallback;
}
else
_ls.NewModel ( rows, cols) ;
}
else return result;
}
else
{
if ( _ls == null )
{
_ls = new LifeSim( ) ;
_ls.UICallback = SimStatusCallback;
}
else
_ls.NewModel ( ) ;
}
InitUIState( ) ;
UIStateChange( UIStateChanges.ModelCreated ) ;
result = true;
}
return result;
}
/// <summary>
/// ExecLoadWeb(string)
///
/// This function handles loading a stream of cells from the Life Lexicon website.
/// Most of the work is actually done in LifeModel.LifeModel(Stream), which is called
/// from LifeSim.NewModel(Stream).
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private bool ExecLoadWeb( string url )
{
bool result = false;
if ( CheckSave( ) == true )
{
Uri uri = new Uri( url) ;
char [ ] sep = { '.' } ;
string [ ] splitstr = uri.LocalPath .Split ( sep) ;
if ( string .Compare ( "cells" , splitstr[ splitstr.Length - 1 ] , true ) == 0 )
{
HttpWebRequest cellReq = ( HttpWebRequest) HttpWebRequest.Create ( uri) ;
cellReq.Timeout = 10000 ;
cellReq.UserAgent = "AvalonLife_1_0" ;
try
{
HttpWebResponse cellRes = ( HttpWebResponse) cellReq.GetResponse ( ) ;
if ( _ls.NewModel ( cellRes.GetResponseStream ( ) ) )
{
InitUIState( ) ;
UIStateChange( UIStateChanges.ModelLoadedFromNet ) ;
_currentModelFileType = ALFileType.None ;
result = true;
}
else
MessageBox.Show ( this , "Failed to load model data." , "Load Error" ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
cellRes.Close ( ) ;
}
catch ( WebException wex)
{
string msg = "Failed to retrieve cell data. Response: " ;
msg += wex.Response ;
MessageBox.Show ( this , msg, "Network Error" ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
}
}
else
{
string msg = "Invalid data type: " + url;
MessageBox.Show ( this , msg, "Invalid Data" ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
}
}
return result;
}
/// <summary>
/// ExecLoadFile(string)
///
/// Does the work of loading a game from a file. Handles saved .avl files as
/// well as .cells files saved from Life Lexicon data.
/// </summary>
/// <returns></returns>
private bool ExecLoadFile( string fileName)
{
bool result = false;
if ( CheckSave( ) == true )
{
ALFileType ft = GetFileType( fileName) ;
if ( ft == ALFileType.AVL )
{
BinaryFormatter formatter = new BinaryFormatter( ) ;
Stream str = File.OpenRead ( fileName) ;
try
{
_ls = formatter.Deserialize ( str) as LifeSim;
_ls.UICallback = SimStatusCallback;
SetFileType( fileName) ;
InitUIState( ) ;
UIStateChange( UIStateChanges.ModelLoadedFromFile ) ;
result = true;
}
catch ( System.Runtime .Serialization .SerializationException ex)
{
MessageBox.Show ( this , Properties.Resources .UI_MB_LoadFailedMsg + " " + ex.Message ,
Properties.Resources .UI_MB_LoadFailedCaption ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
}
finally
{
str.Close ( ) ;
}
SetGameDirty( false ) ;
}
else if ( ft == ALFileType.Cells )
{
Stream str = File.OpenRead ( fileName) ;
if ( _ls.NewModel ( str) )
{
SetFileType( fileName) ;
InitUIState( ) ;
UIStateChange( UIStateChanges.ModelLoadedFromFile ) ;
result = true;
}
else
MessageBox.Show ( this , "Failed to load model data." , "Load Error" ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
str.Close ( ) ;
}
else
{
string msg = "Invalid file type: " + fileName;
MessageBox.Show ( this , msg, "Invalid File" ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
}
}
return result;
}
/// <summary>
/// ExecSave()
///
/// Called from the menu savegame handler, or from the load game handler
/// if necessary. Saves the current model to disk.
/// </summary>
private void ExecSave( bool saveAs )
{
System .Windows .Forms .SaveFileDialog savedlg =
new System .Windows .Forms .SaveFileDialog ( ) ;
savedlg.AddExtension = true;
savedlg.Filter = "AvalonLife Saved Games (.avl)|*.avl|Life Lexicon Cells (.cells)|*.cells" ;
bool haveFile = false;
if ( _currentModelFileBase != null && _currentModelFileType != ALFileType.None )
{
haveFile = true;
savedlg.FileName = _currentModelFileBase;
if ( _currentModelFileType == ALFileType.Cells )
{
savedlg.DefaultExt = ".cells" ;
savedlg.FilterIndex = 2 ;
}
else if ( _currentModelFileType == ALFileType.AVL )
{
savedlg.DefaultExt = ".avl" ;
savedlg.FilterIndex = 1 ;
}
}
else
{
savedlg.FileName = "AvalonLife Saved Game" ;
savedlg.DefaultExt = ".avl" ;
savedlg.FilterIndex = 1 ;
}
if ( saveAs == true )
savedlg.Title = "Save Model As" ;
else
savedlg.Title = "Save Model" ;
if ( savedlg.ShowDialog ( ) == System .Windows .Forms .DialogResult .OK )
{
ALFileType ft = GetFileType( savedlg.FileName ) ;
if ( ft == ALFileType.AVL )
{
BinaryFormatter formatter = new BinaryFormatter( ) ;
Stream str = File.OpenWrite ( savedlg.FileName ) ;
try
{
formatter.Serialize ( str, _ls) ;
if ( ! saveAs || ! haveFile )
SetFileType( savedlg.FileName ) ;
UIStateChange( UIStateChanges.ModelSaved ) ;
}
catch ( System .Exception ex)
{
MessageBox.Show ( this , Properties.Resources .UI_MB_SaveFailedMsg + " " + ex.InnerException .Message ,
Properties.Resources .UI_MB_SaveFailedCaption ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
}
finally
{
str.Close ( ) ;
}
}
else if ( ft == ALFileType.Cells )
{
Stream str = File.OpenWrite ( savedlg.FileName ) ;
try
{
_ls.Model .StreamCells ( str) ;
if ( ! saveAs || ! haveFile)
SetFileType( savedlg.FileName ) ;
UIStateChange( UIStateChanges.ModelSaved ) ;
}
catch ( System .Exception )
{
}
finally
{
str.Close ( ) ;
}
}
else
{
string msg = "Invalid file type: " + savedlg.FileName ;
MessageBox.Show ( this , msg, "Invalid File" ,
MessageBoxButton.OK , MessageBoxImage.Error ) ;
}
}
}
/// <summary>
/// CheckSave()
///
/// This utility function checks to see if the current game is dirty. If it is
/// it asks the user whether they want to save the game before continuing with
/// whatever operation called this function. Returns true if the operation is
/// good to proceed, false if the user clicks cancel.
/// </summary>
/// <returns></returns>
private bool CheckSave( )
{
bool result = true;
if ( _gameIsDirty)
{
MessageBoxResult mbres = MessageBox.Show ( this , Properties.Resources .UI_MB_PromptText3 , Properties.Resources .UI_MB_CaptionText3 ,
MessageBoxButton.YesNoCancel , MessageBoxImage.Question ) ;
if ( mbres == MessageBoxResult.Yes )
ExecSave( false ) ;
else if ( mbres == MessageBoxResult.No )
SetGameDirty( false ) ;
else if ( mbres == MessageBoxResult.Cancel )
result = false;
}
return result;
}
/// <summary>
/// InitGrid()
///
/// Called from the OnLoaded event handler for the main window to initialize the
/// UI display grid with the appropriate number of rows and columns based on the
/// _lm.Rows and _lm.Columns properties. It then adds an ellipse to each cell and
/// sets its style.
/// </summary>
private void InitGrid( )
{
LifeGrid.Children .Clear ( ) ;
LifeGrid.RowDefinitions .Clear ( ) ;
LifeGrid.ColumnDefinitions .Clear ( ) ;
for ( int i = 0 ; i < _ls.Model .Rows ; i++ )
{
LifeGrid.RowDefinitions .Add ( new RowDefinition( ) ) ;
}
for ( int i = 0 ; i < _ls.Model .Columns ; i++ )
{
LifeGrid.ColumnDefinitions .Add ( new ColumnDefinition( ) ) ;
}
}
/// <summary>
/// GetCellBrush()
///
/// Constructs a brush from the settings in the config file.
/// </summary>
/// <returns></returns>
private Brush GetCellBrush( )
{
CellBrushType brushType = ALSettings.Default .LifeCellBrushType ;
if ( brushType == CellBrushType.Radial )
{
RadialGradientBrush brush = new RadialGradientBrush( ) ;
brush.GradientOrigin = new Point( 0.5 , 0.5 ) ;
brush.RadiusX = 0.5 ;
brush.RadiusY = 0.5 ;
brush.GradientStops .Add ( new GradientStop( ALSettings.Default .CellBrushC1 , ALSettings.Default .CellBrushC1Off ) ) ;
brush.GradientStops .Add ( new GradientStop( ALSettings.Default .CellBrushC2 , ALSettings.Default .CellBrushC2Off ) ) ;
brush.GradientStops .Add ( new GradientStop( ALSettings.Default .CellBrushC3 , ALSettings.Default .CellBrushC3Off ) ) ;
brush.Freeze ( ) ;
return brush;
}
else if ( brushType == CellBrushType.Linear )
{
LinearGradientBrush brush = new LinearGradientBrush( ) ;
brush.StartPoint = new Point( 0 , 0 ) ;
brush.EndPoint = new Point( 1 , 1 ) ;
brush.GradientStops .Add ( new GradientStop( ALSettings.Default .CellBrushC1 , ALSettings.Default .CellBrushC1Off ) ) ;
brush.GradientStops .Add ( new GradientStop( ALSettings.Default .CellBrushC2 , ALSettings.Default .CellBrushC2Off ) ) ;
brush.GradientStops .Add ( new GradientStop( ALSettings.Default .CellBrushC3 , ALSettings.Default .CellBrushC3Off ) ) ;
brush.Freeze ( ) ;
return brush;
}
else if ( brushType == CellBrushType.Solid )
{
SolidColorBrush brush = new SolidColorBrush( ALSettings.Default .CellBrushC1 ) ;
brush.Freeze ( ) ;
return brush;
}
else
{
SolidColorBrush brush = new SolidColorBrush( Colors.Red ) ;
brush.Freeze ( ) ;
return brush;
}
}
/// <summary>
/// ApplyRectStyle()
///
/// If you look at the source you'll see that this function is only called when a new
/// grid is being initialized, and when the cell brush has been changed. It builds a new
/// style that sets the Fill property of a rectangle to the new brush, and then goes
/// through the children of the grid setting this style. The style is based on an
/// existing style that binds the opacity property to govern visibility. So why go to
/// all this trouble to change fill brushes? Why not just set the fill property on the
/// rects and be done with it? Here's the issue: again, opacity is controlled by a
/// binding in a style. Element properties override style settings, and they happen at
/// different times too. If in the process of creating a new grid I set the rectangle
/// DataContext to point to a LifeCell, which will drive the opacity binding, and then
/// set the Fill property directly, sometimes, depending on timing, I get a repaint
/// before the opacity property is correctly set, and the grid renders all the cells
/// visible. It looks messy, and I don't want the grid repainted until the state of all
/// the cells is correct. I'm sure there must be other ways to handle suppressing the
/// repaint, but the issue there is that the flash happens after I return control to
/// the message pump. If I somehow surpress the repaint when will I unsurpress it? The
/// best way around this that I have found so far is to do as I have below: change
/// brushes by building a new style and then applying that style.
/// </summary>
private void ApplyRectStyle( )
{
Brush cellBrush = GetCellBrush( ) ;
Style style = new Style( typeof ( Rectangle) , ( Style) LifeGrid.FindResource ( typeof ( Rectangle) ) ) ;
Setter setter = new Setter( ) ;
setter.Property = Rectangle.FillProperty ;
setter.Value = cellBrush;
style.Setters .Add ( setter) ;
LifeGrid.Resources .Remove ( "RectStyle" ) ;
LifeGrid.Resources .Add ( "RectStyle" , style) ;
UIElementCollection rects = LifeGrid.Children ;
foreach ( UIElement uie in rects)
( ( Rectangle) uie) .Style = ( Style) ( LifeGrid.Resources [ "RectStyle" ] ) ;
}
/// <summary>
/// PopulateGrid()
///
/// Does the work of setting up the rectangles in the cells of the life grid. Creates
/// the rectangles and assigns them to the grid, adds them to the child collection,
/// sets up rectangle data contexts for the rect->cell link, sets the rectangle style,
/// and wires up the rectangle mouse events
/// </summary>
private void PopulateGrid( )
{
for ( int row = 0 ; row < _ls.Model .Rows ; row++ )
{
for ( int col = 0 ; col < _ls.Model .Columns ; col++ )
{
Rectangle rect = new Rectangle( ) ;
Grid.SetRow ( rect, row) ;
Grid.SetColumn ( rect, col) ;
LifeGrid.Children .Add ( rect) ;
rect.DataContext = _ls.Model .CellGrid [ row, col] ;
rect.MouseDown += new MouseButtonEventHandler( Rect_OnMouseDown) ;
rect.MouseMove += new MouseEventHandler( Rect_OnMouseEnter) ;
}
}
}
/// <summary>
/// GetFileType(string)
///
/// Called from ExecSave to determine the type of file that the user selected.
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private ALFileType GetFileType( string fileName )
{
string ext = System.IO .Path .GetExtension ( fileName) ;
ALFileType ft = ALFileType.None ;
if ( string .Compare ( ".avl" , ext, true ) == 0 )
ft = ALFileType.AVL ;
else if ( string .Compare ( ".cells" , ext, true ) == 0 )
ft = ALFileType.Cells ;
return ft;
}
/// <summary>
/// SetFileType( string )
///
/// Called from the ExecSave and ExecLoad functions. Retrieves and stores
/// the base filename and sets the file format type.
/// </summary>
/// <param name="fileName"></param>
private ALFileType SetFileType( string fileName )
{
_currentModelFileBase = System.IO .Path .GetFileNameWithoutExtension ( fileName) ;
string ext = System.IO .Path .GetExtension ( fileName) ;
if ( string .Compare ( ".avl" , ext, true ) == 0 )
_currentModelFileType = ALFileType.AVL ;
else if ( string .Compare ( ".cells" , ext, true ) == 0 )
_currentModelFileType = ALFileType.Cells ;
else
_currentModelFileType = ALFileType.None ;
return _currentModelFileType;
}
#endregion
/// <summary>
/// Holds the instance of the simulation controller
/// </summary>
private LifeSim _ls = null;
/// <summary>
/// True if the game has been modified since last save
/// </summary>
private bool _gameIsDirty = false;
/// <summary>
/// Tracks the last cell that the mouse was in
/// </summary>
private LifeCell _lastMouseCell = null;
/// <summary>
/// Container for the ReticleAdorner
/// </summary>
private AdornerLayer _gridAdornerLayer = null;
/// <summary>
/// True if we are dragging across cells in edit mode
/// </summary>
private bool _startEdit = false;
/// <summary>
/// If not null contains the last file name used to load or
/// save the current model.
/// </summary>
private string _currentModelFileBase = null;
private ALFileType _currentModelFileType = ALFileType.AVL ;
/// <summary>
/// Self-explanatory
/// </summary>
private string _winTitleBase = "AvalonLife 1.0" ;
}
}
Apr
26
2008
<Window x:Class ="AvalonLife.ALMainWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AvalonLife 1.0" Height="600" Width="600" Name="ALMainWindow" Background="#FFFFFFFF"
Loaded="ALMainWin_OnLoaded" >
<Window.Resources>
<Style x:Key ="RunSpeedSliderStyle" TargetType ="{x:Type Slider}" >
<Setter Property ="Width" Value ="120" />
<Setter Property ="Value" Value ="{Binding Path=TimerInterval, Mode=TwoWay}" />
<Setter Property ="Orientation" Value ="Horizontal" />
<Setter Property ="HorizontalAlignment" Value ="Left" />
<Setter Property ="IsSnapToTickEnabled" Value ="True" />
<Setter Property ="Minimum" Value ="100" />
<Setter Property ="Maximum" Value ="1000" />
<Setter Property ="TickPlacement" Value ="BottomRight" />
<Setter Property ="TickFrequency" Value ="100" />
<Setter Property ="IsDirectionReversed" Value ="True" />
<Setter Property ="IsMoveToPointEnabled" Value ="True" />
</Style>
<Style x:Key ="GenCountStyle" TargetType ="{x:Type TextBlock}" >
<Setter Property ="Foreground" Value ="Blue" />
<Setter Property ="Text" Value ="{Binding Path=Generation}" />
</Style>
<Style x:Key ="BirthCountStyle" TargetType ="{x:Type TextBlock}" >
<Setter Property ="Foreground" Value ="Blue" />
<Setter Property ="Text" Value ="{Binding Path=CellBirths}" />
</Style>
<Style x:Key ="DeathCountStyle" TargetType ="{x:Type TextBlock}" >
<Setter Property ="Foreground" Value ="Blue" />
<Setter Property ="Text" Value ="{Binding Path=CellDeaths}" />
</Style>
<Style x:Key ="PopulationCountStyle" TargetType ="{x:Type TextBlock}" >
<Setter Property ="Foreground" Value ="Blue" />
<Setter Property ="Text" Value ="{Binding Path=Population}" />
</Style>
<Style x:Key ="PeakPopulationCountStyle" TargetType ="{x:Type TextBlock}" >
<Setter Property ="Foreground" Value ="Blue" />
<Setter Property ="Text" Value ="{Binding Path=PeakPopulation}" />
</Style>
</Window.Resources>
<Grid>
<DockPanel>
<DockPanel DockPanel.Dock ="Top" LastChildFill ="False"
Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}">
<Menu Margin ="0,5,5,2" DockPanel.Dock ="Left" Width ="Auto" HorizontalAlignment ="Left"
Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" >
<MenuItem Header ="Game" >
<MenuItem Name ="MenuGameNew" Header ="New" Click ="Menu_OnGameNew" />
<MenuItem Name ="MenuGameReset" Header ="Reset" Click ="Menu_OnGameReset" />
<MenuItem Name ="MenuGameSave" Header ="Save..." Click ="Menu_OnGameSave" />
<MenuItem Name ="MenuGameSaveAs" Header ="Save as..." Click ="Menu_OnGameSaveAs" />
<MenuItem Name ="MenuGameLoad" Header ="Load..." Click ="Menu_OnGameLoad" />
<Separator />
<MenuItem Header ="Exit" Click ="Menu_OnGameExit" />
</MenuItem>
<MenuItem Header ="Settings" >
<MenuItem Name ="MenuSettingsGridLines" Header ="Show Grid" Click ="Menu_OnSettingsGridLines" />
<MenuItem Name ="MenuSettingsReticle" Header ="Show Reticle" Click ="Menu_OnSettingsReticle" />
<MenuItem Name ="MenuSettingsHaltStable" Header ="Halt Stable Model" Click ="Menu_OnSettingsHaltStable" />
<Separator />
<MenuItem Name ="MenuSettingsGridBkgColor" Header ="Grid Background..." Click ="Menu_OnSettingsGridBackground" />
<MenuItem Name ="MenuSettingsReticleColor" Header ="Reticle Color..." Click ="Menu_OnSettingsReticleColor" />
<MenuItem Name ="MenuSettingsCellBrush" Header ="Cell Color..." Click ="Menu_OnSettingsCellBrush" />
<Separator />
<MenuItem Header ="Grid Type" >
<MenuItem Name ="MenuSettingsGridTorus" Header ="Torus" Click ="Menu_OnSettingsGridType" />
<MenuItem Name ="MenuSettingsGridXCyl" Header ="X Cylinder" Click ="Menu_OnSettingsGridType" />
<MenuItem Name ="MenuSettingsGridYCyl" Header ="Y Cylinder" Click ="Menu_OnSettingsGridType" />
<MenuItem Name ="MenuSettingsGridFinite" Header ="Finite" Click ="Menu_OnSettingsGridType" />
</MenuItem>
<MenuItem Header ="Grid Size" >
<MenuItem Name ="MenuSettingsGrid40x40" Header ="40 x 40" Click ="Menu_OnSettingsGridSize" />
<MenuItem Name ="MenuSettingsGrid50x50" Header ="50 x 50" Click ="Menu_OnSettingsGridSize" />
<MenuItem Name ="MenuSettingsGrid60x60" Header ="60 x 60" Click ="Menu_OnSettingsGridSize" />
<MenuItem Name ="MenuSettingsGrid70x70" Header ="70 x 70" Click ="Menu_OnSettingsGridSize" />
<Separator />
<MenuItem Name ="MenuSettingsGridCustom" Header ="Custom..." Click ="Menu_OnSettingsGridSizeCustom" />
<Separator />
<MenuItem Name ="MenuSettingsGridShrink" Header ="Shrink to Model" Click ="Menu_OnSettingsGridSizeShrink" />
</MenuItem>
<MenuItem Name ="MenuSettingsGridSettings" Header ="Grid Settings..." Click ="Menu_OnSettingsGridSettings" />
<Separator />
<MenuItem Name ="MenuSettingsModelName" Header ="Model Name..." Click ="Menu_OnSettingsModelName" />
</MenuItem>
<MenuItem Header ="Help" >
<MenuItem Name ="MenuHelpHowTo" Header ="How to Play..." Click ="Menu_OnHelpHowTo" />
<MenuItem Name ="MenuHelpAbout" Header ="About AvalonLife..." Click ="Menu_OnHelpAbout" />
<MenuItem Name ="MenuHelpAboutLife" Header ="About the Game of Life..." Click ="Menu_OnHelpAboutLife" />
</MenuItem>
</Menu>
<Button Margin ="10,5,5,2" DockPanel.Dock ="Right" Name ="RunButton" VerticalAlignment ="Center" HorizontalAlignment ="Right"
Click="RunButton_OnClick" Height="20" Width="60" Content="Run" />
<TextBlock Margin ="0,5,5,2" VerticalAlignment ="Center" Width ="60" Foreground ="Blue" Name ="MenuGridSizeText" DockPanel.Dock ="Right" />
<TextBlock Margin ="0,5,5,2" VerticalAlignment ="Center" Width ="50" Text ="Grid Size:" DockPanel.Dock ="Right" />
</DockPanel>
<Canvas Name ="TopWrapIndicator" Height ="5" Width ="Auto" DockPanel.Dock ="Top" HorizontalAlignment ="Stretch" Background ="Gray" />
<StatusBar Background ="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}"
Height="30" DockPanel.Dock="Bottom" Padding="4,0,4,0">
<StatusBarItem>
<TextBlock Width ="Auto" Text ="Time:" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Name ="StatusGenCount" Width ="30" Style ="{StaticResource GenCountStyle}" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Width ="Auto" Text ="Census:" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Name ="PopulationCount" Width ="30" Style ="{StaticResource PopulationCountStyle}" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Width ="Auto" Text ="Peak:" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Name ="PeakPopulationCount" Width ="30" Style ="{StaticResource PeakPopulationCountStyle}" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Width ="Auto" Text ="Born:" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Name ="CellBirthCount" Width ="40" Style ="{StaticResource BirthCountStyle}" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Width ="Auto" Text ="Died:" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Name ="CellDeathCount" Width ="40" Style ="{StaticResource DeathCountStyle}" />
</StatusBarItem>
<StatusBarItem>
<TextBlock Width ="Auto" Padding ="10,0,0,0" > Speed:</TextBlock>
</StatusBarItem>
<StatusBarItem>
<Slider Name ="RunSpeedSlider" Style ="{StaticResource RunSpeedSliderStyle}" />
</StatusBarItem>
</StatusBar>
<Canvas Name ="BottomWrapIndicator" Height ="5" Width ="Auto" DockPanel.Dock ="Bottom" HorizontalAlignment ="Stretch" Background ="Gray" />
<Canvas Name ="LeftWrapIndicator" Height ="Auto" Width ="4" DockPanel.Dock ="Left" VerticalAlignment ="Stretch" Background ="Gray" />
<Canvas Name ="RightWrapIndicator" Height ="Auto" Width ="4" DockPanel.Dock ="Right" VerticalAlignment ="Stretch" Background ="Gray" />
<Grid Name ="LifeGrid" Background ="White" ForceCursor ="True" AllowDrop ="True" >
<Grid.Resources>
<Style TargetType ="{x:Type Rectangle}" >
<Setter Property ="Opacity" Value ="{Binding Path=IsAlive}" />
</Style>
<Style BasedOn ="{StaticResource {x:Type Rectangle}}" TargetType ="{x:Type Rectangle}" x:Key ="RectStyle" >
<Setter Property ="Fill" Value ="Red" />
</Style>
</Grid.Resources>
</Grid>
</DockPanel>
</Grid>
</Window>
Apr
26
2008
using System ;
using System.IO ;
using System.ComponentModel ;
using System.Windows ;
using System.Collections.Generic ;
using System.Text ;
using System.Runtime.Serialization ;
namespace AvalonLife
{
[ Serializable]
class LifeSim : INotifyPropertyChanged, ISerializable
{
#region LifeSim public interface
/// <summary>
/// LifeSim()
///
/// Constructs an instance of the sim controller and creates an empty model
/// for it to run. LifeSim is in the paused state after construction, but the
/// timer is running.
/// </summary>
/// <param name="lm"></param>
public LifeSim( )
{
_lm = new LifeModel( ) ;
_timerInterval = ALSettings.Default .TimerInterval ;
_timer = new System .Windows .Forms .Timer ( ) ;
_timer.Interval = _timerInterval;
_timer.Tick += new EventHandler( TimerEvent) ;
_timer.Tag = this;
_timer.Start ( ) ;
}
/// <summary>
/// LifeSim(int, int)
///
/// Constructs an instance of the sim controller and creates an empty model
/// for it to run, using the specified dimensions. LifeSim is in the paused
/// state after construction, but the timer is running.
/// </summary>
/// <param name="rows">Height of the requested model grid</param>
/// <param name="columns">Width of the requested model grid</param>
public LifeSim( int rows, int columns)
{
_lm = new LifeModel( rows, columns) ;
_timerInterval = ALSettings.Default .TimerInterval ;
_timer = new System .Windows .Forms .Timer ( ) ;
_timer.Interval = _timerInterval;
_timer.Tick += new EventHandler( TimerEvent) ;
_timer.Tag = this;
_timer.Start ( ) ;
}
#region ISerializable methods
/// <summary>
/// LifeSim(SerializationInfo, StreamingContext)
///
/// Called by the BinaryFormatter to construct an instance of LifeSim from a
/// stream. Deserializes the private members, then the LifeModel deserialization
/// is called, and finally the timer is created and started. The sim is constructed
/// in a paused state. The UI is responsible for wiring up events.
/// </summary>
/// <param name="info"></param>
/// <param name="ctxt"></param>
public LifeSim( SerializationInfo info, StreamingContext ctxt )
{
_generation = ( int ) info.GetValue ( "_generation" , typeof ( int ) ) ;
_timerInterval = ( int ) info.GetValue ( "_timerInterval" , typeof ( int ) ) ;
_lm = new LifeModel( info, ctxt ) ;
_isPaused = true;
_timer = new System .Windows .Forms .Timer ( ) ;
_timer.Interval = _timerInterval;
_timer.Tick += new EventHandler( TimerEvent) ;
_timer.Tag = this;
_timer.Start ( ) ;
}
/// <summary>
/// GetObjectData(SerializationInfo, StreamingContext)
///
/// Called by the BinaryFormatter to serialize an instance of LifeSim. Serializes
/// the private members and then calls the LifeModel serialization method directly.
/// </summary>
/// <param name="info"></param>
/// <param name="ctxt"></param>
public void GetObjectData( SerializationInfo info, StreamingContext ctxt )
{
info.AddValue ( "_generation" , _generation ) ;
info.AddValue ( "_timerInterval" , _timerInterval ) ;
_lm.GetObjectData ( info, ctxt ) ;
}
#endregion
/// <summary>
/// ~LifeSim()
///
/// I haven't verified it, but this is almost certainly unnecessary. The timer
/// class dispose() should kill the timer and clean up. Anyway no harm done.
/// </summary>
~LifeSim( )
{
if ( _timer != null )
_timer.Stop ( ) ;
}
/// <summary>
/// TimerEvent(Object, EventArgs)
///
/// This function handles the timer tick. If the game is in the unpaused state it calls
/// the LifeModel.Evaluate() method to iterate the model. It then increments the
/// generation count. It's a static method so we pass in the 'this' pointer for the
/// LifeSim instance that owns the timer in its Tag property. Useful little things, Tags.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void TimerEvent( Object sender, EventArgs e )
{
LifeSim ls = ( ( System .Windows .Forms .Timer ) sender) .Tag as LifeSim;
if ( ls != null && ! ls.IsPaused )
{
if ( ! ls._lm.EvolutionHalted || ALSettings.Default .HaltOnStability == false )
{
ls._lm.Evaluate ( ) ;
ls.Generation ++;
}
else
{
ls.IsPaused = true;
if ( ls._uiCallback != null )
{
ls._uiCallback( ) ;
}
}
}
else if ( ls == null )
throw ( new System .InvalidOperationException ( "TimerEvent" ) ) ;
}
/// <summary>
/// ResetSim()
///
/// Called from the UI to reset the simulation to its starting condition. On
/// exit the simulation is paused.
/// </summary>
public void ResetSim( )
{
if ( ! _isPaused )
IsPaused = true;
Generation = 0 ;
_lm.ResetModel ( ) ;
}
/// <summary>
/// NewModel()
///
/// Called from the UI during handling of a click on Game | New.
/// </summary>
public void NewModel( )
{
if ( ! _isPaused )
_isPaused = true;
_lm = new LifeModel( ) ;
Generation = 0 ;
}
/// <summary>
/// NewModel(int, int)
///
/// Called from the UI during handling of a click on Game | New. Creates a
/// new model using the passed in dimensions.
/// </summary>
public void NewModel( int rows, int columns)
{
if ( ! _isPaused)
_isPaused = true;
_lm = new LifeModel( rows, columns) ;
Generation = 0 ;
}
/// <summary>
/// NewModel(Stream)
///
/// Called to decode a stream of .cells format data into a LifeModel. The bulk
/// of the work is done in the LifeModel.LifeModel(Stream) constructor. If
/// construction fails we will already have warned the user in the constructor,
/// so all we do here is restore the paused state we had on entry.
/// </summary>
/// <param name="str">Stream containing the .cells formated data</param>
/// <returns></returns>
public bool NewModel( Stream str )
{
bool result = false;
bool paused = _isPaused;
_isPaused = true;
try
{
LifeModel lm = new LifeModel( str ) ;
_lm = lm;
Generation = 0 ;
result = true;
}
catch ( System .Exception )
{
_isPaused = paused;
}
return result;
}
#endregion
#region INotifyPropertyChanged members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region LifeSim public properties
/// <summary>
/// Counts the generations that the current model has run
/// </summary>
private int _generation = 0 ;
public int Generation
{
get
{
return _generation;
}
protected set
{
_generation = value;
if ( PropertyChanged != null )
PropertyChanged( this , new PropertyChangedEventArgs( "Generation" ) ) ;
}
}
/// <summary>
/// Holds a reference to the simulation model
/// </summary>
private LifeModel _lm = null;
public LifeModel Model
{
get
{
return _lm;
}
}
/// <summary>
/// Holds the timer interval, set to default on start
/// </summary>
private int _timerInterval = 0 ;
public int TimerInterval
{
get
{
return _timerInterval;
}
set
{
_timerInterval = value;
_timer.Interval = _timerInterval;
if ( PropertyChanged != null )
PropertyChanged( this , new PropertyChangedEventArgs( "TimerInterval" ) ) ;
}
}
/// <summary>
/// Holds the simulation run state: false if running, true if paused.
/// </summary>
private bool _isPaused = true;
public bool IsPaused
{
get
{
return _isPaused;
}
set
{
_isPaused = value;
}
}
/// <summary>
/// The timer handler may detect that the model has ceased evolving,
/// i.e. entered a stable state. In that case it will make a call to
/// the function in this delegate to inform the UI. The UI is responsible
/// for setting a function-typed value to this delegate member if it
/// wants to receive this callback.
/// </summary>
public delegate void UISimStatusCallback( ) ;
private UISimStatusCallback _uiCallback = null;
public UISimStatusCallback UICallback
{
get
{
return _uiCallback;
}
set
{
_uiCallback = value;
}
}
#endregion
#region LifeSim private data
private System .Windows .Forms .Timer _timer = null;
#endregion
}
}
Apr
25
2008
Fatal error : Maximum execution time of 30 seconds exceeded in /data/16/1/24/103/1513103/user/1628232/htdocs/mbn/wp-content/plugins/geshi/geshi.php on line 1852