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