xref: /haiku/src/kits/interface/TextInput.cpp (revision e661df29804f2703a65e23f5789c3c87c0915298)
1 /*
2  * Copyright 2001-2008, 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 	: BTextView(frame, "_input_", textRect, resizeMask, flags),
33 	fPreviousText(NULL)
34 {
35 	MakeResizable(true);
36 }
37 
38 
39 _BTextInput_::_BTextInput_(BMessage* archive)
40 	: BTextView(archive),
41 	fPreviousText(NULL)
42 {
43 	MakeResizable(true);
44 }
45 
46 
47 _BTextInput_::~_BTextInput_()
48 {
49 	free(fPreviousText);
50 }
51 
52 
53 BArchivable*
54 _BTextInput_::Instantiate(BMessage* archive)
55 {
56 	if (validate_instantiation(archive, "_BTextInput_"))
57 		return new _BTextInput_(archive);
58 
59 	return NULL;
60 }
61 
62 
63 status_t
64 _BTextInput_::Archive(BMessage* data, bool deep) const
65 {
66 	return BTextView::Archive(data, true);
67 }
68 
69 
70 void
71 _BTextInput_::MouseDown(BPoint where)
72 {
73 	if (!IsFocus()) {
74 		MakeFocus(true);
75 		return;
76 	}
77 
78 	// only pass through to base class if we already have focus
79 	BTextView::MouseDown(where);
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 		SelectAll();
133 	} else {
134 		if (strcmp(Text(), fPreviousText) != 0)
135 			TextControl()->Invoke();
136 
137 		free(fPreviousText);
138 		fPreviousText = NULL;
139 	}
140 
141 //	if (Window()) {
142 // TODO: why do we have to invalidate here?
143 // I'm leaving this in, but it looks suspicious... :-)
144 //		Invalidate(Bounds());
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 	{
181 		switch(Alignment())
182 		{
183 			case B_ALIGN_LEFT:
184 				hInset = be_control_look->DefaultLabelSpacing();
185 				break;
186 
187 			case B_ALIGN_RIGHT:
188 				hInset  = textRect.right - textFontWidth;
189 				hInset -= be_control_look->DefaultLabelSpacing();
190 				break;
191 
192 			case B_ALIGN_CENTER:
193 				hInset = (textRect.right - textFontWidth)/ 2.0;
194 				break;
195 
196 			default:
197 				break;
198 		}
199 	}
200 
201 	textRect.InsetBy(hInset, vInset);
202 	SetTextRect(textRect);
203 }
204 
205 
206 void
207 _BTextInput_::SetInitialText()
208 {
209 	free(fPreviousText);
210 	fPreviousText = NULL;
211 
212 	if (Text())
213 		fPreviousText = strdup(Text());
214 }
215 
216 
217 void
218 _BTextInput_::Paste(BClipboard* clipboard)
219 {
220 	BTextView::Paste(clipboard);
221 	Invalidate();
222 }
223 
224 
225 void
226 _BTextInput_::InsertText(const char* inText, int32 inLength,
227 	int32 inOffset, const text_run_array* inRuns)
228 {
229 	// Filter all line breaks, note that inText is not terminated.
230 	if (inLength == 1) {
231 		if (*inText == '\n' || *inText == '\r')
232 			BTextView::InsertText(" ", 1, inOffset, inRuns);
233 		else
234 			BTextView::InsertText(inText, 1, inOffset, inRuns);
235 	} else {
236 		BString filteredText(inText, inLength);
237 		filteredText.ReplaceAll('\n', ' ');
238 		filteredText.ReplaceAll('\r', ' ');
239 		BTextView::InsertText(filteredText.String(), inLength, inOffset,
240 			inRuns);
241 	}
242 
243 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
244 		B_CONTROL_MODIFIED);
245 }
246 
247 
248 void
249 _BTextInput_::DeleteText(int32 fromOffset, int32 toOffset)
250 {
251 	BTextView::DeleteText(fromOffset, toOffset);
252 
253 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
254 		B_CONTROL_MODIFIED);
255 }
256 
257 
258 BTextControl*
259 _BTextInput_::TextControl()
260 {
261 	BTextControl* textControl = NULL;
262 
263 	if (Parent())
264 		textControl = dynamic_cast<BTextControl*>(Parent());
265 
266 	if (!textControl)
267 		debugger("_BTextInput_ should have a BTextControl as parent");
268 
269 	return textControl;
270 }
271 
272 
273 }	// namespace BPrivate
274 
275