//////////////////////////////////////////////////////////////////////////////
// Copyright 1990-1993 Microsoft corporation
// all rights reservered
//////////////////////////////////////////////////////////////////////////////
//
// Program: (nominally)Bloodhound
// Module: tab.c
// Purpose: creates and operates a book tab (big file folder) custom control
//
// Note: the current implementation is limited to 4 booktabs, sorry.
//
//
// ---------------------------- TABSTOP = 4 -------------------
//
// Entry Points:
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// BookTab
//
//
//
// Input:
// hwnd - our window handle
//
// Returns:
//
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
#define STRICT
#include "switches.h"
#include <windows.h>
#include <windowsx.h>
#include "tab.h"
// #include "..\bhmem.h"
//////////////////////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////////////////////
#define MAX_TABS 4
#define MAX_TAB_LABEL_LEN 128
#define ANGLE_X 5
#define ANGLE_Y 5
#define CARAT_X 2
#define CARAT_Y 2
#define FLUFF_X 0
#define FLUFF_Y 0
#define FOOTROOM_Y 3
// We use the selected tab for these calculations:
//
// tab_rect:
//
// ANGLE_X|--|
//
// - BBBBBBBBBBBBBBB
// ANGLE_Y | BWWWWWWWWWWWWWWW
// | BWWWWWWWWWWWWWWWW
// - BWWW
// BWW * <-- this is where the text_rect starts
// BWW
// BWW
//
//
// text_rect: (defined by the *'s)
//
// FLUFF_X|----|
//
// - * *
// |
// FLUFF_Y |
// | CARAT_X
// | |---|
// - ............................. -
// . . |
// . . | CARAT_Y
// . . |
// - . XXXXX XXXXX X X XXXXX . -
// text hght| . X X X X X .
// is from | . X XXX X X .
// font | . X X X X X .
// _ . X XXXXX X X X .
// . .
// . .
// . .
// .............................
//
// |---------------------|
// text width is directly
// from the font itself
// * *
//
//
//////////////////////////////////////////////////////////////////////////////
// Data Structures for this file
//////////////////////////////////////////////////////////////////////////////
typedef struct _ONETAB
{
TCHAR label[ MAX_TAB_LABEL_LEN + 1 ];
DWORD data;
RECT tab_rect;
RECT text_rect;
} ONETAB;
typedef struct _TABDATA
{
// do the tabs need updating ?
BOOL fUpdate;
RECT tabs_rect;
// font data
HFONT hfSelected;
HFONT hfUnselected;
// windows data
HWND hwndParent;
// tab data
UINT total_tabs;
UINT selected_tab;
ONETAB tab[ MAX_TABS ];
} TABDATA;
typedef TABDATA *LPTABDATA;
//////////////////////////////////////////////////////////////////////////////
// Variables Global to this file
//////////////////////////////////////////////////////////////////////////////
TCHAR szBookTabName[]=BOOK_TAB_CONTROL;
//////////////////////////////////////////////////////////////////////////////
// Macros Global to this file
//////////////////////////////////////////////////////////////////////////////
#define GetInstanceDataPtr(hwnd) ((LPTABDATA)GetWindowLong(hwnd, 0))
#define SetInstanceDataPtr(hwnd,x) (SetWindowLong(hwnd, 0, (DWORD)x))
//////////////////////////////////////////////////////////////////////////////
// Functional Prototypes for Functions Local to this File
//////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK BookTab_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL BookTab_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct);
void BookTab_OnDestroy(HWND hwnd);
void BookTab_OnLButtonDown(HWND hwnd, BOOL fDblClk, int x, int y, UINT keyFlags);
void BookTab_OnPaint(HWND hwnd);
UINT BookTab_OnGetDlgCode(HWND hwnd, MSG FAR* lpmsg);
void BookTab_OnSize(HWND hwnd, UINT state, int cx, int cy);
void BookTab_OnSetFocus(HWND hwnd, HWND hwndOldFocus);
void BookTab_OnKillFocus(HWND hwnd, HWND hwndNewFocus);
void BookTab_OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);
void BookTab_OnEnable(HWND hwnd, BOOL fEnable);
UINT BookTab_OnAddItem( HWND hwnd, LPTSTR text );
UINT BookTab_OnInsertItem( HWND hwnd, UINT index, LPTSTR text);
BOOL BookTab_OnDeleteItem( HWND hwnd, UINT index );
BOOL BookTab_OnDeleteAllItems( HWND hwnd);
BOOL BookTab_OnSetItem( HWND hwnd, UINT index, LPTSTR text );
BOOL BookTab_OnGetItem( HWND hwnd, UINT index, LPTSTR text );
UINT BookTab_OnSetCurSel( HWND hand, UINT newsel );
UINT BookTab_OnGetCurSel( HWND hand );
UINT BookTab_OnGetItemCount( HWND hwnd );
BOOL BookTab_OnSetItemData( HWND hwnd, UINT index, DWORD data );
DWORD BookTab_OnGetItemData( HWND hwnd, UINT index);
void BookTab_OnPutInBack( HWND hwnd );
BOOL IsPointInRect( int given_x, int given_y, LPRECT pRect );
void BookTab_UpdateButtons( HWND hwnd );
//////////////////////////////////////////////////////////////////////////////
// BookTab_Initialize()
//
// Initializes and registers the BookTab custom class
//
// Input:
// hInstance - the handle to our parent's instance
//
// Returns:
// True if successful, else False
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_Initialize(HINSTANCE hInstance)
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_PARENTDC;
wndclass.lpfnWndProc = BookTab_WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = sizeof( LPTABDATA );
wndclass.hInstance = hInstance;
wndclass.hIcon = NULL;
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szBookTabName;
RegisterClass ( &wndclass );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_WndProc()
//
// Distributes messages coming in to the BookTab control
//
// Input:
// hwnd - Our handle
// message - the ordinal of the incoming message
// wParam - half of the incoming data
// lParam - the other half of the incoming data
//
// Returns:
// True if we handled the message, else False
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK BookTab_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
// standard windows messages
HANDLE_MSG( hwnd, WM_CREATE, BookTab_OnCreate);
HANDLE_MSG( hwnd, WM_DESTROY, BookTab_OnDestroy);
HANDLE_MSG( hwnd, WM_LBUTTONDOWN, BookTab_OnLButtonDown);
HANDLE_MSG( hwnd, WM_PAINT, BookTab_OnPaint);
HANDLE_MSG( hwnd, WM_SIZE, BookTab_OnSize);
HANDLE_MSG( hwnd, WM_SETFOCUS, BookTab_OnSetFocus);
HANDLE_MSG( hwnd, WM_KILLFOCUS, BookTab_OnKillFocus);
HANDLE_MSG( hwnd, WM_KEYDOWN, BookTab_OnKey);
HANDLE_MSG( hwnd, WM_KEYUP, BookTab_OnKey);
// messages specific to all custom controls
HANDLE_MSG( hwnd, WM_GETDLGCODE, BookTab_OnGetDlgCode);
HANDLE_MSG( hwnd, WM_ENABLE, BookTab_OnEnable);
// messages specific to THIS custom control
case BT_ADDITEM:
return( BookTab_OnAddItem( hwnd, (LPTSTR)lParam ));
case BT_INSERTITEM:
return( BookTab_OnInsertItem( hwnd, (UINT)wParam, (LPTSTR)lParam ));
case BT_DELETEITEM:
return( BookTab_OnDeleteItem( hwnd, (UINT)wParam ));
case BT_DELETEALLITEMS:
return( BookTab_OnDeleteAllItems( hwnd ));
case BT_SETITEM:
return( BookTab_OnSetItem( hwnd, (UINT)wParam, (LPTSTR)lParam ));
case BT_GETITEM:
return( BookTab_OnGetItem( hwnd, (UINT)wParam, (LPTSTR)lParam ));
case BT_SETCURSEL:
return( BookTab_OnSetCurSel( hwnd, (UINT)wParam ));
case BT_GETCURSEL:
return( BookTab_OnGetCurSel( hwnd ));
case BT_GETITEMCOUNT:
return( BookTab_OnGetItemCount( hwnd ));
case BT_SETITEMDATA:
return( BookTab_OnSetItemData( hwnd, (UINT)wParam, (DWORD)lParam ));
case BT_GETITEMDATA:
return( BookTab_OnGetItemData( hwnd, (UINT)wParam ));
case BT_PUTINBACK:
BookTab_OnPutInBack( hwnd );
return (TRUE);
}
// pass unprocessed messages to DefWndProc...
return DefWindowProc(hwnd, message, wParam, lParam);
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnCreate()
//
// Initializes a new instance of our lovely custom control
//
// Input:
// hwnd - our window handle
// lpcreatestruct - pointer to the data with which to do our thing
//
// Returns:
// True if the instance is created, else false
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
BOOL BookTab_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct)
{
LPTABDATA pData;
UINT i;
// allocate the instance data for this control
pData = LocalAlloc( LPTR, sizeof( TABDATA ));
if( pData == NULL )
return FALSE;
SetInstanceDataPtr( hwnd, pData );
// initialize values in the control
pData->total_tabs = 0;
pData->selected_tab = 0;
pData->hwndParent = lpCreateStruct->hwndParent;
// fill the prospective tab slots with data
for( i = 0; i < MAX_TABS; i++ )
{
pData->tab[i].label[0] = TEXT('\0');
pData->tab[i].data = (DWORD)0;
}
#ifdef JAPAN // BookTab_OnCreate()
// create the proper fonts:
// 8 pt MS Gothic bold for selected and
// 8 pt MS Gothic regular for not selected
{
#ifdef UNICODE
WCHAR szGothic[] = {0xff2d, 0xff33, 0x20, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 };
#else
#error Ansi string of szGothic is nessesory.
#endif
pData->hfSelected = CreateFont( -8, 0, 0, 0,
FW_BOLD, FALSE, FALSE, FALSE,
SHIFTJIS_CHARSET, OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
0x4, szGothic );
pData->hfUnselected = CreateFont( -8, 0, 0, 0,
FW_NORMAL, FALSE, FALSE, FALSE,
SHIFTJIS_CHARSET, OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
0x4, szGothic );
}
#else
// create the proper fonts:
// 8 pt sans serif bold for selected and
// 8 pt sans serif regular for not selected
pData->hfSelected = CreateFont( -8, 0, 0, 0,
FW_BOLD, FALSE, FALSE, FALSE,
ANSI_CHARSET, OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
0x4, TEXT("MS Sans Serif") );
pData->hfUnselected = CreateFont( -8, 0, 0, 0,
FW_NORMAL, FALSE, FALSE, FALSE,
ANSI_CHARSET, OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
0x4, TEXT("MS Sans Serif") );
#endif // JAPAN
// fill the rest of the sizing info
BookTab_OnSize( hwnd, 0, lpCreateStruct->cx, lpCreateStruct->cy );
// make sure that we are on the bottom
SetWindowPos( hwnd, HWND_BOTTOM, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW );
// make sure we update
pData->fUpdate = TRUE;
// put us last
BookTab_PutInBack( hwnd );
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnDestroy()
//
// Cleans up as our control goes away
//
// Input:
// hwnd - our window handle
//
// Returns:
// nothing
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnDestroy(HWND hwnd)
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// delete our fonts
DeleteObject( pData->hfSelected );
DeleteObject( pData->hfUnselected );
// free up our instance data
LocalFree( pData );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnLButtonDown()
//
// Handles the event where a user has the left mouse button down
//
// Input:
// hwnd - our window handle
// fDblClk - an indication on the second message of a double click
// x - the mouses x coordinate at the time of the message
// y - the mouses y coordinate at the time of the message
// keyFlags - an indication of which keys were pressed at the time
//
// Returns:
// nuthin'
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnLButtonDown(HWND hwnd, BOOL fDblClk, int x, int y, UINT keyFlags)
{
LPTABDATA pData;
UINT i;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// where did they click the mouse...
// loop thru the tabs to find the one struck
for( i = 0; i < pData->total_tabs; i++ )
{
if( IsPointInRect( x, y, &(pData->tab[i].tab_rect) ) )
{
// this is the correct spot
BookTab_OnSetCurSel( hwnd, i );
// notify our parent that the selection has changed
SendMessage( pData->hwndParent, BTN_SELCHANGE,
pData->selected_tab, (DWORD)hwnd);
SetFocus( hwnd );
return;
}
}
// the mouse was clicked outside any of the button areas
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnPaint()
//
// Handles requests from windows that the control repaint itself
//
// Input:
// hwnd - our window handle
//
// Returns:
// hopefully :)
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnPaint(HWND hwnd)
{
LPTABDATA pData;
PAINTSTRUCT ps;
HDC hdc;
TEXTMETRIC tm;
UINT i;
HWND hwndFocus;
HPEN hOldPen;
HPEN hShadowPen;
HPEN hHighlightPen;
HPEN hFramePen;
HPEN hBackPen;
HBRUSH hBackBrush;
HFONT hfOldFont;
WORD cyChar;
WORD yWidth;
WORD xWidth;
RECT total;
RECT temp;
LPRECT pTab;
LPRECT pText;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// right before drawing, make sure that the button sizes are accurate
BookTab_UpdateButtons( hwnd );
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// get the handle to the window with the current focus
hwndFocus = GetFocus();
// prepare for painting...
BeginPaint( hwnd, &ps );
hdc = GetDC( hwnd );
// set text stuff
SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
SetTextAlign( hdc, TA_TOP | TA_LEFT );
// determine proper sizes
GetTextMetrics(hdc, &tm);
cyChar = (WORD)tm.tmHeight;
xWidth = GetSystemMetrics(SM_CXBORDER);
yWidth = GetSystemMetrics(SM_CYBORDER);
GetClientRect( hwnd, &total );
//BUGBUG fudge the rectangle so that the bottom and left do not get cut off
total.bottom -= yWidth;
total.right -= xWidth;
// set up the pens that we will need
hHighlightPen = CreatePen(PS_SOLID, yWidth, GetSysColor(COLOR_BTNHIGHLIGHT));
hShadowPen = CreatePen(PS_SOLID, yWidth, GetSysColor(COLOR_BTNSHADOW));
hFramePen = CreatePen(PS_SOLID, yWidth, GetSysColor(COLOR_WINDOWFRAME));
hBackPen = CreatePen(PS_SOLID, yWidth, GetSysColor(COLOR_BTNFACE));
hBackBrush = CreateSolidBrush( GetSysColor(COLOR_BTNFACE));
// get the old pen by setting a new one
hOldPen = SelectPen( hdc, hHighlightPen );
// clear out behind the tabs if we need to
if( pData->fUpdate == TRUE )
{
FillRect( hdc, &(pData->tabs_rect), hBackBrush );
pData->fUpdate = FALSE;
}
// draw the box...
// left side dark border
SelectPen( hdc, hFramePen );
MoveToEx( hdc, total.left, pData->tab[0].tab_rect.bottom+yWidth, NULL );
LineTo ( hdc, total.left, total.bottom );
// bottom dark border
LineTo ( hdc, total.right, total.bottom );
// right side dark border
LineTo ( hdc, total.right, pData->tab[0].tab_rect.bottom+yWidth);
// top dark border, right half (over to selection)
LineTo ( hdc, pData->tab[pData->selected_tab].tab_rect.right-yWidth,
pData->tab[0].tab_rect.bottom+yWidth);
// skip area under the selected tab
MoveToEx( hdc, pData->tab[pData->selected_tab].tab_rect.left,
pData->tab[0].tab_rect.bottom+yWidth, NULL);
// top dark border, left half (from selection to left border)
LineTo ( hdc, total.left, pData->tab[0].tab_rect.bottom+yWidth);
// left side highlight #1
SelectPen( hdc, hHighlightPen );
MoveToEx( hdc, total.left+xWidth, pData->tab[0].tab_rect.bottom+2*yWidth, NULL );
LineTo( hdc, total.left+xWidth, total.bottom-yWidth );
// bottom shadow #1
SelectPen( hdc, hShadowPen );
LineTo( hdc, total.right-xWidth, total.bottom-yWidth );
// right side shadow #1
LineTo( hdc, total.right-xWidth, pData->tab[0].tab_rect.bottom+2*yWidth );
// top hilite #1
SelectPen( hdc, hHighlightPen );
// top hilite, right half (over to selection)
LineTo ( hdc, pData->tab[pData->selected_tab].tab_rect.right-yWidth,
pData->tab[0].tab_rect.bottom+2*yWidth);
// skip area under the selected tab
MoveToEx( hdc, pData->tab[pData->selected_tab].tab_rect.left,
pData->tab[0].tab_rect.bottom+2*yWidth, NULL);
// top hilite, left half (from selection to left border)
if( pData->selected_tab != 0 )
LineTo ( hdc, total.left+2*xWidth,
pData->tab[0].tab_rect.bottom+2*yWidth);
// left side highlight #2
SelectPen( hdc, hHighlightPen );
MoveToEx( hdc, total.left+2*xWidth, pData->tab[0].tab_rect.bottom+3*yWidth, NULL );
LineTo( hdc, total.left+2*xWidth, total.bottom-2*yWidth );
// bottom shadow #2
SelectPen( hdc, hShadowPen );
LineTo( hdc, total.right-2*xWidth, total.bottom-2*yWidth );
// right side shadow #2
LineTo( hdc, total.right-2*xWidth, pData->tab[0].tab_rect.bottom+3*yWidth );
// top hilite #2
SelectPen( hdc, hHighlightPen );
// top hilite, right half (over to selection)
LineTo ( hdc, pData->tab[pData->selected_tab].tab_rect.right-2*yWidth,
pData->tab[0].tab_rect.bottom+3*yWidth);
// skip area under the selected tab
MoveToEx( hdc, pData->tab[pData->selected_tab].tab_rect.left,
pData->tab[0].tab_rect.bottom+3*yWidth, NULL);
// top hilite, left half (from selection to left border)
if( pData->selected_tab != 0 )
LineTo ( hdc, total.left+2*xWidth,
pData->tab[0].tab_rect.bottom+3*yWidth);
// Draw the tabs...
// loop thru the tabs
for( i = 0; i < pData->total_tabs; i++ )
{
// point our local variables at the current rects
pTab = &(pData->tab[i].tab_rect);
pText = &(pData->tab[i].text_rect);
if( i == pData->selected_tab )
{
// this is the selection, it should not be pushed down...
// left side dark border
SelectPen( hdc, hFramePen );
MoveToEx(hdc, pTab->left, pTab->bottom, NULL);
LineTo(hdc, pTab->left, pTab->top + ANGLE_Y*yWidth);
// left side angle dark border
LineTo(hdc, pTab->left + ANGLE_X*xWidth, pTab->top);
// top dark border
LineTo(hdc, pTab->right - ANGLE_X*xWidth, pTab->top);
// right side angle dark border
LineTo(hdc, pTab->right, pTab->top + ANGLE_Y*yWidth);
// right side dark border (overshoot by one)
LineTo(hdc, pTab->right, pTab->bottom+yWidth);
// left side hilite #1 (extends down 3 below the box to handle
// melding the hilites with the box below)
SelectPen( hdc, hHighlightPen);
MoveToEx(hdc, pTab->left+xWidth, pTab->bottom+3*yWidth, NULL);
LineTo(hdc, pTab->left+xWidth, pTab->top+ANGLE_Y*yWidth );
// left side angle hilight #1
LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top+yWidth );
// top hilite #1
LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top+yWidth );
// right side angle shadow #1
SelectPen( hdc, hShadowPen);
LineTo(hdc, pTab->right-xWidth, pTab->top+ANGLE_Y*yWidth );
// right side shadow #1 (overshoot by one) (see above)
LineTo(hdc, pTab->right-xWidth, pTab->bottom+3*yWidth);
// left side hilite #2 (the 2* are becaus we are the 2nd hilite)
SelectPen( hdc, hHighlightPen);
MoveToEx(hdc, pTab->left+2*xWidth, pTab->bottom+3*yWidth, NULL);
LineTo(hdc, pTab->left+2*xWidth, pTab->top+ANGLE_Y*yWidth );
// left side angle hilight #2
LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top+2*yWidth );
// top hilite #2
LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top+2*yWidth );
// right side angle shadow #2
SelectPen( hdc, hShadowPen);
LineTo(hdc, pTab->right-2*xWidth, pTab->top+ANGLE_Y*yWidth );
// right side shadow #2 (overshoot by one)
LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+4*yWidth );
// clear out the chunk below the active tab
SelectPen(hdc, hBackPen );
MoveToEx(hdc, pTab->left+3*xWidth, pTab->bottom+yWidth, NULL);
LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+yWidth);
MoveToEx(hdc, pTab->left+3*xWidth, pTab->bottom+2*yWidth, NULL);
LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+2*yWidth);
MoveToEx(hdc, pTab->left+3*xWidth, pTab->bottom+3*yWidth, NULL);
LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+3*yWidth);
// clear out the old label...
FillRect( hdc, pText, hBackBrush );
// now print in the label ...
hfOldFont = SelectObject( hdc, pData->hfSelected );
ExtTextOut( hdc,
pText->left+ CARAT_X*xWidth + FLUFF_X*xWidth,
pText->top + CARAT_Y*yWidth + FLUFF_Y*yWidth,
0, NULL, pData->tab[i].label,
lstrlen(pData->tab[i].label), NULL );
SelectFont( hdc, hfOldFont );
// if we have the focus, print the caret
if( hwnd == hwndFocus )
{
// we have the focus
temp.top = pText->top + FLUFF_X*xWidth;
temp.left = pText->left + FLUFF_Y*yWidth;
temp.bottom = pText->bottom - FLUFF_X*xWidth;
temp.right = pText->right - FLUFF_Y*yWidth;
DrawFocusRect( hdc, &temp );
}
}
else
{
// push this tab down one border width...
// this will mean an extra +1 on all ANGLE_Ys...
// left side dark border
SelectPen( hdc, hFramePen );
MoveToEx(hdc, pTab->left, pTab->bottom, NULL);
LineTo(hdc, pTab->left, pTab->top + (ANGLE_Y+1)*yWidth);
// left side angle dark border
LineTo(hdc, pTab->left + ANGLE_X*xWidth, pTab->top+yWidth);
// top dark border
LineTo(hdc, pTab->right - ANGLE_X*yWidth, pTab->top+yWidth);
// right side angle dark border
LineTo(hdc, pTab->right, pTab->top + (ANGLE_Y+1)*yWidth);
// right side dark border (overshoot by one)
LineTo(hdc, pTab->right, pTab->bottom+yWidth);
// left side hilite
SelectPen( hdc, hHighlightPen);
MoveToEx(hdc, pTab->left+xWidth, pTab->bottom, NULL);
LineTo(hdc, pTab->left+xWidth, pTab->top+(ANGLE_Y+1)*yWidth);
// left side angle hilight
LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top+2*yWidth);
// top hilite
LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top+2*yWidth);
// right side angle shadow
SelectPen( hdc, hShadowPen);
LineTo(hdc, pTab->right-xWidth, pTab->top+(ANGLE_Y+1)*yWidth);
// right side shadow (overshoot by one)
LineTo(hdc, pTab->right-xWidth, pTab->bottom+yWidth);
// clean above left angle
SelectPen( hdc, hBackPen );
MoveToEx(hdc, pTab->left, pTab->top+ANGLE_Y*yWidth, NULL );
LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top );
// clean above top
LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top);
// clean above right angle
LineTo(hdc, pTab->right, pTab->top+ANGLE_Y*yWidth );
// clean last corner
LineTo(hdc, pTab->right, pTab->top+(ANGLE_Y+1)*yWidth );
// clean up inside left hilite
MoveToEx(hdc, pTab->left+2*xWidth, pTab->bottom, NULL );
LineTo(hdc, pTab->left+2*xWidth, pTab->top+(ANGLE_Y+1)*yWidth);
// clean up inside left angle hilite
LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top+3*yWidth);
// clean up inside top hilite (noop)
LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top+3*yWidth);
// clean up inside right angle shadow (noop)
LineTo(hdc, pTab->right-2*xWidth, pTab->top+(ANGLE_Y+1)*yWidth);
// clean up inside left hilite (overshoot by one)
LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+yWidth);
// clear out the old label...
FillRect( hdc, pText, hBackBrush );
// now print in the label ...
hfOldFont = SelectObject( hdc, pData->hfUnselected );
ExtTextOut( hdc,
pText->left+ CARAT_X*xWidth + FLUFF_X*xWidth,
pText->top + CARAT_Y*yWidth + FLUFF_Y*yWidth + yWidth,
0, NULL, pData->tab[i].label,
lstrlen(pData->tab[i].label), NULL );
SelectFont( hdc, hfOldFont );
}
}
SelectPen( hdc, hOldPen);
// put the DC we used back into circulation
ReleaseDC( hwnd, hdc );
// tell windows that we're done
EndPaint( hwnd, &ps );
// clean up
DeleteObject( hHighlightPen );
DeleteObject( hShadowPen );
DeleteObject( hFramePen );
DeleteObject( hBackPen );
DeleteObject( hBackBrush );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnSize()
//
// Handles requests from windows that we should resize ourselves
//
// Input:
// hwnd - our window handle
// state - an indication of Minimized, maximized, iconic, blah blah blah
// cx - our new width
// cy - our new height
//
// Returns:
// hopefully :)
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
// need to update the button size stuff, just for hit testing
BookTab_UpdateButtons(hwnd);
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnSetFocus()
//
// Handles windows telling us that we just got the focus
//
// Input:
// hwnd - our window handle
// hwndOld - the guy who used to have the focus (i don't use)
//
// Returns:
// nyaytay
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnSetFocus(HWND hwnd, HWND hwndOldFocus)
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// make sure that we are on the bottom
SetWindowPos( hwnd, HWND_BOTTOM, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW );
// we gotta repaint just the rect for the active tab
InvalidateRect( hwnd, &(pData->tab[pData->selected_tab].tab_rect), FALSE );
UpdateWindow( hwnd );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnKillFocus()
//
// Handles windows telling us that we are just about to lose the focus
//
// Input:
// hwnd - our window handle
// hwndNew - the lucky guy who is about to have the focus (i don't use)
//
// Returns:
// nyaytay
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnKillFocus(HWND hwnd, HWND hwndNewFocus)
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// we gotta repaint just the rect for the active tab
InvalidateRect( hwnd, &(pData->tab[pData->selected_tab].tab_rect), FALSE );
UpdateWindow( hwnd );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnKey()
//
// Handes key messages sent to the control
//
// Input:
// hwnd - our window handle
// vk - the virtual key code
// fDown - is the key down?
// cRepeat - how many times it was pressed
// flags - i don't use
//
// Returns:
// nada
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// don't want key up messages
if( fDown == FALSE )
return;
// we only handle left and right cursor
switch( vk )
{
case VK_LEFT:
// move to the tab to the left (wrap if needed)
BookTab_OnSetCurSel( hwnd, (pData->selected_tab == 0)?
(pData->total_tabs-1):(pData->selected_tab-1));
// notify our parent that the selection has changed
SendMessage( pData->hwndParent, BTN_SELCHANGE,
pData->selected_tab, (DWORD)hwnd);
break;
case VK_RIGHT:
BookTab_OnSetCurSel( hwnd,
(pData->selected_tab+1) % (pData->total_tabs));
// notify our parent that the selection has changed
SendMessage( pData->hwndParent, BTN_SELCHANGE,
pData->selected_tab, (DWORD)hwnd);
break;
}
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnGetDlgCode()
//
// The windows dialog manager is asking us what type of user inputs we want
//
// Input:
// hwnd - our window handle
// lpmsg - who knows, I don't use it, it's not in the paper docs...
//
// Returns:
// a word which is a bitmap of input types
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
UINT BookTab_OnGetDlgCode(HWND hwnd, MSG FAR* lpmsg)
{
// We just want cursor keys and character keys
return( DLGC_WANTARROWS | DLGC_WANTCHARS );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnEnable()
//
// Windows is telling us that we are being enabled/disabled
//
// Input:
// hwnd - our window handle
// fEnable - Are we being enabled?
//
// Returns:
// nada
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnEnable(HWND hwnd, BOOL fEnable)
{
// BUGBUG - we look no different in either state
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnAddItem()
//
// Adds an item to the end of the tab list
//
// Input:
// hwnd - our window handle
// text - the label of the tab to add
//
// Returns:
// the index of the item as added
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
UINT BookTab_OnAddItem( HWND hwnd, LPTSTR text )
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// call the worker for insert with the current end of the tab lizst
return( BookTab_OnInsertItem( hwnd, pData->total_tabs, text) );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnInsertItem()
//
// Inserts the given item at the spot indicated and shoves the others down
//
// Input:
// hwnd - our window handle
// index - the proposed index of the new item
// text - the label to add to the new tab
//
// Returns:
// the ACTUAL new index of the item (we could sort or have to reduce
// the initial request if it would leave a gap)
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
UINT BookTab_OnInsertItem( HWND hwnd, UINT index, LPTSTR text)
{
LPTABDATA pData;
int i;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// make sure that the text will fit
if( lstrlen( text ) > MAX_TAB_LABEL_LEN-1 )
return (UINT)-1;
// are we full
// BUGBUG, silly limit in the future
if( pData->total_tabs >= MAX_TABS )
// we can not add at this time
return (UINT)-1;
// make sure that the requested index is within or adjacent to currently
// used spots
if( index > pData->total_tabs )
// change it so that index now points at the next open slot
index = pData->total_tabs;
// slide over all tabs above
for( i = (int)pData->total_tabs; i > (int)index; i-- )
{
memcpy( &(pData->tab[i]), &(pData->tab[i-1]), sizeof( ONETAB) );
}
// your room is ready sir
lstrcpy( pData->tab[index].label, text );
pData->total_tabs++;
// should clear the background
pData->fUpdate = TRUE;
return index;
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnDeleteItem()
//
// Deletes the item at the index given and closes up the gaps
//
// Input:
// hwnd - our window handle
// index - item to be deleted
//
// Returns:
// nuthin'
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
BOOL BookTab_OnDeleteItem( HWND hwnd, UINT index )
{
LPTABDATA pData;
UINT i;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// make sure that we even have an element like this
if( index >= pData->total_tabs )
return FALSE;
// slide all of the deceased successors over
for( i = index+1; i < pData->total_tabs; i++ )
{
memcpy( &(pData->tab[i-1]), &(pData->tab[i]), sizeof( ONETAB) );
}
// reduce the count to account for the deletion
pData->total_tabs--;
// should clear the background
pData->fUpdate = TRUE;
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnDeleteAllItems()
//
// Genocide on tabs
//
// Input:
// hwnd - our window handle
//
// Returns:
// nothing
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
BOOL BookTab_OnDeleteAllItems( HWND hwnd)
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// BUGBUG just set our count to zero
pData->total_tabs = 0;
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnSetItem()
//
// Sets the title of the booktab given
//
// Input:
// hwnd - our window handle
// index - the tab to label
// text - the words to put on the tab
//
// Returns:
// TRUE if successful, else False
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
BOOL BookTab_OnSetItem( HWND hwnd, UINT index, LPTSTR text )
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// make sure that the text will fit
if( lstrlen( text ) > MAX_TAB_LABEL_LEN-1 )
return FALSE;
// make sure that the index is legal
if( index >= pData->total_tabs )
return FALSE;
// set the title
lstrcpy( pData->tab[index].label, text );
// we are changing the size of the tab, we will need to clean out behind
pData->fUpdate = TRUE;
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnGetItem()
//
// Retrieves a booktab title
//
// Input:
// hwnd - our window handle
// index - the tab to label
// text - the buffer to fill with the tab title
//
// Returns:
// a pointer to the filled buffer if successful, else NULL
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
BOOL BookTab_OnGetItem( HWND hwnd, UINT index, LPTSTR text )
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// make sure that the index is legal
if( index >= pData->total_tabs )
return FALSE;
// get the title
lstrcpy( text, pData->tab[index].label );
return( TRUE );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnSetCurSel()
//
// Sets the current selection
//
// Input:
// hwnd - our window handle
// newsel - the requested selection
//
// Returns:
// the new current selection
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
UINT BookTab_OnSetCurSel( HWND hwnd, UINT newsel )
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// make sure that the requested selection is within the proper bounds
if( newsel >= pData->total_tabs )
return( pData->selected_tab );
// make sure that the selection actually changed
if( newsel != pData->selected_tab )
{
// set selection
pData->selected_tab = newsel;
// make us redraw
InvalidateRect( hwnd, NULL, FALSE );
UpdateWindow( hwnd );
}
return( pData->selected_tab );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_GetCurSel()
//
// Retrieves the current selection
//
// Input:
// hwnd - our window handle
//
// Returns:
// the current selection
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
UINT BookTab_OnGetCurSel( HWND hwnd )
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// get selection
return( pData->selected_tab );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnGetItemCount()
//
// Retrieves the number of tabs currently in use
//
// Input:
// hwnd - our window handle
//
// Returns:
// the number of tabs in use
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
UINT BookTab_OnGetItemCount( HWND hwnd )
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// get the number of tabs
return( pData->total_tabs );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnSetItemData()
//
// Adds a DWORD of data to the data structure for the given tab
//
// Input:
// hwnd - our window handle
// index - which tab to add data to
// data - what to add
//
// Returns:
// TRUE if succcessful, else FALSE
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
BOOL BookTab_OnSetItemData( HWND hwnd, UINT index, DWORD data )
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// set the instance data
pData->tab[index].data = data;
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnPutInBack()
//
// Sets the focus to the booktab and then back to whoever had it first,
// this seemes to put this control in the very back.
//
// Input:
// hwnd - our window handle
//
// Returns:
// <nothing>
//
// History
// Arthur Brooking 1/21/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_OnPutInBack( HWND hwnd )
{
HWND hwndOldFocus;
// set the focus to us
hwndOldFocus = SetFocus( hwnd );
// if there was an old focus, set it back to that.
if( hwndOldFocus )
SetFocus( hwndOldFocus );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_OnGetItemData()
//
// Gets the DWORD of data stored in the data structure for the given tab
//
// Input:
// hwnd - our window handle
// index - which tab to get data from
//
// Returns:
// the stored DWORD
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
DWORD BookTab_OnGetItemData( HWND hwnd, UINT index)
{
LPTABDATA pData;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// get the instance data
return( (DWORD)pData->tab[index].data );
}
//////////////////////////////////////////////////////////////////////////////
// IsPointInRect()
//
// determines if the point specifier is in the rectangle specified
//
// Input:
// given_x - x coordinate of the point to be tested
// given_y - y coordinate of the point to be tested
// pTab - a pointer to the rectangle to test against
//
// Returns:
// True if the point is within the rectangle, False if not
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
BOOL IsPointInRect( int given_x, int given_y, LPRECT pRect )
{
// is it above
if( given_y < pRect->top )
return FALSE;
// is it below
if( given_y > pRect->bottom )
return FALSE;
// is it to the left
if( given_x < pRect->left )
return FALSE;
// is it to the right
if( given_x > pRect->right )
return FALSE;
// well, it must be inside
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_UpdateButtons()
//
// Takes the current data and updates the sizes of the tabs
//
// Input:
// hwnd - our window handle
//
// Returns:
// nuthin
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////
void BookTab_UpdateButtons( HWND hwnd )
{
LPTABDATA pData;
HDC hdc;
SIZE cur_size;
RECT total_rect;
WORD yWidth;
WORD xWidth;
UINT left;
UINT i;
HFONT hfOldFont;
// get the instance data
pData = GetInstanceDataPtr( hwnd );
// preset this so that the MAXes later will work
pData->tabs_rect.bottom = 0;
xWidth = GetSystemMetrics(SM_CXBORDER);
yWidth = GetSystemMetrics(SM_CYBORDER);
GetClientRect( hwnd, &total_rect);
// BUGBUG cheat to see the whole thing
total_rect.bottom -= yWidth;
total_rect.right -= xWidth;
hdc = GetDC( hwnd );
// use the selected font (BOLD) to size the tabs
hfOldFont = SelectObject( hdc, pData->hfSelected );
// loop thru the tabs
left = total_rect.left;
for( i = 0; i < pData->total_tabs; i++ )
{
// get the size of the data for this tab
GetTextExtentPoint( hdc, pData->tab[i].label,
lstrlen( pData->tab[i].label), &cur_size);
// calculate the text rectatangle first ...
// the text top is down the size of the angle
pData->tab[i].text_rect.top = total_rect.top + ANGLE_Y*yWidth;
// the text left is over the size of the angle
pData->tab[i].text_rect.left = left + ANGLE_X*xWidth;
// the text bottom is down from the top the size of the text +
// 2x the fluff(top and bottom) + 2x the carat space
pData->tab[i].text_rect.bottom = pData->tab[i].text_rect.top +
cur_size.cy + 2*FLUFF_Y*yWidth + 2*CARAT_Y*yWidth;
// the text right is over from the left the size of the text +
// 2x the fluff(left and right) + 2x the carat space
pData->tab[i].text_rect.right = pData->tab[i].text_rect.left +
cur_size.cx + 2*FLUFF_X*xWidth + 2*CARAT_X*xWidth;
// then calculate the full tab rectangle
// the tab top is the top of the control
pData->tab[i].tab_rect.top = total_rect.top;
// the left side of the tab is next to the previous right
pData->tab[i].tab_rect.left = left;
// the tab bottom is down the footroom from the text bottom
pData->tab[i].tab_rect.bottom = pData->tab[i].text_rect.bottom +
FOOTROOM_Y*yWidth;
// the tab right is over the size of the angle from the text right
pData->tab[i].tab_rect.right = pData->tab[i].text_rect.right +
ANGLE_Y*yWidth;
// set the left for the next guy to be our right
left = pData->tab[i].tab_rect.right;
// set the bottom of the all tabs rectangle
pData->tabs_rect.bottom = max( pData->tabs_rect.bottom,
pData->tab[i].tab_rect.bottom);
// BUGBUG check for run off the side
}
// set the rest of the cumulative tabs rect
pData->tabs_rect.top = total_rect.top;
pData->tabs_rect.right = total_rect.right;
pData->tabs_rect.left = total_rect.left;
// BUGBUG why
pData->tabs_rect.bottom++;
// reset the font
SelectObject( hdc, hfOldFont );
// free up the resources used
ReleaseDC( hwnd, hdc );
}
//////////////////////////////////////////////////////////////////////////////
// BookTab_()
//
//
//
// Input:
// hwnd - our window handle
//
// Returns:
//
//
// History
// Arthur Brooking 8/06/93 created
//////////////////////////////////////////////////////////////////////////////