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 // notes on PoseView scripting interface: 54 // Indices and entry_refs are used to specify poses; In the case of indices 55 // and previous/next specifiers the current PoseView sort order is used. 56 // If PoseView is not in list view mode, the order in which poses are indexed 57 // is arbitrary. 58 // Both of these specifiers, but indices more so, are likely to be accurate 59 // only untill a next change to the PoseView (a change may be adding, 60 // removing a pose, changing an attribute or stat resulting in a sort ordering 61 // change, changing the sort ordering rule. When getting a selected item, 62 // there is no guarantee that the item will still be selected after the 63 // operation. The client must be able to deal with these inaccuracies. 64 // Specifying an index/entry_ref that no longer exists will be handled well. 65 66 #if 0 67 doo Tracker get Suites of Poses of Window test 68 doo Tracker get Path of Poses of Window test 69 doo Tracker count Entry of Poses of Window test 70 doo Tracker get Entry of Poses of Window test 71 doo Tracker get Entry 2 of Poses of Window test 72 doo Tracker count Selection of Poses of Window test 73 doo Tracker get Selection of Poses of Window test 74 doo Tracker delete Entry 'test/6L6' of Poses of Window test 75 doo Tracker execute Entry 'test/6L6' of Poses of Window test 76 doo Tracker execute Entry 2 of Poses of Window test 77 doo Tracker set Selection of Poses of Window test to [0,2] 78 doo Tracker set Selection of Poses of Window test to 'test/KT55' 79 doo Tracker create Selection of Poses of Window test to 'test/EL34' 80 doo Tracker delete Selection 'test/EL34' of Poses of Window test 81 #endif 82 83 // ToDo: 84 // access list view column state 85 // access poses 86 // - pose location 87 // - pose text widgets 88 89 90 const property_info kPosesPropertyList[] = { 91 { kPropertyPath, 92 { B_GET_PROPERTY }, 93 { B_DIRECT_SPECIFIER }, 94 "get Path of ... # returns the path of a Tracker window, " 95 "error if no path associated", 96 0, 97 { B_REF_TYPE }, 98 {}, 99 {} 100 }, 101 { kPropertyEntry, 102 { B_COUNT_PROPERTIES }, 103 { B_DIRECT_SPECIFIER }, 104 "count Entry of ... # count entries in a PoseView", 105 0, 106 { B_INT32_TYPE }, 107 {}, 108 {} 109 }, 110 { kPropertyEntry, 111 { B_DELETE_PROPERTY }, 112 { B_ENTRY_SPECIFIER, B_INDEX_SPECIFIER }, 113 "delete Entry {path|index} # deletes specified entries in a PoseView", 114 0, 115 {}, 116 {}, 117 {} 118 }, 119 { kPropertyEntry, 120 { B_GET_PROPERTY }, 121 { B_DIRECT_SPECIFIER, B_INDEX_SPECIFIER, kPreviousSpecifier, 122 kNextSpecifier }, 123 "get Entry [next|previous|index] # returns specified entries", 124 0, 125 { B_REF_TYPE }, 126 {}, 127 {} 128 }, 129 { kPropertyEntry, 130 { B_EXECUTE_PROPERTY }, 131 { B_ENTRY_SPECIFIER, B_INDEX_SPECIFIER }, 132 "execute Entry {path|index} # opens specified entries", 133 0, 134 { B_REF_TYPE }, 135 {}, 136 {} 137 }, 138 { kPropertySelection, 139 { B_GET_PROPERTY }, 140 { B_DIRECT_SPECIFIER, kPreviousSpecifier, kNextSpecifier }, 141 "get Selection [next|previous] # returns the selected entries", 142 0, 143 { B_REF_TYPE }, 144 {}, 145 {} 146 }, 147 { kPropertySelection, 148 { B_SET_PROPERTY }, 149 { B_DIRECT_SPECIFIER, kPreviousSpecifier, kNextSpecifier }, 150 "set Selection of ... to {next|previous|entry} # selects specified " 151 "entries", 152 0, 153 {}, 154 {}, 155 {} 156 }, 157 { kPropertySelection, 158 { B_COUNT_PROPERTIES }, 159 { B_DIRECT_SPECIFIER }, 160 "count Selection of ... # counts selected items", 161 0, 162 { B_INT32_TYPE }, 163 {}, 164 {} 165 }, 166 { kPropertySelection, 167 { B_CREATE_PROPERTY }, 168 { B_DIRECT_SPECIFIER }, 169 "create selection of ... to {entry|index} " 170 "# adds specified items to a selection in a PoseView", 171 0, 172 {}, 173 {}, 174 {} 175 }, 176 { kPropertySelection, 177 { B_DELETE_PROPERTY }, 178 { B_ENTRY_SPECIFIER, B_INDEX_SPECIFIER }, 179 "delete selection {path|index} of ... " 180 "# removes specified items from a selection in a PoseView", 181 0, 182 {}, 183 {}, 184 {} 185 }, 186 187 { 0 } 188 }; 189 190 191 status_t 192 BPoseView::GetSupportedSuites(BMessage* data) 193 { 194 data->AddString("suites", kPosesSuites); 195 BPropertyInfo propertyInfo( 196 const_cast<property_info*>(kPosesPropertyList)); 197 data->AddFlat("messages", &propertyInfo); 198 199 return _inherited::GetSupportedSuites(data); 200 } 201 202 203 bool 204 BPoseView::HandleScriptingMessage(BMessage* message) 205 { 206 if (message->what != B_GET_PROPERTY 207 && message->what != B_SET_PROPERTY 208 && message->what != B_CREATE_PROPERTY 209 && message->what != B_COUNT_PROPERTIES 210 && message->what != B_DELETE_PROPERTY 211 && message->what != B_EXECUTE_PROPERTY) { 212 return false; 213 } 214 215 // dispatch scripting messages 216 BMessage reply(B_REPLY); 217 const char* property = 0; 218 bool handled = false; 219 220 int32 index = 0; 221 int32 form = 0; 222 BMessage specifier; 223 status_t result = message->GetCurrentSpecifier(&index, &specifier, 224 &form, &property); 225 226 if (result != B_OK || index == -1) 227 return false; 228 229 ASSERT(property != NULL); 230 231 switch (message->what) { 232 case B_CREATE_PROPERTY: 233 handled = CreateProperty(message, &specifier, form, property, 234 &reply); 235 break; 236 237 case B_GET_PROPERTY: 238 handled = GetProperty(&specifier, form, property, &reply); 239 break; 240 241 case B_SET_PROPERTY: 242 handled = SetProperty(message, &specifier, form, property, 243 &reply); 244 break; 245 246 case B_COUNT_PROPERTIES: 247 handled = CountProperty(&specifier, form, property, &reply); 248 break; 249 250 case B_DELETE_PROPERTY: 251 handled = DeleteProperty(&specifier, form, property, &reply); 252 break; 253 254 case B_EXECUTE_PROPERTY: 255 handled = ExecuteProperty(&specifier, form, property, &reply); 256 break; 257 } 258 259 if (handled) { 260 // done handling message, send a reply 261 message->SendReply(&reply); 262 } 263 264 return handled; 265 } 266 267 268 bool 269 BPoseView::ExecuteProperty(BMessage* specifier, int32 form, 270 const char* property, BMessage* reply) 271 { 272 status_t result = B_OK; 273 bool handled = false; 274 if (strcmp(property, kPropertyEntry) == 0) { 275 BMessage launchMessage(B_REFS_RECEIVED); 276 277 if (form == (int32)B_ENTRY_SPECIFIER) { 278 // move all poses specified by entry_ref to Trash 279 entry_ref ref; 280 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 281 == B_OK; index++) 282 launchMessage.AddRef("refs", &ref); 283 } else if (form == (int32)B_INDEX_SPECIFIER) { 284 // move all poses specified by index to Trash 285 int32 specifyingIndex; 286 for (int32 index = 0; specifier->FindInt32("index", index, 287 &specifyingIndex) == B_OK; index++) { 288 BPose* pose = PoseAtIndex(specifyingIndex); 289 290 if (pose == NULL) { 291 result = B_ENTRY_NOT_FOUND; 292 break; 293 } 294 295 launchMessage.AddRef("refs", pose->TargetModel()->EntryRef()); 296 } 297 } else 298 return false; 299 300 if (result == B_OK) { 301 // add a messenger to the launch message that will be used to 302 // dispatch scripting calls from apps to the PoseView 303 launchMessage.AddMessenger("TrackerViewToken", 304 BMessenger(this, 0, 0)); 305 if (fSelectionHandler) 306 fSelectionHandler->PostMessage(&launchMessage); 307 } 308 handled = true; 309 } 310 311 if (result != B_OK) 312 reply->AddInt32("error", result); 313 314 return handled; 315 } 316 317 318 bool 319 BPoseView::CreateProperty(BMessage* specifier, BMessage*, int32 form, 320 const char* property, BMessage* reply) 321 { 322 status_t result = B_OK; 323 bool handled = false; 324 if (strcmp(property, kPropertySelection) == 0) { 325 // creating on a selection expands the current selection 326 327 if (form != B_DIRECT_SPECIFIER) 328 // only support direct specifier 329 return false; 330 331 // items to add to a selection may be passed as refs or as indices 332 if (specifier->HasRef("data")) { 333 entry_ref ref; 334 // select poses specified by entries 335 for (int32 index = 0; specifier->FindRef("data", index, &ref) 336 == B_OK; index++) { 337 int32 poseIndex; 338 BPose* pose = FindPose(&ref, form, &poseIndex); 339 340 if (pose == NULL) { 341 result = B_ENTRY_NOT_FOUND; 342 handled = true; 343 break; 344 } 345 346 AddPoseToSelection(pose, poseIndex); 347 } 348 handled = true; 349 } else { 350 // select poses specified by indices 351 int32 specifyingIndex; 352 for (int32 index = 0; specifier->FindInt32("data", index, 353 &specifyingIndex) == B_OK; index++) { 354 BPose* pose = PoseAtIndex(specifyingIndex); 355 if (pose == NULL) { 356 result = B_BAD_INDEX; 357 handled = true; 358 break; 359 } 360 361 AddPoseToSelection(pose, specifyingIndex); 362 } 363 handled = true; 364 } 365 } 366 367 if (result != B_OK) 368 reply->AddInt32("error", result); 369 370 return handled; 371 } 372 373 374 bool 375 BPoseView::DeleteProperty(BMessage* specifier, int32 form, 376 const char* property, BMessage* reply) 377 { 378 status_t result = B_OK; 379 bool handled = false; 380 381 if (strcmp(property, kPropertySelection) == 0) { 382 // deleting on a selection is handled as removing a part of the 383 // selection not to be confused with deleting a selected item 384 385 if (form == (int32)B_ENTRY_SPECIFIER) { 386 entry_ref ref; 387 // select poses specified by entries 388 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 389 == B_OK; index++) { 390 int32 poseIndex; 391 BPose* pose = FindPose(&ref, form, &poseIndex); 392 393 if (pose == NULL) { 394 result = B_ENTRY_NOT_FOUND; 395 break; 396 } 397 398 RemovePoseFromSelection(pose); 399 } 400 handled = true; 401 402 } else if (form == B_INDEX_SPECIFIER) { 403 // move all poses specified by index to Trash 404 int32 specifyingIndex; 405 for (int32 index = 0; specifier->FindInt32("index", index, 406 &specifyingIndex) == B_OK; index++) { 407 BPose* pose = PoseAtIndex(specifyingIndex); 408 409 if (pose == NULL) { 410 result = B_BAD_INDEX; 411 break; 412 } 413 414 RemovePoseFromSelection(pose); 415 } 416 handled = true; 417 } else 418 return false; 419 420 } else if (strcmp(property, kPropertyEntry) == 0) { 421 // deleting entries is handled by moving entries to trash 422 423 // build a list of entries, specified by the specifier 424 BObjectList<entry_ref>* entryList = new BObjectList<entry_ref>(); 425 // list will be deleted for us by the trashing thread 426 427 if (form == (int32)B_ENTRY_SPECIFIER) { 428 // move all poses specified by entry_ref to Trash 429 entry_ref ref; 430 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 431 == B_OK; index++) { 432 entryList->AddItem(new entry_ref(ref)); 433 } 434 } else if (form == (int32)B_INDEX_SPECIFIER) { 435 // move all poses specified by index to Trash 436 int32 specifyingIndex; 437 for (int32 index = 0; specifier->FindInt32("index", index, 438 &specifyingIndex) == B_OK; index++) { 439 BPose* pose = PoseAtIndex(specifyingIndex); 440 441 if (pose == NULL) { 442 result = B_BAD_INDEX; 443 break; 444 } 445 446 entryList->AddItem( 447 new entry_ref(*pose->TargetModel()->EntryRef())); 448 } 449 } else { 450 delete entryList; 451 return false; 452 } 453 454 if (result == B_OK) 455 MoveListToTrash(entryList, false, false); 456 else { 457 for (int i = entryList->CountItems() - 1; i >= 0; i--) 458 delete entryList->ItemAt(i); 459 delete entryList; 460 } 461 462 handled = true; 463 } 464 465 if (result != B_OK) 466 reply->AddInt32("error", result); 467 468 return handled; 469 } 470 471 472 bool 473 BPoseView::CountProperty(BMessage*, int32, const char* property, 474 BMessage* reply) 475 { 476 bool handled = false; 477 //PRINT(("BPoseView::CountProperty, %s\n", property)); 478 479 // just return the respecitve counts 480 if (strcmp(property, kPropertySelection) == 0) { 481 reply->AddInt32("result", fSelectionList->CountItems()); 482 handled = true; 483 } else if (strcmp(property, kPropertyEntry) == 0) { 484 reply->AddInt32("result", fPoseList->CountItems()); 485 handled = true; 486 } 487 488 return handled; 489 } 490 491 492 bool 493 BPoseView::GetProperty(BMessage* specifier, int32 form, 494 const char* property, BMessage* reply) 495 { 496 // PRINT(("GetProperty %s\n", property)); 497 bool handled = false; 498 status_t result = B_OK; 499 500 if (strcmp(property, kPropertyPath) == 0) { 501 if (form == B_DIRECT_SPECIFIER) { 502 handled = true; 503 if (TargetModel() == NULL) 504 result = B_NOT_A_DIRECTORY; 505 else 506 reply->AddRef("result", TargetModel()->EntryRef()); 507 } 508 } else if (strcmp(property, kPropertySelection) == 0) { 509 int32 count = fSelectionList->CountItems(); 510 switch (form) { 511 case B_DIRECT_SPECIFIER: 512 // return entries of all poses in selection 513 for (int32 index = 0; index < count; index++) { 514 reply->AddRef("result", fSelectionList->ItemAt(index)-> 515 TargetModel()->EntryRef()); 516 } 517 518 handled = true; 519 break; 520 521 case kPreviousSpecifier: 522 case kNextSpecifier: 523 { 524 // return entry and index of selected pose before or after 525 // specified pose 526 entry_ref ref; 527 if (specifier->FindRef("data", &ref) != B_OK) 528 break; 529 530 int32 poseIndex; 531 BPose* pose = FindPose(&ref, &poseIndex); 532 533 for (;;) { 534 if (form == (int32)kPreviousSpecifier) 535 pose = PoseAtIndex(--poseIndex); 536 else if (form == (int32)kNextSpecifier) 537 pose = PoseAtIndex(++poseIndex); 538 539 if (pose == NULL) { 540 result = B_ENTRY_NOT_FOUND; 541 break; 542 } 543 544 if (pose->IsSelected()) { 545 reply->AddRef("result", 546 pose->TargetModel()->EntryRef()); 547 reply->AddInt32("index", IndexOfPose(pose)); 548 break; 549 } 550 } 551 552 handled = true; 553 break; 554 } 555 } 556 } else if (strcmp(property, kPropertyEntry) == 0) { 557 int32 count = fPoseList->CountItems(); 558 switch (form) { 559 case B_DIRECT_SPECIFIER: 560 { 561 // return all entries of all poses in PoseView 562 for (int32 index = 0; index < count; index++) { 563 reply->AddRef("result", 564 PoseAtIndex(index)->TargetModel()->EntryRef()); 565 } 566 567 handled = true; 568 break; 569 } 570 571 case B_INDEX_SPECIFIER: 572 { 573 // return entry at index 574 int32 index; 575 if (specifier->FindInt32("index", &index) != B_OK) 576 break; 577 578 if (!PoseAtIndex(index)) { 579 result = B_BAD_INDEX; 580 handled = true; 581 break; 582 } 583 reply->AddRef("result", 584 PoseAtIndex(index)->TargetModel()->EntryRef()); 585 586 handled = true; 587 break; 588 } 589 590 case kPreviousSpecifier: 591 case kNextSpecifier: 592 { 593 // return entry and index of pose before or after 594 // specified pose 595 entry_ref ref; 596 if (specifier->FindRef("data", &ref) != B_OK) 597 break; 598 599 int32 tmp; 600 BPose* pose = FindPose(&ref, form, &tmp); 601 602 if (pose == NULL) { 603 result = B_ENTRY_NOT_FOUND; 604 handled = true; 605 break; 606 } 607 608 reply->AddRef("result", pose->TargetModel()->EntryRef()); 609 reply->AddInt32("index", IndexOfPose(pose)); 610 611 handled = true; 612 break; 613 } 614 } 615 } 616 617 if (result != B_OK) 618 reply->AddInt32("error", result); 619 620 return handled; 621 } 622 623 624 bool 625 BPoseView::SetProperty(BMessage* message, BMessage*, int32 form, 626 const char* property, BMessage* reply) 627 { 628 status_t result = B_OK; 629 bool handled = false; 630 631 if (strcmp(property, kPropertySelection) == 0) { 632 entry_ref ref; 633 634 switch (form) { 635 case B_DIRECT_SPECIFIER: 636 { 637 int32 selStart; 638 int32 selEnd; 639 if (message->FindInt32("data", 0, &selStart) == B_OK 640 && message->FindInt32("data", 1, &selEnd) == B_OK) { 641 if (selStart < 0 || selStart >= fPoseList->CountItems() 642 || selEnd < 0 || selEnd >= fPoseList->CountItems()) { 643 result = B_BAD_INDEX; 644 handled = true; 645 break; 646 } 647 648 SelectPoses(selStart, selEnd); 649 handled = true; 650 break; 651 } 652 } // fall thru 653 case kPreviousSpecifier: 654 case kNextSpecifier: 655 { 656 // PRINT(("SetProperty direct/previous/next %s\n", property)); 657 // select/unselect poses specified by entries 658 bool clearSelection = true; 659 for (int32 index = 0; message->FindRef("data", index, &ref) 660 == B_OK; index++) { 661 int32 poseIndex; 662 BPose* pose = FindPose(&ref, form, &poseIndex); 663 664 if (pose == NULL) { 665 result = B_ENTRY_NOT_FOUND; 666 handled = true; 667 break; 668 } 669 670 if (clearSelection) { 671 // first selected item must call SelectPose so the 672 // selection gets cleared first 673 SelectPose(pose, poseIndex); 674 clearSelection = false; 675 } else 676 AddPoseToSelection(pose, poseIndex); 677 678 handled = true; 679 } 680 break; 681 } 682 } 683 } 684 685 if (result != B_OK) 686 reply->AddInt32("error", result); 687 688 return handled; 689 } 690 691 692 BHandler* 693 BPoseView::ResolveSpecifier(BMessage* message, int32 index, 694 BMessage* specifier, int32 form, const char* property) 695 { 696 BPropertyInfo propertyInfo( 697 const_cast<property_info*>(kPosesPropertyList)); 698 699 int32 result = propertyInfo.FindMatch(message, index, specifier, form, 700 property); 701 if (result < 0) { 702 //PRINT(("FindMatch result %d \n")); 703 return _inherited::ResolveSpecifier(message, index, specifier, 704 form, property); 705 } 706 707 return this; 708 } 709 710 711 BPose* 712 BPoseView::FindPose(const entry_ref* ref, int32 specifierForm, 713 int32* index) const 714 { 715 // flavor of FindPose, used by previous/next specifiers 716 717 BPose* pose = FindPose(ref, index); 718 719 if (specifierForm == (int32)kPreviousSpecifier) 720 return PoseAtIndex(--*index); 721 else if (specifierForm == (int32)kNextSpecifier) 722 return PoseAtIndex(++*index); 723 else 724 return pose; 725 } 726