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