#region Imported Libraries
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using Microsoft.Scripting.Hosting;
using System.IO;
using System.Windows.Input;
using IronPython;
using Microsoft.Scripting;
using System.Windows;
using System.Threading;
using System.Reflection;
using IronPython.Hosting;
using System.Diagnostics;
#endregion
#region Namespace Declaration
namespace Org.TechA.Wpf.UserControls
{
internal sealed class DlrConsolePresenter
{
#region Private Members
private readonly DlrConsoleModel _model;
private readonly DlrConsoleWindow _view;
#endregion
#region Constructor
///
/// Default Constructor.
///
///
/// The View.
///
internal DlrConsolePresenter(UserControl view)
{
//set the model and the view
_view = (DlrConsoleWindow)view;
_model = SetupModel();
//set the default scope vars
SetDefaultScopeVariables(_model.Scopes[0]);
//setup the view
InitializeView();
//subscribe to all the view events
WireupEvents();
}
#endregion
#region Accessor Mutators
///
/// The View.
///
internal DlrConsoleWindow View
{
get { return _view; }
}
///
/// The Model.
///
internal DlrConsoleModel Model
{
get { return _model; }
}
#endregion
#region Event Subscriptions
///
/// Wireup all the views events.
///
internal void WireupEvents()
{
_view.txtUserCodeInput.KeyDown +=
new KeyEventHandler(txtUserCodeInput_KeyDown);
_view.txtUserCodeInput.KeyUp += new KeyEventHandler(txtUserCodeInput_KeyUp);
_view.mitmPrintState.Click +=
new RoutedEventHandler(mitmPrintState_Click);
_view.mitmAbout.Click +=
new RoutedEventHandler(mitmAbout_Click);
_view.mitmAddScope.Click +=
new RoutedEventHandler(mitmAddScope_Click);
_view.mitmClearScreen.Click +=
new RoutedEventHandler(mitmClearScreen_Click);
_view.VariablesAdded +=
new Action>(_view_VariablesAdded);
}
void _view_VariablesAdded(Dictionary obj)
{
foreach (var key in obj.Keys)
{
_model.Scopes[0].SetVariable(key, obj[key]);
}
}
///
/// Clears the IronPython output screen.
///
///
/// The menu item.
///
///
/// The routed event arguments.
///
void mitmClearScreen_Click(object sender, RoutedEventArgs e)
{
_view.txtIronPythonOutput.Clear();
}
///
/// Menu item click event for added a scope.
///
///
/// The menu item.
///
///
/// The Routed event args.
///
void mitmAddScope_Click(object sender, RoutedEventArgs e)
{
}
///
/// About window menu item
/// click event hanlder.
///
///
/// The Menu Item.
///
///
/// The routed event arguments.
///
void mitmAbout_Click(object sender, RoutedEventArgs e)
{
AboutDialog dlg = new AboutDialog();
dlg.ShowDialog();
}
///
///
///
///
///
void mitmPrintState_Click(object sender, RoutedEventArgs e)
{
var scope = _model.Scopes[0];
var source =
scope.Engine
.CreateScriptSourceFromString("print '************************************************'\n" +
"print 'PRINTING VARIABLES FROM DEFAULT SCOPE'\n", SourceCodeKind.Statements);
var result = source.Execute(scope);
foreach(var varName in scope.GetVariableNames())
{
//get the variable by name
var variable = scope.GetVariable(varName);
//get the script source from string which will
//simply print the name of the variable
source = scope.Engine
.CreateScriptSourceFromString("print 'Variable Name is [" +
varName + "] and value is [" + variable + "]'", SourceCodeKind.Statements);
result = source.Execute(scope);
//_model.Writer.WriteLine(FormatResult(result));
_model.Writer.WriteLine(result);
}
source =
scope.Engine
.CreateScriptSourceFromString("print '************************************************'\n",
SourceCodeKind.Statements);
result = source.Execute(scope);
}
#endregion
#region Private Worker Methods
///
/// Setup of the View.
///
private void InitializeView()
{
FocusHelper.Focus(_view.txtUserCodeInput);
_view.txtUserCodeInput.SelectionStart = 1;
}
///
/// Setup the IronPython Runtime, Engine
/// and create a default scope.
///
private DlrConsoleModel SetupModel()
{
var runtime = IronPython.Hosting.Python.CreateRuntime();
var engine = runtime.GetEngine("py");
var scope = engine.CreateScope();
// give ruby access to all our assemblies
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
runtime.LoadAssembly(assembly);
}
var writer = SetupIO(runtime);
var default_scope = scope;
var model = new DlrConsoleModel(writer, engine);
model.Scopes.Add(default_scope);
return model;
}
///
/// Setup the IO classes that will have
/// the IronPython runtime have its IO
/// redirected to.
///
///
/// The IronPython runtime.
///
///
/// The Texwriter that the IronPython
/// runtime will write to.
///
private TextWriter SetupIO(ScriptRuntime runtime)
{
var writer = new TextBoxWriter(_view.txtIronPythonOutput);
var stream = new TextWriterStream(writer);
runtime.IO.SetOutput(stream, writer);
runtime.IO.SetErrorOutput(stream, writer);
return writer;
}
///
/// Formats the result of the of
/// execution of code by a ScriptSource.
///
///
/// The result of running a given script.
///
///
/// Returns the formatted result from
/// running a given script.
///
private string FormatResult(object obj)
{
var prefix = ">>>";
var result = default(string);
if (obj == null)
result = "";
else
result = obj.ToString();
return prefix + result;
}
///
/// Runs the code typed in the console window.
///
///
/// The code string.
///
private void RunCode(string theCode)
{
_model.Writer.WriteLine(">>> " + theCode);
//this part takes a while so thread it up
//to stop the UI from becoming non responsive
var scriptrunThread = new Thread(new ParameterizedThreadStart(RunCodeForScript));
scriptrunThread.ApartmentState = ApartmentState.STA;
scriptrunThread.Start(theCode);
}
private void RunCodeForScript(object args)
{
try
{
var theCode = args.ToString();
var scriptSource = _model.Engine.
CreateScriptSourceFromString(theCode,
SourceCodeKind.Statements);
object result = null;
lock(_model.Scopes)
{
result = scriptSource.Execute(_model.Scopes[0]);
}
//_model.Writer.WriteLine(FormatResult(result));
_model.Writer.WriteLine(result);
}
catch (Exception e)
{
_model.Writer.WriteLine(e.Message);
}
}
///
/// Sets any variables required
/// for the default scope.
///
///
/// The default scope.
///
private void SetDefaultScopeVariables(ScriptScope scope)
{
scope.SetVariable("scopename", "Default");
scope.SetVariable("test_callback_method", new Action(callback));
scope.SetVariable("test_callback_function", new Func(funcback));
}
public void callback()
{
Debug.WriteLine("Test Callback method got called");
}
public string funcback(string calledwith)
{
Console.WriteLine("Test Callback function got called with " + calledwith);
return calledwith;
}
#endregion
#region Event Delegates
///
/// The user entry code textbox
/// key down event handler.
///
///
/// The code text box.
///
///
/// The keys event args.
///
void txtUserCodeInput_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
AddInputToCommandCache(this._view.txtUserCodeInput.Text);
RunCode(this._view.txtUserCodeInput.Text);
e.Handled = true;
this._view.txtUserCodeInput.Clear();
}
}
///
/// Reinitialize the command cache index to its default
/// after an ESC command is pressed or when new commands are added
/// by the user
///
private void ResetCommandCacheIndex()
{
_model.CommandCacheCurrentIndex = -1;
}
///
/// Sets the new index prior to updating the command the UI
///
/// the index to change
/// the index of the cache item to display
private int UpateCommandCacheIndex(int indexChange)
{
UpdateCommandCacheIndexForInitialRequest(indexChange);
var newindex = _model.CommandCacheCurrentIndex + indexChange;
newindex = PerformBoundCheckOnUpdatedCacheIndex(newindex);
return newindex;
}
private int PerformBoundCheckOnUpdatedCacheIndex(int newindex)
{
if(newindex >= _model.CommandCache.Count)
{
newindex = _model.CommandCache.Count-1;
}
if(newindex <= 0)
{
newindex = 0;
}
return newindex;
}
private void UpdateCommandCacheIndexForInitialRequest(int indexChange)
{
if(_model.CommandCacheCurrentIndex == -1)
{
if(indexChange <0)//if traversing from the start to the end
{
_model.CommandCacheCurrentIndex = 0;
}
else // if traversing from the end to the start
{
_model.CommandCacheCurrentIndex = _model.CommandCache.Count - 1;
}
}
}
private void UpdateUserInputFromCommandCache(int commandCacheIndex)
{
if (commandCacheIndex < 0)
{
return;
}
_model.CommandCacheCurrentIndex = commandCacheIndex;
_view.txtUserCodeInput.Clear();
_view.txtUserCodeInput.Text = _model.CommandCache[_model.CommandCacheCurrentIndex];
}
///
/// Adds the Command text that is to be executed into
/// the cache so as to allow for the Up and Down arrow command cache navigation
///
///
private void AddInputToCommandCache(string text)
{
_model.CommandCache.Add(text);
ResetCommandCacheIndex();
}
///
/// Handles Key Presses to enable
/// UP and DOWN arrow traversal of items in the
/// command cache
///
/// User Input on the text box
/// The Key Up event arguments
private void txtUserCodeInput_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up)
{
UpdateUserInputFromCommandCache(UpateCommandCacheIndex(-1));
e.Handled = true;
return;
}
if (e.Key == Key.Down)
{
UpdateUserInputFromCommandCache(UpateCommandCacheIndex(1));
e.Handled = true;
return;
}
if (e.Key == Key.Escape)
{
_view.txtUserCodeInput.Clear();
ResetCommandCacheIndex();
e.Handled = true;
return;
}
}
#endregion
}
}
#endregion