context: // 
  // ConsoleView.cs
  //  
  // Author:
  //       Peter Johanson <latexer@gentoo.org>
  //       Lluis Sanchez Gual <lluis@novell.com>
  // 
  // Copyright (c) 2005, Peter Johanson (latexer@gentoo.org)
  // Copyright (c) 2009 Novell, Inc (http://www.novell.com)
  // 
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  // 
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  // THE SOFTWARE.
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using Gtk;
  using Docking.Widgets;
  using Docking.Components;
  using Docking.Tools;
  
  namespace MonoDevelop.Components
  {
     [System.ComponentModel.ToolboxItem(true)]
     public class ConsoleView : ScrolledWindow
     {
        string scriptLines = "";
  
        Stack<string> commandHistoryPast = new Stack<string>();
        Stack<string> commandHistoryFuture = new Stack<string>();
  
        bool inBlock = false;
        string blockText = "";
  
        TextView textView;
        object m_Lock = new object();
  
        public ConsoleView()
        {
           PromptString = "> ";
           PromptMultiLineString = ">> ";
  
           textView = new TextView();
           Add(textView);
           ShowAll();
  
           textView.WrapMode = Gtk.WrapMode.Word;
           textView.KeyPressEvent += TextViewKeyPressEvent;
           textView.PopulatePopup += TextViewPopulatePopup;
  
           // The 'Freezer' tag is used to keep everything except
           // the input line from being editable
           TextTag tag = new TextTag("Freezer");
           tag.Editable = false;
           Buffer.TagTable.Add(tag);
           Prompt(false);
        }
  
        void TextViewPopulatePopup(object o, PopulatePopupArgs args)
        {
           //ReplaceGtkIconsByOwnOnes(args.Menu);
  
           SeparatorMenuItem sep = new SeparatorMenuItem();
           sep.Show();
           args.Menu.Add(sep);
  
           TaggedLocalizedImageMenuItem newitem = new TaggedLocalizedImageMenuItem("Clear");
           newitem.Image = Docking.Tools.ResourceLoader_Docking.LoadImage("Broom-16.png");
           newitem.Activated += (object sender, EventArgs e) => Clear();               
           newitem.ShowAll();
  
           args.Menu.Add(newitem);
  
           Localization.LocalizeMenu(args.Menu);
        }
  
        public void SetFont(Pango.FontDescription font)
        {
           textView.ModifyFont(font);
        }
  
        public string PromptString { get; set; }
  
        public bool AutoIndent { get; set; }
  
        public string PromptMultiLineString { get; set; }
  
        [GLib.ConnectBeforeAttribute]
        void TextViewKeyPressEvent(object o, KeyPressEventArgs args)
        {
           args.RetVal = ProcessKeyPressEvent(args.Event);
        }
  
        bool ProcessKeyPressEvent(Gdk.EventKey ev)
        {
           // Short circuit to avoid getting moved back to the input line
           // when paging up and down in the shell output
           if (ev.Key == Gdk.Key.Page_Up || ev.Key == Gdk.Key.KP_Page_Up
               || ev.Key == Gdk.Key.Page_Down || ev.Key == Gdk.Key.KP_Page_Down)
              return false;
  
           lock (m_Lock)
           {
  
              // Needed so people can copy and paste, but always end up
              // typing in the prompt.
              if (Cursor.Compare(InputLineBegin) < 0)
              {
                 Buffer.MoveMark(Buffer.SelectionBound, InputLineEnd);
                 Buffer.MoveMark(Buffer.InsertMark, InputLineEnd);
              }
  
              //			if (ev.State == Gdk.ModifierType.ControlMask && ev.Key == Gdk.Key.space)
              //				TriggerCodeCompletion ();
  
              if (ev.Key == Gdk.Key.Return || ev.Key == Gdk.Key.KP_Enter)
              {
                 if (inBlock)
                 {
                    if (InputLine == "")
                    {
                       ProcessInput(blockText);
                       blockText = "";
                       inBlock = false;
                    }
                    else
                    {
                       blockText += "\n" + InputLine;
                       string whiteSpace = null;
                       if (AutoIndent)
                       {
                          System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"^(\s+).*");
                          whiteSpace = r.Replace(InputLine, "$1");
                          if (InputLine.EndsWith(BlockStart))
                             whiteSpace += "\t";
                       }
                       Prompt(true, true);
                       if (AutoIndent)
                          InputLine += whiteSpace;
                    }
                 }
                 else
                 {
                    // Special case for start of new code block
                    if (!string.IsNullOrEmpty(BlockStart) && InputLine.Trim().EndsWith(BlockStart))
                    {
                       inBlock = true;
                       blockText = InputLine;
                       Prompt(true, true);
                       if (AutoIndent)
                          InputLine += "\t";
                       return true;
                    }
                    // Bookkeeping
                    if (InputLine != "")
                    {
                       // Everything but the last item (which was input),
                       //in the future stack needs to get put back into the
                       // past stack
                       while (commandHistoryFuture.Count > 1)
                          commandHistoryPast.Push(commandHistoryFuture.Pop());
                       // Clear the pesky junk input line
                       commandHistoryFuture.Clear();
  
                       // Record our input line
                       commandHistoryPast.Push(InputLine);
                       if (scriptLines == "")
                          scriptLines += InputLine;
                       else
                          scriptLines += "\n" + InputLine;
  
                       ProcessInput(InputLine);
                    }
                    else
                    {
                       ProcessInput(null);
                    }
                 }
                 return true;
              }
  
              // The next two cases handle command history	
              else if (ev.Key == Gdk.Key.Up || ev.Key == Gdk.Key.KP_Up)
              {
                 if (!inBlock && commandHistoryPast.Count > 0)
                 {
                    if (commandHistoryFuture.Count == 0)
                       commandHistoryFuture.Push(InputLine);
                    else
                    {
                       if (commandHistoryPast.Count == 1)
                          return true;
                       commandHistoryFuture.Push(commandHistoryPast.Pop());
                    }
  
gt:                   InputLine = commandHistoryPast.Peek();