/* ViewerMainFrame.java created 2007-09-10
*
*/
package org.signalml.app.view.workspace;
import static org.signalml.app.util.i18n.SvarogI18n._;
import static org.signalml.app.util.i18n.SvarogI18n._R;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.apache.log4j.Logger;
import org.signalml.app.SvarogApplication;
import org.signalml.app.action.selector.ActionFocusListener;
import org.signalml.app.action.selector.ActionFocusManager;
import org.signalml.app.action.selector.ViewFocusSelector;
import org.signalml.app.config.ApplicationConfiguration;
import org.signalml.app.config.ConfigurationDefaults;
import org.signalml.app.config.MainFrameConfiguration;
import org.signalml.app.config.workspace.ApplicationWorkspace;
import org.signalml.app.document.DocumentFlowIntegrator;
import org.signalml.app.document.DocumentManager;
import org.signalml.app.document.DocumentManagerEvent;
import org.signalml.app.document.DocumentManagerListener;
import org.signalml.app.document.ManagedDocumentType;
import org.signalml.app.document.signal.SignalDocument;
import org.signalml.app.method.ApplicationMethodDescriptor;
import org.signalml.app.method.ApplicationMethodManager;
import org.signalml.app.method.ApplicationSerializableMethodDescriptor;
import org.signalml.app.model.workspace.WorkspaceTreeModel;
import org.signalml.app.task.ApplicationTaskDescriptor;
import org.signalml.app.task.ApplicationTaskManager;
import org.signalml.app.task.ApplicationTaskManagerDescriptor;
import org.signalml.app.util.IconUtils;
import org.signalml.app.util.SnapToPageRunnable;
import org.signalml.app.view.View;
import org.signalml.app.view.common.components.LockableJSplitPane;
import org.signalml.app.view.common.dialogs.OptionPane;
import org.signalml.app.view.common.dialogs.errors.Dialogs;
import org.signalml.app.view.signal.SignalPlot;
import org.signalml.app.view.signal.SignalView;
import org.signalml.exception.SanityCheckException;
import org.signalml.method.CleanupMethod;
import org.signalml.method.Method;
import org.signalml.method.SerializableMethod;
import org.signalml.method.TrackableMethod;
import org.signalml.method.mp5.MP5Method;
import org.signalml.plugin.export.SignalMLException;
import org.signalml.plugin.export.signal.Document;
import org.signalml.plugin.export.view.DocumentView;
import org.signalml.plugin.loader.PluginLoaderHi;
import org.signalml.task.LocalTask;
import org.signalml.task.Task;
import org.signalml.task.TaskStatus;
import org.signalml.util.SvarogConstants;
/** ViewerMainFrame
*
*
* @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o.
*/
public class ViewerMainFrame extends JFrame implements View, DocumentManagerListener, ViewFocusSelector {
private static final long serialVersionUID = 1L;
protected static final Logger logger = Logger.getLogger(ViewerMainFrame.class);
public static final int INITIALIZATION_STEP_COUNT = 5;
/* Bootstrapping */
private List<MainWindowBootstrapTask> bootstrapList = new LinkedList<MainWindowBootstrapTask>();
/* Configuration */
private MainFrameConfiguration config;
/* Window interface elements */
private JPanel contentPane;
/* GUI element provider */
private ViewerElementManager elementManager;
private boolean viewMode = false;
private boolean leftPanelVisible = true;
private boolean bottomPanelVisible = true;
private boolean mainToolBarVisible = true;
private boolean statusBarVisible = true;
private boolean hadRestoredTasks = false;
public void initialize() {
SvarogApplication.getSharedInstance().splash(_("Initializing main window"), false);
setTitle(_R("Svarog v.{0}", SvarogConstants.VERSION));
setIconImage(IconUtils.loadClassPathImage("org/signalml/app/icon/mainframe.png"));
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
setContentPane(contentPane);
elementManager.setView(this);
elementManager.setOptionPaneParent(getRootPane());
elementManager.setDialogParent(this);
SvarogApplication.getSharedInstance().splash(_("Creating interface"), true);
Method method = elementManager.getMethodManager().getMethodByName("mp5");
if (method == null || !(method instanceof MP5Method)) {
throw new SanityCheckException("No mp5 method");
}
setJMenuBar(elementManager.getMenuBar());
contentPane.add(elementManager.getMainToolBar(), BorderLayout.NORTH);
contentPane.add(elementManager.getStatusBar(), BorderLayout.SOUTH);
contentPane.add(elementManager.getVerticalSplitPane(), BorderLayout.CENTER);
elementManager.configureAcceletators();
bindListeners();
addWindowListeners();
SvarogApplication.getSharedInstance().splash(_("Restoring tasks"), true);
restoreTasks();
SvarogApplication.getSharedInstance().splash(_("Finishing"), true);
pack();
setLocationByPlatform(true);
restoreViewPreferences();
logger.debug("Main window initialized");
SvarogApplication.getSharedInstance().splash(null, true);
}
public void bootstrap() {
SvarogApplication.getSharedInstance().splash(_("Restoring workspace"), false);
if (elementManager.getApplicationConfig().isRestoreWorkspace()) {
restoreWorkspace();
}
Iterator<MainWindowBootstrapTask> it = bootstrapList.iterator();
while (it.hasNext()) {
it.next().run();
}
if (hadRestoredTasks) {
int ans = OptionPane.showResumeRestoredTasks(this);
if (ans == OptionPane.YES_OPTION) {
ApplicationTaskManager taskManager = elementManager.getTaskManager();
int taskCount = taskManager.getTaskCount();
Task task;
for (int i=0; i<taskCount; i++) {
task = taskManager.getTaskAt(i);
if (task.getStatus().isResumable()) {
taskManager.resumeTask(task);
}
}
}
hadRestoredTasks = false;
}
SvarogApplication.getSharedInstance().splash(null, true);
}
private void bindListeners() {
WorkspaceTreeModel workspaceTreeModel = elementManager.getWorkspaceTreeModel();
DocumentManager documentManager = elementManager.getDocumentManager();
ViewerDocumentTabbedPane documentTabbedPane = elementManager.getDocumentTabbedPane();
ActionFocusManager actionFocusManager = elementManager.getActionFocusManager();
// remember listeners are processed last to first
elementManager.getMrudRegistry().addMRUDRegistryListener(workspaceTreeModel);
// XXX listener order matters here!
documentManager.addDocumentManagerListener(documentTabbedPane);
documentManager.addDocumentManagerListener(workspaceTreeModel);
documentManager.addDocumentManagerListener(elementManager.getSignalTreeModel());
documentManager.addDocumentManagerListener(elementManager.getMonitorTreeModel());
documentManager.addDocumentManagerListener(elementManager.getTagTreeModel());
documentManager.addDocumentManagerListener(elementManager.getBookTreeModel());
documentManager.addDocumentManagerListener(this);
actionFocusManager.addActionFocusListener(documentTabbedPane);
actionFocusManager.addActionFocusListener(elementManager.getWorkspaceTree());
documentTabbedPane.addChangeListener(actionFocusManager);
elementManager.getTaskManager().setStatusDialogParent(this);
}
@Override
public DocumentView createDocumentViewPanel(Document document) throws SignalMLException {
return elementManager.createDocumentViewPanel(document);
}
private void addWindowListeners() {
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent ev) {
elementManager.getCloseWindowAction().actionPerformed(new ActionEvent(ViewerMainFrame.this,0,"close"));
}
});
}
public void setStatus(String status) {
elementManager.getStatusBar().setStatus(status);
}
@Override
public void addActionFocusListener(ActionFocusListener listener) {
// ignored
}
@Override
public void removeActionFocusListener(ActionFocusListener listener) {
// ignored
}
@Override
public View getActiveView() {
return this;
}
public void closeView() {
logger.debug("Main window closing");
boolean hasSerializableTasks = false;
boolean hasUnserializableTasks = false;
boolean abortSerializable = false;
ApplicationTaskManager taskManager = elementManager.getTaskManager();
synchronized (taskManager) {
int cnt = taskManager.getTaskCount();
Task task;
TaskStatus status;
Method method;
for (int i=0; i<cnt; i++) {
task = taskManager.getTaskAt(i);
synchronized (task) {
status = task.getStatus();
if (status.isRunning()) {
method = task.getMethod();
if ((method instanceof SerializableMethod) && status.isSuspendable()) {
hasSerializableTasks = true;
} else if (task.getStatus().isAbortable()) {
hasUnserializableTasks = true;
}
}
}
}
}
if (hasSerializableTasks) {
int res = OptionPane.showSerializableTaskRunning(this);
if (res == OptionPane.CANCEL_OPTION) {
return;
} else if (res == OptionPane.NO_OPTION) {
abortSerializable = true;
}
}
if (hasUnserializableTasks) {
int res = OptionPane.showTaskRunning(this);
if (res != OptionPane.OK_OPTION) {
return;
}
}
DocumentFlowIntegrator documentFlowIntegrator = elementManager.getDocumentFlowIntegrator();
try {
boolean ok = documentFlowIntegrator.checkCloseAllDocuments();
if (!ok) {
return;
}
} catch (SignalMLException ex) {
logger.error("Failed to check close documents", ex);
Dialogs.showExceptionDialog((Window) null, ex);
return;
} catch (IOException ex) {
logger.error("Failed to check close documentst - i/o exception", ex);
Dialogs.showExceptionDialog((Window) null, ex);
return;
}
// now only the saved documents are present, others had been closed
// save workspace
saveWorkspace();
ArrayList<ApplicationTaskDescriptor> taskList = new ArrayList<ApplicationTaskDescriptor>();
synchronized (taskManager) {
int taskCount = taskManager.getTaskCount();
int i;
Task task;
TaskStatus status;
Method method;
for (i=0; i<taskCount; i++) {
task = taskManager.getTaskAt(i);
synchronized (task) {
status = task.getStatus();
method = task.getMethod();
if (method instanceof SerializableMethod) {
if (!status.isSuspended()) {
if (!abortSerializable && status.isSuspendable()) {
task.suspend(false);
} else if (status.isAbortable()) {
task.abort(false);
}
}
} else {
if (status.isAbortable()) {
task.abort(false);
}
}
}
}
ApplicationTaskDescriptor taskDescriptor;
SerializableMethod serializableMethod;
File file;
boolean serialized;
for (i=0; i<taskCount; i++) {
task = taskManager.getTaskAt(i);
synchronized (task) {
status = task.getStatus();
}
if (status.isRunning()) {
taskManager.waitForTaskToStopWorking(task);
}
serialized = false;
synchronized (task) {
status = task.getStatus();
method = task.getMethod();
if (status.isSuspended() || status.isFinished()) {
if (method instanceof SerializableMethod) {
serializableMethod = (SerializableMethod) method;
taskDescriptor = new ApplicationTaskDescriptor();
taskDescriptor.setMethodUID(method.getUID());
taskDescriptor.setStatus(status);
try {
file = serializableMethod.writeToPersistence(task.getData());
taskDescriptor.setSerializationPath(file.getAbsolutePath());
taskList.add(taskDescriptor);
serialized = true;
} catch (IOException ex) {
logger.error("Failed to serialize task", ex);
} catch (SignalMLException ex) {
logger.error("Failed to serialize task", ex);
}
}
}
if (!serialized) {
if (method instanceof CleanupMethod) {
((CleanupMethod) method).cleanUp(task.getData());
}
}
}
}
}
ApplicationTaskManagerDescriptor taskManagerDescriptor = new ApplicationTaskManagerDescriptor(taskList);
taskManagerDescriptor.setProfileDir(elementManager.getProfileDir());
try {
taskManagerDescriptor.writeToPersistence(null);
} catch (Exception ex) {
logger.error("Failed to write task manager descriptor", ex);
}
try {
documentFlowIntegrator.closeAllDocuments();
} catch (SignalMLException ex) {
logger.error("Failed to close documents", ex);
Dialogs.showExceptionDialog((Window) null, ex);
return;
} catch (IOException ex) {
logger.error("Failed to close documentst - i/o exception", ex);
Dialogs.showExceptionDialog((Window) null, ex);
return;
}
PluginLoaderHi.getInstance().onClose();
saveViewPreferences();
setVisible(false);
dispose();
SvarogApplication.getSharedInstance().exit(0);
}
private void saveWorkspace() {
ApplicationWorkspace workspace = new ApplicationWorkspace();
workspace.setProfileDir(elementManager.getProfileDir());
workspace.configureFrom(elementManager.getDocumentFlowIntegrator());
try {
workspace.writeToPersistence(null);
} catch (IOException ex) {
logger.error("Failed to write workspace", ex);
}
}
private void restoreWorkspace() {
ApplicationWorkspace workspace = new ApplicationWorkspace();
workspace.setEegSystemsPresetManager(SvarogApplication.getManagerOfPresetsManagers().getEegSystemsPresetManager());
workspace.setProfileDir(elementManager.getProfileDir());
workspace.maybeReadFromPersistence(
"Seems like workspace doesn't exist - workspace will not be restored",
"Failed to read workspace configuration - workspace lost");
workspace.configureIntegrator(elementManager.getDocumentFlowIntegrator());
}
public boolean isViewMode() {
return viewMode;
}
public void setViewMode(boolean viewMode) {
if (this.viewMode != viewMode) {
this.viewMode = viewMode;
elementManager.getViewModeAction().putValue(AbstractAction.SELECTED_KEY, viewMode);
ApplicationConfiguration applicationConfig = elementManager.getApplicationConfig();
if (applicationConfig.isViewModeHidesMainToolBar()) {
setMainToolBarVisible(!viewMode);
}
if (applicationConfig.isViewModeHidesLeftPanel()) {
setLeftPanelVisible(!viewMode);
}
if (applicationConfig.isViewModeHidesBottomPanel()) {
setBottomPanelVisible(!viewMode);
}
if (applicationConfig.isViewModeCompactsPageTagBars() || applicationConfig.isViewModeSnapsToPage()) {
DocumentManager documentManager = elementManager.getDocumentManager();
synchronized (documentManager) {
int cnt = documentManager.getDocumentCount(ManagedDocumentType.SIGNAL);
SignalDocument signalDocument;
SignalView signalView;
for (int i=0; i<cnt; i++) {
signalDocument = (SignalDocument) documentManager.getDocumentAt(ManagedDocumentType.SIGNAL, i);
signalView = (SignalView) signalDocument.getDocumentView();
if (applicationConfig.isViewModeCompactsPageTagBars()) {
for (SignalPlot plot : signalView.getPlots()) {
plot.getSignalPlotColumnHeader().setCompact(viewMode);
}
}
if (applicationConfig.isViewModeSnapsToPage()) {
SwingUtilities.invokeLater(new SnapToPageRunnable(signalView, viewMode));
}
}
}
}
}
}
@Override
public boolean isLeftPanelVisible() {
return leftPanelVisible;
}
@Override
public void setLeftPanelVisible(boolean visible) {
if (this.leftPanelVisible != visible) {
this.leftPanelVisible = visible;
elementManager.getShowLeftPanelAction().putValue(AbstractAction.SELECTED_KEY, visible);
elementManager.getLeftPane().setVisible(visible);
LockableJSplitPane horizontalSplitPane = elementManager.getHorizontalSplitPane();
if (visible) {
horizontalSplitPane.setDividerLocation(horizontalSplitPane.getLastDividerLocation());
} else {
horizontalSplitPane.setDividerLocation(horizontalSplitPane.getMinimumDividerLocation());
}
horizontalSplitPane.setOneTouchExpandable(visible);
horizontalSplitPane.setLocked(!visible);
}
}
@Override
public boolean isBottomPanelVisible() {
return bottomPanelVisible;
}
@Override
public void setBottomPanelVisible(boolean visible) {
if (this.bottomPanelVisible != visible) {
this.bottomPanelVisible = visible;
elementManager.getShowBottomPanelAction().putValue(AbstractAction.SELECTED_KEY, visible);
elementManager.getBottomPane().setVisible(visible);
LockableJSplitPane verticalSplitPane = elementManager.getVerticalSplitPane();
if (visible) {
verticalSplitPane.setDividerLocation(verticalSplitPane.getLastDividerLocation());
} else {
verticalSplitPane.setDividerLocation(verticalSplitPane.getMaximumDividerLocation());
}
verticalSplitPane.setOneTouchExpandable(visible);
verticalSplitPane.setLocked(!visible);
}
}
@Override
public boolean isMainToolBarVisible() {
return mainToolBarVisible;
}
@Override
public void setMainToolBarVisible(boolean visible) {
if (this.mainToolBarVisible != visible) {
this.mainToolBarVisible = visible;
elementManager.getMainToolBar().setVisible(visible);
elementManager.getShowMainToolBarAction().putValue(AbstractAction.SELECTED_KEY, visible);
}
}
@Override
public boolean isStatusBarVisible() {
return statusBarVisible;
}
@Override
public void setStatusBarVisible(boolean visible) {
if (this.statusBarVisible != visible) {
this.statusBarVisible = visible;
elementManager.getStatusBar().setVisible(visible);
elementManager.getShowStatusBarAction().putValue(AbstractAction.SELECTED_KEY, visible);
}
}
private void restoreTasks() {
ApplicationMethodManager methodManager = elementManager.getMethodManager();
ApplicationTaskManager taskManager = elementManager.getTaskManager();
ApplicationTaskManagerDescriptor taskManagerDescriptor = new ApplicationTaskManagerDescriptor();
taskManagerDescriptor.setProfileDir(elementManager.getProfileDir());
try {
taskManagerDescriptor.readFromPersistence(null);
} catch (FileNotFoundException ex) {
logger.debug("Seems like task configuration doesn't exist - tasks will not be restored");
return;
} catch (Exception ex) {
logger.error("Failed to read task manager configuration - tasks lost", ex);
return;
}
Iterator<ApplicationTaskDescriptor> taskIterator = taskManagerDescriptor.taskIterator();
ApplicationTaskDescriptor descriptor;
Method method;
while (taskIterator.hasNext()) {
descriptor = taskIterator.next();
method = methodManager.getMethodByUID(descriptor.getMethodUID());
if (method == null) {
logger.warn("Method with UID [" + descriptor.getMethodUID() + "] not found");
continue;
}
if (!(method instanceof SerializableMethod)) {
logger.warn("Method with UID [" + descriptor.getMethodUID() + "] not serializable");
continue;
}
ApplicationMethodDescriptor methodDescriptor = methodManager.getMethodData(method);
Object data;
if (methodDescriptor == null || !(methodDescriptor instanceof ApplicationSerializableMethodDescriptor)) {
data = method.createData();
} else {
data = ((ApplicationSerializableMethodDescriptor) methodDescriptor).createDeserializedData(methodManager);
}
SerializableMethod serializableMethod = (SerializableMethod) method;
try {
serializableMethod.readFromPersistence(data, new File(descriptor.getSerializationPath()));
} catch (IOException ex) {
logger.error("Failed to deserialize method data", ex);
continue;
} catch (SignalMLException ex) {
logger.error("Failed to deserialize method data", ex);
continue;
}
TaskStatus status = descriptor.getStatus();
if (status != TaskStatus.FINISHED && status != TaskStatus.SUSPENDED) {
logger.error("Incorrect task status [" + status + "]");
continue;
}
Task task = new LocalTask(method, data, (method instanceof TrackableMethod), status);
taskManager.addTask(task);
if (status.isResumable()) {
hadRestoredTasks = true;
}
}
}
public void restoreViewPreferences() {
config = new MainFrameConfiguration();
config.setProfileDir(elementManager.getProfileDir());
config.setStreamer(elementManager.getStreamer());
ConfigurationDefaults.setMainFrameConfigurationDefaults(config);
config.maybeReadFromPersistence("No mainframe configuration, will use defaults",
"Bad mainframe configuration, will use defaults");
setSize(new Dimension(config.getXSize(),config.getYSize()));
if (config.isMaximized()) {
setExtendedState(JFrame.MAXIMIZED_BOTH);
} else {
setExtendedState(JFrame.NORMAL);
}
setMainToolBarVisible(config.isMainToolBarVisible());
setStatusBarVisible(config.isStatusBarVisible());
elementManager.getHorizontalSplitPane().setDividerLocation(config.getHDividerLocation());
elementManager.getVerticalSplitPane().setDividerLocation(config.getVDividerLocation());
setLeftPanelVisible(config.isLeftPanelVisible());
setBottomPanelVisible(config.isBottomPanelVisible());
setViewMode(config.isViewMode());
}
public void saveViewPreferences() {
boolean maximized = ((getExtendedState() & JFrame.MAXIMIZED_BOTH) != 0);
config.setMaximized(maximized);
if (!maximized) {
config.setXSize((int) getSize().width);
config.setYSize((int) getSize().height);
}
config.setMainToolBarVisible(mainToolBarVisible);
config.setStatusBarVisible(statusBarVisible);
config.setLeftPanelVisible(leftPanelVisible);
config.setBottomPanelVisible(bottomPanelVisible);
if (leftPanelVisible) {
config.setHDividerLocation(elementManager.getHorizontalSplitPane().getDividerLocation());
} else {
config.setHDividerLocation(elementManager.getHorizontalSplitPane().getLastDividerLocation());
}
if (bottomPanelVisible) {
config.setVDividerLocation(elementManager.getVerticalSplitPane().getDividerLocation());
} else {
config.setVDividerLocation(elementManager.getVerticalSplitPane().getLastDividerLocation());
}
config.setViewMode(viewMode);
try {
File file = config.getStandardFile(elementManager.getProfileDir());
config.writeToXML(file, elementManager.getStreamer());
} catch (IOException ex) {
logger.error("Failed to write mainframe configuration", ex);
}
}
public ViewerElementManager getElementManager() {
return elementManager;
}
public void setElementManager(ViewerElementManager elementManager) {
this.elementManager = elementManager;
}
@Override
public void documentAdded(DocumentManagerEvent e) {
elementManager.getSaveAllDocumentsAction().setEnabledAsNeeded();
}
@Override
public void documentPathChanged(DocumentManagerEvent e) {
// this is not interesting
}
@Override
public void documentRemoved(DocumentManagerEvent e) {
elementManager.getSaveAllDocumentsAction().setEnabledAsNeeded();
}
public void addBootstrap(MainWindowBootstrapTask task) {
bootstrapList.add(task);
}
public abstract class MainWindowBootstrapTask implements Runnable {};
}