ctakes-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From seanfi...@apache.org
Subject svn commit: r1660963 [11/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...
Date Thu, 19 Feb 2015 18:06:17 GMT
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/MainTimelinePanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/MainTimelinePanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/MainTimelinePanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/MainTimelinePanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,517 @@
+package org.chboston.cnlp.timeline.gui.main;
+
+import org.chboston.cnlp.gui.PositioningSplitPane;
+import org.chboston.cnlp.gui.SmoothTipList;
+import org.chboston.cnlp.gui.VerticalMimicPanel;
+import org.chboston.cnlp.gui.ZoomSlider;
+import org.chboston.cnlp.nlp.annotation.annotation.AnnotationSpanComparator;
+import org.chboston.cnlp.nlp.annotation.annotation.AnnotationTextComparator;
+import org.chboston.cnlp.nlp.annotation.annotation.store.AnnotationStore;
+import org.chboston.cnlp.nlp.annotation.annotation.store.AnnotationStoreFactory;
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.relation.Relation;
+import org.chboston.cnlp.nlp.corpus.Note;
+import org.chboston.cnlp.timeline.gui.TextHighlightPanel;
+import org.chboston.cnlp.timeline.gui.calendar.JTimelineCalendar;
+import org.chboston.cnlp.timeline.gui.corpus.NoteNavigator;
+import org.chboston.cnlp.timeline.gui.event.EventColor;
+import org.chboston.cnlp.timeline.gui.event.list.EventListElement;
+import org.chboston.cnlp.timeline.gui.event.list.EventListModel;
+import org.chboston.cnlp.timeline.gui.event.list.EventListRenderer3;
+import org.chboston.cnlp.timeline.gui.event.text.EventCellRenderer;
+import org.chboston.cnlp.timeline.gui.misc.TimeVisOptionPane;
+import org.chboston.cnlp.timeline.gui.relation.list.RelationListModel;
+import org.chboston.cnlp.timeline.gui.relation.list.RelationListRenderer2;
+import org.chboston.cnlp.timeline.gui.search.TimelineSearchPanel;
+import org.chboston.cnlp.timeline.gui.spiral.JSpiralBar;
+import org.chboston.cnlp.timeline.gui.spiral.TimeZoomSlider;
+import org.chboston.cnlp.timeline.gui.timeline.JTimelineComponent;
+import org.chboston.cnlp.timeline.gui.timeline.stack.SemanticTimelinesStack;
+import org.chboston.cnlp.timeline.gui.timeline.stack.TimelineStack;
+import org.chboston.cnlp.timeline.gui.timespan.selection.DefaultTimeSpanSelectionModel;
+import org.chboston.cnlp.timeline.gui.timespan.selection.SelectionForwarder;
+import org.chboston.cnlp.timeline.gui.timespan.selection.TimeSpanSelectionModel;
+import org.chboston.cnlp.timeline.timeline.SemanticTypeTimeline;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timeline.TimelineFactory;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.border.LineBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * This is a big ugly class that will be refactored once things are a little more "finalized".
+ * <p/>
+ * It all started with a demo, and pushed forward from there.
+ * <p/>
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/27/13
+ */
+final public class MainTimelinePanel extends JPanel {
+
+   // TODO break out some of the components and put them in separate files - PatientLabel, EventLegendPanel, etc.
+
+   static private enum SemanticInfo {
+      SIGNS( "Signs/Symptoms", SemanticClassType.SIGN_OR_SYMPTOM, EventColor.SIGN_SYMPTOM.getColor(),
+            "Sign/Symptom", "Sign/Symp", "S/S", "S" ),
+      TESTS( "Tests/Procedures", SemanticClassType.PROCEDURE, EventColor.TEST_PROCEDURE.getColor(),
+            "Test/Procedure", "Test/Proc", "T/P", "T" ),
+      DISEASES( "Diseases/Disorders", SemanticClassType.DISEASE_DISORDER, EventColor.DISEASE_DISORDER.getColor(),
+            "Disease/Disorder", "Dis/Dis", "D/D", "D" ),
+      MEDS( "Medications", SemanticClassType.MEDICATION, EventColor.MEDICATION.getColor(),
+            "Medication", "Med", "M" );
+      private final String __name;
+      private final SemanticClassType __type;
+      private final Collection<String> __labels;
+      private final Color __color;
+
+      private SemanticInfo( final String name, final SemanticClassType type, final Color color,
+                            final String... labels ) {
+         __name = name;
+         __type = type;
+         __labels = Arrays.asList( labels );
+         __color = color;
+      }
+   }
+
+   static private final JLabel PLEASE_LABEL;
+
+   static {
+      PLEASE_LABEL = new JLabel( "Please Select a Note" );
+      PLEASE_LABEL.setHorizontalAlignment( SwingConstants.CENTER );
+   }
+
+
+   private JComponent _mainComponent = null;
+
+   public MainTimelinePanel() {
+      super( new BorderLayout() );
+      final NoteNavigator noteNavigator = new NoteNavigator( null );
+      noteNavigator.addListSelectionListener( new NoteSelectionListener() );
+      add( noteNavigator, BorderLayout.NORTH );
+      _mainComponent = PLEASE_LABEL;
+      add( _mainComponent, BorderLayout.CENTER );
+   }
+
+
+   private void setNote( final Note note ) {
+      if ( note == null ) {
+         setTimeline( null, null );
+         return;
+      }
+      SwingUtilities.getRoot( this ).setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
+      SwingUtilities.invokeLater( new NoteLoader( note ) );
+   }
+
+   private class NoteLoader implements Runnable {
+      private final Note __note;
+
+      private NoteLoader( final Note note ) {
+         __note = note;
+      }
+
+      @Override
+      public void run() {
+         AnnotationStore annotationStore = null;
+         try {
+            annotationStore = AnnotationStoreFactory.createAnnotationCollection( __note.getPath() );
+         } catch ( NullPointerException npE ) {
+            JOptionPane.showMessageDialog( null, "Unable to load Note " + __note.getPath(),
+                  "Bad Note", JOptionPane.ERROR_MESSAGE );
+            setNote( null );
+            return;
+         }
+         final Timeline timeline = TimelineFactory.createTimeline( __note.getTitle(), annotationStore );
+         final String documentText = annotationStore.getDocumentText();
+         setTimeline( timeline, documentText );
+      }
+   }
+
+   private void setTimeline( final Timeline timeline, final String documentText ) {
+      final JComponent oldMainComponent = _mainComponent;
+      if ( timeline != null ) {
+         SwingUtilities.getRoot( this ).setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
+         _mainComponent = createMainSplitPane( timeline, documentText );
+      } else {
+         _mainComponent = PLEASE_LABEL;
+      }
+      if ( !oldMainComponent.equals( _mainComponent ) ) {
+         remove( oldMainComponent );
+         add( _mainComponent, BorderLayout.CENTER );
+         revalidate();
+      }
+      SwingUtilities.getRoot( this ).setCursor( Cursor.getDefaultCursor() );
+   }
+
+
+//
+//
+//
+//   private void setNote( final Note note ) {
+//      if ( note == null ) {
+//         setTimeline( null, null );
+//         return;
+//      }
+//      remove( _mainComponent );
+//
+//      AnnotationCollection annotationCollection = null;
+//      try {
+//         annotationCollection = AnnotationCollectionFactory.createAnnotationCollection( note.getPath() );
+//      } catch ( NullPointerException npE ) {
+//
+//         JOptionPane.showMessageDialog( this, "Unable to load Note " + note.getPath(),
+//               "Bad Note", JOptionPane.ERROR_MESSAGE );
+//         setNote( null );
+//         return;
+//      }
+//      String title = note.getPath();
+//      if ( title.endsWith( ".txt" ) ) {
+//         title = title.substring( 0, title.length() - 4 );
+//      }
+//      if ( title.endsWith( ".xml" ) ) {
+//         title = title.substring( 0, title.length() - 4 );
+//      }
+//      if ( title.endsWith( ".knowtator" ) ) {
+//         title = title.substring( 0, title.length() - 10 );
+//      }
+//      final Timeline timeline = TimelineFactory.createTimeline( title, annotationCollection );
+//      final String documentText = annotationCollection.getText();
+//
+//      setTimeline( timeline, documentText );
+//   }
+//
+//   private void setTimeline( final Timeline timeline, final String documentText ) {
+//      if ( timeline == null ) {
+//         if ( _mainComponent != null ) {
+//            if ( _mainComponent.equals( PLEASE_LABEL ) ) {
+//               return;
+//            }
+//            remove( _mainComponent );
+//         }
+//         _mainComponent = PLEASE_LABEL;
+//      } else {
+//         if ( _mainComponent != null ) {
+//            remove( _mainComponent );
+//         }
+//         _mainComponent = createMainSplitPane( timeline, documentText );
+//      }
+//      add( _mainComponent, BorderLayout.CENTER );
+//   }
+//
+
+   static public JComponent createMainSplitPane( final Timeline timeline, final String documentText ) {
+      final JSpiralBar spiralBar = new JSpiralBar( timeline );
+      SelectionForwarder.getInstance().addSelectionModel( spiralBar.getSelectionModel() );
+
+      // KLUDGE to get sizing of OldJTimeline to work properly within a BoxLayout
+      final SemanticTimelinesStack semanticsStack = new SemanticTimelinesStack();
+      final VerticalMimicPanel typeHeaderStack = new VerticalMimicPanel( semanticsStack.getStack() );
+      final VerticalMimicPanel buttonFooterStack = new VerticalMimicPanel( semanticsStack.getStack() );
+
+      final TimelineStack signSymptomStack = createSemanticStack( SemanticInfo.SIGNS, timeline,
+            typeHeaderStack, buttonFooterStack );
+      final TimelineStack procedureStack = createSemanticStack( SemanticInfo.TESTS, timeline,
+            typeHeaderStack, buttonFooterStack );
+      final TimelineStack disDisorderStack = createSemanticStack( SemanticInfo.DISEASES, timeline,
+            typeHeaderStack, buttonFooterStack );
+      final TimelineStack medicationStack = createSemanticStack( SemanticInfo.MEDS, timeline,
+            typeHeaderStack, buttonFooterStack );
+      semanticsStack.addTimelineComponent( signSymptomStack );
+      semanticsStack.addSpacer();
+      semanticsStack.addTimelineComponent( procedureStack );
+      semanticsStack.addSpacer();
+      semanticsStack.addTimelineComponent( disDisorderStack );
+      semanticsStack.addSpacer();
+      semanticsStack.addTimelineComponent( medicationStack );
+      semanticsStack.addSpacer();
+      semanticsStack.addTimelineComponent( createAllEventsTimeline( timeline, "All Events", typeHeaderStack ) );
+
+      final JScrollPane timelineScrollPane = spiralBar.createScrollPane();
+      timelineScrollPane.setViewportView( semanticsStack );
+      timelineScrollPane.setCorner( JScrollPane.LOWER_RIGHT_CORNER, buttonFooterStack );
+      timelineScrollPane.setRowHeaderView( typeHeaderStack );
+
+      final JTimelineCalendar calendar = new JTimelineCalendar( timeline );
+      timelineScrollPane.setColumnHeaderView( calendar );
+      final ComponentListener mimickComponentListener = new MimicComponentWidthAdapter( calendar );
+      semanticsStack.addComponentListener( mimickComponentListener );
+
+      final PatientInfoPanel patientInfoPanel = new PatientInfoPanel();
+      patientInfoPanel.setPatientInfo( timeline.getReferenceMillis() );
+
+      final EventListModel listModel = new EventListModel( timeline );
+      SelectionForwarder.getInstance().addSelectionModel( listModel.getSelectionModel() );
+      final JList<EventListElement> eventList = new SmoothTipList<>( listModel );
+      eventList.setCellRenderer( new EventListRenderer3() );
+      eventList.addComponentListener( new ComponentAdapter() {
+         @Override
+         public void componentResized( final ComponentEvent e ) {
+            e.getComponent().invalidate();
+            e.getComponent().repaint();
+         }
+      } );
+      eventList.addMouseListener( new EventListPopupListener( listModel ) );
+
+      final RelationListModel relationListModel = new RelationListModel( listModel, eventList.getSelectionModel() );
+      SelectionForwarder.getInstance().addSelectionModel( relationListModel.getSelectionModel() );
+      final JList<Relation> relationList = new SmoothTipList<>( relationListModel );
+      relationList.setEnabled( false );
+      relationList.setCellRenderer( new RelationListRenderer2() );
+      relationList.addComponentListener( new ComponentAdapter() {
+         @Override
+         public void componentResized( final ComponentEvent e ) {
+            e.getComponent().invalidate();
+            e.getComponent().repaint();
+         }
+      } );
+
+
+      final TimelineSearchPanel searchPanel = new TimelineSearchPanel( timeline,
+            listModel,
+            eventList.getSelectionModel(),
+            signSymptomStack,
+            procedureStack,
+            disDisorderStack,
+            medicationStack );
+      SelectionForwarder.getInstance().addSelectionModel( searchPanel.getSelectionModel() );
+
+      final TextHighlightPanel documentPanel = new TextHighlightPanel( timeline, listModel, eventList
+            .getSelectionModel() );
+      SelectionForwarder.getInstance().addSelectionModel( documentPanel.getSelectionModel() );
+      documentPanel.setText( documentText );
+      final JScrollPane documentScrollPane = new JScrollPane( documentPanel );
+      documentPanel.setVerticalScrollBar( documentScrollPane.getVerticalScrollBar() );
+
+      final JSplitPane timelineDocSplitPane = new PositioningSplitPane( JSplitPane.VERTICAL_SPLIT,
+            timelineScrollPane,
+            documentScrollPane );
+      timelineDocSplitPane.setDividerLocation( 0.8 );
+
+      final JScrollPane infoScrollPane = new JScrollPane( eventList );
+      infoScrollPane.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
+      final JScrollPane relationScrollPane = new JScrollPane( relationList );
+      relationScrollPane.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
+      final JSplitPane eventInfoPanel
+            = new PositioningSplitPane( JSplitPane.VERTICAL_SPLIT, infoScrollPane, relationScrollPane );
+      eventInfoPanel.setDividerLocation( 0.5 );
+
+      final JPanel optionPane = new TimeVisOptionPane( spiralBar, documentPanel );
+      final JComponent legendPanel = EventLegendPanel2.createLegend();
+      final JPanel optionLegendPane = new JPanel( new BorderLayout() );
+      optionLegendPane.add( optionPane, BorderLayout.CENTER );
+      optionLegendPane.add( legendPanel, BorderLayout.SOUTH );
+
+
+      final JPanel leftPanel = new JPanel( new BorderLayout( 0, 0 ) );
+      leftPanel.add( searchPanel, BorderLayout.NORTH );
+      leftPanel.add( timelineDocSplitPane, BorderLayout.CENTER );
+
+      final JPanel rightPanel = new JPanel( new BorderLayout( 0, 5 ) );
+      rightPanel.add( patientInfoPanel, BorderLayout.NORTH );
+      rightPanel.add( eventInfoPanel, BorderLayout.CENTER );
+      rightPanel.add( optionLegendPane, BorderLayout.SOUTH );
+
+      final JSplitPane mainSplitPane = new PositioningSplitPane( JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel );
+      mainSplitPane.setDividerLocation( 0.8d );
+      mainSplitPane.setBorder( new LineBorder( Color.GRAY, 1 ) );
+
+      // DEBUG
+//      final InfoPanel infoPanel = new InfoPanel();
+//      infoPanel.setTimeline( timeline );
+//      final JSplitPane debugPanel = new PositioningSplitPane( JSplitPane.HORIZONTAL_SPLIT, mainSplitPane, infoPanel );
+//      debugPanel.setDividerLocation( 0.95d );
+
+      final ZoomSlider zoomSlider = new TimeZoomSlider( semanticsStack, timeline );
+      spiralBar.setZoomSlider( zoomSlider );
+//      add( debugPanel, BorderLayout.CENTER );
+      return mainSplitPane;
+   }
+
+
+   static private TimelineStack createSemanticStack( final SemanticInfo semanticInfo,
+                                                     final Timeline timeline,
+                                                     final VerticalMimicPanel typeHeaderStack,
+                                                     final VerticalMimicPanel buttonFooterStack ) {
+      final Timeline semanticTimeline = new SemanticTypeTimeline( semanticInfo.__name, timeline, semanticInfo.__type );
+      final TimeSpanSelectionModel selectionModel = new DefaultTimeSpanSelectionModel( semanticInfo.__type );
+      SelectionForwarder.getInstance().addSelectionModel( selectionModel );
+      final TimelineStack timelineStack
+            = createTimelineStack( typeHeaderStack, buttonFooterStack, semanticInfo.__labels );
+      timelineStack.addTimeline( semanticTimeline, semanticInfo.__color, timeline, selectionModel, false );
+      return timelineStack;
+   }
+
+   static private TimelineStack createTimelineStack( final VerticalMimicPanel typeHeaderStack,
+                                                     final VerticalMimicPanel buttonFooterStack,
+                                                     final Collection<String> labels ) {
+      // handle headers
+      final TimelineStack timelineStack = new TimelineStack();
+      final JComponent timelineTitleStack = timelineStack.getTitleStack();
+      final JComponent removeTimelineStack = timelineStack.getRemoveButtonStack();
+
+      final JPanel labelPanel = new JPanel( new BorderLayout() );
+      //      final TimelineStackCheckBox checkBox = new TimelineStackCheckBox( timelineStack, title );
+      //      checkBox.setHorizontalAlignment( SwingConstants.CENTER );
+      //      checkBox.setHorizontalTextPosition( SwingConstants.RIGHT );
+      //      checkBox.setUI( new VerticalCheckBoxUI( false ) );
+      //      final Font oldFont = checkBox.getFont();
+      //      final Font newFont = oldFont.deriveFont( Font.BOLD, oldFont.getSize2D() + 3 );
+      //      checkBox.setFont( newFont );
+      //      labelPanel.add( checkBox, BorderLayout.WEST );
+      labelPanel.add( timelineTitleStack, BorderLayout.CENTER );
+
+      typeHeaderStack.addComponentFor( timelineStack, labelPanel );
+      buttonFooterStack.addComponentFor( timelineStack, removeTimelineStack );
+
+      return timelineStack;
+   }
+
+   static private JComponent createAllEventsTimeline( final Timeline timeline,
+                                                      final String title,
+                                                      final VerticalMimicPanel typeHeaderStack ) {
+      final JTimelineComponent jTimeline = new JTimelineComponent( timeline, timeline );
+      SelectionForwarder.getInstance().addSelectionModel( jTimeline.getSelectionModel() );
+      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, 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;
+         }
+      } );
+
+      // handle headers - this is the "All Events" Label
+      final JLabel semanticTypeLabel = new JLabel( title + " " );
+      semanticTypeLabel.setHorizontalAlignment( JLabel.CENTER );
+      final Font oldFont = semanticTypeLabel.getFont();
+      final Font newFont = oldFont.deriveFont( Font.BOLD, oldFont.getSize2D() + 3 );
+      semanticTypeLabel.setFont( newFont );
+//      semanticTypeLabel.setUI( new VerticalLabelUI( false ) );
+      typeHeaderStack.addComponentFor( jTimeline, semanticTypeLabel );
+      return jTimeline;
+   }
+
+
+   // For resizing the calendar when the semantic stack (main panel) is resized (zoom, etc.)
+   static private class MimicComponentWidthAdapter extends ComponentAdapter {
+      final Component __component;
+
+      private MimicComponentWidthAdapter( final Component component ) {
+         __component = component;
+      }
+
+      private void resize( final ComponentEvent event ) {
+         final int width = __component.getPreferredSize().width;
+         final int eWidth = event.getComponent().getPreferredSize().width;
+         if ( width != eWidth ) {
+            __component.setPreferredSize( new Dimension( eWidth, __component.getPreferredSize().height ) );
+            __component.repaint();
+         }
+      }
+
+      @Override
+      public void componentResized( final ComponentEvent e ) {
+         resize( e );
+      }
+
+      @Override
+      public void componentMoved( ComponentEvent e ) {
+         resize( e );
+      }
+
+      @Override
+      public void componentShown( ComponentEvent e ) {
+         resize( e );
+      }
+   }
+
+
+   private class NoteSelectionListener implements ListSelectionListener {
+      @Override
+      public void valueChanged( final ListSelectionEvent event ) {
+         if ( event.getValueIsAdjusting() ) {
+            return;
+         }
+         final Object source = event.getSource();
+         if ( source instanceof NoteNavigator ) {
+            setNote( ((NoteNavigator)source).getSelectedNote() );
+         }
+      }
+   }
+
+//   static private class TimelineStackCheckBox extends MultiSizeCheckBox {
+//      private TimelineStack __timelineStack;
+//      public TimelineStackCheckBox( final TimelineStack timelineStack, final String... title ) {
+//         super( title );
+//         __timelineStack = timelineStack;
+//         setModel( new MyToggleModel() );
+//         setSelected( true );
+//      }
+//      private class MyToggleModel extends ToggleButtonModel {
+//         public void setSelected( final boolean b ) {
+//            super.setSelected( b );
+//            __timelineStack.collapse( !b );
+//         }
+//      }
+//   }
+
+   static private final class EventListPopupListener extends MouseAdapter {
+      final private EventListModel __eventListModel;
+      private EventListPopupListener( final EventListModel eventListModel ) {
+         __eventListModel = eventListModel;
+      }
+      @Override
+      public void mousePressed( final MouseEvent event ) {
+         checkPopup( event );
+      }
+      @Override
+      public void mouseReleased( final MouseEvent event ) {
+         checkPopup( event );
+      }
+      private void checkPopup( final MouseEvent event ) {
+         if ( event.isPopupTrigger()
+              || event.getButton() == MouseEvent.BUTTON2 || event.getButton() == MouseEvent.BUTTON3 ) {
+            final JPopupMenu menu = new JPopupMenu( "Sort by" );
+            menu.add( new AbstractAction( "Text" ) {
+               public void actionPerformed( final ActionEvent event ) {
+                  __eventListModel.setEventComparator( AnnotationTextComparator.getInstance() );
+
+               }
+            } );
+            menu.add( new AbstractAction( "Span" ) {
+               public void actionPerformed( final ActionEvent event ) {
+                  __eventListModel.setEventComparator( AnnotationSpanComparator.getInstance() );
+
+               }
+            } );
+            menu.show( event.getComponent(), event.getX(), event.getY() );
+         }
+      }
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/MainTimelinePanel2.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/MainTimelinePanel2.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/MainTimelinePanel2.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/MainTimelinePanel2.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,429 @@
+//package org.chboston.cnlp.timeline.gui.main;
+//
+//import org.chboston.cnlp.gui.PositioningSplitPane;
+//import org.chboston.cnlp.gui.SmoothTipList;
+//import org.chboston.cnlp.gui.VerticalMimicPanel;
+//import org.chboston.cnlp.gui.ZoomSlider;
+//import org.chboston.cnlp.nlp.annotation.annotation.AnnotationCollection;
+//import org.chboston.cnlp.nlp.annotation.annotation.AnnotationCollectionFactory;
+//import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+//import org.chboston.cnlp.nlp.annotation.relation.Relation;
+//import org.chboston.cnlp.nlp.corpus.Note;
+//import org.chboston.cnlp.timeline.gui.TextHighlightPanel;
+//import org.chboston.cnlp.timeline.gui.calendar.JTimelineCalendar;
+//import org.chboston.cnlp.timeline.gui.corpus.NoteNavigator;
+//import org.chboston.cnlp.timeline.gui.event.EventColor;
+//import org.chboston.cnlp.timeline.gui.event.list.EventListElement;
+//import org.chboston.cnlp.timeline.gui.event.list.EventListModel;
+//import org.chboston.cnlp.timeline.gui.event.list.EventListRenderer3;
+//import org.chboston.cnlp.timeline.gui.misc.TimeVisOptionPane;
+//import org.chboston.cnlp.timeline.gui.relation.list.RelationListModel;
+//import org.chboston.cnlp.timeline.gui.relation.list.RelationListRenderer2;
+//import org.chboston.cnlp.timeline.gui.search.TimelineSearchPanel;
+//import org.chboston.cnlp.timeline.gui.spiral.JSpiralBar;
+//import org.chboston.cnlp.timeline.gui.spiral.TimeZoomSlider;
+//import org.chboston.cnlp.timeline.gui.timeline.JTimelineComponent;
+//import org.chboston.cnlp.timeline.gui.timeline.stack.SemanticTimelinesStack;
+//import org.chboston.cnlp.timeline.gui.timeline.stack.TimelineStack;
+//import org.chboston.cnlp.timeline.gui.timespan.selection.DefaultTimeSpanSelectionModel;
+//import org.chboston.cnlp.timeline.gui.timespan.selection.SelectionForwarder;
+//import org.chboston.cnlp.timeline.gui.timespan.selection.TimeSpanSelectionModel;
+//import org.chboston.cnlp.timeline.timeline.SemanticTypeTimeline;
+//import org.chboston.cnlp.timeline.timeline.Timeline;
+//import org.chboston.cnlp.timeline.timeline.TimelineFactory;
+//
+//import javax.swing.*;
+//import javax.swing.border.Border;
+//import javax.swing.event.ListSelectionEvent;
+//import javax.swing.event.ListSelectionListener;
+//import java.awt.*;
+//import java.awt.event.ComponentAdapter;
+//import java.awt.event.ComponentEvent;
+//import java.awt.event.ComponentListener;
+//import java.util.Arrays;
+//import java.util.Collection;
+//import java.util.EnumMap;
+//import java.util.Map;
+//
+///**
+// * This is a big ugly class that will be refactored once things are a little more "finalized".
+// *
+// * It all started with a demo, and pushed forward from there.
+// *
+// * Author: SPF
+// * Affiliation: CHIP-NLP
+// * Date: 8/27/13
+// */
+//final public class MainTimelinePanel2 extends JPanel {
+//
+//   // TODO break out some of the components and put them in separate files - PatientLabel, EventLegendPanel, etc.
+//
+//   static private enum SemanticInfo {
+//      SIGNS( "Signs/Symptoms", SemanticClassType.SIGN_OR_SYMPTOM, EventColor.SIGN_SYMPTOM.getColor(),
+//             "Sign/Symptom", "Sign/Symp", "S/S", "S" ),
+//      TESTS( "Tests/Procedures", SemanticClassType.PROCEDURE, EventColor.TEST_PROCEDURE.getColor(),
+//             "Test/Procedure", "Test/Proc", "T/P", "T" ),
+//      DISEASES( "Diseases/Disorders", SemanticClassType.DISEASE_DISORDER, EventColor.DISEASE_DISORDER.getColor(),
+//                "Disease/Disorder", "Dis/Dis", "D/D", "D" ),
+//      MEDS( "Medications", SemanticClassType.MEDICATION, EventColor.MEDICATION.getColor(),
+//            "Medication", "Med", "M" );
+//      private final String __name;
+//      private final SemanticClassType __type;
+//      private final Collection<String> __labels;
+//      private final Color __color;
+//      private SemanticInfo( final String name, final SemanticClassType type, final Color color, final String... labels ) {
+//         __name = name;
+//         __type = type;
+//         __labels = Arrays.asList( labels );
+//         __color = color;
+//      }
+//   }
+//
+//   static private final JLabel PLEASE_LABEL;
+//   static {
+//      PLEASE_LABEL = new JLabel( "Please Select a Note" );
+//      PLEASE_LABEL.setHorizontalAlignment( SwingConstants.CENTER );
+//   }
+//
+//
+//
+//   private JComponent _mainComponent = null;
+//
+//   public MainTimelinePanel2() {
+//      super( new BorderLayout() );
+//      final NoteNavigator noteNavigator = new NoteNavigator( null );
+//      noteNavigator.addListSelectionListener( new NoteSelectionListener() );
+//      add( noteNavigator, BorderLayout.NORTH );
+//      setNote( null );
+//   }
+//
+//
+//   private void setNote( final Note note ) {
+//      if ( note == null ) {
+//         setTimeline( null, null );
+//         return;
+//      }
+//      SwingUtilities.getRoot( this ).setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
+//      SwingUtilities.invokeLater( new NoteLoader( note ) );
+//   }
+//
+//   private class NoteLoader implements Runnable {
+//      private final Note __note;
+//      private NoteLoader( final Note note ) {
+//         __note = note;
+//      }
+//      @Override
+//      public void run() {
+//         AnnotationCollection annotationCollection = null;
+//         try {
+//            annotationCollection = AnnotationCollectionFactory.createAnnotationCollection( __note.getPath() );
+//         } catch ( NullPointerException npE ) {
+//            JOptionPane.showMessageDialog( null, "Unable to load Note " + __note.getPath(),
+//                  "Bad Note", JOptionPane.ERROR_MESSAGE );
+//            setNote( null );
+//            return;
+//         }
+//         final Timeline timeline = TimelineFactory.createTimeline( __note.getTitle(), annotationCollection );
+//         final String documentText = annotationCollection.getText();
+//         setTimeline( timeline, documentText );
+//      }
+//   }
+//
+//   private void setTimeline( final Timeline timeline, final String documentText ) {
+//      final JComponent oldMainComponent = _mainComponent;
+//      if ( timeline != null ) {
+//         SwingUtilities.getRoot( this ).setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
+//         _mainComponent = createMainSplitPane( timeline, documentText );
+//      } else {
+//         _mainComponent = PLEASE_LABEL;
+//      }
+//      if ( !oldMainComponent.equals( _mainComponent ) ) {
+//         remove( oldMainComponent );
+//         add( _mainComponent, BorderLayout.CENTER );
+//      }
+//      SwingUtilities.getRoot( this ).setCursor( Cursor.getDefaultCursor() );
+//   }
+//
+//
+//   private final Map<SemanticInfo, TimelineStack> _semanticStacks = new EnumMap<>( SemanticInfo.class );
+//
+//   private JSpiralBar _spiralBar;
+//   private JTimelineComponent _allSemanticsStack;
+//   private JTimelineCalendar _timelineCalendar;
+//   private PatientInfoPanel _patientInfoPanel;
+//   private EventListModel _selectedEventListModel;
+//   private TimelineSearchPanel _searchPanel;
+//   private TextHighlightPanel _documentPanel;
+//   private ZoomSlider _zoomSlider;
+//
+//
+//   public void setGuiTimeline( final Timeline timeline, final String documentText ) {
+//      _spiralBar.setModel( timeline );
+//      for ( Map.Entry<SemanticInfo,TimelineStack> semanticEntry : _semanticStacks.entrySet() ) {
+//         semanticEntry.getValue().clearTimelines();
+//      }
+//      _allSemanticsStack.setGridModel( timeline );
+//      _allSemanticsStack.setModel( timeline );
+//      _timelineCalendar.setTimeline( timeline );
+//      _patientInfoPanel.setPatientInfo( timeline.getReferenceMillis() );
+//      _selectedEventListModel.setTimeline( timeline );
+//      _searchPanel.set
+//
+//
+//      for ( Map.Entry<SemanticInfo,TimelineStack> semanticEntry : _semanticStacks.entrySet() ) {
+//         final Timeline semanticTimeline = new SemanticTypeTimeline( semanticEntry.getKey().__name,
+//               timeline, semanticEntry.getKey().__type );
+//         final TimeSpanSelectionModel selectionModel = new DefaultTimeSpanSelectionModel( semanticEntry.getKey().__type );
+//         SelectionForwarder.getInstance().addSelectionModel( selectionModel );
+//         semanticEntry.getValue().addTimeline( semanticTimeline, semanticEntry.getKey().__color, timeline, selectionModel, false );
+//      }
+//   }
+//
+//
+//   public JComponent createMainSplitPane( final Timeline timeline, final String documentText ) {
+//      final JSpiralBar spiralBar = new JSpiralBar( timeline );
+//      SelectionForwarder.getInstance().addSelectionModel( spiralBar.getSelectionModel() );
+//
+//      // KLUDGE to get sizing of OldJTimeline to work properly within a BoxLayout
+//      final SemanticTimelinesStack semanticsStack = new SemanticTimelinesStack();
+//      final VerticalMimicPanel typeHeaderStack = new VerticalMimicPanel( semanticsStack.getStack() );
+//      final VerticalMimicPanel buttonFooterStack = new VerticalMimicPanel( semanticsStack.getStack() );
+//
+//      final TimelineStack signSymptomStack = createSemanticStack( SemanticInfo.SIGNS, timeline,
+//            typeHeaderStack, buttonFooterStack );
+//      final TimelineStack procedureStack = createSemanticStack( SemanticInfo.TESTS, timeline,
+//            typeHeaderStack, buttonFooterStack );
+//      final TimelineStack disDisorderStack = createSemanticStack( SemanticInfo.DISEASES, timeline,
+//            typeHeaderStack, buttonFooterStack );
+//      final TimelineStack medicationStack = createSemanticStack( SemanticInfo.MEDS, timeline,
+//            typeHeaderStack, buttonFooterStack );
+//      semanticsStack.addTimelineComponent( signSymptomStack );
+//      semanticsStack.addSpacer();
+//      semanticsStack.addTimelineComponent( procedureStack );
+//      semanticsStack.addSpacer();
+//      semanticsStack.addTimelineComponent( disDisorderStack );
+//      semanticsStack.addSpacer();
+//      semanticsStack.addTimelineComponent( medicationStack );
+//      semanticsStack.addSpacer();
+//      semanticsStack.addTimelineComponent( createAllEventsTimeline( timeline, "All Events", typeHeaderStack ) );
+//
+//      final JScrollPane timelineScrollPane = spiralBar.createScrollPane();
+//      timelineScrollPane.setViewportView( semanticsStack );
+//      timelineScrollPane.setCorner( JScrollPane.LOWER_RIGHT_CORNER, buttonFooterStack );
+//      timelineScrollPane.setRowHeaderView( typeHeaderStack );
+//
+//      final JTimelineCalendar calendar = new JTimelineCalendar( timeline );
+//      timelineScrollPane.setColumnHeaderView( calendar );
+//      final ComponentListener mimickComponentListener = new MimicComponentWidthAdapter( calendar );
+//      semanticsStack.addComponentListener( mimickComponentListener );
+//
+//      final PatientInfoPanel patientInfoPanel = new PatientInfoPanel();
+//      patientInfoPanel.setPatientInfo( timeline.getReferenceMillis() );
+//
+//      final EventListModel listModel = new EventListModel( timeline );
+//      SelectionForwarder.getInstance().addSelectionModel( listModel.getSelectionModel() );
+//      final JList<EventListElement> eventList = new SmoothTipList<>( listModel );
+//      eventList.setCellRenderer( new EventListRenderer3() );
+//      eventList.addComponentListener( new ComponentAdapter() {
+//         @Override
+//         public void componentResized( final ComponentEvent e ) {
+//            e.getComponent().invalidate();
+//            e.getComponent().repaint();
+//         }
+//      } );
+//
+//      final RelationListModel relationListModel = new RelationListModel( listModel, eventList.getSelectionModel() );
+//      SelectionForwarder.getInstance().addSelectionModel( relationListModel.getSelectionModel() );
+//      final JList<Relation> relationList = new SmoothTipList<>( relationListModel );
+//      relationList.setCellRenderer( new RelationListRenderer2() );
+//      relationList.addComponentListener( new ComponentAdapter() {
+//         @Override
+//         public void componentResized( final ComponentEvent e ) {
+//            e.getComponent().invalidate();
+//            e.getComponent().repaint();
+//         }
+//      } );
+//
+//
+//
+//      final TimelineSearchPanel searchPanel = new TimelineSearchPanel( timeline,
+//            listModel,
+//            eventList.getSelectionModel(),
+//            signSymptomStack,
+//            procedureStack,
+//            disDisorderStack,
+//            medicationStack );
+//      SelectionForwarder.getInstance().addSelectionModel( searchPanel.getSelectionModel() );
+//
+//      final TextHighlightPanel documentPanel = new TextHighlightPanel( timeline, listModel, eventList.getSelectionModel() );
+//      SelectionForwarder.getInstance().addSelectionModel( documentPanel.getSelectionModel() );
+//      documentPanel.setText( documentText );
+//      final JScrollPane documentScrollPane = new JScrollPane( documentPanel );
+//      documentPanel.setVerticalScrollBar( documentScrollPane.getVerticalScrollBar() );
+//
+//      final JPanel sketchPanel = new JPanel( new BorderLayout() );
+//      sketchPanel.add( searchPanel, BorderLayout.NORTH );
+//
+//      final JSplitPane timelineDocSplitPane = new PositioningSplitPane( JSplitPane.VERTICAL_SPLIT,
+//            timelineScrollPane,
+//            documentScrollPane );
+//      timelineDocSplitPane.setDividerLocation( 0.8 );
+//      sketchPanel.add( timelineDocSplitPane, BorderLayout.CENTER );
+//
+//      final JScrollPane infoScrollPane = new JScrollPane( eventList );
+//      infoScrollPane.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
+//      final JScrollPane relationScrollPane = new JScrollPane( relationList );
+//      relationScrollPane.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
+////      final JPanel eventInfoPanel = new JPanel( new GridLayout( 2, 1 ) );
+////      eventInfoPanel.add( infoScrollPane );
+////      eventInfoPanel.add( relationScrollPane );
+//      final JSplitPane eventInfoPanel = new PositioningSplitPane( JSplitPane.VERTICAL_SPLIT, infoScrollPane, relationScrollPane );
+//      eventInfoPanel.setDividerLocation( 0.5 );
+//
+//      final JPanel optionPane = new TimeVisOptionPane( spiralBar, documentPanel );
+//      final JComponent legendPanel = new EventLegendPanel();
+//      legendPanel.setBackground( optionPane.getBackground() );
+//      final JPanel optionLegendPane = new JPanel( new BorderLayout() );
+//      optionLegendPane.add( optionPane, BorderLayout.CENTER );
+//      optionLegendPane.add( legendPanel, BorderLayout.SOUTH );
+//
+//      final JPanel rightPanel = new JPanel( new BorderLayout( 0, 5 ) );
+//      rightPanel.add( patientInfoPanel, BorderLayout.NORTH );
+////      rightPanel.add( infoScrollPane, BorderLayout.CENTER );
+//      rightPanel.add( eventInfoPanel, BorderLayout.CENTER );
+//      rightPanel.add( optionLegendPane, BorderLayout.SOUTH );
+//
+//      final JSplitPane mainSplitPane = new PositioningSplitPane( JSplitPane.HORIZONTAL_SPLIT, sketchPanel, rightPanel );
+//      mainSplitPane.setDividerLocation( 0.8d );
+//
+//      // DEBUG
+////      final InfoPanel infoPanel = new InfoPanel();
+////      infoPanel.setTimeline( timeline );
+////      final JSplitPane debugPanel = new PositioningSplitPane( JSplitPane.HORIZONTAL_SPLIT, mainSplitPane, infoPanel );
+////      debugPanel.setDividerLocation( 0.95d );
+//
+//      final ZoomSlider zoomSlider = new TimeZoomSlider( semanticsStack, timeline );
+//      spiralBar.setZoomSlider( zoomSlider );
+////      add( debugPanel, BorderLayout.CENTER );
+//      return mainSplitPane;
+//   }
+//
+//
+//
+//   static private TimelineStack createSemanticStack( final SemanticInfo semanticInfo,
+//                                                     final Timeline timeline,
+//                                                     final VerticalMimicPanel typeHeaderStack,
+//                                                     final VerticalMimicPanel buttonFooterStack ) {
+//      final Timeline semanticTimeline = new SemanticTypeTimeline( semanticInfo.__name, timeline, semanticInfo.__type );
+//      final TimeSpanSelectionModel selectionModel = new DefaultTimeSpanSelectionModel( semanticInfo.__type );
+//      SelectionForwarder.getInstance().addSelectionModel( selectionModel );
+//      final TimelineStack timelineStack = createTimelineStack( typeHeaderStack, buttonFooterStack, semanticInfo.__labels );
+//      timelineStack.addTimeline( semanticTimeline, semanticInfo.__color, timeline, selectionModel, false );
+//      return timelineStack;
+//   }
+//
+//   static private TimelineStack createTimelineStack( final VerticalMimicPanel typeHeaderStack,
+//                                                     final VerticalMimicPanel buttonFooterStack,
+//                                                     final Collection<String> labels ) {
+//      // handle headers
+//      final TimelineStack timelineStack = new TimelineStack();
+//      final JComponent timelineTitleStack = timelineStack.getTitleStack();
+//      final JComponent removeTimelineStack = timelineStack.getRemoveButtonStack();
+//
+//      final JPanel labelPanel = new JPanel( new BorderLayout() );
+//      labelPanel.add( timelineTitleStack, BorderLayout.CENTER );
+//
+//      typeHeaderStack.addComponentFor( timelineStack, labelPanel );
+//      buttonFooterStack.addComponentFor( timelineStack, removeTimelineStack );
+//
+//      return timelineStack;
+//   }
+//
+//   static private JComponent createAllEventsTimeline( final Timeline timeline,
+//                                                      final String title,
+//                                                      final VerticalMimicPanel typeHeaderStack ) {
+//      final JTimelineComponent jTimeline = new JTimelineComponent( timeline, timeline );
+//      SelectionForwarder.getInstance().addSelectionModel( jTimeline.getSelectionModel() );
+//      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, 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;
+//         }
+//      } );
+//
+//      // handle headers - this is the "All Events" Label
+//      final JLabel semanticTypeLabel = new JLabel( title + " " );
+//      semanticTypeLabel.setHorizontalAlignment( JLabel.CENTER );
+//      final Font oldFont = semanticTypeLabel.getFont();
+//      final Font newFont = oldFont.deriveFont( Font.BOLD, oldFont.getSize2D() + 3 );
+//      semanticTypeLabel.setFont( newFont );
+////      semanticTypeLabel.setUI( new VerticalLabelUI( false ) );
+//      typeHeaderStack.addComponentFor( jTimeline, semanticTypeLabel );
+//      return jTimeline;
+//   }
+//
+//
+//   // For resizing the calendar when the semantic stack (main panel) is resized (zoom, etc.)
+//   static private class MimicComponentWidthAdapter extends ComponentAdapter {
+//      final Component __component;
+//      private MimicComponentWidthAdapter( final Component component ) {
+//         __component = component;
+//      }
+//      private void resize( final ComponentEvent event ) {
+//         final int width = __component.getPreferredSize().width;
+//         final int eWidth = event.getComponent().getPreferredSize().width;
+//         if ( width != eWidth ) {
+//            __component.setPreferredSize( new Dimension( eWidth, __component.getPreferredSize().height ) );
+//            __component.repaint();
+//         }
+//      }
+//      @Override
+//      public void componentResized( final ComponentEvent e ) {
+//         resize( e );
+//      }
+//      @Override
+//      public void componentMoved( ComponentEvent e ) {
+//         resize( e );
+//      }
+//      @Override
+//      public void componentShown( ComponentEvent e ) {
+//         resize( e );
+//      }
+//   }
+//
+//
+//   private class NoteSelectionListener implements ListSelectionListener {
+//      @Override
+//      public void valueChanged( final ListSelectionEvent event ) {
+//         if ( event.getValueIsAdjusting() ) {
+//            return;
+//         }
+//         final Object source = event.getSource();
+//         if ( source instanceof NoteNavigator ) {
+//            setNote( ((NoteNavigator)source).getSelectedNote() );
+//         }
+//      }
+//   }
+//
+//
+//}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/PatientInfoPanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/PatientInfoPanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/PatientInfoPanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/PatientInfoPanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,39 @@
+package org.chboston.cnlp.timeline.gui.main;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 4/24/14
+ */
+final public class PatientInfoPanel extends Box {
+
+   public PatientInfoPanel() {
+      super( BoxLayout.Y_AXIS );
+      setBorder( new EmptyBorder( 5, 10, 5, 0 ) );
+   }
+
+   public void setPatientInfo( final long docTimeMillis ) {
+      add( createPatientLabel( "Date:", "05/02/2014" ) );
+      add( createPatientLabel( "Name:", "Dan Testing" ) );
+      add( createPatientLabel( "Sex:", "Female" ) );
+      add( createPatientLabel( "Birth:", "06/23/1958" ) );
+      add( createPatientLabel( "Age:", "55" ) );
+      add( createPatientLabel( "Height:", "2 m" ) );
+      add( createPatientLabel( "Weight:", "100 kg" ) );
+   }
+
+   static private JComponent createPatientLabel( final String key, final String value ) {
+      final JLabel label = new JLabel( key );
+      label.setFont( label.getFont().deriveFont( Font.BOLD ) );
+      label.setPreferredSize( new Dimension( 60, 18 ) );
+      final JPanel panel = new JPanel( new BorderLayout( 5, 0 ) );
+      panel.add( label, BorderLayout.WEST );
+      panel.add( new JLabel( value ), BorderLayout.CENTER );
+      return panel;
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/TimelineMain.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/TimelineMain.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/TimelineMain.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/main/TimelineMain.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,115 @@
+package org.chboston.cnlp.timeline.gui.main;
+
+import org.apache.log4j.Logger;
+import org.chboston.cnlp.timeline.gui.timespan.selection.SelectionForwarder;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/8/13
+ */
+final public class TimelineMain {
+
+   static private final Logger LOGGER = Logger.getLogger( "TimelineMain" );
+
+   private final JFrame _frame;
+
+   private TimelineMain() {
+      _frame = new JFrame( "TimeLanes" );
+
+      _frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
+      // Use 1024 x 768 as the minimum required resolution (XGA)
+      // iPhone 3 : 480 x 320 (3:2, HVGA)
+      // iPhone 4 : 960 x 640  (3:2, unique to Apple)
+      // iPhone 5 : 1136 x 640 (under 16:9, unique to Apple)
+      // iPad 3&4 : 2048 x 1536 (4:3, QXGA)
+      // iPad Mini: 1024 x 768 (4:3, XGA)
+      final Dimension size = new Dimension( 1024, 768 );
+      _frame.setSize( size );
+      _frame.setMinimumSize( size );
+      final JMenuBar menuBar = new JMenuBar();
+      final JMenu fileMenu = new JMenu( "File" );
+      menuBar.add( fileMenu );
+      final JMenu viewMenu = new JMenu( "View" );
+      final JMenu fontSizeMenu = new JMenu( "Font Size" );
+      viewMenu.add( fontSizeMenu );
+      final JSlider fontSizer = new JSlider( 1, 10, 3 );
+      final BoundedRangeModel sizerModel = fontSizer.getModel();
+      sizerModel.addChangeListener( new FontSizerListener() );
+      fontSizeMenu.add( fontSizer );
+      menuBar.add( viewMenu );
+      _frame.setJMenuBar( menuBar );
+      System.setProperty( "apple.laf.useScreenMenuBar", "true" );
+
+      showTimeline();
+   }
+
+   private void showTimeline() {
+      SelectionForwarder.getInstance().removeAllSelectionModels();
+      final Container contentPane = _frame.getContentPane();
+      contentPane.removeAll();
+      contentPane.add( new MainTimelinePanel() );
+      _frame.pack();
+      _frame.setVisible( true );
+   }
+
+
+   private class FontSizerListener implements ChangeListener {
+      private int __oldValue = 3;
+
+      @Override
+      public void stateChanged( final ChangeEvent event ) {
+         if ( event.getSource() instanceof BoundedRangeModel ) {
+            final int value = ((BoundedRangeModel)event.getSource()).getValue();
+            if ( value == __oldValue ) {
+               return;
+            }
+            final int delta = value - __oldValue;
+            resizeFont( _frame, delta );
+            __oldValue = value;
+         }
+      }
+
+      private void resizeFont( final Component comp, final int delta ) {
+         if ( comp == null ) {
+            return;
+         }
+         final Font font = comp.getFont();
+         if ( font == null ) {
+            return;
+         }
+         final float newSize = font.getSize() + delta;
+         final Font newFont = font.deriveFont( newSize );
+         comp.setFont( newFont );
+         if ( comp instanceof Container ) {
+            resizeChildFonts( (Container)comp, delta );
+         }
+      }
+
+      private void resizeChildFonts( final Container container, final int delta ) {
+         final int childCount = container.getComponentCount();
+         for ( int i = 0; i < childCount; i++ ) {
+            final Component child = container.getComponent( i );
+            resizeFont( child, delta );
+         }
+      }
+   }
+
+
+   public static void main( String... args ) {
+      try {
+         UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
+         UIManager.getDefaults().put( "SplitPane.border", BorderFactory.createEmptyBorder() );
+      } catch ( ClassNotFoundException | InstantiationException
+            | IllegalAccessException | UnsupportedLookAndFeelException mtE ) {
+         LOGGER.error( mtE.getLocalizedMessage() );
+      }
+      new TimelineMain();
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/misc/TimeVisOptionPane.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/misc/TimeVisOptionPane.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/misc/TimeVisOptionPane.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/misc/TimeVisOptionPane.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,124 @@
+package org.chboston.cnlp.timeline.gui.misc;
+
+import org.chboston.cnlp.timeline.gui.qaclipper.SimpleTimelineWriter;
+import org.chboston.cnlp.timeline.gui.qaclipper.TimelineAnaforaWriter1;
+import org.chboston.cnlp.timeline.gui.qaclipper.TimelineAnaforaWriter5;
+import org.chboston.cnlp.timeline.gui.spiral.JSpiralBar;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+
+import javax.swing.*;
+import javax.swing.text.JTextComponent;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/20/12
+ */
+public class TimeVisOptionPane extends JPanel {
+
+
+   public TimeVisOptionPane( final JSpiralBar spiralBar, final JTextComponent textComponent ) {
+//      super( new GridLayout( 6, 1 ) );
+      super( new GridLayout( 2, 1 ) );
+      final JCheckBox zoomBox = new JCheckBox( new ZoomToggle( spiralBar ) );
+      zoomBox.setSelected( false );
+      spiralBar.setZoomToSelection( false );
+      add( zoomBox );
+
+//      final JLabel dos2unixLabel = new JLabel( "Annotation Text Format:" );
+//      final JRadioButton dosRadio = new JRadioButton( "Windows" );
+//      final JRadioButton unixRadio = new JRadioButton( "Linux" );
+//      final ButtonGroup dos2unix = new ButtonGroup();
+//      dos2unix.add( dosRadio );
+//      dos2unix.add( unixRadio );
+//      if ( textComponent.getText().contains( "\r\n" ) ) {
+//         dosRadio.setSelected( true );
+//      } else {
+//         unixRadio.setSelected( true );
+//      }
+//      dosRadio.setAction( new DosAction( textComponent ) );
+//      unixRadio.setAction( new UnixAction( textComponent ) );
+//      add( new JLabel( " " ) );
+//      add( dos2unixLabel );
+//      add( dosRadio );
+//      add( unixRadio );
+
+
+      add( new JButton( new SaveTimelineAction( spiralBar ) ) );
+
+      setSize( 20, 100 );
+   }
+
+
+   static private class ZoomToggle extends AbstractAction {
+      private final JSpiralBar __spiralBar;
+
+      private ZoomToggle( final JSpiralBar spiralBar ) {
+         super( "Zoom to Selection" );
+         __spiralBar = spiralBar;
+      }
+
+      public void actionPerformed( final ActionEvent event ) {
+         if ( event.getSource() instanceof JCheckBox ) {
+            __spiralBar.setZoomToSelection( ((JCheckBox)event.getSource()).isSelected() );
+         }
+      }
+   }
+
+   static private class SaveTimelineAction extends AbstractAction {
+      private final JSpiralBar __spiralBar;
+
+      private SaveTimelineAction( final JSpiralBar spiralBar ) {
+         super( "Save Timeline" );
+         __spiralBar = spiralBar;
+      }
+
+      public void actionPerformed( final ActionEvent event ) {
+         final Timeline timeline = __spiralBar.getModel();
+         if ( timeline != null ) {
+            SimpleTimelineWriter.writeTimeline( timeline.getTitle(), timeline );
+            TimelineAnaforaWriter1.writeTimeline( timeline.getTitle(), timeline );
+            TimelineAnaforaWriter5.writeTimeline( timeline.getTitle(), timeline );
+         }
+      }
+   }
+
+   static private class DosAction extends AbstractAction {
+      private final JTextComponent __textComponent;
+
+      private DosAction( final JTextComponent textComponent ) {
+         super( "Windows" );
+         putValue( LONG_DESCRIPTION, "reformat text with Windows line endings" );
+         putValue( SHORT_DESCRIPTION, "reformat text with Windows line endings" );
+         __textComponent = textComponent;
+      }
+
+      public void actionPerformed( final ActionEvent event ) {
+         // first make certain that we aren't making "\r\r\r\n" sequences by going to unix first then dos
+         final String unixText = __textComponent.getText().replace( "\r\n", "\n" );
+         final String dosText = unixText.replace( "\n", "\r\n" );
+         __textComponent.setText( dosText );
+         __textComponent.repaint();
+      }
+   }
+
+   static private class UnixAction extends AbstractAction {
+      private final JTextComponent __textComponent;
+
+      private UnixAction( final JTextComponent textComponent ) {
+         super( "Linux" );
+         putValue( LONG_DESCRIPTION, "reformat text with Linux line endings" );
+         putValue( SHORT_DESCRIPTION, "reformat text with Linux line endings" );
+         __textComponent = textComponent;
+      }
+
+      public void actionPerformed( final ActionEvent event ) {
+         final String unixText = __textComponent.getText().replace( "\r\n", "\n" );
+         __textComponent.setText( unixText );
+         __textComponent.repaint();
+      }
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClip.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClip.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClip.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClip.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,270 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.nlp.annotation.textspan.TextSpan;
+import org.chboston.cnlp.nlp.annotation.textspan.TextSpanComparator;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+public class QaClip {
+
+   static public enum Confidence {
+      Certain, Medium, Uncertain
+   }
+
+   static public enum Difficulty {
+      Difficult, Medium, Easy
+   }
+
+   static public enum QaDocTimeRel {
+      Before, Overlap, After, NA
+   }
+
+   private String _question;
+   private String _answer;
+   private Confidence _confidence;
+   private Difficulty _difficulty;
+   private QaDocTimeRel _qaDocTimeRel;
+
+   final private List<TextClip> _textClips = new ArrayList<>();
+
+   /**
+    * @return question
+    */
+   public String getQuestion() {
+      return _question;
+   }
+
+   /**
+    * @param question question
+    */
+   public void setQuestion( final String question ) {
+      if ( question != null ) {
+         _question = question;
+      }
+   }
+
+   /**
+    * @return answer
+    */
+   public String getAnswer() {
+      return _answer;
+   }
+
+   /**
+    * @param answer answer
+    */
+   public void setAnswer( final String answer ) {
+      if ( answer != null ) {
+         _answer = answer;
+      }
+   }
+
+   /**
+    * @return confidence
+    */
+   public Confidence getConfidence() {
+      if ( _confidence == null ) {
+         return Confidence.Medium;
+      }
+      return _confidence;
+   }
+
+   /**
+    * @param confidence confidence
+    */
+   public void setConfidence( final Confidence confidence ) {
+      if ( confidence != null ) {
+         _confidence = confidence;
+      }
+   }
+
+   /**
+    * @param name confidence name
+    */
+   public void setConfidence( final String name ) {
+      for ( Confidence confidence : Confidence.values() ) {
+         if ( name.equals( confidence.name() ) ) {
+            setConfidence( confidence );
+            return;
+         }
+      }
+   }
+
+   /**
+    * @return difficulty
+    */
+   public Difficulty getDifficulty() {
+      if ( _difficulty == null ) {
+         return Difficulty.Medium;
+      }
+      return _difficulty;
+   }
+
+   /**
+    * @param difficulty difficulty
+    */
+   public void setDifficulty( final Difficulty difficulty ) {
+      if ( difficulty != null ) {
+         _difficulty = difficulty;
+      }
+   }
+
+   /**
+    * @param name difficulty name
+    */
+   public void setDifficulty( final String name ) {
+      for ( Difficulty difficulty : Difficulty.values() ) {
+         if ( name.equals( difficulty.name() ) ) {
+            setDifficulty( difficulty );
+            return;
+         }
+      }
+   }
+
+   /**
+    * @return qaDocTimeRel
+    */
+   public QaDocTimeRel getQaDocTimeRel() {
+      if ( _qaDocTimeRel == null ) {
+         return QaDocTimeRel.NA;
+      }
+      return _qaDocTimeRel;
+   }
+
+   /**
+    * @param qaDocTimeRel qaDocTimeRel
+    */
+   public void setQaDocTimeRel( final QaDocTimeRel qaDocTimeRel ) {
+      if ( qaDocTimeRel != null ) {
+         _qaDocTimeRel = qaDocTimeRel;
+      }
+   }
+
+   /**
+    * @param name difficulty name
+    */
+   public void setQaDocTimeRel( final String name ) {
+      for ( QaDocTimeRel docTimeRel : QaDocTimeRel.values() ) {
+         if ( name.equals( docTimeRel.name() ) ) {
+            setQaDocTimeRel( docTimeRel );
+            return;
+         }
+      }
+   }
+
+
+   /**
+    * @return textClips
+    */
+   public List<TextClip> getTextClips() {
+      return _textClips;
+   }
+
+   /**
+    * @param textClips textClips
+    */
+   public void setTextClips( final List<TextClip> textClips ) {
+      _textClips.clear();
+      if ( textClips != null ) {
+         _textClips.addAll( textClips );
+      }
+   }
+
+   public void addTextClip( final TextClip textClip ) {
+      _textClips.add( textClip );
+   }
+
+   static public final class TextClip {
+      private final TextSpan __textSpan;
+      private int __order;
+      private String __subOrder;
+      private boolean __isExactAnswer = false;
+      private boolean __useDocTimeRel = false;
+      private String[] __additionalInfo = new String[ 5 ];
+
+      public TextClip( final TextSpan textSpan, final int order, final String subOrder ) {
+         __textSpan = textSpan;
+         __order = order;
+         __subOrder = subOrder;
+      }
+
+      public TextSpan getTextSpan() {
+         return __textSpan;
+      }
+
+      public int getOrder() {
+         return __order;
+      }
+
+      public void setOrder( final int order ) {
+         __order = order;
+      }
+
+      public String getSubOrder() {
+         return __subOrder;
+      }
+
+      public void setSubOrder( final String subOrder ) {
+         __subOrder = subOrder;
+      }
+
+      public boolean isExactAnswer() {
+         return __isExactAnswer;
+      }
+
+      public void setIsExactAnswer( final boolean isExactAnswer ) {
+         __isExactAnswer = isExactAnswer;
+      }
+
+      public boolean useDocTimeRel() {
+         return __useDocTimeRel;
+      }
+
+      public void setUseDocTimeRel( final boolean useDocTimeRel ) {
+         __useDocTimeRel = useDocTimeRel;
+      }
+
+      public void setInfo( final int index, final String info ) {
+         if ( index >= 0 && index < __additionalInfo.length ) {
+            __additionalInfo[ index ] = info;
+         }
+      }
+
+      public String[] getInfo() {
+         return __additionalInfo;
+      }
+   }
+
+   static public enum TextClipComparator implements Comparator<TextClip> {
+      INSTANCE;
+
+      static public TextClipComparator getInstance() {
+         return INSTANCE;
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public int compare( final TextClip textClip1, final TextClip textClip2 ) {
+         if ( textClip1 == null || textClip2 == null ) {
+            return 0;
+         }
+         final int orderDiff = textClip1.getOrder() - textClip2.getOrder();
+         if ( orderDiff != 0 ) {
+            return orderDiff;
+         }
+         final TextSpan textSpan1 = textClip1.getTextSpan();
+         final TextSpan textSpan2 = textClip2.getTextSpan();
+         return TextSpanComparator.getInstance().compare( textSpan1, textSpan2 );
+      }
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipAnaforaWriter.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipAnaforaWriter.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipAnaforaWriter.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipAnaforaWriter.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,159 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.nlp.annotation.textspan.TextSpan;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 5/2/14
+ */
+public class QaClipAnaforaWriter {
+
+   static private final String ROOT_ELEMENT_NAME = "data";
+   static private final String ANNOTATION_ELEMENT_NAME = "annotations";
+   static private final String ENTITY_ELEMENT_NAME = "entity";
+   static private final String RELATION_ELEMENT_NAME = "relation";
+   static private final String ANNOTATION_ID_ELEMENT_NAME = "id";
+   static private final String ANNOTATION_SPAN_ELEMENT_NAME = "span";
+   static private final String ANNOTATION_TYPE_ELEMENT_NAME = "type";
+   static private final String ANNOTATION_PROPERTIES_ELEMENT_NAME = "properties";
+   static private final String RELATION_SOURCE_PROPERTY_NAME = "source";
+   static private final String RELATION_TARGET_PROPERTY_NAME = "target";
+
+
+   final private Map<QaClip.TextClip, Integer> _textClipIds = new HashMap<>();
+   private int _id = 0;
+
+   public void saveQaClips( final String textFilePath, final Collection<QaClip> qaClips ) {
+      try ( Writer writer = new BufferedWriter( new FileWriter( textFilePath + ".xml" ) ) ) {
+         final Element root = new Element( ROOT_ELEMENT_NAME );
+         final Document doc = new Document( root );
+         doc.setRootElement( root );
+         final Element annotations = new Element( ANNOTATION_ELEMENT_NAME );
+         for ( QaClip qaClip : qaClips ) {
+            addQaClip( annotations, qaClip );
+         }
+         root.addContent( annotations );
+         final XMLOutputter outputter = new XMLOutputter();
+         outputter.setFormat( Format.getPrettyFormat() );
+         outputter.output( doc, writer );
+      } catch ( IOException ioE ) {
+         System.err.println( "Couldn't save QaClip: " + ioE.getMessage() );
+      }
+   }
+
+
+   private void addQaClip( final Element annotations,
+                           final QaClip qaClip ) throws IOException {
+      final List<QaClip.TextClip> textClips = qaClip.getTextClips();
+      for ( QaClip.TextClip textClip : textClips ) {
+         final Element entity = createTextClipEntity( textClip );
+         if ( entity != null ) {
+            annotations.addContent( entity );
+         }
+      }
+      final int size = textClips.size();
+      if ( size <= 1 ) {
+         return;
+      }
+      for ( int i = 0; i < size - 1; i++ ) {
+//         for ( int j=1; j<size; j++ ) {
+//            final Element relation = createTextClipRelation( textClips.get( i ), textClips.get( j ) );
+         final Element relation = createTextClipRelation( textClips.get( i ), textClips.get( i + 1 ) );
+         if ( relation != null ) {
+            annotations.addContent( relation );
+         }
+//         }
+      }
+   }
+
+   //      <entity>
+   //         <id>37@e@ID001_path_002@ahoward</id>
+   //         <span>118,137</span>
+   //         <type>EVENT</type>
+   //         <parentsType>TemporalEntities</parentsType>
+   //         <properties>
+   //            <DocTimeRel>BEFORE</DocTimeRel>
+   //            <Type>N/A</Type>
+   //            <Degree>N/A</Degree>
+   //            <Polarity>POS</Polarity>
+   //            <ContextualModality>ACTUAL</ContextualModality>
+   //            <ContextualAspect>N/A</ContextualAspect>
+   //            <Permanence>UNDETERMINED</Permanence>
+   //         </properties>
+   //      </entity>
+
+
+   private Element createTextClipEntity( final QaClip.TextClip textClip ) throws IOException {
+      if ( textClip == null ) {
+         return null;
+      }
+      final TextSpan textSpan = textClip.getTextSpan();
+      final Element entity = new Element( ENTITY_ELEMENT_NAME );
+      entity.addContent( new Element( ANNOTATION_ID_ELEMENT_NAME ).setText( "" + _id ) );
+      entity.addContent( new Element( ANNOTATION_SPAN_ELEMENT_NAME )
+            .setText( textSpan.getStartIndex() + "," + textSpan.getEndIndex() ) );
+      entity.addContent( new Element( ANNOTATION_TYPE_ELEMENT_NAME ).setText( "EVENT" ) );
+      entity.addContent( new Element( "parentsType" ).setText( "TemporalEntities" ) );
+      final Element properties = new Element( ANNOTATION_PROPERTIES_ELEMENT_NAME );
+      properties.addContent( new Element( "DocTimeRel" ).setText( "OVERLAP" ) );
+      entity.addContent( properties );
+      _textClipIds.put( textClip, _id );
+      _id++;
+      return entity;
+   }
+
+   //      <relation>
+   //         <id>1@r@ID001_path_002@ahoward</id>
+   //         <type>TLINK</type>
+   //         <parentsType>TemporalRelations</parentsType>
+   //         <properties>
+   //            <Source>14@e@ID001_path_002@ahoward</Source>
+   //            <Target>12@e@ID001_path_002@ahoward</Target>
+   //            <Type>AFTER</Type>
+   //         </properties>
+   //      </relation>
+
+
+   private Element createTextClipRelation( final QaClip.TextClip textClipA,
+                                           final QaClip.TextClip textClipB ) throws IOException {
+      if ( textClipA == null || textClipB == null ) {
+         return null;
+      }
+      final Integer orderA = textClipA.getOrder();
+      final Integer orderB = textClipA.getOrder();
+//      final String subOrder = textClip.getSubOrder();
+      final Element relation = new Element( RELATION_ELEMENT_NAME );
+      relation.addContent( new Element( ANNOTATION_ID_ELEMENT_NAME ).setText( "" + _id ) );
+      relation.addContent( new Element( ANNOTATION_TYPE_ELEMENT_NAME ).setText( "TLINK" ) );
+      relation.addContent( new Element( "parentsType" ).setText( "TemporalRelations" ) );
+      final Element properties = new Element( ANNOTATION_PROPERTIES_ELEMENT_NAME );
+      final int sourceId = _textClipIds.get( textClipA );
+      final int targetId = _textClipIds.get( textClipB );
+      properties.addContent( new Element( RELATION_SOURCE_PROPERTY_NAME ).setText( "" + sourceId ) );
+      properties.addContent( new Element( RELATION_TARGET_PROPERTY_NAME ).setText( "" + targetId ) );
+      if ( orderA < orderB ) {
+         properties.addContent( new Element( ANNOTATION_TYPE_ELEMENT_NAME ).setText( "BEFORE" ) );
+      } else {
+         properties.addContent( new Element( ANNOTATION_TYPE_ELEMENT_NAME ).setText( "OVERLAP" ) );
+      }
+      relation.addContent( properties );
+      _id++;
+      return relation;
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipLoader.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipLoader.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipLoader.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipLoader.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,185 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.gui.error.ErrorListener;
+import org.chboston.cnlp.gui.error.ErrorProducer;
+import org.chboston.cnlp.nlp.annotation.textspan.DefaultTextSpan;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+public class QaClipLoader {
+
+   final private ErrorProducer _errorProducer = new ErrorProducer();
+   private String _comment = "";
+
+   public String getComment() {
+      return _comment;
+   }
+
+   public List<QaClip> loadQaClips( final String textFilePath ) {
+      _comment = "";
+      final List<QaClip> qaClips = new ArrayList<>();
+      final File file = new File( textFilePath + QaClipSaver.FILE_EXTENSION );
+      if ( !file.exists() ) {
+         return qaClips;
+      }
+      try {
+         final BufferedReader reader = new BufferedReader( new FileReader( file ) );
+         _comment = loadHeader( reader );
+         QaClip qaClip = loadQaClip( reader );
+         while ( qaClip != null ) {
+            qaClips.add( qaClip );
+            qaClip = loadQaClip( reader );
+         }
+         reader.close();
+      } catch ( IOException ioE ) {
+         fireErrorOccurred( "Couldn't load QaClips: " + ioE.getMessage() );
+      }
+      return qaClips;
+   }
+
+   private String loadHeader( final BufferedReader reader ) throws IOException {
+      final StringBuilder stringBuilder = new StringBuilder();
+      boolean readingComment = false;
+      String line = reader.readLine();
+      while ( line != null && !line.equals( QaClipSaver.HEADER_END ) ) {
+         line = reader.readLine();
+         if ( readingComment && !line.equals( QaClipSaver.HEADER_END ) ) {
+            stringBuilder.append( line ).append( "\n" );
+         } else if ( line.startsWith( "Comment:  " ) ) {
+            stringBuilder.append( line.substring( 10 ) ).append( "\n" );
+            readingComment = true;
+         }
+      }
+      return stringBuilder.toString();
+   }
+
+   private QaClip loadQaClip( final BufferedReader reader ) throws IOException {
+      final QaClip qaClip = new QaClip();
+      String questionLine = reader.readLine();
+      if ( questionLine != null && questionLine.isEmpty() ) {
+         questionLine = reader.readLine();
+      }
+      if ( questionLine == null ) {
+         return null;
+      }
+      if ( !questionLine.startsWith( QaClipSaver.QUESTION_LABEL ) ) {
+         fireErrorOccurred( "Malformed Question: " + questionLine );
+         return null;
+      }
+      qaClip.setQuestion( questionLine.substring( QaClipSaver.QUESTION_LABEL.length() ) );
+      final String answerLine = reader.readLine();
+      if ( !answerLine.startsWith( QaClipSaver.ANSWER_LABEL ) ) {
+         fireErrorOccurred( "Malformed Answer: " + answerLine );
+         return null;
+      }
+      final String answer = answerLine.substring( QaClipSaver.ANSWER_LABEL.length() );
+      qaClip.setAnswer( answerLine.substring( QaClipSaver.ANSWER_LABEL.length() ) );
+      if ( answer.equals( QaClipSaver.NO_ANSWER ) ) {
+         // skip the next question separator
+         reader.readLine();
+         return qaClip;
+      }
+      final String confidenceLine = reader.readLine();
+      if ( !confidenceLine.startsWith( QaClipSaver.CONFIDENCE_LABEL ) ) {
+         fireErrorOccurred( "Malformed Confidence: " + confidenceLine );
+         return null;
+      }
+      qaClip.setConfidence( confidenceLine.substring( QaClipSaver.CONFIDENCE_LABEL.length() ) );
+      final String difficultyLine = reader.readLine();
+      if ( !difficultyLine.startsWith( QaClipSaver.DIFFICULTY_LABEL ) ) {
+         fireErrorOccurred( "Malformed Difficulty: " + difficultyLine );
+         return null;
+      }
+      qaClip.setDifficulty( difficultyLine.substring( QaClipSaver.DIFFICULTY_LABEL.length() ) );
+      final String docTimeRelLine = reader.readLine();
+      if ( !docTimeRelLine.startsWith( QaClipSaver.DOCTIMEREL_LABEL ) ) {
+         fireErrorOccurred( "Malformed DocTimeRel: " + docTimeRelLine );
+         return null;
+      }
+      qaClip.setQaDocTimeRel( docTimeRelLine.substring( QaClipSaver.DOCTIMEREL_LABEL.length() ) );
+      String textClipLine = reader.readLine();
+      while ( textClipLine != null && !textClipLine.equals( QaClipSaver.QA_CLIP_END ) ) {
+         if ( textClipLine.startsWith( QaClipSaver.TEXT_CLIP_LABEL ) ) {
+            final QaClip.TextClip textClip = loadTextClip( textClipLine
+                  .substring( QaClipSaver.TEXT_CLIP_LABEL.length() ) );
+            qaClip.addTextClip( textClip );
+         }
+         textClipLine = reader.readLine();
+      }
+      return qaClip;
+   }
+
+   private QaClip.TextClip loadTextClip( final String textClipLine ) {
+      final String[] splits = textClipLine.split( "\\s+" );
+      if ( splits.length < 4 ) {
+         fireErrorOccurred( "Malformed Text Span: " + textClipLine );
+         return null;
+      }
+      final String[] startStop = splits[ 1 ].split( "," );
+      if ( startStop.length != 2 ) {
+         fireErrorOccurred( "Malformed Text Span: " + textClipLine );
+         return null;
+      }
+      String orderString = splits[ 0 ];
+      String subOrder = "";
+      final int orderDotIndex = splits[ 0 ].indexOf( '.' );
+      if ( orderDotIndex > 0 ) {
+         orderString = splits[ 0 ].substring( 0, orderDotIndex );
+         if ( splits[ 0 ].length() > orderDotIndex + 1 ) {
+            subOrder = splits[ 0 ].substring( orderDotIndex + 1 );
+         }
+      }
+      Integer order;
+      Integer start;
+      Integer stop;
+      try {
+         order = Integer.parseInt( orderString );
+         start = Integer.parseInt( startStop[ 0 ] );
+         stop = Integer.parseInt( startStop[ 1 ] );
+      } catch ( NumberFormatException nfE ) {
+         fireErrorOccurred( "Malformed Text Span: " + textClipLine );
+         return null;
+      }
+      final QaClip.TextClip textClip = new QaClip.TextClip( new DefaultTextSpan( start, stop ), order, subOrder );
+      textClip.setIsExactAnswer( splits[ 2 ].equals( QaClipSaver.EXACT_ANSWER_CODE ) );
+      textClip.setUseDocTimeRel( splits[ 3 ].equals( QaClipSaver.USE_DOCTIMEREL_CODE ) );
+      if ( splits.length > 4 ) {
+         int lastBarIndex = -1;
+         int nextBarIndex = -1;
+         for ( int i = 0; i < 5; i++ ) {
+            nextBarIndex = splits[ 4 ].indexOf( '|', lastBarIndex + 1 );
+            if ( nextBarIndex < 0 ) {
+               break;
+            }
+            if ( nextBarIndex != lastBarIndex + 1 ) {
+               textClip.setInfo( i, splits[ 4 ].substring( lastBarIndex + 1, nextBarIndex ) );
+            }
+            lastBarIndex = nextBarIndex;
+         }
+      }
+      return textClip;
+   }
+
+   private void fireErrorOccurred( final String error ) {
+      _errorProducer.fireErrorOccurred( error );
+   }
+
+   private void fireErrorCleared() {
+      _errorProducer.fireErrorCleared();
+   }
+
+   public void addErrorListener( final ErrorListener listener ) {
+      _errorProducer.addErrorListener( listener );
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipSaver.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipSaver.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipSaver.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaClipSaver.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,145 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.gui.error.ErrorListener;
+import org.chboston.cnlp.gui.error.ErrorProducer;
+import org.chboston.cnlp.nlp.annotation.textspan.TextSpan;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+public class QaClipSaver {
+
+   static public final String HEADER_END
+         = "   =================================================================================";
+
+   static public final String QA_CLIP_END
+         = "   ---------------------------------------------------------------------------------";
+
+   static public final String QUESTION_LABEL = "Question:   ";
+   static public final String ANSWER_LABEL = "Answer:     ";
+   static public final String CONFIDENCE_LABEL = "Confidence: ";
+   static public final String DIFFICULTY_LABEL = "Difficulty: ";
+   static public final String TEXT_CLIP_LABEL = "Text Clip:  ";
+   static public final String DOCTIMEREL_LABEL = "DocTimeRel: ";
+   static public final String EXACT_ANSWER_CODE = "Exact_Answer";
+   static public final String SUPPORT_ANSWER_CODE = "Support_Answer";
+   static public final String USE_DOCTIMEREL_CODE = "Use_DocTimeRel";
+   static public final String USE_TIME_SPAN_CODE = "Use_Time_Span";
+   static public final String NO_ANSWER = "No answer can be determined from the document";
+
+   static public final String INFO_LABEL_A = "A: ";
+   static public final String INFO_LABEL_B = "B: ";
+   static public final String INFO_LABEL_C = "C: ";
+   static public final String INFO_LABEL_D = "D: ";
+   static public final String INFO_LABEL_E = "E: ";
+
+
+   static public final String FILE_EXTENSION = "_qa.txt";
+
+   final private ErrorProducer _errorProducer = new ErrorProducer();
+
+   public void saveQaClips( final String textFilePath, final String comment, final Collection<QaClip> qaClips,
+                            final String fullText ) {
+      try {
+         final BufferedWriter writer = new BufferedWriter( new FileWriter( textFilePath + FILE_EXTENSION ) );
+         saveHeader( writer, textFilePath, comment );
+         for ( QaClip qaClip : qaClips ) {
+            saveQaClip( writer, qaClip, fullText );
+         }
+         writer.close();
+      } catch ( IOException ioE ) {
+         fireErrorOccurred( "Couldn't save QaClip: " + ioE.getMessage() );
+      }
+   }
+
+   private void saveQaClip( final BufferedWriter writer,
+                            final QaClip qaClip, final String fullText ) throws IOException {
+      final String question = qaClip.getQuestion();
+      saveLine( writer, QUESTION_LABEL + question );
+      final String answer = qaClip.getAnswer();
+      if ( answer.isEmpty() ) {
+         saveLine( writer, NO_ANSWER );
+         saveLine( writer, QA_CLIP_END );
+         return;
+      }
+      saveLine( writer, ANSWER_LABEL + answer );
+      final QaClip.Confidence confidence = qaClip.getConfidence();
+      saveLine( writer, CONFIDENCE_LABEL + confidence.name() );
+      final QaClip.Difficulty difficulty = qaClip.getDifficulty();
+      saveLine( writer, DIFFICULTY_LABEL + difficulty.name() );
+      final QaClip.QaDocTimeRel docTimeRel = qaClip.getQaDocTimeRel();
+      saveLine( writer, DOCTIMEREL_LABEL + docTimeRel );
+      saveTextClips( writer, qaClip, fullText );
+      saveLine( writer, QA_CLIP_END );
+   }
+
+   private void saveTextClips( final BufferedWriter writer,
+                               final QaClip qaClip, final String fullText ) throws IOException {
+      final List<QaClip.TextClip> textClips = qaClip.getTextClips();
+      for ( QaClip.TextClip textClip : textClips ) {
+         saveTextClip( writer, textClip, fullText );
+      }
+   }
+
+   private void saveTextClip( final BufferedWriter writer,
+                              final QaClip.TextClip textClip, final String fullText ) throws IOException {
+      if ( textClip == null ) {
+         return;
+      }
+      final TextSpan textSpan = textClip.getTextSpan();
+      final Integer order = textClip.getOrder();
+      final String subOrder = textClip.getSubOrder();
+      final String exact = textClip.isExactAnswer() ? EXACT_ANSWER_CODE : SUPPORT_ANSWER_CODE;
+      final String docTimeRel = textClip.useDocTimeRel() ? USE_DOCTIMEREL_CODE : USE_TIME_SPAN_CODE;
+      final StringBuilder infoBuilder = new StringBuilder();
+      final String[] infos = textClip.getInfo();
+      for ( String info : infos ) {
+         if ( info != null ) {
+            infoBuilder.append( info );
+         }
+         infoBuilder.append( '|' );
+      }
+      saveLine( writer, TEXT_CLIP_LABEL + order + "." + subOrder
+                        + " " + textSpan.getStartIndex() + "," + textSpan.getEndIndex()
+                        + " " + exact + " " + docTimeRel + " " + infoBuilder.toString() );
+      final String text = fullText.substring( textSpan.getStartIndex(), textSpan.getEndIndex() );
+      saveLine( writer, text );
+   }
+
+   static private void saveHeader( final BufferedWriter writer, final String textFilePath, final String comment )
+         throws IOException {
+      saveLine( writer, "Questions, desired answers, and ordered text selections for a text document" );
+      saveLine( writer, "Document:   " + textFilePath );
+      saveLine( writer, "Date/Time:  " + new Date().toString() );
+      saveLine( writer, "Comment:  " + comment );
+      saveLine( writer, HEADER_END );
+      writer.newLine();
+   }
+
+   static private void saveLine( final BufferedWriter writer, final String text ) throws IOException {
+      writer.write( text );
+      writer.newLine();
+   }
+
+   private void fireErrorOccurred( final String error ) {
+      _errorProducer.fireErrorOccurred( error );
+   }
+
+   private void fireErrorCleared() {
+      _errorProducer.fireErrorCleared();
+   }
+
+   public void addErrorListener( final ErrorListener listener ) {
+      _errorProducer.addErrorListener( listener );
+   }
+
+}



Mime
View raw message