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