1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include <Debug.h> 40 #include <NodeMonitor.h> 41 #include <Volume.h> 42 #include <fs_info.h> 43 44 #include "Attributes.h" 45 #include "Commands.h" 46 #include "FSClipboard.h" 47 #include "IconCache.h" 48 #include "Pose.h" 49 #include "PoseView.h" 50 51 52 int32 53 CalcFreeSpace(BVolume* volume) 54 { 55 off_t capacity = volume->Capacity(); 56 if (capacity == 0) 57 return 100; 58 59 int32 percent 60 = static_cast<int32>(volume->FreeBytes() / (capacity / 100)); 61 62 // warn below 20 MB of free space (if this is less than 10% of free space) 63 if (volume->FreeBytes() < 20 * 1024 * 1024 && percent < 10) 64 return -2 - percent; 65 return percent; 66 } 67 68 69 // SymLink handling: 70 // symlink pose uses the resolved model to retrieve the icon, if not broken 71 // everything else, like the attributes, etc. is retrieved directly from the 72 // symlink itself 73 BPose::BPose(Model* model, BPoseView* view, uint32 clipboardMode, 74 bool selected) 75 : 76 fModel(model), 77 fWidgetList(4, false), 78 fClipboardMode(clipboardMode), 79 fPercent(-1), 80 fSelectionTime(0), 81 fIsSelected(selected), 82 fHasLocation(false), 83 fNeedsSaveLocation(false), 84 fListModeInited(false), 85 fWasAutoPlaced(false), 86 fBrokenSymLink(false), 87 fBackgroundClean(false) 88 { 89 CreateWidgets(view); 90 91 if (model->IsVolume()) { 92 fs_info info; 93 dev_t device = model->NodeRef()->device; 94 BVolume* volume = new BVolume(device); 95 if (volume->InitCheck() == B_OK 96 && fs_stat_dev(device, &info) == B_OK) { 97 // Philosophy here: 98 // Bars go on all read/write volumes 99 // Exceptions: Not on CDDA 100 if (strcmp(info.fsh_name,"cdda") != 0 101 && !volume->IsReadOnly()) { 102 // The volume is ok and we want space bars on it 103 gPeriodicUpdatePoses.AddPose(this, view, 104 _PeriodicUpdateCallback, volume); 105 if (TrackerSettings().ShowVolumeSpaceBar()) 106 fPercent = CalcFreeSpace(volume); 107 } else 108 delete volume; 109 } else 110 delete volume; 111 } 112 } 113 114 115 BPose::~BPose() 116 { 117 if (fModel->IsVolume()) { 118 // we might be registered for periodic updates 119 BVolume* volume = NULL; 120 if (gPeriodicUpdatePoses.RemovePose(this, (void**)&volume)) 121 delete volume; 122 } 123 int32 count = fWidgetList.CountItems(); 124 for (int32 i = 0; i < count; i++) 125 delete fWidgetList.ItemAt(i); 126 127 delete fModel; 128 } 129 130 131 void 132 BPose::CreateWidgets(BPoseView* poseView) 133 { 134 for (int32 index = 0; ; index++) { 135 BColumn* column = poseView->ColumnAt(index); 136 if (column == NULL) 137 break; 138 139 fWidgetList.AddItem(new BTextWidget(fModel, column, poseView)); 140 } 141 } 142 143 144 BTextWidget* 145 BPose::AddWidget(BPoseView* poseView, BColumn* column) 146 { 147 BModelOpener opener(fModel); 148 if (fModel->InitCheck() != B_OK) 149 return NULL; 150 151 BTextWidget* widget = new BTextWidget(fModel, column, poseView); 152 fWidgetList.AddItem(widget); 153 154 return widget; 155 } 156 157 158 BTextWidget* 159 BPose::AddWidget(BPoseView* poseView, BColumn* column, 160 ModelNodeLazyOpener &opener) 161 { 162 opener.OpenNode(); 163 if (fModel->InitCheck() != B_OK) 164 return NULL; 165 166 BTextWidget* widget = new BTextWidget(fModel, column, poseView); 167 fWidgetList.AddItem(widget); 168 169 return widget; 170 } 171 172 173 void 174 BPose::RemoveWidget(BPoseView*, BColumn* column) 175 { 176 int32 index; 177 BTextWidget* widget = WidgetFor(column->AttrHash(), &index); 178 if (widget != NULL) 179 delete fWidgetList.RemoveItemAt(index); 180 } 181 182 183 void 184 BPose::Commit(bool saveChanges, BPoint loc, BPoseView* poseView, 185 int32 poseIndex) 186 { 187 int32 count = fWidgetList.CountItems(); 188 for (int32 index = 0; index < count; index++) { 189 BTextWidget* widget = fWidgetList.ItemAt(index); 190 if (widget != NULL && widget->IsActive()) { 191 widget->StopEdit(saveChanges, loc, poseView, this, poseIndex); 192 break; 193 } 194 } 195 } 196 197 198 inline bool 199 OneMouseUp(BTextWidget* widget, BPose* pose, BPoseView* poseView, 200 BColumn* column, BPoint poseLoc, BPoint where) 201 { 202 BRect rect; 203 if (poseView->ViewMode() == kListMode) 204 rect = widget->CalcClickRect(poseLoc, column, poseView); 205 else 206 rect = widget->CalcClickRect(pose->Location(poseView), NULL, poseView); 207 208 if (rect.Contains(where)) { 209 widget->MouseUp(rect, poseView, pose, where); 210 return true; 211 } 212 213 return false; 214 } 215 216 217 void 218 BPose::MouseUp(BPoint poseLoc, BPoseView* poseView, BPoint where, int32) 219 { 220 WhileEachTextWidget(this, poseView, OneMouseUp, poseLoc, where); 221 } 222 223 224 inline void 225 OneCheckAndUpdate(BTextWidget* widget, BPose*, BPoseView* poseView, 226 BColumn* column, BPoint poseLoc) 227 { 228 widget->CheckAndUpdate(poseLoc, column, poseView, true); 229 } 230 231 232 void 233 BPose::UpdateAllWidgets(int32, BPoint poseLoc, BPoseView* poseView) 234 { 235 if (poseView->ViewMode() != kListMode) 236 poseLoc = Location(poseView); 237 238 ASSERT(fModel->IsNodeOpen()); 239 EachTextWidget(this, poseView, OneCheckAndUpdate, poseLoc); 240 } 241 242 243 void 244 BPose::UpdateWidgetAndModel(Model* resolvedModel, const char* attrName, 245 uint32 attrType, int32, BPoint poseLoc, BPoseView* poseView, bool visible) 246 { 247 if (poseView->ViewMode() != kListMode) 248 poseLoc = Location(poseView); 249 250 ASSERT(resolvedModel == NULL || resolvedModel->IsNodeOpen()); 251 252 if (attrName != NULL) { 253 // pick up new attributes and find out if icon needs updating 254 if (resolvedModel->AttrChanged(attrName) && visible) 255 UpdateIcon(poseLoc, poseView); 256 257 // ToDo: the following code is wrong, because this sort of hashing 258 // may overlap and we get aliasing 259 uint32 attrHash = AttrHashString(attrName, attrType); 260 BTextWidget* widget = WidgetFor(attrHash); 261 if (widget != NULL) { 262 BColumn* column = poseView->ColumnFor(attrHash); 263 if (column != NULL) 264 widget->CheckAndUpdate(poseLoc, column, poseView, visible); 265 } else if (attrType == 0) { 266 // attribute got likely removed, so let's search the 267 // column for the matching attribute name 268 int32 count = fWidgetList.CountItems(); 269 for (int32 i = 0; i < count; i++) { 270 BTextWidget* widget = fWidgetList.ItemAt(i); 271 BColumn* column = poseView->ColumnFor(widget->AttrHash()); 272 if (column != NULL 273 && strcmp(column->AttrName(), attrName) == 0) { 274 widget->CheckAndUpdate(poseLoc, column, poseView, visible); 275 break; 276 } 277 } 278 } 279 } else { 280 // no attr name means check all widgets for stat info changes 281 282 // pick up stat changes 283 if (resolvedModel && resolvedModel->StatChanged()) { 284 if (resolvedModel->InitCheck() != B_OK) 285 return; 286 287 if (visible) 288 UpdateIcon(poseLoc, poseView); 289 } 290 291 // distribute stat changes 292 for (int32 index = 0; ; index++) { 293 BColumn* column = poseView->ColumnAt(index); 294 if (column == NULL) 295 break; 296 297 if (column->StatField()) { 298 BTextWidget* widget = WidgetFor(column->AttrHash()); 299 if (widget != NULL) 300 widget->CheckAndUpdate(poseLoc, column, poseView, visible); 301 } 302 } 303 } 304 } 305 306 307 bool 308 BPose::_PeriodicUpdateCallback(BPose* pose, void* cookie) 309 { 310 return pose->UpdateVolumeSpaceBar((BVolume*)cookie); 311 } 312 313 314 bool 315 BPose::UpdateVolumeSpaceBar(BVolume* volume) 316 { 317 bool enabled = TrackerSettings().ShowVolumeSpaceBar(); 318 if (!enabled) { 319 if (fPercent == -1) 320 return false; 321 322 fPercent = -1; 323 return true; 324 } 325 326 int32 percent = CalcFreeSpace(volume); 327 if (fPercent != percent) { 328 if (percent > 100) 329 fPercent = 100; 330 else 331 fPercent = percent; 332 333 return true; 334 } 335 336 return false; 337 } 338 339 340 void 341 BPose::UpdateIcon(BPoint poseLoc, BPoseView* poseView) 342 { 343 IconCache::sIconCache->IconChanged(ResolvedModel()); 344 345 int32 iconSize = poseView->IconSizeInt(); 346 347 BRect rect; 348 if (poseView->ViewMode() == kListMode) { 349 rect = CalcRect(poseLoc, poseView); 350 rect.left += kListOffset; 351 rect.right = rect.left + iconSize; 352 rect.top = rect.bottom - iconSize; 353 } else { 354 BPoint location = Location(poseView); 355 rect.left = location.x; 356 rect.top = location.y; 357 rect.right = rect.left + iconSize; 358 rect.bottom = rect.top + iconSize; 359 } 360 361 poseView->Invalidate(rect); 362 } 363 364 365 void 366 BPose::UpdateBrokenSymLink(BPoint poseLoc, BPoseView* poseView) 367 { 368 ASSERT(TargetModel()->IsSymLink()); 369 ASSERT(TargetModel()->LinkTo() == NULL); 370 if (!TargetModel()->IsSymLink() || TargetModel()->LinkTo() != NULL) 371 return; 372 373 UpdateIcon(poseLoc, poseView); 374 } 375 376 377 void 378 BPose::UpdateWasBrokenSymlink(BPoint poseLoc, BPoseView* poseView) 379 { 380 if (!fModel->IsSymLink()) 381 return; 382 383 if (fModel->LinkTo() != NULL) { 384 BEntry entry(fModel->EntryRef(), true); 385 if (entry.InitCheck() != B_OK) { 386 watch_node(fModel->LinkTo()->NodeRef(), B_STOP_WATCHING, poseView); 387 fModel->SetLinkTo(NULL); 388 UpdateIcon(poseLoc, poseView); 389 } 390 return; 391 } 392 393 poseView->CreateSymlinkPoseTarget(fModel); 394 UpdateIcon(poseLoc, poseView); 395 if (fModel->LinkTo() != NULL) 396 fModel->LinkTo()->CloseNode(); 397 } 398 399 400 void 401 BPose::EditFirstWidget(BPoint poseLoc, BPoseView* poseView) 402 { 403 // find first editable widget 404 BColumn* column; 405 for (int32 i = 0; (column = poseView->ColumnAt(i)) != NULL; i++) { 406 BTextWidget* widget = WidgetFor(column->AttrHash()); 407 408 if (widget != NULL && widget->IsEditable()) { 409 BRect bounds; 410 // ToDo: 411 // fold the three StartEdit code sequences into a cover call 412 if (poseView->ViewMode() == kListMode) 413 bounds = widget->CalcRect(poseLoc, column, poseView); 414 else 415 bounds = widget->CalcRect(Location(poseView), NULL, poseView); 416 417 widget->StartEdit(bounds, poseView, this); 418 break; 419 } 420 } 421 } 422 423 424 void 425 BPose::EditPreviousNextWidgetCommon(BPoseView* poseView, bool next) 426 { 427 bool found = false; 428 int32 delta = next ? 1 : -1; 429 for (int32 index = next ? 0 : poseView->CountColumns() - 1; ; 430 index += delta) { 431 BColumn* column = poseView->ColumnAt(index); 432 if (column == NULL) { 433 // out of columns 434 break; 435 } 436 437 BTextWidget* widget = WidgetFor(column->AttrHash()); 438 if (widget == NULL) { 439 // no widget for this column, next 440 continue; 441 } 442 443 if (widget->IsActive()) { 444 poseView->CommitActivePose(); 445 found = true; 446 continue; 447 } 448 449 if (found && column->Editable()) { 450 BRect bounds; 451 if (poseView->ViewMode() == kListMode) { 452 int32 poseIndex = poseView->IndexOfPose(this); 453 BPoint poseLoc(0, poseIndex* poseView->ListElemHeight()); 454 bounds = widget->CalcRect(poseLoc, column, poseView); 455 } else 456 bounds = widget->CalcRect(Location(poseView), NULL, poseView); 457 458 widget->StartEdit(bounds, poseView, this); 459 break; 460 } 461 } 462 } 463 464 465 void 466 BPose::EditNextWidget(BPoseView* poseView) 467 { 468 EditPreviousNextWidgetCommon(poseView, true); 469 } 470 471 472 void 473 BPose::EditPreviousWidget(BPoseView* poseView) 474 { 475 EditPreviousNextWidgetCommon(poseView, false); 476 } 477 478 479 bool 480 BPose::PointInPose(const BPoseView* poseView, BPoint where) const 481 { 482 ASSERT(poseView->ViewMode() != kListMode); 483 484 BPoint location = Location(poseView); 485 486 if (poseView->ViewMode() == kIconMode) { 487 // check icon rect, then actual icon pixel 488 BRect rect(location, location); 489 rect.right += poseView->IconSizeInt() - 1; 490 rect.bottom += poseView->IconSizeInt() - 1; 491 492 if (rect.Contains(where)) { 493 return IconCache::sIconCache->IconHitTest(where - location, 494 ResolvedModel(), kNormalIcon, poseView->IconSize()); 495 } 496 497 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 498 if (widget) { 499 float textWidth = ceilf(widget->TextWidth(poseView) + 1); 500 rect.left += (poseView->IconSizeInt() - textWidth) / 2; 501 rect.right = rect.left + textWidth; 502 } 503 504 rect.top = location.y + poseView->IconSizeInt(); 505 rect.bottom = rect.top + poseView->FontHeight(); 506 507 return rect.Contains(where); 508 } 509 510 // MINI_ICON_MODE rect calc 511 BRect rect(location, location); 512 rect.right += B_MINI_ICON + kMiniIconSeparator; 513 rect.bottom += poseView->IconPoseHeight(); 514 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 515 if (widget != NULL) 516 rect.right += ceil(widget->TextWidth(poseView) + 1); 517 518 return rect.Contains(where); 519 } 520 521 522 bool 523 BPose::PointInPose(BPoint loc, const BPoseView* poseView, BPoint where, 524 BTextWidget** hitWidget) const 525 { 526 if (hitWidget) 527 *hitWidget = NULL; 528 529 // check intersection with icon 530 BRect rect = _IconRect(poseView, loc); 531 if (rect.Contains(where)) 532 return true; 533 534 for (int32 index = 0; ; index++) { 535 BColumn* column = poseView->ColumnAt(index); 536 if (column == NULL) 537 break; 538 539 BTextWidget* widget = WidgetFor(column->AttrHash()); 540 if (widget != NULL 541 && widget->CalcClickRect(loc, column, poseView).Contains(where)) { 542 if (hitWidget != NULL) 543 *hitWidget = widget; 544 545 return true; 546 } 547 } 548 549 return false; 550 } 551 552 553 void 554 BPose::Draw(BRect rect, const BRect& updateRect, BPoseView* poseView, 555 BView* drawView, bool fullDraw, BPoint offset, bool selected) 556 { 557 // If the background wasn't cleared and Draw() is not called after 558 // having edited a name or similar (with fullDraw) 559 if (!fBackgroundClean && !fullDraw) { 560 fBackgroundClean = true; 561 poseView->Invalidate(rect); 562 return; 563 } else 564 fBackgroundClean = false; 565 566 bool directDraw = (drawView == poseView); 567 bool windowActive = poseView->Window()->IsActive(); 568 bool showSelectionWhenInactive = poseView->fShowSelectionWhenInactive; 569 bool isDrawingSelectionRect = poseView->fIsDrawingSelectionRect; 570 571 ModelNodeLazyOpener modelOpener(fModel); 572 573 if (poseView->ViewMode() == kListMode) { 574 BRect iconRect = _IconRect(poseView, rect.LeftTop()); 575 if (updateRect.Intersects(iconRect)) { 576 iconRect.OffsetBy(offset); 577 DrawIcon(iconRect.LeftTop(), drawView, poseView->IconSize(), 578 directDraw, !windowActive && !showSelectionWhenInactive); 579 } 580 581 // draw text 582 int32 columnsToDraw = 1; 583 if (fullDraw) 584 columnsToDraw = poseView->CountColumns(); 585 586 for (int32 index = 0; index < columnsToDraw; index++) { 587 BColumn* column = poseView->ColumnAt(index); 588 if (column == NULL) 589 break; 590 591 // if widget doesn't exist, create it 592 BTextWidget* widget = WidgetFor(column, poseView, modelOpener); 593 594 if (widget != NULL && widget->IsVisible()) { 595 BRect widgetRect(widget->ColumnRect(rect.LeftTop(), column, 596 poseView)); 597 598 if (updateRect.Intersects(widgetRect)) { 599 BRect widgetTextRect(widget->CalcRect(rect.LeftTop(), 600 column, poseView)); 601 602 bool selectDuringDraw = directDraw && selected 603 && windowActive; 604 605 if (index == 0 && selectDuringDraw) { 606 //draw with dark background to select text 607 drawView->PushState(); 608 drawView->SetLowColor(0, 0, 0); 609 } 610 611 if (index == 0) { 612 widget->Draw(widgetRect, widgetTextRect, 613 column->Width(), poseView, drawView, selected, 614 fClipboardMode, offset, directDraw); 615 } else { 616 widget->Draw(widgetTextRect, widgetTextRect, 617 column->Width(), poseView, drawView, false, 618 fClipboardMode, offset, directDraw); 619 } 620 621 if (index == 0 && selectDuringDraw) 622 drawView->PopState(); 623 else if (index == 0 && selected) { 624 if (windowActive || isDrawingSelectionRect) { 625 widgetTextRect.OffsetBy(offset); 626 drawView->InvertRect(widgetTextRect); 627 } else if (!windowActive 628 && showSelectionWhenInactive) { 629 widgetTextRect.OffsetBy(offset); 630 drawView->PushState(); 631 drawView->SetDrawingMode(B_OP_BLEND); 632 drawView->SetHighColor(128, 128, 128, 255); 633 drawView->FillRect(widgetTextRect); 634 drawView->PopState(); 635 } 636 } 637 } 638 } 639 } 640 } else { 641 // draw in icon mode 642 BPoint location(Location(poseView)); 643 BPoint iconOrigin(location); 644 iconOrigin += offset; 645 646 DrawIcon(iconOrigin, drawView, poseView->IconSize(), directDraw, 647 !windowActive && !showSelectionWhenInactive); 648 649 BColumn* column = poseView->FirstColumn(); 650 if (column == NULL) 651 return; 652 653 BTextWidget* widget = WidgetFor(column, poseView, modelOpener); 654 if (widget == NULL || !widget->IsVisible()) 655 return; 656 657 rect = widget->CalcRect(location, NULL, poseView); 658 659 bool selectDuringDraw = directDraw && selected 660 && (poseView->IsDesktopWindow() || windowActive); 661 662 if (selectDuringDraw) { 663 // draw with dark background to select text 664 drawView->PushState(); 665 drawView->SetLowColor(0, 0, 0); 666 } 667 668 widget->Draw(rect, rect, rect.Width(), poseView, drawView, 669 selected, fClipboardMode, offset, directDraw); 670 671 if (selectDuringDraw) 672 drawView->PopState(); 673 else if (selected && directDraw) { 674 if (windowActive || isDrawingSelectionRect) { 675 rect.OffsetBy(offset); 676 drawView->InvertRect(rect); 677 } else if (!windowActive && showSelectionWhenInactive) { 678 drawView->PushState(); 679 drawView->SetDrawingMode(B_OP_BLEND); 680 drawView->SetHighColor(128, 128, 128, 255); 681 drawView->FillRect(rect); 682 drawView->PopState(); 683 } 684 } 685 } 686 } 687 688 689 void 690 BPose::DeselectWithoutErasingBackground(BRect, BPoseView* poseView) 691 { 692 ASSERT(poseView->ViewMode() != kListMode); 693 ASSERT(!IsSelected()); 694 695 BPoint location(Location(poseView)); 696 697 // draw icon directly 698 if (fPercent == -1) 699 DrawIcon(location, poseView, poseView->IconSize(), true); 700 else 701 UpdateIcon(location, poseView); 702 703 BColumn* column = poseView->FirstColumn(); 704 if (column == NULL) 705 return; 706 707 BTextWidget* widget = WidgetFor(column->AttrHash()); 708 if (widget == NULL || !widget->IsVisible()) 709 return; 710 711 // just invalidate the background, don't draw anything 712 poseView->Invalidate(widget->CalcRect(location, NULL, poseView)); 713 } 714 715 716 void 717 BPose::MoveTo(BPoint point, BPoseView* poseView, bool invalidate) 718 { 719 point.x = floorf(point.x); 720 point.y = floorf(point.y); 721 722 BRect oldBounds; 723 724 BPoint oldLocation = Location(poseView); 725 726 ASSERT(poseView->ViewMode() != kListMode); 727 if (point == oldLocation || poseView->ViewMode() == kListMode) 728 return; 729 730 if (invalidate) 731 oldBounds = CalcRect(poseView); 732 733 // might need to move a text view if we're active 734 if (poseView->ActivePose() == this) { 735 BView* border_view = poseView->FindView("BorderView"); 736 if (border_view) { 737 border_view->MoveBy(point.x - oldLocation.x, 738 point.y - oldLocation.y); 739 } 740 } 741 742 float scale = 1.0; 743 if (poseView->ViewMode() == kIconMode) { 744 scale = poseView->IconSize() / 32.0; 745 } 746 fLocation.x = point.x / scale; 747 fLocation.y = point.y / scale; 748 749 fHasLocation = true; 750 fNeedsSaveLocation = true; 751 752 if (invalidate) { 753 poseView->Invalidate(oldBounds); 754 poseView->Invalidate(CalcRect(poseView)); 755 } 756 } 757 758 759 BTextWidget* 760 BPose::ActiveWidget() const 761 { 762 for (int32 i = fWidgetList.CountItems(); i-- > 0;) { 763 BTextWidget* widget = fWidgetList.ItemAt(i); 764 if (widget->IsActive()) 765 return widget; 766 } 767 768 return NULL; 769 } 770 771 772 BTextWidget* 773 BPose::WidgetFor(uint32 attr, int32* index) const 774 { 775 int32 count = fWidgetList.CountItems(); 776 for (int32 i = 0; i < count; i++) { 777 BTextWidget* widget = fWidgetList.ItemAt(i); 778 if (widget->AttrHash() == attr) { 779 if (index != NULL) 780 *index = i; 781 782 return widget; 783 } 784 } 785 786 return NULL; 787 } 788 789 790 BTextWidget* 791 BPose::WidgetFor(BColumn* column, BPoseView* poseView, 792 ModelNodeLazyOpener &opener, int32* index) 793 { 794 BTextWidget* widget = WidgetFor(column->AttrHash(), index); 795 if (widget == NULL) 796 widget = AddWidget(poseView, column, opener); 797 798 return widget; 799 } 800 801 802 // the following method is deprecated 803 bool 804 BPose::TestLargeIconPixel(BPoint point) const 805 { 806 return IconCache::sIconCache->IconHitTest(point, ResolvedModel(), 807 kNormalIcon, B_LARGE_ICON); 808 } 809 810 811 void 812 BPose::DrawIcon(BPoint where, BView* view, icon_size which, bool direct, 813 bool drawUnselected) 814 { 815 if (fClipboardMode == kMoveSelectionTo) { 816 view->SetDrawingMode(B_OP_ALPHA); 817 view->SetHighColor(0, 0, 0, 64); 818 // set the level of transparency 819 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 820 } else if (direct) 821 view->SetDrawingMode(B_OP_OVER); 822 823 IconCache::sIconCache->Draw(ResolvedModel(), view, where, 824 fIsSelected && !drawUnselected ? kSelectedIcon : kNormalIcon, which, 825 true); 826 827 if (fPercent != -1) 828 DrawBar(where, view, which); 829 } 830 831 832 void 833 BPose::DrawBar(BPoint where, BView* view, icon_size which) 834 { 835 view->PushState(); 836 837 int32 size; 838 int32 barWidth; 839 int32 barHeight; 840 int32 yOffset; 841 if (which >= B_LARGE_ICON) { 842 size = which - 1; 843 barWidth = (int32)(7.0f / 32.0f * (float)which); 844 yOffset = 2; 845 barHeight = size - 4 - 2 * yOffset; 846 } else { 847 size = B_MINI_ICON; 848 barWidth = 4; 849 yOffset = 0; 850 barHeight = size - 4 - 2 * yOffset; 851 } 852 853 // the black shadowed line 854 view->SetHighColor(32, 32, 32, 92); 855 view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset)); 856 view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset)); 857 view->StrokeLine(BPoint(where.x + size - barWidth + 1, 858 where.y + size - yOffset)); 859 860 view->SetDrawingMode(B_OP_ALPHA); 861 862 // the gray frame 863 view->SetHighColor(76, 76, 76, 192); 864 BRect rect(where.x + size - barWidth,where.y + yOffset, 865 where.x + size - 1,where.y + size - 1 - yOffset); 866 view->StrokeRect(rect); 867 868 // calculate bar height 869 int32 percent = fPercent > -1 ? fPercent : -2 - fPercent; 870 int32 barPos = int32(barHeight * percent / 100.0); 871 if (barPos < 0) 872 barPos = 0; 873 else if (barPos > barHeight) 874 barPos = barHeight; 875 876 // the free space bar 877 view->SetHighColor(TrackerSettings().FreeSpaceColor()); 878 879 rect.InsetBy(1,1); 880 BRect bar(rect); 881 bar.bottom = bar.top + barPos - 1; 882 if (barPos > 0) 883 view->FillRect(bar); 884 885 // the used space bar 886 bar.top = bar.bottom + 1; 887 bar.bottom = rect.bottom; 888 view->SetHighColor(fPercent < -1 889 ? TrackerSettings().WarningSpaceColor() 890 : TrackerSettings().UsedSpaceColor()); 891 view->FillRect(bar); 892 893 view->PopState(); 894 } 895 896 897 void 898 BPose::DrawToggleSwitch(BRect, BPoseView*) 899 { 900 } 901 902 903 BPoint 904 BPose::Location(const BPoseView* poseView) const 905 { 906 float scale = 1.0; 907 if (poseView->ViewMode() == kIconMode) 908 scale = poseView->IconSize() / 32.0; 909 910 return BPoint(fLocation.x * scale, fLocation.y * scale); 911 } 912 913 914 void 915 BPose::SetLocation(BPoint point, const BPoseView* poseView) 916 { 917 float scale = 1.0; 918 if (poseView->ViewMode() == kIconMode) 919 scale = poseView->IconSize() / 32.0; 920 921 fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale)); 922 if (isinff(fLocation.x) || isinff(fLocation.y)) 923 debugger("BPose::SetLocation() - infinite location"); 924 925 fHasLocation = true; 926 } 927 928 929 BRect 930 BPose::CalcRect(BPoint loc, const BPoseView* poseView, bool minimalRect) const 931 { 932 ASSERT(poseView->ViewMode() == kListMode); 933 934 BColumn* column = poseView->LastColumn(); 935 BRect rect; 936 rect.left = loc.x; 937 rect.top = loc.y; 938 rect.right = loc.x + column->Offset() + column->Width(); 939 rect.bottom = rect.top + poseView->ListElemHeight(); 940 941 if (minimalRect) { 942 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 943 if (widget != NULL) { 944 rect.right = widget->CalcRect(loc, poseView->FirstColumn(), 945 poseView).right; 946 } 947 } 948 949 return rect; 950 } 951 952 953 BRect 954 BPose::CalcRect(const BPoseView* poseView) const 955 { 956 ASSERT(poseView->ViewMode() != kListMode); 957 958 BRect rect; 959 BPoint location = Location(poseView); 960 if (poseView->ViewMode() == kIconMode) { 961 rect.left = location.x; 962 rect.right = rect.left + poseView->IconSizeInt(); 963 964 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 965 if (widget) { 966 float textWidth = ceilf(widget->TextWidth(poseView) + 1); 967 if (textWidth > poseView->IconSizeInt()) { 968 rect.left += (poseView->IconSizeInt() - textWidth) / 2; 969 rect.right = rect.left + textWidth; 970 } 971 } 972 973 rect.top = location.y; 974 rect.bottom = rect.top + poseView->IconPoseHeight(); 975 } else { 976 // MINI_ICON_MODE rect calc 977 rect.left = location.x; 978 rect.top = location.y; 979 rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator; 980 rect.bottom = rect.top + poseView->IconPoseHeight(); 981 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 982 if (widget) 983 rect.right += ceil(widget->TextWidth(poseView) + 1); 984 } 985 986 return rect; 987 } 988 989 990 BRect 991 BPose::_IconRect(const BPoseView* poseView, BPoint location) const 992 { 993 uint32 size = poseView->IconSizeInt(); 994 BRect rect; 995 rect.left = location.x + kListOffset; 996 rect.right = rect.left + size; 997 rect.top = location.y + (poseView->ListElemHeight() - size) / 2.f; 998 rect.bottom = rect.top + size; 999 return rect; 1000 } 1001 1002 1003 #if DEBUG 1004 void 1005 BPose::PrintToStream() 1006 { 1007 TargetModel()->PrintToStream(); 1008 switch (fClipboardMode) { 1009 case kMoveSelectionTo: 1010 PRINT(("clipboardMode: Cut\n")); 1011 break; 1012 1013 case kCopySelectionTo: 1014 PRINT(("clipboardMode: Copy\n")); 1015 break; 1016 1017 default: 1018 PRINT(("clipboardMode: 0 - not in clipboard\n")); 1019 break; 1020 } 1021 PRINT(("%sselected\n", IsSelected() ? "" : "not ")); 1022 PRINT(("location %s x:%f y:%f\n", HasLocation() ? "" : "unknown ", 1023 HasLocation() ? fLocation.x : 0, 1024 HasLocation() ? fLocation.y : 0)); 1025 PRINT(("%s autoplaced \n", WasAutoPlaced() ? "was" : "not")); 1026 } 1027 #endif 1028