Page tree

Versions Compared

Key

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

Table of Contents

Introduction to the Context Menu API

Design

...

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 Added

IGB Context Menu

Image Added

Image Added

 

Context Menu API Overview

Image Added

 

Interface

Code Block
themeMidnight
package org.lorainelab.igb.context.menumenu.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.context.menu.menu.api.model;
import java.util.Set;
import java.util.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) {
        super(menuLabel, action);
    }
    public void setMenuSection(ContextMenuSection menuSection) {
        this.menuSection = menuSection;
    }
    public ContextMenuSection getMenuSection() {
        return menuSection;
    }
}

MenuIcon

Code Block
themeMidnight
package org.lorainelab.igb.menu.api.model;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet.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 MenuIcon(InputStream resourceAsStream) {
        try {
            encodedImage = BaseEncoding.base64().encode(IOUtils.toByteArray(resourceAsStream));
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }
    public byte[] getEncodedImage() {
        return BaseEncoding.base64().decode(encodedImage);
    }
}

ContextMenuSection
Code Block
themeMidnight
package org.lorainelab.igb.menu.api.model;

public enum ContextMenuSection {
    INFORMATION,
    SEQUENCE,
    APP,
    UI_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.OptionalArrays;
import java.util.SetList;
import java.util.function.Function;
/**
 *
 * @author dcnorris
 */
public class ContextMenuItem.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.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 LOG String menuLabel= LoggerFactory.getLogger(ContextMenuExtension.class);
    private MenuIcon menuIcon static final String ICONPATH = "terminal.png";
    private Function<Void, Void> action;
    private Set<ContextMenuItem> subMenuItems@Override
    public Optional<List<ContextMenuItem>> buildMenuItem(AnnotationContextEvent event) {
        ContextMenuItem contextMenuItem = new ContextMenuItem("Log Selection ids", (Void t) -> {
            event.getSelectedItems().stream().map(selectedSym -> selectedSym.getID()).forEach(LOG::info);
            return t;
    private int weight = 0});
        publictry ContextMenuItem(StringInputStream menuLabel,resourceAsStream Set<ContextMenuItem> subMenuItems= ContextMenuExtension.class.getClassLoader().getResourceAsStream(ICONPATH)) {
            contextMenuItem.setMenuIcon(new checkNotNullMenuIcon(menuLabelresourceAsStream));
        checkNotNull(subMenuItems);
} catch (Exception ex) {
         checkState(!Strings.isNullOrEmpty(menuLabel));   LOG.error(ex.getMessage(), ex);
        }
        checkState(!subMenuItems.isEmpty());
contextMenuItem.setMenuSection(ContextMenuSection.INFORMATION);
        return  this.menuLabel = menuLabel;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"
        this.subMenuItems xsi:schemaLocation= ImmutableSet.copyOf(subMenuItems);"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    }
<modelVersion>4.0.0</modelVersion>
    <parent>
  public ContextMenuItem(String menuLabel, Function<Void, Void> action) { <groupId>org.lorainelab.igb</groupId>
        checkNotNull(menuLabel);<artifactId>igb-project</artifactId>
        checkState(!Strings.isNullOrEmpty(menuLabel));<version>9.0.0</version>
    </parent>
    checkNotNull(action);<groupId>org.lorainelab.igb</groupId>
    <artifactId>org.lorainelab.igb.menu.api.quickstart</artifactId>
    this.menuLabel = menuLabel;
<version>1.0.0</version>
    <packaging>bundle</packaging>
    <name>Context Menu Extension Quickstart</name>
    
    <dependencies>
        <dependency>
            this.action = action;
<groupId>biz.aQute.bnd</groupId>
            <artifactId>bndlib</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
         subMenuItems  = ImmutableSet.of();
    }
<groupId>org.lorainelab.igb</groupId>
            <artifactId>genometry</artifactId>
            <scope>provided</scope>
        </dependency>  
        <!--Start of logging dependencies-->
        <dependency>
            <groupId>org.slf4j</groupId>
         public String getMenuLabel() { <artifactId>slf4j-api</artifactId>
        return menuLabel;
    <scope>provided</scope>
        </dependency>
        }<!--End of logging dependencies-->
    public   Function<Void, Void> getAction() {<dependency>
           return action;
 <groupId>org.lorainelab.igb</groupId>
            }
<artifactId>org.lorainelab.igb.menu.api</artifactId>
       public   Set<ContextMenuItem> getSubMenuItems() {
  <scope>provided</scope>
        </dependency>
    </dependencies>
   return subMenuItems;
<build>
        }<plugins>
        public void setWeight(int weight) {
<plugin>
               this.weight = weight;
    }
    public void setMenuIcon(MenuIcon menuIcon) {
        this.menuIcon = menuIcon;
    }
    public Optional<MenuIcon> getMenuIcon() { <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <executions>
                    <execution>
                        <id>build plugin repository</id>
                        <goals>
                            <goal>
                                index
                            </goal>                       
                        </goals>
                        <phase>package</phase>
                        <configuration>
                            <obrRepository>${project.build.directory}</obrRepository>    
                            <mavenRepository>${project.build.directory}</mavenRepository>                      
                        </configuration>
                    </execution>
                </executions>
                <configuration>
                    <instructions>
                       <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                        <Import-Package>*</Import-Package>
                        <Export-Package/>
                        <Service-Component>*</Service-Component>
                        <Bundle-Description>${bundleDescription}</Bundle-Description>
                    </instructions>
                </configuration>
            </plugin>     
            <plugin>
        return Optional.ofNullable(menuIcon);
    }
}

...

        <groupId>org.lorainelab</groupId>
                <artifactId>bundle-markdown-encoder</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>encodeMarkdown</goal>
                        </goals>
                    </execution> 
                </executions>
                <configuration>
                    <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.

Example

Code Block
themeMidnight
# 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.modelapi.quickstart;
import comaQute.googlebnd.commonannotation.iocomponent.BaseEncodingComponent;
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.apacheigb.context.commonsmenu.iomodel.IOUtilsMenuIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 *
 * @author dcnorris
 */
public@Component(immediate class MenuIcon {
    private String encodedImage;= true)
public class ContextMenuExtension implements AnnotationContextMenuProvider {
    private static final Logger logger = LoggerFactory.getLogger(MenuIconContextMenuExtension.class);
    private static final String ICONPATH = "terminal.png";
    @Override
    public MenuIconOptional<ContextMenuItem> buildMenuItem(InputStreamAnnotationContextEvent resourceAsStreamevent) {
        ContextMenuItem contextMenuItem = new ContextMenuItem("Log Selection ids", (Void t) try-> {
            event.getSelectedItems().stream().map(selectedSym  encodedImage = BaseEncoding.base64().encode(IOUtils.toByteArray-> 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 byte[]MenuSection getEncodedImagegetMenuSection() {
        return BaseEncoding.base64().decode(encodedImage)MenuSection.INFORMATION;
    }
}
```

 

Building the Example

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

 

Results

 

Image Added