/* * This file is part of the OSMembrane project. * More informations under www.osmembrane.de * * The project is licensed under the GNU GENERAL PUBLIC LICENSE 3.0. * for more details about the license see http://www.osmembrane.de/license/ * * Source: $HeadURL$ ($Revision$) * Last changed: $Date$ */ package de.osmembrane.view.panels; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Image; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.List; import javax.swing.ImageIcon; import javax.swing.SwingUtilities; import de.osmembrane.Application; import de.osmembrane.exceptions.ControlledException; import de.osmembrane.exceptions.ExceptionSeverity; import de.osmembrane.model.pipeline.AbstractFunction; import de.osmembrane.model.pipeline.Function; import de.osmembrane.resources.Constants; import de.osmembrane.tools.I18N; import de.osmembrane.view.ViewRegistry; import de.osmembrane.view.frames.MainFrame; import de.osmembrane.view.frames.MainFrameGlassPane; import de.osmembrane.view.interfaces.IMainFrame; /** * The view function, i.e. the visual representation of a model function on the * View, in the {@link LibraryPanel} and the one being dragged on the * {@link PipelinePanel}. Note, the actually drawn *in* the pipeline, are * {@link PipelineFunction}. * * Unfortunately, this class has a strong dependency with {@link MainFrame} and * {@link MainFrameGlassPane} as well. * * @author tobias_kuhn * */ public class LibraryFunction extends DisplayTemplatePanel { private static final long serialVersionUID = 1663933392202927614L; /** * The minimum amount a {@link LibraryFunction} must be dragged to activate * the drop event. */ protected static final double LIBRARY_FUNCTION_MIN_DRAG_DISTANCE = 32.0; /** * The {@link ImageIcon} resource that keeps an image template used to * prerender the actual image that will be drawn on this function */ protected static ImageIcon displayTemplate = new ImageIcon( LibraryFunction.class .getResource("/de/osmembrane/resources/images/function.png")); /** * The referenced model {@link Function} */ protected AbstractFunction modelFunctionPrototype; /** * The image that will be displayed normally */ protected Image display; /** * The image that will be displayed, if highlighted */ protected Image displayHighlight; /** * Whether this function is currently highlighted */ protected boolean highlighted; /** * Whether this function is currently dragged */ protected boolean dragging; /** * Where the starting click of the drag happened, if canDragAndDrop */ protected Point dragOffset; /** * Initializes a new {@link LibraryFunction} for the given model prototype * function * * @param pipeline * the {@link PipelinePanel} that shall later be able to identify * drags from this class. * @param modelFunctionPrototype * the model's prototype {@link Function} this * {@link LibraryFunction} should represent * @param canDragAndDrop * whether this {@link LibraryFunction} is in the * {@link LibraryPanel} and can be dragged onto the pipeline * panel to create a new {@link PipelineFunction}. Additionally, * it gets highlighted when the mouse cursor moves over it. All * non-library descendants are recommended to set this to false. */ public LibraryFunction(final PipelinePanel pipeline, final AbstractFunction modelFunctionPrototype, final boolean canDragAndDrop) { this.modelFunctionPrototype = modelFunctionPrototype; /* * use DisplayTemplatePanel prerender capabilites in this case we store * it for *FUNCTION GROUPS ONLY* if you ever want images/function, * change this */ setPreferredSize(new Dimension( (int) (displayTemplate.getIconWidth() * Constants.DEFAULT_SIZE_FACTOR), (int) (displayTemplate.getIconHeight() * Constants.DEFAULT_SIZE_FACTOR))); List<Image> prerender = DisplayTemplatePanel .givePrerender(modelFunctionPrototype.getParent()); if (prerender != null) { // images are prerendered, use them display = prerender.get(0); displayHighlight = prerender.get(1); } else { // images are not prerendered, create them Color color = modelFunctionPrototype.getParent().getColor(); float[] colorRGB = color.getComponents(null); Color highlightColor = new Color( Math.min(1.0f, colorRGB[0] + 0.25f), Math.min(1.0f, colorRGB[1] + 0.25f), Math.min(1.0f, colorRGB[2] + 0.25f)); display = DisplayTemplatePanel.prerenderDisplay( modelFunctionPrototype.getParent(), displayTemplate, color, modelFunctionPrototype.getIcon()); displayHighlight = DisplayTemplatePanel.prerenderDisplay( modelFunctionPrototype.getParent(), displayTemplate, highlightColor, modelFunctionPrototype.getIcon()); } highlighted = false; dragging = false; this.setOpaque(false); if (pipeline != null) { addMouseListener(new MouseListener() { // drag & drop @Override public void mouseReleased(MouseEvent e) { dragging = false; if (canDragAndDrop) { IMainFrame mainFrame = ViewRegistry.getInstance() .getCasted(MainFrame.class, IMainFrame.class); mainFrame.getMainGlassPane().endDragAndDrop(); // necessary to make the glass pane go away // require movement of at least some pixels if (e.getPoint().distance(dragOffset) < LIBRARY_FUNCTION_MIN_DRAG_DISTANCE) { return; } // subtract the offset where it got clicked e.translatePoint(-dragOffset.x, -dragOffset.y); // convert the mouse locations into the mainFrame and // pipeline panel components Point mainFramePoint = SwingUtilities.convertPoint( LibraryFunction.this, e.getPoint(), mainFrame.getMainGlassPane()); Point pipelinePoint = SwingUtilities.convertPoint( LibraryFunction.this, e.getPoint(), pipeline); if (mainFrame.isDragAndDropTarget(mainFramePoint)) { pipeline.draggedOnto(LibraryFunction.this, pipelinePoint); } else { Application .handleException(new ControlledException( this, ExceptionSeverity.WARNING, I18N.getInstance() .getString( "View.Library.CannotDropFunction"))); } } } @Override public void mousePressed(MouseEvent e) { dragging = true; dragOffset = e.getPoint(); } // mouse move hint @Override public void mouseExited(MouseEvent e) { // show no hint pipeline.setHint(InspectorPanel.VALID_EMPTY_HINT); if (canDragAndDrop) { highlighted = false; repaint(); } } @Override public void mouseEntered(MouseEvent e) { // show hint for this function pipeline.setHint(modelFunctionPrototype.getDescription()); if (canDragAndDrop) { highlighted = true; repaint(); } } @Override public void mouseClicked(MouseEvent e) { // these clicks add the function to the pipeline if (e.getClickCount() >= 2) { pipeline.draggedOnto(LibraryFunction.this, new Point(0, 0)); } } }); } // nice drag & drop animation if (canDragAndDrop) { addMouseMotionListener(new MouseMotionListener() { @Override public void mouseMoved(MouseEvent e) { } @Override public void mouseDragged(MouseEvent e) { IMainFrame mainFrame = ViewRegistry.getInstance() .getCasted(MainFrame.class, IMainFrame.class); // subtract the offset where it got clicked e.translatePoint(-dragOffset.x, -dragOffset.y); // convert the mouse event into the mainFrame and // pipeline panel components MouseEvent mainFrameEvent = SwingUtilities .convertMouseEvent(LibraryFunction.this, e, mainFrame.getMainGlassPane()); mainFrame.getMainGlassPane().dragAndDrop( LibraryFunction.this, mainFrameEvent.getPoint()); } }); } } @Override protected void paintComponent(Graphics g) { paintAt(g, new Point(0, 0)); } /** * Paints the component on g at position at * * @param g * the {@link Graphics} to draw upon * @param at * where to draw */ protected void paintAt(Graphics g, Point at) { if (highlighted) { g.drawImage(displayHighlight, at.x, at.y, getWidth(), getHeight(), this); } else { g.drawImage(display, at.x, at.y, getWidth(), getHeight(), this); } // get applicable font g.setFont(g.getFont().deriveFont(Font.BOLD) .deriveFont(g.getFont().getSize() * getHeight() / 90.0f)); printCenteredString(g, modelFunctionPrototype.getFriendlyName(), at.x, at.y + 0.8 * getHeight()); } /** * Prints a string centered and with line breaks at spaces fitting into * {@link Graphics} g with y coordinate y * * @param g * Graphics to draw upon * @param str * String to display centered with line breaks * @param x * the base x coordinate for the first character of the string * @param y * the base y coordinate for the last line of the string */ protected void printCenteredString(Graphics g, String str, double x, double y) { // find out how large this is gonna be FontMetrics fm = g.getFontMetrics(); int fontWidth = 0; int fontHeight = fm.getHeight(); List<String> lines = new ArrayList<String>(); String line = new String(); // try to separate lines at " " for (String word : str.split(" ")) { int thisWidth = fm.stringWidth(word + " "); // if this line is wider than possible, make a new line if (fontWidth + thisWidth >= getWidth()) { // no word yet = sorry, but it can't fit if (line.isEmpty()) { lines.add(word); } else { lines.add(line); line = word; } fontWidth = 0; } else { // append fontWidth += thisWidth; line = line.concat(word + " "); } } if (!line.isEmpty()) { lines.add(line); } // print the lines for (int i = lines.size() - 1; i >= 0; i--) { line = lines.get((lines.size() - 1) - i); g.drawString(line, (int) x + (getWidth() - fm.stringWidth(line)) / 2, (int) y - i * fontHeight); } } /** * @return the model {@link Function} prototype associated with this * {@link LibraryFunction} */ public AbstractFunction getModelFunctionPrototype() { return this.modelFunctionPrototype; } /** * @return true, if this {@link LibraryFunction} is currently dragged, false * otherwise */ public boolean isDragging() { return this.dragging; } /** * Forces the change of the highlight value. * * @param highlight * true, if highlighted, false otherwise */ public void forceHighlight(boolean highlight) { this.highlighted = highlight; repaint(); } }