1 /* 2 * Copyright 2001-2005, Haiku Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Frans van Nispen (xlr8@tref.nl) 7 * Stephan Aßmus <superstippi@gmx.de> 8 */ 9 10 /** BTextControl displays text that can act like a control. */ 11 12 13 #include <stdio.h> 14 15 #include <Message.h> 16 #include <Region.h> 17 #include <TextControl.h> 18 #include <Window.h> 19 20 #include "TextInput.h" 21 22 23 BTextControl::BTextControl(BRect frame, const char *name, const char *label, 24 const char *text, BMessage *message, uint32 mask, 25 uint32 flags) 26 : BControl(frame, name, label, message, mask, flags | B_FRAME_EVENTS) 27 { 28 InitData(label, text); 29 30 float height; 31 BTextControl::GetPreferredSize(NULL, &height); 32 33 ResizeTo(Bounds().Width(), height); 34 35 float lineHeight = ceil(fText->LineHeight(0)); 36 fText->ResizeTo(fText->Bounds().Width(), lineHeight); 37 fText->MoveTo(fText->Frame().left, (height - lineHeight) / 2); 38 } 39 40 41 BTextControl::~BTextControl() 42 { 43 SetModificationMessage(NULL); 44 } 45 46 47 BTextControl::BTextControl(BMessage *data) 48 : BControl(data) 49 { 50 InitData(Label(), NULL, data); 51 52 int32 _a_label = B_ALIGN_LEFT; 53 int32 _a_text = B_ALIGN_LEFT; 54 55 if (data->HasInt32("_a_label")) 56 data->FindInt32("_a_label", &_a_label); 57 58 if (data->HasInt32("_a_text")) 59 data->FindInt32("_a_text", &_a_text); 60 61 SetAlignment((alignment)_a_label, (alignment)_a_text); 62 63 if (data->HasFloat("_divide")) 64 data->FindFloat("_a_text", &fDivider); 65 66 if (data->HasMessage("_mod_msg")) { 67 BMessage *_mod_msg = new BMessage; 68 data->FindMessage("_mod_msg", _mod_msg); 69 SetModificationMessage(_mod_msg); 70 } 71 } 72 73 74 BArchivable * 75 BTextControl::Instantiate(BMessage *archive) 76 { 77 if (validate_instantiation(archive, "BTextControl")) 78 return new BTextControl(archive); 79 else 80 return NULL; 81 } 82 83 84 status_t 85 BTextControl::Archive(BMessage *data, bool deep) const 86 { 87 BView::Archive(data, deep); 88 89 alignment labelAlignment, textAlignment; 90 91 GetAlignment(&labelAlignment, &textAlignment); 92 93 data->AddInt32("_a_label", labelAlignment); 94 data->AddInt32("_a_text", textAlignment); 95 data->AddFloat("_divide", Divider()); 96 97 if (ModificationMessage()) 98 data->AddMessage("_mod_msg", ModificationMessage()); 99 100 return B_OK; 101 } 102 103 104 void 105 BTextControl::SetText(const char *text) 106 { 107 if (InvokeKind() != B_CONTROL_INVOKED) 108 return; 109 110 fText->SetText(text); 111 112 if (IsFocus()) 113 fText->SetInitialText(); 114 115 fText->Invalidate(); 116 } 117 118 119 const char * 120 BTextControl::Text() const 121 { 122 return fText->Text(); 123 } 124 125 126 void 127 BTextControl::SetValue(int32 value) 128 { 129 BControl::SetValue(value); 130 } 131 132 133 status_t 134 BTextControl::Invoke(BMessage *message) 135 { 136 return BControl::Invoke(message); 137 } 138 139 140 BTextView * 141 BTextControl::TextView() const 142 { 143 return fText; 144 } 145 146 147 void 148 BTextControl::SetModificationMessage(BMessage *message) 149 { 150 delete fModificationMessage; 151 fModificationMessage = message; 152 } 153 154 155 BMessage * 156 BTextControl::ModificationMessage() const 157 { 158 return fModificationMessage; 159 } 160 161 162 void 163 BTextControl::SetAlignment(alignment labelAlignment, alignment textAlignment) 164 { 165 fText->SetAlignment(textAlignment); 166 fText->AlignTextRect(); 167 168 if (fLabelAlign != labelAlignment) { 169 fLabelAlign = labelAlignment; 170 Invalidate(); 171 } 172 } 173 174 175 void 176 BTextControl::GetAlignment(alignment *label, alignment *text) const 177 { 178 if (label) 179 *label = fLabelAlign; 180 if (text) 181 *text = fText->Alignment(); 182 } 183 184 185 void 186 BTextControl::SetDivider(float dividingLine) 187 { 188 dividingLine = floorf(dividingLine + 0.5); 189 190 float dx = fDivider - dividingLine; 191 192 fDivider = dividingLine; 193 194 fText->MoveBy(-dx, 0.0f); 195 fText->ResizeBy(dx, 0.0f); 196 197 if (Window()) { 198 fText->Invalidate(); 199 Invalidate(); 200 } 201 } 202 203 204 float 205 BTextControl::Divider() const 206 { 207 return fDivider; 208 } 209 210 211 void 212 BTextControl::Draw(BRect updateRect) 213 { 214 rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR), 215 lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT), 216 lighten2 = tint_color(noTint, B_LIGHTEN_2_TINT), 217 lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT), 218 darken1 = tint_color(noTint, B_DARKEN_1_TINT), 219 darken2 = tint_color(noTint, B_DARKEN_2_TINT), 220 darken4 = tint_color(noTint, B_DARKEN_4_TINT), 221 darkenMax = tint_color(noTint, B_DARKEN_MAX_TINT), 222 navigationColor = ui_color(B_KEYBOARD_NAVIGATION_COLOR); 223 224 bool enabled = IsEnabled(); 225 bool active = false; 226 227 if (fText->IsFocus() && Window()->IsActive()) 228 active = true; 229 230 // outer bevel 231 232 BRect rect = Bounds(); 233 rect.left = fDivider; 234 235 if (enabled) 236 SetHighColor(darken1); 237 else 238 SetHighColor(noTint); 239 240 StrokeLine(rect.LeftBottom(), rect.LeftTop()); 241 StrokeLine(rect.RightTop()); 242 243 if (enabled) 244 SetHighColor(lighten2); 245 else 246 SetHighColor(lighten1); 247 248 StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 249 StrokeLine(BPoint(rect.right, rect.top + 1.0f), rect.RightBottom()); 250 251 // inner bevel 252 253 rect.InsetBy(1.0f, 1.0f); 254 255 if (active) { 256 SetHighColor(navigationColor); 257 StrokeRect(rect); 258 } else { 259 if (enabled) 260 SetHighColor(darken4); 261 else 262 SetHighColor(darken2); 263 264 StrokeLine(rect.LeftTop(), rect.LeftBottom()); 265 StrokeLine(rect.LeftTop(), rect.RightTop()); 266 267 SetHighColor(noTint); 268 StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 269 StrokeLine(BPoint(rect.right, rect.top + 1.0f)); 270 } 271 272 // area around text view 273 274 SetHighColor(fText->ViewColor()); 275 rect.InsetBy(1, 1); 276 BRegion region(rect); 277 BRegion updateRegion(updateRect); 278 // why is there no IntersectWith(BRect &) version? 279 region.IntersectWith(&updateRegion); 280 for (int32 i = region.CountRects(); i-- > 0;) { 281 FillRect(region.RectAt(i)); 282 } 283 284 if (Label()) { 285 font_height fontHeight; 286 GetFontHeight(&fontHeight); 287 288 float y = fontHeight.ascent + fText->Frame().top + 1; 289 float x; 290 291 switch (fLabelAlign) { 292 case B_ALIGN_RIGHT: 293 x = fDivider - StringWidth(Label()) - 3.0f; 294 break; 295 296 case B_ALIGN_CENTER: 297 x = fDivider - StringWidth(Label()) / 2.0f; 298 break; 299 300 default: 301 x = 3.0f; 302 break; 303 } 304 305 SetHighColor(IsEnabled() ? darkenMax 306 : tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DISABLED_LABEL_TINT)); 307 DrawString(Label(), BPoint(x, y)); 308 } 309 } 310 311 312 void 313 BTextControl::MouseDown(BPoint where) 314 { 315 if (!fText->IsFocus()) { 316 fText->MakeFocus(true); 317 fText->SelectAll(); 318 } 319 } 320 321 322 void 323 BTextControl::AttachedToWindow() 324 { 325 BControl::AttachedToWindow(); 326 327 bool enabled = IsEnabled(); 328 rgb_color textColor; 329 rgb_color color = HighColor(); 330 BFont font; 331 332 fText->GetFontAndColor(0, &font, &color); 333 334 if (enabled) 335 textColor = color; 336 else 337 textColor = tint_color(color, B_LIGHTEN_2_TINT); 338 339 fText->SetFontAndColor(&font, B_FONT_ALL, &textColor); 340 341 if (enabled) { 342 color.red = 255; 343 color.green = 255; 344 color.blue = 255; 345 } else 346 color = tint_color(color, B_LIGHTEN_2_TINT); 347 348 fText->SetViewColor(color); 349 fText->SetLowColor(color); 350 351 fText->MakeEditable(enabled); 352 } 353 354 355 void 356 BTextControl::MakeFocus(bool state) 357 { 358 if (state != fText->IsFocus()) { 359 fText->MakeFocus(state); 360 361 if (state) 362 fText->SelectAll(); 363 } 364 } 365 366 367 void 368 BTextControl::SetEnabled(bool state) 369 { 370 if (IsEnabled() == state) 371 return; 372 373 if (Window()) { 374 fText->MakeEditable(state); 375 376 rgb_color textColor; 377 rgb_color color = {0, 0, 0, 255}; 378 BFont font; 379 380 fText->GetFontAndColor(0, &font, &color); 381 382 if (state) 383 textColor = color; 384 else 385 textColor = tint_color(color, B_DISABLED_LABEL_TINT); 386 387 fText->SetFontAndColor(&font, B_FONT_ALL, &textColor); 388 389 if (state) { 390 color.red = 255; 391 color.green = 255; 392 color.blue = 255; 393 } else 394 color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 395 B_LIGHTEN_2_TINT); 396 397 fText->SetViewColor(color); 398 fText->SetLowColor(color); 399 400 fText->Invalidate(); 401 Window()->UpdateIfNeeded(); 402 } 403 404 BControl::SetEnabled(state); 405 } 406 407 408 void 409 BTextControl::GetPreferredSize(float *_width, float *_height) 410 { 411 if (_height) { 412 // we need enough space for the label and the child text view 413 font_height fontHeight; 414 GetFontHeight(&fontHeight); 415 float labelHeight = ceil(fontHeight.ascent + fontHeight.descent 416 + fontHeight.leading); 417 float textHeight = fText->LineHeight(0) + 4.0; 418 419 *_height = max_c(labelHeight, textHeight); 420 } 421 422 if (_width) { 423 // TODO: this one I need to find out 424 float width = 20.0f + ceilf(StringWidth(Label())); 425 if (width < Bounds().Width()) 426 width = Bounds().Width(); 427 *_width = width; 428 } 429 } 430 431 432 void 433 BTextControl::ResizeToPreferred() 434 { 435 // TODO: change divider? 436 BView::ResizeToPreferred(); 437 } 438 439 440 void 441 BTextControl::SetFlags(uint32 flags) 442 { 443 if (!fSkipSetFlags) { 444 // If the textview is navigable, set it to not navigable if needed 445 // Else if it is not navigable, set it to navigable if needed 446 if (fText->Flags() & B_NAVIGABLE) { 447 if (!(flags & B_NAVIGABLE)) 448 fText->SetFlags(fText->Flags() & ~B_NAVIGABLE); 449 450 } else { 451 if (flags & B_NAVIGABLE) 452 fText->SetFlags(fText->Flags() | B_NAVIGABLE); 453 } 454 455 // Don't make this one navigable 456 flags &= ~B_NAVIGABLE; 457 } 458 459 BView::SetFlags(flags); 460 } 461 462 463 void 464 BTextControl::MessageReceived(BMessage *msg) 465 { 466 switch(msg->what) { 467 case B_SET_PROPERTY: 468 case B_GET_PROPERTY: 469 // TODO 470 break; 471 default: 472 BControl::MessageReceived(msg); 473 break; 474 } 475 } 476 477 478 BHandler * 479 BTextControl::ResolveSpecifier(BMessage *msg, int32 index, 480 BMessage *specifier, int32 form, 481 const char *property) 482 { 483 /* 484 BPropertyInfo propInfo(prop_list); 485 BHandler *target = NULL; 486 487 if (propInfo.FindMatch(message, 0, specifier, what, property) < B_OK) 488 return BControl::ResolveSpecifier(message, index, specifier, what, 489 property); 490 else 491 return this; 492 */ 493 return BControl::ResolveSpecifier(msg, index, specifier, form, property); 494 } 495 496 497 status_t 498 BTextControl::GetSupportedSuites(BMessage *data) 499 { 500 return BControl::GetSupportedSuites(data); 501 } 502 503 504 void 505 BTextControl::MouseUp(BPoint pt) 506 { 507 BControl::MouseUp(pt); 508 } 509 510 511 void 512 BTextControl::MouseMoved(BPoint pt, uint32 code, const BMessage *msg) 513 { 514 BControl::MouseMoved(pt, code, msg); 515 } 516 517 518 void 519 BTextControl::DetachedFromWindow() 520 { 521 BControl::DetachedFromWindow(); 522 } 523 524 525 void 526 BTextControl::AllAttached() 527 { 528 BControl::AllAttached(); 529 } 530 531 532 void 533 BTextControl::AllDetached() 534 { 535 BControl::AllDetached(); 536 } 537 538 539 void 540 BTextControl::FrameMoved(BPoint newPosition) 541 { 542 BControl::FrameMoved(newPosition); 543 } 544 545 546 void 547 BTextControl::FrameResized(float newWidth, float newHeight) 548 { 549 BControl::FrameResized(newWidth, newHeight); 550 } 551 552 553 void 554 BTextControl::WindowActivated(bool active) 555 { 556 if (fText->IsFocus()) { 557 BRect rect(fText->Frame()); 558 rect.InsetBy(-1.0, -1.0); 559 Invalidate(rect); 560 } 561 } 562 563 564 status_t 565 BTextControl::Perform(perform_code d, void *arg) 566 { 567 return BControl::Perform(d, arg); 568 } 569 570 571 void BTextControl::_ReservedTextControl1() {} 572 void BTextControl::_ReservedTextControl2() {} 573 void BTextControl::_ReservedTextControl3() {} 574 void BTextControl::_ReservedTextControl4() {} 575 576 577 BTextControl & 578 BTextControl::operator=(const BTextControl&) 579 { 580 return *this; 581 } 582 583 584 void 585 BTextControl::CommitValue() 586 { 587 } 588 589 590 void 591 BTextControl::InitData(const char *label, const char *initialText, 592 BMessage *data) 593 { 594 BRect bounds(Bounds()); 595 596 fText = NULL; 597 fModificationMessage = NULL; 598 fLabelAlign = B_ALIGN_LEFT; 599 fDivider = 0.0f; 600 fPrevWidth = 0; 601 fPrevHeight = 0; 602 fSkipSetFlags = false; 603 604 int32 flags = 0; 605 606 BFont font(be_plain_font); 607 608 if (!data || !data->HasString("_fname")) 609 flags |= B_FONT_FAMILY_AND_STYLE; 610 611 if (!data || !data->HasFloat("_fflt")) 612 flags |= B_FONT_SIZE; 613 614 if (flags != 0) 615 SetFont(&font, flags); 616 617 if (label) 618 fDivider = floorf(bounds.Width() / 2.0f); 619 620 uint32 navigableFlags = Flags() & B_NAVIGABLE; 621 if (navigableFlags != 0) { 622 fSkipSetFlags = true; 623 SetFlags(Flags() & ~B_NAVIGABLE); 624 fSkipSetFlags = false; 625 } 626 627 if (data) 628 fText = static_cast<_BTextInput_ *>(FindView("_input_")); 629 else { 630 BRect frame(fDivider, bounds.top, 631 bounds.right, bounds.bottom); 632 // we are stroking the frame around the text view, which 633 // is 2 pixels wide 634 frame.InsetBy(4.0, 3.0); 635 BRect textRect(frame.OffsetToCopy(0.0f, 0.0f)); 636 637 fText = new _BTextInput_(frame, textRect, 638 B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, 639 B_WILL_DRAW | B_FRAME_EVENTS | navigableFlags); 640 AddChild(fText); 641 642 SetText(initialText); 643 fText->SetAlignment(B_ALIGN_LEFT); 644 fText->AlignTextRect(); 645 } 646 } 647