xref: /haiku/src/kits/tracker/RecentItems.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 "RecentItems.h"
37 
38 #include <Roster.h>
39 
40 #include "Attributes.h"
41 #include "IconMenuItem.h"
42 #include "Model.h"
43 #include "NavMenu.h"
44 #include "PoseView.h"
45 #include "SlowMenu.h"
46 #include "Tracker.h"
47 #include "Utilities.h"
48 
49 
50 class RecentItemsMenu : public BSlowMenu {
51 public:
52 	RecentItemsMenu(const char* title, BMessage* openMessage,
53 		BHandler* itemTarget, int32 maxItems);
54 	virtual ~RecentItemsMenu();
55 
56 	virtual bool StartBuildingItemList();
57 	virtual bool AddNextItem();
58 	virtual void DoneBuildingItemList() {}
59 	virtual void ClearMenuBuildingState();
60 
61 protected:
62 	virtual const BMessage* FileMessage()
63 	{
64 		return fTargetMesage;
65 	}
66 
67 	virtual const BMessage* ContainerMessage()
68 	{
69 		return fTargetMesage;
70 	}
71 
72 	BRecentItemsList* fIterator;
73 	BMessage* fTargetMesage;
74 	BHandler* fItemTarget;
75 	int32 fCount;
76 	int32 fSanityCount;
77 	int32 fMaxCount;
78 };
79 
80 
81 class RecentFilesMenu : public RecentItemsMenu {
82 public:
83 	RecentFilesMenu(const char* title, BMessage* openFileMessage,
84 		BMessage* openFolderMessage, BHandler* target,
85 		int32 maxItems, bool navMenuFolders, const char* ofType,
86 		const char* openedByAppSig);
87 
88 	RecentFilesMenu(const char* title, BMessage* openFileMessage,
89 		BMessage* openFolderMessage, BHandler* target,
90 		int32 maxItems, bool navMenuFolders, const char* ofTypeList[],
91 		int32 ofTypeListCount, const char* openedByAppSig);
92 
93 	virtual ~RecentFilesMenu();
94 
95 protected:
96 	virtual const BMessage* ContainerMessage()
97 		{ return openFolderMessage; }
98 
99 private:
100 	BMessage* openFolderMessage;
101 };
102 
103 
104 class RecentFoldersMenu : public RecentItemsMenu {
105 public:
106 	RecentFoldersMenu(const char* title, BMessage* openMessage,
107 		BHandler* target, int32 maxItems, bool navMenuFolders,
108 		const char* openedByAppSig);
109 };
110 
111 
112 class RecentAppsMenu : public RecentItemsMenu {
113 public:
114 	RecentAppsMenu(const char* title, BMessage* openMessage,
115 		BHandler* target, int32 maxItems);
116 };
117 
118 
119 // #pragma mark - RecentItemsMenu
120 
121 
122 RecentItemsMenu::RecentItemsMenu(const char* title, BMessage* openMessage,
123 	BHandler* itemTarget, int32 maxItems)
124 	:
125 	BSlowMenu(title),
126 	fIterator(NULL),
127 	fTargetMesage(openMessage),
128 	fItemTarget(itemTarget),
129 	fCount(-1),
130 	fSanityCount(-1),
131 	fMaxCount(maxItems)
132 {
133 }
134 
135 
136 RecentItemsMenu::~RecentItemsMenu()
137 {
138 	delete fIterator;
139 	delete fTargetMesage;
140 }
141 
142 
143 bool
144 RecentItemsMenu::AddNextItem()
145 {
146 	BMenuItem* item = fIterator->GetNextMenuItem(FileMessage(),
147 		ContainerMessage(), fItemTarget);
148 	if (item != NULL) {
149 		AddItem(item);
150 		fCount++;
151 	}
152 	fSanityCount++;
153 
154 	return fCount < fMaxCount - 1 && (fSanityCount < fMaxCount + 20);
155 		// fSanityCount is a hacky way of dealing with a lot of stale
156 		// recent apps
157 }
158 
159 
160 bool
161 RecentItemsMenu::StartBuildingItemList()
162 {
163 	// remove any preexisting items
164 	int32 itemCount = CountItems();
165 	while (itemCount--)
166 		delete RemoveItem((int32)0);
167 
168 	fCount = 0;
169 	fSanityCount = 0;
170 	fIterator->Rewind();
171 
172 	return true;
173 }
174 
175 
176 void
177 RecentItemsMenu::ClearMenuBuildingState()
178 {
179 	fMenuBuilt = false;
180 		// force rebuilding each time
181 	fIterator->Rewind();
182 }
183 
184 
185 // #pragma mark - RecentFilesMenu
186 
187 
188 RecentFilesMenu::RecentFilesMenu(const char* title, BMessage* openFileMessage,
189 	BMessage* openFolderMessage, BHandler* target, int32 maxItems,
190 	bool navMenuFolders, const char* ofType, const char* openedByAppSig)
191 	:
192 	RecentItemsMenu(title, openFileMessage, target, maxItems),
193 	openFolderMessage(openFolderMessage)
194 {
195 	fIterator = new BRecentFilesList(maxItems + 10, navMenuFolders,
196 		ofType, openedByAppSig);
197 }
198 
199 
200 RecentFilesMenu::RecentFilesMenu(const char* title, BMessage* openFileMessage,
201 	BMessage* openFolderMessage, BHandler* target, int32 maxItems,
202 	bool navMenuFolders, const char* ofTypeList[], int32 ofTypeListCount,
203 	const char* openedByAppSig)
204 	:
205 	RecentItemsMenu(title, openFileMessage, target, maxItems),
206 	openFolderMessage(openFolderMessage)
207 {
208 	fIterator = new BRecentFilesList(maxItems + 10, navMenuFolders,
209 		ofTypeList, ofTypeListCount, openedByAppSig);
210 }
211 
212 
213 RecentFilesMenu::~RecentFilesMenu()
214 {
215 	delete openFolderMessage;
216 }
217 
218 
219 // #pragma mark - RecentFoldersMenu
220 
221 
222 RecentFoldersMenu::RecentFoldersMenu(const char* title, BMessage* openMessage,
223 	BHandler* target, int32 maxItems, bool navMenuFolders,
224 	const char* openedByAppSig)
225 	:
226 	RecentItemsMenu(title, openMessage, target, maxItems)
227 {
228 	fIterator = new BRecentFoldersList(maxItems + 10, navMenuFolders,
229 		openedByAppSig);
230 }
231 
232 
233 // #pragma mark - RecentAppsMenu
234 
235 
236 RecentAppsMenu::RecentAppsMenu(const char* title, BMessage* openMessage,
237 	BHandler* target, int32 maxItems)
238 	:
239 	RecentItemsMenu(title, openMessage, target, maxItems)
240 {
241 	fIterator = new BRecentAppsList(maxItems);
242 }
243 
244 
245 // #pragma mark - BRecentItemsList
246 
247 
248 BRecentItemsList::BRecentItemsList(int32 maxItems, bool navMenuFolders)
249 	:
250 	fMaxItems(maxItems),
251 	fNavMenuFolders(navMenuFolders)
252 {
253 	InitIconPreloader();
254 		// need the icon cache
255 	Rewind();
256 }
257 
258 
259 void
260 BRecentItemsList::Rewind()
261 {
262 	fIndex = 0;
263 	fItems.MakeEmpty();
264 }
265 
266 
267 BMenuItem*
268 BRecentItemsList::GetNextMenuItem(const BMessage* fileOpenInvokeMessage,
269 	const BMessage* containerOpenInvokeMessage, BHandler* target,
270 	entry_ref* currentItemRef)
271 {
272 	entry_ref ref;
273 	if (GetNextRef(&ref) != B_OK)
274 		return NULL;
275 
276 	Model model(&ref, true);
277 	if (model.InitCheck() != B_OK)
278 		return NULL;
279 
280 	bool container = false;
281 	if (model.IsSymLink()) {
282 
283 		Model* newResolvedModel = NULL;
284 		Model* result = model.LinkTo();
285 
286 		if (result == NULL) {
287 			newResolvedModel = new Model(model.EntryRef(), true, true);
288 
289 			if (newResolvedModel->InitCheck() != B_OK) {
290 				// broken link, still can show though, bail
291 				delete newResolvedModel;
292 				result = NULL;
293 			} else
294 				result = newResolvedModel;
295 		} else {
296 			BModelOpener opener(result);
297 				// open the model, if it ain't open already
298 
299 			PoseInfo poseInfo;
300 			BNode* resultNode = result->Node();
301 			if (resultNode != NULL) {
302 				resultNode->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0,
303 					&poseInfo, sizeof(poseInfo));
304 			}
305 
306 			result->CloseNode();
307 
308 			ref = *result->EntryRef();
309 			container = result->IsContainer();
310 		}
311 		model.SetLinkTo(result);
312 	} else {
313 		ref = *model.EntryRef();
314 		container = model.IsContainer();
315 	}
316 
317 	// if user asked for it, return the current item ref
318 	if (currentItemRef != NULL)
319 		*currentItemRef = ref;
320 
321 	BMessage* message;
322 	if (container && containerOpenInvokeMessage)
323 		message = new BMessage(*containerOpenInvokeMessage);
324 	else if (!container && fileOpenInvokeMessage)
325 		message = new BMessage(*fileOpenInvokeMessage);
326 	else
327 		message = new BMessage(B_REFS_RECEIVED);
328 
329 	message->AddRef("refs", model.EntryRef());
330 
331 	// Truncate the name if necessary
332 	BString truncatedString(model.Name());
333 	be_plain_font->TruncateString(&truncatedString, B_TRUNCATE_END,
334 		BNavMenu::GetMaxMenuWidth());
335 
336 	ModelMenuItem* item = NULL;
337 	if (!container || !fNavMenuFolders)
338 		item = new ModelMenuItem(&model, truncatedString.String(), message);
339 	else {
340 		// add another nav menu item if it's a directory
341 		BNavMenu* menu = new BNavMenu(truncatedString.String(), message->what,
342 			target, 0);
343 
344 		menu->SetNavDir(&ref);
345 		item = new ModelMenuItem(&model, menu);
346 		item->SetMessage(message);
347 	}
348 
349 	if (item != NULL && target != NULL)
350 		item->SetTarget(target);
351 
352 	return item;
353 }
354 
355 
356 status_t
357 BRecentItemsList::GetNextRef(entry_ref* result)
358 {
359 	return fItems.FindRef("refs", fIndex++, result);
360 }
361 
362 
363 // #pragma mark - BRecentFilesList
364 
365 
366 BRecentFilesList::BRecentFilesList(int32 maxItems, bool navMenuFolders,
367 	const char* ofType, const char* openedByAppSig)
368 	:
369 	BRecentItemsList(maxItems, navMenuFolders),
370 	fType(ofType),
371 	fTypes(NULL),
372 	fTypeCount(0),
373 	fAppSig(openedByAppSig)
374 {
375 }
376 
377 
378 BRecentFilesList::BRecentFilesList(int32 maxItems, bool navMenuFolders,
379 	const char* ofTypeList[], int32 ofTypeListCount,
380 	const char* openedByAppSig)
381 	:
382 	BRecentItemsList(maxItems, navMenuFolders),
383 	fType(NULL),
384 	fTypes(NULL),
385 	fTypeCount(ofTypeListCount),
386 	fAppSig(openedByAppSig)
387 {
388 	if (fTypeCount > 0) {
389 		fTypes = new char *[ofTypeListCount];
390 		for (int32 index = 0; index < ofTypeListCount; index++)
391 			fTypes[index] = strdup(ofTypeList[index]);
392 	}
393 }
394 
395 
396 BRecentFilesList::~BRecentFilesList()
397 {
398 	if (fTypeCount > 0) {
399 		for (int32 index = 0; index < fTypeCount; index++)
400 			free(fTypes[index]);
401 		delete[] fTypes;
402 	}
403 }
404 
405 
406 status_t
407 BRecentFilesList::GetNextRef(entry_ref* ref)
408 {
409 	if (fIndex == 0) {
410 		// Lazy roster Get
411 		if (fTypes != NULL) {
412 			BRoster().GetRecentDocuments(&fItems, fMaxItems,
413 				const_cast<const char**>(fTypes),
414 				fTypeCount, fAppSig.Length() ? fAppSig.String() : NULL);
415 		} else {
416 			BRoster().GetRecentDocuments(&fItems, fMaxItems,
417 				fType.Length() ? fType.String() : NULL,
418 				fAppSig.Length() ? fAppSig.String() : NULL);
419 		}
420 
421 	}
422 
423 	return BRecentItemsList::GetNextRef(ref);
424 }
425 
426 
427 BMenu*
428 BRecentFilesList::NewFileListMenu(const char* title,
429 	BMessage* openFileMessage, BMessage* openFolderMessage,
430 	BHandler* target, int32 maxItems, bool navMenuFolders, const char* ofType,
431 	const char* openedByAppSig)
432 {
433 	return new RecentFilesMenu(title, openFileMessage,
434 		openFolderMessage, target, maxItems, navMenuFolders, ofType,
435 		openedByAppSig);
436 }
437 
438 
439 BMenu*
440 BRecentFilesList::NewFileListMenu(const char* title,
441 	BMessage* openFileMessage, BMessage* openFolderMessage,
442 	BHandler* target, int32 maxItems, bool navMenuFolders,
443 	const char* ofTypeList[], int32 ofTypeListCount,
444 	const char* openedByAppSig)
445 {
446 	return new RecentFilesMenu(title, openFileMessage,
447 		openFolderMessage, target, maxItems, navMenuFolders, ofTypeList,
448 		ofTypeListCount, openedByAppSig);
449 }
450 
451 
452 // #pragma mark - BRecentFoldersList
453 
454 
455 BMenu*
456 BRecentFoldersList::NewFolderListMenu(const char* title,
457 	BMessage* openMessage, BHandler* target, int32 maxItems,
458 	bool navMenuFolders, const char* openedByAppSig)
459 {
460 	return new RecentFoldersMenu(title, openMessage, target, maxItems,
461 		navMenuFolders, openedByAppSig);
462 }
463 
464 
465 BRecentFoldersList::BRecentFoldersList(int32 maxItems, bool navMenuFolders,
466 	const char* openedByAppSig)
467 	:
468 	BRecentItemsList(maxItems, navMenuFolders),
469 	fAppSig(openedByAppSig)
470 {
471 }
472 
473 
474 status_t
475 BRecentFoldersList::GetNextRef(entry_ref* ref)
476 {
477 	if (fIndex == 0) {
478 		// Lazy roster Get
479 		BRoster().GetRecentFolders(&fItems, fMaxItems,
480 			fAppSig.Length() ? fAppSig.String() : NULL);
481 
482 	}
483 
484 	return BRecentItemsList::GetNextRef(ref);
485 }
486 
487 
488 // #pragma mark - BRecentAppsList
489 
490 
491 BRecentAppsList::BRecentAppsList(int32 maxItems)
492 	:
493 	BRecentItemsList(maxItems, false)
494 {
495 }
496 
497 
498 status_t
499 BRecentAppsList::GetNextRef(entry_ref* ref)
500 {
501 	if (fIndex == 0) {
502 		// Lazy roster Get
503 		BRoster().GetRecentApps(&fItems, fMaxItems);
504 	}
505 
506 	return BRecentItemsList::GetNextRef(ref);
507 }
508 
509 
510 BMenu*
511 BRecentAppsList::NewAppListMenu(const char* title, BMessage* openMessage,
512 	 BHandler* target, int32 maxItems)
513 {
514 	return new RecentAppsMenu(title, openMessage, target, maxItems);
515 }
516