xref: /haiku/src/kits/interface/TextInput.cpp (revision 5889cb5e7e8e7bfea6072ddfe881f55d364a0cf0)
1 /*
2  * Copyright 2001-2015, 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  */
9 
10 
11 #include "TextInput.h"
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include <ControlLook.h>
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 	AlignTextRect();
89 }
90 
91 
92 void
93 _BTextInput_::KeyDown(const char* bytes, int32 numBytes)
94 {
95 	switch (*bytes) {
96 		case B_ENTER:
97 		{
98 			if (!TextControl()->IsEnabled())
99 				break;
100 
101 			if (fPreviousText == NULL || strcmp(Text(), fPreviousText) != 0) {
102 				TextControl()->Invoke();
103 				free(fPreviousText);
104 				fPreviousText = strdup(Text());
105 			}
106 
107 			SelectAll();
108 			break;
109 		}
110 
111 		case B_TAB:
112 			BView::KeyDown(bytes, numBytes);
113 			break;
114 
115 		default:
116 			BTextView::KeyDown(bytes, numBytes);
117 			break;
118 	}
119 }
120 
121 
122 void
123 _BTextInput_::MakeFocus(bool state)
124 {
125 	if (state == IsFocus())
126 		return;
127 
128 	BTextView::MakeFocus(state);
129 
130 	if (state) {
131 		SetInitialText();
132 		if (!fInMouseDown)
133 			SelectAll();
134 	} else {
135 		if (strcmp(Text(), fPreviousText) != 0)
136 			TextControl()->Invoke();
137 
138 		free(fPreviousText);
139 		fPreviousText = NULL;
140 	}
141 
142 	if (Window() != NULL) {
143 		// Invalidate parent to draw or remove the focus mark
144 		if (BTextControl* parent = dynamic_cast<BTextControl*>(Parent())) {
145 			BRect frame = Frame();
146 			frame.InsetBy(-1.0, -1.0);
147 			parent->Invalidate(frame);
148 		}
149 	}
150 }
151 
152 
153 BSize
154 _BTextInput_::MinSize()
155 {
156 	BSize min;
157 	min.height = ceilf(LineHeight(0) + 2.0);
158 	// we always add at least one pixel vertical inset top/bottom for
159 	// the text rect.
160 	min.width = min.height * 3;
161 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
162 }
163 
164 
165 void
166 _BTextInput_::AlignTextRect()
167 {
168 	// the label font could require the control to be higher than
169 	// necessary for the text view, we compensate this by layouting
170 	// the text rect to be in the middle, normally this means there
171 	// is one pixel spacing on each side
172 	BRect textRect(Bounds());
173 	float vInset = max_c(1,
174 			floorf((textRect.Height() - LineHeight(0)) / 2.0));
175 	float hInset = 2;
176 	float textFontWidth = TextRect().right;
177 
178 	switch (Alignment()) {
179 		case B_ALIGN_LEFT:
180 			hInset = be_control_look->DefaultLabelSpacing();
181 			break;
182 
183 		case B_ALIGN_RIGHT:
184 			hInset  = textRect.right - textFontWidth;
185 			hInset -= be_control_look->DefaultLabelSpacing();
186 			break;
187 
188 		case B_ALIGN_CENTER:
189 			hInset = (textRect.right - textFontWidth) / 2.0;
190 			break;
191 
192 		default:
193 			break;
194 	}
195 
196 	textRect.InsetBy(hInset, vInset);
197 	SetTextRect(textRect);
198 }
199 
200 
201 void
202 _BTextInput_::SetInitialText()
203 {
204 	free(fPreviousText);
205 	fPreviousText = NULL;
206 
207 	if (Text() != NULL)
208 		fPreviousText = strdup(Text());
209 }
210 
211 
212 void
213 _BTextInput_::Paste(BClipboard* clipboard)
214 {
215 	BTextView::Paste(clipboard);
216 	Invalidate();
217 }
218 
219 
220 void
221 _BTextInput_::InsertText(const char* inText, int32 inLength,
222 	int32 inOffset, const text_run_array* inRuns)
223 {
224 	// Filter all line breaks, note that inText is not terminated.
225 	if (inLength == 1) {
226 		if (*inText == '\n' || *inText == '\r')
227 			BTextView::InsertText(" ", 1, inOffset, inRuns);
228 		else
229 			BTextView::InsertText(inText, 1, inOffset, inRuns);
230 	} else {
231 		BString filteredText(inText, inLength);
232 		filteredText.ReplaceAll('\n', ' ');
233 		filteredText.ReplaceAll('\r', ' ');
234 		BTextView::InsertText(filteredText.String(), inLength, inOffset,
235 			inRuns);
236 	}
237 
238 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
239 		B_CONTROL_MODIFIED);
240 }
241 
242 
243 void
244 _BTextInput_::DeleteText(int32 fromOffset, int32 toOffset)
245 {
246 	BTextView::DeleteText(fromOffset, toOffset);
247 
248 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
249 		B_CONTROL_MODIFIED);
250 }
251 
252 
253 BTextControl*
254 _BTextInput_::TextControl()
255 {
256 	BTextControl* textControl = NULL;
257 	if (Parent() != NULL)
258 		textControl = dynamic_cast<BTextControl*>(Parent());
259 
260 	if (textControl == NULL)
261 		debugger("_BTextInput_ should have a BTextControl as parent");
262 
263 	return textControl;
264 }
265 
266 
267 }	// namespace BPrivate
268 
269