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 32static 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 52BStringView::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 64BStringView::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 75BStringView::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 92BStringView::~BStringView() 93{ 94 free(fText); 95} 96 97 98// #pragma mark - Archiving methods 99 100 101BArchivable* 102BStringView::Instantiate(BMessage* data) 103{ 104 if (!validate_instantiation(data, "BStringView")) 105 return NULL; 106 107 return new BStringView(data); 108} 109 110 111status_t 112BStringView::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 130void 131BStringView::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 156void 157BStringView::DetachedFromWindow() 158{ 159 BView::DetachedFromWindow(); 160} 161 162 163void 164BStringView::AllAttached() 165{ 166 BView::AllAttached(); 167} 168 169 170void 171BStringView::AllDetached() 172{ 173 BView::AllDetached(); 174} 175 176 177// #pragma mark - Layout methods 178 179 180void 181BStringView::MakeFocus(bool focus) 182{ 183 BView::MakeFocus(focus); 184} 185 186 187void 188BStringView::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 200BSize 201BStringView::MinSize() 202{ 203 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 204 _ValidatePreferredSize()); 205} 206 207 208BSize 209BStringView::MaxSize() 210{ 211 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 212 _ValidatePreferredSize()); 213} 214 215 216BSize 217BStringView::PreferredSize() 218{ 219 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 220 _ValidatePreferredSize()); 221} 222 223 224void 225BStringView::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 238BAlignment 239BStringView::LayoutAlignment() 240{ 241 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 242 BAlignment(fAlign, B_ALIGN_MIDDLE)); 243} 244 245 246// #pragma mark - More hook methods 247 248 249void 250BStringView::FrameMoved(BPoint newPosition) 251{ 252 BView::FrameMoved(newPosition); 253} 254 255 256void 257BStringView::FrameResized(float newWidth, float newHeight) 258{ 259 BView::FrameResized(newWidth, newHeight); 260} 261 262 263void 264BStringView::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 315void 316BStringView::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 367void 368BStringView::MouseDown(BPoint point) 369{ 370 BView::MouseDown(point); 371} 372 373 374void 375BStringView::MouseUp(BPoint point) 376{ 377 BView::MouseUp(point); 378} 379 380 381void 382BStringView::MouseMoved(BPoint point, uint32 transit, const BMessage* msg) 383{ 384 BView::MouseMoved(point, transit, msg); 385} 386 387 388// #pragma mark - 389 390 391void 392BStringView::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 410const char* 411BStringView::Text() const 412{ 413 return fText; 414} 415 416 417void 418BStringView::SetAlignment(alignment flag) 419{ 420 fAlign = flag; 421 Invalidate(); 422} 423 424 425alignment 426BStringView::Alignment() const 427{ 428 return fAlign; 429} 430 431 432void 433BStringView::SetTruncation(uint32 truncationMode) 434{ 435 if (fTruncation != truncationMode) { 436 fTruncation = truncationMode; 437 Invalidate(); 438 } 439} 440 441 442uint32 443BStringView::Truncation() const 444{ 445 return fTruncation; 446} 447 448 449BHandler* 450BStringView::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 461status_t 462BStringView::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 480void 481BStringView::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 492void 493BStringView::LayoutInvalidated(bool descendants) 494{ 495 // invalidate cached preferred size 496 fPreferredSize.height = -1; 497} 498 499 500// #pragma mark - Perform 501 502 503status_t 504BStringView::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 570void BStringView::_ReservedStringView1() {} 571void BStringView::_ReservedStringView2() {} 572void BStringView::_ReservedStringView3() {} 573 574 575// #pragma mark - Private methods 576 577 578BStringView& 579BStringView::operator=(const BStringView&) 580{ 581 // Assignment not allowed (private) 582 return *this; 583} 584 585 586BSize 587BStringView::_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 611float 612BStringView::_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 629extern "C" void 630B_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