xref: /haiku/src/kits/interface/StringView.cpp (revision d284f7cc43cc0d1106c3b0c40e62c58107648573)
1 /*
2  * Copyright 2001-2015, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Frans van Nispen (xlr8@tref.nl)
9  *		Ingo Weinhold <ingo_weinhold@gmx.de>
10  */
11 
12 
13 //!	BStringView draws a non-editable text string.
14 
15 
16 #include <StringView.h>
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include <LayoutUtils.h>
23 #include <Message.h>
24 #include <PropertyInfo.h>
25 #include <View.h>
26 #include <Window.h>
27 
28 #include <binary_compatibility/Interface.h>
29 
30 
31 static property_info sPropertyList[] = {
32 	{
33 		"Text",
34 		{ B_GET_PROPERTY, B_SET_PROPERTY },
35 		{ B_DIRECT_SPECIFIER },
36 		NULL, 0,
37 		{ B_STRING_TYPE }
38 	},
39 	{
40 		"Alignment",
41 		{ B_GET_PROPERTY, B_SET_PROPERTY },
42 		{ B_DIRECT_SPECIFIER },
43 		NULL, 0,
44 		{ B_INT32_TYPE }
45 	},
46 
47 	{ 0 }
48 };
49 
50 
51 BStringView::BStringView(BRect frame, const char* name, const char* text,
52 	uint32 resizingMode, uint32 flags)
53 	:
54 	BView(frame, name, resizingMode, flags | B_FULL_UPDATE_ON_RESIZE),
55 	fText(text ? strdup(text) : NULL),
56 	fTruncation(B_NO_TRUNCATION),
57 	fAlign(B_ALIGN_LEFT),
58 	fPreferredSize(text ? StringWidth(text) : 0.0, -1)
59 {
60 }
61 
62 
63 BStringView::BStringView(const char* name, const char* text, uint32 flags)
64 	:
65 	BView(name, flags | B_FULL_UPDATE_ON_RESIZE),
66 	fText(text ? strdup(text) : NULL),
67 	fTruncation(B_NO_TRUNCATION),
68 	fAlign(B_ALIGN_LEFT),
69 	fPreferredSize(text ? StringWidth(text) : 0.0, -1)
70 {
71 }
72 
73 
74 BStringView::BStringView(BMessage* archive)
75 	:
76 	BView(archive),
77 	fText(NULL),
78 	fTruncation(B_NO_TRUNCATION),
79 	fPreferredSize(0, -1)
80 {
81 	fAlign = (alignment)archive->GetInt32("_align", B_ALIGN_LEFT);
82 	fTruncation = (uint32)archive->GetInt32("_truncation", B_NO_TRUNCATION);
83 
84 	const char* text = archive->GetString("_text", NULL);
85 
86 	SetText(text);
87 	SetFlags(Flags() | B_FULL_UPDATE_ON_RESIZE);
88 }
89 
90 
91 BStringView::~BStringView()
92 {
93 	free(fText);
94 }
95 
96 
97 // #pragma mark - Archiving methods
98 
99 
100 BArchivable*
101 BStringView::Instantiate(BMessage* data)
102 {
103 	if (!validate_instantiation(data, "BStringView"))
104 		return NULL;
105 
106 	return new BStringView(data);
107 }
108 
109 
110 status_t
111 BStringView::Archive(BMessage* data, bool deep) const
112 {
113 	status_t status = BView::Archive(data, deep);
114 
115 	if (status == B_OK && fText)
116 		status = data->AddString("_text", fText);
117 	if (status == B_OK && fTruncation != B_NO_TRUNCATION)
118 		status = data->AddInt32("_truncation", fTruncation);
119 	if (status == B_OK)
120 		status = data->AddInt32("_align", fAlign);
121 
122 	return status;
123 }
124 
125 
126 // #pragma mark - Hook methods
127 
128 
129 void
130 BStringView::AttachedToWindow()
131 {
132 	if (HasDefaultColors())
133 		SetHighUIColor(B_PANEL_TEXT_COLOR);
134 
135 	BView* parent = Parent();
136 
137 	if (parent != NULL) {
138 		float tint = B_NO_TINT;
139 		color_which which = parent->ViewUIColor(&tint);
140 
141 		if (which != B_NO_COLOR) {
142 			SetViewUIColor(which, tint);
143 			SetLowUIColor(which, tint);
144 		} else {
145 			SetViewColor(parent->ViewColor());
146 			SetLowColor(ViewColor());
147 		}
148 	}
149 
150 	if (ViewColor() == B_TRANSPARENT_COLOR)
151 		AdoptSystemColors();
152 }
153 
154 
155 void
156 BStringView::DetachedFromWindow()
157 {
158 	BView::DetachedFromWindow();
159 }
160 
161 
162 void
163 BStringView::AllAttached()
164 {
165 	BView::AllAttached();
166 }
167 
168 
169 void
170 BStringView::AllDetached()
171 {
172 	BView::AllDetached();
173 }
174 
175 
176 // #pragma mark - Layout methods
177 
178 
179 void
180 BStringView::MakeFocus(bool focus)
181 {
182 	BView::MakeFocus(focus);
183 }
184 
185 
186 void
187 BStringView::GetPreferredSize(float* _width, float* _height)
188 {
189 	_ValidatePreferredSize();
190 
191 	if (_width)
192 		*_width = fPreferredSize.width;
193 
194 	if (_height)
195 		*_height = fPreferredSize.height;
196 }
197 
198 
199 BSize
200 BStringView::MinSize()
201 {
202 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
203 		_ValidatePreferredSize());
204 }
205 
206 
207 BSize
208 BStringView::MaxSize()
209 {
210 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
211 		_ValidatePreferredSize());
212 }
213 
214 
215 BSize
216 BStringView::PreferredSize()
217 {
218 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
219 		_ValidatePreferredSize());
220 }
221 
222 
223 void
224 BStringView::ResizeToPreferred()
225 {
226 	float width, height;
227 	GetPreferredSize(&width, &height);
228 
229 	// Resize the width only for B_ALIGN_LEFT (if its large enough already, that is)
230 	if (Bounds().Width() > width && Alignment() != B_ALIGN_LEFT)
231 		width = Bounds().Width();
232 
233 	BView::ResizeTo(width, height);
234 }
235 
236 
237 BAlignment
238 BStringView::LayoutAlignment()
239 {
240 	return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
241 		BAlignment(fAlign, B_ALIGN_MIDDLE));
242 }
243 
244 
245 // #pragma mark - More hook methods
246 
247 
248 void
249 BStringView::FrameMoved(BPoint newPosition)
250 {
251 	BView::FrameMoved(newPosition);
252 }
253 
254 
255 void
256 BStringView::FrameResized(float newWidth, float newHeight)
257 {
258 	BView::FrameResized(newWidth, newHeight);
259 }
260 
261 
262 void
263 BStringView::Draw(BRect updateRect)
264 {
265 	if (!fText)
266 		return;
267 
268 	if (LowUIColor() == B_NO_COLOR)
269 		SetLowColor(ViewColor());
270 
271 	font_height fontHeight;
272 	GetFontHeight(&fontHeight);
273 
274 	BRect bounds = Bounds();
275 
276 	const char* text = fText;
277 	float width = fPreferredSize.width;
278 	BString truncated;
279 	if (fTruncation != B_NO_TRUNCATION && width > bounds.Width()) {
280 		// The string needs to be truncated
281 		// TODO: we should cache this
282 		truncated = fText;
283 		TruncateString(&truncated, fTruncation, bounds.Width());
284 		text = truncated.String();
285 		width = StringWidth(text);
286 	}
287 
288 	float y = (bounds.top + bounds.bottom - ceilf(fontHeight.ascent)
289 		- ceilf(fontHeight.descent)) / 2.0 + ceilf(fontHeight.ascent);
290 	float x;
291 	switch (fAlign) {
292 		case B_ALIGN_RIGHT:
293 			x = bounds.Width() - width;
294 			break;
295 
296 		case B_ALIGN_CENTER:
297 			x = (bounds.Width() - width) / 2.0;
298 			break;
299 
300 		default:
301 			x = 0.0;
302 			break;
303 	}
304 
305 	DrawString(text, BPoint(x, y));
306 }
307 
308 
309 void
310 BStringView::MessageReceived(BMessage* message)
311 {
312 	if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) {
313 		int32 index;
314 		BMessage specifier;
315 		int32 form;
316 		const char* property;
317 		if (message->GetCurrentSpecifier(&index, &specifier, &form, &property)
318 				!= B_OK) {
319 			BView::MessageReceived(message);
320 			return;
321 		}
322 
323 		BMessage reply(B_REPLY);
324 		bool handled = false;
325 		if (strcmp(property, "Text") == 0) {
326 			if (message->what == B_GET_PROPERTY) {
327 				reply.AddString("result", fText);
328 				handled = true;
329 			} else {
330 				const char* text;
331 				if (message->FindString("data", &text) == B_OK) {
332 					SetText(text);
333 					reply.AddInt32("error", B_OK);
334 					handled = true;
335 				}
336 			}
337 		} else if (strcmp(property, "Alignment") == 0) {
338 			if (message->what == B_GET_PROPERTY) {
339 				reply.AddInt32("result", (int32)fAlign);
340 				handled = true;
341 			} else {
342 				int32 align;
343 				if (message->FindInt32("data", &align) == B_OK) {
344 					SetAlignment((alignment)align);
345 					reply.AddInt32("error", B_OK);
346 					handled = true;
347 				}
348 			}
349 		}
350 
351 		if (handled) {
352 			message->SendReply(&reply);
353 			return;
354 		}
355 	}
356 
357 	BView::MessageReceived(message);
358 }
359 
360 
361 void
362 BStringView::MouseDown(BPoint point)
363 {
364 	BView::MouseDown(point);
365 }
366 
367 
368 void
369 BStringView::MouseUp(BPoint point)
370 {
371 	BView::MouseUp(point);
372 }
373 
374 
375 void
376 BStringView::MouseMoved(BPoint point, uint32 transit, const BMessage* msg)
377 {
378 	BView::MouseMoved(point, transit, msg);
379 }
380 
381 
382 // #pragma mark -
383 
384 
385 void
386 BStringView::SetText(const char* text)
387 {
388 	if ((text && fText && !strcmp(text, fText)) || (!text && !fText))
389 		return;
390 
391 	free(fText);
392 	fText = text ? strdup(text) : NULL;
393 
394 	float newStringWidth = StringWidth(fText);
395 	if (fPreferredSize.width != newStringWidth) {
396 		fPreferredSize.width = newStringWidth;
397 		InvalidateLayout();
398 	}
399 
400 	Invalidate();
401 }
402 
403 
404 const char*
405 BStringView::Text() const
406 {
407 	return fText;
408 }
409 
410 
411 void
412 BStringView::SetAlignment(alignment flag)
413 {
414 	fAlign = flag;
415 	Invalidate();
416 }
417 
418 
419 alignment
420 BStringView::Alignment() const
421 {
422 	return fAlign;
423 }
424 
425 
426 void
427 BStringView::SetTruncation(uint32 truncationMode)
428 {
429 	if (fTruncation != truncationMode) {
430 		fTruncation = truncationMode;
431 		Invalidate();
432 	}
433 }
434 
435 
436 uint32
437 BStringView::Truncation() const
438 {
439 	return fTruncation;
440 }
441 
442 
443 BHandler*
444 BStringView::ResolveSpecifier(BMessage* message, int32 index,
445 	BMessage* specifier, int32 form, const char* property)
446 {
447 	BPropertyInfo propInfo(sPropertyList);
448 	if (propInfo.FindMatch(message, 0, specifier, form, property) >= B_OK)
449 		return this;
450 
451 	return BView::ResolveSpecifier(message, index, specifier, form, property);
452 }
453 
454 
455 status_t
456 BStringView::GetSupportedSuites(BMessage* data)
457 {
458 	if (data == NULL)
459 		return B_BAD_VALUE;
460 
461 	status_t status = data->AddString("suites", "suite/vnd.Be-string-view");
462 	if (status != B_OK)
463 		return status;
464 
465 	BPropertyInfo propertyInfo(sPropertyList);
466 	status = data->AddFlat("messages", &propertyInfo);
467 	if (status != B_OK)
468 		return status;
469 
470 	return BView::GetSupportedSuites(data);
471 }
472 
473 
474 void
475 BStringView::SetFont(const BFont* font, uint32 mask)
476 {
477 	BView::SetFont(font, mask);
478 
479 	fPreferredSize.width = StringWidth(fText);
480 
481 	Invalidate();
482 	InvalidateLayout();
483 }
484 
485 
486 void
487 BStringView::LayoutInvalidated(bool descendants)
488 {
489 	// invalidate cached preferred size
490 	fPreferredSize.height = -1;
491 }
492 
493 
494 // #pragma mark - Perform
495 
496 
497 status_t
498 BStringView::Perform(perform_code code, void* _data)
499 {
500 	switch (code) {
501 		case PERFORM_CODE_MIN_SIZE:
502 			((perform_data_min_size*)_data)->return_value
503 				= BStringView::MinSize();
504 			return B_OK;
505 
506 		case PERFORM_CODE_MAX_SIZE:
507 			((perform_data_max_size*)_data)->return_value
508 				= BStringView::MaxSize();
509 			return B_OK;
510 
511 		case PERFORM_CODE_PREFERRED_SIZE:
512 			((perform_data_preferred_size*)_data)->return_value
513 				= BStringView::PreferredSize();
514 			return B_OK;
515 
516 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
517 			((perform_data_layout_alignment*)_data)->return_value
518 				= BStringView::LayoutAlignment();
519 			return B_OK;
520 
521 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
522 			((perform_data_has_height_for_width*)_data)->return_value
523 				= BStringView::HasHeightForWidth();
524 			return B_OK;
525 
526 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
527 		{
528 			perform_data_get_height_for_width* data
529 				= (perform_data_get_height_for_width*)_data;
530 			BStringView::GetHeightForWidth(data->width, &data->min, &data->max,
531 				&data->preferred);
532 			return B_OK;
533 		}
534 
535 		case PERFORM_CODE_SET_LAYOUT:
536 		{
537 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
538 			BStringView::SetLayout(data->layout);
539 			return B_OK;
540 		}
541 
542 		case PERFORM_CODE_LAYOUT_INVALIDATED:
543 		{
544 			perform_data_layout_invalidated* data
545 				= (perform_data_layout_invalidated*)_data;
546 			BStringView::LayoutInvalidated(data->descendants);
547 			return B_OK;
548 		}
549 
550 		case PERFORM_CODE_DO_LAYOUT:
551 		{
552 			BStringView::DoLayout();
553 			return B_OK;
554 		}
555 	}
556 
557 	return BView::Perform(code, _data);
558 }
559 
560 
561 // #pragma mark - FBC padding methods
562 
563 
564 void BStringView::_ReservedStringView1() {}
565 void BStringView::_ReservedStringView2() {}
566 void BStringView::_ReservedStringView3() {}
567 
568 
569 // #pragma mark - Private methods
570 
571 
572 BStringView&
573 BStringView::operator=(const BStringView&)
574 {
575 	// Assignment not allowed (private)
576 	return *this;
577 }
578 
579 
580 BSize
581 BStringView::_ValidatePreferredSize()
582 {
583 	if (fPreferredSize.height < 0) {
584 		// height
585 		font_height fontHeight;
586 		GetFontHeight(&fontHeight);
587 
588 		fPreferredSize.height = ceilf(fontHeight.ascent + fontHeight.descent
589 			+ fontHeight.leading);
590 
591 		ResetLayoutInvalidation();
592 	}
593 
594 	return fPreferredSize;
595 }
596 
597 
598 extern "C" void
599 B_IF_GCC_2(InvalidateLayout__11BStringViewb,
600 	_ZN11BStringView16InvalidateLayoutEb)(BView* view, bool descendants)
601 {
602 	perform_data_layout_invalidated data;
603 	data.descendants = descendants;
604 
605 	view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
606 }
607