PageRenderTime 47ms CodeModel.GetById 6ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 542 lines | 406 code | 23 blank | 113 comment | 60 complexity | 6086ec87597607e900a0638e192b5d95 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. * jEdit - Programmer's Text Editor
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright Š 2010 jEdit contributors
  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. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. */
  21. package org.gjt.sp.jedit.textarea;
  22. import java.util.Vector;
  23. import javax.swing.text.Segment;
  24. import org.gjt.sp.jedit.buffer.BufferAdapter;
  25. import org.gjt.sp.jedit.buffer.JEditBuffer;
  26. import org.gjt.sp.jedit.textarea.TextArea;
  27. public class ElasticTabStopBufferListener extends BufferAdapter
  28. {
  29. TextArea textArea;
  30. private boolean handledInsertion = true;
  31. private boolean handledDeletion = true;
  32. private boolean singleTabDeleted = false;
  33. //{{{ ElasticTabStopBufferListener() method
  34. public ElasticTabStopBufferListener(TextArea textArea)
  35. {
  36. this.textArea = textArea;
  37. }//}}}
  38. //{{{ fullyUpdateColumnBlocks() method
  39. private void fullyUpdateColumnBlocks(JEditBuffer buffer)
  40. {
  41. buffer.indentUsingElasticTabstops();
  42. handledInsertion = true;
  43. handledDeletion = true;
  44. buffer.getColumnBlock().setDirtyStatus(false);
  45. textArea.chunkCache.invalidateChunksFromPhys(0);
  46. textArea.invalidateLineRange(0, buffer.getLineCount()-1);
  47. //textArea.getPainter().repaint();
  48. }//}}}
  49. //{{{ contentInserted() method
  50. public void contentInserted(JEditBuffer buffer, int startLine, int offset,
  51. int numLines, int length)
  52. {
  53. if(!buffer.getBooleanProperty("elasticTabstops"))
  54. {
  55. return;
  56. }
  57. String charDeleted;
  58. boolean isASimpleChar = false;
  59. boolean singleTabInserted = false;
  60. if((numLines==0)&(length==1))
  61. {
  62. isASimpleChar = true;
  63. charDeleted = buffer.getText(offset, length);
  64. if(charDeleted.equals("\t"))
  65. {
  66. singleTabInserted = true;
  67. }
  68. }
  69. ColumnBlock rootBlock = buffer.getColumnBlock();
  70. if(rootBlock==null)
  71. {
  72. return;
  73. }
  74. //System.out.println("BEFORE UPDATING COLUMN BLOCKS-----");
  75. //System.out.println(rootBlock);
  76. int indexofBlockAbove = -1;
  77. ColumnBlock block = rootBlock.searchChildren(startLine);
  78. ColumnBlock blockjustAbove = null;
  79. boolean liesWithinBlock = false;
  80. int startIndex =-1;
  81. if(block!=null)
  82. {
  83. startIndex = rootBlock.getChildren().indexOf(block);
  84. indexofBlockAbove=startIndex-1;
  85. if(block.isLineWithinThisBlock(startLine)==0)
  86. {
  87. //if the line lies within this block we need to redraw it
  88. startIndex++;
  89. liesWithinBlock = true;
  90. }
  91. }
  92. else
  93. {
  94. startIndex = rootBlock.getChildren().size();
  95. indexofBlockAbove = startIndex-1;
  96. }
  97. if(indexofBlockAbove>=0&&((ColumnBlock)(rootBlock.getChildren().get(indexofBlockAbove))).endLine+1==startLine)
  98. {
  99. blockjustAbove = (ColumnBlock)(rootBlock.getChildren().get(indexofBlockAbove));
  100. }
  101. if(numLines>0)
  102. {
  103. rootBlock.endLine += numLines;
  104. for(int i=startIndex;i<rootBlock.getChildren().size();i++)
  105. {
  106. ((ColumnBlock)(rootBlock.getChildren().get(i))).updateLineNo(numLines);
  107. }
  108. }
  109. int startingLine =-1;
  110. int endLine = -1;
  111. if(liesWithinBlock)
  112. {
  113. ColumnBlock innerContainingBlock = block.getContainingBlock(startLine, offset);
  114. if((isASimpleChar)&&!(innerContainingBlock==null&&singleTabInserted))
  115. {
  116. //a simple char has been entered (no newline )
  117. //if this lies inside a column block update the startIndex and endIndex of this blocks corresponding ColumnBlockLine
  118. //and all subsequent ColumnBlock Lines after this one
  119. //check whether columnBlockWidth is valid
  120. //do nothing if this char does not lie inside a column block
  121. if(innerContainingBlock!=null)
  122. {
  123. if(!singleTabInserted)
  124. {
  125. innerContainingBlock.updateColumnBlockLineOffset(startLine, length, false);
  126. ColumnBlockLine containingLine = innerContainingBlock.getLines().elementAt(startLine-innerContainingBlock.startLine);
  127. startingLine = innerContainingBlock.startLine;
  128. innerContainingBlock.setTabSizeDirtyStatus(true,false);
  129. endLine = innerContainingBlock.endLine;
  130. }
  131. else
  132. {
  133. //no need to update line offset as ColumnBlock would be rebuilt
  134. ColumnBlock innerParent = (ColumnBlock)innerContainingBlock.getParent();
  135. //startingLine = innerContainingBlock.startLine;
  136. //endLine = innerContainingBlock.endLine;
  137. //innerParent.getChildren().remove(innerContainingBlock);
  138. startingLine = innerParent.startLine;
  139. endLine = innerParent.endLine;
  140. innerParent.getChildren().removeAllElements();
  141. buffer.updateColumnBlocks(startingLine, endLine,(int)innerParent.columnBlockWidth , innerParent);
  142. }
  143. }
  144. /*else if(innerContainingBlock==null&&singleTabInserted)
  145. {
  146. //TODO handle this case when tab has been inserted in FRONT and not OUTSIDE of a column block
  147. //currently whole column block gets repainted in this case
  148. }*/
  149. else
  150. {
  151. //this line must have been retokenized and repainted by the BufferHandler so repaint it again here after column blocks dirty status is updated
  152. startingLine = startLine;
  153. endLine = startLine;
  154. }
  155. }
  156. if((!isASimpleChar)||(innerContainingBlock==null&&singleTabInserted))
  157. {
  158. startingLine = block.getStartLine();
  159. endLine = block.getEndLine()+numLines;
  160. rootBlock.getChildren().remove(block);
  161. buffer.updateColumnBlocks(startingLine, endLine, 0, rootBlock);
  162. }
  163. }
  164. else
  165. {
  166. Segment seg = new Segment();
  167. buffer.getText(offset, length, seg);
  168. if(buffer.getTabStopPosition(seg)>=0)
  169. {
  170. if(blockjustAbove!=null)
  171. {
  172. rootBlock.getChildren().remove(blockjustAbove);
  173. startingLine=blockjustAbove.startLine;
  174. }
  175. else
  176. {
  177. startingLine=startLine;
  178. }
  179. if((block!=null)&&(block.startLine==startLine+numLines+1))
  180. {
  181. rootBlock.getChildren().remove(block);
  182. endLine = block.endLine;
  183. }
  184. else
  185. {
  186. endLine = startLine + numLines;
  187. }
  188. buffer.updateColumnBlocks(startingLine, endLine, 0, rootBlock);
  189. }
  190. }
  191. handledInsertion = true;
  192. rootBlock.setDirtyStatus(false);
  193. //System.out.println("AFTER UPDATING COLUMN BLOCKS-----");
  194. //System.out.println(rootBlock);
  195. if(startingLine!=-1&&endLine!=-1&&handledDeletion)
  196. {
  197. textArea.chunkCache.invalidateChunksFromPhys(startingLine);
  198. textArea.invalidateLineRange(startingLine, endLine);
  199. }
  200. }//}}}
  201. //{{{ contentRemoved() method
  202. /**
  203. * Called when text is removed from the buffer.
  204. * @param buffer The buffer in question
  205. * @param startLine The first line
  206. * @param offset The start offset, from the beginning of the buffer
  207. * @param numLines The number of lines removed
  208. * @param length The number of characters removed
  209. * @since jEdit 4.3pre3
  210. */
  211. public void contentRemoved(JEditBuffer buffer, int startLine, int offset,
  212. int numLines, int length)
  213. {
  214. if(!buffer.getBooleanProperty("elasticTabstops"))
  215. {
  216. return;
  217. }
  218. String charDeleted;
  219. boolean isASimpleChar = false;
  220. ColumnBlock rootBlock = buffer.getColumnBlock();
  221. if(rootBlock==null)
  222. {
  223. return;
  224. }
  225. if((numLines==0)&(length==1))
  226. {
  227. isASimpleChar = true;
  228. }
  229. if((!isASimpleChar))
  230. {
  231. //we need to remove column blocks
  232. //find the column block lying just below the first line deleted
  233. ColumnBlock firstBlockEffected = rootBlock.searchChildren(startLine);
  234. //info we need to determine inside this if block
  235. int startLineToBuild = -1;
  236. int endLineToBuild = -1;
  237. ColumnBlock firstBlockToBeUpdated = null;
  238. ColumnBlock firstBlockToBeRemoved = null;
  239. ColumnBlock lastBlockToBeRemoved = null;
  240. if(firstBlockEffected!=null)
  241. {
  242. int indexFirstBlockEffected =rootBlock.getChildren().indexOf(firstBlockEffected);
  243. ColumnBlock blockAboveFirstEffected = null;
  244. boolean justBelowBlock = false;
  245. if(indexFirstBlockEffected>0)
  246. {
  247. blockAboveFirstEffected = (ColumnBlock)rootBlock.getChildren().get(indexFirstBlockEffected-1);
  248. if(blockAboveFirstEffected.endLine==startLine-1 )
  249. {
  250. justBelowBlock = true;
  251. }
  252. }
  253. int posFirstLine = firstBlockEffected.isLineWithinThisBlock(startLine);
  254. boolean firstLineLiesInside =posFirstLine==0;
  255. boolean firstLineLiesAbove =posFirstLine<0;
  256. int posLastLine = firstBlockEffected.isLineWithinThisBlock(startLine+numLines);
  257. boolean lastLineLiesInside =posLastLine==0;
  258. boolean lastLineLiesAbove = posLastLine<0;
  259. boolean lastLineLiesBelow = posLastLine>0;
  260. //deletion above block
  261. if(lastLineLiesAbove )
  262. {
  263. //if last line lies above this block cannot be connected to a block above in this deletion without touching the block above
  264. /*if(justBelowBlock&&startLine+numLines+1==firstBlockEffected.startLine)
  265. {
  266. startLineToBuild=blockAboveFirstEffected.startLine;
  267. endLineToBuild= firstBlockEffected.endLine;
  268. firstBlockToBeRemoved = blockAboveFirstEffected;
  269. lastBlockToBeRemoved = firstBlockEffected;
  270. }*/
  271. firstBlockToBeUpdated = firstBlockEffected;
  272. //else
  273. //{
  274. firstBlockToBeRemoved =lastBlockToBeRemoved= null;
  275. startLineToBuild=endLineToBuild=-1;
  276. //}
  277. }
  278. //deletion inside block
  279. else if((firstLineLiesInside||firstLineLiesAbove)&&lastLineLiesInside)
  280. {
  281. startLineToBuild = Math.min( firstBlockEffected.startLine,startLine);
  282. endLineToBuild = firstBlockEffected.endLine-numLines;
  283. //if(indexFirstBlockEffected<rootBlock.getChildren().size()-1)
  284. //{
  285. //firstBlockToBeUpdated =(ColumnBlock)rootBlock.getChildren().get(indexFirstBlockEffected+1) ;
  286. //}
  287. firstBlockToBeRemoved =lastBlockToBeRemoved= firstBlockEffected;
  288. if(justBelowBlock)
  289. {
  290. startLineToBuild =blockAboveFirstEffected.startLine ;
  291. firstBlockToBeRemoved = blockAboveFirstEffected;
  292. }
  293. }
  294. //deletion might cover other blocks as well
  295. else if(((firstLineLiesInside)||(firstLineLiesAbove))&&lastLineLiesBelow)
  296. {
  297. startLineToBuild = Math.min(startLine, firstBlockEffected.startLine);
  298. firstBlockToBeRemoved = firstBlockEffected;
  299. ColumnBlock blockBelow = rootBlock.searchChildren(startLine+numLines);
  300. int indexLastBlock = rootBlock.getChildren().indexOf(blockBelow);
  301. if(blockBelow!=null)
  302. {
  303. //deletion partially overlaps this block
  304. if(blockBelow.isLineWithinThisBlock(startLine+numLines)==0)
  305. {
  306. if(justBelowBlock)
  307. {
  308. startLineToBuild =blockAboveFirstEffected.startLine ;
  309. firstBlockToBeRemoved = blockAboveFirstEffected;
  310. }
  311. lastBlockToBeRemoved = blockBelow;
  312. endLineToBuild = blockBelow.endLine-numLines;
  313. //if(indexLastBlock<rootBlock.getChildren().size()-1)
  314. //{
  315. //firstBlockToBeUpdated = (ColumnBlock)rootBlock.getChildren().get(indexLastBlock+1);
  316. //}
  317. }
  318. //deletion lies above this block
  319. else
  320. {
  321. //do not need to consider blockJustAbove here as we cannot connect two column blocks without
  322. //ending on one of the lines of either
  323. //firstBlockToBeUpdated = blockBelow;
  324. //if we have reached here there is surely a block above this one
  325. lastBlockToBeRemoved = (ColumnBlock)rootBlock.getChildren().get(indexLastBlock-1);
  326. //if the first Block is wholly covered then all column blocks are being deleted completely and there is nothing to build
  327. endLineToBuild = firstLineLiesAbove?-1:startLine;
  328. //consider the case where last line deleted is just above the column block block below
  329. if((blockBelow.startLine==startLine+numLines+1)&&(endLineToBuild!=-1))
  330. {
  331. endLineToBuild = blockBelow.endLine-numLines;
  332. lastBlockToBeRemoved = blockBelow;
  333. }
  334. if(endLineToBuild==-1)
  335. {
  336. startLineToBuild = -1;
  337. }
  338. }
  339. }
  340. //no block below last line
  341. else
  342. {
  343. lastBlockToBeRemoved = (ColumnBlock)rootBlock.getChildren().get(rootBlock.getChildren().size()-1);
  344. //firstBlockToBeUpdated = null;
  345. if(firstLineLiesInside)
  346. {
  347. endLineToBuild = startLine;
  348. }
  349. else
  350. {
  351. startLineToBuild = -1;
  352. endLineToBuild= -1;
  353. }
  354. }
  355. }
  356. }
  357. //deletion lies below all column blocks
  358. else
  359. {
  360. startLineToBuild = -1;
  361. endLineToBuild = -1;
  362. //firstBlockToBeUpdated = null;
  363. firstBlockToBeRemoved = null;
  364. lastBlockToBeRemoved = null;
  365. }
  366. //once we reach here we have three things to do
  367. //1)delete columnBlocks using firstBlockToBeDeleted and lastBlockToBeDeleted
  368. Vector blocksToBeRemoved =null;
  369. if(firstBlockToBeRemoved!=null)
  370. {
  371. int startIndex = rootBlock.getChildren().indexOf(firstBlockToBeRemoved);
  372. blocksToBeRemoved = new Vector();
  373. if(lastBlockToBeRemoved==null)
  374. {
  375. throw new IllegalArgumentException("Deletion not handled properly");
  376. }
  377. int endIndex = rootBlock.getChildren().indexOf(lastBlockToBeRemoved);
  378. for(int i=startIndex;i<=endIndex;i++)
  379. {
  380. blocksToBeRemoved.add(rootBlock.getChildren().get(i));
  381. }
  382. }
  383. //2)update startLine/endLine in column blocks using firstBlockToBeUpdated
  384. if(numLines>0)
  385. {
  386. rootBlock.endLine-=numLines;
  387. if((lastBlockToBeRemoved!=null)||(firstBlockToBeUpdated!=null))
  388. {
  389. int startIndex=-1;
  390. if(lastBlockToBeRemoved!=null)
  391. {
  392. startIndex = rootBlock.getChildren().indexOf(lastBlockToBeRemoved);
  393. //start just after the last block to be removed
  394. startIndex++;
  395. }
  396. else if(firstBlockToBeUpdated!=null)
  397. {
  398. startIndex = rootBlock.getChildren().indexOf(firstBlockToBeUpdated);
  399. }
  400. for(int i=startIndex;i<rootBlock.getChildren().size();i++)
  401. {
  402. ((ColumnBlock)rootBlock.getChildren().get(i)).updateLineNo(-1*numLines);
  403. }
  404. }
  405. }
  406. //once we are done with (2) we can safely change rootBlock
  407. if(blocksToBeRemoved!=null)
  408. {
  409. rootBlock.getChildren().removeAll(blocksToBeRemoved);
  410. }
  411. //3)rebuild column blocks using endLine and startLine
  412. if(startLineToBuild!=-1&&endLineToBuild!=-1)
  413. {
  414. buffer.updateColumnBlocks(startLineToBuild, endLineToBuild, 0, rootBlock);
  415. rootBlock.setDirtyStatus(false);
  416. textArea.chunkCache.invalidateChunksFromPhys(startLineToBuild);
  417. textArea.invalidateLineRange(startLineToBuild, endLineToBuild);
  418. }
  419. rootBlock.setDirtyStatus(false);
  420. handledDeletion = true;
  421. }
  422. else
  423. {
  424. int startingLine = -1;
  425. int endLine = -1;
  426. //a simple char has been entered
  427. //if this lies inside a column block update the startIndex and endIndex of this blocks corresponding ColumnBlockLine
  428. //and all subsequent ColumnBlock Lines after this one
  429. //check whether columnBlockWidth is valid
  430. ColumnBlock innerContainingBlock = rootBlock.getContainingBlock(startLine, offset);
  431. //do nothing if this char does not lie inside a column block
  432. if(innerContainingBlock!=null)
  433. {
  434. if(!singleTabDeleted)
  435. {
  436. innerContainingBlock.updateColumnBlockLineOffset(startLine, -1*length, false);
  437. ColumnBlockLine containingLine = innerContainingBlock.getLines().elementAt(startLine-innerContainingBlock.startLine);
  438. startingLine = innerContainingBlock.startLine;
  439. endLine = innerContainingBlock.endLine;
  440. innerContainingBlock.setTabSizeDirtyStatus(true,false);
  441. }
  442. else
  443. {
  444. //no need to update line offset as ColumnBlock would be rebuilt
  445. ColumnBlock innerParent = (ColumnBlock)innerContainingBlock.getParent();
  446. startingLine = innerContainingBlock.startLine;
  447. endLine = innerContainingBlock.endLine;
  448. innerParent.getChildren().remove(innerContainingBlock);
  449. //startingLine = innerParent.startLine;
  450. //endLine = innerParent.endLine;
  451. //innerParent.getChildren().removeAllElements();
  452. buffer.updateColumnBlocks(startingLine, endLine,(int)innerParent.columnBlockWidth , innerParent);
  453. }
  454. }
  455. else
  456. {
  457. //this line must have been retokenized and repainted by the BufferHandler so repaint it again here after column blocks dirty status is updated
  458. startingLine = startLine;
  459. endLine = startLine;
  460. }
  461. handledDeletion = true;
  462. rootBlock.setDirtyStatus(false);
  463. if(startingLine!=-1&&endLine!=-1)
  464. {
  465. textArea.chunkCache.invalidateChunksFromPhys(startingLine);
  466. textArea.invalidateLineRange(startingLine, endLine);
  467. }
  468. }
  469. }//}}}
  470. //{{{ transactionComplete() method
  471. public void transactionComplete(JEditBuffer buffer)
  472. {
  473. if(!buffer.getBooleanProperty("elasticTabstops"))
  474. {
  475. return;
  476. }
  477. if((buffer.getBooleanProperty("elasticTabstops"))&&((handledInsertion==false)||(handledDeletion==false)))
  478. {
  479. //if we reach here use brute force as a last resolve
  480. fullyUpdateColumnBlocks(buffer);
  481. }
  482. }
  483. //}}}
  484. //{{{ preContentInserted() method
  485. public void preContentInserted(JEditBuffer buffer, int startLine, int offset, int numLines, int length)
  486. {
  487. if(!buffer.getBooleanProperty("elasticTabstops"))
  488. {
  489. return;
  490. }
  491. handledInsertion = false;
  492. if(buffer.getColumnBlock()!=null)
  493. buffer.getColumnBlock().setDirtyStatus(true);
  494. } //}}}
  495. //{{{ preContentRemoved() method
  496. public void preContentRemoved(JEditBuffer buffer, int startLine, int offset,
  497. int numLines, int length)
  498. {
  499. if(!buffer.getBooleanProperty("elasticTabstops"))
  500. {
  501. return;
  502. }
  503. handledDeletion = false;
  504. singleTabDeleted = false;
  505. if(buffer.getColumnBlock()!=null)
  506. {
  507. buffer.getColumnBlock().setDirtyStatus(true);
  508. if((numLines==0)&(length==1))
  509. {
  510. String str = buffer.getText(offset, length);
  511. if(str.equals("\t"))
  512. {
  513. singleTabDeleted = true;
  514. }
  515. }
  516. }
  517. }
  518. //}}}
  519. }