xref: /haiku/src/kits/interface/TextControl.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		TextControl.cpp
23 //	Author:			Frans van Nispen (xlr8@tref.nl)
24 //	Description:	BTextControl displays text that can act like a control.
25 //------------------------------------------------------------------------------
26 
27 // Standard Includes -----------------------------------------------------------
28 #include <stdio.h>
29 
30 // System Includes -------------------------------------------------------------
31 #include <TextControl.h>
32 #include <Window.h>
33 #include <Message.h>
34 
35 // Project Includes ------------------------------------------------------------
36 
37 // Local Includes --------------------------------------------------------------
38 #include "TextInput.h"
39 
40 // Local Defines ---------------------------------------------------------------
41 
42 // Globals ---------------------------------------------------------------------
43 
44 //------------------------------------------------------------------------------
45 BTextControl::BTextControl(BRect frame, const char *name, const char *label,
46 						   const char *text, BMessage *message, uint32 mask,
47 						   uint32 flags)
48 	:	BControl(frame, name, label, message, mask, flags | B_FRAME_EVENTS)
49 {
50 	InitData(label, text);
51 
52 	BRect bounds(Bounds());
53 
54 	font_height fh;
55 	GetFontHeight(&fh);
56 
57 	float height = (float)ceil(fh.ascent + fh.descent + fh.leading);
58 	float lineHeight = fText->LineHeight(0);
59 
60 	ResizeTo(bounds.Width(), height + 8);
61 
62 	BRect textBounds(fText->Bounds());
63 
64 	fText->ResizeTo(textBounds.Width(), lineHeight + 4);
65 	fText->MoveBy(0, (bounds.Height() - height) / 2.0f);
66 }
67 //------------------------------------------------------------------------------
68 BTextControl::~BTextControl()
69 {
70 	SetModificationMessage(NULL);
71 }
72 //------------------------------------------------------------------------------
73 BTextControl::BTextControl(BMessage *data)
74 	:	BControl(data)
75 {
76 	InitData(Label(), NULL, data);
77 
78 	int32 _a_label = B_ALIGN_LEFT;
79 	int32 _a_text = B_ALIGN_LEFT;
80 
81 	if (data->HasInt32("_a_label"))
82 		data->FindInt32("_a_label", &_a_label);
83 
84 	if (data->HasInt32("_a_text"))
85 		data->FindInt32("_a_text", &_a_text);
86 
87 	SetAlignment((alignment)_a_label, (alignment)_a_text);
88 
89 	if (data->HasFloat("_divide"))
90 		data->FindFloat("_a_text", &fDivider);
91 
92 	if (data->HasMessage("_mod_msg"))
93 	{
94 		BMessage *_mod_msg = new BMessage;
95 		data->FindMessage("_mod_msg", _mod_msg);
96 		SetModificationMessage(_mod_msg);
97 	}
98 }
99 //------------------------------------------------------------------------------
100 BArchivable* BTextControl::Instantiate(BMessage *archive)
101 {
102 	if (validate_instantiation(archive, "BTextControl"))
103 		return new BTextControl(archive);
104 	else
105 		return NULL;
106 }
107 //------------------------------------------------------------------------------
108 status_t BTextControl::Archive(BMessage *data, bool deep) const
109 {
110 	BView::Archive(data, deep);
111 
112 	alignment _a_label, _a_text;
113 
114 	GetAlignment(&_a_label, &_a_text);
115 
116 	data->AddInt32("_a_label", _a_label);
117 	data->AddInt32("_a_text", _a_text);
118 
119 	data->AddFloat("_divide", Divider());
120 
121 	if (ModificationMessage())
122 		data->AddMessage("_mod_msg", ModificationMessage());
123 
124 	return B_OK;
125 }
126 //------------------------------------------------------------------------------
127 void BTextControl::SetText(const char *text)
128 {
129 	if (InvokeKind() != B_CONTROL_INVOKED)
130 		return;
131 
132 	fText->SetText(text);
133 
134 	if (IsFocus())
135 		fText->SetInitialText();
136 
137 	fText->Invalidate();
138 }
139 //------------------------------------------------------------------------------
140 const char *BTextControl::Text() const
141 {
142 	return fText->Text();
143 }
144 //------------------------------------------------------------------------------
145 void BTextControl::SetValue(int32 value)
146 {
147 	BControl::SetValue(value);
148 }
149 //------------------------------------------------------------------------------
150 status_t BTextControl::Invoke(BMessage *msg)
151 {
152 	return BControl::Invoke(msg);
153 }
154 //------------------------------------------------------------------------------
155 BTextView* BTextControl::TextView() const
156 {
157 	return fText;
158 }
159 //------------------------------------------------------------------------------
160 void BTextControl::SetModificationMessage(BMessage *message)
161 {
162 	if (fModificationMessage)
163 		delete fModificationMessage;
164 
165 	fModificationMessage = message;
166 }
167 //------------------------------------------------------------------------------
168 BMessage* BTextControl::ModificationMessage() const
169 {
170 	return fModificationMessage;
171 }
172 //------------------------------------------------------------------------------
173 void BTextControl::SetAlignment(alignment label, alignment text)
174 {
175 	fText->SetAlignment(text);
176 	fText->AlignTextRect();
177 
178 	if (fLabelAlign != label)
179 	{
180 		fLabelAlign = label;
181 		Invalidate();
182 	}
183 }
184 //------------------------------------------------------------------------------
185 void BTextControl::GetAlignment(alignment *label, alignment *text) const
186 {
187 	*label = fLabelAlign;
188 	*text = fText->Alignment();
189 }
190 //------------------------------------------------------------------------------
191 void BTextControl::SetDivider(float dividing_line)
192 {
193 	float dx = fDivider - dividing_line;
194 
195 	fDivider = dividing_line;
196 
197 	fText->MoveBy(-dx, 0.0f);
198 	fText->ResizeBy(dx, 0.0f);
199 
200 	if (Window())
201 	{
202 		fText->Invalidate();
203 		Invalidate();
204 	}
205 }
206 //------------------------------------------------------------------------------
207 float BTextControl::Divider() const
208 {
209 	return fDivider;
210 }
211 //------------------------------------------------------------------------------
212 void BTextControl::Draw(BRect updateRect)
213 {
214 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
215 	lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
216 	lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT),
217 	lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT),
218 	darken1 = tint_color(no_tint, B_DARKEN_1_TINT),
219 	darken2 = tint_color(no_tint, B_DARKEN_2_TINT),
220 	darken4 = tint_color(no_tint, B_DARKEN_4_TINT),
221 	darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT),
222 	nav = ui_color(B_KEYBOARD_NAVIGATION_COLOR);
223 
224 	BRect bounds(Bounds());
225 	bool enabled = IsEnabled();
226 	bool active = false;
227 
228 	if (fText->IsFocus() && Window()->IsActive())
229 		active = true;
230 
231 	BRect rect(fText->Frame());
232 	rect.InsetBy(-1.0f, -1.0f);
233 
234 	if (active)
235 	{
236 		SetHighColor(nav);
237 		StrokeRect(rect);
238 	}
239 	else
240 	{
241 		if (enabled)
242 			SetHighColor(darken4);
243 		else
244 			SetHighColor(darken2);
245 
246 		StrokeLine(rect.LeftTop(), rect.LeftBottom());
247 		StrokeLine(rect.LeftTop(), rect.RightTop());
248 
249 		SetHighColor(no_tint);
250 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
251 		StrokeLine(BPoint(rect.right, rect.top + 1.0f));
252 	}
253 
254 	rect.InsetBy(-1.0f, -1.0f);
255 
256 	if (enabled)
257 		SetHighColor(darken1);
258 	else
259 		SetHighColor(no_tint);
260 
261 	StrokeLine(rect.LeftBottom(), rect.LeftTop());
262 	StrokeLine(rect.RightTop());
263 
264 	if (enabled)
265 		SetHighColor(lighten2);
266 	else
267 		SetHighColor(lighten1);
268 
269 	StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
270 	StrokeLine(BPoint(rect.right, rect.top + 1.0f), rect.RightBottom());
271 
272 	if (Label())
273 	{
274 		font_height fh;
275 		GetFontHeight(&fh);
276 
277 		float y = (float)ceil(fh.ascent + fh.descent + fh.leading) + 2.0f;
278 		float x;
279 
280 		switch (fLabelAlign)
281 		{
282 			case B_ALIGN_RIGHT:
283 				x = fDivider - StringWidth(Label()) - 3.0f;
284 				break;
285 
286 			case B_ALIGN_CENTER:
287 				x = fDivider - StringWidth(Label()) / 2.0f;
288 				break;
289 
290 			default:
291 				x = 3.0f;
292 				break;
293 		}
294 
295 		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
296 					 IsEnabled() ? B_DARKEN_MAX_TINT : B_DISABLED_LABEL_TINT));
297 		DrawString(Label(), BPoint(x, y));
298 	}
299 }
300 //------------------------------------------------------------------------------
301 void BTextControl::MouseDown(BPoint where)
302 {
303 	if (!fText->IsFocus())
304 	{
305 		fText->MakeFocus(true);
306 		fText->SelectAll();
307 	}
308 }
309 //------------------------------------------------------------------------------
310 void BTextControl::AttachedToWindow()
311 {
312 	BControl::AttachedToWindow();
313 
314 	bool enabled = IsEnabled();
315 	rgb_color textColor;
316 	rgb_color color = HighColor();
317 	BFont font;
318 
319 	fText->GetFontAndColor(0, &font, &color);
320 
321 	if (enabled)
322 		textColor = color;
323 	else
324 		textColor = tint_color(color, B_LIGHTEN_2_TINT);
325 
326 	fText->SetFontAndColor(&font, B_FONT_ALL, &textColor);
327 
328 	if (enabled)
329 	{
330 		color.red = 255;
331 		color.green = 255;
332 		color.blue = 255;
333 	}
334 	else
335 		color = tint_color(color, B_LIGHTEN_2_TINT);
336 
337 	fText->SetViewColor(color);
338 	fText->SetLowColor(color);
339 
340 	fText->MakeEditable(enabled);
341 }
342 //------------------------------------------------------------------------------
343 void BTextControl::MakeFocus(bool state)
344 {
345 	fText->MakeFocus(state);
346 
347 	if (state)
348 		fText->SelectAll();
349 }
350 //------------------------------------------------------------------------------
351 void BTextControl::SetEnabled(bool state)
352 {
353 	if (IsEnabled() == state)
354 		return;
355 
356 	if (Window())
357 	{
358 		fText->MakeEditable(state);
359 
360 		rgb_color textColor;
361 		rgb_color color = {0, 0, 0, 255};
362 		BFont font;
363 
364 		fText->GetFontAndColor(0, &font, &color);
365 
366 		if (state)
367 			textColor = color;
368 		else
369 			textColor = tint_color(color, B_DISABLED_LABEL_TINT);
370 
371 		fText->SetFontAndColor(&font, B_FONT_ALL, &textColor);
372 
373 		if (state)
374 		{
375 			color.red = 255;
376 			color.green = 255;
377 			color.blue = 255;
378 		}
379 		else
380 			color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
381 				B_LIGHTEN_2_TINT);
382 
383 		fText->SetViewColor(color);
384 		fText->SetLowColor(color);
385 
386 		fText->Invalidate();
387 		Window()->UpdateIfNeeded();
388 	}
389 
390 	BControl::SetEnabled(state);
391 }
392 //------------------------------------------------------------------------------
393 void BTextControl::GetPreferredSize(float *width, float *height)
394 {
395 	BFont font;
396 	GetFont(&font);
397 	font_height fh;
398 	font.GetHeight(&fh);
399 
400 	*height = (float)ceil(fh.ascent + fh.descent + fh.leading) + 7.0f;
401 
402 	// TODO: this one I need to find out
403 	*width = 4.0f + (float)ceil(font.StringWidth(Label()))*2.0f;
404 }
405 //------------------------------------------------------------------------------
406 void BTextControl::ResizeToPreferred()
407 {
408 	float w;
409 	float h;
410 	GetPreferredSize(&w, &h);
411 	BView::ResizeTo(w,h);
412 }
413 //------------------------------------------------------------------------------
414 void BTextControl::SetFlags(uint32 flags)
415 {
416 	if (!fSkipSetFlags)
417 	{
418 		// If the textview is navigable, set it to not navigable if needed
419 		// Else if it is not navigable, set it to navigable if needed
420 		if (fText->Flags() & B_NAVIGABLE)
421 		{
422 			if (!(flags & B_NAVIGABLE))
423 				fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
424 		}
425 		else
426 		{
427 			if (flags & B_NAVIGABLE)
428 				fText->SetFlags(fText->Flags() | B_NAVIGABLE);
429 		}
430 
431 		// Don't make this one navigable
432 		flags &= ~B_NAVIGABLE;
433 	}
434 
435 	BView::SetFlags(flags);
436 }
437 //------------------------------------------------------------------------------
438 void BTextControl::MessageReceived(BMessage *msg)
439 {
440 	switch(msg->what)
441 	{
442 		case B_SET_PROPERTY:
443 		case B_GET_PROPERTY:
444 			// TODO
445 			break;
446 		default:
447 			BControl::MessageReceived(msg);
448 			break;
449 	}
450 }
451 //------------------------------------------------------------------------------
452 BHandler *BTextControl::ResolveSpecifier(BMessage *msg, int32 index,
453 										 BMessage *specifier, int32 form,
454 										 const char *property)
455 {
456 	/*
457 	BPropertyInfo propInfo(prop_list);
458 	BHandler *target = NULL;
459 
460 	if (propInfo.FindMatch(message, 0, specifier, what, property) < B_OK)
461 		return BControl::ResolveSpecifier(message, index, specifier, what,
462 			property);
463 	else
464 		return this;
465 	*/
466 	return BControl::ResolveSpecifier(msg, index, specifier, form, property);
467 }
468 //------------------------------------------------------------------------------
469 status_t BTextControl::GetSupportedSuites(BMessage *data)
470 {
471 	return BControl::GetSupportedSuites(data);
472 }
473 //------------------------------------------------------------------------------
474 void BTextControl::MouseUp(BPoint pt)
475 {
476 	BControl::MouseUp(pt);
477 }
478 //------------------------------------------------------------------------------
479 void BTextControl::MouseMoved(BPoint pt, uint32 code, const BMessage *msg)
480 {
481 	BControl::MouseMoved(pt, code, msg);
482 }
483 //------------------------------------------------------------------------------
484 void BTextControl::DetachedFromWindow()
485 {
486 	BControl::DetachedFromWindow();
487 }
488 //------------------------------------------------------------------------------
489 void BTextControl::AllAttached()
490 {
491 	BControl::AllAttached();
492 }
493 //------------------------------------------------------------------------------
494 void BTextControl::AllDetached()
495 {
496 	BControl::AllDetached();
497 }
498 //------------------------------------------------------------------------------
499 void BTextControl::FrameMoved(BPoint newPosition)
500 {
501 	BControl::FrameMoved(newPosition);
502 }
503 //------------------------------------------------------------------------------
504 void BTextControl::FrameResized(float newWidth, float newHeight)
505 {
506 	BControl::FrameResized(newWidth, newHeight);
507 }
508 //------------------------------------------------------------------------------
509 void BTextControl::WindowActivated(bool active)
510 {
511 	if (fText->IsFocus())
512 		Draw(Bounds());
513 }
514 //------------------------------------------------------------------------------
515 status_t BTextControl::Perform(perform_code d, void *arg)
516 {
517 	return BControl::Perform(d, arg);
518 }
519 //------------------------------------------------------------------------------
520 void BTextControl::_ReservedTextControl1() {}
521 void BTextControl::_ReservedTextControl2() {}
522 void BTextControl::_ReservedTextControl3() {}
523 void BTextControl::_ReservedTextControl4() {}
524 //------------------------------------------------------------------------------
525 BTextControl &BTextControl::operator=(const BTextControl&)
526 {
527 	return *this;
528 }
529 //------------------------------------------------------------------------------
530 void BTextControl::CommitValue()
531 {
532 }
533 //------------------------------------------------------------------------------
534 void BTextControl::InitData(const char *label, const char *initial_text,
535 							BMessage *data)
536 {
537 	BRect bounds(Bounds());
538 
539 	fText = NULL;
540 	//fLabel = NULL;
541 	fModificationMessage = NULL;
542 	fLabelAlign = B_ALIGN_LEFT;
543 	fDivider = 0.0f;
544 	fPrevWidth = 0;
545 	fPrevHeight = 0;
546 	//fClean = true;
547 	fSkipSetFlags = false;
548 
549 	int32 flags = 0;
550 
551 	BFont font(be_bold_font);
552 
553 	if (!data || !data->HasString("_fname"))
554 		flags = 1;
555 
556 	if (!data || !data->HasFloat("_fflt"))
557 		flags |= 2;
558 
559 	if (flags != 0)
560 		SetFont(&font, flags);
561 
562 	if (label)
563 		fDivider = bounds.Width() / 2.0f;
564 
565 	if (Flags() & B_NAVIGABLE)
566 	{
567 		fSkipSetFlags = true;
568 		SetFlags(Flags() & ~B_NAVIGABLE);
569 		fSkipSetFlags = false;
570 	}
571 
572 	if (data)
573 		fText = (_BTextInput_*)FindView("_input_");
574 	else
575 	{
576 		BRect frame(fDivider, bounds.top + 2.0f, bounds.right - 2.0f,
577 			bounds.bottom - 2.0f);
578 		BRect textRect(frame.OffsetToCopy(0.0f, 0.0f));
579 
580 		fText = new _BTextInput_(frame, textRect,
581 			B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS |
582 			B_NAVIGABLE);
583 
584 		AddChild(fText);
585 
586 		SetText(initial_text);
587 		fText->SetAlignment(B_ALIGN_LEFT);
588 		fText->AlignTextRect();
589 	}
590 }
591 //------------------------------------------------------------------------------
592 
593 /*
594  * $Log $
595  *
596  * $Id  $
597  *
598  */
599 
600