/* * Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com) * Licensed under the Apache License, Version 2.0 (the "License") * $Id: StructurePanel.java 3918 2008-04-14 17:35:35Z gbevin $ */ package com.uwyn.rife.gui.old; import java.awt.*; import java.awt.event.*; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; import javax.swing.JLayeredPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import com.uwyn.rife.config.Config; import com.uwyn.rife.swing.Cursors; public class StructurePanel extends JLayeredPane implements ElementListener, MouseListener, MouseMotionListener, KeyListener, PopupMenuListener { private static final float SCALE_FACTOR_UPPER_LIMIT = 4f; private static final float SCALE_FACTOR_LOWER_LIMIT = 0.125f; public static final int SELECTION_TOOL = 0; public static final int ZOOMIN_TOOL = 1; public static final int ZOOMOUT_TOOL = 2; public static final int ELEMENT_TOOL = 3; public static final int CONNECTOR_TOOL = 4; private float mScaleFactor = 1f; private JScrollPane mScrollPane = null; private int mActiveTool = SELECTION_TOOL; private int mWidth = 0; private int mHeight = 0; private ElementStyle mElementStyleOrig = new ElementStyle(1f); private ElementStyle mElementStyleScaled = new ElementStyle(mScaleFactor); private ArrayList<Element> mElements = new ArrayList<Element>(); private ArrayList<Element> mSelectedElements = new ArrayList<Element>(); private ElementPropertyEditor mElementPropertyEditor = null; private ElementProperty mHighlightedProperty = null; private ElementProperty mHighlightedPropertyHidden = null; private Point mDragElementStartPoint = null; private Element mDragElement = null; private Element mDragElementLeftMost = null; private Element mDragElementTopMost = null; private Point mSelectionRectangleStartPoint = null; private GeneralPath mSelectionRectanglePath = null; private Point mSelectionRectangleCurrentPoint = null; private Stroke mSelectionRectangleDashedStroke = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL, 0f, new float[] {3f}, 0f); private boolean mScrollActive = false; private boolean mPopupMenuActive = false; public StructurePanel(JScrollPane scrollPane) { setDoubleBuffered(true); setScrollPane(scrollPane); setOpaque(true); setBackground(Color.white); setLayout(null); addMouseListener(this); addMouseMotionListener(this); calculateDimension(); } public void setScrollPane(JScrollPane scrollPane) { mScrollPane = scrollPane; } public JScrollPane getScrollPane() { return mScrollPane; } public Collection<Element> getElements() { return mElements; } public int getCalculatedWidth() { return mWidth; } public int getCalculatedHeight() { return mHeight; } public Stroke getSelectionRectangleStroke() { return mSelectionRectangleDashedStroke; } public void paintComponent(Graphics g) { super.paintComponent(g); if(Config.getRepInstance().getBool("GRID_SHOW")) { Graphics2D g2d = (Graphics2D)g; g2d.setColor(Color.gray); Rectangle clip_bounds = g2d.getClipBounds(); int grid_size = Config.getRepInstance().getInt("GRID_SIZE"); double grid_size_scaled = grid_size*mScaleFactor; if(grid_size_scaled > 0) { while(grid_size_scaled < 5) { grid_size_scaled = grid_size_scaled*2; } double offset_x = clip_bounds.x-(clip_bounds.x%grid_size_scaled); double offset_y = clip_bounds.y-(clip_bounds.y%grid_size_scaled); double new_clip_width = clip_bounds.width+(clip_bounds.x%grid_size_scaled); double new_clip_height = clip_bounds.height+(clip_bounds.y%grid_size_scaled); int real_x = 0; int real_y = 0; for(double x = 0; x <= new_clip_width; x += grid_size_scaled) { for(double y = 0; y <= new_clip_height; y += grid_size_scaled) { real_x = (int)(offset_x+x); real_y = (int)(offset_y+y); g2d.drawLine(real_x, real_y, real_x, real_y); } } } } } private void addElementMouseListeners() { for (Element element : mElements) { element.addMouseListener(element); element.addMouseMotionListener(element); } } private void removeElementMouseListeners() { for (Element element : mElements) { element.removeMouseListener(element); element.removeMouseMotionListener(element); } } public void setActiveTool(int tool) { mActiveTool = tool; switch(mActiveTool) { case SELECTION_TOOL: setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); addElementMouseListeners(); break; case ZOOMIN_TOOL: setCursor(Cursors.getRepInstance().getCursor("zoomin")); removeElementMouseListeners(); break; case ZOOMOUT_TOOL: setCursor(Cursors.getRepInstance().getCursor("zoomout")); removeElementMouseListeners(); break; case ELEMENT_TOOL: setCursor(Cursors.getRepInstance().getCursor("element")); removeElementMouseListeners(); break; case CONNECTOR_TOOL: setCursor(Cursors.getRepInstance().getCursor("connector")); removeElementMouseListeners(); break; } } public int getActiveTool() { return mActiveTool; } public void setScaleFactor(float scaleFactor) { if(scaleFactor > SCALE_FACTOR_UPPER_LIMIT) { scaleFactor = SCALE_FACTOR_UPPER_LIMIT; } if(scaleFactor < SCALE_FACTOR_LOWER_LIMIT) { scaleFactor = SCALE_FACTOR_LOWER_LIMIT; } mScaleFactor = scaleFactor; } public float getScaleFactor() { return mScaleFactor; } public void addElement(String name) { Element element = new Element(this, name, mElementStyleOrig, mElementStyleScaled); if(mActiveTool == SELECTION_TOOL) { element.addMouseListener(element); element.addMouseMotionListener(element); } int exits = (int)(Math.random()*6); int consumeds = (int)(Math.random()*6); // int useds = (int)(Math.random()*6); int addeds = (int)(Math.random()*6); for(int i = 0; i < exits; i++) { element.addExit("exit"+i); } for(int i = 0; i < consumeds; i++) { element.addConsumedParameter("input"+i); } // for(int i=0; i < useds; i++) // { // element.addUsedParameter("used"+i); // } for(int i = 0; i < addeds; i++) { element.addAddedParameter("output"+i); } Color body_color = new Color(155+(int)(Math.random()*100), 155+(int)(Math.random()*100), 155+(int)(Math.random()*100)); element.setElementColor(body_color); element.addElementListener(this); mElements.add(element); this.add(element); element.setBounds((int)(Math.random()*800), (int)(Math.random()*600), element.getWidth(), element.getHeight()); calculateDimension(); } public void removeElement(Element element) { int index = mElements.indexOf(element); if(index != -1) { mElements.remove(index); } index = mSelectedElements.indexOf(element); if(index != -1) { mSelectedElements.remove(index); } remove(element); calculateDimension(); mScrollPane.revalidate(); repaint(); } void editElementProperty(ElementProperty property, Point location) { mElementPropertyEditor = new ElementPropertyEditor(property, location); elementPropertyHighlighted(property); } void removeElementPropertyEditor() { if(mElementPropertyEditor != null) { mElementPropertyEditor.destroy(); mElementPropertyEditor = null; synchronizeHighlightedProperty(); } } boolean isElementPropertyBeingEdited() { if(mElementPropertyEditor == null) { return false; } else { return true; } } private void calculateDimension() { mWidth = 0; mHeight = 0; int tmp_width = 0; for (Element element : mElements) { tmp_width = element.getWidth()+element.getX(); if(tmp_width > mWidth) { mWidth = tmp_width; } } int tmp_height = 0; for (Element element : mElements) { tmp_height = element.getHeight()+element.getY(); if(tmp_height > mHeight) { mHeight = tmp_height; } } if(mWidth == 0 || mHeight == 0) { mWidth = 800; mHeight = 600; } } public Dimension getMinimumSize() { return new Dimension(mWidth, mHeight); } public Dimension getPreferredSize() { return new Dimension(mWidth, mHeight); } private void deselectOtherElements(Element selected_element) { for (Element element : mSelectedElements) { if (element != selected_element) { element.deselectElement(); } } mSelectedElements = new ArrayList<Element>(); } public void elementRepositioned(Element element) { calculateDimension(); mScrollPane.revalidate(); } public void elementRaised(Element element) { if(mActiveTool == SELECTION_TOOL) { this.moveToFront(element); } } public void elementSelected(Element selectedElement, int modifiers) { removeElementPropertyEditor(); if(mActiveTool == SELECTION_TOOL) { if((modifiers & MouseEvent.SHIFT_MASK) == 0) { deselectOtherElements(selectedElement); selectedElement.fireElementRaised(); } selectedElement.selectElement(); mSelectedElements.add(selectedElement); this.repaint(); } } public void elementDeselected(Element deselectedElement, int modifiers) { removeElementPropertyEditor(); if((modifiers & MouseEvent.SHIFT_MASK) != 0 && mSelectedElements.size() > 1) { mSelectedElements.remove(deselectedElement); deselectedElement.deselectElement(); deselectedElement.repaint(); } } private void findLeftTopMostDragElements(int startX, int startY) { int left_most_x = startX; int top_most_y = startY; for (Element element : mSelectedElements) { element.startDrag(); if(element.getX() <= left_most_x) { left_most_x = element.getX(); mDragElementLeftMost = element; } if(element.getY() <= top_most_y) { top_most_y = element.getY(); mDragElementTopMost = element; } } } public void elementDragStart(Element initiatingElement, Point dragStartPoint) { this.setCursor(new Cursor(Cursor.MOVE_CURSOR)); mDragElement = initiatingElement; mDragElementStartPoint = dragStartPoint; findLeftTopMostDragElements(initiatingElement.getX(), initiatingElement.getY()); } public void repositionElementsDuringDrag(Element initiatingElement, int offsetX, int offsetY) { if(mDragElementLeftMost.getX()+offsetX < 0) { offsetX = -1*mDragElementLeftMost.getX(); } if(mDragElementTopMost.getY()+offsetY < 0) { offsetY = -1*mDragElementTopMost.getY(); } initiatingElement.repositionElementDuringDrag(offsetX, offsetY); if(mSelectedElements.size() > 0) { for (Element element : mSelectedElements) { if (initiatingElement != element) { element.repositionElementDuringDrag(offsetX, offsetY); } } } } public void elementDragged(Element initiatingElement, int dragX, int dragY) { if(mDragElement == initiatingElement) { Point element_location = initiatingElement.getLocation(); Point offset = new Point(dragX-mDragElementStartPoint.x, dragY-mDragElementStartPoint.y); repositionElementsDuringDrag(initiatingElement, offset.x, offset.y); if(isLocationOutsideStructurepanelView(element_location.x+dragX, element_location.y+dragY)) { new AutoScrollElementDrag(this, mDragElementStartPoint, new Point(dragX, dragY), initiatingElement); } } } public boolean isLocationOutsideStructurepanelView(int locationOnStructurePanelX, int locationOnStructurePanelY) { Rectangle visible_rect = mScrollPane.getViewport().getViewRect(); if(locationOnStructurePanelX < visible_rect.x || locationOnStructurePanelX > visible_rect.x+visible_rect.width || locationOnStructurePanelY < visible_rect.y || locationOnStructurePanelY > visible_rect.y+visible_rect.height) { return true; } else { return false; } } public void elementDragEnd() { this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); mDragElement = null; mDragElementStartPoint = null; for (Element element : mSelectedElements) { element.endDrag(); } this.repaint(); } public void elementPropertyHighlighted(ElementProperty property) { if(!isDragActive() && !isElementPropertyBeingEdited() && !isPopupMenuActive()) { ElementProperty previous_highlight = mHighlightedProperty; mHighlightedProperty = property; if(previous_highlight != null) { previous_highlight.getElement().repaint(previous_highlight.getHotSpot().getBounds()); } if(mHighlightedProperty != null) { mHighlightedProperty.getElement().repaint(mHighlightedProperty.getHotSpot().getBounds()); } } if(isElementPropertyBeingEdited() || isPopupMenuActive()) { mHighlightedPropertyHidden = property; } } void drawHighlightedProperty(Element element, Graphics2D g2d) { if(mHighlightedProperty != null && mHighlightedProperty.getElement() == element && !isDragActive() && !isScrollActive()) { mHighlightedProperty.drawActive(g2d); } } private void synchronizeHighlightedProperty() { elementPropertyHighlighted(mHighlightedPropertyHidden); mHighlightedPropertyHidden = null; } public void changeZoom(float multiplier) { changeZoom(multiplier, null); } public void changeZoom(float multiplier, Point centerPoint) { Cursor previous_cursor = this.getCursor(); if(mScaleFactor*multiplier > SCALE_FACTOR_UPPER_LIMIT) { multiplier = SCALE_FACTOR_UPPER_LIMIT/mScaleFactor; } if(mScaleFactor*multiplier < SCALE_FACTOR_LOWER_LIMIT) { multiplier = SCALE_FACTOR_LOWER_LIMIT/mScaleFactor; } if(multiplier != 1) { this.setCursor(new Cursor(Cursor.WAIT_CURSOR)); JPanel panel = new JPanel(); panel.setBackground(getBackground()); mScrollPane.getViewport().setView(panel); setScaleFactor(mScaleFactor*multiplier); mElementStyleScaled.calculateStyle(mScaleFactor); for (Element element : mElements) { element.scalePrecalculatedAreas(multiplier); } mWidth *= multiplier; mHeight *= multiplier; revalidate(); if(centerPoint != null) { centerPoint.x *= multiplier; centerPoint.y *= multiplier; } SwingUtilities.invokeLater(new StructurePanelRepositioner(this, centerPoint, previous_cursor)); } } public boolean isDragActive() { if(mDragElement != null) { return true; } else { return false; } } public boolean isSelectionRectangleActive() { if(mSelectionRectangleStartPoint != null) { return true; } else { return false; } } public boolean isPopupMenuActive() { return mPopupMenuActive; } void resetSelectionRectangle() { mSelectionRectanglePath = null; } private void initializeSelectionRectangle() { mSelectionRectanglePath = new GeneralPath(); } void eraseSelectionRectangle() { Graphics2D g2d = (Graphics2D)getGraphics(); if(g2d != null && mSelectionRectanglePath != null) { g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); g2d.setStroke(mSelectionRectangleDashedStroke); g2d.setXORMode(Color.white); g2d.draw(mSelectionRectanglePath); } } private void drawSelectionRectangle(Point currentPoint) { Graphics2D g2d = (Graphics2D)getGraphics(); g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); g2d.setStroke(mSelectionRectangleDashedStroke); g2d.setXORMode(Color.white); createSelectionRectanglePath(currentPoint); g2d.draw(mSelectionRectanglePath); } private void createSelectionRectanglePath(Point currentPoint) { mSelectionRectangleCurrentPoint = currentPoint; mSelectionRectanglePath.reset(); mSelectionRectanglePath.moveTo(mSelectionRectangleStartPoint.x, mSelectionRectangleStartPoint.y); mSelectionRectanglePath.lineTo(mSelectionRectangleCurrentPoint.x, mSelectionRectangleStartPoint.y); mSelectionRectanglePath.lineTo(mSelectionRectangleCurrentPoint.x, mSelectionRectangleCurrentPoint.y); mSelectionRectanglePath.lineTo(mSelectionRectangleStartPoint.x, mSelectionRectangleCurrentPoint.y); mSelectionRectanglePath.lineTo(mSelectionRectangleStartPoint.x, mSelectionRectangleStartPoint.y); } public void setScrollActive(boolean active) { mScrollActive = active; } public boolean isScrollActive() { return mScrollActive; } public void mouseClicked(MouseEvent e) { if((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) { switch(mActiveTool) { case SELECTION_TOOL: deselectOtherElements(null); this.repaint(); break; case ZOOMIN_TOOL: case ZOOMOUT_TOOL: Point center_point = e.getPoint(); if(mActiveTool==ZOOMIN_TOOL) { changeZoom(2f, center_point); } else { changeZoom(0.5f, center_point); } break; } } } public void mousePressed(MouseEvent e) { if((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) { switch(mActiveTool) { case SELECTION_TOOL: removeElementMouseListeners(); case ZOOMIN_TOOL: case ZOOMOUT_TOOL: mSelectionRectangleStartPoint = e.getPoint(); break; } } } public void mouseReleased(MouseEvent e) { removeElementPropertyEditor(); if((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) { switch(mActiveTool) { case SELECTION_TOOL: addElementMouseListeners(); case ZOOMIN_TOOL: case ZOOMOUT_TOOL: if(mSelectionRectangleStartPoint != null && mSelectionRectangleCurrentPoint == null) { mSelectionRectangleStartPoint = null; mSelectionRectanglePath = null; } else if(mSelectionRectangleStartPoint != null && mSelectionRectangleCurrentPoint != null) { float rect_x = 0; float rect_y = 0; float rect_width = 0; float rect_height = 0; if(mSelectionRectangleStartPoint.x <= mSelectionRectangleCurrentPoint.x && mSelectionRectangleStartPoint.y <= mSelectionRectangleCurrentPoint.y) { rect_x = mSelectionRectangleStartPoint.x; rect_y = mSelectionRectangleStartPoint.y; rect_width = mSelectionRectangleCurrentPoint.x-mSelectionRectangleStartPoint.x; rect_height = mSelectionRectangleCurrentPoint.y-mSelectionRectangleStartPoint.y; } else if(mSelectionRectangleStartPoint.x >= mSelectionRectangleCurrentPoint.x && mSelectionRectangleStartPoint.y <= mSelectionRectangleCurrentPoint.y) { rect_x = mSelectionRectangleCurrentPoint.x; rect_y = mSelectionRectangleStartPoint.y; rect_width = mSelectionRectangleStartPoint.x-mSelectionRectangleCurrentPoint.x; rect_height = mSelectionRectangleCurrentPoint.y-mSelectionRectangleStartPoint.y; } else if(mSelectionRectangleStartPoint.x <= mSelectionRectangleCurrentPoint.x && mSelectionRectangleStartPoint.y >= mSelectionRectangleCurrentPoint.y) { rect_x = mSelectionRectangleStartPoint.x; rect_y = mSelectionRectangleCurrentPoint.y; rect_width = mSelectionRectangleCurrentPoint.x-mSelectionRectangleStartPoint.x; rect_height = mSelectionRectangleStartPoint.y-mSelectionRectangleCurrentPoint.y; } else if(mSelectionRectangleStartPoint.x >= mSelectionRectangleCurrentPoint.x && mSelectionRectangleStartPoint.y >= mSelectionRectangleCurrentPoint.y) { rect_x = mSelectionRectangleCurrentPoint.x; rect_y = mSelectionRectangleCurrentPoint.y; rect_width = mSelectionRectangleStartPoint.x-mSelectionRectangleCurrentPoint.x; rect_height = mSelectionRectangleStartPoint.y-mSelectionRectangleCurrentPoint.y; } switch(mActiveTool) { case SELECTION_TOOL: if((e.getModifiers() & MouseEvent.SHIFT_MASK) == 0) { deselectOtherElements(null); } // get elements in the correct Z order Component[] components = getComponents(); Element element = null; for(int i = components.length-1; i >= 0; i--) { if(components[i] instanceof Element) { element = (Element)components[i]; if(element.getBoundingAreaScaled().intersects(new Rectangle2D.Float(rect_x-element.getX(), rect_y-element.getY(), rect_width, rect_height)) && !element.isSelected()) { mSelectedElements.add(element); element.selectElement(); } } } break; case ZOOMIN_TOOL: { float width_factor = this.getParent().getWidth()/rect_width; float height_factor = this.getParent().getHeight()/rect_height; float factor = 0; if(width_factor < height_factor) { factor = width_factor; } else { factor = height_factor; } changeZoom(factor, new Point((int)(rect_x+rect_width/2), (int)(rect_y+rect_height/2))); } break; case ZOOMOUT_TOOL: { float width_factor = rect_width/this.getParent().getWidth(); float height_factor = rect_height/this.getParent().getHeight(); float factor = 0; if(width_factor < height_factor) { factor = width_factor; } else { factor = height_factor; } changeZoom(factor, new Point((int)(rect_x+rect_width/2), (int)(rect_y+rect_height/2))); } break; } eraseSelectionRectangle(); mSelectionRectangleStartPoint = null; mSelectionRectangleCurrentPoint = null; mSelectionRectanglePath = null; this.repaint(); } break; } } } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { if(isSelectionRectangleActive()) { new AutoScrollSelectionRectangle(this, e.getPoint(), mSelectionRectangleStartPoint); } } public void mouseDragged(MouseEvent e) { Point current_point = e.getPoint(); if(isSelectionRectangleActive()) { Rectangle view_rect = mScrollPane.getViewport().getViewRect(); if(current_point.x-view_rect.getX() > view_rect.getWidth() || current_point.y-view_rect.getY() > view_rect.getHeight()) { new AutoScrollSelectionRectangle(this, e.getPoint(), mSelectionRectangleStartPoint); } else { if(mSelectionRectanglePath == null) { initializeSelectionRectangle(); } else { eraseSelectionRectangle(); } drawSelectionRectangle(current_point); } } } public void mouseMoved(MouseEvent e) { elementPropertyHighlighted(null); } public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { if(mActiveTool == ZOOMIN_TOOL && e.getKeyCode() == KeyEvent.VK_SHIFT) { setActiveTool(ZOOMOUT_TOOL); } } public void keyReleased(KeyEvent e) { if(mActiveTool == ZOOMOUT_TOOL && e.getKeyCode() == KeyEvent.VK_SHIFT) { setActiveTool(ZOOMIN_TOOL); } } public void popupMenuWillBecomeVisible(PopupMenuEvent e) { mPopupMenuActive = true; } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { mPopupMenuActive = false; synchronizeHighlightedProperty(); } public void popupMenuCanceled(PopupMenuEvent e) { } }