1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 package com.sun.star.script.framework.provider.beanshell; 24 25 import javax.swing.JTextArea; 26 import javax.swing.JScrollPane; 27 import javax.swing.JComponent; 28 import javax.swing.event.DocumentListener; 29 import javax.swing.event.DocumentEvent; 30 31 import java.awt.Graphics; 32 import java.awt.Color; 33 import java.awt.Font; 34 import java.awt.FontMetrics; 35 import java.awt.Polygon; 36 import java.awt.Rectangle; 37 import java.awt.Dimension; 38 39 public class PlainSourceView extends JScrollPane 40 implements ScriptSourceView, DocumentListener { 41 42 private ScriptSourceModel model; 43 private JTextArea ta; 44 private GlyphGutter gg; 45 private int linecount; 46 private boolean isModified = false; 47 PlainSourceView(ScriptSourceModel model)48 public PlainSourceView(ScriptSourceModel model) { 49 this.model = model; 50 initUI(); 51 model.setView(this); 52 } 53 clear()54 public void clear() { 55 ta.setText(""); 56 } 57 update()58 public void update() { 59 /* Remove ourselves as a DocumentListener while loading the source 60 so we don't get a storm of DocumentEvents during loading */ 61 ta.getDocument().removeDocumentListener(this); 62 63 if (isModified == false) 64 { 65 int pos = ta.getCaretPosition(); 66 ta.setText(model.getText()); 67 try { 68 ta.setCaretPosition(pos); 69 } 70 catch (IllegalArgumentException iae) { 71 // do nothing and allow JTextArea to set it's own position 72 } 73 } 74 75 // scroll to currentPosition of the model 76 try { 77 int line = ta.getLineStartOffset(model.getCurrentPosition()); 78 Rectangle rect = ta.modelToView(line); 79 ta.scrollRectToVisible(rect); 80 } 81 catch (Exception e) { 82 // couldn't scroll to line, do nothing 83 } 84 85 gg.repaint(); 86 87 // Add back the listener 88 ta.getDocument().addDocumentListener(this); 89 } 90 isModified()91 public boolean isModified() { 92 return isModified; 93 } 94 setModified(boolean value)95 public void setModified(boolean value) { 96 isModified = value; 97 } 98 initUI()99 private void initUI() { 100 ta = new JTextArea(); 101 ta.setRows(15); 102 ta.setColumns(40); 103 ta.setLineWrap(false); 104 ta.insert(model.getText(), 0); 105 linecount = ta.getLineCount(); 106 107 gg = new GlyphGutter(this); 108 109 setViewportView(ta); 110 setRowHeaderView(gg); 111 112 ta.getDocument().addDocumentListener(this); 113 } 114 115 /* Implementation of DocumentListener interface */ insertUpdate(DocumentEvent e)116 public void insertUpdate(DocumentEvent e) { 117 doChanged(e); 118 } 119 removeUpdate(DocumentEvent e)120 public void removeUpdate(DocumentEvent e) { 121 doChanged(e); 122 } 123 changedUpdate(DocumentEvent e)124 public void changedUpdate(DocumentEvent e) { 125 doChanged(e); 126 } 127 128 /* If the number of lines in the JTextArea has changed then update the 129 GlyphGutter */ doChanged(DocumentEvent e)130 public void doChanged(DocumentEvent e) { 131 isModified = true; 132 133 if (linecount != ta.getLineCount()) { 134 gg.update(); 135 linecount = ta.getLineCount(); 136 } 137 } 138 getText()139 public String getText() { 140 return ta.getText(); 141 } 142 getTextArea()143 public JTextArea getTextArea() { 144 return ta; 145 } 146 getCurrentPosition()147 public int getCurrentPosition() { 148 return model.getCurrentPosition(); 149 } 150 } 151 152 class GlyphGutter extends JComponent { 153 154 private PlainSourceView view; 155 private final String DUMMY_STRING = "99"; 156 GlyphGutter(PlainSourceView view)157 GlyphGutter(PlainSourceView view) { 158 this.view = view; 159 update(); 160 } 161 update()162 public void update() { 163 JTextArea textArea = view.getTextArea(); 164 Font font = textArea.getFont(); 165 setFont(font); 166 167 FontMetrics metrics = getFontMetrics(font); 168 int h = metrics.getHeight(); 169 int lineCount = textArea.getLineCount() + 1; 170 171 String dummy = Integer.toString(lineCount); 172 if (dummy.length() < 2) { 173 dummy = DUMMY_STRING; 174 } 175 176 Dimension d = new Dimension(); 177 d.width = metrics.stringWidth(dummy) + 16; 178 d.height = lineCount * h + 100; 179 setPreferredSize(d); 180 setSize(d); 181 } 182 paintComponent(Graphics g)183 public void paintComponent(Graphics g) { 184 JTextArea textArea = view.getTextArea(); 185 186 Font font = textArea.getFont(); 187 g.setFont(font); 188 189 FontMetrics metrics = getFontMetrics(font); 190 Rectangle clip = g.getClipBounds(); 191 192 g.setColor(getBackground()); 193 g.fillRect(clip.x, clip.y, clip.width, clip.height); 194 195 int ascent = metrics.getMaxAscent(); 196 int h = metrics.getHeight(); 197 int lineCount = textArea.getLineCount() + 1; 198 199 int startLine = clip.y / h; 200 int endLine = (clip.y + clip.height) / h + 1; 201 int width = getWidth(); 202 if (endLine > lineCount) { 203 endLine = lineCount; 204 } 205 206 for (int i = startLine; i < endLine; i++) { 207 String text; 208 text = Integer.toString(i + 1) + " "; 209 int w = metrics.stringWidth(text); 210 int y = i * h; 211 g.setColor(Color.blue); 212 g.drawString(text, 0, y + ascent); 213 int x = width - ascent; 214 215 // if currentPosition is not -1 then a red arrow will be drawn 216 if (i == view.getCurrentPosition()) { 217 drawArrow(g, ascent, x, y); 218 } 219 } 220 } 221 drawArrow(Graphics g, int ascent, int x, int y)222 private void drawArrow(Graphics g, int ascent, int x, int y) { 223 Polygon arrow = new Polygon(); 224 int dx = x; 225 y += ascent - 10; 226 int dy = y; 227 arrow.addPoint(dx, dy + 3); 228 arrow.addPoint(dx + 5, dy + 3); 229 for (x = dx + 5; x <= dx + 10; x++, y++) { 230 arrow.addPoint(x, y); 231 } 232 for (x = dx + 9; x >= dx + 5; x--, y++) { 233 arrow.addPoint(x, y); 234 } 235 arrow.addPoint(dx + 5, dy + 7); 236 arrow.addPoint(dx, dy + 7); 237 238 g.setColor(Color.red); 239 g.fillPolygon(arrow); 240 g.setColor(Color.black); 241 g.drawPolygon(arrow); 242 } 243 } 244