xref: /haiku/src/kits/translation/TranslationUtils.cpp (revision 3cb015b1ee509d69c643506e8ff573808c86dcfc)
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 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
417 			BTextView::FreeRunArray(runArray);
418 #else
419 			free(runArray);
420 #endif
421 		} else {
422 			// run array seems to be garbled; the text alone must be enough
423 			intoView->Insert(intoView->TextLength(), text,
424 				textHeader.header.data_size);
425 		}
426 	} else {
427 		intoView->Insert(intoView->TextLength(), text,
428 			textHeader.header.data_size);
429 	}
430 
431 	return B_OK;
432 }
433 
434 
435 /*!
436 	This function takes styled text data from fromView and writes it to
437 	intoStream.  The plain text data and styled text data are combined
438 	when they are written to intoStream.  This is different than how
439 	a save operation in StyledEdit works.  With StyledEdit, it writes
440 	plain text data to the file, but puts the styled text data in
441 	the "styles" attribute.  In other words, this function writes
442 	styled text data to files in a manner that isn't human readable.
443 
444 	So, if you want to write styled text
445 	data to a file, and you want it to behave the way StyledEdit does,
446 	you want to use the BTranslationUtils::WriteStyledEditFile() function.
447 
448 	\param fromView, the view with the styled text in it
449 	\param intoStream, the stream where the styled text is put
450 		roster, not used
451 
452 	\return B_BAD_VALUE, if fromView or intoStream is NULL
453 	\return B_ERROR, if anything else went wrong
454 	\return B_NO_ERROR, if successful
455 */
456 status_t
457 BTranslationUtils::PutStyledText(BTextView *fromView, BPositionIO *intoStream,
458 	BTranslatorRoster *roster)
459 {
460 	if (fromView == NULL || intoStream == NULL)
461 		return B_BAD_VALUE;
462 
463 	int32 textLength = fromView->TextLength();
464 	if (textLength < 0)
465 		return B_ERROR;
466 
467 	const char *pTextData = fromView->Text();
468 		// its OK if the result of fromView->Text() is NULL
469 
470 	int32 runArrayLength = 0;
471 	text_run_array *runArray = fromView->RunArray(0, textLength,
472 		&runArrayLength);
473 	if (runArray == NULL)
474 		return B_ERROR;
475 
476 	int32 flatRunArrayLength = 0;
477 	void *pflatRunArray =
478 		BTextView::FlattenRunArray(runArray, &flatRunArrayLength);
479 	if (pflatRunArray == NULL) {
480 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
481 		BTextView::FreeRunArray(runArray);
482 #else
483 		free(runArray);
484 #endif
485 		return B_ERROR;
486 	}
487 
488 	// Rather than use a goto, I put a whole bunch of code that
489 	// could error out inside of a loop, and break out of the loop
490 	// if there is an error.
491 
492 	// This block of code is where I do all of the writing of the
493 	// data to the stream.  I've gathered all of the data that I
494 	// need at this point.
495 	bool ok = false;
496 	while (ok) {
497 		const size_t kStreamHeaderSize =
498 			sizeof(TranslatorStyledTextStreamHeader);
499 		TranslatorStyledTextStreamHeader stm_header;
500 		stm_header.header.magic = 'STXT';
501 		stm_header.header.header_size = kStreamHeaderSize;
502 		stm_header.header.data_size = 0;
503 		stm_header.version = 100;
504 
505 		// convert the stm_header.header struct to the host format
506 		const size_t kRecordHeaderSize =
507 			sizeof(TranslatorStyledTextRecordHeader);
508 		if (swap_data(B_UINT32_TYPE, &stm_header.header, kRecordHeaderSize,
509 			B_SWAP_HOST_TO_BENDIAN) != B_OK)
510 			break;
511 		if (swap_data(B_INT32_TYPE, &stm_header.version, sizeof(int32),
512 			B_SWAP_HOST_TO_BENDIAN) != B_OK)
513 			break;
514 
515 		const size_t kTextHeaderSize = sizeof(TranslatorStyledTextTextHeader);
516 		TranslatorStyledTextTextHeader txt_header;
517 		txt_header.header.magic = 'TEXT';
518 		txt_header.header.header_size = kTextHeaderSize;
519 		txt_header.header.data_size = textLength;
520 		txt_header.charset = B_UNICODE_UTF8;
521 
522 		// convert the stm_header.header struct to the host format
523 		if (swap_data(B_UINT32_TYPE, &txt_header.header, kRecordHeaderSize,
524 			B_SWAP_HOST_TO_BENDIAN) != B_OK)
525 			break;
526 		if (swap_data(B_INT32_TYPE, &txt_header.charset, sizeof(int32),
527 			B_SWAP_HOST_TO_BENDIAN) != B_OK)
528 			break;
529 
530 		const size_t kStyleHeaderSize =
531 			sizeof(TranslatorStyledTextStyleHeader);
532 		TranslatorStyledTextStyleHeader stl_header;
533 		stl_header.header.magic = 'STYL';
534 		stl_header.header.header_size = kStyleHeaderSize;
535 		stl_header.header.data_size = flatRunArrayLength;
536 		stl_header.apply_offset = 0;
537 		stl_header.apply_length = textLength;
538 
539 		// convert the stl_header.header struct to the host format
540 		if (swap_data(B_UINT32_TYPE, &stl_header.header, kRecordHeaderSize,
541 			B_SWAP_HOST_TO_BENDIAN) != B_OK)
542 			break;
543 		if (swap_data(B_UINT32_TYPE, &stl_header.apply_offset, sizeof(uint32),
544 			B_SWAP_HOST_TO_BENDIAN) != B_OK)
545 			break;
546 		if (swap_data(B_UINT32_TYPE, &stl_header.apply_length, sizeof(uint32),
547 			B_SWAP_HOST_TO_BENDIAN) != B_OK)
548 			break;
549 
550 		// Here, you can see the structure of the styled text data by
551 		// observing the order that the various structs and data are
552 		// written to the stream
553 		ssize_t amountWritten = 0;
554 		amountWritten = intoStream->Write(&stm_header, kStreamHeaderSize);
555 		if ((size_t) amountWritten != kStreamHeaderSize)
556 			break;
557 		amountWritten = intoStream->Write(&txt_header, kTextHeaderSize);
558 		if ((size_t) amountWritten != kTextHeaderSize)
559 			break;
560 		amountWritten = intoStream->Write(pTextData, textLength);
561 		if (amountWritten != textLength)
562 			break;
563 		amountWritten = intoStream->Write(&stl_header, kStyleHeaderSize);
564 		if ((size_t) amountWritten != kStyleHeaderSize)
565 			break;
566 		amountWritten = intoStream->Write(pflatRunArray, flatRunArrayLength);
567 		if (amountWritten != flatRunArrayLength)
568 			break;
569 
570 		ok = true;
571 			// gracefully break out of the loop
572 	}
573 
574 	free(pflatRunArray);
575 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
576 	BTextView::FreeRunArray(runArray);
577 #else
578 	free(runArray);
579 #endif
580 
581 	return ok ? B_OK : B_ERROR;
582 }
583 
584 
585 /*!
586 	\brief Writes the styled text data from \a view to the specified \a file.
587 
588 	This function is similar to PutStyledText() except that it
589 	only writes styled text data to files and it puts the
590 	plain text data in the file and stores the styled data as
591 	the attribute "styles".
592 
593 	You can use PutStyledText() to write styled text data
594 	to files, but it writes the data in a format that isn't
595 	human readable.
596 
597 	\param view the view with the styled text
598 	\param file the file where the styled text is written to
599 
600 	\return B_BAD_VALUE, if either parameter is NULL
601 		B_OK, if successful, and any possible file error
602 		if writing failed.
603 */
604 status_t
605 BTranslationUtils::WriteStyledEditFile(BTextView* view, BFile* file)
606 {
607 	if (view == NULL || file == NULL)
608 		return B_BAD_VALUE;
609 
610 	int32 textLength = view->TextLength();
611 	if (textLength < 0)
612 		return B_ERROR;
613 
614 	const char *text = view->Text();
615 	if (text == NULL && textLength != 0)
616 		return B_ERROR;
617 
618 	// move to the start of the file if not already there
619 	status_t result = file->Seek(0, SEEK_SET);
620 	if (result != B_OK)
621 		return result;
622 
623 	// Write plain text data to file
624 	ssize_t bytesWritten = file->Write(text, textLength);
625 	if (bytesWritten != textLength)
626 		return B_ERROR;
627 
628 	// truncate any extra text
629 	result = file->SetSize(textLength);
630 	if (result != B_OK)
631 		return result;
632 
633 	// Write attributes. We don't report an error anymore after this point,
634 	// as attributes aren't that crucial - not all volumes support attributes.
635 	// However, if writing one attribute fails, no further attributes are
636 	// tried to be written.
637 
638 	BNodeInfo info(file);
639 	char type[B_MIME_TYPE_LENGTH];
640 	if (info.GetType(type) != B_OK) {
641 		// This file doesn't have a file type yet, so let's set it
642 		result = info.SetType("text/plain");
643 		if (result < B_OK)
644 			return B_OK;
645 	}
646 
647 	// word wrap setting, turned on by default
648 	int32 wordWrap = view->DoesWordWrap() ? 1 : 0;
649 	bytesWritten = file->WriteAttr("wrap", B_INT32_TYPE, 0,
650 		&wordWrap, sizeof(int32));
651 	if (bytesWritten != sizeof(int32))
652 		return B_OK;
653 
654 	// alignment, default is B_ALIGN_LEFT
655 	int32 alignment = view->Alignment();
656 	bytesWritten = file->WriteAttr("alignment", B_INT32_TYPE, 0,
657 		&alignment, sizeof(int32));
658 	if (bytesWritten != sizeof(int32))
659 		return B_OK;
660 
661 	// be:encoding, defaults to UTF-8 (65535)
662 	// Note that the B_UNICODE_UTF8 constant is 0 and for some reason
663 	// not appropriate for use here.
664 	int32 encoding = 65535;
665 	bytesWritten = file->WriteAttr("be:encoding", B_INT32_TYPE, 0,
666 		&encoding, sizeof(int32));
667 	if (bytesWritten != sizeof(int32))
668 		return B_OK;
669 
670 	// Write text_run_array, ie. the styles of the text
671 
672 	text_run_array *runArray = view->RunArray(0, view->TextLength());
673 	if (runArray != NULL) {
674 		int32 runArraySize = 0;
675 		void *flattenedRunArray = BTextView::FlattenRunArray(runArray, &runArraySize);
676 		if (flattenedRunArray != NULL) {
677 			file->WriteAttr("styles", B_RAW_TYPE, 0, flattenedRunArray,
678 				runArraySize);
679 		}
680 
681 		free(flattenedRunArray);
682 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
683 		BTextView::FreeRunArray(runArray);
684 #else
685 		free(runArray);
686 #endif
687 	}
688 
689 	return B_OK;
690 }
691 
692 
693 /*!
694 	Each translator can have default settings, set by the
695 	"translations" control panel. You can read these settings to
696 	pass on to a translator using one of these functions.
697 
698 	\param forTranslator, the translator the settings are for
699 		roster, the roster used to get the settings
700 
701 	\return BMessage of configuration data for forTranslator - you own
702 		this message and have to free it when you're done with it.
703 	\return NULL, if anything went wrong
704 */
705 BMessage *
706 BTranslationUtils::GetDefaultSettings(translator_id forTranslator,
707 	BTranslatorRoster *roster)
708 {
709 	// Use default Translator if none is specified
710 	if (roster == NULL) {
711 		roster = BTranslatorRoster::Default();
712 		if (roster == NULL)
713 			return NULL;
714 	}
715 
716 	BMessage *message = new BMessage();
717 	if (message == NULL)
718 		return NULL;
719 
720 	status_t result = roster->GetConfigurationMessage(forTranslator, message);
721 	if (result != B_OK && result != B_NO_TRANSLATOR) {
722 		// Be's version seems to just pass an empty BMessage
723 		// in case of B_NO_TRANSLATOR, well, in some cases anyway
724 		delete message;
725 		return NULL;
726 	}
727 
728 	return message;
729 }
730 
731 
732 // ---------------------------------------------------------------
733 // GetDefaultSettings
734 //
735 // Attempts to find the translator settings for
736 // the translator named kTranslatorName with a version of
737 // translatorVersion.
738 //
739 // Preconditions:
740 //
741 // Parameters: kTranslatorName, the name of the translator
742 //                              the settings are for
743 //             translatorVersion, the version of the translator
744 //                                to retrieve
745 //
746 // Postconditions:
747 //
748 // Returns: NULL, if anything went wrong
749 //          BMessage * of configuration data for kTranslatorName
750 // ---------------------------------------------------------------
751 BMessage *
752 BTranslationUtils::GetDefaultSettings(const char *kTranslatorName,
753 	int32 translatorVersion)
754 {
755 	BTranslatorRoster *roster = BTranslatorRoster::Default();
756 	translator_id *translators = NULL;
757 	int32 numTranslators = 0;
758 	if (roster == NULL
759 		|| roster->GetAllTranslators(&translators, &numTranslators) != B_OK)
760 		return NULL;
761 
762 	// Cycle through all of the default translators
763 	// looking for a translator that matches the name and version
764 	// that I was given
765 	BMessage *pMessage = NULL;
766 	const char *currentTranName = NULL, *currentTranInfo = NULL;
767 	int32 currentTranVersion = 0;
768 	for (int i = 0; i < numTranslators; i++) {
769 
770 		if (roster->GetTranslatorInfo(translators[i], &currentTranName,
771 			&currentTranInfo, &currentTranVersion) == B_OK) {
772 
773 			if (currentTranVersion == translatorVersion
774 				&& strcmp(currentTranName, kTranslatorName) == 0) {
775 				pMessage = GetDefaultSettings(translators[i], roster);
776 				break;
777 			}
778 		}
779 	}
780 
781 	delete[] translators;
782 	return pMessage;
783 }
784 
785 // ---------------------------------------------------------------
786 // AddTranslationItems
787 //
788 // Envious of that "Save As" menu in ShowImage? Well, you can have your own!
789 // AddTranslationItems will add menu items for all translations from the
790 // basic format you specify (B_TRANSLATOR_BITMAP, B_TRANSLATOR_TEXT etc).
791 // The translator ID and format constant chosen will be added to the message
792 // that is sent to you when the menu item is selected.
793 //
794 // The following code is a modified version of code
795 // written by Jon Watte from
796 // http://www.b500.com/bepage/TranslationKit2.html
797 //
798 // Preconditions:
799 //
800 // Parameters: intoMenu, the menu where the entries are created
801 //             fromType, the type of translators to put on
802 //                       intoMenu
803 //             kModel, the BMessage model for creating the menu
804 //                     if NULL, B_TRANSLATION_MENU is used
805 //             kTranslationIdName, the name used for
806 //                                 translator_id in the menuitem,
807 //                                 if NULL, be:translator is used
808 //             kTranslatorTypeName, the name used for
809 //                                  output format id in the menuitem
810 //             roster, BTranslatorRoster used to find translators
811 //                     if NULL, the default translators are used
812 //
813 //
814 // Postconditions:
815 //
816 // Returns: B_BAD_VALUE, if intoMenu is NULL
817 //          B_OK, if successful
818 //          error value if not successful
819 // ---------------------------------------------------------------
820 status_t
821 BTranslationUtils::AddTranslationItems(BMenu *intoMenu, uint32 fromType,
822 	const BMessage *kModel, const char *kTranslatorIdName,
823 	const char *kTranslatorTypeName, BTranslatorRoster *roster)
824 {
825 	if (!intoMenu)
826 		return B_BAD_VALUE;
827 
828 	if (!roster)
829 		roster = BTranslatorRoster::Default();
830 
831 	if (!kTranslatorIdName)
832 		kTranslatorIdName = "be:translator";
833 
834 	if (!kTranslatorTypeName)
835 		kTranslatorTypeName = "be:type";
836 
837 	translator_id * ids = NULL;
838 	int32 count = 0;
839 	status_t err = roster->GetAllTranslators(&ids, &count);
840 	if (err < B_OK)
841 		return err;
842 
843 	for (int tix = 0; tix < count; tix++) {
844 		const translation_format *formats = NULL;
845 		int32 numFormats = 0;
846 		bool ok = false;
847 		err = roster->GetInputFormats(ids[tix], &formats, &numFormats);
848 		if (err == B_OK) {
849 			for (int iix = 0; iix < numFormats; iix++) {
850 				if (formats[iix].type == fromType) {
851 					ok = true;
852 					break;
853 				}
854 			}
855 		}
856 		if (!ok)
857 			continue;
858 
859 		err = roster->GetOutputFormats(ids[tix], &formats, &numFormats);
860 		if (err == B_OK) {
861 			for (int oix = 0; oix < numFormats; oix++) {
862 				if (formats[oix].type != fromType) {
863 					BMessage *itemmsg;
864 					if (kModel)
865 						itemmsg = new BMessage(*kModel);
866 					else
867 						itemmsg = new BMessage(B_TRANSLATION_MENU);
868 					itemmsg->AddInt32(kTranslatorIdName, ids[tix]);
869 					itemmsg->AddInt32(kTranslatorTypeName, formats[oix].type);
870 					intoMenu->AddItem(
871 						new BMenuItem(formats[oix].name, itemmsg));
872 				}
873 			}
874 		}
875 	}
876 
877 	delete[] ids;
878 	return B_OK;
879 }
880