#include "ctlspriv.h" #include "tracki.h" #define THUMBSLOP 3 #define TICKHEIGHT 4 #define ABS(X) (X >= 0) ? X : -X #define BOUND(x,low,high) max(min(x, high),low) TCHAR szSTrackBarClass[] = TRACKBAR_CLASS; LPARAM FAR CALLBACK TrackBarWndProc(HWND hwnd, WORD message, WPARAM wParam, LPARAM lParam); // // convert a logical scroll-bar position to a physical pixel position // int NEAR PASCAL TBLogToPhys(PTrackBar tb, DWORD dwPos) { if (tb->lLogMax == tb->lLogMin) return tb->rc.left; return (WORD)MulDiv(dwPos - tb->lLogMin, tb->iSizePhys - 1, tb->lLogMax - tb->lLogMin) + tb->rc.left; } LONG NEAR PASCAL TBPhysToLog(PTrackBar tb, int iPos) { if (tb->iSizePhys <= 1) return tb->lLogMin; if (iPos <= tb->rc.left) return tb->lLogMin; if (iPos >= tb->rc.right) return tb->lLogMax; return MulDiv(iPos - tb->rc.left, tb->lLogMax - tb->lLogMin, tb->iSizePhys - 1) + tb->lLogMin; } /* Initialize the trackbar code. */ BOOL FAR PASCAL InitTrackBar(HINSTANCE hInstance) { WNDCLASS wc; if (!GetClassInfo(hInstance, szSTrackBarClass, &wc)) { // See if we must register a window class wc.lpszClassName = szSTrackBarClass; wc.lpfnWndProc = (WNDPROC)TrackBarWndProc; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = NULL; wc.lpszMenuName = NULL; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.hInstance = hInstance; wc.style = CS_DBLCLKS | CS_GLOBALCLASS; wc.cbClsExtra = 0; wc.cbWndExtra = EXTRA_TB_BYTES; if (!RegisterClass(&wc)) return FALSE; } return TRUE; } #if 0 /* SelectColorObjects() */ /* I don't know what this does. */ HBRUSH NEAR PASCAL SelectColorObjects(PTrackBar tb, BOOL fSelect) { static HBRUSH hbrSave; HBRUSH hbr; if (fSelect) hbr = (HBRUSH)SendMessage(GetParent(tb->hwnd), WM_CTLCOLORSCROLLBAR, tb->hdc, tb->hwnd); else hbr = hbrSave; hbrSave = SelectObject(tb->hdc, hbr); return hbr; } #endif void NEAR PASCAL PatRect(HDC hdc,int x,int y,int dx,int dy) { RECT rc; rc.left = x; rc.top = y; rc.right = x + dx; rc.bottom = y + dy; ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); } void NEAR PASCAL DrawTic(PTrackBar tb, int x, int yTic) { SetBkColor(tb->hdc, GetSysColor(COLOR_BTNTEXT)); PatRect(tb->hdc,(x),yTic,1,TICKHEIGHT); SetBkColor(tb->hdc, GetSysColor(COLOR_BTNHIGHLIGHT)); PatRect(tb->hdc,(x)+1,yTic,1,TICKHEIGHT); } /* DrawTics() */ /* There is always a tick at the beginning and end of the bar, but you can */ /* add some more of your own with a TBM_SETTIC message. This draws them. */ /* They are kept in an array whose handle is a window word. The first */ /* element is the number of extra ticks, and then the positions. */ void NEAR PASCAL DrawTics(PTrackBar tb) { PDWORD pTics; int iPos; int yTic; int i; yTic = tb->rc.bottom + THUMBSLOP + 1; DrawTic(tb, tb->rc.left, yTic); // first DrawTic(tb, tb->rc.right-1, yTic); // last // those inbetween pTics = tb->pTics; if (pTics) for (i = 0; i < tb->nTics; ++i) { iPos = TBLogToPhys(tb,pTics[i]); DrawTic(tb, iPos, yTic); } // draw the selection range (triangles) if ((tb->Flags & TBF_SELECTION) && (tb->lSelStart <= tb->lSelEnd) && (tb->lSelEnd >= tb->lLogMin)) { SetBkColor(tb->hdc, GetSysColor(COLOR_BTNTEXT)); iPos = TBLogToPhys(tb,tb->lSelStart); for (i = 0; i < TICKHEIGHT; i++) PatRect(tb->hdc, iPos - i, yTic + i, 1, TICKHEIGHT - i); iPos = TBLogToPhys(tb,tb->lSelEnd); for (i = 0; i < TICKHEIGHT; i++) PatRect(tb->hdc, iPos + i, yTic + i, 1, TICKHEIGHT - i); } // line across the bottom SetBkColor(tb->hdc, GetSysColor(COLOR_BTNTEXT)); PatRect(tb->hdc, tb->rc.left, yTic + TICKHEIGHT, tb->iSizePhys, 1); SetBkColor(tb->hdc, GetSysColor(COLOR_BTNHIGHLIGHT)); PatRect(tb->hdc, tb->rc.left, yTic + TICKHEIGHT + 1, tb->iSizePhys, 1); } /* This draws the track bar itself */ void NEAR PASCAL DrawChannel(PTrackBar tb) { HBRUSH hbrTemp; // draw the frame around the window SetBkColor(tb->hdc, GetSysColor(COLOR_WINDOWFRAME)); PatRect(tb->hdc, tb->rc.left, tb->rc.top, tb->iSizePhys, 1); PatRect(tb->hdc, tb->rc.left, tb->rc.bottom-2, tb->iSizePhys, 1); PatRect(tb->hdc, tb->rc.left, tb->rc.top, 1, tb->rc.bottom-tb->rc.top-1); PatRect(tb->hdc, tb->rc.right-1, tb->rc.top, 1, tb->rc.bottom-tb->rc.top-1); SetBkColor(tb->hdc, GetSysColor(COLOR_BTNHIGHLIGHT)); PatRect(tb->hdc, tb->rc.left, tb->rc.bottom-1, tb->iSizePhys, 1); SetBkColor(tb->hdc, GetSysColor(COLOR_BTNSHADOW)); PatRect(tb->hdc, tb->rc.left+1, tb->rc.top + 1, tb->iSizePhys-2,1); // draw the bacground in dither gray hbrTemp = SelectObject(tb->hdc, hbrDither); if (hbrTemp) { PatBlt(tb->hdc, tb->rc.left+1, tb->rc.top + 2, tb->iSizePhys-2, tb->rc.bottom-tb->rc.top-4, PATCOPY); SelectObject(tb->hdc, hbrTemp); } // now highlight the selection range if ((tb->Flags & TBF_SELECTION) && (tb->lSelStart <= tb->lSelEnd) && (tb->lSelEnd > tb->lLogMin)) { int iStart, iEnd; iStart = TBLogToPhys(tb,tb->lSelStart); iEnd = TBLogToPhys(tb,tb->lSelEnd); SetBkColor(tb->hdc, GetSysColor(COLOR_BTNTEXT)); PatRect(tb->hdc, iStart,tb->rc.top+1,1,tb->rc.bottom-tb->rc.top-2); PatRect(tb->hdc, iEnd, tb->rc.top+1,1,tb->rc.bottom-tb->rc.top-2); if (iStart + 2 <= iEnd) { SetBkColor(tb->hdc, GetSysColor(COLOR_BTNHIGHLIGHT)); PatRect(tb->hdc, iStart+1, tb->rc.top + 1, iEnd - iStart - 1, tb->rc.bottom-tb->rc.top - 3); } } } void NEAR PASCAL MoveThumb(PTrackBar tb, LONG lPos) { InvalidateRect(tb->hwnd, &tb->Thumb, TRUE); tb->lLogPos = BOUND(lPos,tb->lLogMin,tb->lLogMax); tb->Thumb.left = TBLogToPhys(tb, tb->lLogPos) - tb->wThumbWidth/2; tb->Thumb.right = tb->Thumb.left + tb->wThumbWidth; tb->Thumb.top = tb->rc.top - THUMBSLOP; tb->Thumb.bottom = tb->rc.bottom + THUMBSLOP; InvalidateRect(tb->hwnd, &tb->Thumb, TRUE); UpdateWindow(tb->hwnd); } void NEAR PASCAL DrawThumb(PTrackBar tb) { HBITMAP hbmT; HDC hdcT; int x; hdcT = CreateCompatibleDC(tb->hdc); if ((tb->Cmd == TB_THUMBTRACK) || !IsWindowEnabled(tb->hwnd)) x = tb->wThumbWidth; else x = 0; hbmT = SelectObject(hdcT, hbmThumb); if (hbmT) { BitBlt(tb->hdc,tb->Thumb.left, tb->rc.top-THUMBSLOP, tb->wThumbWidth, tb->wThumbHeight, hdcT, x, 0, SRCCOPY); } SelectObject(hdcT, hbmT); DeleteDC(hdcT); } /* SetTBCaretPos() */ /* Make the caret flash in the middle of the thumb when it has the focus */ void NEAR PASCAL SetTBCaretPos(PTrackBar tb) { // We only get the caret if we have the focus. if (tb->hwnd == GetFocus()) SetCaretPos(tb->Thumb.left + 3, tb->Thumb.top + 3); } LPARAM FAR CALLBACK TrackBarWndProc(HWND hwnd, WORD message, WPARAM wParam, LPARAM lParam) { PTrackBar tb; PAINTSTRUCT ps; BITMAP bm; HANDLE h; tb = TrackBarLock(hwnd); switch (message) { case WM_CREATE: if (!CreateDitherBrush(FALSE)) return -1; // Get us our window structure. TrackBarCreate(hwnd); tb = TrackBarLock(hwnd); tb->hwnd = hwnd; tb->Cmd = (WORD)-1; /* load the 2 thumb bitmaps (pressed and released) */ CreateThumb(FALSE); GetObject(hbmThumb, sizeof(bm), &bm); tb->wThumbWidth = (WORD)(bm.bmWidth/2); tb->wThumbHeight = (WORD)bm.bmHeight; // tb->lLogMax = 100; // make tracking work by default // fall through to WM_SIZE case WM_SIZE: GetClientRect(hwnd, &tb->rc); tb->rc.bottom = tb->rc.top + tb->wThumbHeight - THUMBSLOP; tb->rc.top += THUMBSLOP; tb->rc.left += tb->wThumbWidth/2; tb->rc.right -= tb->wThumbWidth/2; // Figure out how much room we have to move the thumb in //!!! -2 tb->iSizePhys = tb->rc.right - tb->rc.left; // Elevator isn't there if there's no room. if (tb->iSizePhys == 0) { // Lost our thumb. tb->Flags |= TBF_NOTHUMB; tb->iSizePhys = 1; } else { // Ah. We have a thumb. tb->Flags &= ~TBF_NOTHUMB; } InvalidateRect(hwnd, NULL, TRUE); MoveThumb(tb, tb->lLogPos); break; case WM_DESTROY: TrackBarDestroy(hwnd); FreeDitherBrush(); DestroyThumb(); break; case WM_SETFOCUS: // We gots the focus. We need a caret. CreateCaret(hwnd, (HBITMAP)1, 3, 13); SetTBCaretPos(tb); ShowCaret(hwnd); break; case WM_KILLFOCUS: DestroyCaret(); break; case WM_ERASEBKGND: break; case WM_ENABLE: InvalidateRect(hwnd, NULL, FALSE); break; case WM_PAINT: if (wParam == 0L) tb->hdc = BeginPaint(hwnd, &ps); else tb->hdc = (HDC)wParam; // Update the dither brush if necessary. CheckSysColors(); DrawTics(tb); DrawThumb(tb); ExcludeClipRect(tb->hdc, tb->Thumb.left, tb->Thumb.top, tb->Thumb.right, tb->Thumb.bottom); DrawChannel(tb); SetTBCaretPos(tb); if (wParam == 0L) EndPaint(hwnd, &ps); tb->hdc = NULL; break; case WM_GETDLGCODE: return DLGC_WANTARROWS; break; case WM_LBUTTONDOWN: /* Give ourselves focus */ SetFocus(hwnd); TBTrackInit(tb, lParam); break; case WM_LBUTTONUP: // We're through doing whatever we were doing with the // button down. TBTrackEnd(tb, lParam); break; case WM_TIMER: // The only way we get a timer message is if we're // autotracking. lParam = GetMessagePos(); ScreenToClient(tb->hwnd, (LPPOINT)&lParam); // fall through to WM_MOUSEMOVE case WM_MOUSEMOVE: // We only care that the mouse is moving if we're // tracking the bloody thing. if (tb->Cmd != (WORD)-1) TBTrack(tb, lParam); return 0L; case WM_KEYUP: // If key was any of the keyboard accelerators, send end // track message when user up clicks on keyboard switch (wParam) { case VK_HOME: case VK_END: case VK_PRIOR: case VK_NEXT: case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: DoTrack(tb, TB_ENDTRACK, 0); break; default: break; } break; case WM_KEYDOWN: switch (wParam) { case VK_HOME: wParam = TB_TOP; goto KeyTrack; case VK_END: wParam = TB_BOTTOM; goto KeyTrack; case VK_PRIOR: wParam = TB_PAGEUP; goto KeyTrack; case VK_NEXT: wParam = TB_PAGEDOWN; goto KeyTrack; case VK_LEFT: case VK_UP: wParam = TB_LINEUP; goto KeyTrack; case VK_RIGHT: case VK_DOWN: wParam = TB_LINEDOWN; KeyTrack: DoTrack(tb, wParam, 0); break; default: break; } break; case TBM_GETPOS: return tb->lLogPos; case TBM_GETSELSTART: return tb->lSelStart; case TBM_GETSELEND: return tb->lSelEnd; case TBM_GETRANGEMIN: return tb->lLogMin; case TBM_GETRANGEMAX: return tb->lLogMax; case TBM_GETPTICS: return (LONG)(LPVOID)tb->pTics; case TBM_CLEARSEL: tb->Flags &= ~TBF_SELECTION; tb->lSelStart = -1; tb->lSelEnd = -1; goto RedrawTB; case TBM_CLEARTICS: if (tb->pTics) LocalFree((HLOCAL)tb->pTics); tb->nTics = 0; tb->pTics = NULL; goto RedrawTB; case TBM_GETTIC: if (tb->pTics == NULL || (int)wParam >= tb->nTics) return -1L; return tb->pTics[wParam]; case TBM_GETTICPOS: if (tb->pTics == NULL || (int)wParam >= tb->nTics) return -1L; return TBLogToPhys(tb,tb->pTics[wParam]); case TBM_GETNUMTICS: return tb->nTics; case TBM_SETTIC: /* not a valid position */ if (lParam < 0) break; if (tb->pTics) h = LocalReAlloc((HLOCAL)tb->pTics, sizeof(DWORD) * (WORD)(tb->nTics + 1), LMEM_MOVEABLE | LMEM_ZEROINIT); else h = LocalAlloc(LPTR, sizeof(DWORD)); if (h) tb->pTics = (PDWORD)h; else return (LONG)FALSE; tb->pTics[tb->nTics++] = (DWORD)lParam; InvalidateRect(hwnd, NULL, TRUE); return (LONG)TRUE; break; case TBM_SETPOS: /* Only redraw if it will physically move */ if (wParam && TBLogToPhys(tb, lParam) != TBLogToPhys(tb, tb->lLogPos)) MoveThumb(tb, lParam); else tb->lLogPos = BOUND(lParam,tb->lLogMin,tb->lLogMax); break; case TBM_SETSEL: tb->Flags |= TBF_SELECTION; tb->lSelStart = LOWORD(lParam); tb->lSelEnd = HIWORD(lParam); if (tb->lSelEnd < tb->lSelStart) tb->lSelEnd = tb->lSelStart; goto RedrawTB; case TBM_SETSELSTART: tb->Flags |= TBF_SELECTION; tb->lSelStart = lParam; if (tb->lSelEnd < tb->lSelStart || tb->lSelEnd == -1) tb->lSelEnd = tb->lSelStart; goto RedrawTB; case TBM_SETSELEND: tb->Flags |= TBF_SELECTION; tb->lSelEnd = lParam; if (tb->lSelStart > tb->lSelEnd || tb->lSelStart == -1) tb->lSelStart = tb->lSelEnd; goto RedrawTB; case TBM_SETRANGE: tb->lLogMin = LOWORD(lParam); tb->lLogMax = HIWORD(lParam); goto RedrawTB; case TBM_SETRANGEMIN: tb->lLogMin = (DWORD)lParam; goto RedrawTB; case TBM_SETRANGEMAX: tb->lLogMax = (DWORD)lParam; RedrawTB: tb->lLogPos = BOUND(tb->lLogPos, tb->lLogMin,tb->lLogMax); /* Only redraw if flag says so */ if (wParam) { InvalidateRect(hwnd, NULL, TRUE); MoveThumb(tb, tb->lLogPos); } break; } return DefWindowProc(hwnd, message, wParam, lParam); } /* DoTrack() */ void NEAR PASCAL DoTrack(PTrackBar tb, int cmd, DWORD dwPos) { // note: we only send back a WORD worth of the position. SendMessage(GetParent(tb->hwnd), WM_HSCROLL, (WPARAM)MAKELONG(cmd,LOWORD(dwPos)), (LPARAM)tb->hwnd); } /* WTrackType() */ WORD NEAR PASCAL WTrackType(PTrackBar tb, LONG lParam) { POINT pt; LONG2POINT( lParam, pt ); if (tb->Flags & TBF_NOTHUMB) // If no thumb, just leave. return 0; if (PtInRect(&tb->Thumb, pt)) return TB_THUMBTRACK; if (!PtInRect(&tb->rc, pt)) return 0; if (pt.x >= tb->Thumb.left) return TB_PAGEDOWN; else return TB_PAGEUP; } /* TBTrackInit() */ void NEAR PASCAL TBTrackInit(PTrackBar tb, LONG lParam) { WORD wCmd; if (tb->Flags & TBF_NOTHUMB) // No thumb: just leave. return; wCmd = WTrackType(tb, lParam); if (!wCmd) return; HideCaret(tb->hwnd); SetCapture(tb->hwnd); tb->Cmd = wCmd; tb->dwDragPos = (DWORD)-1; // Set up for auto-track (if needed). if (wCmd != TB_THUMBTRACK) { // Set our timer up tb->Timer = (UINT)SetTimer(tb->hwnd, TIMER_ID, REPEATTIME, NULL); } TBTrack(tb, lParam); } /* EndTrack() */ void near PASCAL TBTrackEnd(PTrackBar tb, long lParam) { if (GetCapture() != tb->hwnd) return; // Let the mouse go. ReleaseCapture(); // Decide how we're ending this thing. if (tb->Cmd == TB_THUMBTRACK) DoTrack(tb, TB_THUMBPOSITION, tb->dwDragPos); if (tb->Timer) KillTimer(tb->hwnd, TIMER_ID); tb->Timer = 0; // Always send TB_ENDTRACK message. DoTrack(tb, TB_ENDTRACK, 0); // Give the caret back. ShowCaret(tb->hwnd); // Nothing going on. tb->Cmd = (WORD)-1; MoveThumb(tb, tb->lLogPos); } void NEAR PASCAL TBTrack(PTrackBar tb, LONG lParam) { DWORD dwPos; // See if we're tracking the thumb if (tb->Cmd == TB_THUMBTRACK) { dwPos = TBPhysToLog(tb, LOWORD(lParam)); // Tentative position changed -- notify the guy. if (dwPos != tb->dwDragPos) { tb->dwDragPos = dwPos; MoveThumb(tb, dwPos); DoTrack(tb, TB_THUMBTRACK, dwPos); } } else { if (tb->Cmd != WTrackType(tb, lParam)) return; DoTrack(tb, tb->Cmd, 0); } } // what is this for? #if 0 BYTE ThumbBitmap[] = { 0x28,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01,0x00,0x04,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xbf,0x00, 0x00,0xbf,0x00,0x00,0x00,0xbf,0xbf,0x00,0xbf,0x00,0x00,0x00,0xbf,0x00,0xbf,0x00, 0xbf,0xbf,0x00,0x00,0xc0,0xc0,0xc0,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0xff,0x00, 0x00,0xff,0x00,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0xff,0x00, 0xff,0xff,0x00,0x00,0xff,0xff,0xff,0x00,0xbb,0xbb,0x0b,0xbb,0xbb,0xbb,0xb0,0xbb, 0xbb,0x00,0x00,0x00,0xbb,0xb0,0x80,0xbb,0xbb,0xbb,0x08,0x0b,0xbb,0x00,0x00,0x00, 0xbb,0x08,0xf8,0x0b,0xbb,0xb0,0x87,0x70,0xbb,0x00,0x00,0x00,0xb0,0x8f,0xf8,0x80, 0xbb,0x08,0x77,0x77,0x0b,0x00,0x00,0x00,0x08,0xf8,0x88,0x88,0x00,0x88,0x88,0x87, 0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00, 0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88, 0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87, 0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00, 0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88, 0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87, 0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00, 0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88, 0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87, 0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x78,0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00, 0x0f,0xff,0xff,0xff,0x00,0x88,0x88,0x88,0x80,0x00,0x00,0x00,0xb0,0x00,0x00,0x00, 0xbb,0x00,0x00,0x00,0x0b,0x00,0x00,0x00 }; #endif