1 /*
2 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
3 *
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
6
7 #include "BrowsingHistory.h"
8
9 #include <new>
10 #include <stdio.h>
11
12 #include <Autolock.h>
13 #include <Entry.h>
14 #include <File.h>
15 #include <FindDirectory.h>
16 #include <Message.h>
17 #include <Path.h>
18
19 #include "BrowserApp.h"
20
21
BrowsingHistoryItem(const BString & url)22 BrowsingHistoryItem::BrowsingHistoryItem(const BString& url)
23 :
24 fURL(url),
25 fDateTime(BDateTime::CurrentDateTime(B_LOCAL_TIME)),
26 fInvokationCount(0)
27 {
28 }
29
30
BrowsingHistoryItem(const BrowsingHistoryItem & other)31 BrowsingHistoryItem::BrowsingHistoryItem(const BrowsingHistoryItem& other)
32 {
33 *this = other;
34 }
35
36
BrowsingHistoryItem(const BMessage * archive)37 BrowsingHistoryItem::BrowsingHistoryItem(const BMessage* archive)
38 {
39 if (!archive)
40 return;
41 BMessage dateTimeArchive;
42 if (archive->FindMessage("date time", &dateTimeArchive) == B_OK)
43 fDateTime = BDateTime(&dateTimeArchive);
44 archive->FindString("url", &fURL);
45 archive->FindUInt32("invokations", &fInvokationCount);
46 }
47
48
~BrowsingHistoryItem()49 BrowsingHistoryItem::~BrowsingHistoryItem()
50 {
51 }
52
53
54 status_t
Archive(BMessage * archive) const55 BrowsingHistoryItem::Archive(BMessage* archive) const
56 {
57 if (!archive)
58 return B_BAD_VALUE;
59 BMessage dateTimeArchive;
60 status_t status = fDateTime.Archive(&dateTimeArchive);
61 if (status == B_OK)
62 status = archive->AddMessage("date time", &dateTimeArchive);
63 if (status == B_OK)
64 status = archive->AddString("url", fURL.String());
65 if (status == B_OK)
66 status = archive->AddUInt32("invokations", fInvokationCount);
67 return status;
68 }
69
70
71 BrowsingHistoryItem&
operator =(const BrowsingHistoryItem & other)72 BrowsingHistoryItem::operator=(const BrowsingHistoryItem& other)
73 {
74 if (this == &other)
75 return *this;
76
77 fURL = other.fURL;
78 fDateTime = other.fDateTime;
79 fInvokationCount = other.fInvokationCount;
80
81 return *this;
82 }
83
84
85 bool
operator ==(const BrowsingHistoryItem & other) const86 BrowsingHistoryItem::operator==(const BrowsingHistoryItem& other) const
87 {
88 if (this == &other)
89 return true;
90
91 return fURL == other.fURL && fDateTime == other.fDateTime
92 && fInvokationCount == other.fInvokationCount;
93 }
94
95
96 bool
operator !=(const BrowsingHistoryItem & other) const97 BrowsingHistoryItem::operator!=(const BrowsingHistoryItem& other) const
98 {
99 return !(*this == other);
100 }
101
102
103 bool
operator <(const BrowsingHistoryItem & other) const104 BrowsingHistoryItem::operator<(const BrowsingHistoryItem& other) const
105 {
106 if (this == &other)
107 return false;
108
109 return fDateTime < other.fDateTime || fURL < other.fURL;
110 }
111
112
113 bool
operator <=(const BrowsingHistoryItem & other) const114 BrowsingHistoryItem::operator<=(const BrowsingHistoryItem& other) const
115 {
116 return (*this == other) || (*this < other);
117 }
118
119
120 bool
operator >(const BrowsingHistoryItem & other) const121 BrowsingHistoryItem::operator>(const BrowsingHistoryItem& other) const
122 {
123 if (this == &other)
124 return false;
125
126 return fDateTime > other.fDateTime || fURL > other.fURL;
127 }
128
129
130 bool
operator >=(const BrowsingHistoryItem & other) const131 BrowsingHistoryItem::operator>=(const BrowsingHistoryItem& other) const
132 {
133 return (*this == other) || (*this > other);
134 }
135
136
137 void
Invoked()138 BrowsingHistoryItem::Invoked()
139 {
140 // Eventually, we may overflow...
141 uint32 count = fInvokationCount + 1;
142 if (count > fInvokationCount)
143 fInvokationCount = count;
144 fDateTime = BDateTime::CurrentDateTime(B_LOCAL_TIME);
145 }
146
147
148 // #pragma mark - BrowsingHistory
149
150
151 BrowsingHistory
152 BrowsingHistory::sDefaultInstance;
153
154
BrowsingHistory()155 BrowsingHistory::BrowsingHistory()
156 :
157 BLocker("browsing history"),
158 fHistoryItems(64),
159 fMaxHistoryItemAge(7),
160 fSettingsLoaded(false)
161 {
162 }
163
164
~BrowsingHistory()165 BrowsingHistory::~BrowsingHistory()
166 {
167 _SaveSettings();
168 _Clear();
169 }
170
171
172 /*static*/ BrowsingHistory*
DefaultInstance()173 BrowsingHistory::DefaultInstance()
174 {
175 if (sDefaultInstance.Lock()) {
176 sDefaultInstance._LoadSettings();
177 sDefaultInstance.Unlock();
178 }
179 return &sDefaultInstance;
180 }
181
182
183 bool
AddItem(const BrowsingHistoryItem & item)184 BrowsingHistory::AddItem(const BrowsingHistoryItem& item)
185 {
186 BAutolock _(this);
187
188 return _AddItem(item, false);
189 }
190
191
192 int32
CountItems() const193 BrowsingHistory::BrowsingHistory::CountItems() const
194 {
195 BAutolock _(const_cast<BrowsingHistory*>(this));
196
197 return fHistoryItems.CountItems();
198 }
199
200
201 BrowsingHistoryItem
HistoryItemAt(int32 index) const202 BrowsingHistory::HistoryItemAt(int32 index) const
203 {
204 BAutolock _(const_cast<BrowsingHistory*>(this));
205
206 BrowsingHistoryItem* existingItem = reinterpret_cast<BrowsingHistoryItem*>(
207 fHistoryItems.ItemAt(index));
208 if (!existingItem)
209 return BrowsingHistoryItem(BString());
210
211 return BrowsingHistoryItem(*existingItem);
212 }
213
214
215 void
Clear()216 BrowsingHistory::Clear()
217 {
218 BAutolock _(this);
219 _Clear();
220 _SaveSettings();
221 }
222
223
224 void
SetMaxHistoryItemAge(int32 days)225 BrowsingHistory::SetMaxHistoryItemAge(int32 days)
226 {
227 BAutolock _(this);
228 if (fMaxHistoryItemAge != days) {
229 fMaxHistoryItemAge = days;
230 _SaveSettings();
231 }
232 }
233
234
235 int32
MaxHistoryItemAge() const236 BrowsingHistory::MaxHistoryItemAge() const
237 {
238 return fMaxHistoryItemAge;
239 }
240
241
242 // #pragma mark - private
243
244
245 void
_Clear()246 BrowsingHistory::_Clear()
247 {
248 int32 count = CountItems();
249 for (int32 i = 0; i < count; i++) {
250 BrowsingHistoryItem* item = reinterpret_cast<BrowsingHistoryItem*>(
251 fHistoryItems.ItemAtFast(i));
252 delete item;
253 }
254 fHistoryItems.MakeEmpty();
255 }
256
257
258 bool
_AddItem(const BrowsingHistoryItem & item,bool internal)259 BrowsingHistory::_AddItem(const BrowsingHistoryItem& item, bool internal)
260 {
261 int32 count = CountItems();
262 int32 insertionIndex = count;
263 for (int32 i = 0; i < count; i++) {
264 BrowsingHistoryItem* existingItem
265 = reinterpret_cast<BrowsingHistoryItem*>(
266 fHistoryItems.ItemAtFast(i));
267 if (item.URL() == existingItem->URL()) {
268 if (!internal) {
269 existingItem->Invoked();
270 _SaveSettings();
271 }
272 return true;
273 }
274 if (item < *existingItem)
275 insertionIndex = i;
276 }
277 BrowsingHistoryItem* newItem = new(std::nothrow) BrowsingHistoryItem(item);
278 if (!newItem || !fHistoryItems.AddItem(newItem, insertionIndex)) {
279 delete newItem;
280 return false;
281 }
282
283 if (!internal) {
284 newItem->Invoked();
285 _SaveSettings();
286 }
287
288 return true;
289 }
290
291
292 void
_LoadSettings()293 BrowsingHistory::_LoadSettings()
294 {
295 if (fSettingsLoaded)
296 return;
297
298 fSettingsLoaded = true;
299
300 BFile settingsFile;
301 if (_OpenSettingsFile(settingsFile, B_READ_ONLY)) {
302 BMessage settingsArchive;
303 settingsArchive.Unflatten(&settingsFile);
304 if (settingsArchive.FindInt32("max history item age",
305 &fMaxHistoryItemAge) != B_OK) {
306 fMaxHistoryItemAge = 7;
307 }
308 BDateTime oldestAllowedDateTime
309 = BDateTime::CurrentDateTime(B_LOCAL_TIME);
310 oldestAllowedDateTime.Date().AddDays(-fMaxHistoryItemAge);
311
312 BMessage historyItemArchive;
313 for (int32 i = 0; settingsArchive.FindMessage("history item", i,
314 &historyItemArchive) == B_OK; i++) {
315 BrowsingHistoryItem item(&historyItemArchive);
316 if (oldestAllowedDateTime < item.DateTime())
317 _AddItem(item, true);
318 historyItemArchive.MakeEmpty();
319 }
320 }
321 }
322
323
324 void
_SaveSettings()325 BrowsingHistory::_SaveSettings()
326 {
327 BFile settingsFile;
328 if (_OpenSettingsFile(settingsFile,
329 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY)) {
330 BMessage settingsArchive;
331 settingsArchive.AddInt32("max history item age", fMaxHistoryItemAge);
332 BMessage historyItemArchive;
333 int32 count = CountItems();
334 for (int32 i = 0; i < count; i++) {
335 BrowsingHistoryItem item = HistoryItemAt(i);
336 if (item.Archive(&historyItemArchive) != B_OK)
337 break;
338 if (settingsArchive.AddMessage("history item",
339 &historyItemArchive) != B_OK) {
340 break;
341 }
342 historyItemArchive.MakeEmpty();
343 }
344 settingsArchive.Flatten(&settingsFile);
345 }
346 }
347
348
349 bool
_OpenSettingsFile(BFile & file,uint32 mode)350 BrowsingHistory::_OpenSettingsFile(BFile& file, uint32 mode)
351 {
352 BPath path;
353 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
354 || path.Append(kApplicationName) != B_OK
355 || path.Append("BrowsingHistory") != B_OK) {
356 return false;
357 }
358 return file.SetTo(path.Path(), mode) == B_OK;
359 }
360
361