1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include <Debug.h> 40 #include <NodeMonitor.h> 41 #include <Volume.h> 42 #include <fs_info.h> 43 44 #include "Attributes.h" 45 #include "Commands.h" 46 #include "FSClipboard.h" 47 #include "IconCache.h" 48 #include "Pose.h" 49 #include "PoseView.h" 50 #include "Utilities.h" 51 52 53 int32 54 CalcFreeSpace(BVolume* volume) 55 { 56 off_t capacity = volume->Capacity(); 57 if (capacity == 0) 58 return 100; 59 60 int32 percent 61 = static_cast<int32>(volume->FreeBytes() / (capacity / 100)); 62 63 // warn below 20 MB of free space (if this is less than 10% of free space) 64 if (volume->FreeBytes() < 20 * 1024 * 1024 && percent < 10) 65 return -2 - percent; 66 return percent; 67 } 68 69 70 // SymLink handling: 71 // symlink pose uses the resolved model to retrieve the icon, if not broken 72 // everything else, like the attributes, etc. is retrieved directly from the 73 // symlink itself 74 BPose::BPose(Model* model, BPoseView* view, uint32 clipboardMode, 75 bool selected) 76 : 77 fModel(model), 78 fWidgetList(4, true), 79 fClipboardMode(clipboardMode), 80 fPercent(-1), 81 fSelectionTime(0), 82 fIsSelected(selected), 83 fHasLocation(false), 84 fNeedsSaveLocation(false), 85 fListModeInited(false), 86 fWasAutoPlaced(false), 87 fBrokenSymLink(false), 88 fBackgroundClean(false) 89 { 90 CreateWidgets(view); 91 92 if (model->IsVolume()) { 93 fs_info info; 94 dev_t device = model->NodeRef()->device; 95 BVolume* volume = new BVolume(device); 96 if (volume->InitCheck() == B_OK 97 && fs_stat_dev(device, &info) == B_OK) { 98 // Philosophy here: 99 // Bars go on all read/write volumes 100 // Exceptions: Not on CDDA 101 if (strcmp(info.fsh_name,"cdda") != 0 102 && !volume->IsReadOnly()) { 103 // The volume is ok and we want space bars on it 104 gPeriodicUpdatePoses.AddPose(this, view, 105 _PeriodicUpdateCallback, volume); 106 if (TrackerSettings().ShowVolumeSpaceBar()) 107 fPercent = CalcFreeSpace(volume); 108 } else 109 delete volume; 110 } else 111 delete volume; 112 } 113 } 114 115 116 BPose::~BPose() 117 { 118 if (fModel->IsVolume()) { 119 // we might be registered for periodic updates 120 BVolume* volume = NULL; 121 if (gPeriodicUpdatePoses.RemovePose(this, (void**)&volume)) 122 delete volume; 123 } 124 125 delete fModel; 126 } 127 128 129 void 130 BPose::CreateWidgets(BPoseView* poseView) 131 { 132 for (int32 index = 0; ; index++) { 133 BColumn* column = poseView->ColumnAt(index); 134 if (column == NULL) 135 break; 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 return widget; 151 } 152 153 154 BTextWidget* 155 BPose::AddWidget(BPoseView* poseView, BColumn* column, 156 ModelNodeLazyOpener &opener) 157 { 158 opener.OpenNode(); 159 if (fModel->InitCheck() != B_OK) 160 return NULL; 161 162 BTextWidget* widget = new BTextWidget(fModel, column, poseView); 163 fWidgetList.AddItem(widget); 164 165 return widget; 166 } 167 168 169 void 170 BPose::RemoveWidget(BPoseView*, BColumn* column) 171 { 172 int32 index; 173 BTextWidget* widget = WidgetFor(column->AttrHash(), &index); 174 if (widget) 175 delete fWidgetList.RemoveItemAt(index); 176 } 177 178 179 void 180 BPose::Commit(bool saveChanges, BPoint loc, BPoseView* poseView, 181 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, 196 BColumn* column, 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(poseView), 0, poseView); 203 204 if (rect.Contains(where)) { 205 widget->MouseUp(rect, poseView, pose, where); 206 return true; 207 } 208 209 return false; 210 } 211 212 213 void 214 BPose::MouseUp(BPoint poseLoc, BPoseView* poseView, BPoint where, int32) 215 { 216 WhileEachTextWidget(this, poseView, OneMouseUp, poseLoc, where); 217 } 218 219 220 inline void 221 OneCheckAndUpdate(BTextWidget* widget, BPose*, BPoseView* poseView, 222 BColumn* column, BPoint poseLoc) 223 { 224 widget->CheckAndUpdate(poseLoc, column, poseView, true); 225 } 226 227 228 void 229 BPose::UpdateAllWidgets(int32, BPoint poseLoc, BPoseView* poseView) 230 { 231 if (poseView->ViewMode() != kListMode) 232 poseLoc = Location(poseView); 233 234 ASSERT(fModel->IsNodeOpen()); 235 EachTextWidget(this, poseView, OneCheckAndUpdate, poseLoc); 236 } 237 238 239 void 240 BPose::UpdateWidgetAndModel(Model* resolvedModel, const char* attrName, 241 uint32 attrType, int32, BPoint poseLoc, BPoseView* poseView, bool visible) 242 { 243 if (poseView->ViewMode() != kListMode) 244 poseLoc = Location(poseView); 245 246 ASSERT(resolvedModel == NULL || resolvedModel->IsNodeOpen()); 247 248 if (attrName != NULL) { 249 // pick up new attributes and find out if icon needs updating 250 if (resolvedModel->AttrChanged(attrName) && visible) 251 UpdateIcon(poseLoc, poseView); 252 253 // ToDo: the following code is wrong, because this sort of hashing 254 // may overlap and we get aliasing 255 uint32 attrHash = AttrHashString(attrName, attrType); 256 BTextWidget* widget = WidgetFor(attrHash); 257 if (widget) { 258 BColumn* column = poseView->ColumnFor(attrHash); 259 if (column) 260 widget->CheckAndUpdate(poseLoc, column, poseView, visible); 261 } else if (attrType == 0) { 262 // attribute got likely removed, so let's search the 263 // column for the matching attribute name 264 int32 count = fWidgetList.CountItems(); 265 for (int32 i = 0; i < count; i++) { 266 BTextWidget* widget = fWidgetList.ItemAt(i); 267 BColumn* column = poseView->ColumnFor(widget->AttrHash()); 268 if (column != NULL && !strcmp(column->AttrName(), attrName)) { 269 widget->CheckAndUpdate(poseLoc, column, poseView, 270 visible); 271 break; 272 } 273 } 274 } 275 } else { 276 // no attr name means check all widgets for stat info changes 277 278 // pick up stat changes 279 if (resolvedModel && resolvedModel->StatChanged()) { 280 if (resolvedModel->InitCheck() != B_OK) 281 return; 282 283 if (visible) 284 UpdateIcon(poseLoc, poseView); 285 } 286 287 // distribute stat changes 288 for (int32 index = 0; ; index++) { 289 BColumn* column = poseView->ColumnAt(index); 290 if (column == NULL) 291 break; 292 293 if (column->StatField()) { 294 BTextWidget* widget = WidgetFor(column->AttrHash()); 295 if (widget) { 296 widget->CheckAndUpdate(poseLoc, column, poseView, 297 visible); 298 } 299 } 300 } 301 } 302 } 303 304 305 bool 306 BPose::_PeriodicUpdateCallback(BPose* pose, void* cookie) 307 { 308 return pose->UpdateVolumeSpaceBar((BVolume*)cookie); 309 } 310 311 312 bool 313 BPose::UpdateVolumeSpaceBar(BVolume* volume) 314 { 315 bool enabled = TrackerSettings().ShowVolumeSpaceBar(); 316 if (!enabled) { 317 if (fPercent == -1) 318 return false; 319 320 fPercent = -1; 321 return true; 322 } 323 324 int32 percent = CalcFreeSpace(volume); 325 if (fPercent != percent) { 326 if (percent > 100) 327 fPercent = 100; 328 else 329 fPercent = percent; 330 331 return true; 332 } 333 334 return false; 335 } 336 337 338 void 339 BPose::UpdateIcon(BPoint poseLoc, BPoseView* poseView) 340 { 341 IconCache::sIconCache->IconChanged(ResolvedModel()); 342 343 int32 iconSize = poseView->IconSizeInt(); 344 345 BRect rect; 346 if (poseView->ViewMode() == kListMode) { 347 rect = CalcRect(poseLoc, poseView); 348 rect.left += kListOffset; 349 rect.right = rect.left + iconSize; 350 rect.top = rect.bottom - iconSize; 351 } else { 352 BPoint location = Location(poseView); 353 rect.left = location.x; 354 rect.top = location.y; 355 rect.right = rect.left + iconSize; 356 rect.bottom = rect.top + iconSize; 357 } 358 359 poseView->Invalidate(rect); 360 } 361 362 363 void 364 BPose::UpdateBrokenSymLink(BPoint poseLoc, BPoseView* poseView) 365 { 366 ASSERT(TargetModel()->IsSymLink()); 367 ASSERT(!TargetModel()->LinkTo()); 368 369 if (!TargetModel()->IsSymLink() || TargetModel()->LinkTo()) 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()) { 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()) 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 && 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 widget->StartEdit(bounds, poseView, this); 416 break; 417 } 418 } 419 } 420 421 422 void 423 BPose::EditPreviousNextWidgetCommon(BPoseView* poseView, bool next) 424 { 425 bool found = false; 426 int32 delta = next ? 1 : -1; 427 for (int32 index = next ? 0 : poseView->CountColumns() - 1; ; 428 index += delta) { 429 BColumn* column = poseView->ColumnAt(index); 430 if (column == NULL) { 431 // out of columns 432 break; 433 } 434 435 BTextWidget* widget = WidgetFor(column->AttrHash()); 436 if (widget == NULL) { 437 // no widget for this column, next 438 continue; 439 } 440 441 if (widget->IsActive()) { 442 poseView->CommitActivePose(); 443 found = true; 444 continue; 445 } 446 447 if (found && column->Editable()) { 448 BRect bounds; 449 if (poseView->ViewMode() == kListMode) { 450 int32 poseIndex = poseView->IndexOfPose(this); 451 BPoint poseLoc(0, poseIndex* poseView->ListElemHeight()); 452 bounds = widget->CalcRect(poseLoc, column, poseView); 453 } else 454 bounds = widget->CalcRect(Location(poseView), 0, poseView); 455 456 widget->StartEdit(bounds, poseView, this); 457 break; 458 } 459 } 460 } 461 462 463 void 464 BPose::EditNextWidget(BPoseView* poseView) 465 { 466 EditPreviousNextWidgetCommon(poseView, true); 467 } 468 469 470 void 471 BPose::EditPreviousWidget(BPoseView* poseView) 472 { 473 EditPreviousNextWidgetCommon(poseView, false); 474 } 475 476 477 bool 478 BPose::PointInPose(const BPoseView* poseView, BPoint where) const 479 { 480 ASSERT(poseView->ViewMode() != kListMode); 481 482 BPoint location = Location(poseView); 483 484 if (poseView->ViewMode() == kIconMode) { 485 // check icon rect, then actual icon pixel 486 BRect rect(location, location); 487 rect.right += poseView->IconSizeInt() - 1; 488 rect.bottom += poseView->IconSizeInt() - 1; 489 490 if (rect.Contains(where)) { 491 return IconCache::sIconCache->IconHitTest(where - location, 492 ResolvedModel(), kNormalIcon, poseView->IconSize()); 493 } 494 495 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 496 if (widget) { 497 float textWidth = ceilf(widget->TextWidth(poseView) + 1); 498 rect.left += (poseView->IconSizeInt() - textWidth) / 2; 499 rect.right = rect.left + textWidth; 500 } 501 502 rect.top = location.y + poseView->IconSizeInt(); 503 rect.bottom = rect.top + poseView->FontHeight(); 504 505 return rect.Contains(where); 506 } 507 508 // MINI_ICON_MODE rect calc 509 BRect rect(location, location); 510 rect.right += B_MINI_ICON + kMiniIconSeparator; 511 rect.bottom += poseView->IconPoseHeight(); 512 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 513 if (widget != NULL) 514 rect.right += ceil(widget->TextWidth(poseView) + 1); 515 516 return rect.Contains(where); 517 } 518 519 520 bool 521 BPose::PointInPose(BPoint loc, const BPoseView* poseView, BPoint where, 522 BTextWidget** hitWidget) const 523 { 524 if (hitWidget) 525 *hitWidget = NULL; 526 527 // check intersection with icon 528 BRect rect; 529 rect.left = loc.x + kListOffset; 530 rect.right = rect.left + B_MINI_ICON; 531 rect.bottom = loc.y + poseView->ListElemHeight(); 532 rect.top = rect.bottom - B_MINI_ICON; 533 if (rect.Contains(where)) 534 return true; 535 536 for (int32 index = 0; ; index++) { 537 BColumn* column = poseView->ColumnAt(index); 538 if (column == NULL) 539 break; 540 BTextWidget* widget = WidgetFor(column->AttrHash()); 541 if (widget 542 && widget->CalcClickRect(loc, column, poseView).Contains(where)) { 543 if (hitWidget) 544 *hitWidget = widget; 545 return true; 546 } 547 } 548 549 return false; 550 } 551 552 553 void 554 BPose::Draw(BRect rect, const BRect& updateRect, BPoseView* poseView, 555 BView* drawView, bool fullDraw, BPoint offset, bool selected) 556 { 557 // If the background wasn't cleared and Draw() is not called after 558 // having edited a name or similar (with fullDraw) 559 if (!fBackgroundClean && !fullDraw) { 560 fBackgroundClean = true; 561 poseView->Invalidate(rect); 562 return; 563 } else 564 fBackgroundClean = false; 565 566 bool directDraw = (drawView == poseView); 567 bool windowActive = poseView->Window()->IsActive(); 568 bool showSelectionWhenInactive = poseView->fShowSelectionWhenInactive; 569 bool isDrawingSelectionRect = poseView->fIsDrawingSelectionRect; 570 571 ModelNodeLazyOpener modelOpener(fModel); 572 573 if (poseView->ViewMode() == kListMode) { 574 uint32 size = poseView->IconSizeInt(); 575 BRect iconRect(rect); 576 iconRect.left += kListOffset; 577 iconRect.right = iconRect.left + size; 578 iconRect.top = iconRect.bottom - size; 579 if (updateRect.Intersects(iconRect)) { 580 iconRect.OffsetBy(offset); 581 DrawIcon(iconRect.LeftTop(), drawView, poseView->IconSize(), 582 directDraw, !windowActive && !showSelectionWhenInactive); 583 } 584 585 // draw text 586 int32 columnsToDraw = 1; 587 if (fullDraw) 588 columnsToDraw = poseView->CountColumns(); 589 590 for (int32 index = 0; index < columnsToDraw; index++) { 591 BColumn* column = poseView->ColumnAt(index); 592 if (column == NULL) 593 break; 594 595 // if widget doesn't exist, create it 596 BTextWidget* widget = WidgetFor(column, poseView, modelOpener); 597 598 if (widget && widget->IsVisible()) { 599 BRect widgetRect(widget->ColumnRect(rect.LeftTop(), column, 600 poseView)); 601 602 if (updateRect.Intersects(widgetRect)) { 603 BRect widgetTextRect(widget->CalcRect(rect.LeftTop(), 604 column, poseView)); 605 606 bool selectDuringDraw = directDraw && selected 607 && windowActive; 608 609 if (index == 0 && selectDuringDraw) { 610 //draw with dark background to select text 611 drawView->PushState(); 612 drawView->SetLowColor(0, 0, 0); 613 } 614 615 if (index == 0) { 616 widget->Draw(widgetRect, widgetTextRect, 617 column->Width(), poseView, drawView, selected, 618 fClipboardMode, offset, directDraw); 619 } else { 620 widget->Draw(widgetTextRect, widgetTextRect, 621 column->Width(), poseView, drawView, false, 622 fClipboardMode, offset, directDraw); 623 } 624 625 if (index == 0 && selectDuringDraw) 626 drawView->PopState(); 627 else if (index == 0 && selected) { 628 if (windowActive || isDrawingSelectionRect) { 629 widgetTextRect.OffsetBy(offset); 630 drawView->InvertRect(widgetTextRect); 631 } else if (!windowActive 632 && showSelectionWhenInactive) { 633 widgetTextRect.OffsetBy(offset); 634 drawView->PushState(); 635 drawView->SetDrawingMode(B_OP_BLEND); 636 drawView->SetHighColor(128, 128, 128, 255); 637 drawView->FillRect(widgetTextRect); 638 drawView->PopState(); 639 } 640 } 641 } 642 } 643 } 644 } else { 645 // draw in icon mode 646 BPoint location(Location(poseView)); 647 BPoint iconOrigin(location); 648 iconOrigin += offset; 649 650 DrawIcon(iconOrigin, drawView, poseView->IconSize(), directDraw, 651 !windowActive && !showSelectionWhenInactive); 652 653 BColumn* column = poseView->FirstColumn(); 654 if (column == NULL) 655 return; 656 657 BTextWidget* widget = WidgetFor(column, poseView, modelOpener); 658 if (widget == NULL || !widget->IsVisible()) 659 return; 660 661 rect = widget->CalcRect(location, 0, poseView); 662 663 bool selectDuringDraw = directDraw && selected 664 && (poseView->IsDesktopWindow() || windowActive); 665 666 if (selectDuringDraw) { 667 // draw with dark background to select text 668 drawView->PushState(); 669 drawView->SetLowColor(0, 0, 0); 670 } 671 672 widget->Draw(rect, rect, rect.Width(), poseView, drawView, 673 selected, fClipboardMode, offset, directDraw); 674 675 if (selectDuringDraw) 676 drawView->PopState(); 677 else if (selected && directDraw) { 678 if (windowActive || isDrawingSelectionRect) { 679 rect.OffsetBy(offset); 680 drawView->InvertRect(rect); 681 } else if (!windowActive && showSelectionWhenInactive) { 682 drawView->PushState(); 683 drawView->SetDrawingMode(B_OP_BLEND); 684 drawView->SetHighColor(128, 128, 128, 255); 685 drawView->FillRect(rect); 686 drawView->PopState(); 687 } 688 } 689 } 690 } 691 692 693 void 694 BPose::DeselectWithoutErasingBackground(BRect, BPoseView* poseView) 695 { 696 ASSERT(poseView->ViewMode() != kListMode); 697 ASSERT(!IsSelected()); 698 699 BPoint location(Location(poseView)); 700 701 // draw icon directly 702 if (fPercent == -1) 703 DrawIcon(location, poseView, poseView->IconSize(), true); 704 else 705 UpdateIcon(location, poseView); 706 707 BColumn* column = poseView->FirstColumn(); 708 if (column == NULL) 709 return; 710 711 BTextWidget* widget = WidgetFor(column->AttrHash()); 712 if (widget == NULL || !widget->IsVisible()) 713 return; 714 715 // just invalidate the background, don't draw anything 716 poseView->Invalidate(widget->CalcRect(location, 0, poseView)); 717 } 718 719 720 void 721 BPose::MoveTo(BPoint point, BPoseView* poseView, bool invalidate) 722 { 723 point.x = floorf(point.x); 724 point.y = floorf(point.y); 725 726 BRect oldBounds; 727 728 BPoint oldLocation = Location(poseView); 729 730 ASSERT(poseView->ViewMode() != kListMode); 731 if (point == oldLocation || poseView->ViewMode() == kListMode) 732 return; 733 734 if (invalidate) 735 oldBounds = CalcRect(poseView); 736 737 // might need to move a text view if we're active 738 if (poseView->ActivePose() == this) { 739 BView* border_view = poseView->FindView("BorderView"); 740 if (border_view) { 741 border_view->MoveBy(point.x - oldLocation.x, 742 point.y - oldLocation.y); 743 } 744 } 745 746 float scale = 1.0; 747 if (poseView->ViewMode() == kIconMode) { 748 scale = poseView->IconSize() / 32.0; 749 } 750 fLocation.x = point.x / scale; 751 fLocation.y = point.y / scale; 752 753 fHasLocation = true; 754 fNeedsSaveLocation = true; 755 756 if (invalidate) { 757 poseView->Invalidate(oldBounds); 758 poseView->Invalidate(CalcRect(poseView)); 759 } 760 } 761 762 763 BTextWidget* 764 BPose::ActiveWidget() const 765 { 766 for (int32 i = fWidgetList.CountItems(); i-- > 0;) { 767 BTextWidget* widget = fWidgetList.ItemAt(i); 768 if (widget->IsActive()) 769 return widget; 770 } 771 772 return NULL; 773 } 774 775 776 BTextWidget* 777 BPose::WidgetFor(uint32 attr, int32* index) const 778 { 779 int32 count = fWidgetList.CountItems(); 780 for (int32 i = 0; i < count; i++) { 781 BTextWidget* widget = fWidgetList.ItemAt(i); 782 if (widget->AttrHash() == attr) { 783 if (index != NULL) 784 *index = i; 785 786 return widget; 787 } 788 } 789 790 return 0; 791 } 792 793 794 BTextWidget* 795 BPose::WidgetFor(BColumn* column, BPoseView* poseView, 796 ModelNodeLazyOpener &opener, int32* index) 797 { 798 BTextWidget* widget = WidgetFor(column->AttrHash(), index); 799 if (widget == NULL) 800 widget = AddWidget(poseView, column, opener); 801 802 return widget; 803 } 804 805 806 // the following method is deprecated 807 bool 808 BPose::TestLargeIconPixel(BPoint point) const 809 { 810 return IconCache::sIconCache->IconHitTest(point, ResolvedModel(), 811 kNormalIcon, B_LARGE_ICON); 812 } 813 814 815 void 816 BPose::DrawIcon(BPoint where, BView* view, icon_size which, bool direct, 817 bool drawUnselected) 818 { 819 if (fClipboardMode == kMoveSelectionTo) { 820 view->SetDrawingMode(B_OP_ALPHA); 821 view->SetHighColor(0, 0, 0, 64); 822 // set the level of transparency 823 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 824 } else if (direct) 825 view->SetDrawingMode(B_OP_OVER); 826 827 IconCache::sIconCache->Draw(ResolvedModel(), view, where, 828 fIsSelected && !drawUnselected ? kSelectedIcon : kNormalIcon, which, 829 true); 830 831 if (fPercent != -1) 832 DrawBar(where, view, which); 833 } 834 835 836 void 837 BPose::DrawBar(BPoint where, BView* view, icon_size which) 838 { 839 view->PushState(); 840 841 int32 size; 842 int32 barWidth; 843 int32 barHeight; 844 int32 yOffset; 845 if (which >= B_LARGE_ICON) { 846 size = which - 1; 847 barWidth = (int32)(7.0f / 32.0f * (float)which); 848 yOffset = 2; 849 barHeight = size - 4 - 2 * yOffset; 850 } else { 851 size = B_MINI_ICON; 852 barWidth = 4; 853 yOffset = 0; 854 barHeight = size - 4 - 2 * yOffset; 855 } 856 857 // the black shadowed line 858 view->SetHighColor(32, 32, 32, 92); 859 view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset)); 860 view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset)); 861 view->StrokeLine(BPoint(where.x + size - barWidth + 1, 862 where.y + size - yOffset)); 863 864 view->SetDrawingMode(B_OP_ALPHA); 865 866 // the gray frame 867 view->SetHighColor(76, 76, 76, 192); 868 BRect rect(where.x + size - barWidth,where.y + yOffset, 869 where.x + size - 1,where.y + size - 1 - yOffset); 870 view->StrokeRect(rect); 871 872 // calculate bar height 873 int32 percent = fPercent > -1 ? fPercent : -2 - fPercent; 874 int32 barPos = int32(barHeight * percent / 100.0); 875 if (barPos < 0) 876 barPos = 0; 877 else if (barPos > barHeight) 878 barPos = barHeight; 879 880 // the free space bar 881 view->SetHighColor(TrackerSettings().FreeSpaceColor()); 882 883 rect.InsetBy(1,1); 884 BRect bar(rect); 885 bar.bottom = bar.top + barPos - 1; 886 if (barPos > 0) 887 view->FillRect(bar); 888 889 // the used space bar 890 bar.top = bar.bottom + 1; 891 bar.bottom = rect.bottom; 892 view->SetHighColor(fPercent < -1 893 ? TrackerSettings().WarningSpaceColor() 894 : TrackerSettings().UsedSpaceColor()); 895 view->FillRect(bar); 896 897 view->PopState(); 898 } 899 900 901 void 902 BPose::DrawToggleSwitch(BRect, BPoseView*) 903 { 904 } 905 906 907 BPoint 908 BPose::Location(const BPoseView* poseView) const 909 { 910 float scale = 1.0; 911 if (poseView->ViewMode() == kIconMode) 912 scale = poseView->IconSize() / 32.0; 913 914 return BPoint(fLocation.x * scale, fLocation.y * scale); 915 } 916 917 918 void 919 BPose::SetLocation(BPoint point, const BPoseView* poseView) 920 { 921 float scale = 1.0; 922 if (poseView->ViewMode() == kIconMode) 923 scale = poseView->IconSize() / 32.0; 924 925 fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale)); 926 if (isinff(fLocation.x) || isinff(fLocation.y)) 927 debugger("BPose::SetLocation() - infinite location"); 928 929 fHasLocation = true; 930 } 931 932 933 BRect 934 BPose::CalcRect(BPoint loc, const BPoseView* poseView, bool minimalRect) const 935 { 936 ASSERT(poseView->ViewMode() == kListMode); 937 938 BColumn* column = poseView->LastColumn(); 939 BRect rect; 940 rect.left = loc.x; 941 rect.top = loc.y; 942 rect.right = loc.x + column->Offset() + column->Width(); 943 rect.bottom = rect.top + poseView->ListElemHeight(); 944 945 if (minimalRect) { 946 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 947 if (widget != NULL) { 948 rect.right = widget->CalcRect(loc, poseView->FirstColumn(), 949 poseView).right; 950 } 951 } 952 953 return rect; 954 } 955 956 957 BRect 958 BPose::CalcRect(const BPoseView* poseView) const 959 { 960 ASSERT(poseView->ViewMode() != kListMode); 961 962 BRect rect; 963 BPoint location = Location(poseView); 964 if (poseView->ViewMode() == kIconMode) { 965 rect.left = location.x; 966 rect.right = rect.left + poseView->IconSizeInt(); 967 968 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 969 if (widget) { 970 float textWidth = ceilf(widget->TextWidth(poseView) + 1); 971 if (textWidth > poseView->IconSizeInt()) { 972 rect.left += (poseView->IconSizeInt() - textWidth) / 2; 973 rect.right = rect.left + textWidth; 974 } 975 } 976 977 rect.top = location.y; 978 rect.bottom = rect.top + poseView->IconPoseHeight(); 979 } else { 980 // MINI_ICON_MODE rect calc 981 rect.left = location.x; 982 rect.top = location.y; 983 rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator; 984 rect.bottom = rect.top + poseView->IconPoseHeight(); 985 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 986 if (widget) 987 rect.right += ceil(widget->TextWidth(poseView) + 1); 988 } 989 990 return rect; 991 } 992 993 994 #if DEBUG 995 void 996 BPose::PrintToStream() 997 { 998 TargetModel()->PrintToStream(); 999 switch (fClipboardMode) { 1000 case kMoveSelectionTo: 1001 PRINT(("clipboardMode: Cut\n")); 1002 break; 1003 1004 case kCopySelectionTo: 1005 PRINT(("clipboardMode: Copy\n")); 1006 break; 1007 1008 default: 1009 PRINT(("clipboardMode: 0 - not in clipboard\n")); 1010 break; 1011 } 1012 PRINT(("%sselected\n", IsSelected() ? "" : "not ")); 1013 PRINT(("location %s x:%f y:%f\n", HasLocation() ? "" : "unknown ", 1014 HasLocation() ? fLocation.x : 0, 1015 HasLocation() ? fLocation.y : 0)); 1016 PRINT(("%s autoplaced \n", WasAutoPlaced() ? "was" : "not")); 1017 } 1018 #endif 1019