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