1 /* 2 * Copyright 2006-2012, 2023, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 * Zardshard 8 */ 9 10 #include "PathListView.h" 11 12 #include <new> 13 #include <stdio.h> 14 15 #include <Application.h> 16 #include <Catalog.h> 17 #include <ListItem.h> 18 #include <Locale.h> 19 #include <Menu.h> 20 #include <MenuItem.h> 21 #include <Message.h> 22 #include <Mime.h> 23 #include <Window.h> 24 25 #include "AddPathsCommand.h" 26 #include "CleanUpPathCommand.h" 27 #include "CommandStack.h" 28 #include "MovePathsCommand.h" 29 #include "Observer.h" 30 #include "PathSourceShape.h" 31 #include "RemovePathsCommand.h" 32 #include "ReversePathCommand.h" 33 #include "RotatePathIndicesCommand.h" 34 #include "Shape.h" 35 #include "Selection.h" 36 #include "UnassignPathCommand.h" 37 #include "Util.h" 38 #include "VectorPath.h" 39 40 41 #undef B_TRANSLATION_CONTEXT 42 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-PathsList" 43 44 45 using std::nothrow; 46 47 static const float kMarkWidth = 14.0; 48 static const float kBorderOffset = 3.0; 49 static const float kTextOffset = 4.0; 50 51 52 class PathListItem : public SimpleItem, public Observer { 53 public: 54 PathListItem(VectorPath* p, PathListView* listView, bool markEnabled) 55 : 56 SimpleItem(""), 57 path(NULL), 58 fListView(listView), 59 fMarkEnabled(markEnabled), 60 fMarked(false) 61 { 62 SetPath(p); 63 } 64 65 66 virtual ~PathListItem() 67 { 68 SetPath(NULL); 69 } 70 71 72 // SimpleItem interface 73 virtual void DrawItem(BView* owner, BRect itemFrame, bool even) 74 { 75 SimpleItem::DrawBackground(owner, itemFrame, even); 76 77 float offset = kBorderOffset + kMarkWidth + kTextOffset; 78 SimpleItem::DrawItem(owner, itemFrame.OffsetByCopy(offset, 0), even); 79 80 if (!fMarkEnabled) 81 return; 82 83 // mark 84 BRect markRect = itemFrame; 85 float markRectBorderTint = B_DARKEN_1_TINT; 86 float markRectFillTint = 1.04; 87 float markTint = B_DARKEN_4_TINT; 88 // Dark Themes 89 rgb_color lowColor = owner->LowColor(); 90 if (lowColor.red + lowColor.green + lowColor.blue < 128 * 3) { 91 markRectBorderTint = B_LIGHTEN_2_TINT; 92 markRectFillTint = 0.85; 93 markTint = 0.1; 94 } 95 markRect.left += kBorderOffset; 96 markRect.right = markRect.left + kMarkWidth; 97 markRect.top = (markRect.top + markRect.bottom - kMarkWidth) / 2.0; 98 markRect.bottom = markRect.top + kMarkWidth; 99 owner->SetHighColor(tint_color(owner->LowColor(), markRectBorderTint)); 100 owner->StrokeRect(markRect); 101 markRect.InsetBy(1, 1); 102 owner->SetHighColor(tint_color(owner->LowColor(), markRectFillTint)); 103 owner->FillRect(markRect); 104 if (fMarked) { 105 markRect.InsetBy(2, 2); 106 owner->SetHighColor(tint_color(owner->LowColor(), 107 markTint)); 108 owner->SetPenSize(2); 109 owner->StrokeLine(markRect.LeftTop(), markRect.RightBottom()); 110 owner->StrokeLine(markRect.LeftBottom(), markRect.RightTop()); 111 owner->SetPenSize(1); 112 } 113 } 114 115 116 // Observer interface 117 virtual void ObjectChanged(const Observable* object) 118 { 119 UpdateText(); 120 } 121 122 123 // PathListItem 124 void SetPath(VectorPath* p) 125 { 126 if (p == path) 127 return; 128 129 if (path) { 130 path->RemoveObserver(this); 131 path->ReleaseReference(); 132 } 133 134 path = p; 135 136 if (path) { 137 path->AcquireReference(); 138 path->AddObserver(this); 139 UpdateText(); 140 } 141 } 142 143 144 void UpdateText() 145 { 146 SetText(path->Name()); 147 Invalidate(); 148 } 149 150 151 void SetMarkEnabled(bool enabled) 152 { 153 if (fMarkEnabled == enabled) 154 return; 155 fMarkEnabled = enabled; 156 Invalidate(); 157 } 158 159 160 void SetMarked(bool marked) 161 { 162 if (fMarked == marked) 163 return; 164 fMarked = marked; 165 Invalidate(); 166 } 167 168 169 void Invalidate() 170 { 171 if (fListView->LockLooper()) { 172 fListView->InvalidateItem( 173 fListView->IndexOf(this)); 174 fListView->UnlockLooper(); 175 } 176 } 177 178 public: 179 VectorPath* path; 180 181 private: 182 PathListView* fListView; 183 bool fMarkEnabled; 184 bool fMarked; 185 }; 186 187 188 class ShapePathListener : public ContainerListener<VectorPath>, 189 public ContainerListener<Shape> { 190 public: 191 ShapePathListener(PathListView* listView) 192 : 193 fListView(listView), 194 fShape(NULL) 195 { 196 } 197 198 199 virtual ~ShapePathListener() 200 { 201 SetShape(NULL); 202 } 203 204 205 // ContainerListener<VectorPath> interface 206 virtual void ItemAdded(VectorPath* path, int32 index) 207 { 208 fListView->_SetPathMarked(path, true); 209 } 210 211 212 virtual void ItemRemoved(VectorPath* path) 213 { 214 fListView->_SetPathMarked(path, false); 215 } 216 217 218 // ContainerListener<Shape> interface 219 virtual void ItemAdded(Shape* shape, int32 index) 220 { 221 } 222 223 224 virtual void ItemRemoved(Shape* shape) 225 { 226 fListView->SetCurrentShape(NULL); 227 } 228 229 230 // ShapePathListener 231 void SetShape(PathSourceShape* shape) 232 { 233 if (fShape == shape) 234 return; 235 236 if (fShape) 237 fShape->Paths()->RemoveListener(this); 238 239 fShape = shape; 240 241 if (fShape) 242 fShape->Paths()->AddListener(this); 243 } 244 245 246 Shape* CurrentShape() const 247 { 248 return fShape; 249 } 250 251 private: 252 PathListView* fListView; 253 PathSourceShape* fShape; 254 }; 255 256 257 // #pragma mark - 258 259 260 enum { 261 MSG_ADD = 'addp', 262 263 MSG_ADD_RECT = 'addr', 264 MSG_ADD_CIRCLE = 'addc', 265 MSG_ADD_ARC = 'adda', 266 267 MSG_DUPLICATE = 'dupp', 268 269 MSG_REVERSE = 'rvrs', 270 MSG_CLEAN_UP = 'clup', 271 MSG_ROTATE_INDICES_CW = 'ricw', 272 MSG_ROTATE_INDICES_CCW = 'ricc', 273 274 MSG_REMOVE = 'remp', 275 }; 276 277 278 PathListView::PathListView(BRect frame, const char* name, BMessage* message, 279 BHandler* target) 280 : 281 SimpleListView(frame, name, NULL, B_SINGLE_SELECTION_LIST), 282 fMessage(message), 283 fMenu(NULL), 284 285 fPathContainer(NULL), 286 fShapeContainer(NULL), 287 fCommandStack(NULL), 288 289 fCurrentShape(NULL), 290 fShapePathListener(new ShapePathListener(this)) 291 { 292 SetTarget(target); 293 } 294 295 296 PathListView::~PathListView() 297 { 298 _MakeEmpty(); 299 delete fMessage; 300 301 if (fPathContainer != NULL) 302 fPathContainer->RemoveListener(this); 303 304 if (fShapeContainer != NULL) 305 fShapeContainer->RemoveListener(fShapePathListener); 306 307 delete fShapePathListener; 308 } 309 310 311 void 312 PathListView::SelectionChanged() 313 { 314 SimpleListView::SelectionChanged(); 315 316 if (!fSyncingToSelection) { 317 // NOTE: single selection list 318 PathListItem* item 319 = dynamic_cast<PathListItem*>(ItemAt(CurrentSelection(0))); 320 if (fMessage != NULL) { 321 BMessage message(*fMessage); 322 message.AddPointer("path", item ? (void*)item->path : NULL); 323 Invoke(&message); 324 } 325 } 326 327 _UpdateMenu(); 328 } 329 330 331 void 332 PathListView::MouseDown(BPoint where) 333 { 334 if (fCurrentShape == NULL) { 335 SimpleListView::MouseDown(where); 336 return; 337 } 338 339 bool handled = false; 340 int32 index = IndexOf(where); 341 PathListItem* item = dynamic_cast<PathListItem*>(ItemAt(index)); 342 if (item != NULL) { 343 BRect itemFrame(ItemFrame(index)); 344 itemFrame.right = itemFrame.left + kBorderOffset + kMarkWidth 345 + kTextOffset / 2.0; 346 347 VectorPath* path = item->path; 348 if (itemFrame.Contains(where) && fCommandStack) { 349 // add or remove the path to the shape 350 ::Command* command; 351 if (fCurrentShape->Paths()->HasItem(path)) { 352 command = new UnassignPathCommand(fCurrentShape, path); 353 } else { 354 VectorPath* paths[1]; 355 paths[0] = path; 356 command = new AddPathsCommand(fCurrentShape->Paths(), 357 paths, 1, false, fCurrentShape->Paths()->CountItems()); 358 } 359 fCommandStack->Perform(command); 360 handled = true; 361 } 362 } 363 364 if (!handled) 365 SimpleListView::MouseDown(where); 366 } 367 368 369 void 370 PathListView::MessageReceived(BMessage* message) 371 { 372 switch (message->what) { 373 case MSG_ADD: 374 if (fCommandStack != NULL) { 375 VectorPath* path; 376 AddPathsCommand* command; 377 new_path(fPathContainer, &path, &command); 378 fCommandStack->Perform(command); 379 } 380 break; 381 382 case MSG_ADD_RECT: 383 if (fCommandStack != NULL) { 384 VectorPath* path; 385 AddPathsCommand* command; 386 new_path(fPathContainer, &path, &command); 387 if (path != NULL) { 388 path->AddPoint(BPoint(16, 16)); 389 path->AddPoint(BPoint(16, 48)); 390 path->AddPoint(BPoint(48, 48)); 391 path->AddPoint(BPoint(48, 16)); 392 path->SetClosed(true); 393 } 394 fCommandStack->Perform(command); 395 } 396 break; 397 398 case MSG_ADD_CIRCLE: 399 // TODO: ask for number of secions 400 if (fCommandStack != NULL) { 401 VectorPath* path; 402 AddPathsCommand* command; 403 new_path(fPathContainer, &path, &command); 404 if (path != NULL) { 405 // add four control points defining a circle: 406 // a 407 // b d 408 // c 409 BPoint a(32, 16); 410 BPoint b(16, 32); 411 BPoint c(32, 48); 412 BPoint d(48, 32); 413 414 path->AddPoint(a); 415 path->AddPoint(b); 416 path->AddPoint(c); 417 path->AddPoint(d); 418 419 path->SetClosed(true); 420 421 float controlDist = 0.552284 * 16; 422 path->SetPoint(0, a, a + BPoint(controlDist, 0.0), 423 a + BPoint(-controlDist, 0.0), true); 424 path->SetPoint(1, b, b + BPoint(0.0, -controlDist), 425 b + BPoint(0.0, controlDist), true); 426 path->SetPoint(2, c, c + BPoint(-controlDist, 0.0), 427 c + BPoint(controlDist, 0.0), true); 428 path->SetPoint(3, d, d + BPoint(0.0, controlDist), 429 d + BPoint(0.0, -controlDist), true); 430 } 431 fCommandStack->Perform(command); 432 } 433 break; 434 435 case MSG_DUPLICATE: 436 if (fCommandStack != NULL) { 437 PathListItem* item = dynamic_cast<PathListItem*>( 438 ItemAt(CurrentSelection(0))); 439 if (item == NULL) 440 break; 441 442 VectorPath* path; 443 AddPathsCommand* command; 444 new_path(fPathContainer, &path, &command, item->path); 445 fCommandStack->Perform(command); 446 } 447 break; 448 449 case MSG_REVERSE: 450 if (fCommandStack != NULL) { 451 PathListItem* item = dynamic_cast<PathListItem*>( 452 ItemAt(CurrentSelection(0))); 453 if (item == NULL) 454 break; 455 456 ReversePathCommand* command 457 = new (nothrow) ReversePathCommand(item->path); 458 fCommandStack->Perform(command); 459 } 460 break; 461 462 case MSG_CLEAN_UP: 463 if (fCommandStack != NULL) { 464 PathListItem* item = dynamic_cast<PathListItem*>( 465 ItemAt(CurrentSelection(0))); 466 if (item == NULL) 467 break; 468 469 CleanUpPathCommand* command 470 = new (nothrow) CleanUpPathCommand(item->path); 471 fCommandStack->Perform(command); 472 } 473 break; 474 475 case MSG_ROTATE_INDICES_CW: 476 case MSG_ROTATE_INDICES_CCW: 477 if (fCommandStack != NULL) { 478 PathListItem* item = dynamic_cast<PathListItem*>( 479 ItemAt(CurrentSelection(0))); 480 if (item == NULL) 481 break; 482 483 RotatePathIndicesCommand* command 484 = new (nothrow) RotatePathIndicesCommand(item->path, 485 message->what == MSG_ROTATE_INDICES_CW); 486 fCommandStack->Perform(command); 487 } 488 break; 489 490 case MSG_REMOVE: 491 RemoveSelected(); 492 break; 493 494 default: 495 SimpleListView::MessageReceived(message); 496 break; 497 } 498 } 499 500 501 status_t 502 PathListView::ArchiveSelection(BMessage* into, bool deep) const 503 { 504 into->what = PathListView::kSelectionArchiveCode; 505 506 int32 count = CountSelectedItems(); 507 for (int32 i = 0; i < count; i++) { 508 PathListItem* item = dynamic_cast<PathListItem*>( 509 ItemAt(CurrentSelection(i))); 510 if (item != NULL) { 511 BMessage archive; 512 if (item->path->Archive(&archive, deep) == B_OK) 513 into->AddMessage("path", &archive); 514 } else 515 return B_ERROR; 516 } 517 518 return B_OK; 519 } 520 521 522 bool 523 PathListView::InstantiateSelection(const BMessage* archive, int32 dropIndex) 524 { 525 if (archive->what != PathListView::kSelectionArchiveCode 526 || fCommandStack == NULL || fPathContainer == NULL) 527 return false; 528 529 // Drag may have come from another instance, like in another window. 530 // Reconstruct the Styles from the archive and add them at the drop 531 // index. 532 int index = 0; 533 BList paths; 534 while (true) { 535 BMessage pathArchive; 536 if (archive->FindMessage("path", index, &pathArchive) != B_OK) 537 break; 538 539 VectorPath* path = new(std::nothrow) VectorPath(&pathArchive); 540 if (path == NULL) 541 break; 542 543 if (!paths.AddItem(path)) { 544 delete path; 545 break; 546 } 547 548 index++; 549 } 550 551 int32 count = paths.CountItems(); 552 if (count == 0) 553 return false; 554 555 AddPathsCommand* command = new(nothrow) AddPathsCommand(fPathContainer, 556 (VectorPath**)paths.Items(), count, true, dropIndex); 557 if (command == NULL) { 558 for (int32 i = 0; i < count; i++) 559 delete (VectorPath*)paths.ItemAtFast(i); 560 return false; 561 } 562 563 fCommandStack->Perform(command); 564 565 return true; 566 } 567 568 569 void 570 PathListView::MoveItems(BList& items, int32 toIndex) 571 { 572 if (fCommandStack == NULL || fPathContainer == NULL) 573 return; 574 575 int32 count = items.CountItems(); 576 VectorPath** paths = new (nothrow) VectorPath*[count]; 577 if (paths == NULL) 578 return; 579 580 for (int32 i = 0; i < count; i++) { 581 PathListItem* item 582 = dynamic_cast<PathListItem*>((BListItem*)items.ItemAtFast(i)); 583 paths[i] = item ? item->path : NULL; 584 } 585 586 MovePathsCommand* command = new (nothrow) MovePathsCommand(fPathContainer, 587 paths, count, toIndex); 588 if (command == NULL) { 589 delete[] paths; 590 return; 591 } 592 593 fCommandStack->Perform(command); 594 } 595 596 597 void 598 PathListView::CopyItems(BList& items, int32 toIndex) 599 { 600 if (fCommandStack == NULL || fPathContainer == NULL) 601 return; 602 603 int32 count = items.CountItems(); 604 VectorPath* paths[count]; 605 606 for (int32 i = 0; i < count; i++) { 607 PathListItem* item 608 = dynamic_cast<PathListItem*>((BListItem*)items.ItemAtFast(i)); 609 paths[i] = item ? new (nothrow) VectorPath(*item->path) : NULL; 610 } 611 612 AddPathsCommand* command = new(nothrow) AddPathsCommand(fPathContainer, 613 paths, count, true, toIndex); 614 if (command == NULL) { 615 for (int32 i = 0; i < count; i++) 616 delete paths[i]; 617 return; 618 } 619 620 fCommandStack->Perform(command); 621 } 622 623 624 void 625 PathListView::RemoveItemList(BList& items) 626 { 627 if (fCommandStack == NULL || fPathContainer == NULL) 628 return; 629 630 int32 count = items.CountItems(); 631 int32 indices[count]; 632 for (int32 i = 0; i < count; i++) 633 indices[i] = IndexOf((BListItem*)items.ItemAtFast(i)); 634 635 RemovePathsCommand* command = new (nothrow) RemovePathsCommand( 636 fPathContainer, indices, count); 637 638 fCommandStack->Perform(command); 639 } 640 641 642 BListItem* 643 PathListView::CloneItem(int32 index) const 644 { 645 if (PathListItem* item = dynamic_cast<PathListItem*>(ItemAt(index))) { 646 return new(nothrow) PathListItem(item->path, 647 const_cast<PathListView*>(this), fCurrentShape != NULL); 648 } 649 return NULL; 650 } 651 652 653 int32 654 PathListView::IndexOfSelectable(Selectable* selectable) const 655 { 656 VectorPath* path = dynamic_cast<VectorPath*>(selectable); 657 if (path == NULL) 658 return -1; 659 660 int32 count = CountItems(); 661 for (int32 i = 0; i < count; i++) { 662 if (SelectableFor(ItemAt(i)) == path) 663 return i; 664 } 665 666 return -1; 667 } 668 669 670 Selectable* 671 PathListView::SelectableFor(BListItem* item) const 672 { 673 PathListItem* pathItem = dynamic_cast<PathListItem*>(item); 674 if (pathItem != NULL) 675 return pathItem->path; 676 return NULL; 677 } 678 679 680 // #pragma mark - 681 682 683 void 684 PathListView::ItemAdded(VectorPath* path, int32 index) 685 { 686 // NOTE: we are in the thread that messed with the 687 // ShapeContainer, so no need to lock the 688 // container, when this is changed to asynchronous 689 // notifications, then it would need to be read-locked! 690 if (!LockLooper()) 691 return; 692 693 if (_AddPath(path, index)) 694 Select(index); 695 696 UnlockLooper(); 697 } 698 699 700 void 701 PathListView::ItemRemoved(VectorPath* path) 702 { 703 // NOTE: we are in the thread that messed with the 704 // ShapeContainer, so no need to lock the 705 // container, when this is changed to asynchronous 706 // notifications, then it would need to be read-locked! 707 if (!LockLooper()) 708 return; 709 710 // NOTE: we're only interested in VectorPath objects 711 _RemovePath(path); 712 713 UnlockLooper(); 714 } 715 716 717 // #pragma mark - 718 719 720 void 721 PathListView::SetPathContainer(Container<VectorPath>* container) 722 { 723 if (fPathContainer == container) 724 return; 725 726 // detach from old container 727 if (fPathContainer != NULL) 728 fPathContainer->RemoveListener(this); 729 730 _MakeEmpty(); 731 732 fPathContainer = container; 733 734 if (fPathContainer == NULL) 735 return; 736 737 fPathContainer->AddListener(this); 738 739 // sync 740 // if (!fPathContainer->ReadLock()) 741 // return; 742 743 int32 count = fPathContainer->CountItems(); 744 for (int32 i = 0; i < count; i++) 745 _AddPath(fPathContainer->ItemAtFast(i), i); 746 747 // fPathContainer->ReadUnlock(); 748 } 749 750 751 void 752 PathListView::SetShapeContainer(Container<Shape>* container) 753 { 754 if (fShapeContainer == container) 755 return; 756 757 // detach from old container 758 if (fShapeContainer != NULL) 759 fShapeContainer->RemoveListener(fShapePathListener); 760 761 fShapeContainer = container; 762 763 if (fShapeContainer != NULL) 764 fShapeContainer->AddListener(fShapePathListener); 765 } 766 767 768 void 769 PathListView::SetCommandStack(CommandStack* stack) 770 { 771 fCommandStack = stack; 772 } 773 774 775 void 776 PathListView::SetMenu(BMenu* menu) 777 { 778 fMenu = menu; 779 if (fMenu == NULL) 780 return; 781 782 fAddMI = new BMenuItem(B_TRANSLATE("Add"), 783 new BMessage(MSG_ADD)); 784 fAddRectMI = new BMenuItem(B_TRANSLATE("Add rect"), 785 new BMessage(MSG_ADD_RECT)); 786 fAddCircleMI = new BMenuItem(B_TRANSLATE("Add circle"/*B_UTF8_ELLIPSIS*/), 787 new BMessage(MSG_ADD_CIRCLE)); 788 // fAddArcMI = new BMenuItem("Add arc" B_UTF8_ELLIPSIS, 789 // new BMessage(MSG_ADD_ARC)); 790 fDuplicateMI = new BMenuItem(B_TRANSLATE("Duplicate"), 791 new BMessage(MSG_DUPLICATE)); 792 fReverseMI = new BMenuItem(B_TRANSLATE("Reverse"), 793 new BMessage(MSG_REVERSE)); 794 fCleanUpMI = new BMenuItem(B_TRANSLATE("Clean up"), 795 new BMessage(MSG_CLEAN_UP)); 796 fRotateIndicesRightMI = new BMenuItem(B_TRANSLATE("Rotate indices forwards"), 797 new BMessage(MSG_ROTATE_INDICES_CCW), 'R'); 798 fRotateIndicesLeftMI = new BMenuItem(B_TRANSLATE("Rotate indices backwards"), 799 new BMessage(MSG_ROTATE_INDICES_CW), 'R', B_SHIFT_KEY); 800 fRemoveMI = new BMenuItem(B_TRANSLATE("Remove"), 801 new BMessage(MSG_REMOVE)); 802 803 fMenu->AddItem(fAddMI); 804 fMenu->AddItem(fAddRectMI); 805 fMenu->AddItem(fAddCircleMI); 806 // fMenu->AddItem(fAddArcMI); 807 808 fMenu->AddSeparatorItem(); 809 810 fMenu->AddItem(fDuplicateMI); 811 fMenu->AddItem(fReverseMI); 812 fMenu->AddItem(fCleanUpMI); 813 814 fMenu->AddSeparatorItem(); 815 816 fMenu->AddItem(fRotateIndicesRightMI); 817 fMenu->AddItem(fRotateIndicesLeftMI); 818 819 fMenu->AddSeparatorItem(); 820 821 fMenu->AddItem(fRemoveMI); 822 823 fMenu->SetTargetForItems(this); 824 825 _UpdateMenu(); 826 } 827 828 829 void 830 PathListView::SetCurrentShape(Shape* shape) 831 { 832 if (fCurrentShape == shape) 833 return; 834 835 fCurrentShape = dynamic_cast<PathSourceShape*>(shape); 836 fShapePathListener->SetShape(fCurrentShape); 837 838 _UpdateMarks(); 839 } 840 841 842 // #pragma mark - 843 844 845 bool 846 PathListView::_AddPath(VectorPath* path, int32 index) 847 { 848 if (path == NULL) 849 return false; 850 851 PathListItem* item = new(nothrow) PathListItem(path, this, 852 fCurrentShape != NULL); 853 if (item == NULL) 854 return false; 855 856 if (!AddItem(item, index)) { 857 delete item; 858 return false; 859 } 860 861 return true; 862 } 863 864 865 bool 866 PathListView::_RemovePath(VectorPath* path) 867 { 868 PathListItem* item = _ItemForPath(path); 869 if (item != NULL && RemoveItem(item)) { 870 delete item; 871 return true; 872 } 873 return false; 874 } 875 876 877 PathListItem* 878 PathListView::_ItemForPath(VectorPath* path) const 879 { 880 int32 count = CountItems(); 881 for (int32 i = 0; i < count; i++) { 882 PathListItem* item = dynamic_cast<PathListItem*>(ItemAt(i)); 883 if (item == NULL) 884 continue; 885 if (item->path == path) 886 return item; 887 } 888 return NULL; 889 } 890 891 892 // #pragma mark - 893 894 895 void 896 PathListView::_UpdateMarks() 897 { 898 int32 count = CountItems(); 899 if (fCurrentShape != NULL) { 900 // enable display of marks and mark items whoes 901 // path is contained in fCurrentShape 902 for (int32 i = 0; i < count; i++) { 903 PathListItem* item = dynamic_cast<PathListItem*>(ItemAt(i)); 904 if (item == NULL) 905 continue; 906 item->SetMarkEnabled(true); 907 item->SetMarked(fCurrentShape->Paths()->HasItem(item->path)); 908 } 909 } else { 910 // disable display of marks 911 for (int32 i = 0; i < count; i++) { 912 PathListItem* item = dynamic_cast<PathListItem*>(ItemAt(i)); 913 if (item == NULL) 914 continue; 915 item->SetMarkEnabled(false); 916 } 917 } 918 919 Invalidate(); 920 } 921 922 923 void 924 PathListView::_SetPathMarked(VectorPath* path, bool marked) 925 { 926 PathListItem* item = _ItemForPath(path); 927 if (item != NULL) 928 item->SetMarked(marked); 929 } 930 931 932 void 933 PathListView::_UpdateMenu() 934 { 935 if (fMenu == NULL) 936 return; 937 938 bool gotSelection = CurrentSelection(0) >= 0; 939 940 fDuplicateMI->SetEnabled(gotSelection); 941 fReverseMI->SetEnabled(gotSelection); 942 fCleanUpMI->SetEnabled(gotSelection); 943 fRotateIndicesLeftMI->SetEnabled(gotSelection); 944 fRotateIndicesRightMI->SetEnabled(gotSelection); 945 fRemoveMI->SetEnabled(gotSelection); 946 } 947 948 949