You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ctakes.apache.org by se...@apache.org on 2015/02/19 19:06:17 UTC
svn commit: r1660963 [15/19] - in /ctakes/sandbox/timelanes: META-INF/ edu/
edu/mayo/ edu/mayo/bmi/ edu/mayo/bmi/annotation/
edu/mayo/bmi/annotation/knowtator/ org/ org/chboston/ org/chboston/cnlp/
org/chboston/cnlp/anafora/ org/chboston/cnlp/anafora/a...
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/stack/TimelineStack.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/stack/TimelineStack.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/stack/TimelineStack.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/stack/TimelineStack.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,348 @@
+package org.chboston.cnlp.timeline.gui.timeline.stack;
+
+import org.chboston.cnlp.gui.VerticalMimicPanel;
+import org.chboston.cnlp.timeline.gui.timeline.JTimelineComponent;
+import org.chboston.cnlp.timeline.gui.timespan.selection.SelectionForwarder;
+import org.chboston.cnlp.timeline.gui.timespan.selection.TimeSpanSelectionModel;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/8/13
+ */
+public class TimelineStack extends Box {
+
+ static private final int COLLAPSED_HEIGHT = 25;
+
+ private boolean _collapsing;
+ private int _collapsingHeight;
+
+ private VerticalMimicPanel _titleStack;
+ private VerticalMimicPanel _removeButtonStack;
+ private boolean _collapsed = false;
+
+ static private final ImageIcon X_ICON = new ImageIcon( TimelineStack.class
+ .getResource( "icon/edit-delete-6_12.png" ) );
+
+ public TimelineStack() {
+ super( BoxLayout.Y_AXIS );
+ // JComponents don't like to be long ...
+ setMaximumSize( new Dimension( Integer.MAX_VALUE, Short.MAX_VALUE ) );
+ setBorder( new Border() {
+ @Override
+ public void paintBorder( final Component c,
+ final Graphics g,
+ final int x,
+ final int y,
+ final int width,
+ final int height ) {
+ Color oldColor = g.getColor();
+ g.setColor( Color.GRAY );
+ g.drawLine( x, y, x + width - 1, y );
+ g.setColor( Color.LIGHT_GRAY );
+ g.drawLine( x, y + 1, x + width - 1, y + 1 );
+ g.setColor( oldColor );
+ }
+
+ @Override
+ public Insets getBorderInsets( final Component c ) {
+ return new Insets( 2, 0, 0, 0 );
+ }
+
+ @Override
+ public boolean isBorderOpaque() {
+ return false;
+ }
+ } );
+ }
+
+
+ public void collapse( final boolean collapse ) {
+ if ( _collapsing ) {
+ return;
+ }
+ if ( !_collapsed && collapse ) {
+ final int high = getPreferredSize().height;
+ final int low = COLLAPSED_HEIGHT;
+ if ( high > low + 3 ) {
+ _collapsingHeight = high;
+ final int incr = -Math.min( Math.max( 2, (high - low) / 10 ), 10 );
+ new CollapseAction( high, low, incr ).start();
+ }
+ } else if ( _collapsed && !collapse ) {
+ final int high = super.getPreferredSize().height;
+ final int low = getPreferredSize().height;
+ if ( high > low + 3 ) {
+ _collapsingHeight = low;
+ final int incr = Math.min( Math.max( 2, (high - low) / 10 ), 10 );
+ new CollapseAction( high, low, incr ).start();
+ }
+ } else {
+ _collapsed = collapse;
+ revalidate();
+ }
+ _collapsed = collapse;
+ }
+
+ private class CollapseAction implements ActionListener {
+ final Timer __timer;
+ final private int __high;
+ final private int __low;
+ final private int __incr;
+
+ private CollapseAction( final int high, final int low, final int incr ) {
+ __high = high;
+ __low = low;
+ __incr = incr;
+ __timer = new Timer( 10, CollapseAction.this );
+ __timer.setRepeats( true );
+ }
+
+ private void start() {
+ _collapsing = true;
+ __timer.start();
+ }
+
+ public void actionPerformed( final ActionEvent event ) {
+ _collapsingHeight += __incr;
+ revalidate();
+ if ( _collapsingHeight <= __low || _collapsingHeight >= __high ) {
+ _collapsing = false;
+ __timer.stop();
+ }
+ }
+ }
+
+ public Dimension getPreferredSize() {
+ final Dimension size = super.getPreferredSize();
+ if ( _collapsing ) {
+ size.height = _collapsingHeight;
+ return size;
+ }
+ if ( !_collapsed ) {
+ return size;
+ }
+ size.height = Math.min( size.height, COLLAPSED_HEIGHT );
+ return size;
+ }
+
+ public VerticalMimicPanel getTitleStack() {
+ if ( _titleStack == null ) {
+ _titleStack = new VerticalMimicPanel( this );
+ }
+ return _titleStack;
+ }
+
+ public VerticalMimicPanel getRemoveButtonStack() {
+ if ( _removeButtonStack == null ) {
+ _removeButtonStack = new VerticalMimicPanel( this );
+ }
+ return _removeButtonStack;
+ }
+
+ public JTimelineComponent addTimeline( final Timeline timeline, final Timeline gridTimeline,
+ final TimeSpanSelectionModel selectionModel ) {
+ return addTimeline( timeline, gridTimeline, selectionModel, true );
+ }
+
+ public JTimelineComponent addTimeline( final Timeline timeline, final Timeline gridTimeline,
+ final TimeSpanSelectionModel selectionModel, final boolean removable ) {
+ if ( timeline == null ) {
+ return null;
+ }
+ return addTimeline( timeline.getTitle(), null, timeline, gridTimeline, selectionModel, removable );
+ }
+
+ public JTimelineComponent addTimeline( final Timeline timeline, final Color color, final Timeline gridTimeline,
+ final TimeSpanSelectionModel selectionModel, final boolean removable ) {
+ if ( timeline == null ) {
+ return null;
+ }
+ return addTimeline( timeline.getTitle(), color, timeline, gridTimeline, selectionModel, removable );
+ }
+
+
+ public JTimelineComponent addTimeline( final String title, final Color color, final Timeline timeline,
+ final Timeline gridTimeline,
+ final TimeSpanSelectionModel selectionModel ) {
+ return addTimeline( title, color, timeline, gridTimeline, selectionModel, true );
+ }
+
+ public JTimelineComponent addTimeline( final String title, final Color color, final Timeline timeline,
+ final Timeline gridTimeline,
+ final TimeSpanSelectionModel selectionModel, final boolean removable ) {
+ if ( timeline == null ) {
+ return null;
+ }
+ for ( Component comp : getComponents() ) {
+ if ( comp instanceof JTimelineComponent ) {
+ final String otherTitle = ((JTimelineComponent)comp).getModel().getTitle();
+ if ( otherTitle.equals( title ) ) {
+ // Already have the timeline ???
+ return (JTimelineComponent)comp;
+ }
+ }
+ }
+ final JTimelineComponent jTimeline = createTimelineComponent( timeline, gridTimeline, selectionModel );
+ add( jTimeline );
+ String htmlTitle = title;
+ if ( title.contains( "/" ) ) {
+ htmlTitle = "<html><p align=right>" + title.replace( "/", "<br>" ) + "</p></html>";
+ }
+ getTitleStack().addComponentFor( jTimeline, new WidthLabel( htmlTitle, color ) );
+ if ( removable ) {
+ final JButton removeButton = new JButton( new RemoveTimelineAction( title, timeline ) );
+ removeButton.setContentAreaFilled( false );
+ removeButton.setFocusPainted( false );
+ removeButton.setPreferredSize( new Dimension( 16, 12 ) );
+ removeButton.setBorder( new EmptyBorder( 0, 2, 0, 0 ) );
+ getRemoveButtonStack().addComponentFor( jTimeline, removeButton );
+ }
+ return jTimeline;
+ }
+
+ public void removeTimeline( final String title, final Timeline timeline ) {
+ if ( title.startsWith( "All " ) || getComponentCount() == 1 ) {
+ return;
+ }
+ for ( Component comp : getComponents() ) {
+ if ( comp instanceof JTimelineComponent ) {
+ final String otherTitle = ((JTimelineComponent)comp).getModel().getTitle();
+ if ( otherTitle.equals( title ) ) {
+ SelectionForwarder.getInstance().removeSelectionModel( ((JTimelineComponent)comp).getSelectionModel() );
+ getTitleStack().removeComponentFor( comp );
+ getRemoveButtonStack().removeComponentFor( comp );
+ remove( comp );
+ break;
+ }
+ }
+ }
+ revalidate();
+ }
+
+
+ public void clearTimelines() {
+ for ( Component comp : getComponents() ) {
+ if ( comp instanceof JTimelineComponent ) {
+// final String title = ((JTimelineComponent) comp).getModel().getTitle();
+// if ( title.startsWith( "All " ) ) {
+// continue;
+// }
+ SelectionForwarder.getInstance().removeSelectionModel( ((JTimelineComponent)comp).getSelectionModel() );
+ getTitleStack().removeComponentFor( comp );
+ getRemoveButtonStack().removeComponentFor( comp );
+ remove( comp );
+ }
+ }
+ revalidate();
+ }
+
+
+ static private JTimelineComponent createTimelineComponent( final Timeline timeline,
+ final Timeline gridTimeline,
+ final TimeSpanSelectionModel selectionModel ) {
+ final JTimelineComponent jTimeline = new JTimelineComponent( timeline, gridTimeline );
+ if ( selectionModel != null ) {
+ jTimeline.setSelectionModel( selectionModel );
+ }
+ jTimeline.setBorder( new Border() {
+ @Override
+ public void paintBorder( final Component c,
+ final Graphics g,
+ final int x,
+ final int y,
+ final int width,
+ final int height ) {
+ Color oldColor = g.getColor();
+ g.setColor( Color.GRAY );
+ g.drawLine( x, y + height - 1, x + width - 1, y + height - 1 );
+ g.setColor( oldColor );
+ }
+
+ @Override
+ public Insets getBorderInsets( final Component c ) {
+ return new Insets( 0, 0, 1, 0 );
+ }
+
+ @Override
+ public boolean isBorderOpaque() {
+ return false;
+ }
+ } );
+ return jTimeline;
+ }
+
+ private class RemoveTimelineAction extends AbstractAction {
+ final private Timeline __timeline;
+ final private String __title;
+
+ private RemoveTimelineAction( final String title, final Timeline timeline ) {
+ super( "", X_ICON );
+ putValue( SHORT_DESCRIPTION, title );
+ putValue( LONG_DESCRIPTION, title );
+ __timeline = timeline;
+ __title = title;
+ }
+
+ public void actionPerformed( final ActionEvent event ) {
+ removeTimeline( __title, __timeline );
+ }
+ }
+
+
+ static private class WidthLabel extends JLabel {
+ private WidthLabel( final String text, final Color color ) {
+ super( text );
+ setBorder( new EmptyBorder( 0, 0, 0, 5 ) );
+ setHorizontalAlignment( JLabel.RIGHT );
+ setVerticalAlignment( JLabel.CENTER );
+ setToolTipText( text );
+ final Font oldFont = getFont();
+ final Font newFont = oldFont.deriveFont( Font.BOLD );
+ setFont( newFont );
+ setHorizontalTextPosition( JLabel.LEFT );
+ addSquare( color );
+ // Doesn't work
+ // setMaximumSize( new Dimension( 120, Short.MAX_VALUE ) );
+ }
+
+ public Dimension getPreferredSize() {
+ final Dimension size = super.getPreferredSize();
+ size.width = Math.max( size.width, 40 );
+ size.width = Math.min( size.width, 120 );
+ return size;
+ }
+
+ private void addSquare( final Color color ) {
+ if ( color == null ) {
+ return;
+ }
+ final Icon icon = new Icon() {
+ public void paintIcon( Component c, Graphics g, int x, int y ) {
+ g.setColor( color );
+ g.fillRect( x, y, getIconWidth(), getIconHeight() );
+ }
+
+ public int getIconWidth() {
+ return 8;
+ }
+
+ public int getIconHeight() {
+ return 8;
+ }
+ };
+ setIcon( icon );
+ }
+
+ }
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/stack/icon/edit-delete-6_12.png
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/stack/icon/edit-delete-6_12.png?rev=1660963&view=auto
==============================================================================
Binary file - no diff available.
Propchange: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/stack/icon/edit-delete-6_12.png
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/AbstractTimelineUI.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/AbstractTimelineUI.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/AbstractTimelineUI.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/AbstractTimelineUI.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,199 @@
+package org.chboston.cnlp.timeline.gui.timeline.ui;
+
+import org.chboston.cnlp.timeline.gui.timeline.TimelineComponent;
+import org.chboston.cnlp.timeline.gui.timespan.TimeSpanDrawUtil;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+abstract public class AbstractTimelineUI extends TimelineUI {
+
+ protected CellRendererPane _rendererPane;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ abstract public Rectangle doLayout( final JComponent component );
+
+ abstract protected void resetLayout();
+
+ abstract protected int getY( final int x1, final int x2 );
+
+ abstract protected int getHeight( final int x1, final int x2 );
+
+ abstract protected Rectangle getCachedBounds();
+
+ abstract protected Rectangle getCachedTimeSpanBounds( final TimeSpan timeSpan );
+
+ abstract protected void paintComponent( final Graphics g, final JComponent component );
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void installUI( final JComponent component ) {
+ _rendererPane = new CellRendererPane();
+ component.add( _rendererPane );
+ // installDefaults();
+ // installDefaults2();
+ // installListeners();
+ // installKeyboardActions();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void uninstallUI( final JComponent component ) {
+ // uninstallDefaults();
+ // uninstallListeners();
+ // uninstallKeyboardActions();
+ if ( _rendererPane == null ) {
+ return;
+ }
+ component.remove( _rendererPane );
+ _rendererPane = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void paint( final Graphics g, final JComponent component ) {
+ final Rectangle bounds = component.getBounds();
+ final Rectangle layout = doLayout( component );
+ if ( bounds.equals( layout ) ) {
+ final Graphics myG = g.create();
+ paintComponent( myG, component );
+ myG.dispose();
+ } else {
+ component.setBounds( layout );
+ }
+ }
+
+ // KLUDGE to get sizing of OldJTimeline to work properly within a BoxLayout
+ // Add any parent panel with a BoxLayout to a Panel without a BoxLayout or OldJTimeline sizing will not work
+ private int getParentWidth( final Component child ) {
+ final Component parent = child.getParent();
+ if ( parent == null ) {
+ return child.getWidth();
+ }
+ if ( parent instanceof JViewport || parent instanceof JScrollPane
+ || parent instanceof CellRendererPane
+ || (parent instanceof JComponent && ((JComponent)parent).getLayout() instanceof BoxLayout) ) {
+ return getParentWidth( parent );
+ }
+ return parent.getWidth();
+ }
+
+ protected int getTotalWidth( final Component component ) {
+ int width = getParentWidth( component );
+ if ( width == 0 ) {
+ width = 1000;
+ }
+ return width;
+ }
+
+ protected Rectangle getTimeSpanBounds( final long beginDrawMillis, final double xform,
+ final TimeSpan timeSpan ) {
+ final Rectangle cachedBounds = getCachedTimeSpanBounds( timeSpan );
+ if ( cachedBounds != null ) {
+ return cachedBounds;
+ }
+ final long begin = timeSpan.getStartMillis();
+ final long end = timeSpan.getStopMillis();
+ double x1 = (begin - beginDrawMillis) / xform;
+ double x2 = (end - beginDrawMillis) / xform;
+ if ( x1 >= x2 ) {
+ x2 = x1 + 1;
+ }
+ x1 -= TimeSpanDrawUtil.getStartOffset( timeSpan );
+ x2 += TimeSpanDrawUtil.getEndOffset( timeSpan );
+ final int y = getY( (int)x1, (int)x2 );
+ int width = Math.max( (int)(x2 - x1), 1 );
+ final int height = getHeight( (int)x1, (int)x2 );
+ return new Rectangle( (int)x1, y, width, height );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Rectangle getTimeSpanBounds( final JComponent component, final TimeSpan timeSpan ) {
+ final Rectangle cachedBounds = getCachedTimeSpanBounds( timeSpan );
+ if ( cachedBounds != null ) {
+ return cachedBounds;
+ }
+ final int totalWidth = getTotalWidth( component );
+ final TimelineComponent timelineComponent = (TimelineComponent)component;
+ final long beginDrawMillis = timelineComponent.getBeginDrawMillis();
+ final long drawTimeDelta = timelineComponent.getDrawTimeDelta();
+ final double xform = drawTimeDelta / (double)totalWidth;
+ return getTimeSpanBounds( beginDrawMillis, xform, timeSpan );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TimeSpan getClosestTimeSpanForLocation( final JComponent component, final int x, final int y ) {
+ final int totalWidth = getTotalWidth( component );
+ final TimelineComponent timelineComponent = (TimelineComponent)component;
+ final long beginDrawMillis = timelineComponent.getBeginDrawMillis();
+ final long drawTimeDelta = timelineComponent.getDrawTimeDelta();
+ final double xform = drawTimeDelta / (double)totalWidth;
+ int smallestWidth = Integer.MAX_VALUE;
+ PointedTimeSpan bestTimeSpan = null;
+ final Timeline timeline = timelineComponent.getModel();
+ for ( PointedTimeSpan timeSpan : timeline ) {
+ final Rectangle bounds = getTimeSpanBounds( beginDrawMillis, xform, timeSpan );
+ if ( bounds.contains( x, y ) && bounds.width < smallestWidth ) {
+ smallestWidth = bounds.width;
+ bestTimeSpan = timeSpan;
+ }
+ }
+ return bestTimeSpan;
+ }
+
+ // TODO SIZES
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Dimension getPreferredSize( final JComponent component ) {
+ final Rectangle layout = doLayout( component );
+ return layout.getSize();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getBaseline( JComponent c, int width, int height ) {
+ super.getBaseline( c, width, height );
+ final Dimension size = getPreferredSize( c );
+ return size.height;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Component.BaselineResizeBehavior getBaselineResizeBehavior( JComponent c ) {
+ return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
+ }
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/BasicTimelineUI.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/BasicTimelineUI.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/BasicTimelineUI.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/BasicTimelineUI.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,296 @@
+package org.chboston.cnlp.timeline.gui.timeline.ui;
+
+import org.chboston.cnlp.timeline.gui.timeline.JTimelineComponent;
+import org.chboston.cnlp.timeline.gui.timespan.TimeSpanDrawUtil;
+import org.chboston.cnlp.timeline.gui.timespan.plus.TimeSpanRenderer;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timespan.DefaultTimeSpan;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.TimeSpanLengthComparator;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+public class BasicTimelineUI extends AbstractTimelineUI {
+
+
+ static private final Color DOC_TIME_COLOR = new Color( 0, 127, 127, 63 );
+
+ // For vertical layout
+ final private java.util.List<Set<Point>> _yEndpointsList = new ArrayList<>();
+
+ // Span Layout cache
+ final private Map<TimeSpan, Rectangle> _layoutCacheMap = new HashMap<>();
+
+ // Recalculating the bounds can be expensive when there are dozens of time lines on the screen
+ private Rectangle _cachedBounds = new Rectangle();
+
+ private boolean _drawGridOnly;
+ final private Collection<Integer> _gridLineXs = new HashSet<>();
+ final private Collection<Integer> _fuzzyGridLineXs = new HashSet<>();
+
+ private TimeSpan _referenceDay;
+
+ /**
+ * @param drawGridOnly true if the ui should only draw the grid of timespans
+ */
+ public void setDrawGridOnly( final boolean drawGridOnly ) {
+ _drawGridOnly = drawGridOnly;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Rectangle doLayout( final JComponent component ) {
+ final int totalWidth = getTotalWidth( component );
+ if ( _cachedBounds.x == 0 && _cachedBounds.width == totalWidth ) {
+ // DefaultTimeline has not been scrolled or resized horizontally, no need to recompute TimeSpan bounds
+ // DefaultTimeline may have moved up or down by resize of sibling
+ _cachedBounds.y = component.getY();
+ return _cachedBounds;
+ }
+ resetLayout();
+ final JTimelineComponent timelineComponent = (JTimelineComponent)component;
+ final long beginDrawMillis = timelineComponent.getBeginDrawMillis();
+ final long drawTimeDelta = timelineComponent.getDrawTimeDelta();
+ final double xform = drawTimeDelta / (double)totalWidth;
+ final Timeline timeline = timelineComponent.getModel();
+ final java.util.List<PointedTimeSpan> timeSpansByWidth = new ArrayList<>( timeline.getTimeSpans() );
+ Collections.sort( timeSpansByWidth, TimeSpanLengthComparator.getInstance() );
+ // Create the bounds for each TimeSpan
+ for ( PointedTimeSpan timeSpan : timeSpansByWidth ) {
+ final Rectangle bounds = getTimeSpanBounds( beginDrawMillis, xform, timeSpan );
+ _layoutCacheMap.put( timeSpan, bounds );
+ }
+ final long docTimeStart = timeline.getReferenceMillis();
+ final long docTimeStop = docTimeStart + 1000 * 60 * 60 * 24 - 1;
+ _referenceDay = new DefaultTimeSpan( docTimeStart, docTimeStop );
+ final int height = Math.max( TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT * 3,
+ _yEndpointsList.size() * TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT );
+ _cachedBounds = new Rectangle( 0, component.getY(), totalWidth, height );
+ final Timeline gridModel = timelineComponent.getGridModel();
+ for ( PointedTimeSpan timeSpan : gridModel ) {
+ addGridTimeSpanBounds( beginDrawMillis, xform, timeSpan );
+ }
+ return _cachedBounds;
+ }
+
+ protected void addGridTimeSpanBounds( final long beginDrawMillis, final double xform,
+ final TimeSpan timeSpan ) {
+ final long begin = timeSpan.getStartMillis();
+ final long end = timeSpan.getStopMillis();
+ double x1 = (begin - beginDrawMillis) / xform;
+ double x2 = (end - beginDrawMillis) / xform;
+// if ( x1 >= x2 ) {
+// x2 = x1 + 1;
+// }
+ if ( timeSpan.isFuzzyDate() && timeSpan instanceof PointedTimeSpan ) {
+ addGridX( (int)x1, ((PointedTimeSpan)timeSpan).getStartTime().isFuzzy() );
+ addGridX( (int)x2, ((PointedTimeSpan)timeSpan).getStopTime().isFuzzy() );
+ return;
+ }
+ addGridX( (int)x1, timeSpan.isFuzzyDate() );
+ addGridX( (int)x2, timeSpan.isFuzzyDate() );
+ }
+
+ protected void addGridX( final int x, final boolean isFuzzy ) {
+ if ( isFuzzy ) {
+ _fuzzyGridLineXs.add( x );
+ } else {
+ _gridLineXs.add( x );
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void resetLayout() {
+ _cachedBounds.width = 0;
+ _layoutCacheMap.clear();
+ _yEndpointsList.clear();
+ _gridLineXs.clear();
+ _fuzzyGridLineXs.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected int getY( final int x1, final int x2 ) {
+ // get Y for time span
+ final int size = _yEndpointsList.size();
+ int validLayer = 0;
+ for ( int i = 0; i < size; i++ ) {
+ final Set<Point> layerPoints = _yEndpointsList.get( i );
+ boolean overlap = false;
+ for ( Point p : layerPoints ) {
+ if ( (x1 >= p.x && x1 <= p.y) || (x2 >= p.x && x2 <= p.y)
+ || (p.x >= x1 && p.x <= x2) || (p.y >= x1 && p.y <= x2) ) {
+ // spans overlap
+ overlap = true;
+ break;
+ }
+ }
+ if ( !overlap ) {
+ break;
+ }
+ validLayer = i + 1;
+ }
+ // Pad the timespans with a minimum gap
+ final Point newPoint = new Point( x1 - TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT,
+ x2 + TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT );
+ if ( validLayer < size ) {
+ final Set<Point> layerPoints = _yEndpointsList.get( validLayer );
+ layerPoints.add( newPoint );
+ } else {
+ final Set<Point> newLayer = new HashSet<>( 1 );
+ newLayer.add( newPoint );
+ _yEndpointsList.add( newLayer );
+ }
+ return validLayer * TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected int getHeight( final int x1, final int x2 ) {
+ return TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Rectangle getCachedBounds() {
+ return _cachedBounds;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Rectangle getCachedTimeSpanBounds( final TimeSpan timeSpan ) {
+ return _layoutCacheMap.get( timeSpan );
+ }
+
+
+ private void paintDocTime( final Graphics2D g2d, final long beginDrawMillis, final double xform ) {
+ if ( _referenceDay == null ) {
+ return;
+ }
+ final long begin = _referenceDay.getStartMillis();
+ final long end = _referenceDay.getStopMillis();
+ double x1 = (begin - beginDrawMillis) / xform;
+ double x2 = (end - beginDrawMillis) / xform;
+ if ( x1 >= x2 ) {
+ x2 = x1 + 1;
+ }
+ x1 -= TimeSpanDrawUtil.getStartOffset( _referenceDay );
+ x2 += TimeSpanDrawUtil.getEndOffset( _referenceDay );
+ final double width = x2 - x1;
+ if ( width < 5 ) {
+ x1 -= (5 - width) / 2;
+ x2 = x1 + 5;
+ }
+ final Rectangle clipBounds = g2d.getClipBounds();
+ g2d.setColor( DOC_TIME_COLOR );
+ g2d.fillRect( (int)x1, clipBounds.y, (int)(x2 - x1), clipBounds.height );
+ }
+
+ private void paintGridLines( final Graphics2D g2d ) {
+ // Drawing directly is much faster than using a grid renderer
+ // Store unique grid X values, otherwise we may have redundant draws
+ if ( _gridLineXs.isEmpty() ) {
+ return;
+ }
+ final Rectangle clipBounds = g2d.getClipBounds();
+ final int y1 = clipBounds.y;
+ final int y2 = y1 + clipBounds.height;
+ g2d.setColor( Color.LIGHT_GRAY );
+ for ( Integer gridLineX : _gridLineXs ) {
+ final int x = gridLineX;
+ for ( int y = y1; y < y2 - 2; y += 4 ) {
+ g2d.drawLine( x, y, x, y + 2 );
+ }
+ }
+ }
+
+ private void paintFuzzyGridLines( final Graphics2D g2d ) {
+ // Drawing directly is much faster than using a grid renderer
+ // Store unique grid X values, otherwise we may have redundant draws
+ if ( _fuzzyGridLineXs.isEmpty() ) {
+ return;
+ }
+ final Rectangle clipBounds = g2d.getClipBounds();
+ final int y1 = clipBounds.y;
+ final int y2 = y1 + clipBounds.height;
+ g2d.setColor( Color.LIGHT_GRAY );
+ for ( Integer gridLineX : _fuzzyGridLineXs ) {
+ final int x = gridLineX;
+ for ( int y = y1; y < y2 - 2; y += 4 ) {
+ g2d.drawLine( x - 1, y, x, y + 1 );
+ g2d.drawLine( x + 1, y + 2, x, y + 3 );
+ }
+ }
+ }
+
+ private void paintTimeSpans( final JTimelineComponent timelineComponent,
+ final Graphics2D g2d, final Iterable<PointedTimeSpan> timeline,
+ final long beginDrawMillis, final double xform ) {
+ final Rectangle clipBounds = g2d.getClipBounds();
+ final Collection<PointedTimeSpan> selectedTimeSpans = timelineComponent.getSelectedTimeSpans();
+ for ( PointedTimeSpan timeSpan : timeline ) {
+ final Rectangle bounds = getTimeSpanBounds( beginDrawMillis, xform, timeSpan );
+ if ( bounds.width <= 0 || bounds.height <= 0 || !bounds.intersects( clipBounds ) ) {
+ continue;
+ }
+ // Renderer loses track of expanded toggle ??
+ final boolean expanded = false;
+ final TimeSpanRenderer renderer = timelineComponent.getTimeSpanRenderer( timeSpan.getClass() );
+ final boolean isSelected = selectedTimeSpans.contains( timeSpan );
+ final JComponent rendererComponent = renderer.getTimeSpanRendererComponent( timelineComponent, timeSpan,
+ isSelected, expanded, false );
+ if ( bounds.width < 5 ) {
+ bounds.x -= (5 - bounds.width) / 2;
+ bounds.width = 5;
+ }
+ _rendererPane.paintComponent( g2d, rendererComponent, timelineComponent,
+ bounds.x, bounds.y, bounds.width, bounds.height, true );
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void paintComponent( final Graphics g, final JComponent component ) {
+ final int totalWidth = _cachedBounds.width;
+ final JTimelineComponent timelineComponent = (JTimelineComponent)component;
+ final Timeline timeline = timelineComponent.getModel();
+ final long beginDrawMillis = timelineComponent.getBeginDrawMillis();
+ final long drawTimeDelta = timelineComponent.getDrawTimeDelta();
+ final double xform = drawTimeDelta / (double)totalWidth;
+ final Graphics2D g2d = (Graphics2D)g.create();
+ paintDocTime( g2d, beginDrawMillis, xform );
+ paintGridLines( g2d );
+ paintFuzzyGridLines( g2d );
+ if ( !_drawGridOnly ) {
+ paintTimeSpans( timelineComponent, g2d, timeline, beginDrawMillis, xform );
+ }
+ g2d.dispose();
+ // remove any renderer components that were left on the RendererPane
+ _rendererPane.removeAll();
+ }
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/TimelineUI.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/TimelineUI.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/TimelineUI.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timeline/ui/TimelineUI.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,25 @@
+package org.chboston.cnlp.timeline.gui.timeline.ui;
+
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+
+import javax.swing.*;
+import javax.swing.plaf.ComponentUI;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/30/12
+ */
+abstract public class TimelineUI extends ComponentUI {
+
+
+ public abstract Rectangle doLayout( final JComponent component );
+
+ public abstract Rectangle getTimeSpanBounds( final JComponent component, final TimeSpan timeSpan );
+
+ public abstract TimeSpan getClosestTimeSpanForLocation( final JComponent component,
+ final int x, final int y );
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanDrawUtil.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanDrawUtil.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanDrawUtil.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanDrawUtil.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,107 @@
+package org.chboston.cnlp.timeline.gui.timespan;
+
+import org.chboston.cnlp.timeline.gui.event.EventColor;
+import org.chboston.cnlp.timeline.timespan.EndPointer;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.TimeEndPoint;
+
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 11/9/12
+ */
+final public class TimeSpanDrawUtil {
+
+ static public final int LONG_X_OFFSET = 10;
+ static public final int SHORT_X_OFFSET = 5;
+
+ public static final int TIMESPAN_CELL_HEIGHT = 12;
+ static public final int LINE_THICKNESS = 5;
+ static public final int LINE_THINNESS = 3;
+
+ static public final int FULL_SPAN_HEIGHT = 8;
+ static public final int MIN_SPAN_HEIGHT = 3;
+ static public final int MIN_EVENT_HEIGHT = 2;
+
+
+ private TimeSpanDrawUtil() {
+ }
+
+ static private int getStartEndPointerOffset( final PointedTimeSpan timeSpan ) {
+ final TimeEndPoint startTime = timeSpan.getStartTime();
+ final EndPointer startPointer = startTime.getPointer();
+ switch ( startPointer ) {
+ case BEFORE:
+ return LONG_X_OFFSET;
+ case AFTER:
+ return 0;
+ case EQUAL:
+ return 0;
+ case OVERLAP:
+ return LONG_X_OFFSET;//SHORT_X_OFFSET;
+ }
+ return 0;
+ }
+
+ static private int getStopEndPointerOffset( final PointedTimeSpan timeSpan ) {
+ final TimeEndPoint stopTime = timeSpan.getStopTime();
+ final EndPointer stopPointer = stopTime.getPointer();
+ switch ( stopPointer ) {
+ case BEFORE:
+ return 0;
+ case AFTER:
+ return LONG_X_OFFSET;
+ case EQUAL:
+ return 0;
+ case OVERLAP:
+ return LONG_X_OFFSET;//SHORT_X_OFFSET;
+ }
+ return 0;
+ }
+
+ static public int getStartOffset( final TimeSpan timeSpan ) {
+ if ( timeSpan instanceof PointedTimeSpan ) {
+ return getStartEndPointerOffset( (PointedTimeSpan)timeSpan );
+ }
+ return 0;
+ }
+
+ static public int getEndOffset( final TimeSpan timeSpan ) {
+ if ( timeSpan instanceof PointedTimeSpan ) {
+ return getStopEndPointerOffset( (PointedTimeSpan)timeSpan );
+ }
+ return 0;
+ }
+
+ static public boolean isEndOnly( final TimeSpan timeSpan ) {
+// if ( timeSpan instanceof TimeSpanPlus ) {
+// return isEndOnly( ((TimeSpanPlus)timeSpan).getStartTime(), ((TimeSpanPlus)timeSpan).getStopTime() );
+// }
+ return false;
+ }
+
+
+// static private boolean isEndOnly( final TimeEndPoint startTimePoint, final TimeEndPoint stopTimePoint ) {
+// return ((startTimePoint.getPointer() == EndPointer.BEFORE &&
+// && stopTimePoint == TimeEndPoint.BEFORE)
+// || (stopTimePoint == TimeEndPoint.AFTER
+// && startTimePoint == TimeEndPoint.AFTER));
+// }
+
+ static public Color getPlainColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy ) {
+ return EventColor.getTimeSpanColor( isSelected, isFocused, isFuzzy ).getColor();
+ }
+
+ static public Color getPlainColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy,
+ final int width ) {
+ if ( width < SHORT_X_OFFSET ) {
+ return EventColor.getTimeSpanColor( isSelected, isFocused, false ).getColor();
+ }
+ return EventColor.getTimeSpanColor( isSelected, isFocused, isFuzzy ).getColor();
+ }
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanSelectionEvent.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanSelectionEvent.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanSelectionEvent.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanSelectionEvent.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,74 @@
+package org.chboston.cnlp.timeline.gui.timespan;
+
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+
+import java.util.Collection;
+import java.util.EventObject;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/20/12
+ */
+final public class TimeSpanSelectionEvent extends EventObject {
+ private final Set<TimeSpan> _selectionSet;
+ private final Set<String> _selectionTexts;
+ private final boolean _isAdjusting;
+
+ /**
+ * Represents a change in selection status between {@code firstIndex} and
+ * {@code lastIndex}, inclusive. {@code firstIndex} is less than or equal to
+ * {@code lastIndex}. The selection of at least one index within the range will
+ * have changed.
+ *
+ * @param selections all selections to which this event applies
+ * @param isAdjusting whether or not this is one in a series of
+ * multiple events, where changes are still being made
+ */
+ public TimeSpanSelectionEvent( final Object source, final Collection<TimeSpan> selections,
+ final Collection<String> selectionTexts, final boolean isAdjusting ) {
+ super( source );
+ _selectionSet = new HashSet<>( selections );
+ _selectionTexts = new HashSet<>( selectionTexts );
+ _isAdjusting = isAdjusting;
+ }
+
+ public Collection<TimeSpan> getSelectedTimeSpans() {
+ return _selectionSet;
+ }
+
+ public Collection<String> getSelectionTexts() {
+ return _selectionTexts;
+ }
+
+ /**
+ * Returns whether or not this is one in a series of multiple events,
+ * where changes are still being made. See the documentation for
+ * {@link javax.swing.ListSelectionModel#setValueIsAdjusting} for
+ * more details on how this is used.
+ *
+ * @return {@code true} if this is one in a series of multiple events,
+ * where changes are still being made
+ */
+ public boolean getValueIsAdjusting() {
+ return _isAdjusting;
+ }
+
+ /**
+ * Returns a {@code String} that displays and identifies this
+ * object's properties.
+ *
+ * @return a String representation of this object
+ */
+ public String toString() {
+ String properties =
+ " source=" + getSource() +
+ " selections= " + _selectionSet.toString() +
+ " isAdjusting= " + _isAdjusting +
+ " ";
+ return getClass().getName() + "[" + properties + "]";
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanSelectionListener.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanSelectionListener.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanSelectionListener.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/TimeSpanSelectionListener.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,18 @@
+package org.chboston.cnlp.timeline.gui.timespan;
+
+import java.util.EventListener;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/20/12
+ */
+public interface TimeSpanSelectionListener extends EventListener {
+ /**
+ * Called whenever the value of the selection changes.
+ *
+ * @param event the event that characterizes the change.
+ */
+ void valueChanged( final TimeSpanSelectionEvent event );
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/list/TimeSpanCellRenderer.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/list/TimeSpanCellRenderer.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/list/TimeSpanCellRenderer.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/list/TimeSpanCellRenderer.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,177 @@
+package org.chboston.cnlp.timeline.gui.timespan.list;
+
+
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.TimeSpanPlus;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+
+/**
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 10/2/2014
+ */
+public class TimeSpanCellRenderer extends JLabel {
+
+ public TimeSpanCellRenderer() {
+ setBorder( new EmptyBorder( 0, 0, 0, 0 ) );
+ setForeground( Color.BLACK );
+ setFont( getFont().deriveFont( Font.BOLD, getFont().getSize() + 2 ) );
+ setOpaque( true );
+ }
+
+ public void displayTimeSpan( final PointedTimeSpan timeSpan ) {
+ setBorder( new EmptyBorder( 12, 4, 4, 0 ) );
+ if ( timeSpan.equals( TimeSpanPlus.UNKNOWN_TIMESPAN_PLUS ) ) {
+ setText( "Unknown Time Span" );
+ setToolTipText( "Unknown Time Span" );
+ } else {
+ setText( timeSpan.toString() );
+ setToolTipText( timeSpan.toString() );
+ }
+ }
+
+ public void clearTimeSpan( final PointedTimeSpan timeSpan ) {
+ setBorder( new EmptyBorder( 0, 0, 0, 0 ) );
+ setText( "" );
+ if ( timeSpan.equals( TimeSpanPlus.UNKNOWN_TIMESPAN_PLUS ) ) {
+ setToolTipText( "Unknown Time Span" );
+ } else {
+ setToolTipText( timeSpan.toString() );
+ }
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void validate() {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ *
+ * @since 1.5
+ */
+ @Override
+ public void invalidate() {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ *
+ * @since 1.5
+ */
+ @Override
+ public void repaint() {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void revalidate() {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void repaint( long tm, int x, int y, int width, int height ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void repaint( Rectangle r ) {
+ }
+
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void firePropertyChange( String propertyName, byte oldValue, byte newValue ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void firePropertyChange( String propertyName, char oldValue, char newValue ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void firePropertyChange( String propertyName, short oldValue, short newValue ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void firePropertyChange( String propertyName, int oldValue, int newValue ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void firePropertyChange( String propertyName, long oldValue, long newValue ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void firePropertyChange( String propertyName, float oldValue, float newValue ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void firePropertyChange( String propertyName, double oldValue, double newValue ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ @Override
+ public void firePropertyChange( String propertyName, boolean oldValue, boolean newValue ) {
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/AbstractTimeSpanRenderer.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/AbstractTimeSpanRenderer.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/AbstractTimeSpanRenderer.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/AbstractTimeSpanRenderer.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,134 @@
+package org.chboston.cnlp.timeline.gui.timespan.plus;
+
+
+import org.chboston.cnlp.timeline.gui.timeline.TimelineComponent;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+abstract public class AbstractTimeSpanRenderer extends JComponent implements TimeSpanRenderer {
+
+ // TODO - new highlight coloring of currently selected list event(s)
+
+ private boolean _isSelected;
+ private boolean _focused;
+ private boolean _isExpanded;
+ private PointedTimeSpan _timeSpan;
+
+ public AbstractTimeSpanRenderer() {
+ super();
+ setOpaque( true );
+ }
+
+
+ @Override
+ public JComponent getTimeSpanRendererComponent( final TimelineComponent timelineComponent,
+ final PointedTimeSpan timeSpan,
+ boolean selected, boolean expanded,
+ boolean Focused ) {
+ _isSelected = selected;
+ _focused = Focused;
+ _isExpanded = expanded;
+ _timeSpan = timeSpan;
+ setToolTipText( timeSpan.toString() );
+ return this;
+ }
+
+ public boolean isFuzzy() {
+ return _timeSpan.isFuzzyDate();
+ }
+
+ public boolean isSelected() {
+ return _isSelected;
+ }
+
+ public boolean isFocused() {
+ return _focused;
+ }
+
+ public boolean isExpanded() {
+ return _isExpanded;
+ }
+
+ protected PointedTimeSpan getTimeSpan() {
+ return _timeSpan;
+ }
+
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ *
+ * @since 1.5
+ */
+ public void invalidate() {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ public void validate() {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ public void revalidate() {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ public void repaint( long tm, int x, int y, int width, int height ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ public void repaint( Rectangle r ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ *
+ * @since 1.5
+ */
+ public void repaint() {
+ }
+
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ protected void firePropertyChange( String propertyName, Object oldValue, Object newValue ) {
+ }
+
+ /**
+ * Overridden for performance reasons.
+ * See the <a href="#override">Implementation Note</a>
+ * for more information.
+ */
+ public void firePropertyChange( String propertyName, boolean oldValue, boolean newValue ) {
+ }
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultRelationRenderer.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultRelationRenderer.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultRelationRenderer.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultRelationRenderer.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,191 @@
+package org.chboston.cnlp.timeline.gui.timespan.plus;
+
+import org.chboston.cnlp.nlp.annotation.attribute.AttributeUtil;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.gui.event.EventColor;
+import org.chboston.cnlp.timeline.gui.timeline.TimelineComponent;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timespan.plus.TimeSpanPlus;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Collection;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+public class DefaultRelationRenderer extends DefaultTimeSpanRenderer {
+
+
+ static private final TimeSpanPainter UNCERTAIN_PAINTER = new UncertainTimeSpanPainter();
+ static private final TimeSpanPainter NEGATED_PAINTER = new NegatedTimeSpanPainter();
+
+
+ // static private boolean hasNegativePolarity( final Annotation event ) {
+// final Attribute polarity = event.getAttribute( DefinedAttributeType.POLARITY.getName() );
+// // Polarity is "POS" or "NEG" for THYME
+// return polarity != null && polarity.getValue().equalsIgnoreCase( "NEG" );
+// }
+//
+// static private boolean hasUncertainty( final Annotation event ) {
+// final Attribute modality = event.getAttribute( DefinedAttributeType.CONTEXT_MODALITY.getName() );
+// // Contextual Modality is "ACTUAL" "HYPOTHETICAL" "HEDGED" or "GENERIC" for THYME
+// return modality != null
+// && (modality.getValue().equalsIgnoreCase( "HYPOTHETICAL" )
+// || modality.getValue().equalsIgnoreCase( "HEDGED" ));
+// }
+//
+ private int _eventCount;
+ private int _negativeCount;
+ private int _uncertainCount;
+ private boolean _paintNegative;
+ private boolean _paintUncertain;
+
+
+ @Override
+ protected Color getPlainColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy ) {
+ return Color.RED;
+// if ( isSelected || (!_paintNegative && !_paintUncertain) ) {
+// return super.getPlainColor( isSelected, isFocused, isFuzzy );
+// }
+// final EventStatus eventStatus = EventStatus.getEventStatus( _paintNegative, _paintUncertain );
+// return EventColor.getEventColor( eventStatus, isFuzzy ).getColor();
+// if ( _paintNegative ) {
+// return isFuzzy ? EventColor.FUZZY_NEGATED.getColor() : EventColor.NEGATED.getColor();
+// } else if ( _paintUncertain ) {
+// return isFuzzy ? EventColor.FUZZY_UNCERTAIN.getColor() : EventColor.UNCERTAIN.getColor();
+// }
+// return super.getPlainColor( isSelected, isFocused, isFuzzy );
+ }
+
+ @Override
+ protected Color getPlainColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy,
+ final int width ) {
+ if ( isSelected || (!_paintNegative && !_paintUncertain) ) {
+ return super.getPlainColor( isSelected, isFocused, isFuzzy, width );
+ }
+ if ( _paintNegative ) {
+ return isFuzzy ? EventColor.FUZZY_NEGATED.getColor() : EventColor.NEGATED.getColor();
+ } else if ( _paintUncertain ) {
+ return isFuzzy ? EventColor.FUZZY_UNCERTAIN.getColor() : EventColor.UNCERTAIN.getColor();
+ }
+ return super.getPlainColor( isSelected, isFocused, isFuzzy, width );
+ }
+
+ @Override
+ protected TimeSpanPainter getPainter() {
+ if ( _paintNegative ) {
+ return NEGATED_PAINTER;
+ } else if ( _paintUncertain ) {
+ return UNCERTAIN_PAINTER;
+ }
+ return super.getPainter();
+ }
+
+
+ public JComponent getTimeSpanRendererComponent( final TimelineComponent timelineComponent,
+ final TimeSpanPlus timeSpan,
+ boolean selected, boolean expanded,
+ boolean Focused ) {
+ _eventCount = 0;
+ _negativeCount = 0;
+ _uncertainCount = 0;
+ final Timeline timeline = timelineComponent.getModel();
+ final Collection<Entity> entities = timeline.getEntities( timeSpan );
+ for ( Entity entity : entities ) {
+ if ( AttributeUtil.hasNegativePolarity( entity ) ) {
+ _negativeCount++;
+ }
+ if ( AttributeUtil.hasUncertainty( entity ) ) {
+ _uncertainCount++;
+ }
+ _eventCount++;
+ }
+ super.getTimeSpanRendererComponent( timelineComponent, timeSpan, selected, expanded, Focused );
+ return this;
+ }
+
+
+ @Override
+ public void paint( final Graphics g ) {
+ _paintNegative = false;
+ _paintUncertain = false;
+ if ( isSelected() || (_negativeCount == 0 && _uncertainCount == 0) ) {
+ super.paint( g );
+ return;
+ }
+ if ( _negativeCount == _eventCount ) {
+ _paintNegative = true;
+ super.paint( g );
+ return;
+ } else if ( _uncertainCount == _eventCount ) {
+ _paintUncertain = true;
+ super.paint( g );
+ return;
+ }
+ if ( _negativeCount > 0 && _uncertainCount > 0 && _negativeCount + _uncertainCount < _eventCount ) {
+ final int thirdHeight = getHeight() / 3;
+ final int bottomHeight = getHeight() - thirdHeight - 1;
+ int rightX = getWidth();
+ final Rectangle clipBounds = g.getClipBounds();
+ _paintNegative = true;
+ final Rectangle topClip = new Rectangle( 0, 0, rightX, thirdHeight + 1 );
+ Rectangle clip = topClip.intersection( clipBounds );
+ g.setClip( clip );
+ super.paint( g );
+ _paintNegative = false;
+ final Rectangle bottomClip = new Rectangle( 0, bottomHeight, rightX, thirdHeight + 1 );
+ clip = bottomClip.intersection( clipBounds );
+ g.setClip( clip );
+ super.paint( g );
+ _paintUncertain = true;
+ final Rectangle centerClip = new Rectangle( 0, thirdHeight + 1, rightX, thirdHeight - 2 );
+ clip = centerClip.intersection( clipBounds );
+ g.setClip( clip );
+ super.paint( g );
+ return;
+ }
+ final int halfHeight = getHeight() / 2 - 1;
+ int rightX = getWidth();
+ final Rectangle clipBounds = g.getClipBounds();
+ final Rectangle topClip = new Rectangle( 0, 0, rightX, halfHeight + 1 );
+ Rectangle clip = topClip.intersection( clipBounds );
+ _paintNegative = (_negativeCount > 0);
+ g.setClip( clip );
+ super.paint( g );
+ _paintNegative = false;
+ _paintUncertain = (_uncertainCount > 0);
+ final Rectangle bottomClip = new Rectangle( 0, halfHeight + 1, rightX, halfHeight );
+ clip = bottomClip.intersection( clipBounds );
+ g.setClip( clip );
+ super.paint( g );
+ }
+
+ static final private class UncertainTimeSpanPainter implements TimeSpanPainter {
+ @Override
+ public void paint( final Graphics2D g2d, final int x, final int y, final int width, final int height ) {
+ for ( int y1 = y; y1 < y + height - 2; y1++ ) {
+ for ( int x1 = x; x1 < x + width; x1 += 4 ) {
+ g2d.drawLine( x1, y1, x1 + 1, y1 + 1 );
+ }
+ for ( int x2 = x + 2; x2 < x + width; x2 += 4 ) {
+ g2d.drawLine( x2, y1 + 2, x2 + 1, y1 - 1 );
+ }
+ }
+ }
+ }
+
+
+ static final private class NegatedTimeSpanPainter implements TimeSpanPainter {
+ @Override
+ public void paint( final Graphics2D g2d, final int x, final int y, final int width, final int height ) {
+ for ( int x1 = x; x1 < x + width - 2; x1 += 4 ) {
+ g2d.fillRect( x1, y, 2, height );
+ }
+ }
+ }
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultRelationRenderer2.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultRelationRenderer2.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultRelationRenderer2.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/timespan/plus/DefaultRelationRenderer2.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,501 @@
+package org.chboston.cnlp.timeline.gui.timespan.plus;
+
+import org.chboston.cnlp.nlp.annotation.attribute.AttributeUtil;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.gui.event.EventColor;
+import org.chboston.cnlp.timeline.gui.event.EventStatus;
+import org.chboston.cnlp.timeline.gui.timeline.TimelineComponent;
+import org.chboston.cnlp.timeline.gui.timespan.TimeSpanDrawUtil;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timespan.EndPointer;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.TimeEndPoint;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Collection;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+final public class DefaultRelationRenderer2 extends AbstractTimeSpanRenderer {
+
+
+ static private final TimeSpanPainter DEFAULT_PAINTER = new DefaultTimeSpanPainter();
+ static private final TimeSpanPainter UNCERTAIN_PAINTER = new UncertainTimeSpanPainter();
+ static private final TimeSpanPainter NEGATED_PAINTER = new NegatedTimeSpanPainter();
+
+
+ private int _eventCount;
+ private int _negativeCount;
+ private int _uncertainCount;
+
+
+ static private Color getSelectedColor( final boolean isFocused, final boolean isFuzzy ) {
+ return EventColor.getTimeSpanColor( true, isFocused, isFuzzy ).getColor();
+ }
+
+ static private Color getUncertainColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy ) {
+ return EventColor.getEventColor( EventStatus.UNCERTAIN, isFuzzy ).getColor();
+ }
+
+ static private Color getNegatedColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy ) {
+ return EventColor.getEventColor( EventStatus.NEGATED, isFuzzy ).getColor();
+ }
+
+ static private Color getAffirmedColor( final boolean isSelected, final boolean isFocused, final boolean isFuzzy ) {
+ return EventColor.getEventColor( EventStatus.NORMAL, isFuzzy ).getColor();
+ }
+
+ @Override
+ public JComponent getTimeSpanRendererComponent( final TimelineComponent timelineComponent,
+ final PointedTimeSpan timeSpan,
+ boolean selected, boolean expanded,
+ boolean Focused ) {
+ _eventCount = 0;
+ _negativeCount = 0;
+ _uncertainCount = 0;
+ final Timeline timeline = timelineComponent.getModel();
+ final Collection<Entity> entities = timeline.getEntities( timeSpan );
+ for ( Entity entity : entities ) {
+ if ( AttributeUtil.hasNegativePolarity( entity ) ) {
+ _negativeCount++;
+ } else if ( AttributeUtil.hasUncertainty( entity ) ) {
+ // Don't want to register a single event as both negated and uncertain - negated overrides
+ _uncertainCount++;
+ }
+ _eventCount++;
+ }
+ super.getTimeSpanRendererComponent( timelineComponent, timeSpan, selected, expanded, Focused );
+ return this;
+ }
+
+ static final private class DrawHeights {
+ private int __marginHeight;
+ private int __affirmedHeight;
+ private int __uncertainHeight;
+ private int __negatedHeight;
+
+ private DrawHeights( final int minHeight, final int maxHeight,
+ final int eventCount, final int uncertainCount, final int negatedCount ) {
+ final int lineHeight = Math.min( Math.max( minHeight, eventCount ), maxHeight );
+ if ( uncertainCount > 0 ) {
+ if ( uncertainCount == eventCount ) {
+ __uncertainHeight = lineHeight;
+ } else {
+ final double uncertain = uncertainCount / (double)eventCount;
+ __uncertainHeight = Math.max( TimeSpanDrawUtil.MIN_EVENT_HEIGHT,
+ (int)Math.floor( uncertain * lineHeight ) );
+ }
+ }
+ if ( negatedCount > 0 ) {
+ if ( negatedCount == eventCount ) {
+ __negatedHeight = lineHeight;
+ } else {
+ final double negative = negatedCount / (double)eventCount;
+ __negatedHeight = Math.max( TimeSpanDrawUtil.MIN_EVENT_HEIGHT,
+ (int)Math.floor( negative * lineHeight ) );
+ }
+ }
+ if ( eventCount == uncertainCount + negatedCount ) {
+ __affirmedHeight = 0;
+ } else {
+ __affirmedHeight = Math.max( TimeSpanDrawUtil.MIN_EVENT_HEIGHT,
+ lineHeight - __uncertainHeight - __negatedHeight );
+ }
+ __marginHeight = (TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT
+ - __affirmedHeight - __uncertainHeight - __negatedHeight) / 2;
+ }
+ }
+
+
+ @Override
+ public void paint( final Graphics g ) {
+ final DrawHeights heights = new DrawHeights( TimeSpanDrawUtil.MIN_SPAN_HEIGHT, TimeSpanDrawUtil.FULL_SPAN_HEIGHT,
+ _eventCount, _uncertainCount, _negativeCount );
+ int y = 0;
+ int width = getWidth();
+ final PointedTimeSpan timeSpan = getTimeSpan();
+ final Graphics2D g2d = (Graphics2D)g.create();
+ if ( isSelected() ) {
+ final Color centerColor = getSelectedColor( false, false );
+ final Color endColor = getSelectedColor( false, true );
+// final Rectangle marginBounds = new Rectangle( 0, heights.__marginHeight-1, width, 1 );
+ final Rectangle marginBounds = new Rectangle( 0, heights.__marginHeight - 2, width, 2 );
+ paintTimeSpanPlusLine( g2d, marginBounds, centerColor, endColor, timeSpan, DEFAULT_PAINTER );
+ }
+ paintRelativeStart( g2d, timeSpan );
+ paintRelativeStop( g2d, timeSpan );
+ y += heights.__marginHeight;
+ if ( heights.__affirmedHeight > 0 ) {
+ final Color centerColor = getAffirmedColor( isSelected(), false, false );
+ final Color endColor = getAffirmedColor( isSelected(), false, true );
+ final Rectangle affirmedBounds = new Rectangle( 0, y, width, heights.__affirmedHeight );
+ paintTimeSpanPlusLine( g2d, affirmedBounds, centerColor, endColor, timeSpan, DEFAULT_PAINTER );
+ y += heights.__affirmedHeight;
+ }
+ // Paint negated before uncertain so that uncertain "wave" is not covered
+ if ( heights.__negatedHeight > 0 ) {
+ y += heights.__uncertainHeight;
+ final Color centerColor = getNegatedColor( isSelected(), false, false );
+ final Color endColor = getNegatedColor( isSelected(), false, true );
+ final Rectangle negatedBounds = new Rectangle( 0, y, width, heights.__negatedHeight );
+ paintTimeSpanPlusLine( g2d, negatedBounds, centerColor, endColor, timeSpan, NEGATED_PAINTER );
+ y -= heights.__uncertainHeight;
+ }
+ if ( heights.__uncertainHeight > 0 ) {
+ final Color centerColor = getUncertainColor( isSelected(), false, false );
+ final Color endColor = getUncertainColor( isSelected(), false, true );
+ final Rectangle uncertainBounds = new Rectangle( 0, y, width, heights.__uncertainHeight );
+ paintTimeSpanPlusLine( g2d, uncertainBounds, centerColor, endColor, timeSpan, UNCERTAIN_PAINTER );
+ y += heights.__uncertainHeight;
+ }
+// if ( heights.__negatedHeight > 0 ) {
+// final Color centerColor = getNegatedColor( isSelected(), false, false );
+// final Color endColor = getNegatedColor( isSelected(), false, true );
+// final Rectangle negatedBounds = new Rectangle( 0, y, width, heights.__negatedHeight );
+// paintTimeSpanPlusLine( g2d, negatedBounds, centerColor, endColor, timeSpan, NEGATED_PAINTER );
+ y += heights.__negatedHeight;
+// }
+ if ( isSelected() ) {
+ final Color centerColor = getSelectedColor( false, false );
+ final Color endColor = getSelectedColor( false, true );
+// final Rectangle marginBounds = new Rectangle( 0, y, width, 1 );
+ final Rectangle marginBounds = new Rectangle( 0, y, width, 2 );
+ paintTimeSpanPlusLine( g2d, marginBounds, centerColor, endColor, timeSpan, DEFAULT_PAINTER );
+ }
+ g2d.dispose();
+ }
+
+
+ // TODO -- Ends on, etc.
+
+ private void paintRelativeStart( final Graphics2D g2d, final PointedTimeSpan timeSpan ) {
+ final int startOffset = TimeSpanDrawUtil.getStartOffset( timeSpan );
+ final TimeEndPoint startTime = timeSpan.getStartTime();
+ switch ( startTime.getPointer() ) {
+ case BEFORE:
+// if ( timeSpan.isSingleDate()
+// && timeSpan.getStopTime().getPointer() == EndPointer.EQUAL ) {
+// // ~ tlink Ends-On
+// paintEdgeLine( g2d, 0, startOffset, startTime.isFuzzy() );
+// return;
+// }
+ paintSolidTriangle( g2d, 0, startOffset, startTime.isFuzzy() );
+ break;
+ case AFTER:
+ if ( timeSpan.isSingleDate() ) {
+ // ~ tlink After
+ return;
+ }
+ // ~ tlink Contains
+ paintEdgeLine( g2d, 0, startOffset + TimeSpanDrawUtil.LINE_THINNESS, startTime.isFuzzy() );
+ break;
+ case EQUAL:
+// if ( !timeSpan.isSingleDate()
+// || timeSpan.getStopTime().getPointer() == EndPointer.AFTER ) {
+ if ( !timeSpan.isSingleDate() ) {
+ // Equal Start OR ~ tlink Begins-On
+ paintEdgeLine( g2d, 0, startOffset, startTime.isFuzzy() );
+ }
+ break;
+ case OVERLAP:
+ paintContinuation( g2d, 0, startOffset, startTime.isFuzzy() );
+ break;
+ }
+ }
+
+ private void paintRelativeStop( final Graphics2D g2d, final PointedTimeSpan timeSpan ) {
+ final int endOffset = TimeSpanDrawUtil.getEndOffset( timeSpan );
+ final TimeEndPoint timeEndPoint = timeSpan.getStopTime();
+ final int width = getWidth();
+ final int leftX = width - endOffset;
+ switch ( timeEndPoint.getPointer() ) {
+ case BEFORE:
+ if ( timeSpan.isSingleDate() ) {
+ // ~ tlink Before
+ return;
+ }
+ // ~ tlink Contains
+ paintEdgeLine( g2d, width - TimeSpanDrawUtil.LINE_THINNESS, width, timeEndPoint.isFuzzy() );
+ break;
+ case AFTER:
+// if ( timeSpan.isSingleDate()
+// && timeSpan.getStartTime().getPointer() == EndPointer.EQUAL ) {
+// // ~ tlink Ends-On
+// paintEdgeLine( g2d, width - TimeSpanDrawUtil.LINE_THINNESS, width, timeEndPoint.isFuzzy() );
+// return;
+// }
+ paintSolidTriangle( g2d, width, leftX, timeEndPoint.isFuzzy() );
+ break;
+ case EQUAL:
+// if ( !timeSpan.isSingleDate()
+// || timeSpan.getStartTime().getPointer() == EndPointer.BEFORE ) {
+ if ( !timeSpan.isSingleDate() ) {
+ // Equal Stop OR ~ tlink Ends-On
+ paintEdgeLine( g2d, width - TimeSpanDrawUtil.LINE_THINNESS, width, timeEndPoint.isFuzzy() );
+ }
+ break;
+ case OVERLAP:
+ paintContinuation( g2d, leftX, width, timeEndPoint.isFuzzy() );
+ break;
+ }
+ }
+
+
+ // TODO This would be faster as an image
+ private void paintSolidTriangle( final Graphics2D g2d, final int pointX, final int nockX, final int height ) {
+ final int halfHeight = height / 2 - 1;
+ final int[] xPoints = { pointX, nockX, nockX, pointX, pointX };
+ final int[] yPoints = { halfHeight - 1, 0, height - 1, halfHeight, halfHeight - 1 };
+ g2d.fillPolygon( xPoints, yPoints, xPoints.length );
+ }
+
+ // TODO This would be faster as an image
+ private void paintBorderTriangle( final Graphics2D g2d, final int pointX, final int nockX, final int height ) {
+ final int halfHeight = height / 2 - 1;
+ g2d.drawLine( pointX, halfHeight - 1, nockX, 0 );
+ g2d.drawLine( pointX, halfHeight, nockX, height - 1 );
+ g2d.drawLine( pointX, halfHeight - 1, nockX, 1 );
+ g2d.drawLine( pointX, halfHeight, nockX, height - 2 );
+ }
+
+ // TODO This might be faster as an image
+ private void paintSolidTriangle( final Graphics2D g2d, final int pointX, final int nockX, final boolean isFuzzy ) {
+ final DrawHeights heights = new DrawHeights( TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT - 1,
+ TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT - 1,
+ _eventCount, _uncertainCount, _negativeCount );
+ final int x = Math.min( pointX, nockX );
+ final int width = Math.max( pointX, nockX ) - x;
+ final int height = getHeight();
+ int y = 0;
+ final Rectangle clipBounds = g2d.getClipBounds();
+ if ( heights.__affirmedHeight > 0 ) {
+ final Color color = getAffirmedColor( isSelected(), false, isFuzzy );
+ g2d.setColor( color );
+ final Rectangle affirmedBounds = new Rectangle( x, y, width, heights.__affirmedHeight );
+ Rectangle clip = affirmedBounds.intersection( clipBounds );
+ g2d.setClip( clip );
+ paintSolidTriangle( g2d, pointX, nockX, height );
+ y += heights.__affirmedHeight;
+ }
+ if ( heights.__uncertainHeight > 0 ) {
+ final Color color = getUncertainColor( isSelected(), false, isFuzzy );
+ g2d.setColor( color );
+ final Rectangle uncertainBounds = new Rectangle( x, y, width, heights.__uncertainHeight );
+ Rectangle clip = uncertainBounds.intersection( clipBounds );
+ g2d.setClip( clip );
+ paintSolidTriangle( g2d, pointX, nockX, height );
+ y += heights.__uncertainHeight;
+ }
+ if ( heights.__negatedHeight > 0 ) {
+ final Color color = getNegatedColor( isSelected(), false, isFuzzy );
+ g2d.setColor( color );
+ final Rectangle negatedBounds = new Rectangle( x, y, width, heights.__negatedHeight );
+ Rectangle clip = negatedBounds.intersection( clipBounds );
+ g2d.setClip( clip );
+ paintSolidTriangle( g2d, pointX, nockX, height );
+ }
+ g2d.setClip( clipBounds );
+ if ( isSelected() ) {
+ final Color color = getSelectedColor( false, isFuzzy );
+ g2d.setColor( color );
+ paintBorderTriangle( g2d, pointX, nockX, height );
+ }
+ }
+
+
+ private void paintEdgeLine( final Graphics2D g2d, final int leftX, final int rightX, final boolean isFuzzy ) {
+ final DrawHeights heights = new DrawHeights( TimeSpanDrawUtil.MIN_SPAN_HEIGHT, TimeSpanDrawUtil.FULL_SPAN_HEIGHT,
+ _eventCount, _uncertainCount, _negativeCount );
+ int y = 0;
+ final int width = Math.abs( rightX - leftX );
+ boolean marginAdded = false;
+ if ( heights.__affirmedHeight > 0 ) {
+ final Color color = getAffirmedColor( isSelected(), false, isFuzzy );
+ final int height = heights.__marginHeight + heights.__affirmedHeight;
+ g2d.setColor( color );
+ g2d.fillRect( leftX, y, width, height );
+ marginAdded = true;
+ y += height;
+ }
+ if ( heights.__uncertainHeight > 0 ) {
+ final Color color = getUncertainColor( isSelected(), false, isFuzzy );
+ final int height = (marginAdded) ? heights.__uncertainHeight
+ : heights.__marginHeight + heights.__uncertainHeight;
+ g2d.setColor( color );
+ g2d.fillRect( leftX, y, width, height );
+ marginAdded = true;
+ y += height;
+ }
+ if ( heights.__negatedHeight > 0 ) {
+ final Color color = getNegatedColor( isSelected(), false, isFuzzy );
+ final int height = (marginAdded) ? heights.__negatedHeight
+ : heights.__marginHeight + heights.__negatedHeight;
+ g2d.setColor( color );
+ g2d.fillRect( leftX, y, width, height );
+ y += height;
+ }
+ g2d.fillRect( leftX, y, width, heights.__marginHeight );
+ if ( isSelected() ) {
+ final Color color = getSelectedColor( false, isFuzzy );
+ g2d.setColor( color );
+// g2d.drawLine( leftX, 0, rightX, 0 );
+// g2d.drawLine( leftX, TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT-1, rightX, TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT-1 );
+ g2d.fillRect( leftX, 0, rightX - leftX, 2 );
+ g2d.fillRect( leftX, TimeSpanDrawUtil.TIMESPAN_CELL_HEIGHT - 2, rightX - leftX, 2 );
+ }
+ }
+
+
+ private void paintTimeSpanPlusLine( final Graphics2D g2d, final Rectangle bounds,
+ final Color centerColor, final Color endColor, final PointedTimeSpan timeSpan,
+ final TimeSpanPainter painter ) {
+ final TimeEndPoint startTime = timeSpan.getStartTime();
+ final boolean isStartFuzzy = startTime.isFuzzy();
+ final EndPointer startPointer = startTime.getPointer();
+ final TimeEndPoint stopTime = timeSpan.getStopTime();
+ final boolean isStopFuzzy = stopTime.isFuzzy();
+ final EndPointer stopPointer = stopTime.getPointer();
+
+ final int relationStartWidth = TimeSpanDrawUtil.getStartOffset( timeSpan )
+ + (startPointer == EndPointer.AFTER ? TimeSpanDrawUtil.LINE_THINNESS : 0);
+ final int relationStopWidth = TimeSpanDrawUtil.getEndOffset( timeSpan )
+ + (stopPointer == EndPointer.BEFORE ? TimeSpanDrawUtil.LINE_THINNESS : 0);
+ final int lineStartX = bounds.x + relationStartWidth;
+ final int lineWidth = bounds.width - relationStartWidth - relationStopWidth;
+
+ final Rectangle lineBounds = new Rectangle( lineStartX, bounds.y, lineWidth, bounds.height );
+ paintTimeSpanLine( g2d, lineBounds, centerColor, endColor, isStartFuzzy, isStopFuzzy, painter );
+ }
+
+ private void paintTimeSpanLine( final Graphics2D g2d, final Rectangle bounds,
+ final Color centerColor, final Color endColor,
+ final boolean isStartFuzzy, final boolean isStopFuzzy,
+ final TimeSpanPainter painter ) {
+ final int fuzzyStartWidth = isStartFuzzy ? (int)(bounds.width / 4.d) : 0;
+ final int fuzzyStopWidth = isStopFuzzy ? (int)(bounds.width / 4.d) : 0;
+ final int centerStartX = bounds.x + fuzzyStartWidth;
+ final int centerWidth = bounds.width - fuzzyStartWidth - fuzzyStopWidth;
+ // paint the center
+ g2d.setPaint( centerColor );
+// g2d.fillRect( centerStartX, bounds.y, centerWidth, bounds.height );
+ painter.paint( g2d, centerStartX, bounds.y, centerWidth, bounds.height );
+ if ( !isStartFuzzy && !isStopFuzzy ) {
+ return;
+ }
+ // paint the fuzzy sides
+ if ( isStartFuzzy ) {
+ final GradientPaint gradient1 = new GradientPaint( bounds.x, bounds.y, endColor,
+ bounds.x + fuzzyStartWidth, bounds.y, centerColor, false );
+ g2d.setPaint( gradient1 );
+// g2d.fillRect( bounds.x, bounds.y, fuzzyStartWidth, bounds.height );
+ painter.paint( g2d, bounds.x, bounds.y, fuzzyStartWidth, bounds.height );
+ }
+ if ( isStopFuzzy ) {
+ final GradientPaint gradient2 = new GradientPaint(
+ bounds.x + bounds.width - fuzzyStopWidth, bounds.y, centerColor,
+ bounds.x + bounds.width, bounds.y, endColor, false );
+ g2d.setPaint( gradient2 );
+// g2d.fillRect( bounds.x + bounds.width - fuzzyStopWidth, bounds.y, fuzzyStopWidth, bounds.height );
+ painter.paint( g2d, bounds.x + bounds.width - fuzzyStopWidth, bounds.y, fuzzyStopWidth, bounds.height );
+ }
+ }
+
+
+ public void paintContinuation( final Graphics2D g2d, final int leftX, final int rightX, final boolean isFuzzy ) {
+ final DrawHeights heights = new DrawHeights( TimeSpanDrawUtil.MIN_SPAN_HEIGHT, TimeSpanDrawUtil.FULL_SPAN_HEIGHT,
+ _eventCount, _uncertainCount, _negativeCount );
+ int y = 0;
+ final int width = Math.abs( rightX - leftX );
+ if ( isSelected() ) {
+ final Color color = getSelectedColor( false, isFuzzy );
+ g2d.setColor( color );
+// g2d.drawLine( leftX, heights.__marginHeight-1, rightX, heights.__marginHeight-1 );
+ g2d.fillRect( leftX, heights.__marginHeight - 2, rightX - leftX, 2 );
+ }
+ y += heights.__marginHeight;
+ if ( heights.__affirmedHeight > 0 ) {
+ final Color color = getAffirmedColor( isSelected(), false, isFuzzy );
+ g2d.setColor( color );
+ g2d.fillRect( leftX, y, width, heights.__affirmedHeight );
+ y += heights.__affirmedHeight;
+ }
+ if ( heights.__uncertainHeight > 0 ) {
+ final Color color = getUncertainColor( isSelected(), false, isFuzzy );
+ g2d.setColor( color );
+ g2d.fillRect( leftX, y, width, heights.__uncertainHeight );
+ y += heights.__uncertainHeight;
+ }
+ if ( heights.__negatedHeight > 0 ) {
+ final Color color = getNegatedColor( isSelected(), false, isFuzzy );
+ g2d.setColor( color );
+ g2d.fillRect( leftX, y, width, heights.__negatedHeight );
+ y += heights.__negatedHeight;
+ }
+ if ( isSelected() ) {
+ final Color color = getSelectedColor( false, isFuzzy );
+ g2d.setColor( color );
+// g2d.drawLine( leftX, y, rightX, y );
+ g2d.fillRect( leftX, y, rightX - leftX, 2 );
+ }
+ }
+
+
+ protected interface TimeSpanPainter {
+ public void paint( final Graphics2D g2d, final int x, final int y, final int width, final int height );
+ }
+
+ static final private class DefaultTimeSpanPainter implements TimeSpanPainter {
+ @Override
+ public void paint( final Graphics2D g2d, final int x, final int y, final int width, final int height ) {
+ if ( width <= 0 ) {
+ return;
+ }
+ g2d.fillRect( x, y, width, height );
+ }
+ }
+
+ static final private class UncertainTimeSpanPainter implements TimeSpanPainter {
+ @Override
+ public void paint( final Graphics2D g2d, final int x, final int y, final int width, final int height ) {
+ if ( width <= 0 ) {
+ return;
+ }
+ for ( int y1 = y; y1 < y + height; y1++ ) {
+ for ( int x1 = x; x1 < x + width; x1 += 4 ) {
+ g2d.drawLine( x1, y1, x1 + 1, y1 - 1 );
+ }
+ for ( int x2 = x + 2; x2 < x + width; x2 += 4 ) {
+ g2d.drawLine( x2, y1, x2 + 1, y1 + 1 );
+ }
+ }
+ }
+ }
+
+
+ static final private class NegatedTimeSpanPainter implements TimeSpanPainter {
+ @Override
+ public void paint( final Graphics2D g2d, final int x, final int y, final int width, final int height ) {
+ if ( width <= 0 ) {
+ return;
+ }
+ // Make sure that the dashed lines always match up 0-4
+ int start = x % 4;
+ if ( start != 3 ) {
+ g2d.fillRect( x, y, 3 - start, height );
+ }
+ for ( int x1 = x - start + 4; x1 < x - start + width; x1 += 4 ) {
+ g2d.fillRect( x1, y, 3, height );
+ }
+ final int end = (x + width) % 4;
+ if ( end != 0 ) {
+ g2d.fillRect( x + width - end, y, end, height );
+ }
+ }
+ }
+
+
+}