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