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