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