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