mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-09-30 06:23:22 +08:00
1063 lines
31 KiB
C
1063 lines
31 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/>.
|
|
*/
|
|
/*
|
|
** button.c: the Button Control module.
|
|
**
|
|
** Create date: 1999/8/23
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "common.h"
|
|
|
|
#ifdef _MGCTRL_BUTTON
|
|
#include "minigui.h"
|
|
#include "gdi.h"
|
|
#include "window.h"
|
|
#include "ctrl/ctrlhelper.h"
|
|
#include "ctrl/button.h"
|
|
#include "cliprect.h"
|
|
#include "internals.h"
|
|
#include "ctrlclass.h"
|
|
#include "fixedmath.h"
|
|
|
|
#include "button_impl.h"
|
|
#include "ctrlmisc.h"
|
|
|
|
# define BTN_WIDTH_BMP 20
|
|
# define BTN_HEIGHT_BMP 20
|
|
# define BTN_INTER_BMPTEXT 2
|
|
|
|
#define BUTTON_STATUS(pctrl) (((PBUTTONDATA)((pctrl)->dwAddData2))->status)
|
|
#define BUTTON_DATA(pctrl) (((PBUTTONDATA)((pctrl)->dwAddData2))->data)
|
|
#define BUTTON_TYPE(pctrl) ((pctrl)->dwStyle & BS_TYPEMASK)
|
|
|
|
#define BUTTON_IS_AUTO(pctrl) \
|
|
(BUTTON_TYPE(pctrl) == BS_AUTO3STATE || \
|
|
BUTTON_TYPE(pctrl) == BS_AUTOCHECKBOX || \
|
|
BUTTON_TYPE(pctrl) == BS_AUTORADIOBUTTON)
|
|
|
|
#define BUTTON_IS_PUSHBTN(pctrl) \
|
|
(BUTTON_TYPE(pctrl) == BS_PUSHBUTTON || \
|
|
BUTTON_TYPE(pctrl) == BS_DEFPUSHBUTTON)
|
|
|
|
#define BUTTON_IS_RADIOBTN(pctrl) \
|
|
(BUTTON_TYPE(pctrl) == BS_AUTORADIOBUTTON || \
|
|
BUTTON_TYPE(pctrl) == BS_RADIOBUTTON)
|
|
|
|
#define BUTTON_IS_CHECKBTN(pctrl) \
|
|
(BUTTON_TYPE(pctrl) == BS_AUTOCHECKBOX || \
|
|
BUTTON_TYPE(pctrl) == BS_CHECKBOX)
|
|
|
|
#define BUTTON_IS_3STATEBTN(pctrl) \
|
|
(BUTTON_TYPE(pctrl) == BS_AUTOCHECKBOX || \
|
|
BUTTON_TYPE(pctrl) == BS_CHECKBOX)
|
|
|
|
#define BUTTON_SET_POSE(pctrl, status) \
|
|
do { \
|
|
BUTTON_STATUS(pctrl) &= ~BST_POSE_MASK; \
|
|
BUTTON_STATUS(pctrl) |= status; \
|
|
}while(0)
|
|
|
|
#define BUTTON_SET_CHECK(pctrl, check) \
|
|
do { \
|
|
BUTTON_STATUS(pctrl) &= ~BST_CHECK_MASK;\
|
|
BUTTON_STATUS(pctrl) |= check;\
|
|
}while(0)
|
|
|
|
#define BUTTON_GET_POSE(pctrl) \
|
|
(BUTTON_STATUS(pctrl) & BST_POSE_MASK)
|
|
|
|
#define BUTTON_GET_CHECK(pctrl) \
|
|
(BUTTON_STATUS(pctrl) & BST_CHECK_MASK)
|
|
|
|
#define BUTTON_OFFSET 1
|
|
|
|
static LRESULT ButtonCtrlProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
static void btnGetRects (PCONTROL pctrl, RECT* prcClient,
|
|
RECT* prcContent,
|
|
RECT* prcBitmap);
|
|
|
|
/*when check a radio button, uncheck others in the same group*/
|
|
static void btnUncheckOthers (PCONTROL pctrl)
|
|
{
|
|
PCONTROL pGroupLeader = pctrl;
|
|
PCONTROL pOthers;
|
|
DWORD type = BUTTON_TYPE(pctrl);
|
|
|
|
while (pGroupLeader) {
|
|
if (pGroupLeader->dwStyle & WS_GROUP)
|
|
break;
|
|
|
|
pGroupLeader = pGroupLeader->prev;
|
|
}
|
|
|
|
if(!pGroupLeader)
|
|
return ;
|
|
|
|
pOthers = pGroupLeader;
|
|
|
|
do{
|
|
if (BUTTON_TYPE(pOthers)!= type)
|
|
break;
|
|
|
|
if ((pOthers != pctrl) &&
|
|
(BUTTON_GET_CHECK(pOthers)!= BST_UNCHECKED)) {
|
|
BUTTON_SET_CHECK(pOthers, BST_UNCHECKED);
|
|
InvalidateRect ((HWND)pOthers, NULL, TRUE);
|
|
}
|
|
|
|
pOthers = pOthers->next;
|
|
} while (pOthers && !(pOthers->dwStyle & WS_GROUP));
|
|
|
|
}
|
|
|
|
/*
|
|
* change_checked:
|
|
* This function change check status.
|
|
* return -1: can't change(the button in disable status)
|
|
* 0 : new check is old check, didn't change
|
|
* 1 : changed
|
|
* Author: XuguangWang
|
|
* Date: 2007-12-7
|
|
*/
|
|
static int change_checked (PCONTROL pctrl, int check_status)
|
|
{
|
|
int old_status = BUTTON_STATUS(pctrl) & BST_CHECK_MASK;
|
|
/*if push button return*/
|
|
MG_CHECK_RET (BUTTON_TYPE(pctrl) != BS_PUSHBUTTON, -1);
|
|
|
|
MG_CHECK_RET (old_status != check_status, 0);
|
|
|
|
switch (pctrl->dwStyle & BS_TYPEMASK)
|
|
{
|
|
case BS_RADIOBUTTON:
|
|
case BS_AUTORADIOBUTTON:
|
|
MG_CHECK_RET (check_status != BST_INDETERMINATE, -1);
|
|
BUTTON_SET_CHECK (pctrl, check_status);
|
|
btnUncheckOthers (pctrl);
|
|
break;
|
|
|
|
case BS_CHECKBOX:
|
|
case BS_AUTOCHECKBOX:
|
|
MG_CHECK_RET (check_status != BST_INDETERMINATE, -1);
|
|
BUTTON_SET_CHECK (pctrl, check_status);
|
|
break;
|
|
|
|
case BS_3STATE:
|
|
case BS_AUTO3STATE:
|
|
BUTTON_SET_CHECK (pctrl, check_status);
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* switch_check:
|
|
* switch check status when the button is clicked
|
|
* return: new check status
|
|
* Author: XuguangWang
|
|
* Date: 2007-12-7
|
|
*/
|
|
static int switch_check (PCONTROL pctrl)
|
|
{
|
|
int old_check = BUTTON_GET_CHECK(pctrl);
|
|
/*calc new ckeck status*/
|
|
switch (BUTTON_TYPE(pctrl))
|
|
{
|
|
case BS_RADIOBUTTON:
|
|
case BS_AUTORADIOBUTTON:
|
|
return BST_CHECKED;
|
|
|
|
case BS_CHECKBOX:
|
|
case BS_AUTOCHECKBOX:
|
|
return old_check==BST_CHECKED ? BST_UNCHECKED : BST_CHECKED;
|
|
|
|
case BS_3STATE:
|
|
case BS_AUTO3STATE:
|
|
return old_check==BST_UNCHECKED ? BST_CHECKED : (
|
|
BUTTON_GET_CHECK(pctrl) == BST_CHECKED ?
|
|
BST_INDETERMINATE : BST_UNCHECKED
|
|
);
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*change_status_from_pose
|
|
* change button status (pose and check status), and invalidate window.
|
|
* pose status -- normal, hilite, pressed(pushed), disable.
|
|
* check status -- unchecked, checked, indeterminate.
|
|
*
|
|
* param noti_pose_changing: indicating if the status changing is from key or
|
|
* mouse lbutton up and down.
|
|
* TRUE -- the button(if it is auto) should notify parent
|
|
* FALSE -- the pose changing is from mouse moving, so button need
|
|
* not notify parent
|
|
*
|
|
* param change_valid: if to normal(new_pose) is valid(it can change check status)
|
|
* TRUE -- key up or mouse lbutton up on the button
|
|
* FALSE -- key lbutton up is not on the button, so it's not a full
|
|
* click
|
|
* */
|
|
|
|
static void change_status_from_pose (PCONTROL pctrl, int new_pose,
|
|
BOOL noti_pose_changing, BOOL change_valid)
|
|
{
|
|
static int nt_code[3][3] = {
|
|
/*normal->nochange, hilite, pressed*/
|
|
{ 0, BN_HILITE, BN_PUSHED},
|
|
/*hilite->normal, nochange, pressed*/
|
|
{BN_UNHILITE, 0, BN_PUSHED},
|
|
/*pressed->normal, hilite, nochange*/
|
|
{BN_UNPUSHED, BN_UNPUSHED, 0},
|
|
};
|
|
|
|
DWORD dwStyle = pctrl->dwStyle;
|
|
int old_pose = BUTTON_GET_POSE(pctrl);
|
|
int new_check;
|
|
|
|
RECT rcClient;
|
|
RECT rcContent;
|
|
RECT rcBitmap;
|
|
|
|
/*make sure it can be changed, and avoid changing more than once*/
|
|
MG_CHECK (old_pose!= BST_DISABLE && old_pose != new_pose);
|
|
|
|
/*set new pose*/
|
|
BUTTON_SET_POSE(pctrl, new_pose);
|
|
|
|
if ((dwStyle & BS_NOTIFY) && noti_pose_changing)
|
|
{
|
|
if (new_pose != BST_DISABLE)
|
|
NotifyParent ((HWND)pctrl, pctrl->id,
|
|
nt_code[old_pose][new_pose]);
|
|
/*disable the button*/
|
|
else
|
|
{
|
|
/*before to disable, firstly to normal*/
|
|
switch (old_pose)
|
|
{
|
|
case BST_PUSHED:
|
|
NotifyParent ((HWND)pctrl, pctrl->id, BN_UNPUSHED);
|
|
case BST_HILITE:
|
|
NotifyParent ((HWND)pctrl, pctrl->id, BN_UNHILITE);
|
|
case BST_NORMAL:
|
|
NotifyParent ((HWND)pctrl, pctrl->id, BN_DISABLE);
|
|
break;
|
|
default:
|
|
assert (0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* DK: When the radio or check button state changes do not erase the background,
|
|
* and the prospects for direct rendering */
|
|
btnGetRects (pctrl, &rcClient, &rcContent, &rcBitmap);
|
|
if (BUTTON_IS_PUSHBTN(pctrl) || (pctrl->dwStyle & BS_PUSHLIKE))
|
|
InvalidateRect((HWND)pctrl, NULL, TRUE);
|
|
else
|
|
InvalidateRect((HWND)pctrl, &rcBitmap, FALSE);
|
|
|
|
/* complete a click if the click if valid
|
|
* (mouse lbutton up in button )*/
|
|
if (old_pose == BST_PUSHED && new_pose != BST_DISABLE
|
|
&& change_valid)
|
|
{
|
|
if (!BUTTON_IS_PUSHBTN(pctrl) && BUTTON_IS_AUTO(pctrl))
|
|
{
|
|
new_check = switch_check(pctrl);
|
|
change_checked(pctrl, new_check);
|
|
}
|
|
|
|
NotifyParent ((HWND)pctrl, pctrl->id, BN_CLICKED);
|
|
}
|
|
}
|
|
|
|
/*btnGetRects:
|
|
* get the client rect(draw body of button), the content rect
|
|
* (draw contern), and bitmap rect(draw a little icon)
|
|
*/
|
|
static void btnGetRects (PCONTROL pctrl, RECT* prcClient,
|
|
RECT* prcContent,
|
|
RECT* prcBitmap)
|
|
{
|
|
GetClientRect ((HWND)pctrl, prcClient);
|
|
|
|
if (BUTTON_IS_PUSHBTN(pctrl) || (pctrl->dwStyle & BS_PUSHLIKE))
|
|
{
|
|
SetRect (prcContent, (prcClient->left + BTN_WIDTH_BORDER),
|
|
(prcClient->top + BTN_WIDTH_BORDER),
|
|
(prcClient->right - BTN_WIDTH_BORDER),
|
|
(prcClient->bottom - BTN_WIDTH_BORDER));
|
|
|
|
if (BUTTON_GET_POSE(pctrl) == BST_PUSHED)
|
|
{
|
|
prcContent->left ++;
|
|
prcContent->top ++;
|
|
prcContent->right ++;
|
|
prcContent->bottom++;
|
|
}
|
|
|
|
SetRectEmpty (prcBitmap);
|
|
return;
|
|
}
|
|
|
|
if (pctrl->dwStyle & BS_LEFTTEXT) {
|
|
SetRect (prcContent, prcClient->left + 1,
|
|
prcClient->top + 1,
|
|
prcClient->right - BTN_WIDTH_BMP - BTN_INTER_BMPTEXT,
|
|
prcClient->bottom - 1);
|
|
SetRect (prcBitmap, prcClient->right - BTN_WIDTH_BMP,
|
|
prcClient->top + 1,
|
|
prcClient->right - 1,
|
|
prcClient->bottom - 1);
|
|
}
|
|
else {
|
|
SetRect (prcContent, prcClient->left + BTN_WIDTH_BMP + BTN_INTER_BMPTEXT,
|
|
prcClient->top + 1,
|
|
prcClient->right - 1,
|
|
prcClient->bottom - 1);
|
|
SetRect (prcBitmap, prcClient->left + 1,
|
|
prcClient->top + 1,
|
|
prcClient->left + BTN_WIDTH_BMP,
|
|
prcClient->bottom - 1);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* calc DrawText format from button style
|
|
*/
|
|
|
|
static int btnGetTextFmt (int dwStyle)
|
|
{
|
|
UINT dt_fmt;
|
|
if (dwStyle & BS_MULTLINE)
|
|
dt_fmt = DT_WORDBREAK;
|
|
else
|
|
dt_fmt = DT_SINGLELINE;
|
|
|
|
if ((dwStyle & BS_TYPEMASK) == BS_PUSHBUTTON
|
|
|| (dwStyle & BS_TYPEMASK) == BS_DEFPUSHBUTTON
|
|
|| (dwStyle & BS_PUSHLIKE))
|
|
dt_fmt |= DT_CENTER | DT_VCENTER;
|
|
else {
|
|
if ((dwStyle & BS_ALIGNMASK) == BS_RIGHT)
|
|
dt_fmt = DT_WORDBREAK | DT_RIGHT;
|
|
else if ((dwStyle & BS_ALIGNMASK) == BS_CENTER)
|
|
dt_fmt = DT_WORDBREAK | DT_CENTER;
|
|
else dt_fmt = DT_WORDBREAK | DT_LEFT;
|
|
|
|
if ((dwStyle & BS_ALIGNMASK) == BS_TOP)
|
|
dt_fmt |= DT_SINGLELINE | DT_TOP;
|
|
else if ((dwStyle & BS_ALIGNMASK) == BS_BOTTOM)
|
|
dt_fmt |= DT_SINGLELINE | DT_BOTTOM;
|
|
else dt_fmt |= DT_SINGLELINE | DT_VCENTER;
|
|
}
|
|
|
|
return dt_fmt;
|
|
}
|
|
|
|
/**
|
|
* draw bitmap content in button with BS_BITMAP style
|
|
*/
|
|
static void draw_bitmap_button (PCONTROL pctrl, HDC hdc, DWORD dwStyle,
|
|
RECT *prcText)
|
|
{
|
|
if (BUTTON_DATA(pctrl)) {
|
|
int x = prcText->left;
|
|
int y = prcText->top;
|
|
int w = RECTWP (prcText);
|
|
int h = RECTHP (prcText);
|
|
PBITMAP bmp = (PBITMAP)(BUTTON_DATA(pctrl));
|
|
RECT prcClient;
|
|
|
|
GetClientRect ((HWND)pctrl, &prcClient);
|
|
if (dwStyle & BS_REALSIZEIMAGE) {
|
|
x += (int)(w - bmp->bmWidth) >> 1;
|
|
y += (int)(h - bmp->bmHeight) >> 1;
|
|
w = h = 0;
|
|
|
|
if (bmp->bmWidth > RECTW(prcClient)){
|
|
x = prcClient.left + BTN_WIDTH_BORDER;
|
|
w = RECTW(prcClient) - 2*BTN_WIDTH_BORDER-1;
|
|
}
|
|
|
|
if (bmp->bmHeight > RECTH(prcClient)){
|
|
y = prcClient.top + BTN_WIDTH_BORDER;
|
|
h = RECTH(prcClient) - 2*BTN_WIDTH_BORDER-1;
|
|
}
|
|
|
|
}
|
|
else {
|
|
x = prcClient.top + BTN_WIDTH_BORDER;
|
|
y = prcClient.left + BTN_WIDTH_BORDER;
|
|
w = RECTW (prcClient) - 2*BTN_WIDTH_BORDER-1;
|
|
h = RECTH (prcClient) - 2*BTN_WIDTH_BORDER-1;
|
|
}
|
|
|
|
FillBoxWithBitmap (hdc, x, y, w, h, bmp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* draw bitmap content in button with BS_ICON style
|
|
*/
|
|
static void draw_icon_button(PCONTROL pctrl, HDC hdc, DWORD dwStyle, RECT *prcText)
|
|
{
|
|
if (BUTTON_DATA(pctrl)) {
|
|
int x = prcText->left;
|
|
int y = prcText->top;
|
|
int w = RECTWP (prcText);
|
|
int h = RECTHP (prcText);
|
|
HICON icon = (HICON)(BUTTON_DATA(pctrl));
|
|
RECT prcClient;
|
|
|
|
if (dwStyle & BS_REALSIZEIMAGE) {
|
|
int iconw, iconh;
|
|
|
|
GetIconSize (icon, &iconw, &iconh);
|
|
x += (w - iconw) >> 1;
|
|
y += (h - iconh) >> 1;
|
|
w = h = 0;
|
|
}
|
|
else {
|
|
GetClientRect ((HWND)pctrl, &prcClient);
|
|
x = prcClient.top;
|
|
y = prcClient.left;
|
|
w = RECTW (prcClient);
|
|
h = RECTH (prcClient);
|
|
}
|
|
|
|
DrawIcon (hdc, x, y, w, h, icon);
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* paint_content_focus:
|
|
* draw content in button and focusRect (if it have focus)
|
|
* Author: XuguangWang
|
|
* Date: 2007-12-7
|
|
*/
|
|
static void paint_content_focus(HDC hdc, PCONTROL pctrl, RECT* prc_cont)
|
|
{
|
|
DWORD dt_fmt = 0;
|
|
DWORD fg_color = 0xFFFF;
|
|
DWORD text_pixel;
|
|
BOOL is_get_fg = FALSE;
|
|
BOOL is_get_fmt = FALSE;
|
|
gal_pixel old_pixel;
|
|
RECT focus_rc;
|
|
const WINDOW_ELEMENT_RENDERER* win_rdr;
|
|
|
|
/* Not used vars.
|
|
int status, type;
|
|
status = BUTTON_STATUS (pctrl);
|
|
type = BUTTON_TYPE (pctrl);
|
|
*/
|
|
win_rdr = GetWindowInfo((HWND)pctrl)->we_rdr;
|
|
|
|
/*draw button content*/
|
|
switch (pctrl->dwStyle & BS_CONTENTMASK)
|
|
{
|
|
case BS_BITMAP:
|
|
draw_bitmap_button(pctrl, hdc, pctrl->dwStyle, prc_cont);
|
|
break;
|
|
case BS_ICON:
|
|
draw_icon_button(pctrl, hdc, pctrl->dwStyle, prc_cont);
|
|
break;
|
|
default:
|
|
fg_color = GetWindowElementAttr((HWND)pctrl, WE_FGC_THREED_BODY);
|
|
|
|
is_get_fg = TRUE;
|
|
text_pixel = RGBA2Pixel(hdc, GetRValue(fg_color),
|
|
GetGValue(fg_color), GetBValue(fg_color),
|
|
GetAValue(fg_color));
|
|
|
|
old_pixel = SetTextColor(hdc, text_pixel);
|
|
dt_fmt = btnGetTextFmt(pctrl->dwStyle);
|
|
is_get_fmt = TRUE;
|
|
SetBkMode (hdc, BM_TRANSPARENT);
|
|
DrawText (hdc, pctrl->spCaption, -1, prc_cont, dt_fmt);
|
|
SetTextColor(hdc, old_pixel);
|
|
|
|
/*disable draw text*/
|
|
if ((BUTTON_GET_POSE(pctrl) == BST_DISABLE)
|
|
| (pctrl->dwStyle & WS_DISABLED))
|
|
win_rdr->disabled_text_out ((HWND)pctrl, hdc, pctrl->spCaption,
|
|
prc_cont, dt_fmt);
|
|
}
|
|
|
|
/*draw focus frame*/
|
|
if (BUTTON_STATUS(pctrl) & BST_FOCUS)
|
|
{
|
|
focus_rc = *prc_cont;
|
|
|
|
if (!BUTTON_IS_PUSHBTN(pctrl) &&
|
|
!(pctrl->dwStyle & BS_PUSHLIKE) &&
|
|
is_get_fmt)
|
|
{
|
|
dt_fmt |= DT_CALCRECT;
|
|
DrawText (hdc, pctrl->spCaption, -1, &focus_rc, dt_fmt);
|
|
}
|
|
|
|
if (!is_get_fg)
|
|
fg_color = GetWindowElementAttr((HWND)pctrl, WE_FGC_THREED_BODY);
|
|
win_rdr->draw_focus_frame(hdc, &focus_rc, fg_color);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* paint_push_btn:
|
|
* draw push or pushlike button
|
|
* Author: XuguangWang
|
|
* Date: 2007-12-7
|
|
*/
|
|
static void paint_push_btn (HDC hdc, PCONTROL pctrl)
|
|
{
|
|
const WINDOW_ELEMENT_RENDERER* win_rdr;
|
|
DWORD main_color;
|
|
|
|
RECT rcClient;
|
|
RECT rcContent;
|
|
RECT rcBitmap;
|
|
win_rdr = GetWindowInfo((HWND)pctrl)->we_rdr;
|
|
main_color = GetWindowElementAttr((HWND)pctrl, WE_MAINC_THREED_BODY);
|
|
|
|
btnGetRects (pctrl, &rcClient, &rcContent, &rcBitmap);
|
|
win_rdr->draw_push_button((HWND)pctrl, hdc, &rcClient, main_color,
|
|
0xFFFFFFFF, BUTTON_STATUS(pctrl));
|
|
|
|
paint_content_focus(hdc, pctrl, &rcContent);
|
|
}
|
|
|
|
|
|
#if 1
|
|
/*a matrix from button control status to renderer status*/
|
|
static int ctrlst_rdrst[12] = {
|
|
/*unckecked*/
|
|
LFRDR_BTN_STATUS_NORMAL,
|
|
LFRDR_BTN_STATUS_HILITE,
|
|
LFRDR_BTN_STATUS_PRESSED,
|
|
LFRDR_BTN_STATUS_DISABLED,
|
|
/*checked*/
|
|
LFRDR_BTN_STATUS_NORMAL|LFRDR_BTN_STATUS_SELECTED,
|
|
LFRDR_BTN_STATUS_HILITE|LFRDR_BTN_STATUS_SELECTED,
|
|
LFRDR_BTN_STATUS_PRESSED|LFRDR_BTN_STATUS_SELECTED,
|
|
LFRDR_BTN_STATUS_DISABLED|LFRDR_BTN_STATUS_SELECTED,
|
|
/*indeterminate*/
|
|
LFRDR_BTN_STATUS_DISABLED,
|
|
LFRDR_BTN_STATUS_DISABLED,
|
|
LFRDR_BTN_STATUS_DISABLED,
|
|
LFRDR_BTN_STATUS_DISABLED,
|
|
};
|
|
|
|
#else
|
|
static int ctrlst_rdrst[12] = {
|
|
/*unckecked*/
|
|
BST_NORMAL,
|
|
BST_HILITE,
|
|
BST_PUSHED,
|
|
BST_DISABLE,
|
|
/*checked*/
|
|
BST_NORMAL | BST_CHECKED,
|
|
BST_HILITE | BST_CHECKED,
|
|
BST_PUSHED | BST_CHECKED,
|
|
BST_DISABLE | BST_CHECKED,
|
|
/*indeterminate*/
|
|
BST_DISABLE,
|
|
BST_DISABLE,
|
|
BST_DISABLE,
|
|
BST_DISABLE,
|
|
};
|
|
#endif
|
|
/**
|
|
* paint_check_radio_btn:
|
|
* draw check button, radio button and 3state button
|
|
* Author: XuguangWang
|
|
* Date: 2007-12-7
|
|
*/
|
|
static void paint_check_radio_btn (HDC hdc, PCONTROL pctrl)
|
|
{
|
|
const WINDOW_ELEMENT_RENDERER* win_rdr;
|
|
int rdr_st;
|
|
|
|
RECT rcClient;
|
|
RECT rcContent;
|
|
RECT rcBitmap;
|
|
|
|
win_rdr = GetWindowInfo((HWND)pctrl)->we_rdr;
|
|
|
|
/* Not used vars
|
|
gal_pixel text_pixel;
|
|
text_pixel = GetWindowElementPixelEx((HWND)pctrl, hdc, WE_FGC_WINDOW);
|
|
*/
|
|
|
|
rdr_st = ctrlst_rdrst [ BUTTON_STATUS (pctrl) &
|
|
(BST_POSE_MASK|BST_CHECK_MASK) ];
|
|
btnGetRects (pctrl, &rcClient, &rcContent, &rcBitmap);
|
|
|
|
if (BUTTON_IS_RADIOBTN(pctrl))
|
|
win_rdr->draw_radio_button((HWND)pctrl, hdc, &rcBitmap, rdr_st);
|
|
else
|
|
win_rdr->draw_check_button((HWND)pctrl, hdc, &rcBitmap, rdr_st);
|
|
|
|
/* DK: just draw text after background has been erased.*/
|
|
paint_content_focus(hdc, pctrl, &rcContent);
|
|
}
|
|
|
|
|
|
|
|
BOOL RegisterButtonControl (void)
|
|
{
|
|
WNDCLASS WndClass;
|
|
|
|
WndClass.spClassName = CTRL_BUTTON;
|
|
WndClass.dwStyle = WS_NONE;
|
|
WndClass.dwExStyle = WS_EX_NONE;
|
|
WndClass.hCursor = GetSystemCursor (IDC_ARROW);
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
WndClass.dwBkColor =
|
|
GetWindowElementAttr (HWND_NULL, WE_MAINC_THREED_BODY);
|
|
#else
|
|
WndClass.iBkColor =
|
|
GetWindowElementPixelEx (HWND_NULL, HDC_SCREEN, WE_MAINC_THREED_BODY);
|
|
#endif
|
|
WndClass.WinProc = ButtonCtrlProc;
|
|
|
|
return gui_AddNewControlClass (&WndClass) == ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* mouse_in_client:
|
|
* calc if mouse is in client
|
|
* Author: XuguangWang
|
|
* Date: 2007-12-7
|
|
*/
|
|
static inline BOOL mouse_in_client(LPARAM lParam, PCONTROL pctrl)
|
|
{
|
|
int x, y;
|
|
x = LOSWORD(lParam);
|
|
y = HISWORD(lParam);
|
|
ScreenToClient (GetParent ((HWND)pctrl), &x, &y);
|
|
return (PtInRect ((PRECT) &(pctrl->cl), x, y));
|
|
}
|
|
|
|
static LRESULT ButtonCtrlProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hdc;
|
|
PCONTROL pctrl;
|
|
DWORD dwStyle;
|
|
PBUTTONDATA pData;
|
|
|
|
pctrl = gui_Control(hWnd);
|
|
dwStyle = pctrl->dwStyle;
|
|
|
|
switch(uMsg)
|
|
{
|
|
case MSG_CREATE:
|
|
pData = (BUTTONDATA*) calloc (1, sizeof(BUTTONDATA));
|
|
SetWindowBkColor (hWnd, GetWindowElementPixelEx (hWnd,
|
|
HDC_INVALID, WE_MAINC_THREED_BODY));
|
|
if (pData == NULL)
|
|
return -1;
|
|
|
|
pData->status = 0;
|
|
pData->data = pctrl->dwAddData;
|
|
pctrl->dwAddData2 = (DWORD) pData;
|
|
|
|
if (!BUTTON_IS_PUSHBTN(pctrl) && dwStyle & BS_CHECKED)
|
|
BUTTON_SET_CHECK(pctrl, BST_CHECKED);
|
|
|
|
break;
|
|
|
|
case MSG_DESTROY:
|
|
free ((void*) pctrl->dwAddData2);
|
|
break;
|
|
|
|
case BM_GETCHECK:
|
|
if (!BUTTON_IS_PUSHBTN(pctrl))
|
|
return BUTTON_GET_CHECK(pctrl);
|
|
else
|
|
return -1;
|
|
|
|
case BM_SETCHECK:
|
|
{
|
|
int set_ret;
|
|
UINT old_check;
|
|
// MG_CHECK_RET(BUTTON_IS_PUSHBTN(pctrl), -1);
|
|
old_check = BUTTON_GET_CHECK(pctrl);
|
|
set_ret = change_checked(pctrl,wParam);
|
|
if (set_ret == 1)
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
|
|
return old_check;
|
|
}
|
|
|
|
case BM_GETSTATE:
|
|
return (int)(BUTTON_GET_POSE(pctrl));
|
|
|
|
case BM_SETSTATE:
|
|
{
|
|
DWORD old_pose = BUTTON_GET_POSE(pctrl);
|
|
|
|
if (wParam)
|
|
BUTTON_SET_POSE(pctrl, BST_PUSHED);
|
|
else
|
|
BUTTON_SET_POSE(pctrl, BST_NORMAL);
|
|
|
|
if (old_pose != BUTTON_GET_POSE(pctrl))
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
|
|
return old_pose;
|
|
}
|
|
|
|
case BM_ENABLE:
|
|
{
|
|
if (wParam == FALSE)
|
|
{
|
|
/*disable a button*/
|
|
MG_CHECK_RET (BUTTON_GET_POSE(pctrl) != BST_DISABLE, 0);
|
|
change_status_from_pose (pctrl, BST_DISABLE, TRUE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
/*enable a button*/
|
|
MG_CHECK_RET (BUTTON_GET_POSE(pctrl) == BST_DISABLE, 0);
|
|
BUTTON_SET_POSE (pctrl, BST_NORMAL);
|
|
NotifyParent(hWnd, pctrl->id, BN_ENABLE);
|
|
}
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
return 0;
|
|
}
|
|
|
|
case BM_GETIMAGE:
|
|
{
|
|
int* image_type = (int*) wParam;
|
|
|
|
if (dwStyle & BS_BITMAP) {
|
|
if (image_type) {
|
|
*image_type = BM_IMAGE_BITMAP;
|
|
}
|
|
return (int)(BUTTON_DATA (pctrl));
|
|
}
|
|
else if (dwStyle & BS_ICON) {
|
|
if (image_type) {
|
|
*image_type = BM_IMAGE_ICON;
|
|
}
|
|
return (int)(BUTTON_DATA (pctrl));
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case BM_SETIMAGE:
|
|
{
|
|
int oldImage = (int)BUTTON_DATA (pctrl);
|
|
|
|
if (wParam == BM_IMAGE_BITMAP) {
|
|
pctrl->dwStyle &= ~BS_ICON;
|
|
pctrl->dwStyle |= BS_BITMAP;
|
|
}
|
|
else if (wParam == BM_IMAGE_ICON){
|
|
pctrl->dwStyle &= ~BS_BITMAP;
|
|
pctrl->dwStyle |= BS_ICON;
|
|
}
|
|
|
|
if (lParam) {
|
|
BUTTON_DATA (pctrl) = (DWORD)lParam;
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
}
|
|
|
|
return oldImage;
|
|
}
|
|
break;
|
|
|
|
case BM_CLICK:
|
|
{
|
|
int new_check;
|
|
if (BUTTON_IS_AUTO(pctrl))
|
|
{
|
|
new_check = switch_check(pctrl);
|
|
change_checked(pctrl, new_check);
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
}
|
|
|
|
NotifyParent (hWnd, pctrl->id, BN_CLICKED);
|
|
}
|
|
break;
|
|
|
|
case MSG_SETTEXT:
|
|
{
|
|
int len = strlen((char*)lParam);
|
|
FreeFixStr (pctrl->spCaption);
|
|
pctrl->spCaption = FixStrAlloc (len);
|
|
if (len > 0)
|
|
strcpy (pctrl->spCaption, (char*)lParam);
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
break;
|
|
}
|
|
|
|
case BM_SETSTYLE:
|
|
pctrl->dwStyle &= 0xFFFF0000;
|
|
pctrl->dwStyle |= (DWORD)(wParam & 0x0000FFFF);
|
|
if (LOWORD (lParam))
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
break;
|
|
|
|
case MSG_CHAR:
|
|
if (HIBYTE (wParam)==0 && BUTTON_IS_CHECKBTN (pctrl)) {
|
|
int old_check = BUTTON_GET_CHECK(pctrl);
|
|
|
|
if (LOBYTE(wParam) == '+' || LOBYTE(wParam) == '=')
|
|
BUTTON_SET_CHECK(pctrl, BST_CHECKED);
|
|
else if (LOBYTE(wParam) == '-')
|
|
BUTTON_SET_CHECK(pctrl, BST_UNCHECKED);
|
|
|
|
if (old_check != BUTTON_GET_CHECK(pctrl))
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
}
|
|
break;
|
|
|
|
case MSG_ENABLE:
|
|
if (wParam && (dwStyle & WS_DISABLED))
|
|
pctrl->dwStyle &= ~WS_DISABLED;
|
|
else if (!wParam && !(dwStyle & WS_DISABLED))
|
|
pctrl->dwStyle |= WS_DISABLED;
|
|
else
|
|
return 0;
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
return 0;
|
|
|
|
case MSG_GETDLGCODE:
|
|
switch (dwStyle & BS_TYPEMASK) {
|
|
case BS_CHECKBOX:
|
|
case BS_AUTOCHECKBOX:
|
|
return DLGC_WANTCHARS | DLGC_BUTTON;
|
|
|
|
case BS_RADIOBUTTON:
|
|
case BS_AUTORADIOBUTTON:
|
|
return DLGC_RADIOBUTTON | DLGC_BUTTON;
|
|
|
|
case BS_DEFPUSHBUTTON:
|
|
return DLGC_DEFPUSHBUTTON;
|
|
|
|
case BS_PUSHBUTTON:
|
|
return DLGC_PUSHBUTTON;
|
|
|
|
case BS_3STATE:
|
|
case BS_AUTO3STATE:
|
|
return DLGC_3STATE;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case MSG_NCHITTEST:
|
|
{
|
|
int x, y;
|
|
|
|
x = (int)wParam;
|
|
y = (int)lParam;
|
|
|
|
if (PtInRect ((PRECT) &(pctrl->cl), x, y))
|
|
return HT_CLIENT;
|
|
else
|
|
return HT_OUT;
|
|
}
|
|
break;
|
|
|
|
case MSG_KILLFOCUS:
|
|
BUTTON_STATUS(pctrl) &= (~BST_FOCUS);
|
|
if (GetCapture() == hWnd) {
|
|
ReleaseCapture ();
|
|
|
|
BUTTON_SET_POSE(pctrl, BST_NORMAL);
|
|
}
|
|
|
|
if (dwStyle & BS_NOTIFY)
|
|
NotifyParent (hWnd, pctrl->id, BN_KILLFOCUS);
|
|
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
break;
|
|
|
|
case MSG_SETFOCUS:
|
|
BUTTON_STATUS(pctrl) |= BST_FOCUS;
|
|
|
|
if (dwStyle & BS_NOTIFY)
|
|
NotifyParent (hWnd, pctrl->id, BN_SETFOCUS);
|
|
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
break;
|
|
|
|
case MSG_KEYDOWN:
|
|
if (wParam != SCANCODE_SPACE
|
|
&& wParam != SCANCODE_ENTER
|
|
&& wParam != SCANCODE_KEYPADENTER)
|
|
break;
|
|
|
|
if (GetCapture () == hWnd)
|
|
break;
|
|
|
|
SetCapture (hWnd);
|
|
//FIXME not needed
|
|
//BUTTON_STATUS(pctrl) |= BST_FOCUS;
|
|
change_status_from_pose(pctrl, BST_PUSHED, TRUE, TRUE);
|
|
|
|
break;
|
|
|
|
case MSG_KEYUP:
|
|
{
|
|
if (wParam != SCANCODE_SPACE
|
|
&& wParam != SCANCODE_ENTER
|
|
&& wParam != SCANCODE_KEYPADENTER)
|
|
break;
|
|
|
|
if (GetCapture () == hWnd)
|
|
{
|
|
ReleaseCapture ();
|
|
change_status_from_pose(pctrl, BST_NORMAL, TRUE, TRUE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MSG_LBUTTONDBLCLK:
|
|
if (dwStyle & BS_NOTIFY &&
|
|
BUTTON_GET_POSE(pctrl) != BST_DISABLE)
|
|
NotifyParent (hWnd, pctrl->id, BN_DBLCLK);
|
|
break;
|
|
|
|
case MSG_LBUTTONDOWN:
|
|
if (GetCapture () == hWnd)
|
|
break;
|
|
|
|
SetCapture (hWnd);
|
|
|
|
BUTTON_STATUS(pctrl) |= BST_FOCUS;
|
|
change_status_from_pose(pctrl, BST_PUSHED, TRUE, TRUE);
|
|
break;
|
|
|
|
case MSG_LBUTTONUP:
|
|
{
|
|
if (GetCapture() == hWnd)
|
|
{
|
|
ReleaseCapture ();
|
|
|
|
if (mouse_in_client(lParam, pctrl))
|
|
change_status_from_pose(pctrl, BST_HILITE, TRUE, TRUE);
|
|
else
|
|
change_status_from_pose(pctrl, BST_NORMAL, TRUE, FALSE);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case MSG_MOUSEMOVEIN:
|
|
{
|
|
/*leave a button, nomralize it*/
|
|
if (!wParam)
|
|
change_status_from_pose(pctrl, BST_NORMAL, TRUE, FALSE);
|
|
|
|
/*because if lbutton downed on space out of the button,
|
|
* and then move to it, it will receive MSG_MOUSEMOVEIN,
|
|
* but it should not be hilited by MSG_MOUSEMOVEIN(wParam == TRUE)
|
|
* */
|
|
break;
|
|
}
|
|
|
|
case MSG_MOUSEMOVE:
|
|
{
|
|
/*left button is not downed*/
|
|
if (!(wParam & KS_LEFTBUTTON))
|
|
{
|
|
/*mouse not captured by keydowning on other button*/
|
|
if (GetCapture()==HWND_NULL)
|
|
change_status_from_pose(pctrl, BST_HILITE, TRUE, FALSE);
|
|
|
|
break;
|
|
}
|
|
|
|
/*the button capture mouse by mouse*/
|
|
if (GetCapture() == hWnd)
|
|
{
|
|
/*normal -- pushed, while lbutton is being downed*/
|
|
if (mouse_in_client(lParam, pctrl))
|
|
change_status_from_pose(pctrl, BST_PUSHED, FALSE, FALSE);
|
|
else
|
|
change_status_from_pose(pctrl, BST_NORMAL, FALSE, FALSE);
|
|
}
|
|
break;
|
|
}
|
|
case MSG_PAINT:
|
|
{
|
|
hdc = BeginPaint (hWnd);
|
|
SelectFont (hdc, GetWindowFont(hWnd));
|
|
|
|
if (BUTTON_IS_PUSHBTN(pctrl) || (pctrl->dwStyle & BS_PUSHLIKE))
|
|
paint_push_btn(hdc, pctrl);
|
|
else
|
|
paint_check_radio_btn(hdc, pctrl);
|
|
|
|
EndPaint (hWnd, hdc);
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return DefaultControlProc (hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
#endif /* _MGCTRL_BUTTON */
|
|
|