xref: /haiku/src/kits/interface/textview_support/TextGapBuffer.cpp (revision 302f62604763c95777d6d04cca456e876f471c4f)
1 /*
2  * Copyright 2001-2006, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Stefano Ceccherini (burton666@libero.it)
8  */
9 
10 #include <cstdlib>
11 #include <cstring>
12 
13 #include <utf8_functions.h>
14 
15 #include <File.h>
16 #include <InterfaceDefs.h> // for B_UTF8_BULLET
17 
18 #include "TextGapBuffer.h"
19 
20 
21 _BTextGapBuffer_::_BTextGapBuffer_()
22 	:	fExtraCount(2048),
23 		fItemCount(0),
24 		fBuffer(NULL),
25 		fBufferCount(fExtraCount + fItemCount),
26 		fGapIndex(fItemCount),
27 		fGapCount(fBufferCount - fGapIndex),
28 		fScratchBuffer(NULL),
29 		fScratchSize(0),
30 		fPasswordMode(false)
31 {
32 	fBuffer = (char *)malloc(fExtraCount + fItemCount);
33 	fScratchBuffer = NULL;
34 }
35 
36 
37 _BTextGapBuffer_::~_BTextGapBuffer_()
38 {
39 	free(fBuffer);
40 	free(fScratchBuffer);
41 }
42 
43 
44 void
45 _BTextGapBuffer_::InsertText(const char *inText, int32 inNumItems, int32 inAtIndex)
46 {
47 	if (inNumItems < 1)
48 		return;
49 
50 	inAtIndex = (inAtIndex > fItemCount) ? fItemCount : inAtIndex;
51 	inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex;
52 
53 	if (inAtIndex != fGapIndex)
54 		MoveGapTo(inAtIndex);
55 
56 	if (fGapCount < inNumItems)
57 		SizeGapTo(inNumItems + fExtraCount);
58 
59 	memcpy(fBuffer + fGapIndex, inText, inNumItems);
60 
61 	fGapCount -= inNumItems;
62 	fGapIndex += inNumItems;
63 	fItemCount += inNumItems;
64 }
65 
66 
67 void
68 _BTextGapBuffer_::InsertText(BFile *file, int32 fileOffset, int32 inNumItems, int32 inAtIndex)
69 {
70 	off_t fileSize;
71 
72 	if (file->GetSize(&fileSize) != B_OK
73 		|| !file->IsReadable())
74 		return;
75 
76 	// Clamp the text length to the file size
77 	fileSize -= fileOffset;
78 
79 	if (fileSize < inNumItems)
80 		inNumItems = fileSize;
81 
82 	if (inNumItems < 1)
83 		return;
84 
85 	inAtIndex = (inAtIndex > fItemCount) ? fItemCount : inAtIndex;
86 	inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex;
87 
88 	if (inAtIndex != fGapIndex)
89 		MoveGapTo(inAtIndex);
90 
91 	if (fGapCount < inNumItems)
92 		SizeGapTo(inNumItems + fExtraCount);
93 
94 	// Finally, read the data and put it into the buffer
95 	if (file->ReadAt(fileOffset, fBuffer + fGapIndex, inNumItems) > 0) {
96 		fGapCount -= inNumItems;
97 		fGapIndex += inNumItems;
98 		fItemCount += inNumItems;
99 	}
100 }
101 
102 
103 void
104 _BTextGapBuffer_::RemoveRange(int32 start, int32 end)
105 {
106 	long inAtIndex = start;
107 	long inNumItems = end - start;
108 
109 	if (inNumItems < 1)
110 		return;
111 
112 	inAtIndex = (inAtIndex > fItemCount - 1) ? (fItemCount - 1) : inAtIndex;
113 	inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex;
114 
115 	MoveGapTo(inAtIndex);
116 
117 	fGapCount += inNumItems;
118 	fItemCount -= inNumItems;
119 
120 	if (fGapCount > fExtraCount)
121 		SizeGapTo(fExtraCount);
122 }
123 
124 
125 void
126 _BTextGapBuffer_::MoveGapTo(int32 toIndex)
127 {
128 	if (toIndex == fGapIndex)
129 		return;
130 
131 	long gapEndIndex = fGapIndex + fGapCount;
132 	long srcIndex = 0;
133 	long dstIndex = 0;
134 	long count = 0;
135 	if (toIndex > fGapIndex) {
136 		long trailGapCount = fBufferCount - gapEndIndex;
137 		srcIndex = toIndex + (gapEndIndex - toIndex);
138 		dstIndex =  fGapIndex;
139 		count = fGapCount + (toIndex - srcIndex);
140 		count = (count > trailGapCount) ? trailGapCount : count;
141 	} else {
142 		srcIndex = toIndex;
143 		dstIndex = toIndex + (gapEndIndex - fGapIndex);
144 		count = gapEndIndex - dstIndex;
145 	}
146 
147 	if (count > 0)
148 		memmove(fBuffer + dstIndex, fBuffer + srcIndex, count);
149 
150 	fGapIndex = toIndex;
151 }
152 
153 
154 void
155 _BTextGapBuffer_::SizeGapTo(long inCount)
156 {
157 	if (inCount == fGapCount)
158 		return;
159 
160 	fBuffer = (char *)realloc(fBuffer, fItemCount + inCount);
161 	memmove(fBuffer + fGapIndex + inCount,
162 			fBuffer + fGapIndex + fGapCount,
163 			fBufferCount - (fGapIndex + fGapCount));
164 
165 	fGapCount = inCount;
166 	fBufferCount = fItemCount + fGapCount;
167 }
168 
169 
170 const char *
171 _BTextGapBuffer_::GetString(int32 fromOffset, int32 *_numBytes)
172 {
173 	char *result = "";
174 	if (_numBytes == NULL)
175 		return result;
176 
177 	int32 numBytes = *_numBytes;
178 	if (numBytes < 1)
179 		return result;
180 
181 	bool isStartBeforeGap = (fromOffset < fGapIndex);
182 	bool isEndBeforeGap = ((fromOffset + numBytes - 1) < fGapIndex);
183 
184 	if (isStartBeforeGap == isEndBeforeGap) {
185 		result = fBuffer + fromOffset;
186 		if (!isStartBeforeGap)
187 			result += fGapCount;
188 
189 	} else {
190 		if (fScratchSize < numBytes) {
191 			fScratchBuffer = (char *)realloc(fScratchBuffer, numBytes);
192 			fScratchSize = numBytes;
193 		}
194 
195 		for (long i = 0; i < numBytes; i++)
196 			fScratchBuffer[i] = (*this)[fromOffset + i];
197 
198 		result = fScratchBuffer;
199 	}
200 
201 	// TODO: this could be improved. We are overwriting what we did some lines ago,
202 	// we could just avoid to do that.
203 	if (fPasswordMode) {
204 		uint32 numChars = UTF8CountChars(result, numBytes);
205 		uint32 charLen = UTF8CountBytes(B_UTF8_BULLET, 1);
206 		uint32 newSize = numChars * charLen + 1;
207 		if ((uint32)fScratchSize < newSize) {
208 			fScratchBuffer = (char *)realloc(fScratchBuffer, newSize);
209 			fScratchSize = newSize;
210 		}
211 		result = fScratchBuffer;
212 
213 		char *scratchPtr = result;
214 		for (uint32 i = 0; i < numChars; i++) {
215 			memcpy(scratchPtr, B_UTF8_BULLET, charLen);
216 			scratchPtr += charLen;
217 		}
218 		scratchPtr = '\0';
219 		*_numBytes = newSize - 1;
220 	}
221 
222 	return result;
223 }
224 
225 
226 bool
227 _BTextGapBuffer_::FindChar(char inChar, long fromIndex, long *ioDelta)
228 {
229 	long numChars = *ioDelta;
230 	for (long i = 0; i < numChars; i++) {
231 		if (((*this)[fromIndex + i] & 0xc0) == 0x80)
232 			continue;
233 		if ((*this)[fromIndex + i] == inChar) {
234 			*ioDelta = i;
235 			return true;
236 		}
237 	}
238 
239 	return false;
240 }
241 
242 
243 const char *
244 _BTextGapBuffer_::Text()
245 {
246 	MoveGapTo(fItemCount);
247 	fBuffer[fItemCount] = '\0';
248 
249 	return fBuffer;
250 }
251 
252 
253 /*char *
254 _BTextGapBuffer_::RealText()
255 {
256 	return fText;
257 }*/
258 
259 
260 void
261 _BTextGapBuffer_::GetString(int32 offset, int32 length, char *buffer)
262 {
263 	if (buffer == NULL)
264 		return;
265 
266 	int32 textLen = Length();
267 
268 	if (offset < 0 || offset > (textLen - 1) || length < 1) {
269 		buffer[0] = '\0';
270 		return;
271 	}
272 
273 	length = ((offset + length) > textLen) ? textLen - offset : length;
274 
275 	bool isStartBeforeGap = (offset < fGapIndex);
276 	bool isEndBeforeGap = ((offset + length - 1) < fGapIndex);
277 
278 	if (isStartBeforeGap == isEndBeforeGap) {
279 		char *source = fBuffer + offset;
280 		if (!isStartBeforeGap)
281 			source += fGapCount;
282 
283 		memcpy(buffer, source, length);
284 
285 	} else {
286 		// if we are here, it can only be that start is before gap,
287 		// and the end is after gap.
288 
289 		int32 beforeLen = fGapIndex - offset;
290 		int32 afterLen = length - beforeLen;
291 
292 		memcpy(buffer, fBuffer + offset, beforeLen);
293 		memcpy(buffer + beforeLen, fBuffer + fGapIndex, afterLen);
294 
295 	}
296 
297 	buffer[length] = '\0';
298 }
299 
300 
301 char
302 _BTextGapBuffer_::RealCharAt(int32 offset) const
303 {
304 	return (offset < fGapIndex) ? fBuffer[offset] : fBuffer[offset + fGapCount];
305 }
306 
307 
308 bool
309 _BTextGapBuffer_::PasswordMode() const
310 {
311 	return fPasswordMode;
312 }
313 
314 
315 void
316 _BTextGapBuffer_::SetPasswordMode(bool state)
317 {
318 	fPasswordMode = state;
319 }
320