xref: /haiku/src/kits/tracker/PoseViewScripting.cpp (revision 1978089f7cec856677e46204e992c7273d70b9af)
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