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    package edu.uchsc.ccp.knowtator.ui;
029    
030    import java.awt.Color;
031    import java.awt.Font;
032    import java.awt.FontMetrics;
033    import java.awt.Graphics;
034    import java.awt.Point;
035    import java.awt.Rectangle;
036    import java.awt.Shape;
037    import java.awt.event.ActionEvent;
038    import java.awt.event.ActionListener;
039    import java.awt.event.KeyAdapter;
040    import java.awt.event.KeyEvent;
041    import java.awt.event.KeyListener;
042    import java.awt.event.MouseAdapter;
043    import java.awt.event.MouseEvent;
044    import java.awt.event.MouseListener;
045    import java.util.ArrayList;
046    import java.util.Collection;
047    import java.util.Collections;
048    import java.util.HashMap;
049    import java.util.HashSet;
050    import java.util.LinkedHashSet;
051    import java.util.List;
052    import java.util.Map;
053    import java.util.Set;
054    import java.util.regex.Pattern;
055    
056    import javax.swing.JMenuItem;
057    import javax.swing.JPopupMenu;
058    import javax.swing.JScrollPane;
059    import javax.swing.JTextPane;
060    import javax.swing.UIManager;
061    import javax.swing.text.BadLocationException;
062    import javax.swing.text.DefaultHighlighter;
063    import javax.swing.text.Highlighter;
064    import javax.swing.text.JTextComponent;
065    import javax.swing.text.Position;
066    import javax.swing.text.Style;
067    import javax.swing.text.StyleConstants;
068    import javax.swing.text.StyleContext;
069    import javax.swing.text.StyledDocument;
070    import javax.swing.text.View;
071    
072    import org.apache.log4j.Logger;
073    
074    import edu.stanford.smi.protege.model.Cls;
075    import edu.stanford.smi.protege.model.Frame;
076    import edu.stanford.smi.protege.model.Instance;
077    import edu.stanford.smi.protege.model.SimpleInstance;
078    import edu.stanford.smi.protege.model.Slot;
079    import edu.uchsc.ccp.knowtator.AnnotationUtil;
080    import edu.uchsc.ccp.knowtator.BrowserTextUtil;
081    import edu.uchsc.ccp.knowtator.DisplayColors;
082    import edu.uchsc.ccp.knowtator.InvalidSpanException;
083    import edu.uchsc.ccp.knowtator.KnowtatorManager;
084    import edu.uchsc.ccp.knowtator.MentionUtil;
085    import edu.uchsc.ccp.knowtator.Span;
086    import edu.uchsc.ccp.knowtator.SpanUtil;
087    import edu.uchsc.ccp.knowtator.event.AnnotationCreatedEvent;
088    import edu.uchsc.ccp.knowtator.event.AnnotationCreatedListener;
089    import edu.uchsc.ccp.knowtator.event.EventHandler;
090    import edu.uchsc.ccp.knowtator.event.RefreshAnnotationsDisplayListener;
091    import edu.uchsc.ccp.knowtator.util.ProtegeUtil;
092    
093    public class KnowtatorTextPane extends JTextPane implements RefreshAnnotationsDisplayListener,
094                    AnnotationCreatedListener {
095    
096            public void annotationCreated(AnnotationCreatedEvent event) {
097                    clearSelectionHighlights();
098            }
099    
100            public static final int NO_LINES = 0;
101    
102            public static final int SOLID_LINES = 1;
103    
104            public static final int DASHED_LINES = 2;
105    
106            public static final int VERTICAL_LINES = 3;
107    
108            Logger logger = Logger.getLogger(KnowtatorTextPane.class);
109    
110            static final long serialVersionUID = 0;
111    
112            Set<Span> annotationSpans;
113    
114            Map<Span, Color> annotationSpanColors;
115    
116            Map<Span, Integer> annotationSpanLines;
117    
118            Map<Span, String> annotationSpanSubtexts;
119    
120            List<Span> selectionSpans;
121    
122            List<Span> selectedAnnotationSpans;
123    
124            // we don't need selectionSpanColors because we will always use
125            // getSelectionColor for selection spans
126    
127            Map<Color, KnowtatorHighlighter> painters;
128    
129            Map<Color, KnowtatorHighlighter> linePainters;
130    
131            Map<Color, KnowtatorHighlighter> dashedLinePainters;
132    
133            Map<Color, KnowtatorHighlighter> verticalLinePainters;
134    
135            String text = "";
136    
137            KnowtatorManager manager;
138    
139            AnnotationUtil annotationUtil;
140    
141            MentionUtil mentionUtil;
142    
143            DisplayColors displayColors;
144    
145            BrowserTextUtil browserTextUtil;
146    
147            SpanUtil spanUtil;
148    
149            String tokenRegex = null;
150    
151            Pattern tokenPattern;
152    
153            Highlighter highlighter;
154    
155            Color lineHighlightColor = Color.BLACK;
156    
157            Map<Integer, Set<SimpleInstance>> window2AnnotationsMap;
158    
159            int windowSize = 20;
160    
161            JPopupMenu popupMenu;
162    
163            boolean mouseButtonDown = false;
164    
165            JScrollPane scrollPane;
166    
167            Style doubleSpace;
168    
169            boolean initComplete = false;
170    
171            public KnowtatorTextPane(KnowtatorManager manager) {
172                    super();
173                    this.manager = manager;
174                    annotationUtil = manager.getAnnotationUtil();
175                    mentionUtil = manager.getMentionUtil();
176                    browserTextUtil = manager.getBrowserTextUtil();
177                    displayColors = manager.getDisplayColors();
178                    spanUtil = manager.getSpanUtil();
179    
180                    popupMenu = new JPopupMenu();
181    
182                    annotationSpans = new LinkedHashSet<Span>();
183                    annotationSpanColors = new HashMap<Span, Color>();
184                    annotationSpanLines = new HashMap<Span, Integer>();
185                    annotationSpanSubtexts = new HashMap<Span, String>();
186                    selectionSpans = new ArrayList<Span>();
187                    selectedAnnotationSpans = new ArrayList<Span>();
188    
189                    addMouseListener(createMouseListener());
190                    addKeyListener(createKeyListener());
191    
192                    painters = new HashMap<Color, KnowtatorHighlighter>();
193                    linePainters = new HashMap<Color, KnowtatorHighlighter>();
194                    dashedLinePainters = new HashMap<Color, KnowtatorHighlighter>();
195                    verticalLinePainters = new HashMap<Color, KnowtatorHighlighter>();
196                    updateTokenRegex();
197                    setEditable(false);
198    
199                    window2AnnotationsMap = new HashMap<Integer, Set<SimpleInstance>>();
200                    highlighter = getHighlighter();
201    
202                    EventHandler.getInstance().addRefreshAnnotationsDisplayListener(this);
203                    EventHandler.getInstance().addAnnotationCreatedListener(this);
204                    logger.debug("made it to here.");
205                    initComplete = true;
206            }
207    
208            public void refreshAnnotationsDisplay(boolean scrollToSelection) {
209                    logger.debug("");
210                    highlightSpans(scrollToSelection);
211                    repaint();
212            }
213    
214            public void repaint() {
215                    super.repaint();
216                    if (!initComplete)
217                            return;
218                    if (doubleSpace == null) {
219                            StyledDocument doc = getStyledDocument();
220                            if (doc == null)
221                                    return;
222                            Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
223                            if (def == null)
224                                    return;
225                            doubleSpace = doc.addStyle("doubleSpace", def);
226                    }
227                    if (doubleSpace != null && text.length() > 0) {
228                            Font font = UIManager.getFont("TextArea.font");
229                            if (font.getSize() != StyleConstants.getFontSize(doubleSpace)) {
230                                    StyleConstants.setFontFamily(doubleSpace, "TextArea.font");
231                                    StyleConstants.setFontSize(doubleSpace, font.getSize());
232                                    setParagraphAttributes(doubleSpace, true);
233                            }
234                            if (manager.getSubtextSlot() == null) {
235                                    if (StyleConstants.getLineSpacing(doubleSpace) != 0.2f) {
236                                            StyleConstants.setLineSpacing(doubleSpace, 0.2f);
237                                            setParagraphAttributes(doubleSpace, true);
238                                    }
239                            } else if (StyleConstants.getLineSpacing(doubleSpace) != 1.0f) {
240                                    StyleConstants.setLineSpacing(doubleSpace, 1.0f);
241                                    setParagraphAttributes(doubleSpace, true);
242                            }
243                    }
244            }
245    
246            public void highlightSpans(boolean scrollToSelection) {
247    
248                    logger.debug("");
249                    SimpleInstance selectedAnnotation = manager.getSelectedAnnotation();
250                    List<SimpleInstance> visibleAnnotations = manager.getVisibleFilteredAnnotations();
251                    highlightSpans(selectedAnnotation, visibleAnnotations, scrollToSelection);
252            }
253    
254            public void setScrollPane(JScrollPane scrollPane) {
255                    logger.debug("");
256                    this.scrollPane = scrollPane;
257            }
258    
259            public void setAnnotationUtil(AnnotationUtil annotationUtil) {
260                    logger.debug("");
261                    this.annotationUtil = annotationUtil;
262            }
263    
264            public void setDisplayColors(DisplayColors displayColors) {
265                    logger.debug("");
266                    this.displayColors = displayColors;
267            }
268    
269            public void updateTokenRegex() {
270                    logger.debug("");
271                    if (tokenRegex == null) {
272                            tokenRegex = manager.getTokenRegex();
273                            tokenPattern = Pattern.compile(tokenRegex);
274                    } else {
275                            String currentRegex = manager.getTokenRegex();
276                            if (!tokenRegex.equals(currentRegex)) {
277                                    tokenRegex = currentRegex;
278                                    tokenPattern = Pattern.compile(tokenRegex);
279                            }
280                    }
281            }
282    
283            public void select(int start) {
284                    logger.debug("");
285                    int st = Math.max(0, start);
286                    st = Math.min(st, text.length() - 1);
287                    select(st, st);
288            }
289    
290            /*
291             * overrides inherited method. super.select is called in
292             * select(Collection<Span)
293             */
294            public void select(int start, int end) {
295                    logger.debug("");
296                    if (start <= end)
297                            select(new Span(start, end));
298                    else
299                            select(start);
300            }
301    
302            public void select(Span span) {
303                    logger.debug("");
304                    Collection<Span> spans = new ArrayList<Span>();
305                    spans.add(span);
306                    select(spans);
307            }
308    
309            public boolean select(Span span, boolean isNew) {
310                    logger.debug("");
311                    select(span);
312                    if (annotationSpans.contains(span))
313                            return true;
314                    return false;
315            }
316    
317            public void select(Collection<Span> spans) {
318                    logger.debug("");
319                    for (Span span : spans) {
320                            int start = Math.max(0, span.getStart());
321                            int end = Math.min(span.getEnd(), text.length() - 1);
322                            super.select(start, end);
323                            if (start != end) {
324                                    selectionSpans.add(span);
325                                    manager.setSelectedSpans(selectionSpans);
326                                    highlightSpan(span, getSelectionColor(), NO_LINES);
327                            }
328                    }
329            }
330    
331            public void addAnnotationHighlights(Collection<Span> spans, Color color, int lines) {
332                    logger.debug("");
333                    for (Span span : spans) {
334                            if (annotationSpans.contains(span))
335                                    continue;
336                            annotationSpans.add(span);
337                            annotationSpanColors.put(span, color);
338                            annotationSpanLines.put(span, lines);
339                    }
340            }
341    
342            public void clearHighlights() {
343                    logger.debug("");
344                    clearAnnotationHighlights();
345                    // clearSelectionHighlights();
346                    hideHighlights();
347            }
348    
349            public void hideHighlights() {
350                    logger.debug("");
351                    highlighter.removeAllHighlights();
352                    repaint();
353            }
354    
355            public void clearAnnotationHighlights() {
356                    logger.debug("");
357                    annotationSpans.clear();
358                    annotationSpanColors.clear();
359                    annotationSpanLines.clear();
360    
361                    annotationSpanSubtexts.clear();
362            }
363    
364            public void clearSelectionHighlights() {
365                    logger.debug("");
366                    selectionSpans.clear();
367                    manager.setSelectedSpans(selectionSpans);
368            }
369    
370            // consider getting rid of this method. I don't think it does anything
371            // useful as
372            // showSelectionHighlights is superceded by shoAnnotationHighlights
373            public void showAllHighlights() {
374                    logger.debug("");
375                    showAnnotationHighlights();
376                    showSelectionHighlights();
377            }
378    
379            public void showSelectionHighlights() {
380                    logger.debug(" number of selection spans = " + selectionSpans.size());
381                    for (Span span : selectionSpans) {
382                            Color color = getSelectionColor();
383    
384                            highlightSpan(span, color, NO_LINES);
385                    }
386            }
387    
388            public void showAnnotationHighlights() {
389                    logger.debug("");
390                    for (Span span : annotationSpans) {
391                            Color color = annotationSpanColors.get(span);
392                            int lines = annotationSpanLines.get(span);
393                            highlightSpan(span, color, lines);
394                    }
395                    repaint();
396            }
397    
398            private void updateAnnotationLines() {
399                    logger.debug("");
400                    List<Span> sortedSpans = new ArrayList<Span>(annotationSpans);
401                    Collections.sort(sortedSpans);
402    
403                    for (int i = 0; i < sortedSpans.size(); i++) {
404                            if (i == 0 || i == sortedSpans.size() - 1)
405                                    continue;
406                            else {
407                                    Span previousSpan = sortedSpans.get(i - 1);
408                                    Span currentSpan = sortedSpans.get(i);
409                                    if (previousSpan.getEnd() == currentSpan.getStart()) {
410                                            if (annotationSpanLines.containsKey(currentSpan)
411                                                            && annotationSpanLines.get(currentSpan) != NO_LINES) {
412                                                    continue;
413                                            } else {
414                                                    annotationSpanLines.put(currentSpan, VERTICAL_LINES);
415                                                    highlightSpan(currentSpan, annotationSpanColors.get(currentSpan), VERTICAL_LINES);
416                                            }
417                                    }
418                            }
419                    }
420            }
421    
422            private Object highlightSpan(Span span, Color color, int lines) {
423                    try {
424                            int start = Math.max(0, span.getStart());
425                            int end = Math.min(span.getEnd(), text.length() - 1);
426                            KnowtatorHighlighter painter = getHighlightPainter(color, lines);
427    
428                            Object highlight = highlighter.addHighlight(start, end, painter);
429                            return highlight;
430                    } catch (BadLocationException ble) {
431                            ble.printStackTrace();
432                            return null;
433                    }
434            }
435    
436            public void setText(String text) {
437                    try {
438                            logger.debug("");
439                            super.setText(text);
440                            // super.setPage(new File(
441                            // "C:/Documents and Settings/Philip/My Documents/CSLR/workspace/CoordinationResolution/data/annotation/pilot/textsources/test.html"
442                            // ).toURL());
443                            // this.text ="Hello!\nThis is some test html.\nGoodbye!";
444                            this.text = text;
445                            select(0, 0);
446                    } catch (Exception e) {
447                            e.printStackTrace();
448                    }
449            }
450    
451            /**
452             * written to provide a way for AnnotationPicker and AnnotationSchemaTree
453             * context menu to temporarily highlight an annotation.
454             * 
455             * @param annotation
456             * @see AnnotationPicker#mouseMoved(MouseEvent)
457             */
458            public void highlightAnnotationTemp(SimpleInstance annotation) {
459                    logger.debug("");
460                    hideHighlights();
461                    try {
462                            Color annotationColor = displayColors.getColor(annotation);
463                            java.util.List<Span> spans = annotationUtil.getSpans(annotation);
464                            if (spans.size() > 0) {
465                                    for (Span span : spans) {
466                                            highlightSpan(span, annotationColor, SOLID_LINES);
467                                    }
468                                    select(spans.get(0).getStart()); // this here so that the
469                                                                                                     // textpane will scroll to the
470                                                                                                     // highlighted text.
471                            } else {
472                                    Set<SimpleInstance> referencedAnnotations = annotationUtil
473                                                    .getRelatedAnnotations((SimpleInstance) annotation);
474                                    boolean textSelectionChanged = false; // we only want to do a
475                                                                                                              // textPane.select(int)
476                                                                                                              // once see comment below.
477                                    for (SimpleInstance referencedAnnotation : referencedAnnotations) {
478                                            annotationColor = displayColors.getColor(referencedAnnotation);
479                                            spans = annotationUtil.getSpans(referencedAnnotation);
480                                            for (Span span : spans) {
481                                                    highlightSpan(span, annotationColor, SOLID_LINES);
482                                                    if (!textSelectionChanged) {
483                                                            select(span.getStart()); // this here so that the
484                                                                                                             // textpane will scroll to
485                                                                                                             // the highlighted text.
486                                                            textSelectionChanged = true;
487                                                    }
488                                            }
489                                    }
490                            }
491                    } catch (InvalidSpanException ise) {
492                            ise.printStackTrace();
493                    }
494                    showAllHighlights();
495            }
496    
497            private void selectAnnotation(int offset) {
498                    logger.debug("");
499                    Collection<SimpleInstance> clickedAnnotations = getAnnotationsAt(offset);
500                    if (clickedAnnotations.size() > 0) {
501                            SimpleInstance annotation = annotationUtil.getShortestAnnotation(clickedAnnotations);
502                            SimpleInstance selectedAnnotation = manager.getSelectedAnnotation();
503                            if (annotation.equals(selectedAnnotation)) {
504                                    manager.setSelectedAnnotation(null);
505                            } else {
506                                    manager.setSelectedAnnotation(annotation);
507                            }
508                    }
509            }
510    
511            public void highlightSelectedInstance(SimpleInstance selectedAnnotation, boolean scrollToSelection) throws InvalidSpanException {
512                    logger.debug("");
513                    selectedAnnotationSpans.clear();
514                    if (selectedAnnotation != null) {
515                            Color annotationColor;
516                            java.util.List<Span> spans;
517    
518                            Set<SimpleInstance> referencedAnnotations = annotationUtil
519                                            .getRelatedAnnotations((SimpleInstance) selectedAnnotation);
520    
521                            boolean textSelectionChanged = false; // we only want to do a
522                                                                                                      // textPane.select(int) once
523                                                                                                      // see comment below.
524    
525                            for (SimpleInstance referencedAnnotation : referencedAnnotations) {
526                                    if (referencedAnnotation.equals(selectedAnnotation))
527                                            continue;
528                                    annotationColor = displayColors.getColor(referencedAnnotation);
529                                    getSubtext(referencedAnnotation);
530                                    spans = annotationUtil.getSpans(referencedAnnotation);
531                                    if (spans.size() > 0) {
532                                            addAnnotationHighlights(spans, annotationColor, DASHED_LINES);
533                                            if (!textSelectionChanged && scrollToSelection) {
534                                                    select(spans.get(0).getStart()); // this here so that
535                                                                                                                     // the textpane will
536                                                                                                                     // scroll to the
537                                                                                                                     // highlighted text.
538                                                    textSelectionChanged = true;
539                                            }
540                                    }
541                            }
542    
543                            annotationColor = displayColors.getColor(selectedAnnotation);
544                            getSubtext(selectedAnnotation);
545                            spans = annotationUtil.getSpans(selectedAnnotation);
546                            if (spans.size() > 0) {
547                                    addAnnotationHighlights(spans, annotationColor, SOLID_LINES);
548                                    if(scrollToSelection) {
549                                    select(spans.get(0).getStart()); // we do this so that the
550                                                                                                     // textpane will scroll to the
551                                                                                                     // highlighted annotation.
552                                    }
553                            }
554                            selectedAnnotationSpans.addAll(spans);
555                    }
556            }
557    
558            public void highlightSpans(SimpleInstance selectedAnnotation, java.util.List<SimpleInstance> annotations, boolean scrollToSelection)
559                            throws InvalidSpanException {
560                    logger.debug("");
561                    clearHighlights();
562                    highlightSelectedInstance(selectedAnnotation, scrollToSelection);
563                    Set<SimpleInstance> referencedAnnotations;
564                    if (selectedAnnotation != null)
565                            referencedAnnotations = annotationUtil.getRelatedAnnotations((SimpleInstance) selectedAnnotation);
566                    else
567                            referencedAnnotations = Collections.emptySet();
568    
569                    if (annotations != null) {
570                            for (SimpleInstance annotation : annotations) {
571                                    if (!annotation.equals(selectedAnnotation) && !referencedAnnotations.contains(annotation)) {
572                                            Color annotationColor = displayColors.getColor(annotation);
573                                            getSubtext(annotation);
574                                            if (manager.getFadeUnselectedAnnotations())
575                                                    annotationColor = DisplayColors.getBackgroundColor(annotationColor);
576                                            if (manager.isConsensusMode() && annotationUtil.hasTeamAnnotator(annotation)) {
577                                                    annotationColor = DisplayColors.getBackgroundColor(annotationColor);
578                                            }
579                                            java.util.List<Span> spans = annotationUtil.getSpans(annotation);
580                                            addAnnotationHighlights(spans, annotationColor, NO_LINES);
581                                    }
582                            }
583                    }
584                    populateSpan2Annotations(annotations);
585                    updateAnnotationLines();
586                    hideHighlights();
587                    showSelectionHighlights();
588                    showAnnotationHighlights();
589            }
590    
591            private void populateSpan2Annotations(Collection<SimpleInstance> annotations) {
592                    logger.debug("");
593                    window2AnnotationsMap.clear();
594                    for (SimpleInstance annotation : annotations) {
595                            List<Span> spans = annotationUtil.getSpans(annotation);
596                            for (Span span : spans) {
597                                    int startKey = span.getStart() / windowSize;
598                                    int endKey = span.getEnd() / windowSize;
599                                    for (int key = startKey; key <= endKey; key++) {
600                                            if (!window2AnnotationsMap.containsKey(key)) {
601                                                    window2AnnotationsMap.put(key, new HashSet<SimpleInstance>());
602                                            }
603                                            window2AnnotationsMap.get(key).add(annotation);
604                                    }
605                            }
606                    }
607            }
608    
609            private Collection<SimpleInstance> getAnnotationsAt(int offset) {
610                    logger.debug("");
611                    Integer key = new Integer(offset / windowSize);
612                    Collection<SimpleInstance> returnValues = new ArrayList<SimpleInstance>();
613                    if (window2AnnotationsMap.containsKey(key)) {
614                            Set<SimpleInstance> candidateAnnotations = window2AnnotationsMap.get(key);
615                            for (SimpleInstance annotation : candidateAnnotations) {
616                                    List<Span> spans = annotationUtil.getSpans(annotation);
617                                    for (Span span : spans) {
618                                            if (offset >= span.getStart() && offset <= span.getEnd()) {
619                                                    returnValues.add(annotation);
620                                                    break;
621                                            }
622                                    }
623                            }
624                    }
625                    return returnValues;
626            }
627    
628            private void showContextMenu(MouseEvent mouseEvent) {
629                    logger.debug("");
630                    popupMenu.removeAll();
631                    int offset = viewToModel(mouseEvent.getPoint());
632                    List<SimpleInstance> clickedAnnotations = new ArrayList<SimpleInstance>(getAnnotationsAt(offset));
633                    if (clickedAnnotations == null || clickedAnnotations.size() == 0)
634                            return;
635                    Collections.sort(clickedAnnotations, spanUtil.comparator(browserTextUtil.comparator()));
636    
637                    for (final SimpleInstance annotation : clickedAnnotations) {
638                            JMenuItem menuItem = new JMenuItem("select: " + browserTextUtil.getBrowserText(annotation, 50),
639                                            displayColors.getIcon(displayColors.getColor(annotation)));
640                            menuItem.addActionListener(new ActionListener() {
641                                    public void actionPerformed(ActionEvent actionEvent) {
642                                            manager.setSelectedAnnotation(annotation);
643                                    }
644                            });
645                            menuItem.addMouseListener(new MouseAdapter() {
646                                    public void mouseEntered(MouseEvent event) {
647                                            highlightAnnotationTemp(annotation);
648                                    }
649    
650                                    public void mouseExited(MouseEvent event) {
651                                            manager.refreshAnnotationsDisplay(true);
652                                    }
653                            });
654                            popupMenu.add(menuItem);
655                    }
656    
657                    try {
658                            SimpleInstance annotation = manager.getSelectedAnnotation();
659                            final SimpleInstance mention = annotationUtil.getMention(annotation);
660                            final Cls cls = manager.getMentionUtil().getMentionCls(mention);
661                            if (cls != null) {
662                                    Collection<Slot> slots = cls.getDirectTemplateSlots();
663                                    for (final Slot slot : slots) {
664                                            if (slot.getBrowserText().startsWith(":"))
665                                                    continue;
666                                            final SimpleInstance slotMention = mentionUtil.getSlotMention(mention, slot);
667                                            if (slotMention == null)
668                                                    continue;
669    
670                                            List<SimpleInstance> slotFillerCandidates = manager.getMentionUtil().getSlotFillerCandidates(
671                                                            mention, slot, clickedAnnotations);
672                                            for (final SimpleInstance candidate : slotFillerCandidates) {
673                                                    String slotLabel = ProtegeUtil.getSlotLabel(cls, slot, manager.getKnowledgeBase().getProject());
674                                                    // String annotationText =
675                                                    // browserTextUtil.getBrowserText(annotation);
676                                                    // <b>"+annotationText+"</b>
677                                                    JMenuItem menuItem = new JMenuItem("<html>fill " + "<b>" + slotLabel
678                                                                    + "</b> slot of selected annotation with " + "<b>"
679                                                                    + browserTextUtil.getBrowserText(candidate, 50) + "</b></html>");
680                                                    menuItem.addActionListener(new ActionListener() {
681                                                            public void actionPerformed(ActionEvent actionEvent) {
682                                                                    mentionUtil.addValueToSlotMention(slotMention, annotationUtil.getMention(candidate));
683                                                                    mentionUtil.adjustSlotMentionForCardinality(cls, slot, mention);
684                                                                    mentionUtil.addInverse(mention, slot, annotationUtil.getMention(candidate));
685    
686                                                                    refreshAnnotationsDisplay(true);
687                                                            }
688                                                    });
689                                                    popupMenu.add(menuItem);
690                                            }
691                                    }
692                            }
693    
694                    } catch (NullPointerException npe) {
695                            npe.printStackTrace();
696                    }
697    
698                    if (popupMenu.getSubElements().length == 1)
699                            selectAnnotation(offset);
700                    else if (popupMenu.getSubElements().length > 1)
701                            popupMenu.show(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY() + 10);
702            }
703    
704            private String getSubtext(SimpleInstance annotation) {
705                    try {
706                            Slot subtextSlot = manager.getSubtextSlot();
707                            if (subtextSlot == null)
708                                    return null;
709                            SimpleInstance mention = annotationUtil.getMention(annotation);
710                            SimpleInstance slotMention = mentionUtil.getSlotMention(mention, subtextSlot);
711                            List<Object> slotValues = mentionUtil.getSlotMentionValues(slotMention);
712                            if (slotValues != null && slotValues.size() > 0) {
713                                    String subtext = slotValues.get(0).toString();
714                                    List<Span> spans = annotationUtil.getSpans(annotation);
715                                    if (subtext != null && !subtext.trim().equals("") && spans != null) {
716                                            for (Span span : spans)
717                                                    annotationSpanSubtexts.put(span, subtext);
718                                    }
719    
720                                    return subtext;
721                            }
722                    } catch (NullPointerException npe) {
723                            return null;
724                    }
725    
726                    return null;
727            }
728    
729            private KnowtatorHighlighter getHighlightPainter(Color color, int lines) {
730                    logger.debug("");
731                    if (lines == SOLID_LINES) {
732                            if (!linePainters.containsKey(color))
733                                    linePainters.put(color, new KnowtatorSelectionHighlighter(color));
734                            return linePainters.get(color);
735                    } else if (lines == DASHED_LINES) {
736                            if (!dashedLinePainters.containsKey(color))
737                                    dashedLinePainters.put(color, new KnowtatorDashedHighlighter(color));
738                            return dashedLinePainters.get(color);
739    
740                    } else if (lines == VERTICAL_LINES) {
741                            if (!verticalLinePainters.containsKey(color))
742                                    verticalLinePainters.put(color, new KnowtatorVerticalHighlighter(color));
743                            return verticalLinePainters.get(color);
744                    } else {
745                            if (!painters.containsKey(color))
746                                    painters.put(color, new KnowtatorHighlighter(color));
747                            return painters.get(color);
748                    }
749            }
750    
751            /**
752             * This code was taken right out of its superclass
753             * DefaultHighlighter.DefaultHighlightPainter and modified to draw lines
754             * above and below the selected annotation.
755             */
756            class KnowtatorHighlighter extends DefaultHighlighter.DefaultHighlightPainter {
757    
758                    public KnowtatorHighlighter(Color c) {
759                            super(c);
760                    }
761    
762                    public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
763                            logger.debug("");
764                            Shape returnValue = super.paintLayer(g, offs0, offs1, bounds, c, view);
765    
766                            g.setColor(lineHighlightColor);
767                            Font textFont = UIManager.getFont("TextArea.font");
768                            int fontSize = textFont.getSize();
769                            g.setFont(new Font(textFont.getName(), textFont.getStyle(), fontSize));
770    
771                            Rectangle rec = getRectangle(offs0, offs1, bounds, view);
772                            if (rec != null) {
773                                    int start = viewToModel(new Point(rec.x, rec.y));
774                                    int end = viewToModel(new Point(rec.x + rec.width, rec.y));
775                                    String subtext = annotationSpanSubtexts.get(new Span(start, end));
776                                    if (subtext != null) {
777                                            FontMetrics fontMetrics = g.getFontMetrics();
778                                            int stringWidth = fontMetrics.stringWidth(subtext);
779                                            int startPoint = rec.x + (rec.width / 2);
780                                            startPoint = startPoint - (stringWidth / 2);
781                                            startPoint = Math.max(0, startPoint);
782                                            g.drawString(subtext, startPoint, rec.y + rec.height + fontSize + 3);
783                                    }
784                            }
785                            return returnValue;
786                    }
787    
788                    public Rectangle getRectangle(int offs0, int offs1, Shape bounds, View view) {
789                            logger.debug("");
790                            Rectangle rec = null;
791                            if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) {
792                                    rec = (bounds instanceof Rectangle) ? (Rectangle) bounds : bounds.getBounds();
793                            } else {
794                                    try {
795                                            Shape shape = view.modelToView(offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
796                                            rec = (shape instanceof Rectangle) ? (Rectangle) shape : shape.getBounds();
797                                    } catch (BadLocationException e) {
798                                    }
799                            }
800                            return rec;
801                    }
802            }
803    
804            /**
805             * This code was taken right out of its superclass
806             * DefaultHighlighter.DefaultHighlightPainter and modified to draw lines
807             * above and below the selected annotation.
808             */
809            class KnowtatorSelectionHighlighter extends KnowtatorHighlighter {
810                    public KnowtatorSelectionHighlighter(Color c) {
811                            super(c);
812                    }
813    
814                    public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
815                            logger.debug("");
816                            Shape returnValue = super.paintLayer(g, offs0, offs1, bounds, c, view);
817    
818                            g.setColor(lineHighlightColor);
819    
820                            Rectangle rec = getRectangle(offs0, offs1, bounds, view);
821                            if (rec != null) {
822                                    g.drawLine(rec.x, rec.y, rec.x + rec.width - 1, rec.y);
823                                    g.drawLine(rec.x, rec.y - 1, rec.x + rec.width - 1, rec.y - 1);
824                                    g.drawLine(rec.x, rec.y + rec.height, rec.x + rec.width - 1, rec.y + rec.height);
825                                    g.drawLine(rec.x, rec.y + rec.height + 1, rec.x + rec.width - 1, rec.y + rec.height + 1);
826                            }
827                            return returnValue;
828                    }
829            }
830    
831            class KnowtatorDashedHighlighter extends KnowtatorHighlighter {
832                    public KnowtatorDashedHighlighter(Color c) {
833                            super(c);
834                    }
835    
836                    public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
837                            logger.debug("");
838                            Shape returnValue = super.paintLayer(g, offs0, offs1, bounds, c, view);
839    
840                            Rectangle rec = getRectangle(offs0, offs1, bounds, view);
841                            if (rec != null) {
842                                    drawDashedLine(g, rec.x, rec.x + rec.width - 1, rec.y);
843                                    drawDashedLine(g, rec.x, rec.x + rec.width - 1, rec.y - 1);
844                                    drawDashedLine(g, rec.x, rec.x + rec.width - 1, rec.y + rec.height);
845                                    drawDashedLine(g, rec.x, rec.x + rec.width - 1, rec.y + rec.height + 1);
846                            }
847                            return returnValue;
848                    }
849    
850                    private void drawDashedLine(Graphics g, int x1, int x2, int y) {
851                            logger.debug("");
852                            if (x1 >= x2) {
853                                    logger.debug("x1 is larger than x2");
854                                    return;
855                            }
856                            for (int x = x1; x < x2; x += 10) {
857                                    g.drawLine(x, y, Math.min(x + 5, x2), y);
858                            }
859                            g.drawLine(x2, y, x2 - 2, y);
860                    }
861            }
862    
863            class KnowtatorVerticalHighlighter extends KnowtatorHighlighter {
864                    public KnowtatorVerticalHighlighter(Color c) {
865                            super(c);
866                    }
867    
868                    public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
869                            logger.debug("");
870                            Shape returnValue = super.paintLayer(g, offs0, offs1, bounds, c, view);
871    
872                            Rectangle rec = getRectangle(offs0, offs1, bounds, view);
873                            if (rec != null) {
874                                    g.drawLine(rec.x, rec.y, rec.x, rec.y + rec.height - 1);
875                            }
876                            return returnValue;
877                    }
878            }
879    
880            private MouseListener createMouseListener() {
881                    return new MouseAdapter() {
882                            public void mousePressed(MouseEvent mouseEvent) {
883                                    logger.debug("");
884                                    hideHighlights();
885                                    if (!mouseEvent.isControlDown()) {
886                                            clearSelectionHighlights();
887                                    } else {
888                                            showSelectionHighlights();
889                                    }
890                                    mouseButtonDown = true;
891                            }
892    
893                            public void mouseClicked(MouseEvent mouseEvent) {
894                                    logger.debug("");
895                                    showContextMenu(mouseEvent);
896                                    // int offset = viewToModel(mouseEvent.getPoint());
897                                    // selectAnnotation(offset);
898                            }
899    
900                            public void mouseReleased(MouseEvent mouseEvent) {
901                                    logger.debug("");
902    
903                                    mouseButtonDown = false;
904                                    if (getSelectionStart() != getSelectionEnd()) {
905                                            Span selectedSpan = new Span(getSelectionStart(), getSelectionEnd());
906                                            updateTokenRegex();
907                                            Span expandedSpan = SpanUtil.expandToSplits(text, selectedSpan, 15, 15, tokenPattern);
908                                            if (expandedSpan != null) {
909                                                    select(expandedSpan);
910                                            }
911                                            showSelectionHighlights();
912                                    }
913                                    showAnnotationHighlights();
914    
915                                    boolean fastAnnotateMode = manager.isFastAnnotateMode();
916                                    Frame fastAnnotateFrame = manager.getFastAnnotateFrame();
917                                    if (fastAnnotateMode && fastAnnotateFrame != null && manager.getSelectedSpans().size() > 0) {
918                                            manager.createAnnotation((Instance) fastAnnotateFrame);
919                                    }
920                            }
921                    };
922            }
923    
924            private KeyListener createKeyListener() {
925                    return new KeyAdapter() {
926                            public void keyReleased(KeyEvent keyEvent) {
927                                    logger.debug("");
928                                    if (keyEvent.getKeyCode() == KeyEvent.VK_CONTROL && mouseButtonDown) {
929                                            hideHighlights();
930                                            clearSelectionHighlights();
931                                    }
932                            }
933                    };
934            }
935    
936    }