...
The goal of this quickstart is to introduce you to the the context menu api, so you will be able to add context menu extensions in your IGB Apps.
- Clone the quickstart project
Info |
---|
|
"A context menu (also called contextual, shortcut, and popup or pop-up menu) is a menu in a graphical user interface (GUI) that appears upon user interaction, such as a right-click mouse operation. A context menu offers a limited set of choices that are available in the current state, or context, of the operating system or application. Usually the available choices are actions related to the selected object." |
Image Modified
IGB Context Menu
...
Image Added
Image Removed
Code Block |
---|
|
package org.lorainelab.igb.context.menu.menu.api;
import java.util.List;
import java.util.Optional;
import org.lorainelab.igb.contextmenu.menuapi.model.AnnotationContextEvent;
import org.lorainelab.igb.contextmenu.menuapi.model.ContextMenuItem;
/**
*
* @author dcnorris
*/
public interface AnnotationContextMenuProvider {public interface AnnotationContextMenuProvider {
public Optional<ContextMenuItem>Optional<List<ContextMenuItem>> buildMenuItem(AnnotationContextEvent event);
public MenuSection getMenuSection();
}}
|
Code Block |
---|
|
package org.lorainelab.igb.contextmenu.menuapi.model;
import com.affymetrix.genometry.symmetry.impl.SeqSymmetry;
import java.util.List;
/**
*
* @author dcnorris
*/
public class AnnotationContextEvent {
private final List<SeqSymmetry> selectedItems;
public AnnotationContextEvent(List<SeqSymmetry> selectedItems) {
this.selectedItems = selectedItems;
}
public List<SeqSymmetry> getSelectedItems() {
return selectedItems;
}
}
|
Code Block |
---|
|
package org.lorainelab.igb.contextmenu.menuapi.model;
import static comjava.google.common.base.Preconditions.checkNotNullutil.Set;
import static comjava.googleutil.common.base.Preconditions.checkState;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
/**
*
* @author dcnorris
*/
public class ContextMenuItemfunction.Function;
public class ContextMenuItem extends MenuItem {
private ContextMenuSection menuSection = ContextMenuSection.APP;
public ContextMenuItem(String menuLabel, Set<MenuItem> subMenuItems) {
private final String super(menuLabel, subMenuItems);
private}
MenuIcon menuIcon;
public ContextMenuItem(String privatemenuLabel, Function<Void, Void> action;) {
private Set<ContextMenuItem> subMenuItems;
private int weight = 0;super(menuLabel, action);
}
public void ContextMenuItemsetMenuSection(String menuLabel, Set<ContextMenuItem> subMenuItemsContextMenuSection menuSection) {
checkNotNull(menuLabel);
checkNotNull(subMenuItems)this.menuSection = menuSection;
checkState(!Strings.isNullOrEmpty(menuLabel));}
public checkState(!subMenuItems.isEmpty());ContextMenuSection getMenuSection() {
this.menuLabel = menuLabelreturn menuSection;
this.subMenuItems = ImmutableSet.copyOf(subMenuItems);
}
public ContextMenuItem(String menuLabel, Function<Void, Void> action)}
}
|
Code Block |
---|
|
package org.lorainelab.igb.menu.api.model;
import com.google.common.io.BaseEncoding;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MenuIcon {
private String encodedImage;
checkNotNull(menuLabel);
private static final Logger logger = checkState(!Strings.isNullOrEmpty(menuLabel)LoggerFactory.getLogger(MenuIcon.class);
public MenuIcon(InputStream checkNotNull(action);resourceAsStream) {
this.menuLabel = menuLabel;
try {
this.action = action;
subMenuItemsencodedImage = ImmutableSet.of()BaseEncoding.base64().encode(IOUtils.toByteArray(resourceAsStream));
}
public} Stringcatch getMenuLabel(Exception ex) {
return menuLabel;
}
public Function<Void, Void> getAction() {logger.error(ex.getMessage(), ex);
return action;}
}
public Set<ContextMenuItem>byte[] getSubMenuItemsgetEncodedImage() {
return subMenuItemsBaseEncoding.base64().decode(encodedImage);
}
}
public void setWeight(int weight)
|
ContextMenuSection
Code Block |
---|
|
package org.lorainelab.igb.menu.api.model;
public enum ContextMenuSection {
INFORMATION,
this.weight = weight;SEQUENCE,
}APP,
public void setMenuIcon(MenuIcon menuIcon) {
this.menuIcon = menuIcon;
}
public Optional<MenuIcon> getMenuIcon() {
return Optional.ofNullable(menuIcon);
}
}
|
Code Block |
---|
|
package org.lorainelab.igb.context.menu.model;
import com.google.common.io.BaseEncoding;
import java.io.InputStreamUI_ACTION;
}
|
Image Added
Code Block |
---|
|
package org.lorainelab.igb.context.menu.api.quickstart;
import aQute.bnd.annotation.component.Component;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.lorainelab.igb.menu.api.AnnotationContextMenuProvider;
import org.lorainelab.igb.menu.api.model.AnnotationContextEvent;
import org.apachelorainelab.commonsigb.io.IOUtils;
import org.slf4j.Loggermenu.api.model.ContextMenuItem;
import org.slf4j.LoggerFactory;
/**
*
* @author dcnorris
*/
public class MenuIcon {
private String encodedImage;lorainelab.igb.menu.api.model.ContextMenuSection;
import org.lorainelab.igb.menu.api.model.MenuIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(immediate = true)
public class ContextMenuExtension implements AnnotationContextMenuProvider {
private static final Logger loggerLOG = LoggerFactory.getLogger(MenuIconContextMenuExtension.class);
private static final publicString MenuIcon(InputStream resourceAsStream) {ICONPATH = "terminal.png";
@Override
trypublic {
Optional<List<ContextMenuItem>> buildMenuItem(AnnotationContextEvent event) {
ContextMenuItem encodedImagecontextMenuItem = new BaseEncoding.base64().encode(IOUtils.toByteArray(resourceAsStream));
} catch (Exception ex)ContextMenuItem("Log Selection ids", (Void t) -> {
loggerevent.errorgetSelectedItems(ex).getMessagestream(), ex);
.map(selectedSym -> selectedSym.getID()).forEach(LOG::info);
}
return }t;
public byte[] getEncodedImage() {
});
try (InputStream resourceAsStream return= BaseEncodingContextMenuExtension.class.base64getClassLoader().decodegetResourceAsStream(encodedImageICONPATH)); {
}
} |
Code Block |
---|
|
package org.lorainelab.igb.context.menu;
/**
*
* @author dcnorris
*/
public enum MenuSection contextMenuItem.setMenuIcon(new MenuIcon(resourceAsStream));
} catch (Exception ex) {
INFORMATION, SEQUENCE, APP, UI_ACTION;
} |
Image Removed
Code Block |
---|
|
package org.lorainelab.igb.context.menu.api.quickstart;
import aQute.bnd.annotation.component.Component;
import java.io.InputStream;
import java.util.Optional;
import org.lorainelab.igb.context.menu.AnnotationContextMenuProvider;
import org.lorainelab.igb.context.menu.MenuSection;
import org.lorainelab.igb.context.menu.model.AnnotationContextEvent;
import org.lorainelab.igb.context.menu.model.ContextMenuItem;
import org.lorainelab.igb.context.menu.model.MenuIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author dcnorris
*/
@Component(immediate = true)
public class ContextMenuExtension implements AnnotationContextMenuProvider {
private static final Logger logger = LoggerFactory.getLogger(ContextMenuExtension.class);
private static final String ICONPATH = "terminal.png";
@Override
public Optional<ContextMenuItem> buildMenuItem(AnnotationContextEvent event) {
ContextMenuItem contextMenuItem = new ContextMenuItem("Log Selection ids", (Void t) -> {
LOG.error(ex.getMessage(), ex);
}
contextMenuItem.setMenuSection(ContextMenuSection.INFORMATION);
return Optional.ofNullable(Arrays.asList(contextMenuItem));
}
}
|
Code Block |
---|
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.lorainelab.igb</groupId>
<artifactId>igb-project</artifactId>
<version>9.0.0</version>
</parent>
<groupId>org.lorainelab.igb</groupId>
<artifactId>org.lorainelab.igb.menu.api.quickstart</artifactId>
<version>1.0.0</version>
<packaging>bundle</packaging>
<name>Context Menu Extension Quickstart</name>
<dependencies>
event.getSelectedItems().stream().map(selectedSym -> selectedSym.getID()).forEach(logger::info);
<dependency>
<groupId>biz.aQute.bnd</groupId>
return t;
});<artifactId>bndlib</artifactId>
try (InputStream resourceAsStream = ContextMenuExtension.class.getClassLoader().getResourceAsStream(ICONPATH)) {
<scope>provided</scope>
</dependency>
contextMenuItem.setMenuIcon(new MenuIcon(resourceAsStream)); <dependency>
} catch (Exception ex) { <groupId>org.lorainelab.igb</groupId>
logger.error(ex.getMessage(), ex);
<artifactId>genometry</artifactId>
}<scope>provided</scope>
</dependency> return Optional.ofNullable(contextMenuItem);
}
<!--Start of logging @Overridedependencies-->
public MenuSection getMenuSection() {<dependency>
return MenuSection.INFORMATION;
}
}
|
Code Block |
---|
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<groupId>org.slf4j</groupId>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <artifactId>slf4j-api</artifactId>
<parent>
<groupId>com.affymetrix<<scope>provided</groupId>scope>
<artifactId>igb-project<</artifactId>dependency>
<version>9.0.0</version>
<!--End of logging dependencies-->
<relativePath>../../pom.xml</relativePath><dependency>
</parent>
<groupId>org.lorainelab.igb</groupId>
<artifactId>context-menu- <artifactId>org.lorainelab.igb.menu.api</artifactId>
<packaging>bundle</packaging>
<name>Context Menu API<<scope>provided</name>scope>
<dependencies></dependency>
</dependencies>
<dependency><build>
<plugins>
<groupId>biz.aQute.bnd</groupId>
<plugin>
<artifactId>bndlib</artifactId>
<scope>provided</scope><groupId>org.apache.felix</groupId>
</dependency>
<dependency><artifactId>maven-bundle-plugin</artifactId>
<groupId>com.affymetrix</groupId>
<extensions>true</extensions>
<artifactId>genometry</artifactId>
<executions>
<scope>provided</scope>
</dependency>
<execution>
<dependency>
<groupId>com.affymetrix</groupId>
<id>build plugin <artifactId>igb-services<repository</artifactId>id>
<scope>provided</scope>
</dependency> <goals>
<dependency>
<groupId>com.google.guava</groupId>
<goal>
<artifactId>guava</artifactId>
<scope>provided</scope>
</dependency>
index
<!--Start of logging dependencies-->
<dependency>
<groupId>org.slf4j</groupId>
</goal> <artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<!--End of logging dependencies-->
<dependency></goals>
<groupId>com.affymetrix</groupId>
<artifactId>affymetrix-common<<phase>package</artifactId>phase>
<scope>provided</scope> <configuration>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<obrRepository>${project.build.directory}</obrRepository>
<artifactId>commons-io</artifactId>
<scope>provided</scope>
<mavenRepository>${project.build.directory}</mavenRepository> </dependency>
</dependencies>
<build>
<plugins>
<plugin>
</configuration>
<artifactId>maven-clean-plugin</artifactId>
</plugin>execution>
<plugin> </executions>
<groupId>org.apache.maven.plugins</groupId>
<configuration>
<artifactId>maven-dependency-plugin</artifactId>
<instructions>
</plugin>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<plugin>
<groupId>org.apache.felix</groupId><Import-Package>*</Import-Package>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions><Export-Package/>
<configuration>
<instructions><Service-Component>*</Service-Component>
<Bundle-SymbolicName>$Description>${project.artifactIdbundleDescription}</Bundle-SymbolicName>Description>
<Import-Package>*</Import-Package>instructions>
</configuration>
<Export-Package> </plugin>
<plugin>
${project.groupId}.context.menu,<groupId>org.lorainelab</groupId>
<artifactId>bundle-markdown-encoder</artifactId>
${project.groupId}.context.menu.model
<executions>
</Export-Package><execution>
<Service-Component>*</Service-Component>
<goals>
< <goal>encodeMarkdown</instructions>goal>
</configuration>
goals>
</plugin>execution>
</plugins>executions>
</build>
</project>
|
...
<configuration>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</configuration>
</plugin>
</plugins>
</build>
</project>
|
Add a README.md markdown file to the root of the project for a customized APP manager display of your new app.
Example
Code Block |
---|
|
# Context Menu API Quickstart
A small example context menu extension that logs the ids of all selected SeqSymmetry objects
```java
package org.lorainelab.igb.context.menu.api.quickstart;
import aQute.bnd.annotation.component.Component;
import java.io.InputStream;
import java.util.Optional;
import org.lorainelab.igb.context.menu.AnnotationContextMenuProvider;
import org.lorainelab.igb.context.menu.MenuSection;
import org.lorainelab.igb.context.menu.model.AnnotationContextEvent;
import org.lorainelab.igb.context.menu.model.ContextMenuItem;
import org.lorainelab.igb.context.menu.model.MenuIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author dcnorris
*/
@Component(immediate = true)
public class ContextMenuExtension implements AnnotationContextMenuProvider {
private static final Logger logger = LoggerFactory.getLogger(ContextMenuExtension.class);
private static final String ICONPATH = "terminal.png";
@Override
public Optional<ContextMenuItem> buildMenuItem(AnnotationContextEvent event) {
ContextMenuItem contextMenuItem = new ContextMenuItem("Log Selection ids", (Void t) -> {
event.getSelectedItems().stream().map(selectedSym -> selectedSym.getID()).forEach(logger::info);
return t;
});
try (InputStream resourceAsStream = ContextMenuExtension.class.getClassLoader().getResourceAsStream(ICONPATH)) {
contextMenuItem.setMenuIcon(new MenuIcon(resourceAsStream));
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
return Optional.ofNullable(contextMenuItem);
}
@Override
public MenuSection getMenuSection() {
return MenuSection.INFORMATION;
}
}
```
|
- Navigate to the root of the project (e.g. the cloned repository https://bitbucket.org/lorainelab/context-menu-api-quickstart.git)
- use maven to build the project
Code Block |
---|
mvn clean install |
- The build process will result in a new folder being added into your project directory (i.e. the target folder)
- Add the target directory as a new IGB App repository
- IGB App Manger Tab -> Launch App Manager -> Manage Repositories -> Add...
- Image Added
- Image Added
- Select and add the target directory
- Use the IGB App Manager to Install the new plugin
- Image Added