From 718a8ff37c36bbb76edb8a9bee017e1856757bd6 Mon Sep 17 00:00:00 2001 From: Phylogeny Date: Thu, 5 Jan 2017 11:11:28 -0800 Subject: [PATCH] Create Key Binding API * Added annotation to the API that allows certain items to bypass KeyConflictContext activity checks, fixed an ItemStack null check, replaced a class check with a method call, and clarified a description. * Added method to the API the returns C&B key bindings. * 1) Added optional argument to the KeyBindingContext annotation that allows classes that use it to be added to ModConflictContext's active class collection; 2) Added an IMC that searches the class hierarchies of all classes of all of the sender's registered items for instances of the annotation that have this argument; 3) Refactored the active class collection to a hashset to avoid duplication. * Optimized IMC annotation search. * Remove nested type enum from ModKeyBinding and replaced the switch on its type with a switch on all its values not in ClientSide. --- .../chiselsandbits/api/IChiselAndBitsAPI.java | 15 ++++ .../chiselsandbits/api/IgnoreBlockLogic.java | 2 +- .../chiselsandbits/api/KeyBindingContext.java | 81 +++++++++++++++++++ .../mod/chiselsandbits/api/ModKeyBinding.java | 43 ++++++++++ .../client/ModConflictContext.java | 78 +++++++++++++++++- .../mod/chiselsandbits/core/ClientSide.java | 23 ++++++ .../core/api/ChiselAndBitsAPI.java | 60 ++++++++++++++ .../chiselsandbits/core/api/IMCHandler.java | 8 ++ .../core/api/IMCHandlerIgnoreLogic.java | 2 +- .../core/api/IMCHandlerKeyBinding.java | 65 +++++++++++++++ .../api/IMCHandlerKeyBindingAnnotations.java | 75 +++++++++++++++++ 11 files changed, 447 insertions(+), 5 deletions(-) create mode 100644 src/main/java/mod/chiselsandbits/api/KeyBindingContext.java create mode 100644 src/main/java/mod/chiselsandbits/api/ModKeyBinding.java create mode 100644 src/main/java/mod/chiselsandbits/core/api/IMCHandlerKeyBinding.java create mode 100644 src/main/java/mod/chiselsandbits/core/api/IMCHandlerKeyBindingAnnotations.java diff --git a/src/main/java/mod/chiselsandbits/api/IChiselAndBitsAPI.java b/src/main/java/mod/chiselsandbits/api/IChiselAndBitsAPI.java index f4c1586a..cf79982a 100644 --- a/src/main/java/mod/chiselsandbits/api/IChiselAndBitsAPI.java +++ b/src/main/java/mod/chiselsandbits/api/IChiselAndBitsAPI.java @@ -6,12 +6,15 @@ import mod.chiselsandbits.api.APIExceptions.InvalidBitItem; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.items.IItemHandler; /** @@ -199,4 +202,16 @@ void addEquivilantMaterial( Material target ); // this should be a material C&B understands, // other wise you'll get stone anyway. + /** + * Get a C&B key binding. + * + * @param modKeyBinding + * the {@link ModKeyBinding} value that denotes the C&B key + * binding to return. + * @return a C&B {@link KeyBinding}. + */ + @SideOnly( Side.CLIENT ) + KeyBinding getKeyBinding( + ModKeyBinding modKeyBinding ); + } diff --git a/src/main/java/mod/chiselsandbits/api/IgnoreBlockLogic.java b/src/main/java/mod/chiselsandbits/api/IgnoreBlockLogic.java index e89f3ef9..86a04aed 100644 --- a/src/main/java/mod/chiselsandbits/api/IgnoreBlockLogic.java +++ b/src/main/java/mod/chiselsandbits/api/IgnoreBlockLogic.java @@ -16,7 +16,7 @@ * Put this on the block, or use the IMC, * * FMLInterModComms.sendMessage( "chiselsandbits", "ignoreblocklogic", - * "myBlockName" ); + * [myBlockName] ); * * If you wish to make a single state compatible, or incompatible you must use * "forcestatecompatibility" instead, if your entire block is intended to be diff --git a/src/main/java/mod/chiselsandbits/api/KeyBindingContext.java b/src/main/java/mod/chiselsandbits/api/KeyBindingContext.java new file mode 100644 index 00000000..2f5d7604 --- /dev/null +++ b/src/main/java/mod/chiselsandbits/api/KeyBindingContext.java @@ -0,0 +1,81 @@ +package mod.chiselsandbits.api; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The key bindings for the following operations are only active when holding C&B items: + * + * - "chisel" - Setting the chisel mode + * - "postivepattern" - Setting the positive pattern mode + * - "tapemeasure" - Setting the tape measure mode + * - "rotateable" - Rotating blocks + * - "menuitem" - Opening the radial menu + * + * If you put this annotation on a class that extends Item, you can allow C&B to bypass + * the normal activity checks when holding an item that is an instance of that class. If + * you include and set true the optional applyToSubClasses argument in the annotation or + * use an IMC, this will apply not only to any item that is of that class, but also to + * any class that extends that class. + * + * + * If use any annotation with applyToSubClasses set to true, you need to send the + * following IMC (of any type - String type is used below) after you register your items + * to find/register/initialize those classes: + * + * FMLInterModComms.sendMessage( "chiselsandbits", "initkeybindingannotations", "" ); + * + * Doing so will not only find and initialize classes of registered items, but also any + * base classes that registered items may extend, but are never directly instantiated. + * + * + * + * ~Example 1~ + * Putting the following annotation on an item class will allow the key bindings for + * chisel modes to be active when holding an item of that class: + * + * @KeyBindingContext( chisel ) + * + * The following two IMCs would do the same for item(s) of that class or of any subclass: + * + * FMLInterModComms.sendMessage( "chiselsandbits", "chisel", [myItemName] ); + * + * + * + * ~Example 2~ + * The following IMC and IMC/annotation set will both allow the key binding for chisel + * modes to be active when holding an item of that class or of any subclass: + * + * @KeyBindingContext( value = { "chisel", "menuitem" }, applyToSubClasses = true ) + * AND + * FMLInterModComms.sendMessage( "chiselsandbits", "initkeybindingannotations", "" ); + * + * OR + * + * FMLInterModComms.sendMessage( "chiselsandbits", "chisel", [myItemName] ); + * FMLInterModComms.sendMessage( "chiselsandbits", "menuitem", [myItemName] ); + * + * + */ +@Retention( RetentionPolicy.RUNTIME ) +public @interface KeyBindingContext +{ + /** + * A list of contexts that will allow all key bindings that use them to be active + * when holding an item of a class (or a subclass, if applyToSubClasses is true) + * with this annotation. + * + * @return a list of key bindings contexts + */ + String[] value (); + + /** + * If true, the key binding context activity check will be bypassed not only for items + * of a class with this annotation, but also to items of any class that extends it. + * + * This argument is optional. + * + * @return whether or not this annotation applies to subclasses + */ + boolean applyToSubClasses() default false; +} diff --git a/src/main/java/mod/chiselsandbits/api/ModKeyBinding.java b/src/main/java/mod/chiselsandbits/api/ModKeyBinding.java new file mode 100644 index 00000000..57c48e54 --- /dev/null +++ b/src/main/java/mod/chiselsandbits/api/ModKeyBinding.java @@ -0,0 +1,43 @@ +package mod.chiselsandbits.api; + +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +@SideOnly( Side.CLIENT ) +public enum ModKeyBinding +{ + //Misc + ROTATE_CCW, + ROTATE_CW, + UNDO, + REDO, + MODE_MENU, + ADD_TO_CLIPBOARD, + PICK_BIT, + + //Chisel Modes + SINGLE, + SNAP2, + SNAP4, + SNAP8, + LINE, + PLANE, + CONNECTED_PLANE, + CUBE_SMALL, + CUBE_MEDIUM, + CUBE_LARGE, + SAME_MATERIAL, + DRAWN_REGION, + CONNECTED_MATERIAL, + + //Positive Pattern Modes + REPLACE, + ADDITIVE, + PLACEMENT, + IMPOSE, + + //Tape Measure Modes + BIT, + BLOCK, + DISTANCE; +} diff --git a/src/main/java/mod/chiselsandbits/client/ModConflictContext.java b/src/main/java/mod/chiselsandbits/client/ModConflictContext.java index 4c139588..931f9b21 100644 --- a/src/main/java/mod/chiselsandbits/client/ModConflictContext.java +++ b/src/main/java/mod/chiselsandbits/client/ModConflictContext.java @@ -1,8 +1,15 @@ package mod.chiselsandbits.client; +import java.lang.annotation.Annotation; +import java.util.HashSet; +import java.util.Set; + +import mod.chiselsandbits.api.KeyBindingContext; import mod.chiselsandbits.core.ClientSide; import mod.chiselsandbits.helpers.ChiselToolType; +import mod.chiselsandbits.helpers.ModUtil; import mod.chiselsandbits.interfaces.IVoxelBlobItem; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumHand; import net.minecraftforge.client.settings.IKeyConflictContext; @@ -16,8 +23,13 @@ public enum ModConflictContext implements IKeyConflictContext @Override public boolean isActive() { + if ( super.isActive() ) + { + return true; + } + final ItemStack held = ClientSide.instance.getPlayer().getHeldItemMainhand(); - return held != null && held.getItem() instanceof IVoxelBlobItem; + return !ModUtil.isEmpty( held ) && held.getItem() instanceof IVoxelBlobItem; } @Override @@ -33,6 +45,11 @@ public boolean conflicts( @Override public boolean isActive() { + if ( super.isActive() ) + { + return true; + } + final ChiselToolType tool = ClientSide.instance.getHeldToolType( EnumHand.MAIN_HAND ); return tool != null && tool.hasMenu(); } @@ -49,7 +66,7 @@ public boolean conflicts( @Override public boolean isActive() { - return ClientSide.instance.getHeldToolType( EnumHand.MAIN_HAND ) == ChiselToolType.TAPEMEASURE; + return super.isActive() || ClientSide.instance.getHeldToolType( EnumHand.MAIN_HAND ) == ChiselToolType.TAPEMEASURE; } @Override @@ -65,7 +82,7 @@ public boolean conflicts( @Override public boolean isActive() { - return ClientSide.instance.getHeldToolType( EnumHand.MAIN_HAND ) == ChiselToolType.POSITIVEPATTERN; + return super.isActive() || ClientSide.instance.getHeldToolType( EnumHand.MAIN_HAND ) == ChiselToolType.POSITIVEPATTERN; } @Override @@ -81,6 +98,11 @@ public boolean conflicts( @Override public boolean isActive() { + if ( super.isActive() ) + { + return true; + } + final ChiselToolType tool = ClientSide.instance.getHeldToolType( EnumHand.MAIN_HAND ); return tool != null && tool.isBitOrChisel(); } @@ -93,4 +115,54 @@ public boolean conflicts( } }; + private Set> activeItemClasses = new HashSet>(); + + public void setItemActive( + final Item item ) + { + activeItemClasses.add( item.getClass() ); + } + + @Override + public boolean isActive() + { + final ItemStack held = ClientSide.instance.getPlayer().getHeldItemMainhand(); + + if ( ModUtil.isEmpty( held ) ) + { + return false; + } + + for ( Class itemClass : activeItemClasses ) + { + if ( itemClass.isInstance( held.getItem() ) ) + { + return true; + } + } + + if ( held.getItem().getClass().isAnnotationPresent( KeyBindingContext.class ) ) + { + final Annotation annotation = held.getItem().getClass().getAnnotation( KeyBindingContext.class ); + + if ( annotation instanceof KeyBindingContext ) + { + for ( String name : ( (KeyBindingContext) annotation ).value() ) + { + if ( name.equals( getName() ) ) + { + return true; + } + } + } + } + + return false; + } + + public String getName() + { + return toString().toLowerCase().replace( "holding_", "" ); + } + } diff --git a/src/main/java/mod/chiselsandbits/core/ClientSide.java b/src/main/java/mod/chiselsandbits/core/ClientSide.java index 51159151..6e7434fe 100644 --- a/src/main/java/mod/chiselsandbits/core/ClientSide.java +++ b/src/main/java/mod/chiselsandbits/core/ClientSide.java @@ -18,6 +18,7 @@ import mod.chiselsandbits.api.IBitAccess; import mod.chiselsandbits.api.IBitBrush; import mod.chiselsandbits.api.ItemType; +import mod.chiselsandbits.api.ModKeyBinding; import mod.chiselsandbits.bittank.BlockBitTank; import mod.chiselsandbits.bittank.TileEntityBitTank; import mod.chiselsandbits.bittank.TileEntitySpecialRenderBitTank; @@ -141,6 +142,28 @@ public class ClientSide final public TapeMeasures tapeMeasures = new TapeMeasures(); + public KeyBinding getKeyBinding( + ModKeyBinding modKeyBinding ) + { + switch ( modKeyBinding ) + { + case ROTATE_CCW: + return rotateCCW; + case ROTATE_CW: + return rotateCW; + case UNDO: + return undo; + case REDO: + return redo; + case ADD_TO_CLIPBOARD: + return addToClipboard; + case PICK_BIT: + return pickBit; + default: + return modeMenu; + } + } + public void preinit( final ChiselsAndBits mod ) { diff --git a/src/main/java/mod/chiselsandbits/core/api/ChiselAndBitsAPI.java b/src/main/java/mod/chiselsandbits/core/api/ChiselAndBitsAPI.java index 49cb3a70..fcb0ab9a 100644 --- a/src/main/java/mod/chiselsandbits/core/api/ChiselAndBitsAPI.java +++ b/src/main/java/mod/chiselsandbits/core/api/ChiselAndBitsAPI.java @@ -8,6 +8,7 @@ import mod.chiselsandbits.api.IBitLocation; import mod.chiselsandbits.api.IChiselAndBitsAPI; import mod.chiselsandbits.api.ItemType; +import mod.chiselsandbits.api.ModKeyBinding; import mod.chiselsandbits.chiseledblock.BlockBitInfo; import mod.chiselsandbits.chiseledblock.ItemBlockChiseled; import mod.chiselsandbits.chiseledblock.TileEntityBlockChiseled; @@ -15,6 +16,7 @@ import mod.chiselsandbits.chiseledblock.data.VoxelBlob; import mod.chiselsandbits.client.UndoTracker; import mod.chiselsandbits.core.ChiselsAndBits; +import mod.chiselsandbits.core.ClientSide; import mod.chiselsandbits.helpers.BitOperation; import mod.chiselsandbits.helpers.DeprecationHelper; import mod.chiselsandbits.helpers.ModUtil; @@ -26,8 +28,12 @@ import mod.chiselsandbits.items.ItemNegativePrint; import mod.chiselsandbits.items.ItemPositivePrint; import mod.chiselsandbits.items.ItemWrench; +import mod.chiselsandbits.modes.ChiselMode; +import mod.chiselsandbits.modes.PositivePatternMode; +import mod.chiselsandbits.modes.TapeMeasureModes; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; @@ -38,6 +44,8 @@ import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.items.CapabilityItemHandler; public class ChiselAndBitsAPI implements IChiselAndBitsAPI @@ -322,4 +330,56 @@ public void endUndoGroup( UndoTracker.getInstance().endGroup( player ); } + @Override + @SideOnly( Side.CLIENT ) + public KeyBinding getKeyBinding( + ModKeyBinding modKeyBinding ) + { + switch ( modKeyBinding ) + { + case SINGLE: + return (KeyBinding) ChiselMode.SINGLE.binding; + case SNAP2: + return (KeyBinding) ChiselMode.SNAP2.binding; + case SNAP4: + return (KeyBinding) ChiselMode.SNAP4.binding; + case SNAP8: + return (KeyBinding) ChiselMode.SNAP8.binding; + case LINE: + return (KeyBinding) ChiselMode.LINE.binding; + case PLANE: + return (KeyBinding) ChiselMode.PLANE.binding; + case CONNECTED_PLANE: + return (KeyBinding) ChiselMode.CONNECTED_PLANE.binding; + case CUBE_SMALL: + return (KeyBinding) ChiselMode.CUBE_SMALL.binding; + case CUBE_MEDIUM: + return (KeyBinding) ChiselMode.CUBE_MEDIUM.binding; + case CUBE_LARGE: + return (KeyBinding) ChiselMode.CUBE_LARGE.binding; + case SAME_MATERIAL: + return (KeyBinding) ChiselMode.SAME_MATERIAL.binding; + case DRAWN_REGION: + return (KeyBinding) ChiselMode.DRAWN_REGION.binding; + case CONNECTED_MATERIAL: + return (KeyBinding) ChiselMode.CONNECTED_MATERIAL.binding; + case REPLACE: + return (KeyBinding) PositivePatternMode.REPLACE.binding; + case ADDITIVE: + return (KeyBinding) PositivePatternMode.ADDITIVE.binding; + case PLACEMENT: + return (KeyBinding) PositivePatternMode.PLACEMENT.binding; + case IMPOSE: + return (KeyBinding) PositivePatternMode.IMPOSE.binding; + case BIT: + return (KeyBinding) TapeMeasureModes.BIT.binding; + case BLOCK: + return (KeyBinding) TapeMeasureModes.BLOCK.binding; + case DISTANCE: + return (KeyBinding) TapeMeasureModes.DISTANCE.binding; + default: + return ClientSide.instance.getKeyBinding( modKeyBinding ); + } + } + } diff --git a/src/main/java/mod/chiselsandbits/core/api/IMCHandler.java b/src/main/java/mod/chiselsandbits/core/api/IMCHandler.java index 508eabd9..9d176e25 100644 --- a/src/main/java/mod/chiselsandbits/core/api/IMCHandler.java +++ b/src/main/java/mod/chiselsandbits/core/api/IMCHandler.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.Map; +import mod.chiselsandbits.client.ModConflictContext; import mod.chiselsandbits.core.Log; import net.minecraftforge.fml.common.event.FMLInterModComms; import net.minecraftforge.fml.common.event.FMLInterModComms.IMCEvent; @@ -18,6 +19,13 @@ public IMCHandler() processors.put( "forcestatecompatibility", new IMCHandlerForceState() ); processors.put( "ignoreblocklogic", new IMCHandlerIgnoreLogic() ); processors.put( "materialequivilancy", new IMCHandlerMaterialEquivilancy() ); + + for ( ModConflictContext conflictContext : ModConflictContext.values() ) + { + processors.put( conflictContext.getName(), new IMCHandlerKeyBinding() ); + } + + processors.put( "initkeybindingannotations", new IMCHandlerKeyBindingAnnotations() ); } public void handleIMCEvent( diff --git a/src/main/java/mod/chiselsandbits/core/api/IMCHandlerIgnoreLogic.java b/src/main/java/mod/chiselsandbits/core/api/IMCHandlerIgnoreLogic.java index f0873885..9803d1cb 100644 --- a/src/main/java/mod/chiselsandbits/core/api/IMCHandlerIgnoreLogic.java +++ b/src/main/java/mod/chiselsandbits/core/api/IMCHandlerIgnoreLogic.java @@ -33,7 +33,7 @@ public void excuteIMC( blk = Block.REGISTRY.getObject( new ResourceLocation( message.getSender(), name ) ); } } - else if ( message.getMessageType() == ResourceLocation.class ) + else if ( message.isResourceLocationMessage() ) { errorName = message.getResourceLocationValue().toString(); blk = Block.REGISTRY.getObject( message.getResourceLocationValue() ); diff --git a/src/main/java/mod/chiselsandbits/core/api/IMCHandlerKeyBinding.java b/src/main/java/mod/chiselsandbits/core/api/IMCHandlerKeyBinding.java new file mode 100644 index 00000000..5f6185ea --- /dev/null +++ b/src/main/java/mod/chiselsandbits/core/api/IMCHandlerKeyBinding.java @@ -0,0 +1,65 @@ +package mod.chiselsandbits.core.api; + +import mod.chiselsandbits.client.ModConflictContext; +import mod.chiselsandbits.core.Log; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.event.FMLInterModComms.IMCMessage; + +public class IMCHandlerKeyBinding implements IMCMessageHandler +{ + + @Override + public void excuteIMC( + final IMCMessage message ) + { + try + { + String errorName = "UNKNOWN"; + Item item = null; + + if ( message.isStringMessage() ) + { + final String name = message.getStringValue(); + + errorName = name; + item = Item.REGISTRY.getObject( new ResourceLocation( name ) ); + + // try finding the item in the mod instead... + if ( item == null || item == Items.field_190931_a ) + { + errorName = message.getSender() + ":" + name; + item = Item.REGISTRY.getObject( new ResourceLocation( message.getSender(), name ) ); + } + } + else if ( message.isResourceLocationMessage() ) + { + errorName = message.getResourceLocationValue().toString(); + item = Item.REGISTRY.getObject( message.getResourceLocationValue() ); + } + else + { + Log.info( "Invalid Type for IMC: " + message.getMessageType().getName() ); + return; + } + + if ( item == null || item == Items.field_190931_a ) + { + throw new RuntimeException( "Unable to locate item " + errorName ); + } + + for ( ModConflictContext conflictContext : ModConflictContext.values() ) + { + if ( conflictContext.getName().equals( message.key ) ) + { + conflictContext.setItemActive( item ); + } + } + } + catch ( final Throwable e ) + { + Log.logError( "IMC registeritemwithkeybinding From " + message.getSender(), e ); + } + } +} diff --git a/src/main/java/mod/chiselsandbits/core/api/IMCHandlerKeyBindingAnnotations.java b/src/main/java/mod/chiselsandbits/core/api/IMCHandlerKeyBindingAnnotations.java new file mode 100644 index 00000000..51d73dc9 --- /dev/null +++ b/src/main/java/mod/chiselsandbits/core/api/IMCHandlerKeyBindingAnnotations.java @@ -0,0 +1,75 @@ +package mod.chiselsandbits.core.api; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.List; + +import mod.chiselsandbits.api.KeyBindingContext; +import mod.chiselsandbits.client.ModConflictContext; +import mod.chiselsandbits.core.Log; +import net.minecraft.item.Item; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.event.FMLInterModComms.IMCMessage; + +public class IMCHandlerKeyBindingAnnotations implements IMCMessageHandler +{ + + @Override + public void excuteIMC( + final IMCMessage message ) + { + try + { + boolean found = false; + Class itemClass; + Annotation annotation; + List conflictContextNames; + ResourceLocation regName; + + for ( Item item : Item.REGISTRY ) + { + regName = item.getRegistryName(); + + if ( regName == null || !regName.getResourceDomain().equals( message.getSender() ) ) + { + continue; + } + + itemClass = item.getClass(); + + while ( itemClass != Item.class ) + { + if ( itemClass.isAnnotationPresent( KeyBindingContext.class ) ) + { + annotation = itemClass.getAnnotation( KeyBindingContext.class ); + + if ( annotation instanceof KeyBindingContext ) + { + conflictContextNames = Arrays.asList( ( (KeyBindingContext) annotation ).value() ); + + for ( ModConflictContext conflictContext : ModConflictContext.values() ) + { + if ( conflictContextNames.contains( conflictContext.getName() ) ) + { + conflictContext.setItemActive( item ); + found = true; + } + } + } + } + + itemClass = itemClass.getSuperclass(); + } + } + + if ( !found ) + { + throw new RuntimeException( "No item classes were found with a KeyBindingContext annotation that applies to sub-classes. Add one with 'applyToSubClasses = true' to do so." ); + } + } + catch ( final Throwable e ) + { + Log.logError( "IMC initkeybindingannotations From " + message.getSender(), e ); + } + } +}