xref: /haiku/src/kits/interface/TextInput.cpp (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 
27 namespace 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 
57 BArchivable*
58 _BTextInput_::Instantiate(BMessage* archive)
59 {
60 	if (validate_instantiation(archive, "_BTextInput_"))
61 		return new _BTextInput_(archive);
62 
63 	return NULL;
64 }
65 
66 
67 status_t
68 _BTextInput_::Archive(BMessage* data, bool deep) const
69 {
70 	return BTextView::Archive(data, true);
71 }
72 
73 
74 void
75 _BTextInput_::MouseDown(BPoint where)
76 {
77 	fInMouseDown = true;
78 	BTextView::MouseDown(where);
79 	fInMouseDown = false;
80 }
81 
82 
83 void
84 _BTextInput_::FrameResized(float width, float height)
85 {
86 	BTextView::FrameResized(width, height);
87 }
88 
89 
90 void
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 
120 void
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 
151 BSize
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 
163 void
164 _BTextInput_::SetInitialText()
165 {
166 	free(fPreviousText);
167 	fPreviousText = NULL;
168 
169 	if (Text() != NULL)
170 		fPreviousText = strdup(Text());
171 }
172 
173 
174 void
175 _BTextInput_::Paste(BClipboard* clipboard)
176 {
177 	BTextView::Paste(clipboard);
178 	Invalidate();
179 }
180 
181 
182 void
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 
205 void
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 
215 BTextControl*
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