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