PageRenderTime 111ms CodeModel.GetById 78ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/textarea/SelectionManager.java

#
Java | 440 lines | 285 code | 53 blank | 102 comment | 63 complexity | 99a324127b87190fa7142f99ef37dabb MD5 | raw file
  1/*
  2 * SelectionManager.java
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2004 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.Iterator;
 28import java.util.List;
 29import java.util.Set;
 30import java.util.TreeSet;
 31import org.gjt.sp.jedit.buffer.*;
 32//}}}
 33
 34class SelectionManager
 35{
 36	// this is package-private so that the painter can use it without
 37	// having to call getSelection() (which involves an array copy)
 38	List selection;
 39
 40	//{{{ SelectionManager constructor
 41	SelectionManager(JEditTextArea textArea)
 42	{
 43		this.textArea = textArea;
 44		selection = new ArrayList();
 45	} //}}}
 46
 47	//{{{ getSelectionCount() method
 48	/**
 49	 * Returns the number of selections. This can be used to test
 50	 * for the existence of selections.
 51	 */
 52	int getSelectionCount()
 53	{
 54		return selection.size();
 55	} //}}}
 56
 57	//{{{ getSelection() method
 58	/**
 59	 * Returns the current selection.
 60	 * @since jEdit 3.2pre1
 61	 */
 62	public Selection[] getSelection()
 63	{
 64		return (Selection[])selection.toArray(
 65			new Selection[selection.size()]);
 66	} //}}}
 67
 68	//{{{ setSelection() method
 69	/**
 70	 * Sets the selection. Nested and overlapping selections are merged
 71	 * where possible.
 72	 */
 73	void setSelection(Selection[] selection)
 74	{
 75		this.selection.clear();
 76		addToSelection(selection);
 77	} //}}}
 78
 79	//{{{ addToSelection() method
 80	/**
 81	 * Adds to the selection. Nested and overlapping selections are merged
 82	 * where possible. Null elements of the array are ignored.
 83	 * @param selection The new selection
 84	 * since jEdit 3.2pre1
 85	 */
 86	void addToSelection(Selection[] selection)
 87	{
 88		if(selection != null)
 89		{
 90			for(int i = 0; i < selection.length; i++)
 91			{
 92				Selection s = selection[i];
 93				if(s != null)
 94					addToSelection(s);
 95			}
 96		}
 97	} //}}}
 98
 99	//{{{ addToSelection() method
100	void addToSelection(Selection addMe)
101	{
102		if(addMe.start > addMe.end)
103		{
104			throw new IllegalArgumentException(addMe.start
105				+ " > " + addMe.end);
106		}
107		else if(addMe.start == addMe.end)
108		{
109			if(addMe instanceof Selection.Range)
110				return;
111			else if(addMe instanceof Selection.Rect)
112			{
113				if(((Selection.Rect)addMe).extraEndVirt == 0)
114					return;
115			}
116		}
117
118		Iterator iter = selection.iterator();
119		while(iter.hasNext())
120		{
121			// try and merge existing selections one by
122			// one with the new selection
123			Selection s = (Selection)iter.next();
124			if(s.overlaps(addMe))
125			{
126				addMe.start = Math.min(s.start,addMe.start);
127				addMe.end = Math.max(s.end,addMe.end);
128				iter.remove();
129			}
130		}
131
132		addMe.startLine = textArea.getLineOfOffset(addMe.start);
133		addMe.endLine = textArea.getLineOfOffset(addMe.end);
134
135		boolean added = false;
136
137		for(int i = 0; i < selection.size(); i++)
138		{
139			Selection s = (Selection)selection.get(i);
140			if(addMe.start < s.start)
141			{
142				selection.add(i,addMe);
143				added = true;
144				break;
145			}
146		}
147
148		if(!added)
149			selection.add(addMe);
150
151		textArea.invalidateLineRange(addMe.startLine,addMe.endLine);
152	} //}}}
153
154	//{{{ setSelection() method
155	/**
156	 * Sets the selection. Nested and overlapping selections are merged
157	 * where possible.
158	 */
159	void setSelection(Selection selection)
160	{
161		this.selection.clear();
162
163		if(selection != null)
164			addToSelection(selection);
165	} //}}}
166
167	//{{{ getSelectionAtOffset() method
168	/**
169	 * Returns the selection containing the specific offset, or <code>null</code>
170	 * if there is no selection at that offset.
171	 * @param offset The offset
172	 * @since jEdit 3.2pre1
173	 */
174	Selection getSelectionAtOffset(int offset)
175	{
176		if(selection != null)
177		{
178			Iterator iter = selection.iterator();
179			while(iter.hasNext())
180			{
181				Selection s = (Selection)iter.next();
182				if(offset >= s.start && offset <= s.end)
183					return s;
184			}
185		}
186
187		return null;
188	} //}}}
189
190	//{{{ removeFromSelection() method
191	/**
192	 * Deactivates the specified selection.
193	 * @param sel The selection
194	 */
195	void removeFromSelection(Selection sel)
196	{
197		selection.remove(sel);
198	} //}}}
199
200	//{{{ resizeSelection() method
201	/**
202	 * Resizes the selection at the specified offset, or creates a new
203	 * one if there is no selection at the specified offset. This is a
204	 * utility method that is mainly useful in the mouse event handler
205	 * because it handles the case of end being before offset gracefully
206	 * (unlike the rest of the selection API).
207	 * @param offset The offset
208	 * @param end The new selection end
209	 * @param extraEndVirt Only for rectangular selections - specifies how
210	 * far it extends into virtual space.
211	 * @param rect Make the selection rectangular?
212	 */
213	void resizeSelection(int offset, int end, int extraEndVirt,
214		boolean rect)
215	{
216		boolean reversed = false;
217		if(end < offset)
218		{
219			int tmp = offset;
220			offset = end;
221			end = tmp;
222			reversed = true;
223		}
224
225		Selection newSel;
226		if(rect)
227		{
228			Selection.Rect rectSel = new Selection.Rect(offset,end);
229			if(reversed)
230				rectSel.extraStartVirt = extraEndVirt;
231			else
232				rectSel.extraEndVirt = extraEndVirt;
233			newSel = rectSel;
234		}
235		else
236			newSel = new Selection.Range(offset,end);
237
238		addToSelection(newSel);
239	} //}}}
240
241	//{{{ getSelectedLines() method
242	/**
243	 * Returns a sorted array of line numbers on which a selection or
244	 * selections are present.<p>
245	 *
246	 * This method is the most convenient way to iterate through selected
247	 * lines in a buffer. The line numbers in the array returned by this
248	 * method can be passed as a parameter to such methods as
249	 * {@link org.gjt.sp.jedit.Buffer#getLineText(int)}.
250	 */
251	int[] getSelectedLines()
252	{
253		Integer line;
254
255		Set set = new TreeSet();
256		Iterator iter = selection.iterator();
257		while(iter.hasNext())
258		{
259			Selection s = (Selection)iter.next();
260			int endLine =
261				(s.end == textArea.getLineStartOffset(s.endLine)
262				? s.endLine - 1
263				: s.endLine);
264
265			for(int j = s.startLine; j <= endLine; j++)
266			{
267				line = new Integer(j);
268				set.add(line);
269			}
270		}
271
272		int[] returnValue = new int[set.size()];
273		int i = 0;
274
275		iter = set.iterator();
276		while(iter.hasNext())
277		{
278			line = (Integer)iter.next();
279			returnValue[i++] = line.intValue();
280		}
281
282		return returnValue;
283	} //}}}
284	
285	//{{{ invertSelection() method
286	void invertSelection()
287	{
288		Selection[] newSelection = new Selection[selection.size() + 1];
289		int lastOffset = 0;
290		for(int i = 0; i < selection.size(); i++)
291		{
292			Selection s = (Selection)selection.get(i);
293			newSelection[i] = new Selection.Range(lastOffset,
294				s.getStart());
295			lastOffset = s.getEnd();
296		}
297		newSelection[selection.size()] = new Selection.Range(
298			lastOffset,textArea.getBufferLength());
299		setSelection(newSelection);
300	} //}}}
301
302	//{{{ getSelectionStartEndOnLine() method
303	/**
304	 * Returns the x co-ordinates of the selection start and end on the
305	 * given line. May return null.
306	 */
307	int[] getSelectionStartAndEnd(int screenLine, int physicalLine,
308		Selection s)
309	{
310		int start = textArea.getScreenLineStartOffset(screenLine);
311		int end = textArea.getScreenLineEndOffset(screenLine);
312
313		if(end <= s.start || start > s.end)
314			return null;
315
316		int selStartScreenLine;
317		if(textArea.displayManager.isLineVisible(s.startLine))
318			selStartScreenLine = textArea.getScreenLineOfOffset(s.start);
319		else
320			selStartScreenLine = -1;
321
322		int selEndScreenLine;
323		if(textArea.displayManager.isLineVisible(s.endLine))
324			selEndScreenLine = textArea.getScreenLineOfOffset(s.end);
325		else
326			selEndScreenLine = -1;
327
328		JEditBuffer buffer = textArea.getBuffer();
329
330		int lineStart = buffer.getLineStartOffset(physicalLine);
331		int x1, x2;
332
333		if(s instanceof Selection.Rect)
334		{
335			start -= lineStart;
336			end -= lineStart;
337
338			Selection.Rect rect = (Selection.Rect)s;
339			int _start = rect.getStartColumn(buffer);
340			int _end = rect.getEndColumn(buffer);
341
342			int lineLen = buffer.getLineLength(physicalLine);
343
344			int[] total = new int[1];
345
346			int rectStart = buffer.getOffsetOfVirtualColumn(
347				physicalLine,_start,total);
348			if(rectStart == -1)
349			{
350				x1 = (_start - total[0]) * textArea.charWidth;
351				rectStart = lineLen;
352			}
353			else
354				x1 = 0;
355
356			int rectEnd = buffer.getOffsetOfVirtualColumn(
357				physicalLine,_end,total);
358			if(rectEnd == -1)
359			{
360				x2 = (_end - total[0]) * textArea.charWidth;
361				rectEnd = lineLen;
362			}
363			else
364				x2 = 0;
365
366			if(end <= rectStart || start > rectEnd)
367				return null;
368
369			x1 = (rectStart < start ? 0
370				: x1 + textArea.offsetToXY(physicalLine,
371				rectStart).x);
372			x2 = (rectEnd > end ? textArea.getWidth()
373				: x2 + textArea.offsetToXY(physicalLine,
374				rectEnd).x);
375		}
376		else if(selStartScreenLine == selEndScreenLine
377			&& selStartScreenLine != -1)
378		{
379			x1 = textArea.offsetToXY(physicalLine,
380				s.start - lineStart).x;
381			x2 = textArea.offsetToXY(physicalLine,
382				s.end - lineStart).x;
383		}
384		else if(screenLine == selStartScreenLine)
385		{
386			x1 = textArea.offsetToXY(physicalLine,
387				s.start - lineStart).x;
388			x2 = textArea.getWidth();
389		}
390		else if(screenLine == selEndScreenLine)
391		{
392			x1 = 0;
393			x2 = textArea.offsetToXY(physicalLine,
394				s.end - lineStart).x;
395		}
396		else
397		{
398			x1 = 0;
399			x2 = textArea.getWidth();
400		}
401
402		if(x1 < 0)
403			x1 = 0;
404		if(x2 < 0)
405			x2 = 0;
406
407		if(x1 == x2)
408			x2++;
409
410		return new int[] { x1, x2 };
411	} //}}}
412
413	//{{{ insideSelection() method
414	/**
415	 * Returns if the given point is inside a selection.
416	 * Used by drag and drop code in MouseHandler below.
417	 */
418	boolean insideSelection(int x, int y)
419	{
420		int offset = textArea.xyToOffset(x,y);
421
422		Selection s = textArea.getSelectionAtOffset(offset);
423		if(s == null)
424			return false;
425
426		int screenLine = textArea.getScreenLineOfOffset(offset);
427		if(screenLine == -1)
428			return false;
429
430		int[] selectionStartAndEnd = getSelectionStartAndEnd(
431			screenLine,textArea.getLineOfOffset(offset),s);
432		if(selectionStartAndEnd == null)
433			return false;
434
435		return x >= selectionStartAndEnd[0]
436			&& x <= selectionStartAndEnd[1];
437	} //}}}
438
439	private JEditTextArea textArea;
440}