1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All 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
67doo Tracker get Suites of Poses of Window test
68doo Tracker get Path of Poses of Window test
69doo Tracker count Entry of Poses of Window test
70doo Tracker get Entry of Poses of Window test
71doo Tracker get Entry 2 of Poses of Window test
72doo Tracker count Selection of Poses of Window test
73doo Tracker get Selection of Poses of Window test
74doo Tracker delete Entry 'test/6L6' of Poses of Window test
75doo Tracker execute Entry 'test/6L6' of Poses of Window test
76doo Tracker execute Entry 2 of Poses of Window test
77doo Tracker set Selection of Poses of Window test to [0,2]
78doo Tracker set Selection of Poses of Window test to 'test/KT55'
79doo Tracker create Selection of Poses of Window test to 'test/EL34'
80doo 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
90const 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
191status_t
192BPoseView::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
203bool
204BPoseView::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
268bool
269BPoseView::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
318bool
319BPoseView::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
374bool
375BPoseView::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
478bool
479BPoseView::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
498bool
499BPoseView::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
630bool
631BPoseView::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
698BHandler*
699BPoseView::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
717BPose*
718BPoseView::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