xref: /haiku/src/kits/interface/TextInput.cpp (revision 0562493379cd52eb7103531f895f10bb8e77c085)
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) + 1.0);
157 	min.width = min.height * 3;
158 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), min);
159 }
160 
161 
162 void
163 _BTextInput_::AlignTextRect()
164 {
165 	// the label font could require the control to be higher than
166 	// necessary for the text view, we compensate this by layouting
167 	// the text rect to be in the middle, normally this means there
168 	// is one pixel spacing on each side
169 	BRect textRect(Bounds());
170 	textRect.left = 0.0;
171 	float vInset = max_c(1, floorf((textRect.Height() - LineHeight(0)) / 2.0));
172 	float hInset = 2;
173 
174 	if (be_control_look)
175 		hInset = be_control_look->DefaultLabelSpacing();
176 
177 	textRect.InsetBy(hInset, vInset);
178 	SetTextRect(textRect);
179 }
180 
181 
182 void
183 _BTextInput_::SetInitialText()
184 {
185 	free(fPreviousText);
186 	fPreviousText = NULL;
187 
188 	if (Text())
189 		fPreviousText = strdup(Text());
190 }
191 
192 
193 void
194 _BTextInput_::Paste(BClipboard* clipboard)
195 {
196 	BTextView::Paste(clipboard);
197 	Invalidate();
198 }
199 
200 
201 void
202 _BTextInput_::InsertText(const char* inText, int32 inLength,
203 	int32 inOffset, const text_run_array* inRuns)
204 {
205 	char* buffer = NULL;
206 
207 	if (strpbrk(inText, "\r\n") && inLength <= 1024) {
208 		buffer = (char*)malloc(inLength);
209 
210 		if (buffer) {
211 			strcpy(buffer, inText);
212 
213 			for (int32 i = 0; i < inLength; i++) {
214 				if (buffer[i] == '\r' || buffer[i] == '\n')
215 					buffer[i] = ' ';
216 			}
217 		}
218 	}
219 
220 	BTextView::InsertText(buffer ? buffer : inText, inLength, inOffset,
221 		inRuns);
222 
223 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
224 		B_CONTROL_MODIFIED);
225 
226 	free(buffer);
227 }
228 
229 
230 void
231 _BTextInput_::DeleteText(int32 fromOffset, int32 toOffset)
232 {
233 	BTextView::DeleteText(fromOffset, toOffset);
234 
235 	TextControl()->InvokeNotify(TextControl()->ModificationMessage(),
236 		B_CONTROL_MODIFIED);
237 }
238 
239 
240 BTextControl*
241 _BTextInput_::TextControl()
242 {
243 	BTextControl* textControl = NULL;
244 
245 	if (Parent())
246 		textControl = dynamic_cast<BTextControl*>(Parent());
247 
248 	if (!textControl)
249 		debugger("_BTextInput_ should have a BTextControl as parent");
250 
251 	return textControl;
252 }
253 
254 
255 }	// namespace BPrivate
256 
257