/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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 Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.designer.core.editor.report; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.designer.core.Messages; import org.pentaho.reporting.designer.core.ReportDesignerBoot; import org.pentaho.reporting.designer.core.ReportDesignerContext; import org.pentaho.reporting.designer.core.editor.ContextMenuUtility; import org.pentaho.reporting.designer.core.editor.ReportDocumentContext; import org.pentaho.reporting.designer.core.editor.ZoomModel; import org.pentaho.reporting.designer.core.editor.ZoomModelListener; import org.pentaho.reporting.designer.core.editor.report.drag.CompoundDragOperation; import org.pentaho.reporting.designer.core.editor.report.drag.MouseDragOperation; import org.pentaho.reporting.designer.core.editor.report.drag.MoveDragOperation; import org.pentaho.reporting.designer.core.editor.report.drag.ResizeBottomDragOperation; import org.pentaho.reporting.designer.core.editor.report.drag.ResizeLeftDragOperation; import org.pentaho.reporting.designer.core.editor.report.drag.ResizeRightDragOperation; import org.pentaho.reporting.designer.core.editor.report.drag.ResizeTopDragOperation; import org.pentaho.reporting.designer.core.editor.report.layouting.AbstractElementRenderer; import org.pentaho.reporting.designer.core.editor.report.layouting.ElementRenderer; import org.pentaho.reporting.designer.core.editor.report.snapping.EmptySnapModel; import org.pentaho.reporting.designer.core.editor.report.snapping.FullSnapModel; import org.pentaho.reporting.designer.core.editor.report.snapping.SnapToPositionModel; import org.pentaho.reporting.designer.core.model.CachedLayoutData; import org.pentaho.reporting.designer.core.model.HorizontalPositionsModel; import org.pentaho.reporting.designer.core.model.ModelUtility; import org.pentaho.reporting.designer.core.model.lineal.GuideLine; import org.pentaho.reporting.designer.core.model.lineal.LinealModel; import org.pentaho.reporting.designer.core.model.lineal.LinealModelEvent; import org.pentaho.reporting.designer.core.model.lineal.LinealModelListener; import org.pentaho.reporting.designer.core.model.selection.DocumentContextSelectionModel; import org.pentaho.reporting.designer.core.model.selection.ReportSelectionEvent; import org.pentaho.reporting.designer.core.model.selection.ReportSelectionListener; import org.pentaho.reporting.designer.core.settings.SettingsListener; import org.pentaho.reporting.designer.core.settings.WorkspaceSettings; import org.pentaho.reporting.designer.core.util.BreakPositionsList; import org.pentaho.reporting.designer.core.util.FpsCalculator; import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel; import org.pentaho.reporting.designer.core.util.undo.AttributeEditUndoEntry; import org.pentaho.reporting.designer.core.util.undo.CompoundUndoEntry; import org.pentaho.reporting.designer.core.util.undo.MassElementStyleUndoEntry; import org.pentaho.reporting.designer.core.util.undo.MassElementStyleUndoEntryBuilder; import org.pentaho.reporting.designer.core.util.undo.UndoEntry; import org.pentaho.reporting.engine.classic.core.AttributeNames; import org.pentaho.reporting.engine.classic.core.Band; import org.pentaho.reporting.engine.classic.core.Element; import org.pentaho.reporting.engine.classic.core.PageDefinition; import org.pentaho.reporting.engine.classic.core.ReportElement; import org.pentaho.reporting.engine.classic.core.RootLevelBand; import org.pentaho.reporting.engine.classic.core.Section; import org.pentaho.reporting.engine.classic.core.designtime.AttributeExpressionChange; import org.pentaho.reporting.engine.classic.core.designtime.DataFactoryChange; import org.pentaho.reporting.engine.classic.core.designtime.StyleExpressionChange; import org.pentaho.reporting.engine.classic.core.designtime.SubReportParameterChange; import org.pentaho.reporting.engine.classic.core.event.ReportModelEvent; import org.pentaho.reporting.engine.classic.core.event.ReportModelListener; import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys; import org.pentaho.reporting.engine.classic.core.style.ResolverStyleSheet; import org.pentaho.reporting.engine.classic.core.style.resolver.SimpleStyleResolver; import org.pentaho.reporting.engine.classic.core.util.PageFormatFactory; import org.pentaho.reporting.engine.classic.core.util.geom.StrictBounds; import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility; import org.pentaho.reporting.libraries.designtime.swing.ColorUtility; import javax.swing.*; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.applet.Applet; import java.awt.*; import java.awt.dnd.DropTarget; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.ImageObserver; import java.awt.print.PageFormat; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; /** * Base class to handle rendering & dnd events of elements rendered inside sub-reports * * @author Thomas Morgner */ public abstract class AbstractRenderComponent extends JComponent implements ReportElementEditorContext, CellEditorListener { protected class AsyncChangeNotifier implements Runnable { public void run() { final AbstractElementRenderer elementRenderer = (AbstractElementRenderer) getElementRenderer(); elementRenderer.fireChangeEvent(); } } protected class RequestFocusHandler extends MouseAdapter implements PropertyChangeListener { /** * Invoked when the mouse has been clicked on a component. */ public void mouseClicked( final MouseEvent e ) { requestFocusInWindow(); setFocused( true ); SwingUtilities.invokeLater( new AsyncChangeNotifier() ); } /** * This method gets called when a bound property is changed. * * @param evt A PropertyChangeEvent object describing the event source and the property that has changed. */ public void propertyChange( final PropertyChangeEvent evt ) { final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); final boolean oldFocused = isFocused(); final boolean focused = ( owner == AbstractRenderComponent.this ); if ( oldFocused != focused ) { setFocused( focused ); repaintConditionally(); } SwingUtilities.invokeLater( new AsyncChangeNotifier() ); } } protected class RootBandChangeHandler implements SettingsListener, ReportModelListener { protected RootBandChangeHandler() { updateGridSettings(); } public void nodeChanged( final ReportModelEvent event ) { final Object element = event.getElement(); if ( element instanceof ReportElement == false ) { return; } final Object parameter = event.getParameter(); if ( parameter instanceof DataFactoryChange || parameter instanceof SubReportParameterChange || parameter instanceof AttributeExpressionChange || parameter instanceof StyleExpressionChange ) { // filter out known change events that cannot alter the layout. // this saves us a few CPU cycles here and there return; } getElementRenderer().invalidateLayout(); revalidate(); repaintConditionally(); } public void settingsChanged() { updateGridSettings(); // this is cheap, just repaint and we will be happy repaintConditionally(); } } protected class MouseSelectionHandler extends MouseAdapter implements MouseMotionListener { private Point2D normalizedSelectionRectangleOrigin; private Point selectionRectangleOrigin; private Point selectionRectangleTarget; private boolean clearSelectionOnDrag; private HashSet<Element> newlySelectedElements; protected MouseSelectionHandler() { newlySelectedElements = new HashSet<Element>(); } /** * Invoked when a mouse button has been released on a component. */ public void mouseReleased( final MouseEvent e ) { getDesignerContext().setSelectionWaiting( false ); normalizedSelectionRectangleOrigin = null; selectionRectangleOrigin = null; selectionRectangleTarget = null; newlySelectedElements.clear(); repaintConditionally(); } /** * Invoked when a mouse button is pressed on a component and then dragged. <code>MOUSE_DRAGGED</code> events will * continue to be delivered to the component where the drag originated until the mouse button is released * (regardless of whether the mouse position is within the bounds of the component). * <p/> * Due to platform-dependent Drag&Drop implementations, <code>MOUSE_DRAGGED</code> events may not be delivered * during a native Drag&Drop operation. */ public void mouseDragged( final MouseEvent e ) { if ( getDesignerContext().isSelectionWaiting() == false ) { return; } // Check to see if this mouse handler should handle this mouse event (Mouse Operation Handler vs the Mouse // Selection Handler) if ( isMouseOperationInProgress() ) { return; } if ( normalizedSelectionRectangleOrigin == null ) { return; } final Point2D normalizedSelectionRectangleTarget = normalize( e.getPoint() ); normalizedSelectionRectangleTarget.setLocation( Math.max( 0, normalizedSelectionRectangleTarget.getX() ), Math.max( 0, normalizedSelectionRectangleTarget .getY() ) ); final ElementRenderer rendererRoot = getElementRenderer(); final ReportDocumentContext renderContext = getRenderContext(); if ( clearSelectionOnDrag ) { final DocumentContextSelectionModel selectionModel = renderContext.getSelectionModel(); selectionModel.clearSelection(); clearSelectionOnDrag = false; } selectionRectangleTarget = e.getPoint(); // Calculate the bounding box for the selection final double y1 = Math.min( normalizedSelectionRectangleOrigin.getY(), normalizedSelectionRectangleTarget.getY() ); final double x1 = Math.min( normalizedSelectionRectangleOrigin.getX(), normalizedSelectionRectangleTarget.getX() ); final double y2 = Math.max( normalizedSelectionRectangleOrigin.getY(), normalizedSelectionRectangleTarget.getY() ); final double x2 = Math.max( normalizedSelectionRectangleOrigin.getX(), normalizedSelectionRectangleTarget.getX() ); final Element[] allNodes = rendererRoot.getElementsAt( x1, y1, x2 - x1, y2 - y1 ); final DocumentContextSelectionModel selectionModel = renderContext.getSelectionModel(); // Convert between points to micro-points (1 point X 100K is a micro-point) final StrictBounds rect1 = StrictGeomUtility.createBounds( x1, y1, x2 - x1, y2 - y1 ); final StrictBounds rect2 = new StrictBounds(); for ( int i = allNodes.length - 1; i >= 0; i -= 1 ) { final Element element = allNodes[ i ]; if ( element instanceof RootLevelBand ) { continue; } final CachedLayoutData data = ModelUtility.getCachedLayoutData( element ); rect2.setRect( data.getX(), data.getY(), data.getWidth(), data.getHeight() ); // Checking if the bounding box intersects an element if ( StrictBounds.intersects( rect1, rect2 ) ) { if ( selectionModel.add( element ) ) { newlySelectedElements.add( element ); } } } // second step, check which previously added elements are no longer selected by the rectangle. for ( Iterator<Element> visualReportElementIterator = newlySelectedElements.iterator(); visualReportElementIterator.hasNext(); ) { final Element element = visualReportElementIterator.next(); final CachedLayoutData data = ModelUtility.getCachedLayoutData( element ); rect2.setRect( data.getX(), data.getY(), data.getWidth(), data.getHeight() ); if ( StrictBounds.intersects( rect1, rect2 ) == false ) { selectionModel.remove( element ); visualReportElementIterator.remove(); } } repaintConditionally(); } public Point getSelectionRectangleOrigin() { return selectionRectangleOrigin; } public Point getSelectionRectangleTarget() { return selectionRectangleTarget; } /** * Invoked when the mouse cursor has been moved onto a component but no buttons have been pushed. */ public void mouseMoved( final MouseEvent e ) { } /** * Invoked when a mouse button has been pressed on a component. */ public void mousePressed( final MouseEvent e ) { if ( isMouseOperationPossible() ) { return; } if ( getDesignerContext().isSelectionWaiting() == false ) { return; } newlySelectedElements.clear(); normalizedSelectionRectangleOrigin = normalize( e.getPoint() ); normalizedSelectionRectangleOrigin.setLocation( Math.max( 0, normalizedSelectionRectangleOrigin.getX() ), Math.max( 0, normalizedSelectionRectangleOrigin.getY() ) ); selectionRectangleOrigin = e.getPoint(); if ( e.isShiftDown() == false ) { clearSelectionOnDrag = true; } final ReportDocumentContext renderContext = getRenderContext(); final ElementRenderer rendererRoot = getElementRenderer(); final DocumentContextSelectionModel selectionModel = renderContext.getSelectionModel(); final Element[] allNodes = rendererRoot.getElementsAt ( normalizedSelectionRectangleOrigin.getX(), normalizedSelectionRectangleOrigin.getY() ); for ( int i = allNodes.length - 1; i >= 0; i -= 1 ) { final Element element = allNodes[ i ]; if ( element instanceof RootLevelBand ) { continue; } if ( !e.isShiftDown() ) { if ( !selectionModel.isSelected( element ) ) { selectionModel.clearSelection(); selectionModel.add( element ); return; } } } } /** * Invoked when the mouse has been clicked on a component. */ public void mouseClicked( final MouseEvent e ) { final Point2D point = normalize( e.getPoint() ); if ( point.getX() < 0 || point.getY() < 0 ) { return; // we do not handle that one .. } final DocumentContextSelectionModel selectionModel = getRenderContext().getSelectionModel(); final ElementRenderer rendererRoot = getElementRenderer(); // Sorted list of all elements that intersect the point we are seeking final Element[] allNodes = rendererRoot.getElementsAt( point.getX(), point.getY() ); for ( int i = allNodes.length - 1; i >= 0; i -= 1 ) { // Check if element is null due to structural helper node like (SectionRenderBox) final Element element = allNodes[ i ]; if ( element instanceof RootLevelBand ) { continue; } if ( e.isShiftDown() ) { toggleSelection( selectionModel, element ); } else { if ( !selectionModel.isSelected( element ) ) { selectionModel.clearSelection(); selectionModel.add( element ); } } return; } if ( e.isShiftDown() == false ) { selectionModel.clearSelection(); } final Element element = rendererRoot.getElement(); if ( element instanceof RootLevelBand ) { if ( e.isShiftDown() ) { toggleSelection( selectionModel, element ); } else { if ( !selectionModel.isSelected( element ) ) { selectionModel.clearSelection(); selectionModel.add( element ); } } } } private void toggleSelection( final DocumentContextSelectionModel selectionModel, final Element element ) { // toggle selection .. if ( selectionModel.isSelected( element ) ) { selectionModel.remove( element ); } else { selectionModel.add( element ); } } } protected class SelectionModelListener implements ReportSelectionListener { protected SelectionModelListener() { } public void selectionAdded( final ReportSelectionEvent event ) { final Object element = event.getElement(); if ( element instanceof Element == false ) { return; } final Element velement = (Element) element; ReportElement parentSearch = velement; final Section rootBand = getElementRenderer().getElement(); final ZoomModel zoomModel = getRenderContext().getZoomModel(); while ( parentSearch != null ) { if ( parentSearch == rootBand ) { final SelectionOverlayInformation renderer = new SelectionOverlayInformation( velement ); renderer.validate( zoomModel.getZoomAsPercentage() ); velement .setAttribute( ReportDesignerBoot.DESIGNER_NAMESPACE, ReportDesignerBoot.SELECTION_OVERLAY_INFORMATION, renderer, false ); repaintConditionally(); return; } parentSearch = parentSearch.getParentSection(); } } public void selectionRemoved( final ReportSelectionEvent event ) { final Object element = event.getElement(); if ( element instanceof Element ) { final Element e = (Element) element; e.setAttribute( ReportDesignerBoot.DESIGNER_NAMESPACE, ReportDesignerBoot.SELECTION_OVERLAY_INFORMATION, null, false ); } repaintConditionally(); } public void leadSelectionChanged( final ReportSelectionEvent event ) { if ( event.getModel().getSelectionCount() != 1 ) { return; } final Object raw = event.getElement(); if ( raw instanceof Element == false ) { return; } Element e = (Element) raw; while ( e != null && e instanceof RootLevelBand == false ) { e = e.getParent(); } if ( e == getRootBand() ) { setFocused( true ); repaintConditionally(); SwingUtilities.invokeLater( new AsyncChangeNotifier() ); } else { setFocused( false ); repaintConditionally(); SwingUtilities.invokeLater( new AsyncChangeNotifier() ); } } } protected static final class RepaintHandler implements LinealModelListener, ZoomModelListener, ChangeListener { private AbstractRenderComponent component; private RepaintHandler( final AbstractRenderComponent component ) { this.component = component; } public void stateChanged( final ChangeEvent e ) { // this is cheap, just repaint and we will be happy component.revalidate(); component.repaintConditionally(); } public void modelChanged( final LinealModelEvent event ) { component.revalidate(); component.repaintConditionally(); } public void zoomFactorChanged() { component.revalidate(); component.repaintConditionally(); component.stopCellEditing(); } } protected class SettingsUpdateHandler implements SettingsListener { protected SettingsUpdateHandler() { } public void settingsChanged() { updateGridSettings(); revalidate(); repaintConditionally(); } } protected class KeyboardElementMoveHandler extends KeyAdapter { public KeyboardElementMoveHandler() { } public void keyReleased( final KeyEvent e ) { if ( e.isShiftDown() == false && getDesignerContext().isSelectionWaiting() ) { if ( currentIndicator == SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE ) { setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ); } else if ( currentIndicator == SelectionOverlayInformation.InRangeIndicator.MOVE ) { setCursor( Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR ) ); } getDesignerContext().setSelectionWaiting( false ); } } public void keyPressed( final KeyEvent keyEvent ) { // move all selected components 1px List<Element> selectedElements = getRenderContext().getSelectionModel().getSelectedElementsOfType( Element.class ); if ( selectedElements.isEmpty() ) { return; } // if any element's X or Y is == 0, then do not move anything // PRD-1442 final int keyCode = keyEvent.getKeyCode(); if ( keyCode != KeyEvent.VK_UP && keyCode != KeyEvent.VK_DOWN && keyCode != KeyEvent.VK_LEFT && keyCode != KeyEvent.VK_RIGHT ) { return; } if ( keyEvent.isShiftDown() || keyEvent.isAltDown() || keyEvent.isControlDown() ) { return; } keyEvent.consume(); for ( final Element element : selectedElements ) { if ( element instanceof RootLevelBand ) { continue; } final double elementX = element.getStyle().getDoubleStyleProperty( ElementStyleKeys.POS_X, 0 ); final double elementY = element.getStyle().getDoubleStyleProperty( ElementStyleKeys.POS_Y, 0 ); // check if we can't move, one of the elements in the group is already at the minimum position if ( keyCode == KeyEvent.VK_UP && elementY == 0 ) { return; } else if ( keyCode == KeyEvent.VK_LEFT && elementX == 0 ) { return; } } final MassElementStyleUndoEntryBuilder builder = new MassElementStyleUndoEntryBuilder( selectedElements ); final MoveDragOperation mop = new MoveDragOperation( selectedElements, new Point(), EmptySnapModel.INSTANCE, EmptySnapModel.INSTANCE ); if ( keyCode == KeyEvent.VK_UP ) { mop.update( new Point( 0, -1 ), 1 ); } else if ( keyCode == KeyEvent.VK_DOWN ) { mop.update( new Point( 0, 1 ), 1 ); } else if ( keyCode == KeyEvent.VK_LEFT ) { mop.update( new Point( -1, 0 ), 1 ); } else { mop.update( new Point( 1, 0 ), 1 ); } final MassElementStyleUndoEntry massElementStyleUndoEntry = builder.finish(); getRenderContext().getUndo() .addChange( Messages.getString( "AbstractRenderComponent.MoveUndoName" ), massElementStyleUndoEntry ); mop.finish(); } } /** * When you double-click on an element, you can edit it inside the canvas editor area. */ protected class MouseEditorActionHandler extends MouseAdapter { private MouseEditorActionHandler() { } /** * Invoked when the mouse has been clicked on a component. */ public void mouseClicked( final MouseEvent e ) { if ( stopCellEditing() == false ) { return; } if ( e.isPopupTrigger() ) { final Point2D point = normalize( e.getPoint() ); if ( point.getX() < 0 || point.getY() < 0 ) { return; // we do not handle that one .. } showElementPopup( e, point ); return; } // ReportElementInlineEditor ... if ( e.getClickCount() >= 2 && ( e.getButton() == MouseEvent.BUTTON1 ) ) { final Point2D point = normalize( e.getPoint() ); if ( point.getX() < 0 || point.getY() < 0 ) { return; // we do not handle that one .. } final Element element = getElementForLocation( point, true ); if ( element == null ) { return; } final String typeName = element.getElementTypeName(); ReportElementEditor elementEditor = ReportElementEditorRegistry.getInstance().getPlugin( typeName ); if ( elementEditor == null ) { elementEditor = ReportElementEditorRegistry.getInstance().getPlugin( null ); if ( elementEditor == null ) { return; } } final ReportElementInlineEditor inlineEditor = elementEditor.createInlineEditor(); if ( inlineEditor == null ) { return; } installEditor( inlineEditor, element ); } } /** * Invoked when a mouse button has been released on a component. */ public void mouseReleased( final MouseEvent e ) { if ( stopCellEditing() == false ) { return; } if ( e.isPopupTrigger() ) { final Point2D point = normalize( e.getPoint() ); if ( point.getX() < 0 || point.getY() < 0 ) { return; // we do not handle that one .. } showElementPopup( e, point ); } } /** * Invoked when a mouse button has been pressed on a component. */ public void mousePressed( final MouseEvent e ) { if ( stopCellEditing() == false ) { return; } if ( e.isPopupTrigger() ) { final Point2D point = normalize( e.getPoint() ); if ( point.getX() < 0 || point.getY() < 0 ) { return; // we do not handle that one .. } showElementPopup( e, point ); } } protected void showElementPopup( final MouseEvent e, final Point2D normalizedPoint ) { Element element = getElementForLocation( normalizedPoint, true ); if ( element == null ) { element = (Element) findRootBandForPosition( normalizedPoint ); } if ( element == null ) { return; } final JPopupMenu pop = ContextMenuUtility.getMenu( getDesignerContext(), element ); if ( pop == null ) { return; } pop.show( AbstractRenderComponent.this, e.getX(), e.getY() ); } } private class MouseOperationHandler extends MouseAdapter implements MouseMotionListener { private SelectionOverlayInformation currentRenderer; private Point2D lastPoint; private MouseOperationHandler() { } /** * Invoked when a mouse button is pressed on a component and then dragged. <code>MOUSE_DRAGGED</code> events will * continue to be delivered to the component where the drag originated until the mouse button is released * (regardless of whether the mouse position is within the bounds of the component). * <p/> * Due to platform-dependent Drag&Drop implementations, <code>MOUSE_DRAGGED</code> events may not be delivered * during a native Drag&Drop operation. */ public void mouseDragged( final MouseEvent e ) { if ( lastPoint == null ) { return; } final Point2D normalizedPoint = normalize( e.getPoint() ); updateElements( normalizedPoint, e.isAltDown(), e.isControlDown() ); } /** * Invoked when the mouse cursor has been moved onto a component but no buttons have been pushed. */ public void mouseMoved( final MouseEvent e ) { final Point point1 = e.getPoint(); updateCursor( point1 ); } private void updateCursor( final Point rawPoint ) { final boolean selectionMode = getDesignerContext().isSelectionWaiting(); if ( selectionMode ) { setCursor( Cursor.getPredefinedCursor( Cursor.CROSSHAIR_CURSOR ) ); currentIndicator = SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE; return; } currentIndicator = SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE; final Point2D normalizedPoint = normalize( rawPoint ); if ( currentRenderer != null ) { currentIndicator = currentRenderer.getMouseInRangeIndicator( normalizedPoint ); if ( currentIndicator == SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE ) { currentRenderer = null; currentIndicator = SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE; } if ( currentIndicator == SelectionOverlayInformation.InRangeIndicator.MOVE ) { currentRenderer = null; currentIndicator = SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE; } } for ( final Element e : getRenderContext().getSelectionModel().getSelectedElementsOfType( Element.class ) ) { final Object o = e.getAttribute( ReportDesignerBoot.DESIGNER_NAMESPACE, "selection-overlay-information" ); // NON-NLS if ( o instanceof SelectionOverlayInformation == false ) { continue; } if ( isLocalElement( e ) == false ) { continue; } final SelectionOverlayInformation renderer = (SelectionOverlayInformation) o; final SelectionOverlayInformation.InRangeIndicator indicator = renderer.getMouseInRangeIndicator( normalizedPoint ); if ( indicator == SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE ) { continue; } // a resize-handle wins over a ordinary move selection if ( currentIndicator == SelectionOverlayInformation.InRangeIndicator.MOVE || currentIndicator == SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE ) { currentIndicator = indicator; currentRenderer = renderer; } else { break; } } updateCursorForIndicator(); } /** * Invoked when a mouse button has been pressed on a component. */ public void mousePressed( final MouseEvent e ) { lastPoint = normalize( e.getPoint() ); updateCursor( e.getPoint() ); initializeDragOperation( lastPoint, currentIndicator ); } /** * Invoked when a mouse button has been released on a component. */ public void mouseReleased( final MouseEvent e ) { if ( lastPoint == null ) { return; } if ( lastPoint.equals( normalize( e.getPoint() ) ) == false ) { // only fire a drag operation if we moved the mouse finishDragOperation(); } } /** * Invoked when the mouse enters a component. */ public void mouseEntered( final MouseEvent e ) { updateCursor( e.getPoint() ); } /** * Invoked when the mouse has been clicked on a component. */ public void mouseClicked( final MouseEvent e ) { updateCursor( e.getPoint() ); } } protected class CellEditorRemover implements PropertyChangeListener { private KeyboardFocusManager focusManager; public CellEditorRemover( final KeyboardFocusManager fm ) { this.focusManager = fm; } public void propertyChange( final PropertyChangeEvent ev ) { if ( !isEditing() || isTerminateEditOnFocusLost() == false ) { return; } Component c = focusManager.getPermanentFocusOwner(); while ( c != null ) { if ( c == AbstractRenderComponent.this ) { // focus remains inside the table return; } else if ( ( c instanceof Window ) || ( c instanceof Applet && c.getParent() == null ) ) { if ( c == SwingUtilities.getRoot( AbstractRenderComponent.this ) ) { if ( !getCellEditor().stopCellEditing() ) { getCellEditor().cancelCellEditing(); } } break; } c = c.getParent(); } } } private class DragAbortReportModelListener implements ReportModelListener { private DragAbortReportModelListener() { } public void nodeChanged( final ReportModelEvent event ) { if ( event.isNodeAddedEvent() || event.isNodeDeleteEvent() ) { finishDragOperation(); } } } private class SelectionStateHandler implements PropertyChangeListener { /** * This method gets called when a bound property is changed. * * @param evt A PropertyChangeEvent object describing the event source and the property that has changed. */ public void propertyChange( final PropertyChangeEvent evt ) { if ( getDesignerContext().isSelectionWaiting() ) { setCursor( Cursor.getPredefinedCursor( Cursor.CROSSHAIR_CURSOR ) ); } else { updateCursorForIndicator(); } } } protected class SelectionRectangleOverlayRenderer implements OverlayRenderer { public SelectionRectangleOverlayRenderer() { } public void validate( final ReportDocumentContext context, final double zoomFactor, final Point2D sectionOffset ) { } public void draw( final Graphics2D graphics, final Rectangle2D bounds, final ImageObserver obs ) { paintSelectionRectangle( graphics ); } } // 50 fps max private static final long REPAINT_INTERVAL = 1000 / 50; private static final Log logger = LogFactory.getLog( AbstractRenderComponent.class ); private static final BasicStroke SELECTION_STROKE = new BasicStroke( 0.5f ); private CellEditorRemover editorRemover; private RepaintHandler repaintHandler; private SettingsUpdateHandler settingsUpdateHandler; private ReportDesignerContext designerContext; private ReportDocumentContext renderContext; private boolean showLeftBorder; private boolean showTopBorder; private double gridSize; private int gridDivisions; private boolean terminateEditOnFocusLost; private Component editorComponent; private ReportElementInlineEditor inlineEditor; private MouseDragOperation operation; private MassElementStyleUndoEntryBuilder undoEntryBuilder; private FullSnapModel horizontalSnapModel; private FullSnapModel verticalSnapModel; private LinealModel verticalLinealModel; private LinealModel horizontalLinealModel; private HorizontalPositionsModel horizontalPositionsModel; private boolean focused; private SelectionOverlayInformation.InRangeIndicator currentIndicator; private SelectionStateHandler selectionStateHandler; private ArrayList<Object> oldValues = new ArrayList<Object>(); private MouseSelectionHandler selectionHandler; private RequestFocusHandler focusHandler; private SelectionModelListener selectionModelListener; private RootBandChangeHandler changeHandler; private SimpleStyleResolver styleResolver; private ResolverStyleSheet resolvedStyle; private FpsCalculator fpsCalculator; private boolean paintingImmediately = false; protected AbstractRenderComponent( final ReportDesignerContext designerContext, final ReportDocumentContext renderContext ) { if ( renderContext == null ) { throw new NullPointerException(); } if ( designerContext == null ) { throw new NullPointerException(); } setDoubleBuffered( true ); setOpaque( false ); setFocusable( true ); setFocusCycleRoot( true ); setFocusTraversalKeysEnabled( false ); setLayout( null ); this.fpsCalculator = new FpsCalculator(); this.showLeftBorder = true; this.showTopBorder = false; this.repaintHandler = new RepaintHandler( this ); this.designerContext = designerContext; this.renderContext = renderContext; this.settingsUpdateHandler = new SettingsUpdateHandler(); this.horizontalSnapModel = new FullSnapModel(); this.verticalSnapModel = new FullSnapModel(); this.terminateEditOnFocusLost = true; gridSize = WorkspaceSettings.getInstance().getGridSize(); gridDivisions = WorkspaceSettings.getInstance().getGridDivisions(); WorkspaceSettings.getInstance().addSettingsListener( settingsUpdateHandler ); new DropTarget( this, new BandDndHandler( this ) ); renderContext.getZoomModel().addZoomModelListener( repaintHandler ); renderContext.getReportDefinition().addReportModelListener( new DragAbortReportModelListener() ); addMouseListener( new MouseEditorActionHandler() ); addKeyListener( new KeyboardElementMoveHandler() ); selectionStateHandler = new SelectionStateHandler(); designerContext .addPropertyChangeListener( ReportDesignerContext.SELECTION_WAITING_PROPERTY, selectionStateHandler ); focusHandler = new RequestFocusHandler(); addMouseListener( focusHandler ); KeyboardFocusManager.getCurrentKeyboardFocusManager() .addPropertyChangeListener( "permanentFocusOwner", focusHandler ); // NON-NLS this.selectionHandler = new MouseSelectionHandler(); addMouseListener( selectionHandler ); addMouseMotionListener( selectionHandler ); this.changeHandler = new RootBandChangeHandler(); this.selectionModelListener = new SelectionModelListener(); renderContext.getSelectionModel().addReportSelectionListener( selectionModelListener ); new DropTarget( this, new BandDndHandler( this ) ); installMouseOperationHandler(); styleResolver = new SimpleStyleResolver( true ); resolvedStyle = new ResolverStyleSheet(); renderContext.getReportDefinition().addReportModelListener( changeHandler ); } /** * Abstract method to retrieve the element renderer * * @return ElementRenderer */ protected abstract ElementRenderer getElementRenderer(); /** * Abstract method to return the default element * * @return Element */ public abstract Element getDefaultElement(); public Band getRootBand() { return (Band) getElementRenderer().getElement(); } public boolean isTerminateEditOnFocusLost() { return terminateEditOnFocusLost; } public void setTerminateEditOnFocusLost( final boolean terminateEditOnFocusLost ) { this.terminateEditOnFocusLost = terminateEditOnFocusLost; } protected abstract boolean isLocalElement( ReportElement e ); protected void installMouseOperationHandler() { // must be added *after* the selection handler final MouseOperationHandler operationHandler = new MouseOperationHandler(); addMouseListener( operationHandler ); addMouseMotionListener( operationHandler ); } protected boolean isFocused() { return focused; } protected void setFocused( final boolean focused ) { this.focused = focused; } public boolean isShowLeftBorder() { return showLeftBorder; } public void setShowLeftBorder( final boolean showLeftBorder ) { this.showLeftBorder = showLeftBorder; } public boolean isShowTopBorder() { return showTopBorder; } public void setShowTopBorder( final boolean showTopBorder ) { this.showTopBorder = showTopBorder; } protected double getLeftBorder() { if ( renderContext == null ) { return 0; } if ( showLeftBorder == false ) { return 0; } final PageDefinition pageDefinition = renderContext.getContextRoot().getPageDefinition(); final PageFormat pageFormat = pageDefinition.getPageFormat( 0 ); final PageFormatFactory pageFormatFactory = PageFormatFactory.getInstance(); return pageFormatFactory.getLeftBorder( pageFormat.getPaper() ); } protected double getTopBorder() { if ( renderContext == null ) { return 0; } if ( showTopBorder == false ) { return 0; } final PageDefinition pageDefinition = renderContext.getContextRoot().getPageDefinition(); final PageFormat pageFormat = pageDefinition.getPageFormat( 0 ); final PageFormatFactory pageFormatFactory = PageFormatFactory.getInstance(); return pageFormatFactory.getTopBorder( pageFormat.getPaper() ); } public Point2D normalize( final Point2D e ) { final double topBorder = getTopBorder(); final double leftBorder = getLeftBorder(); final float scaleFactor = getRenderContext().getZoomModel().getZoomAsPercentage(); final double x = ( e.getX() / scaleFactor ) - leftBorder; final double y = ( e.getY() / scaleFactor ) - topBorder; final Point2D o = getOffset(); o.setLocation( x, y + o.getY() ); return o; } protected Point2D getOffset() { final StrictBounds bounds = getElementRenderer().getRootElementBounds(); return new Point2D.Double( StrictGeomUtility.toExternalValue( bounds.getX() ), StrictGeomUtility.toExternalValue( bounds.getY() ) ); } public ReportDocumentContext getRenderContext() { return renderContext; } public ReportDesignerContext getDesignerContext() { return designerContext; } protected void paintComponent( final Graphics g ) { if ( fpsCalculator.isActive() ) { fpsCalculator.tick(); } final Graphics2D g2 = (Graphics2D) g.create(); g2.setColor( new Color( 224, 224, 224 ) ); g2.fillRect( 0, 0, getWidth(), getHeight() ); final int leftBorder = (int) getLeftBorder(); final int topBorder = (int) getTopBorder(); final float scaleFactor = getRenderContext().getZoomModel().getZoomAsPercentage(); // draw the page area .. final PageDefinition pageDefinition = getRenderContext().getContextRoot().getPageDefinition(); final Rectangle2D.Double area = new Rectangle2D.Double( 0, 0, pageDefinition.getWidth() * scaleFactor, getHeight() ); g2.translate( leftBorder * scaleFactor, topBorder * scaleFactor ); g2.clip( area ); g2.setColor( Color.WHITE ); g2.fill( area ); // draw the grid (unscaled, but translated) final Point2D offset = getOffset(); if ( offset.getX() != 0 ) { // The blackout area is for inline sub-reports and is the area where the subreport is not interested in // (so we can clip out). The blackout area is only visible in the sub-report. final Rectangle2D.Double blackoutArea = new Rectangle2D.Double( 0, 0, offset.getX() * scaleFactor, getHeight() ); g2.setColor( Color.LIGHT_GRAY ); g2.fill( blackoutArea ); } paintGrid( g2 ); paintElementAlignment( g2 ); g2.dispose(); final Graphics2D logicalPageAreaG2 = (Graphics2D) g.create(); // draw the renderable content ... logicalPageAreaG2.translate( leftBorder * scaleFactor, topBorder * scaleFactor ); logicalPageAreaG2.clip( area ); logicalPageAreaG2.scale( scaleFactor, scaleFactor ); try { final ElementRenderer rendererRoot = getElementRenderer(); if ( rendererRoot != null ) { if ( rendererRoot.draw( logicalPageAreaG2 ) == false ) { rendererRoot.handleError( designerContext, renderContext ); logicalPageAreaG2.scale( 1f / scaleFactor, 1f / scaleFactor ); logicalPageAreaG2.setPaint( Color.WHITE ); logicalPageAreaG2.fill( area ); } } } catch ( Exception e ) { // ignore for now.. UncaughtExceptionsModel.getInstance().addException( e ); } logicalPageAreaG2.dispose(); final OverlayRenderer[] renderers = new OverlayRenderer[ 4 ]; renderers[ 0 ] = new OverlappingElementOverlayRenderer( getDefaultElement() ); // displays the red border for warning renderers[ 1 ] = new SelectionOverlayRenderer( getDefaultElement() ); renderers[ 2 ] = new GuidelineOverlayRenderer( horizontalLinealModel, verticalLinealModel ); renderers[ 3 ] = new SelectionRectangleOverlayRenderer(); // blue box when you shift and drag the region to select multiple // elements for ( int i = 0; i < renderers.length; i++ ) { final OverlayRenderer renderer = renderers[ i ]; final Graphics2D selectionG2 = (Graphics2D) g.create(); renderer.validate( getRenderContext(), scaleFactor, offset ); renderer .draw( selectionG2, new Rectangle2D.Double( getLeftBorder(), getTopBorder(), getWidth(), getHeight() ), this ); selectionG2.dispose(); } } protected void paintSelectionRectangle( final Graphics2D g2 ) { final Point origin = selectionHandler.getSelectionRectangleOrigin(); final Point target = selectionHandler.getSelectionRectangleTarget(); if ( origin == null || target == null ) { return; } g2.setColor( Color.BLUE ); g2.setStroke( SELECTION_STROKE ); final double y1 = Math.min( origin.getY(), target.getY() ); final double x1 = Math.min( origin.getX(), target.getX() ); final double y2 = Math.max( origin.getY(), target.getY() ); final double x2 = Math.max( origin.getX(), target.getX() ); g2.draw( new Rectangle2D.Double( x1, y1, x2 - x1, y2 - y1 ) ); } protected void paintGrid( final Graphics2D g2d ) { if ( WorkspaceSettings.getInstance().isShowGrid() ) { final float scaleFactor = getRenderContext().getZoomModel().getZoomAsPercentage(); final double gridSize = getGridSize() * scaleFactor; if ( gridSize < 1 ) { return; } final int gridDivisions = Math.max( 1, getGridDivisions() ); final Color primaryColor = WorkspaceSettings.getInstance().getGridColor(); final Color secondaryColor = ColorUtility.convertToBrighter( primaryColor ); // draw vertical lines g2d.setStroke( new BasicStroke( .1f ) ); int horizontalLineCount = 0; final Line2D.Double line = new Line2D.Double(); final double gridHeight = getHeight(); final double gridWidth = getWidth(); for ( double w = gridSize; w < gridWidth; w += gridSize ) { if ( horizontalLineCount % gridDivisions == gridDivisions - 1 ) { g2d.setColor( primaryColor ); } else { g2d.setColor( secondaryColor ); } horizontalLineCount++; line.setLine( w, 0, w, gridHeight ); g2d.draw( line ); } // draw horizontal lines int verticalLineCount = 0; for ( double h = gridSize; h < gridHeight; h += gridSize ) { if ( verticalLineCount % gridDivisions == gridDivisions - 1 ) { g2d.setColor( primaryColor ); } else { g2d.setColor( secondaryColor ); } verticalLineCount++; line.setLine( 0, h, gridWidth, h ); g2d.draw( line ); } } } protected void paintElementAlignment( final Graphics2D g2d ) { if ( WorkspaceSettings.getInstance().isShowElementAlignmentHints() ) { final float scaleFactor = getRenderContext().getZoomModel().getZoomAsPercentage(); g2d.setColor( WorkspaceSettings.getInstance().getAlignmentHintColor() ); g2d.setStroke( new BasicStroke( .2f ) ); final double gridHeight = getHeight(); final double gridWidth = getWidth(); final long[] hPositions; if ( getHorizontalPositionsModel() == null ) { final BreakPositionsList horizontalPositions = getHorizontalEdgePositions(); hPositions = horizontalPositions.getKeys(); } else { hPositions = getHorizontalPositionsModel().getBreaks(); } final Line2D.Double line = new Line2D.Double(); for ( int i = 0; i < hPositions.length; i++ ) { final double position = StrictGeomUtility.toExternalValue( hPositions[ i ] ); final double x = position * scaleFactor; line.setLine( x, 0, x, gridHeight ); g2d.draw( line ); } final Point2D offset = getOffset(); final BreakPositionsList verticalPositions = getVerticalEdgePositions(); final long[] vPositions = verticalPositions.getKeys(); for ( int i = 0; i < vPositions.length; i++ ) { final double position = StrictGeomUtility.toExternalValue( vPositions[ i ] ) - offset.getY(); final double y2 = position * scaleFactor; line.setLine( 0, y2, gridWidth, y2 ); g2d.draw( line ); } } } protected void updateGridSettings() { gridSize = WorkspaceSettings.getInstance().getGridSize(); gridDivisions = WorkspaceSettings.getInstance().getGridDivisions(); } public double getGridSize() { return gridSize; } public int getGridDivisions() { return gridDivisions; } public Element getElementForLocation( final Point2D point, final boolean onlySelected ) { final ElementRenderer rendererRoot = getElementRenderer(); final Element[] allNodes = rendererRoot.getElementsAt( point.getX(), point.getY() ); for ( int i = allNodes.length - 1; i >= 0; i -= 1 ) { final Element element = allNodes[ i ]; if ( ModelUtility.isHideInLayoutGui( element ) == true ) { continue; } styleResolver.resolve( element, resolvedStyle ); if ( resolvedStyle.getBooleanStyleProperty( ElementStyleKeys.VISIBLE ) == false ) { continue; } if ( onlySelected == false || getRenderContext().getSelectionModel().isSelected( element ) ) { return element; } } return null; } protected RootLevelBand findRootBandForPosition( final Point2D point ) { if ( getElementRenderer() == null ) { return null; } final Element[] elementsAt = getElementRenderer().getElementsAt( point.getX(), point.getY() ); for ( int i = elementsAt.length - 1; i >= 0; i -= 1 ) { final Element element = elementsAt[ i ]; if ( element instanceof RootLevelBand ) { return (RootLevelBand) element; } } final Section section = getElementRenderer().getElement(); if ( section instanceof RootLevelBand ) { return (RootLevelBand) section; } return null; } public void dispose() { WorkspaceSettings.getInstance().removeSettingsListener( settingsUpdateHandler ); if ( this.verticalLinealModel != null ) { this.verticalLinealModel.removeLinealModelListener( repaintHandler ); } if ( this.horizontalLinealModel != null ) { this.horizontalLinealModel.removeLinealModelListener( repaintHandler ); } if ( getElementRenderer() != null ) { getElementRenderer().removeChangeListener( repaintHandler ); } designerContext .removePropertyChangeListener( ReportDesignerContext.SELECTION_WAITING_PROPERTY, selectionStateHandler ); KeyboardFocusManager.getCurrentKeyboardFocusManager() .removePropertyChangeListener( "permanentFocusOwner", focusHandler ); // NON-NLS final ReportDocumentContext renderContext = getRenderContext(); renderContext.getReportDefinition().removeReportModelListener( changeHandler ); renderContext.getSelectionModel().removeReportSelectionListener( selectionModelListener ); getElementRenderer().dispose(); } protected void removeEditor() { if ( editorRemover != null ) { KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener ( "permanentFocusOwner", editorRemover ); // NON-NLS editorRemover = null; } if ( editorComponent == null ) { inlineEditor = null; return; } remove( editorComponent ); inlineEditor.removeCellEditorListener( this ); editorComponent = null; inlineEditor = null; } protected ReportElementInlineEditor getCellEditor() { return inlineEditor; } protected boolean installEditor( final ReportElementInlineEditor inlineEditor, final Element element ) { if ( inlineEditor == null ) { throw new NullPointerException(); } this.inlineEditor = inlineEditor; final CachedLayoutData data = ModelUtility.getCachedLayoutData( element ); if ( data == null ) { removeEditor(); return false; } final Component editorComponent = inlineEditor.getElementCellEditorComponent( this, element ); if ( editorComponent == null ) { removeEditor(); return false; } if ( editorRemover == null ) { final KeyboardFocusManager fm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); editorRemover = new CellEditorRemover( fm ); fm.addPropertyChangeListener( "permanentFocusOwner", editorRemover ); // NON-NLS } this.editorComponent = editorComponent; final float zoomFactor = getRenderContext().getZoomModel().getZoomAsPercentage(); final Rectangle2D bounds = getElementRenderer().getBounds(); final int x = (int) ( ( getLeftBorder() + StrictGeomUtility.toExternalValue( data.getX() ) ) * zoomFactor ); final int y = (int) ( ( getTopBorder() + ( StrictGeomUtility.toExternalValue( data.getY() ) - bounds.getY() ) * zoomFactor ) ); final int width = (int) ( StrictGeomUtility.toExternalValue( data.getWidth() ) * zoomFactor ); final int height = (int) ( StrictGeomUtility.toExternalValue( data.getHeight() ) * zoomFactor ); editorComponent.setBounds( x, y, width, height ); add( editorComponent ); editorComponent.validate(); inlineEditor.addCellEditorListener( this ); List<Element> selectedElements = getRenderContext().getSelectionModel().getSelectedElementsOfType( Element.class ); final Element[] visualElements = selectedElements.toArray( new Element[ selectedElements.size() ] ); if ( visualElements.length > 0 ) { oldValues = new ArrayList<Object>(); for ( int i = 0; i < visualElements.length; i++ ) { final Object attribute = visualElements[ i ].getAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE ); oldValues.add( attribute ); } } return true; } protected boolean isEditing() { return inlineEditor != null; } public void editingStopped( final ChangeEvent e ) { List<Element> selectedElements = getRenderContext().getSelectionModel().getSelectedElementsOfType( Element.class ); final Element[] visualElements = selectedElements.toArray( new Element[ selectedElements.size() ] ); if ( visualElements.length > 0 ) { final ArrayList<UndoEntry> undos = new ArrayList<UndoEntry>(); for ( int i = 0; i < visualElements.length; i++ ) { final Object attribute = visualElements[ i ].getAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE ); undos.add( new AttributeEditUndoEntry( visualElements[ i ].getObjectID(), AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, oldValues.get( i ), attribute ) ); } getRenderContext().getUndo().addChange( Messages.getString( "AbstractRenderComponent.InlineEditUndoName" ), new CompoundUndoEntry( (UndoEntry[]) undos.toArray( new UndoEntry[ undos.size() ] ) ) ); } removeEditor(); } public void editingCanceled( final ChangeEvent e ) { operation = null; undoEntryBuilder = null; removeEditor(); } public JComponent getRepresentationContainer() { return this; } public LinealModel getVerticalLinealModel() { return verticalLinealModel; } public LinealModel getHorizontalLinealModel() { return horizontalLinealModel; } public HorizontalPositionsModel getHorizontalPositionsModel() { return horizontalPositionsModel; } protected void updateElements( final Point2D normalizedPoint, final boolean snapToGrid, final boolean snapToElements ) { if ( operation != null ) { horizontalSnapModel.setEnableElements( snapToElements || WorkspaceSettings.getInstance().isSnapToElements() ); horizontalSnapModel.setEnableGrid( snapToGrid || WorkspaceSettings.getInstance().isSnapToGrid() ); horizontalSnapModel.setEnableGuides( true ); verticalSnapModel.setEnableElements( snapToElements || WorkspaceSettings.getInstance().isSnapToElements() ); verticalSnapModel.setEnableGrid( snapToGrid || WorkspaceSettings.getInstance().isSnapToGrid() ); verticalSnapModel.setEnableGuides( true ); operation.update( normalizedPoint, getRenderContext().getZoomModel().getZoomAsPercentage() ); } } /** * Returns the break positions for inner-band drag-operations (snap to element). * * @return the edge positions of all elements. */ protected BreakPositionsList getHorizontalEdgePositions() { return getElementRenderer().getHorizontalEdgePositions(); } /** * Returns the break positions for inner-band drag-operations (snap to element). * * @return the edge positions of all elements. */ protected BreakPositionsList getVerticalEdgePositions() { return getElementRenderer().getVerticalEdgePositions(); } protected Element[] filterLocalElements( final Element[] originalElements ) { final ArrayList<Element> result = new ArrayList<Element>( originalElements.length ); for ( int i = 0; i < originalElements.length; i++ ) { final Element element = originalElements[ i ]; if ( isLocalElement( element ) == false ) { continue; } result.add( element ); } return result.toArray( new Element[ result.size() ] ); } protected void initializeDragOperation( final Point2D originPoint, final SelectionOverlayInformation.InRangeIndicator currentIndicator ) { fpsCalculator.reset(); fpsCalculator.setActive( true ); List<Element> visualElements = getRenderContext().getSelectionModel().getSelectedElementsOfType( Element.class ); if ( visualElements.isEmpty() ) { return; } horizontalSnapModel.getGridModel().setGridSize( StrictGeomUtility.toInternalValue( getGridSize() ) ); verticalSnapModel.getGridModel().setGridSize( StrictGeomUtility.toInternalValue( getGridSize() ) ); horizontalSnapModel.setEnableGrid( WorkspaceSettings.getInstance().isSnapToGrid() ); verticalSnapModel.setEnableGrid( WorkspaceSettings.getInstance().isSnapToGrid() ); final SnapToPositionModel horizontalGuildesPositions = horizontalSnapModel.getGuidesModel(); horizontalGuildesPositions.clear(); final GuideLine[] hlines = horizontalLinealModel.getGuideLines(); for ( int i = 0; i < hlines.length; i++ ) { final GuideLine guideLine = hlines[ i ]; if ( guideLine.isActive() ) { horizontalGuildesPositions.add( StrictGeomUtility.toInternalValue( guideLine.getPosition() ), null ); } } final SnapToPositionModel verticalGuidesPositions = verticalSnapModel.getGuidesModel(); verticalGuidesPositions.clear(); final GuideLine[] vlines = verticalLinealModel.getGuideLines(); for ( int i = 0; i < vlines.length; i++ ) { final GuideLine guideLine = vlines[ i ]; if ( guideLine.isActive() ) { verticalGuidesPositions.add( StrictGeomUtility.toInternalValue( guideLine.getPosition() ), null ); } } final SnapToPositionModel hElementModel = horizontalSnapModel.getElementModel(); hElementModel.clear(); final BreakPositionsList horizontalPositions = getHorizontalEdgePositions(); final long[] horizontalKeys; if ( horizontalPositionsModel == null ) { horizontalKeys = horizontalPositions.getKeys(); } else { horizontalKeys = horizontalPositionsModel.getBreaks(); } for ( int i = 0; i < horizontalKeys.length; i++ ) { final long key = horizontalKeys[ i ]; hElementModel.add( key, horizontalPositions.getOwner( key ) ); } final SnapToPositionModel vElementModel = verticalSnapModel.getElementModel(); vElementModel.clear(); final BreakPositionsList verticalPositions = getVerticalEdgePositions(); final long[] verticalKeys = verticalPositions.getKeys(); for ( int i = 0; i < verticalKeys.length; i++ ) { final long key = verticalKeys[ i ]; vElementModel.add( key, verticalPositions.getOwner( key ) ); } switch( currentIndicator ) { case MOVE: operation = new MoveDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ); break; case BOTTOM_CENTER: operation = new ResizeBottomDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ); break; case MIDDLE_RIGHT: operation = new ResizeRightDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ); break; case MIDDLE_LEFT: operation = new ResizeLeftDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ); break; case TOP_CENTER: operation = new ResizeTopDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ); break; case BOTTOM_LEFT: { final CompoundDragOperation op = new CompoundDragOperation(); op.add( new ResizeLeftDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ) ); op.add( new ResizeBottomDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ) ); operation = op; break; } case BOTTOM_RIGHT: { final CompoundDragOperation op = new CompoundDragOperation(); op.add( new ResizeRightDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ) ); op.add( new ResizeBottomDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ) ); operation = op; break; } case TOP_LEFT: { final CompoundDragOperation op = new CompoundDragOperation(); op.add( new ResizeLeftDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ) ); op.add( new ResizeTopDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ) ); operation = op; break; } case TOP_RIGHT: { final CompoundDragOperation op = new CompoundDragOperation(); op.add( new ResizeRightDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ) ); op.add( new ResizeTopDragOperation( visualElements, originPoint, horizontalSnapModel, verticalSnapModel ) ); operation = op; break; } default: } if ( operation != null ) { undoEntryBuilder = new MassElementStyleUndoEntryBuilder( visualElements ); } } protected void finishDragOperation() { if ( operation != null ) { operation.finish(); final MassElementStyleUndoEntry undoEntry = undoEntryBuilder.finish(); getRenderContext().getUndo() .addChange( Messages.getString( "AbstractRenderComponent.ResizeUndoName" ), undoEntry ); } operation = null; undoEntryBuilder = null; repaintConditionally(); fpsCalculator.setActive( false ); logger.debug( "MoveOperation-performance: " + fpsCalculator.getFps() ); } public void repaintConditionally() { if ( !paintingImmediately && operation != null ) { // guard against paintImmediately being called again if we're // already in the middle of repainting. paintingImmediately = true; paintImmediately( 0, 0, getWidth(), getHeight() ); paintingImmediately = false; } else { repaint( REPAINT_INTERVAL ); } } protected boolean isMouseOperationInProgress() { return operation != null; } protected boolean isMouseOperationPossible() { return currentIndicator != null && currentIndicator != SelectionOverlayInformation.InRangeIndicator.NOT_IN_RANGE; } protected void installLineals( final LinealModel horizontalLinealModel, final HorizontalPositionsModel horizontalPositionsModel ) { final LinealModel verticalLinealModel; final ElementRenderer elementRenderer = getElementRenderer(); if ( elementRenderer != null ) { verticalLinealModel = elementRenderer.getVerticalLinealModel(); } else { verticalLinealModel = null; } if ( this.verticalLinealModel != null ) { this.verticalLinealModel.removeLinealModelListener( repaintHandler ); } if ( this.horizontalLinealModel != null ) { this.horizontalLinealModel.removeLinealModelListener( repaintHandler ); } this.horizontalPositionsModel = horizontalPositionsModel; this.verticalLinealModel = verticalLinealModel; this.horizontalLinealModel = horizontalLinealModel; if ( this.verticalLinealModel != null ) { this.verticalLinealModel.addLinealModelListener( repaintHandler ); } if ( this.horizontalLinealModel != null ) { this.horizontalLinealModel.addLinealModelListener( repaintHandler ); } if ( elementRenderer != null ) { elementRenderer.removeChangeListener( repaintHandler ); elementRenderer.addChangeListener( repaintHandler ); } } public Dimension getMinimumSize() { return getPreferredSize(); } public Dimension getPreferredSize() { final ElementRenderer rendererRoot = getElementRenderer(); if ( rendererRoot == null ) { return new Dimension( 0, 0 ); } final float zoom = getRenderContext().getZoomModel().getZoomAsPercentage(); try { final Rectangle2D bounds = rendererRoot.getBounds(); final int leftBorder; if ( isShowLeftBorder() ) { leftBorder = (int) getLeftBorder(); } else { leftBorder = 0; } final int topBorder; if ( isShowTopBorder() ) { topBorder = (int) getTopBorder(); } else { topBorder = 0; } final int width = (int) ( zoom * ( leftBorder + bounds.getWidth() ) ); final int height = (int) ( zoom * ( topBorder + bounds.getHeight() ) ); return new Dimension( width, height ); } catch ( Exception e ) { UncaughtExceptionsModel.getInstance().addException( e ); return new Dimension( 0, (int) ( zoom * rendererRoot.getVisualHeight() ) ); } } public void removeNotify() { KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener ( "permanentFocusOwner", editorRemover ); // NON-NLS editorRemover = null; super.removeNotify(); } protected boolean stopCellEditing() { if ( isEditing() == false ) { return true; } final ReportElementInlineEditor elementInlineEditor = getCellEditor(); if ( elementInlineEditor == null ) { return true; } return elementInlineEditor.stopCellEditing(); } protected void updateCursorForIndicator() { if ( currentIndicator == null ) { setCursor( Cursor.getDefaultCursor() ); return; } switch( currentIndicator ) { case NOT_IN_RANGE: setCursor( Cursor.getDefaultCursor() ); break; case MOVE: setCursor( Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR ) ); break; case BOTTOM_CENTER: setCursor( Cursor.getPredefinedCursor( Cursor.S_RESIZE_CURSOR ) ); break; case BOTTOM_LEFT: setCursor( Cursor.getPredefinedCursor( Cursor.SW_RESIZE_CURSOR ) ); break; case BOTTOM_RIGHT: setCursor( Cursor.getPredefinedCursor( Cursor.SE_RESIZE_CURSOR ) ); break; case MIDDLE_LEFT: setCursor( Cursor.getPredefinedCursor( Cursor.W_RESIZE_CURSOR ) ); break; case MIDDLE_RIGHT: setCursor( Cursor.getPredefinedCursor( Cursor.E_RESIZE_CURSOR ) ); break; case TOP_LEFT: setCursor( Cursor.getPredefinedCursor( Cursor.NW_RESIZE_CURSOR ) ); break; case TOP_CENTER: setCursor( Cursor.getPredefinedCursor( Cursor.N_RESIZE_CURSOR ) ); break; case TOP_RIGHT: setCursor( Cursor.getPredefinedCursor( Cursor.NE_RESIZE_CURSOR ) ); break; } } }