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			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
472bool
473BPoseView::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
492bool
493BPoseView::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
624bool
625BPoseView::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
692BHandler*
693BPoseView::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
711BPose*
712BPoseView::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