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	return B_OK;
109}
110
111
112/*! \brief Adds the signature of the application referred to by \a ref at
113	the front of the recent apps list.
114
115	The entry is checked for a BEOS:APP_SIG attribute. If that fails, the
116	app's resources are checked. If no signature can be found, the call
117	fails.
118*/
119status_t
120RecentApps::Add(const entry_ref *ref, int32 appFlags)
121{
122	if (ref == NULL)
123		return B_BAD_VALUE;
124
125	BFile file;
126	BAppFileInfo info;
127	char signature[B_MIME_TYPE_LENGTH];
128
129	status_t err = file.SetTo(ref, B_READ_ONLY);
130	if (!err)
131		err = info.SetTo(&file);
132	if (!err)
133		err = info.GetSignature(signature);
134	if (!err)
135		err = Add(signature, appFlags);
136	return err;
137}
138
139
140/*! \brief Returns the first \a maxCount recent apps in the \c BMessage
141	pointed to by \a list.
142
143	The message is cleared first, and \c entry_refs for the the apps are
144	stored in the \c "refs" field of the message (\c B_REF_TYPE).
145
146	If there are fewer than \a maxCount items in the list, the entire
147	list is returned.
148
149	Since BRoster::GetRecentApps() returns \c void, the message pointed
150	to by \a list is simply cleared if maxCount is invalid (i.e. <= 0).
151*/
152status_t
153RecentApps::Get(int32 maxCount, BMessage *list)
154{
155	if (list == NULL)
156		return B_BAD_VALUE;
157
158	// Clear
159	list->MakeEmpty();
160
161	// Fill
162	std::list<std::string>::iterator item;
163	status_t status = B_OK;
164	int counter = 0;
165	for (item = fAppList.begin();
166			status == B_OK && counter < maxCount && item != fAppList.end();
167			counter++, item++) {
168		entry_ref ref;
169		if (GetRefForApp(item->c_str(), &ref) == B_OK)
170			status = list->AddRef("refs", &ref);
171		else {
172			D(PRINT("WARNING: RecentApps::Get(): No ref found for app '%s'\n",
173				item->c_str()));
174		}
175	}
176
177	return status;
178}
179
180
181/*! \brief Clears the list of recently launched apps
182*/
183status_t
184RecentApps::Clear()
185{
186	fAppList.clear();
187	return B_OK;
188}
189
190
191/*! \brief Dumps the the current list of apps to stdout.
192*/
193status_t
194RecentApps::Print()
195{
196	std::list<std::string>::iterator item;
197	int counter = 1;
198	for (item = fAppList.begin(); item != fAppList.end(); item++) {
199		printf("%d: '%s'\n", counter++, item->c_str());
200	}
201	return B_OK;
202}
203
204
205/*! \brief Outputs a textual representation of the current recent
206	apps list to the given file stream.
207
208*/
209status_t
210RecentApps::Save(FILE* file)
211{
212	status_t error = file ? B_OK : B_BAD_VALUE;
213	if (!error) {
214		fprintf(file, "# Recent applications\n");
215		std::list<std::string>::iterator item;
216		for (item = fAppList.begin(); item != fAppList.end(); item++) {
217			fprintf(file, "RecentApp %s\n", item->c_str());
218		}
219		fprintf(file, "\n");
220	}
221	return error;
222}
223
224
225/*! \brief Fetches an \c entry_ref for the application with the
226	given signature.
227
228	First the MIME database is checked for a matching application type
229	with a valid app hint attribute. If that fails, a query is established
230	to track down such an application, if there is one available.
231*/
232status_t
233RecentApps::GetRefForApp(const char *appSig, entry_ref *result)
234{
235	if (appSig == NULL || result == NULL)
236		return B_BAD_VALUE;
237
238	// We'll use BMimeType to check for the app hint, since I'm lazy
239	// and Ingo's on vacation :-P :-)
240	BMimeType mime(appSig);
241	status_t err = mime.InitCheck();
242	if (!err)
243		err = mime.GetAppHint(result);
244	return err;
245}
246
247