466 lines
17 KiB
C#
466 lines
17 KiB
C#
#region License
|
|
//
|
|
// The Open Toolkit Library License
|
|
//
|
|
// Copyright (c) 2006 - 2009 the Open Toolkit library.
|
|
//
|
|
// 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.
|
|
//
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Drawing.Text;
|
|
using System.Reflection;
|
|
using System.Windows.Forms;
|
|
using OpenTK.Examples.Properties;
|
|
using System.Threading;
|
|
using System.IO;
|
|
|
|
namespace Examples
|
|
{
|
|
public partial class ExampleBrowser : Form
|
|
{
|
|
#region Fields
|
|
|
|
//PrivateFontCollection font_collection = new PrivateFontCollection();
|
|
|
|
bool show_warning = true;
|
|
|
|
static readonly string SourcePath = FindSourcePath();
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
public ExampleBrowser()
|
|
{
|
|
Font = SystemFonts.DialogFont;
|
|
|
|
InitializeComponent();
|
|
Icon = Resources.App;
|
|
|
|
// Windows 6 (Vista) and higher come with Consolas, a high-quality monospace font. Use that or fallback to
|
|
// the generic monospace font on other systems.
|
|
if (System.Environment.OSVersion.Platform == PlatformID.Win32NT &&
|
|
System.Environment.OSVersion.Version.Major >= 6)
|
|
{
|
|
textBoxOutput.Font = richTextBoxSource.Font = new Font("Consolas", 10.0f, FontStyle.Regular);
|
|
}
|
|
else
|
|
{
|
|
textBoxOutput.Font = richTextBoxSource.Font =
|
|
new Font(FontFamily.GenericMonospace, 10.0f, FontStyle.Regular);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Protected Members
|
|
|
|
protected override void OnLoad(EventArgs e)
|
|
{
|
|
base.OnLoad(e);
|
|
|
|
// Add those by hand, because using the designer results in an empty
|
|
// image list when cross-compiling on Mono.
|
|
imageListSampleCategories.Images.Add("OpenAL", Resources.OpenAL);
|
|
imageListSampleCategories.Images.Add("OpenGL", Resources.OpenGL);
|
|
imageListSampleCategories.Images.Add("OpenGLES", Resources.OpenGLES);
|
|
imageListSampleCategories.Images.Add("OpenCL", Resources.OpenCL);
|
|
imageListSampleCategories.Images.Add("OpenTK", Resources.OpenTK);
|
|
imageListSampleCategories.Images.Add("1.x", Resources.v1x);
|
|
imageListSampleCategories.Images.Add("2.x", Resources.v2x);
|
|
imageListSampleCategories.Images.Add("3.x", Resources.v3x);
|
|
imageListSampleCategories.Images.Add("4.x", Resources.v4x);
|
|
|
|
Debug.Listeners.Add(new TextBoxTraceListener(textBoxOutput));
|
|
treeViewSamples.TreeViewNodeSorter = new SamplesTreeViewSorter();
|
|
|
|
LoadSamplesFromAssembly(Assembly.GetExecutingAssembly());
|
|
}
|
|
|
|
protected override void OnShown(EventArgs e)
|
|
{
|
|
if (show_warning)
|
|
{
|
|
//MessageBox.Show("The new Sample Browser is not complete. Please report any issues at http://www.opentk.com/project/issues.",
|
|
// "Work in Progress", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
show_warning = false;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Members
|
|
|
|
#region Events
|
|
|
|
#region TreeView
|
|
|
|
private void treeViewSamples_AfterSelect(object sender, TreeViewEventArgs e)
|
|
{
|
|
const string no_docs = "Documentation has not been entered.";
|
|
const string no_source = "Source code has not been entered.";
|
|
|
|
if (e.Node.Tag != null && !String.IsNullOrEmpty(((ExampleInfo)e.Node.Tag).Attribute.Documentation))
|
|
{
|
|
string docs = null;
|
|
string source = null;
|
|
|
|
ExampleInfo einfo = (ExampleInfo)e.Node.Tag;
|
|
string sample = einfo.Attribute.Documentation;
|
|
string category = einfo.Attribute.Category.ToString();
|
|
string subcategory = einfo.Attribute.Subcategory;
|
|
|
|
string path = Path.Combine(Path.Combine(Path.Combine(SourcePath, category), subcategory), sample);
|
|
string sample_rtf = Path.ChangeExtension(path, "rtf");
|
|
string sample_cs = Path.ChangeExtension(path, "cs");
|
|
|
|
if (File.Exists(sample_rtf))
|
|
{
|
|
docs = File.ReadAllText(sample_rtf);
|
|
}
|
|
|
|
if (File.Exists(sample_cs))
|
|
{
|
|
source = File.ReadAllText(sample_cs);
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(docs))
|
|
richTextBoxDescription.Text = String.Format("File {0} not found.", sample_rtf);
|
|
else
|
|
richTextBoxDescription.Rtf = docs;
|
|
|
|
if (String.IsNullOrEmpty(source))
|
|
richTextBoxSource.Text = String.Format("File {0} not found.", sample_cs);
|
|
else
|
|
richTextBoxSource.Text = source;
|
|
}
|
|
else
|
|
{
|
|
richTextBoxDescription.Text = no_docs;
|
|
richTextBoxSource.Text = no_source;
|
|
}
|
|
}
|
|
|
|
private void treeViewSamples_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
|
|
{
|
|
if (e.Node.Tag != null)
|
|
{
|
|
ActivateNode(e.Node);
|
|
}
|
|
}
|
|
|
|
private void treeViewSamples_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
// The enter key activates a node (either expands/collapses or executes its sample).
|
|
switch (e.KeyCode)
|
|
{
|
|
case Keys.Enter:
|
|
ActivateNode(treeViewSamples.SelectedNode);
|
|
e.Handled = true;
|
|
e.SuppressKeyPress = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void treeViewSamples_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
// Make sure that right-clicking a new node will select that node before displaying
|
|
// the context menu. Without this, right-clicking a node does not select it, which
|
|
// is completely disorienting.
|
|
// As a bonus, make any mouse button select the underlying node,
|
|
TreeNode node = treeViewSamples.HitTest(e.Location).Node;
|
|
if (node != null)
|
|
treeViewSamples.SelectedNode = node;
|
|
|
|
// Middle click selects and activates a node (either expands/collapses or executes its sample).
|
|
// Right button displays the context menu.
|
|
// All other mouse buttons simply select the underlying node.
|
|
switch (e.Button)
|
|
{
|
|
case MouseButtons.Middle:
|
|
ActivateNode(node);
|
|
break;
|
|
|
|
case MouseButtons.Right:
|
|
treeViewSamples.ContextMenuStrip.Show(sender as Control, e.Location);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void treeViewSamples_AfterExpand(object sender, TreeViewEventArgs e)
|
|
{
|
|
foreach (TreeNode child in e.Node.Nodes)
|
|
child.EnsureVisible();
|
|
}
|
|
|
|
private void contextMenuStripSamples_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
|
|
{
|
|
switch (e.ClickedItem.Text)
|
|
{
|
|
case "&Run Sample": RunSample(this, (ExampleInfo)treeViewSamples.SelectedNode.Tag); break;
|
|
case "View Description": tabControlSample.SelectedTab = tabDescription; break;
|
|
case "View Source Code": tabControlSample.SelectedTab = tabSource; break;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Description
|
|
|
|
private void richTextBoxDescription_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
richTextBoxDescription.ContextMenuStrip.Show(sender as Control, e.X, e.Y);
|
|
}
|
|
}
|
|
|
|
private void contextMenuStripDescription_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
|
|
{
|
|
if (e.ClickedItem.Text == "&Copy")
|
|
{
|
|
Clipboard.SetText(richTextBoxDescription.SelectedRtf, TextDataFormat.Rtf);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Source Code
|
|
|
|
private void richTextBoxSource_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
richTextBoxSource.ContextMenuStrip.Show(sender as Control, e.X, e.Y);
|
|
}
|
|
}
|
|
|
|
private void contextMenuStripSource_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
|
|
{
|
|
if (e.ClickedItem.Text == "&Copy")
|
|
{
|
|
Clipboard.SetText(richTextBoxSource.SelectedText, TextDataFormat.Text);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Actions
|
|
|
|
void LoadSamplesFromAssembly(Assembly assembly)
|
|
{
|
|
if (assembly == null)
|
|
throw new ArgumentNullException("assembly");
|
|
|
|
Type[] types = assembly.GetTypes();
|
|
foreach (Type type in types)
|
|
{
|
|
object[] attributes = type.GetCustomAttributes(false);
|
|
ExampleAttribute example = null;
|
|
foreach (object attr in attributes)
|
|
{
|
|
if (attr is ExampleAttribute)
|
|
{
|
|
example = (ExampleAttribute)attr;
|
|
|
|
if (example.Visible)
|
|
{
|
|
// Add this example to the sample TreeView.
|
|
// First check whether the ExampleCategory exists in the tree (and add it if it doesn't).
|
|
// Then add the example as a child node on this category.
|
|
|
|
if (!treeViewSamples.Nodes.ContainsKey(example.Category.ToString()))
|
|
{
|
|
int category_index = GetImageIndexForSample(imageListSampleCategories, example.Category.ToString(), String.Empty);
|
|
treeViewSamples.Nodes.Add(example.Category.ToString(), String.Format("{0} samples", example.Category),
|
|
category_index, category_index);
|
|
}
|
|
|
|
int image_index = GetImageIndexForSample(imageListSampleCategories, example.Category.ToString(), example.Subcategory);
|
|
TreeNode node = new TreeNode(example.Title, image_index, image_index);
|
|
node.Name = example.Title;
|
|
node.Tag = new ExampleInfo(type, example);
|
|
treeViewSamples.Nodes[example.Category.ToString()].Nodes.Add(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
treeViewSamples.Sort();
|
|
}
|
|
|
|
void ActivateNode(TreeNode node)
|
|
{
|
|
if (node == null)
|
|
return;
|
|
|
|
if (node.Tag == null)
|
|
{
|
|
if (node.IsExpanded)
|
|
node.Collapse();
|
|
else
|
|
node.Expand();
|
|
}
|
|
else
|
|
{
|
|
tabControlSample.SelectedTab = tabPageOutput;
|
|
textBoxOutput.Clear();
|
|
RunSample(node.TreeView.TopLevelControl, (ExampleInfo)node.Tag);
|
|
}
|
|
}
|
|
|
|
static int GetImageIndexForSample(ImageList list, string category, string subcategory)
|
|
{
|
|
if (list == null)
|
|
throw new ArgumentNullException("list");
|
|
|
|
foreach (string extension in new string[] { "", ".png", ".jpg" })
|
|
{
|
|
string name = subcategory.ToString() + extension;
|
|
if (list.Images.ContainsKey(name))
|
|
return list.Images.IndexOfKey(name);
|
|
|
|
name = category.ToString() + extension;
|
|
if (list.Images.ContainsKey(name))
|
|
return list.Images.IndexOfKey(name);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void RunSample(Control parent, ExampleInfo e)
|
|
{
|
|
if (e == null)
|
|
return;
|
|
|
|
MethodInfo main =
|
|
e.Example.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ??
|
|
e.Example.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(object), typeof(object) }, null);
|
|
if (main != null)
|
|
{
|
|
try
|
|
{
|
|
if (parent != null)
|
|
{
|
|
parent.Visible = false;
|
|
Application.DoEvents();
|
|
}
|
|
Trace.WriteLine(String.Format("Launching sample: \"{0}\"", e.Attribute.Title));
|
|
Trace.WriteLine(String.Empty);
|
|
|
|
Thread thread = new Thread((ThreadStart)delegate
|
|
{
|
|
try
|
|
{
|
|
main.Invoke(null, null);
|
|
}
|
|
catch (TargetInvocationException expt)
|
|
{
|
|
string ex_info;
|
|
if (expt.InnerException != null)
|
|
ex_info = expt.InnerException.ToString();
|
|
else
|
|
ex_info = expt.ToString();
|
|
MessageBox.Show(ex_info, "An OpenTK example encountered an error.", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
|
|
Debug.Print(expt.ToString());
|
|
}
|
|
catch (NullReferenceException expt)
|
|
{
|
|
MessageBox.Show(expt.ToString(), "The Example launcher failed to load the example.", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
}
|
|
});
|
|
thread.IsBackground = true;
|
|
thread.Start();
|
|
thread.Join();
|
|
}
|
|
finally
|
|
{
|
|
if (parent != null)
|
|
{
|
|
parent.Visible = true;
|
|
Application.DoEvents();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageBox.Show("The selected example does not define a Main method", "Entry point not found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
|
|
}
|
|
}
|
|
|
|
// Tries to detect the path that contains the source for the examples.
|
|
static string FindSourcePath()
|
|
{
|
|
string current_dir = Directory.GetCurrentDirectory();
|
|
|
|
// Typically, our working directory is either "[opentk]/Binaries/OpenTK/[config]" or "[opentk]".
|
|
// The desired source path is "[opentk]/Source/Examples/[ExampleCategory]"
|
|
|
|
string guess = current_dir;
|
|
if (CheckPath(ref guess))
|
|
return guess; // We were in [opentk] after all
|
|
|
|
guess = current_dir;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
DirectoryInfo dir = Directory.GetParent(guess);
|
|
if (!dir.Exists)
|
|
break;
|
|
guess = dir.FullName;
|
|
}
|
|
|
|
if (CheckPath(ref guess))
|
|
return guess; // We were in [opentk]/Binaries/OpenTK/[config] after all
|
|
|
|
throw new DirectoryNotFoundException();
|
|
}
|
|
|
|
static bool CheckPath(ref string path)
|
|
{
|
|
string guess = path;
|
|
if (Directory.Exists(guess))
|
|
{
|
|
guess = Path.Combine(guess, "Source");
|
|
if (Directory.Exists(guess))
|
|
{
|
|
guess = Path.Combine(guess, "Examples");
|
|
if (Directory.Exists(guess))
|
|
{
|
|
// We are have found [opentk]/Source/Examples
|
|
path = guess;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
}
|