/*============================================================================= Project: SharpImage Module: siFormScriptConsole.cs Language: C# Author: Dan Mueller Date: $Date: 2007-07-06 10:57:00 +1000 (Fri, 06 Jul 2007) $ Revision: $Revision: 2 $ Copyright (c) Queensland University of Technology (QUT) 2007. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =============================================================================*/ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Diagnostics; using Crownwood.Magic.Collections; using Crownwood.Magic.Common; using Crownwood.Magic.Controls; using Crownwood.Magic.Docking; using Crownwood.Magic.Forms; using Crownwood.Magic.Win32; using SharpImage.Main; using SharpImage.Properties; namespace SharpImage.Script { public partial class siFormScriptConsole : Form { #region Constants //===================================================================== public const string PROMPT = "> "; //===================================================================== #endregion #region Instance Variables //===================================================================== //===================================================================== #endregion #region Construction and Disposal //===================================================================== /// /// Default constructor /// /// public siFormScriptConsole(IApplication parent) { InitializeComponent(); this.m_ParentApplication = parent; this.txtConsole.ContextMenuStrip = this.ContextMenuStrip; this.EnterImmediateMode(); } //===================================================================== #endregion #region Properties //===================================================================== private IApplication m_ParentApplication = null; private bool m_ImmediateMode = false; private bool m_ImmediateModeFirstTime = true; private List m_CommandList = new List(); private int m_CurrentCommandInList = 0; /// /// Gets the application which parents this console. /// protected IApplication ParentApplication { get { return this.m_ParentApplication; } } /// /// Gets/sets if the console is allowing user input. /// private bool ImmediateMode { get { return this.m_ImmediateMode; } set { // Set the flag this.m_ImmediateMode = value; // Nothing more to do if exiting immediate mode if (!value) return; // Show the prompt if we are entering immediate mode if (this.m_ImmediateModeFirstTime) { this.txtConsole.AppendText(PROMPT); this.m_ImmediateModeFirstTime = false; } else { this.txtConsole.AppendText(Environment.NewLine); this.txtConsole.AppendText(PROMPT); }// end if } } /// /// Gets a list of the commands executed. /// protected List CommandList { get { return this.m_CommandList; } } /// /// Gets the current command in the list. /// protected int CurrentCommandInList { get { return this.m_CurrentCommandInList; } set { if (value < 0) this.m_CurrentCommandInList = 0; else if (value >= this.m_CommandList.Count) this.m_CurrentCommandInList = this.m_CommandList.Count - 1; else this.m_CurrentCommandInList = value; } } //===================================================================== #endregion #region Events and Delegates //===================================================================== public delegate void ScriptCommandHandler(string command); private delegate void VoidHandler(); private delegate void StringHandler(string str); private ScriptCommandHandler m_EventStorage_ScriptCommand; /// /// An event raised when the user types a script command. /// public event ScriptCommandHandler UserEnteredScriptCommand { add { this.m_EventStorage_ScriptCommand += value; } remove { this.m_EventStorage_ScriptCommand -= value; } } /// /// Raise the UserEnteredScriptCommand event. /// /// protected void RaiseScriptCommand(string command) { // Add the command to the command list // NOTE: Newest commands are stored at the back of the list this.CommandList.Add(command); this.CurrentCommandInList = this.CommandList.Count - 1; // Raise event if (this.m_EventStorage_ScriptCommand != null) this.m_EventStorage_ScriptCommand(command); } //============================================================================ #endregion #region Public Methods //===================================================================== /// /// Focus the console. /// /// public new bool Focus() { base.Focus(); return this.txtConsole.Focus(); } /// /// Tell the console to enter immediate mode, which shows the prompt /// and allows users to enter text. /// /// This method is thread-safe. public void EnterImmediateMode() { // Make the call thread-safe if (this.InvokeRequired) { this.Invoke(new VoidHandler(this.EnterImmediateMode)); return; } this.ImmediateMode = true; this.txtConsole.Focus(); this.CurrentCommandInList = this.CommandList.Count; } /// /// Writes the given string to the console with no carriage return. /// /// /// This method is thread-safe. public void Write(string info) { // Make the call thread-safe if (this.InvokeRequired) { this.Invoke(new StringHandler(this.Write), info); return; } this.txtConsole.AppendText(info); } /// /// Writes the given string to the console and appends a carriage /// return at the end of the line. /// /// /// This method is thread-safe. public void WriteLine(string info) { // Make the call thread-safe if (this.InvokeRequired) { this.Invoke(new StringHandler(this.WriteLine), info); return; } // Split at "\r" or "\n" or "\r\n" string[] split = new string[] { "\r\n", "\r", "\n" }; string[] lines = info.Split(split, 100, StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) { string formattedLine = line.Trim(' '); this.txtConsole.AppendText(formattedLine + Environment.NewLine); } // Scroll to bottom this.txtConsole.ScrollToCaret(); } /// /// Writes a new line to the console. /// /// This method is thread-safe. public void WriteNewLine() { // Make the call thread-safe if (this.InvokeRequired) { this.Invoke(new VoidHandler(this.WriteNewLine)); return; } this.txtConsole.AppendText(Environment.NewLine); } /// /// Clears the console text. /// /// This method is thread-safe. public void Clear() { // Make the call thread-safe if (this.InvokeRequired) { this.Invoke(new VoidHandler(this.Clear)); return; } this.txtConsole.Clear(); this.m_ImmediateModeFirstTime = true; } //===================================================================== #endregion #region Private Methods //===================================================================== private void menuContextCopy_Click(object sender, EventArgs e) { if (this.txtConsole.Text != null && this.txtConsole.Text.Length > 0) { if (this.txtConsole.SelectionLength == 0) // Copy the whole console Clipboard.SetText(this.txtConsole.Text, TextDataFormat.Text); else Clipboard.SetText(this.txtConsole.SelectedText, TextDataFormat.Text); } } private void menuContextPaste_Click(object sender, EventArgs e) { this.RemoveNewLineFromClipboard(); this.txtConsole.Paste(); } private void menuContextClearAll_Click(object sender, EventArgs e) { this.Clear(); this.EnterImmediateMode(); } private void txtConsole_KeyDown(object sender, KeyEventArgs e) { // Get the last line in the text box int indexLastLine = this.txtConsole.Lines.Length - 1; string lastline = this.txtConsole.Lines[indexLastLine]; // Determine which action to take based on the key pressed if (e.KeyCode == Keys.Escape) { if (string.Compare(lastline, siFormScriptConsole.PROMPT) == 0) // Don't do anything, the user has pressed escape on an empty prompt return; else { // Abort the current command int lastPrompt = this.txtConsole.Text.LastIndexOf(PROMPT); this.txtConsole.Text = this.txtConsole.Text.Substring(0, lastPrompt); this.m_ImmediateModeFirstTime = true; this.EnterImmediateMode(); } } else if (this.ImmediateMode && (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)) { e.SuppressKeyPress = true; if (lastline.Length <= 2) { // The command was invalid return; } else { // Try to run the command this.ImmediateMode = false; this.txtConsole.AppendText(Environment.NewLine); this.RaiseScriptCommand(lastline.Replace(PROMPT, "")); } } else if (this.ImmediateMode && e.KeyCode == Keys.Up) { e.SuppressKeyPress = true; // Check there is something in the command list if (this.CommandList.Count == 0) return; // Cycle backwards through the command list string command = PROMPT + this.CommandList[this.CurrentCommandInList]; this.CurrentCommandInList--; if (string.Compare(command, lastline, true) == 0) command = PROMPT + this.CommandList[this.CurrentCommandInList]; // Set the command in the console this.SetLastLineInConsole(command); this.txtConsole.Select(this.txtConsole.Text.Length, 0); this.txtConsole.ScrollToCaret(); } else if (this.ImmediateMode && e.KeyCode == Keys.Down) { e.SuppressKeyPress = true; // Check there is something in the command list if (this.CommandList.Count == 0) return; // Cycle forwards through the command list string command = PROMPT + this.CommandList[this.CurrentCommandInList]; this.CurrentCommandInList++; if (string.Compare(command, lastline, true) == 0) command = PROMPT + this.CommandList[this.CurrentCommandInList]; // Set the command in the console this.SetLastLineInConsole(command); this.txtConsole.Select(this.txtConsole.Text.Length, 0); this.txtConsole.ScrollToCaret(); } else if (this.ImmediateMode && e.KeyCode == Keys.Back && lastline.Length == PROMPT.Length) { // The user is trying to delete the prompt - stop them e.SuppressKeyPress = true; } else if (this.ImmediateMode) { // Test for allowable keys if (e.Control && e.KeyCode == Keys.C) return; if (e.Control && e.KeyCode == Keys.V) { this.RemoveNewLineFromClipboard(); return; } if (e.KeyCode == Keys.Left) return; if (e.KeyCode == Keys.Right) return; if (e.KeyCode == Keys.Home) return; if (e.KeyCode == Keys.End) return; // Work out if the user is allowed to edit at the current position bool isLastLine = this.txtConsole.GetLineFromCharIndex(this.txtConsole.SelectionStart) == (indexLastLine); bool doesLastLineStartWithPrompt = lastline.StartsWith(siFormScriptConsole.PROMPT); bool isCaretAfterPrompt = (this.txtConsole.GetFirstCharIndexFromLine(indexLastLine) + 1) < this.txtConsole.SelectionStart; if (isLastLine && doesLastLineStartWithPrompt && isCaretAfterPrompt) // The user is allowed to edit return; else // The user is NOT allowed to edit e.SuppressKeyPress = true; } else { // The user is NOT allowed to edit e.SuppressKeyPress = true; } } /// /// Replace the last line in the text box with the given value. /// /// private void SetLastLineInConsole(string lastline) { if (this.txtConsole.Lines.Length == 0) { this.txtConsole.Text = lastline; } else { string[] lines = this.txtConsole.Lines; lines[lines.Length - 1] = lastline; this.txtConsole.Lines = lines; } } private void RemoveNewLineFromClipboard() { string clipboard = Clipboard.GetText(); if (clipboard == null || clipboard.Length == 0) return; clipboard = clipboard.Replace("\r\n", ""); clipboard = clipboard.Replace("\r", ""); clipboard = clipboard.Replace("\n", ""); Clipboard.SetText(clipboard); } //===================================================================== #endregion } }