/** * eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the * <e-UCM> research group. * * Copyright 2005-2010 <e-UCM> research group. * * You can access a list of all the contributors to eAdventure at: * http://e-adventure.e-ucm.es/contributors * * <e-UCM> is a research group of the Department of Software Engineering * and Artificial Intelligence at the Complutense University of Madrid * (School of Computer Science). * * C Profesor Jose Garcia Santesmases sn, * 28040 Madrid (Madrid), Spain. * * For more info please visit: <http://e-adventure.e-ucm.es> or * <http://www.e-ucm.es> * * **************************************************************************** * * This file is part of eAdventure, version 2.0 * * eAdventure 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 3 of the License, or * (at your option) any later version. * * eAdventure 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 eAdventure. If not, see <http://www.gnu.org/licenses/>. */ package es.eucm.ead.editor.view; import java.awt.Dimension; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.swing.Action; import javax.swing.JFrame; import javax.swing.JMenuBar; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JSplitPane; import es.eucm.ead.editor.R; import es.eucm.ead.editor.control.Controller; import es.eucm.ead.editor.control.EditorConfig; import es.eucm.ead.editor.model.nodes.CharacterNode; import es.eucm.ead.editor.model.nodes.DependencyNode; import es.eucm.ead.editor.model.nodes.asset.AssetsNode; import es.eucm.ead.editor.model.nodes.asset.CaptionAssetNode; import es.eucm.ead.editor.model.nodes.asset.ImageAssetNode; import es.eucm.ead.editor.view.dock.ClassDockableFactory; import es.eucm.ead.editor.view.dock.ElementPanel; import es.eucm.ead.editor.view.menu.EditMenu; import es.eucm.ead.editor.view.menu.FileMenu; import es.eucm.ead.editor.view.menu.RunMenu; import es.eucm.ead.editor.view.menu.WindowMenu; import es.eucm.ead.editor.view.panel.ActorPanel; import es.eucm.ead.editor.view.panel.RawElementPanel; import es.eucm.ead.editor.view.panel.asset.AssetsPanel; import es.eucm.ead.editor.view.panel.asset.CaptionAssetPanel; import es.eucm.ead.editor.view.panel.asset.ImageAssetPanel; import es.eucm.ead.editor.view.structure.StructureElement; import es.eucm.ead.editor.view.structure.StructurePanel; import es.eucm.ead.editor.view.toolbar.ToolPanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Singleton; import bibliothek.gui.DockStation; import bibliothek.gui.Dockable; import bibliothek.gui.dock.common.CControl; import bibliothek.gui.dock.common.DefaultMultipleCDockable; import bibliothek.gui.dock.common.MultipleCDockable; import bibliothek.gui.dock.common.MultipleCDockableFactory; import bibliothek.gui.dock.common.intern.CDockable; import bibliothek.gui.dock.common.intern.CommonDockable; import bibliothek.gui.dock.common.mode.ExtendedMode; import es.eucm.ead.editor.control.ViewController; import es.eucm.ead.editor.model.nodes.LogNode; import es.eucm.ead.editor.model.nodes.SceneNode; import es.eucm.ead.editor.view.menu.AbstractEditorMenu; import es.eucm.ead.editor.util.i18n.Resource; import es.eucm.ead.editor.util.SwingUtilities; import es.eucm.ead.editor.view.panel.LogPanel; import es.eucm.ead.editor.view.panel.ScenePanel; /** * Default implementation of the main editor window */ @Singleton public class EditorWindow implements ViewController { /** * Logger */ static private Logger logger = LoggerFactory.getLogger(EditorWindow.class); /** * Main left panel in the editor; contains list-of-tools */ private StructurePanel leftPanel; /** * edit menu */ private ToolPanel toolPanel; /** * The menu bar for the editor */ private JMenuBar editorMenuBar; /** * Editor window where everything will be placed */ protected JFrame editorWindow; /** * Dock controller for content views; takes up rightPanel */ protected CControl dockController; /** * Model controller */ protected Controller controller; private void initializeStructurePanel() { for (Action a : controller.getActions()) { if (a instanceof EditMenu.EditorAction) { leftPanel.addElement(new StructureElement(a)); } } leftPanel.commit(); // register panel to listen to changes in the project leftPanel.setController(controller); controller.getProjectController().addChangeListener(leftPanel); leftPanel.processChange(null); } /** * Registers a panel factory associated to a node-type * @param nodeType * @param panelType */ public void registerElementPanelFactory( Class<? extends DependencyNode> nodeType, Class<? extends ElementPanel> panelType) { dockController.addMultipleDockableFactory(nodeType.getName(), new ClassDockableFactory(panelType, nodeType, controller)); } @Override public void restoreViews() { File f = controller.getModel().getLoader().relativeFile("views.xml"); if (f.exists() && f.canRead() && f.length() > 0) { try { dockController.readXML(f); } catch (Exception ex) { logger.error("Could not restore views from {}", f, ex); clearViews(); } } } @Override public void clearViews() { while (dockController.getCDockableCount() > 0) { CDockable c = dockController.getCDockable(0); // We need to access the Core API to find out which other Dockables exist. DockStation parent = c.intern().getDockParent(); // Closing a Dockable may remove the parent DockStation; // first save a list, then delete it Dockable[] children = new Dockable[parent.getDockableCount()]; for (int i = 0; i < children.length; i++) { children[i] = parent.getDockable(i); } for (Dockable child : children) { // Avoids closing stacks if (child instanceof CommonDockable) { CDockable cChild = ((CommonDockable) child).getDockable(); cChild.setVisible(false); } } logger.debug("Cleared one round"); } File f = controller.getModel().getLoader().relativeFile("views.xml"); f.delete(); logger.info("Cleared views"); } @Override public void saveViews() { File f = controller.getModel().getLoader().relativeFile("views.xml"); try { dockController.writeXML(f); } catch (IOException ex) { logger.error("Could not save views into {}", f, ex); } } /** * Sets a title qualifier, used to display (say) currently-edited file. * @param title */ @Override public void setTitleQualifier(String title) { String nextTitle = title + " - " + Messages.main_window_title; editorWindow.setTitle(nextTitle); } /** * Creates a new view of a given category. */ private void createNewView(String id) { logger.info("opening view for #{}...", id); if (dockController.getMultipleDockable(id) != null) { MultipleCDockable d = dockController.getMultipleDockable(id); d.setVisible(true); d.setExtendedMode(ExtendedMode.MAXIMIZED); logger.info("Expect dockable {} to be visible now!", id); } else { DependencyNode node = controller.getModel().getElement(id); ClassDockableFactory f = null; Class parentClass = node.getClass(); while (f == null) { f = (ClassDockableFactory) dockController .getMultipleDockableFactory(parentClass.getName()); parentClass = parentClass.getSuperclass(); } DefaultMultipleCDockable d = f.createDockable(id); dockController.addDockable(id, d); setLocationToRelativesIfPossible(d); d.setVisible(true); } } @Override public void addView(String type, String elementId, boolean reuseExisting) { createNewView(elementId); } @Override public void showWindow() { SwingUtilities.doInEDT(new Runnable() { @Override public void run() { editorWindow.setVisible(true); setSizeAndPosition(); } }); } /** * Current modal dialog, if any */ private JFrame currentModal = null; @Override public void addModalPanel(JPanel modalPanel) { if (currentModal != null) { removeModalPanel(true); } logger .info( "addModal: received a panel with {} components, and size {}x{}; isEDT = {}", new Object[] { modalPanel.getComponentCount(), modalPanel.getPreferredSize().width, modalPanel.getPreferredSize().height, javax.swing.SwingUtilities .isEventDispatchThread() }); currentModal = new JFrame(); currentModal.setUndecorated(true); currentModal.add(modalPanel); currentModal.setLocationRelativeTo(editorWindow); currentModal.setAlwaysOnTop(true); currentModal.pack(); currentModal.setVisible(true); } @Override public void removeModalPanel(boolean cancelChanges) { currentModal.setVisible(false); currentModal.dispose(); currentModal = null; } /** * Create the main window of the editor */ private void createMainWindow() { editorWindow.setTitle(Messages.main_window_title); editorWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, dockController.getContentArea()); editorWindow.add(splitPane); // initialize the menus editorMenuBar = new JMenuBar(); AbstractEditorMenu menus[] = new AbstractEditorMenu[] { new FileMenu(controller), new EditMenu(controller), new RunMenu(controller), new WindowMenu(controller), }; for (AbstractEditorMenu m : menus) { m.initialize(); editorMenuBar.add(m); } editorWindow.setJMenuBar(editorMenuBar); toolPanel = new ToolPanel(controller); editorMenuBar.add(new JSeparator()); editorMenuBar.add(toolPanel.getPanel()); SwingUtilities.doInEDTNow(new Runnable() { @Override public void run() { editorWindow.setVisible(false); } }); } private void setLocationToRelativesIfPossible(MultipleCDockable d) { MultipleCDockableFactory<?, ?> f = d.getFactory(); for (int i = 0; i < dockController.getCDockableCount(); i++) { CDockable c = dockController.getCDockable(i); if ((c instanceof MultipleCDockable) && ((MultipleCDockable) c).getFactory() == f) { d.setLocation(c.getBaseLocation()); break; } } } /** * Set the icons of the applications. * * NOTE: This method does not work in Mac, where applications can no change * the icon programmatically. An application bundle must be used, see: * http://developer.apple.com/library/mac/#documentation/Java/Conceptual/ * Java14Development/03-JavaDeployment/JavaDeployment.html */ private void setIcons() { SwingUtilities.doInEDTNow(new Runnable() { @Override public void run() { List<Image> icons = new ArrayList<Image>(); icons.add(Resource.loadImage(R.Drawable.EditorIcon16x16_png)); icons.add(Resource.loadImage(R.Drawable.EditorIcon32x32_png)); icons.add(Resource.loadImage(R.Drawable.EditorIcon64x64_png)); icons.add(Resource.loadImage(R.Drawable.EditorIcon128x128_png)); editorWindow.setIconImages(icons); } }); } /** * Set the size and position of the editor window */ private void setSizeAndPosition() { EditorConfig ec = controller.getConfig(); final int width = ec .containsKey(EditorConfig.EditorConf.WindowSizeWidth) ? ec .getInt(EditorConfig.EditorConf.WindowSizeWidth) : -1; final int height = ec .containsKey(EditorConfig.EditorConf.WindowSizeHeight) ? ec .getInt(EditorConfig.EditorConf.WindowSizeHeight) : -1; final int xPos = ec.containsKey(EditorConfig.EditorConf.WindowPosX) ? ec .getInt(EditorConfig.EditorConf.WindowPosX) : -1; final int yPos = ec.containsKey(EditorConfig.EditorConf.WindowPosY) ? ec .getInt(EditorConfig.EditorConf.WindowPosY) : -1; SwingUtilities.doInEDTNow(new Runnable() { @Override public void run() { editorWindow.setMinimumSize(new Dimension(640, 400)); Dimension screenSize = Toolkit.getDefaultToolkit() .getScreenSize(); int w = width < 0 ? (int) (screenSize.width * .8f) : width; int h = height < 0 ? (int) (screenSize.height * .8f) : height; int x = xPos < 0 ? (screenSize.width - w) / 2 : xPos; int y = yPos < 0 ? (screenSize.height - h) / 2 : yPos; editorWindow.setSize(w, h); editorWindow.setLocation(x, y); logger.info("Saved window settings {},{} {}x{}", new Object[] { x, y, w, h }); editorWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); editorWindow.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { saveWindowPreferences(); } @Override public void componentMoved(ComponentEvent e) { saveWindowPreferences(); } }); } }); } private void saveWindowPreferences() { EditorConfig ec = controller.getConfig(); ec.put(EditorConfig.EditorConf.WindowSizeWidth, "" + editorWindow.getWidth()); ec.put(EditorConfig.EditorConf.WindowSizeHeight, "" + editorWindow.getHeight()); ec.put(EditorConfig.EditorConf.WindowPosX, "" + editorWindow.getLocationOnScreen().x); ec.put(EditorConfig.EditorConf.WindowPosY, "" + editorWindow.getLocationOnScreen().y); logger.debug("Saved window settings {},{} {}x{}", new Object[] { ec.getInt(EditorConfig.EditorConf.WindowSizeWidth), ec.getInt(EditorConfig.EditorConf.WindowSizeHeight), ec.getInt(EditorConfig.EditorConf.WindowPosX), ec.getInt(EditorConfig.EditorConf.WindowPosY) }); ec.save(null); } /** * Set the actual super-controller. * This will finish building the interface, and then launch it. * * @param controller the main controller, providing access to model, views, * and more */ @Override public void setController(Controller controller) { this.controller = controller; // left panel, right panel, and the splitPane leftPanel = new StructurePanel(); editorWindow = new JFrame(); dockController = new CControl(editorWindow); createMainWindow(); initializeStructurePanel(); // requires main window setIcons(); // Add your panel factories here registerElementPanelFactory(CharacterNode.class, ActorPanel.class); registerElementPanelFactory(DependencyNode.class, RawElementPanel.class); registerElementPanelFactory(ImageAssetNode.class, ImageAssetPanel.class); registerElementPanelFactory(CaptionAssetNode.class, CaptionAssetPanel.class); registerElementPanelFactory(AssetsNode.class, AssetsPanel.class); registerElementPanelFactory(SceneNode.class, ScenePanel.class); registerElementPanelFactory(LogNode.class, LogPanel.class); } }