home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / odbc / crsrdemo / child.c next >
Encoding:
C/C++ Source or Header  |  1996-09-27  |  82.6 KB  |  2,727 lines

  1. /*--------------------------------------------------------------------------
  2.   Child.C --- Cursors child window procedure
  3.  
  4.   Description:
  5.    This sample is spread across four files, each named for the role
  6.    the contained functions play.  Each file header contains a brief
  7.    description of its purpose and the routines it contains.
  8.  
  9.    CHILD.C contains those routines which maintain a child window.
  10.    These include most interfaces with ODBC including all data
  11.    retrieval and display.  These functions are:
  12.  
  13.      AllocChild      - Allocate and prepare child window memory
  14.      AllocClipRgn    - Allocate clip region for painting
  15.      Cancel          - Cancel SQL request
  16.      CancelSQL       - Cancel asynchronous SQL request
  17.      ChildProc       - Process child window messages
  18.      CvtSqlToCType   - Return the default ODBC C type for a SQL type
  19.      DeleteRow       - Build and issue a positioned delete
  20.      DoChildMenu     - Process a menu request
  21.      DoSQL           - Issue SQL statement and prepare all required
  22.                        variables necessary for displaying the data
  23.      Fetch           - Retrieve one row set
  24.      FreeStmt        - Issue ODBC SQLFreeStmt (and adjust child memory)
  25.      GetCurrentValue - Retrieve (in character format) column value
  26.                        from current row (used by DIALOGS.C)
  27.      GetData         - Issue a SQLGetData request
  28.      GetTableName    - Extract table name from SQL
  29.      IsUpdateable    - Check whether a column can be updated
  30.      OnDataRow       - Determine if point is over displayed row of data
  31.      PaintChild      - Paint child window
  32.      ParamValid      - Validate the max column width of lpChild
  33.      SetCurrentValue - Set a column value in the current row
  34.                        (used by DIALOGS.C)
  35.      SetPos          - Set current position in row set
  36.      SetScroll       - Set scroll bar states and ranges
  37.      SizeScroll      - Size and position scroll bars
  38.      UpdateRow       - Build and issue a positioned update request
  39.  
  40.    This code is furnished on an as-is basis as part of the ODBC SDK and is
  41.    intended for example purposes only.
  42.  
  43. --------------------------------------------------------------------------*/
  44.  
  45. /* Includes --------------------------------------------------------------*/
  46. #include "headers.h"
  47.  
  48. #pragma warning(disable:4001)
  49. #include   "resource.h"
  50. #include   "crsrdemo.h"
  51.  
  52.  
  53. // Constants ---------------------------------------------------------------
  54. #define fDISABLED (MF_BYCOMMAND | MF_DISABLED | MF_GRAYED)
  55. #define fENABLED  (MF_BYCOMMAND | MF_ENABLED)
  56. #define NULLIFEMPTY(x) (*x?x:NULL)
  57.  
  58. const char szDELETE[]       = "DELETE FROM ";
  59. const char szFROM[]         = "FROM";
  60. const char szUPDATE[]       = "UPDATE ";
  61. const char szWHERE[]        = " WHERE CURRENT OF ";
  62. const char szNoDataTitle[]  = "No data to display";
  63. const char szNoData[]       = "The query didn't return any data";
  64. const char szSET[]          = " SET ";
  65. const char szDataAffected[] = "%ld rows were affected";
  66. const char szRowAffected[]  = "%ld row was affected";
  67.  
  68. const int   cMAXCOLS   = 15;
  69.  
  70. #define Async(x)     lpChild->fCanceled = FALSE;               \
  71.                      while ((rc = (x)) == SQL_STILL_EXECUTING) \
  72.                      CancelSQL(lpChild);
  73.  
  74. #define STMTError(x) ODBCError(lpChild->hwnd, SQL_HANDLE_STMT, lpChild->hstmt, (x))
  75.  
  76.  
  77. // Prototypes --------------------------------------------------------------
  78. LPCHILD INTFUNC AllocChild(HWND);
  79. void    INTFUNC AllocClipRgn(LPCHILD);
  80. void    INTFUNC Cancel(LPCHILD);
  81. void    INTFUNC CancelSQL(LPCHILD);
  82. SWORD   INTFUNC CvtSqlToCType(SWORD);
  83. void    INTFUNC DeleteRow(LPCHILD);
  84. BOOL    INTFUNC DoChildMenu(LPCHILD, WPARAM, LPARAM);
  85. void    INTFUNC DoSQL(LPCHILD);
  86. void    INTFUNC Fetch(LPCHILD);
  87. void    INTFUNC FreeStmt(UWORD, LPCHILD);
  88. void    INTFUNC GetData(LPCHILD);
  89. void    INTFUNC GetTableName(LPSTR, LPCSTR);
  90. int     INTFUNC OnDataRow(LPCHILD, LPARAM);
  91. void    INTFUNC PaintChild(LPCHILD, HDC, BOOL, BOOL, BOOL);
  92. BOOL    INTFUNC  ParamValid(LPCHILD);
  93. void    INTFUNC SetPos(LPCHILD, UWORD);
  94. void    INTFUNC SetScroll(LPCHILD);
  95. void    INTFUNC SizeScroll(LPCHILD);
  96. void    INTFUNC UpdateRow(LPCHILD);
  97. #ifdef THREAD
  98. void    INTFUNC DeleteRowThread(LPCHILD);
  99. void    INTFUNC DoSQLThread(LPCHILD);
  100. void    INTFUNC FetchThread(LPCHILD);
  101. void    INTFUNC GetDataThread(LPCHILD);
  102. void    INTFUNC UpdateRowThread(LPCHILD);
  103. #endif
  104.  
  105. /* AllocChild --------------------------------------------------------------
  106.    Description: Allocate and initialize child variables
  107.    --------------------------------------------------------------------------*/
  108. LPCHILD INTFUNC AllocChild(HWND hwnd)
  109. {
  110.    SQLHSTMT hstmt;
  111.    LPCHILD  lpChild;
  112.    char  sz[cbSTRLEN];
  113.  
  114.    // Allocate ODBC SQLHSTMT and set cursor name
  115.    if (DBCError(hwnd, SQLAllocHandle(SQL_HANDLE_STMT,g_hdbc, &hstmt)))
  116.       return NULL;
  117.  
  118. #ifdef THREAD
  119.    wsprintf(sz, szTITLEFMT, (LPSTR)g_szDSN, g_cCursor, GetCurrentThreadId());
  120. #else
  121.    wsprintf(sz, szTITLEFMT, (LPSTR)g_szDSN, g_cCursor);
  122. #endif
  123.  
  124.    SetWindowText(hwnd, sz);
  125.  
  126.    wsprintf(sz, szCURSORNAME, g_cCursor);
  127.    if (ODBCError(hwnd, SQL_HANDLE_STMT, hstmt,
  128.                  SQLSetCursorName(hstmt, (UCHAR FAR *)sz, SQL_NTS)))
  129.       return NULL;
  130.  
  131.    // Allocate child window structure and initialize
  132.    lpChild = (LPCHILD)AllocPtr(sizeof(CHILD));
  133.  
  134.    lpChild->hwnd             = hwnd;
  135.    lpChild->fInSetScroll     = FALSE;
  136.    lpChild->fIsMinimized     = FALSE;
  137.    lpChild->fHaveMouse       = FALSE;
  138.    lpChild->iMouseRow        = -1;
  139.    lpChild->fNoConcurrency   = FALSE;
  140.    lpChild->fNoCursorType    = FALSE;
  141.  
  142.    lpChild->ccols            = 0;
  143.    lpChild->crowwin          = 0;
  144.    lpChild->ccolwin          = 0;
  145.    lpChild->fVScroll         =
  146.       lpChild->fHScroll         = FALSE;
  147.    lpChild->lpsz             = AllocPtr(cbBUFSIZE);
  148.  
  149.    lpChild->hrgn             = NULL;
  150.  
  151.    lpChild->hstmt            = hstmt;
  152.    lpChild->hstmtTmp         = SQL_NULL_HSTMT;
  153.  
  154.    lpChild->fBindByRow       = IDC_RADIO_BINDROW;
  155.    lpChild->fConcurrency     = SQL_CONCUR_VALUES;
  156.    lpChild->crowKeyset       = SQL_CURSOR_STATIC;
  157.    lpChild->crowRowset       = 10;
  158.    lpChild->fAsync           = FALSE;
  159.    lpChild->irowPos          = 0;
  160.    lpChild->irow             = 0;
  161.    lpChild->cBind            = 0;
  162.    lpChild->fBindAll         = TRUE;
  163.    lpChild->ccolRetrieved    = 0;
  164.  
  165.    lpChild->arow             = 1;
  166.    lpChild->rrow             = 10;
  167.    lpChild->ccol             = 0;
  168.    lpChild->lpnTabs          = NULL;
  169.    lpChild->lpcol            = NULL;
  170.    lpChild->lpfStatus        = NULL;
  171.    lpChild->fResultSetExists = FALSE;
  172.    lpChild->fDataFetched     = FALSE;
  173.    lpChild->rglpv            = NULL;
  174.    lpChild->crowMaxBind      = DEF_MAXBIND;
  175.  
  176.    lpChild->lpb              = NULL;
  177.    lpChild->sql              = AllocPtr(cbMAXSQL);
  178.    lpChild->cbrow            = 0;
  179.    lpChild->dwGuiFlags       = GUIF_ALWAYSFETCH;
  180. #ifdef THREAD
  181.    InitializeCriticalSection (&lpChild->ThreadCreation);
  182. #endif
  183.  
  184.    // Create scroll bars
  185.    lpChild->hwndVScroll = CreateWindow(szSCROLLCLASS, NULL,
  186.                                        WS_CHILD | SBS_VERT,
  187.                                        0, 0, 0, 0,
  188.                                        hwnd, (HMENU)1, g_hinst, NULL);
  189.  
  190.    lpChild->hwndHScroll = CreateWindow(szSCROLLCLASS, NULL,
  191.                                        WS_CHILD | SBS_HORZ,
  192.                                        0, 0, 0, 0,
  193.                                        hwnd, (HMENU)2, g_hinst, NULL);
  194.  
  195.    // Load default SQL string
  196.    LoadString(g_hinst, IDS_SQL, sz, cbSTRLEN);
  197.    wsprintf(lpChild->sql, sz, g_szTable);
  198.  
  199.    return lpChild;
  200. }
  201.  
  202.  
  203. /* AllocClipRgn ------------------------------------------------------------
  204.    Description: Allocate child window clip region
  205.    --------------------------------------------------------------------------*/
  206. void INTFUNC AllocClipRgn(LPCHILD lpChild)
  207. {
  208.    RECT  rc;
  209.  
  210.    // Determine client window size less space for scroll bars
  211.    GetClientRect(lpChild->hwnd, &rc);
  212.  
  213.    if (lpChild->hrgn) DeleteObject(lpChild->hrgn);
  214.  
  215.    if (lpChild->fVScroll)
  216.       rc.right -= g_cxVScroll - 1;
  217.    rc.bottom -= g_cyHScroll - 1;
  218.  
  219.    // Allocate clip region
  220.    lpChild->hrgn = CreateRectRgn(rc.left,
  221.                                  rc.top,
  222.                                  rc.right,
  223.                                  rc.bottom);
  224.    return;
  225. }
  226.  
  227.  
  228. /* CancelSQL ---------------------------------------------------------------
  229.    Description: Display message while an async request is executing and
  230.                 give the user a chance to cancel the request (if it has
  231.                 not already been canceled)
  232.                 --------------------------------------------------------------------------*/
  233. void INTFUNC CancelSQL(LPCHILD lpChild)
  234. {
  235.    char  sz[cbSTRLEN];
  236.    int   rc;
  237.  
  238.    // Display message
  239.    LoadString(g_hinst, IDS_STILLEXEC, sz, sizeof(sz));
  240.    rc = MessageBox(lpChild->hwnd,
  241.                    sz, g_szTITLE,
  242.                    MB_ICONINFORMATION |
  243.                    (lpChild->fCanceled
  244.                     ? MB_OK
  245.                     : MB_OKCANCEL | MB_DEFBUTTON1));
  246.  
  247.    // If the user requested, cancel the current request
  248.    if (rc == IDCANCEL)
  249.       lpChild->fCanceled = SUCCESS(SQLCancel(lpChild->hstmt));
  250.  
  251.    return;
  252. }
  253.  
  254.  
  255. /* ChildProc ---------------------------------------------------------------
  256.    Description: Child window procedure
  257.    --------------------------------------------------------------------------*/
  258. LRESULT EXPFUNC ChildProc(HWND    hwnd,
  259.                           UINT   msg,
  260.                           WPARAM wparam,
  261.                           LPARAM lparam)
  262. {
  263.    LPCHILD  lpChild;
  264.  
  265.    // Get access to child variables and set current window handle
  266.    lpChild = (LPCHILD)GetWindowLong(hwnd, 0);
  267.  
  268.    switch (msg) {
  269.  
  270.       // Allocate child variables and save pointer
  271.      case WM_CREATE:
  272.       lpChild = AllocChild(hwnd);
  273.  
  274.       SetWindowLong(hwnd, 0, (LONG)lpChild);
  275.  
  276.       if (!lpChild)
  277.          return -1;
  278.       break;
  279.  
  280.       // Paint child window (active or inactive)
  281.      case WM_PAINT: {
  282.         PAINTSTRUCT ps;
  283.         BOOL     fActive;
  284.  
  285.         fActive = (hwnd ==
  286.                    FORWARD_WM_MDIGETACTIVE(g_hwndClient, SendMessage));
  287.  
  288.         BeginPaint(hwnd, &ps);
  289.         PaintChild(lpChild, ps.hdc, TRUE, FALSE, fActive);
  290.         EndPaint(hwnd, &ps);
  291.         break;
  292.      }
  293.  
  294.       // Trap mouse if over a rowset row
  295.      case WM_LBUTTONDOWN:
  296.       lpChild->iMouseRow = OnDataRow(lpChild, lparam);
  297.       if (lpChild->iMouseRow >= 0) {
  298.          lpChild->fHaveMouse = TRUE;
  299.          SetCapture(hwnd);
  300.       }
  301.       break;
  302.  
  303.       // Make row current row (if mouse is still on the row)
  304.      case WM_LBUTTONUP:
  305.       if (!lpChild->fHaveMouse)
  306.          break;
  307.  
  308.       ReleaseCapture();
  309.       lpChild->fHaveMouse = FALSE;
  310.  
  311.       if (lpChild->fDataFetched                         &&
  312.           lpChild->fConcurrency != SQL_CONCUR_READ_ONLY &&
  313.           lpChild->crowKeyset != SQL_CURSOR_FORWARD_ONLY &&
  314.           lpChild->iMouseRow    == OnDataRow(lpChild, lparam)) {
  315.          RECT  rc;
  316.          int   y;
  317.  
  318.          GetClientRect(hwnd, &rc);
  319.  
  320.          y = (int)HIWORD(lparam) - rc.top - g_cy;
  321.  
  322.          SetPos(lpChild,
  323.                 (UWORD)(GetScrollPos(lpChild->hwndVScroll, SB_CTL) + (y / g_cy) + 1));
  324.       }
  325.       break;
  326.  
  327.       // Convert keyboard requests to scroll and change window requests
  328.      case WM_KEYDOWN:
  329.       if (wparam == VK_TAB) {
  330.          FORWARD_WM_MDINEXT(g_hwndClient, hwnd,
  331.                             (GetKeyState(VK_BACK) & 0x1000 ? TRUE :FALSE),
  332.                             SendMessage);
  333.          break;
  334.       }
  335.  
  336.       else if (wparam == VK_DOWN || wparam == VK_UP) {
  337.  
  338.          msg    = WM_VSCROLL;
  339.          GET_WM_VSCROLL_CODE(wparam, lparam) =
  340.             (wparam == VK_DOWN
  341.              ? SB_LINEDOWN
  342.              : SB_LINEUP);
  343.       }
  344.       else if (wparam == VK_LEFT || wparam == VK_RIGHT) {
  345.          if (!lpChild->fHScroll)
  346.             break;
  347.  
  348.          msg    = WM_HSCROLL;
  349.          GET_WM_HSCROLL_CODE(wparam, lparam) =
  350.             (wparam == VK_RIGHT
  351.              ? SB_LINEDOWN
  352.              : SB_LINEUP);
  353.       }
  354.       else
  355.          break;
  356.  
  357.       // Scroll window
  358.      case WM_HSCROLL:
  359.      case WM_VSCROLL: {
  360.         HWND  hwndCtl;
  361.         int   cInc;
  362.         int   iPos;
  363.         int   cPage;
  364.         int   nPos;
  365.         int   iOrig;
  366.         int   nMin, nMax;
  367.  
  368.         if (!lpChild->fDataFetched)
  369.            break;
  370.  
  371.         // Determine scroll direction and distance
  372.         hwndCtl = (msg == WM_HSCROLL
  373.                    ? lpChild->hwndHScroll
  374.                    : lpChild->hwndVScroll);
  375.         cInc    = (msg == WM_HSCROLL ? 1 : 1);
  376.         cPage   = (msg == WM_HSCROLL
  377.                    ? lpChild->ccolwin
  378.                    : lpChild->crowwin - 1);
  379.         nPos    = GET_WM_HSCROLL_POS(wparam, lparam);
  380.         iPos    =
  381.            iOrig   = GetScrollPos(hwndCtl, SB_CTL);
  382.  
  383.         GetScrollRange(hwndCtl, SB_CTL, &nMin, &nMax);
  384.         switch (GET_WM_HSCROLL_CODE(wparam, lparam)) {
  385.           case SB_BOTTOM:        iPos = nMax;  break;
  386.           case SB_LINEDOWN:      iPos+= cInc;  break;
  387.           case SB_LINEUP:        iPos-= cInc;  break;
  388.           case SB_PAGEDOWN:      iPos+= cPage; break;
  389.           case SB_PAGEUP:        iPos-= cPage; break;
  390.           case SB_TOP:           iPos = nMin;  break;
  391.           case SB_THUMBPOSITION: iPos = nPos;  break;
  392.         }
  393.  
  394.         // For updateable cursors, vertical scroll requests move the
  395.         // current row scroll the window only as needed to keep the
  396.         // current row visible
  397.         if (msg == WM_VSCROLL &&
  398.             lpChild->crowKeyset != SQL_CURSOR_FORWARD_ONLY &&
  399.             lpChild->fConcurrency != SQL_CONCUR_READ_ONLY) {
  400.            int   delta;
  401.  
  402.            if (GET_WM_HSCROLL_CODE(wparam, lparam) == SB_LINEDOWN)
  403.               delta = cInc;
  404.            else if (GET_WM_HSCROLL_CODE(wparam, lparam) == SB_LINEUP)
  405.               delta = -cInc;
  406.            else if (GET_WM_HSCROLL_CODE(wparam, lparam) == SB_PAGEDOWN) {
  407.               if (iPos <= nMax)
  408.                  delta = cPage;
  409.               else
  410.                  delta = lpChild->crowRowset - lpChild->irowPos;
  411.            }
  412.            else if (GET_WM_HSCROLL_CODE(wparam, lparam) == SB_PAGEUP) {
  413.               if (iPos >= nMin)
  414.                  delta = -cPage;
  415.               else
  416.                  delta = 1 - lpChild->irowPos;
  417.            }
  418.            else if (GET_WM_HSCROLL_CODE(wparam, lparam) == SB_BOTTOM)
  419.               delta = lpChild->crowRowset - lpChild->irowPos;
  420.            else if (GET_WM_HSCROLL_CODE(wparam, lparam) == SB_TOP)
  421.               delta = 1 - lpChild->irowPos;
  422.            else if (GET_WM_HSCROLL_CODE(wparam, lparam) == SB_THUMBPOSITION) {
  423.               if (lpChild->irowPos > (UWORD)iPos &&
  424.                   lpChild->irowPos < (UWORD)(iPos+lpChild->crowwin))
  425.                  delta = 0;
  426.               else if (iPos == nMin)
  427.                  delta = 1 - lpChild->irowPos;
  428.               else if (iPos == nMax)
  429.                  delta = lpChild->crowRowset - lpChild->irowPos;
  430.               else if (iPos <= iOrig)
  431.                  delta = iPos + lpChild->crowwin - 1 - lpChild->irowPos;
  432.               else
  433.                  delta = iPos - lpChild->irowPos + 1;
  434.            }
  435.            else
  436.               break;
  437.  
  438.            SetPos(lpChild, (UWORD)(lpChild->irowPos + delta));
  439.  
  440.            if ((GET_WM_HSCROLL_CODE(wparam, lparam) == SB_LINEDOWN ||
  441.                 GET_WM_HSCROLL_CODE(wparam, lparam) == SB_LINEUP) &&
  442.                lpChild->irowPos > (UWORD)iOrig                &&
  443.                lpChild->irowPos < (UWORD)(iOrig+lpChild->crowwin))
  444.               break;
  445.         }
  446.  
  447.         // Pin scroll requests within scroll boundaries
  448.         if (iPos < nMin)
  449.            iPos = nMin;
  450.         else if (iPos > nMax)
  451.            iPos = nMax;
  452.  
  453.         // Scroll the window if movement has occurred
  454.         if (iPos != iOrig) {
  455.            HDC      hdc;
  456.            BOOL  fTitle;
  457.  
  458.            hdc = GetDC(hwnd);
  459.  
  460.            SetScrollPos(hwndCtl, SB_CTL, iPos, TRUE);
  461.  
  462.            fTitle = (msg == WM_HSCROLL);
  463.  
  464.            PaintChild(lpChild, hdc, fTitle, FALSE, TRUE);
  465.  
  466.            ReleaseDC(hwnd, hdc);
  467.         }
  468.         break;
  469.      }
  470.  
  471.       // Activate the child window
  472.      case WM_MDIACTIVATE: {
  473.         HDC   hdc;
  474.  
  475.         AdjustMenus();
  476.  
  477.         hdc = GetDC(hwnd);
  478.  
  479.         PaintChild(lpChild, hdc, TRUE, TRUE,
  480.                    GET_WM_MDIACTIVATE_FACTIVATE(lpChild->hwnd, wparam, lparam));
  481.  
  482.         ReleaseDC(lpChild->hwnd, hdc);
  483.         break;
  484.      }
  485.  
  486.       // Free all child memory
  487.      case WM_DESTROY:
  488.       if (lpChild) {
  489.          FreeStmt(SQL_DROP, lpChild);
  490.  
  491.          FreePtr(lpChild->lpsz);
  492.          FreePtr(lpChild->sql);
  493. #ifdef THREAD
  494.          DeleteCriticalSection (&lpChild->ThreadCreation);
  495. #endif
  496.          FreePtr(lpChild);
  497.  
  498.          SetWindowLong(hwnd, 0, 0L);
  499.       }
  500.       break;
  501.  
  502.       // Close the window
  503.      case WM_CLOSE:
  504.       g_cChild--;
  505.  
  506.       if( !g_cChild )
  507. #ifdef WIN32
  508.          SendMessage(g_hwndClient, WM_MDISETMENU,
  509.                      (WPARAM)g_hmenuFrame,
  510.                      (LPARAM)g_hmenuFrameWindow);
  511. #else
  512.       FORWARD_WM_MDISETMENU(g_hwndClient, 0, g_hmenuFrame,
  513.                             g_hmenuFrameWindow, SendMessage);
  514. #endif
  515.  
  516.       // Destroy child window
  517.       FORWARD_WM_MDIDESTROY(g_hwndClient, hwnd, SendMessage);
  518.       AdjustMenus();
  519.       break;
  520.  
  521.       // Pass all other messages (eventually) to the MDI window procedure
  522.      default:
  523.  
  524.       // Reset scroll bars (if needed) when the window is resized
  525.       if (msg == WM_SIZE) {
  526.  
  527.          if (wparam == SIZE_MINIMIZED)
  528.             lpChild->fIsMinimized = TRUE;
  529.  
  530.          else {
  531.             if (lpChild->fIsMinimized)
  532.                lpChild->fIsMinimized = FALSE;
  533.  
  534.             SizeScroll(lpChild);
  535.  
  536.             if (lpChild->fDataFetched) {
  537.                int   row;
  538.  
  539.                SetScroll(lpChild);
  540.  
  541.                row = GetScrollPos(lpChild->hwndVScroll, SB_CTL);
  542.  
  543.                if (lpChild->fConcurrency != SQL_CONCUR_READ_ONLY &&
  544.                    lpChild->irowPos >= (UWORD)(row+lpChild->crowwin))
  545.                   if (lpChild->crowwin > 1)
  546.                      SetScrollPos(lpChild->hwndVScroll,
  547.                                   SB_CTL,
  548.                                   lpChild->irowPos-lpChild->crowwin+1,
  549.                                   TRUE);
  550.                   else
  551.                      SetScrollPos(lpChild->hwndVScroll,
  552.                                   SB_CTL,
  553.                                   lpChild->irowPos-lpChild->crowwin,
  554.                                   TRUE);
  555.             }
  556.  
  557.             AllocClipRgn(lpChild);
  558.          }
  559.  
  560.          InvalidateRect(hwnd, NULL, TRUE);
  561.       }
  562.  
  563.       // Handle child window menu requests
  564.       else if (msg == WM_COMMAND)
  565.          DoChildMenu(lpChild, wparam, lparam);
  566.  
  567.       // Pass message on to the MDI window procedure
  568.       return DefMDIChildProc(hwnd, msg, wparam, lparam);
  569.    }
  570.  
  571.    return (LRESULT)NULL;
  572. }
  573.  
  574.  
  575. /* CvtSqlToCType -----------------------------------------------------------
  576.    Description: Determine the default ODBC C type for a given SQL type
  577.    --------------------------------------------------------------------------*/
  578. SWORD INTFUNC CvtSqlToCType(SWORD fSqlType)
  579. {
  580.    switch (fSqlType) {
  581.      case SQL_CHAR:
  582.      case SQL_VARCHAR:
  583.      case SQL_LONGVARCHAR:
  584.      case SQL_DECIMAL:
  585.      case SQL_NUMERIC:
  586.      case SQL_BIGINT:           return SQL_C_CHAR;
  587.  
  588.      case SQL_BIT:              return SQL_C_BIT;
  589.      case SQL_TINYINT:
  590.      case SQL_SMALLINT:         return SQL_C_SHORT;
  591.      case SQL_INTEGER:          return SQL_C_LONG;
  592.      case SQL_REAL:             return SQL_C_FLOAT;
  593.  
  594.      case SQL_FLOAT:
  595.      case SQL_DOUBLE:           return SQL_C_DOUBLE;
  596.  
  597.      case SQL_BINARY:
  598.      case SQL_VARBINARY:
  599.      case SQL_LONGVARBINARY:    return SQL_C_BINARY;
  600.  
  601.      case SQL_TYPE_DATE:        return SQL_C_TYPE_DATE;
  602.      case SQL_TYPE_TIME:        return SQL_C_TYPE_TIME;
  603.      case SQL_TYPE_TIMESTAMP:   return SQL_C_TYPE_TIMESTAMP;
  604.  
  605.      case SQL_DATE:             return SQL_C_DATE;
  606.      case SQL_TIME:             return SQL_C_TIME;
  607.      case SQL_TIMESTAMP:        return SQL_C_TIMESTAMP;
  608.    }
  609.  
  610.    return SQL_C_CHAR;
  611. }
  612.  
  613.  
  614. /* DeleteRow ---------------------------------------------------------------
  615.    Description: Delete the current (positioned) row
  616.    --------------------------------------------------------------------------*/
  617. void INTFUNC DeleteRow(LPCHILD lpChild)
  618. {
  619.    HCURSOR  hcur;
  620.    LPSTR    lpsz;
  621.    LPSTR    lpszT;
  622.    SWORD    cb;
  623.  
  624.    // Ensure the delete request is valid
  625.    if (!lpChild->fDataFetched) {
  626.       DoMessage(lpChild->hwnd, IDS_NODATAFETCHED);
  627.       return;
  628.    }
  629.  
  630.    if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_NOROW) {
  631.       DoMessage(lpChild->hwnd, IDS_NOROWDELETE);
  632.       return;
  633.    }
  634.  
  635.    if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_ERROR) {
  636.       DoMessage(lpChild->hwnd, IDS_ERRORROWDELETE);
  637.       return;
  638.    }
  639.  
  640.    if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_DELETED) {
  641.       DoMessage(lpChild->hwnd, IDS_DELROWDELETE);
  642.       return;
  643.    }
  644.  
  645.    if (lpChild->fConcurrency == SQL_CONCUR_READ_ONLY) {
  646.       DoMessage(lpChild->hwnd, IDS_NOUPDATE);
  647.       return;
  648.    }
  649.  
  650.    lpsz  = AllocPtr(2 * cbMAXSQL);
  651.    lpszT = lpsz + cbMAXSQL;
  652.  
  653.    // Verify the request and allocate a new (temporary) SQLHSTMT for the delete
  654.    LoadString(g_hinst, IDS_DELETEROW, lpsz, cbMAXSQL);
  655.    if (IDYES == MessageBox(lpChild->hwnd, lpsz,
  656.                            g_szTITLE, MB_ICONQUESTION | MB_YESNO) &&
  657.        !DBCError(lpChild->hwnd, SQLAllocHandle(SQL_HANDLE_STMT,g_hdbc, &lpChild->hstmtTmp))) {
  658.  
  659.       // Build DELETE <table> WHERE CURRENT OF <cursor> statement
  660.       lstrcpy(lpsz, szDELETE);
  661.  
  662.       GetTableName(lpszT, lpChild->sql);
  663.       lstrcat(lpsz, lpszT);
  664.  
  665.       lstrcat(lpsz, szWHERE);
  666.  
  667.       lpszT = lpsz + lstrlen(lpsz);
  668.  
  669.       hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  670.  
  671.       if (!STMTError(SQLGetCursorName(lpChild->hstmt, (UCHAR FAR *)lpszT,
  672.                                       cbMAXSQL, &cb))) {
  673.  
  674.          // Issue the request via SQLPrepare/SQLExecute
  675.          if (!ODBCError(lpChild->hwnd, SQL_HANDLE_STMT, lpChild->hstmtTmp,
  676.                         SQLPrepare(lpChild->hstmtTmp, (UCHAR FAR *)lpsz, SQL_NTS)) &&
  677.              !ODBCError(lpChild->hwnd, SQL_HANDLE_STMT, lpChild->hstmtTmp,
  678.                         SQLExecute(lpChild->hstmtTmp)) ) {
  679.             UWORD irowPos;
  680.  
  681.             irowPos = lpChild->irowPos;
  682.  
  683.             // Completely refresh local rowset buffer
  684.             lpChild->rrow = 0;
  685.             lpChild->FetchOP = SQL_FETCH_RELATIVE;
  686.             Fetch(lpChild);
  687.  
  688.             // Reset current position (fetching sets it to the first row)
  689.             SetPos(lpChild, irowPos);
  690.  
  691.             // Repaint window
  692.             InvalidateRect(lpChild->hwnd, NULL, FALSE);
  693.          }
  694.       }
  695.  
  696.       DBCError(lpChild->hwnd, SQLFreeHandle(SQL_HANDLE_STMT,lpChild->hstmtTmp));
  697.       lpChild->hstmtTmp = SQL_NULL_HSTMT;
  698.  
  699.       SetCursor(hcur);
  700.  
  701.    }
  702.  
  703.    FreePtr(lpsz);
  704.  
  705.    return;
  706. }
  707.  
  708.  
  709. /* DoChildMenu -------------------------------------------------------------
  710.    Description: Respond to a request from the child window menu
  711.    --------------------------------------------------------------------------*/
  712. BOOL INTFUNC DoChildMenu(LPCHILD lpChild, WPARAM  wParam, LPARAM  lParam)
  713. {
  714.    UNREF_PARAM (lParam);
  715.    switch (GET_WM_COMMAND_ID(wParam, lparam)) {
  716.  
  717.      case IDM_STMT_SEND:
  718.       if (IDOK == DoDialog(lpChild->hwnd, IDD_STATEMENT, StmtDlgProc))
  719. #ifdef THREAD
  720.          DoSQLThread(lpChild);
  721. #else
  722.       DoSQL(lpChild);
  723. #endif
  724.       break;
  725.  
  726.      case IDM_STMT_TABLE:
  727.       if (IDOK == DoDialog(lpChild->hwnd, IDD_TABLE_INFO, SQLTablesDlgProc))
  728. #ifdef THREAD
  729.          DoSQLThread(lpChild);
  730. #else
  731.       DoSQL(lpChild);
  732. #endif
  733.       break;
  734.  
  735.      case IDM_STMT_TYPE:
  736.       lpChild->dwOperation = OPER_TYPES;
  737. #ifdef THREAD
  738.       DoSQLThread(lpChild);
  739. #else
  740.       DoSQL(lpChild);
  741. #endif
  742.       break;
  743.  
  744.  
  745.  
  746.      case IDM_STMT_OPTIONS:     // general
  747.       {
  748.          CHILD    ChildOld;
  749.  
  750.          // save old values (only works because no pointers
  751.          // get modified in options)
  752.  
  753.          memcpy(&ChildOld, lpChild, sizeof(ChildOld));
  754.          // no modification on those option values yet
  755.          lpChild->fBind =
  756.             lpChild->fMaxBind =
  757.                lpChild->fRowset = FALSE;
  758.  
  759.          if (IDOK == DoDialog(lpChild->hwnd, IDD_OPTION_DIALOG,
  760.                               OptionsDlgProc) && ParamValid(lpChild)) {
  761.             if (lpChild->fDataFetched) {
  762.                FreeStmt(SQL_CLOSE,  lpChild);
  763.                FreeStmt(SQL_UNBIND, lpChild);
  764.             }
  765.          }
  766.          else {
  767.             // restore previous state
  768.             memcpy(lpChild, &ChildOld, sizeof(ChildOld));
  769.          }
  770.          break;
  771.       }
  772.  
  773.      case IDM_STMT_CANCEL:      // general
  774.       Cancel(lpChild);
  775.       break;
  776.  
  777.      case IDM_FETCH_FIRST:
  778.       lpChild->FetchOP = SQL_FETCH_FIRST;
  779. #ifdef THREAD
  780.       FetchThread(lpChild);
  781. #else
  782.       Fetch(lpChild);
  783. #endif
  784.       break;
  785.  
  786.      case IDM_FETCH_PRIOR:
  787.       lpChild->FetchOP = SQL_FETCH_PRIOR;
  788. #ifdef THREAD
  789.       FetchThread(lpChild);
  790. #else
  791.       Fetch(lpChild);
  792. #endif
  793.       break;
  794.  
  795.      case IDM_FETCH_NEXT:
  796.       lpChild->FetchOP = SQL_FETCH_NEXT;
  797. #ifdef THREAD
  798.       FetchThread(lpChild);
  799. #else
  800.       Fetch(lpChild);
  801. #endif
  802.       break;
  803.  
  804.      case IDM_FETCH_LAST:
  805.       lpChild->FetchOP = SQL_FETCH_LAST;
  806. #ifdef THREAD
  807.       FetchThread(lpChild);
  808. #else
  809.       Fetch(lpChild);
  810. #endif
  811.       break;
  812.  
  813.      case IDM_FETCH_ABSOLUTE:
  814.       if (IDOK == DoDialog(lpChild->hwnd, IDD_ABSOLUTE, AbsDlgProc)) {
  815.          lpChild->FetchOP = SQL_FETCH_ABSOLUTE;
  816. #ifdef THREAD
  817.          FetchThread(lpChild);
  818. #else
  819.          Fetch(lpChild);
  820. #endif
  821.       }
  822.       break;
  823.  
  824.      case IDM_FETCH_RELATIVE:
  825.       if (IDOK == DoDialog(lpChild->hwnd, IDD_RELATIVE, RelDlgProc)) {
  826.          lpChild->FetchOP = SQL_FETCH_RELATIVE;
  827. #ifdef THREAD
  828.          FetchThread(lpChild);
  829. #else
  830.          Fetch(lpChild);
  831. #endif
  832.       }
  833.       break;
  834.  
  835.      case IDM_FETCH_GET:
  836. #ifdef THREAD
  837.       GetDataThread(lpChild);
  838. #else
  839.       GetData(lpChild);
  840. #endif
  841.       break;
  842.  
  843.      case IDM_FETCH_DELETEROW:
  844. #ifdef THREAD
  845.       DeleteRowThread(lpChild);
  846. #else
  847.       DeleteRow(lpChild);
  848. #endif
  849.       break;
  850.  
  851.      case IDM_FETCH_UPDATEROW:
  852. #ifdef THREAD
  853.       UpdateRowThread(lpChild);
  854. #else
  855.       UpdateRow(lpChild);
  856. #endif
  857.       break;
  858.      default:
  859.       return FALSE;
  860.    }
  861.  
  862.    AdjustMenus();
  863.    return TRUE;
  864. }
  865.  
  866. #ifdef THREAD
  867. void INTFUNC DoSQLThread(LPCHILD lpChild)
  868. {
  869.    DWORD dwThreadId;
  870.  
  871.    EnterCriticalSection (&lpChild->ThreadCreation);
  872.    lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)DoSQL,
  873.                                     (LPVOID)lpChild, 0, &dwThreadId);
  874.    LeaveCriticalSection (&lpChild->ThreadCreation);
  875. }
  876.  
  877. void INTFUNC FetchThread(LPCHILD lpChild)
  878. {
  879.    DWORD dwThreadId;
  880.  
  881.    EnterCriticalSection (&lpChild->ThreadCreation);
  882.    lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)Fetch,
  883.                                     (LPVOID)lpChild, 0, &dwThreadId);
  884.    LeaveCriticalSection (&lpChild->ThreadCreation);
  885. }
  886.  
  887. void INTFUNC GetDataThread(LPCHILD lpChild)
  888. {
  889.    DWORD dwThreadId;
  890.  
  891.    EnterCriticalSection (&lpChild->ThreadCreation);
  892.    lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)GetData,
  893.                                     (LPVOID)lpChild, 0, &dwThreadId);
  894.    LeaveCriticalSection (&lpChild->ThreadCreation);
  895. }
  896.  
  897. void INTFUNC UpdateRowThread(LPCHILD lpChild)
  898. {
  899.    DWORD dwThreadId;
  900.  
  901.    EnterCriticalSection (&lpChild->ThreadCreation);
  902.    lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)UpdateRow,
  903.                                     (LPVOID)lpChild, 0, &dwThreadId);
  904.    LeaveCriticalSection (&lpChild->ThreadCreation);
  905. }
  906.  
  907. void INTFUNC DeleteRowThread(LPCHILD lpChild)
  908. {
  909.    DWORD dwThreadId;
  910.  
  911.    EnterCriticalSection (&lpChild->ThreadCreation);
  912.    lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)DeleteRow,
  913.                                     (LPVOID)lpChild, 0, &dwThreadId);
  914.    LeaveCriticalSection (&lpChild->ThreadCreation);
  915. }
  916. #endif
  917.  
  918.  
  919. /* DoSQL -------------------------------------------------------------------
  920.    Description: Issue a SQL statement and prepare for fetching data
  921.    --------------------------------------------------------------------------*/
  922. void INTFUNC DoSQL(LPCHILD lpChild)
  923. {
  924.    SQLRETURN rc;
  925.  
  926.    // Prepare the statement
  927.  
  928.    if (PrepareStmt(lpChild) == SQL_ERROR)
  929.       return;
  930.  
  931.    switch(lpChild->dwOperation) {
  932.      case  OPER_SELECT:
  933.       // Issue the request via SQLExecDirect
  934.       Async(SQLExecDirect(lpChild->hstmt,
  935.                           (UCHAR FAR *)lpChild->sql,
  936.                           lstrlen(lpChild->sql)));
  937.       break;
  938.  
  939.      case  OPER_TYPES:
  940.       // Issue a GetTypeInfo request
  941.       Async(SQLGetTypeInfo(lpChild->hstmt, SQL_ALL_TYPES));
  942.       break;
  943.  
  944.  
  945.      case  IDC_TABLE_RAD_TABLE:
  946.       if (!*lpChild->szTable &&
  947.           ((!*lpChild->szUser && strcmp(lpChild->szQualifier, "%") == 0)||
  948.            (!*lpChild->szQualifier && strcmp(lpChild->szUser, "%") == 0))) {  // Special qualifier enumeration
  949.          Async(SQLTables(lpChild->hstmt,
  950.                          lpChild->szQualifier, SQL_NTS,
  951.                          lpChild->szUser, SQL_NTS,
  952.                          lpChild->szTable, SQL_NTS,
  953.                          lpChild->szType,  SQL_NTS));
  954.       }
  955.       else
  956.          {  // Normal SQLTables call
  957.             Async(SQLTables(lpChild->hstmt,
  958.                             NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  959.                             NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  960.                             NULLIFEMPTY(lpChild->szTable),  SQL_NTS,
  961.                             NULLIFEMPTY(lpChild->szType),  SQL_NTS));
  962.          }
  963.       break;
  964.  
  965.      case  IDC_TABLE_RAD_PRIV:
  966.       Async(SQLTablePrivileges(lpChild->hstmt,
  967.                                NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  968.                                NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  969.                                NULLIFEMPTY(lpChild->szTable),  SQL_NTS));
  970.  
  971.       break;
  972.  
  973.      case  IDC_TABLE_RAD_STATISTICS:
  974.       Async(SQLStatistics(lpChild->hstmt,
  975.                           NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  976.                           NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  977.                           NULLIFEMPTY(lpChild->szTable),  SQL_NTS,
  978.                           SQL_INDEX_ALL,      // XXX
  979.                           SQL_QUICK));     // XXX
  980.       break;
  981.  
  982.  
  983.  
  984.      case  IDC_TABLE_RAD_PROC:
  985.       Async(SQLProcedures(lpChild->hstmt,
  986.                           NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  987.                           NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  988.                           NULLIFEMPTY(lpChild->szTable),  SQL_NTS));
  989.  
  990.       break;
  991.  
  992.      case  IDC_TABLE_RAD_COLUMN:
  993.       Async(SQLColumns(lpChild->hstmt,
  994.                        NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  995.                        NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  996.                        NULLIFEMPTY(lpChild->szTable),  SQL_NTS,
  997.                        NULLIFEMPTY(lpChild->szColName), SQL_NTS));
  998.  
  999.    }
  1000.  
  1001.    if (STMTError(rc))
  1002.       return;
  1003.  
  1004.    if (ProcessResults(lpChild)) {
  1005.  
  1006.       if (lpChild->dwGuiFlags & GUIF_ALWAYSFETCH) {
  1007.          lpChild->FetchOP = SQL_FETCH_NEXT;
  1008.          Fetch(lpChild);
  1009.       }
  1010.    }
  1011.  
  1012.    AdjustMenus();
  1013.    return;
  1014.  
  1015. }
  1016.  
  1017. /* ProcessResults---------------------------------------------------------------
  1018.    Description: Process the results from a query or statement
  1019.    --------------------------------------------------------------------------*/
  1020. BOOL INTFUNC ProcessResults(LPCHILD lpChild)
  1021. {
  1022.    LPINT     lptab;
  1023.    LPCOL     lpcol;
  1024.    LPBYTE    lpb;
  1025.    SDWORD    cbMsg;
  1026.    SDWORD    cbNull;
  1027.    int       nLastTab;
  1028.    SWORD     i;
  1029.    SQLRETURN rc;
  1030.  
  1031.  
  1032.    // Retrieve number of columns in the result set
  1033.    Async(SQLNumResultCols(lpChild->hstmt, &i));
  1034.    if (STMTError(rc))
  1035.       return FALSE;
  1036.    lpChild->ccol = ((UWORD)i < lpChild->cBind || lpChild->fBindAll
  1037.                     ? i
  1038.                     : lpChild->cBind);
  1039.  
  1040.    // If a result set exists, continue; otherwise, return immediately
  1041.    if (lpChild->ccol)
  1042.       lpChild->fResultSetExists = TRUE;
  1043.    else
  1044.       return FALSE;
  1045.  
  1046.    // Allocate painting related storage and row status array
  1047.  
  1048.    lpChild->rglpv     = (char *)AllocPtr(sizeof(char *) * lpChild->ccol);
  1049.    lpChild->lpnTabs   = (LPINT)AllocPtr(sizeof(int) * (lpChild->ccol+1));
  1050.    lpChild->lpcol     = (LPCOL)AllocPtr(sizeof(COL) * lpChild->ccol);
  1051.    lpChild->lpfStatus = (LPUWORD)AllocPtr((DWORD) sizeof(UWORD)
  1052.                                           * lpChild->crowRowset);
  1053.  
  1054.    for (i=1, lpcol=lpChild->lpcol; i <= lpChild->ccol; i++, lpcol++) {
  1055.       lpcol->lpb  = NULL;
  1056.       lpcol->lpcb = NULL;
  1057.    }
  1058.  
  1059.    cbMsg  = lstrlen(g_szRowDeleted);
  1060.    cbNull = lstrlen(g_szNull);
  1061.    cbMsg  = max(cbMsg, lstrlen(g_szNoRow));
  1062.    cbMsg  = max(cbMsg, cbNull);
  1063.  
  1064.    // Initialize row width (in bytes) and total number of characters per line
  1065.    lpChild->cbrow = 0;
  1066.    lpChild->ccols = 0;
  1067.  
  1068.    lpcol = lpChild->lpcol;
  1069.    lptab = lpChild->lpnTabs;
  1070.  
  1071.    nLastTab =
  1072.       *lptab++ = cxBORDER;
  1073.  
  1074.    // For each bound column
  1075.    //   a) Get column attributes (e.g., name, size, data type)
  1076.    //   b) Add column to physical and display row widths
  1077.    //   c) Determine tab location
  1078.    for (i=1; i <= lpChild->ccol; i++, lpcol++, lptab++) {
  1079.  
  1080.       // Get column name
  1081.       Async(SQLColAttribute(lpChild->hstmt, i,
  1082.                             SQL_DESC_NAME,
  1083.                             lpcol->szName, sizeof(lpcol->szName), NULL,
  1084.                             NULL));
  1085.       if (STMTError(rc)) {
  1086.          FreeStmt(SQL_DROP, lpChild);
  1087.          return FALSE;
  1088.       }
  1089.  
  1090.       // Get actual column length (number of physical data bytes)
  1091.         // if the sample were to be used with a 3.0 driver only SQLColAttribute
  1092.         // with fattribute of SQL_DESC_OCTECT_LENGTH could be called instead.
  1093.          Async(SQLColAttributes(lpChild->hstmt, i,
  1094.                             SQL_COLUMN_LENGTH,
  1095.                             NULL, 0, NULL,
  1096.                             &lpcol->cb));
  1097.  
  1098.  
  1099.       if (STMTError(rc)) {
  1100.          FreeStmt(SQL_DROP, lpChild);
  1101.          return FALSE;
  1102.       }
  1103.  
  1104.       // Get display width
  1105.       Async(SQLColAttribute(lpChild->hstmt, i,
  1106.                             SQL_DESC_DISPLAY_SIZE,
  1107.                             NULL, 0, NULL,
  1108.                             &lpcol->cbc));
  1109.       if (STMTError(rc)) {
  1110.          FreeStmt(SQL_DROP, lpChild);
  1111.          return FALSE;
  1112.       }
  1113.  
  1114.  
  1115.       // if the display size is too big, force to the the maximum
  1116.       if (lpcol->cbc > lpChild->crowMaxBind)
  1117.          lpcol->cbc = lpChild->crowMaxBind;
  1118.  
  1119.       // Ensure display width is wide enough for:
  1120.       //   a) Column name
  1121.       //   b) Null string
  1122.       //   c) Row status (for the first column only)
  1123.       if (lpcol->cbc < cbMsg)
  1124.          lpcol->cbc = cbMsg;
  1125.  
  1126.       if (lstrlen(lpcol->szName) > lpcol->cbc)
  1127.          lpcol->cbc = lstrlen(lpcol->szName);
  1128.  
  1129.       lpcol->cbc++;
  1130.  
  1131.       // Get column SQL type
  1132.       Async(SQLColAttribute(lpChild->hstmt, i,
  1133.                             SQL_DESC_CONCISE_TYPE,
  1134.                             NULL, 0, NULL,
  1135.                             (SDWORD FAR*) &lpcol->fSqlType));
  1136.       if (STMTError(rc)) {
  1137.          FreeStmt(SQL_DROP, lpChild);
  1138.          return FALSE;
  1139.       }
  1140.  
  1141.       // Determine target C type
  1142.       lpcol->fCType = CvtSqlToCType(lpcol->fSqlType);
  1143.  
  1144.       // For hard to handle C types, let the driver convert to character
  1145.       if (lpcol->fCType == SQL_C_BIT    ||
  1146.           lpcol->fCType == SQL_C_BINARY ||
  1147.           lpcol->fCType == SQL_C_DATE   ||
  1148.           lpcol->fCType == SQL_C_TIME   ||
  1149.           lpcol->fCType == SQL_C_TIMESTAMP ||
  1150.           lpcol->fCType == SQL_C_TYPE_DATE   ||
  1151.           lpcol->fCType == SQL_C_TYPE_TIME   ||
  1152.           lpcol->fCType == SQL_C_TYPE_TIMESTAMP) {
  1153.          lpcol->fCType = SQL_C_CHAR;
  1154.          lpcol->cb = lpcol->cbc;
  1155.       }
  1156.  
  1157.       // Determine next column tab (based on column width plus border)
  1158.       nLastTab =
  1159.          *lptab    = nLastTab + ((int)lpcol->cbc * g_cx) + (2 * cxBORDER);
  1160.  
  1161.       // Set maximum column length for character data
  1162.       if( lpcol->cb > lpChild->crowMaxBind &&
  1163.          lpcol->fCType == SQL_C_CHAR )
  1164.          lpcol->cb = lpChild->crowMaxBind;
  1165.  
  1166.       if( lpcol->fCType == SQL_C_CHAR )
  1167.          lpcol->cb++;
  1168.  
  1169.       // Increment total phsyical row width and display width
  1170.       lpChild->cbrow += lpcol->cb;
  1171.       lpChild->ccols += (int)lpcol->cbc;
  1172.  
  1173.       cbMsg = cbNull;
  1174.    }
  1175.  
  1176.    // Include a count field for each bound column in physical row width
  1177.    lpChild->cbrow += lpChild->ccol * sizeof(SDWORD);
  1178.  
  1179.    // Add intra-column border amounts to total character width
  1180.    lpChild->ccols += (lpChild->ccol * (2 * cxBORDER)) / g_cx;
  1181.  
  1182.    // For each column, include an element in the format string
  1183.    lpb  = (LPBYTE)lpChild->szFmt;
  1184.    for (i=0, lpcol=lpChild->lpcol; i < lpChild->ccol; i++, lpcol++) {
  1185.       *(lpb++) = '\t';
  1186.       *(lpb++) = '%';
  1187.       *(lpb++) = 's';
  1188.    }
  1189.    *lpb = 0;
  1190.  
  1191.    // If row-wise binding, allocate a buffer; switch to column-wise
  1192.    // if the entire row-set cannot fit into 64K minus cursor library
  1193.    // headers
  1194.    lpb = NULL;
  1195.    if (ROW_BINDING(lpChild)) {
  1196.       if ((lpChild->cbrow * lpChild->crowRowset) > 65500L) {
  1197.          DoMessage(lpChild->hwnd, IDS_BIGROWSET);
  1198.          lpChild->fBindByRow = IDC_RADIO_BINDCOL ;
  1199.       }
  1200.       else {
  1201.          lpb          =
  1202.             lpChild->lpb = (LPBYTE)AllocPtr(lpChild->cbrow *
  1203.                                             lpChild->crowRowset);
  1204.       }
  1205.    }
  1206.  
  1207.    // Set binding type
  1208.    if (STMTError(SQLSetStmtAttr(lpChild->hstmt,
  1209.                                 SQL_ATTR_ROW_BIND_TYPE,
  1210.                                 (SQLPOINTER) (ROW_BINDING(lpChild)
  1211.                                               ? lpChild->cbrow
  1212.                                               : SQL_BIND_BY_COLUMN),
  1213.                                 SQL_IS_INTEGER)))
  1214.       return FALSE;
  1215.  
  1216.    // Finally, for each bound column, bind the data value
  1217.    for (i=1, lpcol=lpChild->lpcol; i <= lpChild->ccol; i++, lpcol++) {
  1218.       if (!ROW_BINDING(lpChild)) {
  1219.          lpcol->lpb  = (LPBYTE)AllocPtr(lpcol->cb * lpChild->crowRowset);
  1220.          lpcol->lpcb = (LPSDWORD)AllocPtr((DWORD) sizeof(SDWORD) *
  1221.                                           lpChild->crowRowset);
  1222.       }
  1223.       else {
  1224.          lpcol->lpb  = (LPBYTE)lpb;
  1225.          lpcol->lpcb = (LPSDWORD)(lpb + lpcol->cb);
  1226.          lpb += lpcol->cb + sizeof(SDWORD);
  1227.       }
  1228.  
  1229.       if (STMTError(SQLBindCol(lpChild->hstmt, i, (SWORD)lpcol->fCType,
  1230.                                (PTR)(lpcol->lpb),
  1231.                                lpcol->cb,
  1232.                                lpcol->lpcb)))
  1233.          return FALSE;
  1234.    }
  1235.  
  1236.    return TRUE;
  1237. }
  1238.  
  1239.  
  1240. /* PrepareStmt -------------------------------------------------------------
  1241.    Description: Prepare a statement for future processing
  1242.  
  1243.    Returns: SQL_ERROR if an error occurs
  1244.    --------------------------------------------------------------------------*/
  1245. SQLRETURN INTFUNC PrepareStmt(LPCHILD lpChild)
  1246. {
  1247.  
  1248.    // Close the statement and drop bindings
  1249.    FreeStmt(SQL_CLOSE,  lpChild);
  1250.    FreeStmt(SQL_UNBIND, lpChild);
  1251.  
  1252.    // Set scroll options
  1253.    if (!(SUCCESS(SQLSetStmtAttr(lpChild->hstmt,
  1254.                                 SQL_ATTR_CURSOR_TYPE,
  1255.                                 (SQLPOINTER) lpChild->crowKeyset,
  1256.                                 SQL_IS_INTEGER))))
  1257.       lpChild->fNoCursorType = TRUE;
  1258.  
  1259.    if (!(SUCCESS(SQLSetStmtAttr(lpChild->hstmt,
  1260.                                 SQL_ATTR_CONCURRENCY,
  1261.                                 (SQLPOINTER) lpChild->fConcurrency,
  1262.                                 SQL_IS_INTEGER))))
  1263.       lpChild->fNoConcurrency = TRUE;
  1264.  
  1265.  
  1266.    if (STMTError(SQLSetStmtAttr(lpChild->hstmt,
  1267.                                 SQL_ATTR_ROW_ARRAY_SIZE,
  1268.                                 (SQLPOINTER) lpChild->crowRowset,
  1269.                                 SQL_IS_INTEGER)))
  1270.       return SQL_ERROR;
  1271.  
  1272.    // Set async mode (if supported by the driver)
  1273.    if (g_fAsyncSupported &&
  1274.        STMTError(SQLSetStmtAttr(lpChild->hstmt,
  1275.                                 SQL_ATTR_ASYNC_ENABLE,
  1276.                                 (SQLPOINTER) (lpChild->fAsync ? 1 : 0),
  1277.                                 SQL_IS_INTEGER)))
  1278.       return SQL_ERROR;
  1279.  
  1280.  
  1281.  
  1282.    return FALSE;
  1283.  
  1284. }
  1285.  
  1286. /* Cancel -------------------------------------------------------------------
  1287.    Description: Issue cancel request
  1288.    --------------------------------------------------------------------------*/
  1289. void INTFUNC Cancel(LPCHILD lpChild)
  1290. {
  1291.    SQLRETURN rc;
  1292.  
  1293.    // Call SQLCancel
  1294.    if (lpChild->hstmtTmp != SQL_NULL_HSTMT) {
  1295.       rc = SQLCancel(lpChild->hstmtTmp);  // Cancel temp hstmt
  1296.       STMTError(rc);
  1297.    }
  1298.    else {
  1299.       rc = SQLCancel(lpChild->hstmt);
  1300.       STMTError(rc);
  1301.       FreeStmt(SQL_CLOSE,  lpChild);   // Cleanup statement
  1302.    }
  1303.  
  1304.    return;
  1305. }
  1306.  
  1307.  
  1308. /* Fetch -------------------------------------------------------------------
  1309.    Description: Issue fetch request
  1310.    --------------------------------------------------------------------------*/
  1311. void INTFUNC Fetch(LPCHILD lpChild)
  1312. {
  1313.    SQLRETURN rc;
  1314.    UDWORD    crow;
  1315.    HCURSOR   hcur;
  1316.    SDWORD    sdwIrowLast = lpChild->irow;
  1317.    SDWORD    sdwRowsAffected;
  1318.    SDWORD    irow;
  1319.    UWORD     fFetchType;
  1320.  
  1321.    static UWORD   fFetchTypeLast = SQL_FETCH_NEXT;
  1322.  
  1323.    fFetchType = lpChild->FetchOP;
  1324.    if (fFetchType == SQL_FETCH_ABSOLUTE)
  1325.       irow = lpChild->arow;
  1326.    else if (fFetchType == SQL_FETCH_RELATIVE)
  1327.       irow = lpChild->rrow;
  1328.    else
  1329.       irow = 0;
  1330.    if (!(lpChild->fResultSetExists)) {
  1331.       STMTError(SQLRowCount(lpChild->hstmt, &sdwRowsAffected));
  1332.  
  1333.       if (sdwRowsAffected > -1) {
  1334.          UCHAR szBuffer[200];
  1335.          wsprintf(szBuffer,
  1336.                   (sdwRowsAffected == 1) ? szRowAffected:szDataAffected,
  1337.                   sdwRowsAffected);
  1338.  
  1339.          MessageBox(lpChild->hwnd,
  1340.                     szBuffer,
  1341.                     szNoDataTitle,
  1342.                     MB_ICONINFORMATION);
  1343.       }
  1344.       else
  1345.          {
  1346.             MessageBox( lpChild->hwnd,
  1347.                        szNoDataTitle,
  1348.                        szNoData,
  1349.                        MB_ICONINFORMATION);
  1350.          }
  1351.       return;
  1352.    }
  1353.  
  1354.    hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1355.  
  1356.    SQLSetStmtAttr(lpChild->hstmt, SQL_ATTR_ROWS_FETCHED_PTR,
  1357.                   &crow, SQL_IS_POINTER);
  1358.    SQLSetStmtAttr(lpChild->hstmt, SQL_ATTR_ROW_STATUS_PTR,
  1359.                   lpChild->lpfStatus, SQL_IS_POINTER);
  1360.  
  1361.    // Call SQLFetchScroll
  1362.    Async(SQLFetchScroll(lpChild->hstmt, fFetchType, irow));
  1363.  
  1364.    if ((rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) &&
  1365.        lpChild->irowPos > crow) {
  1366.       SetPos(lpChild, (UWORD) crow);
  1367.    }
  1368.  
  1369.    if (!STMTError(rc) && rc != SQL_NO_DATA) {
  1370.  
  1371.       // Reset columns retrieved count
  1372.       lpChild->ccolRetrieved = (UWORD)lpChild->ccol;
  1373.  
  1374.       // If this is the first fetch, initialize child variables
  1375.       if (!lpChild->fDataFetched) {
  1376.  
  1377.          lpChild->fDataFetched = TRUE;
  1378.  
  1379.          lpChild->irow    = -((SDWORD)lpChild->crowRowset);
  1380.          lpChild->irowPos = 1;
  1381.  
  1382.          SetScrollPos(lpChild->hwndVScroll, SB_CTL, 0, FALSE);
  1383.          SetScrollPos(lpChild->hwndHScroll, SB_CTL, 0, FALSE);
  1384.  
  1385.          SetScroll(lpChild);
  1386.          AllocClipRgn(lpChild);
  1387.       }
  1388.  
  1389.       // Otherwise, maintain current row position in the row-set
  1390.       else if (lpChild->fConcurrency != SQL_CONCUR_READ_ONLY &&
  1391.                lpChild->crowKeyset != SQL_CURSOR_FORWARD_ONLY)
  1392.          SetPos(lpChild, lpChild->irowPos);
  1393.  
  1394.       // if (fFetchType == SQL_FETCH_RESUME)
  1395.       //    fFetchType = fFetchTypeLast;
  1396.  
  1397.       // Adjust absolute row number by fetch amount
  1398.       switch (fFetchType) {
  1399.         case SQL_FETCH_FIRST:
  1400.          lpChild->irow = 1;
  1401.          break;
  1402.  
  1403.         case SQL_FETCH_PRIOR:
  1404.          lpChild->irow -= crow;
  1405.          if (lpChild->irow < 1)
  1406.             lpChild->irow = 1;
  1407.          break;
  1408.  
  1409.         case SQL_FETCH_NEXT:
  1410.          lpChild->irow += lpChild->crowCurrent;  // Previous RS
  1411.          break;
  1412.  
  1413.         case SQL_FETCH_LAST: {
  1414.            SDWORD iRowT;   // scratch variable
  1415.  
  1416.            if (STMTError(SQLGetStmtAttr(lpChild->hstmt,
  1417.                                         SQL_ATTR_ROW_NUMBER,
  1418.                                         &iRowT,
  1419.                                         SQL_IS_INTEGER,
  1420.                                         NULL))) {
  1421.               // the cursor library does not support SQL_ROW_NUMBER,
  1422.               // so use the following calculation. The  result might
  1423.               // be incorrect
  1424.               STMTError(SQLRowCount(lpChild->hstmt, &iRowT));
  1425.               lpChild->irow += (iRowT - crow);
  1426.               if (lpChild->irow < 1) lpChild->irow = 1;
  1427.  
  1428.            }
  1429.            else {    // the cursor library supports SQL_ROW_NUMBER
  1430.               lpChild->irow = iRowT;
  1431.            }
  1432.         }
  1433.          break;
  1434.  
  1435.         case SQL_FETCH_RELATIVE:
  1436.          lpChild->irow += irow;
  1437.          if (lpChild->irow < 1)
  1438.             lpChild->irow = 1;
  1439.          break;
  1440.  
  1441.         case SQL_FETCH_ABSOLUTE:
  1442.          if (irow > 0)
  1443.             lpChild->irow = irow;
  1444.          else {
  1445.             STMTError(SQLRowCount(lpChild->hstmt, &lpChild->irow));
  1446.             lpChild->irow = lpChild->irow + irow + 1;
  1447.             if (lpChild->irow < 1) lpChild->irow = 1;
  1448.          }
  1449.          break;
  1450.       }
  1451.  
  1452.       // Repaint window
  1453.       InvalidateRect(lpChild->hwnd, NULL, FALSE);
  1454.  
  1455.       lpChild->crowCurrent = crow;
  1456.    }
  1457.  
  1458.    fFetchTypeLast = fFetchType;
  1459.  
  1460.    AdjustMenus();
  1461.    SetCursor(hcur);
  1462.    return;
  1463. }
  1464.  
  1465.  
  1466. /* FreeStmt ----------------------------------------------------------------
  1467.    Description: Free SQLHSTMT and reset associated variables of a child window
  1468.                 NOTE: Only SQL_CLOSE, SQL_DROP, and SQL_UNBIND are valid
  1469.                 --------------------------------------------------------------------------*/
  1470. void INTFUNC FreeStmt(UWORD fOption, LPCHILD lpChild)
  1471. {
  1472.    SWORD i;
  1473.    LPCOL lpcol;
  1474.  
  1475.    if (!lpChild->hstmt)
  1476.       return;
  1477.  
  1478.    // Issue the real SQLFreeStmt call
  1479.    if (fOption == SQL_DROP) {
  1480.       if (STMTError(SQLFreeHandle(SQL_HANDLE_STMT, lpChild->hstmt)))
  1481.          return;
  1482.    }
  1483.    else {
  1484.       if (STMTError(SQLFreeStmt(lpChild->hstmt, fOption)))
  1485.          return;
  1486.    }
  1487.  
  1488.    // Drop data buffers for SQL_DROP and SQL_UNBIND requests
  1489.    if (fOption == SQL_DROP || fOption == SQL_UNBIND) {
  1490.       if (!ROW_BINDING(lpChild)) {
  1491.          for (i=0, lpcol=lpChild->lpcol; i < lpChild->ccol; i++, lpcol++) {
  1492.             FreePtr(lpcol->lpb);  lpcol->lpb  = NULL;
  1493.             FreePtr(lpcol->lpcb); lpcol->lpcb = NULL;
  1494.          }
  1495.       }
  1496.       else if (lpChild->lpb) {
  1497.          FreePtr(lpChild->lpb);
  1498.          lpChild->lpb = NULL;
  1499.       }
  1500.    }
  1501.  
  1502.    // Only drop memory for SQL_UNBIND requests
  1503.    if (fOption == SQL_UNBIND)
  1504.       return;
  1505.  
  1506.    // Clear SQLHSTMT handle for SQL_DROP
  1507.    if (fOption == SQL_DROP)
  1508.       lpChild->hstmt = SQL_NULL_HSTMT;
  1509.  
  1510.    // Always reset and free result set related variables
  1511.    lpChild->fResultSetExists = FALSE;
  1512.    lpChild->ccol         = 0;
  1513.    lpChild->fDataFetched = FALSE;
  1514.    lpChild->cbrow        = 0;
  1515.    lpChild->ccols        = 0;
  1516.  
  1517.  
  1518.    FreePtr(lpChild->rglpv);     lpChild->rglpv     = NULL;
  1519.    FreePtr(lpChild->lpnTabs);   lpChild->lpnTabs   = NULL;
  1520.    FreePtr(lpChild->lpcol);     lpChild->lpcol     = NULL;
  1521.    FreePtr(lpChild->lpfStatus); lpChild->lpfStatus = NULL;
  1522.  
  1523.    if (lpChild->hrgn) {
  1524.       DeleteObject(lpChild->hrgn);
  1525.       lpChild->hrgn = NULL;
  1526.    }
  1527.  
  1528.    if (lpChild->hwnd) {
  1529.       lpChild->fVScroll = FALSE;
  1530.       lpChild->fHScroll = FALSE;
  1531.  
  1532.       ShowWindow(lpChild->hwndVScroll, SW_HIDE);
  1533.       ShowWindow(lpChild->hwndHScroll, SW_HIDE);
  1534.       InvalidateRect(lpChild->hwnd, NULL, FALSE);
  1535.    }
  1536.    return;
  1537. }
  1538.  
  1539.  
  1540. /* GetCurrentValue ---------------------------------------------------------
  1541.    Description: Convert to character and return column data from current
  1542.                 row
  1543. --------------------------------------------------------------------------*/
  1544. #pragma optimize("ceglntw", off)
  1545. void INTFUNC GetCurrentValue(LPSTR lpsz, LPCOL lpcol, LPCHILD lpChild)
  1546. {
  1547.    LPBYTE      lpb;
  1548.    SDWORD FAR  *lpcb;
  1549.    UWORD       irowPos;
  1550.  
  1551.    irowPos = lpChild->irowPos - 1;
  1552.  
  1553.    // Get data and count field pointers based on binding type
  1554.    if (ROW_BINDING(lpChild)) {
  1555.       lpb  = lpcol->lpb + (irowPos * lpChild->cbrow);
  1556.       lpcb = (LPSDWORD)(lpb + lpcol->cb);
  1557.    }
  1558.    else {
  1559.       lpb  = lpcol->lpb + (irowPos * lpcol->cb);
  1560.       lpcb = lpcol->lpcb + irowPos;
  1561.    }
  1562.  
  1563.    // Convert column data to character using the supplied buffer
  1564.    if (*lpcb == SQL_NULL_DATA) {
  1565.       lstrcpy(lpsz, g_szNull);
  1566.       return;
  1567.    }
  1568.  
  1569.    switch (lpcol->fSqlType) {
  1570.  
  1571.      case SQL_CHAR:
  1572.      case SQL_VARCHAR:
  1573.       lstrcpy(lpsz, (LPSTR)lpb);
  1574.       break;
  1575.  
  1576.      case SQL_INTEGER:
  1577.      case SQL_SMALLINT:
  1578.      case SQL_TINYINT: {
  1579.         long  l;
  1580.  
  1581.         l = (lpcol->fSqlType == SQL_INTEGER
  1582.              ? *((DWORD FAR *)lpb)
  1583.              : lpcol->fSqlType == SQL_SMALLINT
  1584.              ? *((WORD FAR *)lpb)
  1585.              : *((UCHAR FAR *)lpb));
  1586.  
  1587.         _ltoa(l, lpsz, 10);
  1588.         break;
  1589.      }
  1590.  
  1591.      case SQL_REAL:
  1592.      case SQL_FLOAT:
  1593.      case SQL_DOUBLE: {
  1594.         double   d;
  1595.  
  1596.         d = (lpcol->fSqlType == SQL_REAL
  1597.              ? *((float FAR *)lpb)
  1598.              : *((double FAR *)lpb));
  1599.  
  1600.         _gcvt(d, 15, lpsz);
  1601.         break;
  1602.      }
  1603.  
  1604.      default:
  1605.       *lpsz = '\0';
  1606.       break;
  1607.    }
  1608.  
  1609.    return;
  1610. }
  1611. #pragma optimize("ceglntw", on)
  1612.  
  1613.  
  1614. /* GetData -----------------------------------------------------------------
  1615.    Description: Retrieve the next unbound column and display the data
  1616.    --------------------------------------------------------------------------*/
  1617. void INTFUNC GetData(LPCHILD lpChild)
  1618. {
  1619.    HCURSOR   hcur;
  1620.    BIGCOL    bcol;
  1621.    UWORD     icol;
  1622.    SQLRETURN rc;
  1623.  
  1624.    // Prevent Cancel from closing statement
  1625.    lpChild->hstmtTmp = lpChild->hstmt;
  1626.  
  1627.    // Determine next unbound column index
  1628.    icol = lpChild->ccolRetrieved + 1;
  1629.  
  1630.    hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1631.  
  1632.    // Get column name
  1633.    Async(SQLColAttribute(lpChild->hstmt, icol,
  1634.                          SQL_DESC_NAME,
  1635.                          bcol.szName, sizeof(bcol.szName), NULL,
  1636.                          NULL));
  1637.  
  1638.    SetCursor(hcur);
  1639.  
  1640.    if (STMTError(rc)) {
  1641.       lpChild->hstmtTmp = SQL_NULL_HSTMT;
  1642.       return;
  1643.    }
  1644.  
  1645.    bcol.lpsz = lpChild->lpsz;
  1646.  
  1647.    hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1648.  
  1649.    // Get the data converting it to character
  1650.    Async(SQLGetData(lpChild->hstmt, icol, SQL_C_CHAR,
  1651.                     bcol.lpsz, cbBUFSIZE-1, &bcol.cb));
  1652.  
  1653.    SetCursor(hcur);
  1654.  
  1655.    lpChild->hstmtTmp = SQL_NULL_HSTMT;
  1656.  
  1657.    if (STMTError(rc) || rc == SQL_NO_DATA)
  1658.       return;
  1659.  
  1660.    // Display the retrieved data
  1661.    DialogBoxParam(g_hinst,
  1662.                   MAKEINTRESOURCE(IDD_DATADLG),
  1663.                   lpChild->hwnd,
  1664.                   DataDlgProc,
  1665.                   (LPARAM)((LPSTR)&bcol));
  1666.  
  1667.    lpChild->ccolRetrieved = icol;
  1668.    return;
  1669. }
  1670.  
  1671.  
  1672. /* GetTableName ------------------------------------------------------------
  1673.    Description: Extract table name from a SELECT statement
  1674.    --------------------------------------------------------------------------*/
  1675. void INTFUNC GetTableName(LPSTR lpszTable, LPCSTR szSql)
  1676. {
  1677.    LPCSTR   lpsz;
  1678.    int      cp;
  1679.    int      cb;
  1680.  
  1681.    cb = lstrlen(szFROM);
  1682.  
  1683.    for (lpsz=szSql, cp=0; *lpsz; ) {
  1684.  
  1685.       while (*lpsz && ISWHITE(*lpsz)) lpsz++;
  1686.  
  1687.       if (!cp && !_fstrnicmp(lpsz, szFROM, cb) && ISWHITE(*(lpsz+cb)))
  1688.          break;
  1689.  
  1690.       if (ISLPAREN(*lpsz))
  1691.          cp++;
  1692.       else if (ISRPAREN(*lpsz))
  1693.          cp--;
  1694.  
  1695.       while (*lpsz && !ISWHITE(*lpsz)) lpsz++;
  1696.    }
  1697.  
  1698.    while (*lpsz && !ISWHITE(*lpsz)) lpsz++;
  1699.    while (*lpsz && ISWHITE(*lpsz))  lpsz++;
  1700.  
  1701.    if (*lpsz == *g_szQuoteChar) {
  1702.       *lpszTable++ = *lpsz++; // Copy beginning quote
  1703.       while (*lpsz && *lpsz != *g_szQuoteChar) *lpszTable++ = *lpsz++;
  1704.       *lpszTable++ = *lpsz++; // Copy ending quote
  1705.    }
  1706.    else  // Not a quoted identifier
  1707.       while (*lpsz && !ISCOMMA(*lpsz) && !ISWHITE(*lpsz)) *lpszTable++ = *lpsz++;
  1708.  
  1709.    *lpszTable = '\0';
  1710.  
  1711.    return;
  1712. }
  1713.  
  1714.  
  1715. /* IsUpdateable ------------------------------------------------------------
  1716.    Description: Return TRUE if this app supports updating the particular
  1717.                 SQL data type (due to limited conversion support)
  1718.                 --------------------------------------------------------------------------*/
  1719. BOOL INTFUNC IsUpdateable(SDWORD fSqlType)
  1720. {
  1721.    switch (fSqlType) {
  1722.      case SQL_CHAR:
  1723.      case SQL_VARCHAR:
  1724.      case SQL_SMALLINT:
  1725.      case SQL_INTEGER:
  1726.      case SQL_REAL:
  1727.      case SQL_FLOAT:
  1728.      case SQL_DOUBLE:
  1729.      case SQL_TINYINT:
  1730.       return TRUE;
  1731.  
  1732.      default:
  1733.       return FALSE;
  1734.    }
  1735. }
  1736.  
  1737.  
  1738. /* OnDataRow ---------------------------------------------------------------
  1739.    Description: Return 0 or greater if the mouse coordinates in lparam are
  1740.                 over a valid data row, Otherwise return -1
  1741.                 --------------------------------------------------------------------------*/
  1742. int INTFUNC OnDataRow(LPCHILD lpChild, LPARAM lparam)
  1743. {
  1744.    RECT  rc;
  1745.    int   row;
  1746.  
  1747.    if (!(lpChild->hrgn))
  1748.       return -1;
  1749.  
  1750.    GetRgnBox(lpChild->hrgn, &rc);
  1751.  
  1752.    row = (int)HIWORD(lparam) - g_cy;
  1753.  
  1754.    if (row < 0)
  1755.       return FALSE;
  1756.  
  1757.    row /= g_cy;
  1758.  
  1759.    if (row >= 0                        &&
  1760.        (UWORD)row < lpChild->crowRowset )
  1761.       return row;
  1762.    else
  1763.       return -1;
  1764. }
  1765.  
  1766.  
  1767. /* PaintChild --------------------------------------------------------------
  1768.    Description: Paint child window
  1769. --------------------------------------------------------------------------*/
  1770. void INTFUNC PaintChild(LPCHILD  lpChild,
  1771.                         HDC      hdc,
  1772.                         BOOL  fTitle,
  1773.                         BOOL  fRefresh,
  1774.                         BOOL  fActive)
  1775. {
  1776.    RECT  rc;
  1777.  
  1778.    GetClientRect(lpChild->hwnd, &rc);
  1779.  
  1780.    // If no data exists, just erase the window
  1781.    if (!lpChild->fDataFetched)
  1782.       FillRect(hdc, &rc, g_hbrWin);
  1783.  
  1784.    // Otherwise paint the data in a simple scrollable grid
  1785.    else {
  1786.       HRGN     hrgn;
  1787.       HFONT    hfontOld;
  1788.       HBRUSH   hbrOld;
  1789.       UWORD    ir;
  1790.       int      ic;
  1791.       int      icFirst, icLast;
  1792.       LPCOL    lpcol;
  1793.       UWORD    row;
  1794.       int      col;
  1795.       int      cx, cy;
  1796.       LPINT    lpnTab;
  1797.       LPSTR    lpszValues;
  1798.       LPSTR    lpsz;
  1799.       LPUWORD  lpfStatus;
  1800.       UWORD    irowLast;
  1801.       UWORD    irowPos;
  1802.       COLORREF clrfTxt;
  1803.       COLORREF clrfBkg;
  1804.       char     szFmt[cbSTRLEN];
  1805.  
  1806.       // Prepare the device context
  1807.       SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  1808.  
  1809.       clrfTxt = GetTextColor(hdc);
  1810.       clrfBkg = GetBkColor(hdc);
  1811.  
  1812.       // Determine first (row,col) and corresponding (x,y) offset
  1813.       row = (UWORD)GetScrollPos(lpChild->hwndVScroll, SB_CTL);
  1814.       col = GetScrollPos(lpChild->hwndHScroll, SB_CTL);
  1815.  
  1816.       cx = col * g_cx;
  1817.       cy = row * g_cy;
  1818.  
  1819.       // Determine last row to be painted
  1820.       irowLast = (UWORD)row + ((UWORD)lpChild->crowwin < lpChild->crowRowset
  1821.                                ? lpChild->crowwin
  1822.                                : lpChild->crowRowset);
  1823.       if (irowLast > lpChild->crowRowset)
  1824.          irowLast--;
  1825.  
  1826.       // Get current row number as a zero based index
  1827.       irowPos  = lpChild->irowPos - 1;
  1828.  
  1829.       // Determine which columns will be painted
  1830.       lpnTab = lpChild->lpnTabs;
  1831.       for (icFirst=0;
  1832.            icFirst < (lpChild->ccol-1) && (cx + cxBORDER) > *(lpnTab+1);
  1833.            icFirst++, lpnTab++);
  1834.       for (icLast=icFirst+1;
  1835.            icLast < lpChild->ccol &&
  1836.            (cx + cxBORDER + (lpChild->ccolwin * g_cx)) > *lpnTab;
  1837.            icLast++, lpnTab++);
  1838.  
  1839.       // Offset the device context to appropriate position in the rowset
  1840.       SetWindowOrgEx(hdc, cx, cy, NULL);
  1841.  
  1842.       // Set the clip region (to keep from erasing scroll bars, etc.)
  1843.  
  1844.       if (!(lpChild->hrgn))
  1845.          return;
  1846.  
  1847.       SelectClipRgn(hdc, lpChild->hrgn);
  1848.  
  1849.       // Allocate working buffer to converted data values
  1850.       lpszValues = AllocPtr((DWORD) lpChild->ccol * cbSTRLEN);
  1851.  
  1852.       // If requested, paint column titles
  1853.       if (fTitle) {
  1854.  
  1855.          // Prepare device context
  1856.          SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
  1857.          hfontOld = SelectObject(hdc, g_hfontName);
  1858.  
  1859.          // Determine bounding rectangle in logical coordinates
  1860.          rc.top    = cy;
  1861.          rc.bottom = rc.top + g_cy;
  1862.          rc.left   = *(lpChild->lpnTabs + icFirst) - cxBORDER;
  1863.          rc.right  = *(lpChild->lpnTabs + icLast)  - cxBORDER;
  1864.  
  1865.          // Fill rectangle with appropriate color
  1866.          FillRect(hdc, &rc, g_hbrBtn);
  1867.  
  1868.          // Paint white bar across the top
  1869.          PatBlt(hdc, 0, rc.top, rc.right, 1, WHITENESS);
  1870.  
  1871.          // For each visible (or partially visible) column, paint
  1872.          // separating lines and column name
  1873.          lpcol  = lpChild->lpcol   + icFirst;
  1874.          lpnTab = lpChild->lpnTabs + icFirst + 1;
  1875.  
  1876.          for (ic=icFirst; ic < icLast; ic++, lpcol++, lpnTab++) {
  1877.  
  1878.             PatBlt(hdc, rc.left, rc.top, 1, g_cy, WHITENESS);
  1879.  
  1880.             rc.right = *lpnTab - cxBORDER + 1;
  1881.  
  1882.             DrawText(hdc, lpcol->szName, lstrlen(lpcol->szName),
  1883.                      &rc, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
  1884.  
  1885.             rc.left = rc.right;
  1886.  
  1887.             PatBlt(hdc, rc.left-1, rc.top, 1, g_cy, BLACKNESS);
  1888.          }
  1889.  
  1890.          // Paint black line across the bottom
  1891.          PatBlt(hdc, 1, rc.bottom-1, rc.right-1, 1, BLACKNESS);
  1892.  
  1893.          // Reset device context
  1894.          SelectObject(hdc, hfontOld);
  1895.          SetBkColor(hdc, clrfBkg);
  1896.       }
  1897.  
  1898.       // Determine bounding rectangle for first row to be painted
  1899.       rc.top    = cy + g_cy;
  1900.       rc.bottom = rc.top + g_cy - 1;
  1901.       rc.left   = *(lpChild->lpnTabs + icFirst) - cxBORDER;
  1902.       rc.right  = *(lpChild->lpnTabs + icLast)  - cxBORDER;
  1903.  
  1904.       // Make format string from the whole format string created earlier
  1905. #ifdef WIN32
  1906.       _fstrncpy(szFmt, lpChild->szFmt+(icFirst*3), ((icLast-icFirst)*3)+1);
  1907. #else
  1908.       lstrcpyn(szFmt, lpChild->szFmt+(icFirst*3), ((icLast-icFirst)*3)+1);
  1909. #endif
  1910.       szFmt[((icLast-icFirst)*3)+1] = '\0';
  1911.  
  1912.       // Offset into the row status array
  1913.       lpfStatus = lpChild->lpfStatus + row;
  1914.  
  1915.       // Prepare device context
  1916.       hfontOld = SelectObject(hdc, g_hfontData);
  1917.       hbrOld   = SelectObject(hdc, g_hbrBtn);
  1918.  
  1919.       // Paint each row
  1920.       for (ir=row; ir < irowLast; ir++) {
  1921.  
  1922.          // Erase row if requested (i.e., !fRefresh) or if just
  1923.          // painting the row is insufficient to remove old data
  1924.          if (!fRefresh                   ||
  1925.              (ir == irowPos && !fActive) ||
  1926.              *lpfStatus == SQL_ROW_NOROW ||
  1927.              *lpfStatus == SQL_ROW_ERROR ||
  1928.              *lpfStatus == SQL_ROW_DELETED)
  1929.             FillRect(hdc, &rc, g_hbrWin);
  1930.  
  1931.          // For an empty row, write the empty row string
  1932.          if (*lpfStatus == SQL_ROW_NOROW) {
  1933.             if (icFirst)
  1934.                *lpChild->lpsz = '\0';
  1935.             else
  1936.                lstrcpy(lpChild->lpsz, g_szNoRow);
  1937.          }
  1938.  
  1939.          // For a deleted row, write the deleted row string
  1940.          else if (*lpfStatus == SQL_ROW_DELETED) {
  1941.             if (icFirst)
  1942.                *lpChild->lpsz = '\0';
  1943.             else
  1944.                lstrcpy(lpChild->lpsz, g_szRowDeleted);
  1945.          }
  1946.  
  1947.          // For an error row, write the error row string
  1948.          else if (*lpfStatus == SQL_ROW_ERROR) {
  1949.             if (icFirst)
  1950.                *lpChild->lpsz = '\0';
  1951.             else
  1952.                lstrcpy(lpChild->lpsz, g_szRowError);
  1953.          }
  1954.  
  1955.          // For all other rows, build a string of data values
  1956.          else {
  1957.             LPSTR FAR   *lplpsz;
  1958.             LPBYTE      lpb;
  1959.             LPSDWORD lpcb;
  1960.        
  1961.  
  1962.             // Paint updated rows in RED
  1963.             if (*lpfStatus == SQL_ROW_UPDATED)
  1964.                SetTextColor(hdc, RGB(255,0,0));
  1965.  
  1966.                                
  1967.             lplpsz = (LPSTR FAR *)lpChild->rglpv;
  1968.                                
  1969.             lpsz   = lpszValues;
  1970.  
  1971.             lpcol  = lpChild->lpcol   + icFirst;
  1972.             lpnTab = lpChild->lpnTabs + icFirst;
  1973.  
  1974.             // Convert each column to character data
  1975.             for (ic=icFirst; ic < icLast; ic++, lpcol++) {
  1976.  
  1977.                lpb  = lpcol->lpb  + (ir * (ROW_BINDING(lpChild)
  1978.                                            ? lpChild->cbrow
  1979.                                            : lpcol->cb));
  1980.                lpcb = (ROW_BINDING(lpChild)
  1981.                        ? (LPSDWORD)(lpb + lpcol->cb)
  1982.                        : lpcol->lpcb + ir);
  1983.  
  1984.                if (*lpcb == SQL_NULL_DATA)
  1985.                   *lplpsz++ = g_szNull;
  1986.  
  1987.                else if (lpcol->fCType == SQL_C_CHAR)
  1988.                   *lplpsz++ = (LPSTR)lpb;
  1989.  
  1990.                else {
  1991.  
  1992.                   if (lpcol->fCType == SQL_C_FLOAT ||
  1993.                       lpcol->fCType == SQL_C_DOUBLE ) {
  1994.                      double   d;
  1995.  
  1996.                      d = (lpcol->fCType == SQL_C_FLOAT
  1997.                           ? *((float FAR *)lpb)
  1998.                           : *((double FAR *)lpb));
  1999.  
  2000.                      _gcvt(d, 15, lpsz);
  2001.                   }
  2002.  
  2003.                   else {
  2004.                      long  l;
  2005.  
  2006.                      l = ( lpcol->fCType == SQL_C_SHORT
  2007.                           ? *((short FAR *)lpb)
  2008.                           : lpcol->fCType == SQL_C_LONG
  2009.                           ? *((long FAR *)lpb)
  2010.                           : *((signed char FAR *)lpb));
  2011.  
  2012.                      _ltoa(l, lpsz, 10);
  2013.                   }
  2014.  
  2015.                   *lplpsz++ = lpsz;
  2016.                   lpsz += cbSTRLEN;
  2017.                }
  2018.             }
  2019.  
  2020.             // Combine all columns into one string (with tab markers)
  2021.                 Print(lpChild->lpsz, szFmt, lpChild->rglpv );
  2022.  
  2023.                 
  2024.                 
  2025.          }
  2026.  
  2027.          // Paint the row
  2028.          TabbedTextOut(hdc,
  2029.                        rc.left, rc.top,
  2030.                        lpChild->lpsz, lstrlen(lpChild->lpsz),
  2031.                        icLast - icFirst, lpnTab, 0);
  2032.  
  2033.          // Paint bottom separator
  2034.          PatBlt(hdc, 0, rc.bottom, rc.right, 1, PATCOPY);
  2035.  
  2036.          // Paint inter-column separators
  2037.          lpnTab = lpChild->lpnTabs + icFirst + 1;
  2038.          for (ic=icFirst+1; ic < (icLast+1); ic++, lpnTab++)
  2039.             PatBlt(hdc, *lpnTab-cxBORDER, rc.top, 1, g_cy, PATCOPY);
  2040.  
  2041.          // Reset text color (if it was changed)
  2042.          if (*lpfStatus == SQL_ROW_UPDATED)
  2043.             SetTextColor(hdc, clrfTxt);
  2044.  
  2045.          // Hilite the current row
  2046.          if (ir == irowPos                                 &&
  2047.              lpChild->fConcurrency != SQL_CONCUR_READ_ONLY &&
  2048.              fActive                                        ) {
  2049.             RECT  rcTemp, rcInvert;
  2050.  
  2051.             GetClipBox(hdc, &rcTemp);
  2052.  
  2053.             IntersectRect(&rcInvert, &rc, &rcTemp);
  2054.  
  2055.             GetClientRect(lpChild->hwnd, &rcTemp);
  2056.  
  2057.             if( rcInvert.top == rc.top )
  2058.                rcInvert.top++;
  2059.  
  2060.             if( rcInvert.bottom == rc.bottom )
  2061.                rcInvert.bottom--;
  2062.  
  2063.             if( rcInvert.left == rcTemp.left )
  2064.                rcInvert.left++;
  2065.  
  2066.             if( rcInvert.right == rcTemp.right ||
  2067.                rcInvert.right == rc.right )
  2068.                rcInvert.right--;
  2069.  
  2070.             InvertRect(hdc, &rcInvert);
  2071.          }
  2072.  
  2073.          // Advance row rectangle and status array offset
  2074.          rc.top    += g_cy;
  2075.          rc.bottom += g_cy;
  2076.          lpfStatus++;
  2077.       }
  2078.  
  2079.       // Erase any partial row which may be displayed after the last
  2080.       // row of the row-set has been painted
  2081.       if (ir == lpChild->crowRowset)
  2082.          FillRect(hdc, &rc, g_hbrWin);
  2083.  
  2084.       // Erase any partial column which may be displayed after the last
  2085.       // column has been painted
  2086.       if (icLast == lpChild->ccol) {
  2087.          GetClipBox(hdc, &rc);
  2088.          rc.left = *(lpChild->lpnTabs + icLast) - cxBORDER + 1;
  2089.          FillRect(hdc, &rc, g_hbrWin);
  2090.       }
  2091.  
  2092.       FreePtr(lpszValues);
  2093.  
  2094.       // Reset clip region to paint areas outside data grid
  2095.       GetClientRect(lpChild->hwnd, &rc);
  2096.  
  2097.       hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
  2098.  
  2099.       SelectClipRgn(hdc, hrgn);
  2100.  
  2101.       OffsetRect(&rc, cx, cy);
  2102.  
  2103.       // Paint bottom record display bar and/or the corner box
  2104.       // between the scroll bars
  2105.       rc.top = rc.bottom - g_cyHScroll + 1;
  2106.  
  2107.       PatBlt(hdc, rc.left, rc.top, rc.right, 1, BLACKNESS);
  2108.  
  2109.       SelectObject(hdc, g_hbrScroll);
  2110.  
  2111.       rc.top++;
  2112.  
  2113.       cx = (2 * cxBORDER) + g_cxRecord + g_cxRecnum + 2;
  2114.  
  2115.       if (lpChild->fHScroll) {
  2116.          if (lpChild->fVScroll)
  2117.             PatBlt(hdc,
  2118.                    rc.right - g_cxVScroll + 2, rc.top,
  2119.                    g_cxVScroll, g_cyHScroll,
  2120.                    PATCOPY);
  2121.       }
  2122.       else
  2123.          PatBlt(hdc,
  2124.                 rc.left + cx, rc.top,
  2125.                 rc.right - rc.left, g_cyHScroll,
  2126.                 PATCOPY);
  2127.  
  2128.       rc.right = rc.left + cx;
  2129.  
  2130.       PatBlt(hdc, rc.right, rc.top, 1, rc.bottom, BLACKNESS);
  2131.  
  2132.       rc.right--;
  2133.  
  2134.       // Paint current row number
  2135.       if (fRefresh)
  2136.          FillRect(hdc, &rc, g_hbrWin);
  2137.  
  2138.       rc.left += cxBORDER;
  2139.  
  2140.       TextOut(hdc, rc.left, rc.top, szRECORD, lstrlen(szRECORD));
  2141.  
  2142.       rc.left += g_cxRecord + 2;
  2143.  
  2144.       FillRect(hdc, &rc, g_hbrWin);
  2145.  
  2146.       wsprintf(lpChild->lpsz, szRECNUM, lpChild->irow-1 + lpChild->irowPos);
  2147.  
  2148.       TextOut(hdc, rc.left, rc.top, lpChild->lpsz, lstrlen(lpChild->lpsz));
  2149.  
  2150.       DeleteObject(hrgn);
  2151.  
  2152.       // Reset device context
  2153.       SelectObject(hdc, hbrOld);
  2154.  
  2155.       SelectObject(hdc, hfontOld);
  2156.    }
  2157.  
  2158.    return;
  2159. }
  2160.  
  2161.  
  2162.  
  2163. /* SetCurrentValue ---------------------------------------------------------
  2164.    Description: Set a column value from the user buffer
  2165. --------------------------------------------------------------------------*/
  2166. #pragma optimize("ceglntw", off)
  2167. BOOL INTFUNC SetCurrentValue(LPSTR lpsz, LPCOL lpcol, LPCHILD lpChild)
  2168. {
  2169.    LPBYTE   lpb;
  2170.    LPSDWORD lpcb;
  2171.    BOOL     fNew;
  2172.    UWORD    irowPos;
  2173.  
  2174.    irowPos = lpChild->irowPos - 1;
  2175.  
  2176.    // Get data and count field pointers based on binding type
  2177.    if (ROW_BINDING(lpChild)) {
  2178.       lpb  = lpcol->lpb + (irowPos * lpChild->cbrow);
  2179.       lpcb = (LPSDWORD)(lpb + lpcol->cb);
  2180.    }
  2181.    else {
  2182.       lpb  = lpcol->lpb + (irowPos * lpcol->cb);
  2183.       lpcb = lpcol->lpcb + irowPos;
  2184.    }
  2185.  
  2186.    // If the data is NULL, just set the count field to SQL_NULL_DATA
  2187.    if (!lstrcmpi(lpsz, g_szNull)) {
  2188.       if (*lpcb != SQL_NULL_DATA) {
  2189.          *lpcb = SQL_NULL_DATA;
  2190.          fNew  = TRUE;
  2191.       }
  2192.    }
  2193.  
  2194.    // Otherwise, convert the character data back to the appropriate type
  2195.    else switch (lpcol->fSqlType) {
  2196.  
  2197.      case SQL_CHAR:
  2198.      case SQL_VARCHAR:
  2199.       if (lstrcmp(lpsz, (LPSTR)lpb)) {
  2200.          lstrcpy((LPSTR)lpb, lpsz);
  2201.          *lpcb = lstrlen(lpsz);
  2202.          fNew  = TRUE;
  2203.       }
  2204.       break;
  2205.  
  2206.      case SQL_INTEGER:
  2207.      case SQL_SMALLINT:
  2208.      case SQL_TINYINT: {
  2209.         long  lNew, lCur;
  2210.         char  *EndPtr;
  2211.  
  2212.         lNew = strtol(lpsz, &EndPtr, 10);
  2213.         for (; *EndPtr && ISWHITE(*EndPtr); EndPtr = AnsiNext(EndPtr));
  2214.         if (*EndPtr)  { // check to see if there exists non-numeric chars
  2215.            UCHAR szBuffer[128];
  2216.  
  2217.            LoadString(g_hinst, IDS_BADNUMERIC, szBuffer, sizeof(szBuffer));
  2218.            MessageBox(lpChild->hwnd,
  2219.                       szBuffer, NULL, MB_ICONSTOP);
  2220.            fNew = FALSE;
  2221.            break;
  2222.         }
  2223.  
  2224.         lCur = (lpcol->fSqlType == SQL_INTEGER
  2225.                 ? *((DWORD FAR *)lpb)
  2226.                 : lpcol->fSqlType == SQL_SMALLINT
  2227.                 ? *((WORD FAR *)lpb)
  2228.                 : *((UCHAR FAR *)lpb));
  2229.  
  2230.         if (lNew != lCur) {
  2231.  
  2232.            switch (lpcol->fSqlType) {
  2233.              case SQL_INTEGER:
  2234.               *((DWORD FAR *)lpb) = lNew;
  2235.               *lpcb = sizeof(DWORD);
  2236.               break;
  2237.  
  2238.              case SQL_SMALLINT:
  2239.               *((WORD FAR *)lpb) = (WORD)lNew;
  2240.               *lpcb = sizeof(WORD);
  2241.               break;
  2242.  
  2243.              case SQL_TINYINT:
  2244.               *((UCHAR FAR *)lpb) = (UCHAR)lNew;
  2245.               *lpcb = sizeof(UCHAR);
  2246.               break;
  2247.            }
  2248.  
  2249.            fNew = TRUE;
  2250.         }
  2251.         break;
  2252.      }
  2253.  
  2254.      case SQL_REAL:
  2255.      case SQL_FLOAT:
  2256.      case SQL_DOUBLE: {
  2257.         double   dNew, dCur;
  2258.         char  *EndPtr;
  2259.  
  2260.         dNew = strtod(lpsz, &EndPtr);
  2261.         for (; *EndPtr && ISWHITE(*EndPtr); EndPtr = AnsiNext(EndPtr));
  2262.         if (*EndPtr)  { // check to see if there exists non-numeric chars
  2263.            UCHAR szBuffer[128];
  2264.  
  2265.            LoadString(g_hinst, IDS_BADNUMERIC, szBuffer, sizeof(szBuffer));
  2266.            MessageBox(lpChild->hwnd,
  2267.                       szBuffer, NULL, MB_ICONSTOP);
  2268.            fNew = FALSE;
  2269.            break;
  2270.         }
  2271.  
  2272.         dCur = (lpcol->fSqlType == SQL_REAL
  2273.                 ? *((float FAR *)lpb)
  2274.                 : *((double FAR *)lpb));
  2275.  
  2276.         if (dNew != dCur) {
  2277.  
  2278.            switch (lpcol->fSqlType) {
  2279.              case SQL_REAL:
  2280.               *((float FAR *)lpb) = (float)dNew;
  2281.               *lpcb = sizeof(float);
  2282.               break;
  2283.  
  2284.              case SQL_FLOAT:
  2285.              case SQL_DOUBLE:
  2286.               *((double FAR *)lpb) = dNew;
  2287.               *lpcb = sizeof(double);
  2288.               break;
  2289.            }
  2290.  
  2291.            fNew = TRUE;
  2292.         }
  2293.         break;
  2294.      }
  2295.    }
  2296.  
  2297.    return fNew;
  2298. }
  2299. #pragma optimize("ceglntw", on)
  2300.  
  2301.  
  2302. /* SetPos ------------------------------------------------------------------
  2303.    Description: Set current row, de-hilite last current row and hilite
  2304.                 new current row
  2305.                 --------------------------------------------------------------------------*/
  2306. void INTFUNC SetPos(LPCHILD lpChild, UWORD irowPos)
  2307. {
  2308.    HDC      hdc;
  2309.    HFONT    hfont;
  2310.    RECT     rc, rcClip, rcInvert;
  2311.    int      row, col;
  2312.    int      cx, cy;
  2313.  
  2314.    if (!(lpChild->hrgn))
  2315.       return ;
  2316.  
  2317.    if (irowPos < 1)
  2318.       irowPos = 1;
  2319.  
  2320.    else if (irowPos > lpChild->crowRowset)
  2321.       irowPos = lpChild->crowRowset;
  2322.  
  2323.    // Call SQLSetPos to set current row in the row-set
  2324.    if (STMTError(SQLSetPos(lpChild->hstmt, irowPos, SQL_POSITION,
  2325.                            SQL_LOCK_NO_CHANGE)))
  2326.       return;
  2327.  
  2328.    // Reset number of columns retrieved
  2329.    lpChild->ccolRetrieved = (UWORD)lpChild->ccol;
  2330.  
  2331.    // Obtain a device context for painting
  2332.    hdc = GetDC(lpChild->hwnd);
  2333.  
  2334.    row = GetScrollPos(lpChild->hwndVScroll, SB_CTL);
  2335.    col = GetScrollPos(lpChild->hwndHScroll, SB_CTL);
  2336.  
  2337.    cx  = col * g_cx;
  2338.    cy  = row * g_cy;
  2339.  
  2340.    SetWindowOrgEx(hdc, cx, cy, NULL);
  2341.  
  2342.    GetClientRect(lpChild->hwnd, &rc);
  2343.    GetRgnBox(lpChild->hrgn, &rcClip);
  2344.    OffsetRect(&rcClip, cx, cy);
  2345.  
  2346.    rcClip.top += g_cy;
  2347.  
  2348.    // Offset to last current row
  2349.    rc.top    = ((lpChild->irowPos - 1) * g_cy) + g_cy;
  2350.    rc.bottom = rc.top + g_cy - 1;
  2351.    rc.left   = 0;
  2352.    rc.right  = *(lpChild->lpnTabs + lpChild->ccol) - cxBORDER;
  2353.  
  2354.    // De-hilite last current row
  2355.    IntersectRect(&rcInvert, &rc, &rcClip);
  2356.    InflateRect(&rcInvert, -1, -1);
  2357.    InvertRect(hdc, &rcInvert);
  2358.  
  2359.    // Save new current row
  2360.    lpChild->irowPos = irowPos;
  2361.  
  2362.    // Offset to new current row
  2363.    rc.top    = ((lpChild->irowPos - 1) * g_cy) + g_cy;
  2364.    rc.bottom = rc.top + g_cy - 1;
  2365.  
  2366.    // Hilite current row (if visible)
  2367.    if (lpChild->irowPos > (UWORD)row) {
  2368.  
  2369.       IntersectRect(&rcInvert, &rc, &rcClip);
  2370.       InflateRect(&rcInvert, -1, -1);
  2371.       InvertRect(hdc, &rcInvert);
  2372.    }
  2373.  
  2374.    // Update record number
  2375.    GetClientRect(lpChild->hwnd, &rc);
  2376.    OffsetRect(&rc, cx, cy);
  2377.  
  2378.    rc.left += cxBORDER + g_cxRecord + 2;
  2379.    rc.top   = rc.bottom - g_cyHScroll + 2;
  2380.    rc.right = rc.left + g_cxRecnum;
  2381.  
  2382.    FillRect(hdc, &rc, g_hbrWin);
  2383.  
  2384.    wsprintf(lpChild->lpsz, szRECNUM, lpChild->irow-1 + lpChild->irowPos);
  2385.  
  2386.    hfont = SelectObject(hdc, g_hfontData);
  2387.  
  2388.    TextOut(hdc, rc.left, rc.top, lpChild->lpsz, lstrlen(lpChild->lpsz));
  2389.  
  2390.    SelectObject(hdc, hfont);
  2391.  
  2392.    // Release device context
  2393.    ReleaseDC(lpChild->hwnd, hdc);
  2394.  
  2395.    return;
  2396. }
  2397.  
  2398.  
  2399. /* SetScroll ---------------------------------------------------------------
  2400.    Description: Determine if scroll bars are required and their ranges
  2401.    --------------------------------------------------------------------------*/
  2402. void INTFUNC SetScroll(LPCHILD lpChild)
  2403. {
  2404.    RECT  rc;
  2405.    int   cx, cy;
  2406.    int   row, col;
  2407.  
  2408.    // Use the fInSetScroll flag to prevent recursion due WM_SIZE messages
  2409.    if (lpChild->fInSetScroll)
  2410.       return;
  2411.  
  2412.    lpChild->fInSetScroll = TRUE;
  2413.  
  2414.    // Save current scroll positions
  2415.    row = GetScrollPos(lpChild->hwndVScroll, SB_CTL);
  2416.    col = GetScrollPos(lpChild->hwndHScroll, SB_CTL);
  2417.  
  2418.    // Get window dimensions
  2419.    GetClientRect(lpChild->hwnd, &rc);
  2420.    cx = rc.right - rc.left;
  2421.    cy = rc.bottom - rc.top - g_cy - g_cyHScroll;
  2422.  
  2423.    // Assume no scrolling is required
  2424.    lpChild->fHScroll =
  2425.       lpChild->fVScroll = FALSE;
  2426.  
  2427.    // Include a vertical scroll bar if all rows do not fit
  2428.    lpChild->crowwin = cy / g_cy;
  2429.    if ((UWORD)lpChild->crowwin < lpChild->crowRowset) {
  2430.       lpChild->fVScroll = TRUE;
  2431.       cx -= g_cxVScroll;
  2432.    }
  2433.  
  2434.    // Include a horizontal scroll bar if all columns do not fit
  2435.    lpChild->ccolwin = cx / g_cx;
  2436.    if (lpChild->ccolwin < lpChild->ccols)
  2437.       lpChild->fHScroll = TRUE;
  2438.  
  2439.    // Reset scroll positions if no scrolling is necessary
  2440.    if (!lpChild->fVScroll) row = 0;
  2441.    if (!lpChild->fHScroll) col = 0;
  2442.  
  2443.    // Set scroll ranges, positions, and scroll bar visibility
  2444.    SetScrollRange(lpChild->hwndVScroll, SB_CTL, 0, lpChild->crowRowset - lpChild->crowwin, TRUE);
  2445.    SetScrollRange(lpChild->hwndHScroll, SB_CTL, 0, lpChild->ccols - lpChild->ccolwin,      TRUE);
  2446.  
  2447.    SetScrollPos(lpChild->hwndVScroll, SB_CTL, row, TRUE);
  2448.    SetScrollPos(lpChild->hwndHScroll, SB_CTL, col, TRUE);
  2449.  
  2450.    ShowWindow(lpChild->hwndVScroll, (lpChild->fVScroll ? SW_SHOW : SW_HIDE));
  2451.    ShowWindow(lpChild->hwndHScroll, (lpChild->fHScroll ? SW_SHOW : SW_HIDE));
  2452.  
  2453.    // Add one extra to window depth so no white space is left between
  2454.    // the last full row and bottom record display bar
  2455.    lpChild->crowwin++;
  2456.  
  2457.    // Size and position scroll bars
  2458.    SizeScroll(lpChild);
  2459.  
  2460.    lpChild->fInSetScroll = FALSE;
  2461.  
  2462.    return;
  2463. }
  2464.  
  2465.  
  2466. /* SizeScroll --------------------------------------------------------------
  2467.    Description: Size and position scroll bars
  2468.    --------------------------------------------------------------------------*/
  2469. void INTFUNC SizeScroll(LPCHILD lpChild)
  2470. {
  2471.    RECT  rc;
  2472.    int   cxRecord;
  2473.  
  2474.    GetClientRect(lpChild->hwnd, &rc);
  2475.  
  2476.    // Place vertical scroll bar
  2477.    MoveWindow(lpChild->hwndVScroll,
  2478.               rc.right - g_cxVScroll + 1,
  2479.               rc.top,
  2480.               g_cxVScroll,
  2481.               rc.bottom - g_cyHScroll + 2,
  2482.               TRUE);
  2483.  
  2484.    // Place horizontal scroll bar
  2485.    cxRecord = (2 * cxBORDER) + g_cxRecord + g_cxRecnum + 2;
  2486.  
  2487.    MoveWindow(lpChild->hwndHScroll,
  2488.               rc.left + cxRecord,
  2489.               rc.bottom - g_cyHScroll + 1,
  2490.               (lpChild->fVScroll
  2491.                ? rc.right - g_cxVScroll + 2 - cxRecord
  2492.                : rc.right + 2 - cxRecord),
  2493.               g_cyHScroll,
  2494.               TRUE);
  2495.  
  2496.    return;
  2497. }
  2498.  
  2499.  
  2500. /* UpdateRow ---------------------------------------------------------------
  2501.    Description: Update current (positioned) row
  2502.    --------------------------------------------------------------------------*/
  2503. void INTFUNC UpdateRow(LPCHILD lpChild)
  2504. {
  2505.    HCURSOR  hcur;
  2506.    LPSTR    lpsz;
  2507.    LPSTR    lpszT;
  2508.    SWORD    cb;
  2509.    LPCOL    lpcol;
  2510.    int      cCtls;
  2511.    UWORD    irowPos;
  2512.    int      i;
  2513.  
  2514.    // Ensure the update request is valid
  2515.    if (!lpChild->fDataFetched) {
  2516.       DoMessage(lpChild->hwnd, IDS_NODATAFETCHED);
  2517.       return;
  2518.    }
  2519.  
  2520.    if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_NOROW) {
  2521.       DoMessage(lpChild->hwnd, IDS_NOROWUPDATE);
  2522.       return;
  2523.    }
  2524.  
  2525.    if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_ERROR) {
  2526.       DoMessage(lpChild->hwnd, IDS_ERRORROWUPDATE);
  2527.       return;
  2528.    }
  2529.  
  2530.    if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_DELETED) {
  2531.       DoMessage(lpChild->hwnd, IDS_DELROWUPDATE);
  2532.       return;
  2533.    }
  2534.  
  2535.    if (lpChild->fConcurrency == SQL_CONCUR_READ_ONLY) {
  2536.       DoMessage(lpChild->hwnd, IDS_NOUPDATE);
  2537.       return;
  2538.    }
  2539.  
  2540.    // Count number of updateable columns
  2541.    for (cCtls=0, i=0, lpcol=lpChild->lpcol; i < lpChild->ccol; i++, lpcol++)
  2542.       if (IsUpdateable(lpcol->fSqlType))
  2543.          cCtls++;
  2544.  
  2545.    // Ensure a valid number of updateable columns exist
  2546.    if (cCtls > cMAXCOLS) {
  2547.       DoMessage(lpChild->hwnd, IDS_TOOMANYCOLS);
  2548.       return;
  2549.    }
  2550.  
  2551.    if (!cCtls) {
  2552.       DoMessage(lpChild->hwnd, IDS_CANNOTUPDATE);
  2553.       return;
  2554.    }
  2555.  
  2556.    hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2557.  
  2558.    lpsz  = AllocPtr(2 * cbMAXSQL);
  2559.    lpszT = lpsz + cbMAXSQL;
  2560.  
  2561.    // Build and display update dialog to get new values, then issue update
  2562.    // (use a temporary SQLHSTMT for the update request)
  2563.    if (IDOK == DoDialog(lpChild->hwnd, IDD_UPDATEROW, UpdateDlgProc) &&
  2564.        !DBCError(lpChild->hwnd, SQLAllocHandle(SQL_HANDLE_STMT,g_hdbc, &lpChild->hstmtTmp))) {
  2565.  
  2566.       // Build UPDATE <table> WHERE CURRENT OF <cursor name> statement
  2567.       lstrcpy(lpsz, szUPDATE);
  2568.  
  2569.       GetTableName(lpszT, lpChild->sql);
  2570.       lstrcat(lpsz, lpszT);
  2571.  
  2572.       // Build the Set Clause
  2573.       lstrcat(lpsz, szSET);
  2574.       for (lpcol = lpChild->lpcol, i = 0; i < lpChild->ccol; i++, lpcol++) {
  2575.          if (IsUpdateable(lpcol->fSqlType)) {
  2576.             LPSDWORD lpcb;
  2577.             LPBYTE   lpb;
  2578.  
  2579.             if (i == 0) wsprintf(lpszT, "%s=?", lpcol->szName);
  2580.             else wsprintf(lpszT, ",%s=?", lpcol->szName);
  2581.             lstrcat(lpsz, lpszT);
  2582.  
  2583.             irowPos = lpChild->irowPos - 1;
  2584.  
  2585.             // Get data and count field pointers based on binding type
  2586.             if (ROW_BINDING(lpChild)) {
  2587.                lpb  = lpcol->lpb + (irowPos * lpChild->cbrow);
  2588.                lpcb = (LPSDWORD)(lpb + lpcol->cb);
  2589.             }
  2590.             else {
  2591.                lpb  = lpcol->lpb + (irowPos * lpcol->cb);
  2592.                lpcb = lpcol->lpcb + irowPos;
  2593.             }
  2594.  
  2595.             // set parameter
  2596.             ODBCError(lpChild->hwnd, SQL_HANDLE_STMT, lpChild->hstmtTmp,
  2597.                       SQLSetParam(lpChild->hstmtTmp, (UWORD) (i+1), lpcol->fCType,
  2598.                                   lpcol->fSqlType, (UDWORD) lpcol->cb, (SWORD) 0,
  2599.                                   lpb, lpcb));
  2600.          }
  2601.       }
  2602.  
  2603.       lstrcat(lpsz, szWHERE);
  2604.  
  2605.       lpszT = lpsz + lstrlen(lpsz);
  2606.  
  2607.       if (!STMTError(SQLGetCursorName(lpChild->hstmt, (UCHAR FAR *)lpszT,
  2608.                                       cbMAXSQL, &cb))) {
  2609.  
  2610.          // Issue update request via SQLExecDirect
  2611.          ODBCError(lpChild->hwnd, SQL_HANDLE_STMT, lpChild->hstmtTmp,
  2612.                    SQLExecDirect(lpChild->hstmtTmp, (UCHAR FAR *)lpsz, SQL_NTS));
  2613.       }
  2614.  
  2615.       DBCError(lpChild->hwnd, SQLFreeHandle(SQL_HANDLE_STMT,lpChild->hstmtTmp));
  2616.       lpChild->hstmtTmp = SQL_NULL_HSTMT;
  2617.  
  2618.    }
  2619.  
  2620.    // Refresh entire row-set buffer (saving current row position)
  2621.    irowPos = lpChild->irowPos;
  2622.  
  2623.    lpChild->FetchOP = SQL_FETCH_RELATIVE;
  2624.    lpChild->rrow = 0;
  2625.    Fetch(lpChild);
  2626.  
  2627.    SetPos(lpChild, irowPos);
  2628.  
  2629.    // Repaint the row-set
  2630.    InvalidateRect(lpChild->hwnd, NULL, FALSE);
  2631.  
  2632.    FreePtr(lpsz);
  2633.  
  2634.    SetCursor(hcur);
  2635.  
  2636.    return;
  2637. }
  2638.  
  2639. /*--------------------------------------------------------------------
  2640. Input:  lpChild
  2641. Output: TRUE if the option parameters are valid, FALSE otherwise
  2642. --------------------------------------------------------------------*/
  2643. BOOL INTFUNC ParamValid(LPCHILD lpChild)
  2644. {
  2645.    char szBuffer[128]; // error message
  2646.    char *EndMaxBind, *EndRowset, *EndBind;
  2647.  
  2648.    // the maximum column width
  2649.    wsprintf((LPSTR) szBuffer,
  2650.             "Maximum column width must be at least 1 and at most %d",
  2651.             MAX_MAXBIND);
  2652.  
  2653.    if (lpChild->fMaxBind) { // it's been changed
  2654.       lpChild->crowMaxBind =
  2655.          (SDWORD) strtol((char*) lpChild->szMaxBind, &EndMaxBind, 10);
  2656.       for (; *EndMaxBind && ISWHITE(*EndMaxBind);
  2657.            EndMaxBind = AnsiNext(EndMaxBind));
  2658.    }
  2659.  
  2660.    if (lpChild->fBind) {  // cBind has been changed
  2661.       lpChild->cBind =  (UWORD) strtol((char*) lpChild->szBind, &EndBind, 10);
  2662.       for (; *EndBind && ISWHITE(*EndBind); EndBind = AnsiNext(EndBind));
  2663.    }
  2664.  
  2665.    if (lpChild->fRowset) { // rowset has been changed
  2666.       lpChild->crowRowset =
  2667.          (UWORD) strtol((char*) lpChild->szRowset, &EndRowset, 10);
  2668.       for (; *EndRowset && ISWHITE(*EndRowset);
  2669.            EndRowset = AnsiNext(EndRowset));
  2670.    }
  2671.  
  2672.    while (lpChild->fMaxBind && *EndMaxBind  ||
  2673.           lpChild->crowMaxBind < 1 ||
  2674.           lpChild->crowMaxBind > MAX_MAXBIND ||
  2675.           lpChild->fRowset && *EndRowset ||
  2676.           lpChild->crowRowset < 1 ||
  2677.           lpChild->crowRowset > 4096 ||
  2678.           lpChild->fBind && *EndBind) {
  2679.  
  2680.       // the maximum column width
  2681.       if (lpChild->crowMaxBind < 1 || lpChild->crowMaxBind > MAX_MAXBIND
  2682.           || lpChild->fMaxBind && *EndMaxBind)
  2683.          MessageBox(lpChild->hwnd,  szBuffer, NULL, MB_ICONSTOP);
  2684.  
  2685.       // the rowset size
  2686.       if (lpChild->crowRowset < 1 || lpChild->crowRowset > 4096
  2687.           || lpChild->fRowset && *EndRowset) {
  2688.          char szBuffer[128];
  2689.  
  2690.          LoadString(g_hinst, IDS_BADROWSET, szBuffer, sizeof(szBuffer));
  2691.          MessageBox(lpChild->hwnd, szBuffer, NULL, MB_ICONSTOP);
  2692.       }
  2693.  
  2694.       // number of bound columns
  2695.       if (lpChild->fBind && *EndBind)
  2696.          MessageBox(lpChild->hwnd,
  2697.                     "Invalid number of bound columns", NULL, MB_ICONSTOP);
  2698.  
  2699.       if (IDOK != DoDialog(lpChild->hwnd, IDD_OPTION_DIALOG, OptionsDlgProc))
  2700.          return FALSE;
  2701.  
  2702.       if (lpChild->fMaxBind) { // it's been changed
  2703.          lpChild->crowMaxBind =
  2704.             (SDWORD) strtol((char*) lpChild->szMaxBind, &EndMaxBind, 10);
  2705.          for (; *EndMaxBind && ISWHITE(*EndMaxBind);
  2706.               EndMaxBind = AnsiNext(EndMaxBind));
  2707.       }
  2708.  
  2709.       if (lpChild->fBind) {  // cBind has been changed
  2710.          lpChild->cBind =
  2711.             (UWORD) strtol((char*) lpChild->szBind, &EndBind, 10);
  2712.  
  2713.          for (; *EndBind && ISWHITE(*EndBind); EndBind = AnsiNext(EndBind));
  2714.       }
  2715.  
  2716.       if (lpChild->fRowset) { // rowset has been changed
  2717.          lpChild->crowRowset =
  2718.             (UWORD) strtol((char*) lpChild->szRowset, &EndRowset, 10);
  2719.          for (; *EndRowset && ISWHITE(*EndRowset);
  2720.               EndRowset = AnsiNext(EndRowset));
  2721.       }
  2722.    }
  2723.  
  2724.    return TRUE;
  2725.  
  2726. }
  2727.