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