WindowsXP-SP1/enduser/stuff/hhctrl/subset.cpp

2212 lines
66 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;
}