home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / odbc / admndemo / results.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-08  |  31.7 KB  |  950 lines

  1. //*---------------------------------------------------------------------------------
  2. //|  ODBC System Administrator
  3. //|
  4. //|  This code is furnished on an as-is basis as part of the ODBC SDK and is
  5. //|  intended for example purposes only.
  6. //|
  7. //|   Title:   RESULTS.C
  8. //|      This module contains functions which allow you to view results via an
  9. //|      owner-drawn list box.  Having the list box functionality made it easy
  10. //|      to implement Pipes, but has some serious draw-backs as far as record
  11. //|      limits, storage methods, etc...  If you are looking for a great way to
  12. //|      paint data in a grid, check out the C++ or Cursor Demo samples that came
  13. //|      with this SDK.
  14. //*---------------------------------------------------------------------------------
  15. #include <stdio.h>
  16. #include "errcheck.h"
  17. #include "standard.h"
  18. #include "results.h"
  19. #include "math.h"
  20. #include "ini.h"
  21.  
  22.  
  23. //------------------------------------------------------------------------
  24. //  Defines
  25. //------------------------------------------------------------------------
  26. VSZFile;
  27.  
  28.  
  29. //------------------------------------------------------------------------
  30. //  Globals
  31. //------------------------------------------------------------------------
  32. char szErrMsg[100];
  33. dCSEG(char) szONE[]                       =  "1";
  34. dCSEG(char) szZERO[]                      =  "0";
  35. dCSEG(char) szdate[]                      =  "%02u/%02u/%02u";
  36. dCSEG(char) sztime[]                      =  "%02u:%02u:%02u";
  37. dCSEG(char) sztimestmp[]                  =  "%02u/%02u/%02u %02u:%02u:%02u.%lu";
  38. dCSEG(char) szTypeNotFound[]              =  "Not found";
  39. dCSEG(char) szMaxRowsFetched[]            =  "Maximum rows fetched.  Total rows: %lu";
  40.  
  41.  
  42. struct {
  43.    SWORD type;                      // Data type value
  44.    LPSTR sztype;                    // String equivalent
  45.    } SqlTypes[] = {
  46. // type                 sztype
  47. // -------------------  -----------------------------
  48.    SQL_BIGINT,          "SQL_BIGINT=-5",
  49.    SQL_BINARY,          "SQL_BINARY=-2",
  50.    SQL_BIT,             "SQL_BIT=-7",
  51.    SQL_CHAR,            "SQL_CHAR=1",
  52.    SQL_DATE,            "SQL_DATE=9",
  53.    SQL_DECIMAL,         "SQL_DECIMAL=3",
  54.    SQL_DOUBLE,          "SQL_DOUBLE=8",
  55.    SQL_FLOAT,           "SQL_FLOAT=6",
  56.    SQL_INTEGER,         "SQL_INTEGER=4",
  57.    SQL_LONGVARBINARY,   "SQL_LONGVARBINARY=-4",
  58.    SQL_LONGVARCHAR,     "SQL_LONGVARCHAR=-1",
  59.    SQL_NUMERIC,         "SQL_NUMERIC=2",
  60.    SQL_REAL,            "SQL_REAL=7",
  61.    SQL_SMALLINT,        "SQL_SMALLINT=5",
  62.    SQL_TIME,            "SQL_TIME=10",
  63.    SQL_TIMESTAMP,       "SQL_TIMESTAMP=11",
  64.    SQL_TINYINT,         "SQL_TINYINT=-6",
  65.    SQL_VARBINARY,       "SQL_VARBINARY=-3",
  66.    SQL_VARCHAR,         "SQL_VARCHAR=12",
  67.    };
  68.  
  69.  
  70. struct {
  71.    SWORD type;                      // Data type value
  72.    LPSTR sztype;                    // String equivalent
  73.    } CTypes[] = {
  74. // type                 sztype
  75. // -------------------  -----------------------------
  76.    SQL_C_BINARY,        "SQL_C_BINARY=-2",
  77.    SQL_C_BIT,           "SQL_C_BIT=-7",
  78.    SQL_C_CHAR,          "SQL_C_CHAR=1",
  79.    SQL_C_DATE,          "SQL_C_DATE=9",
  80.    SQL_C_DOUBLE,        "SQL_C_DOUBLE=8",
  81.    SQL_C_FLOAT,         "SQL_C_FLOAT=7",
  82.    SQL_C_LONG,          "SQL_C_LONG=4",
  83.    SQL_C_SHORT,         "SQL_C_SHORT=5",
  84.    SQL_C_TIME,          "SQL_C_TIME=10",
  85.    SQL_C_TIMESTAMP,     "SQL_C_TIMESTAMP=11",
  86.    SQL_C_TINYINT,       "SQL_C_TINYINT=-6",
  87.    };
  88.  
  89. typedef struct tagDATATYPE{
  90.    SWORD type;                      // Data type value
  91.    LPSTR sztype;                    // String equivalent
  92.    } DATATYPE;
  93.  
  94.  
  95. //------------------------------------------------------------------------
  96. //  Local function prototypes
  97. //------------------------------------------------------------------------
  98. void CheckDisplayMode(LPSTR strin, SDWORD cbin, LPSTR strout);
  99. BOOL INTFUN             DrawRow(DRAWITEMSTRUCT FAR * dw,
  100.                            RESULTSSET FAR * rs, int xLeftCol, int xRightCol, BOOL fSelect);
  101.  
  102.  
  103. //*------------------------------------------------------------------------
  104. //| CreateResultsSet:
  105. //|   This is the first function that should be called to create a
  106. //|      results set.  When there is no more use for the results set,
  107. //|      call DeleteResultsSet to delete it.
  108. //|
  109. //|   Parameters:
  110. //|      in    rs                Pointer to a new results set
  111. //|      in    hwndClient        Client window
  112. //|      in    hInst             Instance handle of caller
  113. //|      in    count             How many columns in the results set
  114. //|      in    szTitle           Title for the window
  115. //|
  116. //|   Returns:
  117. //|      TRUE if there were no errors,
  118. //|      FALSE otherwise
  119. //*------------------------------------------------------------------------
  120. BOOL EXTFUN CreateResultsSet(RESULTSSET FAR * rs, HWND hwndClient, HINSTANCE hInst,
  121.             int count, LPSTR szTitle)
  122. {
  123.    if(!rs ||
  124.       count <=0)
  125.       return FALSE;
  126.  
  127.    memset(rs, 0, sizeof(RESULTSSET));
  128.    rs->cbColumns = count;
  129.    rs->hInst = hInst;
  130.    rs->hwndClient = hwndClient;
  131.    if(*szTitle)
  132.       lstrcpy(rs->szTitle, szTitle);
  133.  
  134.    rs->md = (METADATA FAR *)GetMemory(sizeof(METADATA) * count);
  135.    if(!rs->md)
  136.       return FALSE;
  137.  
  138.    return TRUE;
  139. }
  140.  
  141.  
  142. //*------------------------------------------------------------------------
  143. //| SetMetaDataColumn:
  144. //|   This function must be called for each column in the results set.  The
  145. //|      information placed for each row can be obtained using ODBC functions
  146. //|      such as SQLDescribeCol and SQLColAttribute.
  147. //|   Parameters:
  148. //|      in    rs                Pointer to a results set
  149. //|      in    iCol              Column number
  150. //|      in    szCol             Pointer to column name
  151. //|      in    szTypeName        Data type name
  152. //|      in    fSqlType          ODBC data type number
  153. //|      in    precision         Precision
  154. //|      in    scale             Scale
  155. //|      in    cbDisplay         Display size
  156. //|      in    fAlign            Alignment
  157. //|   Returns:
  158. //|      Nothing
  159. //*------------------------------------------------------------------------
  160. BOOL EXTFUN SetMetaDataColumn(RESULTSSET FAR * rs, int iCol, LPSTR szCol,
  161.       LPSTR szTypeName, SDWORD fSqlType, UDWORD precision, SWORD scale,
  162.       int cbDisplay, UINT fAlign)
  163. {
  164.    if(!rs ||
  165.       iCol < 0 ||
  166.       !szCol ||
  167.       !*szCol ||
  168.       !szTypeName ||
  169.       !*szTypeName) {
  170.       PostError((LPSTR)szInvalidParms);
  171.       return FALSE;
  172.    }
  173.  
  174.    rs->md[iCol].szColumnName = (LPSTR)GetMemory(lstrlen(szCol)+1);
  175.    if(!rs->md[iCol].szColumnName)
  176.       return FALSE;
  177.    precision = min(precision, MAXBYTES);
  178.    lstrcpy(rs->md[iCol].szColumnName, szCol);
  179.    lstrcpy(rs->md[iCol].szTypeName, szTypeName);
  180.    rs->md[iCol].fSqlType = fSqlType;
  181.    rs->md[iCol].precision = precision;
  182.    rs->md[iCol].scale = scale;
  183.    rs->md[iCol].cbDisplaySize = cbDisplay;
  184.    rs->md[iCol].fAlign = fAlign;
  185.    rs->md[iCol].cbOffset = (iCol > 0) ? (UINT)(precision + rs->md[iCol-1].cbOffset) : (UINT)(precision);
  186.    ++rs->md[iCol].cbOffset;               // Room for terminators
  187.  
  188.    return TRUE;
  189. }
  190.  
  191.  
  192.  
  193. //*------------------------------------------------------------------------
  194. //| AllocateRowData:
  195. //|   Call this function for each row in the results set to allocate
  196. //|      memory and insert a row into the results set.
  197. //|   Parameters:
  198. //|      in    rs                Pointer to results set
  199. //|      in    rd                Pointer to a row data structure
  200. //|      in    cColor            Text color
  201. //|      in    cBkg              Background color
  202. //|   Returns:
  203. //|      Pointer to a ROWDATA structure
  204. //*------------------------------------------------------------------------
  205. ROWDATA FAR * AllocateRowData(RESULTSSET FAR * rs, COLORREF cColor, COLORREF cBkg)
  206. {
  207.    ROWDATA FAR *  rd;
  208.    int            dex;
  209.  
  210.    if(!rs) {
  211.       PostError((LPSTR)szInvalidParms);
  212.       return FALSE;
  213.    }
  214.    rd = (ROWDATA FAR *)GetMemory(sizeof(ROWDATA));
  215.    if(!rd)
  216.       return NULL;
  217.    rd->textColor = cColor;
  218.    rd->bkgrnd = cBkg;
  219.    rd->cd = (COLUMNDATA FAR *)GetMemory((sizeof(COLUMNDATA) * rs->cbColumns));
  220.    if(!rd->cd)
  221.       return NULL;
  222.    rd->data = (LPSTR)GetMemory(rs->md[rs->cbColumns-1].cbOffset + 1);
  223.    if(!rd->data)
  224.       return NULL;
  225.    rd->cd[0].szCols = rd->data;
  226.    for(dex=1;  dex<rs->cbColumns;  dex++)
  227.       rd->cd[dex].szCols = rd->data + rs->md[dex-1].cbOffset;
  228.  
  229.    return rd;
  230. }
  231.  
  232.  
  233.  
  234. //*------------------------------------------------------------------------
  235. //| SetColumnData:
  236. //|   Call this function for a particular column in a ROWDATA structure.
  237. //|      If memory has been allocated for the column, it will be freed
  238. //|      and reallocated for the new string.
  239. //|   Parameters:
  240. //|      in    icol              Which column?
  241. //|      in    rd                Pointer to a row data structure
  242. //|      in    str               Pointer to the new buffer
  243. //|   Returns:
  244. //|      TRUE if successful
  245. //|      FALSE on error
  246. //*------------------------------------------------------------------------
  247. BOOL EXTFUN SetColumnData(int icol, ROWDATA FAR * rd, LPSTR str)
  248. {
  249.    if(!str ||
  250.       !*str)
  251.       return FALSE;
  252.  
  253.    lstrcpy(rd->cd[icol].szCols, str);
  254.    return TRUE;
  255. }
  256.  
  257.  
  258. //*------------------------------------------------------------------------
  259. //| FreeRowData:
  260. //|   Pass a pointer to the ROWDATA structure to free.  Obviously since
  261. //|      you are asked for a RESULTSSET pointer, you should call this
  262. //|      function before freeing the results set data.
  263. //| Parms:
  264. //|   in       rs                   Pointer to results set
  265. //|   in       rd                   Pointer to row data
  266. //| Returns:
  267. //|   Nothing.
  268. //*------------------------------------------------------------------------
  269. void EXTFUN FreeRowData(RESULTSSET FAR * rs, ROWDATA FAR * rd)
  270. {
  271.    ReleaseMemory(rd->data);
  272.    ReleaseMemory((LPVOID)rd->cd);
  273.    ReleaseMemory((LPVOID)rd);
  274. }
  275.  
  276.  
  277.  
  278. //*------------------------------------------------------------------------
  279. //| FreeResultsSet:
  280. //|   Call this function to free all of the memory for a results set.
  281. //| Parms:
  282. //|   in       rs                   Pointer to results set data to free
  283. //| Returns:
  284. //|   TRUE     If successful
  285. //|   FALSE    if there was an error
  286. //*------------------------------------------------------------------------
  287. void EXTFUN FreeResultsSet(RESULTSSET FAR * rs)
  288. {
  289.    int   dex;
  290.  
  291.    DeleteObject(rs->hFont);
  292.  
  293.    for(dex=0;  dex<rs->cbColumns;  dex++)
  294.       ReleaseMemory(rs->md[dex].szColumnName);
  295.    ReleaseMemory(rs->md);
  296.    ReleaseMemory(rs);
  297.    return;
  298. }
  299.  
  300.  
  301.  
  302. //*------------------------------------------------------------------------
  303. //| CreateResultsFont:
  304. //|   This function is called to create a font for the results set.  The
  305. //|   default font is used if the lf parameter is NULL.  Alternatively
  306. //|   the user can pass in a complete LOGFONT structure to use for the
  307. //|   font.
  308. //| Parms:
  309. //|   in       rs                   Pointer to results set to store info
  310. //|   in       hwnd                 Window handle to verify font
  311. //|   in       lf                   LOGFONT structure to use, NULL for dft
  312. //| Returns:
  313. //|   Nothing.
  314. //*------------------------------------------------------------------------
  315. void CreateResultsFont(RESULTSSET FAR * rs, HWND hwnd, LOGFONT FAR * lf)
  316. {
  317.    HDC                     hdc;
  318.    LOGFONT                 logfont;
  319.    HFONT                   hf;
  320.    TEXTMETRIC              tm;
  321.    SIZE                    sz;
  322.    int                     tmp, dex, cbExtra;
  323.  
  324.    if(!lf) {
  325.       memset(&logfont, 0, sizeof(LOGFONT));
  326.       GetDefaultFont(&logfont);
  327.    }
  328.    else
  329.       memmove(&logfont, lf, sizeof(LOGFONT));
  330.  
  331.    rs->hFont = CreateFontIndirect(&logfont);
  332.    hdc = GetDC(hwnd);
  333.    hf = SelectObject(hdc, rs->hFont);
  334.    GetTextMetrics(hdc, &tm);
  335.    rs->cx = tm.tmAveCharWidth;
  336.  
  337.    rs->cy = tm.tmHeight + tm.tmExternalLeading;
  338.    cbExtra = GetSystemMetrics(SM_CYBORDER);
  339.    rs->cTitleHeight = rs->cy + (7 * cbExtra);
  340.    rs->yTitleLoc = (rs->cTitleHeight / 2) + rs->cy;
  341.    for(dex=0, tmp=0;  dex<rs->cbColumns;  dex++) {
  342.       GetTextExtentPoint(hdc, rs->md[dex].szColumnName,
  343.                          lstrlen(rs->md[dex].szColumnName), &sz);
  344.       rs->md[dex].cColWidth = (rs->md[dex].cbDisplaySize * rs->cx) + (7 * cbExtra);
  345.       rs->md[dex].cColWidth = max((UINT)(sz.cx * 1.5),
  346.                                   rs->md[dex].cColWidth);
  347.       rs->md[dex].xCol = tmp;
  348.       tmp += rs->md[dex].cColWidth;
  349.    }
  350.    rs->cRowWidth = tmp;
  351.  
  352.    SelectObject(hdc,hf);
  353.    ReleaseDC(hwnd, hdc);
  354. }
  355.  
  356.  
  357. //*------------------------------------------------------------------------
  358. //| FindRightCol:
  359. //|   This function will take the left column and a results set descriptor
  360. //|      and return the right column index based on what will fit in the
  361. //|      window.
  362. //| Parms:
  363. //|   in       rs                   Pointer to results set to store info
  364. //|   in       xLeftCol             Current left column index
  365. //|   in       cWidth               Available width
  366. //| Returns:
  367. //|   Index to be used for right column
  368. //*------------------------------------------------------------------------
  369. int FindRightCol(RESULTSSET FAR * rs, int xLeftCol, int cWidth)
  370. {
  371.    int xRightCol;
  372.    int cSpace;
  373.  
  374.    xRightCol = xLeftCol;
  375.    cSpace = cWidth - rs->md[xLeftCol].cColWidth;
  376.    while(cSpace>0 &&
  377.          xRightCol < rs->cbColumns-1) {
  378.       ++xRightCol;
  379.       cSpace -= rs->md[xRightCol].cColWidth;
  380.    }
  381.    return xRightCol;
  382. }
  383.  
  384.  
  385. //*------------------------------------------------------------------------
  386. //| DrawRowData:
  387. //|   This function will do the actual drawing on the screen based on the
  388. //|      control structures passed in.
  389. //| Parms:
  390. //|   in       rs                   Pointer to results set to store info
  391. //|   in       dwitem               Draw structure
  392. //|   in       xLeftCol             Current left column index
  393. //|   in       xRightCol            Right column index
  394. //| Returns:
  395. //|   Nothing.
  396. //*------------------------------------------------------------------------
  397. void DrawRowData(RESULTSSET FAR * rs, DRAWITEMSTRUCT FAR * dwitem,
  398.                int xLeftCol, int xRightCol)
  399. {
  400.    switch(dwitem->itemAction) {
  401.      case ODA_DRAWENTIRE:
  402.      case ODA_SELECT:
  403.       DrawRow(dwitem, rs, xLeftCol, xRightCol,
  404.               (dwitem->itemState == ODS_SELECTED));
  405.       return;
  406.    }
  407. }
  408.  
  409.  
  410. //*------------------------------------------------------------------------
  411. //| DrawColumnTitles:
  412. //|   This function is called when we need to paint the column titles for a
  413. //|      results set.  We will simply write them out.
  414. //| Parms:
  415. //|   in       hdc                  Handle to our device contex
  416. //|   in       rs                   Our results set to draw
  417. //|   in       crect                Client rectangle to paint in
  418. //|   in       xLeftCol             Left column
  419. //|   in       xRightCol            Right column
  420. //| Returns:
  421. //|   Nothing.
  422. //*------------------------------------------------------------------------
  423. void INTFUN DrawColumnTitles(HDC hdc, RESULTSSET FAR * rs,
  424.             RECT FAR * crect, int xLeftCol, int xRightCol)
  425. {
  426.    int               dex, offset, cright=0;
  427.    RECT              rect;
  428.    HFONT             hf;
  429.  
  430.    hf = SelectObject(hdc, rs->hFont);
  431.    SetTextColor(hdc, RDATA_BLACK);
  432.    offset = 0 - rs->md[xLeftCol].xCol;
  433.    for (dex=xLeftCol; dex<=xRightCol; dex++)
  434.       cright += rs->md[dex].cColWidth;
  435.    Rectangle(hdc, crect->left, crect->top, min(cright, crect->right), crect->bottom+1);
  436.    SetBkColor(hdc, RDATA_GRAY);
  437.  
  438.    rect.top = crect->top +1;
  439.    rect.bottom = crect->bottom;
  440.    for(dex=xLeftCol;  dex<=xRightCol;  dex++) {
  441.       rect.left = rs->md[dex].xCol + offset;
  442.       rect.right = rect.left + rs->md[dex].cColWidth;
  443.       MoveTo(hdc, rect.right, rect.top);
  444.       LineTo(hdc, rect.right, rect.bottom);
  445.       ++rect.left;
  446. #ifdef TITLE_DEBUG
  447.       {
  448.          char tmpbuff[50];
  449.          wsprintf(tmpbuff, "Column: %d, left=%d, top=%d, right=%d, bottom=%d",
  450.                   dex,
  451.                   rect.left, rect.top,
  452.                   rect.right, rect.bottom);
  453.          DrawFocusRect(hdc, &rect);
  454.          MessageBox(NULL, (LPSTR)tmpbuff, "Debug", MB_OK);
  455.          DrawFocusRect(hdc, &rect);
  456.       }
  457. #endif
  458.       ExtTextOut(hdc, rs->md[dex].xCol + 3 + offset, rect.top + 4,
  459.                  ETO_CLIPPED | ETO_OPAQUE,
  460.                  &rect,
  461.                  rs->md[dex].szColumnName,
  462.                  lstrlen(rs->md[dex].szColumnName),
  463.                  NULL);
  464.    }
  465.    SelectObject(hdc,hf);               // change font back
  466. }
  467.  
  468.  
  469.  
  470. //*------------------------------------------------------------------------
  471. //| DrawRow:
  472. //|   Call this function for each row which must be painted.
  473. //| Parms:
  474. //|   in       dw                   Draw structure
  475. //|   in       rs                   Our results set to draw
  476. //|   in       xLeftCol             Index to left-most column displayed
  477. //|   in       xRightCol            Index to right-most column displayed
  478. //|   in       fSelect              Is the item supposed to be selected?
  479. //| Returns:
  480. //|   TRUE if successful,
  481. //|   FALSE otherwise
  482. //*------------------------------------------------------------------------
  483. //#define RECT_DEBUG
  484. BOOL INTFUN DrawRow(DRAWITEMSTRUCT FAR * dw,
  485.                      RESULTSSET FAR * rs,
  486.                      int xLeftCol, int xRightCol,
  487.                      BOOL fSelect)
  488. {
  489.    ROWDATA FAR *     rd=(ROWDATA FAR *)dw->itemData;
  490.    int               dex;
  491.    int               offset;
  492.    int               cright=0;
  493.    RECT              rect;
  494.    HFONT             hf;
  495.  
  496.    //
  497.    // First set the font and text colors according to the user's request, then draw
  498.    //    a line at the bottom of the row for a separator.  Note that the rcItem
  499.    //    rectangle passed to us in the DRAWITEMSTRUCT is for the
  500.    //
  501.    hf = SelectObject(dw->hDC, rs->hFont);
  502.    dw->rcItem.right = min(rs->cRowWidth, dw->rcItem.right);
  503.    for (dex=xLeftCol; dex<=xRightCol; dex++)
  504.       cright += rs->md[dex].cColWidth;
  505.    // Draw top of box
  506.    MoveTo(dw->hDC, dw->rcItem.left, dw->rcItem.top);
  507.    LineTo(dw->hDC, min(cright, dw->rcItem.right), dw->rcItem.top);
  508.  
  509.    // Draw bottom also, to take care of last line
  510.    MoveTo(dw->hDC, dw->rcItem.left, dw->rcItem.bottom);
  511.    LineTo(dw->hDC, min(cright, dw->rcItem.right), dw->rcItem.bottom);
  512.  
  513. #ifdef RECT_DEBUG
  514.    {
  515.       char tmpbuff[50];
  516.       wsprintf(tmpbuff, "dw->rcItem, left=%d, top=%d, right=%d, bottom=%d",
  517.                dw->rcItem.left, dw->rcItem.top,
  518.                dw->rcItem.right, dw->rcItem.bottom);
  519.       DrawFocusRect(dw->hDC, &dw->rcItem);
  520.       MessageBox(NULL, (LPSTR)tmpbuff, "Debug", MB_OK);
  521.       DrawFocusRect(dw->hDC, &dw->rcItem);
  522.    }
  523. #endif
  524.  
  525.    //
  526.    // Now loop through each column in the row and draw it's contents by creating
  527.    //    a logical rectangle for each column, then filling in that rectangle with
  528.    //    the value to be displayed.
  529.    //
  530.    rect.top = dw->rcItem.top+1;
  531.    rect.bottom = dw->rcItem.bottom;
  532.    SetBkMode(dw->hDC, TRANSPARENT);
  533.    if(fSelect) {
  534.       SetBkColor(dw->hDC, GetSysColor(COLOR_HIGHLIGHT));
  535.       SetTextColor(dw->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  536.    }
  537.    else {
  538.       SetBkColor(dw->hDC, rd->bkgrnd);
  539.       SetTextColor(dw->hDC, rd->textColor);
  540.    }
  541.    offset = 0 - rs->md[xLeftCol].xCol;
  542.    for (dex=xLeftCol; dex<=xRightCol; dex++) {
  543.       rect.left = offset + rs->md[dex].xCol;
  544.       rect.right = rect.left + rs->md[dex].cColWidth;
  545.       MoveTo(dw->hDC, rect.right, rect.top);
  546.       LineTo(dw->hDC, rect.right, rect.bottom);
  547.  
  548. #ifdef RECT_DEBUG
  549.       {
  550.          char tmpbuff[50];
  551.          wsprintf(tmpbuff, "Column: %d, left=%d, top=%d, right=%d, bottom=%d",
  552.                   dex,
  553.                   rect.left, rect.top,
  554.                   rect.right, rect.bottom);
  555.          DrawFocusRect(dw->hDC, &rect);
  556.          MessageBox(NULL, (LPSTR)tmpbuff, "Debug", MB_OK);
  557.          DrawFocusRect(dw->hDC, &rect);
  558.       }
  559. #endif
  560.       SetTextAlign(dw->hDC, rs->md[dex].fAlign);
  561.       if(dex != xLeftCol)
  562.          ++rect.left;
  563.       ExtTextOut(dw->hDC, rs->md[dex].xCol + 3 + offset, rect.top + 4,
  564.                  ETO_CLIPPED | ETO_OPAQUE,
  565.                  &rect,
  566.                  rd->cd[dex].szCols,
  567.                  lstrlen(rd->cd[dex].szCols),
  568.                  NULL);
  569.    }
  570.    SelectObject(dw->hDC,hf);              // change font back
  571.  
  572.    return TRUE;
  573. }
  574.  
  575.  
  576. //*------------------------------------------------------------------------
  577. //| HandleHScroll:
  578. //|   This function adds a new item to our results set.
  579. //| Parms:
  580. //|   in       wParam               Scroll option
  581. //|   in       rs                   Results set pointer
  582. //|   in       hwnd                 Window handle for column title
  583. //|   in       hwndHScroll          Scroll bar window handle
  584. //|   in       xLeftCol             Left column index
  585. //|   in       xRightCol            Right column index
  586. //|   in       hwndList             Listbox window handle
  587. //|   in       cbColumns            Number of columns
  588. //|   in       cbClient             Width of screen available to draw in
  589. //|   in       tRect                Bounding rectangle for client window
  590. //| Returns:
  591. //|   Index to string if successful, LB_ERRSPACE otherwise
  592. //*------------------------------------------------------------------------
  593. void HandleHScroll(WPARAM wParam, RESULTSSET FAR * rs,
  594.          HWND hwnd, HWND hwndHScroll, int FAR * xLeftCol, int FAR * xRightCol,
  595.          HWND hwndList, int cbColumns, int cbClient, RECT FAR * tRect)
  596. {
  597.    int      cHScrollPos;
  598.    int      fhScroll=FALSE;
  599.  
  600.    cHScrollPos = GetScrollPos(hwndHScroll, SB_CTL);
  601.    switch(wParam) {
  602.      case SB_LINEUP:          // Shift right one column
  603.       if(!*xLeftCol)
  604.          fhScroll = FALSE;
  605.       else {
  606.          --cHScrollPos;
  607.          fhScroll = TRUE;
  608.          --*xLeftCol;
  609.       }
  610.       break;
  611.  
  612.      case SB_LINEDOWN:           // Shift left one column
  613.       if(*xLeftCol+1 == cbColumns)
  614.          fhScroll = FALSE;             // No change required
  615.       else {
  616.          ++cHScrollPos;
  617.          fhScroll = TRUE;
  618.          ++*xLeftCol;
  619.       }
  620.       break;
  621.  
  622.      case SB_PAGEUP:          // Shift right one screen
  623.       if(!*xLeftCol)
  624.          fhScroll = FALSE;
  625.       else {
  626.          --cHScrollPos;
  627.          fhScroll = TRUE;
  628.          --*xLeftCol;
  629.       }
  630.       break;
  631.  
  632.      case SB_PAGEDOWN:           // Shift left one screen
  633.       if(*xLeftCol+1 == cbColumns)
  634.          fhScroll = FALSE;             // No change required
  635.       else {
  636.          if(*xLeftCol < *xRightCol) {
  637.             cHScrollPos += *xRightCol - *xLeftCol;
  638.             *xLeftCol = *xRightCol;
  639.             fhScroll = TRUE;
  640.          }
  641.          else {
  642.             ++cHScrollPos;
  643.             ++*xLeftCol;
  644.             fhScroll = TRUE;
  645.          }
  646.       }
  647.       break;
  648.  
  649.      case SB_THUMBPOSITION:      // Specific location
  650.       break;
  651.    }
  652.  
  653.    //
  654.    // If movement is required, we will have adjusted the scroll position
  655.    //    and columns already.  Calculate what columns will fit on our current
  656.    //    display to find the rightmost column.  Next invalidate the areas
  657.    //    requiring painting and set the new scroll position.  This will cause
  658.    //    each row to be redrawn starting with the new rwi->xLeftCol.
  659.    //
  660.    if(fhScroll) {                      // Movement is required
  661.       RECT     rect;
  662.       *xRightCol = FindRightCol(rs, *xLeftCol, cbClient);
  663.       GetClientRect(hwndList, &rect);
  664.       InvalidateRect(hwndList, &rect, TRUE);
  665.       SetScrollPos(hwndHScroll, SB_CTL, cHScrollPos, TRUE);
  666.       InvalidateRect(hwnd, tRect, TRUE);
  667.    }
  668. }
  669.  
  670.  
  671. //*------------------------------------------------------------------------
  672. //| HandleVirtualHScroll:
  673. //|   This function should be called in response to the WM_KEYDOWN
  674. //|   message. It will look for a virtual key to see if the user
  675. //|   is trying to do scrolling.  If so, we will force the scroll
  676. //|   to happen.
  677. //| Parms:
  678. //|   in       wParam               Value of wParam for WM_KEYDOWN
  679. //|   in       hwndList             Handle of list box
  680. //|   in       hwndOwner            Owner window of the horizontal scrollbar
  681. //| Returns:
  682. //|   Nothing.
  683. //*------------------------------------------------------------------------
  684. void HandleVirtualHScroll(WPARAM wParam, HWND hwndList, HWND hwndOwner)
  685. {
  686.    switch(wParam) {
  687.      case VK_HOME:
  688.       SendMessage(hwndList, WM_VSCROLL, SB_TOP, 0L);
  689.       return;
  690.  
  691.      case VK_END:
  692.       SendMessage(hwndList, WM_VSCROLL, SB_BOTTOM, 0L);
  693.       return;
  694.  
  695.      case VK_PRIOR:
  696.       SendMessage(hwndList, WM_VSCROLL, SB_PAGEUP, 0L);
  697.       return;
  698.  
  699.      case VK_NEXT:
  700.       SendMessage(hwndList, WM_VSCROLL, SB_PAGEDOWN, 0L);
  701.       return;
  702.  
  703.      case VK_UP:
  704.       SendMessage(hwndList, WM_VSCROLL, SB_LINEUP, 0L);
  705.       return;
  706.  
  707.      case VK_DOWN:
  708.       SendMessage(hwndList, WM_VSCROLL, SB_LINEDOWN, 0L);
  709.       return;
  710.  
  711.      case VK_LEFT:
  712.       SendMessage(hwndOwner, WM_HSCROLL, SB_LINEUP, 0L);
  713.       return;
  714.  
  715.      case VK_RIGHT:
  716.       SendMessage(hwndOwner, WM_HSCROLL, SB_LINEDOWN, 0L);
  717.       return;
  718.    }
  719. }
  720.  
  721.  
  722. //*------------------------------------------------------------------------
  723. //| AddRowData:
  724. //|   This function adds a new item to our results set.
  725. //| Parms:
  726. //|   in       rs                   Pointer to results set
  727. //|   in       rd                   Pointer to row data to add
  728. //| Returns:
  729. //|   Index to string if successful, LB_ERRSPACE otherwise
  730. //*------------------------------------------------------------------------
  731. int AddRowData(RESULTSSET FAR * rs, ROWDATA FAR * rd)
  732. {
  733.    int         rtn;
  734.    DWORD       cbCnt;
  735.    rtn = (int)SendMessage(rs->hwndList, LB_ADDSTRING, 0, (LPARAM)(ROWDATA FAR *)rd);
  736.    if(rtn == LB_ERRSPACE) {
  737.       cbCnt = SendMessage(rs->hwndList, LB_GETCOUNT, 0, 0L);
  738.       wsprintf(szErrMsg, szMaxRowsFetched, cbCnt);
  739.       MessageBox(rs->hwndClient, szErrMsg, szErrTitle, MB_OK);
  740.    }
  741.    return rtn;
  742. }
  743.  
  744.  
  745.  
  746.  
  747.  
  748.  
  749. //*------------------------------------------------------------------------
  750. //| GetNumResultsCols:
  751. //|   Given an hstmt which has an executed statement on it, find the number
  752. //|      of results columns in it.
  753. //| Parms:
  754. //|   in       hstmt                Statement handle with results set
  755. //| Returns:
  756. //|   Number of columns
  757. //*------------------------------------------------------------------------
  758. SWORD GetNumResultsCols(HSTMT hstmt)
  759. {
  760.    SWORD cbCols;
  761.    RETCODE retcode;
  762.  
  763.    retcode = SQLNumResultCols(hstmt, &cbCols);
  764.    if(RC_NOTSUCCESSFUL(retcode))
  765.       return -1;
  766.    else
  767.       return cbCols;
  768. }
  769.  
  770.  
  771.  
  772. //*------------------------------------------------------------------------
  773. //| GetTypeName:
  774. //|   This function will return the null-terminated character name of
  775. //|   the type passed in.
  776. //| Parms:
  777. //|   in       type                 SQL_TYPE or C_TYPE
  778. //|   in       fType                The fCType or fSqlType
  779. //| Returns:
  780. //|   Nothing.
  781. //*------------------------------------------------------------------------
  782. LPSTR GetTypeName(int type, int fType)
  783. {
  784.    int               dex, stopdex;
  785.    DATATYPE FAR *    dt;
  786.  
  787.    if(type == SQL_TYPE) {
  788.       stopdex = NumItems(SqlTypes);
  789.       dt = (DATATYPE FAR *)&SqlTypes;
  790.    }
  791.    else {
  792.       stopdex = NumItems(CTypes);
  793.       dt = (DATATYPE FAR *)&CTypes;
  794.    }
  795.    for(dex=0;  dex<stopdex;  dex++)
  796.       if(dt[dex].type == fType)
  797.          return dt[dex].sztype;
  798.    return (LPSTR)szTypeNotFound;
  799. }
  800.  
  801.  
  802. //*------------------------------------------------------------------------
  803. //| ConvertSqlTypeToChar:
  804. //|   This function will convert the value passed in to it's character equivalent.
  805. //| Parms:
  806. //|   in       rs                   Pointer to results set
  807. //|   in       col                  Which column is it?
  808. //|   in       inbuff               Input buffer
  809. //|   in       outbuff              Output buffer
  810. //|   in       rtnd                 Returned bytes from SQLGetData
  811. //| Returns:
  812. //|   Nothing.
  813. //*------------------------------------------------------------------------
  814. void ConvertSqlTypeToChar(RESULTSSET FAR * rs, int col, LPSTR inbuff,
  815.          LPSTR outbuff, SDWORD rtnd)
  816. {
  817.    LPSTR                   tmpstr;
  818.    SWORD FAR *             tmpsword;
  819.    SDWORD FAR *            tmpsdword;
  820.    SFLOAT FAR *            tmpsfloat;
  821.    SDOUBLE FAR *           tmpsdouble;
  822.    DATE_STRUCT FAR *       tmpdate;
  823.    TIME_STRUCT FAR *       tmptime;
  824.    TIMESTAMP_STRUCT FAR *  tmptimestmp;
  825.  
  826.    *outbuff = '\0';
  827.    switch(rs->md[col].fSqlType) {
  828.       //
  829.       // Look for any non-displayable characters and change them to periods
  830.       //
  831.      case SQL_CHAR:
  832.      case SQL_VARCHAR:
  833.      case SQL_LONGVARCHAR:
  834.       CheckDisplayMode((LPSTR)inbuff, rtnd, outbuff);
  835.       tmpstr = outbuff + rtnd;
  836.       *tmpstr = '\0';
  837.       break;
  838.  
  839.      case SQL_BINARY:
  840.      case SQL_VARBINARY:
  841.      case SQL_LONGVARBINARY:
  842.       lstrcpy(outbuff, "0x");
  843.       BinToChar(outbuff+2, (LPSTR)inbuff, rtnd);
  844.       break;
  845.  
  846.      case SQL_TINYINT:
  847.      case SQL_SMALLINT:
  848.       tmpsword = (SWORD FAR *)inbuff;
  849.       wsprintf(outbuff, "%d", *tmpsword);
  850.       break;
  851.  
  852.      case SQL_INTEGER:
  853.      case SQL_BIGINT:
  854.       tmpsdword = (SDWORD FAR *)inbuff;
  855.       wsprintf(outbuff, "%ld", *tmpsdword);
  856.       break;
  857.  
  858.      case SQL_FLOAT:
  859.      case SQL_DOUBLE:
  860.       tmpsdouble = (SDOUBLE FAR *)inbuff;
  861.       sprintf(outbuff, "%Fg", *tmpsdouble);
  862.       break;
  863.  
  864.      case SQL_REAL:
  865.       tmpsfloat = (SFLOAT FAR *)inbuff;
  866.       sprintf(outbuff, "%Fg", *tmpsfloat);
  867.       break;
  868.  
  869.      case SQL_BIT:
  870.       tmpsword = (SWORD FAR *)inbuff;
  871.       lstrcpy(outbuff, (*tmpsword) ? (LPSTR)szONE : (LPSTR)szZERO);
  872.       break;
  873.  
  874.      case SQL_DECIMAL:
  875.      case SQL_NUMERIC:
  876.       lstrcpy(outbuff, inbuff);
  877.       break;
  878.  
  879.      case SQL_DATE:
  880.       tmpdate = (DATE_STRUCT FAR *)inbuff;
  881.       wsprintf(outbuff, szdate, tmpdate->month, tmpdate->day, tmpdate->year);
  882.       break;
  883.  
  884.      case SQL_TIME:
  885.       tmptime= (TIME_STRUCT FAR *)inbuff;
  886.       wsprintf(outbuff, sztime, tmptime->hour, tmptime->minute, tmptime->second);
  887.       break;
  888.  
  889.      case SQL_TIMESTAMP:
  890.       tmptimestmp = (TIMESTAMP_STRUCT FAR *)inbuff;
  891.       wsprintf(outbuff, sztimestmp, tmptimestmp->year, tmptimestmp->month,
  892.                tmptimestmp->day, tmptimestmp->hour, tmptimestmp->minute,
  893.                tmptimestmp->second, tmptimestmp->fraction);
  894.       break;
  895.    }
  896.  
  897.    return;
  898. }
  899.  
  900.  
  901. //*------------------------------------------------------------------------
  902. //| CheckDisplayMode:
  903. //|   This function looks through a string for the count specified, then
  904. //|      changes any x"00" to a period so it can be displayed.
  905. //| Parms:
  906. //|   strin       - String coming in
  907. //|   cbin        - Byte count of incoming string
  908. //|   strout      - Output string
  909. //*------------------------------------------------------------------------
  910. void CheckDisplayMode(LPSTR strin, SDWORD cbin, LPSTR strout)
  911. {
  912.    SDWORD      dex,max=cbin;
  913.    LPSTR    str=strout;
  914.  
  915.    if(cbin < 0)
  916.       max = lstrlen(strin);
  917.    memcpy(strout, strin, (size_t)max);
  918.    for(dex=0; dex<cbin; dex++, str++)
  919.       if(!*str)
  920.          *str = '.';
  921. }
  922.  
  923.  
  924. //*------------------------------------------------------------------------
  925. //| BinToChar:
  926. //|   Takes a string and converts to its hexidecimal equivalent
  927. //*------------------------------------------------------------------------
  928. void BinToChar(LPSTR outstr, LPSTR instr, SDWORD count)
  929. {
  930.    UCHAR uletter;
  931.    LPSTR istr=instr;
  932.    LPSTR ostr=outstr;
  933.  
  934.    while(count--) {
  935.       uletter = (*instr & 0xF0) >> 4;              // High nibble
  936.       if(uletter <= 9)
  937.          *ostr++ = uletter + '0';
  938.       else
  939.          *ostr++ = 'A' + (uletter - 10);
  940.       uletter = *instr++ & 0x0F;
  941.       if(uletter <= 9)
  942.          *ostr++ = uletter + '0';
  943.       else
  944.          *ostr++ = 'A' + (uletter - 10);
  945.    }
  946.    *ostr = '\0';
  947. }
  948.  
  949.  
  950.