org.scijava.ui.DefaultUIService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scijava-common Show documentation
Show all versions of scijava-common Show documentation
SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO.
/*
* #%L
* SciJava Common shared library for SciJava software.
* %%
* Copyright (C) 2009 - 2017 Board of Regents of the University of
* Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
* Institute of Molecular Cell Biology and Genetics, University of
* Konstanz, and KNIME GmbH.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.scijava.ui;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.scijava.app.AppService;
import org.scijava.app.StatusService;
import org.scijava.app.event.StatusEvent;
import org.scijava.display.Display;
import org.scijava.display.DisplayService;
import org.scijava.display.event.DisplayActivatedEvent;
import org.scijava.display.event.DisplayCreatedEvent;
import org.scijava.display.event.DisplayDeletedEvent;
import org.scijava.display.event.DisplayUpdatedEvent;
import org.scijava.event.EventHandler;
import org.scijava.event.EventService;
import org.scijava.log.LogService;
import org.scijava.platform.event.AppQuitEvent;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.plugin.PluginInfo;
import org.scijava.plugin.PluginService;
import org.scijava.service.AbstractService;
import org.scijava.service.Service;
import org.scijava.thread.ThreadService;
import org.scijava.ui.DialogPrompt.MessageType;
import org.scijava.ui.DialogPrompt.OptionType;
import org.scijava.ui.DialogPrompt.Result;
import org.scijava.ui.event.UIShownEvent;
import org.scijava.ui.headless.HeadlessUI;
import org.scijava.ui.viewer.DisplayViewer;
/**
* Default service for handling SciJava user interfaces.
*
* @author Curtis Rueden
*/
@Plugin(type = Service.class)
public final class DefaultUIService extends AbstractService implements
UIService
{
@Parameter
private LogService log;
@Parameter
private EventService eventService;
@Parameter
private StatusService statusService;
@Parameter
private ThreadService threadService;
@Parameter
private AppService appService;
@Parameter
private PluginService pluginService;
@Parameter
private DisplayService displayService;
/**
* A list of extant display viewers. It's needed in order to find the viewer
* associated with a display.
*/
private List> displayViewers;
/** List of available user interfaces, ordered by priority. */
private List uiList;
/** Map of available user interfaces, keyed off their names. */
private Map uiMap;
/** Whether lazy initialization is complete. */
private boolean initialized;
/** Whether the service was disposed */
private boolean disposed;
/** The default user interface to use, if one is not explicitly specified. */
private UserInterface defaultUI;
private boolean activationInvocationPending = false;
// -- UIService methods --
@Override
public void addUI(final UserInterface ui) {
addUI(null, ui);
}
@Override
public void addUI(final String name, final UserInterface ui) {
if (!initialized) discoverUIs();
addUserInterface(name, ui);
}
@Override
public void showUI() {
if (disposed) return;
final UserInterface ui = getDefaultUI();
if (ui == null) throw noUIsAvailableException();
showUI(ui);
}
@Override
public void showUI(final String name) {
final UserInterface ui = uiMap().get(name);
if (ui == null) {
throw new IllegalArgumentException("No such user interface: " + name);
}
showUI(ui);
}
@Override
public void showUI(final UserInterface ui) {
log.debug("Launching user interface: " + ui.getClass().getName());
ui.show();
// NB: Also show all the current displays.
for (final Display> display : displayService.getDisplays()) {
ui.show(display);
}
eventService.publish(new UIShownEvent(ui));
}
@Override
public boolean isVisible() {
final UserInterface ui = getDefaultUI();
if (ui == null) throw noUIsAvailableException();
return ui.isVisible();
}
@Override
public boolean isVisible(final String name) {
final UserInterface ui = uiMap().get(name);
if (ui == null) {
throw new IllegalArgumentException("No such user interface: " + name);
}
return ui.isVisible();
}
@Override
public void setHeadless(final boolean headless) {
System.setProperty("java.awt.headless", String.valueOf(headless));
}
@Override
public boolean isHeadless() {
return Boolean.getBoolean("java.awt.headless");
}
@Override
public UserInterface getDefaultUI() {
if (!initialized) discoverUIs();
if (isHeadless()) return uiMap().get(HeadlessUI.NAME);
if (defaultUI != null) return defaultUI;
return uiList().isEmpty() ? null : uiList().get(0);
}
@Override
public void setDefaultUI(final UserInterface ui) {
defaultUI = ui;
}
@Override
public boolean isDefaultUI(final String name) {
return getDefaultUI() == getUI(name);
}
@Override
public UserInterface getUI(final String name) {
return uiMap().get(name);
}
@Override
public List getAvailableUIs() {
return Collections.unmodifiableList(uiList());
}
@Override
public List getVisibleUIs() {
final ArrayList uis = new ArrayList<>();
for (final UserInterface ui : uiList()) {
if (ui.isVisible()) uis.add(ui);
}
return uis;
}
@Override
public List>> getViewerPlugins() {
@SuppressWarnings({ "rawtypes", "unchecked" })
final List>> viewers =
(List) pluginService.getPluginsOfType(DisplayViewer.class);
return viewers;
}
@Override
public void show(final Object o) {
getDefaultUI().show(o);
}
@Override
public void show(final String name, final Object o) {
getDefaultUI().show(name, o);
}
@Override
public void show(final Display> display) {
getDefaultUI().show(display);
}
@Override
public void addDisplayViewer(final DisplayViewer> viewer) {
displayViewers().add(viewer);
}
@Override
public DisplayViewer> getDisplayViewer(final Display> display) {
for (final DisplayViewer> displayViewer : displayViewers()) {
if (displayViewer.getDisplay() == display) return displayViewer;
}
return null;
}
@Override
public DialogPrompt.Result showDialog(final String message) {
return showDialog(message, getTitle());
}
@Override
public Result showDialog(final String message, final MessageType messageType)
{
return showDialog(message, getTitle(), messageType);
}
@Override
public Result showDialog(final String message, final MessageType messageType,
final OptionType optionType)
{
return showDialog(message, getTitle(), messageType, optionType);
}
@Override
public DialogPrompt.Result
showDialog(final String message, final String title)
{
return showDialog(message, title,
DialogPrompt.MessageType.INFORMATION_MESSAGE);
}
@Override
public DialogPrompt.Result showDialog(final String message,
final String title, final DialogPrompt.MessageType messageType)
{
return showDialog(message, title, messageType,
DialogPrompt.OptionType.DEFAULT_OPTION);
}
@Override
public DialogPrompt.Result showDialog(final String message,
final String title, final DialogPrompt.MessageType messageType,
final DialogPrompt.OptionType optionType)
{
final UserInterface ui = getDefaultUI();
if (ui == null) return null;
final DialogPrompt dialogPrompt =
ui.dialogPrompt(message, title, messageType, optionType);
return dialogPrompt == null ? null : dialogPrompt.prompt();
}
@Override
public File chooseFile(final File file, final String style) {
final UserInterface ui = getDefaultUI();
return ui == null ? null : ui.chooseFile(file, style);
}
@Override
public File
chooseFile(final String title, final File file, final String style)
{
final UserInterface ui = getDefaultUI();
return ui == null ? null : ui.chooseFile(title, file, style);
}
@Override
public File[] chooseFiles(File parent, File[] files, FileFilter filter, String style) {
final UserInterface ui = getDefaultUI();
return ui == null ? null : ui.chooseFiles(parent, files, filter, style);
}
@Override
public List chooseFiles(File parent, List fileList, FileFilter filter, String style) {
final UserInterface ui = getDefaultUI();
return ui == null ? null : ui.chooseFiles(parent, fileList, filter, style);
}
@Override
public void showContextMenu(final String menuRoot, final Display> display,
final int x, final int y)
{
final UserInterface ui = getDefaultUI();
if (ui != null) ui.showContextMenu(menuRoot, display, x, y);
}
@Override
public String getStatusMessage(final StatusEvent statusEvent) {
final String appName = appService.getApp().getInfo().getName();
return statusService.getStatusMessage(appName, statusEvent);
}
// -- Disposable methods --
@Override
public synchronized void dispose() {
// dispose active display viewers
// NB - copy list to avoid ConcurrentModificationExceptions
final List> viewers = new ArrayList<>();
viewers.addAll(displayViewers());
for (final DisplayViewer> viewer : viewers) {
viewer.dispose();
}
// dispose UIs in reverse priority, "just in case" the order matters
final List uis = getAvailableUIs();
for (int i = uis.size() - 1; i >= 0; i--) {
uis.get(i).dispose();
}
disposed = true;
}
// -- Event handlers --
/**
* Called when a display is created. This is the magical place where the
* display model is connected with the real UI.
*/
@EventHandler
protected void onEvent(final DisplayCreatedEvent e) {
final Display> display = e.getObject();
for (final UserInterface ui : getVisibleUIs()) {
ui.show(display);
}
}
/**
* Called when a display is deleted. The display viewer is not removed from
* the list of viewers until after this returns.
*/
@EventHandler
protected void onEvent(final DisplayDeletedEvent e) {
final Display> display = e.getObject();
final DisplayViewer> displayViewer = getDisplayViewer(display);
if (displayViewer != null) {
displayViewer.onDisplayDeletedEvent(e);
displayViewers().remove(displayViewer);
}
}
/** Called when a display is updated. */
@EventHandler
protected void onEvent(final DisplayUpdatedEvent e) {
final Display> display = e.getDisplay();
final DisplayViewer> displayViewer = getDisplayViewer(display);
if (displayViewer != null) {
displayViewer.onDisplayUpdatedEvent(e);
}
}
/**
* Called when a display is activated.
*
* The goal here is to eventually synchronize the window activation state with
* the display activation state if the display activation state changed
* programmatically. We queue a call on the UI thread to activate the display
* viewer of the currently active window.
*
*/
@EventHandler
protected void onEvent(final DisplayActivatedEvent e) {
// CTR FIXME: Verify whether this threading logic is really necessary.
if (activationInvocationPending) return;
activationInvocationPending = true;
threadService.queue(new Runnable() {
@Override
public void run() {
final Display> activeDisplay = displayService.getActiveDisplay();
if (activeDisplay != null) {
final DisplayViewer> displayViewer =
getDisplayViewer(activeDisplay);
if (displayViewer != null) displayViewer.onDisplayActivatedEvent(e);
}
activationInvocationPending = false;
}
});
}
@EventHandler
protected void onEvent(@SuppressWarnings("unused") final AppQuitEvent event) {
for (final UserInterface ui : getVisibleUIs()) {
ui.saveLocation();
}
}
@EventHandler
protected void onEvent(final StatusEvent event) {
if (event.isWarning()) {
// report warning messages to the user in a dialog box
final String message = event.getStatusMessage();
if (message != null && !message.isEmpty()) {
showDialog(message, MessageType.WARNING_MESSAGE);
}
}
else {
// tell each UI to report status updates in the status bar
final int val = event.getProgressValue();
final int max = event.getProgressMaximum();
final String message = getStatusMessage(event);
if (max < 0 && message == null) return;
for (UserInterface ui : getAvailableUIs()) {
final StatusBar statusBar = ui.getStatusBar();
if (statusBar != null) {
if (max >= 0) {
statusBar.setProgress(val, max);
}
if (message != null) {
statusBar.setStatus(message);
}
}
}
}
}
// -- Helper methods --
private List> displayViewers() {
if (!initialized) discoverUIs();
return displayViewers;
}
private List uiList() {
if (!initialized) discoverUIs();
return uiList;
}
private Map uiMap() {
if (!initialized) discoverUIs();
return uiMap;
}
/** Discovers available user interfaces. */
private synchronized void discoverUIs() {
if (initialized) return;
displayViewers = new ArrayList<>();
uiList = new ArrayList<>();
uiMap = new HashMap<>();
final List> infos =
pluginService.getPluginsOfType(UserInterface.class);
for (final PluginInfo info : infos) {
// instantiate user interface
final UserInterface ui = pluginService.createInstance(info);
if (ui == null) continue;
log.debug("Discovered user interface: " + ui.getClass().getName());
addUserInterface(info.getName(), ui);
}
// check system property for explicit UI preference
final String uiProp = System.getProperty(UI_PROPERTY);
final UserInterface ui = uiMap.get(uiProp);
if (ui != null) {
// set the default UI to the one provided by the system property
setDefaultUI(ui);
}
initialized = true;
}
private void addUserInterface(final String name, final UserInterface ui) {
// add to UI list
uiList.add(ui);
// add to UI map
uiMap.put(ui.getClass().getName(), ui);
if (name != null && !name.isEmpty()) uiMap.put(name, ui);
}
private String getTitle() {
return appService.getApp().getTitle();
}
private IllegalStateException noUIsAvailableException() {
return new IllegalStateException("No UIs available. " +
"Please add a component containing a UIPlugin " +
"(e.g., scijava-ui-swing) to your class-path.");
}
}