1 /* 2 * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <stdio.h> 7 8 #include <LayoutUtils.h> 9 #include <ScrollView.h> 10 #include <Message.h> 11 #include <Window.h> 12 13 14 static const float kFancyBorderSize = 2; 15 static const float kPlainBorderSize = 1; 16 17 18 BScrollView::BScrollView(const char *name, BView *target, uint32 resizingMode, 19 uint32 flags, bool horizontal, bool vertical, border_style border) 20 : BView(CalcFrame(target, horizontal, vertical, border), name, 21 resizingMode, ModifyFlags(flags, border)), 22 fTarget(target), 23 fHorizontalScrollBar(NULL), 24 fVerticalScrollBar(NULL), 25 fBorder(border), 26 fHighlighted(false) 27 { 28 BRect targetFrame; 29 if (fTarget) { 30 // layout target and add it 31 fTarget->TargetedByScrollView(this); 32 fTarget->MoveTo(B_ORIGIN); 33 34 if (border != B_NO_BORDER) 35 fTarget->MoveBy(BorderSize(border), BorderSize(border)); 36 37 AddChild(fTarget); 38 targetFrame = fTarget->Frame(); 39 } else { 40 // no target specified 41 targetFrame = Bounds(); 42 if (horizontal) 43 targetFrame.bottom -= B_H_SCROLL_BAR_HEIGHT + 1; 44 if (vertical) 45 targetFrame.right -= B_V_SCROLL_BAR_WIDTH + 1; 46 if (border == B_FANCY_BORDER) { 47 targetFrame.bottom--; 48 targetFrame.right--; 49 } 50 } 51 52 if (horizontal) { 53 BRect rect = targetFrame; 54 rect.top = rect.bottom + 1; 55 rect.bottom = rect.top + B_H_SCROLL_BAR_HEIGHT; 56 if (border != B_NO_BORDER || vertical) { 57 // extend scrollbar so that it overlaps one pixel with vertical scrollbar 58 rect.right++; 59 } 60 if (border != B_NO_BORDER) { 61 // the scrollbar draws part of the surrounding frame on the left 62 rect.left--; 63 } 64 fHorizontalScrollBar = new BScrollBar(rect, "_HSB_", fTarget, 0, 1000, B_HORIZONTAL); 65 AddChild(fHorizontalScrollBar); 66 } 67 68 if (vertical) { 69 BRect rect = targetFrame; 70 rect.left = rect.right + 1; 71 rect.right = rect.left + B_V_SCROLL_BAR_WIDTH; 72 if (border != B_NO_BORDER || horizontal) { 73 // extend scrollbar so that it overlaps one pixel with vertical scrollbar 74 rect.bottom++; 75 } 76 if (border != B_NO_BORDER) { 77 // the scrollbar draws part of the surrounding frame on the left 78 rect.top--; 79 } 80 fVerticalScrollBar = new BScrollBar(rect, "_VSB_", fTarget, 0, 1000, B_VERTICAL); 81 AddChild(fVerticalScrollBar); 82 } 83 84 fPreviousWidth = uint16(Bounds().Width()); 85 fPreviousHeight = uint16(Bounds().Height()); 86 } 87 88 89 BScrollView::BScrollView(BMessage *archive) 90 : BView(archive), 91 fHighlighted(false) 92 { 93 int32 border; 94 fBorder = archive->FindInt32("_style", &border) == B_OK ? 95 (border_style)border : B_FANCY_BORDER; 96 97 // In a shallow archive, we may not have a target anymore. We must 98 // be prepared for this case 99 100 // don't confuse our scroll bars with our (eventual) target 101 int32 firstBar = 0; 102 if (!archive->FindBool("_no_target_")) { 103 fTarget = ChildAt(0); 104 firstBar++; 105 } else 106 fTarget = NULL; 107 108 // search for our scroll bars 109 110 fHorizontalScrollBar = NULL; 111 fVerticalScrollBar = NULL; 112 113 BView *view; 114 while ((view = ChildAt(firstBar++)) != NULL) { 115 BScrollBar *bar = dynamic_cast<BScrollBar *>(view); 116 if (bar == NULL) 117 continue; 118 119 if (bar->Orientation() == B_HORIZONTAL) 120 fHorizontalScrollBar = bar; 121 else if (bar->Orientation() == B_VERTICAL) 122 fVerticalScrollBar = bar; 123 } 124 125 fPreviousWidth = uint16(Bounds().Width()); 126 fPreviousHeight = uint16(Bounds().Height()); 127 } 128 129 130 BScrollView::~BScrollView() 131 { 132 } 133 134 135 BArchivable * 136 BScrollView::Instantiate(BMessage *archive) 137 { 138 if (validate_instantiation(archive, "BScrollView")) 139 return new BScrollView(archive); 140 141 return NULL; 142 } 143 144 145 status_t 146 BScrollView::Archive(BMessage *archive, bool deep) const 147 { 148 status_t status = BView::Archive(archive, deep); 149 if (status != B_OK) 150 return status; 151 152 // If this is a deep archive, the BView class will take care 153 // of our children. 154 155 if (status == B_OK && fBorder != B_FANCY_BORDER) 156 status = archive->AddInt32("_style", fBorder); 157 if (status == B_OK && fTarget == NULL) 158 status = archive->AddBool("_no_target_", true); 159 160 // The highlighted state is not archived, but since it is 161 // usually (or should be) used to indicate focus, this 162 // is probably the right thing to do. 163 164 return status; 165 } 166 167 168 void 169 BScrollView::AttachedToWindow() 170 { 171 BView::AttachedToWindow(); 172 173 if ((fHorizontalScrollBar == NULL && fVerticalScrollBar == NULL) 174 || (fHorizontalScrollBar != NULL && fVerticalScrollBar != NULL) 175 || Window()->Look() != B_DOCUMENT_WINDOW_LOOK) 176 return; 177 178 // If we have only one bar, we need to check if we are in the 179 // bottom right edge of a window with the B_DOCUMENT_LOOK to 180 // adjust the size of the bar to acknowledge the resize knob. 181 182 BRect bounds = ConvertToScreen(Bounds()); 183 BRect windowBounds = Window()->Frame(); 184 185 if (bounds.right - BorderSize(fBorder) != windowBounds.right 186 || bounds.bottom - BorderSize(fBorder) != windowBounds.bottom) 187 return; 188 189 if (fHorizontalScrollBar) 190 fHorizontalScrollBar->ResizeBy(-B_V_SCROLL_BAR_WIDTH, 0); 191 else if (fVerticalScrollBar) 192 fVerticalScrollBar->ResizeBy(0, -B_H_SCROLL_BAR_HEIGHT); 193 } 194 195 196 void 197 BScrollView::DetachedFromWindow() 198 { 199 BView::DetachedFromWindow(); 200 } 201 202 203 void 204 BScrollView::AllAttached() 205 { 206 BView::AllAttached(); 207 } 208 209 210 void 211 BScrollView::AllDetached() 212 { 213 BView::AllDetached(); 214 } 215 216 217 void 218 BScrollView::Draw(BRect updateRect) 219 { 220 if (fBorder == B_PLAIN_BORDER) { 221 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_2_TINT)); 222 StrokeRect(Bounds()); 223 return; 224 } else if (fBorder != B_FANCY_BORDER) 225 return; 226 227 BRect bounds = Bounds(); 228 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_2_TINT)); 229 StrokeRect(bounds.InsetByCopy(1, 1)); 230 231 if (fHighlighted && Window()->IsActive()) { 232 SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR)); 233 StrokeRect(bounds); 234 } else { 235 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT)); 236 StrokeLine(bounds.LeftBottom(), bounds.LeftTop()); 237 bounds.left++; 238 StrokeLine(bounds.LeftTop(), bounds.RightTop()); 239 240 SetHighColor(ui_color(B_SHINE_COLOR)); 241 StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); 242 bounds.top++; 243 bounds.bottom--; 244 StrokeLine(bounds.RightBottom(), bounds.RightTop()); 245 } 246 } 247 248 249 BScrollBar * 250 BScrollView::ScrollBar(orientation posture) const 251 { 252 if (posture == B_HORIZONTAL) 253 return fHorizontalScrollBar; 254 255 return fVerticalScrollBar; 256 } 257 258 259 void 260 BScrollView::SetBorder(border_style border) 261 { 262 if (fBorder == border) 263 return; 264 265 float offset = BorderSize(fBorder) - BorderSize(border); 266 float resize = 2 * offset; 267 268 float horizontalGap = 0, verticalGap = 0; 269 float change = 0; 270 if (border == B_NO_BORDER || fBorder == B_NO_BORDER) { 271 if (fHorizontalScrollBar != NULL) 272 verticalGap = border != B_NO_BORDER ? 1 : -1; 273 if (fVerticalScrollBar != NULL) 274 horizontalGap = border != B_NO_BORDER ? 1 : -1; 275 276 change = border != B_NO_BORDER ? -1 : 1; 277 if (fHorizontalScrollBar == NULL || fVerticalScrollBar == NULL) 278 change *= 2; 279 } 280 281 fBorder = border; 282 283 int32 savedResizingMode = 0; 284 if (fTarget != NULL) { 285 savedResizingMode = fTarget->ResizingMode(); 286 fTarget->SetResizingMode(B_FOLLOW_NONE); 287 } 288 289 MoveBy(offset, offset); 290 ResizeBy(-resize - horizontalGap, -resize - verticalGap); 291 292 if (fTarget != NULL) { 293 fTarget->MoveBy(-offset, -offset); 294 fTarget->SetResizingMode(savedResizingMode); 295 } 296 297 if (fHorizontalScrollBar != NULL) { 298 fHorizontalScrollBar->MoveBy(-offset - verticalGap, offset + verticalGap); 299 fHorizontalScrollBar->ResizeBy(resize + horizontalGap - change, 0); 300 } 301 if (fVerticalScrollBar != NULL) { 302 fVerticalScrollBar->MoveBy(offset + horizontalGap, -offset - horizontalGap); 303 fVerticalScrollBar->ResizeBy(0, resize + verticalGap - change); 304 } 305 306 SetFlags(ModifyFlags(Flags(), border)); 307 } 308 309 310 border_style 311 BScrollView::Border() const 312 { 313 return fBorder; 314 } 315 316 317 status_t 318 BScrollView::SetBorderHighlighted(bool state) 319 { 320 if (fHighlighted == state) 321 return B_OK; 322 323 if (fBorder != B_FANCY_BORDER) 324 // highlighting only works for B_FANCY_BORDER 325 return B_ERROR; 326 327 fHighlighted = state; 328 329 /* The BeBook describes something like this: 330 331 if (LockLooper()) { 332 Draw(Bounds()); 333 UnlockLooper(); 334 } 335 */ 336 337 // but this is much cleaner, I think: 338 BRect bounds = Bounds(); 339 Invalidate(BRect(bounds.left, bounds.top, bounds.right, bounds.top)); 340 Invalidate(BRect(bounds.left, bounds.top + 1, bounds.left, bounds.bottom - 1)); 341 Invalidate(BRect(bounds.right, bounds.top + 1, bounds.right, bounds.bottom - 1)); 342 Invalidate(BRect(bounds.left, bounds.bottom, bounds.right, bounds.bottom)); 343 344 return B_OK; 345 } 346 347 348 bool 349 BScrollView::IsBorderHighlighted() const 350 { 351 return fHighlighted; 352 } 353 354 355 void 356 BScrollView::SetTarget(BView *target) 357 { 358 if (fTarget == target) 359 return; 360 361 if (fTarget != NULL) { 362 fTarget->TargetedByScrollView(NULL); 363 RemoveChild(fTarget); 364 365 // we are not supposed to delete the view 366 } 367 368 fTarget = target; 369 if (fHorizontalScrollBar != NULL) 370 fHorizontalScrollBar->SetTarget(target); 371 if (fVerticalScrollBar != NULL) 372 fVerticalScrollBar->SetTarget(target); 373 374 if (target != NULL) { 375 target->MoveTo(BorderSize(fBorder), BorderSize(fBorder)); 376 target->TargetedByScrollView(this); 377 378 AddChild(target, ChildAt(0)); 379 // This way, we are making sure that the target will 380 // be added top most in the list (which is important 381 // for unarchiving) 382 } 383 } 384 385 386 BView * 387 BScrollView::Target() const 388 { 389 return fTarget; 390 } 391 392 393 void 394 BScrollView::MessageReceived(BMessage *message) 395 { 396 switch (message->what) { 397 default: 398 BView::MessageReceived(message); 399 } 400 } 401 402 403 void 404 BScrollView::MouseDown(BPoint point) 405 { 406 BView::MouseDown(point); 407 } 408 409 410 void 411 BScrollView::MouseUp(BPoint point) 412 { 413 BView::MouseUp(point); 414 } 415 416 417 void 418 BScrollView::MouseMoved(BPoint point, uint32 code, const BMessage *dragMessage) 419 { 420 BView::MouseMoved(point, code, dragMessage); 421 } 422 423 424 void 425 BScrollView::FrameMoved(BPoint position) 426 { 427 BView::FrameMoved(position); 428 } 429 430 431 void 432 BScrollView::FrameResized(float width, float height) 433 { 434 BView::FrameResized(width, height); 435 436 if (fBorder == B_NO_BORDER) 437 return; 438 439 // changes in width 440 441 BRect bounds = Bounds(); 442 float border = BorderSize(fBorder) - 1; 443 444 if (bounds.Width() > fPreviousWidth) { 445 // invalidate the region between the old and the new right border 446 BRect rect = bounds; 447 rect.left += fPreviousWidth - border; 448 rect.right--; 449 Invalidate(rect); 450 } else if (bounds.Width() < fPreviousWidth) { 451 // invalidate the region of the new right border 452 BRect rect = bounds; 453 rect.left = rect.right - border; 454 Invalidate(rect); 455 } 456 457 // changes in height 458 459 if (bounds.Height() > fPreviousHeight) { 460 // invalidate the region between the old and the new bottom border 461 BRect rect = bounds; 462 rect.top += fPreviousHeight - border; 463 rect.bottom--; 464 Invalidate(rect); 465 } else if (bounds.Height() < fPreviousHeight) { 466 // invalidate the region of the new bottom border 467 BRect rect = bounds; 468 rect.top = rect.bottom - border; 469 Invalidate(rect); 470 } 471 472 fPreviousWidth = uint16(bounds.Width()); 473 fPreviousHeight = uint16(bounds.Height()); 474 } 475 476 477 void 478 BScrollView::ResizeToPreferred() 479 { 480 BView::ResizeToPreferred(); 481 } 482 483 484 void 485 BScrollView::GetPreferredSize(float *_width, float *_height) 486 { 487 BRect frame = CalcFrame(fTarget, fHorizontalScrollBar, fVerticalScrollBar, fBorder); 488 489 if (fTarget != NULL) { 490 float width, height; 491 fTarget->GetPreferredSize(&width, &height); 492 493 frame.right += width - fTarget->Frame().Width(); 494 frame.bottom += height - fTarget->Frame().Height(); 495 } 496 497 if (_width) 498 *_width = frame.Width(); 499 if (_height) 500 *_height = frame.Height(); 501 } 502 503 504 505 506 /** This static method is used to calculate the frame that the 507 * ScrollView will cover depending on the frame of its target 508 * and which border style is used. 509 * It is used in the constructor and at other places. 510 */ 511 512 BRect 513 BScrollView::CalcFrame(BView *target, bool horizontal, bool vertical, border_style border) 514 { 515 BRect frame = target != NULL ? target->Frame() : BRect(0, 0, 80, 80); 516 517 if (vertical) 518 frame.right += B_V_SCROLL_BAR_WIDTH; 519 if (horizontal) 520 frame.bottom += B_H_SCROLL_BAR_HEIGHT; 521 522 float borderSize = BorderSize(border); 523 frame.InsetBy(-borderSize, -borderSize); 524 525 if (borderSize == 0) { 526 if (vertical) 527 frame.right++; 528 if (horizontal) 529 frame.bottom++; 530 } 531 532 return frame; 533 } 534 535 536 /** This method returns the size of the specified border 537 */ 538 539 float 540 BScrollView::BorderSize(border_style border) 541 { 542 if (border == B_FANCY_BORDER) 543 return kFancyBorderSize; 544 if (border == B_PLAIN_BORDER) 545 return kPlainBorderSize; 546 547 return 0; 548 } 549 550 551 /** This method changes the "flags" argument as passed on to 552 * the BView constructor. 553 */ 554 555 int32 556 BScrollView::ModifyFlags(int32 flags, border_style border) 557 { 558 // We either need B_FULL_UPDATE_ON_RESIZE or 559 // B_FRAME_EVENTS if we have to draw a border 560 if (border != B_NO_BORDER) 561 return flags | B_WILL_DRAW | (flags & B_FULL_UPDATE_ON_RESIZE ? 0 : B_FRAME_EVENTS); 562 563 return flags & ~(B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE); 564 } 565 566 567 void 568 BScrollView::WindowActivated(bool active) 569 { 570 if (fHighlighted) 571 Invalidate(); 572 573 BView::WindowActivated(active); 574 } 575 576 577 void 578 BScrollView::MakeFocus(bool state) 579 { 580 BView::MakeFocus(state); 581 } 582 583 584 BHandler * 585 BScrollView::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, int32 form, const char *property) 586 { 587 return BView::ResolveSpecifier(msg, index, specifier, form, property); 588 } 589 590 591 status_t 592 BScrollView::GetSupportedSuites(BMessage *data) 593 { 594 return BView::GetSupportedSuites(data); 595 } 596 597 598 BSize 599 BScrollView::MinSize() 600 { 601 // TODO: This is not yet correct. 602 BSize size = (fTarget ? fTarget->MinSize() : BSize(-1, -1)); 603 604 if (fVerticalScrollBar) 605 size.width += B_V_SCROLL_BAR_WIDTH; 606 if (fHorizontalScrollBar) 607 size.height += B_H_SCROLL_BAR_HEIGHT; 608 609 float borderSize = BorderSize(fBorder); 610 size.width += 2 * borderSize; 611 size.height += 2 * borderSize; 612 613 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 614 } 615 616 617 BSize 618 BScrollView::PreferredSize() 619 { 620 // TODO: This is not yet correct. 621 BSize size = (fTarget ? fTarget->PreferredSize() : BSize(-1, -1)); 622 623 if (fVerticalScrollBar) 624 size.width += B_V_SCROLL_BAR_WIDTH; 625 if (fHorizontalScrollBar) 626 size.height += B_H_SCROLL_BAR_HEIGHT; 627 628 float borderSize = BorderSize(fBorder); 629 size.width += 2 * borderSize; 630 size.height += 2 * borderSize; 631 632 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 633 } 634 635 636 status_t 637 BScrollView::Perform(perform_code d, void *arg) 638 { 639 return BView::Perform(d, arg); 640 } 641 642 643 BScrollView & 644 BScrollView::operator=(const BScrollView &) 645 { 646 return *this; 647 } 648 649 650 /** Although BScrollView::InitObject() was defined in the original ScrollView.h, 651 * it is not exported by the R5 libbe.so, so we don't have to export it as well. 652 */ 653 654 #if 0 655 void 656 BScrollView::InitObject() 657 { 658 } 659 #endif 660 661 662 // #pragma mark - 663 // Reserved virtuals 664 665 666 void BScrollView::_ReservedScrollView1() {} 667 void BScrollView::_ReservedScrollView2() {} 668 void BScrollView::_ReservedScrollView3() {} 669 void BScrollView::_ReservedScrollView4() {} 670 671