home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 18.ddi / SAMPLES / LISTHORZ / LISTHSCR.C_ / LISTHSCR.C
Encoding:
C/C++ Source or Header  |  1993-02-08  |  14.3 KB  |  568 lines

  1. /*
  2.  * LISTHSCR.C
  3.  *
  4.  * Added functions to support horizontal listbox scrolling.  This
  5.  * DLL is generalized to support any listbox.  The FInitListboxExtents
  6.  * function allocates local memory (from the DLLs DATA segment) for
  7.  * the list of string extents to go in the listbox.  The local handle
  8.  * is then assigned as a property of the window, so every other
  9.  * function first looks at this property.
  10.  *
  11.  * This means that any number of horizontal scrolling listboxes can
  12.  * be used in the system and make use of these functions, as long
  13.  * as the DLLs memory is not full.
  14.  *
  15.  */
  16.  
  17.  
  18. #include <windows.h>
  19. #include "listhscr.h"
  20.  
  21.  
  22. /*
  23.  * This is just the label of the property given to each listbox
  24.  * that asks for an extent list.
  25.  */
  26.  
  27. char szXTList[]="XTList";
  28.  
  29. /*
  30.  * FInitListboxExtents
  31.  *
  32.  * Purpose:
  33.  *  Simple helper function to initialize everything for maintaining
  34.  *  horizontal extents in a listbox.  This function allocates memory
  35.  *  to hold the list of extents and assigns it to the window as a property.
  36.  *
  37.  * Parameters:
  38.  *  hList       HWND of the listbox concerned.
  39.  *
  40.  * Return Value:
  41.  *  BOOL        TRUE if the function was successful.
  42.  *              FALSE if memory could not be allocated.
  43.  */
  44.  
  45. BOOL __export CALLBACK FInitListboxExtents(HWND hList)
  46. {
  47.     HANDLE      hMem;
  48.     WORD        *pw;
  49.  
  50.     /*
  51.      * Initially allocate 260 bytes, or 130 WORDs since the majority
  52.      * of listbox usage will not require a reallocation, and
  53.      * allocating 256 bytes is just as efficient as allocating 2
  54.      * bytes, if not more so because of reduces overhead.
  55.      *
  56.      * The extra two words store the current number of extent entries
  57.      * and the maximum number possible in this memory block.
  58.      *
  59.      */
  60.  
  61.     hMem=LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, CBALLOCUNIT + sizeof(WORD)<<1);
  62.  
  63.     if (hMem==NULL)
  64.         return FALSE;
  65.  
  66.     /*
  67.      * Set the first two words in the memory to the appropriate values.
  68.      * If we can't lock it we can;'t use it!
  69.      */
  70.     pw=(WORD *)LocalLock(hMem);
  71.  
  72.     if (pw==NULL)
  73.     {
  74.         LocalFree(hMem);
  75.         return FALSE;
  76.     }
  77.  
  78.     *pw=0;                  //cExtentEntries
  79.     *(pw+1)=CALLOCUNITS;    //cExtentEntriesMax
  80.  
  81.     LocalUnlock(hMem);
  82.  
  83.     /*
  84.      * Assign the memory handle as a property of the listbox.  This allows
  85.      * this code to take any hList and get it's extent entry list,
  86.      * therefore having full support for multiple listboxes.
  87.      */
  88.     SetProp(hList, (LPSTR)szXTList, hMem);
  89.     return TRUE;
  90. }
  91.  
  92.  
  93. /*
  94.  * FFreeListboxExtents
  95.  *
  96.  * Purpose:
  97.  *  Release any memory used for storing the extents of the
  98.  *  horizontal listbox.  This MUST be called when the listbox
  99.  *  is destroyed, like in the WM_DESTROY case of the parent window.
  100.  *
  101.  * Parameters:
  102.  *  hList       HWND of the listbox concerned.
  103.  *
  104.  * Return Value:
  105.  *  BOOL        TRUE if the function was successful.
  106.  *              FALSE if there is an error.
  107.  */
  108.  
  109. BOOL __export CALLBACK FFreeListboxExtents(HWND hList)
  110. {
  111.     HANDLE      hMem;
  112.     BOOL        fSuccess;
  113.  
  114.     //Load the handle to free.
  115.     hMem=GetProp(hList, (LPSTR)szXTList);
  116.  
  117.     /*
  118.      * Return a BOOL on the result.  An app could keep calling this
  119.      * function until it worked since hMem is still around.
  120.      */
  121.     fSuccess=(BOOL)LocalFree(hMem);
  122.  
  123.     if (fSuccess)
  124.         RemoveProp(hList, (LPSTR)szXTList); //Only if handle was freed!
  125.  
  126.     return fSuccess;
  127. }
  128.  
  129.  
  130. /*
  131.  * ResetListboxExtents
  132.  *
  133.  * Purpose:
  134.  *  Deletes all extents in the extent list to be used AFTER an
  135.  *  LB_RESETCONTENT is sent to the listbox.
  136.  *
  137.  * Parameters:
  138.  *  hList       HWND of the listbox.
  139.  *
  140.  * Return Value:
  141.  *  none
  142.  *
  143.  */
  144.  
  145. void __export CALLBACK ResetListboxExtents(HWND hList)
  146. {
  147.     FFreeListboxExtents(hList);
  148.     FInitListboxExtents(hList);
  149.  
  150.     SendMessage(hList, LB_SETHORIZONTALEXTENT, 0, 0L);
  151.  
  152.     //This is required to remove the scrollbar.
  153.     SendMessage(hList, LB_DELETESTRING, 0, 0L);
  154.     return;
  155. }
  156.  
  157.  
  158. /*
  159.  * WAddExtentEntry
  160.  *
  161.  * Purpose:
  162.  *  Facilitates handling of the horizontal listbox by keeping
  163.  *  track of the pixel width of the longest string in the listbox.
  164.  *  The number of pixels that the listbox scrolls is the width
  165.  *  of the longest string.
  166.  *
  167.  * Parameters:
  168.  *  hList       HWND of the listbox.
  169.  *  psz         Pointer to string that is added.  This must be passed
  170.  *              instead of an index into the listbox since this must
  171.  *              be called before the string is added if the scrollbar
  172.  *              is to be maintained properly.
  173.  *
  174.  * Return Value:
  175.  *  WORD        0 if the string added was not the longest string in
  176.  *              the listbox and therefore did not change the visibility
  177.  *              of the horizontal scrollbar.
  178.  *
  179.  *              wExtent if the added string was the longest, thus either
  180.  *              making the scrollbar visible or changing the extent.
  181.  *
  182.  *              -1 on an error.
  183.  *
  184.  */
  185.  
  186. WORD __export CALLBACK WAddExtentEntry(HWND hList, LPSTR psz)
  187. {
  188.     HANDLE      hMem;
  189.     WORD        cExtentEntries;
  190.     WORD        cExtentEntriesMax;
  191.     WORD        *pw;       //Pointer to extent memory.
  192.     WORD        wExtent;
  193.     WORD        i=0;
  194.     WORD        iRev;
  195.  
  196.  
  197.     hMem=GetProp(hList, (LPSTR)szXTList);
  198.  
  199.     if (hMem==NULL)
  200.         return ((WORD)-1);
  201.  
  202.  
  203.     pw=(WORD *)LocalLock(hMem);
  204.  
  205.     if (pw==NULL)
  206.         return ((WORD)-1);
  207.  
  208.     //Load the values and set pointer to start of list.
  209.     cExtentEntries=*pw++;
  210.     cExtentEntriesMax=*pw++;
  211.  
  212.     //Reallocate if necessary.
  213.     if (cExtentEntries==cExtentEntriesMax)
  214.     {
  215.         LocalUnlock(hMem);
  216.  
  217.         //This call takes care of cExtentEntriesMax
  218.         if (!FReAllocExtentList(hMem, TRUE))
  219.             return ((WORD)-1);
  220.  
  221.         cExtentEntriesMax += CALLOCUNITS;
  222.         pw=(WORD *)LocalLock(hMem);
  223.  
  224.         if (pw==NULL)
  225.             return ((WORD)-1);
  226.  
  227.         pw+=2;  //Skip the two counters.
  228.     }
  229.  
  230.     wExtent=WGetListboxStringExtent(hList, psz);
  231.  
  232.  
  233.     /*
  234.      * Insert the new extent into the list.  This list is just a sorted
  235.      * list (descending) of the largest to smallest extents in the
  236.      * listbox.  When deleting a string, we just need to look in this
  237.      * list for it's extent and remove that entry.
  238.      *
  239.      * Yeah, this can be inefficient, but this is not a real case for
  240.      * optimization.
  241.      *
  242.      */
  243.  
  244.     if (cExtentEntries==0)
  245.         pw[0]=wExtent;
  246.     else
  247.     {
  248.         i=IFindExtentInList(pw, wExtent, cExtentEntries);
  249.  
  250.         for (iRev=cExtentEntries+1; iRev > i; iRev--)
  251.             pw[iRev]=pw[iRev-1];
  252.  
  253.         pw[i]=wExtent;
  254.     }
  255.  
  256.     cExtentEntries++;
  257.  
  258.     //Save these values back.  pw must be decremented first.
  259.     *(--pw)=cExtentEntriesMax;
  260.     *(--pw)=cExtentEntries;
  261.  
  262.     LocalUnlock(hMem);
  263.  
  264.  
  265.     /*
  266.      * Check if the one we added is now the first.  If so, then
  267.      * we need to reset the horizontal extent.
  268.      */
  269.  
  270.     if (i==0)
  271.     {
  272.         SendMessage(hList, LB_SETHORIZONTALEXTENT, wExtent, 0L);
  273.         return wExtent;
  274.     }
  275.  
  276.     return ((WORD)0);
  277. }
  278.  
  279.  
  280. /*
  281.  * WRemoveExtentEntry
  282.  *
  283.  * Purpose:
  284.  *  Facilitates handling of the horizontal listbox by keeping
  285.  *  track of the pixel width of the longest string in the listbox.
  286.  *  The number of pixels that the listbox scrolls is the width
  287.  *  of the longest string.
  288.  *
  289.  * Parameters:
  290.  *  hList       HWND of the listbox.
  291.  *  iSel        WORD index of the string to be removed.
  292.  *
  293.  * Return Value:
  294.  *  WORD        0 if the string removed did not affect the visibilty
  295.  *              of the horizontal scrollbar, i.e. if there still is
  296.  *              a longer string or there is no scrollbar in the first
  297.  *              place.
  298.  *
  299.  *              wExtent of the new longest string if the one removed
  300.  *              was the longest.
  301.  *
  302.  *              -1 on an error.
  303.  */
  304.  
  305. WORD __export CALLBACK WRemoveExtentEntry(HWND hList, WORD iSel)
  306. {
  307.     WORD        *pw;       //Pointer to extent memory.
  308.     WORD        cExtentEntries;
  309.     WORD        cExtentEntriesMax;
  310.     WORD        wExtent;
  311.     WORD        i;
  312.     WORD        iSave;
  313.     HANDLE      hMem;
  314.     HANDLE      hMemT;
  315.     char        *pch;
  316.     WORD        cb;
  317.  
  318.  
  319.     hMem=GetProp(hList, (LPSTR)szXTList);
  320.  
  321.     if (hMem==NULL)
  322.         return ((WORD)-1);
  323.  
  324.  
  325.     pw=(WORD *)LocalLock(hMem);
  326.  
  327.     if (pw==NULL)
  328.         return ((WORD)-1);
  329.  
  330.     //Load the values and set pointer to start of list.
  331.     cExtentEntries=*pw++;
  332.     cExtentEntriesMax=*pw++;
  333.  
  334.     if (cExtentEntries==0)
  335.     {
  336.         LocalUnlock(hMem);
  337.         return ((WORD)-1);
  338.     }
  339.  
  340.     //Free up memory if necessary.  No reallocating smaller is not fatal.
  341.     if ((cExtentEntriesMax-cExtentEntries)==CALLOCUNITS)
  342.     {
  343.         LocalUnlock(hMem);
  344.  
  345.         if (!FReAllocExtentList(hMem, FALSE))
  346.             return ((WORD)-1);
  347.  
  348.         cExtentEntriesMax += CALLOCUNITS;
  349.         pw=(WORD *)LocalLock(hMem);
  350.  
  351.         if (pw==NULL)
  352.             return ((WORD)-1);
  353.  
  354.         pw+=2;  //Skip the two counters.
  355.     }
  356.  
  357.     cb=(WORD)SendMessage(hList, LB_GETTEXTLEN, iSel, 0L);
  358.  
  359.     //Temporary memory to copy the listbox string so we can get the extent.
  360.     hMemT=LocalAlloc(LMEM_MOVEABLE, cb+2);  //One extra to be safe.
  361.     pch=LocalLock(hMemT);
  362.  
  363.     if (pch==NULL)
  364.     {
  365.         LocalUnlock(hMem);
  366.         LocalFree(hMemT);
  367.         return ((WORD)-1);
  368.     }
  369.  
  370.     cb=(WORD)SendMessage(hList, LB_GETTEXT, iSel, (LONG)(LPSTR)pch);
  371.  
  372.     wExtent=WGetListboxStringExtent(hList, (LPSTR)pch);
  373.  
  374.     LocalUnlock(hMemT);
  375.     LocalFree(hMemT);
  376.  
  377.  
  378.     /*
  379.      * Find the extent in the list and remove it.  If it's the first,
  380.      * then reset the horizontal extent to the second.
  381.      */
  382.  
  383.     i=IFindExtentInList(pw, wExtent, cExtentEntries);
  384.     iSave=i;
  385.  
  386.     while (i < cExtentEntries)
  387.         pw[i++]=pw[i+1];
  388.  
  389.     cExtentEntries--;
  390.  
  391.     //Save these values back.  pw must be decremented first.
  392.     *(--pw)=cExtentEntriesMax;
  393.     *(--pw)=cExtentEntries;
  394.  
  395.     LocalUnlock(hMem);
  396.  
  397.     if (iSave==0)
  398.     {
  399.         /*
  400.          * Before we change the horizontal extent, we must make sure that
  401.          * the origin of the listbox is visible through forcing a scroll.
  402.          * If this is not done, and the listbox is scrolled one or
  403.          * more pixels to the right, the scrollbar WILL NOT disappear
  404.          * even if all remaining strings fit inside the client area
  405.          * of the listbox.
  406.          *
  407.          * This is only done here since this the only case where this
  408.          * might happen is when we change the extent.
  409.          */
  410.         SendMessage(hList, WM_HSCROLL, SB_TOP, MAKELONG(0, hList));
  411.         SendMessage(hList, LB_SETHORIZONTALEXTENT, pw[2], 0L);
  412.  
  413.         return pw[2];
  414.     }
  415.  
  416.  
  417.     return ((WORD)0);
  418. }
  419.  
  420.  
  421. /*
  422.  * FReAllocExtentList
  423.  *
  424.  * Purpose:
  425.  *  Handles reallocation of the list in blocks of +/- CBALLOCUNIT
  426.  *
  427.  * Parameters:
  428.  *  fGrow       BOOL if TRUE, instructs this function to allocate
  429.  *              an additional ALLOCUNIT.
  430.  *              If FALSE, shrinks the memory block by an ALLOCUNIT.
  431.  *
  432.  * Return Value:
  433.  *  BOOL        TRUE if successfully reallocated.  FALSE otherwise.
  434.  *
  435.  */
  436.  
  437. BOOL FReAllocExtentList(HANDLE hMem, BOOL fGrow)
  438. {
  439.     WORD    wSize;
  440.  
  441.     /*
  442.      * Allocate an additional 128 entries.  A 256 byte block is a
  443.      * decent reallocation size.
  444.      */
  445.     wSize=LocalSize((HLOCAL)hMem);
  446.     wSize+=(fGrow) ?  ((int)CBALLOCUNIT) : (-(int)CBALLOCUNIT);
  447.  
  448.     /*
  449.      * This returns FALSE if the realloc was unsuccessful.  TRUE
  450.      * otherwise because the return handle is  !=0
  451.      *
  452.      */
  453.     return (BOOL)LocalReAlloc(hMem, wSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
  454. }
  455.  
  456.  
  457. /*
  458.  * WGetListboxStringExtent
  459.  *
  460.  * Purpose:
  461.  *  Returns the extent, in pixels, of a string that will be or is
  462.  *  in a listbox.  The hDC of the listbox is used and an extra
  463.  *  average character width is added to the extent to insure that
  464.  *  a horizontal scrolling listbox that is based on this extent
  465.  *  will scroll such that the end of the string is visible.
  466.  *
  467.  * Parameters:
  468.  *  hList       HWND handle to the listbox.
  469.  *  psz         LPSTR pointer to string in question.
  470.  *
  471.  * Return Value:
  472.  *  WORD        Extent of the string relative to listbox.
  473.  *
  474.  */
  475.  
  476. WORD WGetListboxStringExtent(HWND hList, LPSTR psz)
  477. {
  478.     TEXTMETRIC  tm;
  479.     HDC         hDC;
  480.     HFONT       hFont;
  481.     WORD        wExtent;
  482.  
  483.  
  484.     /*
  485.      * Make sure we are using the correct font.
  486.      */
  487.     hDC=GetDC(hList);
  488.     hFont=(HFONT)SendMessage(hList, WM_GETFONT, 0, 0L);
  489.  
  490.     if (hFont!=NULL)
  491.         SelectObject(hDC, hFont);
  492.  
  493.     GetTextMetrics(hDC, &tm);
  494.  
  495.     /*
  496.      * Add one average text width to insure that we see the end of the
  497.      * string when scrolled horizontally.
  498.      */
  499.     wExtent=(WORD)GetTextExtent(hDC, psz, lstrlen(psz))+tm.tmAveCharWidth;
  500.     ReleaseDC(hList, hDC);
  501.  
  502.     return wExtent;
  503. }
  504.  
  505.  
  506. /*
  507.  * IFindExtentInList
  508.  *
  509.  * Purpose:
  510.  *  Does an binary search on the sorted extent list and returns
  511.  *  an index to the one that matches.  If there is no match,
  512.  *  the index gives the point where the extent entry should go.
  513.  *
  514.  *  Note that an altered search algorithm is used since the list
  515.  *  is descending instead of ascending.
  516.  *
  517.  * Parameters:
  518.  *  pw             WORD * pointer to extent list.
  519.  *  wExtent        WORD extent to find or find an index for.
  520.  *  cExtentEntries WORD count of entries in list.
  521.  *
  522.  * Return Value:
  523.  *  iExtent    WORD index into lpw where wExtent exists or where
  524.  *             it should be inserted.
  525.  *
  526.  */
  527.  
  528. WORD IFindExtentInList(WORD *pw, WORD wExtent, WORD cExtentEntries)
  529. {
  530.     int     i;      //These MUST be signed!
  531.     int     iPrev;
  532.     int     iMin;
  533.     int     iMax;
  534.  
  535.     //Set upper limits on search.
  536.     iMin=0;
  537.     iMax=cExtentEntries+1;
  538.  
  539.  
  540.     do
  541.     {
  542.         iPrev=i;
  543.         i=(iMin + iMax) >> 1;
  544.  
  545.         if (i==iPrev)
  546.         {
  547.             i++;
  548.             break;
  549.         }
  550.  
  551.         //Change the min and max depending on which way we need to look.
  552.         if (wExtent < pw[i])   // < since list is descending. > otherwise
  553.             iMin=i;
  554.         else
  555.             iMax=i;
  556.  
  557.         if (iMax==iMin)
  558.             break;
  559.     }
  560.     while (wExtent != pw[i]);
  561.  
  562.  
  563.     /*
  564.      * When we get here, i is either where wExtent is or where it should
  565.      * go--so return it.
  566.      */
  567.     return i;
  568. }