Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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. 

Getting Started

What is a context menu?

Info
titleWikipedia

"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."

Example Context Menu on OS X 10.9

Image Modified

IGB Context Menu

...

Context Menu API Overview

Image Added

Image Removed 

Interface

Code Block
themeMidnight
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();
}}

Data Model

AnnotationContextEvent

Code Block
themeMidnight
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;
    }
}

ContextMenuItem

Code Block
themeMidnight
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 menuLabelfunction.Function;

public class ContextMenuItem extends MenuItem {
    private ContextMenuSection menuSection = ContextMenuSection.APP;
    public ContextMenuItem(String menuLabel, Set<MenuItem> subMenuItems) {
        super(menuLabel, subMenuItems);
    private}
 MenuIcon menuIcon;
  public ContextMenuItem(String privatemenuLabel, Function<Void, Void> action;) {
    private Set<ContextMenuItem> subMenuItems;
  super(menuLabel, action);
 private int weight = 0;}
    public void ContextMenuItemsetMenuSection(String menuLabel, Set<ContextMenuItem> subMenuItemsContextMenuSection menuSection) {
        this.menuSection = checkNotNull(menuLabel)menuSection;
    }
    checkNotNullpublic ContextMenuSection getMenuSection(subMenuItems); {
        checkState(!Strings.isNullOrEmpty(menuLabel))return menuSection;
        checkState(!subMenuItems.isEmpty());
        this.menuLabel = menuLabel;
        this.subMenuItems = ImmutableSet.copyOf(subMenuItems);
    }}
}

MenuIcon

Code Block
themeMidnight
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;
    private static final Logger logger = LoggerFactory.getLogger(MenuIcon.class);
    public ContextMenuItemMenuIcon(String menuLabel, Function<Void, Void> actionInputStream resourceAsStream) {
        try checkNotNull(menuLabel);
  {
      checkState(!Strings.isNullOrEmpty(menuLabel));
      encodedImage  checkNotNull(action= BaseEncoding.base64().encode(IOUtils.toByteArray(resourceAsStream));
        } catch this.menuLabel = menuLabel;
(Exception ex) {
            this.action = actionlogger.error(ex.getMessage(), ex);
        subMenuItems = ImmutableSet.of();}
    }
    public Stringbyte[] getMenuLabelgetEncodedImage() {
        return menuLabelBaseEncoding.base64().decode(encodedImage);
    }
}

    public Function<Void, Void> getAction()
ContextMenuSection
Code Block
themeMidnight
package org.lorainelab.igb.menu.api.model;

public enum ContextMenuSection {
    INFORMATION,
    return action;SEQUENCE,
    }APP,
    public Set<ContextMenuItem> getSubMenuItems() {
        return subMenuItems;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
    public void setMenuIcon(MenuIcon menuIcon) {
        this.menuIcon = menuIcon;
    }
    public Optional<MenuIcon> getMenuIcon() {
        return Optional.ofNullable(menuIcon);
    }
}


MenuIcon

Code Block
themeMidnight
packageUI_ACTION;
}

Context Menu Sections

Image Added

Context Menu Quickstart Code

ContextMenuExtension

Code Block
themeMidnight
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.lorainelab.igb.menu.api.model.ContextMenuItem;
import org.lorainelab.igb.contextmenu.menuapi.model.ContextMenuSection;
import comorg.googlelorainelab.commonigb.io.BaseEncoding;
import java.io.InputStreammenu.api.model.MenuIcon;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
@Component(immediate *
 * @author dcnorris
 */= true)
public class MenuIconContextMenuExtension {
implements    private String encodedImage;AnnotationContextMenuProvider {
    private static final Logger loggerLOG = LoggerFactory.getLogger(MenuIconContextMenuExtension.class);
    publicprivate MenuIcon(InputStream resourceAsStream) {
   static final String ICONPATH = "terminal.png";
     try {@Override
    public Optional<List<ContextMenuItem>> buildMenuItem(AnnotationContextEvent event) {
    encodedImage = BaseEncoding.base64().encode(IOUtils.toByteArray(resourceAsStream));
  ContextMenuItem contextMenuItem = new ContextMenuItem("Log Selection }ids", catch (ExceptionVoid ext) -> {
            loggerevent.errorgetSelectedItems(ex).getMessagestream(), ex.map(selectedSym -> selectedSym.getID()).forEach(LOG::info);
         }
   return }t;
      public byte[] getEncodedImage(}) {;
        return BaseEncoding.base64try (InputStream resourceAsStream = ContextMenuExtension.class.getClassLoader().decodegetResourceAsStream(encodedImageICONPATH));
    }
}

MenuSection

Code Block
themeMidnight
package org.lorainelab.igb.context.menu;
/**
 *
 * @author dcnorris
 */
public enum MenuSection {
    INFORMATION, SEQUENCE, APP, UI_ACTION;
}

Context Menu Sections

Image Removed

Conext Menu Quickstart Code

ContextMenuExtension

Code Block
themeMidnight
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) -> {
   {
            contextMenuItem.setMenuIcon(new MenuIcon(resourceAsStream));
        } catch (Exception ex) {
            LOG.error(ex.getMessage(), ex);
        }
        contextMenuItem.setMenuSection(ContextMenuSection.INFORMATION);
        return Optional.ofNullable(Arrays.asList(contextMenuItem));
    }
}

pom.xml

Code Block
themeMidnight
<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>
            <artifactId>bndlib</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
          event.getSelectedItems().stream().map(selectedSym -> selectedSym.getID()).forEach(logger::info);<groupId>org.lorainelab.igb</groupId>
            return t;
<artifactId>genometry</artifactId>
            });
<scope>provided</scope>
        </dependency>  try
 (InputStream resourceAsStream = ContextMenuExtension.class.getClassLoader().getResourceAsStream(ICONPATH)) {
   <!--Start of logging dependencies-->
      contextMenuItem.setMenuIcon(new MenuIcon(resourceAsStream));  <dependency>
        } catch (Exception ex) { <groupId>org.slf4j</groupId>
            logger.error(ex.getMessage(), ex);
<artifactId>slf4j-api</artifactId>
            }<scope>provided</scope>
        return Optional.ofNullable(contextMenuItem);</dependency>
    }
    @Override
    public MenuSection getMenuSection() {
<!--End of logging dependencies-->
          <dependency>
  return MenuSection.INFORMATION;
    }
}

pom.xml

Code Block
themeMidnight
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    <groupId>org.lorainelab.igb</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>
    <parent>
<artifactId>org.lorainelab.igb.menu.api</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <groupId>com.affymetrix</groupId><plugin>
        <artifactId>igb-project</artifactId>
        <version>9<groupId>org.0apache.0<felix</version>groupId>
        <relativePath>../../pom.xml</relativePath>        <artifactId>maven-bundle-plugin</artifactId>
    </parent>
    <groupId>org.lorainelab.igb</groupId>
    <artifactId>context-menu-api</artifactId>
    <packaging>bundle<<extensions>true</packaging>extensions>
      <name>Context Menu API</name>
    
    <dependencies><executions>
        <dependency>
            <groupId>biz.aQute.bnd</groupId>
<execution>
              <artifactId>bndlib</artifactId>
          <id>build plugin <scope>provided<repository</scope>id>
        </dependency>
        <dependency>
        <goals>
    <groupId>com.affymetrix</groupId>
            <artifactId>genometry</artifactId>
            <scope>provided</scope>
     <goal>
   </dependency>  
        <dependency>
            <groupId>com.affymetrix</groupId>
       index
     <artifactId>igb-services</artifactId>
            <scope>provided</scope>
        </dependency>   </goal>   
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <scope>provided<</scope>goals>
           </dependency>
        <!--Start of logging dependencies-->
  <phase>package</phase>
      <dependency>
            <groupId>org.slf4j</groupId>
      <configuration>
      <artifactId>slf4j-api</artifactId>
              <scope>provided</scope>
        <obrRepository>${project.build.directory}</dependency>
obrRepository>    
     <!--End of logging dependencies-->
        <dependency>
            <groupId>com.affymetrix</groupId>
<mavenRepository>${project.build.directory}</mavenRepository>               <artifactId>affymetrix-common</artifactId>
       
     <scope>provided</scope>           
        </dependency>configuration>
        <dependency>
            <groupId>commons-io<</groupId>execution>
             <artifactId>commons-io</artifactId>
   </executions>
         <scope>provided</scope>       <configuration>
        </dependency>
    </dependencies>
    <build>
    <instructions>
    <plugins>
            <plugin>
       <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
         <artifactId>maven-clean-plugin</artifactId>
               <Import-Package>*</plugin>Import-Package>
              <plugin>
          <Export-Package/>
        <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
 <Service-Component>*</Service-Component>
           </plugin>          
     <Bundle-Description>${bundleDescription}</Bundle-Description>
       <plugin>
                <groupId>org.apache.felix</groupId></instructions>
                <artifactId>maven-bundle-plugin</artifactId>
</configuration>
            </plugin>     <extensions>true</extensions>
                <configuration><plugin>
                <groupId>org.lorainelab</groupId>
    <instructions>
             <artifactId>bundle-markdown-encoder</artifactId>
           <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
     <executions>
                   <Import-Package>*</Import-Package> <execution>
                        <Export-Package><goals>
                            ${project.groupId}.context.menu,<goal>encodeMarkdown</goal>
                        </goals>
      ${project.groupId}.context.menu.model
              </execution> 
         </Export-Package>
       </executions>
                 <Service-Component>*</Service-Component><configuration>
                    </instructions><manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
                </configuration>
            </plugin>       
        </plugins>
    </build>
</project>

(Optional) Add README.md

Add a README.md markdown file to the root of the project for a customized APP manager display of your new app.

...

...