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
BTranslationUtils()42 BTranslationUtils::BTranslationUtils()
43 {
44 }
45
46
~BTranslationUtils()47 BTranslationUtils::~BTranslationUtils()
48 {
49 }
50
51
BTranslationUtils(const BTranslationUtils & kUtils)52 BTranslationUtils::BTranslationUtils(const BTranslationUtils &kUtils)
53 {
54 }
55
56
57 BTranslationUtils &
operator =(const BTranslationUtils & kUtils)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 *
GetBitmap(const char * kName,BTranslatorRoster * roster)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 *
GetBitmap(uint32 type,int32 id,BTranslatorRoster * roster)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 *
GetBitmap(uint32 type,const char * kName,BTranslatorRoster * roster)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 *
GetBitmapFile(const char * kName,BTranslatorRoster * roster)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 *
GetBitmap(const entry_ref * kRef,BTranslatorRoster * roster)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 *
GetBitmap(BPositionIO * stream,BTranslatorRoster * roster)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
GetStyledText(BPositionIO * source,BTextView * intoView,const char * encoding,BTranslatorRoster * roster)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
GetStyledText(BPositionIO * source,BTextView * intoView,BTranslatorRoster * roster)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
PutStyledText(BTextView * fromView,BPositionIO * intoStream,BTranslatorRoster * roster)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
WriteStyledEditFile(BTextView * view,BFile * file,const char * encoding)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
WriteStyledEditFile(BTextView * view,BFile * file)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 *
GetDefaultSettings(translator_id forTranslator,BTranslatorRoster * roster)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 *
GetDefaultSettings(const char * kTranslatorName,int32 translatorVersion)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
AddTranslationItems(BMenu * intoMenu,uint32 fromType,const BMessage * kModel,const char * kTranslatorIdName,const char * kTranslatorTypeName,BTranslatorRoster * roster)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*
_BuildTranslatorInfo(const translator_id id,const translation_format * format)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
_CompareTranslatorInfoByName(const translator_info * info1,const translator_info * info2)973 BTranslationUtils::_CompareTranslatorInfoByName(const translator_info* info1, const translator_info* info2)
974 {
975 return strcasecmp(info1->name, info2->name);
976 }
977