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 44using std::nothrow; 45 46static const float kMarkWidth = 14.0; 47static const float kBorderOffset = 3.0; 48static const float kTextOffset = 4.0; 49 50 51class PathListItem : public SimpleItem, public Observer { 52public: 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 179public: 180 VectorPath* path; 181 182private: 183 PathListView* fListView; 184 bool fMarkEnabled; 185 bool fMarked; 186}; 187 188 189class ShapePathListener : public PathContainerListener, 190 public ShapeContainerListener { 191public: 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 252private: 253 PathListView* fListView; 254 Shape* fShape; 255}; 256 257 258// #pragma mark - 259 260 261enum { 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 279PathListView::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 297PathListView::~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 312void 313PathListView::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 332void 333PathListView::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 370void 371PathListView::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 502void 503PathListView::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 522bool 523PathListView::AcceptDragMessage(const BMessage* message) const 524{ 525 return SimpleListView::AcceptDragMessage(message); 526} 527 528 529void 530PathListView::SetDropTargetRect(const BMessage* message, BPoint where) 531{ 532 SimpleListView::SetDropTargetRect(message, where); 533} 534 535 536bool 537PathListView::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 586void 587PathListView::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 614void 615PathListView::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 641void 642PathListView::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 665BListItem* 666PathListView::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 676int32 677PathListView::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 693Selectable* 694PathListView::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 706void 707PathListView::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 723void 724PathListView::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 743void 744PathListView::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 774void 775PathListView::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 791void 792PathListView::SetCommandStack(CommandStack* stack) 793{ 794 fCommandStack = stack; 795} 796 797 798void 799PathListView::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 852void 853PathListView::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 868bool 869PathListView::_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 888bool 889PathListView::_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 900PathListItem* 901PathListView::_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 918void 919PathListView::_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 946void 947PathListView::_SetPathMarked(VectorPath* path, bool marked) 948{ 949 PathListItem* item = _ItemForPath(path); 950 if (item != NULL) 951 item->SetMarked(marked); 952} 953 954 955void 956PathListView::_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