[BROWSEUI]
[reactos.git] / reactos / dll / win32 / browseui / travellog.cpp
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
44 #include "precomp.h"
45
46 class CTravelEntry :
47 public CComObjectRootEx<CComMultiThreadModelNoCS>,
48 public ITravelEntry
49 {
50 public:
51 CTravelEntry *fNextEntry;
52 CTravelEntry *fPreviousEntry;
53 private:
54 LPITEMIDLIST fPIDL;
55 HGLOBAL fPersistState;
56 public:
57 CTravelEntry();
58 ~CTravelEntry();
59 HRESULT GetToolTipText(IUnknown *punk, LPWSTR pwzText) const;
60 long GetSize() const;
61
62 // *** ITravelEntry methods ***
63 virtual HRESULT STDMETHODCALLTYPE Invoke(IUnknown *punk);
64 virtual HRESULT STDMETHODCALLTYPE Update(IUnknown *punk, BOOL fIsLocalAnchor);
65 virtual HRESULT STDMETHODCALLTYPE GetPidl(LPITEMIDLIST *ppidl);
66
67 BEGIN_COM_MAP(CTravelEntry)
68 COM_INTERFACE_ENTRY_IID(IID_ITravelEntry, ITravelEntry)
69 END_COM_MAP()
70 };
71
72 class CTravelLog :
73 public CComObjectRootEx<CComMultiThreadModelNoCS>,
74 public ITravelLog
75 {
76 private:
77 CTravelEntry *fListHead;
78 CTravelEntry *fListTail;
79 CTravelEntry *fCurrentEntry;
80 long fMaximumSize;
81 long fCurrentSize;
82 unsigned long fEntryCount;
83 public:
84 CTravelLog();
85 ~CTravelLog();
86 HRESULT Initialize();
87 HRESULT FindRelativeEntry(int offset, CTravelEntry **foundEntry);
88 void DeleteChain(CTravelEntry *startHere);
89 void AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry);
90 public:
91
92 // *** ITravelLog methods ***
93 virtual HRESULT STDMETHODCALLTYPE AddEntry(IUnknown *punk, BOOL fIsLocalAnchor);
94 virtual HRESULT STDMETHODCALLTYPE UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor);
95 virtual HRESULT STDMETHODCALLTYPE UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext);
96 virtual HRESULT STDMETHODCALLTYPE Travel(IUnknown *punk, int iOffset);
97 virtual HRESULT STDMETHODCALLTYPE GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte);
98 virtual HRESULT STDMETHODCALLTYPE FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte);
99 virtual HRESULT STDMETHODCALLTYPE GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText);
100 virtual HRESULT STDMETHODCALLTYPE InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags);
101 virtual HRESULT STDMETHODCALLTYPE Clone(ITravelLog **pptl);
102 virtual DWORD STDMETHODCALLTYPE CountEntries(IUnknown *punk);
103 virtual HRESULT STDMETHODCALLTYPE Revert();
104
105 BEGIN_COM_MAP(CTravelLog)
106 COM_INTERFACE_ENTRY_IID(IID_ITravelLog, ITravelLog)
107 END_COM_MAP()
108 };
109
110 CTravelEntry::CTravelEntry()
111 {
112 fNextEntry = NULL;
113 fPreviousEntry = NULL;
114 fPIDL = NULL;
115 fPersistState = NULL;
116 }
117
118 CTravelEntry::~CTravelEntry()
119 {
120 ILFree(fPIDL);
121 GlobalFree(fPersistState);
122 }
123
124 HRESULT CTravelEntry::GetToolTipText(IUnknown *punk, LPWSTR pwzText) const
125 {
126 HRESULT hResult;
127
128 hResult = ILGetDisplayNameEx(NULL, fPIDL, pwzText, ILGDN_NORMAL) ? S_OK : S_FALSE;
129 if (FAILED_UNEXPECTEDLY(hResult))
130 return hResult;
131
132 return S_OK;
133 }
134
135 long CTravelEntry::GetSize() const
136 {
137 return 0;
138 }
139
140 HRESULT STDMETHODCALLTYPE CTravelEntry::Invoke(IUnknown *punk)
141 {
142 CComPtr<IPersistHistory> persistHistory;
143 CComPtr<IStream> globalStream;
144 HRESULT hResult;
145
146 TRACE("CTravelEntry::Invoke for IUnknown punk=%p\n", punk);
147
148 hResult = punk->QueryInterface(IID_PPV_ARG(IPersistHistory, &persistHistory));
149 if (FAILED_UNEXPECTEDLY(hResult))
150 return hResult;
151 hResult = CreateStreamOnHGlobal(fPersistState, FALSE, &globalStream);
152 if (FAILED_UNEXPECTEDLY(hResult))
153 return hResult;
154 hResult = persistHistory->LoadHistory(globalStream, NULL);
155 if (FAILED_UNEXPECTEDLY(hResult))
156 return hResult;
157 return S_OK;
158 }
159
160 HRESULT STDMETHODCALLTYPE CTravelEntry::Update(IUnknown *punk, BOOL fIsLocalAnchor)
161 {
162 CComPtr<ITravelLogClient> travelLogClient;
163 CComPtr<IPersistHistory> persistHistory;
164 CComPtr<IStream> globalStream;
165 WINDOWDATA windowData;
166 HRESULT hResult;
167
168 TRACE("CTravelEntry::Update for IUnknown punk=%p, fIsLocalAnchor=%s\n", punk, fIsLocalAnchor ? "TRUE" : "FALSE");
169
170
171 WCHAR wch[MAX_PATH * 2];
172 GetToolTipText(punk, wch);
173 TRACE("Updating entry with display name: %S\n", wch);
174
175 ILFree(fPIDL);
176 fPIDL = NULL;
177 GlobalFree(fPersistState);
178 fPersistState = NULL;
179 hResult = punk->QueryInterface(IID_PPV_ARG(ITravelLogClient, &travelLogClient));
180 if (FAILED_UNEXPECTEDLY(hResult))
181 return hResult;
182 hResult = punk->QueryInterface(IID_PPV_ARG(IPersistHistory, &persistHistory));
183 if (FAILED_UNEXPECTEDLY(hResult))
184 return hResult;
185 hResult = CreateStreamOnHGlobal(NULL, FALSE, &globalStream);
186 if (FAILED_UNEXPECTEDLY(hResult))
187 return hResult;
188 hResult = persistHistory->SaveHistory(globalStream);
189 if (FAILED_UNEXPECTEDLY(hResult))
190 return hResult;
191 hResult = travelLogClient->GetWindowData(globalStream, &windowData);
192 if (FAILED_UNEXPECTEDLY(hResult))
193 return hResult;
194 fPIDL = windowData.pidl;
195 // TODO: Properly free the windowData
196 hResult = GetHGlobalFromStream(globalStream, &fPersistState);
197 if (FAILED_UNEXPECTEDLY(hResult))
198 return hResult;
199
200 GetToolTipText(punk, wch);
201 TRACE("Updated entry display name is now: %S\n", wch);
202
203 return S_OK;
204 }
205
206 HRESULT STDMETHODCALLTYPE CTravelEntry::GetPidl(LPITEMIDLIST *ppidl)
207 {
208 if (ppidl == NULL)
209 return E_POINTER;
210 *ppidl = ILClone(fPIDL);
211 if (*ppidl == NULL)
212 return E_OUTOFMEMORY;
213
214 TRACE("CTravelEntry::GetPidl returning ppidl=%p\n", *ppidl);
215
216 return S_OK;
217 }
218
219 CTravelLog::CTravelLog()
220 {
221 fListHead = NULL;
222 fListTail = NULL;
223 fCurrentEntry = NULL;
224 fMaximumSize = 0;
225 fCurrentSize = 0;
226 fEntryCount = 0;
227 TRACE("CTravelLog created\n");
228 }
229
230 CTravelLog::~CTravelLog()
231 {
232 CTravelEntry *anEntry;
233 CTravelEntry *next;
234
235 anEntry = fListHead;
236 while (anEntry != NULL)
237 {
238 next = anEntry->fNextEntry;
239 anEntry->Release();
240 anEntry = next;
241 }
242 TRACE("CTravelLog destroyed\n");
243 }
244
245 HRESULT CTravelLog::Initialize()
246 {
247 FIXME("CTravelLog::Initialize using hardcoded fMaximumSize.\n");
248 fMaximumSize = 1024 * 1024; // TODO: change to read this from registry
249 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog
250 // MaxSize
251 return S_OK;
252 }
253
254 HRESULT CTravelLog::FindRelativeEntry(int _offset, CTravelEntry **foundEntry)
255 {
256 CTravelEntry *curEntry;
257 int offset = _offset;
258
259 if (foundEntry == NULL)
260 return E_INVALIDARG;
261
262 *foundEntry = NULL;
263 curEntry = fCurrentEntry;
264 if (offset < 0)
265 {
266 while (offset < 0 && curEntry != NULL)
267 {
268 curEntry = curEntry->fPreviousEntry;
269 offset++;
270 }
271 }
272 else
273 {
274 while (offset > 0 && curEntry != NULL)
275 {
276 curEntry = curEntry->fNextEntry;
277 offset--;
278 }
279 }
280 if (curEntry == NULL)
281 return E_INVALIDARG;
282
283 *foundEntry = curEntry;
284
285 TRACE("CTravelLog::FindRelativeEntry for offset %d, returning %p\n", offset, *foundEntry);
286
287 return S_OK;
288 }
289
290 void CTravelLog::DeleteChain(CTravelEntry *startHere)
291 {
292 CTravelEntry *saveNext;
293 long itemSize;
294
295 TRACE("CTravelLog::DeleteChain deleting chain starting at %p\n", startHere);
296
297 long startEntryCount = fEntryCount;
298
299 if (startHere->fPreviousEntry != NULL)
300 {
301 startHere->fPreviousEntry->fNextEntry = NULL;
302 fListTail = startHere->fPreviousEntry;
303 }
304 else
305 {
306 fListHead = NULL;
307 fListTail = NULL;
308 }
309 while (startHere != NULL)
310 {
311 saveNext = startHere->fNextEntry;
312 itemSize = startHere->GetSize();
313 fCurrentSize -= itemSize;
314 startHere->Release();
315 startHere = saveNext;
316 fEntryCount--;
317 }
318
319 TRACE("CTravelLog::DeleteChain chain of %d items deleted\n", startEntryCount - fEntryCount);
320 }
321
322 void CTravelLog::AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry)
323 {
324 if (afterEntry == NULL)
325 {
326 TRACE("CTravelLog::AppendEntry appending %p after NULL. Resetting head and tail\n", newEntry);
327 fListHead = newEntry;
328 fListTail = newEntry;
329 }
330 else
331 {
332 TRACE("CTravelLog::AppendEntry appending %p after %p\n", newEntry, afterEntry);
333 newEntry->fNextEntry = afterEntry->fNextEntry;
334 afterEntry->fNextEntry = newEntry;
335 newEntry->fPreviousEntry = afterEntry;
336 if (newEntry->fNextEntry == NULL)
337 fListTail = newEntry;
338 else
339 newEntry->fNextEntry->fPreviousEntry = newEntry;
340 }
341 fEntryCount++;
342 }
343
344 HRESULT STDMETHODCALLTYPE CTravelLog::AddEntry(IUnknown *punk, BOOL fIsLocalAnchor)
345 {
346 CComObject<CTravelEntry> *newEntry;
347 long itemSize;
348
349 TRACE("CTravelLog::AddEntry for IUnknown punk=%p, fIsLocalAnchor=%s\n", punk, fIsLocalAnchor ? "TRUE" : "FALSE");
350
351 if (punk == NULL)
352 return E_INVALIDARG;
353 ATLTRY (newEntry = new CComObject<CTravelEntry>);
354 if (newEntry == NULL)
355 return E_OUTOFMEMORY;
356 newEntry->AddRef();
357 if (fCurrentEntry != NULL && fCurrentEntry->fNextEntry != NULL)
358 DeleteChain(fCurrentEntry->fNextEntry);
359 AppendEntry(fCurrentEntry, newEntry);
360 itemSize = newEntry->GetSize();
361 fCurrentSize += itemSize;
362 fCurrentEntry = newEntry;
363 return S_OK;
364 }
365
366 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor)
367 {
368 if (punk == NULL)
369 return E_INVALIDARG;
370 if (fCurrentEntry == NULL)
371 return E_UNEXPECTED;
372 return fCurrentEntry->Update(punk, fIsLocalAnchor);
373 }
374
375 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext)
376 {
377 return E_NOTIMPL;
378 }
379
380 HRESULT STDMETHODCALLTYPE CTravelLog::Travel(IUnknown *punk, int iOffset)
381 {
382 CTravelEntry *destinationEntry;
383 HRESULT hResult;
384
385 TRACE("CTravelLog::Travel for IUnknown punk=%p at offset=%d\n", punk, iOffset);
386
387 hResult = FindRelativeEntry(iOffset, &destinationEntry);
388 if (FAILED_UNEXPECTEDLY(hResult))
389 return hResult;
390 fCurrentEntry = destinationEntry;
391 hResult = destinationEntry->Invoke(punk);
392 if (FAILED_UNEXPECTEDLY(hResult))
393 return hResult;
394 return S_OK;
395 }
396
397 HRESULT STDMETHODCALLTYPE CTravelLog::GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte)
398 {
399 CTravelEntry *destinationEntry;
400 HRESULT hResult;
401
402 hResult = FindRelativeEntry(iOffset, &destinationEntry);
403 if (FAILED_UNEXPECTEDLY(hResult))
404 return hResult;
405 hResult = destinationEntry->QueryInterface(IID_PPV_ARG(ITravelEntry, ppte));
406 if (FAILED_UNEXPECTEDLY(hResult))
407 return hResult;
408
409 TRACE("CTravelLog::GetTravelEntry for IUnknown punk=%p at offset=%d returning %p\n", punk, iOffset, *ppte);
410
411 return hResult;
412 }
413
414 HRESULT STDMETHODCALLTYPE CTravelLog::FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte)
415 {
416 if (ppte == NULL)
417 return E_POINTER;
418 if (punk == NULL || pidl == NULL)
419 return E_INVALIDARG;
420
421 UNIMPLEMENTED;
422
423 return E_NOTIMPL;
424 }
425
426 HRESULT STDMETHODCALLTYPE CTravelLog::GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText)
427 {
428 CTravelEntry *destinationEntry;
429 wchar_t tempString[MAX_PATH];
430 wchar_t templateString[200];
431 wchar_t *resourceString;
432 HRESULT hResult;
433
434 if (pwzText == NULL)
435 return E_POINTER;
436 if (punk == NULL || cchText == 0)
437 return E_INVALIDARG;
438 hResult = FindRelativeEntry(iOffset, &destinationEntry);
439 if (FAILED_UNEXPECTEDLY(hResult))
440 return hResult;
441 hResult = destinationEntry->GetToolTipText(punk, tempString);
442 if (FAILED_UNEXPECTEDLY(hResult))
443 return hResult;
444 if (iOffset < 0)
445 {
446 hResult = LoadStringW(_AtlBaseModule.GetResourceInstance(),
447 IDS_BACK, (wchar_t*)&resourceString, 0);
448 if (FAILED_UNEXPECTEDLY(hResult))
449 return hResult;
450 }
451 else
452 {
453 hResult = LoadStringW(_AtlBaseModule.GetResourceInstance(),
454 IDS_FORWARD, (wchar_t*)&resourceString, 0);
455 if (FAILED_UNEXPECTEDLY(hResult))
456 return hResult;
457 }
458 wcscpy(templateString, resourceString);
459 _snwprintf(pwzText, cchText, templateString, tempString);
460
461 TRACE("CTravelLog::GetToolTipText for IUnknown punk=%p at offset=%d returning L\"%S\"\n", punk, iOffset, pwzText);
462
463 return S_OK;
464 }
465
466 static void FixAmpersands(wchar_t *buffer)
467 {
468 wchar_t tempBuffer[MAX_PATH * 2];
469 wchar_t ch;
470 wchar_t *srcPtr;
471 wchar_t *dstPtr;
472
473 srcPtr = buffer;
474 dstPtr = tempBuffer;
475 while (*srcPtr != 0)
476 {
477 ch = *srcPtr++;
478 *dstPtr++ = ch;
479 if (ch == '&')
480 *dstPtr++ = '&';
481 }
482 *dstPtr = 0;
483 wcscpy(buffer, tempBuffer);
484 }
485
486 HRESULT STDMETHODCALLTYPE CTravelLog::InsertMenuEntries(IUnknown *punk, HMENU hmenu,
487 int nPos, int idFirst, int idLast, DWORD dwFlags)
488 {
489 CTravelEntry *currentItem;
490 MENUITEMINFO menuItemInfo;
491 wchar_t itemTextBuffer[MAX_PATH * 2];
492 HRESULT hResult;
493
494 TRACE("CTravelLog::InsertMenuEntries for IUnknown punk=%p, nPos=%d, idFirst=%d, idLast=%d\n", punk);
495
496 // TLMENUF_BACK - include back entries
497 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry
498 // TLMENUF_FORE - include next entries
499 // if fore+back, list from oldest to newest
500 // if back, list from newest to oldest
501 // if fore, list from newest to oldest
502
503 // don't forget to patch ampersands before adding to menu
504 if (punk == NULL)
505 return E_INVALIDARG;
506 if (idLast <= idFirst)
507 return E_INVALIDARG;
508 menuItemInfo.cbSize = sizeof(menuItemInfo);
509 menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING;
510 menuItemInfo.fType = MFT_STRING;
511 menuItemInfo.wID = idFirst;
512 menuItemInfo.fState = MFS_ENABLED;
513 menuItemInfo.dwTypeData = itemTextBuffer;
514 if ((dwFlags & TLMENUF_BACK) != 0)
515 {
516 if ((dwFlags & TLMENUF_FORE) != 0)
517 {
518 currentItem = fCurrentEntry;
519 if (currentItem != NULL)
520 {
521 while (currentItem->fPreviousEntry != NULL)
522 currentItem = currentItem->fPreviousEntry;
523 }
524 while (currentItem != fCurrentEntry)
525 {
526 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
527 if (SUCCEEDED(hResult))
528 {
529 FixAmpersands(itemTextBuffer);
530 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID);
531 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
532 {
533 nPos++;
534 menuItemInfo.wID++;
535 }
536 }
537 currentItem = currentItem->fNextEntry;
538 }
539 }
540 else
541 {
542 currentItem = fCurrentEntry;
543 if (currentItem != NULL)
544 currentItem = currentItem->fPreviousEntry;
545 while (currentItem != NULL)
546 {
547 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
548 if (SUCCEEDED(hResult))
549 {
550 FixAmpersands(itemTextBuffer);
551 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID);
552 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
553 {
554 nPos++;
555 menuItemInfo.wID++;
556 }
557 }
558 currentItem = currentItem->fPreviousEntry;
559 }
560 }
561 }
562 if ((dwFlags & TLMENUF_INCLUDECURRENT) != 0)
563 {
564 if (fCurrentEntry != NULL)
565 {
566 hResult = fCurrentEntry->GetToolTipText(punk, itemTextBuffer);
567 if (SUCCEEDED(hResult))
568 {
569 FixAmpersands(itemTextBuffer);
570 if ((dwFlags & TLMENUF_CHECKCURRENT) != 0)
571 menuItemInfo.fState |= MFS_CHECKED;
572 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID);
573 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
574 {
575 nPos++;
576 menuItemInfo.wID++;
577 }
578 menuItemInfo.fState &= ~MFS_CHECKED;
579 }
580 }
581 }
582 if ((dwFlags & TLMENUF_FORE) != 0)
583 {
584 currentItem = fCurrentEntry;
585 if (currentItem != NULL)
586 currentItem = currentItem->fNextEntry;
587 while (currentItem != NULL)
588 {
589 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
590 if (SUCCEEDED(hResult))
591 {
592 FixAmpersands(itemTextBuffer);
593 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer, menuItemInfo.dwTypeData, menuItemInfo.wID);
594 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
595 {
596 nPos++;
597 menuItemInfo.wID++;
598 }
599 }
600 currentItem = currentItem->fNextEntry;
601 }
602 }
603 return S_OK;
604 }
605
606 HRESULT STDMETHODCALLTYPE CTravelLog::Clone(ITravelLog **pptl)
607 {
608 if (pptl == NULL)
609 return E_POINTER;
610 *pptl = NULL;
611 // duplicate the log
612 UNIMPLEMENTED;
613 return E_NOTIMPL;
614 }
615
616 DWORD STDMETHODCALLTYPE CTravelLog::CountEntries(IUnknown *punk)
617 {
618 if (punk == NULL)
619 return E_INVALIDARG;
620 return fEntryCount;
621 }
622
623 HRESULT STDMETHODCALLTYPE CTravelLog::Revert()
624 {
625 // remove the current entry?
626 UNIMPLEMENTED;
627 return E_NOTIMPL;
628 }
629
630 HRESULT CreateTravelLog(REFIID riid, void **ppv)
631 {
632 return ShellObjectCreatorInit<CTravelLog>(riid, ppv);
633 }