/*
* (C) 2004 - Geotechnical Software Services
*
* This code is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This code 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
package no.geosoft.cc.graphics;
import no.geosoft.cc.geometry.Rect;
/**
* Common base class for objects that can be positioned through
* hints within their container.
*
* @author <a href="mailto:info@geosoft.no">GeoSoft</a>
*/
abstract class GPositional
{
protected GSegment segment_;
protected int positionHint_;
protected Rect rectangle_;
protected boolean isVisible_;
protected GStyle style_;
protected GStyle actualStyle_; // Adjusted for owner inherits
private final boolean isAllowingOverlaps_;
/**
* Create a positional instance with specified position hint.
* @see #setPositionHint(int)
*
* @param positionHint Position hint for this instance.
* @param isAllowingOverlaps True if this instance can be overlapped by
* other positionals, false otherwise.
*/
GPositional (int positionHint, boolean isAllowingOverlaps)
{
rectangle_ = new Rect();
isVisible_ = false;
isAllowingOverlaps_ = isAllowingOverlaps;
segment_ = null;
style_ = null;
actualStyle_ = new GStyle();
setPositionHint (positionHint);
}
/**
* Set owner segment of this positional.
*
* @param segment New owner segment of this positional.
*/
void setSegment (GSegment segment)
{
segment_ = segment;
updateStyle();
}
/**
* Return owner segment of this positional.
*
* @return Owner segment of this positional (or null if not
* attached to a segment).
*/
GSegment getSegment()
{
return segment_;
}
/**
* Return graphics object of this positional.
*
* @return Object of this positional (or null if not attached to an
* object).
*/
protected GObject getObject()
{
return segment_ != null ? segment_.getOwner() : null;
}
/**
* Set position hint for this positional.
* <p>
* Position hints is a or'ed list of:
*
* <ul>
* <li>Line position hint - FIRST, LAST, TOP, BOTTON, LEFT, RIGHT
* <li>Point position hint - CENTER, NORTH, SOUTH, EAST, WEST, NORTHEAST,
* NORTHWEST, SOUTHEAST, SOUTHWEST
* <li>Algorithm - STATIC, DYNAMIC
* </ul>
*
* <p>
* Line position hints are interpreted as follows:
*
* <ul>
* <li> GPosition.FIRST - Attach to first polyline point.
* <li> GPosition.LAST - Attach to last polyline point.
* <li> GPosition.TOP - Attach to top polyline as it appears on screen
* when rendered.
* <li> GPosition.BOTTOM - Attach to bottom polyline as it appears on screen
* when rendered.
* <li> GPosition.RIGHT - Attach to right polyline as it appears on screen
* when rendered.
* <li> GPosition.LEFT - Attach to left polyline as it appears on screen
* when rendered.
* </ul>
*
* If a line position hint is not given, the text is attached to the n'th
* point of the polyline according to when it was added to the segment,
* i.e, the first text added is attached to the first coordinate, the second
* text added is attatched to the second coordinate and so on. This is
* convenient if there is a text associated with each line vertex.
*
* <p>
* For the above, the given point becomes the initial approach for
* positioning. If this initial position is outside the window (and position
* hint not explicitly set to GPosition.STATIC) the text is moved along
* the polyline till it becomes fully visible.
*
* <p>
* If text position hint is GPosition.MIDDLE, the initial position is
* the center of the polyline bounding box. If this point is not visble
* the text is not rendered.
*
* <p>
* At this point an anchor point is found for the text. Now the point
* positioning text hints are examined:
*
* <ul>
* <li> GPosition.CENTER - Put text centered on top of anchor point.
* <li> GPosition.NORTH - Put text above anchor point.
* <li> GPosition.SOUTH - Put text below anchor point.
* <li> GPosition.EAST - Put text to the right of anchor point.
* <li> GPosition.WEST - Put text to the left of anchor point.
* <li> GPosition.NORTHEAST - Put text above and right of the anchor point.
* <li> GPosition.NORTHWEST - Put text above and left of the anchor point.
* <li> GPosition.SOUTHEAST - Put text below and right of the anchor point.
* <li> GPosition.SOUTHWEST - Put text below and left of the anchor point.
* </ul>
*
* <p>
* If a point positioing hint is not given, the defualt is GPosition.CENTER
* unless line position hint is GPosition.TOP (implying GPosition.NORTH),
* GPosition.BOTTOM (implying GPosition.SOUTH), GPosition.LEFT (implying
* GPosition.WEST) or GPosition:RIGHT (implying GPosition.EAST).
*
* <p>
* Now, if the text in this location overlap an already positioned text,
* it is further adjusted (unless position hint is not explicitly set to
* GPosition.STATIC as discussed above). It is again moved along the
* polyline till a free location is found. If this cannot be acheived,
* the text is not rendered
*
* @see GPosition
*
* @param positionHint Position hint for this positional.
*/
public void setPositionHint (int positionHint)
{
positionHint_ = positionHint;
// If point hint is not specified, default it according to line hint
if ((positionHint_ &
(GPosition.CENTER |
GPosition.NORTHWEST |
GPosition.NORTH |
GPosition.NORTHEAST |
GPosition.WEST |
GPosition.EAST |
GPosition.SOUTHWEST |
GPosition.SOUTH |
GPosition.SOUTHEAST)) == 0) {
if ((positionHint_ & GPosition.TOP) != 0)
positionHint_ |= GPosition.NORTH;
else if ((positionHint_ & GPosition.BOTTOM) != 0)
positionHint_ |= GPosition.SOUTH;
else if ((positionHint_ & GPosition.LEFT) != 0)
positionHint_ |= GPosition.WEST;
else if ((positionHint_ & GPosition.RIGHT) != 0)
positionHint_ |= GPosition.EAST;
}
// If algorithm is not specified, default to dynamic
if ((positionHint_ & GPosition.DYNAMIC) == 0 &&
(positionHint_ & GPosition.STATIC) == 0)
positionHint_ |= GPosition.DYNAMIC;
}
/**
* Return position hint of this positional.
* @see #setPositionHint(int).
*
* @return Position hint of this positional.
*/
public int getPositionHint()
{
return positionHint_;
}
/**
* Return if this positional is allowed to be overlapped by other positionals.
*
* @return True if this positional is allowed to be overlapped other
* positionals false otherwise.
*/
boolean isAllowingOverlaps()
{
return isAllowingOverlaps_;
}
/**
* Return true if the position of this positional is along the line
* (as opposed to be fixed at a point on the line).
*
* @return True if this poitional is "line positional", false otherwise.
*/
boolean isLinePositional()
{
return (positionHint_ & (GPosition.FIRST |
GPosition.LAST |
GPosition.TOP |
GPosition.BOTTOM |
GPosition.LEFT |
GPosition.RIGHT |
GPosition.MIDDLE)) != 0;
}
/**
* Return true if this positional is visible after the annotation process.
*
* @return True if this positional is visible, false otherwise.
*/
boolean isVisible()
{
return isVisible_;
}
/**
* Set visibility of this positional. Visibility is due to positional
* layout, not visibility settings on the owner object.
*
* @param isVisible True if positional is visible, false otherwise.
*/
void setVisible (boolean isVisible)
{
isVisible_ = isVisible;
}
/**
* Return rectangle bounding box of this positional.
*
* @return Rectangle bounding box of this positional.
*/
Rect getRectangle()
{
return rectangle_;
}
/**
* Compute the size (width and height component of the rectangle variable)
* of this positional.
*/
abstract void computeSize();
/**
* Return margin to use when positioning this positional at a point.
* If point position hint is NORTH, the positional rectangle is put
* this many pixels above the associated point etc.
*
* @return Margin for point positioning.
*/
int getMargin()
{
// Default to 0, override in subclass if necessary.
return 0;
}
/**
* Set new style for this object. Style elements not explicitly
* set within this GStyle object are inherited from parent objects.
* Child objects without explicit set style will inherit from this
* style object.
*
* @param style Style for this object.
*/
public void setStyle (GStyle style)
{
style_ = style;
updateStyle();
}
/**
* Get style of this GText. This is the style set by setStyle()
* and not necesserily the style as it appears on screen as unset
* style elements are inherited from parents.
*
* @return Style of this object.
*/
public GStyle getStyle()
{
return style_;
}
/**
* These are the actual style used for this object when
* inheritance for unset values are resolved.
* TODO: Make this public?
*
* @return Actual style for this segment.
*/
GStyle getActualStyle()
{
return actualStyle_;
}
/**
* Update actualStyle based on this style and style of parent
* elements. Actual style is how this element is actually rendered
* on screen.
*/
void updateStyle()
{
// Initialize style
actualStyle_ = new GStyle();
// Update with parent style
if (segment_ != null)
actualStyle_.update (segment_.getActualStyle());
// Update (and possible override) with present style
if (style_ != null)
actualStyle_.update (style_);
// Flag owner region and scene annotation as invalid
GObject object = getObject();
GScene scene = object != null ? object.getScene() : null;
if (object != null) object.flagRegionValid (false);
if (scene != null) scene.setAnnotationValid (false);
updateDamage();
}
/**
* Mark the extent of this positional as damaged.
*/
void updateDamage()
{
GObject object = getObject();
if (isVisible() &&
rectangle_ != null &&
!rectangle_.isEmpty() &&
object != null &&
object.getWindow() != null)
object.getWindow().updateDamageArea (rectangle_);
}
}