/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: UserInterfaceMain.java
*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.user;
import com.sun.electric.Main;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.Environment;
import com.sun.electric.database.Snapshot;
import com.sun.electric.database.change.DatabaseChangeEvent;
import com.sun.electric.database.change.DatabaseChangeListener;
import com.sun.electric.database.change.Undo;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.id.IdManager;
import com.sun.electric.database.text.Pref;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.technology.TechPool;
import com.sun.electric.tool.AbstractUserInterface;
import com.sun.electric.tool.Client;
import com.sun.electric.tool.EJob;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.Listener;
import com.sun.electric.tool.Tool;
import com.sun.electric.tool.ToolSettings;
import com.sun.electric.tool.user.dialogs.Progress;
import com.sun.electric.tool.user.ui.ClickZoomWireListener;
import com.sun.electric.tool.user.ui.EditWindow;
import com.sun.electric.tool.user.ui.ErrorLoggerTree;
import com.sun.electric.tool.user.ui.JobTree;
import com.sun.electric.tool.user.ui.LayerVisibility;
import com.sun.electric.tool.user.ui.MessagesWindow;
import com.sun.electric.tool.user.ui.ToolBar;
import com.sun.electric.tool.user.ui.WindowContent;
import com.sun.electric.tool.user.ui.WindowFrame;
import com.sun.electric.util.TextUtils;
import java.awt.AWTEvent;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;
/**
* Class to build the UserInterface for the main GUI version of the user interface.
*/
public class UserInterfaceMain extends AbstractUserInterface
{
static final Logger logger = Logger.getLogger("com.sun.electric.tool.user");
// /** Property fired if ability to Undo changes */ public static final String propUndoEnabled = "UndoEnabled";
// /** Property fired if ability to Redo changes */ public static final String propRedoEnabled = "RedoEnabled";
static volatile boolean initializationFinished = false;
private static Main Contain;
private static volatile boolean undoEnabled = false;
private static volatile boolean redoEnabled = false;
// private static final EventListenerList undoRedoListenerList = new EventListenerList();
private static EventListenerList listenerList = new EventListenerList();
private static Snapshot currentSnapshot = IdManager.stdIdManager.getInitialSnapshot();
private static GraphicsPreferences currentGraphicsPreferences = null;
// private static EDatabase database = EDatabase.clientDatabase();
/** The progress during input. */ protected static Progress progress = null;
private PrintStream stdout = System.out;
public UserInterfaceMain(List<String> argsList, Main thisContain) {
new EventProcessor();
Contain = thisContain;
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
assert SwingUtilities.isEventDispatchThread();
setClientThread();
Environment.setThreadEnvironment(IdManager.stdIdManager.getInitialEnvironment());
}
});
} catch (Exception e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new InitializationRun(argsList));
}
@Override
protected void terminateJob(Job.Key jobKey, String jobName, Tool tool,
Job.Type jobType, byte[] serializedJob,
boolean doItOk, byte[] serializedResult, Snapshot newSnapshot) {
boolean undoRedo = jobType == Job.Type.UNDO;
if (jobType != Job.Type.SERVER_EXAMINE && jobType != Job.Type.CLIENT_EXAMINE) {
int restoredHighlights = Undo.endChanges(currentSnapshot, tool, jobName, newSnapshot);
showSnapshot(newSnapshot, undoRedo);
restoreHighlights(restoredHighlights);
}
if (jobKey.clientId != getConnectionId())
return;
EJob ejob;
Throwable jobException = null;
if (jobKey.startedByServer()) {
ejob = new EJob(this, jobKey.jobId, jobType, jobName, serializedJob);
jobException = ejob.deserializeToClient();
} else {
ejob = removeProcessingEJob(jobKey);
}
if (jobException != null) {
System.out.println("Error deserializing " + jobName);
ActivityLogger.logException(jobException);
return;
}
ejob.serializedResult = serializedResult;
jobException = ejob.deserializeResult();
Job job = ejob.clientJob;
if (job == null) {
ActivityLogger.logException(jobException);
return;
}
try {
job.terminateIt(jobException);
} catch (Throwable ex) {
System.out.println("Exception executing terminateIt");
ex.printStackTrace(System.out);
}
job.timer.end();
job.finished = true; // is this redundant with Thread.isAlive()?
// say something if it took more than a minute by default
if (job.reportExecution || job.timer.getTime() >= Job.MIN_NUM_SECONDS) {
if (User.isBeepAfterLongJobs())
Job.getExtendedUserInterface().beep();
System.out.println(job.getInfo());
}
}
@Override
protected void showJobQueue(final Job.Inform[] jobQueue) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
showJobQueue(jobQueue);
}
});
return;
}
boolean busyCursor = false;
for (Job.Inform jobInform: jobQueue) {
if (jobInform.isChangeJobQueuedOrRunning())
busyCursor = true;
}
JobTree.update(Arrays.asList(jobQueue));
Main.setBusyCursor(busyCursor);
}
public void addEvent(Client.ServerEvent serverEvent) { SwingUtilities.invokeLater(serverEvent); }
private static String getMacClassName()
{
return "com.sun.electric.tool.user.MacOSXInterface";
}
// private class InitializationSetJob implements Runnable {
// Job initJob;
// public InitializationSetJob(Job job)
// {
// this.initJob = job;
// }
// public void run()
// {
// if (!Client.isOSMac()) return;
//
// try {
// Class<?> osXClass = Class.forName(getMacClassName());
// Method osXSetJobMethod = null;
//
// // find the necessary methods on the Mac OS/X class
// try {
// osXSetJobMethod = osXClass.getMethod("setInitJob", new Class[] {Job.class});
// } catch (NoSuchMethodException e) {
// osXSetJobMethod = null;
// }
// if (osXSetJobMethod != null) {
// try {
// osXSetJobMethod.invoke(osXClass, new Object[] {initJob});
// } catch (Exception e) {
// System.out.println("Error initializing Mac OS/X interface");
// }
// }
// } catch (ClassNotFoundException e) {}
// }
// }
private class InitializationRun implements Runnable {
List<String> argsList;
InitializationRun(List<String> argsList) {
this.argsList = argsList;
}
public void run() {
if (!Job.isClientThread())
assert Job.isClientThread();
Pref.setCachedObjsFromPreferences();
EditingPreferences.setThreadEditingPreferences(new EditingPreferences(true, null));
currentGraphicsPreferences = new GraphicsPreferences(true, new TechPool(IdManager.stdIdManager));
// see if there is a Macintosh OS/X interface
if (Client.isOSMac()) {
try {
Class<?> osXClass = Class.forName(getMacClassName());
Method osXRegisterMethod = null;
// find the necessary methods on the Macintosh OS/X class
try {
osXRegisterMethod = osXClass.getMethod("registerMacOSXApplication", new Class[] {List.class});
} catch (NoSuchMethodException e) {
osXRegisterMethod = null;
}
if (osXRegisterMethod != null) {
try {
osXRegisterMethod.invoke(osXClass, new Object[] {argsList});
} catch (Exception e) {
System.out.println("Error initializing Mac OS/X interface");
}
}
} catch (ClassNotFoundException e) {}
}
//runThreadStatusTimer();
Main.InitializeMessagesWindow();
}
}
/**
* Method is called when initialization was finished.
*/
public void finishInitialization() {
initializationFinished = true;
Main.InitializeWindows(Contain);
WindowFrame.wantToOpenCurrentLibrary(true, null);
// report on missing components
if (Job.getDebug())
{
Set<String> missingComponents = TextUtils.getMissingComponentNames();
String errorMsg = null;
for(String comp : missingComponents)
{
if (errorMsg == null) errorMsg = "Warning: optional components not found: "; else
errorMsg += ", ";
errorMsg += comp;
}
if (errorMsg != null)
System.out.println(errorMsg);
missingComponents = TextUtils.getMissingPrivateComponentNames();
errorMsg = null;
for(String comp : missingComponents)
{
if (errorMsg == null) errorMsg = "Warning: private components not found: "; else
errorMsg += ", ";
errorMsg += comp;
}
if (errorMsg != null)
System.out.println(errorMsg);
}
}
public EDatabase getDatabase() {
return EDatabase.clientDatabase();
}
public EditWindow_ getCurrentEditWindow_() { return EditWindow.getCurrent(); }
public EditWindow_ needCurrentEditWindow_() { return EditWindow.needCurrent(); }
public Cell getCurrentCell() { return WindowFrame.getCurrentCell(); }
public Cell needCurrentCell() { return WindowFrame.needCurCell(); }
/**
* Method to adjust reference point in WindowFrame containing the cell
*/
public void adjustReferencePoint(Cell theCell, double cX, double cY)
{
// adjust all windows showing this cell
for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); )
{
WindowFrame wf = it.next();
WindowContent content = wf.getContent();
if (!(content instanceof EditWindow_)) continue;
Cell cell = content.getCell();
if (cell != theCell) continue;
EditWindow_ wnd = (EditWindow_)content;
Point2D off = wnd.getOffset();
off.setLocation(off.getX()-cX, off.getY()-cY);
wnd.setOffset(off);
}
}
public void repaintAllWindows() { WindowFrame.repaintAllWindows(); }
// public void loadComponentMenuForTechnology()
// {
// WindowFrame wf = WindowFrame.getCurrentWindowFrame(false);
// if (wf != null) wf.loadComponentMenuForTechnology();
// }
public int getDefaultTextSize() { return EditWindow.getDefaultFontSize(); }
public EditWindow_ displayCell(Cell cell)
{
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (User.isShowCellsInNewWindow()) wf = null;
if (wf == null) wf = WindowFrame.createEditWindow(cell);
wf.setCellWindow(cell, null);
if (wf.getContent() instanceof EditWindow_) return (EditWindow_)wf.getContent();
return null;
}
// ErrorLogger functions
public void termLogging(final ErrorLogger log, boolean explain, boolean terminate)
{
if (!log.isPersistent() && log.getNumLogs() == 0) return;
ErrorLoggerTree.addLogger(log, explain, terminate);
}
public void updateNetworkErrors(Cell cell, List<ErrorLogger.MessageLog> errors) {
ErrorLoggerTree.updateNetworkErrors(cell, errors);
}
public void updateIncrementalDRCErrors(Cell cell, List<ErrorLogger.MessageLog> newErrors,
List<ErrorLogger.MessageLog> delErrors) {
ErrorLoggerTree.updateDrcErrors(cell, newErrors, delErrors);
}
/**
* Method to return the error message associated with the current error.
* Highlights associated graphics if "showhigh" is nonzero.
*/
public String reportLog(ErrorLogger.MessageLog log, boolean showhigh, boolean separateWindow, int position)
{
EDatabase database = EDatabase.clientDatabase();
// show the error
if (showhigh)
{
// in case multiple windows are involved in the action so all will be brought up
// and properly terminated for display
Set<EditWindow> updatedWindows = new HashSet<EditWindow>();
// clear all highlighting
for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); )
{
WindowFrame wf = it.next();
if (wf.getContent() instanceof EditWindow)
{
EditWindow wnd = (EditWindow)wf.getContent();
Highlighter highlighter = wnd.getHighlighter();
highlighter.clear();
}
}
// first show the geometry associated with this error
int pos = -1;
for(Iterator<ErrorHighlight> it = log.getHighlights(); it.hasNext(); )
{
ErrorHighlight eh = it.next();
Highlighter highlighter = null;
// Checking whether a specific geometry is displayed
pos++;
if (position != -1 && pos != position) continue; // not this one
Cell cell = eh.getCell(database);
// validate the cell (it may have been deleted)
if (cell != null)
{
if (!cell.isLinked())
{
return "(cell deleted): " + log.getMessageString();
}
// make sure it is shown
EditWindow wnd;
if (separateWindow)
{
wnd = EditWindow.showEditWindowForCell(cell, eh.getVarContext());
} else
{
wnd = EditWindow.getCurrent();
if (wnd != null)
{
if (wnd.getCell() != cell)
wnd.setCell(cell, eh.getVarContext(), null);
} else
{
wnd = EditWindow.showEditWindowForCell(cell, eh.getVarContext());
}
}
// nothing clean yet
highlighter = wnd.getHighlighter();
updatedWindows.add(wnd);
} else
{
System.out.println("No cell associated with this error");
}
if (highlighter == null) continue;
eh.addToHighlighter(highlighter, database);
}
// finish all open windows
boolean nothingDone = true;
for(EditWindow wnd : updatedWindows)
{
Highlighter highlighter = wnd.getHighlighter();
// Something found to highlight
if (highlighter != null)
{
highlighter.finished();
if (User.isShiftWindowToErrors())
{
Rectangle2D bounds = highlighter.getHighlightedArea(wnd);
Rectangle2D displayBounds = wnd.displayableBounds();
if (wnd.isInPlaceEdit())
{
Point2D llPt = new Point2D.Double(displayBounds.getMinX(), displayBounds.getMinY());
Point2D urPt = new Point2D.Double(displayBounds.getMaxX(), displayBounds.getMaxY());
AffineTransform intoCell = wnd.getInPlaceTransformIn();
intoCell.transform(llPt, llPt);
intoCell.transform(urPt, urPt);
double lX = Math.min(llPt.getX(), urPt.getX());
double hX = Math.max(llPt.getX(), urPt.getX());
double lY = Math.min(llPt.getY(), urPt.getY());
double hY = Math.max(llPt.getY(), urPt.getY());
displayBounds = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
}
if (bounds.getMinX() >= displayBounds.getMaxX() ||
bounds.getMaxX() <= displayBounds.getMinX() ||
bounds.getMinY() >= displayBounds.getMaxY() ||
bounds.getMaxY() <= displayBounds.getMinY())
{
Rectangle2D newBounds = highlighter.getHighlightedArea(wnd);
double newLX = newBounds.getMinX() - newBounds.getWidth()/2;
double newHX = newBounds.getMaxX() + newBounds.getWidth()/2;
double newLY = newBounds.getMinY() - newBounds.getHeight()/2;
double newHY = newBounds.getMaxY() + newBounds.getHeight()/2;
newBounds.setRect(newLX, newLY, newHX-newLX, newHY-newLY);
wnd.focusScreen(newBounds);
} else
{
highlighter.ensureHighlightingSeen();
}
} else
{
highlighter.ensureHighlightingSeen();
}
// // make sure the selection is visible
// Rectangle2D hBounds = highlighter.getHighlightedArea(wnd);
// Rectangle2D shown = wnd.getDisplayedBounds();
// if (wnd.isInPlaceEdit())
// {
// Point2D llPt = new Point2D.Double(shown.getMinX(), shown.getMinY());
// Point2D urPt = new Point2D.Double(shown.getMaxX(), shown.getMaxY());
// AffineTransform intoCell = wnd.getInPlaceTransformIn();
// intoCell.transform(llPt, llPt);
// intoCell.transform(urPt, urPt);
// double lX = Math.min(llPt.getX(), urPt.getX());
// double hX = Math.max(llPt.getX(), urPt.getX());
// double lY = Math.min(llPt.getY(), urPt.getY());
// double hY = Math.max(llPt.getY(), urPt.getY());
// shown = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
// }
// if (!shown.intersects(hBounds))
// {
// wnd.focusOnHighlighted();
// }
}
nothingDone = false;
}
if (nothingDone)
{
Cell logCell = log.getCell();
// Checking also that the cell hasn't been removed yet
if (logCell != null && logCell.isLinked())
{
// in case of errors or warnings with only cell information
// This would bring the EditWindow to the front.
EditWindow.showEditWindowForCell(logCell, null);
}
}
}
// return the error message
return log.getMessageString();
}
/**
* Method to show an error message.
* @param message the error message to show.
* @param title the title of a dialog with the error message.
*/
public void showErrorMessage(final String message, final String title)
{
if (Job.isClientThread()) {
JOptionPane.showMessageDialog(Main.getCurrentJFrame(), message, title, JOptionPane.ERROR_MESSAGE);
} else {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
showErrorMessage(message, title);
}
});
}
}
/**
* Method to show an informational message.
* @param message the message to show.
* @param title the title of a dialog with the message.
*/
public void showInformationMessage(final String message, final String title)
{
if (Job.isClientThread())
JOptionPane.showMessageDialog(Main.getCurrentJFrame(), message, title, JOptionPane.INFORMATION_MESSAGE);
else {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
showInformationMessage(message, title);
}
});
}
}
/**
* Method print a message.
* @param message the message to show.
* @param newLine add new line after the message
*/
public void printMessage(String message, boolean newLine) {
final String s = newLine ? message + "\n" : message;
if (Job.isClientThread())
appendString(s);
else {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
MessagesWindow messagesWindow = Main.getMessagesWindow();
if (messagesWindow != null)
appendString(s);
else
stdout.print(s);
}
});
}
}
/**
* Method to start saving messages.
* @param filePath file to save
*/
public void saveMessages(final String filePath) {
if (!Job.isClientThread()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() { saveMessages(filePath); }
});
}
try
{
if (printWriter != null) {
printWriter.close();
printWriter = null;
}
if (filePath == null) return;
printWriter = new PrintWriter(new BufferedWriter(new FileWriter(filePath)));
} catch (IOException e)
{
System.err.println("Error creating " + filePath);
System.out.println("Error creating " + filePath);
return;
}
System.out.println("Messages will be saved to " + filePath);
}
private static PrintWriter printWriter = null;
private static boolean newCommand = true;
private static int commandNumber = 1;
/**
* Method to report that the user issued a new command (click, keystroke, pulldown menu).
* The messages window separates output by command so that each command's results
* can be distinguished from others.
*/
public static void userCommandIssued()
{
newCommand = true;
}
public void appendString(String str)
{
if (str.length() == 0) return;
if (newCommand)
{
newCommand = false;
str = "=================================" + (commandNumber++) + "=================================\n" + str;
}
if (printWriter != null)
{
printWriter.print(str);
printWriter.flush();
}
MessagesWindow mw = Main.getMessagesWindow();
if (mw != null)
Main.getMessagesWindow().appendString(str);
else
// Error before the message window is available. Sending error to std error output
System.err.println(str);
}
/**
* Method to show a message and ask for confirmation.
* @param message the message to show.
* @return true if "yes" was selected, false if "no" was selected.
*/
public boolean confirmMessage(Object message)
{
int response = JOptionPane.showConfirmDialog(Main.getCurrentJFrame(), message);
return response == JOptionPane.YES_OPTION;
}
/**
* Method to ask for a choice among possibilities.
* @param message the message to show.
* @param title the title of the dialog with the query.
* @param choices an array of choices to present, each in a button.
* @param defaultChoice the default choice.
* @return the index into the choices array that was selected.
*/
public int askForChoice(String message, String title, String [] choices, String defaultChoice)
{
// make sure the message is not too long and add \n if necessary
String msg = message;
int size = msg.length();
int pos = 0;
int lineNumber = 0;
String newMsg = "";
while (pos < size && lineNumber < 10)
{
int endIndex = pos+256;
if (endIndex > size) endIndex = size;
newMsg += msg.substring(pos, endIndex);
newMsg += "\n";
pos +=256;
lineNumber++;
}
if (pos < size) // too many lines
{
newMsg += "........\n";
// adding the end of the message. If end of message is close then add the remainder otherwise
// print the last 256 characters.
int index = (size - pos > 256) ? (size - 256) : (pos);
newMsg += msg.substring(index, size);
}
msg= newMsg;
message = msg;
int val = JOptionPane.showOptionDialog(Main.getCurrentJFrame(), message, title,
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, choices, defaultChoice);
return val;
}
/**
* Method to ask for a line of text.
* @param message the prompt message.
* @param title the title of a dialog with the message.
* @param def the default response.
* @return the string (null if canceled).
*/
public String askForInput(Object message, String title, String def)
{
Object ret = JOptionPane.showInputDialog(Main.getCurrentJFrame(), message, title, JOptionPane.QUESTION_MESSAGE, null, null, def);
if (ret == null) return null;
return ret.toString();
}
/** For Preference */
public static void importPrefs(URL fileURL) {
assert Job.isClientThread();
if (fileURL == null) return;
System.out.println("Importing preferences...");
Pref.importPrefs(fileURL);
Environment env = EDatabase.clientDatabase().getEnvironment();
// Mirror Settings in Preferences
env.saveToPreferences();
// recache all preferences
loadPreferences(env.techPool);
Main.getEMenuBar().restoreSavedBindings(false); //trying to cache again
User.technologyChanged();
WindowFrame.repaintAllWindows();
System.out.println("...preferences imported from " + fileURL.getFile());
}
// ExtendedUserInterface
/**
* Save current state of highlights and return its ID.
*/
@Override
public int saveHighlights() {
EditWindow_ wnd = getCurrentEditWindow_();
if (wnd == null) return -1;
SavedHighlights sh = new SavedHighlights(lastId++, wnd);
while (savedHighlights.size() >= User.getMaxUndoHistory() && !savedHighlights.isEmpty())
savedHighlights.remove(0);
savedHighlights.add(sh);
return sh.id;
}
/**
* Restore state of highlights by its ID.
*/
@Override
public void restoreHighlights(int highlightsId) {
for (SavedHighlights sh: savedHighlights) {
if (sh.id == highlightsId) {
sh.restore();
break;
}
}
}
/**
* Show status of undo/redo buttons
* @param newUndoEnabled new status of undo button.
* @param newRedoEnabled new status of redo button.
*/
@Override
public void showUndoRedoStatus(boolean newUndoEnabled, boolean newRedoEnabled) {
PropertyChangeEvent e = null;
if (undoEnabled != newUndoEnabled) {
// PropertyChangeEvent e = new PropertyChangeEvent(this, propUndoEnabled, undoEnabled, newUndoEnabled);
undoEnabled = newUndoEnabled;
SwingUtilities.invokeLater(new PropertyChangeRun(e));
}
if (redoEnabled != newRedoEnabled) {
// PropertyChangeEvent e = new PropertyChangeEvent(this, propRedoEnabled, redoEnabled, newRedoEnabled);
redoEnabled = newRedoEnabled;
SwingUtilities.invokeLater(new PropertyChangeRun(e));
}
}
/**
* Show new database snapshot.saveh
* @param newSnapshot new snapshot.
*/
public void showSnapshot(Snapshot newSnapshot, boolean undoRedo) {
assert Job.isClientThread();
DatabaseChangeEvent event = new DatabaseChangeEvent(currentSnapshot, newSnapshot);
Snapshot oldSnapshot = currentSnapshot;
EDatabase database = EDatabase.clientDatabase();
database.lock(true);
try {
// database.checkFresh(oldSnapshot);
database.lowLevelSetCanUndoing(true);
try {
database.undo(newSnapshot);
} catch (Throwable e) {
ActivityLogger.logException(e);
database.recover(newSnapshot);
}
database.lowLevelSetCanUndoing(false);
} finally {
database.unlock();
endChanging();
}
currentSnapshot = newSnapshot;
if (newSnapshot.environment != oldSnapshot.environment) {
Environment.setThreadEnvironment(newSnapshot.environment);
if (newSnapshot.environment.toolSettings != oldSnapshot.environment.toolSettings)
ToolSettings.attachToGroup(newSnapshot.environment.toolSettings);
if (newSnapshot.techPool != oldSnapshot.techPool) {
LayerVisibility.preserveVisibility();
loadPreferences(newSnapshot.techPool);
User.technologyChanged();
WindowFrame.repaintAllWindows();
}
}
for(Iterator<Listener> it = Tool.getListeners(); it.hasNext(); ) {
Listener listener = it.next();
listener.endBatch(oldSnapshot, newSnapshot, undoRedo);
}
fireDatabaseChangeEvent(event);
// SwingUtilities.invokeLater(new DatabaseChangeRun(newSnapshot, undoRedo));
}
@Override
public void beep() {
if (Job.isClientThread()) {
User.playSound();
// Toolkit.getDefaultToolkit().beep();
} else {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
beep();
}
});
}
}
/**
* Method to tell whether undo can be done.
* This is used by the tool bar to determine whether the undo button should be available.
* @return true if undo can be done.
*/
public static boolean getUndoEnabled() { return undoEnabled; }
/**
* Method to tell whether redo can be done.
* This is used by the tool bar to determine whether the undo button should be available.
* @return true if redo can be done.
*/
public static boolean getRedoEnabled() { return redoEnabled; }
// /** Add a property change listener. This generates Undo and Redo enabled property changes */
// public static synchronized void addUndoRedoListener(PropertyChangeListener l)
// {
// assert SwingUtilities.isEventDispatchThread();
// undoRedoListenerList.add(PropertyChangeListener.class, l);
// }
//
// /** Remove a property change listener. */
// public static synchronized void removeUndoRedoListener(PropertyChangeListener l)
// {
// assert SwingUtilities.isEventDispatchThread();
// undoRedoListenerList.remove(PropertyChangeListener.class, l);
// }
private static void firePropertyChange(PropertyChangeEvent e) {
assert Job.isClientThread();
ToolBar.updateUndoRedoButtons(getUndoEnabled(), getRedoEnabled());
// Check all current WindowFrames and determine if displayed cells are still valid
// close windows that reference this cell
for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); )
{
WindowFrame wf = it.next();
WindowContent content = wf.getContent();
if (content == null) continue;
Cell c = content.getCell();
if (c != null && !c.isLinked()) // got removed in undo
{
wf.setCellWindow(null, null);
content.fullRepaint();
}
}
// Object[] listeners;
// synchronized (UserInterfaceMain.class) {
// listeners = undoRedoListenerList.getListenerList();
// }
// // Process the listeners last to first, notifying those that are interested in this event
// for (int i = listeners.length-2; i>=0; i-=2) {
// if (listeners[i] == PropertyChangeListener.class)
// ((PropertyChangeListener)listeners[i+1]).propertyChange(e);
// }
}
private static class PropertyChangeRun implements Runnable {
private PropertyChangeEvent e;
private PropertyChangeRun(PropertyChangeEvent e) { this.e = e; }
public void run() { firePropertyChange(e); }
}
/** Add a DatabaseChangeListener. It will be notified when
* state of the database changes.
* @param l the listener
*/
public static synchronized void addDatabaseChangeListener(DatabaseChangeListener l) {
listenerList.add(DatabaseChangeListener.class, l);
}
/** Remove a DatabaseChangeListener. */
public static synchronized void removeDatabaseChangeListener(DatabaseChangeListener l) {
listenerList.remove(DatabaseChangeListener.class, l);
}
/**
* Fire DatabaseChangeEvent to DatabaseChangeListeners.
* @param e DatabaseChangeEvent.
*/
public static void fireDatabaseChangeEvent(DatabaseChangeEvent e) {
Object[] listeners;
synchronized (User.class) {
listeners = listenerList.getListenerList();
}
// Process the listeners last to first, notifying those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i] == DatabaseChangeListener.class)
((DatabaseChangeListener)listeners[i+1]).databaseChanged(e);
}
}
private static void loadPreferences(TechPool techPool) {
assert techPool == Environment.getThreadEnvironment().techPool;
Pref.setCachedObjsFromPreferences();
EditingPreferences ep = new EditingPreferences(false, techPool);
EditingPreferences.setThreadEditingPreferences(ep);
lastSavedEp = ep;
currentGraphicsPreferences = new GraphicsPreferences(false, techPool);
EditWindow.setLayerVisibilityAll(LayerVisibility.loadPreferences());
ClickZoomWireListener.theOne.readPrefs();
}
public static EditingPreferences getEditingPreferences() {
assert Job.isClientThread();
return EditingPreferences.getThreadEditingPreferences();
}
private static EditingPreferences lastSavedEp;
public static void setEditingPreferences(EditingPreferences newEp) {
EditingPreferences oldEp = getEditingPreferences();
if (newEp.equals(oldEp)) return;
EditingPreferences.setThreadEditingPreferences(newEp);
Pref.delayPrefFlushing();
newEp.putPrefs(Pref.getPrefRoot(), true, lastSavedEp);
ClickZoomWireListener.theOne.readPrefs();
lastSavedEp = newEp;
Pref.resumePrefFlushing();
}
private static boolean badAccessReported = false;
public static GraphicsPreferences getGraphicsPreferences() {
if (!badAccessReported && !Job.isClientThread()) {
String msg = "GraphicsPreferences is accessed from " + Job.getRunningJob();
if (Job.getDebug()) {
ActivityLogger.logMessage(msg);
System.out.println(msg);
}
badAccessReported = true;
}
return currentGraphicsPreferences;
}
public static void setGraphicsPreferences(GraphicsPreferences gp) {
assert Job.isClientThread();
if (gp.equals(currentGraphicsPreferences)) return;
Pref.delayPrefFlushing();
gp.putPrefs(Pref.getPrefRoot(), true, currentGraphicsPreferences);
Pref.resumePrefFlushing();
currentGraphicsPreferences = gp;
}
private int lastId = 0;
private ArrayList<SavedHighlights> savedHighlights = new ArrayList<SavedHighlights>();
private static class SavedHighlights {
/** id of this saved state */ private final int id;
/** EditWindow_ of highlights */ private final EditWindow_ wnd;
/** list of saved Highlights */ private final List<Highlight> savedHighlights;
/** saved Highlight offset */ private final Point2D savedHighlightsOffset;
private SavedHighlights(int id, EditWindow_ wnd) {
this.id = id;
this.wnd = wnd;
savedHighlights = wnd.saveHighlightList();
savedHighlightsOffset = wnd.getHighlightOffset();
}
private void restore() {
wnd.restoreHighlightList(savedHighlights);
wnd.setHighlightOffset((int)savedHighlightsOffset.getX(), (int)savedHighlightsOffset.getY());
wnd.finishedHighlighting();
}
}
/**
* This class handles deactivation of the splash screen and forces it back to the top.
*/
private static class WindowsEvents implements WindowListener
{
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowClosing(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
public void windowDeactivated(WindowEvent e)
{
// TopLevel tl = TopLevel.getCurrentJFrame(false);
Window w = e.getOppositeWindow();
}
}
/**
* Places a custom event processor on the event queue in order to
* catch all exceptions generated by event processing.
*/
private static class EventProcessor extends EventQueue
{
private final String CLASS_NAME = getClass().getName();
private int dispatchDepth = 0;
private EventProcessor() {
Toolkit kit = Toolkit.getDefaultToolkit();
kit.getSystemEventQueue().push(this);
}
// public void postEvent(AWTEvent theEvent) {
// logger.entering(CLASS_NAME, "postEvent", theEvent);
// super.postEvent(theEvent);
// logger.exiting(CLASS_NAME, "postEvent");
// }
//
// public AWTEvent getNextEvent() throws InterruptedException {
// logger.entering(CLASS_NAME, "getNextEvent");
// AWTEvent event = super.getNextEvent();
// logger.exiting(CLASS_NAME, "getNextEvent", event);
// return event;
// }
//
// public synchronized AWTEvent peekEvent() {
// logger.entering(CLASS_NAME, "peekEvent");
// AWTEvent event = super.peekEvent();
// logger.exiting(CLASS_NAME, "peekEvent", event);
// return event;
// }
//
// public synchronized AWTEvent peekEvent(int id) {
// logger.entering(CLASS_NAME, "peekEvent", id);
// AWTEvent event = super.peekEvent(id);
// logger.exiting(CLASS_NAME, "peekEvent", event);
// return event;
// }
protected void dispatchEvent(AWTEvent e) {
// logger.entering(CLASS_NAME, "dispatchEvent", e);
// if (dispatchDepth == 0)
// database.lock(false);
dispatchDepth++;
try {
super.dispatchEvent(e);
}
catch(Throwable ex) {
ex.printStackTrace(System.err);
ActivityLogger.logException(ex);
if (ex instanceof Error && (!(ex instanceof AssertionError))) {
logger.throwing(CLASS_NAME, "dispatchEvent", ex);
throw (Error)ex;
}
// } finally {
// dispatchDepth--;
// if (dispatchDepth == 0)
// database.unlock();
}
// logger.exiting(CLASS_NAME, "dispatchEvent");
}
}
// private static void runThreadStatusTimer() {
// int delay = 1000*60*10; // milliseconds
// ElapseTimer timer = new ElapseTimer(delay, new ThreadStatusTask());
// timer.start();
// }
//
// private static class ThreadStatusTask implements ActionListener {
// public void actionPerformed(ActionEvent e) {
// Thread t = Thread.currentThread();
// ThreadGroup group = t.getThreadGroup();
// // get the top level group
// while (group.getParent() != null)
// group = group.getParent();
// Thread [] threads = new Thread[200];
// int numThreads = group.enumerate(threads, true);
// StringBuffer buf = new StringBuffer();
// for (int i=0; i<numThreads; i++) {
// buf.append("Thread["+i+"] "+threads[i]+": alive: "+threads[i].isAlive()+", interrupted: "+threads[i].isInterrupted()+"\n");
// }
// ActivityLogger.logThreadMessage(buf.toString());
// }
// }
/**
* Method to start the display of a progress dialog.
* @param msg the message to show in the progress dialog.
* @param filePath the file being read (null if not reading a file).
*/
public void startProgressDialog(String msg, String filePath)
{
stopProgressDialog();
try{
String message;
if (filePath == null) message = msg + "..."; else
message = "Reading " + msg + " " + filePath + "...";
progress = new Progress(message);
} catch (Exception e)
{
e.printStackTrace();
}
progress.setProgress(0);
}
/**
* Method to stop the progress bar
*/
public void stopProgressDialog()
{
if (progress != null)
{
progress.close();
progress = null;
}
}
/**
* Method to update the progress bar
* @param pct the percentage done (from 0 to 100).
*/
public void setProgressValue(int pct)
{
// progress is null if it is in quiet mode
if (progress != null)
{
progress.setProgress(pct);
}
}
/**
* Method to set a text message in the progress dialog.
* @param message the new progress message.
*/
public void setProgressNote(String message)
{
// progress is null if it is in quiet mode
if (progress != null)
progress.setNote(message);
}
/**
* Method to get text message in the progress dialog.
* @return text message in the progress dialog.
*/
public String getProgressNote()
{
if (progress == null) return "";
return progress.getNote();
}
}