1 /* 2 * Copyright 2001-2008, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Frans van Nispen (xlr8@tref.nl) 7 * Ingo Weinhold <ingo_weinhold@gmx.de> 8 * Stephan Aßmus <superstippi@gmx.de> 9 */ 10 11 //! BStringView draws a non-editable text string. 12 13 14 #include <StringView.h> 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 20 #include <LayoutUtils.h> 21 #include <Message.h> 22 #include <PropertyInfo.h> 23 #include <View.h> 24 #include <Window.h> 25 26 #include <binary_compatibility/Interface.h> 27 28 29 static property_info sPropertyList[] = { 30 { 31 "Text", 32 { B_GET_PROPERTY, B_SET_PROPERTY }, 33 { B_DIRECT_SPECIFIER }, 34 NULL, 0, 35 { B_STRING_TYPE } 36 }, 37 { 38 "Alignment", 39 { B_GET_PROPERTY, B_SET_PROPERTY }, 40 { B_DIRECT_SPECIFIER }, 41 NULL, 0, 42 { B_INT32_TYPE } 43 }, 44 {} 45 }; 46 47 48 BStringView::BStringView(BRect frame, const char* name, const char* text, 49 uint32 resizeMask, uint32 flags) 50 : BView(frame, name, resizeMask, flags | B_FULL_UPDATE_ON_RESIZE), 51 fText(text ? strdup(text) : NULL), 52 fStringWidth(text ? StringWidth(text) : 0.0), 53 fAlign(B_ALIGN_LEFT), 54 fPreferredSize(-1, -1) 55 { 56 } 57 58 59 BStringView::BStringView(const char* name, const char* text, uint32 flags) 60 : BView(name, flags | B_FULL_UPDATE_ON_RESIZE), 61 fText(text ? strdup(text) : NULL), 62 fStringWidth(text ? StringWidth(text) : 0.0), 63 fAlign(B_ALIGN_LEFT), 64 fPreferredSize(-1, -1) 65 { 66 } 67 68 69 BStringView::BStringView(BMessage* data) 70 : BView(data), 71 fText(NULL), 72 fStringWidth(0.0), 73 fPreferredSize(-1, -1) 74 { 75 int32 align; 76 if (data->FindInt32("_align", &align) == B_OK) 77 fAlign = (alignment)align; 78 else 79 fAlign = B_ALIGN_LEFT; 80 81 const char* text; 82 if (data->FindString("_text", &text) != B_OK) 83 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 BArchivable* 97 BStringView::Instantiate(BMessage* data) 98 { 99 if (!validate_instantiation(data, "BStringView")) 100 return NULL; 101 102 return new BStringView(data); 103 } 104 105 106 status_t 107 BStringView::Archive(BMessage* data, bool deep) const 108 { 109 status_t err = BView::Archive(data, deep); 110 111 if (err == B_OK && fText) 112 err = data->AddString("_text", fText); 113 114 if (err == B_OK) 115 err = data->AddInt32("_align", fAlign); 116 117 return err; 118 } 119 120 121 // #pragma mark - 122 123 124 void 125 BStringView::AttachedToWindow() 126 { 127 rgb_color color = B_TRANSPARENT_COLOR; 128 129 BView* parent = Parent(); 130 if (parent != NULL) 131 color = parent->ViewColor(); 132 133 if (color == B_TRANSPARENT_COLOR) 134 color = ui_color(B_PANEL_BACKGROUND_COLOR); 135 136 SetViewColor(color); 137 } 138 139 140 void 141 BStringView::DetachedFromWindow() 142 { 143 BView::DetachedFromWindow(); 144 } 145 146 147 void 148 BStringView::AllAttached() 149 { 150 BView::AllAttached(); 151 } 152 153 154 void 155 BStringView::AllDetached() 156 { 157 BView::AllDetached(); 158 } 159 160 161 // #pragma mark - 162 163 164 void 165 BStringView::MakeFocus(bool state) 166 { 167 BView::MakeFocus(state); 168 } 169 170 171 void 172 BStringView::GetPreferredSize(float* _width, float* _height) 173 { 174 _ValidatePreferredSize(); 175 176 if (_width) 177 *_width = fPreferredSize.width; 178 179 if (_height) 180 *_height = fPreferredSize.height; 181 } 182 183 184 BSize 185 BStringView::MinSize() 186 { 187 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 188 _ValidatePreferredSize()); 189 } 190 191 192 BSize 193 BStringView::MaxSize() 194 { 195 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 196 _ValidatePreferredSize()); 197 } 198 199 200 BSize 201 BStringView::PreferredSize() 202 { 203 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 204 _ValidatePreferredSize()); 205 } 206 207 208 void 209 BStringView::ResizeToPreferred() 210 { 211 float width, height; 212 GetPreferredSize(&width, &height); 213 214 // Resize the width only for B_ALIGN_LEFT (if its large enough already, that is) 215 if (Bounds().Width() > width && Alignment() != B_ALIGN_LEFT) 216 width = Bounds().Width(); 217 218 BView::ResizeTo(width, height); 219 } 220 221 222 BAlignment 223 BStringView::LayoutAlignment() 224 { 225 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 226 BAlignment(fAlign, B_ALIGN_MIDDLE)); 227 } 228 229 230 void 231 BStringView::FrameMoved(BPoint newPosition) 232 { 233 BView::FrameMoved(newPosition); 234 } 235 236 237 void 238 BStringView::FrameResized(float newWidth, float newHeight) 239 { 240 BView::FrameResized(newWidth, newHeight); 241 } 242 243 244 // #pragma mark - 245 246 247 void 248 BStringView::Draw(BRect updateRect) 249 { 250 if (!fText) 251 return; 252 253 SetLowColor(ViewColor()); 254 255 font_height fontHeight; 256 GetFontHeight(&fontHeight); 257 258 BRect bounds = Bounds(); 259 260 float y = (bounds.top + bounds.bottom - ceilf(fontHeight.ascent) 261 - ceilf(fontHeight.descent)) / 2.0 + ceilf(fontHeight.ascent); 262 float x; 263 switch (fAlign) { 264 case B_ALIGN_RIGHT: 265 x = bounds.Width() - fStringWidth; 266 break; 267 268 case B_ALIGN_CENTER: 269 x = (bounds.Width() - fStringWidth) / 2.0; 270 break; 271 272 default: 273 x = 0.0; 274 break; 275 } 276 277 DrawString(fText, BPoint(x, y)); 278 } 279 280 281 void 282 BStringView::MessageReceived(BMessage* message) 283 { 284 if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) { 285 int32 index; 286 BMessage specifier; 287 int32 form; 288 const char* property; 289 if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) 290 != B_OK) { 291 BView::MessageReceived(message); 292 return; 293 } 294 295 BMessage reply(B_REPLY); 296 bool handled = false; 297 if (strcmp(property, "Text") == 0) { 298 if (message->what == B_GET_PROPERTY) { 299 reply.AddString("result", fText); 300 handled = true; 301 } else { 302 const char* text; 303 if (message->FindString("data", &text) == B_OK) { 304 SetText(text); 305 reply.AddInt32("error", B_OK); 306 handled = true; 307 } 308 } 309 } else if (strcmp(property, "Alignment") == 0) { 310 if (message->what == B_GET_PROPERTY) { 311 reply.AddInt32("result", (int32)fAlign); 312 handled = true; 313 } else { 314 int32 align; 315 if (message->FindInt32("data", &align) == B_OK) { 316 SetAlignment((alignment)align); 317 reply.AddInt32("error", B_OK); 318 handled = true; 319 } 320 } 321 } 322 323 if (handled) { 324 message->SendReply(&reply); 325 return; 326 } 327 } 328 329 BView::MessageReceived(message); 330 } 331 332 333 void 334 BStringView::MouseDown(BPoint point) 335 { 336 BView::MouseDown(point); 337 } 338 339 340 void 341 BStringView::MouseUp(BPoint point) 342 { 343 BView::MouseUp(point); 344 } 345 346 347 void 348 BStringView::MouseMoved(BPoint point, uint32 transit, const BMessage* msg) 349 { 350 BView::MouseMoved(point, transit, msg); 351 } 352 353 354 // #pragma mark - 355 356 357 void 358 BStringView::SetText(const char* text) 359 { 360 if ((text && fText && !strcmp(text, fText)) || (!text && !fText)) 361 return; 362 363 free(fText); 364 fText = text ? strdup(text) : NULL; 365 366 float newStringWidth = StringWidth(fText); 367 if (fStringWidth != newStringWidth) { 368 fStringWidth = newStringWidth; 369 InvalidateLayout(); 370 } 371 372 Invalidate(); 373 } 374 375 376 const char* 377 BStringView::Text() const 378 { 379 return fText; 380 } 381 382 383 void 384 BStringView::SetAlignment(alignment flag) 385 { 386 fAlign = flag; 387 Invalidate(); 388 } 389 390 391 alignment 392 BStringView::Alignment() const 393 { 394 return fAlign; 395 } 396 397 398 BHandler* 399 BStringView::ResolveSpecifier(BMessage* message, int32 index, 400 BMessage* specifier, int32 form, const char* property) 401 { 402 BPropertyInfo propInfo(sPropertyList); 403 if (propInfo.FindMatch(message, 0, specifier, form, property) >= B_OK) 404 return this; 405 406 return BView::ResolveSpecifier(message, index, specifier, form, property); 407 } 408 409 410 status_t 411 BStringView::GetSupportedSuites(BMessage* data) 412 { 413 if (data == NULL) 414 return B_BAD_VALUE; 415 416 status_t status = data->AddString("suites", "suite/vnd.Be-string-view"); 417 if (status != B_OK) 418 return status; 419 420 BPropertyInfo propertyInfo(sPropertyList); 421 status = data->AddFlat("messages", &propertyInfo); 422 if (status != B_OK) 423 return status; 424 425 return BView::GetSupportedSuites(data); 426 } 427 428 429 void 430 BStringView::SetFont(const BFont* font, uint32 mask) 431 { 432 BView::SetFont(font, mask); 433 434 fStringWidth = StringWidth(fText); 435 436 Invalidate(); 437 InvalidateLayout(); 438 } 439 440 441 void 442 BStringView::LayoutInvalidated(bool descendants) 443 { 444 // invalidate cached preferred size 445 fPreferredSize.Set(-1, -1); 446 } 447 448 449 // #pragma mark - 450 451 452 status_t 453 BStringView::Perform(perform_code code, void* _data) 454 { 455 switch (code) { 456 case PERFORM_CODE_MIN_SIZE: 457 ((perform_data_min_size*)_data)->return_value 458 = BStringView::MinSize(); 459 return B_OK; 460 case PERFORM_CODE_MAX_SIZE: 461 ((perform_data_max_size*)_data)->return_value 462 = BStringView::MaxSize(); 463 return B_OK; 464 case PERFORM_CODE_PREFERRED_SIZE: 465 ((perform_data_preferred_size*)_data)->return_value 466 = BStringView::PreferredSize(); 467 return B_OK; 468 case PERFORM_CODE_LAYOUT_ALIGNMENT: 469 ((perform_data_layout_alignment*)_data)->return_value 470 = BStringView::LayoutAlignment(); 471 return B_OK; 472 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 473 ((perform_data_has_height_for_width*)_data)->return_value 474 = BStringView::HasHeightForWidth(); 475 return B_OK; 476 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 477 { 478 perform_data_get_height_for_width* data 479 = (perform_data_get_height_for_width*)_data; 480 BStringView::GetHeightForWidth(data->width, &data->min, &data->max, 481 &data->preferred); 482 return B_OK; 483 } 484 case PERFORM_CODE_SET_LAYOUT: 485 { 486 perform_data_set_layout* data = (perform_data_set_layout*)_data; 487 BStringView::SetLayout(data->layout); 488 return B_OK; 489 } 490 case PERFORM_CODE_LAYOUT_INVALIDATED: 491 { 492 perform_data_layout_invalidated* data 493 = (perform_data_layout_invalidated*)_data; 494 BStringView::LayoutInvalidated(data->descendants); 495 return B_OK; 496 } 497 case PERFORM_CODE_DO_LAYOUT: 498 { 499 BStringView::DoLayout(); 500 return B_OK; 501 } 502 } 503 504 return BView::Perform(code, _data); 505 } 506 507 508 509 void BStringView::_ReservedStringView1() {} 510 void BStringView::_ReservedStringView2() {} 511 void BStringView::_ReservedStringView3() {} 512 513 514 BStringView& 515 BStringView::operator=(const BStringView&) 516 { 517 // Assignment not allowed (private) 518 return *this; 519 } 520 521 522 // #pragma mark - 523 524 525 BSize 526 BStringView::_ValidatePreferredSize() 527 { 528 if (fPreferredSize.width < 0) { 529 // width 530 fPreferredSize.width = ceilf(fStringWidth); 531 532 // height 533 font_height fontHeight; 534 GetFontHeight(&fontHeight); 535 536 fPreferredSize.height = ceilf(fontHeight.ascent + fontHeight.descent 537 + fontHeight.leading); 538 539 ResetLayoutInvalidation(); 540 } 541 542 return fPreferredSize; 543 } 544 545 546 extern "C" void 547 B_IF_GCC_2(InvalidateLayout__11BStringViewb, 548 _ZN11BStringView16InvalidateLayoutEb)(BView* view, bool descendants) 549 { 550 perform_data_layout_invalidated data; 551 data.descendants = descendants; 552 553 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data); 554 } 555 556