xref: /haiku/src/kits/tracker/FavoritesMenu.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
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 				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 
306 void
307 FavoritesMenu::DoneBuildingItemList()
308 {
309 	SetTargetForItems(fTarget);
310 }
311 
312 
313 void
314 FavoritesMenu::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 
325 bool
326 FavoritesMenu::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 
345 RecentsMenu::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 
367 void
368 RecentsMenu::DetachedFromWindow()
369 {
370 	//
371 	//	BNavMenu::DetachedFromWindow sets the TypesList to NULL
372 	//
373 	BMenu::DetachedFromWindow();
374 }
375 
376 
377 bool
378 RecentsMenu::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 
399 bool
400 RecentsMenu::AddNextItem()
401 {
402 	if (fRecentsCount > 0 && AddRecents(fRecentsCount))
403 		return true;
404 
405 	fItemIndex = 0;
406 	return false;
407 }
408 
409 
410 bool
411 RecentsMenu::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 
461 void
462 RecentsMenu::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 
480 void
481 RecentsMenu::ClearMenuBuildingState()
482 {
483 	fMenuBuilt = false;
484 	BNavMenu::ClearMenuBuildingState();
485 }
486