/*!
* 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;
}
}
}