Changeset - 2040107278aa
[Not reviewed]
default
0 16 8
Jason Maltzen (jmaltzen) - 8 years ago 2015-12-23 21:25:07
jason.maltzen@unsanctioned.net
Fixes #8 #9 #6 #3 : Order of ingredients should now match the paint bench. Basic brute-force recipe generation implementation. Fixed rounding error on the simulator. Give feedback when using the simulator if reactions are missing. Also: adds an interface for modifying ingredient settings for recipe generation, adds a help option to view missing recorded reactions
24 files changed with 3360 insertions and 230 deletions:
0 comments (0 inline, 0 general)
DesertPaintLab.csproj
Show inline comments
...
 
@@ -75,6 +75,12 @@
 
    <Compile Include="ReactionRecorder.cs" />
 
    <Compile Include="ReactionStatusWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.ReactionStatusWindow.cs" />
 
    <Compile Include="RecipeGeneratorWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.RecipeGeneratorWindow.cs" />
 
    <Compile Include="ReactionSet.cs" />
 
    <Compile Include="RecipeGenerator.cs" />
 
    <Compile Include="ReagentWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.ReagentWindow.cs" />
 
  </ItemGroup>
 
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 
  <ProjectExtensions>
MainWindow.cs
Show inline comments
...
 
@@ -29,15 +29,6 @@ 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;
...
 
@@ -94,6 +85,9 @@ public partial class MainWindow : Gtk.Wi
 

	
 
        string colorsPath = FileUtils.FindApplicationResourceFile("colors.txt");
 
		Palette.Load(colorsPath);
 

	
 
        string ingredientsPath = FileUtils.FindApplicationResourceFile("ingredients.txt");
 
        ReagentManager.Load(ingredientsPath);
 
		
 
		Build();
 
		
...
 
@@ -134,11 +128,7 @@ public partial class MainWindow : Gtk.Wi
 

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

	
 
		if (!OpenProfile())
 
		{
...
 
@@ -345,6 +335,7 @@ public partial class MainWindow : Gtk.Wi
 
		{
 
			profile.Load();
 
			PopulateDropDowns();
 
            recipe.Reactions = profile.Reactions;
 
		}
 

	
 
		return profileSelected;
...
 
@@ -493,13 +484,13 @@ public partial class MainWindow : Gtk.Wi
 
						}
 
					}
 
				}
 
                recipe.ComputeBaseColor(ref expectedColor);
 
                expectedColor.Set(recipe.BaseColor);
 
                unmodifiedSwatch.Color = expectedColor;
 
                //SetExpectedColor(recipeColor.Red, recipeColor.Green, recipeColor.Blue);
 
				
 
				if (reactionKnown)
 
				{
 
                    recipe.ComputeReactedColor(profile, ref reactedColor);
 
                    reactedColor.Set(recipe.ReactedColor);
 
					reactionSwatch.Color = reactedColor;
 
				}
 
				else
...
 
@@ -538,8 +529,9 @@ public partial class MainWindow : Gtk.Wi
 
		//	rootWindow.Colormap, 0, 0, 0, 0, screenWidth, screenHeight);
 
		int stride = screenBuffer.Rowstride;
 
		byte* pixBytes = (byte*)screenBuffer.Pixels;
 
        int redPixelStart = -1;
 
	
 
        bool wasCaptured = ReactionRecorder.CaptureReaction(pixBytes, screenWidth, screenHeight, stride, ref reactedColor);
 
        bool wasCaptured = ReactionRecorder.CaptureReaction(pixBytes, screenWidth, screenHeight, stride, ref reactedColor, ref redPixelStart);
 
        if (!wasCaptured && enableDebugMenu)
 
        {
 
            // write out the screenshot
...
 
@@ -553,9 +545,29 @@ public partial class MainWindow : Gtk.Wi
 
            } while (System.IO.File.Exists(filename));
 
            screenBuffer.Save(filename, "png");
 
        }
 
        else
 
        {
 
            // convert to pixel offset instead of byte
 
            int redPixelStartX = (redPixelStart % stride) / 3;
 
            int redPixelStartY = (redPixelStart / stride);
 
            // write out the screenshot
 
            string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
 
            string filename;
 
            int i = 0;
 
            do
 
            {
 
                ++i;
 
                filename = System.IO.Path.Combine(screenshotDir, String.Format("DesertPaintLab_Colormatch{0}.png", i));
 
            } while (System.IO.File.Exists(filename));
 
            int captureAreaWidth = Math.Min(64, screenWidth - redPixelStartX + 64);
 
            int captureAreaHeight = Math.Min(64, screenWidth - redPixelStartY + 64);
 
            Gdk.Pixbuf outPixBuf = new Gdk.Pixbuf(screenBuffer, Math.Max(0, redPixelStartX - 16), Math.Max(0, redPixelStartY - 16), captureAreaWidth, captureAreaHeight);
 
            //screenBuffer.Save(filename, "png");
 
            outPixBuf.Save(filename, "png");
 
        }
 
        //screenBuffer.Save("screenshot.png", "png");
 
		
 
		return !wasCaptured;
 
		return wasCaptured;
 
	}
 

	
 
    protected virtual void OnDebugScreenshot(object sender, System.EventArgs e)
...
 
@@ -628,6 +640,7 @@ public partial class MainWindow : Gtk.Wi
 
	            MessageType.Error, ButtonsType.Ok, 
 
	            "Pigment Lab dialog box NOT FOUND.  Please ensure " +
 
	            "that there is an unobstructed view of the dialog " +
 
                "and that your interface size is set to 'small' " +
 
	            "when you press the Capture button.");
 
	   
 
			md.Run();
...
 
@@ -681,6 +694,7 @@ public partial class MainWindow : Gtk.Wi
 
		{
 
			profile.Load();
 
			PopulateDropDowns();
 
            recipe.Reactions = profile.Reactions;
 
		}
 
	}
 
	
...
 
@@ -753,7 +767,16 @@ public partial class MainWindow : Gtk.Wi
 
	
 
    protected void OnOpenRecipeGenerator(object sender, EventArgs e)
 
    {
 
        throw new NotImplementedException();
 
        RecipeGeneratorWindow win = new RecipeGeneratorWindow(profile);
 
        win.Show();
 
        //RecipeGenerator gen = new RecipeGenerator();
 
        //gen.BeginRecipeGeneration(profile.Reactions, 15, 7);
 
        //MessageDialog md = new MessageDialog(this, 
 
        //    DialogFlags.DestroyWithParent,
 
        //    MessageType.Info, ButtonsType.Close, 
 
        //    "Coming Soon!");
 
        //md.Run();
 
        //md.Destroy();
 
    }
 

	
 
    protected void OnShowReactionStatus(object sender, EventArgs e)
...
 
@@ -761,5 +784,11 @@ public partial class MainWindow : Gtk.Wi
 
        ReactionStatusWindow win = new ReactionStatusWindow(profile);
 
        win.Show();
 
    }
 

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

	
PaintColor.cs
Show inline comments
...
 
@@ -83,6 +83,14 @@ namespace DesertPaintLab
 
			this.green = green;
 
			this.blue = blue;			
 
		}
 

 
        public PaintColor(PaintColor other)
 
        {
 
            name = other.name;
 
            red = other.red;
 
            green = other.green;
 
            blue = other.blue;
 
        }
 
				
 
		public int GetDistanceSquared(PaintColor otherColor)
 
		{
...
 
@@ -97,6 +105,14 @@ namespace DesertPaintLab
 
			green = 0;
 
			blue = 0;
 
		}
 

 
        public void Set(PaintColor other)
 
        {
 
            this.red = other.red;
 
            this.green = other.green;
 
            this.blue = other.blue;
 
            this.name = other.name;
 
        }
 
		
 
		public override string ToString()
 
		{
PaintRecipe.cs
Show inline comments
 
using System;
 
/*
 
 * Copyright (c) 2015, Jason Maltzen
 

	
 
 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
...
 
@@ -12,48 +34,147 @@ namespace DesertPaintLab
 
            public int b;
 
        };
 

	
 
        public class RecipeIngredient
 
        {
 
            public string name;
 
            public uint quantity;
 

	
 
            public RecipeIngredient(string name, uint quantity)
 
            {
 
                this.name = name;
 
                this.quantity = quantity;
 
            }
 
        };
 

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

	
 
        private bool dirty = false;
 
        private PaintColor reactedColor = new PaintColor();
 
        private PaintColor baseColor = new PaintColor();
 
        private ReactionSet reactions;
 

	
 
        public PaintRecipe()
 
        {
 
        }
 

	
 
        public List<RecipeIngredient> Ingredients
 
        {
 
            get {
 
                return recipe;
 
            }
 
        }
 

	
 
        public ReactionSet Reactions
 
        {
 
            get
 
            {
 
                return reactions;
 
            }
 
            set
 
            {
 
                dirty = true;
 
                reactions = value;
 
            }
 
        }
 

	
 
        public PaintColor ReactedColor
 
        {
 
            get
 
            {
 
                if (dirty)
 
                {
 
                    ComputeBaseColor();
 
                    ComputeReactedColor();
 
                    dirty = false;
 
                }
 
                return reactedColor;
 
            }
 
        }
 

	
 
        public PaintColor BaseColor
 
        {
 
            get
 
            {
 
                if (dirty)
 
                {
 
                    ComputeBaseColor();
 
                    ComputeReactedColor();
 
                    dirty = false;
 
                }
 
                return baseColor;
 
            }
 
        }
 

	
 
        public void AddReagent(String reagentName)
 
        {
 
            AddReagent(reagentName, 1);
 
        }
 

	
 
        public void AddReagent(String reagentName, uint quantity)
 
        {
 
            if (quantity == 0)
 
            {
 
                return;
 
            }
 
            Reagent reagent = ReagentManager.GetReagent(reagentName);
 
            if (reagent == null)
 
            {
 
                Console.WriteLine("ERROR: invalid reagent {0}", reagentName);
 
                return;
 
            }
 

	
 
            RecipeIngredient ingredient = recipe.Find(x => x.name.Equals(reagentName));
 
            if (ingredient != null)
 
            {
 
                if (!reagent.IsCatalyst)
 
                {
 
                    ingredient.quantity += quantity;
 
                    dirty = true;
 
                }
 
            }
 
            else
 
            {
 
                RecipeIngredient newIngredient = new RecipeIngredient(reagentName, reagent.IsCatalyst ? 1 : quantity);
 
                recipe.Add(newIngredient);
 
                dirty = true;
 
            }
 
            reagents.Add(reagentName);
 
        }
 

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

	
 
        byte CalculateColor(int baseSum, int pigmentCount, int reactSum)
 
        byte CalculateColor(int baseSum, uint pigmentCount, int reactSum)
 
        {
 
            return (byte)Math.Max(Math.Min(Math.Round((((float)baseSum / (float)pigmentCount) + (float)reactSum)), 255), 0);
 
            // Changed to Math.Floor from Math.Round, since Round appears to be incorrect.
 
            return (byte)Math.Max(Math.Min(Math.Floor((((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)
 
        private void ComputeReactedColor()
 
        {
 
            RGB baseColor;
 
            baseColor.r = 0;
 
            baseColor.g = 0;
 
            baseColor.b = 0;
 
            RGB baseRGB;
 
            baseRGB.r = 0;
 
            baseRGB.g = 0;
 
            baseRGB.b = 0;
 
            RGB reactionColor;
 
            reactionColor.r = 0;
 
            reactionColor.g = 0;
 
            reactionColor.b = 0;
 

	
 
            int pigmentCount = 0;
 
            string prevReagent = null;
 
            uint pigmentCount = 0;
 

	
 
            // 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)
 
            foreach (RecipeIngredient ingredient in recipe)
 
            {
 
                string reagentName = ingredient.name;
 
                if (reagentName == null)
 
                {
 
                    continue;   
...
 
@@ -62,47 +183,45 @@ namespace DesertPaintLab
 
                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;
 
                    baseRGB.r += (reagent.Color.Red * (int)ingredient.quantity);
 
                    baseRGB.g += (reagent.Color.Green * (int)ingredient.quantity);
 
                    baseRGB.b += (reagent.Color.Blue * (int)ingredient.quantity);
 
                    pigmentCount += ingredient.quantity;
 
                }
 
                if (prevReagent == null || !prevReagent.Equals(reagentName))
 

	
 
                if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
 
                {
 
                    if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
 
                    reagentSet[reagentName] = true;
 
                    // Run reactions.
 
                    foreach (Reagent otherReagent in prevReagents)
 
                    {
 
                        reagentSet[reagentName] = true;
 
                        // Run reactions.
 
                        foreach (Reagent otherReagent in prevReagents)
 
                        Reaction reaction = reactions.Find(otherReagent, reagent);
 
                        if (reaction != null)
 
                        {
 
                            Reaction reaction = profile.FindReaction(otherReagent, reagent);
 
                            if (reaction != null)
 
                            {
 
                                reactionColor.r += reaction.Red;
 
                                reactionColor.g += reaction.Green;
 
                                reactionColor.b += reaction.Blue;
 
                            }
 
                            reactionColor.r += reaction.Red;
 
                            reactionColor.g += reaction.Green;
 
                            reactionColor.b += reaction.Blue;
 
                        }
 
                        prevReagents.Add(reagent);
 
                    }
 
                    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);
 
            reactedColor.Red = CalculateColor(baseRGB.r, pigmentCount, reactionColor.r);
 
            reactedColor.Green = CalculateColor(baseRGB.g, pigmentCount, reactionColor.g);
 
            reactedColor.Blue = CalculateColor(baseRGB.b, pigmentCount, reactionColor.b);
 
        }
 

	
 
        // Compute the base color without any reactions
 
        public void ComputeBaseColor(ref PaintColor color)
 
        private void ComputeBaseColor()
 
        {
 
            RGB baseColor;
 
            baseColor.r = 0;
 
            baseColor.g = 0;
 
            baseColor.b = 0;
 
            int pigmentCount = 0;
 
            foreach (string reagentName in reagents)
 
            RGB baseRGB;
 
            baseRGB.r = 0;
 
            baseRGB.g = 0;
 
            baseRGB.b = 0;
 
            uint pigmentCount = 0;
 
            foreach (RecipeIngredient ingredient in recipe)
 
            {
 
                string reagentName = ingredient.name;
 
                if (reagentName == null)
 
                {
 
                    continue;   
...
 
@@ -111,15 +230,110 @@ namespace DesertPaintLab
 
                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;
 
                    baseRGB.r += (reagent.Color.Red * (int)ingredient.quantity);
 
                    baseRGB.g += (reagent.Color.Green * (int)ingredient.quantity);
 
                    baseRGB.b += (reagent.Color.Blue * (int)ingredient.quantity);
 
                    pigmentCount += ingredient.quantity;
 
                }
 
            }
 
            color.Red = CalculateColor(baseColor.r, pigmentCount, 0);
 
            color.Green = CalculateColor(baseColor.g, pigmentCount, 0);
 
            color.Blue = CalculateColor(baseColor.b, pigmentCount, 0);
 
            baseColor.Red = CalculateColor(baseRGB.r, pigmentCount, 0);
 
            baseColor.Green = CalculateColor(baseRGB.g, pigmentCount, 0);
 
            baseColor.Blue = CalculateColor(baseRGB.b, pigmentCount, 0);
 
        }
 

	
 
        // Compute the base color without any reactions
 
        public uint Cost
 
        {
 
            get {
 
                uint cost = 0;
 
                foreach (RecipeIngredient ingredient in recipe)
 
                {
 
                    string reagentName = ingredient.name;
 
                    if (reagentName == null)
 
                    {
 
                        continue;   
 
                    }
 
                    
 
                    Reagent reagent = ReagentManager.GetReagent(reagentName);
 
                    cost += (reagent.Cost * ingredient.quantity);
 
                }
 
                return cost;
 
            }
 
        }
 

	
 
        public uint GetBulkCost(uint quantity)
 
        {
 
            uint cost = 0;
 
            foreach (RecipeIngredient ingredient in recipe)
 
            {
 
                string reagentName = ingredient.name;
 
                if (reagentName == null)
 
                {
 
                    continue;   
 
                }
 
                
 
                Reagent reagent = ReagentManager.GetReagent(reagentName);
 
                cost += (reagent.Cost * ingredient.quantity);
 
            }
 
            uint batchCount = (uint)Math.Ceiling((double)quantity / (double)cost);  // number of batches require to make quantity
 
            return batchCount * cost;
 
        }
 

	
 
        public bool IsValid
 
        {
 
            get
 
            {
 
                uint weight = 0;
 
                foreach (RecipeIngredient ingredient in recipe)
 
                {
 
                    string reagentName = ingredient.name;
 
                    if (reagentName == null)
 
                    {
 
                        continue;   
 
                    }
 
                    
 
                    Reagent reagent = ReagentManager.GetReagent(reagentName);
 
                    if (!reagent.IsCatalyst)
 
                    {
 
                        weight += ingredient.quantity;
 
                    }
 
                }
 
                return (weight >= 10);
 
            }
 
        }
 

	
 
        public bool CheckMissingReactions(ref List<KeyValuePair<string, string>> missing)
 
        {
 
            missing.Clear();
 

	
 
            SortedDictionary<string,bool> reagentSet = new SortedDictionary<string,bool>();
 
            List<Reagent> prevReagents = new List<Reagent>();
 

	
 
            foreach (RecipeIngredient ingredient in recipe)
 
            {
 
                string reagentName = ingredient.name;
 
                if (reagentName == null)
 
                {
 
                    continue;   
 
                }
 
                
 
                Reagent reagent = ReagentManager.GetReagent(reagentName);
 
                if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
 
                {
 
                    reagentSet[reagentName] = true;
 
                    // Run reactions.
 
                    foreach (Reagent otherReagent in prevReagents)
 
                    {
 
                        Reaction reaction = reactions.Find(otherReagent, reagent);
 
                        if (reaction == null)
 
                        {
 
                            missing.Add(new KeyValuePair<string, string>(otherReagent.Name, reagent.Name));
 
                        }
 
                    }
 
                    prevReagents.Add(reagent);
 
                }
 
            }
 
            return (missing.Count > 0);
 
        }
 
    }
 
}
Palette.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;
...
 
@@ -10,6 +32,12 @@ namespace DesertPaintLab
 
		static List<PaintColor> colors = new List<PaintColor>();
 
		static Regex colorEntry = new Regex(@"\#(?<red>\w\w)(?<green>\w\w)(?<blue>\w\w)\s*(?<name>\w+)");
 

 
        public static List<PaintColor> Colors
 
        {
 
            get {
 
                return colors;
 
            }
 
        }
 
		
 
		public Palette ()
 
		{
...
 
@@ -35,6 +63,14 @@ namespace DesertPaintLab
 
                }
 
			}
 
		}
 

 
        public static int Count
 
        {
 
            get
 
            {
 
                return colors.Count;
 
            }
 
        }
 
		
 
		public static string FindNearest(PaintColor color)
 
		{
PlayerProfile.cs
Show inline comments
...
 
@@ -23,6 +23,7 @@
 
using System;
 
using System.IO;
 
using System.Collections.Generic;
 
using System.Text.RegularExpressions;
 

 
namespace DesertPaintLab
 
{
...
 
@@ -31,16 +32,61 @@ namespace DesertPaintLab
 
		string name;
 
		string directory;
 
		string reactFile;
 
        string reagentFile;
 
		
 
		SortedDictionary<string, SortedDictionary<string, Reaction>> reactions =
 
			new SortedDictionary<string, SortedDictionary<string, Reaction>>();
 
        static Regex recipeHeaderRegex = new Regex(@"^--- Recipe: (?<colorname>(\w*\s)*\w+)\s*");
 
        static Regex recipeIngredientRegex = new Regex(@"(?<ingredient>(\w+\s)?\w+)\s*\|\s*(?<quantity>\d+)\s*");
 

 
        ReactionSet reactions = new ReactionSet();
 
        // ingredient -> [ingredient, reaction]
 
		//SortedDictionary<string, SortedDictionary<string, Reaction>> reactions =
 
		//	new SortedDictionary<string, SortedDictionary<string, Reaction>>();
 
        SortedDictionary<string, PaintRecipe> recipes;
 

 
        static PlayerProfile current = null;
 

 
        static PlayerProfile Current
 
        {
 
            get {
 
                return current;
 
            }
 
        }
 
		
 
		public PlayerProfile(string name, string directory)
 
		{
 
			this.name = name;
 
			this.directory = directory;
 
			this.reactFile = System.IO.Path.Combine(directory, "dp_reactions.txt");
 
            this.reagentFile = System.IO.Path.Combine(directory, "ingredients.txt");
 
		}
 

 
        public string Directory
 
        {
 
            get {
 
                return this.directory;
 
            }
 
        }
 

 
        public ReactionSet Reactions
 
        {
 
            get {
 
                return this.reactions;
 
            }
 
        }
 

 
        public string ReagentFile
 
        {
 
            get {
 
                return this.reagentFile;
 
            }
 
        }
 

 
        public SortedDictionary<string, PaintRecipe> Recipes
 
        {
 
            get {
 
                return this.recipes;
 
            }
 
        }
 
		
 
		public void Initialize()
 
		{
...
 
@@ -120,25 +166,26 @@ namespace DesertPaintLab
 
		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 + " | ");
 
                foreach (string reagentName1 in ReagentManager.Names)
 
                {
 
                    // TODO: could be more efficient by only iterating over the names after reagent1
 
                    foreach (string reagentName2 in ReagentManager.Names)
 
                    {
 
                        if (reagentName1.Equals(reagentName2))
 
                        {
 
                            continue;
 
                        }
 
                        Reagent reagent1 = ReagentManager.GetReagent(reagentName1);
 
                        Reagent reagent2 = ReagentManager.GetReagent(reagentName2);
 
                        reaction1 = reactions.Find(reagent1, reagent2);
 
                        if (reaction1 != null && !reaction1.Exported)
 
                        {
 
                            reaction2 = reactions.Find(reagent2, reagent1);
 
                            if (reaction2 != null)
 
                            {
 
								writer.Write(reagent1.PracticalPaintName + " | " + reagent2.PracticalPaintName + " | ");
 
								if ((Math.Abs(reaction1.Red) > Math.Abs(reaction1.Green)) ||
 
								    (Math.Abs(reaction2.Red) > Math.Abs(reaction2.Green)))
 
								{
...
 
@@ -167,14 +214,27 @@ namespace DesertPaintLab
 
			}
 
			
 
			// 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;
 
					}
 
            foreach (string reagentName1 in ReagentManager.Names)
 
            {
 
                // TODO: could be more efficient by only iterating over the names after reagent1
 
                foreach (string reagentName2 in ReagentManager.Names)
 
                {
 
                    if (reagentName1.Equals(reagentName2))
 
                    {
 
                        continue;
 
                    }
 
                    Reagent reagent1 = ReagentManager.GetReagent(reagentName1);
 
                    Reagent reagent2 = ReagentManager.GetReagent(reagentName2);
 
                    reaction1 = reactions.Find(reagent1, reagent2);
 
                    if (reaction1 != null)
 
                    {   
 
                        reaction1.Exported = false;
 
                    }
 
                    reaction2 = reactions.Find(reagent2, reagent1);
 
                    if (reaction2 != null)
 
                    {
 
                        reaction2.Exported = false;
 
                    }
 
				}
 
			}
 
			return true;
...
 
@@ -208,17 +268,17 @@ namespace DesertPaintLab
 
		public void Load()
 
		{
 
			string line;
 
			SortedDictionary<string, Reaction> dict;
 
			reactions.Clear();
 
			ReagentManager.Load(System.IO.Path.Combine(directory, "ingredients.txt"));
 
			ReagentManager.LoadProfileReagents(reagentFile);
 
			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]));
 
                    Reagent reagent1 = ReagentManager.GetReagent(tokens[0]);
 
                    Reagent reagent2 = ReagentManager.GetReagent(tokens[1]);
 
					reactions.Set(reagent1, reagent2, new Reaction(int.Parse(tokens[2]), int.Parse(tokens[3]), int.Parse(tokens[4])));
 
				}
 
			}
 
		}
...
 
@@ -228,43 +288,144 @@ namespace DesertPaintLab
 
			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);
 
						}
 
					}
 
				}
 
			}
 
                foreach (string reagentName1 in ReagentManager.Names)
 
                {
 
                    // TODO: could be more efficient by only iterating over the names after reagent1
 
                    foreach (string reagentName2 in ReagentManager.Names)
 
                    {
 
                        if (reagentName1.Equals(reagentName2))
 
                        {
 
                            continue;
 
                        }
 
                        Reagent reagent1 = ReagentManager.GetReagent(reagentName1);
 
                        Reagent reagent2 = ReagentManager.GetReagent(reagentName2);
 
                        reaction = reactions.Find(reagent1, reagent2);
 
    					if (reaction != null)
 
    					{
 
                                writer.WriteLine(reagent1.PracticalPaintName + " " + reagent2.PracticalPaintName + " " +
 
    							reaction.Red + " " + reaction.Green + " " + reaction.Blue);
 
    					}
 
    				}
 
    			}
 
            }
 
		}
 

 
        public void LoadRecipes()
 
        {
 
            this.recipes = new SortedDictionary<string, PaintRecipe>();
 
            string recipeFile = System.IO.Path.Combine(directory, "dp_recipes.txt");
 
            string line;
 
            Match match;
 
            bool inRecipe = false;
 
            PaintRecipe recipe = null;
 
            string currentRecipeColor = null;
 
            if (File.Exists(recipeFile))
 
            {
 
                using (StreamReader reader = new StreamReader(recipeFile))
 
                {
 
                    while ((line = reader.ReadLine()) != null) 
 
                    {
 
                        match = recipeHeaderRegex.Match(line); 
 
                        if (match.Success)
 
                        {
 
                            if (recipe != null && currentRecipeColor != null)
 
                            {
 
                                recipes.Add(currentRecipeColor, recipe);
 
                            }
 
                            recipe = new PaintRecipe();
 
                            recipe.Reactions = reactions;
 
                            currentRecipeColor = match.Groups["colorname"].Value;
 
                            inRecipe = true;
 
                        }
 
                        else if (inRecipe)
 
                        {
 
                            match = recipeIngredientRegex.Match(line);
 
                            if (match.Success)
 
                            {
 
                                string ingredient = match.Groups["ingredient"].Value;
 
                                uint quantity = uint.Parse(match.Groups["quantity"].Value);
 
                                recipe.AddReagent(ingredient, quantity);
 
                            }
 
                        }
 
                    }
 
                    if (inRecipe)
 
                    {
 
                        recipes.Add(currentRecipeColor, recipe);
 
                    }
 
                }
 
            }
 
        }
 

 
        public void SaveRecipes()
 
        {
 
            if (recipes != null)
 
            {
 
                string recipeFile = System.IO.Path.Combine(directory, "dp_recipes.txt");
 
                using (StreamWriter writer = new StreamWriter(recipeFile, false))
 
                {
 
                    foreach (KeyValuePair<string, PaintRecipe> pair in recipes)
 
                    {
 
                        writer.WriteLine("--- Recipe: {0}", pair.Key);
 
                        foreach (PaintRecipe.RecipeIngredient ingredient in pair.Value.Ingredients)
 
                        {
 
                            writer.WriteLine("{0,-14} | {1}", ingredient.name, ingredient.quantity);
 
                        }
 
                    }
 
                }
 
            }
 
        }
 

 
        public void ExportWikiRecipes(string file)
 
        {
 
            PaintRecipe recipe;
 
            using (StreamWriter writer = new StreamWriter(file))
 
            {
 
                writer.WriteLine("{| class='wikitable sortable' border=\"1\" style=\"background-color:#DEB887;\"");
 
                writer.WriteLine("! Color !! Recipe !! Verified");    
 
                foreach (PaintColor color in Palette.Colors)
 
                {
 
                    writer.WriteLine("|-");
 
                    string colorLine = "| ";
 
                    colorLine += "style=\"font-weight: bold; background-color: #" + color.Red.ToString("X2") + color.Green.ToString("X2") + color.Blue.ToString("X2") + ";";
 
                    if (color.Red < 128 && color.Green < 128 && color.Blue < 128)
 
                    {
 
                        // dark color gets light text
 
                        colorLine += " color: #FFFFFF;";
 
                    }
 
                    colorLine += "\" | " + color.Name + " || ";
 
                    if (recipes.TryGetValue(color.Name, out recipe))
 
                    {
 
                        foreach (PaintRecipe.RecipeIngredient ingredient in recipe.Ingredients)
 
                        {
 
                            colorLine += " " + ingredient.name + " " + ingredient.quantity.ToString();
 
                        }
 
                    } 
 
                    else
 
                    {
 
                        // no recipe
 
                    }
 
                    colorLine += " || ";
 
                    writer.WriteLine(colorLine);
 
                }
 
                writer.WriteLine("|}");
 
            }
 
        }
 
		
 
		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;
 
		{
 
            return reactions.Find(reagent1, reagent2);
 
		}
 
		
 
		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;
 
			}
 
            reactions.Set(reagent1, reagent2, reaction);
 
		}
 

 
        public void SetRecipe(PaintRecipe recipe)
 
        {
 
            string colorName = Palette.FindNearest(recipe.ReactedColor);
 
            recipes[colorName] = recipe;
 
        }
 
	}
 
}
 

Reaction.cs
Show inline comments
 
/*
 
 * Copyright (c) 2015, 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;
 
namespace DesertPaintLab
 
{
ReactionRecorder.cs
Show inline comments
 
using System;
 
/*
 
 * Copyright (c) 2015, Jason Maltzen
 

	
 
 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;
 

	
 
namespace DesertPaintLab
 
{
...
 
@@ -7,11 +29,17 @@ namespace DesertPaintLab
 
    {
 
        const int colorTolerance = 2;
 

	
 
        const int swatchHeight = 24;
 
        const int colorBarWidth = 306;
 
        const int redBarSpacing = 32;
 
        const int greenBarSpacing = 42;
 
        const int blueBarSpacing = 52;
 
        const int DEFAULT_SWATCH_HEIGHT = 24;
 
        const int DEFAULT_COLOR_BAR_WIDTH = 306;
 
        const int DEFAULT_RED_BAR_SPACING = 32;
 
        const int DEFAULT_GREEN_BAR_SPACING = 42;
 
        const int DEFAULT_BLUE_BAR_SPACING = 52;
 

	
 
        static int swatchHeight = DEFAULT_SWATCH_HEIGHT;
 
        static int colorBarWidth = DEFAULT_COLOR_BAR_WIDTH;
 
        static int redBarSpacing = DEFAULT_RED_BAR_SPACING;
 
        static int greenBarSpacing = DEFAULT_GREEN_BAR_SPACING;
 
        static int blueBarSpacing = DEFAULT_BLUE_BAR_SPACING;
 

	
 
        private static bool IsPapyTexture(byte r, byte g, byte b)
 
        {
...
 
@@ -19,9 +47,18 @@ namespace DesertPaintLab
 
                    ((r < 0xF4) && (g < 0xE0) && (b < 0xC4));
 
        }
 

	
 
        unsafe public static bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor)
 
        public static void SetPixelMultiplier(int pixelMultiplier)
 
        {
 
            byte r, g, b;
 
            swatchHeight    = DEFAULT_SWATCH_HEIGHT * pixelMultiplier;
 
            colorBarWidth   = DEFAULT_COLOR_BAR_WIDTH * pixelMultiplier;
 
            redBarSpacing   = DEFAULT_RED_BAR_SPACING * pixelMultiplier;
 
            greenBarSpacing = DEFAULT_GREEN_BAR_SPACING * pixelMultiplier;
 
            blueBarSpacing  = DEFAULT_BLUE_BAR_SPACING * pixelMultiplier;
 
        }
 

	
 
        unsafe public static bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor, ref int redPixelStart)
 
        {
 
            byte pixel_r, pixel_g, pixel_b;
 
            int pixelStart, otherPixelStart;
 
            bool colorMatch;
 
            for (int x = 0; x < screenshotWidth - colorBarWidth; ++x)
...
 
@@ -30,12 +67,12 @@ namespace DesertPaintLab
 
                {
 
                    // Look for the color swatch.
 
                    pixelStart = (y * stride) + (x * 3);
 
                    r = pixBytes[pixelStart];
 
                    g = pixBytes[pixelStart + 1];
 
                    b = pixBytes[pixelStart + 2];
 
                    pixel_r = pixBytes[pixelStart];
 
                    pixel_g = pixBytes[pixelStart + 1];
 
                    pixel_b = pixBytes[pixelStart + 2];
 
                    
 
                    // 1.) Check if this is a dark pixel.
 
                    if ((r < 0x46) && (g < 0x46) && (b < 0x46))
 
                    if ((pixel_r < 0x46) && (pixel_g < 0x46) && (pixel_b < 0x46))
 
                    {
 
                        // 2.) Check the pixel above it,
 
                        // to see if it's from the papy texture.
...
 
@@ -62,9 +99,9 @@ namespace DesertPaintLab
 
                                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))
 
                                    if ((Math.Abs(pixel_r - pixBytes[otherPixelStart++]) > colorTolerance) ||
 
                                        (Math.Abs(pixel_g - pixBytes[otherPixelStart++]) > colorTolerance) ||
 
                                        (Math.Abs(pixel_b - pixBytes[otherPixelStart]) > colorTolerance))
 
                                    {
 
                                        colorMatch = false;
 
                                        break;
...
 
@@ -75,7 +112,7 @@ namespace DesertPaintLab
 
                                {
 
                                    // WE FOUND THE SWATCH!
 
                                    // Now we know where the color bars are.
 
                                    int redPixelStart = pixelStart + (redBarSpacing * stride);
 
                                    redPixelStart = pixelStart + (redBarSpacing * stride);
 
                                    int redPixelCount = 0;
 
                                    while ((pixBytes[redPixelStart] > 0x9F) &&
 
                                           (pixBytes[redPixelStart + 1] < 0x62) &&
ReactionSet.cs
Show inline comments
 
new file 100644
 
/*
 
 * Copyright (c) 2015, Jason Maltzen
 

	
 
 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 class ReactionSet
 
    {
 
        // ingredient -> [ingredient, reaction]
 
        SortedDictionary<string, SortedDictionary<string, Reaction>> reactions =
 
            new SortedDictionary<string, SortedDictionary<string, Reaction>>();
 

	
 
        public ReactionSet()
 
        {
 
        }
 

	
 
        public Reaction Find(Reagent reagent1, Reagent reagent2)
 
        {   
 
            SortedDictionary<string, Reaction> secondReagentDict = null;
 
            Reaction reaction = null;
 
            reactions.TryGetValue(reagent1.PracticalPaintName, out secondReagentDict);
 
            if (secondReagentDict != null)
 
            {
 
                secondReagentDict.TryGetValue(reagent2.PracticalPaintName, out reaction);
 
            }
 
            return reaction;
 
        }
 
        
 
        public void Set(Reagent reagent1, Reagent reagent2, Reaction reaction)
 
        {
 
            SortedDictionary<string, Reaction> secondReagentDict = null;
 
            reactions.TryGetValue(reagent1.PracticalPaintName, out secondReagentDict);
 
            if (secondReagentDict == null)
 
            {
 
                secondReagentDict = new SortedDictionary<string, Reaction>();
 
                reactions.Add(reagent1.PracticalPaintName, secondReagentDict);
 
            }
 
            secondReagentDict[reagent2.PracticalPaintName] = reaction;
 
        }
 

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

	
Reagent.cs
Show inline comments
 
/*
 
 * Copyright (c) 2015, Jason Maltzen
 

 
 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;
 

 
namespace DesertPaintLab
...
 
@@ -5,8 +27,11 @@ namespace DesertPaintLab
 
	public class Reagent
 
	{ 
 
		string name;
 
        string ppName;
 
		bool isCatalyst = false;
 
        int cost = 0;
 
        uint cost = 0;
 
        bool enabled = true;
 
        uint recipeMax = 10;
 
		PaintColor color;
 
		
 
		public bool IsCatalyst
...
 
@@ -32,21 +57,76 @@ namespace DesertPaintLab
 
				return 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, int cost)
 
		{
 
			color = new PaintColor(red, green, blue);
 
			this.name = name;
 
            this.cost = cost;
 
		}
 
		
 

 
        public string PracticalPaintName
 
        {
 
            get
 
            {
 
                return ppName;
 
            }
 
        }
 

 
        public bool Enabled
 
        {
 
            get
 
            {
 
                return enabled;
 
            }
 
            set
 
            {
 
                enabled = value;
 
            }
 
        }
 

 
        public uint Cost
 
        {
 
            get
 
            {
 
                return cost;
 
            }
 
            set
 
            {
 
                cost = Math.Max(1, value);
 
            }
 
        }
 

 
        public uint RecipeMax
 
        {
 
            get
 
            {
 
                return recipeMax;
 
            }
 
            set
 
            {
 
                if (!isCatalyst)
 
                {
 
                    recipeMax = Math.Max(0, value);
 
                }
 
            }
 
        }
 

 
        // catalyst
 
        public Reagent(string name, string ppName)
 
        {
 
            this.name = name;
 
            this.ppName = ppName;
 
            this.cost = 2;
 
            this.enabled = true;
 
            this.recipeMax = 1;
 
            this.isCatalyst = true;
 
        }
 
        
 
        public Reagent(string name, string ppName, byte red, byte green, byte blue)
 
        {
 
            this.color = new PaintColor(red, green, blue);
 
            this.name = name;
 
            this.ppName = ppName;
 
            this.cost = 1;
 
            this.recipeMax = 10;
 
            this.enabled = true;
 
            this.isCatalyst = false;
 
        }
 
        
 
		public override string ToString()
 
		{
 
			if (isCatalyst)
ReagentManager.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;
...
 
@@ -7,11 +28,15 @@ namespace DesertPaintLab
 
{
 
	public class ReagentManager
 
	{
 
		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*\|.*");
 
        // PP format
 
		static Regex reagentRegex = new Regex(@"(?<name>\w+)\s*\|\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+)\s*\|\s*(?<cost>\d+)\s*\|\s*(?<enabled>[YN])\s*\|\s*(?<bulk>(bulk|normal))\s*\|\s*(?<max>\d+).*");
 
		static Regex catalystRegex = new Regex(@"(?<name>\w+)\s*\|\s*catalyst\s*\|\s*(?<cost>\d+)\s*\|\s*(?<enabled>[YN])\s*\|\s*(?<bulk>(bulk|normal)).*");
 
        static Regex internalReagentRegex = new Regex(@"(?<name>(\w*\s)*\w+)\s*\|\s*(?<ppname>\w+)\s*\|\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+).*");
 
        static Regex internalCatalystRegex = new Regex(@"(?<name>(\w+\s)*\w+)\s*\|\s*(?<ppname>\w+)\s*\|\s*catalyst.*");
 
		
 
		static SortedDictionary<string,Reagent> reagents = new SortedDictionary<string, Reagent>();
 
        static List<string> names = new List<string>();
 
        static Dictionary<string, string> nameLookup = new Dictionary<string, string>(); // pp name to our name
 
		
 
		static Gtk.ListStore nameStore = new Gtk.ListStore(typeof(string));
 

...
 
@@ -35,12 +60,52 @@ namespace DesertPaintLab
 
		{
 
			
 
		}
 

 
        // Loads reagent name/colors
 
        public static void Load(string file)
 
        {
 
            Match match;
 
            string line;
 
            reagents.Clear();
 
            using (StreamReader reader = new StreamReader(file))
 
            {
 
                while ((line = reader.ReadLine()) != null) 
 
                {
 
                    match = internalReagentRegex.Match(line); 
 
                    if (match.Success)
 
                    {
 
                        string name = match.Groups["name"].Value;
 
                        string ppname = match.Groups["ppname"].Value;
 
                        reagents.Add(name,
 
                            new Reagent(name, ppname,
 
                                byte.Parse(match.Groups["red"].Value),
 
                                byte.Parse(match.Groups["green"].Value),
 
                                byte.Parse(match.Groups["blue"].Value)));
 
                        nameStore.AppendValues(name);
 
                        names.Add(name);
 
                        nameLookup.Add(ppname, name);
 
                    }
 
                    else
 
                    {
 
                        match = internalCatalystRegex.Match(line);
 
                        if (match.Success)
 
                        {
 
                            string name = match.Groups["name"].Value;
 
                            string ppname = match.Groups["ppname"].Value;
 
                            reagents.Add(name, new Reagent(ppname, ppname));
 
                            nameStore.AppendValues(name);
 
                            names.Add(name);
 
                            nameLookup.Add(ppname, name);
 
                        }
 
                    }
 
                }
 
            }
 
        }
 
		
 
		public static void Load(string file)
 
		public static void LoadProfileReagents(string file)
 
		{	
 
			Match match;
 
			string line;
 
			reagents.Clear();
 
			using (StreamReader reader = new StreamReader(file))
 
			{
 
				while ((line = reader.ReadLine()) != null) 
...
 
@@ -48,45 +113,88 @@ namespace DesertPaintLab
 
					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),
 
                                int.Parse(match.Groups["cost"].Value)));
 
						nameStore.AppendValues(name);
 
                        names.Add(name);
 
						string ppname = match.Groups["name"].Value;
 
                        string name = null;
 
                        nameLookup.TryGetValue(ppname, out name);
 
                        Reagent reagent = GetReagent(name);
 
                        if (reagent != null && !reagent.IsCatalyst)
 
                        {
 
                            reagent.Enabled = match.Groups["enabled"].Value.Equals("Y");
 
                            reagent.Cost = uint.Parse(match.Groups["cost"].Value);
 
                            reagent.RecipeMax = uint.Parse(match.Groups["max"].Value);
 
                        }
 
					}
 
					else
 
					{
 
						match = catalystRegex.Match(line);
 
						if (match.Success)
 
						{
 
							string name = match.Groups["name"].Value;
 
                            int cost = int.Parse(match.Groups["cost"].Value);
 
							reagents.Add(name, new Reagent(name, cost));
 
							nameStore.AppendValues(name);
 
                            names.Add(name);
 
							string ppname = match.Groups["name"].Value;
 
                            string name = null;
 
                            nameLookup.TryGetValue(ppname, out name);
 
                            Reagent reagent = GetReagent(name);
 
                            if (reagent != null && reagent.IsCatalyst)
 
                            {
 
                                reagent.Enabled = match.Groups["enabled"].Value.Equals("Y");
 
                                reagent.Cost = uint.Parse(match.Groups["cost"].Value);
 
                            }
 
						}
 
					}
 
                }
 
			}
 
		}
 

 
        public static void SaveProfileReagents(string file)
 
        {
 
            using (StreamWriter writer = new StreamWriter(file))
 
            {
 
                writer.WriteLine("// Ingredients are in the form:");
 
                writer.WriteLine("// Name | RGB values | cost | enabled (Y/N) | bulk/normal | max items per paint (1-20)");
 
                writer.WriteLine("//");
 
                writer.WriteLine("// It is recommended to only change the cost value");
 
                writer.WriteLine("// It is not recommended to set many of the ingredients above 10 per paint");
 

 
                List<Reagent> sortedReagents = new List<Reagent>(reagents.Count);
 
                foreach (KeyValuePair<string, Reagent> pair in reagents)
 
                {
 
                    sortedReagents.Add(pair.Value);
 
                }
 
                sortedReagents.Sort( (x,y) => ((x.IsCatalyst && !y.IsCatalyst) ? 1 : ((y.IsCatalyst && !x.IsCatalyst) ? -1 : x.PracticalPaintName.CompareTo(y.PracticalPaintName))) );
 
                foreach (Reagent reagent in sortedReagents)
 
                {
 
                    if (!reagent.IsCatalyst)
 
                    {
 
                        writer.WriteLine("{0,-10} | {1,3}, {2,3}, {3,3} | {4,7} | {5} | {6} | {7}",
 
                            reagent.PracticalPaintName, 
 
                            reagent.Color.Red, reagent.Color.Blue, reagent.Color.Green,
 
                            reagent.Cost,
 
                            reagent.Enabled ? "Y" : "N",
 
                            reagent.RecipeMax >= 10 ? "  bulk" : "normal",
 
                            reagent.RecipeMax);
 
                    }
 
                    else
 
                    {
 
                        writer.WriteLine("{0,-10} | catalyst | {1,7} | {2} | normal | 1",
 
                            reagent.PracticalPaintName, 
 
                            reagent.Cost,
 
                            reagent.Enabled ? "Y" : "N");
 
                    }
 
                }
 
            }
 
        }
 

 
		
 
		public static void InitializeReactions(ref SortedDictionary<string, SortedDictionary<string, Reaction>> reactions)
 
		public static void InitializeReactions(ref ReactionSet 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.Set(pair1.Value, pair2.Value, null);
 
					}
 
				}
 
				reactions.Add(pair1.Key, dict);
 
			}
 
		}
 
		
...
 
@@ -133,6 +241,16 @@ namespace DesertPaintLab
 
		{
 
			Reagent returnVal;
 
			reagents.TryGetValue(reagentName, out returnVal);
 
            if (returnVal == null)
 
            {
 
                // convert pp name to our internal name
 
                string otherName = null;
 
                nameLookup.TryGetValue(reagentName, out otherName);
 
                if (otherName != null)
 
                {
 
                    reagents.TryGetValue(otherName, out returnVal);
 
                }
 
            }
 
			return returnVal;
 
		}
 
	}
ReagentWindow.cs
Show inline comments
 
new file 100644
 
using System;
 
using System.Collections.Generic;
 

	
 
namespace DesertPaintLab
 
{
 
    public partial class ReagentWindow : Gtk.Window
 
    {
 
        private class ReagentCheckButton : Gtk.CheckButton
 
        {
 
            private Reagent reagent;
 
            public Reagent Reagent
 
            {
 
                get {
 
                    return reagent;
 
                }
 
                set {
 
                    reagent = value;
 
                }
 
            }
 
        }
 
        private class ReagentEntry : Gtk.Entry
 
        {
 
            private Reagent reagent;
 
            public Reagent Reagent
 
            {
 
                get {
 
                    return reagent;
 
                }
 
                set {
 
                    reagent = value;
 
                }
 
            }
 

	
 
            // public ReagentEntry(int size) : base(size)
 
            //{
 
            //}
 

	
 
            override protected void OnTextInserted(string text, ref int position)
 
            {
 
                uint val;
 
                if (uint.TryParse(text, out val))
 
                {
 
                    base.OnTextInserted(text, ref position);
 
                }
 
            }
 
        }
 

	
 
        bool dirty = false;
 
        PlayerProfile profile;
 

	
 
        SortedDictionary<string, ReagentCheckButton> ingredientCheckButtons = new SortedDictionary<string, ReagentCheckButton>();
 
        SortedDictionary<string, ReagentEntry> ingredientCostEntries = new SortedDictionary<string, ReagentEntry>();
 
        SortedDictionary<string, ReagentEntry> ingredientQuantityEntries = new SortedDictionary<string, ReagentEntry>();
 
        int numReagents = 14;
 
        public ReagentWindow(PlayerProfile profile)
 
            : base(Gtk.WindowType.Toplevel)
 
        {
 
            this.profile = profile;
 
            this.Build();
 

	
 
            numReagents = ReagentManager.Names.Count;
 
            ingredientTable.NRows = (uint)numReagents;
 
            ingredientTable.NColumns = 4;
 
            uint row = 0;
 
            foreach (string reagentName in ReagentManager.Names)
 
            {
 
                Reagent reagent = ReagentManager.GetReagent(reagentName);
 
                ReagentCheckButton checkButton = new ReagentCheckButton();
 
                checkButton.Active = reagent.Enabled;
 
                checkButton.Reagent = reagent;
 
                checkButton.Toggled += OnEnableToggled;
 
                ingredientCheckButtons.Add(reagentName, checkButton);
 
                ingredientTable.Attach(checkButton, 0, 1, row, row+1); // checkbox for enabled
 
                ingredientTable.Attach(new Gtk.Label(reagentName), 1, 2, row, row+1); // name label
 
                ReagentEntry costEntry = new ReagentEntry();
 
                costEntry.MaxLength = 6;
 
                costEntry.WidthChars = 6;
 
                costEntry.Text = reagent.Cost.ToString();
 
                // TODO: set up validator
 
                // TODO: set up event handler for changed
 
                costEntry.Reagent = reagent;
 
                costEntry.TextDeleted += OnCostChanged;
 
                costEntry.TextInserted += OnCostChanged;
 
                ingredientCostEntries.Add(reagentName, costEntry);
 
                ingredientTable.Attach(costEntry, 2, 3, row, row+1); // cost input
 
                ReagentEntry maxQuantityEntry = new ReagentEntry();
 
                maxQuantityEntry.Text = reagent.RecipeMax.ToString();
 
                maxQuantityEntry.MaxLength = 4;
 
                maxQuantityEntry.WidthChars = 4;
 
                // TODO: set up validator
 
                // TODO: set up event handler for changed
 
                maxQuantityEntry.Reagent = reagent;
 
                if (reagent.IsCatalyst)
 
                {
 
                    maxQuantityEntry.Sensitive = false;
 
                }
 
                else
 
                {
 
                    maxQuantityEntry.TextDeleted += OnQuantityChanged;
 
                    maxQuantityEntry.TextInserted += OnQuantityChanged;
 
                }
 
                ingredientQuantityEntries.Add(reagentName, maxQuantityEntry);
 
                ingredientTable.Attach(maxQuantityEntry, 3, 4, row, row+1); // maximum quantity input
 
                ++row;
 
            }
 

	
 
            okButton.Clicked += OnOK;
 
            cancelButton.Clicked += OnCancel;
 
            ShowAll();
 
        }
 

	
 
        private void OnCostChanged(object o, EventArgs args)
 
        {
 
            ReagentEntry costEntry = (ReagentEntry)o;
 
            uint newCost;
 
            if (uint.TryParse(costEntry.Text, out newCost))
 
            {
 
                if (costEntry.Reagent.Cost != newCost)
 
                {
 
                    dirty = true;
 
                }
 
            }
 
        }
 

	
 
        private void OnQuantityChanged(object o, EventArgs args)
 
        {
 
            ReagentEntry qtyEntry = (ReagentEntry)o;
 
            uint newCost;
 
            if (uint.TryParse(qtyEntry.Text, out newCost))
 
            {
 
                if (qtyEntry.Reagent.Cost != newCost)
 
                {
 
                    dirty = true;
 
                }
 
            }
 
        }
 

	
 
        private void OnEnableToggled(object o, EventArgs args)
 
        {
 
            ReagentCheckButton btn = (ReagentCheckButton)o;
 
            if (btn.Active != btn.Reagent.Enabled)
 
            {
 
                dirty = true;
 
            }
 
        }
 

	
 
        private void OnOK(object obj, EventArgs args)
 
        {
 
            if (dirty)
 
            {
 
                // save out state
 
                foreach (string reagentName in ReagentManager.Names)
 
                {
 
                    ReagentCheckButton checkButton = ingredientCheckButtons[reagentName];
 
                    ReagentEntry costEntry = ingredientCostEntries[reagentName];
 
                    ReagentEntry qtyEntry = ingredientQuantityEntries[reagentName];
 
                    checkButton.Reagent.Enabled = checkButton.Active;
 
                    uint val;
 
                    if (uint.TryParse(costEntry.Text, out val))
 
                    {
 
                        costEntry.Reagent.Cost = val;
 
                    }
 
                    if (uint.TryParse(qtyEntry.Text, out val))
 
                    {
 
                        qtyEntry.Reagent.RecipeMax = val;
 
                    }
 
                }
 

	
 
                ReagentManager.SaveProfileReagents(profile.ReagentFile);
 
            }
 
            this.Destroy();
 
        }
 

	
 
        private void OnCancel(object obj, EventArgs args)
 
        {
 
            this.Destroy();
 
        }
 
    }
 
}
 

	
RecipeGenerator.cs
Show inline comments
 
new file 100644
 
/*
 
 * Copyright (c) 2015, Jason Maltzen
 

	
 
 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;
 
using System.Collections.Concurrent;
 
using System.Threading;
 

	
 
namespace DesertPaintLab
 
{
 
    public class NewRecipeEventArgs : EventArgs
 
    {
 
        string color;
 
        PaintRecipe recipe;
 

	
 
        public NewRecipeEventArgs(string color, PaintRecipe recipe)
 
        {
 
            this.color = color;
 
            this.recipe = recipe;
 
        }
 

	
 
        public string Color
 
        {
 
            get {
 
                return color;
 
            }
 
        }
 

	
 
        public PaintRecipe Recipe
 
        {
 
            get {
 
                return recipe;
 
            }
 
        }
 
    }
 

	
 
    public class RecipeGenerator
 
    {
 
        private class SearchNode
 
        {
 
            //int initialReagentCount;
 
            List<uint> reagents;
 
            HashSet<uint> reagentInUse = new HashSet<uint>();
 
            List<Reagent> costSortedReagents;
 
            PaintRecipe testRecipe = null;
 
            public PaintRecipe TestRecipe
 
            {
 
                get
 
                {
 
                    return testRecipe;
 
                }
 
                set
 
                {
 
                    testRecipe = value;
 
                }
 
            }
 
            public uint CurrentTargetQuantity { get; set; }
 
            public uint MaxQuantity { get; set; }
 
            uint maxReagents;
 
            public uint MaxReagents
 
            {
 
                get
 
                {
 
                    return maxReagents;
 
                }
 
                set
 
                {
 
                    maxReagents = value;
 
                    currentWeights = new uint[maxReagents];
 
                }
 
            }
 
            public uint UsedQuantity { get; private set; }
 

	
 
            public uint CatalystCount { get; set; }
 

	
 
            uint[] currentWeights;
 
            public uint[] CurrentWeights
 
            {
 
                get
 
                {
 
                    return currentWeights;
 
                }
 
            }
 

	
 
            public SearchNode(List<Reagent> costSortedReagents, List<uint> reagents)
 
            {
 
                this.costSortedReagents = new List<Reagent>(costSortedReagents);
 
                this.reagents = new List<uint>(reagents);
 
                foreach (uint reagentIdx in reagents)
 
                {
 
                    reagentInUse.Add(reagentIdx);
 
                }
 
                InitialCount = this.reagents.Count;
 
                MaxReagents = (uint)this.reagents.Count; // better set this later!
 
                UsedQuantity = 0;
 
            }
 

	
 
            // top-level search
 
            public SearchNode(List<Reagent> costSortedReagents, uint startReagent)
 
            {
 
                this.costSortedReagents = new List<Reagent>(costSortedReagents);
 
                this.reagents = new List<uint>();
 
                this.reagents.Add(NextFreeReagent(startReagent));
 
                InitialCount = 1; // don't iterate up beyond the start reagent
 
                MaxReagents = 1;
 
                UsedQuantity = 0;
 
            }
 

	
 
            public int InitialCount { get; private set; }
 
            public List<uint> Reagents
 
            {
 
                get
 
                {
 
                    return reagents;
 
                }
 
            }
 

	
 
            public Reagent Reagent(int idx)
 
            {
 
                return costSortedReagents[(int)reagents[idx]];
 
            }
 

	
 
            public uint LastReagent
 
            {
 
                get
 
                {
 
                    return reagents[reagents.Count - 1];
 
                }
 
            }
 

	
 
            public void RemoveLastReagent()
 
            {
 
                uint reagentIdx = reagents[reagents.Count-1];
 
                ReleaseReagent(reagentIdx);
 
                if (costSortedReagents[(int)reagentIdx].IsCatalyst)
 
                {
 
                    --CatalystCount;
 
                }
 
                reagents.RemoveAt(reagents.Count-1);
 
            }
 

	
 
            public void ReplaceLastReagent(uint reagentIdx)
 
            {
 
                uint oldReagentIdx = reagents[reagents.Count-1];
 
                ReleaseReagent(oldReagentIdx);
 
                reagents[reagents.Count-1] = reagentIdx;
 
                if (costSortedReagents[(int)oldReagentIdx].IsCatalyst)
 
                {
 
                    --CatalystCount;
 
                }
 
                if (costSortedReagents[(int)reagentIdx].IsCatalyst)
 
                {
 
                    ++CatalystCount;
 
                }
 
            }
 

	
 
            public uint NextFreeReagent(uint startIdx)
 
            {
 
                uint idx = startIdx;
 
                for (; idx < costSortedReagents.Count; ++idx)
 
                {
 
                    bool inUse = reagentInUse.Contains(idx);
 
                    if (inUse == false)
 
                    {
 
                        //Console.WriteLine("Found free reagent idx {0}", idx);
 
                        reagentInUse.Add(idx);
 
                        return idx;
 
                    }
 
                }
 
                //Console.WriteLine("Failed to find free reagent.");
 
                return (uint)costSortedReagents.Count;
 
            }
 
    
 
            private void ReleaseReagent(uint reagentIdx)
 
            {
 
                reagentInUse.Remove(reagentIdx);
 
            }
 
    
 
            public bool AddNextReagent()
 
            {
 
                bool ok = (reagents.Count < MaxReagents);
 
                if (ok)
 
                {
 
                    uint nextReagent = NextFreeReagent(0);
 
                    reagents.Add(nextReagent);
 
                    if (costSortedReagents[(int)nextReagent].IsCatalyst)
 
                    {
 
                        ++CatalystCount;
 
                    }
 
                    InitForQuantity(CurrentTargetQuantity);
 
                }
 
                return ok;
 
            }
 

	
 
            public void InitForQuantity(uint quantity)
 
            {
 
                //System.Console.WriteLine("Target quantity: {0}, reagent count: {1}", quantity, reagents.Count);
 
                CurrentTargetQuantity = quantity;
 
                if (CurrentTargetQuantity < (10 + CatalystCount))
 
                {
 
                    return;
 
                }
 
                UsedQuantity = 0;
 
                uint remainingReagents = ((uint)reagents.Count - CatalystCount);
 
                uint remainingWeight = CurrentTargetQuantity - CatalystCount;
 
                for (int i = 0; i < reagents.Count; ++i)
 
                {
 
                    Reagent reagent = Reagent(i);
 
    
 
                    if (reagent.IsCatalyst)
 
                    {
 
                        currentWeights[i] = 1;
 
                        ++UsedQuantity;
 
                    }
 
                    else
 
                    {
 
                        uint weight = (uint)Math.Min(remainingWeight - (remainingReagents-1), reagent.RecipeMax);
 
                        remainingWeight -= weight;
 
                        currentWeights[i] = weight;
 
                        UsedQuantity += weight;
 
                    }
 
                    --remainingReagents;
 
                }
 
            }
 

	
 
            public void SetWeight(int idx, uint quantity)
 
            {
 
                UsedQuantity -= currentWeights[idx];
 
                currentWeights[idx] = quantity;
 
                UsedQuantity += quantity;
 
            }
 
        }
 

	
 
        const uint DEFAULT_MAX_QUANTITY = 14; // minimum recipe: 10 base + 4 catalysts
 
        const uint DEFAULT_MAX_REAGENTS = 5;
 

	
 
        //uint maxQuantity; // maximum number of total ingredients
 
        uint maxReagents; // maximum number of reagents to use in the recipe
 
        uint fullQuantityDepth; // at or equal this number of reagents, ignore ingredient settings for max quantity
 
        uint fullQuantity;  // The max number of a reagent to use at full quantity
 

	
 
        ReactionSet reactions;
 
        bool running = false;
 
        
 
        SortedDictionary<string, uint> recipeCosts = new SortedDictionary<string, uint>();
 
        SortedDictionary<string, PaintRecipe> recipes = new SortedDictionary<string, PaintRecipe>();
 

	
 
        uint totalReagents;
 

	
 
        List<Reagent> costSortedReagents = new List<Reagent>();
 

	
 
        ConcurrentQueue<SearchNode> searchQueue = new ConcurrentQueue<SearchNode>();
 

	
 
        int recipeCount = 0;
 

	
 
        List<Thread> generatorThreads = new List<Thread>();
 
        Object workerLock = new Object();
 

	
 
        bool requestCancel = false;
 

	
 
        // events
 
        public event EventHandler Finished;
 
        public event EventHandler Progress;
 
        public event EventHandler<NewRecipeEventArgs> NewRecipe;
 

	
 
        public RecipeGenerator()
 
        {
 
        }
 

	
 
        public SortedDictionary<string, PaintRecipe> Recipes
 
        {
 
            get
 
            {
 
                return recipes;
 
            }
 
        }
 

	
 
        public int RecipeCount
 
        {
 
            get
 
            {
 
                return recipeCount;
 
            }
 
        }
 

	
 
        private class ReagentCostSort : IComparer<Reagent>
 
        {
 
            public int Compare(Reagent reagent1, Reagent reagent2)
 
            {
 
                return (int)reagent1.Cost - (int)reagent2.Cost;
 
            }
 
        }
 

	
 
        public void InitRecipes(SortedDictionary<string, PaintRecipe> recipes, ReactionSet reactions)
 
        {
 
            if (running)
 
            {
 
                return;
 
            }
 
            this.reactions = reactions;
 
            foreach (PaintRecipe recipe in recipes.Values)
 
            {
 
                // TODO: copy?
 
                AddCheapestRecipe(recipe);
 
            }
 
        }
 

	
 
        public void BeginRecipeGeneration(ReactionSet reactions, uint maxQuantity, uint maxReagents, uint fullQuantityDepth, uint fullQuantity)
 
        {
 
            if (running)
 
            {
 
                // Already running - don't start again
 
                return;
 
            }
 
            this.running = true;
 

	
 
            this.reactions = reactions;
 
            //this.maxQuantity = maxQuantity;
 
            this.maxReagents = maxReagents;
 
            this.fullQuantity = fullQuantity;
 
            this.fullQuantityDepth = fullQuantityDepth;
 

	
 
            // first, sort reagents by cost.
 
            costSortedReagents.Clear();
 
            foreach (string name in ReagentManager.Names)
 
            {
 
                Reagent reagent = ReagentManager.GetReagent(name);
 
                if (reagent.Enabled)
 
                {
 
                    costSortedReagents.Add(reagent);
 
                }
 
            }
 
            costSortedReagents.Sort(new ReagentCostSort());
 
            this.maxReagents = (uint)Math.Min(costSortedReagents.Count, this.maxReagents);
 

	
 
            totalReagents = (uint)costSortedReagents.Count;
 

	
 
            // Pre-populate recipes list with:
 
            // 1) 1-ingredient recipes @ 10db for all enabled ingredients with a count >= 10
 
            // 2) any previously-generated recipes
 
            foreach (Reagent reagent in costSortedReagents)
 
            {
 
                if (!reagent.IsCatalyst && reagent.RecipeMax >= 10)
 
                {
 
                    PaintRecipe recipe = new PaintRecipe();
 
                    recipe.Reactions = reactions;
 
                    recipe.AddReagent(reagent.Name, 10);
 
                    AddCheapestRecipe(recipe);
 
                }
 
            }
 

	
 
            for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx)
 
            {
 
                SearchNode initialNode = new SearchNode(costSortedReagents, reagentIdx);
 
                initialNode.MaxQuantity = maxQuantity;
 
                initialNode.MaxReagents = maxReagents;
 
                searchQueue.Enqueue(initialNode);
 
            }
 
            
 
            // start worker threads to do the actual work
 

	
 
            requestCancel = false;
 
            running = true;
 
            // Start the workers thread
 
            for (int i = 0; i < costSortedReagents.Count; ++i)
 
            {
 
                Thread thr = new Thread(new ThreadStart(this.Generate));
 
                generatorThreads.Add(thr);
 
            }
 
            foreach (Thread thr in generatorThreads)
 
            {
 
                thr.Start();
 
            }
 
        
 
        }
 

	
 
        public void ResumeRecipeGeneration()
 
        {
 
            if (running)
 
            {
 
                // Already running - don't start again
 
                return;
 
            }
 
            this.running = true;
 
            requestCancel = false;
 

	
 
            for (int i = 0; i < costSortedReagents.Count; ++i)
 
            {
 
                Thread thr = new Thread(new ThreadStart(this.Generate));
 
                generatorThreads.Add(thr);
 
            }
 
            foreach (Thread thr in generatorThreads)
 
            {
 
                thr.Start();
 
            }
 
        }
 

	
 
        private void Generate()
 
        {
 
            SearchNode node;
 

	
 
            bool ok = true;
 
            do
 
            {
 
                lock (workerLock)
 
                {
 
                    ok = searchQueue.TryDequeue(out node);
 
                }
 
                if (ok)
 
                {
 
                    uint targetQuantity = node.MaxQuantity + 1;
 
                    do {
 
                        --targetQuantity;
 
                        node.InitForQuantity(targetQuantity);
 
                    } while (targetQuantity > 10 && (node.CurrentTargetQuantity != node.UsedQuantity));
 

	
 
                    while (ok = Iterate(node) && !requestCancel)
 
                    {
 
                        if (Progress != null)
 
                        {
 
                            Progress(this, null);
 
                        }
 
                    }
 
                    if (ok)
 
                    {
 
                        // stopped because cancel was requested - requeue the node in its current state for resume
 
                        searchQueue.Enqueue(node);
 
                    }
 
                }
 
            } while (!requestCancel && ok);
 

	
 
            bool done = false;
 
            lock(workerLock)
 
            {
 
                generatorThreads.Remove(Thread.CurrentThread);
 

	
 
                done = (generatorThreads.Count == 0);
 
            }
 
            if (done)
 
            {
 
                if (Finished != null)
 
                {
 
                    Finished(this, null);
 
                }
 
                running = false;
 
            }
 
        }
 

	
 
        // Add the cheapest recipe to the recipe list
 
        // returns the discarded recipe from the pair (or null if no original recipe to replace)
 
        private PaintRecipe AddCheapestRecipe(PaintRecipe recipe)
 
        {
 
            PaintRecipe discarded = recipe;
 
            if (recipe.IsValid)
 
            {
 
                recipe.Reactions = reactions;
 

	
 
                string colorName = Palette.FindNearest(recipe.ReactedColor);
 
                //System.Console.WriteLine("Recipe: {0} {1}:", colorName, recipe.Cost);
 
                //foreach (PaintRecipe.RecipeIngredient ingr in recipe.Ingredients)
 
                //{
 
                //    System.Console.WriteLine("    -> {0} {1}", ingr.quantity, ingr.name);
 
                //}
 
                uint cost;
 
                lock (workerLock)
 
                {
 
                    if (recipeCosts.TryGetValue(colorName, out cost))
 
                    {
 
                        if (cost > recipe.Cost)
 
                        {
 
                            discarded = recipes[colorName];
 
                            recipeCosts[colorName] = recipe.Cost;
 
                            recipes[colorName] = recipe;
 
                            if (NewRecipe != null)
 
                            {
 
                                NewRecipeEventArgs args = new NewRecipeEventArgs(colorName, recipe);
 
                                NewRecipe(this, args);
 
                            }
 
                        }
 
                    }
 
                    else
 
                    {
 
                        discarded = null;
 
                        recipeCosts.Add(colorName, recipe.Cost);
 
                        recipes.Add(colorName, recipe);
 
                        if (NewRecipe != null)
 
                        {
 
                            NewRecipeEventArgs args = new NewRecipeEventArgs(colorName, recipe);
 
                            NewRecipe(this, args);
 
                        }
 
                    }
 
                }
 
            }
 
            else
 
            {
 
                string msg = String.Format("Recipe is invalid ({0} ingredients)\n", recipe.Ingredients.Count);
 
                foreach (PaintRecipe.RecipeIngredient ingr in recipe.Ingredients)
 
                {
 
                    msg += String.Format("    -> {0} {1}", ingr.quantity, ingr.name);
 
                }
 
                lock (workerLock) {
 
                    Console.WriteLine(msg);
 
                }
 
            }
 
            
 
            return discarded;
 
        }
 

	
 
        private bool Iterate(SearchNode node)
 
        {
 
            // pick recipe quantities at current recipe ingredients/size
 
            if (NextRecipe(node))
 
            {
 
                lock(workerLock)
 
                {
 
                    ++recipeCount;
 
                }
 
                //System.Console.WriteLine("Found next recipe at size {0} qty {1}", node.Reagents.Count, node.CurrentTargetQuantity);
 
                return true;
 
            }
 

	
 
            if (NextRecipeSize(node))
 
            {
 
                //System.Console.WriteLine("Found next recipee size {0}", node.CurrentTargetQuantity);
 
                return true;
 
            }
 

	
 
            // Search for next ingredient combo - all quantity combos for previous were searched
 
            //System.Console.WriteLine("Finding next ingredient combo");
 
            do
 
            {
 
                if (!node.AddNextReagent())
 
                {
 
                    while ((node.Reagents.Count > node.InitialCount) && (node.LastReagent == (totalReagents-1)))
 
                    {
 
                        node.RemoveLastReagent();
 
                    }
 
                    if (node.Reagents.Count == node.InitialCount)
 
                    {
 
                        // done
 
                        return false;
 
                    }
 
                    uint nextReagent = node.NextFreeReagent(node.LastReagent);
 
                    while ((node.Reagents.Count > node.InitialCount) && (nextReagent >= totalReagents))
 
                    {
 
                        // No more reagents to try at this level
 
                        node.RemoveLastReagent();
 
                        if (node.Reagents.Count > node.InitialCount)
 
                        {
 
                            nextReagent = node.NextFreeReagent(node.LastReagent);
 
                        }
 
                    }
 
                    if (node.Reagents.Count == node.InitialCount)
 
                    {
 
                        // done
 
                        return false;
 
                    }
 
                    node.ReplaceLastReagent(nextReagent);
 
                }
 
            } while (node.MaxQuantity < (10 + node.CatalystCount));
 
            node.InitForQuantity(node.MaxQuantity);
 

	
 
            //string outStr = "{0} : {1} : ";
 
            //for (int i = 0; i < currentReagents.Count; ++i)
 
            //{
 
            //    Reagent reagent = costSortedReagents[(int)currentReagents[i]];
 
            //    if (i > 0)
 
            //    {
 
            //        outStr += ", ";
 
            //    }
 
            //    outStr += reagent.Name + " (" + reagent.Cost + ")";
 
            //}
 
            //Console.WriteLine(outStr, currentReagents.Count, recipeCount);
 
            return true;
 
        }
 

	
 
        private bool NextRecipe(SearchNode node)
 
        {
 
            // First, run the current recipe
 
            if (node.TestRecipe == null)
 
            {
 
                node.TestRecipe = new PaintRecipe();
 
                node.TestRecipe.Reactions = reactions;
 
            }
 
            node.TestRecipe.Clear();
 
            for (int i = 0; i < node.Reagents.Count; ++i)
 
            {
 
                node.TestRecipe.AddReagent(node.Reagent(i).Name, node.CurrentWeights[i]);
 
            }
 
            PaintRecipe replacement = AddCheapestRecipe(node.TestRecipe);
 
            if (replacement == null)
 
            {
 
                node.TestRecipe = new PaintRecipe();
 
                node.TestRecipe.Reactions = reactions;
 
            }
 
            else
 
            {
 
                node.TestRecipe = replacement;
 
            }
 
            
 
            // check for the next recipe
 
            uint remainingWeight = node.CurrentTargetQuantity - node.CatalystCount;
 
            if (remainingWeight < 10)
 
            {
 
                // not possible to make a valid recipe
 
                return false;
 
            }
 
            //uint remainingReagents = (uint)node.Reagents.Count - node.CatalystCount;
 

	
 
            uint depth = (uint)node.Reagents.Count;
 
            uint weightToConsume = 0;
 
            uint spaceBelow = 0;
 
            int reagentsBelow = 0;
 
            for (int i = (int)depth-1 ; i >= 0; --i)
 
            {
 
                uint currentWeight = node.CurrentWeights[i];
 

	
 
                if ((spaceBelow >= (weightToConsume+1)) && (currentWeight > 1))
 
                {
 
                    // reduce this node by 1, allocate remaining weight to reagents below it
 
                    node.SetWeight(i, currentWeight-1);
 
                    weightToConsume += 1;
 
                    for (int j = i+1; j < depth; ++j)
 
                    {
 
                        --reagentsBelow;
 
                        Reagent reagent = node.Reagent(j);
 
                        uint allocated = (uint)Math.Min(reagent.IsCatalyst ? 1 : (depth <= fullQuantityDepth ? fullQuantity : reagent.RecipeMax), weightToConsume - reagentsBelow);
 
                        if (allocated > 100)
 
                        {
 
                            Console.WriteLine("ACK: allocated = {0}", allocated);
 
                        }
 
                        node.SetWeight(j, allocated);
 
                        weightToConsume -= allocated;
 
                    }
 
                    break;
 
                }
 
                else
 
                {
 
                    Reagent reagent = node.Reagent(i);
 
                    spaceBelow += (reagent.IsCatalyst ? 1 : (depth <= fullQuantityDepth ? fullQuantity : reagent.RecipeMax));
 
                    weightToConsume += currentWeight;
 
                    ++reagentsBelow;
 
                }
 
            }
 

	
 
            //int recipeWeight = 0;
 
            //foreach (int weight in node.CurrentWeights)
 
            //{
 
            //    recipeWeight += weight;
 
            //}
 
            //if ((weightToConsume != 0) || (recipeWeight != node.CurrentTargetQuantity))
 
            //{
 
            //    Console.WriteLine("Failed recipe with leftover weight {0} ({1}/{2}):", weightToConsume, recipeWeight, node.CurrentTargetQuantity);
 
            //    for (int i = 0; i < node.Reagents.Count; ++i)
 
            //    {
 
            //        Console.WriteLine("   > {0} {1}", node.Reagent(i).Name, node.CurrentWeights[i]);
 
            //    }
 
            //}
 
            
 
            return (weightToConsume == 0);
 
        }
 

	
 
        private bool NextRecipeSize(SearchNode node)
 
        {
 
            uint newQuantity = node.CurrentTargetQuantity - 1;
 
            if (newQuantity < (10 + node.CatalystCount))
 
            {
 
                return false;
 
            }
 

	
 
            node.InitForQuantity(newQuantity);
 
            if (node.CurrentTargetQuantity > node.UsedQuantity)
 
            {
 
                return false;
 
            }
 

	
 
            return true;
 
        }
 

	
 
        public void Wait()
 
        {
 
            if (running)
 
            {
 
                foreach (Thread thr in generatorThreads)
 
                {
 
                    thr.Join();
 
                }
 
            }
 
        }
 

	
 
        public void Stop()
 
        {
 
            this.requestCancel = true;
 
        }
 

	
 
        public void Reset()
 
        {
 
            recipes.Clear();
 
            recipeCosts.Clear();
 
        }
 
    }
 
}
 

	
RecipeGeneratorWindow.cs
Show inline comments
 
new file 100644
 
/*
 
 * Copyright (c) 2015, Jason Maltzen
 

	
 
 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 RecipeGeneratorWindow : Gtk.Window
 
    {
 
        RecipeGenerator generator;
 
        PlayerProfile profile;
 
        bool canceling = false;
 
        bool running = false;
 

	
 
        static Gtk.ListStore colorStore = new Gtk.ListStore(typeof(string));
 

	
 
        List<KeyValuePair<string, string>> missingReactions = new List<KeyValuePair<string, string>>();
 

	
 
        long lastProgressUpdate;
 
        long lastStatusUpdate;
 

	
 
        static public Gtk.ListStore RecipeModel
 
        {
 
            get
 
            {
 
                return colorStore;   
 
            }
 
        }
 

	
 
        public RecipeGeneratorWindow(PlayerProfile profile) : base(Gtk.WindowType.Toplevel)
 
        {
 
            this.profile = profile;
 
            this.Build();
 
            maxIngredientsSpinButton.Value = 5; // TODO: read/save profile info
 
            maxRecipeSpinButton.Value = 20; // TODO: read/save profile info
 
            fullQuantitySpinButton.Value = 20; // TODO: read/save profile info
 
            fullQuantityDepthSpinButton.Value = 4; // TODO: read/save profile info
 

	
 
            fullQuantityDepthSpinButton.SetRange(0, ReagentManager.Names.Count);
 
            maxIngredientsSpinButton.SetRange(0, ReagentManager.Names.Count);
 

	
 
            Gtk.TreeViewColumn recipeColorColumn = new Gtk.TreeViewColumn();
 
            Gtk.CellRendererText recipeColumnCell = new Gtk.CellRendererText();
 
            recipeColorColumn.PackStart(recipeColumnCell, true);       
 
            recipeColorColumn.Title = "Color";
 

	
 
            recipeList.AppendColumn(recipeColorColumn);
 
            recipeColorColumn.AddAttribute(recipeColumnCell, "text", 0);
 

	
 
            colorStore.SetSortColumnId(0, Gtk.SortType.Ascending);
 
            recipeList.Model = RecipeModel;
 

	
 
            recipeList.Selection.Changed += OnColorSelected;
 

	
 
            profile.LoadRecipes();
 

	
 
            // init UI
 
            foreach (string key in profile.Recipes.Keys)
 
            {
 
                colorStore.AppendValues(key);
 
            }
 

	
 
            countLabel.Text = String.Format("{0} / {1}", profile.Recipes.Count, Palette.Count);
 
            canceling = false;
 
            running = false;
 
        }
 

	
 
        protected void OnMaxIngredientsChanged(object sender, EventArgs e)
 
        {
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
 
        }
 

	
 
        protected void OnMaxRecipeChanged(object sender, EventArgs e)
 
        {
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
 
        }
 

	
 
        protected void OnFullQuantityDepthChanged(object sender, EventArgs e)
 
        {
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
 
        }
 

	
 
        protected void OnFullQuantityChanged(object sender, EventArgs e)
 
        {
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
 
        }
 

	
 
        protected void OnBegin(object sender, EventArgs e)
 
        {
 
            maxIngredientsSpinButton.Sensitive = false;
 
            ExportAction.Sensitive = false;
 
            SettingsAction.Sensitive = false;
 
            maxRecipeSpinButton.Sensitive = false;
 
            beginButton.Sensitive = false; // TODO: change to "pause"?
 
            stopResumeButton.Sensitive = true;
 
            fullQuantitySpinButton.Sensitive = false;
 
            fullQuantityDepthSpinButton.Sensitive = false;
 

	
 
            generator = new RecipeGenerator();
 

	
 
            generator.InitRecipes(profile.Recipes, profile.Reactions);
 
            countLabel.Text = String.Format("{0} / {1}", generator.Recipes.Count, Palette.Count);
 

	
 
            generator.Progress += OnProgress;
 
            generator.Finished += OnFinished;
 
            generator.NewRecipe += OnNewRecipe;
 
            // TODO: hook up event notifications
 
            // - progress
 
            // - complete
 
            // - new recipe / recipe update
 

	
 
            // Total recipe search count
 
            //int current = ReagentManager.Names.Count;
 
            //long recipePermutations = 1;
 
            //for (int i = 0; i < maxIngredientsSpinButton.ValueAsInt; ++i)
 
            //{
 
            //    recipePermutations *= current;
 
            //    --current;
 
            //}
 
            //System.Console.WriteLine("Will search {0} reagent permutations.", recipePermutations);
 

	
 
            lastProgressUpdate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
 
            lastStatusUpdate = lastProgressUpdate;
 

	
 
            running = true;
 
            stopResumeButton.Label = "Stop";
 

	
 
            generator.BeginRecipeGeneration(profile.Reactions, (uint)maxRecipeSpinButton.ValueAsInt, (uint)maxIngredientsSpinButton.ValueAsInt, (uint)fullQuantityDepthSpinButton.ValueAsInt, (uint)fullQuantitySpinButton.ValueAsInt);
 
        }
 

	
 
        protected void OnStopResume(object sender, EventArgs e)
 
        {
 
            if (generator != null)
 
            {
 
                if (running)
 
                {
 
                    canceling = true;
 
                    generator.Stop();
 
                }
 
                else
 
                {
 
                    // this must be a resume
 
                    lastProgressUpdate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
 
                    lastStatusUpdate = lastProgressUpdate;
 

	
 
                    canceling = false;
 
                    running = true;
 
        
 
                    stopResumeButton.Label = "Stop";
 
                    generator.BeginRecipeGeneration(profile.Reactions, (uint)maxRecipeSpinButton.ValueAsInt, (uint)maxIngredientsSpinButton.ValueAsInt, (uint)fullQuantityDepthSpinButton.ValueAsInt, (uint)fullQuantitySpinButton.ValueAsInt);
 
                }
 
            }
 
        }
 

	
 
        protected void OnFinished(object sender, EventArgs args)
 
        {
 
            Gtk.Application.Invoke(delegate {
 
                running = false;
 
                beginButton.Sensitive = true;
 
                ExportAction.Sensitive = true;
 
                SettingsAction.Sensitive = true;
 
                stopResumeButton.Sensitive = false;
 
                maxIngredientsSpinButton.Sensitive = true;
 
                maxRecipeSpinButton.Sensitive = true;
 
                fullQuantitySpinButton.Sensitive = true;
 
                fullQuantityDepthSpinButton.Sensitive = true;
 
                //generator = null; // don't. Hang on to generator for resume.
 
                profile.SaveRecipes();
 
                if (canceling)
 
                {
 
                    stopResumeButton.Label = "Resume";
 
                    stopResumeButton.Sensitive = true;
 
                }
 
            });
 
        }
 

	
 
        protected void OnNewRecipe(object sender, NewRecipeEventArgs args)
 
        {
 
            Gtk.Application.Invoke(delegate
 
            {
 
                progressBar.Pulse();
 
                // TODO: Add item to recipe list only if not already listed
 
                bool exists = false;
 
                Gtk.TreeIter iter;
 
                if (colorStore.GetIterFirst(out iter))
 
                {
 
                    do
 
                    {
 
                        string color = (string)colorStore.GetValue(iter, 0);
 
                        if (color.Equals(args.Color))
 
                        {
 
                            exists = true;
 
                            break;
 
                        }
 
                    } while (colorStore.IterNext(ref iter));
 
                }
 
                if (!exists)
 
                {
 
                    Console.WriteLine("Add new recipe for {0}", args.Color);
 
                    //    bool isMissingReactions = args.Recipe.CheckMissingReactions(ref missingReactions);
 
                    //    string missingReactionLabel = isMissingReactions ? "X" : "";
 
                    colorStore.AppendValues(args.Color); // , missingReactionLabel);
 
                    countLabel.Text = String.Format("{0} / {1}", generator.Recipes.Count, Palette.Count);
 
                }
 
                profile.SetRecipe(args.Recipe);
 
            });
 
        }
 

	
 
        protected void OnProgress(object sender, EventArgs args)
 
        {
 
            Gtk.Application.Invoke(delegate
 
            {
 
                // TODO: based on time rather than count
 
                long progressTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
 
                long delta = progressTime - lastProgressUpdate;
 
                if (delta > 30)
 
                {
 
                    progressBar.Pulse();
 
                    lastProgressUpdate = progressTime;
 
                }
 
                delta = progressTime - lastStatusUpdate;
 
                if (delta > 500)
 
                {
 
                    // Update recipe count label as well
 
                    statusLabel.Text = String.Format("Recipes searched: {0:N00}", generator.RecipeCount);
 
                    lastStatusUpdate = progressTime;
 
                }
 
                //progressBar.Fraction = (double)((generator.RecipeCount / 10000) % 100) / 100.0;
 
            });
 
        }
 

	
 
        protected void OnColorSelected(object o, EventArgs args)
 
        {
 
            Gtk.TreeModel model;
 
            Gtk.TreeIter iter;
 
            Gtk.TreeSelection selection = recipeList.Selection;
 
            if ((selection != null) && selection.GetSelected(out model, out iter))
 
            {
 
                string colorName = (string)colorStore.GetValue(iter, 0);
 
                PaintRecipe recipe;
 
                if (profile.Recipes.TryGetValue(colorName, out recipe))
 
                {
 
                    foreach (Gtk.Widget child in recipeListBox.AllChildren)
 
                    {
 
                        recipeListBox.Remove(child);
 
                    }
 
                    if (recipe.CheckMissingReactions(ref missingReactions))
 
                    {
 
                        statusLabel.Text = "WARNING: This recipe includes reactions that have not yet been recorded.";
 
                    }
 
                    foreach (PaintRecipe.RecipeIngredient ingredient in recipe.Ingredients)
 
                    {
 
                        Gtk.Label label = new Gtk.Label(ingredient.quantity.ToString() + " " + ingredient.name);
 
                        recipeListBox.PackStart(label);
 
                        label.Show();
 
                    }
 
                }
 
                paintSwatch.Color = recipe.ReactedColor;
 
            }
 
        }
 

	
 
        protected void OnExportToWiki(object sender, EventArgs e)
 
        {
 
            Gtk.FileChooserDialog fileDialog =
 
                new Gtk.FileChooserDialog("Select destination file.",
 
                        this, Gtk.FileChooserAction.Save,
 
                        Gtk.Stock.Cancel, Gtk.ResponseType.Cancel,
 
                        Gtk.Stock.Save, Gtk.ResponseType.Accept);
 
            Gtk.ResponseType resp = (Gtk.ResponseType)fileDialog.Run();
 
            if (resp == Gtk.ResponseType.Accept)
 
            {
 
                string fileName = fileDialog.Filename;
 
                string directory = fileDialog.CurrentFolder;
 
                profile.ExportWikiRecipes(System.IO.Path.Combine(directory, fileName));
 
            }
 
            fileDialog.Destroy();
 
        }
 

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

	
SimulatorWindow.cs
Show inline comments
...
 
@@ -27,14 +27,30 @@ namespace DesertPaintLab
 
{
 
	public partial class SimulatorWindow : Gtk.Window
 
	{
 
		PlayerProfile profile;
 
		Gtk.ListStore recipeData = new Gtk.ListStore(typeof(string), typeof(int));
 
        PaintRecipe paintRecipe = new PaintRecipe();
 
        PaintRecipe paintRecipe;
 

 
        List<IngredientPair> missingWarned = new List<IngredientPair>();
 
        List<KeyValuePair<string, string>> newMissing = new List<KeyValuePair<string, string>>();
 

 
        private class IngredientPair
 
        {
 
            public string first;
 
            public string second;
 

 
            public IngredientPair(string first, string second)
 
            {
 
                this.first = first;
 
                this.second = second;
 
            }
 
        }
 

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

 
            paintRecipe = new PaintRecipe();
 
            paintRecipe.Reactions = profile.Reactions;
 
			
 
			Gtk.TreeViewColumn reagentColumn = new Gtk.TreeViewColumn();
 
			Gtk.CellRendererText reagentColumnCell = new Gtk.CellRendererText();
...
 
@@ -145,9 +161,35 @@ namespace DesertPaintLab
 
			}
 
			while (recipeData.IterNext(ref iter));
 
			
 
            PaintColor resultColor = new PaintColor();
 
            paintRecipe.ComputeReactedColor(profile, ref resultColor);
 
			paintSwatch.Color = resultColor;	
 
            PaintColor resultColor = new PaintColor(paintRecipe.ReactedColor);
 
			paintSwatch.Color = resultColor;
 
            if (paintRecipe.CheckMissingReactions(ref newMissing))
 
            {
 
                string warningMsg = "";
 

 
                foreach (KeyValuePair<string, string> newEntry in newMissing)
 
                {
 
                    IngredientPair match = missingWarned.Find(x => (x.first.Equals(newEntry.Key) && x.second.Equals(newEntry.Value)));
 
                    if (match == null)
 
                    {
 
                        match = new IngredientPair(newEntry.Key, newEntry.Value);
 
                        missingWarned.Add(match);
 
                        warningMsg += newEntry.Key + " + " + newEntry.Value + "\n";
 
                    }
 
                }
 
                if (warningMsg.Length > 0)
 
                {
 
                    Gtk.MessageDialog md = new Gtk.MessageDialog(this,
 
                        Gtk.DialogFlags.DestroyWithParent,
 
                        Gtk.MessageType.Warning,
 
                        Gtk.ButtonsType.Ok,
 
                        "These combinations have not yet had reactions recorded:\n\n" +
 
                        warningMsg);
 
                    md.Run();
 
                    md.Destroy();
 
                }
 
            }
 
            
 
		}
 
		
 
		protected virtual void OnIncrementReagent (object sender, System.EventArgs e)
bin/Debug/DesertPaintLab/template/ingredients.txt
Show inline comments
...
 
@@ -5,17 +5,17 @@
 
// It is not recommended to set many of the ingredients above 10 per paint
 

 
Cabbage		| 128, 64, 144   | 8      | Y | bulk    | 10
 
Clay		| 128, 96, 32    | 4      | Y | bulk    | 20
 
Carrot		| 224, 112, 32   | 10     | Y | bulk    | 10
 
Copper		| 64, 192, 192   | 30     | Y | normal  | 8
 
Iron		| 96, 48, 32     | 30     | Y | normal  | 8
 
Carrot		| 224, 112, 32   | 8      | Y | bulk    | 10
 
Clay		| 128, 96, 32    | 10     | Y | bulk    | 20
 
DeadTongue	| 112, 64, 64    | 500    | Y | normal  | 4
 
ToadSkin	| 48, 96, 48     | 500    | Y | normal  | 4
 
EarthLight	| 128, 240, 224  | 10000  | Y | normal  | 4
 
RedSand		| 144, 16, 24    | 4      | Y | bulk    | 20
 
Lead		| 80, 80, 96     | 50     | Y | normal  | 6
 
RedSand		| 144, 16, 24    | 10     | Y | bulk    | 20
 
Silver		| 16, 16, 32     | 50     | N | normal  | 6
 
ToadSkin	| 48, 96, 48     | 500    | N | normal  | 4
 
DeadTongue	| 112, 64, 64    | 500    | N | normal  | 4
 
EarthLight	| 128, 240, 224  | 10000  | N | normal  | 4
 
Silver		| 16, 16, 32     | 50     | Y | normal  | 6
 
Iron		| 96, 48, 32     | 30     | Y | normal  | 8
 
Copper		| 64, 192, 192   | 30     | Y | normal  | 8
 
Sulfur      | catalyst       | 15     | Y | normal  | 1
 
Potash      | catalyst       | 50     | Y | normal  | 1
 
Lime        | catalyst       | 20     | Y | normal  | 1
 
Sulfur      | catalyst       | 10     | Y | normal  | 1
 
Potash      | catalyst       | 50     | Y | normal  | 1
 
Saltpeter   | catalyst       | 10     | Y | normal  | 1
...
 
\ No newline at end of file
 
Saltpeter   | catalyst       | 10     | Y | normal  | 1
bin/Debug/ingredients.txt
Show inline comments
 
new file 100644
 
// Desert Paint Lab Ingredients
 
// Name | Practical Paint Name | RGB values
 
// These should be kept in the order they show up on the paint bench
 

 
Cabbage Juice	| Cabbage	| 128, 64, 144
 
Carrot		| Carrot	| 224, 112, 32
 
Clay		| Clay		| 128, 96, 32
 
Dead Tongue	| DeadTongue	| 112, 64, 64
 
Toad Skin	| ToadSkin	| 48, 96, 48
 
Earth Light	| EarthLight	| 128, 240, 224
 
Red Sand	| RedSand	| 144, 16, 24
 
Lead		| Lead		| 80, 80, 96
 
Silver Powder	| Silver	| 16, 16, 32
 
Iron		| Iron		| 96, 48, 32
 
Copper		| Copper	| 64, 192, 192
 
Sulfur		| Sulfur	| catalyst
 
Potash		| Potash	| catalyst
 
Lime		| Lime		| catalyst
 
Saltpeter	| Saltpeter	| catalyst
bin/Debug/template/ingredients.txt
Show inline comments
...
 
@@ -5,17 +5,17 @@
 
// It is not recommended to set many of the ingredients above 10 per paint
 

 
Cabbage		| 128, 64, 144   | 8      | Y | bulk    | 10
 
Clay		| 128, 96, 32    | 4      | Y | bulk    | 20
 
Carrot		| 224, 112, 32   | 10     | Y | bulk    | 10
 
Copper		| 64, 192, 192   | 30     | Y | normal  | 8
 
Iron		| 96, 48, 32     | 30     | Y | normal  | 8
 
Carrot		| 224, 112, 32   | 8      | Y | bulk    | 10
 
Clay		| 128, 96, 32    | 10     | Y | bulk    | 20
 
DeadTongue	| 112, 64, 64    | 500    | Y | normal  | 4
 
ToadSkin	| 48, 96, 48     | 500    | Y | normal  | 4
 
EarthLight	| 128, 240, 224  | 10000  | Y | normal  | 4
 
RedSand		| 144, 16, 24    | 4      | Y | bulk    | 20
 
Lead		| 80, 80, 96     | 50     | Y | normal  | 6
 
RedSand		| 144, 16, 24    | 10     | Y | bulk    | 20
 
Silver		| 16, 16, 32     | 50     | N | normal  | 6
 
ToadSkin	| 48, 96, 48     | 500    | N | normal  | 4
 
DeadTongue	| 112, 64, 64    | 500    | N | normal  | 4
 
EarthLight	| 128, 240, 224  | 10000  | N | normal  | 4
 
Silver		| 16, 16, 32     | 50     | Y | normal  | 6
 
Iron		| 96, 48, 32     | 30     | Y | normal  | 8
 
Copper		| 64, 192, 192   | 30     | Y | normal  | 8
 
Sulfur      | catalyst       | 15     | Y | normal  | 1
 
Potash      | catalyst       | 50     | Y | normal  | 1
 
Lime        | catalyst       | 20     | Y | normal  | 1
 
Sulfur      | catalyst       | 10     | Y | normal  | 1
 
Potash      | catalyst       | 50     | Y | normal  | 1
 
Saltpeter   | catalyst       | 10     | Y | normal  | 1
...
 
\ No newline at end of file
 
Saltpeter   | catalyst       | 10     | Y | normal  | 1
bin/Release/ingredients.txt
Show inline comments
 
new file 100644
 
// Desert Paint Lab Ingredients
 
// Name | Practical Paint Name | RGB values
 
// These should be kept in the order they show up on the paint bench
 

 
Cabbage Juice	| Cabbage	| 128, 64, 144
 
Carrot		| Carrot	| 224, 112, 32
 
Clay		| Clay		| 128, 96, 32
 
Dead Tongue	| DeadTongue	| 112, 64, 64
 
Toad Skin	| ToadSkin	| 48, 96, 48
 
Earth Light	| EarthLight	| 128, 240, 224
 
Red Sand	| RedSand	| 144, 16, 24
 
Lead		| Lead		| 80, 80, 96
 
Silver Powder	| Silver	| 16, 16, 32
 
Iron		| Iron		| 96, 48, 32
 
Copper		| Copper	| 64, 192, 192
 
Sulfur		| Sulfur	| catalyst
 
Potash		| Potash	| catalyst
 
Lime		| Lime		| catalyst
 
Saltpeter	| Saltpeter	| catalyst
bin/Release/template/ingredients.txt
Show inline comments
...
 
@@ -5,17 +5,17 @@
 
// It is not recommended to set many of the ingredients above 10 per paint
 

 
Cabbage		| 128, 64, 144   | 8      | Y | bulk    | 10
 
Clay		| 128, 96, 32    | 4      | Y | bulk    | 20
 
Carrot		| 224, 112, 32   | 10     | Y | bulk    | 10
 
Copper		| 64, 192, 192   | 30     | Y | normal  | 8
 
Iron		| 96, 48, 32     | 30     | Y | normal  | 8
 
Carrot		| 224, 112, 32   | 8      | Y | bulk    | 10
 
Clay		| 128, 96, 32    | 10     | Y | bulk    | 20
 
DeadTongue	| 112, 64, 64    | 500    | Y | normal  | 4
 
ToadSkin	| 48, 96, 48     | 500    | Y | normal  | 4
 
EarthLight	| 128, 240, 224  | 10000  | Y | normal  | 4
 
RedSand		| 144, 16, 24    | 4      | Y | bulk    | 20
 
Lead		| 80, 80, 96     | 50     | Y | normal  | 6
 
RedSand		| 144, 16, 24    | 10     | Y | bulk    | 20
 
Silver		| 16, 16, 32     | 50     | N | normal  | 6
 
ToadSkin	| 48, 96, 48     | 500    | N | normal  | 4
 
DeadTongue	| 112, 64, 64    | 500    | N | normal  | 4
 
EarthLight	| 128, 240, 224  | 10000  | N | normal  | 4
 
Silver		| 16, 16, 32     | 50     | Y | normal  | 6
 
Iron		| 96, 48, 32     | 30     | Y | normal  | 8
 
Copper		| 64, 192, 192   | 30     | Y | normal  | 8
 
Sulfur      | catalyst       | 15     | Y | normal  | 1
 
Potash      | catalyst       | 50     | Y | normal  | 1
 
Lime        | catalyst       | 20     | Y | normal  | 1
 
Sulfur      | catalyst       | 10     | Y | normal  | 1
 
Potash      | catalyst       | 50     | Y | normal  | 1
 
Saltpeter   | catalyst       | 10     | Y | normal  | 1
...
 
\ No newline at end of file
 
Saltpeter   | catalyst       | 10     | Y | normal  | 1
gtk-gui/DesertPaintLab.ReagentWindow.cs
Show inline comments
 
new file 100644
 

	
 
// This file has been generated by the GUI designer. Do not modify.
 
namespace DesertPaintLab
 
{
 
	public partial class ReagentWindow
 
	{
 
		private global::Gtk.VBox vbox2;
 
		
 
		private global::Gtk.Frame frame3;
 
		
 
		private global::Gtk.Alignment GtkAlignment;
 
		
 
		private global::Gtk.Table ingredientTable;
 
		
 
		private global::Gtk.Label GtkLabel;
 
		
 
		private global::Gtk.HButtonBox hbuttonbox3;
 
		
 
		private global::Gtk.Button okButton;
 
		
 
		private global::Gtk.Button cancelButton;
 

	
 
		protected virtual void Build ()
 
		{
 
			global::Stetic.Gui.Initialize (this);
 
			// Widget DesertPaintLab.ReagentWindow
 
			this.Name = "DesertPaintLab.ReagentWindow";
 
			this.Title = "ReagentWindow";
 
			this.WindowPosition = ((global::Gtk.WindowPosition)(4));
 
			// Container child DesertPaintLab.ReagentWindow.Gtk.Container+ContainerChild
 
			this.vbox2 = new global::Gtk.VBox ();
 
			this.vbox2.Name = "vbox2";
 
			this.vbox2.Spacing = 6;
 
			this.vbox2.BorderWidth = ((uint)(8));
 
			// Container child vbox2.Gtk.Box+BoxChild
 
			this.frame3 = new global::Gtk.Frame ();
 
			this.frame3.Name = "frame3";
 
			this.frame3.ShadowType = ((global::Gtk.ShadowType)(0));
 
			// Container child frame3.Gtk.Container+ContainerChild
 
			this.GtkAlignment = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
 
			this.GtkAlignment.Name = "GtkAlignment";
 
			this.GtkAlignment.LeftPadding = ((uint)(12));
 
			// Container child GtkAlignment.Gtk.Container+ContainerChild
 
			this.ingredientTable = new global::Gtk.Table (((uint)(3)), ((uint)(3)), false);
 
			this.ingredientTable.Name = "ingredientTable";
 
			this.ingredientTable.RowSpacing = ((uint)(6));
 
			this.ingredientTable.ColumnSpacing = ((uint)(6));
 
			this.GtkAlignment.Add (this.ingredientTable);
 
			this.frame3.Add (this.GtkAlignment);
 
			this.GtkLabel = new global::Gtk.Label ();
 
			this.GtkLabel.Name = "GtkLabel";
 
			this.GtkLabel.LabelProp = "<b>Ingredients</b>";
 
			this.GtkLabel.UseMarkup = true;
 
			this.frame3.LabelWidget = this.GtkLabel;
 
			this.vbox2.Add (this.frame3);
 
			global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.frame3]));
 
			w3.Position = 0;
 
			// Container child vbox2.Gtk.Box+BoxChild
 
			this.hbuttonbox3 = new global::Gtk.HButtonBox ();
 
			this.hbuttonbox3.Name = "hbuttonbox3";
 
			// Container child hbuttonbox3.Gtk.ButtonBox+ButtonBoxChild
 
			this.okButton = new global::Gtk.Button ();
 
			this.okButton.WidthRequest = 80;
 
			this.okButton.CanFocus = true;
 
			this.okButton.Name = "okButton";
 
			this.okButton.UseUnderline = true;
 
			this.okButton.Label = "Ok";
 
			this.hbuttonbox3.Add (this.okButton);
 
			global::Gtk.ButtonBox.ButtonBoxChild w4 = ((global::Gtk.ButtonBox.ButtonBoxChild)(this.hbuttonbox3 [this.okButton]));
 
			w4.Expand = false;
 
			w4.Fill = false;
 
			// Container child hbuttonbox3.Gtk.ButtonBox+ButtonBoxChild
 
			this.cancelButton = new global::Gtk.Button ();
 
			this.cancelButton.WidthRequest = 80;
 
			this.cancelButton.CanFocus = true;
 
			this.cancelButton.Name = "cancelButton";
 
			this.cancelButton.UseUnderline = true;
 
			this.cancelButton.Label = "Cancel";
 
			this.hbuttonbox3.Add (this.cancelButton);
 
			global::Gtk.ButtonBox.ButtonBoxChild w5 = ((global::Gtk.ButtonBox.ButtonBoxChild)(this.hbuttonbox3 [this.cancelButton]));
 
			w5.Position = 1;
 
			w5.Expand = false;
 
			w5.Fill = false;
 
			this.vbox2.Add (this.hbuttonbox3);
 
			global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbuttonbox3]));
 
			w6.PackType = ((global::Gtk.PackType)(1));
 
			w6.Position = 1;
 
			w6.Expand = false;
 
			w6.Fill = false;
 
			this.Add (this.vbox2);
 
			if ((this.Child != null)) {
 
				this.Child.ShowAll ();
 
			}
 
			this.DefaultWidth = 400;
 
			this.DefaultHeight = 357;
 
			this.Show ();
 
			this.okButton.Clicked += new global::System.EventHandler (this.OnOK);
 
			this.cancelButton.Clicked += new global::System.EventHandler (this.OnCancel);
 
		}
 
	}
 
}
gtk-gui/DesertPaintLab.RecipeGeneratorWindow.cs
Show inline comments
 
new file 100644
 

	
 
// This file has been generated by the GUI designer. Do not modify.
 
namespace DesertPaintLab
 
{
 
	public partial class RecipeGeneratorWindow
 
	{
 
		private global::Gtk.UIManager UIManager;
 
		
 
		private global::Gtk.Action ExportAction;
 
		
 
		private global::Gtk.Action ExportToWikiAction;
 
		
 
		private global::Gtk.Action SettingsAction;
 
		
 
		private global::Gtk.Action IngredientsAction;
 
		
 
		private global::Gtk.VBox vbox2;
 
		
 
		private global::Gtk.MenuBar menubar1;
 
		
 
		private global::Gtk.HBox hbox1;
 
		
 
		private global::Gtk.VBox vbox8;
 
		
 
		private global::Gtk.Label label3;
 
		
 
		private global::Gtk.SpinButton maxIngredientsSpinButton;
 
		
 
		private global::Gtk.HSeparator hseparator3;
 
		
 
		private global::Gtk.Label label4;
 
		
 
		private global::Gtk.SpinButton maxRecipeSpinButton;
 
		
 
		private global::Gtk.HSeparator hseparator4;
 
		
 
		private global::Gtk.Label label8;
 
		
 
		private global::Gtk.SpinButton fullQuantityDepthSpinButton;
 
		
 
		private global::Gtk.Label label9;
 
		
 
		private global::Gtk.SpinButton fullQuantitySpinButton;
 
		
 
		private global::Gtk.ScrolledWindow GtkScrolledWindow1;
 
		
 
		private global::Gtk.TreeView recipeList;
 
		
 
		private global::Gtk.VBox vbox3;
 
		
 
		private global::Gtk.Frame frame2;
 
		
 
		private global::Gtk.Alignment GtkAlignment;
 
		
 
		private global::Gtk.ScrolledWindow scrolledwindow1;
 
		
 
		private global::Gtk.VBox recipeListBox;
 
		
 
		private global::Gtk.Label recipeLabel;
 
		
 
		private global::DesertPaintLab.PaintSwatch paintSwatch;
 
		
 
		private global::Gtk.HBox hbox3;
 
		
 
		private global::Gtk.HSeparator hseparator2;
 
		
 
		private global::Gtk.Button stopResumeButton;
 
		
 
		private global::Gtk.Label countLabel;
 
		
 
		private global::Gtk.Button beginButton;
 
		
 
		private global::Gtk.HSeparator hseparator1;
 
		
 
		private global::Gtk.Label statusLabel;
 
		
 
		private global::Gtk.ProgressBar progressBar;
 

	
 
		protected virtual void Build ()
 
		{
 
			global::Stetic.Gui.Initialize (this);
 
			// Widget DesertPaintLab.RecipeGeneratorWindow
 
			this.UIManager = new global::Gtk.UIManager ();
 
			global::Gtk.ActionGroup w1 = new global::Gtk.ActionGroup ("Default");
 
			this.ExportAction = new global::Gtk.Action ("ExportAction", "Export", null, null);
 
			this.ExportAction.ShortLabel = "Export";
 
			w1.Add (this.ExportAction, null);
 
			this.ExportToWikiAction = new global::Gtk.Action ("ExportToWikiAction", "Export to Wiki", null, null);
 
			this.ExportToWikiAction.ShortLabel = "Export to Wiki";
 
			w1.Add (this.ExportToWikiAction, null);
 
			this.SettingsAction = new global::Gtk.Action ("SettingsAction", "Settings", null, null);
 
			this.SettingsAction.ShortLabel = "Tools";
 
			w1.Add (this.SettingsAction, null);
 
			this.IngredientsAction = new global::Gtk.Action ("IngredientsAction", "Ingredients", null, null);
 
			this.IngredientsAction.ShortLabel = "Ingredients";
 
			w1.Add (this.IngredientsAction, null);
 
			this.UIManager.InsertActionGroup (w1, 0);
 
			this.AddAccelGroup (this.UIManager.AccelGroup);
 
			this.Name = "DesertPaintLab.RecipeGeneratorWindow";
 
			this.Title = "Recipe Generator";
 
			this.WindowPosition = ((global::Gtk.WindowPosition)(4));
 
			// Container child DesertPaintLab.RecipeGeneratorWindow.Gtk.Container+ContainerChild
 
			this.vbox2 = new global::Gtk.VBox ();
 
			this.vbox2.Name = "vbox2";
 
			this.vbox2.Spacing = 6;
 
			this.vbox2.BorderWidth = ((uint)(8));
 
			// Container child vbox2.Gtk.Box+BoxChild
 
			this.UIManager.AddUiFromString ("<ui><menubar name='menubar1'><menu name='ExportAction' action='ExportAction'><menuitem name='ExportToWikiAction' action='ExportToWikiAction'/></menu><menu name='SettingsAction' action='SettingsAction'><menuitem name='IngredientsAction' action='IngredientsAction'/></menu></menubar></ui>");
 
			this.menubar1 = ((global::Gtk.MenuBar)(this.UIManager.GetWidget ("/menubar1")));
 
			this.menubar1.Name = "menubar1";
 
			this.vbox2.Add (this.menubar1);
 
			global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.menubar1]));
 
			w2.Position = 0;
 
			w2.Expand = false;
 
			w2.Fill = false;
 
			// Container child vbox2.Gtk.Box+BoxChild
 
			this.hbox1 = new global::Gtk.HBox ();
 
			this.hbox1.Name = "hbox1";
 
			this.hbox1.Spacing = 6;
 
			// Container child hbox1.Gtk.Box+BoxChild
 
			this.vbox8 = new global::Gtk.VBox ();
 
			this.vbox8.Name = "vbox8";
 
			this.vbox8.Spacing = 6;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.label3 = new global::Gtk.Label ();
 
			this.label3.Name = "label3";
 
			this.label3.LabelProp = "Maximum Ingredients";
 
			this.vbox8.Add (this.label3);
 
			global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label3]));
 
			w3.Position = 0;
 
			w3.Expand = false;
 
			w3.Fill = false;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.maxIngredientsSpinButton = new global::Gtk.SpinButton (0, 14, 1);
 
			this.maxIngredientsSpinButton.CanFocus = true;
 
			this.maxIngredientsSpinButton.Name = "maxIngredientsSpinButton";
 
			this.maxIngredientsSpinButton.Adjustment.PageIncrement = 10;
 
			this.maxIngredientsSpinButton.ClimbRate = 1;
 
			this.maxIngredientsSpinButton.Numeric = true;
 
			this.vbox8.Add (this.maxIngredientsSpinButton);
 
			global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.maxIngredientsSpinButton]));
 
			w4.Position = 1;
 
			w4.Expand = false;
 
			w4.Fill = false;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.hseparator3 = new global::Gtk.HSeparator ();
 
			this.hseparator3.Name = "hseparator3";
 
			this.vbox8.Add (this.hseparator3);
 
			global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator3]));
 
			w5.Position = 2;
 
			w5.Expand = false;
 
			w5.Fill = false;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.label4 = new global::Gtk.Label ();
 
			this.label4.Name = "label4";
 
			this.label4.LabelProp = "Max Total Quantity";
 
			this.label4.UseMarkup = true;
 
			this.label4.Wrap = true;
 
			this.vbox8.Add (this.label4);
 
			global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label4]));
 
			w6.Position = 3;
 
			w6.Expand = false;
 
			w6.Fill = false;
 
			w6.Padding = ((uint)(8));
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.maxRecipeSpinButton = new global::Gtk.SpinButton (0, 100, 1);
 
			this.maxRecipeSpinButton.CanFocus = true;
 
			this.maxRecipeSpinButton.Name = "maxRecipeSpinButton";
 
			this.maxRecipeSpinButton.Adjustment.PageIncrement = 10;
 
			this.maxRecipeSpinButton.ClimbRate = 1;
 
			this.maxRecipeSpinButton.Numeric = true;
 
			this.vbox8.Add (this.maxRecipeSpinButton);
 
			global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.maxRecipeSpinButton]));
 
			w7.Position = 4;
 
			w7.Expand = false;
 
			w7.Fill = false;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.hseparator4 = new global::Gtk.HSeparator ();
 
			this.hseparator4.Name = "hseparator4";
 
			this.vbox8.Add (this.hseparator4);
 
			global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator4]));
 
			w8.Position = 5;
 
			w8.Expand = false;
 
			w8.Fill = false;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.label8 = new global::Gtk.Label ();
 
			this.label8.Name = "label8";
 
			this.label8.LabelProp = "Full Quantity Depth";
 
			this.vbox8.Add (this.label8);
 
			global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label8]));
 
			w9.Position = 6;
 
			w9.Expand = false;
 
			w9.Fill = false;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.fullQuantityDepthSpinButton = new global::Gtk.SpinButton (0, 15, 1);
 
			this.fullQuantityDepthSpinButton.CanFocus = true;
 
			this.fullQuantityDepthSpinButton.Name = "fullQuantityDepthSpinButton";
 
			this.fullQuantityDepthSpinButton.Adjustment.PageIncrement = 10;
 
			this.fullQuantityDepthSpinButton.ClimbRate = 1;
 
			this.fullQuantityDepthSpinButton.Numeric = true;
 
			this.vbox8.Add (this.fullQuantityDepthSpinButton);
 
			global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.fullQuantityDepthSpinButton]));
 
			w10.Position = 7;
 
			w10.Expand = false;
 
			w10.Fill = false;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.label9 = new global::Gtk.Label ();
 
			this.label9.Name = "label9";
 
			this.label9.LabelProp = "FullQuantity";
 
			this.vbox8.Add (this.label9);
 
			global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label9]));
 
			w11.Position = 8;
 
			w11.Expand = false;
 
			w11.Fill = false;
 
			// Container child vbox8.Gtk.Box+BoxChild
 
			this.fullQuantitySpinButton = new global::Gtk.SpinButton (0, 30, 1);
 
			this.fullQuantitySpinButton.CanFocus = true;
 
			this.fullQuantitySpinButton.Name = "fullQuantitySpinButton";
 
			this.fullQuantitySpinButton.Adjustment.PageIncrement = 10;
 
			this.fullQuantitySpinButton.ClimbRate = 1;
 
			this.fullQuantitySpinButton.Numeric = true;
 
			this.vbox8.Add (this.fullQuantitySpinButton);
 
			global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.fullQuantitySpinButton]));
 
			w12.Position = 9;
 
			w12.Expand = false;
 
			w12.Fill = false;
 
			this.hbox1.Add (this.vbox8);
 
			global::Gtk.Box.BoxChild w13 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox8]));
 
			w13.Position = 0;
 
			w13.Expand = false;
 
			w13.Fill = false;
 
			// Container child hbox1.Gtk.Box+BoxChild
 
			this.GtkScrolledWindow1 = new global::Gtk.ScrolledWindow ();
 
			this.GtkScrolledWindow1.Name = "GtkScrolledWindow1";
 
			this.GtkScrolledWindow1.ShadowType = ((global::Gtk.ShadowType)(1));
 
			// Container child GtkScrolledWindow1.Gtk.Container+ContainerChild
 
			this.recipeList = new global::Gtk.TreeView ();
 
			this.recipeList.WidthRequest = 300;
 
			this.recipeList.CanFocus = true;
 
			this.recipeList.Name = "recipeList";
 
			this.GtkScrolledWindow1.Add (this.recipeList);
 
			this.hbox1.Add (this.GtkScrolledWindow1);
 
			global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.GtkScrolledWindow1]));
 
			w15.Position = 1;
 
			// Container child hbox1.Gtk.Box+BoxChild
 
			this.vbox3 = new global::Gtk.VBox ();
 
			this.vbox3.Name = "vbox3";
 
			this.vbox3.Spacing = 6;
 
			// Container child vbox3.Gtk.Box+BoxChild
 
			this.frame2 = new global::Gtk.Frame ();
 
			this.frame2.WidthRequest = 200;
 
			this.frame2.HeightRequest = 200;
 
			this.frame2.Name = "frame2";
 
			this.frame2.ShadowType = ((global::Gtk.ShadowType)(0));
 
			// Container child frame2.Gtk.Container+ContainerChild
 
			this.GtkAlignment = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
 
			this.GtkAlignment.Name = "GtkAlignment";
 
			this.GtkAlignment.LeftPadding = ((uint)(12));
 
			// Container child GtkAlignment.Gtk.Container+ContainerChild
 
			this.scrolledwindow1 = new global::Gtk.ScrolledWindow ();
 
			this.scrolledwindow1.CanFocus = true;
 
			this.scrolledwindow1.Name = "scrolledwindow1";
 
			this.scrolledwindow1.ShadowType = ((global::Gtk.ShadowType)(1));
 
			// Container child scrolledwindow1.Gtk.Container+ContainerChild
 
			global::Gtk.Viewport w16 = new global::Gtk.Viewport ();
 
			w16.ShadowType = ((global::Gtk.ShadowType)(0));
 
			// Container child GtkViewport.Gtk.Container+ContainerChild
 
			this.recipeListBox = new global::Gtk.VBox ();
 
			this.recipeListBox.Name = "recipeListBox";
 
			this.recipeListBox.Homogeneous = true;
 
			this.recipeListBox.Spacing = 6;
 
			this.recipeListBox.BorderWidth = ((uint)(1));
 
			w16.Add (this.recipeListBox);
 
			this.scrolledwindow1.Add (w16);
 
			this.GtkAlignment.Add (this.scrolledwindow1);
 
			this.frame2.Add (this.GtkAlignment);
 
			this.recipeLabel = new global::Gtk.Label ();
 
			this.recipeLabel.Name = "recipeLabel";
 
			this.recipeLabel.LabelProp = "<b>Recipe</b>";
 
			this.recipeLabel.UseMarkup = true;
 
			this.frame2.LabelWidget = this.recipeLabel;
 
			this.vbox3.Add (this.frame2);
 
			global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.frame2]));
 
			w21.Position = 0;
 
			// Container child vbox3.Gtk.Box+BoxChild
 
			this.paintSwatch = new global::DesertPaintLab.PaintSwatch ();
 
			this.paintSwatch.HeightRequest = 200;
 
			this.paintSwatch.Events = ((global::Gdk.EventMask)(256));
 
			this.paintSwatch.Name = "paintSwatch";
 
			this.vbox3.Add (this.paintSwatch);
 
			global::Gtk.Box.BoxChild w22 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.paintSwatch]));
 
			w22.Position = 1;
 
			this.hbox1.Add (this.vbox3);
 
			global::Gtk.Box.BoxChild w23 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox3]));
 
			w23.Position = 2;
 
			w23.Expand = false;
 
			w23.Fill = false;
 
			this.vbox2.Add (this.hbox1);
 
			global::Gtk.Box.BoxChild w24 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox1]));
 
			w24.Position = 1;
 
			// Container child vbox2.Gtk.Box+BoxChild
 
			this.hbox3 = new global::Gtk.HBox ();
 
			this.hbox3.Name = "hbox3";
 
			this.hbox3.Spacing = 6;
 
			// Container child hbox3.Gtk.Box+BoxChild
 
			this.hseparator2 = new global::Gtk.HSeparator ();
 
			this.hseparator2.Name = "hseparator2";
 
			this.hbox3.Add (this.hseparator2);
 
			global::Gtk.Box.BoxChild w25 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.hseparator2]));
 
			w25.Position = 0;
 
			// Container child hbox3.Gtk.Box+BoxChild
 
			this.stopResumeButton = new global::Gtk.Button ();
 
			this.stopResumeButton.Sensitive = false;
 
			this.stopResumeButton.CanFocus = true;
 
			this.stopResumeButton.Name = "stopResumeButton";
 
			this.stopResumeButton.UseUnderline = true;
 
			this.stopResumeButton.Label = "Stop";
 
			this.hbox3.Add (this.stopResumeButton);
 
			global::Gtk.Box.BoxChild w26 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.stopResumeButton]));
 
			w26.Position = 1;
 
			w26.Expand = false;
 
			w26.Fill = false;
 
			w26.Padding = ((uint)(20));
 
			// Container child hbox3.Gtk.Box+BoxChild
 
			this.countLabel = new global::Gtk.Label ();
 
			this.countLabel.Name = "countLabel";
 
			this.countLabel.LabelProp = "0/192";
 
			this.hbox3.Add (this.countLabel);
 
			global::Gtk.Box.BoxChild w27 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.countLabel]));
 
			w27.Position = 2;
 
			w27.Expand = false;
 
			w27.Fill = false;
 
			w27.Padding = ((uint)(40));
 
			// Container child hbox3.Gtk.Box+BoxChild
 
			this.beginButton = new global::Gtk.Button ();
 
			this.beginButton.CanFocus = true;
 
			this.beginButton.Name = "beginButton";
 
			this.beginButton.UseUnderline = true;
 
			this.beginButton.Label = "Begin";
 
			this.hbox3.Add (this.beginButton);
 
			global::Gtk.Box.BoxChild w28 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.beginButton]));
 
			w28.Position = 3;
 
			w28.Expand = false;
 
			w28.Fill = false;
 
			w28.Padding = ((uint)(20));
 
			// Container child hbox3.Gtk.Box+BoxChild
 
			this.hseparator1 = new global::Gtk.HSeparator ();
 
			this.hseparator1.Name = "hseparator1";
 
			this.hbox3.Add (this.hseparator1);
 
			global::Gtk.Box.BoxChild w29 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.hseparator1]));
 
			w29.Position = 4;
 
			this.vbox2.Add (this.hbox3);
 
			global::Gtk.Box.BoxChild w30 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox3]));
 
			w30.Position = 2;
 
			w30.Expand = false;
 
			w30.Fill = false;
 
			// Container child vbox2.Gtk.Box+BoxChild
 
			this.statusLabel = new global::Gtk.Label ();
 
			this.statusLabel.Name = "statusLabel";
 
			this.vbox2.Add (this.statusLabel);
 
			global::Gtk.Box.BoxChild w31 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.statusLabel]));
 
			w31.PackType = ((global::Gtk.PackType)(1));
 
			w31.Position = 3;
 
			w31.Expand = false;
 
			w31.Fill = false;
 
			// Container child vbox2.Gtk.Box+BoxChild
 
			this.progressBar = new global::Gtk.ProgressBar ();
 
			this.progressBar.Name = "progressBar";
 
			this.vbox2.Add (this.progressBar);
 
			global::Gtk.Box.BoxChild w32 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.progressBar]));
 
			w32.PackType = ((global::Gtk.PackType)(1));
 
			w32.Position = 4;
 
			w32.Expand = false;
 
			w32.Fill = false;
 
			this.Add (this.vbox2);
 
			if ((this.Child != null)) {
 
				this.Child.ShowAll ();
 
			}
 
			this.DefaultWidth = 887;
 
			this.DefaultHeight = 570;
 
			this.Show ();
 
			this.ExportToWikiAction.Activated += new global::System.EventHandler (this.OnExportToWiki);
 
			this.IngredientsAction.Activated += new global::System.EventHandler (this.OnShowIngredients);
 
			this.maxIngredientsSpinButton.ValueChanged += new global::System.EventHandler (this.OnMaxIngredientsChanged);
 
			this.maxRecipeSpinButton.ValueChanged += new global::System.EventHandler (this.OnMaxRecipeChanged);
 
			this.fullQuantityDepthSpinButton.ValueChanged += new global::System.EventHandler (this.OnFullQuantityDepthChanged);
 
			this.fullQuantitySpinButton.Input += new global::Gtk.InputHandler (this.OnFullQuantityChanged);
 
			this.stopResumeButton.Clicked += new global::System.EventHandler (this.OnStopResume);
 
			this.beginButton.Clicked += new global::System.EventHandler (this.OnBegin);
 
		}
 
	}
 
}
gtk-gui/MainWindow.cs
Show inline comments
...
 
@@ -27,10 +27,12 @@ public partial class MainWindow
 
	
 
	private global::Gtk.Action ScreenshotAction;
 
	
 
	private global::Gtk.Action RecipeGeneratorAction;
 
	private global::Gtk.Action RecipesAction;
 
	
 
	private global::Gtk.Action ReactionStatusAction;
 
	
 
	private global::Gtk.Action IngredientsAction;
 
	
 
	private global::Gtk.VBox vbox1;
 
	
 
	private global::Gtk.MenuBar menubar1;
...
 
@@ -128,12 +130,15 @@ public partial class MainWindow
 
		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.RecipesAction = new global::Gtk.Action ("RecipesAction", "Recipes", null, null);
 
		this.RecipesAction.ShortLabel = "Recipe Generator";
 
		w1.Add (this.RecipesAction, null);
 
		this.ReactionStatusAction = new global::Gtk.Action ("ReactionStatusAction", "Reaction Status", null, null);
 
		this.ReactionStatusAction.ShortLabel = "Reaction Status";
 
		w1.Add (this.ReactionStatusAction, null);
 
		this.IngredientsAction = new global::Gtk.Action ("IngredientsAction", "Ingredients", null, null);
 
		this.IngredientsAction.ShortLabel = "Ingredients";
 
		w1.Add (this.IngredientsAction, null);
 
		this.UIManager.InsertActionGroup (w1, 0);
 
		this.AddAccelGroup (this.UIManager.AccelGroup);
 
		this.Name = "MainWindow";
...
 
@@ -143,7 +148,7 @@ public partial class MainWindow
 
		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'/><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.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='RecipesAction' action='RecipesAction'/><menuitem name='IngredientsAction' action='IngredientsAction'/></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);
...
 
@@ -374,8 +379,9 @@ public partial class MainWindow
 
		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.RecipesAction.Activated += new global::System.EventHandler (this.OnOpenRecipeGenerator);
 
		this.ReactionStatusAction.Activated += new global::System.EventHandler (this.OnShowReactionStatus);
 
		this.IngredientsAction.Activated += new global::System.EventHandler (this.OnShowIngredients);
 
		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);
gtk-gui/gui.stetic
Show inline comments
...
 
@@ -78,9 +78,9 @@
 
        <property name="ShortLabel" translatable="yes">Screenshot</property>
 
        <signal name="Activated" handler="OnDebugScreenshot" />
 
      </action>
 
      <action id="RecipeGeneratorAction">
 
      <action id="RecipesAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Recipe Generator</property>
 
        <property name="Label" translatable="yes">Recipes</property>
 
        <property name="ShortLabel" translatable="yes">Recipe Generator</property>
 
        <signal name="Activated" handler="OnOpenRecipeGenerator" />
 
      </action>
...
 
@@ -90,6 +90,12 @@
 
        <property name="ShortLabel" translatable="yes">Reaction Status</property>
 
        <signal name="Activated" handler="OnShowReactionStatus" />
 
      </action>
 
      <action id="IngredientsAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Ingredients</property>
 
        <property name="ShortLabel" translatable="yes">Ingredients</property>
 
        <signal name="Activated" handler="OnShowIngredients" />
 
      </action>
 
    </action-group>
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">Desert Paint Lab</property>
...
 
@@ -111,7 +117,8 @@
 
              </node>
 
              <node type="Menu" action="WindowAction">
 
                <node type="Menuitem" action="RunSimulatorAction" />
 
                <node type="Menuitem" action="RecipeGeneratorAction" />
 
                <node type="Menuitem" action="RecipesAction" />
 
                <node type="Menuitem" action="IngredientsAction" />
 
              </node>
 
              <node type="Menu" action="HelpAction">
 
                <node type="Menuitem" action="AboutAction" />
...
 
@@ -1133,4 +1140,549 @@ You can either import an existing Practi
 
      </widget>
 
    </child>
 
  </widget>
 
  <widget class="Gtk.Window" id="DesertPaintLab.RecipeGeneratorWindow" design-size="887 570">
 
    <action-group name="Default">
 
      <action id="ExportAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Export</property>
 
        <property name="ShortLabel" translatable="yes">Export</property>
 
      </action>
 
      <action id="ExportToWikiAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Export to Wiki</property>
 
        <property name="ShortLabel" translatable="yes">Export to Wiki</property>
 
        <signal name="Activated" handler="OnExportToWiki" />
 
      </action>
 
      <action id="SettingsAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Settings</property>
 
        <property name="ShortLabel" translatable="yes">Tools</property>
 
      </action>
 
      <action id="IngredientsAction">
 
        <property name="Type">Action</property>
 
        <property name="Label" translatable="yes">Ingredients</property>
 
        <property name="ShortLabel" translatable="yes">Ingredients</property>
 
        <signal name="Activated" handler="OnShowIngredients" />
 
      </action>
 
    </action-group>
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">Recipe Generator</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <child>
 
      <widget class="Gtk.VBox" id="vbox2">
 
        <property name="MemberName" />
 
        <property name="Spacing">6</property>
 
        <property name="BorderWidth">8</property>
 
        <child>
 
          <widget class="Gtk.MenuBar" id="menubar1">
 
            <property name="MemberName" />
 
            <node name="__gtksharp_64_Stetic_Editor_ActionMenuBar" type="Menubar">
 
              <node type="Menu" action="ExportAction">
 
                <node type="Menuitem" action="ExportToWikiAction" />
 
              </node>
 
              <node type="Menu" action="SettingsAction">
 
                <node type="Menuitem" action="IngredientsAction" />
 
              </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>
 
            <child>
 
              <widget class="Gtk.VBox" id="vbox8">
 
                <property name="MemberName" />
 
                <property name="Spacing">6</property>
 
                <child>
 
                  <widget class="Gtk.Label" id="label3">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">Maximum Ingredients</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.SpinButton" id="maxIngredientsSpinButton">
 
                    <property name="MemberName" />
 
                    <property name="CanFocus">True</property>
 
                    <property name="Upper">14</property>
 
                    <property name="PageIncrement">10</property>
 
                    <property name="StepIncrement">1</property>
 
                    <property name="ClimbRate">1</property>
 
                    <property name="Numeric">True</property>
 
                    <signal name="ValueChanged" handler="OnMaxIngredientsChanged" />
 
                  </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.HSeparator" id="hseparator3">
 
                    <property name="MemberName" />
 
                  </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.Label" id="label4">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">Max Total Quantity</property>
 
                    <property name="UseMarkup">True</property>
 
                    <property name="Wrap">True</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">3</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                    <property name="Padding">8</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.SpinButton" id="maxRecipeSpinButton">
 
                    <property name="MemberName" />
 
                    <property name="CanFocus">True</property>
 
                    <property name="Upper">100</property>
 
                    <property name="PageIncrement">10</property>
 
                    <property name="StepIncrement">1</property>
 
                    <property name="ClimbRate">1</property>
 
                    <property name="Numeric">True</property>
 
                    <signal name="ValueChanged" handler="OnMaxRecipeChanged" />
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">4</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.HSeparator" id="hseparator4">
 
                    <property name="MemberName" />
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">5</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.Label" id="label8">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">Full Quantity Depth</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">6</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.SpinButton" id="fullQuantityDepthSpinButton">
 
                    <property name="MemberName" />
 
                    <property name="CanFocus">True</property>
 
                    <property name="Upper">15</property>
 
                    <property name="PageIncrement">10</property>
 
                    <property name="StepIncrement">1</property>
 
                    <property name="ClimbRate">1</property>
 
                    <property name="Numeric">True</property>
 
                    <signal name="ValueChanged" handler="OnFullQuantityDepthChanged" />
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">7</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.Label" id="label9">
 
                    <property name="MemberName" />
 
                    <property name="LabelProp" translatable="yes">FullQuantity</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">8</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">False</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="Gtk.SpinButton" id="fullQuantitySpinButton">
 
                    <property name="MemberName" />
 
                    <property name="CanFocus">True</property>
 
                    <property name="Upper">30</property>
 
                    <property name="PageIncrement">10</property>
 
                    <property name="StepIncrement">1</property>
 
                    <property name="ClimbRate">1</property>
 
                    <property name="Numeric">True</property>
 
                    <signal name="Input" handler="OnFullQuantityChanged" />
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">9</property>
 
                    <property name="AutoSize">True</property>
 
                    <property name="Expand">False</property>
 
                    <property name="Fill">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.ScrolledWindow" id="GtkScrolledWindow1">
 
                <property name="MemberName" />
 
                <property name="ShadowType">In</property>
 
                <child>
 
                  <widget class="Gtk.TreeView" id="recipeList">
 
                    <property name="MemberName" />
 
                    <property name="WidthRequest">300</property>
 
                    <property name="CanFocus">True</property>
 
                    <property name="ShowScrollbars">True</property>
 
                  </widget>
 
                </child>
 
              </widget>
 
              <packing>
 
                <property name="Position">1</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.Frame" id="frame2">
 
                    <property name="MemberName" />
 
                    <property name="WidthRequest">200</property>
 
                    <property name="HeightRequest">200</property>
 
                    <property name="ShadowType">None</property>
 
                    <child>
 
                      <widget class="Gtk.Alignment" id="GtkAlignment">
 
                        <property name="MemberName" />
 
                        <property name="Xalign">0</property>
 
                        <property name="Yalign">0</property>
 
                        <property name="LeftPadding">12</property>
 
                        <child>
 
                          <widget class="Gtk.ScrolledWindow" id="scrolledwindow1">
 
                            <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="recipeListBox">
 
                                    <property name="MemberName" />
 
                                    <property name="Homogeneous">True</property>
 
                                    <property name="Spacing">6</property>
 
                                    <property name="BorderWidth">1</property>
 
                                    <child>
 
                                      <placeholder />
 
                                    </child>
 
                                    <child>
 
                                      <placeholder />
 
                                    </child>
 
                                    <child>
 
                                      <placeholder />
 
                                    </child>
 
                                  </widget>
 
                                </child>
 
                              </widget>
 
                            </child>
 
                          </widget>
 
                        </child>
 
                      </widget>
 
                    </child>
 
                    <child>
 
                      <widget class="Gtk.Label" id="recipeLabel">
 
                        <property name="MemberName" />
 
                        <property name="LabelProp" translatable="yes">&lt;b&gt;Recipe&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">True</property>
 
                  </packing>
 
                </child>
 
                <child>
 
                  <widget class="DesertPaintLab.PaintSwatch" id="paintSwatch">
 
                    <property name="MemberName" />
 
                    <property name="HeightRequest">200</property>
 
                    <property name="Events">ButtonPressMask</property>
 
                  </widget>
 
                  <packing>
 
                    <property name="Position">1</property>
 
                    <property name="AutoSize">True</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.HBox" id="hbox3">
 
            <property name="MemberName" />
 
            <property name="Spacing">6</property>
 
            <child>
 
              <widget class="Gtk.HSeparator" id="hseparator2">
 
                <property name="MemberName" />
 
              </widget>
 
              <packing>
 
                <property name="Position">0</property>
 
                <property name="AutoSize">True</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Button" id="stopResumeButton">
 
                <property name="MemberName" />
 
                <property name="Sensitive">False</property>
 
                <property name="CanFocus">True</property>
 
                <property name="Type">TextOnly</property>
 
                <property name="Label" translatable="yes">Stop</property>
 
                <property name="UseUnderline">True</property>
 
                <signal name="Clicked" handler="OnStopResume" />
 
              </widget>
 
              <packing>
 
                <property name="Position">1</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
                <property name="Padding">20</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Label" id="countLabel">
 
                <property name="MemberName" />
 
                <property name="LabelProp" translatable="yes">0/192</property>
 
              </widget>
 
              <packing>
 
                <property name="Position">2</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
                <property name="Padding">40</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Button" id="beginButton">
 
                <property name="MemberName" />
 
                <property name="CanFocus">True</property>
 
                <property name="Type">TextOnly</property>
 
                <property name="Label" translatable="yes">Begin</property>
 
                <property name="UseUnderline">True</property>
 
                <signal name="Clicked" handler="OnBegin" />
 
              </widget>
 
              <packing>
 
                <property name="Position">3</property>
 
                <property name="AutoSize">True</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
                <property name="Padding">20</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.HSeparator" id="hseparator1">
 
                <property name="MemberName" />
 
              </widget>
 
              <packing>
 
                <property name="Position">4</property>
 
                <property name="AutoSize">True</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>
 
        <child>
 
          <widget class="Gtk.Label" id="statusLabel">
 
            <property name="MemberName" />
 
          </widget>
 
          <packing>
 
            <property name="PackType">End</property>
 
            <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.ProgressBar" id="progressBar">
 
            <property name="MemberName" />
 
          </widget>
 
          <packing>
 
            <property name="PackType">End</property>
 
            <property name="Position">4</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.ReagentWindow" design-size="400 357">
 
    <property name="MemberName" />
 
    <property name="Title" translatable="yes">ReagentWindow</property>
 
    <property name="WindowPosition">CenterOnParent</property>
 
    <child>
 
      <widget class="Gtk.VBox" id="vbox2">
 
        <property name="MemberName" />
 
        <property name="Spacing">6</property>
 
        <property name="BorderWidth">8</property>
 
        <child>
 
          <widget class="Gtk.Frame" id="frame3">
 
            <property name="MemberName" />
 
            <property name="ShadowType">None</property>
 
            <child>
 
              <widget class="Gtk.Alignment" id="GtkAlignment">
 
                <property name="MemberName" />
 
                <property name="Xalign">0</property>
 
                <property name="Yalign">0</property>
 
                <property name="LeftPadding">12</property>
 
                <child>
 
                  <widget class="Gtk.Table" id="ingredientTable">
 
                    <property name="MemberName" />
 
                    <property name="NRows">3</property>
 
                    <property name="NColumns">3</property>
 
                    <property name="RowSpacing">6</property>
 
                    <property name="ColumnSpacing">6</property>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                    <child>
 
                      <placeholder />
 
                    </child>
 
                  </widget>
 
                </child>
 
              </widget>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Label" id="GtkLabel">
 
                <property name="MemberName" />
 
                <property name="LabelProp" translatable="yes">&lt;b&gt;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">True</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <widget class="Gtk.HButtonBox" id="hbuttonbox3">
 
            <property name="MemberName" />
 
            <property name="Size">2</property>
 
            <child>
 
              <widget class="Gtk.Button" id="okButton">
 
                <property name="MemberName" />
 
                <property name="WidthRequest">80</property>
 
                <property name="CanFocus">True</property>
 
                <property name="Type">TextOnly</property>
 
                <property name="Label" translatable="yes">Ok</property>
 
                <property name="UseUnderline">True</property>
 
                <signal name="Clicked" handler="OnOK" />
 
              </widget>
 
              <packing>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
            <child>
 
              <widget class="Gtk.Button" id="cancelButton">
 
                <property name="MemberName" />
 
                <property name="WidthRequest">80</property>
 
                <property name="CanFocus">True</property>
 
                <property name="Type">TextOnly</property>
 
                <property name="Label" translatable="yes">Cancel</property>
 
                <property name="UseUnderline">True</property>
 
                <signal name="Clicked" handler="OnCancel" />
 
              </widget>
 
              <packing>
 
                <property name="Position">1</property>
 
                <property name="Expand">False</property>
 
                <property name="Fill">False</property>
 
              </packing>
 
            </child>
 
          </widget>
 
          <packing>
 
            <property name="PackType">End</property>
 
            <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>
 
</stetic-interface>
...
 
\ No newline at end of file
0 comments (0 inline, 0 general)