BROWSER.CPP
//*************************************************************************** 
// 
//  File: browser.cpp 
// 
//  Copyright (c) 1994, 1995 Microsoft Corp. 
// 
//  Sample Windows application for the SMS API. 
// 
//  Author: 
//      Jonathan Shuval 
// 
//  This program implements a browser for SMS objects. 
// 
//  See the readme.txt file in this directory for full details. 
// 
//*************************************************************************** 
 
 
// ==================================================================== 
// 
//  Includes 
// 
// ==================================================================== 
#include "resource.h" 
 
#include <afx.h> 
#include <afxcoll.h> 
#include <time.h>               // For converting time scalars into strings. 
 
#include <smsapi.h>             // SMS API 
 
// 3d controls. 
// ----------------------- 
#include <ctl3d.h> 
 
// My header. 
// ----------------------- 
#include "browser.h" 
 
 
// ==================================================================== 
// 
//  Manifests. 
// 
// ==================================================================== 
#define WM_LOGIN_MSG    WM_USER+101     // So we can send ourself an 
                                        // instruction to connect. 
 
 
 
 
 
// ------------------------------------------------------------- 
//  Debug utility 
// ------------------------------------------------------------- 
#ifdef DEBUG 
#include <io.h> 
#include <stdio.h> 
#include <time.h> 
int  Log( char *pFmt, ... ); 
#endif  // DEBUG 
 
 
// ==================================================================== 
// 
//  The program starts here. 
// 
// ==================================================================== 
 
int PASCAL WinMain( 
                HINSTANCE hInstance, 
                HINSTANCE hPrevInstance, 
                LPSTR lpszCmdLine, 
                int nCmdShow) 
{ 
    int rc; 
 
    _hInstance = hInstance; 
 
    // Register the 3d controls. 
    // ========================= 
    Ctl3dRegister(_hInstance); 
    Ctl3dAutoSubclass(_hInstance); 
 
 
    // Load the saved db login information into the globals. 
    // Change to suite your preference. 
    // ========================================================= 
    strcat( gszServer,   "jonshu2" ); 
    strcat( gszDbName,   "SMS" ); 
    strcat( gszUserName, "sa" ); 
 
    // Dialogue does the work. 
    // ======================= 
    rc = DialogBox( _hInstance, 
                    MAKEINTRESOURCE(IDD_MAIN_DIALOG), 
                    0, 
                    (DLGPROC)MainDlg ); 
 
 
 
    Ctl3dUnregister(_hInstance); 
 
    return(rc); 
} 
 
 
 
// ==================================================================== 
// 
//  Dialogue to solicit login information for the datasource. 
// 
// ==================================================================== 
 
extern "C" BOOL CALLBACK LoginDlg(  HWND hDlg, 
                                    UINT message, 
                                    WPARAM wParam, 
                                    LPARAM lParam) 
{ 
 
    HWND hEdit; 
 
    switch (message) { 
 
    case WM_INITDIALOG: 
        //KLUDGE: set it with the params I most often use. 
        SetDlgItemText( hDlg, IDC_SQLSERVER, gszServer ); 
        SetDlgItemText( hDlg, IDC_DATABASE,  gszDbName ); 
        SetDlgItemText( hDlg, IDC_LOGINID,   gszUserName); 
 
        // Set focus on this control. 
        hEdit = GetDlgItem( hDlg, IDC_SQLSERVER ); 
        SetFocus(hEdit); 
        return(TRUE); 
 
    case WM_CLOSE: 
        EndDialog( hDlg, IDCANCEL ); 
        return(TRUE); 
 
    case WM_SYSCOLORCHANGE: 
        Ctl3dColorChange(); 
        return(TRUE); 
 
 
    case WM_COMMAND: 
 
        switch (wParam) { 
 
        case IDOK: 
            // Retrieve the values from the edit controls. 
            //LATER: validate. 
            gdsParams.sqlParams.ds          = DB_SQL; 
            gdsParams.sqlParams.pFunc       = NULL;     // No encryption. 
            gdsParams.sqlParams.pszKey      = ""; 
            gdsParams.sqlParams.pszServer   = gszServer; 
            gdsParams.sqlParams.pszDbName   = gszDbName; 
            gdsParams.sqlParams.pszUserName = gszUserName; 
            gdsParams.sqlParams.pszPasswd   = gszPasswd; 
 
            hEdit = GetDlgItem( hDlg, IDC_SQLSERVER ); 
            SendMessage( hEdit, WM_GETTEXT, sizeof(gszServer), (LPARAM)gszServer ); 
 
            hEdit = GetDlgItem( hDlg, IDC_DATABASE ); 
            SendMessage( hEdit, WM_GETTEXT, sizeof(gszDbName), (LPARAM)gszDbName ); 
 
            hEdit = GetDlgItem( hDlg, IDC_LOGINID ); 
            SendMessage( hEdit, WM_GETTEXT, sizeof(gszUserName), (LPARAM)gszUserName ); 
 
            hEdit = GetDlgItem( hDlg, IDC_PASSWORD ); 
            SendMessage( hEdit, WM_GETTEXT, sizeof(gszPasswd), (LPARAM)gszPasswd ); 
 
            // Disable the Login button. This will be enabled after a 
            // container enumeration has completed. 
            hEdit = GetDlgItem( hDlg, IDC_CONNECT ); 
            EnableWindow( hEdit, FALSE ); 
 
 
            // And now terminate. 
            EndDialog( hDlg, IDOK ); 
            return(TRUE); 
        } 
    } 
 
    return(FALSE); 
 
}  /* LoginDlg */ 
 
 
 
 
// ==================================================================== 
// 
//  The real work starts here. 
// 
//  This dialogue allows the user to select a container from the list 
//  of available containers. Once selected the user can select filters 
//  to be applied to the container. 
//  When these steps are completed the container can be viewed by 
//  pressing the "View container" button. 
// ==================================================================== 
 
extern "C" BOOL CALLBACK MainDlg(   HWND hDlg, 
                                    UINT message, 
                                    WPARAM wParam, 
                                    LPARAM lParam) 
{ 
 
    // At init we disable all controls except the "Select container" button. 
    // Once a container has been selected this button is disabled and the 
    // "Select filter" button and associated listbox are enabled. 
    // This button enables us to select multiple filters. 
    // Pressing the "View container" button causes us to dismiss this 
    // dialogue and display the container contents. 
    // ===================================================================== 
    HWND hEdit; 
    SMS_STATUS stat; 
    DWORD dwI; 
    FOLDER_INFO *pCont = NULL; 
    FILTER_INFO    *pF = NULL; 
    MY_FILTER_INFO *pMyFilter = NULL; 
    DWORD ctContainers = 0; 
    int index; 
    static CObArray aFilters;       // Array of FILTER_INFOs representing 
                                    // selected filters. 
    // Get the container types and insert them into the menu 
    static FOLDER_INFO **pContainerList = NULL; 
    int reply; 
    HANDLE hFilter; 
 
    switch (message) { 
    case WM_INITDIALOG: 
        // Dialogue initialisation. 
 
 
        // Post a message to ourselves. This will be processed after the init 
        // completes. 
        // This message causes us to connect to the data source. 
        PostMessage( hDlg, WM_LOGIN_MSG, 0, 0 ); 
 
 
        // ============================================================== 
        // We need to enumerate the containers and filters 
        // and populate the appropriate listboxes. 
        // Enumeration is done by calling into the engine. 
        // The resulting lists are held in globals. 
        // ============================================================== 
 
        // Enumerate the container types. 
        // --------------------------------------------------------- 
 
            // Get the number of containers. 
        SmsEnumContainers(NULL, &ctContainers); 
 
            // Allocate memory and call the enum again. 
        pContainerList = new FOLDER_INFO *[ctContainers]; 
        SmsEnumContainers(pContainerList, &ctContainers); 
 
            // Populate the listbox. 
        hEdit = GetDlgItem(hDlg, IDC_AVAIL_CONTAINER_LIST); 
 
        for (dwI = 0; dwI < ctContainers; dwI++) { 
 
            pCont = pContainerList[dwI]; 
            SendMessage(hEdit, LB_ADDSTRING, 0, (LPARAM)pCont->pszTag); 
        } 
 
            // Select the first one in the list. 
        SendMessage(hEdit, LB_SETCURSEL, 0, 0); 
 
 
        // Enumerate the filter types. 
        // --------------------------------------------------------- 
            // Get the number of filters. 
        SmsEnumFilters(0, &ctFilters); 
 
            // Allocate memory and call the enum again. 
        gpAvailFilterList = new FILTER_INFO[ctFilters]; 
        SmsEnumFilters(gpAvailFilterList, &ctFilters); 
 
            // Populate the listbox. 
        hEdit = GetDlgItem(hDlg, IDC_AVAIL_FILTER_LIST); 
 
        for (dwI = 0; dwI < ctFilters; dwI++) { 
            pF = &gpAvailFilterList[dwI]; 
            SendMessage(hEdit, LB_ADDSTRING, 0, (LPARAM)pF->szTag); 
            pF++; 
        } 
 
            // Select the first one in the list. 
        SendMessage(hEdit, LB_SETCURSEL, 0, 0); 
 
 
        // Disable all except the select container button. 
        // =============================================== 
            // Container group 
            // --------------- 
        hEdit = GetDlgItem(hDlg, IDC_SELECT_CONTAINER_BTN); 
        EnableWindow( hEdit, TRUE ); 
 
        hEdit = GetDlgItem(hDlg, IDC_SELECT_CONTAINER_DONE); 
        EnableWindow( hEdit, FALSE ); 
 
            // Filter group. 
            // ------------- 
        hEdit = GetDlgItem(hDlg, IDC_SELECT_FILTER_BTN); 
        EnableWindow( hEdit, FALSE ); 
 
        hEdit = GetDlgItem(hDlg, IDC_DESELECT_FILTER_BTN); 
        EnableWindow( hEdit, FALSE ); 
 
        hEdit = GetDlgItem(hDlg, IDC_SELECT_FILTERS_DONE); 
        EnableWindow( hEdit, FALSE ); 
 
        // Disable the "view container" button. 
        hEdit = GetDlgItem(hDlg, IDOK); 
        EnableWindow( hEdit, FALSE ); 
 
        // Disable the "configure filter" button. 
        hEdit = GetDlgItem(hDlg, IDC_CONFIG_FILTER_BTN); 
        EnableWindow( hEdit, FALSE ); 
 
        // Disable the "View selected filters" button. 
        hEdit = GetDlgItem(hDlg, IDC_VIEW_SEL_FILTERS_BTN); 
        EnableWindow( hEdit, FALSE ); 
 
        return(TRUE); 
 
 
    case WM_CLOSE: 
        EndDialog( hDlg, IDCANCEL ); 
        return(TRUE); 
 
 
    case WM_SYSCOLORCHANGE: 
        Ctl3dColorChange(); 
        return(TRUE); 
 
 
    case WM_LOGIN_MSG: 
        // Establish connection to datasource. 
        // Done via the Login dialogue. 
        // ==================================== 
        DialogBox( _hInstance, 
                   MAKEINTRESOURCE(IDD_LOGIN), 
                   hDlg, 
                   (DLGPROC)LoginDlg ); 
 
        stat = SmsDataSourceConnect( &gdsParams, &ghConnect ); 
        if (stat != SMS_OK) { 
            wsprintf(szMsg, "DataSourceConnect fails: %d", stat); 
            MessageBox(hDlg, szMsg, "Problem here", MB_OK); 
 
        } else { 
            // Enable "select container" button. 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_CONTAINER_BTN); 
            EnableWindow( hEdit, TRUE ); 
        } 
        return(TRUE); 
 
    case WM_COMMAND: 
 
        switch (wParam) { 
 
        case IDC_SELECT_CONTAINER_BTN: 
            // This takes the current selection from the avail containers 
            // list and puts it in the selected container control. 
            hEdit = GetDlgItem(hDlg, IDC_AVAIL_CONTAINER_LIST); 
            index = SendMessage(hEdit, LB_GETCURSEL, 0, 0); 
            gpCInfo = pContainerList[index]; 
 
            hEdit = GetDlgItem(hDlg, IDC_SELECTED_CONTAINER); 
            SendMessage(hEdit, WM_SETTEXT, 0, (LPARAM)gpCInfo->pszTag); 
 
            // Enable the container done button. 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_CONTAINER_DONE); 
            EnableWindow( hEdit, TRUE ); 
 
            // Disable the "Connect" button. 
            hEdit = GetDlgItem(hDlg, IDC_CONNECT); 
            EnableWindow( hEdit, FALSE ); 
 
            return(TRUE); 
 
 
        case IDC_SELECT_CONTAINER_DONE: 
 
            // Disable the "Select container" button and enable the 
            // "Select filters" button. 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_CONTAINER_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
            // Disable container done btn. 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_CONTAINER_DONE); 
            EnableWindow( hEdit, FALSE ); 
 
            // Enable the filters buttons. 
            // --------------------------- 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_FILTER_BTN); 
            EnableWindow( hEdit, TRUE ); 
 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_FILTERS_DONE); 
            EnableWindow( hEdit, TRUE ); 
 
 
 
            // Open the container. 
            // ------------------- 
            stat = SmsOpenContainer( gpCInfo->dwTag, ghConnect, &ghContainer ); 
            if (stat != SMS_OK) { 
                wsprintf(szMsg, "bad return from OpenContainer: %d", stat); 
                MessageBox(hDlg, szMsg, "Badness", MB_OK ); 
            } 
 
            return(TRUE); 
 
 
        case IDC_SELECT_FILTER_BTN: 
            // "Select filter" button. 
            // This button is grayed-out until a container has 
            // been selected. 
 
            // Take the currently selected filter from the 
            // available filter listbox and add it to the selected 
            // filters listbox. 
            hEdit = GetDlgItem(hDlg, IDC_AVAIL_FILTER_LIST); 
            index = SendMessage(hEdit, LB_GETCURSEL, 0, 0); 
            pF = &gpAvailFilterList[index]; 
 
            hEdit = GetDlgItem(hDlg, IDC_SELECTED_FILTERS); 
            index = SendMessage(hEdit, LB_ADDSTRING, 0, (LPARAM)pF->szTag); 
 
            // Highlight this selection in the selected box. 
            SendMessage(hEdit, LB_SETCURSEL, (WPARAM)index, 0 ); 
 
 
            // Add the filter entry to a list of filters that we 
            // keep handy. Maintain the index for easier access. 
            //NOTE: remember to free it all. 
            pMyFilter = new MY_FILTER_INFO; 
            pMyFilter->pF = pF; 
            pMyFilter->hFilter = NULL; 
            aFilters.Add( (CObject *)pMyFilter ); 
 
            // Enable the deselect button. 
            // --------------------------- 
            hEdit = GetDlgItem(hDlg, IDC_DESELECT_FILTER_BTN); 
            EnableWindow( hEdit, TRUE ); 
 
            // And the "configure filter" button. 
            // ---------------------------------- 
            hEdit = GetDlgItem(hDlg, IDC_CONFIG_FILTER_BTN); 
            EnableWindow( hEdit, TRUE ); 
 
            // Enable the "selected filters" label and combo. 
            hEdit = GetDlgItem(hDlg, IDC_SELECTED_FILTERS); 
            EnableWindow( hEdit, TRUE ); 
 
            return(TRUE); 
 
 
        case IDC_DESELECT_FILTER_BTN: 
            // Remove the current item from the selected filters listbox. 
            hEdit = GetDlgItem(hDlg, IDC_SELECTED_FILTERS); 
            index = SendMessage(hEdit, LB_GETCURSEL, 0, 0); 
            SendMessage(hEdit, LB_DELETESTRING, (WPARAM)index, 0); 
 
            // Remove this filter from the selected filters list. 
            pMyFilter = (MY_FILTER_INFO *)aFilters.GetAt( index ); 
            delete pMyFilter; 
            pMyFilter = NULL; 
            aFilters.RemoveAt( index ); 
 
            // Select the previous one in the list. 
            // If this was the first (index = 0) then leave index alone 
            // else decrement it. 
            if (index > 0) { 
                index--; 
            } 
            SendMessage(hEdit, LB_SETCURSEL, (WPARAM)index, 0); 
 
            // Check if we need to disable any buttons. 
            // If the selected filters listbox is now empty disable 
            // the "deselect" and "configure" filter buttons. 
            // ----------------------------------------------------- 
            index = SendMessage(hEdit, LB_GETCOUNT, 0, 0); 
            if (index == 0) { 
                // deselect button. 
                // --------------------------- 
                hEdit = GetDlgItem(hDlg, IDC_DESELECT_FILTER_BTN); 
                EnableWindow( hEdit, FALSE ); 
 
                // configure filter button. 
                // ---------------------------------- 
                hEdit = GetDlgItem(hDlg, IDC_CONFIG_FILTER_BTN); 
                EnableWindow( hEdit, FALSE ); 
            } 
 
 
            return(TRUE); 
 
 
        case IDC_CONFIG_FILTER_BTN: 
            // Call a dialogue to handle this. 
 
            // Retrieve the filter and pass it in a global so that 
            // the dlgproc can use it. 
            hEdit = GetDlgItem(hDlg, IDC_SELECTED_FILTERS); 
            index = SendMessage(hEdit, LB_GETCURSEL, 0, 0); 
            gpMyFilter = (MY_FILTER_INFO *)aFilters[index]; 
 
            reply = DialogBox(  _hInstance, 
                                MAKEINTRESOURCE(IDD_CONFIG_FILTER), 
                                hDlg, 
                                (DLGPROC)ConfigFilterDlg ); 
 
            //LATER: put in cancel btn 
            return(TRUE); 
 
 
        case IDC_VIEW_SEL_FILTERS_BTN: 
            // User has requested to view filters that have been 
            // applied to the container. 
            // ================================================= 
            DialogBox(  _hInstance, 
                        MAKEINTRESOURCE(IDD_VIEW_SEL_FILTERS_DLG), 
                        hDlg, 
                        (DLGPROC)ViewSelFiltersDlg ); 
            return(TRUE); 
 
 
 
        case IDC_SELECT_FILTERS_DONE: 
            // Check that all of the filters that we've selected have been 
            // configured, warning if not. 
            // (NOTE: we should put in a check mark next to selected 
            // filters to show if they've been configured.) 
            // It is only at this point that we can actually apply the 
            // filter to the container. 
 
            // Loop through the filters. 
            for (dwI = 0; dwI < (DWORD)aFilters.GetSize(); dwI++) { 
                pMyFilter = (MY_FILTER_INFO *)aFilters[dwI]; 
                if (pMyFilter->hFilter == NULL) { 
                    wsprintf(szMsg, "A %s has not been configured,\ndo you want to configure it now?", 
                                    pMyFilter->pF->szTag); 
                    reply = MessageBox( hDlg, 
                                        szMsg, 
                                        "Warning", 
                                        MB_ICONEXCLAMATION| MB_YESNO ); 
                    if (reply == IDYES) { 
                        // We want to configure this filter now. 
                        // Stubbed: needs implementing. 
                        MessageBox(hDlg, "Not implemented", "Go away", MB_OK); 
                    } 
 
                } else { 
                    // We can apply the filter now. 
                    hFilter = pMyFilter->hFilter; 
                    stat = SmsSetFilter( ghContainer, hFilter ); 
                    SmsCloseFilter( hFilter); 
                    if (stat != SMS_OK) { 
                        wsprintf(szMsg, "SetFilter for %s failed: %d", 
                                            pMyFilter->pF->szTag, stat); 
                        MessageBox(hDlg, szMsg, "Warning", MB_OK); 
                    } 
                } 
            } 
 
            // Must grey-out all other filter controls. 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_FILTERS_DONE); 
            EnableWindow( hEdit, FALSE ); 
 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_FILTER_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
            hEdit = GetDlgItem(hDlg, IDC_DESELECT_FILTER_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
            hEdit = GetDlgItem(hDlg, IDC_CONFIG_FILTER_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
 
            // The view container btn enabled only when filter done btn 
            // clicked. 
            hEdit = GetDlgItem(hDlg, IDOK); 
            EnableWindow( hEdit, TRUE ); 
 
            // Enable the "Connect" button. 
            hEdit = GetDlgItem(hDlg, IDC_CONNECT); 
            EnableWindow( hEdit, TRUE ); 
 
            // Enable the "View selected filters" button. 
            // We only want to do this if we have any filters. 
            if (aFilters.GetSize() > 0) { 
                hEdit = GetDlgItem(hDlg, IDC_VIEW_SEL_FILTERS_BTN); 
                EnableWindow( hEdit, TRUE ); 
            } 
 
            return(TRUE); 
 
        case IDOK: 
            // This is the "View Container" button. 
 
            // Tell the container to populate himself. 
            stat = SmsPopulate( ghContainer, POP_SYNC, NULL ); 
            if (stat != SMS_OK) { 
                wsprintf(szMsg, "bad return from Populate: %d", stat); 
                MessageBox(hDlg, szMsg, "Badness", MB_OK ); 
            } 
 
            // Enter the container view dialogue. 
            DialogBox(  _hInstance, 
                        MAKEINTRESOURCE(IDD_THE_CONTAINER), 
                        hDlg, 
                        (DLGPROC)ContainerViewDlg ); 
 
            SmsCloseContainer( ghContainer ); 
 
            // Log off from data source. 
            SmsDataSourceDisconnect( ghConnect ); 
 
 
            //////////////////////////////////////////////////// 
            //  Ready for restart. 
            //////////////////////////////////////////////////// 
 
            // Free all global and static data. 
            // ------------------------------------------------- 
            for (index = 0; index < aFilters.GetSize(); index ++) { 
                pMyFilter = (MY_FILTER_INFO *)aFilters.GetAt( index ); 
                delete pMyFilter; 
                pMyFilter = NULL; 
            } 
            aFilters.RemoveAll(); 
 
            pCont = NULL; 
            pF = NULL; 
            gpCInfo = NULL; 
            ghConnect = NULL; 
 
            // Delete any strings that were in the selected 
            // container and filter edit controls. 
            // ------------------------------------------------- 
            SetDlgItemText( hDlg, IDC_SELECTED_CONTAINER, "" ); 
            hEdit = GetDlgItem(hDlg, IDC_SELECTED_FILTERS); 
            SendMessage( hEdit, LB_RESETCONTENT, 0, 0 ); 
 
            // Disable all controls except Login. 
            // ------------------------------------------------- 
                // Container group 
                // --------------- 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_CONTAINER_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_CONTAINER_DONE); 
            EnableWindow( hEdit, FALSE ); 
 
                // Filter group. 
                // ------------- 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_FILTER_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
            hEdit = GetDlgItem(hDlg, IDC_DESELECT_FILTER_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
            hEdit = GetDlgItem(hDlg, IDC_SELECT_FILTERS_DONE); 
            EnableWindow( hEdit, FALSE ); 
 
            // Disable the "view container" button. 
            hEdit = GetDlgItem(hDlg, IDOK); 
            EnableWindow( hEdit, FALSE ); 
 
            // Disable the "configure filter" button. 
            hEdit = GetDlgItem(hDlg, IDC_CONFIG_FILTER_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
            // Disable the "View selected filters" button. 
            hEdit = GetDlgItem(hDlg, IDC_VIEW_SEL_FILTERS_BTN); 
            EnableWindow( hEdit, FALSE ); 
 
            // Reset listbox selections. 
            // ------------------------------------------------- 
            // Reset listbox selections to first in list. 
            hEdit = GetDlgItem(hDlg, IDC_AVAIL_CONTAINER_LIST); 
            SendMessage(hEdit, LB_SETCURSEL, 0, 0); 
 
            hEdit = GetDlgItem(hDlg, IDC_AVAIL_FILTER_LIST); 
            SendMessage(hEdit, LB_SETCURSEL, 0, 0); 
 
 
            //////////////////////////////////////////////////// 
 
            // Enable the Login button so use can try again. 
            hEdit = GetDlgItem( hDlg, IDC_CONNECT ); 
            EnableWindow( hEdit, TRUE ); 
 
            return(TRUE); 
 
 
        case IDC_CONNECT: 
            // This is the "Connect" button. 
            SendMessage( hDlg, WM_LOGIN_MSG, 0, 0 ); 
            return(TRUE); 
 
 
        case IDC_ALL_DONE: 
            // This is the "done" button. 
            EndDialog( hDlg, IDOK ); 
 
            // And now terminate. 
            return(TRUE); 
        } 
    } 
 
    return(FALSE); 
 
} 
 
 
 
 
// ==================================================================== 
// 
//  This is the view container dialogue that is brought up in response 
//  to the "view container" button in the main dialogue. 
// 
//  We have a listbox for the sub-folders, which we need to populate. 
//  We also have a group box containing details about the container. 
// 
//  The container handle is held in a global "ghContainer". 
// 
// ==================================================================== 
 
extern "C" BOOL CALLBACK ContainerViewDlg( 
                                    HWND hDlg, 
                                    UINT message, 
                                    WPARAM wParam, 
                                    LPARAM lParam) 
{ 
 
    // We need to fill in the container tag and the number of folders. 
    // The first we'll put into a global for now (from MainDlg), the 
    // latter is currently unavailable so we'll put in some gunk. 
 
    HWND hEdit; 
    static DWORD ctFolders = 0; 
    SMS_STATUS stat; 
    static CStringArray asID;           // Array of CStrings for IDs. 
    CString sID; 
    char szID[SMS_DATA_BUFF_SIZE+1];    // buffer to read in folder ID. 
    DWORD dwI; 
    HANDLE handle;                      // Temp holds handle of folder. 
    int index; 
    static CDWordArray folderList;      // List of opened folder handles. 
 
 
    switch (message) { 
 
    case WM_INITDIALOG: 
        // Dialogue initialisation. 
 
        // We have to open all the sub-folders, holding their 
        // handles in some array. For each folder we want to extract 
        // and display in the listbox it's ID. 
        // NOTE: GetFolderCount not yet imnplemented for containers. 
        // Instead I'll do GetNextFolder till I run out of folders. 
        ctFolders = 0; 
 
        for (ctFolders = 0; 1; ctFolders++) { 
 
            stat = SmsGetNextFolder( ghContainer, F_ANY, &handle ); 
            if (stat != SMS_OK) { 
                // assume no more folders for now 
                break; 
            } 
            // Save the handle. 
            folderList.Add( (DWORD)handle ); 
        } 
 
 
        // Get the ID of each folder and insert into listbox. 
        // -------------------------------------------------- 
        hEdit = GetDlgItem(hDlg, IDC_ID_LB); 
 
        for (dwI = 0; dwI < ctFolders; dwI++) { 
 
            // Retrieve the ID and store in the listbox. 
            handle = (HANDLE)folderList.GetAt( dwI ); 
            stat = SmsGetFolderID( handle, szID ); 
            if (stat != SMS_OK) { 
                wsprintf(szMsg, "GetID [%d] fails: %d", dwI, stat); 
                MessageBox(hDlg, szMsg, "API problem", MB_OK); 
            } 
            asID.SetAtGrow( dwI, (CString)szID ); 
 
            SendMessage(hEdit, LB_ADDSTRING, 0, (LPARAM)szID); 
        } 
 
 
        // Select the first one in the list. 
        SendMessage(hEdit, LB_SETCURSEL, 0, 0); 
 
 
        // Set container name and number of folders. 
        // ----------------------------------------- 
        SetDlgItemText( hDlg, IDC_WHAT_CONTAINER, (LPCSTR)gpCInfo->pszTag ); 
        SetDlgItemInt( hDlg, IDC_CTR_NUM_FOLDERS, ctFolders, FALSE ); 
 
        // If no folders disable the "View folder" button. 
        // ----------------------------------------------- 
        if (ctFolders == 0) { 
            hEdit = GetDlgItem(hDlg, IDOK); 
            EnableWindow( hEdit, FALSE ); 
        } 
 
        return(TRUE); 
 
 
    case WM_CLOSE: 
        // Close all folders. 
        for (dwI = 0; dwI < ctFolders; dwI++) { 
            handle = (HANDLE)folderList.GetAt( dwI ); 
            SmsCloseFolder( handle ); 
        } 
        folderList.RemoveAll(); 
 
        // Clear the ID string array. 
        asID.RemoveAll(); 
 
        EndDialog( hDlg, IDCANCEL ); 
        return(TRUE); 
 
 
    case WM_SYSCOLORCHANGE: 
        Ctl3dColorChange(); 
        return(TRUE); 
 
 
    case WM_COMMAND: 
 
        switch (wParam) { 
 
        case IDOK: 
            // Get the current selection and store it in a global 
            // so that it can be picked up by FolderViewDlg. 
            // -------------------------------------------------- 
            hEdit = GetDlgItem(hDlg, IDC_ID_LB); 
            index = SendMessage(hEdit, LB_GETCURSEL, 0, 0); 
            ghFolder = (HANDLE)folderList.GetAt( index ); 
 
            // Now we rewind the folder. This means that we can re-enumerate 
            // the folder's properties (ID, scalars etc). 
            SmsRewind( ghFolder, RW_ALL ); 
            // Enter the folder view dialogue. 
            DialogBox(  _hInstance, 
                        MAKEINTRESOURCE(IDD_FOLDER_VIEW), 
                        hDlg, 
                        (DLGPROC)FolderViewDlg ); 
 
            return(TRUE); 
 
        case IDC_DONE: 
            // We're done with this container. Close all the folders 
            // and the container. 
            for (dwI = 0; dwI < ctFolders; dwI++) { 
                handle = (HANDLE)folderList.GetAt( dwI ); 
                // Close the folder 
                SmsCloseFolder( handle ); 
            } 
            folderList.RemoveAll(); 
 
 
            // Clear the ID string array. 
            asID.RemoveAll(); 
 
 
            // Container closed in calling dlg. 
 
            // This returns us to the previous dialogue. 
            EndDialog(hDlg, IDOK); 
            return(TRUE); 
        } 
    } 
 
    return(FALSE); 
 
 
 
}  /* ContainerViewDlg */ 
 
 
// ==================================================================== 
// 
//  This is the view folder dialogue that is brought up in response to 
//  the "view folder" button in the container dialogue. 
// 
//  We have a listbox for the sub-folders, which we need to populate. 
//  We also have a group box containing details about the folder. 
// 
//  The handle to the current folder has been placed in the ghFolder 
//  global. 
// 
//  When this dlgproc is entered via INITDIALOG we create a folder 
//  frame that contains information that we have gleaned from the 
//  folder, including the folder's handle. This frame is stacked. 
//  When the 'view sub-folder" button is pressed we will simulate this 
//  same action. A 'Done" button press causes us to free the current 
//  frame and reload from the top of stack (if any). 
//  The WM_CLOSE causes us to walk the stack frame freeing all frames. 
// 
// ==================================================================== 
 
// Macro for easier checking of returns from the APIs. 
// Not sure about the NO_MORE_DATA. Check it. 
#define CHKIT(h, str) if (stat != SMS_OK && stat != SMS_MORE_DATA && stat != SMS_NO_MORE_DATA) { \ 
    wsprintf(szMsg, "Handle: %08X %s fails: %d", h, str, stat); \ 
    MessageBox(hDlg, szMsg, "API Error", MB_OK); return((CFrame *)NULL); } 
 
 
extern "C" BOOL CALLBACK FolderViewDlg( 
                                    HWND hDlg, 
                                    UINT message, 
                                    WPARAM wParam, 
                                    LPARAM lParam) 
{ 
    HWND hEdit; 
    DWORD dwI;                          // Loop index. 
 
    int index; 
    CString sID;                        // Temp sub-folder's ID. 
    static CFrame *pf = NULL;           // Current stack frame. 
    char *pszTmp; 
    char szBuff[SMS_DATA_BUFF_SIZE+1];  // Just for setting window title. 
 
    static CFrameStack frameStack(hDlg); 
 
    switch (message) { 
 
    case WM_INITDIALOG: 
        // Dialogue initialisation. 
 
        // Set the window title to say "View of folder: <name>". 
        SmsGetFolderID( ghFolder, szBuff ); 
        wsprintf(szMsg, "View of folder: %s", szBuff); 
        SetWindowText( hDlg, szMsg ); 
 
        // Extract requisite information about the folder: 
        // ID, type (use tag), scalar count, folder count, folder type count. 
        pf = CreateFolderFrame( hDlg, ghFolder ); 
        if (pf) { 
            // And display it. 
            DisplayFolderFrame( hDlg, pf ); 
 
        } else { 
            // This is an error. 
            wsprintf(szMsg, "CreateFolderFrame failed for handle %08X", ghFolder); 
            MessageBox(hDlg, szMsg, "An error", MB_OK); 
            EndDialog( hDlg, IDCANCEL ); 
        } 
 
        return(TRUE); 
 
 
    case WM_CLOSE: 
        // Delete current frame. 
        // ===================== 
        // remove the ID strings from the string array 
        for (dwI = 0; dwI < (DWORD)pf->aszID.GetSize(); dwI++) { 
            pszTmp = (char *)pf->aszID[dwI]; 
            delete pszTmp; 
        } 
        // remove the handles 
        delete pf->phFolders; 
 
        delete pf; 
        pf = NULL; 
 
        // Free all previous frames. 
        // ========================= 
        while (pf = frameStack.pop()) { 
            // remove the ID strings from the string array 
            for (dwI = 0; dwI < (DWORD)pf->aszID.GetSize(); dwI++) { 
                pszTmp = (char *)pf->aszID[dwI]; 
                delete pszTmp; 
            } 
            // Remove the handles 
            delete pf->phFolders; 
            ghFolder = pf->hFolder; 
            delete pf; 
 
        } 
        EndDialog( hDlg, IDCANCEL ); 
        return(TRUE); 
 
 
    case WM_SYSCOLORCHANGE: 
        Ctl3dColorChange(); 
        return(TRUE); 
 
 
    case WM_COMMAND: 
 
        switch (wParam) { 
 
        case IDC_VIEW_FOLDER: 
            // We're going down a level so stack our frame. 
            frameStack.push( pf ); 
 
            // Get the current selection index. Use this to locate 
            // the folder's handle. 
            // We must save the existing folder handle in the frame 
            // and re-instate it when we return. 
 
            hEdit = GetDlgItem( hDlg, IDC_SUBFOLDER_LIST ); 
            index = SendMessage(hEdit, LB_GETCURSEL, 0, 0); 
            ghFolder = pf->phFolders[index]; 
 
            // First clear the listbox entries (should clear other entries 
            // as well, but I think they'll just get overwritten.) 
            SendMessage(hEdit, LB_RESETCONTENT, 0, 0); 
 
            // Create a new stack frame with all the details 
            // that we did in the INITDLG. 
            pf = CreateFolderFrame( hDlg, ghFolder ); 
            if (pf) { 
                // And display it. 
                DisplayFolderFrame( hDlg, pf ); 
 
            } else { 
                // This is an error. 
                wsprintf(szMsg, "CreateFolderFrame failed for handle %08X", ghFolder); 
                MessageBox(hDlg, szMsg, "An error", MB_OK); 
                EndDialog( hDlg, IDCANCEL ); 
            } 
 
 
            // Select the first one in the list and set focus here. 
            SendMessage(hEdit, LB_SETCURSEL, 0, 0); 
            SetFocus(hEdit); 
 
            return(TRUE); 
 
 
        case IDC_VIEW_SCALARS: 
            // So we can enumerate scalars from the beginning. 
            SmsRewind( ghFolder, RW_ALL ); 
 
            DialogBox( _hInstance, 
                       MAKEINTRESOURCE(IDD_SCALARS), 
                       hDlg, 
                       (DLGPROC)ScalarViewDlg ); 
            return(TRUE); 
 
 
        case IDC_VIEW_EXPRESSION: 
            // So we can enumerate tokens in expression from the beginning. 
            SmsRewind( ghFolder, RW_ALL ); 
 
            DialogBox( _hInstance, 
                       MAKEINTRESOURCE(IDD_EXPRESSION), 
                       hDlg, 
                       (DLGPROC)ExpressionViewDlg ); 
            return(TRUE); 
 
 
 
        case IDC_BACK: 
            // This will take us back one level. 
            // We have to free all data from the current stack frame 
            // and then restore the previous one. 
            // When the stack frame is empty we return to the previous 
            // dialogue. 
 
            // Delete current frame. 
            // ===================== 
            // remove the ID strings from the string array 
            for (dwI = 0; dwI < (DWORD)pf->aszID.GetSize(); dwI++) { 
                pszTmp = (char *)pf->aszID[dwI]; 
                delete pszTmp; 
            } 
            // remove the handles 
            delete pf->phFolders; 
 
            delete pf; 
            pf = NULL; 
 
 
            // Restore the previous frame. 
            // =========================== 
            pf = frameStack.pop(); 
            if (pf == NULL) { 
                // Nothing there, return to previous dialogue. 
                EndDialog(hDlg, IDOK); 
                return(TRUE); 
            } 
 
 
            DisplayFolderFrame( hDlg, pf ); 
            // restore folder handle 
            ghFolder = pf->hFolder; 
 
            return(TRUE); 
        } 
    } 
 
    return(FALSE); 
 
}   /* FolderViewDlg */ 
 
 
 
// ==================================================================== 
// 
//  Creates a frame for this folder. 
//  Note: We could check if we have already stacked this folder. 
// 
// ==================================================================== 
 
CFrame *CreateFolderFrame( HWND hDlg, HANDLE hFolder ) 
{ 
    HANDLE hSubFolder = NULL;           // Temp reference to sub-folder. 
    CFrame *pFrame = new CFrame; 
    SMS_STATUS stat; 
 
 
    SmsRewind( hFolder, RW_ALL ); 
 
    // Save the folder's handle. 
    pFrame->hFolder = ghFolder; 
 
    stat = SmsGetFolderID( hFolder, pFrame->szFolderID ); 
    CHKIT(hFolder, "GetFolderID"); 
 
    stat = SmsGetFolderType( hFolder, &pFrame->fType, pFrame->szfType ); 
    CHKIT(hFolder, "GetFolderType"); 
 
    stat = SmsGetScalarCount( hFolder, &pFrame->ctScalars ); 
    CHKIT(hFolder, "GetScalarCount"); 
 
    stat = SmsGetFolderCount( hFolder, F_ANY, &pFrame->ctFolders ); 
    CHKIT(hFolder, "GetFolderCount"); 
 
    stat = SmsEnumFolderTypes( hFolder, NULL, &pFrame->ctFolderTypes ); 
    CHKIT(hFolder, "EnumFolderTypes"); 
 
    // 02-May-96. Add expression display (inventory folder). 
    stat = SmsGetTokenCount( hFolder, &pFrame->ctTokens ); 
//    CHKIT(hFolder, "GetTokenCount"); 
 
 
    // Enumerate the sub-folders. 
    // ----------------------------------------- 
    char szBuff[SMS_DATA_BUFF_SIZE+1]; 
 
    // Allocate a handle for each folder. 
    pFrame->phFolders = (HANDLE *)new HANDLE[pFrame->ctFolders]; 
    char *pszTmp; 
 
    DWORD dwI; 
    for (dwI = 0; dwI < pFrame->ctFolders; dwI++) { 
        // Rewind the folder - just in case. 
 
        stat = SmsGetNextFolder( hFolder, F_ANY, &hSubFolder ); 
        CHKIT(hFolder, "GetNextFolder"); 
        pFrame->phFolders[dwI] = hSubFolder; 
 
        // Get folder's ID and add it to our cstring array and to the listbox. 
        // We want it saved since we will have to redraw the listbox when 
        // drilling down and then up in the folder list. 
        stat = SmsGetFolderID( hSubFolder, szBuff ); 
        CHKIT(hFolder, "GetFolderID"); 
        pszTmp = new char[strlen(szBuff)+1];        // deleted when we do a BACK or CLOSE 
        strcpy(pszTmp, szBuff); 
        pFrame->aszID.Add( (CObject *)pszTmp ); 
 
    } 
 
    return(pFrame); 
 
}  /* CreateFolderFrame */ 
 
 
 
 
// ==================================================================== 
// 
//  Display the given folder frame. This is part of the Folder view 
//  dialogue. 
// 
// ==================================================================== 
 
void DisplayFolderFrame( HWND hDlg, CFrame *pFrame ) 
{ 
    HWND hEdit; 
 
    // First of all lets clear the listbox (whether needs it or not). 
    hEdit = GetDlgItem( hDlg, IDC_SUBFOLDER_LIST ); 
    SendMessage(hEdit, LB_RESETCONTENT, 0, 0); 
 
 
    SetDlgItemText( hDlg, IDC_FOLDERID, (LPCSTR)pFrame->szFolderID); 
    SetDlgItemText( hDlg, IDC_FOLDERTYPE, (LPCSTR)pFrame->szfType); 
    SetDlgItemInt( hDlg,  IDC_FOLDERSC_CT, pFrame->ctScalars, FALSE); 
    SetDlgItemInt( hDlg,  IDC_FOLDERFOLDER_CT, pFrame->ctFolders, FALSE); 
    SetDlgItemInt( hDlg,  IDC_FOLDERFT_CT, pFrame->ctFolderTypes, FALSE); 
 
 
    // Display the sub-folder IDs. 
    // ----------------------------------------- 
    hEdit = GetDlgItem( hDlg, IDC_SUBFOLDER_LIST ); 
    char *pszTmp; 
 
    for (DWORD dwI = 0; dwI < pFrame->ctFolders; dwI++) { 
 
        pszTmp = (char *)pFrame->aszID.GetAt( dwI ); 
        SendMessage(hEdit, LB_ADDSTRING, 0, (LPARAM)pszTmp); 
    } 
 
    // If there are no sub-folders or no scalars then 
    // disable the appropriate buttons. 
    // ----------------------------------------------------- 
    hEdit = GetDlgItem( hDlg, IDC_VIEW_SCALARS ); 
    if (pFrame->ctScalars == 0) { 
        EnableWindow( hEdit, FALSE ); 
    } else { 
        EnableWindow( hEdit, TRUE ); 
    } 
 
    hEdit = GetDlgItem( hDlg, IDC_VIEW_FOLDER ); 
    if (pFrame->ctFolders == 0) { 
        EnableWindow( hEdit, FALSE ); 
    } else { 
        EnableWindow( hEdit, TRUE ); 
    } 
 
 
    // Select the first one in the list and set focus here. 
    hEdit = GetDlgItem( hDlg, IDC_SUBFOLDER_LIST ); 
    SendMessage(hEdit, LB_SETCURSEL, 0, 0); 
 
    // [02-May-96] If there are no tokens in this folder (ie it doesn't 
    // have an expression) then disable the 'View expression' button. 
    // ---------------------------------------------------------------- 
    hEdit = GetDlgItem( hDlg, IDC_VIEW_EXPRESSION ); 
    if (pFrame->ctTokens == 0) { 
        EnableWindow( hEdit, FALSE ); 
    } else { 
        EnableWindow( hEdit, TRUE ); 
    } 
} 
 
 
// ========================================================================== 
// 
//  Dialogue proc for the scalar view. 
// 
//  We need the hFolder, and preferably it's ID as well. We can put that 
//  in the dlg title. 
//  The hFolder is needed since we're going to enumerate its scalars. 
//  We'll construct 2 string arrays, one for the scalar name, the other 
//  for the value (maybe put in type as well later). 
// 
// ========================================================================== 
 
extern "C" BOOL CALLBACK ScalarViewDlg( 
                                    HWND hDlg, 
                                    UINT message, 
                                    WPARAM wParam, 
                                    LPARAM lParam) 
{ 
 
    HWND hEditName;                 // Explicit handle to name listbox. 
    HWND hEditVal;                  // Explicit handle to value listbox. 
    HWND hEditType;                 // Explicit handle to type listbox. 
    int index; 
    SCALAR scalar; 
    char szName[SMS_DATA_BUFF_SIZE+1];      // Scalar name. 
    char szValue[SMS_DATA_BUFF_SIZE+1];     // String value. 
    BYTE byValue[SMS_DATA_BUFF_SIZE];       // Binary value. 
    scalar.pszName  = szName; 
    scalar.pszValue = szValue; 
    scalar.pValue   = byValue; 
    SMS_STATUS stat; 
    char szBuff[SMS_DATA_BUFF_SIZE+1]; 
 
    char *pszVal;                   // Point to scalar's value. 
    char *pszType;                  // Point to scalar's type. 
 
    CTime tTime;                    // For time scalars 
    CString sTime;                  //  ditto 
 
 
    switch (message) { 
 
    case WM_INITDIALOG: 
        // Dialogue initialisation. 
 
        // 1. Set the window title to say "Scalars for folder <name>". 
        SmsGetFolderID( ghFolder, szBuff ); 
        wsprintf(szMsg, "Scalars for folder %s", szBuff); 
        SetWindowText( hDlg, szMsg ); 
 
        // 2. Enumerate the scalars storing the strings (strings only at 
        // the moment) in 2 string lists: name and value. 
 
        hEditName = GetDlgItem(hDlg, IDC_SCNAME_LB); 
        hEditVal  = GetDlgItem(hDlg, IDC_SCVAL_LB); 
        hEditType = GetDlgItem(hDlg, IDC_SCTYPE_LB); 
 
        while (1) { 
            scalar.dwLen = sizeof(szValue)-1; 
 
            stat = SmsGetNextScalar( ghFolder, &scalar ); 
            if (stat != SMS_OK) { 
                break; 
            } 
 
            // Copy the name and value into the listboxes - don't think I'll 
            // bother with saving them though. 
 
 
            switch (scalar.scType) { 
            case SCALAR_STRING: 
                pszVal = scalar.pszValue; 
                pszType = "String"; 
                break; 
 
            case SCALAR_INT: 
                // Convert to string. 
                wsprintf(szBuff, "%d", scalar.dwValue); 
                pszVal = szBuff; 
                pszType = "Integer"; 
                break; 
 
            case SCALAR_TIME: 
                // Check if we have a string equivalence. If so use it. 
                if (scalar.bStringEquivalence) { 
                    pszVal = scalar.pszValue; 
 
                } else { 
                    // Use time functions to make a string out of it. 
                    // NOTE: we really need to get rid of trailing newline. 
                    tTime = scalar.tValue; 
                    sTime = tTime.Format( "%m/%d/%y %I:%M %p" ); 
                    pszVal = (char *)(const char *)sTime; 
                } 
                pszType = "Time"; 
                break; 
 
            case SCALAR_BINARY: 
                // Just print out its length. 
                pszType = "Binary"; 
                wsprintf( szBuff, "Length of binary data: %ld", scalar.dwLen); 
                pszVal = szBuff; 
                break; 
 
            default: 
                wsprintf(szBuff, "UNKNOWN TYPE"); 
                pszVal = szBuff; 
                pszType = "Unknown type"; 
                break; 
            } 
 
            // Scalar name. 
            SendMessage(hEditName, LB_ADDSTRING, 0, (LPARAM)scalar.pszName); 
 
            // Scalar type. 
            SendMessage(hEditType, LB_ADDSTRING, 0, (LPARAM)pszType); 
 
            // Scalar value. 
            SendMessage(hEditVal,  LB_ADDSTRING, 0, (LPARAM)pszVal); 
 
 
        } 
 
        // Set current selections to the first in each list. 
        SendMessage(hEditName, LB_SETCURSEL, (WPARAM)0, 0); 
        SendMessage(hEditType, LB_SETCURSEL, (WPARAM)0, 0); 
        SendMessage(hEditVal,  LB_SETCURSEL, (WPARAM)0, 0); 
 
        // Set focus on this control. 
        SetFocus(hEditName); 
 
        return(TRUE); 
 
 
    case WM_CLOSE: 
        // Do I need to delete the strings from the listboxes?? 
        EndDialog( hDlg, IDCANCEL ); 
        return(TRUE); 
 
    case WM_SYSCOLORCHANGE: 
        Ctl3dColorChange(); 
        return(TRUE); 
 
 
    case WM_COMMAND: 
 
        // Whatever listbox is selected we want to select the 
        // corresponding entry in the other. 
        if (HIWORD(wParam) == LBN_SELCHANGE) { 
 
            index = SendMessage((HWND)lParam, LB_GETCURSEL, 0, 0); 
 
 
            hEditName = GetDlgItem(hDlg, IDC_SCNAME_LB); 
            hEditType = GetDlgItem(hDlg, IDC_SCTYPE_LB); 
            hEditVal  = GetDlgItem(hDlg, IDC_SCVAL_LB); 
 
            // Select the corresponding entry all listboxes. 
            SendMessage(hEditName, LB_SETCURSEL, (WPARAM)index, 0); 
            SendMessage(hEditType, LB_SETCURSEL, (WPARAM)index, 0); 
            SendMessage(hEditVal,  LB_SETCURSEL, (WPARAM)index, 0); 
 
            return(TRUE); 
        } 
 
 
        switch (wParam) { 
 
        case IDOK: 
            // Do I need to delete the strings from the listboxes?? 
            EndDialog(hDlg, IDOK); 
            return(TRUE); 
 
        } 
    } 
 
    return(FALSE); 
 
}  /* ScalarViewDlg */ 
 
 
// ========================================================================== 
// 
//  Dialogue proc for the expression view. 
//##// 
//  We need the hFolder, and preferably it's ID as well. We can put that 
//  in the dlg title. 
//  The hFolder is needed since we're going to enumerate its scalars. 
//  We'll construct 2 string arrays, one for the scalar name, the other 
//  for the value (maybe put in type as well later). 
// 
// ========================================================================== 
 
extern "C" BOOL CALLBACK ExpressionViewDlg( 
                                    HWND hDlg, 
                                    UINT message, 
                                    WPARAM wParam, 
                                    LPARAM lParam) 
{ 
 
    HWND hEditToken;                // Explicit handle to token listbox. 
    char szBuff[SMS_DATA_BUFF_SIZE+1];  // Just for setting window title. 
    DWORD dwLoop = 0; 
 
 
    switch (message) { 
 
    case WM_INITDIALOG: 
        // Dialogue initialisation. 
 
        // 1. Set the window title to say "Expression for folder <name>". 
        SmsGetFolderID( ghFolder, szBuff ); 
        wsprintf(szMsg, "Expression for folder %s", szBuff); 
        SetWindowText( hDlg, szMsg ); 
 
        // 2. Enumerate the tokens storing them (ie their display strings) 
        // into a string list. 
 
        hEditToken = GetDlgItem(hDlg, IDC_TOKENS_LB); 
 
        // Clear the token. 
        // ================ 
        TOKEN Token; 
        DWORD ctTokens; 
 
        // Loop through its tokens, display the token string in the 
        // edit control. 
        // ======================================================== 
        SmsGetTokenCount( ghFolder, &ctTokens ); 
        for (dwLoop = 0; dwLoop < ctTokens; dwLoop++) { 
 
            memset(&Token, 0, sizeof(TOKEN)); 
            Token.bIndent = 1;                  // Request indentation. 
            // Get token. 
            SmsGetToken( ghFolder, dwLoop, &Token ); 
 
            SendMessage(hEditToken, LB_ADDSTRING, 0, (LPARAM)Token.szTokenString); 
        } 
 
 
        // Set focus on this control. 
        SetFocus(hEditToken); 
 
        return(TRUE); 
 
 
    case WM_CLOSE: 
        // Do I need to delete the strings from the listboxes?? 
        EndDialog( hDlg, IDCANCEL ); 
        return(TRUE); 
 
    case WM_SYSCOLORCHANGE: 
        Ctl3dColorChange(); 
        return(TRUE); 
 
 
    case WM_COMMAND: 
 
        switch (wParam) { 
 
        case IDOK: 
            // Do I need to delete the strings from the listboxes?? 
            EndDialog(hDlg, IDOK); 
            return(TRUE); 
 
        } 
#ifdef XXX 
        // Whatever listbox is selected we want to select the 
        // corresponding entry in the other. 
        if (HIWORD(wParam) == LBN_SELCHANGE) { 
 
            index = SendMessage((HWND)lParam, LB_GETCURSEL, 0, 0); 
 
 
            hEditName = GetDlgItem(hDlg, IDC_SCNAME_LB); 
            hEditType = GetDlgItem(hDlg, IDC_SCTYPE_LB); 
            hEditVal  = GetDlgItem(hDlg, IDC_SCVAL_LB); 
 
            // Select the corresponding entry all listboxes. 
            SendMessage(hEditName, LB_SETCURSEL, (WPARAM)index, 0); 
            SendMessage(hEditType, LB_SETCURSEL, (WPARAM)index, 0); 
            SendMessage(hEditVal,  LB_SETCURSEL, (WPARAM)index, 0); 
 
            return(TRUE); 
        } 
#else 
        return(TRUE); 
#endif  // XXX 
 
 
        switch (wParam) { 
 
        case IDOK: 
            // Do I need to delete the strings from the listboxes?? 
            EndDialog(hDlg, IDOK); 
            return(TRUE); 
 
        } 
    } 
 
    return(FALSE); 
 
}  /* ExpressionViewDlg */ 
 
 
 
 
// ==================================================================== 
// 
// This is the dialogue that is brought up in response to the 
// config filter button in the main dialogue. 
// 
//  A MY_FILTER_INFO struct is passed via a global. This 
//  contains a description of the filter template (ie a FILTER_INFO 
//  structure) and the handle of a particular filter. By examining 
//  the filter info we determine how the filter's tokens are composed. 
 
//  The dialogue itself has the group name controls set to not- 
//  visible. If this is a Token2 then we make them visible. 
// 
//  For each token there is a name, a value and an operator. 
//  I'll hard-code the operators for the moment. 
//  Not all tokens need the value and operator, this should be 
//  determined by the token processing. 
//  The nature of the value is also dependent on the token, it's 
//  (currently) either a DWORD or a string. 
// 
//  We create the filter and do the add tokens here. The filter 
//  handle is stored in the MY_FILTER_INFO struct. When we 
//  return the invoker of this dialogue will do a SetFilter. 
// 
// ==================================================================== 
 
extern "C" BOOL CALLBACK ConfigFilterDlg( 
                                    HWND hDlg, 
                                    UINT message, 
                                    WPARAM wParam, 
                                    LPARAM lParam) 
{ 
 
    HWND hEdit; 
    FILTER_INFO *pFltr = NULL; 
    static MY_FILTER_INFO *pMyF; 
    int index; 
    SMS_STATUS stat; 
    static HANDLE hFilter = NULL; 
    ANDOR opAndOr; 
    TOKEN Token;                        // Token to add into filter. 
 
    // We only know what fields the filter supports when we check the 
    // TOKEN_INFO structure. The fields there that contain strings 
    // tell us a) that the field is used by the filter, and b) what 
    // the label should be for that field. 
    // For simplicity we have two dialogues, we use one dialogue proc 
    // to process both. This is only because of the difficulties 
    // involved in moving edit controls around. 
    // Different filter types will use different combinations of the 
    // edit controls. 
    // =============================================================== 
    static BOOL bHasName; 
    static BOOL bHasValue; 
    static BOOL bHasGroupClass; 
    static BOOL bHasArch; 
    static BOOL bHasOp; 
    static BOOL bHasAttribute; 
 
    int iLoop;                          // Loop index. 
 
    switch (message) { 
 
    case WM_INITDIALOG: 
        // Dialogue initialisation. 
        pMyF = gpMyFilter; 
        pFltr = pMyF->pF; 
 
        // Look at the filter to determine what fields it will use. 
        // make the appropriate fields visible, and set their labels. 
        // ========================================================== 
        bHasName       = FALSE; 
        bHasValue      = FALSE; 
        bHasGroupClass = FALSE; 
        bHasArch       = FALSE; 
        bHasOp         = FALSE; 
        bHasAttribute  = FALSE; 
 
        // Clear the token. 
        // ================ 
        memset(&Token, 0, sizeof(TOKEN)); 
 
        // Name. 
        if (strlen(pFltr->szName)) { 
            hEdit = GetDlgItem(hDlg, IDC_NAME_STATIC); 
            SetWindowText( hEdit, pFltr->szName ); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
 
            hEdit = GetDlgItem(hDlg, IDC_NAME); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
            bHasName = TRUE; 
        } 
 
        // Value. 
        if (strlen(pFltr->szValue)) { 
            hEdit = GetDlgItem(hDlg, IDC_VALUE_STATIC); 
            SetWindowText( hEdit, pFltr->szValue ); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
 
            hEdit = GetDlgItem(hDlg, IDC_VALUE); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
            bHasValue = TRUE; 
        } 
 
        // GroupClass. 
        if (strlen(pFltr->szGroupClass)) { 
            hEdit = GetDlgItem(hDlg, IDC_GROUPCLASS_STATIC); 
            SetWindowText( hEdit, pFltr->szGroupClass ); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
 
            hEdit = GetDlgItem(hDlg, IDC_GROUPCLASS); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
            bHasGroupClass = TRUE; 
        } 
 
        // AttributeName. 
        if (strlen(pFltr->szAttributeName)) { 
            hEdit = GetDlgItem(hDlg, IDC_ATTRIBUTE_STATIC); 
            SetWindowText( hEdit, pFltr->szAttributeName ); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
 
            hEdit = GetDlgItem(hDlg, IDC_ATTRIBUTE); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
            bHasAttribute = TRUE; 
        } 
 
        // Architecture. 
        if (strlen(pFltr->szArchitecture)) { 
            hEdit = GetDlgItem(hDlg, IDC_ARCHITECTURE_STATIC); 
            SetWindowText( hEdit, pFltr->szArchitecture ); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
 
            hEdit = GetDlgItem(hDlg, IDC_ARCHITECTURE); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
            bHasArch = TRUE; 
        } 
 
        // Operator. 
        if (strlen(pFltr->szOperator)) { 
            hEdit = GetDlgItem(hDlg, IDC_OPERATOR_STATIC); 
            SetWindowText( hEdit, pFltr->szOperator ); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
 
            hEdit = GetDlgItem(hDlg, IDC_OP_CB); 
            ShowWindow( hEdit, SW_SHOWNORMAL ); 
            bHasOp = TRUE; 
        } 
 
 
 
        // Create the filter. 
        // If all goes ok then this will be stored in 
        // gpMyFilter, and used in the main dlg. 
        // ----------------------------------------------------- 
        stat = SmsCreateFilter( pFltr->filterType, ghConnect, &hFilter ); 
        if (stat != SMS_OK) { 
            wsprintf(szMsg, "OpenFilter fails: %d", stat); 
            MessageBox(hDlg, szMsg, "Whoops", MB_OK); 
        } 
 
 
        // Insert the operators that we support into the combobox. 
        hEdit = GetDlgItem(hDlg, IDC_OP_CB); 
        for (iLoop = 0; iLoop < QOP_LAST; iLoop++) { 
            SendMessage(hEdit, CB_ADDSTRING, 0, (LPARAM)OpName[iLoop]); 
        } 
 
        // Default to selecting string equals. 
        SendMessage(hEdit, CB_SETCURSEL, QOP_STR_EQ, 0); 
 
        // Modify the window text to "Configure <type> filter" 
        // We access the global MyFilter pointer, this points to 
        // the filter type that we're working with. 
        wsprintf(szMsg, "Configuring %s", pFltr->szTag); 
        SetWindowText( hDlg, szMsg ); 
 
        // Disable the "Done" button. 
        hEdit = GetDlgItem(hDlg, IDOK); 
        EnableWindow( hEdit, FALSE ); 
 
        return(TRUE); 
 
 
    case WM_CLOSE: 
EndDialog( hDlg, IDCANCEL ); 
        return(TRUE); 
 
 
    case WM_SYSCOLORCHANGE: 
        Ctl3dColorChange(); 
        return(TRUE); 
 
    case WM_COMMAND: 
 
        switch (wParam) { 
 
        case IDC_ADDAND_BTN: 
        case IDC_ADDOR_BTN: 
            if (wParam == IDC_ADDAND_BTN) { 
                opAndOr = OP_AND; 
            } else { 
                opAndOr = OP_OR; 
            } 
 
            // Extract the fields. We recorded in INITDIALOG which fields 
            // we have. 
                // Name 
            if (bHasName) { 
                GetDlgItemText(hDlg, IDC_NAME, (LPTSTR)Token.szName, sizeof(Token.szName)); 
            } 
 
                // Value (string first) 
            if (bHasValue) { 
                GetDlgItemText(hDlg, IDC_VALUE, (LPTSTR)Token.szValue, sizeof(Token.szValue)); 
            } 
 
                // Operator. Get the index of the selection in the listbox 
                // and convert to operator code. 
            if (bHasOp) { 
                hEdit = GetDlgItem(hDlg, IDC_OP_CB); 
                index = SendMessage(hEdit, CB_GETCURSEL, 0, 0); 
                Token.dwOp = index; 
            } 
 
                // GroupClass 
            if (bHasGroupClass) { 
                GetDlgItemText(hDlg, IDC_GROUPCLASS, (LPTSTR)Token.szGroupClass, sizeof(Token.szGroupClass)); 
            } 
 
                // Attribute name. 
            if (bHasAttribute) { 
                GetDlgItemText(hDlg, IDC_ATTRIBUTE, (LPTSTR)Token.szAttributeName, sizeof(Token.szAttributeName)); 
            } 
 
                // Architecture 
            if (bHasArch) { 
                GetDlgItemText(hDlg, IDC_ARCHITECTURE, (LPTSTR)Token.szArchitecture, sizeof(Token.szArchitecture)); 
            } 
            // Add the token into the filter. 
            // -2 for the index parameter means add the token at the end 
            // of the filter's expression. 
            // --------------------------------------------------------- 
            pMyF = gpMyFilter; 
            pFltr = pMyF->pF; 
 
            stat = SmsAddToken( hFilter, opAndOr, &Token, -2 ); // -2 = at end. 
            if (stat != SMS_OK) { 
               wsprintf(szMsg, "AddToken fails: %d", stat); 
               MessageBox(hDlg, szMsg, "Whoops", MB_OK); 
            } 
 
 
            // Clear the edit controls. 
            // ------------------------ 
            SetDlgItemText( hDlg, IDC_NAME        , "" ); 
            SetDlgItemText( hDlg, IDC_VALUE       , "" ); 
            SetDlgItemText( hDlg, IDC_GROUPCLASS  , "" ); 
            SetDlgItemText( hDlg, IDC_ATTRIBUTE   , "" ); 
            SetDlgItemText( hDlg, IDC_ARCHITECTURE, "" ); 
 
 
            // Enable the "Done" button. 
            // ------------------------- 
            hEdit = GetDlgItem(hDlg, IDOK); 
            EnableWindow( hEdit, TRUE ); 
 
            return(TRUE); 
 
        case IDOK: 
            // We need to set the filter handle in the MYFILTER. 
            pMyF = gpMyFilter; 
            pMyF->hFilter = hFilter; 
            hFilter = NULL; 
 
            EndDialog(hDlg, IDOK); 
            return(TRUE); 
 
 
        } 
    } 
 
    return(FALSE); 
 
}  /* SelectFilterDlg */ 
 
 
 
// ==================================================================== 
// 
// This is the dialogue that is brought up in response to the 
// view selected filters button in the main dialogue. 
// It enables the user to view the filters that they have 
// created and applied to the container. It shows the use of the 
// SmsRetrieveFilters and SmsGetToken APIs. 
// 
// ==================================================================== 
 
extern "C" BOOL CALLBACK ViewSelFiltersDlg( 
                                    HWND hDlg, 
                                    UINT message, 
                                    WPARAM wParam, 
                                    LPARAM lParam) 
{ 
 
    HWND hEdit, hEdit1; 
    SMS_STATUS stat; 
    static DWORD ctFilters = 0;     // How many filters there are in the 
                                    // container. 
    DWORD ctTokens;                 // How many tokens in filter. 
    static HANDLE *ahFilters;       // Retrieve an array of handles. 
    DWORD dwLoop;                   // Loop index. 
    TOKEN Token; 
    int index;                      // Get index of selected filter. 
    char szBuff[256];               // Build up filter tokens here. 
 
 
 
 
    switch (message) { 
 
    case WM_INITDIALOG: 
        // Dialogue initialisation. 
 
        // Get the number of filters, then allocate spae and call again. 
        // ============================================================= 
        stat = SmsGetAllFilters( ghContainer, NULL, &ctFilters ); 
 
        ahFilters = new HANDLE[ctFilters]; 
        stat = SmsGetAllFilters( ghContainer, ahFilters, &ctFilters ); 
 
        // Build up the list of filters. We want to populate the listbox 
        // with the filter tags. When one of these is selected (via the 
        // view button) we'll display its contents in the edit control box. 
 
            // Populate the listbox. 
        hEdit = GetDlgItem(hDlg, IDC_SEL_FILTERS_LB); 
 
        HANDLE hFilter; 
        DWORD filterType;               // Current filter's type and tag 
        char szFilterTag[50];           // ... 
 
        for (dwLoop = 0; dwLoop < ctFilters; dwLoop++) { 
            hFilter = ahFilters[dwLoop]; 
            SmsGetFilterType( hFilter, &filterType, szFilterTag ); 
 
            SendMessage(hEdit, LB_ADDSTRING, 0, (LPARAM)szFilterTag); 
        } 
 
            // Select the first one in the list. 
        SendMessage(hEdit, LB_SETCURSEL, 0, 0); 
 
            // Send the listbox a focus message. This should cause him 
            // to display the contents of the specified filter. 
        if (ctFilters > 0) { 
            WPARAM wp = MAKEWPARAM( IDC_SEL_FILTERS_LB, LBN_SELCHANGE ); 
            SendMessage(hDlg, WM_COMMAND, wp, (LPARAM)hEdit); 
        } 
        return(TRUE); 
 
 
    case WM_CLOSE: 
        // Close all the filters that we retrieved in SmsGetAllFilters. 
        for (dwLoop = 0; dwLoop < ctFilters; dwLoop++) { 
            hFilter = ahFilters[dwLoop]; 
            SmsCloseFilter( hFilter ); 
        } 
        delete ahFilters; 
 
        EndDialog( hDlg, IDCANCEL ); 
        return(TRUE); 
 
 
    case WM_SYSCOLORCHANGE: 
        Ctl3dColorChange(); 
        return(TRUE); 
 
    case WM_COMMAND: 
 
        if ((HIWORD(wParam) == LBN_SELCHANGE) && 
            (LOWORD(wParam) == IDC_SEL_FILTERS_LB)) { 
 
            hEdit1 = (HWND)lParam; 
 
            // Clear the contents of the view listbox. 
            // ======================================= 
            szBuff[0] = '\0'; 
            hEdit = GetDlgItem(hDlg, IDC_VIEWFILTER); 
            SendMessage(hEdit, WM_SETTEXT, 0, (LPARAM)""); 
 
            // Find out which filter we've selected and get it's handle. 
            // ========================================================= 
            index = SendMessage(hEdit1, LB_GETCURSEL, 0, 0); 
            hFilter = ahFilters[index]; 
 
            // Clear the token. 
            // ================ 
            memset(&Token, 0, sizeof(TOKEN)); 
 
            // Loop through its tokens, display the token string in the 
            // edit control. 
            // ======================================================== 
            SmsGetTokenCount( hFilter, &ctTokens ); 
            for (dwLoop = 0; dwLoop < ctTokens; dwLoop++) { 
                // Get token. 
                SmsGetToken( hFilter, dwLoop, &Token ); 
 
                strcat( szBuff, Token.szTokenString ); 
                strcat( szBuff, " "); 
            } 
            SendMessage(hEdit, WM_SETTEXT, 0, (LPARAM)szBuff); 
 
            return(TRUE); 
        } 
 
        switch (wParam) { 
 
 
        case IDOK: 
            EndDialog(hDlg, IDOK); 
            return(TRUE); 
 
 
        } 
    } 
 
    return(FALSE); 
 
}  /* ViewSelFiltersDlg */ 
 
 
 
 
 
// ==================================================================== 
// 
//  CFrameStack class. 
// 
//  This is only used in the folder view dialogue. It enables us to 
//  descend into sub-folders but keep this current folder's data 
//  around. 
//  When we want to look at a sub-folder we just pop the current 
//  folder's data onto the stack. when we return from a sub-folder we 
//  just pop the stack. 
// 
// ==================================================================== 
CFrameStack::CFrameStack(HWND hDlg) 
{ 
    sp = 0; 
    _hDlg = hDlg; 
} 
 
CFrameStack::~CFrameStack() 
{ 
    // Clear all objects from stack and delete them. 
    CFrame *pFrame; 
    for (int i = 0; i < stack.GetSize(); i++) { 
        pFrame = (CFrame *)stack[i]; 
        delete pFrame; 
    } 
    stack.RemoveAll(); 
} 
 
 
void CFrameStack::push( CFrame *pFrame ) 
{ 
    stack.Add( (CObject *)pFrame ); 
    sp++; 
 
} 
 
 
CFrame *CFrameStack::pop() 
{ 
    if (sp == 0) { 
        return(NULL); 
    } 
    --sp; 
    CFrame *pFrame = (CFrame *)stack.GetAt( sp ); 
    stack.RemoveAt( sp ); 
 
    return(pFrame); 
} 
 
 
//Debug: dump the stack 
void CFrameStack::Dump( const char *psz ) 
{ 
    CFrame *pf; 
    char *p = szMsg; 
 
    wsprintf(p, "sp=%d GetUpperBound=%d\n", sp-1, stack.GetUpperBound()); 
    p += strlen(p); 
 
    for (int i = 0; i <= sp-1; i++) { 
        pf = (CFrame *)stack[i]; 
        wsprintf(p, "%d ID: %s\n", i, pf->szFolderID); 
        p += strlen(p); 
    } 
    MessageBox( _hDlg, szMsg, psz, MB_OK ); 
} 
 
 
 
#ifdef DEBUG 
 
// ==================================================================== 
// 
//  Debug log utility. 
//  Here so people can see an example of such a thing. 
// 
// eg: Log("WM_TIMER GetWindowRect fails: %d", GetLastError()); 
// 
// ==================================================================== 
int Log(char *fmt, ...) 
{ 
    char *buffer = new char[2048]; 
 
    va_list argptr; 
    int cnt; 
    va_start(argptr, fmt); 
    cnt = vsprintf(buffer, fmt, argptr); 
    va_end(argptr); 
 
    FILE *fp; 
 
    fp = fopen("browser.log", "at"); 
 
    if (!fp) { 
        delete buffer; 
        return( 0 ); 
    } 
 
    time_t now; 
    time(&now); 
    struct tm *local = localtime(&now); 
 
    fprintf(fp, "%s\n", buffer); 
 
    BOOL killfile = FALSE; 
 
    if (ftell(fp) > 0x10000) { 
        killfile = TRUE;   // 64K Limit on log 
    } 
 
    fclose(fp); 
 
    if (killfile) { 
        remove("log.log"); 
    } 
 
    delete buffer; 
 
    return( cnt ); 
} 
#endif  // DEBUG 
 
 
/* EOF: browser.cpp */