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		memcpy(RedoText, redo_text, length);
18		if (RedoText != NULL)
19			fStatus = B_OK;
20		else
21			fStatus = B_ERROR;
22	}
23}
24
25
26KUndoItem::~KUndoItem()
27{
28	free(RedoText);
29}
30
31
32status_t
33KUndoItem::InitCheck()
34{
35	return fStatus;
36}
37
38
39void
40KUndoItem::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
48KUndoBuffer::KUndoBuffer():BList(1024)
49{
50	fIndex = 0;
51	Off();
52	fNewItem = true;
53}
54
55
56KUndoBuffer::~KUndoBuffer()
57{
58	MakeEmpty();
59}
60
61
62bool
63KUndoBuffer::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
72bool
73KUndoBuffer::AddItem(KUndoItem* item)
74{
75	return BList::AddItem(item);
76}
77
78
79void
80KUndoBuffer::MakeEmpty(void)
81{
82	for (int32 i = CountItems() - 1; i >= 0; i--)
83		RemoveItem(i);
84}
85
86
87KUndoItem*
88KUndoBuffer::RemoveItem(int32 index)
89{
90	if (fIndex >= CountItems())
91		fIndex--;
92	delete this->ItemAt(index);
93	return (KUndoItem*)BList::RemoveItem(index);
94}
95
96
97KUndoItem*
98KUndoBuffer::ItemAt(int32 index) const
99{
100	return (KUndoItem*)BList::ItemAt(index);
101}
102
103
104void
105KUndoBuffer::On()
106{
107	fNoTouch = false;
108}
109
110
111void
112KUndoBuffer::Off()
113{
114	fNoTouch = true;
115}
116
117
118status_t
119KUndoBuffer::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
136status_t
137KUndoBuffer::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
180status_t
181KUndoBuffer::MakeNewUndoItem()
182{
183	if (fIndex >= CountItems()) {
184		fNewItem = true;
185		return B_OK;
186	}
187	return B_ERROR;
188}
189
190
191status_t
192KUndoBuffer::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
214status_t
215KUndoBuffer::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
241void
242KUndoBuffer::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