Skip to content

Commit

Permalink
Create OpViewer
Browse files Browse the repository at this point in the history
Adds an OpViewer, a tree-based view of all available ops. This list is
sorted by namespace and op type, with each op signature as leaves in the
tree.

The OpViewer is exposed via a companion BrowseOps command.

The goal of this addition is to provide users an easy method to find out
what ops are available, while creatign possibilities for future
functionality - e.g. via selection listeners to do code insertion, or
open javadoc.
  • Loading branch information
hinerm committed Nov 16, 2015
1 parent c10e702 commit a54b96f
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
<groupId>net.imagej</groupId>
<artifactId>imagej-deprecated</artifactId>
</dependency>
<dependency>
<groupId>net.imagej</groupId>
<artifactId>imagej-ops</artifactId>
</dependency>
<dependency>
<groupId>net.imagej</groupId>
<artifactId>imagej-updater</artifactId>
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/net/imagej/ui/swing/ops/BrowseOps.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* #%L
* ImageJ software for multidimensional image processing and analysis.
* %%
* Copyright (C) 2014 - 2015 Board of Regents of the University of
* Wisconsin-Madison, University of Konstanz and Brian Northan.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/

package net.imagej.ui.swing.ops;

import org.scijava.command.Command;
import org.scijava.command.ContextCommand;
import org.scijava.menu.MenuConstants;
import org.scijava.plugin.Menu;
import org.scijava.plugin.Plugin;

/**
* Simple command to create and show a new {@link OpViewer}.
*
* @author Mark Hiner <[email protected]>
*/
@Plugin(type = Command.class, menu = { @Menu(
label = MenuConstants.PLUGINS_LABEL, weight = MenuConstants.PLUGINS_WEIGHT,
mnemonic = MenuConstants.PLUGINS_MNEMONIC), @Menu(label = "Browse Ops...",
weight = 22) }, headless = false)
public class BrowseOps extends ContextCommand {

@Override
public void run() {
new OpViewer(getContext()).setVisible(true);
}

}
219 changes: 219 additions & 0 deletions src/main/java/net/imagej/ui/swing/ops/OpViewer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
* #%L
* ImageJ software for multidimensional image processing and analysis.
* %%
* Copyright (C) 2014 - 2015 Board of Regents of the University of
* Wisconsin-Madison, University of Konstanz and Brian Northan.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/

package net.imagej.ui.swing.ops;

import java.awt.Dimension;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeSelectionModel;

import org.scijava.Context;
import org.scijava.plugin.Parameter;
import org.scijava.prefs.PrefService;

import net.imagej.ops.Namespace;
import net.imagej.ops.Op;
import net.imagej.ops.OpInfo;
import net.imagej.ops.OpService;
import net.imagej.ops.OpUtils;

/**
* A scrollable tree view of all discovered {@link Op} implementations. The goal
* of this class is to make it easy to discover the available {@code Ops}, their
* associated {@link Namespace}, and specific signatures available for these
* ops.
* <p>
* {@code Ops} are sorted with {@code Namespaces} as the top level, then
* {@code Op} name, finally with {@code Op} signatures as the leaves.
* </p>
*
* @author Mark Hiner <[email protected]>
*/
@SuppressWarnings("serial")
public class OpViewer extends JFrame {

public static final int DEFAULT_WINDOW_WIDTH = 500;
public static final int DEFAULT_WINDOW_HEIGHT = 700;
public static final String WINDOW_HEIGHT = "op.viewer.height";
public static final String WINDOW_WIDTH = "op.viewer.width";
public static final String NO_NAMESPACE = "default namespace";

@Parameter
private OpService opService;

@Parameter
private PrefService prefService;

public OpViewer(final Context context) {
super("Op Viewer");
context.inject(this);

// Load the frame size
loadPreferences();

// Top node of the JTree
final DefaultMutableTreeNode top = new DefaultMutableTreeNode(
"Available Ops");
createNodes(top);

final JTree tree = new JTree(top);
tree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);

setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

// Make the JTree scrollable
final JScrollPane pane = new JScrollPane(tree,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

setContentPane(pane);

try {
if (SwingUtilities.isEventDispatchThread()) {
pack();
}
else {
SwingUtilities.invokeAndWait(new Runnable() {

@Override
public void run() {
pack();
}
});
}
}
catch (final Exception ie) {
/* ignore */
}

setLocationRelativeTo(null); // center on screen
requestFocus();
}

/**
* Load any preferences saved via the {@link PrefService}, such as window
* width and height.
*/
public void loadPreferences() {
final Dimension dim = getSize();

// If a dimension is 0 then use the default dimension size
if (0 == dim.width) {
dim.width = DEFAULT_WINDOW_WIDTH;
}
if (0 == dim.height) {
dim.height = DEFAULT_WINDOW_HEIGHT;
}

setPreferredSize(new Dimension(prefService.getInt(WINDOW_WIDTH, dim.width),
prefService.getInt(WINDOW_HEIGHT, dim.height)));
}

/**
* Helper method to populate the {@link Op} nodes. Ops without a valid name
* will be skipped. Ops with no namespace will be put in a
* {@link #NO_NAMESPACE} category.
*/
private void createNodes(final DefaultMutableTreeNode top) {
// Map namespaces and ops to their parent tree node
final Map<String, DefaultMutableTreeNode> namespaces =
new HashMap<String, DefaultMutableTreeNode>();

final Map<String, DefaultMutableTreeNode> ops =
new HashMap<String, DefaultMutableTreeNode>();

// Iterate over all ops
for (final OpInfo info : opService.infos()) {
final String namespace = getName(info.getNamespace(), NO_NAMESPACE);

// Get the namespace node for this Op
final DefaultMutableTreeNode nsCategory = getCategory(top, namespaces,
namespace);

final String opName = getName(info.getSimpleName(), info.getName());

if (!opName.isEmpty()) {
// get the general Op node for this Op
final DefaultMutableTreeNode opCategory = getCategory(nsCategory, ops,
opName);

// Create a leaf node for this particular Op's signature
final DefaultMutableTreeNode opSignature = new DefaultMutableTreeNode(
OpUtils.opString(info.cInfo()));

opCategory.add(opSignature);
}
}
}

/**
* Helper method to get a properly formatted name. {@code name} is tried
* first, then {@code backupName} if needed (i.e. {@code name} is {@code null}
* or empty).
* <p>
* The resulting string is trimmed and set to lowercase.
* </p>
*/
private String getName(String name, final String backupName) {
if (name == null || name.isEmpty()) name = backupName;

return name == null ? "" : name.toLowerCase().trim();
}

/**
* Helper method to retrieved a map category with the specified name. If the
* category does not exist yet, it's created, added to the map, and added as a
* child to the parent tree node.
*/
private DefaultMutableTreeNode getCategory(
final DefaultMutableTreeNode parent,
final Map<String, DefaultMutableTreeNode> categoryMap,
final String categoryName)
{
DefaultMutableTreeNode nsCategory = categoryMap.get(categoryName);
if (nsCategory == null) {
nsCategory = new DefaultMutableTreeNode(categoryName);
parent.add(nsCategory);
categoryMap.put(categoryName, nsCategory);
}

return nsCategory;
}
}

0 comments on commit a54b96f

Please sign in to comment.