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