1ee694ac8SAxel Dörfler/*
2103adddbSAxel Dörfler * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3ee694ac8SAxel Dörfler * Distributed under the terms of the MIT License.
4ee694ac8SAxel Dörfler *
5ee694ac8SAxel Dörfler * Authors:
6ab21cedcSJohn Scipione *		Stephan A��mus, superstippi@gmx.de
7ab21cedcSJohn Scipione *		Stefano Ceccherini, stefano.ceccherini@gmail.com
8ab21cedcSJohn Scipione *		Marc Flerackers, mflerackers@androme.be
9ee694ac8SAxel Dörfler *		Hiroshi Lockheimer (BTextView is based on his STEEngine)
10eb774c29SJohn Scipione *		John Scipione, jscipione@gmail.com
11ab21cedcSJohn Scipione *		Oliver Tappe, zooey@hirschkaefer.de
12ee694ac8SAxel Dörfler */
13ee694ac8SAxel Dörfler
1472f334d1Sshadow
1513ca2d94SAxel Dörfler// TODOs:
166adf049cSStefano Ceccherini// - Consider using BObjectList instead of BList
17322853c8SStefano Ceccherini// 	 for disallowed characters (it would remove a lot of reinterpret_casts)
1813ca2d94SAxel Dörfler// - Check for correctness and possible optimizations the calls to _Refresh(),
19a682d981SStephan Aßmus// 	 to refresh only changed parts of text (currently we often redraw the whole
20a682d981SStephan Aßmus//   text)
2175ad8970SStefano Ceccherini
2270751f7bSStefano Ceccherini// Known Bugs:
23e005dc75SStefano Ceccherini// - Double buffering doesn't work well (disabled by default)
2470751f7bSStefano Ceccherini
251d1e61bbSJohn Scipione
261d1e61bbSJohn Scipione#include <TextView.h>
271d1e61bbSJohn Scipione
281d1e61bbSJohn Scipione#include <new>
291d1e61bbSJohn Scipione
30ee694ac8SAxel Dörfler#include <stdio.h>
31ee694ac8SAxel Dörfler#include <stdlib.h>
3294b9294fSStefano Ceccherini
3372f334d1Sshadow#include <Application.h>
345a5aa2e3SStefano Ceccherini#include <Beep.h>
35b032f972SStefano Ceccherini#include <Bitmap.h>
3672f334d1Sshadow#include <Clipboard.h>
376adf049cSStefano Ceccherini#include <Debug.h>
387c0f5738SRene Gollent#include <Entry.h>
395a5aa2e3SStefano Ceccherini#include <Input.h>
4091f0846fSRyan Leavengood#include <LayoutBuilder.h>
4188643347SStephan Aßmus#include <LayoutUtils.h>
4291b84c77SStefano Ceccherini#include <MessageRunner.h>
437c0f5738SRene Gollent#include <Path.h>
4491f0846fSRyan Leavengood#include <PopUpMenu.h>
4572f334d1Sshadow#include <PropertyInfo.h>
46323ddd57Sshatty#include <Region.h>
47323ddd57Sshatty#include <ScrollBar.h>
4860f75e90SOliver Tappe#include <SystemCatalog.h>
49323ddd57Sshatty#include <Window.h>
5072f334d1Sshadow
5139fbf550SOliver Tappe#include <binary_compatibility/Interface.h>
5239fbf550SOliver Tappe
536adf049cSStefano Ceccherini#include "InlineInput.h"
54b032f972SStefano Ceccherini#include "LineBuffer.h"
55b032f972SStefano Ceccherini#include "StyleBuffer.h"
56b032f972SStefano Ceccherini#include "TextGapBuffer.h"
57b032f972SStefano Ceccherini#include "UndoBuffer.h"
5876f84757SStefano Ceccherini#include "WidthBuffer.h"
59323ddd57Sshatty
6039fbf550SOliver Tappe
6113ca2d94SAxel Dörflerusing namespace std;
6260f75e90SOliver Tappeusing BPrivate::gSystemCatalog;
6391f0846fSRyan Leavengood
6491f0846fSRyan Leavengood
65546208a5SOliver Tappe#undef B_TRANSLATION_CONTEXT
66546208a5SOliver Tappe#define B_TRANSLATION_CONTEXT "TextView"
6791f0846fSRyan Leavengood
6891f0846fSRyan Leavengood
6960f75e90SOliver Tappe#define TRANSLATE(str) \
70eaa5e093SOliver Tappe	gSystemCatalog.GetString(B_TRANSLATE_MARK(str), "TextView")
718b7faba2SStefano Ceccherini
7288643347SStephan Aßmus#undef TRACE
7388643347SStephan Aßmus#undef CALLED
7488643347SStephan Aßmus//#define TRACE_TEXT_VIEW
7588643347SStephan Aßmus#ifdef TRACE_TEXT_VIEW
7688643347SStephan Aßmus#	include <FunctionTracer.h>
7788643347SStephan Aßmus	static int32 sFunctionDepth = -1;
7888643347SStephan Aßmus#	define CALLED(x...)	FunctionTracer _ft("BTextView", __FUNCTION__, \
7988643347SStephan Aßmus							sFunctionDepth)
8088643347SStephan Aßmus#	define TRACE(x...)	{ BString _to; \
8188643347SStephan Aßmus							_to.Append(' ', (sFunctionDepth + 1) * 2); \
8288643347SStephan Aßmus							printf("%s", _to.String()); printf(x); }
837446a252SStefano Ceccherini#else
8488643347SStephan Aßmus#	define CALLED(x...)
8588643347SStephan Aßmus#	define TRACE(x...)
867446a252SStefano Ceccherini#endif
8772f334d1Sshadow
8888643347SStephan Aßmus
89c6811df0SStephan Aßmus#define USE_WIDTHBUFFER 1
908b7faba2SStefano Ceccherini#define USE_DOUBLEBUFFERING 0
918b7faba2SStefano Ceccherini
928b7faba2SStefano Ceccherini
93323ddd57Sshattystruct flattened_text_run {
94323ddd57Sshatty	int32	offset;
958b7faba2SStefano Ceccherini	font_family	family;
968b7faba2SStefano Ceccherini	font_style style;
97323ddd57Sshatty	float	size;
98b55f61d6SJohn Scipione	float	shear;		// typically 90.0
99b55f61d6SJohn Scipione	uint16	face;		// typically 0
100323ddd57Sshatty	uint8	red;
101323ddd57Sshatty	uint8	green;
102323ddd57Sshatty	uint8	blue;
103b55f61d6SJohn Scipione	uint8	alpha;		// 255 == opaque
104b55f61d6SJohn Scipione	uint16	_reserved_;	// 0
105323ddd57Sshatty};
106323ddd57Sshatty
107323ddd57Sshattystruct flattened_text_run_array {
10813ca2d94SAxel Dörfler	uint32	magic;
10913ca2d94SAxel Dörfler	uint32	version;
11013ca2d94SAxel Dörfler	int32	count;
11113ca2d94SAxel Dörfler	flattened_text_run styles[1];
112323ddd57Sshatty};
113323ddd57Sshatty
11404d40ff2SAxel Dörflerstatic const uint32 kFlattenedTextRunArrayMagic = 'Ali!';
11504d40ff2SAxel Dörflerstatic const uint32 kFlattenedTextRunArrayVersion = 0;
116356acd7dSStefano Ceccherini
11732c29a08SOliver Tappe
118356acd7dSStefano Ceccherinienum {
11932c29a08SOliver Tappe	CHAR_CLASS_DEFAULT,
12032c29a08SOliver Tappe	CHAR_CLASS_WHITESPACE,
12132c29a08SOliver Tappe	CHAR_CLASS_GRAPHICAL,
12232c29a08SOliver Tappe	CHAR_CLASS_QUOTE,
12332c29a08SOliver Tappe	CHAR_CLASS_PUNCTUATION,
12432c29a08SOliver Tappe	CHAR_CLASS_PARENS_OPEN,
12532c29a08SOliver Tappe	CHAR_CLASS_PARENS_CLOSE,
12632c29a08SOliver Tappe	CHAR_CLASS_END_OF_TEXT
127fe23fb66SAxel Dörfler};
128356acd7dSStefano Ceccherini
1297889e7c9SStefano Ceccherini
130a682d981SStephan Aßmusclass BTextView::TextTrackState {
13172f334d1Sshadowpublic:
132a682d981SStephan Aßmus	TextTrackState(BMessenger messenger);
133a682d981SStephan Aßmus	~TextTrackState();
1346343dc98SStefano Ceccherini
135a682d981SStephan Aßmus	void SimulateMouseMovement(BTextView* view);
1366343dc98SStefano Ceccherini
137a682d981SStephan Aßmuspublic:
138a682d981SStephan Aßmus	int32				clickOffset;
139a682d981SStephan Aßmus	bool				shiftDown;
140a682d981SStephan Aßmus	BRect				selectionRect;
1416f260d07SOliver Tappe	BPoint				where;
1423f3ab162SOliver Tappe
143a682d981SStephan Aßmus	int32				anchor;
144a682d981SStephan Aßmus	int32				selStart;
145a682d981SStephan Aßmus	int32				selEnd;
1466343dc98SStefano Ceccherini
1476343dc98SStefano Ceccheriniprivate:
148a682d981SStephan Aßmus	BMessageRunner*		fRunner;
14972f334d1Sshadow};
15075ad8970SStefano Ceccherini
15172f334d1Sshadow
152a682d981SStephan Aßmusstruct BTextView::LayoutData {
15388643347SStephan Aßmus	LayoutData()
15488643347SStephan Aßmus		: leftInset(0),
15588643347SStephan Aßmus		  topInset(0),
15688643347SStephan Aßmus		  rightInset(0),
15788643347SStephan Aßmus		  bottomInset(0),
158a682d981SStephan Aßmus		  valid(false)
159a682d981SStephan Aßmus	{
160a682d981SStephan Aßmus	}
161a682d981SStephan Aßmus
16288643347SStephan Aßmus	void UpdateInsets(const BRect& bounds, const BRect& textRect)
16388643347SStephan Aßmus	{
1644b0f176dSOliver Tappe		// we disallow negative insets, as they would cause parts of the
1654b0f176dSOliver Tappe		// text to be hidden
1664b0f176dSOliver Tappe		leftInset = textRect.left >= bounds.left
1674b0f176dSOliver Tappe			? textRect.left - bounds.left
1684b0f176dSOliver Tappe			: 0;
1694b0f176dSOliver Tappe		topInset = textRect.top >= bounds.top
1704b0f176dSOliver Tappe			? textRect.top - bounds.top
1714b0f176dSOliver Tappe			: 0;
1724b0f176dSOliver Tappe		rightInset = bounds.right >= textRect.right
1734b0f176dSOliver Tappe			? bounds.right - textRect.right
1744b0f176dSOliver Tappe			: leftInset;
1754b0f176dSOliver Tappe		bottomInset = bounds.bottom >= textRect.bottom
1764b0f176dSOliver Tappe			? bounds.bottom - textRect.bottom
1774b0f176dSOliver Tappe			: topInset;
17888643347SStephan Aßmus	}
17988643347SStephan Aßmus
180a682d981SStephan Aßmus	float				leftInset;
181a682d981SStephan Aßmus	float				topInset;
182a682d981SStephan Aßmus	float				rightInset;
183a682d981SStephan Aßmus	float				bottomInset;
184a682d981SStephan Aßmus
185a682d981SStephan Aßmus	BSize				min;
18688643347SStephan Aßmus	BSize				preferred;
187a682d981SStephan Aßmus	bool				valid;
188a682d981SStephan Aßmus};
189a682d981SStephan Aßmus
1906343dc98SStefano Ceccherini
191f287ca7dSOliver Tappestatic const rgb_color kBlueInputColor = { 152, 203, 255, 255 };
192f287ca7dSOliver Tappestatic const rgb_color kRedInputColor = { 255, 152, 152, 255 };
193f287ca7dSOliver Tappe
194f287ca7dSOliver Tappestatic const float kHorizontalScrollBarStep = 10.0;
195f287ca7dSOliver Tappestatic const float kVerticalScrollBarStep = 12.0;
1966343dc98SStefano Ceccherini
197eb774c29SJohn Scipionestatic const int32 kMsgNavigateArrow = '_NvA';
198eb774c29SJohn Scipionestatic const int32 kMsgNavigatePage  = '_NvP';
19917c9e987SKacper Kasperstatic const int32 kMsgRemoveWord    = '_RmW';
200402c3b2cSOliver Tappe
201402c3b2cSOliver Tappe
20213ca2d94SAxel Dörflerstatic property_info sPropertyList[] = {
20372f334d1Sshadow	{
204f4fc3d62SJérôme Duval		"selection",
20572f334d1Sshadow		{ B_GET_PROPERTY, 0 },
20672f334d1Sshadow		{ B_DIRECT_SPECIFIER, 0 },
20772f334d1Sshadow		"Returns the current selection.", 0,
20872f334d1Sshadow		{ B_INT32_TYPE, 0 }
20972f334d1Sshadow	},
21072f334d1Sshadow	{
211f4fc3d62SJérôme Duval		"selection",
21272f334d1Sshadow		{ B_SET_PROPERTY, 0 },
21372f334d1Sshadow		{ B_DIRECT_SPECIFIER, 0 },
21472f334d1Sshadow		"Sets the current selection.", 0,
21572f334d1Sshadow		{ B_INT32_TYPE, 0 }
21672f334d1Sshadow	},
21772f334d1Sshadow	{
21872f334d1Sshadow		"Text",
21972f334d1Sshadow		{ B_COUNT_PROPERTIES, 0 },
22072f334d1Sshadow		{ B_DIRECT_SPECIFIER, 0 },
22172f334d1Sshadow		"Returns the length of the text in bytes.", 0,
22272f334d1Sshadow		{ B_INT32_TYPE, 0 }
22372f334d1Sshadow	},
22472f334d1Sshadow	{
22572f334d1Sshadow		"Text",
22672f334d1Sshadow		{ B_GET_PROPERTY, 0 },
22772f334d1Sshadow		{ B_RANGE_SPECIFIER, B_REVERSE_RANGE_SPECIFIER, 0 },
22872f334d1Sshadow		"Returns the text in the specified range in the BTextView.", 0,
22972f334d1Sshadow		{ B_STRING_TYPE, 0 }
23072f334d1Sshadow	},
23172f334d1Sshadow	{
23272f334d1Sshadow		"Text",
23372f334d1Sshadow		{ B_SET_PROPERTY, 0 },
23472f334d1Sshadow		{ B_RANGE_SPECIFIER, B_REVERSE_RANGE_SPECIFIER, 0 },
23572f334d1Sshadow		"Removes or inserts text into the specified range in the BTextView.", 0,
23672f334d1Sshadow		{ B_STRING_TYPE, 0 }
23772f334d1Sshadow	},
23872f334d1Sshadow	{
23972f334d1Sshadow		"text_run_array",
24072f334d1Sshadow		{ B_GET_PROPERTY, 0 },
24172f334d1Sshadow		{ B_RANGE_SPECIFIER, B_REVERSE_RANGE_SPECIFIER, 0 },
242a682d981SStephan Aßmus		"Returns the style information for the text in the specified range in "
243a682d981SStephan Aßmus			"the BTextView.", 0,
24472f334d1Sshadow		{ B_RAW_TYPE, 0 },
24572f334d1Sshadow	},
24672f334d1Sshadow	{
24772f334d1Sshadow		"text_run_array",
24872f334d1Sshadow		{ B_SET_PROPERTY, 0 },
24972f334d1Sshadow		{ B_RANGE_SPECIFIER, B_REVERSE_RANGE_SPECIFIER, 0 },
250a682d981SStephan Aßmus		"Sets the style information for the text in the specified range in the "
251a682d981SStephan Aßmus			"BTextView.", 0,
25272f334d1Sshadow		{ B_RAW_TYPE, 0 },
25372f334d1Sshadow	},
254346d1496SHumdinger
25572f334d1Sshadow	{ 0 }
25672f334d1Sshadow};
25772f334d1Sshadow
25876f84757SStefano Ceccherini
259ab21cedcSJohn ScipioneBTextView::BTextView(BRect frame, const char* name, BRect textRect,
260f5c284eeSJohn Scipione	uint32 resizeMask, uint32 flags)
261f5c284eeSJohn Scipione	:
262f5c284eeSJohn Scipione	BView(frame, name, resizeMask,
26313ca2d94SAxel Dörfler		flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE)
26472f334d1Sshadow{
26513ca2d94SAxel Dörfler	_InitObject(textRect, NULL, NULL);
26607838f4cSAdrien Destugues	SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
26772f334d1Sshadow}
2685a5aa2e3SStefano Ceccherini
2695a5aa2e3SStefano Ceccherini
270ab21cedcSJohn ScipioneBTextView::BTextView(BRect frame, const char* name, BRect textRect,
271f5c284eeSJohn Scipione	const BFont* initialFont, const rgb_color* initialColor,
272f5c284eeSJohn Scipione	uint32 resizeMask, uint32 flags)
273f5c284eeSJohn Scipione	:
274f5c284eeSJohn Scipione	BView(frame, name, resizeMask,
27513ca2d94SAxel Dörfler		flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE)
27672f334d1Sshadow{
27713ca2d94SAxel Dörfler	_InitObject(textRect, initialFont, initialColor);
27807838f4cSAdrien Destugues	SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
27972f334d1Sshadow}
28075ad8970SStefano Ceccherini
28175ad8970SStefano Ceccherini
282a682d981SStephan AßmusBTextView::BTextView(const char* name, uint32 flags)
283f5c284eeSJohn Scipione	:
284f5c284eeSJohn Scipione	BView(name,
285f5c284eeSJohn Scipione		flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE)
286a682d981SStephan Aßmus{
28788643347SStephan Aßmus	_InitObject(Bounds(), NULL, NULL);
28807838f4cSAdrien Destugues	SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
289a682d981SStephan Aßmus}
290a682d981SStephan Aßmus
291a682d981SStephan Aßmus
292a682d981SStephan AßmusBTextView::BTextView(const char* name, const BFont* initialFont,
293a682d981SStephan Aßmus	const rgb_color* initialColor, uint32 flags)
294f5c284eeSJohn Scipione	:
295f5c284eeSJohn Scipione	BView(name,
296f5c284eeSJohn Scipione		flags | B_FRAME_EVENTS | B_PULSE_NEEDED | B_INPUT_METHOD_AWARE)
297a682d981SStephan Aßmus{
29888643347SStephan Aßmus	_InitObject(Bounds(), initialFont, initialColor);
29907838f4cSAdrien Destugues	SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
300a682d981SStephan Aßmus}
301a682d981SStephan Aßmus
302a682d981SStephan Aßmus
303ab21cedcSJohn ScipioneBTextView::BTextView(BMessage* archive)
304f5c284eeSJohn Scipione	:
305f5c284eeSJohn Scipione	BView(archive)
30672f334d1Sshadow{
3077446a252SStefano Ceccherini	CALLED();
308323ddd57Sshatty	BRect rect;
309323ddd57Sshatty
310aa4d8c57SStefano Ceccherini	if (archive->FindRect("_trect", &rect) != B_OK)
311aa4d8c57SStefano Ceccherini		rect.Set(0, 0, 0, 0);
312aa4d8c57SStefano Ceccherini
31313ca2d94SAxel Dörfler	_InitObject(rect, NULL, NULL);
3143f3ab162SOliver Tappe
3159c89ceb8SAdrien Destugues	bool toggle;
3169c89ceb8SAdrien Destugues
3179c89ceb8SAdrien Destugues	if (archive->FindBool("_password", &toggle) == B_OK)
3189c89ceb8SAdrien Destugues		HideTyping(toggle);
3199c89ceb8SAdrien Destugues
320ab21cedcSJohn Scipione	const char* text = NULL;
321323ddd57Sshatty	if (archive->FindString("_text", &text) == B_OK)
322323ddd57Sshatty		SetText(text);
323323ddd57Sshatty
324356acd7dSStefano Ceccherini	int32 flag, flag2;
325323ddd57Sshatty	if (archive->FindInt32("_align", &flag) == B_OK)
326323ddd57Sshatty		SetAlignment((alignment)flag);
327323ddd57Sshatty
328356acd7dSStefano Ceccherini	float value;
329356acd7dSStefano Ceccherini
330323ddd57Sshatty	if (archive->FindFloat("_tab", &value) == B_OK)
331323ddd57Sshatty		SetTabWidth(value);
3323f3ab162SOliver Tappe
333323ddd57Sshatty	if (archive->FindInt32("_col_sp", &flag) == B_OK)
334323ddd57Sshatty		SetColorSpace((color_space)flag);
335323ddd57Sshatty
336323ddd57Sshatty	if (archive->FindInt32("_max", &flag) == B_OK)
3370ddc8de6Shaydentech		SetMaxBytes(flag);
338323ddd57Sshatty
339323ddd57Sshatty	if (archive->FindInt32("_sel", &flag) == B_OK &&
340323ddd57Sshatty		archive->FindInt32("_sel", &flag2) == B_OK)
341323ddd57Sshatty		Select(flag, flag2);
3423f3ab162SOliver Tappe
343323ddd57Sshatty	if (archive->FindBool("_stylable", &toggle) == B_OK)
344323ddd57Sshatty		SetStylable(toggle);
345323ddd57Sshatty
346323ddd57Sshatty	if (archive->FindBool("_auto_in", &toggle) == B_OK)
347323ddd57Sshatty		SetAutoindent(toggle);
348323ddd57Sshatty
349323ddd57Sshatty	if (archive->FindBool("_wrap", &toggle) == B_OK)
350323ddd57Sshatty		SetWordWrap(toggle);
351323ddd57Sshatty
352323ddd57Sshatty	if (archive->FindBool("_nsel", &toggle) == B_OK)
353323ddd57Sshatty		MakeSelectable(!toggle);
354323ddd57Sshatty
355323ddd57Sshatty	if (archive->FindBool("_nedit", &toggle) == B_OK)
356323ddd57Sshatty		MakeEditable(!toggle);
357aa4d8c57SStefano Ceccherini
358aa4d8c57SStefano Ceccherini	ssize_t disallowedCount = 0;
359ab21cedcSJohn Scipione	const int32* disallowedChars = NULL;
360aa4d8c57SStefano Ceccherini	if (archive->FindData("_dis_ch", B_RAW_TYPE,
361ab21cedcSJohn Scipione		(const void**)&disallowedChars, &disallowedCount) == B_OK) {
3623f3ab162SOliver Tappe
363aa4d8c57SStefano Ceccherini		fDisallowedChars = new BList;
364aa4d8c57SStefano Ceccherini		disallowedCount /= sizeof(int32);
365a682d981SStephan Aßmus		for (int32 x = 0; x < disallowedCount; x++) {
366a682d981SStephan Aßmus			fDisallowedChars->AddItem(
367ab21cedcSJohn Scipione				reinterpret_cast<void*>(disallowedChars[x]));
368a682d981SStephan Aßmus		}
369aa4d8c57SStefano Ceccherini	}
3703f3ab162SOliver Tappe
371aa4d8c57SStefano Ceccherini	ssize_t runSize = 0;
372ab21cedcSJohn Scipione	const void* flattenedRun = NULL;
3733f3ab162SOliver Tappe
374a682d981SStephan Aßmus	if (archive->FindData("_runs", B_RAW_TYPE, &flattenedRun, &runSize)
375a682d981SStephan Aßmus			== B_OK) {
376ab21cedcSJohn Scipione		text_run_array* runArray = UnflattenRunArray(flattenedRun,
377a682d981SStephan Aßmus			(int32*)&runSize);
37874dca7b4SStefano Ceccherini		if (runArray) {
37974dca7b4SStefano Ceccherini			SetRunArray(0, TextLength(), runArray);
380e3338433SStefano Ceccherini			FreeRunArray(runArray);
38174dca7b4SStefano Ceccherini		}
382aa4d8c57SStefano Ceccherini	}
38372f334d1Sshadow}
38475ad8970SStefano Ceccherini
38575ad8970SStefano Ceccherini
38672f334d1SshadowBTextView::~BTextView()
38772f334d1Sshadow{
38813ca2d94SAxel Dörfler	_CancelInputMethod();
38913ca2d94SAxel Dörfler	_StopMouseTracking();
39013ca2d94SAxel Dörfler	_DeleteOffscreen();
3916fb34cf9SStefano Ceccherini
39272f334d1Sshadow	delete fText;
39372f334d1Sshadow	delete fLines;
394323ddd57Sshatty	delete fStyles;
395ba926063SStefano Ceccherini	delete fDisallowedChars;
396b140f1deSMarc Flerackers	delete fUndo;
3976343dc98SStefano Ceccherini	delete fClickRunner;
3983f3ab162SOliver Tappe	delete fDragRunner;
39988643347SStephan Aßmus	delete fLayoutData;
40072f334d1Sshadow}
40175ad8970SStefano Ceccherini
40275ad8970SStefano Ceccherini
403ab21cedcSJohn ScipioneBArchivable*
404ab21cedcSJohn ScipioneBTextView::Instantiate(BMessage* archive)
40572f334d1Sshadow{
4067446a252SStefano Ceccherini	CALLED();
40772f334d1Sshadow	if (validate_instantiation(archive, "BTextView"))
40872f334d1Sshadow		return new BTextView(archive);
4095a596083SStefano Ceccherini	return NULL;
41072f334d1Sshadow}
41175ad8970SStefano Ceccherini
41275ad8970SStefano Ceccherini
41313ca2d94SAxel Dörflerstatus_t
414ab21cedcSJohn ScipioneBTextView::Archive(BMessage* data, bool deep) const
41572f334d1Sshadow{
4167446a252SStefano Ceccherini	CALLED();
417323ddd57Sshatty	status_t err = BView::Archive(data, deep);
4185a596083SStefano Ceccherini	if (err == B_OK)
4195a596083SStefano Ceccherini		err = data->AddString("_text", Text());
4205a596083SStefano Ceccherini	if (err == B_OK)
4215a596083SStefano Ceccherini		err = data->AddInt32("_align", fAlignment);
4225a596083SStefano Ceccherini	if (err == B_OK)
4235a596083SStefano Ceccherini		err = data->AddFloat("_tab", fTabWidth);
4245a596083SStefano Ceccherini	if (err == B_OK)
4255a596083SStefano Ceccherini		err = data->AddInt32("_col_sp", fColorSpace);
4265a596083SStefano Ceccherini	if (err == B_OK)
4275a596083SStefano Ceccherini		err = data->AddRect("_trect", fTextRect);
4285a596083SStefano Ceccherini	if (err == B_OK)
4295a596083SStefano Ceccherini		err = data->AddInt32("_max", fMaxBytes);
4305a596083SStefano Ceccherini	if (err == B_OK)
4315a596083SStefano Ceccherini		err = data->AddInt32("_sel", fSelStart);
4325a596083SStefano Ceccherini	if (err == B_OK)
4333f3ab162SOliver Tappe		err = data->AddInt32("_sel", fSelEnd);
4345a596083SStefano Ceccherini	if (err == B_OK)
4355a596083SStefano Ceccherini		err = data->AddBool("_stylable", fStylable);
4365a596083SStefano Ceccherini	if (err == B_OK)
4375a596083SStefano Ceccherini		err = data->AddBool("_auto_in", fAutoindent);
4385a596083SStefano Ceccherini	if (err == B_OK)
4395a596083SStefano Ceccherini		err = data->AddBool("_wrap", fWrap);
4405a596083SStefano Ceccherini	if (err == B_OK)
4415a596083SStefano Ceccherini		err = data->AddBool("_nsel", !fSelectable);
4425a596083SStefano Ceccherini	if (err == B_OK)
4435a596083SStefano Ceccherini		err = data->AddBool("_nedit", !fEditable);
4449c89ceb8SAdrien Destugues	if (err == B_OK)
4459c89ceb8SAdrien Destugues		err = data->AddBool("_password", IsTypingHidden());
4463f3ab162SOliver Tappe
447e683838eSSean Healy	if (err == B_OK && fDisallowedChars != NULL && fDisallowedChars->CountItems() > 0) {
4485a596083SStefano Ceccherini		err = data->AddData("_dis_ch", B_RAW_TYPE, fDisallowedChars->Items(),
449aa4d8c57SStefano Ceccherini			fDisallowedChars->CountItems() * sizeof(int32));
45013ca2d94SAxel Dörfler	}
451aa4d8c57SStefano Ceccherini
4525a596083SStefano Ceccherini	if (err == B_OK) {
4535a596083SStefano Ceccherini		int32 runSize = 0;
454ab21cedcSJohn Scipione		text_run_array* runArray = RunArray(0, TextLength());
4553f3ab162SOliver Tappe
456ab21cedcSJohn Scipione		void* flattened = FlattenRunArray(runArray, &runSize);
4575a596083SStefano Ceccherini		if (flattened != NULL) {
4583f3ab162SOliver Tappe			data->AddData("_runs", B_RAW_TYPE, flattened, runSize);
4595a596083SStefano Ceccherini			free(flattened);
4605a596083SStefano Ceccherini		} else
4615a596083SStefano Ceccherini			err = B_NO_MEMORY;
4623f3ab162SOliver Tappe
4635a596083SStefano Ceccherini		FreeRunArray(runArray);
4645a596083SStefano Ceccherini	}
4653f3ab162SOliver Tappe
466323ddd57Sshatty	return err;
46772f334d1Sshadow}
46875ad8970SStefano Ceccherini
46975ad8970SStefano Ceccherini
470323ddd57Sshattyvoid
471323ddd57SshattyBTextView::AttachedToWindow()
47272f334d1Sshadow{
473323ddd57Sshatty	BView::AttachedToWindow();
4743f3ab162SOliver Tappe
475f3e0a5e9SStefano Ceccherini	SetDrawingMode(B_OP_COPY);
4763f3ab162SOliver Tappe
47775ad8970SStefano Ceccherini	Window()->SetPulseRate(500000);
4783f3ab162SOliver Tappe
479323ddd57Sshatty	fCaretVisible = false;
480323ddd57Sshatty	fCaretTime = 0;
481323ddd57Sshatty	fClickCount = 0;
482323ddd57Sshatty	fClickTime = 0;
483323ddd57Sshatty	fDragOffset = -1;
484323ddd57Sshatty	fActive = false;
4853f3ab162SOliver Tappe
486b8872c02SStephan Aßmus	_AutoResize(true);
4873f3ab162SOliver Tappe
48813ca2d94SAxel Dörfler	_UpdateScrollbars();
4893f3ab162SOliver Tappe
4905a596083SStefano Ceccherini	SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
49172f334d1Sshadow}
49275ad8970SStefano Ceccherini
49375ad8970SStefano Ceccherini
494323ddd57Sshattyvoid
495323ddd57SshattyBTextView::DetachedFromWindow()
49672f334d1Sshadow{
497b032f972SStefano Ceccherini	BView::DetachedFromWindow();
49872f334d1Sshadow}
49975ad8970SStefano Ceccherini
50075ad8970SStefano Ceccherini
501323ddd57Sshattyvoid
502323ddd57SshattyBTextView::Draw(BRect updateRect)
50372f334d1Sshadow{
504323ddd57Sshatty	// what lines need to be drawn?
505588b46eaSOliver Tappe	int32 startLine = _LineAt(BPoint(0.0, updateRect.top));
506588b46eaSOliver Tappe	int32 endLine = _LineAt(BPoint(0.0, updateRect.bottom));
50772f334d1Sshadow
508b18f133dSOliver Tappe	_DrawLines(startLine, endLine, -1, true);
50972f334d1Sshadow}
51075ad8970SStefano Ceccherini
51175ad8970SStefano Ceccherini
512323ddd57Sshattyvoid
513323ddd57SshattyBTextView::MouseDown(BPoint where)
51472f334d1Sshadow{
515323ddd57Sshatty	// should we even bother?
516b032f972SStefano Ceccherini	if (!fEditable && !fSelectable)
517323ddd57Sshatty		return;
5183f3ab162SOliver Tappe
51913ca2d94SAxel Dörfler	_CancelInputMethod();
5203f3ab162SOliver Tappe
5216fb34cf9SStefano Ceccherini	if (!IsFocus())
522323ddd57Sshatty		MakeFocus();
5233f3ab162SOliver Tappe
52499584ef9SStefano Ceccherini	_HideCaret();
5253f3ab162SOliver Tappe
52613ca2d94SAxel Dörfler	_StopMouseTracking();
5273f3ab162SOliver Tappe
5286e6f8fffSStefano Ceccherini	int32 modifiers = 0;
5297cd8f5f9SPhilippe Saint-Pierre	uint32 buttons = 0;
530ab21cedcSJohn Scipione	BMessage* currentMessage = Window()->CurrentMessage();
5316343dc98SStefano Ceccherini	if (currentMessage != NULL) {
5326e6f8fffSStefano Ceccherini		currentMessage->FindInt32("modifiers", &modifiers);
533ab21cedcSJohn Scipione		currentMessage->FindInt32("buttons", (int32*)&buttons);
53491f0846fSRyan Leavengood	}
53591f0846fSRyan Leavengood
53691f0846fSRyan Leavengood	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
53791f0846fSRyan Leavengood		_ShowContextMenu(where);
53891f0846fSRyan Leavengood		return;
539323ddd57Sshatty	}
5406be70a0fSStefano Ceccherini
54191f0846fSRyan Leavengood	BMessenger messenger(this);
54291f0846fSRyan Leavengood	fTrackingMouse = new (nothrow) TextTrackState(messenger);
54391f0846fSRyan Leavengood	if (fTrackingMouse == NULL)
54491f0846fSRyan Leavengood		return;
54591f0846fSRyan Leavengood
5466343dc98SStefano Ceccherini	fTrackingMouse->clickOffset = OffsetAt(where);
5476343dc98SStefano Ceccherini	fTrackingMouse->shiftDown = modifiers & B_SHIFT_KEY;
5486f260d07SOliver Tappe	fTrackingMouse->where = where;
5496343dc98SStefano Ceccherini
5506343dc98SStefano Ceccherini	bigtime_t clickTime = system_time();
551323ddd57Sshatty	bigtime_t clickSpeed = 0;
552323ddd57Sshatty	get_click_speed(&clickSpeed);
5533f3ab162SOliver Tappe	bool multipleClick
5543f3ab162SOliver Tappe		= clickTime - fClickTime < clickSpeed
5559946ba71SOliver Tappe			&& fLastClickOffset == fTrackingMouse->clickOffset;
5566343dc98SStefano Ceccherini
5573f3ab162SOliver Tappe	fWhere = where;
5585a5aa2e3SStefano Ceccherini
559a682d981SStephan Aßmus	SetMouseEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS,
560a682d981SStephan Aßmus		B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
5613f3ab162SOliver Tappe
5626343dc98SStefano Ceccherini	if (fSelStart != fSelEnd && !fTrackingMouse->shiftDown && !multipleClick) {
5636343dc98SStefano Ceccherini		BRegion region;
5646343dc98SStefano Ceccherini		GetTextRegion(fSelStart, fSelEnd, &region);
5656343dc98SStefano Ceccherini		if (region.Contains(where)) {
5666343dc98SStefano Ceccherini			// Setup things for dragging
5676343dc98SStefano Ceccherini			fTrackingMouse->selectionRect = region.Frame();
5683f3ab162SOliver Tappe			fClickCount = 1;
5693f3ab162SOliver Tappe			fClickTime = clickTime;
5709946ba71SOliver Tappe			fLastClickOffset = OffsetAt(where);
5716343dc98SStefano Ceccherini			return;
5726343dc98SStefano Ceccherini		}
5736343dc98SStefano Ceccherini	}
5743f3ab162SOliver Tappe
5756343dc98SStefano Ceccherini	if (multipleClick) {
5763f3ab162SOliver Tappe		if (fClickCount > 3) {
577323ddd57Sshatty			fClickCount = 0;
578323ddd57Sshatty			fClickTime = 0;
579b032f972SStefano Ceccherini		} else {
5803f3ab162SOliver Tappe			fClickCount++;
5816343dc98SStefano Ceccherini			fClickTime = clickTime;
582323ddd57Sshatty		}
5833f3ab162SOliver Tappe	} else if (!fTrackingMouse->shiftDown) {
5849946ba71SOliver Tappe		// If no multiple click yet and shift is not pressed, this is an
5859946ba71SOliver Tappe		// independent first click somewhere into the textview - we initialize
5869946ba71SOliver Tappe		// the corresponding members for handling potential multiple clicks:
5879946ba71SOliver Tappe		fLastClickOffset = fCaretOffset = fTrackingMouse->clickOffset;
588323ddd57Sshatty		fClickCount = 1;
5896343dc98SStefano Ceccherini		fClickTime = clickTime;
5906343dc98SStefano Ceccherini
5916343dc98SStefano Ceccherini		// Deselect any previously selected text
5923f3ab162SOliver Tappe		Select(fTrackingMouse->clickOffset, fTrackingMouse->clickOffset);
593323ddd57Sshatty	}
5943f3ab162SOliver Tappe
5956343dc98SStefano Ceccherini	if (fClickTime == clickTime) {
5966343dc98SStefano Ceccherini		BMessage message(_PING_);
5976343dc98SStefano Ceccherini		message.AddInt64("clickTime", clickTime);
5986343dc98SStefano Ceccherini		delete fClickRunner;
5995b5e713fSStephan Aßmus
6006343dc98SStefano Ceccherini		BMessenger messenger(this);
601a682d981SStephan Aßmus		fClickRunner = new (nothrow) BMessageRunner(messenger, &message,
602a682d981SStephan Aßmus			clickSpeed, 1);
60391b84c77SStefano Ceccherini	}
6045b5e713fSStephan Aßmus
6056343dc98SStefano Ceccherini	if (!fSelectable) {
60613ca2d94SAxel Dörfler		_StopMouseTracking();
6076343dc98SStefano Ceccherini		return;
60813ca2d94SAxel Dörfler	}
6097575ec2dSStefano Ceccherini
6106343dc98SStefano Ceccherini	int32 offset = fSelStart;
6116343dc98SStefano Ceccherini	if (fTrackingMouse->clickOffset > fSelStart)
6126343dc98SStefano Ceccherini		offset = fSelEnd;
6137575ec2dSStefano Ceccherini
6146343dc98SStefano Ceccherini	fTrackingMouse->anchor = offset;
6153f3ab162SOliver Tappe
6163f3ab162SOliver Tappe	MouseMoved(where, B_INSIDE_VIEW, NULL);
617323ddd57Sshatty}
61875ad8970SStefano Ceccherini
61975ad8970SStefano Ceccherini
620323ddd57Sshattyvoid
621323ddd57SshattyBTextView::MouseUp(BPoint where)
622323ddd57Sshatty{
6236343dc98SStefano Ceccherini	BView::MouseUp(where);
62413ca2d94SAxel Dörfler	_PerformMouseUp(where);
6253f3ab162SOliver Tappe
6266343dc98SStefano Ceccherini	delete fDragRunner;
6276343dc98SStefano Ceccherini	fDragRunner = NULL;
628323ddd57Sshatty}
62975ad8970SStefano Ceccherini
63075ad8970SStefano Ceccherini
631323ddd57Sshattyvoid
6321f424632SJohn ScipioneBTextView::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
633323ddd57Sshatty{
634b55f61d6SJohn Scipione	// check if it's a "click'n'move"
63513ca2d94SAxel Dörfler	if (_PerformMouseMoved(where, code))
636322853c8SStefano Ceccherini		return;
6372f86ba45SStephan Aßmus
638323ddd57Sshatty	switch (code) {
639323ddd57Sshatty		case B_ENTERED_VIEW:
640323ddd57Sshatty		case B_INSIDE_VIEW:
6411f424632SJohn Scipione			_TrackMouse(where, dragMessage, true);
642323ddd57Sshatty			break;
64313ca2d94SAxel Dörfler
644323ddd57Sshatty		case B_EXITED_VIEW:
64513ca2d94SAxel Dörfler			_DragCaret(-1);
6461f424632SJohn Scipione			if (Window()->IsActive() && dragMessage == NULL)
647b032f972SStefano Ceccherini				SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
648323ddd57Sshatty			break;
64913ca2d94SAxel Dörfler
650323ddd57Sshatty		default:
6511f424632SJohn Scipione			BView::MouseMoved(where, code, dragMessage);
65272f334d1Sshadow	}
65372f334d1Sshadow}
65475ad8970SStefano Ceccherini
65575ad8970SStefano Ceccherini
656323ddd57Sshattyvoid
6571f424632SJohn ScipioneBTextView::WindowActivated(bool active)
65872f334d1Sshadow{
6591f424632SJohn Scipione	BView::WindowActivated(active);
6603f3ab162SOliver Tappe
6611f424632SJohn Scipione	if (active && IsFocus()) {
662323ddd57Sshatty		if (!fActive)
66313ca2d94SAxel Dörfler			_Activate();
664356acd7dSStefano Ceccherini	} else {
665323ddd57Sshatty		if (fActive)
66613ca2d94SAxel Dörfler			_Deactivate();
6676fb34cf9SStefano Ceccherini	}
6683f3ab162SOliver Tappe
6696fb34cf9SStefano Ceccherini	BPoint where;
6709be774b5SAlex Smith	uint32 buttons;
6715ce268a6SAxel Dörfler	GetMouse(&where, &buttons, false);
6723f3ab162SOliver Tappe
6736fb34cf9SStefano Ceccherini	if (Bounds().Contains(where))
67413ca2d94SAxel Dörfler		_TrackMouse(where, NULL);
67572f334d1Sshadow}
67675ad8970SStefano Ceccherini
67775ad8970SStefano Ceccherini
678323ddd57Sshattyvoid
679ab21cedcSJohn ScipioneBTextView::KeyDown(const char* bytes, int32 numBytes)
68072f334d1Sshadow{
681b032f972SStefano Ceccherini	const char keyPressed = bytes[0];
682b032f972SStefano Ceccherini
683ee694ac8SAxel Dörfler	if (!fEditable) {
684ee694ac8SAxel Dörfler		// only arrow and page keys are allowed
685ee694ac8SAxel Dörfler		// (no need to hide the cursor)
686ee694ac8SAxel Dörfler		switch (keyPressed) {
687ee694ac8SAxel Dörfler			case B_LEFT_ARROW:
688ee694ac8SAxel Dörfler			case B_RIGHT_ARROW:
689ee694ac8SAxel Dörfler			case B_UP_ARROW:
690ee694ac8SAxel Dörfler			case B_DOWN_ARROW:
69113ca2d94SAxel Dörfler				_HandleArrowKey(keyPressed);
692ee694ac8SAxel Dörfler				break;
693ee694ac8SAxel Dörfler
694ee694ac8SAxel Dörfler			case B_HOME:
695ee694ac8SAxel Dörfler			case B_END:
696ee694ac8SAxel Dörfler			case B_PAGE_UP:
697ee694ac8SAxel Dörfler			case B_PAGE_DOWN:
69813ca2d94SAxel Dörfler				_HandlePageKey(keyPressed);
699ee694ac8SAxel Dörfler				break;
700ee694ac8SAxel Dörfler
70113ca2d94SAxel Dörfler			default:
702ee694ac8SAxel Dörfler				BView::KeyDown(bytes, numBytes);
703ee694ac8SAxel Dörfler				break;
704ee694ac8SAxel Dörfler		}
705ee694ac8SAxel Dörfler
7065a5aa2e3SStefano Ceccherini		return;
7075a5aa2e3SStefano Ceccherini	}
7085a5aa2e3SStefano Ceccherini
709323ddd57Sshatty	// hide the cursor and caret
7106f260d07SOliver Tappe	if (IsFocus())
7116f260d07SOliver Tappe		be_app->ObscureCursor();
71299584ef9SStefano Ceccherini	_HideCaret();
713ee694ac8SAxel Dörfler
714b032f972SStefano Ceccherini	switch (keyPressed) {
71572f334d1Sshadow		case B_BACKSPACE:
71613ca2d94SAxel Dörfler			_HandleBackspace();
71772f334d1Sshadow			break;
718ee694ac8SAxel Dörfler
71972f334d1Sshadow		case B_LEFT_ARROW:
72072f334d1Sshadow		case B_RIGHT_ARROW:
721323ddd57Sshatty		case B_UP_ARROW:
722323ddd57Sshatty		case B_DOWN_ARROW:
72313ca2d94SAxel Dörfler			_HandleArrowKey(keyPressed);
72472f334d1Sshadow			break;
725ee694ac8SAxel Dörfler
72672f334d1Sshadow		case B_DELETE:
72713ca2d94SAxel Dörfler			_HandleDelete();
72872f334d1Sshadow			break;
729ee694ac8SAxel Dörfler
730323ddd57Sshatty		case B_HOME:
731323ddd57Sshatty		case B_END:
73272f334d1Sshadow		case B_PAGE_UP:
73372f334d1Sshadow		case B_PAGE_DOWN:
73413ca2d94SAxel Dörfler			_HandlePageKey(keyPressed);
735323ddd57Sshatty			break;
736ee694ac8SAxel Dörfler
737323ddd57Sshatty		case B_ESCAPE:
738323ddd57Sshatty		case B_INSERT:
739323ddd57Sshatty		case B_FUNCTION_KEY:
740323ddd57Sshatty			// ignore, pass it up to superclass
741323ddd57Sshatty			BView::KeyDown(bytes, numBytes);
74272f334d1Sshadow			break;
743ee694ac8SAxel Dörfler
74472f334d1Sshadow		default:
745b55f61d6SJohn Scipione			// bail out if the character is not allowed
746ee694ac8SAxel Dörfler			if (fDisallowedChars
747a682d981SStephan Aßmus				&& fDisallowedChars->HasItem(
748ab21cedcSJohn Scipione					reinterpret_cast<void*>((uint32)keyPressed))) {
749ee694ac8SAxel Dörfler				beep();
750ee694ac8SAxel Dörfler				return;
751ee694ac8SAxel Dörfler			}
752ee694ac8SAxel Dörfler
75313ca2d94SAxel Dörfler			_HandleAlphaKey(bytes, numBytes);
754323ddd57Sshatty			break;
755323ddd57Sshatty	}
756ee694ac8SAxel Dörfler
757323ddd57Sshatty	// draw the caret
75899584ef9SStefano Ceccherini	if (fSelStart == fSelEnd)
75999584ef9SStefano Ceccherini		_ShowCaret();
76072f334d1Sshadow}
76175ad8970SStefano Ceccherini
76275ad8970SStefano Ceccherini
763323ddd57Sshattyvoid
764323ddd57SshattyBTextView::Pulse()
76572f334d1Sshadow{
766356acd7dSStefano Ceccherini	if (fActive && fEditable && fSelStart == fSelEnd) {
767323ddd57Sshatty		if (system_time() > (fCaretTime + 500000.0))
76813ca2d94SAxel Dörfler			_InvertCaret();
76972f334d1Sshadow	}
77072f334d1Sshadow}
77175ad8970SStefano Ceccherini
77275ad8970SStefano Ceccherini
773323ddd57Sshattyvoid
7741f424632SJohn ScipioneBTextView::FrameResized(float newWidth, float newHeight)
77572f334d1Sshadow{
7761f424632SJohn Scipione	BView::FrameResized(newWidth, newHeight);
77713ca2d94SAxel Dörfler	_UpdateScrollbars();
77872f334d1Sshadow}
77975ad8970SStefano Ceccherini
78075ad8970SStefano Ceccherini
781323ddd57Sshattyvoid
7821f424632SJohn ScipioneBTextView::MakeFocus(bool focus)
78372f334d1Sshadow{
7841f424632SJohn Scipione	BView::MakeFocus(focus);
7853f3ab162SOliver Tappe
7861f424632SJohn Scipione	if (focus && Window() != NULL && Window()->IsActive()) {
787323ddd57Sshatty		if (!fActive)
78813ca2d94SAxel Dörfler			_Activate();
789356acd7dSStefano Ceccherini	} else {
790323ddd57Sshatty		if (fActive)
79113ca2d94SAxel Dörfler			_Deactivate();
79213ca2d94SAxel Dörfler	}
79372f334d1Sshadow}
79475ad8970SStefano Ceccherini
79575ad8970SStefano Ceccherini
796323ddd57Sshattyvoid
797ab21cedcSJohn ScipioneBTextView::MessageReceived(BMessage* message)
79872f334d1Sshadow{
799