PageRenderTime 177ms CodeModel.GetById 144ms app.highlight 21ms RepoModel.GetById 9ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/textarea/Selection.java

#
Java | 765 lines | 499 code | 91 blank | 175 comment | 108 complexity | c7bbe8b6fa981200b67f0d8194d37613 MD5 | raw file
  1/*
  2 * Selection.java - Selected text
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2001, 2005 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit.textarea;
 24
 25//{{{ Imports
 26import java.util.ArrayList;
 27import java.util.List;
 28
 29import org.gjt.sp.jedit.buffer.JEditBuffer;
 30import org.gjt.sp.util.StandardUtilities;
 31//}}}
 32
 33/**
 34 * An abstract class that holds data on a region of selected text.
 35 * As an abstract class, it cannot be used
 36 * directly, but instead serves as a parent class for two specific types
 37 * of selection structures:
 38 * <ul>
 39 * <li>{@link Selection.Range} - represents an ordinary range of selected text.</li>
 40 * <li>{@link Selection.Rect} - represents a rectangular selection.</li>
 41 * </ul>
 42 *
 43 * @author Slava Pestov
 44 * @author John Gellene (API documentation)
 45 * @version $Id: Selection.java 15570 2009-06-25 00:43:57Z ezust $
 46 * @since jEdit 3.2pre1
 47 */
 48public abstract class Selection implements Cloneable
 49{
 50	//{{{ getStart() method
 51	/**
 52	 * Returns the start offset of this selection.
 53	 */
 54	public int getStart()
 55	{
 56		return start;
 57	} //}}}
 58
 59	//{{{ getEnd() method
 60	/**
 61	 * Returns the end offset of this selection.
 62	 */
 63	public int getEnd()
 64	{
 65		return end;
 66	} //}}}
 67
 68	//{{{ getStart() method
 69	/**
 70	 * Returns the beginning of the portion of the selection
 71	 * falling on the specified line. Used to manipulate
 72	 * selection text on a line-by-line basis.
 73	 * @param buffer The buffer
 74	 * @param line The line number
 75	 * @since jEdit 4.1pre1
 76	 */
 77	public abstract int getStart(JEditBuffer buffer, int line);
 78	//}}}
 79
 80	//{{{ getEnd() method
 81	/**
 82	 * Returns the end of the portion of the selection
 83	 * falling on the specified line. Used to manipulate
 84	 * selection text on a line-by-line basis.
 85	 * @param buffer The buffer
 86	 * @param line The line number
 87	 * @since jEdit 4.1pre1
 88	 */
 89	public abstract int getEnd(JEditBuffer buffer, int line);
 90	//}}}
 91
 92	//{{{ getStartLine() method
 93	/**
 94	 * Returns the starting line number of this selection.
 95	 */
 96	public int getStartLine()
 97	{
 98		return startLine;
 99	} //}}}
100
101	//{{{ getEndLine() method
102	/**
103	 * Returns the ending line number of this selection.
104	 */
105	public int getEndLine()
106	{
107		return endLine;
108	} //}}}
109
110	//{{{ overlaps() method
111	/**
112	 * Returns if this selection and the specified selection overlap.
113	 * @param s The other selection
114	 * @since jEdit 4.1pre1
115	 */
116	public boolean overlaps(Selection s)
117	{
118		if((start >= s.start && start <= s.end)
119			|| (end >= s.start && end <= s.end))
120			return true;
121		else
122			return false;
123	} //}}}
124
125	//{{{ toString() method
126	@Override
127	public String toString()
128	{
129		return getClass().getName() + "[start=" + start
130			+ ",end=" + end + ",startLine=" + startLine
131			+ ",endLine=" + endLine + ']';
132	} //}}}
133
134	//{{{ clone() method
135	@Override
136	public Object clone()
137	{
138		try
139		{
140			return super.clone();
141		}
142		catch(CloneNotSupportedException e)
143		{
144			throw new InternalError("I just drank a whole "
145				+ "bottle of cough syrup and I feel "
146				+ "funny!");
147		}
148	} //}}}
149
150	//{{{ Package-private members
151	int start, end;
152	int startLine, endLine;
153
154	//{{{ Selection constructor
155	protected Selection()
156	{
157	} //}}}
158
159	//{{{ Selection constructor
160	protected Selection(Selection sel)
161	{
162		this.start = sel.start;
163		this.end = sel.end;
164	} //}}}
165
166	//{{{ Selection constructor
167	protected Selection(int start, int end)
168	{
169		this.start = start;
170		this.end = end;
171	} //}}}
172
173	// should the next two be public, maybe?
174	abstract void getText(JEditBuffer buffer, StringBuilder buf);
175
176	/**
177	 * Replace the selection with the given text
178	 * @param buffer the buffer
179	 * @param text the text
180	 * @return the offset at the end of the inserted text
181	 */
182	abstract int setText(JEditBuffer buffer, String text);
183
184	/**
185	 * Called when text is inserted into the buffer.
186	 * @param buffer The buffer in question
187	 * @param startLine The first line
188	 * @param start The start offset, from the beginning of the buffer
189	 * @param numLines The number of lines inserted
190	 * @param length The number of characters inserted
191	 * @return <code>true</code> if the selection was changed
192	 */
193	abstract boolean contentInserted(JEditBuffer buffer, int startLine, int start,
194		int numLines, int length);
195
196	/**
197	 * Called when text is removed from the buffer.
198	 * @param buffer The buffer in question
199	 * @param startLine The first line
200	 * @param start The start offset, from the beginning of the buffer
201	 * @param numLines The number of lines removed
202	 * @param length The number of characters removed
203	 * @return <code>true</code> if the selection was changed
204	 */
205	abstract boolean contentRemoved(JEditBuffer buffer, int startLine, int start,
206		int numLines, int length);
207	//}}}
208
209	//{{{ Range class
210	/**
211	 * An ordinary range selection.
212	 * @since jEdit 3.2pre1
213	 */
214	public static class Range extends Selection
215	{
216		//{{{ Range constructor
217		public Range()
218		{
219		} //}}}
220
221		//{{{ Range constructor
222		public Range(Selection sel)
223		{
224			super(sel);
225		} //}}}
226
227		//{{{ Range constructor
228		public Range(int start, int end)
229		{
230			super(start,end);
231		} //}}}
232
233		//{{{ getStart() method
234		@Override
235		public int getStart(JEditBuffer buffer, int line)
236		{
237			if(line == startLine)
238				return start;
239			else
240				return buffer.getLineStartOffset(line);
241		} //}}}
242
243		//{{{ getEnd() method
244		@Override
245		public int getEnd(JEditBuffer buffer, int line)
246		{
247			if(line == endLine)
248				return end;
249			else
250				return buffer.getLineEndOffset(line) - 1;
251		} //}}}
252
253		//{{{ Package-private members
254
255		//{{{ getText() method
256		@Override
257		void getText(JEditBuffer buffer, StringBuilder buf)
258		{
259			buf.append(buffer.getText(start,end - start));
260		} //}}}
261
262		//{{{ setText() method
263		/**
264		 * Replace the selection with the given text
265		 * @param buffer the buffer
266		 * @param text the text
267		 * @return the offset at the end of the inserted text
268		 */
269		@Override
270		int setText(JEditBuffer buffer, String text)
271		{
272			buffer.remove(start,end - start);
273			if(text != null && text.length() != 0)
274			{
275				buffer.insert(start,text);
276				return start + text.length();
277			}
278			else
279				return start;
280		} //}}}
281
282		//{{{ contentInserted() method
283		@Override
284		boolean contentInserted(JEditBuffer buffer, int startLine, int start,
285			int numLines, int length)
286		{
287			boolean changed = false;
288
289			if(this.start >= start)
290			{
291				this.start += length;
292				if(numLines != 0)
293					this.startLine = buffer.getLineOfOffset(this.start);
294				changed = true;
295			}
296
297			if(this.end >= start)
298			{
299				this.end += length;
300				if(numLines != 0)
301					this.endLine = buffer.getLineOfOffset(this.end);
302				changed = true;
303			}
304
305			return changed;
306		} //}}}
307
308		//{{{ contentRemoved() method
309		@Override
310		boolean contentRemoved(JEditBuffer buffer, int startLine, int start,
311			int numLines, int length)
312		{
313			int end = start + length;
314			boolean changed = false;
315
316			if(this.start > start && this.start <= end)
317			{
318				this.start = start;
319				changed = true;
320			}
321			else if(this.start > end)
322			{
323				this.start -= length;
324				changed = true;
325			}
326
327			if(this.end > start && this.end <= end)
328			{
329				this.end = start;
330				changed = true;
331			}
332			else if(this.end > end)
333			{
334				this.end -= length;
335				changed = true;
336			}
337
338			if(changed && numLines != 0)
339			{
340				this.startLine = buffer.getLineOfOffset(this.start);
341				this.endLine = buffer.getLineOfOffset(this.end);
342			}
343
344			return changed;
345		} //}}}
346
347		//}}}
348	} //}}}
349
350	//{{{ Rect class
351	/**
352	 * A rectangular selection.
353	 * @since jEdit 3.2pre1
354	 */
355	// this class is not very fast...
356	public static class Rect extends Selection
357	{
358		//{{{ Rect constructor
359		public Rect()
360		{
361		} //}}}
362
363		//{{{ Rect constructor
364		public Rect(Selection sel)
365		{
366			super(sel);
367		} //}}}
368
369		//{{{ Rect constructor
370		public Rect(int start, int end)
371		{
372			super(start,end);
373		} //}}}
374
375		//{{{ Rect constructor
376		public Rect(int startLine, int start, int endLine, int end)
377		{
378			this.startLine = startLine;
379			this.start = start;
380			this.endLine = endLine;
381			this.end = end;
382		} //}}}
383
384		//{{{ Rect constructor
385		public Rect(JEditBuffer buffer, int startLine, int startColumn,
386			int endLine, int endColumn)
387		{
388			this.startLine = startLine;
389			this.endLine = endLine;
390
391			int[] width = new int[1];
392			int startOffset = buffer.getOffsetOfVirtualColumn(startLine,
393				startColumn,width);
394			if(startOffset == -1)
395			{
396				extraStartVirt = startColumn - width[0];
397				//startOffset = buffer.getLineEndOffset(startLine) - 1;
398			}
399			/*else
400				startOffset += buffer.getLineStartOffset(startLine);*/
401
402			int endOffset = buffer.getOffsetOfVirtualColumn(endLine,
403				endColumn,width);
404			if(endOffset == -1)
405			{
406				extraEndVirt = endColumn - width[0];
407				//endOffset = buffer.getLineEndOffset(endLine) - 1;
408			}
409			/*else
410				endOffset += buffer.getLineStartOffset(endLine);*/
411		} //}}}
412
413		//{{{ getStartColumn() method
414		public int getStartColumn(JEditBuffer buffer)
415		{
416			int virtColStart = buffer.getVirtualWidth(startLine,
417				start - buffer.getLineStartOffset(startLine)) + extraStartVirt;
418			int virtColEnd = buffer.getVirtualWidth(endLine,
419				end - buffer.getLineStartOffset(endLine)) + extraEndVirt;
420			return Math.min(virtColStart,virtColEnd);
421		} //}}}
422
423		//{{{ getEndColumn() method
424		public int getEndColumn(JEditBuffer buffer)
425		{
426			int virtColStart = buffer.getVirtualWidth(startLine,
427				start - buffer.getLineStartOffset(startLine)) + extraStartVirt;
428			int virtColEnd = buffer.getVirtualWidth(endLine,
429				end - buffer.getLineStartOffset(endLine)) + extraEndVirt;
430			return Math.max(virtColStart,virtColEnd);
431		} //}}}
432
433		//{{{ getStart() method
434		@Override
435		public int getStart(JEditBuffer buffer, int line)
436		{
437			return getColumnOnOtherLine(buffer,line,
438				getStartColumn(buffer));
439		} //}}}
440
441		//{{{ getEnd() method
442		@Override
443		public int getEnd(JEditBuffer buffer, int line)
444		{
445			return getColumnOnOtherLine(buffer,line,
446				getEndColumn(buffer));
447		} //}}}
448
449		//{{{ Package-private members
450		int extraStartVirt;
451		int extraEndVirt;
452
453		//{{{ getText() method
454		@Override
455		void getText(JEditBuffer buffer, StringBuilder buf)
456		{
457			int start = getStartColumn(buffer);
458			int end = getEndColumn(buffer);
459
460			for(int i = startLine; i <= endLine; i++)
461			{
462				int lineStart = buffer.getLineStartOffset(i);
463				int lineLen = buffer.getLineLength(i);
464
465				int rectStart = buffer.getOffsetOfVirtualColumn(
466					i,start,null);
467				if(rectStart == -1)
468					rectStart = lineLen;
469
470				int rectEnd = buffer.getOffsetOfVirtualColumn(
471					i,end,null);
472				if(rectEnd == -1)
473					rectEnd = lineLen;
474
475				if(rectEnd < rectStart)
476					System.err.println(i + ":::" + start + ':' + end
477						+ " ==> " + rectStart + ':' + rectEnd);
478				buf.append(buffer.getText(lineStart + rectStart,
479					rectEnd - rectStart));
480
481				if(i != endLine)
482					buf.append('\n');
483			}
484		} //}}}
485
486		//{{{ setText() method
487		/**
488		 * Replace the selection with the given text
489		 * @param buffer the buffer
490		 * @param text the text
491		 * @return the offset at the end of the inserted text
492		 */
493		@Override
494		int setText(JEditBuffer buffer, String text)
495		{
496			int startColumn = getStartColumn(buffer);
497			int endColumn = getEndColumn(buffer);
498
499			int tabSize = buffer.getTabSize();
500
501			int maxWidth = 0;
502			int totalLines = 0;
503			/** This list will contains Strings and Integer. */
504			List<Object> lines = new ArrayList<Object>();
505
506			//{{{ Split the text into lines
507			if(text != null)
508			{
509				int lastNewline = 0;
510				int currentWidth = startColumn;
511				for(int i = 0; i < text.length(); i++)
512				{
513					char ch = text.charAt(i);
514					if(ch == '\n')
515					{
516						totalLines++;
517						lines.add(text.substring(
518							lastNewline,i));
519						lastNewline = i + 1;
520						maxWidth = Math.max(maxWidth,currentWidth);
521						lines.add(currentWidth);
522						currentWidth = startColumn;
523					}
524					else if(ch == '\t')
525						currentWidth += tabSize - (currentWidth % tabSize);
526					else
527						currentWidth++;
528				}
529
530				if(lastNewline != text.length())
531				{
532					totalLines++;
533					lines.add(text.substring(lastNewline));
534					lines.add(currentWidth);
535					maxWidth = Math.max(maxWidth,currentWidth);
536				}
537			} //}}}
538
539			//{{{ Insert the lines into the buffer
540			int endOffset = 0;
541			int[] total = new int[1];
542			int lastLine = Math.max(startLine + totalLines - 1,endLine);
543			for(int i = startLine; i <= lastLine; i++)
544			{
545				if(i == buffer.getLineCount())
546					buffer.insert(buffer.getLength(),"\n");
547
548				int lineStart = buffer.getLineStartOffset(i);
549				int lineLen = buffer.getLineLength(i);
550
551				int rectStart = buffer.getOffsetOfVirtualColumn(
552					i,startColumn,total);
553				int startWhitespace;
554				if(rectStart == -1)
555				{
556					startWhitespace = startColumn - total[0];
557					rectStart = lineLen;
558				}
559				else
560					startWhitespace = 0;
561
562				int rectEnd = buffer.getOffsetOfVirtualColumn(
563					i,endColumn,null);
564				if(rectEnd == -1)
565					rectEnd = lineLen;
566
567				buffer.remove(rectStart + lineStart,rectEnd - rectStart);
568
569				if(startWhitespace != 0)
570				{
571					buffer.insert(rectStart + lineStart,
572						StandardUtilities.createWhiteSpace(startWhitespace,0));
573				}
574
575				int endWhitespace;
576				if(totalLines == 0)
577				{
578					if(rectEnd == lineLen)
579						endWhitespace = 0;
580					else
581						endWhitespace = maxWidth - startColumn;
582				}
583				else
584				{
585					int index = 2 * ((i - startLine) % totalLines);
586					String str = (String)lines.get(index);
587					buffer.insert(rectStart + lineStart + startWhitespace,str);
588					if(rectEnd == lineLen)
589						endWhitespace = 0;
590					else
591					{
592						endWhitespace = maxWidth
593							- (Integer) lines.get(index + 1);
594					}
595					startWhitespace += str.length();
596				}
597
598				if(endWhitespace != 0)
599				{
600					buffer.insert(rectStart + lineStart
601						+ startWhitespace,
602						StandardUtilities.createWhiteSpace(endWhitespace,0));
603				}
604
605				endOffset = rectStart + lineStart
606					+ startWhitespace
607					+ endWhitespace;
608			} //}}}
609
610			//{{{ Move the caret down a line
611			if(text == null || text.length() == 0)
612				return end;
613			else
614				return endOffset;
615			//}}}
616		} //}}}
617
618		//{{{ contentInserted() method
619		@Override
620		boolean contentInserted(JEditBuffer buffer, int startLine, int start,
621			int numLines, int length)
622		{
623			if(this.end < start)
624				return false;
625
626			this.end += length;
627
628			if(this.startLine > startLine)
629			{
630				this.start += length;
631				if(numLines != 0)
632				{
633					this.startLine = buffer.getLineOfOffset(
634						this.start);
635					this.endLine = buffer.getLineOfOffset(
636						this.end);
637				}
638				return true;
639			}
640
641			int endVirtualColumn = buffer.getVirtualWidth(
642				this.endLine,end
643				- buffer.getLineStartOffset(this.endLine));
644
645			if(this.start == start)
646			{
647				int startVirtualColumn = buffer.getVirtualWidth(
648					this.startLine,start
649					- buffer.getLineStartOffset(
650					this.startLine));
651
652				this.start += length;
653
654				int newStartVirtualColumn
655					= buffer.getVirtualWidth(
656						startLine,start -
657						buffer.getLineStartOffset(
658						this.startLine));
659
660				int[] totalVirtualWidth = new int[1];
661				int newEnd = buffer.getOffsetOfVirtualColumn(
662					this.endLine,endVirtualColumn +
663					newStartVirtualColumn -
664					startVirtualColumn,
665					totalVirtualWidth);
666
667				if(newEnd != -1)
668				{
669					end = buffer.getLineStartOffset(
670						this.endLine) + newEnd;
671				}
672				else
673				{
674					end = buffer.getLineEndOffset(
675						this.endLine) - 1;
676					extraEndVirt = totalVirtualWidth[0]
677						- endVirtualColumn;
678				}
679			}
680			else if(this.start > start)
681			{
682				this.start += length;
683				if(numLines != 0)
684				{
685					this.startLine = buffer.getLineOfOffset(
686						this.start);
687				}
688			}
689
690			if(numLines != 0)
691				this.endLine = buffer.getLineOfOffset(this.end);
692			int newEndVirtualColumn = buffer.getVirtualWidth(
693				endLine,
694				end - buffer.getLineStartOffset(this.endLine));
695			if(startLine == this.endLine && extraEndVirt != 0)
696			{
697				extraEndVirt += endVirtualColumn - newEndVirtualColumn;
698			}
699			else if(startLine == this.startLine
700				&& extraStartVirt != 0)
701			{
702				extraStartVirt += endVirtualColumn - newEndVirtualColumn;
703			}
704
705			return true;
706		} //}}}
707
708		//{{{ contentRemoved() method
709		@Override
710		boolean contentRemoved(JEditBuffer buffer, int startLine, int start,
711			int numLines, int length)
712		{
713			int end = start + length;
714			boolean changed = false;
715
716			if(this.start > start && this.start <= end)
717			{
718				this.start = start;
719				changed = true;
720			}
721			else if(this.start > end)
722			{
723				this.start -= length;
724				changed = true;
725			}
726
727			if(this.end > start && this.end <= end)
728			{
729				this.end = start;
730				changed = true;
731			}
732			else if(this.end > end)
733			{
734				this.end -= length;
735				changed = true;
736			}
737
738			if(changed && numLines != 0)
739			{
740				this.startLine = buffer.getLineOfOffset(this.start);
741				this.endLine = buffer.getLineOfOffset(this.end);
742			}
743
744			return changed;
745		} //}}}
746
747		//}}}
748
749		//{{{ Private members
750
751		//{{{ getColumnOnOtherLine() method
752		private static int getColumnOnOtherLine(JEditBuffer buffer, int line,
753			int col)
754		{
755			int returnValue = buffer.getOffsetOfVirtualColumn(
756				line,col,null);
757			if(returnValue == -1)
758				return buffer.getLineEndOffset(line) - 1;
759			else
760				return buffer.getLineStartOffset(line) + returnValue;
761		} //}}}
762
763		//}}}
764	} //}}}
765}