//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1999 - 1999 // // File: favorite.cpp // //-------------------------------------------------------------------------- // favorite.cpp #include "stdafx.h" #include "amcdoc.h" #include "favorite.h" #include "favui.h" //############################################################################ //############################################################################ // // Implementation of class CFavObject // //############################################################################ //############################################################################ CFavObject::CFavObject(bool bIsGroup) : m_pFavParent(NULL), m_pFavNext(NULL), m_pFavChild(NULL), m_bIsGroup(bIsGroup), m_strName(_T("")), m_strPath(_T("")) { } CFavObject::~CFavObject() { // delete siblings iteratively CFavObject* pFavSib = GetNext(); while(pFavSib) { CFavObject* pFavNext = pFavSib->GetNext(); pFavSib->SetNext(NULL); delete pFavSib; pFavSib = pFavNext; } // delete children recursively if (GetChild()) delete GetChild(); } int CFavObject::GetImage() { return ( IsGroup() ? eStockImage_Folder : eStockImage_Favorite); } int CFavObject::GetOpenImage() { return ( IsGroup() ? eStockImage_OpenFolder: eStockImage_Favorite); } DWORD CFavObject::GetChildCount() { ASSERT(IsGroup()); DWORD dwCount = 0; CFavObject *pObject = m_pFavChild; while(pObject != NULL) { dwCount++; pObject = pObject->GetNext(); } return dwCount; } void CFavObject::AddChild(CFavObject* pFavNew, CFavObject* pFavPrev) { ASSERT(IsGroup()); ASSERT(pFavNew != NULL); // if adding to end, locate last child if (pFavPrev == LAST_FAVORITE) { pFavPrev = GetChild(); if (pFavPrev != NULL) while (pFavPrev->GetNext()) pFavPrev = pFavPrev->GetNext(); } // if no previous object if (pFavPrev == NULL) { // add as first child pFavNew->SetNext(GetChild()); SetChild(pFavNew); } else { // add after previous pFavNew->SetNext(pFavPrev->GetNext()); pFavPrev->SetNext(pFavNew); } // always set self as parent pFavNew->SetParent(this); } void CFavObject::RemoveChild(CFavObject* pFavDelete) { ASSERT(pFavDelete != NULL); ASSERT(pFavDelete->GetParent() == this); if (GetChild() == pFavDelete) { SetChild(pFavDelete->GetNext()); } else { CFavObject* pFavPrev = GetChild(); while(pFavPrev != NULL && pFavPrev->GetNext() != pFavDelete) pFavPrev = pFavPrev->GetNext(); ASSERT(pFavPrev != NULL); pFavPrev->SetNext(pFavDelete->GetNext()); } pFavDelete->SetNext(NULL); } void CFavObject::SetPath(LPCTSTR pszPath) { // Drop first part of path (because it is always the console root) // unless the shortcut is to the root itself TCHAR* pSep = _tcschr(pszPath, _T('\\')); m_strPath = (pSep != NULL) ? CharNext(pSep) : pszPath; } HRESULT CFavObject::ReadSerialObject (IStream &stm, UINT nVersion) { HRESULT hr = S_FALSE; // assume bad version if (nVersion == 1) { try { stm >> m_bIsGroup; stm >> m_strName; if(IsGroup()) { DWORD cChildren = 0; stm >> cChildren; for(int i = 0; i< cChildren; i++) { CFavObject *pObject = new CFavObject(true); // the true parameter gets overwritten. hr = pObject->ReadSerialObject(stm, nVersion); if(FAILED(hr)) { delete pObject; pObject = NULL; return hr; } AddChild(pObject); } hr = S_OK; } else // is an item { hr = m_memento.Read(stm); stm >> m_strPath; } } catch (_com_error& err) { hr = err.Error(); ASSERT (false && "Caught _com_error"); } } return hr; } //############################################################################ //############################################################################ // // Implementation of class CFavorites // //############################################################################ //############################################################################ CFavorites::CFavorites() : m_pFavRoot(NULL) { // create root group CString strName; LoadString(strName, IDS_FAVORITES); m_pFavRoot = new CFavObject(true /*bIsGroup*/); m_pFavRoot->m_strName = strName; } CFavorites::~CFavorites() { // delete the entire tree if (m_pFavRoot != NULL) delete m_pFavRoot; } ///////////////////////////////////////////////////////// // CTreeSource methods TREEITEMID CFavorites::GetRootItem() { return TIDFromFavObj(m_pFavRoot); } TREEITEMID CFavorites::GetParentItem(TREEITEMID tid) { CFavObject* pFav = FavObjFromTID(tid); return TIDFromFavObj(pFav->GetParent()); } TREEITEMID CFavorites::GetChildItem(TREEITEMID tid) { CFavObject* pFav = FavObjFromTID(tid); return TIDFromFavObj(pFav->GetChild()); } TREEITEMID CFavorites::GetNextSiblingItem(TREEITEMID tid) { CFavObject* pFav = FavObjFromTID(tid); return TIDFromFavObj(pFav->GetNext()); } LPARAM CFavorites::GetItemParam(TREEITEMID tid) { return 0; } void CFavorites::GetItemName(TREEITEMID tid, LPTSTR pszName, int cchMaxName) { DECLARE_SC(sc, TEXT("CFavorites::GetItemName")); sc = ScCheckPointers(pszName); if (sc) return; CFavObject* pFav = FavObjFromTID(tid); sc = StringCchCopy(pszName, cchMaxName, pFav->GetName()); if (sc) return; } void CFavorites::GetItemPath(TREEITEMID tid, LPTSTR pszPath, int cchMaxPath) { DECLARE_SC(sc, TEXT("CFavorites::GetItemPath")); sc = ScCheckPointers(pszPath); if (sc) return; CFavObject* pFav = FavObjFromTID(tid); sc = StringCchCopy(pszPath, cchMaxPath, pFav->GetPath()); if (sc) return; } int CFavorites::GetItemImage(TREEITEMID tid) { CFavObject* pFav = FavObjFromTID(tid); return pFav->GetImage(); } int CFavorites::GetItemOpenImage(TREEITEMID tid) { CFavObject* pFav = FavObjFromTID(tid); return pFav->GetOpenImage(); } BOOL CFavorites::IsFolderItem(TREEITEMID tid) { CFavObject* pFav = FavObjFromTID(tid); return pFav->IsGroup(); } /////////////////////////////////////////////////////////////////////////// // CFavorites methods HRESULT CFavorites::AddFavorite(TREEITEMID tidParent, LPCTSTR strName, CFavObject** ppFavRet) { ASSERT(tidParent != NULL && strName != NULL); ASSERT(FavObjFromTID(tidParent)->IsGroup()); CFavObject* pFavParent = reinterpret_cast(tidParent); // Create a favorite item CFavObject* pFavItem = new CFavObject(false /*bIsGroup*/); if (pFavItem == NULL) return E_OUTOFMEMORY; pFavItem->m_strName = strName; // Add to end of group pFavParent->AddChild(pFavItem); // Notify all observers of addition FOR_EACH_OBSERVER(CTreeObserver, iter) { (*iter)->ItemAdded(TIDFromFavObj(pFavItem)); } if (ppFavRet) *ppFavRet = pFavItem; return S_OK; } HRESULT CFavorites::AddGroup(TREEITEMID tidParent, LPCTSTR strName, CFavObject** ppFavRet) { ASSERT(tidParent != NULL && strName != NULL); ASSERT(FavObjFromTID(tidParent)->IsGroup()); CFavObject* pFavParent = reinterpret_cast(tidParent); CFavObject* pFavGrp = new CFavObject(true /*bIsGroup*/); if (pFavGrp == NULL) return E_OUTOFMEMORY; pFavGrp->m_strName = strName; pFavParent->AddChild(pFavGrp); // Notify all observers of addition FOR_EACH_OBSERVER(CTreeObserver, iter) { (*iter)->ItemAdded(TIDFromFavObj(pFavGrp)); } if (ppFavRet) *ppFavRet = pFavGrp; return S_OK; } HRESULT CFavorites::DeleteItem(TREEITEMID tid) { CFavObject* pFav = FavObjFromTID(tid); CFavObject* pFavParent = pFav->GetParent(); if (pFavParent) pFavParent->RemoveChild(pFav); else m_pFavRoot = NULL; delete pFav; // Notify all observers of deletion FOR_EACH_OBSERVER(CTreeObserver, iter) { (*iter)->ItemRemoved((TREEITEMID)pFavParent, tid); } return S_OK; } HRESULT CFavorites::MoveItem(TREEITEMID tid, TREEITEMID tidNewGroup, TREEITEMID tidPrev) { CFavObject* pFav = FavObjFromTID(tid); CFavObject* pFavPrev = FavObjFromTID(tidPrev); ASSERT(FavObjFromTID(tidNewGroup)->IsGroup()); CFavObject* pFavNewGroup = reinterpret_cast(tidNewGroup); // Verify not moving item into itself or under itself CFavObject* pFavTemp = pFavNewGroup; while (pFavTemp != NULL) { if (pFavTemp == pFav) return E_FAIL; pFavTemp = pFavTemp->GetParent(); } // Remove object from current group CFavObject* pFavParent = pFav->GetParent(); ASSERT(pFavParent != NULL); pFavParent->RemoveChild(pFav); // Notify all observers of removal FOR_EACH_OBSERVER(CTreeObserver, iter) { (*iter)->ItemRemoved((TREEITEMID)pFavParent, tid); } // Insert item into the new group pFavNewGroup->AddChild(pFav, pFavPrev); // Notify all observers of addition FOR_EACH_OBSERVER(CTreeObserver, iter1) { (*iter1)->ItemAdded(tid); } return S_OK; } HRESULT CFavorites::SetItemName(TREEITEMID tid, LPCTSTR pszName) { CFavObject* pFav = FavObjFromTID(tid); ASSERT(pszName != NULL && pszName[0] != 0); // Change item name pFav->m_strName = pszName; // Notify all observers of change FOR_EACH_OBSERVER(CTreeObserver, iter) { (*iter)->ItemChanged(tid, TIA_NAME); } return S_OK; } HRESULT CFavorites::AddToFavorites(LPCTSTR szName, LPCTSTR szPath, CMemento &memento, CWnd* pwndHost) { DECLARE_SC (sc, _T("CFavorites::AddToFavorites")); CAddFavDialog dlg(szName, this, pwndHost); CFavObject* pFavItem = NULL; sc = dlg.CreateFavorite(&pFavItem); // Note: S_FALSE is returned if user cancels dialog if (sc.ToHr() != S_OK) return (sc.ToHr()); sc = ScCheckPointers (pFavItem, E_UNEXPECTED); if (sc) return (sc.ToHr()); pFavItem->SetPath(szPath); pFavItem->SetMemento(memento); return S_OK; } CImageList* CFavorites::GetImageList() { if (m_ImageList != NULL) return CImageList::FromHandle (m_ImageList); do { BOOL bStat = m_ImageList.Create(16, 16, ILC_COLORDDB | ILC_MASK, 20, 10); if (!bStat) break; CBitmap bmap; bStat = bmap.LoadBitmap(IDB_AMC_NODES16); if (!bStat) break; int ipos = m_ImageList.Add(bmap, RGB(255,0,255)); if (ipos == -1) break; } while (0); return CImageList::FromHandle (m_ImageList); } HRESULT CFavorites::OrganizeFavorites(CWnd* pwndHost) { COrganizeFavDialog dlg(this, pwndHost); dlg.DoModal(); return S_OK; } HRESULT CFavorites::ReadSerialObject (IStream &stm, UINT nVersion) { HRESULT hr = m_pFavRoot->ReadSerialObject(stm, nVersion); if(FAILED(hr)) return hr; // Notify all observers of addition FOR_EACH_OBSERVER(CTreeObserver, iter) { (*iter)->ItemRemoved(NULL, TIDFromFavObj(m_pFavRoot)); (*iter)->ItemAdded(TIDFromFavObj(m_pFavRoot)); } return hr; } bool CFavorites::IsEmpty() { // the list is empty if the root has no children. return (m_pFavRoot->GetChild()==NULL); } /*****************************************************************\ | METHOD: CFavorites::Persist | DESCR: Persists favorites, by delegating to root item \*****************************************************************/ void CFavorites::Persist(CPersistor &persistor) { DECLARE_SC(sc, TEXT("CFavorites::Persist")); sc = ScCheckPointers(m_pFavRoot, E_POINTER); if (sc) sc.Throw(); persistor.Persist(*m_pFavRoot); } /*****************************************************************\ | METHOD: CFavoriteXMLList::PersistItself | DESCR: "soft" version of Persist - ignores missing element | RETURN: true == element exists and persistence succeeded \*****************************************************************/ bool CFavoriteXMLList::PersistItself(CPersistor& persistor) { DECLARE_SC(sc, TEXT("CFavoriteXMLList::PersistItself")); if (persistor.IsLoading()) { if (!persistor.HasElement(GetXMLType(), NULL)) return false; } persistor.Persist(*this); return true; } /*****************************************************************\ | METHOD: CFavoriteXMLList::Persist | DESCR: Perists collection (linked list) contents \*****************************************************************/ void CFavoriteXMLList::Persist(CPersistor& persistor) { if (persistor.IsStoring()) { for (CFavObject *pObj = m_rpRoot; pObj; pObj = pObj->GetNext()) { persistor.Persist(*pObj); } } else { ASSERT(m_rpRoot == NULL); // this is to upload new entries only!!! m_rpRoot = NULL; XMLListCollectionBase::Persist(persistor); } } /*****************************************************************\ | METHOD: CFavoriteXMLList::OnNewElement | DESCR: called for every new element when loading \*****************************************************************/ void CFavoriteXMLList::OnNewElement(CPersistor& persistor) { DECLARE_SC(sc, TEXT("CFavoriteXMLList::OnNewElement")); CFavObject **pObj = &m_rpRoot; while (*pObj) pObj = &(*pObj)->m_pFavNext; CFavObject *pNewObj = new CFavObject(false); *pObj = pNewObj; sc = ScCheckPointers(pNewObj, E_OUTOFMEMORY); if (sc) sc.Throw(); pNewObj->SetParent(m_Parent); persistor.Persist(*pNewObj); } /*****************************************************************\ | METHOD: CFavObject::Persist | DESCR: Persists Favorites item. \*****************************************************************/ void CFavObject::Persist(CPersistor &persistor) { persistor.PersistString(XML_ATTR_NAME, m_strName); // persist the type of favorite CStr strType(IsGroup() ? XML_VAL_FAVORITE_GROUP : XML_VAL_FAVORITE_SINGLE); persistor.PersistAttribute(XML_ATTR_FAVORITE_TYPE, strType); m_bIsGroup = (0 == strType.CompareNoCase(XML_VAL_FAVORITE_GROUP)); // its either group or memento. if (IsGroup()) { CFavoriteXMLList children(m_pFavChild, this); children.PersistItself(persistor); } else // if (!IsGroup()) { persistor.Persist(m_memento); } }