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.contextmenu.menuapi;

import java.util.List;
import java.util.Optional;
import org.lorainelab.igb.contextmenu.menuapi.model.AnnotationContextEvent;
import org.lorainelab.igb.contextmenu.menuapi.model.ContextMenuItem;

/**
 *public interface AnnotationContextMenuProvider {
 * @author dcnorris
   */
public interface AnnotationContextMenuProvider {
    public Optional<List<ContextMenuItem>> buildMenuItem(AnnotationContextEvent event);
}

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;
import org.lorainelab.igb.context.menu.MenuSection;
/**
 *
 * @author dcnorris
 */
public class ContextMenuItem {
    private final String menuLabel;
    private MenuIcon menuIcon;
    private function.Function;

public class ContextMenuItem extends MenuItem {
    private ContextMenuSection menuSection = ContextMenuSection.APP;
    public ContextMenuItem(String menuLabel, Set<MenuItem> subMenuItems) {
        super(menuLabel, subMenuItems);
    }
    public ContextMenuItem(String menuLabel, Function<Void, Void> action;) {
    private Set<ContextMenuItem> subMenuItems;
    private int weight = 0super(menuLabel, action);
    private MenuSection menuSection = MenuSection.APP;}
    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)}
}

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;
  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 int getWeight() {
        return weight;
    }
    public void setWeight(int weight)

ContextMenuSection
Code Block
themeMidnight
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);
    }
    public void setMenuSection(MenuSection menuSection) {
        this.menuSection = menuSection;
    }
    public MenuSection getMenuSection() {
        return menuSection;
    }
}




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.contextmenu.menuapi.model.AnnotationContextEvent;
import com.google.common.io.BaseEncodingorg.lorainelab.igb.menu.api.model.ContextMenuItem;
import java.io.InputStreamorg.lorainelab.igb.menu.api.model.ContextMenuSection;
import org.apache.commons.io.IOUtils.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;
import org.slf4j.LoggerFactory;
/**
 *
 * @author dcnorris
 */
public class MenuIcon LOG = LoggerFactory.getLogger(ContextMenuExtension.class);
    private static final String ICONPATH = "terminal.png";
    @Override
    public Optional<List<ContextMenuItem>> buildMenuItem(AnnotationContextEvent event) {
        ContextMenuItem contextMenuItem = new ContextMenuItem("Log Selection ids", (Void t) -> {
    private String encodedImage;
    private static final Logger logger = LoggerFactory.getLogger(MenuIcon.class);
    public MenuIcon(InputStream resourceAsStream) {
        try { event.getSelectedItems().stream().map(selectedSym -> selectedSym.getID()).forEach(LOG::info);
            encodedImage = BaseEncoding.base64().encode(IOUtils.toByteArray(resourceAsStream))return t;
        } catch (Exception ex) {);
        try (InputStream resourceAsStream = loggerContextMenuExtension.class.errorgetClassLoader(ex).getMessagegetResourceAsStream(ICONPATH)), ex);
{
             }contextMenuItem.setMenuIcon(new MenuIcon(resourceAsStream));
    }
    public} byte[]catch getEncodedImage(Exception ex) {
           return BaseEncodingLOG.base64error()ex.decodegetMessage(encodedImage);
    }
}

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

Context Menu Quickstart Code

ContextMenuExtension

, ex);
        }
        contextMenuItem.setMenuSection(ContextMenuSection.INFORMATION);
        return Optional.ofNullable(Arrays.asList(contextMenuItem));
    }
}

pom.xml

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.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<List<ContextMenuItem>> buildMenuItem(AnnotationContextEvent event) {<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>
        ContextMenuItem<dependency>
 contextMenuItem = new ContextMenuItem("Log Selection ids", (Void t) -> {
  <groupId>org.lorainelab.igb</groupId>
          event.getSelectedItems().stream().map(selectedSym -> selectedSym.getID()).forEach(logger::info); <artifactId>genometry</artifactId>
            return t;
<scope>provided</scope>
        </dependency>  });
        <!--Start of  try (InputStream resourceAsStream = ContextMenuExtension.class.getClassLoader().getResourceAsStream(ICONPATH)) {
 logging dependencies-->
        <dependency>
           contextMenuItem.setMenuIcon(new MenuIcon(resourceAsStream)); <groupId>org.slf4j</groupId>
        } catch (Exception ex) { <artifactId>slf4j-api</artifactId>
            logger.error(ex.getMessage(), ex);<scope>provided</scope>
        }
</dependency>
        <!--End of  contextMenuItem.setMenuSection(MenuSection.INFORMATION);logging dependencies-->
        return Optional.ofNullable(Arrays.asList(contextMenuItem));  <dependency>
    }
}



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.

...

...