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