home *** CD-ROM | disk | FTP | other *** search
- /*
- * LISTHSCR.C
- *
- * Added functions to support horizontal listbox scrolling. This
- * DLL is generalized to support any listbox. The FInitListboxExtents
- * function allocates local memory (from the DLLs DATA segment) for
- * the list of string extents to go in the listbox. The local handle
- * is then assigned as a property of the window, so every other
- * function first looks at this property.
- *
- * This means that any number of horizontal scrolling listboxes can
- * be used in the system and make use of these functions, as long
- * as the DLLs memory is not full.
- *
- */
-
-
- #include <windows.h>
- #include "listhscr.h"
-
-
- /*
- * This is just the label of the property given to each listbox
- * that asks for an extent list.
- */
-
- char szXTList[]="XTList";
-
- /*
- * FInitListboxExtents
- *
- * Purpose:
- * Simple helper function to initialize everything for maintaining
- * horizontal extents in a listbox. This function allocates memory
- * to hold the list of extents and assigns it to the window as a property.
- *
- * Parameters:
- * hList HWND of the listbox concerned.
- *
- * Return Value:
- * BOOL TRUE if the function was successful.
- * FALSE if memory could not be allocated.
- */
-
- BOOL __export CALLBACK FInitListboxExtents(HWND hList)
- {
- HANDLE hMem;
- WORD *pw;
-
- /*
- * Initially allocate 260 bytes, or 130 WORDs since the majority
- * of listbox usage will not require a reallocation, and
- * allocating 256 bytes is just as efficient as allocating 2
- * bytes, if not more so because of reduces overhead.
- *
- * The extra two words store the current number of extent entries
- * and the maximum number possible in this memory block.
- *
- */
-
- hMem=LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, CBALLOCUNIT + sizeof(WORD)<<1);
-
- if (hMem==NULL)
- return FALSE;
-
- /*
- * Set the first two words in the memory to the appropriate values.
- * If we can't lock it we can;'t use it!
- */
- pw=(WORD *)LocalLock(hMem);
-
- if (pw==NULL)
- {
- LocalFree(hMem);
- return FALSE;
- }
-
- *pw=0; //cExtentEntries
- *(pw+1)=CALLOCUNITS; //cExtentEntriesMax
-
- LocalUnlock(hMem);
-
- /*
- * Assign the memory handle as a property of the listbox. This allows
- * this code to take any hList and get it's extent entry list,
- * therefore having full support for multiple listboxes.
- */
- SetProp(hList, (LPSTR)szXTList, hMem);
- return TRUE;
- }
-
-
- /*
- * FFreeListboxExtents
- *
- * Purpose:
- * Release any memory used for storing the extents of the
- * horizontal listbox. This MUST be called when the listbox
- * is destroyed, like in the WM_DESTROY case of the parent window.
- *
- * Parameters:
- * hList HWND of the listbox concerned.
- *
- * Return Value:
- * BOOL TRUE if the function was successful.
- * FALSE if there is an error.
- */
-
- BOOL __export CALLBACK FFreeListboxExtents(HWND hList)
- {
- HANDLE hMem;
- BOOL fSuccess;
-
- //Load the handle to free.
- hMem=GetProp(hList, (LPSTR)szXTList);
-
- /*
- * Return a BOOL on the result. An app could keep calling this
- * function until it worked since hMem is still around.
- */
- fSuccess=(BOOL)LocalFree(hMem);
-
- if (fSuccess)
- RemoveProp(hList, (LPSTR)szXTList); //Only if handle was freed!
-
- return fSuccess;
- }
-
-
- /*
- * ResetListboxExtents
- *
- * Purpose:
- * Deletes all extents in the extent list to be used AFTER an
- * LB_RESETCONTENT is sent to the listbox.
- *
- * Parameters:
- * hList HWND of the listbox.
- *
- * Return Value:
- * none
- *
- */
-
- void __export CALLBACK ResetListboxExtents(HWND hList)
- {
- FFreeListboxExtents(hList);
- FInitListboxExtents(hList);
-
- SendMessage(hList, LB_SETHORIZONTALEXTENT, 0, 0L);
-
- //This is required to remove the scrollbar.
- SendMessage(hList, LB_DELETESTRING, 0, 0L);
- return;
- }
-
-
- /*
- * WAddExtentEntry
- *
- * Purpose:
- * Facilitates handling of the horizontal listbox by keeping
- * track of the pixel width of the longest string in the listbox.
- * The number of pixels that the listbox scrolls is the width
- * of the longest string.
- *
- * Parameters:
- * hList HWND of the listbox.
- * psz Pointer to string that is added. This must be passed
- * instead of an index into the listbox since this must
- * be called before the string is added if the scrollbar
- * is to be maintained properly.
- *
- * Return Value:
- * WORD 0 if the string added was not the longest string in
- * the listbox and therefore did not change the visibility
- * of the horizontal scrollbar.
- *
- * wExtent if the added string was the longest, thus either
- * making the scrollbar visible or changing the extent.
- *
- * -1 on an error.
- *
- */
-
- WORD __export CALLBACK WAddExtentEntry(HWND hList, LPSTR psz)
- {
- HANDLE hMem;
- WORD cExtentEntries;
- WORD cExtentEntriesMax;
- WORD *pw; //Pointer to extent memory.
- WORD wExtent;
- WORD i=0;
- WORD iRev;
-
-
- hMem=GetProp(hList, (LPSTR)szXTList);
-
- if (hMem==NULL)
- return ((WORD)-1);
-
-
- pw=(WORD *)LocalLock(hMem);
-
- if (pw==NULL)
- return ((WORD)-1);
-
- //Load the values and set pointer to start of list.
- cExtentEntries=*pw++;
- cExtentEntriesMax=*pw++;
-
- //Reallocate if necessary.
- if (cExtentEntries==cExtentEntriesMax)
- {
- LocalUnlock(hMem);
-
- //This call takes care of cExtentEntriesMax
- if (!FReAllocExtentList(hMem, TRUE))
- return ((WORD)-1);
-
- cExtentEntriesMax += CALLOCUNITS;
- pw=(WORD *)LocalLock(hMem);
-
- if (pw==NULL)
- return ((WORD)-1);
-
- pw+=2; //Skip the two counters.
- }
-
- wExtent=WGetListboxStringExtent(hList, psz);
-
-
- /*
- * Insert the new extent into the list. This list is just a sorted
- * list (descending) of the largest to smallest extents in the
- * listbox. When deleting a string, we just need to look in this
- * list for it's extent and remove that entry.
- *
- * Yeah, this can be inefficient, but this is not a real case for
- * optimization.
- *
- */
-
- if (cExtentEntries==0)
- pw[0]=wExtent;
- else
- {
- i=IFindExtentInList(pw, wExtent, cExtentEntries);
-
- for (iRev=cExtentEntries+1; iRev > i; iRev--)
- pw[iRev]=pw[iRev-1];
-
- pw[i]=wExtent;
- }
-
- cExtentEntries++;
-
- //Save these values back. pw must be decremented first.
- *(--pw)=cExtentEntriesMax;
- *(--pw)=cExtentEntries;
-
- LocalUnlock(hMem);
-
-
- /*
- * Check if the one we added is now the first. If so, then
- * we need to reset the horizontal extent.
- */
-
- if (i==0)
- {
- SendMessage(hList, LB_SETHORIZONTALEXTENT, wExtent, 0L);
- return wExtent;
- }
-
- return ((WORD)0);
- }
-
-
- /*
- * WRemoveExtentEntry
- *
- * Purpose:
- * Facilitates handling of the horizontal listbox by keeping
- * track of the pixel width of the longest string in the listbox.
- * The number of pixels that the listbox scrolls is the width
- * of the longest string.
- *
- * Parameters:
- * hList HWND of the listbox.
- * iSel WORD index of the string to be removed.
- *
- * Return Value:
- * WORD 0 if the string removed did not affect the visibilty
- * of the horizontal scrollbar, i.e. if there still is
- * a longer string or there is no scrollbar in the first
- * place.
- *
- * wExtent of the new longest string if the one removed
- * was the longest.
- *
- * -1 on an error.
- */
-
- WORD __export CALLBACK WRemoveExtentEntry(HWND hList, WORD iSel)
- {
- WORD *pw; //Pointer to extent memory.
- WORD cExtentEntries;
- WORD cExtentEntriesMax;
- WORD wExtent;
- WORD i;
- WORD iSave;
- HANDLE hMem;
- HANDLE hMemT;
- char *pch;
- WORD cb;
-
-
- hMem=GetProp(hList, (LPSTR)szXTList);
-
- if (hMem==NULL)
- return ((WORD)-1);
-
-
- pw=(WORD *)LocalLock(hMem);
-
- if (pw==NULL)
- return ((WORD)-1);
-
- //Load the values and set pointer to start of list.
- cExtentEntries=*pw++;
- cExtentEntriesMax=*pw++;
-
- if (cExtentEntries==0)
- {
- LocalUnlock(hMem);
- return ((WORD)-1);
- }
-
- //Free up memory if necessary. No reallocating smaller is not fatal.
- if ((cExtentEntriesMax-cExtentEntries)==CALLOCUNITS)
- {
- LocalUnlock(hMem);
-
- if (!FReAllocExtentList(hMem, FALSE))
- return ((WORD)-1);
-
- cExtentEntriesMax += CALLOCUNITS;
- pw=(WORD *)LocalLock(hMem);
-
- if (pw==NULL)
- return ((WORD)-1);
-
- pw+=2; //Skip the two counters.
- }
-
- cb=(WORD)SendMessage(hList, LB_GETTEXTLEN, iSel, 0L);
-
- //Temporary memory to copy the listbox string so we can get the extent.
- hMemT=LocalAlloc(LMEM_MOVEABLE, cb+2); //One extra to be safe.
- pch=LocalLock(hMemT);
-
- if (pch==NULL)
- {
- LocalUnlock(hMem);
- LocalFree(hMemT);
- return ((WORD)-1);
- }
-
- cb=(WORD)SendMessage(hList, LB_GETTEXT, iSel, (LONG)(LPSTR)pch);
-
- wExtent=WGetListboxStringExtent(hList, (LPSTR)pch);
-
- LocalUnlock(hMemT);
- LocalFree(hMemT);
-
-
- /*
- * Find the extent in the list and remove it. If it's the first,
- * then reset the horizontal extent to the second.
- */
-
- i=IFindExtentInList(pw, wExtent, cExtentEntries);
- iSave=i;
-
- while (i < cExtentEntries)
- pw[i++]=pw[i+1];
-
- cExtentEntries--;
-
- //Save these values back. pw must be decremented first.
- *(--pw)=cExtentEntriesMax;
- *(--pw)=cExtentEntries;
-
- LocalUnlock(hMem);
-
- if (iSave==0)
- {
- /*
- * Before we change the horizontal extent, we must make sure that
- * the origin of the listbox is visible through forcing a scroll.
- * If this is not done, and the listbox is scrolled one or
- * more pixels to the right, the scrollbar WILL NOT disappear
- * even if all remaining strings fit inside the client area
- * of the listbox.
- *
- * This is only done here since this the only case where this
- * might happen is when we change the extent.
- */
- SendMessage(hList, WM_HSCROLL, SB_TOP, MAKELONG(0, hList));
- SendMessage(hList, LB_SETHORIZONTALEXTENT, pw[2], 0L);
-
- return pw[2];
- }
-
-
- return ((WORD)0);
- }
-
-
- /*
- * FReAllocExtentList
- *
- * Purpose:
- * Handles reallocation of the list in blocks of +/- CBALLOCUNIT
- *
- * Parameters:
- * fGrow BOOL if TRUE, instructs this function to allocate
- * an additional ALLOCUNIT.
- * If FALSE, shrinks the memory block by an ALLOCUNIT.
- *
- * Return Value:
- * BOOL TRUE if successfully reallocated. FALSE otherwise.
- *
- */
-
- BOOL FReAllocExtentList(HANDLE hMem, BOOL fGrow)
- {
- WORD wSize;
-
- /*
- * Allocate an additional 128 entries. A 256 byte block is a
- * decent reallocation size.
- */
- wSize=LocalSize((HLOCAL)hMem);
- wSize+=(fGrow) ? ((int)CBALLOCUNIT) : (-(int)CBALLOCUNIT);
-
- /*
- * This returns FALSE if the realloc was unsuccessful. TRUE
- * otherwise because the return handle is !=0
- *
- */
- return (BOOL)LocalReAlloc(hMem, wSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
- }
-
-
- /*
- * WGetListboxStringExtent
- *
- * Purpose:
- * Returns the extent, in pixels, of a string that will be or is
- * in a listbox. The hDC of the listbox is used and an extra
- * average character width is added to the extent to insure that
- * a horizontal scrolling listbox that is based on this extent
- * will scroll such that the end of the string is visible.
- *
- * Parameters:
- * hList HWND handle to the listbox.
- * psz LPSTR pointer to string in question.
- *
- * Return Value:
- * WORD Extent of the string relative to listbox.
- *
- */
-
- WORD WGetListboxStringExtent(HWND hList, LPSTR psz)
- {
- TEXTMETRIC tm;
- HDC hDC;
- HFONT hFont;
- WORD wExtent;
-
-
- /*
- * Make sure we are using the correct font.
- */
- hDC=GetDC(hList);
- hFont=(HFONT)SendMessage(hList, WM_GETFONT, 0, 0L);
-
- if (hFont!=NULL)
- SelectObject(hDC, hFont);
-
- GetTextMetrics(hDC, &tm);
-
- /*
- * Add one average text width to insure that we see the end of the
- * string when scrolled horizontally.
- */
- wExtent=(WORD)GetTextExtent(hDC, psz, lstrlen(psz))+tm.tmAveCharWidth;
- ReleaseDC(hList, hDC);
-
- return wExtent;
- }
-
-
- /*
- * IFindExtentInList
- *
- * Purpose:
- * Does an binary search on the sorted extent list and returns
- * an index to the one that matches. If there is no match,
- * the index gives the point where the extent entry should go.
- *
- * Note that an altered search algorithm is used since the list
- * is descending instead of ascending.
- *
- * Parameters:
- * pw WORD * pointer to extent list.
- * wExtent WORD extent to find or find an index for.
- * cExtentEntries WORD count of entries in list.
- *
- * Return Value:
- * iExtent WORD index into lpw where wExtent exists or where
- * it should be inserted.
- *
- */
-
- WORD IFindExtentInList(WORD *pw, WORD wExtent, WORD cExtentEntries)
- {
- int i; //These MUST be signed!
- int iPrev;
- int iMin;
- int iMax;
-
- //Set upper limits on search.
- iMin=0;
- iMax=cExtentEntries+1;
-
-
- do
- {
- iPrev=i;
- i=(iMin + iMax) >> 1;
-
- if (i==iPrev)
- {
- i++;
- break;
- }
-
- //Change the min and max depending on which way we need to look.
- if (wExtent < pw[i]) // < since list is descending. > otherwise
- iMin=i;
- else
- iMax=i;
-
- if (iMax==iMin)
- break;
- }
- while (wExtent != pw[i]);
-
-
- /*
- * When we get here, i is either where wExtent is or where it should
- * go--so return it.
- */
- return i;
- }