Files
MiniGUI/src/control/scrolled.c
T

732 lines
21 KiB
C

///////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT NOTICE
//
// The following open source license statement does not apply to any
// entity in the Exception List published by FMSoft.
//
// For more information, please visit:
//
// https://www.fmsoft.cn/exception-list
//
//////////////////////////////////////////////////////////////////////////////
/*
* This file is part of MiniGUI, a mature cross-platform windowing
* and Graphics User Interface (GUI) support system for embedded systems
* and smart IoT devices.
*
* Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd.
* Copyright (C) 1998~2002, WEI Yongming
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Or,
*
* As this program is a library, any link to this program must follow
* GNU General Public License version 3 (GPLv3). If you cannot accept
* GPLv3, you need to be licensed from FMSoft.
*
* If you have got a commercial license of this program, please use it
* under the terms and conditions of the commercial license.
*
* For more information about the commercial license, please refer to
* <http://www.minigui.com/blog/minigui-licensing-policy/>.
*/
/*
** scrolled.c: basic scrolled window support
**
** Create date: 2004/03/01
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "common.h"
#ifdef _MGCTRL_SCROLLVIEW
#include "minigui.h"
#include "gdi.h"
#include "window.h"
#include "ctrl/ctrlhelper.h"
#include "ctrl/scrollview.h"
#include "cliprect.h"
#include "internals.h"
#include "ctrlclass.h"
#include "scrolled.h"
#define SV_H_OUTRANGE() \
(pscrdata->nContWidth > pscrdata->visibleWidth)
#define SV_V_OUTRANGE() \
(pscrdata->nContHeight > pscrdata->visibleHeight)
#define svHScroll(scrollval, newpos) \
svScroll(hWnd, pscrdata, scrollval, newpos, TRUE);
#define svVScroll(scrollval, newpos) \
svScroll (hWnd, pscrdata, scrollval, newpos, FALSE);
/* ---------------------------------------------------------------------------- */
/* adjust the horz scrollbar according to the content offset value */
int scrolled_set_hscrollinfo (HWND hWnd, PSCRDATA pscrdata)
{
SCROLLINFO si;
if (pscrdata->sbPolicy == SB_POLICY_NEVER) {
ShowScrollBar (hWnd, SB_HORZ, FALSE);
return 0;
}
else if (pscrdata->sbPolicy == SB_POLICY_AUTOMATIC) {
if (!SV_H_OUTRANGE()) {
ShowScrollBar (hWnd, SB_HORZ, FALSE);
return 0;
}
}
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = pscrdata->nContWidth - 1;
si.nPage = pscrdata->visibleWidth;
si.nPos = pscrdata->nContX;
SetScrollInfo (hWnd, SB_HORZ, &si, TRUE);
ShowScrollBar (hWnd, SB_HORZ, TRUE);
return 0;
}
/* adjust the vert scrollbar according to the content offset value */
int scrolled_set_vscrollinfo (HWND hWnd, PSCRDATA pscrdata)
{
SCROLLINFO si;
if (pscrdata->sbPolicy == SB_POLICY_NEVER) {
ShowScrollBar (hWnd, SB_VERT, FALSE);
return 0;
}
else if (pscrdata->sbPolicy == SB_POLICY_AUTOMATIC) {
if (!SV_V_OUTRANGE()) {
ShowScrollBar (hWnd, SB_VERT, FALSE);
return 0;
}
}
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = pscrdata->nContHeight - 1;
si.nPage = pscrdata->visibleHeight;
si.nPos = pscrdata->nContY;
SetScrollInfo (hWnd, SB_VERT, &si, TRUE);
ShowScrollBar (hWnd, SB_VERT, TRUE);
return 0;
}
/* set content position */
BOOL scrolled_set_cont_pos (HWND hWnd, PSCRDATA pscrdata, int cont_x, int cont_y)
{
BOOL bChange = FALSE;
int cont_w, cont_h;
cont_w = pscrdata->nContWidth - pscrdata->visibleWidth;
cont_h = pscrdata->nContHeight - pscrdata->visibleHeight;
if (cont_x > cont_w && cont_w > 0)
cont_x = cont_w;
if (cont_y > cont_h && cont_h > 0)
cont_y = cont_h;
if (cont_x >= 0 && cont_x <= cont_w) {
//move content
pscrdata->prevnContX = pscrdata->nContX;
pscrdata->nContX = cont_x;
scrolled_set_content (hWnd, pscrdata, FALSE);
pscrdata->prevnContX = pscrdata->nContX;
SetScrollPos (hWnd, SB_HORZ, cont_x);
bChange = TRUE;
}
if (cont_y >= 0 && cont_y <= cont_h) {
//move content
pscrdata->prevnContY = pscrdata->nContY;
pscrdata->nContY = cont_y;
scrolled_set_content (hWnd, pscrdata, FALSE);
pscrdata->prevnContY = pscrdata->nContY;
SetScrollPos (hWnd, SB_VERT, cont_y);
bChange = TRUE;
}
if (bChange)
UpdateInvalidClient(hWnd, FALSE);
return bChange;
}
/* make a point in the content area visible */
BOOL scrolled_make_pos_visible (HWND hWnd, PSCRDATA pscrdata, int pos_x, int pos_y)
{
int cont_x = -1, cont_y = -1;
if (pos_x >= 0) {
if (pos_x >= pscrdata->nContWidth)
pos_x = pscrdata->nContWidth - 1;
if (pos_x < pscrdata->nContX)
cont_x = pos_x;
else if (pos_x > pscrdata->nContX + pscrdata->visibleWidth - 1)
cont_x = pos_x - pscrdata->visibleWidth + 1;
}
if (pos_y >= 0) {
if (pos_y >= pscrdata->nContHeight)
pos_y = pscrdata->nContHeight - 1;
if (pos_y < pscrdata->nContY)
cont_y = pos_y;
else if (pos_y > pscrdata->nContY + pscrdata->visibleHeight - 1)
cont_y = pos_y - pscrdata->visibleHeight + 1;
}
return scrolled_set_cont_pos (hWnd, pscrdata, cont_x, cont_y);
}
/* scrollbar action */
static void svScroll (HWND hWnd, PSCRDATA pscrdata, int scrollval, int newpos, BOOL bHScroll)
{
int scrollBoundMax;
int scrollBoundMin;
int scroll = 0;
BOOL bScroll = FALSE;
int nOffset;
if (bHScroll) {
scrollBoundMax = pscrdata->nContWidth - pscrdata->visibleWidth;
nOffset = pscrdata->nContX;
}
else {
scrollBoundMax = pscrdata->nContHeight - pscrdata->visibleHeight;
nOffset = pscrdata->nContY;
}
scrollBoundMin = 0;
if (newpos >= 0) { // SB_THUMBTRACK
scrollval = newpos - nOffset;
}
scroll = (scrollval>0?scrollval:-scrollval);
if (scrollval > 0 && nOffset < scrollBoundMax) {
if ((nOffset + scroll) > scrollBoundMax)
nOffset = scrollBoundMax;
else
nOffset += scroll;
bScroll = TRUE;
}
else if ( scrollval < 0 && nOffset > scrollBoundMin) {
if ((nOffset - scroll) < scrollBoundMin)
nOffset = scrollBoundMin;
else
nOffset -= scroll;
bScroll = TRUE;
}
if (bScroll) {
scrolled_set_cont_pos (hWnd, pscrdata, (bHScroll?nOffset:-1), (bHScroll?-1:nOffset));
}
}
/* set the position and size of the viewport window */
void scrolled_set_visible (HWND hWnd, PSCRDATA pscrdata)
{
/* maybe we need to reset content width and height */
if (pscrdata->nContWidth < pscrdata->visibleWidth) {
/* houhh 20101012, do not need to reset nContWidth here, Because the content
* width should be modify by add content.*/
#if 0
pscrdata->nContWidth = pscrdata->visibleWidth;
#endif
pscrdata->nContX = 0;
pscrdata->prevnContX = 0;
}
else {
if(pscrdata->nContX > pscrdata->nContWidth - pscrdata->visibleWidth
&&(pscrdata->nContWidth - pscrdata->visibleWidth) > 0 ) {
pscrdata->prevnContX = pscrdata->nContX;
pscrdata->nContX = pscrdata->nContWidth - pscrdata->visibleWidth;
}
}
if (pscrdata->nContHeight < pscrdata->visibleHeight) {
#if 0
pscrdata->nContHeight = pscrdata->visibleHeight;
#endif
pscrdata->nContY = 0;
pscrdata->prevnContY = 0;
}
else{
if(pscrdata->nContY > pscrdata->nContHeight - pscrdata->visibleHeight
&& (pscrdata->nContHeight - pscrdata->visibleHeight) > 0) {
pscrdata->nContY = pscrdata->nContHeight - pscrdata->visibleHeight;
pscrdata->prevnContY = pscrdata->nContY;
}
}
/* refresh the whole window as long as the visible area is changed */
scrolled_set_content (hWnd, pscrdata, TRUE);
pscrdata->prevnContX = pscrdata->nContX;
pscrdata->prevnContY = pscrdata->nContY;
}
int scrolled_visible_to_content (PSCRDATA pscrdata, int *x, int *y)
{
if (x)
*x = *x + pscrdata->nContX;
if (y)
*y = *y + pscrdata->nContY;
return 0;
}
int scrolled_content_to_visible (PSCRDATA pscrdata, int *x, int *y)
{
if (x)
*x = *x - pscrdata->nContX;
if (y)
*y = *y - pscrdata->nContY;
if (!x || !y)
return -1;
if (*x < 0 || *x > pscrdata->visibleWidth ||
*y < 0 || *y > pscrdata->visibleHeight)
return -1;
return 0;
}
/* recalc visible area and container area, then set scrollbar */
void scrolled_recalc_areas (HWND hWnd, PSCRDATA pscrdata, int new_w, int new_h)
{
if (!pscrdata || new_w <= 0 || new_h <= 0)
return;
/* reset viewport as client - margin */
pscrdata->visibleWidth = new_w;
pscrdata->visibleHeight = new_h;
scrolled_set_visible (hWnd, pscrdata);
/*
int old_vw, old_vh;
old_vw = pscrdata->visibleWidth;
old_vh = pscrdata->visibleHeight;
if (pscrdata->nContWidth == old_vw) {
pscrdata->nContWidth = pscrdata->visibleWidth;
//should refresh the whole window
scrolled_set_content (hWnd, pscrdata, TRUE);
}
if (pscrdata->nContHeight == old_vh) {
pscrdata->nContHeight = pscrdata->visibleHeight;
//should refresh the whole window
scrolled_set_content (hWnd, pscrdata, TRUE);
}
*/
scrolled_set_hscrollinfo (hWnd, pscrdata);
scrolled_set_vscrollinfo (hWnd, pscrdata);
}
void scrolled_hscroll (HWND hWnd, PSCRDATA pscrdata, WPARAM wParam, LPARAM lParam)
{
int hscroll = 0, newpos = -1;
if (wParam == SB_LINERIGHT)
hscroll = pscrdata->hScrollVal;
else if (wParam == SB_LINELEFT)
hscroll = -pscrdata->hScrollVal;
else if (wParam == SB_PAGERIGHT)
hscroll = pscrdata->hScrollPageVal;
else if (wParam == SB_PAGELEFT)
hscroll = -pscrdata->hScrollPageVal;
else if (wParam == SB_THUMBTRACK)
newpos = lParam;
else if (wParam == SB_THUMBPOSITION) {
newpos = lParam;
}
svHScroll (hscroll, newpos);
}
void scrolled_vscroll (HWND hWnd, PSCRDATA pscrdata, WPARAM wParam, LPARAM lParam)
{
int vscroll = 0, newpos = -1;
if (wParam == SB_LINEDOWN)
vscroll = pscrdata->vScrollVal;
else if (wParam == SB_LINEUP)
vscroll = -pscrdata->vScrollVal;
else if (wParam == SB_PAGEDOWN)
vscroll = pscrdata->vScrollPageVal;
else if (wParam == SB_PAGEUP)
vscroll = -pscrdata->vScrollPageVal;
else if (wParam == SB_THUMBTRACK) {
newpos = lParam;
}
else if (wParam == SB_THUMBPOSITION) {
newpos = lParam;
}
if (wParam != SB_THUMBPOSITION)
svVScroll (vscroll, newpos);
}
int scrolled_set_cont_width (HWND hWnd, PSCRDATA pscrdata, int cont_w)
{
int oldval = pscrdata->nContWidth;
if (cont_w < 0)
return 0;
//pscrdata->nContWidth = cont_w < pscrdata->visibleWidth ? pscrdata->visibleWidth : cont_w;
pscrdata->nContWidth = cont_w;
if (pscrdata->nContWidth == oldval)
return 0;
if (pscrdata->nContX > pscrdata->nContWidth - pscrdata->visibleWidth
&& (pscrdata->nContWidth - pscrdata->visibleWidth) > 0) {
pscrdata->prevnContX = pscrdata->nContX;
pscrdata->nContX = pscrdata->nContWidth - pscrdata->visibleWidth;
}
else if(pscrdata->nContWidth <= pscrdata->visibleWidth) {
pscrdata->prevnContX = pscrdata->nContX;
pscrdata->nContX = 0;
}
scrolled_set_hscrollinfo (hWnd, pscrdata);
//move content
scrolled_set_content (hWnd, pscrdata, FALSE);
pscrdata->prevnContX = pscrdata->nContX;
return pscrdata->nContWidth;
}
int scrolled_set_cont_height (HWND hWnd, PSCRDATA pscrdata, int cont_h)
{
int oldval = pscrdata->nContHeight;
if (cont_h < 0)
return 0;
//pscrdata->nContHeight = cont_h < pscrdata->visibleHeight ? pscrdata->visibleHeight : cont_h;
pscrdata->nContHeight = cont_h;
if (pscrdata->nContHeight == oldval)
return 0;
if (pscrdata->nContY > pscrdata->nContHeight - pscrdata->visibleHeight
&& (pscrdata->nContHeight - pscrdata->visibleHeight) > 0) {
pscrdata->prevnContY = pscrdata->nContY;
pscrdata->nContY = pscrdata->nContHeight - pscrdata->visibleHeight;
}
else if (pscrdata->nContHeight <= pscrdata->visibleHeight) {
pscrdata->prevnContY = pscrdata->nContY;
pscrdata->nContY = 0;
}
scrolled_set_vscrollinfo (hWnd, pscrdata);
//move content
scrolled_set_content (hWnd, pscrdata, FALSE);
pscrdata->prevnContY = pscrdata->nContY;
return pscrdata->nContHeight;
}
/*
int scrolled_SetVisibleWidth (HWND hWnd, PSCRDATA pscrdata, int vis_w)
{
int oldval = pscrdata->visibleWidth;
if (vis_w < 0)
return 0;
pscrdata->visibleWidth = vis_w < pscrdata->nContWidth ? vis_w : pscrdata->nContWidth;
if (pscrdata->visibleWidth = oldval)
return 0;
scrolled_set_hscrollinfo (hWnd, pscrdata);
return pscrdata->visibleWidth;
}
*/
void scrolled_set_scrollval (PSCRDATA pscrdata, int hval, int vval)
{
if (hval > 0)
pscrdata->hScrollVal = hval;
if (vval > 0)
pscrdata->vScrollVal = vval;
}
void scrolled_set_scrollpageval (PSCRDATA pscrdata, int hval, int vval)
{
if (hval > 0)
pscrdata->hScrollPageVal = hval;
if (vval > 0)
pscrdata->vScrollPageVal = vval;
}
void scrolled_set_margins (PSCRDATA pscrdata, RECT *rc)
{
RECT rcWnd;
if (!rc)
return;
if (rc->left >= 0)
pscrdata->leftMargin = rc->left;
if (rc->right >= 0)
pscrdata->rightMargin = rc->right;
if (rc->top >= 0)
pscrdata->topMargin = rc->top;
if (rc->bottom >= 0)
pscrdata->bottomMargin = rc->bottom;
GetClientRect (pscrdata->hSV, &rcWnd);
scrolled_recalc_areas (pscrdata->hSV, pscrdata,
RECTW(rcWnd) - pscrdata->leftMargin - pscrdata->rightMargin,
RECTH(rcWnd) - pscrdata->topMargin - pscrdata->bottomMargin);
}
void scrolled_refresh_rect (PSCRDATA pscrdata, RECT *rc)
{
RECT rcUpdate;
memcpy (&rcUpdate, rc, sizeof(RECT));
scrolled_content_to_window (pscrdata, NULL, &rcUpdate.top);
rcUpdate.bottom = rcUpdate.top + RECTHP(rc);
if (rcUpdate.right == 0) {
rcUpdate.left = 0;
scrolled_content_to_window (pscrdata, &rcUpdate.left, NULL);
if (rcUpdate.left < 0)
rcUpdate.right = rcUpdate.left + pscrdata->nContWidth;
else
rcUpdate.right = rcUpdate.left + pscrdata->visibleWidth;
}
InvalidateRect ( pscrdata->hSV, &rcUpdate, TRUE);
}
void scrolled_init_contsize (HWND hWnd, PSCRDATA pscrdata)
{
pscrdata->nContX = 0;
pscrdata->nContY = 0;
pscrdata->prevnContX = 0;
pscrdata->prevnContY = 0;
pscrdata->nContWidth = pscrdata->visibleWidth;
pscrdata->nContHeight = pscrdata->visibleHeight;
}
void scrolled_init_margins (PSCRDATA pscrdata, int left, int top,
int right, int bottom)
{
pscrdata->leftMargin = left;
pscrdata->topMargin = top;
pscrdata->rightMargin = right;
pscrdata->bottomMargin = bottom;
}
/* set initial content size and viewport size */
int scrolled_init (HWND hWnd, PSCRDATA pscrdata, int w, int h)
{
RECT rcWnd;
/* new added */
pscrdata->leftMargin = 0;
pscrdata->topMargin = 0;
pscrdata->rightMargin = 0;
pscrdata->bottomMargin = 0;
pscrdata->nContX = 0;
pscrdata->nContY = 0;
pscrdata->prevnContX = 0;
pscrdata->prevnContY = 0;
if (w > 0 && h > 0) {
pscrdata->visibleWidth = w;
pscrdata->visibleHeight = h;
}
else {
GetClientRect (hWnd, &rcWnd);
pscrdata->visibleWidth = RECTW(rcWnd);
pscrdata->visibleHeight = RECTH(rcWnd);
}
pscrdata->nContWidth = pscrdata->visibleWidth;
pscrdata->nContHeight = pscrdata->visibleHeight;
pscrdata->sbPolicy = SB_POLICY_AUTOMATIC;
pscrdata->hScrollVal = HSCROLL;
pscrdata->vScrollVal = VSCROLL;
pscrdata->hScrollPageVal = pscrdata->visibleWidth;
pscrdata->vScrollPageVal = pscrdata->visibleHeight;
pscrdata->hSV = hWnd;
return 0;
}
LRESULT DefaultScrolledProc (HWND hWnd, int message, WPARAM wParam, LPARAM lParam)
{
PSCRDATA pscrdata = NULL;
if (message != MSG_CREATE)
pscrdata = (PSCRDATA) GetWindowAdditionalData2 (hWnd);
switch (message) {
case MSG_HSCROLL:
scrolled_hscroll (hWnd, pscrdata, wParam, lParam);
break;
case MSG_VSCROLL:
scrolled_vscroll (hWnd, pscrdata, wParam, lParam);
break;
/*
* three cases:
* 1. move window; 2. set content range; 3. show scrollbar
*/
case MSG_SIZECHANGED:
{
RECT *rcClient = (RECT*)lParam;
//default case : the whole client area is the visible area
//scrollwnd control is this case
//scrollview control is different
if (!pscrdata)
return 0;
//scrolled_recalc_areas (hWnd, pscrdata, RECTWP(rcClient), RECTHP(rcClient));
scrolled_recalc_areas (hWnd, pscrdata,
RECTWP(rcClient) - pscrdata->leftMargin - pscrdata->rightMargin,
RECTHP(rcClient) - pscrdata->topMargin - pscrdata->bottomMargin);
return 0;
}
case MSG_KEYDOWN:
{
if (wParam == SCANCODE_PAGEDOWN) {
SendMessage (hWnd, MSG_VSCROLL, SB_PAGEDOWN, 0);
}
else if (wParam == SCANCODE_PAGEUP)
SendMessage (hWnd, MSG_VSCROLL, SB_PAGEUP, 0);
else if (wParam == SCANCODE_CURSORBLOCKLEFT)
SendMessage (hWnd, MSG_HSCROLL, SB_PAGELEFT, 0);
else if (wParam == SCANCODE_CURSORBLOCKRIGHT)
SendMessage (hWnd, MSG_HSCROLL, SB_PAGERIGHT, 0);
break;
}
case SVM_SETCONTRANGE:
scrolled_set_cont_width (hWnd, pscrdata, wParam);
scrolled_set_cont_height (hWnd, pscrdata, lParam);
return 0;
case SVM_SETCONTWIDTH:
return scrolled_set_cont_width (hWnd, pscrdata, wParam);
case SVM_SETCONTHEIGHT:
return scrolled_set_cont_height (hWnd, pscrdata, wParam);
case SVM_GETHSCROLLVAL:
return pscrdata->hScrollVal;
case SVM_GETVSCROLLVAL:
return pscrdata->vScrollVal;
case SVM_GETHSCROLLPAGEVAL:
return pscrdata->hScrollPageVal;
case SVM_GETVSCROLLPAGEVAL:
return pscrdata->vScrollPageVal;
case SVM_SETSCROLLVAL:
{
scrolled_set_scrollval (pscrdata, wParam, lParam);
return 0;
}
case SVM_SETSCROLLPAGEVAL:
{
scrolled_set_scrollpageval (pscrdata, wParam, lParam);
return 0;
}
case SVM_SETCONTPOS:
{
scrolled_set_cont_pos (hWnd, pscrdata, wParam, lParam);
return 0;
}
/* get scrolled size information */
case SVM_GETCONTWIDTH:
return pscrdata->nContWidth;
case SVM_GETCONTHEIGHT:
return pscrdata->nContHeight;
case SVM_GETCONTENTX:
return pscrdata->nContX;
case SVM_GETCONTENTY:
return pscrdata->nContY;
case SVM_GETVISIBLEWIDTH:
return pscrdata->visibleWidth;
case SVM_GETVISIBLEHEIGHT:
return pscrdata->visibleHeight;
case SVM_GETMARGINS:
{
RECT *rc = (RECT *)lParam;
if (rc)
scrolled_get_margins (pscrdata, rc);
return 0;
}
case SVM_GETLEFTMARGIN:
return pscrdata->leftMargin;
case SVM_GETRIGHTMARGIN:
return pscrdata->rightMargin;
case SVM_GETTOPMARGIN:
return pscrdata->topMargin;
case SVM_GETBOTTOMMARGIN:
return pscrdata->bottomMargin;
case SVM_SETMARGINS:
{
RECT *rc = (RECT *)lParam;
if (rc)
scrolled_set_margins (pscrdata, rc);
return 0;
}
case SVM_MAKEPOSVISIBLE:
scrolled_make_pos_visible (hWnd, pscrdata, wParam, lParam);
return 0;
}/* end switch */
return DefaultControlProc (hWnd, message, wParam, lParam);
}
#endif /* _MGCTRL_SCROLLVIEW */