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				bind2nd(std::equal_to<entry_ref>(), ref))
273					!= fUniqueRefCheck.end()) {
274				continue;
275			}
276
277			Model model(&ref, true);
278			if (model.InitCheck() != B_OK)
279				return true;
280
281			if (!ShouldShowModel(&model))
282				return true;
283
284			BMenuItem* item = BNavMenu::NewModelItem(&model,
285				fOpenFolderMessage, fTarget, true);
286			if (item != NULL) {
287				if (!fAddedSeparatorForSection) {
288					fAddedSeparatorForSection = true;
289					AddItem(new TitledSeparatorItem(
290						B_TRANSLATE("Recent folders")));
291				}
292				AddItem(item);
293				item->SetEnabled(true);
294					// BNavMenu::NewModelItem returns a disabled item here -
295					// need to fix this in BNavMenu::NewModelItem
296
297				return true;
298			}
299		}
300	}
301
302	return false;
303}
304
305
306void
307FavoritesMenu::DoneBuildingItemList()
308{
309	SetTargetForItems(fTarget);
310}
311
312
313void
314FavoritesMenu::ClearMenuBuildingState()
315{
316	delete fContainer;
317	fContainer = NULL;
318	fState = kDone;
319
320	// force the menu to get rebuilt each time
321	fMenuBuilt = false;
322}
323
324
325bool
326FavoritesMenu::ShouldShowModel(const Model* model)
327{
328	if (fIsSavePanel && model->IsFile())
329		return false;
330
331	if (!fRefFilter || model->Node() == NULL)
332		return true;
333
334	struct stat_beos statBeOS;
335	convert_to_stat_beos(model->StatBuf(), &statBeOS);
336
337	return fRefFilter->Filter(model->EntryRef(), model->Node(), &statBeOS,
338		model->MimeType());
339}
340
341
342//	#pragma mark - RecentsMenu
343
344
345RecentsMenu::RecentsMenu(const char* name, int32 which, uint32 what,
346	BHandler* target)
347	:
348	BNavMenu(name, what, target),
349	fWhich(which),
350	fRecentsCount(0),
351	fItemIndex(0)
352{
353	int32 applications;
354	int32 documents;
355	int32 folders;
356	TrackerSettings().RecentCounts(&applications,&documents,&folders);
357
358	if (fWhich == 0)
359		fRecentsCount = documents;
360	else if (fWhich == 1)
361		fRecentsCount = applications;
362	else if (fWhich == 2)
363		fRecentsCount = folders;
364}
365
366
367void
368RecentsMenu::DetachedFromWindow()
369{
370	//
371	//	BNavMenu::DetachedFromWindow sets the TypesList to NULL
372	//
373	BMenu::DetachedFromWindow();
374}
375
376
377bool
378RecentsMenu::StartBuildingItemList()
379{
380	int32 count = CountItems()-1;
381	for (int32 index = count; index >= 0; index--) {
382		BMenuItem* item = ItemAt(index);
383		ASSERT(item);
384
385		RemoveItem(index);
386		delete item;
387	}
388	//
389	//	!! note: don't call inherited from here
390	//	the navref is not set for this menu
391	//	but it still needs to be a draggable navmenu
392	//	simply return true so that AddNextItem is called
393	//
394	//	return BNavMenu::StartBuildingItemList();
395	return true;
396}
397
398
399bool
400RecentsMenu::AddNextItem()
401{
402	if (fRecentsCount > 0 && AddRecents(fRecentsCount))
403		return true;
404
405	fItemIndex = 0;
406	return false;
407}
408
409
410bool
411RecentsMenu::AddRecents(int32 count)
412{
413	if (fItemIndex == 0) {
414		fRecentList.MakeEmpty();
415		BRoster roster;
416
417		switch(fWhich) {
418			case 0:
419				roster.GetRecentDocuments(&fRecentList, count);
420				break;
421			case 1:
422				roster.GetRecentApps(&fRecentList, count);
423				break;
424			case 2:
425				roster.GetRecentFolders(&fRecentList, count);
426				break;
427			default:
428				return false;
429				break;
430		}
431	}
432	for (;;) {
433		entry_ref ref;
434		if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK)
435			break;
436
437		if (ref.name != NULL && strlen(ref.name) > 0) {
438			Model model(&ref, true);
439			ModelMenuItem* item = BNavMenu::NewModelItem(&model,
440					new BMessage(fMessage.what),
441					Target(), false, NULL, TypesList());
442
443			if (item != NULL) {
444				AddItem(item);
445
446				//	return true so that we know to reenter this list
447				return true;
448			}
449
450			return true;
451		}
452	}
453
454	//
455	//	return false if we are done with this list
456	//
457	return false;
458}
459
460
461void
462RecentsMenu::DoneBuildingItemList()
463{
464	//
465	//	!! note: don't call inherited here
466	//	the object list is not built
467	//	and this list does not need to be sorted
468	//	BNavMenu::DoneBuildingItemList();
469	//
470
471	if (CountItems() <= 0) {
472		BMenuItem* item = new BMenuItem(B_TRANSLATE("<No recent items>"), 0);
473		item->SetEnabled(false);
474		AddItem(item);
475	} else
476		SetTargetForItems(Target());
477}
478
479
480void
481RecentsMenu::ClearMenuBuildingState()
482{
483	fMenuBuilt = false;
484	BNavMenu::ClearMenuBuildingState();
485}
486