Changeset - f28757bb21cb
[Not reviewed]
default
0 9 8
Jason Maltzen (jmaltzen) - 9 years ago 2015-12-19 05:46:18
jason.maltzen@unsanctioned.net
Refactor recipe / reaction computation into a common class. Add some file utilities for supporting Mac bundles. Add some scripts for building Mac app bundles. Add a help window that shows the missing reactions.
17 files changed with 717 insertions and 281 deletions:
0 comments (0 inline, 0 general)
.hgignore
Show inline comments
 
syntax: glob
 
obj/
 
*.userprefs
 
*.orig
 
*.swp
 
*.zip
 
.DS_Store
 
mac/build/DesertPaintLab.app
DesertPaintLab.csproj
Show inline comments
 
<?xml version="1.0" encoding="utf-8"?>
 
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
 
  <PropertyGroup>
 
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
 
    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
 
    <ProjectGuid>{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}</ProjectGuid>
 
    <OutputType>WinExe</OutputType>
 
    <RootNamespace>DesertPaintLab</RootNamespace>
 
    <AssemblyName>DesertPaintLab</AssemblyName>
 
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
 
  </PropertyGroup>
 
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
 
    <DebugSymbols>true</DebugSymbols>
 
    <DebugType>full</DebugType>
 
    <Optimize>false</Optimize>
 
    <OutputPath>bin\Debug</OutputPath>
 
    <DefineConstants>DEBUG</DefineConstants>
 
    <ErrorReport>prompt</ErrorReport>
 
    <WarningLevel>4</WarningLevel>
 
    <PlatformTarget>x86</PlatformTarget>
 
    <ConsolePause>false</ConsolePause>
 
    <additionalargs>/unsafe</additionalargs>
 
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 
  </PropertyGroup>
 
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
 
    <DebugType>none</DebugType>
 
    <Optimize>false</Optimize>
 
    <OutputPath>bin\Release</OutputPath>
 
    <ErrorReport>prompt</ErrorReport>
 
    <WarningLevel>4</WarningLevel>
 
    <PlatformTarget>x86</PlatformTarget>
 
    <ConsolePause>false</ConsolePause>
 
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 
  </PropertyGroup>
 
  <ItemGroup>
 
    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="System" />
 
    <Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
  </ItemGroup>
 
  <ItemGroup>
 
    <EmbeddedResource Include="gtk-gui\gui.stetic">
 
      <LogicalName>gui.stetic</LogicalName>
 
    </EmbeddedResource>
 
  </ItemGroup>
 
  <ItemGroup>
 
    <Compile Include="gtk-gui\generated.cs" />
 
    <Compile Include="MainWindow.cs" />
 
    <Compile Include="Main.cs" />
 
    <Compile Include="AssemblyInfo.cs" />
 
    <Compile Include="PlayerProfile.cs" />
 
    <Compile Include="FirstRunDialog.cs" />
 
    <Compile Include="SelectProfileDialog.cs" />
 
    <Compile Include="NewProfileDialog.cs" />
 
    <Compile Include="PaintSwatch.cs" />
 
    <Compile Include="Palette.cs" />
 
    <Compile Include="PaintColor.cs" />
 
    <Compile Include="Reaction.cs" />
 
    <Compile Include="ReagentManager.cs" />
 
    <Compile Include="Reagent.cs" />
 
    <Compile Include="gtk-gui\MainWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.FirstRunDialog.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.SelectProfileDialog.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.NewProfileDialog.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.PaintSwatch.cs" />
 
    <Compile Include="SimulatorWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.SimulatorWindow.cs" />
 
    <Compile Include="ScreenCheckDialog.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.ScreenCheckDialog.cs" />
 
    <Compile Include="FileUtils.cs" />
 
    <Compile Include="PaintRecipe.cs" />
 
    <Compile Include="ReactionRecorder.cs" />
 
    <Compile Include="ReactionStatusWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.ReactionStatusWindow.cs" />
 
  </ItemGroup>
 
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 
  <ProjectExtensions>
 
    <MonoDevelop>
 
      <Properties>
 
        <Policies>
 
          <TextStylePolicy inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
 
          <CSharpFormattingPolicy IndentSwitchBody="True" IndentBlocksInsideExpressions="True" AnonymousMethodBraceStyle="NextLine" PropertyBraceStyle="NextLine" PropertyGetBraceStyle="NextLine" PropertySetBraceStyle="NextLine" EventBraceStyle="NextLine" EventAddBraceStyle="NextLine" EventRemoveBraceStyle="NextLine" StatementBraceStyle="NextLine" ElseNewLinePlacement="NewLine" CatchNewLinePlacement="NewLine" FinallyNewLinePlacement="NewLine" WhileNewLinePlacement="DoNotCare" ArrayInitializerWrapping="DoNotChange" ArrayInitializerBraceStyle="NextLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
 
        </Policies>
 
        <GtkDesignInfo generateGettext="False" />
 
      </Properties>
 
    </MonoDevelop>
 
  </ProjectExtensions>
 
</Project>
...
 
\ No newline at end of file
FileUtils.cs
Show inline comments
 
new file 100644
 
using System;
 

	
 
namespace DesertPaintLab
 
{
 
    public class FileUtils
 
    {
 
        public FileUtils()
 
        {
 
        }
 

	
 
        public static string FindApplicationResourceDirectory(string dirname)
 
        {
 
            string dirPath = System.IO.Path.Combine(
 
                    System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
 
                    dirname);
 
            if (System.IO.Directory.Exists(dirPath))
 
            {
 
                return dirPath;
 
            }
 
            // try "Resources" in case this is a Mac app bundle
 
            dirPath = System.IO.Path.Combine(
 
                Environment.GetFolderPath(Environment.SpecialFolder.Resources), dirname);
 
            if (System.IO.Directory.Exists(dirPath))
 
            {
 
                return dirPath;
 
            }
 
            dirPath = System.IO.Path.Combine(
 
                System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
 
                "Resources", 
 
                dirname);
 
            if (!System.IO.Directory.Exists(dirPath))
 
            {
 
                // not found
 
                dirPath = null;
 
            }
 
            return dirPath;
 
        }
 

	
 
        public static string FindApplicationResourceFile(string filename)
 
        {
 
            string filePath = System.IO.Path.Combine(
 
                    System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
 
                    filename);
 
            if (!System.IO.File.Exists(filePath))
 
            {
 
                // try "Resources" in case this is a Mac app bundle
 
                filePath = System.IO.Path.Combine(
 
                    Environment.GetFolderPath(Environment.SpecialFolder.Resources), filename);
 
            }
 
            if (!System.IO.File.Exists(filePath))
 
            {
 
                filePath = System.IO.Path.Combine(
 
                    System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
 
                    "Resources", 
 
                    filename);
 
            }
 
            if (!System.IO.File.Exists(filePath))
 
            {
 
                // not found
 
                filePath = null;
 
            }
 
            return filePath;
 
        }
 
    }
 
}
 

	
MainWindow.cs
Show inline comments
 
/*
 
 * Copyright (c) 2010, Tess Snider
 

	
 
 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.IO;
 
using System.Collections.Generic;
 
using System.Text.RegularExpressions;
 
using Gtk;
 
using DesertPaintLab;
 

	
 
public partial class MainWindow : Gtk.Window
 
{
 
	const int colorTolerance = 2;
 

	
 
	int swatchHeight = 24;
 
	int colorBarWidth = 306;
 
	int redBarSpacing = 32;
 
	int greenBarSpacing = 42;
 
	int blueBarSpacing = 52;
 

	
 

	
 
	bool unsavedData = false;
 
	bool shouldShutDown = false;
 
	string appDataPath;
 
	List<string> profileList = new List<string>();
 
	PlayerProfile profile = null;
 
	PaintColor expectedColor = new PaintColor();
 
	PaintColor reactedColor = new PaintColor();
 
	
 
	int screenWidth = 0;
 
	int screenHeight = 0;
 
	int pixelMultiplier = 1;
 

	
 
    bool enableDebugMenu = false;
 

	
 
	Gdk.Window rootWindow = null;
 
	Gdk.Pixbuf screenBuffer = null;
 

	
 
	Reagent reagent1 = null;
 
	Reagent reagent2 = null;
 
	Reagent reagent3 = null;
 

	
 
    Reagent[] reagents = new Reagent[3];
 
    PaintRecipe recipe = new PaintRecipe();
 
	
 
	public bool ShouldShutDown
 
	{
 
		get
 
		{
 
			return shouldShutDown;	
 
		}
 
	}
 
	
 
	
 
	public MainWindow () : base(Gtk.WindowType.Toplevel)
 
	{
 
		appDataPath = System.IO.Path.Combine(
 
			Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
 
		    "DesertPaintLab");
 
		
 
		if (!System.IO.Directory.Exists(appDataPath))
 
		{
 
			System.IO.Directory.CreateDirectory(appDataPath);	
 
		}
 
		
 
		DirectoryInfo di = new DirectoryInfo(appDataPath);
 
		DirectoryInfo[] dirs = di.GetDirectories();
 
		foreach (DirectoryInfo dir in dirs)
 
		{
 
			if (dir.Name != "template")
 
			{
 
				profileList.Add(dir.Name);
 
			}
 
		}
 

	
 
		Palette.Load(System.IO.Path.Combine(
 
				System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
 
		    	"colors.txt"));
 
        reagents[0] = null;
 
        reagents[1] = null;
 
        reagents[2] = null;
 

	
 
        string colorsPath = FileUtils.FindApplicationResourceFile("colors.txt");
 
		Palette.Load(colorsPath);
 
		
 
		Build();
 
		
 
		if (unmodifiedSwatch != null)
 
		{
 
			unmodifiedSwatch.Clear();
 
		}
 
		if (reactionSwatch != null)
 
		{
 
			reactionSwatch.Clear();
 
		}
 

	
 
		// get the root window
 
		rootWindow = Gdk.Global.DefaultRootWindow;
 

	
 
		// get its width and height
 
		rootWindow.GetSize(out screenWidth, out screenHeight);
 

	
 
        string settingsPath = System.IO.Path.Combine(appDataPath, "settings");
 
        if (System.IO.File.Exists(settingsPath))
 
        {
 
            LoadSettings(settingsPath);
 
        }
 

	
 
        this.DebugAction.Visible = enableDebugMenu;
 

	
 
		ScreenCheckDialog screenCheckDialog = new ScreenCheckDialog();
 
		screenCheckDialog.ScreenWidth = screenWidth;
 
		screenCheckDialog.ScreenHeight = screenHeight;
 
		screenCheckDialog.GamePixelWidth = pixelMultiplier;
 
		ResponseType resp = (ResponseType)screenCheckDialog.Run();
 
		screenWidth = screenCheckDialog.ScreenWidth;
 
		screenHeight = screenCheckDialog.ScreenHeight;
 
		pixelMultiplier = screenCheckDialog.GamePixelWidth;
 
		screenCheckDialog.Destroy();
 

	
 
        SaveSettings(settingsPath);
 

	
 
		screenBuffer = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, screenWidth, screenHeight);
 
	
 
		swatchHeight    *= pixelMultiplier;
 
		colorBarWidth   *= pixelMultiplier;
 
		redBarSpacing   *= pixelMultiplier;
 
		greenBarSpacing *= pixelMultiplier;
 
		blueBarSpacing  *= pixelMultiplier;
 

	
 
		if (!OpenProfile())
 
		{
 
			shouldShutDown = true;
 
		}
 
	}
 

	
 
    static Regex optionEntry = new Regex(@"(?<opt>[^#=][^=]*)=(?<optval>.*)$");
 
    void LoadSettings(string file)
 
    {
 
        string line;
 
        Match match;
 
        using (StreamReader reader = new StreamReader(file))
 
        {
 
            while ((line = reader.ReadLine()) != null) 
 
            {
 
                match = optionEntry.Match(line);
 
                if (match.Success)
 
                {
 
                    String optName = match.Groups["opt"].Value.ToLower();
 
                    String optVal = match.Groups["optval"].Value.Trim();
 
                    switch (optName)
 
                    {
 
                        case "screenwidth":
 
                            try {
 
                                int val = Int32.Parse(optVal);
 
                                if (val > 0)
 
                                {
 
                                    screenWidth = val;
 
                                }
 
                            } catch (FormatException) {
 
                                // ignore
 
                            }
 
                            break;
 
                        case "screenheight":
 
                            try {
 
                                int val = Int32.Parse(optVal);
 
                                if (val > 0)
 
                                {
 
                                    screenHeight = val;
 
                                }
 
                            } catch (FormatException) {
 
                                // ignore
 
                            }
 
                            break;
 
                        case "pixelmultiplier":
 
                            try {
 
                                int val = Int32.Parse(optVal);
 
                                if (val > 0)
 
                                {
 
                                    pixelMultiplier = val;
 
                                }
 
                            } catch (FormatException) {
 
                                // ignore
 
                            }
 
                            break;
 
                        case "debug":
 
                            try {
 
                                bool val = Boolean.Parse(optVal.ToLower());
 
                                enableDebugMenu = val;
 
                            } catch (FormatException) {
 
                                // ignore
 
                            }
 
                            break;
 
                        default:
 
                            // ignore
 
                            break;
 
                    }
 
                }
 
            }
 
        }
 
    }
 

	
 
    void SaveSettings(string file)
 
    {
 
        using (StreamWriter writer = new StreamWriter(file))
 
        {
 
            writer.WriteLine("screenwidth={0}", screenWidth);
 
            writer.WriteLine("screenheight={0}", screenHeight);
 
            writer.WriteLine("pixelmultiplier={0}", pixelMultiplier);
 
            writer.WriteLine("debug={0}", enableDebugMenu);
 
        }
 
    }
 

	
 
	bool ConfirmedExit()
 
	{
 
		if (unsavedData)
 
		{
 
			MessageDialog md = new MessageDialog(this, 
 
	            DialogFlags.DestroyWithParent,
 
	            MessageType.Warning, ButtonsType.OkCancel, 
 
	            "Your last reaction was unsaved." +
 
	            "Are you sure you want to quit?");
 
	   
 
			ResponseType resp = (ResponseType)md.Run();
 
			md.Destroy();
 
			return (resp == ResponseType.Ok);
 
		}
 
		return true;
 
	}
 
	
 
	void SetProfileName(string name)
 
	{	
 
		profile = new PlayerProfile(name,
 
		                System.IO.Path.Combine(appDataPath, name));
 
		
 
		statusBar.Push(0, name);
 
	}
 
	
 
	bool NewProfile()
 
	{
 
		bool newProfileCreated = false;
 
		bool duplicateName = false;
 
		NewProfileDialog newProfileDialog = new NewProfileDialog();
 
		ResponseType resp = (ResponseType)newProfileDialog.Run();
 
		if (resp == ResponseType.Ok)
 
		{
 
			// Make sure profile doesn't already exist.
 
			foreach (string profileName in profileList)
 
			{
 
				if (profileName == newProfileDialog.ProfileName)
 
				{
 
					MessageDialog md = new MessageDialog(this, 
 
	            		DialogFlags.DestroyWithParent,
 
	            		MessageType.Error, ButtonsType.Ok, 
 
	            		"That profile name already exists.");
 
					resp = (ResponseType)md.Run();
 
					md.Destroy();
 
					duplicateName = true;
 
					break;	
 
				}
 
			}
 
			
 
			if (!duplicateName)
 
			{
 
				// Set profile name.
 
				SetProfileName(newProfileDialog.ProfileName);
 
				
 
				profile.Initialize();
 
				
 
				newProfileCreated = true;
 
			}
 
		}
 
		newProfileDialog.Destroy();
 
		return newProfileCreated;
 
	}
 
	
 
	bool OpenProfile()
 
	{
 
		bool profileSelected = false;
 
		
 
		if (profileList.Count > 0)
 
		{
 
			SelectProfileDialog selectProfileDialog = new SelectProfileDialog();
 
			selectProfileDialog.ProfileList = profileList;
 
			ResponseType resp = (ResponseType)selectProfileDialog.Run();
 
			selectProfileDialog.Destroy();
 
			string selectedProfile = selectProfileDialog.SelectedProfile;
 
			if ((resp == ResponseType.Ok) && (selectedProfile.Length > 0)) 
 
				// Selected a profile.
 
			{
 
				SetProfileName(selectedProfile);
 
				profileSelected = true;
 
			}
 
			else if (resp == ResponseType.Accept) // New profile.
 
			{
 
				profileSelected = NewProfile();
 
			}
 
		}
 
		else
 
		{
 
			FirstRunDialog firstRunDialog = new FirstRunDialog();
 
			ResponseType resp = (ResponseType)firstRunDialog.Run();
 
			firstRunDialog.Destroy();
 
			if (resp == ResponseType.Ok) // New profile
 
			{
 
				profileSelected = NewProfile();
 
			}
 
			else if (resp == ResponseType.Accept) // Import
 
			{
 
				FileChooserDialog fileDialog =
 
					new FileChooserDialog("Select reactions.txt file.",
 
				    this, FileChooserAction.Open,
 
					Gtk.Stock.Cancel, ResponseType.Cancel,
 
		            Gtk.Stock.Open, ResponseType.Accept);
 
				resp = (ResponseType)fileDialog.Run();
 
				if (resp == ResponseType.Accept)
 
				{
 
					string fileName = fileDialog.Filename;
 
					string directory = fileDialog.CurrentFolder;
 
					if (fileName == "reactions.txt")
 
					{
 
						profileSelected = NewProfile();
 
						if (profileSelected)
 
						{
 
							profile.Import(directory);
 
						}
 
					}
 
				}
 
			}
 
		}
 
		
 
		if (profileSelected)
 
		{
 
			profile.Load();
 
			PopulateDropDowns();
 
		}
 

	
 
		return profileSelected;
 
	}
 
	
 
	void PopulateDropDowns()
 
	{
 
		ReagentManager.PopulateReagents(ref ingredient1ComboBox);
 
		ReagentManager.PopulateReagents(ref ingredient2ComboBox);
 
		ReagentManager.PopulateReagents(ref ingredient3ComboBox);
 
		
 
		ingredient2ComboBox.Sensitive = false;
 
		ingredient3ComboBox.Sensitive = false;
 
		
 
        Gtk.TreeIter iter;
 
        ingredient1ComboBox.Model.IterNthChild(out iter, 0);
 
        ingredient1ComboBox.SetActiveIter(iter);
 
		ingredient2ComboBox.Model.IterNthChild(out iter, 0);
 
        ingredient2ComboBox.SetActiveIter(iter);
 
		ingredient3ComboBox.Model.IterNthChild(out iter, 0);
 
        ingredient3ComboBox.SetActiveIter(iter);
 
	}
 
	
 
	protected void SetExpectedColor(byte red, byte green, byte blue)
 
	{
 
		expectedColor.Red = red;
 
		expectedColor.Green = green;
 
		expectedColor.Blue = blue;
 
		unmodifiedSwatch.Color = expectedColor;
 
	}
 
	
 
	protected void SetExpectedColor(PaintColor color)
 
	{
 
		SetExpectedColor(color.Red, color.Green, color.Blue);
 
	}
 
	
 
	protected void UpdateIngredients()
 
	{
 
		Reaction reaction1, reaction2;
 
		TreeIter selectIter;
 
		string reagentName;
 
		reagent1 = null;
 
		reagent2 = null;
 
		reagent3 = null;
 
		
 
		int expRedSum = 0;
 
		int expGreenSum = 0;
 
		int expBlueSum = 0;
 
		
 
		int reactRedSum = 0;
 
		int reactGreenSum = 0;
 
		int reactBlueSum = 0;
 
        reagents[0] = null;
 
        reagents[1] = null;
 
        reagents[2] = null;
 
		
 
		bool reactionKnown = true;
 
		
 
		int pigmentCount = 0;
 
		saveButton.Sensitive = false;
 
		
 
		saveButton.Sensitive = false;
 
        recipe.Clear();
 
		
 
		if (ingredient1ComboBox.GetActiveIter(out selectIter))
 
		{
 
			reagentName = (string)ingredient1ComboBox.Model.GetValue(selectIter, 0);
 
			if ((reagentName == null) || (reagentName.Length == 0))
 
			{
 
                // Nothing selected as reagent 1
 
				ingredient2ComboBox.Sensitive = false;
 
				ingredient3ComboBox.Sensitive = false;
 
				unmodifiedSwatch.Clear();
 
				captureButton.Sensitive = false;
 
			}
 
			else
 
			{
 
				reagent1 = ReagentManager.GetReagent(reagentName);
 
                recipe.AddReagent(reagentName);
 
                reagents[0] = ReagentManager.GetReagent(reagentName);
 
				ingredient2ComboBox.Sensitive = true;
 
				if (!reagent1.IsCatalyst)
 
				{
 
					expRedSum = reagent1.Color.Red;
 
					expGreenSum = reagent1.Color.Green;
 
					expBlueSum = reagent1.Color.Blue;
 
					pigmentCount = 1;
 
				}
 
				if (ingredient2ComboBox.GetActiveIter(out selectIter))
 
				{
 
					reagentName = (string)ingredient2ComboBox.Model.GetValue(selectIter, 0);
 
					if ((reagentName == null) || (reagentName.Length == 0))
 
					{
 
						ingredient3ComboBox.Sensitive = false;
 
						saveButton.Sensitive = false;
 
						reactionKnown = false;
 
					}
 
					else
 
					{
 
						reagent2 = ReagentManager.GetReagent(reagentName);
 
                        recipe.AddReagent(reagentName);
 
						reagents[1] = ReagentManager.GetReagent(reagentName);
 
						ingredient3ComboBox.Sensitive = true;
 
						captureButton.Sensitive = true;
 
						if (!reagent2.IsCatalyst)
 
						{
 
							expRedSum += reagent2.Color.Red;
 
							expGreenSum += reagent2.Color.Green;
 
							expBlueSum += reagent2.Color.Blue;
 
							pigmentCount++;
 
						}
 
						
 
						reaction1 = profile.FindReaction(reagent1, reagent2);
 
                        reaction1 = profile.FindReaction(reagents[0], reagents[1]);
 
						
 
						if (reaction1 != null)
 
                        if ((reaction1 != null) || (reagents[0] == reagents[1]))
 
						{
 
							ingredient3ComboBox.Sensitive = true;
 
							reactRedSum = reaction1.Red;
 
							reactGreenSum = reaction1.Green;
 
							reactBlueSum = reaction1.Blue;;	
 
						}
 
						else
 
						{
 
							reactionKnown = false;
 
							ingredient3ComboBox.Sensitive = false;
 
						}
 
						
 
						if (ingredient3ComboBox.GetActiveIter(out selectIter))
 
						{
 
							reagentName = (string)ingredient3ComboBox.Model.GetValue(selectIter, 0);
 
							if ((reagentName != null) && (reagentName.Length != 0))
 
							{
 
								reagent3 = ReagentManager.GetReagent(reagentName);
 
                                recipe.AddReagent(reagentName);
 
                                reagents[2] = ReagentManager.GetReagent(reagentName);
 
						
 
								if (!reactionKnown)
 
								{
 
									MessageDialog md = new MessageDialog(this, 
 
	            						DialogFlags.DestroyWithParent,
 
	            						MessageType.Error, ButtonsType.Ok, 
 
	            						"To do a three-ingredient reaction test, " +
 
									    "you must first recored the reaction of " +
 
									    "the first two ingredients.");
 
	   
 
									md.Run();
 
									md.Destroy();
 
									captureButton.Sensitive = false;
 
								}
 
								
 
								if (!reagent3.IsCatalyst)
 
								{
 
									expRedSum += reagent3.Color.Red;
 
									expGreenSum += reagent3.Color.Green;
 
									expBlueSum += reagent3.Color.Blue;
 
									pigmentCount++;
 
								}
 
								
 
								reaction1 = profile.FindReaction(reagent1, reagent3);
 
								reaction2 = profile.FindReaction(reagent2, reagent3);
 
                                reaction1 = profile.FindReaction(reagents[0], reagents[2]);
 
                                reaction2 = profile.FindReaction(reagents[1], reagents[2]);
 
								
 
								if (reactionKnown && (reaction1 == null) && (reaction2 == null))
 
								{
 
									MessageDialog md = new MessageDialog(this, 
 
	            						DialogFlags.DestroyWithParent,
 
	            						MessageType.Error, ButtonsType.Ok, 
 
	            						"To do a three-ingredient reaction test, " +
 
									    "you must first record the reaction of " +
 
									    "either the first or second ingredient " +
 
									    "with the third ingredient.");
 
	   
 
									md.Run();
 
									md.Destroy();	
 
									captureButton.Sensitive = false;
 
								}
 
								
 
								if (reaction1 != null)
 
								{
 
									reactRedSum += reaction1.Red;
 
									reactGreenSum += reaction1.Green;
 
									reactBlueSum += reaction1.Blue;
 
								}
 
								else
 
                                if ((reaction1 == null) && (reagents[0] != reagents[2]))
 
								{
 
									reactionKnown = false;	
 
								}
 

	
 
								if (reaction2 != null)
 
								{
 
									reactRedSum += reaction2.Red;
 
									reactGreenSum += reaction2.Green;
 
									reactBlueSum += reaction2.Blue;
 
								}
 
								else
 
                                if ((reaction2 == null) && (reagents[1] != reagents[2]))
 
								{
 
									reactionKnown = false;	
 
								}
 
							}
 
						}
 
					}
 
				}
 
				SetExpectedColor((byte)Math.Round((float)expRedSum / (float)pigmentCount),
 
					(byte)Math.Round((float)expGreenSum / (float)pigmentCount),
 
					(byte)Math.Round((float)expBlueSum / (float)pigmentCount));
 
                recipe.ComputeBaseColor(ref expectedColor);
 
                unmodifiedSwatch.Color = expectedColor;
 
                //SetExpectedColor(recipeColor.Red, recipeColor.Green, recipeColor.Blue);
 
				
 
				if (reactionKnown)
 
				{
 
					reactedColor.Red = (byte)Math.Min(255, Math.Max(0, expectedColor.Red + reactRedSum));
 
					reactedColor.Green = (byte)Math.Min(255, Math.Max(0, expectedColor.Green + reactGreenSum));
 
					reactedColor.Blue = (byte)Math.Min(255, Math.Max(0, expectedColor.Blue + reactBlueSum));
 
                    recipe.ComputeReactedColor(profile, ref reactedColor);
 
					reactionSwatch.Color = reactedColor;
 
				}
 
				else
 
				{
 
					reactionSwatch.Clear();	
 
				}
 
			}
 
		}
 
	}
 
	
 
	protected void OnDeleteEvent(object sender, DeleteEventArgs a)
 
	{
 
		if (ConfirmedExit())
 
		{
 
			a.RetVal = true;
 
			Application.Quit();
 
		}
 
		else
 
		{
 
			a.RetVal = false;
 
		}
 
	}
 
	
 
	bool IsPapyTexture(byte r, byte g, byte b)
 
	{
 
		return ((r > 0xD0) && (g > 0xC8) && (b > 0xA0)) &&
 
				((r < 0xF4) && (g < 0xE0) && (b < 0xC4));
 
	}
 
	
 
	unsafe bool CaptureReactionColor()
 
	{
 
		// Take a screenshot.
 
		byte r, g, b;
 
		int pixelStart, otherPixelStart;
 
		bool colorMatch;
 
		Gdk.Image rootImage = rootWindow.GetImage(0, 0, screenWidth, screenHeight);
 
		screenBuffer.GetFromImage(rootImage, rootImage.Colormap, 0, 0, 0, 0, screenWidth, screenHeight);
 
		//screenBuffer.GetFromDrawable(rootWindow,
 
		//	rootWindow.Colormap, 0, 0, 0, 0, screenWidth, screenHeight);
 
		int stride = screenBuffer.Rowstride;
 
		byte* pixBytes = (byte*)screenBuffer.Pixels;
 
	
 
		for (int x = 0; x < screenWidth - colorBarWidth; ++x)
 
		{
 
			for (int y = 0; y < (screenHeight - 53); ++y)
 
			{
 
				// Look for the color swatch.
 
				pixelStart = (y * stride) + (x * 3);
 
				r = pixBytes[pixelStart];
 
				g = pixBytes[pixelStart + 1];
 
				b = pixBytes[pixelStart + 2];
 
				
 
				// 1.) Check if this is a dark pixel.
 
				if ((r < 0x46) && (g < 0x46) && (b < 0x46))
 
				{
 
					// 2.) Check the pixel above it,
 
					// to see if it's from the papy texture.
 
					otherPixelStart = pixelStart - stride;
 
					if ((otherPixelStart >= 0) &&
 
					    IsPapyTexture(pixBytes[otherPixelStart++],
 
					                   pixBytes[otherPixelStart++],
 
					                   pixBytes[otherPixelStart]))
 
					{
 
						// 3.) Check the pixel below where the swatch should be,
 
						// to see if it's also from the papy texture.
 
						otherPixelStart = pixelStart + (stride * swatchHeight);
 
						if (IsPapyTexture(pixBytes[otherPixelStart++],
 
					                   pixBytes[otherPixelStart++],
 
					                   pixBytes[otherPixelStart]))
 
						{
 
							// pixBytes[pixelStart] = 0xFF;
 
							// pixBytes[pixelStart + 1] = 0x00;
 
							// pixBytes[pixelStart + 2] = 0xFF;
 
							
 
							// 4.) Scan the left border of the potential swatch
 
							// location.
 
							colorMatch = true;
 
							for (int i = 1; i < swatchHeight - 2; ++i)
 
							{
 
								otherPixelStart = pixelStart + (stride * i);
 
								if ((Math.Abs(r - pixBytes[otherPixelStart++]) > colorTolerance) ||
 
								    (Math.Abs(g - pixBytes[otherPixelStart++]) > colorTolerance) ||
 
								    (Math.Abs(b - pixBytes[otherPixelStart]) > colorTolerance))
 
								{
 
									colorMatch = false;
 
									break;
 
								}
 
							}
 
							
 
							if (colorMatch)
 
        bool wasCaptured = ReactionRecorder.CaptureReaction(pixBytes, screenWidth, screenHeight, stride, ref reactedColor);
 
        if (!wasCaptured && enableDebugMenu)
 
							{
 
								// WE FOUND THE SWATCH!
 
								// Now we know where the color bars are.
 
								otherPixelStart = pixelStart + (redBarSpacing * stride);
 
								int pixelCount = 0;
 
								while ((pixBytes[otherPixelStart] > 0x9F) &&
 
								       (pixBytes[otherPixelStart + 1] < 0x62) &&
 
								       (pixBytes[otherPixelStart + 2]  < 0x62))
 
								{
 
									pixelCount++;
 
									// pixBytes[otherPixelStart] = 0x00;
 
									// pixBytes[otherPixelStart + 1] = 0xFF;
 
									// pixBytes[otherPixelStart + 2] = 0xFF;
 
									otherPixelStart += 3;
 
								}
 
									
 
								reactedColor.Red = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth);
 
								otherPixelStart = pixelStart + (greenBarSpacing * stride);
 
								
 
								pixelCount = 0;
 
								while ((pixBytes[otherPixelStart] < 0x62) &&
 
								       (pixBytes[otherPixelStart + 1] > 0x9F) &&
 
								       (pixBytes[otherPixelStart + 2] < 0x62))
 
            // write out the screenshot
 
            string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
 
            string filename;
 
            int i = 0;
 
            do
 
								{
 
									pixelCount++;
 
									// pixBytes[otherPixelStart] = 0x00;
 
									// pixBytes[otherPixelStart + 1] = 0xFF;
 
									// pixBytes[otherPixelStart + 2] = 0xFF;
 
									otherPixelStart += 3;
 
								}
 

	
 
								reactedColor.Green = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth);
 
								otherPixelStart = pixelStart + (blueBarSpacing * stride);
 
								
 
								pixelCount = 0;
 
								while ((pixBytes[otherPixelStart] < 0x62) &&
 
									(pixBytes[otherPixelStart + 1] < 0x62) &&
 
									(pixBytes[otherPixelStart + 2] > 0x9F))
 
								{
 
									pixelCount++;
 
									// pixBytes[otherPixelStart] = 0x00;
 
									// pixBytes[otherPixelStart + 1] = 0xFF;
 
									// pixBytes[otherPixelStart + 2] = 0xFF;
 
									otherPixelStart += 3;
 
								}
 

	
 
								reactedColor.Blue = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth);
 
								
 
								// write out the screenshot
 
								//screenBuffer.Save("screenshot.png", "png");
 
								return true;
 
							}
 
						}
 
					}
 
				}
 
			}
 
                ++i;
 
                filename = System.IO.Path.Combine(screenshotDir, String.Format("DesertPaintLab_Colormatch{0}.png", i));
 
            } while (System.IO.File.Exists(filename));
 
            screenBuffer.Save(filename, "png");
 
		}
 
        //screenBuffer.Save("screenshot.png", "png");
 
		
 
		return false;
 
		
 
		return !wasCaptured;
 
	}
 
	
 
    protected virtual void OnDebugScreenshot(object sender, System.EventArgs e)
 
    {
 
        Gdk.Image rootImage = rootWindow.GetImage(0, 0, screenWidth, screenHeight);
 
        screenBuffer.GetFromImage(rootImage, rootImage.Colormap, 0, 0, 0, 0, screenWidth, screenHeight);
 
        string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
 
        string filename;
 
        int i = 0;
 
        do
 
        {
 
            ++i;
 
            filename = System.IO.Path.Combine(screenshotDir, String.Format("DesertPaintLab_{0}.png", i));
 
        } while (System.IO.File.Exists(filename));
 
        screenBuffer.Save(filename, "png");
 
    }
 

	
 
	protected virtual void OnCaptureButton(object sender, System.EventArgs e)
 
	{
 
		if (CaptureReactionColor())
 
		{
 
			string warning = "";
 
			if (reactedColor.Red == 0)
 
			{
 
				warning = warning + "\nRed is too low.";
 
			}
 
			if (reactedColor.Green == 0)
 
			{
 
				warning = warning + "\nGreen is too low.";	
 
			}
 
			if (reactedColor.Blue == 0)
 
			{
 
				warning = warning + "\nBlue is too low.";	
 
			}
 
			if (reactedColor.Red == 255)
 
			{
 
				warning = warning + "\nRed is too high.";
 
			}
 
			if (reactedColor.Green == 255)
 
			{
 
				warning = warning + "\nGreen is too high.";	
 
			}
 
			if (reactedColor.Blue == 255)
 
			{
 
				warning = warning + "\nBlue is too high.";	
 
			}
 
			
 
			if (warning.Length != 0)
 
			{
 
				MessageDialog md = new MessageDialog(this, 
 
	            DialogFlags.DestroyWithParent,
 
	            MessageType.Error, ButtonsType.Ok, 
 
	            "Reaction clipped.  You will need to do a " +
 
	            "3-way reaction to test this pair.  Details: " +
 
	            warning);
 
	   
 
				md.Run();
 
				md.Destroy();
 
			}
 
			else
 
			{
 
				this.reactionSwatch.Color = reactedColor;
 
				saveButton.Sensitive = true;
 
			}
 
		}
 
		else
 
		{
 
			MessageDialog md = new MessageDialog(this, 
 
	            DialogFlags.DestroyWithParent,
 
	            MessageType.Error, ButtonsType.Ok, 
 
	            "Pigment Lab dialog box NOT FOUND.  Please ensure " +
 
	            "that there is an unobstructed view of the dialog " +
 
	            "when you press the Capture button.");
 
	   
 
			md.Run();
 
			md.Destroy();	
 
		}
 
	}
 
	
 
	protected virtual void OnSaveButton(object sender, System.EventArgs e)
 
	{
 
		int r, g, b;
 
		if (reagent3 != null)
 
		{
 
			// A 3-reagent reaction.
 
			Reaction reaction1 = profile.FindReaction(reagent1, reagent2);
 
			Reaction reaction2 = profile.FindReaction(reagent1, reagent3);
 
			Reaction reaction3 = profile.FindReaction(reagent2, reagent3);
 
			
 
			r = reactedColor.Red - expectedColor.Red;
 
			g = reactedColor.Green - expectedColor.Green;
 
			b = reactedColor.Blue - expectedColor.Blue;
 
			
 
			if (reaction2 == null)
 
        if (ReactionRecorder.RecordReaction(profile, expectedColor, reactedColor, reagents))
 
			{
 
				r = r - reaction1.Red - reaction3.Red;
 
				g = g - reaction1.Green - reaction3.Green;
 
				b = b - reaction1.Blue - reaction3.Blue;
 
				profile.SetReaction(reagent1, reagent3, new Reaction(r, g, b));
 
				profile.Save();
 
				saveButton.Sensitive = false;
 
			}
 
			else if (reaction3 == null)
 
			{
 
				r = r - reaction1.Red - reaction2.Red;
 
				g = g - reaction1.Green - reaction2.Green;
 
				b = b - reaction1.Blue - reaction2.Blue;
 
				profile.SetReaction(reagent2, reagent3, new Reaction(r, g, b));
 
				profile.Save();
 
				saveButton.Sensitive = false;
 
			}	
 
		}
 
		else if ((reagent1 != null) && (reagent2 != null))
 
		{
 
			// A 2-reagent reaction.
 
			r = reactedColor.Red - expectedColor.Red;
 
			g = reactedColor.Green - expectedColor.Green;
 
			b = reactedColor.Blue - expectedColor.Blue;
 
			profile.SetReaction(reagent1, reagent2, new Reaction(r, g, b));
 
			profile.Save();
 
			saveButton.Sensitive = false;
 
		}
 
	}
 
	
 
	protected virtual void OnChangedIngredient1(object sender, System.EventArgs e)
 
	{
 
		UpdateIngredients();
 
	}
 
	
 
	protected virtual void OnChangedIngredient2(object sender, System.EventArgs e)
 
	{
 
		UpdateIngredients();
 
	}
 
	
 
	protected virtual void OnChangedIngredient3(object sender, System.EventArgs e)
 
	{
 
		UpdateIngredients();
 

	
 
	}
 
	
 
	protected virtual void OnNewProfile(object sender, System.EventArgs e)
 
	{
 
		if (unsavedData)
 
		{
 
			MessageDialog md = new MessageDialog(this, 
 
	            DialogFlags.DestroyWithParent,
 
	            MessageType.Warning, ButtonsType.OkCancel, 
 
	            "Your last reaction was unsaved." +
 
	            "Are you sure you want to lose your changes?");
 
	   
 
			ResponseType resp = (ResponseType)md.Run();
 
			md.Destroy();
 
			if (resp != ResponseType.Ok)
 
			{
 
				return;	
 
			}
 
		}
 
		
 
		if (NewProfile())
 
		{
 
			profile.Load();
 
			PopulateDropDowns();
 
		}
 
	}
 
	
 
	protected virtual void OnOpenProfile(object sender, System.EventArgs e)
 
	{
 
		bool profileSelected = false;
 
		SelectProfileDialog selectProfileDialog = new SelectProfileDialog();
 
		selectProfileDialog.ProfileList = profileList;
 
		ResponseType resp = (ResponseType)selectProfileDialog.Run();
 
		selectProfileDialog.Destroy();
 
		if (resp == ResponseType.Ok) // Selected a profile.
 
		{
 
			SetProfileName(selectProfileDialog.SelectedProfile);
 
			profileSelected = true;
 
		}
 
		else if (resp == ResponseType.Accept) // New profile.
 
		{
 
			profileSelected = NewProfile();
 
		}
 
		if (profileSelected)
 
		{
 
			profile.Load();
 
			PopulateDropDowns();
 
		}
 
	}
 
	
 
	protected virtual void OnAbout(object sender, System.EventArgs e)
 
	{
 
		AboutDialog aboutDialog = new AboutDialog();
 

	
 
        aboutDialog.ProgramName = "Desert Paint Lab";
 
        aboutDialog.Version = "0.0.1";
 
        aboutDialog.Comments = "Desert Paint Lab paint reaction recorder for A Tale in the Desert";
 
        aboutDialog.Authors = new string [] {"Tess Snider", "Jason Maltzen"};
 
        //aboutDialog.Website = "http://www.google.com/";
 
		aboutDialog.Run();
 
		aboutDialog.Destroy();
 
	}
 
	
 
	protected virtual void OnMenuExit (object sender, System.EventArgs e)
 
	{
 
		if (ConfirmedExit())
 
		{
 
			Application.Quit();
 
		}
 
	}
 
	
 
	protected virtual void OnExport(object sender, System.EventArgs e)
 
	{
 
		FileChooserDialog fileDialog =
 
			new FileChooserDialog("Select destination file.",
 
				    this, FileChooserAction.Save,
 
			        Gtk.Stock.Cancel, ResponseType.Cancel,
 
		            Gtk.Stock.Save, ResponseType.Accept);
 
		ResponseType resp = (ResponseType)fileDialog.Run();
 
		if (resp == ResponseType.Accept)
 
		{
 
			string fileName = fileDialog.Filename;
 
			string directory = fileDialog.CurrentFolder;
 
			profile.Export(System.IO.Path.Combine(directory, fileName));
 
		}
 
		fileDialog.Destroy();
 
	}
 
	
 
	protected virtual void RunSimulator(object sender, System.EventArgs e)
 
	{
 
		SimulatorWindow win = new SimulatorWindow(profile);
 
		win.Show();
 
	}
 
	
 
	
 
	
 
	
 
    protected void OnOpenRecipeGenerator(object sender, EventArgs e)
 
    {
 
        throw new NotImplementedException();
 
}
 

	
 
    protected void OnShowReactionStatus(object sender, EventArgs e)
 
    {
 
        ReactionStatusWindow win = new ReactionStatusWindow(profile);
 
        win.Show();
 
    }
 
}
 

	
PaintRecipe.cs
Show inline comments
 
new file 100644
 
using System;
 
using System.Collections.Generic;
 

	
 
namespace DesertPaintLab
 
{
 
    public class PaintRecipe
 
    {
 
        public struct RGB
 
        {
 
            public int r;
 
            public int g;
 
            public int b;
 
        };
 

	
 
        private List<string> reagents = new List<string>();
 

	
 
        public PaintRecipe()
 
        {
 
        }
 

	
 
        public void AddReagent(String reagentName)
 
        {
 
            reagents.Add(reagentName);
 
        }
 

	
 
        public void Clear()
 
        {
 
            reagents.Clear();
 
        }
 

	
 
        byte CalculateColor(int baseSum, int pigmentCount, int reactSum)
 
        {
 
            return (byte)Math.Max(Math.Min(Math.Round((((float)baseSum / (float)pigmentCount) + (float)reactSum)), 255), 0);
 
        }
 

	
 
        // Compute the color including reactions based on the player's profile
 
        public void ComputeReactedColor(PlayerProfile profile, ref PaintColor paintColor)
 
        {
 
            RGB baseColor;
 
            baseColor.r = 0;
 
            baseColor.g = 0;
 
            baseColor.b = 0;
 
            RGB reactionColor;
 
            reactionColor.r = 0;
 
            reactionColor.g = 0;
 
            reactionColor.b = 0;
 

	
 
            int pigmentCount = 0;
 
            string prevReagent = null;
 

	
 
            // track visited reagents so the reaction is only applied once
 
            SortedDictionary<string,bool> reagentSet = new SortedDictionary<string,bool>();
 
            List<Reagent> prevReagents = new List<Reagent>();
 

	
 
            foreach (string reagentName in reagents)
 
            {
 
                if (reagentName == null)
 
                {
 
                    continue;   
 
                }
 
                
 
                Reagent reagent = ReagentManager.GetReagent(reagentName);
 
                if (!reagent.IsCatalyst)
 
                {
 
                    baseColor.r += reagent.Color.Red;
 
                    baseColor.g += reagent.Color.Green;
 
                    baseColor.b += reagent.Color.Blue;
 
                    pigmentCount += 1;
 
                }
 
                if (prevReagent == null || !prevReagent.Equals(reagentName))
 
                {
 
                    if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
 
                    {
 
                        reagentSet[reagentName] = true;
 
                        // Run reactions.
 
                        foreach (Reagent otherReagent in prevReagents)
 
                        {
 
                            Reaction reaction = profile.FindReaction(otherReagent, reagent);
 
                            if (reaction != null)
 
                            {
 
                                reactionColor.r += reaction.Red;
 
                                reactionColor.g += reaction.Green;
 
                                reactionColor.b += reaction.Blue;
 
                            }
 
                        }
 
                        prevReagents.Add(reagent);
 
                    }
 
                }
 
                prevReagent = reagentName;
 
            }
 
            paintColor.Red = CalculateColor(baseColor.r, pigmentCount, reactionColor.r);
 
            paintColor.Green = CalculateColor(baseColor.g, pigmentCount, reactionColor.g);
 
            paintColor.Blue = CalculateColor(baseColor.b, pigmentCount, reactionColor.b);
 
        }
 

	
 
        // Compute the base color without any reactions
 
        public void ComputeBaseColor(ref PaintColor color)
 
        {
 
            RGB baseColor;
 
            baseColor.r = 0;
 
            baseColor.g = 0;
 
            baseColor.b = 0;
 
            int pigmentCount = 0;
 
            foreach (string reagentName in reagents)
 
            {
 
                if (reagentName == null)
 
                {
 
                    continue;   
 
                }
 
                
 
                Reagent reagent = ReagentManager.GetReagent(reagentName);
 
                if (!reagent.IsCatalyst)
 
                {
 
                    baseColor.r += reagent.Color.Red;
 
                    baseColor.g += reagent.Color.Green;
 
                    baseColor.b += reagent.Color.Blue;
 
                    pigmentCount += 1;
 
                }
 
            }
 
            color.Red = CalculateColor(baseColor.r, pigmentCount, 0);
 
            color.Green = CalculateColor(baseColor.g, pigmentCount, 0);
 
            color.Blue = CalculateColor(baseColor.b, pigmentCount, 0);
 
        }
 
    }
 
}
 

	
PlayerProfile.cs
Show inline comments
 
/*
 
 * Copyright (c) 2010, Tess Snider
 

 
 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.IO;
 
using System.Collections.Generic;
 

 
namespace DesertPaintLab
 
{
 
	public class PlayerProfile
 
	{
 
		string name;
 
		string directory;
 
		string reactFile;
 
		
 
		SortedDictionary<string, SortedDictionary<string, Reaction>> reactions =
 
			new SortedDictionary<string, SortedDictionary<string, Reaction>>();
 
		
 
		public PlayerProfile(string name, string directory)
 
		{
 
			this.name = name;
 
			this.directory = directory;
 
			this.reactFile = System.IO.Path.Combine(directory, "dp_reactions.txt");
 
		}
 
		
 
		public void Initialize()
 
		{
 
			// Create new directory.
 
			System.IO.Directory.CreateDirectory(directory);
 
				
 
			// Copy template files into new directory.
 
			string templatePath = System.IO.Path.Combine(
 
				System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
 
		    	"template");
 
			string templatePath = FileUtils.FindApplicationResourceDirectory("template");
 
			
 
            if (!System.IO.Directory.Exists(templatePath))
 
            {
 
            }
 
			
 
			DirectoryInfo di = new DirectoryInfo(templatePath);
 
			FileInfo[] templateFiles = di.GetFiles();
 

 
			foreach (FileInfo file in templateFiles)
 
			{
 
				System.IO.File.Copy(file.FullName,
 
					System.IO.Path.Combine(directory, file.Name), true);					
 
			}
 
		}
 
		
 
		public void ConvertFromPP(string ppFile, string dpFile)
 
		{
 
			string line;
 
			using (StreamReader reader = new StreamReader(ppFile))
 
			{
 
				using (StreamWriter writer = new StreamWriter(dpFile))
 
				{
 
					while ((line = reader.ReadLine()) != null) 
 
                	{
 
						string[] tokens = line.Split(null);
 
						if ((tokens.Length > 0) && (tokens[0] != "//"))
 
						{							
 
							// Write reaction.
 
							writer.Write(tokens[0] + " " + tokens[2] + " ");
 
							switch (tokens[4])
 
							{
 
							case "W":
 
								writer.WriteLine(tokens[6] + " " + tokens[6] + " " + tokens[6]);
 
								break;
 
							case "R":
 
								writer.WriteLine(tokens[6] + " 0 0");
 
								break;
 
							case "G":
 
								writer.WriteLine("0 " + tokens[6] + " 0");
 
								break;
 
							case "B":
 
								writer.WriteLine("0 0 " + tokens[6]);
 
								break;	
 
							}
 
							
 
							// Write reverse reaction.
 
							writer.Write(tokens[2] + " " + tokens[0] + " ");
 
							switch (tokens[4])
 
							{
 
							case "W":
 
								writer.WriteLine(tokens[8] + " " + tokens[8] + " " + tokens[8]);
 
								break;
 
							case "R":
 
								writer.WriteLine(tokens[8] + " 0 0");
 
								break;
 
							case "G":
 
								writer.WriteLine("0 " + tokens[8] + " 0");
 
								break;
 
							case "B":
 
								writer.WriteLine("0 0 " + tokens[8]);
 
								break;	
 
							}							
 
						}
 
					}
 
				}
 
			}
 
		}
 
		
 
		public bool SaveToPP(string ppFile)
 
		{
 
			Reaction reaction1, reaction2;
 
			SortedDictionary<string, Reaction> secondReagentDict;
 
			using (StreamWriter writer = new StreamWriter(ppFile))
 
			{
 
				foreach (KeyValuePair<string, SortedDictionary<string, Reaction>> firstPair in reactions)
 
				{
 
					foreach (KeyValuePair<string, Reaction> secondPair in firstPair.Value)
 
					{
 
						reaction1 = secondPair.Value;
 
						if ((reaction1 != null) && !reaction1.Exported)
 
						{
 
							reaction2 = null;
 
							reactions.TryGetValue(secondPair.Key, out secondReagentDict);
 
							if (secondReagentDict != null)
 
							{
 
								secondReagentDict.TryGetValue(firstPair.Key, out reaction2);
 
							}
 
							if (reaction2 != null)
 
							{
 
								writer.Write(firstPair.Key + " | " + secondPair.Key + " | ");
 
								if ((Math.Abs(reaction1.Red) > Math.Abs(reaction1.Green)) ||
 
								    (Math.Abs(reaction2.Red) > Math.Abs(reaction2.Green)))
 
								{
 
									writer.WriteLine("R | " + reaction1.Red + " | " + reaction2.Red);	
 
								}
 
								else if ((Math.Abs(reaction1.Green) > Math.Abs(reaction1.Red)) ||
 
									(Math.Abs(reaction2.Green) > Math.Abs(reaction2.Red)))
 
								{
 
									writer.WriteLine("G | " + reaction1.Green + " | " + reaction2.Green);	
 
								}
 
								else if ((Math.Abs(reaction1.Blue) > Math.Abs(reaction1.Red)) ||
 
									(Math.Abs(reaction2.Blue) > Math.Abs(reaction2.Red)))
 
								{
 
									writer.WriteLine("B | " + reaction1.Blue + " | " + reaction2.Blue);
 
								}
 
								else
 
								{
 
									writer.WriteLine("W | " + reaction1.Red + " | " + reaction2.Red);	
 
								}
 
								reaction1.Exported = true;
 
								reaction2.Exported = true;
 
							}
 
						}
 
					}
 
				}
 
			}
 
			
 
			// Clear Exported flags.
 
			foreach (KeyValuePair<string, SortedDictionary<string, Reaction>> firstPair in reactions)
 
			{
 
				foreach (KeyValuePair<string, Reaction> secondPair in firstPair.Value)
 
				{
 
					if (secondPair.Value != null)
 
					{
 
						secondPair.Value.Exported = false;
 
					}
 
				}
 
			}
 
			return true;
 
		}
 
		
 
		public void Import(string importDir)
 
		{
 
			// Convert old file.
 
			ConvertFromPP(
 
			    System.IO.Path.Combine(importDir, "reactions.txt"),
 
				reactFile);
 
			try
 
			{
 
				// If there is an ingredients file, move it in.
 
				System.IO.File.Copy(
 
				    System.IO.Path.Combine(importDir, "ingredients.txt"),
 
				    System.IO.Path.Combine(directory, "ingredients.txt"),
 
				    true);
 
			}
 
			catch (Exception)
 
			{
 
				// If there is no ingredients file, we don't really care.	
 
			}
 
		}
 
		
 
		public void Export(string file)
 
		{
 
			SaveToPP(file);
 
		}
 
		
 
		public void Load()
 
		{
 
			string line;
 
			SortedDictionary<string, Reaction> dict;
 
			reactions.Clear();
 
			ReagentManager.Load(System.IO.Path.Combine(directory, "ingredients.txt"));
 
			ReagentManager.InitializeReactions(ref reactions);
 
			using (StreamReader reader = new StreamReader(reactFile))
 
			{
 
				while ((line = reader.ReadLine()) != null) 
 
               	{
 
					string[] tokens = line.Split(null);
 
					reactions.TryGetValue(tokens[0], out dict);
 
					dict[tokens[1]] = new Reaction(int.Parse(tokens[2]), int.Parse(tokens[3]), int.Parse(tokens[4]));
 
				}
 
			}
 
		}
 
		
 
		public void Save()
 
		{
 
			Reaction reaction;
 
			using (StreamWriter writer = new StreamWriter(reactFile, false))
 
			{
 
				foreach (KeyValuePair<string, SortedDictionary<string, Reaction>> firstPair in reactions)
 
				{
 
					foreach (KeyValuePair<string, Reaction> secondPair in firstPair.Value)
 
					{
 
						reaction = secondPair.Value;
 
						
 
						if (reaction != null)
 
						{
 
							writer.WriteLine(firstPair.Key + " " + secondPair.Key + " " +
 
								reaction.Red + " " + reaction.Green + " " + reaction.Blue);
 
						}
 
					}
 
				}
 
			}
 
		}
 
		
 
		public Reaction FindReaction(Reagent reagent1, Reagent reagent2)
 
		{	
 
			SortedDictionary<string, Reaction> secondReagentDict = null;
 
			Reaction reaction = null;
 
			reactions.TryGetValue(reagent1.Name, out secondReagentDict);
 
			if (secondReagentDict != null)
 
			{
 
				secondReagentDict.TryGetValue(reagent2.Name, out reaction);
 
			}
 
			return reaction;
 
		}
 
		
 
		public void SetReaction(Reagent reagent1, Reagent reagent2, Reaction reaction)
 
		{
 
			SortedDictionary<string, Reaction> secondReagentDict = null;
 
			reactions.TryGetValue(reagent1.Name, out secondReagentDict);
 
			if (secondReagentDict != null)
 
			{
 
				secondReagentDict[reagent2.Name] = reaction;
 
			}
 
		}
 
	}
 
}
 

ReactionRecorder.cs
Show inline comments
 
new file 100644
 
using System;
 

	
 
namespace DesertPaintLab
 
{
 
    // ReactionRecorder - business logic for recording paint reactions
 
    public class ReactionRecorder
 
    {
 
        const int colorTolerance = 2;
 

	
 
        const int swatchHeight = 24;
 
        const int colorBarWidth = 306;
 
        const int redBarSpacing = 32;
 
        const int greenBarSpacing = 42;
 
        const int blueBarSpacing = 52;
 

	
 
        private static bool IsPapyTexture(byte r, byte g, byte b)
 
        {
 
            return ((r > 0xD0) && (g > 0xC8) && (b > 0xA0)) &&
 
                    ((r < 0xF4) && (g < 0xE0) && (b < 0xC4));
 
        }
 

	
 
        unsafe public static bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor)
 
        {
 
            byte r, g, b;
 
            int pixelStart, otherPixelStart;
 
            bool colorMatch;
 
            for (int x = 0; x < screenshotWidth - colorBarWidth; ++x)
 
            {
 
                for (int y = 0; y < (screenshotHeight - 53); ++y)
 
                {
 
                    // Look for the color swatch.
 
                    pixelStart = (y * stride) + (x * 3);
 
                    r = pixBytes[pixelStart];
 
                    g = pixBytes[pixelStart + 1];
 
                    b = pixBytes[pixelStart + 2];
 
                    
 
                    // 1.) Check if this is a dark pixel.
 
                    if ((r < 0x46) && (g < 0x46) && (b < 0x46))
 
                    {
 
                        // 2.) Check the pixel above it,
 
                        // to see if it's from the papy texture.
 
                        otherPixelStart = pixelStart - stride;
 
                        if ((otherPixelStart >= 0) &&
 
                            IsPapyTexture(pixBytes[otherPixelStart++],
 
                                           pixBytes[otherPixelStart++],
 
                                           pixBytes[otherPixelStart]))
 
                        {
 
                            // 3.) Check the pixel below where the swatch should be,
 
                            // to see if it's also from the papy texture.
 
                            otherPixelStart = pixelStart + (stride * swatchHeight);
 
                            if (IsPapyTexture(pixBytes[otherPixelStart++],
 
                                           pixBytes[otherPixelStart++],
 
                                           pixBytes[otherPixelStart]))
 
                            {
 
                                // pixBytes[pixelStart] = 0xFF;
 
                                // pixBytes[pixelStart + 1] = 0x00;
 
                                // pixBytes[pixelStart + 2] = 0xFF;
 
                                
 
                                // 4.) Scan the left border of the potential swatch
 
                                // location.
 
                                colorMatch = true;
 
                                for (int i = 1; i < swatchHeight - 2; ++i)
 
                                {
 
                                    otherPixelStart = pixelStart + (stride * i);
 
                                    if ((Math.Abs(r - pixBytes[otherPixelStart++]) > colorTolerance) ||
 
                                        (Math.Abs(g - pixBytes[otherPixelStart++]) > colorTolerance) ||
 
                                        (Math.Abs(b - pixBytes[otherPixelStart]) > colorTolerance))
 
                                    {
 
                                        colorMatch = false;
 
                                        break;
 
                                    }
 
                                }
 
                                
 
                                if (colorMatch)
 
                                {
 
                                    // WE FOUND THE SWATCH!
 
                                    // Now we know where the color bars are.
 
                                    int redPixelStart = pixelStart + (redBarSpacing * stride);
 
                                    int redPixelCount = 0;
 
                                    while ((pixBytes[redPixelStart] > 0x9F) &&
 
                                           (pixBytes[redPixelStart + 1] < 0x62) &&
 
                                           (pixBytes[redPixelStart + 2]  < 0x62))
 
                                    {
 
                                        redPixelCount++;
 
                                        // pixBytes[redPixelStart] = 0x00;
 
                                        // pixBytes[redPixelStart + 1] = 0xFF;
 
                                        // pixBytes[redPixelStart + 2] = 0xFF;
 
                                        redPixelStart += 3;
 
                                    }
 
                                        
 
                                    reactedColor.Red = (byte)Math.Round((float)redPixelCount * 255f / (float)colorBarWidth);
 
                                    int greenPixelStart = pixelStart + (greenBarSpacing * stride);
 
                                    
 
                                    int greenPixelCount = 0;
 
                                    while ((pixBytes[greenPixelStart] < 0x62) &&
 
                                           (pixBytes[greenPixelStart + 1] > 0x9F) &&
 
                                           (pixBytes[greenPixelStart + 2] < 0x62))
 
                                    {
 
                                        greenPixelCount++;
 
                                        // pixBytes[greenPixelStart] = 0x00;
 
                                        // pixBytes[greenPixelStart + 1] = 0xFF;
 
                                        // pixBytes[greenPixelStart + 2] = 0xFF;
 
                                        greenPixelStart += 3;
 
                                    }
 
    
 
                                    reactedColor.Green = (byte)Math.Round((float)greenPixelCount * 255f / (float)colorBarWidth);
 
                                    int bluePixelStart = pixelStart + (blueBarSpacing * stride);
 
                                    
 
                                    int bluePixelCount = 0;
 
                                    while ((pixBytes[bluePixelStart] < 0x62) &&
 
                                        (pixBytes[bluePixelStart + 1] < 0x62) &&
 
                                        (pixBytes[bluePixelStart + 2] > 0x9F))
 
                                    {
 
                                        bluePixelCount++;
 
                                        // pixBytes[bluePixelStart] = 0x00;
 
                                        // pixBytes[bluePixelStart + 1] = 0xFF;
 
                                        // pixBytes[bluePixelStart + 2] = 0xFF;
 
                                        bluePixelStart += 3;
 
                                    }
 
    
 
                                    reactedColor.Blue = (byte)Math.Round((float)bluePixelCount * 255f / (float)colorBarWidth);
 
                                    return true;
 
                                }
 
                            }
 
                        }
 
                    }
 
                }
 
            }
 
            return false;
 
        }
 

	
 
        public static bool RecordReaction(PlayerProfile profile, PaintColor expectedColor, PaintColor reactedColor, Reagent[] reagents)
 
        {
 
            bool saved = false;
 
            int r, g, b;
 
            if (reagents[2] != null)
 
            {
 
                // A 3-reagent reaction.
 
                Reaction reaction1 = profile.FindReaction(reagents[0], reagents[1]);
 
                Reaction reaction2 = profile.FindReaction(reagents[0], reagents[2]);
 
                Reaction reaction3 = profile.FindReaction(reagents[1], reagents[2]);
 
                
 
                r = reactedColor.Red - expectedColor.Red;
 
                g = reactedColor.Green - expectedColor.Green;
 
                b = reactedColor.Blue - expectedColor.Blue;
 
                
 
                if (reaction2 == null)
 
                {
 
                    r = r - reaction1.Red - reaction3.Red;
 
                    g = g - reaction1.Green - reaction3.Green;
 
                    b = b - reaction1.Blue - reaction3.Blue;
 
                    profile.SetReaction(reagents[0], reagents[2], new Reaction(r, g, b));
 
                    profile.Save();
 
                    saved = true;
 
                }
 
                else if (reaction3 == null)
 
                {
 
                    r = r - reaction1.Red - reaction2.Red;
 
                    g = g - reaction1.Green - reaction2.Green;
 
                    b = b - reaction1.Blue - reaction2.Blue;
 
                    profile.SetReaction(reagents[1], reagents[2], new Reaction(r, g, b));
 
                    profile.Save();
 
                    saved = true;
 
                }   
 
            }
 
            else if ((reagents[0] != null) && (reagents[1] != null))
 
            {
 
                // A 2-reagent reaction.
 
                r = reactedColor.Red - expectedColor.Red;
 
                g = reactedColor.Green - expectedColor.Green;
 
                b = reactedColor.Blue - expectedColor.Blue;
 
                profile.SetReaction(reagents[0], reagents[1], new Reaction(r, g, b));
 
                profile.Save();
 
                saved = true;
 
            }
 
            return saved;
 
        }
 
    }
 
}
 

	
ReactionStatusWindow.cs
Show inline comments
 
new file 100644
 
using System;
 

	
 
namespace DesertPaintLab
 
{
 
    public partial class ReactionStatusWindow : Gtk.Window
 
    {
 
        //PlayerProfile profile;
 

	
 
        public ReactionStatusWindow(PlayerProfile profile) : base(Gtk.WindowType.Toplevel)
 
        {
 
            //this.profile = profile;
 
            this.Build();
 

	
 
            //Gtk.CellRendererText reagentColumnCell = new Gtk.CellRendererText();
 

	
 
            int count = 0;
 
            foreach (string name1 in ReagentManager.Names)
 
            {
 
                Reagent reagent1 = ReagentManager.GetReagent(name1);
 
                foreach (string name2 in ReagentManager.Names)
 
                {
 
                    if (name1.Equals(name2))
 
                    {
 
                        continue;
 
                    }
 
                    Reagent reagent2 = ReagentManager.GetReagent(name2);
 
                    Reaction reaction = profile.FindReaction(reagent1, reagent2);
 
                    if (reaction == null)
 
                    {
 
                        if (count == 0)
 
                        {
 
                            Gtk.Label header = new Gtk.Label("Missing Reactions:");
 
                            resultbox.PackStart(header, false, false, 0);
 
                            Gtk.HSeparator sep = new Gtk.HSeparator();
 
                            resultbox.PackStart(sep, false, false, 0);
 
                        }
 
                        Gtk.Label label = new Gtk.Label(name1 + " + " + name2);
 
                        resultbox.PackStart(label, false, false, 0);
 

	
 
                        ++count;
 
                    }
 
                }
 
            }
 

	
 
            if (count == 0)
 
            {
 
                Gtk.Label header = new Gtk.Label("All reactions recorded!");
 
                resultbox.PackStart(header, false, false, 0);
 
            }
 
            ShowAll();
 
        }
 
    }
 
}
 

	
Reagent.cs
Show inline comments
 
using System;
 

 
namespace DesertPaintLab
 
{
 
	public class Reagent
 
	{ 
 
		string name;
 
		bool isCatalyst = false;
 
        int cost = 0;
 
		PaintColor color;
 
		
 
		public bool IsCatalyst
 
		{
 
			get
 
			{
 
				return isCatalyst;	
 
			}
 
		}
 
		
 
		public PaintColor Color
 
		{
 
			get
 
			{
 
				return color;	
 
			}
 
		}
 
		
 
		public string Name
 
		{
 
			get
 
			{
 
				return name;	
 
			}
 
		}
 
		
 
		public Reagent(string name)
 
        public Reagent(string name, int cost)
 
		{
 
			this.name = name;
 
            this.cost = cost;
 
			isCatalyst = true;
 
		}
 
		
 
		public Reagent(string name, byte red, byte green, byte blue)
 
        public Reagent(string name, byte red, byte green, byte blue, int cost)
 
		{
 
			color = new PaintColor(red, green, blue);
 
			this.name = name;
 
            this.cost = cost;
 
		}
 
		
 
		public override string ToString()
 
		{
 
			if (isCatalyst)
 
			{
 
				return "[" + name + ", catalyst]";
 
			}
 
			else
 
			{
 
				return "[" + name + ", " + color.ToString() + "]";
 
			}
 
		}
 
		
 
		
 
	}
 
}
 

ReagentManager.cs
Show inline comments
 
using System;
 
using System.IO;
 
using System.Collections.Generic;
 
using System.Text.RegularExpressions;
 

 
namespace DesertPaintLab
 
{
 
	public class ReagentManager
 
	{
 
		static Regex reagentRegex = new Regex(@"(?<name>\w+)\s*\|\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+).*");
 
		static Regex catalystRegex = new Regex(@"(?<name>\w+)\s*\|\s*catalyst");
 
		static Regex reagentRegex = new Regex(@"(?<name>\w+)\s*\|\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+)\s*\|\s*(?<cost>\d+)\s*\|.*");
 
		static Regex catalystRegex = new Regex(@"(?<name>\w+)\s*\|\s*catalyst\s*\|\s*(?<cost>\d+)\s*\|.*");
 
		
 
		static SortedDictionary<string,Reagent> reagents = new SortedDictionary<string, Reagent>();
 
        static List<string> names = new List<string>();
 
		
 
		static Gtk.ListStore nameStore = new Gtk.ListStore(typeof(string));
 

 
		static public Gtk.ListStore NameListModel
 
		{
 
			get
 
			{
 
				return nameStore;	
 
			}
 
		}
 
		
 
        static public List<string> Names
 
        {
 
            get
 
            {
 
                return names;
 
            }
 
        }
 
		
 
		public ReagentManager ()
 
		{
 
			
 
		}
 
		
 
		public static void Load(string file)
 
		{	
 
			Match match;
 
			string line;
 
			reagents.Clear();
 
			using (StreamReader reader = new StreamReader(file))
 
			{
 
				while ((line = reader.ReadLine()) != null) 
 
                {
 
					match = reagentRegex.Match(line); 
 
					if (match.Success)
 
					{
 
						string name = match.Groups["name"].Value;
 
						reagents.Add(name,
 
							new Reagent(name,
 
						        byte.Parse(match.Groups["red"].Value),
 
						    	byte.Parse(match.Groups["green"].Value),
 
						        byte.Parse(match.Groups["blue"].Value)));
 
						        byte.Parse(match.Groups["blue"].Value),
 
                                int.Parse(match.Groups["cost"].Value)));
 
						nameStore.AppendValues(name);
 
                        names.Add(name);
 
					}
 
					else
 
					{
 
						match = catalystRegex.Match(line);
 
						if (match.Success)
 
						{
 
							string name = match.Groups["name"].Value;
 
							reagents.Add(name, new Reagent(name));
 
                            int cost = int.Parse(match.Groups["cost"].Value);
 
							reagents.Add(name, new Reagent(name, cost));
 
							nameStore.AppendValues(name);
 
                            names.Add(name);
 
						}
 
					}
 
                }
 
			}
 
		}
 
		
 
		public static void InitializeReactions(ref SortedDictionary<string, SortedDictionary<string, Reaction>> reactions)
 
		{
 
			foreach (KeyValuePair<string, Reagent> pair1 in reagents)
 
			{
 
				SortedDictionary<string, Reaction> dict = new SortedDictionary<string, Reaction>();
 
				foreach (KeyValuePair<string, Reagent> pair2 in reagents)
 
				{
 
					if (pair1.Key != pair2.Key)
 
					{
 
						dict.Add(pair2.Key, null);
 
					}
 
				}
 
				reactions.Add(pair1.Key, dict);
 
			}
 
		}
 
		
 
		public static void PopulateReagents(ref Gtk.ComboBox comboBox)
 
		{
 
			comboBox.Clear();
 
			
 
			Gtk.CellRendererText cell = new Gtk.CellRendererText();
 
	        comboBox.PackStart(cell, false);
 
	        comboBox.AddAttribute(cell, "text", 0);
 
	        Gtk.ListStore store = new Gtk.ListStore(typeof(string));
 
	        comboBox.Model = store;
 
			
 
			store.AppendValues("");
 
			foreach (KeyValuePair<string, Reagent> pair in reagents)
 
            foreach (string name in names)
 
			{
 
				store.AppendValues(pair.Key);
 
				store.AppendValues(name);
 
			}
 
		}
 
		
 
		/*
 
		public static void PopulatePigments(ref Gtk.ComboBox comboBox)
 
		{
 
			comboBox.Clear();
 
			
 
			Gtk.CellRendererText cell = new Gtk.CellRendererText();
 
	        comboBox.PackStart(cell, false);
 
	        comboBox.AddAttribute(cell, "text", 0);
 
	        Gtk.ListStore store = new Gtk.ListStore(typeof(string));
 
	        comboBox.Model = store;
 
			
 
			store.AppendValues("");
 
			foreach (KeyValuePair<string, Reagent> pair in reagents)
 
			{
 
				if (!pair.Value.IsCatalyst)
 
				{
 
					store.AppendValues(pair.Key);
 
				}
 
			}			
 
		}
 
		*/
 
		
 
		public static Reagent GetReagent(string reagentName)
 
		{
 
			Reagent returnVal;
 
			reagents.TryGetValue(reagentName, out returnVal);
 
			return returnVal;
 
		}
 
	}
 
	
 
}
 

SimulatorWindow.cs
Show inline comments
 
/*
 
 * Copyright (c) 2010, Tess Snider
 

 
 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.Generic;
 

 
namespace DesertPaintLab
 
{
 
	public partial class SimulatorWindow : Gtk.Window
 
	{
 
		PlayerProfile profile;
 
		Gtk.ListStore recipeData = new Gtk.ListStore(typeof(string), typeof(int));
 
        PaintRecipe paintRecipe = new PaintRecipe();
 

 
		public SimulatorWindow(PlayerProfile profile) : base(Gtk.WindowType.Toplevel)
 
		{
 
			this.profile = profile;
 
			this.Build ();
 
			
 
			Gtk.TreeViewColumn reagentColumn = new Gtk.TreeViewColumn();
 
			Gtk.CellRendererText reagentColumnCell = new Gtk.CellRendererText();
 
			reagentColumn.PackStart(reagentColumnCell, true);		
 
			reagentColumn.Title = "Ingredient";
 
			
 
			reagentListView.AppendColumn(reagentColumn);
 
			reagentColumn.AddAttribute(reagentColumnCell, "text", 0);
 
			
 
			reagentListView.Model = ReagentManager.NameListModel;
 
			
 
			Gtk.TreeViewColumn additiveColumn = new Gtk.TreeViewColumn();
 
			Gtk.CellRendererText additiveColumnCell = new Gtk.CellRendererText();
 
			additiveColumn.PackStart(additiveColumnCell, true);		
 
			additiveColumn.Title = "Ingredient";
 
			
 
			recipeView.AppendColumn(additiveColumn);
 
			additiveColumn.AddAttribute(additiveColumnCell, "text", 0);
 
			
 
			Gtk.TreeViewColumn qtyColumn = new Gtk.TreeViewColumn();
 
			Gtk.CellRendererText qtyColumnCell = new Gtk.CellRendererText();
 
			qtyColumnCell.Editable = true;
 
			qtyColumnCell.Edited += OnQtyEdited;
 
			qtyColumn.PackStart(qtyColumnCell, true);		
 
			qtyColumn.Title = "Qty";
 
			
 
			recipeView.AppendColumn(qtyColumn);
 
			qtyColumn.AddAttribute(qtyColumnCell, "text", 1);
 
			
 
			recipeView.Model = recipeData;
 
			
 
			recipeData.RowChanged += OnRecipeChanged;
 
			recipeData.RowDeleted += OnRecipeChanged;
 
			recipeData.RowInserted += OnRecipeChanged;
 
			recipeData.RowsReordered += OnRecipeChanged;
 
			
 
		}
 
		
 
		protected virtual void OnAddReagent(object sender, System.EventArgs e)
 
		{
 
			Gtk.TreeModel model;
 
			Gtk.TreeIter iter;
 

 
			Gtk.TreeSelection selection = reagentListView.Selection;
 
			if ((selection != null) && selection.GetSelected(out model, out iter))
 
			{					
 
				recipeData.AppendValues(model.GetValue(iter, 0).ToString(), 1);	
 
				
 
				recipeData.IterNthChild(out iter, recipeView.Children.Length - 1);
 
				
 
				selection = recipeView.Selection;
 
				selection.SelectIter(iter);
 
			} 
 
		}	
 
		
 
		protected void OnQtyEdited(object sender, Gtk.EditedArgs args)
 
        {
 
			Gtk.TreeIter iter;
 
			recipeData.GetIter(out iter, new Gtk.TreePath(args.Path));
 
			
 
			int oldValue = (int)recipeData.GetValue(iter, 1);
 
			
 
            try
 
            {
 
                recipeData.SetValue(iter, 1, int.Parse(args.NewText));
 
				UpdateRecipeColor();
 
            }
 
            catch (Exception)
 
            {
 
            	recipeData.SetValue(iter, 1, oldValue);
 
			}
 
        }	
 
	
 
		protected void OnRecipeChanged(object sender, GLib.SignalArgs args)
 
	    {
 
			UpdateRecipeColor();
 
	    }	
 

 
		void UpdateRecipeColor()
 
		{
 
			if (recipeView.Children.Length == 0)
 
			{
 
				paintSwatch.Clear();
 
			}
 
			
 
			Reaction reaction;
 
            paintRecipe.Clear();
 

 
			Gtk.TreeIter iter;
 
			string reagentName;
 
			int qty;
 
			PaintColor color = null;
 
			Reagent reagent = null;
 
			
 
			int baseRedSum = 0;
 
			int baseGreenSum = 0;
 
			int baseBlueSum = 0;
 
			
 
			int reactRedSum = 0;
 
			int reactGreenSum = 0;
 
			int reactBlueSum = 0;
 
			
 
			int pigmentCount = 0;
 
			
 
			SortedDictionary<string,bool> reagentSet = new SortedDictionary<string,bool>();
 
			List<Reagent> reagents = new List<Reagent>();
 
			
 
			recipeData.GetIterFirst(out iter);
 
			
 
			do
 
			{
 
   				reagentName = (string) recipeData.GetValue(iter, 0);
 
				
 
				if (reagentName == null)
 
				{
 
					continue;	
 
				}
 
				
 
				qty = (int)recipeData.GetValue(iter, 1);
 
				reagent = ReagentManager.GetReagent(reagentName);
 
				if (!reagent.IsCatalyst)
 
				{
 
					color = reagent.Color;
 
					baseRedSum += qty * color.Red;
 
					baseGreenSum += qty * color.Green;
 
					baseBlueSum += qty * color.Blue;
 
					pigmentCount += qty;
 
				}
 
				if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
 
                for (int i = 0; i < qty; ++i)
 
				{
 
					reagentSet[reagentName] = true;
 
					// Run reactions.
 
					foreach (Reagent otherReagent in reagents)
 
					{
 
						reaction = profile.FindReaction(otherReagent, reagent);
 
						if (reaction != null)
 
						{
 
							reactRedSum += reaction.Red;
 
							reactGreenSum += reaction.Green;
 
							reactBlueSum += reaction.Blue;
 
                    paintRecipe.AddReagent(reagentName);
 
						}
 
					}
 
					reagents.Add(reagent);
 
				}
 
				
 
			}
 
			while (recipeData.IterNext(ref iter));
 
			
 
			paintSwatch.Color = new PaintColor(
 
				CalculateColor(baseRedSum, pigmentCount, reactRedSum),
 
			    CalculateColor(baseGreenSum, pigmentCount, reactGreenSum),
 
				CalculateColor(baseBlueSum,  pigmentCount, reactBlueSum));	
 
		}
 
		
 
		byte CalculateColor(int baseSum, int pigmentCount, int reactSum)
 
		{
 
			return (byte)Math.Max(Math.Min(Math.Round((((float)baseSum / (float)pigmentCount) + (float)reactSum)), 255), 0);
 
            PaintColor resultColor = new PaintColor();
 
            paintRecipe.ComputeReactedColor(profile, ref resultColor);
 
			paintSwatch.Color = resultColor;	
 
		}
 

 
		protected virtual void OnIncrementReagent (object sender, System.EventArgs e)
 
		{
 
			Gtk.TreeModel model;
 
			Gtk.TreeIter iter;
 

 
			Gtk.TreeSelection selection = recipeView.Selection;
 
			if ((selection != null) && selection.GetSelected(out model, out iter))
 
			{
 
				int oldValue = (int)recipeData.GetValue(iter, 1);
 
				recipeData.SetValue(iter, 1, oldValue + 1);
 
			}
 
			
 
		}
 
		
 
		protected virtual void OnDecrementReagent (object sender, System.EventArgs e)
 
		{
 
			Gtk.TreeModel model;
 
			Gtk.TreeIter iter;
 

 
			Gtk.TreeSelection selection = recipeView.Selection;
 
			if ((selection != null) && selection.GetSelected(out model, out iter))
 
			{
 
				int oldValue = (int)recipeData.GetValue(iter, 1);
 
				if (oldValue == 1)
 
				{
 
					recipeData.Remove(ref iter);
 
				}
 
				else
 
				{
 
					recipeData.SetValue(iter, 1, oldValue - 1);
 
				}
 
			}			
 
			
 
		}
 

 
        protected virtual void OnFlushReagents (object sender, System.EventArgs e)
 
        {
 
            recipeData.Clear();
 
        }
 
	}
 
}
...
 
\ No newline at end of file
gtk-gui/DesertPaintLab.ReactionStatusWindow.cs
Show inline comments
 
new file 100644
 

	
 
// This file has been generated by the GUI designer. Do not modify.
 
namespace DesertPaintLab
 
{
 
	public partial class ReactionStatusWindow
 
	{
 
		private global::Gtk.ScrolledWindow scroller;
 
		
 
		private global::Gtk.VBox resultbox;
 

	
 
		protected virtual void Build ()
 
		{
 
			global::Stetic.Gui.Initialize (this);
 
			// Widget DesertPaintLab.ReactionStatusWindow
 
			this.Name = "DesertPaintLab.ReactionStatusWindow";
 
			this.Title = "Reaction Status";
 
			this.WindowPosition = ((global::Gtk.WindowPosition)(4));
 
			// Container child DesertPaintLab.ReactionStatusWindow.Gtk.Container+ContainerChild
 
			this.scroller = new global::Gtk.ScrolledWindow ();
 
			this.scroller.CanFocus = true;
 
			this.scroller.Name = "scroller";
 
			this.scroller.ShadowType = ((global::Gtk.ShadowType)(1));
 
			// Container child scroller.Gtk.Container+ContainerChild
 
			global::Gtk.Viewport w1 = new global::Gtk.Viewport ();
 
			w1.ShadowType = ((global::Gtk.ShadowType)(0));
 
			// Container child GtkViewport.Gtk.Container+ContainerChild
 
			this.resultbox = new global::Gtk.VBox ();
 
			this.resultbox.Name = "resultbox";
 
			this.resultbox.Spacing = 6;
 
			w1.Add (this.resultbox);
 
			this.scroller.Add (w1);
 
			this.Add (this.scroller);
 
			if ((this.Child != null)) {
 
				this.Child.ShowAll ();
 
			}
 
			this.DefaultWidth = 400;
 
			this.DefaultHeight = 300;
 
			this.Show ();
 
		}
 
	}
 
}
gtk-gui/MainWindow.cs
Show inline comments
 

	
 
// This file has been generated by the GUI designer. Do not modify.
 

	
 
public partial class MainWindow
 
{
 
	private global::Gtk.UIManager UIManager;
 
	
 
	private global::Gtk.Action FileAction;
 
	
 
	private global::Gtk.Action HelpAction;
 
	
 
	private global::Gtk.Action AboutAction;
 
	
 
	private global::Gtk.Action NewProfileAction;
 
	
 
	private global::Gtk.Action OpenProfileAction;
 
	
 
	private global::Gtk.Action ExitAction;
 
	
 
	private global::Gtk.Action ExportForPracticalPaintAction;
 
	
 
	private global::Gtk.Action WindowAction;
 
	
 
	private global::Gtk.Action RunSimulatorAction;
 
	
 
	private global::Gtk.Action DebugAction;
 
	
 
	private global::Gtk.Action ScreenshotAction;
 
	
 
	private global::Gtk.Action RecipeGeneratorAction;
 
	
 
	private global::Gtk.Action ReactionStatusAction;
 
	
 
	private global::Gtk.VBox vbox1;
 
	
 
	private global::Gtk.MenuBar menubar1;
 
	
 
	private global::Gtk.HBox hbox1;
 
	
 
	private global::Gtk.Frame frame2;
 
	
 
	private global::Gtk.Alignment GtkAlignment;
 
	
 
	private global::Gtk.VBox vbox3;
 
	
 
	private global::Gtk.HBox hbox6;
 
	
 
	private global::Gtk.Label label4;
 
	
 
	private global::Gtk.ComboBox ingredient1ComboBox;
 
	
 
	private global::Gtk.HBox hbox7;
 
	
 
	private global::Gtk.Label label5;
 
	
 
	private global::Gtk.ComboBox ingredient2ComboBox;
 
	
 
	private global::Gtk.HBox hbox8;
 
	
 
	private global::Gtk.Label label6;
 
	
 
	private global::Gtk.ComboBox ingredient3ComboBox;
 
	
 
	private global::Gtk.Label GtkLabel2;
 
	
 
	private global::Gtk.Frame frame3;
 
	
 
	private global::Gtk.Alignment GtkAlignment1;
 
	
 
	private global::Gtk.VBox vbox4;
 
	
 
	private global::DesertPaintLab.PaintSwatch unmodifiedSwatch;
 
	
 
	private global::Gtk.Button captureButton;
 
	
 
	private global::Gtk.Label GtkLabel25;
 
	
 
	private global::Gtk.Frame frame4;
 
	
 
	private global::Gtk.Alignment GtkAlignment2;
 
	
 
	private global::Gtk.VBox vbox5;
 
	
 
	private global::DesertPaintLab.PaintSwatch reactionSwatch;
 
	
 
	private global::Gtk.Button saveButton;
 
	
 
	private global::Gtk.Label GtkLabel26;
 
	
 
	private global::Gtk.Statusbar statusBar;
 

	
 
	protected virtual void Build ()
 
	{
 
		global::Stetic.Gui.Initialize (this);
 
		// Widget MainWindow
 
		this.UIManager = new global::Gtk.UIManager ();
 
		global::Gtk.ActionGroup w1 = new global::Gtk.ActionGroup ("Default");
 
		this.FileAction = new global::Gtk.Action ("FileAction", "_File", null, null);
 
		this.FileAction.ShortLabel = "_File";
 
		w1.Add (this.FileAction, "<Alt>f");
 
		this.HelpAction = new global::Gtk.Action ("HelpAction", "_Help", null, null);
 
		this.HelpAction.ShortLabel = "_Help";
 
		w1.Add (this.HelpAction, "<Alt>a");
 
		this.AboutAction = new global::Gtk.Action ("AboutAction", "_About...", null, null);
 
		this.AboutAction.ShortLabel = "_About...";
 
		w1.Add (this.AboutAction, "<Alt>a");
 
		this.NewProfileAction = new global::Gtk.Action ("NewProfileAction", "_New Profile...", null, null);
 
		this.NewProfileAction.ShortLabel = "_New Profile...";
 
		w1.Add (this.NewProfileAction, "<Alt>n");
 
		this.OpenProfileAction = new global::Gtk.Action ("OpenProfileAction", "_Open Profile...", null, null);
 
		this.OpenProfileAction.ShortLabel = "_Open Profile...";
 
		w1.Add (this.OpenProfileAction, "<Alt>o");
 
		this.ExitAction = new global::Gtk.Action ("ExitAction", "E_xit", null, null);
 
		this.ExitAction.ShortLabel = "E_xit";
 
		w1.Add (this.ExitAction, "<Alt>x");
 
		this.ExportForPracticalPaintAction = new global::Gtk.Action ("ExportForPracticalPaintAction", "Export for _PracticalPaint...", null, null);
 
		this.ExportForPracticalPaintAction.ShortLabel = "Export for _PracticalPaint...";
 
		w1.Add (this.ExportForPracticalPaintAction, null);
 
		this.WindowAction = new global::Gtk.Action ("WindowAction", "_Window", null, null);
 
		this.WindowAction.ShortLabel = "_Window";
 
		w1.Add (this.WindowAction, null);
 
		this.RunSimulatorAction = new global::Gtk.Action ("RunSimulatorAction", "_Run Simulator", null, null);
 
		this.RunSimulatorAction.ShortLabel = "_Run Simulator";
 
		w1.Add (this.RunSimulatorAction, null);
 
		this.DebugAction = new global::Gtk.Action ("DebugAction", "Debug", null, null);
 
		this.DebugAction.ShortLabel = "Debug";
 
		w1.Add (this.DebugAction, null);
 
		this.ScreenshotAction = new global::Gtk.Action ("ScreenshotAction", "Screenshot", null, null);
 
		this.ScreenshotAction.ShortLabel = "Screenshot";
 
		w1.Add (this.ScreenshotAction, null);
 
		this.RecipeGeneratorAction = new global::Gtk.Action ("RecipeGeneratorAction", "Recipe Generator", null, null);
 
		this.RecipeGeneratorAction.ShortLabel = "Recipe Generator";
 
		w1.Add (this.RecipeGeneratorAction, null);
 
		this.ReactionStatusAction = new global::Gtk.Action ("ReactionStatusAction", "Reaction Status", null, null);
 
		this.ReactionStatusAction.ShortLabel = "Reaction Status";
 
		w1.Add (this.ReactionStatusAction, null);
 
		this.UIManager.InsertActionGroup (w1, 0);
 
		this.AddAccelGroup (this.UIManager.AccelGroup);
 
		this.Name = "MainWindow";
 
		this.Title = "Desert Paint Lab";
 
		this.WindowPosition = ((global::Gtk.WindowPosition)(4));
 
		// Container child MainWindow.Gtk.Container+ContainerChild
 
		this.vbox1 = new global::Gtk.VBox ();
 
		this.vbox1.Name = "vbox1";
 
		// Container child vbox1.Gtk.Box+BoxChild
 
		this.UIManager.AddUiFromString ("<ui><menubar name='menubar1'><menu name='FileAction' action='FileAction'><menuitem name='NewProfileAction' action='NewProfileAction'/><menuitem name='OpenProfileAction' action='OpenProfileAction'/><menuitem name='ExportForPracticalPaintAction' action='ExportForPracticalPaintAction'/><separator/><menuitem name='ExitAction' action='ExitAction'/></menu><menu name='WindowAction' action='WindowAction'><menuitem name='RunSimulatorAction' action='RunSimulatorAction'/></menu><menu name='HelpAction' action='HelpAction'><menuitem name='AboutAction' action='AboutAction'/></menu><menu name='DebugAction' action='DebugAction'><menuitem name='ScreenshotAction' action='ScreenshotAction'/></menu></menubar></ui>");
 
		this.UIManager.AddUiFromString ("<ui><menubar name='menubar1'><menu name='FileAction' action='FileAction'><menuitem name='NewProfileAction' action='NewProfileAction'/><menuitem name='OpenProfileAction' action='OpenProfileAction'/><menuitem name='ExportForPracticalPaintAction' action='ExportForPracticalPaintAction'/><separator/><menuitem name='ExitAction' action='ExitAction'/></menu><menu name='WindowAction' action='WindowAction'><menuitem name='RunSimulatorAction' action='RunSimulatorAction'/><menuitem name='RecipeGeneratorAction' action='RecipeGeneratorAction'/></menu><menu name='HelpAction' action='HelpAction'><menuitem name='AboutAction' action='AboutAction'/><menuitem name='ReactionStatusAction' action='ReactionStatusAction'/></menu><menu name='DebugAction' action='DebugAction'><menuitem name='ScreenshotAction' action='ScreenshotAction'/></menu></menubar></ui>");
 
		this.menubar1 = ((global::Gtk.MenuBar)(this.UIManager.GetWidget ("/menubar1")));
 
		this.menubar1.Name = "menubar1";
 
		this.vbox1.Add (this.menubar1);
 
		global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.menubar1]));
 
		w2.Position = 0;
 
		w2.Expand = false;
 
		w2.Fill = false;
 
		// Container child vbox1.Gtk.Box+BoxChild
 
		this.hbox1 = new global::Gtk.HBox ();
 
		this.hbox1.Name = "hbox1";
 
		this.hbox1.Spacing = 6;
 
		this.hbox1.BorderWidth = ((uint)(4));
 
		// Container child hbox1.Gtk.Box+BoxChild
 
		this.frame2 = new global::Gtk.Frame ();
 
		this.frame2.Name = "frame2";
 
		this.frame2.BorderWidth = ((uint)(4));
 
		// Container child frame2.Gtk.Container+ContainerChild
 
		this.GtkAlignment = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
 
		this.GtkAlignment.Name = "GtkAlignment";
 
		this.GtkAlignment.LeftPadding = ((uint)(6));
 
		this.GtkAlignment.RightPadding = ((uint)(6));
 
		// Container child GtkAlignment.Gtk.Container+ContainerChild
 
		this.vbox3 = new global::Gtk.VBox ();
 
		this.vbox3.Name = "vbox3";
 
		this.vbox3.Homogeneous = true;
 
		this.vbox3.Spacing = 6;
 
		// Container child vbox3.Gtk.Box+BoxChild
 
		this.hbox6 = new global::Gtk.HBox ();
 
		this.hbox6.Name = "hbox6";
 
		this.hbox6.Spacing = 6;
 
		// Container child hbox6.Gtk.Box+BoxChild
 
		this.label4 = new global::Gtk.Label ();
 
		this.label4.Name = "label4";
 
		this.label4.LabelProp = "Ingredient 1:";
 
		this.hbox6.Add (this.label4);
 
		global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.hbox6 [this.label4]));
 
		w3.Position = 0;
 
		w3.Expand = false;
 
		w3.Fill = false;
 
		// Container child hbox6.Gtk.Box+BoxChild
 
		this.ingredient1ComboBox = global::Gtk.ComboBox.NewText ();
 
		this.ingredient1ComboBox.Name = "ingredient1ComboBox";
 
		this.hbox6.Add (this.ingredient1ComboBox);
 
		global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.hbox6 [this.ingredient1ComboBox]));
 
		w4.Position = 1;
 
		this.vbox3.Add (this.hbox6);
 
		global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.hbox6]));
 
		w5.Position = 0;
 
		w5.Expand = false;
 
		w5.Fill = false;
 
		// Container child vbox3.Gtk.Box+BoxChild
 
		this.hbox7 = new global::Gtk.HBox ();
 
		this.hbox7.Name = "hbox7";
 
		this.hbox7.Spacing = 6;
 
		// Container child hbox7.Gtk.Box+BoxChild
 
		this.label5 = new global::Gtk.Label ();
 
		this.label5.Name = "label5";
 
		this.label5.LabelProp = "Ingredient 2:";
 
		this.hbox7.Add (this.label5);
 
		global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.hbox7 [this.label5]));
 
		w6.Position = 0;
 
		w6.Expand = false;
 
		w6.Fill = false;
 
		// Container child hbox7.Gtk.Box+BoxChild
 
		this.ingredient2ComboBox = global::Gtk.ComboBox.NewText ();
 
		this.ingredient2ComboBox.Name = "ingredient2ComboBox";
 
		this.hbox7.Add (this.ingredient2ComboBox);
 
		global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.hbox7 [this.ingredient2ComboBox]));
 
		w7.Position = 1;
 
		this.vbox3.Add (this.hbox7);
 
		global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.hbox7]));
 
		w8.Position = 1;
 
		w8.Expand = false;
 
		w8.Fill = false;
 
		// Container child vbox3.Gtk.Box+BoxChild
 
		this.hbox8 = new global::Gtk.HBox ();
 
		this.hbox8.Name = "hbox8";
 
		this.hbox8.Spacing = 6;
 
		// Container child hbox8.Gtk.Box+BoxChild
 
		this.label6 = new global::Gtk.Label ();
 
		this.label6.Name = "label6";
 
		this.label6.LabelProp = "Ingredient 3:";
 
		this.hbox8.Add (this.label6);
 
		global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.hbox8 [this.label6]));
 
		w9.Position = 0;
 
		w9.Expand = false;
 
		w9.Fill = false;
 
		// Container child hbox8.Gtk.Box+BoxChild
 
		this.ingredient3ComboBox = global::Gtk.ComboBox.NewText ();
 
		this.ingredient3ComboBox.Name = "ingredient3ComboBox";
 
		this.hbox8.Add (this.ingredient3ComboBox);
 
		global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.hbox8 [this.ingredient3ComboBox]));
 
		w10.Position = 1;
 
		this.vbox3.Add (this.hbox8);
 
		global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.hbox8]));
 
		w11.Position = 2;
 
		w11.Expand = false;
 
		w11.Fill = false;
 
		this.GtkAlignment.Add (this.vbox3);
 
		this.frame2.Add (this.GtkAlignment);
 
		this.GtkLabel2 = new global::Gtk.Label ();
 
		this.GtkLabel2.Name = "GtkLabel2";
 
		this.GtkLabel2.LabelProp = "<b>Select Ingredients</b>";
 
		this.GtkLabel2.UseMarkup = true;
 
		this.frame2.LabelWidget = this.GtkLabel2;
 
		this.hbox1.Add (this.frame2);
 
		global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.frame2]));
 
		w14.Position = 0;
 
		// Container child hbox1.Gtk.Box+BoxChild
 
		this.frame3 = new global::Gtk.Frame ();
 
		this.frame3.Name = "frame3";
 
		this.frame3.BorderWidth = ((uint)(4));
 
		// Container child frame3.Gtk.Container+ContainerChild
 
		this.GtkAlignment1 = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
 
		this.GtkAlignment1.Name = "GtkAlignment1";
 
		this.GtkAlignment1.LeftPadding = ((uint)(5));
 
		this.GtkAlignment1.TopPadding = ((uint)(5));
 
		this.GtkAlignment1.RightPadding = ((uint)(5));
 
		this.GtkAlignment1.BottomPadding = ((uint)(6));
 
		// Container child GtkAlignment1.Gtk.Container+ContainerChild
 
		this.vbox4 = new global::Gtk.VBox ();
 
		this.vbox4.WidthRequest = 120;
 
		this.vbox4.Name = "vbox4";
 
		this.vbox4.Spacing = 6;
 
		// Container child vbox4.Gtk.Box+BoxChild
 
		this.unmodifiedSwatch = new global::DesertPaintLab.PaintSwatch ();
 
		this.unmodifiedSwatch.Events = ((global::Gdk.EventMask)(256));
 
		this.unmodifiedSwatch.Name = "unmodifiedSwatch";
 
		this.vbox4.Add (this.unmodifiedSwatch);
 
		global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.vbox4 [this.unmodifiedSwatch]));
 
		w15.Position = 0;
 
		// Container child vbox4.Gtk.Box+BoxChild
 
		this.captureButton = new global::Gtk.Button ();
 
		this.captureButton.WidthRequest = 100;
 
		this.captureButton.CanFocus = true;
 
		this.captureButton.Name = "captureButton";
 
		this.captureButton.UseUnderline = true;
 
		this.captureButton.Label = "Capture";
 
		this.vbox4.Add (this.captureButton);
 
		global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.vbox4 [this.captureButton]));
 
		w16.Position = 1;
 
		w16.Expand = false;
 
		w16.Fill = false;
 
		this.GtkAlignment1.Add (this.vbox4);
 
		this.frame3.Add (this.GtkAlignment1);
 
		this.GtkLabel25 = new global::Gtk.Label ();
 
		this.GtkLabel25.Name = "GtkLabel25";
 
		this.GtkLabel25.LabelProp = "<b>Unmodified</b>";
 
		this.GtkLabel25.UseMarkup = true;
 
		this.frame3.LabelWidget = this.GtkLabel25;
 
		this.hbox1.Add (this.frame3);
 
		global::Gtk.Box.BoxChild w19 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.frame3]));
 
		w19.Position = 1;
 
		w19.Expand = false;
 
		w19.Fill = false;
 
		// Container child hbox1.Gtk.Box+BoxChild
 
		this.frame4 = new global::Gtk.Frame ();
 
		this.frame4.Name = "frame4";
 
		this.frame4.BorderWidth = ((uint)(4));
 
		// Container child frame4.Gtk.Container+ContainerChild
 
		this.GtkAlignment2 = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
 
		this.GtkAlignment2.WidthRequest = 130;
 
		this.GtkAlignment2.Name = "GtkAlignment2";
 
		this.GtkAlignment2.LeftPadding = ((uint)(5));
 
		this.GtkAlignment2.TopPadding = ((uint)(5));
 
		this.GtkAlignment2.RightPadding = ((uint)(5));
 
		this.GtkAlignment2.BottomPadding = ((uint)(6));
 
		// Container child GtkAlignment2.Gtk.Container+ContainerChild
 
		this.vbox5 = new global::Gtk.VBox ();
 
		this.vbox5.WidthRequest = 120;
 
		this.vbox5.Name = "vbox5";
 
		this.vbox5.Spacing = 6;
 
		// Container child vbox5.Gtk.Box+BoxChild
 
		this.reactionSwatch = new global::DesertPaintLab.PaintSwatch ();
 
		this.reactionSwatch.Events = ((global::Gdk.EventMask)(256));
 
		this.reactionSwatch.Name = "reactionSwatch";
 
		this.vbox5.Add (this.reactionSwatch);
 
		global::Gtk.Box.BoxChild w20 = ((global::Gtk.Box.BoxChild)(this.vbox5 [this.reactionSwatch]));
 
		w20.Position = 0;
 
		// Container child vbox5.Gtk.Box+BoxChild
 
		this.saveButton = new global::Gtk.Button ();
 
		this.saveButton.WidthRequest = 100;
 
		this.saveButton.CanFocus = true;
 
		this.saveButton.Name = "saveButton";
 
		this.saveButton.UseUnderline = true;
 
		this.saveButton.Label = "Record";
 
		this.vbox5.Add (this.saveButton);
 
		global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.vbox5 [this.saveButton]));
 
		w21.Position = 1;
 
		w21.Expand = false;
 
		w21.Fill = false;
 
		this.GtkAlignment2.Add (this.vbox5);
 
		this.frame4.Add (this.GtkAlignment2);
 
		this.GtkLabel26 = new global::Gtk.Label ();
 
		this.GtkLabel26.Name = "GtkLabel26";
 
		this.GtkLabel26.LabelProp = "<b>Reaction</b>";
 
		this.GtkLabel26.UseMarkup = true;
 
		this.frame4.LabelWidget = this.GtkLabel26;
 
		this.hbox1.Add (this.frame4);
 
		global::Gtk.Box.BoxChild w24 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.frame4]));
 
		w24.Position = 2;
 
		w24.Expand = false;
 
		w24.Fill = false;
 
		this.vbox1.Add (this.hbox1);
 
		global::Gtk.Box.BoxChild w25 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.hbox1]));
 
		w25.Position = 1;
 
		// Container child vbox1.Gtk.Box+BoxChild
 
		this.statusBar = new global::Gtk.Statusbar ();
 
		this.statusBar.Name = "statusBar";
 
		this.statusBar.Spacing = 6;
 
		this.vbox1.Add (this.statusBar);
 
		global::Gtk.Box.BoxChild w26 = ((global::Gtk.Box.BoxChild)(this.vbox1 [this.statusBar]));
 
		w26.Position = 2;
 
		w26.Expand = false;
 
		w26.Fill = false;
 
		this.Add (this.vbox1);
 
		if ((this.Child != null)) {
 
			this.Child.ShowAll ();
 
		}
 
		this.DefaultWidth = 629;
 
		this.DefaultHeight = 265;
 
		this.Show ();
 
		this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent);
 
		this.AboutAction.Activated += new global::System.EventHandler (this.OnAbout);
 
		this.NewProfileAction.Activated += new global::System.EventHandler (this.OnNewProfile);
 
		this.OpenProfileAction.Activated += new global::System.EventHandler (this.OnOpenProfile);
 
		this.ExitAction.Activated += new global::System.EventHandler (this.OnMenuExit);
 
		this.ExportForPracticalPaintAction.Activated += new global::System.EventHandler (this.OnExport);
 
		this.RunSimulatorAction.Activated += new global::System.EventHandler (this.RunSimulator);
 
		this.ScreenshotAction.Activated += new global::System.EventHandler (this.OnDebugScreenshot);
 
		this.RecipeGeneratorAction.Activated += new global::System.EventHandler (this.OnOpenRecipeGenerator);
 
		this.ReactionStatusAction.Activated += new global::System.EventHandler (this.OnShowReactionStatus);
 
		this.ingredient1ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient1);
 
		this.ingredient2ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient2);
 
		this.ingredient3ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient3);
 
		this.captureButton.Clicked += new global::System.EventHandler (this.OnCaptureButton);
 
		this.saveButton.Clicked += new global::System.EventHandler (this.OnSaveButton);
 
	}
 
}
gtk-gui/gui.stetic
Show inline comments
 
<?xml version="1.0" encoding="utf-8"?>
 
<stetic-interface>
 
  <configuration>
 
    <images-root-path>..</images-root-path>
 
    <target-gtk-version>2.12</target-gtk-version>
 
  </configuration>
 
  <import>
 
    <widget-library name="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <widget-library name="../bin/Debug/DesertPaintLab.exe" internal="true" />
 
  </import>
 
  <widget class="Gtk.Window" id="MainWindow" design-size="629 265">
 
    <action-group name="Default">
 
      <action id="FileAction">
 
        <property name="Type">Action</property>
 
        <property name="Accelerator">&lt;Alt&gt;f</property>
 
        <property name="Label" translatable="yes">_File</property>
 
        <property name="ShortLabel" translatable="yes">_File</property>
 
      </action>
 
      <action id="HelpAction">
 
        <property name="Type">Action</property>
 
        <property name="Accelerator">&lt;Alt&gt;a</property>
 
        <property name="Label" translatable="yes">_Help</property>
 
        <property name="ShortLabel" translatable="yes">_Help</property>
 
      </action>
 
      <action id="AboutAction">
 
        <property name="Type">Action</property>
 
        <property name="Accelerator">&lt;Alt&gt;a</property>
 
        <property name="Label" translatable="yes">_About...</property>
 
        <property name="ShortLabel" translatable="yes">_About...</property>
 
        <signal name="Activated" handler="OnAbout" />
 
      </action>
 
      <action id="NewProfileAction">
 
        <property name="Type">Action</property>
 
        <property name="Accelerator">&lt;Alt&gt;n</property>
 
        <property name="Label" translatable="yes">_New Profile...</property>
 
        <property name="ShortLabel" translatable="yes">_New Profile...</property>
 
        <signal name="Activated" handler="OnNewProfile" />
 
      </action>
 
      <action id="OpenProfileAction">
 
        <property name="Type">Action</property>
 
        <property name="Accelerator">&lt;Alt&gt;o</property>
 
        <property name="Label" translatable="yes">_Open Profile...</property>
 
        <property name="ShortLabel" translatable="yes">_Open Profile...</property>
 
        <signal name="Activated" handler="OnOpenProfile" />
 
      </action>
 
      <action id="ExitAction">
 
        <property name="Type">Action</property>
 
        <property name="Accelerator">&lt;Alt&gt;x</property>
 
        <property name="Label" translatable="yes">E_xit</property>
 
        <property name="ShortLabel" translatable="yes">E_xit</property>
 
        <signal name="Activated" handler="OnMenuExit" />
 
      </action>
 
      <action id="ExportForPracticalPaintAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Export for _PracticalPaint...</property>
 
        <property name="ShortLabel" translatable="yes">Export for _PracticalPaint...</property>
 
        <signal name="Activated" handler="OnExport" />
 
      </action>
 
      <action id="WindowAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">_Window</property>
 
        <property name="ShortLabel" translatable="yes">_Window</property>
 
      </action>
 
      <action id="RunSimulatorAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">_Run Simulator</property>
 
        <property name="ShortLabel" translatable="yes">_Run Simulator</property>
 
        <signal name="Activated" handler="RunSimulator" />
 
      </action>
 
      <action id="DebugAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Debug</property>
 
        <property name="ShortLabel" translatable="yes">Debug</property>
 
      </action>
 
      <action id="ScreenshotAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Screenshot</property>
 
        <property name="ShortLabel" translatable="yes">Screenshot</property>
 
        <signal name="Activated" handler="OnDebugScreenshot" />
 
      </action>
 
      <action id="RecipeGeneratorAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Recipe Generator</property>
 
        <property name="ShortLabel" translatable="yes">Recipe Generator</property>
 
        <signal name="Activated" handler="OnOpenRecipeGenerator" />
 
      </action>
 
      <action id="ReactionStatusAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Reaction Status</property>
 
        <property name="ShortLabel" translatable="yes">Reaction Status</property>
 
        <signal name="Activated" handler="OnShowReactionStatus" />
 
      </action>
 
    </action-group>
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">Desert Paint Lab</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <signal name="DeleteEvent" handler="OnDeleteEvent" />
 
    <child>
 
      <widget class="Gtk.VBox" id="vbox1">
 
        <property name="MemberName" />
 
        <child>
 
          <widget class="Gtk.MenuBar" id="menubar1">
 
            <property name="MemberName" />
 
            <node name="menubar1" type="Menubar">
 
              <node type="Menu" action="FileAction">
 
                <node type="Menuitem" action="NewProfileAction" />
 
                <node type="Menuitem" action="OpenProfileAction" />
 
                <node type="Menuitem" action="ExportForPracticalPaintAction" />
 
                <node type="Separator" />
 
                <node type="Menuitem" action="ExitAction" />
 
              </node>
 
              <node type="Menu" action="WindowAction">
 
                <node type="Menuitem" action="RunSimulatorAction" />
 
                <node type="Menuitem" action="RecipeGeneratorAction" />
 
              </node>
 
              <node type="Menu" action="HelpAction">
 
                <node type="Menuitem" action="AboutAction" />
 
                <node type="Menuitem" action="ReactionStatusAction" />
 
              </node>
 
              <node type="Menu" action="DebugAction">
 
                <node type="Menuitem" action="ScreenshotAction" />
 
              </node>
 
            </node>
 
          </widget>
 
          <packing>
 
            <property name="Position">0</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.HBox" id="hbox1">
 
            <property name="MemberName" />
 
            <property name="Spacing">6</property>
 
            <property name="BorderWidth">4</property>
 
            <child>
 
              <widget class="Gtk.Frame" id="frame2">
 
                <property name="MemberName" />
 
                <property name="BorderWidth">4</property>
 
                <child>
 
                  <widget class="Gtk.Alignment" id="GtkAlignment">
 
                    <property name="MemberName" />
 
                    <property name="Xalign">0</property>
 
                    <property name="Yalign">0</property>
 
                    <property name="LeftPadding">6</property>
 
                    <property name="RightPadding">6</property>
 
                    <child>
 
                      <widget class="Gtk.VBox" id="vbox3">
 
                        <property name="MemberName" />
 
                        <property name="Homogeneous">True</property>
 
                        <property name="Spacing">6</property>
 
                        <child>
 
                          <widget class="Gtk.HBox" id="hbox6">
 
                            <property name="MemberName" />
 
                            <property name="Spacing">6</property>
 
                            <child>
 
                              <widget class="Gtk.Label" id="label4">
 
                                <property name="MemberName" />
 
                                <property name="LabelProp" translatable="yes">Ingredient 1:</property>
 
                              </widget>
 
                              <packing>
 
                                <property name="Position">0</property>
 
                                <property name="AutoSize">True</property>
 
                                <property name="Expand">False</property>
 
                                <property name="Fill">False</property>
 
                              </packing>
 
                            </child>
 
                            <child>
 
                              <widget class="Gtk.ComboBox" id="ingredient1ComboBox">
 
                                <property name="MemberName" />
 
                                <property name="IsTextCombo">True</property>
 
                                <property name="Items" translatable="yes" />
 
                                <signal name="Changed" handler="OnChangedIngredient1" />
 
                              </widget>
 
                              <packing>
 
                                <property name="Position">1</property>
 
                                <property name="AutoSize">False</property>
 
                              </packing>
 
                            </child>
 
                          </widget>
 
                          <packing>
 
                            <property name="Position">0</property>
 
                            <property name="AutoSize">True</property>
 
                            <property name="Expand">False</property>
 
                            <property name="Fill">False</property>
 
                          </packing>
 
                        </child>
 
                        <child>
 
                          <widget class="Gtk.HBox" id="hbox7">
 
                            <property name="MemberName" />
 
                            <property name="Spacing">6</property>
 
                            <child>
 
                              <widget class="Gtk.Label" id="label5">
 
                                <property name="MemberName" />
 
                                <property name="LabelProp" translatable="yes">Ingredient 2:</property>
 
                              </widget>
 
                              <packing>
 
                                <property name="Position">0</property>
 
                                <property name="AutoSize">True</property>
 
                                <property name="Expand">False</property>
 
                                <property name="Fill">False</property>
 
                              </packing>
 
                            </child>
 
                            <child>
 
                              <widget class="Gtk.ComboBox" id="ingredient2ComboBox">
 
                                <property name="MemberName" />
 
                                <property name="IsTextCombo">True</property>
 
                                <property name="Items" translatable="yes" />
 
                                <signal name="Changed" handler="OnChangedIngredient2" />
 
                              </widget>
 
                              <packing>
 
                                <property name="Position">1</property>
 
                                <property name="AutoSize">False</property>
 
                              </packing>
 
                            </child>
 
                          </widget>
 
                          <packing>
 
                            <property name="Position">1</property>
 
                            <property name="AutoSize">True</property>
 
                            <property name="Expand">False</property>
 
                            <property name="Fill">False</property>
 
                          </packing>
 
                        </child>
 
                        <child>
 
                          <widget class="Gtk.HBox" id="hbox8">
 
                            <property name="MemberName" />
 
                            <property name="Spacing">6</property>
 
                            <child>
 
                              <widget class="Gtk.Label" id="label6">
 
                                <property name="MemberName" />
 
                                <property name="LabelProp" translatable="yes">Ingredient 3:</property>
 
                              </widget>
 
                              <packing>
 
                                <property name="Position">0</property>
 
                                <property name="AutoSize">True</property>
 
                                <property name="Expand">False</property>
 
                                <property name="Fill">False</property>
 
                              </packing>
 
                            </child>
 
                            <child>
 
                              <widget class="Gtk.ComboBox" id="ingredient3ComboBox">
 
                                <property name="MemberName" />
 
                                <property name="IsTextCombo">True</property>
 
                                <property name="Items" translatable="yes" />
 
                                <signal name="Changed" handler="OnChangedIngredient3" />
 
                              </widget>
 
                              <packing>
 
                                <property name="Position">1</property>
 
                                <property name="AutoSize">False</property>
 
                              </packing>
 
                            </child>
 
                          </widget>
 
                          <packing>
 
                            <property name="Position">2</property>
 
                            <property name="AutoSize">True</property>
 
                            <property name="Expand">False</property>
 
                            <property name="Fill">False</property>
 
                          </packing>
 
                        </child>
 
                      </widget>
 
                    </child>
 
                  </widget>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.Label" id="GtkLabel2">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">&lt;b&gt;Select Ingredients&lt;/b&gt;</property>
 
                    <property name="UseMarkup">True</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="type">label_item</property>
 
                  </packing>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">0</property>
 
                <property name="AutoSize">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Frame" id="frame3">
 
                <property name="MemberName" />
 
                <property name="BorderWidth">4</property>
 
                <child>
 
                  <widget class="Gtk.Alignment" id="GtkAlignment1">
 
                    <property name="MemberName" />
 
                    <property name="Xalign">0</property>
 
                    <property name="Yalign">0</property>
 
                    <property name="LeftPadding">5</property>
 
                    <property name="TopPadding">5</property>
 
                    <property name="RightPadding">5</property>
 
                    <property name="BottomPadding">6</property>
 
                    <child>
 
                      <widget class="Gtk.VBox" id="vbox4">
 
                        <property name="MemberName" />
 
                        <property name="WidthRequest">120</property>
 
                        <property name="Spacing">6</property>
 
                        <child>
 
                          <widget class="DesertPaintLab.PaintSwatch" id="unmodifiedSwatch">
 
                            <property name="MemberName" />
 
                            <property name="Events">ButtonPressMask</property>
 
                          </widget>
 
                          <packing>
 
                            <property name="Position">0</property>
 
                            <property name="AutoSize">True</property>
 
                          </packing>
 
                        </child>
 
                        <child>
 
                          <widget class="Gtk.Button" id="captureButton">
 
                            <property name="MemberName" />
 
                            <property name="WidthRequest">100</property>
 
                            <property name="CanFocus">True</property>
 
                            <property name="Type">TextOnly</property>
 
                            <property name="Label" translatable="yes">Capture</property>
 
                            <property name="UseUnderline">True</property>
 
                            <signal name="Clicked" handler="OnCaptureButton" />
 
                          </widget>
 
                          <packing>
 
                            <property name="Position">1</property>
 
                            <property name="AutoSize">True</property>
 
                            <property name="Expand">False</property>
 
                            <property name="Fill">False</property>
 
                          </packing>
 
                        </child>
 
                      </widget>
 
                    </child>
 
                  </widget>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.Label" id="GtkLabel25">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">&lt;b&gt;Unmodified&lt;/b&gt;</property>
 
                    <property name="UseMarkup">True</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="type">label_item</property>
 
                  </packing>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">1</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Frame" id="frame4">
 
                <property name="MemberName" />
 
                <property name="BorderWidth">4</property>
 
                <child>
 
                  <widget class="Gtk.Alignment" id="GtkAlignment2">
 
                    <property name="MemberName" />
 
                    <property name="WidthRequest">130</property>
 
                    <property name="Xalign">0</property>
 
                    <property name="Yalign">0</property>
 
                    <property name="LeftPadding">5</property>
 
                    <property name="TopPadding">5</property>
 
                    <property name="RightPadding">5</property>
 
                    <property name="BottomPadding">6</property>
 
                    <child>
 
                      <widget class="Gtk.VBox" id="vbox5">
 
                        <property name="MemberName" />
 
                        <property name="WidthRequest">120</property>
 
                        <property name="Spacing">6</property>
 
                        <child>
 
                          <widget class="DesertPaintLab.PaintSwatch" id="reactionSwatch">
 
                            <property name="MemberName" />
 
                            <property name="Events">ButtonPressMask</property>
 
                          </widget>
 
                          <packing>
 
                            <property name="Position">0</property>
 
                            <property name="AutoSize">True</property>
 
                          </packing>
 
                        </child>
 
                        <child>
 
                          <widget class="Gtk.Button" id="saveButton">
 
                            <property name="MemberName" />
 
                            <property name="WidthRequest">100</property>
 
                            <property name="CanFocus">True</property>
 
                            <property name="Type">TextOnly</property>
 
                            <property name="Label" translatable="yes">Record</property>
 
                            <property name="UseUnderline">True</property>
 
                            <signal name="Clicked" handler="OnSaveButton" />
 
                          </widget>
 
                          <packing>
 
                            <property name="Position">1</property>
 
                            <property name="AutoSize">True</property>
 
                            <property name="Expand">False</property>
 
                            <property name="Fill">False</property>
 
                          </packing>
 
                        </child>
 
                      </widget>
 
                    </child>
 
                  </widget>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.Label" id="GtkLabel26">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">&lt;b&gt;Reaction&lt;/b&gt;</property>
 
                    <property name="UseMarkup">True</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="type">label_item</property>
 
                  </packing>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">2</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
          </widget>
 
          <packing>
 
            <property name="Position">1</property>
 
            <property name="AutoSize">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.Statusbar" id="statusBar">
 
            <property name="MemberName" />
 
            <property name="Spacing">6</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">2</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
  </widget>
 
  <widget class="Gtk.Dialog" id="DesertPaintLab.FirstRunDialog" design-size="416 189">
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">Set Up Profile</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <property name="Modal">True</property>
 
    <property name="AllowShrink">True</property>
 
    <property name="Buttons">3</property>
 
    <property name="HelpButton">False</property>
 
    <child internal-child="VBox">
 
      <widget class="Gtk.VBox" id="dialog1_VBox">
 
        <property name="MemberName" />
 
        <property name="BorderWidth">2</property>
 
        <child>
 
          <widget class="Gtk.Label" id="label2">
 
            <property name="MemberName" />
 
            <property name="LabelProp" translatable="yes">Since this is your first time using this program, you need a new profile.
 

	
 
You can either import an existing PracticalPaint reactions.txt file, or you can start a new profile from scratch.</property>
 
            <property name="Wrap">True</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">0</property>
 
            <property name="AutoSize">False</property>
 
            <property name="Padding">5</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
    <child internal-child="ActionArea">
 
      <widget class="Gtk.HButtonBox" id="dialog1_ActionArea">
 
        <property name="MemberName" />
 
        <property name="Spacing">10</property>
 
        <property name="BorderWidth">5</property>
 
        <property name="Size">3</property>
 
        <property name="LayoutStyle">End</property>
 
        <child>
 
          <widget class="Gtk.Button" id="buttonCancel">
 
            <property name="MemberName" />
 
            <property name="CanDefault">True</property>
 
            <property name="CanFocus">True</property>
 
            <property name="UseStock">True</property>
 
            <property name="Type">StockItem</property>
 
            <property name="StockId">gtk-cancel</property>
 
            <property name="ResponseId">-6</property>
 
            <property name="label">gtk-cancel</property>
 
          </widget>
 
          <packing>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.Button" id="buttonImport">
 
            <property name="MemberName" />
 
            <property name="CanDefault">True</property>
 
            <property name="CanFocus">True</property>
 
            <property name="Type">TextOnly</property>
 
            <property name="Label" translatable="yes">Import reactions.txt</property>
 
            <property name="UseUnderline">True</property>
 
            <property name="ResponseId">-3</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">1</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.Button" id="buttonNew">
 
            <property name="MemberName" />
 
            <property name="CanFocus">True</property>
 
            <property name="Type">TextOnly</property>
 
            <property name="Label" translatable="yes">New Profile</property>
 
            <property name="UseUnderline">True</property>
 
            <property name="ResponseId">-5</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">2</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
  </widget>
 
  <widget class="Gtk.Dialog" id="DesertPaintLab.SelectProfileDialog" design-size="400 288">
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">Open Profile</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <property name="Modal">True</property>
 
    <property name="BorderWidth">5</property>
 
    <property name="Buttons">3</property>
 
    <property name="HelpButton">False</property>
 
    <child internal-child="VBox">
 
      <widget class="Gtk.VBox" id="dialog1_VBox">
 
        <property name="MemberName" />
 
        <property name="Spacing">10</property>
 
        <property name="BorderWidth">2</property>
 
        <child>
 
          <widget class="Gtk.Label" id="label3">
 
            <property name="MemberName" />
 
            <property name="LabelProp" translatable="yes">Select the profile you would like to open:</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">0</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.TreeView" id="profileListView">
 
            <property name="MemberName" />
 
            <property name="CanFocus">True</property>
 
            <property name="EnableSearch">False</property>
 
            <signal name="CursorChanged" handler="OnCursorChanged" />
 
          </widget>
 
          <packing>
 
            <property name="Position">1</property>
 
            <property name="AutoSize">True</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
    <child internal-child="ActionArea">
 
      <widget class="Gtk.HButtonBox" id="dialog1_ActionArea">
 
        <property name="MemberName" />
 
        <property name="Spacing">10</property>
 
        <property name="BorderWidth">5</property>
 
        <property name="Size">3</property>
 
        <property name="LayoutStyle">End</property>
 
        <child>
 
          <widget class="Gtk.Button" id="buttonCancel">
 
            <property name="MemberName" />
 
            <property name="CanDefault">True</property>
 
            <property name="CanFocus">True</property>
 
            <property name="UseStock">True</property>
 
            <property name="Type">StockItem</property>
 
            <property name="StockId">gtk-cancel</property>
 
            <property name="ResponseId">-6</property>
 
            <property name="label">gtk-cancel</property>
 
          </widget>
 
          <packing>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.Button" id="button56">
 
            <property name="MemberName" />
 
            <property name="CanFocus">True</property>
 
            <property name="Type">TextOnly</property>
 
            <property name="Label" translatable="yes">New Profile</property>
 
            <property name="UseUnderline">True</property>
 
            <property name="ResponseId">-3</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">1</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.Button" id="buttonOk">
 
            <property name="MemberName" />
 
            <property name="CanDefault">True</property>
 
            <property name="CanFocus">True</property>
 
            <property name="UseStock">True</property>
 
            <property name="Type">StockItem</property>
 
            <property name="StockId">gtk-ok</property>
 
            <property name="ResponseId">-5</property>
 
            <property name="label">gtk-ok</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">2</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
  </widget>
 
  <widget class="Gtk.Dialog" id="DesertPaintLab.NewProfileDialog" design-size="318 177">
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">New Profile</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <property name="BorderWidth">5</property>
 
    <property name="Buttons">2</property>
 
    <property name="HelpButton">False</property>
 
    <child internal-child="VBox">
 
      <widget class="Gtk.VBox" id="dialog1_VBox">
 
        <property name="MemberName" />
 
        <property name="Spacing">10</property>
 
        <property name="BorderWidth">9</property>
 
        <child>
 
          <widget class="Gtk.Label" id="label1">
 
            <property name="MemberName" />
 
            <property name="LabelProp" translatable="yes">Name your new profile:</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">0</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.Entry" id="profileNameEntry">
 
            <property name="MemberName" />
 
            <property name="CanFocus">True</property>
 
            <property name="IsEditable">True</property>
 
            <property name="InvisibleChar">●</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">1</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
    <child internal-child="ActionArea">
 
      <widget class="Gtk.HButtonBox" id="dialog1_ActionArea">
 
        <property name="MemberName" />
 
        <property name="Spacing">10</property>
 
        <property name="BorderWidth">5</property>
 
        <property name="Size">2</property>
 
        <property name="LayoutStyle">End</property>
 
        <child>
 
          <widget class="Gtk.Button" id="buttonCancel">
 
            <property name="MemberName" />
 
            <property name="CanDefault">True</property>
 
            <property name="CanFocus">True</property>
 
            <property name="UseStock">True</property>
 
            <property name="Type">StockItem</property>
 
            <property name="StockId">gtk-cancel</property>
 
            <property name="ResponseId">-6</property>
 
            <property name="label">gtk-cancel</property>
 
          </widget>
 
          <packing>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.Button" id="buttonOk">
 
            <property name="MemberName" />
 
            <property name="CanDefault">True</property>
 
            <property name="CanFocus">True</property>
 
            <property name="UseStock">True</property>
 
            <property name="Type">StockItem</property>
 
            <property name="StockId">gtk-ok</property>
 
            <property name="ResponseId">-5</property>
 
            <property name="label">gtk-ok</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">1</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
  </widget>
 
  <widget class="Gtk.Bin" id="DesertPaintLab.PaintSwatch" design-size="245 235">
 
    <property name="MemberName" />
 
    <property name="Visible">False</property>
 
    <child>
 
      <widget class="Gtk.VBox" id="vbox1">
 
        <property name="MemberName" />
 
        <property name="Spacing">6</property>
 
        <child>
 
          <widget class="Gtk.Label" id="colorNameLabel">
 
            <property name="MemberName" />
 
            <property name="LabelProp" translatable="yes">Unknown</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">0</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.DrawingArea" id="colorBox">
 
            <property name="MemberName" />
 
          </widget>
 
          <packing>
 
            <property name="Position">1</property>
 
            <property name="AutoSize">True</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.Label" id="rgbLabel">
 
            <property name="MemberName" />
 
            <property name="LabelProp" translatable="yes">???, ???, ???</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">2</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
  </widget>
 
  <widget class="Gtk.Window" id="DesertPaintLab.SimulatorWindow" design-size="804 390">
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">Simulator</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <child>
 
      <widget class="Gtk.HBox" id="hbox1">
 
        <property name="MemberName" />
 
        <property name="Spacing">6</property>
 
        <property name="BorderWidth">12</property>
 
        <child>
 
          <widget class="Gtk.ScrolledWindow" id="GtkScrolledWindow">
 
            <property name="MemberName" />
 
            <property name="ShadowType">In</property>
 
            <child>
 
              <widget class="Gtk.TreeView" id="reagentListView">
 
                <property name="MemberName" />
 
                <property name="CanFocus">True</property>
 
                <property name="ShowScrollbars">True</property>
 
              </widget>
 
            </child>
 
          </widget>
 
          <packing>
 
            <property name="Position">0</property>
 
            <property name="AutoSize">True</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.VBox" id="vbox162">
 
            <property name="MemberName" />
 
            <property name="Spacing">6</property>
 
            <child>
 
              <widget class="Gtk.Alignment" id="alignment1">
 
                <property name="MemberName" />
 
                <child>
 
                  <placeholder />
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">0</property>
 
                <property name="AutoSize">True</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Button" id="addReagentButton">
 
                <property name="MemberName" />
 
                <property name="CanFocus">True</property>
 
                <property name="Type">Custom</property>
 
                <signal name="Clicked" handler="OnAddReagent" />
 
                <child>
 
                  <widget class="Gtk.Image" id="addReagentButtonImage">
 
                    <property name="MemberName" />
 
                    <property name="Pixbuf">stock:gtk-go-forward Menu</property>
 
                  </widget>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">1</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Alignment" id="alignment3">
 
                <property name="MemberName" />
 
                <child>
 
                  <placeholder />
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">2</property>
 
                <property name="AutoSize">True</property>
 
              </packing>
 
            </child>
 
          </widget>
 
          <packing>
 
            <property name="Position">1</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.ScrolledWindow" id="GtkScrolledWindow1">
 
            <property name="MemberName" />
 
            <property name="ShadowType">In</property>
 
            <child>
 
              <widget class="Gtk.TreeView" id="recipeView">
 
                <property name="MemberName" />
 
                <property name="CanFocus">True</property>
 
                <property name="ShowScrollbars">True</property>
 
                <property name="EnableSearch">False</property>
 
                <property name="Reorderable">True</property>
 
              </widget>
 
            </child>
 
          </widget>
 
          <packing>
 
            <property name="Position">2</property>
 
            <property name="AutoSize">True</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.VBox" id="vbox3">
 
            <property name="MemberName" />
 
            <property name="Spacing">6</property>
 
            <child>
 
              <widget class="Gtk.Alignment" id="alignment2">
 
                <property name="MemberName" />
 
                <child>
 
                  <placeholder />
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">0</property>
 
                <property name="AutoSize">True</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Button" id="button65">
 
                <property name="MemberName" />
 
                <property name="CanFocus">True</property>
 
                <property name="Type">Custom</property>
 
                <signal name="Clicked" handler="OnIncrementReagent" />
 
                <child>
 
                  <widget class="Gtk.Image" id="image1">
 
                    <property name="MemberName" />
 
                    <property name="Pixbuf">stock:gtk-add Menu</property>
 
                  </widget>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">1</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Button" id="button66">
 
                <property name="MemberName" />
 
                <property name="CanFocus">True</property>
 
                <property name="Type">Custom</property>
 
                <signal name="Clicked" handler="OnDecrementReagent" />
 
                <child>
 
                  <widget class="Gtk.Image" id="image2">
 
                    <property name="MemberName" />
 
                    <property name="Pixbuf">stock:gtk-remove Menu</property>
 
                  </widget>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">2</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Button" id="button67">
 
                <property name="MemberName" />
 
                <property name="CanFocus">True</property>
 
                <property name="Type">Custom</property>
 
                <signal name="Clicked" handler="OnFlushReagents" />
 
                <child>
 
                  <widget class="Gtk.Image" id="image3">
 
                    <property name="MemberName" />
 
                    <property name="Pixbuf">stock:gtk-goto-first Menu</property>
 
                  </widget>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">3</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Alignment" id="alignment4">
 
                <property name="MemberName" />
 
                <child>
 
                  <placeholder />
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">4</property>
 
                <property name="AutoSize">True</property>
 
              </packing>
 
            </child>
 
          </widget>
 
          <packing>
 
            <property name="Position">3</property>
 
            <property name="AutoSize">True</property>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="DesertPaintLab.PaintSwatch" id="paintSwatch">
 
            <property name="MemberName" />
 
            <property name="WidthRequest">200</property>
 
            <property name="Events">ButtonPressMask</property>
 
          </widget>
 
          <packing>
 
            <property name="Position">4</property>
 
            <property name="AutoSize">False</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
  </widget>
 
  <widget class="Gtk.Dialog" id="DesertPaintLab.ScreenCheckDialog" design-size="378 245">
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">Screen Check</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <property name="Modal">True</property>
 
    <property name="BorderWidth">9</property>
 
    <property name="Buttons">1</property>
 
    <property name="HelpButton">False</property>
 
    <child internal-child="VBox">
 
      <widget class="Gtk.VBox" id="dialog1_VBox">
 
        <property name="MemberName" />
 
        <child>
 
          <widget class="Gtk.VBox" id="vbox2">
 
            <property name="MemberName" />
 
            <property name="Spacing">20</property>
 
            <property name="BorderWidth">20</property>
 
            <child>
 
              <widget class="Gtk.HBox" id="hbox1">
 
                <property name="MemberName" />
 
                <property name="Spacing">20</property>
 
                <property name="BorderWidth">10</property>
 
                <child>
 
                  <widget class="Gtk.Label" id="label1">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">Screen Resolution</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">0</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.Entry" id="screenWidthEntry">
 
                    <property name="MemberName" />
 
                    <property name="WidthRequest">50</property>
 
                    <property name="CanFocus">True</property>
 
                    <property name="IsEditable">True</property>
 
                    <property name="InvisibleChar">●</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">1</property>
 
                    <property name="AutoSize">True</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.Entry" id="screenHeightEntry">
 
                    <property name="MemberName" />
 
                    <property name="WidthRequest">50</property>
 
                    <property name="CanFocus">True</property>
 
                    <property name="IsEditable">True</property>
 
                    <property name="InvisibleChar">●</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">2</property>
 
                    <property name="AutoSize">True</property>
 
                  </packing>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">0</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.HBox" id="hbox2">
 
                <property name="MemberName" />
 
                <property name="Spacing">20</property>
 
                <property name="BorderWidth">10</property>
 
                <child>
 
                  <widget class="Gtk.Label" id="label2">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">Game Pixel Width in Screen Pixels</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">0</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.Entry" id="gamePixelWidthEntry">
 
                    <property name="MemberName" />
 
                    <property name="WidthRequest">50</property>
 
                    <property name="CanFocus">True</property>
 
                    <property name="IsEditable">True</property>
 
                    <property name="InvisibleChar">●</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">1</property>
 
                    <property name="AutoSize">False</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <placeholder />
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">1</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <placeholder />
 
            </child>
 
          </widget>
 
          <packing>
 
            <property name="Position">0</property>
 
            <property name="AutoSize">True</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
    <child internal-child="ActionArea">
 
      <widget class="Gtk.HButtonBox" id="dialog1_ActionArea">
 
        <property name="MemberName" />
 
        <property name="Spacing">10</property>
 
        <property name="BorderWidth">5</property>
 
        <property name="Size">1</property>
 
        <property name="LayoutStyle">End</property>
 
        <child>
 
          <widget class="Gtk.Button" id="buttonOk">
 
            <property name="MemberName" />
 
            <property name="CanDefault">True</property>
 
            <property name="CanFocus">True</property>
 
            <property name="UseStock">True</property>
 
            <property name="Type">StockItem</property>
 
            <property name="StockId">gtk-ok</property>
 
            <property name="ResponseId">-5</property>
 
            <property name="label">gtk-ok</property>
 
          </widget>
 
          <packing>
 
            <property name="Expand">False</property>
 
            <property name="Fill">False</property>
 
          </packing>
 
        </child>
 
      </widget>
 
    </child>
 
  </widget>
 
  <widget class="Gtk.Window" id="DesertPaintLab.ReactionStatusWindow" design-size="400 300">
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">Reaction Status</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <child>
 
      <widget class="Gtk.ScrolledWindow" id="scroller">
 
        <property name="MemberName" />
 
        <property name="CanFocus">True</property>
 
        <property name="ShadowType">In</property>
 
        <child>
 
          <widget class="Gtk.Viewport" id="GtkViewport">
 
            <property name="MemberName" />
 
            <property name="ShadowType">None</property>
 
            <child>
 
              <widget class="Gtk.VBox" id="resultbox">
 
                <property name="MemberName" />
 
                <property name="Spacing">6</property>
 
                <child>
 
                  <placeholder />
 
                </child>
 
                <child>
 
                  <placeholder />
 
                </child>
 
                <child>
 
                  <placeholder />
 
                </child>
 
              </widget>
 
            </child>
 
          </widget>
 
        </child>
 
      </widget>
 
    </child>
 
  </widget>
 
</stetic-interface>
...
 
\ No newline at end of file
mac/Info.plist
Show inline comments
 
new file 100644
 
<?xml version="1.0" encoding="UTF-8"?>
 
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 
<plist version="1.0">
 
<dict>
 
	<key>CFBundleDevelopmentRegion</key>
 
	<string>English</string>
 
	<key>CFBundleExecutable</key>
 
	<string>launcher.sh</string>
 
	<key>CFBundleIconFile</key>
 
	<string>DesertPaintLab.icns</string>
 
	<key>CFBundleIdentifier</key>
 
	<string>org.malkyne.desert-paint-lab</string>
 
	<key>CFBundleInfoDictionaryVersion</key>
 
	<string>6.0</string>
 
	<key>CFBundleName</key>
 
	<string>Desert Paint Lab</string>
 
	<key>CFBundlePackageType</key>
 
	<string>APPL</string>
 
	<key>CFBundleShortVersionString</key>
 
	<string>1.7.4</string>
 
	<key>CFBundleSignature</key>
 
	<string>xmmd</string>
 
	<key>CFBundleVersion</key>
 
	<string>1.7.4</string>
 
	<key>NSAppleScriptEnabled</key>
 
	<string>NO</string>
 
</dict>
 
</plist>
mac/build_mac_bundle.sh
Show inline comments
 
new file 100644
 
#!/bin/sh
 

	
 
/bin/mv DesertPaintLab.app DesertPaintLab.app.`/bin/date +"%Y%m%d%H%M%S"`
 
/bin/mkdir -p DesertPaintLab.app
 
/bin/cp Info.plist DesertPaintLab.app/Info.plist
 
/bin/mkdir -p DesertPaintLab.app/Contents/MacOS
 
/bin/cp launcher.sh DesertPaintLab.app/Contents/MacOS
 
/bin/chmod 755 DesertPaintLab.app/Contents/MacOS/launcher.sh
 
/bin/cp ../bin/Release/DesertPaintLab.exe DesertPaintLab.app/Contents/MacOS/
 
/bin/mkdir -p DesertPaintLab.app/Contents/Resources
 
/bin/cp ../bin/Release/colors.txt DesertPaintLab.app/Contents/Resources/
 
/bin/cp -r ../bin/Release/template DesertPaintLab.app/Contents/Resources/template
mac/launcher.sh
Show inline comments
 
new file 100755
 
#!/bin/sh
 

	
 
APPNAME="DesertPaintLab"
 

	
 
DIR=$(cd "$(dirname "$0")"; pwd)
 

	
 
EXE_PATH="$DIR\DesertPaintLab.exe"
 
PROCESS_NAME=desertpaintlab
 

	
 
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
 
export DYLD_FALLBACK_LIBRARY_PATH="$DIR:$MONO_FRAMEWORK_PATH/lib:/lib:/usr/lib"
 
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
 
 
 
#mono version check
 
 
 
REQUIRED_MAJOR=4
 
REQUIRED_MINOR=0
 
 
 
VERSION_TITLE="Cannot launch $APPNAME"
 
VERSION_MSG="$APPNAME requires the Mono Framework version $REQUIRED_MAJOR.$REQUIRED_MINOR or later."
 
DOWNLOAD_URL="http://www.go-mono.com/mono-downloads/download.html"
 
 
 
MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' |  cut -f5 -d\ )"
 
MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
 
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
 
if [ -z "$MONO_VERSION" ] \
 
	|| [ $MONO_VERSION_MAJOR -lt $REQUIRED_MAJOR ] \
 
	|| [ $MONO_VERSION_MAJOR -eq $REQUIRED_MAJOR -a $MONO_VERSION_MINOR -lt $REQUIRED_MINOR ] 
 
then
 
	/usr/bin/osascript \
 
		-e "set question to display dialog \"$VERSION_MSG\" with title \"$VERSION_TITLE\" buttons {\"Cancel\", \"Download...\"} default button 2" \
 
		-e "if button returned of question is equal to \"Download...\" then open location \"$DOWNLOAD_URL\""
 
	echo "$VERSION_TITLE"
 
	echo "$VERSION_MSG"
 
	exit 1
 
fi
 

	
 
OSX_VERSION=$(uname -r | cut -f1 -d.)
 
if [ $OSX_VERSION -lt 9 ]; then  # If OSX version is 10.4
 
	MONO_EXEC="exec mono"
 
else
 
	MONO_EXEC="exec -a \"$PROCESS_NAME\" mono"
 
fi
 

	
 
LOG_FILE="$HOME/Library/Logs/$APPNAME/$APPNAME.log"
 
/bin/mkdir -p "`dirname \"$LOG_FILE\"`"
 
$MONO_EXEC $MONO_OPTIONS "$EXE_PATH" $* 2>&1 1> "$LOG_FILE"
 

	
0 comments (0 inline, 0 general)