📄 trackbar.c
字号:
/*
* Trackbar control
*
* Copyright 1998, 1999 Eric Kohl
* Copyright 1998, 1999 Alex Priem
* Copyright 2002 Dimitrie O. Paun
*
* 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
*
* NOTE
*
* This code was audited for completeness against the documented features
* of Comctl32.dll version 6.0 on Sep. 12, 2002, by Dimitrie O. Paun.
*
* 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.
*
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "uxtheme.h"
#include "tmschema.h"
#include "wine/debug.h"
#include "comctl32.h"
WINE_DEFAULT_DEBUG_CHANNEL(trackbar);
typedef struct
{
HWND hwndSelf;
LONG lRangeMin;
LONG lRangeMax;
LONG lLineSize;
LONG lPageSize;
LONG lSelMin;
LONG lSelMax;
LONG lPos;
UINT uThumbLen;
UINT uNumTics;
UINT uTicFreq;
HWND hwndNotify;
HWND hwndToolTip;
HWND hwndBuddyLA;
HWND hwndBuddyRB;
INT fLocation;
INT flags;
BOOL bUnicode;
BOOL bFocussed;
RECT rcChannel;
RECT rcSelection;
RECT rcThumb;
LPLONG tics;
} TRACKBAR_INFO;
#define TB_REFRESH_TIMER 1
#define TB_REFRESH_DELAY 500
#define TOOLTIP_OFFSET 2 /* distance from ctrl edge to tooltip */
/* Used by TRACKBAR_Refresh to find out which parts of the control
need to be recalculated */
#define TB_THUMBPOSCHANGED 1
#define TB_THUMBSIZECHANGED 2
#define TB_THUMBCHANGED (TB_THUMBPOSCHANGED | TB_THUMBSIZECHANGED)
#define TB_SELECTIONCHANGED 4
#define TB_DRAG_MODE 8 /* we're dragging the slider */
#define TB_AUTO_PAGE_LEFT 16
#define TB_AUTO_PAGE_RIGHT 32
#define TB_AUTO_PAGE (TB_AUTO_PAGE_LEFT | TB_AUTO_PAGE_RIGHT)
#define TB_THUMB_HOT 64 /* mouse hovers above thumb */
/* helper defines for TRACKBAR_DrawTic */
#define TIC_EDGE 0x20
#define TIC_SELECTIONMARKMAX 0x80
#define TIC_SELECTIONMARKMIN 0x100
#define TIC_SELECTIONMARK (TIC_SELECTIONMARKMAX | TIC_SELECTIONMARKMIN)
static const WCHAR themeClass[] = { 'T','r','a','c','k','b','a','r',0 };
static inline int
notify_customdraw(TRACKBAR_INFO *infoPtr, NMCUSTOMDRAW *pnmcd, int stage)
{
pnmcd->dwDrawStage = stage;
return SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
pnmcd->hdr.idFrom, (LPARAM)pnmcd);
}
static LRESULT notify_hdr(TRACKBAR_INFO *infoPtr, INT code, LPNMHDR pnmh)
{
LRESULT result;
TRACE("(code=%d)\n", code);
pnmh->hwndFrom = infoPtr->hwndSelf;
pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
pnmh->code = code;
result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
(WPARAM)pnmh->idFrom, (LPARAM)pnmh);
TRACE(" <= %ld\n", result);
return result;
}
static inline int notify(TRACKBAR_INFO *infoPtr, INT code)
{
NMHDR nmh;
return notify_hdr(infoPtr, code, &nmh);
}
static BOOL
notify_with_scroll (TRACKBAR_INFO *infoPtr, UINT code)
{
BOOL bVert = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT;
TRACE("%x\n", code);
return (BOOL) SendMessageW (infoPtr->hwndNotify,
bVert ? WM_VSCROLL : WM_HSCROLL,
(WPARAM)code, (LPARAM)infoPtr->hwndSelf);
}
static void TRACKBAR_RecalculateTics (TRACKBAR_INFO *infoPtr)
{
int i, tic, nrTics;
if (infoPtr->uTicFreq && infoPtr->lRangeMax >= infoPtr->lRangeMin)
nrTics=(infoPtr->lRangeMax - infoPtr->lRangeMin)/infoPtr->uTicFreq;
else {
nrTics = 0;
Free (infoPtr->tics);
infoPtr->tics = NULL;
infoPtr->uNumTics = 0;
return;
}
if (nrTics != infoPtr->uNumTics) {
infoPtr->tics=ReAlloc (infoPtr->tics,
(nrTics+1)*sizeof (DWORD));
if (!infoPtr->tics) {
infoPtr->uNumTics = 0;
notify(infoPtr, NM_OUTOFMEMORY);
return;
}
infoPtr->uNumTics = nrTics;
}
tic = infoPtr->lRangeMin + infoPtr->uTicFreq;
for (i = 0; i < nrTics; i++, tic += infoPtr->uTicFreq)
infoPtr->tics[i] = tic;
}
/* converts from physical (mouse) position to logical position
(in range of trackbar) */
static inline LONG
TRACKBAR_ConvertPlaceToPosition (TRACKBAR_INFO *infoPtr, int place,
int vertical)
{
double range, width, pos, offsetthumb;
range = infoPtr->lRangeMax - infoPtr->lRangeMin;
if (vertical) {
offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
width = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - (offsetthumb * 2) - 1;
pos = (range*(place - infoPtr->rcChannel.top - offsetthumb)) / width;
} else {
offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - (offsetthumb * 2) - 1;
pos = (range*(place - infoPtr->rcChannel.left - offsetthumb)) / width;
}
pos += infoPtr->lRangeMin;
if (pos > infoPtr->lRangeMax)
pos = infoPtr->lRangeMax;
else if (pos < infoPtr->lRangeMin)
pos = infoPtr->lRangeMin;
TRACE("%.2f\n", pos);
return (LONG)(pos + 0.5);
}
/* return: 0> prev, 0 none, >0 next */
static LONG
TRACKBAR_GetAutoPageDirection (TRACKBAR_INFO *infoPtr, POINT clickPoint)
{
DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
RECT pageRect;
if (dwStyle & TBS_VERT) {
pageRect.top = infoPtr->rcChannel.top;
pageRect.bottom = infoPtr->rcChannel.bottom;
pageRect.left = infoPtr->rcThumb.left;
pageRect.right = infoPtr->rcThumb.right;
} else {
pageRect.top = infoPtr->rcThumb.top;
pageRect.bottom = infoPtr->rcThumb.bottom;
pageRect.left = infoPtr->rcChannel.left;
pageRect.right = infoPtr->rcChannel.right;
}
if (PtInRect(&pageRect, clickPoint))
{
int clickPlace = (dwStyle & TBS_VERT) ? clickPoint.y : clickPoint.x;
LONG clickPos = TRACKBAR_ConvertPlaceToPosition(infoPtr, clickPlace,
dwStyle & TBS_VERT);
return clickPos - infoPtr->lPos;
}
return 0;
}
inline static void
TRACKBAR_PageDown (TRACKBAR_INFO *infoPtr)
{
if (infoPtr->lPos == infoPtr->lRangeMax) return;
infoPtr->lPos += infoPtr->lPageSize;
if (infoPtr->lPos > infoPtr->lRangeMax)
infoPtr->lPos = infoPtr->lRangeMax;
notify_with_scroll (infoPtr, TB_PAGEDOWN);
}
inline static void
TRACKBAR_PageUp (TRACKBAR_INFO *infoPtr)
{
if (infoPtr->lPos == infoPtr->lRangeMin) return;
infoPtr->lPos -= infoPtr->lPageSize;
if (infoPtr->lPos < infoPtr->lRangeMin)
infoPtr->lPos = infoPtr->lRangeMin;
notify_with_scroll (infoPtr, TB_PAGEUP);
}
inline static void TRACKBAR_LineUp(TRACKBAR_INFO *infoPtr)
{
if (infoPtr->lPos == infoPtr->lRangeMin) return;
infoPtr->lPos -= infoPtr->lLineSize;
if (infoPtr->lPos < infoPtr->lRangeMin)
infoPtr->lPos = infoPtr->lRangeMin;
notify_with_scroll (infoPtr, TB_LINEUP);
}
inline static void TRACKBAR_LineDown(TRACKBAR_INFO *infoPtr)
{
if (infoPtr->lPos == infoPtr->lRangeMax) return;
infoPtr->lPos += infoPtr->lLineSize;
if (infoPtr->lPos > infoPtr->lRangeMax)
infoPtr->lPos = infoPtr->lRangeMax;
notify_with_scroll (infoPtr, TB_LINEDOWN);
}
static void
TRACKBAR_CalcChannel (TRACKBAR_INFO *infoPtr)
{
DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
INT cyChannel, offsetthumb, offsetedge;
RECT lpRect, *channel = & infoPtr->rcChannel;
GetClientRect (infoPtr->hwndSelf, &lpRect);
offsetthumb = infoPtr->uThumbLen / 4;
offsetedge = offsetthumb + 3;
cyChannel = (dwStyle & TBS_ENABLESELRANGE) ? offsetthumb*3 : 4;
if (dwStyle & TBS_VERT) {
channel->top = lpRect.top + offsetedge;
channel->bottom = lpRect.bottom - offsetedge;
if (dwStyle & TBS_ENABLESELRANGE)
channel->left = lpRect.left + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
else
channel->left = lpRect.left + (infoPtr->uThumbLen / 2) - 1;
if (dwStyle & TBS_BOTH) {
if (dwStyle & TBS_NOTICKS)
channel->left += 1;
else
channel->left += 9;
}
else if (dwStyle & TBS_TOP) {
if (dwStyle & TBS_NOTICKS)
channel->left += 2;
else
channel->left += 10;
}
channel->right = channel->left + cyChannel;
} else {
channel->left = lpRect.left + offsetedge;
channel->right = lpRect.right - offsetedge;
if (dwStyle & TBS_ENABLESELRANGE)
channel->top = lpRect.top + ((infoPtr->uThumbLen - cyChannel + 2) / 2);
else
channel->top = lpRect.top + (infoPtr->uThumbLen / 2) - 1;
if (dwStyle & TBS_BOTH) {
if (dwStyle & TBS_NOTICKS)
channel->top += 1;
else
channel->top += 9;
}
else if (dwStyle & TBS_TOP) {
if (dwStyle & TBS_NOTICKS)
channel->top += 2;
else
channel->top += 10;
}
channel->bottom = channel->top + cyChannel;
}
}
static void
TRACKBAR_CalcThumb (TRACKBAR_INFO *infoPtr, LONG lPos, RECT *thumb)
{
int range, width, height, thumbwidth;
DWORD dwStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE);
RECT lpRect;
range = infoPtr->lRangeMax - infoPtr->lRangeMin;
thumbwidth = (infoPtr->uThumbLen / 2) | 1;
if (!range) range = 1;
GetClientRect(infoPtr->hwndSelf, &lpRect);
if (dwStyle & TBS_VERT)
{
height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - thumbwidth;
if ((dwStyle & (TBS_BOTH | TBS_LEFT)) && !(dwStyle & TBS_NOTICKS))
thumb->left = 10;
else
thumb->left = 2;
thumb->right = thumb->left + infoPtr->uThumbLen;
thumb->top = infoPtr->rcChannel.top +
(height*(lPos - infoPtr->lRangeMin))/range;
thumb->bottom = thumb->top + thumbwidth;
}
else
{
width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - thumbwidth;
thumb->left = infoPtr->rcChannel.left +
(width*(lPos - infoPtr->lRangeMin))/range;
thumb->right = thumb->left + thumbwidth;
if ((dwStyle & (TBS_BOTH | TBS_TOP)) && !(dwStyle & TBS_NOTICKS))
thumb->top = 10;
else
thumb->top = 2;
thumb->bottom = thumb->top + infoPtr->uThumbLen;
}
}
inline static void
TRACKBAR_UpdateThumb (TRACKBAR_INFO *infoPtr)
{
TRACKBAR_CalcThumb(infoPtr, infoPtr->lPos, &infoPtr->rcThumb);
}
static inline void
TRACKBAR_InvalidateAll(TRACKBAR_INFO * infoPtr)
{
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
}
static void
TRACKBAR_InvalidateThumb (TRACKBAR_INFO *infoPtr, LONG thumbPos)
{
RECT rcThumb;
TRACKBAR_CalcThumb(infoPtr, thumbPos, &rcThumb);
InflateRect(&rcThumb, 1, 1);
InvalidateRect(infoPtr->hwndSelf, &rcThumb, FALSE);
}
static inline void
TRACKBAR_InvalidateThumbMove (TRACKBAR_INFO *infoPtr, LONG oldPos, LONG newPos)
{
TRACKBAR_InvalidateThumb (infoPtr, oldPos);
if (newPos != oldPos)
TRACKBAR_InvalidateThumb (infoPtr, newPos);
}
inline static BOOL
TRACKBAR_HasSelection (TRACKBAR_INFO *infoPtr)
{
return infoPtr->lSelMin != infoPtr->lSelMax;
}
static void
TRACKBAR_CalcSelection (TRACKBAR_INFO *infoPtr)
{
RECT *selection = &infoPtr->rcSelection;
int range = infoPtr->lRangeMax - infoPtr->lRangeMin;
int offsetthumb, height, width;
if (range <= 0) {
SetRectEmpty (selection);
} else {
if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT) {
offsetthumb = (infoPtr->rcThumb.bottom - infoPtr->rcThumb.top)/2;
height = infoPtr->rcChannel.bottom - infoPtr->rcChannel.top - offsetthumb*2;
selection->top = infoPtr->rcChannel.top + offsetthumb +
(height*infoPtr->lSelMin)/range;
selection->bottom = infoPtr->rcChannel.top + offsetthumb +
(height*infoPtr->lSelMax)/range;
selection->left = infoPtr->rcChannel.left + 3;
selection->right = infoPtr->rcChannel.right - 3;
} else {
offsetthumb = (infoPtr->rcThumb.right - infoPtr->rcThumb.left)/2;
width = infoPtr->rcChannel.right - infoPtr->rcChannel.left - offsetthumb*2;
selection->left = infoPtr->rcChannel.left + offsetthumb +
(width*infoPtr->lSelMin)/range;
selection->right = infoPtr->rcChannel.left + offsetthumb +
(width*infoPtr->lSelMax)/range;
selection->top = infoPtr->rcChannel.top + 3;
selection->bottom = infoPtr->rcChannel.bottom - 3;
}
}
TRACE("selection[left=%d, top=%d, right=%d, bottom=%d]\n",
selection->left, selection->top, selection->right, selection->bottom);
}
static BOOL
TRACKBAR_AutoPage (TRACKBAR_INFO *infoPtr, POINT clickPoint)
{
LONG dir = TRACKBAR_GetAutoPageDirection(infoPtr, clickPoint);
LONG prevPos = infoPtr->lPos;
TRACE("x=%d, y=%d, dir=%d\n", clickPoint.x, clickPoint.y, dir);
if (dir > 0 && (infoPtr->flags & TB_AUTO_PAGE_RIGHT))
TRACKBAR_PageDown(infoPtr);
else if (dir < 0 && (infoPtr->flags & TB_AUTO_PAGE_LEFT))
TRACKBAR_PageUp(infoPtr);
else return FALSE;
infoPtr->flags |= TB_THUMBPOSCHANGED;
TRACKBAR_InvalidateThumbMove (infoPtr, prevPos, infoPtr->lPos);
return TRUE;
}
/* Trackbar drawing code. I like my spaghetti done milanese. */
static void
TRACKBAR_DrawChannel (TRACKBAR_INFO *infoPtr, HDC hdc, DWORD dwStyle)
{
RECT rcChannel = infoPtr->rcChannel;
HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
if (theme)
{
DrawThemeBackground (theme, hdc,
(GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TBS_VERT) ?
TKP_TRACKVERT : TKP_TRACK, TKS_NORMAL, &rcChannel, 0);
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -