/** * */ package cz.cuni.mff.peckam.java.origamist.gui.viewer; import java.applet.AppletContext; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Arrays; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ResourceBundle; import java.util.concurrent.Callable; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRootPane; import javax.swing.JScrollPane; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.origamist.BackgroundImageSupport; import javax.swing.origamist.BackgroundImageSupport.BackgroundRepeat; import javax.swing.origamist.JDropDownButton; import javax.swing.origamist.JHideablePanel; import javax.swing.origamist.JToolBarWithBgImage; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import javax.xml.bind.JAXBException; import org.apache.log4j.Level; import org.apache.log4j.Logger; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.forms.util.DefaultUnitConverter; import cz.cuni.mff.peckam.java.origamist.exceptions.UnsupportedDataFormatException; import cz.cuni.mff.peckam.java.origamist.files.Listing; import cz.cuni.mff.peckam.java.origamist.files.ObjectFactory; import cz.cuni.mff.peckam.java.origamist.gui.common.CommonGui; import cz.cuni.mff.peckam.java.origamist.gui.common.DiagramRenderer; import cz.cuni.mff.peckam.java.origamist.gui.common.Java3DBootstrappingApplet; import cz.cuni.mff.peckam.java.origamist.gui.viewer.listing.ListingTree; import cz.cuni.mff.peckam.java.origamist.gui.viewer.listing.ListingTreeSelectionListener; import cz.cuni.mff.peckam.java.origamist.model.Origami; import cz.cuni.mff.peckam.java.origamist.services.ServiceLocator; import cz.cuni.mff.peckam.java.origamist.services.StepThumbnailGenerator; import cz.cuni.mff.peckam.java.origamist.services.TooltipFactory; import cz.cuni.mff.peckam.java.origamist.services.interfaces.ConfigurationManager; import cz.cuni.mff.peckam.java.origamist.services.interfaces.ListingHandler; import cz.cuni.mff.peckam.java.origamist.services.interfaces.OrigamiHandler; import cz.cuni.mff.peckam.java.origamist.utils.ExportFormat; /** * The viewer of the origami model. <br /> * <br /> * The applet recognizes these parameters:<br /> * * <code>files</code> (<b>required</b>): * <ul> * <li>either a string containing either a location of <code>listing.xml</code>, or</li> * <li>a list of files and directories.</li> * </ul> * <code>recursive</code>: Effective only if <code>files</code> contain some directories. * <ul> * <li>If not set, no files from subdirectories will be loaded (but direct children of the specified folders will be * loaded).</li> * <li>If set to <code>recursive</code>, then all subdirectories will be searched for models.</li> * <li>If set to a number, the number means the depth od subdirectories to search in. * <em>Please notice, that the value <code>0</code> means that only the specified files will be loaded and the specified directories will be ignored!</em> * </li> * </ul> * <code>displayMode</code>: * <ul> * <li>either <code>page</code> (displays a bunch of steps at once) or</li> * <li><code>diagram</code> (displays only one step at once). Defaults to <code>page</code>.</li> * <li>when not set, defaults to <code>page</code></li> * </ul> * <code>modelDownloadMode</code>: * <ul> * <li>if set to <code>all</code>, all contents of all models defined in <code>files</code> will be downloaded at * startup.</li> * <li>If set to <code>headers</code>, only metadata for each model will be downloaded immediately and the models * themselves will be downloaded on demand.</li> * <li>If set to <code>none</code> , no files will be downloaded at startup and everything will be downloaded on-demand * (this means the metadata will not be available at startup).</li> * <li>If set to a number <code>n</code>, mode <code>all</code> will be used for first <code>n</code> files and mode * <code>none</code> will be used for the rest of them.</li> * <li>if not set, defaults to <code>10</code></li> * </ul> * * Properties handled by this class (events are fired when they change - but not at startup): * <ul> * <li><code>showFileListing</code></li> * <li><code>displayMode</code></li> * <li><code>displayedOrigami</code></li> * </ul> * * This applet is intended to be started using a bootstrapper that will add support for Java3D without the need to * install it on the client computer. * * @author Martin Pecka */ public class OrigamiViewer extends CommonGui { private static final long serialVersionUID = -6853141518719373854L; /** Download whole models for all selected files. */ public static int MODEL_DOWNLOAD_MODE_ALL = -2; /** Don't download models. They'll be downloaded on demand (metadata will not be accessible). */ public static int MODEL_DOWNLOAD_MODE_HEADERS = -1; /** Download only metadata for all models. */ public static int MODEL_DOWNLOAD_MODE_NONE = 0; /** * Meaning of the values is listed below. The values only have effect when a whole directory or a set of files is * specified as the list of files to show. * * MODEL_DOWNLOAD_MODE_ALL - Download whole models for all selected files. * MODEL_DOWNLOAD_MODE_NONE - Don't download models. They'll be downloaded on demand (metadata will not be * accessible). * MODEL_DOWNLOAD_MODE_HEADERS - Download only metadata for all models. * other positive number <code>n</code> - use the ALL mode for the first <code>n</code> files and the NONE mode * for all other files. */ protected int modelDownloadMode = 10; /** Name of the file containing the listing of files to display. */ public static String LISTING_FILE_NAME = "listing.xml"; /** The files to be displayed by the viewer. */ protected Listing filesToDisplay = null; /** The mode of displaying diagrams. Do not access directly, use setter/getter. */ protected DisplayMode displayMode = DisplayMode.PAGE; /** The origami currently displayed. Do not access directly, use setter/getter. */ protected Origami displayedOrigami = null; /** The list of files that were recognized at startup. */ protected ListingTree fileListing = null; /** The scroll pane containing the file listing. */ protected JScrollPane fileListingScrollPane = null; /** If true, show the sidebar with open files. Do not access directly, use setter/getter. */ protected boolean showFileListing = false; /** The renderer used to render the diagrams. */ protected DiagramRenderer renderer = null; /** The panel that shows info about the model. */ protected ModelInfoPanel modelInfo = null; /** The (hideable) panel that holds listing and model info. */ protected JHideablePanel listingPanel = null; /** The main menu component. */ protected JComponent mainToolbar = null; /** The panel containing the listing and the diagram renderer. */ protected JPanel mainPanel = null; /** Toolbar buttons. */ protected JButton diagramPrev = null, diagramNext = null; /** The bootstrapper that has started this applet, or <code>null</code>, if it has not been bootstrapped. */ protected JApplet bootstrap = null; /** The if the init() method has already finished. */ protected boolean guiInitialized = false; /** * Instantiate the origami viewer without a bootstrapper. */ public OrigamiViewer() { this(null); } /** * Instanitate the origami viewer with the given bootstrapper. * * @param bootstrap The bootstrapper that starts this applet. */ public OrigamiViewer(JApplet bootstrap) { super(); this.bootstrap = bootstrap; UIManager.getDefaults().addResourceBundle("viewer"); } @Override public void init() { addGlobalKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_F10 && mainToolbar.getComponentCount() > 0) { mainToolbar.getComponent(0).requestFocusInWindow(); e.consume(); } } }); super.init(); guiInitialized = true; } /** * Create and setup all the form components. */ @Override protected void createComponents() { fileListing = new ListingTree(); fileListing.setExpandsSelectedPaths(true); fileListingScrollPane = new JScrollPane(fileListing); fileListingScrollPane.setPreferredSize(new Dimension(DefaultUnitConverter.getInstance().dialogUnitXAsPixel(170, fileListing), 0)); modelInfo = new ModelInfoPanel(null, isShowFileListing() ? BorderLayout.NORTH : BorderLayout.EAST); addPropertyChangeListener("displayedOrigami", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { modelInfo.setOrigami(displayedOrigami); } }); } }); listingPanel = new JHideablePanel(BorderLayout.EAST); listingPanel.getHideButton().setBackground(new Color(231, 231, 189)); listingPanel.getHideButton().setBorder(BorderFactory.createEmptyBorder()); mainToolbar = createMainToolbar(); mainPanel = new JPanel(); getTopmostComponent().setMinimumSize(new Dimension(650, 500)); renderer = new DiagramRenderer(); renderer.setPreferredSize(new Dimension(500, 500)); try { handleAppletParams(); configureComponentsFromFilesToDisplay(); if (displayedOrigami != null) renderer.setOrigami(displayedOrigami, displayedOrigami.getModel().getSteps().getStep().get(0)); } catch (UnsupportedDataFormatException e) { Logger.getLogger("viewer").error( appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), e); JOptionPane.showMessageDialog(this, appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), appMessages.getString("exception.UnsupportedDataFormatException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IOException e) { Logger.getLogger("viewer").error(appMessages.getString("exception.IOException.loadModel"), e); JOptionPane.showMessageDialog(this, appMessages.getString("exception.IOException.loadModel"), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IllegalArgumentException e) { Logger.getLogger("viewer").error(e.getMessage(), e); JOptionPane.showMessageDialog(this, e.getMessage(), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } finally { // we intentionally add the selection listener just after the first selection has been performed fileListing.addTreeSelectionListener(new ListingTreeSelectionListener()); addPropertyChangeListener("displayMode", new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { renderer.setDisplayMode((DisplayMode) evt.getNewValue()); } }); } }); // it could look like there could be a cycle when changing display mode, but that's not true - the listeners // are notified only if the value really changes, so setting it twice to the same value doesn't fire the // event renderer.addPropertyChangeListener("mode", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { setDisplayMode(renderer.getDisplayMode()); } }); } }); } } /** * Setup the form layout. */ @Override protected void buildLayout() { Container pane = getContentPane(); // the "0:grow" items in the layout specification says that the preferred size of the component is the maximum // available space and no minimum constraints are applied pane.setLayout(new FormLayout("fill:0:grow", "default,fill:default:grow")); CellConstraints cc = new CellConstraints(); pane.add(mainToolbar, cc.xy(1, 1)); mainPanel.setLayout(new FormLayout("min(default;200dlu),0:grow", "fill:0:grow")); pane.add(mainPanel, cc.xy(1, 2)); relayoutComponents(); mainPanel.add(renderer, cc.xy(2, 1)); } protected void relayoutComponents() { CellConstraints cc = new CellConstraints(); if (isShowFileListing()) { listingPanel.getContent().setLayout(new BorderLayout()); listingPanel.getContent().add(fileListingScrollPane, BorderLayout.CENTER); listingPanel.getContent().add(modelInfo, BorderLayout.PAGE_END); mainPanel.add(listingPanel, cc.xy(1, 1)); } else { mainPanel.add(modelInfo, cc.xy(1, 1)); } } /** * Handles the applet parameters. Eg. loads listing.xml, or sets other settings. * * @throws IllegalArgumentException If an argument has bad value and the continuation of the app is impossible due * to it. */ protected void handleAppletParams() throws IllegalArgumentException { handleStartupModeParam(); handleModelDownloadModeParam(); try { handleFilesParam(); // the "recursive" param is handled by the previous handler } catch (IllegalArgumentException e) { Logger.getLogger("viewer").warn(e); } } /** * Handles the "displayMode" applet parameter. */ protected void handleStartupModeParam() { if (getParameter("displayMode") != null) { try { displayMode = DisplayMode.valueOf(getParameter("displayMode").toUpperCase()); } catch (IllegalArgumentException e) { Logger.getLogger("viewer").l7dlog(Level.ERROR, "startupModeParamInvalid", new Object[] { Arrays.asList(DisplayMode.values()) }, e); // we just do nothing, so the default value remains set } } } /** * Handles the "modelDownloadMode" applet parameter. */ protected void handleModelDownloadModeParam() { String param = getParameter("modelDownloadMode"); if (param != null) { try { modelDownloadMode = Integer.parseInt(param); if (modelDownloadMode < 0) { modelDownloadMode = MODEL_DOWNLOAD_MODE_NONE; throw new NumberFormatException(); } } catch (NumberFormatException e) { if (param.equalsIgnoreCase("all")) modelDownloadMode = MODEL_DOWNLOAD_MODE_ALL; else if (param.equalsIgnoreCase("headers")) modelDownloadMode = MODEL_DOWNLOAD_MODE_HEADERS; else if (param.equalsIgnoreCase("none")) { modelDownloadMode = MODEL_DOWNLOAD_MODE_NONE; } else { Logger.getLogger("viewer").l7dlog(Level.ERROR, "modelDownloadModeParamInvalid", e); // we just do nothing, so the default value remains set } } } } /** * Handles the "files" applet parameter. (Also handles the "recursive" parameter, where applicable) * * @throws IllegalArgumentException If the loaded listing would be empty or listing.xml could not be loaded. */ protected void handleFilesParam() throws IllegalArgumentException { Integer recursive; String recursiveParam = getParameter("recursive"); if (recursiveParam == null) { recursive = 1; } else if (recursiveParam.equalsIgnoreCase("recursive")) { recursive = null; } else { try { recursive = Integer.parseInt(recursiveParam); } catch (NumberFormatException e) { recursive = 1; } } setFilesToDisplay(getParameter("files"), recursive); } /** * Recursively loads a listing from the given files. * * @param filesParam The files to be loaded, space separated URIs. * @throws IllegalArgumentException If the loaded listing would be empty or listing.xml could not be loaded. */ protected void setFilesToDisplay(String filesParam) throws IllegalArgumentException { setFilesToDisplay(filesParam, null); } /** * Loads a listing from the given files, using the given recursive parameter. * * @param filesParam The files to be loaded, space separated URIs. * @param recursive The recursive parameter defining how to treat directories. * @throws IllegalArgumentException If the loaded listing would be empty or listing.xml could not be loaded. */ protected void setFilesToDisplay(String filesParam, Integer recursive) throws IllegalArgumentException { String param = filesParam; if (param == null) { Logger.getLogger("viewer").l7dlog(Level.ERROR, "filesParamMissing", new IllegalArgumentException("The 'files' applet parameter must be set")); JOptionPane.showMessageDialog(this, Logger.getLogger("viewer").getResourceBundle().getString("filesParamMissing"), "Error", JOptionPane.ERROR_MESSAGE); return; } if (param.endsWith(LISTING_FILE_NAME)) { if (!guiInitialized) showFileListing = true; else setShowFileListing(true); try { URL paramURL = new URL(getDocumentBase(), param); // use the listing.xml location as the base for relative model URLs ServiceLocator.get(OrigamiHandler.class).setDocumentBase(paramURL); filesToDisplay = ServiceLocator.get(ListingHandler.class).load(paramURL); } catch (MalformedURLException e) { ResourceBundle messages = ResourceBundle.getBundle("viewer", ServiceLocator.get(ConfigurationManager.class).get().getLocale()); throw new IllegalArgumentException(messages.getString("filesParamInvalidListingURL"), e); } catch (UnsupportedDataFormatException e) { ResourceBundle messages = ResourceBundle.getBundle("viewer", ServiceLocator.get(ConfigurationManager.class).get().getLocale()); throw new IllegalArgumentException(messages.getString("filesParamInvalidListingFormat"), e); } catch (IOException e) { ResourceBundle messages = ResourceBundle.getBundle("viewer", ServiceLocator.get(ConfigurationManager.class).get().getLocale()); throw new IllegalArgumentException(messages.getString("filesParamInvalidListingUnread"), e); } } else { List<String> filesAsStrings = Arrays.asList(param.split(" ")); ObjectFactory of = new ObjectFactory(); filesToDisplay = (Listing) of.createListing(); List<URI> files = new LinkedList<URI>(); for (String fileString : filesAsStrings) { try { URI file = new URL(getDocumentBase(), fileString).toURI(); files.add(file); } catch (MalformedURLException e) { Logger.getLogger("viewer").l7dlog(Level.ERROR, "filesParamInvalidItem", new Object[] { fileString }, e); } catch (URISyntaxException e) { Logger.getLogger("viewer").l7dlog(Level.ERROR, "filesParamInvalidItem", new Object[] { fileString }, e); } } filesToDisplay.addFiles(files, recursive, filesToDisplay); if ((filesToDisplay.getFiles() == null || filesToDisplay.getFiles().getFile().size() == 0) && (filesToDisplay.getCategories() == null || (filesToDisplay.getCategories()) .numOfFilesRecursive() == 0)) { Logger.getLogger("viewer").l7dlog(Level.ERROR, "filesParamInvalidFileList", null, new IllegalArgumentException("No input files defined.")); JOptionPane.showMessageDialog(this, Logger.getLogger("viewer").getResourceBundle().getString("filesParamInvalidFileList"), "Error", JOptionPane.ERROR_MESSAGE); } if (modelDownloadMode == MODEL_DOWNLOAD_MODE_HEADERS || modelDownloadMode == MODEL_DOWNLOAD_MODE_ALL || modelDownloadMode > 0) { Iterator<cz.cuni.mff.peckam.java.origamist.files.File> iterator = filesToDisplay .recursiveFileIterator(); boolean onlyMetadata = modelDownloadMode == MODEL_DOWNLOAD_MODE_HEADERS; int i = 0; while (iterator.hasNext() && (modelDownloadMode == MODEL_DOWNLOAD_MODE_ALL || modelDownloadMode == MODEL_DOWNLOAD_MODE_HEADERS || i++ < modelDownloadMode)) { cz.cuni.mff.peckam.java.origamist.files.File file = iterator.next(); try { file.getOrigami(onlyMetadata, false); file.fillFromOrigami(); continue; } catch (UnsupportedDataFormatException e) { Logger.getLogger("viewer").l7dlog(Level.ERROR, "invalidModelFile", new Object[] { file.getSrc() }, null); } catch (IOException e) { Logger.getLogger("viewer").l7dlog(Level.ERROR, "modelLoadIOError", new Object[] { file.getSrc() }, null); } i--; iterator.remove(); file.setParent(null); } iterator = null; } // we only show the file listing if two or more models are being displayed Iterator<cz.cuni.mff.peckam.java.origamist.files.File> it = filesToDisplay.recursiveFileIterator(); int numOfModels = 0; if (it != null) { while (it.hasNext()) { it.next(); numOfModels++; if (numOfModels == 2) break; } } it = null; // we must access this property directly, because we don't want to fire events before the GUI is properly // setup. if (!guiInitialized) showFileListing = numOfModels >= 2; else setShowFileListing(numOfModels >= 2); if (numOfModels == 0) { ResourceBundle messages = ResourceBundle.getBundle("viewer", ServiceLocator.get(ConfigurationManager.class).get().getLocale()); throw new IllegalArgumentException(messages.getString("filesParamInvalidFileList")); } } } /** * Recursively loads a listing from the given files. * * @param filesParam The files to be loaded, space separated URIs. * * @throws IllegalArgumentException If the loaded listing would be empty or listing.xml could not be loaded. * @throws UnsupportedDataFormatException If the first model in listing has errors. * @throws IOException If the first model in listing has errors. */ protected void updateFilesToDisplay(String filesParam) throws IllegalArgumentException, UnsupportedDataFormatException, IOException { updateFilesToDisplay(filesParam, null); } /** * Loads a listing from the given files, using the given recursive parameter. * * @param filesParam The files to be loaded, space separated URIs. * @param recursive The recursive parameter defining how to treat directories. * * @throws IllegalArgumentException If the loaded listing would be empty or listing.xml could not be loaded. * @throws UnsupportedDataFormatException If the first model in listing has errors. * @throws IOException If the first model in listing has errors. */ protected void updateFilesToDisplay(String filesParam, Integer recursive) throws IllegalArgumentException, UnsupportedDataFormatException, IOException { boolean isListingShown = isShowFileListing(); try { setFilesToDisplay(filesParam, recursive); configureComponentsFromFilesToDisplay(); } finally { if (isListingShown != isShowFileListing()) { mainPanel.remove(listingPanel); mainPanel.remove(modelInfo); relayoutComponents(); } } } /** * Configure fileListing and modelInfo to reflect the current value of filesToDisplay. Also set the displayed * origami to the first one found. * * @throws UnsupportedDataFormatException If the first model in listing has errors. * @throws IOException If the first model in listing has errors. */ protected void configureComponentsFromFilesToDisplay() throws UnsupportedDataFormatException, IOException { fileListing.setListing(filesToDisplay); fileListing.setVisible(isShowFileListing()); DefaultMutableTreeNode root = (DefaultMutableTreeNode) fileListing.getModel().getRoot(); DefaultMutableTreeNode toSelect = null; boolean wasFound = false; if (root != null) { @SuppressWarnings("rawtypes") Enumeration bfNodeEnum = root.breadthFirstEnumeration(); while ((toSelect = (DefaultMutableTreeNode) bfNodeEnum.nextElement()) != null) { if (toSelect.getUserObject() instanceof cz.cuni.mff.peckam.java.origamist.files.File) { wasFound = true; break; } } } if (toSelect != null && wasFound) fileListing.setSelectionPath(new TreePath(toSelect.getPath())); modelInfo.setButtonAlignment(isShowFileListing() ? BorderLayout.NORTH : BorderLayout.EAST); diagramNext.setVisible(isShowFileListing()); diagramPrev.setVisible(isShowFileListing()); if (toSelect != null && wasFound) { if (!guiInitialized) { displayedOrigami = ((cz.cuni.mff.peckam.java.origamist.files.File) toSelect.getUserObject()) .getOrigami(); } else { setDisplayedOrigami(((cz.cuni.mff.peckam.java.origamist.files.File) toSelect.getUserObject()) .getOrigami()); } } else { if (!guiInitialized) { // if the iterator should be empty, then configureComponentsFromFilesToDisplay will die with an // exception displayedOrigami = filesToDisplay == null ? null : filesToDisplay.recursiveFileIterator().next() .getOrigami(); if (filesToDisplay == null) modelInfo.hidePanel(); } else { if (filesToDisplay != null) { setDisplayedOrigami(filesToDisplay.recursiveFileIterator().next().getOrigami()); } else { modelInfo.hidePanel(); } } } modelInfo.setOrigami(displayedOrigami); } /** * Return the component that is to be used as the main menu. * * Also register all provided accelerators. Currently implemented by a JToolbar. * * @return The component that is to be used as the main menu. */ protected JComponent createMainToolbar() { JToolBarWithBgImage toolbar = new JToolBarWithBgImage("viewer"); toolbar.setFloatable(false); toolbar.setBackground(new Color(231, 231, 184, 230)); toolbar.setBackgroundImage(new BackgroundImageSupport(getClass() .getResource("/resources/images/tooltip-bg.png"), toolbar, 0, 0, BackgroundRepeat.REPEAT_X)); JDropDownButton dropDownOpen = toolbar.createToolbarDropdownButton(null, "menu.open", "open.png"); toolbar.add(dropDownOpen); dropDownOpen.addComponent(toolbar.createToolbarDropdownSeparator("menu.separator.files")); dropDownOpen .addComponent(toolbar.createToolbarDropdownItem(new OpenFileAction(), "menu.open.file", "file.png")); dropDownOpen.addComponent(toolbar.createToolbarDropdownItem(new OpenDirAction(), "menu.open.dir", "directory.png")); dropDownOpen.addComponent(toolbar .createToolbarDropdownItem(new OpenURLAction(), "menu.open.url", "urlFile.png")); dropDownOpen.addComponent(toolbar.createToolbarDropdownSeparator("menu.separator.listings")); dropDownOpen.addComponent(toolbar.createToolbarDropdownItem(new OpenLocalListingAction(), "menu.open.local.listing", "listingFile.png")); dropDownOpen.addComponent(toolbar.createToolbarDropdownItem(new OpenURLAction(), "menu.open.url.listing", "listingUrl.png")); toolbar.add(new JToolBar.Separator()); JDropDownButton dropDownSave = toolbar.createToolbarDropdownButton(null, "menu.save", "save.png"); toolbar.add(dropDownSave); dropDownSave.addComponent(toolbar.createToolbarDropdownSeparator("menu.separator.editable")); dropDownSave.addComponent(toolbar.createToolbarDropdownItem(new ExportAction(ExportFormat.XML), "menu.save.asXML", "xml.png")); dropDownSave.addComponent(toolbar.createToolbarDropdownItem(new ExportAction(ExportFormat.SVG), "menu.save.asSVG", "svg.png")); dropDownSave.addComponent(toolbar.createToolbarDropdownSeparator("menu.separator.non-editable")); dropDownSave.addComponent(toolbar.createToolbarDropdownItem(new ExportAction(ExportFormat.PDF), "menu.save.asPDF", "pdf.png")); dropDownSave.addComponent(toolbar.createToolbarDropdownItem(new ExportAction(ExportFormat.PNG), "menu.save.asPNG", "png.png")); dropDownSave.addComponent(toolbar.createToolbarDropdownSeparator("menu.separator.animation")); dropDownSave.addComponent(toolbar.createToolbarDropdownItem(new ExportAction(ExportFormat.MOV), "menu.save.asMOV", "mov.png")); dropDownSave.addComponent(toolbar.createToolbarDropdownSeparator("menu.separator.listing")); dropDownSave.addComponent(toolbar.createToolbarDropdownItem(new ExportListingAction(), "menu.save.listing", "listing.png")); toolbar.add(new JToolBar.Separator()); final ButtonGroup displayGroup = new ButtonGroup(); final JToggleButton displayDiagram; toolbar.add(displayDiagram = toolbar.createToolbarItem(new JToggleButton(), new DisplayModeAction( DisplayMode.DIAGRAM), "menu.display.diagram", "view-diagram.png")); displayGroup.add(displayDiagram); addPropertyChangeListener("displayMode", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (getDisplayMode() == DisplayMode.DIAGRAM) displayGroup.setSelected(displayDiagram.getModel(), true); } }); } }); final JToggleButton displayPage; toolbar.add(displayPage = toolbar.createToolbarItem(new JToggleButton(), new DisplayModeAction(DisplayMode.PAGE), "menu.display.page", "view-page.png")); displayGroup.add(displayPage); addPropertyChangeListener("displayMode", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (getDisplayMode() == DisplayMode.PAGE) displayGroup.setSelected(displayPage.getModel(), true); } }); } }); displayGroup.setSelected(displayPage.getModel(), true); toolbar.add(new JToolBar.Separator()); toolbar.add(toolbar.createToolbarButton(new ZoomInAction(), "menu.zoom.in", "zoom-in.png")); toolbar.add(toolbar.createToolbarButton(new ZoomOutAction(), "menu.zoom.out", "zoom-out.png")); toolbar.add(new JToolBar.Separator()); toolbar.add(diagramPrev = toolbar.createToolbarButton(new PrevOrigamiAction(), "menu.prevDiagram", "left.png")); TreeSelectionListener prevListener = new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { diagramPrev.setEnabled(((DefaultMutableTreeNode) fileListing.getSelectionPath() .getLastPathComponent()).getPreviousLeaf() != null); } catch (NullPointerException e) {} } }); } }; fileListing.addTreeSelectionListener(prevListener); prevListener.valueChanged(new TreeSelectionEvent(this, null, true, null, null)); toolbar.add(diagramNext = toolbar.createToolbarButton(new NextOrigamiAction(), "menu.nextDiagram", "right.png")); TreeSelectionListener nextListener = new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { diagramNext.setEnabled(((DefaultMutableTreeNode) fileListing.getSelectionPath() .getLastPathComponent()).getNextLeaf() != null); } catch (NullPointerException e) {} } }); } }; fileListing.addTreeSelectionListener(nextListener); nextListener.valueChanged(new TreeSelectionEvent(this, null, true, null, null)); toolbar.add(new JToolBar.Separator()); toolbar.add(toolbar.createToolbarButton(new SettingsAction(), "menu.settings", "settings.png")); return toolbar; } @Override public void start() { super.start(); } @Override public void stop() { super.stop(); } @Override public void destroy() { super.destroy(); } @Override protected void setupLoggers() { super.setupLoggers(); Logger l = Logger.getLogger("viewer"); l.setResourceBundle(ResourceBundle.getBundle("viewer", ServiceLocator.get(ConfigurationManager.class).get() .getLocale())); l.setLevel(Level.ALL); } /** * @return Whether to show the sidebar with open files. */ public boolean isShowFileListing() { return showFileListing; } /** * @param showFileListing Whether to show the sidebar with open files. */ public void setShowFileListing(boolean showFileListing) { boolean oldVal = this.showFileListing; this.showFileListing = showFileListing; this.firePropertyChange("showFileListing", oldVal, showFileListing); } /** * @return The mode of displaying diagrams. */ public DisplayMode getDisplayMode() { return displayMode; } /** * @param displayMode The mode of displaying diagrams. */ public void setDisplayMode(DisplayMode displayMode) { DisplayMode oldVal = this.displayMode; this.displayMode = displayMode; this.firePropertyChange("displayMode", oldVal, displayMode); } /** * @return The origami currently displayed. */ public Origami getDisplayedOrigami() { return displayedOrigami; } /** * @param displayedOrigami The origami currently displayed. */ public void setDisplayedOrigami(Origami displayedOrigami) { Origami oldVal = this.displayedOrigami; this.displayedOrigami = displayedOrigami; this.firePropertyChangeEvenIfNewValIsEqual("displayedOrigami", oldVal, displayedOrigami); renderer.setOrigami(displayedOrigami); if (oldVal != null) oldVal.unloadModelStates(); } /** * Fires the property changed event even if the new value <code>.equals()</code> the old value. * * @param property The property name. * @param oldVal Old value. * @param newVal New value. */ protected void firePropertyChangeEvenIfNewValIsEqual(String property, Object oldVal, Object newVal) { for (PropertyChangeListener l : getPropertyChangeListeners(property)) { l.propertyChange(new PropertyChangeEvent(this, property, oldVal, newVal)); } } @Override protected void registerServices() { super.registerServices(); ServiceLocator.add(OrigamiViewer.class, this); ServiceLocator.add(StepThumbnailGenerator.class, new Callable<StepThumbnailGenerator>() { @Override public StepThumbnailGenerator call() throws Exception { return new StepThumbnailGenerator(); } }); } @Override protected Component getTopmostComponent() { if (bootstrap != null && bootstrap instanceof Java3DBootstrappingApplet && ((Java3DBootstrappingApplet) bootstrap).getWindow() != null) return ((Java3DBootstrappingApplet) bootstrap).getWindow(); Component parent = (bootstrap != null ? bootstrap : this); while (parent.getParent() != null) parent = parent.getParent(); return parent; } // bootstrapping support @Override public JRootPane getRootPane() { if (bootstrap != null) return bootstrap.getRootPane(); return super.getRootPane(); } @Override public Container getContentPane() { if (bootstrap != null) return bootstrap.getContentPane(); return super.getContentPane(); } @Override public URL getDocumentBase() { if (bootstrap != null) return bootstrap.getDocumentBase(); return super.getDocumentBase(); } @Override public URL getCodeBase() { if (bootstrap != null) return bootstrap.getCodeBase(); return super.getCodeBase(); } @Override public String getParameter(String name) { if (bootstrap != null) return bootstrap.getParameter(name); return super.getParameter(name); } @Override public AppletContext getAppletContext() { if (bootstrap != null) return bootstrap.getAppletContext(); return super.getAppletContext(); } @Override public String getAppletInfo() { if (bootstrap != null) return bootstrap.getAppletInfo(); return super.getAppletInfo(); } /** * Action for changing the current display mode to the one given in constructor. * * @author Martin Pecka */ class DisplayModeAction extends AbstractAction { /** */ private static final long serialVersionUID = -4054470305119745178L; DisplayMode mode; public DisplayModeAction(DisplayMode mode) { this.mode = mode; } @Override public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { setDisplayMode(mode); } }); } } /** * Sets the previous origami as the current origami. * * @author Martin Pecka */ class PrevOrigamiAction extends AbstractAction { /** */ private static final long serialVersionUID = -768961575848838395L; @Override public void actionPerformed(ActionEvent evt) { TreePath currPath = fileListing.getSelectionPath(); try { final TreePath newPath = new TreePath(((DefaultMutableTreeNode) currPath.getLastPathComponent()) .getPreviousLeaf().getPath()); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { fileListing.setSelectionPath(newPath); } }); } catch (NullPointerException e) {} } } /** * Sets the next origami as the current origami. * * @author Martin Pecka */ class NextOrigamiAction extends AbstractAction { /** */ private static final long serialVersionUID = -768961575848838395L; @Override public void actionPerformed(ActionEvent evt) { TreePath currPath = fileListing.getSelectionPath(); try { final TreePath newPath = new TreePath(((DefaultMutableTreeNode) currPath.getLastPathComponent()) .getNextLeaf().getPath()); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { fileListing.setSelectionPath(newPath); } }); } catch (NullPointerException e) {} } } /** * Exports the currently selected origami to the desired format. * * @author Martin Pecka */ protected class ExportAction extends cz.cuni.mff.peckam.java.origamist.gui.common.ExportAction { /** */ private static final long serialVersionUID = 5881716509643737123L; public ExportAction(ExportFormat format) { super(null, format); } @Override protected void beforeAction(ActionEvent evt) { this.origami = OrigamiViewer.this.displayedOrigami; if (evt.getSource() instanceof Component) ((Component) evt.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); } @Override protected void done(ActionEvent evt) { if (evt.getSource() instanceof Component) ((Component) evt.getSource()).setCursor(Cursor.getDefaultCursor()); } } /** * Exports the current listing. * * @author Martin Pecka */ class ExportListingAction extends AbstractAction { /** */ private static final long serialVersionUID = -904576077867527286L; @Override public void actionPerformed(ActionEvent e) { // ask the user for the base URI for URL relativization boolean relativeBaseCorrect = false; URI relativeBase = null; URI modelRelativeBase = null; try { // if you would just use URL.toString(), URL encoding would be performed on non-ASCII characters in the // path, which we don't want to happen modelRelativeBase = new File(ServiceLocator.get(OrigamiHandler.class).getDocumentBase().toURI()) .getParentFile().toURI(); } catch (URISyntaxException e2) {} String defaultPath = (modelRelativeBase != null ? modelRelativeBase.toString() : null); while (!relativeBaseCorrect) { String base = (String) JOptionPane.showInputDialog(rootPane, appMessages.getString("exportListing.selectBase"), appMessages.getString("exportListing.selectBase.title"), JOptionPane.QUESTION_MESSAGE, null, null, defaultPath); if (base == null) { // relativeBase = null; // is already done relativeBaseCorrect = true; } else { defaultPath = base; try { relativeBase = new URI(base); relativeBaseCorrect = true; } catch (URISyntaxException e1) { JOptionPane.showMessageDialog(rootPane, appMessages.getString("exportListing.selectBase.invalidURI"), appMessages.getString("exportListing.selectBase.invalidURI.title"), JOptionPane.ERROR_MESSAGE); } } } // ask the user for the file name JFileChooser chooser = new JFileChooser(); File defaultDir = ServiceLocator.get(ConfigurationManager.class).get().getLastExportPath().getParentFile(); File defaultFile = new File(defaultDir, "listing.xml"); chooser.setSelectedFile(defaultFile); chooser.setFileFilter(new FileNameExtensionFilter("listing.xml", "xml")); chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); chooser.setDialogType(JFileChooser.SAVE_DIALOG); chooser.setApproveButtonText(appMessages.getString("exportListingDialog.approve")); chooser.setApproveButtonMnemonic(KeyStroke.getKeyStroke( appMessages.getString("exportListingDialog.approve.mnemonic")).getKeyCode()); chooser.setApproveButtonToolTipText(ServiceLocator.get(TooltipFactory.class).getDecorated( appMessages.getString("exportListingDialog.approve.tooltip.message"), appMessages.getString("exportListingDialog.approve.tooltip.title"), "save.png", KeyStroke.getKeyStroke("alt " + appMessages.getString("exportListingDialog.approve.mnemonic")))); if (chooser.showDialog(OrigamiViewer.this, null) == JFileChooser.APPROVE_OPTION) { File f = chooser.getSelectedFile(); ServiceLocator.get(ConfigurationManager.class).get().setLastExportPath(f); if (!chooser.accept(f)) { f = new File(f, "listing.xml"); } if (f.exists()) { OrigamiViewer.this.format.applyPattern(appMessages.getString("exportListingDialog.overwrite")); if (JOptionPane.showConfirmDialog(OrigamiViewer.this, OrigamiViewer.this.format.format(new Object[] { f }), appMessages.getString("exportListingDialog.overwrite.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null) != JOptionPane.YES_OPTION) { return; } } final URI relativeBase2 = relativeBase; final URI modelRelativeBase2 = modelRelativeBase; final File file = f; new Thread(new Runnable() { @Override public void run() { try { try { ServiceLocator.get(ListingHandler.class).export(filesToDisplay, file, relativeBase2, modelRelativeBase2); OrigamiViewer.this.format.applyPattern(appMessages .getString("exportListingSuccessful.message")); JOptionPane.showMessageDialog(getRootPane(), OrigamiViewer.this.format.format(new Object[] { file.toString() }), appMessages.getString("exportListingSuccessful.title"), JOptionPane.INFORMATION_MESSAGE, null); } catch (JAXBException e1) { throw new IOException(e1); } } catch (IOException e1) { OrigamiViewer.this.format.applyPattern(appMessages .getString("failedToExportListing.message")); JOptionPane.showMessageDialog(getRootPane(), OrigamiViewer.this.format.format(new Object[] { file.toString() }), appMessages.getString("failedToExportListing.title"), JOptionPane.ERROR_MESSAGE, null); Logger.getLogger("application").warn("Unable to export listing.", e1); } } }).start(); } } } /** * Zooms all the displayed steps by 10% in. * * @author Martin Pecka */ class ZoomInAction extends AbstractAction { /** */ private static final long serialVersionUID = 2401463300217345719L; @Override public void actionPerformed(ActionEvent e) { renderer.incZoom(); } } /** * Zooms all the displayed steps by 10% out. * * @author Martin Pecka */ class ZoomOutAction extends AbstractAction { /** */ private static final long serialVersionUID = 6762119407937700068L; @Override public void actionPerformed(ActionEvent e) { renderer.decZoom(); } } /** * Shows the settings dialog. * * @author Martin Pecka */ class SettingsAction extends AbstractAction { /** */ private static final long serialVersionUID = -583073126579360879L; @Override public void actionPerformed(final ActionEvent e) { if (e.getSource() instanceof JComponent) ((JComponent) e.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); new Thread(new Runnable() { @Override public void run() { final SettingsFrame frame = new SettingsFrame(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (e.getSource() instanceof JComponent) ((JComponent) e.getSource()).setCursor(Cursor.getDefaultCursor()); frame.setVisible(true); } }); } }).start(); } } /** * Base for the open file from disk dialog. * * @author Martin Pecka */ protected abstract class AbstractOpenFileAction extends AbstractAction { /** */ private static final long serialVersionUID = 2983076419495185680L; protected JFileChooser chooser; { chooser = new JFileChooser(); configureChooser(); } /** * Setup the file chooser. */ protected void configureChooser() { File currentDir = ServiceLocator.get(ConfigurationManager.class).get().getLastOpenPath().getParentFile(); chooser.setCurrentDirectory(currentDir); chooser.setDialogType(JFileChooser.OPEN_DIALOG); chooser.setApproveButtonText(appMessages.getString("openDialog.approve")); chooser.setApproveButtonMnemonic(KeyStroke.getKeyStroke( appMessages.getString("openDialog.approve.mnemonic")).getKeyCode()); chooser.setApproveButtonToolTipText(ServiceLocator.get(TooltipFactory.class).getDecorated( appMessages.getString("openDialog.approve.tooltip.message"), appMessages.getString("openDialog.approve.tooltip.title"), "open-file-32.png", KeyStroke.getKeyStroke("alt " + appMessages.getString("openDialog.approve.mnemonic")))); } /** * The action that is taken after the user approved the open file dialog. */ protected abstract void onDialogApproved(); @Override public void actionPerformed(ActionEvent e) { if (e.getSource() instanceof JComponent) ((JComponent) e.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); if (chooser.showDialog(OrigamiViewer.this, null) == JFileChooser.APPROVE_OPTION) { if (e.getSource() instanceof JComponent) ((JComponent) e.getSource()).setCursor(Cursor.getDefaultCursor()); onDialogApproved(); } else { if (e.getSource() instanceof JComponent) ((JComponent) e.getSource()).setCursor(Cursor.getDefaultCursor()); } } } /** * Shows the open file from disk dialog. * * @author Martin Pecka */ protected class OpenFileAction extends AbstractOpenFileAction { /** */ private static final long serialVersionUID = -4079648565735159553L; @Override protected void configureChooser() { super.configureChooser(); final FileFilter extFilter = new FileNameExtensionFilter("*.xml", "XML"); chooser.setFileFilter(new FileFilter() { @Override public String getDescription() { return extFilter.getDescription(); } @Override public boolean accept(File f) { boolean extAccepted = extFilter.accept(f); if (!extAccepted) return false; return !f.getName().equals(LISTING_FILE_NAME); } }); chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); chooser.setMultiSelectionEnabled(true); } @Override protected void onDialogApproved() { final File[] selected = chooser.getSelectedFiles(); ServiceLocator.get(ConfigurationManager.class).get().setLastOpenPath(selected[0]); StringBuilder files = new StringBuilder(); for (int i = 0; i < selected.length; i++) { if (i > 0) files.append(" "); files.append(selected[i].toURI().toString()); } try { updateFilesToDisplay(files.toString()); } catch (UnsupportedDataFormatException e) { Logger.getLogger("viewer").error( appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), e); JOptionPane.showMessageDialog(OrigamiViewer.this, appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), appMessages.getString("exception.UnsupportedDataFormatException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IOException e) { Logger.getLogger("viewer").error(appMessages.getString("exception.IOException.loadModel"), e); JOptionPane.showMessageDialog(OrigamiViewer.this, appMessages.getString("exception.IOException.loadModel"), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IllegalArgumentException e) { Logger.getLogger("viewer").error(e.getMessage(), e); JOptionPane.showMessageDialog(OrigamiViewer.this, e.getMessage(), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } } } /** * Shows the open directory dialog. * * @author Martin Pecka */ protected class OpenDirAction extends AbstractOpenFileAction { /** */ private static final long serialVersionUID = -4079648565735159553L; @Override protected void configureChooser() { super.configureChooser(); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); } @Override protected void onDialogApproved() { final File[] selected = chooser.getSelectedFile().listFiles(); ServiceLocator.get(ConfigurationManager.class).get().setLastOpenPath(selected[0]); StringBuilder files = new StringBuilder(); for (int i = 0; i < selected.length; i++) { if (i > 0) files.append(" "); files.append(selected[i].toURI().toString()); } try { updateFilesToDisplay(files.toString()); } catch (UnsupportedDataFormatException e) { Logger.getLogger("viewer").error( appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), e); JOptionPane.showMessageDialog(OrigamiViewer.this, appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), appMessages.getString("exception.UnsupportedDataFormatException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IOException e) { Logger.getLogger("viewer").error(appMessages.getString("exception.IOException.loadModel"), e); JOptionPane.showMessageDialog(OrigamiViewer.this, appMessages.getString("exception.IOException.loadModel"), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IllegalArgumentException e) { Logger.getLogger("viewer").error(e.getMessage(), e); JOptionPane.showMessageDialog(OrigamiViewer.this, e.getMessage(), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } } } /** * Shows the open file from disk dialog. * * @author Martin Pecka */ protected class OpenLocalListingAction extends AbstractOpenFileAction { /** */ private static final long serialVersionUID = -3005249957434468741L; @Override protected void configureChooser() { super.configureChooser(); chooser.setFileFilter(new FileFilter() { @Override public String getDescription() { return LISTING_FILE_NAME; } @Override public boolean accept(File f) { return f.isDirectory() || f.getName().equals(LISTING_FILE_NAME); } }); chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); } @Override protected void onDialogApproved() { final File selected = chooser.getSelectedFile(); ServiceLocator.get(ConfigurationManager.class).get().setLastOpenPath(selected); try { updateFilesToDisplay(selected.toURI().toString()); } catch (UnsupportedDataFormatException e1) { Logger.getLogger("viewer").error( appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), e1); JOptionPane.showMessageDialog(OrigamiViewer.this, appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), appMessages.getString("exception.UnsupportedDataFormatException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IOException e1) { Logger.getLogger("viewer").error(appMessages.getString("exception.IOException.loadModel"), e1); JOptionPane.showMessageDialog(OrigamiViewer.this, appMessages.getString("exception.IOException.loadModel"), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IllegalArgumentException e1) { Logger.getLogger("viewer").error(e1.getMessage(), e1); JOptionPane.showMessageDialog(OrigamiViewer.this, e1.getMessage(), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } } } /** * Shows the open file from URL dialog. * * @author Martin Pecka */ protected class OpenURLAction extends AbstractAction { /** */ private static final long serialVersionUID = -4948201244271857952L; @Override public void actionPerformed(ActionEvent e) { Object selected = null; URL selectedUrl = null; Object defaultURL = ServiceLocator.get(ConfigurationManager.class).get().getLastOpenURL(); if (defaultURL == null) defaultURL = "http://"; while ((selected = JOptionPane.showInputDialog(rootPane, appMessages.getString("openUrl.message"), appMessages.getString("openUrl.title"), JOptionPane.QUESTION_MESSAGE, new ImageIcon( OrigamiViewer.this.getClass().getResource("/resources/images/open-url.png")), null, selected == null ? defaultURL : selected)) != null) { try { selectedUrl = new URL(selected.toString()); if (!selectedUrl.toURI().isAbsolute()) { throw new MalformedURLException("The URL to open must be absolute!"); } break; } catch (MalformedURLException e1) { JOptionPane.showMessageDialog(rootPane, appMessages.getString("openUrl.invalidUrl"), appMessages.getString("openUrl.invalidUrl.title"), JOptionPane.ERROR_MESSAGE); } catch (URISyntaxException e1) { JOptionPane.showMessageDialog(rootPane, appMessages.getString("openUrl.invalidUrl"), appMessages.getString("openUrl.invalidUrl.title"), JOptionPane.ERROR_MESSAGE); } } // the user has cancelled the dialog if (selectedUrl == null) return; ServiceLocator.get(ConfigurationManager.class).get().setLastOpenURL(selectedUrl); final URL url = selectedUrl; try { updateFilesToDisplay(url.toString()); } catch (UnsupportedDataFormatException e1) { Logger.getLogger("viewer").error( appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), e1); JOptionPane.showMessageDialog(OrigamiViewer.this, appMessages.getString("exception.UnsupportedDataFormatException.loadModel"), appMessages.getString("exception.UnsupportedDataFormatException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IOException e1) { Logger.getLogger("viewer").error(appMessages.getString("exception.IOException.loadModel"), e1); JOptionPane.showMessageDialog(OrigamiViewer.this, appMessages.getString("exception.IOException.loadModel"), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } catch (IllegalArgumentException e1) { Logger.getLogger("viewer").error(e1.getMessage(), e1); JOptionPane.showMessageDialog(OrigamiViewer.this, e1.getMessage(), appMessages.getString("exception.IOException.loadModel.title"), JOptionPane.ERROR_MESSAGE); } } } }