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