xref: /haiku/src/kits/interface/TextControl.cpp (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
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 	bool enabled = IsEnabled();
225 	bool active = false;
226 
227 	if (fText->IsFocus() && Window()->IsActive())
228 		active = true;
229 
230 	BRect rect(fText->Frame());
231 	rect.InsetBy(-1.0f, -1.0f);
232 
233 	if (active)
234 	{
235 		SetHighColor(nav);
236 		StrokeRect(rect);
237 	}
238 	else
239 	{
240 		if (enabled)
241 			SetHighColor(darken4);
242 		else
243 			SetHighColor(darken2);
244 
245 		StrokeLine(rect.LeftTop(), rect.LeftBottom());
246 		StrokeLine(rect.LeftTop(), rect.RightTop());
247 
248 		SetHighColor(no_tint);
249 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
250 		StrokeLine(BPoint(rect.right, rect.top + 1.0f));
251 	}
252 
253 	rect.InsetBy(-1.0f, -1.0f);
254 
255 	if (enabled)
256 		SetHighColor(darken1);
257 	else
258 		SetHighColor(no_tint);
259 
260 	StrokeLine(rect.LeftBottom(), rect.LeftTop());
261 	StrokeLine(rect.RightTop());
262 
263 	if (enabled)
264 		SetHighColor(lighten2);
265 	else
266 		SetHighColor(lighten1);
267 
268 	StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
269 	StrokeLine(BPoint(rect.right, rect.top + 1.0f), rect.RightBottom());
270 
271 	if (Label())
272 	{
273 		font_height fh;
274 		GetFontHeight(&fh);
275 
276 		float y = (float)ceil(fh.ascent + fh.descent + fh.leading) + 2.0f;
277 		float x;
278 
279 		switch (fLabelAlign)
280 		{
281 			case B_ALIGN_RIGHT:
282 				x = fDivider - StringWidth(Label()) - 3.0f;
283 				break;
284 
285 			case B_ALIGN_CENTER:
286 				x = fDivider - StringWidth(Label()) / 2.0f;
287 				break;
288 
289 			default:
290 				x = 3.0f;
291 				break;
292 		}
293 
294 		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
295 					 IsEnabled() ? B_DARKEN_MAX_TINT : B_DISABLED_LABEL_TINT));
296 		DrawString(Label(), BPoint(x, y));
297 	}
298 }
299 //------------------------------------------------------------------------------
300 void BTextControl::MouseDown(BPoint where)
301 {
302 	if (!fText->IsFocus())
303 	{
304 		fText->MakeFocus(true);
305 		fText->SelectAll();
306 	}
307 }
308 //------------------------------------------------------------------------------
309 void BTextControl::AttachedToWindow()
310 {
311 	BControl::AttachedToWindow();
312 
313 	bool enabled = IsEnabled();
314 	rgb_color textColor;
315 	rgb_color color = HighColor();
316 	BFont font;
317 
318 	fText->GetFontAndColor(0, &font, &color);
319 
320 	if (enabled)
321 		textColor = color;
322 	else
323 		textColor = tint_color(color, B_LIGHTEN_2_TINT);
324 
325 	fText->SetFontAndColor(&font, B_FONT_ALL, &textColor);
326 
327 	if (enabled)
328 	{
329 		color.red = 255;
330 		color.green = 255;
331 		color.blue = 255;
332 	}
333 	else
334 		color = tint_color(color, B_LIGHTEN_2_TINT);
335 
336 	fText->SetViewColor(color);
337 	fText->SetLowColor(color);
338 
339 	fText->MakeEditable(enabled);
340 }
341 //------------------------------------------------------------------------------
342 void BTextControl::MakeFocus(bool state)
343 {
344 	fText->MakeFocus(state);
345 
346 	if (state)
347 		fText->SelectAll();
348 }
349 //------------------------------------------------------------------------------
350 void BTextControl::SetEnabled(bool state)
351 {
352 	if (IsEnabled() == state)
353 		return;
354 
355 	if (Window())
356 	{
357 		fText->MakeEditable(state);
358 
359 		rgb_color textColor;
360 		rgb_color color = {0, 0, 0, 255};
361 		BFont font;
362 
363 		fText->GetFontAndColor(0, &font, &color);
364 
365 		if (state)
366 			textColor = color;
367 		else
368 			textColor = tint_color(color, B_DISABLED_LABEL_TINT);
369 
370 		fText->SetFontAndColor(&font, B_FONT_ALL, &textColor);
371 
372 		if (state)
373 		{
374 			color.red = 255;
375 			color.green = 255;
376 			color.blue = 255;
377 		}
378 		else
379 			color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
380 				B_LIGHTEN_2_TINT);
381 
382 		fText->SetViewColor(color);
383 		fText->SetLowColor(color);
384 
385 		fText->Invalidate();
386 		Window()->UpdateIfNeeded();
387 	}
388 
389 	BControl::SetEnabled(state);
390 }
391 //------------------------------------------------------------------------------
392 void BTextControl::GetPreferredSize(float *width, float *height)
393 {
394 	BFont font;
395 	GetFont(&font);
396 	font_height fh;
397 	font.GetHeight(&fh);
398 
399 	*height = (float)ceil(fh.ascent + fh.descent + fh.leading) + 7.0f;
400 
401 	// TODO: this one I need to find out
402 	*width = 4.0f + (float)ceil(font.StringWidth(Label()))*2.0f;
403 }
404 //------------------------------------------------------------------------------
405 void BTextControl::ResizeToPreferred()
406 {
407 	float w;
408 	float h;
409 	GetPreferredSize(&w, &h);
410 	BView::ResizeTo(w,h);
411 }
412 //------------------------------------------------------------------------------
413 void BTextControl::SetFlags(uint32 flags)
414 {
415 	if (!fSkipSetFlags)
416 	{
417 		// If the textview is navigable, set it to not navigable if needed
418 		// Else if it is not navigable, set it to navigable if needed
419 		if (fText->Flags() & B_NAVIGABLE)
420 		{
421 			if (!(flags & B_NAVIGABLE))
422 				fText->SetFlags(fText->Flags() & ~B_NAVIGABLE);
423 		}
424 		else
425 		{
426 			if (flags & B_NAVIGABLE)
427 				fText->SetFlags(fText->Flags() | B_NAVIGABLE);
428 		}
429 
430 		// Don't make this one navigable
431 		flags &= ~B_NAVIGABLE;
432 	}
433 
434 	BView::SetFlags(flags);
435 }
436 //------------------------------------------------------------------------------
437 void BTextControl::MessageReceived(BMessage *msg)
438 {
439 	switch(msg->what)
440 	{
441 		case B_SET_PROPERTY:
442 		case B_GET_PROPERTY:
443 			// TODO
444 			break;
445 		default:
446 			BControl::MessageReceived(msg);
447 			break;
448 	}
449 }
450 //------------------------------------------------------------------------------
451 BHandler *BTextControl::ResolveSpecifier(BMessage *msg, int32 index,
452 										 BMessage *specifier, int32 form,
453 										 const char *property)
454 {
455 	/*
456 	BPropertyInfo propInfo(prop_list);
457 	BHandler *target = NULL;
458 
459 	if (propInfo.FindMatch(message, 0, specifier, what, property) < B_OK)
460 		return BControl::ResolveSpecifier(message, index, specifier, what,
461 			property);
462 	else
463 		return this;
464 	*/
465 	return BControl::ResolveSpecifier(msg, index, specifier, form, property);
466 }
467 //------------------------------------------------------------------------------
468 status_t BTextControl::GetSupportedSuites(BMessage *data)
469 {
470 	return BControl::GetSupportedSuites(data);
471 }
472 //------------------------------------------------------------------------------
473 void BTextControl::MouseUp(BPoint pt)
474 {
475 	BControl::MouseUp(pt);
476 }
477 //------------------------------------------------------------------------------
478 void BTextControl::MouseMoved(BPoint pt, uint32 code, const BMessage *msg)
479 {
480 	BControl::MouseMoved(pt, code, msg);
481 }
482 //------------------------------------------------------------------------------
483 void BTextControl::DetachedFromWindow()
484 {
485 	BControl::DetachedFromWindow();
486 }
487 //------------------------------------------------------------------------------
488 void BTextControl::AllAttached()
489 {
490 	BControl::AllAttached();
491 }
492 //------------------------------------------------------------------------------
493 void BTextControl::AllDetached()
494 {
495 	BControl::AllDetached();
496 }
497 //------------------------------------------------------------------------------
498 void BTextControl::FrameMoved(BPoint newPosition)
499 {
500 	BControl::FrameMoved(newPosition);
501 }
502 //------------------------------------------------------------------------------
503 void BTextControl::FrameResized(float newWidth, float newHeight)
504 {
505 	BControl::FrameResized(newWidth, newHeight);
506 }
507 //------------------------------------------------------------------------------
508 void BTextControl::WindowActivated(bool active)
509 {
510 	if (fText->IsFocus())
511 		Draw(Bounds());
512 }
513 //------------------------------------------------------------------------------
514 status_t BTextControl::Perform(perform_code d, void *arg)
515 {
516 	return BControl::Perform(d, arg);
517 }
518 //------------------------------------------------------------------------------
519 void BTextControl::_ReservedTextControl1() {}
520 void BTextControl::_ReservedTextControl2() {}
521 void BTextControl::_ReservedTextControl3() {}
522 void BTextControl::_ReservedTextControl4() {}
523 //------------------------------------------------------------------------------
524 BTextControl &BTextControl::operator=(const BTextControl&)
525 {
526 	return *this;
527 }
528 //------------------------------------------------------------------------------
529 void BTextControl::CommitValue()
530 {
531 }
532 //------------------------------------------------------------------------------
533 void BTextControl::InitData(const char *label, const char *initial_text,
534 							BMessage *data)
535 {
536 	BRect bounds(Bounds());
537 
538 	fText = NULL;
539 	//fLabel = NULL;
540 	fModificationMessage = NULL;
541 	fLabelAlign = B_ALIGN_LEFT;
542 	fDivider = 0.0f;
543 	fPrevWidth = 0;
544 	fPrevHeight = 0;
545 	//fClean = true;
546 	fSkipSetFlags = false;
547 
548 	int32 flags = 0;
549 
550 	BFont font(be_bold_font);
551 
552 	if (!data || !data->HasString("_fname"))
553 		flags = 1;
554 
555 	if (!data || !data->HasFloat("_fflt"))
556 		flags |= 2;
557 
558 	if (flags != 0)
559 		SetFont(&font, flags);
560 
561 	if (label)
562 		fDivider = bounds.Width() / 2.0f;
563 
564 	if (Flags() & B_NAVIGABLE)
565 	{
566 		fSkipSetFlags = true;
567 		SetFlags(Flags() & ~B_NAVIGABLE);
568 		fSkipSetFlags = false;
569 	}
570 
571 	if (data)
572 		fText = (_BTextInput_*)FindView("_input_");
573 	else
574 	{
575 		BRect frame(fDivider, bounds.top + 2.0f, bounds.right - 2.0f,
576 			bounds.bottom - 2.0f);
577 		BRect textRect(frame.OffsetToCopy(0.0f, 0.0f));
578 
579 		fText = new _BTextInput_(frame, textRect,
580 			B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS |
581 			B_NAVIGABLE);
582 
583 		AddChild(fText);
584 
585 		SetText(initial_text);
586 		fText->SetAlignment(B_ALIGN_LEFT);
587 		fText->AlignTextRect();
588 	}
589 }
590 //------------------------------------------------------------------------------
591 
592 /*
593  * $Log $
594  *
595  * $Id  $
596  *
597  */
598 
599