home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Internet Business Development Kit / PRODUCT_CD.iso / sqlsvr / odbcsdk / samples / crsrdemo / child.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-07  |  70.7 KB  |  2,697 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_NULL_HENV, SQL_NULL_HDBC, 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.     HSTMT    hstmt;
  111.     LPCHILD    lpChild;
  112.     char    sz[cbSTRLEN];
  113.  
  114.     // Allocate ODBC HSTMT and set cursor name
  115.     if (DBCError(hwnd, SQLAllocStmt(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_NULL_HENV, SQL_NULL_HDBC, 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_DATE:          return SQL_C_DATE;
  602.         case SQL_TIME:          return SQL_C_TIME;
  603.         case SQL_TIMESTAMP:     return SQL_C_TIMESTAMP;
  604.     }
  605.  
  606.     return SQL_C_CHAR;
  607. }
  608.  
  609.  
  610. /* DeleteRow ---------------------------------------------------------------
  611.     Description: Delete the current (positioned) row
  612. --------------------------------------------------------------------------*/
  613. void INTFUNC DeleteRow(LPCHILD lpChild)
  614. {
  615.     HCURSOR    hcur;
  616.     LPSTR    lpsz;
  617.     LPSTR    lpszT;
  618.     SWORD    cb;
  619.  
  620.     // Ensure the delete request is valid
  621.     if (!lpChild->fDataFetched) {
  622.         DoMessage(lpChild->hwnd, IDS_NODATAFETCHED);
  623.         return;
  624.     }
  625.  
  626.     if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_NOROW) {
  627.         DoMessage(lpChild->hwnd, IDS_NOROWDELETE);
  628.         return;
  629.     }
  630.  
  631.     if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_ERROR) {
  632.         DoMessage(lpChild->hwnd, IDS_ERRORROWDELETE);
  633.         return;
  634.     }
  635.  
  636.     if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_DELETED) {
  637.         DoMessage(lpChild->hwnd, IDS_DELROWDELETE);
  638.         return;
  639.     }
  640.  
  641.     if (lpChild->fConcurrency == SQL_CONCUR_READ_ONLY) {
  642.         DoMessage(lpChild->hwnd, IDS_NOUPDATE);
  643.         return;
  644.     }
  645.  
  646.     lpsz  = AllocPtr(2 * cbMAXSQL);
  647.     lpszT = lpsz + cbMAXSQL;
  648.  
  649.     // Verify the request and allocate a new (temporary) HSTMT for the delete
  650.     LoadString(g_hinst, IDS_DELETEROW, lpsz, cbMAXSQL);
  651.     if (IDYES == MessageBox(lpChild->hwnd, lpsz,
  652.                             g_szTITLE, MB_ICONQUESTION | MB_YESNO) &&
  653.         !DBCError(lpChild->hwnd, SQLAllocStmt(g_hdbc, &lpChild->hstmtTmp))) {
  654.  
  655.         // Build DELETE <table> WHERE CURRENT OF <cursor> statement
  656.         lstrcpy(lpsz, szDELETE);
  657.  
  658.         GetTableName(lpszT, lpChild->sql);
  659.         lstrcat(lpsz, lpszT);
  660.  
  661.         lstrcat(lpsz, szWHERE);
  662.  
  663.         lpszT = lpsz + lstrlen(lpsz);
  664.  
  665.         hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  666.  
  667.         if (!STMTError(SQLGetCursorName(lpChild->hstmt, (UCHAR FAR *)lpszT,
  668.                                         cbMAXSQL, &cb))) {
  669.  
  670.             // Issue the request via SQLPrepare/SQLExecute
  671.             if (!ODBCError(lpChild->hwnd, SQL_NULL_HENV,
  672.                         SQL_NULL_HDBC,
  673.                         lpChild->hstmtTmp,
  674.                         SQLPrepare(lpChild->hstmtTmp, (UCHAR FAR *)lpsz, SQL_NTS)) &&
  675.                 !ODBCError(lpChild->hwnd, SQL_NULL_HENV,
  676.                         SQL_NULL_HDBC,
  677.                         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, SQLFreeStmt(lpChild->hstmtTmp, SQL_DROP));
  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.             } else
  766.             {
  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.             {
  816.                 lpChild->FetchOP = SQL_FETCH_ABSOLUTE;
  817. #ifdef THREAD
  818.                 FetchThread(lpChild);
  819. #else
  820.                 Fetch(lpChild);
  821. #endif
  822.             }
  823.             break;
  824.  
  825.         case IDM_FETCH_RELATIVE:
  826.             if (IDOK == DoDialog(lpChild->hwnd, IDD_RELATIVE, RelDlgProc))
  827.             {
  828.                 lpChild->FetchOP = SQL_FETCH_RELATIVE;
  829. #ifdef THREAD
  830.                 FetchThread(lpChild);
  831. #else
  832.                 Fetch(lpChild);
  833. #endif
  834.             }
  835.             break;
  836.  
  837.         case IDM_FETCH_GET:
  838. #ifdef THREAD
  839.             GetDataThread(lpChild);
  840. #else
  841.             GetData(lpChild);
  842. #endif
  843.             break;
  844.  
  845.         case IDM_FETCH_DELETEROW:
  846. #ifdef THREAD
  847.             DeleteRowThread(lpChild);
  848. #else
  849.             DeleteRow(lpChild);
  850. #endif
  851.             break;
  852.  
  853.         case IDM_FETCH_UPDATEROW:
  854. #ifdef THREAD
  855.             UpdateRowThread(lpChild);
  856. #else
  857.             UpdateRow(lpChild);
  858. #endif
  859.             break;
  860.         default:
  861.             return FALSE;
  862.     }
  863.  
  864.     AdjustMenus();
  865.     return TRUE;
  866. }
  867.  
  868. #ifdef THREAD
  869. void INTFUNC DoSQLThread(LPCHILD lpChild)
  870. {
  871.     DWORD dwThreadId;
  872.  
  873.     EnterCriticalSection (&lpChild->ThreadCreation);
  874.     lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)DoSQL,
  875.         (LPVOID)lpChild, 0, &dwThreadId);
  876.     LeaveCriticalSection (&lpChild->ThreadCreation);
  877. }
  878.  
  879. void INTFUNC FetchThread(LPCHILD lpChild)
  880. {
  881.     DWORD dwThreadId;
  882.  
  883.     EnterCriticalSection (&lpChild->ThreadCreation);
  884.     lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)Fetch,
  885.         (LPVOID)lpChild, 0, &dwThreadId);
  886.     LeaveCriticalSection (&lpChild->ThreadCreation);
  887. }
  888.  
  889. void INTFUNC GetDataThread(LPCHILD lpChild)
  890. {
  891.     DWORD dwThreadId;
  892.  
  893.     EnterCriticalSection (&lpChild->ThreadCreation);
  894.     lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)GetData,
  895.         (LPVOID)lpChild, 0, &dwThreadId);
  896.     LeaveCriticalSection (&lpChild->ThreadCreation);
  897. }
  898.  
  899. void INTFUNC UpdateRowThread(LPCHILD lpChild)
  900. {
  901.     DWORD dwThreadId;
  902.  
  903.     EnterCriticalSection (&lpChild->ThreadCreation);
  904.     lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)UpdateRow,
  905.         (LPVOID)lpChild, 0, &dwThreadId);
  906.     LeaveCriticalSection (&lpChild->ThreadCreation);
  907. }
  908.  
  909. void INTFUNC DeleteRowThread(LPCHILD lpChild)
  910. {
  911.     DWORD dwThreadId;
  912.  
  913.     EnterCriticalSection (&lpChild->ThreadCreation);
  914.     lpChild->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)DeleteRow,
  915.         (LPVOID)lpChild, 0, &dwThreadId);
  916.     LeaveCriticalSection (&lpChild->ThreadCreation);
  917. }
  918. #endif
  919.  
  920.  
  921. /* DoSQL -------------------------------------------------------------------
  922.     Description: Issue a SQL statement and prepare for fetching data
  923. --------------------------------------------------------------------------*/
  924. void INTFUNC DoSQL(LPCHILD lpChild)
  925. {
  926.     RETCODE    rc;
  927.  
  928.     // Prepare the statement
  929.  
  930.     if (PrepareStmt(lpChild) == SQL_ERROR)
  931.         return;
  932.  
  933.     switch(lpChild->dwOperation)
  934.     {
  935.         case     OPER_SELECT:
  936.             // Issue the request via SQLExecDirect
  937.             Async(SQLExecDirect(lpChild->hstmt,
  938.                                 (UCHAR FAR *)lpChild->sql,
  939.                                 lstrlen(lpChild->sql)));
  940.             break;
  941.  
  942.         case    OPER_TYPES:
  943.             // Issue a GetTypeInfo request
  944.             Async(SQLGetTypeInfo(lpChild->hstmt, SQL_ALL_TYPES));
  945.             break;
  946.                         
  947.  
  948.         case    IDC_TABLE_RAD_TABLE:
  949.             if (!*lpChild->szTable &&
  950.                 ((!*lpChild->szUser && strcmp(lpChild->szQualifier, "%") == 0)||
  951.                 (!*lpChild->szQualifier && strcmp(lpChild->szUser, "%") == 0)))
  952.             {    // Special qualifier enumeration
  953.                 Async(SQLTables(lpChild->hstmt,
  954.                            lpChild->szQualifier, SQL_NTS,
  955.                            lpChild->szUser, SQL_NTS,
  956.                            lpChild->szTable, SQL_NTS,
  957.                            lpChild->szType,  SQL_NTS));
  958.             }
  959.             else
  960.             {    // Normal SQLTables call
  961.                 Async(SQLTables(lpChild->hstmt,
  962.                            NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  963.                            NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  964.                            NULLIFEMPTY(lpChild->szTable),  SQL_NTS,
  965.                            NULLIFEMPTY(lpChild->szType),  SQL_NTS));
  966.             }
  967.             break;
  968.  
  969.         case    IDC_TABLE_RAD_PRIV:
  970.             Async(SQLTablePrivileges(lpChild->hstmt,
  971.                                      NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  972.                                      NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  973.                                      NULLIFEMPTY(lpChild->szTable),    SQL_NTS));
  974.  
  975.             break;
  976.  
  977.         case    IDC_TABLE_RAD_STATISTICS:
  978.             Async(SQLStatistics(lpChild->hstmt,
  979.                                      NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  980.                                      NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  981.                                      NULLIFEMPTY(lpChild->szTable),    SQL_NTS,
  982.                                      SQL_INDEX_ALL,        // XXX
  983.                                      SQL_QUICK));        // XXX
  984.             break;
  985.  
  986.  
  987.  
  988.         case    IDC_TABLE_RAD_PROC:
  989.             Async(SQLProcedures(lpChild->hstmt,
  990.                                      NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  991.                                      NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  992.                                      NULLIFEMPTY(lpChild->szTable),    SQL_NTS));
  993.  
  994.             break;
  995.  
  996.         case    IDC_TABLE_RAD_COLUMN:
  997.             Async(SQLColumns(lpChild->hstmt,
  998.                                      NULLIFEMPTY(lpChild->szQualifier), SQL_NTS,
  999.                                      NULLIFEMPTY(lpChild->szUser), SQL_NTS,
  1000.                                      NULLIFEMPTY(lpChild->szTable),    SQL_NTS,
  1001.                                      NULLIFEMPTY(lpChild->szColName), SQL_NTS));
  1002.  
  1003.     }
  1004.  
  1005.     if (STMTError(rc))
  1006.         return;
  1007.  
  1008.     ProcessResults(lpChild);
  1009.  
  1010.     if (lpChild->dwGuiFlags & GUIF_ALWAYSFETCH)
  1011.     {
  1012.         lpChild->FetchOP = SQL_FETCH_FIRST;
  1013.         Fetch(lpChild);
  1014.     }
  1015.     AdjustMenus();
  1016.     return;
  1017.  
  1018. }
  1019.  
  1020. /* ProcessResults---------------------------------------------------------------
  1021.     Description: Process the results from a query or statement
  1022. --------------------------------------------------------------------------*/
  1023. void INTFUNC ProcessResults(LPCHILD lpChild)
  1024. {
  1025.     LPINT    lptab;
  1026.     LPCOL    lpcol;
  1027.     LPBYTE    lpb;
  1028.     SDWORD    cbMsg;
  1029.     SDWORD    cbNull;
  1030.     int        nLastTab;
  1031.     SWORD    i;
  1032.     RETCODE    rc;
  1033.  
  1034.     // Retrieve number of columns in the result set
  1035.     Async(SQLNumResultCols(lpChild->hstmt, &i));
  1036.     if (STMTError(rc))
  1037.         return;
  1038.     lpChild->ccol = ((UWORD)i < lpChild->cBind || lpChild->fBindAll
  1039.                         ? i
  1040.                         : lpChild->cBind);
  1041.  
  1042.     // If a result set exists, continue; otherwise, return immediately
  1043.     if (lpChild->ccol)
  1044.         lpChild->fResultSetExists = TRUE;
  1045.     else
  1046.         return;
  1047.  
  1048.     // Allocate painting related storage and row status array
  1049.     lpChild->rglpv     = (LPVOID)AllocPtr(sizeof(LPVOID) * lpChild->ccol);
  1050.     lpChild->lpnTabs   = (LPINT)AllocPtr(sizeof(int) * (lpChild->ccol+1));
  1051.     lpChild->lpcol     = (LPCOL)AllocPtr(sizeof(COL) * lpChild->ccol);
  1052.     lpChild->lpfStatus = (LPUWORD)AllocPtr((DWORD) sizeof(UWORD) 
  1053.                                         * lpChild->crowRowset);
  1054.  
  1055.     for (i=1, lpcol=lpChild->lpcol; i <= lpChild->ccol; i++, lpcol++) {
  1056.         lpcol->lpb  = NULL;
  1057.         lpcol->lpcb = NULL;
  1058.     }
  1059.  
  1060.     cbMsg  = lstrlen(g_szRowDeleted);
  1061.     cbNull = lstrlen(g_szNull);
  1062.     cbMsg  = max(cbMsg, lstrlen(g_szNoRow));
  1063.     cbMsg  = max(cbMsg, cbNull);
  1064.  
  1065.     // Initialize row width (in bytes) and total number of characters per line
  1066.     lpChild->cbrow = 0;
  1067.     lpChild->ccols = 0;
  1068.  
  1069.     lpcol = lpChild->lpcol;
  1070.     lptab = lpChild->lpnTabs;
  1071.  
  1072.     nLastTab =
  1073.     *lptab++ = cxBORDER;
  1074.  
  1075.     // For each bound column
  1076.     //   a) Get column attributes (e.g., name, size, data type)
  1077.     //   b) Add column to physical and display row widths
  1078.     //   c) Determine tab location
  1079.     for (i=1; i <= lpChild->ccol; i++, lpcol++, lptab++) {
  1080.  
  1081.         // Get column name
  1082.         Async(SQLColAttributes(lpChild->hstmt, i,
  1083.                                 SQL_COLUMN_NAME,
  1084.                                 lpcol->szName, sizeof(lpcol->szName), NULL,
  1085.                                 NULL));
  1086.         if (STMTError(rc)) {
  1087.             FreeStmt(SQL_DROP, lpChild);
  1088.             return;
  1089.         }
  1090.  
  1091.         // Get actual column length (number of physical data bytes)
  1092.         Async(SQLColAttributes(lpChild->hstmt, i,
  1093.                                 SQL_COLUMN_LENGTH,
  1094.                                 NULL, 0, NULL,
  1095.                                 &lpcol->cb));
  1096.         if (STMTError(rc)) {
  1097.             FreeStmt(SQL_DROP, lpChild);
  1098.             return;
  1099.         }
  1100.  
  1101.         // Get display width
  1102.         Async(SQLColAttributes(lpChild->hstmt, i,
  1103.                                SQL_COLUMN_DISPLAY_SIZE,
  1104.                                NULL, 0, NULL,
  1105.                                &lpcol->cbc));
  1106.         if (STMTError(rc))
  1107.         {
  1108.             FreeStmt(SQL_DROP, lpChild);
  1109.             return;
  1110.         }
  1111.  
  1112.  
  1113.         // if the display size is too big, force to the the maximum
  1114.         if (lpcol->cbc > lpChild->crowMaxBind) 
  1115.             lpcol->cbc = lpChild->crowMaxBind;
  1116.  
  1117.         // Ensure display width is wide enough for:
  1118.         //   a) Column name
  1119.         //   b) Null string
  1120.         //   c) Row status (for the first column only)
  1121.         if (lpcol->cbc < cbMsg)
  1122.             lpcol->cbc = cbMsg;
  1123.  
  1124.         if (lstrlen(lpcol->szName) > lpcol->cbc)
  1125.             lpcol->cbc = lstrlen(lpcol->szName);
  1126.  
  1127.         lpcol->cbc++;
  1128.  
  1129.         // Get column SQL type
  1130.         Async(SQLColAttributes(lpChild->hstmt, i,
  1131.                                 SQL_COLUMN_TYPE,
  1132.                                 NULL, 0, NULL,
  1133.                                 (SDWORD FAR*) &lpcol->fSqlType));
  1134.         if (STMTError(rc)) {
  1135.             FreeStmt(SQL_DROP, lpChild);
  1136.             return;
  1137.         }
  1138.  
  1139.         // Determine target C type
  1140.         lpcol->fCType = CvtSqlToCType(lpcol->fSqlType);
  1141.  
  1142.         // For hard to handle C types, let the driver convert to character
  1143.         if (lpcol->fCType == SQL_C_BIT    ||
  1144.             lpcol->fCType == SQL_C_BINARY ||
  1145.             lpcol->fCType == SQL_C_DATE   ||
  1146.             lpcol->fCType == SQL_C_TIME   ||
  1147.             lpcol->fCType == SQL_C_TIMESTAMP) {
  1148.             lpcol->fCType = SQL_C_CHAR;
  1149.             lpcol->cb = lpcol->cbc;
  1150.         }
  1151.  
  1152.         // Determine next column tab (based on column width plus border)
  1153.         nLastTab =
  1154.         *lptab     = nLastTab + ((int)lpcol->cbc * g_cx) + (2 * cxBORDER);
  1155.  
  1156.         // Set maximum column length for character data
  1157.         if( lpcol->cb > lpChild->crowMaxBind &&
  1158.             lpcol->fCType == SQL_C_CHAR )
  1159.             lpcol->cb = lpChild->crowMaxBind;
  1160.  
  1161.         if( lpcol->fCType == SQL_C_CHAR )
  1162.             lpcol->cb++;
  1163.  
  1164.         // Increment total phsyical row width and display width
  1165.         lpChild->cbrow += lpcol->cb;
  1166.         lpChild->ccols += (int)lpcol->cbc;
  1167.  
  1168.         cbMsg = cbNull;
  1169.     }
  1170.  
  1171.     // Include a count field for each bound column in physical row width
  1172.     lpChild->cbrow += lpChild->ccol * sizeof(SDWORD);
  1173.  
  1174.     // Add intra-column border amounts to total character width
  1175.     lpChild->ccols += (lpChild->ccol * (2 * cxBORDER)) / g_cx;
  1176.  
  1177.     // For each column, include an element in the format string
  1178.     lpb  = (LPBYTE)lpChild->szFmt;
  1179.     for (i=0, lpcol=lpChild->lpcol; i < lpChild->ccol; i++, lpcol++) {
  1180.         *(lpb++) = '\t';
  1181.         *(lpb++) = '%';
  1182.         *(lpb++) = 's';
  1183.     }
  1184.     *lpb = 0;
  1185.  
  1186.     // If row-wise binding, allocate a buffer; switch to column-wise
  1187.     // if the entire row-set cannot fit into 64K minus cursor library
  1188.     // headers
  1189.     lpb = NULL;
  1190.     if (ROW_BINDING(lpChild)) {
  1191.         if ((lpChild->cbrow * lpChild->crowRowset) > 65500L) {
  1192.             DoMessage(lpChild->hwnd, IDS_BIGROWSET);
  1193.             lpChild->fBindByRow = IDC_RADIO_BINDCOL ;
  1194.         }
  1195.         else {
  1196.             lpb          =
  1197.             lpChild->lpb = (LPBYTE)AllocPtr(lpChild->cbrow *
  1198.                                             lpChild->crowRowset);
  1199.         }
  1200.     }
  1201.  
  1202.     // Set binding type
  1203.     if (STMTError(SQLSetStmtOption(lpChild->hstmt,
  1204.                                     SQL_BIND_TYPE,
  1205.                                     ROW_BINDING(lpChild)
  1206.                                         ? lpChild->cbrow
  1207.                                         : SQL_BIND_BY_COLUMN)))
  1208.         return;
  1209.  
  1210.     // Finally, for each bound column, bind the data value
  1211.     for (i=1, lpcol=lpChild->lpcol; i <= lpChild->ccol; i++, lpcol++) {
  1212.         if (!ROW_BINDING(lpChild)) {
  1213.             lpcol->lpb    = (LPBYTE)AllocPtr(lpcol->cb * lpChild->crowRowset);
  1214.             lpcol->lpcb = (LPSDWORD)AllocPtr((DWORD) sizeof(SDWORD) *
  1215.                                             lpChild->crowRowset);
  1216.         }
  1217.         else {
  1218.             lpcol->lpb    = (LPBYTE)lpb;
  1219.             lpcol->lpcb = (LPSDWORD)(lpb + lpcol->cb);
  1220.             lpb += lpcol->cb + sizeof(SDWORD);
  1221.         }
  1222.  
  1223.         if (STMTError(SQLBindCol(lpChild->hstmt, i, (SWORD)lpcol->fCType,
  1224.                                 (PTR)(lpcol->lpb),
  1225.                                 lpcol->cb,
  1226.                                 lpcol->lpcb)))
  1227.             return;
  1228.     }
  1229.  
  1230.     return;
  1231. }
  1232.  
  1233.  
  1234. /* PrepareStmt -------------------------------------------------------------
  1235.     Description: Prepare a statement for future processing
  1236.  
  1237.     Returns:    SQL_ERROR if an error occurs
  1238. --------------------------------------------------------------------------*/
  1239. RETCODE INTFUNC PrepareStmt(LPCHILD lpChild)
  1240. {
  1241.  
  1242.     // Close the statement and drop bindings
  1243.     FreeStmt(SQL_CLOSE,  lpChild);
  1244.     FreeStmt(SQL_UNBIND, lpChild);
  1245.  
  1246.     // Set scroll options
  1247.     if (!(SUCCESS(SQLSetStmtOption(lpChild->hstmt,
  1248.                                     SQL_CURSOR_TYPE,
  1249.                                     lpChild->crowKeyset))))
  1250.         lpChild->fNoCursorType = TRUE;
  1251.  
  1252.      if (!(SUCCESS(SQLSetStmtOption(lpChild->hstmt,
  1253.                                     SQL_CONCURRENCY,
  1254.                                     lpChild->fConcurrency))))
  1255.         lpChild->fNoConcurrency = TRUE;
  1256.  
  1257.  
  1258.     if (STMTError(SQLSetStmtOption(lpChild->hstmt,
  1259.                                     SQL_ROWSET_SIZE, 
  1260.                                     lpChild->crowRowset)))
  1261.         return SQL_ERROR;
  1262.  
  1263.     // Set async mode (if supported by the driver)
  1264.     if (g_fAsyncSupported &&
  1265.         STMTError(SQLSetStmtOption(lpChild->hstmt,
  1266.                                     SQL_ASYNC_ENABLE,
  1267.                                     (lpChild->fAsync ? 1 : 0))))
  1268.         return SQL_ERROR;
  1269.  
  1270.  
  1271.  
  1272.     return FALSE;
  1273.  
  1274. }
  1275.  
  1276. /* Cancel -------------------------------------------------------------------
  1277.     Description: Issue cancel request
  1278. --------------------------------------------------------------------------*/
  1279. void INTFUNC Cancel(LPCHILD lpChild)
  1280. {
  1281.     RETCODE rc;
  1282.  
  1283.     // Call SQLCancel
  1284.     if (lpChild->hstmtTmp != SQL_NULL_HSTMT) {
  1285.         rc = SQLCancel(lpChild->hstmtTmp);    //    Cancel temp hstmt
  1286.         STMTError(rc);
  1287.     }
  1288.     else {
  1289.         rc = SQLCancel(lpChild->hstmt);
  1290.         STMTError(rc);
  1291.         FreeStmt(SQL_CLOSE,  lpChild);    // Cleanup statement
  1292.     }
  1293.  
  1294.     return;
  1295. }
  1296.  
  1297.  
  1298. /* Fetch -------------------------------------------------------------------
  1299.     Description: Issue fetch request
  1300. --------------------------------------------------------------------------*/
  1301. void INTFUNC Fetch(LPCHILD lpChild)
  1302. {
  1303.     RETCODE    rc;
  1304.     UDWORD    crow;
  1305.     HCURSOR    hcur;
  1306.     SDWORD    sdwIrowLast = lpChild->irow;
  1307.     SDWORD    sdwRowsAffected;
  1308.     SDWORD irow;
  1309.     UWORD fFetchType;
  1310.  
  1311. static UWORD    fFetchTypeLast = SQL_FETCH_NEXT;
  1312.  
  1313.     fFetchType = lpChild->FetchOP;
  1314.     if (fFetchType == SQL_FETCH_ABSOLUTE)
  1315.         irow = lpChild->arow;
  1316.     else if (fFetchType == SQL_FETCH_RELATIVE)
  1317.         irow = lpChild->rrow;
  1318.     else
  1319.         irow = 0;
  1320.     if (!(lpChild->fResultSetExists))
  1321.     {
  1322.         STMTError(SQLRowCount(lpChild->hstmt, &sdwRowsAffected));
  1323.  
  1324.         if (sdwRowsAffected > -1)
  1325.         {
  1326.             UCHAR szBuffer[200];
  1327.             wsprintf(szBuffer, 
  1328.                     (sdwRowsAffected == 1) ? szRowAffected:szDataAffected,
  1329.                      sdwRowsAffected);
  1330.  
  1331.             MessageBox(lpChild->hwnd, 
  1332.                        szBuffer,
  1333.                        szNoDataTitle,
  1334.                        MB_ICONINFORMATION);
  1335.         }
  1336.         else
  1337.         {
  1338.                 MessageBox(    lpChild->hwnd,
  1339.                             szNoDataTitle,
  1340.                             szNoData,
  1341.                             MB_ICONINFORMATION);
  1342.         }
  1343.         return;
  1344.     }
  1345.  
  1346.     hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1347.  
  1348.     // Call SQLExtendedFetch
  1349.     Async(SQLExtendedFetch(lpChild->hstmt, fFetchType,
  1350.                             irow, &crow, lpChild->lpfStatus));
  1351.  
  1352.     if ((rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) &&
  1353.             lpChild->irowPos > crow)
  1354.     {
  1355.         SetPos(lpChild, (UWORD) crow);
  1356.     }
  1357.  
  1358.     if (!STMTError(rc) && rc != SQL_NO_DATA_FOUND) {
  1359.  
  1360.         // Reset columns retrieved count
  1361.         lpChild->ccolRetrieved = (UWORD)lpChild->ccol;
  1362.  
  1363.         // If this is the first fetch, initialize child variables
  1364.         if (!lpChild->fDataFetched) {
  1365.  
  1366.             lpChild->fDataFetched = TRUE;
  1367.  
  1368.             lpChild->irow    = -((SDWORD)lpChild->crowRowset);
  1369.             lpChild->irowPos = 1;
  1370.  
  1371.             SetScrollPos(lpChild->hwndVScroll, SB_CTL, 0, FALSE);
  1372.             SetScrollPos(lpChild->hwndHScroll, SB_CTL, 0, FALSE);
  1373.  
  1374.             SetScroll(lpChild);
  1375.             AllocClipRgn(lpChild);
  1376.         }
  1377.  
  1378.         // Otherwise, maintain current row position in the row-set
  1379.         else if (lpChild->fConcurrency != SQL_CONCUR_READ_ONLY &&
  1380.                  lpChild->crowKeyset != SQL_CURSOR_FORWARD_ONLY)
  1381.             SetPos(lpChild, lpChild->irowPos);
  1382.  
  1383.         if (fFetchType == SQL_FETCH_RESUME)
  1384.             fFetchType = fFetchTypeLast;
  1385.  
  1386.         // Adjust absolute row number by fetch amount
  1387.         switch (fFetchType) {
  1388.             case SQL_FETCH_FIRST:
  1389.                 lpChild->irow = 1;
  1390.                 break;
  1391.  
  1392.             case SQL_FETCH_PRIOR:
  1393.                 lpChild->irow -= crow;
  1394.                 if (lpChild->irow < 1)
  1395.                     lpChild->irow = 1;
  1396.                 break;
  1397.  
  1398.             case SQL_FETCH_NEXT:
  1399.                 lpChild->irow += lpChild->crowCurrent;  // Previous RS
  1400.                 break;
  1401.  
  1402.             case SQL_FETCH_LAST: {
  1403.                     SDWORD iRowT;   // scratch variable
  1404.  
  1405.                     if (STMTError(SQLGetStmtOption(lpChild->hstmt,
  1406.                         SQL_ROW_NUMBER, &iRowT))) {
  1407.                     // the cursor library does not support SQL_ROW_NUMBER,
  1408.                     // so use the following calculation. The  result might
  1409.                     // be incorrect
  1410.                         STMTError(SQLRowCount(lpChild->hstmt, &iRowT));
  1411.                         lpChild->irow += (iRowT - crow);
  1412.                         if (lpChild->irow < 1) lpChild->irow = 1;
  1413.  
  1414.                     } else {    // the cursor library supports SQL_ROW_NUMBER
  1415.                         lpChild->irow = iRowT;
  1416.                     }
  1417.                 }
  1418.                 break;
  1419.  
  1420.             case SQL_FETCH_RELATIVE:
  1421.                 lpChild->irow += irow;
  1422.                 if (lpChild->irow < 1)
  1423.                     lpChild->irow = 1;
  1424.                 break;
  1425.  
  1426.             case SQL_FETCH_ABSOLUTE:
  1427.                 if (irow > 0)
  1428.                     lpChild->irow = irow;
  1429.                 else {
  1430.                     STMTError(SQLRowCount(lpChild->hstmt, &lpChild->irow));
  1431.                     lpChild->irow = lpChild->irow + irow + 1;
  1432.                     if (lpChild->irow < 1) lpChild->irow = 1;
  1433.                 }
  1434.                 break;
  1435.         }
  1436.  
  1437.         // Repaint window
  1438.         InvalidateRect(lpChild->hwnd, NULL, FALSE);
  1439.  
  1440.         lpChild->crowCurrent = crow;
  1441.     }
  1442.  
  1443.     fFetchTypeLast = fFetchType;
  1444.  
  1445.     AdjustMenus();
  1446.     SetCursor(hcur);
  1447.     return;
  1448. }
  1449.  
  1450.  
  1451. /* FreeStmt ----------------------------------------------------------------
  1452.     Description: Free HSTMT and reset associated variables of a child window
  1453.                  NOTE: Only SQL_CLOSE, SQL_DROP, and SQL_UNBIND are valid 
  1454. --------------------------------------------------------------------------*/
  1455. void INTFUNC FreeStmt(UWORD fOption, LPCHILD lpChild)
  1456. {
  1457.     SWORD    i;
  1458.     LPCOL    lpcol;
  1459.     
  1460.     if (!lpChild->hstmt)
  1461.         return;
  1462.  
  1463.     // Issue the real SQLFreeStmt call
  1464.     if (STMTError(SQLFreeStmt(lpChild->hstmt, fOption)))
  1465.         return;
  1466.     
  1467.     // Drop data buffers for SQL_DROP and SQL_UNBIND requests
  1468.     if (fOption == SQL_DROP || fOption == SQL_UNBIND) {
  1469.         if (!ROW_BINDING(lpChild)) {
  1470.             for (i=0, lpcol=lpChild->lpcol; i < lpChild->ccol; i++, lpcol++) {
  1471.                 FreePtr(lpcol->lpb);  lpcol->lpb  = NULL;
  1472.                 FreePtr(lpcol->lpcb); lpcol->lpcb = NULL;
  1473.             }
  1474.         }
  1475.         else if (lpChild->lpb) {
  1476.             FreePtr(lpChild->lpb);
  1477.             lpChild->lpb = NULL;
  1478.         }
  1479.     }
  1480.  
  1481.     // Only drop memory for SQL_UNBIND requests
  1482.     if (fOption == SQL_UNBIND)
  1483.         return;
  1484.  
  1485.     // Clear HSTMT handle for SQL_DROP
  1486.     if (fOption == SQL_DROP)
  1487.         lpChild->hstmt = SQL_NULL_HSTMT;
  1488.         
  1489.     // Always reset and free result set related variables
  1490.     lpChild->fResultSetExists = FALSE;
  1491.     lpChild->ccol         = 0;
  1492.     lpChild->fDataFetched = FALSE;
  1493.     lpChild->cbrow        = 0;
  1494.     lpChild->ccols        = 0;
  1495.  
  1496.     FreePtr(lpChild->rglpv);     lpChild->rglpv     = NULL;
  1497.     FreePtr(lpChild->lpnTabs);   lpChild->lpnTabs   = NULL;
  1498.     FreePtr(lpChild->lpcol);     lpChild->lpcol     = NULL;
  1499.     FreePtr(lpChild->lpfStatus); lpChild->lpfStatus = NULL;
  1500.  
  1501.     if (lpChild->hrgn) {
  1502.         DeleteObject(lpChild->hrgn);
  1503.         lpChild->hrgn = NULL;
  1504.     }
  1505.  
  1506.     if (lpChild->hwnd) {
  1507.         lpChild->fVScroll = FALSE;
  1508.         lpChild->fHScroll = FALSE;
  1509.  
  1510.         ShowWindow(lpChild->hwndVScroll, SW_HIDE);
  1511.         ShowWindow(lpChild->hwndHScroll, SW_HIDE);
  1512.         InvalidateRect(lpChild->hwnd, NULL, FALSE);
  1513.     }
  1514.     return;
  1515. }
  1516.  
  1517.  
  1518. /* GetCurrentValue ---------------------------------------------------------
  1519.     Description: Convert to character and return column data from current
  1520.                  row
  1521. --------------------------------------------------------------------------*/
  1522. #pragma optimize("ceglntw", off)
  1523. void INTFUNC GetCurrentValue(LPSTR lpsz, LPCOL lpcol, LPCHILD lpChild)
  1524. {
  1525.     LPBYTE        lpb;
  1526.     SDWORD FAR    *lpcb;
  1527.     UWORD        irowPos;
  1528.  
  1529.     irowPos = lpChild->irowPos - 1;
  1530.  
  1531.     // Get data and count field pointers based on binding type
  1532.     if (ROW_BINDING(lpChild)) {
  1533.         lpb  = lpcol->lpb + (irowPos * lpChild->cbrow);
  1534.         lpcb = (LPSDWORD)(lpb + lpcol->cb);
  1535.     }
  1536.     else {
  1537.         lpb  = lpcol->lpb + (irowPos * lpcol->cb);
  1538.         lpcb = lpcol->lpcb + irowPos;
  1539.     }
  1540.  
  1541.     // Convert column data to character using the supplied buffer
  1542.     if (*lpcb == SQL_NULL_DATA) {
  1543.         lstrcpy(lpsz, g_szNull);
  1544.         return;
  1545.     }
  1546.  
  1547.     switch (lpcol->fSqlType) {
  1548.  
  1549.         case SQL_CHAR:
  1550.         case SQL_VARCHAR:
  1551.             lstrcpy(lpsz, (LPSTR)lpb);
  1552.             break;
  1553.  
  1554.         case SQL_INTEGER:
  1555.         case SQL_SMALLINT:
  1556.         case SQL_TINYINT: {
  1557.             long    l;
  1558.  
  1559.             l = (lpcol->fSqlType == SQL_INTEGER
  1560.                     ? *((DWORD FAR *)lpb)
  1561.                     : lpcol->fSqlType == SQL_SMALLINT
  1562.                         ? *((WORD FAR *)lpb)
  1563.                         : *((UCHAR FAR *)lpb));
  1564.  
  1565.             _ltoa(l, lpsz, 10);
  1566.             break;
  1567.         }
  1568.  
  1569.         case SQL_REAL:
  1570.         case SQL_FLOAT:
  1571.         case SQL_DOUBLE: {
  1572.             double    d;
  1573.  
  1574.             d = (lpcol->fSqlType == SQL_REAL
  1575.                     ? *((float FAR *)lpb)
  1576.                     : *((double FAR *)lpb));
  1577.  
  1578.             _gcvt(d, 15, lpsz);
  1579.             break;
  1580.         }
  1581.  
  1582.         default:
  1583.             *lpsz = '\0';
  1584.             break;
  1585.     }
  1586.  
  1587.     return;
  1588. }
  1589. #pragma optimize("ceglntw", on)
  1590.  
  1591.  
  1592. /* GetData -----------------------------------------------------------------
  1593.     Description: Retrieve the next unbound column and display the data
  1594. --------------------------------------------------------------------------*/
  1595. void INTFUNC GetData(LPCHILD lpChild)
  1596. {
  1597.     HCURSOR    hcur;
  1598.     BIGCOL    bcol;
  1599.     UWORD    icol;
  1600.     RETCODE    rc;
  1601.  
  1602.     // Prevent Cancel from closing statement
  1603.     lpChild->hstmtTmp = lpChild->hstmt;
  1604.  
  1605.     // Determine next unbound column index
  1606.     icol = lpChild->ccolRetrieved + 1;
  1607.  
  1608.     hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1609.  
  1610.     // Get column name
  1611.     Async(SQLColAttributes(lpChild->hstmt, icol,
  1612.                             SQL_COLUMN_NAME,
  1613.                             bcol.szName, sizeof(bcol.szName), NULL,
  1614.                             NULL));
  1615.  
  1616.     SetCursor(hcur);
  1617.  
  1618.     if (STMTError(rc)) {
  1619.         lpChild->hstmtTmp = SQL_NULL_HSTMT;
  1620.         return;
  1621.     }
  1622.  
  1623.     bcol.lpsz = lpChild->lpsz;
  1624.  
  1625.     hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1626.  
  1627.     // Get the data converting it to character
  1628.     Async(SQLGetData(lpChild->hstmt, icol, SQL_C_CHAR,
  1629.                     bcol.lpsz, cbBUFSIZE-1, &bcol.cb));
  1630.  
  1631.     SetCursor(hcur);
  1632.  
  1633.     lpChild->hstmtTmp = SQL_NULL_HSTMT;
  1634.  
  1635.     if (STMTError(rc) || rc == SQL_NO_DATA_FOUND)
  1636.         return;
  1637.  
  1638.     // Display the retrieved data
  1639.     DialogBoxParam(g_hinst,
  1640.                     MAKEINTRESOURCE(IDD_DATADLG),
  1641.                     lpChild->hwnd,
  1642.                     DataDlgProc,
  1643.                     (LPARAM)((LPSTR)&bcol));
  1644.  
  1645.     lpChild->ccolRetrieved = icol;
  1646.     return;
  1647. }
  1648.  
  1649.  
  1650. /* GetTableName ------------------------------------------------------------
  1651.     Description: Extract table name from a SELECT statement
  1652. --------------------------------------------------------------------------*/
  1653. void INTFUNC GetTableName(LPSTR lpszTable, LPCSTR szSql)
  1654. {
  1655.     LPCSTR    lpsz;
  1656.     int        cp;
  1657.     int        cb;
  1658.  
  1659.     cb = lstrlen(szFROM);
  1660.  
  1661.     for (lpsz=szSql, cp=0; *lpsz; ) {
  1662.  
  1663.         while (*lpsz && ISWHITE(*lpsz)) lpsz++;
  1664.  
  1665.         if (!cp && !_fstrnicmp(lpsz, szFROM, cb) && ISWHITE(*(lpsz+cb)))
  1666.             break;
  1667.  
  1668.         if (ISLPAREN(*lpsz))
  1669.             cp++;
  1670.         else if (ISRPAREN(*lpsz))
  1671.             cp--;
  1672.  
  1673.         while (*lpsz && !ISWHITE(*lpsz)) lpsz++;
  1674.     }
  1675.  
  1676.     while (*lpsz && !ISWHITE(*lpsz)) lpsz++;
  1677.     while (*lpsz && ISWHITE(*lpsz))  lpsz++;
  1678.  
  1679.     if (*lpsz == *g_szQuoteChar) {
  1680.         *lpszTable++ = *lpsz++; //    Copy beginning quote
  1681.         while (*lpsz && *lpsz != *g_szQuoteChar) *lpszTable++ = *lpsz++;
  1682.         *lpszTable++ = *lpsz++; //    Copy ending quote
  1683.     }
  1684.     else    //    Not a quoted identifier
  1685.         while (*lpsz && !ISCOMMA(*lpsz) && !ISWHITE(*lpsz)) *lpszTable++ = *lpsz++;
  1686.  
  1687.     *lpszTable = '\0';
  1688.  
  1689.     return;
  1690. }
  1691.  
  1692.  
  1693. /* IsUpdateable ------------------------------------------------------------
  1694.     Description: Return TRUE if this app supports updating the particular
  1695.                  SQL data type (due to limited conversion support)
  1696. --------------------------------------------------------------------------*/
  1697. BOOL INTFUNC IsUpdateable(SDWORD fSqlType)
  1698. {
  1699.     switch (fSqlType) {
  1700.         case SQL_CHAR:
  1701.         case SQL_VARCHAR:
  1702.         case SQL_SMALLINT:
  1703.         case SQL_INTEGER:
  1704.         case SQL_REAL:
  1705.         case SQL_FLOAT:
  1706.         case SQL_DOUBLE:
  1707.         case SQL_TINYINT:
  1708.             return TRUE;
  1709.  
  1710.         default:
  1711.             return FALSE;
  1712.     }
  1713. }
  1714.  
  1715.  
  1716. /* OnDataRow ---------------------------------------------------------------
  1717.     Description: Return 0 or greater if the mouse coordinates in lparam are
  1718.                  over a valid data row, Otherwise return -1
  1719. --------------------------------------------------------------------------*/
  1720. int INTFUNC OnDataRow(LPCHILD lpChild, LPARAM lparam)
  1721. {
  1722.     RECT    rc;
  1723.     int        row;
  1724.  
  1725.     if (!(lpChild->hrgn))
  1726.         return -1;
  1727.  
  1728.     GetRgnBox(lpChild->hrgn, &rc);
  1729.  
  1730.     row = (int)HIWORD(lparam) - g_cy;
  1731.  
  1732.     if (row < 0)
  1733.         return FALSE;
  1734.  
  1735.     row /= g_cy;
  1736.  
  1737.     if (row >= 0                        &&
  1738.         (UWORD)row < lpChild->crowRowset )
  1739.         return row;
  1740.     else
  1741.         return -1;
  1742. }
  1743.  
  1744.  
  1745. /* PaintChild --------------------------------------------------------------
  1746.     Description: Paint child window
  1747. --------------------------------------------------------------------------*/
  1748. void INTFUNC PaintChild(LPCHILD    lpChild,
  1749.                             HDC        hdc,
  1750.                             BOOL    fTitle,
  1751.                             BOOL    fRefresh,
  1752.                             BOOL    fActive)
  1753. {
  1754.     RECT    rc;
  1755.  
  1756.     GetClientRect(lpChild->hwnd, &rc);
  1757.  
  1758.     // If no data exists, just erase the window
  1759.     if (!lpChild->fDataFetched)
  1760.         FillRect(hdc, &rc, g_hbrWin);
  1761.  
  1762.     // Otherwise paint the data in a simple scrollable grid
  1763.     else {
  1764.         HRGN        hrgn;
  1765.         HFONT        hfontOld;
  1766.         HBRUSH        hbrOld;
  1767.         UWORD        ir;
  1768.         int            ic;
  1769.         int            icFirst, icLast;
  1770.         LPCOL        lpcol;
  1771.         UWORD        row;
  1772.         int            col;
  1773.         int            cx, cy;
  1774.         LPINT        lpnTab;
  1775.         LPSTR        lpszValues;
  1776.         LPSTR        lpsz;
  1777.         LPUWORD        lpfStatus;
  1778.         UWORD        irowLast;
  1779.         UWORD        irowPos;
  1780.         COLORREF    clrfTxt;
  1781.         COLORREF    clrfBkg;
  1782.         char        szFmt[cbSTRLEN];
  1783.  
  1784.         // Prepare the device context
  1785.         SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  1786.  
  1787.         clrfTxt = GetTextColor(hdc);
  1788.         clrfBkg = GetBkColor(hdc);
  1789.  
  1790.         // Determine first (row,col) and corresponding (x,y) offset
  1791.         row = (UWORD)GetScrollPos(lpChild->hwndVScroll, SB_CTL);
  1792.         col = GetScrollPos(lpChild->hwndHScroll, SB_CTL);
  1793.  
  1794.         cx = col * g_cx;
  1795.         cy = row * g_cy;
  1796.  
  1797.         // Determine last row to be painted
  1798.         irowLast = (UWORD)row + ((UWORD)lpChild->crowwin < lpChild->crowRowset
  1799.                                                 ? lpChild->crowwin
  1800.                                                 : lpChild->crowRowset);
  1801.         if (irowLast > lpChild->crowRowset)
  1802.             irowLast--;
  1803.  
  1804.         // Get current row number as a zero based index
  1805.         irowPos  = lpChild->irowPos - 1;
  1806.  
  1807.         // Determine which columns will be painted
  1808.         lpnTab = lpChild->lpnTabs;
  1809.         for (icFirst=0;
  1810.             icFirst < (lpChild->ccol-1) && (cx + cxBORDER) > *(lpnTab+1);
  1811.             icFirst++, lpnTab++);
  1812.         for (icLast=icFirst+1;
  1813.             icLast < lpChild->ccol &&
  1814.             (cx + cxBORDER + (lpChild->ccolwin * g_cx)) > *lpnTab;
  1815.             icLast++, lpnTab++);
  1816.  
  1817.         // Offset the device context to appropriate position in the rowset
  1818.         SetWindowOrgEx(hdc, cx, cy, NULL);
  1819.  
  1820.         // Set the clip region (to keep from erasing scroll bars, etc.)
  1821.  
  1822.         if (!(lpChild->hrgn))
  1823.             return;
  1824.  
  1825.         SelectClipRgn(hdc, lpChild->hrgn);
  1826.  
  1827.         // Allocate working buffer to converted data values
  1828.         lpszValues = AllocPtr((DWORD) lpChild->ccol * cbSTRLEN);
  1829.  
  1830.         // If requested, paint column titles
  1831.         if (fTitle) {
  1832.  
  1833.             // Prepare device context
  1834.             SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
  1835.             hfontOld = SelectObject(hdc, g_hfontName);
  1836.  
  1837.             // Determine bounding rectangle in logical coordinates
  1838.             rc.top    = cy;
  1839.             rc.bottom = rc.top + g_cy;
  1840.             rc.left   = *(lpChild->lpnTabs + icFirst) - cxBORDER;
  1841.             rc.right  = *(lpChild->lpnTabs + icLast)  - cxBORDER;
  1842.  
  1843.             // Fill rectangle with appropriate color
  1844.             FillRect(hdc, &rc, g_hbrBtn);
  1845.  
  1846.             // Paint white bar across the top
  1847.             PatBlt(hdc, 0, rc.top, rc.right, 1, WHITENESS);
  1848.  
  1849.             // For each visible (or partially visible) column, paint
  1850.             // separating lines and column name
  1851.             lpcol  = lpChild->lpcol   + icFirst;
  1852.             lpnTab = lpChild->lpnTabs + icFirst + 1;
  1853.  
  1854.             for (ic=icFirst; ic < icLast; ic++, lpcol++, lpnTab++) {
  1855.  
  1856.                 PatBlt(hdc, rc.left, rc.top, 1, g_cy, WHITENESS);
  1857.  
  1858.                 rc.right = *lpnTab - cxBORDER + 1;
  1859.  
  1860.                 DrawText(hdc, lpcol->szName, lstrlen(lpcol->szName),
  1861.                         &rc, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
  1862.  
  1863.                 rc.left = rc.right;
  1864.  
  1865.                 PatBlt(hdc, rc.left-1, rc.top, 1, g_cy, BLACKNESS);
  1866.             }
  1867.  
  1868.             // Paint black line across the bottom
  1869.             PatBlt(hdc, 1, rc.bottom-1, rc.right-1, 1, BLACKNESS);
  1870.  
  1871.             // Reset device context
  1872.             SelectObject(hdc, hfontOld);
  1873.             SetBkColor(hdc, clrfBkg);
  1874.         }
  1875.  
  1876.         // Determine bounding rectangle for first row to be painted
  1877.         rc.top    = cy + g_cy;
  1878.         rc.bottom = rc.top + g_cy - 1;
  1879.         rc.left   = *(lpChild->lpnTabs + icFirst) - cxBORDER;
  1880.         rc.right  = *(lpChild->lpnTabs + icLast)  - cxBORDER;
  1881.  
  1882.         // Make format string from the whole format string created earlier
  1883. #ifdef WIN32
  1884.         _fstrncpy(szFmt, lpChild->szFmt+(icFirst*3), ((icLast-icFirst)*3)+1);
  1885. #else
  1886.         lstrcpyn(szFmt, lpChild->szFmt+(icFirst*3), ((icLast-icFirst)*3)+1);
  1887. #endif
  1888.         szFmt[((icLast-icFirst)*3)+1] = '\0';
  1889.  
  1890.         // Offset into the row status array
  1891.         lpfStatus = lpChild->lpfStatus + row;
  1892.  
  1893.         // Prepare device context
  1894.         hfontOld = SelectObject(hdc, g_hfontData);
  1895.         hbrOld   = SelectObject(hdc, g_hbrBtn);
  1896.  
  1897.         // Paint each row
  1898.         for (ir=row; ir < irowLast; ir++) {
  1899.  
  1900.             // Erase row if requested (i.e., !fRefresh) or if just
  1901.             // painting the row is insufficient to remove old data
  1902.             if (!fRefresh                   ||
  1903.                 (ir == irowPos && !fActive) ||
  1904.                 *lpfStatus == SQL_ROW_NOROW ||
  1905.                 *lpfStatus == SQL_ROW_ERROR ||
  1906.                 *lpfStatus == SQL_ROW_DELETED)
  1907.                 FillRect(hdc, &rc, g_hbrWin);
  1908.  
  1909.             // For an empty row, write the empty row string
  1910.             if (*lpfStatus == SQL_ROW_NOROW) {
  1911.                 if (icFirst)
  1912.                     *lpChild->lpsz = '\0';
  1913.                 else
  1914.                     lstrcpy(lpChild->lpsz, g_szNoRow);
  1915.             }
  1916.  
  1917.             // For a deleted row, write the deleted row string
  1918.             else if (*lpfStatus == SQL_ROW_DELETED) {
  1919.                 if (icFirst)
  1920.                     *lpChild->lpsz = '\0';
  1921.                 else
  1922.                     lstrcpy(lpChild->lpsz, g_szRowDeleted);
  1923.             }
  1924.  
  1925.             // For an error row, write the error row string
  1926.             else if (*lpfStatus == SQL_ROW_ERROR) {
  1927.                 if (icFirst)
  1928.                     *lpChild->lpsz = '\0';
  1929.                 else
  1930.                     lstrcpy(lpChild->lpsz, g_szRowError);
  1931.             }
  1932.  
  1933.             // For all other rows, build a string of data values
  1934.             else {
  1935.                 LPSTR FAR    *lplpsz;
  1936.                 LPBYTE         lpb;
  1937.                 LPSDWORD    lpcb;
  1938.  
  1939.                 // Paint updated rows in RED
  1940.                 if (*lpfStatus == SQL_ROW_UPDATED)
  1941.                     SetTextColor(hdc, RGB(255,0,0));
  1942.  
  1943.                 lplpsz = (LPSTR FAR *)lpChild->rglpv;
  1944.                 lpsz   = lpszValues;
  1945.  
  1946.                 lpcol  = lpChild->lpcol   + icFirst;
  1947.                 lpnTab = lpChild->lpnTabs + icFirst;
  1948.  
  1949.                 // Convert each column to character data
  1950.                 for (ic=icFirst; ic < icLast; ic++, lpcol++) {
  1951.  
  1952.                     lpb  = lpcol->lpb  + (ir * (ROW_BINDING(lpChild)
  1953.                                                     ? lpChild->cbrow
  1954.                                                     : lpcol->cb));
  1955.                     lpcb = (ROW_BINDING(lpChild)
  1956.                                 ? (LPSDWORD)(lpb + lpcol->cb)
  1957.                                 : lpcol->lpcb + ir);
  1958.  
  1959.                     if (*lpcb == SQL_NULL_DATA)
  1960.                         *lplpsz++ = g_szNull;
  1961.  
  1962.                     else if (lpcol->fCType == SQL_C_CHAR)
  1963.                         *lplpsz++ = (LPSTR)lpb;
  1964.  
  1965.                     else {
  1966.  
  1967.                         if (lpcol->fCType == SQL_C_FLOAT ||
  1968.                             lpcol->fCType == SQL_C_DOUBLE ) {
  1969.                             double    d;
  1970.  
  1971.                             d = (lpcol->fCType == SQL_C_FLOAT
  1972.                                     ? *((float FAR *)lpb)
  1973.                                     : *((double FAR *)lpb));
  1974.  
  1975.                             _gcvt(d, 15, lpsz);
  1976.                         }
  1977.  
  1978.                         else {
  1979.                             long    l;
  1980.  
  1981.                             l = ( lpcol->fCType == SQL_C_SHORT
  1982.                                     ? *((short FAR *)lpb)
  1983.                                 : lpcol->fCType == SQL_C_LONG
  1984.                                     ? *((long FAR *)lpb)
  1985.                                     : *((signed char FAR *)lpb));
  1986.  
  1987.                             _ltoa(l, lpsz, 10);
  1988.                         }
  1989.  
  1990.                         *lplpsz++ = lpsz;
  1991.                         lpsz += cbSTRLEN;
  1992.                     }
  1993.                 }
  1994.  
  1995.                 // Combine all columns into one string (with tab markers)
  1996.                 wvsprintf(lpChild->lpsz, szFmt, lpChild->rglpv);
  1997.             }
  1998.  
  1999.             // Paint the row
  2000.             TabbedTextOut(hdc,
  2001.                         rc.left, rc.top,
  2002.                         lpChild->lpsz, lstrlen(lpChild->lpsz),
  2003.                         icLast - icFirst, lpnTab, 0);
  2004.  
  2005.             // Paint bottom separator
  2006.             PatBlt(hdc, 0, rc.bottom, rc.right, 1, PATCOPY);
  2007.  
  2008.             // Paint inter-column separators
  2009.             lpnTab = lpChild->lpnTabs + icFirst + 1;
  2010.             for (ic=icFirst+1; ic < (icLast+1); ic++, lpnTab++)
  2011.                 PatBlt(hdc, *lpnTab-cxBORDER, rc.top, 1, g_cy, PATCOPY);
  2012.  
  2013.             // Reset text color (if it was changed)
  2014.             if (*lpfStatus == SQL_ROW_UPDATED)
  2015.                 SetTextColor(hdc, clrfTxt);
  2016.  
  2017.             // Hilite the current row
  2018.             if (ir == irowPos                                 &&
  2019.                 lpChild->fConcurrency != SQL_CONCUR_READ_ONLY &&
  2020.                 fActive                                        ) {
  2021.                 RECT    rcTemp, rcInvert;
  2022.  
  2023.                 GetClipBox(hdc, &rcTemp);
  2024.  
  2025.                 IntersectRect(&rcInvert, &rc, &rcTemp);
  2026.  
  2027.                 GetClientRect(lpChild->hwnd, &rcTemp);
  2028.  
  2029.                 if( rcInvert.top == rc.top )
  2030.                     rcInvert.top++;
  2031.  
  2032.                 if( rcInvert.bottom == rc.bottom )
  2033.                     rcInvert.bottom--;
  2034.  
  2035.                 if( rcInvert.left == rcTemp.left )
  2036.                     rcInvert.left++;
  2037.  
  2038.                 if( rcInvert.right == rcTemp.right ||
  2039.                     rcInvert.right == rc.right )
  2040.                     rcInvert.right--;
  2041.  
  2042.                 InvertRect(hdc, &rcInvert);
  2043.             }
  2044.  
  2045.             // Advance row rectangle and status array offset
  2046.             rc.top    += g_cy;
  2047.             rc.bottom += g_cy;
  2048.             lpfStatus++;
  2049.         }
  2050.  
  2051.         // Erase any partial row which may be displayed after the last
  2052.         // row of the row-set has been painted
  2053.         if (ir == lpChild->crowRowset)
  2054.             FillRect(hdc, &rc, g_hbrWin);
  2055.  
  2056.         // Erase any partial column which may be displayed after the last
  2057.         // column has been painted
  2058.         if (icLast == lpChild->ccol) {
  2059.             GetClipBox(hdc, &rc);
  2060.             rc.left = *(lpChild->lpnTabs + icLast) - cxBORDER + 1;
  2061.             FillRect(hdc, &rc, g_hbrWin);
  2062.         }
  2063.  
  2064.         FreePtr(lpszValues);
  2065.  
  2066.         // Reset clip region to paint areas outside data grid
  2067.         GetClientRect(lpChild->hwnd, &rc);
  2068.  
  2069.         hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
  2070.  
  2071.         SelectClipRgn(hdc, hrgn);
  2072.  
  2073.         OffsetRect(&rc, cx, cy);
  2074.  
  2075.         // Paint bottom record display bar and/or the corner box
  2076.         // between the scroll bars
  2077.         rc.top = rc.bottom - g_cyHScroll + 1;
  2078.  
  2079.         PatBlt(hdc, rc.left, rc.top, rc.right, 1, BLACKNESS);
  2080.  
  2081.         SelectObject(hdc, g_hbrScroll);
  2082.  
  2083.         rc.top++;
  2084.  
  2085.         cx = (2 * cxBORDER) + g_cxRecord + g_cxRecnum + 2;
  2086.  
  2087.         if (lpChild->fHScroll) {
  2088.             if (lpChild->fVScroll)
  2089.                 PatBlt(hdc,
  2090.                         rc.right - g_cxVScroll + 2, rc.top,
  2091.                         g_cxVScroll, g_cyHScroll,
  2092.                         PATCOPY);
  2093.         }
  2094.         else
  2095.             PatBlt(hdc,
  2096.                     rc.left + cx, rc.top,
  2097.                     rc.right - rc.left, g_cyHScroll,
  2098.                     PATCOPY);
  2099.  
  2100.         rc.right = rc.left + cx;
  2101.  
  2102.         PatBlt(hdc, rc.right, rc.top, 1, rc.bottom, BLACKNESS);
  2103.  
  2104.         rc.right--;
  2105.  
  2106.         // Paint current row number
  2107.         if (fRefresh)
  2108.             FillRect(hdc, &rc, g_hbrWin);
  2109.  
  2110.         rc.left += cxBORDER;
  2111.  
  2112.         TextOut(hdc, rc.left, rc.top, szRECORD, lstrlen(szRECORD));
  2113.  
  2114.         rc.left += g_cxRecord + 2;
  2115.         
  2116.         FillRect(hdc, &rc, g_hbrWin);
  2117.  
  2118.         wsprintf(lpChild->lpsz, szRECNUM, lpChild->irow-1 + lpChild->irowPos);
  2119.  
  2120.         TextOut(hdc, rc.left, rc.top, lpChild->lpsz, lstrlen(lpChild->lpsz));
  2121.  
  2122.         DeleteObject(hrgn);
  2123.  
  2124.         // Reset device context
  2125.         SelectObject(hdc, hbrOld);
  2126.  
  2127.         SelectObject(hdc, hfontOld);
  2128.     }
  2129.  
  2130.     return;
  2131. }
  2132.  
  2133.  
  2134.  
  2135. /* SetCurrentValue ---------------------------------------------------------
  2136.     Description: Set a column value from the user buffer
  2137. --------------------------------------------------------------------------*/
  2138. #pragma optimize("ceglntw", off)
  2139. BOOL INTFUNC SetCurrentValue(LPSTR lpsz, LPCOL lpcol, LPCHILD lpChild)
  2140. {
  2141.     LPBYTE        lpb;
  2142.     LPSDWORD    lpcb;
  2143.     BOOL        fNew;
  2144.     UWORD        irowPos;
  2145.  
  2146.     irowPos = lpChild->irowPos - 1;
  2147.  
  2148.     // Get data and count field pointers based on binding type
  2149.     if (ROW_BINDING(lpChild)) {
  2150.         lpb  = lpcol->lpb + (irowPos * lpChild->cbrow);
  2151.         lpcb = (LPSDWORD)(lpb + lpcol->cb);
  2152.     }
  2153.     else {
  2154.         lpb  = lpcol->lpb + (irowPos * lpcol->cb);
  2155.         lpcb = lpcol->lpcb + irowPos;
  2156.     }
  2157.  
  2158.     // If the data is NULL, just set the count field to SQL_NULL_DATA
  2159.     if (!lstrcmpi(lpsz, g_szNull)) {
  2160.         if (*lpcb != SQL_NULL_DATA) {
  2161.             *lpcb = SQL_NULL_DATA;
  2162.             fNew  = TRUE;
  2163.         }
  2164.     }
  2165.  
  2166.     // Otherwise, convert the character data back to the appropriate type
  2167.     else switch (lpcol->fSqlType) {
  2168.  
  2169.         case SQL_CHAR:
  2170.         case SQL_VARCHAR:
  2171.             if (lstrcmp(lpsz, (LPSTR)lpb)) {
  2172.                 lstrcpy((LPSTR)lpb, lpsz);
  2173.                 *lpcb = lstrlen(lpsz);
  2174.                 fNew  = TRUE;
  2175.             }
  2176.             break;
  2177.  
  2178.         case SQL_INTEGER:
  2179.         case SQL_SMALLINT:
  2180.         case SQL_TINYINT: {
  2181.             long    lNew, lCur;
  2182.             char    *EndPtr;
  2183.  
  2184.             lNew = strtol(lpsz, &EndPtr, 10);
  2185.             for (; *EndPtr && ISWHITE(*EndPtr); EndPtr = AnsiNext(EndPtr));
  2186.             if (*EndPtr)  { // check to see if there exists non-numeric chars
  2187.                 UCHAR szBuffer[128];
  2188.  
  2189.                 LoadString(g_hinst, IDS_BADNUMERIC, szBuffer, sizeof(szBuffer)); 
  2190.                 MessageBox(lpChild->hwnd, 
  2191.                         szBuffer, NULL, MB_ICONSTOP); 
  2192.                 fNew = FALSE;
  2193.                 break;
  2194.             }
  2195.  
  2196.             lCur = (lpcol->fSqlType == SQL_INTEGER
  2197.                         ? *((DWORD FAR *)lpb)
  2198.                         : lpcol->fSqlType == SQL_SMALLINT
  2199.                             ? *((WORD FAR *)lpb)
  2200.                             : *((UCHAR FAR *)lpb));
  2201.  
  2202.             if (lNew != lCur) {
  2203.  
  2204.                 switch (lpcol->fSqlType) {
  2205.                     case SQL_INTEGER:
  2206.                         *((DWORD FAR *)lpb) = lNew;
  2207.                         *lpcb = sizeof(DWORD);
  2208.                         break;
  2209.  
  2210.                     case SQL_SMALLINT:
  2211.                         *((WORD FAR *)lpb) = (WORD)lNew;
  2212.                         *lpcb = sizeof(WORD);
  2213.                         break;
  2214.  
  2215.                     case SQL_TINYINT:
  2216.                         *((UCHAR FAR *)lpb) = (UCHAR)lNew;
  2217.                         *lpcb = sizeof(UCHAR);
  2218.                         break;
  2219.                 }
  2220.  
  2221.                 fNew = TRUE;
  2222.             }
  2223.             break;
  2224.         }
  2225.  
  2226.         case SQL_REAL:
  2227.         case SQL_FLOAT:
  2228.         case SQL_DOUBLE: {
  2229.             double    dNew, dCur;
  2230.             char    *EndPtr;
  2231.  
  2232.             dNew = strtod(lpsz, &EndPtr);
  2233.             for (; *EndPtr && ISWHITE(*EndPtr); EndPtr = AnsiNext(EndPtr));
  2234.             if (*EndPtr)  { // check to see if there exists non-numeric chars
  2235.                 UCHAR szBuffer[128];
  2236.  
  2237.                 LoadString(g_hinst, IDS_BADNUMERIC, szBuffer, sizeof(szBuffer));
  2238.                 MessageBox(lpChild->hwnd, 
  2239.                         szBuffer, NULL, MB_ICONSTOP); 
  2240.                 fNew = FALSE;
  2241.                 break;
  2242.             }
  2243.  
  2244.             dCur = (lpcol->fSqlType == SQL_REAL
  2245.                         ? *((float FAR *)lpb)
  2246.                         : *((double FAR *)lpb));
  2247.  
  2248.             if (dNew != dCur) {
  2249.  
  2250.                 switch (lpcol->fSqlType) {
  2251.                     case SQL_REAL:
  2252.                         *((float FAR *)lpb) = (float)dNew;
  2253.                         *lpcb = sizeof(float);
  2254.                         break;
  2255.  
  2256.                     case SQL_FLOAT:
  2257.                     case SQL_DOUBLE:
  2258.                         *((double FAR *)lpb) = dNew;
  2259.                         *lpcb = sizeof(double);
  2260.                         break;
  2261.                 }
  2262.  
  2263.                 fNew = TRUE;
  2264.             }
  2265.             break;
  2266.         }
  2267.     }
  2268.  
  2269.     return fNew;
  2270. }
  2271. #pragma optimize("ceglntw", on)
  2272.  
  2273.  
  2274. /* SetPos ------------------------------------------------------------------
  2275.     Description: Set current row, de-hilite last current row and hilite
  2276.                  new current row
  2277. --------------------------------------------------------------------------*/
  2278. void INTFUNC SetPos(LPCHILD lpChild, UWORD irowPos)
  2279. {
  2280.     HDC        hdc;
  2281.     HFONT    hfont;
  2282.     RECT    rc, rcClip, rcInvert;
  2283.     int        row, col;
  2284.     int        cx, cy;
  2285.  
  2286.     if (!(lpChild->hrgn))
  2287.         return ;
  2288.  
  2289.     if (irowPos < 1)
  2290.         irowPos = 1;
  2291.  
  2292.     else if (irowPos > lpChild->crowRowset)
  2293.         irowPos = lpChild->crowRowset;
  2294.  
  2295.     // Call SQLSetPos to set current row in the row-set
  2296.     if (STMTError(SQLSetPos(lpChild->hstmt, irowPos, SQL_POSITION,
  2297.         SQL_LOCK_NO_CHANGE)))
  2298.         return;
  2299.  
  2300.     // Reset number of columns retrieved
  2301.     lpChild->ccolRetrieved = (UWORD)lpChild->ccol;
  2302.  
  2303.     // Obtain a device context for painting
  2304.     hdc = GetDC(lpChild->hwnd);
  2305.  
  2306.     row = GetScrollPos(lpChild->hwndVScroll, SB_CTL);
  2307.     col = GetScrollPos(lpChild->hwndHScroll, SB_CTL);
  2308.  
  2309.     cx  = col * g_cx;
  2310.     cy  = row * g_cy;
  2311.  
  2312.     SetWindowOrgEx(hdc, cx, cy, NULL);
  2313.  
  2314.     GetClientRect(lpChild->hwnd, &rc);
  2315.     GetRgnBox(lpChild->hrgn, &rcClip);
  2316.     OffsetRect(&rcClip, cx, cy);
  2317.  
  2318.     rcClip.top += g_cy;
  2319.  
  2320.     // Offset to last current row
  2321.     rc.top    = ((lpChild->irowPos - 1) * g_cy) + g_cy;
  2322.     rc.bottom = rc.top + g_cy - 1;
  2323.     rc.left   = 0;
  2324.     rc.right  = *(lpChild->lpnTabs + lpChild->ccol) - cxBORDER;
  2325.  
  2326.     // De-hilite last current row
  2327.      IntersectRect(&rcInvert, &rc, &rcClip);
  2328.      InflateRect(&rcInvert, -1, -1);
  2329.      InvertRect(hdc, &rcInvert);
  2330.  
  2331.     // Save new current row
  2332.     lpChild->irowPos = irowPos;
  2333.  
  2334.     // Offset to new current row
  2335.      rc.top    = ((lpChild->irowPos - 1) * g_cy) + g_cy;
  2336.      rc.bottom = rc.top + g_cy - 1;
  2337.  
  2338.     // Hilite current row (if visible)
  2339.     if (lpChild->irowPos > (UWORD)row) {
  2340.  
  2341.          IntersectRect(&rcInvert, &rc, &rcClip);
  2342.         InflateRect(&rcInvert, -1, -1);
  2343.          InvertRect(hdc, &rcInvert);
  2344.     }
  2345.  
  2346.     // Update record number
  2347.     GetClientRect(lpChild->hwnd, &rc);
  2348.     OffsetRect(&rc, cx, cy);
  2349.  
  2350.     rc.left += cxBORDER + g_cxRecord + 2;
  2351.     rc.top   = rc.bottom - g_cyHScroll + 2;
  2352.     rc.right = rc.left + g_cxRecnum;
  2353.  
  2354.     FillRect(hdc, &rc, g_hbrWin);
  2355.  
  2356.     wsprintf(lpChild->lpsz, szRECNUM, lpChild->irow-1 + lpChild->irowPos);
  2357.  
  2358.     hfont = SelectObject(hdc, g_hfontData);
  2359.  
  2360.     TextOut(hdc, rc.left, rc.top, lpChild->lpsz, lstrlen(lpChild->lpsz));
  2361.  
  2362.     SelectObject(hdc, hfont);
  2363.  
  2364.     // Release device context
  2365.     ReleaseDC(lpChild->hwnd, hdc);
  2366.  
  2367.     return;
  2368. }
  2369.  
  2370.  
  2371. /* SetScroll ---------------------------------------------------------------
  2372.     Description: Determine if scroll bars are required and their ranges
  2373. --------------------------------------------------------------------------*/
  2374. void INTFUNC SetScroll(LPCHILD lpChild)
  2375. {
  2376.     RECT    rc;
  2377.     int        cx, cy;
  2378.     int        row, col;
  2379.  
  2380.     // Use the fInSetScroll flag to prevent recursion due WM_SIZE messages
  2381.     if (lpChild->fInSetScroll)
  2382.         return;
  2383.  
  2384.     lpChild->fInSetScroll = TRUE;
  2385.  
  2386.     // Save current scroll positions
  2387.     row = GetScrollPos(lpChild->hwndVScroll, SB_CTL);
  2388.     col = GetScrollPos(lpChild->hwndHScroll, SB_CTL);
  2389.  
  2390.     // Get window dimensions
  2391.     GetClientRect(lpChild->hwnd, &rc);
  2392.     cx = rc.right - rc.left;
  2393.     cy = rc.bottom - rc.top - g_cy - g_cyHScroll;
  2394.  
  2395.     // Assume no scrolling is required
  2396.     lpChild->fHScroll =
  2397.     lpChild->fVScroll = FALSE;
  2398.  
  2399.     // Include a vertical scroll bar if all rows do not fit
  2400.     lpChild->crowwin = cy / g_cy;
  2401.     if ((UWORD)lpChild->crowwin < lpChild->crowRowset) {
  2402.         lpChild->fVScroll = TRUE;
  2403.         cx -= g_cxVScroll;
  2404.     }
  2405.  
  2406.     // Include a horizontal scroll bar if all columns do not fit
  2407.     lpChild->ccolwin = cx / g_cx;
  2408.     if (lpChild->ccolwin < lpChild->ccols)
  2409.         lpChild->fHScroll = TRUE;
  2410.  
  2411.     // Reset scroll positions if no scrolling is necessary
  2412.     if (!lpChild->fVScroll) row = 0;
  2413.     if (!lpChild->fHScroll) col = 0;
  2414.  
  2415.     // Set scroll ranges, positions, and scroll bar visibility
  2416.     SetScrollRange(lpChild->hwndVScroll, SB_CTL, 0, lpChild->crowRowset - lpChild->crowwin, TRUE);
  2417.     SetScrollRange(lpChild->hwndHScroll, SB_CTL, 0, lpChild->ccols - lpChild->ccolwin,      TRUE);
  2418.  
  2419.     SetScrollPos(lpChild->hwndVScroll, SB_CTL, row, TRUE);
  2420.     SetScrollPos(lpChild->hwndHScroll, SB_CTL, col, TRUE);
  2421.  
  2422.     ShowWindow(lpChild->hwndVScroll, (lpChild->fVScroll ? SW_SHOW : SW_HIDE));
  2423.     ShowWindow(lpChild->hwndHScroll, (lpChild->fHScroll ? SW_SHOW : SW_HIDE));
  2424.  
  2425.     // Add one extra to window depth so no white space is left between
  2426.     // the last full row and bottom record display bar
  2427.     lpChild->crowwin++;
  2428.  
  2429.     // Size and position scroll bars
  2430.     SizeScroll(lpChild);
  2431.  
  2432.     lpChild->fInSetScroll = FALSE;
  2433.  
  2434.     return;
  2435. }
  2436.  
  2437.  
  2438. /* SizeScroll --------------------------------------------------------------
  2439.     Description: Size and position scroll bars
  2440. --------------------------------------------------------------------------*/
  2441. void INTFUNC SizeScroll(LPCHILD lpChild)
  2442. {
  2443.     RECT    rc;
  2444.     int        cxRecord;
  2445.  
  2446.     GetClientRect(lpChild->hwnd, &rc);
  2447.  
  2448.     // Place vertical scroll bar
  2449.     MoveWindow(lpChild->hwndVScroll,
  2450.                 rc.right - g_cxVScroll + 1,
  2451.                 rc.top,
  2452.                 g_cxVScroll,
  2453.                 rc.bottom - g_cyHScroll + 2,
  2454.                 TRUE);
  2455.  
  2456.     // Place horizontal scroll bar
  2457.     cxRecord = (2 * cxBORDER) + g_cxRecord + g_cxRecnum + 2;
  2458.  
  2459.     MoveWindow(lpChild->hwndHScroll,
  2460.                 rc.left + cxRecord,
  2461.                 rc.bottom - g_cyHScroll + 1,
  2462.                 (lpChild->fVScroll
  2463.                     ? rc.right - g_cxVScroll + 2 - cxRecord
  2464.                     : rc.right + 2 - cxRecord),
  2465.                 g_cyHScroll,
  2466.                 TRUE);
  2467.  
  2468.     return;
  2469. }
  2470.  
  2471.  
  2472. /* UpdateRow ---------------------------------------------------------------
  2473.     Description: Update current (positioned) row
  2474. --------------------------------------------------------------------------*/
  2475. void INTFUNC UpdateRow(LPCHILD lpChild)
  2476. {
  2477.     HCURSOR    hcur;
  2478.     LPSTR    lpsz;
  2479.     LPSTR    lpszT;
  2480.     SWORD    cb;
  2481.     LPCOL    lpcol;
  2482.     int        cCtls;
  2483.     UWORD    irowPos;
  2484.     int        i;
  2485.  
  2486.     // Ensure the update request is valid
  2487.     if (!lpChild->fDataFetched) {
  2488.         DoMessage(lpChild->hwnd, IDS_NODATAFETCHED);
  2489.         return;
  2490.     }
  2491.  
  2492.     if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_NOROW) {
  2493.         DoMessage(lpChild->hwnd, IDS_NOROWUPDATE);
  2494.         return;
  2495.     }
  2496.  
  2497.     if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_ERROR) {
  2498.         DoMessage(lpChild->hwnd, IDS_ERRORROWUPDATE);
  2499.         return;
  2500.     }
  2501.  
  2502.     if (*(lpChild->lpfStatus + lpChild->irowPos - 1) == SQL_ROW_DELETED) {
  2503.         DoMessage(lpChild->hwnd, IDS_DELROWUPDATE);
  2504.         return;
  2505.     }
  2506.  
  2507.     if (lpChild->fConcurrency == SQL_CONCUR_READ_ONLY) {
  2508.         DoMessage(lpChild->hwnd, IDS_NOUPDATE);
  2509.         return;
  2510.     }
  2511.  
  2512.     // Count number of updateable columns
  2513.     for (cCtls=0, i=0, lpcol=lpChild->lpcol; i < lpChild->ccol; i++, lpcol++)
  2514.         if (IsUpdateable(lpcol->fSqlType))
  2515.             cCtls++;
  2516.  
  2517.     // Ensure a valid number of updateable columns exist
  2518.     if (cCtls > cMAXCOLS) {
  2519.         DoMessage(lpChild->hwnd, IDS_TOOMANYCOLS);
  2520.         return;
  2521.     }
  2522.  
  2523.     if (!cCtls) {
  2524.         DoMessage(lpChild->hwnd, IDS_CANNOTUPDATE);
  2525.         return;
  2526.     }
  2527.  
  2528.     hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2529.  
  2530.     lpsz  = AllocPtr(2 * cbMAXSQL);
  2531.     lpszT = lpsz + cbMAXSQL;
  2532.  
  2533.     // Build and display update dialog to get new values, then issue update
  2534.     // (use a temporary HSTMT for the update request)
  2535.     if (IDOK == DoDialog(lpChild->hwnd, IDD_UPDATEROW, UpdateDlgProc) &&
  2536.         !DBCError(lpChild->hwnd, SQLAllocStmt(g_hdbc, &lpChild->hstmtTmp))) {
  2537.  
  2538.         // Build UPDATE <table> WHERE CURRENT OF <cursor name> statement
  2539.         lstrcpy(lpsz, szUPDATE);
  2540.  
  2541.         GetTableName(lpszT, lpChild->sql);
  2542.         lstrcat(lpsz, lpszT);
  2543.  
  2544.         // Build the Set Clause
  2545.         lstrcat(lpsz, szSET);
  2546.         for (lpcol = lpChild->lpcol, i = 0; i < lpChild->ccol; i++, lpcol++) {
  2547.             if (IsUpdateable(lpcol->fSqlType)) {
  2548.                 LPSDWORD lpcb;
  2549.                 LPBYTE    lpb;
  2550.  
  2551.                 if (i == 0) wsprintf(lpszT, "%s=?", lpcol->szName);
  2552.                 else wsprintf(lpszT, ",%s=?", lpcol->szName);
  2553.                 lstrcat(lpsz, lpszT);
  2554.  
  2555.                 irowPos = lpChild->irowPos - 1;
  2556.  
  2557.                 // Get data and count field pointers based on binding type
  2558.                 if (ROW_BINDING(lpChild)) {
  2559.                     lpb  = lpcol->lpb + (irowPos * lpChild->cbrow);
  2560.                     lpcb = (LPSDWORD)(lpb + lpcol->cb);
  2561.                 } else {
  2562.                     lpb  = lpcol->lpb + (irowPos * lpcol->cb);
  2563.                     lpcb = lpcol->lpcb + irowPos;
  2564.                 }
  2565.  
  2566.                 // set parameter
  2567.                 ODBCError(lpChild->hwnd, SQL_NULL_HENV, SQL_NULL_HDBC, lpChild->hstmtTmp,
  2568.                     SQLSetParam(lpChild->hstmtTmp, (UWORD) (i+1), lpcol->fCType,
  2569.                         lpcol->fSqlType, (UDWORD) lpcol->cb, (SWORD) 0, 
  2570.                         lpb, lpcb));
  2571.             }
  2572.         }
  2573.  
  2574.         lstrcat(lpsz, szWHERE);
  2575.  
  2576.         lpszT = lpsz + lstrlen(lpsz);
  2577.  
  2578.         if (!STMTError(SQLGetCursorName(lpChild->hstmt, (UCHAR FAR *)lpszT,
  2579.                                         cbMAXSQL, &cb))) {
  2580.  
  2581.             // Issue update request via SQLExecDirect
  2582.             ODBCError(lpChild->hwnd, SQL_NULL_HENV, SQL_NULL_HDBC, lpChild->hstmtTmp,
  2583.                         SQLExecDirect(lpChild->hstmtTmp, (UCHAR FAR *)lpsz, SQL_NTS));
  2584.         }
  2585.  
  2586.         DBCError(lpChild->hwnd, SQLFreeStmt(lpChild->hstmtTmp, SQL_DROP));
  2587.         lpChild->hstmtTmp = SQL_NULL_HSTMT;
  2588.  
  2589.     }
  2590.  
  2591.     // Refresh entire row-set buffer (saving current row position)
  2592.     irowPos = lpChild->irowPos;
  2593.  
  2594.     lpChild->FetchOP = SQL_FETCH_RELATIVE;
  2595.     lpChild->rrow = 0;
  2596.     Fetch(lpChild);
  2597.  
  2598.     SetPos(lpChild, irowPos);
  2599.  
  2600.     // Repaint the row-set
  2601.     InvalidateRect(lpChild->hwnd, NULL, FALSE);
  2602.  
  2603.     FreePtr(lpsz);
  2604.  
  2605.     SetCursor(hcur);
  2606.  
  2607.     return;
  2608. }
  2609.  
  2610. /*--------------------------------------------------------------------
  2611. Input:  lpChild
  2612. Output: TRUE if the option parameters are valid, FALSE otherwise
  2613. --------------------------------------------------------------------*/
  2614. BOOL INTFUNC ParamValid(LPCHILD lpChild) {
  2615.     char szBuffer[128]; // error message
  2616.     char *EndMaxBind, *EndRowset, *EndBind;
  2617.  
  2618.     // the maximum column width
  2619.     wsprintf((LPSTR) szBuffer,
  2620.         "Maximum column width must be at least 1 and at most %d",
  2621.         MAX_MAXBIND);
  2622.  
  2623.     if (lpChild->fMaxBind) { // it's been changed 
  2624.         lpChild->crowMaxBind = 
  2625.             (SDWORD) strtol((char*) lpChild->szMaxBind, &EndMaxBind, 10);
  2626.         for (; *EndMaxBind && ISWHITE(*EndMaxBind); 
  2627.                 EndMaxBind = AnsiNext(EndMaxBind));
  2628.     } 
  2629.  
  2630.     if (lpChild->fBind) {  // cBind has been changed
  2631.         lpChild->cBind =  (UWORD) strtol((char*) lpChild->szBind, &EndBind, 10);
  2632.         for (; *EndBind && ISWHITE(*EndBind); EndBind = AnsiNext(EndBind));
  2633.     }
  2634.  
  2635.     if (lpChild->fRowset) { // rowset has been changed
  2636.         lpChild->crowRowset =  
  2637.             (UWORD) strtol((char*) lpChild->szRowset, &EndRowset, 10);
  2638.         for (; *EndRowset && ISWHITE(*EndRowset); 
  2639.                 EndRowset = AnsiNext(EndRowset));
  2640.     }
  2641.  
  2642.     while (lpChild->fMaxBind && *EndMaxBind  ||
  2643.            lpChild->crowMaxBind < 1 || 
  2644.            lpChild->crowMaxBind > MAX_MAXBIND || 
  2645.            lpChild->fRowset && *EndRowset ||
  2646.            lpChild->crowRowset < 1 ||
  2647.            lpChild->crowRowset > 4096 ||
  2648.            lpChild->fBind && *EndBind) {
  2649.  
  2650.         // the maximum column width
  2651.         if (lpChild->crowMaxBind < 1 || lpChild->crowMaxBind > MAX_MAXBIND
  2652.             || lpChild->fMaxBind && *EndMaxBind)
  2653.             MessageBox(lpChild->hwnd,  szBuffer, NULL, MB_ICONSTOP);
  2654.  
  2655.         // the rowset size
  2656.         if (lpChild->crowRowset < 1 || lpChild->crowRowset > 4096 
  2657.             || lpChild->fRowset && *EndRowset) {
  2658.             char szBuffer[128];
  2659.  
  2660.             LoadString(g_hinst, IDS_BADROWSET, szBuffer, sizeof(szBuffer));
  2661.             MessageBox(lpChild->hwnd, szBuffer, NULL, MB_ICONSTOP);
  2662.         }
  2663.  
  2664.         // number of bound columns
  2665.         if (lpChild->fBind && *EndBind) 
  2666.             MessageBox(lpChild->hwnd,  
  2667.                 "Invalid number of bound columns", NULL, MB_ICONSTOP);
  2668.  
  2669.         if (IDOK != DoDialog(lpChild->hwnd, IDD_OPTION_DIALOG, OptionsDlgProc))
  2670.             return FALSE;
  2671.  
  2672.         if (lpChild->fMaxBind) { // it's been changed 
  2673.             lpChild->crowMaxBind = 
  2674.                 (SDWORD) strtol((char*) lpChild->szMaxBind, &EndMaxBind, 10);
  2675.             for (; *EndMaxBind && ISWHITE(*EndMaxBind); 
  2676.                     EndMaxBind = AnsiNext(EndMaxBind));
  2677.         } 
  2678.  
  2679.         if (lpChild->fBind) {  // cBind has been changed
  2680.             lpChild->cBind =  
  2681.                 (UWORD) strtol((char*) lpChild->szBind, &EndBind, 10);
  2682.         
  2683.             for (; *EndBind && ISWHITE(*EndBind); EndBind = AnsiNext(EndBind));
  2684.         }
  2685.  
  2686.         if (lpChild->fRowset) { // rowset has been changed
  2687.             lpChild->crowRowset =  
  2688.                 (UWORD) strtol((char*) lpChild->szRowset, &EndRowset, 10);
  2689.             for (; *EndRowset && ISWHITE(*EndRowset); 
  2690.                     EndRowset = AnsiNext(EndRowset));
  2691.         }
  2692.     }
  2693.  
  2694.     return TRUE;
  2695.  
  2696. }
  2697.