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