- fixed bug introduced with automatic release of all keys when a dialog appears on BeginAction causing an EndAction which shows a dialog to run twice

- implemented Join Sectors and Merge Sectors
- added an ordered selection list to SectorsMode
- removed selection on undo/redo
- Interface AddMenu now completes short action names on menu items Tag property to fully qualified action names depending on calling plugin
This commit is contained in:
codeimp 2008-05-16 20:00:49 +00:00
parent d72ebc4330
commit 0d35ce0536
11 changed files with 340 additions and 104 deletions

View file

@ -31,6 +31,7 @@ using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
using System.Drawing;
using CodeImp.DoomBuilder.Controls;
#endregion
@ -52,6 +53,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Highlighted item
private Sector highlighted;
// The methods GetSelected* and MarkSelected* on the MapSet do not
// retain the order in which items were selected.
// This list keeps in order while sectors are selected/deselected.
private List<Sector> orderedselection;
#endregion
#region ================== Properties
@ -63,6 +69,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Constructor
public SectorsMode()
{
// Make ordered selection list
orderedselection = new List<Sector>();
// Fill the list with selected sectors (these are not in order, but we have no other choice)
ICollection<Sector> selectedsectors = General.Map.Map.GetSelectedSectors(true);
foreach(Sector s in selectedsectors) orderedselection.Add(s);
}
// Disposer
@ -72,7 +84,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(!isdisposed)
{
// Clean up
orderedselection = null;
// Dispose base
base.Dispose();
}
@ -82,19 +95,53 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Methods
// When undo is used
[EndAction("undo", BaseAction = true)]
public void Undo()
{
// Clear ordered selection
orderedselection.Clear();
}
// When redo is used
[EndAction("redo", BaseAction = true)]
public void Redo()
{
// Clear ordered selection
orderedselection.Clear();
}
// This selectes or deselects a sector
protected void SelectSector(Sector s, bool selectstate)
{
// Flip selection
s.Selected = selectstate;
// Make update lines selection
foreach(Sidedef sd in s.Sidedefs)
bool selectionchanged = false;
// Select the sector?
if(selectstate && !s.Selected)
{
bool front, back;
if(sd.Line.Front != null) front = sd.Line.Front.Sector.Selected; else front = false;
if(sd.Line.Back != null) back = sd.Line.Back.Sector.Selected; else back = false;
sd.Line.Selected = front | back;
orderedselection.Add(s);
s.Selected = true;
selectionchanged = true;
}
// Deselect the sector?
else if(!selectstate && s.Selected)
{
orderedselection.Remove(s);
s.Selected = false;
selectionchanged = true;
}
// Selection changed?
if(selectionchanged)
{
// Make update lines selection
foreach(Sidedef sd in s.Sidedefs)
{
bool front, back;
if(sd.Line.Front != null) front = sd.Line.Front.Sector.Selected; else front = false;
if(sd.Line.Back != null) back = sd.Line.Back.Sector.Selected; else back = false;
sd.Line.Selected = front | back;
}
}
}
@ -462,5 +509,84 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
#endregion
#region ================== Actions
// Thi joins sectors together and keeps all lines
[BeginAction("joinsectors")]
public void JoinSectors()
{
// Worth our money?
int count = General.Map.Map.GetSelectedSectors(true).Count;
if(count > 1)
{
// Make undo
General.Map.UndoRedo.CreateUndo("Join " + count + " sectors", UndoGroup.None, 0);
// Merge
JoinMergeSectors(false);
// Deselect
General.Map.Map.ClearSelectedSectors();
General.Map.Map.ClearSelectedLinedefs();
// Redraw display
General.Interface.RedrawDisplay();
}
}
// This joins sectors together and removes the lines in between
[BeginAction("mergesectors")]
public void MergeSectors()
{
// Worth our money?
int count = General.Map.Map.GetSelectedSectors(true).Count;
if(count > 1)
{
// Make undo
General.Map.UndoRedo.CreateUndo("Merge " + count + " sectors", UndoGroup.None, 0);
// Merge
JoinMergeSectors(true);
// Deselect
General.Map.Map.ClearSelectedSectors();
General.Map.Map.ClearSelectedLinedefs();
// Redraw display
General.Interface.RedrawDisplay();
}
}
// Support function for joining and merging sectors
private void JoinMergeSectors(bool removelines)
{
// Remove lines in betwen joining sectors?
if(removelines)
{
// Go for all selected linedefs
ICollection<Linedef> selectedlines = General.Map.Map.GetSelectedLinedefs(true);
foreach(Linedef ld in selectedlines)
{
// Front and back side?
if((ld.Front != null) && (ld.Back != null))
{
// Both a selected sector, but not the same?
if(ld.Front.Sector.Selected && ld.Back.Sector.Selected &&
(ld.Front.Sector != ld.Back.Sector))
{
// Remove this line
ld.Dispose();
}
}
}
}
// Join all selected sectors with the first
for(int i = 1; i < orderedselection.Count; i++)
orderedselection[i].Join(orderedselection[0]);
}
#endregion
}
}

View file

@ -72,7 +72,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
//
this.fliplinedefsitem.Name = "fliplinedefsitem";
this.fliplinedefsitem.Size = new System.Drawing.Size(169, 22);
this.fliplinedefsitem.Tag = "buildermodes_fliplinedefs";
this.fliplinedefsitem.Tag = "fliplinedefs";
this.fliplinedefsitem.Text = "Flip Linedefs";
this.fliplinedefsitem.Click += new System.EventHandler(this.InvokeTaggedAction);
//
@ -80,7 +80,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
//
this.flipsidedefsitem.Name = "flipsidedefsitem";
this.flipsidedefsitem.Size = new System.Drawing.Size(169, 22);
this.flipsidedefsitem.Tag = "buildermodes_flipsidedefs";
this.flipsidedefsitem.Tag = "flipsidedefs";
this.flipsidedefsitem.Text = "Flip Sidedefs";
this.flipsidedefsitem.Click += new System.EventHandler(this.InvokeTaggedAction);
//
@ -93,6 +93,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
//
this.curvelinedefsitem.Name = "curvelinedefsitem";
this.curvelinedefsitem.Size = new System.Drawing.Size(169, 22);
this.curvelinedefsitem.Tag = "curvelinesmode";
this.curvelinedefsitem.Text = "Curve Linedefs...";
//
// sectorsmenu
@ -110,13 +111,17 @@ namespace CodeImp.DoomBuilder.BuilderModes
//
this.joinsectorsitem.Name = "joinsectorsitem";
this.joinsectorsitem.Size = new System.Drawing.Size(154, 22);
this.joinsectorsitem.Tag = "joinsectors";
this.joinsectorsitem.Text = "Join Sectors";
this.joinsectorsitem.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// mergesectorsitem
//
this.mergesectorsitem.Name = "mergesectorsitem";
this.mergesectorsitem.Size = new System.Drawing.Size(154, 22);
this.mergesectorsitem.Tag = "mergesectors";
this.mergesectorsitem.Text = "Merge Sectors";
this.mergesectorsitem.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// toolStripMenuItem2
//

View file

@ -145,3 +145,21 @@ curvelinesmode
allowmouse = true;
allowscroll = true;
}
joinsectors
{
title = "Sectors: Join";
description = "Joins two or more selected sectors together and keeps all linedefs.";
allowkeys = true;
allowmouse = true;
allowscroll = true;
}
mergesectors
{
title = "Sectors: merge";
description = "Joins two or more selected sectors together and removes the shared linedefs.";
allowkeys = true;
allowmouse = true;
allowscroll = true;
}

View file

@ -492,34 +492,37 @@ namespace CodeImp.DoomBuilder.Controls
// This will end active actions for which the pressed keys do not match
private void EndActiveActions()
{
List<Action> keepactions = new List<Action>();
// Go for all active actions
foreach(Action a in activeactions)
bool listchanged;
do
{
// Go for all pressed keys
bool stillactive = false;
foreach(int k in pressedkeys)
// Go for all active actions
listchanged = false;
for(int i = 0; i < activeactions.Count; i++)
{
if((k == (int)Keys.ShiftKey) || (k == (int)Keys.ControlKey))
stillactive |= a.KeyMatches(k);
else
stillactive |= a.KeyMatches(k | modifiers);
}
Action a = activeactions[i];
// Go for all pressed keys
bool stillactive = false;
foreach(int k in pressedkeys)
{
if((k == (int)Keys.ShiftKey) || (k == (int)Keys.ControlKey))
stillactive |= a.KeyMatches(k);
else
stillactive |= a.KeyMatches(k | modifiers);
}
// End the action if no longer matches any of the keys
if(!stillactive)
{
a.End();
}
else
{
keepactions.Add(a);
// End the action if no longer matches any of the keys
if(!stillactive)
{
activeactions.RemoveAt(i);
listchanged = true;
a.End();
break;
}
}
}
// Update list of activate actions
activeactions = keepactions;
while(listchanged);
}
// This returns all action names for a given key

View file

@ -226,6 +226,10 @@ namespace CodeImp.DoomBuilder.Editing
// Reset grouping
lastgroup = UndoGroup.None;
// Remove selection
u.map.ClearAllMarks();
u.map.ClearAllSelected();
// Change map set
General.Map.ChangeMapSet(u.map);
@ -265,6 +269,10 @@ namespace CodeImp.DoomBuilder.Editing
// Reset grouping
lastgroup = UndoGroup.None;
// Remove selection
r.map.ClearAllMarks();
r.map.ClearAllSelected();
// Change map set
General.Map.ChangeMapSet(r.map);

View file

@ -465,6 +465,7 @@ namespace CodeImp.DoomBuilder
// Load plugin manager
General.WriteLogLine("Loading plugins...");
plugins = new PluginManager();
plugins.LoadAllPlugins();
// Load game configurations
General.WriteLogLine("Loading game configurations...");

View file

@ -68,8 +68,29 @@ namespace CodeImp.DoomBuilder.Interface
void BreakExclusiveMouseInput();
void ResumeExclusiveMouseInput();
bool CheckActionActive(Assembly assembly, string actionname);
/// <summary>
/// This adds a menu to the Doom Builder menu strip.
/// <para>
/// NOTE: When the Tag property of menu items is set with a string, this changes the
/// tag to a fully qualified action name by prefixing it with the assembly name.
/// </para>
/// </summary>
/// <param name="menu">The menu to add to Doom Builder.</param>
void AddMenu(ToolStripMenuItem menu);
/// <summary>
/// This removes a menu from the Doom Builder menu strip.
/// </summary>
/// <param name="menu">The menu to remove.</param>
void RemoveMenu(ToolStripMenuItem menu);
/// <summary>
/// This method invokes the action specified on the Tag property of the given menu item.
/// </summary>
/// <param name="sender">Menu item with Tag property set to the name of an action
/// that you want to invoke.</param>
/// <param name="e">Unused.</param>
void InvokeTaggedAction(object sender, EventArgs e);
}
}

View file

@ -939,12 +939,18 @@ namespace CodeImp.DoomBuilder.Interface
// This adds a menu to the menus bar
public void AddMenu(ToolStripMenuItem menu)
{
// Find the plugin that called this method
Plugin plugin = General.Plugins.FindPluginByAssembly(Assembly.GetCallingAssembly());
// Fix tags to full action names
RenameTagsToFullActions(menu.DropDownItems, plugin);
// Insert the menu before the Tools menu
menumain.Items.Insert(menumain.Items.IndexOf(menutools), menu);
ApplyShortcutKeys(menu.DropDownItems);
}
// This removes a menu to the menus bar
// Removes a menu
public void RemoveMenu(ToolStripMenuItem menu)
{
menumain.Items.Remove(menu);
@ -960,9 +966,6 @@ namespace CodeImp.DoomBuilder.Interface
// This sets the shortcut keys on menu items
private void ApplyShortcutKeys(ToolStripItemCollection items)
{
ToolStripMenuItem menuitem;
string actionname;
// Go for all controls to find menu items
foreach(ToolStripItem item in items)
{
@ -970,13 +973,13 @@ namespace CodeImp.DoomBuilder.Interface
if(item is ToolStripMenuItem)
{
// Get the item in proper type
menuitem = (item as ToolStripMenuItem);
ToolStripMenuItem menuitem = (item as ToolStripMenuItem);
// Tag set for this item?
if(menuitem.Tag != null)
if((menuitem.Tag != null) && (menuitem.Tag is string))
{
// Get the action name
actionname = menuitem.Tag.ToString();
string actionname = menuitem.Tag.ToString();
// Action with this name available?
if(General.Actions.Exists(actionname))
@ -992,6 +995,39 @@ namespace CodeImp.DoomBuilder.Interface
}
}
// This fixes short action names to fully qualified
// action names on menu item tags
private void RenameTagsToFullActions(ToolStripItemCollection items, Plugin plugin)
{
// Go for all controls to find menu items
foreach(ToolStripItem item in items)
{
// This is a menu item?
if(item is ToolStripMenuItem)
{
// Get the item in proper type
ToolStripMenuItem menuitem = (item as ToolStripMenuItem);
// Tag set for this item?
if((menuitem.Tag != null) && (menuitem.Tag is string))
{
// Get the action name
string actionname = menuitem.Tag.ToString();
// Check if the tag doe not already begin with the assembly name
if(!(menuitem.Tag as string).StartsWith(plugin.Name + "_", StringComparison.InvariantCultureIgnoreCase))
{
// Change the tag to a fully qualified action name
menuitem.Tag = plugin.Name.ToLowerInvariant() + "_" + (menuitem.Tag as string);
}
}
// Recursively perform operation on child menu items
RenameTagsToFullActions(menuitem.DropDownItems, plugin);
}
}
}
#endregion
#region ================== File Menu

View file

@ -237,7 +237,21 @@ namespace CodeImp.DoomBuilder.Map
}
#endregion
#region ================== Methods
// This joins the sector with another sector
// This sector will be disposed
public void Join(Sector other)
{
// Change secter reference on my sidedefs
// This automatically disposes this sector
while(sidedefs != null)
sidedefs.First.Value.ChangeSector(other);
}
#endregion
#region ================== Changes
// This updates all properties

View file

@ -94,12 +94,6 @@ namespace CodeImp.DoomBuilder.Plugins
throw new InvalidProgramException();
}
// Load actions
General.Actions.LoadActions(asm);
// Plugin is now initialized
plug.OnInitialize();
// We have no destructor
GC.SuppressFinalize(this);
}

View file

@ -60,64 +60,9 @@ namespace CodeImp.DoomBuilder.Plugins
// Constructor
public PluginManager()
{
string[] filenames;
Type[] editclasses;
EditModeAttribute[] emattrs;
EditModeInfo editmodeinfo;
Plugin p;
// Make lists
this.plugins = new List<Plugin>();
this.editmodes = new List<EditModeInfo>();
// Find all .dll files
filenames = Directory.GetFiles(General.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly);
foreach(string fn in filenames)
{
// Load plugin from this file
General.MainWindow.DisplayStatus("Loading plugin '" + Path.GetFileName(fn) + "'...");
try
{
p = new Plugin(fn);
}
catch(InvalidProgramException)
{
General.WriteLogLine("WARNING: Plugin file '" + Path.GetFileName(fn) + "' was not loaded.");
p = null;
}
// Continue if no errors
if((p != null) && (!p.IsDisposed))
{
// Add to plugins
this.plugins.Add(p);
// For all classes that inherit from EditMode
editclasses = p.FindClasses(typeof(EditMode));
foreach(Type t in editclasses)
{
// For all defined EditMode attributes
emattrs = (EditModeAttribute[])t.GetCustomAttributes(typeof(EditModeAttribute), true);
foreach(EditModeAttribute a in emattrs)
{
// Make edit mode information
editmodeinfo = new EditModeInfo(p, t, a);
editmodes.Add(editmodeinfo);
}
}
}
}
// Sort the list in order for buttons
editmodes.Sort();
// Go for all edit modes to add buttons
foreach(EditModeInfo emi in editmodes)
{
// Add all non-config-specific buttons to interface
if((emi.ButtonImage != null) && (emi.ButtonDesc != null) && !emi.ConfigSpecific)
General.MainWindow.AddEditModeButton(emi);
}
// We have no destructor
GC.SuppressFinalize(this);
@ -141,6 +86,71 @@ namespace CodeImp.DoomBuilder.Plugins
#region ================== Methods
// This loads all plugins
public void LoadAllPlugins()
{
string[] filenames;
Type[] editclasses;
EditModeAttribute[] emattrs;
EditModeInfo editmodeinfo;
Plugin p;
// Find all .dll files
filenames = Directory.GetFiles(General.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly);
foreach(string fn in filenames)
{
// Load plugin from this file
General.MainWindow.DisplayStatus("Loading plugin '" + Path.GetFileName(fn) + "'...");
try
{
p = new Plugin(fn);
}
catch(InvalidProgramException)
{
General.WriteLogLine("WARNING: Plugin file '" + Path.GetFileName(fn) + "' was not loaded.");
p = null;
}
// Continue if no errors
if((p != null) && (!p.IsDisposed))
{
// Add to plugins
this.plugins.Add(p);
// Load actions
General.Actions.LoadActions(p.Assembly);
// For all classes that inherit from EditMode
editclasses = p.FindClasses(typeof(EditMode));
foreach(Type t in editclasses)
{
// For all defined EditMode attributes
emattrs = (EditModeAttribute[])t.GetCustomAttributes(typeof(EditModeAttribute), true);
foreach(EditModeAttribute a in emattrs)
{
// Make edit mode information
editmodeinfo = new EditModeInfo(p, t, a);
editmodes.Add(editmodeinfo);
}
}
// Plugin is now initialized
p.Plug.OnInitialize();
}
}
// Sort the list in order for buttons
editmodes.Sort();
// Go for all edit modes to add buttons
foreach(EditModeInfo emi in editmodes)
{
// Add all non-config-specific buttons to interface
if((emi.ButtonImage != null) && (emi.ButtonDesc != null) && !emi.ConfigSpecific)
General.MainWindow.AddEditModeButton(emi);
}
}
// This returns specific editing mode info by name
public EditModeInfo GetEditModeInfo(string editmodename)
{