#include "pch.h"
#include "uxtheme.h"
#pragma hdrstop


/*-----------------------------------------------------------------------------
/ Private data and helper functions
/----------------------------------------------------------------------------*/

//
// ICommonQuery stuff 
//

class CCommonQuery : public ICommonQuery, IObjectWithSite
{
private:
    LONG _cRef;
    IUnknown* _punkSite;

public:
    CCommonQuery();
    ~CCommonQuery();

    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

    // ICommonQuery
    STDMETHOD(OpenQueryWindow)(THIS_ HWND hwndParent, LPOPENQUERYWINDOW pOpenQueryWnd, IDataObject** ppDataObject);

    // IObjectWithSite
    STDMETHODIMP SetSite(IUnknown* punk);
    STDMETHODIMP GetSite(REFIID riid, void **ppv);
};

//
// View layout constants used by our dialogs
//

#define VIEWER_DEFAULT_CY   200

#define COMBOEX_IMAGE_CX    16
#define COMBOEX_IMAGE_CY    16

typedef struct
{
    HDSA   hdsaPages;                   // DSA containing page entries
    DWORD  dwFlags;                     // flags
    CLSID  clsidForm;                   // CLSID identifier for this form
    LPTSTR pTitle;                      // title used for drop down / title bar
    HICON  hIcon;                       // hIcon passed by caller
    INT    iImage;                      // image list index of icon
    INT    iForm;                       // visible index of form in control
    INT    iPage;                       // currently selected page on form
} QUERYFORM, * LPQUERYFORM;

typedef struct
{
    CLSID    clsidForm;                 // CLSID to associate this form with
    LPCQPAGE pPage;                 // CQPAGE structures
    LPCQPAGEPROC pPageProc;         // PageProc's used by thunking layer
    LPARAM   lParam;                    // PAGEPROC lParam
    HWND     hwndPage;                  // hWnd of page dialog // = NULL if none
} QUERYFORMPAGE, * LPQUERYFORMPAGE;

typedef struct
{
    LPCQSCOPE pScope;
    INT iImage;
} QUERYSCOPE, * LPQUERYSCOPE;

class CQueryFrame : public IQueryFrame
{
    friend INT QueryWnd_MessageProc(HWND hwnd, LPMSG pMsg);
    friend INT_PTR CALLBACK QueryWnd_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    public:
        CQueryFrame(IUnknown* punkSite, LPOPENQUERYWINDOW pOpenQueryWindow, IDataObject** ppDataObject);
        ~CQueryFrame();

        // IUnknown
        STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
        STDMETHOD_(ULONG, AddRef)();
        STDMETHOD_(ULONG, Release)();

        // Internal helper functions
        STDMETHOD(DoModal)(HWND hwndParent);

        // IQueryFrame
        STDMETHOD(AddScope)(THIS_ LPCQSCOPE pScope, INT i, BOOL fSelect);
        STDMETHOD(GetWindow)(THIS_ HWND* phWnd);
        STDMETHOD(InsertMenus)(THIS_ HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidth);
        STDMETHOD(RemoveMenus)(THIS_ HMENU hmenuShared);
        STDMETHOD(SetMenu)(THIS_ HMENU hmenuShared, HOLEMENU holereservedMenu);
        STDMETHOD(SetStatusText)(THIS_ LPCTSTR pszStatusText);
        STDMETHOD(StartQuery)(THIS_ BOOL fStarting);
        STDMETHOD(LoadQuery)(THIS_ IPersistQuery* pPersistQuery);
        STDMETHOD(SaveQuery)(THIS_ IPersistQuery* pPersistQuery);
        STDMETHOD(CallForm)(THIS_ LPCLSID pclsidForm, UINT uMsg, WPARAM wParam, LPARAM lParam);
        STDMETHOD(GetScope)(THIS_ LPCQSCOPE* ppScope);
        STDMETHOD(GetHandler)(THIS_ REFIID riid, void **ppv);

    protected:
        // Helper functions
        VOID CloseQueryFrame(HRESULT hres);
        INT FrameMessageBox(LPCTSTR pPrompt, UINT uType);

        // Message handlers
        HRESULT OnInitDialog(HWND hwnd);
        VOID DoEnableControls(VOID);
        LRESULT OnNotify(INT idCtrl, LPNMHDR pNotify);
        VOID OnSize(INT cx, INT cy);
        VOID OnGetMinMaxInfo(LPMINMAXINFO lpmmi);    
        VOID OnCommand(WPARAM wParam, LPARAM lParam);
        VOID OnInitMenu(HMENU hMenu);
        VOID OnEnterMenuLoop(BOOL fEntering);
        VOID OnMenuSelect(HMENU hMenu, UINT uID);
        HRESULT OnFindNow(VOID);
        BOOL OnNewQuery(BOOL fAlwaysPrompt);
        HRESULT OnBrowse(VOID);    
        HRESULT OnHelp(LPHELPINFO pHelpInfo);
        
        // Form/Scope helper fucntions
        HRESULT InsertScopeIntoList(LPCQSCOPE pScope, INT i, BOOL fAddToControl);
        HRESULT AddScopeToControl(LPQUERYSCOPE pQueryScope, INT i);
        HRESULT PopulateScopeControl(VOID);
        HRESULT GetSelectedScope(LPQUERYSCOPE* ppQueryScope);
        HRESULT AddFromIQueryForm(IQueryForm* pQueryForm, HKEY hkeyForm);
        HRESULT GatherForms(VOID);
        HRESULT GetForms(HKEY hKeyForms, LPTSTR pName);
        HRESULT PopulateFormControl(BOOL fIncludeHidden);
        HRESULT SelectForm(REFCLSID clsidForm);
        VOID SelectFormPage(LPQUERYFORM pQueryForm, INT iPage);
        HRESULT CallFormPages(LPQUERYFORM pQueryForm, UINT uMsg, WPARAM wParam, LPARAM lParam);
        LPQUERYFORM FindQueryForm(REFCLSID clsidForm);

    private:
        LONG _cRef;                             // reference count for the object

        IUnknown* _punkSite;                   // site object we need to pass through
        IQueryHandler* _pQueryHandler;         // IQueryHandler object we need to interact with
        LPOPENQUERYWINDOW _pOpenQueryWnd;      // copy of initial parameters provided by caller
        IDataObject** _ppDataObject;           // receives the resulting data object from handler

        DWORD      _dwHandlerViewFlags;        // flags from the handler

        BOOL       _fQueryRunning:1;           // = 1 => query has been started, via IQueryFrame::StartQuery(TRUE)
        BOOL       _fExitModalLoop:1;          // = 1 => must leave modal loop
        BOOL       _fScopesPopulated:1;        // = 1 => scope control has been populated
        BOOL       _fTrackingMenuBar:1;        // = 1 => then we are tracking the menu bar, therefore send activates etc
        BOOL       _fAddScopesNYI:1;           // = 1 => did AddScopes return E_NOTIMPL
        BOOL       _fScopesAddedAsync:1;       // = 1 => scopes added async by the handler
        BOOL       _fScopeImageListSet:1;      // = 1 => scope image list has been set
        BOOL       _fFormFirstEnable:1;        // = 1 => enabling controls for first item, so ensure we set focus

        HRESULT    _hResult;                   // result value stored by CloseQueryFrame
        HKEY       _hkHandler;                 // registry key for the handler

        HWND       _hwnd;                      // main window handle
        HWND       _hwndResults;               // result viewer
        HWND       _hwndStatus;                // status bar

        HWND       _hwndFrame;                 // Query Pages tab control
        HWND       _hwndLookForLabel;          // "Find:"
        HWND       _hwndLookFor;               // Form combo
        HWND       _hwndLookInLabel;           // "In:"
        HWND       _hwndLookIn;                // Scope combo
        HWND       _hwndBrowse;                // "Browse"
        HWND       _hwndFindNow;               // "Find now"
        HWND       _hwndStop;                  // "Stop"
        HWND       _hwndNewQuery;              // "New Query"
        HWND       _hwndOK;                    // "OK"
        HWND       _hwndCancel;                // "Cancel"
        HWND       _hwndFindAnimation;         // Query issued animation

        HICON      _hiconSmall;                // large/small app icons
        HICON      _hiconLarge;

        HMENU      _hmenuFile;                 // handle of the frames menu bar

        HIMAGELIST _himlForms;                 // image list for query form objects

        SIZE       _szMinTrack;                // minimum track size of the window

        INT        _dxFormAreaLeft;            // offset to left edge of form area (from window left)
        INT        _dxFormAreaRight;           // offset to right edge of form area (from window right)
        INT        _dxButtonsLeft;             // offset to left edge of buttons (from window right)
        INT        _dxAnimationLeft;           // offset to left edge of aniimation (from window right)
        INT        _dyResultsTop;              // offset to top of results (from top of window)
        INT        _dyOKTop;                   // offset to top of "OK" buttom (from results top)
        INT        _dxGap;                     // gap between OK + Cancel / LookIn + Browse
        INT        _dyGap;                     // gap between bottom of OK,Cancel and the frame.

        INT        _cyStatus;                  // height of the status bar

        HDSA       _hdsaForms;                 // forms DSA
        HDSA       _hdsaPages;                 // pages DSA
        SIZE       _szForm;                    // size of the (current form we are displaying)

        HDSA       _hdsaScopes;                // scopes DSA
        INT        _iDefaultScope;             // index of the defualt scope to select (into DSA)

        LPQUERYFORM _pCurrentForm;              // == NULL if none / else -> form structure
        LPQUERYFORMPAGE _pCurrentFormPage;      // == NULL if none / else -> page structure
};

//
// Helper functions
//

INT_PTR CALLBACK QueryWnd_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT QueryWnd_MessageProc(HWND hwnd, LPMSG pMsg);

HRESULT _CallScopeProc(LPQUERYSCOPE pQueryScope, UINT uMsg, LPVOID pVoid);
INT _FreeScope(LPQUERYSCOPE pQueryScope);
INT _FreeScopeCB(LPVOID pItem, LPVOID pData);

HRESULT _CallPageProc(LPQUERYFORMPAGE pQueryFormPage, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT _FreeQueryFormCB(LPVOID pItem, LPVOID pData);
INT _FreeQueryForm(LPQUERYFORM pQueryForm);
INT _FreeQueryFormPageCB(LPVOID pItem, LPVOID pData);
INT _FreeQueryFormPage(LPQUERYFORMPAGE pQueryFormPage);

HRESULT _AddFormsProc(LPARAM lParam, LPCQFORM pForm);
HRESULT _AddPagesProc(LPARAM lParam, REFCLSID clsidForm, LPCQPAGE pPage);

//
// Help stuff
//

#define HELP_FILE (NULL)

static DWORD const aHelpIDs[] =
{
    0, 0
};

//
// constant strings
//

TCHAR const c_szCLSID[]             = TEXT("CLSID");
TCHAR const c_szForms[]             = TEXT("Forms");
TCHAR const c_szFlags[]             = TEXT("Flags");

TCHAR const c_szCommonQuery[]       = TEXT("CommonQuery");
TCHAR const c_szHandlerIs[]         = TEXT("Handler");
TCHAR const c_szFormIs[]            = TEXT("Form");
TCHAR const c_szSearchPaneHidden[]  = TEXT("SearchPaneHidden");


/*-----------------------------------------------------------------------------
/ CCommonQuery
/----------------------------------------------------------------------------*/

CCommonQuery::CCommonQuery() :
    _punkSite(NULL), _cRef(1)
{
    DllAddRef();
}

CCommonQuery::~CCommonQuery()
{
    DoRelease(_punkSite);
    DllRelease();
}


// QI handling

ULONG CCommonQuery::AddRef()
{
    return InterlockedIncrement(&_cRef);
}

ULONG CCommonQuery::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;

    delete this;
    return 0;
}

HRESULT CCommonQuery::QueryInterface(REFIID riid, void **ppv)
{
    static const QITAB qit[] = 
    {
        QITABENT(CCommonQuery, ICommonQuery),       // IID_ICommonQuery
        QITABENT(CCommonQuery, IObjectWithSite),    // IID_IObjectWithSite
        {0, 0 },
    };
    return QISearch(this, qit, riid, ppv);
}

STDAPI CCommonQuery_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
    CCommonQuery *pcq = new CCommonQuery;
    if (!pcq)
        return E_OUTOFMEMORY;

    HRESULT hres = pcq->QueryInterface(IID_IUnknown, (void **)ppunk);
    pcq->Release();

    return hres;
}


// ICommonQuery methods

STDMETHODIMP CCommonQuery::OpenQueryWindow(THIS_ HWND hwndParent, LPOPENQUERYWINDOW pOpenQueryWnd, IDataObject** ppDataObject)
{
    HRESULT hres;
    CQueryFrame* pQueryFrame = NULL;

    TraceEnter(TRACE_QUERY, "CCommonQuery::OpenQueryWindow");

    if (!pOpenQueryWnd || (hwndParent && !IsWindow(hwndParent)))
        ExitGracefully(hres, E_INVALIDARG, "Bad parameters");
   
    if (ppDataObject)
        *(ppDataObject) = NULL;

    pQueryFrame = new CQueryFrame(_punkSite, pOpenQueryWnd, ppDataObject);
    TraceAssert(pQueryFrame);

    if (!pQueryFrame)
        ExitGracefully(hres, E_OUTOFMEMORY, "Failed to construct the query window object");

    hres = pQueryFrame->DoModal(hwndParent);                // don't bother fail gracefully etc
    FailGracefully(hres, "Failed on calling DoModal");

exit_gracefully:

    DoRelease(pQueryFrame);

    TraceLeaveResult(hres);
}


// IObjectWithSite

STDMETHODIMP CCommonQuery::SetSite(IUnknown* punk)
{
    HRESULT hres = S_OK;

    TraceEnter(TRACE_QUERY, "CCommonQuery::SetSite");

    DoRelease(_punkSite);

    if (punk)
    {
        TraceMsg("QIing for IUnknown from the site object");

        hres = punk->QueryInterface(IID_IUnknown, (void **)&_punkSite);
        FailGracefully(hres, "Failed to get IUnknown from the site object");
    }

exit_gracefully:

    TraceLeaveResult(hres);
}


STDMETHODIMP CCommonQuery::GetSite(REFIID riid, void **ppv)
{
    HRESULT hres;
    
    TraceEnter(TRACE_QUERY, "CCommonQuery::GetSite");

    if (!_punkSite)
        ExitGracefully(hres, E_NOINTERFACE, "No site to QI from");

    hres = _punkSite->QueryInterface(riid, ppv);
    FailGracefully(hres, "QI failed on the site unknown object");

exit_gracefully:

    TraceLeaveResult(hres);
}



// IQueryFrame stuff

CQueryFrame::CQueryFrame(IUnknown *punkSite, LPOPENQUERYWINDOW pOpenQueryWindow, IDataObject** ppDataObject) :
    _cRef(1), _punkSite(punkSite), _pOpenQueryWnd(pOpenQueryWindow),
    _ppDataObject(ppDataObject), _hiconLarge(NULL), _hiconSmall(NULL)
{
    if (_punkSite)
        _punkSite->AddRef();
    
    DllAddRef();
}

CQueryFrame::~CQueryFrame()
{
    DoRelease(_punkSite);

    if (_hiconLarge)
        DestroyIcon(_hiconLarge);

    if (_hiconSmall)
        DestroyIcon(_hiconSmall);

    if (_hkHandler)
        RegCloseKey(_hkHandler);

    if (_hmenuFile)
        DestroyMenu(_hmenuFile);

    if (_himlForms)
        ImageList_Destroy(_himlForms);

    if (_hdsaForms)
    {
        Trace(TEXT("Destroying QUERYFORM DSA (%d)"), DSA_GetItemCount(_hdsaForms));
        DSA_DestroyCallback(_hdsaForms, _FreeQueryFormCB, NULL);
        _hdsaForms = NULL;
    }

    if (_hdsaPages)
    {
        Trace(TEXT("Destroying QUERYFORMPAGE DSA (%d)"), DSA_GetItemCount(_hdsaPages));
        DSA_DestroyCallback(_hdsaPages, _FreeQueryFormPageCB, NULL);
        _hdsaPages = NULL;
    }

    if (_hdsaScopes)
    {
        Trace(TEXT("Destroying QUERYSCOPE DSA (%d)"), DSA_GetItemCount(_hdsaScopes));
        DSA_DestroyCallback(_hdsaScopes, _FreeScopeCB, NULL);
        _hdsaScopes = NULL;
    }

    _pCurrentForm = NULL;
    _pCurrentFormPage = NULL;

    // Now discard the handler and its window (if we have one), if
    // we don't do this they will never kill their objects

    if (_hwndResults)
    {
        DestroyWindow(_hwndResults);
        _hwndResults = NULL;
    }

    DllRelease();
}

// QI handling

ULONG CQueryFrame::AddRef()
{
    return InterlockedIncrement(&_cRef);
}

ULONG CQueryFrame::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;

    delete this;
    return 0;
}

HRESULT CQueryFrame::QueryInterface(REFIID riid, void **ppv)
{
    static const QITAB qit[] = 
    {
        QITABENT(CQueryFrame, IQueryFrame),    // IID_IQueryFrame
        {0, 0 },
    };
    return QISearch(this, qit, riid, ppv);
}


/*-----------------------------------------------------------------------------
/ IQueryFrame
/----------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::DoModal(HWND hwndParent)
{
    HRESULT hres;
    HWND hwndFrame = NULL;
    HWND hwndFocus = NULL;
    HWND hwndTopOwner = hwndParent;
    MSG msg;
    INITCOMMONCONTROLSEX iccex;

    TraceEnter(TRACE_FRAME, "CQueryFrame::DoModal");

    // initialize with the query handler we need

    hres = CoCreateInstance(_pOpenQueryWnd->clsidHandler, NULL, CLSCTX_INPROC_SERVER, IID_IQueryHandler, (LPVOID*)&_pQueryHandler);
    FailGracefully(hres, "Failed to get IQueryHandler for the given CLSID");

    hres = _pQueryHandler->Initialize(this, _pOpenQueryWnd->dwFlags, _pOpenQueryWnd->pHandlerParameters);
    FailGracefully(hres, "Failed to initialize the handler");

    // mimic the behaviour of DialogBox by working out which control previously
    // had focus, which window to disable and then running a message
    // pump for our dialog.  Having done this we can then restore the state
    // back to something sensible.

    _fExitModalLoop = FALSE;                   // can be changed from hear down

    iccex.dwSize = SIZEOF(iccex);
    iccex.dwICC = ICC_USEREX_CLASSES;
    InitCommonControlsEx(&iccex);

    if (_pOpenQueryWnd->dwFlags & OQWF_HIDESEARCHUI)
    {
        hwndFrame = CreateDialogParam(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDD_FILTER),
                                      hwndParent, 
                                      QueryWnd_DlgProc, (LPARAM)this);
    }
    else
    {
        hwndFrame = CreateDialogParam(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDD_FIND),
                                      hwndParent, 
                                      QueryWnd_DlgProc, (LPARAM)this);
    }

    if (!hwndFrame)
        ExitGracefully(hres, E_FAIL, "Failed to create the dialog");

    hwndFocus = GetFocus();

    if (hwndTopOwner)
    {
        // walk up the window stack looking for the window to be disabled, this must
        // be the top-most non-child window.  If the resulting window is either
        // the desktop or is already disabled then don't bother.

        while (GetWindowLong(hwndTopOwner, GWL_STYLE) & WS_CHILD)
            hwndTopOwner = GetParent(hwndTopOwner);

        TraceAssert(hwndTopOwner);

        if ((hwndTopOwner == GetDesktopWindow()) 
                                || EnableWindow(hwndTopOwner, FALSE))
        { 
            TraceMsg("Parent is disabled or the desktop window, therefore setting to NULL");
            hwndTopOwner = NULL;
        }
    }

    ShowWindow(hwndFrame, SW_SHOW);                     // show the query window
    
    while (!_fExitModalLoop && GetMessage(&msg, NULL, 0, 0) > 0) 
    {
        if (!QueryWnd_MessageProc(hwndFrame, &msg) && !IsDialogMessage(hwndFrame, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    // Now tidy up, make the parent the active window, enable the top most
    // window if there is one and restore focus as required.

    if (hwndTopOwner)
        EnableWindow(hwndTopOwner, TRUE);

    if (hwndParent && (GetActiveWindow() == hwndFrame))
    {
        TraceMsg("Passing activation to parent");
        SetActiveWindow(hwndParent);
    }
    
    if (IsWindow(hwndFocus))
        SetFocus(hwndFocus);

    DestroyWindow(hwndFrame);                   // discard the current frame window

exit_gracefully:

    DoRelease(_pQueryHandler);

    TraceLeaveResult(_hResult);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::AddScope(THIS_ LPCQSCOPE pScope, INT i, BOOL fSelect)
{
    HRESULT hres;

    TraceEnter(TRACE_FRAME, "CQueryFrame::AddScope");

    if (!pScope)
        ExitGracefully(hres, E_INVALIDARG, "No scope to add to the list");

    // Add the scope to the control and then ensure that we either have
    // its index stored (for default selection) or we select the 
    // item.

    if (!_hdsaScopes || !DSA_GetItemCount(_hdsaScopes))
    {
        TraceMsg("First scope being added, thefore selecting");
        fSelect = TRUE;
    }

    hres = InsertScopeIntoList(pScope, i, _fScopesPopulated);
    FailGracefully(hres, "Failed to add scope to control");

    if (fSelect) 
    {
        if (!_fScopesPopulated)
        {
            Trace(TEXT("Storing default scope index %d"), ShortFromResult(hres));
            _iDefaultScope = ShortFromResult(hres);
        }
        else
        {
            Trace(TEXT("Selecting scope index %d"), ShortFromResult(hres));
            ComboBox_SetCurSel(_hwndLookIn, ShortFromResult(hres));
        }   
    }

    // hres = S_OK;

exit_gracefully:

    TraceLeaveResult(hres);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::GetWindow(THIS_ HWND* phWnd)
{
    TraceEnter(TRACE_FRAME, "CQueryFrame::GetWindow");

    TraceAssert(phWnd);
    *phWnd = _hwnd;

    TraceLeaveResult(S_OK);
}

/*---------------------------------------------------------------------------*/

// Add a menu group to the given menu bar, updating the width index accordingly
// so that other people can merge in accordingly

VOID _DoInsertMenu(HMENU hMenu, INT iIndexTo, HMENU hMenuToInsert, INT iIndexFrom)
{
    TCHAR szBuffer[MAX_PATH];
    HMENU hPopupMenu = NULL;

    TraceEnter(TRACE_FRAME, "_DoInsertMenu");
    
    hPopupMenu = CreatePopupMenu();
    
    if (hPopupMenu)
    {
        Shell_MergeMenus(hPopupMenu, GetSubMenu(hMenuToInsert, iIndexFrom), 0x0, 0x0, 0x7fff, 0);

        GetMenuString(hMenuToInsert, iIndexFrom, szBuffer, ARRAYSIZE(szBuffer), MF_BYPOSITION);
        InsertMenu(hMenu, iIndexTo, MF_BYPOSITION|MF_POPUP, (UINT_PTR)hPopupMenu, szBuffer);
    }

    TraceLeave();
}

VOID _AddMenuGroup(HMENU hMenuShared, HMENU hMenuGroup, LONG iInsertAt, LPLONG pWidth)
{
    HRESULT hres;
    TCHAR szBuffer[MAX_PATH];
    HMENU hMenu;
    INT i;

    TraceEnter(TRACE_FRAME, "_AddMenuGroup");

    TraceAssert(hMenuShared);
    TraceAssert(hMenuGroup);
    TraceAssert(pWidth);

    for (i = 0 ; i < GetMenuItemCount(hMenuGroup) ; i++)
    {
        _DoInsertMenu(hMenuShared, iInsertAt+i, hMenuGroup, i);
        *pWidth += 1;
    }

    TraceLeave();
}

STDMETHODIMP CQueryFrame::InsertMenus(THIS_ HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidth)
{
    HRESULT hres;

    TraceEnter(TRACE_FRAME, "CQueryFrame::InsertMenus");

    if (!hmenuShared || !lpMenuWidth)
        ExitGracefully(hres, E_INVALIDARG, "Unable to insert menus");

    // if we don't have the menu bar already loaded then lets load it,
    // having done that we can then add our menu to the bar (we only
    // provide entries for the file menu).
    
    if (!_hmenuFile)
    {
        _hmenuFile = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_FILEMENUGROUP));

        if (!_hmenuFile)
            ExitGracefully(hres, E_FAIL, "Failed to load base menu defn");
    }

    _AddMenuGroup(hmenuShared, _hmenuFile, 0, &lpMenuWidth->width[0]);

    hres = S_OK;              // success

exit_gracefully:

    TraceLeaveResult(hres);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::RemoveMenus(THIS_ HMENU hmenuShared)
{
    TraceEnter(TRACE_FRAME, "CQueryFrame::RemoveMenus");

    // We don't need to implement this as we copy or menus into the
    // menu that the handler supplies - fix DSQUERY if this ever
    // changes.

    TraceLeaveResult(S_OK);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::SetMenu(THIS_ HMENU hmenuShared, HOLEMENU holereservedMenu)
{
    TraceEnter(TRACE_FRAME, "CQueryFrame::SetMenu");

    if (!(_pOpenQueryWnd->dwFlags & OQWF_HIDEMENUS))
    {
        HMENU hmenuOld = ::GetMenu(_hwnd);

        if (!hmenuShared)
            hmenuShared = _hmenuFile;

        ::SetMenu(_hwnd, hmenuShared);
        DoEnableControls();             // ensure the menu state is valid    
        ::DrawMenuBar(_hwnd);

        if (hmenuOld && (hmenuOld != _hmenuFile) && (hmenuOld != hmenuShared))
        {
            TraceMsg("Destroying old menu");
            DestroyMenu(hmenuOld);
        }
    }

    TraceLeaveResult(S_OK);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::SetStatusText(THIS_ LPCTSTR pszStatusText)
{
    TraceEnter(TRACE_FRAME, "CQueryFrame::SetStatusText");
    Trace(TEXT("Setting status text to: %s"), pszStatusText);

    if (_hwndStatus)
        SendMessage(_hwndStatus, SB_SETTEXT, 0, (LPARAM)pszStatusText); 

    TraceLeaveResult(S_OK);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::StartQuery(THIS_ BOOL fStarting)
{
    TraceEnter(TRACE_FRAME, "CQueryFrame::StartQuery");

    if (fStarting)
    {
        Animate_Play(_hwndFindAnimation, 0, -1, -1);
    }
    else
    {
        Animate_Stop(_hwndFindAnimation);
        Animate_Seek(_hwndFindAnimation, 0);        // go to start
    }

    if (_pQueryHandler)
        _pQueryHandler->ActivateView(CQRVA_STARTQUERY, (WPARAM)fStarting, 0);
    
    // now set the controls into a sensble state

    _fQueryRunning = fStarting;
    DoEnableControls();

    TraceLeaveResult(S_OK);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::LoadQuery(THIS_ IPersistQuery* pPersistQuery)
{
    HRESULT hres;
    TCHAR szGUID[GUIDSTR_MAX+1];
    LPQUERYFORM pQueryForm = NULL;
    GUID guid;

    TraceEnter(TRACE_FRAME, "CQueryFrame::LoadQuery");

    _pQueryHandler->StopQuery();                       // ensure that the handler stops its processing

    // Attempt to read the handler GUID from the query stream, first try reading it as
    // as string then parsing it into something that we can use, if that fails then
    // try again, but this time read it as a structure.
    //
    // having aquired the GUID for the handler make sure that we have the correct handler
    // selected.

    if (FAILED(pPersistQuery->ReadString(c_szCommonQuery, c_szHandlerIs, szGUID, ARRAYSIZE(szGUID))) ||
         !GetGUIDFromString(szGUID, &guid))
    {
        TraceMsg("Trying new style handler GUID as struct");

        hres = pPersistQuery->ReadStruct(c_szCommonQuery, c_szHandlerIs, &guid, SIZEOF(guid));
        FailGracefully(hres, "Failed to read handler GUID as struct");
    }    

    if (guid != _pOpenQueryWnd->clsidHandler)
        ExitGracefully(hres, E_FAIL, "Persisted handler GUID and specified handler GUID don't match");

    hres = _pQueryHandler->LoadQuery(pPersistQuery);
    FailGracefully(hres, "Handler failed to load its query data");

    // Get the form ID, then look up the form to see if we have one that matches,
    // if not then we cannot load any thing else. If we do haved that form then
    // ensure that we clear it and then load away.

    if (FAILED(pPersistQuery->ReadString(c_szCommonQuery, c_szFormIs, szGUID, ARRAYSIZE(szGUID))) ||
         !GetGUIDFromString(szGUID, &guid))
    {
        TraceMsg("Trying new style form GUID as struct");

        hres = pPersistQuery->ReadStruct(c_szCommonQuery, c_szFormIs, &guid, SIZEOF(guid));
        FailGracefully(hres, "Failed to read handler GUID as struct");
    }    

    hres = SelectForm(guid);
    FailGracefully(hres, "Failed to select the query form");

    if (hres == S_FALSE)
        ExitGracefully(hres, E_FAIL, "Failed to select the query form to read the query info");
    
    hres = CallFormPages(_pCurrentForm, CQPM_CLEARFORM, 0, 0);
    FailGracefully(hres, "Failed to clear form before loading");

    // Load the persisted query from the stream, coping correctly with the 
    // UNICODE / ANSI issue.  We will be passed an IPersistQuery object which
    // we must then thunk accordingly if we are UNICODE for the pages we
    // are going to talk to.

    hres = CallFormPages(_pCurrentForm, CQPM_PERSIST, TRUE, (LPARAM)pPersistQuery);
    FailGracefully(hres, "Failed to load page data (UNICODE)");

    hres = S_OK;          //  success

exit_gracefully:

    if (SUCCEEDED(hres))
    {
        TraceMsg("Query loaded successfully, select form query");
        SelectForm(guid);
    }

    TraceLeaveResult(hres);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::SaveQuery(THIS_ IPersistQuery* pPersistQuery)
{
    HRESULT hres;
    LPQUERYSCOPE pQueryScope;
    TCHAR szBuffer[MAX_PATH];
    
    TraceEnter(TRACE_FRAME, "CQueryFrame::SaveQuery");

    if (!pPersistQuery)
        ExitGracefully(hres, E_INVALIDARG, "No pPersistQuery object to write into");

    pPersistQuery->Clear();             // flush the contents

    hres = pPersistQuery->WriteStruct(c_szCommonQuery, c_szHandlerIs, 
                                                        &_pOpenQueryWnd->clsidHandler, 
                                                        SIZEOF(_pOpenQueryWnd->clsidHandler));
    FailGracefully(hres, "Failed to write handler GUID");

    hres = pPersistQuery->WriteStruct(c_szCommonQuery, c_szFormIs, 
                                                        &_pCurrentForm->clsidForm, 
                                                        SIZEOF(_pCurrentForm->clsidForm));
    FailGracefully(hres, "Failed to write form GUID");

    // Allow the handler to persist itself into the the stream, this includes
    // giving it the current scope to store.

    hres = GetSelectedScope(&pQueryScope);
    FailGracefully(hres, "Failed to get the scope from the LookIn control");

    hres = _pQueryHandler->SaveQuery(pPersistQuery, pQueryScope->pScope);
    FailGracefully(hres, "Failed when calling handler to persist itself");

    // Save the query into the stream, coping correctly with the 
    // UNICODE / ANSI issue.  We will be passed an IPersistQuery object which
    // we must then thunk accordingly if we are UNICODE for the pages we
    // are going to talk to.

    hres = CallFormPages(_pCurrentForm, CQPM_PERSIST, FALSE, (LPARAM)pPersistQuery);
    FailGracefully(hres, "Failed to load page data (UNICODE)");

    hres = S_OK;

exit_gracefully:

    TraceLeaveResult(hres);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::CallForm(THIS_ LPCLSID pclsidForm, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HRESULT hres;
    LPQUERYFORM pQueryForm = _pCurrentForm;

    TraceEnter(TRACE_FRAME, "CQueryFrame::CallForm");
    
    if (pclsidForm)
    {
        pQueryForm = FindQueryForm(*pclsidForm);
        TraceAssert(pQueryForm);
    }

    if (!pQueryForm)
        ExitGracefully(hres, E_FAIL, "Failed to find query form for given CLSID");

    hres = CallFormPages(pQueryForm, uMsg, wParam, lParam);
    FailGracefully(hres, "Failed when calling CallFormPages");

    // hres = S_OK;

exit_gracefully:

    TraceLeaveResult(hres);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::GetScope(THIS_ LPCQSCOPE* ppScope)
{
    HRESULT hres;
    LPQUERYSCOPE pQueryScope;

    TraceEnter(TRACE_FRAME, "CQueryFrame::GetScope");

    if (!ppScope)
        ExitGracefully(hres, E_INVALIDARG, "ppScope == NULL, thats bad");

    hres = GetSelectedScope(&pQueryScope);
    FailGracefully(hres, "Failed to get the current scope");

    *ppScope = (LPCQSCOPE)CoTaskMemAlloc(pQueryScope->pScope->cbStruct);
    TraceAssert(*ppScope);

    if (!*ppScope)
        ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate the scope block");
                        
    memcpy(*ppScope, pQueryScope->pScope, pQueryScope->pScope->cbStruct);

    hres = S_OK;

exit_gracefully:

    TraceLeaveResult(hres);
}

/*---------------------------------------------------------------------------*/

STDMETHODIMP CQueryFrame::GetHandler(THIS_ REFIID riid, void **ppv)
{
    HRESULT hres;

    TraceEnter(TRACE_FRAME, "CQueryFrame::GetHandler");

    if (!_pQueryHandler)
        ExitGracefully(hres, E_UNEXPECTED, "_pQueryHandler is NULL");

    hres = _pQueryHandler->QueryInterface(riid, ppv);

exit_gracefully:

    TraceLeaveResult(hres);
}

/*-----------------------------------------------------------------------------
/ Dialog box handler functions (core guts)
/----------------------------------------------------------------------------*/

#define REAL_WINDOW(hwnd)                   \
        (hwnd &&                            \
            IsWindowVisible(hwnd) &&        \
                IsWindowEnabled(hwnd) &&    \
                    (GetWindowLong(hwnd, GWL_STYLE) & WS_TABSTOP))

HWND _NextTabStop(HWND hwndSearch, BOOL fShift)
{
    HWND hwnd;

    Trace(TEXT("hwndSearch %08x, fShift %d"), hwndSearch, fShift);

    // do we have a window to search into?
    
    while (hwndSearch)
    {
        // if we have a window then lets check to see if it has any children?

        hwnd = GetWindow(hwndSearch, GW_CHILD);
        Trace(TEXT("Child of %08x is %08x"), hwndSearch, hwnd);

        if (hwnd)
        {
            // it has a child therefore lets to go its first/last
            // and continue the search there for a window that
            // matches the criteria we are looking for.

            hwnd = GetWindow(hwnd, fShift ? GW_HWNDLAST:GW_HWNDFIRST);

            if (!REAL_WINDOW(hwnd))
            {
                Trace(TEXT("Trying to recurse into %08x"), hwnd);
                hwnd = _NextTabStop(hwnd, fShift);
            }

            Trace(TEXT("Tabstop child of %08x is %08x"), hwndSearch, hwnd);
        }

        // after all that is hwnd a valid window?  if so then pass
        // that back out to the caller.

        if (REAL_WINDOW(hwnd))
        {
            Trace(TEXT("Child tab stop was %08x"), hwnd);
            return hwnd;
        }

        // do we have a sibling?  if so then lets return that otherwise
        // lets just continue to search until we either run out of windows
        // or hit something interesting

        hwndSearch = GetWindow(hwndSearch, fShift ? GW_HWNDPREV:GW_HWNDNEXT);

        if (REAL_WINDOW(hwndSearch))
        {
            Trace(TEXT("Next tab stop was %08x"), hwndSearch);
            return hwndSearch;
        }
    }

    return hwndSearch;
}

INT QueryWnd_MessageProc(HWND hwnd, LPMSG pMsg)
{
    LRESULT lResult = 0;
    CQueryFrame* pQueryFrame = NULL;
    NMHDR nmhdr;

    pQueryFrame = (CQueryFrame*)GetWindowLongPtr(hwnd, DWLP_USER);

    if (!pQueryFrame)
        return 0;

    if ((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB))
    {
        BOOL fCtrl = GetAsyncKeyState(VK_CONTROL) < 0;
        BOOL fShift = GetAsyncKeyState(VK_SHIFT) < 0;

        // ensure that the focus rectangles are shown

#if (_WIN32_WINNT >= 0x0500)
        SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
#endif

        if (fCtrl)
        {
            // if this is a key press within the parent then lets ensure that we
            // allow the tab control to change the page correctly.  otherwise lets
            // just hack around the problem of the result view not handling tabs
            // properly.

            INT iCur = TabCtrl_GetCurSel(pQueryFrame->_hwndFrame);
            INT nPages = TabCtrl_GetItemCount(pQueryFrame->_hwndFrame);

            if (fShift)
                iCur += (nPages-1);
            else
                iCur++;

            pQueryFrame->SelectFormPage(pQueryFrame->_pCurrentForm, iCur % nPages);

            return 1;                   // we processed it
        }
        else
        {
            // is the window that has the focus a child of the result view, if
            // so then we must attempt to pass focus to its 1st child and hope
            // that is can do the rest.

            HWND hwndNext, hwnd = GetFocus();
            Trace(TEXT("Current focus window %08x"), hwnd);
           
            while (hwnd && GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
            {      
                hwndNext = _NextTabStop(hwnd, fShift);
                Trace(TEXT("_NextTabStop yeilds %08x from %08x"), hwndNext, hwnd);
        
                if (hwndNext)
                {
                    Trace(TEXT("SetFocus on child %08x"), hwndNext);
                    SetFocus(hwndNext);
                    return 1;
                }

                while (TRUE)
                {
                    // look up the parent list trying to find a window that we can 
                    // tab back into.  We must watch that when we walk out of the
                    // child list we loop correctly at the top of the list.

                    hwndNext = GetParent(hwnd);
                    Trace(TEXT("Parent hwnd %08x"), hwndNext);

                    if (GetWindowLong(hwndNext, GWL_STYLE) & WS_CHILD)
                    {
                        // the parent window is a child, therefore we can check
                        // to see if has any siblings.
                        
                        Trace(TEXT("hwndNext is a child, therefore hwndNext of it is %08x"), 
                                                        GetWindow(hwndNext, fShift ? GW_HWNDPREV:GW_HWNDNEXT));
                                                                                
                        if (GetWindow(hwndNext, fShift ? GW_HWNDPREV:GW_HWNDNEXT))
                        {
                            hwnd = GetWindow(hwndNext, fShift ? GW_HWNDPREV:GW_HWNDNEXT);
                            Trace(TEXT("Silbing window found %08x"), hwnd);
                            break;
                        }
                        else
                        {
                            TraceMsg("There was no sibling, therefore continuing parent loop");
                            hwnd = hwndNext;
                        }
                    }
                    else
                    {
                        // we have hit the parent window of it all (the overlapped one)
                        // therefore we must attempt to go to its first child.  Walk forward
                        // in the stack looking for a window that matches the
                        // "REAL_WINDOW" conditions.

                        hwnd = GetWindow(hwnd, fShift ? GW_HWNDLAST:GW_HWNDFIRST);
                        Trace(TEXT("First child is %08x"), hwnd);
                        break;                                  // continue the sibling search etc
                    }
                }

                if (REAL_WINDOW(hwnd))
                {
                    SetFocus(hwnd);
                    return 1;
                }
            }
        }
    }

    return 0;
}

//
// Main DLGPROC
//

INT_PTR CALLBACK QueryWnd_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CQueryFrame* pQueryFrame;

    if (uMsg == WM_INITDIALOG)
    {
        HRESULT hres;
        pQueryFrame = (CQueryFrame*)lParam;
        SetWindowLongPtr(hwnd, DWLP_USER, (LRESULT)pQueryFrame);

        hres = pQueryFrame->OnInitDialog(hwnd);
        Trace(TEXT("OnInitDialog returns %08x"), hres);
        
        if (FAILED(hres))
        {
            TraceMsg("Failed to initialize the dialog, Destroying the window");    
            pQueryFrame->CloseQueryFrame(hres);
            DestroyWindow(hwnd);
        }
    }
    else
    {
        pQueryFrame = (CQueryFrame*)GetWindowLongPtr(hwnd, DWLP_USER);

        if (!pQueryFrame)
            goto exit_gracefully;

        switch (uMsg)
        {
            case WM_ERASEBKGND:
            {
                HDC hdc = (HDC)wParam;
                RECT rc;

                // if we have a DC then lets fill it, and if we have a 
                // query form then lets paint the divider between the menu bar and
                // this area.

                if (hdc)
                {
                    GetClientRect(hwnd, &rc);
                    FillRect(hdc, &rc, (HBRUSH)(COLOR_3DFACE+1));

                    if (!(pQueryFrame->_pOpenQueryWnd->dwFlags & OQWF_HIDEMENUS))
                        DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOP);

                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, 1L);
                }
                
                return 1;
            }

            case WM_NOTIFY:
                return pQueryFrame->OnNotify((int)wParam, (LPNMHDR)lParam);

            case WM_SIZE:
                pQueryFrame->OnSize(LOWORD(lParam), HIWORD(lParam));
                return(1);

            case WM_GETMINMAXINFO:
                pQueryFrame->OnGetMinMaxInfo((LPMINMAXINFO)lParam);
                return(1);

            case WM_COMMAND:
                pQueryFrame->OnCommand(wParam, lParam);
                return(1);

            case WM_ACTIVATE:
                pQueryFrame->_pQueryHandler->ActivateView(wParam ? CQRVA_ACTIVATE : CQRVA_DEACTIVATE, 0, 0);
                return(1);
            
            case WM_INITMENU:
                pQueryFrame->OnInitMenu((HMENU)wParam);
                return(1);

            case WM_SETCURSOR:
            {
                // do we have any scopes? if not then let us display the wait
                // cursor for the user.  if we have a query running then lets
                // display the app start cursor.

                if (!pQueryFrame->_fAddScopesNYI &&
                            !ComboBox_GetCount(pQueryFrame->_hwndLookIn))
                {
                    if (LOWORD(lParam) == HTCLIENT)
                    {
                        SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT)));
                        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, 1L);
                        return 1;
                    }
                }

                break;
            }

            case WM_INITMENUPOPUP:
            {
                // only send sub-menu activates if the menu bar is being tracked, this is
                // handled within OnInitMenu, if we are not tracking the menu then we
                // assume that the client has already primed the menu and that they are
                // using some kind of popup menu.

                if (pQueryFrame->_fTrackingMenuBar)
                    pQueryFrame->_pQueryHandler->ActivateView(CQRVA_INITMENUBARPOPUP, wParam, lParam);

                return(1);
            }
            
            case WM_ENTERMENULOOP:
                pQueryFrame->OnEnterMenuLoop(TRUE);
                return(1);

            case WM_EXITMENULOOP:
                pQueryFrame->OnEnterMenuLoop(FALSE);
                return(1);

            case WM_MENUSELECT:
            {
                UINT uID = LOWORD(wParam);
                UINT uFlags = HIWORD(wParam);
                HMENU hMenu = (HMENU)lParam;
                
                // the command opens a popup menu the the uID is actually
                // the index into the menu, so lets ensure that we pick
                // up the correct ID by calling GetMenuItemInfo, note that
                // GetMenuItemID returns -1 in this case which is totally
                // useless.

                if (uFlags & MF_POPUP)    
                {
                    MENUITEMINFO mii;

                    ZeroMemory(&mii, SIZEOF(mii));
                    mii.cbSize = SIZEOF(mii);
                    mii.fMask = MIIM_ID;

                    if (GetMenuItemInfo(hMenu, uID, TRUE, &mii))
                        uID = mii.wID;
                }

                pQueryFrame->OnMenuSelect(hMenu, uID);
                return(1);
            }

            case WM_SYSCOMMAND:
                if (wParam == SC_CLOSE)
                {
                    pQueryFrame->CloseQueryFrame(S_FALSE);
                    return(1);
                }
                break;

            case WM_CONTEXTMENU:
            {
                // there are a couple of controls we don't care about for the
                // frame, so lets ignore those when passing the CQRVA_CONTEXTMENU
                // through to the handler.

                POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
                ScreenToClient((HWND)wParam, &pt);
                
                switch (GetDlgCtrlID(ChildWindowFromPoint((HWND)wParam, pt)))
                {
                    case IDC_FORMAREA:
                    case IDC_FINDANIMATION:
                    case IDC_STATUS:
                        return TRUE;                // handled

                    default:
                        pQueryFrame->_pQueryHandler->ActivateView(CQRVA_CONTEXTMENU, wParam, lParam);
                        return TRUE;
                }

                return FALSE;
            }
            
            case WM_HELP:
            {
                LPHELPINFO phi = (LPHELPINFO)lParam;

                // filter out those controls we are not interested in (they make no sense)
                // to bother the user with

                switch (GetDlgCtrlID((HWND)phi->hItemHandle))
                {
                    case IDC_FORMAREA:
                    case IDC_FINDANIMATION:
                    case IDC_STATUS:
                        return TRUE;

                    default:
                        pQueryFrame->OnHelp(phi);
                        return TRUE;
                }

                return FALSE;                   
            }

            case CQFWM_ADDSCOPE:
            {
                LPCQSCOPE pScope = (LPCQSCOPE)wParam;
                BOOL fSelect = LOWORD(lParam);
                INT iIndex = HIWORD(lParam);

                if (SUCCEEDED(pQueryFrame->AddScope(pScope, iIndex, fSelect)))
                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, 1L);

                return 1;
            }

            case CQFWM_GETFRAME:
            {
                IQueryFrame** ppQueryFrame = (IQueryFrame**)lParam;

                if (ppQueryFrame)
                {
                    pQueryFrame->AddRef();
                    *ppQueryFrame = pQueryFrame;
                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, 1L);
                }

                return 1;
            }

            case CQFWM_ALLSCOPESADDED:
            {
                // there is an async scope collector, it has added all the scopes
                // so we must now attempt to issue the query if the we are in the
                // holding pattern waiting for the scopes to be collected.

                pQueryFrame->_fScopesAddedAsync = FALSE;            // all scopes have been added

                if (pQueryFrame->_pOpenQueryWnd->dwFlags & OQWF_ISSUEONOPEN)
                    PostMessage(pQueryFrame->_hwnd, CQFWM_STARTQUERY, 0, 0);

                return 1;
            }

            case CQFWM_STARTQUERY:
                pQueryFrame->OnFindNow();
                return 1;

            case CQFWM_SETDEFAULTFOCUS:   
            {
                HWND hwndNextTab = _NextTabStop(pQueryFrame->_pCurrentFormPage->hwndPage, FALSE);
                SendMessage(pQueryFrame->_pCurrentFormPage->hwndPage, WM_NEXTDLGCTL, (WPARAM)hwndNextTab, 1);
                break;
            }

            default:
                break;
        }
    }

exit_gracefully:

    return(0);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::CloseQueryFrame
/ ----------------------------
/   Close the query window passing back the data object if required, and ensuring
/   that our result code indicates what is going on.
/
/ In:
/   hResult = result code to pass to the caller
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
VOID CQueryFrame::CloseQueryFrame(HRESULT hres)
{
    TraceEnter(TRACE_FRAME, "CQueryFrame::CloseQueryFrame");
    Trace(TEXT("hResult %08x"), hres);

    // If we succeeded then attempt to collect the IDataObject and pass it
    // back to the caller.

    if (hres == S_OK)
    {
        if (_ppDataObject)
        {
            hres = _pQueryHandler->GetViewObject(CQRVS_SELECTION, IID_IDataObject, (LPVOID*)_ppDataObject);
            FailGracefully(hres, "Failed when collecting the data object");
        }

        if ((_pOpenQueryWnd->dwFlags & OQWF_SAVEQUERYONOK) && _pOpenQueryWnd->pPersistQuery)
        {
            hres = SaveQuery(_pOpenQueryWnd->pPersistQuery);
            FailGracefully(hres, "Failed when persisting query to IPersistQuery blob");
        }

        hres = S_OK;           // success
    }

exit_gracefully:

    _hResult = hres;
    _fExitModalLoop = TRUE;                // bomb out of the modal loop

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::FrameMessageBox
/ ----------------------------
/   Our message box for putting up prompts that relate to the current
/   query.  We handle getting the view information and displaying
/   the prompt, returning the result from MessageBox.
/
/ In:
/   pPrompt = text displayed as a prompt
/   uType = message box type
/
/ Out:
/   INT
/----------------------------------------------------------------------------*/
INT CQueryFrame::FrameMessageBox(LPCTSTR pPrompt, UINT uType)
{
    TCHAR szTitle[MAX_PATH];        
    CQVIEWINFO vi;

    TraceEnter(TRACE_FRAME, "CQueryFrame::FrameMessageBox");

    ZeroMemory(&vi, SIZEOF(vi));
    //vi. dwFlags = 0;                // display attributes

    if (SUCCEEDED(_pQueryHandler->GetViewInfo(&vi)) && vi.hInstance && vi.idTitle)
        LoadString(vi.hInstance, vi.idTitle, szTitle, ARRAYSIZE(szTitle));
    else
        GetWindowText(_hwnd, szTitle, ARRAYSIZE(szTitle));

    TraceLeaveValue(MessageBox(_hwnd, pPrompt, szTitle, uType));    
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnInitDlg
/ ----------------------
/   Handle a WM_INITDAILOG message, this is sent as the first thing the
/   dialog receives, therefore we must handle our initialization that
/   was not handled in the constructor.
/
/ In:
/   hwnd = handle of dialog we are initializing
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::OnInitDialog(HWND hwnd)
{
    HRESULT hres;
    HICON hIcon = NULL;
    TCHAR szGUID[GUIDSTR_MAX+1];
    TCHAR szBuffer[MAX_PATH];
    CQVIEWINFO vi;
    INT dyControls = 0;
    RECT rect, rect2;
    SIZE size;
    
    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnInitDialog");

    // get the HKEY for the handler we are using

    hres = GetKeyForCLSID(_pOpenQueryWnd->clsidHandler, NULL, &_hkHandler);
    FailGracefully(hres, "Failed to open handlers HKEY");

    // pick up the control handles and store them, saves picking them up later

    _hwnd              = hwnd;
    _hwndFrame         = GetDlgItem(hwnd, IDC_FORMAREA);
    _hwndLookForLabel  = GetDlgItem(hwnd, CQID_LOOKFORLABEL);
    _hwndLookFor       = GetDlgItem(hwnd, CQID_LOOKFOR); 
    _hwndLookInLabel   = GetDlgItem(hwnd, CQID_LOOKINLABEL);
    _hwndLookIn        = GetDlgItem(hwnd, CQID_LOOKIN);    
    _hwndBrowse        = GetDlgItem(hwnd, CQID_BROWSE);
    _hwndFindNow       = GetDlgItem(hwnd, CQID_FINDNOW);
    _hwndStop          = GetDlgItem(hwnd, CQID_STOP);
    _hwndNewQuery      = GetDlgItem(hwnd, CQID_CLEARALL);
    _hwndFindAnimation = GetDlgItem(hwnd, IDC_FINDANIMATION);
    _hwndOK            = GetDlgItem(hwnd, IDOK);
    _hwndCancel        = GetDlgItem(hwnd, IDCANCEL);

    // when enable is called this will be the first
    _fFormFirstEnable   = TRUE; 

    // call the IQueryHandler interface and get its display attributes,
    // then reflect these into the dialog we are about to display to the
    // outside world.

    vi.dwFlags = 0;
    vi.hInstance = NULL;
    vi.idLargeIcon = 0;
    vi.idSmallIcon = 0;
    vi.idTitle = 0;
    vi.idAnimation = 0;

    hres = _pQueryHandler->GetViewInfo(&vi);
    FailGracefully(hres, "Failed when getting the view info from the handler");

    _dwHandlerViewFlags = vi.dwFlags;

    if (vi.hInstance)
    {
        HICON hiTemp = NULL;

        if (vi.idLargeIcon)
        {
            _hiconLarge = (HICON)LoadImage(vi.hInstance, 
                                           MAKEINTRESOURCE(vi.idLargeIcon), 
                                           IMAGE_ICON,
                                           0, 0, 
                                           LR_DEFAULTCOLOR|LR_DEFAULTSIZE);
            if (_hiconLarge)
                SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)_hiconLarge);
        }

        if (vi.idSmallIcon)
        {
            _hiconSmall = (HICON)LoadImage(vi.hInstance, 
                                           MAKEINTRESOURCE(vi.idLargeIcon), 
                                           IMAGE_ICON,
                                           GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 
                                           LR_DEFAULTCOLOR);
            if (_hiconSmall)
                SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)_hiconSmall);
        }

        if (vi.idTitle)
        {
            LoadString(vi.hInstance, vi.idTitle, szBuffer, ARRAYSIZE(szBuffer));
            SetWindowText(hwnd, szBuffer);
        }
    }

    if (vi.hInstance && vi.idAnimation)
    {
        SetWindowLongPtr(_hwndFindAnimation, GWLP_HINSTANCE, (LRESULT)vi.hInstance);
        Animate_Open(_hwndFindAnimation, MAKEINTRESOURCE(vi.idAnimation));
    }
    else
    {
        Animate_Open(_hwndFindAnimation, MAKEINTRESOURCE(IDR_FINDANIMATION));
    }

    // now adjust the positions and hide the controls we are not interested in

    if (_pOpenQueryWnd->dwFlags & OQWF_REMOVEFORMS)
    {
        ShowWindow(_hwndLookForLabel, SW_HIDE);
        ShowWindow(_hwndLookFor, SW_HIDE);
    }

    if (_pOpenQueryWnd->dwFlags & OQWF_REMOVESCOPES)
    {
        ShowWindow(_hwndLookInLabel, SW_HIDE);
        ShowWindow(_hwndLookIn, SW_HIDE);
        ShowWindow(_hwndBrowse, SW_HIDE);
    }

    // hiding both the scopes and the forms control causes us to
    // move all the controls up by so many units.  

    if ((_pOpenQueryWnd->dwFlags & (OQWF_REMOVEFORMS|OQWF_REMOVESCOPES)) 
                                        == (OQWF_REMOVEFORMS|OQWF_REMOVESCOPES))
    {
        GetRealWindowInfo(_hwndLookForLabel, &rect, NULL);
        GetRealWindowInfo(_hwndFrame, &rect2, NULL);

        dyControls += rect2.top - rect.top;         
        Trace(TEXT("Moving all controls up by %d units"), dyControls);

        OffsetWindow(_hwndFrame, 0, -dyControls);
        OffsetWindow(_hwndFindNow, 0, -dyControls);
        OffsetWindow(_hwndStop, 0, -dyControls);
        OffsetWindow(_hwndNewQuery, 0, -dyControls);
        OffsetWindow(_hwndFindAnimation, 0, -dyControls);
        OffsetWindow(_hwndOK, 0, -dyControls);

        if (_hwndCancel)
            OffsetWindow(_hwndCancel, 0, -dyControls);
    }

    // hiding OK/Cancel so lets adjust the size here to include the
    // OK/Cancel buttons disappearing, note that we update dyControls
    // to include this delta

    if (!(_pOpenQueryWnd->dwFlags & OQWF_OKCANCEL))
    {
        ShowWindow(_hwndOK, SW_HIDE);        

        if (_hwndCancel)
            ShowWindow(_hwndCancel, SW_HIDE);

        // if this is the filter dialog then lets ensure that 
        // we trim the OK/Cancel buttons from the size by adjusting the 
        // dyControls further.

        GetRealWindowInfo(_hwndOK, &rect, NULL);
        GetRealWindowInfo(_hwndFrame, &rect2, NULL);
        dyControls += rect.bottom - rect2.bottom;
    }

    // having performed that extra bit of initialization lets cache the
    // positions of the various controls, to make sizing more fun...

    GetClientRect(hwnd, &rect2);
    rect2.bottom -= dyControls;

    _dyResultsTop = rect2.bottom;    

    GetRealWindowInfo(hwnd, NULL, &size);
    GetRealWindowInfo(_hwndFrame, &rect, &_szForm);

    Trace(TEXT("dyControls %d"), dyControls);
    size.cy -= dyControls;

    _dxFormAreaLeft = rect.left;
    _dxFormAreaRight = rect2.right - rect.right;
    
    _szMinTrack.cx = size.cx - _szForm.cx;
    _szMinTrack.cy = size.cy - _szForm.cy;

    if (!(_pOpenQueryWnd->dwFlags & OQWF_HIDEMENUS))
    {
        TraceMsg("Adjusting _szMinTrack.cy to account for menu bar");
        _szMinTrack.cy += GetSystemMetrics(SM_CYMENU);
    }
    
    GetRealWindowInfo(_hwndBrowse, &rect, NULL);
    _dxButtonsLeft = rect2.right - rect.left;

    GetRealWindowInfo(_hwndLookIn, &rect, NULL);
    _dxGap = (rect2.right - rect.right) - _dxButtonsLeft;

    GetRealWindowInfo(_hwndFindAnimation, &rect, NULL);
    _dxAnimationLeft = rect2.right - rect.left;

    GetRealWindowInfo(_hwndOK, &rect, NULL);
    _dyOKTop = rect2.bottom - rect.top;
    _dyGap = size.cy - rect.bottom;

    // Now collect the forms and pages, then walk them building the size
    // information that we need.

    hres = GatherForms();
    FailGracefully(hres, "Failed to init form list");

    _szMinTrack.cx += _szForm.cx;
    _szMinTrack.cy += _szForm.cy;

    // Populate the scope control by querying the handler for them,
    // if there are none then we display a suitable message box and
    // let the user know that something went wrong.

    hres = PopulateScopeControl();
    FailGracefully(hres, "Failed to init scope list");

    _fScopesPopulated = TRUE;                              // scope control now populated

    // perform final fix up of the window, ensure that we size it so that
    // the entire form and buttons are visible.  Then set ourselves into the
    // no query state and reset the animation.

    SetWindowPos(hwnd, 
                 NULL,
                 0, 0,
                 _szMinTrack.cx, _szMinTrack.cy,
                 SWP_NOMOVE|SWP_NOZORDER);


    if (_pOpenQueryWnd->dwFlags & OQWF_HIDEMENUS)
        ::SetMenu(hwnd, NULL);

    hres = PopulateFormControl(_pOpenQueryWnd->dwFlags & OQWF_SHOWOPTIONAL);
    FailGracefully(hres, "Failed to populate form control");

    // Now load the query which inturn selects the form that we should be using,
    // if there is no query to load then either use the default form or
    // the first in the list.

    if ((_pOpenQueryWnd->dwFlags & OQWF_LOADQUERY) && _pOpenQueryWnd->pPersistQuery) 
    {
        hres = LoadQuery(_pOpenQueryWnd->pPersistQuery);
        FailGracefully(hres, "Failed when to load query from supplied IPersistQuery");
    }
    else
    {
        if (_pOpenQueryWnd->dwFlags & OQWF_DEFAULTFORM)
        {
            SelectForm(_pOpenQueryWnd->clsidDefaultForm);

            if (!_pCurrentForm)
                ExitGracefully(hres, E_FAIL, "Failed to select the query form");                
        }
        else
        {
            INT iForm = (int)ComboBox_GetItemData(_hwndLookFor, 0);
            LPQUERYFORM pQueryForm = (LPQUERYFORM)DSA_GetItemPtr(_hdsaForms, iForm);
            TraceAssert(pQueryForm);

            SelectForm(pQueryForm->clsidForm);
        }
    }

    StartQuery(FALSE);
    
    // issue on open, therefore lets get the query going, if there is async
    // scope collection then the query will be issued by the bg thread.

    if (_pOpenQueryWnd->dwFlags & OQWF_ISSUEONOPEN)
        PostMessage(_hwnd, CQFWM_STARTQUERY, 0, 0);

    SetForegroundWindow(hwnd);

    hres = S_OK;                          // success

exit_gracefully:

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::EnableControls
/ ---------------------------
/   Set the controls into their enabled/disabled state based on the
/   state of the dialog.
/
/ In:
/   -
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
VOID CQueryFrame::DoEnableControls(VOID)
{   
    BOOL fScopes = (_fAddScopesNYI || ComboBox_GetCount(_hwndLookIn));
    BOOL fEnable = fScopes;
    UINT uEnable = fScopes ? MF_ENABLED:MF_GRAYED;
    HMENU hMenu = GetMenu(_hwnd);
    INT i;

    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::DoEnableControls");

    EnableWindow(_hwndFindNow, !_fQueryRunning && fEnable);
    EnableWindow(_hwndStop, _fQueryRunning && fEnable);
    EnableWindow(_hwndNewQuery, fEnable);

    EnableWindow(_hwndLookFor, !_fQueryRunning && fEnable);
    EnableWindow(_hwndLookIn, !_fQueryRunning && fEnable);
    EnableWindow(_hwndBrowse, !_fQueryRunning && fEnable);

    if (_pCurrentForm)
    {
        CallFormPages(_pCurrentForm, CQPM_ENABLE, (BOOL)(!_fQueryRunning && fEnable), 0);

        if (_fFormFirstEnable)
        {
            PostMessage(_hwnd, CQFWM_SETDEFAULTFOCUS, 0, 0);
            _fFormFirstEnable = FALSE;
        }
    }

    if (_hwndOK)
        EnableWindow(_hwndOK, !_fQueryRunning && fEnable);
    if (_hwndCancel)
        EnableWindow(_hwndCancel, !_fQueryRunning && fEnable);

    for (i = 0 ; i < GetMenuItemCount(hMenu) ; i++)
        EnableMenuItem(hMenu, i, MF_BYPOSITION|uEnable);

    DrawMenuBar(_hwnd);

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnNotify
/ ---------------------
/   Notify event received, decode it and handle accordingly
/
/ In:
/   idCtrl = ID of control issuing notify
/   pNotify -> LPNMHDR structure
/
/ Out:
/   LRESULT
/----------------------------------------------------------------------------*/
LRESULT CQueryFrame::OnNotify(INT idCtrl, LPNMHDR pNotify)
{
    LRESULT lr = 0;

    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnNotify");

    // TCN_SELCHANGE used to indicate that the currently active
    // tab has been changed

    if (pNotify->code == TCN_SELCHANGE)
    {
        INT iPage = TabCtrl_GetCurSel(_hwndFrame);
        TraceAssert(iPage >= 0);

        if (iPage >= 0)
        {
            SelectFormPage(_pCurrentForm, iPage);
            lr = 0;
        }
    }

    TraceLeaveResult((HRESULT)lr);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnSize
/ -------------------
/   The window is being sized and we received a WM_SIZE, therefore move 
/   the content of the window about.
/
/ In:
/   cx = new width
/   cy = new height
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
VOID CQueryFrame::OnSize(INT cx, INT cy)
{
    HDWP hdwp;
    RECT rect, rect2;
    SIZE sz, sz2;
    INT x, cxForm, cyForm;
    INT dyResultsTop = 0;

    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnSize");

    // do as much as we can within a DefWindowPos to aVOID too
    // much flicker.

    hdwp = BeginDeferWindowPos(16);

    if (hdwp)
    {
        {
            // adjust the look for controls, if there is no scope then 
            // stretch the look for control over the entire client area
            // of the window.

            if (!(_pOpenQueryWnd->dwFlags & OQWF_REMOVEFORMS))
            {
                if (_pOpenQueryWnd->dwFlags & OQWF_REMOVESCOPES)
                {
                    GetRealWindowInfo(_hwndLookFor, &rect, &sz);
                    hdwp = DeferWindowPos(hdwp, _hwndLookFor, NULL,
                                       0, 0, 
                                       (cx - _dxFormAreaRight) - rect.left, sz.cy,
                                       SWP_NOZORDER|SWP_NOMOVE);
                }
            }

            // adjust the "look in" controls, if there is a form control
            // then stretch across the remaining space, otherwise move the
            // label and stretch the scope over the remaining space.
        
            if (!(_pOpenQueryWnd->dwFlags & OQWF_REMOVESCOPES))
            {
                INT xScopeRight;

                GetRealWindowInfo(_hwndLookIn, &rect, &sz);
                xScopeRight = cx - _dxFormAreaRight - _dxGap;
                             
                if (_pOpenQueryWnd->dwFlags & OQWF_HIDESEARCHUI)
                {
                    // 
                    // when hiding the search UI, then adjust the button position to account for the
                    // right edge of the dialog not having buttons.
                    //

                    xScopeRight -= (_dxButtonsLeft - _dxFormAreaRight) + _dxGap;
                }
                
                if (_pOpenQueryWnd->dwFlags & OQWF_REMOVEFORMS)
                {
                    GetRealWindowInfo(_hwndLookInLabel, &rect2, &sz2);
                    hdwp = DeferWindowPos(hdwp, _hwndLookInLabel, NULL,
                                          _dxFormAreaLeft, rect2.top, 
                                          0, 0,
                                          SWP_NOSIZE|SWP_NOZORDER);

                    hdwp = DeferWindowPos(hdwp, _hwndLookIn, NULL,
                                          _dxFormAreaLeft+sz2.cx, rect.top, 
                                          xScopeRight - (_dxFormAreaLeft + sz2.cx), sz.cy,
                                          SWP_NOZORDER);
                }
                else
                {
                    hdwp = DeferWindowPos(hdwp, _hwndLookIn, NULL,
                                          0, 0, 
                                          xScopeRight - rect.left, sz.cy,
                                          SWP_NOZORDER|SWP_NOMOVE);
                }

                // browse control is displayed always if we are showing the 
                // scopes.

                GetRealWindowInfo(_hwndBrowse, &rect, NULL);
                hdwp = DeferWindowPos(hdwp, _hwndBrowse, NULL,
                                      xScopeRight+_dxGap, rect.top,
                                      0, 0,
                                      SWP_NOZORDER|SWP_NOSIZE);
            }
                    
            // all the buttons have a fixed offset from the right edege
            // of the dialog, so just handle that as we can.
            
            if (!(_pOpenQueryWnd->dwFlags & OQWF_HIDESEARCHUI))
            {
                GetRealWindowInfo(_hwndFindNow, &rect, NULL);
                hdwp = DeferWindowPos(hdwp, _hwndFindNow, NULL,
                                     (cx - _dxButtonsLeft), rect.top, 
                                     0, 0,
                                     SWP_NOZORDER|SWP_NOSIZE);

                GetRealWindowInfo(_hwndStop, &rect, &sz);
                hdwp = DeferWindowPos(hdwp, _hwndStop, NULL,
                                      (cx - _dxButtonsLeft), rect.top, 
                                      0, 0,
                                      SWP_NOZORDER|SWP_NOSIZE);

                GetRealWindowInfo(_hwndNewQuery, &rect, NULL);
                hdwp = DeferWindowPos(hdwp, _hwndNewQuery, NULL,
                                      (cx - _dxButtonsLeft), rect.top, 
                                      0, 0,
                                      SWP_NOZORDER|SWP_NOSIZE);
 
                GetRealWindowInfo(_hwndFindAnimation, &rect2, &sz2);
                hdwp = DeferWindowPos(hdwp, _hwndFindAnimation, NULL,
                                     (cx - _dxAnimationLeft), rect2.top, 
                                     0, 0,
                                     SWP_NOZORDER|SWP_NOSIZE);
            }

            // position the form "frame" control
        
            GetRealWindowInfo(_hwndFrame, &rect, &sz);
            cxForm = (cx - _dxFormAreaRight) - rect.left;

            hdwp = DeferWindowPos(hdwp, _hwndFrame, NULL,
                                  0, 0, 
                                  cxForm, _szForm.cy,
                                  SWP_NOZORDER|SWP_NOMOVE);

            dyResultsTop = _dyResultsTop;
            
            // when we have a cancel button then ensure that it is to the right
            // of the OK button.

            if (_hwndCancel)
            {
                GetRealWindowInfo(_hwndCancel, &rect, &sz);
                hdwp = DeferWindowPos(hdwp, _hwndCancel, NULL,
                                      (cx - _dxButtonsLeft), dyResultsTop - _dyOKTop,
                                      0, 0,    
                                      SWP_NOZORDER|SWP_NOSIZE);

                GetRealWindowInfo(_hwndOK, &rect, &sz);
                hdwp = DeferWindowPos(hdwp, _hwndOK, NULL,
                                      (cx - _dxButtonsLeft - _dxGap - sz.cx), dyResultsTop - _dyOKTop,
                                      0, 0,    
                                      SWP_NOZORDER|SWP_NOSIZE);
            }
            else
            {
                GetRealWindowInfo(_hwndOK, &rect, &sz);
                hdwp = DeferWindowPos(hdwp, _hwndOK, NULL,
                                      (cx - _dxButtonsLeft), dyResultsTop - _dyOKTop,
                                      0, 0,    
                                      SWP_NOZORDER|SWP_NOSIZE);
            }                                                                
        }

        // move the results and status bar as required

        if (_hwndResults)
        {
            hdwp = DeferWindowPos(hdwp, _hwndStatus, NULL,
                                  0, cy - _cyStatus,
                                  cx, _cyStatus,
                                  SWP_SHOWWINDOW|SWP_NOZORDER);

            hdwp = DeferWindowPos(hdwp, _hwndResults, NULL,
                                  0, dyResultsTop, 
                                  cx, max(0, cy - (dyResultsTop + _cyStatus)),
                                  SWP_SHOWWINDOW|SWP_NOZORDER);
        }

        EndDeferWindowPos(hdwp);

        // here is the strange bit, by this point we have moved & sized all the
        // controls on the dialog except the current page, as this is a child window
        // and not a control which in turn has controls doing this would break
        // the DefWindowPos path, therefore having updated everybody, lets update
        // the page.

        if (_pCurrentFormPage && _pCurrentFormPage->hwndPage)
        {
            GetRealWindowInfo(_hwndFrame, &rect, NULL);
            TabCtrl_AdjustRect(_hwndFrame, FALSE, &rect);

            cxForm = rect.right - rect.left;
            cyForm = rect.bottom - rect.top;

            SetWindowPos(_pCurrentFormPage->hwndPage, NULL,
                         rect.left, rect.top, cxForm, cyForm,
                         SWP_NOZORDER);
        }
    }

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnGetMinMaxInfo
/ ----------------------------
/   The window is being sized and we received a WM_SIZE, therefore move 
/   the content of the window about.
/
/ In:
/   lpmmin -> MINMAXINFO structure
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
VOID CQueryFrame::OnGetMinMaxInfo(LPMINMAXINFO lpmmi)
{
    RECT rect = {0, 0, 0, 0};

    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnGetMinMaxInfo");

#if 0
    if (!_fHideSearchPane)
#endif
    {
        lpmmi->ptMinTrackSize.x = _szMinTrack.cx;
        lpmmi->ptMinTrackSize.y = _szMinTrack.cy;

        if (!_hwndResults)
        {
            lpmmi->ptMaxSize.y = lpmmi->ptMinTrackSize.y;
            lpmmi->ptMaxTrackSize.y = lpmmi->ptMinTrackSize.y;
        }
    }
#if 0
    else
    {
        AdjustWindowRect(&rect, GetWindowLong(_hwnd, GWL_STYLE), (NULL != GetMenu(_hwnd)));
        lpmmi->ptMinTrackSize.y = rect.bottom - rect.top;
    }
#endif

    if (_hwndResults && _hwndStatus)
        lpmmi->ptMinTrackSize.y += _cyStatus;

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnCommand
/ ----------------------
/   We have recieved a WM_COMMAND so process it accordingly.
/
/ In:
/   wParam, lParam = parameters from the message    
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
VOID CQueryFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
    HRESULT hres;
    UINT uID = LOWORD(wParam);
    UINT uNotify = HIWORD(wParam); 
    HWND hwndControl = (HWND)lParam;
    INT i;
    
    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnCommand");
    Trace(TEXT("uID %08x, uNotify %d, hwndControl %08x"), uID, uNotify, hwndControl);

    switch (uID)
    {
        case IDOK:
            TraceMsg("IDOK received");
            CloseQueryFrame(S_OK);
            break;

        case IDCANCEL:
            TraceMsg("IDCANCEL received");
            CloseQueryFrame(S_FALSE);
            break;

        case CQID_LOOKFOR:
        {
            if (uNotify == CBN_SELCHANGE)
            {
                INT iSel = ComboBox_GetCurSel(_hwndLookFor);
                INT iForm = (int)ComboBox_GetItemData(_hwndLookFor, iSel);
                LPQUERYFORM pQueryForm = (LPQUERYFORM)DSA_GetItemPtr(_hdsaForms, iForm);
                TraceAssert(pQueryForm);

                if (S_FALSE == SelectForm(pQueryForm->clsidForm))
                {
                    TraceMsg("SelectForm return S_FALSE, so the user doesn't want the new form");
                    PostMessage(_hwndLookFor, CB_SETCURSEL, (WPARAM)_pCurrentForm->iForm, 0);
                }
                    
            }

            break;
        }

        case CQID_BROWSE:
            OnBrowse();
            break;

        case CQID_FINDNOW:
            OnFindNow();
            break;

        case CQID_STOP:
        {
            LONG style;

            _pQueryHandler->StopQuery();
            // For some reason, the standard method of getting the old
            // def button used in SetDefButton() below isn't working,
            // so we have to forcibly remove the BS_DEFPUSHBUTTON style
            // from the CQID_STOP button.
            style = GetWindowLong(_hwndStop, GWL_STYLE) & ~BS_DEFPUSHBUTTON;
            SendMessage(_hwndStop, 
                        BM_SETSTYLE, 
                        MAKEWPARAM(style, 0), 
                        MAKELPARAM(TRUE, 0));
            SetDefButton(_hwnd, CQID_FINDNOW);
            SetFocus(_hwndFindNow);
            break;
        }

        case CQID_CLEARALL:
            OnNewQuery(TRUE);                        // discard the current query
            break;

        case CQID_FILE_CLOSE:
            TraceMsg("CQID_FILE_CLOSE received");
            CloseQueryFrame(S_FALSE);
            break;

        default:
            _pQueryHandler->InvokeCommand(_hwnd, uID);
            break;
    }

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnInitMenu
/ -----------------------
/   Handle telling the handler that the menu is being initialised, however
/   this should only happen if the menu being activated is the
/   menu bar, otherwise we assume that the caller is tracking a popup
/   menu and has performed the required initalization.
/
/ In:
/   wParam, lParam = parameters from the WM_INITMENU
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
VOID CQueryFrame::OnInitMenu(HMENU hMenu)
{
    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnInitMenu");

    _fTrackingMenuBar = (GetMenu(_hwnd) == hMenu);

    if (_fTrackingMenuBar)
    {
        TraceMsg("Tracking the menu bar, sending activate");

        _pQueryHandler->ActivateView(CQRVA_INITMENUBAR, (WPARAM)hMenu, 0L);

        EnableMenuItem(hMenu, CQID_VIEW_SEARCHPANE, 
                                MF_BYCOMMAND|(_hwndResults != NULL) ? MF_ENABLED:MF_GRAYED);
    }
        
    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnEnterMenuLoop
/ ----------------------------
/   When the user displays a menu we must reflect this into the status bar
/   so that we can give the user help text relating to the commands they 
/   select.
/
/ In:
/   fEntering = entering the menu loop, or leaving.
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
VOID CQueryFrame::OnEnterMenuLoop(BOOL fEntering)
{
    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnEnterMenuLoop");

    if (_hwndStatus)
    {
        if (fEntering)
        {
            SendMessage(_hwndStatus, SB_SIMPLE, (WPARAM)TRUE, 0L);
            SendMessage(_hwndStatus, SB_SETTEXT, (WPARAM)SBT_NOBORDERS|255, 0L);
        }
        else
        {
            SendMessage(_hwndStatus, SB_SIMPLE, (WPARAM)FALSE, 0L);
        }
    }

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnMenuSelect
/ -------------------------
/   Get the status text for this menu item and display it to the user,
/   if this doesn't map to any particular command then NULL out
/   the string.  At this point we also trap our commands.
/
/ In:
/   hMenu = menu the user is on
/   uID = command ID for that item
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
VOID CQueryFrame::OnMenuSelect(HMENU hMenu, UINT uID)
{
    TCHAR szBuffer[MAX_PATH] = { TEXT('\0') };

    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnMenuSelect");
    Trace(TEXT("hMenu %08x, uID %08x"), hMenu, uID);
        
    if (_hwndStatus)
    {
        switch (uID)
        {
            case CQID_FILE_CLOSE:
            case CQID_VIEW_SEARCHPANE:
                LoadString(GLOBAL_HINSTANCE, uID, szBuffer, ARRAYSIZE(szBuffer));
                break;

            default:
                _pQueryHandler->GetCommandString(uID, 0x0, szBuffer, ARRAYSIZE(szBuffer));
                break;
        }

        Trace(TEXT("Setting status bar to: %s"), szBuffer);
        SendMessage(_hwndStatus, SB_SETTEXT, (WPARAM)SBT_NOBORDERS|255, (LPARAM)szBuffer);
    }    

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnFindNow
/ ----------------------
//  Issue the query, resulting in a view window being created and then issuing
//  the parameter block to the query client.
/
/ In:
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::OnFindNow(VOID)
{
    HRESULT hres;
    CQPARAMS qp = { 0 };
    LPQUERYSCOPE pQueryScope = NULL;
    TCHAR szBuffer[MAX_PATH];
    BOOL fFixSize = TRUE;
    RECT rc;
    DECLAREWAITCURSOR;
    BOOL fSetCursor = FALSE;

    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnFindNow");
    TraceAssert(_pCurrentForm != NULL);

    if (_fQueryRunning)
        ExitGracefully(hres, E_FAIL, "Quyery is already running");

    SetWaitCursor();

    fSetCursor = TRUE;

    // If we have not created the viewer before now lets do so, also at the
    // same time we attempt to fix the window size to ensure that enough
    // of the view is visible.

    if (!_hwndResults)
    {
        if (!_hwndStatus)
        {
            _hwndStatus = CreateStatusWindow(WS_CHILD, NULL, _hwnd, IDC_STATUS);
            GetClientRect(_hwndStatus, &rc);
            _cyStatus = rc.bottom - rc.top;
        }

        // Now construct the result viewer for us to use
  
        hres = _pQueryHandler->CreateResultView(_hwnd, &_hwndResults);
        FailGracefully(hres, "Failed when creating the view object");
    
        GetWindowRect(_hwnd, &rc);
        SetWindowPos(_hwnd, NULL,
                     0, 0,
                     rc.right - rc.left, 
                     _szMinTrack.cy + VIEWER_DEFAULT_CY,
                     SWP_NOZORDER|SWP_NOMOVE);        
    }

    // are we still collecting the scopes async?  If so then lets wait until
    // they have all arrived before we set the UI running.

    if (_hdsaScopes && DSA_GetItemCount(_hdsaScopes))
    {         
        // Collect the parameters ready for starting the query, if this fails then
        // there is no point us continuing.

        ZeroMemory(&qp, SIZEOF(qp));
        qp.cbStruct = SIZEOF(qp);
        //qp.dwFlags = 0x0;
        qp.clsidForm = _pCurrentForm->clsidForm;           // new NT5 beta 2

        hres = GetSelectedScope(&pQueryScope);
        FailGracefully(hres, "Failed to get the scope from the LookIn control");

        if (pQueryScope)
        {
            Trace(TEXT("pQueryScope %08x"), pQueryScope);
            qp.pQueryScope = pQueryScope->pScope;
        }

        hres = CallFormPages(_pCurrentForm, CQPM_GETPARAMETERS, 0, (LPARAM)&qp.pQueryParameters);
        FailGracefully(hres, "Failed when collecting parameters from form");

        if (!qp.pQueryParameters)
        {
            LoadString(GLOBAL_HINSTANCE, IDS_ERR_NOPARAMS, szBuffer, ARRAYSIZE(szBuffer));
            FrameMessageBox(szBuffer, MB_ICONERROR|MB_OK);
            ExitGracefully(hres, E_FAIL, "Failed to issue the query, no parameters");
        }

        // We either already had a view, or have just created one.  Either way
        // we must now prepare the query for sending.

        Trace(TEXT("qp.cbStruct %08x"), qp.cbStruct);
        Trace(TEXT("qp.dwFlags %08x"), qp.dwFlags);
        Trace(TEXT("qp.pQueryScope %08x"), qp.pQueryScope);
        Trace(TEXT("qp.pQueryParameters %08x"), qp.pQueryParameters);
        TraceGUID("qp.clsidForm: ", qp.clsidForm);

        hres = _pQueryHandler->IssueQuery(&qp);
        FailGracefully(hres, "Failed in IssueQuery");
    }
    else
    {
        // set the status text to reflect that we are initializng, otherwise it is
        // left empty and looks like we have crashed.

        if (LoadString(GLOBAL_HINSTANCE, IDS_INITIALIZING, szBuffer, ARRAYSIZE(szBuffer)))
        {
            SetStatusText(szBuffer);
        }
    }

    hres = S_OK;               // success

exit_gracefully:

    if (qp.pQueryParameters)
        CoTaskMemFree(qp.pQueryParameters);

    if (fSetCursor)
        ResetWaitCursor();

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnNewQuery
/ -----------------------
/   Discard the current query, prompting the user as requierd.
/
/ In:
/   fAlwaysPrompt = TRUE if we force prompting of the user
/
/ Out:
/   BOOL
/----------------------------------------------------------------------------*/
BOOL CQueryFrame::OnNewQuery(BOOL fAlwaysPrompt)
{
    BOOL fQueryCleared = TRUE;
    TCHAR szBuffer[MAX_PATH];
    RECT rc;

    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnNewQuery");

    if (_hwndResults || fAlwaysPrompt)
    {
        LoadString(GLOBAL_HINSTANCE, IDS_CLEARCURRENT, szBuffer, ARRAYSIZE(szBuffer));
        if (IDOK != FrameMessageBox(szBuffer, MB_ICONINFORMATION|MB_OKCANCEL))
            ExitGracefully(fQueryCleared, FALSE, "Used cancled new query");

        if (_pQueryHandler)
            _pQueryHandler->StopQuery();

        CallFormPages(_pCurrentForm, CQPM_CLEARFORM, 0, 0);

        if (_hwndResults)
        {
            DestroyWindow(_hwndResults);           // no result view now
            _hwndResults = NULL;

            DestroyWindow(_hwndStatus);            // no status bar
            _hwndStatus = NULL;

            GetWindowRect(_hwnd, &rc);             // shrink the window
            SetWindowPos(_hwnd, NULL,
                         0, 0, rc.right - rc.left, _szMinTrack.cy,         
                         SWP_NOZORDER|SWP_NOMOVE);
        }
    }

exit_gracefully:

    TraceLeaveValue(fQueryCleared);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnBrowse
/ ---------------------
/   Browse for a new scope, adding it to the list if not already present,
/   or selecting the previous scope.
/
/ In:
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::OnBrowse(VOID)
{
    HRESULT hres;
    LPQUERYSCOPE pQueryScope = NULL;
    LPCQSCOPE pScope = NULL;
    
    TraceEnter(TRACE_FRAMEDLG, "CQueryFrame::OnBrowse");

    // Call the handler and get a scope allocation back, then add it to the list
    // of scopes to be displayed.

    hres = GetSelectedScope(&pQueryScope);
    FailGracefully(hres, "Failed to get the scope from the LookIn control");

    Trace(TEXT("Calling BrowseForScope _hwnd %08x, pQueryScope %08x (%08x)"), 
                                            _hwnd, pQueryScope, pQueryScope->pScope);

    hres = _pQueryHandler->BrowseForScope(_hwnd, pQueryScope ? pQueryScope->pScope:NULL, &pScope);
    FailGracefully(hres, "Failed when calling BrowseForScope");

    if ((hres != S_FALSE) && pScope)
    {
        hres = InsertScopeIntoList(pScope, DA_LAST, TRUE);
        FailGracefully(hres, "Failed when adding the scope to the control");

        ComboBox_SetCurSel(_hwndLookIn, ShortFromResult(hres));
    }

    hres = S_OK;

exit_gracefully:

    if (pScope)
        CoTaskMemFree(pScope);

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::OnHelp
/ -------------------
/   Invoke the context sensitive help for the window, catch the 
/   handler specific and page specific stuff and pass those help
/   requests down to the relevant objects.
/
/ In:
/   pHelpInfo -> help info structure
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::OnHelp(LPHELPINFO pHelpInfo)
{
    HRESULT hres;
    RECT rc;
    HWND hwnd = (HWND)pHelpInfo->hItemHandle;

    TraceEnter(TRACE_FRAME, "CQueryFrame::OnHelp");

    // We are invoking help, theroefore we need ot check to see where element
    // of the window we are being invoked for.  If it is the 
    // result view then route the message to that, if its the form then
    // likewise.
    //
    // If we don't hit any of the extension controls then lets pass the
    // help onto WinHelp and get it to display the topics we have.

    if (pHelpInfo->iContextType != HELPINFO_WINDOW)
        ExitGracefully(hres, E_FAIL, "WM_HELP handler only copes with WINDOW objects");

    if (_pCurrentFormPage->hwndPage && IsChild(_pCurrentFormPage->hwndPage, hwnd))
    {
        // it was on the query form page, therefore let it go there, that way
        // they can provide topics specific to them

        TraceMsg("Invoking help on the form pane");

        hres = _CallPageProc(_pCurrentFormPage, CQPM_HELP, 0, (LPARAM)pHelpInfo);
        FailGracefully(hres, "Failed when calling page proc to get help");
    }
    else
    {
        // pass the help information through to the handler as an activation,
        // this should really just be a new method, but this works.

        TraceMsg("Invoking help on the results pane");
        TraceAssert(_pQueryHandler);

        hres = _pQueryHandler->ActivateView(CQRVA_HELP, 0, (LPARAM)pHelpInfo);
        FailGracefully(hres, "Handler WndProc returned FALSE");
    }

    hres = S_OK;

exit_gracefully:

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ Scope helper functions
/----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
/ _CallScopeProc
/ --------------
/   Releae the given scope object, freeing the object that is referenced
/   and passing a CQSM_RELEASE message to it.
/
/ In:
/   pQueryScope -> scope object to be called
/   uMsg, pVoid -> parameters for the scope
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT _CallScopeProc(LPQUERYSCOPE pQueryScope, UINT uMsg, LPVOID pVoid)
{
    HRESULT hres;

    TraceEnter(TRACE_SCOPES, "_CallScopeProc");
    Trace(TEXT("pQueryScope %08x, uMsg %d, pVoid %08x"), pQueryScope, uMsg, pVoid);
    
    Trace(TEXT("(cbStruct %d, pScopeProc %08x, lParam %08x)"),
                    pQueryScope->pScope->cbStruct,
                    pQueryScope->pScope->pScopeProc,
                    pQueryScope->pScope->lParam);

    if (!pQueryScope)
        ExitGracefully(hres, S_OK, "pQueryScope == NULL");

    hres = (pQueryScope->pScope->pScopeProc)(pQueryScope->pScope, uMsg, pVoid);
    FailGracefully(hres, "Failed calling ScopeProc");

exit_gracefully:

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ _FreeScope
/ ----------
/   Releae the given scope object, freeing the object that is referenced
/   and passing a CQSM_RELEASE message to it.
/
/ In:
/   pQueryScope -> scope object to be released
/
/ Out:
/   INT == 1 always
/----------------------------------------------------------------------------*/

INT _FreeScopeCB(LPVOID pItem, LPVOID pData)
{
    return _FreeScope((LPQUERYSCOPE)pItem);
}

INT _FreeScope(LPQUERYSCOPE pQueryScope)
{   
    TraceEnter(TRACE_SCOPES, "_FreeScope");
    Trace(TEXT("pQueryScope %08x, pQueryScope->pScope %08x"), pQueryScope, pQueryScope->pScope);
 
    if (pQueryScope)
    {
        _CallScopeProc(pQueryScope, CQSM_RELEASE, NULL);

        if (pQueryScope->pScope)
        {
            LocalFree((HLOCAL)pQueryScope->pScope);
            pQueryScope->pScope = NULL;
        }
    }

    TraceLeaveValue(TRUE);
}   


/*-----------------------------------------------------------------------------
/ CQueryFrame::InsertScopeIntoList
/ --------------------------------
/   Adds the given scope to the scope picker.
/
/ In:
/   pQueryScope -> zcope object to be added to the view
/   i = index to insert the scope at
/   fAddToControl = add the scope the picker control
/   ppQueryScope -> recieves the new query scope object / = NULL
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::InsertScopeIntoList(LPCQSCOPE pScope, INT i, BOOL fAddToControl)
{
    HRESULT hres;
    QUERYSCOPE qs;
    INT iScope;

    TraceEnter(TRACE_SCOPES, "CQueryFrame::InsertScopeIntoList");
    Trace(TEXT("pScope %08x, i %d, fAddToControl %d"), pScope, i, fAddToControl);
    
    if (!pScope)
        ExitGracefully(hres, E_INVALIDARG, "pScope == NULL, not allowed");

    // if we don't have any scopes then allocate the DSA

    if (!_hdsaScopes)
    {
        _hdsaScopes = DSA_Create(SIZEOF(QUERYSCOPE), 4);
        TraceAssert(_hdsaScopes);

        if (!_hdsaScopes)
            ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate the scope DPA");
    }

    // Walk the list of scopes checking to see if this one is already in
    // there, if not then we can add it.

    for (iScope = 0 ; iScope < DSA_GetItemCount(_hdsaScopes) ; iScope++)
    {
        LPQUERYSCOPE pQueryScope = (LPQUERYSCOPE)DSA_GetItemPtr(_hdsaScopes, iScope);
        TraceAssert(pQueryScope);

        if (S_OK == _CallScopeProc(pQueryScope, CQSM_SCOPEEQUAL, pScope))
        {
            hres = ResultFromShort(iScope);
            goto exit_gracefully;
        }
    }

    // Take a copy of the scope blob passed by the caller.  We copy the entire
    // structure who's size is defined by cbStruct into a LocalAlloc block,
    // once we have this we can then build the QUERYSCOPE structure that references
    // it.

    Trace(TEXT("pScope->cbStruct == %d"), pScope->cbStruct);
    qs.pScope = (LPCQSCOPE)LocalAlloc(LPTR, pScope->cbStruct);
    
    if (!qs.pScope)
        ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate query scope");

    Trace(TEXT("Copying structure qs.pScope %08x, pScope %08x"), qs.pScope, pScope);
    CopyMemory(qs.pScope, pScope, pScope->cbStruct);

    //qs.pScope = NULL;
    qs.iImage = -1;         // no image

    // We have a QUERYSCOPE, so initialize it, if that works then append it to the
    // DSA before either setting the return value or appending it to the control.

    _CallScopeProc(&qs, CQSM_INITIALIZE, NULL);
    
    iScope = DSA_InsertItem(_hdsaScopes, i, &qs);
    Trace(TEXT("iScope = %d"), iScope);

    if (iScope == -1)
    {
        _FreeScope(&qs);
        ExitGracefully(hres, E_OUTOFMEMORY, "Failed to add scope to DSA");
    }

    if (fAddToControl)
    {
        LPQUERYSCOPE pQueryScope = (LPQUERYSCOPE)DSA_GetItemPtr(_hdsaScopes, iScope);
        TraceAssert(pQueryScope);

        Trace(TEXT("Calling AddScopeToControl with %08x (%d)"), pQueryScope, iScope);
        hres = AddScopeToControl(pQueryScope, iScope);
    }
    else
    {
        hres = ResultFromShort(iScope);
    }

exit_gracefully:

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::AddScopeToControl
/ ------------------------------
/   Adds the given scope to the scope picker.
/
/ In:
/   pQueryScope -> zcope object to be added to the view
/   i = index into view where to insert the scope
/
/ Out:
/   HRESULT (== index of item added)
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::AddScopeToControl(LPQUERYSCOPE pQueryScope, INT i)
{
    HRESULT hres;
    CQSCOPEDISPLAYINFO cqsdi;
    COMBOBOXEXITEM cbi;
    TCHAR szBuffer[MAX_PATH];
    TCHAR szIconLocation[MAX_PATH] = { 0 };
    INT item;

    TraceEnter(TRACE_SCOPES, "CQueryFrame::AddScopeToControl");

    if (!pQueryScope)
        ExitGracefully(hres, E_INVALIDARG, "No scope specified");

    // Call the scope to get the display information about this
    // scope before we attempt to add it.

    cqsdi.cbStruct = SIZEOF(cqsdi);
    cqsdi.dwFlags = 0;
    cqsdi.pDisplayName = szBuffer;
    cqsdi.cchDisplayName = ARRAYSIZE(szBuffer);
    cqsdi.pIconLocation = szIconLocation;
    cqsdi.cchIconLocation = ARRAYSIZE(szIconLocation);
    cqsdi.iIconResID = 0;
    cqsdi.iIndent = 0;
    
    hres = _CallScopeProc(pQueryScope, CQSM_GETDISPLAYINFO, &cqsdi);
    FailGracefully(hres, "Failed to get display info for the scope");               

    // Now add the item to the control, if they gave as an image then
    // add that to the image list (and tweak the INSERTITEM structure
    // accordingly).

    cbi.mask = CBEIF_TEXT|CBEIF_INDENT;
    cbi.iItem = i;
    cbi.pszText = cqsdi.pDisplayName;
    cbi.iIndent = cqsdi.iIndent;

    Trace(TEXT("Indent is %d"), cqsdi.iIndent);

    if (szIconLocation[0] && cqsdi.iIconResID)
    {
        INT iImage;

        if (!_fScopeImageListSet)
        {
            HIMAGELIST himlSmall;

            Shell_GetImageLists(NULL, &himlSmall);
            SendMessage(_hwndLookIn, CBEM_SETIMAGELIST, 0, (LPARAM)himlSmall);

            _fScopeImageListSet = TRUE;
        }

        cbi.mask |= CBEIF_IMAGE|CBEIF_SELECTEDIMAGE;
        cbi.iImage = Shell_GetCachedImageIndex(szIconLocation, cqsdi.iIconResID, 0x0);;
        cbi.iSelectedImage = cbi.iImage;

        Trace(TEXT("Image index set to: %d"), cbi.iImage);
    }

    item = (INT)SendMessage(_hwndLookIn, CBEM_INSERTITEM, 0, (LPARAM)&cbi);

    if (item == -1)
        ExitGracefully(hres, E_FAIL, "Failed when inserting the scope to the list");

    DoEnableControls();                     // reflect button changes into UI

    hres = ResultFromShort(item);

exit_gracefully:

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::PopulateScopeControl
/ ---------------------------------
/   Collect the scopes that we want to display in the scope control and
/   then populate it.  If the handler doesn't return any scopes then
/   we remove the control and assume that know what to do when they
/   don't receive a scope pointer.
/
/ In:
/   -
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::PopulateScopeControl(VOID)
{
    HRESULT hres;
    LPQUERYSCOPE pQueryScope;
    INT i;
    
    TraceEnter(TRACE_SCOPES, "CQueryFrame::PopulateScopeControl");

    // Collect the scopes that we should be showing in the view, if we don't
    // get any back then we disable the scope control, if we do get some then
    // populate the scope control with them.

    hres = _pQueryHandler->AddScopes();    
    _fAddScopesNYI = (hres == E_NOTIMPL);

    if (hres != E_NOTIMPL)
        FailGracefully(hres, "Failed when calling handler to add scopes");        

    if (_hdsaScopes)
    {
        // We have some scopes, so now we create the image list that we can use
        // for icons with scopes.  Then walk through the DPA getting the scope
        // to give us some display information about itself that we can
        // add to the combo box.

        ComboBox_SetExtendedUI(_hwndLookIn, TRUE);

        for (i = 0 ; i < DSA_GetItemCount(_hdsaScopes); i++)
        {
            pQueryScope = (LPQUERYSCOPE)DSA_GetItemPtr(_hdsaScopes, i);
            TraceAssert(pQueryScope);

            AddScopeToControl(pQueryScope, i);
        }
    }
    else
    {
        // we don't have any scopes after calling AddScopes, this is either 
        // because the ::AddScopes method is not implemented, or the
        // scopes are being added async.  If IssueQuery returned a success
        // we assume they are coming in async and flag as such in our
        // state.

        if (!_fAddScopesNYI)
        {
            TraceMsg("Handler adding scopes async, so marking so");
            _fScopesAddedAsync = TRUE;
        }
    }

    hres = S_OK;                                      // success

exit_gracefully:

    Trace(TEXT("Default scope is index %d"), _iDefaultScope);
    ComboBox_SetCurSel(_hwndLookIn, _iDefaultScope);

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::GetSelectedScope
/ -----------------------------
/   Get the selected from the the scope ComboBox, this is a reference into the 
/   scope DSA.
/
/ In:
/   ppQueryScope = receives a pointer to the new scope
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::GetSelectedScope(LPQUERYSCOPE* ppQueryScope)
{
    HRESULT hres;
    COMBOBOXEXITEM cbi;
    INT iScope;

    TraceEnter(TRACE_SCOPES, "CQueryFrame::GetSelectedScope");

    *ppQueryScope = NULL;

    if (_hdsaScopes)
    {
        // Get the index for the current scope, if it doesn't give a real
        // index to a item in our view then barf!  Otherwise look up the
        // associated scope.

        iScope = ComboBox_GetCurSel(_hwndLookIn);
        Trace(TEXT("iScope %d"), iScope);

        if (iScope == -1)
            ExitGracefully(hres, E_FAIL, "User entered scopes not supported yet");

        *ppQueryScope = (LPQUERYSCOPE)DSA_GetItemPtr(_hdsaScopes, iScope);
        TraceAssert(*ppQueryScope);
    }

    hres = *ppQueryScope ? S_OK : E_FAIL;

exit_gracefully:

    Trace(TEXT("Returning LPQUERYSCOPE %08x"), *ppQueryScope); 

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ Form handling functions
/----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
/ _FreeQueryForm
/ ---------------
/   Destroy the QUERYFORM allocation being used to describe the form in
/   our DPA.  We ensure that we issue a CQPM_RELEASE before doing anything
/
/ In:
/   pQueryForm -> query form to be destroyed
/
/ Out:
/   INT == 1 always
/----------------------------------------------------------------------------*/

INT _FreeQueryFormCB(LPVOID pItem, LPVOID pData)
{
    return _FreeQueryForm((LPQUERYFORM)pItem);
}

INT _FreeQueryForm(LPQUERYFORM pQueryForm)
{
    TraceEnter(TRACE_FORMS, "_FreeQueryForm");
 
    if (pQueryForm)
    {
        if (pQueryForm->hdsaPages)
        {
            DSA_DestroyCallback(pQueryForm->hdsaPages, _FreeQueryFormPageCB, NULL);
            pQueryForm->hdsaPages = NULL;
        }

        Str_SetPtr(&pQueryForm->pTitle, NULL);
        if (pQueryForm->hIcon)
        {
            DestroyIcon(pQueryForm->hIcon);
        }
    }

    TraceLeaveValue(TRUE);
}   


/*-----------------------------------------------------------------------------
/ _FreeQueryFormPage
/ ------------------
/   Given a pointer to a query form page structure release the members that
//  are of interest, including calling the PAGEPROC to releasee the underlying
/   object.
/
/ In:
/   pQueryFormPage -> page to be removed
/
/ Out:
/   INT == 1 always
/----------------------------------------------------------------------------*/

INT _FreeQueryFormPageCB(LPVOID pItem, LPVOID pData)
{
    return _FreeQueryFormPage((LPQUERYFORMPAGE)pItem);
}

INT _FreeQueryFormPage(LPQUERYFORMPAGE pQueryFormPage)
{   
    TraceEnter(TRACE_FORMS, "_FreeQueryFormPage");

    if (pQueryFormPage)
    {
        _CallPageProc(pQueryFormPage, CQPM_RELEASE, 0, 0);          // NB: ignore return code

        if (pQueryFormPage->hwndPage)
        {
            EnableThemeDialogTexture(pQueryFormPage->hwndPage, ETDT_DISABLE);

            DestroyWindow(pQueryFormPage->hwndPage);
            pQueryFormPage->hwndPage = NULL;
        }

        if (pQueryFormPage->pPage)
        {
            LocalFree(pQueryFormPage->pPage);
            pQueryFormPage->pPage = NULL;
        }
    }        

    TraceLeaveValue(TRUE);
}   


/*-----------------------------------------------------------------------------
/ _CallPageProc
/ -------------
/   Call the given page object thunking the arguments as required if the
/   page object is non-UNICODE (only if building UNICODE).
/
/ In:
/   pQueryFormPage -> page object to be called
/   uMsg, wParam, lParam = parameters for message
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT _CallPageProc(LPQUERYFORMPAGE pQueryFormPage, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HRESULT hres;

    TraceEnter(TRACE_FORMS, "_CallPageProc");
    Trace(TEXT("pQueryFormPage %08x, pPage %08x, uMsg %d, wParam %08x, lParam %08x"), 
                        pQueryFormPage, pQueryFormPage->pPage, uMsg, wParam, lParam);

    if (!pQueryFormPage)
        ExitGracefully(hres, S_OK, "pQueryFormPage == NULL");
    
    hres = (pQueryFormPage->pPage->pPageProc)(pQueryFormPage->pPage, pQueryFormPage->hwndPage, uMsg, wParam, lParam);
    FailGracefully(hres, "Failed calling PageProc");

    // hres = S_OK;

exit_gracefully:

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ Functions for adding query forms/pages
/----------------------------------------------------------------------------*/

// CB to add forms to the form DSA.

HRESULT _AddFormsProc(LPARAM lParam, LPCQFORM pForm)
{
    HRESULT hres;
    QUERYFORM qf = {0};
    HDSA hdsaForms = (HDSA)lParam;

    TraceEnter(TRACE_FORMS, "_AddFormsProc");

    if (!pForm || !hdsaForms)
        ExitGracefully(hres, E_INVALIDARG, "Failed to add page pForm == NULL");

    // Allocate and thunk as required

    qf.hdsaPages = NULL;               // DSA of pages
    qf.dwFlags = pForm->dwFlags;       // flags
    qf.clsidForm = pForm->clsid;       // CLSID identifier for this form
    qf.pTitle = NULL;                  // title used for drop down / title bar
    qf.hIcon = pForm->hIcon;           // hIcon passed by caller
    qf.iImage = -1;                    // image list index of icon
    qf.iForm = 0;                      // visible index of form in control
    qf.iPage = 0;                      // currently selected page on form

    if (!Str_SetPtr(&qf.pTitle, pForm->pszTitle))
        ExitGracefully(hres, E_OUTOFMEMORY, "Failed to copy form title string");

    // Allocate the DSA if one doesn't exist yet, then add in the form
    // structure as required.

    if (-1 == DSA_AppendItem(hdsaForms, &qf))
        ExitGracefully(hres, E_FAIL, "Failed to add form to the form DSA");

    hres = S_OK;                          // success
    
exit_gracefully:

    if (FAILED(hres))
        _FreeQueryForm(&qf);

    TraceLeaveResult(hres);
}

// CB to add pages to the page DSA.

HRESULT _AddPagesProc(LPARAM lParam, REFCLSID clsidForm, LPCQPAGE pPage)
{
    HRESULT hres;
    QUERYFORMPAGE qfp = {0};
    HDSA hdsaPages = (HDSA)lParam;

    TraceEnter(TRACE_FORMS, "_AddPagesProc");

    if (!pPage || !hdsaPages)
        ExitGracefully(hres, E_INVALIDARG, "Failed to add page pPage == NULL");

    // copy the pPage structure for us to pass to the PAGEPROC later, nb: we
    // use the cbStruct field to indicate the size of blob we must copy.

    Trace(TEXT("pPage->cbStruct == %d"), pPage->cbStruct);
    qfp.pPage = (LPCQPAGE)LocalAlloc(LPTR, pPage->cbStruct);
   
    if (!qfp.pPage)
        ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate copy of page structure");

    Trace(TEXT("Copying structure qfp.pPage %08x, pPage %08x"), qfp.pPage, pPage);
    CopyMemory(qfp.pPage, pPage, pPage->cbStruct);              // copy the page structure

    //qfp.pPage = NULL;
    qfp.clsidForm = clsidForm;
    qfp.pPageProc = pPage->pPageProc;
    qfp.lParam = pPage->lParam;
    qfp.hwndPage = NULL;

    _CallPageProc(&qfp, CQPM_INITIALIZE, 0, 0);
        
    if (-1 == DSA_AppendItem(hdsaPages, &qfp))
        ExitGracefully(hres, E_FAIL, "Failed to add the form to the DSA");

    hres = S_OK;                      // succcess

exit_gracefully:

    if (FAILED(hres))
        _FreeQueryFormPage(&qfp);

    TraceLeaveResult(hres);
}

// Add forms/pages from a UNICODE IQueryForm iface

HRESULT CQueryFrame::AddFromIQueryForm(IQueryForm* pQueryForm, HKEY hKeyForm)
{
    HRESULT hres;

    TraceEnter(TRACE_FORMS, "CQueryFrame::AddFromIQueryForm");

    if (!pQueryForm)
        ExitGracefully(hres, E_FAIL, "pQueryForm == NULL, failing");

    hres = pQueryForm->Initialize(hKeyForm);
    FailGracefully(hres, "Failed in IQueryFormW::Initialize");

    // Call the form object to add its form and then its pages

    hres = pQueryForm->AddForms(_AddFormsProc, (LPARAM)_hdsaForms);
    
    if (SUCCEEDED(hres) || (hres == E_NOTIMPL))
    {
        hres = pQueryForm->AddPages(_AddPagesProc, (LPARAM)_hdsaPages);
        FailGracefully(hres, "Failed in IQueryForm::AddPages");
    }
    else    
    {
        FailGracefully(hres, "Failed when calling IQueryForm::AddForms");
    }

    hres = S_OK;                      // success

exit_gracefully:

    TraceLeaveResult(hres);
}

#ifdef UNICODE
#define ADD_FROM_IQUERYFORM AddFromIQueryFormW
#else
#define ADD_FROM_IQUERYFORM AddFromIQueryFormA
#endif


/*-----------------------------------------------------------------------------
/ CQueryFrame::GatherForms
/ ------------------------
/   Enumerate all the query forms for the given query handler and build
/   the DPA containing the list of them.  Once we have done this we
/   can then populate the control at some more convientent moment.  
/
/   When gathering we first hit the "handler", then the "Forms" sub-key
/   trying to load all the InProc servers that provide forms.  We build
/   list of hidden, never shown etc.
/
/ In:
/   -
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/

HRESULT _AddPageToForm(LPQUERYFORM pQueryForm, LPQUERYFORMPAGE pQueryFormPage, BOOL fClone)
{
    HRESULT hres;
    QUERYFORMPAGE qfp;
    LPCQPAGE pPage;

    TraceEnter(TRACE_FORMS, "_AddPageToForm");
    TraceAssert(pQueryForm);
    TraceAssert(pQueryFormPage);

    // ensure that we have a page DSA for this form object

    if (!pQueryForm->hdsaPages)
    {
        TraceMsg("Creating a new page DSA for form");
        pQueryForm->hdsaPages = DSA_Create(SIZEOF(QUERYFORMPAGE), 4);

        if (!pQueryForm->hdsaPages)
            ExitGracefully(hres, E_OUTOFMEMORY, "*** No page DSA on form object ***");
    }

    if (!fClone)
    {
        // Moving this page structure to the one associated with the query form,
        // therefore just ensure that the form has a DSA for pages and just 
        // insert an item at the header (yes, we add the pages in reverse).

        Trace(TEXT("Adding page %08x to form %s"), pQueryFormPage, pQueryForm->pTitle);

        if (-1 == DSA_InsertItem(pQueryForm->hdsaPages, 0, pQueryFormPage))
            ExitGracefully(hres, E_FAIL, "Failed to copy page to form page DSA");
    }
    else
    {
        LPCQPAGE pPage = pQueryFormPage->pPage;

        // Copying the page structure (it must be global), therefore clone
        // the QUERYFORMPAGE strucutre and the CQPAGE into a new allocation
        // and insert that into the page DSA.

        Trace(TEXT("Cloning page %08x to form %s"), pQueryFormPage, pQueryForm->pTitle);

        CopyMemory(&qfp, pQueryFormPage, SIZEOF(QUERYFORMPAGE));
        qfp.pPage = (LPCQPAGE)LocalAlloc(LPTR, pPage->cbStruct);

        if (!qfp.pPage)
            ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate copy of page structure");

        Trace(TEXT("Copying structure qfp.pPage %08x, pPage %08x"), qfp.pPage, pPage);
        CopyMemory(qfp.pPage, pPage, pPage->cbStruct);                                      // copy the page structure

        _CallPageProc(&qfp, CQPM_INITIALIZE, 0, 0);
        
        if (-1 == DSA_AppendItem(pQueryForm->hdsaPages, &qfp))
        {
            _FreeQueryFormPage(&qfp);
            ExitGracefully(hres, E_FAIL, "Failed to copy page to form DSA");
        }
    }

    hres = S_OK;                  // success

exit_gracefully:

    TraceLeaveResult(hres);
}

HRESULT CQueryFrame::GatherForms(VOID)
{
    HRESULT hres;
    IQueryForm* pQueryForm = NULL;
    HKEY hKeyForms = NULL;
    TCHAR szBuffer[MAX_PATH];
    INT i, iPage, iForm;
    RECT rect;
    TC_ITEM tci;

    TraceEnter(TRACE_FORMS, "CQueryFrame::GatherForms");

    // Construct DSA's so we can store the forms and pages as required.

    _hdsaForms = DSA_Create(SIZEOF(QUERYFORM), 4);
    _hdsaPages = DSA_Create(SIZEOF(QUERYFORMPAGE), 4);

    if (!_hdsaForms || !_hdsaPages)
        ExitGracefully(hres, E_OUTOFMEMORY, "Failed to create DSA's for storing pages/forms");

    // First check the IQueryHandler to see if it supports IQueryForm, if it does
    // then call it to add its objects.  Note that we don't bother with ANSI/UNICODE
    // at this point as the handler is assumed to be built the same the 
    // the query framework. 

    if (SUCCEEDED(_pQueryHandler->QueryInterface(IID_IQueryForm, (LPVOID*)&pQueryForm)))
    {
        hres = AddFromIQueryForm(pQueryForm, NULL);
        FailGracefully(hres, "Failed when calling AddFromIQueryForm on handlers IQueryForm iface)");
    }

    // now attempt to build the list of forms and pages from the registered form
    // extensions.  These are declared under the handlers CLSID in the registry,
    // under the sub-key "Forms". 

    if (ERROR_SUCCESS != RegOpenKeyEx(_hkHandler, c_szForms, NULL, KEY_READ, &hKeyForms))
    {
        TraceMsg("No 'Forms' sub-key found, therefore skipping");
    }
    else
    {
        // Enumerate all the keys in the "Forms" key, these are assumed to be a list of
        // the form handlers.

        for (i = 0 ; TRUE ; i++)
        {
            DWORD cchStruct = ARRAYSIZE(szBuffer);
            if (ERROR_SUCCESS != RegEnumKeyEx(hKeyForms, i, szBuffer, &cchStruct, NULL, NULL, NULL, NULL))
            {
                TraceMsg("RegEnumKeyEx return's false, therefore stopping eunmeration");
                break;
            }

            GetForms(hKeyForms, szBuffer);
        }
    }

    // Now tally the form/page information together and remove duplicates and attach the pages 
    // to forms, take special note of the global pages.   As all forms will now be in the
    // DSA we can check for a zero count and we don't have to worry about the order
    // in which the the forms and pages were added.

    if (!DSA_GetItemCount(_hdsaForms) || !DSA_GetItemCount(_hdsaPages))
        ExitGracefully(hres, E_FAIL, "Either the forms or pages DSA is empty");
        
    for (iPage = DSA_GetItemCount(_hdsaPages) ; --iPage >= 0 ;)
    {
        LPQUERYFORMPAGE pQueryFormPage = (LPQUERYFORMPAGE)DSA_GetItemPtr(_hdsaPages, iPage);
        TraceAssert(pQueryFormPage);

        Trace(TEXT("iPage %d (of %d)"), iPage, DSA_GetItemCount(_hdsaPages));

        if (!(pQueryFormPage->pPage->dwFlags & CQPF_ISGLOBAL))
        {
            LPQUERYFORM pQueryForm = FindQueryForm(pQueryFormPage->clsidForm);
            TraceAssert(pQueryForm);

            TraceGUID("Adding page to form:", pQueryFormPage->clsidForm);

            if (pQueryForm)
            {
                hres = _AddPageToForm(pQueryForm, pQueryFormPage, FALSE);
                FailGracefully(hres, "Failed when adding page to form");

                if (!DSA_DeleteItem(_hdsaPages, iPage))
                    TraceMsg("**** Failed to remove page from global DSA ****");
            }
        }
    }

    for (iPage = DSA_GetItemCount(_hdsaPages) ; --iPage >= 0 ;)
    {
        LPQUERYFORMPAGE pQueryFormPage = (LPQUERYFORMPAGE)DSA_GetItemPtr(_hdsaPages, iPage);
        TraceAssert(pQueryFormPage);

        if ((pQueryFormPage->pPage->dwFlags & CQPF_ISGLOBAL))
        {
            Trace(TEXT("Adding global page to %d forms"), DSA_GetItemCount(_hdsaForms));

            for (iForm = 0 ; iForm < DSA_GetItemCount(_hdsaForms); iForm++)
            {
                LPQUERYFORM pQueryForm = (LPQUERYFORM)DSA_GetItemPtr(_hdsaForms, iForm);
                TraceAssert(pQueryForm);

                if (!(pQueryForm->dwFlags & CQFF_NOGLOBALPAGES))
                {
                    hres = _AddPageToForm(pQueryForm, pQueryFormPage, TRUE);
                    FailGracefully(hres, "Failed when adding global page to form");
                }
            }

            _FreeQueryFormPage(pQueryFormPage);
            
            if (!DSA_DeleteItem(_hdsaPages, iPage))
                TraceMsg("**** Failed to remove page from global DSA ****");        
        }
    }

    // Walk the list of forms, rmeoving the ones which have no pages assocaited with
    // them, we don't need these around confusing the world around us.  Note that
    // we walk backwards through the list removing.
    //
    // Also remove the optional forms we don't want to ehw orld to see

    for (iForm = DSA_GetItemCount(_hdsaForms) ; --iForm >= 0 ;)
    {
        LPQUERYFORM pQueryForm = (LPQUERYFORM)DSA_GetItemPtr(_hdsaForms, iForm);
        TraceAssert(pQueryForm);

        Trace(TEXT("pQueryForm %08x (%s), pQueryForm->hdsaPages %08x (%d)"), 
                        pQueryForm, 
                        pQueryForm->pTitle,
                        pQueryForm->hdsaPages, 
                        pQueryForm->hdsaPages ? DSA_GetItemCount(pQueryForm->hdsaPages):0);

        if (!pQueryForm->hdsaPages 
                || !DSA_GetItemCount(pQueryForm->hdsaPages)
                    || ((pQueryForm->dwFlags & CQFF_ISOPTIONAL) && !(_pOpenQueryWnd->dwFlags & OQWF_SHOWOPTIONAL)))
        {
            TraceGUID("Removing form: ", pQueryForm->clsidForm);
            _FreeQueryForm(pQueryForm);
            DSA_DeleteItem(_hdsaForms, iForm);
        } 
    }

    if (!DSA_GetItemCount(_hdsaForms))
        ExitGracefully(hres, E_FAIL, "!!!!! No forms registered after page/form fix ups !!!!!");

    // The pages have been attached to the forms so we can now attempt to create the
    // form/page objects.

    _szForm.cx = 0;
    _szForm.cy = 0;

    tci.mask = TCIF_TEXT;
    tci.pszText = TEXT("");
    tci.cchTextMax = 0;
    TabCtrl_InsertItem(_hwndFrame, 0, &tci);           // tabctrl needs at least one item so we can compute sizes

    for (iForm = 0 ; iForm < DSA_GetItemCount(_hdsaForms); iForm++)
    {
        LPQUERYFORM pQueryForm = (LPQUERYFORM)DSA_GetItemPtr(_hdsaForms, iForm);
        TraceAssert(pQueryForm);

        // Create each of the modeless page dialoges that we show to allow the user
        // to edit the search criteria.  We also grab the size and modify the 
        // form informaiton we have so that the default size of the dialog can be 
        // correctly computed.

        for (iPage = 0 ; iPage < DSA_GetItemCount(pQueryForm->hdsaPages); iPage++)
        {
            LPQUERYFORMPAGE pQueryFormPage = (LPQUERYFORMPAGE)DSA_GetItemPtr(pQueryForm->hdsaPages, iPage);
            TraceAssert(pQueryFormPage);

            pQueryFormPage->hwndPage = CreateDialogParam(pQueryFormPage->pPage->hInstance, 
                                                         MAKEINTRESOURCE(pQueryFormPage->pPage->idPageTemplate),
                                                         _hwnd, 
                                                         pQueryFormPage->pPage->pDlgProc, 
                                                         (LPARAM)pQueryFormPage->pPage);
            if (!pQueryFormPage->hwndPage)
                ExitGracefully(hres, E_FAIL, "Failed to create query form page");

            EnableThemeDialogTexture(pQueryFormPage->hwndPage, ETDT_ENABLETAB);

            GetRealWindowInfo(pQueryFormPage->hwndPage, &rect, NULL);
            TabCtrl_AdjustRect(_hwndFrame, TRUE, &rect);

            _szForm.cx = max(rect.right-rect.left, _szForm.cx);
            _szForm.cy = max(rect.bottom-rect.top, _szForm.cy);

            // flush the form parameters

            _CallPageProc(pQueryFormPage, CQPM_CLEARFORM, 0, 0);

            // Call the page with CQPM_SETDEFAULTPARAMETERS with the
            // OPENQUERYWINDOW structure.  wParam is TRUE/FALSE indiciating if
            // the form is the default one, and therefore if the pFormParam is 
            // valid.

            _CallPageProc(pQueryFormPage, CQPM_SETDEFAULTPARAMETERS, 
                          (WPARAM)((_pOpenQueryWnd->dwFlags & OQWF_DEFAULTFORM) &&
                                IsEqualCLSID(_pOpenQueryWnd->clsidDefaultForm, pQueryFormPage->clsidForm)),
                          (LPARAM)_pOpenQueryWnd);
        }

        // If the form has an hIcon then lets ensure that we add that to the form image
        // list, any failure here is non-fatal, in that we will just skip that forms
        // icon in the list (rather than barfing)

        if (pQueryForm->hIcon)
        {
            if (!_himlForms)
                _himlForms = ImageList_Create(COMBOEX_IMAGE_CX, COMBOEX_IMAGE_CY, 0, 4, 1);                
            
            if (_himlForms)
            {
                pQueryForm->iImage = ImageList_AddIcon(_himlForms, pQueryForm->hIcon);
                TraceAssert(pQueryForm->iImage >= 0);
            }            

            DestroyIcon(pQueryForm->hIcon);
            pQueryForm->hIcon = NULL;
        }
    }

    hres = S_OK;                  // success

exit_gracefully:

    DoRelease(pQueryForm);

    if (hKeyForms)
        RegCloseKey(hKeyForms);

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::GetForms
/ ---------------------
/   Given a HKEY to the forms list and the value name for the form we want
/   to add, query for the form information add add the form objects
/   to the master list.
/
/ In:
/   hKeyForms = HKEY for the {CLSID provider}\Forms key
/   pName -> key value to query for
/
/ Out:
/   VOID
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::GetForms(HKEY hKeyForms, LPTSTR pName)
{
    HRESULT hres;
    HKEY hKeyForm = NULL;
    TCHAR szQueryFormCLSID[GUIDSTR_MAX+1];
    DWORD dwFlags;
    DWORD dwSize;
    IUnknown* pUnknown = NULL;
    IQueryForm* pQueryForm = NULL;
    CLSID clsidForm;
    BOOL fIncludeForms = FALSE;

    TraceEnter(TRACE_FORMS, "CQueryFrame::_GetForms");
    Trace(TEXT("pName %s"), pName);

    if (ERROR_SUCCESS != RegOpenKeyEx(hKeyForms, pName, NULL, KEY_READ, &hKeyForm))
        ExitGracefully(hres, E_UNEXPECTED, "Failed to open the form key");

    // Read the flags and try to determine if we should invoke this form object.

    dwSize = SIZEOF(dwFlags);
    if (ERROR_SUCCESS != RegQueryValueEx(hKeyForm, c_szFlags, NULL, NULL, (LPBYTE)&dwFlags, &dwSize))
    {
        TraceMsg("No flags, defaulting to something sensible");
        dwFlags = QUERYFORM_CHANGESFORMLIST;
    }

    Trace(TEXT("Forms flag is %08x"), dwFlags);

    // should be invoke this form object?
    //
    //  - if dwFlags has QUERYFORM_CHANGESFORMSLIST, or
    //  - if dwFlags has QUERYFORM_CHANGESOPTFORMLIST and we are showing optional forms, or
    //  - neither set and the form object supports the requested form

    if (!(dwFlags & QUERYFORM_CHANGESFORMLIST)) 
    {
        if ((dwFlags & QUERYFORM_CHANGESOPTFORMLIST) &&
                (_pOpenQueryWnd->dwFlags & OQWF_SHOWOPTIONAL))
        {
            TraceMsg("Form is optional, are we are showing optional forms");
            fIncludeForms = TRUE;
        }
        else
        {
            // OK, so it either didn't update the form list, or wasn't marked as optional,
            // so lets check to see if it supports the form the user has requested, if not
            // then don't bother loading this guy.

            if (_pOpenQueryWnd->dwFlags & OQWF_DEFAULTFORM)
            {
                TCHAR szBuffer[GUIDSTR_MAX+32];
                HKEY hkFormsSupported;

                TraceMsg("Checking for supported form");                

                if (ERROR_SUCCESS == RegOpenKeyEx(hKeyForm, TEXT("Forms Supported"), NULL, KEY_READ, &hkFormsSupported)) 
                {
                    TraceMsg("Form has a 'Supported Forms' sub-key");

                    GetStringFromGUID(_pOpenQueryWnd->clsidDefaultForm, szQueryFormCLSID, ARRAYSIZE(szQueryFormCLSID));
                    Trace(TEXT("Checking for: %s"), szQueryFormCLSID);

                    if (ERROR_SUCCESS == RegQueryValueEx(hkFormsSupported, szQueryFormCLSID, NULL, NULL, NULL, NULL))
                    {
                        TraceMsg("Query form is in supported list");
                        fIncludeForms = TRUE;
                    }

                    RegCloseKey(hkFormsSupported);
                }
                else
                {
                    TraceMsg("No forms supported sub-key, so loading form object anyway");
                    fIncludeForms = TRUE;
                }
            }                
        }
    }
    else
    {
        TraceMsg("Form updates form list");
        fIncludeForms = TRUE;
    }

    // if fIncludeForms is TRUE, then the checks above succeeded and we are including forms
    // from this object (identified by pName), so we must now get the CLSID of the object
    // we are invoking and use its IQueryForm interface to add the forms that we want.

    if (fIncludeForms)
    {
        // get the form object CLSID, having parse it, then CoCreate it adding the forms.

        dwSize = SIZEOF(szQueryFormCLSID);
        if (ERROR_SUCCESS != RegQueryValueEx(hKeyForm, c_szCLSID, NULL, NULL, (LPBYTE)szQueryFormCLSID, &dwSize))
            ExitGracefully(hres, E_UNEXPECTED, "Failed to read the CLSID of the form");

        Trace(TEXT("szQueryFormCLSID: %s"), szQueryFormCLSID);

        if (!GetGUIDFromString(szQueryFormCLSID, &clsidForm))
            ExitGracefully(hres, E_UNEXPECTED, "Fialed to parse the string as a GUID");

        // we now have the CLISD of the form object, so we must attempt to CoCreate it, we try for
        // the current build type (eg UNICODE) and then fall back to ANSI if thats not supported,
        // so we can support ANSI query form objects on a UNICODE platform.

        hres = CoCreateInstance(clsidForm, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnknown);
        FailGracefully(hres, "Failed to CoCreate the form object");

        if (SUCCEEDED(pUnknown->QueryInterface(IID_IQueryForm, (LPVOID*)&pQueryForm)))
        {
            hres = AddFromIQueryForm(pQueryForm, hKeyForm);
            FailGracefully(hres, "Failed when adding forms from specified IQueryForm iface");
        }
        else
        {
            ExitGracefully(hres, E_UNEXPECTED, "Form object doesn't support IQueryForm(A/W)");
        }
    }

    hres = S_OK;

exit_gracefully:

    if (hKeyForm)
        RegCloseKey(hKeyForm);

    DoRelease(pUnknown);
    DoRelease(pQueryForm);

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::PopulateFormControl
/ ---------------------------------
/   Enumerate all the query forms for the given query handler and build
/   the DPA containing the list of them.  Once we have done this we
/   can then populate the control at some more convientent moment.  
/
/   When gathering we first hit the "handler", then the "Forms" sub-key
/   trying to load all the InProc servers that provide forms.  We build
/   list of hidden, never shown etc.
/
/ In:
/   fIncludeHidden = list forms marked as hidden in control
/
/ Out:
/   VOID
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::PopulateFormControl(BOOL fIncludeHidden)
{
    HRESULT hres;
    COMBOBOXEXITEM cbi;
    LPQUERYFORM pQueryForm;
    INT i, iForm;

    TraceEnter(TRACE_FORMS, "CQueryFrame::PopulateFormControl");
    Trace(TEXT("fIncludeHidden: %d"), fIncludeHidden);

    // list which forms within the control

    if (!_hdsaForms)
        ExitGracefully(hres, E_FAIL, "No forms to list");
        
    ComboBox_ResetContent(_hwndLookFor);                           // remove all items from that control
    
    for (i = 0, iForm = 0 ; iForm < DSA_GetItemCount(_hdsaForms); iForm++)
    {
        LPQUERYFORM pQueryForm = (LPQUERYFORM)DSA_GetItemPtr(_hdsaForms, iForm);
        TraceAssert(pQueryForm);

        // filter out those forms that are not of interest to this instance of the
        // dialog.

        if (((pQueryForm->dwFlags & CQFF_ISOPTIONAL) && !fIncludeHidden) || 
              (pQueryForm->dwFlags & CQFF_ISNEVERLISTED))
        {
            Trace(TEXT("Hiding form: %s"), pQueryForm->pTitle);
            continue;
        }

        // now add the form to the control, including the image if there is an image
        // specified.

        cbi.mask = CBEIF_TEXT|CBEIF_LPARAM;
        cbi.iItem = i++;
        cbi.pszText = pQueryForm->pTitle;
        cbi.cchTextMax = lstrlen(pQueryForm->pTitle);
        cbi.lParam = iForm;

        if (pQueryForm->iImage >= 0)
        {
            Trace(TEXT("Form has an image %d"), pQueryForm->iImage);

            cbi.mask |= CBEIF_IMAGE|CBEIF_SELECTEDIMAGE;
            cbi.iImage = pQueryForm->iImage;
            cbi.iSelectedImage = pQueryForm->iImage;
        }

        pQueryForm->iForm = (int)SendMessage(_hwndLookFor, CBEM_INSERTITEM, 0, (LPARAM)&cbi);

        if (pQueryForm->iForm < 0)
        {
            Trace(TEXT("Form name: %s"), pQueryForm->pTitle);
            ExitGracefully(hres, E_FAIL, "Failed to add the entry to the combo box");
        }
    }

    hres = S_OK;

exit_gracefully:

    TraceLeaveValue(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::SelectForm
/ -----------------------
/   Changes the current form to the one specified as an into the DPA.
/
/ In:
/   iForm = form to be selected
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::SelectForm(REFCLSID clsidForm)
{
    HRESULT hres;
    LPQUERYFORM pQueryForm, pOldQueryForm;
    LPQUERYFORMPAGE pQueryFormPage;
    LPCQPAGE pPage;
    INT nCmdShow = SW_SHOW;
    TCHAR szBuffer[64], szTitle[MAX_PATH];;
    TC_ITEM tci;
    INT i;
    
    TraceEnter(TRACE_FORMS, "CQueryFrame::SelectForm");
    
    pQueryForm = FindQueryForm(clsidForm);
    TraceAssert(pQueryForm);

    if (!pQueryForm)
        ExitGracefully(hres, S_FALSE, "Failed to find the requested form");

    // Change the currently displayed form and change the displayed
    // tabs to correctly indicate this

    if ((pQueryForm != _pCurrentForm))
    {            
        if (!OnNewQuery(FALSE))                               // prompt the user
            ExitGracefully(hres, S_FALSE, "Failed to select the new form");

        TabCtrl_DeleteAllItems(_hwndFrame);

        for (i = 0 ; i < DSA_GetItemCount(pQueryForm->hdsaPages) ; i++)
        {
            pQueryFormPage = (LPQUERYFORMPAGE)DSA_GetItemPtr(pQueryForm->hdsaPages, i);
            pPage = pQueryFormPage->pPage;

            tci.mask = TCIF_TEXT;
            tci.pszText = pQueryForm->pTitle;
            tci.cchTextMax = MAX_PATH;

            if (pPage->idPageName && 
                    LoadString(pPage->hInstance, pPage->idPageName, szBuffer, ARRAYSIZE(szBuffer))) 
            {
                Trace(TEXT("Loaded page title string %s"), szBuffer);
                tci.pszText = szBuffer;
            }

            TabCtrl_InsertItem(_hwndFrame, i, &tci);
        }

        ComboBox_SetCurSel(_hwndLookFor, pQueryForm->iForm);
        _pCurrentForm = pQueryForm;

        SelectFormPage(pQueryForm, pQueryForm->iPage);
       
        // Change the dialog title to reflect the new form

        if (LoadString(GLOBAL_HINSTANCE, IDS_FRAMETITLE, szBuffer, ARRAYSIZE(szBuffer)))
        {
            wsprintf(szTitle, szBuffer, pQueryForm->pTitle);
            SetWindowText(_hwnd, szTitle);
        }

        // Tell the handler that we have changed the form, they can then use this
        // new form name to modify their UI.

        _pQueryHandler->ActivateView(CQRVA_FORMCHANGED, (WPARAM)lstrlen(pQueryForm->pTitle), (LPARAM)pQueryForm->pTitle);
    }

    hres = S_OK;

exit_gracefully:

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::SelectFormPage
/ ---------------------------
/   Change the currently active page of a query form to the one specified
/   by the index.
/
/ In:
/   pQueryForm = query form to be changed
/   iForm = form to be selected
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
VOID CQueryFrame::SelectFormPage(LPQUERYFORM pQueryForm, INT iPage)
{
    LPQUERYFORMPAGE pQueryFormPage;
    RECT rect;

    TraceEnter(TRACE_FORMS, "CQueryFrame::SelectFormPage");

    pQueryFormPage = (LPQUERYFORMPAGE)DSA_GetItemPtr(pQueryForm->hdsaPages, iPage);
       
    // Have we changed the query form page?  If so then display the now dialog
    // hiding the previous one.  We call the TabCtrl to find out where we should
    // be placing this new control.

    if (pQueryFormPage != _pCurrentFormPage)
    {
        // Reflect the change into the tab control

        TabCtrl_SetCurSel(_hwndFrame, iPage);
        pQueryForm->iPage = iPage;

        // Fix the size and visability of the new form
        
        if (_pCurrentFormPage)
            ShowWindow(_pCurrentFormPage->hwndPage, SW_HIDE);
    
        GetRealWindowInfo(_hwndFrame, &rect, NULL);
        TabCtrl_AdjustRect(_hwndFrame, FALSE, &rect);

        SetWindowPos(pQueryFormPage->hwndPage, 
                     HWND_TOP,
                     rect.left, rect.top, 
                     rect.right - rect.left,
                     rect.bottom - rect.top,
                     SWP_SHOWWINDOW);

        _pCurrentFormPage = pQueryFormPage;    
    }

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::CallFormPages
/ --------------------------
/   Given a query form traverse the array of pages calling each of them
/   with the given message information.  If any of the pages return
/   an error code (other than E_NOTIMPL) we bail.
/
/ In:
/   pQueryForm = query form to call
/   uMsg, wParam, lParam = parameters for the page
/   
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
HRESULT CQueryFrame::CallFormPages(LPQUERYFORM pQueryForm, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HRESULT hres = S_OK;    
    INT iPage;

    TraceEnter(TRACE_FORMS, "CQueryFrame::CallFormPages");

    if (!pQueryForm || !pQueryForm->hdsaPages)
        ExitGracefully(hres, E_FAIL, "No pQueryForm || pQueryForm->hdsaPages == NULL");

    Trace(TEXT("pQueryForm %08x"), pQueryForm);
    Trace(TEXT("uMsg %08x, wParam %08x, lParam %08x"), uMsg, wParam, lParam);
    Trace(TEXT("%d pages to call"), DSA_GetItemCount(pQueryForm->hdsaPages));

    // Call each page in turn if it matches the filter we have been given for calling
    // down.  If a page returns S_FALSE or a FAILURE then we exit the loop.  If the
    // failure however is E_NOTIMPL then we ignore.

    for (iPage = 0 ; iPage < DSA_GetItemCount(pQueryForm->hdsaPages); iPage++)
    {
        LPQUERYFORMPAGE pQueryFormPage = (LPQUERYFORMPAGE)DSA_GetItemPtr(pQueryForm->hdsaPages, iPage);
        TraceAssert(pQueryFormPage);

        hres = _CallPageProc(pQueryFormPage, uMsg, wParam, lParam);
        if (FAILED(hres) && (hres != E_NOTIMPL))
        {
            TraceMsg("PageProc returned a FAILURE");
            break;
        }
        else if (hres == S_FALSE)
        {
            TraceMsg("PageProc returned S_FALSE, exiting loop");
            break;
        }
    }

exit_gracefully:

    TraceLeaveResult(hres);
}


/*-----------------------------------------------------------------------------
/ CQueryFrame::FindQueryForm
/ --------------------------
/   Given the CLSID for the form return a pointer to its LPQUERYFORM structure,
/   or NULL if not found.
/
/ In:
/   clsidForm = ID of the form
/   
/ Out:
/   LPQUERYFORM
/----------------------------------------------------------------------------*/
LPQUERYFORM CQueryFrame::FindQueryForm(REFCLSID clsidForm)
{
    LPQUERYFORM pQueryForm = NULL;
    INT i;

    TraceEnter(TRACE_FORMS, "CQueryFrame::FindQueryForm");
    TraceGUID("Form ID", clsidForm);

    for (i = 0 ; _hdsaForms && (i < DSA_GetItemCount(_hdsaForms)) ; i++)
    {
        pQueryForm = (LPQUERYFORM)DSA_GetItemPtr(_hdsaForms, i);
        TraceAssert(pQueryForm);

        if (IsEqualCLSID(clsidForm, pQueryForm->clsidForm))
        {
            Trace(TEXT("Form is index %d (%08x)"), i, pQueryForm);
            break;
        }
    }

    if (!_hdsaForms || (i >= DSA_GetItemCount(_hdsaForms)))
        pQueryForm = NULL;

    TraceLeaveValue(pQueryForm);
}
