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