Commit 047513f3 authored by Alexandre Julliard's avatar Alexandre Julliard

Don Kelly

In Windows Property Sheet can have any mix of icon-less tabs and tabs with icons. Adds a check to see if the icon we're adding is non-NULL (otherwise random junk from memory can be rendered) when the application has specified the PSP_USEICONID flag is set. Changes to the Tab control to only render icons for tabs that have the TCIF_IMAGE flag set (previously, if the flag was set the entire image list of icons was rendered). Stephane Lussier <stephane@macadamian.com> Fixes for some tab control bugs Henning Hoffmann Fixed some width problem with OWNERDRAW tab. Luc Tourangeau <luc@macadamian.com> TCM_ADJUSTRECT is now returning consistant compare to Windows. Serge Ivanov <sergei@corel.ca> Fixed problem with tab selection. When you select tab it becames first visible tab. Now leftmost visible tab is calculated properly. - Added code for correct handling of updown control. - Forced recalculation of tabs' coordinates when: a) all items are deleted, b) window style is canged
parent 2d12700f
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "tab.h" #include "tab.h"
#include "debugtools.h" #include "debugtools.h"
#include "cache.h" #include "cache.h"
#include "win.h"
DEFAULT_DEBUG_CHANNEL(tab) DEFAULT_DEBUG_CHANNEL(tab)
...@@ -30,8 +31,8 @@ DEFAULT_DEBUG_CHANNEL(tab) ...@@ -30,8 +31,8 @@ DEFAULT_DEBUG_CHANNEL(tab)
#define ROUND_CORNER_SIZE 2 #define ROUND_CORNER_SIZE 2
#define FOCUS_RECT_HOFFSET 2 #define FOCUS_RECT_HOFFSET 2
#define FOCUS_RECT_VOFFSET 1 #define FOCUS_RECT_VOFFSET 1
#define DISPLAY_AREA_PADDINGX 5 #define DISPLAY_AREA_PADDINGX 2
#define DISPLAY_AREA_PADDINGY 5 #define DISPLAY_AREA_PADDINGY 2
#define CONTROL_BORDER_SIZEX 2 #define CONTROL_BORDER_SIZEX 2
#define CONTROL_BORDER_SIZEY 2 #define CONTROL_BORDER_SIZEY 2
#define BUTTON_SPACINGX 10 #define BUTTON_SPACINGX 10
...@@ -519,26 +520,19 @@ static LRESULT TAB_OnHScroll( ...@@ -519,26 +520,19 @@ static LRESULT TAB_OnHScroll(
{ {
TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
if (nScrollCode == SB_LINELEFT) if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
{ {
if (infoPtr->leftmostVisible>0) if(nPos < infoPtr->leftmostVisible)
{ infoPtr->leftmostVisible--;
infoPtr->leftmostVisible--; else
infoPtr->leftmostVisible++;
TAB_InvalidateTabArea(hwnd, infoPtr); TAB_InvalidateTabArea(hwnd, infoPtr);
} SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
} MAKELONG(infoPtr->leftmostVisible, 0));
else if (nScrollCode == SB_LINERIGHT) }
{
if (infoPtr->leftmostVisible< (infoPtr->uNumItem-1))
{
infoPtr->leftmostVisible++;
TAB_InvalidateTabArea(hwnd, infoPtr); return 0;
}
}
return 0;
} }
/****************************************************************************** /******************************************************************************
...@@ -552,9 +546,11 @@ static void TAB_SetupScrolling( ...@@ -552,9 +546,11 @@ static void TAB_SetupScrolling(
TAB_INFO* infoPtr, TAB_INFO* infoPtr,
const RECT* clientRect) const RECT* clientRect)
{ {
INT maxRange = 0;
if (infoPtr->needsScrolling) if (infoPtr->needsScrolling)
{ {
RECT controlPos; RECT controlPos;
INT vsize, tabwidth;
/* /*
* Calculate the position of the scroll control. * Calculate the position of the scroll control.
...@@ -579,13 +575,9 @@ static void TAB_SetupScrolling( ...@@ -579,13 +575,9 @@ static void TAB_SetupScrolling(
*/ */
if (infoPtr->hwndUpDown==0) if (infoPtr->hwndUpDown==0)
{ {
/* infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
* I use a scrollbar since it seems to be more stable than the Updown
* control.
*/
infoPtr->hwndUpDown = CreateWindowA("ScrollBar",
"", "",
WS_VISIBLE | WS_CHILD | WS_OVERLAPPED | SBS_HORZ, WS_VISIBLE | WS_CHILD | UDS_HORZ,
controlPos.left, controlPos.top, controlPos.left, controlPos.top,
controlPos.right - controlPos.left, controlPos.right - controlPos.left,
controlPos.bottom - controlPos.top, controlPos.bottom - controlPos.top,
...@@ -603,6 +595,26 @@ static void TAB_SetupScrolling( ...@@ -603,6 +595,26 @@ static void TAB_SetupScrolling(
controlPos.bottom - controlPos.top, controlPos.bottom - controlPos.top,
SWP_SHOWWINDOW | SWP_NOZORDER); SWP_SHOWWINDOW | SWP_NOZORDER);
} }
/* Now calculate upper limit of the updown control range.
* We do this by calculating how many tabs will be offscreen when the
* last tab is visible.
*/
if(infoPtr->uNumItem)
{
vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
maxRange = infoPtr->uNumItem;
tabwidth = infoPtr->items[maxRange-1].rect.right;
for(; maxRange > 0; maxRange--)
{
if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
break;
}
if(maxRange == infoPtr->uNumItem)
maxRange--;
}
} }
else else
{ {
...@@ -614,6 +626,8 @@ static void TAB_SetupScrolling( ...@@ -614,6 +626,8 @@ static void TAB_SetupScrolling(
ShowWindow(infoPtr->hwndUpDown, SW_HIDE); ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
} }
} }
if (infoPtr->hwndUpDown)
SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
} }
/****************************************************************************** /******************************************************************************
...@@ -658,7 +672,7 @@ static void TAB_SetItemBounds (HWND hwnd) ...@@ -658,7 +672,7 @@ static void TAB_SetItemBounds (HWND hwnd)
*/ */
curItemLeftPos = 0; curItemLeftPos = 0;
if (!((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))) if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
{ {
int item_height; int item_height;
int icon_height = 0; int icon_height = 0;
...@@ -715,7 +729,7 @@ static void TAB_SetItemBounds (HWND hwnd) ...@@ -715,7 +729,7 @@ static void TAB_SetItemBounds (HWND hwnd)
*/ */
infoPtr->items[curItem].rect.left = curItemLeftPos; infoPtr->items[curItem].rect.left = curItemLeftPos;
if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED)) if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
{ {
infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left + infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
infoPtr->tabWidth + infoPtr->tabWidth +
...@@ -830,7 +844,7 @@ static void TAB_DrawItem( ...@@ -830,7 +844,7 @@ static void TAB_DrawItem(
/* /*
* Background color. * Background color.
*/ */
if (!(lStyle & TCS_OWNERDRAWFIXED)) if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
{ {
COLORREF bk = GetSysColor(COLOR_3DHILIGHT); COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
DeleteObject(hbr); DeleteObject(hbr);
...@@ -968,11 +982,52 @@ static void TAB_DrawItem( ...@@ -968,11 +982,52 @@ static void TAB_DrawItem(
InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING); InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
/* /*
* if owner draw, tell the owner to draw
*/
if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
{
DRAWITEMSTRUCT dis;
WND *pwndPtr;
UINT id;
/*
* get the control id
*/
pwndPtr = WIN_FindWndPtr( hwnd );
id = pwndPtr->wIDmenu;
WIN_ReleaseWndPtr(pwndPtr);
/*
* put together the DRAWITEMSTRUCT
*/
dis.CtlType = ODT_TAB;
dis.CtlID = id;
dis.itemID = iItem;
dis.itemAction = ODA_DRAWENTIRE;
if ( iItem == infoPtr->iSelected )
dis.itemState = ODS_SELECTED;
else
dis.itemState = 0;
dis.hwndItem = hwnd; /* */
dis.hDC = hdc;
dis.rcItem = r; /* */
dis.itemData = infoPtr->items[iItem].lParam;
/*
* send the draw message
*/
SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
}
else
{
/*
* If not owner draw, then do the drawing ourselves.
*
* Draw the icon. * Draw the icon.
*/ */
if (infoPtr->himl) if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE) )
{ {
ImageList_Draw (infoPtr->himl, iItem, hdc, ImageList_Draw (infoPtr->himl, infoPtr->items[iItem].iImage, hdc,
r.left, r.top+1, ILD_NORMAL); r.left, r.top+1, ILD_NORMAL);
ImageList_GetIconSize (infoPtr->himl, &cx, &cy); ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
r.left+=(cx + HORIZONTAL_ITEM_PADDING); r.left+=(cx + HORIZONTAL_ITEM_PADDING);
...@@ -986,6 +1041,7 @@ static void TAB_DrawItem( ...@@ -986,6 +1041,7 @@ static void TAB_DrawItem(
lstrlenA(infoPtr->items[iItem].pszText), lstrlenA(infoPtr->items[iItem].pszText),
&r, &r,
DT_LEFT|DT_SINGLELINE|DT_VCENTER); DT_LEFT|DT_SINGLELINE|DT_VCENTER);
}
/* /*
* Draw the focus rectangle * Draw the focus rectangle
...@@ -1163,10 +1219,7 @@ static void TAB_EnsureSelectionVisible( ...@@ -1163,10 +1219,7 @@ static void TAB_EnsureSelectionVisible(
HWND hwnd, HWND hwnd,
TAB_INFO* infoPtr) TAB_INFO* infoPtr)
{ {
RECT selectedRect; INT iSelected = infoPtr->iSelected;
RECT visibleRect;
RECT scrollerRect;
BOOL isVisible;
/* /*
* Do the trivial cases first. * Do the trivial cases first.
...@@ -1175,76 +1228,45 @@ static void TAB_EnsureSelectionVisible( ...@@ -1175,76 +1228,45 @@ static void TAB_EnsureSelectionVisible(
(infoPtr->hwndUpDown==0) ) (infoPtr->hwndUpDown==0) )
return; return;
if (infoPtr->leftmostVisible > infoPtr->iSelected) if (infoPtr->leftmostVisible >= iSelected)
{ {
infoPtr->leftmostVisible = infoPtr->iSelected; infoPtr->leftmostVisible = iSelected;
return;
} }
else
/*
* Calculate the part of the client area that is visible.
*/
GetClientRect(hwnd, &visibleRect);
GetClientRect(infoPtr->hwndUpDown, &scrollerRect);
visibleRect.right -= scrollerRect.right;
/*
* Get the rectangle for the item
*/
isVisible = TAB_InternalGetItemRect(hwnd,
infoPtr,
infoPtr->iSelected,
NULL,
&selectedRect);
/*
* If this function can't say it's completely invisible, maybe it
* is partially visible. Let's check.
*/
if (isVisible)
{ {
POINT pt1; RECT r;
POINT pt2; INT width, i;
/*
pt1.x = selectedRect.left; * Calculate the part of the client area that is visible.
pt1.y = selectedRect.top; */
pt2.x = selectedRect.right - 1; GetClientRect(hwnd, &r);
pt2.y = selectedRect.bottom - 1; width = r.right;
isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2); GetClientRect(infoPtr->hwndUpDown, &r);
width -= r.right;
if ((infoPtr->items[iSelected].rect.right -
infoPtr->items[iSelected].rect.left) >= width )
{
/* Special case: width of selected item is greater than visible
* part of control.
*/
infoPtr->leftmostVisible = iSelected;
}
else
{
for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
{
if ((infoPtr->items[iSelected].rect.right -
infoPtr->items[i].rect.left) < width)
break;
}
infoPtr->leftmostVisible = i;
}
} }
while ( (infoPtr->leftmostVisible < infoPtr->iSelected) && SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
!isVisible) MAKELONG(infoPtr->leftmostVisible, 0));
{
infoPtr->leftmostVisible++;
/*
* Get the rectangle for the item
*/
isVisible = TAB_InternalGetItemRect(hwnd,
infoPtr,
infoPtr->iSelected,
NULL,
&selectedRect);
/*
* If this function can't say it's completely invisible, maybe it
* is partially visible. Let's check.
*/
if (isVisible)
{
POINT pt1;
POINT pt2;
pt1.x = selectedRect.left;
pt1.y = selectedRect.top;
pt2.x = selectedRect.right - 1;
pt2.y = selectedRect.bottom - 1;
isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
}
}
} }
/****************************************************************************** /******************************************************************************
...@@ -1264,7 +1286,7 @@ static void TAB_InvalidateTabArea( ...@@ -1264,7 +1286,7 @@ static void TAB_InvalidateTabArea(
if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
{ {
clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 1); clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 3);
} }
else else
{ {
...@@ -1374,6 +1396,7 @@ TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam) ...@@ -1374,6 +1396,7 @@ TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
infoPtr->tabWidth = (INT)LOWORD(lParam); infoPtr->tabWidth = (INT)LOWORD(lParam);
infoPtr->tabHeight = (INT)HIWORD(lParam); infoPtr->tabHeight = (INT)HIWORD(lParam);
} }
infoPtr->fSizeSet = TRUE;
return lResult; return lResult;
} }
...@@ -1518,7 +1541,9 @@ TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam) ...@@ -1518,7 +1541,9 @@ TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
COMCTL32_Free (infoPtr->items); COMCTL32_Free (infoPtr->items);
infoPtr->uNumItem = 0; infoPtr->uNumItem = 0;
infoPtr->iSelected = -1; infoPtr->iSelected = -1;
TAB_SetItemBounds(hwnd);
TAB_InvalidateTabArea(hwnd,infoPtr);
return TRUE; return TRUE;
} }
...@@ -1621,6 +1646,7 @@ TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) ...@@ -1621,6 +1646,7 @@ TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
TEXTMETRICA fontMetrics; TEXTMETRICA fontMetrics;
HDC hdc; HDC hdc;
HFONT hOldFont; HFONT hOldFont;
DWORD dwStyle;
infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO)); infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
...@@ -1637,9 +1663,17 @@ TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) ...@@ -1637,9 +1663,17 @@ TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
infoPtr->needsScrolling = FALSE; infoPtr->needsScrolling = FALSE;
infoPtr->hwndUpDown = 0; infoPtr->hwndUpDown = 0;
infoPtr->leftmostVisible = 0; infoPtr->leftmostVisible = 0;
infoPtr->fSizeSet = FALSE;
TRACE("Created tab control, hwnd [%04x]\n", hwnd); TRACE("Created tab control, hwnd [%04x]\n", hwnd);
if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_TOOLTIPS) {
/* The tab control always has the WS_CLIPSIBLINGS style. Even
if you don't specify in CreateWindow. This is necesary in
order for paint to work correctly. This follows windows behaviour. */
dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
if (dwStyle & TCS_TOOLTIPS) {
/* Create tooltip control */ /* Create tooltip control */
infoPtr->hwndToolTip = infoPtr->hwndToolTip =
CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0, CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
...@@ -1697,6 +1731,9 @@ TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam) ...@@ -1697,6 +1731,9 @@ TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
INT iItem; INT iItem;
if (!infoPtr)
return 0;
if (infoPtr->items) { if (infoPtr->items) {
for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) { for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
if (infoPtr->items[iItem].pszText) if (infoPtr->items[iItem].pszText)
...@@ -1814,8 +1851,8 @@ TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ...@@ -1814,8 +1851,8 @@ TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
case TCM_SETCURFOCUS: case TCM_SETCURFOCUS:
return TAB_SetCurFocus (hwnd, wParam); return TAB_SetCurFocus (hwnd, wParam);
case TCM_SETMINTTABWIDTH: case TCM_SETMINTABWIDTH:
FIXME("Unimplemented msg TCM_SETMINTTABWIDTH\n"); FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
return 0; return 0;
case TCM_DESELECTALL: case TCM_DESELECTALL:
...@@ -1871,6 +1908,11 @@ TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ...@@ -1871,6 +1908,11 @@ TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
case WM_HSCROLL: case WM_HSCROLL:
return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam); return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
case WM_STYLECHANGED:
TAB_SetItemBounds (hwnd);
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_KILLFOCUS: case WM_KILLFOCUS:
case WM_SETFOCUS: case WM_SETFOCUS:
......
...@@ -41,6 +41,7 @@ typedef struct tagTAB_INFO ...@@ -41,6 +41,7 @@ typedef struct tagTAB_INFO
BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/ BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
BOOL needsScrolling; /* TRUE if the size of the tabs is greater than BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
* the size of the control */ * the size of the control */
BOOL fSizeSet; /* was the size of the tabs explicitly set? */
HWND hwndUpDown; /* Updown control used for scrolling */ HWND hwndUpDown; /* Updown control used for scrolling */
} TAB_INFO; } TAB_INFO;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment