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