Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.swing / org.gvsig.scripting.swing.impl / src / main / java / org / gvsig / scripting / swing / impl / syntaxhighlight / JRSyntaxTextArea.java @ 1440

History | View | Annotate | Download (24.3 KB)

1
package org.gvsig.scripting.swing.impl.syntaxhighlight;
2

    
3
import java.awt.BorderLayout;
4
import java.awt.Color;
5
import java.awt.Component;
6
import java.awt.Container;
7
import java.awt.Cursor;
8
import java.awt.Dimension;
9
import java.awt.Font;
10
import java.awt.Point;
11
import java.awt.Rectangle;
12
import java.awt.event.ActionEvent;
13
import java.awt.event.ActionListener;
14
import java.awt.event.InputEvent;
15
import java.awt.event.KeyEvent;
16
import java.awt.event.KeyListener;
17
import java.awt.event.MouseAdapter;
18
import java.awt.event.MouseEvent;
19
import java.net.URL;
20
import java.util.ArrayList;
21
import java.util.Arrays;
22
import java.util.Comparator;
23
import java.util.HashMap;
24
import java.util.HashSet;
25
import java.util.List;
26
import java.util.Map;
27
import java.util.Set;
28
import java.util.TreeSet;
29
import javax.swing.DefaultListCellRenderer;
30
import javax.swing.Icon;
31
import javax.swing.ImageIcon;
32
import javax.swing.JCheckBox;
33
import javax.swing.JComponent;
34
import javax.swing.JLabel;
35
import javax.swing.JList;
36
import javax.swing.JOptionPane;
37
import javax.swing.JPanel;
38

    
39
import javax.swing.JScrollPane;
40
import javax.swing.JViewport;
41
import javax.swing.KeyStroke;
42
import javax.swing.SwingUtilities;
43
import javax.swing.UIDefaults;
44
import javax.swing.UIManager;
45
import javax.swing.event.CaretEvent;
46
import javax.swing.event.CaretListener;
47
import javax.swing.text.BadLocationException;
48
import javax.swing.text.Element;
49
import javax.swing.text.JTextComponent;
50
import org.apache.commons.lang3.StringUtils;
51
import org.fife.rsta.ui.CollapsibleSectionPanel;
52
import org.fife.rsta.ui.search.FindToolBar;
53
import org.fife.rsta.ui.search.ReplaceToolBar;
54
import org.fife.rsta.ui.search.SearchEvent;
55
import org.fife.rsta.ui.search.SearchListener;
56
import org.fife.ui.autocomplete.AutoCompletion;
57
import org.fife.ui.autocomplete.BasicCompletion;
58
import org.fife.ui.autocomplete.Completion;
59
import org.fife.ui.autocomplete.CompletionProvider;
60
import org.fife.ui.autocomplete.DefaultCompletionProvider;
61

    
62
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
63
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
64
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
65
import org.fife.ui.rtextarea.GutterIconInfo;
66
import org.fife.ui.rtextarea.RTextScrollPane;
67
import org.fife.ui.rtextarea.SearchContext;
68
import org.fife.ui.rtextarea.SearchEngine;
69
import org.fife.ui.rtextarea.SearchResult;
70
import org.gvsig.scripting.swing.api.ScriptingSwingLocator;
71
import org.gvsig.scripting.swing.api.ScriptingUIManager;
72
import org.gvsig.scripting.swing.api.SyntaxtHighlightTextComponent;
73
import org.slf4j.Logger;
74
import org.slf4j.LoggerFactory;
75

    
76
public class JRSyntaxTextArea implements SyntaxtHighlightTextComponent, SearchListener {
77

    
78
    private static final Logger logger = LoggerFactory.getLogger(JRSyntaxTextArea.class);
79
    
80
    public static class MyCheckBox extends JCheckBox {
81

    
82
        private static final String uiClassID = "ToggleButtonUI";
83

    
84
        @Override
85
        public String getUIClassID() {
86
            return uiClassID;
87
        }
88

    
89
    }
90

    
91
    private class MyFindToolBar extends FindToolBar {
92

    
93
        public MyFindToolBar(SearchListener listener) {
94
            super(listener);
95
        }
96

    
97
        @Override
98
        protected JCheckBox createCB(String key) {
99
            JCheckBox cb = new MyCheckBox();
100
            cb.setToolTipText(searchMsg.getString(key));
101
            cb.addActionListener(listener);
102
            cb.addMouseListener(listener);
103
            cb.setIcon(getIcon("scripting-search-" + key.toLowerCase()));
104
            return cb;
105
        }
106

    
107
    }
108

    
109
    private class MyReplaceToolBar extends ReplaceToolBar {
110

    
111
        public MyReplaceToolBar(SearchListener listener) {
112
            super(listener);
113
        }
114

    
115
        @Override
116
        protected JCheckBox createCB(String key) {
117
            JCheckBox cb = new MyCheckBox();
118
            cb.setToolTipText(searchMsg.getString(key));
119
            cb.addActionListener(listener);
120
            cb.addMouseListener(listener);
121
            cb.setIcon(getIcon("scripting-search-" + key.toLowerCase()));
122
            return cb;
123
        }
124
    }
125

    
126
    private class LineTrackerPanel extends JPanel {
127

    
128
        private static final long serialVersionUID = -1923556745197997373L;
129

    
130
        private class Line extends JLabel {
131

    
132
            private static final long serialVersionUID = 8966359978957026490L;
133
            private final int lineno;
134
            private final String tag;
135

    
136
            public Line(String tag, final int lineno, int posy, Color color, String tooltip) {
137
                UIDefaults uidefaults = UIManager.getLookAndFeel().getDefaults();
138
                this.setBackground((Color) uidefaults.get("EditorPane.background"));
139
                this.setForeground((Color) uidefaults.get("EditorPane.foreground"));
140

    
141
                this.lineno = lineno;
142
                this.tag = tag;                
143
                this.setBackground(color);
144
                this.setBounds(0, posy, 15, 2);
145
                this.setOpaque(true);
146
                this.setToolTipText(tooltip);
147
                this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
148
                this.addMouseListener(new MouseAdapter() {
149

    
150
                    @Override
151
                    public void mouseClicked(MouseEvent e) {
152
                        gotoline(lineno);
153
                    }
154
                });
155
//                Rectangle r = this.getBounds();
156
//                logger.info("LineTracker: created lineno:"+lineno+", tag:"+tag+", x:"+ r.x + ", y:"+ r.y + ", w:"+ r.width+", h:"+ r.height);
157
            }
158

    
159
            public int getLineno() {
160
                return this.lineno;
161
            }
162
            
163
            public String getTag() {
164
                return this.tag;
165
            }
166
        }
167

    
168
        public LineTrackerPanel() {
169
            setLayout(null);
170
        }
171

    
172
        @Override
173
        public Dimension getPreferredSize() {
174
            Dimension d = super.getPreferredSize(); 
175
            return new Dimension(15, d.height);
176
        }
177
        
178
        public void addLineTracker(String tag, int lineno, String tooltip, Color color) {
179
            int posy = (int) (((float)lineno)*this.getSize().height / textArea.getLineCount());
180
            Line line = new Line(tag,lineno,posy,color,tooltip);
181
            this.add(line);
182
            this.repaint();
183
        }
184

    
185
        public void removeLineTrackers() {
186
            this.removeAll();
187
            this.repaint();
188
        }
189

    
190
        public void removeLineTrackers(String tag) {
191
            if( StringUtils.isEmpty(tag) ) {
192
                return;
193
            }
194
            List<Line> toremove = new ArrayList<>();
195
            for( int i=0; i<this.getComponentCount(); i++ ) {
196
                Line line = (Line) this.getComponent(i);
197
                if( tag.equalsIgnoreCase(line.getTag()) ) {
198
                    toremove.add(line);
199
                } 
200
            }
201
            for (Line line : toremove) {
202
                this.remove(line);
203
            }
204
            this.repaint();
205
        }
206
    }
207

    
208
    private static class IconsTracker {
209

    
210
        private Map<String,Set<GutterIconInfo>>iconInfos = new HashMap<>();
211
        
212
        public IconsTracker() {
213
            
214
        }
215
        
216
        public void add(String tag, GutterIconInfo x) {
217
            Set<GutterIconInfo> set = this.iconInfos.get(tag);
218
            if( set == null ) {
219
                set = new HashSet<>();
220
                this.iconInfos.put(tag, set);
221
            }
222
            set.add(x);
223
        }
224
        
225
        public void removeAll() {
226
            this.iconInfos = new HashMap<>();
227
        }
228
        
229
        public void remove(String tag) {
230
            this.iconInfos.remove(tag);
231
        }
232
        
233
        public void remove(String tag, GutterIconInfo x) {
234
            Set<GutterIconInfo> set = this.iconInfos.get(tag);
235
            if( set == null ) {
236
                return;
237
            }
238
            set.remove(x);
239
        }
240
        
241
        public boolean exists(String tag) {
242
            return this.iconInfos.containsKey(tag);
243
        }
244
        
245
        public Set<GutterIconInfo> get(String tag) {
246
            Set<GutterIconInfo> set = this.iconInfos.get(tag);
247
            return set;
248
        }
249
    }
250
    
251
    private final FindToolBar findToolBar;
252
    private final ReplaceToolBar replaceToolBar;
253
    private final CollapsibleSectionPanel csp;
254

    
255
    public class DefaultUpdateCaretPositionActionEvent extends ActionEvent implements UpdateCaretPositionActionEvent {
256

    
257
        /**
258
         *
259
         */
260
        private static final long serialVersionUID = 8238486105726094074L;
261
        int line = -1;
262
        int column = -1;
263

    
264
        public DefaultUpdateCaretPositionActionEvent(Object source, int id,
265
                String command, int line, int column) {
266
            super(source, id, command);
267
            this.line = line;
268
            this.column = column;
269
        }
270

    
271
        public int getLine() {
272
            return this.line + 1;
273
        }
274

    
275
        public int getColumn() {
276
            return this.column;
277
        }
278

    
279
        public boolean hasLineAndColumn() {
280
            return this.line >= 0 && this.column >= 0;
281
        }
282

    
283
    }
284

    
285
    protected ActionListener updateCaretPosition = null;
286
    protected RSyntaxTextArea textArea = null;
287
    private final RTextScrollPane scrollPanel;
288
    private final JPanel panel;
289
    private final Map<String, String> contentTypeAlias;
290
    private final IconsTracker iconsTracker = new IconsTracker();
291
    private LineTrackerPanel lineTrackerPanel;
292
    private ScriptingUIManager scriptingUIManager;
293
    
294

    
295
    public JRSyntaxTextArea() {
296
        this.contentTypeAlias = new HashMap<>();
297
        this.contentTypeAlias.put("text/r", SyntaxConstants.SYNTAX_STYLE_C);
298
        this.contentTypeAlias.put("text/ecmascript", SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
299
        this.contentTypeAlias.put("text/cosa", SyntaxConstants.SYNTAX_STYLE_SQL);
300

    
301
        this.textArea = new RSyntaxTextArea(20, 60);
302
        
303
        UIDefaults uidefaults = UIManager.getLookAndFeel().getDefaults();
304
        
305
        // https://alvinalexander.com/java/java-uimanager-color-keys-list/
306
        this.textArea.setBackground((Color) uidefaults.get("EditorPane.background"));
307
        this.textArea.setForeground((Color) uidefaults.get("EditorPane.foreground"));
308
        this.textArea.setFont((Font) uidefaults.get("EditorPane.font"));
309
        
310
        this.init();
311
        lineTrackerPanel = new LineTrackerPanel();
312
        this.scrollPanel = new RTextScrollPane(this.textArea);
313
        this.scrollPanel.setBackground((Color) uidefaults.get("EditorPane.background"));
314
        this.scrollPanel.setForeground((Color) uidefaults.get("EditorPane.foreground"));
315
        this.scrollPanel.setFont((Font) uidefaults.get("EditorPane.font"));
316
        this.scrollPanel.setIconRowHeaderEnabled(true);
317
        this.scrollPanel.setLineNumbersEnabled(true);
318
        this.scrollPanel.setFoldIndicatorEnabled(true);
319
        
320
        panel = new JPanel();
321
        panel.setLayout(new BorderLayout());
322
        panel.add(scrollPanel, BorderLayout.CENTER);
323
        panel.add(lineTrackerPanel,BorderLayout.EAST);
324
        
325
        findToolBar = new MyFindToolBar(this);
326
        replaceToolBar = new MyReplaceToolBar(this);
327
        replaceToolBar.setSearchContext(findToolBar.getSearchContext());
328
        csp = new CollapsibleSectionPanel();
329
        csp.add(scrollPanel);
330
        panel.add(csp, BorderLayout.CENTER);
331
        int ctrl = (KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK);
332
        KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F, ctrl);
333
        csp.addBottomComponent(ks, findToolBar);
334
        ks = KeyStroke.getKeyStroke(KeyEvent.VK_H, ctrl);
335
        csp.addBottomComponent(ks, replaceToolBar);
336

    
337
        textArea.addKeyListener(new KeyListener() {
338

    
339
            @Override
340
            public void keyTyped(KeyEvent ke) {
341
            }
342

    
343
            @Override
344
            public void keyPressed(KeyEvent ke) {
345
                if (ke.getModifiers() == (KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK)) {
346
                    if (ke.getKeyCode() == KeyEvent.VK_H) {
347
                        csp.showBottomComponent(replaceToolBar);
348
                    } else if (ke.getKeyCode() == KeyEvent.VK_F) {
349
                        csp.showBottomComponent(findToolBar);
350
                    }
351
                }
352
            }
353

    
354
            @Override
355
            public void keyReleased(KeyEvent ke) {
356
            }
357
        });
358

    
359
    }
360

    
361
    protected void init() {
362
        textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_PYTHON);
363
        textArea.setCodeFoldingEnabled(true);
364
        textArea.setClearWhitespaceLinesEnabled(true);
365
        textArea.setAutoIndentEnabled(true);
366
        textArea.setCloseCurlyBraces(true);
367
        textArea.setWhitespaceVisible(true);
368
        textArea.setAnimateBracketMatching(true);
369
        textArea.setBracketMatchingEnabled(true);
370
        textArea.setAutoIndentEnabled(true);
371
        textArea.setTabsEmulated(true);
372
        textArea.setTabSize(2);
373
        textArea.setAntiAliasingEnabled(true);
374

    
375
        textArea.addCaretListener(new CaretListener() {
376
            public void caretUpdate(CaretEvent e) {
377
                if (updateCaretPosition == null) {
378
                    return;
379
                }
380
                updateCaretPosition.actionPerformed(
381
                        new DefaultUpdateCaretPositionActionEvent(textArea, 1, "position", textArea.getCaretLineNumber(), textArea.getCaretOffsetFromLineStart())
382
                );
383
            }
384

    
385
        });
386

    
387
        CompletionProvider provider = new MyCompletionProvider(python_keywords);
388
        AutoCompletion ac = new AutoCompletion(provider);
389
        ac.setListCellRenderer(new MyListCellRenderer());
390
        ac.setTriggerKey(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_DOWN_MASK));
391
        ac.install(textArea);
392
    }
393

    
394
    private ScriptingUIManager getUIManager() {
395
        if( this.scriptingUIManager==null ) {
396
            this.scriptingUIManager = ScriptingSwingLocator.getUIManager();
397
        }
398
        return this.scriptingUIManager;
399
    }
400
    
401
    private ImageIcon getIcon(String key) {
402
        ImageIcon icon = this.getUIManager().getIcon(key);
403
        return icon;
404
    }
405

    
406

    
407
    private static class MyListCellRenderer extends DefaultListCellRenderer {
408

    
409
        @Override
410
        public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
411
            Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); //To change body of generated methods, choose Tools | Templates.
412
            ((JLabel) comp).setIcon(((Completion) value).getIcon());
413
            return comp;
414
        }
415

    
416
    }
417

    
418
    private String[] python_keywords = new String[]{
419
        "except",
420
        "class",
421
        "continue",
422
        "else",
423
        "finally",
424
        "if",
425
        "elif",
426
        "in",
427
        "return",
428
        "raise",
429
        "try",
430
        "while",
431
        "def",
432
        "print",
433
        "globals",
434
        "for",
435
        "pass",
436
        "from",
437
        "import",
438
        "True",
439
        "False",
440
        "None",
441
        "self",
442
        "break",
443
        "__init__",
444
        "__len__",
445
        "__str__",
446
        "__repr__",
447
        "__call__",
448
        "__iter__",
449
        "__class__",
450
        "__getattr__",
451
        "__setattr__",
452
        "__delattr__",
453
        "__getitem__",
454
        "__setitem__",
455
        "__delitem__",
456
        "__getslice__",
457
        "__setslice__",
458
        "__delslice__",
459
        "__getattribute__",
460
        "__setattribute__",
461
        "__contains__",
462
        "__reduce__",
463
        "__cmp__",
464
        "__eq__",
465
        "__ne__",
466
        "__lt__",
467
        "__le__",
468
        "__gt__",
469
        "__ge__",
470
        "__mul__",
471
        "__imul__",
472
        "__rmul__",
473
        "__add__",
474
        "__iadd__",
475
        "__radd__",
476
        "__mod__",
477
        "__rmod__",
478
        "int",
479
        "unicode",
480
        "str",
481
        "float",
482
        "long",
483
        "list",
484
        "tuple",
485
        "dict",
486
        "iter",
487
        "vars",
488
        "isinstance",
489
        "dir",
490
        "repr",
491
        "reduce",
492
        "zip",
493
        "chr",
494
        "hex",
495
        "id",
496
        "max",
497
        "min",
498
        "oct",
499
        "pow",
500
        "ord",
501
        "unichr",
502
        "range",
503
        "xrange",
504
        "abs",
505
        "len",
506
        "apply",
507
        "open",
508
        "getattr",
509
        "setattr",
510
        "hasattr"
511
    };
512

    
513
    private class MyCompletionProvider extends DefaultCompletionProvider {
514

    
515
        private List<Completion> keywords;
516
        private Icon keywordIcon;
517
        private Icon wordIcon;
518

    
519
        public MyCompletionProvider(String[] keywords) {
520
            ClassLoader loader = this.getClass().getClassLoader();
521
            URL url = loader.getResource("org/gvsig/scripting/swing/impl/syntaxhighlight/images/autocompletion_keywords.png");
522
            if (url != null) {
523
                keywordIcon = new ImageIcon(url);
524
            }
525
            url = loader.getResource("org/gvsig/scripting/swing/impl/syntaxhighlight/images/autocompletion_words.png");
526
            if (url != null) {
527
                wordIcon = new ImageIcon(url);
528
            }
529

    
530
            this.keywords = new ArrayList<>();
531
            for (String keyword : keywords) {
532
                BasicCompletion completion = new BasicCompletion(this, keyword);
533
                completion.setIcon(keywordIcon);
534
                this.keywords.add(completion);
535
            }
536
        }
537

    
538
        
539
        
540
        @Override
541
        protected List<Completion> getCompletionsImpl(JTextComponent comp) {
542
            TreeSet all = new TreeSet();
543
            all.addAll(this.keywords);
544

    
545
            String s = comp.getText();
546
            String[] x = StringUtils.split(s, "\\ \n\t\r!\"><'@#|$%&/()=??,.;:{}[]*-+");
547
            TreeSet<String> words = new TreeSet(new Comparator<String>() {
548
                @Override
549
                public int compare(String o1, String o2) {
550
                    return o1.compareTo(o2);
551
                }
552
            });
553
            words.addAll(Arrays.asList(x));
554
            for (String word : words) {
555
                BasicCompletion completion = new BasicCompletion(this, word);
556
                completion.setIcon(wordIcon);
557
                completion.setRelevance(10);
558
                all.add(completion);
559
            }
560
            completions = new ArrayList<>();
561
            completions.addAll(all);
562
            return super.getCompletionsImpl(comp);
563
        }
564

    
565
    }
566

    
567
    private String normalizeContentType(String s) {
568
        s = s.toLowerCase();
569
        if (this.contentTypeAlias.containsKey(s)) {
570
            s = this.contentTypeAlias.get(s);
571
        }
572
        return s;
573
    }
574

    
575
    public void setContentType(String contentType) {
576
        TokenMakerFactory factory = TokenMakerFactory.getDefaultInstance();
577
        Set<String> mimetypes = factory.keySet();
578
        String mimetype = this.normalizeContentType(contentType);
579
        if( !mimetypes.contains(mimetype)) {
580
          mimetype = mimetype.replace("-source", "");
581
          if( !mimetypes.contains(mimetype)) {
582
            mimetype = mimetype.replace("/x-", "/");
583
            if( !mimetypes.contains(mimetype)) {
584
              mimetype = this.normalizeContentType(contentType);
585
            }
586
          }
587
        }
588
        this.textArea.setSyntaxEditingStyle(mimetype);
589
    }
590

    
591
    public JScrollPane getJScrollPane() {
592
        return this.scrollPanel;
593
    }
594

    
595
    public JTextComponent getJTextComponent() {
596
        return this.textArea;
597
    }
598

    
599
    @Override
600
    public JComponent asJComponent() {
601
        return this.panel;
602
    }
603

    
604
    public String getContentType() {
605
        return this.textArea.getSyntaxEditingStyle();
606
    }
607

    
608
    public void addUpdateCaretPositionActionListener(ActionListener updateCaretPosition) {
609
        this.updateCaretPosition = updateCaretPosition;
610
    }
611

    
612
    public void setText(String text) {
613
        this.textArea.setText(text);
614
    }
615

    
616
    public String getText() {
617
        return this.textArea.getText();
618
    }
619

    
620
    public void addKeyListener(KeyListener keyListener) {
621
        this.textArea.addKeyListener(keyListener);
622
    }
623

    
624
    @Override
625
    public void searchEvent(SearchEvent e) {
626

    
627
        SearchEvent.Type type = e.getType();
628
        SearchContext context = e.getSearchContext();
629
        SearchResult result = null;
630

    
631
        switch (type) {
632
            default: // Prevent FindBugs warning later
633
            case MARK_ALL:
634
                result = SearchEngine.markAll(textArea, context);
635
                break;
636
            case FIND:
637
                result = SearchEngine.find(textArea, context);
638
                if (!result.wasFound()) {
639
                    textArea.getCaret().setDot(0);
640
                    result = SearchEngine.find(textArea, context);
641
                    if (!result.wasFound()) {
642
                        UIManager.getLookAndFeel().provideErrorFeedback(textArea);
643
                    }
644
                }
645
                break;
646
            case REPLACE:
647
                result = SearchEngine.replace(textArea, context);
648
                if (!result.wasFound()) {
649
                    UIManager.getLookAndFeel().provideErrorFeedback(textArea);
650
                }
651
                break;
652
            case REPLACE_ALL:
653
                result = SearchEngine.replaceAll(textArea, context);
654
                JOptionPane.showMessageDialog(null, result.getCount()
655
                        + " occurrences replaced.");
656
                break;
657
        }
658
        String text = null;
659
        if (result.wasFound()) {
660
            text = "Text found; occurrences marked: " + result.getMarkedCount();
661
        } else if (type == SearchEvent.Type.MARK_ALL) {
662
            if (result.getMarkedCount() > 0) {
663
                text = "Occurrences marked: " + result.getMarkedCount();
664
            } else {
665
                text = "";
666
            }
667
        } else {
668
            text = "Text not found";
669
        }
670
        //setMessage(text);
671
    }
672

    
673
    @Override
674
    public String getSelectedText() {
675
        return textArea.getSelectedText();
676
    }
677

    
678
    /**
679
     *
680
     */
681
    @Override
682
    public void discardAllEdits() {
683
        this.textArea.discardAllEdits();
684
    }
685
    
686
    @Override
687
    public void removeTrackingIcons() {
688
        this.scrollPanel.getGutter().removeAllTrackingIcons();
689
        this.iconsTracker.removeAll();
690
        this.lineTrackerPanel.removeLineTrackers();
691
    }
692
    
693
    @Override
694
    public void addLineTrackingIcon(String tag, int line, Icon icon, String tip) {
695
        addLineTrackingIcon(tag, line, icon, tip, Color.BLUE.darker());
696
    }
697
    
698
    @Override
699
    public void addLineTrackingIcon(String tag, int line, Icon icon, String tip, Color color) {
700
        try {
701
            GutterIconInfo x = this.scrollPanel.getGutter().addLineTrackingIcon(line,icon,tip);
702
            this.iconsTracker.add(tag, x);
703
            this.lineTrackerPanel.addLineTracker(tag, line, tip, color);
704
        } catch (BadLocationException ex) {
705
            throw new RuntimeException("Can't add tracking icon with tag '"+tag+"' in line '"+line+"', tip='"+tip+"'", ex);
706
        }
707
    }
708
    
709
    @Override
710
    public void removeTrackingIcons(String tag) {
711
        Set<GutterIconInfo> icontags = this.iconsTracker.get(tag);
712
        if( icontags!=null ) {
713
            for (GutterIconInfo icontag : icontags) {
714
                this.scrollPanel.getGutter().removeTrackingIcon(icontag);
715
            }
716
            this.iconsTracker.remove(tag);
717
        }
718
        this.lineTrackerPanel.removeLineTrackers(tag);
719
    }
720

    
721
    @Override
722
    public void gotoline(final int line) {
723
        JTextComponent component = getJTextComponent();
724
        
725
        Element root = component.getDocument().getDefaultRootElement();
726
        int lineno = Math.max(line, 1);
727
        int maxlines = root.getElementCount();
728
        lineno = Math.min(lineno, maxlines);
729
        int startOfLineOffset = root.getElement( lineno - 1 ).getStartOffset();
730
        component.setCaretPosition( startOfLineOffset );
731
            
732
        Container container = SwingUtilities.getAncestorOfClass(JViewport.class, component);
733
        if (container == null) {
734
            return;
735
        }
736
        try {
737
            Rectangle r = component.modelToView(component.getCaretPosition());
738
            JViewport viewport = (JViewport) container;
739
            int extentHeight = viewport.getExtentSize().height;
740
            int viewHeight = viewport.getViewSize().height;
741

    
742
            int y = Math.max(0, r.y - ((extentHeight - r.height) / 2));
743
            y = Math.min(y, viewHeight - extentHeight);
744

    
745
            viewport.setViewPosition(new Point(0, y));
746
        } catch (BadLocationException ble) {
747
            ble.printStackTrace();
748
        }
749
        try {
750
            component.setCaretPosition( startOfLineOffset+1 );
751
        } catch (Exception e) {
752
            e.printStackTrace();
753
        }
754
    }
755
    
756
}