1/*
2 * Copyright 2001-2020 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Frans van Nispen (xlr8@tref.nl)
7 *		Marc Flerackers (mflerackers@androme.be)
8 *		John Scipione (jscipione@gmail.com)
9 */
10
11
12#include "TextInput.h"
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18#include <InterfaceDefs.h>
19#include <LayoutUtils.h>
20#include <Message.h>
21#include <String.h>
22#include <TextControl.h>
23#include <TextView.h>
24#include <Window.h>
25
26
27namespace BPrivate {
28
29
30_BTextInput_::_BTextInput_(BRect frame, BRect textRect, uint32 resizeMask,
31	uint32 flags)
32	:
33	BTextView(frame, "_input_", textRect, resizeMask, flags),
34	fPreviousText(NULL),
35	fInMouseDown(false)
36{
37	MakeResizable(true);
38}
39
40
41_BTextInput_::_BTextInput_(BMessage* archive)
42	:
43	BTextView(archive),
44	fPreviousText(NULL),
45	fInMouseDown(false)
46{
47	MakeResizable(true);
48}
49
50
51_BTextInput_::~_BTextInput_()
52{
53	free(fPreviousText);
54}
55
56
57BArchivable*
58_BTextInput_::Instantiate(BMessage* archive)
59{
60	if (validate_instantiation(archive, "_BTextInput_"))
61		return new _BTextInput_(archive);
62
63	return NULL;
64}
65
66
67status_t
68_BTextInput_::Archive(BMessage* data, bool deep) const
69{
70	return BTextView::Archive(data, true);
71}
72
73
74void
75_BTextInput_::MouseDown(BPoint where)
76{
77	fInMouseDown = true;
78	BTextView::MouseDown(where);
79	fInMouseDown = false;
80}
81
82
83void
84_BTextInput_::FrameResized(float width, float height)
85{
86	BTextView::FrameResized(width, height);
87}
88
89
90void
91_BTextInput_::KeyDown(const char* bytes, int32 numBytes)
92{
93	switch (*bytes) {
94		case B_ENTER:
95		{
96			if (!TextControl()->IsEnabled())
97				break;
98
99			if (fPreviousText == NULL || strcmp(Text(), fPreviousText) != 0) {
100				TextControl()->Invoke();
101				free(fPreviousText);
102				fPreviousText = strdup(Text());
103			}
104
105			SelectAll();
106			break;
107		}
108
109		case B_TAB:
110			BView::KeyDown(bytes, numBytes);
111			break;
112
113		default:
114			BTextView::KeyDown(bytes, numBytes);
115			break;
116	}
117}
118
119
120void
121_BTextInput_::MakeFocus(bool state)
122{
123	if (state == IsFocus())
124		return;
125
126	BTextView::MakeFocus(state);
127
128	if (state) {
129		SetInitialText();
130		if (!fInMouseDown)
131			SelectAll();
132	} else {
133		if (strcmp(Text(), fPreviousText) != 0)
134			TextControl()->Invoke();
135
136		free(fPreviousText);
137		fPreviousText = NULL;
138	}
139
140	if (Window() != NULL) {
141		// Invalidate parent to draw or remove the focus mark
142		if (BTextControl* parent = dynamic_cast<BTextControl*>(Parent())) {
143			BRect frame = Frame();
144			frame.InsetBy(-1.0, -1.0);
145			parent->Invalidate(frame);
146		}
147	}
148}
149
150
151BSize
152_BTextInput_::MinSize()
153{
154	BSize min;
155	min.height = ceilf(LineHeight(0) + 2.0);
156	// we always add at least one pixel vertical inset top/bottom for
157	// the text rect.
158	min.width = min.height * 3;
159	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
160}
161
162
163void
164_BTextInput_::SetInitialText()
165{
166	free(fPreviousText);
167	fPreviousText = NULL;
168
169	if (Text() != NULL)
170		fPreviousText = strdup(Text());
171}
172
173
174void
175_BTextInput_::Paste(BClipboard* clipboard)
176{
177	BTextView::Paste(clipboard);
178	Invalidate();
179}
180
181
182void
183_BTextInput_::InsertText(const char* inText, int32 inLength,
184	int32 inOffset, const text_run_array* inRuns)
185{
186	// Filter all line breaks, note that inText is not terminated.
187	if (inLength == 1) {
188		if (*inText == '\n' || *inText == '\r')
189			BTextView::InsertText(" ", 1, inOffset, inRuns);
190		else
191			BTextView::InsertText(inText, 1, inOffset, inRuns);
192	} else {
193		BString filteredText(inText, inLength);
194		filteredText.ReplaceAll('\n', ' ');
195		filteredText.ReplaceAll('\r', ' ');
196		BTextView::InsertText(filteredText.String(), inLength, inOffset,
197			inRuns);
198	}
199
200	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
201		B_CONTROL_MODIFIED);
202}
203
204
205void
206_BTextInput_::DeleteText(int32 fromOffset, int32 toOffset)
207{
208	BTextView::DeleteText(fromOffset, toOffset);
209
210	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
211		B_CONTROL_MODIFIED);
212}
213
214
215BTextControl*
216_BTextInput_::TextControl()
217{
218	BTextControl* textControl = NULL;
219	if (Parent() != NULL)
220		textControl = dynamic_cast<BTextControl*>(Parent());
221
222	if (textControl == NULL)
223		debugger("_BTextInput_ should have a BTextControl as parent");
224
225	return textControl;
226}
227
228
229}	// namespace BPrivate
230
231