2212 lines
64 KiB
C++
2212 lines
64 KiB
C++
/*****************************************************************************
|
|
* Subset.cpp
|
|
*
|
|
* Copyright (C) Microsoft, 1989-1998
|
|
* Feb 22, 1998
|
|
*
|
|
* Modification History:
|
|
*
|
|
* Ported to hhctrl.ocx from the old B2 code base.
|
|
*
|
|
*****************************************************************************/
|
|
#include "header.h"
|
|
#include "subset.h"
|
|
#include "resource.h"
|
|
#include "toc.h"
|
|
#include "cdefinss.h"
|
|
#include "verdef.h"
|
|
#include "secwin.h"
|
|
|
|
#define CmdID LOWORD(wParam)
|
|
#define CmdCode HIWORD(wParam)
|
|
#define CmdHwnd (HWND)lParam
|
|
/*
|
|
* Defines, the EXPANDED define aids in intrepreting the proper bit in the
|
|
* FGT data structure depending on which LB we're operating on.
|
|
*/
|
|
#define EXPANDED(lb_id, ds) ((lb_id) ? ds->f_A_Open : ds->f_F_Open)
|
|
#define PRESENT(lb_id, ds) ((lb_id) ? ds->f_Available : ds->f_Filter)
|
|
|
|
// declare a static this pointer for our window procedures.
|
|
//
|
|
CDefineSS* CDefineSS::m_pThis;
|
|
|
|
//********************************************************************************
|
|
//
|
|
// CStructuralSubset Implementation - This is object representation of a subset.
|
|
//
|
|
//********************************************************************************
|
|
|
|
|
|
CSSList::CSSList()
|
|
{
|
|
m_iSubSetCount = 0;
|
|
m_pHeadSubSets = m_pFTS_SS = m_pF1_SS = m_pTOC_SS = m_pNew_SS = m_pEC_SS = NULL;
|
|
}
|
|
|
|
CSSList::~CSSList()
|
|
{
|
|
CStructuralSubset *pSS, *pSSNext;
|
|
|
|
pSS = m_pHeadSubSets;
|
|
while ( pSS )
|
|
{
|
|
pSSNext = pSS->m_pNext;
|
|
delete pSS;
|
|
pSS = pSSNext;
|
|
}
|
|
}
|
|
|
|
CStructuralSubset* CSSList::GetSubset(PSTR pszSSName)
|
|
{
|
|
CStructuralSubset *pSS = m_pHeadSubSets;
|
|
|
|
while ( pSS )
|
|
{
|
|
if (! lstrcmpi(pSS->GetName(), pszSSName) )
|
|
return pSS;
|
|
pSS = pSS->m_pNext;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CSSList::DeleteSubset(CStructuralSubset* pSS, CStructuralSubset* pSSNew, /* == NULL */ HWND hWndUI /* == NULL */)
|
|
{
|
|
CStructuralSubset *pSSCurrCB, *pSSl = m_pHeadSubSets;
|
|
TCHAR szSel[MAX_SS_NAME_LEN];
|
|
HWND hWndCB;
|
|
INT_PTR i;
|
|
CHHWinType* phh;
|
|
|
|
if (! pSSNew )
|
|
pSSNew = m_pEC_SS;
|
|
|
|
if ( pSS->m_dwFlags & SS_READ_ONLY )
|
|
return;
|
|
|
|
if ( pSS == m_pFTS_SS )
|
|
m_pFTS_SS = pSSNew;
|
|
if ( pSS == m_pF1_SS )
|
|
m_pF1_SS = pSSNew;
|
|
if ( pSS == m_pTOC_SS )
|
|
m_pTOC_SS = pSSNew;
|
|
//
|
|
// If we're given an hwnd, update the combo-box UI.
|
|
//
|
|
if ( hWndUI && (hWndCB = GetDlgItem(hWndUI, IDC_SS_PICKER)) )
|
|
{
|
|
//
|
|
// Get the current CB selection and see if it's the one we're deleting ?
|
|
//
|
|
if ( ((i = SendMessage(hWndCB, CB_GETCURSEL, 0, 0L)) != -1) )
|
|
{
|
|
GetDlgItemText(hWndUI, IDC_SS_PICKER, szSel, sizeof(szSel));
|
|
|
|
pSSCurrCB = GetSubset(szSel);
|
|
if ( pSSCurrCB == pSS ) // Yep, we're deleting the current one, select the new one.
|
|
{
|
|
SendMessage(hWndCB, CB_SELECTSTRING, -1, (LPARAM)pSSNew->GetName());
|
|
if ( (phh = FindWindowIndex(hWndUI)) )
|
|
{
|
|
pSSNew->SelectAsTOCSubset(phh->m_phmData->m_pTitleCollection);
|
|
phh->UpdateInformationTypes(); // This call re-draws the TOC.
|
|
}
|
|
}
|
|
}
|
|
if ( lstrcmpi(pSS->GetName(), pSSNew->GetName()) )
|
|
{
|
|
if ( (i = SendMessage(hWndCB, CB_FINDSTRING, -1, (LPARAM)pSS->GetName())) != -1 )
|
|
SendMessage(hWndCB, CB_DELETESTRING, i, 0L);
|
|
}
|
|
}
|
|
//
|
|
// Take the delete victum out of the linked list.
|
|
//
|
|
while ( pSSl )
|
|
{
|
|
if ( pSSl->m_pNext == pSS )
|
|
{
|
|
pSSl->m_pNext = pSS->m_pNext;
|
|
delete pSS;
|
|
break;
|
|
}
|
|
pSSl = pSSl->m_pNext;
|
|
}
|
|
m_iSubSetCount--;
|
|
}
|
|
|
|
HRESULT CSSList::PersistSubsets(CExCollection* pCollection)
|
|
{
|
|
LPCSTR lpszFQSSStore;
|
|
HANDLE hFile;
|
|
SSHEADER ssHeader;
|
|
CStructuralSubset* pSS;
|
|
CStructuralSubset* pSSCurr = NULL;
|
|
PSS pSSFile = NULL;
|
|
unsigned long ulCnt;
|
|
int iSSSize;
|
|
int i = 0;
|
|
|
|
if (! (pSS = m_pHeadSubSets) )
|
|
return S_OK;
|
|
//
|
|
// Count'em
|
|
//
|
|
do
|
|
{
|
|
if ( pSS->IsTOC() )
|
|
pSSCurr = pSS;
|
|
if ( !pSS->IsEntire() && !pSS->IsEmpty() && !pSS->IsReadOnly() )
|
|
i++;
|
|
} while ( (pSS = GetNextSubset(pSS)) );
|
|
//
|
|
// Prepare a file.
|
|
//
|
|
if (! (lpszFQSSStore = pCollection->GetUserCHSLocalStoragePathnameByLanguage()) )
|
|
return E_FAIL;
|
|
hFile = CreateFile(lpszFQSSStore, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
return E_FAIL;
|
|
//
|
|
// Fill in the header, write out the header.
|
|
//
|
|
// Need to store the persisted subset in the header since it may be a predefine.
|
|
//
|
|
ssHeader.dwSig = SS_FILE_SIGNATURE;
|
|
ssHeader.dwVer = (VER_PRODUCTVERSION_DW & 0x0000);
|
|
ssHeader.iSSCount = i;
|
|
ssHeader.dwFlags = 0;
|
|
if ( pSSCurr )
|
|
lstrcpy(ssHeader.lpszCurrentSSName, pSSCurr->GetName());
|
|
if (! WriteFile(hFile, &ssHeader, sizeof(SSHEADER), &ulCnt, NULL) )
|
|
{
|
|
MsgBox(IDS_PERSIST_SUBSET_ERR, MB_OK | MB_ICONHAND);
|
|
return E_FAIL;
|
|
}
|
|
//
|
|
// Write out the subsets.
|
|
//
|
|
pSS = m_pHeadSubSets;
|
|
do
|
|
{
|
|
if ( pSS->IsEntire() || pSS->IsEmpty() || pSS->IsReadOnly() )
|
|
continue;
|
|
int iHashCnt = pSS->GetHashCount();
|
|
iSSSize = (sizeof(SS) + ((iHashCnt - 1) * sizeof(DWORD)));
|
|
if ( (pSSFile = (PSS)lcReAlloc(pSSFile, iSSSize)) )
|
|
{
|
|
pSSFile->iHashCount = iHashCnt;
|
|
lstrcpy(pSSFile->lpszSSName, pSS->GetName());
|
|
lstrcpy(pSSFile->lpszSSID, pSS->GetID());
|
|
pSSFile->dwFlags = pSS->m_dwFlags;
|
|
while ( --iHashCnt >= 0)
|
|
pSSFile->dwHashes[iHashCnt] = pSS->EnumHashes(iHashCnt);
|
|
if (! WriteFile(hFile, pSSFile, iSSSize, &ulCnt, NULL) )
|
|
{
|
|
MsgBox(IDS_PERSIST_SUBSET_ERR, MB_OK | MB_ICONHAND);
|
|
lcFree(pSSFile);
|
|
CloseHandle(hFile);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
} while ( (pSS = GetNextSubset(pSS)) );
|
|
|
|
lcFree(pSSFile);
|
|
CloseHandle(hFile);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSSList::RestoreSubsets(CExCollection* pCollection, PSTR pszRestoreSS)
|
|
{
|
|
LPCSTR lpszFQSSStore;
|
|
HANDLE hFile;
|
|
SSHEADER ssHeader;
|
|
PSS pSSFile;
|
|
CStructuralSubset* pSS;
|
|
unsigned long ulHashCnt, ulRead;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (! (pSSFile = (PSS)lcMalloc(sizeof(SS) + sizeof(DWORD) * 50)) )
|
|
return hr;
|
|
lpszFQSSStore = pCollection->GetUserCHSLocalStoragePathnameByLanguage();
|
|
hFile = CreateFile(lpszFQSSStore, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if ( hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
lpszFQSSStore = pCollection->GetUserCHSLocalStoragePathname();
|
|
hFile = CreateFile(lpszFQSSStore, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if ( hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
lpszFQSSStore = pCollection->GetLocalStoragePathname(".chs");
|
|
hFile = CreateFile(lpszFQSSStore, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
lcFree(pSSFile);
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Read the header.
|
|
//
|
|
if (! ReadFile(hFile, &ssHeader, sizeof(SSHEADER), &ulRead, NULL) )
|
|
goto crap_out;
|
|
if ( ssHeader.dwSig != SS_FILE_SIGNATURE )
|
|
goto crap_out;
|
|
//
|
|
// Get the subsets.
|
|
//
|
|
while ( ssHeader.iSSCount-- )
|
|
{
|
|
if (! ReadFile(hFile, &ulHashCnt, sizeof(int), &ulRead, NULL) )
|
|
goto crap_out;
|
|
if ( ulHashCnt > 51 )
|
|
{
|
|
if (! (pSSFile = (PSS)lcReAlloc(pSSFile, sizeof(SS) + (sizeof(DWORD) * ulHashCnt))) )
|
|
goto crap_out;
|
|
}
|
|
pSSFile->iHashCount = ulHashCnt;
|
|
unsigned long ulAmt = ((sizeof(SS) - sizeof(int)) + (sizeof(DWORD) * (ulHashCnt - 1)));
|
|
if (! ReadFile(hFile, (((BYTE*)pSSFile) + sizeof(int)), ulAmt, &ulRead, NULL) )
|
|
goto crap_out;
|
|
//
|
|
// Now create a CStructuralSubset from the data.
|
|
//
|
|
pSS = new CStructuralSubset(pSSFile->lpszSSName);
|
|
pSS->m_dwFlags = pSSFile->dwFlags;
|
|
pSS->m_dwFlags &= ~(SS_FTS | SS_TOC | SS_F1); // Insure selection bits are reset.
|
|
lstrcpy(pSS->m_szSSID, pSSFile->lpszSSID);
|
|
while ( ulHashCnt )
|
|
pSS->AddHash(pSSFile->dwHashes[--ulHashCnt]);
|
|
//
|
|
// Add the subset to the list appropiatly.
|
|
//
|
|
AddSubset(pSS);
|
|
if (! lstrcmpi(ssHeader.lpszCurrentSSName, pSS->GetName() ) )
|
|
{
|
|
SetFTS(pSS);
|
|
SetTOC(pSS);
|
|
SetF1(pSS);
|
|
}
|
|
}
|
|
if ( pszRestoreSS )
|
|
lstrcpy(pszRestoreSS, ssHeader.lpszCurrentSSName);
|
|
hr = S_OK;
|
|
crap_out:
|
|
lcFree(pSSFile);
|
|
CloseHandle(hFile);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSSList::ReadPreDefinedSubsets(CExCollection* pCollection, PSTR pszRestoreSS)
|
|
{
|
|
SSHEADER ssHeader;
|
|
PSS pSSFile;
|
|
CStructuralSubset* pSS;
|
|
CSubFileSystem* pSubFS;
|
|
unsigned long ulHashCnt, ulRead;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (! (pSSFile = (PSS)lcMalloc(sizeof(SS) + sizeof(DWORD) * 50)) )
|
|
return hr;
|
|
|
|
if (! pCollection || !pCollection->GetMasterTitle() )
|
|
return hr;
|
|
|
|
pSubFS = new CSubFileSystem(pCollection->GetMasterTitle()->GetTitleIdxFileSystem());
|
|
if ( !SUCCEEDED(pSubFS->OpenSub("predef.chs")) )
|
|
goto crap_out;
|
|
//
|
|
// Read the header.
|
|
//
|
|
if ( !SUCCEEDED(pSubFS->ReadSub(&ssHeader, sizeof(SSHEADER), &ulRead)) )
|
|
goto crap_out;
|
|
if ( ssHeader.dwSig != SS_FILE_SIGNATURE )
|
|
goto crap_out;
|
|
//
|
|
// Get the subsets.
|
|
//
|
|
while ( ssHeader.iSSCount-- )
|
|
{
|
|
if ( !SUCCEEDED(pSubFS->ReadSub(&ulHashCnt, sizeof(int), &ulRead)) )
|
|
goto crap_out;
|
|
if ( ulHashCnt > 51 )
|
|
{
|
|
if (! (pSSFile = (PSS)lcReAlloc(pSSFile, sizeof(SS) + (sizeof(DWORD) * ulHashCnt))) )
|
|
goto crap_out;
|
|
}
|
|
pSSFile->iHashCount = ulHashCnt;
|
|
unsigned long ulAmt = ((sizeof(SS) - sizeof(int)) + (sizeof(DWORD) * (ulHashCnt - 1)));
|
|
if ( !SUCCEEDED(pSubFS->ReadSub((((BYTE*)pSSFile) + sizeof(int)), ulAmt, &ulRead)) )
|
|
goto crap_out;
|
|
//
|
|
// Now create a CStructuralSubset from the data.
|
|
//
|
|
pSS = new CStructuralSubset(pSSFile->lpszSSName);
|
|
pSS->m_dwFlags = pSSFile->dwFlags;
|
|
pSS->m_dwFlags &= ~(SS_FTS | SS_TOC | SS_F1); // Insure selection bits are reset.
|
|
pSS->m_dwFlags |= SS_READ_ONLY;
|
|
lstrcpy(pSS->m_szSSID, pSSFile->lpszSSID);
|
|
while ( ulHashCnt )
|
|
pSS->AddHash(pSSFile->dwHashes[--ulHashCnt]);
|
|
//
|
|
// Add the subset to the list appropiatly.
|
|
//
|
|
AddSubset(pSS);
|
|
if (! lstrcmpi(pszRestoreSS, pSS->GetName() ) )
|
|
{
|
|
SetFTS(pSS);
|
|
SetTOC(pSS);
|
|
SetF1(pSS);
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
crap_out:
|
|
lcFree(pSSFile);
|
|
delete pSubFS;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSSList::AddSubset(CStructuralSubset* pSS, HWND hWndUI /* == NULL */)
|
|
{
|
|
CStructuralSubset *pSSl;
|
|
HWND hWndCB;
|
|
|
|
if (! m_pHeadSubSets )
|
|
m_pHeadSubSets = pSS;
|
|
else
|
|
{
|
|
pSSl = m_pHeadSubSets;
|
|
while ( pSSl->m_pNext )
|
|
pSSl = pSSl->m_pNext;
|
|
|
|
pSSl->m_pNext = pSS;
|
|
pSS->m_pNext = NULL;
|
|
}
|
|
m_iSubSetCount++;
|
|
if ( hWndUI && (hWndCB = GetDlgItem(hWndUI, IDC_SS_PICKER)) )
|
|
{
|
|
if ( SendMessage(hWndCB, CB_FINDSTRING, -1, (LPARAM)pSS->GetName()) == -1 )
|
|
SendMessage(hWndCB, CB_ADDSTRING, 0, (LPARAM)pSS->GetName());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
void CSSList::Set(CStructuralSubset* pSSNew, CStructuralSubset** pSSOld, DWORD dwFlags)
|
|
{
|
|
if (! pSSNew )
|
|
return;
|
|
|
|
pSSNew->m_dwFlags |= dwFlags;
|
|
if ( *pSSOld && (pSSNew != *pSSOld) )
|
|
(*pSSOld)->m_dwFlags &= ~dwFlags;
|
|
*pSSOld = pSSNew;
|
|
}
|
|
|
|
//********************************************************************************
|
|
//
|
|
// CStructuralSubset Implementation - This is object representation of a subset.
|
|
//
|
|
//********************************************************************************
|
|
|
|
CStructuralSubset::CStructuralSubset(PCSTR pszSubSetName)
|
|
{
|
|
TCHAR* psz;
|
|
int i = 0;
|
|
|
|
m_szSSID[0] = '\0';
|
|
|
|
if ( pszSubSetName )
|
|
{
|
|
if ( (psz = StrChr(pszSubSetName, '|')) ) // If the subset name contains an identifier, process it.
|
|
{
|
|
psz = AnsiNext(psz);
|
|
lstrcpyn(m_szSubSetName, psz, MAX_SS_NAME_LEN);
|
|
while ( pszSubSetName[i] != '|' && (i < MAX_SS_NAME_LEN) )
|
|
{
|
|
m_szSSID[i] = pszSubSetName[i];
|
|
i++;
|
|
}
|
|
m_szSSID[i] = '\0'; // terminate.
|
|
}
|
|
else
|
|
lstrcpyn(m_szSubSetName, pszSubSetName, MAX_SS_NAME_LEN);
|
|
}
|
|
else
|
|
{
|
|
m_szSubSetName[0] = '\0';
|
|
m_szSSID[0] = '\0';
|
|
}
|
|
m_pdwHashes = NULL;
|
|
m_iAllocatedCount = m_iHashCount = 0;
|
|
m_dwFlags = 0;
|
|
m_pNext = NULL;
|
|
}
|
|
|
|
CStructuralSubset::~CStructuralSubset()
|
|
{
|
|
if ( m_pdwHashes )
|
|
lcFree(m_pdwHashes);
|
|
}
|
|
|
|
HASH CStructuralSubset::EnumHashes(int pos)
|
|
{
|
|
if ( !m_iHashCount || (pos >= m_iHashCount) || (pos < 0) )
|
|
return 0;
|
|
return m_pdwHashes[pos];
|
|
}
|
|
|
|
void CStructuralSubset::AddHash(DWORD dwHash)
|
|
{
|
|
if ( (m_iHashCount && (!(m_iHashCount % 12))) || (m_iAllocatedCount == 0) )
|
|
{
|
|
m_iAllocatedCount += 12;
|
|
m_pdwHashes = (HASH*)lcReAlloc(m_pdwHashes, sizeof(HASH) * m_iAllocatedCount);
|
|
}
|
|
m_pdwHashes[m_iHashCount] = dwHash;
|
|
m_iHashCount++;
|
|
}
|
|
|
|
BOOL CStructuralSubset::IsTitleInSubset(CExTitle* pTitle)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < m_iHashCount; i++)
|
|
{
|
|
if ( m_pdwHashes[i] == pTitle->m_dwHash )
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Function selects the subset as the current TOC filter by setting the f_IsVisable bit accordingly.
|
|
// NOTE: we don't bother selecting the bits for entire contents, this is special cased for performance reasons.
|
|
//
|
|
void CStructuralSubset::SelectAsTOCSubset(CExCollection* pCollection)
|
|
{
|
|
CFolder* pFgt; // pointer to filtered group tree.
|
|
UINT i = 0;
|
|
HASH Hash;
|
|
|
|
if ( IsEntire() || IsEmpty() )
|
|
return;
|
|
|
|
pFgt = pCollection->m_Collection.GetVisableRootFolder();
|
|
//
|
|
// De-select all node's filter bits and select the proper
|
|
// tree nodes based on the hash list!
|
|
//
|
|
while ( pFgt )
|
|
{
|
|
MarkNode(pFgt, 0); // Remove nodes by marking as not visable. // RemoveNodeFromFilter(pFgt);
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
while ( Hash = EnumHashes(i++) )
|
|
{
|
|
if ( (pFgt = pCollection->m_pCSlt->HashToCFolder(Hash)) )
|
|
MarkNode(pFgt, 1); // Add nodes by marking as "visable". // AddNode2Filter(pFgt);
|
|
}
|
|
}
|
|
|
|
void CStructuralSubset::MarkNode(CFolder* pFgti, BOOL bVisable)
|
|
{
|
|
CFolder* pFgt;
|
|
CFolder* pFgtLast;
|
|
|
|
// First, mark the node and any children of the node.
|
|
//
|
|
pFgt = pFgti;
|
|
if ( pFgt->pKid )
|
|
{
|
|
while ( pFgt )
|
|
{
|
|
do
|
|
{
|
|
pFgt->f_IsVisable = bVisable;
|
|
pFgtLast = pFgt;
|
|
pFgt = pFgt->pKid;
|
|
|
|
} while ( pFgt );
|
|
pFgt = pFgtLast;
|
|
while ( pFgt && (! (pFgt->pNext)) )
|
|
{
|
|
if ( pFgt->pParent != pFgti )
|
|
pFgt = pFgt->pParent;
|
|
else
|
|
break;
|
|
}
|
|
if ( pFgt )
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
}
|
|
// Next, assure any parents of the node are properly marked.
|
|
//
|
|
pFgt = pFgti;
|
|
pFgt->f_IsVisable = bVisable;
|
|
|
|
while ( (pFgt = pFgt->pParent) )
|
|
pFgt->f_IsVisable = bVisable;
|
|
}
|
|
|
|
//****************************************************************************
|
|
//
|
|
// CDefineSS Implementation - This is the guy that does all the UI.
|
|
//
|
|
//*****************************************************************************
|
|
|
|
CDefineSS::CDefineSS(CExCollection* pCollection)
|
|
{
|
|
m_bShouldSave = FALSE;
|
|
m_pSS = NULL;
|
|
m_pCollection = pCollection;
|
|
m_hIL = 0;
|
|
m_hWndParent = 0;
|
|
}
|
|
|
|
CDefineSS::~CDefineSS()
|
|
{
|
|
}
|
|
|
|
/****************************************************************************
|
|
* IDefineFilter()
|
|
*
|
|
* Internal entry point for filter manipulation dialog.
|
|
*
|
|
* ENTRY:
|
|
* none.
|
|
*
|
|
* EXIT:
|
|
* none.
|
|
*
|
|
****************************************************************************/
|
|
CStructuralSubset* CDefineSS::DefineSubset(HWND hWnd, CStructuralSubset* pSS)
|
|
{
|
|
m_pSS = pSS;
|
|
|
|
if (! m_hIL )
|
|
{
|
|
m_hIL = ImageList_LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDBMP_CNT_IMAGE_LIST), CWIDTH_IMAGE_LIST,
|
|
0, 0x00FF00FF, IMAGE_BITMAP, 0);
|
|
if (! m_hIL )
|
|
return NULL;
|
|
}
|
|
//
|
|
// Create the model dialog.
|
|
//
|
|
m_pThis = this;
|
|
m_hWndParent = hWnd;
|
|
if(g_bWinNT5)
|
|
return (CStructuralSubset*)DialogBoxW(_Module.GetResourceInstance(), MAKEINTRESOURCEW(DLG_FILTERS), hWnd, FilterDlgProc);
|
|
else
|
|
return (CStructuralSubset*)DialogBox(_Module.GetResourceInstance(), MAKEINTRESOURCE(DLG_FILTERS), hWnd, FilterDlgProc);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* MyFilterLBFunc()
|
|
*
|
|
* ListBox subclasser. Used for our filter construction LB's. Needed so
|
|
* we can do proper mouse move processing for diddiling the cursor and
|
|
* implementing the silly collpase feature.
|
|
*
|
|
* ENTRY:
|
|
* The usual window procedure parameters.
|
|
*
|
|
* EXIT:
|
|
* a long LRESULT.
|
|
*
|
|
*****************************************************************************/
|
|
LRESULT CDefineSS::MyFilterLBFunc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int iTop, iHeight, iItem;
|
|
POINT pt;
|
|
CFolder* pFgt;
|
|
|
|
switch (uiMsg)
|
|
{
|
|
case WM_SETCURSOR:
|
|
m_pThis->m_giXpos = -1;
|
|
iTop = (int)SendMessage(hWnd, LB_GETTOPINDEX, 0, 0L);
|
|
iHeight = (int)SendMessage(hWnd, LB_GETITEMHEIGHT, 0, 0L);
|
|
GetCursorPos(&pt);
|
|
ScreenToClient(hWnd, &pt);
|
|
//
|
|
// Compute index of the item the cursor is currently hovering over
|
|
// and then get the item data for that item so we can determine it's
|
|
// level and diddle the cursor appropiatly.
|
|
//
|
|
iItem = ((pt.y / iHeight) + iTop);
|
|
if ( (pFgt = (CFolder*)SendMessage(hWnd, LB_GETITEMDATA, iItem, (LPARAM)0)) != (CFolder*)LB_ERR )
|
|
{
|
|
if ( pFgt->iLevel >= 1 )
|
|
{
|
|
if ( pt.x < (pFgt->iLevel * m_pThis->m_giIndentSpacing) )
|
|
{
|
|
SetCursor(LoadCursor(_Module.GetResourceInstance(),MAKEINTRESOURCE(IDC_COLLAPSE)));
|
|
m_pThis->m_giXpos = pt.x;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
PostMessage(hWnd, WM_SETCURSOR, 0, 0L);
|
|
break;
|
|
|
|
// HACKHACK - we have a hidden button called IDOK that
|
|
// we use to trap VK_RETURNs so we can send these to
|
|
// the list boxes or other controls
|
|
case WM_SETFOCUS:
|
|
m_pThis->SetDefaultButton( GetParent(hWnd), IDOK, FALSE );
|
|
break;
|
|
|
|
}
|
|
return(CallWindowProc(m_pThis->m_lpfnOrigLBProc, hWnd, uiMsg, wParam, lParam));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FilterDlgProc()
|
|
*
|
|
* Filter dialog handler
|
|
*
|
|
* ENTRY:
|
|
* Standard windows callback params.
|
|
*
|
|
* EXIT:
|
|
* BOOL - TRUE if we handled it. FALSE if we didn't.
|
|
*
|
|
****************************************************************************/
|
|
INT_PTR CDefineSS::FilterDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int i;
|
|
CFolder* pfgt;
|
|
|
|
switch(msg)
|
|
{
|
|
case WM_INITDIALOG: {
|
|
//
|
|
// Set the this pointers for our LB message hook procedures. Also, set the procedure hooks
|
|
// for our LB's so we can do cursor changes, tree collapse and proper keyboard interface.
|
|
//
|
|
SendMessage(GetDlgItem(hDlg,IDF_AVAIL), WM_SETFONT, (WPARAM)m_pThis->m_pCollection->m_phmData->GetContentFont(), 0);
|
|
SendMessage(GetDlgItem(hDlg,IDF_RANGE), WM_SETFONT, (WPARAM)m_pThis->m_pCollection->m_phmData->GetContentFont(), 0);
|
|
SendMessage(GetDlgItem(hDlg,IDF_RANGES), WM_SETFONT, (WPARAM)m_pThis->m_pCollection->m_phmData->GetContentFont(), 0);
|
|
// SendMessage(GetDlgItem(hDlg,IDF_RANGES), WM_SETFONT, (WPARAM)_Resource.GetUIFont(), 0);
|
|
m_pThis->m_lpfnOrigLBProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hDlg,IDF_RANGE), GWLP_WNDPROC, (LONG_PTR)MyFilterLBFunc);
|
|
SetWindowLongPtr(GetDlgItem(hDlg,IDF_AVAIL), GWLP_WNDPROC, (LONG_PTR)MyFilterLBFunc);
|
|
SendDlgItemMessage(hDlg, IDF_SAVE_EC, EM_LIMITTEXT, MAX_SS_NAME_LEN - 1, 0L);
|
|
SendMessage(GetDlgItem(hDlg,IDF_SAVE_EC), WM_SETFONT, (WPARAM)m_pThis->m_pCollection->m_phmData->GetContentFont(), 0);
|
|
m_pThis->InitDialog(hDlg, NULL);
|
|
m_pThis->m_bShouldSave = FALSE;
|
|
// set the Close button as the default button
|
|
m_pThis->SetDefaultButton( hDlg, IDCANCEL, FALSE );
|
|
m_pThis->m_giXpos = -1;
|
|
break;
|
|
}
|
|
|
|
case WM_CHARTOITEM: // swallow these
|
|
return( -2 );
|
|
|
|
case WM_VKEYTOITEM:
|
|
switch (CmdID)
|
|
{
|
|
case 0xBD: // '-'
|
|
case VK_SUBTRACT:
|
|
pfgt=(CFolder*)SendMessage(CmdHwnd, LB_GETITEMDATA, CmdCode, (LPARAM)0);
|
|
m_pThis->ExpandContract(CmdHwnd, pfgt, FALSE, (HWND)CmdHwnd == GetDlgItem(hDlg,IDF_AVAIL));
|
|
return(-2);
|
|
|
|
case VK_HOME:
|
|
if (CmdID == VK_HOME && !(GetKeyState(VK_CONTROL) & 0x8000 ))
|
|
return( -1 );
|
|
|
|
// otherwise fall through
|
|
|
|
case VK_LEFT:
|
|
if ( ((CmdID == VK_HOME)||(CmdID == VK_LEFT)) && (GetKeyState(VK_CONTROL) & 0x8000 ))
|
|
{
|
|
// Close everyone
|
|
pfgt= (CFolder*)SendMessage(CmdHwnd, LB_GETITEMDATA, 0, (LPARAM)0);
|
|
while (pfgt->pNext)
|
|
{
|
|
|
|
m_pThis->ExpandContract(CmdHwnd, pfgt, FALSE, (HWND)CmdHwnd == GetDlgItem(hDlg,IDF_AVAIL));
|
|
pfgt = pfgt->pNext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pfgt= (CFolder*)SendMessage(CmdHwnd, LB_GETITEMDATA, CmdCode, (LPARAM)0);
|
|
m_pThis->ExpandContract(CmdHwnd, pfgt, FALSE, (HWND)CmdHwnd == GetDlgItem(hDlg,IDF_AVAIL));
|
|
}
|
|
return(-2);
|
|
|
|
case VK_BACK: { // go to parent
|
|
CFolder* pFgtCaret;
|
|
int caret;
|
|
|
|
if ( (caret = (int)SendMessage(CmdHwnd,LB_GETCARETINDEX,0,0L)) == -1 )
|
|
return -2;
|
|
if (! (pFgtCaret=(CFolder*)SendMessage(CmdHwnd, LB_GETITEMDATA, caret, (LPARAM)0)) )
|
|
return -2;
|
|
pFgtCaret = pFgtCaret->pParent;
|
|
if( pFgtCaret )
|
|
{
|
|
SendMessage(CmdHwnd, LB_SETSEL, FALSE, MAKELPARAM(caret,0));
|
|
if ( (caret = (int)SendMessage(CmdHwnd,LB_FINDSTRINGEXACT,0,(LPARAM)pFgtCaret)) == -1 )
|
|
caret = 0;
|
|
SendMessage(CmdHwnd, LB_SETCARETINDEX, caret, 0L);
|
|
SendMessage(CmdHwnd, LB_SETSEL, TRUE, MAKELPARAM(caret,0));
|
|
}
|
|
return(-2);
|
|
}
|
|
|
|
case 0xBB: // '+'
|
|
case VK_ADD:
|
|
pfgt=(CFolder*)SendMessage(CmdHwnd, LB_GETITEMDATA, CmdCode, (LPARAM)0);
|
|
m_pThis->ExpandContract(CmdHwnd, pfgt, TRUE, (HWND)CmdHwnd == GetDlgItem(hDlg,IDF_AVAIL) );
|
|
return(-2);
|
|
|
|
case VK_RIGHT: {
|
|
INT ci;
|
|
|
|
pfgt=(CFolder*)SendMessage(CmdHwnd, LB_GETITEMDATA, CmdCode, (LPARAM)0);
|
|
m_pThis->ExpandContract(CmdHwnd, pfgt, TRUE, (HWND)CmdHwnd == GetDlgItem(hDlg,IDF_AVAIL));
|
|
|
|
// select the first child if it has one
|
|
if ( CmdID == VK_RIGHT && pfgt->pKid && (ci = (INT)SendMessage(CmdHwnd, LB_GETCARETINDEX, 0, 0L)) != LB_ERR ) {
|
|
SendMessage(CmdHwnd, LB_SETSEL, FALSE, MAKELPARAM(ci,0));
|
|
SendMessage(CmdHwnd, LB_SETCARETINDEX, ci++, MAKELPARAM(0,0));
|
|
SendMessage(CmdHwnd, LB_SETSEL, TRUE, MAKELPARAM(ci,0));
|
|
}
|
|
|
|
return(-2);
|
|
}
|
|
|
|
case VK_RETURN:
|
|
SendMessage( CmdHwnd, WM_COMMAND, ((HWND)CmdHwnd == GetDlgItem(hDlg,IDF_AVAIL))?IDF_AVAIL:IDF_RANGE,
|
|
MAKELPARAM(CmdHwnd,LBN_DBLCLK));
|
|
return(-2);
|
|
|
|
}
|
|
return(-1); // Tell dlgmgr we didn't handle this key stroke.
|
|
|
|
case WM_NCLBUTTONDOWN:
|
|
SendDlgItemMessage(hDlg,IDF_RANGES,CB_SHOWDROPDOWN,FALSE,0L);
|
|
return(FALSE);
|
|
|
|
case WM_DRAWITEM:
|
|
// draw one of our listbox items
|
|
m_pThis->DrawFBItem(hDlg, (LPDRAWITEMSTRUCT)lParam, (UINT)wParam);
|
|
break;
|
|
|
|
case WM_MEASUREITEM:
|
|
// report how big our items are
|
|
m_pThis->MeasureFBItem((LPMEASUREITEMSTRUCT)lParam);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
if ( m_pThis->FilterDlgCommand(hDlg, wParam, lParam) )
|
|
{
|
|
i = m_pThis->FillFilterTree(GetDlgItem(hDlg,IDF_RANGE), FALSE, FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDF_REMOVE), (i != 0));
|
|
EnableWindow(GetDlgItem(hDlg,IDF_REMOVEALL), (i != 0));
|
|
i = m_pThis->FillFilterTree(GetDlgItem(hDlg,IDF_AVAIL), TRUE, FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDF_ADD), (i != 0));
|
|
EnableWindow(GetDlgItem(hDlg,IDF_ADDALL), (i != 0));
|
|
m_pThis->SetSaveStatus(hDlg);
|
|
}
|
|
else
|
|
return FALSE;
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
// Closing the Dialog behaves the same as Cancel
|
|
PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0L);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
break;
|
|
|
|
default:
|
|
return FALSE; // say we didn't handle it
|
|
break;
|
|
}
|
|
return TRUE; // say we did handle it
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* MeasureFBItem()
|
|
*
|
|
* Set appropiate size entries in the measure item struc according to the
|
|
* items we'll be rendering in the filter listboxes.
|
|
*
|
|
* ENTRY:
|
|
* hDlg - Handle to the filter dialog.
|
|
* lpMIS - Pointer to the LPMEASUREITEMSTRUCT
|
|
*
|
|
* EXIT:
|
|
* None.
|
|
*
|
|
*****************************************************************************/
|
|
void CDefineSS::MeasureFBItem(LPMEASUREITEMSTRUCT lpMIS)
|
|
{
|
|
POINT pt;
|
|
HDC hDC;
|
|
HFONT hOldFont;
|
|
TEXTMETRIC tm;
|
|
|
|
ImageList_GetIconSize(m_hIL, (int*)&pt.x, (int*)&pt.y);
|
|
hDC = GetDC(NULL);
|
|
hOldFont = (HFONT)SelectObject(hDC, m_pCollection->m_phmData->GetContentFont());
|
|
GetTextMetrics(hDC, &tm);
|
|
SelectObject(hDC, hOldFont);
|
|
ReleaseDC(NULL, hDC);
|
|
|
|
if ( pt.y > tm.tmHeight )
|
|
lpMIS->itemHeight = pt.y;
|
|
else
|
|
lpMIS->itemHeight = tm.tmHeight;
|
|
|
|
m_giIndentSpacing = pt.x;
|
|
m_iFontHeight = tm.tmHeight;
|
|
m_iGlyphX = pt.x;
|
|
m_iGlyphY = pt.y;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* InitDialog()
|
|
*
|
|
* Do all initialization of the range definition dialog for a particular
|
|
* situation, Add, Delete or Change.
|
|
*
|
|
* ENTRY:
|
|
* hDlg - The handle to the dialog.
|
|
* szRange - Pointer to new range or NULL if initing the range combo-box.
|
|
*
|
|
* EXIT:
|
|
* BOOL - TRUE on success, FALSE on failure.
|
|
*
|
|
****************************************************************************/
|
|
BOOL CDefineSS::InitDialog(HWND hDlg, LPSTR szRange)
|
|
{
|
|
int iSel = 0;
|
|
int i = 0;
|
|
int iFilterCnt = 0;
|
|
BOOL bDef = FALSE;
|
|
CStructuralSubset* pSS = NULL;
|
|
|
|
SendDlgItemMessage(hDlg,IDF_RANGES,CB_SETEXTENDEDUI,TRUE,0L);
|
|
|
|
if (! m_pCollection->m_pSSList )
|
|
return(FALSE);
|
|
|
|
if (! szRange )
|
|
{
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_RESETCONTENT, 0, 0L);
|
|
while ( (pSS = m_pCollection->m_pSSList->GetNextSubset(pSS)) )
|
|
{
|
|
i = (int) SendDlgItemMessage(hDlg, IDF_RANGES, CB_INSERTSTRING, (WPARAM) -1, (LPARAM)(LPSTR)pSS->GetName());
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_SETITEMDATA, i, (LPARAM)((DWORD_PTR)pSS));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (iSel = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_FINDSTRINGEXACT,0, (LPARAM)szRange)) != LB_ERR )
|
|
pSS = (CStructuralSubset*)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETITEMDATA, iSel,0L);
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_SETCURSEL, iSel, 0L);
|
|
bDef = TRUE;
|
|
}
|
|
//
|
|
// Select the current filter. If it's "entire CD" select "New".
|
|
//
|
|
if (! bDef || ! pSS)
|
|
{
|
|
pSS = m_pCollection->m_pSSList->GetNew();
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)pSS->GetName());
|
|
}
|
|
//
|
|
// Init the listboxes!
|
|
//
|
|
SetRangeToTree(pSS);
|
|
//
|
|
// Init filter LB and remove options.
|
|
//
|
|
i = FillFilterTree(GetDlgItem(hDlg,IDF_RANGE), FALSE, FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDF_REMOVE), (i != 0));
|
|
EnableWindow(GetDlgItem(hDlg,IDF_REMOVEALL), (i != 0));
|
|
//
|
|
// Init available LB and add options.
|
|
//
|
|
i = FillFilterTree(GetDlgItem(hDlg,IDF_AVAIL), TRUE, FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDF_ADD), (i != 0));
|
|
EnableWindow(GetDlgItem(hDlg,IDF_ADDALL), (i != 0));
|
|
//
|
|
// deal with buttons
|
|
//
|
|
if ( pSS->IsReadOnly() )
|
|
{
|
|
EnableWindow(GetDlgItem(hDlg, IDF_DELETE), FALSE);
|
|
SetWindowText(GetDlgItem(hDlg,IDF_SAVE_EC),GetStringResource(IDS_UNTITLED_SUBSET));
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(GetDlgItem(hDlg, IDF_DELETE), TRUE);
|
|
SetWindowText(GetDlgItem(hDlg,IDF_SAVE_EC), pSS->GetName());
|
|
}
|
|
EnableWindow(GetDlgItem(hDlg,IDF_SAVE), FALSE);
|
|
return(TRUE);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DoesNodeHaveANext()
|
|
*
|
|
* aids DrawFBItem() in painting the vertical connecting codes in the filter
|
|
* list boxes.
|
|
*
|
|
* ENTRY:
|
|
* LbId - ListBox ID.
|
|
* n - What level are we painting.
|
|
* pFgt - Pointer to the node we're painting.
|
|
*
|
|
* EXIT:
|
|
* BOOL - TRUE if a next will appear on the given level. FALSE otherwise.
|
|
*
|
|
****************************************************************************/
|
|
BOOL CDefineSS::DoesNodeHaveANext(UINT LbId, int n, CFolder* pFgt)
|
|
{
|
|
while ( pFgt->iLevel > (n + 1) )
|
|
pFgt = pFgt->pParent;
|
|
|
|
while ( pFgt->pNext )
|
|
{
|
|
pFgt = pFgt->pNext;
|
|
if ( (PRESENT((LbId == IDF_AVAIL), pFgt)) )
|
|
return(TRUE);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DrawFBItem()
|
|
*
|
|
* Function is responsible for painting the items in the owner-draw list boxes
|
|
* that are used in the filter contents dialog.
|
|
*
|
|
* ENTRY:
|
|
* hDlg - Handle to the dialog.
|
|
* lpDI - Pointer to the draw item struct. Contains a pFgt to the item
|
|
* we're painting.
|
|
* Lbid - ListBox identifier.
|
|
*
|
|
* EXIT:
|
|
* None.
|
|
*
|
|
****************************************************************************/
|
|
void CDefineSS::DrawFBItem(HWND hDlg, LPDRAWITEMSTRUCT lpDI, UINT LbId)
|
|
{
|
|
RECT rc;
|
|
int n;
|
|
WORD wTopText;
|
|
HDC hDC;
|
|
CFolder* pFgt;
|
|
INT xBitmap = 0;
|
|
int iHeight;
|
|
BOOL bHasNext;
|
|
char* lpszText;
|
|
char szScratch[MAX_PATH];
|
|
static int bNoReEnter = 0;
|
|
|
|
if (lpDI->itemID == 0xFFFF)
|
|
return;
|
|
pFgt = (CFolder*)lpDI->itemData;
|
|
if (!pFgt || lpDI->itemData == -1 )
|
|
return;
|
|
|
|
bNoReEnter++;
|
|
if ( bNoReEnter > 1 )
|
|
{
|
|
bNoReEnter--;
|
|
return;
|
|
}
|
|
|
|
hDC = lpDI->hDC;
|
|
rc = lpDI->rcItem;
|
|
iHeight = rc.bottom - rc.top;
|
|
wTopText = (WORD)((rc.top + ((iHeight) / 2)) - ((m_iFontHeight - 2) / 2));
|
|
/*
|
|
* Paint the proper lines based on the level and position of the item.
|
|
*/
|
|
if ( pFgt->iLevel )
|
|
{
|
|
for ( n = 0; n < pFgt->iLevel; n++ )
|
|
{
|
|
// Draw the verticle line indicating level. Remember to not leave a
|
|
// "tail".
|
|
//
|
|
bHasNext = DoesNodeHaveANext(LbId, n, pFgt);
|
|
if ( bHasNext )
|
|
QRect(hDC, rc.left + m_iGlyphX / 2, rc.top, // Full verticle line.
|
|
1, iHeight, COLOR_WINDOWTEXT);
|
|
else if ( n == (pFgt->iLevel - 1) )
|
|
QRect(hDC, rc.left + m_iGlyphX / 2, rc.top, // Half vertical line.
|
|
1, (iHeight / 2), COLOR_WINDOWTEXT);
|
|
|
|
rc.left += m_iGlyphX;
|
|
}
|
|
// Draw the horizontal connector line.
|
|
//
|
|
QRect(hDC, (rc.left - m_iGlyphX / 2), (rc.top + (m_iGlyphY / 2)), ((m_iGlyphX / 2)), 1, COLOR_WINDOWTEXT);
|
|
}
|
|
//
|
|
// draw the little book.
|
|
//
|
|
if ( EXPANDED((LbId == IDF_AVAIL), pFgt) ) // Is the book open or closed ?
|
|
xBitmap++; // It's an open book.
|
|
ImageList_Draw(m_hIL, xBitmap, hDC, rc.left+1, rc.top, ILD_NORMAL);
|
|
rc.left += m_iGlyphX + 2;
|
|
if ( lpDI->itemState & ODS_SELECTED )
|
|
{
|
|
SetBkColor(hDC, GetSysColor(COLOR_HIGHLIGHT));
|
|
SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
|
|
}
|
|
else
|
|
{
|
|
SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
|
|
SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
|
|
}
|
|
//
|
|
// Now paint the text.
|
|
//
|
|
if ( pFgt->f_HasHash && pFgt->pExTitle )
|
|
{
|
|
pFgt->pExTitle->GetTopicLocation(0, szScratch, sizeof(szScratch));
|
|
lpszText = szScratch;
|
|
}
|
|
else
|
|
lpszText = pFgt->Title;
|
|
//
|
|
// Set LB Horizontal extent based upon the width of the string.
|
|
//
|
|
int iWidth, len = lstrlen(lpszText);
|
|
SIZE size;
|
|
|
|
if ( lpDI->itemAction == ODA_DRAWENTIRE )
|
|
{
|
|
GetTextExtentPoint32(hDC, lpszText, len, &size);
|
|
size.cx += rc.left + 4;
|
|
iWidth = (int)SendDlgItemMessage(hDlg, LbId, LB_GETHORIZONTALEXTENT, 0, 0L);
|
|
if ( size.cx > iWidth )
|
|
SendDlgItemMessage(hDlg, LbId, LB_SETHORIZONTALEXTENT, (WPARAM)size.cx, 0L);
|
|
}
|
|
//
|
|
// Paint the string.
|
|
//
|
|
ExtTextOut(hDC, (rc.left + 2), wTopText, ETO_OPAQUE | ETO_CLIPPED, &rc, lpszText, len, NULL);
|
|
if (lpDI->itemState & ODS_FOCUS)
|
|
DrawFocusRect(hDC,&rc);
|
|
|
|
bNoReEnter--;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* SetRangeToTree()
|
|
*
|
|
* Function will set the filter tree up according the the subset specified
|
|
* by the given CStructuraSubset pointer.
|
|
*
|
|
* ENTRY:
|
|
* CStructuralSubset* A NULL object is the equivelent to RF_NONE.
|
|
* Use of this feature will cause all nodes to be
|
|
* removed from the filter.
|
|
*
|
|
* EXIT:
|
|
* BOOL - TRUE for success. FALSE for failure!
|
|
*
|
|
****************************************************************************/
|
|
BOOL CDefineSS::SetRangeToTree(CStructuralSubset* pSS)
|
|
{
|
|
CFolder* pFgt; // pointer to filtered group tree.
|
|
UINT i = 0;
|
|
HASH Hash;
|
|
|
|
pFgt = m_pCollection->m_Collection.GetVisableRootFolder();
|
|
if ( pSS )
|
|
{
|
|
if ( pSS->m_dwFlags & SS_ENTIRE_CONTENTS )
|
|
{
|
|
while ( pFgt )
|
|
{
|
|
AddNode2Filter(pFgt);
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// De-select all nodes filter bits and select the proper
|
|
// tree nodes based on the hash list!
|
|
//
|
|
while ( pFgt )
|
|
{
|
|
RemoveNodeFromFilter(pFgt);
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
while ( Hash = pSS->EnumHashes(i++) )
|
|
{
|
|
if ( (pFgt = m_pCollection->m_pCSlt->HashToCFolder(Hash)) )
|
|
AddNode2Filter(pFgt);
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
while ( pFgt )
|
|
{
|
|
RemoveNodeFromFilter(pFgt);
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* GetRangeFromTree()
|
|
*
|
|
* Function will extract a range from the filter LB and return a handle to
|
|
* the range.
|
|
*
|
|
* ENTRY:
|
|
* sz - Range name.
|
|
*
|
|
* EXIT:
|
|
*
|
|
****************************************************************************/
|
|
CStructuralSubset* CDefineSS::GetRangeFromTree(LPSTR sz)
|
|
{
|
|
CFolder* pFgt;
|
|
CFolder* pFgtLast;
|
|
int i = 0;
|
|
CStructuralSubset* pSS;
|
|
CExTitle* pExTitle;
|
|
|
|
if (! (pSS = new CStructuralSubset(sz)) )
|
|
return NULL; // Young girls are chaining themselves to the axels of big mac trucks.
|
|
|
|
// Walk root level nodes and gather up the prefix hashes from each node
|
|
// and all it's kids if the filter bit is set.
|
|
//
|
|
pFgt = m_pCollection->m_Collection.GetVisableRootFolder();
|
|
while ( pFgt )
|
|
{
|
|
do
|
|
{
|
|
pFgtLast = pFgt;
|
|
if ( pFgt->f_Filter && !pFgt->f_Available && pFgt->f_HasHash )
|
|
{
|
|
pSS->AddHash(pFgt->pExTitle->m_dwHash);
|
|
//
|
|
// Any merged .CHM's ?
|
|
//
|
|
pExTitle = pFgt->pExTitle->m_pKid;
|
|
while ( pExTitle )
|
|
{
|
|
pSS->AddHash(pExTitle->m_dwHash);
|
|
pExTitle = pExTitle->m_pNextKid;
|
|
}
|
|
i++;
|
|
break;
|
|
}
|
|
pFgt = pFgt->pKid;
|
|
|
|
} while ( pFgt );
|
|
|
|
pFgt = pFgtLast;
|
|
while ( pFgt && (! (pFgt->pNext)) )
|
|
pFgt = pFgt->pParent;
|
|
if ( pFgt )
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
// Lastly, setup the range entry and add it to the range list.
|
|
//
|
|
if (! i )
|
|
{
|
|
delete pSS;
|
|
return(NULL);
|
|
}
|
|
return pSS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SetSaveStatus()
|
|
*
|
|
* If you don't want'em saving then disable the save button!
|
|
*
|
|
* ENTRY:
|
|
* hDlg - Handle to the define contents dialog.
|
|
*
|
|
* EXIT:
|
|
* None.
|
|
*
|
|
****************************************************************************/
|
|
void CDefineSS::SetSaveStatus(HWND hDlg)
|
|
{
|
|
char sz[MAX_SS_NAME_LEN];
|
|
int i;
|
|
CStructuralSubset* pSS;
|
|
HWND hWnd = GetDlgItem(hDlg, IDF_SAVE);
|
|
|
|
GetWindowText(GetDlgItem(hDlg, IDF_SAVE_EC), sz, sizeof(sz));
|
|
i = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETCURSEL, 0, 0L);
|
|
pSS = (CStructuralSubset*)SendDlgItemMessage(hDlg,IDF_RANGES,CB_GETITEMDATA,i,0L);
|
|
|
|
if ( ((pSS->m_dwFlags & SS_READ_ONLY) && (! lstrcmpi(pSS->GetName(), sz))) ||
|
|
(! SendDlgItemMessage(hDlg, IDF_RANGE, LB_GETCOUNT, 0, 0L)) ||
|
|
(! lstrlen(sz)) || !m_bShouldSave )
|
|
EnableWindow(hWnd, FALSE);
|
|
else
|
|
EnableWindow(hWnd, TRUE);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ShouldSave()
|
|
*
|
|
* Alert the user that they may want to save the changes they've made to
|
|
* the filter.
|
|
*
|
|
* ENTRY:
|
|
* hDlg - Handle to the filter definition dialog.
|
|
*
|
|
* EXIT:
|
|
* INT - IDYES, IDNO or IDCANCEL. 0 if saving is not necessary.
|
|
*
|
|
****************************************************************************/
|
|
INT CDefineSS::ShouldSave(HWND hDlg)
|
|
{
|
|
char sz[MAX_SS_NAME_LEN];
|
|
|
|
if ( IsWindowEnabled(GetDlgItem(hDlg, IDF_SAVE)) )
|
|
{
|
|
GetWindowText(GetDlgItem(hDlg, IDF_SAVE_EC), sz, sizeof(sz));
|
|
return MsgBox(IDS_SAVE_FILTER, sz, MB_YESNOCANCEL | MB_ICONQUESTION);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* FilterDlgCommand()
|
|
*
|
|
* Function handles all WM_COMMAND messages from the filter dialog.
|
|
*
|
|
* ENTRY:
|
|
* hDlg - Handle to the dialog.
|
|
* wParam - Message dependent.
|
|
* lParam - Message dependent.
|
|
*
|
|
* EXIT:
|
|
* Returns TRUE is range list boxes need updating. False otherwise.
|
|
*
|
|
***************************************************************************/
|
|
BOOL CDefineSS::FilterDlgCommand(HWND hDlg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int i, n;
|
|
CFolder* pFgt;
|
|
char sz[MAX_SS_NAME_LEN];
|
|
static il;
|
|
static HWND hWndCombo = NULL;
|
|
|
|
switch (CmdID)
|
|
{
|
|
// HACKHACK - this is an invisible button that we use to route double-clicks
|
|
// to other controls
|
|
case IDOK:
|
|
{
|
|
HWND hWndCurrent, hWndFocus;
|
|
int id = 0;
|
|
// find out which control we are on
|
|
if( (hWndFocus = GetFocus()) )
|
|
{
|
|
hWndCurrent = GetDlgItem(hDlg,IDF_AVAIL);
|
|
if( hWndFocus == hWndCurrent )
|
|
id = IDF_AVAIL;
|
|
hWndCurrent = GetDlgItem(hDlg,IDF_RANGE);
|
|
if( hWndFocus == hWndCurrent )
|
|
id = IDF_RANGE;
|
|
hWndCurrent = GetDlgItem(hDlg,IDF_RANGES);
|
|
if( hWndFocus == hWndCurrent )
|
|
id = IDF_RANGES;
|
|
|
|
SendMessage( hDlg, WM_COMMAND, MAKEWPARAM(id,LBN_DBLCLK), 0L);
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
case IDF_SAVE:
|
|
if ( SaveFilter(hDlg) )
|
|
{
|
|
SetDefaultButton( hDlg, IDCANCEL, TRUE );
|
|
i = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETCURSEL, 0, 0L);
|
|
m_pSS = (CStructuralSubset*)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETITEMDATA, i, 0L);
|
|
EnableWindow(GetDlgItem(hDlg, IDF_DELETE), TRUE);
|
|
m_bShouldSave = FALSE;
|
|
}
|
|
else
|
|
SetFocus(GetDlgItem(hDlg, IDF_RANGES));
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
// if a combo box is open then close it and don't close the dialog
|
|
if( hWndCombo )
|
|
{
|
|
SendMessage( hWndCombo, CB_SHOWDROPDOWN, FALSE, 0L );
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Has the range been modified ? Might it need to be saved.
|
|
//
|
|
if ( m_bShouldSave )
|
|
{
|
|
i = ShouldSave(hDlg);
|
|
if ( i == IDYES )
|
|
{
|
|
if (! SaveFilter(hDlg) )
|
|
{
|
|
SetFocus(GetDlgItem(hDlg, IDF_RANGES));
|
|
break;
|
|
}
|
|
i = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETCURSEL, 0, 0L);
|
|
m_pSS = (CStructuralSubset*)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETITEMDATA, i, 0L);
|
|
m_bShouldSave = FALSE;
|
|
}
|
|
else if ( i == IDCANCEL )
|
|
break;
|
|
}
|
|
EndDialog(hDlg, (INT_PTR)m_pSS);
|
|
break;
|
|
|
|
case IDF_DELETE:
|
|
if ( (i = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETCURSEL, 0, 0L)) != CB_ERR )
|
|
{
|
|
int n;
|
|
m_pSS = (CStructuralSubset*)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETITEMDATA, i, 0L);
|
|
if ( MsgBox(IDS_CONFIRM_SSDELETE, m_pSS->GetName(), MB_YESNO | MB_ICONQUESTION) == IDYES )
|
|
{
|
|
if ( (i = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_FINDSTRINGEXACT, 0, (LPARAM)m_pSS->GetName())) != LB_ERR)
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_DELETESTRING, i, 0L);
|
|
m_pCollection->m_pSSList->DeleteSubset(m_pSS, NULL, m_hWndParent);
|
|
n = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETCOUNT, 0, 0L);
|
|
n--;
|
|
i = min(n,i);
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_SETCURSEL, i, 0L);
|
|
i = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETCURSEL, 0, 0L);
|
|
m_pSS = (CStructuralSubset*)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETITEMDATA, i, 0L);
|
|
InitDialog(hDlg, m_pSS->GetName());
|
|
m_bShouldSave = FALSE;
|
|
return(FALSE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDF_SAVE_EC:
|
|
switch (CmdCode)
|
|
{
|
|
case EN_UPDATE:
|
|
m_bShouldSave = TRUE;
|
|
SetSaveStatus(hDlg);
|
|
SetDefaultButton( hDlg, IDF_SAVE, FALSE );
|
|
break;
|
|
|
|
case EN_CHANGE:
|
|
m_bShouldSave = TRUE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IDF_RANGES:
|
|
switch (CmdCode)
|
|
{
|
|
case CBN_DROPDOWN:
|
|
il = (int)SendDlgItemMessage(hDlg,IDF_RANGES,CB_GETCURSEL,0,0L);
|
|
break;
|
|
|
|
case CBN_SELCHANGE:
|
|
i = (int)SendDlgItemMessage(hDlg,IDF_RANGES,CB_GETCURSEL,0,0L);
|
|
SendDlgItemMessage(hDlg,IDF_RANGES,CB_GETLBTEXT,i,(LPARAM)sz);
|
|
if ( m_bShouldSave )
|
|
{
|
|
n = ShouldSave(hDlg);
|
|
if ( n == IDCANCEL )
|
|
goto cancel_it;
|
|
else if ( n == IDYES )
|
|
{
|
|
if (! SaveFilter(hDlg) )
|
|
goto cancel_it;
|
|
}
|
|
//SetDefaultButton( hDlg, IDCANCEL, TRUE );
|
|
}
|
|
i = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETCURSEL, 0, 0L);
|
|
m_pSS = (CStructuralSubset*)SendDlgItemMessage(hDlg, IDF_RANGES, CB_GETITEMDATA, i, 0L);
|
|
m_bShouldSave = FALSE;
|
|
InitDialog(hDlg, sz);
|
|
return(FALSE);
|
|
cancel_it:
|
|
SendDlgItemMessage(hDlg,IDF_RANGES,CB_SETCURSEL,(WPARAM)il,0L);
|
|
}
|
|
break;
|
|
|
|
case IDHELP:
|
|
break;
|
|
|
|
|
|
case IDF_NEW:
|
|
m_bShouldSave = TRUE;
|
|
SetSaveStatus(hDlg);
|
|
break;
|
|
|
|
case IDF_REMOVE:
|
|
n = (int)SendDlgItemMessage(hDlg, IDF_RANGE, LB_GETCOUNT, 0, 0L);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
if (SendDlgItemMessage(hDlg, IDF_RANGE, LB_GETSEL, i, 0L))
|
|
{
|
|
pFgt=(CFolder*)SendDlgItemMessage(hDlg,IDF_RANGE,LB_GETITEMDATA,i, (LPARAM)0);
|
|
RemoveNodeFromFilter(pFgt);
|
|
SendDlgItemMessage(hDlg, IDF_RANGE, LB_SETSEL, 0, (LPARAM)i);
|
|
m_bShouldSave = TRUE;
|
|
}
|
|
}
|
|
SetFocus(GetDlgItem(hDlg, IDF_RANGE));
|
|
return(TRUE);
|
|
break;
|
|
|
|
case IDF_ADDALL:
|
|
//
|
|
// Enumerate root level nodes Adding each!
|
|
//
|
|
pFgt = m_pCollection->m_Collection.GetVisableRootFolder();
|
|
while ( pFgt )
|
|
{
|
|
AddNode2Filter(pFgt);
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
m_bShouldSave = TRUE;
|
|
SetFocus(GetDlgItem(hDlg, IDF_RANGE));
|
|
return(TRUE);
|
|
break;
|
|
|
|
case IDF_ADD:
|
|
n = (int)SendDlgItemMessage(hDlg, IDF_AVAIL, LB_GETCOUNT, 0, 0L);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
if (SendDlgItemMessage(hDlg, IDF_AVAIL, LB_GETSEL, i, 0L))
|
|
{
|
|
pFgt=(CFolder*)SendDlgItemMessage(hDlg,IDF_AVAIL,LB_GETITEMDATA,i, (LPARAM)0);
|
|
AddNode2Filter(pFgt);
|
|
SendDlgItemMessage(hDlg, IDF_AVAIL, LB_SETSEL, 0, (LPARAM)i);
|
|
m_bShouldSave = TRUE;
|
|
}
|
|
}
|
|
SetFocus(GetDlgItem(hDlg, IDF_AVAIL));
|
|
return(TRUE);
|
|
break;
|
|
|
|
case IDF_REMOVEALL:
|
|
//
|
|
// Enumerate root level nodes removing each!
|
|
//
|
|
pFgt = m_pCollection->m_Collection.GetVisableRootFolder();
|
|
while ( pFgt )
|
|
{
|
|
RemoveNodeFromFilter(pFgt);
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
m_bShouldSave = TRUE;
|
|
SetFocus(GetDlgItem(hDlg, IDF_AVAIL));
|
|
return(TRUE);
|
|
break;
|
|
|
|
case IDF_RANGE:
|
|
switch (CmdCode)
|
|
{
|
|
case LBN_DBLCLK:
|
|
n = 0;
|
|
goto expcont;
|
|
}
|
|
break;
|
|
|
|
case IDF_AVAIL:
|
|
switch (CmdCode)
|
|
{
|
|
case LBN_DBLCLK:
|
|
n = 1;
|
|
expcont:
|
|
i = (int)SendDlgItemMessage(hDlg,CmdID,LB_GETCARETINDEX,0,0L);
|
|
if (i == (int)LB_ERR)
|
|
break;
|
|
|
|
pFgt=(CFolder*)SendDlgItemMessage(hDlg,CmdID,LB_GETITEMDATA,i, (LPARAM)0);
|
|
if ( !pFgt || pFgt == (CFolder*)-1 )
|
|
break;
|
|
//
|
|
// giXpos is set in my LB hook proc. If it's != -1 then this
|
|
// double click is intended as a collapsing dbl click and giXpos
|
|
// will indicate the x position within the LB where the double
|
|
// click occured. Sound reasonable ?
|
|
//
|
|
if ( m_giXpos > 0 )
|
|
{
|
|
int iLevel;
|
|
|
|
iLevel = m_giXpos / m_giIndentSpacing;
|
|
while ( (iLevel != pFgt->iLevel) && pFgt->pParent )
|
|
pFgt = pFgt->pParent;
|
|
}
|
|
ExpandContract(GetDlgItem(hDlg,CmdID), pFgt, -1, n);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch( CmdCode )
|
|
{
|
|
case CBN_DROPDOWN:
|
|
hWndCombo = (HWND) lParam;
|
|
SetDefaultButton( hDlg, IDCANCEL, FALSE );
|
|
break;
|
|
|
|
case CBN_CLOSEUP:
|
|
hWndCombo = NULL;
|
|
SetDefaultButton( hDlg, IDCANCEL, FALSE );
|
|
break;
|
|
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SaveFilter()
|
|
*
|
|
* Function saves a filter to the filter list, insuring a read-only filter
|
|
* is not over-written.
|
|
*
|
|
* ENTRY:
|
|
* hDlg - Handle to the filter definition dialog.
|
|
*
|
|
* EXIT:
|
|
* BOOL - TRUE Indicates the filter was saved. FALSE indicates filter
|
|
* was not saved.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL CDefineSS::SaveFilter(HWND hDlg)
|
|
{
|
|
char szGivenRN[MAX_SS_NAME_LEN];
|
|
int i = 0;
|
|
CStructuralSubset* pSS = NULL;
|
|
CStructuralSubset* pSSNew;
|
|
|
|
GetDlgItemText(hDlg, IDF_SAVE_EC, szGivenRN, sizeof(szGivenRN));
|
|
//
|
|
// Extract range from the filter LB.
|
|
//
|
|
if (! (pSSNew = GetRangeFromTree(szGivenRN)) )
|
|
return(FALSE);
|
|
//
|
|
// Insure we're not trashing a read only ramge.
|
|
//
|
|
while ( (pSS = m_pCollection->m_pSSList->GetNextSubset(pSS)) )
|
|
{
|
|
if (! lstrcmpi(pSS->GetName(), pSSNew->GetName()) )
|
|
{
|
|
if ( pSS->m_dwFlags & SS_READ_ONLY )
|
|
{
|
|
MsgBox(IDS_BAD_RANGE_NAME, MB_OK | MB_ICONEXCLAMATION);
|
|
return(FALSE);
|
|
}
|
|
// Sure you want to over-write the existing rnage ?
|
|
//
|
|
if ( MsgBox(IDS_OVERWRITE, MB_YESNO | MB_ICONQUESTION) == IDNO )
|
|
return(FALSE);
|
|
if ( (i = (int)SendDlgItemMessage(hDlg, IDF_RANGES, CB_FINDSTRINGEXACT, 0, (LPARAM)pSS->GetName())) != LB_ERR)
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_DELETESTRING, i, 0L);
|
|
m_pCollection->m_pSSList->DeleteSubset(pSS, pSSNew, m_hWndParent);
|
|
break;
|
|
}
|
|
}
|
|
// Add new subset to the list and update UI...
|
|
//
|
|
m_pCollection->m_pSSList->AddSubset(pSSNew, m_hWndParent);
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_INSERTSTRING, 0, (LPARAM)pSSNew->GetName());
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_SETITEMDATA, 0, (LPARAM)pSSNew);
|
|
SendDlgItemMessage(hDlg, IDF_RANGES, CB_SETCURSEL, 0, 0L);
|
|
EnableWindow(GetDlgItem(hDlg,IDF_SAVE), FALSE);
|
|
return(TRUE);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* RemoveNodeFromFilter()
|
|
*
|
|
* Function removes the node and all it's kids from the filter.
|
|
*
|
|
* ENTRY:
|
|
* pFgt - Poiner to the node to be removed.
|
|
*
|
|
* EXIT:
|
|
* None.
|
|
*
|
|
*****************************************************************************/
|
|
void CDefineSS::RemoveNodeFromFilter(CFolder* pFgti)
|
|
{
|
|
CFolder* pFgt;
|
|
CFolder* pFgtLast;
|
|
|
|
// First, de-select the node and any children of the node taking care
|
|
// to set the f_Available bit from each!
|
|
//
|
|
pFgt = pFgti;
|
|
if ( pFgt->pKid )
|
|
{
|
|
while ( pFgt )
|
|
{
|
|
do
|
|
{
|
|
pFgt->f_Available = 1;
|
|
pFgt->f_Filter = 0;
|
|
pFgtLast = pFgt;
|
|
pFgt = pFgt->pKid;
|
|
|
|
} while ( pFgt );
|
|
pFgt = pFgtLast;
|
|
while ( pFgt && (! (pFgt->pNext)) )
|
|
{
|
|
if ( pFgt->pParent != pFgti )
|
|
pFgt = pFgt->pParent;
|
|
else
|
|
break;
|
|
}
|
|
if ( pFgt )
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
}
|
|
// Next, assure any parents of the node are marked as available. We also
|
|
// have to take care to see that a parent who has no children who are
|
|
// part of the filter are removed from the filter!
|
|
//
|
|
pFgt = pFgti;
|
|
pFgt->f_Filter = 0;
|
|
pFgt->f_Available = 1;
|
|
|
|
while ( pFgt = pFgt->pParent )
|
|
{
|
|
pFgt->f_Available = 1;
|
|
SetFilterStatus(pFgt);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SetFilterStatus()
|
|
*
|
|
* If ALL children of the given node are marked as available, that nodes
|
|
* filter bit will be set to zero.
|
|
*
|
|
* ENTRY:
|
|
* pFgt - Pointer to the node to start from.
|
|
*
|
|
* EXIT:
|
|
*
|
|
****************************************************************************/
|
|
void CDefineSS::SetFilterStatus(CFolder* pFgt)
|
|
{
|
|
CFolder* pFgtl = pFgt->pKid;
|
|
CFolder* pFgtLast;
|
|
|
|
while ( pFgtl )
|
|
{
|
|
do
|
|
{
|
|
if (! pFgtl->f_Available )
|
|
return;
|
|
pFgtLast = pFgtl;
|
|
pFgtl = pFgtl->pKid;
|
|
} while ( pFgtl );
|
|
pFgtl = pFgtLast;
|
|
while ( pFgtl && (! (pFgtl->pNext)) && (pFgtl->pParent != pFgt) )
|
|
pFgtl = pFgtl->pParent;
|
|
if ( pFgtl )
|
|
pFgtl = pFgtl->pNext;
|
|
}
|
|
pFgt->f_Filter = 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* AddNode2Filter()
|
|
*
|
|
* Function adds a node to the filter assuring that necessary parents are
|
|
* added, and necessary nodes are removed from the available listbox.
|
|
*
|
|
* ENTRY:
|
|
* pFgti = Node to be added.
|
|
*
|
|
* EXIT:
|
|
* None.
|
|
*
|
|
*****************************************************************************/
|
|
void CDefineSS::AddNode2Filter(CFolder* pFgti)
|
|
{
|
|
CFolder* pFgt;
|
|
CFolder* pFgtLast;
|
|
|
|
// First, select the node and any children of the node taking care
|
|
// to remove the f_Available bit from each!
|
|
//
|
|
pFgt = pFgti;
|
|
while ( pFgt )
|
|
{
|
|
do
|
|
{
|
|
pFgt->f_Available = 0;
|
|
pFgt->f_Filter = 1;
|
|
|
|
pFgtLast = pFgt;
|
|
pFgt = pFgt->pKid;
|
|
|
|
} while ( pFgt );
|
|
pFgt = pFgtLast;
|
|
if ( pFgt == pFgti )
|
|
break;
|
|
while ( pFgt && (! (pFgt->pNext)) )
|
|
{
|
|
if ( pFgt->pParent != pFgti )
|
|
pFgt = pFgt->pParent;
|
|
else
|
|
break;
|
|
}
|
|
if ( pFgt )
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
// Next, assure parents are selected.
|
|
//
|
|
pFgt = pFgti;
|
|
if ( pFgt->pParent )
|
|
{
|
|
while ( pFgt = pFgt->pParent )
|
|
{
|
|
pFgt->f_Filter = 1;
|
|
pFgt->f_F_Open = 1;
|
|
SetAvailableStatus(pFgt); // Should it still be available ?
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SetAvailabilityStatus()
|
|
*
|
|
* If ALL children of the given node are selected as part of a filter, that
|
|
* nodes availability bit will be set to zero.
|
|
*
|
|
* ENTRY:
|
|
* pFgt - Pointer to the node to start from.
|
|
*
|
|
* EXIT:
|
|
*
|
|
****************************************************************************/
|
|
void CDefineSS::SetAvailableStatus(CFolder* pFgt)
|
|
{
|
|
CFolder* pFgtl = pFgt->pKid;
|
|
CFolder* pFgtLast;
|
|
|
|
while ( pFgtl )
|
|
{
|
|
do
|
|
{
|
|
if (! pFgtl->f_Filter )
|
|
return;
|
|
pFgtLast = pFgtl;
|
|
pFgtl = pFgtl->pKid;
|
|
} while ( pFgtl );
|
|
pFgtl = pFgtLast;
|
|
while ( pFgtl && (! (pFgtl->pNext)) && (pFgtl->pParent != pFgt) )
|
|
pFgtl = pFgtl->pParent;
|
|
if ( pFgtl )
|
|
pFgtl = pFgtl->pNext;
|
|
}
|
|
pFgt->f_Available = 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* ExpandContract()
|
|
*
|
|
* Function set's appropiate bits in the appropiate nodes to cause
|
|
* a parent node to expand or contract when FillRangeTree() is called.
|
|
*
|
|
* ENTRY:
|
|
* hwndLB - Handle to the listbox that contains the node were operating on.
|
|
*
|
|
* pFgt - Pointer to the node were expanding or contracting.
|
|
*
|
|
* sel - Identifies action: TRUE for Expansion
|
|
* FALSE for Contraction
|
|
* -1 for double-click which really means that
|
|
* we toggle the current state for the appropiate
|
|
* LB identified by the "which" arg.
|
|
*
|
|
* which - Which ListBox are we working with: TRUE == Available LB.
|
|
* FALSE == Range LB.
|
|
*
|
|
* EXIT:
|
|
* WORD - Returns the AX from FillRangeTree which happens to be the number
|
|
* of items placed in the LB.
|
|
*
|
|
*****************************************************************************/
|
|
WORD CDefineSS::ExpandContract(HWND hwndLB, CFolder* pFgt, int sel, BOOL which)
|
|
{
|
|
if (sel != 0 && sel != 1)
|
|
{
|
|
if (! pFgt->pKid )
|
|
return(0);
|
|
//
|
|
// This is the double click case.
|
|
//
|
|
if ( which ) {
|
|
if( pFgt->f_A_Open )
|
|
pFgt->f_A_Open = FALSE;
|
|
else
|
|
pFgt->f_A_Open = TRUE;
|
|
}
|
|
else
|
|
pFgt->f_F_Open = !pFgt->f_F_Open;
|
|
}
|
|
else
|
|
{
|
|
// This is the +/- case
|
|
//
|
|
// Contraction works a little differently than expansion.
|
|
// For contraction, if the selected node is not expanded, we close
|
|
// it's parent. If it is expanded, we close it.
|
|
//
|
|
if ( !sel ) // if we're doing contraction...
|
|
{
|
|
if ( ! (which ? pFgt->f_A_Open : pFgt->f_F_Open) ) // if node closed...
|
|
{
|
|
if ( pFgt->pParent )
|
|
pFgt = pFgt->pParent;
|
|
}
|
|
}
|
|
|
|
if (! pFgt->pKid && pFgt->pParent)
|
|
pFgt = pFgt->pParent;
|
|
|
|
if ( which )
|
|
pFgt->f_A_Open = sel;
|
|
else
|
|
pFgt->f_F_Open = sel;
|
|
}
|
|
return (WORD)FillFilterTree(hwndLB, which, sel);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FillFilterTree()
|
|
*
|
|
* Function actually makes the send message calls to cause a repaint of
|
|
* the listbox identified by hwndLB.
|
|
*
|
|
* ENTRY:
|
|
* hwndLB - Identifies the listbox were working with (it's handle)
|
|
* fAvailable - Which ListBox are we working with: TRUE == Available LB.
|
|
* FALSE == Range LB.
|
|
* fScrollup - If TRUE and a parents children will appear off the bottom
|
|
* of the LB we'll scroll the parent to the top of the LB.
|
|
*
|
|
* EXIT:
|
|
* UINT - Returns the number of items placed in the LB.
|
|
*
|
|
*****************************************************************************/
|
|
UINT CDefineSS::FillFilterTree(HWND hwndLB, BOOL fAvailable, BOOL fScrollup)
|
|
{
|
|
UINT uiRet = 0;
|
|
int n, nVis, top, caret;
|
|
CFolder* pFgtTop, *pFgtCaret, *pFgt, *plFgt;
|
|
RECT rc;
|
|
|
|
/*
|
|
* Compute the number of visible lines, get the top and caret indicies.
|
|
*/
|
|
GetClientRect(hwndLB,&rc);
|
|
n = (int)SendMessage(hwndLB,LB_GETITEMHEIGHT,0,0L);
|
|
nVis = rc.bottom / n;
|
|
SendMessage(hwndLB,WM_SETREDRAW,FALSE,0L); // Don't re-paint!
|
|
|
|
caret = (int)SendMessage(hwndLB,LB_GETCARETINDEX,0,0L);
|
|
top = (int)SendMessage(hwndLB,LB_GETTOPINDEX,0,0L);
|
|
/*
|
|
* Get the items living at the top and the caret position of the listbox.
|
|
*/
|
|
if (top == (int)LB_ERR)
|
|
pFgtTop = NULL;
|
|
else
|
|
{
|
|
if ( (pFgtTop=(CFolder*)SendMessage(hwndLB, LB_GETITEMDATA, top, (LPARAM)0)) == (CFolder*)LB_ERR )
|
|
pFgtTop = NULL;
|
|
}
|
|
|
|
if (caret == (int)LB_ERR)
|
|
pFgtCaret = NULL;
|
|
else
|
|
{
|
|
if ( (pFgtCaret=(CFolder*)SendMessage(hwndLB, LB_GETITEMDATA, caret, (LPARAM)0)) == (CFolder*)LB_ERR )
|
|
pFgtCaret = NULL;
|
|
}
|
|
/*
|
|
* If the top entry is a child and it's not expanded, set it's parent
|
|
* as the top. ie it's been collapsed.
|
|
*/
|
|
if ( pFgtTop && pFgtTop->pParent && !EXPANDED(fAvailable, pFgtTop->pParent) )
|
|
pFgtTop = pFgtTop->pParent;
|
|
/*
|
|
* If the caret position is on a child and it's not expanded, set it's
|
|
* parent as the caret position. ie it's been collapsed.
|
|
*/
|
|
if ( pFgtCaret && pFgtCaret->pParent && !EXPANDED(fAvailable, pFgtCaret->pParent) )
|
|
pFgtCaret = pFgtCaret->pParent;
|
|
/*
|
|
* Insure we have not set the top or caret position to a non selected
|
|
* node.
|
|
*/
|
|
if ( pFgtTop && !PRESENT(fAvailable,pFgtTop) )
|
|
{
|
|
plFgt = pFgtTop;
|
|
do
|
|
{
|
|
do
|
|
{
|
|
pFgtTop = pFgtTop->pNext;
|
|
} while ( pFgtTop && !PRESENT(fAvailable,pFgtTop) );
|
|
|
|
if (! pFgtTop )
|
|
{
|
|
pFgtTop = plFgt->pParent;
|
|
plFgt = pFgtTop;
|
|
}
|
|
else
|
|
break;
|
|
} while ( pFgtTop );
|
|
}
|
|
if ( pFgtCaret && !PRESENT(fAvailable,pFgtCaret) )
|
|
{
|
|
plFgt = pFgtCaret;
|
|
do
|
|
{
|
|
do
|
|
{
|
|
pFgtCaret = pFgtCaret->pNext;
|
|
} while ( pFgtCaret && !PRESENT(fAvailable,pFgtCaret) );
|
|
|
|
if (! pFgtCaret )
|
|
{
|
|
pFgtCaret = plFgt->pParent;
|
|
plFgt = pFgtCaret;
|
|
}
|
|
else
|
|
break;
|
|
} while ( pFgtCaret );
|
|
}
|
|
SendMessage(hwndLB,LB_RESETCONTENT,FALSE,0L);
|
|
/*
|
|
* Reset horizontal extent.
|
|
*/
|
|
SendMessage(hwndLB, LB_SETHORIZONTALEXTENT, 0, 0L);
|
|
/*
|
|
* Now, add the appropiate items to the list box.
|
|
*/
|
|
if ( fAvailable )
|
|
uiRet = AvailableWalk(hwndLB);
|
|
else
|
|
uiRet = FilteredWalk(hwndLB);
|
|
/*
|
|
* Lastly, insure a parent is scrolled to the top of the LB if it's kids
|
|
* have run off of the bottom of the LB.
|
|
*/
|
|
if (pFgtTop)
|
|
top = (int)SendMessage(hwndLB,LB_FINDSTRINGEXACT,0,(LPARAM)pFgtTop);
|
|
|
|
if (pFgtCaret)
|
|
caret = (int)SendMessage(hwndLB,LB_FINDSTRINGEXACT,0,(LPARAM)pFgtCaret);
|
|
|
|
if (!pFgtTop || top == (int)LB_ERR)
|
|
top = 0;
|
|
|
|
if (!pFgtCaret || caret == (int)LB_ERR)
|
|
caret = 0;
|
|
|
|
if ( fScrollup && pFgtCaret && pFgtCaret->pKid )
|
|
{
|
|
if (caret < top)
|
|
top = caret;
|
|
|
|
for (pFgt = pFgtCaret->pKid, n = 1; pFgt; pFgt = pFgt->pNext)
|
|
++n;
|
|
|
|
if (n >= nVis)
|
|
top = caret;
|
|
else if (caret + n > top + nVis)
|
|
top = caret - nVis + n;
|
|
}
|
|
SendMessage(hwndLB, LB_SETTOPINDEX, top, 0L);
|
|
SendMessage(hwndLB, LB_SETCARETINDEX, caret, 0L);
|
|
SendMessage(hwndLB, LB_SETSEL, TRUE, MAKELPARAM(caret,0));
|
|
|
|
SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
|
|
InvalidateRect(hwndLB, NULL, TRUE);
|
|
return(uiRet);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FilteredWalk()
|
|
*
|
|
* Helper function called only from FillFilterTree(). This bit of code walks
|
|
* the filter group tree and adds the selected nodes to the listbox given.
|
|
*
|
|
* ENTRY:
|
|
* hwndLB - Handle to the list box to which items are to be added.
|
|
*
|
|
* EXIT:
|
|
* uint - Returns the number of items added to the listbox.
|
|
*
|
|
****************************************************************************/
|
|
UINT CDefineSS::FilteredWalk(HWND hwndLB)
|
|
{
|
|
CFolder* pFgt;
|
|
CFolder* pFgtLast;
|
|
UINT i = 0;
|
|
|
|
pFgt = m_pCollection->m_Collection.GetVisableRootFolder();
|
|
/*
|
|
* Ok, now we actually do all the walking of the filter linked list to
|
|
* re-generate the filter listbox contnets. This must be done in the
|
|
* order in which the items appear in the LB. ie. Depthwise.
|
|
*/
|
|
while ( pFgt )
|
|
{
|
|
do
|
|
{
|
|
if ( pFgt->f_Filter && !pFgt->f_IsOrphan )
|
|
{
|
|
SendMessage(hwndLB, LB_ADDSTRING, 0, (LPARAM)pFgt);
|
|
i++;
|
|
}
|
|
pFgtLast = pFgt;
|
|
if ( pFgt->f_F_Open )
|
|
pFgt = pFgt->pKid;
|
|
else
|
|
break;
|
|
} while ( pFgt );
|
|
pFgt = pFgtLast;
|
|
while ( pFgt && (! (pFgt->pNext)) )
|
|
pFgt = pFgt->pParent;
|
|
if ( pFgt )
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
return(i);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* AvailableWalk()
|
|
*
|
|
* Helper function called only from FillFilterTree(). This bit of code walks
|
|
* the filter group tree and adds the selected nodes to the listbox given.
|
|
*
|
|
* ENTRY:
|
|
* hwndLB - Handle to the list box to which items are to be added.
|
|
*
|
|
* EXIT:
|
|
* uint - Returns the number of items added to the listbox.
|
|
*
|
|
****************************************************************************/
|
|
UINT CDefineSS::AvailableWalk(HWND hwndLB)
|
|
{
|
|
CFolder* pFgt;
|
|
CFolder* pFgtLast;
|
|
UINT i = 0;
|
|
|
|
pFgt = m_pCollection->m_Collection.GetVisableRootFolder();
|
|
/*
|
|
* Ok, now we actually do all the walking of the filter linked list to
|
|
* re-generate the filter listbox contnets. This must be done in the
|
|
* order in which the items appear in the LB. ie. Depthwise.
|
|
*/
|
|
while ( pFgt )
|
|
{
|
|
do
|
|
{
|
|
if ( pFgt->f_Available && !pFgt->f_IsOrphan )
|
|
{
|
|
SendMessage(hwndLB, LB_ADDSTRING, 0, (LPARAM)pFgt);
|
|
i++;
|
|
}
|
|
pFgtLast = pFgt;
|
|
if ( pFgt->f_A_Open )
|
|
pFgt = pFgt->pKid;
|
|
else
|
|
break;
|
|
} while ( pFgt );
|
|
pFgt = pFgtLast;
|
|
while ( pFgt && (! (pFgt->pNext)) )
|
|
pFgt = pFgt->pParent;
|
|
if ( pFgt )
|
|
pFgt = pFgt->pNext;
|
|
}
|
|
return(i);
|
|
}
|
|
|
|
//
|
|
// Change a dialog's default push button
|
|
//
|
|
BOOL CDefineSS::SetDefaultButton( HWND hDlg, int iID, BOOL fFocus )
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
|
|
if( hDlg ) {
|
|
|
|
// Get the current default push button and reset it.
|
|
ResetDefaultButton( hDlg );
|
|
|
|
// Update the default push button's control ID.
|
|
SendMessage( hDlg, DM_SETDEFID, (WPARAM) iID, 0L );
|
|
|
|
// Set the new style.
|
|
bReturn = (BOOL) SendDlgItemMessage( hDlg, iID, BM_SETSTYLE, (WPARAM) BS_DEFPUSHBUTTON, (LPARAM) TRUE );
|
|
|
|
// Set the focus to the new default push button.
|
|
if( fFocus )
|
|
bReturn &= (SetFocus( GetDlgItem( hDlg, iID ) ) != NULL);
|
|
|
|
}
|
|
return( bReturn );
|
|
}
|
|
|
|
//
|
|
// Clear the current default button status from the default push button
|
|
//
|
|
void CDefineSS::ResetDefaultButton( HWND hDlg )
|
|
{
|
|
DWORD dwResult;
|
|
|
|
if( hDlg ) {
|
|
|
|
// Get the current default push button.
|
|
dwResult = (DWORD) SendMessage( hDlg, DM_GETDEFID, 0, 0L );
|
|
|
|
// Reset the current default push button to a regular button.
|
|
if( HIWORD(dwResult) == DC_HASDEFID )
|
|
SendDlgItemMessage( hDlg, LOWORD(dwResult), BM_SETSTYLE, (WPARAM) BS_PUSHBUTTON, (LPARAM) TRUE );
|
|
|
|
}
|
|
|
|
return;
|
|
}
|