diff --git a/MainWindow.cs b/MainWindow.cs --- a/MainWindow.cs +++ b/MainWindow.cs @@ -1,813 +1,821 @@ -/* - * Copyright (c) 2010, Tess Snider - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -using System; -using System.IO; -using System.Collections.Generic; -using Gtk; -using DesertPaintLab; - -public partial class MainWindow : Gtk.Window -{ - const int colorTolerance = 2; - - int swatchHeight = 24; - int colorBarWidth = 306; - int redBarSpacing = 32; - int greenBarSpacing = 42; - int blueBarSpacing = 52; - - - bool unsavedData = false; - bool shouldShutDown = false; - string appDataPath; - List profileList = new List(); - PlayerProfile profile = null; - PaintColor expectedColor = new PaintColor(); - PaintColor reactedColor = new PaintColor(); - - int screenWidth = 0; - int screenHeight = 0; - int pixelMultiplier = 1; - - Gdk.Window rootWindow = null; - Gdk.Pixbuf screenBuffer = null; - - Reagent reagent1 = null; - Reagent reagent2 = null; - Reagent reagent3 = null; - - - public bool ShouldShutDown - { - get - { - return shouldShutDown; - } - } - - - public MainWindow () : base(Gtk.WindowType.Toplevel) - { - appDataPath = System.IO.Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "DesertPaintLab"); - - if (!System.IO.Directory.Exists(appDataPath)) - { - System.IO.Directory.CreateDirectory(appDataPath); - } - - DirectoryInfo di = new DirectoryInfo(appDataPath); - DirectoryInfo[] dirs = di.GetDirectories(); - foreach (DirectoryInfo dir in dirs) - { - if (dir.Name != "template") - { - profileList.Add(dir.Name); - } - } - - Palette.Load(System.IO.Path.Combine( - System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), - "colors.txt")); - - Build(); - - unmodifiedSwatch.Clear(); - reactionSwatch.Clear(); - - // get the root window - rootWindow = Gdk.Global.DefaultRootWindow; - - // get its width and height - rootWindow.GetSize(out screenWidth, out screenHeight); - - ScreenCheckDialog screenCheckDialog = new ScreenCheckDialog(); - screenCheckDialog.ScreenWidth = screenWidth; - screenCheckDialog.ScreenHeight = screenHeight; - screenCheckDialog.GamePixelWidth = pixelMultiplier; - ResponseType resp = (ResponseType)screenCheckDialog.Run(); - screenWidth = screenCheckDialog.ScreenWidth; - screenHeight = screenCheckDialog.ScreenHeight; - pixelMultiplier = screenCheckDialog.GamePixelWidth; - screenCheckDialog.Destroy(); - - screenBuffer = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, screenWidth, screenHeight); - - swatchHeight *= pixelMultiplier; - colorBarWidth *= pixelMultiplier; - redBarSpacing *= pixelMultiplier; - greenBarSpacing *= pixelMultiplier; - blueBarSpacing *= pixelMultiplier; - - if (!OpenProfile()) - { - shouldShutDown = true; - } - } - - bool ConfirmedExit() - { - if (unsavedData) - { - MessageDialog md = new MessageDialog(this, - DialogFlags.DestroyWithParent, - MessageType.Warning, ButtonsType.OkCancel, - "Your last reaction was unsaved." + - "Are you sure you want to quit?"); - - ResponseType resp = (ResponseType)md.Run(); - md.Destroy(); - return (resp == ResponseType.Ok); - } - return true; - } - - void SetProfileName(string name) - { - profile = new PlayerProfile(name, - System.IO.Path.Combine(appDataPath, name)); - - statusBar.Push(0, name); - } - - bool NewProfile() - { - bool newProfileCreated = false; - bool duplicateName = false; - NewProfileDialog newProfileDialog = new NewProfileDialog(); - ResponseType resp = (ResponseType)newProfileDialog.Run(); - if (resp == ResponseType.Ok) - { - // Make sure profile doesn't already exist. - foreach (string profileName in profileList) - { - if (profileName == newProfileDialog.ProfileName) - { - MessageDialog md = new MessageDialog(this, - DialogFlags.DestroyWithParent, - MessageType.Error, ButtonsType.Ok, - "That profile name already exists."); - resp = (ResponseType)md.Run(); - md.Destroy(); - duplicateName = true; - break; - } - } - - if (!duplicateName) - { - // Set profile name. - SetProfileName(newProfileDialog.ProfileName); - - profile.Initialize(); - - newProfileCreated = true; - } - } - newProfileDialog.Destroy(); - return newProfileCreated; - } - - bool OpenProfile() - { - bool profileSelected = false; - - if (profileList.Count > 0) - { - SelectProfileDialog selectProfileDialog = new SelectProfileDialog(); - selectProfileDialog.ProfileList = profileList; - ResponseType resp = (ResponseType)selectProfileDialog.Run(); - selectProfileDialog.Destroy(); - string selectedProfile = selectProfileDialog.SelectedProfile; - if ((resp == ResponseType.Ok) && (selectedProfile.Length > 0)) - // Selected a profile. - { - SetProfileName(selectedProfile); - profileSelected = true; - } - else if (resp == ResponseType.Accept) // New profile. - { - profileSelected = NewProfile(); - } - } - else - { - FirstRunDialog firstRunDialog = new FirstRunDialog(); - ResponseType resp = (ResponseType)firstRunDialog.Run(); - firstRunDialog.Destroy(); - if (resp == ResponseType.Ok) // New profile - { - profileSelected = NewProfile(); - } - else if (resp == ResponseType.Accept) // Import - { - FileChooserDialog fileDialog = - new FileChooserDialog("Select reactions.txt file.", - this, FileChooserAction.Open, - Gtk.Stock.Cancel, ResponseType.Cancel, - Gtk.Stock.Open, ResponseType.Accept); - resp = (ResponseType)fileDialog.Run(); - if (resp == ResponseType.Accept) - { - string fileName = fileDialog.Filename; - string directory = fileDialog.CurrentFolder; - if (fileName == "reactions.txt") - { - profileSelected = NewProfile(); - if (profileSelected) - { - profile.Import(directory); - } - } - } - } - } - - if (profileSelected) - { - profile.Load(); - PopulateDropDowns(); - } - - return profileSelected; - } - - void PopulateDropDowns() - { - ReagentManager.PopulateReagents(ref ingredient1ComboBox); - ReagentManager.PopulateReagents(ref ingredient2ComboBox); - ReagentManager.PopulateReagents(ref ingredient3ComboBox); - - ingredient2ComboBox.Sensitive = false; - ingredient3ComboBox.Sensitive = false; - - Gtk.TreeIter iter; - ingredient1ComboBox.Model.IterNthChild(out iter, 0); - ingredient1ComboBox.SetActiveIter(iter); - ingredient2ComboBox.Model.IterNthChild(out iter, 0); - ingredient2ComboBox.SetActiveIter(iter); - ingredient3ComboBox.Model.IterNthChild(out iter, 0); - ingredient3ComboBox.SetActiveIter(iter); - } - - protected void SetExpectedColor(byte red, byte green, byte blue) - { - expectedColor.Red = red; - expectedColor.Green = green; - expectedColor.Blue = blue; - unmodifiedSwatch.Color = expectedColor; - } - - protected void SetExpectedColor(PaintColor color) - { - SetExpectedColor(color.Red, color.Green, color.Blue); - } - - protected void UpdateIngredients() - { - Reaction reaction1, reaction2; - TreeIter selectIter; - string reagentName; - reagent1 = null; - reagent2 = null; - reagent3 = null; - - int expRedSum = 0; - int expGreenSum = 0; - int expBlueSum = 0; - - int reactRedSum = 0; - int reactGreenSum = 0; - int reactBlueSum = 0; - - bool reactionKnown = true; - - int pigmentCount = 0; - - saveButton.Sensitive = false; - - if (ingredient1ComboBox.GetActiveIter(out selectIter)) - { - reagentName = (string)ingredient1ComboBox.Model.GetValue(selectIter, 0); - if ((reagentName == null) || (reagentName.Length == 0)) - { - ingredient2ComboBox.Sensitive = false; - ingredient3ComboBox.Sensitive = false; - unmodifiedSwatch.Clear(); - captureButton.Sensitive = false; - } - else - { - reagent1 = ReagentManager.GetReagent(reagentName); - ingredient2ComboBox.Sensitive = true; - if (!reagent1.IsCatalyst) - { - expRedSum = reagent1.Color.Red; - expGreenSum = reagent1.Color.Green; - expBlueSum = reagent1.Color.Blue; - pigmentCount = 1; - } - if (ingredient2ComboBox.GetActiveIter(out selectIter)) - { - reagentName = (string)ingredient2ComboBox.Model.GetValue(selectIter, 0); - if ((reagentName == null) || (reagentName.Length == 0)) - { - ingredient3ComboBox.Sensitive = false; - saveButton.Sensitive = false; - reactionKnown = false; - } - else - { - reagent2 = ReagentManager.GetReagent(reagentName); - ingredient3ComboBox.Sensitive = true; - captureButton.Sensitive = true; - if (!reagent2.IsCatalyst) - { - expRedSum += reagent2.Color.Red; - expGreenSum += reagent2.Color.Green; - expBlueSum += reagent2.Color.Blue; - pigmentCount++; - } - - reaction1 = profile.FindReaction(reagent1, reagent2); - - if (reaction1 != null) - { - ingredient3ComboBox.Sensitive = true; - reactRedSum = reaction1.Red; - reactGreenSum = reaction1.Green; - reactBlueSum = reaction1.Blue;; - } - else - { - reactionKnown = false; - ingredient3ComboBox.Sensitive = false; - } - - if (ingredient3ComboBox.GetActiveIter(out selectIter)) - { - reagentName = (string)ingredient3ComboBox.Model.GetValue(selectIter, 0); - if ((reagentName != null) && (reagentName.Length != 0)) - { - reagent3 = ReagentManager.GetReagent(reagentName); - - if (!reactionKnown) - { - MessageDialog md = new MessageDialog(this, - DialogFlags.DestroyWithParent, - MessageType.Error, ButtonsType.Ok, - "To do a three-ingredient reaction test, " + - "you must first recored the reaction of " + - "the first two ingredients."); - - md.Run(); - md.Destroy(); - captureButton.Sensitive = false; - } - - if (!reagent3.IsCatalyst) - { - expRedSum += reagent3.Color.Red; - expGreenSum += reagent3.Color.Green; - expBlueSum += reagent3.Color.Blue; - pigmentCount++; - } - - reaction1 = profile.FindReaction(reagent1, reagent3); - reaction2 = profile.FindReaction(reagent2, reagent3); - - if (reactionKnown && (reaction1 == null) && (reaction2 == null)) - { - MessageDialog md = new MessageDialog(this, - DialogFlags.DestroyWithParent, - MessageType.Error, ButtonsType.Ok, - "To do a three-ingredient reaction test, " + - "you must first record the reaction of " + - "either the first or second ingredient " + - "with the third ingredient."); - - md.Run(); - md.Destroy(); - captureButton.Sensitive = false; - } - - if (reaction1 != null) - { - reactRedSum += reaction1.Red; - reactGreenSum += reaction1.Green; - reactBlueSum += reaction1.Blue; - } - else - { - reactionKnown = false; - } - - if (reaction2 != null) - { - reactRedSum += reaction2.Red; - reactGreenSum += reaction2.Green; - reactBlueSum += reaction2.Blue; - } - else - { - reactionKnown = false; - } - } - } - } - } - SetExpectedColor((byte)Math.Round((float)expRedSum / (float)pigmentCount), - (byte)Math.Round((float)expGreenSum / (float)pigmentCount), - (byte)Math.Round((float)expBlueSum / (float)pigmentCount)); - - if (reactionKnown) - { - reactedColor.Red = (byte)Math.Min(255, Math.Max(0, expectedColor.Red + reactRedSum)); - reactedColor.Green = (byte)Math.Min(255, Math.Max(0, expectedColor.Green + reactGreenSum)); - reactedColor.Blue = (byte)Math.Min(255, Math.Max(0, expectedColor.Blue + reactBlueSum)); - reactionSwatch.Color = reactedColor; - } - else - { - reactionSwatch.Clear(); - } - } - } - } - - protected void OnDeleteEvent(object sender, DeleteEventArgs a) - { - if (ConfirmedExit()) - { - a.RetVal = true; - Application.Quit(); - } - else - { - a.RetVal = false; - } - } - - bool IsPapyTexture(byte r, byte g, byte b) - { - return ((r > 0xD0) && (g > 0xC8) && (b > 0xA0)) && - ((r < 0xF4) && (g < 0xE0) && (b < 0xC4)); - } - - unsafe bool CaptureReactionColor() - { - // Take a screenshot. - byte r, g, b; - int pixelStart, otherPixelStart; - bool colorMatch; - screenBuffer.GetFromDrawable(rootWindow, - rootWindow.Colormap, 0, 0, 0, 0, screenWidth, screenHeight); - int stride = screenBuffer.Rowstride; - byte* pixBytes = (byte*)screenBuffer.Pixels; - - for (int x = 0; x < screenWidth - colorBarWidth; ++x) - { - for (int y = 0; y < (screenHeight - 53); ++y) - { - // Look for the color swatch. - pixelStart = (y * stride) + (x * 3); - r = pixBytes[pixelStart]; - g = pixBytes[pixelStart + 1]; - b = pixBytes[pixelStart + 2]; - - // 1.) Check if this is a dark pixel. - if ((r < 0x46) && (g < 0x46) && (b < 0x46)) - { - // 2.) Check the pixel above it, - // to see if it's from the papy texture. - otherPixelStart = pixelStart - stride; - if ((otherPixelStart >= 0) && - IsPapyTexture(pixBytes[otherPixelStart++], - pixBytes[otherPixelStart++], - pixBytes[otherPixelStart])) - { - // 3.) Check the pixel below where the swatch should be, - // to see if it's also from the papy texture. - otherPixelStart = pixelStart + (stride * swatchHeight); - if (IsPapyTexture(pixBytes[otherPixelStart++], - pixBytes[otherPixelStart++], - pixBytes[otherPixelStart])) - { - // pixBytes[pixelStart] = 0xFF; - // pixBytes[pixelStart + 1] = 0x00; - // pixBytes[pixelStart + 2] = 0xFF; - - // 4.) Scan the left border of the potential swatch - // location. - colorMatch = true; - for (int i = 1; i < swatchHeight - 2; ++i) - { - otherPixelStart = pixelStart + (stride * i); - if ((Math.Abs(r - pixBytes[otherPixelStart++]) > colorTolerance) || - (Math.Abs(g - pixBytes[otherPixelStart++]) > colorTolerance) || - (Math.Abs(b - pixBytes[otherPixelStart]) > colorTolerance)) - { - colorMatch = false; - break; - } - } - - if (colorMatch) - { - // WE FOUND THE SWATCH! - // Now we know where the color bars are. - otherPixelStart = pixelStart + (redBarSpacing * stride); - int pixelCount = 0; - while ((pixBytes[otherPixelStart] > 0x9F) && - (pixBytes[otherPixelStart + 1] < 0x62) && - (pixBytes[otherPixelStart + 2] < 0x62)) - { - pixelCount++; - // pixBytes[otherPixelStart] = 0x00; - // pixBytes[otherPixelStart + 1] = 0xFF; - // pixBytes[otherPixelStart + 2] = 0xFF; - otherPixelStart += 3; - } - - reactedColor.Red = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth); - otherPixelStart = pixelStart + (greenBarSpacing * stride); - - pixelCount = 0; - while ((pixBytes[otherPixelStart] < 0x62) && - (pixBytes[otherPixelStart + 1] > 0x9F) && - (pixBytes[otherPixelStart + 2] < 0x62)) - { - pixelCount++; - // pixBytes[otherPixelStart] = 0x00; - // pixBytes[otherPixelStart + 1] = 0xFF; - // pixBytes[otherPixelStart + 2] = 0xFF; - otherPixelStart += 3; - } - - reactedColor.Green = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth); - otherPixelStart = pixelStart + (blueBarSpacing * stride); - - pixelCount = 0; - while ((pixBytes[otherPixelStart] < 0x62) && - (pixBytes[otherPixelStart + 1] < 0x62) && - (pixBytes[otherPixelStart + 2] > 0x9F)) - { - pixelCount++; - // pixBytes[otherPixelStart] = 0x00; - // pixBytes[otherPixelStart + 1] = 0xFF; - // pixBytes[otherPixelStart + 2] = 0xFF; - otherPixelStart += 3; - } - - reactedColor.Blue = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth); - - - // screenBuffer.Save("screenshot.png", "png"); - return true; - } - } - } - } - } - } - // screenBuffer.Save("screenshot.png", "png"); - - return false; - - } - - protected virtual void OnCaptureButton(object sender, System.EventArgs e) - { - if (CaptureReactionColor()) - { - string warning = ""; - if (reactedColor.Red == 0) - { - warning = warning + "\nRed is too low."; - } - if (reactedColor.Green == 0) - { - warning = warning + "\nGreen is too low."; - } - if (reactedColor.Blue == 0) - { - warning = warning + "\nBlue is too low."; - } - if (reactedColor.Red == 255) - { - warning = warning + "\nRed is too high."; - } - if (reactedColor.Green == 255) - { - warning = warning + "\nGreen is too high."; - } - if (reactedColor.Blue == 255) - { - warning = warning + "\nBlue is too high."; - } - - if (warning.Length != 0) - { - MessageDialog md = new MessageDialog(this, - DialogFlags.DestroyWithParent, - MessageType.Error, ButtonsType.Ok, - "Reaction clipped. You will need to do a " + - "3-way reaction to test this pair. Details: " + - warning); - - md.Run(); - md.Destroy(); - } - else - { - this.reactionSwatch.Color = reactedColor; - saveButton.Sensitive = true; - } - } - else - { - MessageDialog md = new MessageDialog(this, - DialogFlags.DestroyWithParent, - MessageType.Error, ButtonsType.Ok, - "Pigment Lab dialog box NOT FOUND. Please ensure " + - "that there is an unobstructed view of the dialog " + - "when you press the Capture button."); - - md.Run(); - md.Destroy(); - } - } - - protected virtual void OnSaveButton(object sender, System.EventArgs e) - { - int r, g, b; - if (reagent3 != null) - { - // A 3-reagent reaction. - Reaction reaction1 = profile.FindReaction(reagent1, reagent2); - Reaction reaction2 = profile.FindReaction(reagent1, reagent3); - Reaction reaction3 = profile.FindReaction(reagent2, reagent3); - - r = reactedColor.Red - expectedColor.Red; - g = reactedColor.Green - expectedColor.Green; - b = reactedColor.Blue - expectedColor.Blue; - - if (reaction2 == null) - { - r = r - reaction1.Red - reaction3.Red; - g = g - reaction1.Green - reaction3.Green; - b = b - reaction1.Blue - reaction3.Blue; - profile.SetReaction(reagent1, reagent3, new Reaction(r, g, b)); - profile.Save(); - saveButton.Sensitive = false; - } - else if (reaction3 == null) - { - r = r - reaction1.Red - reaction2.Red; - g = g - reaction1.Green - reaction2.Green; - b = b - reaction1.Blue - reaction2.Blue; - profile.SetReaction(reagent2, reagent3, new Reaction(r, g, b)); - profile.Save(); - saveButton.Sensitive = false; - } - } - else if ((reagent1 != null) && (reagent2 != null)) - { - // A 2-reagent reaction. - r = reactedColor.Red - expectedColor.Red; - g = reactedColor.Green - expectedColor.Green; - b = reactedColor.Blue - expectedColor.Blue; - profile.SetReaction(reagent1, reagent2, new Reaction(r, g, b)); - profile.Save(); - saveButton.Sensitive = false; - } - } - - protected virtual void OnChangedIngredient1(object sender, System.EventArgs e) - { - UpdateIngredients(); - } - - protected virtual void OnChangedIngredient2(object sender, System.EventArgs e) - { - UpdateIngredients(); - } - - protected virtual void OnChangedIngredient3(object sender, System.EventArgs e) - { - UpdateIngredients(); - - } - - protected virtual void OnNewProfile(object sender, System.EventArgs e) - { - if (unsavedData) - { - MessageDialog md = new MessageDialog(this, - DialogFlags.DestroyWithParent, - MessageType.Warning, ButtonsType.OkCancel, - "Your last reaction was unsaved." + - "Are you sure you want to lose your changes?"); - - ResponseType resp = (ResponseType)md.Run(); - md.Destroy(); - if (resp != ResponseType.Ok) - { - return; - } - } - - if (NewProfile()) - { - profile.Load(); - PopulateDropDowns(); - } - } - - protected virtual void OnOpenProfile(object sender, System.EventArgs e) - { - bool profileSelected = false; - SelectProfileDialog selectProfileDialog = new SelectProfileDialog(); - selectProfileDialog.ProfileList = profileList; - ResponseType resp = (ResponseType)selectProfileDialog.Run(); - selectProfileDialog.Destroy(); - if (resp == ResponseType.Ok) // Selected a profile. - { - SetProfileName(selectProfileDialog.SelectedProfile); - profileSelected = true; - } - else if (resp == ResponseType.Accept) // New profile. - { - profileSelected = NewProfile(); - } - if (profileSelected) - { - profile.Load(); - PopulateDropDowns(); - } - } - - protected virtual void OnAbout(object sender, System.EventArgs e) - { - AboutDialog aboutDialog = new AboutDialog(); - aboutDialog.Run(); - aboutDialog.Destroy(); - } - - protected virtual void OnMenuExit (object sender, System.EventArgs e) - { - if (ConfirmedExit()) - { - Application.Quit(); - } - } - - protected virtual void OnExport(object sender, System.EventArgs e) - { - FileChooserDialog fileDialog = - new FileChooserDialog("Select destination file.", - this, FileChooserAction.Save, - Gtk.Stock.Cancel, ResponseType.Cancel, - Gtk.Stock.Save, ResponseType.Accept); - ResponseType resp = (ResponseType)fileDialog.Run(); - if (resp == ResponseType.Accept) - { - string fileName = fileDialog.Filename; - string directory = fileDialog.CurrentFolder; - profile.Export(System.IO.Path.Combine(directory, fileName)); - } - fileDialog.Destroy(); - } - - protected virtual void RunSimulator(object sender, System.EventArgs e) - { - SimulatorWindow win = new SimulatorWindow(profile); - win.Show(); - } - - - - -} - +/* + * Copyright (c) 2010, Tess Snider + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +using System; +using System.IO; +using System.Collections.Generic; +using Gtk; +using DesertPaintLab; + +public partial class MainWindow : Gtk.Window +{ + const int colorTolerance = 2; + + int swatchHeight = 24; + int colorBarWidth = 306; + int redBarSpacing = 32; + int greenBarSpacing = 42; + int blueBarSpacing = 52; + + + bool unsavedData = false; + bool shouldShutDown = false; + string appDataPath; + List profileList = new List(); + PlayerProfile profile = null; + PaintColor expectedColor = new PaintColor(); + PaintColor reactedColor = new PaintColor(); + + int screenWidth = 0; + int screenHeight = 0; + int pixelMultiplier = 1; + + Gdk.Window rootWindow = null; + Gdk.Pixbuf screenBuffer = null; + + Reagent reagent1 = null; + Reagent reagent2 = null; + Reagent reagent3 = null; + + + public bool ShouldShutDown + { + get + { + return shouldShutDown; + } + } + + + public MainWindow () : base(Gtk.WindowType.Toplevel) + { + appDataPath = System.IO.Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "DesertPaintLab"); + + if (!System.IO.Directory.Exists(appDataPath)) + { + System.IO.Directory.CreateDirectory(appDataPath); + } + + DirectoryInfo di = new DirectoryInfo(appDataPath); + DirectoryInfo[] dirs = di.GetDirectories(); + foreach (DirectoryInfo dir in dirs) + { + if (dir.Name != "template") + { + profileList.Add(dir.Name); + } + } + + Palette.Load(System.IO.Path.Combine( + System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), + "colors.txt")); + + Build(); + + if (unmodifiedSwatch != null) + { + unmodifiedSwatch.Clear(); + } + if (reactionSwatch != null) + { + reactionSwatch.Clear(); + } + + // get the root window + rootWindow = Gdk.Global.DefaultRootWindow; + + // get its width and height + rootWindow.GetSize(out screenWidth, out screenHeight); + + ScreenCheckDialog screenCheckDialog = new ScreenCheckDialog(); + screenCheckDialog.ScreenWidth = screenWidth; + screenCheckDialog.ScreenHeight = screenHeight; + screenCheckDialog.GamePixelWidth = pixelMultiplier; + ResponseType resp = (ResponseType)screenCheckDialog.Run(); + screenWidth = screenCheckDialog.ScreenWidth; + screenHeight = screenCheckDialog.ScreenHeight; + pixelMultiplier = screenCheckDialog.GamePixelWidth; + screenCheckDialog.Destroy(); + + screenBuffer = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, screenWidth, screenHeight); + + swatchHeight *= pixelMultiplier; + colorBarWidth *= pixelMultiplier; + redBarSpacing *= pixelMultiplier; + greenBarSpacing *= pixelMultiplier; + blueBarSpacing *= pixelMultiplier; + + if (!OpenProfile()) + { + shouldShutDown = true; + } + } + + bool ConfirmedExit() + { + if (unsavedData) + { + MessageDialog md = new MessageDialog(this, + DialogFlags.DestroyWithParent, + MessageType.Warning, ButtonsType.OkCancel, + "Your last reaction was unsaved." + + "Are you sure you want to quit?"); + + ResponseType resp = (ResponseType)md.Run(); + md.Destroy(); + return (resp == ResponseType.Ok); + } + return true; + } + + void SetProfileName(string name) + { + profile = new PlayerProfile(name, + System.IO.Path.Combine(appDataPath, name)); + + statusBar.Push(0, name); + } + + bool NewProfile() + { + bool newProfileCreated = false; + bool duplicateName = false; + NewProfileDialog newProfileDialog = new NewProfileDialog(); + ResponseType resp = (ResponseType)newProfileDialog.Run(); + if (resp == ResponseType.Ok) + { + // Make sure profile doesn't already exist. + foreach (string profileName in profileList) + { + if (profileName == newProfileDialog.ProfileName) + { + MessageDialog md = new MessageDialog(this, + DialogFlags.DestroyWithParent, + MessageType.Error, ButtonsType.Ok, + "That profile name already exists."); + resp = (ResponseType)md.Run(); + md.Destroy(); + duplicateName = true; + break; + } + } + + if (!duplicateName) + { + // Set profile name. + SetProfileName(newProfileDialog.ProfileName); + + profile.Initialize(); + + newProfileCreated = true; + } + } + newProfileDialog.Destroy(); + return newProfileCreated; + } + + bool OpenProfile() + { + bool profileSelected = false; + + if (profileList.Count > 0) + { + SelectProfileDialog selectProfileDialog = new SelectProfileDialog(); + selectProfileDialog.ProfileList = profileList; + ResponseType resp = (ResponseType)selectProfileDialog.Run(); + selectProfileDialog.Destroy(); + string selectedProfile = selectProfileDialog.SelectedProfile; + if ((resp == ResponseType.Ok) && (selectedProfile.Length > 0)) + // Selected a profile. + { + SetProfileName(selectedProfile); + profileSelected = true; + } + else if (resp == ResponseType.Accept) // New profile. + { + profileSelected = NewProfile(); + } + } + else + { + FirstRunDialog firstRunDialog = new FirstRunDialog(); + ResponseType resp = (ResponseType)firstRunDialog.Run(); + firstRunDialog.Destroy(); + if (resp == ResponseType.Ok) // New profile + { + profileSelected = NewProfile(); + } + else if (resp == ResponseType.Accept) // Import + { + FileChooserDialog fileDialog = + new FileChooserDialog("Select reactions.txt file.", + this, FileChooserAction.Open, + Gtk.Stock.Cancel, ResponseType.Cancel, + Gtk.Stock.Open, ResponseType.Accept); + resp = (ResponseType)fileDialog.Run(); + if (resp == ResponseType.Accept) + { + string fileName = fileDialog.Filename; + string directory = fileDialog.CurrentFolder; + if (fileName == "reactions.txt") + { + profileSelected = NewProfile(); + if (profileSelected) + { + profile.Import(directory); + } + } + } + } + } + + if (profileSelected) + { + profile.Load(); + PopulateDropDowns(); + } + + return profileSelected; + } + + void PopulateDropDowns() + { + ReagentManager.PopulateReagents(ref ingredient1ComboBox); + ReagentManager.PopulateReagents(ref ingredient2ComboBox); + ReagentManager.PopulateReagents(ref ingredient3ComboBox); + + ingredient2ComboBox.Sensitive = false; + ingredient3ComboBox.Sensitive = false; + + Gtk.TreeIter iter; + ingredient1ComboBox.Model.IterNthChild(out iter, 0); + ingredient1ComboBox.SetActiveIter(iter); + ingredient2ComboBox.Model.IterNthChild(out iter, 0); + ingredient2ComboBox.SetActiveIter(iter); + ingredient3ComboBox.Model.IterNthChild(out iter, 0); + ingredient3ComboBox.SetActiveIter(iter); + } + + protected void SetExpectedColor(byte red, byte green, byte blue) + { + expectedColor.Red = red; + expectedColor.Green = green; + expectedColor.Blue = blue; + unmodifiedSwatch.Color = expectedColor; + } + + protected void SetExpectedColor(PaintColor color) + { + SetExpectedColor(color.Red, color.Green, color.Blue); + } + + protected void UpdateIngredients() + { + Reaction reaction1, reaction2; + TreeIter selectIter; + string reagentName; + reagent1 = null; + reagent2 = null; + reagent3 = null; + + int expRedSum = 0; + int expGreenSum = 0; + int expBlueSum = 0; + + int reactRedSum = 0; + int reactGreenSum = 0; + int reactBlueSum = 0; + + bool reactionKnown = true; + + int pigmentCount = 0; + + saveButton.Sensitive = false; + + if (ingredient1ComboBox.GetActiveIter(out selectIter)) + { + reagentName = (string)ingredient1ComboBox.Model.GetValue(selectIter, 0); + if ((reagentName == null) || (reagentName.Length == 0)) + { + ingredient2ComboBox.Sensitive = false; + ingredient3ComboBox.Sensitive = false; + unmodifiedSwatch.Clear(); + captureButton.Sensitive = false; + } + else + { + reagent1 = ReagentManager.GetReagent(reagentName); + ingredient2ComboBox.Sensitive = true; + if (!reagent1.IsCatalyst) + { + expRedSum = reagent1.Color.Red; + expGreenSum = reagent1.Color.Green; + expBlueSum = reagent1.Color.Blue; + pigmentCount = 1; + } + if (ingredient2ComboBox.GetActiveIter(out selectIter)) + { + reagentName = (string)ingredient2ComboBox.Model.GetValue(selectIter, 0); + if ((reagentName == null) || (reagentName.Length == 0)) + { + ingredient3ComboBox.Sensitive = false; + saveButton.Sensitive = false; + reactionKnown = false; + } + else + { + reagent2 = ReagentManager.GetReagent(reagentName); + ingredient3ComboBox.Sensitive = true; + captureButton.Sensitive = true; + if (!reagent2.IsCatalyst) + { + expRedSum += reagent2.Color.Red; + expGreenSum += reagent2.Color.Green; + expBlueSum += reagent2.Color.Blue; + pigmentCount++; + } + + reaction1 = profile.FindReaction(reagent1, reagent2); + + if (reaction1 != null) + { + ingredient3ComboBox.Sensitive = true; + reactRedSum = reaction1.Red; + reactGreenSum = reaction1.Green; + reactBlueSum = reaction1.Blue;; + } + else + { + reactionKnown = false; + ingredient3ComboBox.Sensitive = false; + } + + if (ingredient3ComboBox.GetActiveIter(out selectIter)) + { + reagentName = (string)ingredient3ComboBox.Model.GetValue(selectIter, 0); + if ((reagentName != null) && (reagentName.Length != 0)) + { + reagent3 = ReagentManager.GetReagent(reagentName); + + if (!reactionKnown) + { + MessageDialog md = new MessageDialog(this, + DialogFlags.DestroyWithParent, + MessageType.Error, ButtonsType.Ok, + "To do a three-ingredient reaction test, " + + "you must first recored the reaction of " + + "the first two ingredients."); + + md.Run(); + md.Destroy(); + captureButton.Sensitive = false; + } + + if (!reagent3.IsCatalyst) + { + expRedSum += reagent3.Color.Red; + expGreenSum += reagent3.Color.Green; + expBlueSum += reagent3.Color.Blue; + pigmentCount++; + } + + reaction1 = profile.FindReaction(reagent1, reagent3); + reaction2 = profile.FindReaction(reagent2, reagent3); + + if (reactionKnown && (reaction1 == null) && (reaction2 == null)) + { + MessageDialog md = new MessageDialog(this, + DialogFlags.DestroyWithParent, + MessageType.Error, ButtonsType.Ok, + "To do a three-ingredient reaction test, " + + "you must first record the reaction of " + + "either the first or second ingredient " + + "with the third ingredient."); + + md.Run(); + md.Destroy(); + captureButton.Sensitive = false; + } + + if (reaction1 != null) + { + reactRedSum += reaction1.Red; + reactGreenSum += reaction1.Green; + reactBlueSum += reaction1.Blue; + } + else + { + reactionKnown = false; + } + + if (reaction2 != null) + { + reactRedSum += reaction2.Red; + reactGreenSum += reaction2.Green; + reactBlueSum += reaction2.Blue; + } + else + { + reactionKnown = false; + } + } + } + } + } + SetExpectedColor((byte)Math.Round((float)expRedSum / (float)pigmentCount), + (byte)Math.Round((float)expGreenSum / (float)pigmentCount), + (byte)Math.Round((float)expBlueSum / (float)pigmentCount)); + + if (reactionKnown) + { + reactedColor.Red = (byte)Math.Min(255, Math.Max(0, expectedColor.Red + reactRedSum)); + reactedColor.Green = (byte)Math.Min(255, Math.Max(0, expectedColor.Green + reactGreenSum)); + reactedColor.Blue = (byte)Math.Min(255, Math.Max(0, expectedColor.Blue + reactBlueSum)); + reactionSwatch.Color = reactedColor; + } + else + { + reactionSwatch.Clear(); + } + } + } + } + + protected void OnDeleteEvent(object sender, DeleteEventArgs a) + { + if (ConfirmedExit()) + { + a.RetVal = true; + Application.Quit(); + } + else + { + a.RetVal = false; + } + } + + bool IsPapyTexture(byte r, byte g, byte b) + { + return ((r > 0xD0) && (g > 0xC8) && (b > 0xA0)) && + ((r < 0xF4) && (g < 0xE0) && (b < 0xC4)); + } + + unsafe bool CaptureReactionColor() + { + // Take a screenshot. + byte r, g, b; + int pixelStart, otherPixelStart; + bool colorMatch; + Gdk.Image rootImage = rootWindow.GetImage(0, 0, screenWidth, screenHeight); + screenBuffer.GetFromImage(rootImage, rootImage.Colormap, 0, 0, 0, 0, screenWidth, screenHeight); + //screenBuffer.GetFromDrawable(rootWindow, + // rootWindow.Colormap, 0, 0, 0, 0, screenWidth, screenHeight); + int stride = screenBuffer.Rowstride; + byte* pixBytes = (byte*)screenBuffer.Pixels; + + for (int x = 0; x < screenWidth - colorBarWidth; ++x) + { + for (int y = 0; y < (screenHeight - 53); ++y) + { + // Look for the color swatch. + pixelStart = (y * stride) + (x * 3); + r = pixBytes[pixelStart]; + g = pixBytes[pixelStart + 1]; + b = pixBytes[pixelStart + 2]; + + // 1.) Check if this is a dark pixel. + if ((r < 0x46) && (g < 0x46) && (b < 0x46)) + { + // 2.) Check the pixel above it, + // to see if it's from the papy texture. + otherPixelStart = pixelStart - stride; + if ((otherPixelStart >= 0) && + IsPapyTexture(pixBytes[otherPixelStart++], + pixBytes[otherPixelStart++], + pixBytes[otherPixelStart])) + { + // 3.) Check the pixel below where the swatch should be, + // to see if it's also from the papy texture. + otherPixelStart = pixelStart + (stride * swatchHeight); + if (IsPapyTexture(pixBytes[otherPixelStart++], + pixBytes[otherPixelStart++], + pixBytes[otherPixelStart])) + { + // pixBytes[pixelStart] = 0xFF; + // pixBytes[pixelStart + 1] = 0x00; + // pixBytes[pixelStart + 2] = 0xFF; + + // 4.) Scan the left border of the potential swatch + // location. + colorMatch = true; + for (int i = 1; i < swatchHeight - 2; ++i) + { + otherPixelStart = pixelStart + (stride * i); + if ((Math.Abs(r - pixBytes[otherPixelStart++]) > colorTolerance) || + (Math.Abs(g - pixBytes[otherPixelStart++]) > colorTolerance) || + (Math.Abs(b - pixBytes[otherPixelStart]) > colorTolerance)) + { + colorMatch = false; + break; + } + } + + if (colorMatch) + { + // WE FOUND THE SWATCH! + // Now we know where the color bars are. + otherPixelStart = pixelStart + (redBarSpacing * stride); + int pixelCount = 0; + while ((pixBytes[otherPixelStart] > 0x9F) && + (pixBytes[otherPixelStart + 1] < 0x62) && + (pixBytes[otherPixelStart + 2] < 0x62)) + { + pixelCount++; + // pixBytes[otherPixelStart] = 0x00; + // pixBytes[otherPixelStart + 1] = 0xFF; + // pixBytes[otherPixelStart + 2] = 0xFF; + otherPixelStart += 3; + } + + reactedColor.Red = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth); + otherPixelStart = pixelStart + (greenBarSpacing * stride); + + pixelCount = 0; + while ((pixBytes[otherPixelStart] < 0x62) && + (pixBytes[otherPixelStart + 1] > 0x9F) && + (pixBytes[otherPixelStart + 2] < 0x62)) + { + pixelCount++; + // pixBytes[otherPixelStart] = 0x00; + // pixBytes[otherPixelStart + 1] = 0xFF; + // pixBytes[otherPixelStart + 2] = 0xFF; + otherPixelStart += 3; + } + + reactedColor.Green = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth); + otherPixelStart = pixelStart + (blueBarSpacing * stride); + + pixelCount = 0; + while ((pixBytes[otherPixelStart] < 0x62) && + (pixBytes[otherPixelStart + 1] < 0x62) && + (pixBytes[otherPixelStart + 2] > 0x9F)) + { + pixelCount++; + // pixBytes[otherPixelStart] = 0x00; + // pixBytes[otherPixelStart + 1] = 0xFF; + // pixBytes[otherPixelStart + 2] = 0xFF; + otherPixelStart += 3; + } + + reactedColor.Blue = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth); + + // write out the screenshot + //screenBuffer.Save("screenshot.png", "png"); + return true; + } + } + } + } + } + } + //screenBuffer.Save("screenshot.png", "png"); + + return false; + + } + + protected virtual void OnCaptureButton(object sender, System.EventArgs e) + { + if (CaptureReactionColor()) + { + string warning = ""; + if (reactedColor.Red == 0) + { + warning = warning + "\nRed is too low."; + } + if (reactedColor.Green == 0) + { + warning = warning + "\nGreen is too low."; + } + if (reactedColor.Blue == 0) + { + warning = warning + "\nBlue is too low."; + } + if (reactedColor.Red == 255) + { + warning = warning + "\nRed is too high."; + } + if (reactedColor.Green == 255) + { + warning = warning + "\nGreen is too high."; + } + if (reactedColor.Blue == 255) + { + warning = warning + "\nBlue is too high."; + } + + if (warning.Length != 0) + { + MessageDialog md = new MessageDialog(this, + DialogFlags.DestroyWithParent, + MessageType.Error, ButtonsType.Ok, + "Reaction clipped. You will need to do a " + + "3-way reaction to test this pair. Details: " + + warning); + + md.Run(); + md.Destroy(); + } + else + { + this.reactionSwatch.Color = reactedColor; + saveButton.Sensitive = true; + } + } + else + { + MessageDialog md = new MessageDialog(this, + DialogFlags.DestroyWithParent, + MessageType.Error, ButtonsType.Ok, + "Pigment Lab dialog box NOT FOUND. Please ensure " + + "that there is an unobstructed view of the dialog " + + "when you press the Capture button."); + + md.Run(); + md.Destroy(); + } + } + + protected virtual void OnSaveButton(object sender, System.EventArgs e) + { + int r, g, b; + if (reagent3 != null) + { + // A 3-reagent reaction. + Reaction reaction1 = profile.FindReaction(reagent1, reagent2); + Reaction reaction2 = profile.FindReaction(reagent1, reagent3); + Reaction reaction3 = profile.FindReaction(reagent2, reagent3); + + r = reactedColor.Red - expectedColor.Red; + g = reactedColor.Green - expectedColor.Green; + b = reactedColor.Blue - expectedColor.Blue; + + if (reaction2 == null) + { + r = r - reaction1.Red - reaction3.Red; + g = g - reaction1.Green - reaction3.Green; + b = b - reaction1.Blue - reaction3.Blue; + profile.SetReaction(reagent1, reagent3, new Reaction(r, g, b)); + profile.Save(); + saveButton.Sensitive = false; + } + else if (reaction3 == null) + { + r = r - reaction1.Red - reaction2.Red; + g = g - reaction1.Green - reaction2.Green; + b = b - reaction1.Blue - reaction2.Blue; + profile.SetReaction(reagent2, reagent3, new Reaction(r, g, b)); + profile.Save(); + saveButton.Sensitive = false; + } + } + else if ((reagent1 != null) && (reagent2 != null)) + { + // A 2-reagent reaction. + r = reactedColor.Red - expectedColor.Red; + g = reactedColor.Green - expectedColor.Green; + b = reactedColor.Blue - expectedColor.Blue; + profile.SetReaction(reagent1, reagent2, new Reaction(r, g, b)); + profile.Save(); + saveButton.Sensitive = false; + } + } + + protected virtual void OnChangedIngredient1(object sender, System.EventArgs e) + { + UpdateIngredients(); + } + + protected virtual void OnChangedIngredient2(object sender, System.EventArgs e) + { + UpdateIngredients(); + } + + protected virtual void OnChangedIngredient3(object sender, System.EventArgs e) + { + UpdateIngredients(); + + } + + protected virtual void OnNewProfile(object sender, System.EventArgs e) + { + if (unsavedData) + { + MessageDialog md = new MessageDialog(this, + DialogFlags.DestroyWithParent, + MessageType.Warning, ButtonsType.OkCancel, + "Your last reaction was unsaved." + + "Are you sure you want to lose your changes?"); + + ResponseType resp = (ResponseType)md.Run(); + md.Destroy(); + if (resp != ResponseType.Ok) + { + return; + } + } + + if (NewProfile()) + { + profile.Load(); + PopulateDropDowns(); + } + } + + protected virtual void OnOpenProfile(object sender, System.EventArgs e) + { + bool profileSelected = false; + SelectProfileDialog selectProfileDialog = new SelectProfileDialog(); + selectProfileDialog.ProfileList = profileList; + ResponseType resp = (ResponseType)selectProfileDialog.Run(); + selectProfileDialog.Destroy(); + if (resp == ResponseType.Ok) // Selected a profile. + { + SetProfileName(selectProfileDialog.SelectedProfile); + profileSelected = true; + } + else if (resp == ResponseType.Accept) // New profile. + { + profileSelected = NewProfile(); + } + if (profileSelected) + { + profile.Load(); + PopulateDropDowns(); + } + } + + protected virtual void OnAbout(object sender, System.EventArgs e) + { + AboutDialog aboutDialog = new AboutDialog(); + aboutDialog.Run(); + aboutDialog.Destroy(); + } + + protected virtual void OnMenuExit (object sender, System.EventArgs e) + { + if (ConfirmedExit()) + { + Application.Quit(); + } + } + + protected virtual void OnExport(object sender, System.EventArgs e) + { + FileChooserDialog fileDialog = + new FileChooserDialog("Select destination file.", + this, FileChooserAction.Save, + Gtk.Stock.Cancel, ResponseType.Cancel, + Gtk.Stock.Save, ResponseType.Accept); + ResponseType resp = (ResponseType)fileDialog.Run(); + if (resp == ResponseType.Accept) + { + string fileName = fileDialog.Filename; + string directory = fileDialog.CurrentFolder; + profile.Export(System.IO.Path.Combine(directory, fileName)); + } + fileDialog.Destroy(); + } + + protected virtual void RunSimulator(object sender, System.EventArgs e) + { + SimulatorWindow win = new SimulatorWindow(profile); + win.Show(); + } + + + + +} +