xref: /haiku/src/apps/webpositive/BrowsingHistory.cpp (revision e688bf23d48bfd1216a0cacbdbda5e35a1bcd779)
1 /*
2  * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "BrowsingHistory.h"
29 
30 #include <new>
31 #include <stdio.h>
32 
33 #include <Autolock.h>
34 #include <Entry.h>
35 #include <File.h>
36 #include <FindDirectory.h>
37 #include <Message.h>
38 #include <Path.h>
39 
40 #include "BrowserApp.h"
41 
42 
43 BrowsingHistoryItem::BrowsingHistoryItem(const BString& url)
44 	:
45 	fURL(url),
46 	fDateTime(BDateTime::CurrentDateTime(B_LOCAL_TIME)),
47 	fInvokationCount(0)
48 {
49 }
50 
51 
52 BrowsingHistoryItem::BrowsingHistoryItem(const BrowsingHistoryItem& other)
53 {
54 	*this = other;
55 }
56 
57 
58 BrowsingHistoryItem::BrowsingHistoryItem(const BMessage* archive)
59 {
60 	if (!archive)
61 		return;
62 	BMessage dateTimeArchive;
63 	if (archive->FindMessage("date time", &dateTimeArchive) == B_OK)
64 		fDateTime = BDateTime(&dateTimeArchive);
65 	archive->FindString("url", &fURL);
66 	archive->FindUInt32("invokations", &fInvokationCount);
67 }
68 
69 
70 BrowsingHistoryItem::~BrowsingHistoryItem()
71 {
72 }
73 
74 
75 status_t
76 BrowsingHistoryItem::Archive(BMessage* archive) const
77 {
78 	if (!archive)
79 		return B_BAD_VALUE;
80 	BMessage dateTimeArchive;
81 	status_t status = fDateTime.Archive(&dateTimeArchive);
82 	if (status == B_OK)
83 		status = archive->AddMessage("date time", &dateTimeArchive);
84 	if (status == B_OK)
85 		status = archive->AddString("url", fURL.String());
86 	if (status == B_OK)
87 		status = archive->AddUInt32("invokations", fInvokationCount);
88 	return status;
89 }
90 
91 
92 BrowsingHistoryItem&
93 BrowsingHistoryItem::operator=(const BrowsingHistoryItem& other)
94 {
95 	if (this == &other)
96 		return *this;
97 
98 	fURL = other.fURL;
99 	fDateTime = other.fDateTime;
100 	fInvokationCount = other.fInvokationCount;
101 
102 	return *this;
103 }
104 
105 
106 bool
107 BrowsingHistoryItem::operator==(const BrowsingHistoryItem& other) const
108 {
109 	if (this == &other)
110 		return true;
111 
112 	return fURL == other.fURL && fDateTime == other.fDateTime
113 		&& fInvokationCount == other.fInvokationCount;
114 }
115 
116 
117 bool
118 BrowsingHistoryItem::operator!=(const BrowsingHistoryItem& other) const
119 {
120 	return !(*this == other);
121 }
122 
123 
124 bool
125 BrowsingHistoryItem::operator<(const BrowsingHistoryItem& other) const
126 {
127 	if (this == &other)
128 		return false;
129 
130 	return fDateTime < other.fDateTime || fURL < other.fURL;
131 }
132 
133 
134 bool
135 BrowsingHistoryItem::operator<=(const BrowsingHistoryItem& other) const
136 {
137 	return (*this == other) || (*this < other);
138 }
139 
140 
141 bool
142 BrowsingHistoryItem::operator>(const BrowsingHistoryItem& other) const
143 {
144 	if (this == &other)
145 		return false;
146 
147 	return fDateTime > other.fDateTime || fURL > other.fURL;
148 }
149 
150 
151 bool
152 BrowsingHistoryItem::operator>=(const BrowsingHistoryItem& other) const
153 {
154 	return (*this == other) || (*this > other);
155 }
156 
157 
158 void
159 BrowsingHistoryItem::Invoked()
160 {
161 	// Eventually, we may overflow...
162 	uint32 count = fInvokationCount + 1;
163 	if (count > fInvokationCount)
164 		fInvokationCount = count;
165 	fDateTime = BDateTime::CurrentDateTime(B_LOCAL_TIME);
166 }
167 
168 
169 // #pragma mark - BrowsingHistory
170 
171 
172 BrowsingHistory
173 BrowsingHistory::sDefaultInstance;
174 
175 
176 BrowsingHistory::BrowsingHistory()
177 	:
178 	BLocker("browsing history"),
179 	fHistoryItems(64),
180 	fMaxHistoryItemAge(7),
181 	fSettingsLoaded(false)
182 {
183 }
184 
185 
186 BrowsingHistory::~BrowsingHistory()
187 {
188 	_SaveSettings();
189 	_Clear();
190 }
191 
192 
193 /*static*/ BrowsingHistory*
194 BrowsingHistory::DefaultInstance()
195 {
196 	if (sDefaultInstance.Lock()) {
197 		sDefaultInstance._LoadSettings();
198 		sDefaultInstance.Unlock();
199 	}
200 	return &sDefaultInstance;
201 }
202 
203 
204 bool
205 BrowsingHistory::AddItem(const BrowsingHistoryItem& item)
206 {
207 	BAutolock _(this);
208 
209 	return _AddItem(item, false);
210 }
211 
212 
213 int32
214 BrowsingHistory::BrowsingHistory::CountItems() const
215 {
216 	BAutolock _(const_cast<BrowsingHistory*>(this));
217 
218 	return fHistoryItems.CountItems();
219 }
220 
221 
222 BrowsingHistoryItem
223 BrowsingHistory::HistoryItemAt(int32 index) const
224 {
225 	BAutolock _(const_cast<BrowsingHistory*>(this));
226 
227 	BrowsingHistoryItem* existingItem = reinterpret_cast<BrowsingHistoryItem*>(
228 		fHistoryItems.ItemAt(index));
229 	if (!existingItem)
230 		return BrowsingHistoryItem(BString());
231 
232 	return BrowsingHistoryItem(*existingItem);
233 }
234 
235 
236 void
237 BrowsingHistory::Clear()
238 {
239 	BAutolock _(this);
240 	_Clear();
241 	_SaveSettings();
242 }
243 
244 
245 void
246 BrowsingHistory::SetMaxHistoryItemAge(int32 days)
247 {
248 	BAutolock _(this);
249 	if (fMaxHistoryItemAge != days) {
250 		fMaxHistoryItemAge = days;
251 		_SaveSettings();
252 	}
253 }
254 
255 
256 int32
257 BrowsingHistory::MaxHistoryItemAge() const
258 {
259 	return fMaxHistoryItemAge;
260 }
261 
262 
263 // #pragma mark - private
264 
265 
266 void
267 BrowsingHistory::_Clear()
268 {
269 	int32 count = CountItems();
270 	for (int32 i = 0; i < count; i++) {
271 		BrowsingHistoryItem* item = reinterpret_cast<BrowsingHistoryItem*>(
272 			fHistoryItems.ItemAtFast(i));
273 		delete item;
274 	}
275 	fHistoryItems.MakeEmpty();
276 }
277 
278 
279 bool
280 BrowsingHistory::_AddItem(const BrowsingHistoryItem& item, bool internal)
281 {
282 	int32 count = CountItems();
283 	int32 insertionIndex = count;
284 	for (int32 i = 0; i < count; i++) {
285 		BrowsingHistoryItem* existingItem
286 			= reinterpret_cast<BrowsingHistoryItem*>(
287 			fHistoryItems.ItemAtFast(i));
288 		if (item.URL() == existingItem->URL()) {
289 			if (!internal) {
290 				existingItem->Invoked();
291 				_SaveSettings();
292 			}
293 			return true;
294 		}
295 		if (item < *existingItem)
296 			insertionIndex = i;
297 	}
298 	BrowsingHistoryItem* newItem = new(std::nothrow) BrowsingHistoryItem(item);
299 	if (!newItem || !fHistoryItems.AddItem(newItem, insertionIndex)) {
300 		delete newItem;
301 		return false;
302 	}
303 
304 	if (!internal) {
305 		newItem->Invoked();
306 		_SaveSettings();
307 	}
308 
309 	return true;
310 }
311 
312 
313 void
314 BrowsingHistory::_LoadSettings()
315 {
316 	if (fSettingsLoaded)
317 		return;
318 
319 	fSettingsLoaded = true;
320 
321 	BFile settingsFile;
322 	if (_OpenSettingsFile(settingsFile, B_READ_ONLY)) {
323 		BMessage settingsArchive;
324 		settingsArchive.Unflatten(&settingsFile);
325 		if (settingsArchive.FindInt32("max history item age",
326 				&fMaxHistoryItemAge) != B_OK) {
327 			fMaxHistoryItemAge = 7;
328 		}
329 		BDateTime oldestAllowedDateTime
330 			= BDateTime::CurrentDateTime(B_LOCAL_TIME);
331 		oldestAllowedDateTime.Date().AddDays(-fMaxHistoryItemAge);
332 
333 		BMessage historyItemArchive;
334 		for (int32 i = 0; settingsArchive.FindMessage("history item", i,
335 				&historyItemArchive) == B_OK; i++) {
336 			BrowsingHistoryItem item(&historyItemArchive);
337 			if (oldestAllowedDateTime < item.DateTime())
338 				_AddItem(item, true);
339 			historyItemArchive.MakeEmpty();
340 		}
341 	}
342 }
343 
344 
345 void
346 BrowsingHistory::_SaveSettings()
347 {
348 	BFile settingsFile;
349 	if (_OpenSettingsFile(settingsFile,
350 			B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY)) {
351 		BMessage settingsArchive;
352 		settingsArchive.AddInt32("max history item age", fMaxHistoryItemAge);
353 		BMessage historyItemArchive;
354 		int32 count = CountItems();
355 		for (int32 i = 0; i < count; i++) {
356 			BrowsingHistoryItem item = HistoryItemAt(i);
357 			if (item.Archive(&historyItemArchive) != B_OK)
358 				break;
359 			if (settingsArchive.AddMessage("history item",
360 					&historyItemArchive) != B_OK) {
361 				break;
362 			}
363 			historyItemArchive.MakeEmpty();
364 		}
365 		settingsArchive.Flatten(&settingsFile);
366 	}
367 }
368 
369 
370 bool
371 BrowsingHistory::_OpenSettingsFile(BFile& file, uint32 mode)
372 {
373 	BPath path;
374 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
375 		|| path.Append(kApplicationName) != B_OK
376 		|| path.Append("BrowsingHistory") != B_OK) {
377 		return false;
378 	}
379 	return file.SetTo(path.Path(), mode) == B_OK;
380 }
381 
382