undo.cc 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Copyright (C) 2003 Mooffie <mooffie@typo.co.il>
  2. //
  3. // This program is free software; you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation; either version 2 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  16. #include "undo.h"
  17. #include "dbg.h"
  18. #define DEFAULT_LIMIT 4000
  19. #define RESERVE 1000
  20. UndoStack::UndoStack()
  21. {
  22. top = 0;
  23. merge_small_ops = false;
  24. bytes_size = 0;
  25. bytes_size_limit = DEFAULT_LIMIT;
  26. truncated = false;
  27. }
  28. void UndoStack::clear()
  29. {
  30. stack.clear();
  31. bytes_size = 0;
  32. top = 0;
  33. truncated = false;
  34. }
  35. // get_prev_op() - returns a pointer to the last operation made to the buffer.
  36. // returns NULL if the undo stack is empty.
  37. UndoOp* UndoStack::get_prev_op()
  38. {
  39. if (top == 0)
  40. return NULL;
  41. return &stack[--top];
  42. }
  43. // get_next_op() - returns a pointer to the last operation that was undone.
  44. UndoOp *UndoStack::get_next_op()
  45. {
  46. if (top == stack.size())
  47. return NULL;
  48. return &stack[top++];
  49. }
  50. void UndoStack::set_size_limit(size_t limit)
  51. {
  52. bytes_size_limit = limit;
  53. clear();
  54. }
  55. void UndoStack::update_size_up(int chars_count)
  56. {
  57. bytes_size += chars_count * sizeof(unichar);
  58. }
  59. void UndoStack::update_size_up(const UndoOp &op)
  60. {
  61. bytes_size += op.calc_size();
  62. }
  63. void UndoStack::update_size_down(const UndoOp &op)
  64. {
  65. bytes_size -= op.calc_size();
  66. }
  67. // truncate_undo() - called when the stack size is too big. it erases the
  68. // old operations.
  69. void UndoStack::truncate_undo()
  70. {
  71. unsigned new_size = bytes_size_limit;
  72. // we have to reserve some space in order not to reach the
  73. // size-limit very soon.
  74. if (new_size > RESERVE)
  75. new_size -= RESERVE;
  76. else
  77. new_size = 0;
  78. unsigned i = 0;
  79. while (i < stack.size() && bytes_size > new_size) {
  80. update_size_down(stack[i++]);
  81. }
  82. stack.erase(stack.begin(), stack.begin() + i);
  83. top -= i;
  84. truncated = true;
  85. }
  86. // merge() - merges a new operation with the last operation. returns false if
  87. // that was not possible.
  88. //
  89. // An example: suppose the last operation was to insert "a" into the buffer.
  90. // If the user now types "b", merge() will change the last operation to record
  91. // an insertion of "ab".
  92. //
  93. // However, if the user moves the cursor to a different location before typing
  94. // "b", such a merge is not possible, because "a" and "b" are not adjacent.
  95. bool UndoOp::merge(const UndoOp &op)
  96. {
  97. if (type != op.type)
  98. return false;
  99. if (op.inserted_text.len() + op.deleted_text.len() != 1)
  100. return false;
  101. if (point.para != op.point.para)
  102. return false;
  103. switch (type) {
  104. case opInsert:
  105. if (op.point.pos == point.pos + inserted_text.len()) {
  106. inserted_text.append(op.inserted_text);
  107. return true;
  108. }
  109. break;
  110. case opDelete:
  111. if (op.point.pos == point.pos) {
  112. deleted_text.append(op.deleted_text);
  113. return true;
  114. }
  115. if (op.point.pos == point.pos - 1) {
  116. point.pos--;
  117. deleted_text.insert(deleted_text.begin(),
  118. op.deleted_text.begin(),
  119. op.deleted_text.end());
  120. return true;
  121. }
  122. break;
  123. default:
  124. break;
  125. }
  126. return false;
  127. }
  128. // erase_redo_ops() - erases the operations that were undone.
  129. void UndoStack::erase_redo_ops()
  130. {
  131. for (unsigned i = top; i < stack.size(); i++)
  132. update_size_down(stack[i]);
  133. stack.erase(stack.begin() + top, stack.end());
  134. }
  135. // record_op() - records an operation on the stack.
  136. void UndoStack::record_op(const UndoOp &op)
  137. {
  138. if (disabled())
  139. return;
  140. // recording an opearation erases the recordings of all the operations
  141. // that were undone till now.
  142. if (is_redo_available())
  143. erase_redo_ops();
  144. if (undo_size_too_big())
  145. truncate_undo();
  146. // first try to merge this operation with the previous one. if that
  147. // fails, record it as a separate operation.
  148. if (merge_small_ops && !stack.empty() && stack.back().merge(op)) {
  149. update_size_up(1); // one character was recorded
  150. } else {
  151. stack.push_back(op);
  152. update_size_up(op);
  153. top++;
  154. }
  155. }