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