package ie.dcu.apps.ist; import ie.dcu.apps.ist.actions.*; import ie.dcu.apps.ist.event.*; import ie.dcu.apps.ist.exp.Experiment; import ie.dcu.apps.ist.views.*; import ie.dcu.segment.*; import ie.dcu.segment.annotate.*; import ie.dcu.swt.*; import ie.dcu.swt.event.*; import ie.dcu.swt.layout.LayoutFactory; import ie.dcu.util.*; import java.io.*; import java.net.*; import java.util.Properties; import java.util.logging.*; import org.eclipse.jface.action.*; import org.eclipse.jface.resource.*; import org.eclipse.jface.window.ApplicationWindow; import org.eclipse.swt.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.*; /** * Main interactive segmentation tool application window. * * @author Kevin McGuinness */ public class AppWindow extends ApplicationWindow implements FileDropListener { private static final Logger log = Logger.getLogger("AppWindow"); public static Properties props; public static Properties curatornames; private AppPrefsManager prefsManager; private ActionManager actions; private Composite content; private SegmentationView view; private Shell shell; private ImageObserver observer; private Experiment experiment; private ExperimentPanel experimentPanel; private boolean experimentModeEmbedded; public AppWindow() { super(null); props = loadProperties(); curatornames =loadCuratorNames(); addMenuBar(); addStatusLine(); } @Override protected boolean canHandleShellCloseEvent() { if (!ExitAction.confirmExit(this)) { return false; } return super.canHandleShellCloseEvent(); } private Properties loadProperties() { try { return Application.loadProperties(getPropertiesFile()); } catch (IOException e) { log.severe("Unable to load properties file " + e.getMessage()); throw new RuntimeException(e); } } private Properties loadCuratorNames() { try { return Application.loadProperties(getCuratorNames()); } catch (IOException e) { log.severe("Unable to load properties file " + e.getMessage()); throw new RuntimeException(e); } } private String getPropertiesFile() { return OsUtils.isMacOS() ? "application.mac" : "application"; } private String getCuratorNames() { return "curator"; } public void updateWindowTitle() { String title; if (hasContext()) { String file = getContext().getFile().getName(); title = String.format("%s [%s]", Application.APP_NAME, file); } else { title = Application.APP_NAME; } if (isExperimentMode()) { title += " - Experiment Mode"; } getShell().setText(title); } public SegmentationView createView(Composite parent) { // Load props Properties props; try { props = Application.loadProperties("view"); } catch (IOException e) { log.severe("Error loading view properties " + e.getLocalizedMessage()); throw new RuntimeException(e); } // Create view SegmentationView view = new SegmentationView(props,curatornames, parent, 0); boolean blocksOnFork = false; // Set runnable context view.setRunnableContext(this, blocksOnFork); // Add observer observer = new ImageObserver(view); // Return new view return view; } /* public static SegmentationView resetView(SegmentationView view) { view.dispose(); return createView(content); } */ private static ImageDescriptor createImageDescriptor(String url) { try { return ImageDescriptor.createFromURL(new URL(url)); } catch (MalformedURLException e) { throw new RuntimeException(e); } } private void updateEnabledActions() { ActionManager a = getActions(); boolean ctxAvailable = hasContext(); boolean experimentMode = isExperimentMode(); a.setEnabled(OpenAction.class, !experimentMode); a.setEnabled(PreferencesAction.class, !experimentMode); a.setEnabled(SaveAction.class, ctxAvailable && !experimentMode); a.setEnabled(SaveAsAction.class, ctxAvailable && !experimentMode); a.setEnabled(ExportViewAction.class, ctxAvailable && !experimentMode); a.setEnabled(CloseAction.class, ctxAvailable && !experimentMode); a.setEnabled(ExportTransparentPNGAction.class, ctxAvailable && !experimentMode); a.setEnabled(PrintAction.class, ctxAvailable && !experimentMode); a.setEnabled(CopyAction.class, ctxAvailable && !experimentMode); a.setEnabled(UndoAction.class, ctxAvailable && view.canUndo()); a.setEnabled(RedoAction.class, ctxAvailable && view.canRedo()); a.setEnabled(NextAction.class, ctxAvailable && !experimentMode); a.setEnabled(PreviousAction.class, ctxAvailable && !experimentMode); a.setEnabled(OpenExperimentAction.class, !experimentMode); updateExportImageMapAction(); if (experimentMode) { boolean changeSelection = false; // Enable experiment segmenters for (SelectSegmenterAction s : a.getSegmentationActions()) { Segmenter segmenter = s.getSegmenter(); boolean enabled = experiment.using(segmenter); s.setEnabled(enabled); if (!s.isEnabled() && s.isChecked()) { // The selected segmenter has been disabled, // we need to select a different one changeSelection = true; s.setChecked(false); } } if (changeSelection) { // Select the first available segmenter for (SelectSegmenterAction s : a.getSegmentationActions()) { if (experiment.using(s.getSegmenter())) { s.setChecked(true); s.run(); break; } } } } else { // Enable all select segmenter actions for (SelectSegmenterAction s : a.getSegmentationActions()) { s.setEnabled(true); } } } private void updateExportImageMapAction() { ActionManager a = getActions(); SegmentationContext ctx = getContext(); if (ctx != null && !isExperimentMode()) { a.setEnabled(ExportImageMapAction.class, ctx.hasSegmentationMasks()); } else { a.setEnabled(ExportImageMapAction.class, false); } } private void updateUndoRedoActions() { ActionManager a = getActions(); boolean ctxAvailable = hasContext(); a.setEnabled(UndoAction.class, ctxAvailable && view.canUndo()); a.setEnabled(RedoAction.class, ctxAvailable && view.canRedo()); } @Override protected void configureShell(Shell shell) { this.shell = shell; super.configureShell(shell); updateWindowTitle(); loadIcons(shell); shell.setSize(1280, 768); SwtUtils.center(shell); } private void loadIcons(Shell shell) { final String[] icons = { "resources/icons/icon-16.png", "resources/icons/icon-24.png", "resources/icons/icon-32.png", "resources/icons/icon-48.png" }; Image[] images = new Image[icons.length]; Display display = shell.getDisplay(); try { int i = 0; for (String file : icons) { images[i++] = new Image(display, file); } shell.setImages(images); } catch (SWTException e) { log.warning("Error loading application window icons: " + e.getLocalizedMessage()); } } @Override protected Control createContents(Composite parent) { content = new Composite(parent, SWT.BORDER); content.setLayout(LayoutFactory.createGridLayout(0, 0, 1, false)); view = createView(content); view.setLayoutData(LayoutFactory.createGridData()); view.addContextChangeListener(contextListener); SwtUtils.createFileDropTarget(view, this); // Done creating contents getPrefsManager().update(); updateEnabledActions(); return content; } @Override protected boolean showTopSeperator() { return false; } @Override protected MenuManager createMenuManager() { MenuManager bar = new MenuManager(); ActionManager actions = getActions(); MenuManager file = addMenu(bar, "&File"); file.add(actions.get(OpenAction.class)); file.add(new AppRecentMenu(this, file)); file.add(new Separator()); file.add(actions.get(SaveAction.class)); //file.add(actions.get(SaveAsAction.class)); file.add(new Separator()); // Export Menu is hidden so as not to confuse the user. Instead this functionality is provided as Save // Export menu -> /*MenuManager exportMenu = addMenu(file, "&Export", props.getProperty("ExportMenu.icon")); exportMenu.add(actions.get(ExportViewAction.class)); exportMenu.add(actions.get(ExportImageMapAction.class)); exportMenu.add(actions.get(ExportTransparentPNGAction.class)); file.add(new Separator()); file.add(actions.get(PrintAction.class)); file.add(new Separator());*/ file.add(actions.get(CloseAction.class)); file.add(actions.get(ExitAction.class)); MenuManager edit = addMenu(bar, "&Edit"); edit.add(actions.get(UndoAction.class)); edit.add(actions.get(RedoAction.class)); edit.add(new Separator()); edit.add(actions.get(CopyAction.class)); edit.add(new Separator()); edit.add(actions.get(PreferencesAction.class)); /* MenuManager tools = addMenu(bar, "&Tools"); tools.add(actions.get(OpenExperimentAction.class)); tools.add(new Separator()); for (SelectSegmenterAction a : actions.getSegmentationActions()) { tools.add(a); } tools.add(new Separator()); tools.add(actions.get(ConfigureSegmenterAction.class)); MenuManager go = addMenu(bar, "&Go"); go.add(actions.get(NextAction.class)); go.add(actions.get(PreviousAction.class)); */ MenuManager help = addMenu(bar, "&Help"); help.add(actions.get(HelpAction.class)); help.add(actions.get(AboutAction.class)); if (OsUtils.isMacOSX()) { // Enhance the UI on the Mac IAction aboutAction = actions.get(AboutAction.class); IAction prefAction = actions.get(PreferencesAction.class); Listener quitListener = new Listener() { public void handleEvent(Event evt) { getActions().get(ExitAction.class).runWithEvent(evt); } }; CocoaUIEnhancer enhancer = new CocoaUIEnhancer(Application.APP_NAME); enhancer.hookApplicationMenu(Display.getDefault(), quitListener, aboutAction, prefAction); } return bar; } protected static MenuManager addMenu(MenuManager parent, String text) { MenuManager menu = new HoverMenuManager(text); parent.add(menu); return menu; } protected static MenuManager addMenu(MenuManager parent, String text, String image) { MenuManager menu; try { menu = new HoverMenuManager(text, image != null ? new URL(image) : null); } catch (MalformedURLException e) { log.log(Level.WARNING, "Malformed URL", e); menu = new HoverMenuManager(text); } parent.add(menu); return menu; } public void setExperiment(Experiment ex) { if (experiment != ex) { experiment = ex; setContext(null); if (ex == null) { if (experimentPanel != null) { experimentPanel.dispose(); experimentPanel = null; content.layout(); } } else { if (experimentModeEmbedded) { experimentPanel = new ExperimentPanel(this, content, SWT.NONE); GridData data = new GridData(SWT.FILL, SWT.FILL, false, true); data.widthHint = 180; data.verticalIndent = 5; experimentPanel.setLayoutData(data); content.layout(); } else { ExperimentPanel.open(this); } } updateEnabledActions(); updateWindowTitle(); } } public Experiment getExperiment() { return experiment; } public boolean isExperimentMode() { return experiment != null; } public void setExperimentModeEmbedded(boolean on) { this.experimentModeEmbedded = on; } public AppPrefs getPrefs() { return getPrefsManager().getPrefs(); } AppPrefsManager getPrefsManager() { if (prefsManager == null) { prefsManager = new AppPrefsManager(this); } return prefsManager; } public Properties getProperties() { return props; } public ActionManager getActions() { if (actions == null) { actions = new ActionManager(this); } return actions; } public Shell getShell() { Shell shell = super.getShell(); if (shell == null) { return this.shell; } return shell; } public SegmentationView getView() { return view; } public SegmentationContext getContext() { if (view != null) { return view.getContext(); } return null; } public File getContextFile() { if (hasContextFile()) { return getContext().getFile(); } return null; } public Image getIcon(String url) { Image image = JFaceResources.getImage(url); if (image == null) { ImageRegistry registry = JFaceResources.getImageRegistry(); ImageDescriptor descriptor = createImageDescriptor(url); registry.put(url, descriptor); image = registry.get(url); } return image; } public boolean hasContext() { if (view != null) { return view.getContext() != null; } return false; } public boolean hasContextFile() { return getContext() != null ? getContext().hasContextFile() : false; } public void setEnableFeedback(boolean on) { if (observer != null) { observer.setEnabled(on); } } public void setContext(SegmentationContext ctx) { if (ctx != null) { // Set a segmenter if one has not been set if (!view.hasSegmenter()) { SegmenterRegistry registry = SegmenterRegistry.getInstance(); view.setSegmenter(registry.getDefault()); } } // Set context view.setContext(ctx); updateWindowTitle(); } public void status(String message) { setStatus(message); } public void status(String format, Object... args) { setStatus(String.format(format, args)); } public void status(Image image, String format, Object... args) { StatusLineManager manager = getStatusLineManager(); if (manager != null) { manager.setMessage(image, String.format(format, args)); } } public void status(AppStatus s, String format, Object... args) { if (format == null) { setStatus(null); } else { StatusLineManager manager = getStatusLineManager(); if (manager != null) { if (s == AppStatus.Error) { manager.setErrorMessage(s.getIcon(), String.format(format, args)); } else { manager.setMessage(s.getIcon(), String.format(format, args)); } } } } public void drop(FileDropEvent evt) { System.out.println("drop received"); for (File f : evt.files()) { if (isAcceptable(f)) { getActions().get(OpenAction.class).open(f); break; } } } public boolean isAcceptable(File file) { String ext = FileUtils.getExtension(file); return (ext.equals(".ctx") || SwtUtils.getImageFormat(file) != -1); } private void handleContextChanged(ContextChangedEvent evt) { // Remove listener from old context if (evt.oldContext != null) { evt.oldContext.removeAnnotationListener(annotationListener); } // Add listener to new context if (evt.newContext != null) { evt.newContext.addAnnotationListener(annotationListener); } // Update application state updateEnabledActions(); } private void handleAnnotationPerformed(AnnotationEvent e) { // Update state of undo and redo menu items updateUndoRedoActions(); updateExportImageMapAction(); } private final ContextChangeListener contextListener = new ContextChangeListener() { public void contextChanged(ContextChangedEvent evt) { handleContextChanged(evt); } }; private final AnnotationListener annotationListener = new AnnotationAdapter() { public void annotationsChanged(AnnotationEvent e) { handleAnnotationPerformed(e); } }; private class ImageObserver implements MouseMoveListener, ContextChangeListener { private static final String MONOSPACE_FONT = "Monospace"; private final ImageControl ctrl; private ImageData image; private boolean inside; private boolean enabled; public ImageObserver(SegmentationView view) { view.addContextChangeListener(this); view.getCanvas().addMouseMoveListener(this); ctrl = view.getImageControl(); contextChanged(view.getContext()); FontDescriptor fd = FontDescriptor.createFrom(MONOSPACE_FONT, 8, SWT.NORMAL); JFaceResources.getFontRegistry().put(MONOSPACE_FONT, fd.getFontData()); } public void setEnabled(boolean enabled) { if (this.enabled != enabled) { status(null); Font font = JFaceResources.getDefaultFont(); getStatusLineManager().getControl().setFont(font); this.inside = false; this.enabled = enabled; } } public boolean isEnabled() { return enabled; } public void mouseMove(MouseEvent e) { if (isEnabled()) { Point pt = getImagePoint(e); if (pt != null) { inside(pt, getColor(pt)); } else { outside(); } } } private void outside() { if (inside) { status(null); Font font = JFaceResources.getDefaultFont(); getStatusLineManager().getControl().setFont(font); inside = false; } } private final Point getImagePoint(MouseEvent e) { if (image != null) { Point pt = new Point(e.x, e.y); if (ctrl.imageContains(pt)) { return ctrl.canvasToImage(pt); } } return null; } private final RGB getColor(Point pt) { int pixel = image.getPixel(pt.x, pt.y); return image.palette.getRGB(pixel); } private void inside(Point pt, RGB c) { if (!inside) { Font font = JFaceResources.getFont(MONOSPACE_FONT); getStatusLineManager().getControl().setFont(font); } if (inside) { status(AppStatus.Information, "Location [%4d,%4d] Color [%3d,%3d,%3d]",pt.x, pt.y, c.red, c.green, c.blue); if(view.getContext().isEnabled()) { if(view.getContext().getEnabledMask().getImageData().getPixel(pt.x,pt.y) != 0 && (view.getPainter().getName().equals("Segmented")) && SegmentationView.getLabelMode()) { String segmentName = view.getContext().getEnabledMask().ontologyTerm.getName(); view.getCanvas().setToolTipText(segmentName); SegmentationView.comboLabel.setText(segmentName); return; } else { view.getCanvas().setToolTipText(null); } } } inside = true; } public void contextChanged(ContextChangedEvent evt) { contextChanged(evt.newContext); } public void contextChanged(SegmentationContext ctx) { if (ctx != null) { image = ctx.getImageData(); } else { image = null; } } /* * clear all image and metadata */ public void disposeAll() { } } }