2 * Copyright 2001-2009, Haiku Inc.
3 * Distributed under the terms of the MIT License.
7 * Ingo Weinhold, bonefish@users.sf.net
8 * Axel Dörfler, axeld@pinc-software.de
12 //! Recently launched apps list
15 #include "RecentEntries.h"
22 #include <AppFileInfo.h>
31 #include <storage_support.h>
39 /*! \struct recent_entry
41 \brief A recent entry, the corresponding signature of the application
42 that launched/used/opened/viewed/whatevered it, and an index used for
43 keeping track of orderings when loading/storing the recent entries list
48 /*! \brief Creates a new recent_entry object.
50 recent_entry::recent_entry(const entry_ref
*ref
, const char *appSig
,
53 ref(ref
? *ref
: entry_ref()),
63 /*! \class RecentEntries
64 \brief Implements the common functionality used by the roster's recent
65 folders and recent documents lists.
69 /*! \var std::list<std::string> RecentEntries::fEntryList
70 \brief The list of entries and their corresponding app sigs, most recent first
72 The signatures are expected to be stored all lowercase, as MIME
73 signatures are case-independent.
77 /*! \brief Creates a new list.
79 The list is initially empty.
81 RecentEntries::RecentEntries()
86 /*! \brief Frees all resources associated with the object.
88 RecentEntries::~RecentEntries()
94 /*! \brief Places the given entry Places the app with the given signature at the front of
97 If the app already exists elsewhere in the list, that item is
98 removed so only one instance exists in the list at any time.
100 \param appSig The application's signature
101 \param appFlags The application's flags. If \a appFlags contains
102 either \c B_ARGV_ONLY or \c B_BACKGROUND_APP, the
103 application is \b not added to the list (but \c B_OK
106 - \c B_OK: success (even if the app was not added due to appFlags)
107 - error code: failure
110 RecentEntries::Add(const entry_ref
*ref
, const char *appSig
)
112 if (ref
== NULL
|| appSig
== NULL
)
115 // Look for a previous instance of this entry
116 std::list
<recent_entry
*>::iterator item
;
117 for (item
= fEntryList
.begin(); item
!= fEntryList
.end(); item
++) {
118 if ((*item
)->ref
== *ref
&& !strcasecmp((*item
)->sig
.c_str(), appSig
)) {
119 fEntryList
.erase(item
);
124 // Add this entry to the front of the list
125 recent_entry
*entry
= new (nothrow
) recent_entry(ref
, appSig
, 0);
130 fEntryList
.push_front(entry
);
139 /*! \brief Returns the first \a maxCount recent apps in the \c BMessage
140 pointed to by \a list.
142 The message is cleared first, and \c entry_refs for the the apps are
143 stored in the \c "refs" field of the message (\c B_REF_TYPE).
145 If there are fewer than \a maxCount items in the list, the entire
148 Duplicate entries are never returned, i.e. if two instances of the
149 same entry were added under different app sigs, and both instances
150 match the given filter criterion, only the most recent instance is
151 returned; the latter instance is ignored and not counted towards
152 the \a maxCount number of entries to return.
154 Since BRoster::GetRecentEntries() returns \c void, the message pointed
155 to by \a list is simply cleared if maxCount is invalid (i.e. <= 0).
157 \param fileTypes An array of file type filters. These file types are
158 expected to be all lowercase.
161 RecentEntries::Get(int32 maxCount
, const char *fileTypes
[],
162 int32 fileTypesCount
, const char *appSig
, BMessage
*result
)
165 || fileTypesCount
< 0
166 || (fileTypesCount
> 0 && fileTypes
== NULL
))
171 std::list
<recent_entry
*> duplicateList
;
172 std::list
<recent_entry
*>::iterator item
;
173 status_t error
= B_OK
;
176 for (item
= fEntryList
.begin();
177 error
== B_OK
&& count
< maxCount
&& item
!= fEntryList
.end();
180 if (appSig
!= NULL
&& strcasecmp((*item
)->sig
.c_str(), appSig
))
183 // Filter by file type
184 if (fileTypesCount
> 0) {
185 char type
[B_MIME_TYPE_LENGTH
];
186 if (GetTypeForRef(&(*item
)->ref
, type
) == B_OK
) {
188 for (int i
= 0; i
< fileTypesCount
; i
++) {
189 if (!strcasecmp(type
, fileTypes
[i
])) {
199 // Check for duplicates
200 bool duplicate
= false;
201 for (std::list
<recent_entry
*>::iterator dupItem
= duplicateList
.begin();
202 dupItem
!= duplicateList
.end(); dupItem
++) {
203 if ((*dupItem
)->ref
== (*item
)->ref
) {
211 // Add the ref to the list used to check
212 // for duplicates, and then to the result
214 duplicateList
.push_back(*item
);
219 error
= result
->AddRef("refs", &(*item
)->ref
);
228 /*! \brief Clears the list of recently launched apps
231 RecentEntries::Clear()
233 std::list
<recent_entry
*>::iterator i
;
234 for (i
= fEntryList
.begin(); i
!= fEntryList
.end(); i
++) {
242 /*! \brief Dumps the the current list of entries to stdout.
245 RecentEntries::Print()
247 std::list
<recent_entry
*>::iterator item
;
249 for (item
= fEntryList
.begin(); item
!= fEntryList
.end(); item
++) {
250 printf("%d: device == '%" B_PRIdDEV
"', dir == '%" B_PRIdINO
"', "
251 "name == '%s', app == '%s', index == %" B_PRId32
"\n", counter
++,
252 (*item
)->ref
.device
, (*item
)->ref
.directory
, (*item
)->ref
.name
,
253 (*item
)->sig
.c_str(), (*item
)->index
);
260 RecentEntries::Save(FILE* file
, const char *description
, const char *tag
)
262 if (file
== NULL
|| description
== NULL
|| tag
== NULL
)
265 fprintf(file
, "# %s\n", description
);
267 /* In order to write our entries out in the format used by the
268 Roster settings file, we need to collect all the signatures
269 for each entry in one place, while at the same time updating
270 the index values for each entry/sig pair to reflect the current
271 ordering of the list. I believe this is the data structure
272 R5 actually maintains all the time, as their indices do not
273 change over time (whereas ours will). If our implementation
274 proves to be slower that R5, we may want to consider using
275 the data structure pervasively.
277 std::map
<entry_ref
, std::list
<recent_entry
*> > map
;
278 uint32 count
= fEntryList
.size();
281 for (std::list
<recent_entry
*>::iterator item
= fEntryList
.begin();
282 item
!= fEntryList
.end(); count
--, item
++) {
283 recent_entry
*entry
= *item
;
285 entry
->index
= count
;
286 map
[entry
->ref
].push_back(entry
);
288 D(PRINT("WARNING: RecentEntries::Save(): The entry %ld entries "
289 "from the front of fEntryList was found to be NULL\n",
290 fEntryList
.size() - count
));
297 for (std::map
<entry_ref
, std::list
<recent_entry
*> >::iterator mapItem
= map
.begin();
298 mapItem
!= map
.end(); mapItem
++) {
299 // We're going to need to properly escape the path name we
300 // get, which will at absolute worst double the length of
303 char escapedPath
[B_PATH_NAME_LENGTH
*2];
304 status_t outputError
= path
.SetTo(&mapItem
->first
);
306 BPrivate::Storage::escape_path(path
.Path(), escapedPath
);
307 fprintf(file
, "%s %s", tag
, escapedPath
);
308 std::list
<recent_entry
*> &list
= mapItem
->second
;
310 for (std::list
<recent_entry
*>::iterator item
= list
.begin();
311 item
!= list
.end(); i
++, item
++) {
312 recent_entry
*entry
= *item
;
314 fprintf(file
, " \"%s\" %" B_PRId32
, entry
->sig
.c_str(),
317 D(PRINT("WARNING: RecentEntries::Save(): The entry %"
318 B_PRId32
" entries from the front of the compiled "
319 "recent_entry* list for the entry ref (%" B_PRId32
", %"
320 B_PRId64
", '%s') was found to be NULL\n", i
,
321 mapItem
->first
.device
, mapItem
->first
.directory
,
322 mapItem
->first
.name
));
327 D(PRINT("WARNING: RecentEntries::Save(): entry_ref_to_path() "
328 "failed on the entry_ref (%" B_PRId32
", %" B_PRId64
", '%s') "
329 "with error 0x%" B_PRIx32
"\n",
330 mapItem
->first
.device
, mapItem
->first
.directory
,
331 mapItem
->first
.name
, outputError
));
341 /*! \brief Fetches the file type of the given file.
343 If the file has no type, an empty string is returned. The file
347 RecentEntries::GetTypeForRef(const entry_ref
*ref
, char *result
)
349 if (ref
== NULL
|| result
== NULL
)
354 status_t error
= node
.SetTo(ref
);
356 ssize_t bytes
= node
.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE
,
357 0, result
, B_MIME_TYPE_LENGTH
- 1);
361 result
[bytes
] = '\0';