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