001: /*
002: * Selection.java - Selected text
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2001, 2005 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.textarea;
024:
025: //{{{ Imports
026: import java.util.ArrayList;
027: import java.util.List;
028:
029: import org.gjt.sp.jedit.buffer.JEditBuffer;
030: import org.gjt.sp.util.StandardUtilities;
031:
032: //}}}
033:
034: /**
035: * An abstract class that holds data on a region of selected text.
036: * As an abstract class, it cannot be used
037: * directly, but instead serves as a parent class for two specific types
038: * of selection structures:
039: * <ul>
040: * <li>{@link Selection.Range} - represents an ordinary range of selected text.</li>
041: * <li>{@link Selection.Rect} - represents a rectangular selection.</li>
042: * </ul>
043: *
044: * @author Slava Pestov
045: * @author John Gellene (API documentation)
046: * @version $Id: Selection.java 10797 2007-10-04 08:59:24Z kpouer $
047: * @since jEdit 3.2pre1
048: */
049: public abstract class Selection implements Cloneable {
050: //{{{ getStart() method
051: /**
052: * Returns the start offset of this selection.
053: */
054: public int getStart() {
055: return start;
056: } //}}}
057:
058: //{{{ getEnd() method
059: /**
060: * Returns the end offset of this selection.
061: */
062: public int getEnd() {
063: return end;
064: } //}}}
065:
066: //{{{ getStart() method
067: /**
068: * Returns the beginning of the portion of the selection
069: * falling on the specified line. Used to manipulate
070: * selection text on a line-by-line basis.
071: * @param buffer The buffer
072: * @param line The line number
073: * @since jEdit 4.1pre1
074: */
075: public abstract int getStart(JEditBuffer buffer, int line);
076:
077: //}}}
078:
079: //{{{ getEnd() method
080: /**
081: * Returns the end of the portion of the selection
082: * falling on the specified line. Used to manipulate
083: * selection text on a line-by-line basis.
084: * @param buffer The buffer
085: * @param line The line number
086: * @since jEdit 4.1pre1
087: */
088: public abstract int getEnd(JEditBuffer buffer, int line);
089:
090: //}}}
091:
092: //{{{ getStartLine() method
093: /**
094: * Returns the starting line number of this selection.
095: */
096: public int getStartLine() {
097: return startLine;
098: } //}}}
099:
100: //{{{ getEndLine() method
101: /**
102: * Returns the ending line number of this selection.
103: */
104: public int getEndLine() {
105: return endLine;
106: } //}}}
107:
108: //{{{ overlaps() method
109: /**
110: * Returns if this selection and the specified selection overlap.
111: * @param s The other selection
112: * @since jEdit 4.1pre1
113: */
114: public boolean overlaps(Selection s) {
115: if ((start >= s.start && start <= s.end)
116: || (end >= s.start && end <= s.end))
117: return true;
118: else
119: return false;
120: } //}}}
121:
122: //{{{ toString() method
123: public String toString() {
124: return getClass().getName() + "[start=" + start + ",end=" + end
125: + ",startLine=" + startLine + ",endLine=" + endLine
126: + ']';
127: } //}}}
128:
129: //{{{ clone() method
130: public Object clone() {
131: try {
132: return super .clone();
133: } catch (CloneNotSupportedException e) {
134: throw new InternalError("I just drank a whole "
135: + "bottle of cough syrup and I feel " + "funny!");
136: }
137: } //}}}
138:
139: //{{{ Package-private members
140: int start, end;
141: int startLine, endLine;
142:
143: //{{{ Selection constructor
144: protected Selection() {
145: } //}}}
146:
147: //{{{ Selection constructor
148: protected Selection(Selection sel) {
149: this .start = sel.start;
150: this .end = sel.end;
151: } //}}}
152:
153: //{{{ Selection constructor
154: protected Selection(int start, int end) {
155: this .start = start;
156: this .end = end;
157: } //}}}
158:
159: // should the next two be public, maybe?
160: abstract void getText(JEditBuffer buffer, StringBuffer buf);
161:
162: /**
163: * Replace the selection with the given text
164: * @param buffer the buffer
165: * @param text the text
166: * @return the offset at the end of the inserted text
167: */
168: abstract int setText(JEditBuffer buffer, String text);
169:
170: /**
171: * Called when text is inserted into the buffer.
172: * @param buffer The buffer in question
173: * @param startLine The first line
174: * @param start The start offset, from the beginning of the buffer
175: * @param numLines The number of lines inserted
176: * @param length The number of characters inserted
177: * @return <code>true</code> if the selection was changed
178: */
179: abstract boolean contentInserted(JEditBuffer buffer, int startLine,
180: int start, int numLines, int length);
181:
182: /**
183: * Called when text is removed from the buffer.
184: * @param buffer The buffer in question
185: * @param startLine The first line
186: * @param start The start offset, from the beginning of the buffer
187: * @param numLines The number of lines removed
188: * @param length The number of characters removed
189: * @return <code>true</code> if the selection was changed
190: */
191: abstract boolean contentRemoved(JEditBuffer buffer, int startLine,
192: int start, int numLines, int length);
193:
194: //}}}
195:
196: //{{{ Range class
197: /**
198: * An ordinary range selection.
199: * @since jEdit 3.2pre1
200: */
201: public static class Range extends Selection {
202: //{{{ Range constructor
203: public Range() {
204: } //}}}
205:
206: //{{{ Range constructor
207: public Range(Selection sel) {
208: super (sel);
209: } //}}}
210:
211: //{{{ Range constructor
212: public Range(int start, int end) {
213: super (start, end);
214: } //}}}
215:
216: //{{{ getStart() method
217: public int getStart(JEditBuffer buffer, int line) {
218: if (line == startLine)
219: return start;
220: else
221: return buffer.getLineStartOffset(line);
222: } //}}}
223:
224: //{{{ getEnd() method
225: public int getEnd(JEditBuffer buffer, int line) {
226: if (line == endLine)
227: return end;
228: else
229: return buffer.getLineEndOffset(line) - 1;
230: } //}}}
231:
232: //{{{ Package-private members
233:
234: //{{{ getText() method
235: void getText(JEditBuffer buffer, StringBuffer buf) {
236: buf.append(buffer.getText(start, end - start));
237: } //}}}
238:
239: //{{{ setText() method
240: /**
241: * Replace the selection with the given text
242: * @param buffer the buffer
243: * @param text the text
244: * @return the offset at the end of the inserted text
245: */
246: int setText(JEditBuffer buffer, String text) {
247: buffer.remove(start, end - start);
248: if (text != null && text.length() != 0) {
249: buffer.insert(start, text);
250: return start + text.length();
251: } else
252: return start;
253: } //}}}
254:
255: //{{{ contentInserted() method
256: boolean contentInserted(JEditBuffer buffer, int startLine,
257: int start, int numLines, int length) {
258: boolean changed = false;
259:
260: if (this .start >= start) {
261: this .start += length;
262: if (numLines != 0)
263: this .startLine = buffer.getLineOfOffset(this .start);
264: changed = true;
265: }
266:
267: if (this .end >= start) {
268: this .end += length;
269: if (numLines != 0)
270: this .endLine = buffer.getLineOfOffset(this .end);
271: changed = true;
272: }
273:
274: return changed;
275: } //}}}
276:
277: //{{{ contentRemoved() method
278: boolean contentRemoved(JEditBuffer buffer, int startLine,
279: int start, int numLines, int length) {
280: int end = start + length;
281: boolean changed = false;
282:
283: if (this .start > start && this .start <= end) {
284: this .start = start;
285: changed = true;
286: } else if (this .start > end) {
287: this .start -= length;
288: changed = true;
289: }
290:
291: if (this .end > start && this .end <= end) {
292: this .end = start;
293: changed = true;
294: } else if (this .end > end) {
295: this .end -= length;
296: changed = true;
297: }
298:
299: if (changed && numLines != 0) {
300: this .startLine = buffer.getLineOfOffset(this .start);
301: this .endLine = buffer.getLineOfOffset(this .end);
302: }
303:
304: return changed;
305: } //}}}
306:
307: //}}}
308: } //}}}
309:
310: //{{{ Rect class
311: /**
312: * A rectangular selection.
313: * @since jEdit 3.2pre1
314: */
315: // this class is not very fast...
316: public static class Rect extends Selection {
317: //{{{ Rect constructor
318: public Rect() {
319: super ();
320: } //}}}
321:
322: //{{{ Rect constructor
323: public Rect(Selection sel) {
324: super (sel);
325: } //}}}
326:
327: //{{{ Rect constructor
328: public Rect(int start, int end) {
329: super (start, end);
330: } //}}}
331:
332: //{{{ Rect constructor
333: public Rect(int startLine, int start, int endLine, int end) {
334: this .startLine = startLine;
335: this .start = start;
336: this .endLine = endLine;
337: this .end = end;
338: } //}}}
339:
340: //{{{ Rect constructor
341: public Rect(JEditBuffer buffer, int startLine, int startColumn,
342: int endLine, int endColumn) {
343: this .startLine = startLine;
344: this .endLine = endLine;
345:
346: int[] width = new int[1];
347: int startOffset = buffer.getOffsetOfVirtualColumn(
348: startLine, startColumn, width);
349: if (startOffset == -1) {
350: extraStartVirt = startColumn - width[0];
351: //startOffset = buffer.getLineEndOffset(startLine) - 1;
352: }
353: /*else
354: startOffset += buffer.getLineStartOffset(startLine);*/
355:
356: int endOffset = buffer.getOffsetOfVirtualColumn(endLine,
357: endColumn, width);
358: if (endOffset == -1) {
359: extraEndVirt = endColumn - width[0];
360: //endOffset = buffer.getLineEndOffset(endLine) - 1;
361: }
362: /*else
363: endOffset += buffer.getLineStartOffset(endLine);*/
364: } //}}}
365:
366: //{{{ getStartColumn() method
367: public int getStartColumn(JEditBuffer buffer) {
368: int virtColStart = buffer.getVirtualWidth(startLine, start
369: - buffer.getLineStartOffset(startLine))
370: + extraStartVirt;
371: int virtColEnd = buffer.getVirtualWidth(endLine, end
372: - buffer.getLineStartOffset(endLine))
373: + extraEndVirt;
374: return Math.min(virtColStart, virtColEnd);
375: } //}}}
376:
377: //{{{ getEndColumn() method
378: public int getEndColumn(JEditBuffer buffer) {
379: int virtColStart = buffer.getVirtualWidth(startLine, start
380: - buffer.getLineStartOffset(startLine))
381: + extraStartVirt;
382: int virtColEnd = buffer.getVirtualWidth(endLine, end
383: - buffer.getLineStartOffset(endLine))
384: + extraEndVirt;
385: return Math.max(virtColStart, virtColEnd);
386: } //}}}
387:
388: //{{{ getStart() method
389: public int getStart(JEditBuffer buffer, int line) {
390: return getColumnOnOtherLine(buffer, line,
391: getStartColumn(buffer));
392: } //}}}
393:
394: //{{{ getEnd() method
395: public int getEnd(JEditBuffer buffer, int line) {
396: return getColumnOnOtherLine(buffer, line,
397: getEndColumn(buffer));
398: } //}}}
399:
400: //{{{ Package-private members
401: int extraStartVirt;
402: int extraEndVirt;
403:
404: //{{{ getText() method
405: void getText(JEditBuffer buffer, StringBuffer buf) {
406: int start = getStartColumn(buffer);
407: int end = getEndColumn(buffer);
408:
409: for (int i = startLine; i <= endLine; i++) {
410: int lineStart = buffer.getLineStartOffset(i);
411: int lineLen = buffer.getLineLength(i);
412:
413: int rectStart = buffer.getOffsetOfVirtualColumn(i,
414: start, null);
415: if (rectStart == -1)
416: rectStart = lineLen;
417:
418: int rectEnd = buffer.getOffsetOfVirtualColumn(i, end,
419: null);
420: if (rectEnd == -1)
421: rectEnd = lineLen;
422:
423: if (rectEnd < rectStart)
424: System.err.println(i + ":::" + start + ':' + end
425: + " ==> " + rectStart + ':' + rectEnd);
426: buf.append(buffer.getText(lineStart + rectStart,
427: rectEnd - rectStart));
428:
429: if (i != endLine)
430: buf.append('\n');
431: }
432: } //}}}
433:
434: //{{{ setText() method
435: /**
436: * Replace the selection with the given text
437: * @param buffer the buffer
438: * @param text the text
439: * @return the offset at the end of the inserted text
440: */
441: int setText(JEditBuffer buffer, String text) {
442: int startColumn = getStartColumn(buffer);
443: int endColumn = getEndColumn(buffer);
444:
445: int tabSize = buffer.getTabSize();
446:
447: int maxWidth = 0;
448: int totalLines = 0;
449: /** This list will contains Strings and Integer. */
450: List lines = new ArrayList();
451:
452: //{{{ Split the text into lines
453: if (text != null) {
454: int lastNewline = 0;
455: int currentWidth = startColumn;
456: for (int i = 0; i < text.length(); i++) {
457: char ch = text.charAt(i);
458: if (ch == '\n') {
459: totalLines++;
460: lines.add(text.substring(lastNewline, i));
461: lastNewline = i + 1;
462: maxWidth = Math.max(maxWidth, currentWidth);
463: lines.add(currentWidth);
464: currentWidth = startColumn;
465: } else if (ch == '\t')
466: currentWidth += tabSize
467: - (currentWidth % tabSize);
468: else
469: currentWidth++;
470: }
471:
472: if (lastNewline != text.length()) {
473: totalLines++;
474: lines.add(text.substring(lastNewline));
475: lines.add(currentWidth);
476: maxWidth = Math.max(maxWidth, currentWidth);
477: }
478: } //}}}
479:
480: //{{{ Insert the lines into the buffer
481: int endOffset = 0;
482: int[] total = new int[1];
483: int lastLine = Math
484: .max(startLine + totalLines - 1, endLine);
485: for (int i = startLine; i <= lastLine; i++) {
486: if (i == buffer.getLineCount())
487: buffer.insert(buffer.getLength(), "\n");
488:
489: int lineStart = buffer.getLineStartOffset(i);
490: int lineLen = buffer.getLineLength(i);
491:
492: int rectStart = buffer.getOffsetOfVirtualColumn(i,
493: startColumn, total);
494: int startWhitespace;
495: if (rectStart == -1) {
496: startWhitespace = startColumn - total[0];
497: rectStart = lineLen;
498: } else
499: startWhitespace = 0;
500:
501: int rectEnd = buffer.getOffsetOfVirtualColumn(i,
502: endColumn, null);
503: if (rectEnd == -1)
504: rectEnd = lineLen;
505:
506: buffer.remove(rectStart + lineStart, rectEnd
507: - rectStart);
508:
509: if (startWhitespace != 0) {
510: buffer.insert(rectStart + lineStart,
511: StandardUtilities.createWhiteSpace(
512: startWhitespace, 0));
513: }
514:
515: int endWhitespace;
516: if (totalLines == 0) {
517: if (rectEnd == lineLen)
518: endWhitespace = 0;
519: else
520: endWhitespace = maxWidth - startColumn;
521: } else {
522: int index = 2 * ((i - startLine) % totalLines);
523: String str = (String) lines.get(index);
524: buffer.insert(rectStart + lineStart
525: + startWhitespace, str);
526: if (rectEnd == lineLen)
527: endWhitespace = 0;
528: else {
529: endWhitespace = maxWidth
530: - (Integer) lines.get(index + 1);
531: }
532: startWhitespace += str.length();
533: }
534:
535: if (endWhitespace != 0) {
536: buffer.insert(rectStart + lineStart
537: + startWhitespace, StandardUtilities
538: .createWhiteSpace(endWhitespace, 0));
539: }
540:
541: endOffset = rectStart + lineStart + startWhitespace
542: + endWhitespace;
543: } //}}}
544:
545: //{{{ Move the caret down a line
546: if (text == null || text.length() == 0)
547: return end;
548: else
549: return endOffset;
550: //}}}
551: } //}}}
552:
553: //{{{ contentInserted() method
554: boolean contentInserted(JEditBuffer buffer, int startLine,
555: int start, int numLines, int length) {
556: if (this .end < start)
557: return false;
558:
559: this .end += length;
560:
561: if (this .startLine > startLine) {
562: this .start += length;
563: if (numLines != 0) {
564: this .startLine = buffer.getLineOfOffset(this .start);
565: this .endLine = buffer.getLineOfOffset(this .end);
566: }
567: return true;
568: }
569:
570: int endVirtualColumn = buffer.getVirtualWidth(this .endLine,
571: end - buffer.getLineStartOffset(this .endLine));
572:
573: if (this .start == start) {
574: int startVirtualColumn = buffer
575: .getVirtualWidth(
576: this .startLine,
577: start
578: - buffer
579: .getLineStartOffset(this .startLine));
580:
581: this .start += length;
582:
583: int newStartVirtualColumn = buffer
584: .getVirtualWidth(
585: startLine,
586: start
587: - buffer
588: .getLineStartOffset(this .startLine));
589:
590: int[] totalVirtualWidth = new int[1];
591: int newEnd = buffer
592: .getOffsetOfVirtualColumn(this .endLine,
593: endVirtualColumn
594: + newStartVirtualColumn
595: - startVirtualColumn,
596: totalVirtualWidth);
597:
598: if (newEnd != -1) {
599: end = buffer.getLineStartOffset(this .endLine)
600: + newEnd;
601: } else {
602: end = buffer.getLineEndOffset(this .endLine) - 1;
603: extraEndVirt = totalVirtualWidth[0]
604: - endVirtualColumn;
605: }
606: } else if (this .start > start) {
607: this .start += length;
608: if (numLines != 0) {
609: this .startLine = buffer.getLineOfOffset(this .start);
610: }
611: }
612:
613: if (numLines != 0)
614: this .endLine = buffer.getLineOfOffset(this .end);
615: int newEndVirtualColumn = buffer.getVirtualWidth(endLine,
616: end - buffer.getLineStartOffset(this .endLine));
617: if (startLine == this .endLine && extraEndVirt != 0) {
618: extraEndVirt += endVirtualColumn - newEndVirtualColumn;
619: } else if (startLine == this .startLine
620: && extraStartVirt != 0) {
621: extraStartVirt += endVirtualColumn
622: - newEndVirtualColumn;
623: }
624:
625: return true;
626: } //}}}
627:
628: //{{{ contentRemoved() method
629: boolean contentRemoved(JEditBuffer buffer, int startLine,
630: int start, int numLines, int length) {
631: int end = start + length;
632: boolean changed = false;
633:
634: if (this .start > start && this .start <= end) {
635: this .start = start;
636: changed = true;
637: } else if (this .start > end) {
638: this .start -= length;
639: changed = true;
640: }
641:
642: if (this .end > start && this .end <= end) {
643: this .end = start;
644: changed = true;
645: } else if (this .end > end) {
646: this .end -= length;
647: changed = true;
648: }
649:
650: if (changed && numLines != 0) {
651: this .startLine = buffer.getLineOfOffset(this .start);
652: this .endLine = buffer.getLineOfOffset(this .end);
653: }
654:
655: return changed;
656: } //}}}
657:
658: //}}}
659:
660: //{{{ Private members
661:
662: //{{{ getColumnOnOtherLine() method
663: private static int getColumnOnOtherLine(JEditBuffer buffer,
664: int line, int col) {
665: int returnValue = buffer.getOffsetOfVirtualColumn(line,
666: col, null);
667: if (returnValue == -1)
668: return buffer.getLineEndOffset(line) - 1;
669: else
670: return buffer.getLineStartOffset(line) + returnValue;
671: } //}}}
672:
673: //}}}
674: } //}}}
675: }
|