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