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