/*******************************************************************************
* 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.appframe;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
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.JPanel;
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 com.opendoorlogistics.api.ExecutionReport;
import com.opendoorlogistics.api.IO;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.api.app.DatastoreModifier;
import com.opendoorlogistics.api.app.ODLAppLoadedState;
import com.opendoorlogistics.api.app.ui.BeanEditorFactory;
import com.opendoorlogistics.api.app.ui.ODLAppUI;
import com.opendoorlogistics.api.app.ui.UIAction;
import com.opendoorlogistics.api.components.ODLComponent;
import com.opendoorlogistics.api.components.ProcessingApi;
import com.opendoorlogistics.api.io.ImportFileType;
import com.opendoorlogistics.api.tables.DatastoreManagerPlugin;
import com.opendoorlogistics.api.tables.DatastoreManagerPlugin.DatastoreManagerPluginState;
import com.opendoorlogistics.api.tables.DatastoreManagerPlugin.ProcessDatastoreResult;
import com.opendoorlogistics.api.tables.HasUndoStateListeners.UndoStateChangedListener;
import com.opendoorlogistics.api.tables.ODLDatastoreAlterable;
import com.opendoorlogistics.api.tables.ODLDatastoreUndoable;
import com.opendoorlogistics.api.tables.ODLTableAlterable;
import com.opendoorlogistics.api.tables.ODLTableDefinition;
import com.opendoorlogistics.api.tables.beans.BeanMappedRow;
import com.opendoorlogistics.codefromweb.IconToImage;
import com.opendoorlogistics.core.AppConstants;
import com.opendoorlogistics.core.AppProperties;
import com.opendoorlogistics.core.CommandLineInterface;
import com.opendoorlogistics.core.DisposeCore;
import com.opendoorlogistics.core.api.impl.IOImpl;
import com.opendoorlogistics.core.api.impl.ODLApiImpl;
import com.opendoorlogistics.core.api.impl.scripts.ScriptTemplatesImpl;
import com.opendoorlogistics.core.cache.ApplicationCache;
import com.opendoorlogistics.core.components.ODLGlobalComponents;
import com.opendoorlogistics.core.components.ODLWizardTemplateConfig;
import com.opendoorlogistics.core.distances.DistancesSingleton;
import com.opendoorlogistics.core.scripts.ScriptsProvider;
import com.opendoorlogistics.core.scripts.elements.Script;
import com.opendoorlogistics.core.scripts.execution.ExecutionReportImpl;
import com.opendoorlogistics.core.tables.DatastoreManagerGlobalPlugin;
import com.opendoorlogistics.core.tables.decorators.datastores.DataUpdaterDecorator;
import com.opendoorlogistics.core.tables.decorators.datastores.ListenerDecorator;
import com.opendoorlogistics.core.tables.decorators.datastores.deepcopying.OptimisedDeepCopierDecorator;
import com.opendoorlogistics.core.tables.decorators.datastores.undoredo.UndoRedoDecorator;
import com.opendoorlogistics.core.tables.memory.ODLDatastoreImpl;
import com.opendoorlogistics.core.tables.utils.ExampleData;
import com.opendoorlogistics.core.utils.IOUtils;
import com.opendoorlogistics.core.utils.ui.ExecutionReportDialog;
import com.opendoorlogistics.core.utils.ui.OkCancelDialog;
import com.opendoorlogistics.core.utils.ui.TextInformationDialog;
import com.opendoorlogistics.studio.DatastoreTablesPanel;
import com.opendoorlogistics.studio.DropFileImporterListener;
import com.opendoorlogistics.studio.InitialiseStudio;
import com.opendoorlogistics.studio.LoadedState;
import com.opendoorlogistics.studio.PreferencesManager;
import com.opendoorlogistics.studio.PreferencesManager.PrefKey;
import com.opendoorlogistics.studio.controls.buttontable.ButtonTableDialog;
import com.opendoorlogistics.studio.dialogs.ProgressDialog;
import com.opendoorlogistics.studio.dialogs.ProgressDialog.OnFinishedSwingThreadCB;
import com.opendoorlogistics.studio.internalframes.ProgressFrame;
import com.opendoorlogistics.studio.panels.ProgressPanel;
import com.opendoorlogistics.studio.scripts.editor.ScriptEditor;
import com.opendoorlogistics.studio.scripts.editor.ScriptWizardActions;
import com.opendoorlogistics.studio.scripts.execution.PluginDatastoreModifierTask;
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.custom.CustomTableEditorFrame;
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;
public class AppFrame extends DesktopAppFrame {
public static void main(String[] args) {
InitialiseStudio.initialise(true);
if (!CommandLineInterface.process(args)) {
new AppFrame();
}
}
private final JSplitPane splitterLeftSide;
private final JSplitPane splitterMain;
private volatile boolean haltJVMOnDispose = true;
private final DatastoreTablesPanel tables;
private final ScriptUIManagerImpl scriptManager;
private final ScriptsPanel scriptsPanel;
private final JToolBar mainToolbar = new JToolBar(SwingConstants.VERTICAL);
private final ODLApi api = new ODLApiImpl(){
private final IO io = new IOImpl(){
@Override
public File getLoadedExcelFile() {
return loaded!=null ? loaded.getFile():null;
}
};
@Override
public IO io() {
return io;
}
};
private final List<UIAction> allActions = new ArrayList<UIAction>();
private final AppPermissions appPermissions;
private List<NewDatastoreProvider> newDatastoreProviders = NewDatastoreProvider.createDefaults();
private JMenu mnScripts;
private LoadedState loaded;
private boolean datastoreCloseNeedsUseConfirmation = true;
/**
* Start the appframe up and add the input components
*
* @param components
*/
public static void startWithComponents(ODLComponent... components) {
InitialiseStudio.initialise(true);
for (ODLComponent c : components) {
ODLGlobalComponents.register(c);
}
new AppFrame();
}
public AppFrame() {
this(new ActionFactory(), new MenuFactory(), null, null);
}
public AppFrame(ActionFactory actionFactory, MenuFactory menuFactory, Image appIcon, AppPermissions permissions) {
if (appIcon == null) {
appIcon = Icons.loadFromStandardPath("App logo.png").getImage();
}
if (permissions == null) {
permissions = new AppPermissions() {
@Override
public boolean isScriptEditingAllowed() {
return true;
}
@Override
public boolean isScriptDirectoryLocked() {
return false;
}
};
}
this.appPermissions = permissions;
// then create other objects which might use the components
tables = new DatastoreTablesPanel(this);
// create scripts panel after registering components
scriptManager = new ScriptUIManagerImpl(this);
File scriptDir;
if (appPermissions.isScriptDirectoryLocked()) {
scriptDir = new File(AppConstants.SCRIPTS_DIRECTORY).getAbsoluteFile();
} else {
scriptDir = PreferencesManager.getSingleton().getScriptsDirectory();
}
scriptsPanel = new ScriptsPanel(getApi(), scriptDir, scriptManager);
// set my icon
if (appIcon != null) {
setIconImage(appIcon);
}
// create actions - this assumes that actions live for the lifetime of the app
List<UIAction> fileActions = actionFactory.createFileActions(this);
allActions.addAll(fileActions);
List<UIAction> editActions = actionFactory.createEditActions(this);
allActions.addAll(editActions);
// create left-hand panel with scripts and tables
splitterLeftSide = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tables, scriptsPanel);
splitterLeftSide.setPreferredSize(new Dimension(200, splitterLeftSide.getPreferredSize().height));
splitterLeftSide.setResizeWeight(0.5);
// split center part into tables/scripts browser on the left and desktop
// pane on the right
JPanel rightPane = new JPanel();
rightPane.setLayout(new BorderLayout());
rightPane.add(getDesktopPane(), BorderLayout.CENTER);
rightPane.add(getWindowToolBar(), BorderLayout.SOUTH);
splitterMain = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitterLeftSide, rightPane);
getContentPane().add(splitterMain, BorderLayout.CENTER);
// add toolbar
initToolbar(actionFactory, fileActions, editActions);
initMenus(actionFactory, menuFactory, fileActions, editActions);
// 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();
if (haltJVMOnDispose) {
System.exit(0);
}
}
}
});
// add myself as a drop target for importing excels etc from file
new DropTarget(this, new DropFileImporterListener(this));
setVisible(true);
updateAppearance();
// start the software checker on a latter event so its called after UI has been drawn
if(AppProperties.getBool("checkforupdates")!=Boolean.FALSE){
SwingUtilities.invokeLater(()->new UpdatedSoftwareChecker(AppFrame.this).start());
}
}
private void initToolbar(ActionFactory actionBuilder, List<? extends Action> fileActions, List<? extends Action> editActions) {
getContentPane().add(mainToolbar, BorderLayout.WEST);
mainToolbar.setFloatable(false);
mainToolbar.removeAll();
for (Action action : fileActions) {
if (action != null && action.getValue(Action.LARGE_ICON_KEY)!= null) {
mainToolbar.add(action);
}
}
for (Action action : editActions) {
if (action != null && action.getValue(Action.LARGE_ICON_KEY)!= null) {
mainToolbar.add(action);
}
}
Action helpsite = actionBuilder.createGotoWebsiteAction(this);
if (helpsite != null) {
mainToolbar.add(helpsite);
}
mainToolbar.revalidate();
mainToolbar.repaint();
}
@Override
public LoadedState getLoadedDatastore() {
return loaded;
}
private void initMenus(ActionFactory actionBuilder, MenuFactory menuBuilder, List<? extends Action> fileActions, List<? extends Action> editActions) {
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, fileActions, actionBuilder, menuBuilder);
}
@Override
public void menuDeselected(MenuEvent e) {
// TODO Auto-generated method stub
}
@Override
public void menuCanceled(MenuEvent e) {
// TODO Auto-generated method stub
}
});
initFileMenu(mnFile, fileActions, actionBuilder, menuBuilder);
menuBar.add(mnFile);
addSpace.add();
// add edit menu
JMenu mnEdit = new JMenu("Edit");
mnEdit.setMnemonic('E');
menuBar.add(mnEdit);
addSpace.add();
for (Action action : editActions) {
mnEdit.add(action);
// if (action.accelerator != null) {
// item.setAccelerator(action.accelerator);
// }
}
// add run scripts menu (hidden until a datastore is loaded)
mnScripts = new JMenu(appPermissions.isScriptEditingAllowed() ? "Run script" : "Run");
mnScripts.setMnemonic('R');
mnScripts.setVisible(false);
mnScripts.addMenuListener(new MenuListener() {
private void addScriptNode(JMenu parentMenu, boolean usePopupForChildren, ScriptNode node) {
if (node.isAvailable() == false) {
return;
}
if (node.isRunnable()) {
parentMenu.add(new AbstractAction(node.getDisplayName(), node.getIcon()) {
@Override
public void actionPerformed(ActionEvent e) {
postScriptExecution(node.getFile(), node.getLaunchExecutorId());
}
});
} else if (node.getChildCount() > 0) {
JMenu newParent = parentMenu;
if (usePopupForChildren) {
newParent = new JMenu(node.getDisplayName());
parentMenu.add(newParent);
}
;
for (int i = 0; i < node.getChildCount(); i++) {
addScriptNode(newParent, true, (ScriptNode) node.getChildAt(i));
}
}
}
@Override
public void menuSelected(MenuEvent e) {
mnScripts.removeAll();
ScriptNode[] scripts = scriptsPanel.getScripts();
for (final ScriptNode item : scripts) {
addScriptNode(mnScripts, scripts.length > 1, item);
}
mnScripts.validate();
}
@Override
public void menuDeselected(MenuEvent e) {
}
@Override
public void menuCanceled(MenuEvent e) {
}
});
menuBar.add(mnScripts);
addSpace.add();
// add create script menu
if (appPermissions.isScriptEditingAllowed()) {
JMenu scriptsMenu = menuBuilder.createScriptCreationMenu(this, scriptManager);
if (scriptsMenu != null) {
menuBar.add(scriptsMenu);
}
addSpace.add();
}
// tools menu
JMenu tools = new JMenu("Tools");
menuBar.add(tools);
JMenu memoryCache = new JMenu("Memory cache");
tools.add(memoryCache);
memoryCache.add(new AbstractAction("View cache statistics") {
@Override
public void actionPerformed(ActionEvent e) {
TextInformationDialog dlg = new TextInformationDialog(AppFrame.this, "Memory cache statistics", ApplicationCache.singleton().getUsageReport());
dlg.setMinimumSize(new Dimension(400, 400));
dlg.setLocationRelativeTo(AppFrame.this);
dlg.setVisible(true);
}
});
memoryCache.add(new AbstractAction("Clear memory cache") {
@Override
public void actionPerformed(ActionEvent e) {
ApplicationCache.singleton().clearCache();
DistancesSingleton.singleton().closeCHGraph();
}
});
addSpace.add();
// add window menu
JMenu mnWindow = menuBuilder.createWindowsMenu(this);
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]);
splitterMain.setDividerLocation(0.175);
splitterLeftSide.setDividerLocation(0.3);
}
});
}
mnWindow.add(mnResizeTo);
menuBar.add(mnWindow);
addSpace.add();
menuBar.add(menuBuilder.createHelpMenu(actionBuilder, this));
addSpace.add();
}
@Override
public PendingScriptExecution postScriptExecution(File file, String[] optionIds) {
Future<Void> future = scriptManager.executeScript(file, optionIds);
return new PendingScriptExecution() {
@Override
public boolean isDone() {
return future.isDone();
}
};
}
@Override
public void importFile(final ImportFileType option) {
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(option.getFilter());
IOUtils.setFile(PreferencesManager.getSingleton().getLastImportFile(option), chooser);
if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) {
return;
}
final File file = chooser.getSelectedFile();
PreferencesManager.getSingleton().setLastImportFile(file, option);
importFile(file, option);
}
@Override
public void importFile(final File file, final ImportFileType option) {
DatastoreLoader.importFile(this, file, option);
}
@Override
public void updateAppearance() {
for (UIAction action : allActions) {
if (action != null) {
action.updateEnabledState();
}
}
setTitle(calculateTitle());
tables.setEnabled(loaded != null);
mnScripts.setVisible(loaded != null);
scriptsPanel.updateAppearance();
}
protected String calculateTitle() {
String title = AppConstants.WEBSITE;
if (loaded != null) {
title += " - ";
if (loaded.getFile() != null) {
title += loaded.getFile();
} else {
title += "untitled";
}
}
return title;
}
@Override
public void openDatastoreWithUserPrompt() {
if (!canCloseDatastore()) {
return;
}
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(ImportFileType.EXCEL.getFilter());
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();
}
@Override
public void openFile(final File file) {
DatastoreLoader.loadExcel(this, file);
}
@Override
public boolean canCloseDatastore() {
if (loaded == null) {
return true;
}
// if (loaded.isModified()) {
if (datastoreCloseNeedsUseConfirmation) {
if (JOptionPane.showConfirmDialog(this, "Any unsaved work will be lost. Do you want to exit?", "Confirm exit", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) {
return false;
}
}
// }
return true;
}
@Override
public void closeDatastore() {
setTitle("");
tables.onDatastoreClosed();
if (loaded != null) {
loaded.getDs().removeListener(scriptManager);
loaded.dispose();
loaded = null;
}
for (JInternalFrame frame : getInternalFrames()) {
if (ProgressFrame.class.isInstance(frame)) {
// cancel running operations...
((ProgressFrame) frame).getProgressPanel().cancel();
} else if (ScriptEditor.class.isInstance(frame) == false) {
frame.dispose();
}
}
scriptManager.datastoreStructureChanged();
updateAppearance();
}
@SuppressWarnings("serial")
@Override
public void createNewDatastore() {
if (!canCloseDatastore()) {
return;
}
ArrayList<JButton> buttons = new ArrayList<>();
for (NewDatastoreProvider ndp : newDatastoreProviders) {
buttons.add(new JButton(new AbstractAction(ndp.name()) {
@Override
public void actionPerformed(ActionEvent e) {
DatastoreLoader.useNewDatastoreProvider(AppFrame.this, ndp);
}
}));
}
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();
}
@Override
public 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;
final ProgressDialog<Boolean> pd = new ProgressDialog<>(AppFrame.this, message, false, true);
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"), ProgressPanel.createProcessingApi(getApi(), pd), 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();
}
});
}
/**
* Set the new datastore, which has already been decorated with listeners, undo / redo etc...
* @param decoratedDs
* @param sourceFile
*/
@SuppressWarnings("unchecked")
void setDecoratedDatastore(ODLDatastoreUndoable<? extends ODLTableAlterable> decoratedDs,DatastoreManagerPluginState dmps, File sourceFile){
if (loaded != null) {
closeDatastore();
}
loaded = new LoadedState(decoratedDs, sourceFile, this);
DatastoreManagerPlugin plugin = DatastoreManagerGlobalPlugin.getPlugin();
if(plugin!=null && dmps!=null){
loaded.putPluginState(plugin, dmps);
}
tables.setDatastore(loaded.getDs());
decoratedDs.addListener(scriptManager);
UndoStateChangedListener<ODLTableAlterable> undoStateChangedListener = new UndoStateChangedListener< ODLTableAlterable>() {
@Override
public void undoStateChanged(ODLDatastoreUndoable<? extends ODLTableAlterable> datastoreUndoable) {
Runnable runnable = new Runnable() {
@Override
public void run() {
for (UIAction action : allActions) {
if (action != null) {
action.updateEnabledState();
}
}
}
};
if (SwingUtilities.isEventDispatchThread()) {
runnable.run();
} else {
SwingUtilities.invokeLater(runnable);
}
}
};
// having java compilation oddities so we do the cast ... type erasure should ensure its safe
((ODLDatastoreUndoable< ODLTableAlterable>)decoratedDs).addUndoStateListener(undoStateChangedListener);
updateAppearance();
scriptManager.datastoreStructureChanged();
}
@Override
public void setDatastore(ODLDatastoreAlterable<? extends ODLTableAlterable> undecoratedDs, File sourceFile) {
ExecutionReportImpl report = new ExecutionReportImpl();
setDecoratedDatastore(decorateNewDatastore(undecoratedDs, sourceFile,null, null, report), null,sourceFile);
// This method can be called from ODL Connect when ODL Studio is starting up, so we show
// a delayed warning message to ensure the appframe window is initialised
if(report.isFailed() || report.size()>0){
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
String title = report.isFailed()? "Error": "Warning";
new ExecutionReportDialog(AppFrame.this, title, report, false);
}
});
}
}
@Override
public void dispose() {
PreferencesManager.getSingleton().setScreenState(new WindowState(this));
// dispose all child windows to save their screen state
for (JInternalFrame frame : getInternalFrames()) {
frame.dispose();
}
scriptsPanel.dispose();
if(haltJVMOnDispose){
DisposeCore.dispose();
}
// call super last as it calls the listeners
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);
}
}
tileVisibleFrames(frames.toArray(new JInternalFrame[frames.size()]));
}
}
@Override
public void launchTableSchemaEditor(int tableId) {
if (loaded != null) {
for (JInternalFrame frame : getInternalFrames()) {
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);
}
}
@Override
public JComponent launchTableGrid(int tableId) {
if (loaded != null) {
for (JInternalFrame frame : getInternalFrames()) {
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());
addInternalFrame(gf, FramePlacement.AUTOMATIC);
return gf;
}
}
return null;
}
@Override
public 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 ScriptUIManager getScriptUIManager() {
return scriptManager;
}
private void initFileMenu(JMenu mnFile, List<? extends Action> fileActions, ActionFactory actionFactory, MenuFactory menuBuilder) {
mnFile.removeAll();
// non-dynamic
for (Action action : fileActions) {
if (action == null) {
mnFile.addSeparator();
} else {
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 = menuBuilder.createImportMenu(this);
mnFile.add(mnImport);
// dynamic
mnFile.addSeparator();
for (AppFrameAction action : actionFactory.createLoadRecentFilesActions(this)) {
mnFile.add(action);
}
// clear recent
mnFile.addSeparator();
mnFile.add(new AppFrameAction("Clear recent files", "Clear recent files", null, null, false, null, this) {
@Override
public void actionPerformed(ActionEvent e) {
PreferencesManager.getSingleton().clearRecentFiles();
}
});
// finally exit
mnFile.addSeparator();
JMenuItem item = mnFile.add(new AppFrameAction("Exit", "Exit", null, null, false, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q,
java.awt.Event.CTRL_MASK), this) {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
System.exit(0);
}
});
// item.setAccelerator(((AppFrameAction) item.getAction()).accelerator);
mnFile.validate();
}
@Override
public ODLApi getApi() {
return api;
}
@Override
public ScriptsProvider getScriptsProvider() {
return scriptsPanel.getScriptsProvider();
}
public void setScriptsDirectory(File directory) {
scriptsPanel.setScriptsDirectory(directory);
}
public boolean isHaltJVMOnDispose() {
return haltJVMOnDispose;
}
@Override
public void setHaltJVMOnDispose(boolean haltJVMOnDispose) {
this.haltJVMOnDispose = haltJVMOnDispose;
}
@Override
public AppPermissions getAppPermissions() {
return appPermissions;
}
public List<NewDatastoreProvider> getNewDatastoreProviders() {
return newDatastoreProviders;
}
public void setNewDatastoreProviders(List<NewDatastoreProvider> newDatastoreProviders) {
this.newDatastoreProviders = newDatastoreProviders;
}
@Override
public void setDatastoreCloseNeedsUseConfirmation(boolean needsUserConfirmation) {
this.datastoreCloseNeedsUseConfirmation = needsUserConfirmation;
}
@Override
public ODLAppLoadedState getLoadedState() {
return loaded;
}
/**
* Decorate a new datastore to add functionality like undo / redo, listeners.
* @param ds
* @param file
* @param papi
* @param report
* @return
*/
ODLDatastoreUndoable<? extends ODLTableAlterable> decorateNewDatastore(ODLDatastoreAlterable<? extends ODLTableAlterable> ds,
File file, ProcessingApi papi, DatastoreManagerPluginState []dsmps,ExecutionReport report) {
ODLDatastoreUndoable<? extends ODLTableAlterable> ret;
if (ODLDatastoreImpl.class.isInstance(ds) == false) {
throw new RuntimeException();
}
// wrap in the decorator that allows lazy deep copying first of all
ds = new OptimisedDeepCopierDecorator<>(ds);
// wrap in listener decorator
ds = new ListenerDecorator<ODLTableAlterable>(ODLTableAlterable.class, ds);
// then undo / redo
ret = new UndoRedoDecorator<ODLTableAlterable>(ODLTableAlterable.class, ds);
// then a data updater decorator automatically calls any data updater options in all loaded scripts
// whenever the datastore is modified in a transaction.
ret = new DataUpdaterDecorator(getApi(), ret, this);
// finally any plugin logic
DatastoreManagerPlugin plugin = DatastoreManagerGlobalPlugin.getPlugin();
if(plugin!=null && ds!=null && papi!=null && report!=null){
ProcessDatastoreResult pdr = plugin.processNewDatastore(file, ret, papi, report);
if(report.isFailed()){
return null;
}
ret = pdr.getDs();
if(dsmps!=null && dsmps.length>0){
dsmps[0]=pdr.getState();
}
// ensure any changes not added to undo redo buffer as don't want them unable
ret.clearUndoBuffer();
}
return ret;
}
@Override
public void postAsynchronousDatastoreModify(DatastoreModifier modifier) {
if (getLoadedDatastore() == null ) {
JOptionPane.showMessageDialog(AppFrame.this, "Cannot execute " + modifier.name() + " as no datastore is loaded.");
return;
}
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
new PluginDatastoreModifierTask(AppFrame.this,modifier).executeNonEDT();
return null;
}
}.execute();;
}
@Override
public ODLAppUI getUI() {
return new ODLAppUI() {
@Override
public void showModalMessage(String title, String message, Dimension prefWindowSize){
TextInformationDialog dlg=new TextInformationDialog(AppFrame.this, title, message);
// dlg.setMinimumSize(new Dimension(1000, 200));
dlg.setLocationRelativeTo(AppFrame.this);
// dlg.pack();
if(prefWindowSize!=null){
dlg.setPreferredSize(prefWindowSize);
}
dlg.pack();
dlg.setVisible(true);
}
@Override
public void showModalMessage(String title, String message) {
showModalMessage(title, message,null);
}
@Override
public <T extends BeanMappedRow> void launchDataEditor(Class<T> beanDefinition, BeanEditorFactory<T> editorFactory) {
CustomTableEditorFrame<T> editor = new CustomTableEditorFrame<>(AppFrame.this, getApi(), beanDefinition, editorFactory, AppFrame.this);
addInternalFrame(editor, FramePlacement.CENTRAL);
}
};
}
}