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