Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly sort all menu entries (alphabetically by default) #179

Open
imagejan opened this issue Jan 9, 2018 · 3 comments
Open

Correctly sort all menu entries (alphabetically by default) #179

imagejan opened this issue Jan 9, 2018 · 3 comments

Comments

@imagejan
Copy link
Member

imagejan commented Jan 9, 2018

Currently, legacy-compatible IJ2 commands end up in a menu group separate from IJ1 plugins, e.g. the Debug, Sandbox, AutoRun, Kymograph and Scripting submenus of the Plugins menu are in a separate section.

Let's merge them all and sort them alphabetically.

See also PreibischLab/BigStitcher#7 (comment).

The adding and sorting is supposed to happen in IJ1Helper#addMenuItems() method:

/**
* Adds legacy-compatible scripts and commands to the ImageJ1 menu structure.
*/
public synchronized void addMenuItems() {
if (menuInitialized) return;
final Map<String, ModuleInfo> modules = //
legacyService.getScriptsAndNonLegacyCommands();
final Hashtable<String, String> ij1Commands = getCommands();
final ImageJ ij1 = hasInstance() ? IJ.getInstance() : null;
final IJ1MenuWrapper wrapper = ij1 == null ? null : new IJ1MenuWrapper(ij1);
class Item implements Comparable<Item> {
private double weight;
private MenuPath path;
private String name, identifier;
private ModuleInfo info;
@Override
public int compareTo(final Item o) {
if (weight != o.weight) return Double.compare(weight, o.weight);
return compare(path, o.path);
}
public int compare(final MenuPath a, final MenuPath b) {
int i = 0;
while (i < a.size() && i < b.size()) {
final MenuEntry a2 = a.get(i), b2 = b.get(i);
int diff = Double.compare(a.get(i).getWeight(), b.get(i).getWeight());
if (diff != 0) return diff;
diff = a2.getName().compareTo(b2.getName());
if (diff != 0) return diff;
i++;
}
return 0;
}
}
final List<Item> items = new ArrayList<>();
for (final Entry<String, ModuleInfo> entry : modules.entrySet()) {
final String key = entry.getKey();
final ModuleInfo info = entry.getValue();
final MenuEntry leaf = info.getMenuPath().getLeaf();
if (leaf == null) continue;
final MenuPath path = info.getMenuPath();
final String name = leaf.getName();
final Item item = new Item();
item.weight = leaf.getWeight();
item.path = path;
item.name = name;
item.identifier = key;
item.info = info;
items.add(item);
}
// sort by menu weight, then alphabetically
Collections.sort(items);
for (final Item item : items) {
if (ij1Commands.containsKey(item.name)) {
log.info("Overriding " + item.name + //
"; identifier: " + item.identifier + //
"; jar: " + ClassUtils.getLocation(item.info.getDelegateClassName()));
if (wrapper != null) try {
wrapper.create(item.path, true);
}
catch (final Throwable t) {
log.error(t);
}
}
else if (wrapper != null) try {
wrapper.create(item.path, false);
}
catch (final Throwable t) {
log.error(t);
}
ij1Commands.put(item.name, item.identifier);
}
menuInitialized = true;
}

@imagejan
Copy link
Member Author

I created a small test project (https://github.com/imagejan/test-menu-path), containing:

  • an IJ1 plugin,
  • an IJ2 command, and
  • an IJ2 script

in the same submenu Plugins > ASubmenu.

When placing the project's jar file (Test_Menu_Path-0.1.0-SNAPSHOT.jar) in ./Fiji.app/plugins/, all three menu entries appear in ASubmenu in the upper part of the Plugins menu.

However, when the jar file is placed in ./Fiji.app/jars/ (where the IJ1 plugin isn't recognized), the ASubmenu entry ends up being in the lower part of Plugins, below the separator.

@imagejan
Copy link
Member Author

Menu entries are sorted alphabetically if and only if their menu weight is the same.

/**
* Helper method to look up special cases for menu weighting
*/
private int getIndex(final Menu menu, final String label) {
// Place export sub-menu after import sub-menu
if (menu.getLabel().equals("File") && label.equals("Export")) {
for (int i = 0; i < menu.getItemCount(); i++) {
final MenuItem menuItem = menu.getItem(i);
if (menuItem.getLabel().equals("Import")) return i + 1;
}
}
// TODO pass and use actual command weight from IJ2.. maybe?
// No special case: append to end of menu
return menu.getItemCount();
}

So in order to unify menus of Legacy and IJ2 commands, we need to either:

  • Specify a weight for each IJ2 command (and possibly make clear in the javadoc that new commands will always be added at the end of the menu unless a weight is specified
  • Remove the specified weight from legacy commands (at least in the lowest-level menus) such that they're always sorted alphabetically, possibly disturbing the (current) menu order of some of them.
  • Auto-generate weights for IJ2 commands at the same time as legacy commands (?)

(Maybe I'm missing something here.)

@imagejan
Copy link
Member Author

For the record: here's a script I used to display the menu weights for each menu entry:

#@ MenuService menus

def printMenu(menu, prefix) {
	entry = menu.getMenuEntry()
	if(entry!=null) println "${prefix}[${entry.getWeight()}] ${entry.getName()}"
	if (!menu.isLeaf()) {
		menu.getChildren().each {
			printMenu(it, prefix+"   ")
		}
	}
}

printMenu(menus.getMenu(), "")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant