xref: /haiku/src/apps/mail/KUndoBuffer.cpp (revision 74252cefbcf266291fb069466189b4734eb05455)
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <memory.h>
4 #include "KUndoBuffer.h"
5 
6 
7 KUndoItem::KUndoItem(const char* redo_text, int32 length, int32 offset,
8 	undo_type history, int32 cursor_pos)
9 {
10 	Offset = offset;
11 	Length = length;
12 	History = history;
13 	CursorPos = cursor_pos;
14 
15 	if (redo_text != NULL) {
16 		RedoText = (char*)malloc(length);
17 		memcpy(RedoText, redo_text, length);
18 		if (RedoText != NULL)
19 			fStatus = B_OK;
20 		else
21 			fStatus = B_ERROR;
22 	}
23 }
24 
25 
26 KUndoItem::~KUndoItem()
27 {
28 	free(RedoText);
29 }
30 
31 
32 status_t
33 KUndoItem::InitCheck()
34 {
35 	return fStatus;
36 }
37 
38 
39 void
40 KUndoItem::Merge(const char* text, int32 length)
41 {
42 	RedoText = (char*)realloc(RedoText, Length + length);
43 	memcpy(&RedoText[Length], text, length);
44 	Length += length;
45 }
46 
47 
48 KUndoBuffer::KUndoBuffer():BList(1024)
49 {
50 	fIndex = 0;
51 	Off();
52 	fNewItem = true;
53 }
54 
55 
56 KUndoBuffer::~KUndoBuffer()
57 {
58 	MakeEmpty();
59 }
60 
61 
62 bool
63 KUndoBuffer::AddItem(KUndoItem* item, int32 index)
64 {
65 	for (int32 i = CountItems() - 1; i >= index; i--)
66 		RemoveItem(i);
67 
68 	return AddItem(item);
69 }
70 
71 
72 bool
73 KUndoBuffer::AddItem(KUndoItem* item)
74 {
75 	return BList::AddItem(item);
76 }
77 
78 
79 void
80 KUndoBuffer::MakeEmpty(void)
81 {
82 	for (int32 i = CountItems() - 1; i >= 0; i--)
83 		RemoveItem(i);
84 }
85 
86 
87 KUndoItem*
88 KUndoBuffer::RemoveItem(int32 index)
89 {
90 	if (fIndex >= CountItems())
91 		fIndex--;
92 	delete this->ItemAt(index);
93 	return (KUndoItem*)BList::RemoveItem(index);
94 }
95 
96 
97 KUndoItem*
98 KUndoBuffer::ItemAt(int32 index) const
99 {
100 	return (KUndoItem*)BList::ItemAt(index);
101 }
102 
103 
104 void
105 KUndoBuffer::On()
106 {
107 	fNoTouch = false;
108 }
109 
110 
111 void
112 KUndoBuffer::Off()
113 {
114 	fNoTouch = true;
115 }
116 
117 
118 status_t
119 KUndoBuffer::NewUndo(const char* text, int32 length, int32 offset,
120 	undo_type history, int32 cursor_pos)
121 {
122 	KUndoItem* NewUndoItem = new KUndoItem(text, length, offset, history,
123 		cursor_pos);
124 
125 	status_t status = NewUndoItem->InitCheck();
126 	if (status != B_OK) {
127 		delete NewUndoItem;
128 		return status;
129 	}
130 	AddItem(NewUndoItem, fIndex);
131 	fIndex++;
132 	return status;
133 }
134 
135 
136 status_t
137 KUndoBuffer::AddUndo(const char* text, int32 length, int32 offset,
138 	undo_type history, int32 cursor_pos)
139 {
140 	if (fNoTouch)
141 		return B_OK;
142 
143 	status_t status = B_OK;
144 
145 	if (fNewItem || fIndex < CountItems() || CountItems() == 0) {
146 		status = NewUndo(text, length, offset, history, cursor_pos);
147 		fNewItem = false;
148 	} else {
149 		KUndoItem* CurrentUndoItem;
150 		CurrentUndoItem = ItemAt(fIndex - 1);
151 		if (CurrentUndoItem != NULL) {
152 			int32 c_length = CurrentUndoItem->Length;
153 			int32 c_offset = CurrentUndoItem->Offset;
154 			undo_type c_history = CurrentUndoItem->History;
155 			if (c_history == history) {
156 				switch(c_history) {
157 					case K_INSERTED:
158 					case K_REPLACED:
159 						if ((c_offset + c_length) == offset)
160 							CurrentUndoItem->Merge(text, length);
161 						else {
162 							status = NewUndo(text, length, offset, history,
163 								cursor_pos);
164 						}
165 						break;
166 					case K_DELETED:
167 						status = NewUndo(text, length, offset, history,
168 							cursor_pos);
169 						break;
170 				}
171 			} else
172 				status = NewUndo(text, length, offset, history, cursor_pos);
173 		}
174 	}
175 
176 	return status;
177 }
178 
179 
180 status_t
181 KUndoBuffer::MakeNewUndoItem()
182 {
183 	if (fIndex >= CountItems()) {
184 		fNewItem = true;
185 		return B_OK;
186 	}
187 	return B_ERROR;
188 }
189 
190 
191 status_t
192 KUndoBuffer::Undo(char** text, int32* length, int32* offset,
193 	undo_type* history, int32* cursor_pos)
194 {
195 	KUndoItem* undoItem;
196 	status_t status = B_ERROR;
197 
198 	if (fIndex > 0) {
199 		undoItem = ItemAt(fIndex - 1);
200 		if (undoItem != NULL) {
201 			*text = undoItem->RedoText;
202 			*length = undoItem->Length;
203 			*offset = undoItem->Offset;
204 			*history = undoItem->History;
205 			*cursor_pos = undoItem->CursorPos + undoItem->Length;
206 			status = B_OK;
207 		}
208 		fIndex--;
209 	}
210 	return status;
211 }
212 
213 
214 status_t
215 KUndoBuffer::Redo(char** text, int32* length, int32* offset,
216 	undo_type* history, int32* cursor_pos, bool* replaced)
217 {
218 	KUndoItem* undoItem;
219 	status_t status = B_ERROR;
220 
221 	if (fIndex < CountItems()) {
222 		undoItem = ItemAt(fIndex);
223 		if (undoItem != NULL) {
224 			*text = undoItem->RedoText;
225 			*length = undoItem->Length;
226 			*offset = undoItem->Offset;
227 			*history = undoItem->History;
228 			*cursor_pos = undoItem->CursorPos;
229 			if (fIndex + 1 < CountItems())
230 				*replaced = ItemAt(fIndex + 1)->History == K_REPLACED;
231 			else
232 				*replaced = false;
233 			status = B_OK;
234 		}
235 		fIndex++;
236 	}
237 	return status;
238 }
239 
240 
241 void
242 KUndoBuffer::PrintToStream()
243 {
244 	for (int32 i = 0; i < CountItems(); i++) {
245 		KUndoItem* item = ItemAt(i);
246 		printf("%3.3d   ", (int)i);
247 		switch (item->History) {
248 			case K_INSERTED:
249 				printf("INSERTED  ");
250 				break;
251 			case K_DELETED:
252 				printf("DELETED   ");
253 				break;
254 			case K_REPLACED:
255 				printf("REPLACED  ");
256 				break;
257 		}
258 		printf("Offset = %d  ", (int)item->Offset);
259 		printf("Length = %d  ", (int)item->Length);
260 		printf("CursorPos = %d  ", (int)item->CursorPos);
261 		printf("RedoText = '");
262 		for (int32 j = 0; j < item->Length; j++) {
263 			uchar c = (uchar)item->RedoText[j];
264 			if (c >= 0x20)
265 				printf("%c", c);
266 			else
267 				printf("?");
268 		}
269 		printf("'\n");
270 	}
271 }
272 
273