xref: /haiku/src/apps/haikudepot/textview/TextDocumentView.cpp (revision 73b4c7a6983d6120716989f379c52d474b4fb55f)
1d7f7bf2dSAxel Dörfler /*
281f55cc8SStephan Aßmus  * Copyright 2013-2015, Stephan Aßmus <superstippi@gmx.de>.
3d7f7bf2dSAxel Dörfler  * All rights reserved. Distributed under the terms of the MIT License.
4d7f7bf2dSAxel Dörfler  */
5d7f7bf2dSAxel Dörfler 
6d7f7bf2dSAxel Dörfler #include "TextDocumentView.h"
7d7f7bf2dSAxel Dörfler 
8d7f7bf2dSAxel Dörfler #include <algorithm>
9d7f7bf2dSAxel Dörfler #include <stdio.h>
10d7f7bf2dSAxel Dörfler 
11d7f7bf2dSAxel Dörfler #include <Clipboard.h>
12d7f7bf2dSAxel Dörfler #include <Cursor.h>
1381f55cc8SStephan Aßmus #include <MessageRunner.h>
14d7f7bf2dSAxel Dörfler #include <ScrollBar.h>
15d7f7bf2dSAxel Dörfler #include <Shape.h>
16d7f7bf2dSAxel Dörfler #include <Window.h>
17d7f7bf2dSAxel Dörfler 
18d7f7bf2dSAxel Dörfler 
19c4e439b5SAndrew Lindesay const char* kMimeTypePlainText = "text/plain";
20c4e439b5SAndrew Lindesay 
21c4e439b5SAndrew Lindesay 
2281f55cc8SStephan Aßmus enum {
2381f55cc8SStephan Aßmus 	MSG_BLINK_CARET		= 'blnk',
2481f55cc8SStephan Aßmus };
2581f55cc8SStephan Aßmus 
2681f55cc8SStephan Aßmus 
TextDocumentView(const char * name)27d7f7bf2dSAxel Dörfler TextDocumentView::TextDocumentView(const char* name)
28d7f7bf2dSAxel Dörfler 	:
2981f55cc8SStephan Aßmus 	BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
30*73b4c7a6SJohn Scipione 	fTextDocument(NULL),
31*73b4c7a6SJohn Scipione 	fTextEditor(NULL),
32d7f7bf2dSAxel Dörfler 	fInsetLeft(0.0f),
33d7f7bf2dSAxel Dörfler 	fInsetTop(0.0f),
34d7f7bf2dSAxel Dörfler 	fInsetRight(0.0f),
35d7f7bf2dSAxel Dörfler 	fInsetBottom(0.0f),
36d7f7bf2dSAxel Dörfler 
37d7f7bf2dSAxel Dörfler 	fCaretBounds(),
3881f55cc8SStephan Aßmus 	fCaretBlinker(NULL),
3981f55cc8SStephan Aßmus 	fCaretBlinkToken(0),
406e2ac177SStephan Aßmus 	fSelectionEnabled(true),
41*73b4c7a6SJohn Scipione 	fShowCaret(false)
42d7f7bf2dSAxel Dörfler {
43d7f7bf2dSAxel Dörfler 	fTextDocumentLayout.SetWidth(_TextLayoutWidth(Bounds().Width()));
44d7f7bf2dSAxel Dörfler 
45d7f7bf2dSAxel Dörfler 	// Set default TextEditor
46d7f7bf2dSAxel Dörfler 	SetTextEditor(TextEditorRef(new(std::nothrow) TextEditor(), true));
47d7f7bf2dSAxel Dörfler 
48fa19dd44Slooncraz 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
49fa19dd44Slooncraz 	SetLowUIColor(ViewUIColor());
50d7f7bf2dSAxel Dörfler }
51d7f7bf2dSAxel Dörfler 
52d7f7bf2dSAxel Dörfler 
~TextDocumentView()53d7f7bf2dSAxel Dörfler TextDocumentView::~TextDocumentView()
54d7f7bf2dSAxel Dörfler {
55d7f7bf2dSAxel Dörfler 	// Don't forget to remove listeners
56d7f7bf2dSAxel Dörfler 	SetTextEditor(TextEditorRef());
578d611f17SStephan Aßmus 	delete fCaretBlinker;
58d7f7bf2dSAxel Dörfler }
59d7f7bf2dSAxel Dörfler 
60d7f7bf2dSAxel Dörfler 
61d7f7bf2dSAxel Dörfler void
MessageReceived(BMessage * message)62d7f7bf2dSAxel Dörfler TextDocumentView::MessageReceived(BMessage* message)
63d7f7bf2dSAxel Dörfler {
64d7f7bf2dSAxel Dörfler 	switch (message->what) {
65d7f7bf2dSAxel Dörfler 		case B_COPY:
66d7f7bf2dSAxel Dörfler 			Copy(be_clipboard);
67d7f7bf2dSAxel Dörfler 			break;
68c4e439b5SAndrew Lindesay 		case B_PASTE:
69c4e439b5SAndrew Lindesay 			Paste(be_clipboard);
70c4e439b5SAndrew Lindesay 			break;
7196ebc1d6SStephan Aßmus 		case B_SELECT_ALL:
7296ebc1d6SStephan Aßmus 			SelectAll();
7396ebc1d6SStephan Aßmus 			break;
74d7f7bf2dSAxel Dörfler 
7581f55cc8SStephan Aßmus 		case MSG_BLINK_CARET:
7681f55cc8SStephan Aßmus 		{
7781f55cc8SStephan Aßmus 			int32 token;
7881f55cc8SStephan Aßmus 			if (message->FindInt32("token", &token) == B_OK
7981f55cc8SStephan Aßmus 				&& token == fCaretBlinkToken) {
8081f55cc8SStephan Aßmus 				_BlinkCaret();
8181f55cc8SStephan Aßmus 			}
8281f55cc8SStephan Aßmus 			break;
8381f55cc8SStephan Aßmus 		}
8481f55cc8SStephan Aßmus 
85d7f7bf2dSAxel Dörfler 		default:
86d7f7bf2dSAxel Dörfler 			BView::MessageReceived(message);
87d7f7bf2dSAxel Dörfler 	}
88d7f7bf2dSAxel Dörfler }
89d7f7bf2dSAxel Dörfler 
90d7f7bf2dSAxel Dörfler 
91d7f7bf2dSAxel Dörfler void
Draw(BRect updateRect)92d7f7bf2dSAxel Dörfler TextDocumentView::Draw(BRect updateRect)
93d7f7bf2dSAxel Dörfler {
94d7f7bf2dSAxel Dörfler 	FillRect(updateRect, B_SOLID_LOW);
95d7f7bf2dSAxel Dörfler 
96d7f7bf2dSAxel Dörfler 	fTextDocumentLayout.SetWidth(_TextLayoutWidth(Bounds().Width()));
97d7f7bf2dSAxel Dörfler 	fTextDocumentLayout.Draw(this, BPoint(fInsetLeft, fInsetTop), updateRect);
98d7f7bf2dSAxel Dörfler 
99779ab335SX512 	if (!fSelectionEnabled || !fTextEditor.IsSet())
100d7f7bf2dSAxel Dörfler 		return;
101d7f7bf2dSAxel Dörfler 
102d7f7bf2dSAxel Dörfler 	bool isCaret = fTextEditor->SelectionLength() == 0;
103d7f7bf2dSAxel Dörfler 
104d7f7bf2dSAxel Dörfler 	if (isCaret) {
105d7f7bf2dSAxel Dörfler 		if (fShowCaret && fTextEditor->IsEditingEnabled())
106d7f7bf2dSAxel Dörfler 			_DrawCaret(fTextEditor->CaretOffset());
107d7f7bf2dSAxel Dörfler 	} else {
108d7f7bf2dSAxel Dörfler 		_DrawSelection();
109d7f7bf2dSAxel Dörfler 	}
110d7f7bf2dSAxel Dörfler }
111d7f7bf2dSAxel Dörfler 
112d7f7bf2dSAxel Dörfler 
113d7f7bf2dSAxel Dörfler void
AttachedToWindow()114d7f7bf2dSAxel Dörfler TextDocumentView::AttachedToWindow()
115d7f7bf2dSAxel Dörfler {
116d7f7bf2dSAxel Dörfler 	_UpdateScrollBars();
117d7f7bf2dSAxel Dörfler }
118d7f7bf2dSAxel Dörfler 
119d7f7bf2dSAxel Dörfler 
120d7f7bf2dSAxel Dörfler void
FrameResized(float width,float height)121d7f7bf2dSAxel Dörfler TextDocumentView::FrameResized(float width, float height)
122d7f7bf2dSAxel Dörfler {
123d7f7bf2dSAxel Dörfler 	fTextDocumentLayout.SetWidth(width);
124d7f7bf2dSAxel Dörfler 	_UpdateScrollBars();
125d7f7bf2dSAxel Dörfler }
126d7f7bf2dSAxel Dörfler 
127d7f7bf2dSAxel Dörfler 
128d7f7bf2dSAxel Dörfler void
WindowActivated(bool active)129d7f7bf2dSAxel Dörfler TextDocumentView::WindowActivated(bool active)
130d7f7bf2dSAxel Dörfler {
131d7f7bf2dSAxel Dörfler 	Invalidate();
132d7f7bf2dSAxel Dörfler }
133d7f7bf2dSAxel Dörfler 
134d7f7bf2dSAxel Dörfler 
135d7f7bf2dSAxel Dörfler void
MakeFocus(bool focus)136d7f7bf2dSAxel Dörfler TextDocumentView::MakeFocus(bool focus)
137d7f7bf2dSAxel Dörfler {
138d7f7bf2dSAxel Dörfler 	if (focus != IsFocus())
139d7f7bf2dSAxel Dörfler 		Invalidate();
140d7f7bf2dSAxel Dörfler 	BView::MakeFocus(focus);
141d7f7bf2dSAxel Dörfler }
142d7f7bf2dSAxel Dörfler 
143d7f7bf2dSAxel Dörfler 
144d7f7bf2dSAxel Dörfler void
MouseDown(BPoint where)145d7f7bf2dSAxel Dörfler TextDocumentView::MouseDown(BPoint where)
146d7f7bf2dSAxel Dörfler {
147*73b4c7a6SJohn Scipione 	if (!fTextEditor.IsSet() || !fTextDocument.IsSet())
148*73b4c7a6SJohn Scipione 		return BView::MouseDown(where);
149*73b4c7a6SJohn Scipione 
1506af13813SPulkoMandy 	BMessage* currentMessage = NULL;
1516af13813SPulkoMandy 	if (Window() != NULL)
1526af13813SPulkoMandy 		currentMessage = Window()->CurrentMessage();
1536af13813SPulkoMandy 
1546af13813SPulkoMandy 	// First of all, check for links and other clickable things
1556af13813SPulkoMandy 	bool unused;
1566af13813SPulkoMandy 	int32 offset = fTextDocumentLayout.TextOffsetAt(where.x, where.y, unused);
1576af13813SPulkoMandy 	const BMessage* message = fTextDocument->ClickMessageAt(offset);
1586af13813SPulkoMandy 	if (message != NULL) {
1596af13813SPulkoMandy 		BMessage clickMessage(*message);
1606af13813SPulkoMandy 		clickMessage.Append(*currentMessage);
1616af13813SPulkoMandy 		Invoke(&clickMessage);
1626af13813SPulkoMandy 	}
1636af13813SPulkoMandy 
1646e2ac177SStephan Aßmus 	if (!fSelectionEnabled)
1656e2ac177SStephan Aßmus 		return;
1666e2ac177SStephan Aßmus 
167d7f7bf2dSAxel Dörfler 	MakeFocus();
168d7f7bf2dSAxel Dörfler 
169d7f7bf2dSAxel Dörfler 	int32 modifiers = 0;
1706af13813SPulkoMandy 	if (currentMessage != NULL)
1716af13813SPulkoMandy 		currentMessage->FindInt32("modifiers", &modifiers);
172d7f7bf2dSAxel Dörfler 
173d7f7bf2dSAxel Dörfler 	SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
174d7f7bf2dSAxel Dörfler 
175d7f7bf2dSAxel Dörfler 	bool extendSelection = (modifiers & B_SHIFT_KEY) != 0;
176d7f7bf2dSAxel Dörfler 	SetCaret(where, extendSelection);
177*73b4c7a6SJohn Scipione 
178*73b4c7a6SJohn Scipione 	BView::MouseDown(where);
179d7f7bf2dSAxel Dörfler }
180d7f7bf2dSAxel Dörfler 
181d7f7bf2dSAxel Dörfler 
182d7f7bf2dSAxel Dörfler void
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)183*73b4c7a6SJohn Scipione TextDocumentView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
184d7f7bf2dSAxel Dörfler {
185*73b4c7a6SJohn Scipione 	if (!fTextEditor.IsSet() || !fTextDocument.IsSet())
186*73b4c7a6SJohn Scipione 		return BView::MouseMoved(where, transit, dragMessage);
187d7f7bf2dSAxel Dörfler 
1886af13813SPulkoMandy 	BCursor cursor(B_CURSOR_ID_I_BEAM);
1896af13813SPulkoMandy 
1906af13813SPulkoMandy 	if (transit != B_EXITED_VIEW) {
1916af13813SPulkoMandy 		bool unused;
1926af13813SPulkoMandy 		int32 offset = fTextDocumentLayout.TextOffsetAt(where.x, where.y, unused);
1936af13813SPulkoMandy 		const BCursor& newCursor = fTextDocument->CursorAt(offset);
1946af13813SPulkoMandy 		if (newCursor.InitCheck() == B_OK) {
1956af13813SPulkoMandy 			cursor = newCursor;
1966af13813SPulkoMandy 			SetViewCursor(&cursor);
1976af13813SPulkoMandy 		}
1986af13813SPulkoMandy 	}
1996af13813SPulkoMandy 
2006e2ac177SStephan Aßmus 	if (!fSelectionEnabled)
2016e2ac177SStephan Aßmus 		return;
2026e2ac177SStephan Aßmus 
2036af13813SPulkoMandy 	SetViewCursor(&cursor);
204d7f7bf2dSAxel Dörfler 
205*73b4c7a6SJohn Scipione 	uint32 buttons = 0;
206*73b4c7a6SJohn Scipione 	if (Window() != NULL)
207*73b4c7a6SJohn Scipione 		Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
208*73b4c7a6SJohn Scipione 	if (buttons > 0)
209d7f7bf2dSAxel Dörfler 		SetCaret(where, true);
210*73b4c7a6SJohn Scipione 
211*73b4c7a6SJohn Scipione 	BView::MouseMoved(where, transit, dragMessage);
212d7f7bf2dSAxel Dörfler }
213d7f7bf2dSAxel Dörfler 
214d7f7bf2dSAxel Dörfler 
215d7f7bf2dSAxel Dörfler void
KeyDown(const char * bytes,int32 numBytes)216d7f7bf2dSAxel Dörfler TextDocumentView::KeyDown(const char* bytes, int32 numBytes)
217d7f7bf2dSAxel Dörfler {
218779ab335SX512 	if (!fTextEditor.IsSet())
219d7f7bf2dSAxel Dörfler 		return;
220d7f7bf2dSAxel Dörfler 
221d7f7bf2dSAxel Dörfler 	KeyEvent event;
222d7f7bf2dSAxel Dörfler 	event.bytes = bytes;
223d7f7bf2dSAxel Dörfler 	event.length = numBytes;
224d7f7bf2dSAxel Dörfler 	event.key = 0;
225d7f7bf2dSAxel Dörfler 	event.modifiers = modifiers();
226d7f7bf2dSAxel Dörfler 
227d7f7bf2dSAxel Dörfler 	if (Window() != NULL && Window()->CurrentMessage() != NULL) {
228d7f7bf2dSAxel Dörfler 		BMessage* message = Window()->CurrentMessage();
229d7f7bf2dSAxel Dörfler 		message->FindInt32("raw_char", &event.key);
230d7f7bf2dSAxel Dörfler 		message->FindInt32("modifiers", &event.modifiers);
231d7f7bf2dSAxel Dörfler 	}
232d7f7bf2dSAxel Dörfler 
2336a0e78e5SAndrew Lindesay 	float viewHeightPrior = fTextEditor->Layout()->Height();
2346a0e78e5SAndrew Lindesay 
235d7f7bf2dSAxel Dörfler 	fTextEditor->KeyDown(event);
23681f55cc8SStephan Aßmus 	_ShowCaret(true);
23781f55cc8SStephan Aßmus 	// TODO: It is necessary to invalidate all, since neither the caret bounds
23881f55cc8SStephan Aßmus 	// are updated in a way that would work here, nor is the text updated
2396a0e78e5SAndrew Lindesay 	// correctly which has been edited.
240d7f7bf2dSAxel Dörfler 	Invalidate();
2416a0e78e5SAndrew Lindesay 
2426a0e78e5SAndrew Lindesay 	if (fTextEditor->Layout()->Height() != viewHeightPrior)
2436a0e78e5SAndrew Lindesay 		_UpdateScrollBars();
244d7f7bf2dSAxel Dörfler }
245d7f7bf2dSAxel Dörfler 
246d7f7bf2dSAxel Dörfler 
247d7f7bf2dSAxel Dörfler void
KeyUp(const char * bytes,int32 numBytes)248d7f7bf2dSAxel Dörfler TextDocumentView::KeyUp(const char* bytes, int32 numBytes)
249d7f7bf2dSAxel Dörfler {
250d7f7bf2dSAxel Dörfler }
251d7f7bf2dSAxel Dörfler 
252d7f7bf2dSAxel Dörfler 
253d7f7bf2dSAxel Dörfler BSize
MinSize()254d7f7bf2dSAxel Dörfler TextDocumentView::MinSize()
255d7f7bf2dSAxel Dörfler {
256d7f7bf2dSAxel Dörfler 	return BSize(fInsetLeft + fInsetRight + 50.0f, fInsetTop + fInsetBottom);
257d7f7bf2dSAxel Dörfler }
258d7f7bf2dSAxel Dörfler 
259d7f7bf2dSAxel Dörfler 
260d7f7bf2dSAxel Dörfler BSize
MaxSize()261d7f7bf2dSAxel Dörfler TextDocumentView::MaxSize()
262d7f7bf2dSAxel Dörfler {
263d7f7bf2dSAxel Dörfler 	return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
264d7f7bf2dSAxel Dörfler }
265d7f7bf2dSAxel Dörfler 
266d7f7bf2dSAxel Dörfler 
267d7f7bf2dSAxel Dörfler BSize
PreferredSize()268d7f7bf2dSAxel Dörfler TextDocumentView::PreferredSize()
269d7f7bf2dSAxel Dörfler {
270d7f7bf2dSAxel Dörfler 	return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
271d7f7bf2dSAxel Dörfler }
272d7f7bf2dSAxel Dörfler 
273d7f7bf2dSAxel Dörfler 
274d7f7bf2dSAxel Dörfler bool
HasHeightForWidth()275d7f7bf2dSAxel Dörfler TextDocumentView::HasHeightForWidth()
276d7f7bf2dSAxel Dörfler {
277d7f7bf2dSAxel Dörfler 	return true;
278d7f7bf2dSAxel Dörfler }
279d7f7bf2dSAxel Dörfler 
280d7f7bf2dSAxel Dörfler 
281d7f7bf2dSAxel Dörfler void
GetHeightForWidth(float width,float * min,float * max,float * preferred)282d7f7bf2dSAxel Dörfler TextDocumentView::GetHeightForWidth(float width, float* min, float* max,
283d7f7bf2dSAxel Dörfler 	float* preferred)
284d7f7bf2dSAxel Dörfler {
285d7f7bf2dSAxel Dörfler 	TextDocumentLayout layout(fTextDocumentLayout);
286d7f7bf2dSAxel Dörfler 	layout.SetWidth(_TextLayoutWidth(width));
287d7f7bf2dSAxel Dörfler 
288d7f7bf2dSAxel Dörfler 	float height = layout.Height() + 1 + fInsetTop + fInsetBottom;
289d7f7bf2dSAxel Dörfler 
290d7f7bf2dSAxel Dörfler 	if (min != NULL)
291d7f7bf2dSAxel Dörfler 		*min = height;
292d7f7bf2dSAxel Dörfler 	if (max != NULL)
293d7f7bf2dSAxel Dörfler 		*max = height;
294d7f7bf2dSAxel Dörfler 	if (preferred != NULL)
295d7f7bf2dSAxel Dörfler 		*preferred = height;
296d7f7bf2dSAxel Dörfler }
297d7f7bf2dSAxel Dörfler 
298d7f7bf2dSAxel Dörfler 
299d7f7bf2dSAxel Dörfler // #pragma mark -
300d7f7bf2dSAxel Dörfler 
301d7f7bf2dSAxel Dörfler 
302d7f7bf2dSAxel Dörfler void
SetTextDocument(const TextDocumentRef & document)303d7f7bf2dSAxel Dörfler TextDocumentView::SetTextDocument(const TextDocumentRef& document)
304d7f7bf2dSAxel Dörfler {
305d7f7bf2dSAxel Dörfler 	fTextDocument = document;
306d7f7bf2dSAxel Dörfler 	fTextDocumentLayout.SetTextDocument(fTextDocument);
307779ab335SX512 	if (fTextEditor.IsSet())
308d7f7bf2dSAxel Dörfler 		fTextEditor->SetDocument(document);
309d7f7bf2dSAxel Dörfler 
310d7f7bf2dSAxel Dörfler 	InvalidateLayout();
311d7f7bf2dSAxel Dörfler 	Invalidate();
312d7f7bf2dSAxel Dörfler 	_UpdateScrollBars();
313d7f7bf2dSAxel Dörfler }
314d7f7bf2dSAxel Dörfler 
315d7f7bf2dSAxel Dörfler 
316d7f7bf2dSAxel Dörfler void
SetEditingEnabled(bool enabled)317d7f7bf2dSAxel Dörfler TextDocumentView::SetEditingEnabled(bool enabled)
318d7f7bf2dSAxel Dörfler {
319779ab335SX512 	if (fTextEditor.IsSet())
320d7f7bf2dSAxel Dörfler 		fTextEditor->SetEditingEnabled(enabled);
321d7f7bf2dSAxel Dörfler }
322d7f7bf2dSAxel Dörfler 
323d7f7bf2dSAxel Dörfler 
324d7f7bf2dSAxel Dörfler void
SetTextEditor(const TextEditorRef & editor)325d7f7bf2dSAxel Dörfler TextDocumentView::SetTextEditor(const TextEditorRef& editor)
326d7f7bf2dSAxel Dörfler {
327d7f7bf2dSAxel Dörfler 	if (fTextEditor == editor)
328d7f7bf2dSAxel Dörfler 		return;
329d7f7bf2dSAxel Dörfler 
330779ab335SX512 	if (fTextEditor.IsSet()) {
331d7f7bf2dSAxel Dörfler 		fTextEditor->SetDocument(TextDocumentRef());
332d7f7bf2dSAxel Dörfler 		fTextEditor->SetLayout(TextDocumentLayoutRef());
333d7f7bf2dSAxel Dörfler 		// TODO: Probably has to remove listeners
334d7f7bf2dSAxel Dörfler 	}
335d7f7bf2dSAxel Dörfler 
336d7f7bf2dSAxel Dörfler 	fTextEditor = editor;
337d7f7bf2dSAxel Dörfler 
338779ab335SX512 	if (fTextEditor.IsSet()) {
339d7f7bf2dSAxel Dörfler 		fTextEditor->SetDocument(fTextDocument);
340d7f7bf2dSAxel Dörfler 		fTextEditor->SetLayout(TextDocumentLayoutRef(
341d7f7bf2dSAxel Dörfler 			&fTextDocumentLayout));
342d7f7bf2dSAxel Dörfler 		// TODO: Probably has to add listeners
343d7f7bf2dSAxel Dörfler 	}
344d7f7bf2dSAxel Dörfler }
345d7f7bf2dSAxel Dörfler 
346d7f7bf2dSAxel Dörfler 
347d7f7bf2dSAxel Dörfler void
SetInsets(float inset)348d7f7bf2dSAxel Dörfler TextDocumentView::SetInsets(float inset)
349d7f7bf2dSAxel Dörfler {
350d7f7bf2dSAxel Dörfler 	SetInsets(inset, inset, inset, inset);
351d7f7bf2dSAxel Dörfler }
352d7f7bf2dSAxel Dörfler 
353d7f7bf2dSAxel Dörfler 
354d7f7bf2dSAxel Dörfler void
SetInsets(float horizontal,float vertical)355d7f7bf2dSAxel Dörfler TextDocumentView::SetInsets(float horizontal, float vertical)
356d7f7bf2dSAxel Dörfler {
357d7f7bf2dSAxel Dörfler 	SetInsets(horizontal, vertical, horizontal, vertical);
358d7f7bf2dSAxel Dörfler }
359d7f7bf2dSAxel Dörfler 
360d7f7bf2dSAxel Dörfler 
361d7f7bf2dSAxel Dörfler void
SetInsets(float left,float top,float right,float bottom)362d7f7bf2dSAxel Dörfler TextDocumentView::SetInsets(float left, float top, float right, float bottom)
363d7f7bf2dSAxel Dörfler {
364d7f7bf2dSAxel Dörfler 	if (fInsetLeft == left && fInsetTop == top
365d7f7bf2dSAxel Dörfler 		&& fInsetRight == right && fInsetBottom == bottom) {
366d7f7bf2dSAxel Dörfler 		return;
367d7f7bf2dSAxel Dörfler 	}
368d7f7bf2dSAxel Dörfler 
369d7f7bf2dSAxel Dörfler 	fInsetLeft = left;
370d7f7bf2dSAxel Dörfler 	fInsetTop = top;
371d7f7bf2dSAxel Dörfler 	fInsetRight = right;
372d7f7bf2dSAxel Dörfler 	fInsetBottom = bottom;
373d7f7bf2dSAxel Dörfler 
374d7f7bf2dSAxel Dörfler 	InvalidateLayout();
375d7f7bf2dSAxel Dörfler 	Invalidate();
376d7f7bf2dSAxel Dörfler }
377d7f7bf2dSAxel Dörfler 
378d7f7bf2dSAxel Dörfler 
379d7f7bf2dSAxel Dörfler void
SetSelectionEnabled(bool enabled)3806e2ac177SStephan Aßmus TextDocumentView::SetSelectionEnabled(bool enabled)
3816e2ac177SStephan Aßmus {
3826e2ac177SStephan Aßmus 	if (fSelectionEnabled == enabled)
3836e2ac177SStephan Aßmus 		return;
3846e2ac177SStephan Aßmus 	fSelectionEnabled = enabled;
3856e2ac177SStephan Aßmus 	Invalidate();
3866e2ac177SStephan Aßmus 	// TODO: Deselect
3876e2ac177SStephan Aßmus }
3886e2ac177SStephan Aßmus 
3896e2ac177SStephan Aßmus 
3906e2ac177SStephan Aßmus void
SetCaret(BPoint location,bool extendSelection)391d7f7bf2dSAxel Dörfler TextDocumentView::SetCaret(BPoint location, bool extendSelection)
392d7f7bf2dSAxel Dörfler {
393779ab335SX512 	if (!fSelectionEnabled || !fTextEditor.IsSet())
394d7f7bf2dSAxel Dörfler 		return;
395d7f7bf2dSAxel Dörfler 
396d7f7bf2dSAxel Dörfler 	location.x -= fInsetLeft;
397d7f7bf2dSAxel Dörfler 	location.y -= fInsetTop;
398d7f7bf2dSAxel Dörfler 
399d7f7bf2dSAxel Dörfler 	fTextEditor->SetCaret(location, extendSelection);
40081f55cc8SStephan Aßmus 	_ShowCaret(!extendSelection);
401d7f7bf2dSAxel Dörfler 	Invalidate();
402d7f7bf2dSAxel Dörfler }
403d7f7bf2dSAxel Dörfler 
404d7f7bf2dSAxel Dörfler 
40596ebc1d6SStephan Aßmus void
SelectAll()40696ebc1d6SStephan Aßmus TextDocumentView::SelectAll()
40796ebc1d6SStephan Aßmus {
408779ab335SX512 	if (!fSelectionEnabled || !fTextEditor.IsSet())
40996ebc1d6SStephan Aßmus 		return;
41096ebc1d6SStephan Aßmus 
41196ebc1d6SStephan Aßmus 	fTextEditor->SelectAll();
41281f55cc8SStephan Aßmus 	_ShowCaret(false);
4131fa37d5aSStephan Aßmus 	Invalidate();
41496ebc1d6SStephan Aßmus }
41596ebc1d6SStephan Aßmus 
41696ebc1d6SStephan Aßmus 
417d7f7bf2dSAxel Dörfler bool
HasSelection() const418d7f7bf2dSAxel Dörfler TextDocumentView::HasSelection() const
419d7f7bf2dSAxel Dörfler {
420779ab335SX512 	return fTextEditor.IsSet() && fTextEditor->HasSelection();
421d7f7bf2dSAxel Dörfler }
422d7f7bf2dSAxel Dörfler 
423d7f7bf2dSAxel Dörfler 
424d7f7bf2dSAxel Dörfler void
GetSelection(int32 & start,int32 & end) const425d7f7bf2dSAxel Dörfler TextDocumentView::GetSelection(int32& start, int32& end) const
426d7f7bf2dSAxel Dörfler {
427779ab335SX512 	if (fTextEditor.IsSet()) {
428d7f7bf2dSAxel Dörfler 		start = fTextEditor->SelectionStart();
429d7f7bf2dSAxel Dörfler 		end = fTextEditor->SelectionEnd();
430d7f7bf2dSAxel Dörfler 	}
431d7f7bf2dSAxel Dörfler }
432d7f7bf2dSAxel Dörfler 
433d7f7bf2dSAxel Dörfler 
434d7f7bf2dSAxel Dörfler void
Paste(BClipboard * clipboard)435c4e439b5SAndrew Lindesay TextDocumentView::Paste(BClipboard* clipboard)
436c4e439b5SAndrew Lindesay {
437c4e439b5SAndrew Lindesay 	if (!fTextDocument.IsSet() || !fTextEditor.IsSet())
438c4e439b5SAndrew Lindesay 		return;
439c4e439b5SAndrew Lindesay 
440c4e439b5SAndrew Lindesay 	if (!clipboard->Lock())
441c4e439b5SAndrew Lindesay 		return;
442c4e439b5SAndrew Lindesay 
443c4e439b5SAndrew Lindesay 	BMessage* clip = clipboard->Data();
444c4e439b5SAndrew Lindesay 
445c4e439b5SAndrew Lindesay 	if (clip != NULL) {
446c4e439b5SAndrew Lindesay 		const void* plainTextData;
447c4e439b5SAndrew Lindesay 		ssize_t plainTextDataSize;
448c4e439b5SAndrew Lindesay 
449c4e439b5SAndrew Lindesay 		if (clip->FindData(kMimeTypePlainText, B_MIME_TYPE, &plainTextData, &plainTextDataSize)
450c4e439b5SAndrew Lindesay 			== B_OK) {
451c4e439b5SAndrew Lindesay 
452c4e439b5SAndrew Lindesay 			if (plainTextDataSize > 0) {
453c4e439b5SAndrew Lindesay 				if (_PastePossiblyDisallowedChars(static_cast<const char*>(plainTextData),
454c4e439b5SAndrew Lindesay 					static_cast<int32>(plainTextDataSize)) != B_OK) {
455c4e439b5SAndrew Lindesay 					fprintf(stderr, "unable to paste text owing to internal error");
456c4e439b5SAndrew Lindesay 						// don't use HaikuDepot logging system as this is in the text engine
457c4e439b5SAndrew Lindesay 				}
458c4e439b5SAndrew Lindesay 			}
459c4e439b5SAndrew Lindesay 		}
460c4e439b5SAndrew Lindesay 	}
461c4e439b5SAndrew Lindesay 
462c4e439b5SAndrew Lindesay 	clipboard->Unlock();
463c4e439b5SAndrew Lindesay }
464c4e439b5SAndrew Lindesay 
465c4e439b5SAndrew Lindesay 
466c4e439b5SAndrew Lindesay /*!	This method will check that all of the characters in the provided
467c4e439b5SAndrew Lindesay 	string are allowed in the text document. Returns true if this is the case.
468c4e439b5SAndrew Lindesay */
469c4e439b5SAndrew Lindesay /*static*/ bool
_AreCharsAllowed(const char * str,int32 maxLength)470c4e439b5SAndrew Lindesay TextDocumentView::_AreCharsAllowed(const char* str, int32 maxLength)
471c4e439b5SAndrew Lindesay {
472c4e439b5SAndrew Lindesay 	for (int32 i = 0; str[i] != 0 && i < maxLength; i++) {
473c4e439b5SAndrew Lindesay 		if (!TextDocumentView::_IsAllowedChar(i))
474c4e439b5SAndrew Lindesay 			return false;
475c4e439b5SAndrew Lindesay 	}
476c4e439b5SAndrew Lindesay 	return true;
477c4e439b5SAndrew Lindesay }
478c4e439b5SAndrew Lindesay 
479c4e439b5SAndrew Lindesay 
480c4e439b5SAndrew Lindesay /*static*/ bool
_IsAllowedChar(char c)481c4e439b5SAndrew Lindesay TextDocumentView::_IsAllowedChar(char c)
482c4e439b5SAndrew Lindesay {
483c4e439b5SAndrew Lindesay 	return c >= ' '
484c4e439b5SAndrew Lindesay 		|| c == '\t'
485c4e439b5SAndrew Lindesay 		|| c == '\n'
486c4e439b5SAndrew Lindesay 		|| c == 127 // delete
487c4e439b5SAndrew Lindesay 		;
488c4e439b5SAndrew Lindesay }
489c4e439b5SAndrew Lindesay 
490c4e439b5SAndrew Lindesay 
491c4e439b5SAndrew Lindesay void
Copy(BClipboard * clipboard)492d7f7bf2dSAxel Dörfler TextDocumentView::Copy(BClipboard* clipboard)
493d7f7bf2dSAxel Dörfler {
494779ab335SX512 	if (!HasSelection() || !fTextDocument.IsSet()) {
495d7f7bf2dSAxel Dörfler 		// Nothing to copy, don't clear clipboard contents for now reason.
496d7f7bf2dSAxel Dörfler 		return;
497d7f7bf2dSAxel Dörfler 	}
498d7f7bf2dSAxel Dörfler 
499d7f7bf2dSAxel Dörfler 	if (clipboard == NULL || !clipboard->Lock())
500d7f7bf2dSAxel Dörfler 		return;
501d7f7bf2dSAxel Dörfler 
502d7f7bf2dSAxel Dörfler 	clipboard->Clear();
503d7f7bf2dSAxel Dörfler 
504d7f7bf2dSAxel Dörfler 	BMessage* clip = clipboard->Data();
505d7f7bf2dSAxel Dörfler 	if (clip != NULL) {
506d7f7bf2dSAxel Dörfler 		int32 start;
507d7f7bf2dSAxel Dörfler 		int32 end;
508d7f7bf2dSAxel Dörfler 		GetSelection(start, end);
509d7f7bf2dSAxel Dörfler 
510d7f7bf2dSAxel Dörfler 		BString text = fTextDocument->Text(start, end - start);
511c4e439b5SAndrew Lindesay 		clip->AddData(kMimeTypePlainText, B_MIME_TYPE, text.String(), text.Length());
512d7f7bf2dSAxel Dörfler 
513d7f7bf2dSAxel Dörfler 		// TODO: Support for "application/x-vnd.Be-text_run_array"
514d7f7bf2dSAxel Dörfler 
515d7f7bf2dSAxel Dörfler 		clipboard->Commit();
516d7f7bf2dSAxel Dörfler 	}
517d7f7bf2dSAxel Dörfler 
518d7f7bf2dSAxel Dörfler 	clipboard->Unlock();
519d7f7bf2dSAxel Dörfler }
520d7f7bf2dSAxel Dörfler 
521d7f7bf2dSAxel Dörfler 
5224f9df95dSPulkoMandy void
Relayout()5234f9df95dSPulkoMandy TextDocumentView::Relayout()
5244f9df95dSPulkoMandy {
5254f9df95dSPulkoMandy 	fTextDocumentLayout.Invalidate();
5264f9df95dSPulkoMandy 	_UpdateScrollBars();
5274f9df95dSPulkoMandy }
5284f9df95dSPulkoMandy 
5294f9df95dSPulkoMandy 
530d7f7bf2dSAxel Dörfler // #pragma mark - private
531d7f7bf2dSAxel Dörfler 
532d7f7bf2dSAxel Dörfler 
533d7f7bf2dSAxel Dörfler float
_TextLayoutWidth(float viewWidth) const534d7f7bf2dSAxel Dörfler TextDocumentView::_TextLayoutWidth(float viewWidth) const
535d7f7bf2dSAxel Dörfler {
536d7f7bf2dSAxel Dörfler 	return viewWidth - (fInsetLeft + fInsetRight);
537d7f7bf2dSAxel Dörfler }
538d7f7bf2dSAxel Dörfler 
539d7f7bf2dSAxel Dörfler 
540d7f7bf2dSAxel Dörfler static const float kHorizontalScrollBarStep = 10.0f;
541d7f7bf2dSAxel Dörfler static const float kVerticalScrollBarStep = 12.0f;
542d7f7bf2dSAxel Dörfler 
543d7f7bf2dSAxel Dörfler 
544d7f7bf2dSAxel Dörfler void
_UpdateScrollBars()545d7f7bf2dSAxel Dörfler TextDocumentView::_UpdateScrollBars()
546d7f7bf2dSAxel Dörfler {
547d7f7bf2dSAxel Dörfler 	BRect bounds(Bounds());
548d7f7bf2dSAxel Dörfler 
549d7f7bf2dSAxel Dörfler 	BScrollBar* horizontalScrollBar = ScrollBar(B_HORIZONTAL);
550d7f7bf2dSAxel Dörfler 	if (horizontalScrollBar != NULL) {
551d7f7bf2dSAxel Dörfler 		long viewWidth = bounds.IntegerWidth();
552d7f7bf2dSAxel Dörfler 		long dataWidth = (long)ceilf(
553d7f7bf2dSAxel Dörfler 			fTextDocumentLayout.Width() + fInsetLeft + fInsetRight);
554d7f7bf2dSAxel Dörfler 
555d7f7bf2dSAxel Dörfler 		long maxRange = dataWidth - viewWidth;
556d7f7bf2dSAxel Dörfler 		maxRange = std::max(maxRange, 0L);
557d7f7bf2dSAxel Dörfler 
558d7f7bf2dSAxel Dörfler 		horizontalScrollBar->SetRange(0, (float)maxRange);
559d7f7bf2dSAxel Dörfler 		horizontalScrollBar->SetProportion((float)viewWidth / dataWidth);
560d7f7bf2dSAxel Dörfler 		horizontalScrollBar->SetSteps(kHorizontalScrollBarStep, dataWidth / 10);
561d7f7bf2dSAxel Dörfler 	}
562d7f7bf2dSAxel Dörfler 
563d7f7bf2dSAxel Dörfler  	BScrollBar* verticalScrollBar = ScrollBar(B_VERTICAL);
564d7f7bf2dSAxel Dörfler 	if (verticalScrollBar != NULL) {
565d7f7bf2dSAxel Dörfler 		long viewHeight = bounds.IntegerHeight();
566d7f7bf2dSAxel Dörfler 		long dataHeight = (long)ceilf(
567d7f7bf2dSAxel Dörfler 			fTextDocumentLayout.Height() + fInsetTop + fInsetBottom);
568d7f7bf2dSAxel Dörfler 
569d7f7bf2dSAxel Dörfler 		long maxRange = dataHeight - viewHeight;
570d7f7bf2dSAxel Dörfler 		maxRange = std::max(maxRange, 0L);
571d7f7bf2dSAxel Dörfler 
572d7f7bf2dSAxel Dörfler 		verticalScrollBar->SetRange(0, maxRange);
573d7f7bf2dSAxel Dörfler 		verticalScrollBar->SetProportion((float)viewHeight / dataHeight);
574d7f7bf2dSAxel Dörfler 		verticalScrollBar->SetSteps(kVerticalScrollBarStep, viewHeight);
575d7f7bf2dSAxel Dörfler 	}
576d7f7bf2dSAxel Dörfler }
577d7f7bf2dSAxel Dörfler 
578d7f7bf2dSAxel Dörfler 
579d7f7bf2dSAxel Dörfler void
_ShowCaret(bool show)58081f55cc8SStephan Aßmus TextDocumentView::_ShowCaret(bool show)
58181f55cc8SStephan Aßmus {
58281f55cc8SStephan Aßmus 	fShowCaret = show;
58381f55cc8SStephan Aßmus 	if (fCaretBounds.IsValid())
58481f55cc8SStephan Aßmus 		Invalidate(fCaretBounds);
58581f55cc8SStephan Aßmus 	else
58681f55cc8SStephan Aßmus 		Invalidate();
58781f55cc8SStephan Aßmus 	// Cancel previous blinker, increment blink token so we only accept
58881f55cc8SStephan Aßmus 	// the message from the blinker we just created
58981f55cc8SStephan Aßmus 	fCaretBlinkToken++;
59081f55cc8SStephan Aßmus 	BMessage message(MSG_BLINK_CARET);
59181f55cc8SStephan Aßmus 	message.AddInt32("token", fCaretBlinkToken);
59281f55cc8SStephan Aßmus 	delete fCaretBlinker;
59381f55cc8SStephan Aßmus 	fCaretBlinker = new BMessageRunner(BMessenger(this), &message,
59481f55cc8SStephan Aßmus 		500000, 1);
59581f55cc8SStephan Aßmus }
59681f55cc8SStephan Aßmus 
59781f55cc8SStephan Aßmus 
59881f55cc8SStephan Aßmus void
_BlinkCaret()59981f55cc8SStephan Aßmus TextDocumentView::_BlinkCaret()
60081f55cc8SStephan Aßmus {
601779ab335SX512 	if (!fSelectionEnabled || !fTextEditor.IsSet())
60281f55cc8SStephan Aßmus 		return;
60381f55cc8SStephan Aßmus 
60481f55cc8SStephan Aßmus 	_ShowCaret(!fShowCaret);
60581f55cc8SStephan Aßmus }
60681f55cc8SStephan Aßmus 
60781f55cc8SStephan Aßmus 
60881f55cc8SStephan Aßmus void
_DrawCaret(int32 textOffset)609d7f7bf2dSAxel Dörfler TextDocumentView::_DrawCaret(int32 textOffset)
610d7f7bf2dSAxel Dörfler {
611d7f7bf2dSAxel Dörfler 	if (!IsFocus() || Window() == NULL || !Window()->IsActive())
612d7f7bf2dSAxel Dörfler 		return;
613d7f7bf2dSAxel Dörfler 
614d7f7bf2dSAxel Dörfler 	float x1;
615d7f7bf2dSAxel Dörfler 	float y1;
616d7f7bf2dSAxel Dörfler 	float x2;
617d7f7bf2dSAxel Dörfler 	float y2;
618d7f7bf2dSAxel Dörfler 
619d7f7bf2dSAxel Dörfler 	fTextDocumentLayout.GetTextBounds(textOffset, x1, y1, x2, y2);
620d7f7bf2dSAxel Dörfler 	x2 = x1 + 1;
621d7f7bf2dSAxel Dörfler 
622d7f7bf2dSAxel Dörfler 	fCaretBounds = BRect(x1, y1, x2, y2);
623d7f7bf2dSAxel Dörfler 	fCaretBounds.OffsetBy(fInsetLeft, fInsetTop);
624d7f7bf2dSAxel Dörfler 
625d7f7bf2dSAxel Dörfler 	SetDrawingMode(B_OP_INVERT);
626d7f7bf2dSAxel Dörfler 	FillRect(fCaretBounds);
627d7f7bf2dSAxel Dörfler }
628d7f7bf2dSAxel Dörfler 
629d7f7bf2dSAxel Dörfler 
630d7f7bf2dSAxel Dörfler void
_DrawSelection()631d7f7bf2dSAxel Dörfler TextDocumentView::_DrawSelection()
632d7f7bf2dSAxel Dörfler {
633d7f7bf2dSAxel Dörfler 	int32 start;
634d7f7bf2dSAxel Dörfler 	int32 end;
635d7f7bf2dSAxel Dörfler 	GetSelection(start, end);
636d7f7bf2dSAxel Dörfler 
637d7f7bf2dSAxel Dörfler 	BShape shape;
638d7f7bf2dSAxel Dörfler 	_GetSelectionShape(shape, start, end);
639d7f7bf2dSAxel Dörfler 
640d7f7bf2dSAxel Dörfler 	SetDrawingMode(B_OP_SUBTRACT);
641d7f7bf2dSAxel Dörfler 
642d7f7bf2dSAxel Dörfler 	SetLineMode(B_ROUND_CAP, B_ROUND_JOIN);
643d7f7bf2dSAxel Dörfler 	MovePenTo(fInsetLeft - 0.5f, fInsetTop - 0.5f);
644d7f7bf2dSAxel Dörfler 
645d7f7bf2dSAxel Dörfler 	if (IsFocus() && Window() != NULL && Window()->IsActive()) {
646d7f7bf2dSAxel Dörfler 		SetHighColor(30, 30, 30);
647d7f7bf2dSAxel Dörfler 		FillShape(&shape);
648d7f7bf2dSAxel Dörfler 	}
649d7f7bf2dSAxel Dörfler 
650d7f7bf2dSAxel Dörfler 	SetHighColor(40, 40, 40);
651d7f7bf2dSAxel Dörfler 	StrokeShape(&shape);
652d7f7bf2dSAxel Dörfler }
653d7f7bf2dSAxel Dörfler 
654d7f7bf2dSAxel Dörfler 
655d7f7bf2dSAxel Dörfler void
_GetSelectionShape(BShape & shape,int32 start,int32 end)656d7f7bf2dSAxel Dörfler TextDocumentView::_GetSelectionShape(BShape& shape, int32 start, int32 end)
657d7f7bf2dSAxel Dörfler {
658d7f7bf2dSAxel Dörfler 	float startX1;
659d7f7bf2dSAxel Dörfler 	float startY1;
660d7f7bf2dSAxel Dörfler 	float startX2;
661d7f7bf2dSAxel Dörfler 	float startY2;
662d7f7bf2dSAxel Dörfler 	fTextDocumentLayout.GetTextBounds(start, startX1, startY1, startX2,
663d7f7bf2dSAxel Dörfler 		startY2);
664d7f7bf2dSAxel Dörfler 
665d7f7bf2dSAxel Dörfler 	startX1 = floorf(startX1);
666d7f7bf2dSAxel Dörfler 	startY1 = floorf(startY1);
667d7f7bf2dSAxel Dörfler 	startX2 = ceilf(startX2);
668d7f7bf2dSAxel Dörfler 	startY2 = ceilf(startY2);
669d7f7bf2dSAxel Dörfler 
670d7f7bf2dSAxel Dörfler 	float endX1;
671d7f7bf2dSAxel Dörfler 	float endY1;
672d7f7bf2dSAxel Dörfler 	float endX2;
673d7f7bf2dSAxel Dörfler 	float endY2;
674d7f7bf2dSAxel Dörfler 	fTextDocumentLayout.GetTextBounds(end, endX1, endY1, endX2, endY2);
675d7f7bf2dSAxel Dörfler 
676d7f7bf2dSAxel Dörfler 	endX1 = floorf(endX1);
677d7f7bf2dSAxel Dörfler 	endY1 = floorf(endY1);
678d7f7bf2dSAxel Dörfler 	endX2 = ceilf(endX2);
679d7f7bf2dSAxel Dörfler 	endY2 = ceilf(endY2);
680d7f7bf2dSAxel Dörfler 
681d7f7bf2dSAxel Dörfler 	int32 startLineIndex = fTextDocumentLayout.LineIndexForOffset(start);
682d7f7bf2dSAxel Dörfler 	int32 endLineIndex = fTextDocumentLayout.LineIndexForOffset(end);
683d7f7bf2dSAxel Dörfler 
684d7f7bf2dSAxel Dörfler 	if (startLineIndex == endLineIndex) {
685d7f7bf2dSAxel Dörfler 		// Selection on one line
686d7f7bf2dSAxel Dörfler 		BPoint lt(startX1, startY1);
687d7f7bf2dSAxel Dörfler 		BPoint rt(endX1, endY1);
688d7f7bf2dSAxel Dörfler 		BPoint rb(endX1, endY2);
689d7f7bf2dSAxel Dörfler 		BPoint lb(startX1, startY2);
690d7f7bf2dSAxel Dörfler 
691d7f7bf2dSAxel Dörfler 		shape.MoveTo(lt);
692d7f7bf2dSAxel Dörfler 		shape.LineTo(rt);
693d7f7bf2dSAxel Dörfler 		shape.LineTo(rb);
694d7f7bf2dSAxel Dörfler 		shape.LineTo(lb);
695d7f7bf2dSAxel Dörfler 		shape.Close();
696d7f7bf2dSAxel Dörfler 	} else if (startLineIndex == endLineIndex - 1 && endX1 <= startX1) {
697d7f7bf2dSAxel Dörfler 		// Selection on two lines, with gap:
698d7f7bf2dSAxel Dörfler 		// ---------
699d7f7bf2dSAxel Dörfler 		// ------###
700d7f7bf2dSAxel Dörfler 		// ##-------
701d7f7bf2dSAxel Dörfler 		// ---------
702d7f7bf2dSAxel Dörfler 		float width = ceilf(fTextDocumentLayout.Width());
703d7f7bf2dSAxel Dörfler 
704d7f7bf2dSAxel Dörfler 		BPoint lt(startX1, startY1);
705d7f7bf2dSAxel Dörfler 		BPoint rt(width, startY1);
706d7f7bf2dSAxel Dörfler 		BPoint rb(width, startY2);
707d7f7bf2dSAxel Dörfler 		BPoint lb(startX1, startY2);
708d7f7bf2dSAxel Dörfler 
709d7f7bf2dSAxel Dörfler 		shape.MoveTo(lt);
710d7f7bf2dSAxel Dörfler 		shape.LineTo(rt);
711d7f7bf2dSAxel Dörfler 		shape.LineTo(rb);
712d7f7bf2dSAxel Dörfler 		shape.LineTo(lb);
713d7f7bf2dSAxel Dörfler 		shape.Close();
714d7f7bf2dSAxel Dörfler 
715d7f7bf2dSAxel Dörfler 		lt = BPoint(0, endY1);
716d7f7bf2dSAxel Dörfler 		rt = BPoint(endX1, endY1);
717d7f7bf2dSAxel Dörfler 		rb = BPoint(endX1, endY2);
718d7f7bf2dSAxel Dörfler 		lb = BPoint(0, endY2);
719d7f7bf2dSAxel Dörfler 
720d7f7bf2dSAxel Dörfler 		shape.MoveTo(lt);
721d7f7bf2dSAxel Dörfler 		shape.LineTo(rt);
722d7f7bf2dSAxel Dörfler 		shape.LineTo(rb);
723d7f7bf2dSAxel Dörfler 		shape.LineTo(lb);
724d7f7bf2dSAxel Dörfler 		shape.Close();
725d7f7bf2dSAxel Dörfler 	} else {
726d7f7bf2dSAxel Dörfler 		// Selection over multiple lines
727d7f7bf2dSAxel Dörfler 		float width = ceilf(fTextDocumentLayout.Width());
728d7f7bf2dSAxel Dörfler 
729d7f7bf2dSAxel Dörfler 		shape.MoveTo(BPoint(startX1, startY1));
730d7f7bf2dSAxel Dörfler 		shape.LineTo(BPoint(width, startY1));
731d7f7bf2dSAxel Dörfler 		shape.LineTo(BPoint(width, endY1));
732d7f7bf2dSAxel Dörfler 		shape.LineTo(BPoint(endX1, endY1));
733d7f7bf2dSAxel Dörfler 		shape.LineTo(BPoint(endX1, endY2));
734d7f7bf2dSAxel Dörfler 		shape.LineTo(BPoint(0, endY2));
735d7f7bf2dSAxel Dörfler 		shape.LineTo(BPoint(0, startY2));
736d7f7bf2dSAxel Dörfler 		shape.LineTo(BPoint(startX1, startY2));
737d7f7bf2dSAxel Dörfler 		shape.Close();
738d7f7bf2dSAxel Dörfler 	}
739d7f7bf2dSAxel Dörfler }
740d7f7bf2dSAxel Dörfler 
741d7f7bf2dSAxel Dörfler 
742c4e439b5SAndrew Lindesay /*!	The data provided in the `str` parameter may contain characters that are
743c4e439b5SAndrew Lindesay 	not allowed. This method should filter those out and then apply them to
744c4e439b5SAndrew Lindesay 	the text body.
745c4e439b5SAndrew Lindesay */
746c4e439b5SAndrew Lindesay status_t
_PastePossiblyDisallowedChars(const char * str,int32 maxLength)747c4e439b5SAndrew Lindesay TextDocumentView::_PastePossiblyDisallowedChars(const char* str, int32 maxLength)
748c4e439b5SAndrew Lindesay {
749c4e439b5SAndrew Lindesay 	if (maxLength <= 0)
750c4e439b5SAndrew Lindesay 		return B_OK;
751c4e439b5SAndrew Lindesay 
752c4e439b5SAndrew Lindesay 	if (TextDocumentView::_AreCharsAllowed(str, maxLength)) {
753c4e439b5SAndrew Lindesay 		_PasteAllowedChars(str, maxLength);
754c4e439b5SAndrew Lindesay 	} else {
755c4e439b5SAndrew Lindesay 		char* strFiltered = new(std::nothrow) char[maxLength];
756c4e439b5SAndrew Lindesay 
757c4e439b5SAndrew Lindesay 		if (strFiltered == NULL)
758c4e439b5SAndrew Lindesay 			return B_NO_MEMORY;
759c4e439b5SAndrew Lindesay 
760c4e439b5SAndrew Lindesay 		int32 strFilteredLength = 0;
761c4e439b5SAndrew Lindesay 
762c4e439b5SAndrew Lindesay 		for (int i = 0; str[i] != '\0' && i < maxLength; i++) {
763c4e439b5SAndrew Lindesay 			if (_IsAllowedChar(str[i])) {
764c4e439b5SAndrew Lindesay 				strFiltered[strFilteredLength] = str[i];
765c4e439b5SAndrew Lindesay 				strFilteredLength++;
766c4e439b5SAndrew Lindesay 			}
767c4e439b5SAndrew Lindesay 		}
768c4e439b5SAndrew Lindesay 
769c4e439b5SAndrew Lindesay 		strFiltered[strFilteredLength] = '\0';
770c4e439b5SAndrew Lindesay 		_PasteAllowedChars(strFiltered, strFilteredLength);
771c4e439b5SAndrew Lindesay 
772c4e439b5SAndrew Lindesay 		delete[] strFiltered;
773c4e439b5SAndrew Lindesay 	}
774c4e439b5SAndrew Lindesay 
775c4e439b5SAndrew Lindesay 	return B_OK;
776c4e439b5SAndrew Lindesay }
777c4e439b5SAndrew Lindesay 
778c4e439b5SAndrew Lindesay 
779c4e439b5SAndrew Lindesay /*! Here the data in `str` should be clean of control characters.
780c4e439b5SAndrew Lindesay  */
781c4e439b5SAndrew Lindesay void
_PasteAllowedChars(const char * str,int32 maxLength)782c4e439b5SAndrew Lindesay TextDocumentView::_PasteAllowedChars(const char* str, int32 maxLength)
783c4e439b5SAndrew Lindesay {
784c4e439b5SAndrew Lindesay 	BString plainText(str, maxLength);
785c4e439b5SAndrew Lindesay 
786c4e439b5SAndrew Lindesay 	if (plainText.IsEmpty())
787c4e439b5SAndrew Lindesay 		return;
788c4e439b5SAndrew Lindesay 
789c4e439b5SAndrew Lindesay 	if (fTextEditor.IsSet()) {
790c4e439b5SAndrew Lindesay 		if (fTextEditor->HasSelection()) {
791c4e439b5SAndrew Lindesay 			int32 start = fTextEditor->SelectionStart();
792c4e439b5SAndrew Lindesay 			int32 end = fTextEditor->SelectionEnd();
793c4e439b5SAndrew Lindesay 			fTextEditor->Replace(start, end - start, plainText);
794c4e439b5SAndrew Lindesay 			Invalidate();
795c4e439b5SAndrew Lindesay 			_UpdateScrollBars();
796c4e439b5SAndrew Lindesay 		} else {
797c4e439b5SAndrew Lindesay 			int32 caretOffset = fTextEditor->CaretOffset();
798c4e439b5SAndrew Lindesay 			if (caretOffset >= 0) {
799c4e439b5SAndrew Lindesay 				fTextEditor->Insert(caretOffset, plainText);
800c4e439b5SAndrew Lindesay 				Invalidate();
801c4e439b5SAndrew Lindesay 				_UpdateScrollBars();
802c4e439b5SAndrew Lindesay 			}
803c4e439b5SAndrew Lindesay 		}
804c4e439b5SAndrew Lindesay 	}
805c4e439b5SAndrew Lindesay }