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