/*
* EventsRenderer.java
*
* Created on December 13, 2005, 3:37 PM
*
*
*/
package org.das2.graph;
import java.awt.Shape;
import javax.swing.Icon;
import org.das2.datum.Datum;
import org.das2.datum.DatumRange;
import org.das2.datum.DatumUtil;
import org.das2.event.LabelDragRenderer;
import org.das2.event.MouseModule;
import org.das2.system.DasLogger;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import org.das2.datum.Units;
import org.das2.datum.UnitsUtil;
import static org.das2.graph.Renderer.encodeBooleanControl;
import org.das2.util.GrannyTextRenderer;
import org.das2.qds.DDataSet;
import org.das2.qds.DataSetOps;
import org.das2.qds.DataSetUtil;
import org.das2.qds.IDataSet;
import org.das2.qds.JoinDataSet;
import org.das2.qds.QDataSet;
import org.das2.qds.RankZeroDataSet;
import org.das2.qds.SemanticOps;
import org.das2.qds.TagGenDataSet;
import org.das2.qds.WritableDataSet;
import org.das2.qds.ops.Ops;
import org.das2.qds.util.DataSetBuilder;
import org.das2.util.LoggerManager;
/**
* Draw colored horizontal bars for the dataset, marking events datasets or modes of the data. This expects
* a QDataSet with the canonical scheme:
*
{@code
* Events[:,BUNDLE_1=4] where the columns are:
* BUNDLE_1=startTime,stopTime,Color,Message
* startTime,stopTime are in some time location unit. stopTime may also be an offset from startTime (e.g. seconds)
* Color is an int, that is either 0xRRGGBB or 0xAARRGGBB.
* Message is any datum, so typically an enumeration unit is used.
*}
* Note this also contains systems for coloring data in old schemes, such as the colorSpecifier interface and textSpecifier.
* These should not be used when a dataset will be sufficient.
*
* @see org.das2.qds.examples.Schemes#eventsList()
* @author Jeremy
*/
public class EventsRenderer extends Renderer {
public static final String PROP_COLOR = "color";
/**
* if true, then only then eventsMap is used, otherwise we look though all the events for hits.
*/
private boolean useOnlyEventsMap= false;
private static Logger logger= LoggerManager.getLogger("das2.graphics.renderer.events");
/**
* return bounding cube
* @param ds
* @return
*/
public static QDataSet doAutorange(QDataSet ds) {
QDataSet xrange;
DDataSet yrange;
yrange= DDataSet.createRank1(2);
yrange.putValue(0,0);
yrange.putValue(1,10);
QDataSet xmins;
QDataSet xmaxs;
if ( ds.rank()==1 && ds.property(QDataSet.DEPEND_0)==null ) {
xmins= ds; //vap+inline:2010-002T03:50,2010-002T03:54,2010-002T03:56&RENDER_TYPE=eventsBar
xmaxs= ds;
} else if ( ds.rank()==1 && ds.property(QDataSet.DEPEND_0)!=null ) {
xmins= (QDataSet)ds.property(QDataSet.DEPEND_0); //vap+inline:2010-002T03:50,2010-002T03:54,2010-002T03:56&RENDER_TYPE=eventsBar
xmaxs= xmins;
} else if ( ds.rank()==0 ) {
xmins= Ops.join( null, ds );
xmaxs= xmins;
} else {
xmins= SemanticOps.xtagsDataSet(ds);
if ( ds.length(0)>1 ) {
xmaxs= DataSetOps.unbundle( ds,1 );
} else {
xmaxs= xmins;
}
}
Units u0= SemanticOps.getUnits(xmins);
Units u1= SemanticOps.getUnits(xmaxs);
if ( xmins.length()==0 ) {
xrange= DDataSet.wrap( new double[] {0,1}, u0 );
} else {
if ( UnitsUtil.isIntervalOrRatioMeasurement(u1) ) {
//TODO: probably the day/days containing would be better
xrange= Ops.extent(xmins);
if ( !u1.isConvertibleTo(u0) && u1.isConvertibleTo(u0.getOffsetUnits()) ) {
xmaxs= Ops.add( xmins, xmaxs );
xrange= Ops.extent(xmaxs,xrange);
} else {
xrange= Ops.extent(xmaxs,xrange);
}
} else {
xrange= DDataSet.createRank1(2);
((DDataSet)xrange).putValue(0,0);
((DDataSet)xrange).putValue(1,10);
}
if ( xrange.value(0)= eventMap.length ) {
setLabel(null);
} else {
Units sxunits= SemanticOps.getUnits(xmins);
Units zunits= SemanticOps.getUnits(msgs);
Units sxmaxunits= SemanticOps.getUnits( xmaxs );
List ii= new ArrayList();
if ( useOnlyEventsMap==true && eventMap[ix]>-1 ) {
ii.add( eventMap[ix] );
} else {
if ( eventMap[ix]>-1 ) ii.add( eventMap[ix] );
for ( int i=0; i0 ) {
StringBuilder sb= new StringBuilder();
int count= 0;
for ( Integer ii1 : ii ) {
int i = ii1;
double sxmin= xmins.value(i);
double sxmax= xmaxs.value(i);
if ( !sxmaxunits.isConvertibleTo(sxunits) ) {
if ( sxmaxunits.isConvertibleTo(sxunits.getOffsetUnits() ) ) {
sxmax= sxmin + sxmaxunits.convertDoubleTo( sxunits.getOffsetUnits(), sxmax );
} else {
sxmax= sxmin;
}
} else {
sxmax= sxmaxunits.convertDoubleTo( sxunits, sxmax );
}
if ( sxmax10 ) {
break;
}
}
if ( ii.size()>count ) {
sb.append("(").append(ii.size()-count).append( " more items not shown)");
}
setLabel( sb.toString() );
} else {
setLabel(null);
}
}
return super.renderDrag( g, p1, p2 );
}
}
private MouseModule mouseModule=null;
private MouseModule getMouseModule() {
if ( mouseModule==null ) {
DasPlot parent= getParent();
mouseModule= new MouseModule( parent, new EventLabelDragRenderer(parent), "Event Lookup" );
}
return mouseModule;
}
/**
* canonical dataset containing rank2 bundle of start,stop,color,text.
*/
private QDataSet cds=null;
/**
* make the canonical dataset smaller by combining adjacent records. This is introduced
* because we now support event datasets with a regular cadence.
* @param vds
* @return
*/
private QDataSet coalesce( QDataSet vds ) {
QDataSet bds= (QDataSet) vds.property(QDataSet.BUNDLE_1);
DataSetBuilder build= new DataSetBuilder(2,100,4);
DDataSet v= DDataSet.createRank1(4);
QDataSet dep0= DataSetOps.unbundle( vds,0 );
double tlim= 1e-31;
RankZeroDataSet cad= org.das2.qds.DataSetUtil.guessCadenceNew( dep0, null );
if ( cad!=null ) {
tlim= cad.value()/100;
}
int count=0;
if ( vds.length()==0 ) {
return vds;
}
v.putValue( 0,vds.value(0,0) );
v.putValue( 1,vds.value(0,1) );
v.putValue( 2,vds.value(0,2) );
v.putValue( 3,vds.value(0,3) );
for ( int i=1; i tlim // they don't connect
|| vds.value(i,3)!=vds.value(i-1,3) // the message changed
|| Math.abs( vds.value(i,2)-vds.value(i-1,2) ) > 1e-31 // the color changed
) {
build.putValues( -1, v, 4 );
build.nextRecord();
v.putValue( 0,vds.value(i,0) );
v.putValue( 1,vds.value(i,1) );
v.putValue( 2,vds.value(i,2) );
v.putValue( 3,vds.value(i,3) );
count=1;
} else {
v.putValue( 1,vds.value(i,1) );
count++;
}
}
build.putValues( -1, v, 4 );
build.putProperty( QDataSet.BUNDLE_1, bds );
return build.getDataSet();
}
/**
* make canonical rank 2 bundle dataset of min,max,color,text
* @param vds events list in one of several supported forms
* @return rank 2 N by 4 dataset.
*/
private QDataSet makeCanonical( QDataSet vds ) {
logger.entering( "EventsRenderer", "makeCanonical" );
QDataSet xmins;
QDataSet xmaxs;
QDataSet colors;
QDataSet msgs;
if ( vds==null ) {
return null;
}
if ( vds.rank()==2 ) {
QDataSet dep0= (QDataSet) vds.property(QDataSet.DEPEND_0);
if ( dep0==null ) {
try {
xmins= DataSetOps.unbundle( vds,0 );
xmaxs= DataSetOps.unbundle( vds,1 );
} catch ( IndexOutOfBoundsException ex ) {
if ( vds.length()==0 ) {
//TODO: unbundle should be able to handle this.
logger.exiting( "EventsRenderer", "makeCanonical", "null");
return null;
} else {
throw ex;
}
}
if ( useColor ) {
colors= Ops.replicate( getColor().getRGB(), xmins.length() );
} else {
if ( vds.length(0)>3 ) {
colors= DataSetOps.unbundle( vds,2 );
} else {
colors= Ops.replicate( getColor().getRGB(), xmins.length() );
}
}
} else if ( dep0.rank()==2 ) {
if ( SemanticOps.isBins(dep0) ) {
xmins= DataSetOps.slice1( dep0, 0 );
xmaxs= DataSetOps.slice1( dep0, 1 );
colors= Ops.replicate( getColor().getRGB(), xmins.length() );
Units u0= SemanticOps.getUnits(xmins );
Units u1= SemanticOps.getUnits(xmaxs );
if ( !u1.isConvertibleTo(u0) && u1.isConvertibleTo(u0.getOffsetUnits()) ) {
xmaxs= Ops.add( xmins, xmaxs );
}
} else {
postMessage( "DEPEND_0 is rank 2 but not bins", DasPlot.WARNING, null, null );
logger.exiting( "EventsRenderer", "makeCanonical", "null" );
return null;
}
} else if ( dep0.rank() == 1 ) {
Datum width= SemanticOps.guessXTagWidth( dep0, null );
if ( width!=null ) {
width= width.divide(2);
} else {
QDataSet sort= Ops.sort(dep0);
QDataSet diffs= Ops.diff( DataSetOps.applyIndex(dep0,0,sort,false) );
QDataSet w= Ops.reduceMin( diffs,0 );
width= DataSetUtil.asDatum(w);
}
xmins= Ops.subtract(dep0, org.das2.qds.DataSetUtil.asDataSet(width) );
xmaxs= Ops.add(dep0, org.das2.qds.DataSetUtil.asDataSet(width) );
colors= Ops.replicate( getColor().getRGB(), xmins.length() );
} else {
postMessage( "rank 2 dataset must have dep0 of rank 1 or rank 2 bins", DasPlot.WARNING, null, null );
logger.exiting( "EventsRenderer", "makeCanonical", "null");
return null;
}
msgs= DataSetOps.unbundle( vds, vds.length(0)-1 );
} else if ( vds.rank()==1 ) {
QDataSet dep0= (QDataSet) vds.property(QDataSet.DEPEND_0);
if ( dep0==null ) {
if ( UnitsUtil.isNominalMeasurement(SemanticOps.getUnits(vds)) ) {
xmins= new TagGenDataSet(vds.length(),1.0,0.0);
xmaxs= new TagGenDataSet(vds.length(),1.0,1.0);
msgs= vds;
} else {
xmins= vds;
xmaxs= vds;
msgs= vds;
}
} else if ( dep0.rank() == 2 ) {
if ( SemanticOps.isBins(dep0) ) {
xmins= DataSetOps.slice1( dep0, 0 );
xmaxs= DataSetOps.slice1( dep0, 1 );
Units u0= SemanticOps.getUnits(xmins );
Units u1= SemanticOps.getUnits(xmaxs );
if ( !u1.isConvertibleTo(u0) && u1.isConvertibleTo(u0.getOffsetUnits()) ) {
xmaxs= Ops.add( xmins, xmaxs );
}
msgs= vds;
} else {
postMessage( "DEPEND_0 is rank 2 but not bins", DasPlot.WARNING, null, null );
logger.exiting( "EventsRenderer", "makeCanonical", "null");
return null;
}
} else if ( dep0.rank() == 1 ) {
Datum width= SemanticOps.guessXTagWidth( dep0, null );
if ( width!=null ) {
width= width.divide(2);
} else {
Units dep0units= SemanticOps.getUnits(dep0);
if ( UnitsUtil.isNominalMeasurement(dep0units) ) {
throw new IllegalArgumentException("dep0units are norminal units");
} else {
QDataSet sort= Ops.sort(dep0);
QDataSet diffs= Ops.diff( DataSetOps.applyIndex(dep0,0,sort,false) );
QDataSet w= Ops.reduceMin( diffs,0 );
width= DataSetUtil.asDatum(w);
}
}
xmins= Ops.subtract(dep0, org.das2.qds.DataSetUtil.asDataSet(width) );
xmaxs= Ops.add(dep0, org.das2.qds.DataSetUtil.asDataSet(width) );
msgs= vds;
} else {
postMessage( "dataset is not correct form", DasPlot.WARNING, null, null );
logger.exiting( "EventsRenderer", "makeCanonical", "null");
return null;
}
Color c0= getColor();
int alpha= c0.getAlpha()==255 ?
( opaque ? 255 : 128 ) :
c0.getAlpha();
Color c1= new Color( c0.getRed(), c0.getGreen(), c0.getBlue(), alpha );
int irgb= c1.getRGB();
colors= Ops.replicate( irgb, xmins.length() );
} else if ( vds.rank()==0 ) {
xmins= Ops.replicate(vds,1); // increase rank from 0 to 1.
xmaxs= xmins;
Color c0= getColor();
int alpha= c0.getAlpha()==255 ?
( opaque ? 255 : 128 ) :
c0.getAlpha();
Color c1= new Color( c0.getRed(), c0.getGreen(), c0.getBlue(), alpha );
int irgb= c1.getRGB();
colors= Ops.replicate( irgb, xmins.length() );
msgs= Ops.replicate(vds,1);
} else {
postMessage( "dataset must be rank 0, 1 or 2", DasPlot.WARNING, null, null );
logger.exiting( "EventsRenderer", "makeCanonical", "null");
return null;
}
if ( this.colorSpecifier!=null ) {
Units u= SemanticOps.getUnits(msgs);
WritableDataSet wds= IDataSet.copy(colors);
for ( int i=0; i0 && vds.length() == 0) ) {
DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("empty data set");
return;
}
Graphics2D g= ( Graphics2D ) g1;
g.setColor(color);
if ( cds==null ) {
// a message should be posted by makeCanonical
return;
}
String mode= this.mode.intern();
QDataSet cds1= cds;
if ( mode.equals("gantt2") ) {
QDataSet xmins= DataSetOps.unbundle( cds,0 );
QDataSet xmaxs= DataSetOps.unbundle( cds,1 );
QDataSet allBefore= Ops.lt( xmaxs, xAxis.getDatumRange().min() );
QDataSet allAfter= Ops.gt( xmins, xAxis.getDatumRange().max() );
QDataSet r= Ops.where( Ops.not( Ops.or( allBefore, allAfter ) ) );
cds1= Ops.applyIndex( cds, r );
ganttMode= true;
}
QDataSet xmins= DataSetOps.unbundle( cds1,0 );
QDataSet xmaxs= DataSetOps.unbundle( cds1,1 );
QDataSet msgs= DataSetOps.unbundle( cds1,3 );
Units eu= SemanticOps.getUnits( msgs );
QDataSet lcolor= DataSetOps.unbundle( cds1,2 );
long t0= System.currentTimeMillis();
DasPlot parent= getParent();
Rectangle current= null;
if ( lastException!=null ) {
renderException( g, xAxis, yAxis, lastException );
} else {
DasColumn column= xAxis.getColumn();
DasRow row= parent.getRow();
eventMap= new int[column.getWidth()];
for ( int k=0; k0 ) {
int ivds0= 0;
int ivds1= xmins.length();
Font f= getParent().getFont();
if ( getFontSize()!=null && getFontSize().length()>0 && !getFontSize().equals("1em") ) {
try {
double[] size= DasDevicePosition.parseLayoutStr(getFontSize());
double s= f.getSize2D() * size[0]/100 + f.getSize2D() * size[1] + size[2];
f= f.deriveFont((float)s);
} catch ( ParseException ex ) {
logger.log( Level.WARNING, ex.getMessage(), ex );
}
}
g1.setFont(f);
GrannyTextRenderer gtr= new GrannyTextRenderer();
int gymax,gymin;
QDataSet u;
Map map= new HashMap<>();
Map pam= new HashMap<>();
try {
QDataSet s= Ops.sort(msgs);
u= Ops.uniq(msgs,s);
gymax= u.length();
gymin= 0;
for ( int i=0; i0;
if ( drawLineThick ) {
double t= DasDevicePosition.parseLayoutStr( this.lineThick, f.getSize2D(), getParent().getWidth(), 1.0 );
g.setStroke( lineStyle.getStroke( (float)t ) );
}
for ( int i=ivds0; i renderTimeLimitMs ) {
parent.postMessage( this, "renderer ran out of time, dataset truncated", DasPlot.WARNING, null, null);
break;
}
if ( wxmins.value(i)==0.0 ) { // allow for fill in events dataset.
continue;
}
int ixmin= (int)xAxis.transform( xmins.value(i),xunits);
int ixmax= (int)xAxis.transform( xmaxs.value(i),xunits);
if ( ixmax<=-10000 ) {
continue;
}
if ( ixmin>=10000 ) {
continue;
}
int ixmin0= ixmin;
ixmin= Math.max( ixmin, imin );
ixmax= Math.min( ixmax, imax );
int iwidth= Math.max( ixmax- ixmin, 1 );
if ( lcolor!=null ) {
int irgb= (int)lcolor.value(i);
int rr= ( irgb & 0xFF0000 ) >> 16;
int gg= ( irgb & 0x00FF00 ) >> 8;
int bb= ( irgb & 0x0000FF );
int aa= ( irgb >> 24 & 0xFF );
if ( aa>0 ) {
g.setColor( new Color( rr, gg, bb, aa ) );
} else {
g.setColor( new Color( rr, gg, bb, 128 ) );
}
}
if ( column.getDMinimum() < ixmax && ixmin0 < column.getDMaximum() ) { // if any part is visible
if ( iwidth==0 ) iwidth=1;
Rectangle r1;
if ( this.orbitMode ) {
r1= new Rectangle( ixmin, row.getDMaximum()-textHeight, iwidth-1, textHeight );
g.fill( r1 );
} else if ( this.ganttMode ) {
int ord=(int)msgs.value(i);
int iy= map.get(ord);
int iymin= row.getDMinimum() + row.getHeight() * (iy) / ( gymax - gymin ) + 1;
int iymax= row.getDMinimum() + row.getHeight() * (1+iy) / ( gymax - gymin ) - 1;
//int iymin= row.getDMinimum() + row.getHeight() * ((int)msgs.value(i)-gymin) / ( gymax - gymin + 1 ) + 1;
//int iymax= row.getDMinimum() + row.getHeight() * (1+(int)msgs.value(i)-gymin) / ( gymax - gymin + 1 ) - 1;
r1= new Rectangle( ixmin, iymin, iwidth, Math.max( iymax-iymin, 2 ) );
g.fill( r1 );
} else {
if ( iwidth<=1 && drawLineThick ) {
r1= new Rectangle( ixmin, row.getDMinimum(), iwidth, row.getHeight() );
Line2D.Double l1= new Line2D.Double( ixmin, row.getDMinimum(), ixmin, row.getDMaximum() );
g.draw( l1 );
} else {
r1= new Rectangle( ixmin, row.getDMinimum(), iwidth, row.getHeight() );
g.fill( r1 );
if ( drawLineThick ) {
g.draw( r1 );
}
}
}
r1.x= r1.x-2;
r1.y= r1.y-2;
r1.width= r1.width+4;
r1.height= r1.height+4;
if ( current==null ) {
current= r1;
} else if ( current.intersects(r1) ) {
current= current.union(r1);
} else {
sa.append( current, false );
current= r1;
}
int im= ixmin-column.getDMinimum();
int em0= im-1;
int em1= im+iwidth+1;
for ( int k=em0; k=0 && k controls= new LinkedHashMap();
controls.put( "showLabels", encodeBooleanControl( isShowLabels() ) );
controls.put( "orbitMode", encodeBooleanControl( isOrbitMode() ) );
controls.put( Renderer.CONTROL_KEY_FONT_SIZE, getFontSize() );
controls.put( "ganttMode", encodeBooleanControl( isGanttMode() ) );
controls.put( Renderer.CONTROL_KEY_LINE_STYLE, getLineStyle().toString() );
controls.put("lineThick", getLineThick() );
controls.put("opaque", encodeBooleanControl( isOpaque() ) );
if ( this.useColor ) {
controls.put( Renderer.CONTROL_KEY_COLOR, encodeColorControl(color) );
}
return Renderer.formatControl(controls);
}
private boolean useColor= false;
private Color color= new Color(100,100,100);
public Color getColor() {
return color;
}
private PsymConnector lineStyle = PsymConnector.SOLID;
public static final String PROP_LINESTYLE = "lineStyle";
public PsymConnector getLineStyle() {
return lineStyle;
}
public void setLineStyle(PsymConnector lineStyle) {
PsymConnector oldLineStyle = this.lineStyle;
this.lineStyle = lineStyle;
if ( !oldLineStyle.equals(lineStyle) ) {
super.invalidateParentCacheImage();
}
propertyChangeSupport.firePropertyChange(PROP_LINESTYLE, oldLineStyle, lineStyle);
}
private String lineThick = "";
public static final String PROP_LINETHICK = "lineThick";
/**
* the line thickness, examples include "5pt" and "0.1em"
* @return
*/
public String getLineThick() {
return lineThick;
}
public void setLineThick(String lineThick) {
String oldLineThick = this.lineThick;
this.lineThick = lineThick;
if ( !oldLineThick.equals(lineThick) ) {
super.invalidateParentCacheImage();
}
propertyChangeSupport.firePropertyChange(PROP_LINETHICK, oldLineThick, lineThick);
}
private boolean opaque = false;
public static final String PROP_OPAQUE = "opaque";
public boolean isOpaque() {
return opaque;
}
public void setOpaque(boolean opaque) {
boolean oldOpaque = this.opaque;
this.opaque = opaque;
if ( oldOpaque!=opaque ) {
cds= makeCanonical(ds);
super.invalidateParentCacheImage();
}
propertyChangeSupport.firePropertyChange( PROP_OPAQUE, oldOpaque, opaque );
}
/**
* set the color to use when the data doesn't specify a color. If an alpha channel is specified, then
* this alpha value is used, otherwise 80% is used.
* @param color
*/
public void setColor( Color color ) {
Color old= this.color;
this.color= color;
if ( !old.equals(color) ) {
cds= makeCanonical(ds);
super.invalidateParentCacheImage();
}
propertyChangeSupport.firePropertyChange( PROP_COLOR, old, color);
}
public int getRenderTimeLimitMs() {
return renderTimeLimitMs;
}
public void setRenderTimeLimitMs(int renderTimeLimitMs) {
this.renderTimeLimitMs = renderTimeLimitMs;
}
/**
* true means draw the event label next to the bar.
*/
protected boolean showLabels = false;
public static final String PROP_SHOWLABELS = "showLabels";
public boolean isShowLabels() {
return showLabels;
}
public void setShowLabels(boolean showLabels) {
boolean oldShowLabels = this.showLabels;
this.showLabels = showLabels;
DasPlot parent= getParent();
if ( parent!=null ) {
parent.invalidateCacheImage();
parent.repaint();
}
propertyChangeSupport.firePropertyChange(PROP_SHOWLABELS, oldShowLabels, showLabels);
}
private String mode = "";
public static final String PROP_MODE = "mode";
public String getMode() {
return mode;
}
/**
* if non-empty, then use this named mode
* @param mode
*/
public void setMode(String mode) {
String oldMode = this.mode;
this.mode = mode;
propertyChangeSupport.firePropertyChange(PROP_MODE, oldMode, mode);
}
/**
* orbitMode true means don't show times, draw bars with 1-pixel breaks
*/
protected boolean orbitMode = false;
public static final String PROP_ORBITMODE = "orbitMode";
public boolean isOrbitMode() {
return orbitMode;
}
public void setOrbitMode(boolean orbitMode) {
boolean oldOrbitMode = this.orbitMode;
this.orbitMode = orbitMode;
propertyChangeSupport.firePropertyChange(PROP_ORBITMODE, oldOrbitMode, orbitMode);
}
/**
* gantt mode true means the event types are laid out vertically. The user
* has very little control over the position, but at least you can see
* common messages.
*/
private boolean ganttMode = false;
public static final String PROP_GANTTMODE = "ganttMode";
public boolean isGanttMode() {
return ganttMode;
}
public void setGanttMode(boolean ganttMode) {
boolean oldGanttMode = this.ganttMode;
this.ganttMode = ganttMode;
propertyChangeSupport.firePropertyChange(PROP_GANTTMODE, oldGanttMode, ganttMode);
}
private int rotateLabel = 0;
/**
* rotate the label counter clockwise to make more room in orbitMode.
*/
public static final String PROP_ROTATELABEL = "rotateLabel";
public int getRotateLabel() {
return rotateLabel;
}
public void setRotateLabel(int rotateLabel) {
int oldRotateLabel = this.rotateLabel;
this.rotateLabel = rotateLabel;
propertyChangeSupport.firePropertyChange(PROP_ROTATELABEL, oldRotateLabel, rotateLabel);
}
/**
* fontSize allows the font to be rescaled. 1em is the default size. 2em is twice the size. 12pt is 12 pixels.
*/
protected String fontSize = "1em";
public static final String PROP_FONTSIZE = "fontSize";
public String getFontSize() {
return fontSize;
}
public void setFontSize(String fontSize) {
String oldFontSize = this.fontSize;
this.fontSize = fontSize;
propertyChangeSupport.firePropertyChange(PROP_FONTSIZE, oldFontSize, fontSize);
}
public static final String PROP_COLOR_SPECIFIER= "colorSpecifier";
private ColorSpecifier colorSpecifier=null;
/**
* set this to be an object implementing ColorSpecifier interface, if more than
* one color is to be used when drawing the bars. Setting this to null will
* restore the initial behavior of drawing all bars in one color (or with rank 2 bundle containing color).
* @param spec the color specifier.
*/
public void setColorSpecifier( ColorSpecifier spec ) {
Object old= this.colorSpecifier;
this.colorSpecifier= spec;
cds= makeCanonical(ds);
propertyChangeSupport.firePropertyChange( PROP_COLOR_SPECIFIER, old , spec );
super.invalidateParentCacheImage();
}
public ColorSpecifier getColorSpecifier( ) {
return this.colorSpecifier;
}
/**
* Old TextSpecifier provided an alternate means to get text for any datum, allowing the user to avoid use of EnumerationUnits.
* This is currently not used in this version of the library.
*/
private TextSpecifier textSpecifier= DEFAULT_TEXT_SPECIFIER;
/**
* Getter for property textSpecifier.
* @return Value of property textSpecifier.
*/
public TextSpecifier getTextSpecifier() {
return this.textSpecifier;
}
/**
* Setter for property textSpecifier.
* @param textSpecifier New value of property textSpecifier.
*/
public void setTextSpecifier(TextSpecifier textSpecifier) {
TextSpecifier oldTextSpecifier = this.textSpecifier;
this.textSpecifier = textSpecifier;
propertyChangeSupport.firePropertyChange("textSpecifier", oldTextSpecifier, textSpecifier);
}
}