Archive for April, 2008

Apr 28 2008

MySQL Net Setup Problem

Published by Mark under Programming

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…

mysql_neterr.png

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.

No responses yet

Apr 27 2008

AvalonLife Listing 4 – ALMainWin.xaml.cs

Published by Mark under Code

  1. using System;
  2. using System.ComponentModel;
  3. using System.IO;
  4. using System.Net;
  5. using System.Text;
  6. using System.Runtime.Serialization.Formatters.Binary;
  7. using System.Collections.Generic;
  8. using System.Windows;
  9. using System.Windows.Media;
  10. using System.Windows.Documents;
  11. using System.Windows.Controls;
  12. using System.Windows.Input;
  13. using System.Windows.Shapes;
  14.  
  15. namespace AvalonLife
  16. {
  17. /// <summary>
  18. /// UIStateChanges
  19. ///
  20. /// This enum provides indicators for the user interface states
  21. /// that the program can be in. Used to call UIStateChange().
  22. /// </summary>
  23. enum UIStateChanges
  24. {
  25. ModelCreated,
  26. ModelSaved,
  27. ModelSavedAs,
  28. ModelLoadedFromFile,
  29. ModelLoadedFromNet,
  30. ModelCellEdited,
  31. ModelRun,
  32. ModelPaused,
  33. ModelHalted,
  34. ModelReset,
  35. ModelPropertiesEdited
  36. }
  37.  
  38. /// <summary>
  39. /// Used to indicate the current type of cell brush used to pain
  40. /// the grid
  41. /// </summary>
  42. enum CellBrushType
  43. {
  44. Radial,
  45. Linear,
  46. Solid
  47. }
  48.  
  49. enum ALFileType
  50. {
  51. None,
  52. AVL,
  53. Cells
  54. }
  55.  
  56. public partial class ALMainWin : System.Windows.Window
  57. {
  58.  
  59. #region ReticleAdorner class
  60.  
  61. /// <summary>
  62. /// ReticleAdorner
  63. ///
  64. /// This class is derived from Adorner, and renders the reticle (crosshairs)
  65. /// over the main game grid.
  66. /// </summary>
  67. public class ReticleAdorner : Adorner
  68. {
  69. public ReticleAdorner( UIElement target )
  70. : base(target)
  71. {
  72. _reticleColor = ALSettings.Default.ReticleColor;
  73. _reticlePen = new Pen(new SolidColorBrush(_reticleColor), 1);
  74. }
  75.  
  76. protected override void OnRender( DrawingContext dctxt )
  77. {
  78. double height = ((Grid)(this.AdornedElement)).ActualHeight;
  79. double width = ((Grid)(this.AdornedElement)).ActualWidth;
  80.  
  81. Point start = new Point( 0, height / 2 );
  82. Point end = new Point( width, height / 2 );
  83. dctxt.DrawLine( _reticlePen, start, end );
  84.  
  85. start = new Point( width / 2, 0 );
  86. end = new Point( width / 2, height );
  87. dctxt.DrawLine( _reticlePen, start, end );
  88. }
  89.  
  90. /// <summary>
  91. /// Set this property to change the color of the reticle
  92. /// </summary>
  93. private Color _reticleColor;
  94. public Color ReticleColor
  95. {
  96. get
  97. {
  98. return _reticleColor;
  99. }
  100. set
  101. {
  102. _reticleColor = value;
  103. ALSettings.Default.ReticleColor = _reticleColor;
  104. _reticlePen = new Pen(new SolidColorBrush(_reticleColor), 1);
  105. }
  106. }
  107.  
  108. private Pen _reticlePen;
  109. }
  110.  
  111. #endregion
  112.  
  113. public ALMainWin()
  114. {
  115. InitializeComponent();
  116. }
  117.  
  118. /// <summary>
  119. /// PrepMessage( string, int )
  120. ///
  121. /// A simple function to insert line breaks into a string destined for a messagebox.
  122. /// MessageBox.Show() seems to work differently on XP and Vista. On Vista a long
  123. /// message is sensibly wrapped at word boundaries. On XP it is not. If you insert
  124. /// line breaks to look right in XP, they don't look right in Vista. So this is an
  125. /// attempt to get some consistency.
  126. /// </summary>
  127. /// <param name="msg"></param>
  128. /// <param name="lineLen"></param>
  129. /// <returns></returns>
  130. public string PrepMessage(string msg, int lineLen)
  131. {
  132. string strout = "";
  133. int j = 0;
  134. for (int i = 0; i < msg.Length; i++)
  135. {
  136. if ((j >= lineLen && msg[i] == ' ') || i == msg.Length - 1)
  137. {
  138. strout += msg.Substring(i - j, j);
  139. strout += "\n";
  140. j = 0;
  141. }
  142. else j++;
  143. }
  144. return strout;
  145. }
  146.  
  147. #region menu bar event handlers
  148.  
  149. /// <summary>
  150. /// Menu_OnGameNew(Object, RoutedEventArgs)
  151. ///
  152. /// Handles the click event for the game menu, new command. Prompts the user and
  153. /// on yes rebuilds the simulation and life models and resets the grid.
  154. /// </summary>
  155. /// <param name="sender"></param>
  156. /// <param name="e"></param>
  157. public void Menu_OnGameNew(Object sender, RoutedEventArgs e)
  158. {
  159. Cursor oldCursor = LifeGrid.Cursor;
  160. LifeGrid.Cursor = Cursors.Wait;
  161.  
  162. bool paused = _ls.IsPaused;
  163. _ls.IsPaused = true;
  164.  
  165. if ( ExecNew(false) )
  166. oldCursor = LifeGrid.Cursor;
  167. else
  168. _ls.IsPaused = paused;
  169.  
  170. LifeGrid.Cursor = oldCursor;
  171. }
  172.  
  173. /// <summary>
  174. /// Menu_OnGameReset(Object, RoutedEventArgs)
  175. ///
  176. /// Handles a click event on the Game menu, Reset item. Prompts the user, and on 'Yes'
  177. /// rebuilds the model and simulation controller, and resets various parameters.
  178. /// </summary>
  179. /// <param name="sender"></param>
  180. /// <param name="e"></param>
  181. public void Menu_OnGameReset(Object sender, RoutedEventArgs e)
  182. {
  183. Cursor oldCursor = LifeGrid.Cursor;
  184. LifeGrid.Cursor = Cursors.Wait;
  185.  
  186. bool paused = _ls.IsPaused;
  187. _ls.IsPaused = true;
  188.  
  189. if (MessageBox.Show(this, Properties.Resources.UI_MB_PromptText2, Properties.Resources.UI_MB_CaptionText2,
  190. MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
  191. {
  192. _ls.ResetSim();
  193. UIStateChange(UIStateChanges.ModelReset);
  194. oldCursor = LifeGrid.Cursor;
  195. }
  196. else
  197. _ls.IsPaused = paused;
  198.  
  199. LifeGrid.Cursor = oldCursor;
  200. }
  201.  
  202. /// <summary>
  203. /// Menu_OnGameSave(Object, RoutedEventArgs)
  204. ///
  205. /// Handles the click event for the game menu, save command. Pauses the sim if
  206. /// it is not already, and then calls ExecSave()
  207. /// </summary>
  208. /// <param name="sender"></param>
  209. /// <param name="e"></param>
  210. public void Menu_OnGameSave(Object sender, RoutedEventArgs e)
  211. {
  212. Cursor oldCursor = LifeGrid.Cursor;
  213. LifeGrid.Cursor = Cursors.Wait;
  214.  
  215. bool paused = _ls.IsPaused;
  216. _ls.IsPaused = true;
  217.  
  218. ExecSave(false);
  219.  
  220. _ls.IsPaused = paused;
  221.  
  222. LifeGrid.Cursor = oldCursor;
  223. }
  224.  
  225. /// <summary>
  226. /// Menu_OnGameSaveAs(Object, RoutedEventArgs)
  227. ///
  228. /// Handles a click on the "save as" command in the Game menu. The only difference
  229. /// from the function above is that this one calls ExecSave with true, causing
  230. /// a new filename to be chosen.
  231. /// </summary>
  232. /// <param name="sender"></param>
  233. /// <param name="e"></param>
  234. public void Menu_OnGameSaveAs(Object sender, RoutedEventArgs e)
  235. {
  236. Cursor oldCursor = LifeGrid.Cursor;
  237. LifeGrid.Cursor = Cursors.Wait;
  238.  
  239. bool paused = _ls.IsPaused;
  240. _ls.IsPaused = true;
  241.  
  242. ExecSave(true);
  243.  
  244. _ls.IsPaused = paused;
  245.  
  246. LifeGrid.Cursor = oldCursor;
  247. }
  248.  
  249. /// <summary>
  250. /// Menu_OnGameLoad(Object, RoutedEventArgs)
  251. ///
  252. /// Handles the click event for the game menu, load command. Pauses the sim and
  253. /// then checks to see if the current game is dirty. If so it pops a save dialog
  254. /// and calls ExecSave() if the user answers affirm. It then calls ExecLoad(). If
  255. /// load fails (ExecLoad returns false) the function unpauses the game (if it was
  256. /// paused on entry).
  257. /// </summary>
  258. /// <param name="sender"></param>
  259. /// <param name="e"></param>
  260. public void Menu_OnGameLoad(Object sender, RoutedEventArgs e)
  261. {
  262. Cursor oldCursor = LifeGrid.Cursor;
  263. LifeGrid.Cursor = Cursors.Wait;
  264.  
  265. bool paused = _ls.IsPaused;
  266. _ls.IsPaused = true;
  267. bool final = false;
  268.  
  269. System.Windows.Forms.OpenFileDialog opendlg =
  270. new System.Windows.Forms.OpenFileDialog();
  271.  
  272. opendlg.DefaultExt = ".avl";
  273. opendlg.Filter = "AvalonLife Saved Games (.avl)|*.avl|Life Lexicon Cells (.cells)|*.cells";
  274. opendlg.Title = "Load Saved Model";
  275. if (opendlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  276. {
  277. if ( ExecLoadFile(opendlg.FileName) )
  278. {
  279. final = true;
  280. }
  281. }
  282. if ( !final )
  283. _ls.IsPaused = paused;
  284.  
  285. LifeGrid.Cursor = oldCursor;
  286. }
  287.  
  288. /// <summary>
  289. /// Menu_OnGameExit(Object, RoutedEventArgs)
  290. ///
  291. /// Handles a click on the game menu, exit command, and shuts down the application
  292. /// </summary>
  293. /// <param name="sender"></param>
  294. /// <param name="e"></param>
  295. public void Menu_OnGameExit(Object sender, RoutedEventArgs e)
  296. {
  297. bool paused = _ls.IsPaused;
  298. _ls.IsPaused = true;
  299.  
  300. if ( CheckSave() == true )
  301. Application.Current.Shutdown();
  302.  
  303. _ls.IsPaused = paused;
  304. }
  305.  
  306. /// <summary>
  307. /// Menu_OnSettingsGridLines(Object, RoutedEventArgs)
  308. ///
  309. /// Handles a click on the settings menu, show grid command, and turns the
  310. /// grid lines on/off accordingly.
  311. /// </summary>
  312. /// <param name="sender"></param>
  313. /// <param name="e"></param>
  314. public void Menu_OnSettingsGridLines(Object sender, RoutedEventArgs e)
  315. {
  316. if (MenuSettingsGridLines.IsChecked == false)
  317. {
  318. ALSettings.Default.GridOn = true;
  319. LifeGrid.ShowGridLines = true;
  320. MenuSettingsGridLines.IsChecked = true;
  321. }
  322. else
  323. {
  324. ALSettings.Default.GridOn = false;
  325. LifeGrid.ShowGridLines = false;
  326. MenuSettingsGridLines.IsChecked = false;
  327. }
  328. }
  329.  
  330. /// <summary>
  331. /// Menu_OnSettingsReticle(Object, RoutedEventArgs)
  332. ///
  333. /// Handles a click on the settings menu, show reticle command, and turns the
  334. /// reticle (crosshairs) on/off accordingly.
  335. /// </summary>
  336. /// <param name="sender"></param>
  337. /// <param name="e"></param>
  338. public void Menu_OnSettingsReticle(Object sender, RoutedEventArgs e)
  339. {
  340. if ( MenuSettingsReticle.IsChecked == false )
  341. {
  342. ALSettings.Default.ReticleOn = true;
  343. _gridAdornerLayer.GetAdorners(LifeGrid)[0].Visibility = Visibility.Visible;
  344. MenuSettingsReticle.IsChecked = true;
  345. }
  346. else
  347. {
  348. ALSettings.Default.ReticleOn = false;
  349. _gridAdornerLayer.GetAdorners(LifeGrid)[0].Visibility = Visibility.Hidden;
  350. MenuSettingsReticle.IsChecked = false;
  351. }
  352. }
  353.  
  354. /// <summary>
  355. /// Menu_OnSettingsHaltStable(Object, RoutedEventArgs)
  356. ///
  357. /// Handles a click on the Settings | Halt Stable Model command. Flips the
  358. /// state of this property in ALSettings. Controls whether the simulation will
  359. /// halt when a model ceases evolving.
  360. /// </summary>
  361. /// <param name="sender"></param>
  362. /// <param name="e"></param>
  363. public void Menu_OnSettingsHaltStable(Object sender, RoutedEventArgs e)
  364. {
  365. if (MenuSettingsHaltStable.IsChecked == false)
  366. {
  367. MenuSettingsHaltStable.IsChecked = true;
  368. ALSettings.Default.HaltOnStability = true;
  369. }
  370. else
  371. {
  372. MenuSettingsHaltStable.IsChecked = false;
  373. ALSettings.Default.HaltOnStability = false;
  374. }
  375. }
  376.  
  377. /// <summary>
  378. /// Menu_OnSettingsGridBackground(Object, RoutedEventArgs)
  379. ///
  380. /// Handles a click on the settings menu, grid background command. Opens a color picker
  381. /// dialog to let the user choose the grid background color. I'm sure there must be an easier
  382. /// way to deal with the collision between System.Windows.Media.xxx, which is used by
  383. /// the WPF shapes and brushes, and System.Drawing.xxx which is used by the standard color
  384. /// dialog in System.Windows.Forms, but I haven't taken the time to research it yet. So
  385. /// for the moment this function is full of ugly conversions. Works, though.
  386. /// </summary>
  387. /// <param name="sender"></param>
  388. /// <param name="e"></param>
  389. public void Menu_OnSettingsGridBackground(Object sender, RoutedEventArgs e)
  390. {
  391. bool paused = _ls.IsPaused;
  392. _ls.IsPaused = true;
  393.  
  394. System.Windows.Forms.ColorDialog clrDlg = new System.Windows.Forms.ColorDialog();
  395.  
  396. clrDlg.AllowFullOpen = true;
  397. clrDlg.SolidColorOnly = true;
  398. System.Drawing.Color origColor = System.Drawing.Color.FromArgb(
  399. ((SolidColorBrush)LifeGrid.Background).Color.A,
  400. ((SolidColorBrush)LifeGrid.Background).Color.R,
  401. ((SolidColorBrush)LifeGrid.Background).Color.G,
  402. ((SolidColorBrush)LifeGrid.Background).Color.B );
  403.  
  404. clrDlg.Color = origColor;
  405. if ( clrDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK )
  406. {
  407. Color newColor = Color.FromArgb(clrDlg.Color.A, clrDlg.Color.R, clrDlg.Color.G, clrDlg.Color.B);
  408. ALSettings.Default.GridBackground = newColor;
  409. SolidColorBrush gridBkgBrush = new SolidColorBrush( newColor );
  410. LifeGrid.Background = gridBkgBrush;
  411. }
  412. _ls.IsPaused = paused;
  413. }
  414.  
  415. /// <summary>
  416. /// Menu_OnSettingsReticleColor(Object, RoutedEventArgs)
  417. ///
  418. /// Handles a click on the settings menu, reticle color command. Opens a color
  419. /// picker and allows the user to choose a new color for drawing the reticle.
  420. /// </summary>
  421. /// <param name="sender"></param>
  422. /// <param name="e"></param>
  423. public void Menu_OnSettingsReticleColor(Object sender, RoutedEventArgs e)
  424. {
  425. bool paused = _ls.IsPaused;
  426. _ls.IsPaused = true;
  427. ReticleAdorner ad = _gridAdornerLayer.GetAdorners(LifeGrid)[0] as ReticleAdorner;
  428.  
  429. System.Windows.Forms.ColorDialog clrDlg = new System.Windows.Forms.ColorDialog();
  430.  
  431. clrDlg.AllowFullOpen = true;
  432. clrDlg.SolidColorOnly = true;
  433.  
  434. if ( clrDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK )
  435. {
  436. Color newColor = Color.FromArgb(clrDlg.Color.A, clrDlg.Color.R, clrDlg.Color.G, clrDlg.Color.B);
  437. ad.ReticleColor = newColor;
  438. ALSettings.Default.ReticleColor = newColor;
  439. ad.InvalidateVisual();
  440. }
  441. _ls.IsPaused = paused;
  442. }
  443.  
  444. /// <summary>
  445. /// Menu_OnSettingsCellBrush(Object, RoutedEventArgs)
  446. ///
  447. /// Handles a click on the Settings | Cell Brush command, and pops the brush definer
  448. /// dialog. Calls ApplyCellBrush on return if the user accepted the dialog.
  449. /// </summary>
  450. /// <param name="sender"></param>
  451. /// <param name="e"></param>
  452. public void Menu_OnSettingsCellBrush(Object sender, RoutedEventArgs e)
  453. {
  454. Cursor oldCursor = LifeGrid.Cursor;
  455.  
  456. bool paused = _ls.IsPaused;
  457. _ls.IsPaused = true;
  458.  
  459. ALBrushDlg dlg = new ALBrushDlg();
  460.  
  461. dlg.Owner = this;
  462. if ( dlg.ShowDialog() == true )
  463. {
  464. LifeGrid.Cursor = Cursors.Wait;
  465. ApplyRectStyle();
  466. LifeGrid.Cursor = oldCursor;
  467. }
  468. _ls.IsPaused = paused;
  469. }
  470.  
  471. /// <summary>
  472. /// Menu_OnSettingsGridType(Object, RoutedEventArgs)
  473. ///
  474. /// Handles a click on one of the grid type checkable menuitems in Settings | Grid Type.
  475. /// The Tag property of these controls has been set to the appropriate value from
  476. /// the GridType enumeration, so the tag can just be cast and passed through to
  477. /// SetGridType.
  478. /// </summary>
  479. /// <param name="sender"></param>
  480. /// <param name="e"></param>
  481. public void Menu_OnSettingsGridType(Object sender, RoutedEventArgs e)
  482. {
  483. GridType gridType = (GridType)((MenuItem)sender).Tag;
  484. if (_ls.Model.LifeGridType != gridType)
  485. {
  486. _ls.Model.LifeGridType = gridType;
  487. SetGridType(gridType);
  488. UIStateChange(UIStateChanges.ModelPropertiesEdited);
  489. }
  490. }
  491.  
  492. /// <summary>
  493. /// Menu_OnSettingsGridSize(Object, RoutedEventArgs)
  494. ///
  495. /// Handles a click on one of the pre-defined grid sizes in the Settings | Grid Size
  496. /// | [predefined grid size] menu. Since the predefined model sizes are square we can
  497. /// just store one dimensionin the object tag at startup, grab it here, and use it to
  498. /// figure out what size was requested.
  499. /// </summary>
  500. /// <param name="sender"></param>
  501. /// <param name="e"></param>
  502. public void Menu_OnSettingsGridSize(Object sender, RoutedEventArgs e)
  503. {
  504. Cursor oldCursor = LifeGrid.Cursor;
  505. LifeGrid.Cursor = Cursors.Wait;
  506.  
  507. bool paused = _ls.IsPaused;
  508. _ls.IsPaused = true;
  509.  
  510. int size = (int)(((MenuItem)sender).Tag);
  511. if ( _ls.Model.ResizeModel(size, size) )
  512. {
  513. InitUIState();
  514. UIStateChange(UIStateChanges.ModelPropertiesEdited);
  515. }
  516. else
  517. MessageBox.Show(this, Properties.Resources.UI_MB_ResizeFailedMsg, Properties.Resources.UI_MB_ResizeFailedCaption,
  518. MessageBoxButton.OK, MessageBoxImage.Error);
  519.  
  520. _ls.IsPaused = paused;
  521. LifeGrid.Cursor = oldCursor;
  522. }
  523.  
  524. /// <summary>
  525. /// Menu_OnSettingsGridSizeCustom(Object, RoutedEventArgs)
  526. ///
  527. /// Handles a click on the Settings | Grid Size | Custom menu. The dialog enforces type
  528. /// constraints on the edit, so we know we're getting ints, but we need to check how
  529. /// large a model the user has asked for. You can ask the sim to make a 1000 x 1000 grid.
  530. /// I did, and by the time I killed the process five minutes later it had a 1.7 gig
  531. /// working set. A grid that size requires the system to create about 3 million objects,
  532. /// plus or minus a couple hundred thousand. So we check to see if the aggregate grid
  533. /// size is greater than 10k cells, and pop a warning dialog. If the user wants to go
  534. /// ahead we let them. I've done a 200 x 200 grid and it loads relatively quickly and will
  535. /// actually run, but not smoothly.
  536. /// </summary>
  537. /// <param name="sender"></param>
  538. /// <param name="e"></param>
  539. public void Menu_OnSettingsGridSizeCustom(Object sender, RoutedEventArgs e)
  540. {
  541. Cursor oldCursor = LifeGrid.Cursor;
  542. LifeGrid.Cursor = Cursors.Wait;
  543.  
  544. bool paused = _ls.IsPaused;
  545. _ls.IsPaused = true;
  546.  
  547. ALModelSizeDlg dlg = new ALModelSizeDlg();
  548. dlg.Owner = this;
  549. dlg.Tag = _ls.Model;
  550. dlg.Resize = true;
  551.  
  552. if ( dlg.ShowDialog() == true )
  553. {
  554. int rows = dlg.Rows;
  555. int cols = dlg.Columns;
  556. if ( rows * cols > 10000 )
  557. {
  558. string msg = PrepMessage(Properties.Resources.UI_MB_LargeModelMsg, 80);
  559. msg += "\n\nRequested model size will cause the program to create approximately "
  560. + Convert.ToString((rows * cols) * 3) + " objects.";
  561. if ( MessageBox.Show(this, msg, Properties.Resources.UI_MB_LargeModelCaption,
  562. MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.Cancel )
  563. {
  564. LifeGrid.Cursor = oldCursor;
  565. return;
  566. }
  567. }
  568.  
  569. if (_ls.Model.ResizeModel(rows, cols))
  570. {
  571. InitUIState();
  572. UIStateChange(UIStateChanges.ModelPropertiesEdited);
  573. }
  574. else
  575. MessageBox.Show(this, Properties.Resources.UI_MB_ResizeFailedMsg, Properties.Resources.UI_MB_ResizeFailedCaption,
  576. MessageBoxButton.OK, MessageBoxImage.Error);
  577. }
  578. _ls.IsPaused = paused;
  579. LifeGrid.Cursor = oldCursor;
  580. }
  581.  
  582. /// <summary>
  583. /// Menu_OnSettingsGridSizeShrink(Object, RoutedEventArgs)
  584. ///
  585. /// Handles a click on the Settings | Grid Size | Shrink to Model menu item. Calls
  586. /// the model to resize the grid to the model size. See LifeModel.ResizeModel() for
  587. /// failure conditions.
  588. /// </summary>
  589. /// <param name="sender"></param>
  590. /// <param name="e"></param>
  591. public void Menu_OnSettingsGridSizeShrink(Object sender, RoutedEventArgs e)
  592. {
  593. Cursor oldCursor = LifeGrid.Cursor;
  594. LifeGrid.Cursor = Cursors.Wait;
  595.  
  596. bool paused = _ls.IsPaused;
  597. _ls.IsPaused = true;
  598.  
  599. if (_ls.Model.ResizeModel(0, 0))
  600. {
  601. InitUIState();
  602. UIStateChange(UIStateChanges.ModelPropertiesEdited);
  603. }
  604. else
  605. MessageBox.Show(this, Properties.Resources.UI_MB_ResizeFailedMsg, Properties.Resources.UI_MB_ResizeFailedCaption,
  606. MessageBoxButton.OK, MessageBoxImage.Error);
  607.  
  608. _ls.IsPaused = paused;
  609. LifeGrid.Cursor = oldCursor;
  610. }
  611.  
  612. /// <summary>
  613. /// Menu_OnSettingsGridSettings(Object, RoutedEventArgs)
  614. ///
  615. /// Handles a click on the grid settings menuitem in Settings | Grid Settings. Pops
  616. /// the grid settings dialog window.
  617. /// </summary>
  618. /// <param name="sender"></param>
  619. /// <param name="e"></param>
  620. public void Menu_OnSettingsGridSettings(Object sender, RoutedEventArgs e)
  621. {
  622. ALGridDlg dlg = new ALGridDlg();
  623. dlg.Owner = this;
  624. dlg.ShowDialog();
  625. }
  626.  
  627. /// <summary>
  628. /// Menu_OnSettingsModelName(Object, RoutedEventArgs)
  629. ///
  630. /// Handles a click on the model name menuitem in Settings. Pops the model
  631. /// name dialog window. Tracks changes to the name of the model so it can
  632. /// update the window title bar after the dialog closes.
  633. /// </summary>
  634. /// <param name="sender"></param>
  635. /// <param name="e"></param>
  636. public void Menu_OnSettingsModelName(Object sender, RoutedEventArgs e)
  637. {
  638. ALModelNameDlg dlg = new ALModelNameDlg();
  639. dlg.Owner = this;
  640. dlg.Tag = _ls;
  641. if ( dlg.ShowDialog() == true )
  642. UIStateChange(UIStateChanges.ModelPropertiesEdited);
  643. }
  644.  
  645. /// <summary>
  646. /// Menu_OnHelpHowTo(Object, RoutedEventArgs)
  647. ///
  648. /// Handles a click on the help menu, How to Play command.
  649. /// </summary>
  650. /// <param name="sender"></param>
  651. /// <param name="e"></param>
  652. public void Menu_OnHelpHowTo(Object sender, RoutedEventArgs e)
  653. {
  654. System.Windows.Forms.HelpNavigator nav =
  655. System.Windows.Forms.HelpNavigator.TopicId;
  656.  
  657. System.Windows.Forms.Help.ShowHelp(null, @"avalonlife.chm", nav, "1020");
  658. }
  659.  
  660. /// <summary>
  661. /// Menu_OnHelpAbout(Object, RoutedEventArgs)
  662. ///
  663. /// Handles a click on the help menu, about AvalonLife command.
  664. /// </summary>
  665. /// <param name="sender"></param>
  666. /// <param name="e"></param>
  667. public void Menu_OnHelpAbout(Object sender, RoutedEventArgs e)
  668. {
  669. System.Windows.Forms.HelpNavigator nav =
  670. System.Windows.Forms.HelpNavigator.TopicId;
  671.  
  672. System.Windows.Forms.Help.ShowHelp(null, @"avalonlife.chm", nav, "1000");
  673. }
  674.  
  675. /// <summary>
  676. /// Menu_OnHelpAboutLife(Object, RoutedEventArgs)
  677. ///
  678. /// Handles a click on the help menu, about the Game of Life command.
  679. /// </summary>
  680. /// <param name="sender"></param>
  681. /// <param name="e"></param>
  682. public void Menu_OnHelpAboutLife(Object sender, RoutedEventArgs e)
  683. {
  684. System.Windows.Forms.HelpNavigator nav =
  685. System.Windows.Forms.HelpNavigator.TopicId;
  686.  
  687. System.Windows.Forms.Help.ShowHelp(null, @"avalonlife.chm", nav, "1010");
  688. }
  689.  
  690. #endregion
  691.  
  692. #region other ui event handlers
  693.  
  694. /// <summary>
  695. /// ALMainWin_OnLoaded()
  696. ///
  697. /// Handles the loaded event for the main window. Initializes the simulation
  698. /// model, controller, and display grid.
  699. /// </summary>
  700. /// <param name="sender"></param>
  701. /// <param name="e"></param>
  702. public void ALMainWin_OnLoaded(Object sender, RoutedEventArgs e)
  703. {
  704. ExecNew(true);
  705. RunSpeedSlider.ToolTip = Properties.Resources.UI_RunSpeedSlider_ToolTip;
  706. StatusGenCount.ToolTip = Properties.Resources.UI_StatusGenCount_ToolTip;
  707. CellBirthCount.ToolTip = Properties.Resources.UI_CellBirthCount_ToolTip;
  708. CellDeathCount.ToolTip = Properties.Resources.UI_CellDeathCount_ToolTip;
  709. PopulationCount.ToolTip = Properties.Resources.UI_PopulationCount_ToolTip;
  710. PeakPopulationCount.ToolTip = Properties.Resources.UI_PeakPopulationCount_ToolTip;
  711. MenuGridSizeText.ToolTip = Properties.Resources.UI_GridSize_ToolTip;
  712.  
  713. _gridAdornerLayer = AdornerLayer.GetAdornerLayer(LifeGrid);
  714. ReticleAdorner ad = new ReticleAdorner(LifeGrid);
  715. _gridAdornerLayer.Add(ad);
  716.  
  717. MenuSettingsReticle.IsChecked = ALSettings.Default.ReticleOn;
  718. if ( MenuSettingsReticle.IsChecked )
  719. ad.Visibility = Visibility.Visible;
  720. else
  721. ad.Visibility = Visibility.Hidden;
  722.  
  723.  
  724. LifeGrid.Drop += new DragEventHandler( Grid_OnDrop );
  725. LifeGrid.MouseUp += new MouseButtonEventHandler( Grid_OnMouseUp );
  726. this.MouseLeave += new MouseEventHandler( Window_OnMouseLeave );
  727. this.Closing += new CancelEventHandler( ALMainWin_OnClosing );
  728.  
  729. Application.Current.SessionEnding += new SessionEndingCancelEventHandler( ALMainWin_OnClosing );
  730.  
  731. LifeGrid.Background = new SolidColorBrush(ALSettings.Default.GridBackground);
  732. LifeGrid.ShowGridLines = ALSettings.Default.GridOn;
  733. MenuSettingsGridLines.IsChecked = LifeGrid.ShowGridLines;
  734.  
  735. MenuSettingsGridTorus.Tag = GridType.Torus;
  736. MenuSettingsGridXCyl.Tag = GridType.XCylinder;
  737. MenuSettingsGridYCyl.Tag = GridType.YCylinder;
  738. MenuSettingsGridFinite.Tag = GridType.Finite;
  739.  
  740. MenuSettingsGrid40x40.Tag = 40;
  741. MenuSettingsGrid50x50.Tag = 50;
  742. MenuSettingsGrid60x60.Tag = 60;
  743. MenuSettingsGrid70x70.Tag = 70;
  744.  
  745. if ( ALSettings.Default.HaltOnStability )
  746. MenuSettingsHaltStable.IsChecked = true;
  747. else
  748. MenuSettingsHaltStable.IsChecked = false;
  749. }
  750.  
  751. /// <summary>
  752. /// ALMainWin_OnClosing(Object, CancelEventArgs)
  753. ///
  754. /// This handles the closing event for the main window. If the game is not
  755. /// dirty or the user saves it using 'Ok' in CheckSave, then this handler
  756. /// will commit any settings changes and allow the close to continue. Other
  757. /// wise if the user clicks Cancel in CheckSave it will cancel.
  758. /// </summary>
  759. /// <param name="sender"></param>
  760. /// <param name="e"></param>
  761. public void ALMainWin_OnClosing(Object sender, CancelEventArgs e )
  762. {
  763. bool paused = _ls.IsPaused;
  764. _ls.IsPaused = true;
  765. if ( CheckSave() == true )
  766. {
  767. if ( ALSettings.Default.Changed )
  768. ALSettings.Default.Save();
  769. }
  770. else
  771. {
  772. e.Cancel = true;
  773. }
  774. _ls.IsPaused = paused;
  775. }
  776.  
  777. /// <summary>
  778. /// RunButton_OnClick()
  779. ///
  780. /// Handles the click event for the run button on the main window. The button
  781. /// changes state when it is clicked. The behavior of the sim drives off of
  782. /// the IsPaused property of _ls (LifeSim). When the sim is paused the UI allows
  783. /// editing of the grid cells.
  784. /// </summary>
  785. /// <param name="sender"></param>
  786. /// <param name="e"></param>
  787. public void RunButton_OnClick(Object sender, RoutedEventArgs e)
  788. {
  789. if ( _ls.IsPaused )
  790. {
  791. _ls.IsPaused = false;
  792. UIStateChange(UIStateChanges.ModelRun);
  793. }
  794. else
  795. {
  796. _ls.IsPaused = true;
  797. UIStateChange(UIStateChanges.ModelPaused);
  798. }
  799. }
  800.  
  801. /// <summary>
  802. /// Rect_OnMouseDown(Object, MouseButtonEventArgs)
  803. ///
  804. /// Handles the mouse down event on a Rectangle in the grid, and if the game is
  805. /// in the paused state (editable) flips the cell state. Since we allow dragging
  806. /// _lastMouseCell is used to avoid the effects of repeated MouseEnter events
  807. /// getting fired.
  808. /// </summary>
  809. /// <param name="sender"></param>
  810. /// <param name="e"></param>
  811. public void Rect_OnMouseDown(Object sender, MouseButtonEventArgs e)
  812. {
  813. if ( _ls.IsPaused && (_ls.Generation == 0) )
  814. {
  815. LifeCell lc = ((Rectangle)sender).DataContext as LifeCell;
  816. if ( lc != null )
  817. {
  818. _startEdit = true;
  819. _lastMouseCell = lc;
  820. lc.IsAlive = !lc.IsAlive;
  821. UIStateChange(UIStateChanges.ModelCellEdited);
  822. }
  823. else throw (new System.InvalidOperationException("Rect_OnMouseDown"));
  824. }
  825. }
  826.  
  827. /// <summary>
  828. /// Rect_OnMouseEnter(Object, MouseEventArgs)
  829. ///
  830. /// Handles the mouseover event for an Ellipse. If the game is paused and the mouse
  831. /// is entering a new cell, flip that cell's state.
  832. /// </summary>
  833. /// <param name="sender"></param>
  834. /// <param name="e"></param>
  835. public void Rect_OnMouseEnter(Object sender, MouseEventArgs e)
  836. {
  837. if ( _startEdit )
  838. {
  839. LifeCell lc = ((Rectangle)sender).DataContext as LifeCell;
  840. if ( lc != null && lc != _lastMouseCell )
  841. {
  842. _lastMouseCell = lc;
  843. lc.IsAlive = !lc.IsAlive;
  844. }
  845. else if ( lc == null )
  846. throw (new System.InvalidOperationException("Rect_OnMouseEnter"));
  847. }
  848. }
  849.  
  850. /// <summary>
  851. /// Grid_OnMouseUp(Object, MouseEventArgs)
  852. ///
  853. /// Handles the mouse left button up event for the grid. We use it to check
  854. /// the grid state and enable/disable menu items accordingly.
  855. /// </summary>
  856. /// <param name="sender"></param>
  857. /// <param name="e"></param>
  858. public void Grid_OnMouseUp(Object sender, MouseButtonEventArgs e)
  859. {
  860. if ( _startEdit )
  861. {
  862. _startEdit = false;
  863. if ( _ls.Model.IsEmpty() )
  864. {
  865. UIStateChange(UIStateChanges.ModelCreated);
  866. }
  867. }
  868. }
  869.  
  870. /// <summary>
  871. /// Window_OnMouseLeave(Object, MouseEventArgs)
  872. ///
  873. /// This function makes sure we handle things correctly if the user drags
  874. /// the mouse out of the window while drawing cells. Ordinarily you would
  875. /// capture the mouse and not release it until mouse up, but that's
  876. /// cumbersome here, because we're getting events at the rectangle level
  877. /// but can't capture the mouse there. If we capture it at the grid the
  878. /// rectangles stop getting events. So this is the next best alternative.
  879. /// </summary>
  880. /// <param name="sender"></param>
  881. /// <param name="e"></param>
  882. public void Window_OnMouseLeave(Object sender, MouseEventArgs e)
  883. {
  884. if ( _startEdit )
  885. {
  886. _startEdit = false;
  887. if (_ls.Model.IsEmpty())
  888. {
  889. UIStateChange(UIStateChanges.ModelCreated);
  890. }
  891. }
  892. }
  893.  
  894. /// <summary>
  895. /// Grid_OnDrop(Object, DragEventArgs)
  896. ///
  897. /// This function gets called when an object is dropped on the grid. It handles drops
  898. /// of .cells data from the Life Lexicon website, as well as .avl and .cells saved files.
  899. /// The first half of the function detects a drop of a link from the Lexicon, and calls
  900. /// ExecLoadWeb to get the data from the net. The second half detects file drops, and hands
  901. /// off to ExecFileLoad.
  902. /// </summary>
  903. /// <param name="sender"></param>
  904. /// <param name="e"></param>
  905. public void Grid_OnDrop(Object sender, DragEventArgs e)
  906. {
  907. Cursor old = this.Cursor;
  908. bool force = this.ForceCursor;
  909. this.ForceCursor = true;
  910. this.Cursor = Cursors.Wait;
  911.  
  912. if (e.Data.GetDataPresent(DataFormats.Text))
  913. {
  914. if ( (e.AllowedEffects & DragDropEffects.Link) == DragDropEffects.Link )
  915. {
  916. string url = e.Data.GetData(DataFormats.Text) as string;
  917. if ( Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) )
  918. {
  919. ExecLoadWeb( url );
  920. }
  921. }
  922. }
  923. else if (e.Data.GetDataPresent(DataFormats.FileDrop) )
  924. {
  925. string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
  926. ExecLoadFile(files[files.Length - 1]);
  927. }
  928. this.Cursor = old;
  929. this.ForceCursor = force;
  930. }
  931.  
  932. /// <summary>
  933. /// SimStatusCallback()
  934. ///
  935. /// This function handles a callback from the simulation controller when it detects
  936. /// that model evolution has halted. Will only be called if ALSettings.HaltOnStability
  937. /// is true.
  938. /// </summary>
  939. /// <returns></returns>
  940. public void SimStatusCallback()
  941. {
  942. string msg = null;
  943.  
  944. if ( _ls.Model.EvolutionHalted )
  945. {
  946. msg = Properties.Resources.UI_SimStatus_HaltMsg;
  947. msg += " " + _ls.Generation.ToString();
  948. msg += "\n\nCell births: " + _ls.Model.CellBirths.ToString();
  949. msg += "\nCell deaths: " + _ls.Model.CellDeaths.ToString();
  950. msg += "\nPopulation: " + _ls.Model.Population.ToString();
  951. msg += "\nPeak population: " + _ls.Model.PeakPopulation.ToString();
  952. msg += "\n\nThe simulation has been halted.";
  953.  
  954. MessageBox.Show(this, msg, "Simulation Status", MessageBoxButton.OK, MessageBoxImage.Information);
  955.  
  956. UIStateChange(UIStateChanges.ModelHalted);
  957. }
  958. }
  959.  
  960. #endregion
  961.  
  962. #region ALMainWin private methods
  963.  
  964. /// <summary>
  965. /// UIStateChange(UIStateChanges)
  966. ///
  967. /// This function wraps up all the UI state changes that the program goes through.
  968. /// The possible states are defined in the AvalonLIfe.UIStateChanges enum. Each
  969. /// case of the switch statement handles setting the UI controls and some variables
  970. /// to appropriate settings for a given state.
  971. /// </summary>
  972. /// <param name="uis"></param>
  973. private void UIStateChange(UIStateChanges uis)
  974. {
  975. switch (uis)
  976. {
  977. case UIStateChanges.ModelCreated:
  978. RunButton.Content = Properties.Resources.UI_RunButton_Content1;
  979. RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
  980. LifeGrid.ToolTip = Properties.Resources.UI_LifeGrid_ToolTip;
  981. LifeGrid.Cursor = Cursors.Hand;
  982. RunButton.IsEnabled = false;
  983. MenuGameSave.IsEnabled = false;
  984. MenuGameSaveAs.IsEnabled = false;
  985. MenuGameReset.IsEnabled = false;
  986. SetGameDirty(false);
  987. _currentModelFileBase = null;
  988. break;
  989.  
  990. case UIStateChanges.ModelSaved:
  991. case UIStateChanges.ModelSavedAs:
  992. MenuGameSave.IsEnabled = false;
  993. SetGameDirty(false);
  994. break;
  995.  
  996. case UIStateChanges.ModelLoadedFromFile:
  997. RunButton.Content = Properties.Resources.UI_RunButton_Content1;
  998. RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
  999. RunButton.IsEnabled = true;
  1000. LifeGrid.ToolTip = Properties.Resources.UI_LifeGrid_ToolTip;
  1001. LifeGrid.Cursor = Cursors.Hand;
  1002. MenuGameSave.IsEnabled = false;
  1003. MenuGameSaveAs.IsEnabled = true;
  1004. SetGameDirty(false);
  1005. break;
  1006.  
  1007. case UIStateChanges.ModelLoadedFromNet:
  1008. RunButton.Content = Properties.Resources.UI_RunButton_Content1;
  1009. RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
  1010. RunButton.IsEnabled = true;
  1011. LifeGrid.ToolTip = Properties.Resources.UI_LifeGrid_ToolTip;
  1012. LifeGrid.Cursor = Cursors.Hand;
  1013. MenuGameSave.IsEnabled = true;
  1014. MenuGameSaveAs.IsEnabled = true;
  1015. SetGameDirty(true);
  1016. break;
  1017.  
  1018. case UIStateChanges.ModelCellEdited:
  1019. RunButton.IsEnabled = true;
  1020. MenuGameSave.IsEnabled = true;
  1021. MenuGameSaveAs.IsEnabled = true;
  1022. SetGameDirty(true);
  1023. break;
  1024.  
  1025. case UIStateChanges.ModelRun:
  1026. RunButton.Content = Properties.Resources.UI_RunButton_Content2;
  1027. RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip2;
  1028. LifeGrid.Cursor = Cursors.Arrow;
  1029. LifeGrid.ToolTip = null;
  1030. MenuGameReset.IsEnabled = true;
  1031. break;
  1032.  
  1033. case UIStateChanges.ModelPaused:
  1034. RunButton.Content = Properties.Resources.UI_RunButton_Content1;
  1035. RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
  1036. break;
  1037.  
  1038. case UIStateChanges.ModelHalted:
  1039. RunButton.Content = Properties.Resources.UI_RunButton_Content1;
  1040. RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
  1041. RunButton.IsEnabled = false;
  1042. break;
  1043.  
  1044. case UIStateChanges.ModelReset:
  1045. RunButton.Content = Properties.Resources.UI_RunButton_Content1;
  1046. RunButton.ToolTip = Properties.Resources.UI_RunButton_ToolTip1;
  1047. RunButton.IsEnabled = true;
  1048. LifeGrid.ToolTip = Properties.Resources.UI_LifeGrid_ToolTip;
  1049. LifeGrid.Cursor = Cursors.Hand;
  1050. MenuGameReset.IsEnabled = false;
  1051. break;
  1052.  
  1053. case UIStateChanges.ModelPropertiesEdited:
  1054. MenuGameSave.IsEnabled = true;
  1055. SetGameDirty(true);
  1056. break;
  1057. }
  1058. }
  1059.  
  1060. /// <summary>
  1061. /// UpdateWrapInidicators(bool, bool, bool, bool)
  1062. ///
  1063. /// This method is called from SetGridType() to enable/disable the wrap indicator
  1064. /// bars that border the grid.
  1065. /// </summary>
  1066. /// <param name="left">True if the left bar is on, else false</param>
  1067. /// <param name="top">True if the top bar is on, else false</param>
  1068. /// <param name="right">True if the right bar is on, else false</param>
  1069. /// <param name="bottom">True if the bottom bar is on, else false</param>
  1070. private void UpdateWrapIndicators( bool left, bool top, bool right, bool bottom )
  1071. {
  1072. if ( left )
  1073. LeftWrapIndicator.Width = 4;
  1074. else
  1075. LeftWrapIndicator.Width = 0;
  1076.  
  1077. if (top)
  1078. TopWrapIndicator.Height = 5;
  1079. else
  1080. TopWrapIndicator.Height = 0;
  1081.  
  1082. if (right)
  1083. RightWrapIndicator.Width = 4;
  1084. else
  1085. RightWrapIndicator.Width = 0;
  1086.  
  1087. if (bottom)
  1088. BottomWrapIndicator.Height = 5;
  1089. else
  1090. BottomWrapIndicator.Height = 0;
  1091. }
  1092.  
  1093. /// <summary>
  1094. /// SetGridType( GridType )
  1095. ///
  1096. /// This method is called from ExecLoad or Menu_OnGridXXX to update the menu and UI to
  1097. /// correspond with the grid type of the loaded model.
  1098. /// </summary>
  1099. /// <param name="gridType"></param>
  1100. private void SetGridType( GridType gridType )
  1101. {
  1102. MenuSettingsGridTorus.IsChecked = false;
  1103. MenuSettingsGridXCyl.IsChecked = false;
  1104. MenuSettingsGridYCyl.IsChecked = false;
  1105. MenuSettingsGridFinite.IsChecked = false;
  1106.  
  1107. switch ( gridType )
  1108. {
  1109. case GridType.Torus:
  1110. MenuSettingsGridTorus.IsChecked = true;
  1111. UpdateWrapIndicators(false, false, false, false);
  1112. break;
  1113.  
  1114. case GridType.XCylinder:
  1115. MenuSettingsGridXCyl.IsChecked = true;
  1116. UpdateWrapIndicators(false, true, false, true);
  1117. break;
  1118.  
  1119. case GridType.YCylinder:
  1120. MenuSettingsGridYCyl.IsChecked = true;
  1121. UpdateWrapIndicators(true, false, true, false);
  1122. break;
  1123.  
  1124. case GridType.Finite:
  1125. MenuSettingsGridFinite.IsChecked = true;
  1126. UpdateWrapIndicators(true, true, true, true);
  1127. break;
  1128. }
  1129. }
  1130.  
  1131. /// <summary>
  1132. /// SetWindowTitle()
  1133. ///
  1134. /// Called from one or two spots to set the proper window title.
  1135. /// </summary>
  1136. private void SetWindowTitle()
  1137. {
  1138. this.Title = _winTitleBase;
  1139. if (_ls.Model.ModelName != null && _ls.Model.ModelName.Length > 0)
  1140. this.Title += " [" + _ls.Model.ModelName;
  1141. else
  1142. this.Title += " [Untitled";
  1143.  
  1144. if (_gameIsDirty)
  1145. this.Title += "*";
  1146.  
  1147. this.Title += "]";
  1148. }
  1149.  
  1150. /// <summary>
  1151. /// SetGameDirty(bool)
  1152. ///
  1153. /// Called from various places where the current game becomes "dirty", or in need
  1154. /// of saving to disk. Called with true if the game has become dirty. Updates
  1155. /// the _gameIsDirty flag and window title on a state change.
  1156. /// </summary>
  1157. /// <param name="dirty"></param>
  1158. private void SetGameDirty( bool dirty )
  1159. {
  1160. _gameIsDirty = dirty;
  1161. SetWindowTitle();
  1162. }
  1163.  
  1164. /// <summary>
  1165. /// SetGridSizeMenu()
  1166. ///
  1167. /// Called from InitUIState to update the state of the grid size menu
  1168. /// to reflect the size of a model.
  1169. /// </summary>
  1170. private void SetGridSizeMenu()
  1171. {
  1172. MenuSettingsGrid40x40.IsChecked = false;
  1173. MenuSettingsGrid50x50.IsChecked = false;
  1174. MenuSettingsGrid60x60.IsChecked = false;
  1175. MenuSettingsGrid70x70.IsChecked = false;
  1176. MenuSettingsGridCustom.IsChecked = false;
  1177.  
  1178. if ((_ls.Model.Columns != _ls.Model.Rows) ||
  1179. (_ls.Model.Columns != 40 && _ls.Model.Columns != 50 &&
  1180. _ls.Model.Columns != 60 && _ls.Model.Columns != 70))
  1181. {
  1182. MenuSettingsGridCustom.IsChecked = true;
  1183. }
  1184. else
  1185. {
  1186. switch (_ls.Model.Rows)
  1187. {
  1188. case 40:
  1189. MenuSettingsGrid40x40.IsChecked = true;
  1190. break;
  1191. case 50:
  1192. MenuSettingsGrid50x50.IsChecked = true;
  1193. break;
  1194. case 60:
  1195. MenuSettingsGrid60x60.IsChecked = true;
  1196. break;
  1197. case 70:
  1198. MenuSettingsGrid70x70.IsChecked = true;
  1199. break;
  1200. }
  1201. }
  1202. MenuGridSizeText.Text = "r:" + _ls.Model.Rows.ToString() + " c:" + _ls.Model.Columns.ToString();
  1203. }
  1204.  
  1205. /// <summary>
  1206. /// InitUIState()
  1207. ///
  1208. /// This function peforms common setup work when a game is created or loaded.
  1209. /// Initializes the grid and populates it, sets up some data contexts, and sets
  1210. /// the window title.
  1211. /// </summary>
  1212. private void InitUIState()
  1213. {
  1214. InitGrid();
  1215. PopulateGrid();
  1216. ApplyRectStyle();
  1217. SetGridSizeMenu();
  1218. SetWindowTitle();
  1219. SetGridType(_ls.Model.LifeGridType);
  1220.  
  1221. StatusGenCount.DataContext = _ls;
  1222. RunSpeedSlider.DataContext = _ls;
  1223. CellBirthCount.DataContext = _ls.Model;
  1224. CellDeathCount.DataContext = _ls.Model;
  1225. PopulationCount.DataContext = _ls.Model;
  1226. PeakPopulationCount.DataContext = _ls.Model;
  1227. }
  1228.  
  1229. /// <summary>
  1230. /// ExecNew()
  1231. ///
  1232. /// Does the grunt work of initializing a new game with an empty grid. Initializes
  1233. /// the model and controller, populates the grid, and wires up some UI fields by setting
  1234. /// data contexts for items with property bindings.
  1235. /// </summary>
  1236. private bool ExecNew(bool defaults)
  1237. {
  1238. bool result = false;
  1239.  
  1240. if ( CheckSave() == true )
  1241. {
  1242. if ( !defaults)
  1243. {
  1244. ALModelSizeDlg dlg = new ALModelSizeDlg();
  1245. dlg.Owner = this;
  1246. dlg.Tag = _ls.Model;
  1247. if ( dlg.ShowDialog() == true )
  1248. {
  1249. int rows = dlg.Rows;
  1250. int cols = dlg.Columns;
  1251. if ( rows * cols > 10000 )
  1252. {
  1253. string msg = PrepMessage(Properties.Resources.UI_MB_LargeModelMsg, 80);
  1254. msg += "\n\nRequested model size will cause the program to create approximately "
  1255. + Convert.ToString((rows * cols) * 3) + " objects.";
  1256. if ( MessageBox.Show(this, msg, Properties.Resources.UI_MB_LargeModelCaption,
  1257. MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.Cancel )
  1258. {
  1259. return result;
  1260. }
  1261. }
  1262. if (_ls == null)
  1263. {
  1264. _ls = new LifeSim(rows, cols);
  1265. _ls.UICallback = SimStatusCallback;
  1266. }
  1267. else
  1268. _ls.NewModel(rows, cols);
  1269. }
  1270. else return result;
  1271. }
  1272. else
  1273. {
  1274. if (_ls == null)
  1275. {
  1276. _ls = new LifeSim();
  1277. _ls.UICallback = SimStatusCallback;
  1278. }
  1279. else
  1280. _ls.NewModel();
  1281. }
  1282. InitUIState();
  1283. UIStateChange(UIStateChanges.ModelCreated);
  1284. result = true;
  1285. }
  1286. return result;
  1287. }
  1288.  
  1289. /// <summary>
  1290. /// ExecLoadWeb(string)
  1291. ///
  1292. /// This function handles loading a stream of cells from the Life Lexicon website.
  1293. /// Most of the work is actually done in LifeModel.LifeModel(Stream), which is called
  1294. /// from LifeSim.NewModel(Stream).
  1295. /// </summary>
  1296. /// <param name="url"></param>
  1297. /// <returns></returns>
  1298. private bool ExecLoadWeb( string url )
  1299. {
  1300. bool result = false;
  1301. if (CheckSave() == true)
  1302. {
  1303. Uri uri = new Uri(url);
  1304.  
  1305. char[] sep = { '.' };
  1306. string[] splitstr = uri.LocalPath.Split(sep);
  1307. if (string.Compare("cells", splitstr[splitstr.Length - 1], true) == 0)
  1308. {
  1309. HttpWebRequest cellReq = (HttpWebRequest)HttpWebRequest.Create(uri);
  1310. cellReq.Timeout = 10000;
  1311. cellReq.UserAgent = "AvalonLife_1_0";
  1312. try
  1313. {
  1314. HttpWebResponse cellRes = (HttpWebResponse)cellReq.GetResponse();
  1315. if ( _ls.NewModel(cellRes.GetResponseStream()) )
  1316. {
  1317. InitUIState();
  1318. UIStateChange(UIStateChanges.ModelLoadedFromNet);
  1319. _currentModelFileType = ALFileType.None;
  1320. result = true;
  1321. }
  1322. else
  1323. MessageBox.Show(this, "Failed to load model data.", "Load Error",
  1324. MessageBoxButton.OK, MessageBoxImage.Error);
  1325.  
  1326. cellRes.Close();
  1327. }
  1328. catch (WebException wex)
  1329. {
  1330. string msg = "Failed to retrieve cell data. Response: ";
  1331. msg += wex.Response;
  1332. MessageBox.Show(this, msg, "Network Error",
  1333. MessageBoxButton.OK, MessageBoxImage.Error);
  1334. }
  1335. }
  1336. else
  1337. {
  1338. string msg = "Invalid data type: " + url;
  1339. MessageBox.Show(this, msg, "Invalid Data",
  1340. MessageBoxButton.OK, MessageBoxImage.Error);
  1341. }
  1342. }
  1343. return result;
  1344. }
  1345.  
  1346. /// <summary>
  1347. /// ExecLoadFile(string)
  1348. ///
  1349. /// Does the work of loading a game from a file. Handles saved .avl files as
  1350. /// well as .cells files saved from Life Lexicon data.
  1351. /// </summary>
  1352. /// <returns></returns>
  1353. private bool ExecLoadFile(string fileName)
  1354. {
  1355. bool result = false;
  1356.  
  1357. if (CheckSave() == true)
  1358. {
  1359. ALFileType ft = GetFileType(fileName);
  1360. if ( ft == ALFileType.AVL )
  1361. {
  1362. BinaryFormatter formatter = new BinaryFormatter();
  1363. Stream str = File.OpenRead(fileName);
  1364. try
  1365. {
  1366. _ls = formatter.Deserialize(str) as LifeSim;
  1367. _ls.UICallback = SimStatusCallback;
  1368. SetFileType(fileName);
  1369. InitUIState();
  1370. UIStateChange(UIStateChanges.ModelLoadedFromFile);
  1371. result = true;
  1372. }
  1373. catch (System.Runtime.Serialization.SerializationException ex)
  1374. {
  1375. MessageBox.Show(this, Properties.Resources.UI_MB_LoadFailedMsg + " " + ex.Message,
  1376. Properties.Resources.UI_MB_LoadFailedCaption,
  1377. MessageBoxButton.OK, MessageBoxImage.Error);
  1378. }
  1379. finally
  1380. {
  1381. str.Close();
  1382. }
  1383. SetGameDirty(false);
  1384. }
  1385. else if ( ft == ALFileType.Cells )
  1386. {
  1387. Stream str = File.OpenRead(fileName);
  1388. if ( _ls.NewModel(str) )
  1389. {
  1390. SetFileType(fileName);
  1391. InitUIState();
  1392. UIStateChange(UIStateChanges.ModelLoadedFromFile);
  1393. result = true;
  1394. }
  1395. else
  1396. MessageBox.Show(this, "Failed to load model data.", "Load Error",
  1397. MessageBoxButton.OK, MessageBoxImage.Error);
  1398. str.Close();
  1399. }
  1400. else
  1401. {
  1402. string msg = "Invalid file type: " + fileName;
  1403. MessageBox.Show(this, msg, "Invalid File",
  1404. MessageBoxButton.OK, MessageBoxImage.Error);
  1405. }
  1406. }
  1407. return result;
  1408. }
  1409.  
  1410. /// <summary>
  1411. /// ExecSave()
  1412. ///
  1413. /// Called from the menu savegame handler, or from the load game handler
  1414. /// if necessary. Saves the current model to disk.
  1415. /// </summary>
  1416. private void ExecSave( bool saveAs )
  1417. {
  1418. System.Windows.Forms.SaveFileDialog savedlg =
  1419. new System.Windows.Forms.SaveFileDialog();
  1420.  
  1421. savedlg.AddExtension = true;
  1422. savedlg.Filter = "AvalonLife Saved Games (.avl)|*.avl|Life Lexicon Cells (.cells)|*.cells";
  1423. bool haveFile = false;
  1424.  
  1425. if (_currentModelFileBase != null && _currentModelFileType != ALFileType.None)
  1426. {
  1427. haveFile = true;
  1428. savedlg.FileName = _currentModelFileBase;
  1429. if ( _currentModelFileType == ALFileType.Cells )
  1430. {
  1431. savedlg.DefaultExt = ".cells";
  1432. savedlg.FilterIndex = 2;
  1433. }
  1434. else if ( _currentModelFileType == ALFileType.AVL )
  1435. {
  1436. savedlg.DefaultExt = ".avl";
  1437. savedlg.FilterIndex = 1;
  1438. }
  1439. }
  1440. else
  1441. {
  1442. savedlg.FileName = "AvalonLife Saved Game";
  1443. savedlg.DefaultExt = ".avl";
  1444. savedlg.FilterIndex = 1;
  1445. }
  1446.  
  1447. if ( saveAs == true )
  1448. savedlg.Title = "Save Model As";
  1449. else
  1450. savedlg.Title = "Save Model";
  1451.  
  1452. if (savedlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  1453. {
  1454.  
  1455. ALFileType ft = GetFileType(savedlg.FileName);
  1456.  
  1457. if ( ft == ALFileType.AVL )
  1458. {
  1459. BinaryFormatter formatter = new BinaryFormatter();
  1460. Stream str = File.OpenWrite(savedlg.FileName);
  1461. try
  1462. {
  1463. formatter.Serialize(str, _ls);
  1464. if ( !saveAs || !haveFile )
  1465. SetFileType(savedlg.FileName);
  1466. UIStateChange(UIStateChanges.ModelSaved);
  1467. }
  1468. catch (System.Exception ex)
  1469. {
  1470. MessageBox.Show(this, Properties.Resources.UI_MB_SaveFailedMsg + " " + ex.InnerException.Message,
  1471. Properties.Resources.UI_MB_SaveFailedCaption,
  1472. MessageBoxButton.OK, MessageBoxImage.Error);
  1473. }
  1474. finally
  1475. {
  1476. str.Close();
  1477. }
  1478. }
  1479. else if ( ft == ALFileType.Cells )
  1480. {
  1481. Stream str = File.OpenWrite(savedlg.FileName);
  1482. try
  1483. {
  1484. _ls.Model.StreamCells(str);
  1485. if (!saveAs || !haveFile)
  1486. SetFileType(savedlg.FileName);
  1487. UIStateChange(UIStateChanges.ModelSaved);
  1488. }
  1489. catch (System.Exception)
  1490. {
  1491. }
  1492. finally
  1493. {
  1494. str.Close();
  1495. }
  1496. }
  1497. else
  1498. {
  1499. string msg = "Invalid file type: " + savedlg.FileName;
  1500. MessageBox.Show(this, msg, "Invalid File",
  1501. MessageBoxButton.OK, MessageBoxImage.Error);
  1502. }
  1503. }
  1504. }
  1505.  
  1506. /// <summary>
  1507. /// CheckSave()
  1508. ///
  1509. /// This utility function checks to see if the current game is dirty. If it is
  1510. /// it asks the user whether they want to save the game before continuing with
  1511. /// whatever operation called this function. Returns true if the operation is
  1512. /// good to proceed, false if the user clicks cancel.
  1513. /// </summary>
  1514. /// <returns></returns>
  1515. private bool CheckSave()
  1516. {
  1517. bool result = true;
  1518.  
  1519. if (_gameIsDirty)
  1520. {
  1521. MessageBoxResult mbres = MessageBox.Show( this, Properties.Resources.UI_MB_PromptText3, Properties.Resources.UI_MB_CaptionText3,
  1522. MessageBoxButton.YesNoCancel, MessageBoxImage.Question );
  1523. if ( mbres == MessageBoxResult.Yes )
  1524. ExecSave(false);
  1525. else if ( mbres == MessageBoxResult.No )
  1526. SetGameDirty(false);
  1527. else if ( mbres == MessageBoxResult.Cancel )
  1528. result = false;
  1529. }
  1530. return result;
  1531. }
  1532.  
  1533. /// <summary>
  1534. /// InitGrid()
  1535. ///
  1536. /// Called from the OnLoaded event handler for the main window to initialize the
  1537. /// UI display grid with the appropriate number of rows and columns based on the
  1538. /// _lm.Rows and _lm.Columns properties. It then adds an ellipse to each cell and
  1539. /// sets its style.
  1540. /// </summary>
  1541. private void InitGrid()
  1542. {
  1543. LifeGrid.Children.Clear();
  1544. LifeGrid.RowDefinitions.Clear();
  1545. LifeGrid.ColumnDefinitions.Clear();
  1546.  
  1547. for (int i = 0; i < _ls.Model.Rows; i++)
  1548. {
  1549. LifeGrid.RowDefinitions.Add(new RowDefinition());
  1550. }
  1551. for (int i = 0; i < _ls.Model.Columns; i++)
  1552. {
  1553. LifeGrid.ColumnDefinitions.Add(new ColumnDefinition());
  1554. }
  1555. }
  1556.  
  1557. /// <summary>
  1558. /// GetCellBrush()
  1559. ///
  1560. /// Constructs a brush from the settings in the config file.
  1561. /// </summary>
  1562. /// <returns></returns>
  1563. private Brush GetCellBrush()
  1564. {
  1565. CellBrushType brushType = ALSettings.Default.LifeCellBrushType;
  1566.  
  1567. if ( brushType == CellBrushType.Radial )
  1568. {
  1569. RadialGradientBrush brush = new RadialGradientBrush();
  1570. brush.GradientOrigin = new Point(0.5, 0.5);
  1571. brush.RadiusX = 0.5;
  1572. brush.RadiusY = 0.5;
  1573.  
  1574. brush.GradientStops.Add( new GradientStop(ALSettings.Default.CellBrushC1, ALSettings.Default.CellBrushC1Off) );
  1575. brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC2, ALSettings.Default.CellBrushC2Off));
  1576. brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC3, ALSettings.Default.CellBrushC3Off));
  1577.  
  1578. brush.Freeze();
  1579.  
  1580. return brush;
  1581. }
  1582. else if ( brushType == CellBrushType.Linear )
  1583. {
  1584. LinearGradientBrush brush = new LinearGradientBrush();
  1585. brush.StartPoint = new Point(0, 0);
  1586. brush.EndPoint = new Point(1, 1);
  1587.  
  1588. brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC1, ALSettings.Default.CellBrushC1Off));
  1589. brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC2, ALSettings.Default.CellBrushC2Off));
  1590. brush.GradientStops.Add(new GradientStop(ALSettings.Default.CellBrushC3, ALSettings.Default.CellBrushC3Off));
  1591.  
  1592. brush.Freeze();
  1593.  
  1594. return brush;
  1595. }
  1596. else if ( brushType == CellBrushType.Solid )
  1597. {
  1598. SolidColorBrush brush = new SolidColorBrush(ALSettings.Default.CellBrushC1);
  1599. brush.Freeze();
  1600.  
  1601. return brush;
  1602. }
  1603. else
  1604. {
  1605. SolidColorBrush brush = new SolidColorBrush(Colors.Red);
  1606. brush.Freeze();
  1607.  
  1608. return brush;
  1609. }
  1610. }
  1611.  
  1612. /// <summary>
  1613. /// ApplyRectStyle()
  1614. ///
  1615. /// If you look at the source you'll see that this function is only called when a new
  1616. /// grid is being initialized, and when the cell brush has been changed. It builds a new
  1617. /// style that sets the Fill property of a rectangle to the new brush, and then goes
  1618. /// through the children of the grid setting this style. The style is based on an
  1619. /// existing style that binds the opacity property to govern visibility. So why go to
  1620. /// all this trouble to change fill brushes? Why not just set the fill property on the
  1621. /// rects and be done with it? Here's the issue: again, opacity is controlled by a
  1622. /// binding in a style. Element properties override style settings, and they happen at
  1623. /// different times too. If in the process of creating a new grid I set the rectangle
  1624. /// DataContext to point to a LifeCell, which will drive the opacity binding, and then
  1625. /// set the Fill property directly, sometimes, depending on timing, I get a repaint
  1626. /// before the opacity property is correctly set, and the grid renders all the cells
  1627. /// visible. It looks messy, and I don't want the grid repainted until the state of all
  1628. /// the cells is correct. I'm sure there must be other ways to handle suppressing the
  1629. /// repaint, but the issue there is that the flash happens after I return control to
  1630. /// the message pump. If I somehow surpress the repaint when will I unsurpress it? The
  1631. /// best way around this that I have found so far is to do as I have below: change
  1632. /// brushes by building a new style and then applying that style.
  1633. /// </summary>
  1634. private void ApplyRectStyle()
  1635. {
  1636. Brush cellBrush = GetCellBrush();
  1637.  
  1638. Style style = new Style(typeof(Rectangle), (Style)LifeGrid.FindResource(typeof(Rectangle)));
  1639. Setter setter = new Setter();
  1640. setter.Property = Rectangle.FillProperty;
  1641. setter.Value = cellBrush;
  1642. style.Setters.Add(setter);
  1643. LifeGrid.Resources.Remove("RectStyle");
  1644. LifeGrid.Resources.Add("RectStyle", style);
  1645.  
  1646. UIElementCollection rects = LifeGrid.Children;
  1647.  
  1648. foreach (UIElement uie in rects)
  1649. ((Rectangle)uie).Style = (Style)(LifeGrid.Resources["RectStyle"]);
  1650. }
  1651.  
  1652. /// <summary>
  1653. /// PopulateGrid()
  1654. ///
  1655. /// Does the work of setting up the rectangles in the cells of the life grid. Creates
  1656. /// the rectangles and assigns them to the grid, adds them to the child collection,
  1657. /// sets up rectangle data contexts for the rect->cell link, sets the rectangle style,
  1658. /// and wires up the rectangle mouse events
  1659. /// </summary>
  1660. private void PopulateGrid()
  1661. {
  1662. for (int row = 0; row < _ls.Model.Rows; row++)
  1663. {
  1664. for (int col = 0; col < _ls.Model.Columns; col++)
  1665. {
  1666. Rectangle rect = new Rectangle();
  1667. Grid.SetRow(rect, row);
  1668. Grid.SetColumn(rect, col);
  1669. LifeGrid.Children.Add(rect);
  1670. rect.DataContext = _ls.Model.CellGrid[row, col];
  1671. rect.MouseDown += new MouseButtonEventHandler(Rect_OnMouseDown);
  1672. rect.MouseMove += new MouseEventHandler(Rect_OnMouseEnter);
  1673. }
  1674. }
  1675. }
  1676.  
  1677. /// <summary>
  1678. /// GetFileType(string)
  1679. ///
  1680. /// Called from ExecSave to determine the type of file that the user selected.
  1681. /// </summary>
  1682. /// <param name="fileName"></param>
  1683. /// <returns></returns>
  1684. private ALFileType GetFileType( string fileName )
  1685. {
  1686. string ext = System.IO.Path.GetExtension(fileName);
  1687. ALFileType ft = ALFileType.None;
  1688.  
  1689. if (string.Compare(".avl", ext, true) == 0)
  1690. ft = ALFileType.AVL;
  1691. else if (string.Compare(".cells", ext, true) == 0)
  1692. ft = ALFileType.Cells;
  1693.  
  1694. return ft;
  1695. }
  1696.  
  1697. /// <summary>
  1698. /// SetFileType( string )
  1699. ///
  1700. /// Called from the ExecSave and ExecLoad functions. Retrieves and stores
  1701. /// the base filename and sets the file format type.
  1702. /// </summary>
  1703. /// <param name="fileName"></param>
  1704. private ALFileType SetFileType( string fileName )
  1705. {
  1706. _currentModelFileBase = System.IO.Path.GetFileNameWithoutExtension(fileName);
  1707. string ext = System.IO.Path.GetExtension(fileName);
  1708.  
  1709. if ( string.Compare(".avl", ext, true) == 0 )
  1710. _currentModelFileType = ALFileType.AVL;
  1711. else if ( string.Compare(".cells", ext, true) == 0 )
  1712. _currentModelFileType = ALFileType.Cells;
  1713. else
  1714. _currentModelFileType = ALFileType.None;
  1715.  
  1716. return _currentModelFileType;
  1717. }
  1718.  
  1719. #endregion
  1720.  
  1721. /// <summary>
  1722. /// Holds the instance of the simulation controller
  1723. /// </summary>
  1724. private LifeSim _ls = null;
  1725.  
  1726. /// <summary>
  1727. /// True if the game has been modified since last save
  1728. /// </summary>
  1729. private bool _gameIsDirty = false;
  1730.  
  1731. /// <summary>
  1732. /// Tracks the last cell that the mouse was in
  1733. /// </summary>
  1734. private LifeCell _lastMouseCell = null;
  1735.  
  1736. /// <summary>
  1737. /// Container for the ReticleAdorner
  1738. /// </summary>
  1739. private AdornerLayer _gridAdornerLayer = null;
  1740.  
  1741. /// <summary>
  1742. /// True if we are dragging across cells in edit mode
  1743. /// </summary>
  1744. private bool _startEdit = false;
  1745.  
  1746. /// <summary>
  1747. /// If not null contains the last file name used to load or
  1748. /// save the current model.
  1749. /// </summary>
  1750. private string _currentModelFileBase = null;
  1751.  
  1752. private ALFileType _currentModelFileType = ALFileType.AVL;
  1753.  
  1754. /// <summary>
  1755. /// Self-explanatory
  1756. /// </summary>
  1757. private string _winTitleBase = "AvalonLife 1.0";
  1758. }
  1759. }

No responses yet

Apr 26 2008

AvalonLife Listing 3 – ALMainWin.xaml

Published by Mark under Code

  1. <Window x:Class="AvalonLife.ALMainWin"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="AvalonLife 1.0" Height="600" Width="600" Name="ALMainWindow" Background="#FFFFFFFF"
  5. Loaded="ALMainWin_OnLoaded" >
  6. <Window.Resources>
  7. <Style x:Key="RunSpeedSliderStyle" TargetType="{x:Type Slider}">
  8. <Setter Property="Width" Value="120" />
  9. <Setter Property="Value" Value="{Binding Path=TimerInterval, Mode=TwoWay}" />
  10. <Setter Property="Orientation" Value="Horizontal" />
  11. <Setter Property="HorizontalAlignment" Value="Left" />
  12. <Setter Property="IsSnapToTickEnabled" Value="True" />
  13. <Setter Property="Minimum" Value="100" />
  14. <Setter Property="Maximum" Value="1000" />
  15. <Setter Property="TickPlacement" Value="BottomRight" />
  16. <Setter Property="TickFrequency" Value="100" />
  17. <Setter Property="IsDirectionReversed" Value="True" />
  18. <Setter Property="IsMoveToPointEnabled" Value="True" />
  19. </Style>
  20. <Style x:Key="GenCountStyle" TargetType="{x:Type TextBlock}">
  21. <Setter Property="Foreground" Value="Blue" />
  22. <Setter Property="Text" Value="{Binding Path=Generation}" />
  23. </Style>
  24. <Style x:Key="BirthCountStyle" TargetType="{x:Type TextBlock}">
  25. <Setter Property="Foreground" Value="Blue" />
  26. <Setter Property="Text" Value="{Binding Path=CellBirths}" />
  27. </Style>
  28. <Style x:Key="DeathCountStyle" TargetType="{x:Type TextBlock}">
  29. <Setter Property="Foreground" Value="Blue" />
  30. <Setter Property="Text" Value="{Binding Path=CellDeaths}" />
  31. </Style>
  32. <Style x:Key="PopulationCountStyle" TargetType="{x:Type TextBlock}">
  33. <Setter Property="Foreground" Value="Blue" />
  34. <Setter Property="Text" Value="{Binding Path=Population}" />
  35. </Style>
  36. <Style x:Key="PeakPopulationCountStyle" TargetType="{x:Type TextBlock}">
  37. <Setter Property="Foreground" Value="Blue" />
  38. <Setter Property="Text" Value="{Binding Path=PeakPopulation}" />
  39. </Style>
  40. </Window.Resources>
  41. <Grid>
  42. <DockPanel>
  43. <DockPanel DockPanel.Dock="Top" LastChildFill="False"
  44. Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}">
  45. <Menu Margin="0,5,5,2" DockPanel.Dock="Left" Width="Auto" HorizontalAlignment="Left"
  46. Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" >
  47. <MenuItem Header="Game">
  48. <MenuItem Name="MenuGameNew" Header="New" Click="Menu_OnGameNew" />
  49. <MenuItem Name="MenuGameReset" Header="Reset" Click="Menu_OnGameReset" />
  50. <MenuItem Name="MenuGameSave" Header="Save..." Click="Menu_OnGameSave" />
  51. <MenuItem Name="MenuGameSaveAs" Header="Save as..." Click="Menu_OnGameSaveAs" />
  52. <MenuItem Name="MenuGameLoad" Header="Load..." Click="Menu_OnGameLoad" />
  53. <Separator />
  54. <MenuItem Header="Exit" Click="Menu_OnGameExit" />
  55. </MenuItem>
  56. <MenuItem Header="Settings">
  57. <MenuItem Name="MenuSettingsGridLines" Header="Show Grid" Click="Menu_OnSettingsGridLines" />
  58. <MenuItem Name="MenuSettingsReticle" Header="Show Reticle" Click="Menu_OnSettingsReticle" />
  59. <MenuItem Name="MenuSettingsHaltStable" Header="Halt Stable Model" Click="Menu_OnSettingsHaltStable" />
  60. <Separator />
  61. <MenuItem Name="MenuSettingsGridBkgColor" Header="Grid Background..." Click="Menu_OnSettingsGridBackground" />
  62. <MenuItem Name="MenuSettingsReticleColor" Header="Reticle Color..." Click="Menu_OnSettingsReticleColor" />
  63. <MenuItem Name="MenuSettingsCellBrush" Header="Cell Color..." Click="Menu_OnSettingsCellBrush" />
  64. <Separator />
  65. <MenuItem Header="Grid Type">
  66. <MenuItem Name="MenuSettingsGridTorus" Header="Torus" Click="Menu_OnSettingsGridType" />
  67. <MenuItem Name="MenuSettingsGridXCyl" Header="X Cylinder" Click="Menu_OnSettingsGridType" />
  68. <MenuItem Name="MenuSettingsGridYCyl" Header="Y Cylinder" Click="Menu_OnSettingsGridType" />
  69. <MenuItem Name="MenuSettingsGridFinite" Header="Finite" Click="Menu_OnSettingsGridType" />
  70. </MenuItem>
  71. <MenuItem Header="Grid Size">
  72. <MenuItem Name="MenuSettingsGrid40x40" Header="40 x 40" Click="Menu_OnSettingsGridSize" />
  73. <MenuItem Name="MenuSettingsGrid50x50" Header="50 x 50" Click="Menu_OnSettingsGridSize" />
  74. <MenuItem Name="MenuSettingsGrid60x60" Header="60 x 60" Click="Menu_OnSettingsGridSize" />
  75. <MenuItem Name="MenuSettingsGrid70x70" Header="70 x 70" Click="Menu_OnSettingsGridSize" />
  76. <Separator />
  77. <MenuItem Name="MenuSettingsGridCustom" Header="Custom..." Click="Menu_OnSettingsGridSizeCustom" />
  78. <Separator />
  79. <MenuItem Name="MenuSettingsGridShrink" Header="Shrink to Model" Click="Menu_OnSettingsGridSizeShrink" />
  80. </MenuItem>
  81. <MenuItem Name="MenuSettingsGridSettings" Header="Grid Settings..." Click="Menu_OnSettingsGridSettings" />
  82. <Separator />
  83. <MenuItem Name="MenuSettingsModelName" Header="Model Name..." Click="Menu_OnSettingsModelName" />
  84. </MenuItem>
  85. <MenuItem Header="Help">
  86. <MenuItem Name="MenuHelpHowTo" Header="How to Play..." Click="Menu_OnHelpHowTo" />
  87. <MenuItem Name="MenuHelpAbout" Header="About AvalonLife..." Click="Menu_OnHelpAbout" />
  88. <MenuItem Name="MenuHelpAboutLife" Header="About the Game of Life..." Click="Menu_OnHelpAboutLife" />
  89. </MenuItem>
  90. </Menu>
  91. <Button Margin="10,5,5,2" DockPanel.Dock="Right" Name="RunButton" VerticalAlignment="Center" HorizontalAlignment="Right"
  92. Click="RunButton_OnClick" Height="20" Width="60" Content="Run" />
  93. <TextBlock Margin="0,5,5,2" VerticalAlignment="Center" Width="60" Foreground="Blue" Name="MenuGridSizeText" DockPanel.Dock="Right" />
  94. <TextBlock Margin="0,5,5,2" VerticalAlignment="Center" Width="50" Text="Grid Size:" DockPanel.Dock="Right" />
  95. </DockPanel>
  96. <Canvas Name="TopWrapIndicator" Height="5" Width="Auto" DockPanel.Dock="Top" HorizontalAlignment="Stretch" Background="Gray" />
  97. <StatusBar Background="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}"
  98. Height="30" DockPanel.Dock="Bottom" Padding="4,0,4,0">
  99. <StatusBarItem>
  100. <TextBlock Width="Auto" Text="Time:" />
  101. </StatusBarItem>
  102. <StatusBarItem>
  103. <TextBlock Name="StatusGenCount" Width="30" Style="{StaticResource GenCountStyle}" />
  104. </StatusBarItem>
  105. <StatusBarItem>
  106. <TextBlock Width="Auto" Text="Census:" />
  107. </StatusBarItem>
  108. <StatusBarItem>
  109. <TextBlock Name="PopulationCount" Width="30" Style="{StaticResource PopulationCountStyle}" />
  110. </StatusBarItem>
  111. <StatusBarItem>
  112. <TextBlock Width="Auto" Text="Peak:" />
  113. </StatusBarItem>
  114. <StatusBarItem>
  115. <TextBlock Name="PeakPopulationCount" Width="30" Style="{StaticResource PeakPopulationCountStyle}" />
  116. </StatusBarItem>
  117. <StatusBarItem>
  118. <TextBlock Width="Auto" Text="Born:" />
  119. </StatusBarItem>
  120. <StatusBarItem>
  121. <TextBlock Name="CellBirthCount" Width="40" Style="{StaticResource BirthCountStyle}" />
  122. </StatusBarItem>
  123. <StatusBarItem>
  124. <TextBlock Width="Auto" Text="Died:" />
  125. </StatusBarItem>
  126. <StatusBarItem>
  127. <TextBlock Name="CellDeathCount" Width="40" Style="{StaticResource DeathCountStyle}" />
  128. </StatusBarItem>
  129. <StatusBarItem>
  130. <TextBlock Width="Auto" Padding="10,0,0,0">Speed:</TextBlock>
  131. </StatusBarItem>
  132. <StatusBarItem>
  133. <Slider Name="RunSpeedSlider" Style="{StaticResource RunSpeedSliderStyle}" />
  134. </StatusBarItem>
  135. </StatusBar>
  136. <Canvas Name="BottomWrapIndicator" Height="5" Width="Auto" DockPanel.Dock="Bottom" HorizontalAlignment="Stretch" Background="Gray" />
  137. <Canvas Name="LeftWrapIndicator" Height="Auto" Width="4" DockPanel.Dock="Left" VerticalAlignment="Stretch" Background="Gray" />
  138. <Canvas Name="RightWrapIndicator" Height="Auto" Width="4" DockPanel.Dock="Right" VerticalAlignment="Stretch" Background="Gray" />
  139. <Grid Name="LifeGrid" Background="White" ForceCursor="True" AllowDrop="True" >
  140. <Grid.Resources>
  141. <Style TargetType="{x:Type Rectangle}">
  142. <Setter Property="Opacity" Value="{Binding Path=IsAlive}" />
  143. </Style>
  144. <Style BasedOn="{StaticResource {x:Type Rectangle}}" TargetType="{x:Type Rectangle}" x:Key="RectStyle" >
  145. <Setter Property="Fill" Value="Red" />
  146. </Style>
  147. </Grid.Resources>
  148. </Grid>
  149. </DockPanel>
  150. </Grid>
  151. </Window>

No responses yet

Apr 26 2008

AvalonLife Listing 2 – LifeSim.cs

Published by Mark under Code

  1. using System;
  2. using System.IO;
  3. using System.ComponentModel;
  4. using System.Windows;
  5. using System.Collections.Generic;
  6. using System.Text;
  7. using System.Runtime.Serialization;
  8.  
  9. namespace AvalonLife
  10. {
  11. [Serializable]
  12. class LifeSim : INotifyPropertyChanged, ISerializable
  13. {
  14. #region LifeSim public interface
  15. /// <summary>
  16. /// LifeSim()
  17. ///
  18. /// Constructs an instance of the sim controller and creates an empty model
  19. /// for it to run. LifeSim is in the paused state after construction, but the
  20. /// timer is running.
  21. /// </summary>
  22. /// <param name="lm"></param>
  23. public LifeSim()
  24. {
  25. _lm = new LifeModel();
  26. _timerInterval = ALSettings.Default.TimerInterval;
  27. _timer = new System.Windows.Forms.Timer();
  28. _timer.Interval = _timerInterval;
  29. _timer.Tick += new EventHandler(TimerEvent);
  30. _timer.Tag = this;
  31. _timer.Start();
  32. }
  33.  
  34. /// <summary>
  35. /// LifeSim(int, int)
  36. ///
  37. /// Constructs an instance of the sim controller and creates an empty model
  38. /// for it to run, using the specified dimensions. LifeSim is in the paused
  39. /// state after construction, but the timer is running.
  40. /// </summary>
  41. /// <param name="rows">Height of the requested model grid</param>
  42. /// <param name="columns">Width of the requested model grid</param>
  43. public LifeSim(int rows, int columns)
  44. {
  45. _lm = new LifeModel(rows, columns);
  46. _timerInterval = ALSettings.Default.TimerInterval;
  47. _timer = new System.Windows.Forms.Timer();
  48. _timer.Interval = _timerInterval;
  49. _timer.Tick += new EventHandler(TimerEvent);
  50. _timer.Tag = this;
  51. _timer.Start();
  52. }
  53.  
  54. #region ISerializable methods
  55.  
  56. /// <summary>
  57. /// LifeSim(SerializationInfo, StreamingContext)
  58. ///
  59. /// Called by the BinaryFormatter to construct an instance of LifeSim from a
  60. /// stream. Deserializes the private members, then the LifeModel deserialization
  61. /// is called, and finally the timer is created and started. The sim is constructed
  62. /// in a paused state. The UI is responsible for wiring up events.
  63. /// </summary>
  64. /// <param name="info"></param>
  65. /// <param name="ctxt"></param>
  66. public LifeSim( SerializationInfo info, StreamingContext ctxt )
  67. {
  68. _generation = (int)info.GetValue( "_generation", typeof(int) );
  69. _timerInterval = (int)info.GetValue( "_timerInterval", typeof(int) );
  70.  
  71. _lm = new LifeModel( info, ctxt );
  72.  
  73. _isPaused = true;
  74.  
  75. _timer = new System.Windows.Forms.Timer();
  76. _timer.Interval = _timerInterval;
  77. _timer.Tick += new EventHandler(TimerEvent);
  78. _timer.Tag = this;
  79. _timer.Start();
  80. }
  81.  
  82. /// <summary>
  83. /// GetObjectData(SerializationInfo, StreamingContext)
  84. ///
  85. /// Called by the BinaryFormatter to serialize an instance of LifeSim. Serializes
  86. /// the private members and then calls the LifeModel serialization method directly.
  87. /// </summary>
  88. /// <param name="info"></param>
  89. /// <param name="ctxt"></param>
  90. public void GetObjectData( SerializationInfo info, StreamingContext ctxt )
  91. {
  92. info.AddValue( "_generation", _generation );
  93. info.AddValue( "_timerInterval", _timerInterval );
  94.  
  95. _lm.GetObjectData( info, ctxt );
  96. }
  97.  
  98. #endregion
  99.  
  100. /// <summary>
  101. /// ~LifeSim()
  102. ///
  103. /// I haven't verified it, but this is almost certainly unnecessary. The timer
  104. /// class dispose() should kill the timer and clean up. Anyway no harm done.
  105. /// </summary>
  106. ~LifeSim()
  107. {
  108. if ( _timer != null )
  109. _timer.Stop();
  110. }
  111.  
  112. /// <summary>
  113. /// TimerEvent(Object, EventArgs)
  114. ///
  115. /// This function handles the timer tick. If the game is in the unpaused state it calls
  116. /// the LifeModel.Evaluate() method to iterate the model. It then increments the
  117. /// generation count. It's a static method so we pass in the 'this' pointer for the
  118. /// LifeSim instance that owns the timer in its Tag property. Useful little things, Tags.
  119. /// </summary>
  120. /// <param name="sender"></param>
  121. /// <param name="e"></param>
  122. private static void TimerEvent( Object sender, EventArgs e )
  123. {
  124. LifeSim ls = ((System.Windows.Forms.Timer)sender).Tag as LifeSim;
  125. if ( ls != null && !ls.IsPaused )
  126. {
  127. if ( !ls._lm.EvolutionHalted || ALSettings.Default.HaltOnStability == false )
  128. {
  129. ls._lm.Evaluate();
  130. ls.Generation++;
  131. }
  132. else
  133. {
  134. ls.IsPaused = true;
  135. if (ls._uiCallback != null)
  136. {
  137. ls._uiCallback();
  138. }
  139. }
  140. }
  141. else if ( ls == null )
  142. throw( new System.InvalidOperationException("TimerEvent") );
  143. }
  144.  
  145. /// <summary>
  146. /// ResetSim()
  147. ///
  148. /// Called from the UI to reset the simulation to its starting condition. On
  149. /// exit the simulation is paused.
  150. /// </summary>
  151. public void ResetSim()
  152. {
  153. if ( !_isPaused )
  154. IsPaused = true;
  155.  
  156. Generation = 0;
  157.  
  158. _lm.ResetModel();
  159. }
  160.  
  161. /// <summary>
  162. /// NewModel()
  163. ///
  164. /// Called from the UI during handling of a click on Game | New.
  165. /// </summary>
  166. public void NewModel()
  167. {
  168. if ( !_isPaused )
  169. _isPaused = true;
  170.  
  171. _lm = new LifeModel();
  172. Generation = 0;
  173. }
  174.  
  175. /// <summary>
  176. /// NewModel(int, int)
  177. ///
  178. /// Called from the UI during handling of a click on Game | New. Creates a
  179. /// new model using the passed in dimensions.
  180. /// </summary>
  181. public void NewModel(int rows, int columns)
  182. {
  183. if (!_isPaused)
  184. _isPaused = true;
  185.  
  186. _lm = new LifeModel(rows, columns);
  187. Generation = 0;
  188. }
  189.  
  190. /// <summary>
  191. /// NewModel(Stream)
  192. ///
  193. /// Called to decode a stream of .cells format data into a LifeModel. The bulk
  194. /// of the work is done in the LifeModel.LifeModel(Stream) constructor. If
  195. /// construction fails we will already have warned the user in the constructor,
  196. /// so all we do here is restore the paused state we had on entry.
  197. /// </summary>
  198. /// <param name="str">Stream containing the .cells formated data</param>
  199. /// <returns></returns>
  200. public bool NewModel( Stream str )
  201. {
  202. bool result = false;
  203. bool paused = _isPaused;
  204.  
  205. _isPaused = true;
  206.  
  207. try
  208. {
  209. LifeModel lm = new LifeModel( str );
  210. _lm = lm;
  211. Generation = 0;
  212. result = true;
  213. }
  214. catch( System.Exception )
  215. {
  216. _isPaused = paused;
  217. }
  218. return result;
  219. }
  220.  
  221. #endregion
  222.  
  223. #region INotifyPropertyChanged members
  224.  
  225. public event PropertyChangedEventHandler PropertyChanged;
  226.  
  227. #endregion
  228.  
  229. #region LifeSim public properties
  230. /// <summary>
  231. /// Counts the generations that the current model has run
  232. /// </summary>
  233. private int _generation = 0;
  234. public int Generation
  235. {
  236. get
  237. {
  238. return _generation;
  239. }
  240. protected set
  241. {
  242. _generation = value;
  243. if (PropertyChanged != null)
  244. PropertyChanged(this, new PropertyChangedEventArgs("Generation"));
  245. }
  246. }
  247.  
  248. /// <summary>
  249. /// Holds a reference to the simulation model
  250. /// </summary>
  251. private LifeModel _lm = null;
  252. public LifeModel Model
  253. {
  254. get
  255. {
  256. return _lm;
  257. }
  258. }
  259.  
  260. /// <summary>
  261. /// Holds the timer interval, set to default on start
  262. /// </summary>
  263. private int _timerInterval = 0;
  264. public int TimerInterval
  265. {
  266. get
  267. {
  268. return _timerInterval;
  269. }
  270. set
  271. {
  272. _timerInterval = value;
  273. _timer.Interval = _timerInterval;
  274. if (PropertyChanged != null)
  275. PropertyChanged(this, new PropertyChangedEventArgs("TimerInterval"));
  276. }
  277. }
  278.  
  279. /// <summary>
  280. /// Holds the simulation run state: false if running, true if paused.
  281. /// </summary>
  282. private bool _isPaused = true;
  283. public bool IsPaused
  284. {
  285. get
  286. {
  287. return _isPaused;
  288. }
  289. set
  290. {
  291. _isPaused = value;
  292. }
  293. }
  294.  
  295. /// <summary>
  296. /// The timer handler may detect that the model has ceased evolving,
  297. /// i.e. entered a stable state. In that case it will make a call to
  298. /// the function in this delegate to inform the UI. The UI is responsible
  299. /// for setting a function-typed value to this delegate member if it
  300. /// wants to receive this callback.
  301. /// </summary>
  302. public delegate void UISimStatusCallback();
  303.  
  304. private UISimStatusCallback _uiCallback = null;
  305. public UISimStatusCallback UICallback
  306. {
  307. get
  308. {
  309. return _uiCallback;
  310. }
  311. set
  312. {
  313. _uiCallback = value;
  314. }
  315. }
  316.  
  317. #endregion
  318.  
  319. #region LifeSim private data
  320.  
  321. private System.Windows.Forms.Timer _timer = null;
  322.  
  323. #endregion
  324. }
  325. }

No responses yet

Apr 25 2008

AvalonLife Listing 1 – LifeModel.cs

Published by Mark under Code


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