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