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