#region ================== Copyright (c) 2007 Pascal vd Heiden /* * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com * This program is released under GNU General Public License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #endregion #region ================== Namespaces using System; using System.Collections.Generic; using System.Windows.Forms; using CodeImp.DoomBuilder.Windows; using CodeImp.DoomBuilder.Actions; using CodeImp.DoomBuilder.Plugins; using CodeImp.DoomBuilder.VisualModes; #endregion namespace CodeImp.DoomBuilder.Editing { public sealed class EditingManager { #region ================== Constants #endregion #region ================== Variables // All editing mode groups, sorted alphabetically private List groups; // All editing modes available private List allmodes; // Editing modes selected through configuration private List usedmodes; // Status private EditMode mode; private EditMode newmode; private Type prevmode; private Type prevstablemode; private Type prevclassicmode; private bool disengaging; // Disposing private bool isdisposed; #endregion #region ================== Properties internal List ModesInfo { get { return allmodes; } } public EditMode Mode { get { return mode; } } public EditMode NewMode { get { return newmode; } } public Type PreviousMode { get { return prevmode; } } public Type PreviousStableMode { get { return prevstablemode; } } public Type PreviousClassicMode { get { return prevclassicmode; } } public bool IsDisposed { get { return isdisposed; } } #endregion #region ================== Constructor / Disposer // Constructor internal EditingManager() { // Initialize allmodes = new List(); usedmodes = new List(); groups = new List(); // Bind any methods General.Actions.BindMethods(this); // Make list of all editing modes we can find foreach(Plugin p in General.Plugins.Plugins) { // For all classes that inherit from EditMode Type[] editclasses = p.FindClasses(typeof(EditMode)); foreach(Type t in editclasses) { // For all defined EditMode attributes EditModeAttribute[] emattrs = (EditModeAttribute[])t.GetCustomAttributes(typeof(EditModeAttribute), false); foreach(EditModeAttribute a in emattrs) { // Make edit mode information EditModeInfo modeinfo = new EditModeInfo(p, t, a); allmodes.Add(modeinfo); // Add group if not added yet if(!groups.Contains(modeinfo.Attributes.ButtonGroup)) groups.Add(modeinfo.Attributes.ButtonGroup); } } } // Sort the lists allmodes.Sort(); groups.Sort(); // Update modes UpdateCurrentEditModes(); // We have no destructor GC.SuppressFinalize(this); } // Disposer internal void Dispose() { // Not already disposed? if(!isdisposed) { // Unbind any methods General.Actions.UnbindMethods(this); // Clean up foreach(EditModeInfo i in allmodes) i.Dispose(); //mxd // Done isdisposed = true; } } #endregion #region ================== Switch Actions // This unbinds all editing mode switch actions private void UnbindSwitchActions() { foreach(EditModeInfo emi in allmodes) { emi.UnbindSwitchAction(); } } // This binds all editing mode switch actions for the available modes only private void BindAvailableSwitchActions() { // In case of VisualMode, we only bind the switch action // of the VisualMode to switch back to the previous mode if(mode is VisualMode) { // Bind only the switch action for this mode EditModeInfo info = GetEditModeInfo(mode.GetType()); info.BindSwitchAction(); } else { // Bind all available mode swtich actions foreach(EditModeInfo emi in usedmodes) { emi.BindSwitchAction(); } } } #endregion #region ================== Methods // This cancels a volatile mode, as if the user presses cancel public bool CancelVolatileMode() { // Volatile mode? if((General.Map != null) & (mode != null) && mode.Attributes.Volatile && !disengaging) { // Cancel disengaging = true; General.Plugins.OnEditCancel(); mode.OnCancel(); return true; } else { // Mode is not volatile return false; } } // This disengages a volatile mode, leaving the choice to cancel or accept to the editing mode public bool DisengageVolatileMode() { // Volatile mode? if((General.Map != null) && (mode != null) && mode.Attributes.Volatile && !disengaging) { // Change back to normal mode disengaging = true; ChangeMode(prevstablemode.Name); return true; } else { // Mode is not volatile return false; } } // This returns specific editing mode info by name internal EditModeInfo GetEditModeInfo(string editmodename) { // Find the edit mode foreach(EditModeInfo emi in usedmodes) { // Mode matches class name? if(emi.Type.Name == editmodename) return emi; } // No such mode found return null; } // This returns specific editing mode info by name internal EditModeInfo GetEditModeInfo(Type modetype) { // Find the edit mode foreach(EditModeInfo emi in usedmodes) { // Mode matches class name? if(emi.Type == modetype) return emi; } // No such mode found return null; } // This is called when the editing modes must update internal void UpdateCurrentEditModes() { // Unbind editing mode switch actions UnbindSwitchActions(); // Rebuild list of used modes usedmodes.Clear(); if(General.Map != null) { foreach(EditModeInfo emi in allmodes) { // Include the mode if it supports current map format (mxd) and map features // Also include the mode when it is listed and enabled or when it's not optional if( (emi.Attributes.SupportedMapFormats == null || Array.IndexOf(emi.Attributes.SupportedMapFormats, General.Map.Config.FormatInterface) != -1) && (emi.Attributes.RequiredMapFeatures == null || General.Map.Config.SupportsMapFeatures(emi.Attributes.RequiredMapFeatures)) && ((General.Map.ConfigSettings.EditModes.ContainsKey(emi.Type.FullName) && General.Map.ConfigSettings.EditModes[emi.Type.FullName] ) || !emi.IsOptional) ) { // Add the mode to be used and bind switch action usedmodes.Add(emi); } } } // Bind switch action for used modes BindAvailableSwitchActions(); // Remove editing mode buttons from interface General.MainWindow.RemoveEditModeButtons(); // Go for all the editing mode groups foreach(string grp in groups) { General.MainWindow.AddEditModeSeperator(grp); // Go for all used edit modes to add buttons foreach(EditModeInfo emi in usedmodes) { if((emi.ButtonImage != null) && (emi.ButtonDesc != null) && (emi.Attributes.ButtonGroup == grp)) General.MainWindow.AddEditModeButton(emi); } } } // // This changes the editing mode. // Order in which events occur for the old and new modes: // // - Constructor of new mode is called // - Disengage of old mode is called // ----- Mode switches ----- // - Engage of new mode is called // - Dispose of old mode is called // // Returns false when cancelled public bool ChangeMode(EditMode nextmode) { EditMode oldmode = mode; if(nextmode != null) { // Verify that this mode is usable bool allowuse = false; foreach(EditModeInfo emi in usedmodes) { if(emi.Type.FullName == nextmode.GetType().FullName) { allowuse = true; break; } } if(!allowuse) { General.Interface.MessageBeep(MessageBeepType.Error); General.WriteLogLine("Attempt to switch to an invalid edit mode " + nextmode.GetType().Name + "!"); return false; } else { General.WriteLogLine("Preparing to change editing mode to " + nextmode.GetType().Name + "..."); } } else { General.WriteLogLine("Stopping editing mode..."); } // Remember previous mode newmode = nextmode; if(mode != null) { prevmode = mode.GetType(); if(!mode.Attributes.Volatile) { prevstablemode = prevmode; if(mode is ClassicMode) prevclassicmode = prevmode; } } else { prevmode = null; prevstablemode = null; prevclassicmode = null; } // Let the plugins know beforehand and check if not cancelled if(General.Plugins.ModeChanges(oldmode, newmode)) { // Disenagage old mode disengaging = true; if(oldmode != null) { General.Plugins.OnEditDisengage(oldmode, newmode); oldmode.OnDisengage(); } // Reset cursor General.Interface.SetCursor(Cursors.Default); // Apply new mode General.WriteLogLine("Editing mode changes from " + TypeNameOrNull(oldmode) + " to " + TypeNameOrNull(nextmode)); General.WriteLogLine("Previous stable mode is " + TypeNameOrNull(prevstablemode) + ", previous classic mode is " + TypeNameOrNull(prevclassicmode)); mode = newmode; disengaging = false; // Engage new mode if(newmode != null) { if (newmode.Attributes.IsDeprecated) General.ErrorLogger.Add(ErrorType.Warning, "The editing mode \"" + newmode.Attributes.DisplayName + "\" is deprecated and will be removed in the future. " + newmode.Attributes.DeprecationMessage); newmode.OnEngage(); General.Plugins.OnEditEngage(oldmode, newmode); } // Bind new switch actions UnbindSwitchActions(); BindAvailableSwitchActions(); // Update the interface General.MainWindow.EditModeChanged(); // Dispose old mode if(oldmode != null) oldmode.Dispose(); // Done switching General.WriteLogLine("Editing mode change complete."); newmode = null; // Redraw the display General.MainWindow.RedrawDisplay(); return true; } else { // Cancelled General.WriteLogLine("Editing mode change cancelled."); return false; } } // This changes mode by class name and optionally with arguments public void ChangeMode(string classname, params object[] args) { EditModeInfo emi = GetEditModeInfo(classname); if(emi != null) emi.SwitchToMode(args); } // This returns the type name as string private static string TypeNameOrNull(Type type) { return (type != null) ? type.Name : "NULL"; } // This returns the type name as string private static string TypeNameOrNull(object obj) { return (obj != null) ? obj.GetType().Name : "NULL"; } #endregion #region ================== Actions /// /// This cancels the current mode. /// [BeginAction("cancelmode")] public void CancelMode() { // Let the mode know if(mode != null) { General.Plugins.OnEditCancel(); mode.OnCancel(); } } /// /// This accepts the changes in the current mode. /// [BeginAction("acceptmode")] public void AcceptMode() { // Let the mode know if(mode != null) { General.Plugins.OnEditAccept(); mode.OnAccept(); } } #endregion } }