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