xref: /haiku/src/kits/tracker/FavoritesMenu.cpp (revision 3be9edf8da228afd9fec0390f408c964766122aa)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include "FavoritesMenu.h"
36 
37 #include <compat/sys/stat.h>
38 
39 #include <Application.h>
40 #include <FindDirectory.h>
41 #include <FilePanel.h>
42 #include <Message.h>
43 #include <Path.h>
44 #include <Query.h>
45 #include <Roster.h>
46 
47 #include <functional>
48 #include <algorithm>
49 
50 #include "EntryIterator.h"
51 #include "IconMenuItem.h"
52 #include "NavMenu.h"
53 #include "PoseView.h"
54 #include "QueryPoseView.h"
55 #include "Tracker.h"
56 #include "Utilities.h"
57 
58 
59 FavoritesMenu::FavoritesMenu(const char *title, BMessage *openFolderMessage,
60 	BMessage *openFileMessage, const BMessenger &target,
61 	bool isSavePanel, BRefFilter *filter)
62 	:	BSlowMenu(title),
63 		fOpenFolderMessage(openFolderMessage),
64 		fOpenFileMessage(openFileMessage),
65 		fTarget(target),
66 		fContainer(NULL),
67 		fInitialItemCount(0),
68 		fIsSavePanel(isSavePanel),
69 		fRefFilter(filter)
70 {
71 }
72 
73 
74 FavoritesMenu::~FavoritesMenu()
75 {
76 	delete fOpenFolderMessage;
77 	delete fOpenFileMessage;
78 	delete fContainer;
79 }
80 
81 
82 void
83 FavoritesMenu::SetRefFilter(BRefFilter *filter)
84 {
85 	fRefFilter = filter;
86 }
87 
88 
89 bool
90 FavoritesMenu::StartBuildingItemList()
91 {
92 	// initialize the menu building state
93 
94 	if (!fInitialItemCount)
95 		fInitialItemCount = CountItems();
96 	else {
97 		// strip the old items so we can add new fresh ones
98 		int32 count = CountItems() - fInitialItemCount;
99 		// keep the items that were added by the FavoritesMenu creator
100 		while (count--)
101 			delete RemoveItem(fInitialItemCount);
102 	}
103 
104 	fUniqueRefCheck.clear();
105 	fState = kStart;
106 	return true;
107 }
108 
109 
110 bool
111 FavoritesMenu::AddNextItem()
112 {
113 	// run the next chunk of code for a given item adding state
114 
115 	if (fState == kStart) {
116 		fState = kAddingFavorites;
117 		fSectionItemCount = 0;
118 		fAddedSeparatorForSection = false;
119 		// set up adding the GoTo menu items
120 
121 		try {
122 			BPath path;
123 			ThrowOnError( find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) );
124 			path.Append(kGoDirectory);
125 			mkdir(path.Path(), 0777);
126 
127 			BEntry entry(path.Path());
128 			Model startModel(&entry, true);
129 			ThrowOnInitCheckError(&startModel);
130 
131 			if (!startModel.IsContainer())
132 				throw B_ERROR;
133 
134 			if (startModel.IsQuery())
135 				fContainer = new QueryEntryListCollection(&startModel);
136 			else
137 				fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory *>
138 					(startModel.Node()));
139 
140 			ThrowOnInitCheckError(fContainer);
141 			ThrowOnError( fContainer->Rewind() );
142 
143 		} catch (...) {
144 			delete fContainer;
145 			fContainer = NULL;
146 		}
147 	}
148 
149 
150 	if (fState == kAddingFavorites) {
151 		entry_ref ref;
152 		if (fContainer
153 			&& fContainer->GetNextRef(&ref) == B_OK) {
154 			Model model(&ref, true);
155 			if (model.InitCheck() != B_OK)
156 				return true;
157 
158 			if (!ShouldShowModel(&model))
159 				return true;
160 
161 			BMenuItem *item = BNavMenu::NewModelItem(&model,
162 				model.IsDirectory() ? fOpenFolderMessage : fOpenFileMessage,
163 				fTarget);
164 
165 			item->SetLabel(ref.name);		// this is the name of the link in the Go dir
166 
167 			if (!fAddedSeparatorForSection) {
168 				fAddedSeparatorForSection = true;
169 				AddItem(new TitledSeparatorItem("Favorites"));
170 			}
171 			fUniqueRefCheck.push_back(*model.EntryRef());
172 			AddItem(item);
173 			fSectionItemCount++;
174 			return true;
175 		}
176 
177 		// done with favorites, set up for adding recent files
178 		fState = kAddingFiles;
179 
180 		fAddedSeparatorForSection = false;
181 
182 		app_info info;
183 		be_app->GetAppInfo(&info);
184 		fItems.MakeEmpty();
185 
186 		int32 apps, docs, folders;
187 		TrackerSettings().RecentCounts(&apps, &docs, &folders);
188 
189 		BRoster().GetRecentDocuments(&fItems, docs, NULL, info.signature);
190 		fIndex = 0;
191 		fSectionItemCount = 0;
192 	}
193 
194 	if (fState == kAddingFiles) {
195 		//	if this is a Save panel, not an Open panel
196 		//	then don't add the recent documents
197 		if (!fIsSavePanel) {
198 			for (;;) {
199 				entry_ref ref;
200 				if (fItems.FindRef("refs", fIndex++, &ref) != B_OK)
201 					break;
202 
203 				Model model(&ref, true);
204 				if (model.InitCheck() != B_OK)
205 					return true;
206 
207 				if (!ShouldShowModel(&model))
208 					return true;
209 
210 				BMenuItem *item = BNavMenu::NewModelItem(&model, fOpenFileMessage, fTarget);
211 				if (item) {
212 					if (!fAddedSeparatorForSection) {
213 						fAddedSeparatorForSection = true;
214 						AddItem(new TitledSeparatorItem("Recent Documents"));
215 					}
216 					AddItem(item);
217 					fSectionItemCount++;
218 					return true;
219 				}
220 			}
221 		}
222 
223 		// done with recent files, set up for adding recent folders
224 		fState = kAddingFolders;
225 
226 		fAddedSeparatorForSection = false;
227 
228 		app_info info;
229 		be_app->GetAppInfo(&info);
230 		fItems.MakeEmpty();
231 
232 		int32 apps, docs, folders;
233 		TrackerSettings().RecentCounts(&apps, &docs, &folders);
234 
235 		BRoster().GetRecentFolders(&fItems, folders, info.signature);
236 		fIndex = 0;
237 	}
238 
239 	if (fState == kAddingFolders) {
240 		for (;;) {
241 			entry_ref ref;
242 			if (fItems.FindRef("refs", fIndex++, &ref) != B_OK)
243 				break;
244 
245 			// don't add folders that are already in the GoTo section
246 			if (find_if(fUniqueRefCheck.begin(), fUniqueRefCheck.end(),
247 				bind2nd(std::equal_to<entry_ref>(), ref)) != fUniqueRefCheck.end())
248 				continue;
249 
250 			Model model(&ref, true);
251 			if (model.InitCheck() != B_OK)
252 				return true;
253 
254 			if (!ShouldShowModel(&model))
255 				return true;
256 
257 			BMenuItem *item = BNavMenu::NewModelItem(&model, fOpenFolderMessage,
258 				fTarget, true);
259 			if (item) {
260 				if (!fAddedSeparatorForSection) {
261 					fAddedSeparatorForSection = true;
262 					AddItem(new TitledSeparatorItem("Recent Folders"));
263 				}
264 				AddItem(item);
265 				item->SetEnabled(true);
266 					// BNavMenu::NewModelItem returns a disabled item here -
267 					// need to fix this in BNavMenu::NewModelItem
268 				return true;
269 			}
270 		}
271 	}
272 	return false;
273 }
274 
275 
276 void
277 FavoritesMenu::DoneBuildingItemList()
278 {
279 	SetTargetForItems(fTarget);
280 }
281 
282 
283 void
284 FavoritesMenu::ClearMenuBuildingState()
285 {
286 	delete fContainer;
287 	fContainer = NULL;
288 	fState = kDone;
289 
290 	// force the menu to get rebuilt each time
291 	fMenuBuilt = false;
292 }
293 
294 
295 bool
296 FavoritesMenu::ShouldShowModel(const Model *model)
297 {
298 	if (fIsSavePanel && model->IsFile())
299 		return false;
300 
301 	if (!fRefFilter)
302 		return true;
303 
304 	struct stat_beos statBeOS;
305 	convert_to_stat_beos(model->StatBuf(), &statBeOS);
306 
307 	return fRefFilter->Filter(model->EntryRef(), model->Node(), &statBeOS,
308 		model->MimeType());
309 }
310 
311 
312 //	#pragma mark -
313 
314 
315 RecentsMenu::RecentsMenu(const char *name,int32 which,uint32 what,BHandler *target)
316 	: BNavMenu(name, what, target),
317 	fWhich(which),
318 	fRecentsCount(0),
319 	fItemIndex(0)
320 {
321 	int32 applications;
322 	int32 documents;
323 	int32 folders;
324 	TrackerSettings().RecentCounts(&applications,&documents,&folders);
325 
326 	if (fWhich == 0)
327 		fRecentsCount = documents;
328 	else if (fWhich == 1)
329 		fRecentsCount = applications;
330 	else if (fWhich == 2)
331 		fRecentsCount = folders;
332 }
333 
334 
335 void
336 RecentsMenu::DetachedFromWindow()
337 {
338 	//
339 	//	BNavMenu::DetachedFromWindow sets the TypesList to NULL
340 	//
341 	BMenu::DetachedFromWindow();
342 }
343 
344 
345 bool
346 RecentsMenu::StartBuildingItemList()
347 {
348 	int32 count = CountItems()-1;
349 	for (int32 index = count; index >= 0; index--) {
350 		BMenuItem *item = ItemAt(index);
351 		ASSERT(item);
352 
353 		RemoveItem(index);
354 		delete item;
355 	}
356 	//
357 	//	!! note: don't call inherited from here
358 	//	the navref is not set for this menu
359 	//	but it still needs to be a draggable navmenu
360 	//	simply return true so that AddNextItem is called
361 	//
362 	//	return BNavMenu::StartBuildingItemList();
363 	return true;
364 }
365 
366 
367 bool
368 RecentsMenu::AddNextItem()
369 {
370 	if (fRecentsCount > 0 && AddRecents(fRecentsCount))
371 		return true;
372 
373 	fItemIndex = 0;
374 	return false;
375 }
376 
377 
378 bool
379 RecentsMenu::AddRecents(int32 count)
380 {
381 	if (fItemIndex == 0) {
382 		fRecentList.MakeEmpty();
383 		BRoster roster;
384 
385 		switch(fWhich) {
386 			case 0:
387 				roster.GetRecentDocuments(&fRecentList, count);
388 				break;
389 			case 1:
390 				roster.GetRecentApps(&fRecentList, count);
391 				break;
392 			case 2:
393 				roster.GetRecentFolders(&fRecentList, count);
394 				break;
395 			default:
396 				return false;
397 				break;
398 		}
399 	}
400 	for (;;) {
401 		entry_ref ref;
402 		if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK)
403 			break;
404 
405 		if (ref.name && strlen(ref.name) > 0) {
406 			Model model(&ref, true);
407 			ModelMenuItem *item = BNavMenu::NewModelItem(&model,
408 					new BMessage(fMessage.what),
409 					Target(), false, NULL, TypesList());
410 
411 			if (item) {
412 				AddItem(item);
413 
414 				//	return true so that we know to reenter this list
415 				return true;
416 			}
417 			return true;
418 		}
419 	}
420 
421 	//
422 	//	return false if we are done with this list
423 	//
424 	return false;
425 }
426 
427 
428 void
429 RecentsMenu::DoneBuildingItemList()
430 {
431 	//
432 	//	!! note: don't call inherited here
433 	//	the object list is not built
434 	//	and this list does not need to be sorted
435 	//	BNavMenu::DoneBuildingItemList();
436 	//
437 
438 	if (CountItems() <= 0) {
439 		BMenuItem *item = new BMenuItem("<No Recent Items>", 0);
440 		item->SetEnabled(false);
441 		AddItem(item);
442 	} else
443 		SetTargetForItems(Target());
444 }
445 
446 
447 void
448 RecentsMenu::ClearMenuBuildingState()
449 {
450 	fMenuBuilt = false;
451 	BNavMenu::ClearMenuBuildingState();
452 }
453 
454