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
22BrowsingHistoryItem::BrowsingHistoryItem(const BString& url)
23	:
24	fURL(url),
25	fDateTime(BDateTime::CurrentDateTime(B_LOCAL_TIME)),
26	fInvokationCount(0)
27{
28}
29
30
31BrowsingHistoryItem::BrowsingHistoryItem(const BrowsingHistoryItem& other)
32{
33	*this = other;
34}
35
36
37BrowsingHistoryItem::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
49BrowsingHistoryItem::~BrowsingHistoryItem()
50{
51}
52
53
54status_t
55BrowsingHistoryItem::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
71BrowsingHistoryItem&
72BrowsingHistoryItem::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
85bool
86BrowsingHistoryItem::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
96bool
97BrowsingHistoryItem::operator!=(const BrowsingHistoryItem& other) const
98{
99	return !(*this == other);
100}
101
102
103bool
104BrowsingHistoryItem::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
113bool
114BrowsingHistoryItem::operator<=(const BrowsingHistoryItem& other) const
115{
116	return (*this == other) || (*this < other);
117}
118
119
120bool
121BrowsingHistoryItem::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
130bool
131BrowsingHistoryItem::operator>=(const BrowsingHistoryItem& other) const
132{
133	return (*this == other) || (*this > other);
134}
135
136
137void
138BrowsingHistoryItem::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
151BrowsingHistory
152BrowsingHistory::sDefaultInstance;
153
154
155BrowsingHistory::BrowsingHistory()
156	:
157	BLocker("browsing history"),
158	fHistoryItems(64),
159	fMaxHistoryItemAge(7),
160	fSettingsLoaded(false)
161{
162}
163
164
165BrowsingHistory::~BrowsingHistory()
166{
167	_SaveSettings();
168	_Clear();
169}
170
171
172/*static*/ BrowsingHistory*
173BrowsingHistory::DefaultInstance()
174{
175	if (sDefaultInstance.Lock()) {
176		sDefaultInstance._LoadSettings();
177		sDefaultInstance.Unlock();
178	}
179	return &sDefaultInstance;
180}
181
182
183bool
184BrowsingHistory::AddItem(const BrowsingHistoryItem& item)
185{
186	BAutolock _(this);
187
188	return _AddItem(item, false);
189}
190
191
192int32
193BrowsingHistory::BrowsingHistory::CountItems() const
194{
195	BAutolock _(const_cast<BrowsingHistory*>(this));
196
197	return fHistoryItems.CountItems();
198}
199
200
201BrowsingHistoryItem
202BrowsingHistory::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
215void
216BrowsingHistory::Clear()
217{
218	BAutolock _(this);
219	_Clear();
220	_SaveSettings();
221}
222
223
224void
225BrowsingHistory::SetMaxHistoryItemAge(int32 days)
226{
227	BAutolock _(this);
228	if (fMaxHistoryItemAge != days) {
229		fMaxHistoryItemAge = days;
230		_SaveSettings();
231	}
232}
233
234
235int32
236BrowsingHistory::MaxHistoryItemAge() const
237{
238	return fMaxHistoryItemAge;
239}
240
241
242// #pragma mark - private
243
244
245void
246BrowsingHistory::_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
258bool
259BrowsingHistory::_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
292void
293BrowsingHistory::_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
324void
325BrowsingHistory::_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
349bool
350BrowsingHistory::_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