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