1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 // PoseView scripting interface 36 37 #include <stdlib.h> 38 #include <stdio.h> 39 #include <string.h> 40 41 #include <ByteOrder.h> 42 #include <Debug.h> 43 #include <Message.h> 44 #include <PropertyInfo.h> 45 46 #include "Tracker.h" 47 #include "PoseView.h" 48 49 #define kPosesSuites "suite/vnd.Be-TrackerPoses" 50 51 #define kPropertyPath "Path" 52 53 #ifndef _SCRIPTING_ONLY 54 #if _SUPPORTS_FEATURE_SCRIPTING 55 #define _SCRIPTING_ONLY(x) x 56 #else 57 #define _SCRIPTING_ONLY(x) 58 #endif 59 #endif 60 61 // notes on PoseView scripting interface: 62 // Indices and entry_refs are used to specify poses; In the case of indices 63 // and previous/next specifiers the current PoseView sort order is used. 64 // If PoseView is not in list view mode, the order in which poses are indexed 65 // is arbitrary. 66 // Both of these specifiers, but indices more so, are likely to be accurate only 67 // till a next change to the PoseView (a change may be adding, removing a pose, changing 68 // an attribute or stat resulting in a sort ordering change, changing the sort ordering 69 // rule. When getting a selected item, there is no guarantee that the item will still 70 // be selected after the operation. The client must be able to deal with these 71 // inaccuracies. 72 // Specifying an index/entry_ref that no longer exists will be handled well. 73 74 #if 0 75 doo Tracker get Suites of Poses of Window test 76 doo Tracker get Path of Poses of Window test 77 doo Tracker count Entry of Poses of Window test 78 doo Tracker get Entry of Poses of Window test 79 doo Tracker get Entry 2 of Poses of Window test 80 doo Tracker count Selection of Poses of Window test 81 doo Tracker get Selection of Poses of Window test 82 doo Tracker delete Entry 'test/6L6' of Poses of Window test 83 doo Tracker execute Entry 'test/6L6' of Poses of Window test 84 doo Tracker execute Entry 2 of Poses of Window test 85 doo Tracker set Selection of Poses of Window test to [0,2] 86 doo Tracker set Selection of Poses of Window test to 'test/KT55' 87 doo Tracker create Selection of Poses of Window test to 'test/EL34' 88 doo Tracker delete Selection 'test/EL34' of Poses of Window test 89 #endif 90 91 // ToDo: 92 // access list view column state 93 // access poses 94 // - pose location 95 // - pose text widgets 96 97 98 #if _SUPPORTS_FEATURE_SCRIPTING 99 100 const property_info kPosesPropertyList[] = { 101 { kPropertyPath, 102 { B_GET_PROPERTY }, 103 { B_DIRECT_SPECIFIER }, 104 "get Path of ... # returns the path of a Tracker window, " 105 "error if no path associated", 106 0, 107 { B_REF_TYPE }, 108 {}, 109 {} 110 }, 111 { kPropertyEntry, 112 { B_COUNT_PROPERTIES }, 113 { B_DIRECT_SPECIFIER }, 114 "count Entry of ... # count entries in a PoseView", 115 0, 116 { B_INT32_TYPE }, 117 {}, 118 {} 119 }, 120 { kPropertyEntry, 121 { B_DELETE_PROPERTY }, 122 { B_ENTRY_SPECIFIER, B_INDEX_SPECIFIER }, 123 "delete Entry {path|index} # deletes specified entries in a PoseView", 124 0, 125 {}, 126 {}, 127 {} 128 }, 129 { kPropertyEntry, 130 { B_GET_PROPERTY }, 131 { B_DIRECT_SPECIFIER, B_INDEX_SPECIFIER, kPreviousSpecifier, kNextSpecifier }, 132 "get Entry [next|previous|index] # returns specified entries", 133 0, 134 { B_REF_TYPE }, 135 {}, 136 {} 137 }, 138 { kPropertyEntry, 139 { B_EXECUTE_PROPERTY }, 140 { B_ENTRY_SPECIFIER, B_INDEX_SPECIFIER }, 141 "execute Entry {path|index} # opens specified entries", 142 0, 143 { B_REF_TYPE }, 144 {}, 145 {} 146 }, 147 { kPropertySelection, 148 { B_GET_PROPERTY }, 149 { B_DIRECT_SPECIFIER, kPreviousSpecifier, kNextSpecifier }, 150 "get Selection [next|previous] # returns the selected entries", 151 0, 152 { B_REF_TYPE }, 153 {}, 154 {} 155 }, 156 { kPropertySelection, 157 { B_SET_PROPERTY }, 158 { B_DIRECT_SPECIFIER, kPreviousSpecifier, kNextSpecifier }, 159 "set Selection of ... to {next|previous|entry} # selects specified entries", 160 0, 161 {}, 162 {}, 163 {} 164 }, 165 { kPropertySelection, 166 { B_COUNT_PROPERTIES }, 167 { B_DIRECT_SPECIFIER }, 168 "count Selection of ... # counts selected items", 169 0, 170 { B_INT32_TYPE }, 171 {}, 172 {} 173 }, 174 { kPropertySelection, 175 { B_CREATE_PROPERTY }, 176 { B_DIRECT_SPECIFIER }, 177 "create selection of ... to {entry|index} " 178 "# adds specified items to a selection in a PoseView", 179 0, 180 {}, 181 {}, 182 {} 183 }, 184 { kPropertySelection, 185 { B_DELETE_PROPERTY }, 186 { B_ENTRY_SPECIFIER, B_INDEX_SPECIFIER }, 187 "delete selection {path|index} of ... " 188 "# removes specified items from a selection in a PoseView", 189 0, 190 {}, 191 {}, 192 {} 193 }, 194 {NULL, 195 {}, 196 {}, 197 NULL, 0, 198 {}, 199 {}, 200 {} 201 } 202 }; 203 204 #endif 205 206 status_t 207 BPoseView::GetSupportedSuites(BMessage *_SCRIPTING_ONLY(data)) 208 { 209 #if _SUPPORTS_FEATURE_SCRIPTING 210 data->AddString("suites", kPosesSuites); 211 BPropertyInfo propertyInfo(const_cast<property_info *>(kPosesPropertyList)); 212 data->AddFlat("messages", &propertyInfo); 213 214 return _inherited::GetSupportedSuites(data); 215 #else 216 return B_UNSUPPORTED; 217 #endif 218 } 219 220 bool 221 BPoseView::HandleScriptingMessage(BMessage *_SCRIPTING_ONLY(message)) 222 { 223 #if _SUPPORTS_FEATURE_SCRIPTING 224 if (message->what != B_GET_PROPERTY 225 && message->what != B_SET_PROPERTY 226 && message->what != B_CREATE_PROPERTY 227 && message->what != B_COUNT_PROPERTIES 228 && message->what != B_DELETE_PROPERTY 229 && message->what != B_EXECUTE_PROPERTY) 230 return false; 231 232 // dispatch scripting messages 233 BMessage reply(B_REPLY); 234 const char *property = 0; 235 bool handled = false; 236 237 int32 index = 0; 238 int32 form = 0; 239 BMessage specifier; 240 status_t result = message->GetCurrentSpecifier(&index, &specifier, 241 &form, &property); 242 243 if (result != B_OK || index == -1) 244 return false; 245 246 ASSERT(property); 247 248 switch (message->what) { 249 case B_CREATE_PROPERTY: 250 handled = CreateProperty(message, &specifier, form, property, &reply); 251 break; 252 253 case B_GET_PROPERTY: 254 handled = GetProperty(&specifier, form, property, &reply); 255 break; 256 257 case B_SET_PROPERTY: 258 handled = SetProperty(message, &specifier, form, property, &reply); 259 break; 260 261 case B_COUNT_PROPERTIES: 262 handled = CountProperty(&specifier, form, property, &reply); 263 break; 264 265 case B_DELETE_PROPERTY: 266 handled = DeleteProperty(&specifier, form, property, &reply); 267 break; 268 269 case B_EXECUTE_PROPERTY: 270 handled = ExecuteProperty(&specifier, form, property, &reply); 271 break; 272 } 273 274 if (handled) 275 // done handling message, send a reply 276 message->SendReply(&reply); 277 return handled; 278 #else 279 return false; 280 #endif 281 } 282 283 bool 284 BPoseView::ExecuteProperty(BMessage *_SCRIPTING_ONLY(specifier), 285 int32 _SCRIPTING_ONLY(form), const char *_SCRIPTING_ONLY(property), 286 BMessage *_SCRIPTING_ONLY(reply)) 287 { 288 #if _SUPPORTS_FEATURE_SCRIPTING 289 status_t error = B_OK; 290 bool handled = false; 291 if (strcmp(property, kPropertyEntry) == 0) { 292 BMessage launchMessage(B_REFS_RECEIVED); 293 294 if (form == (int32)B_ENTRY_SPECIFIER) { 295 // move all poses specified by entry_ref to Trash 296 entry_ref ref; 297 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 298 == B_OK; index++) 299 launchMessage.AddRef("refs", &ref); 300 301 } else if (form == (int32)B_INDEX_SPECIFIER) { 302 // move all poses specified by index to Trash 303 int32 specifyingIndex; 304 for (int32 index = 0; specifier->FindInt32("index", index, 305 &specifyingIndex) == B_OK; index++) { 306 BPose *pose = PoseAtIndex(specifyingIndex); 307 308 if (!pose) { 309 error = B_ENTRY_NOT_FOUND; 310 break; 311 } 312 313 launchMessage.AddRef("refs", pose->TargetModel()->EntryRef()); 314 } 315 } else 316 return false; 317 318 if (error == B_OK) { 319 // add a messenger to the launch message that will be used to 320 // dispatch scripting calls from apps to the PoseView 321 launchMessage.AddMessenger("TrackerViewToken", BMessenger(this, 0, 0)); 322 if (fSelectionHandler) 323 fSelectionHandler->PostMessage(&launchMessage); 324 } 325 handled = true; 326 } 327 328 if (error != B_OK) 329 reply->AddInt32("error", error); 330 331 return handled; 332 #else 333 return false; 334 #endif 335 } 336 337 bool 338 BPoseView::CreateProperty(BMessage *_SCRIPTING_ONLY(specifier), BMessage *, 339 int32 _SCRIPTING_ONLY(form), const char *_SCRIPTING_ONLY(property), 340 BMessage *_SCRIPTING_ONLY(reply)) 341 { 342 #if _SUPPORTS_FEATURE_SCRIPTING 343 status_t error = B_OK; 344 bool handled = false; 345 if (strcmp(property, kPropertySelection) == 0) { 346 // creating on a selection expands the current selection 347 348 if (form != B_DIRECT_SPECIFIER) 349 // only support direct specifier 350 return false; 351 352 // items to add to a selection may be passed as refs or as indices 353 if (specifier->HasRef("data")) { 354 entry_ref ref; 355 // select poses specified by entries 356 for (int32 index = 0; specifier->FindRef("data", index, &ref) 357 == B_OK; index++) { 358 359 int32 poseIndex; 360 BPose *pose = FindPose(&ref, form, &poseIndex); 361 362 if (!pose) { 363 error = B_ENTRY_NOT_FOUND; 364 handled = true; 365 break; 366 } 367 368 AddPoseToSelection(pose, poseIndex); 369 } 370 handled = true; 371 } else { 372 // select poses specified by indices 373 int32 specifyingIndex; 374 for (int32 index = 0; specifier->FindInt32("data", index, 375 &specifyingIndex) == B_OK; index++) { 376 377 BPose *pose = PoseAtIndex(specifyingIndex); 378 if (!pose) { 379 error = B_BAD_INDEX; 380 handled = true; 381 break; 382 } 383 384 AddPoseToSelection(pose, specifyingIndex); 385 } 386 handled = true; 387 } 388 } 389 390 if (error != B_OK) 391 reply->AddInt32("error", error); 392 393 return handled; 394 #else 395 return false; 396 #endif 397 } 398 399 bool 400 BPoseView::DeleteProperty(BMessage *_SCRIPTING_ONLY(specifier), 401 int32 _SCRIPTING_ONLY(form), const char *_SCRIPTING_ONLY(property), 402 BMessage *_SCRIPTING_ONLY(reply)) 403 { 404 #if _SUPPORTS_FEATURE_SCRIPTING 405 status_t error = B_OK; 406 bool handled = false; 407 408 if (strcmp(property, kPropertySelection) == 0) { 409 // deleting on a selection is handled as removing a part of the selection 410 // not to be confused with deleting a selected item 411 412 if (form == (int32)B_ENTRY_SPECIFIER) { 413 entry_ref ref; 414 // select poses specified by entries 415 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 416 == B_OK; index++) { 417 418 int32 poseIndex; 419 BPose *pose = FindPose(&ref, form, &poseIndex); 420 421 if (!pose) { 422 error = B_ENTRY_NOT_FOUND; 423 break; 424 } 425 426 RemovePoseFromSelection(pose); 427 } 428 handled = true; 429 430 } else if (form == B_INDEX_SPECIFIER) { 431 // move all poses specified by index to Trash 432 int32 specifyingIndex; 433 for (int32 index = 0; specifier->FindInt32("index", index, 434 &specifyingIndex) == B_OK; index++) { 435 BPose *pose = PoseAtIndex(specifyingIndex); 436 437 if (!pose) { 438 error = B_BAD_INDEX; 439 break; 440 } 441 442 RemovePoseFromSelection(pose); 443 } 444 handled = true; 445 } else 446 return false; 447 448 } else if (strcmp(property, kPropertyEntry) == 0) { 449 // deleting entries is handled by moving entries to trash 450 451 // build a list of entries, specified by the specifier 452 BObjectList<entry_ref> *entryList = new BObjectList<entry_ref>(); 453 // list will be deleted for us by the trashing thread 454 455 if (form == (int32)B_ENTRY_SPECIFIER) { 456 // move all poses specified by entry_ref to Trash 457 entry_ref ref; 458 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 459 == B_OK; index++) 460 entryList->AddItem(new entry_ref(ref)); 461 462 } else if (form == (int32)B_INDEX_SPECIFIER) { 463 // move all poses specified by index to Trash 464 int32 specifyingIndex; 465 for (int32 index = 0; specifier->FindInt32("index", index, &specifyingIndex) 466 == B_OK; index++) { 467 BPose *pose = PoseAtIndex(specifyingIndex); 468 469 if (!pose) { 470 error = B_BAD_INDEX; 471 break; 472 } 473 474 entryList->AddItem(new entry_ref(*pose->TargetModel()->EntryRef())); 475 } 476 } else 477 return false; 478 479 if (error == B_OK) { 480 TrackerSettings settings; 481 if (!settings.DontMoveFilesToTrash()) { 482 // move the list we build into trash, don't make the trashing task 483 // select the next item 484 MoveListToTrash(entryList, false, false); 485 } else 486 Delete(entryList, false, settings.AskBeforeDeleteFile()); 487 } 488 489 handled = true; 490 } 491 492 if (error != B_OK) 493 reply->AddInt32("error", error); 494 495 return handled; 496 #else 497 return false; 498 #endif 499 } 500 501 bool 502 BPoseView::CountProperty(BMessage *, int32, const char *_SCRIPTING_ONLY(property), 503 BMessage *_SCRIPTING_ONLY(reply)) 504 { 505 #if _SUPPORTS_FEATURE_SCRIPTING 506 bool handled = false; 507 // PRINT(("BPoseView::CountProperty, %s\n", property)); 508 509 // just return the respecitve counts 510 if (strcmp(property, kPropertySelection) == 0) { 511 reply->AddInt32("result", fSelectionList->CountItems()); 512 handled = true; 513 } else if (strcmp(property, kPropertyEntry) == 0) { 514 reply->AddInt32("result", fPoseList->CountItems()); 515 handled = true; 516 } 517 return handled; 518 #else 519 return false; 520 #endif 521 } 522 523 bool 524 BPoseView::GetProperty(BMessage *_SCRIPTING_ONLY(specifier), 525 int32 _SCRIPTING_ONLY(form), const char *_SCRIPTING_ONLY(property), 526 BMessage *_SCRIPTING_ONLY(reply)) 527 { 528 #if _SUPPORTS_FEATURE_SCRIPTING 529 // PRINT(("GetProperty %s\n", property)); 530 bool handled = false; 531 status_t error = B_OK; 532 533 if (strcmp(property, kPropertyPath) == 0) { 534 if (form == B_DIRECT_SPECIFIER) { 535 handled = true; 536 if (!TargetModel()) 537 error = B_NOT_A_DIRECTORY; 538 else 539 reply->AddRef("result", TargetModel()->EntryRef()); 540 } 541 } else if (strcmp(property, kPropertySelection) == 0) { 542 int32 count = fSelectionList->CountItems(); 543 switch (form) { 544 case B_DIRECT_SPECIFIER: 545 // return entries of all poses in selection 546 for (int32 index = 0; index < count; index++) 547 reply->AddRef("result", fSelectionList->ItemAt(index)-> 548 TargetModel()->EntryRef()); 549 550 handled = true; 551 break; 552 553 case kPreviousSpecifier: 554 case kNextSpecifier: 555 { 556 // return entry and index of selected pose before or after 557 // specified pose 558 entry_ref ref; 559 if (specifier->FindRef("data", &ref) != B_OK) 560 break; 561 562 int32 poseIndex; 563 BPose *pose = FindPose(&ref, &poseIndex); 564 565 for (;;) { 566 if (form == (int32)kPreviousSpecifier) 567 pose = PoseAtIndex(--poseIndex); 568 else if (form == (int32)kNextSpecifier) 569 pose = PoseAtIndex(++poseIndex); 570 571 if (!pose) { 572 error = B_ENTRY_NOT_FOUND; 573 break; 574 } 575 576 if (pose->IsSelected()) { 577 reply->AddRef("result", pose->TargetModel()->EntryRef()); 578 reply->AddInt32("index", IndexOfPose(pose)); 579 break; 580 } 581 } 582 583 handled = true; 584 break; 585 } 586 } 587 } else if (strcmp(property, kPropertyEntry) == 0) { 588 int32 count = fPoseList->CountItems(); 589 switch (form) { 590 case B_DIRECT_SPECIFIER: 591 // return all entries of all poses in PoseView 592 for (int32 index = 0; index < count; index++) 593 reply->AddRef("result", PoseAtIndex(index)->TargetModel()->EntryRef()); 594 595 handled = true; 596 break; 597 case B_INDEX_SPECIFIER: 598 { 599 // return entry at index 600 int32 index; 601 if (specifier->FindInt32("index", &index) != B_OK) 602 break; 603 604 if (!PoseAtIndex(index)) { 605 error = B_BAD_INDEX; 606 handled = true; 607 break; 608 } 609 reply->AddRef("result", PoseAtIndex(index)->TargetModel()->EntryRef()); 610 611 handled = true; 612 break; 613 } 614 case kPreviousSpecifier: 615 case kNextSpecifier: 616 { 617 // return entry and index of pose before or after specified pose 618 entry_ref ref; 619 if (specifier->FindRef("data", &ref) != B_OK) 620 break; 621 622 int32 tmp; 623 BPose *pose = FindPose(&ref, form, &tmp); 624 625 if (!pose) { 626 error = B_ENTRY_NOT_FOUND; 627 handled = true; 628 break; 629 } 630 631 reply->AddRef("result", pose->TargetModel()->EntryRef()); 632 reply->AddInt32("index", IndexOfPose(pose)); 633 634 handled = true; 635 break; 636 } 637 } 638 } 639 640 if (error != B_OK) 641 reply->AddInt32("error", error); 642 643 return handled; 644 #else 645 return false; 646 #endif 647 } 648 649 bool 650 BPoseView::SetProperty(BMessage *_SCRIPTING_ONLY(message), BMessage *, 651 int32 _SCRIPTING_ONLY(form), const char *_SCRIPTING_ONLY(property), 652 BMessage *_SCRIPTING_ONLY(reply)) 653 { 654 #if _SUPPORTS_FEATURE_SCRIPTING 655 status_t error = B_OK; 656 bool handled = false; 657 658 if (strcmp(property, kPropertySelection) == 0) { 659 entry_ref ref; 660 661 switch (form) { 662 case B_DIRECT_SPECIFIER: 663 { 664 int32 selStart; 665 int32 selEnd; 666 if (message->FindInt32("data", 0, &selStart) == B_OK 667 && message->FindInt32("data", 1, &selEnd) == B_OK) { 668 669 if (selStart < 0 || selStart >= fPoseList->CountItems() 670 || selEnd < 0 || selEnd >= fPoseList->CountItems()) { 671 error = B_BAD_INDEX; 672 handled = true; 673 break; 674 } 675 676 SelectPoses(selStart, selEnd); 677 handled = true; 678 break; 679 } 680 } 681 // fall thru 682 case kPreviousSpecifier: 683 case kNextSpecifier: 684 { 685 // PRINT(("SetProperty direct/previous/next %s\n", property)); 686 // select/unselect poses specified by entries 687 bool clearSelection = true; 688 for (int32 index = 0; message->FindRef("data", index, &ref) 689 == B_OK; index++) { 690 691 int32 poseIndex; 692 BPose *pose = FindPose(&ref, form, &poseIndex); 693 694 if (!pose) { 695 error = B_ENTRY_NOT_FOUND; 696 handled = true; 697 break; 698 } 699 700 if (clearSelection) { 701 // first selected item must call SelectPose so the selection 702 // gets cleared first 703 SelectPose(pose, poseIndex); 704 clearSelection = false; 705 } else 706 AddPoseToSelection(pose, poseIndex); 707 708 handled = true; 709 } 710 break; 711 } 712 } 713 } 714 715 if (error != B_OK) 716 reply->AddInt32("error", error); 717 718 return handled; 719 #else 720 return false; 721 #endif 722 } 723 724 BHandler * 725 BPoseView::ResolveSpecifier(BMessage *_SCRIPTING_ONLY(message), 726 int32 _SCRIPTING_ONLY(index), BMessage *_SCRIPTING_ONLY(specifier), 727 int32 _SCRIPTING_ONLY(form), const char *_SCRIPTING_ONLY(property)) 728 { 729 #if _SUPPORTS_FEATURE_SCRIPTING 730 BPropertyInfo propertyInfo(const_cast<property_info *>(kPosesPropertyList)); 731 732 int32 result = propertyInfo.FindMatch(message, index, specifier, form, property); 733 if (result < 0) { 734 // PRINT(("FindMatch result %d \n")); 735 return _inherited::ResolveSpecifier(message, index, specifier, 736 form, property); 737 } 738 739 return this; 740 #else 741 return NULL; 742 #endif 743 } 744 745 BPose * 746 BPoseView::FindPose(const entry_ref *_SCRIPTING_ONLY(ref), 747 int32 _SCRIPTING_ONLY(specifierForm), int32 *_SCRIPTING_ONLY(index)) const 748 { 749 #if _SUPPORTS_FEATURE_SCRIPTING 750 // flavor of FindPose, used by previous/next specifiers 751 752 BPose *pose = FindPose(ref, index); 753 754 if (specifierForm == (int32)kPreviousSpecifier) 755 return PoseAtIndex(--*index); 756 else if (specifierForm == (int32)kNextSpecifier) 757 return PoseAtIndex(++*index); 758 else 759 return pose; 760 #else 761 return NULL; 762 #endif 763 } 764 765