001    /*
002     * The contents of this file are subject to the Mozilla Public
003     * License Version 1.1 (the "License"); you may not use this file
004     * except in compliance with the License. You may obtain a copy of
005     * the License at http://www.mozilla.org/MPL/
006     *
007     * Software distributed under the License is distributed on an "AS
008     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
009     * implied. See the License for the specific language governing
010     * rights and limitations under the License.
011     *
012     * The Original Code is Knowtator.
013     *
014     * The Initial Developer of the Original Code is University of Colorado.  
015     * Copyright (C) 2005 - 2008.  All Rights Reserved.
016     *
017     * Knowtator was developed by the Center for Computational Pharmacology
018     * (http://compbio.uchcs.edu) at the University of Colorado Health 
019     *  Sciences Center School of Medicine with support from the National 
020     *  Library of Medicine.  
021     *
022     * Current information about Knowtator can be obtained at 
023     * http://knowtator.sourceforge.net/
024     *
025     * Contributor(s):
026     *   Philip V. Ogren <philip@ogren.info> (Original Author)
027     */
028    
029    /**
030     * Changes:
031     * 8/11/2005    pvo   modified highlightSelectedInstance such that annotations 
032     *                    referenced by the selected instance are also highlighted.
033     */
034    package edu.uchsc.ccp.knowtator.ui;
035    
036    /**
037     * 
038     * changes 1/23/2006 added 
039     *              textPane.setFont(UIManager.getFont("TextArea.font")); 
040     * to initialize method
041     */
042    
043    import java.awt.BorderLayout;
044    import java.awt.Color;
045    import java.awt.Point;
046    import java.awt.Rectangle;
047    import java.io.PrintWriter;
048    import java.io.StringWriter;
049    import java.util.Comparator;
050    import java.util.List;
051    
052    import javax.swing.BorderFactory;
053    import javax.swing.JOptionPane;
054    import javax.swing.JPanel;
055    import javax.swing.JScrollBar;
056    import javax.swing.JScrollPane;
057    import javax.swing.UIManager;
058    import javax.swing.border.TitledBorder;
059    import javax.swing.event.ChangeEvent;
060    import javax.swing.event.ChangeListener;
061    import javax.swing.text.BadLocationException;
062    
063    import edu.stanford.smi.protege.model.SimpleInstance;
064    import edu.uchsc.ccp.knowtator.KnowtatorManager;
065    import edu.uchsc.ccp.knowtator.Span;
066    import edu.uchsc.ccp.knowtator.SpanUtil;
067    import edu.uchsc.ccp.knowtator.event.EventHandler;
068    import edu.uchsc.ccp.knowtator.event.FilterChangedEvent;
069    import edu.uchsc.ccp.knowtator.event.FilterChangedListener;
070    import edu.uchsc.ccp.knowtator.textsource.TextSource;
071    import edu.uchsc.ccp.knowtator.textsource.TextSourceChangeEvent;
072    import edu.uchsc.ccp.knowtator.textsource.TextSourceChangeListener;
073    
074    public class TextViewer implements TextSourceChangeListener, FilterChangedListener {
075            JPanel panel;
076    
077            KnowtatorTextPane textPane;
078    
079            JScrollPane scrollPane;
080    
081            JScrollBar verticalScrollBar;
082    
083            String textSourceDisplayText;
084    
085            String filterInstanceDisplayText;
086    
087            TitledBorder border;
088    
089            KnowtatorManager manager;
090    
091            Rectangle viewportBounds = null;
092    
093            public TextViewer(KnowtatorTextPane textPane, KnowtatorManager manager) {
094                    this.textPane = textPane;
095                    this.manager = manager;
096                    initialize();
097                    EventHandler.getInstance().addFilterChangedListener(this);
098            }
099    
100            public Color getSelectedTextColor() {
101                    Color selectionColor = textPane.getSelectionColor();
102                    return selectionColor;
103            }
104    
105            public void initialize() {
106                    scrollPane = new JScrollPane(textPane);
107                    scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
108                    scrollPane.getViewport().addChangeListener(new ChangeListener() {
109                            public void stateChanged(ChangeEvent event) {
110                                    manager.updateVisibleFilteredAnnotations();
111                                    manager.refreshAnnotationsDisplay(false);
112                            }
113                    });
114                    verticalScrollBar = scrollPane.getVerticalScrollBar();
115    
116                    panel = new JPanel(new BorderLayout());
117                    panel.add(scrollPane, BorderLayout.CENTER);
118                    border = BorderFactory.createTitledBorder("");
119                    border.setTitleFont(UIManager.getFont("Label.font"));
120                    panel.setBorder(border);
121    
122                    textPane.setScrollPane(scrollPane);
123            }
124    
125            public void filterChanged(FilterChangedEvent event) {
126                    SimpleInstance filter = event.getNewFilter();
127                    setFilterInstanceDisplayText(filter.getBrowserText());
128            }
129    
130            public JPanel getContentPane() {
131                    return panel;
132            }
133    
134            public void textSourceChanged(TextSourceChangeEvent event) {
135    
136                    try {
137                            TextSource textSource = event.getTextSource();
138                            setTextSourceDisplayText(textSource.getName());
139                            panel.repaint();
140                            panel.validate();
141                            textPane.setText(textSource.getText());
142                            verticalScrollBar.setValue(verticalScrollBar.getMinimum());
143                            manager.refreshAnnotationsDisplay(true);
144                    } catch (Exception exception) {
145                            StringWriter stackTraceWriter = new StringWriter();
146                            exception.printStackTrace(new PrintWriter(stackTraceWriter));
147                            String stackTrace = stackTraceWriter.toString();
148                            JOptionPane.showMessageDialog(panel, "Exception thrown when text source changed: " + "\n" + stackTrace,
149                                            "Exception", JOptionPane.ERROR_MESSAGE);
150                            exception.printStackTrace();
151                    }
152            }
153    
154            public void setTextSourceDisplayText(String textSourceDisplayText) {
155                    this.textSourceDisplayText = textSourceDisplayText;
156                    setBorderText();
157            }
158    
159            private void setFilterInstanceDisplayText(String filterInstanceDisplayText) {
160                    this.filterInstanceDisplayText = filterInstanceDisplayText;
161                    setBorderText();
162            }
163    
164            private void setBorderText() {
165                    String borderText = "";
166                    if (textSourceDisplayText != null)
167                            borderText = "text source: " + textSourceDisplayText;
168                    if (filterInstanceDisplayText != null)
169                            borderText = borderText + "        filter: " + filterInstanceDisplayText;
170                    border.setTitle(borderText);
171                    panel.repaint();
172            }
173    
174            public KnowtatorTextPane getTextPane() {
175                    return textPane;
176            }
177    
178            /**
179             * Calculates the verticle distance in pixels between annotation1 and
180             * annotation2 as they appear in the text. If annotation1 comes before
181             * annotation2 in the text, then the vertical distance will be a positive
182             * number. The spans compared are the spans returned from
183             * SpanUtil.getFirstSpan()
184             * 
185             * @param annotation1
186             * @param annotation2
187             * @return the calculated vertical distance or if spans do not exist, then
188             *         Integer.MIN_VALUE
189             * @see SpanUtil#getFirstSpan(SimpleInstance)
190             */
191            public int getVerticalDistance(SimpleInstance annotation1, SimpleInstance annotation2) {
192                    Span span1 = manager.getSpanUtil().getFirstSpan(annotation1);
193                    Span span2 = manager.getSpanUtil().getFirstSpan(annotation2);
194    
195                    if (span1 == null || span2 == null)
196                            return Integer.MIN_VALUE;
197    
198                    try {
199                            Rectangle span1Pos = textPane.modelToView(span1.getStart());
200                            Rectangle span2Pos = textPane.modelToView(span2.getStart());
201                            return span2Pos.y - span1Pos.y;
202                    } catch (BadLocationException ble) {
203                            ble.printStackTrace();
204                            return Integer.MIN_VALUE;
205                    }
206    
207            }
208    
209            public boolean isVisible(SimpleInstance annotation) {
210                    List<Span> spans = manager.getAnnotationUtil().getSpans(annotation);
211                    Span span;
212                    if(spans == null || spans.size() == 0) {
213                            span = manager.getSpanUtil().getAReferencedSpan(annotation);
214                    } else {
215                            span = spans.get(0);
216                    }
217                    if (span != null) {
218                            try {
219                                    Rectangle annotationLocation = textPane.modelToView(span.getStart());
220                                    if (annotationLocation == null)
221                                            return false;
222                                    annotationLocation = new Rectangle(annotationLocation.x, annotationLocation.y, 1, 1);
223                                    Rectangle viewportBounds = scrollPane.getViewport().getViewRect();
224                                    return viewportBounds.contains(annotationLocation);
225                            } catch (BadLocationException ble) {
226                                    return false;
227                            }
228                    }
229                    return false;
230            }
231    
232            public Span getVisibleSpan() {
233                    Rectangle viewportBounds = scrollPane.getViewport().getViewRect();
234                    int start = textPane.viewToModel(new Point(viewportBounds.x, viewportBounds.y));
235    
236                    start = Math.max(0, start);
237                    int end = textPane.viewToModel(new Point(viewportBounds.x + viewportBounds.width, viewportBounds.y
238                                    + viewportBounds.height));
239                    end = Math.max(end, start);
240                    return new Span(start, end);
241            }
242    
243            public Comparator<SimpleInstance> comparator(final SimpleInstance annotation) {
244                    return new Comparator<SimpleInstance>() {
245                            public int compare(SimpleInstance annotation1, SimpleInstance annotation2) {
246                                    boolean isVisible1 = isVisible(annotation1);
247                                    boolean isVisible2 = isVisible(annotation2);
248                                    if (isVisible1 && !isVisible2)
249                                            return -1;
250                                    if (!isVisible1 && isVisible2)
251                                            return 1;
252                                    else {
253                                            int distance1 = Math.abs(getVerticalDistance(annotation, annotation1));
254                                            int distance2 = Math.abs(getVerticalDistance(annotation, annotation2));
255                                            return distance1 <= distance2 ? -1 : 1;
256                                    }
257                            }
258                    };
259            }
260    }