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
36#include "RecentItems.h"
37
38#include <Roster.h>
39
40#include "Attributes.h"
41#include "IconMenuItem.h"
42#include "Model.h"
43#include "NavMenu.h"
44#include "PoseView.h"
45#include "SlowMenu.h"
46#include "Tracker.h"
47#include "Utilities.h"
48
49
50class RecentItemsMenu : public BSlowMenu {
51public:
52	RecentItemsMenu(const char* title, BMessage* openMessage,
53		BHandler* itemTarget, int32 maxItems)
54		:	BSlowMenu(title),
55			fTargetMesage(openMessage),
56			fItemTarget(itemTarget),
57			fMaxCount(maxItems)
58		{}
59	virtual ~RecentItemsMenu();
60
61	virtual bool StartBuildingItemList();
62	virtual bool AddNextItem();
63	virtual void DoneBuildingItemList() {}
64	virtual void ClearMenuBuildingState();
65
66protected:
67	virtual const BMessage* FileMessage()
68		{ return fTargetMesage; }
69	virtual const BMessage* ContainerMessage()
70		{ return fTargetMesage; }
71
72	BRecentItemsList* fTterator;
73	BMessage* fTargetMesage;
74	BHandler* fItemTarget;
75	int32 fCount;
76	int32 fSanityCount;
77	int32 fMaxCount;
78};
79
80
81class RecentFilesMenu : public RecentItemsMenu {
82public:
83	RecentFilesMenu(const char* title, BMessage* openFileMessage,
84		BMessage* openFolderMessage, BHandler* target,
85		int32 maxItems, bool navMenuFolders, const char* ofType,
86		const char* openedByAppSig);
87
88	RecentFilesMenu(const char* title, BMessage* openFileMessage,
89		BMessage* openFolderMessage, BHandler* target,
90		int32 maxItems, bool navMenuFolders, const char* ofTypeList[],
91		int32 ofTypeListCount, const char* openedByAppSig);
92
93	virtual ~RecentFilesMenu();
94
95protected:
96	virtual const BMessage* ContainerMessage()
97		{ return openFolderMessage; }
98
99private:
100	BMessage* openFolderMessage;
101};
102
103
104class RecentFoldersMenu : public RecentItemsMenu {
105public:
106	RecentFoldersMenu(const char* title, BMessage* openMessage,
107		BHandler* target, int32 maxItems, bool navMenuFolders,
108		const char* openedByAppSig);
109};
110
111
112class RecentAppsMenu : public RecentItemsMenu {
113public:
114	RecentAppsMenu(const char* title, BMessage* openMessage,
115		BHandler* target, int32 maxItems);
116};
117
118
119// #pragma mark -
120
121
122RecentItemsMenu::~RecentItemsMenu()
123{
124	delete fTterator;
125	delete fTargetMesage;
126}
127
128
129bool
130RecentItemsMenu::AddNextItem()
131{
132	BMenuItem* item = fTterator->GetNextMenuItem(FileMessage(),
133		ContainerMessage(), fItemTarget);
134
135	if (item) {
136		AddItem(item);
137		fCount++;
138	}
139	fSanityCount++;
140
141	return fCount < fMaxCount - 1 && (fSanityCount < fMaxCount + 20);
142		// fSanityCount is a hacky way of dealing with a lot of stale
143		// recent apps
144}
145
146
147bool
148RecentItemsMenu::StartBuildingItemList()
149{
150	// remove any preexisting items
151	int32 itemCount = CountItems();
152	while (itemCount--)
153		delete RemoveItem((int32)0);
154
155	fCount = 0;
156	fSanityCount = 0;
157	fTterator->Rewind();
158	return true;
159}
160
161
162void
163RecentItemsMenu::ClearMenuBuildingState()
164{
165	fMenuBuilt = false;
166		// force rebuilding each time
167	fTterator->Rewind();
168}
169
170
171// #pragma mark -
172
173
174RecentFilesMenu::RecentFilesMenu(const char* title, BMessage* openFileMessage,
175	BMessage* openFolderMessage, BHandler* target, int32 maxItems,
176	bool navMenuFolders, const char* ofType, const char* openedByAppSig)
177	:
178	RecentItemsMenu(title, openFileMessage, target, maxItems),
179	openFolderMessage(openFolderMessage)
180{
181	fTterator = new BRecentFilesList(maxItems + 10, navMenuFolders,
182		ofType, openedByAppSig);
183}
184
185
186RecentFilesMenu::RecentFilesMenu(const char* title, BMessage* openFileMessage,
187	BMessage* openFolderMessage, BHandler* target, int32 maxItems,
188	bool navMenuFolders, const char* ofTypeList[], int32 ofTypeListCount,
189	const char* openedByAppSig)
190	:
191	RecentItemsMenu(title, openFileMessage, target, maxItems),
192	openFolderMessage(openFolderMessage)
193{
194	fTterator = new BRecentFilesList(maxItems + 10, navMenuFolders,
195		ofTypeList, ofTypeListCount, openedByAppSig);
196}
197
198
199RecentFilesMenu::~RecentFilesMenu()
200{
201	delete openFolderMessage;
202}
203
204
205// #pragma mark -
206
207
208RecentFoldersMenu::RecentFoldersMenu(const char* title, BMessage* openMessage,
209	BHandler* target, int32 maxItems, bool navMenuFolders,
210	const char* openedByAppSig)
211	:
212	RecentItemsMenu(title, openMessage, target, maxItems)
213{
214	fTterator = new BRecentFoldersList(maxItems + 10, navMenuFolders,
215		openedByAppSig);
216}
217
218
219// #pragma mark -
220
221
222RecentAppsMenu::RecentAppsMenu(const char* title, BMessage* openMessage,
223	BHandler* target, int32 maxItems)
224	:	RecentItemsMenu(title, openMessage, target, maxItems)
225{
226	fTterator = new BRecentAppsList(maxItems);
227}
228
229
230// #pragma mark -
231
232
233BRecentItemsList::BRecentItemsList(int32 maxItems, bool navMenuFolders)
234	:
235	fMaxItems(maxItems),
236	fNavMenuFolders(navMenuFolders)
237{
238	InitIconPreloader();
239		// need the icon cache
240	Rewind();
241}
242
243
244void
245BRecentItemsList::Rewind()
246{
247	fIndex = 0;
248	fItems.MakeEmpty();
249}
250
251
252BMenuItem*
253BRecentItemsList::GetNextMenuItem(const BMessage* fileOpenInvokeMessage,
254	const BMessage* containerOpenInvokeMessage, BHandler* target,
255	entry_ref* currentItemRef)
256{
257	entry_ref ref;
258	if (GetNextRef(&ref) != B_OK)
259		return NULL;
260
261	Model model(&ref, true);
262	if (model.InitCheck() != B_OK)
263		return NULL;
264
265	bool container = false;
266	if (model.IsSymLink()) {
267
268		Model* newResolvedModel = NULL;
269		Model* result = model.LinkTo();
270
271		if (!result) {
272			newResolvedModel = new Model(model.EntryRef(), true, true);
273
274			if (newResolvedModel->InitCheck() != B_OK) {
275				// broken link, still can show though, bail
276				delete newResolvedModel;
277				result = NULL;
278			} else
279				result = newResolvedModel;
280		}
281
282		if (result) {
283			BModelOpener opener(result);
284				// open the model, if it ain't open already
285
286			PoseInfo poseInfo;
287
288			if (result->Node()) {
289				result->Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0,
290					&poseInfo, sizeof(poseInfo));
291			}
292
293			result->CloseNode();
294
295			ref = *result->EntryRef();
296			container = result->IsContainer();
297		}
298		model.SetLinkTo(result);
299	} else {
300		ref = *model.EntryRef();
301		container = model.IsContainer();
302	}
303
304	// if user asked for it, return the current item ref
305	if (currentItemRef)
306		*currentItemRef = ref;
307
308	BMessage* message;
309	if (container && containerOpenInvokeMessage)
310		message = new BMessage(*containerOpenInvokeMessage);
311	else if (!container && fileOpenInvokeMessage)
312		message = new BMessage(*fileOpenInvokeMessage);
313	else
314		message = new BMessage(B_REFS_RECEIVED);
315
316	message->AddRef("refs", model.EntryRef());
317
318	// Truncate the name if necessary
319	BString truncatedString(model.Name());
320	be_plain_font->TruncateString(&truncatedString, B_TRUNCATE_END,
321		BNavMenu::GetMaxMenuWidth());
322
323	ModelMenuItem* item = NULL;
324	if (!container || !fNavMenuFolders)
325		item = new ModelMenuItem(&model, truncatedString.String(), message);
326	else {
327		// add another nav menu item if it's a directory
328		BNavMenu* menu = new BNavMenu(truncatedString.String(), message->what,
329			target, 0);
330
331		menu->SetNavDir(&ref);
332		item = new ModelMenuItem(&model, menu);
333		item->SetMessage(message);
334	}
335
336	if (item && target)
337		item->SetTarget(target);
338
339	return item;
340}
341
342
343status_t
344BRecentItemsList::GetNextRef(entry_ref* result)
345{
346	return fItems.FindRef("refs", fIndex++, result);
347}
348
349
350// #pragma mark -
351
352
353BRecentFilesList::BRecentFilesList(int32 maxItems, bool navMenuFolders,
354	const char* ofType, const char* openedByAppSig)
355	:
356	BRecentItemsList(maxItems, navMenuFolders),
357	fType(ofType),
358	fTypes(NULL),
359	fTypeCount(0),
360	fAppSig(openedByAppSig)
361{
362}
363
364
365BRecentFilesList::BRecentFilesList(int32 maxItems, bool navMenuFolders,
366	const char* ofTypeList[], int32 ofTypeListCount,
367	const char* openedByAppSig)
368	:
369	BRecentItemsList(maxItems, navMenuFolders),
370	fType(NULL),
371	fTypes(NULL),
372	fTypeCount(ofTypeListCount),
373	fAppSig(openedByAppSig)
374{
375	if (fTypeCount) {
376		fTypes = new char *[ofTypeListCount];
377		for (int32 index = 0; index < ofTypeListCount; index++)
378			fTypes[index] = strdup(ofTypeList[index]);
379	}
380}
381
382
383BRecentFilesList::~BRecentFilesList()
384{
385	if (fTypeCount) {
386		for (int32 index = 0; index < fTypeCount; index++)
387			free(fTypes[index]);
388		delete [] fTypes;
389	}
390}
391
392
393status_t
394BRecentFilesList::GetNextRef(entry_ref* ref)
395{
396	if (fIndex == 0) {
397		// Lazy roster Get
398		if (fTypes)
399			BRoster().GetRecentDocuments(&fItems, fMaxItems,
400				const_cast<const char**>(fTypes),
401				fTypeCount, fAppSig.Length() ? fAppSig.String() : NULL);
402		else
403			BRoster().GetRecentDocuments(&fItems, fMaxItems,
404				fType.Length() ? fType.String() : NULL,
405				fAppSig.Length() ? fAppSig.String() : NULL);
406
407	}
408	return BRecentItemsList::GetNextRef(ref);
409}
410
411
412BMenu*
413BRecentFilesList::NewFileListMenu(const char* title,
414	BMessage* openFileMessage, BMessage* openFolderMessage,
415	BHandler* target, int32 maxItems, bool navMenuFolders, const char* ofType,
416	const char* openedByAppSig)
417{
418	return new RecentFilesMenu(title, openFileMessage,
419		openFolderMessage, target, maxItems, navMenuFolders, ofType,
420		openedByAppSig);
421}
422
423
424BMenu*
425BRecentFilesList::NewFileListMenu(const char* title,
426	BMessage* openFileMessage, BMessage* openFolderMessage,
427	BHandler* target, int32 maxItems, bool navMenuFolders,
428	const char* ofTypeList[], int32 ofTypeListCount,
429	const char* openedByAppSig)
430{
431	return new RecentFilesMenu(title, openFileMessage,
432		openFolderMessage, target, maxItems, navMenuFolders, ofTypeList,
433		ofTypeListCount, openedByAppSig);
434}
435
436
437// #pragma mark -
438
439
440BMenu*
441BRecentFoldersList::NewFolderListMenu(const char* title,
442	BMessage* openMessage, BHandler* target, int32 maxItems,
443	bool navMenuFolders, const char* openedByAppSig)
444{
445	return new RecentFoldersMenu(title, openMessage, target, maxItems,
446		navMenuFolders, openedByAppSig);
447}
448
449
450BRecentFoldersList::BRecentFoldersList(int32 maxItems, bool navMenuFolders,
451	const char* openedByAppSig)
452	:
453	BRecentItemsList(maxItems, navMenuFolders),
454	fAppSig(openedByAppSig)
455{
456}
457
458
459status_t
460BRecentFoldersList::GetNextRef(entry_ref* ref)
461{
462	if (fIndex == 0) {
463		// Lazy roster Get
464		BRoster().GetRecentFolders(&fItems, fMaxItems,
465			fAppSig.Length() ? fAppSig.String() : NULL);
466
467	}
468	return BRecentItemsList::GetNextRef(ref);
469}
470
471
472// #pragma mark -
473
474
475BRecentAppsList::BRecentAppsList(int32 maxItems)
476	:
477	BRecentItemsList(maxItems, false)
478{
479}
480
481
482status_t
483BRecentAppsList::GetNextRef(entry_ref* ref)
484{
485	if (fIndex == 0) {
486		// Lazy roster Get
487		BRoster().GetRecentApps(&fItems, fMaxItems);
488	}
489	return BRecentItemsList::GetNextRef(ref);
490}
491
492
493BMenu*
494BRecentAppsList::NewAppListMenu(const char* title, BMessage* openMessage,
495	 BHandler* target, int32 maxItems)
496{
497	return new RecentAppsMenu(title, openMessage, target, maxItems);
498}
499