1/*
2 * Copyright 2001-2009, Haiku Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Tyler Dauwalder
7 *		Ingo Weinhold, bonefish@users.sf.net
8 *		Axel D��rfler, axeld@pinc-software.de
9 */
10
11
12//!	Recently launched apps list
13
14
15#include "RecentApps.h"
16
17#include <tracker_private.h>
18
19#include <AppFileInfo.h>
20#include <Entry.h>
21#include <File.h>
22#include <Message.h>
23#include <Mime.h>
24#include <Roster.h>
25#include <storage_support.h>
26
27#include <strings.h>
28
29#include "Debug.h"
30
31
32/*!	\class RecentApps
33	\brief Manages the roster's list of recently launched applications
34
35*/
36
37
38/*!	\var std::list<std::string> RecentApps::fAppList
39	\brief The list of app sigs, most recent first
40
41	The signatures are expected to be stored all lowercase, as MIME
42	signatures are case-independent.
43*/
44
45
46/*!	\brief Creates a new list.
47
48	The list is initially empty.
49*/
50RecentApps::RecentApps()
51{
52}
53
54
55/*!	\brief Frees all resources associated with the object.
56
57	Currently does nothing.
58*/
59RecentApps::~RecentApps()
60{
61}
62
63
64/*! \brief Places the app with the given signature at the front of
65	the recent apps list.
66
67	If the app already exists elsewhere in the list, that item is
68	removed so only one instance exists in the list at any time.
69
70	\param appSig The application's signature
71	\param appFlags The application's flags. If \a appFlags contains
72	                either \c B_ARGV_ONLY or \c B_BACKGROUND_APP, the
73	                application is \b not added to the list (but \c B_OK
74	                is still returned).
75	\return
76	- \c B_OK: success (even if the app was not added due to appFlags)
77	- error code: failure
78*/
79status_t
80RecentApps::Add(const char *appSig, int32 appFlags)
81{
82	if (appSig == NULL)
83		return B_BAD_VALUE;
84
85	// don't add background apps, as well as Tracker and Deskbar to the list
86	// of recent apps
87	if (!strcasecmp(appSig, kTrackerSignature)
88		|| !strcasecmp(appSig, kDeskbarSignature)
89		|| (appFlags & (B_ARGV_ONLY | B_BACKGROUND_APP)) != 0)
90		return B_OK;
91
92	// Remove any previous instance
93	std::list<std::string>::iterator i;
94	for (i = fAppList.begin(); i != fAppList.end(); i++) {
95		if (!strcasecmp((*i).c_str(), appSig)) {
96			fAppList.erase(i);
97			break;
98		}
99	}
100
101	try {
102		// Add to the front
103		fAppList.push_front(appSig);
104	} catch (...) {
105		return B_NO_MEMORY;
106	}
107
108	int32 remove = fAppList.size() - kMaxRecentApps;
109	while (remove > 0) {
110		fAppList.pop_back();
111		remove--;
112	}
113
114	return B_OK;
115}
116
117
118/*! \brief Adds the signature of the application referred to by \a ref at
119	the front of the recent apps list.
120
121	The entry is checked for a BEOS:APP_SIG attribute. If that fails, the
122	app's resources are checked. If no signature can be found, the call
123	fails.
124*/
125status_t
126RecentApps::Add(const entry_ref *ref, int32 appFlags)
127{
128	if (ref == NULL)
129		return B_BAD_VALUE;
130
131	BFile file;
132	BAppFileInfo info;
133	char signature[B_MIME_TYPE_LENGTH];
134
135	status_t err = file.SetTo(ref, B_READ_ONLY);
136	if (!err)
137		err = info.SetTo(&file);
138	if (!err)
139		err = info.GetSignature(signature);
140	if (!err)
141		err = Add(signature, appFlags);
142	return err;
143}
144
145
146/*! \brief Returns the first \a maxCount recent apps in the \c BMessage
147	pointed to by \a list.
148
149	The message is cleared first, and \c entry_refs for the the apps are
150	stored in the \c "refs" field of the message (\c B_REF_TYPE).
151
152	If there are fewer than \a maxCount items in the list, the entire
153	list is returned.
154
155	Since BRoster::GetRecentApps() returns \c void, the message pointed
156	to by \a list is simply cleared if maxCount is invalid (i.e. <= 0).
157*/
158status_t
159RecentApps::Get(int32 maxCount, BMessage *list)
160{
161	if (list == NULL)
162		return B_BAD_VALUE;
163
164	// Clear
165	list->MakeEmpty();
166
167	// Fill
168	std::list<std::string>::iterator item;
169	status_t status = B_OK;
170	int counter = 0;
171	for (item = fAppList.begin();
172			status == B_OK && counter < maxCount && item != fAppList.end();
173			counter++, item++) {
174		entry_ref ref;
175		if (GetRefForApp(item->c_str(), &ref) == B_OK)
176			status = list->AddRef("refs", &ref);
177		else {
178			D(PRINT("WARNING: RecentApps::Get(): No ref found for app '%s'\n",
179				item->c_str()));
180		}
181	}
182
183	return status;
184}
185
186
187/*! \brief Clears the list of recently launched apps
188*/
189status_t
190RecentApps::Clear()
191{
192	fAppList.clear();
193	return B_OK;
194}
195
196
197/*! \brief Dumps the the current list of apps to stdout.
198*/
199status_t
200RecentApps::Print()
201{
202	std::list<std::string>::iterator item;
203	int counter = 1;
204	for (item = fAppList.begin(); item != fAppList.end(); item++) {
205		printf("%d: '%s'\n", counter++, item->c_str());
206	}
207	return B_OK;
208}
209
210
211/*! \brief Outputs a textual representation of the current recent
212	apps list to the given file stream.
213
214*/
215status_t
216RecentApps::Save(FILE* file)
217{
218	status_t error = file ? B_OK : B_BAD_VALUE;
219	if (!error) {
220		fprintf(file, "# Recent applications\n");
221		std::list<std::string>::iterator item;
222		for (item = fAppList.begin(); item != fAppList.end(); item++) {
223			fprintf(file, "RecentApp %s\n", item->c_str());
224		}
225		fprintf(file, "\n");
226	}
227	return error;
228}
229
230
231/*! \brief Fetches an \c entry_ref for the application with the
232	given signature.
233
234	First the MIME database is checked for a matching application type
235	with a valid app hint attribute. If that fails, a query is established
236	to track down such an application, if there is one available.
237*/
238status_t
239RecentApps::GetRefForApp(const char *appSig, entry_ref *result)
240{
241	if (appSig == NULL || result == NULL)
242		return B_BAD_VALUE;
243
244	// We'll use BMimeType to check for the app hint, since I'm lazy
245	// and Ingo's on vacation :-P :-)
246	BMimeType mime(appSig);
247	status_t err = mime.InitCheck();
248	if (!err)
249		err = mime.GetAppHint(result);
250	return err;
251}
252
253