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 { NULL, 187 {}, 188 {}, 189 NULL, 0, 190 {}, 191 {}, 192 {} 193 } 194 }; 195 196 197 status_t 198 BPoseView::GetSupportedSuites(BMessage* data) 199 { 200 data->AddString("suites", kPosesSuites); 201 BPropertyInfo propertyInfo( 202 const_cast<property_info*>(kPosesPropertyList)); 203 data->AddFlat("messages", &propertyInfo); 204 205 return _inherited::GetSupportedSuites(data); 206 } 207 208 209 bool 210 BPoseView::HandleScriptingMessage(BMessage* message) 211 { 212 if (message->what != B_GET_PROPERTY 213 && message->what != B_SET_PROPERTY 214 && message->what != B_CREATE_PROPERTY 215 && message->what != B_COUNT_PROPERTIES 216 && message->what != B_DELETE_PROPERTY 217 && message->what != B_EXECUTE_PROPERTY) { 218 return false; 219 } 220 221 // dispatch scripting messages 222 BMessage reply(B_REPLY); 223 const char* property = 0; 224 bool handled = false; 225 226 int32 index = 0; 227 int32 form = 0; 228 BMessage specifier; 229 status_t result = message->GetCurrentSpecifier(&index, &specifier, 230 &form, &property); 231 232 if (result != B_OK || index == -1) 233 return false; 234 235 ASSERT(property != NULL); 236 237 switch (message->what) { 238 case B_CREATE_PROPERTY: 239 handled = CreateProperty(message, &specifier, form, property, 240 &reply); 241 break; 242 243 case B_GET_PROPERTY: 244 handled = GetProperty(&specifier, form, property, &reply); 245 break; 246 247 case B_SET_PROPERTY: 248 handled = SetProperty(message, &specifier, form, property, 249 &reply); 250 break; 251 252 case B_COUNT_PROPERTIES: 253 handled = CountProperty(&specifier, form, property, &reply); 254 break; 255 256 case B_DELETE_PROPERTY: 257 handled = DeleteProperty(&specifier, form, property, &reply); 258 break; 259 260 case B_EXECUTE_PROPERTY: 261 handled = ExecuteProperty(&specifier, form, property, &reply); 262 break; 263 } 264 265 if (handled) { 266 // done handling message, send a reply 267 message->SendReply(&reply); 268 } 269 270 return handled; 271 } 272 273 274 bool 275 BPoseView::ExecuteProperty(BMessage* specifier, int32 form, 276 const char* property, BMessage* reply) 277 { 278 status_t result = B_OK; 279 bool handled = false; 280 if (strcmp(property, kPropertyEntry) == 0) { 281 BMessage launchMessage(B_REFS_RECEIVED); 282 283 if (form == (int32)B_ENTRY_SPECIFIER) { 284 // move all poses specified by entry_ref to Trash 285 entry_ref ref; 286 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 287 == B_OK; index++) 288 launchMessage.AddRef("refs", &ref); 289 } else if (form == (int32)B_INDEX_SPECIFIER) { 290 // move all poses specified by index to Trash 291 int32 specifyingIndex; 292 for (int32 index = 0; specifier->FindInt32("index", index, 293 &specifyingIndex) == B_OK; index++) { 294 BPose* pose = PoseAtIndex(specifyingIndex); 295 296 if (pose == NULL) { 297 result = B_ENTRY_NOT_FOUND; 298 break; 299 } 300 301 launchMessage.AddRef("refs", pose->TargetModel()->EntryRef()); 302 } 303 } else 304 return false; 305 306 if (result == B_OK) { 307 // add a messenger to the launch message that will be used to 308 // dispatch scripting calls from apps to the PoseView 309 launchMessage.AddMessenger("TrackerViewToken", 310 BMessenger(this, 0, 0)); 311 if (fSelectionHandler) 312 fSelectionHandler->PostMessage(&launchMessage); 313 } 314 handled = true; 315 } 316 317 if (result != B_OK) 318 reply->AddInt32("error", result); 319 320 return handled; 321 } 322 323 324 bool 325 BPoseView::CreateProperty(BMessage* specifier, BMessage*, int32 form, 326 const char* property, BMessage* reply) 327 { 328 status_t result = B_OK; 329 bool handled = false; 330 if (strcmp(property, kPropertySelection) == 0) { 331 // creating on a selection expands the current selection 332 333 if (form != B_DIRECT_SPECIFIER) 334 // only support direct specifier 335 return false; 336 337 // items to add to a selection may be passed as refs or as indices 338 if (specifier->HasRef("data")) { 339 entry_ref ref; 340 // select poses specified by entries 341 for (int32 index = 0; specifier->FindRef("data", index, &ref) 342 == B_OK; index++) { 343 int32 poseIndex; 344 BPose* pose = FindPose(&ref, form, &poseIndex); 345 346 if (pose == NULL) { 347 result = B_ENTRY_NOT_FOUND; 348 handled = true; 349 break; 350 } 351 352 AddPoseToSelection(pose, poseIndex); 353 } 354 handled = true; 355 } else { 356 // select poses specified by indices 357 int32 specifyingIndex; 358 for (int32 index = 0; specifier->FindInt32("data", index, 359 &specifyingIndex) == B_OK; index++) { 360 BPose* pose = PoseAtIndex(specifyingIndex); 361 if (pose == NULL) { 362 result = B_BAD_INDEX; 363 handled = true; 364 break; 365 } 366 367 AddPoseToSelection(pose, specifyingIndex); 368 } 369 handled = true; 370 } 371 } 372 373 if (result != B_OK) 374 reply->AddInt32("error", result); 375 376 return handled; 377 } 378 379 380 bool 381 BPoseView::DeleteProperty(BMessage* specifier, int32 form, 382 const char* property, BMessage* reply) 383 { 384 status_t result = B_OK; 385 bool handled = false; 386 387 if (strcmp(property, kPropertySelection) == 0) { 388 // deleting on a selection is handled as removing a part of the 389 // selection not to be confused with deleting a selected item 390 391 if (form == (int32)B_ENTRY_SPECIFIER) { 392 entry_ref ref; 393 // select poses specified by entries 394 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 395 == B_OK; index++) { 396 int32 poseIndex; 397 BPose* pose = FindPose(&ref, form, &poseIndex); 398 399 if (pose == NULL) { 400 result = B_ENTRY_NOT_FOUND; 401 break; 402 } 403 404 RemovePoseFromSelection(pose); 405 } 406 handled = true; 407 408 } else if (form == B_INDEX_SPECIFIER) { 409 // move all poses specified by index to Trash 410 int32 specifyingIndex; 411 for (int32 index = 0; specifier->FindInt32("index", index, 412 &specifyingIndex) == B_OK; index++) { 413 BPose* pose = PoseAtIndex(specifyingIndex); 414 415 if (pose == NULL) { 416 result = B_BAD_INDEX; 417 break; 418 } 419 420 RemovePoseFromSelection(pose); 421 } 422 handled = true; 423 } else 424 return false; 425 426 } else if (strcmp(property, kPropertyEntry) == 0) { 427 // deleting entries is handled by moving entries to trash 428 429 // build a list of entries, specified by the specifier 430 BObjectList<entry_ref>* entryList = new BObjectList<entry_ref>(); 431 // list will be deleted for us by the trashing thread 432 433 if (form == (int32)B_ENTRY_SPECIFIER) { 434 // move all poses specified by entry_ref to Trash 435 entry_ref ref; 436 for (int32 index = 0; specifier->FindRef("refs", index, &ref) 437 == B_OK; index++) { 438 entryList->AddItem(new entry_ref(ref)); 439 } 440 } else if (form == (int32)B_INDEX_SPECIFIER) { 441 // move all poses specified by index to Trash 442 int32 specifyingIndex; 443 for (int32 index = 0; specifier->FindInt32("index", index, 444 &specifyingIndex) == B_OK; index++) { 445 BPose* pose = PoseAtIndex(specifyingIndex); 446 447 if (pose == NULL) { 448 result = B_BAD_INDEX; 449 break; 450 } 451 452 entryList->AddItem( 453 new entry_ref(*pose->TargetModel()->EntryRef())); 454 } 455 } else { 456 delete entryList; 457 return false; 458 } 459 460 if (result == B_OK) { 461 TrackerSettings settings; 462 if (!settings.DontMoveFilesToTrash()) { 463 // move the list we build into trash, don't make the 464 // trashing task select the next item 465 MoveListToTrash(entryList, false, false); 466 } else 467 Delete(entryList, false, settings.AskBeforeDeleteFile()); 468 } else { 469 for (int i = entryList->CountItems() - 1; i >= 0; i--) 470 delete entryList->ItemAt(i); 471 delete entryList; 472 } 473 474 handled = true; 475 } 476 477 if (result != B_OK) 478 reply->AddInt32("error", result); 479 480 return handled; 481 } 482 483 484 bool 485 BPoseView::CountProperty(BMessage*, int32, const char* property, 486 BMessage* reply) 487 { 488 bool handled = false; 489 //PRINT(("BPoseView::CountProperty, %s\n", property)); 490 491 // just return the respecitve counts 492 if (strcmp(property, kPropertySelection) == 0) { 493 reply->AddInt32("result", fSelectionList->CountItems()); 494 handled = true; 495 } else if (strcmp(property, kPropertyEntry) == 0) { 496 reply->AddInt32("result", fPoseList->CountItems()); 497 handled = true; 498 } 499 500 return handled; 501 } 502 503 504 bool 505 BPoseView::GetProperty(BMessage* specifier, int32 form, 506 const char* property, BMessage* reply) 507 { 508 // PRINT(("GetProperty %s\n", property)); 509 bool handled = false; 510 status_t result = B_OK; 511 512 if (strcmp(property, kPropertyPath) == 0) { 513 if (form == B_DIRECT_SPECIFIER) { 514 handled = true; 515 if (TargetModel() == NULL) 516 result = B_NOT_A_DIRECTORY; 517 else 518 reply->AddRef("result", TargetModel()->EntryRef()); 519 } 520 } else if (strcmp(property, kPropertySelection) == 0) { 521 int32 count = fSelectionList->CountItems(); 522 switch (form) { 523 case B_DIRECT_SPECIFIER: 524 // return entries of all poses in selection 525 for (int32 index = 0; index < count; index++) { 526 reply->AddRef("result", fSelectionList->ItemAt(index)-> 527 TargetModel()->EntryRef()); 528 } 529 530 handled = true; 531 break; 532 533 case kPreviousSpecifier: 534 case kNextSpecifier: 535 { 536 // return entry and index of selected pose before or after 537 // specified pose 538 entry_ref ref; 539 if (specifier->FindRef("data", &ref) != B_OK) 540 break; 541 542 int32 poseIndex; 543 BPose* pose = FindPose(&ref, &poseIndex); 544 545 for (;;) { 546 if (form == (int32)kPreviousSpecifier) 547 pose = PoseAtIndex(--poseIndex); 548 else if (form == (int32)kNextSpecifier) 549 pose = PoseAtIndex(++poseIndex); 550 551 if (pose == NULL) { 552 result = B_ENTRY_NOT_FOUND; 553 break; 554 } 555 556 if (pose->IsSelected()) { 557 reply->AddRef("result", 558 pose->TargetModel()->EntryRef()); 559 reply->AddInt32("index", IndexOfPose(pose)); 560 break; 561 } 562 } 563 564 handled = true; 565 break; 566 } 567 } 568 } else if (strcmp(property, kPropertyEntry) == 0) { 569 int32 count = fPoseList->CountItems(); 570 switch (form) { 571 case B_DIRECT_SPECIFIER: 572 { 573 // return all entries of all poses in PoseView 574 for (int32 index = 0; index < count; index++) { 575 reply->AddRef("result", 576 PoseAtIndex(index)->TargetModel()->EntryRef()); 577 } 578 579 handled = true; 580 break; 581 } 582 583 case B_INDEX_SPECIFIER: 584 { 585 // return entry at index 586 int32 index; 587 if (specifier->FindInt32("index", &index) != B_OK) 588 break; 589 590 if (!PoseAtIndex(index)) { 591 result = B_BAD_INDEX; 592 handled = true; 593 break; 594 } 595 reply->AddRef("result", 596 PoseAtIndex(index)->TargetModel()->EntryRef()); 597 598 handled = true; 599 break; 600 } 601 602 case kPreviousSpecifier: 603 case kNextSpecifier: 604 { 605 // return entry and index of pose before or after 606 // specified pose 607 entry_ref ref; 608 if (specifier->FindRef("data", &ref) != B_OK) 609 break; 610 611 int32 tmp; 612 BPose* pose = FindPose(&ref, form, &tmp); 613 614 if (pose == NULL) { 615 result = B_ENTRY_NOT_FOUND; 616 handled = true; 617 break; 618 } 619 620 reply->AddRef("result", pose->TargetModel()->EntryRef()); 621 reply->AddInt32("index", IndexOfPose(pose)); 622 623 handled = true; 624 break; 625 } 626 } 627 } 628 629 if (result != B_OK) 630 reply->AddInt32("error", result); 631 632 return handled; 633 } 634 635 636 bool 637 BPoseView::SetProperty(BMessage* message, BMessage*, int32 form, 638 const char* property, BMessage* reply) 639 { 640 status_t result = B_OK; 641 bool handled = false; 642 643 if (strcmp(property, kPropertySelection) == 0) { 644 entry_ref ref; 645 646 switch (form) { 647 case B_DIRECT_SPECIFIER: 648 { 649 int32 selStart; 650 int32 selEnd; 651 if (message->FindInt32("data", 0, &selStart) == B_OK 652 && message->FindInt32("data", 1, &selEnd) == B_OK) { 653 if (selStart < 0 || selStart >= fPoseList->CountItems() 654 || selEnd < 0 || selEnd >= fPoseList->CountItems()) { 655 result = B_BAD_INDEX; 656 handled = true; 657 break; 658 } 659 660 SelectPoses(selStart, selEnd); 661 handled = true; 662 break; 663 } 664 } // fall thru 665 case kPreviousSpecifier: 666 case kNextSpecifier: 667 { 668 // PRINT(("SetProperty direct/previous/next %s\n", property)); 669 // select/unselect poses specified by entries 670 bool clearSelection = true; 671 for (int32 index = 0; message->FindRef("data", index, &ref) 672 == B_OK; index++) { 673 int32 poseIndex; 674 BPose* pose = FindPose(&ref, form, &poseIndex); 675 676 if (pose == NULL) { 677 result = B_ENTRY_NOT_FOUND; 678 handled = true; 679 break; 680 } 681 682 if (clearSelection) { 683 // first selected item must call SelectPose so the 684 // selection gets cleared first 685 SelectPose(pose, poseIndex); 686 clearSelection = false; 687 } else 688 AddPoseToSelection(pose, poseIndex); 689 690 handled = true; 691 } 692 break; 693 } 694 } 695 } 696 697 if (result != B_OK) 698 reply->AddInt32("error", result); 699 700 return handled; 701 } 702 703 704 BHandler* 705 BPoseView::ResolveSpecifier(BMessage* message, int32 index, 706 BMessage* specifier, int32 form, const char* property) 707 { 708 BPropertyInfo propertyInfo( 709 const_cast<property_info*>(kPosesPropertyList)); 710 711 int32 result = propertyInfo.FindMatch(message, index, specifier, form, 712 property); 713 if (result < 0) { 714 //PRINT(("FindMatch result %d \n")); 715 return _inherited::ResolveSpecifier(message, index, specifier, 716 form, property); 717 } 718 719 return this; 720 } 721 722 723 BPose* 724 BPoseView::FindPose(const entry_ref* ref, int32 specifierForm, 725 int32* index) const 726 { 727 // flavor of FindPose, used by previous/next specifiers 728 729 BPose* pose = FindPose(ref, index); 730 731 if (specifierForm == (int32)kPreviousSpecifier) 732 return PoseAtIndex(--*index); 733 else if (specifierForm == (int32)kNextSpecifier) 734 return PoseAtIndex(++*index); 735 else 736 return pose; 737 } 738