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