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 TrackerSettings settings; 456 if (!settings.DontMoveFilesToTrash()) { 457 // move the list we build into trash, don't make the 458 // trashing task select the next item 459 MoveListToTrash(entryList, false, false); 460 } else 461 Delete(entryList, false, settings.AskBeforeDeleteFile()); 462 } else { 463 for (int i = entryList->CountItems() - 1; i >= 0; i--) 464 delete entryList->ItemAt(i); 465 delete entryList; 466 } 467 468 handled = true; 469 } 470 471 if (result != B_OK) 472 reply->AddInt32("error", result); 473 474 return handled; 475 } 476 477 478 bool 479 BPoseView::CountProperty(BMessage*, int32, const char* property, 480 BMessage* reply) 481 { 482 bool handled = false; 483 //PRINT(("BPoseView::CountProperty, %s\n", property)); 484 485 // just return the respecitve counts 486 if (strcmp(property, kPropertySelection) == 0) { 487 reply->AddInt32("result", fSelectionList->CountItems()); 488 handled = true; 489 } else if (strcmp(property, kPropertyEntry) == 0) { 490 reply->AddInt32("result", fPoseList->CountItems()); 491 handled = true; 492 } 493 494 return handled; 495 } 496 497 498 bool 499 BPoseView::GetProperty(BMessage* specifier, int32 form, 500 const char* property, BMessage* reply) 501 { 502 // PRINT(("GetProperty %s\n", property)); 503 bool handled = false; 504 status_t result = B_OK; 505 506 if (strcmp(property, kPropertyPath) == 0) { 507 if (form == B_DIRECT_SPECIFIER) { 508 handled = true; 509 if (TargetModel() == NULL) 510 result = B_NOT_A_DIRECTORY; 511 else 512 reply->AddRef("result", TargetModel()->EntryRef()); 513 } 514 } else if (strcmp(property, kPropertySelection) == 0) { 515 int32 count = fSelectionList->CountItems(); 516 switch (form) { 517 case B_DIRECT_SPECIFIER: 518 // return entries of all poses in selection 519 for (int32 index = 0; index < count; index++) { 520 reply->AddRef("result", fSelectionList->ItemAt(index)-> 521 TargetModel()->EntryRef()); 522 } 523 524 handled = true; 525 break; 526 527 case kPreviousSpecifier: 528 case kNextSpecifier: 529 { 530 // return entry and index of selected pose before or after 531 // specified pose 532 entry_ref ref; 533 if (specifier->FindRef("data", &ref) != B_OK) 534 break; 535 536 int32 poseIndex; 537 BPose* pose = FindPose(&ref, &poseIndex); 538 539 for (;;) { 540 if (form == (int32)kPreviousSpecifier) 541 pose = PoseAtIndex(--poseIndex); 542 else if (form == (int32)kNextSpecifier) 543 pose = PoseAtIndex(++poseIndex); 544 545 if (pose == NULL) { 546 result = B_ENTRY_NOT_FOUND; 547 break; 548 } 549 550 if (pose->IsSelected()) { 551 reply->AddRef("result", 552 pose->TargetModel()->EntryRef()); 553 reply->AddInt32("index", IndexOfPose(pose)); 554 break; 555 } 556 } 557 558 handled = true; 559 break; 560 } 561 } 562 } else if (strcmp(property, kPropertyEntry) == 0) { 563 int32 count = fPoseList->CountItems(); 564 switch (form) { 565 case B_DIRECT_SPECIFIER: 566 { 567 // return all entries of all poses in PoseView 568 for (int32 index = 0; index < count; index++) { 569 reply->AddRef("result", 570 PoseAtIndex(index)->TargetModel()->EntryRef()); 571 } 572 573 handled = true; 574 break; 575 } 576 577 case B_INDEX_SPECIFIER: 578 { 579 // return entry at index 580 int32 index; 581 if (specifier->FindInt32("index", &index) != B_OK) 582 break; 583 584 if (!PoseAtIndex(index)) { 585 result = B_BAD_INDEX; 586 handled = true; 587 break; 588 } 589 reply->AddRef("result", 590 PoseAtIndex(index)->TargetModel()->EntryRef()); 591 592 handled = true; 593 break; 594 } 595 596 case kPreviousSpecifier: 597 case kNextSpecifier: 598 { 599 // return entry and index of pose before or after 600 // specified pose 601 entry_ref ref; 602 if (specifier->FindRef("data", &ref) != B_OK) 603 break; 604 605 int32 tmp; 606 BPose* pose = FindPose(&ref, form, &tmp); 607 608 if (pose == NULL) { 609 result = B_ENTRY_NOT_FOUND; 610 handled = true; 611 break; 612 } 613 614 reply->AddRef("result", pose->TargetModel()->EntryRef()); 615 reply->AddInt32("index", IndexOfPose(pose)); 616 617 handled = true; 618 break; 619 } 620 } 621 } 622 623 if (result != B_OK) 624 reply->AddInt32("error", result); 625 626 return handled; 627 } 628 629 630 bool 631 BPoseView::SetProperty(BMessage* message, BMessage*, int32 form, 632 const char* property, BMessage* reply) 633 { 634 status_t result = B_OK; 635 bool handled = false; 636 637 if (strcmp(property, kPropertySelection) == 0) { 638 entry_ref ref; 639 640 switch (form) { 641 case B_DIRECT_SPECIFIER: 642 { 643 int32 selStart; 644 int32 selEnd; 645 if (message->FindInt32("data", 0, &selStart) == B_OK 646 && message->FindInt32("data", 1, &selEnd) == B_OK) { 647 if (selStart < 0 || selStart >= fPoseList->CountItems() 648 || selEnd < 0 || selEnd >= fPoseList->CountItems()) { 649 result = B_BAD_INDEX; 650 handled = true; 651 break; 652 } 653 654 SelectPoses(selStart, selEnd); 655 handled = true; 656 break; 657 } 658 } // fall thru 659 case kPreviousSpecifier: 660 case kNextSpecifier: 661 { 662 // PRINT(("SetProperty direct/previous/next %s\n", property)); 663 // select/unselect poses specified by entries 664 bool clearSelection = true; 665 for (int32 index = 0; message->FindRef("data", index, &ref) 666 == B_OK; index++) { 667 int32 poseIndex; 668 BPose* pose = FindPose(&ref, form, &poseIndex); 669 670 if (pose == NULL) { 671 result = B_ENTRY_NOT_FOUND; 672 handled = true; 673 break; 674 } 675 676 if (clearSelection) { 677 // first selected item must call SelectPose so the 678 // selection gets cleared first 679 SelectPose(pose, poseIndex); 680 clearSelection = false; 681 } else 682 AddPoseToSelection(pose, poseIndex); 683 684 handled = true; 685 } 686 break; 687 } 688 } 689 } 690 691 if (result != B_OK) 692 reply->AddInt32("error", result); 693 694 return handled; 695 } 696 697 698 BHandler* 699 BPoseView::ResolveSpecifier(BMessage* message, int32 index, 700 BMessage* specifier, int32 form, const char* property) 701 { 702 BPropertyInfo propertyInfo( 703 const_cast<property_info*>(kPosesPropertyList)); 704 705 int32 result = propertyInfo.FindMatch(message, index, specifier, form, 706 property); 707 if (result < 0) { 708 //PRINT(("FindMatch result %d \n")); 709 return _inherited::ResolveSpecifier(message, index, specifier, 710 form, property); 711 } 712 713 return this; 714 } 715 716 717 BPose* 718 BPoseView::FindPose(const entry_ref* ref, int32 specifierForm, 719 int32* index) const 720 { 721 // flavor of FindPose, used by previous/next specifiers 722 723 BPose* pose = FindPose(ref, index); 724 725 if (specifierForm == (int32)kPreviousSpecifier) 726 return PoseAtIndex(--*index); 727 else if (specifierForm == (int32)kNextSpecifier) 728 return PoseAtIndex(++*index); 729 else 730 return pose; 731 } 732