...
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 RemovedImage Added
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 ContextMenuItem {
private final String menuLabel;function.Function;
public class ContextMenuItem extends MenuItem {
private ContextMenuSection menuSection = ContextMenuSection.APP;
public ContextMenuItem(String menuLabel, Set<MenuItem> subMenuItems) {
private MenuIcon menuIcon;
private Function<Void, Void> action;
private Set<ContextMenuItem> subMenuItemssuper(menuLabel, subMenuItems);
private int weight = 0;}
public ContextMenuItem(String menuLabel, Function<Void, Set<ContextMenuItem>Void> subMenuItemsaction) {
checkNotNullsuper(menuLabel, action);
}
public void checkNotNullsetMenuSection(subMenuItems);ContextMenuSection menuSection) {
checkState(!Strings.isNullOrEmpty(menuLabel))this.menuSection = menuSection;
}
public ContextMenuSection checkStategetMenuSection(!subMenuItems.isEmpty());) {
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;
encodedImage subMenuItems = 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 |
---|
|
packageUI_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.contextmenu.menuapi.modelAnnotationContextMenuProvider;
import comorg.googlelorainelab.commonigb.io.BaseEncoding;
import java.io.InputStreammenu.api.model.AnnotationContextEvent;
import org.lorainelab.igb.apachemenu.commonsapi.iomodel.IOUtilsContextMenuItem;
import org.lorainelab.igb.menu.api.model.ContextMenuSection;
import org.lorainelab.igb.menu.api.model.MenuIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
@Component(immediate *
* @author dcnorris
*/= true)
public class MenuIconContextMenuExtension implements AnnotationContextMenuProvider {
private String encodedImage;
private static static final Logger loggerLOG = LoggerFactory.getLogger(MenuIconContextMenuExtension.class);
public MenuIcon(InputStream resourceAsStream) {private static final String ICONPATH = "terminal.png";
@Override
trypublic {
Optional<List<ContextMenuItem>> buildMenuItem(AnnotationContextEvent event) {
ContextMenuItem encodedImagecontextMenuItem = BaseEncoding.base64().encode(IOUtils.toByteArray(resourceAsStream));
} catch (Exception ex) new 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.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.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);
@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);
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>
<dependency>
<groupId>biz.aQute.bnd</groupId>
return t;
});<artifactId>bndlib</artifactId>
return Optional.ofNullable(contextMenuItem);
}<scope>provided</scope>
@Override
public</dependency>
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.lorainelab.igb</groupId>
<artifactId>genometry</artifactId>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <scope>provided</scope>
</dependency>
<modelVersion>4.0.0</modelVersion>
<parent><!--Start of logging dependencies-->
<groupId>com.affymetrix</groupId>
<dependency>
<artifactId>igb-project</artifactId><groupId>org.slf4j</groupId>
<version>9.0.0</version>
<relativePath>../../pom.xml</relativePath><artifactId>slf4j-api</artifactId>
</parent>
<groupId>org.lorainelab.igb</groupId>
<artifactId>context-menu-api</artifactId>
<scope>provided</scope>
<packaging>bundle</packaging>
<name>Context Menu API<</name>dependency>
<dependencies>
<!--End of logging dependencies-->
<dependency>
<groupId>biz<groupId>org.aQutelorainelab.bnd<igb</groupId>
<artifactId>bndlib<<artifactId>org.lorainelab.igb.menu.api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<dependency><build>
<plugins>
<groupId>com.affymetrix</groupId>
<plugin>
<artifactId>genometry</artifactId>
<scope>provided</scope><groupId>org.apache.felix</groupId>
</dependency>
<artifactId>maven-bundle-plugin</artifactId>
<dependency>
<groupId>com.affymetrix<<extensions>true</groupId>extensions>
<artifactId>igb-services</artifactId> <executions>
<scope>provided</scope>
</dependency><execution>
<dependency>
<id>build plugin <groupId>com.google.guava</groupId>repository</id>
<artifactId>guava</artifactId>
<scope>provided</scope><goals>
</dependency>
<!--Start of logging dependencies-->
<dependency><goal>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
index
<scope>provided</scope>
</dependency>
<!--End of logging dependencies-->
</goal> <dependency>
<groupId>com.affymetrix</groupId>
<artifactId>affymetrix-common</artifactId>
<scope>provided</scope> </goals>
</dependency>
<dependency><phase>package</phase>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId><configuration>
<scope>provided</scope>
<obrRepository>${project.build.directory}</dependency>
obrRepository> </dependencies>
<build>
<plugins>
<plugin>
<mavenRepository>${project.build.directory}</mavenRepository> <artifactId>maven-clean-plugin</artifactId>
</plugin>
<plugin>
</configuration>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId></execution>
</plugin> </executions>
<plugin><configuration>
<groupId>org.apache.felix</groupId>
<instructions>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<configuration>
<Import-Package>*</Import-Package>
<instructions>
<Export-Package/>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName> <Service-Component>*</Service-Component>
<Import-Package>*<Bundle-Description>${bundleDescription}</ImportBundle-Package>Description>
</instructions>
<Export-Package>
</configuration>
</plugin> ${project.groupId}.context.menu,
<plugin>
${project.groupId}.context.menu.model<groupId>org.lorainelab</groupId>
<artifactId>bundle-markdown-encoder</artifactId>
</Export-Package>
<executions>
<Service-Component>*</Service-Component>
<execution>
</instructions>
<goals>
</configuration> <goal>encodeMarkdown</goal>
</plugin>
</plugins>
goals>
</build>
</project>
execution>
</executions>
<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
Image Added