Skip to content

Commit a54b96f

Browse files
committed
Create OpViewer
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.
1 parent c10e702 commit a54b96f

File tree

3 files changed

+278
-0
lines changed

3 files changed

+278
-0
lines changed

pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
<groupId>net.imagej</groupId>
4848
<artifactId>imagej-deprecated</artifactId>
4949
</dependency>
50+
<dependency>
51+
<groupId>net.imagej</groupId>
52+
<artifactId>imagej-ops</artifactId>
53+
</dependency>
5054
<dependency>
5155
<groupId>net.imagej</groupId>
5256
<artifactId>imagej-updater</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2014 - 2015 Board of Regents of the University of
6+
* Wisconsin-Madison, University of Konstanz and Brian Northan.
7+
* %%
8+
* Redistribution and use in source and binary forms, with or without
9+
* modification, are permitted provided that the following conditions are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright notice,
12+
* this list of conditions and the following disclaimer.
13+
* 2. Redistributions in binary form must reproduce the above copyright notice,
14+
* this list of conditions and the following disclaimer in the documentation
15+
* and/or other materials provided with the distribution.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
* POSSIBILITY OF SUCH DAMAGE.
28+
* #L%
29+
*/
30+
31+
package net.imagej.ui.swing.ops;
32+
33+
import org.scijava.command.Command;
34+
import org.scijava.command.ContextCommand;
35+
import org.scijava.menu.MenuConstants;
36+
import org.scijava.plugin.Menu;
37+
import org.scijava.plugin.Plugin;
38+
39+
/**
40+
* Simple command to create and show a new {@link OpViewer}.
41+
*
42+
* @author Mark Hiner <[email protected]>
43+
*/
44+
@Plugin(type = Command.class, menu = { @Menu(
45+
label = MenuConstants.PLUGINS_LABEL, weight = MenuConstants.PLUGINS_WEIGHT,
46+
mnemonic = MenuConstants.PLUGINS_MNEMONIC), @Menu(label = "Browse Ops...",
47+
weight = 22) }, headless = false)
48+
public class BrowseOps extends ContextCommand {
49+
50+
@Override
51+
public void run() {
52+
new OpViewer(getContext()).setVisible(true);
53+
}
54+
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2014 - 2015 Board of Regents of the University of
6+
* Wisconsin-Madison, University of Konstanz and Brian Northan.
7+
* %%
8+
* Redistribution and use in source and binary forms, with or without
9+
* modification, are permitted provided that the following conditions are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright notice,
12+
* this list of conditions and the following disclaimer.
13+
* 2. Redistributions in binary form must reproduce the above copyright notice,
14+
* this list of conditions and the following disclaimer in the documentation
15+
* and/or other materials provided with the distribution.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
* POSSIBILITY OF SUCH DAMAGE.
28+
* #L%
29+
*/
30+
31+
package net.imagej.ui.swing.ops;
32+
33+
import java.awt.Dimension;
34+
import java.util.HashMap;
35+
import java.util.Map;
36+
37+
import javax.swing.JFrame;
38+
import javax.swing.JScrollPane;
39+
import javax.swing.JTree;
40+
import javax.swing.ScrollPaneConstants;
41+
import javax.swing.SwingUtilities;
42+
import javax.swing.WindowConstants;
43+
import javax.swing.tree.DefaultMutableTreeNode;
44+
import javax.swing.tree.TreeSelectionModel;
45+
46+
import org.scijava.Context;
47+
import org.scijava.plugin.Parameter;
48+
import org.scijava.prefs.PrefService;
49+
50+
import net.imagej.ops.Namespace;
51+
import net.imagej.ops.Op;
52+
import net.imagej.ops.OpInfo;
53+
import net.imagej.ops.OpService;
54+
import net.imagej.ops.OpUtils;
55+
56+
/**
57+
* A scrollable tree view of all discovered {@link Op} implementations. The goal
58+
* of this class is to make it easy to discover the available {@code Ops}, their
59+
* associated {@link Namespace}, and specific signatures available for these
60+
* ops.
61+
* <p>
62+
* {@code Ops} are sorted with {@code Namespaces} as the top level, then
63+
* {@code Op} name, finally with {@code Op} signatures as the leaves.
64+
* </p>
65+
*
66+
* @author Mark Hiner <[email protected]>
67+
*/
68+
@SuppressWarnings("serial")
69+
public class OpViewer extends JFrame {
70+
71+
public static final int DEFAULT_WINDOW_WIDTH = 500;
72+
public static final int DEFAULT_WINDOW_HEIGHT = 700;
73+
public static final String WINDOW_HEIGHT = "op.viewer.height";
74+
public static final String WINDOW_WIDTH = "op.viewer.width";
75+
public static final String NO_NAMESPACE = "default namespace";
76+
77+
@Parameter
78+
private OpService opService;
79+
80+
@Parameter
81+
private PrefService prefService;
82+
83+
public OpViewer(final Context context) {
84+
super("Op Viewer");
85+
context.inject(this);
86+
87+
// Load the frame size
88+
loadPreferences();
89+
90+
// Top node of the JTree
91+
final DefaultMutableTreeNode top = new DefaultMutableTreeNode(
92+
"Available Ops");
93+
createNodes(top);
94+
95+
final JTree tree = new JTree(top);
96+
tree.getSelectionModel().setSelectionMode(
97+
TreeSelectionModel.SINGLE_TREE_SELECTION);
98+
99+
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
100+
101+
// Make the JTree scrollable
102+
final JScrollPane pane = new JScrollPane(tree,
103+
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
104+
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
105+
106+
setContentPane(pane);
107+
108+
try {
109+
if (SwingUtilities.isEventDispatchThread()) {
110+
pack();
111+
}
112+
else {
113+
SwingUtilities.invokeAndWait(new Runnable() {
114+
115+
@Override
116+
public void run() {
117+
pack();
118+
}
119+
});
120+
}
121+
}
122+
catch (final Exception ie) {
123+
/* ignore */
124+
}
125+
126+
setLocationRelativeTo(null); // center on screen
127+
requestFocus();
128+
}
129+
130+
/**
131+
* Load any preferences saved via the {@link PrefService}, such as window
132+
* width and height.
133+
*/
134+
public void loadPreferences() {
135+
final Dimension dim = getSize();
136+
137+
// If a dimension is 0 then use the default dimension size
138+
if (0 == dim.width) {
139+
dim.width = DEFAULT_WINDOW_WIDTH;
140+
}
141+
if (0 == dim.height) {
142+
dim.height = DEFAULT_WINDOW_HEIGHT;
143+
}
144+
145+
setPreferredSize(new Dimension(prefService.getInt(WINDOW_WIDTH, dim.width),
146+
prefService.getInt(WINDOW_HEIGHT, dim.height)));
147+
}
148+
149+
/**
150+
* Helper method to populate the {@link Op} nodes. Ops without a valid name
151+
* will be skipped. Ops with no namespace will be put in a
152+
* {@link #NO_NAMESPACE} category.
153+
*/
154+
private void createNodes(final DefaultMutableTreeNode top) {
155+
// Map namespaces and ops to their parent tree node
156+
final Map<String, DefaultMutableTreeNode> namespaces =
157+
new HashMap<String, DefaultMutableTreeNode>();
158+
159+
final Map<String, DefaultMutableTreeNode> ops =
160+
new HashMap<String, DefaultMutableTreeNode>();
161+
162+
// Iterate over all ops
163+
for (final OpInfo info : opService.infos()) {
164+
final String namespace = getName(info.getNamespace(), NO_NAMESPACE);
165+
166+
// Get the namespace node for this Op
167+
final DefaultMutableTreeNode nsCategory = getCategory(top, namespaces,
168+
namespace);
169+
170+
final String opName = getName(info.getSimpleName(), info.getName());
171+
172+
if (!opName.isEmpty()) {
173+
// get the general Op node for this Op
174+
final DefaultMutableTreeNode opCategory = getCategory(nsCategory, ops,
175+
opName);
176+
177+
// Create a leaf node for this particular Op's signature
178+
final DefaultMutableTreeNode opSignature = new DefaultMutableTreeNode(
179+
OpUtils.opString(info.cInfo()));
180+
181+
opCategory.add(opSignature);
182+
}
183+
}
184+
}
185+
186+
/**
187+
* Helper method to get a properly formatted name. {@code name} is tried
188+
* first, then {@code backupName} if needed (i.e. {@code name} is {@code null}
189+
* or empty).
190+
* <p>
191+
* The resulting string is trimmed and set to lowercase.
192+
* </p>
193+
*/
194+
private String getName(String name, final String backupName) {
195+
if (name == null || name.isEmpty()) name = backupName;
196+
197+
return name == null ? "" : name.toLowerCase().trim();
198+
}
199+
200+
/**
201+
* Helper method to retrieved a map category with the specified name. If the
202+
* category does not exist yet, it's created, added to the map, and added as a
203+
* child to the parent tree node.
204+
*/
205+
private DefaultMutableTreeNode getCategory(
206+
final DefaultMutableTreeNode parent,
207+
final Map<String, DefaultMutableTreeNode> categoryMap,
208+
final String categoryName)
209+
{
210+
DefaultMutableTreeNode nsCategory = categoryMap.get(categoryName);
211+
if (nsCategory == null) {
212+
nsCategory = new DefaultMutableTreeNode(categoryName);
213+
parent.add(nsCategory);
214+
categoryMap.put(categoryName, nsCategory);
215+
}
216+
217+
return nsCategory;
218+
}
219+
}

0 commit comments

Comments
 (0)