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