Java tutorial
/******************************************************************************* * Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com) * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v3 * which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt ******************************************************************************/ package com.opendoorlogistics.studio; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Desktop; import java.awt.Dimension; import java.awt.Event; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Rectangle; import java.awt.TexturePaint; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.beans.PropertyVetoException; import java.io.File; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JSplitPane; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import org.apache.commons.io.FilenameUtils; import org.apache.poi.ss.usermodel.Workbook; import com.opendoorlogistics.api.ExecutionReport; import com.opendoorlogistics.api.ODLApi; import com.opendoorlogistics.api.components.ODLComponent; import com.opendoorlogistics.api.tables.ODLDatastoreAlterable; import com.opendoorlogistics.api.tables.ODLTableAlterable; import com.opendoorlogistics.api.tables.ODLTableDefinition; import com.opendoorlogistics.api.tables.ODLTableReadOnly; import com.opendoorlogistics.codefromweb.DesktopScrollPane; import com.opendoorlogistics.codefromweb.IconToImage; import com.opendoorlogistics.codefromweb.TileInternalFrames; import com.opendoorlogistics.core.AppConstants; import com.opendoorlogistics.core.DisposeCore; import com.opendoorlogistics.core.api.impl.ODLApiImpl; import com.opendoorlogistics.core.api.impl.scripts.ScriptTemplatesImpl; import com.opendoorlogistics.core.components.ODLGlobalComponents; import com.opendoorlogistics.core.components.ODLWizardTemplateConfig; import com.opendoorlogistics.core.scripts.ScriptsProvider; import com.opendoorlogistics.core.scripts.ScriptsProvider.HasScriptsProvider; import com.opendoorlogistics.core.scripts.elements.Script; import com.opendoorlogistics.core.scripts.execution.ExecutionReportImpl; import com.opendoorlogistics.core.tables.ODLDatastoreUndoable; import com.opendoorlogistics.core.tables.ODLDatastoreUndoable.UndoStateChangedListener; import com.opendoorlogistics.core.tables.io.PoiIO; import com.opendoorlogistics.core.tables.io.SupportedFileType; import com.opendoorlogistics.core.tables.io.TableIOUtils; import com.opendoorlogistics.core.tables.memory.ODLDatastoreImpl; import com.opendoorlogistics.core.tables.utils.TableUtils; import com.opendoorlogistics.core.utils.IOUtils; import com.opendoorlogistics.core.utils.images.ImageUtils; import com.opendoorlogistics.core.utils.strings.Strings; import com.opendoorlogistics.core.utils.ui.ExecutionReportDialog; import com.opendoorlogistics.core.utils.ui.LayoutUtils; import com.opendoorlogistics.core.utils.ui.OkCancelDialog; import com.opendoorlogistics.studio.PreferencesManager.PrefKey; import com.opendoorlogistics.studio.components.map.RegisterMapComponent; import com.opendoorlogistics.studio.components.tables.EditableTableComponent; import com.opendoorlogistics.studio.controls.buttontable.ButtonTableDialog; import com.opendoorlogistics.studio.dialogs.AboutBoxDialog; import com.opendoorlogistics.studio.dialogs.ProgressDialog; import com.opendoorlogistics.studio.dialogs.ProgressDialog.OnFinishedSwingThreadCB; import com.opendoorlogistics.studio.internalframes.HasInternalFrames; import com.opendoorlogistics.studio.internalframes.ODLInternalFrame; import com.opendoorlogistics.studio.internalframes.ProgressFrame; import com.opendoorlogistics.studio.panels.FunctionsListPanel; import com.opendoorlogistics.studio.scripts.editor.ScriptEditor; import com.opendoorlogistics.studio.scripts.editor.ScriptWizardActions; import com.opendoorlogistics.studio.scripts.editor.ScriptWizardActions.WizardActionsCallback; import com.opendoorlogistics.studio.scripts.execution.ScriptUIManager; import com.opendoorlogistics.studio.scripts.execution.ScriptUIManagerImpl; import com.opendoorlogistics.studio.scripts.list.ScriptNode; import com.opendoorlogistics.studio.scripts.list.ScriptsPanel; import com.opendoorlogistics.studio.tables.grid.ODLGridFrame; import com.opendoorlogistics.studio.tables.schema.TableSchemaEditor; import com.opendoorlogistics.studio.utils.WindowState; import com.opendoorlogistics.utils.ui.Icons; import com.opendoorlogistics.utils.ui.ODLAction; import com.opendoorlogistics.utils.ui.SimpleAction; public final class AppFrame extends JFrame implements HasInternalFrames, HasScriptsProvider { private BufferedImage background; private final DesktopScrollPane desktopScrollPane; private final JSplitPane splitterTablesScripts; private final JSplitPane splitterLeftPanelMain; private final JDesktopPane desktopPane = new JDesktopPane() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; if (background != null) { TexturePaint paint = new TexturePaint(background, new Rectangle(0, 0, background.getWidth(), background.getHeight())); if (paint != null) { g2d.setPaint(paint); g2d.fill(g2d.getClip()); } } else { g2d.setColor(AppBackground.BACKGROUND_COLOUR); g2d.fillRect(0, 0, (int) getSize().getWidth(), (int) getSize().getHeight()); } // g.drawImage(image, 0, 0, this); } } ; private final DatastoreTablesPanel tables; private final ScriptUIManagerImpl scriptManager; private final ScriptsPanel scriptsPanel; private final JToolBar toolbar = new JToolBar(SwingConstants.VERTICAL); private final List<MyAction> fileActions; private final List<MyAction> editActions; private final ODLApi api = new ODLApiImpl(); private JMenu mnScripts; private LoadedDatastore loaded; private abstract class MyAction extends SimpleAction { private final boolean needsOpenFile; private final KeyStroke accelerator; public MyAction(String name, String tooltip, String smallIconPng, String largeIconPng, boolean needsOpenFile, KeyStroke accelerator) { super(name, tooltip, smallIconPng, largeIconPng); this.needsOpenFile = needsOpenFile; this.accelerator = accelerator; } public void updateEnabled() { setEnabled(needsOpenFile ? loaded != null : true); } } /** * Start the appframe up and add the input components * @param components */ public static void startWithComponents(ODLComponent... components) { InitialiseStudio.initialise(); for (ODLComponent c : components) { ODLGlobalComponents.register(c); } new AppFrame(); } public static void main(String[] args) { InitialiseStudio.initialise(); new AppFrame(); } public AppFrame() { // create frame with desktop pane Container con = getContentPane(); con.setLayout(new BorderLayout()); SwingWorker<BufferedImage, BufferedImage> createBackground = new SwingWorker<BufferedImage, BufferedImage>() { @Override protected BufferedImage doInBackground() throws Exception { // background = new AppBackground().create(); AppBackground ab = new AppBackground(); ab.start(); long lastTime = System.currentTimeMillis(); int lastRendered = 0; while (ab.getNbConsecutiveFails() < 100) { ab.doStep(); long current = System.currentTimeMillis(); if (current - lastTime > 100 && lastRendered != ab.getNbRendered()) { background = ImageUtils.deepCopy(ab.getImage()); publish(background); lastTime = current; lastRendered = ab.getNbRendered(); } } ab.finish(); background = ab.getImage(); return background; } @Override protected void process(List<BufferedImage> chunks) { repaint(); } @Override public void done() { AppFrame.this.repaint(); } }; createBackground.execute(); initWindowPosition(); registerAppFrameDependentComponents(); // then create other objects which might use the components tables = new DatastoreTablesPanel(this); // create scripts panel after registering components scriptManager = new ScriptUIManagerImpl(this); scriptsPanel = new ScriptsPanel(getApi(), PreferencesManager.getSingleton().getScriptsDirectory(), scriptManager); // set my icon setIconImage(Icons.loadFromStandardPath("App logo.png").getImage()); // create actions fileActions = initFileActions(); editActions = initEditActions(); // create left-hand panel with scripts and tables splitterTablesScripts = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tables, scriptsPanel); splitterTablesScripts.setPreferredSize(new Dimension(200, splitterTablesScripts.getPreferredSize().height)); splitterTablesScripts.setResizeWeight(0.5); // split center part into tables/scripts browser on the left and desktop // pane on the right desktopScrollPane = new DesktopScrollPane(desktopPane); splitterLeftPanelMain = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitterTablesScripts, desktopScrollPane); con.add(splitterLeftPanelMain, BorderLayout.CENTER); // add toolbar initToolbar(con); initMenus(); // control close operation to stop changed being lost setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { if (canCloseDatastore()) { dispose(); System.exit(0); } } }); setVisible(true); updateAppearance(); } /** * */ private void registerAppFrameDependentComponents() { // register custom components that need the appframe RegisterMapComponent.register(this); ODLGlobalComponents.register(new EditableTableComponent(this)); } private void initToolbar(Container con) { con.add(toolbar, BorderLayout.WEST); toolbar.setFloatable(false); for (MyAction action : fileActions) { if (action != null && action.getConfig().getLargeicon() != null) { toolbar.add(action); } } for (Action action : editActions) { if (action != null) { toolbar.add(action); } } toolbar.add(initGotoWebsiteAction()); } private SimpleAction initGotoWebsiteAction() { return new SimpleAction("Go to help website", "Go to www.opendoorlogistics for help", "help 16x16.png", "help 32x32.png") { @Override public void actionPerformed(ActionEvent e) { if (Desktop.isDesktopSupported() && Desktop.getDesktop() != null) { Desktop desktop = Desktop.getDesktop(); ExecutionReportImpl report = new ExecutionReportImpl(); try { desktop.browse(java.net.URI.create("www.opendoorlogistics.com")); } catch (Exception e2) { report.setFailed(e2); ExecutionReportDialog.show(AppFrame.this, "Failed to open website", report); } } } }; } private void initWindowPosition() { WindowState screenState = PreferencesManager.getSingleton().getScreenState(); boolean boundsSet = false; GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); if (screenState != null) { setExtendedState(screenState.getExtendedState()); int safety = 20; int screenWidth = gd.getDisplayMode().getWidth(); int screenHeight = gd.getDisplayMode().getHeight(); if (getExtendedState() == JFrame.NORMAL && screenState.getX() < (screenWidth - safety) && (screenState.getY() < screenHeight - safety && screenState.getWidth() <= screenWidth && screenState.getHeight() <= screenHeight)) { boundsSet = true; setBounds(screenState.getX(), screenState.getY(), screenState.getWidth(), screenState.getHeight()); } } // make a fraction of the screen size by default if (!boundsSet && gd != null && getExtendedState() == JFrame.NORMAL) { int screenWidth = gd.getDisplayMode().getWidth(); int screenHeight = gd.getDisplayMode().getHeight(); setSize(3 * screenWidth / 4, 3 * screenHeight / 4); } } public LoadedDatastore getLoaded() { return loaded; } private void initMenus() { final JMenuBar menuBar = new JMenuBar(); class AddSpace { void add() { JMenu dummy = new JMenu(); dummy.setEnabled(false); menuBar.add(dummy); } } AddSpace addSpace = new AddSpace(); // add file menu ... build on the fly for recent files.. setJMenuBar(menuBar); final JMenu mnFile = new JMenu("File"); mnFile.setMnemonic('F'); mnFile.addMenuListener(new MenuListener() { @Override public void menuSelected(MenuEvent e) { initFileMenu(mnFile); } @Override public void menuDeselected(MenuEvent e) { // TODO Auto-generated method stub } @Override public void menuCanceled(MenuEvent e) { // TODO Auto-generated method stub } }); initFileMenu(mnFile); menuBar.add(mnFile); addSpace.add(); // add edit menu JMenu mnEdit = new JMenu("Edit"); mnEdit.setMnemonic('E'); menuBar.add(mnEdit); addSpace.add(); for (MyAction action : editActions) { JMenuItem item = mnEdit.add(action); if (action.accelerator != null) { item.setAccelerator(action.accelerator); } } // add run scripts menu (hidden until a datastore is loaded) mnScripts = new JMenu("Run script"); mnScripts.setMnemonic('R'); mnScripts.setVisible(false); mnScripts.addMenuListener(new MenuListener() { @Override public void menuSelected(MenuEvent e) { mnScripts.removeAll(); for (final ScriptNode item : scriptsPanel.getScripts()) { if (item.isAvailable() == false) { continue; } if (item.isRunnable()) { mnScripts.add(new AbstractAction(item.getDisplayName(), item.getIcon()) { @Override public void actionPerformed(ActionEvent e) { scriptManager.executeScript(item.getFile(), item.getLaunchExecutorId()); } }); } else if (item.getChildCount() > 0) { JMenu popup = new JMenu(item.getDisplayName()); mnScripts.add(popup); for (int i = 0; i < item.getChildCount(); i++) { final ScriptNode child = (ScriptNode) item.getChildAt(i); if (child.isRunnable()) { popup.add(new AbstractAction(child.getDisplayName(), child.getIcon()) { @Override public void actionPerformed(ActionEvent e) { scriptManager.executeScript(child.getFile(), child.getLaunchExecutorId()); } }); } } } } mnScripts.validate(); } @Override public void menuDeselected(MenuEvent e) { } @Override public void menuCanceled(MenuEvent e) { } }); menuBar.add(mnScripts); addSpace.add(); // add create script menu menuBar.add(initCreateScriptsMenu()); addSpace.add(); // add window menu JMenu mnWindow = new JMenu("Window"); mnWindow.setMnemonic('W'); menuBar.add(mnWindow); addSpace.add(); initWindowMenus(mnWindow); menuBar.add(initHelpMenu()); addSpace.add(); } private JMenu initCreateScriptsMenu() { JMenu mnCreateScript = new JMenu("Create script"); mnCreateScript.setMnemonic('C'); for (ODLAction action : new ScriptWizardActions(getApi(), this, scriptManager.getAvailableFieldsQuery()) .createComponentActions(new WizardActionsCallback() { @Override public void onNewScript(Script script) { scriptManager.launchScriptEditor(script, null, null); } })) { mnCreateScript.add(action); } return mnCreateScript; } private JMenu initHelpMenu() { JMenu mnHelp = new JMenu("Help"); mnHelp.setMnemonic('H'); mnHelp.add(initGotoWebsiteAction()); mnHelp.add(new AbstractAction("About ODL Studio") { @Override public void actionPerformed(ActionEvent e) { final AboutBoxDialog dlg = new AboutBoxDialog(AppFrame.this, false); dlg.setLocationRelativeTo(AppFrame.this); dlg.setVisible(true); } }); mnHelp.add(new AbstractAction("List of data adapter functions") { @Override public void actionPerformed(ActionEvent e) { addInternalFrame(FunctionsListPanel.createFrame(), FramePlacement.AUTOMATIC); } }); mnHelp.add(new AbstractAction("List of 3rd party data & libraries") { @Override public void actionPerformed(ActionEvent e) { final AboutBoxDialog dlg = new AboutBoxDialog(AppFrame.this, true); dlg.setTitle("3rd party data & libs used in ODL Studio"); dlg.setLocationRelativeTo(AppFrame.this); dlg.setVisible(true); } }); return mnHelp; } private void initWindowMenus(JMenu mnWindow) { mnWindow.add(new AbstractAction("Tile open windows") { @Override public void actionPerformed(ActionEvent e) { tileWindows(); } }).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, Event.CTRL_MASK)); mnWindow.add(new AbstractAction("Cascade open windows") { @Override public void actionPerformed(ActionEvent e) { cascadeWindows(); } }).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, Event.CTRL_MASK)); mnWindow.add(new AbstractAction("Close all open windows") { @Override public void actionPerformed(ActionEvent e) { closeWindows(); } }); mnWindow.add(new AbstractAction("Minimise all open windows") { @Override public void actionPerformed(ActionEvent e) { minimiseWindows(); } }).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, Event.CTRL_MASK)); mnWindow.add(new AbstractAction("Show all tables") { @Override public void actionPerformed(ActionEvent e) { tileTables(); } }); JMenu mnResizeTo = new JMenu("Resize application to..."); for (final int[] size : new int[][] { new int[] { 1280, 720 }, new int[] { 1920, 1080 } }) { mnResizeTo.add(new AbstractAction("" + size[0] + " x " + size[1]) { @Override public void actionPerformed(ActionEvent e) { // set standard layout setSize(size[0], size[1]); splitterLeftPanelMain.setDividerLocation(0.175); splitterTablesScripts.setDividerLocation(0.3); } }); } mnWindow.add(mnResizeTo); } private void importFile(SupportedFileType option) { // to do.. excel import needs to show progress dialog (as its slow) final JFileChooser chooser = option.createFileChooser(); IOUtils.setFile(PreferencesManager.getSingleton().getLastImportFile(option), chooser); final ExecutionReport report = new ExecutionReportImpl(); if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { try { PreferencesManager.getSingleton().setLastImportFile(chooser.getSelectedFile(), option); ODLDatastoreAlterable<ODLTableAlterable> imported = TableIOUtils .importFile(chooser.getSelectedFile(), option, report); if (imported == null) { throw new RuntimeException(); } if (loaded == null) { openEmptyDatastore(); } if (!TableUtils.addDatastores(loaded.getDs(), imported, true)) { throw new RuntimeException(); } for (int i = 0; i < imported.getTableCount(); i++) { ODLTableReadOnly table = imported.getTableAt(i); report.log("Imported table \"" + table.getName() + "\" with " + table.getRowCount() + " rows and " + table.getColumnCount() + " columns."); } report.log("Imported " + imported.getTableCount() + " tables."); } catch (Throwable e) { report.setFailed(e); report.log("Error importing " + Strings.convertEnumToDisplayFriendly(option)); report.log("Could not import file: " + chooser.getSelectedFile().getAbsolutePath()); String message = report.getReportString(true, false); if (message.length() > 0) { message += System.lineSeparator(); } } ExecutionReportDialog.show(this, "Import result", report); } } void updateAppearance() { for (MyAction action : fileActions) { if (action != null) { action.updateEnabled(); } } for (MyAction action : editActions) { action.updateEnabled(); } String title = AppConstants.WEBSITE; if (loaded != null) { title += " - "; if (loaded.getLastFile() != null) { title += loaded.getLastFile(); } else { title += "untitled"; } } setTitle(title); tables.setEnabled(loaded != null); mnScripts.setVisible(loaded != null); scriptsPanel.updateAppearance(); } private void openDatastoreWithUserPrompt() { if (!canCloseDatastore()) { return; } JFileChooser chooser = SupportedFileType.EXCEL.createFileChooser(); File defaultDir = PreferencesManager.getSingleton().getFile(PrefKey.LAST_IO_DIR); if (defaultDir != null) { IOUtils.setFile(defaultDir, chooser); } if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { final File file = chooser.getSelectedFile(); openFile(file); } updateAppearance(); } private void openFile(final File file) { class WorkbookDatastore extends AbstractMap.SimpleEntry<Workbook, ODLDatastoreAlterable<ODLTableAlterable>> { public WorkbookDatastore(Workbook key, ODLDatastoreAlterable<ODLTableAlterable> value) { super(key, value); } } String message = "Loading " + file; ProgressDialog<WorkbookDatastore> pd = new ProgressDialog<>(AppFrame.this, message, false); pd.setLocationRelativeTo(this); pd.setText("Loading file, please wait."); final ExecutionReport report = new ExecutionReportImpl(); pd.start(new Callable<WorkbookDatastore>() { @Override public WorkbookDatastore call() throws Exception { try { ODLDatastoreAlterable<ODLTableAlterable> ret = ODLDatastoreImpl.alterableFactory.create(); Workbook wb = PoiIO.importExcel(file, ret, report); return new WorkbookDatastore(wb, ret); } catch (Throwable e) { report.setFailed(e); return null; } } }, new OnFinishedSwingThreadCB<WorkbookDatastore>() { @Override public void onFinished(WorkbookDatastore result, boolean userCancelled, boolean userFinishedNow) { if (result != null) { onOpenedDatastore(result.getValue(), result.getKey(), file); PreferencesManager.getSingleton().addRecentFile(file); PreferencesManager.getSingleton().setDirectory(PrefKey.LAST_IO_DIR, file); } else { report.setFailed("Could not open file " + file.getAbsolutePath()); ExecutionReportDialog.show(AppFrame.this, "Error opening file", report); } } }); } private boolean canCloseDatastore() { if (loaded == null) { return true; } if (loaded.isModified()) { if (JOptionPane.showConfirmDialog(this, "Spreadsheet has been modified but not saved. Continue without saving?", "Changes will be lost!", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { return false; } } return true; } private void closeDatastore() { setTitle(""); tables.onDatastoreClosed(); if (loaded != null) { loaded.getDs().removeListener(scriptManager); loaded.dispose(); loaded = null; } for (JInternalFrame frame : desktopPane.getAllFrames()) { if (ProgressFrame.class.isInstance(frame)) { // cancel running operations... ((ProgressFrame) frame).getProgressPanel().cancel(); } else if (ScriptEditor.class.isInstance(frame) == false) { frame.dispose(); } } scriptManager.datastoreStructureChanged(); updateAppearance(); } private void closeWindows() { for (JInternalFrame frame : desktopPane.getAllFrames()) { if (ScriptEditor.class.isInstance(frame)) { ((ScriptEditor) frame).disposeWithSavePrompt(); } else { frame.dispose(); } } } private void createNewDatastore() { if (!canCloseDatastore()) { return; } ArrayList<JButton> buttons = new ArrayList<>(); buttons.add(new JButton(new AbstractAction("Create empty datastore") { @Override public void actionPerformed(ActionEvent e) { openEmptyDatastore(); } })); // buttons.add(new JButton(new AbstractAction("Create example datastore") { // // @Override // public void actionPerformed(ActionEvent e) { // onOpenedDatastore(ExampleData.createExampleDatastore(false), null, null); // } // })); for (final String exampleDs : new String[] { "Customers" // , "Sales territories" // disable sales territories for the moment as it takes 30 seconds to load! }) { buttons.add(new JButton(new AbstractAction("Create example " + exampleDs + " datastore") { @Override public void actionPerformed(ActionEvent e) { onOpenedDatastore(TableIOUtils.importExampleDatastore(exampleDs + ".xlsx", null), null, null); } })); } // buttons.add(new JButton(new AbstractAction("Run datastore creation script wizard") { // // @Override // public void actionPerformed(ActionEvent e) { // Script script = WizardUtils.createTableCreationScript(); // scriptManager.launchScriptEditor(script, null); // } // })); // for (final File file : scriptsPanel.getScriptsByType(ScriptType.CREATE_TABLES)) { // buttons.add(new JButton(new AbstractAction("Run script \"" + file.getName() + "\"") { // // @Override // public void actionPerformed(ActionEvent e) { // openEmptyDatastore(); // // // run script // scriptManager.executeScript(file); // // } // })); // // } launchButtonsListDialog("Create new spreadsheet", "Choose creation option:", null, buttons); } private void launchButtonsListDialog(String popupTitle, String dialogMessage, Icon icon, List<JButton> buttons) { OkCancelDialog dlg = new ButtonTableDialog(this, dialogMessage, buttons.toArray(new JButton[buttons.size()])); dlg.setTitle(popupTitle); dlg.setLocationRelativeTo(this); if (icon != null) { Image image = IconToImage.iconToImage(icon); if (image != null) { dlg.setIconImage(image); } } dlg.showModal(); } private void saveDatastoreWithoutUserPrompt(File file) { String ext = FilenameUtils.getExtension(file.getAbsolutePath()).toLowerCase(); // ensure we have spreadsheet extension if (!ext.equals("xls") && !ext.equals("xlsx")) { ext = "xlsx"; String filename = FilenameUtils.removeExtension(file.getAbsolutePath()) + "." + ext; file = new File(filename); } final File finalFile = file; final String finalExt = ext; String message = "Saving " + file; ProgressDialog<Boolean> pd = new ProgressDialog<>(AppFrame.this, message, false); pd.setLocationRelativeTo(this); pd.setText("Saving file, please wait."); final ExecutionReport report = new ExecutionReportImpl(); pd.start(new Callable<Boolean>() { @Override public Boolean call() throws Exception { // return PoiIO.export(loaded.getDs(), finalFile, // finalExt.equals("xlsx")); try { return loaded.save(finalFile, finalExt.equals("xlsx"), report); } catch (Throwable e) { report.setFailed(e); return false; } } }, new OnFinishedSwingThreadCB<Boolean>() { @Override public void onFinished(Boolean result, boolean userCancelled, boolean userFinishedNow) { if (result == false) { report.setFailed("Could not save file " + finalFile.getAbsolutePath()); ExecutionReportDialog.show(AppFrame.this, "Error saving file", report); } else { loaded.onSaved(finalFile); if (report.size() > 0) { ExecutionReportDialog.show(AppFrame.this, "Warning saving file", report); } PreferencesManager.getSingleton().addRecentFile(finalFile); PreferencesManager.getSingleton().setDirectory(PrefKey.LAST_IO_DIR, finalFile); } updateAppearance(); } }); } private void onOpenedDatastore(ODLDatastoreAlterable<ODLTableAlterable> newDs, Workbook workbook, File file) { if (loaded != null) { closeDatastore(); } loaded = new LoadedDatastore(newDs, workbook, file, this); tables.setDatastore(loaded.getDs()); // loaded.lastSaveVersionNumber = loaded.ds.getDataVersion(); loaded.getDs().addListener(scriptManager); loaded.getDs().addUndoStateListener(new UndoStateChangedListener<ODLTableAlterable>() { @Override public void undoStateChanged(ODLDatastoreUndoable<ODLTableAlterable> datastoreUndoable) { Runnable runnable = new Runnable() { @Override public void run() { for (MyAction action : editActions) { action.updateEnabled(); } } }; if (SwingUtilities.isEventDispatchThread()) { runnable.run(); } else { SwingUtilities.invokeLater(runnable); } } }); updateAppearance(); scriptManager.datastoreStructureChanged(); } // Based on // http://www.javalobby.org/forums/thread.jspa?threadID=15690&tstart=0 private void cascadeWindows() { JInternalFrame[] frames = desktopPane.getAllFrames(); Rectangle dBounds = desktopPane.getBounds(); int separation = 40; // make standard size which is 2/3 of available width int width = Math.max(100, 2 * dBounds.width / 3); int height = Math.max(100, 2 * dBounds.width / 3); for (int i = 0; i < frames.length; i++) { try { frames[i].setIcon(false); } catch (PropertyVetoException e) { } frames[i].setBounds(i * separation, i * separation, width, height); frames[i].toFront(); } } @Override public void dispose() { PreferencesManager.getSingleton().setScreenState(new WindowState(this)); // dispose all child windows to save their screen state for (JInternalFrame frame : desktopPane.getAllFrames()) { frame.dispose(); } DisposeCore.dispose(); super.dispose(); } private void tileTables() { if (loaded != null) { // launch all tables ArrayList<JInternalFrame> frames = new ArrayList<>(); for (int i = 0; i < loaded.getDs().getTableCount(); i++) { JInternalFrame frame = (JInternalFrame) launchTableGrid( loaded.getDs().getTableAt(i).getImmutableId()); if (frame != null) { frames.add(frame); } } TileInternalFrames.tile(desktopPane, frames.toArray(new JInternalFrame[frames.size()])); } } private void minimiseWindows() { for (JInternalFrame frame : desktopPane.getAllFrames()) { try { frame.setIcon(true); } catch (PropertyVetoException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // Based on http://www.javalobby.org/java/forums/t15696.html private void tileWindows() { JInternalFrame[] frames = desktopPane.getAllFrames(); if (frames.length == 0) { return; } TileInternalFrames.tile(desktopPane, frames); } private List<MyAction> initEditActions() { ArrayList<MyAction> ret = new ArrayList<>(); ret.add(new MyAction("Undo", "Undo last action", null, "edit-undo-7-32x32.png", true, KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.CTRL_MASK)) { @Override public void actionPerformed(ActionEvent e) { loaded.getDs().undo(); } @Override public void updateEnabled() { setEnabled(loaded != null && loaded.getDs().hasUndo()); } }); ret.add(new MyAction("Redo", "Redo last undone action", null, "edit-redo-7-32x32.png", true, KeyStroke.getKeyStroke(KeyEvent.VK_Y, Event.CTRL_MASK)) { @Override public void actionPerformed(ActionEvent e) { loaded.getDs().redo(); } @Override public void updateEnabled() { setEnabled(loaded != null && loaded.getDs().hasRedo()); } }); return ret; } @SuppressWarnings("serial") private List<MyAction> initFileActions() { ArrayList<MyAction> ret = new ArrayList<>(); ret.add(new MyAction("New", "Create new file", null, "document-new-6.png", false, KeyStroke.getKeyStroke(KeyEvent.VK_N, Event.CTRL_MASK)) { @Override public void actionPerformed(ActionEvent e) { createNewDatastore(); } }); ret.add(new MyAction("Open", "Open file", null, "document-open-3.png", false, KeyStroke.getKeyStroke(KeyEvent.VK_O, java.awt.Event.CTRL_MASK)) { @Override public void actionPerformed(ActionEvent e) { openDatastoreWithUserPrompt(); } }); ret.add(null); ret.add(new MyAction("Close", "Close file", null, "document-close-4.png", true, KeyStroke.getKeyStroke(KeyEvent.VK_W, java.awt.Event.CTRL_MASK)) { @Override public void actionPerformed(ActionEvent e) { if (!canCloseDatastore()) { return; } closeDatastore(); } }); ret.add(null); ret.add(new MyAction("Save", "Save file", null, "document-save-2.png", true, KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK)) { @Override public void actionPerformed(ActionEvent e) { saveDatastoreWithoutUserPrompt(loaded.getLastFile()); } @Override public void updateEnabled() { setEnabled(loaded != null && loaded.getLastFile() != null); } }); ret.add(new MyAction("Save as", "Save file as", null, "document-save-as-2.png", true, KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK | Event.ALT_MASK)) { @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = SupportedFileType.EXCEL.createFileChooser(); if (loaded.getLastFile() != null) { chooser.setSelectedFile(loaded.getLastFile()); } else { File file = PreferencesManager.getSingleton().getFile(PrefKey.LAST_IO_DIR); IOUtils.setFile(file, chooser); } if (chooser.showSaveDialog(AppFrame.this) == JFileChooser.APPROVE_OPTION) { saveDatastoreWithoutUserPrompt(chooser.getSelectedFile()); } } }); return ret; } public JInternalFrame[] getInternalFrames() { return desktopPane.getAllFrames(); } @Override public void addInternalFrame(JInternalFrame frame, FramePlacement placement) { desktopPane.add(frame); frame.pack(); frame.setVisible(true); // if(ScriptEditor.class.isInstance(frame)){ // try { // frame.setMaximum(true); // } catch (PropertyVetoException e) { // } // } // else{ // WindowState state = PreferencesManager.getSingleton().getWindowState(frame) if (placement == FramePlacement.AUTOMATIC) { boolean placed = false; if (ODLInternalFrame.class.isInstance(frame)) { ODLInternalFrame odlFrame = (ODLInternalFrame) frame; placed = odlFrame.placeInLastPosition(desktopScrollPane.getViewport().getBounds()); } if (!placed) { LayoutUtils.placeInternalFrame(desktopPane, frame); } } else if (placement == FramePlacement.CENTRAL) { Dimension desktopSize = desktopPane.getSize(); Dimension frameSize = frame.getSize(); int x = (desktopSize.width - frameSize.width) / 2; int y = (desktopSize.height - frameSize.height) / 2; frame.setLocation(x, y); } else if (placement == FramePlacement.CENTRAL_RANDOMISED) { Dimension desktopSize = desktopPane.getSize(); Dimension frameSize = frame.getSize(); Dimension remaining = new Dimension(Math.max(0, desktopSize.width - frameSize.width), Math.max(0, desktopSize.height - frameSize.height)); Dimension halfRemaining = new Dimension(remaining.width / 2, remaining.height / 2); Random random = new Random(); int x = remaining.width / 4 + random.nextInt(halfRemaining.width); int y = remaining.height / 4 + random.nextInt(halfRemaining.height); frame.setLocation(x, y); } frame.toFront(); } void launchTableSchemaEditor(int tableId) { if (loaded != null) { for (JInternalFrame frame : desktopPane.getAllFrames()) { if (TableSchemaEditor.class.isInstance(frame) && ((TableSchemaEditor) frame).getTableId() == tableId) { frame.setVisible(true); frame.moveToFront(); try { frame.setMaximum(true); } catch (Throwable e) { throw new RuntimeException(e); } return; } } TableSchemaEditor gf = new TableSchemaEditor(loaded.getDs(), tableId); addInternalFrame(gf, FramePlacement.AUTOMATIC); } } JComponent launchTableGrid(int tableId) { if (loaded != null) { for (JInternalFrame frame : desktopPane.getAllFrames()) { if (ODLGridFrame.class.isInstance(frame) && ((ODLGridFrame) frame).getTableId() == tableId) { frame.setVisible(true); frame.moveToFront(); try { frame.setMaximum(true); } catch (Throwable e) { throw new RuntimeException(e); } // if not within visible areas, then recentre? return frame; } } ODLTableDefinition table = loaded.getDs().getTableByImmutableId(tableId); if (table != null) { ODLGridFrame gf = new ODLGridFrame(loaded.getDs(), table.getImmutableId(), true, null, loaded.getDs(), this); addInternalFrame(gf, FramePlacement.AUTOMATIC); return gf; } } return null; } void launchScriptWizard(final int tableIds[], final ODLComponent component) { // final ODLTableDefinition dfn = (tableId != -1 && loaded != null) ? loaded.getDs().getTableByImmutableId(tableId) : null; // create button to launch the wizard ArrayList<JButton> buttons = new ArrayList<>(); for (final ODLWizardTemplateConfig config : ScriptTemplatesImpl.getTemplates(getApi(), component)) { Action action = new AbstractAction( "Launch wizard \"" + config.getName() + "\" to configure new script") { @Override public void actionPerformed(ActionEvent e) { Script script = ScriptWizardActions.createScriptFromMasterComponent(getApi(), AppFrame.this, component, config, loaded != null ? loaded.getDs() : null, tableIds); if (script != null) { // ScriptEditor dlg = new ScriptEditorWizardGenerated(script, null, getScriptUIManager()); // AppFrame.this.addInternalFrame(dlg); scriptManager.launchScriptEditor(script, null, null); } } }; JButton button = new JButton(action); button.setToolTipText(config.getDescription()); buttons.add(button); } // launch dialog to select the option if (buttons.size() > 1) { launchButtonsListDialog(component.getName(), "Choose \"" + component.getName() + "\" option:", component.getIcon(getApi(), ODLComponent.MODE_DEFAULT), buttons); } else { // pick the only option... buttons.get(0).doClick(); } } public void openEmptyDatastore() { onOpenedDatastore(ODLDatastoreImpl.alterableFactory.create(), null, null); } public ScriptUIManager getScriptUIManager() { return scriptManager; } private void initFileMenu(JMenu mnFile) { mnFile.removeAll(); // non-dynamic for (MyAction action : fileActions) { if (action == null) { mnFile.addSeparator(); } else { JMenuItem item = mnFile.add(action); if (action.accelerator != null) { item.setAccelerator(action.accelerator); } } } // import (not in action list as doesn't appear on toolbar) mnFile.addSeparator(); JMenu mnImport = new JMenu("Import"); mnFile.add(mnImport); class ImportPair { String menuString; SupportedFileType type; public ImportPair(String menuString, SupportedFileType type) { super(); this.menuString = menuString; this.type = type; } } for (final ImportPair type : new ImportPair[] { new ImportPair("Comma separated (CSV) text", SupportedFileType.CSV), new ImportPair("Tab separated text", SupportedFileType.TABBED), new ImportPair("Excel", SupportedFileType.EXCEL), new ImportPair("Shapefile (link geometry to original file)", SupportedFileType.SHAPEFILE_LINKED_GEOM), new ImportPair("Shapefile (copy geometry into spreadsheet)", SupportedFileType.SHAPEFILE_COPIED_GEOM), }) { mnImport.add(new AbstractAction(type.menuString) { @Override public void actionPerformed(ActionEvent e) { importFile(type.type); } }); } // dynamic mnFile.addSeparator(); List<File> recent = PreferencesManager.getSingleton().getRecentFiles(); for (int i = 0; i < recent.size(); i++) { final File file = recent.get(i); String s = Integer.toString(i + 1) + ". " + file.getAbsolutePath(); int maxLen = 100; if (s.length() > maxLen) { s = s.substring(0, maxLen) + "..."; } mnFile.add(new MyAction(s, "Load file " + file.getAbsolutePath(), null, null, false, null) { @Override public void actionPerformed(ActionEvent e) { if (!canCloseDatastore()) { return; } openFile(file); updateAppearance(); } }); } // clear recent mnFile.addSeparator(); mnFile.add(new MyAction("Clear recent files", "Clear recent files", null, null, false, null) { @Override public void actionPerformed(ActionEvent e) { PreferencesManager.getSingleton().clearRecentFiles(); } }); // finally exit mnFile.addSeparator(); JMenuItem item = mnFile.add(new MyAction("Exit", "Exit", null, null, false, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.Event.CTRL_MASK)) { @Override public void actionPerformed(ActionEvent e) { dispose(); System.exit(0); } }); item.setAccelerator(((MyAction) item.getAction()).accelerator); mnFile.validate(); } public ODLApi getApi() { return api; } @Override public ScriptsProvider getScriptsProvider() { return scriptsPanel.getScriptsProvider(); } }