1 /* 2 * ReactOS Explorer 3 * 4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 /* 22 Implements a class that keeps track of a PIDL history and allows 23 navigation forward and backward. This really should be in shdocvw, but it 24 is not registered for external instantiation, and the entire IBrowserService 25 hierarchy that normally spans browseui and shdocvw are collapsed into one 26 hierarchy in browseui, so I am moving it to browseui for now. If someone 27 decides to refactor code later, it wouldn't be difficult to move it. 28 29 TODO: 30 ****Does original travel log update the current item in the Travel method before or after calling ITravelEntry::Invoke? 31 ****Change to load maximum size from registry 32 ****Add code to track current size 33 ****Fix InsertMenuEntries to not exceed limit of menu item ids provided. Perhaps the method should try to be intelligent and if there are 34 too many items, center around the current item? Could cause dispatch problems... 35 ****Move tool tip text templates to resources 36 **Simplify code in InsertMenuEntries 37 Implement UpdateExternal 38 Implement FindTravelEntry 39 Implement Clone 40 Implement Revert 41 42 */ 43 #include "precomp.h" 44 45 class CTravelEntry : 46 public CComObjectRootEx<CComMultiThreadModelNoCS>, 47 public ITravelEntry 48 { 49 public: 50 CTravelEntry *fNextEntry; 51 CTravelEntry *fPreviousEntry; 52 private: 53 LPITEMIDLIST fPIDL; 54 HGLOBAL fPersistState; 55 public: 56 CTravelEntry(); 57 ~CTravelEntry(); 58 HRESULT GetToolTipText(IUnknown *punk, LPWSTR pwzText) const; 59 long GetSize() const; 60 61 // *** ITravelEntry methods *** 62 virtual HRESULT STDMETHODCALLTYPE Invoke(IUnknown *punk); 63 virtual HRESULT STDMETHODCALLTYPE Update(IUnknown *punk, BOOL fIsLocalAnchor); 64 virtual HRESULT STDMETHODCALLTYPE GetPidl(LPITEMIDLIST *ppidl); 65 66 BEGIN_COM_MAP(CTravelEntry) 67 COM_INTERFACE_ENTRY_IID(IID_ITravelEntry, ITravelEntry) 68 END_COM_MAP() 69 }; 70 71 class CTravelLog : 72 public CComObjectRootEx<CComMultiThreadModelNoCS>, 73 public ITravelLog 74 { 75 private: 76 CTravelEntry *fListHead; 77 CTravelEntry *fListTail; 78 CTravelEntry *fCurrentEntry; 79 long fMaximumSize; 80 long fCurrentSize; 81 unsigned long fEntryCount; 82 public: 83 CTravelLog(); 84 ~CTravelLog(); 85 HRESULT Initialize(); 86 HRESULT FindRelativeEntry(int offset, CTravelEntry **foundEntry); 87 void DeleteChain(CTravelEntry *startHere); 88 void AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry); 89 public: 90 91 // *** ITravelLog methods *** 92 virtual HRESULT STDMETHODCALLTYPE AddEntry(IUnknown *punk, BOOL fIsLocalAnchor); 93 virtual HRESULT STDMETHODCALLTYPE UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor); 94 virtual HRESULT STDMETHODCALLTYPE UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext); 95 virtual HRESULT STDMETHODCALLTYPE Travel(IUnknown *punk, int iOffset); 96 virtual HRESULT STDMETHODCALLTYPE GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte); 97 virtual HRESULT STDMETHODCALLTYPE FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte); 98 virtual HRESULT STDMETHODCALLTYPE GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText); 99 virtual HRESULT STDMETHODCALLTYPE InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags); 100 virtual HRESULT STDMETHODCALLTYPE Clone(ITravelLog **pptl); 101 virtual DWORD STDMETHODCALLTYPE CountEntries(IUnknown *punk); 102 virtual HRESULT STDMETHODCALLTYPE Revert(); 103 104 BEGIN_COM_MAP(CTravelLog) 105 COM_INTERFACE_ENTRY_IID(IID_ITravelLog, ITravelLog) 106 END_COM_MAP() 107 }; 108 109 CTravelEntry::CTravelEntry() 110 { 111 fNextEntry = NULL; 112 fPreviousEntry = NULL; 113 fPIDL = NULL; 114 fPersistState = NULL; 115 } 116 117 CTravelEntry::~CTravelEntry() 118 { 119 ILFree(fPIDL); 120 GlobalFree(fPersistState); 121 } 122 123 HRESULT CTravelEntry::GetToolTipText(IUnknown *punk, LPWSTR pwzText) const 124 { 125 HRESULT hResult; 126 127 hResult = ILGetDisplayNameEx(NULL, fPIDL, pwzText, ILGDN_NORMAL); 128 if (FAILED(hResult)) 129 return hResult; 130 return S_OK; 131 } 132 133 long CTravelEntry::GetSize() const 134 { 135 return 0; 136 } 137 138 HRESULT STDMETHODCALLTYPE CTravelEntry::Invoke(IUnknown *punk) 139 { 140 CComPtr<IPersistHistory> persistHistory; 141 CComPtr<IStream> globalStream; 142 HRESULT hResult; 143 144 hResult = punk->QueryInterface(IID_IPersistHistory, (void **)&persistHistory); 145 if (FAILED(hResult)) 146 return hResult; 147 hResult = CreateStreamOnHGlobal(fPersistState, FALSE, &globalStream); 148 if (FAILED(hResult)) 149 return hResult; 150 hResult = persistHistory->LoadHistory(globalStream, NULL); 151 if (FAILED(hResult)) 152 return hResult; 153 return S_OK; 154 } 155 156 HRESULT STDMETHODCALLTYPE CTravelEntry::Update(IUnknown *punk, BOOL fIsLocalAnchor) 157 { 158 CComPtr<ITravelLogClient> travelLogClient; 159 CComPtr<IPersistHistory> persistHistory; 160 CComPtr<IStream> globalStream; 161 WINDOWDATA windowData; 162 HGLOBAL globalStorage; 163 HRESULT hResult; 164 165 ILFree(fPIDL); 166 fPIDL = NULL; 167 GlobalFree(fPersistState); 168 fPersistState = NULL; 169 hResult = punk->QueryInterface(IID_ITravelLogClient, (void **)&travelLogClient); 170 if (FAILED(hResult)) 171 return hResult; 172 hResult = travelLogClient->GetWindowData(&windowData); 173 if (FAILED(hResult)) 174 return hResult; 175 fPIDL = windowData.pidl; 176 // TODO: Properly free the windowData 177 hResult = punk->QueryInterface(IID_IPersistHistory, (void **)&persistHistory); 178 if (FAILED(hResult)) 179 return hResult; 180 globalStorage = GlobalAlloc(GMEM_FIXED, 0); 181 hResult = CreateStreamOnHGlobal(globalStorage, FALSE, &globalStream); 182 if (FAILED(hResult)) 183 return hResult; 184 hResult = persistHistory->SaveHistory(globalStream); 185 if (FAILED(hResult)) 186 return hResult; 187 hResult = GetHGlobalFromStream(globalStream, &fPersistState); 188 if (FAILED(hResult)) 189 return hResult; 190 return S_OK; 191 } 192 193 HRESULT STDMETHODCALLTYPE CTravelEntry::GetPidl(LPITEMIDLIST *ppidl) 194 { 195 if (ppidl == NULL) 196 return E_POINTER; 197 *ppidl = ILClone(fPIDL); 198 if (*ppidl == NULL) 199 return E_OUTOFMEMORY; 200 return S_OK; 201 } 202 203 CTravelLog::CTravelLog() 204 { 205 fListHead = NULL; 206 fListTail = NULL; 207 fCurrentEntry = NULL; 208 fMaximumSize = 0; 209 fCurrentSize = 0; 210 fEntryCount = 0; 211 } 212 213 CTravelLog::~CTravelLog() 214 { 215 CTravelEntry *anEntry; 216 CTravelEntry *next; 217 218 anEntry = fListHead; 219 while (anEntry != NULL) 220 { 221 next = anEntry->fNextEntry; 222 anEntry->Release(); 223 anEntry = next; 224 } 225 } 226 227 HRESULT CTravelLog::Initialize() 228 { 229 fMaximumSize = 1024 * 1024; // TODO: change to read this from registry 230 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog 231 // MaxSize 232 return S_OK; 233 } 234 235 HRESULT CTravelLog::FindRelativeEntry(int offset, CTravelEntry **foundEntry) 236 { 237 CTravelEntry *curEntry; 238 239 *foundEntry = NULL; 240 curEntry = fCurrentEntry; 241 if (offset < 0) 242 { 243 while (offset < 0 && curEntry != NULL) 244 { 245 curEntry = curEntry->fPreviousEntry; 246 offset++; 247 } 248 } 249 else 250 { 251 while (offset > 0 && curEntry != NULL) 252 { 253 curEntry = curEntry->fNextEntry; 254 offset--; 255 } 256 } 257 if (curEntry == NULL) 258 return E_INVALIDARG; 259 *foundEntry = curEntry; 260 return S_OK; 261 } 262 263 void CTravelLog::DeleteChain(CTravelEntry *startHere) 264 { 265 CTravelEntry *saveNext; 266 long itemSize; 267 268 if (startHere->fPreviousEntry != NULL) 269 { 270 startHere->fPreviousEntry->fNextEntry = NULL; 271 fListTail = startHere->fPreviousEntry; 272 } 273 else 274 { 275 fListHead = NULL; 276 fListTail = NULL; 277 } 278 while (startHere != NULL) 279 { 280 saveNext = startHere->fNextEntry; 281 itemSize = startHere->GetSize(); 282 fCurrentSize -= itemSize; 283 startHere->Release(); 284 startHere = saveNext; 285 fEntryCount--; 286 } 287 } 288 289 void CTravelLog::AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry) 290 { 291 if (afterEntry == NULL) 292 { 293 fListHead = newEntry; 294 fListTail = newEntry; 295 } 296 else 297 { 298 newEntry->fNextEntry = afterEntry->fNextEntry; 299 afterEntry->fNextEntry = newEntry; 300 newEntry->fPreviousEntry = afterEntry; 301 if (newEntry->fNextEntry == NULL) 302 fListTail = newEntry; 303 else 304 newEntry->fNextEntry->fPreviousEntry = newEntry; 305 } 306 fEntryCount++; 307 } 308 309 HRESULT STDMETHODCALLTYPE CTravelLog::AddEntry(IUnknown *punk, BOOL fIsLocalAnchor) 310 { 311 CComObject<CTravelEntry> *newEntry; 312 long itemSize; 313 314 if (punk == NULL) 315 return E_INVALIDARG; 316 ATLTRY (newEntry = new CComObject<CTravelEntry>); 317 if (newEntry == NULL) 318 return E_OUTOFMEMORY; 319 newEntry->AddRef(); 320 if (fCurrentEntry != NULL && fCurrentEntry->fNextEntry != NULL) 321 DeleteChain(fCurrentEntry->fNextEntry); 322 AppendEntry(fCurrentEntry, newEntry); 323 itemSize = newEntry->GetSize(); 324 fCurrentSize += itemSize; 325 fCurrentEntry = newEntry; 326 return S_OK; 327 } 328 329 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor) 330 { 331 if (punk == NULL) 332 return E_INVALIDARG; 333 if (fCurrentEntry == NULL) 334 return E_UNEXPECTED; 335 return fCurrentEntry->Update(punk, fIsLocalAnchor); 336 } 337 338 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext) 339 { 340 return E_NOTIMPL; 341 } 342 343 HRESULT STDMETHODCALLTYPE CTravelLog::Travel(IUnknown *punk, int iOffset) 344 { 345 CTravelEntry *destinationEntry; 346 HRESULT hResult; 347 348 hResult = FindRelativeEntry(iOffset, &destinationEntry); 349 if (FAILED(hResult)) 350 return hResult; 351 fCurrentEntry = destinationEntry; 352 hResult = destinationEntry->Invoke(punk); 353 if (FAILED(hResult)) 354 return hResult; 355 return S_OK; 356 } 357 358 HRESULT STDMETHODCALLTYPE CTravelLog::GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte) 359 { 360 CTravelEntry *destinationEntry; 361 HRESULT hResult; 362 363 hResult = FindRelativeEntry(iOffset, &destinationEntry); 364 if (FAILED(hResult)) 365 return hResult; 366 return destinationEntry->QueryInterface(IID_ITravelEntry, (void **)ppte); 367 } 368 369 HRESULT STDMETHODCALLTYPE CTravelLog::FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte) 370 { 371 if (ppte == NULL) 372 return E_POINTER; 373 if (punk == NULL || pidl == NULL) 374 return E_INVALIDARG; 375 return E_NOTIMPL; 376 } 377 378 HRESULT STDMETHODCALLTYPE CTravelLog::GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText) 379 { 380 CTravelEntry *destinationEntry; 381 wchar_t tempString[MAX_PATH]; 382 wchar_t templateString[200]; 383 HRESULT hResult; 384 385 if (pwzText == NULL) 386 return E_POINTER; 387 if (punk == NULL || cchText == 0) 388 return E_INVALIDARG; 389 hResult = FindRelativeEntry(iOffset, &destinationEntry); 390 if (FAILED(hResult)) 391 return hResult; 392 hResult = destinationEntry->GetToolTipText(punk, tempString); 393 if (FAILED(hResult)) 394 return hResult; 395 if (iOffset < 0) 396 { 397 wcscpy(templateString, L"Back to %s"); 398 } 399 else 400 { 401 wcscpy(templateString, L"Forward to %s"); 402 } 403 _snwprintf(pwzText, cchText, templateString, tempString); 404 return S_OK; 405 } 406 407 static void FixAmpersands(wchar_t *buffer) 408 { 409 wchar_t tempBuffer[MAX_PATH * 2]; 410 wchar_t ch; 411 wchar_t *srcPtr; 412 wchar_t *dstPtr; 413 414 srcPtr = buffer; 415 dstPtr = tempBuffer; 416 while (*srcPtr != 0) 417 { 418 ch = *srcPtr++; 419 *dstPtr++ = ch; 420 if (ch == '&') 421 *dstPtr++ = '&'; 422 } 423 *dstPtr = 0; 424 wcscpy(buffer, tempBuffer); 425 } 426 427 HRESULT STDMETHODCALLTYPE CTravelLog::InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags) 428 { 429 CTravelEntry *currentItem; 430 MENUITEMINFO menuItemInfo; 431 wchar_t itemTextBuffer[MAX_PATH * 2]; 432 HRESULT hResult; 433 434 // TLMENUF_BACK - include back entries 435 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry 436 // TLMENUF_FORE - include next entries 437 // if fore+back, list from oldest to newest 438 // if back, list from newest to oldest 439 // if fore, list from newest to oldest 440 441 // don't forget to patch ampersands before adding to menu 442 if (punk == NULL) 443 return E_INVALIDARG; 444 if (idLast <= idFirst) 445 return E_INVALIDARG; 446 menuItemInfo.cbSize = sizeof(menuItemInfo); 447 menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING; 448 menuItemInfo.fType = MFT_STRING; 449 menuItemInfo.wID = idFirst; 450 menuItemInfo.fState = MFS_ENABLED; 451 menuItemInfo.dwTypeData = itemTextBuffer; 452 if ((dwFlags & TLMENUF_BACK) != 0) 453 { 454 if ((dwFlags & TLMENUF_FORE) != 0) 455 { 456 currentItem = fCurrentEntry; 457 if (currentItem != NULL) 458 { 459 while (currentItem->fPreviousEntry != NULL) 460 currentItem = currentItem->fPreviousEntry; 461 } 462 while (currentItem != fCurrentEntry) 463 { 464 hResult = currentItem->GetToolTipText(punk, itemTextBuffer); 465 if (SUCCEEDED(hResult)) 466 { 467 FixAmpersands(itemTextBuffer); 468 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo)) 469 { 470 nPos++; 471 menuItemInfo.wID++; 472 } 473 } 474 currentItem = currentItem->fNextEntry; 475 } 476 } 477 else 478 { 479 currentItem = fCurrentEntry; 480 if (currentItem != NULL) 481 currentItem = currentItem->fPreviousEntry; 482 while (currentItem != NULL) 483 { 484 hResult = currentItem->GetToolTipText(punk, itemTextBuffer); 485 if (SUCCEEDED(hResult)) 486 { 487 FixAmpersands(itemTextBuffer); 488 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo)) 489 { 490 nPos++; 491 menuItemInfo.wID++; 492 } 493 } 494 currentItem = currentItem->fPreviousEntry; 495 } 496 } 497 } 498 if ((dwFlags & TLMENUF_INCLUDECURRENT) != 0) 499 { 500 if (fCurrentEntry != NULL) 501 { 502 hResult = fCurrentEntry->GetToolTipText(punk, itemTextBuffer); 503 if (SUCCEEDED(hResult)) 504 { 505 FixAmpersands(itemTextBuffer); 506 if ((dwFlags & TLMENUF_CHECKCURRENT) != 0) 507 menuItemInfo.fState |= MFS_CHECKED; 508 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo)) 509 { 510 nPos++; 511 menuItemInfo.wID++; 512 } 513 menuItemInfo.fState &= ~MFS_CHECKED; 514 } 515 } 516 } 517 if ((dwFlags & TLMENUF_FORE) != 0) 518 { 519 currentItem = fCurrentEntry; 520 if (currentItem != NULL) 521 currentItem = currentItem->fNextEntry; 522 while (currentItem != NULL) 523 { 524 hResult = currentItem->GetToolTipText(punk, itemTextBuffer); 525 if (SUCCEEDED(hResult)) 526 { 527 FixAmpersands(itemTextBuffer); 528 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo)) 529 { 530 nPos++; 531 menuItemInfo.wID++; 532 } 533 } 534 currentItem = currentItem->fNextEntry; 535 } 536 } 537 return S_OK; 538 } 539 540 HRESULT STDMETHODCALLTYPE CTravelLog::Clone(ITravelLog **pptl) 541 { 542 if (pptl == NULL) 543 return E_POINTER; 544 *pptl = NULL; 545 // duplicate the log 546 return E_NOTIMPL; 547 } 548 549 DWORD STDMETHODCALLTYPE CTravelLog::CountEntries(IUnknown *punk) 550 { 551 if (punk == NULL) 552 return E_INVALIDARG; 553 return fEntryCount; 554 } 555 556 HRESULT STDMETHODCALLTYPE CTravelLog::Revert() 557 { 558 // remove the current entry? 559 return E_NOTIMPL; 560 } 561 562 HRESULT CreateTravelLog(REFIID riid, void **ppv) 563 { 564 CComObject<CTravelLog> *theTravelLog; 565 HRESULT hResult; 566 567 if (ppv == NULL) 568 return E_POINTER; 569 *ppv = NULL; 570 ATLTRY (theTravelLog = new CComObject<CTravelLog>); 571 if (theTravelLog == NULL) 572 return E_OUTOFMEMORY; 573 hResult = theTravelLog->QueryInterface (riid, (void **)ppv); 574 if (FAILED (hResult)) 575 { 576 delete theTravelLog; 577 return hResult; 578 } 579 hResult = theTravelLog->Initialize(); 580 if (FAILED(hResult)) 581 return hResult; 582 return S_OK; 583 } 584