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 char realChar = RealCharAt(fromIndex + i); 234 if ((realChar & 0xc0) == 0x80) 235 continue; 236 if (realChar == inChar) { 237 *ioDelta = i; 238 return true; 239 } 240 } 241 242 return false; 243 } 244 245 246 const char * 247 _BTextGapBuffer_::Text() 248 { 249 const char *realText = RealText(); 250 251 if (fPasswordMode) { 252 const uint32 numChars = UTF8CountChars(realText, Length()); 253 const uint32 bulletCharLen = UTF8CountBytes(B_UTF8_BULLET, 1); 254 uint32 newSize = numChars * bulletCharLen + 1; 255 256 if ((uint32)fScratchSize < newSize) { 257 fScratchBuffer = (char *)realloc(fScratchBuffer, newSize); 258 fScratchSize = newSize; 259 } 260 261 char *scratchPtr = fScratchBuffer; 262 for (uint32 i = 0; i < numChars; i++) { 263 memcpy(scratchPtr, B_UTF8_BULLET, bulletCharLen); 264 scratchPtr += bulletCharLen; 265 } 266 scratchPtr = '\0'; 267 268 return fScratchBuffer; 269 } 270 271 return realText; 272 } 273 274 275 const char * 276 _BTextGapBuffer_::RealText() 277 { 278 MoveGapTo(fItemCount); 279 fBuffer[fItemCount] = '\0'; 280 281 return fBuffer; 282 } 283 284 285 void 286 _BTextGapBuffer_::GetString(int32 offset, int32 length, char *buffer) 287 { 288 if (buffer == NULL) 289 return; 290 291 int32 textLen = Length(); 292 293 if (offset < 0 || offset > (textLen - 1) || length < 1) { 294 buffer[0] = '\0'; 295 return; 296 } 297 298 length = ((offset + length) > textLen) ? textLen - offset : length; 299 300 bool isStartBeforeGap = (offset < fGapIndex); 301 bool isEndBeforeGap = ((offset + length - 1) < fGapIndex); 302 303 if (isStartBeforeGap == isEndBeforeGap) { 304 char *source = fBuffer + offset; 305 if (!isStartBeforeGap) 306 source += fGapCount; 307 308 memcpy(buffer, source, length); 309 310 } else { 311 // if we are here, it can only be that start is before gap, 312 // and the end is after gap. 313 314 int32 beforeLen = fGapIndex - offset; 315 int32 afterLen = length - beforeLen; 316 317 memcpy(buffer, fBuffer + offset, beforeLen); 318 memcpy(buffer + beforeLen, fBuffer + fGapIndex, afterLen); 319 320 } 321 322 buffer[length] = '\0'; 323 } 324 325 326 bool 327 _BTextGapBuffer_::PasswordMode() const 328 { 329 return fPasswordMode; 330 } 331 332 333 void 334 _BTextGapBuffer_::SetPasswordMode(bool state) 335 { 336 fPasswordMode = state; 337 } 338