2 * Copyright (c) Lynn J. Gasch
\r
4 * Redmond, Washington
\r
6 * typhoonUI displays the contents of an xml file in a tree control.
\r
7 * It was developed to support chess programming, but can really be
\r
8 * used for any xml file you wish.
\r
10 * Xml nodes that do not have children should be written within
\r
11 * a single tag, as < tag anAttrib="value" /> so that they can
\r
12 * be displayed on a single line (node) of the tree.
\r
14 * Tags starting with "FEN" can be used to lauch chess board
\r
15 * viewers with the board value (they must use the single-line
\r
16 * formatting as described above to trigger this functionality).
\r
18 * This app is pretty special purpose and I'm sure there are plenty
\r
19 * of ways to break it with various inputs. I coded it up in a few
\r
20 * hours and it meets our needs.
\r
24 using System.Drawing;
\r
25 using System.Collections;
\r
26 using System.ComponentModel;
\r
27 using System.Windows.Forms;
\r
31 using System.Diagnostics;
\r
36 // TODO: write tag for eval; Write evals dumps to file
\r
37 // and retrieve the text into a separate text block
\r
38 // on selection of the node
\r
39 // TODO: command line file launch
\r
40 // TODO: speed up loading
\r
43 /// Summary description for Form1.
\r
45 public class Form1 : System.Windows.Forms.Form
\r
47 #region Private Fields
\r
50 /// inserted between tag names and their values for display
\r
51 /// in the tree. For tags that have no children other than their
\r
52 /// values, list them in the xml as
\r
53 /// < tag attrib="something to display" /> and they will only
\r
54 /// take up one tree node and be listed as
\r
55 /// tag something to display.
\r
57 private const string VALUE_SEPARATOR = " ";
\r
60 /// Use this tag name to identify the board configuration node
\r
61 /// in your xml. This will provide menu item and double-clicking
\r
62 /// to launch the position in a viewer that you can specify.
\r
64 private const string TAG_BOARD = "FEN";
\r
67 /// name of a tag for which the first attrib should be added to its parent
\r
68 /// node text. If it's the only child, the node itself will be skipped
\r
70 private const string TAG_SCORE = "score";
\r
73 /// Attributes named tip will be set as tool tip text for the node
\r
75 private const string ATTRIB_TIP = "tip";
\r
78 /// an attrib name to denote request for boldfacing the text.
\r
79 /// In order for the text to be displayed, it must be the frst attrib.
\r
81 private const string ATTRIB_BOLD = "bold";
\r
84 /// Name of a (reusable) file that is written with the board
\r
85 /// position and used as a command arg to the board viewer app.
\r
87 private const string POSITION_FILE_NAME = "boardConfigFile.txt";
\r
90 /// Windows designer generated code
\r
92 private System.Windows.Forms.MainMenu mainMenu1;
\r
93 private System.Windows.Forms.MenuItem menuItem1;
\r
94 private System.Windows.Forms.MenuItem menuItem2;
\r
95 private System.Windows.Forms.TreeView _treeView1;
\r
98 /// appears when right-clicking on a "board" node. See
\r
101 private System.Windows.Forms.ContextMenu _contextMenu;
\r
104 /// Path to the viewer app, to be collected from the user.
\r
105 /// TODO: allow the user to enter it into the Path environment
\r
106 /// variable and just attempt to run it; only request path
\r
107 /// if fail to run.
\r
109 private string _boardViewerAppPath = null;
\r
112 /// the view app process, so we can kill it if another board
\r
113 /// is to be viewed
\r
115 private Process _process = null;
\r
118 /// local dir for writing the board position file for input
\r
119 /// into the viewer app.
\r
121 private string _applicationDir;
\r
124 /// Required designer variable.
\r
126 private System.ComponentModel.Container components = null;
\r
129 /// for displaying long or multi-line text
\r
131 private ToolTip _tip;
\r
133 private string _tipText;
\r
137 #region Construction / Destruction
\r
142 public Form1(string startFile)
\r
145 // Required for Windows Form Designer support
\r
147 InitializeComponent();
\r
149 // create the context menu that will be used for board nodes
\r
150 _contextMenu = new ContextMenu();
\r
151 MenuItem item = new MenuItem("View board");
\r
152 item.Click += new EventHandler(OnViewBoardMenuClicked);
\r
153 _contextMenu.MenuItems.Add(item);
\r
155 _applicationDir = Path.GetDirectoryName(
\r
156 Application.ExecutablePath);
\r
158 _tip = new ToolTip();
\r
159 _tipText = string.Empty;
\r
160 _tip.SetToolTip(_treeView1, _tipText);
\r
162 if ((startFile != null) && (startFile.Length > 0))
\r
163 Display(startFile);
\r
167 /// Clean up any resources being used.
\r
169 protected override void Dispose( bool disposing )
\r
174 if (components != null)
\r
176 components.Dispose();
\r
179 base.Dispose( disposing );
\r
185 #region Windows Form Designer generated code
\r
187 /// Required method for Designer support - do not modify
\r
188 /// the contents of this method with the code editor.
\r
190 private void InitializeComponent()
\r
192 this.mainMenu1 = new System.Windows.Forms.MainMenu();
\r
193 this.menuItem1 = new System.Windows.Forms.MenuItem();
\r
194 this.menuItem2 = new System.Windows.Forms.MenuItem();
\r
195 this._treeView1 = new System.Windows.Forms.TreeView();
\r
196 this.SuspendLayout();
\r
200 this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
\r
205 this.menuItem1.Index = 0;
\r
206 this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
\r
208 this.menuItem1.Text = "File";
\r
212 this.menuItem2.Index = 0;
\r
213 this.menuItem2.Text = "Open";
\r
214 this.menuItem2.Click += new System.EventHandler(this.OnOpenMenuClick);
\r
218 this._treeView1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
\r
219 | System.Windows.Forms.AnchorStyles.Left)
\r
220 | System.Windows.Forms.AnchorStyles.Right);
\r
221 this._treeView1.HideSelection = false;
\r
222 this._treeView1.ImageIndex = -1;
\r
223 this._treeView1.Name = "_treeView1";
\r
224 this._treeView1.SelectedImageIndex = -1;
\r
225 this._treeView1.Size = new System.Drawing.Size(680, 600);
\r
226 this._treeView1.TabIndex = 0;
\r
227 this._treeView1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.OnTreeViewMouseDown);
\r
228 this._treeView1.DoubleClick += new System.EventHandler(this.OnTreeViewDoubleClick);
\r
229 this._treeView1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.OnTreeViewMouseMove);
\r
233 this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
\r
234 this.ClientSize = new System.Drawing.Size(680, 598);
\r
235 this.Controls.AddRange(new System.Windows.Forms.Control[] {
\r
237 this.Menu = this.mainMenu1;
\r
238 this.Name = "Form1";
\r
239 this.Text = "Scott\'s Move Tree";
\r
240 this.ResumeLayout(false);
\r
246 /// The main entry point for the application.
\r
249 static void Main(string[] args)
\r
251 string filename = string.Empty;
\r
252 if (args.Length > 0)
\r
253 filename = args[0];
\r
255 Application.Run(new Form1(filename));
\r
259 /// Use Xml support to load and parse the indicated xml doc.
\r
260 /// Populate tree control with it.
\r
262 /// <param name="filename">path to the xml file to load</param>
\r
263 private void Display(string filename)
\r
265 _treeView1.BeginUpdate();
\r
268 XmlDocument doc = new XmlDocument();
\r
269 doc.Load(filename);
\r
270 _treeView1.Nodes.Clear();
\r
271 TreeNode root = new TreeNode(doc.DocumentElement.Name);
\r
272 _treeView1.Nodes.Add(root);
\r
273 // recursively add all
\r
274 StringBuilder builder = new StringBuilder();
\r
275 foreach(XmlNode node in doc.DocumentElement.ChildNodes)
\r
277 AddNode(node, ref root, ref builder);
\r
282 MessageBox.Show(this, e.Message + "\r\n" +
\r
283 e.StackTrace, "Error",
\r
284 MessageBoxButtons.OK, MessageBoxIcon.Error);
\r
286 _treeView1.EndUpdate();
\r
290 /// Recursively add nodes to the tree.
\r
292 /// <param name="element">the current info to add; corresponds
\r
293 /// to the parent node so that we are adding its children.</param>
\r
294 /// <param name="parent">parent node in the tree</param>
\r
295 /// <param name="builder">on;y create one instance of string builder
\r
296 /// and use it for text manipulation</param>
\r
297 /// <remarks>Nodes that don't have any nested nodes (other than their
\r
298 /// own "value") should be written in the form
\r
299 /// < tagName attrib="Attrib value" />
\r
300 /// In this case, the tag name and the first attrib value are
\r
301 /// shown in one line (in one node) in the tree. NOTE: any
\r
302 /// additional tags are ignored. (the attrib value is not required).
\r
304 private void AddNode(XmlNode element, ref TreeNode parent,
\r
305 ref StringBuilder builder)
\r
307 builder.Length = 0;
\r
308 string text = element.Name;
\r
311 // - score: append parent text with score instead of adding
\r
313 // - any with attribs - append first attrib val to node text
\r
314 // - bold - (must be first attrib) - boldface the node
\r
315 // - tip - (any attrib) - set as the tag of the node for tooltip
\r
317 if (text.Equals(TAG_SCORE))
\r
319 builder.Append(parent.Text);
\r
320 builder.Append(VALUE_SEPARATOR);
\r
321 builder.Append(TAG_SCORE);
\r
322 builder.Append(" ");
\r
323 builder.Append(element.Attributes[0].Value);
\r
324 parent.Text = builder.ToString();
\r
329 builder.Append(text);
\r
330 XmlAttributeCollection coll = element.Attributes;
\r
331 int numAttribs = coll.Count;
\r
332 XmlAttribute attrib = null;
\r
333 if (numAttribs > 0)
\r
336 // append the tag name with the first attrib value
\r
337 builder.Append(VALUE_SEPARATOR);
\r
338 builder.Append(attrib.Value);
\r
340 TreeNode node = new TreeNode(builder.ToString());
\r
342 if ((attrib != null) && attrib.Name.Equals(ATTRIB_BOLD))
\r
343 node.ForeColor = Color.Red;
\r
344 if (numAttribs > 1)
\r
347 if (attrib.Name.Equals(ATTRIB_TIP))
\r
349 node.Tag = attrib.Value;
\r
353 parent.Nodes.Add(node);
\r
355 foreach (XmlElement child in element.ChildNodes)
\r
357 AddNode(child, ref node, ref builder);
\r
363 /// Gets and saves the path to the viewer app from the user.
\r
365 private DialogResult SetViewerAppPath()
\r
367 DialogResult result = DialogResult.OK;
\r
368 if (_boardViewerAppPath == null)
\r
370 result = MessageBox.Show(this,
\r
371 "Please set path to Winboard or XBoard viewer app",
\r
372 "Input requested", MessageBoxButtons.OKCancel,
\r
373 MessageBoxIcon.Question);
\r
374 if ( result == DialogResult.OK)
\r
376 OpenFileDialog browse = new OpenFileDialog();
\r
377 browse.Filter = "Executable files (*.exe)|*.exe";
\r
378 browse.Multiselect = false;
\r
379 result = browse.ShowDialog(this);
\r
380 if (result == DialogResult.OK)
\r
382 _boardViewerAppPath = browse.FileName;
\r
390 /// Closes the viewer app
\r
392 private void KillProcess()
\r
394 if (_process != null)
\r
396 if (!_process.HasExited)
\r
398 if (!_process.CloseMainWindow())
\r
406 /// Writes board position to file and calls the viewer app with it.
\r
408 /// <param name="text">text of the board node, starting with the
\r
409 /// "board" tag and separator</param>
\r
410 private void ViewBoard(string text)
\r
412 string boardCode = text.Substring(TAG_BOARD.Length
\r
413 + VALUE_SEPARATOR.Length);
\r
414 StreamWriter f = new StreamWriter(new FileStream(
\r
415 Path.Combine(_applicationDir, POSITION_FILE_NAME),
\r
416 FileMode.Create, FileAccess.Write));
\r
417 f.WriteLine(boardCode);
\r
420 if (SetViewerAppPath() == DialogResult.OK)
\r
426 ProcessStartInfo pInfo =
\r
427 new ProcessStartInfo(_boardViewerAppPath);
\r
429 "/mode editposition /ncp /top /coords /size small /lpf " +
\r
430 Path.Combine(_applicationDir, POSITION_FILE_NAME);
\r
432 _process = new Process();
\r
433 _process.StartInfo = pInfo;
\r
438 MessageBox.Show(this, "Unable to run viewer app", "Error",
\r
439 MessageBoxButtons.OK, MessageBoxIcon.Error);
\r
440 _boardViewerAppPath = null;
\r
447 #region Event Handlers
\r
450 /// Handler for File | Open menu. Get the xml file to load.
\r
452 /// <param name="sender">ignored</param>
\r
453 /// <param name="e">ignored</param>
\r
454 private void OnOpenMenuClick(object sender, System.EventArgs e)
\r
456 OpenFileDialog dialog = new OpenFileDialog();
\r
457 dialog.Multiselect = false;
\r
458 dialog.Filter = "XML files (*.xml)|*.xml|All files (*.*)|*.*";
\r
459 if (dialog.ShowDialog(this) == DialogResult.OK)
\r
461 Display(dialog.FileName);
\r
466 /// Handles mouse down. if right-click on "board" node,
\r
467 /// displays board context menu.
\r
469 /// <param name="sender">ignored</param>
\r
470 /// <param name="e">select the tree node at the click
\r
472 private void OnTreeViewMouseDown(object sender, MouseEventArgs e)
\r
474 // this doesn't automatically happen
\r
475 _treeView1.SelectedNode = _treeView1.GetNodeAt(e.X, e.Y);
\r
476 if (e.Button.Equals(MouseButtons.Right))
\r
478 string text = _treeView1.SelectedNode.Text;
\r
479 if (text.StartsWith(TAG_BOARD))
\r
481 _treeView1.ContextMenu = _contextMenu;
\r
487 /// when view board menu item chosen, writes the selected board
\r
488 /// position to a file and calls the viewer app.
\r
490 /// <param name="sender">ignored</param>
\r
491 /// <param name="e">ignored</param>
\r
492 private void OnViewBoardMenuClicked(object sender, EventArgs e)
\r
494 string text = _treeView1.SelectedNode.Text;
\r
495 if (text.StartsWith(TAG_BOARD))
\r
499 _treeView1.ContextMenu = null;
\r
503 /// If double-click a "board" node, calls the viewer
\r
504 /// for the position
\r
506 /// <param name="sender"></param>
\r
507 /// <param name="e"></param>
\r
508 private void OnTreeViewDoubleClick(object sender, System.EventArgs e)
\r
510 string text = _treeView1.SelectedNode.Text;
\r
511 if (text.StartsWith(TAG_BOARD))
\r
518 /// set tooltip if over a node with a displayable tag
\r
520 /// <param name="sender">ignored</param>
\r
521 /// <param name="e">holds mouse position</param>
\r
522 private void OnTreeViewMouseMove(object sender, MouseEventArgs e)
\r
524 TreeNode node = _treeView1.GetNodeAt(e.X, e.Y);
\r
525 string tag = string.Empty;
\r
526 if ((node != null) && (node.Tag != null))
\r
528 tag = (string)node.Tag;
\r
530 if (!tag.Equals(_tipText))
\r
533 _tip.SetToolTip(_treeView1, _tipText);
\r