/*******************************************************************************
* Copyright © 2008, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.ide.rui.visualeditor.internal.editor;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.edt.ide.rui.visualeditor.internal.nl.Messages;
import org.eclipse.edt.ide.rui.visualeditor.internal.preferences.EvPreferences;
import org.eclipse.edt.ide.rui.visualeditor.internal.widget.WidgetPainter;
import org.eclipse.edt.ide.rui.visualeditor.internal.widget.WidgetPart;
import org.eclipse.edt.ide.rui.visualeditor.internal.widget.layout.WidgetLayout;
import org.eclipse.edt.ide.rui.visualeditor.internal.widget.layout.WidgetLayoutRegistry;
import org.eclipse.edt.ide.rui.visualeditor.plugin.Activator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
/**
* A class that does all the painting for the overlay.
* The methods in this class were placed in this file to reduce the size of the overlay class.
* This class directly reads some of the overlay's protected instance variables.
*/
public class EvDesignOverlayPainter {
protected final static Rectangle NOT_SHOWN_REC = new Rectangle( 0, 0, 16, 16 );
protected boolean _bPatternSelection = false;
protected boolean _bPatternDropLocationSelected = false;
protected Image _imageTransparencyFixed = null;
protected Image _imageTransparencyVariable = null;
protected EvDesignOverlay _overlay = null;
protected WidgetPainter _widgetPainter = null;
protected Point _mouseDownPoint = null;
protected final static Color COLOR_BLACK = Display.getCurrent().getSystemColor( SWT.COLOR_BLACK );
protected final static Color COLOR_WHITE = Display.getCurrent().getSystemColor( SWT.COLOR_WHITE );
protected final static Color COLOR_DARK_GRAY = Display.getCurrent().getSystemColor( SWT.COLOR_DARK_GRAY );
protected final static Color COLOR_GRAY = Display.getCurrent().getSystemColor( SWT.COLOR_GRAY );
public EvDesignOverlayPainter( EvDesignOverlay overlay ) {
_overlay = overlay;
_widgetPainter = new WidgetPainter( _overlay );
_imageTransparencyFixed = Activator.getImage( "obj16/transparencymaskfixed.gif" );
_imageTransparencyVariable = Activator.getImage( "obj16/transparencymaskvariable.jpg" );
}
public void setMouseDownPoint(Point mouseDownPoint){
_mouseDownPoint = mouseDownPoint;
}
/**
* Paints the entire overlay with a background color.
*/
protected void paintBlank( GC gc ){
Rectangle rectBounds = _overlay.getBounds();
gc.fillRectangle( rectBounds );
}
/**
* Paints a drop rectangle
*/
protected void paintDropLocationPotential( GC gc, Rectangle rectDrop, int iLocation, int transparent, int lineStyle, int lineWidth ) {
if( gc.getClipping().intersects( rectDrop ) == false )
return;
gc.setAlpha( transparent );
gc.setBackground( _overlay._colorDropTargetPotential );
gc.fillRectangle( rectDrop );
gc.setLineStyle( lineStyle );
gc.setLineWidth( lineWidth );
gc.setForeground( COLOR_BLACK );
paintRectangle( gc, rectDrop, iLocation );
gc.setAlpha( 255 );
}
/**
*
*/
protected void paintDropLocations( GC gc ) {
if( _overlay._listDropLocations == null )
return;
// Paint all potential drop locations in backward order
// to paint the children before the parents
//-----------------------------------------------------
Object[] objArray = _overlay._listDropLocations.toArray();
for( int i = objArray.length - 1; i >= 0; i-- )
if( objArray[i] != _overlay._dropLocation )
paintDropLocationPotential( gc, ( (EvDesignOverlayDropLocation)objArray[ i ] ).rectDrop,
( (EvDesignOverlayDropLocation)objArray[ i ] ).iLocation,
( (EvDesignOverlayDropLocation)objArray[ i ] ).selectedTransparent,
( (EvDesignOverlayDropLocation)objArray[ i ] ).lineStyle,
( (EvDesignOverlayDropLocation)objArray[ i ] ).lineWidth);
// Paint the selected drop location
//---------------------------------
if( _overlay._dropLocation != null ) {
Rectangle rectDrop = _overlay._dropLocation.rectDrop;
gc.setAlpha( _overlay._dropLocation.selectedTransparent );
// Fill with color
//----------------
gc.setBackground( _overlay._colorDropTargetSelected );
gc.fillRectangle( rectDrop );
// Pattern lines
// Determine whether to use black or white lines
// Luminance = 0.30Red + 0.59Grn + 0.11Blu
//----------------------------------------------
if( _bPatternDropLocationSelected == true ) {
int iRed = _overlay._colorDropTargetSelected.getRed();
int iGrn = _overlay._colorDropTargetSelected.getGreen();
int iBlu = _overlay._colorDropTargetSelected.getBlue();
double dLuminance = 0.30 * iRed + 0.59 * iGrn + 0.11 * iBlu;
boolean bWhite = dLuminance < EvConstants.LUMINOSITY_WHITE_BLACK_BOUNDARY;
gc.setLineStyle( SWT.LINE_SOLID );
gc.setLineWidth( 1 );
gc.setForeground( bWhite ? COLOR_WHITE : COLOR_BLACK );
if( rectDrop.width > 8 ) // Vertical lines
for( int i = 1; i < rectDrop.width; i += 4 )
gc.drawLine( rectDrop.x + i, rectDrop.y + 1, rectDrop.x + i, rectDrop.y + rectDrop.height - 2 );
if( rectDrop.height > 8 ) // Horizontal lines
for( int i = 1; i < rectDrop.height; i += 4 )
gc.drawLine( rectDrop.x + 1, rectDrop.y + i, rectDrop.x + rectDrop.width - 2, rectDrop.y + i );
}
// Outer rectangle
//----------------
gc.setLineStyle( _overlay._dropLocation.lineStyle );
gc.setLineWidth( 2 );
gc.setForeground( COLOR_BLACK );
paintRectangle( gc, rectDrop, _overlay._dropLocation.iLocation );
gc.setAlpha( 255 );
}
}
/**
* Paints the hierarchy and the insertion point. For each node in the hierarchy
* the widget icon and its type string is painted.
*/
protected void paintHierarchy( GC gc ) {
if( _overlay._dropLocation == null || _overlay._listDropLocations == null )
return;
if( _overlay._dropLocation.widgetParent == null || _overlay._dropLocation.widgetLayoutData != null )
return;
Display display = Display.getCurrent();
if( display == null )
return;
// Obtain the height of widget icons
//----------------------------------
WidgetPart widgetRoot = _overlay._pageDesign.getWidgetManager().getWidgetRoot();
Image image = Activator.getDefault().getWidgetImage( widgetRoot );
int iFontImageHeight = gc.getFontMetrics().getHeight();
iFontImageHeight = Math.max( iFontImageHeight, image.getBounds().height );
// Offset between rows and between image location to widget type string
//---------------------------------------------------------------------
int iOffset = iFontImageHeight + 8;
ScrolledComposite scroll = (ScrolledComposite)_overlay.getParent().getParent();
Rectangle rectClient = scroll.getClientArea();
// Obtain a list of parent widgets
//--------------------------------
List listParents = new ArrayList();
WidgetPart widgetParent = _overlay._dropLocation.widgetParent;
while( widgetParent != widgetRoot ) {
listParents.add( 0, widgetParent );
widgetParent = widgetParent.getParent();
}
// Find the maximum text length of the children
//---------------------------------------------
int iMaxTextWidth = 16;
List listChildren = _overlay._dropLocation.widgetParent.getChildren();
for( int i = 0; i < listChildren.size(); i++ ) {
WidgetPart widgetChild = (WidgetPart)listChildren.get( i );
iMaxTextWidth = Math.max( iMaxTextWidth, gc.stringExtent( widgetChild.getLabel() ).x );
}
// Also do the parent text length
//-------------------------------
iMaxTextWidth = Math.max( iMaxTextWidth, gc.stringExtent( _overlay._dropLocation.widgetParent.getLabel() ).x );
String strWidgetTypeDragging = null;
Dimension dimHeading = new Dimension( 0, 0 );
if( _overlay._widgetDragging != null ) {
strWidgetTypeDragging = _overlay._widgetDragging.getLabel();
Point ptWidgetTypeSize = gc.stringExtent( strWidgetTypeDragging );
dimHeading.width = iOffset + ptWidgetTypeSize.x + 8; // Image width plus type string width plus space
dimHeading.height = ptWidgetTypeSize.y + 8; // Type string plus space
}
// Compute the bounds
//-------------------
Rectangle rectBounds = new Rectangle( 0, 0, 0, 0 );
Point ptOrigin = scroll.getOrigin();
rectBounds.y = ptOrigin.y + 16;
rectBounds.width = 4 + listParents.size() * 16 + iOffset + iMaxTextWidth + 8;
rectBounds.width = Math.max( rectBounds.width, dimHeading.width );
rectBounds.height = dimHeading.height + ( listParents.size() + listChildren.size() ) * iOffset + 16;
boolean bLeft = _overlay._ptMouse.x > +ptOrigin.x + rectClient.width / 2;
rectBounds.x = ptOrigin.x + rectClient.width / 2 + 16;
if( bLeft == true )
rectBounds.x -= ( rectBounds.width + 32 );
// Paint the box
//--------------
gc.setLineWidth( 1 );
gc.setLineStyle( SWT.LINE_SOLID );
gc.setBackground( COLOR_WHITE );
gc.fillRectangle( rectBounds );
// Inner line
//-----------
gc.setForeground( COLOR_GRAY );
gc.drawRectangle( rectBounds );
// Outer line
//-----------
rectBounds.x -= 1;
rectBounds.y -= 1;
rectBounds.width += 2;
rectBounds.height += 2;
gc.setForeground( COLOR_DARK_GRAY );
gc.drawRectangle( rectBounds );
rectBounds.x += 1;
rectBounds.y += 1;
rectBounds.width -= 2;
rectBounds.height -= 2;
// Separator line and widget type name
//------------------------------------
if( strWidgetTypeDragging != null ) {
gc.setForeground( COLOR_GRAY );
gc.drawLine( rectBounds.x + 1, rectBounds.y + dimHeading.height + 2, rectBounds.x + rectBounds.width - 1, rectBounds.y + dimHeading.height + 2 );
gc.drawLine( rectBounds.x + 1, rectBounds.y + dimHeading.height + 4, rectBounds.x + rectBounds.width - 1, rectBounds.y + dimHeading.height + 4 );
image = Activator.getDefault().getWidgetImage( _overlay._widgetDragging );
gc.drawImage( image, rectBounds.x + 4, rectBounds.y + 4 );
gc.setForeground( COLOR_BLACK );
gc.drawString( strWidgetTypeDragging, rectBounds.x + 4 + iOffset, rectBounds.y + 4, true );
}
// Paint the insertion point. Its vertical line has an approximate length that may be too long.
// We do this first so that an icon or another line can be drawn on top of the vertical line.
//----------------------------------------------------------------------------------------------
gc.setLineStyle( SWT.LINE_SOLID );
gc.setBackground( _overlay._colorDropTargetSelected );
gc.setForeground( COLOR_BLACK );
Rectangle rect = new Rectangle( 0, 0, 0, 0 );
rect.x = rectBounds.x + listParents.size() * 16 + 4;
rect.y = rectBounds.y + dimHeading.height + 10 + ( listParents.size() + _overlay._dropLocation.iIndex ) * iOffset;
rect.width = rectBounds.x + rectBounds.width - rect.x - 8;
rect.height = 4;
gc.fillRectangle( rect );
gc.drawRectangle( rect );
// Vertical lines in the insertion point
//--------------------------------------
_bPatternDropLocationSelected = EvPreferences.getBoolean( EvConstants.PREFERENCE_PATTERN_DROP_LOCATION_SELECTED );
if( _bPatternDropLocationSelected == true ){
for( int i = 4; i < rect.width; i += 4 )
gc.drawLine( rect.x + i, rect.y, rect.x + i, rect.y + rect.height - 1 );
}
// Line to the insertion point
//----------------------------
gc.setForeground( COLOR_GRAY );
if( _overlay._dropLocation.widgetParent != widgetRoot ) {
gc.drawLine( rect.x - 8, rect.y + rect.height / 2 - iFontImageHeight, rect.x - 8, rect.y + rect.height / 2 ); // Vertical
gc.drawLine( rect.x - 8, rect.y + rect.height / 2, rect.x - 1, rect.y + rect.height / 2 ); // Horizontal
}
// Paint the parent hierarchy
//---------------------------
rect = new Rectangle( rectBounds.x + 4, rectBounds.y + dimHeading.height + 16, image.getBounds().width, iFontImageHeight );
for( int i = 0; i < listParents.size(); i++ ) {
widgetParent = (WidgetPart)listParents.get( i );
// Vertical, horizontal line
//--------------------------
if( i > 0 ) {
gc.setForeground( COLOR_GRAY );
gc.drawLine( 4 + rect.x - iOffset / 2, rect.y - ( iOffset - iFontImageHeight ), 4 + rect.x - iOffset / 2, rect.y + iFontImageHeight / 2 );
gc.drawLine( 4 + rect.x - iOffset / 2, rect.y + iFontImageHeight / 2, 4 + rect.x - iOffset / 2 + iFontImageHeight / 2, rect.y + iFontImageHeight / 2 );
}
// Widget icon
//------------
image = Activator.getDefault().getWidgetImage( widgetParent );
gc.drawImage( image, rect.x, rect.y );
// Widget type
//------------
gc.setBackground( COLOR_WHITE );
gc.setForeground( COLOR_BLACK );
gc.drawString( widgetParent.getLabel(), rect.x + iOffset, rect.y, true );
rect.x += 16;
rect.y += iOffset;
}
// Paint the widget's children
//----------------------------
for( int i = 0; i < listChildren.size(); i++ ) {
gc.setBackground( COLOR_WHITE );
gc.setForeground( COLOR_GRAY );
WidgetPart widgetChild = (WidgetPart)listChildren.get( i );
// Vertical, horizontal line
//--------------------------
if( widgetChild.getParent() != widgetRoot ) {
if( i < listChildren.size() - 1 )
gc.drawLine( 4 + rect.x - iOffset / 2, rect.y - ( iOffset - iFontImageHeight ), 4 + rect.x - iOffset / 2, rect.y + iOffset );
else
gc.drawLine( 4 + rect.x - iOffset / 2, rect.y - ( iOffset - iFontImageHeight ), 4 + rect.x - iOffset / 2, rect.y + iFontImageHeight / 2 );
gc.drawLine( 4 + rect.x - iOffset / 2, rect.y + iFontImageHeight / 2, 4 + rect.x - iOffset / 2 + iFontImageHeight / 2, rect.y + iFontImageHeight / 2 );
}
// Widget icon
//------------
image = Activator.getDefault().getWidgetImage( widgetChild );
gc.drawImage( image, rect.x, rect.y );
// Widget type
//------------
gc.setForeground( COLOR_BLACK );
gc.drawString( widgetChild.getLabel(), rect.x + iOffset, rect.y, true );
// Rectangle around the widget being dragged
// Draw gray solid line under selection color dotted line
// so the line is always visible for all selection colors
//-------------------------------------------------------
if( widgetChild == _overlay._widgetDragging ) {
Rectangle rectBorder = new Rectangle( rect.x - 1, rect.y - 1, iOffset + iMaxTextWidth + 3, rect.height + 3 );
gc.setLineWidth( 2 );
gc.setLineStyle( SWT.LINE_SOLID );
gc.setForeground( COLOR_GRAY );
gc.drawRectangle( rectBorder );
gc.setLineStyle( SWT.LINE_DOT );
gc.setForeground( _overlay._colorWidgetSelected );
gc.drawRectangle( rectBorder );
gc.setLineStyle( SWT.LINE_SOLID );
gc.setLineWidth( 1 );
}
rect.y += iOffset;
}
}
/**
* Displays how to start instructions when there are no widgets.
*/
protected void paintInstructions( GC gc ) {
// Disable for now. The instructions sometimes gets drawn when
// a runtime message appears instead of the web page
//-------------------------------------------------------------
if( true /* _overlay._bShowInstructions == false */)
return;
gc.setBackground( Display.getCurrent().getSystemColor( SWT.COLOR_WHITE ) );
gc.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_BLACK ) );
gc.drawString( Messages.NL_Drag_an_item_from_the_palette_and_drop_it_here, 16, 16 );
}
/**
* Paints a white dotted pattern over the entire client area.
*/
protected void paintOpaque( GC gc ) {
// Copy the transparency mode in case
// we need to override it for dragging
//------------------------------------
int iTransparencyMode = _overlay._iTransparencyMode;
// Override the transparency mode to dotted pattern if we are
// dragging and the 'half transparency while dragging' preference is on
//---------------------------------------------------------------------
if( _overlay._listDropLocations != null ) {
boolean bSemiTransparentWhileDragging = EvPreferences.getBoolean( EvConstants.PREFERENCE_SEMITRANSPARENCY_WHILE_DRAGGING );
if( bSemiTransparentWhileDragging == true )
iTransparencyMode = EvConstants.SEMITRANSPARENCY_FIXED;
}
// Do nothing if there is no transparency pattern to paint
//--------------------------------------------------------
if( iTransparencyMode == EvConstants.SEMITRANSPARENCY_NONE )
return;
int iImageW = _imageTransparencyFixed.getBounds().width;
int iImageH = _imageTransparencyVariable.getBounds().height;
ScrolledComposite compositeParent = (ScrolledComposite)_overlay.getParent().getParent();
Rectangle rectClient = compositeParent.getClientArea();
Rectangle rectParent = _overlay.getParent().getBounds();
Rectangle rectBounds = new Rectangle( -rectParent.x - iImageW, -rectParent.y - iImageH, rectClient.width + iImageW, rectClient.height + iImageH );
// Start on image boundary
//------------------------
rectBounds.x = ( rectBounds.x / 2 ) * 2;
rectBounds.y = ( rectBounds.y / 2 ) * 2;
if( rectBounds.intersects( gc.getClipping() ) == false )
return;
boolean bUseAlpha = iTransparencyMode == EvConstants.SEMITRANSPARENCY_VARIABLE;
// Variable amount of transparency
//--------------------------------
if( bUseAlpha == true ) {
gc.setAlpha( 255 - _overlay._iTransparencyAmount );
gc.drawImage( _imageTransparencyVariable, 0, 0, iImageW, iImageH, rectBounds.x, rectBounds.y, rectBounds.width, rectBounds.height );
gc.setAlpha( 255 );
}
// Fixed amount of transparency
//-----------------------------
else {
for( int y = rectBounds.y; y < rectBounds.y + rectBounds.height; y += iImageH )
for( int x = rectBounds.x; x < rectBounds.x + rectBounds.width; x += iImageW )
gc.drawImage( _imageTransparencyFixed, x, y );
}
}
/**
* Paints a rectangle based on its facing orientation. The location side is painted dashed.
*/
protected void paintRectangle( GC gc, Rectangle rect, int iLocation ) {
// gc.setLineStyle( SWT.LINE_SOLID );
switch( iLocation ){
case SWT.LEFT:
gc.drawLine( rect.x, rect.y, rect.x + rect.width, rect.y );
gc.drawLine( rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height );
gc.drawLine( rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height );
gc.setLineStyle( SWT.LINE_DASH );
gc.drawLine( rect.x, rect.y, rect.x, rect.y + rect.height );
break;
case SWT.RIGHT:
gc.drawLine( rect.x, rect.y, rect.x + rect.width, rect.y );
gc.drawLine( rect.x, rect.y, rect.x, rect.y + rect.height );
gc.drawLine( rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height );
gc.setLineStyle( SWT.LINE_DASH );
gc.drawLine( rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height );
break;
case SWT.TOP:
gc.drawLine( rect.x, rect.y, rect.x, rect.y + rect.height );
gc.drawLine( rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height );
gc.drawLine( rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height );
gc.setLineStyle( SWT.LINE_DASH );
gc.drawLine( rect.x, rect.y, rect.x + rect.width, rect.y );
break;
case SWT.BOTTOM:
gc.drawLine( rect.x, rect.y, rect.x, rect.y + rect.height );
gc.drawLine( rect.x, rect.y, rect.x + rect.width, rect.y );
gc.drawLine( rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height );
gc.setLineStyle( SWT.LINE_DASH );
gc.drawLine( rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height );
break;
case SWT.CENTER:
default:
gc.drawRectangle( rect );
break;
}
}
/**
* Paints all widgets
*/
protected void paintWidgets( GC gc ) {
Iterator iterWidgets = _overlay.getWidgets();
boolean bDrawOutline = _overlay._listDropLocations != null && _overlay._listDropLocations.size() > 0;
try {
while( iterWidgets.hasNext() == true ) {
WidgetPart widget = (WidgetPart)iterWidgets.next();
if( widget.getBounds().intersects( gc.getClipping() ) == false || widget.getBounds().equals( NOT_SHOWN_REC ) )
continue;
boolean isContainer = _overlay.isContainer( widget );
_widgetPainter.setBounds( widget.getBounds() );
_widgetPainter.setDragging( _overlay._widgetDragging == widget );
_widgetPainter.setMouseOver( widget.getMouseOver() );
_widgetPainter.setSelected( widget.getSelected() );
_widgetPainter.setFocus( _overlay.isFocusControl() );
_widgetPainter.setDrawOutline( bDrawOutline );
_widgetPainter.setIsContainer( isContainer );
WidgetLayout layout = WidgetLayoutRegistry.getInstance().getWidgetLayout( WidgetLayoutRegistry.getLayoutName(widget), isContainer );
if ( layout != null ) {
if(widget.getSelected()){
_widgetPainter.setMouseDownPoint(_mouseDownPoint);
_widgetPainter.setIsSelectedWidget(true);
}else{
_widgetPainter.setIsSelectedWidget(false);
}
_widgetPainter.setInnerRectangles( layout.getInnerRectanglesForPaintingWidget(widget) );
} else {
_widgetPainter.setInnerRectangles( null );
}
_widgetPainter.paintWidget( gc );
}
} catch ( ConcurrentModificationException cme ) {
//do nothing
}
}
/**
* Paints the widget being dragged if any
*/
protected void paintWidgetDragging( GC gc ) {
if ( _overlay._widgetDragging == null )
return;
Iterator iterWidgets = _overlay.getWidgets();
while( iterWidgets.hasNext() == true ) {
WidgetPart widget = (WidgetPart)iterWidgets.next();
if( widget.getBoundsDragging().isEmpty() == false ) {
_widgetPainter.setBoundsDragging( widget.getBoundsDragging() );
_widgetPainter.paintWidgetDragging( gc );
}
}
}
/**
* Updates the selection color and pattern based on what is specified in the preferences.
* Updates the widget painter.
*/
public void updateColors() {
_bPatternDropLocationSelected = EvPreferences.getBoolean( EvConstants.PREFERENCE_PATTERN_DROP_LOCATION_SELECTED );
_bPatternSelection = EvPreferences.getBoolean( EvConstants.PREFERENCE_PATTERN_SELECTION );
_widgetPainter.updateColors();
}
}