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#include "FavoritesMenu.h"
36
37#include <compat/sys/stat.h>
38
39#include <Application.h>
40#include <Catalog.h>
41#include <FindDirectory.h>
42#include <FilePanel.h>
43#include <Locale.h>
44#include <Message.h>
45#include <Path.h>
46#include <Query.h>
47#include <Roster.h>
48
49#include <functional>
50#include <algorithm>
51
52#include "IconMenuItem.h"
53#include "NavMenu.h"
54#include "PoseView.h"
55#include "QueryPoseView.h"
56#include "Tracker.h"
57#include "Utilities.h"
58
59
60#undef B_TRANSLATION_CONTEXT
61#define B_TRANSLATION_CONTEXT "FavoritesMenu"
62
63FavoritesMenu::FavoritesMenu(const char* title, BMessage* openFolderMessage,
64	BMessage* openFileMessage, const BMessenger &target,
65	bool isSavePanel, BRefFilter* filter)
66	:	BSlowMenu(title),
67		fOpenFolderMessage(openFolderMessage),
68		fOpenFileMessage(openFileMessage),
69		fTarget(target),
70		fContainer(NULL),
71		fInitialItemCount(0),
72		fIsSavePanel(isSavePanel),
73		fRefFilter(filter)
74{
75}
76
77
78FavoritesMenu::~FavoritesMenu()
79{
80	delete fOpenFolderMessage;
81	delete fOpenFileMessage;
82	delete fContainer;
83}
84
85
86void
87FavoritesMenu::SetRefFilter(BRefFilter* filter)
88{
89	fRefFilter = filter;
90}
91
92
93bool
94FavoritesMenu::StartBuildingItemList()
95{
96	// initialize the menu building state
97
98	if (!fInitialItemCount)
99		fInitialItemCount = CountItems();
100	else {
101		// strip the old items so we can add new fresh ones
102		int32 count = CountItems() - fInitialItemCount;
103		// keep the items that were added by the FavoritesMenu creator
104		while (count--)
105			delete RemoveItem(fInitialItemCount);
106	}
107
108	fUniqueRefCheck.clear();
109	fState = kStart;
110	return true;
111}
112
113
114bool
115FavoritesMenu::AddNextItem()
116{
117	// run the next chunk of code for a given item adding state
118
119	if (fState == kStart) {
120		fState = kAddingFavorites;
121		fSectionItemCount = 0;
122		fAddedSeparatorForSection = false;
123		// set up adding the GoTo menu items
124
125		try {
126			BPath path;
127			ThrowOnError(find_directory(B_USER_SETTINGS_DIRECTORY,
128				&path, true));
129			path.Append(kGoDirectory);
130			mkdir(path.Path(), 0777);
131
132			BEntry entry(path.Path());
133			Model startModel(&entry, true);
134			ThrowOnInitCheckError(&startModel);
135
136			if (!startModel.IsContainer())
137				throw B_ERROR;
138
139			if (startModel.IsQuery())
140				fContainer = new QueryEntryListCollection(&startModel);
141			else
142				fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory*>
143					(startModel.Node()));
144
145			ThrowOnInitCheckError(fContainer);
146			ThrowOnError( fContainer->Rewind() );
147
148		} catch (...) {
149			delete fContainer;
150			fContainer = NULL;
151		}
152	}
153
154
155	if (fState == kAddingFavorites) {
156		entry_ref ref;
157		if (fContainer
158			&& fContainer->GetNextRef(&ref) == B_OK) {
159			Model model(&ref, true);
160			if (model.InitCheck() != B_OK)
161				return true;
162
163			if (!ShouldShowModel(&model))
164				return true;
165
166			BMenuItem* item = BNavMenu::NewModelItem(&model,
167				model.IsDirectory() ? fOpenFolderMessage : fOpenFileMessage,
168				fTarget);
169
170			if (item == NULL)
171				return true;
172
173			item->SetLabel(ref.name);
174				// this is the name of the link in the Go dir
175
176			if (!fAddedSeparatorForSection) {
177				fAddedSeparatorForSection = true;
178				AddItem(new TitledSeparatorItem(B_TRANSLATE("Favorites")));
179			}
180			fUniqueRefCheck.push_back(*model.EntryRef());
181			AddItem(item);
182			fSectionItemCount++;
183			return true;
184		}
185
186		// done with favorites, set up for adding recent files
187		fState = kAddingFiles;
188
189		fAddedSeparatorForSection = false;
190
191		app_info info;
192		be_app->GetAppInfo(&info);
193		fItems.MakeEmpty();
194
195		int32 apps, docs, folders;
196		TrackerSettings().RecentCounts(&apps, &docs, &folders);
197
198		BRoster().GetRecentDocuments(&fItems, docs, NULL, info.signature);
199		fIndex = 0;
200		fSectionItemCount = 0;
201	}
202
203	if (fState == kAddingFiles) {
204		//	if this is a Save panel, not an Open panel
205		//	then don't add the recent documents
206		if (!fIsSavePanel) {
207			for (;;) {
208				entry_ref ref;
209				if (fItems.FindRef("refs", fIndex++, &ref) != B_OK)
210					break;
211
212				Model model(&ref, true);
213				if (model.InitCheck() != B_OK)
214					return true;
215
216				if (!ShouldShowModel(&model))
217					return true;
218
219				BMenuItem* item = BNavMenu::NewModelItem(&model,
220					fOpenFileMessage, fTarget);
221				if (item) {
222					if (!fAddedSeparatorForSection) {
223						fAddedSeparatorForSection = true;
224						AddItem(new TitledSeparatorItem(
225							B_TRANSLATE("Recent documents")));
226					}
227					AddItem(item);
228					fSectionItemCount++;
229					return true;
230				}
231			}
232		}
233
234		// done with recent files, set up for adding recent folders
235		fState = kAddingFolders;
236
237		fAddedSeparatorForSection = false;
238
239		app_info info;
240		be_app->GetAppInfo(&info);
241		fItems.MakeEmpty();
242
243		int32 apps, docs, folders;
244		TrackerSettings().RecentCounts(&apps, &docs, &folders);
245
246		BRoster().GetRecentFolders(&fItems, folders, info.signature);
247		fIndex = 0;
248	}
249
250	if (fState == kAddingFolders) {
251		for (;;) {
252			entry_ref ref;
253			if (fItems.FindRef("refs", fIndex++, &ref) != B_OK)
254				break;
255
256			// don't add folders that are already in the GoTo section
257			if (find_if(fUniqueRefCheck.begin(), fUniqueRefCheck.end(),
258				bind2nd(std::equal_to<entry_ref>(), ref))
259					!= fUniqueRefCheck.end()) {
260				continue;
261			}
262
263			Model model(&ref, true);
264			if (model.InitCheck() != B_OK)
265				return true;
266
267			if (!ShouldShowModel(&model))
268				return true;
269
270			BMenuItem* item = BNavMenu::NewModelItem(&model,
271				fOpenFolderMessage, fTarget, true);
272			if (item) {
273				if (!fAddedSeparatorForSection) {
274					fAddedSeparatorForSection = true;
275					AddItem(new TitledSeparatorItem(
276						B_TRANSLATE("Recent folders")));
277				}
278				AddItem(item);
279				item->SetEnabled(true);
280					// BNavMenu::NewModelItem returns a disabled item here -
281					// need to fix this in BNavMenu::NewModelItem
282				return true;
283			}
284		}
285	}
286	return false;
287}
288
289
290void
291FavoritesMenu::DoneBuildingItemList()
292{
293	SetTargetForItems(fTarget);
294}
295
296
297void
298FavoritesMenu::ClearMenuBuildingState()
299{
300	delete fContainer;
301	fContainer = NULL;
302	fState = kDone;
303
304	// force the menu to get rebuilt each time
305	fMenuBuilt = false;
306}
307
308
309bool
310FavoritesMenu::ShouldShowModel(const Model* model)
311{
312	if (fIsSavePanel && model->IsFile())
313		return false;
314
315	if (!fRefFilter || model->Node() == NULL)
316		return true;
317
318	struct stat_beos statBeOS;
319	convert_to_stat_beos(model->StatBuf(), &statBeOS);
320
321	return fRefFilter->Filter(model->EntryRef(), model->Node(), &statBeOS,
322		model->MimeType());
323}
324
325
326//	#pragma mark -
327
328
329RecentsMenu::RecentsMenu(const char* name, int32 which, uint32 what,
330	BHandler* target)
331	: BNavMenu(name, what, target),
332	fWhich(which),
333	fRecentsCount(0),
334	fItemIndex(0)
335{
336	int32 applications;
337	int32 documents;
338	int32 folders;
339	TrackerSettings().RecentCounts(&applications,&documents,&folders);
340
341	if (fWhich == 0)
342		fRecentsCount = documents;
343	else if (fWhich == 1)
344		fRecentsCount = applications;
345	else if (fWhich == 2)
346		fRecentsCount = folders;
347}
348
349
350void
351RecentsMenu::DetachedFromWindow()
352{
353	//
354	//	BNavMenu::DetachedFromWindow sets the TypesList to NULL
355	//
356	BMenu::DetachedFromWindow();
357}
358
359
360bool
361RecentsMenu::StartBuildingItemList()
362{
363	int32 count = CountItems()-1;
364	for (int32 index = count; index >= 0; index--) {
365		BMenuItem* item = ItemAt(index);
366		ASSERT(item);
367
368		RemoveItem(index);
369		delete item;
370	}
371	//
372	//	!! note: don't call inherited from here
373	//	the navref is not set for this menu
374	//	but it still needs to be a draggable navmenu
375	//	simply return true so that AddNextItem is called
376	//
377	//	return BNavMenu::StartBuildingItemList();
378	return true;
379}
380
381
382bool
383RecentsMenu::AddNextItem()
384{
385	if (fRecentsCount > 0 && AddRecents(fRecentsCount))
386		return true;
387
388	fItemIndex = 0;
389	return false;
390}
391
392
393bool
394RecentsMenu::AddRecents(int32 count)
395{
396	if (fItemIndex == 0) {
397		fRecentList.MakeEmpty();
398		BRoster roster;
399
400		switch(fWhich) {
401			case 0:
402				roster.GetRecentDocuments(&fRecentList, count);
403				break;
404			case 1:
405				roster.GetRecentApps(&fRecentList, count);
406				break;
407			case 2:
408				roster.GetRecentFolders(&fRecentList, count);
409				break;
410			default:
411				return false;
412				break;
413		}
414	}
415	for (;;) {
416		entry_ref ref;
417		if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK)
418			break;
419
420		if (ref.name && strlen(ref.name) > 0) {
421			Model model(&ref, true);
422			ModelMenuItem* item = BNavMenu::NewModelItem(&model,
423					new BMessage(fMessage.what),
424					Target(), false, NULL, TypesList());
425
426			if (item) {
427				AddItem(item);
428
429				//	return true so that we know to reenter this list
430				return true;
431			}
432			return true;
433		}
434	}
435
436	//
437	//	return false if we are done with this list
438	//
439	return false;
440}
441
442
443void
444RecentsMenu::DoneBuildingItemList()
445{
446	//
447	//	!! note: don't call inherited here
448	//	the object list is not built
449	//	and this list does not need to be sorted
450	//	BNavMenu::DoneBuildingItemList();
451	//
452
453	if (CountItems() <= 0) {
454		BMenuItem* item = new BMenuItem(B_TRANSLATE("<No recent items>"), 0);
455		item->SetEnabled(false);
456		AddItem(item);
457	} else
458		SetTargetForItems(Target());
459}
460
461
462void
463RecentsMenu::ClearMenuBuildingState()
464{
465	fMenuBuilt = false;
466	BNavMenu::ClearMenuBuildingState();
467}
468