ZYZTLB.C

//==========================================================================; 
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
// A PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// zyztlb.c
//
// Description:
//
//
// History:
// 5/18/93
//
//==========================================================================;

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>

#include "appport.h"
#include "zyztlb.h"

#include "debug.h"


//==========================================================================;
//
//
//
//
//==========================================================================;

//--------------------------------------------------------------------------;
//
// int GetRealTextMetrics
//
// Description:
// This function gets the textmetrics of the font currently selected
// into the hdc. It returns the average char width as the return value.
//
// This function computes the average character width correctly by
// using GetTextExtent() on the string "abc...xzyABC...XYZ" which works
// out much better for proportional fonts. This is also necessary
// for correct alignment between dialog and client units.
//
// Note that this function returns the same TEXTMETRIC values that
// GetTextMetrics() does, it simply has a different return value.
//
// Arguments:
// HDC hdc:
//
// LPTEXTMETRIC ptm:
//
// Return (int):
//
// History:
// 05/11/93
//
//--------------------------------------------------------------------------;

int FNGLOBAL GetRealTextMetrics
(
HDC hdc,
LPTEXTMETRIC ptm
)
{
TCHAR achAlphabet[26 * 2]; // upper and lower case
SIZE sSize;
UINT u;
int nAveWidth;

//
// get the text metrics of the current font. note that GetTextMetrics
// gets the incorrect nAveCharWidth value for proportional fonts.
//
GetTextMetrics(hdc, ptm);
nAveWidth = ptm->tmAveCharWidth;

//
// if it's not a variable pitch font GetTextMetrics was correct
// so just return.
//
if (ptm->tmPitchAndFamily & FIXED_PITCH)
{
//
//
//
for (u = 0; u < 26; u++)
{
achAlphabet[u] = (TCHAR)(u + (UINT)'a');
achAlphabet[u + 26] = (TCHAR)(u + (UINT)'A');
}

//
// round up
//
GetTextExtentPoint(hdc, achAlphabet, SIZEOF(achAlphabet), &sSize);
nAveWidth = ((sSize.cx / 26) + 1) / 2;
}

//
// return the calculated average char width
//
return (nAveWidth);
} // GetRealTextMetrics()


//==========================================================================;
//
//
//
//
//==========================================================================;

//--------------------------------------------------------------------------;
//
// BOOL TlbPaint
//
// Description:
//
//
// Arguments:
// PZYZTABBEDLISTBOX ptlb:
//
// HWND hwnd:
//
// HDC hdc:
//
// Return (BOOL):
//
// History:
// 05/17/93
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL TlbPaint
(
PZYZTABBEDLISTBOX ptlb,
HWND hwnd,
HDC hdc
)
{
RECT rc;
HFONT hfont;
COLORREF crBk;
COLORREF crText;
int nHeight;

//
//
//
hfont = GetWindowFont(ptlb->hlb);
if (NULL == hfont)
hfont = GetStockFont(SYSTEM_FONT);

hfont = SelectObject(hdc, hfont);

crBk = SetBkColor(hdc, GetSysColor(COLOR_ACTIVECAPTION));
crText = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));

//
// compute bounding rect for title only
//
rc = ptlb->rc;
nHeight = min(ptlb->nFontHeight, rc.bottom - rc.top);
rc.bottom = rc.top + nHeight;

ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rc, NULL, 0, NULL);
TabbedTextOut(hdc, rc.left, rc.top,
ptlb->pszTitleText,
ptlb->cchTitleText,
ptlb->uTabStops,
ptlb->panTitleTabs, 0);

//
// restore the dc
//
SetBkColor(hdc, crBk);
SetTextColor(hdc, crText);

SelectObject(hdc, hfont);

return (TRUE);
} // TlbPaint()


//--------------------------------------------------------------------------;
//
// BOOL TlbMove
//
// Description:
//
//
// Arguments:
// PZYZTABBEDLISTBOX ptlb:
//
// PRECT prc:
//
// BOOL fRedraw:
//
// Return (BOOL):
//
// History:
// 05/16/93
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL TlbMove
(
PZYZTABBEDLISTBOX ptlb,
PRECT prc,
BOOL fRedraw
)
{
RECT rc;
int nHeight;
HWND hwnd;


hwnd = GetParent(ptlb->hlb);

//
// invalidate only the region occupied by the current title bar. this
// will make sure that area gets repainted. the listbox portion will
// be invalidated correctly by the SetWindowPos() function below..
//
rc = ptlb->rc;

nHeight = min(ptlb->nFontHeight, rc.bottom - rc.top);
rc.bottom = rc.top + nHeight;

InvalidateRect(hwnd, &rc, TRUE);


//
// now move the listbox--we modify values in the rect structure, so
// copy to local storage
//
rc = *prc;

//
// leave room at the top of the bounding rect for the title text
//
nHeight = min(ptlb->nFontHeight, rc.bottom - rc.top);
rc.top += nHeight;

SetWindowPos(ptlb->hlb, NULL, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, SWP_NOZORDER);

//
// save the new location and invalidate the area so it is repainted
//
ptlb->rc = *prc;
InvalidateRect(hwnd, prc, TRUE);

if (fRedraw)
{
UpdateWindow(hwnd);
}

return (TRUE);
} // TlbMove()


//--------------------------------------------------------------------------;
//
// BOOL TlbRecalcTabs
//
// Description:
//
//
// Arguments:
// PZYZTABBEDLISTBOX ptlb:
//
// Return (BOOL):
//
// History:
// 05/18/93
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL TlbRecalcTabs
(
PZYZTABBEDLISTBOX ptlb
)
{
static TCHAR szGonzoThing[] = TEXT("M");

int anTabsList[TLB_MAX_TAB_STOPS];
HDC hdc;
HFONT hfont;
TEXTMETRIC tm;
int nAveCharWidth;

UINT u;
int nWidth;
int nPrevTabTitle;
int nPrevTabList;
SIZE sSize;


//
//
//
hdc = GetDC(NULL);
{
//
// get the average char width and height of the current font so we
// can compute tabs correctly. note that GetTextMetrics is pretty
// bogus when it comes to the average char width--it normally gives
// you the width of the character 'x'. what it should be is the
// average width of all capitals and lower case letters..
//
hfont = GetWindowFont(ptlb->hlb);
if (NULL == hfont)
hfont = GetStockFont(SYSTEM_FONT);

hfont = SelectObject(hdc, hfont);

#if 0
GetTextMetrics(hdc, &tm);
nAveCharWidth = tm.tmAveCharWidth;
#else
nAveCharWidth = GetRealTextMetrics(hdc, &tm);
#endif
ptlb->nFontHeight = tm.tmHeight;


//
//
//
GetTextExtentPoint(hdc, szGonzoThing, 1, &sSize);


//
//
//
hfont = SelectObject(hdc, hfont);
}
ReleaseDC(NULL, hdc);


//
// calculate the width of each column
//
nPrevTabTitle = 0;
nPrevTabList = 0;
for (u = 0; u < ptlb->uTabStops; u++)
{
// nWidth = nAveCharWidth * ptlb->panTabs[u] + nAveCharWidth * 2;
nWidth = sSize.cx * ptlb->panTabs[u] + (sSize.cx * 2);

//
// set tabstop for title text--this is in client units
// for TabbedTextOut in TlbPaint
//
ptlb->panTitleTabs[u] = nPrevTabTitle + nWidth;
nPrevTabTitle = ptlb->panTitleTabs[u];

//
// set tabstop for listbox--this is in dialog units
//
anTabsList[u] = nPrevTabList + MulDiv(nWidth, 4, nAveCharWidth);
nPrevTabList = anTabsList[u];
}


//
// now setup the tabstops in the listbox
//
ListBox_SetTabStops(ptlb->hlb, ptlb->uTabStops, anTabsList);

return (TRUE);
} // TlbRecalcTabs()


//--------------------------------------------------------------------------;
//
// HFONT TlbSetFont
//
// Description:
//
//
// Arguments:
// PZYZTABBEDLISTBOX ptlb:
//
// HFONT hfont:
//
// BOOL fRedraw:
//
// Return (HFONT):
//
// History:
// 05/16/93
//
//--------------------------------------------------------------------------;

HFONT FNGLOBAL TlbSetFont
(
PZYZTABBEDLISTBOX ptlb,
HFONT hfont,
BOOL fRedraw
)
{
HFONT hfontOld;

//
//
//
hfontOld = GetWindowFont(ptlb->hlb);
SetWindowFont(ptlb->hlb, hfont, FALSE);

TlbRecalcTabs(ptlb);
TlbMove(ptlb, &ptlb->rc, fRedraw);

return (hfontOld);
} // TlbSetFont()


//--------------------------------------------------------------------------;
//
// BOOL TlbSetTitleAndTabs
//
// Description:
// This function sets the title text and tab stops for a Tabbed List
// Box (TLB). The pszTitleFormat specifies the title text for each
// column along with the tabstop position for each column. The format
// of this string is as follows:
//
// <columnname1>\t<tab1>!<columnname2>
//
// TCHAR szTlbThings[] = TEXT("Index\t6!Code\t5!Name");
//
//
// Arguments:
// PZYZTABBEDLISTBOX ptlb:
//
// PTSTR pszTitleFormat:
//
// BOOL fRedraw:
//
// Return (BOOL):
//
// History:
// 05/18/93
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL TlbSetTitleAndTabs
(
PZYZTABBEDLISTBOX ptlb,
PTSTR pszTitleFormat,
BOOL fRedraw
)
{
TCHAR szTitleText[TLB_MAX_TITLE_CHARS];
int anTabs[TLB_MAX_TAB_STOPS];
PTSTR pch;
PTSTR pchTitleText;
UINT uTabStops;
UINT cchTitleText;
HWND hwnd;

//
// parse the title format counting tab stops and actual size of title
// text
//
uTabStops = 0;
pchTitleText = szTitleText;
for (pch = pszTitleFormat; '\0' != *pch; )
{
TCHAR ch;

//
// scan to tab
//
while ('\0' != (ch = *pch))
{
*pchTitleText++ = *pch++;

if ('\t' == ch)
break;
}

if ('\0' == ch)
break;

//
// grab the next tab stop value
//
anTabs[uTabStops] = atoi(pch);
uTabStops++;

//
// skip to start of next column name
//
while ('!' != *pch++)
;
}


//
// terminate the converted title text
//
*pchTitleText = '\0';
cchTitleText = lstrlen(szTitleText);

//
// free the memory used for the previous tab stops and title text
//
if (NULL != ptlb->panTabs)
{
LocalFree((HLOCAL)ptlb->panTabs);

ptlb->uTabStops = 0;
ptlb->panTabs = NULL;
ptlb->panTitleTabs = NULL;
}

if (NULL != ptlb->pszTitleText)
{
LocalFree((HLOCAL)ptlb->pszTitleText);

ptlb->cchTitleText = 0;
ptlb->pszTitleText = NULL;
}


//
// allocate new space for tab stops. there are two different tab
// arrays:
//
// panTabs: original tab values as passed by caller. these are
// virtual tab locations represented as number of characters. we
// need to keep these values for recomputing the real tabs when
// the font changes.
//
// panTitleTabs: these values are computed by TlbRecalcTabs and
// are actual tab positions in client coordinates for the title
// text (needed for TabbedTextOut in TlbPaint).
//
// the tabs for the listbox are computed and set in TlbRecalcTabs
//
if (0 != uTabStops)
{
ptlb->panTabs = (PINT)LocalAlloc(LPTR, (uTabStops * sizeof(int)) * 2);
if (NULL == ptlb->panTabs)
return (FALSE);

ptlb->uTabStops = uTabStops;
ptlb->panTitleTabs = ptlb->panTabs + uTabStops;
memcpy(ptlb->panTabs, anTabs, uTabStops * sizeof(int));
}


//
// allocate space for the converted title text (stripped of the tab
// spacing values). this string is passed directly to TabbedTextOut
// in TlbPaint.
//
if (0 != cchTitleText)
{
ptlb->pszTitleText = (PTSTR)LocalAlloc(LPTR, (cchTitleText + 1) * sizeof(TCHAR));
if (NULL == ptlb->pszTitleText)
return (FALSE);

ptlb->cchTitleText = cchTitleText;
lstrcpy(ptlb->pszTitleText, szTitleText);
}



//
//
//
TlbRecalcTabs(ptlb);


//
// force a complete repaint of the title text and listbox--redraw
// immediately if we are supposed to
//
hwnd = GetParent(ptlb->hlb);
InvalidateRect(hwnd, &ptlb->rc, TRUE);
if (fRedraw)
{
UpdateWindow(hwnd);
}
} // TlbSetTitleAndTabs()


//--------------------------------------------------------------------------;
//
// PZYZTABBEDLISTBOX TlbDestroy
//
// Description:
//
//
// Arguments:
// PZYZTABBEDLISTBOX ptlb:
//
// Return (PZYZTABBEDLISTBOX):
//
// History:
// 05/16/93
//
//--------------------------------------------------------------------------;

PZYZTABBEDLISTBOX FNGLOBAL TlbDestroy
(
PZYZTABBEDLISTBOX ptlb
)
{
HWND hwnd;
int nHeight;

//
// get rid of the listbox
//
if (NULL != ptlb->hlb)
{
DestroyWindow(ptlb->hlb);

//
// invalidate area where title text was so it will be clean
//
nHeight = min(ptlb->nFontHeight, ptlb->rc.bottom - ptlb->rc.top);
ptlb->rc.bottom = ptlb->rc.top + nHeight;

hwnd = GetParent(ptlb->hlb);
InvalidateRect(hwnd, &ptlb->rc, TRUE);


//
// free the memory used for tab stops and title text
//
if (NULL != ptlb->panTabs)
LocalFree((HLOCAL)ptlb->panTabs);

if (NULL != ptlb->pszTitleText)
LocalFree((HLOCAL)ptlb->pszTitleText);
}

LocalFree((HLOCAL)ptlb);

return (NULL);
} // TlbDestroy()


//--------------------------------------------------------------------------;
//
// PZYZTABBEDLISTBOX TlbCreate
//
// Description:
//
//
// Arguments:
// HWND hwnd:
//
// int nId:
//
// PRECT prc:
//
// Return (PZYZTABBEDLISTBOX):
//
// History:
// 05/16/93
//
//--------------------------------------------------------------------------;

PZYZTABBEDLISTBOX FNGLOBAL TlbCreate
(
HWND hwnd,
int nId,
PRECT prc
)
{
#define TLB_DEF_STYLE (WS_VISIBLE|WS_CHILD|WS_VSCROLL|WS_BORDER| \
LBS_NOTIFY|LBS_NOINTEGRALHEIGHT|LBS_USETABSTOPS)

static TCHAR szNull[] = TEXT("");
static TCHAR szListBox[] = TEXT("ListBox");

PZYZTABBEDLISTBOX ptlb;
HINSTANCE hinst;


//
// create a new instance data structure..
//
ptlb = (PZYZTABBEDLISTBOX)LocalAlloc(LPTR, sizeof(*ptlb));
if (NULL == ptlb)
return (NULL);


//
// create the listbox
//
hinst = GetWindowInstance(hwnd);

ptlb->hlb = CreateWindow(szListBox, szNull, TLB_DEF_STYLE,
0, 0, 0, 0, hwnd, (HMENU)nId, hinst, NULL);
if (NULL == ptlb->hlb)
{
TlbDestroy(ptlb);
return (NULL);
}

TlbRecalcTabs(ptlb);

if (NULL != prc)
{
ptlb->rc = *prc;
TlbMove(ptlb, prc, FALSE);
}

return (ptlb);
} // TlbCreate()