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