1/* 2Open Tracker License 3 4Terms and Conditions 5 6Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8Permission is hereby granted, free of charge, to any person obtaining a copy of 9this software and associated documentation files (the "Software"), to deal in 10the Software without restriction, including without limitation the rights to 11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12of the Software, and to permit persons to whom the Software is furnished to do 13so, subject to the following conditions: 14 15The above copyright notice and this permission notice applies to all licensees 16and shall be included in all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25Except as contained in this notice, the name of Be Incorporated shall not be 26used in advertising or otherwise to promote the sale, use or other dealings in 27this Software without prior written authorization from Be Incorporated. 28 29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30of Be Incorporated in the United States and other countries. Other brand product 31names are registered trademarks or trademarks of their respective holders. 32All 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 52int32 53CalcFreeSpace(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 73BPose::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 115BPose::~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 131void 132BPose::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 144BTextWidget* 145BPose::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 158BTextWidget* 159BPose::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 173void 174BPose::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 183void 184BPose::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 198inline bool 199OneMouseUp(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 217void 218BPose::MouseUp(BPoint poseLoc, BPoseView* poseView, BPoint where, int32) 219{ 220 WhileEachTextWidget(this, poseView, OneMouseUp, poseLoc, where); 221} 222 223 224inline void 225OneCheckAndUpdate(BTextWidget* widget, BPose*, BPoseView* poseView, 226 BColumn* column, BPoint poseLoc) 227{ 228 widget->CheckAndUpdate(poseLoc, column, poseView, true); 229} 230 231 232void 233BPose::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 243void 244BPose::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 307bool 308BPose::_PeriodicUpdateCallback(BPose* pose, void* cookie) 309{ 310 return pose->UpdateVolumeSpaceBar((BVolume*)cookie); 311} 312 313 314bool 315BPose::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 340void 341BPose::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 rect.top = rect.bottom - iconSize; 353 } else { 354 BPoint location = Location(poseView); 355 rect.left = location.x; 356 rect.top = location.y; 357 rect.right = rect.left + iconSize; 358 rect.bottom = rect.top + iconSize; 359 } 360 361 poseView->Invalidate(rect); 362} 363 364 365void 366BPose::UpdateBrokenSymLink(BPoint poseLoc, BPoseView* poseView) 367{ 368 ASSERT(TargetModel()->IsSymLink()); 369 ASSERT(TargetModel()->LinkTo() == NULL); 370 if (!TargetModel()->IsSymLink() || TargetModel()->LinkTo() != NULL) 371 return; 372 373 UpdateIcon(poseLoc, poseView); 374} 375 376 377void 378BPose::UpdateWasBrokenSymlink(BPoint poseLoc, BPoseView* poseView) 379{ 380 if (!fModel->IsSymLink()) 381 return; 382 383 if (fModel->LinkTo() != NULL) { 384 BEntry entry(fModel->EntryRef(), true); 385 if (entry.InitCheck() != B_OK) { 386 watch_node(fModel->LinkTo()->NodeRef(), B_STOP_WATCHING, poseView); 387 fModel->SetLinkTo(NULL); 388 UpdateIcon(poseLoc, poseView); 389 } 390 return; 391 } 392 393 poseView->CreateSymlinkPoseTarget(fModel); 394 UpdateIcon(poseLoc, poseView); 395 if (fModel->LinkTo() != NULL) 396 fModel->LinkTo()->CloseNode(); 397} 398 399 400void 401BPose::EditFirstWidget(BPoint poseLoc, BPoseView* poseView) 402{ 403 // find first editable widget 404 BColumn* column; 405 for (int32 i = 0; (column = poseView->ColumnAt(i)) != NULL; i++) { 406 BTextWidget* widget = WidgetFor(column->AttrHash()); 407 408 if (widget != NULL && widget->IsEditable()) { 409 BRect bounds; 410 // ToDo: 411 // fold the three StartEdit code sequences into a cover call 412 if (poseView->ViewMode() == kListMode) 413 bounds = widget->CalcRect(poseLoc, column, poseView); 414 else 415 bounds = widget->CalcRect(Location(poseView), NULL, poseView); 416 417 widget->StartEdit(bounds, poseView, this); 418 break; 419 } 420 } 421} 422 423 424void 425BPose::EditPreviousNextWidgetCommon(BPoseView* poseView, bool next) 426{ 427 bool found = false; 428 int32 delta = next ? 1 : -1; 429 for (int32 index = next ? 0 : poseView->CountColumns() - 1; ; 430 index += delta) { 431 BColumn* column = poseView->ColumnAt(index); 432 if (column == NULL) { 433 // out of columns 434 break; 435 } 436 437 BTextWidget* widget = WidgetFor(column->AttrHash()); 438 if (widget == NULL) { 439 // no widget for this column, next 440 continue; 441 } 442 443 if (widget->IsActive()) { 444 poseView->CommitActivePose(); 445 found = true; 446 continue; 447 } 448 449 if (found && column->Editable()) { 450 BRect bounds; 451 if (poseView->ViewMode() == kListMode) { 452 int32 poseIndex = poseView->IndexOfPose(this); 453 BPoint poseLoc(0, poseIndex* poseView->ListElemHeight()); 454 bounds = widget->CalcRect(poseLoc, column, poseView); 455 } else 456 bounds = widget->CalcRect(Location(poseView), NULL, poseView); 457 458 widget->StartEdit(bounds, poseView, this); 459 break; 460 } 461 } 462} 463 464 465void 466BPose::EditNextWidget(BPoseView* poseView) 467{ 468 EditPreviousNextWidgetCommon(poseView, true); 469} 470 471 472void 473BPose::EditPreviousWidget(BPoseView* poseView) 474{ 475 EditPreviousNextWidgetCommon(poseView, false); 476} 477 478 479bool 480BPose::PointInPose(const BPoseView* poseView, BPoint where) const 481{ 482 ASSERT(poseView->ViewMode() != kListMode); 483 484 BPoint location = Location(poseView); 485 486 if (poseView->ViewMode() == kIconMode) { 487 // check icon rect, then actual icon pixel 488 BRect rect(location, location); 489 rect.right += poseView->IconSizeInt() - 1; 490 rect.bottom += poseView->IconSizeInt() - 1; 491 492 if (rect.Contains(where)) { 493 return IconCache::sIconCache->IconHitTest(where - location, 494 ResolvedModel(), kNormalIcon, poseView->IconSize()); 495 } 496 497 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 498 if (widget) { 499 float textWidth = ceilf(widget->TextWidth(poseView) + 1); 500 rect.left += (poseView->IconSizeInt() - textWidth) / 2; 501 rect.right = rect.left + textWidth; 502 } 503 504 rect.top = location.y + poseView->IconSizeInt(); 505 rect.bottom = rect.top + poseView->FontHeight(); 506 507 return rect.Contains(where); 508 } 509 510 // MINI_ICON_MODE rect calc 511 BRect rect(location, location); 512 rect.right += B_MINI_ICON + kMiniIconSeparator; 513 rect.bottom += poseView->IconPoseHeight(); 514 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 515 if (widget != NULL) 516 rect.right += ceil(widget->TextWidth(poseView) + 1); 517 518 return rect.Contains(where); 519} 520 521 522bool 523BPose::PointInPose(BPoint where, const BPoseView* poseView, BPoint point, 524 BTextWidget** hitWidget) const 525{ 526 if (hitWidget != NULL) 527 *hitWidget = NULL; 528 529 // check intersection with icon 530 BRect rect = _IconRect(poseView, where); 531 if (rect.Contains(point)) 532 return true; 533 534 for (int32 index = 0; ; index++) { 535 BColumn* column = poseView->ColumnAt(index); 536 if (column == NULL) 537 break; 538 539 BTextWidget* widget = WidgetFor(column->AttrHash()); 540 if (widget != NULL 541 && widget->CalcClickRect(where, column, poseView).Contains(point)) { 542 if (hitWidget != NULL) 543 *hitWidget = widget; 544 545 return true; 546 } 547 } 548 549 return false; 550} 551 552 553void 554BPose::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 direct = (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 // draw in list mode 575 BRect iconRect = _IconRect(poseView, rect.LeftTop()); 576 if (updateRect.Intersects(iconRect)) { 577 iconRect.OffsetBy(offset); 578 DrawIcon(iconRect.LeftTop(), drawView, poseView->IconSize(), 579 direct, !windowActive && !showSelectionWhenInactive); 580 } 581 582 // draw text 583 int32 columnsToDraw = 1; 584 if (fullDraw) 585 columnsToDraw = poseView->CountColumns(); 586 587 for (int32 index = 0; index < columnsToDraw; index++) { 588 BColumn* column = poseView->ColumnAt(index); 589 if (column == NULL) 590 break; 591 592 // if widget doesn't exist, create it 593 BTextWidget* widget = WidgetFor(column, poseView, modelOpener); 594 if (widget == NULL || !widget->IsVisible()) 595 continue; 596 597 BRect widgetRect(widget->ColumnRect(rect.LeftTop(), column, 598 poseView)); 599 if (!updateRect.Intersects(widgetRect)) 600 continue; 601 602 BRect widgetTextRect(widget->CalcRect(rect.LeftTop(), 603 column, poseView)); 604 605 bool selectDuringDraw = direct && selected 606 && windowActive; 607 608 if (index == 0 && selectDuringDraw) { 609 // draw with "reverse video" to select text 610 drawView->PushState(); 611 drawView->SetLowColor(poseView->BackColor(true)); 612 } else if (!direct && index == 0 && selected 613 && (windowActive || isDrawingSelectionRect)) { 614 drawView->SetLowColor(poseView->TextColor(true)); // inverse 615 drawView->FillRect(widgetTextRect.OffsetByCopy(offset), B_SOLID_LOW); 616 } 617 618 if (index == 0) { 619 widget->Draw(widgetRect, widgetTextRect, 620 column->Width(), poseView, drawView, selected, 621 fClipboardMode, offset, direct); 622 } else { 623 widget->Draw(widgetTextRect, widgetTextRect, 624 column->Width(), poseView, drawView, false, 625 fClipboardMode, offset, direct); 626 } 627 628 if (index == 0 && selected) { 629 if (selectDuringDraw) 630 drawView->PopState(); 631 else if (windowActive || isDrawingSelectionRect) { 632 drawView->SetLowColor(poseView->LowColor()); 633 drawView->InvertRect(widgetTextRect.OffsetByCopy(offset)); 634 } else if (!windowActive && showSelectionWhenInactive) { 635 widgetTextRect.OffsetBy(offset); 636 drawView->PushState(); 637 drawView->SetDrawingMode(B_OP_BLEND); 638 drawView->SetHighColor(128, 128, 128, 255); 639 drawView->FillRect(widgetTextRect); 640 drawView->PopState(); 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(), direct, 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, NULL, poseView); 662 663 bool selectDuringDraw = direct && selected && windowActive; 664 665 if (selectDuringDraw) { 666 // draw with "reverse video" to select text 667 drawView->PushState(); 668 drawView->SetLowColor(poseView->BackColor(true)); 669 } 670 671 widget->Draw(rect, rect, rect.Width(), poseView, drawView, 672 selected, fClipboardMode, offset, direct); 673 674 if (selectDuringDraw) 675 drawView->PopState(); 676 else if (selected && direct) { 677 if (windowActive || isDrawingSelectionRect) { 678 rect.OffsetBy(offset); 679 drawView->InvertRect(rect); 680 } else if (!windowActive && showSelectionWhenInactive) { 681 drawView->PushState(); 682 drawView->SetDrawingMode(B_OP_BLEND); 683 drawView->SetHighColor(128, 128, 128, 255); 684 drawView->FillRect(rect); 685 drawView->PopState(); 686 } 687 } 688 } 689} 690 691 692void 693BPose::DeselectWithoutErasingBackground(BRect, BPoseView* poseView) 694{ 695 ASSERT(poseView->ViewMode() != kListMode); 696 ASSERT(!IsSelected()); 697 698 BPoint location(Location(poseView)); 699 700 // draw icon directly 701 if (fPercent == -1) 702 DrawIcon(location, poseView, poseView->IconSize(), true); 703 else 704 UpdateIcon(location, poseView); 705 706 BColumn* column = poseView->FirstColumn(); 707 if (column == NULL) 708 return; 709 710 BTextWidget* widget = WidgetFor(column->AttrHash()); 711 if (widget == NULL || !widget->IsVisible()) 712 return; 713 714 // just invalidate the background, don't draw anything 715 poseView->Invalidate(widget->CalcRect(location, NULL, poseView)); 716} 717 718 719void 720BPose::MoveTo(BPoint point, BPoseView* poseView, bool invalidate) 721{ 722 point.x = floorf(point.x); 723 point.y = floorf(point.y); 724 725 BRect oldBounds; 726 727 BPoint oldLocation = Location(poseView); 728 729 ASSERT(poseView->ViewMode() != kListMode); 730 if (point == oldLocation || poseView->ViewMode() == kListMode) 731 return; 732 733 if (invalidate) 734 oldBounds = CalcRect(poseView); 735 736 // might need to move a text view if we're active 737 if (poseView->ActivePose() == this) { 738 BView* border_view = poseView->FindView("BorderView"); 739 if (border_view) { 740 border_view->MoveBy(point.x - oldLocation.x, 741 point.y - oldLocation.y); 742 } 743 } 744 745 float scale = 1.0; 746 if (poseView->ViewMode() == kIconMode) { 747 scale = (float)poseView->IconSizeInt() / 32.0; 748 } 749 fLocation.x = point.x / scale; 750 fLocation.y = point.y / scale; 751 752 fHasLocation = true; 753 fNeedsSaveLocation = true; 754 755 if (invalidate) { 756 poseView->Invalidate(oldBounds); 757 poseView->Invalidate(CalcRect(poseView)); 758 } 759} 760 761 762BTextWidget* 763BPose::ActiveWidget() const 764{ 765 for (int32 i = fWidgetList.CountItems(); i-- > 0;) { 766 BTextWidget* widget = fWidgetList.ItemAt(i); 767 if (widget->IsActive()) 768 return widget; 769 } 770 771 return NULL; 772} 773 774 775BTextWidget* 776BPose::WidgetFor(uint32 attr, int32* index) const 777{ 778 int32 count = fWidgetList.CountItems(); 779 for (int32 i = 0; i < count; i++) { 780 BTextWidget* widget = fWidgetList.ItemAt(i); 781 if (widget->AttrHash() == attr) { 782 if (index != NULL) 783 *index = i; 784 785 return widget; 786 } 787 } 788 789 return NULL; 790} 791 792 793BTextWidget* 794BPose::WidgetFor(BColumn* column, BPoseView* poseView, 795 ModelNodeLazyOpener &opener, int32* index) 796{ 797 BTextWidget* widget = WidgetFor(column->AttrHash(), index); 798 if (widget == NULL) 799 widget = AddWidget(poseView, column, opener); 800 801 return widget; 802} 803 804 805void 806BPose::DrawIcon(BPoint where, BView* view, BSize size, bool direct, 807 bool drawUnselected) 808{ 809 if (fClipboardMode == kMoveSelectionTo) { 810 view->SetDrawingMode(B_OP_ALPHA); 811 view->SetHighColor(0, 0, 0, 64); 812 // set the level of transparency 813 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 814 } else if (direct) 815 view->SetDrawingMode(B_OP_OVER); 816 817 IconCache::sIconCache->Draw(ResolvedModel(), view, where, 818 fIsSelected && !drawUnselected ? kSelectedIcon : kNormalIcon, size, 819 true); 820 821 if (fPercent != -1) 822 DrawBar(where, view, size); 823} 824 825 826void 827BPose::DrawBar(BPoint where, BView* view, BSize iconSize) 828{ 829 view->PushState(); 830 831 int32 size = iconSize.IntegerWidth(); 832 int32 yOffset; 833 int32 barWidth = (int32)(7.0f / 32.0f * (float)(size + 1)); 834 if (barWidth < 4) { 835 barWidth = 4; 836 yOffset = 0; 837 } else 838 yOffset = 2; 839 int32 barHeight = size - 4 - 2 * yOffset; 840 841 // the black shadowed line 842 view->SetHighColor(32, 32, 32, 92); 843 view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset)); 844 view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset)); 845 view->StrokeLine(BPoint(where.x + size - barWidth + 1, 846 where.y + size - yOffset)); 847 848 view->SetDrawingMode(B_OP_ALPHA); 849 850 // the gray frame 851 view->SetHighColor(76, 76, 76, 192); 852 BRect rect(where.x + size - barWidth,where.y + yOffset, 853 where.x + size - 1,where.y + size - 1 - yOffset); 854 view->StrokeRect(rect); 855 856 // calculate bar height 857 int32 percent = fPercent > -1 ? fPercent : -2 - fPercent; 858 int32 barPos = int32(barHeight * percent / 100.0); 859 if (barPos < 0) 860 barPos = 0; 861 else if (barPos > barHeight) 862 barPos = barHeight; 863 864 // the free space bar 865 view->SetHighColor(TrackerSettings().FreeSpaceColor()); 866 867 rect.InsetBy(1,1); 868 BRect bar(rect); 869 bar.bottom = bar.top + barPos - 1; 870 if (barPos > 0) 871 view->FillRect(bar); 872 873 // the used space bar 874 bar.top = bar.bottom + 1; 875 bar.bottom = rect.bottom; 876 view->SetHighColor(fPercent < -1 877 ? TrackerSettings().WarningSpaceColor() 878 : TrackerSettings().UsedSpaceColor()); 879 view->FillRect(bar); 880 881 view->PopState(); 882} 883 884 885void 886BPose::DrawToggleSwitch(BRect, BPoseView*) 887{ 888} 889 890 891BPoint 892BPose::Location(const BPoseView* poseView) const 893{ 894 float scale = 1.0; 895 if (poseView->ViewMode() == kIconMode) 896 scale = (float)poseView->IconSizeInt() / 32.0; 897 898 return BPoint(fLocation.x * scale, fLocation.y * scale); 899} 900 901 902void 903BPose::SetLocation(BPoint point, const BPoseView* poseView) 904{ 905 float scale = 1.0; 906 if (poseView->ViewMode() == kIconMode) 907 scale = (float)poseView->IconSizeInt() / 32.0; 908 909 fLocation = BPoint(floorf(point.x / scale), floorf(point.y / scale)); 910 if (isinff(fLocation.x) || isinff(fLocation.y)) 911 debugger("BPose::SetLocation() - infinite location"); 912 913 fHasLocation = true; 914} 915 916 917BRect 918BPose::CalcRect(BPoint loc, const BPoseView* poseView, bool minimalRect) const 919{ 920 ASSERT(poseView->ViewMode() == kListMode); 921 922 BColumn* column = poseView->LastColumn(); 923 BRect rect; 924 rect.left = loc.x; 925 rect.top = loc.y; 926 rect.right = loc.x + column->Offset() + column->Width(); 927 rect.bottom = rect.top + poseView->ListElemHeight(); 928 929 if (minimalRect) { 930 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 931 if (widget != NULL) { 932 rect.right = widget->CalcRect(loc, poseView->FirstColumn(), 933 poseView).right; 934 } 935 } 936 937 return rect; 938} 939 940 941BRect 942BPose::CalcRect(const BPoseView* poseView) const 943{ 944 ASSERT(poseView->ViewMode() != kListMode); 945 946 BRect rect; 947 BPoint location = Location(poseView); 948 if (poseView->ViewMode() == kIconMode) { 949 rect.left = location.x; 950 rect.right = rect.left + poseView->IconSizeInt(); 951 952 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 953 if (widget != NULL) { 954 float textWidth = ceilf(widget->TextWidth(poseView) + 1); 955 if (textWidth > poseView->IconSizeInt()) { 956 rect.left += (poseView->IconSizeInt() - textWidth) / 2; 957 rect.right = rect.left + textWidth; 958 } 959 } 960 961 rect.top = location.y; 962 rect.bottom = rect.top + poseView->IconPoseHeight(); 963 } else { 964 // MINI_ICON_MODE rect calc 965 rect.left = location.x; 966 rect.right = rect.left + B_MINI_ICON + kMiniIconSeparator; 967 968 // big font sizes can push top above icon location top 969 rect.bottom = location.y 970 + roundf((B_MINI_ICON + poseView->FontHeight()) / 2); 971 rect.top = rect.bottom - floorf(poseView->FontHeight()); 972 BTextWidget* widget = WidgetFor(poseView->FirstColumn()->AttrHash()); 973 if (widget != NULL) 974 rect.right += ceil(widget->TextWidth(poseView) + 1); 975 } 976 977 return rect; 978} 979 980 981BRect 982BPose::_IconRect(const BPoseView* poseView, BPoint location) const 983{ 984 uint32 size = poseView->IconSizeInt(); 985 BRect rect; 986 rect.left = location.x + poseView->ListOffset(); 987 rect.right = rect.left + size; 988 rect.top = location.y + (poseView->ListElemHeight() - size) / 2.f; 989 rect.bottom = rect.top + size; 990 return rect; 991} 992 993 994#if DEBUG 995void 996BPose::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