//---------------------------------------------------------------------------------------------------------------------------------------------------
// CompilerControl class, inherits from UserControl
// The code behind the CompilerControl form
// Builds the form for the student input and code output. Handles loading challenges from XML, calls compiling and running code
// (CSharpCompiler), handles form items (scrolling, adding line numbers, buttons, resizing, etc.) and docking/undocking of the window.
//---------------------------------------------------------------------------------------------------------------------------------------------------
// Engineered by: Amanda Chaffin
//---------------------------------------------------------------------------------------------------------------------------------------------------
namespace DarkWynter.App
{
#region Using Statements
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.CodeDom.Compiler;
using System.CodeDom;
using System.IO;
using System.Xml;
using Microsoft.Xna;
using Microsoft.Xna.Framework;
using DarkWynter.Stream;
using DarkWynter.Engine.Globals;
using DarkWynter.Engine.Init;
using DarkWynter.Engine.Utilities;
using DarkWynter.Engine.EventControl;
using DarkWynter.Game;
using DarkWynter.Engine.GameObjects;
using DarkWynter.App.Surveys;
using DarkWynter.App.ChallengeSet;
using System.Threading;
using CSTextEditor;
#endregion
///
/// The compiler form
///
public partial class CompilerControl : UserControl
{
public static CompilerControl compilerControl;
public static List challengeList;
public static MenuStrip menuStripPointer;
public static RichTextBox codeWindow_RTB;
public static string customErrorMessage;
public static string studentCode;
public delegate void Output_EventHandler(string message);
CompilerResults compilerResults;
PrettyCompilerControl prettyCompilerControl;
int unsuccessfulCompiles = 0;
string cleanedStudentCode;
bool docked = true;
///
/// Creates a new compiler control (form), initializes, and displays
///
public CompilerControl()
{
InitializeComponent();
this.Dock = DockStyle.Fill;
LoadChallenges();
menuStripPointer = compiler_MS;
CompilerControl.codeWindow_RTB = this.code_TB;
Engine.DarkWynterEngine.onLevelChange += new DarkWynter.Engine.DarkWynterEngine.OnLevelChange(this.Load_OnLevelChange);
compilerControl = this;
number_L.Font = new Font(code_TB.Font.FontFamily, code_TB.Font.Size - 0.0f);
}
///
/// Resets the text to the next challenge, the delegate starts the level change sequence
///
/// Controller boolean event arguments
public void Load_OnLevelChange(EventArgs e)
{
Statics_Engine.GameSettings.isProblemFinished = true;
resetText();
}
///
/// Loads the challenges from XML
///
private void LoadChallenges()
{
#region Challenges
// Create Challenge List
challengeList = new List();
for (int i = 0; i < XML.ChallengeNodes.Count; i++)
{
if (int.Parse(XML.ChallengeNodes[i].Attributes["id"].Value) == 0)
{
challengeList.Add(new Challenge0(XML.ChallengeNodes[i]));
}
if (int.Parse(XML.ChallengeNodes[i].Attributes["id"].Value) == 1)
{
challengeList.Add(new Challenge1(XML.ChallengeNodes[i]));
}
if (int.Parse(XML.ChallengeNodes[i].Attributes["id"].Value) == 2)
{
challengeList.Add(new Challenge2(XML.ChallengeNodes[i]));
}
}
// Init first problem
Statics_Engine.GameSettings.currentChallengeNumber = DarkWynter.Engine.Globals.Statics_Engine.levelIndex;
// Load the first problem
code_TB.Text =
challengeList[Statics_Engine.GameSettings.currentChallengeNumber].GetScaffolding();
// Load the first instructions
InstructionControl.dialoguePointer_TB.Text +=
challengeList[Statics_Engine.GameSettings.currentChallengeNumber].challengeDialogue[challengeList[Statics_Engine.GameSettings.currentChallengeNumber].dialogueCounter] + "\r\n";
#endregion
}
#region Compile and Run the code
///
/// Compile the code and provide output
///
/// String output of the program, stores in output text box
public string CompileCodeWithOutput()
{
CompileCode();
return output_TB.Text;
}
///
/// Compiles the code in the codeTextBox.Text field
///
public void CompileCode()
{
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Button Message] Student hit compiler button [End Button Message]");
Logging.G2LLogList.Add("\r\n[Student Code] - \r\n");
Logging.G2LLogList.Add(cleanedStudentCode + "\r\n[End Student Code]\r\n");
compilerResults = DarkWynter.Engine.Compiler.CSharpCompiler.CompileCode(studentCode);
// If _does_not_ compile
if (compilerResults.Errors.Count > 0)
{
// Display compilation errors.
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendFormat("Errors building {0} into {1}\n\n",
studentCode, compilerResults.PathToAssembly);
foreach (CompilerError ce in compilerResults.Errors)
{
stringBuilder.Append(ce.ToString());
stringBuilder.Append("\n");
}
unsuccessfulCompiles++;
GameEventHandler.CurrentGameConditions.triggerSanity = 11;
output_TB.Text = stringBuilder.ToString();
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Error Message] There were errors in the code [End Error Message] ");
Logging.G2LLogList.Add(stringBuilder.ToString());
Logging.G2LLogList.Add(" [Record Message] This is the " + unsuccessfulCompiles + " attempt at this problem. [End Record Message] ");
}
if (compilerResults.Errors.Count == 0)
{
output_TB.Text = "Compile Successful\n";
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Success Message] There were no errors in the code [End Success Message] ");
}
}
///
/// Runs the compiled code
///
///
///
private void runMenuItem_TS_Click(object sender, EventArgs e)
{
RunCode();
}
///
/// Runs the Code, after compilation, in the codeTextBoc.Text field
///
public void RunCode()
{
// If it hasn't been compiled, ask the students to compile it
if (compilerResults == null)
{
output_TB.Text = "You must compile before you can run!";
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Error Message] Student tried to run without compiling first. [End Error Message]");
return;
}
// If compiles, validate output
if (compilerResults.Errors.Count == 0)
{
StringWriter stringWriter = new StringWriter();
Console.SetOut(stringWriter);
// Execute user code
DarkWynter.Engine.Compiler.CSharpCompiler.Execute(compilerResults.CompiledAssembly, "StudentCode", "StudentCode", null);
// List output in compiler feedback box
try
{
output_TB.Text += stringWriter.ToString();
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Compiler output] - ");
Logging.G2LLogList.Add(stringWriter.ToString() + "[End Compiler output]\r\n");
// Execute instructor evaluation and feedback function
if (challengeList[Statics_Engine.GameSettings.currentChallengeNumber].ValidateStudentCode(studentCode, stringWriter.ToString()))
{
if (Statics_Engine.GameSettings.isProblemFinished == false)
{
// If correct, call the vis code
challengeList[Statics_Engine.GameSettings.currentChallengeNumber].RunVizualization();
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Success Message] Bridge " +
Statics_Engine.GameSettings.currentChallengeNumber + " built successfully. [End Success Message]");
// Ensure Current Prob does not go out of bounds
if (Statics_Engine.GameSettings.currentChallengeNumber < challengeList.Count - 1)
{
// Finish the vis and don't run it again
Statics_Engine.GameSettings.isProblemFinished = true;
}
}
}
// write the custom error message to the screen and to the log
else
output_TB.Text = customErrorMessage;
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Custom Error Message] " + customErrorMessage + " [End Custom Error Message]");
}
catch (NullReferenceException nre)
{
output_TB.Text = nre.ToString();
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Compiler output] - ");
}
// Move the scroller down
scroll2Text();
stringWriter.Close();
}
}
#endregion
#region Buttons, buttons, and more buttons
///
/// Open the form, deactivates Engine controls, resize the gameWindow, handle resetText() trigger
///
///
///
private void openMenuItem_TS_Click(object sender, EventArgs e)
{
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Button Message] Compiler panels opened [End Button Message]");
Logging.G2LLogList.Add("\r\n[Game Message] Student is on problem number " +
Statics_Engine.GameSettings.currentChallengeNumber);
Logging.G2LLogList.Add("Student is on island number " +
Statics_Engine.GameSettings.currentChallengeNumber + " [End Game Message] \r\n");
Engine.DarkWynterEngine.Deactivate();
if (Statics_Engine.PlayerSettings.doResetText == true)
{
resetText();
}
// Set the viewport
Microsoft.Xna.Framework.Graphics.Viewport viewport = new Microsoft.Xna.Framework.Graphics.Viewport();
viewport.MinDepth = Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport.MinDepth;
viewport.MaxDepth = Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport.MaxDepth;
Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport = viewport;
}
///
/// Reactivates Engine controls, resets gameWindow
///
///
///
private void closeMenuItem_TS_Click(object sender, EventArgs e)
{
Logging.G2LLogList.Add(DateTime.Now.ToString() + " - [Button Message] Compiler panels closed [End Button Message] ");
Engine.DarkWynterEngine.Activate();
// Set the viewport
Microsoft.Xna.Framework.Graphics.Viewport viewport = new Microsoft.Xna.Framework.Graphics.Viewport();
viewport.MinDepth = Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport.MinDepth;
viewport.MaxDepth = Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport.MaxDepth;
Statics_Stream.RenderSettings.graphics.GraphicsDevice.Viewport = viewport;
}
///
/// Will save out to a text file when I finish it
///
///
///
private void save_TS_Click(object sender, EventArgs e)
{
// save the code out
String name = "problem" + Statics_Engine.GameSettings.currentChallengeNumber + ".txt";
string logFile = "../../../__Game/Compiler/Logs/" + name;
StreamWriter myFile = null;
FileStream fileStream = File.Open(logFile, FileMode.CreateNew, FileAccess.Write);
myFile = new StreamWriter(fileStream);
myFile.WriteLine(DateTime.Now);
myFile.Write(code_TB.Text);
myFile.Close();
}
///
/// Activate Compiler
///
///
///
private void compileMenuItem_TS_Click(object sender, EventArgs e)
{
// Compile student code
studentCode = code_TB.Text;
cleanedStudentCode = studentCode.Trim();
cleanedStudentCode = studentCode.Replace('\n', ' ');
CompileCode();
}
///
/// Refreshes to the next code set
///
///
///
public void refreshMenuItem_TS_Click(object sender, EventArgs e)
{
if (Statics_Engine.GameSettings.isProblemFinished == true)
{
//HERE
Statics_Engine.GameSettings.currentChallengeNumber++;
resetText();
resetText();
}
else
resetText();
}
#endregion
#region Helper methods
///
/// Autoscoll boxNumber keeps track of which one we are changing
///
public void scroll2Text()
{
output_TB.SelectionStart = output_TB.Text.Length;
output_TB.ScrollToCaret();
output_TB.Select();
}
///
/// Resets all the text boxes back to initial state
///
public void resetText()
{
// Reset dialog counter in problem
if (Statics_Engine.GameSettings.currentChallengeNumber != -1)
{
challengeList[Statics_Engine.GameSettings.currentChallengeNumber].dialogueCounter = 0;
// Clear boxes
InstructionControl.dialoguePointer_TB.Text = "";
output_TB.Text = "";
code_TB.Text = "";
// Load the problem
code_TB.Text =
challengeList[Statics_Engine.GameSettings.currentChallengeNumber].GetScaffolding();
// Load the first instructions
InstructionControl.dialoguePointer_TB.Text +=
challengeList[Statics_Engine.GameSettings.currentChallengeNumber]
.challengeDialogue[challengeList[Statics_Engine.GameSettings.currentChallengeNumber].dialogueCounter] + "\r\n";
Statics_Engine.GameSettings.isProblemFinished = false;
}
}
private void updateNumber_L()
{
//we get index of first visible char and number of first visible line
System.Drawing.Point pos = new System.Drawing.Point(0, 0);
int firstIndex = code_TB.GetCharIndexFromPosition(pos);
int firstLine = code_TB.GetLineFromCharIndex(firstIndex);
//now we get index of last visible char and number of last visible line
pos.X = ClientRectangle.Width;
pos.Y = ClientRectangle.Height;
int lastIndex = code_TB.GetCharIndexFromPosition(pos);
int lastLine = code_TB.GetLineFromCharIndex(lastIndex);
//this is point position of last visible char, we'll use its Y value for calculating numberLabel size
pos = code_TB.GetPositionFromCharIndex(lastIndex);
//finally, renumber label
number_L.Text = "";
for (int i = firstLine; i <= lastLine + 1; i++)
{
number_L.Text += i + 1 + "\n";
}
}
private void code_TB_TextChanged(object sender, EventArgs e)
{
updateNumber_L();
}
private void code_TB_VScroll(object sender, EventArgs e)
{
//move location of numberLabel for amount of pixels caused by scrollbar
int d = code_TB.GetPositionFromCharIndex(0).Y % (code_TB.Font.Height + 1);
number_L.Location = new System.Drawing.Point(0, d);
updateNumber_L();
}
private void code_TB_Resize(object sender, EventArgs e)
{
code_TB_VScroll(null, null);
}
private void code_TB_FontChanged(object sender, EventArgs e)
{
updateNumber_L();
code_TB_VScroll(null, null);
}
#endregion
#region External Compiler
///
/// Allows the compiler to run on a seperate thread
///
/// Sender controller
/// Controller boolean event arguments
private void cSCompilerMenuItem_TS_Click(object sender, EventArgs e)
{
// Form thread
Thread t = new Thread(new ThreadStart(compilerForm));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
///
/// Initialize controller form
///
private void compilerForm()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
prettyCompilerControl = new PrettyCompilerControl();
prettyCompilerControl.textEditorControl1.Text =
challengeList[Statics_Engine.GameSettings.currentChallengeNumber].GetScaffolding();
Application.Run(prettyCompilerControl);
}
///
/// Allows compiler form to be docked/undocked into/from game window
///
/// Sender controller
/// Controller boolean event arguments
private void undockMenuItem_TS_Click(object sender, EventArgs e)
{
if (docked)
{
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Controls.Clear();
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Controls.Add(this);
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Name = "CompilerForm";
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Text = "Compiler Form";
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.WindowState = FormWindowState.Maximized;
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.ControlBox = false;
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.MaximizeBox = false;
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.MinimizeBox = false;
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.ResumeLayout(false);
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Show();
UCADInterfaceControl._UCAD_Inteface.LeftPanelCollapse();
undockMenuItem_TS.Text = "Dock";
docked = false;
}
else
{
UCADInterfaceControl._UCAD_Inteface.UCAD_Left_Panel.Controls.Clear();
UCADInterfaceControl._UCAD_Inteface.UCAD_Left_Panel.Controls.Add(this);
UCADInterfaceControl._UCAD_Inteface.UCAD_ChildForm.Hide();
UCADInterfaceControl._UCAD_Inteface.LeftPanelExpand();
undockMenuItem_TS.Text = "Undock";
docked = true;
}
}
#endregion
}
}