/*======================================================================== Copyright (C) 2006 by Geir-Tore Lindsve, Torbjørn Meistad and Yngve Raudberget, hereby refered to as "the authors". All rights reserved Permission is hereby granted, without written agreement and without license or royalty fees, to use, reproduce, prepare derivative works, distribute, and display this software and its documentation for NONCOMMERCIAL RESEARCH AND EDUCATIONAL PURPOSES, provided that (1) the above copyright notice and the following two paragraphs appear in all copies of the source code and (2) redistributions, including without limitation binaries, reproduce these notices in the supporting documentation. IN NO EVENT SHALL THE AUTHORS, OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ========================================================================*/ using System; using System.Collections.Generic; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Text.RegularExpressions; using System.Runtime.InteropServices; using CLab.Exceptions; /* * Syntax highlighting derived and improved from example at * http://www.c-sharpcorner.com/Code/2003/June/SyntaxHighlightInRichTextBoxP2.asp */ namespace ClabGui { /// /// Main GUI class. /// public partial class MainGui : Form { private Hashtable parameterVars; private Program program; private Boolean textBoxChanged = false; private Boolean problemOpened = false; private Boolean problemRunning = false; private int textLength = 0; private Boolean loadFileParsing = false; private Regex startLine = new Regex("[\n]+(?[ ]*)"); private Boolean doAutoComplete = true; private int maxStatusUpdates; delegate void IntParameterDelegate(int value); delegate void StringParameterDelegate(String value); delegate void ExceptionParameterDelegate(Exception e); delegate void AddVarDelegate(String varName, List domainV, int id); delegate void UpdateDomainDelegate(int id, List domainV); /// /// Initializes a new instance of the class. /// /// The . public MainGui(Program program) { parameterVars = new Hashtable(); InitializeComponent(); this.program = program; codeTextBox.TextChanged += new EventHandler(this.TextChangedEvent); fileMenuCloseProblem.Enabled = false; runMenuStartSearch.Enabled = false; runMenuStopSearch.Enabled = false; runMenuRestart.Enabled = false; runMenuPauseSearch.Enabled = false; fileMenuCloseProblem.Enabled = false; } private void fileMenuOpen_Click(object sender, EventArgs e) { Boolean ok = true; if (textBoxChanged) { DialogResult res = MessageBox.Show("Do you want to save your changes before opening a new file?", "Unsaved changes", MessageBoxButtons.YesNoCancel); if (res.Equals(DialogResult.Yes)) { fileMenuSave_Click(sender, e); if (program.ProblemFilename != "") ok = false; } else if (res.Equals(DialogResult.Cancel)) ok = false; } if (ok) { textBoxChanged = false; OpenFileDialog ofd = new OpenFileDialog(); ofd.Multiselect = false; ofd.Filter = "xml files (*.xml)|*.xml|cp files (*.cp)|*.cp"; DialogResult res = ofd.ShowDialog(); if (res == DialogResult.OK) { fileMenuCloseProblem_Click(sender, e); String fileName = ofd.FileName; if (fileName != null && !fileName.Equals("")) { OpenFile(fileName); } } } } /// /// Opens the file and loads the text into the gui text window. /// /// Name of the file. public void OpenFile(String fileName) { String text = program.OpenFile(fileName); loadFileParsing = true; FlickerFreeRichEditTextBox._Paint = false; codeTextBox.Text = text; textBoxChanged = false; Parse(); loadFileParsing = false; FlickerFreeRichEditTextBox._Paint = true; fileMenuCloseProblem.Enabled = true; problemOpened = true; EnableStart(); SetStatusMessage(program.StripPathFromFilename(program.ProblemFilename) + " opened"); } private void fileMenuSave_Click(object sender, EventArgs e) { if (program.ProblemFilename != "") { program.SaveFile(codeTextBox.Text); textBoxChanged = false; statusBarText.Text = "Changes saved"; } else fileMenuSaveAs_Click(sender, e); } private void fileMenuSaveAs_Click(object sender, EventArgs e) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "xml file (*.xml)|*.xml|cp file (*.cp)|*.cp"; sfd.ShowDialog(); if (sfd.FileName != null && !sfd.FileName.Equals("")) { program.SaveFileAs(sfd.FileName, codeTextBox.Text); textBoxChanged = false; statusBarText.Text = ("Problem saved"); } } private void tabControl_Click(object sender, EventArgs e) { if (tabControl.SelectedIndex == 1) { positionNumberTF.Visible = false; statusBarProgress.Visible = true; if (!problemRunning && problemOpened) { runMenuStartSearch_Click(sender, e); } } else { statusBarProgress.Visible = false; positionNumberTF.Visible = true; } } private void fileMenuExit_Click(object sender, EventArgs e) { Boolean ok = true; if (textBoxChanged) { DialogResult res = MessageBox.Show("Do you want to save your changes before quitting?", "Unsaved changes", MessageBoxButtons.YesNoCancel); if (res.Equals(DialogResult.Yes)) fileMenuSave_Click(sender, e); else if (res.Equals(DialogResult.Cancel)) ok = false; } if (ok) { program.StopClabThreadIfRunning(); SetStatusMessage("Stopping CLab"); Dispose(); Application.Exit(); } } private void codeTextBox_TextChanged(object sender, EventArgs e) { if (!loadFileParsing) { textBoxChanged = true; } } private void fileMenuCloseProblem_Click(object sender, EventArgs e) { Boolean ok = true; if (textBoxChanged) { DialogResult res = MessageBox.Show("Do you want to save your changes before closing?", "Unsaved changes", MessageBoxButtons.YesNoCancel); if (res.Equals(DialogResult.Yes)) fileMenuSave_Click(sender, e); else if (res.Equals(DialogResult.Cancel)) ok = false; } if (ok) { StopProblem(); codeTextBox.Text = ""; program.ProblemFilename = ""; textBoxChanged = false; problemOpened = false; parameterVars.Clear(); runMenuStartSearch.Enabled = false; tsStart.Enabled = false; SetStatusMessage(program.StripPathFromFilename(program.ProblemFilename) + " closed"); } } private void editMenuCut_Click(object sender, EventArgs e) { Clipboard.SetDataObject(codeTextBox.SelectedText); codeTextBox.SelectedText = ""; } private void editMenuCopy_Click(object sender, EventArgs e) { codeTextBox.Copy(); } private void editMenuPaste_Click(object sender, EventArgs e) { codeTextBox.Paste(); } private void statusTekstBox_MouseHover(object sender, EventArgs e) { statusBoxMouseOver.Show(statusBarText.Text, this, this.Width, this.Height); } private void statusTekstBox_MouseLeave(object sender, EventArgs e) { statusBoxMouseOver.Hide(this); } private void runMenuStartSearch_Click(object sender, EventArgs e) { try { Boolean ok = program.StartClab(); if (ok) { program.StartClabInitialSearchThread(); SetStatusMessage(program.StripPathFromFilename(program.ProblemFilename) + " started"); problemRunning = true; runMenuStartSearch.Enabled = false; runMenuPauseSearch.Enabled = true; runMenuStopSearch.Enabled = true; runMenuRestart.Enabled = true; tsStart.Enabled = false; tsPause.Enabled = true; tsStop.Enabled = true; tsRestart.Enabled = true; } } catch (ClabException ex) { SetStatusMessage(program.StripPathFromFilename(program.ProblemFilename) + " failed"); ShowExceptionInGui(ex); } } private void runMenuStopSearch_Click(object sender, EventArgs e) { EnableStart(); StopProblem(); SetStatusMessage(program.StripPathFromFilename(program.ProblemFilename) + " stopped"); } private void runMenuRestart_Click(object sender, EventArgs e) { runMenuStopSearch_Click(sender, e); runMenuStartSearch_Click(sender, e); SetStatusMessage(program.StripPathFromFilename(program.ProblemFilename) + " restarted"); } private void runMenuPauseSearch_Click(object sender, EventArgs e) { Boolean paused = program.PauseClabThreadIfRunning(); if (paused) SetStatusMessage("CLab paused, press pause button again to resume"); else SetStatusMessage("CLab resumed"); } private void helpMenuAbout_Click(object sender, EventArgs e) { MessageBox.Show("Made by:\n\nGeir-Tore Lindsve\nTorbjørn Meistad\nYngve Raudberget\n\nStudents at IT-University of Copenhagen, 2006"); } /// /// Enables the play button and start button. /// public void EnableStart() { runMenuStartSearch.Enabled = true; tsStart.Enabled = true; } /// /// Stops the problem and resets the GUI. /// public void StopProblem() { program.StopClabThreadIfRunning(); statusBarProgress.Value = 0; runMenuRestart.Enabled = false; runMenuStopSearch.Enabled = false; runMenuPauseSearch.Enabled = false; tsRestart.Enabled = false; tsStop.Enabled = false; tsPause.Enabled = false; program.StopClab(); problemRunning = false; foreach (DictionaryEntry de in parameterVars) { VarParaGui vpGui = (VarParaGui)de.Value; progressPanel.Controls.Remove(vpGui); } statusBarProgress.Value = 0; } /// /// Sets the status message. /// /// The status message. public void SetStatusMessage(String statusMessage) { if (InvokeRequired) { BeginInvoke(new StringParameterDelegate(SetStatusMessage), new Object[] { statusMessage }); return; } statusBarText.Text = statusMessage; } /// /// Shows the message box with the passed in message. /// /// The message. public void ShowMessageBox(String message) { if (InvokeRequired) { BeginInvoke(new StringParameterDelegate(ShowMessageBox), new Object[] { message }); return; } MessageBox.Show(message); } /// /// Adds the variable and domain to the GUI. /// /// Name of the variable. /// The domain values. /// The id of this variable. public void AddVar(String varName, List domainV, int id) { if (InvokeRequired) //Called by another thread { BeginInvoke(new AddVarDelegate(AddVar), new Object[] {varName, domainV, id}); return; } //Only run by GUI thread VarParaGui vp = new VarParaGui(this, id, varName, domainV); vp.Dock = DockStyle.Top; progressPanel.Controls.Add(vp); parameterVars["" + id] = vp; } /// /// Updates the domain values for a certain variable. /// /// The id of the variable. /// The valid domain values. public void UpdateDomainV(int id, List domainV) { if (InvokeRequired) { BeginInvoke(new UpdateDomainDelegate(UpdateDomainV), new Object[] { id, domainV }); return; } VarParaGui vp = (VarParaGui)parameterVars["" + id]; vp.UpdateDomainValues(domainV); } /// /// Setup the status bar. /// /// The maximum steps of the progress bar. public void SetupStatusBar(int max) { if (InvokeRequired) { BeginInvoke(new IntParameterDelegate(SetupStatusBar), new Object[] { max }); return; } maxStatusUpdates = max; statusBarProgress.Value = 0; statusBarProgress.Minimum = 0; statusBarProgress.Maximum = max; statusBarProgress.Step = 1; statusBarProgress.Style = ProgressBarStyle.Continuous; } /// /// Steps one step on the progress bar. /// /// The status value public void StatusBarStep(int value) { if (InvokeRequired) { BeginInvoke(new IntParameterDelegate(StatusBarStep), new Object[] { value }); return; } statusBarProgress.PerformStep(); statusBarProgress.Update(); int valuef = value + 1; statusBarText.Text = " Running operation " +valuef +" of " +maxStatusUpdates; statusBarText.Update(); } /// /// Shows an exception in GUI, and gives option to display a complete description if the exception contains more info. /// /// The exception to display information about public void ShowExceptionInGui(Exception e) { if (InvokeRequired) { BeginInvoke(new ExceptionParameterDelegate(ShowExceptionInGui), new Object[] { e }); return; } if (e is TokenParserException && !e.Message.StartsWith("Error")) //if (e.GetType() == typeof(TokenParserException) && !e.Message.StartsWith("Error")) { Match m = Regex.Match(e.Message, "Position: [0-9]+"); if (m.Success) { Match m2 = Regex.Match(m.ToString(), "[0-9]+"); DialogResult res = MessageBox.Show(e.Message + "\n\n\nDo you want to move cursor to this position?", "Error", MessageBoxButtons.YesNo); if (res.Equals(DialogResult.Yes)) { codeTextBox.SelectionStart = int.Parse(m2.ToString()); } } else MessageBox.Show(e.Message, "Error"); } else { if ((e.InnerException != null) || (e.StackTrace != "")) { DialogResult res = MessageBox.Show(e.Message + "\n\nDo you want to see detailed description?", "Error", MessageBoxButtons.YesNo); if (res.Equals(DialogResult.Yes)) { MessageBox.Show(e.ToString()); } } else { MessageBox.Show(e.Message, "Error"); } } } /// /// Runs the value chosen search through . /// /// Id of the chosen variable. /// The domain value. /// public Boolean RunValueChosenSearch(int varID, String domainValue) { if (program.ClabThreadIsAlive()) return true; statusBarProgress.Value = 0; program.ValueChosenSearchThread(varID, domainValue); return false; } void Parse() { textLength = codeTextBox.Text.Length; // Foreach line in input, // identify key words and format them when adding to the rich text box. Regex r = new Regex("\\n"); String[] lines = r.Split(codeTextBox.Text); codeTextBox.Clear(); foreach (string l in lines) { ParseLine(l); } codeTextBox.SelectionStart = 0; //Makes sure the cursor is at at the beginning of the textfield after opening the file textBoxChanged = false; } void ParseLine(string line) { Regex r = new Regex("([ \\t{}();])"); String[] tokens = r.Split(line); foreach (string token in tokens) { // Set the token's default color and font. codeTextBox.SelectionColor = Color.Black; codeTextBox.SelectionFont = new Font("Courier New", 8, FontStyle.Regular); // Check for a comment. if (token == "//" || token.StartsWith("//")) { // Find the start of the comment and then extract the whole comment. int index = line.IndexOf("//"); string comment = line.Substring(index, line.Length - index); codeTextBox.SelectionColor = Color.Green; codeTextBox.SelectionFont = new Font("Courier New", 8, FontStyle.Regular); codeTextBox.SelectedText = comment; break; } // Check whether the token is a keyword. String[] keywords = { "type", "variable", "rule" }; for (int i = 0; i < keywords.Length; i++) { if (keywords[i] == token) { // Apply alternative color and font to highlight keyword. codeTextBox.SelectionColor = Color.Blue; codeTextBox.SelectionFont = new Font("Courier New", 8, FontStyle.Bold); break; } else if (token == "bool") { codeTextBox.SelectionColor = Color.DarkBlue; codeTextBox.SelectionFont = new Font("Courier New", 8, FontStyle.Bold); break; } } codeTextBox.SelectedText = token; } codeTextBox.SelectedText = "\n"; } private void TextChangedEvent(object sender, EventArgs e) { if ((codeTextBox.SelectionStart > 0) & !loadFileParsing) { SetStatusMessage("There are unsaved changes"); try { //Stop the TextBox from redrawing itself while we update the coloring FlickerFreeRichEditTextBox._Paint = false; //Simple autocomplete if (codeTextBox.Text.Length >= textLength) { int currentPos = codeTextBox.SelectionStart; codeTextBox.SelectionStart = currentPos - 1; codeTextBox.SelectionLength = 1; if (doAutoComplete) { // Autocomplete of these symbols can be turned on/off in the app menu if (codeTextBox.SelectedText == "(") codeTextBox.SelectedText = "()"; else if (codeTextBox.SelectedText == "{") codeTextBox.SelectedText = "{};"; else if (codeTextBox.SelectedText == "[") codeTextBox.SelectedText = "[];"; else if (codeTextBox.SelectedText == "\"") codeTextBox.SelectedText = "\"\""; } else if (codeTextBox.SelectedText == "\n") { String prevText = codeTextBox.Text.Substring(0, codeTextBox.SelectionStart); MatchCollection prevLineStarts = startLine.Matches(prevText); if (prevLineStarts.Count > 0) { String emptyChars = prevLineStarts[prevLineStarts.Count - 1].Groups["emptyChars"].Value; codeTextBox.SelectedText += emptyChars; currentPos += emptyChars.Length; } } codeTextBox.SelectionStart = currentPos; codeTextBox.SelectionLength = 0; } textLength = codeTextBox.Text.Length; // Calculate the starting position of the current line. int start = 0, end = 0; for (start = codeTextBox.SelectionStart - 1; start > 0; start--) { if (codeTextBox.Text[start] == '\n') { start++; break; } } // Calculate the end position of the current line. for (end = codeTextBox.SelectionStart; end < codeTextBox.Text.Length; end++) { if (codeTextBox.Text[end] == '\n') break; } // Extract the current line that is being edited. int lineLength = end - start; String line; if ((lineLength >= 0) & (start >= 0)) line = codeTextBox.Text.Substring(start, end - start); else line = ""; // Backup the users current selection point. int selectionStart = codeTextBox.SelectionStart; int selectionLength = codeTextBox.SelectionLength; // Split the line into tokens. Regex r = new Regex("([ \\t{}();])"); string[] tokens = r.Split(line); int index = start; foreach (string token in tokens) { // Set the token's default color and font. codeTextBox.SelectionStart = index; codeTextBox.SelectionLength = token.Length; codeTextBox.SelectionColor = Color.Black; codeTextBox.SelectionFont = new Font("Courier New", 8, FontStyle.Regular); // Check for a comment. if (token == "//" || token.StartsWith("//")) { // Find the start of the comment and then extract the whole comment. int length = line.Length - (index - start); string commentText = codeTextBox.Text.Substring(index, length); codeTextBox.SelectionStart = index; codeTextBox.SelectionLength = length; codeTextBox.SelectionColor = Color.Green; codeTextBox.SelectionFont = new Font("Courier New", 8, FontStyle.Regular); break; } // Check whether the token is a keyword. String[] keywords = { "type", "variable", "rule" }; for (int i = 0; i < keywords.Length; i++) { if (keywords[i] == token) { // Apply alternative color and font to highlight keyword. codeTextBox.SelectionColor = Color.Blue; codeTextBox.SelectionFont = new Font("Courier New", 8, FontStyle.Bold); break; } else if (token == "bool") { codeTextBox.SelectionColor = Color.DarkBlue; codeTextBox.SelectionFont = new Font("Courier New", 8, FontStyle.Bold); break; } } index += token.Length; } // Restore the users current selection point. codeTextBox.SelectionStart = selectionStart; codeTextBox.SelectionLength = selectionLength; } finally { FlickerFreeRichEditTextBox._Paint = true; } } } private void codeTextBox_SelectionChanged(object sender, EventArgs e) { //int selectionStart = codeTextBox.SelectionStart; positionNumberTF.Text = "Position: " + codeTextBox.SelectionStart; } private void CSPVO_Static_Click(object sender, EventArgs e) { program.SetCSPVariableOrdering(CLab.CspVariableOrdering.vo_static); statusBarText.Text = "CSP: Static variable ordering selected"; } private void CSPVO_MinimumWidth_Click(object sender, EventArgs e) { program.SetCSPVariableOrdering(CLab.CspVariableOrdering.vo_minwidth); statusBarText.Text = "CSP: Minimum width variable ordering selected"; } private void SolveWithBDD_Click(object sender, EventArgs e) { program.Modus = CLab.CLabModus.bdd_modus; statusBarText.Text = "BDD Modus chosen"; } private void SolveWithCSP_Click(object sender, EventArgs e) { program.Modus = CLab.CLabModus.csp_modus; statusBarText.Text = "CSP Modus chosen"; } private void BDDCM_Static_Click(object sender, EventArgs e) { program.SetBDDCompileMethod(CLab.BddCompileMethod.cm_static); statusBarText.Text = "BDD: Static compile method selected."; } private void BDDCM_Dynamic_Click(object sender, EventArgs e) { program.SetBDDCompileMethod(CLab.BddCompileMethod.cm_dynamic); statusBarText.Text = "BDD: Dynamic compile method selected."; } private void BDDCM_Ascending_Click(object sender, EventArgs e) { program.SetBDDCompileMethod(CLab.BddCompileMethod.cm_ascending); statusBarText.Text = "BDD: Ascending compile method selected."; } /// /// Gets the BDD initial settings from the GUI settings. /// /// public int[] GetBDDInitialSettings() { int[] settings = new int[3]; try { settings[0] = int.Parse(initialDBCache_tb.Text); settings[1] = int.Parse(initialBDDNodes_tb.Text); settings[2] = int.Parse(bddMaxIncrease_tb.Text); return settings; } catch (Exception e) { ShowMessageBox("Initial DB Cache, Initial BDD Nodes and Max increase values are not integers! " +e.ToString()); return null; } } private void autocompleteToolStripMenuItem_Click(object sender, EventArgs e) { if (autocompleteToolStripMenuItem.Checked) { autocompleteToolStripMenuItem.Checked = false; doAutoComplete = false; } else { autocompleteToolStripMenuItem.Checked = true; doAutoComplete = true; } } } }