/******************************************************************************
* Copyright: GPL v3 *
* *
* This program 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. *
* *
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *
******************************************************************************/
package dba.gui.auxClasses.jGraph;
import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxStylesheet;
import dba.gui.CustomTree;
import dba.options.Options;
import dba.utils.Localization;
import dba.utils.constants;
import dbaCore.data.Database;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
public abstract class JGraphView extends JPanel implements Observer {
/**
*
*/
private static final long serialVersionUID = -8564817921313692339L;
private ArrayList<Observer> observers;
protected mxGraph graph;
protected boolean zoomEnabled;
protected mxGraphComponent graphComponent;
protected mxCell selectedCell;
protected Localization locale;
protected JGraphView() {
super();
observers = new ArrayList<>();
graph = new mxGraph();
locale = Localization.getInstance();
zoomEnabled = true;
}
/**
* Initializes the Listeners for the graph and the graphcomponent
*/
protected void initListeners() {
//Manages the click on the graphComponent when it has the focus
graph.getSelectionModel().addListener(mxEvent.CHANGE, new mxEventSource.mxIEventListener() {
@Override
public void invoke(Object arg0, mxEventObject arg1) {
updateSelectedCell();
}
});
//Manages the clicks on the graphComponent when it hasn't the focus
graphComponent.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent focusEvent) {
updateSelectedCell();
}
@Override
public void focusLost(FocusEvent focusEvent) {
}
});
//Notice when user zooms via the outline-tab
graph.getView().addListener("scale", new mxEventSource.mxIEventListener() {
@Override
public void invoke(Object sender, mxEventObject evt) {
notifyObservers();
}
});
//Uses Mouswheel+Control to zoom zoom zoom
graphComponent.addMouseWheelListener(new MouseWheelListener() {
@Override
public void mouseWheelMoved(MouseWheelEvent event) {
double scale = graph.getView().getScale();
// Only scroll when Control is pressed
if (!event.isControlDown()) {
return;
}
if (event.getWheelRotation() < 0) {
if (scale < constants.maximumZoomFactor) {
graphComponent.zoomIn();
}
} else {
if (scale > constants.minimumZoomFactor) {
graphComponent.zoomOut();
}
}
notifyObservers();
}
});
}
/**
* Returns the mxGraphComponent used in this View
*
* @return the currently used mxGraphComponent
*/
public mxGraphComponent getGraphComponent() {
return graphComponent;
}
/**
* Returns all available zoomFactors
*
* @return a array containing the available zoomfactors
*/
public String[] getZoomFactors() {
ArrayList<String> factors = new ArrayList<>();
String[] fixedFactors = new String[]{"25%", "50%", "75%", "100%", "125%", "150%", locale.getString("Width"),
locale.getString("Page")};
factors.add((int) (graph.getView().getScale() * 100) + "%");
factors.addAll(Arrays.asList(fixedFactors));
return factors.toArray(new String[factors.size()]);
}
public void setZoomEnabled(boolean zoomEnabled) {
this.zoomEnabled = zoomEnabled;
}
/**
* Zooms to the given percentage
*
* @param factor the zoomFactor e.g. 100, 50%
*/
public void zoom(String factor) {
if (zoomEnabled) {
if (factor.equalsIgnoreCase(locale.getString("Width"))) {
fitWidth();
} else if (factor.equalsIgnoreCase(locale.getString("Page"))) {
fitPage();
} else {
setZoomFactor(filterZoomFactor(factor));
}
}
}
/**
* Changes the ZoomFactor in order to display all horizontal content
*/
private void fitWidth() {
double difference = (graphComponent.getWidth() - 25) / graph.getGraphBounds().getWidth();
setZoomFactor((graph.getView().getScale() * 100) * difference);
}
/**
* Changes the ZoomFactor in order to display all horizonal and vertical content
*/
private void fitPage() {
double horizontalDelta = (graphComponent.getWidth() - 25) / graph.getGraphBounds().getWidth();
double verticalDelta = (graphComponent.getHeight() - 25) / graph.getGraphBounds().getHeight();
double delta = horizontalDelta < verticalDelta ? horizontalDelta : verticalDelta;
setZoomFactor((graph.getView().getScale() * 100) * delta);
}
/**
* Changes the zoomFactor of the GraphComponent
*
* @param factor the zoomFactor
*/
private void setZoomFactor(Double factor) {
if (factor != null) {
factor /= 100;
if (factor != graph.getView().getScale()) {
graphComponent.zoomTo(factor, false);
}
notifyObservers();
}
}
/**
* Filters the zoomLevel entered by the user
* @param value the value to be filtered
* @return a zoomlevel between acceptable bounds
*/
private double filterZoomFactor(String value){
double factor;
try{
factor = Double.parseDouble((value).replace("%",""));
}catch (NumberFormatException ex){
factor = -1;
}
if (factor > constants.maximumZoomFactor * 100) {
factor = constants.maximumZoomFactor * 100;
} else if (factor < constants.minimumZoomFactor * 100) {
factor = constants.minimumZoomFactor * 100;
}
return factor;
}
/**
* Updates the selected Item of the Tree when the user click on a Cell
*/
private void updateSelectedCell() {
selectedCell = (mxCell) graph.getSelectionCell();
if (selectedCell != null) {
CustomTree.getInstance().setSelectedNode(selectedCell.getValue());
} else {
CustomTree.getInstance().setSelectedItem(0);
}
}
/**
* Exports the graph to a .png-file
*
* @param path the target-location for the .png-file
* @param name the name of the .png-file
*/
public void exportToPng(String path, String name) {
Dimension d = graphComponent.getGraphControl().getSize();
BufferedImage image = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
graphComponent.getGraphControl().paint(g);
final File outputfile = new File(path.replace(".png", name + ".png"));
try {
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Defines Stylesheets for the Attributes
*/
protected void initStyle() {
mxStylesheet stylesheet = graph.getStylesheet();
// Define Style for Attribute with no Image
Hashtable<String, Object> style = new Hashtable<>();
style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_LABEL);
style.put(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_BOTTOM);
style.put(mxConstants.STYLE_ALIGN, mxConstants.ALIGN_LEFT);
style.put(mxConstants.STYLE_AUTOSIZE, true);
style.put(mxConstants.STYLE_FILLCOLOR, Options.getInstance().getAttributeColor());
style.put(mxConstants.STYLE_FONTCOLOR, Options.getInstance().getFontColor());
style.put(mxConstants.STYLE_FONTSIZE, 15);
style.put(mxConstants.STYLE_MOVABLE, false);
style.put(mxConstants.STYLE_EDITABLE, false);
style.put(mxConstants.STYLE_RESIZABLE, false);
style.put(mxConstants.STYLE_STROKEWIDTH, 1);
style.put(mxConstants.STYLE_IMAGE_HEIGHT, 22);
style.put(mxConstants.STYLE_STROKECOLOR, Options.getInstance().getBorderColor());
stylesheet.putCellStyle("ATTRIBUTE_NOIMAGE", style);
// Define Style for Primary-Key Attribute represented by a small "space"-image
style = (Hashtable<String, Object>) style.clone();
style.put(mxConstants.STYLE_SPACING_LEFT, 21); //16 + 5 Pixel space
stylesheet.putCellStyle("ATTRIBUTE_SPACE_SMALL", style);
// Define Style for Primary-Key Attribute represented by a big "space"-image
style = (Hashtable<String, Object>) style.clone();
style.put(mxConstants.STYLE_SPACING_LEFT, 38); //33 + 5 Pixel space
stylesheet.putCellStyle("ATTRIBUTE_SPACE_BIG", style);
// Define Style for Primary-Key/Foreign-Key Attribute
style = (Hashtable<String, Object>) style.clone();
style.put(mxConstants.STYLE_IMAGE_WIDTH, 33);
style.put(mxConstants.STYLE_IMAGE, "/res/icons/graph_pkfk.png");
stylesheet.putCellStyle("ATTRIBUTE_PKFK", style);
// Define Style for Primary-Key Attribute represented by a big image
style = (Hashtable<String, Object>) style.clone();
style.put(mxConstants.STYLE_IMAGE_WIDTH, 16);
style.put(mxConstants.STYLE_IMAGE, "/res/icons/graph_pk.png");
stylesheet.putCellStyle("ATTRIBUTE_PK_BIG", style);
// Define Style for Foreign-Key Attribute represented by a big image
style = (Hashtable<String, Object>) style.clone();
style.put(mxConstants.STYLE_IMAGE, "/res/icons/graph_fk.png");
stylesheet.putCellStyle("ATTRIBUTE_FK_BIG", style);
// Define Style for Primary-Key Attribute represented by a small image
style = (Hashtable<String, Object>) style.clone();
style.put(mxConstants.STYLE_SPACING_LEFT, 21); //16 + 5 Pixel space
style.put(mxConstants.STYLE_IMAGE, "/res/icons/graph_pk.png");
stylesheet.putCellStyle("ATTRIBUTE_PK_SMALL", style);
// Define Style for Foreign-KEy Attribute represented by a small image
style = (Hashtable<String, Object>) style.clone();
style.put(mxConstants.STYLE_IMAGE, "/res/icons/graph_fk.png");
stylesheet.putCellStyle("ATTRIBUTE_FK_SMALL", style);
}
// Observer methods
/**
* Add a Observer to the Collection
*
* @param observer the observer to add
* @return true for success
*/
public boolean addObserver(Observer observer) {
return observers.add(observer);
}
/**
* Notifies Observers about change
*/
protected void notifyObservers() {
for (Observer stalker : observers) {
stalker.update(null, this);
}
}
public abstract void display(Database database);
@Override
public void update(Observable observable, Object o) {
graph.getModel().beginUpdate();
initStyle();
graph.getModel().endUpdate();
graphComponent.getViewport().setBackground(Color.decode(Options.getInstance().getBackgroundColor()));
display(CustomTree.getInstance().getDatabase());
this.revalidate();
}
}