xref: /haiku/src/kits/interface/TextInput.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
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 void
122 _BTextInput_::MakeFocus(bool state)
123 {
124 	if (state == IsFocus())
125 		return;
126 
127 	BTextView::MakeFocus(state);
128 
129 	if (state) {
130 		SetInitialText();
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()) {
141 // TODO: why do we have to invalidate here?
142 // I'm leaving this in, but it looks suspicious... :-)
143 //		Invalidate(Bounds());
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 	textRect.left = 0.0;
174 	float vInset = max_c(1, floorf((textRect.Height() - LineHeight(0)) / 2.0));
175 	float hInset = 2;
176 
177 	if (be_control_look)
178 		hInset = be_control_look->DefaultLabelSpacing();
179 
180 	textRect.InsetBy(hInset, vInset);
181 	SetTextRect(textRect);
182 }
183 
184 
185 void
186 _BTextInput_::SetInitialText()
187 {
188 	free(fPreviousText);
189 	fPreviousText = NULL;
190 
191 	if (Text())
192 		fPreviousText = strdup(Text());
193 }
194 
195 
196 void
197 _BTextInput_::Paste(BClipboard* clipboard)
198 {
199 	BTextView::Paste(clipboard);
200 	Invalidate();
201 }
202 
203 
204 void
205 _BTextInput_::InsertText(const char* inText, int32 inLength,
206 	int32 inOffset, const text_run_array* inRuns)
207 {
208 	// Filter all line breaks, note that inText is not terminated.
209 	if (inLength == 1) {
210 		if (*inText == '\n' || *inText == '\r')
211 			BTextView::InsertText(" ", 1, inOffset, inRuns);
212 		else
213 			BTextView::InsertText(inText, 1, inOffset, inRuns);
214 	} else {
215 		BString filteredText(inText, inLength);
216 		filteredText.ReplaceAll('\n', ' ');
217 		filteredText.ReplaceAll('\r', ' ');
218 		BTextView::InsertText(filteredText.String(), inLength, inOffset,
219 			inRuns);
220 	}
221 
222 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
223 		B_CONTROL_MODIFIED);
224 }
225 
226 
227 void
228 _BTextInput_::DeleteText(int32 fromOffset, int32 toOffset)
229 {
230 	BTextView::DeleteText(fromOffset, toOffset);
231 
232 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
233 		B_CONTROL_MODIFIED);
234 }
235 
236 
237 BTextControl*
238 _BTextInput_::TextControl()
239 {
240 	BTextControl* textControl = NULL;
241 
242 	if (Parent())
243 		textControl = dynamic_cast<BTextControl*>(Parent());
244 
245 	if (!textControl)
246 		debugger("_BTextInput_ should have a BTextControl as parent");
247 
248 	return textControl;
249 }
250 
251 
252 }	// namespace BPrivate
253 
254