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