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