/*******************************************************************************
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package hr.fer.zemris.vhdllab.applets.schema2.gui;
import hr.fer.zemris.vhdllab.applets.editor.schema2.constants.Constants;
import hr.fer.zemris.vhdllab.applets.editor.schema2.enums.ECanvasState;
import hr.fer.zemris.vhdllab.applets.editor.schema2.enums.EPropertyChange;
import hr.fer.zemris.vhdllab.applets.editor.schema2.exceptions.CommandExecutorException;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ICommand;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ILocalGuiController;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ISchemaComponent;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ISchemaController;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ISchemaCore;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ISchemaInfo;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.Caseless;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.PlacedComponent;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.LocalController;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.SchemaCore;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.commands.AddUpdatePredefinedPrototype;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.commands.DeleteComponentCommand;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.commands.DeleteWireCommand;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.commands.InvalidateObsoleteUserComponents;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.commands.RebuildPrototypeCollection;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.commands.RemovePrototype;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.serialization.SchemaDeserializer;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.serialization.SchemaSerializer;
import hr.fer.zemris.vhdllab.applets.editor.schema2.predefined.PredefinedComponentsParser;
import hr.fer.zemris.vhdllab.applets.schema2.gui.canvas.CanvasToolbar;
import hr.fer.zemris.vhdllab.applets.schema2.gui.canvas.CanvasToolbarLocalGUIController;
import hr.fer.zemris.vhdllab.applets.schema2.gui.canvas.SchemaCanvas;
import hr.fer.zemris.vhdllab.applets.schema2.gui.toolbars.componentproperty.CPToolbar;
import hr.fer.zemris.vhdllab.applets.schema2.gui.toolbars.selectcomponent.TabbedCTAddToolbar;
import hr.fer.zemris.vhdllab.entity.File;
import hr.fer.zemris.vhdllab.entity.Project;
import hr.fer.zemris.vhdllab.platform.manager.editor.impl.AbstractEditor;
import hr.fer.zemris.vhdllab.platform.manager.workspace.WorkspaceAdapter;
import hr.fer.zemris.vhdllab.platform.manager.workspace.WorkspaceListener;
import hr.fer.zemris.vhdllab.service.ci.CircuitInterface;
import hr.fer.zemris.vhdllab.service.hierarchy.Hierarchy;
import hr.fer.zemris.vhdllab.service.workspace.FileReport;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
public class SchemaMainPanel extends AbstractEditor {
protected class ModificationListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
setModified(true);
}
}
/* static fields */
private static final long serialVersionUID = -6643347269051956602L;
/* model private fields */
private ISchemaCore core;
protected ISchemaController controller;
/* GUI private fields */
private SchemaCanvas canvas;
private CanvasToolbar canTool;
protected ILocalGuiController localGUIController;
private CPToolbar componentPropertyToolbar;
private TabbedCTAddToolbar componentToAddToolbar;
/* IEditor private fields */
private WorkspaceListener appletListener;
/**
* Right panel divider width
*/
protected double rightPanelWidth;
/**
* Glavni split pane koji dijeli canvas od toolbara
*/
protected JSplitPane verticalSplitPane;
/**
* desni split pane koji dijeli property toolbar i componentToAdd toolbar
*/
protected JSplitPane horizontalSplitPane;
protected double rightPanelDividerPosition;
protected boolean resizingIsOver = false;
/* ctors */
public SchemaMainPanel() {
wrapInScrollPane = false;
initStatic();
}
/* methods */
/**
* Initializes fields with initial value and assumes that the project
* container IS NOT SET. Therefore, this is called from a ctor.
*/
private void initStatic() {
core = new SchemaCore();
controller = new LocalController();
canvas = new SchemaCanvas();
localGUIController = new CanvasToolbarLocalGUIController();
rightPanelWidth = 200;
rightPanelDividerPosition = .5;
controller.registerCore(core);
controller.addListener(EPropertyChange.ANY_CHANGE,
new ModificationListener());
}
/**
* Performs all initialization assuming that a project container HAS BEEN
* SET. Therefore, this is not called from a ctor.
*/
private JComponent initDynamic() {
// init prototype components
initPrototypes();
// init gui
return initGUI();
}
private void initPrototypes() {
InputStream predefined;
// init default prototypes
predefined = PredefinedComponentsParser.class
.getResourceAsStream(Constants.PREDEFINED_FILENAME);
// init user component prototypes
List<CircuitInterface> usercis = getUserPrototypeList();
// send EmptyCommand to alert listeners
controller.send(new RebuildPrototypeCollection(predefined, usercis));
if (usercis != null) {
controller.send(new InvalidateObsoleteUserComponents(usercis));
}
}
private List<CircuitInterface> getUserPrototypeList() {
File thisfile = getFile();
if (thisfile == null)
return null;
System.out.println("Initializing user prototypes.");
Project project = thisfile.getProject();
List<File> circuitnames = new ArrayList<File>();
for (File info : container.getWorkspaceManager().getFilesForProject(
project)) {
if (info.getType().isCircuit()) {
circuitnames.add(info);
}
}
Hierarchy hierarchy = container.getWorkspaceManager().getHierarchy(
project);
List<CircuitInterface> usercircuits = new ArrayList<CircuitInterface>();
for (File circuit : circuitnames) {
// do not put prototypes for the modelled component or for
// components that depend on this component
if (thisfile.equals(circuit)
|| hierarchy.fileHasDependency(circuit, thisfile))
continue;
// get circuit interface for the component
CircuitInterface circint = container.getMetadataExtractionService()
.extractCircuitInterface(circuit.getId());
usercircuits.add(circint);
}
return usercircuits;
}
private JComponent initGUI() {
final JPanel control = new JPanel(new BorderLayout());
componentPropertyToolbar = new CPToolbar(localGUIController, controller);
componentToAddToolbar = new TabbedCTAddToolbar(controller,
localGUIController);
controller.addListener(EPropertyChange.CANVAS_CHANGE, canvas);
controller.addListener(EPropertyChange.PROTOTYPES_CHANGE,
componentToAddToolbar);
controller.addListener(EPropertyChange.PROPERTY_CHANGE,
componentPropertyToolbar);
// component selection toolbar
localGUIController.addListener(
CanvasToolbarLocalGUIController.PROPERTY_CHANGE_STATE,
componentToAddToolbar);
// canvas toolbar
localGUIController.addListener(canvas);
// property toolbar
localGUIController.addListener(
CanvasToolbarLocalGUIController.PROPERTY_CHANGE_SELECTION,
componentPropertyToolbar);
// #########Added by Delac: key listener for delete and escape
// action#########
control.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
"delete_key_action");
control.getActionMap().put("delete_key_action", new AbstractAction() {
private static final long serialVersionUID = 1844240025875439799L;
@Override
public void actionPerformed(ActionEvent e) {
if (localGUIController.getSelectedType() == CanvasToolbarLocalGUIController.TYPE_WIRE) {
if (SchemaCanvas.doDeleteWire(control)) {
Caseless sel = localGUIController
.getSelectedComponent();
localGUIController
.setSelectedComponent(
sel,
CanvasToolbarLocalGUIController.TYPE_NOTHING_SELECTED);
ICommand delete = new DeleteWireCommand(sel);
controller.send(delete);
}
} else if (localGUIController.getSelectedType() == CanvasToolbarLocalGUIController.TYPE_COMPONENT) {
Caseless sel = localGUIController.getSelectedComponent();
localGUIController
.setSelectedComponent(
sel,
CanvasToolbarLocalGUIController.TYPE_NOTHING_SELECTED);
ICommand delete = new DeleteComponentCommand(sel);
controller.send(delete);
}
}
});
control.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
"escape_key_action");
control.getActionMap().put("escape_key_action", new AbstractAction() {
private static final long serialVersionUID = 1844240025875439799L;
@Override
public void actionPerformed(ActionEvent e) {
localGUIController.setState(ECanvasState.MOVE_STATE);
}
});
// #############
canvas.registerLocalController(localGUIController);
canvas.registerSchemaController(controller);
canTool = new CanvasToolbar(null);
canTool.registerController(localGUIController);
localGUIController.addListener(
CanvasToolbarLocalGUIController.PROPERTY_CHANGE_STATE, canTool);
JScrollPane componentPropertyToolbarScrollPane = new JScrollPane(
componentPropertyToolbar);
JScrollPane componentToAddToolbarScrollPane = new JScrollPane(
componentToAddToolbar);
horizontalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
componentPropertyToolbarScrollPane,
componentToAddToolbarScrollPane);
/* init canvas */
JScrollPane pane = new JScrollPane(canvas);
JPanel panel = new JPanel(new BorderLayout());
panel.add(canTool, BorderLayout.NORTH);
panel.add(pane, BorderLayout.CENTER);
verticalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel,
horizontalSplitPane);
horizontalSplitPane.setOneTouchExpandable(true);
verticalSplitPane.setOneTouchExpandable(true);
control.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
int width = control.getWidth();
int height = control.getHeight();
if (width <= 0) {
return;
}
verticalSplitPane
.setDividerLocation(1
- rightPanelWidth
/ ((width >= rightPanelWidth) ? width
: rightPanelWidth));
if (height <= 0) {
return;
}
horizontalSplitPane
.setDividerLocation(rightPanelDividerPosition);
resizingIsOver = true;
}
});
control.add(verticalSplitPane, BorderLayout.CENTER);
return control;
}
private void resetSchema() {
core.reset();
controller.clearCommandCache();
}
public ISchemaController getController() {
return controller;
}
/* IEditor methods */
@Override
protected void doDispose() {
container.getWorkspaceManager().removeListener(appletListener);
}
@Override
public void undo() {
try {
if (controller.canUndo()) {
controller.undo();
}
} catch (CommandExecutorException e) {
e.printStackTrace();
}
}
@Override
public void redo() {
try {
if (controller.canRedo()) {
controller.redo();
}
} catch (CommandExecutorException e) {
e.printStackTrace();
}
}
@Override
public String getData() {
SchemaSerializer ss = new SchemaSerializer();
StringWriter writer = new StringWriter(500);
try {
ss.serializeSchema(writer, core.getSchemaInfo());
} catch (IOException e) {
return "<schemaInfo></schemaInfo>";
}
return writer.toString();
}
public boolean isProtoInEditor(Caseless cmpname) {
boolean isused = false;
ISchemaInfo nfo = controller.getSchemaInfo();
for (Entry<Caseless, ISchemaComponent> ntry : nfo.getPrototypes()
.entrySet()) {
if (ntry.getKey().equals(cmpname)) {
isused = true;
break;
}
}
return isused;
}
public boolean isPlacedInEditor(String fileName) {
boolean isplaced = false;
for (PlacedComponent plc : controller.getSchemaInfo().getComponents()) {
CircuitInterface plcci = plc.comp.getCircuitInterface();
if (plcci.isName(fileName)) {
isplaced = true;
break;
}
}
return isplaced;
}
protected boolean hasCircuitInterfaceChanged(String fileName,
CircuitInterface ci) {
for (PlacedComponent plc : controller.getSchemaInfo().getComponents()) {
CircuitInterface plcci = plc.comp.getCircuitInterface();
if (plcci.isName(fileName)) {
if (!ci.equals(plcci))
return true;
}
}
return false;
}
public boolean shouldBeAdded(FileReport report) {
Hierarchy hierarchy = report.getHierarchy();
File thisfile = getFile();
System.out.println("This = " + thisfile.getName() + "; other = "
+ report.getFile().getName());
if (thisfile.equals(report.getFile())
|| hierarchy.fileHasDependency(report.getFile(), thisfile)) {
System.out.println("Other should NOT be added to this.");
return false;
}
System.out.println("Other should be added to this.");
return true;
}
@Override
protected JComponent doInitWithoutData() {
return initDynamic();
}
@Override
protected void doInitWithData(File f) {
resetSchema();
if (f != null) {
SchemaDeserializer sd = new SchemaDeserializer();
StringReader stread = new StringReader(f.getData());
core.setSchemaInfo(sd.deserializeSchema(stread));
}
initPrototypes();
appletListener = new WorkspaceAdapter() {
@Override
public void fileSaved(FileReport report) {
File file = report.getFile();
// don't do anything if this editor was saved
if (file.getName().equals(
SchemaMainPanel.this.getFile().getName()))
return;
// don't add a non circuit
if (!file.getType().isCircuit())
return;
// shouldn't be added to prototypes
if (!shouldBeAdded(report)) {
// then remove it, if it's used in schema
Caseless cfn = new Caseless(file.getName());
if (isProtoInEditor(cfn)) {
boolean oldmodifiedstatus = SchemaMainPanel.this
.isModified();
controller.send(new RemovePrototype(cfn));
if (isPlacedInEditor(file.getName())) {
controller
.send(new InvalidateObsoleteUserComponents(
controller.getSchemaInfo()
.getPrototyper()));
} else {
// only prototype changes were made
SchemaMainPanel.this.setModified(oldmodifiedstatus);
}
}
return;
}
// must be added to prototype collection
CircuitInterface ci = container.getMetadataExtractionService()
.extractCircuitInterface(file.getId());
boolean oldmodifiedstatus = SchemaMainPanel.this.isModified();
boolean isplaced = isPlacedInEditor(file.getName());
controller.send(new AddUpdatePredefinedPrototype(ci));
if (isplaced) {
// now check if circuit interface has changed
if (hasCircuitInterfaceChanged(file.getName(), ci)) {
// only perform invalidation if circuit interface has
// changed
controller.send(new InvalidateObsoleteUserComponents(
controller.getSchemaInfo().getPrototyper()));
} else {
// changes were made only to the prototype collection
SchemaMainPanel.this.setModified(oldmodifiedstatus);
}
} else {
// changes were made only to the prototype collection
SchemaMainPanel.this.setModified(oldmodifiedstatus);
}
}
@Override
public void fileCreated(FileReport report) {
File file = report.getFile();
// don't add a non-circuit
if (!file.getType().isCircuit())
return;
// check hierarchy to see if this should be added
if (!shouldBeAdded(report))
return;
// we must add - fetch circuit interface
CircuitInterface ci = container.getMetadataExtractionService()
.extractCircuitInterface(file.getId());
boolean oldmodifiedstatus = SchemaMainPanel.this.isModified();
controller.send(new AddUpdatePredefinedPrototype(ci));
SchemaMainPanel.this.setModified(oldmodifiedstatus);
}
@Override
public void fileDeleted(FileReport report) {
File file = report.getFile();
String fileName = file.getName();
// don't bother with non-circuits
if (!file.getType().isCircuit())
return;
// check if it was used in editor at all
Caseless cfn = new Caseless(fileName);
if (!isProtoInEditor(cfn))
return;
// it was used in editor, thus, it must be removed
boolean oldmodifiedstatus = SchemaMainPanel.this.isModified();
controller.send(new RemovePrototype(cfn));
if (isPlacedInEditor(fileName)) {
controller.send(new InvalidateObsoleteUserComponents(
controller.getSchemaInfo().getPrototyper()));
} else {
// only changes to prototypes were made
SchemaMainPanel.this.setModified(oldmodifiedstatus);
}
}
// @Override
// public void resourceDeleted(String projectName, String fileName)
// {
// /* check if the deleted resource was used in schema */
// boolean isused = false;
//
// for (PlacedComponent plc :
// controller.getSchemaInfo().getComponents()) {
// CircuitInterface plcci = plc.comp.getCircuitInterface();
// if (plcci.getEntityName().equalsIgnoreCase(fileName)) {
// isused = true;
// break;
// }
// }
//
// /* perform changes */
// if (isused) {
// boolean oldmodifiedstatus = isModified();
//
// List<CircuitInterface> usercis = getUserPrototypeList();
// controller.send(new RebuildPrototypeCollection(null, usercis));
// controller.send(new InvalidateObsoleteUserComponents(usercis));
//
// SchemaMainPanel.this.setModified(oldmodifiedstatus);
// }
// }
};
container.getWorkspaceManager().addListener(appletListener);
}
}