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