StyledEditView.cpp revision 2583a582
1/*
2 * Copyright 2002-2006, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Mattias Sundblad
7 *		Andrew Bachmann
8 */
9
10
11#include "Constants.h"
12#include "StyledEditView.h"
13
14#include <Message.h>
15#include <Messenger.h>
16#include <Rect.h>
17#include <Region.h>
18#include <TranslationUtils.h>
19#include <Node.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <CharacterSet.h>
23#include <CharacterSetRoster.h>
24#include <UTF8.h>
25
26using namespace BPrivate;
27
28
29StyledEditView::StyledEditView(BRect viewFrame, BRect textBounds, BHandler *handler)
30	: BTextView(viewFrame, "textview", textBounds,
31		B_FOLLOW_ALL, B_FRAME_EVENTS|B_WILL_DRAW)
32{
33	fHandler = handler;
34	fMessenger = new BMessenger(handler);
35	fSuppressChanges = false;
36	fEncoding = 0;
37}
38
39
40StyledEditView::~StyledEditView()
41{
42	delete fMessenger;
43}
44
45
46void
47StyledEditView::FrameResized(float width, float height)
48{
49	BTextView::FrameResized(width, height);
50
51	if (DoesWordWrap()) {
52		BRect textRect;
53		textRect = Bounds();
54		textRect.OffsetTo(B_ORIGIN);
55		textRect.InsetBy(TEXT_INSET, TEXT_INSET);
56		SetTextRect(textRect);
57	}
58
59/*	// I tried to do some sort of intelligent resize thing but it just doesn't work
60	// so we revert to the R5 stylededit yucky practice of setting the text rect to
61	// some crazy large number when word wrap is turned off :-(
62	 else if (textRect.Width() > TextRect().Width()) {
63		SetTextRect(textRect);
64	}
65
66	BRegion region;
67	GetTextRegion(0,TextLength(),&region);
68	float textWidth = region.Frame().Width();
69	if (textWidth < textRect.Width()) {
70		BRect textRect(B_ORIGIN,BPoint(textWidth+TEXT_INSET*2,Bounds().Height()));
71		textRect.InsetBy(TEXT_INSET,TEXT_INSET);
72		SetTextRect(textRect);
73	}
74	*/
75}
76
77
78status_t
79StyledEditView::GetStyledText(BPositionIO* stream)
80{
81	fSuppressChanges = true;
82	status_t result = BTranslationUtils::GetStyledText(stream, this, NULL);
83	fSuppressChanges = false;
84
85	if (result != B_OK)
86		return result;
87
88	BNode* node = dynamic_cast<BNode*>(stream);
89	if (node != NULL) {
90		ssize_t bytesRead;
91		// decode encoding
92		int32 encoding;
93		bytesRead = node->ReadAttr("be:encoding", 0, 0, &encoding, sizeof(encoding));
94		if (bytesRead == (ssize_t)sizeof(encoding)) {
95			if (encoding == 65535) {
96				// UTF-8
97				fEncoding = 0;
98			} else {
99				const BCharacterSet* characterSet
100					= BCharacterSetRoster::GetCharacterSetByConversionID(encoding);
101				if (characterSet != 0)
102					fEncoding = characterSet->GetFontID();
103			}
104		}
105
106		// restore alignment
107		int32 align;
108		bytesRead = node->ReadAttr("alignment", 0, 0, &align, sizeof(align));
109		if (bytesRead == (ssize_t)sizeof(align))
110			SetAlignment((alignment)align);
111
112		// restore wrapping
113		bool wrap;
114		bytesRead = node->ReadAttr("wrap", 0, 0, &wrap, sizeof(wrap));
115		if (bytesRead == (ssize_t)sizeof(wrap)) {
116			SetWordWrap(wrap);
117			if (wrap == false) {
118				BRect textRect;
119				textRect = Bounds();
120				textRect.OffsetTo(B_ORIGIN);
121				textRect.InsetBy(TEXT_INSET, TEXT_INSET);
122					// the width comes from stylededit R5. TODO: find a better way
123				textRect.SetRightBottom(BPoint(1500.0, textRect.RightBottom().y));
124				SetTextRect(textRect);
125			}
126		}
127	}
128
129	if (fEncoding != 0) {
130		int32 length = stream->Seek(0, SEEK_END);
131
132		// Here we save the run_array before it gets overwritten...
133		text_run_array* runArray = RunArray(0, length);
134		uint32 id = BCharacterSetRoster::GetCharacterSetByFontID(fEncoding)->GetConversionID();
135
136		fSuppressChanges = true;
137		SetText("");
138		fSuppressChanges = false;
139
140		char inBuffer[32768];
141		off_t location = 0;
142		int32 textOffset = 0;
143		int32 state = 0;
144		int32 bytesRead;
145		while ((bytesRead = stream->ReadAt(location, inBuffer, sizeof(inBuffer))) > 0) {
146			char* inPtr = inBuffer;
147			char textBuffer[32768];
148			int32 textLength = sizeof(textBuffer);
149			int32 bytes = bytesRead;
150			while (textLength > 0 && bytes > 0) {
151				result = convert_to_utf8(id, inPtr, &bytes, textBuffer, &textLength, &state);
152				if (result != B_OK)
153					return result;
154
155				fSuppressChanges = true;
156				InsertText(textBuffer, textLength, textOffset);
157				fSuppressChanges = false;
158				textOffset += textLength;
159				inPtr += bytes;
160				location += bytes;
161				bytesRead -= bytes;
162				bytes = bytesRead;
163				if (textLength > 0)
164					textLength = sizeof(textBuffer);
165			}
166		}
167
168		// ... and here we restore it
169		SetRunArray(0, length, runArray);
170
171		#ifdef HAIKU_TARGET_PLATFORM_BEOS
172		// FreeRunArray does not exist on R5
173
174		// Call destructors explicitly
175		for (int32 i = 0; i < runArray->count; i++)
176			runArray->runs[i].font.~BFont();
177
178		free(runArray);
179
180		#else
181		FreeRunArray(runArray);
182		#endif
183
184	}
185
186	return result;
187}
188
189
190status_t
191StyledEditView::WriteStyledEditFile(BFile* file)
192{
193	status_t result = B_OK;
194	ssize_t bytes = 0;
195	result = BTranslationUtils::WriteStyledEditFile(this, file);
196	if (result != B_OK)
197		return result;
198
199	if (fEncoding == 0) {
200		int32 encoding = 65535;
201		bytes = file->WriteAttr("be:encoding", B_INT32_TYPE, 0, &encoding, sizeof(encoding));
202		if (bytes < 0)
203			return bytes;
204	} else {
205		result = file->SetSize(0);
206		if (result != B_OK)
207			return result;
208
209		bytes = file->Seek(0, SEEK_SET);
210		if (bytes != 0)
211			return bytes;
212
213		const BCharacterSet* cs = BCharacterSetRoster::GetCharacterSetByFontID(fEncoding);
214		if (cs != 0) {
215			uint32 id = cs->GetConversionID();
216			const char * outText = Text();
217			int32 sourceLength = TextLength();
218			int32 state = 0;
219			char buffer[32768];
220			while (sourceLength > 0) {
221				int32 length = sourceLength;
222				int32 written = 32768;
223				result = convert_from_utf8(id,outText,&length,buffer,&written,&state);
224				if (result != B_OK) {
225					return result;
226				}
227				bytes = file->Write(buffer,written);
228				if (bytes < 0)
229					return bytes;
230				sourceLength -= length;
231				outText += length;
232			}
233			bytes = file->WriteAttr("be:encoding", B_INT32_TYPE, 0, &id, sizeof(id));
234			if (bytes < 0)
235				return bytes;
236		}
237	}
238
239	int32 align = Alignment();
240	bytes = file->WriteAttr("alignment", B_INT32_TYPE, 0, &align, sizeof(align));
241	if (bytes < 0)
242		return bytes;
243
244	bool wrap = DoesWordWrap();
245	bytes = file->WriteAttr("wrap", B_BOOL_TYPE, 0, &wrap, sizeof(wrap));
246	if (bytes < 0)
247		return bytes;
248
249	return result;
250}
251
252
253void
254StyledEditView::Reset()
255{
256	fSuppressChanges = true;
257	SetText("");
258	fSuppressChanges = false;
259}
260
261
262void
263StyledEditView::Select(int32 start, int32 finish)
264{
265	fChangeMessage = new BMessage(start == finish ? DISABLE_ITEMS : ENABLE_ITEMS);
266	fMessenger->SendMessage(fChangeMessage);
267
268	BTextView::Select(start, finish);
269}
270
271
272void
273StyledEditView::SetEncoding(uint32 encoding)
274{
275	fEncoding = encoding;
276}
277
278
279uint32
280StyledEditView::GetEncoding() const
281{
282	return fEncoding;
283}
284
285
286void
287StyledEditView::InsertText(const char *text, int32 length, int32 offset,
288	const text_run_array *runs)
289{
290	if (!fSuppressChanges)
291		fMessenger->SendMessage(new BMessage(TEXT_CHANGED));
292
293	BTextView::InsertText(text, length, offset, runs);
294}
295
296
297void
298StyledEditView::DeleteText(int32 start, int32 finish)
299{
300	if (!fSuppressChanges)
301		fMessenger-> SendMessage(new BMessage(TEXT_CHANGED));
302
303	BTextView::DeleteText(start, finish);
304}
305
306