-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Launch custom commands directly from Menu Item or Dropdown #59 - Draft
Example: A custom action purely configured in layer.xml creates an context sensitive action/shortkey and toolbar icon
- Loading branch information
Showing
4 changed files
with
232 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
QuickOpener/src/main/java/me/dsnet/quickopener/actions/layer/ActionRegistrationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package me.dsnet.quickopener.actions.layer; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import javax.swing.Action; | ||
import org.openide.filesystems.FileObject; | ||
import org.openide.filesystems.FileUtil; | ||
|
||
/** | ||
* | ||
* Taken from http://wiki.netbeans.org/DevFaqActionsAddAtRuntime | ||
*/ | ||
public class ActionRegistrationService { | ||
|
||
/** | ||
* Registers an action with the platform along with optional shortcuts and | ||
* menu items. | ||
* | ||
* @param name Display name of the action. | ||
* @param category Category in the Keymap tool. | ||
* @param shortcut Default shortcut, use an empty string or null for none. | ||
* @param menuPath Menu location starting with "Menu", like "Menu/File" | ||
* @param action an action object to attach to the action entry. | ||
* @throws IOException | ||
*/ | ||
public static void registerAction(String name, String category, String shortcut, String menuPath, Action action) throws IOException { | ||
/////////////////////// | ||
// Add/Update Action // | ||
/////////////////////// | ||
String originalFile = "Actions/" + category + "/" + name + ".instance"; | ||
FileObject in = getFolderAt("Actions/" + category); | ||
FileObject obj = in.getFileObject(name, "instance"); | ||
if (obj == null) { | ||
obj = in.createData(name, "instance"); | ||
} | ||
action.putValue(Action.NAME, name); | ||
obj.setAttribute("instanceCreate", action); | ||
obj.setAttribute("instanceClass", action.getClass().getName()); | ||
|
||
///////////////////// | ||
// Add/Update Menu // | ||
///////////////////// | ||
in = getFolderAt(menuPath); | ||
obj = in.getFileObject(name, "shadow"); | ||
// Create if missing. | ||
if (obj == null) { | ||
obj = in.createData(name, "shadow"); | ||
obj.setAttribute("originalFile", originalFile); | ||
} | ||
|
||
///////////////////////// | ||
// Add/Update Shortcut // | ||
///////////////////////// | ||
in = getFolderAt("Shortcuts"); | ||
obj = in.getFileObject(shortcut, "shadow"); | ||
if (obj == null) { | ||
obj = in.createData(shortcut, "shadow"); | ||
obj.setAttribute("originalFile", originalFile); | ||
} | ||
} | ||
|
||
private static FileObject getFolderAt(String inputPath) throws IOException { | ||
final String[] split = inputPath.split("/"); | ||
if (null == split || split.length == 0) { | ||
return null; | ||
} | ||
List<String> parts = Arrays.asList(split); | ||
FileObject existing = FileUtil.getConfigFile(inputPath); | ||
if (existing != null) { | ||
return existing; | ||
} | ||
|
||
FileObject base = FileUtil.getConfigFile(parts.get(0)); | ||
if (base == null) { | ||
return null; | ||
} | ||
|
||
for (int i = 1; i < parts.size(); i++) { | ||
String path = join("/", parts.subList(0, i + 1)); | ||
FileObject next = FileUtil.getConfigFile(path); | ||
if (next == null) { | ||
next = base.createFolder(parts.get(i)); | ||
} | ||
base = next; | ||
} | ||
|
||
return FileUtil.getConfigFile(inputPath); | ||
} | ||
|
||
private static String join(String separator, List<String> list) { | ||
StringBuilder sb = new StringBuilder(); | ||
final int size = list.size(); | ||
for (int i = 0; i < size; i++) { | ||
String text = list.get(i); | ||
sb.append(text); | ||
if (i != (size - 1)) { | ||
sb.append(separator); | ||
} | ||
} | ||
return sb.toString(); | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
...rc/main/java/me/dsnet/quickopener/actions/layer/LayerXMLConfiguredCustomRunnerAction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package me.dsnet.quickopener.actions.layer; | ||
|
||
import java.awt.event.ActionEvent; | ||
import java.util.Map; | ||
import javax.swing.AbstractAction; | ||
import javax.swing.Action; | ||
import me.dsnet.quickopener.actions.RunCommand; | ||
import org.openide.awt.DynamicMenuContent; | ||
import org.openide.util.Lookup; | ||
import org.openide.util.LookupEvent; | ||
import org.openide.util.LookupListener; | ||
import org.openide.util.Utilities; | ||
|
||
/** | ||
* Action for running custom commands. The configuration is purely based on | ||
* custom attribute tags of the action registration in the layer.xml file. | ||
* | ||
* <pre> | ||
* <file name="action1.instance"> | ||
* <attr methodvalue="me.dsnet.quickopener.actions.layer.SuperclassSensitiveAction.create" name="instanceCreate"/> | ||
* <attr name="imagePath" stringvalue="me/dsnet/quickopener/icons/run.png"/> | ||
* <attr name="displayName" stringvalue="Notepad"/> | ||
* <attr name="custom-command" stringvalue="notepad ${file}"/> | ||
* </file> | ||
* </pre> | ||
* | ||
* <p> | ||
* Based on | ||
* https://blogs.oracle.com/geertjan/entry/enabling_an_action_on_object, | ||
* https://blogs.oracle.com/geertjan/entry/superclass_sensitive_actions and | ||
* http://wiki.netbeans.org/DevFaqActionsAddAtRuntime | ||
* </p> | ||
* | ||
* @author markiewb | ||
*/ | ||
public final class LayerXMLConfiguredCustomRunnerAction extends AbstractAction { | ||
|
||
private final Lookup.Result<Object> lookupResult; | ||
private final RunCommand runCommand; | ||
|
||
/** | ||
* Referenced from layer.xml like this | ||
* | ||
* @param map | ||
* @return | ||
*/ | ||
static Action create(Map map) { | ||
final String command = (String) map.get("custom-command"); | ||
final String displayName = (String) map.get("displayName"); | ||
final String iconBase = (String) map.get("imagePath"); | ||
return new LayerXMLConfiguredCustomRunnerAction(iconBase, displayName, command); | ||
} | ||
|
||
/** | ||
* Only to be instanciated by the layer.xml configured based on the | ||
* "instanceCreate"-value. | ||
* | ||
* @param iconBase | ||
* @param displayName | ||
* @param command | ||
*/ | ||
private LayerXMLConfiguredCustomRunnerAction(String iconBase, String displayName, final String command) { | ||
super(displayName); | ||
Lookup context = Utilities.actionsGlobalContext(); | ||
runCommand = new RunCommand(command); | ||
//enable action, if all placeholders are replaced | ||
setEnabled(runCommand.areAllPlaceHoldersReplaced()); | ||
//always show | ||
putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, false); | ||
putValue("iconBase", iconBase); | ||
|
||
// make this action context aware using custom code | ||
lookupResult = context.lookupResult(Object.class); | ||
lookupResult.addLookupListener(new LookupListener() { | ||
@Override | ||
public void resultChanged(LookupEvent le) { | ||
//enable action, if all placeholders are replaced | ||
setEnabled(runCommand.areAllPlaceHoldersReplaced()); | ||
} | ||
}); | ||
|
||
} | ||
|
||
@Override | ||
public void actionPerformed(ActionEvent ev) { | ||
runCommand.actionPerformed(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters