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 break; 432 433 BTextWidget* widget = WidgetFor(column->AttrHash()); 434 if (widget != NULL && widget->IsActive()) { 435 poseView->CommitActivePose(); 436 found = true; 437 continue; 438 } 439 440 if (found && column->Editable()) { 441 BRect bounds; 442 if (poseView->ViewMode() == kListMode) { 443 int32 poseIndex = poseView->IndexOfPose(this); 444 BPoint poseLoc(0, poseIndex* poseView->ListElemHeight()); 445 bounds = widget->CalcRect(poseLoc, column, poseView); 446 } else 447 bounds = widget->CalcRect(Location(poseView), 0, poseView); 448 449 widget->StartEdit(bounds, poseView, this); 450 break; 451 } 452 } 453 } 454 455 456 void 457 BPose::EditNextWidget(BPoseView* poseView) 458 { 459 EditPreviousNextWidgetCommon(poseView, true); 460 } 461 462 463 void 464 BPose::EditPreviousWidget(BPoseView* poseView) 465 { 466 EditPreviousNextWidgetCommon(poseView, false); 467 } 468 469 470 bool 471 BPose::PointInPose(const BPoseView* poseView, BPoint where) const 472 { 473 ASSERT(poseView->ViewMode() != kListMode); 474 475 BPoint location = Location(poseView); 476 477 if (poseView->ViewMode() == kIconMode) { 478 // check icon rect, then actual icon pixel 479 BRect rect(location, location); 480 rect.right += poseView->IconSizeInt() - 1; 481 rect.bottom += poseView->IconSizeInt() - 1; 482 483 if (rect.Contains(where)) { 484 return IconCache::sIconCache->IconHitTest(where - location, 485 ResolvedModel(), kNormalIcon, poseView->IconSize()); 486 } 487 488 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 489 if (widget) { 490 float textWidth = ceilf(widget->TextWidth(poseView) + 1); 491 rect.left += (poseView->IconSizeInt() - textWidth) / 2; 492 rect.right = rect.left + textWidth; 493 } 494 495 rect.top = location.y + poseView->IconSizeInt(); 496 rect.bottom = rect.top + poseView->FontHeight(); 497 498 return rect.Contains(where); 499 } 500 501 // MINI_ICON_MODE rect calc 502 BRect rect(location, location); 503 rect.right += B_MINI_ICON + kMiniIconSeparator; 504 rect.bottom += poseView->IconPoseHeight(); 505 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 506 if (widget != NULL) 507 rect.right += ceil(widget->TextWidth(poseView) + 1); 508 509 return rect.Contains(where); 510 } 511 512 513 bool 514 BPose::PointInPose(BPoint loc, const BPoseView* poseView, BPoint where, 515 BTextWidget** hitWidget) const 516 { 517 if (hitWidget) 518 *hitWidget = NULL; 519 520 // check intersection with icon 521 BRect rect; 522 rect.left = loc.x + kListOffset; 523 rect.right = rect.left + B_MINI_ICON; 524 rect.bottom = loc.y + poseView->ListElemHeight(); 525 rect.top = rect.bottom - B_MINI_ICON; 526 if (rect.Contains(where)) 527 return true; 528 529 for (int32 index = 0; ; index++) { 530 BColumn* column = poseView->ColumnAt(index); 531 if (column == NULL) 532 break; 533 BTextWidget* widget = WidgetFor(column->AttrHash()); 534 if (widget 535 && widget->CalcClickRect(loc, column, poseView).Contains(where)) { 536 if (hitWidget) 537 *hitWidget = widget; 538 return true; 539 } 540 } 541 542 return false; 543 } 544 545 546 void 547 BPose::Draw(BRect rect, const BRect& updateRect, BPoseView* poseView, 548 BView* drawView, bool fullDraw, BPoint offset, bool selected) 549 { 550 // If the background wasn't cleared and Draw() is not called after 551 // having edited a name or similar (with fullDraw) 552 if (!fBackgroundClean && !fullDraw) { 553 fBackgroundClean = true; 554 poseView->Invalidate(rect); 555 return; 556 } else 557 fBackgroundClean = false; 558 559 bool directDraw = (drawView == poseView); 560 bool windowActive = poseView->Window()->IsActive(); 561 bool showSelectionWhenInactive = poseView->fShowSelectionWhenInactive; 562 bool isDrawingSelectionRect = poseView->fIsDrawingSelectionRect; 563 564 ModelNodeLazyOpener modelOpener(fModel); 565 566 if (poseView->ViewMode() == kListMode) { 567 uint32 size = poseView->IconSizeInt(); 568 BRect iconRect(rect); 569 iconRect.left += kListOffset; 570 iconRect.right = iconRect.left + size; 571 iconRect.top = iconRect.bottom - size; 572 if (updateRect.Intersects(iconRect)) { 573 iconRect.OffsetBy(offset); 574 DrawIcon(iconRect.LeftTop(), drawView, poseView->IconSize(), 575 directDraw, !windowActive && !showSelectionWhenInactive); 576 } 577 578 // draw text 579 int32 columnsToDraw = 1; 580 if (fullDraw) 581 columnsToDraw = poseView->CountColumns(); 582 583 for (int32 index = 0; index < columnsToDraw; index++) { 584 BColumn* column = poseView->ColumnAt(index); 585 if (column == NULL) 586 break; 587 588 // if widget doesn't exist, create it 589 BTextWidget* widget = WidgetFor(column, poseView, modelOpener); 590 591 if (widget && widget->IsVisible()) { 592 BRect widgetRect(widget->ColumnRect(rect.LeftTop(), column, 593 poseView)); 594 595 if (updateRect.Intersects(widgetRect)) { 596 BRect widgetTextRect(widget->CalcRect(rect.LeftTop(), 597 column, poseView)); 598 599 bool selectDuringDraw = directDraw && selected 600 && windowActive; 601 602 if (index == 0 && selectDuringDraw) { 603 //draw with dark background to select text 604 drawView->PushState(); 605 drawView->SetLowColor(0, 0, 0); 606 } 607 608 if (index == 0) { 609 widget->Draw(widgetRect, widgetTextRect, 610 column->Width(), poseView, drawView, selected, 611 fClipboardMode, offset, directDraw); 612 } else { 613 widget->Draw(widgetTextRect, widgetTextRect, 614 column->Width(), poseView, drawView, false, 615 fClipboardMode, offset, directDraw); 616 } 617 618 if (index == 0 && selectDuringDraw) 619 drawView->PopState(); 620 else if (index == 0 && selected) { 621 if (windowActive || isDrawingSelectionRect) { 622 widgetTextRect.OffsetBy(offset); 623 drawView->InvertRect(widgetTextRect); 624 } else if (!windowActive 625 && showSelectionWhenInactive) { 626 widgetTextRect.OffsetBy(offset); 627 drawView->PushState(); 628 drawView->SetDrawingMode(B_OP_BLEND); 629 drawView->SetHighColor(128, 128, 128, 255); 630 drawView->FillRect(widgetTextRect); 631 drawView->PopState(); 632 } 633 } 634 } 635 } 636 } 637 } else { 638 // draw in icon mode 639 BPoint location(Location(poseView)); 640 BPoint iconOrigin(location); 641 iconOrigin += offset; 642 643 DrawIcon(iconOrigin, drawView, poseView->IconSize(), directDraw, 644 !windowActive && !showSelectionWhenInactive); 645 646 BColumn* column = poseView->FirstColumn(); 647 if (column == NULL) 648 return; 649 650 BTextWidget* widget = WidgetFor(column, poseView, modelOpener); 651 if (widget == NULL || !widget->IsVisible()) 652 return; 653 654 rect = widget->CalcRect(location, 0, poseView); 655 656 bool selectDuringDraw = directDraw && selected 657 && (poseView->IsDesktopWindow() || windowActive); 658 659 if (selectDuringDraw) { 660 // draw with dark background to select text 661 drawView->PushState(); 662 drawView->SetLowColor(0, 0, 0); 663 } 664 665 widget->Draw(rect, rect, rect.Width(), poseView, drawView, 666 selected, fClipboardMode, offset, directDraw); 667 668 if (selectDuringDraw) 669 drawView->PopState(); 670 else if (selected && directDraw) { 671 if (windowActive || isDrawingSelectionRect) { 672 rect.OffsetBy(offset); 673 drawView->InvertRect(rect); 674 } else if (!windowActive && showSelectionWhenInactive) { 675 drawView->PushState(); 676 drawView->SetDrawingMode(B_OP_BLEND); 677 drawView->SetHighColor(128, 128, 128, 255); 678 drawView->FillRect(rect); 679 drawView->PopState(); 680 } 681 } 682 } 683 } 684 685 686 void 687 BPose::DeselectWithoutErasingBackground(BRect, BPoseView* poseView) 688 { 689 ASSERT(poseView->ViewMode() != kListMode); 690 ASSERT(!IsSelected()); 691 692 BPoint location(Location(poseView)); 693 694 // draw icon directly 695 if (fPercent == -1) 696 DrawIcon(location, poseView, poseView->IconSize(), true); 697 else 698 UpdateIcon(location, poseView); 699 700 BColumn* column = poseView->FirstColumn(); 701 if (column == NULL) 702 return; 703 704 BTextWidget* widget = WidgetFor(column->AttrHash()); 705 if (widget == NULL || !widget->IsVisible()) 706 return; 707 708 // just invalidate the background, don't draw anything 709 poseView->Invalidate(widget->CalcRect(location, 0, poseView)); 710 } 711 712 713 void 714 BPose::MoveTo(BPoint point, BPoseView* poseView, bool invalidate) 715 { 716 point.x = floorf(point.x); 717 point.y = floorf(point.y); 718 719 BRect oldBounds; 720 721 BPoint oldLocation = Location(poseView); 722 723 ASSERT(poseView->ViewMode() != kListMode); 724 if (point == oldLocation || poseView->ViewMode() == kListMode) 725 return; 726 727 if (invalidate) 728 oldBounds = CalcRect(poseView); 729 730 // might need to move a text view if we're active 731 if (poseView->ActivePose() == this) { 732 BView* border_view = poseView->FindView("BorderView"); 733 if (border_view) { 734 border_view->MoveBy(point.x - oldLocation.x, 735 point.y - oldLocation.y); 736 } 737 } 738 739 float scale = 1.0; 740 if (poseView->ViewMode() == kIconMode) { 741 scale = poseView->IconSize() / 32.0; 742 } 743 fLocation.x = point.x / scale; 744 fLocation.y = point.y / scale; 745 746 fHasLocation = true; 747 fNeedsSaveLocation = true; 748 749 if (invalidate) { 750 poseView->Invalidate(oldBounds); 751 poseView->Invalidate(CalcRect(poseView)); 752 } 753 } 754 755 756 BTextWidget* 757 BPose::ActiveWidget() const 758 { 759 for (int32 i = fWidgetList.CountItems(); i-- > 0;) { 760 BTextWidget* widget = fWidgetList.ItemAt(i); 761 if (widget->IsActive()) 762 return widget; 763 } 764 765 return NULL; 766 } 767 768 769 BTextWidget* 770 BPose::WidgetFor(uint32 attr, int32* index) const 771 { 772 int32 count = fWidgetList.CountItems(); 773 for (int32 i = 0; i < count; i++) { 774 BTextWidget* widget = fWidgetList.ItemAt(i); 775 if (widget->AttrHash() == attr) { 776 if (index != NULL) 777 *index = i; 778 779 return widget; 780 } 781 } 782 783 return 0; 784 } 785 786 787 BTextWidget* 788 BPose::WidgetFor(BColumn* column, BPoseView* poseView, 789 ModelNodeLazyOpener &opener, int32* index) 790 { 791 BTextWidget* widget = WidgetFor(column->AttrHash(), index); 792 if (widget == NULL) 793 widget = AddWidget(poseView, column, opener); 794 795 return widget; 796 } 797 798 799 // the following method is deprecated 800 bool 801 BPose::TestLargeIconPixel(BPoint point) const 802 { 803 return IconCache::sIconCache->IconHitTest(point, ResolvedModel(), 804 kNormalIcon, B_LARGE_ICON); 805 } 806 807 808 void 809 BPose::DrawIcon(BPoint where, BView* view, icon_size kind, bool direct, 810 bool drawUnselected) 811 { 812 if (fClipboardMode == kMoveSelectionTo) { 813 view->SetDrawingMode(B_OP_ALPHA); 814 view->SetHighColor(0, 0, 0, 64); 815 // set the level of transparency 816 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 817 } else if (direct) 818 view->SetDrawingMode(B_OP_OVER); 819 820 IconCache::sIconCache->Draw(ResolvedModel(), view, where, 821 fIsSelected && !drawUnselected ? kSelectedIcon : kNormalIcon, kind, 822 true); 823 824 if (fPercent != -1) 825 DrawBar(where, view, kind); 826 } 827 828 829 void 830 BPose::DrawBar(BPoint where, BView* view, icon_size kind) 831 { 832 view->PushState(); 833 834 int32 size; 835 int32 barWidth; 836 int32 barHeight; 837 int32 yOffset; 838 if (kind >= B_LARGE_ICON) { 839 size = kind - 1; 840 barWidth = (int32)(7.0f / 32.0f * (float)kind); 841 yOffset = 2; 842 barHeight = size - 4 - 2 * yOffset; 843 } else { 844 size = B_MINI_ICON; 845 barWidth = 4; 846 yOffset = 0; 847 barHeight = size - 4 - 2 * yOffset; 848 } 849 850 // the black shadowed line 851 view->SetHighColor(32, 32, 32, 92); 852 view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset)); 853 view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset)); 854 view->StrokeLine(BPoint(where.x + size - barWidth + 1, 855 where.y + size - yOffset)); 856 857 view->SetDrawingMode(B_OP_ALPHA); 858 859 // the gray frame 860 view->SetHighColor(76, 76, 76, 192); 861 BRect rect(where.x + size - barWidth,where.y + yOffset, 862 where.x + size - 1,where.y + size - 1 - yOffset); 863 view->StrokeRect(rect); 864 865 // calculate bar height 866 int32 percent = fPercent > -1 ? fPercent : -2 - fPercent; 867 int32 barPos = int32(barHeight * percent / 100.0); 868 if (barPos < 0) 869 barPos = 0; 870 else if (barPos > barHeight) 871 barPos = barHeight; 872 873 // the free space bar 874 view->SetHighColor(TrackerSettings().FreeSpaceColor()); 875 876 rect.InsetBy(1,1); 877 BRect bar(rect); 878 bar.bottom = bar.top + barPos - 1; 879 if (barPos > 0) 880 view->FillRect(bar); 881 882 // the used space bar 883 bar.top = bar.bottom + 1; 884 bar.bottom = rect.bottom; 885 view->SetHighColor(fPercent < -1 886 ? TrackerSettings().WarningSpaceColor() 887 : TrackerSettings().UsedSpaceColor()); 888 view->FillRect(bar); 889 890 view->PopState(); 891 } 892 893 894 void 895 BPose::DrawToggleSwitch(BRect, BPoseView*) 896 { 897 } 898 899 900 BPoint 901 BPose::Location(const BPoseView* poseView) const 902 { 903 float scale = 1.0; 904 if (poseView->ViewMode() == kIconMode) 905 scale = poseView->IconSize() / 32.0; 906 907 return BPoint(fLocation.x * scale, fLocation.y * scale); 908 } 909 910 911 void 912 BPose::SetLocation(BPoint point, const BPoseView* poseView) 913 { 914 float scale = 1.0; 915 if (poseView->ViewMode() == kIconMode) 916 scale = poseView->IconSize() / 32.0; 917 918 fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale)); 919 if (isinff(fLocation.x) || isinff(fLocation.y)) 920 debugger("BPose::SetLocation() - infinite location"); 921 922 fHasLocation = true; 923 } 924 925 926 BRect 927 BPose::CalcRect(BPoint loc, const BPoseView* poseView, bool minimalRect) const 928 { 929 ASSERT(poseView->ViewMode() == kListMode); 930 931 BColumn* column = poseView->LastColumn(); 932 BRect rect; 933 rect.left = loc.x; 934 rect.top = loc.y; 935 rect.right = loc.x + column->Offset() + column->Width(); 936 rect.bottom = rect.top + poseView->ListElemHeight(); 937 938 if (minimalRect) { 939 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 940 if (widget != NULL) { 941 rect.right = widget->CalcRect(loc, poseView->FirstColumn(), 942 poseView).right; 943 } 944 } 945 946 return rect; 947 } 948 949 950 BRect 951 BPose::CalcRect(const BPoseView* poseView) const 952 { 953 ASSERT(poseView->ViewMode() != kListMode); 954 955 BRect rect; 956 BPoint location = Location(poseView); 957 if (poseView->ViewMode() == kIconMode) { 958 rect.left = location.x; 959 rect.right = rect.left + poseView->IconSizeInt(); 960 961 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 962 if (widget) { 963 float textWidth = ceilf(widget->TextWidth(poseView) + 1); 964 if (textWidth > poseView->IconSizeInt()) { 965 rect.left += (poseView->IconSizeInt() - textWidth) / 2; 966 rect.right = rect.left + textWidth; 967 } 968 } 969 970 rect.top = location.y; 971 rect.bottom = rect.top + poseView->IconPoseHeight(); 972 } else { 973 // MINI_ICON_MODE rect calc 974 rect.left = location.x; 975 rect.top = location.y; 976 rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator; 977 rect.bottom = rect.top + poseView->IconPoseHeight(); 978 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 979 if (widget) 980 rect.right += ceil(widget->TextWidth(poseView) + 1); 981 } 982 983 return rect; 984 } 985 986 987 #if DEBUG 988 void 989 BPose::PrintToStream() 990 { 991 TargetModel()->PrintToStream(); 992 switch (fClipboardMode) { 993 case kMoveSelectionTo: 994 PRINT(("clipboardMode: Cut\n")); 995 break; 996 997 case kCopySelectionTo: 998 PRINT(("clipboardMode: Copy\n")); 999 break; 1000 1001 default: 1002 PRINT(("clipboardMode: 0 - not in clipboard\n")); 1003 break; 1004 } 1005 PRINT(("%sselected\n", IsSelected() ? "" : "not ")); 1006 PRINT(("location %s x:%f y:%f\n", HasLocation() ? "" : "unknown ", 1007 HasLocation() ? fLocation.x : 0, 1008 HasLocation() ? fLocation.y : 0)); 1009 PRINT(("%s autoplaced \n", WasAutoPlaced() ? "was" : "not")); 1010 } 1011 #endif 1012