1 /* 2 * Copyright 2002-2007, Haiku Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Wilber 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 /*! Utility functions for the Translation Kit */ 11 12 13 #include <Application.h> 14 #include <Bitmap.h> 15 #include <BitmapStream.h> 16 #include <CharacterSet.h> 17 #include <CharacterSetRoster.h> 18 #include <Entry.h> 19 #include <File.h> 20 #include <MenuItem.h> 21 #include <NodeInfo.h> 22 #include <Path.h> 23 #include <Resources.h> 24 #include <Roster.h> 25 #include <TextView.h> 26 #include <TranslationUtils.h> 27 #include <TranslatorFormats.h> 28 #include <TranslatorRoster.h> 29 #include <UTF8.h> 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 36 using namespace BPrivate; 37 38 39 BTranslationUtils::BTranslationUtils() 40 { 41 } 42 43 44 BTranslationUtils::~BTranslationUtils() 45 { 46 } 47 48 49 BTranslationUtils::BTranslationUtils(const BTranslationUtils &kUtils) 50 { 51 } 52 53 54 BTranslationUtils & 55 BTranslationUtils::operator=(const BTranslationUtils &kUtils) 56 { 57 return *this; 58 } 59 60 // --------------------------------------------------------------- 61 // GetBitmap 62 // 63 // Returns a BBitmap object for the bitmap file or resource 64 // kName. The user has to delete this object. It first tries 65 // to open kName as a file, then as a resource. 66 // 67 // Preconditions: 68 // 69 // Parameters: kName, the name of the bitmap file or resource to 70 // be returned 71 // roster, BTranslatorRoster used to do the translation 72 // 73 // Postconditions: 74 // 75 // Returns: NULL, if the file could not be opened and the 76 // resource couldn't be found or couldn't be 77 // translated to a BBitmap 78 // BBitmap * to the bitmap reference by kName 79 // --------------------------------------------------------------- 80 BBitmap * 81 BTranslationUtils::GetBitmap(const char *kName, BTranslatorRoster *roster) 82 { 83 BBitmap *pBitmap = GetBitmapFile(kName, roster); 84 // Try loading a bitmap from the file named name 85 86 // Try loading the bitmap as an application resource 87 if (pBitmap == NULL) 88 pBitmap = GetBitmap(B_TRANSLATOR_BITMAP, kName, roster); 89 90 return pBitmap; 91 } 92 93 // --------------------------------------------------------------- 94 // GetBitmap 95 // 96 // Returns a BBitmap object for the bitmap resource identified by 97 // the type type with the resource id, id. 98 // The user has to delete this object. 99 // 100 // Preconditions: 101 // 102 // Parameters: type, the type of resource to be loaded 103 // id, the id for the resource to be loaded 104 // roster, BTranslatorRoster used to do the translation 105 // 106 // Postconditions: 107 // 108 // Returns: NULL, if the resource couldn't be loaded or couldn't 109 // be translated to a BBitmap 110 // BBitmap * to the bitmap identified by type and id 111 // --------------------------------------------------------------- 112 BBitmap * 113 BTranslationUtils::GetBitmap(uint32 type, int32 id, BTranslatorRoster *roster) 114 { 115 BResources *pResources = BApplication::AppResources(); 116 // Remember: pResources must not be freed because 117 // it belongs to the application 118 if (pResources == NULL || pResources->HasResource(type, id) == false) 119 return NULL; 120 121 // Load the bitmap resource from the application file 122 // pRawData should be NULL if the resource is an 123 // unknown type or not available 124 size_t bitmapSize = 0; 125 const void *kpRawData = pResources->LoadResource(type, id, &bitmapSize); 126 if (kpRawData == NULL || bitmapSize == 0) 127 return NULL; 128 129 BMemoryIO memio(kpRawData, bitmapSize); 130 // Put the pointer to the raw image data into a BMemoryIO object 131 // so that it can be used with BTranslatorRoster->Translate() in 132 // the GetBitmap(BPositionIO *, BTranslatorRoster *) function 133 134 return GetBitmap(&memio, roster); 135 // Translate the data in memio using the BTranslatorRoster roster 136 } 137 138 // --------------------------------------------------------------- 139 // GetBitmap 140 // 141 // Returns a BBitmap object for the bitmap resource identified by 142 // the type type with the resource name, kName. 143 // The user has to delete this object. Note that a resource type 144 // and name does not uniquely identify a resource in a file. 145 // 146 // Preconditions: 147 // 148 // Parameters: type, the type of resource to be loaded 149 // kName, the name of the resource to be loaded 150 // roster, BTranslatorRoster used to do the translation 151 // 152 // Postconditions: 153 // 154 // Returns: NULL, if the resource couldn't be loaded or couldn't 155 // be translated to a BBitmap 156 // BBitmap * to the bitmap identified by type and kName 157 // --------------------------------------------------------------- 158 BBitmap * 159 BTranslationUtils::GetBitmap(uint32 type, const char *kName, 160 BTranslatorRoster *roster) 161 { 162 BResources *pResources = BApplication::AppResources(); 163 // Remember: pResources must not be freed because 164 // it belongs to the application 165 if (pResources == NULL || pResources->HasResource(type, kName) == false) 166 return NULL; 167 168 // Load the bitmap resource from the application file 169 size_t bitmapSize = 0; 170 const void *kpRawData = pResources->LoadResource(type, kName, &bitmapSize); 171 if (kpRawData == NULL || bitmapSize == 0) 172 return NULL; 173 174 BMemoryIO memio(kpRawData, bitmapSize); 175 // Put the pointer to the raw image data into a BMemoryIO object so 176 // that it can be used with BTranslatorRoster->Translate() 177 178 return GetBitmap(&memio, roster); 179 // Translate the data in memio using the BTranslatorRoster roster 180 } 181 182 // --------------------------------------------------------------- 183 // GetBitmapFile 184 // 185 // Returns a BBitmap object for the bitmap file named kName. 186 // The user has to delete this object. 187 // 188 // Preconditions: 189 // 190 // Parameters: kName, the name of the bitmap file 191 // roster, BTranslatorRoster used to do the translation 192 // 193 // Postconditions: 194 // 195 // Returns: NULL, if the file couldn't be opened or couldn't 196 // be translated to a BBitmap 197 // BBitmap * to the bitmap file named kName 198 // --------------------------------------------------------------- 199 BBitmap * 200 BTranslationUtils::GetBitmapFile(const char *kName, BTranslatorRoster *roster) 201 { 202 if (!be_app || !kName || kName[0] == '\0') 203 return NULL; 204 205 BPath path; 206 if (kName[0] != '/') { 207 // If kName is a relative path, use the path of the application's 208 // executable as the base for the relative path 209 app_info info; 210 if (be_app->GetAppInfo(&info) != B_OK) 211 return NULL; 212 BEntry appRef(&info.ref); 213 if (path.SetTo(&appRef) != B_OK) 214 return NULL; 215 if (path.GetParent(&path) != B_OK) 216 return NULL; 217 if (path.Append(kName) != B_OK) 218 return NULL; 219 220 } else if (path.SetTo(kName) != B_OK) 221 return NULL; 222 223 BFile bitmapFile(path.Path(), B_READ_ONLY); 224 if (bitmapFile.InitCheck() != B_OK) 225 return NULL; 226 227 return GetBitmap(&bitmapFile, roster); 228 // Translate the data in memio using the BTranslatorRoster roster 229 } 230 231 // --------------------------------------------------------------- 232 // GetBitmap 233 // 234 // Returns a BBitmap object for the bitmap file with the entry_ref 235 // kRef. The user has to delete this object. 236 // 237 // Preconditions: 238 // 239 // Parameters: kRef, the entry_ref for the bitmap file 240 // roster, BTranslatorRoster used to do the translation 241 // 242 // Postconditions: 243 // 244 // Returns: NULL, if the file couldn't be opened or couldn't 245 // be translated to a BBitmap 246 // BBitmap * to the bitmap file referenced by kRef 247 // --------------------------------------------------------------- 248 BBitmap * 249 BTranslationUtils::GetBitmap(const entry_ref *kRef, BTranslatorRoster *roster) 250 { 251 BFile bitmapFile(kRef, B_READ_ONLY); 252 if (bitmapFile.InitCheck() != B_OK) 253 return NULL; 254 255 return GetBitmap(&bitmapFile, roster); 256 // Translate the data in bitmapFile using the BTranslatorRoster roster 257 } 258 259 // --------------------------------------------------------------- 260 // GetBitmap 261 // 262 // Returns a BBitmap object from the BPositionIO *stream. The 263 // user must delete the returned object. This GetBitmap function 264 // is used by the other GetBitmap functions to do all of the 265 // "real" work. 266 // 267 // Preconditions: 268 // 269 // Parameters: stream, the stream with bitmap data in it 270 // roster, BTranslatorRoster used to do the translation 271 // 272 // Postconditions: 273 // 274 // Returns: NULL, if the stream couldn't be translated to a BBitmap 275 // BBitmap * for the bitmap data from pio if successful 276 // --------------------------------------------------------------- 277 BBitmap * 278 BTranslationUtils::GetBitmap(BPositionIO *stream, BTranslatorRoster *roster) 279 { 280 if (stream == NULL) 281 return NULL; 282 283 // Use default Translator if none is specified 284 if (roster == NULL) { 285 roster = BTranslatorRoster::Default(); 286 if (roster == NULL) 287 return NULL; 288 } 289 290 // Translate the file from whatever format it is in the file 291 // to the type format so that it can be stored in a BBitmap 292 BBitmapStream bitmapStream; 293 if (roster->Translate(stream, NULL, NULL, &bitmapStream, 294 B_TRANSLATOR_BITMAP) < B_OK) 295 return NULL; 296 297 // Detach the BBitmap from the BBitmapStream so the user 298 // of this function can do what they please with it. 299 BBitmap *pBitmap = NULL; 300 if (bitmapStream.DetachBitmap(&pBitmap) == B_NO_ERROR) 301 return pBitmap; 302 else 303 return NULL; 304 } 305 306 307 /*! 308 This function translates the styled text in fromStream and 309 inserts it at the end of the text in intoView, using the 310 BTranslatorRoster *roster to do the translation. The structs 311 that make it possible to work with the translated data are 312 defined in 313 /boot/develop/headers/be/translation/TranslatorFormats.h 314 315 \param source the stream with the styled text 316 \param intoView the view where the test will be inserted 317 roster, BTranslatorRoster used to do the translation 318 \param the encoding to use, defaults to UTF-8 319 320 \return B_BAD_VALUE, if fromStream or intoView is NULL 321 \return B_ERROR, if any other error occurred 322 \return B_OK, if successful 323 */ 324 status_t 325 BTranslationUtils::GetStyledText(BPositionIO* source, BTextView* intoView, 326 const char* encoding, BTranslatorRoster* roster) 327 { 328 if (source == NULL || intoView == NULL) 329 return B_BAD_VALUE; 330 331 // Use default Translator if none is specified 332 if (roster == NULL) { 333 roster = BTranslatorRoster::Default(); 334 if (roster == NULL) 335 return B_ERROR; 336 } 337 338 BMessage config; 339 if (encoding != NULL && encoding[0]) 340 config.AddString("be:encoding", encoding); 341 342 // Translate the file from whatever format it is to B_STYLED_TEXT_FORMAT 343 // we understand 344 BMallocIO mallocIO; 345 if (roster->Translate(source, NULL, &config, &mallocIO, 346 B_STYLED_TEXT_FORMAT) < B_OK) 347 return B_BAD_TYPE; 348 349 const uint8* buffer = (const uint8*)mallocIO.Buffer(); 350 351 // make sure there is enough data to fill the stream header 352 const size_t kStreamHeaderSize = sizeof(TranslatorStyledTextStreamHeader); 353 if (mallocIO.BufferLength() < kStreamHeaderSize) 354 return B_BAD_DATA; 355 356 // copy the stream header from the mallio buffer 357 TranslatorStyledTextStreamHeader header = 358 *(reinterpret_cast<const TranslatorStyledTextStreamHeader *>(buffer)); 359 360 // convert the stm_header.header struct to the host format 361 const size_t kRecordHeaderSize = sizeof(TranslatorStyledTextRecordHeader); 362 swap_data(B_UINT32_TYPE, &header.header, kRecordHeaderSize, B_SWAP_BENDIAN_TO_HOST); 363 swap_data(B_INT32_TYPE, &header.version, sizeof(int32), B_SWAP_BENDIAN_TO_HOST); 364 365 if (header.header.magic != 'STXT') 366 return B_BAD_TYPE; 367 368 // copy the text header from the mallocIO buffer 369 370 uint32 offset = header.header.header_size + header.header.data_size; 371 const size_t kTextHeaderSize = sizeof(TranslatorStyledTextTextHeader); 372 if (mallocIO.BufferLength() < offset + kTextHeaderSize) 373 return B_BAD_DATA; 374 375 TranslatorStyledTextTextHeader textHeader = 376 *(const TranslatorStyledTextTextHeader *)(buffer + offset); 377 378 // convert the stm_header.header struct to the host format 379 swap_data(B_UINT32_TYPE, &textHeader.header, kRecordHeaderSize, B_SWAP_BENDIAN_TO_HOST); 380 swap_data(B_INT32_TYPE, &textHeader.charset, sizeof(int32), B_SWAP_BENDIAN_TO_HOST); 381 382 if (textHeader.header.magic != 'TEXT' || textHeader.charset != B_UNICODE_UTF8) 383 return B_BAD_TYPE; 384 385 offset += textHeader.header.header_size; 386 if (mallocIO.BufferLength() < offset + textHeader.header.data_size) { 387 // text buffer misses its end; handle this gracefully 388 textHeader.header.data_size = mallocIO.BufferLength() - offset; 389 } 390 391 const char* text = (const char*)buffer + offset; 392 // point text pointer at the actual character data 393 bool hasStyles = false; 394 395 if (mallocIO.BufferLength() > offset + textHeader.header.data_size) { 396 // If the stream contains information beyond the text data 397 // (which means that this data is probably styled text data) 398 399 offset += textHeader.header.data_size; 400 const size_t kStyleHeaderSize = 401 sizeof(TranslatorStyledTextStyleHeader); 402 if (mallocIO.BufferLength() >= offset + kStyleHeaderSize) { 403 TranslatorStyledTextStyleHeader styleHeader = 404 *(reinterpret_cast<const TranslatorStyledTextStyleHeader *>(buffer + offset)); 405 swap_data(B_UINT32_TYPE, &styleHeader.header, kRecordHeaderSize, B_SWAP_BENDIAN_TO_HOST); 406 swap_data(B_UINT32_TYPE, &styleHeader.apply_offset, sizeof(uint32), B_SWAP_BENDIAN_TO_HOST); 407 swap_data(B_UINT32_TYPE, &styleHeader.apply_length, sizeof(uint32), B_SWAP_BENDIAN_TO_HOST); 408 if (styleHeader.header.magic == 'STYL') { 409 offset += styleHeader.header.header_size; 410 if (mallocIO.BufferLength() >= offset + styleHeader.header.data_size) 411 hasStyles = true; 412 } 413 } 414 } 415 416 text_run_array *runArray = NULL; 417 if (hasStyles) 418 runArray = BTextView::UnflattenRunArray(buffer + offset); 419 420 if (runArray != NULL) { 421 intoView->Insert(intoView->TextLength(), 422 text, textHeader.header.data_size, runArray); 423 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 424 BTextView::FreeRunArray(runArray); 425 #else 426 free(runArray); 427 #endif 428 } else { 429 intoView->Insert(intoView->TextLength(), text, 430 textHeader.header.data_size); 431 } 432 433 return B_OK; 434 } 435 436 437 status_t 438 BTranslationUtils::GetStyledText(BPositionIO* source, BTextView* intoView, 439 BTranslatorRoster* roster) 440 { 441 return GetStyledText(source, intoView, NULL, roster); 442 } 443 444 445 /*! 446 This function takes styled text data from fromView and writes it to 447 intoStream. The plain text data and styled text data are combined 448 when they are written to intoStream. This is different than how 449 a save operation in StyledEdit works. With StyledEdit, it writes 450 plain text data to the file, but puts the styled text data in 451 the "styles" attribute. In other words, this function writes 452 styled text data to files in a manner that isn't human readable. 453 454 So, if you want to write styled text 455 data to a file, and you want it to behave the way StyledEdit does, 456 you want to use the BTranslationUtils::WriteStyledEditFile() function. 457 458 \param fromView, the view with the styled text in it 459 \param intoStream, the stream where the styled text is put 460 roster, not used 461 462 \return B_BAD_VALUE, if fromView or intoStream is NULL 463 \return B_ERROR, if anything else went wrong 464 \return B_NO_ERROR, if successful 465 */ 466 status_t 467 BTranslationUtils::PutStyledText(BTextView *fromView, BPositionIO *intoStream, 468 BTranslatorRoster *roster) 469 { 470 if (fromView == NULL || intoStream == NULL) 471 return B_BAD_VALUE; 472 473 int32 textLength = fromView->TextLength(); 474 if (textLength < 0) 475 return B_ERROR; 476 477 const char *pTextData = fromView->Text(); 478 // its OK if the result of fromView->Text() is NULL 479 480 int32 runArrayLength = 0; 481 text_run_array *runArray = fromView->RunArray(0, textLength, 482 &runArrayLength); 483 if (runArray == NULL) 484 return B_ERROR; 485 486 int32 flatRunArrayLength = 0; 487 void *pflatRunArray = 488 BTextView::FlattenRunArray(runArray, &flatRunArrayLength); 489 if (pflatRunArray == NULL) { 490 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 491 BTextView::FreeRunArray(runArray); 492 #else 493 free(runArray); 494 #endif 495 return B_ERROR; 496 } 497 498 // Rather than use a goto, I put a whole bunch of code that 499 // could error out inside of a loop, and break out of the loop 500 // if there is an error. 501 502 // This block of code is where I do all of the writing of the 503 // data to the stream. I've gathered all of the data that I 504 // need at this point. 505 bool ok = false; 506 while (ok) { 507 const size_t kStreamHeaderSize = 508 sizeof(TranslatorStyledTextStreamHeader); 509 TranslatorStyledTextStreamHeader stm_header; 510 stm_header.header.magic = 'STXT'; 511 stm_header.header.header_size = kStreamHeaderSize; 512 stm_header.header.data_size = 0; 513 stm_header.version = 100; 514 515 // convert the stm_header.header struct to the host format 516 const size_t kRecordHeaderSize = 517 sizeof(TranslatorStyledTextRecordHeader); 518 if (swap_data(B_UINT32_TYPE, &stm_header.header, kRecordHeaderSize, 519 B_SWAP_HOST_TO_BENDIAN) != B_OK) 520 break; 521 if (swap_data(B_INT32_TYPE, &stm_header.version, sizeof(int32), 522 B_SWAP_HOST_TO_BENDIAN) != B_OK) 523 break; 524 525 const size_t kTextHeaderSize = sizeof(TranslatorStyledTextTextHeader); 526 TranslatorStyledTextTextHeader txt_header; 527 txt_header.header.magic = 'TEXT'; 528 txt_header.header.header_size = kTextHeaderSize; 529 txt_header.header.data_size = textLength; 530 txt_header.charset = B_UNICODE_UTF8; 531 532 // convert the stm_header.header struct to the host format 533 if (swap_data(B_UINT32_TYPE, &txt_header.header, kRecordHeaderSize, 534 B_SWAP_HOST_TO_BENDIAN) != B_OK) 535 break; 536 if (swap_data(B_INT32_TYPE, &txt_header.charset, sizeof(int32), 537 B_SWAP_HOST_TO_BENDIAN) != B_OK) 538 break; 539 540 const size_t kStyleHeaderSize = 541 sizeof(TranslatorStyledTextStyleHeader); 542 TranslatorStyledTextStyleHeader stl_header; 543 stl_header.header.magic = 'STYL'; 544 stl_header.header.header_size = kStyleHeaderSize; 545 stl_header.header.data_size = flatRunArrayLength; 546 stl_header.apply_offset = 0; 547 stl_header.apply_length = textLength; 548 549 // convert the stl_header.header struct to the host format 550 if (swap_data(B_UINT32_TYPE, &stl_header.header, kRecordHeaderSize, 551 B_SWAP_HOST_TO_BENDIAN) != B_OK) 552 break; 553 if (swap_data(B_UINT32_TYPE, &stl_header.apply_offset, sizeof(uint32), 554 B_SWAP_HOST_TO_BENDIAN) != B_OK) 555 break; 556 if (swap_data(B_UINT32_TYPE, &stl_header.apply_length, sizeof(uint32), 557 B_SWAP_HOST_TO_BENDIAN) != B_OK) 558 break; 559 560 // Here, you can see the structure of the styled text data by 561 // observing the order that the various structs and data are 562 // written to the stream 563 ssize_t amountWritten = 0; 564 amountWritten = intoStream->Write(&stm_header, kStreamHeaderSize); 565 if ((size_t) amountWritten != kStreamHeaderSize) 566 break; 567 amountWritten = intoStream->Write(&txt_header, kTextHeaderSize); 568 if ((size_t) amountWritten != kTextHeaderSize) 569 break; 570 amountWritten = intoStream->Write(pTextData, textLength); 571 if (amountWritten != textLength) 572 break; 573 amountWritten = intoStream->Write(&stl_header, kStyleHeaderSize); 574 if ((size_t) amountWritten != kStyleHeaderSize) 575 break; 576 amountWritten = intoStream->Write(pflatRunArray, flatRunArrayLength); 577 if (amountWritten != flatRunArrayLength) 578 break; 579 580 ok = true; 581 // gracefully break out of the loop 582 } 583 584 free(pflatRunArray); 585 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 586 BTextView::FreeRunArray(runArray); 587 #else 588 free(runArray); 589 #endif 590 591 return ok ? B_OK : B_ERROR; 592 } 593 594 595 /*! 596 \brief Writes the styled text data from \a view to the specified \a file. 597 598 This function is similar to PutStyledText() except that it 599 only writes styled text data to files and it puts the 600 plain text data in the file and stores the styled data as 601 the attribute "styles". 602 603 You can use PutStyledText() to write styled text data 604 to files, but it writes the data in a format that isn't 605 human readable. 606 607 \param view the view with the styled text 608 \param file the file where the styled text is written to 609 \param the encoding to use, defaults to UTF-8 610 611 \return B_BAD_VALUE, if either parameter is NULL 612 B_OK, if successful, and any possible file error 613 if writing failed. 614 */ 615 status_t 616 BTranslationUtils::WriteStyledEditFile(BTextView* view, BFile* file, const char *encoding) 617 { 618 if (view == NULL || file == NULL) 619 return B_BAD_VALUE; 620 621 int32 textLength = view->TextLength(); 622 if (textLength < 0) 623 return B_ERROR; 624 625 const char *text = view->Text(); 626 if (text == NULL && textLength != 0) 627 return B_ERROR; 628 629 // move to the start of the file if not already there 630 status_t status = file->Seek(0, SEEK_SET); 631 if (status != B_OK) 632 return status; 633 634 const BCharacterSet* characterSet = NULL; 635 if (encoding != NULL && strcmp(encoding, "")) 636 characterSet = BCharacterSetRoster::FindCharacterSetByName(encoding); 637 if (characterSet == NULL) { 638 // default encoding - UTF-8 639 // Write plain text data to file 640 ssize_t bytesWritten = file->Write(text, textLength); 641 if (bytesWritten != textLength) { 642 if (bytesWritten < B_OK) 643 return bytesWritten; 644 645 return B_ERROR; 646 } 647 648 // be:encoding, defaults to UTF-8 (65535) 649 // Note that the B_UNICODE_UTF8 constant is 0 and for some reason 650 // not appropriate for use here. 651 int32 value = 65535; 652 file->WriteAttr("be:encoding", B_INT32_TYPE, 0, &value, sizeof(value)); 653 } else { 654 // we need to convert the text 655 uint32 id = characterSet->GetConversionID(); 656 const char* outText = view->Text(); 657 int32 sourceLength = textLength; 658 int32 state = 0; 659 660 textLength = 0; 661 662 do { 663 char buffer[32768]; 664 int32 length = sourceLength; 665 int32 bufferSize = sizeof(buffer); 666 status = convert_from_utf8(id, outText, &length, buffer, &bufferSize, &state); 667 if (status != B_OK) 668 return status; 669 670 ssize_t bytesWritten = file->Write(buffer, bufferSize); 671 if (bytesWritten < B_OK) 672 return bytesWritten; 673 674 sourceLength -= length; 675 textLength += bytesWritten; 676 outText += length; 677 } while (sourceLength > 0); 678 679 file->WriteAttr("be:encoding", B_STRING_TYPE, 0, 680 encoding, strlen(encoding)); 681 } 682 683 // truncate any extra text 684 status = file->SetSize(textLength); 685 if (status != B_OK) 686 return status; 687 688 // Write attributes. We don't report an error anymore after this point, 689 // as attributes aren't that crucial - not all volumes support attributes. 690 // However, if writing one attribute fails, no further attributes are 691 // tried to be written. 692 693 BNodeInfo info(file); 694 char type[B_MIME_TYPE_LENGTH]; 695 if (info.GetType(type) != B_OK) { 696 // This file doesn't have a file type yet, so let's set it 697 if (info.SetType("text/plain") < B_OK) 698 return B_OK; 699 } 700 701 // word wrap setting, turned on by default 702 int32 wordWrap = view->DoesWordWrap() ? 1 : 0; 703 ssize_t bytesWritten = file->WriteAttr("wrap", B_INT32_TYPE, 0, 704 &wordWrap, sizeof(int32)); 705 if (bytesWritten != sizeof(int32)) 706 return B_OK; 707 708 // alignment, default is B_ALIGN_LEFT 709 int32 alignment = view->Alignment(); 710 bytesWritten = file->WriteAttr("alignment", B_INT32_TYPE, 0, 711 &alignment, sizeof(int32)); 712 if (bytesWritten != sizeof(int32)) 713 return B_OK; 714 715 // Write text_run_array, ie. the styles of the text 716 717 text_run_array *runArray = view->RunArray(0, view->TextLength()); 718 if (runArray != NULL) { 719 int32 runArraySize = 0; 720 void *flattenedRunArray = BTextView::FlattenRunArray(runArray, &runArraySize); 721 if (flattenedRunArray != NULL) { 722 file->WriteAttr("styles", B_RAW_TYPE, 0, flattenedRunArray, 723 runArraySize); 724 } 725 726 free(flattenedRunArray); 727 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 728 BTextView::FreeRunArray(runArray); 729 #else 730 free(runArray); 731 #endif 732 } 733 734 return B_OK; 735 } 736 737 738 status_t 739 BTranslationUtils::WriteStyledEditFile(BTextView* view, BFile* file) 740 { 741 return WriteStyledEditFile(view, file, NULL); 742 } 743 744 745 /*! 746 Each translator can have default settings, set by the 747 "translations" control panel. You can read these settings to 748 pass on to a translator using one of these functions. 749 750 \param forTranslator, the translator the settings are for 751 roster, the roster used to get the settings 752 753 \return BMessage of configuration data for forTranslator - you own 754 this message and have to free it when you're done with it. 755 \return NULL, if anything went wrong 756 */ 757 BMessage * 758 BTranslationUtils::GetDefaultSettings(translator_id forTranslator, 759 BTranslatorRoster *roster) 760 { 761 // Use default Translator if none is specified 762 if (roster == NULL) { 763 roster = BTranslatorRoster::Default(); 764 if (roster == NULL) 765 return NULL; 766 } 767 768 BMessage *message = new BMessage(); 769 if (message == NULL) 770 return NULL; 771 772 status_t result = roster->GetConfigurationMessage(forTranslator, message); 773 if (result != B_OK && result != B_NO_TRANSLATOR) { 774 // Be's version seems to just pass an empty BMessage 775 // in case of B_NO_TRANSLATOR, well, in some cases anyway 776 delete message; 777 return NULL; 778 } 779 780 return message; 781 } 782 783 784 // --------------------------------------------------------------- 785 // GetDefaultSettings 786 // 787 // Attempts to find the translator settings for 788 // the translator named kTranslatorName with a version of 789 // translatorVersion. 790 // 791 // Preconditions: 792 // 793 // Parameters: kTranslatorName, the name of the translator 794 // the settings are for 795 // translatorVersion, the version of the translator 796 // to retrieve 797 // 798 // Postconditions: 799 // 800 // Returns: NULL, if anything went wrong 801 // BMessage * of configuration data for kTranslatorName 802 // --------------------------------------------------------------- 803 BMessage * 804 BTranslationUtils::GetDefaultSettings(const char *kTranslatorName, 805 int32 translatorVersion) 806 { 807 BTranslatorRoster *roster = BTranslatorRoster::Default(); 808 translator_id *translators = NULL; 809 int32 numTranslators = 0; 810 if (roster == NULL 811 || roster->GetAllTranslators(&translators, &numTranslators) != B_OK) 812 return NULL; 813 814 // Cycle through all of the default translators 815 // looking for a translator that matches the name and version 816 // that I was given 817 BMessage *pMessage = NULL; 818 const char *currentTranName = NULL, *currentTranInfo = NULL; 819 int32 currentTranVersion = 0; 820 for (int i = 0; i < numTranslators; i++) { 821 822 if (roster->GetTranslatorInfo(translators[i], ¤tTranName, 823 ¤tTranInfo, ¤tTranVersion) == B_OK) { 824 825 if (currentTranVersion == translatorVersion 826 && strcmp(currentTranName, kTranslatorName) == 0) { 827 pMessage = GetDefaultSettings(translators[i], roster); 828 break; 829 } 830 } 831 } 832 833 delete[] translators; 834 return pMessage; 835 } 836 837 // --------------------------------------------------------------- 838 // AddTranslationItems 839 // 840 // Envious of that "Save As" menu in ShowImage? Well, you can have your own! 841 // AddTranslationItems will add menu items for all translations from the 842 // basic format you specify (B_TRANSLATOR_BITMAP, B_TRANSLATOR_TEXT etc). 843 // The translator ID and format constant chosen will be added to the message 844 // that is sent to you when the menu item is selected. 845 // 846 // The following code is a modified version of code 847 // written by Jon Watte from 848 // http://www.b500.com/bepage/TranslationKit2.html 849 // 850 // Preconditions: 851 // 852 // Parameters: intoMenu, the menu where the entries are created 853 // fromType, the type of translators to put on 854 // intoMenu 855 // kModel, the BMessage model for creating the menu 856 // if NULL, B_TRANSLATION_MENU is used 857 // kTranslationIdName, the name used for 858 // translator_id in the menuitem, 859 // if NULL, be:translator is used 860 // kTranslatorTypeName, the name used for 861 // output format id in the menuitem 862 // roster, BTranslatorRoster used to find translators 863 // if NULL, the default translators are used 864 // 865 // 866 // Postconditions: 867 // 868 // Returns: B_BAD_VALUE, if intoMenu is NULL 869 // B_OK, if successful 870 // error value if not successful 871 // --------------------------------------------------------------- 872 status_t 873 BTranslationUtils::AddTranslationItems(BMenu *intoMenu, uint32 fromType, 874 const BMessage *kModel, const char *kTranslatorIdName, 875 const char *kTranslatorTypeName, BTranslatorRoster *roster) 876 { 877 if (!intoMenu) 878 return B_BAD_VALUE; 879 880 if (!roster) 881 roster = BTranslatorRoster::Default(); 882 883 if (!kTranslatorIdName) 884 kTranslatorIdName = "be:translator"; 885 886 if (!kTranslatorTypeName) 887 kTranslatorTypeName = "be:type"; 888 889 translator_id * ids = NULL; 890 int32 count = 0; 891 status_t err = roster->GetAllTranslators(&ids, &count); 892 if (err < B_OK) 893 return err; 894 895 for (int tix = 0; tix < count; tix++) { 896 const translation_format *formats = NULL; 897 int32 numFormats = 0; 898 bool ok = false; 899 err = roster->GetInputFormats(ids[tix], &formats, &numFormats); 900 if (err == B_OK) { 901 for (int iix = 0; iix < numFormats; iix++) { 902 if (formats[iix].type == fromType) { 903 ok = true; 904 break; 905 } 906 } 907 } 908 if (!ok) 909 continue; 910 911 err = roster->GetOutputFormats(ids[tix], &formats, &numFormats); 912 if (err == B_OK) { 913 for (int oix = 0; oix < numFormats; oix++) { 914 if (formats[oix].type != fromType) { 915 BMessage *itemmsg; 916 if (kModel) 917 itemmsg = new BMessage(*kModel); 918 else 919 itemmsg = new BMessage(B_TRANSLATION_MENU); 920 itemmsg->AddInt32(kTranslatorIdName, ids[tix]); 921 itemmsg->AddInt32(kTranslatorTypeName, formats[oix].type); 922 intoMenu->AddItem( 923 new BMenuItem(formats[oix].name, itemmsg)); 924 } 925 } 926 } 927 } 928 929 delete[] ids; 930 return B_OK; 931 } 932