📄 tab.c
字号:
/*
* Tab control
*
* Copyright 1998 Anders Carlsson
* Copyright 1999 Alex Priem <alexp@sci.kun.nl>
* Copyright 1999 Francis Beaudet
* Copyright 2003 Vitaliy Margolen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTES
*
* This code was audited for completeness against the documented features
* of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
*
* Unless otherwise noted, we believe this code to be complete, as per
* the specification mentioned above.
* If you discover missing features, or bugs, please note them below.
*
* TODO:
*
* Styles:
* TCS_MULTISELECT
* TCS_RIGHT
* TCS_RIGHTJUSTIFY
* TCS_SCROLLOPPOSITE
* TCS_SINGLELINE
* TCIF_RTLREADING
*
* Extended Styles:
* TCS_EX_FLATSEPARATORS
* TCS_EX_REGISTERDROP
*
* States:
* TCIS_BUTTONPRESSED
*
* Notifications:
* NM_RELEASEDCAPTURE
* TCN_FOCUSCHANGE
* TCN_GETOBJECT
* TCN_KEYDOWN
*
* Messages:
* TCM_REMOVEIMAGE
* TCM_DESELECTALL
* TCM_GETEXTENDEDSTYLE
* TCM_SETEXTENDEDSTYLE
*
* Macros:
* TabCtrl_AdjustRect
*
*/
#include <stdarg.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "uxtheme.h"
#include "tmschema.h"
#include "wine/debug.h"
#include <math.h>
WINE_DEFAULT_DEBUG_CHANNEL(tab);
typedef struct
{
UINT mask;
DWORD dwState;
LPWSTR pszText;
INT iImage;
RECT rect; /* bounding rectangle of the item relative to the
* leftmost item (the leftmost item, 0, would have a
* "left" member of 0 in this rectangle)
*
* additionally the top member holds the row number
* and bottom is unused and should be 0 */
BYTE extra[1]; /* Space for caller supplied info, variable size */
} TAB_ITEM;
/* The size of a tab item depends on how much extra data is requested */
#define TAB_ITEM_SIZE(infoPtr) (sizeof(TAB_ITEM) - sizeof(BYTE) + infoPtr->cbInfo)
typedef struct
{
HWND hwnd; /* Tab control window */
HWND hwndNotify; /* notification window (parent) */
UINT uNumItem; /* number of tab items */
UINT uNumRows; /* number of tab rows */
INT tabHeight; /* height of the tab row */
INT tabWidth; /* width of tabs */
INT tabMinWidth; /* minimum width of items */
USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
USHORT uVItemPadding; /* amount of vertical padding, in pixels */
USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
HFONT hFont; /* handle to the current font */
HCURSOR hcurArrow; /* handle to the current cursor */
HIMAGELIST himl; /* handle to an image list (may be 0) */
HWND hwndToolTip; /* handle to tab's tooltip */
INT leftmostVisible; /* Used for scrolling, this member contains
* the index of the first visible item */
INT iSelected; /* the currently selected item */
INT iHotTracked; /* the highlighted item under the mouse */
INT uFocus; /* item which has the focus */
TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
* the size of the control */
BOOL fHeightSet; /* was the height of the tabs explicitly set? */
BOOL bUnicode; /* Unicode control? */
HWND hwndUpDown; /* Updown control used for scrolling */
INT cbInfo; /* Number of bytes of caller supplied info per tab */
} TAB_INFO;
/******************************************************************************
* Positioning constants
*/
#define SELECTED_TAB_OFFSET 2
#define ROUND_CORNER_SIZE 2
#define DISPLAY_AREA_PADDINGX 2
#define DISPLAY_AREA_PADDINGY 2
#define CONTROL_BORDER_SIZEX 2
#define CONTROL_BORDER_SIZEY 2
#define BUTTON_SPACINGX 3
#define BUTTON_SPACINGY 3
#define FLAT_BTN_SPACINGX 8
#define DEFAULT_MIN_TAB_WIDTH 54
#define DEFAULT_TAB_WIDTH_FIXED 96
#define DEFAULT_PADDING_X 6
#define EXTRA_ICON_PADDING 3
#define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
/* Since items are variable sized, cannot directly access them */
#define TAB_GetItem(info,i) \
((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
#define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
/******************************************************************************
* Hot-tracking timer constants
*/
#define TAB_HOTTRACK_TIMER 1
#define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
static const WCHAR themeClass[] = { 'T','a','b',0 };
/******************************************************************************
* Prototypes
*/
static void TAB_InvalidateTabArea(TAB_INFO *);
static void TAB_EnsureSelectionVisible(TAB_INFO *);
static void TAB_DrawItemInterior(TAB_INFO *, HDC, INT, RECT*);
static BOOL
TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
{
NMHDR nmhdr;
nmhdr.hwndFrom = infoPtr->hwnd;
nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
nmhdr.code = code;
return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
(WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
}
static void
TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
MSG msg;
msg.hwnd = hwndMsg;
msg.message = uMsg;
msg.wParam = wParam;
msg.lParam = lParam;
msg.time = GetMessageTime ();
msg.pt.x = (short)LOWORD(GetMessagePos ());
msg.pt.y = (short)HIWORD(GetMessagePos ());
SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
}
static void
TAB_DumpItemExternalT(TCITEMW *pti, UINT iItem, BOOL isW)
{
if (TRACE_ON(tab)) {
TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
}
}
static void
TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
{
if (TRACE_ON(tab)) {
TAB_ITEM *ti;
ti = TAB_GetItem(infoPtr, iItem);
TRACE("tab %d, mask=0x%08x, dwState=0x%08x, pszText=%s, iImage=%d\n",
iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
ti->iImage);
TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
iItem, ti->rect.left, ti->rect.top);
}
}
/* RETURNS
* the index of the selected tab, or -1 if no tab is selected. */
static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
{
return infoPtr->iSelected;
}
/* RETURNS
* the index of the tab item that has the focus
* NOTE
* we have not to return negative value
* TODO
* test for windows */
static inline LRESULT
TAB_GetCurFocus (const TAB_INFO *infoPtr)
{
if (infoPtr->uFocus<0)
{
FIXME("we have not to return negative value\n");
return 0;
}
return infoPtr->uFocus;
}
static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
{
if (infoPtr == NULL) return 0;
return (LRESULT)infoPtr->hwndToolTip;
}
static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
{
INT prevItem = -1;
if (iItem >= 0 && iItem < infoPtr->uNumItem) {
prevItem=infoPtr->iSelected;
if (infoPtr->iSelected != iItem) {
infoPtr->iSelected=iItem;
TAB_EnsureSelectionVisible(infoPtr);
TAB_InvalidateTabArea(infoPtr);
}
}
return prevItem;
}
static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
{
if (iItem < 0 || iItem >= infoPtr->uNumItem) return 0;
if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) {
FIXME("Should set input focus\n");
} else {
int oldFocus = infoPtr->uFocus;
if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
infoPtr->uFocus = iItem;
if (oldFocus != -1) {
if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) {
infoPtr->iSelected = iItem;
TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
}
else
infoPtr->iSelected = iItem;
TAB_EnsureSelectionVisible(infoPtr);
TAB_InvalidateTabArea(infoPtr);
}
}
}
return 0;
}
static inline LRESULT
TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
{
if (infoPtr)
infoPtr->hwndToolTip = hwndToolTip;
return 0;
}
static inline LRESULT
TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
{
if (infoPtr)
{
infoPtr->uHItemPadding_s=LOWORD(lParam);
infoPtr->uVItemPadding_s=HIWORD(lParam);
}
return 0;
}
/******************************************************************************
* TAB_InternalGetItemRect
*
* This method will calculate the rectangle representing a given tab item in
* client coordinates. This method takes scrolling into account.
*
* This method returns TRUE if the item is visible in the window and FALSE
* if it is completely outside the client area.
*/
static BOOL TAB_InternalGetItemRect(
const TAB_INFO* infoPtr,
INT itemIndex,
RECT* itemRect,
RECT* selectedRect)
{
RECT tmpItemRect,clientRect;
LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
/* Perform a sanity check and a trivial visibility check. */
if ( (infoPtr->uNumItem <= 0) ||
(itemIndex >= infoPtr->uNumItem) ||
(!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
{
TRACE("Not Visible\n");
/* need to initialize these to empty rects */
if (itemRect)
{
memset(itemRect,0,sizeof(RECT));
itemRect->bottom = infoPtr->tabHeight;
}
if (selectedRect)
memset(selectedRect,0,sizeof(RECT));
return FALSE;
}
/*
* Avoid special cases in this procedure by assigning the "out"
* parameters if the caller didn't supply them
*/
if (itemRect == NULL)
itemRect = &tmpItemRect;
/* Retrieve the unmodified item rect. */
*itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
/* calculate the times bottom and top based on the row */
GetClientRect(infoPtr->hwnd, &clientRect);
if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
{
itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
itemRect->left = itemRect->right - infoPtr->tabHeight;
}
else if (lStyle & TCS_VERTICAL)
{
itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
itemRect->right = itemRect->left + infoPtr->tabHeight;
}
else if (lStyle & TCS_BOTTOM)
{
itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
itemRect->top = itemRect->bottom - infoPtr->tabHeight;
}
else /* not TCS_BOTTOM and not TCS_VERTICAL */
{
itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
itemRect->bottom = itemRect->top + infoPtr->tabHeight;
}
/*
* "scroll" it to make sure the item at the very left of the
* tab control is the leftmost visible tab.
*/
if(lStyle & TCS_VERTICAL)
{
OffsetRect(itemRect,
0,
-TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
/*
* Move the rectangle so the first item is slightly offset from
* the bottom of the tab control.
*/
OffsetRect(itemRect,
0,
SELECTED_TAB_OFFSET);
} else
{
OffsetRect(itemRect,
-TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
0);
/*
* Move the rectangle so the first item is slightly offset from
* the left of the tab control.
*/
OffsetRect(itemRect,
SELECTED_TAB_OFFSET,
0);
}
TRACE("item %d tab h=%d, rect=(%d,%d)-(%d,%d)\n",
itemIndex, infoPtr->tabHeight,
itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
/* Now, calculate the position of the item as if it were selected. */
if (selectedRect!=NULL)
{
CopyRect(selectedRect, itemRect);
/* The rectangle of a selected item is a bit wider. */
if(lStyle & TCS_VERTICAL)
InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -