/* * Copyright (C) 2004 The Concord Consortium, Inc., * 10 Concord Crossing, Concord, MA 01742 * * Web Site: http://www.concord.org * Email: info@concord.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * END LICENSE */ /* * Last modification information: * $Revision: 1.92 $ * $Date: 2007-10-16 22:06:33 $ * $Author: scytacki $ * * Licence Information * Copyright 2004 The Concord Consortium */ package org.concord.otrunk.view; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.JWindow; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.WindowConstants; import javax.swing.border.BevelBorder; import javax.swing.border.EmptyBorder; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreePath; import org.concord.applesupport.AppleApplicationAdapter; import org.concord.applesupport.AppleApplicationUtil; import org.concord.framework.otrunk.OTChangeEvent; import org.concord.framework.otrunk.OTChangeListener; import org.concord.framework.otrunk.OTID; import org.concord.framework.otrunk.OTObject; import org.concord.framework.otrunk.OTObjectList; import org.concord.framework.otrunk.OTUser; import org.concord.framework.otrunk.OTrunk; import org.concord.framework.otrunk.view.OTExternalAppService; import org.concord.framework.otrunk.view.OTFrameManager; import org.concord.framework.otrunk.view.OTJComponentServiceFactory; import org.concord.framework.otrunk.view.OTUserListService; import org.concord.framework.otrunk.view.OTViewContainer; import org.concord.framework.otrunk.view.OTViewContainerChangeEvent; import org.concord.framework.otrunk.view.OTViewContainerListener; import org.concord.framework.otrunk.view.OTViewContext; import org.concord.framework.otrunk.view.OTViewFactory; import org.concord.framework.text.UserMessageHandler; import org.concord.framework.text.UserMessageHandlerExt1; import org.concord.framework.util.SimpleTreeNode; import org.concord.otrunk.OTMLToXHTMLConverter; import org.concord.otrunk.OTSystem; import org.concord.otrunk.OTrunkImpl; import org.concord.otrunk.OTrunkServiceEntry; import org.concord.otrunk.PageXHTMLExporter; import org.concord.otrunk.datamodel.OTDataObject; import org.concord.otrunk.handlers.UrlStreamHandlerFactory; import org.concord.otrunk.net.HTTPRequestException; import org.concord.otrunk.overlay.OTOverlayGroup; import org.concord.otrunk.user.OTUserObject; import org.concord.otrunk.xml.ExporterJDOM; import org.concord.otrunk.xml.XMLDatabase; import org.concord.otrunk.xml.XMLDatabaseChangeEvent; import org.concord.otrunk.xml.XMLDatabaseChangeListener; import org.concord.swing.CustomDialog; import org.concord.swing.MemoryMonitorPanel; import org.concord.swing.MostRecentFileDialog; import org.concord.swing.StreamRecord; import org.concord.swing.StreamRecordView; import org.concord.swing.util.Util; import org.concord.utilities.InstallationResponseCache; import org.concord.view.SimpleTreeModel; import org.concord.view.SwingUserMessageHandler; /** * OTViewer Class name and description * * Date created: Dec 14, 2004 * * @author scott * */ public class OTViewer extends JFrame implements TreeSelectionListener, OTViewContainerListener, AppleApplicationAdapter { /** * first version of this class */ private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(OTViewer.class.getCanonicalName()); public final static String TITLE_PROP = "otrunk.view.frame_title"; public final static String HIDE_TREE_PROP = "otrunk.view.hide_tree"; public final static String SHOW_CONSOLE_PROP = "otrunk.view.show_console"; public final static String DEMO_ONLY_PROP = "otrunk.view.demo"; public final static String OTML_URL_PROP = "otrunk.otmlurl"; public final static String HTTP_PUT = "PUT"; public final static String HTTP_POST = "POST"; public final static String HTTP_HEAD = "HEAD"; final static String DEFAULT_BASE_FRAME_TITLE = "OTrunk Viewer"; private static OTViewFactory otViewFactory; OTFrameManagerImpl frameManager; JSplitPane splitPane; OTViewContainerPanel bodyPanel; SimpleTreeModel folderTreeModel; SimpleTreeModel dataTreeModel; JTree folderTreeArea; JTree dataTreeArea; /** * Dialog used for instruction panel that shows when the program starts * Prompts "new" or "open" portfolio */ private JDialog commDialog; private static JFrame consoleFrame; JMenuBar menuBar; private JPanel statusPanel; String baseFrameTitle; boolean showTree = false; private boolean useScrollPane; boolean justStarted = false; protected int userMode = OTConfig.NO_USER_MODE; File currentAuthoredFile = null; URL currentURL = null; URL remoteURL; OTUserSession userSession; private static OTrunkImpl otrunk; XMLDatabase xmlDB; private OTSystem userSystem; private ArrayList<OTrunkServiceEntry<?>> services = new ArrayList<OTrunkServiceEntry<?>>(); private OTChangeListener systemChangeListener; /** * This is true if the user was asked about saving user data after they * initiated a close of the current view. */ private boolean askedAboutSavingUserData = false; /** * This is true if the user was asked about saving the user data, and said * yes */ private boolean needToSaveUserData = false; /** * This is true if the SailOTViewer created this OTViewer */ private boolean launchedBySailOTViewer = false; private boolean sailSaveEnabled = true; // Temp, to close the window AbstractAction exitAction; AbstractAction saveAsAction; private AbstractAction saveUserDataAsAction; private AbstractAction saveUserDataAction; private AbstractAction debugAction; private AbstractAction showConsoleAction; private AbstractAction newUserDataAction; private AbstractAction loadUserDataAction; private AbstractAction loadAction; private AbstractAction reloadWindowAction; private AbstractAction saveAction; private AbstractAction saveRemoteAsAction; private AbstractAction exportImageAction; private AbstractAction exportHiResImageAction; private AbstractAction exportToHtmlAction; private AbstractAction exportCurrentToHtmlAction; private boolean settingUp; public OTViewer() { super(); if (OTConfig.isSingleUserMode()) { InstallationResponseCache.installResponseCache(); } try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { logger.warning("Error setting native LAF: " + e); } this.showTree = true; AppleApplicationUtil.registerWithMacOSX(this); baseFrameTitle = DEFAULT_BASE_FRAME_TITLE; try { // this overrides the default base frame title String title = System.getProperty(TITLE_PROP, null); if (title != null) { baseFrameTitle = title; } } catch (Throwable t) { // do nothing, just use the default title } setTitle(baseFrameTitle); // If "otrunk.view.demo" is true, closing frame will just dispose // the frame, not exit. if (!Boolean.getBoolean(DEMO_ONLY_PROP)){ setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { exitAction.actionPerformed(null); } }); } commDialog = new JDialog(this, true); registerDebugEventListeners(); } private static void createConsoleFrame() { if (consoleFrame == null) { consoleFrame = new JFrame("Console"); StreamRecord record = new StreamRecord(50000); StreamRecordView view = new StreamRecordView(record); consoleFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); System.setOut((PrintStream) view.addOutputStream(System.out, "Console")); System.setErr((PrintStream) view.addOutputStream(System.err, System.out)); consoleFrame.getContentPane().add(view); consoleFrame.setSize(800, 600); if (OTConfig.getBooleanProp(SHOW_CONSOLE_PROP, false)) { consoleFrame.setVisible(true); } } } public static void setConsoleFrame(JFrame frame) { consoleFrame = frame; } private void registerDebugEventListeners() { // for debugging // add a breakpoint below, run in debugging mode, and then hit Meta-B // the object you're currently focused on will be passed in here and you // can // start exploring the data structures, etc. KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); KeyEventDispatcher deleteDispatcher = new KeyEventDispatcher(){ public boolean dispatchKeyEvent(KeyEvent e) { if ((e.getID() == java.awt.event.KeyEvent.KEY_RELEASED) && ((e.getModifiersEx() & java.awt.event.InputEvent.META_DOWN_MASK) != 0) && (e.getKeyCode() == java.awt.event.KeyEvent.VK_B)) { Object o = e.getSource(); logger.fine(o.toString()); return true; } return false; } }; focusManager.addKeyEventDispatcher(deleteDispatcher); // If the mouse click is a right click event and alt is pressed // or on a Mac the ctrl and option keys are down // then this code will print out the toString method of the // object which the mouse is over to the console. AWTEventListener awtEventListener = new AWTEventListener(){ public void eventDispatched(AWTEvent event) { if(!(event instanceof MouseEvent)){ return; } MouseEvent mEvent = (MouseEvent) event; if (mEvent.getID() == MouseEvent.MOUSE_CLICKED && (mEvent.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0) { logger.info(event.getSource().toString()); } } }; Toolkit.getDefaultToolkit().addAWTEventListener(awtEventListener, AWTEvent.MOUSE_EVENT_MASK); } /** * this needs to be called before initialized. * * @param serviceInterface * @param service */ public <T> void addService(Class<T> serviceInterface, T service) { OTrunkServiceEntry<T> entry = new OTrunkServiceEntry<T>(service, serviceInterface); services.add(entry); } public void setUserMode(int mode) { userMode = mode; } public int getUserMode() { return userMode; } public void updateTreePane() { Dimension minimumSize = new Dimension(100, 50); folderTreeArea = new JTree(folderTreeModel); // we are just disabling this however if we want to // use this tree for authoring, or for managing student // created objects this will need to be some form of option folderTreeArea.setEditable(false); folderTreeArea.addTreeSelectionListener(this); JComponent leftComponent = null; JScrollPane folderTreeScrollPane = new JScrollPane(folderTreeArea); if (System.getProperty(OTConfig.DEBUG_PROP, "").equals("true")) { // ViewFactory.getComponent(root); dataTreeArea = new JTree(dataTreeModel); dataTreeArea.setEditable(false); dataTreeArea.addTreeSelectionListener(this); JScrollPane dataTreeScrollPane = new JScrollPane(dataTreeArea); JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.add("Folders", folderTreeScrollPane); tabbedPane.add("Resources", dataTreeScrollPane); // Provide minimum sizes for the two components in the split pane folderTreeScrollPane.setMinimumSize(minimumSize); dataTreeScrollPane.setMinimumSize(minimumSize); tabbedPane.setMinimumSize(minimumSize); leftComponent = tabbedPane; } else { leftComponent = folderTreeScrollPane; } if (splitPane == null) { splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftComponent, bodyPanel); } else { splitPane.setLeftComponent(leftComponent); } splitPane.setOneTouchExpandable(true); splitPane.setDividerLocation(200); } public void initArgs(String[] args) { URL authorOTMLURL = OTViewerHelper.getURLFromArgs(args); if (authorOTMLURL == null && System.getProperty(OTML_URL_PROP, null) != null) { try { authorOTMLURL = new URL(System.getProperty(OTML_URL_PROP, null)); } catch (MalformedURLException e) { e.printStackTrace(); } } if (authorOTMLURL == null) { authorOTMLURL = OTViewer.class.getResource("no-arguments-page.otml"); } initWithWizard(authorOTMLURL); } /** * @param args * @return */ public String getURL(String[] args) { URL authorOTMLURL = OTViewerHelper.getURLFromArgs(args); if ("file".equalsIgnoreCase(authorOTMLURL.getProtocol())) { try { URI authorOTMLURI = new URI(authorOTMLURL.toString()); currentAuthoredFile = new File(authorOTMLURI); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return authorOTMLURL.toString(); } // private static Object getURLStreamHandlerFactoryLock() // throws IllegalAccessException // { // Object lock; // try { // Field streamHandlerLockField = URL.class.getDeclaredField("streamHandlerLock"); //$NON-NLS-1$ // streamHandlerLockField.setAccessible(true); // lock = streamHandlerLockField.get(null); // } catch (NoSuchFieldException noField) { // // could not find the lock, lets sync on the class object // lock = URL.class; // } // return lock; // } private void registerURLStreamHandlers(){ try { UrlStreamHandlerFactory.registerHandlerClass(org.concord.otrunk.handlers.jres.Handler.class); } catch (Exception e){ // don't do anything here. If SailStreamHandlerFactory has already been // created, SailOTViewer should have already added the handlers } /* * If we wanted to do something clever at a later date, the reflection code below would * allow us to get the current URLStreamHandlerFactory */ // Field factoryField = null; // Field[] fields = URL.class.getDeclaredFields(); // for (int i = 0; i < fields.length; i++) { // if (Modifier.isStatic(fields[i].getModifiers()) // && fields[i].getType().equals(URLStreamHandlerFactory.class)) { // factoryField = fields[i]; // break; // } // } // factoryField.setAccessible(true); // try { // URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null); // if (factory == null){ // UrlStreamHandlerFactory.registerHandlerClass(org.concord.otrunk.handlers.jres.Handler.class); // } // } catch (Exception e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } public boolean init(String url) { try { return init(new URL(url)); } catch (MalformedURLException e) { e.printStackTrace(); } return false; } /** * * @param url * @return true if the url was loaded, return false if an error happens and it wasn't loaded */ public boolean init(URL url) { registerURLStreamHandlers(); updateRemoteURL(url.toExternalForm()); createActions(); updateMenuBar(); setJMenuBar(menuBar); frameManager = new OTFrameManagerImpl(); frameManager.setMainFrame(this); bodyPanel = new OTViewContainerPanel(frameManager); bodyPanel.addViewContainerListener(this); if (showTree) { dataTreeModel = new SimpleTreeModel(); folderTreeModel = new SimpleTreeModel(); updateTreePane(); getContentPane().add(splitPane); } else { getContentPane().add(bodyPanel); } if (OTConfig.isShowStatus() || noClassAssignedForStudent()) { initStatusBar(); } initFrameDimensions(); if (url == null) { return false; } try { initializeURL(url); } catch (MalformedURLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (Exception e) { // FIXME: this should popup a dialog logger.log(Level.SEVERE, "Can't load url", e); return false; } return true; } /* * Test to determine whether to display warning that the student may not * be running the activity with correct portal settings. * * Shoud return true when the activity is not run in student mode and * is not assigned a class and a teacher through the portal. */ private boolean noClassAssignedForStudent() { return getUserMode() == OTConfig.SINGLE_USER_MODE && ! isSailSaveEnabled(); } private Point bottomCorner; public void setStatusPanelVisible(boolean visible) { if (visible) { if (statusPanel == null) { // wants to be visible, but hasn't been set up yet initStatusBar(); } } if (statusPanel != null) { statusPanel.setVisible(visible); } } public JPanel getStatusPanel() { return this.statusPanel; } private void initStatusBar() { statusPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 3, 1)); if (OTConfig.isShowStatus()) { final JLabel saveStateLabel = new JLabel("File saved"); statusPanel.add(saveStateLabel); statusPanel.add(Box.createHorizontalStrut(20)); statusPanel.add(new MemoryMonitorPanel()); statusPanel.add(Box.createHorizontalStrut(5)); // It takes a while for xmlDM to be initialized... Thread waitForDB = new Thread() { @Override public void run() { while (xmlDB == null) { try { sleep(1000); } catch (Exception e) { } } xmlDB.addXMLDatabaseChangeListener(new XMLDatabaseChangeListener() { public void stateChanged(XMLDatabaseChangeEvent e) { if (xmlDB.isDirty()) { saveStateLabel.setText("Unsaved changes"); } else { saveStateLabel.setText("File saved"); if (!settingUp){ // show save confirmation dialog final JWindow savedWindow = new JWindow(); JLabel savedLabel = new JLabel("File saved"); savedLabel.setFont(saveStateLabel.getFont().deriveFont(Font.BOLD)); String fileName = currentAuthoredFile.getAbsolutePath(); if (fileName.length() > 35){ fileName = "... "+fileName.substring(fileName.length()-35); } JLabel locationLabel = new JLabel(fileName); locationLabel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(Color.BLACK), BorderFactory.createEmptyBorder(3, 3, 3, 3))); final JPanel savedPanel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(8, 8, 8, 8); gbc.gridwidth = GridBagConstraints.REMAINDER; savedPanel.add(savedLabel, gbc); savedPanel.add(locationLabel, gbc); savedPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); savedWindow.add(savedPanel); savedWindow.pack(); final int desiredHeight = savedWindow.getHeight(); savedWindow.getContentPane().setLayout(null); savedWindow.setSize(savedWindow.getWidth(), 0); bottomCorner = new Point(OTViewer.this.getLocationOnScreen().x + OTViewer.this.getWidth(), OTViewer.this.getLocationOnScreen().y + OTViewer.this.getHeight()); savedWindow.setLocation(bottomCorner.x - savedWindow.getWidth(), bottomCorner.y); savedWindow.setVisible(true); javax.swing.Timer timer = new javax.swing.Timer(0, new ActionListener() { public void actionPerformed(ActionEvent e) { int newHeight = Math.min(savedWindow.getHeight()+6, desiredHeight); savedWindow.setSize(savedWindow.getWidth(),newHeight); savedWindow.setLocation(bottomCorner.x - savedWindow.getWidth(), bottomCorner.y-savedWindow.getHeight()); savedPanel.repaint(); if(newHeight >= desiredHeight) { ((javax.swing.Timer) e.getSource()).stop(); //restore original layout savedWindow.getContentPane().setLayout(new BorderLayout()); savedWindow.validate(); } } }); timer.start(); javax.swing.Timer timer2 = new javax.swing.Timer(0, new ActionListener() { public void actionPerformed(ActionEvent e) { int newHeight = Math.max(savedWindow.getHeight()-6, 0); savedWindow.setSize(savedWindow.getWidth(),newHeight); savedWindow.setLocation(bottomCorner.x - savedWindow.getWidth(), bottomCorner.y-savedWindow.getHeight()); savedPanel.repaint(); if(newHeight <= 0) { ((javax.swing.Timer) e.getSource()).stop(); savedWindow.dispose(); } } }); timer2.setInitialDelay(2500); timer2.start(); // TimerTask closeTask = new TimerTask() { // // @Override // public void run() // { // savedWindow.setVisible(false); // } // }; // Timer timer2 = new Timer(); // timer2.schedule(closeTask, 2500); } } statusPanel.repaint(); } }); } }; waitForDB.start(); } if (noClassAssignedForStudent()) { JLabel readOnlyLabel = new JLabel("User Data Will Not Be Saved!"); statusPanel.setBackground(Color.RED); statusPanel.add(readOnlyLabel); statusPanel.add(Box.createHorizontalStrut(5)); } getContentPane().add(statusPanel, BorderLayout.SOUTH); } private void initFrameDimensions() { SwingUtilities.invokeLater(new Runnable() { public void run() { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); if (screenSize.width < 1000 || screenSize.height < 700) { setVisible(true); int state = getExtendedState(); // Set the maximized bits state |= Frame.MAXIMIZED_BOTH; // Maximize the frame setExtendedState(state); } else { int cornerX = 50; int cornerY = 50; int sizeX = 800; int sizeY = 500; // OTViewService viewService = otViewFactory. setBounds(cornerX, cornerY, cornerX + sizeX, cornerY + sizeY); setVisible(true); } } }); } public void initializeURL(URL url) throws Exception { loadURL(url); otrunk.setSailSavingDisabled(noClassAssignedForStudent()); OTMainFrame mainFrame = otrunk.getService(OTMainFrame.class); if (OTConfig.getBooleanProp(HIDE_TREE_PROP, false) || !mainFrame.getShowLeftPanel()) { splitPane.getLeftComponent().setVisible(false); } useScrollPane = true; if (mainFrame.getFrame() != null) { if (mainFrame.getFrame().isResourceSet("openMaximized") && mainFrame.getFrame().getOpenMaximized()){ setExtendedState(Frame.MAXIMIZED_BOTH); } else if (mainFrame.getFrame().isResourceSet("width") && mainFrame.getFrame().isResourceSet("height")) { int cornerX = mainFrame.getFrame().getPositionX(); int cornerY = mainFrame.getFrame().getPositionY(); int sizeX = mainFrame.getFrame().getWidth(); int sizeY = mainFrame.getFrame().getHeight(); // See if title is set for main frame (only if name is // still default, so that jnlp prop will still // overrides this) if (baseFrameTitle == DEFAULT_BASE_FRAME_TITLE && mainFrame.getFrame().isResourceSet("title")){ logger.fine("change"); baseFrameTitle = mainFrame.getFrame().getTitle(); setTitle(baseFrameTitle); } setBounds(cornerX, cornerY, cornerX + sizeX, cornerY + sizeY); repaint(); } useScrollPane = mainFrame.getFrame().getUseScrollPane(); } bodyPanel.setUseScrollPane(useScrollPane); setupBodyPanel(); } public void initWithWizard(String url) { try { initWithWizard(new URL(url)); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void initWithWizard(URL url) { justStarted = true; init(url); if (userMode == OTConfig.SINGLE_USER_MODE) { SwingUtilities.invokeLater(new Runnable() { public void run() { instructionPanel(); } }); } } public void loadUserDataFile(File file) { try { OTMLUserSession xmlUserSession = new OTMLUserSession(file, null); loadUserSession(xmlUserSession); } catch (Exception e) { e.printStackTrace(); } } /** * This method just sets the viewers userSession without loading it. * This is useful if a user needs to select the data they want to use * when the load method is called. Currently this is used by the "wizard" in * the ot viewer. * * @param userSession */ public void setUserSession(OTUserSession userSession) { this.userSession = userSession; userSession.setOTrunk(otrunk); } /** * This method loads in a user session, and reloads the window. * * @param userSession * @throws Exception */ public void loadUserSession(OTUserSession userSession) throws Exception { this.userSession = userSession; otrunk.registerUserSession(userSession); reloadWindow(); } public void updateOverlayGroupListeners() { OTObjectList overlays = userSystem.getOverlays(); for(int i=0; i<overlays.size(); i++){ OTObject item = overlays.get(i); if(item instanceof OTOverlayGroup){ ((OTOverlayGroup) item).addOTChangeListener(systemChangeListener); } } } public void reloadOverlays() { try { otrunk.reloadOverlays(userSession); updateOverlayGroupListeners(); reloadWindow(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void loadFile(File file) { currentAuthoredFile = file; try { initializeURL(currentAuthoredFile.toURL()); } catch (Exception e) { e.printStackTrace(); } } public void loadURL(URL url) throws Exception { if ("file".equalsIgnoreCase(url.getProtocol())) { currentAuthoredFile = new File(url.getPath()); } XMLDatabase systemDB = null; try { // try loading in the system object if there is one String systemOtmlUrlStr = OTConfig.getStringProp(OTConfig.SYSTEM_OTML_PROP); if(systemOtmlUrlStr != null){ URL systemOtmlUrl = new URL(systemOtmlUrlStr); systemDB = new XMLDatabase(systemOtmlUrl, true, System.out); // don't track the resource info on the system db. systemDB.loadObjects(); } } catch (Exception e){ e.printStackTrace(); systemDB = null; } try { xmlDB = new XMLDatabase(url, true, System.out); // Only track the resource info when there isn't a user. Currently // all classroom uses // of OTViewer has NO_USER_MODE turned off, so using this setting // safe to test // the resource tracking without affecting real users. if (userMode == OTConfig.NO_USER_MODE) { xmlDB.setTrackResourceInfo(true); } xmlDB.loadObjects(); } catch (org.jdom.input.JDOMParseException e) { String xmlWarningTitle = "XML Decoding error"; String xmlWarningMessage = "There appears to a problem parsing the XML of this document. \n" + "Please show this error message to one of the workshop leaders. \n\n" + e.getMessage(); JOptionPane.showMessageDialog(null, xmlWarningMessage, xmlWarningTitle, JOptionPane.ERROR_MESSAGE); throw e; } addService(UserMessageHandler.class, new SwingUserMessageHandler(this)); addService(OTViewer.class, this); otrunk = new OTrunkImpl(systemDB, xmlDB, services); OTViewFactory myViewFactory = otrunk.getService(OTViewFactory.class); if (myViewFactory != null) { otViewFactory = myViewFactory; } OTViewContext factoryContext = otViewFactory.getViewContext(); factoryContext.addViewService(OTrunk.class, otrunk); factoryContext.addViewService(OTFrameManager.class, frameManager); factoryContext.addViewService(OTJComponentServiceFactory.class, new OTJComponentServiceFactoryImpl()); factoryContext.addViewService(OTExternalAppService.class, new OTExternalAppServiceImpl()); factoryContext.addViewService(OTUserListService.class, new OTUserListService() { public Vector<OTUser> getUserList() { return otrunk.getUsers(); } }); ((OTViewFactoryImpl)otViewFactory).contextSetupComplete(); currentURL = url; } // This method was refactored out of loadURL private void setupBodyPanel() throws Exception { bodyPanel.setTopLevelContainer(true); bodyPanel.setOTViewFactory(otViewFactory); // set the current mode from the viewservice to the main bodyPanel // bodyPanel.setViewMode(otViewFactory.getDefaultMode()); // set the viewFactory of the frame manager frameManager.setViewFactory(otViewFactory); settingUp = true; xmlDB.setDirty(false); reloadWindow(); settingUp = false; } public OTObject getAuthoredRoot() throws Exception { String rootLocalId = OTConfig.getStringProp(OTConfig.ROOT_OBJECT_PROP); if(rootLocalId != null){ OTID rootID = xmlDB.getOTIDFromLocalID(rootLocalId); return otrunk.getOTObject(rootID); } return otrunk.getRoot(); } public OTObject getRoot() throws Exception { switch (userMode) { case OTConfig.NO_USER_MODE: return getAuthoredRoot(); case OTConfig.SINGLE_USER_MODE: if (userSession == null) { return null; } OTObject otRoot = getAuthoredRoot(); return otrunk.getUserRuntimeObject(otRoot, getCurrentUser()); } return null; } public void reloadWindow() throws Exception { OTObject root = getRoot(); boolean overrideShowTree = false; if (userMode == OTConfig.SINGLE_USER_MODE) { if (root == null) { // FIXME This is an error // the newAnonUserData should have been called before this // method is // called // no user file has been started yet overrideShowTree = true; } else { OTObject realRoot = otrunk.getRealRoot(); if (realRoot instanceof OTSystem) { OTSystem localUserSystem = (OTSystem) otrunk.getUserRuntimeObject(realRoot, getCurrentUser()); // FIXME there should be a better way than this because we // have to handle // multiple users. if (localUserSystem != userSystem) { userSystem = localUserSystem; systemChangeListener = new OTChangeListener() { public void stateChanged(OTChangeEvent e) { if ("overlays".equals(e.getProperty())) { reloadOverlays(); } } }; updateOverlayGroupListeners(); userSystem.addOTChangeListener(systemChangeListener); } } } } if (showTree && !overrideShowTree) { OTDataObject rootDataObject = xmlDB.getRoot(); dataTreeModel.setRoot(new OTDataObjectNode("root", rootDataObject, otrunk)); folderTreeModel.setRoot(new OTFolderNode(root)); } bodyPanel.setCurrentObject(root); if (showTree && !overrideShowTree) { folderTreeModel.fireTreeStructureChanged(new TreePath( folderTreeModel.getRoot())); dataTreeModel.fireTreeStructureChanged(new TreePath( dataTreeModel.getRoot())); } Frame frame = (Frame) SwingUtilities.getRoot(this); switch (userMode) { case OTConfig.NO_USER_MODE: if (frame.getTitle().equals(baseFrameTitle) && baseFrameTitle.equals(DEFAULT_BASE_FRAME_TITLE)) { // update the frame title to reflect the document location, but only if the frame title isn't custom if (remoteURL != null) { frame.setTitle(baseFrameTitle + ": " + remoteURL.toString()); } else { frame.setTitle(baseFrameTitle + ": " + currentURL.toString()); } } break; case OTConfig.SINGLE_USER_MODE: String userSessionLabel = null; if (userSession != null) { userSessionLabel = userSession.getLabel(); } if(userSessionLabel != null){ frame.setTitle(baseFrameTitle + ": " + userSessionLabel); } else { frame.setTitle(baseFrameTitle); } break; } if(userSession == null){ saveUserDataAction.setEnabled(false); saveUserDataAsAction.setEnabled(false); } else { saveUserDataAction.setEnabled(userSession.allowSave()); saveUserDataAsAction.setEnabled(userSession.allowSaveAs()); } } public void reload() throws Exception { initializeURL(currentURL); } public OTUserSession getUserSession() { return userSession; } public OTUserObject getCurrentUser() { if(userSession == null){ return null; } return userSession.getUserObject(); } /* * (non-Javadoc) * * @see org.concord.otrunk.view.OTViewContainerListener#currentObjectChanged(org.concord.framework.otrunk.view.OTViewContainer) */ public void currentObjectChanged(OTViewContainerChangeEvent evt) { final OTViewContainer myContainer = (OTViewContainer) evt.getSource(); // TODO Auto-generated method stub SwingUtilities.invokeLater(new Runnable() { public void run() { OTObject currentObject = myContainer.getCurrentObject(); if (folderTreeArea != null) { OTFolderNode node = (OTFolderNode) folderTreeArea.getLastSelectedPathComponent(); if (node == null) return; if (node.getPfObject() != currentObject) { folderTreeArea.setSelectionPath(null); } } } }); } /* * (non-Javadoc) * * @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent) */ public void valueChanged(TreeSelectionEvent event) { if (event.getSource() == folderTreeArea) { OTFolderNode node = (OTFolderNode) folderTreeArea.getLastSelectedPathComponent(); if (node == null) return; OTObject pfObject = node.getPfObject(); bodyPanel.setCurrentObject(pfObject); if (splitPane.getRightComponent() != bodyPanel) { splitPane.setRightComponent(bodyPanel); } } else if (event.getSource() == dataTreeArea) { SimpleTreeNode node = (SimpleTreeNode) dataTreeArea.getLastSelectedPathComponent(); Object resourceValue = null; if (node != null) { resourceValue = node.getObject(); if (resourceValue == null) { resourceValue = "null resource value"; } } else { resourceValue = "no selected data object"; } JComponent nodeView = null; if (resourceValue instanceof OTDataObject) { nodeView = new OTDataObjectView((OTDataObject) resourceValue); } else { nodeView = new JTextArea(resourceValue.toString()); } JScrollPane scrollPane = new JScrollPane(nodeView); splitPane.setRightComponent(scrollPane); } } private void updateRemoteURL(String defaultURL) { String remote = System.getProperty(OTConfig.REMOTE_URL_PROP, null); try { if (remote == null) { if (defaultURL != null && defaultURL.startsWith("http:")) { remoteURL = new URL(defaultURL); } } else { remoteURL = new URL(remote); } } catch (Exception e) { remoteURL = null; logger.log(Level.WARNING, "Remote URL is invalid.", e); } } /** * @return Returns the menuBar. */ public JMenuBar updateMenuBar() { // /////////////////////////////////////////////// JMenu fileMenu = null; if (menuBar == null) { menuBar = new JMenuBar(); fileMenu = new JMenu("File"); menuBar.add(fileMenu); } else { fileMenu = menuBar.getMenu(0); fileMenu.removeAll(); } // This is a hacky place to override the userMode, but it is currently // the best place. if (OTConfig.isAuthorMode() || OTConfig.isNoUserMode()) { userMode = OTConfig.NO_USER_MODE; } if (userMode == OTConfig.SINGLE_USER_MODE) { fileMenu.setEnabled(!justStarted); // we add new and load ("open") menu items only if this is NOT launched // by sail or if otrunk.view.destructive_menu is set to true if (!isLaunchedBySailOTViewer() || (OTConfig.isShowDestructiveMenuItems())){ fileMenu.add(newUserDataAction); fileMenu.add(loadUserDataAction); } fileMenu.add(saveUserDataAction); fileMenu.add(saveUserDataAsAction); } if (OTConfig.isDebug()) { if (userMode == OTConfig.SINGLE_USER_MODE) { loadAction.putValue(Action.NAME, "Open Authored Content..."); saveAction.putValue(Action.NAME, "Save Authored Content"); saveAsAction.putValue(Action.NAME, "Save Authored Content As..."); saveRemoteAsAction.putValue(Action.NAME, "Save Authored Content Remotely As..."); } else { loadAction.putValue(Action.NAME, "Open..."); saveAction.putValue(Action.NAME, "Save"); saveAsAction.putValue(Action.NAME, "Save As..."); saveRemoteAsAction.putValue(Action.NAME, "Save Remotely As..."); } fileMenu.add(loadAction); fileMenu.add(saveAction); fileMenu.add(saveAsAction); fileMenu.add(saveRemoteAsAction); } if (OTConfig.isAuthorMode() && !OTConfig.isDebug()) { if (userMode == OTConfig.SINGLE_USER_MODE) { loadAction.putValue(Action.NAME, "Open Authored Content..."); saveAction.putValue(Action.NAME, "Save Authored Content"); saveAsAction.putValue(Action.NAME, "Save Authored Content As..."); } else { loadAction.putValue(Action.NAME, "Open..."); saveAction.putValue(Action.NAME, "Save"); saveAsAction.putValue(Action.NAME, "Save As..."); } fileMenu.add(loadAction); fileMenu.add(saveAction); fileMenu.add(saveAsAction); } if (OTConfig.getBooleanProp("otrunk.view.export_image", false)) { fileMenu.add(exportImageAction); fileMenu.add(exportHiResImageAction); } if (OTConfig.isUseAlternativeExport()) { exportCurrentToHtmlAction.putValue(Action.NAME, exportToHtmlAction.getValue(Action.NAME)); fileMenu.add(exportCurrentToHtmlAction); } else { fileMenu.add(exportToHtmlAction); } fileMenu.add(showConsoleAction); if (userMode == OTConfig.NO_USER_MODE || OTConfig.isDebug()) { fileMenu.add(reloadWindowAction); } JCheckBoxMenuItem debugItem = new JCheckBoxMenuItem(debugAction); debugItem.setSelected(OTConfig.isDebug()); fileMenu.add(debugItem); fileMenu.add(exitAction); return menuBar; } boolean checkForReplace(File file) { if (file == null){ return false; } if (!file.exists()) { return true; // File doesn't exist, so go ahead and save } if (currentAuthoredFile != null && file.compareTo(currentAuthoredFile) == 0){ return true; // we're already authoring this file, so no need to // prompt } final Object[] options = { "Yes", "No" }; return javax.swing.JOptionPane.showOptionDialog(null, "The file '" + file.getName() + "' already exists. " + "Replace existing file?", "Warning", javax.swing.JOptionPane.YES_NO_OPTION, javax.swing.JOptionPane.WARNING_MESSAGE, null, options, options[1]) == javax.swing.JOptionPane.YES_OPTION; } /** * FIXME This method name is confusing. The method first checks for unsaved * changes, and if there are unsaved changes it asks the user if they want to * "don't save", "cancel" or "save" * * @return */ public boolean checkForUnsavedUserData() { if(userSession == null){ return true; } if(userSession.hasUnsavedChanges()) { // show dialog message telling them they haven't // saved their work // FIXME String options[] = { "Don't Save", "Cancel", "Save" }; askedAboutSavingUserData = true; int chosenOption = JOptionPane.showOptionDialog(this, "Save Changes?", "Save Changes?", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[2]); switch (chosenOption) { case 0: logger.info("Not saving work"); break; case 1: logger.info("Canceling close"); return false; case 2: logger.info("Set needToSaveUserData true"); needToSaveUserData = true; break; } } return true; } /** * Checks if the user has unsaved authored data. If they do then it prompts * them to confirm what they are doing. If they cancel then it returns * false. * * @return */ public boolean checkForUnsavedAuthorData() { if (xmlDB != null) { if (xmlDB.isDirty()) { // show dialog message telling them they haven't // saved their work // FIXME String options[] = { "Don't Save", "Cancel", "Save" }; int chosenOption = JOptionPane.showOptionDialog(this, "Save Changes?", "Save Changes?", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[2]); switch (chosenOption) { case 0: logger.info("Not saving authored data"); break; case 1: logger.info("Canceling close"); return false; case 2: logger.info("Saving authored data"); saveAction.actionPerformed(null); // check again if the save was successful // it is possible there was an error and the save was unsuccessful. return checkForUnsavedAuthorData(); } } } return true; } /** * This does not check for unsaved user data * */ public void newAnonUserData() { newUserData(OTViewerHelper.ANON_SINGLE_USER_NAME); } /** * This does not check for unsaved user data, it uses the current OTUserSession object * to create the new user data. If there is no OTUserSession object it will throw an * IllegalStateException * */ public void newUserData(String userName) { // call some new method for creating a new un-saved user state // this should set the currentUserFile to null, so the save check // prompts // for a file name if(userSession == null) { throw new IllegalStateException("a OTUserSession must be supplied before newUserData can be called"); } try { userSession.newLayer(); OTUserObject userObject = userSession.getUserObject(); if(userName != null){ userObject.setName(userName); } // This will request the label from the userSession reloadWindow(); } catch (Exception e) { e.printStackTrace(); } } public boolean exit() { try { if (!checkForUnsavedUserData()) { // the user canceled the operation return false; } if (OTConfig.isAuthorMode() && !checkForUnsavedAuthorData()) { // the user canceled the operation return false; } // FIXME there is a problem with this logic. If the user saved data // just before closing // checkForUnsavedUserData will not see any unsaved data. But if // some view creates // data in the viewClosed method then that data will not get saved // here. // I think the key to solving this is to separate the // automatic/logging data from the // user visible data. And then make a rule that saving data in the // viewClosed method // is not allowed. bodyPanel.setCurrentObject(null); conditionalSaveUserData(); if (otrunk != null) otrunk.close(); } catch (Exception exp) { exp.printStackTrace(); // exit anyhow } System.exit(0); return true; } protected void conditionalSaveUserData() { if (!askedAboutSavingUserData) { checkForUnsavedUserData(); } if (needToSaveUserData) { saveUserDataAction.actionPerformed(null); } else { logger.info("Not saving work before closing."); } // Reset these back to false, so if the user is switching to a new // user or loading a new file we are in a clean state, for that file or // user askedAboutSavingUserData = false; needToSaveUserData = false; } public File getReportFile() { Frame frame = (Frame) SwingUtilities.getRoot(OTViewer.this); MostRecentFileDialog mrfd = new MostRecentFileDialog("org.concord.otviewer.saveotml"); mrfd.setFilenameFilter("html"); if (userSession instanceof OTMLUserSession){ OTMLUserSession otmlUserSession = (OTMLUserSession) userSession; if(otmlUserSession.currentUserFile != null){ mrfd.setCurrentDirectory(otmlUserSession.currentUserFile.getParentFile()); } } int retval = mrfd.showSaveDialog(frame); File file = null; if (retval == MostRecentFileDialog.APPROVE_OPTION) { file = mrfd.getSelectedFile(); String fileName = file.getPath(); if (!fileName.toLowerCase().endsWith(".html")) { file = new File(file.getAbsolutePath() + ".html"); } return file; } return null; } public void createNewUser() { if (!checkForUnsavedUserData()) { // the user canceled the operation return; } // This ensures viewClosed is called bodyPanel.setCurrentObject(null); conditionalSaveUserData(); // call some new method for creating a new un-saved user state // this should set the currentUserFile to null, so the save check // prompts // for a file name newAnonUserData(); exportToHtmlAction.setEnabled(true); } public void openUserData(boolean saveCurrentData) { if(saveCurrentData){ if (!checkForUnsavedUserData()) { // the user canceled the operation return; } // FIXME Calling the method below would insure the view is closed, and // that any data that is // is modified in that view closed operation will get saved, however if // the user // cancels the open dialog then we would be left in an unknown // state. The current view would be closed which they would want to see // again. // bodyPanel.setCurrentObject(null); conditionalSaveUserData(); } if(userSession == null){ throw new IllegalStateException("can't open user data without a userSession"); } boolean success = userSession.open(); try { reloadWindow(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } if(success){ exportToHtmlAction.setEnabled(true); } } public void instructionPanel() { commDialog.setResizable(false); JPanel panel = new JPanel(); panel.setBackground(Color.WHITE); panel.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 0.5; c.insets = new Insets(5,10,5,10); JLabel lNew = new JLabel("Click the \"New\" button to create a new portfolio:"); JLabel lOpen = new JLabel("Click the \"Open\" button to open a saved portfolio:"); JButton bNew = new JButton("New"); JButton bOpen = new JButton("Open"); c.gridx = 0; c.gridy = 0; panel.add(lNew, c); c.gridx = 1; panel.add(bNew, c); c.gridx = 0; c.gridy = 1; panel.add(lOpen, c); c.gridx = 1; panel.add(bOpen, c); bNew.setOpaque(false); bOpen.setOpaque(false); setUserSession(new OTMLUserSession()); bNew.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { commDialog.setVisible(false); newAnonUserData(); exportToHtmlAction.setEnabled(true); } }); bOpen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { commDialog.setVisible(false); openUserData(false); } }); commDialog.getContentPane().add(panel); commDialog.setBounds(200, 200, 450, 200); SwingUtilities.invokeLater(new Runnable() { public void run() { commDialog.setVisible(true); justStarted = false; updateMenuBar(); } }); } public OTViewContainerPanel getViewContainerPanel() { return this.bodyPanel; } public void setExitAction(AbstractAction exitAction) { this.exitAction = exitAction; } /* * (non-Javadoc) * * @see org.concord.applesupport.AppleApplicationAdapter#about() */ public void about() { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see org.concord.applesupport.AppleApplicationAdapter#preferences() */ public void preferences() { // TODO Auto-generated method stub } public void setLaunchedBySailOTViewer(boolean launchedBySailOTViewer){ this.launchedBySailOTViewer = launchedBySailOTViewer; } public boolean isLaunchedBySailOTViewer(){ return launchedBySailOTViewer; } /* * (non-Javadoc) * * @see org.concord.applesupport.AppleApplicationAdapter#quit() */ public void quit() { exitAction.actionPerformed(null); } public OTrunk getOTrunk() { return otrunk; } /** * @param sailSaveEnabled the sailSaveEnabled to set */ public void setSailSaveEnabled(boolean sailSaveEnabled) { this.sailSaveEnabled = sailSaveEnabled; } /** * True is sail saving is enabled * @return the sailSaveEnabled */ public boolean isSailSaveEnabled() { return sailSaveEnabled; } public static void setOTViewFactory(OTViewFactory factory) { otViewFactory = factory; } public void createActions() { newUserDataAction = new AbstractAction("New") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent arg0) { createNewUser(); } }; loadUserDataAction = new AbstractAction("Open...") { /** * nothing to serialize here. Just the parent class. */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent arg0) { openUserData(true); } }; exportToHtmlAction = new AbstractAction("Export to html...") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent arg0) { File fileToSave = getReportFile(); OTMLToXHTMLConverter otxc = new OTMLToXHTMLConverter(otViewFactory, bodyPanel.getViewContainer()); otxc.setXHTMLParams(fileToSave, 800, 600); (new Thread(otxc)).start(); } }; // Isn't it enabled by default? exportToHtmlAction.setEnabled(true); exportCurrentToHtmlAction = new AbstractAction("Export current to html...") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent arg0) { try { File fileToSave = getReportFile(); PageXHTMLExporter otxc = new PageXHTMLExporter(bodyPanel); otxc.setXHTMLParams(fileToSave); (new Thread(otxc)).start(); } catch (Exception e) { logger.log(Level.SEVERE, "Couldn't get the root object from otrunk", e); } } }; // Isn't it enabled by default? exportCurrentToHtmlAction.setEnabled(true); saveUserDataAction = new AbstractAction("Save") { /** * Nothing to serialize here */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent arg0) { userSession.save(); // the save operation might change the label used by the session setTitle(baseFrameTitle + ": " + userSession.getLabel()); } }; saveUserDataAsAction = new AbstractAction("Save As...") { /** * nothing to serizile here */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent arg0) { if(userSession instanceof OTMLUserSession){ ((OTMLUserSession)userSession).setDialogParent(SwingUtilities.getRoot(OTViewer.this)); } userSession.saveAs(); } }; loadAction = new AbstractAction("Open Authored Content...") { /** * nothing to serizile here */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent arg0) { Frame frame = (Frame) SwingUtilities.getRoot(OTViewer.this); MostRecentFileDialog mrfd = new MostRecentFileDialog("org.concord.otviewer.openotml"); mrfd.setFilenameFilter("otml"); int retval = mrfd.showOpenDialog(frame); File file = null; if (retval == MostRecentFileDialog.APPROVE_OPTION) { file = mrfd.getSelectedFile(); } if (file != null && file.exists()) { logger.info("load file name: " + file); // if they open an authored file then they are overriding // the remoteURL, // at least for now. This makes the title bar update // correctly, and // fixes a lockup that happens when they have opened a local // file and then // try to save it again. remoteURL = null; loadFile(file); exportToHtmlAction.setEnabled(true); } } }; loadAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); saveAction = new AbstractAction("Save Authored Content...") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent arg0) { if (OTConfig.isRemoteSaveData() && remoteURL != null) { try { if (OTConfig.isRestEnabled()) { try { otrunk.remoteSaveData(xmlDB, remoteURL, OTViewer.HTTP_PUT); setTitle(remoteURL.toString()); } catch (Exception e) { otrunk.remoteSaveData(xmlDB, remoteURL, OTViewer.HTTP_POST); setTitle(remoteURL.toString()); } } else { otrunk.remoteSaveData(xmlDB, remoteURL, OTViewer.HTTP_POST); setTitle(remoteURL.toString()); } } catch (Exception e) { JOptionPane.showMessageDialog( SwingUtilities.getRoot(OTViewer.this), "There was an error saving. Check your URL and try again.", "Error Saving", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } } else { if (currentAuthoredFile == null) { saveAsAction.actionPerformed(arg0); return; } if (checkForReplace(currentAuthoredFile)) { try { ExporterJDOM.export(currentAuthoredFile, xmlDB.getRoot(), xmlDB); xmlDB.setDirty(false); } catch (Exception e) { handleSavingException(e); } } } // end if (remoteUrl == null) } }; saveAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); saveAsAction = new AbstractAction("Save Authored Content As...") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent arg0) { Frame frame = (Frame) SwingUtilities.getRoot(OTViewer.this); MostRecentFileDialog mrfd = new MostRecentFileDialog("org.concord.otviewer.saveotml"); mrfd.setFilenameFilter("otml"); if (currentAuthoredFile != null) { mrfd.setCurrentDirectory(currentAuthoredFile.getParentFile()); mrfd.setSelectedFile(currentAuthoredFile); } int retval = mrfd.showSaveDialog(frame); File file = null; if (retval == MostRecentFileDialog.APPROVE_OPTION) { file = mrfd.getSelectedFile(); String fileName = file.getPath(); if (!fileName.toLowerCase().endsWith(".otml")) { file = new File(file.getAbsolutePath() + ".otml"); } if (checkForReplace(file)) { try { ExporterJDOM.export(file, xmlDB.getRoot(), xmlDB); currentAuthoredFile = file; currentURL = file.toURL(); xmlDB.setDirty(false); } catch (Exception e) { handleSavingException(e); } } frame.setTitle(fileName); remoteURL = null; updateMenuBar(); } } }; saveRemoteAsAction = new AbstractAction("Save Remotely As...") { /** * nothing to serizile here */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent arg0) { // Pop up a dialog asking for a URL // Post the otml to the url JPanel panel = new JPanel(); panel.setBorder(new EmptyBorder(10, 10, 10, 10)); panel.setLayout(new BorderLayout()); JLabel prompt = new JLabel("Please enter the URL to which you would like to save:"); prompt.setBorder(new EmptyBorder(0, 0, 10, 0)); JTextField textField = new JTextField(); if (remoteURL == null) { textField.setText("http://"); } else { textField.setText(remoteURL.toString()); } JPanel checkboxPanel = new JPanel(); JCheckBox restCheckbox = new JCheckBox("REST Enabled?"); restCheckbox.setSelected(OTConfig.isRestEnabled()); checkboxPanel.setBorder(new EmptyBorder(5, 5, 0, 0)); checkboxPanel.add(restCheckbox); panel.add(prompt, BorderLayout.NORTH); panel.add(textField, BorderLayout.CENTER); panel.add(checkboxPanel, BorderLayout.SOUTH); int returnVal = CustomDialog.showOKCancelDialog( SwingUtilities.getRoot(OTViewer.this), // parent panel, // custom content "Save URL", // title false, // resizeable true // modal ); if (returnVal == 0) { try { remoteURL = new URL(textField.getText()); System.setProperty(OTConfig.REST_ENABLED_PROP, Boolean.toString(restCheckbox.isSelected())); save(OTViewer.HTTP_POST); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { // CANCELLED } } private void save(String method) { try { // WARNING this will cause a security exception if we // are running in a applet or jnlp which // has a security sandbox. otrunk.remoteSaveData(xmlDB, remoteURL, method); setTitle(remoteURL.toString()); updateMenuBar(); } catch (HTTPRequestException e) { if (e.getResponseCode() == 405 && ! method.equals(OTViewer.HTTP_PUT)) { // try again with a PUT since some REST servers don't allow you to POST to an already-created object save(OTViewer.HTTP_PUT); } else { badURL(e); } } catch (Exception e) { badURL(e); } } private void badURL(Exception e) { logger.log(Level.INFO, "Bad URL. Not saving.", e); JOptionPane.showMessageDialog( SwingUtilities.getRoot(OTViewer.this), "There was an error saving. Check your URL and try again.", "Error Saving", JOptionPane.ERROR_MESSAGE); } }; exportImageAction = new AbstractAction("Export Image...") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent e) { // this introduces a dependency on concord Swing project // instead there needs to be a way to added these actions // through // the xml Component currentComp = bodyPanel.getCurrentComponent(); Util.makeScreenShot(currentComp); } }; exportHiResImageAction = new AbstractAction("Export Hi Res Image...") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent e) { Component currentComp = bodyPanel.getCurrentComponent(); Util.makeScreenShot(currentComp, 2, 2); } }; debugAction = new AbstractAction("Debug Mode") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if (((JCheckBoxMenuItem) source).isSelected()) { System.setProperty(OTConfig.DEBUG_PROP, "true"); } else { System.setProperty(OTConfig.DEBUG_PROP, "false"); } try { reloadWindow(); } catch (Exception exp) { exp.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { public void run() { updateMenuBar(); } }); exportToHtmlAction.setEnabled(true); } }; showConsoleAction = new AbstractAction("Show Console") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent e) { if (consoleFrame != null) { consoleFrame.setVisible(true); } } }; reloadWindowAction = new AbstractAction("Reload window") { /** * nothing to serialize here */ private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent e) { try { reload(); } catch (Exception e1) { e1.printStackTrace(); } } }; reloadWindowAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, java.awt.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); exitAction = new ExitAction(); } public static void main(String[] args) { System.setProperty("apple.laf.useScreenMenuBar", "true"); createConsoleFrame(); OTViewer viewer = new OTViewer(); if (OTConfig.getBooleanProp(OTConfig.SINGLE_USER_PROP, false)) { viewer.setUserMode(OTConfig.SINGLE_USER_MODE); } else if (OTConfig.getBooleanProp(OTConfig.NO_USER_PROP, false)) { viewer.setUserMode(OTConfig.NO_USER_MODE); } viewer.initArgs(args); } class ExitAction extends AbstractAction { /** * nothing to serialize here. */ private static final long serialVersionUID = 1L; public ExitAction() { super("Exit"); } public void actionPerformed(ActionEvent e) { // If this suceeds then the VM will exit so // the window will get disposed exit(); } } private void handleSavingException(Exception e) { e.printStackTrace(); UserMessageHandler service = otrunk.getService(UserMessageHandler.class); String title = "Saving Error!"; String message = "There was an error saving!\n"; message += "Please click on the details button and send the text to the developers.\n"; message += "If you were overwritting an existing file, that file should be not have changed.\n"; message += "Any changes made since the last save will be lost when you quit, so if possible\n"; message += "copy and paste your changes into a text editor.\n"; message += "Then restart the application and reload your last saved file.\n"; message += "When quiting the application you'll need to click the 'don't save' button."; if(service instanceof UserMessageHandlerExt1){ StringWriter sw = new StringWriter(); PrintWriter printStream = new PrintWriter(sw); e.printStackTrace(printStream); printStream.close(); ((UserMessageHandlerExt1) service).showMessage(message, title, sw.toString()); } else if(service != null){ service.showMessage(message, title); } } } // @jve:decl-index=0:visual-constraint="10,10" class HtmlFileFilter extends javax.swing.filechooser.FileFilter { @Override public boolean accept(File f) { if (f == null) return false; if (f.isDirectory()) return true; return (f.getName().toLowerCase().endsWith(".html")); } @Override public String getDescription() { return "HTML files"; } } // @jve:decl-index=0:visual-constraint="10,10"