Sign in to follow this  
dev578

Scroll Bar Problem

Recommended Posts

I am using C++ with the Win32 API. I am loading one bitmap into the client area of a window. When the bitmap is larger than the client area, I need scroll bars. When the window is sized, and it is larger than the bitmap, I don't need the bars, but when it is sized and it is smaller than the bitmap, I need to create scroll bars. Everything seems to be working fine except that they are triggering off and on at the incorrect position. Here is how I handle WM_SIZE, almost exactly what MSDN does in their "How to scroll a bitmap" article:
case WM_SIZE:
{
     int nHMax = max(FontCreator.m_nBmpWidth - LOWORD(lParam),0);
     int nHPage = LOWORD(lParam);

     SCROLLINFO si = {0};

     si.cbSize = sizeof(si);
     si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
     si.nMax   = nHMax;
     si.nMin   = 0;
     si.nPage  = nHPage;
     si.nPos   = cur_xpos;

     SetScrollInfo(hWnd, SB_HORZ, &si, true);

     //then I do the same for the vertical scroll bar
}
break;

They tigger on and off at about half way through the bitmap. Another odd find is that when I set the page to 0, it triggers at the correct position, but then the size of the bar is a constant small. When the page is set to the client width or height, then it grows and shrinks as you size it, like it should. Does anyone know how I can fix this problem? Any help is appreciated. Thank you, -Dev578

Share this post


Link to post
Share on other sites
almost forgot...
msdn bitmap scroll example not very good as it doesn't clamp the
nPage parameter.

From MSDN:
The SetScrollInfo function performs range checking on the values specified by the nPage and nPos members of the SCROLLINFO structure. The nPage member must specify a value from 0 to nMax - nMin +1. The nPos member must specify a value between nMin and nMax - max( nPage– 1, 0). If either value is beyond its range, the function sets it to a value that is just within the range.

Basically this means if using SIF_PAGE, you assume you can scroll the entire
image (by setting nMin and nMax to the bitmap extremes), but clamp nPos and
nPage.


#include<windows.h>
#include<fstream>
using namespace std;

// window variables
static HWND window = 0;
static int window_dx = 0;
static int window_dy = 0;

// bitmap variables
static HBITMAP bitmap_handle = 0;
static BYTE* bitmap_memory = 0;
static int bitmap_dx = 0;
static int bitmap_dy = 0;

// scrolling variables
static int scroll_origin_x = 0;
static int scroll_origin_y = 0;
static int scroll_min_x = 0;
static int scroll_min_y = 0;
static int scroll_max_x = 0;
static int scroll_max_y = 0;

// functions
LRESULT CALLBACK MainWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam);
bool LoadBitmap(HWND parent);
void FreeBitmap(void);
void DrawBitmap(HDC winDC, HBITMAP bitmap, int x, int y, int xoff, int yoff);
void InitScrollers(void);
void RecomputeScrollers(void);
bool OffsetScrollers(int dx, int dy);
bool AdjustScrollers(int x, int y);

template<class T>
inline void clamp(T& value, const T& minval, const T& maxval)
{
if(value < minval) value = minval;
if(value > maxval) value = maxval;
}

int WINAPI WinMain(HINSTANCE basead, HINSTANCE, char*, int)
{
WNDCLASS wc;
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = basead;
wc.hIcon = 0;
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "ScrollWindow";
if(!RegisterClass(&wc)) return MessageBox(0, "Could not register main window.", "Error", MB_ICONSTOP);

// create window
DWORD style = WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL;
int x = CW_USEDEFAULT; int w = CW_USEDEFAULT;
int y = CW_USEDEFAULT; int h = CW_USEDEFAULT;
HWND retval = CreateWindow("ScrollWindow", "ScrollWindow", style, x, y, w, h, 0, 0, basead, 0);
if(!retval) return MessageBox(0, "Could not create main window.", "Error", MB_ICONSTOP);
ShowWindow(retval, SW_SHOW);
UpdateWindow(retval);

// load bitmap and initialize scrollers and repaint
LoadBitmap(retval);
InitScrollers();
InvalidateRect(retval, 0, TRUE);

MSG msg;
while(GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return (int)msg.wParam;
}

LRESULT CALLBACK MainWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
{
switch(message) {
case(WM_NCCREATE) : {
// set window handle
::window = window;
return DefWindowProc(window, message, wparam, lparam);
}
case(WM_NCDESTROY) : {
// set window handle
::window = 0;
return DefWindowProc(window, message, wparam, lparam);
}
case(WM_CREATE) : {
return DefWindowProc(window, message, wparam, lparam);
}
case(WM_DESTROY) : {
FreeBitmap();
PostQuitMessage(0);
return DefWindowProc(window, message, wparam, lparam);
}
case(WM_SIZE) : {
// set window dimensions
window_dx = LOWORD(lparam);
window_dy = HIWORD(lparam);
// recompute scroll information and repaint
RecomputeScrollers();
InvalidateRect(window, 0, TRUE);
return 0;
}
case(WM_PAINT) : {
PAINTSTRUCT ps;
HDC winDC = BeginPaint(window, &ps);
DrawBitmap(winDC, bitmap_handle, 0, 0, scroll_origin_x, scroll_origin_y);
EndPaint(window, &ps);
return 0;
}
case(WM_HSCROLL) : {
bool repaint = false;
int code = (int)LOWORD(wparam);
int npos = (int)HIWORD(wparam);
switch(code) {
case(SB_LINELEFT) :
repaint = OffsetScrollers(-1, 0);
break;
case(SB_LINERIGHT) :
repaint = OffsetScrollers(+1, 0);
break;
case(SB_PAGELEFT) : {
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_PAGE;
GetScrollInfo(::window, SB_HORZ, &scrollinfo);
repaint = OffsetScrollers(-scrollinfo.nPage, 0);
break;
}
case(SB_PAGERIGHT) : {
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_PAGE;
GetScrollInfo(::window, SB_HORZ, &scrollinfo);
repaint = OffsetScrollers(+scrollinfo.nPage, 0);
break;
}
case(SB_THUMBTRACK) :
repaint = AdjustScrollers(npos, -1);
break;
case(SB_THUMBPOSITION) :
repaint = AdjustScrollers(npos, -1);
break;
}
if(repaint) InvalidateRect(window, 0, TRUE);
return 0;
}
case(WM_VSCROLL) : {
bool repaint = false;
int code = (int)LOWORD(wparam);
int npos = (int)HIWORD(wparam);
switch(code) {
case(SB_LINEUP) :
repaint = OffsetScrollers(0, -1);
break;
case(SB_LINEDOWN) :
repaint = OffsetScrollers(0, +1);
break;
case(SB_PAGEUP) : {
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_PAGE;
GetScrollInfo(::window, SB_VERT, &scrollinfo);
repaint = OffsetScrollers(0, -scrollinfo.nPage);
break;
}
case(SB_PAGEDOWN) : {
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_PAGE;
GetScrollInfo(::window, SB_VERT, &scrollinfo);
repaint = OffsetScrollers(0, +scrollinfo.nPage);
break;
}
case(SB_THUMBTRACK) :
repaint = AdjustScrollers(-1, npos);
break;
case(SB_THUMBPOSITION) :
repaint = AdjustScrollers(-1, npos);
break;
}
if(repaint) InvalidateRect(window, 0, TRUE);
return 0;
}
default : return DefWindowProc(window, message, wparam, lparam);
}
return 0;
}

bool LoadBitmap(HWND parent)
{
// open file dialog
char buffer1[MAX_PATH];
char buffer2[MAX_PATH];
buffer1[0] = '\0';
buffer2[0] = '\0';
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = parent;
ofn.hInstance = (HINSTANCE)GetWindowLong(parent, GWL_HINSTANCE);
ofn.lpstrFilter = "Bitmap Files (*.bmp)\0*.bmp\0";
ofn.lpstrFile = buffer1;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFileTitle = buffer2;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrInitialDir = 0;
ofn.lpstrTitle = "Open";
ofn.Flags = OFN_FILEMUSTEXIST;
ofn.lpstrDefExt = "bmp";
if(!GetOpenFileName(&ofn)) return false;

// delete previous bitmap
FreeBitmap();

// open file
ifstream file(buffer1, ios::binary);
if(!file) return false;

// read headers
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
if(!file.read((char*)&fileHeader, sizeof(fileHeader))) return false;
if(!file.read((char*)&infoHeader, sizeof(infoHeader))) return false;

// assume 24-bit bitmap
if(fileHeader.bfType != 0x4d42) return false;
if(infoHeader.biSize != sizeof(infoHeader)) return false;
if(infoHeader.biPlanes != 1) return false;
if(infoHeader.biBitCount != 24) return false;
if(infoHeader.biCompression != BI_RGB) return false;

// create new bitmap
BITMAPINFO info;
info.bmiHeader = infoHeader;
HDC winDC = GetDC(parent);
bitmap_handle = CreateDIBSection(winDC, &info, DIB_RGB_COLORS, (void**)&bitmap_memory, 0, 0);
ReleaseDC(parent, winDC);
if(!bitmap_handle) return false;

// read bitmap data
bitmap_dx = infoHeader.biWidth;
bitmap_dy = infoHeader.biHeight;
int total_bytes = (((24*bitmap_dx + 31) & (~31))/8)*bitmap_dy;
if(!file.read((char*)bitmap_memory, total_bytes)) return false;

return true;
}

void FreeBitmap(void)
{
DeleteObject(bitmap_handle);
bitmap_handle = 0;
bitmap_memory = 0;
bitmap_dx = bitmap_dy = 0;
}

void DrawBitmap(HDC winDC, HBITMAP bitmap, int x, int y, int xoff, int yoff)
{
if(!bitmap) return;
HDC memDC = CreateCompatibleDC(winDC);
if(!memDC) return;
BITMAP bm;
GetObject(bitmap, sizeof(bm), &bm);
HBITMAP old_bitmap = (HBITMAP)SelectObject(memDC, bitmap);
BitBlt(winDC, x, y, bm.bmWidth, bm.bmHeight, memDC, xoff, yoff, SRCCOPY);
SelectObject(memDC, old_bitmap);
DeleteDC(memDC);
}

void InitScrollers(void)
{
// must have bitmap
if(!bitmap_handle) {
EnableScrollBar(::window, SB_HORZ, ESB_DISABLE_BOTH);
EnableScrollBar(::window, SB_VERT, ESB_DISABLE_BOTH);
return;
}

// enable/disable scroll bars if client is larger than bitmap
if(window_dx > bitmap_dx) EnableScrollBar(::window, SB_HORZ, ESB_DISABLE_BOTH);
else EnableScrollBar(::window, SB_HORZ, ESB_ENABLE_BOTH);

if(window_dy > bitmap_dy) EnableScrollBar(::window, SB_VERT, ESB_DISABLE_BOTH);
else EnableScrollBar(::window, SB_VERT, ESB_ENABLE_BOTH);

// initialize scroll information
scroll_origin_x = 0;
scroll_origin_y = 0;
scroll_min_x = 0;
scroll_min_y = 0;
scroll_max_x = bitmap_dx;
scroll_max_y = bitmap_dy;

// set scroll bar parameters
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
scrollinfo.nMin = scroll_min_x;
scrollinfo.nMax = scroll_max_x;
scrollinfo.nPage = min(window_dx, scroll_max_x + 1);
scrollinfo.nPos = scroll_origin_x;
scrollinfo.nTrackPos = 0;
SetScrollInfo(::window, SB_HORZ, &scrollinfo, TRUE);

scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
scrollinfo.nMin = scroll_min_y;
scrollinfo.nMax = scroll_max_y;
scrollinfo.nPage = min(window_dy, scroll_max_y + 1);
scrollinfo.nPos = scroll_origin_y;
scrollinfo.nTrackPos = 0;
SetScrollInfo(::window, SB_VERT, &scrollinfo, TRUE);
}

void RecomputeScrollers(void)
{
// must have bitmap
if(!bitmap_handle) {
EnableScrollBar(::window, SB_HORZ, ESB_DISABLE_BOTH);
EnableScrollBar(::window, SB_VERT, ESB_DISABLE_BOTH);
return;
}

// enable/disable scroll bars if client is larger than bitmap
if(window_dx > bitmap_dx) EnableScrollBar(::window, SB_HORZ, ESB_DISABLE_BOTH);
else EnableScrollBar(::window, SB_HORZ, ESB_ENABLE_BOTH);

if(window_dy > bitmap_dy) EnableScrollBar(::window, SB_VERT, ESB_DISABLE_BOTH);
else EnableScrollBar(::window, SB_VERT, ESB_ENABLE_BOTH);

// this is different than in InitScrollers...
// reinitialize scroll information, but don't change the origin
scroll_min_x = 0;
scroll_min_y = 0;
scroll_max_x = bitmap_dx;
scroll_max_y = bitmap_dy;

// this is different than in InitScrollers...
// clamp the scroll origins to the min and max values
int limit_x = max(0, bitmap_dx - window_dx);
int limit_y = max(0, bitmap_dy - window_dy);
clamp(scroll_origin_x, scroll_min_x, limit_x);
clamp(scroll_origin_y, scroll_min_y, limit_y);

// set scroll bar parameters
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
scrollinfo.nMin = scroll_min_x;
scrollinfo.nMax = scroll_max_x;
scrollinfo.nPage = min(window_dx, scroll_max_x + 1);
scrollinfo.nPos = scroll_origin_x;
scrollinfo.nTrackPos = 0;
SetScrollInfo(::window, SB_HORZ, &scrollinfo, TRUE);

scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
scrollinfo.nMin = scroll_min_y;
scrollinfo.nMax = scroll_max_y;
scrollinfo.nPage = min(window_dy, scroll_max_y + 1);
scrollinfo.nPos = scroll_origin_y;
scrollinfo.nTrackPos = 0;
SetScrollInfo(::window, SB_VERT, &scrollinfo, TRUE);
}

bool OffsetScrollers(int dx, int dy)
{
// set and adjust new scroll origin
int new_x_origin = scroll_origin_x + dx;
clamp(new_x_origin, scroll_min_x, scroll_max_x);

int new_y_origin = scroll_origin_y + dy;
clamp(new_y_origin, scroll_min_y, scroll_max_y);

// no need to repaint, nothing has changed
if(new_x_origin == scroll_origin_x && new_y_origin == scroll_origin_y)
return false;

// set new scroll origins
scroll_origin_x = new_x_origin;
scroll_origin_y = new_y_origin;

// clamp the scroll origins to the min and max values
int limit_x = max(0, bitmap_dx - window_dx);
int limit_y = max(0, bitmap_dy - window_dy);
clamp(scroll_origin_x, scroll_min_x, limit_x);
clamp(scroll_origin_y, scroll_min_y, limit_y);

// set scroll bar parameters
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_RANGE | SIF_POS;
scrollinfo.nMin = scroll_min_x;
scrollinfo.nMax = scroll_max_x;
scrollinfo.nPos = scroll_origin_x;
scrollinfo.nTrackPos = 0;
SetScrollInfo(::window, SB_HORZ, &scrollinfo, TRUE);

scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_RANGE | SIF_POS;
scrollinfo.nMin = scroll_min_y;
scrollinfo.nMax = scroll_max_y;
scrollinfo.nPos = scroll_origin_y;
scrollinfo.nTrackPos = 0;
SetScrollInfo(::window, SB_VERT, &scrollinfo, TRUE);

return true;
}

bool AdjustScrollers(int x, int y)
{
// set and adjust new scroll origin
int new_x_origin = (x == -1 ? scroll_origin_x : x); // -1 = no change
clamp(new_x_origin, scroll_min_x, scroll_max_x);

int new_y_origin = (y == -1 ? scroll_origin_y : y); // -1 = no change
clamp(new_x_origin, scroll_min_y, scroll_max_y);

// no need to repaint, nothing has changed
if(new_x_origin == scroll_origin_x && new_y_origin == scroll_origin_y)
return false;

// set new scroll origins
scroll_origin_x = new_x_origin;
scroll_origin_y = new_y_origin;

// clamp the scroll origins to the min and max values
int limit_x = max(0, bitmap_dx - window_dx);
int limit_y = max(0, bitmap_dy - window_dy);
clamp(scroll_origin_x, scroll_min_x, limit_x);
clamp(scroll_origin_y, scroll_min_y, limit_y);

// set scroll bar parameters
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_RANGE | SIF_POS;
scrollinfo.nMin = scroll_min_x;
scrollinfo.nMax = scroll_max_x;
scrollinfo.nPos = scroll_origin_x;
scrollinfo.nTrackPos = 0;
SetScrollInfo(::window, SB_HORZ, &scrollinfo, TRUE);

scrollinfo.cbSize = sizeof(SCROLLINFO);
scrollinfo.fMask = SIF_RANGE | SIF_POS;
scrollinfo.nMin = scroll_min_y;
scrollinfo.nMax = scroll_max_y;
scrollinfo.nPos = scroll_origin_y;
scrollinfo.nTrackPos = 0;
SetScrollInfo(::window, SB_VERT, &scrollinfo, TRUE);

return true;
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this