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