redcar-javamateview 0.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. data/LICENSE +34 -0
  2. data/README +58 -0
  3. data/Rakefile +94 -0
  4. data/lib/javamateview.rb +41 -0
  5. data/lib/javamateview/example.rb +334 -0
  6. data/lib/javamateview/jar/java-mateview.jar +0 -0
  7. data/lib/javamateview/jcodings.jar +0 -0
  8. data/lib/javamateview/jdom.jar +0 -0
  9. data/lib/javamateview/joni.jar +0 -0
  10. data/spec/onig/match_spec.rb +50 -0
  11. data/spec/parsing/dynamic_parsing_spec.rb +172 -0
  12. data/spec/parsing/static_parsing_spec.rb +476 -0
  13. data/spec/spec_helper.rb +33 -0
  14. data/src/com/redcareditor/mate/Bundle.java +81 -0
  15. data/src/com/redcareditor/mate/DoublePattern.java +89 -0
  16. data/src/com/redcareditor/mate/Grammar.java +129 -0
  17. data/src/com/redcareditor/mate/IAnnotationAreaListener.java +7 -0
  18. data/src/com/redcareditor/mate/IGrammarListener.java +5 -0
  19. data/src/com/redcareditor/mate/IncludePattern.java +10 -0
  20. data/src/com/redcareditor/mate/LineNumberRulerColumn.java +922 -0
  21. data/src/com/redcareditor/mate/Marker.java +22 -0
  22. data/src/com/redcareditor/mate/MateText.java +697 -0
  23. data/src/com/redcareditor/mate/ParseThunk.java +71 -0
  24. data/src/com/redcareditor/mate/Parser.java +627 -0
  25. data/src/com/redcareditor/mate/ParserScheduler.java +237 -0
  26. data/src/com/redcareditor/mate/Pattern.java +152 -0
  27. data/src/com/redcareditor/mate/RangeSet.java +91 -0
  28. data/src/com/redcareditor/mate/Scanner.java +178 -0
  29. data/src/com/redcareditor/mate/Scope.java +534 -0
  30. data/src/com/redcareditor/mate/ScopeMatcher.java +162 -0
  31. data/src/com/redcareditor/mate/SharedTextColors.java +110 -0
  32. data/src/com/redcareditor/mate/SinglePattern.java +20 -0
  33. data/src/com/redcareditor/mate/WhitespaceCharacterPainter.java +395 -0
  34. data/src/com/redcareditor/mate/colouring/Colourer.java +16 -0
  35. data/src/com/redcareditor/mate/colouring/swt/MarginPaintListener.java +62 -0
  36. data/src/com/redcareditor/mate/colouring/swt/SwtColourer.java +501 -0
  37. data/src/com/redcareditor/mate/document/MateDocument.java +15 -0
  38. data/src/com/redcareditor/mate/document/MateTextFactory.java +9 -0
  39. data/src/com/redcareditor/mate/document/MateTextLocation.java +8 -0
  40. data/src/com/redcareditor/mate/document/MateTextLocationComparator.java +17 -0
  41. data/src/com/redcareditor/mate/document/MateTextRange.java +18 -0
  42. data/src/com/redcareditor/mate/document/swt/SwtMateDocument.java +143 -0
  43. data/src/com/redcareditor/mate/document/swt/SwtMateTextLocation.java +88 -0
  44. data/src/com/redcareditor/mate/document/swt/SwtMateTextRange.java +92 -0
  45. data/src/com/redcareditor/mate/document/swt/SwtScopePositionUpdater.java +90 -0
  46. data/src/com/redcareditor/mate/undo/MateTextUndoManager.java +11 -0
  47. data/src/com/redcareditor/mate/undo/swt/SwtMateTextUndoManager.java +166 -0
  48. data/src/com/redcareditor/onig/Match.java +212 -0
  49. data/src/com/redcareditor/onig/NullMatch.java +57 -0
  50. data/src/com/redcareditor/onig/NullRx.java +29 -0
  51. data/src/com/redcareditor/onig/Range.java +45 -0
  52. data/src/com/redcareditor/onig/Rx.java +167 -0
  53. data/src/com/redcareditor/plist/Dict.java +119 -0
  54. data/src/com/redcareditor/plist/PlistNode.java +52 -0
  55. data/src/com/redcareditor/plist/PlistPropertyLoader.java +44 -0
  56. data/src/com/redcareditor/theme/ScopeSelector.java +39 -0
  57. data/src/com/redcareditor/theme/Theme.java +122 -0
  58. data/src/com/redcareditor/theme/ThemeManager.java +41 -0
  59. data/src/com/redcareditor/theme/ThemeSetting.java +78 -0
  60. data/src/com/redcareditor/util/FileUtility.java +64 -0
  61. data/src/com/redcareditor/util/SingleLineFormatter.java +11 -0
  62. data/src/com/redcareditor/util/swt/ColourUtil.java +56 -0
  63. data/src/ruby/java-mateview.rb +68 -0
  64. data/test/com/redcareditor/mate/BundleTest.java +33 -0
  65. data/test/com/redcareditor/mate/EmptyRangeSetTest.java +27 -0
  66. data/test/com/redcareditor/mate/FilledRangeSetTest.java +82 -0
  67. data/test/com/redcareditor/mate/GrammarTest.java +158 -0
  68. data/test/com/redcareditor/mate/MateTextTest.java +35 -0
  69. data/test/com/redcareditor/mate/ScopeMatcherMatchingTest.java +55 -0
  70. data/test/com/redcareditor/mate/ScopeMatcherRankingTest.java +40 -0
  71. data/test/com/redcareditor/onig/RxTest.java +54 -0
  72. data/test/com/redcareditor/plist/DictTest.java +33 -0
  73. data/test/com/redcareditor/theme/RailsCastThemeTest.java +37 -0
  74. data/test/com/redcareditor/theme/ScopeSelectorTest.java +38 -0
  75. data/test/com/redcareditor/theme/ThemeManagerTest.java +29 -0
  76. data/test/com/redcareditor/util/swt/ColourUtilTest.java +17 -0
  77. metadata +142 -0
@@ -0,0 +1,33 @@
1
+
2
+ require File.join(File.dirname(__FILE__), *%w(.. src ruby java-mateview))
3
+
4
+ JavaMateView::Bundle.load_bundles("input/")
5
+ JavaMateView::ThemeManager.load_themes("input/")
6
+
7
+ class JavaMateView::MateText
8
+
9
+ def type(line, line_offset, char)
10
+ line_start = get_text_widget.get_offset_at_line(line)
11
+ getMateDocument.replace(line_start + line_offset, 0, char)
12
+ end
13
+
14
+ def backspace(line, line_offset)
15
+ line_start = get_text_widget.get_offset_at_line(line)
16
+ getMateDocument.replace(line_start + line_offset - 1, 1, "")
17
+ end
18
+
19
+ def clean_reparse
20
+ shell = Swt::Widgets::Shell.new($display)
21
+ mt = JavaMateView::MateText.new(shell, false)
22
+ mt.set_grammar_by_name(self.parser.grammar.name)
23
+ st = mt.get_text_widget
24
+ st.text = get_text_widget.getText
25
+ mt.parser.root.pretty(0)
26
+ end
27
+
28
+ def pretty
29
+ parser.root.pretty(0)
30
+ end
31
+ end
32
+
33
+ JavaMateView::ParserScheduler.synchronousParsing = true
@@ -0,0 +1,81 @@
1
+ package com.redcareditor.mate;
2
+
3
+ import java.io.File;
4
+ import java.util.ArrayList;
5
+ import java.util.List;
6
+
7
+ public class Bundle {
8
+ private String name;
9
+ private List<Grammar> grammars;
10
+ private static List<Bundle> bundles;
11
+
12
+ public Bundle(String name) {
13
+ this.name = name;
14
+ grammars = new ArrayList<Grammar>();
15
+ }
16
+
17
+ public static Bundle getBundleByName(String findName) {
18
+ for (Bundle b : getBundles()) {
19
+ if (b.getName().equals(findName))
20
+ return b;
21
+ }
22
+ return null;
23
+ }
24
+
25
+ public String getName() {
26
+ return name;
27
+ }
28
+
29
+ public List<Grammar> getGrammars() {
30
+ return grammars;
31
+ }
32
+
33
+ public static List<Bundle> getBundles() {
34
+ return bundles;
35
+ }
36
+
37
+ /**
38
+ * Return a list of bundle names like "Ruby.tmbundle"
39
+ */
40
+ public static List<String> bundleDirs(String textmateDir) {
41
+ List<String> names = new ArrayList<String>();
42
+ File dir = new File(textmateDir + "/Bundles");
43
+ if (dir.exists()) {
44
+ for (String name : dir.list()) {
45
+ if (name.endsWith(".tmbundle")) {
46
+ names.add(name);
47
+ }
48
+ }
49
+ }
50
+ return names;
51
+ }
52
+
53
+ public static void loadBundles(String textmateDir) {
54
+ if (getBundles() == null) {
55
+ bundles = new ArrayList<Bundle>();
56
+ }
57
+ for (String bundleDir : bundleDirs(textmateDir)) {
58
+ // System.out.printf("loading %s\n", bundleDir);
59
+ Bundle bundle = new Bundle(bundleDir.split("\\.")[0]);
60
+ getBundles().add(bundle);
61
+ File grammarDirectory = new File(textmateDir + "/Bundles/" + bundleDir + "/Syntaxes");
62
+ if (grammarDirectory.exists()) {
63
+ loadGrammar(bundle, grammarDirectory);
64
+ }
65
+ }
66
+ }
67
+
68
+ private static void loadGrammar(Bundle bundle, File syntaxDir) {
69
+ for (String grammarFileName : syntaxDir.list()) {
70
+ if (isTmBundlefile(grammarFileName)) {
71
+ String grammarFilePath = syntaxDir.getPath() + "/" + grammarFileName;
72
+ Grammar grammar = new Grammar(grammarFilePath);
73
+ bundle.getGrammars().add(grammar);
74
+ }
75
+ }
76
+ }
77
+
78
+ private static boolean isTmBundlefile(String syntaxFileName) {
79
+ return (syntaxFileName.endsWith(".tmLanguage") || syntaxFileName.endsWith(".plist"));
80
+ }
81
+ }
@@ -0,0 +1,89 @@
1
+ package com.redcareditor.mate;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.List;
5
+ import java.util.Map;
6
+
7
+ import org.joni.exception.ValueException;
8
+
9
+ import com.redcareditor.onig.Rx;
10
+ import com.redcareditor.plist.Dict;
11
+ import com.redcareditor.plist.PlistNode;
12
+
13
+ public class DoublePattern extends Pattern {
14
+ public String contentName;
15
+ public Rx begin;
16
+ public Rx end;
17
+ public String endString;
18
+ public String beginString;
19
+ public Map<Integer, String> beginCaptures;
20
+ public Map<Integer, String> endCaptures;
21
+ public Map<Integer, String> bothCaptures;
22
+ public List<Pattern> patterns;
23
+ public boolean hasReplacedGrammarIncludes = false;
24
+
25
+ public DoublePattern() {}
26
+
27
+ public DoublePattern(List<Pattern> grammarPatterns, Dict dict) {
28
+ name = dict.getString("name");
29
+ // System.out.printf("new DoublePattern name: %s\n", name);
30
+ try {
31
+ setDisabled(dict);
32
+ endString = dict.getString("end");
33
+ contentName = dict.getString("contentName");
34
+ begin = Rx.createRx(dict.getString("begin"));
35
+
36
+ loadCaptures(dict);
37
+ loadPatterns(grammarPatterns, dict);
38
+ grammarPatterns.add(this);
39
+ }
40
+ catch(ValueException e) {
41
+ System.out.printf("joni.exception.ValueException: %s in %s\n", e.getMessage(), dict.getString("begin"));
42
+ }
43
+ }
44
+
45
+ private void loadPatterns(List<Pattern> grammarPatterns, Dict dict) {
46
+ patterns = new ArrayList<Pattern>();
47
+ if (dict.containsElement("patterns")) {
48
+ for (PlistNode<?> plistPattern : dict.getArray("patterns")) {
49
+ Pattern subPattern = Pattern.createPattern(grammarPatterns, (Dict) plistPattern);
50
+ if (subPattern != null) {
51
+ patterns.add(subPattern);
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ private void loadCaptures(Dict dict) {
58
+ if (dict.containsElement("beginCaptures")) {
59
+ beginCaptures = Pattern.makeCapturesFromPlist(dict.getDictionary("beginCaptures"));
60
+ }
61
+ if (dict.containsElement("captures")) {
62
+ bothCaptures = Pattern.makeCapturesFromPlist(dict.getDictionary("captures"));
63
+ }
64
+ if (dict.containsElement("endCaptures")) {
65
+ endCaptures = Pattern.makeCapturesFromPlist(dict.getDictionary("endCaptures"));
66
+ }
67
+ }
68
+
69
+ public void replaceGrammarIncludes() {
70
+ if (hasReplacedGrammarIncludes)
71
+ return;
72
+ Grammar ng;
73
+ int i = 0;
74
+ while (i < patterns.size()) {
75
+ Pattern p = patterns.get(i);
76
+ if (p instanceof IncludePattern) {
77
+ if ((ng = Grammar.findByScopeName(p.name)) != null) {
78
+ ng.initForUse();
79
+ patterns.remove(i);
80
+ patterns.addAll(i, ng.patterns);
81
+ i--;
82
+ }
83
+ }
84
+ i++;
85
+ }
86
+ hasReplacedGrammarIncludes = true;
87
+ }
88
+
89
+ }
@@ -0,0 +1,129 @@
1
+ package com.redcareditor.mate;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.HashMap;
5
+ import java.util.List;
6
+ import java.util.Map;
7
+
8
+ import com.redcareditor.onig.Rx;
9
+ import com.redcareditor.plist.Dict;
10
+ import com.redcareditor.plist.PlistNode;
11
+ import com.redcareditor.plist.PlistPropertyLoader;
12
+
13
+ public class Grammar {
14
+ public String name;
15
+ public String fileName;
16
+ public Dict plist;
17
+ private PlistPropertyLoader propertyLoader;
18
+ public String[] fileTypes;
19
+ public String keyEquivalent;
20
+ public String scopeName;
21
+ public String comment;
22
+
23
+ public List<Pattern> allPatterns;
24
+ public List<Pattern> patterns;
25
+ public List<Pattern> singlePatterns;
26
+ public Map<String, List<Pattern>> repository;
27
+ public Rx firstLineMatch;
28
+ public Rx foldingStartMarker;
29
+ public Rx foldingStopMarker;
30
+
31
+ /* these are here for lookup speed purposes */
32
+ private static Map<String, Grammar> grammarsByScopeNames = new HashMap<String, Grammar>();
33
+
34
+ public Grammar(String plistFile){
35
+ this.plist = Dict.parseFile(plistFile);
36
+ propertyLoader = new PlistPropertyLoader(plist, this);
37
+ initForReference();
38
+ }
39
+
40
+ private void initForReference() {
41
+ String[] properties = new String[] { "name", "keyEquivalent", "scopeName", "comment" };
42
+ for (String property : properties) {
43
+ propertyLoader.loadStringProperty(property);
44
+ }
45
+ grammarsByScopeNames.put(scopeName, this);
46
+ if (scopeName == null)
47
+ System.out.printf("** WARNING: syntax %s has no top level scope name.\n", name);
48
+ propertyLoader.loadRegexProperty("firstLineMatch");
49
+ if (plist.containsElement("fileTypes"))
50
+ fileTypes = plist.getStrings("fileTypes");
51
+ }
52
+
53
+ public void initForUse() {
54
+ if (loaded())
55
+ return;
56
+
57
+ initForReference();
58
+ propertyLoader.loadRegexProperty("foldingStartMarker");
59
+ propertyLoader.loadRegexProperty("foldingStopMarker");
60
+
61
+ this.allPatterns = new ArrayList<Pattern>();
62
+ loadPatterns();
63
+ loadRepository();
64
+ replaceIncludePatterns();
65
+ }
66
+
67
+ private void loadPatterns() {
68
+ this.patterns = new ArrayList<Pattern>();
69
+ Dict[] dictPatterns = plist.getDictionaries("patterns");
70
+ for (Dict p : dictPatterns) {
71
+ Pattern pattern = Pattern.createPattern(allPatterns, p);
72
+ if (pattern != null) {
73
+ pattern.grammar = this;
74
+ this.patterns.add(pattern);
75
+ }
76
+ }
77
+ }
78
+
79
+ private void loadRepository() {
80
+ repository = new HashMap<String, List<Pattern>>();
81
+ Dict plistRepo = plist.getDictionary("repository");
82
+ if (plistRepo == null)
83
+ return;
84
+ Dict plistRepoEntry;
85
+ for (String key : plistRepo.keys()) {
86
+ // System.out.printf("loading repository entry: %s\n", key);
87
+ List<Pattern> repoArray = new ArrayList<Pattern>();
88
+ plistRepoEntry = plistRepo.getDictionary(key);
89
+ if (plistRepoEntry.containsElement("begin") || plistRepoEntry.containsElement("match")) {
90
+ // System.out.printf(" contains begin or match\n");
91
+ Pattern pattern = Pattern.createPattern(this.allPatterns, plistRepoEntry);
92
+ if (pattern != null) {
93
+ pattern.grammar = this;
94
+ repoArray.add(pattern);
95
+ }
96
+ }
97
+ else if (plistRepoEntry.containsElement("patterns")) {
98
+ // System.out.printf(" contains patterns\n");
99
+ for (PlistNode<?> plistPattern : plistRepoEntry.getArray("patterns")) {
100
+ Pattern pattern = Pattern.createPattern(this.allPatterns, (Dict) plistPattern);
101
+ if (pattern != null) {
102
+ pattern.grammar = this;
103
+ repoArray.add(pattern);
104
+ }
105
+ }
106
+ }
107
+ repository.put(key, repoArray);
108
+ }
109
+ }
110
+
111
+ private void replaceIncludePatterns() {
112
+ Pattern.replaceIncludePatterns(patterns, this);
113
+ for (Pattern p : allPatterns) {
114
+ // System.out.printf("%s replaceIncludePattern for %s\n", this.name, p.name);
115
+ if (p instanceof DoublePattern) {
116
+ Pattern.replaceIncludePatterns(((DoublePattern) p).patterns, this);
117
+ }
118
+ // System.out.printf("%s replaceIncludePattern for %s [done]\n", this.name, p.name);
119
+ }
120
+ }
121
+
122
+ public static Grammar findByScopeName(String scope) {
123
+ return grammarsByScopeNames.get(scope);
124
+ }
125
+
126
+ private boolean loaded() {
127
+ return allPatterns != null && repository != null;
128
+ }
129
+ }
@@ -0,0 +1,7 @@
1
+
2
+ package com.redcareditor.mate;
3
+
4
+ public interface IAnnotationAreaListener {
5
+ public void mouseClick(int line);
6
+ public void mouseDoubleClick(int line);
7
+ }
@@ -0,0 +1,5 @@
1
+ package com.redcareditor.mate;
2
+
3
+ public interface IGrammarListener {
4
+ public void grammarChanged(String new_name);
5
+ }
@@ -0,0 +1,10 @@
1
+ package com.redcareditor.mate;
2
+
3
+ import com.redcareditor.plist.Dict;
4
+
5
+ public class IncludePattern extends Pattern {
6
+ public IncludePattern(Dict dict) {
7
+ name = dict.getString("include");
8
+ // System.out.printf("ip: %s\n", name);
9
+ }
10
+ }
@@ -0,0 +1,922 @@
1
+ /*******************************************************************************
2
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
3
+ * All rights reserved. This program and the accompanying materials
4
+ * are made available under the terms of the Eclipse Public License v1.0
5
+ * which accompanies this distribution, and is available at
6
+ * http://www.eclipse.org/legal/epl-v10.html
7
+ *
8
+ * Contributors:
9
+ * IBM Corporation - initial API and implementation
10
+ * Nikolay Botev <bono8106@hotmail.com> - [rulers] Shift clicking in line number column doesn't select range - https://bugs.eclipse.org/bugs/show_bug.cgi?id=32166
11
+ * Nikolay Botev <bono8106@hotmail.com> - [rulers] Clicking in line number ruler should not trigger annotation ruler - https://bugs.eclipse.org/bugs/show_bug.cgi?id=40889
12
+ *******************************************************************************/
13
+ package com.redcareditor.mate;
14
+
15
+ import java.util.Arrays;
16
+
17
+ import org.eclipse.swt.SWT;
18
+ import org.eclipse.swt.custom.StyledText;
19
+ import org.eclipse.swt.events.DisposeEvent;
20
+ import org.eclipse.swt.events.DisposeListener;
21
+ import org.eclipse.swt.events.MouseEvent;
22
+ import org.eclipse.swt.events.MouseListener;
23
+ import org.eclipse.swt.events.MouseMoveListener;
24
+ import org.eclipse.swt.events.PaintEvent;
25
+ import org.eclipse.swt.events.PaintListener;
26
+ import org.eclipse.swt.graphics.Color;
27
+ import org.eclipse.swt.graphics.Font;
28
+ import org.eclipse.swt.graphics.FontMetrics;
29
+ import org.eclipse.swt.graphics.GC;
30
+ import org.eclipse.swt.graphics.Image;
31
+ import org.eclipse.swt.graphics.Point;
32
+ import org.eclipse.swt.graphics.Rectangle;
33
+ import org.eclipse.swt.widgets.Canvas;
34
+ import org.eclipse.swt.widgets.Composite;
35
+ import org.eclipse.swt.widgets.Control;
36
+ import org.eclipse.swt.widgets.Display;
37
+ import org.eclipse.swt.widgets.TypedListener;
38
+
39
+ import org.eclipse.jface.text.BadLocationException;
40
+ import org.eclipse.jface.text.IDocument;
41
+ import org.eclipse.jface.text.IRegion;
42
+ import org.eclipse.jface.text.ITextListener;
43
+ import org.eclipse.jface.text.ITextViewer;
44
+ import org.eclipse.jface.text.ITextViewerExtension;
45
+ import org.eclipse.jface.text.ITextViewerExtension5;
46
+ import org.eclipse.jface.text.IViewportListener;
47
+ import org.eclipse.jface.text.JFaceTextUtil;
48
+ import org.eclipse.jface.text.TextEvent;
49
+
50
+ import org.eclipse.jface.text.source.IVerticalRulerColumn;
51
+ import org.eclipse.jface.text.source.CompositeRuler;
52
+ import org.eclipse.jface.text.source.ILineRange;
53
+ import org.eclipse.jface.text.source.IAnnotationModel;
54
+
55
+
56
+ /**
57
+ * A vertical ruler column displaying line numbers.
58
+ * Clients usually instantiate and configure object of this class.
59
+ *
60
+ * @since 2.0
61
+ */
62
+ public class LineNumberRulerColumn implements IVerticalRulerColumn {
63
+
64
+ /**
65
+ * Internal listener class.
66
+ */
67
+ class InternalListener implements IViewportListener, ITextListener {
68
+
69
+ /**
70
+ * @since 3.1
71
+ */
72
+ private boolean fCachedRedrawState= true;
73
+
74
+ /*
75
+ * @see IViewportListener#viewportChanged(int)
76
+ */
77
+ public void viewportChanged(int verticalPosition) {
78
+ if (fCachedRedrawState && verticalPosition != fScrollPos)
79
+ redraw();
80
+ }
81
+
82
+ /*
83
+ * @see ITextListener#textChanged(TextEvent)
84
+ */
85
+ public void textChanged(TextEvent event) {
86
+
87
+ fCachedRedrawState= event.getViewerRedrawState();
88
+ if (!fCachedRedrawState)
89
+ return;
90
+
91
+ if (updateNumberOfDigits()) {
92
+ computeIndentations();
93
+ layout(event.getViewerRedrawState());
94
+ return;
95
+ }
96
+
97
+ boolean viewerCompletelyShown= isViewerCompletelyShown();
98
+ if (viewerCompletelyShown || fSensitiveToTextChanges || event.getDocumentEvent() == null)
99
+ postRedraw();
100
+ fSensitiveToTextChanges= viewerCompletelyShown;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Handles all the mouse interaction in this line number ruler column.
106
+ */
107
+ class MouseHandler implements MouseListener, MouseMoveListener {
108
+
109
+ /** The cached view port size. */
110
+ private int fCachedViewportSize;
111
+ /** The area of the line at which line selection started. */
112
+ private int fStartLineOffset;
113
+ /** The number of the line at which line selection started. */
114
+ private int fStartLineNumber;
115
+ /** The auto scroll direction. */
116
+ private int fAutoScrollDirection;
117
+ /* @since 3.2 */
118
+ private boolean fIsListeningForMove= false;
119
+
120
+ /*
121
+ * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
122
+ */
123
+ public void mouseUp(MouseEvent event) {
124
+ // see bug 45700
125
+ if (event.button == 1) {
126
+ stopSelecting();
127
+ stopAutoScroll();
128
+ }
129
+ }
130
+
131
+ /*
132
+ * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
133
+ */
134
+ public void mouseDown(MouseEvent event) {
135
+ fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
136
+ // see bug 45700
137
+ if (event.button == 1) {
138
+ startSelecting((event.stateMask & SWT.SHIFT) != 0);
139
+ }
140
+ }
141
+
142
+ /*
143
+ * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
144
+ */
145
+ public void mouseDoubleClick(MouseEvent event) {
146
+ fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
147
+ stopSelecting();
148
+ stopAutoScroll();
149
+ }
150
+
151
+ /*
152
+ * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
153
+ */
154
+ public void mouseMove(MouseEvent event) {
155
+ if (fIsListeningForMove && !autoScroll(event)) {
156
+ int newLine= fParentRuler.toDocumentLineNumber(event.y);
157
+ expandSelection(newLine);
158
+ }
159
+ fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
160
+ }
161
+
162
+ /**
163
+ * Called when line drag selection started. Adds mouse move and track
164
+ * listeners to this column's control.
165
+ *
166
+ * @param expandExistingSelection if <code>true</code> the existing selection will be expanded,
167
+ * otherwise a new selection is started
168
+ */
169
+ private void startSelecting(boolean expandExistingSelection) {
170
+ try {
171
+
172
+ // select line
173
+ IDocument document= fCachedTextViewer.getDocument();
174
+ int lineNumber= fParentRuler.getLineOfLastMouseButtonActivity();
175
+ if (expandExistingSelection && fCachedTextViewer instanceof ITextViewerExtension5
176
+ && fCachedTextViewer.getTextWidget() != null) {
177
+ ITextViewerExtension5 extension5= ((ITextViewerExtension5)fCachedTextViewer);
178
+ // Find model curosr position
179
+ int widgetCaret= fCachedTextViewer.getTextWidget().getCaretOffset();
180
+ int modelCaret= extension5.widgetOffset2ModelOffset(widgetCaret);
181
+ // Find model selection range
182
+ Point selection= fCachedTextViewer.getSelectedRange();
183
+ // Start from tail of selection range (opposite of cursor position)
184
+ int startOffset= modelCaret == selection.x ? selection.x + selection.y : selection.x;
185
+
186
+ fStartLineNumber= document.getLineOfOffset(startOffset);
187
+ fStartLineOffset= startOffset;
188
+
189
+ expandSelection(lineNumber);
190
+ } else {
191
+ fStartLineNumber= lineNumber;
192
+ fStartLineOffset= document.getLineInformation(fStartLineNumber).getOffset();
193
+ fCachedTextViewer.setSelectedRange(fStartLineOffset, 0);
194
+ }
195
+ fCachedViewportSize= getVisibleLinesInViewport();
196
+
197
+ // prepare for drag selection
198
+ fIsListeningForMove= true;
199
+
200
+ } catch (BadLocationException x) {
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Called when line drag selection stopped. Removes all previously
206
+ * installed listeners from this column's control.
207
+ */
208
+ private void stopSelecting() {
209
+ // drag selection stopped
210
+ fIsListeningForMove= false;
211
+ }
212
+
213
+ /**
214
+ * Expands the line selection from the remembered start line to the
215
+ * given line.
216
+ *
217
+ * @param lineNumber the line to which to expand the selection
218
+ */
219
+ private void expandSelection(int lineNumber) {
220
+ try {
221
+
222
+ IDocument document= fCachedTextViewer.getDocument();
223
+ IRegion lineInfo= document.getLineInformation(lineNumber);
224
+
225
+ Display display= fCachedTextWidget.getDisplay();
226
+ Point absolutePosition= display.getCursorLocation();
227
+ Point relativePosition= fCachedTextWidget.toControl(absolutePosition);
228
+
229
+ int offset;
230
+
231
+ if (relativePosition.x < 0)
232
+ offset= lineInfo.getOffset();
233
+ else {
234
+ try {
235
+ int widgetOffset= fCachedTextWidget.getOffsetAtLocation(relativePosition);
236
+ Point p= fCachedTextWidget.getLocationAtOffset(widgetOffset);
237
+ if (p.x > relativePosition.x)
238
+ widgetOffset--;
239
+
240
+ // Convert to model offset
241
+ if (fCachedTextViewer instanceof ITextViewerExtension5) {
242
+ ITextViewerExtension5 extension= (ITextViewerExtension5)fCachedTextViewer;
243
+ offset= extension.widgetOffset2ModelOffset(widgetOffset);
244
+ } else
245
+ offset= widgetOffset + fCachedTextViewer.getVisibleRegion().getOffset();
246
+
247
+ } catch (IllegalArgumentException ex) {
248
+ int lineEndOffset= lineInfo.getOffset() + lineInfo.getLength();
249
+
250
+ // Convert to widget offset
251
+ int lineEndWidgetOffset;
252
+ if (fCachedTextViewer instanceof ITextViewerExtension5) {
253
+ ITextViewerExtension5 extension= (ITextViewerExtension5)fCachedTextViewer;
254
+ lineEndWidgetOffset= extension.modelOffset2WidgetOffset(lineEndOffset);
255
+ } else
256
+ lineEndWidgetOffset= lineEndOffset - fCachedTextViewer.getVisibleRegion().getOffset();
257
+
258
+ Point p= fCachedTextWidget.getLocationAtOffset(lineEndWidgetOffset);
259
+ if (p.x < relativePosition.x)
260
+ offset= lineEndOffset;
261
+ else
262
+ offset= lineInfo.getOffset();
263
+ }
264
+ }
265
+
266
+ int start= Math.min(fStartLineOffset, offset);
267
+ int end= Math.max(fStartLineOffset, offset);
268
+
269
+ if (lineNumber < fStartLineNumber)
270
+ fCachedTextViewer.setSelectedRange(end, start - end);
271
+ else
272
+ fCachedTextViewer.setSelectedRange(start, end - start);
273
+
274
+ } catch (BadLocationException x) {
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Called when auto scrolling stopped. Clears the auto scroll direction.
280
+ */
281
+ private void stopAutoScroll() {
282
+ fAutoScrollDirection= SWT.NULL;
283
+ }
284
+
285
+ /**
286
+ * Called on drag selection.
287
+ *
288
+ * @param event the mouse event caught by the mouse move listener
289
+ * @return <code>true</code> if scrolling happened, <code>false</code> otherwise
290
+ */
291
+ private boolean autoScroll(MouseEvent event) {
292
+ Rectangle area= fCanvas.getClientArea();
293
+
294
+ if (event.y > area.height) {
295
+ autoScroll(SWT.DOWN);
296
+ return true;
297
+ }
298
+
299
+ if (event.y < 0) {
300
+ autoScroll(SWT.UP);
301
+ return true;
302
+ }
303
+
304
+ stopAutoScroll();
305
+ return false;
306
+ }
307
+
308
+ /**
309
+ * Scrolls the viewer into the given direction.
310
+ *
311
+ * @param direction the scroll direction
312
+ */
313
+ private void autoScroll(int direction) {
314
+
315
+ if (fAutoScrollDirection == direction)
316
+ return;
317
+
318
+ final int TIMER_INTERVAL= 5;
319
+ final Display display= fCanvas.getDisplay();
320
+ Runnable timer= null;
321
+ switch (direction) {
322
+ case SWT.UP:
323
+ timer= new Runnable() {
324
+ public void run() {
325
+ if (fAutoScrollDirection == SWT.UP) {
326
+ int top= getInclusiveTopIndex();
327
+ if (top > 0) {
328
+ fCachedTextViewer.setTopIndex(top -1);
329
+ expandSelection(top -1);
330
+ display.timerExec(TIMER_INTERVAL, this);
331
+ }
332
+ }
333
+ }
334
+ };
335
+ break;
336
+ case SWT.DOWN:
337
+ timer= new Runnable() {
338
+ public void run() {
339
+ if (fAutoScrollDirection == SWT.DOWN) {
340
+ int top= getInclusiveTopIndex();
341
+ fCachedTextViewer.setTopIndex(top +1);
342
+ expandSelection(top +1 + fCachedViewportSize);
343
+ display.timerExec(TIMER_INTERVAL, this);
344
+ }
345
+ }
346
+ };
347
+ break;
348
+ }
349
+
350
+ if (timer != null) {
351
+ fAutoScrollDirection= direction;
352
+ display.timerExec(TIMER_INTERVAL, timer);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Returns the viewer's first visible line, even if only partially visible.
358
+ *
359
+ * @return the viewer's first visible line
360
+ */
361
+ private int getInclusiveTopIndex() {
362
+ if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed()) {
363
+ return JFaceTextUtil.getPartialTopIndex(fCachedTextViewer);
364
+ }
365
+ return -1;
366
+ }
367
+ }
368
+
369
+ /** This column's parent ruler */
370
+ private CompositeRuler fParentRuler;
371
+ /** Cached text viewer */
372
+ private ITextViewer fCachedTextViewer;
373
+ /** Cached text widget */
374
+ private StyledText fCachedTextWidget;
375
+ /** The columns canvas */
376
+ private Canvas fCanvas;
377
+ /** Cache for the actual scroll position in pixels */
378
+ private int fScrollPos;
379
+ /** The drawable for double buffering */
380
+ private Image fBuffer;
381
+ /** The internal listener */
382
+ private InternalListener fInternalListener= new InternalListener();
383
+ /** The font of this column */
384
+ private Font fFont;
385
+ /** The indentation cache */
386
+ private int[] fIndentation;
387
+ /** Indicates whether this column reacts on text change events */
388
+ private boolean fSensitiveToTextChanges= false;
389
+ /** The foreground color */
390
+ private Color fForeground;
391
+ /** The background color */
392
+ private Color fBackground;
393
+ /** Cached number of displayed digits */
394
+ private int fCachedNumberOfDigits= -1;
395
+ /** Flag indicating whether a relayout is required */
396
+ private boolean fRelayoutRequired= false;
397
+ /**
398
+ * Redraw runnable lock
399
+ * @since 3.0
400
+ */
401
+ private Object fRunnableLock= new Object();
402
+ /**
403
+ * Redraw runnable state
404
+ * @since 3.0
405
+ */
406
+ private boolean fIsRunnablePosted= false;
407
+ /**
408
+ * Redraw runnable
409
+ * @since 3.0
410
+ */
411
+ private Runnable fRunnable= new Runnable() {
412
+ public void run() {
413
+ synchronized (fRunnableLock) {
414
+ fIsRunnablePosted= false;
415
+ }
416
+ redraw();
417
+ }
418
+ };
419
+ /* @since 3.2 */
420
+ private MouseHandler fMouseHandler;
421
+
422
+
423
+ /**
424
+ * Constructs a new vertical ruler column.
425
+ */
426
+ public LineNumberRulerColumn() {
427
+ }
428
+
429
+ /**
430
+ * Sets the foreground color of this column.
431
+ *
432
+ * @param foreground the foreground color
433
+ */
434
+ public void setForeground(Color foreground) {
435
+ fForeground= foreground;
436
+ }
437
+
438
+ /**
439
+ * Returns the foreground color being used to print the line numbers.
440
+ *
441
+ * @return the configured foreground color
442
+ * @since 3.0
443
+ */
444
+ protected Color getForeground() {
445
+ return fForeground;
446
+ }
447
+
448
+ /**
449
+ * Sets the background color of this column.
450
+ *
451
+ * @param background the background color
452
+ */
453
+ public void setBackground(Color background) {
454
+ fBackground= background;
455
+ if (fCanvas != null && !fCanvas.isDisposed())
456
+ fCanvas.setBackground(getBackground(fCanvas.getDisplay()));
457
+ }
458
+
459
+ /**
460
+ * Returns the System background color for list widgets.
461
+ *
462
+ * @param display the display
463
+ * @return the System background color for list widgets
464
+ */
465
+ protected Color getBackground(Display display) {
466
+ if (fBackground == null)
467
+ return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
468
+ return fBackground;
469
+ }
470
+
471
+ /*
472
+ * @see IVerticalRulerColumn#getControl()
473
+ */
474
+ public Control getControl() {
475
+ return fCanvas;
476
+ }
477
+
478
+ /*
479
+ * @see IVerticalRuleColumnr#getWidth
480
+ */
481
+ public int getWidth() {
482
+ return fIndentation[0];
483
+ }
484
+
485
+ /**
486
+ * Computes the number of digits to be displayed. Returns
487
+ * <code>true</code> if the number of digits changed compared
488
+ * to the previous call of this method. If the method is called
489
+ * for the first time, the return value is also <code>true</code>.
490
+ *
491
+ * @return whether the number of digits has been changed
492
+ * @since 3.0
493
+ */
494
+ protected boolean updateNumberOfDigits() {
495
+ if (fCachedTextViewer == null)
496
+ return false;
497
+
498
+ int digits= computeNumberOfDigits();
499
+
500
+ if (fCachedNumberOfDigits != digits) {
501
+ fCachedNumberOfDigits= digits;
502
+ return true;
503
+ }
504
+
505
+ return false;
506
+ }
507
+
508
+ /**
509
+ * Does the real computation of the number of digits. Subclasses may override this method if
510
+ * they need extra space on the line number ruler.
511
+ *
512
+ * @return the number of digits to be displayed on the line number ruler.
513
+ */
514
+ protected int computeNumberOfDigits() {
515
+ IDocument document= fCachedTextViewer.getDocument();
516
+ int lines= document == null ? 0 : document.getNumberOfLines();
517
+
518
+ int digits= 2;
519
+ while (lines > Math.pow(10, digits) -1) {
520
+ ++digits;
521
+ }
522
+ return Math.max(digits, 3);
523
+ }
524
+
525
+ /**
526
+ * Layouts the enclosing viewer to adapt the layout to changes of the
527
+ * size of the individual components.
528
+ *
529
+ * @param redraw <code>true</code> if this column can be redrawn
530
+ */
531
+ protected void layout(boolean redraw) {
532
+ if (!redraw) {
533
+ fRelayoutRequired= true;
534
+ return;
535
+ }
536
+
537
+ fRelayoutRequired= false;
538
+ if (fCachedTextViewer instanceof ITextViewerExtension) {
539
+ ITextViewerExtension extension= (ITextViewerExtension) fCachedTextViewer;
540
+ Control control= extension.getControl();
541
+ if (control instanceof Composite && !control.isDisposed()) {
542
+ Composite composite= (Composite) control;
543
+ composite.layout(true);
544
+ }
545
+ }
546
+ }
547
+
548
+ /**
549
+ * Computes the indentations for the given font and stores them in
550
+ * <code>fIndentation</code>.
551
+ */
552
+ protected void computeIndentations() {
553
+ if (fCanvas == null || fCanvas.isDisposed())
554
+ return;
555
+
556
+ GC gc= new GC(fCanvas);
557
+ try {
558
+
559
+ gc.setFont(fCanvas.getFont());
560
+
561
+ fIndentation= new int[fCachedNumberOfDigits + 1];
562
+
563
+ char[] nines= new char[fCachedNumberOfDigits];
564
+ Arrays.fill(nines, '9');
565
+ String nineString= new String(nines);
566
+ Point p= gc.stringExtent(nineString);
567
+ fIndentation[0]= p.x;
568
+
569
+ for (int i= 1; i <= fCachedNumberOfDigits; i++) {
570
+ p= gc.stringExtent(nineString.substring(0, i));
571
+ fIndentation[i]= fIndentation[0] - p.x;
572
+ }
573
+
574
+ } finally {
575
+ gc.dispose();
576
+ }
577
+ }
578
+
579
+ /*
580
+ * @see IVerticalRulerColumn#createControl(CompositeRuler, Composite)
581
+ */
582
+ public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
583
+
584
+ fParentRuler= parentRuler;
585
+ fCachedTextViewer= parentRuler.getTextViewer();
586
+ fCachedTextWidget= fCachedTextViewer.getTextWidget();
587
+
588
+ fCanvas= new Canvas(parentControl, SWT.NO_FOCUS ) {
589
+ /*
590
+ * @see org.eclipse.swt.widgets.Control#addMouseListener(org.eclipse.swt.events.MouseListener)
591
+ * @since 3.4
592
+ */
593
+ public void addMouseListener(MouseListener listener) {
594
+ // see bug 40889, bug 230073 and AnnotationRulerColumn#isPropagatingMouseListener()
595
+ if (listener == fMouseHandler)
596
+ super.addMouseListener(listener);
597
+ else {
598
+ TypedListener typedListener= null;
599
+ if (listener != null)
600
+ typedListener= new TypedListener(listener);
601
+ addListener(SWT.MouseDoubleClick, typedListener);
602
+ }
603
+ }
604
+ };
605
+ fCanvas.setBackground(getBackground(fCanvas.getDisplay()));
606
+ fCanvas.setForeground(fForeground);
607
+
608
+ fCanvas.addPaintListener(new PaintListener() {
609
+ public void paintControl(PaintEvent event) {
610
+ if (fCachedTextViewer != null)
611
+ doubleBufferPaint(event.gc);
612
+ }
613
+ });
614
+
615
+ fCanvas.addDisposeListener(new DisposeListener() {
616
+ public void widgetDisposed(DisposeEvent e) {
617
+ handleDispose();
618
+ fCachedTextViewer= null;
619
+ fCachedTextWidget= null;
620
+ }
621
+ });
622
+
623
+ fMouseHandler= new MouseHandler();
624
+ fCanvas.addMouseListener(fMouseHandler);
625
+ fCanvas.addMouseMoveListener(fMouseHandler);
626
+
627
+ if (fCachedTextViewer != null) {
628
+
629
+ fCachedTextViewer.addViewportListener(fInternalListener);
630
+ fCachedTextViewer.addTextListener(fInternalListener);
631
+
632
+ if (fFont == null) {
633
+ if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed())
634
+ fFont= fCachedTextWidget.getFont();
635
+ }
636
+ }
637
+
638
+ if (fFont != null)
639
+ fCanvas.setFont(fFont);
640
+
641
+ updateNumberOfDigits();
642
+ computeIndentations();
643
+ return fCanvas;
644
+ }
645
+
646
+ /**
647
+ * Disposes the column's resources.
648
+ */
649
+ protected void handleDispose() {
650
+
651
+ if (fCachedTextViewer != null) {
652
+ fCachedTextViewer.removeViewportListener(fInternalListener);
653
+ fCachedTextViewer.removeTextListener(fInternalListener);
654
+ }
655
+
656
+ if (fBuffer != null) {
657
+ fBuffer.dispose();
658
+ fBuffer= null;
659
+ }
660
+ }
661
+
662
+ /**
663
+ * Double buffer drawing.
664
+ *
665
+ * @param dest the GC to draw into
666
+ */
667
+ private void doubleBufferPaint(GC dest) {
668
+
669
+ Point size= fCanvas.getSize();
670
+
671
+ if (size.x <= 0 || size.y <= 0)
672
+ return;
673
+
674
+ if (fBuffer != null) {
675
+ Rectangle r= fBuffer.getBounds();
676
+ if (r.width != size.x || r.height != size.y) {
677
+ fBuffer.dispose();
678
+ fBuffer= null;
679
+ }
680
+ }
681
+ if (fBuffer == null)
682
+ fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
683
+
684
+ GC gc= new GC(fBuffer);
685
+ gc.setFont(fCanvas.getFont());
686
+ if (fForeground != null)
687
+ gc.setForeground(fForeground);
688
+
689
+ try {
690
+ gc.setBackground(getBackground(fCanvas.getDisplay()));
691
+ gc.fillRectangle(0, 0, size.x, size.y);
692
+
693
+ ILineRange visibleLines= JFaceTextUtil.getVisibleModelLines(fCachedTextViewer);
694
+ if (visibleLines == null)
695
+ return;
696
+ fScrollPos= fCachedTextWidget.getTopPixel();
697
+ doPaint(gc, visibleLines);
698
+ } finally {
699
+ gc.dispose();
700
+ }
701
+
702
+ dest.drawImage(fBuffer, 0, 0);
703
+ }
704
+
705
+ /**
706
+ * Returns the view port height in lines.
707
+ *
708
+ * @return the view port height in lines
709
+ * @deprecated as of 3.2 the number of lines in the viewport cannot be computed because
710
+ * StyledText supports variable line heights
711
+ */
712
+ protected int getVisibleLinesInViewport() {
713
+ return getVisibleLinesInViewport(fCachedTextWidget);
714
+ }
715
+
716
+
717
+ /**
718
+ * Returns <code>true</code> if the viewport displays the entire viewer contents, i.e. the
719
+ * viewer is not vertically scrollable.
720
+ *
721
+ * @return <code>true</code> if the viewport displays the entire contents, <code>false</code> otherwise
722
+ * @since 3.2
723
+ */
724
+ protected final boolean isViewerCompletelyShown() {
725
+ return JFaceTextUtil.isShowingEntireContents(fCachedTextWidget);
726
+ }
727
+
728
+ /**
729
+ * Draws the ruler column.
730
+ *
731
+ * @param gc the GC to draw into
732
+ * @param visibleLines the visible model lines
733
+ * @since 3.2
734
+ */
735
+ void doPaint(GC gc, ILineRange visibleLines) {
736
+ Display display= fCachedTextWidget.getDisplay();
737
+
738
+ // draw diff info
739
+ int y= -JFaceTextUtil.getHiddenTopLinePixels(fCachedTextWidget);
740
+
741
+ int lastLine= end(visibleLines);
742
+ int prevLine= -1;
743
+ int actualModelLine= -1;
744
+ for (int line= visibleLines.getStartLine(); line < lastLine; ) {
745
+ int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line);
746
+ if (widgetLine == -1)
747
+ continue;
748
+
749
+ int lineHeight= fCachedTextWidget.getLineHeight(fCachedTextWidget.getOffsetAtLine(widgetLine));
750
+ if (fCachedTextWidget.getWordWrap()) {
751
+ Point pt = new Point(5, y);
752
+ actualModelLine = fCachedTextWidget.getLineAtOffset(fCachedTextWidget.getOffsetAtLocation(pt));
753
+ if (actualModelLine != prevLine) {
754
+ paintLine(line, y, lineHeight, gc, display);
755
+ line++;
756
+ }
757
+ else {
758
+ }
759
+ prevLine = actualModelLine;
760
+ }
761
+ else {
762
+ paintLine(line, y, lineHeight, gc, display);
763
+ line++;
764
+ }
765
+ y += lineHeight;
766
+ }
767
+ }
768
+
769
+ /* @since 3.2 */
770
+ private static int end(ILineRange range) {
771
+ return range.getStartLine() + range.getNumberOfLines();
772
+ }
773
+
774
+ /**
775
+ * Computes the string to be printed for <code>line</code>. The default implementation returns
776
+ * <code>Integer.toString(line + 1)</code>.
777
+ *
778
+ * @param line the line number for which the line number string is generated
779
+ * @return the string to be printed on the line number bar for <code>line</code>
780
+ * @since 3.0
781
+ */
782
+ protected String createDisplayString(int line) {
783
+ return Integer.toString(line + 1);
784
+ }
785
+
786
+ /**
787
+ * Returns the difference between the baseline of the widget and the
788
+ * baseline as specified by the font for <code>gc</code>. When drawing
789
+ * line numbers, the returned bias should be added to obtain text lined up
790
+ * on the correct base line of the text widget.
791
+ *
792
+ * @param gc the <code>GC</code> to get the font metrics from
793
+ * @param widgetLine the widget line
794
+ * @return the baseline bias to use when drawing text that is lined up with
795
+ * <code>fCachedTextWidget</code>
796
+ * @since 3.2
797
+ */
798
+ private int getBaselineBias(GC gc, int widgetLine) {
799
+ /*
800
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951
801
+ * widget line height may be more than the font height used for the
802
+ * line numbers, since font styles (bold, italics...) can have larger
803
+ * font metrics than the simple font used for the numbers.
804
+ */
805
+ int offset= fCachedTextWidget.getOffsetAtLine(widgetLine);
806
+ int widgetBaseline= fCachedTextWidget.getBaseline(offset);
807
+
808
+ FontMetrics fm= gc.getFontMetrics();
809
+ int fontBaseline= fm.getAscent() + fm.getLeading();
810
+ int baselineBias= widgetBaseline - fontBaseline;
811
+ return Math.max(0, baselineBias);
812
+ }
813
+
814
+ /**
815
+ * Paints the line. After this method is called the line numbers are painted on top
816
+ * of the result of this method.
817
+ *
818
+ * @param line the line of the document which the ruler is painted for
819
+ * @param y the y-coordinate of the box being painted for <code>line</code>, relative to <code>gc</code>
820
+ * @param lineheight the height of one line (and therefore of the box being painted)
821
+ * @param gc the drawing context the client may choose to draw on.
822
+ * @param display the display the drawing occurs on
823
+ * @since 3.0
824
+ */
825
+ protected void paintLine(int line, int y, int lineheight, GC gc, Display display) {
826
+ int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line);
827
+
828
+ String s= createDisplayString(line);
829
+ int indentation= fIndentation[s.length()];
830
+ int baselineBias= getBaselineBias(gc, widgetLine);
831
+ gc.drawString(s, indentation, y + baselineBias, true);
832
+ }
833
+
834
+ /**
835
+ * Triggers a redraw in the display thread.
836
+ *
837
+ * @since 3.0
838
+ */
839
+ protected final void postRedraw() {
840
+ if (fCanvas != null && !fCanvas.isDisposed()) {
841
+ Display d= fCanvas.getDisplay();
842
+ if (d != null) {
843
+ synchronized (fRunnableLock) {
844
+ if (fIsRunnablePosted)
845
+ return;
846
+ fIsRunnablePosted= true;
847
+ }
848
+ d.asyncExec(fRunnable);
849
+ }
850
+ }
851
+ }
852
+
853
+ /*
854
+ * @see IVerticalRulerColumn#redraw()
855
+ */
856
+ public void redraw() {
857
+
858
+ if (fRelayoutRequired) {
859
+ layout(true);
860
+ return;
861
+ }
862
+
863
+ if (fCachedTextViewer != null && fCanvas != null && !fCanvas.isDisposed()) {
864
+ GC gc= new GC(fCanvas);
865
+ doubleBufferPaint(gc);
866
+ gc.dispose();
867
+ }
868
+ }
869
+
870
+ /*
871
+ * @see IVerticalRulerColumn#setModel(IAnnotationModel)
872
+ */
873
+ public void setModel(IAnnotationModel model) {
874
+ }
875
+
876
+ /*
877
+ * @see IVerticalRulerColumn#setFont(Font)
878
+ */
879
+ public void setFont(Font font) {
880
+ fFont= font;
881
+ if (fCanvas != null && !fCanvas.isDisposed()) {
882
+ fCanvas.setFont(fFont);
883
+ updateNumberOfDigits();
884
+ computeIndentations();
885
+ }
886
+ }
887
+
888
+ /**
889
+ * Returns the parent (composite) ruler of this ruler column.
890
+ *
891
+ * @return the parent ruler
892
+ * @since 3.0
893
+ */
894
+ protected CompositeRuler getParentRuler() {
895
+ return fParentRuler;
896
+ }
897
+
898
+
899
+ /**
900
+ * Returns the number of lines in the view port.
901
+ *
902
+ * @param textWidget the styled text widget
903
+ * @return the number of lines visible in the view port <code>-1</code> if there's no client
904
+ * area
905
+ * @deprecated this method should not be used - it relies on the widget using a uniform line
906
+ * height
907
+ */
908
+ static int getVisibleLinesInViewport(StyledText textWidget) {
909
+ if (textWidget != null) {
910
+ Rectangle clArea= textWidget.getClientArea();
911
+ if (!clArea.isEmpty()) {
912
+ int firstPixel= 0;
913
+ int lastPixel= clArea.height - 1; // XXX: what about margins? don't take trims as they include scrollbars
914
+ int first= JFaceTextUtil.getLineIndex(textWidget, firstPixel);
915
+ int last= JFaceTextUtil.getLineIndex(textWidget, lastPixel);
916
+ return last - first;
917
+ }
918
+ }
919
+ return -1;
920
+ }
921
+
922
+ }