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 43BrowsingHistoryItem::BrowsingHistoryItem(const BString& url) 44 : 45 fURL(url), 46 fDateTime(BDateTime::CurrentDateTime(B_LOCAL_TIME)), 47 fInvokationCount(0) 48{ 49} 50 51 52BrowsingHistoryItem::BrowsingHistoryItem(const BrowsingHistoryItem& other) 53{ 54 *this = other; 55} 56 57 58BrowsingHistoryItem::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 70BrowsingHistoryItem::~BrowsingHistoryItem() 71{ 72} 73 74 75status_t 76BrowsingHistoryItem::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 92BrowsingHistoryItem& 93BrowsingHistoryItem::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 106bool 107BrowsingHistoryItem::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 117bool 118BrowsingHistoryItem::operator!=(const BrowsingHistoryItem& other) const 119{ 120 return !(*this == other); 121} 122 123 124bool 125BrowsingHistoryItem::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 134bool 135BrowsingHistoryItem::operator<=(const BrowsingHistoryItem& other) const 136{ 137 return (*this == other) || (*this < other); 138} 139 140 141bool 142BrowsingHistoryItem::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 151bool 152BrowsingHistoryItem::operator>=(const BrowsingHistoryItem& other) const 153{ 154 return (*this == other) || (*this > other); 155} 156 157 158void 159BrowsingHistoryItem::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 172BrowsingHistory 173BrowsingHistory::sDefaultInstance; 174 175 176BrowsingHistory::BrowsingHistory() 177 : 178 BLocker("browsing history"), 179 fHistoryItems(64), 180 fMaxHistoryItemAge(7), 181 fSettingsLoaded(false) 182{ 183} 184 185 186BrowsingHistory::~BrowsingHistory() 187{ 188 _SaveSettings(); 189 _Clear(); 190} 191 192 193/*static*/ BrowsingHistory* 194BrowsingHistory::DefaultInstance() 195{ 196 if (sDefaultInstance.Lock()) { 197 sDefaultInstance._LoadSettings(); 198 sDefaultInstance.Unlock(); 199 } 200 return &sDefaultInstance; 201} 202 203 204bool 205BrowsingHistory::AddItem(const BrowsingHistoryItem& item) 206{ 207 BAutolock _(this); 208 209 return _AddItem(item, false); 210} 211 212 213int32 214BrowsingHistory::BrowsingHistory::CountItems() const 215{ 216 BAutolock _(const_cast<BrowsingHistory*>(this)); 217 218 return fHistoryItems.CountItems(); 219} 220 221 222BrowsingHistoryItem 223BrowsingHistory::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 236void 237BrowsingHistory::Clear() 238{ 239 BAutolock _(this); 240 _Clear(); 241 _SaveSettings(); 242} 243 244 245void 246BrowsingHistory::SetMaxHistoryItemAge(int32 days) 247{ 248 BAutolock _(this); 249 if (fMaxHistoryItemAge != days) { 250 fMaxHistoryItemAge = days; 251 _SaveSettings(); 252 } 253} 254 255 256int32 257BrowsingHistory::MaxHistoryItemAge() const 258{ 259 return fMaxHistoryItemAge; 260} 261 262 263// #pragma mark - private 264 265 266void 267BrowsingHistory::_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 279bool 280BrowsingHistory::_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 313void 314BrowsingHistory::_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 345void 346BrowsingHistory::_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 370bool 371BrowsingHistory::_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