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