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