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