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