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 @ 1486

History | View | Annotate | Download (24.6 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.FocusListener;
15
import java.awt.event.InputEvent;
16
import java.awt.event.KeyEvent;
17
import java.awt.event.KeyListener;
18
import java.awt.event.MouseAdapter;
19
import java.awt.event.MouseEvent;
20
import java.net.URL;
21
import java.util.ArrayList;
22
import java.util.Arrays;
23
import java.util.Comparator;
24
import java.util.HashMap;
25
import java.util.HashSet;
26
import java.util.List;
27
import java.util.Map;
28
import java.util.Set;
29
import java.util.TreeSet;
30
import javax.swing.DefaultListCellRenderer;
31
import javax.swing.Icon;
32
import javax.swing.ImageIcon;
33
import javax.swing.JCheckBox;
34
import javax.swing.JComponent;
35
import javax.swing.JLabel;
36
import javax.swing.JList;
37
import javax.swing.JOptionPane;
38
import javax.swing.JPanel;
39

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

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

    
77
public class JRSyntaxTextArea implements SyntaxtHighlightTextComponent, SearchListener {
78

    
79
    private static final Logger logger = LoggerFactory.getLogger(JRSyntaxTextArea.class);
80

    
81
    public static class MyCheckBox extends JCheckBox {
82

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

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

    
90
    }
91

    
92
    private class MyFindToolBar extends FindToolBar {
93

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

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

    
108
    }
109

    
110
    private class MyReplaceToolBar extends ReplaceToolBar {
111

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

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

    
127
    private class LineTrackerPanel extends JPanel {
128

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

    
131
        private class Line extends JLabel {
132

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

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

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

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

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

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

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

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

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

    
209
    private static class IconsTracker {
210

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

    
256
    public class DefaultUpdateCaretPositionActionEvent extends ActionEvent implements UpdateCaretPositionActionEvent {
257

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

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

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

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

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

    
284
    }
285

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

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

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

    
338
        textArea.addKeyListener(new KeyListener() {
339

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

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

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

    
360
    }
361

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

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

    
386
        });
387

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

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

    
407

    
408
    private static class MyListCellRenderer extends DefaultListCellRenderer {
409

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

    
417
    }
418

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

    
514
    private class MyCompletionProvider extends DefaultCompletionProvider {
515

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

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

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

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

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

    
566
    }
567

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
746
            viewport.setViewPosition(new Point(0, y));
747
        } catch (BadLocationException ble) {
748
            ble.printStackTrace();
749
        }
750
        try {
751
            component.setCaretPosition( startOfLineOffset+1 );
752
        } catch (Exception e) {
753
            e.printStackTrace();
754
        }
755
    }
756
    
757
    @Override
758
    public void setEditable(boolean editable) {
759
        this.textArea.setEditable(editable);
760
    }
761

    
762
    @Override
763
    public boolean isEditable() {
764
        return this.textArea.isEditable();
765
    }
766

    
767
    @Override
768
    public void addFocusListener(FocusListener l) {
769
        this.textArea.addFocusListener(l);
770
    }
771
    
772
}