mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-02-09 04:57:05 +08:00
1198 lines
33 KiB
C
1198 lines
33 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/>.
|
|
*/
|
|
/*
|
|
** dialog.c: The Dialog module.
|
|
**
|
|
** Create date: 1999.09.10
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "common.h"
|
|
#include "minigui.h"
|
|
#include "gdi.h"
|
|
#include "window.h"
|
|
#include "control.h"
|
|
|
|
#include "cliprect.h"
|
|
#include "gal.h"
|
|
#include "internals.h"
|
|
#include "ctrlclass.h"
|
|
|
|
void GUIAPI DestroyAllControls (HWND hDlg)
|
|
{
|
|
PMAINWIN pDlg = (PMAINWIN)hDlg;
|
|
PCONTROL pCtrl = (PCONTROL)(pDlg->hFirstChild);
|
|
PCONTROL pNext;
|
|
|
|
while (pCtrl) {
|
|
pNext = pCtrl->next;
|
|
DestroyWindow ((HWND)pCtrl);
|
|
pCtrl = pNext;
|
|
}
|
|
}
|
|
|
|
#define dlgDestroyAllControls DestroyAllControls
|
|
|
|
HWND GUIAPI CreateMainWindowIndirectParamEx (PDLGTEMPLATE pDlgTemplate,
|
|
HWND hOwner, WNDPROC WndProc, LPARAM lParam,
|
|
const char* werdr_name, WINDOW_ELEMENT_ATTR* we_attrs,
|
|
const char* window_name, const char* layer_name)
|
|
{
|
|
MAINWINCREATE CreateInfo;
|
|
HWND hMainWin;
|
|
int i;
|
|
PCTRLDATA pCtrlData;
|
|
HWND hCtrl;
|
|
HWND hFocus;
|
|
|
|
if (pDlgTemplate->controlnr > 0 && !pDlgTemplate->controls)
|
|
return HWND_INVALID;
|
|
|
|
hOwner = GetMainWindowHandle (hOwner);
|
|
|
|
CreateInfo.dwReserved = (DWORD)pDlgTemplate;
|
|
|
|
CreateInfo.dwStyle = pDlgTemplate->dwStyle & ~WS_VISIBLE;
|
|
CreateInfo.dwExStyle = pDlgTemplate->dwExStyle;
|
|
CreateInfo.spCaption = pDlgTemplate->caption;
|
|
CreateInfo.hMenu = pDlgTemplate->hMenu;
|
|
CreateInfo.hCursor = GetSystemCursor (IDC_ARROW);
|
|
CreateInfo.hIcon = pDlgTemplate->hIcon;
|
|
CreateInfo.MainWindowProc = WndProc ? WndProc : DefaultMainWinProc;
|
|
CreateInfo.lx = pDlgTemplate->x;
|
|
CreateInfo.ty = pDlgTemplate->y;
|
|
CreateInfo.rx = pDlgTemplate->x + pDlgTemplate->w;
|
|
CreateInfo.by = pDlgTemplate->y + pDlgTemplate->h;
|
|
CreateInfo.iBkColor =
|
|
GetWindowElementPixelEx (HWND_NULL, HDC_SCREEN, WE_MAINC_THREED_BODY);
|
|
CreateInfo.dwAddData = pDlgTemplate->dwAddData;
|
|
CreateInfo.hHosting = hOwner;
|
|
|
|
|
|
hMainWin = CreateMainWindowEx (&CreateInfo,
|
|
werdr_name, we_attrs, window_name, layer_name);
|
|
|
|
if (hMainWin == HWND_INVALID)
|
|
return HWND_INVALID;
|
|
|
|
for (i = 0; i < pDlgTemplate->controlnr; i++) {
|
|
pCtrlData = pDlgTemplate->controls + i;
|
|
hCtrl = CreateWindowEx2 (pCtrlData->class_name,
|
|
pCtrlData->caption,
|
|
pCtrlData->dwStyle | WS_CHILD,
|
|
pCtrlData->dwExStyle,
|
|
pCtrlData->id,
|
|
pCtrlData->x,
|
|
pCtrlData->y,
|
|
pCtrlData->w,
|
|
pCtrlData->h,
|
|
hMainWin,
|
|
pCtrlData->werdr_name,
|
|
pCtrlData->we_attrs,
|
|
pCtrlData->dwAddData);
|
|
|
|
if (hCtrl == HWND_INVALID) {
|
|
DestroyMainWindow (hMainWin);
|
|
MainWindowThreadCleanup (hMainWin);
|
|
return HWND_INVALID;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
hFocus = GetNextDlgTabItem (hMainWin, (HWND)0, FALSE);
|
|
#else
|
|
/* houhh 20100706, set the forefront control as focus. */
|
|
{
|
|
PCONTROL pCtrl;
|
|
for (pCtrl = (PCONTROL)(((PMAINWIN)hMainWin)->hFirstChild);
|
|
pCtrl && pCtrl->next; pCtrl = pCtrl->next);
|
|
hFocus = (HWND)pCtrl;
|
|
}
|
|
#endif
|
|
if (SendMessage (hMainWin, MSG_INITDIALOG, (WPARAM)hFocus, lParam)) {
|
|
if (hFocus)
|
|
SetFocus (hFocus);
|
|
}
|
|
if (!(pDlgTemplate->dwExStyle & WS_EX_DLGHIDE))
|
|
ShowWindow (hMainWin, SW_SHOWNORMAL);
|
|
|
|
return hMainWin;
|
|
}
|
|
|
|
BOOL GUIAPI DestroyMainWindowIndirect (HWND hMainWin)
|
|
{
|
|
DestroyMainWindow (hMainWin);
|
|
MainWindowThreadCleanup (hMainWin);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int GUIAPI DialogBoxIndirectParamEx (PDLGTEMPLATE pDlgTemplate,
|
|
HWND hOwner, WNDPROC DlgProc, LPARAM lParam,
|
|
const char* werdr_name, WINDOW_ELEMENT_ATTR* we_attrs,
|
|
const char* window_name, const char* layer_name)
|
|
{
|
|
HWND hDlg;
|
|
int retCode = IDCANCEL;
|
|
MSG Msg;
|
|
|
|
if (hOwner && hOwner != HWND_INVALID && hOwner != HWND_DESKTOP)
|
|
hOwner = GetMainWindowHandle(hOwner);
|
|
|
|
/* Since 5.0.0: use internal extended style for dialgbox */
|
|
pDlgTemplate->dwExStyle |= WS_EX_DIALOGBOX;
|
|
|
|
hDlg = CreateMainWindowIndirectParamEx(pDlgTemplate,
|
|
hOwner, DlgProc, lParam,
|
|
werdr_name, we_attrs,
|
|
window_name, layer_name);
|
|
|
|
if (hDlg == HWND_INVALID)
|
|
return -1;
|
|
|
|
//MiniGUI maybe change dialog owner in CreateMainWindow, so we
|
|
//should update its owner by GetHosting.
|
|
hOwner = GetHosting (hDlg);
|
|
SetWindowAdditionalData2 (hDlg, (DWORD)(&retCode));
|
|
|
|
if (hOwner && hOwner != HWND_DESKTOP) {
|
|
if (IsWindowEnabled (hOwner)) {
|
|
EnableWindow (hOwner, FALSE);
|
|
IncludeWindowExStyle (hOwner, WS_EX_MODALDISABLED);
|
|
}
|
|
/* Throw away MSG_KEYDOWN, MSG_CHAR and MSG_KEYUP to hOwner */
|
|
while (PeekPostMessage (&Msg, hOwner,
|
|
MSG_KEYDOWN, MSG_KEYUP, PM_REMOVE));
|
|
}
|
|
|
|
while (GetMessage (&Msg, hDlg)) {
|
|
TranslateMessage (&Msg);
|
|
DispatchMessage (&Msg);
|
|
}
|
|
|
|
MainWindowCleanup (hDlg);
|
|
|
|
return retCode;
|
|
}
|
|
|
|
BOOL GUIAPI EndDialog (HWND hDlg, int endCode)
|
|
{
|
|
HWND hOwner;
|
|
BOOL isActive;
|
|
DWORD dwAddData2;
|
|
|
|
dwAddData2 = GetWindowAdditionalData2 (hDlg);
|
|
|
|
if (dwAddData2 == 0)
|
|
return FALSE;
|
|
|
|
*((int *)(dwAddData2)) = endCode;
|
|
|
|
hOwner = GetHosting (hDlg);
|
|
isActive = (GetActiveWindow() == hDlg);
|
|
|
|
DestroyMainWindow (hDlg);
|
|
|
|
if (hOwner != HWND_DESKTOP && hOwner != HWND_NULL) {
|
|
if (GetWindowExStyle (hOwner) & WS_EX_MODALDISABLED) {
|
|
EnableWindow (hOwner, TRUE);
|
|
ExcludeWindowExStyle (hOwner, WS_EX_MODALDISABLED);
|
|
if (isActive && IsWindowVisible (hOwner)) {
|
|
ShowWindow (hOwner, SW_SHOWNORMAL);
|
|
SetActiveWindow (hOwner);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT GUIAPI PreDefDialogProc (HWND hWnd, UINT message,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HWND hCurFocus;
|
|
|
|
switch (message) {
|
|
#if 0
|
|
case MSG_CREATE:
|
|
{
|
|
int i;
|
|
PCTRLDATA pCtrlData;
|
|
HWND hCtrl;
|
|
|
|
PDLGTEMPLATE pDlgTmpl
|
|
= (PDLGTEMPLATE)(((PMAINWINCREATE)lParam)->dwReserved);
|
|
|
|
for (i = 0; i < pDlgTmpl->controlnr; i++) {
|
|
pCtrlData = pDlgTmpl->controls + i;
|
|
if (pCtrlData->class_name) {
|
|
hCtrl = CreateWindowEx2 (pCtrlData->class_name,
|
|
pCtrlData->caption,
|
|
pCtrlData->dwStyle | WS_CHILD,
|
|
pCtrlData->dwExStyle,
|
|
pCtrlData->id,
|
|
pCtrlData->x,
|
|
pCtrlData->y,
|
|
pCtrlData->w,
|
|
pCtrlData->h,
|
|
hWnd,
|
|
pCtrlData->werdr_name,
|
|
pCtrlData->we_attrs,
|
|
pCtrlData->dwAddData);
|
|
}
|
|
else
|
|
break;
|
|
|
|
if (hCtrl == HWND_INVALID) {
|
|
dlgDestroyAllControls (hWnd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
case MSG_DLG_GETDEFID:
|
|
{
|
|
HWND hDef;
|
|
|
|
hDef = GetDlgDefPushButton (hWnd);
|
|
if (hDef)
|
|
return GetDlgCtrlID (hDef);
|
|
return 0;
|
|
}
|
|
|
|
case MSG_DLG_SETDEFID:
|
|
{
|
|
HWND hOldDef;
|
|
HWND hNewDef;
|
|
|
|
hNewDef = GetDlgItem (hWnd, wParam);
|
|
if (SendMessage (hNewDef, MSG_GETDLGCODE, 0, 0L) & DLGC_PUSHBUTTON) {
|
|
hOldDef = GetDlgDefPushButton (hWnd);
|
|
if (hOldDef) {
|
|
ExcludeWindowStyle (hOldDef, BS_DEFPUSHBUTTON);
|
|
InvalidateRect (hOldDef, NULL, TRUE);
|
|
}
|
|
IncludeWindowStyle (hNewDef, BS_DEFPUSHBUTTON);
|
|
InvalidateRect (hNewDef, NULL, TRUE);
|
|
|
|
return (LRESULT)hOldDef;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MSG_COMMAND:
|
|
if (wParam == IDCANCEL) {
|
|
HWND hCancel;
|
|
|
|
hCancel = GetDlgItem (hWnd, IDCANCEL);
|
|
if (hCancel && IsWindowEnabled (hCancel)
|
|
&& IsWindowVisible (hCancel))
|
|
EndDialog (hWnd, IDCANCEL);
|
|
}
|
|
break;
|
|
|
|
case MSG_CLOSE:
|
|
{
|
|
HWND hCancel;
|
|
|
|
hCancel = GetDlgItem (hWnd, IDCANCEL);
|
|
if (hCancel && IsWindowEnabled (hCancel)
|
|
&& IsWindowVisible (hCancel))
|
|
EndDialog (hWnd, IDCANCEL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case MSG_ISDIALOG:
|
|
return 1;
|
|
|
|
case MSG_KEYDOWN:
|
|
if ((hCurFocus = GetFocusChild (hWnd))
|
|
&& SendMessage (hCurFocus, MSG_GETDLGCODE, 0, 0L) &
|
|
DLGC_WANTALLKEYS)
|
|
break;
|
|
|
|
switch (wParam) {
|
|
case SCANCODE_ESCAPE:
|
|
SendMessage (hWnd, MSG_COMMAND, IDCANCEL, 0L);
|
|
return 0;
|
|
|
|
case SCANCODE_TAB:
|
|
{
|
|
HWND hNewFocus;
|
|
|
|
if (hCurFocus && SendMessage (hCurFocus, MSG_GETDLGCODE, 0, 0L) &
|
|
DLGC_WANTTAB)
|
|
break;
|
|
|
|
if (lParam & KS_SHIFT)
|
|
hNewFocus = GetNextDlgTabItem (hWnd, hCurFocus, TRUE);
|
|
else
|
|
hNewFocus = GetNextDlgTabItem (hWnd, hCurFocus, FALSE);
|
|
|
|
if (hNewFocus != hCurFocus) {
|
|
SetNullFocus (hCurFocus);
|
|
SetFocus (hNewFocus);
|
|
#if 0
|
|
SendMessage (hWnd, MSG_DLG_SETDEFID,
|
|
GetDlgCtrlID (hNewFocus), 0L);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
case SCANCODE_KEYPADENTER:
|
|
case SCANCODE_ENTER:
|
|
{
|
|
HWND hDef;
|
|
|
|
if (hCurFocus && SendMessage (hCurFocus, MSG_GETDLGCODE, 0, 0L) &
|
|
DLGC_WANTENTER)
|
|
break;
|
|
|
|
if (SendMessage (hCurFocus, MSG_GETDLGCODE, 0, 0L) &
|
|
DLGC_PUSHBUTTON)
|
|
break;
|
|
|
|
hDef = GetDlgDefPushButton (hWnd);
|
|
/* DK[07/05/10]Fix Bug4798, Check the control if has WS_DISABLED property. */
|
|
if (hDef && IsWindowEnabled(hDef)) {
|
|
SendMessage (hWnd, MSG_COMMAND, GetDlgCtrlID (hDef), 0L);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
case SCANCODE_CURSORBLOCKDOWN:
|
|
case SCANCODE_CURSORBLOCKRIGHT:
|
|
case SCANCODE_CURSORBLOCKUP:
|
|
case SCANCODE_CURSORBLOCKLEFT:
|
|
{
|
|
HWND hNewFocus;
|
|
|
|
if (hCurFocus && SendMessage (hCurFocus, MSG_GETDLGCODE, 0, 0L) &
|
|
DLGC_WANTARROWS)
|
|
break;
|
|
|
|
if (LOWORD (wParam) == SCANCODE_CURSORBLOCKDOWN
|
|
|| LOWORD (wParam) == SCANCODE_CURSORBLOCKRIGHT)
|
|
hNewFocus = GetNextDlgGroupItem (hWnd, hCurFocus, FALSE);
|
|
else
|
|
hNewFocus = GetNextDlgGroupItem (hWnd, hCurFocus, TRUE);
|
|
|
|
if (hNewFocus != hCurFocus) {
|
|
|
|
if (SendMessage (hCurFocus, MSG_GETDLGCODE, 0, 0L) &
|
|
DLGC_STATIC)
|
|
return 0;
|
|
|
|
SetFocus (hNewFocus);
|
|
#if 0
|
|
SendMessage (hWnd, MSG_DLG_SETDEFID,
|
|
GetDlgCtrlID (hNewFocus), 0L);
|
|
#endif
|
|
|
|
if (SendMessage (hNewFocus, MSG_GETDLGCODE, 0, 0L)
|
|
& DLGC_RADIOBUTTON) {
|
|
SendMessage (hNewFocus, BM_CLICK, 0, 0L);
|
|
ExcludeWindowStyle (hCurFocus, WS_TABSTOP);
|
|
IncludeWindowStyle (hNewFocus, WS_TABSTOP);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return DefaultMainWinProc (hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
LINT GUIAPI GetDlgCtrlID (HWND hwndCtl)
|
|
{
|
|
PCONTROL pCtrl = (PCONTROL)hwndCtl;
|
|
|
|
if (pCtrl) {
|
|
return pCtrl->id;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
HWND GUIAPI GetDlgItem (HWND hDlg, LINT nIDDlgItem)
|
|
{
|
|
PCONTROL pCtrl;
|
|
PMAINWIN pMainWin = (PMAINWIN)hDlg;
|
|
|
|
pCtrl = (PCONTROL)(pMainWin->hFirstChild);
|
|
|
|
while (pCtrl) {
|
|
if (pCtrl->id == nIDDlgItem)
|
|
return (HWND)pCtrl;
|
|
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
HWND GUIAPI GetDlgDefPushButton (HWND hWnd)
|
|
{
|
|
PCONTROL pCtrl;
|
|
PMAINWIN pMainWin = (PMAINWIN)hWnd;
|
|
|
|
pCtrl = (PCONTROL)(pMainWin->hFirstChild);
|
|
|
|
while (pCtrl) {
|
|
if (SendMessage ((HWND)pCtrl,
|
|
MSG_GETDLGCODE, 0, 0L) & DLGC_DEFPUSHBUTTON)
|
|
return (HWND)pCtrl;
|
|
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
HWND GetNextDlgGroupItem (HWND hDlg, HWND hCtl, BOOL bPrevious)
|
|
{
|
|
PCONTROL pStartCtrl, pCtrl;
|
|
PMAINWIN pMainWin = (PMAINWIN)hDlg;
|
|
|
|
if (hCtl)
|
|
pStartCtrl = (PCONTROL)hCtl;
|
|
else {
|
|
pStartCtrl = (PCONTROL)(pMainWin->hFirstChild);
|
|
if (!pStartCtrl)
|
|
return hCtl;
|
|
if (bPrevious) {
|
|
while (pStartCtrl->next) {
|
|
pStartCtrl = pStartCtrl->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bPrevious) {
|
|
if (pStartCtrl->dwStyle & WS_GROUP) {
|
|
if (pStartCtrl->next) {
|
|
pCtrl = pStartCtrl->next;
|
|
while (pCtrl && pCtrl->next) {
|
|
if (pCtrl->dwStyle & WS_GROUP)
|
|
break;
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
}
|
|
else
|
|
pCtrl = pStartCtrl;
|
|
}
|
|
else
|
|
pCtrl = pStartCtrl;
|
|
|
|
if (pCtrl->dwStyle & WS_GROUP || pCtrl == pStartCtrl)
|
|
pCtrl = pCtrl->prev;
|
|
|
|
while (pCtrl) {
|
|
if (pCtrl->dwStyle & WS_VISIBLE
|
|
&& !(pCtrl->dwStyle & WS_DISABLED) )
|
|
return (HWND)pCtrl;
|
|
|
|
pCtrl = pCtrl->prev;
|
|
}
|
|
}
|
|
else {
|
|
pCtrl = pStartCtrl->next;
|
|
while (pCtrl) {
|
|
if ( !(pCtrl->dwStyle & WS_GROUP) ) {
|
|
if (pCtrl->dwStyle & WS_VISIBLE
|
|
&& !(pCtrl->dwStyle & WS_DISABLED) )
|
|
return (HWND)pCtrl;
|
|
}
|
|
else
|
|
break;
|
|
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
|
|
pCtrl = pStartCtrl->prev;
|
|
while (pCtrl) {
|
|
if (pCtrl->dwStyle & WS_GROUP) {
|
|
if (pCtrl->dwStyle & WS_VISIBLE
|
|
&& !(pCtrl->dwStyle & WS_DISABLED) )
|
|
return (HWND)pCtrl;
|
|
else
|
|
break;
|
|
}
|
|
|
|
pCtrl = pCtrl->prev;
|
|
}
|
|
|
|
if (pCtrl) { // pCtrl is the first control in group.
|
|
pCtrl = pCtrl->next;
|
|
while (pCtrl) {
|
|
if ( !(pCtrl->dwStyle & WS_GROUP) ) {
|
|
if (pCtrl->dwStyle & WS_VISIBLE
|
|
&& !(pCtrl->dwStyle & WS_DISABLED) )
|
|
return (HWND)pCtrl;
|
|
}
|
|
else
|
|
break;
|
|
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hCtl;
|
|
}
|
|
|
|
HWND GUIAPI GetNextDlgTabItem (HWND hDlg, HWND hCtl, BOOL bPrevious)
|
|
{
|
|
PCONTROL pFirstCtrl, pLastCtrl, pCtrl;
|
|
PMAINWIN pMainWin = (PMAINWIN)hDlg;
|
|
|
|
pFirstCtrl = pCtrl = (PCONTROL)(pMainWin->hFirstChild);
|
|
if (!pCtrl) return hCtl;
|
|
|
|
while (pCtrl->next) {
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
pLastCtrl = pCtrl;
|
|
|
|
if (hCtl) {
|
|
pCtrl = (PCONTROL)hCtl;
|
|
if (bPrevious)
|
|
pCtrl = pCtrl->prev;
|
|
else
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
else if (bPrevious)
|
|
pCtrl = pLastCtrl;
|
|
else
|
|
pCtrl = pFirstCtrl;
|
|
|
|
if (bPrevious) {
|
|
while (pCtrl) {
|
|
if (pCtrl->dwStyle & WS_TABSTOP
|
|
&& pCtrl->dwStyle & WS_VISIBLE
|
|
&& !(pCtrl->dwStyle & WS_DISABLED) )
|
|
return (HWND)pCtrl;
|
|
|
|
pCtrl = pCtrl->prev;
|
|
}
|
|
|
|
pCtrl = pLastCtrl;
|
|
while (pCtrl) {
|
|
if (pCtrl->dwStyle & WS_TABSTOP
|
|
&& pCtrl->dwStyle & WS_VISIBLE
|
|
&& !(pCtrl->dwStyle & WS_DISABLED) )
|
|
return (HWND)pCtrl;
|
|
|
|
pCtrl = pCtrl->prev;
|
|
}
|
|
}
|
|
else {
|
|
while (pCtrl) {
|
|
if (pCtrl->dwStyle & WS_TABSTOP
|
|
&& pCtrl->dwStyle & WS_VISIBLE
|
|
&& !(pCtrl->dwStyle & WS_DISABLED) )
|
|
return (HWND)pCtrl;
|
|
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
|
|
pCtrl = pFirstCtrl;
|
|
while (pCtrl) {
|
|
if (pCtrl->dwStyle & WS_TABSTOP
|
|
&& pCtrl->dwStyle & WS_VISIBLE
|
|
&& !(pCtrl->dwStyle & WS_DISABLED) )
|
|
return (HWND)pCtrl;
|
|
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
}
|
|
|
|
return hCtl;
|
|
}
|
|
|
|
LRESULT GUIAPI SendDlgItemMessage (HWND hDlg, LINT nIDDlgItem,
|
|
UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HWND hCtrl;
|
|
|
|
hCtrl = GetDlgItem (hDlg, nIDDlgItem);
|
|
|
|
if (hCtrl)
|
|
return SendMessage (hCtrl, message, wParam, lParam);
|
|
|
|
return -1;
|
|
}
|
|
|
|
UINT GUIAPI GetDlgItemInt (HWND hDlg, LINT nIDDlgItem, BOOL *lpTranslated,
|
|
BOOL bSigned)
|
|
{
|
|
HWND hCtrl;
|
|
char buffer [101];
|
|
unsigned long int result;
|
|
|
|
if ( !(hCtrl = GetDlgItem (hDlg, nIDDlgItem)))
|
|
goto error;
|
|
|
|
if (SendMessage (hCtrl, MSG_GETTEXT, 100, (LPARAM)buffer) == 0)
|
|
goto error;
|
|
|
|
if (bSigned)
|
|
result = strtoul (buffer, NULL, 0);
|
|
else
|
|
result = (unsigned long int)strtol (buffer, NULL, 0);
|
|
|
|
if (lpTranslated)
|
|
*lpTranslated = TRUE;
|
|
|
|
return (UINT)result;
|
|
|
|
error:
|
|
if (lpTranslated)
|
|
*lpTranslated = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
BOOL GUIAPI SetDlgItemInt (HWND hDlg, LINT nIDDlgItem, UINT uValue,
|
|
BOOL bSigned)
|
|
{
|
|
HWND hCtrl;
|
|
char buffer [101];
|
|
|
|
if ( !(hCtrl = GetDlgItem (hDlg, nIDDlgItem)))
|
|
return FALSE;
|
|
|
|
if (bSigned)
|
|
snprintf (buffer, 100, "%ld", (long)uValue);
|
|
else
|
|
snprintf (buffer, 100, "%u", uValue);
|
|
buffer [100] = '\0';
|
|
|
|
return SendMessage (hCtrl, MSG_SETTEXT, 0, (LPARAM)buffer) == 0;
|
|
}
|
|
|
|
int GUIAPI GetDlgItemText (HWND hDlg, LINT nIDDlgItem, char* lpString,
|
|
int nMaxCount)
|
|
{
|
|
HWND hCtrl;
|
|
|
|
if ( !(hCtrl = GetDlgItem (hDlg, nIDDlgItem)))
|
|
return 0;
|
|
|
|
return SendMessage (hCtrl,
|
|
MSG_GETTEXT, (WPARAM)nMaxCount, (LPARAM)lpString);
|
|
}
|
|
|
|
char* GUIAPI GetDlgItemText2 (HWND hDlg, LINT id, int* lenPtr)
|
|
{
|
|
int len;
|
|
char* buff;
|
|
|
|
len = SendDlgItemMessage (hDlg, id,
|
|
MSG_GETTEXTLENGTH, 0, 0L);
|
|
if (len > 0) {
|
|
buff = malloc (len + 1);
|
|
if (buff)
|
|
SendDlgItemMessage (hDlg, id,
|
|
MSG_GETTEXT, len, (LPARAM)(buff));
|
|
}
|
|
else
|
|
buff = NULL;
|
|
|
|
if (lenPtr) *lenPtr = len;
|
|
|
|
return buff;
|
|
}
|
|
|
|
BOOL GUIAPI SetDlgItemText (HWND hDlg, LINT nIDDlgItem, const char* lpString)
|
|
{
|
|
HWND hCtrl;
|
|
|
|
if ( !(hCtrl = GetDlgItem (hDlg, nIDDlgItem)))
|
|
return FALSE;
|
|
|
|
return SendMessage (hCtrl,
|
|
MSG_SETTEXT, 0, (LPARAM)lpString) == 0;
|
|
}
|
|
|
|
#ifdef _MGCTRL_BUTTON
|
|
void GUIAPI CheckDlgButton (HWND hDlg, LINT nIDDlgItem, int nCheck)
|
|
{
|
|
HWND hCtrl;
|
|
int DlgCode;
|
|
|
|
if ( !(hCtrl = GetDlgItem (hDlg, nIDDlgItem)))
|
|
return;
|
|
|
|
DlgCode = SendMessage (hCtrl, MSG_GETDLGCODE, 0, 0L);
|
|
|
|
if (DlgCode & DLGC_BUTTON)
|
|
SendMessage (hCtrl, BM_SETCHECK, nCheck?BST_CHECKED:BST_UNCHECKED, 0L);
|
|
else if (DlgCode & DLGC_3STATE) {
|
|
SendMessage (hCtrl, BM_SETCHECK, nCheck, 0L);
|
|
}
|
|
}
|
|
|
|
void GUIAPI CheckRadioButton (HWND hDlg,
|
|
LINT idFirstButton, LINT idLastButton, LINT idCheckButton)
|
|
{
|
|
HWND hCtrl;
|
|
|
|
if ( !(hCtrl = GetDlgItem (hDlg, idCheckButton)))
|
|
return;
|
|
|
|
SendMessage (hCtrl, BM_SETCHECK, BST_CHECKED, 0L);
|
|
}
|
|
|
|
int GUIAPI IsDlgButtonChecked (HWND hDlg, LINT idButton)
|
|
{
|
|
HWND hCtrl;
|
|
|
|
if ( !(hCtrl = GetDlgItem (hDlg, idButton)))
|
|
return -1;
|
|
|
|
return SendMessage (hCtrl, BM_GETCHECK, 0, 0L);
|
|
}
|
|
|
|
#ifdef _MGCTRL_STATIC
|
|
static LRESULT MsgBoxProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message) {
|
|
case MSG_INITDIALOG:
|
|
{
|
|
HWND hFocus = GetDlgDefPushButton (hWnd);
|
|
if (hFocus)
|
|
SetFocus (hFocus);
|
|
|
|
SetWindowAdditionalData (hWnd, (DWORD)lParam);
|
|
SetWindowFont (hWnd,
|
|
(PLOGFONT)GetWindowElementAttr (hWnd, WE_FONT_MESSAGEBOX));
|
|
|
|
/* set the messagebox's body text font and color.*/
|
|
hFocus = GetDlgItem(hWnd, IDC_STATIC+100);
|
|
SetWindowFont (hFocus,
|
|
(PLOGFONT)GetWindowElementAttr (hFocus, WE_FONT_MESSAGEBOX));
|
|
|
|
SetWindowElementAttr (hFocus,WE_FGC_WINDOW,
|
|
GetWindowElementAttr (hFocus, WE_FGC_MESSAGEBOX));
|
|
return 0;
|
|
}
|
|
|
|
case MSG_COMMAND:
|
|
{
|
|
switch (wParam) {
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
case IDABORT:
|
|
case IDRETRY:
|
|
case IDIGNORE:
|
|
case IDYES:
|
|
case IDNO:
|
|
if (GetDlgItem (hWnd, wParam))
|
|
EndDialog (hWnd, wParam);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MSG_CHAR:
|
|
{
|
|
int id = 0;
|
|
|
|
if (HIBYTE (wParam))
|
|
break;
|
|
switch (LOBYTE (wParam)) {
|
|
case 'Y':
|
|
case 'y':
|
|
id = IDYES;
|
|
break;
|
|
case 'N':
|
|
case 'n':
|
|
id = IDNO;
|
|
break;
|
|
case 'A':
|
|
case 'a':
|
|
id = IDABORT;
|
|
break;
|
|
case 'R':
|
|
case 'r':
|
|
id = IDRETRY;
|
|
break;
|
|
case 'I':
|
|
case 'i':
|
|
id = IDIGNORE;
|
|
break;
|
|
}
|
|
|
|
if (id != 0 && GetDlgItem (hWnd, id))
|
|
EndDialog (hWnd, id);
|
|
break;
|
|
}
|
|
|
|
case MSG_CLOSE:
|
|
if (GetDlgItem (hWnd, IDCANCEL)) {
|
|
EndDialog (hWnd, IDCANCEL);
|
|
}
|
|
else if (GetDlgItem (hWnd, IDIGNORE)) {
|
|
EndDialog (hWnd, IDIGNORE);
|
|
}
|
|
else if (GetDlgItem (hWnd, IDNO)) {
|
|
EndDialog (hWnd, IDNO);
|
|
}
|
|
else if (GetDlgItem (hWnd, IDOK)) {
|
|
EndDialog (hWnd, IDOK);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return DefaultDialogProc (hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
static void get_box_xy (HWND hParentWnd, DWORD dwStyle, DLGTEMPLATE* MsgBoxData)
|
|
{
|
|
RECT rcTemp;
|
|
|
|
if (dwStyle & MB_BASEDONPARENT) {
|
|
GetWindowRect (hParentWnd, &rcTemp);
|
|
}
|
|
else {
|
|
rcTemp = g_rcScr;
|
|
}
|
|
|
|
switch (dwStyle & MB_ALIGNMASK) {
|
|
case MB_ALIGNCENTER:
|
|
MsgBoxData->x = rcTemp.left + (RECTW(rcTemp) - MsgBoxData->w)/2;
|
|
MsgBoxData->y = rcTemp.top + (RECTH(rcTemp) - MsgBoxData->h)/2;
|
|
break;
|
|
|
|
case MB_ALIGNTOPLEFT:
|
|
MsgBoxData->x = rcTemp.left;
|
|
MsgBoxData->y = rcTemp.top;
|
|
break;
|
|
|
|
case MB_ALIGNBTMLEFT:
|
|
MsgBoxData->x = rcTemp.left;
|
|
MsgBoxData->y = rcTemp.bottom - MsgBoxData->h;
|
|
break;
|
|
|
|
case MB_ALIGNTOPRIGHT:
|
|
MsgBoxData->x = rcTemp.right - MsgBoxData->w;
|
|
MsgBoxData->y = rcTemp.top;
|
|
break;
|
|
|
|
case MB_ALIGNBTMRIGHT:
|
|
MsgBoxData->x = rcTemp.right - MsgBoxData->w;
|
|
MsgBoxData->y = rcTemp.bottom - MsgBoxData->h;
|
|
break;
|
|
}
|
|
|
|
if ((MsgBoxData->x + MsgBoxData->w) > g_rcScr.right) {
|
|
MsgBoxData->x = g_rcScr.right - MsgBoxData->w;
|
|
}
|
|
|
|
if ((MsgBoxData->y + MsgBoxData->h) > g_rcScr.bottom) {
|
|
MsgBoxData->y = g_rcScr.bottom - MsgBoxData->h;
|
|
}
|
|
}
|
|
|
|
int GUIAPI MessageBox (HWND hParentWnd, const char* pszText,
|
|
const char* pszCaption, DWORD dwStyle)
|
|
{
|
|
BOOL IsTiny;
|
|
DLGTEMPLATE MsgBoxData =
|
|
{
|
|
WS_CAPTION | WS_BORDER,
|
|
WS_EX_NONE, 0, 0, 0, 0, NULL, 0, 0, 0, NULL, 0L
|
|
};
|
|
CTRLDATA CtrlData [5] =
|
|
{
|
|
{"button",
|
|
BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE | WS_GROUP,
|
|
0, 0, 0, 0, 0, NULL, 0L},
|
|
{"button",
|
|
BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE,
|
|
0, 0, 0, 0, 0, NULL, 0L},
|
|
{"button",
|
|
BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE,
|
|
0, 0, 0, 0, 0, NULL, 0L}
|
|
};
|
|
|
|
int i, nButtons, buttonx, iBorder;
|
|
RECT rcText, rcButtons, rcIcon;
|
|
int width, height;
|
|
int mb_margin, mb_buttonw, mb_buttonh, mb_textw;
|
|
|
|
IsTiny = !strcasecmp (GetDefaultWindowElementRenderer()->name, "tiny");
|
|
if (IsTiny) {
|
|
int font_size = GetSysFontHeight (SYSLOGFONT_CONTROL);
|
|
mb_margin = 2;
|
|
mb_buttonw = font_size * 3;
|
|
mb_buttonh = font_size + 6;
|
|
mb_textw = 120;
|
|
}
|
|
else {
|
|
int font_size = GetSysFontHeight (SYSLOGFONT_CONTROL);
|
|
mb_margin = font_size;
|
|
mb_buttonw = font_size * 4;
|
|
mb_buttonh = (font_size < 8) ? (font_size + 6) : (font_size * 3 / 2);
|
|
mb_textw = 300;
|
|
}
|
|
|
|
/* use system font height to determine the width and height of button */
|
|
|
|
if (pszCaption)
|
|
MsgBoxData.caption = pszCaption;
|
|
else
|
|
MsgBoxData.caption = "MiniGUI";
|
|
|
|
switch (dwStyle & MB_TYPEMASK) {
|
|
case MB_OK:
|
|
MsgBoxData.controlnr = 1;
|
|
CtrlData [0].caption = GetSysText (IDS_MGST_OK);
|
|
CtrlData [0].id = IDOK;
|
|
break;
|
|
case MB_OKCANCEL:
|
|
MsgBoxData.controlnr = 2;
|
|
CtrlData [0].caption = GetSysText (IDS_MGST_OK);
|
|
CtrlData [0].id = IDOK;
|
|
CtrlData [1].caption = (dwStyle & MB_CANCELASBACK) ?
|
|
GetSysText (IDS_MGST_PREV) : GetSysText (IDS_MGST_CANCEL);
|
|
CtrlData [1].id = IDCANCEL;
|
|
break;
|
|
case MB_YESNO:
|
|
MsgBoxData.controlnr = 2;
|
|
CtrlData [0].caption = GetSysText (IDS_MGST_YES);
|
|
CtrlData [0].id = IDYES;
|
|
CtrlData [1].caption = GetSysText (IDS_MGST_NO);
|
|
CtrlData [1].id = IDNO;
|
|
break;
|
|
case MB_RETRYCANCEL:
|
|
MsgBoxData.controlnr = 2;
|
|
CtrlData [0].caption = GetSysText (IDS_MGST_RETRY);
|
|
CtrlData [0].id = IDRETRY;
|
|
CtrlData [1].caption = (dwStyle & MB_CANCELASBACK) ?
|
|
GetSysText (IDS_MGST_PREV) : GetSysText (IDS_MGST_CANCEL);
|
|
CtrlData [1].id = IDCANCEL;
|
|
break;
|
|
case MB_ABORTRETRYIGNORE:
|
|
MsgBoxData.controlnr = 3;
|
|
CtrlData [0].caption = GetSysText (IDS_MGST_ABORT);
|
|
CtrlData [0].id = IDABORT;
|
|
CtrlData [1].caption = GetSysText (IDS_MGST_RETRY);
|
|
CtrlData [1].id = IDRETRY;
|
|
CtrlData [2].caption = GetSysText (IDS_MGST_IGNORE);
|
|
CtrlData [2].id = IDIGNORE;
|
|
break;
|
|
case MB_YESNOCANCEL:
|
|
MsgBoxData.controlnr = 3;
|
|
CtrlData [0].caption = GetSysText (IDS_MGST_YES);
|
|
CtrlData [0].id = IDYES;
|
|
CtrlData [1].caption = GetSysText (IDS_MGST_NO);
|
|
CtrlData [1].id = IDNO;
|
|
CtrlData [2].caption = (dwStyle & MB_CANCELASBACK) ?
|
|
GetSysText (IDS_MGST_PREV) : GetSysText (IDS_MGST_CANCEL);
|
|
CtrlData [2].id = IDCANCEL;
|
|
break;
|
|
}
|
|
|
|
switch (dwStyle & MB_DEFMASK) {
|
|
case MB_DEFBUTTON1:
|
|
CtrlData [0].dwStyle |= BS_DEFPUSHBUTTON;
|
|
break;
|
|
case MB_DEFBUTTON2:
|
|
if (MsgBoxData.controlnr > 1)
|
|
CtrlData [1].dwStyle |= BS_DEFPUSHBUTTON;
|
|
break;
|
|
case MB_DEFBUTTON3:
|
|
if (MsgBoxData.controlnr > 2)
|
|
CtrlData [2].dwStyle |= BS_DEFPUSHBUTTON;
|
|
break;
|
|
}
|
|
|
|
nButtons = MsgBoxData.controlnr;
|
|
rcButtons.left = 0;
|
|
rcButtons.top = 0;
|
|
rcButtons.bottom = mb_buttonh;
|
|
rcButtons.right = MsgBoxData.controlnr * mb_buttonw +
|
|
(MsgBoxData.controlnr - 1) * (mb_margin << 1);
|
|
|
|
rcIcon.left = 0;
|
|
rcIcon.top = 0;
|
|
rcIcon.right = 0;
|
|
rcIcon.bottom = 0;
|
|
if (dwStyle & MB_ICONMASK) {
|
|
int id_icon = -1;
|
|
i = MsgBoxData.controlnr;
|
|
CtrlData [i].class_name= "static";
|
|
CtrlData [i].dwStyle = WS_VISIBLE | SS_ICON | WS_GROUP;
|
|
CtrlData [i].x = mb_margin;
|
|
CtrlData [i].y = mb_margin;
|
|
|
|
if (IsTiny) {
|
|
CtrlData [i].w = 16;
|
|
CtrlData [i].h = 16;
|
|
}
|
|
else {
|
|
CtrlData [i].w = 32;
|
|
CtrlData [i].h = 32;
|
|
}
|
|
|
|
CtrlData [i].id = IDC_STATIC;
|
|
CtrlData [i].caption = "";
|
|
|
|
switch (dwStyle & MB_ICONMASK) {
|
|
case MB_ICONSTOP:
|
|
id_icon = IDI_STOP;
|
|
break;
|
|
case MB_ICONINFORMATION:
|
|
id_icon = IDI_INFORMATION;
|
|
break;
|
|
case MB_ICONEXCLAMATION:
|
|
id_icon = IDI_EXCLAMATION;
|
|
break;
|
|
case MB_ICONQUESTION:
|
|
id_icon = IDI_QUESTION;
|
|
break;
|
|
}
|
|
|
|
if (IsTiny) {
|
|
if (id_icon != -1) {
|
|
CtrlData [i].dwAddData = (DWORD)GetSmallSystemIcon (id_icon);
|
|
}
|
|
rcIcon.right = 16;
|
|
rcIcon.bottom = 16;
|
|
}
|
|
else {
|
|
if (id_icon != -1) {
|
|
CtrlData [i].dwAddData = (DWORD)GetLargeSystemIcon (id_icon);
|
|
MsgBoxData.hIcon = GetSmallSystemIcon (id_icon);
|
|
}
|
|
rcIcon.right = 32;
|
|
rcIcon.bottom = 32;
|
|
}
|
|
|
|
MsgBoxData.controlnr ++;
|
|
}
|
|
|
|
rcText.left = 0;
|
|
rcText.top = 0;
|
|
rcText.right = rcButtons.right + (mb_margin << 1);
|
|
rcText.right = MAX (rcText.right, mb_textw);
|
|
rcText.bottom = GetSysCharHeight ();
|
|
|
|
SelectFont (HDC_SCREEN, __mg_def_renderer->we_fonts[WE_MESSAGEBOX]);
|
|
DrawText (HDC_SCREEN, pszText, -1, &rcText,
|
|
DT_LEFT | DT_TOP | DT_WORDBREAK | DT_EXPANDTABS | DT_CALCRECT);
|
|
|
|
if (IsTiny)
|
|
rcText.right = MAX (rcText.right, mb_textw);
|
|
|
|
i = MsgBoxData.controlnr;
|
|
CtrlData [i].class_name= "static";
|
|
CtrlData [i].dwStyle = WS_VISIBLE | SS_LEFT | WS_GROUP;
|
|
CtrlData [i].dwExStyle = 0;
|
|
CtrlData [i].x = RECTW (rcIcon) + (mb_margin << 1);
|
|
CtrlData [i].y = mb_margin;
|
|
CtrlData [i].w = RECTW (rcText);
|
|
CtrlData [i].h = RECTH (rcText);
|
|
CtrlData [i].id = IDC_STATIC+100;
|
|
CtrlData [i].caption = pszText;
|
|
CtrlData [i].dwAddData = 0;
|
|
MsgBoxData.controlnr ++;
|
|
|
|
iBorder = 1 + 2*GetWindowElementAttr (hParentWnd, WE_METRICS_WND_BORDER);
|
|
|
|
width = MAX (RECTW (rcText), RECTW (rcButtons)) + RECTW (rcIcon)
|
|
+ (mb_margin << 2)
|
|
+ (iBorder << 1);
|
|
height = MAX (RECTH (rcText), RECTH (rcIcon)) + RECTH (rcButtons)
|
|
+ mb_margin + (mb_margin << 1)
|
|
+ (iBorder << 1)
|
|
+ GetWindowElementAttr (hParentWnd, WE_METRICS_CAPTION);
|
|
|
|
buttonx = (width - RECTW (rcButtons)) >> 1;
|
|
for (i = 0; i < nButtons; i++) {
|
|
CtrlData[i].x = buttonx + i*(mb_buttonw + mb_margin);
|
|
CtrlData[i].y = MAX (RECTH (rcIcon), RECTH (rcText)) + (mb_margin<<1);
|
|
CtrlData[i].w = mb_buttonw;
|
|
CtrlData[i].h = mb_buttonh;
|
|
}
|
|
|
|
MsgBoxData.w = width;
|
|
MsgBoxData.h = height;
|
|
get_box_xy (hParentWnd, dwStyle, &MsgBoxData);
|
|
|
|
MsgBoxData.controls = CtrlData;
|
|
|
|
return DialogBoxIndirectParam (&MsgBoxData, hParentWnd, MsgBoxProc,
|
|
(LPARAM)dwStyle);
|
|
}
|
|
|
|
#endif /* _MGCTRL_STATIC */
|
|
#endif /* _MGCTRL_BUTTON */
|
|
|
|
void GUIAPI MessageBeep (DWORD dwBeep)
|
|
{
|
|
Beep ();
|
|
}
|
|
|