001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: *
041: * Contributor(s): Ivan Soleimanipour.
042: */
043:
044: /*
045: * "Buffer.java"
046: * Buffer.java 1.8 01/07/30
047: */
048:
049: package org.netbeans.lib.terminalemulator;
050:
051: import java.util.Vector;
052:
053: /**
054: * The Buffer used by Term is _not_ related to javax.swing.text.Document.
055: * <p>
056: * The Swing Document is Element based while terms is Line based.
057: * <br>
058: * The Swing Document uses offsets for coordinates, while term uses cartesian
059: * BCoords.
060: * <p>
061: */
062:
063: class Buffer {
064:
065: /*
066: * For some odd reason Vector.removeRange is protected, so
067: * we have to do this to gain access to it.
068: */
069: static class OurVector extends Vector {
070: public void removeRange(int fromIndex, int toIndex) {
071: super .removeRange(fromIndex, toIndex);
072: }
073: }
074:
075: private OurVector lines = new OurVector(); // buffer
076:
077: public int nlines; // number of lines in buffer
078: // How is this different from lines.length?
079:
080: private int visible_cols; // number of columns visible in view
081: private int extra_cols; // columns needed to support lines longer
082:
083: // than visible_cols. Only grows.
084:
085: public int visibleCols() {
086: return visible_cols;
087: }
088:
089: public int totalCols() {
090: return visible_cols + extra_cols;
091: }
092:
093: public Buffer(int visible_cols) {
094: this .visible_cols = visible_cols;
095: }
096:
097: public void setVisibleCols(int visible_cols) {
098: int delta = visible_cols - this .visible_cols;
099: this .visible_cols = visible_cols;
100: extra_cols -= delta;
101: if (extra_cols < 0)
102: extra_cols = 0;
103: }
104:
105: /*
106: * Keep track of the largest column # to help set the extent of
107: * the horizontal scrollbar.
108: */
109: public void noteColumn(int col) {
110: int new_extra = col - visible_cols;
111: if (new_extra > extra_cols) {
112: extra_cols = new_extra;
113: // LATER hrange_listener.adjustHRange(extra_cols);
114: }
115: }
116:
117: /* DEBUG
118: public static volatile boolean lock = false;
119:
120: private void ck_lock() {
121: if (lock) {
122: System.out.println("Buffer ck_lock fail"); // NOI18N
123: printStats();
124: Thread.dumpStack();
125: }
126: }
127: */
128:
129: Line lineAt(int brow) {
130: try {
131: return (Line) lines.elementAt(brow);
132: } catch (ArrayIndexOutOfBoundsException x) {
133: //XXX swallowing this exception caused issue 40129.
134: //I've put in a null-check on the return value in sel.paint()
135: //as a hotfix. Should find out why bad values are being passed
136: //here. Ivan?
137:
138: /* DEBUG
139: System.out.println("Buffer.lineAt(" +brow+ ") -> null\n");// NOI18N
140: Thread.dumpStack();
141: */
142: return null;
143: }
144: }
145:
146: Line bottom() {
147: return lineAt(nlines);
148: }
149:
150: public Line appendLine() {
151: // DEBUG ck_lock();
152: Line l = new Line();
153: lines.add(l);
154: nlines++;
155: return l;
156: }
157:
158: public Line addLineAt(int row) {
159: // DEBUG ck_lock();
160: Line l = new Line();
161: lines.add(row, l);
162: nlines++;
163: return l;
164: }
165:
166: /**
167: * Remove 'n' lines starting at 'row'.
168: * Return the number of characters deleted as a result.
169: */
170: public int removeLinesAt(int row, int n) {
171: // DEBUG ck_lock();
172: int nchars = 0;
173: for (int r = row; r < row + n; r++)
174: nchars += lineAt(r).length() + 1;
175:
176: lines.removeRange(row, row + n);
177: nlines -= n;
178:
179: return nchars;
180: }
181:
182: public void removeLineAt(int row) {
183: // DEBUG ck_lock();
184: lines.remove(row);
185: nlines--;
186: }
187:
188: public Line moveLineFromTo(int from, int to) {
189: // DEBUG ck_lock();
190: Line l = (Line) lines.remove(from);
191: lines.add(to, l);
192: return l;
193: }
194:
195: /**
196: * Visit the physical lines from 'begin', through 'end'.
197: * <p>
198: * If 'newlines' is set, the passed 'ecol' is set to the actual
199: * number of columns in the view to signify that the newline is included.
200: * This way of doing it helps with rendering of a whole-line selection.
201: * Also Line knows about this and will tack on a "\n" when Line.text()
202: * is asked for.
203: */
204: void visitLines(BCoord begin, BCoord end, boolean newlines,
205: LineVisitor visitor) {
206:
207: // In the general case a range is made up of three
208: // rectangles. The partial line at top, the partial line
209: // at the bottom and the middle range of fully selected lines.
210:
211: Line l;
212: if (begin.row == end.row) {
213: // range is on one line
214: l = lineAt(begin.row);
215: visitor.visit(l, begin.row, begin.col, end.col);
216:
217: } else {
218: boolean cont = false;
219:
220: // range spans multiple lines
221: l = lineAt(begin.row);
222: if (newlines && !l.isWrapped())
223: cont = visitor.visit(l, begin.row, begin.col,
224: totalCols());
225: else
226: cont = visitor.visit(l, begin.row, begin.col, l
227: .length() - 1);
228: if (!cont)
229: return;
230:
231: for (int r = begin.row + 1; r < end.row; r++) {
232: l = lineAt(r);
233: if (newlines && !l.isWrapped())
234: cont = visitor.visit(l, r, 0, totalCols());
235: else
236: cont = visitor.visit(l, r, 0, l.length() - 1);
237: if (!cont)
238: return;
239: }
240:
241: l = lineAt(end.row);
242: cont = visitor.visit(l, end.row, 0, end.col);
243: if (!cont)
244: return;
245: }
246: }
247:
248: /*
249: * Like visitLines() except in reverse.
250: * <p>
251: * Starts at 'end' and goes to 'begin'.
252: */
253: void reverseVisitLines(BCoord begin, BCoord end, boolean newlines,
254: LineVisitor visitor) {
255:
256: // very similar to visitLines
257:
258: Line l;
259: if (begin.row == end.row) {
260: // range is on one line
261: l = lineAt(begin.row);
262: visitor.visit(l, begin.row, begin.col, end.col);
263:
264: } else {
265: boolean cont = false;
266:
267: // range spans multiple lines
268: l = lineAt(end.row);
269: cont = visitor.visit(l, end.row, 0, end.col);
270: if (!cont)
271: return;
272:
273: for (int r = end.row - 1; r > begin.row; r--) {
274: l = lineAt(r);
275: if (newlines && !l.isWrapped())
276: cont = visitor.visit(l, r, 0, totalCols());
277: else
278: cont = visitor.visit(l, r, 0, l.length() - 1);
279: if (!cont)
280: return;
281: }
282:
283: l = lineAt(begin.row);
284: if (newlines && !l.isWrapped())
285: cont = visitor.visit(l, begin.row, begin.col,
286: totalCols());
287: else
288: cont = visitor.visit(l, begin.row, begin.col, l
289: .length() - 1);
290: if (!cont)
291: return;
292: }
293: }
294:
295: public BExtent find_word(WordDelineator word_delineator,
296: BCoord coord) {
297: /*
298: * Find the boundaries of a "word" at 'coord'.
299: */
300:
301: Line l = lineAt(coord.row);
302:
303: if (coord.col >= l.length())
304: return new BExtent(coord, coord);
305:
306: int lx = word_delineator.findLeft(l.stringBuffer(), coord.col);
307: int rx = word_delineator.findRight(l.stringBuffer(), coord.col);
308:
309: return new BExtent(new BCoord(coord.row, lx), new BCoord(
310: coord.row, rx));
311: }
312:
313: /**
314: * Back up the coordinate by one character and return new BCoord
315: * <p>
316: * Travels back over line boundaries
317: * <br>
318: * Returns null if 'c' is the first character of the buffer.
319: */
320:
321: public BCoord backup(BCoord c) {
322: if (c.col > 0)
323: return new BCoord(c.row, c.col - 1); // back one in line
324:
325: // Cursor is at beginning of line.
326: // Need to find the end of previous line, but it might empty,
327: // so we go one line back etc
328: for (int prevrow = c.row - 1; prevrow >= 0; prevrow--) {
329: Line l = lineAt(prevrow);
330: if (l.length() != 0)
331: return new BCoord(prevrow, l.length() - 1);
332: }
333:
334: // prevrow == -1, at beginning of file; nowhere to back to
335: return null;
336: }
337:
338: /*
339: * Advance the coordinate by one character and return a new coord.
340: * <p>
341: * Wraps around line boundaries.
342: * <br>
343: * Returns null if 'c' is at the last character of the buffer.
344: */
345: public BCoord advance(BCoord c) {
346: int row = c.row;
347: int col = c.col;
348:
349: col++;
350: Line l = lineAt(row);
351: if (col < l.length())
352: return new BCoord(row, col);
353:
354: // Need to wrap, but the next line might be empty ... so we
355: // keep going til either we find a non-empty line or the end
356: // of the buffer.
357: while (++row < nlines) {
358: l = lineAt(row);
359: if (l.length() != 0)
360: return new BCoord(row, 0);
361: }
362: return null;
363: }
364:
365: /*
366: * Print interesting statistics and facts about this Term
367: */
368: public void printStats() {
369: int nchars = 0;
370: int ncapacity = 0;
371: for (int lx = 0; lx < nlines; lx++) {
372: Line l = lineAt(lx);
373: ncapacity += l.capacity();
374: nchars += l.length();
375: }
376: System.out.println(" nlines " + nlines + // NOI18N
377: " nchars " + nchars + // NOI18N
378: " ncapacity " + ncapacity); // NOI18N
379: }
380: }
|