The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

942 lines
27KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_CodeDocument.h"
  21. //==============================================================================
  22. class CodeDocumentLine
  23. {
  24. public:
  25. CodeDocumentLine (const juce_wchar* const line_,
  26. const int lineLength_,
  27. const int numNewLineChars,
  28. const int lineStartInFile_)
  29. : line (line_, lineLength_),
  30. lineStartInFile (lineStartInFile_),
  31. lineLength (lineLength_),
  32. lineLengthWithoutNewLines (lineLength_ - numNewLineChars)
  33. {
  34. }
  35. ~CodeDocumentLine()
  36. {
  37. }
  38. static void createLines (Array <CodeDocumentLine*>& newLines, const String& text)
  39. {
  40. const juce_wchar* const t = text;
  41. int pos = 0;
  42. while (t [pos] != 0)
  43. {
  44. const int startOfLine = pos;
  45. int numNewLineChars = 0;
  46. while (t[pos] != 0)
  47. {
  48. if (t[pos] == '\r')
  49. {
  50. ++numNewLineChars;
  51. ++pos;
  52. if (t[pos] == '\n')
  53. {
  54. ++numNewLineChars;
  55. ++pos;
  56. }
  57. break;
  58. }
  59. if (t[pos] == '\n')
  60. {
  61. ++numNewLineChars;
  62. ++pos;
  63. break;
  64. }
  65. ++pos;
  66. }
  67. newLines.add (new CodeDocumentLine (t + startOfLine, pos - startOfLine,
  68. numNewLineChars, startOfLine));
  69. }
  70. jassert (pos == text.length());
  71. }
  72. bool endsWithLineBreak() const throw()
  73. {
  74. return lineLengthWithoutNewLines != lineLength;
  75. }
  76. void updateLength() throw()
  77. {
  78. lineLengthWithoutNewLines = lineLength = line.length();
  79. while (lineLengthWithoutNewLines > 0
  80. && (line [lineLengthWithoutNewLines - 1] == '\n'
  81. || line [lineLengthWithoutNewLines - 1] == '\r'))
  82. {
  83. --lineLengthWithoutNewLines;
  84. }
  85. }
  86. String line;
  87. int lineStartInFile, lineLength, lineLengthWithoutNewLines;
  88. };
  89. //==============================================================================
  90. CodeDocument::Iterator::Iterator (CodeDocument* const document_)
  91. : document (document_),
  92. currentLine (document_->lines[0]),
  93. line (0),
  94. position (0)
  95. {
  96. }
  97. CodeDocument::Iterator::Iterator (const CodeDocument::Iterator& other)
  98. : document (other.document),
  99. currentLine (other.currentLine),
  100. line (other.line),
  101. position (other.position)
  102. {
  103. }
  104. CodeDocument::Iterator& CodeDocument::Iterator::operator= (const CodeDocument::Iterator& other) throw()
  105. {
  106. document = other.document;
  107. currentLine = other.currentLine;
  108. line = other.line;
  109. position = other.position;
  110. return *this;
  111. }
  112. CodeDocument::Iterator::~Iterator() throw()
  113. {
  114. }
  115. juce_wchar CodeDocument::Iterator::nextChar()
  116. {
  117. if (currentLine == 0)
  118. return 0;
  119. jassert (currentLine == document->lines.getUnchecked (line));
  120. const juce_wchar result = currentLine->line [position - currentLine->lineStartInFile];
  121. if (++position >= currentLine->lineStartInFile + currentLine->lineLength)
  122. {
  123. ++line;
  124. currentLine = document->lines [line];
  125. }
  126. return result;
  127. }
  128. void CodeDocument::Iterator::skip()
  129. {
  130. if (currentLine != 0)
  131. {
  132. jassert (currentLine == document->lines.getUnchecked (line));
  133. if (++position >= currentLine->lineStartInFile + currentLine->lineLength)
  134. {
  135. ++line;
  136. currentLine = document->lines [line];
  137. }
  138. }
  139. }
  140. void CodeDocument::Iterator::skipToEndOfLine()
  141. {
  142. if (currentLine != 0)
  143. {
  144. jassert (currentLine == document->lines.getUnchecked (line));
  145. ++line;
  146. currentLine = document->lines [line];
  147. if (currentLine != 0)
  148. position = currentLine->lineStartInFile;
  149. else
  150. position = document->getNumCharacters();
  151. }
  152. }
  153. juce_wchar CodeDocument::Iterator::peekNextChar() const
  154. {
  155. if (currentLine == 0)
  156. return 0;
  157. jassert (currentLine == document->lines.getUnchecked (line));
  158. return const_cast <const String&> (currentLine->line) [position - currentLine->lineStartInFile];
  159. }
  160. void CodeDocument::Iterator::skipWhitespace()
  161. {
  162. while (CharacterFunctions::isWhitespace (peekNextChar()))
  163. skip();
  164. }
  165. bool CodeDocument::Iterator::isEOF() const throw()
  166. {
  167. return currentLine == 0;
  168. }
  169. //==============================================================================
  170. CodeDocument::Position::Position() throw()
  171. : owner (0), characterPos (0), line (0),
  172. indexInLine (0), positionMaintained (false)
  173. {
  174. }
  175. CodeDocument::Position::Position (const CodeDocument* const ownerDocument,
  176. const int line_, const int indexInLine_) throw()
  177. : owner (const_cast <CodeDocument*> (ownerDocument)),
  178. characterPos (0), line (line_),
  179. indexInLine (indexInLine_), positionMaintained (false)
  180. {
  181. setLineAndIndex (line_, indexInLine_);
  182. }
  183. CodeDocument::Position::Position (const CodeDocument* const ownerDocument,
  184. const int characterPos_) throw()
  185. : owner (const_cast <CodeDocument*> (ownerDocument)),
  186. positionMaintained (false)
  187. {
  188. setPosition (characterPos_);
  189. }
  190. CodeDocument::Position::Position (const Position& other) throw()
  191. : owner (other.owner), characterPos (other.characterPos), line (other.line),
  192. indexInLine (other.indexInLine), positionMaintained (false)
  193. {
  194. jassert (*this == other);
  195. }
  196. CodeDocument::Position::~Position()
  197. {
  198. setPositionMaintained (false);
  199. }
  200. CodeDocument::Position& CodeDocument::Position::operator= (const Position& other)
  201. {
  202. if (this != &other)
  203. {
  204. const bool wasPositionMaintained = positionMaintained;
  205. if (owner != other.owner)
  206. setPositionMaintained (false);
  207. owner = other.owner;
  208. line = other.line;
  209. indexInLine = other.indexInLine;
  210. characterPos = other.characterPos;
  211. setPositionMaintained (wasPositionMaintained);
  212. jassert (*this == other);
  213. }
  214. return *this;
  215. }
  216. bool CodeDocument::Position::operator== (const Position& other) const throw()
  217. {
  218. jassert ((characterPos == other.characterPos)
  219. == (line == other.line && indexInLine == other.indexInLine));
  220. return characterPos == other.characterPos
  221. && line == other.line
  222. && indexInLine == other.indexInLine
  223. && owner == other.owner;
  224. }
  225. bool CodeDocument::Position::operator!= (const Position& other) const throw()
  226. {
  227. return ! operator== (other);
  228. }
  229. void CodeDocument::Position::setLineAndIndex (const int newLineNum, const int newIndexInLine)
  230. {
  231. jassert (owner != 0);
  232. if (owner->lines.size() == 0)
  233. {
  234. line = 0;
  235. indexInLine = 0;
  236. characterPos = 0;
  237. }
  238. else
  239. {
  240. if (newLineNum >= owner->lines.size())
  241. {
  242. line = owner->lines.size() - 1;
  243. CodeDocumentLine* const l = owner->lines.getUnchecked (line);
  244. jassert (l != 0);
  245. indexInLine = l->lineLengthWithoutNewLines;
  246. characterPos = l->lineStartInFile + indexInLine;
  247. }
  248. else
  249. {
  250. line = jmax (0, newLineNum);
  251. CodeDocumentLine* const l = owner->lines.getUnchecked (line);
  252. jassert (l != 0);
  253. if (l->lineLengthWithoutNewLines > 0)
  254. indexInLine = jlimit (0, l->lineLengthWithoutNewLines, newIndexInLine);
  255. else
  256. indexInLine = 0;
  257. characterPos = l->lineStartInFile + indexInLine;
  258. }
  259. }
  260. }
  261. void CodeDocument::Position::setPosition (const int newPosition)
  262. {
  263. jassert (owner != 0);
  264. line = 0;
  265. indexInLine = 0;
  266. characterPos = 0;
  267. if (newPosition > 0)
  268. {
  269. int lineStart = 0;
  270. int lineEnd = owner->lines.size();
  271. for (;;)
  272. {
  273. if (lineEnd - lineStart < 4)
  274. {
  275. for (int i = lineStart; i < lineEnd; ++i)
  276. {
  277. CodeDocumentLine* const l = owner->lines.getUnchecked (i);
  278. int index = newPosition - l->lineStartInFile;
  279. if (index >= 0 && (index < l->lineLength || i == lineEnd - 1))
  280. {
  281. line = i;
  282. indexInLine = jmin (l->lineLengthWithoutNewLines, index);
  283. characterPos = l->lineStartInFile + indexInLine;
  284. }
  285. }
  286. break;
  287. }
  288. else
  289. {
  290. const int midIndex = (lineStart + lineEnd + 1) / 2;
  291. CodeDocumentLine* const mid = owner->lines.getUnchecked (midIndex);
  292. if (newPosition >= mid->lineStartInFile)
  293. lineStart = midIndex;
  294. else
  295. lineEnd = midIndex;
  296. }
  297. }
  298. }
  299. }
  300. void CodeDocument::Position::moveBy (int characterDelta)
  301. {
  302. jassert (owner != 0);
  303. if (characterDelta == 1)
  304. {
  305. setPosition (getPosition());
  306. // If moving right, make sure we don't get stuck between the \r and \n characters..
  307. if (line < owner->lines.size())
  308. {
  309. CodeDocumentLine* const l = owner->lines.getUnchecked (line);
  310. if (indexInLine + characterDelta < l->lineLength
  311. && indexInLine + characterDelta >= l->lineLengthWithoutNewLines + 1)
  312. ++characterDelta;
  313. }
  314. }
  315. setPosition (characterPos + characterDelta);
  316. }
  317. const CodeDocument::Position CodeDocument::Position::movedBy (const int characterDelta) const
  318. {
  319. CodeDocument::Position p (*this);
  320. p.moveBy (characterDelta);
  321. return p;
  322. }
  323. const CodeDocument::Position CodeDocument::Position::movedByLines (const int deltaLines) const
  324. {
  325. CodeDocument::Position p (*this);
  326. p.setLineAndIndex (getLineNumber() + deltaLines, getIndexInLine());
  327. return p;
  328. }
  329. const juce_wchar CodeDocument::Position::getCharacter() const
  330. {
  331. const CodeDocumentLine* const l = owner->lines [line];
  332. return l == 0 ? 0 : l->line [getIndexInLine()];
  333. }
  334. const String CodeDocument::Position::getLineText() const
  335. {
  336. const CodeDocumentLine* const l = owner->lines [line];
  337. return l == 0 ? String::empty : l->line;
  338. }
  339. void CodeDocument::Position::setPositionMaintained (const bool isMaintained)
  340. {
  341. if (isMaintained != positionMaintained)
  342. {
  343. positionMaintained = isMaintained;
  344. if (owner != 0)
  345. {
  346. if (isMaintained)
  347. {
  348. jassert (! owner->positionsToMaintain.contains (this));
  349. owner->positionsToMaintain.add (this);
  350. }
  351. else
  352. {
  353. // If this happens, you may have deleted the document while there are Position objects that are still using it...
  354. jassert (owner->positionsToMaintain.contains (this));
  355. owner->positionsToMaintain.removeValue (this);
  356. }
  357. }
  358. }
  359. }
  360. //==============================================================================
  361. CodeDocument::CodeDocument()
  362. : undoManager (std::numeric_limits<int>::max(), 10000),
  363. currentActionIndex (0),
  364. indexOfSavedState (-1),
  365. maximumLineLength (-1),
  366. newLineChars ("\r\n")
  367. {
  368. }
  369. CodeDocument::~CodeDocument()
  370. {
  371. }
  372. const String CodeDocument::getAllContent() const
  373. {
  374. return getTextBetween (Position (this, 0),
  375. Position (this, lines.size(), 0));
  376. }
  377. const String CodeDocument::getTextBetween (const Position& start, const Position& end) const
  378. {
  379. if (end.getPosition() <= start.getPosition())
  380. return String::empty;
  381. const int startLine = start.getLineNumber();
  382. const int endLine = end.getLineNumber();
  383. if (startLine == endLine)
  384. {
  385. CodeDocumentLine* const line = lines [startLine];
  386. return (line == 0) ? String::empty : line->line.substring (start.getIndexInLine(), end.getIndexInLine());
  387. }
  388. String result;
  389. result.preallocateStorage (end.getPosition() - start.getPosition() + 4);
  390. String::Concatenator concatenator (result);
  391. const int maxLine = jmin (lines.size() - 1, endLine);
  392. for (int i = jmax (0, startLine); i <= maxLine; ++i)
  393. {
  394. const CodeDocumentLine* line = lines.getUnchecked(i);
  395. int len = line->lineLength;
  396. if (i == startLine)
  397. {
  398. const int index = start.getIndexInLine();
  399. concatenator.append (line->line.substring (index, len));
  400. }
  401. else if (i == endLine)
  402. {
  403. len = end.getIndexInLine();
  404. concatenator.append (line->line.substring (0, len));
  405. }
  406. else
  407. {
  408. concatenator.append (line->line);
  409. }
  410. }
  411. return result;
  412. }
  413. int CodeDocument::getNumCharacters() const throw()
  414. {
  415. const CodeDocumentLine* const lastLine = lines.getLast();
  416. return (lastLine == 0) ? 0 : lastLine->lineStartInFile + lastLine->lineLength;
  417. }
  418. const String CodeDocument::getLine (const int lineIndex) const throw()
  419. {
  420. const CodeDocumentLine* const line = lines [lineIndex];
  421. return (line == 0) ? String::empty : line->line;
  422. }
  423. int CodeDocument::getMaximumLineLength() throw()
  424. {
  425. if (maximumLineLength < 0)
  426. {
  427. maximumLineLength = 0;
  428. for (int i = lines.size(); --i >= 0;)
  429. maximumLineLength = jmax (maximumLineLength, lines.getUnchecked(i)->lineLength);
  430. }
  431. return maximumLineLength;
  432. }
  433. void CodeDocument::deleteSection (const Position& startPosition, const Position& endPosition)
  434. {
  435. remove (startPosition.getPosition(), endPosition.getPosition(), true);
  436. }
  437. void CodeDocument::insertText (const Position& position, const String& text)
  438. {
  439. insert (text, position.getPosition(), true);
  440. }
  441. void CodeDocument::replaceAllContent (const String& newContent)
  442. {
  443. remove (0, getNumCharacters(), true);
  444. insert (newContent, 0, true);
  445. }
  446. bool CodeDocument::loadFromStream (InputStream& stream)
  447. {
  448. replaceAllContent (stream.readEntireStreamAsString());
  449. setSavePoint();
  450. clearUndoHistory();
  451. return true;
  452. }
  453. bool CodeDocument::writeToStream (OutputStream& stream)
  454. {
  455. for (int i = 0; i < lines.size(); ++i)
  456. {
  457. String temp (lines.getUnchecked(i)->line); // use a copy to avoid bloating the memory footprint of the stored string.
  458. const char* utf8 = temp.toUTF8();
  459. if (! stream.write (utf8, (int) strlen (utf8)))
  460. return false;
  461. }
  462. return true;
  463. }
  464. void CodeDocument::setNewLineCharacters (const String& newLineChars_) throw()
  465. {
  466. jassert (newLineChars_ == "\r\n" || newLineChars_ == "\n" || newLineChars_ == "\r");
  467. newLineChars = newLineChars_;
  468. }
  469. void CodeDocument::newTransaction()
  470. {
  471. undoManager.beginNewTransaction (String::empty);
  472. }
  473. void CodeDocument::undo()
  474. {
  475. newTransaction();
  476. undoManager.undo();
  477. }
  478. void CodeDocument::redo()
  479. {
  480. undoManager.redo();
  481. }
  482. void CodeDocument::clearUndoHistory()
  483. {
  484. undoManager.clearUndoHistory();
  485. }
  486. void CodeDocument::setSavePoint() throw()
  487. {
  488. indexOfSavedState = currentActionIndex;
  489. }
  490. bool CodeDocument::hasChangedSinceSavePoint() const throw()
  491. {
  492. return currentActionIndex != indexOfSavedState;
  493. }
  494. //==============================================================================
  495. namespace CodeDocumentHelpers
  496. {
  497. int getCharacterType (const juce_wchar character) throw()
  498. {
  499. return (CharacterFunctions::isLetterOrDigit (character) || character == '_')
  500. ? 2 : (CharacterFunctions::isWhitespace (character) ? 0 : 1);
  501. }
  502. }
  503. const CodeDocument::Position CodeDocument::findWordBreakAfter (const Position& position) const throw()
  504. {
  505. Position p (position);
  506. const int maxDistance = 256;
  507. int i = 0;
  508. while (i < maxDistance
  509. && CharacterFunctions::isWhitespace (p.getCharacter())
  510. && (i == 0 || (p.getCharacter() != '\n'
  511. && p.getCharacter() != '\r')))
  512. {
  513. ++i;
  514. p.moveBy (1);
  515. }
  516. if (i == 0)
  517. {
  518. const int type = CodeDocumentHelpers::getCharacterType (p.getCharacter());
  519. while (i < maxDistance && type == CodeDocumentHelpers::getCharacterType (p.getCharacter()))
  520. {
  521. ++i;
  522. p.moveBy (1);
  523. }
  524. while (i < maxDistance
  525. && CharacterFunctions::isWhitespace (p.getCharacter())
  526. && (i == 0 || (p.getCharacter() != '\n'
  527. && p.getCharacter() != '\r')))
  528. {
  529. ++i;
  530. p.moveBy (1);
  531. }
  532. }
  533. return p;
  534. }
  535. const CodeDocument::Position CodeDocument::findWordBreakBefore (const Position& position) const throw()
  536. {
  537. Position p (position);
  538. const int maxDistance = 256;
  539. int i = 0;
  540. bool stoppedAtLineStart = false;
  541. while (i < maxDistance)
  542. {
  543. const juce_wchar c = p.movedBy (-1).getCharacter();
  544. if (c == '\r' || c == '\n')
  545. {
  546. stoppedAtLineStart = true;
  547. if (i > 0)
  548. break;
  549. }
  550. if (! CharacterFunctions::isWhitespace (c))
  551. break;
  552. p.moveBy (-1);
  553. ++i;
  554. }
  555. if (i < maxDistance && ! stoppedAtLineStart)
  556. {
  557. const int type = CodeDocumentHelpers::getCharacterType (p.movedBy (-1).getCharacter());
  558. while (i < maxDistance && type == CodeDocumentHelpers::getCharacterType (p.movedBy (-1).getCharacter()))
  559. {
  560. p.moveBy (-1);
  561. ++i;
  562. }
  563. }
  564. return p;
  565. }
  566. void CodeDocument::checkLastLineStatus()
  567. {
  568. while (lines.size() > 0
  569. && lines.getLast()->lineLength == 0
  570. && (lines.size() == 1 || ! lines.getUnchecked (lines.size() - 2)->endsWithLineBreak()))
  571. {
  572. // remove any empty lines at the end if the preceding line doesn't end in a newline.
  573. lines.removeLast();
  574. }
  575. const CodeDocumentLine* const lastLine = lines.getLast();
  576. if (lastLine != 0 && lastLine->endsWithLineBreak())
  577. {
  578. // check that there's an empty line at the end if the preceding one ends in a newline..
  579. lines.add (new CodeDocumentLine (String::empty, 0, 0, lastLine->lineStartInFile + lastLine->lineLength));
  580. }
  581. }
  582. //==============================================================================
  583. void CodeDocument::addListener (CodeDocument::Listener* const listener) throw()
  584. {
  585. listeners.add (listener);
  586. }
  587. void CodeDocument::removeListener (CodeDocument::Listener* const listener) throw()
  588. {
  589. listeners.remove (listener);
  590. }
  591. void CodeDocument::sendListenerChangeMessage (const int startLine, const int endLine)
  592. {
  593. Position startPos (this, startLine, 0);
  594. Position endPos (this, endLine, 0);
  595. listeners.call (&CodeDocument::Listener::codeDocumentChanged, startPos, endPos);
  596. }
  597. //==============================================================================
  598. class CodeDocumentInsertAction : public UndoableAction
  599. {
  600. public:
  601. CodeDocumentInsertAction (CodeDocument& owner_, const String& text_, const int insertPos_) throw()
  602. : owner (owner_),
  603. text (text_),
  604. insertPos (insertPos_)
  605. {
  606. }
  607. bool perform()
  608. {
  609. owner.currentActionIndex++;
  610. owner.insert (text, insertPos, false);
  611. return true;
  612. }
  613. bool undo()
  614. {
  615. owner.currentActionIndex--;
  616. owner.remove (insertPos, insertPos + text.length(), false);
  617. return true;
  618. }
  619. int getSizeInUnits() { return text.length() + 32; }
  620. private:
  621. CodeDocument& owner;
  622. const String text;
  623. int insertPos;
  624. JUCE_DECLARE_NON_COPYABLE (CodeDocumentInsertAction);
  625. };
  626. void CodeDocument::insert (const String& text, const int insertPos, const bool undoable)
  627. {
  628. if (text.isEmpty())
  629. return;
  630. if (undoable)
  631. {
  632. undoManager.perform (new CodeDocumentInsertAction (*this, text, insertPos));
  633. }
  634. else
  635. {
  636. Position pos (this, insertPos);
  637. const int firstAffectedLine = pos.getLineNumber();
  638. int lastAffectedLine = firstAffectedLine + 1;
  639. CodeDocumentLine* const firstLine = lines [firstAffectedLine];
  640. String textInsideOriginalLine (text);
  641. if (firstLine != 0)
  642. {
  643. const int index = pos.getIndexInLine();
  644. textInsideOriginalLine = firstLine->line.substring (0, index)
  645. + textInsideOriginalLine
  646. + firstLine->line.substring (index);
  647. }
  648. maximumLineLength = -1;
  649. Array <CodeDocumentLine*> newLines;
  650. CodeDocumentLine::createLines (newLines, textInsideOriginalLine);
  651. jassert (newLines.size() > 0);
  652. CodeDocumentLine* const newFirstLine = newLines.getUnchecked (0);
  653. newFirstLine->lineStartInFile = firstLine != 0 ? firstLine->lineStartInFile : 0;
  654. lines.set (firstAffectedLine, newFirstLine);
  655. if (newLines.size() > 1)
  656. {
  657. for (int i = 1; i < newLines.size(); ++i)
  658. {
  659. CodeDocumentLine* const l = newLines.getUnchecked (i);
  660. lines.insert (firstAffectedLine + i, l);
  661. }
  662. lastAffectedLine = lines.size();
  663. }
  664. int i, lineStart = newFirstLine->lineStartInFile;
  665. for (i = firstAffectedLine; i < lines.size(); ++i)
  666. {
  667. CodeDocumentLine* const l = lines.getUnchecked (i);
  668. l->lineStartInFile = lineStart;
  669. lineStart += l->lineLength;
  670. }
  671. checkLastLineStatus();
  672. const int newTextLength = text.length();
  673. for (i = 0; i < positionsToMaintain.size(); ++i)
  674. {
  675. CodeDocument::Position* const p = positionsToMaintain.getUnchecked(i);
  676. if (p->getPosition() >= insertPos)
  677. p->setPosition (p->getPosition() + newTextLength);
  678. }
  679. sendListenerChangeMessage (firstAffectedLine, lastAffectedLine);
  680. }
  681. }
  682. //==============================================================================
  683. class CodeDocumentDeleteAction : public UndoableAction
  684. {
  685. public:
  686. CodeDocumentDeleteAction (CodeDocument& owner_, const int startPos_, const int endPos_) throw()
  687. : owner (owner_),
  688. startPos (startPos_),
  689. endPos (endPos_)
  690. {
  691. removedText = owner.getTextBetween (CodeDocument::Position (&owner, startPos),
  692. CodeDocument::Position (&owner, endPos));
  693. }
  694. bool perform()
  695. {
  696. owner.currentActionIndex++;
  697. owner.remove (startPos, endPos, false);
  698. return true;
  699. }
  700. bool undo()
  701. {
  702. owner.currentActionIndex--;
  703. owner.insert (removedText, startPos, false);
  704. return true;
  705. }
  706. int getSizeInUnits() { return removedText.length() + 32; }
  707. private:
  708. CodeDocument& owner;
  709. int startPos, endPos;
  710. String removedText;
  711. JUCE_DECLARE_NON_COPYABLE (CodeDocumentDeleteAction);
  712. };
  713. void CodeDocument::remove (const int startPos, const int endPos, const bool undoable)
  714. {
  715. if (endPos <= startPos)
  716. return;
  717. if (undoable)
  718. {
  719. undoManager.perform (new CodeDocumentDeleteAction (*this, startPos, endPos));
  720. }
  721. else
  722. {
  723. Position startPosition (this, startPos);
  724. Position endPosition (this, endPos);
  725. maximumLineLength = -1;
  726. const int firstAffectedLine = startPosition.getLineNumber();
  727. const int endLine = endPosition.getLineNumber();
  728. int lastAffectedLine = firstAffectedLine + 1;
  729. CodeDocumentLine* const firstLine = lines.getUnchecked (firstAffectedLine);
  730. if (firstAffectedLine == endLine)
  731. {
  732. firstLine->line = firstLine->line.substring (0, startPosition.getIndexInLine())
  733. + firstLine->line.substring (endPosition.getIndexInLine());
  734. firstLine->updateLength();
  735. }
  736. else
  737. {
  738. lastAffectedLine = lines.size();
  739. CodeDocumentLine* const lastLine = lines.getUnchecked (endLine);
  740. jassert (lastLine != 0);
  741. firstLine->line = firstLine->line.substring (0, startPosition.getIndexInLine())
  742. + lastLine->line.substring (endPosition.getIndexInLine());
  743. firstLine->updateLength();
  744. int numLinesToRemove = endLine - firstAffectedLine;
  745. lines.removeRange (firstAffectedLine + 1, numLinesToRemove);
  746. }
  747. int i;
  748. for (i = firstAffectedLine + 1; i < lines.size(); ++i)
  749. {
  750. CodeDocumentLine* const l = lines.getUnchecked (i);
  751. const CodeDocumentLine* const previousLine = lines.getUnchecked (i - 1);
  752. l->lineStartInFile = previousLine->lineStartInFile + previousLine->lineLength;
  753. }
  754. checkLastLineStatus();
  755. const int totalChars = getNumCharacters();
  756. for (i = 0; i < positionsToMaintain.size(); ++i)
  757. {
  758. CodeDocument::Position* p = positionsToMaintain.getUnchecked(i);
  759. if (p->getPosition() > startPosition.getPosition())
  760. p->setPosition (jmax (startPos, p->getPosition() + startPos - endPos));
  761. if (p->getPosition() > totalChars)
  762. p->setPosition (totalChars);
  763. }
  764. sendListenerChangeMessage (firstAffectedLine, lastAffectedLine);
  765. }
  766. }
  767. END_JUCE_NAMESPACE