Sign in to follow this  
dev578

Scroll Bar Problem

Recommended Posts

dev578    128
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
yadango    567
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