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