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