redcar-javamateview 0.2

Sign up to get free protection for your applications and to get access to all the features.
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 +134 -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
+ }