xref: /haiku/src/kits/interface/TextInput.cpp (revision e433b3cfc3f089f7681f6d4e81d43f950ca6a440)
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 {
36 	MakeResizable(true);
37 }
38 
39 
40 _BTextInput_::_BTextInput_(BMessage* archive)
41 	:
42 	BTextView(archive),
43 	fPreviousText(NULL)
44 {
45 	MakeResizable(true);
46 }
47 
48 
49 _BTextInput_::~_BTextInput_()
50 {
51 	free(fPreviousText);
52 }
53 
54 
55 BArchivable*
56 _BTextInput_::Instantiate(BMessage* archive)
57 {
58 	if (validate_instantiation(archive, "_BTextInput_"))
59 		return new _BTextInput_(archive);
60 
61 	return NULL;
62 }
63 
64 
65 status_t
66 _BTextInput_::Archive(BMessage* data, bool deep) const
67 {
68 	return BTextView::Archive(data, true);
69 }
70 
71 
72 void
73 _BTextInput_::MouseDown(BPoint where)
74 {
75 	if (!IsFocus()) {
76 		MakeFocus(true);
77 		return;
78 	}
79 
80 	// only pass through to base class if we already have focus
81 	BTextView::MouseDown(where);
82 }
83 
84 
85 void
86 _BTextInput_::FrameResized(float width, float height)
87 {
88 	BTextView::FrameResized(width, height);
89 
90 	AlignTextRect();
91 }
92 
93 
94 void
95 _BTextInput_::KeyDown(const char* bytes, int32 numBytes)
96 {
97 	switch (*bytes) {
98 		case B_ENTER:
99 		{
100 			if (!TextControl()->IsEnabled())
101 				break;
102 
103 			if (fPreviousText == NULL || strcmp(Text(), fPreviousText) != 0) {
104 				TextControl()->Invoke();
105 				free(fPreviousText);
106 				fPreviousText = strdup(Text());
107 			}
108 
109 			SelectAll();
110 			break;
111 		}
112 
113 		case B_TAB:
114 			BView::KeyDown(bytes, numBytes);
115 			break;
116 
117 		default:
118 			BTextView::KeyDown(bytes, numBytes);
119 			break;
120 	}
121 }
122 
123 
124 void
125 _BTextInput_::MakeFocus(bool state)
126 {
127 	if (state == IsFocus())
128 		return;
129 
130 	BTextView::MakeFocus(state);
131 
132 	if (state) {
133 		SetInitialText();
134 		SelectAll();
135 	} else {
136 		if (strcmp(Text(), fPreviousText) != 0)
137 			TextControl()->Invoke();
138 
139 		free(fPreviousText);
140 		fPreviousText = NULL;
141 	}
142 
143 	if (Window() != NULL) {
144 		// Invalidate parent to draw or remove the focus mark
145 		if (BTextControl* parent = dynamic_cast<BTextControl*>(Parent())) {
146 			BRect frame = Frame();
147 			frame.InsetBy(-1.0, -1.0);
148 			parent->Invalidate(frame);
149 		}
150 	}
151 }
152 
153 
154 BSize
155 _BTextInput_::MinSize()
156 {
157 	BSize min;
158 	min.height = ceilf(LineHeight(0) + 2.0);
159 	// we always add at least one pixel vertical inset top/bottom for
160 	// the text rect.
161 	min.width = min.height * 3;
162 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
163 }
164 
165 
166 void
167 _BTextInput_::AlignTextRect()
168 {
169 	// the label font could require the control to be higher than
170 	// necessary for the text view, we compensate this by layouting
171 	// the text rect to be in the middle, normally this means there
172 	// is one pixel spacing on each side
173 	BRect textRect(Bounds());
174 	float vInset = max_c(1,
175 			floorf((textRect.Height() - LineHeight(0)) / 2.0));
176 	float hInset = 2;
177 	float textFontWidth = TextRect().right;
178 
179 	if (be_control_look != NULL)  {
180 		switch (Alignment()) {
181 			case B_ALIGN_LEFT:
182 				hInset = be_control_look->DefaultLabelSpacing();
183 				break;
184 
185 			case B_ALIGN_RIGHT:
186 				hInset  = textRect.right - textFontWidth;
187 				hInset -= be_control_look->DefaultLabelSpacing();
188 				break;
189 
190 			case B_ALIGN_CENTER:
191 				hInset = (textRect.right - textFontWidth) / 2.0;
192 				break;
193 
194 			default:
195 				break;
196 		}
197 	}
198 
199 	textRect.InsetBy(hInset, vInset);
200 	SetTextRect(textRect);
201 }
202 
203 
204 void
205 _BTextInput_::SetInitialText()
206 {
207 	free(fPreviousText);
208 	fPreviousText = NULL;
209 
210 	if (Text() != NULL)
211 		fPreviousText = strdup(Text());
212 }
213 
214 
215 void
216 _BTextInput_::Paste(BClipboard* clipboard)
217 {
218 	BTextView::Paste(clipboard);
219 	Invalidate();
220 }
221 
222 
223 void
224 _BTextInput_::InsertText(const char* inText, int32 inLength,
225 	int32 inOffset, const text_run_array* inRuns)
226 {
227 	// Filter all line breaks, note that inText is not terminated.
228 	if (inLength == 1) {
229 		if (*inText == '\n' || *inText == '\r')
230 			BTextView::InsertText(" ", 1, inOffset, inRuns);
231 		else
232 			BTextView::InsertText(inText, 1, inOffset, inRuns);
233 	} else {
234 		BString filteredText(inText, inLength);
235 		filteredText.ReplaceAll('\n', ' ');
236 		filteredText.ReplaceAll('\r', ' ');
237 		BTextView::InsertText(filteredText.String(), inLength, inOffset,
238 			inRuns);
239 	}
240 
241 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
242 		B_CONTROL_MODIFIED);
243 }
244 
245 
246 void
247 _BTextInput_::DeleteText(int32 fromOffset, int32 toOffset)
248 {
249 	BTextView::DeleteText(fromOffset, toOffset);
250 
251 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
252 		B_CONTROL_MODIFIED);
253 }
254 
255 
256 BTextControl*
257 _BTextInput_::TextControl()
258 {
259 	BTextControl* textControl = NULL;
260 	if (Parent() != NULL)
261 		textControl = dynamic_cast<BTextControl*>(Parent());
262 
263 	if (textControl == NULL)
264 		debugger("_BTextInput_ should have a BTextControl as parent");
265 
266 	return textControl;
267 }
268 
269 
270 }	// namespace BPrivate
271 
272