package com.baselet.diagram; import java.awt.Component; import java.awt.MouseInfo; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Vector; import javax.swing.JOptionPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.baselet.control.ErrorMessages; import com.baselet.control.HandlerElementMap; import com.baselet.control.Main; import com.baselet.control.SharedUtils; import com.baselet.control.basics.Converter; import com.baselet.control.basics.geom.Point; import com.baselet.control.constants.Constants; import com.baselet.control.enums.Program; import com.baselet.diagram.io.DiagramFileHandler; import com.baselet.element.ComponentSwing; import com.baselet.element.NewGridElement; import com.baselet.element.interfaces.GridElement; import com.baselet.element.old.element.Relation; import com.baselet.gui.BaseGUI; import com.baselet.gui.CurrentGui; import com.baselet.gui.command.Controller; import com.baselet.gui.listener.DiagramListener; import com.baselet.gui.listener.GridElementListener; import com.baselet.gui.listener.OldRelationListener; public class DiagramHandler { private static final Logger log = LoggerFactory.getLogger(DiagramHandler.class); private boolean isChanged; private final DiagramFileHandler fileHandler; private FontHandler fontHandler; protected DrawPanel drawpanel; private final Controller controller; protected DiagramListener listener; private String helptext; private boolean enabled; private int gridSize; private OldRelationListener relationListener; private GridElementListener gridElementListener; public static DiagramHandler forExport(FontHandler fontHandler) { DiagramHandler returnHandler = new DiagramHandler(null, false); if (fontHandler != null) { returnHandler.fontHandler = fontHandler; } return returnHandler; } public DiagramHandler(File diagram) { this(diagram, false); } protected DiagramHandler(File diagram, boolean nolistener) { gridSize = Constants.DEFAULTGRIDSIZE; isChanged = false; enabled = true; drawpanel = createDrawPanel(); controller = new Controller(this); fontHandler = new FontHandler(this); fileHandler = DiagramFileHandler.createInstance(this, diagram); if (!nolistener) { setListener(new DiagramListener(this)); } if (diagram != null) { fileHandler.doOpen(); } boolean extendedPopupMenu = false; BaseGUI gui = CurrentGui.getInstance().getGui(); if (gui != null) { gui.setValueOfZoomDisplay(getGridSize()); extendedPopupMenu = gui.hasExtendedContextMenu(); } initDiagramPopupMenu(extendedPopupMenu); } protected DrawPanel createDrawPanel() { return new DrawPanel(this, true); } protected void initDiagramPopupMenu(boolean extendedPopupMenu) { drawpanel.setComponentPopupMenu(new DiagramPopupMenu(extendedPopupMenu)); } public void setEnabled(boolean en) { if (!en && enabled) { drawpanel.removeMouseListener(listener); drawpanel.removeMouseMotionListener(listener); enabled = false; } else if (en && !enabled) { drawpanel.addMouseListener(listener); drawpanel.addMouseMotionListener(listener); enabled = true; } } protected void setListener(DiagramListener listener) { this.listener = listener; drawpanel.addMouseListener(this.listener); drawpanel.addMouseMotionListener(this.listener); drawpanel.addMouseWheelListener(this.listener); } public DiagramListener getListener() { return listener; } public void setChanged(boolean changed) { if (isChanged != changed) { isChanged = changed; BaseGUI gui = CurrentGui.getInstance().getGui(); if (gui != null) { gui.setDiagramChanged(this, changed); } } } public DrawPanel getDrawPanel() { return drawpanel; } public DiagramFileHandler getFileHandler() { return fileHandler; } public FontHandler getFontHandler() { return fontHandler; } public Controller getController() { return controller; } // returnvalue needed for eclipse plugin // returns true if the file is saved, else returns false public boolean doSave() { try { fileHandler.doSave(); reloadPalettes(); CurrentGui.getInstance().getGui().afterSaving(); return true; } catch (IOException e) { log.error(ErrorMessages.ERROR_SAVING_FILE, e); displayError(ErrorMessages.ERROR_SAVING_FILE + e.getMessage()); return false; } } public void doSaveAs(String extension) { if (drawpanel.getGridElements().isEmpty()) { displayError(ErrorMessages.ERROR_SAVING_EMPTY_DIAGRAM); } else { try { fileHandler.doSaveAs(extension); reloadPalettes(); CurrentGui.getInstance().getGui().afterSaving(); } catch (IOException e) { log.error(ErrorMessages.ERROR_SAVING_FILE, e); displayError(ErrorMessages.ERROR_SAVING_FILE + e.getMessage()); } } } public void doPrint() { PrinterJob printJob = PrinterJob.getPrinterJob(); printJob.setPrintable(getDrawPanel()); if (printJob.printDialog()) { try { printJob.print(); } catch (PrinterException pe) { displayError(ErrorMessages.ERROR_PRINTING); } } } // reloads the diagram from file + updates gui public void reload() { drawpanel.removeAll(); fileHandler.doOpen(); drawpanel.updatePanelAndScrollbars(); } // reloads palettes if the palette has been changed. private void reloadPalettes() { for (DiagramHandler d : Main.getInstance().getPalettes().values()) { if (d.getFileHandler().equals(getFileHandler()) && !d.equals(this)) { d.reload(); } } getDrawPanel().getSelector().updateSelectorInformation(); // Must be updated to remain in the current Property Panel } public void doClose() { if (askSaveIfDirty()) { Main.getInstance().getDiagrams().remove(this); // remove this DiagramHandler from the list of managed diagrams drawpanel.getSelector().deselectAll(); // deselect all elements of the drawpanel (must be done BEFORE closing the tab, because otherwise it resets this DrawHandler again as the current DrawHandler CurrentGui.getInstance().getGui().close(this); // close the GUI (tab, ...) and set the next active tab as the CurrentDiagram // update property panel to now selected diagram (or to empty if no diagram exists) DiagramHandler newhandler = CurrentDiagram.getInstance().getDiagramHandler(); if (newhandler != null) { newhandler.getDrawPanel().getSelector().updateSelectorInformation(); } else { Main.getInstance().setPropertyPanelToGridElement(null); } } } /** * closes this diagram handler and adds a new empty diagram if this was the last diagram of UMLet */ public void doCloseAndAddNewIfNoLeft() { doClose(); if (Main.getInstance().getDiagrams().size() == 0) { Main.getInstance().doNew(); } } public String getName() { String name = fileHandler.getFileName(); if (name.contains(".")) { name = name.substring(0, name.lastIndexOf(".")); } return name; } public String getFullPathName() { return fileHandler.getFullPathName(); } public GridElementListener getEntityListener(GridElement e) { if (e instanceof Relation) { if (relationListener == null) { relationListener = new OldRelationListener(this); } return relationListener; } else { if (gridElementListener == null) { gridElementListener = new GridElementListener(this); } return gridElementListener; } } public boolean askSaveIfDirty() { if (isChanged) { int ch = JOptionPane.showOptionDialog(CurrentGui.getInstance().getGui().getMainFrame(), "Save changes?", Program.getInstance().getProgramName() + " - " + getName(), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null); if (ch == JOptionPane.YES_OPTION) { doSave(); return true; } else if (ch == JOptionPane.NO_OPTION) { return true; } return false; // JOptionPane.CANCEL_OPTION } return true; } public void setHelpText(String helptext) { this.helptext = helptext; } public String getHelpText() { if (helptext == null) { return Constants.getDefaultHelptext(); } else { return helptext; } } public boolean isChanged() { return isChanged; } public int getGridSize() { return gridSize; } public float getZoomFactor() { return (float) getGridSize() / (float) Constants.DEFAULTGRIDSIZE; } public void setGridSize(int gridSize) { this.gridSize = gridSize; } public int realignToGrid(double val) { return realignToGrid(true, val, false); } public int realignToGrid(boolean logRealign, double val) { return realignToGrid(logRealign, val, false); } public int realignToGrid(boolean logRealign, double val, boolean roundUp) { return SharedUtils.realignTo(logRealign, val, roundUp, gridSize); } public static int realignTo(int val, int toVal) { return SharedUtils.realignTo(false, val, false, toVal); } public static void zoomEntity(int fromFactor, int toFactor, GridElement e) { Vector<GridElement> vec = new Vector<GridElement>(); vec.add(e); zoomEntities(fromFactor, toFactor, vec); } public static void zoomEntities(int fromFactor, int toFactor, List<GridElement> selectedEntities) { /** * The entities must be resized to the new factor */ for (GridElement entity : selectedEntities) { int newX = entity.getRectangle().x * toFactor / fromFactor; int newY = entity.getRectangle().y * toFactor / fromFactor; int newW = entity.getRectangle().width * toFactor / fromFactor; int newH = entity.getRectangle().height * toFactor / fromFactor; entity.setLocation(realignTo(newX, toFactor), realignTo(newY, toFactor)); // Normally there should be no realign here but relations and custom elements sometimes must be realigned therefore we don't log it as an error entity.setSize(realignTo(newW, toFactor), realignTo(newH, toFactor)); // Resize the coordinates of the points of the relations if (entity instanceof Relation) { for (Point point : ((Relation) entity).getLinePoints()) { newX = point.getX() * toFactor / fromFactor; newY = point.getY() * toFactor / fromFactor; point.setX(realignTo(newX, toFactor)); point.setY(realignTo(newY, toFactor)); } } } } public void setGridAndZoom(int factor) { setGridAndZoom(factor, true); } public void setGridAndZoom(int factor, boolean manualZoom) { /** * Store the old gridsize and the new one. Furthermore check if the zoom process must be made */ int oldGridSize = getGridSize(); if (factor < 1 || factor > 20) { return; // Only zoom between 10% and 200% is allowed } if (factor == oldGridSize) { return; // Only zoom if gridsize has changed } setGridSize(factor); /** * Zoom entities to the new gridsize */ zoomEntities(oldGridSize, gridSize, getDrawPanel().getGridElements()); // AB: Zoom origin getDrawPanel().zoomOrigin(oldGridSize, gridSize); /** * The zoomed diagram will shrink to the upper left corner and grow to the lower right * corner but we want to have the zoom center in the middle of the actual visible drawpanel * so we have to change the coordinates of the entities again */ if (manualZoom) { // calculate mouse position relative to UMLet scrollpane Point mouseLocation = Converter.convert(MouseInfo.getPointerInfo().getLocation()); Point viewportLocation = Converter.convert(getDrawPanel().getScrollPane().getViewport().getLocationOnScreen()); float x = mouseLocation.x - viewportLocation.x; float y = mouseLocation.y - viewportLocation.y; // And add any space on the upper left corner which is not visible but reachable by scrollbar x += getDrawPanel().getScrollPane().getViewport().getViewPosition().getX(); y += getDrawPanel().getScrollPane().getViewport().getViewPosition().getY(); // The result is the point where we want to center the zoom of the diagram float diffx, diffy; diffx = x - x * gridSize / oldGridSize; diffy = y - y * gridSize / oldGridSize; // AB: Move origin in opposite direction log.debug("diffX/diffY: " + diffx + "/" + diffy); log.debug("Manual Zoom Delta: " + realignToGrid(false, diffx) + "/" + realignToGrid(false, diffy)); getDrawPanel().moveOrigin(realignToGrid(false, -diffx), realignToGrid(false, -diffy)); for (GridElement e : getDrawPanel().getGridElements()) { e.setLocationDifference(realignToGrid(false, diffx), realignToGrid(false, diffy)); } /** * Now we have to do some additional "clean up" stuff which is related to the zoom */ getDrawPanel().updatePanelAndScrollbars(); // Set changed only if diagram is not empty (otherwise no element has been changed) if (!drawpanel.getGridElements().isEmpty()) { setChanged(true); } BaseGUI gui = CurrentGui.getInstance().getGui(); if (gui != null) { gui.setValueOfZoomDisplay(factor); } float zoomFactor = CurrentDiagram.getInstance().getDiagramHandler().getZoomFactor() * 100; String zoomtext; if (CurrentDiagram.getInstance().getDiagramHandler() instanceof PaletteHandler) { zoomtext = "Palette zoomed to " + Integer.toString((int) zoomFactor) + "%"; } else { zoomtext = "Diagram zoomed to " + Integer.toString((int) zoomFactor) + "%"; } Notifier.getInstance().showInfo(zoomtext); } } private void displayError(String error) { JOptionPane.showMessageDialog(CurrentGui.getInstance().getGui().getMainFrame(), error, "ERROR", JOptionPane.ERROR_MESSAGE); } public void setHandlerAndInitListeners(GridElement element) { if (HandlerElementMap.getHandlerForElement(element) != null) { ((Component) element.getComponent()).removeMouseListener(HandlerElementMap.getHandlerForElement(element).getEntityListener(element)); ((Component) element.getComponent()).removeMouseMotionListener(HandlerElementMap.getHandlerForElement(element).getEntityListener(element)); } HandlerElementMap.setHandlerForElement(element, this); ((Component) element.getComponent()).addMouseListener(HandlerElementMap.getHandlerForElement(element).getEntityListener(element)); ((Component) element.getComponent()).addMouseMotionListener(HandlerElementMap.getHandlerForElement(element).getEntityListener(element)); if (element instanceof NewGridElement) { ((ComponentSwing) element.getComponent()).setHandler(this); } element.updateModelFromText(); // must be updated here because the new handler could have a different zoom level } }