xref: /haiku/src/kits/tracker/FavoritesMenu.cpp (revision 1454f3c27aec3e7879a58e7cedc9d66d90c773a7)
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 
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 
68 FavoritesMenu::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 
89 FavoritesMenu::~FavoritesMenu()
90 {
91 	delete fOpenFolderMessage;
92 	delete fOpenFileMessage;
93 	delete fContainer;
94 }
95 
96 
97 void
98 FavoritesMenu::SetRefFilter(BRefFilter* filter)
99 {
100 	fRefFilter = filter;
101 }
102 
103 
104 bool
105 FavoritesMenu::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 
125 bool
126 FavoritesMenu::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 
311 void
312 FavoritesMenu::DoneBuildingItemList()
313 {
314 	SetTargetForItems(fTarget);
315 }
316 
317 
318 void
319 FavoritesMenu::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 
330 bool
331 FavoritesMenu::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 
350 RecentsMenu::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 
372 void
373 RecentsMenu::DetachedFromWindow()
374 {
375 	//
376 	//	BNavMenu::DetachedFromWindow sets the TypesList to NULL
377 	//
378 	BMenu::DetachedFromWindow();
379 }
380 
381 
382 bool
383 RecentsMenu::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 
404 bool
405 RecentsMenu::AddNextItem()
406 {
407 	if (fRecentsCount > 0 && AddRecents(fRecentsCount))
408 		return true;
409 
410 	fItemIndex = 0;
411 	return false;
412 }
413 
414 
415 bool
416 RecentsMenu::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 
466 void
467 RecentsMenu::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 
485 void
486 RecentsMenu::ClearMenuBuildingState()
487 {
488 	fMenuBuilt = false;
489 	BNavMenu::ClearMenuBuildingState();
490 }
491