You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1351 lines
33 KiB
1351 lines
33 KiB
// Copyright (c) 1995, Microsoft Corporation, all rights reserved
|
|
//
|
|
// edit.c
|
|
// Remote Access Common Dialog APIs
|
|
// List editor, string editor dialog routines
|
|
//
|
|
// 08/28/95 Steve Cobb
|
|
|
|
|
|
#include "rasdlgp.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local datatypes (alphabetically)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// List editor dialog argument block.
|
|
//
|
|
typedef struct
|
|
_LEARGS
|
|
{
|
|
// Caller's arguments to the stub API.
|
|
//
|
|
DTLLIST* pList;
|
|
BOOL* pfCheck;
|
|
DWORD dwMaxItemLen;
|
|
TCHAR* pszTitle;
|
|
TCHAR* pszItemLabel;
|
|
TCHAR* pszListLabel;
|
|
TCHAR* pszCheckLabel;
|
|
TCHAR* pszDefaultItem;
|
|
INT iSelInitial;
|
|
DWORD* pdwHelp;
|
|
DWORD dwfFlags;
|
|
PDESTROYNODE pDestroyId;
|
|
}
|
|
LEARGS;
|
|
|
|
|
|
// List editor dialog context block.
|
|
//
|
|
typedef struct
|
|
_LEINFO
|
|
{
|
|
// Caller's arguments to the dialog.
|
|
//
|
|
LEARGS* pArgs;
|
|
|
|
// Handle of this dialog and some of it's controls.
|
|
//
|
|
HWND hwndDlg;
|
|
HWND hwndStItem;
|
|
HWND hwndStList;
|
|
HWND hwndPbAdd;
|
|
HWND hwndPbReplace;
|
|
HWND hwndPbUp;
|
|
HWND hwndPbDown;
|
|
HWND hwndPbDelete;
|
|
HWND hwndPbOk;
|
|
HWND hwndEb;
|
|
HWND hwndLb;
|
|
HWND hwndCb;
|
|
|
|
// Convenient alternatives to (pInfo->pArgs->dwFlags & LEDFLAG_Sorted) and
|
|
// (pInfo->pArgs->dwFlags & LEDFLAG_NoDeleteLastItem).
|
|
//
|
|
BOOL fSorted;
|
|
BOOL fNoDeleteLast;
|
|
|
|
// Button bitmaps.
|
|
//
|
|
HBITMAP hbmUp;
|
|
HBITMAP hbmDown;
|
|
|
|
// List of empty nodes whose node-IDs should be 'pDestroyId'ed if user
|
|
// presses OK.
|
|
//
|
|
DTLLIST* pListDeletes;
|
|
}
|
|
LEINFO;
|
|
|
|
|
|
// String Editor dialog arument block.
|
|
//
|
|
typedef struct
|
|
_ZEARGS
|
|
{
|
|
/* Caller's aruments to the stub API.
|
|
*/
|
|
TCHAR* pszIn;
|
|
DWORD dwSidTitle;
|
|
DWORD dwSidLabel;
|
|
DWORD cbMax;
|
|
DWORD dwHelpId;
|
|
TCHAR** ppszOut;
|
|
}
|
|
ZEARGS;
|
|
|
|
|
|
// String Editor dialog context block.
|
|
//
|
|
typedef struct
|
|
_ZEINFO
|
|
{
|
|
// Caller's arguments to the stub API.
|
|
//
|
|
ZEARGS* pArgs;
|
|
|
|
// Dialog and control handles.
|
|
//
|
|
HWND hwndDlg;
|
|
HWND hwndEb;
|
|
}
|
|
ZEINFO;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local prototypes (alphabetically)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
INT_PTR CALLBACK
|
|
LeDlgProc(
|
|
IN HWND hwnd,
|
|
IN UINT unMsg,
|
|
IN WPARAM wparam,
|
|
IN LPARAM lparam );
|
|
|
|
VOID
|
|
LeAdd(
|
|
IN LEINFO* pInfo );
|
|
|
|
BOOL
|
|
LeCommand(
|
|
IN LEINFO* pInfo,
|
|
IN WORD wNotification,
|
|
IN WORD wId,
|
|
IN HWND hwndCtrl );
|
|
|
|
VOID
|
|
LeDelete(
|
|
IN LEINFO* pInfo );
|
|
|
|
VOID
|
|
LeDown(
|
|
IN LEINFO* pInfo );
|
|
|
|
VOID
|
|
LeEnableUpAndDownButtons(
|
|
IN LEINFO* pInfo );
|
|
|
|
VOID
|
|
LeExitNoMemory(
|
|
IN LEINFO* pInfo );
|
|
|
|
BOOL
|
|
LeInit(
|
|
IN HWND hwndDlg,
|
|
IN LEARGS* pArgs );
|
|
|
|
VOID
|
|
LeItemTextFromListSelection(
|
|
IN LEINFO* pInfo );
|
|
|
|
VOID
|
|
LeReplace(
|
|
IN LEINFO* pInfo );
|
|
|
|
BOOL
|
|
LeSaveSettings(
|
|
IN LEINFO* pInfo );
|
|
|
|
VOID
|
|
LeTerm(
|
|
IN HWND hwndDlg );
|
|
|
|
VOID
|
|
LeUp(
|
|
IN LEINFO* pInfo );
|
|
|
|
INT_PTR CALLBACK
|
|
ZeDlgProc(
|
|
IN HWND hwnd,
|
|
IN UINT unMsg,
|
|
IN WPARAM wparam,
|
|
IN LPARAM lparam );
|
|
|
|
BOOL
|
|
ZeCommand(
|
|
IN ZEINFO* pInfo,
|
|
IN WORD wNotification,
|
|
IN WORD wId,
|
|
IN HWND hwndCtrl );
|
|
|
|
BOOL
|
|
ZeInit(
|
|
IN HWND hwndDlg,
|
|
IN ZEARGS* pArgs );
|
|
|
|
VOID
|
|
ZeTerm(
|
|
IN HWND hwndDlg );
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// List Editor dialog entry point
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
BOOL
|
|
ListEditorDlg(
|
|
IN HWND hwndOwner,
|
|
IN OUT DTLLIST* pList,
|
|
IN OUT BOOL* pfCheck,
|
|
IN DWORD dwMaxItemLen,
|
|
IN TCHAR* pszTitle,
|
|
IN TCHAR* pszItemLabel,
|
|
IN TCHAR* pszListLabel,
|
|
IN TCHAR* pszCheckLabel,
|
|
IN TCHAR* pszDefaultItem,
|
|
IN INT iSelInitial,
|
|
IN DWORD* pdwHelp,
|
|
IN DWORD dwfFlags,
|
|
IN PDESTROYNODE pDestroyId )
|
|
|
|
// Pops-up the List Editor dialog.
|
|
//
|
|
// 'HwndOwner' is the owner of the dialog. 'PList' is, on entry, the Psz
|
|
// list to display initially, and on successful exit, the result list.
|
|
// 'PfCheck' is the state of the check box or NULL for the non-checkbox
|
|
// style. 'DwMaxItemLen' is the maximum length of an individual list
|
|
// item. 'PszTitle' is the dialog title. 'PszItemLabel' is the label
|
|
// (and hotkey) associated with the item box. 'PszListLabel' is the label
|
|
// (and hotkey) associated with the list. 'PszCheckLabel' is the label
|
|
// (and hotkey) associated with the checkbox. 'PszDefaultItem' is the
|
|
// default contents of the edit box or for the selected list text.
|
|
// 'ISelInitial' is the item the list to initally select. 'PdwHelp' is
|
|
// the array of CID_LE_* help contexts to use. 'DwfFlags' indicates
|
|
// LEDFLAG_* behavior options. 'PDestroyId' is the routine to use to
|
|
// destroy node IDs when they are deleted or NULL if none.
|
|
//
|
|
// Returns true if user pressed OK and succeeded, false if he pressed
|
|
// Cancel or encountered an error.
|
|
//
|
|
{
|
|
INT_PTR nStatus;
|
|
LEARGS args;
|
|
|
|
TRACE( "ListEditorDlg" );
|
|
|
|
args.pList = pList;
|
|
args.pfCheck = pfCheck;
|
|
args.dwMaxItemLen = dwMaxItemLen;
|
|
args.pszTitle = pszTitle;
|
|
args.pszItemLabel = pszItemLabel;
|
|
args.pszListLabel = pszListLabel;
|
|
args.pszCheckLabel = pszCheckLabel;
|
|
args.pszDefaultItem = pszDefaultItem;
|
|
args.iSelInitial = iSelInitial;
|
|
args.pdwHelp = pdwHelp;
|
|
args.dwfFlags = dwfFlags;
|
|
args.pDestroyId = pDestroyId;
|
|
|
|
nStatus =
|
|
DialogBoxParam(
|
|
g_hinstDll,
|
|
(pfCheck)
|
|
? MAKEINTRESOURCE( DID_LE_ListEditor2 )
|
|
: ((dwfFlags & LEDFLAG_Sorted)
|
|
? MAKEINTRESOURCE( DID_LE_ListEditor3 )
|
|
: MAKEINTRESOURCE( DID_LE_ListEditor )),
|
|
hwndOwner,
|
|
LeDlgProc,
|
|
(LPARAM )&args );
|
|
|
|
if (nStatus == -1)
|
|
{
|
|
ErrorDlg( hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
|
|
nStatus = FALSE;
|
|
}
|
|
|
|
return (nStatus) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// List Editor dialog routines
|
|
// Listed alphabetically following dialog proc
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT_PTR CALLBACK
|
|
LeDlgProc(
|
|
IN HWND hwnd,
|
|
IN UINT unMsg,
|
|
IN WPARAM wparam,
|
|
IN LPARAM lparam )
|
|
|
|
// DialogProc callback for the List Editor dialog. Parameters and return
|
|
// value are as described for standard windows 'DialogProc's.
|
|
//
|
|
{
|
|
#if 0
|
|
TRACE4( "LeDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
|
|
(DWORD )hwnd, (DWORD )unMsg, (DWORD )wparam, (DWORD )lparam );
|
|
#endif
|
|
|
|
switch (unMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
return LeInit( hwnd, (LEARGS* )lparam );
|
|
}
|
|
|
|
case WM_HELP:
|
|
case WM_CONTEXTMENU:
|
|
{
|
|
LEINFO* pInfo = (LEINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
|
|
ASSERT( pInfo );
|
|
|
|
ContextHelp( pInfo->pArgs->pdwHelp, hwnd, unMsg, wparam, lparam );
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
LEINFO* pInfo = (LEINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
|
|
ASSERT( pInfo );
|
|
|
|
return LeCommand(
|
|
pInfo, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
LeTerm( hwnd );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
LeAdd(
|
|
IN LEINFO* pInfo )
|
|
|
|
// Add button click handler. 'PInfo' is the dialog context.
|
|
//
|
|
{
|
|
TCHAR* psz;
|
|
|
|
psz = GetText( pInfo->hwndEb );
|
|
if (!psz)
|
|
{
|
|
LeExitNoMemory( pInfo );
|
|
return;
|
|
}
|
|
|
|
if (pInfo->pArgs->dwfFlags & LEDFLAG_Unique)
|
|
{
|
|
if (ListBox_IndexFromString( pInfo->hwndLb, psz ) >= 0)
|
|
{
|
|
MSGARGS msgargs;
|
|
|
|
ZeroMemory( &msgargs, sizeof(msgargs) );
|
|
msgargs.apszArgs[ 0 ] = psz;
|
|
MsgDlg( pInfo->hwndDlg, SID_NotUnique, &msgargs );
|
|
Edit_SetSel( pInfo->hwndEb, 0, -1 );
|
|
SetFocus( pInfo->hwndEb );
|
|
Free( psz );
|
|
return;
|
|
}
|
|
}
|
|
|
|
ListBox_SetCurSel( pInfo->hwndLb,
|
|
ListBox_AddItem( pInfo->hwndLb, psz, 0 ) );
|
|
Free( psz );
|
|
LeEnableUpAndDownButtons( pInfo );
|
|
EnableWindow( pInfo->hwndPbReplace, FALSE );
|
|
|
|
if (!pInfo->fNoDeleteLast || ListBox_GetCount( pInfo->hwndLb ) > 1)
|
|
{
|
|
EnableWindow( pInfo->hwndPbDelete, TRUE );
|
|
}
|
|
|
|
SetWindowText( pInfo->hwndEb, TEXT("") );
|
|
SetFocus( pInfo->hwndEb );
|
|
}
|
|
|
|
|
|
BOOL
|
|
LeCommand(
|
|
IN LEINFO* pInfo,
|
|
IN WORD wNotification,
|
|
IN WORD wId,
|
|
IN HWND hwndCtrl )
|
|
|
|
// Called on WM_COMMAND. 'PInfo' is the dialog context. 'WNotification'
|
|
// is the notification code of the command. 'wId' is the control/menu
|
|
// identifier of the command. 'HwndCtrl' is the control window handle of
|
|
// the command.
|
|
//
|
|
// Returns true if processed message, false otherwise.
|
|
//
|
|
{
|
|
TRACE3( "LeCommand(n=%d,i=%d,c=$%x)",
|
|
(DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );
|
|
|
|
switch (wId)
|
|
{
|
|
case CID_LE_PB_Add:
|
|
{
|
|
LeAdd( pInfo );
|
|
return TRUE;
|
|
}
|
|
|
|
case CID_LE_PB_Replace:
|
|
{
|
|
LeReplace( pInfo );
|
|
return TRUE;
|
|
}
|
|
|
|
case CID_LE_PB_Up:
|
|
{
|
|
LeUp( pInfo );
|
|
return TRUE;
|
|
}
|
|
|
|
case CID_LE_PB_Down:
|
|
{
|
|
LeDown( pInfo );
|
|
return TRUE;
|
|
}
|
|
|
|
case CID_LE_PB_Delete:
|
|
{
|
|
LeDelete( pInfo );
|
|
return TRUE;
|
|
}
|
|
|
|
case CID_LE_EB_Item:
|
|
{
|
|
if (wNotification == EN_SETFOCUS || wNotification == EN_UPDATE)
|
|
{
|
|
TCHAR* psz = GetText( pInfo->hwndEb );
|
|
|
|
if (psz && lstrlen( psz ) > 0 && !IsAllWhite( psz ))
|
|
{
|
|
EnableWindow( pInfo->hwndPbAdd, TRUE );
|
|
EnableWindow( pInfo->hwndPbReplace, TRUE );
|
|
Button_MakeDefault( pInfo->hwndDlg, pInfo->hwndPbAdd );
|
|
}
|
|
else
|
|
{
|
|
EnableWindow( pInfo->hwndPbAdd, FALSE );
|
|
EnableWindow( pInfo->hwndPbReplace, FALSE );
|
|
Button_MakeDefault( pInfo->hwndDlg, pInfo->hwndPbOk );
|
|
}
|
|
|
|
Free0( psz );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case CID_LE_LB_List:
|
|
{
|
|
if (wNotification == LBN_SELCHANGE)
|
|
{
|
|
LeEnableUpAndDownButtons( pInfo );
|
|
if (ListBox_GetCurSel( pInfo->hwndLb ) >= 0)
|
|
{
|
|
LeItemTextFromListSelection( pInfo );
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case CID_LE_CB_Promote:
|
|
{
|
|
if (wNotification == BN_SETFOCUS)
|
|
{
|
|
Button_MakeDefault( pInfo->hwndDlg, pInfo->hwndPbOk );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case IDOK:
|
|
{
|
|
EndDialog( pInfo->hwndDlg, LeSaveSettings( pInfo ) );
|
|
return TRUE;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
{
|
|
DTLLIST* pList;
|
|
DTLNODE* pNode;
|
|
|
|
TRACE( "Cancel pressed" );
|
|
EndDialog( pInfo->hwndDlg, FALSE );
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
LeDelete(
|
|
IN LEINFO* pInfo )
|
|
|
|
// Delete button click handler. 'PInfo' is the dialog context.
|
|
//
|
|
{
|
|
INT i;
|
|
INT c;
|
|
|
|
i = ListBox_GetCurSel( pInfo->hwndLb );
|
|
if (pInfo->pArgs->pDestroyId)
|
|
{
|
|
LONG_PTR lId = ListBox_GetItemData( pInfo->hwndLb, i );
|
|
if (lId != 0)
|
|
{
|
|
DTLNODE* pNode;
|
|
|
|
pNode = DtlCreateNode( NULL, lId );
|
|
if (!pNode)
|
|
{
|
|
ErrorDlg( pInfo->hwndDlg, SID_OP_DisplayData,
|
|
ERROR_NOT_ENOUGH_MEMORY, NULL );
|
|
EndDialog( pInfo->hwndDlg, FALSE );
|
|
return;
|
|
}
|
|
|
|
DtlAddNodeFirst( pInfo->pListDeletes, pNode );
|
|
}
|
|
}
|
|
ListBox_DeleteString( pInfo->hwndLb, i );
|
|
c = ListBox_GetCount( pInfo->hwndLb );
|
|
|
|
if (c == 0)
|
|
{
|
|
EnableWindow( pInfo->hwndPbReplace, FALSE );
|
|
EnableWindow( pInfo->hwndPbDelete, FALSE );
|
|
SetFocus( pInfo->hwndEb );
|
|
Edit_SetSel( pInfo->hwndEb, 0, -1 );
|
|
}
|
|
else
|
|
{
|
|
if (c == 1 && pInfo->fNoDeleteLast)
|
|
{
|
|
EnableWindow( pInfo->hwndPbDelete, FALSE );
|
|
}
|
|
|
|
if (i >= c)
|
|
{
|
|
i = c - 1;
|
|
}
|
|
|
|
ListBox_SetCurSel( pInfo->hwndLb, i );
|
|
}
|
|
|
|
LeEnableUpAndDownButtons( pInfo );
|
|
|
|
if (IsWindowEnabled( GetFocus() ))
|
|
{
|
|
SetFocus( pInfo->hwndEb );
|
|
Edit_SetSel( pInfo->hwndEb, 0, -1 );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LeDown(
|
|
IN LEINFO* pInfo )
|
|
|
|
// Down button click handler. 'PInfo' is the dialog context.
|
|
//
|
|
{
|
|
TCHAR* psz;
|
|
INT i;
|
|
LONG_PTR lId;
|
|
|
|
ASSERT( !pInfo->fSorted );
|
|
|
|
i = ListBox_GetCurSel( pInfo->hwndLb );
|
|
psz = ListBox_GetPsz( pInfo->hwndLb, i );
|
|
if (!psz)
|
|
{
|
|
LeExitNoMemory( pInfo );
|
|
return;
|
|
}
|
|
|
|
lId = ListBox_GetItemData( pInfo->hwndLb, i );
|
|
ListBox_InsertString( pInfo->hwndLb, i + 2, psz );
|
|
ListBox_SetItemData( pInfo->hwndLb, i + 2, lId );
|
|
Free( psz );
|
|
ListBox_DeleteString( pInfo->hwndLb, i );
|
|
ListBox_SetCurSel( pInfo->hwndLb, i + 1 );
|
|
|
|
if (i == ListBox_GetCount( pInfo->hwndLb ))
|
|
{
|
|
Button_MakeDefault( pInfo->hwndDlg, pInfo->hwndPbUp );
|
|
SetFocus( pInfo->hwndPbUp );
|
|
}
|
|
|
|
LeEnableUpAndDownButtons( pInfo );
|
|
}
|
|
|
|
|
|
VOID
|
|
LeEnableUpAndDownButtons(
|
|
IN LEINFO* pInfo )
|
|
|
|
// Determine if the Up and Down operations make sense and enable/disable
|
|
// the buttons as appropriate. 'PInfo' is the dialog context.
|
|
//
|
|
{
|
|
INT i;
|
|
INT c;
|
|
|
|
if (pInfo->fSorted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
i = ListBox_GetCurSel( pInfo->hwndLb );
|
|
c = ListBox_GetCount( pInfo->hwndLb );
|
|
|
|
EnableWindow( pInfo->hwndPbDown, (i < c - 1 ) );
|
|
EnableWindow( pInfo->hwndPbUp, (i > 0) );
|
|
}
|
|
|
|
|
|
VOID
|
|
LeExitNoMemory(
|
|
IN LEINFO* pInfo )
|
|
|
|
// End the dialog reporting a memory. 'PInfo' is the dialog context.
|
|
//
|
|
{
|
|
ErrorDlg( pInfo->hwndDlg,
|
|
SID_OP_DisplayData, ERROR_NOT_ENOUGH_MEMORY, NULL );
|
|
EndDialog( pInfo->hwndDlg, FALSE );
|
|
}
|
|
|
|
|
|
BOOL
|
|
LeInit(
|
|
IN HWND hwndDlg,
|
|
IN LEARGS* pArgs )
|
|
|
|
// Called on WM_INITDIALOG. 'HwndDlg' is the handle of the phonebook
|
|
// dialog window. 'PArgs' is caller's arguments as passed to the stub
|
|
// API.
|
|
//
|
|
// Return false if focus was set, true otherwise, i.e. as defined for
|
|
// WM_INITDIALOG.
|
|
//
|
|
{
|
|
DWORD dwErr;
|
|
LEINFO* pInfo;
|
|
DTLNODE* pNode;
|
|
INT c;
|
|
|
|
TRACE( "LeInit" );
|
|
|
|
// Allocate the dialog context block. Initialize minimally for proper
|
|
// cleanup, then attach to the dialog window.
|
|
//
|
|
{
|
|
pInfo = Malloc( sizeof(*pInfo) );
|
|
if (!pInfo)
|
|
{
|
|
ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
|
|
EndDialog( hwndDlg, FALSE );
|
|
return TRUE;
|
|
}
|
|
|
|
ZeroMemory( pInfo, sizeof(*pInfo) );
|
|
pInfo->pArgs = pArgs;
|
|
pInfo->hwndDlg = hwndDlg;
|
|
|
|
SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );
|
|
TRACE( "Context set" );
|
|
}
|
|
|
|
// Set up convenient shortcuts.
|
|
//
|
|
if (pArgs->dwfFlags & LEDFLAG_Sorted)
|
|
{
|
|
pInfo->fSorted = TRUE;
|
|
}
|
|
|
|
if (pArgs->dwfFlags & LEDFLAG_NoDeleteLastItem)
|
|
{
|
|
pInfo->fNoDeleteLast = TRUE;
|
|
}
|
|
|
|
pInfo->hwndStItem = GetDlgItem( hwndDlg, CID_LE_ST_Item );
|
|
ASSERT( pInfo->hwndStItem );
|
|
pInfo->hwndStList = GetDlgItem( hwndDlg, CID_LE_ST_List );
|
|
ASSERT( pInfo->hwndStList );
|
|
pInfo->hwndPbAdd = GetDlgItem( hwndDlg, CID_LE_PB_Add );
|
|
ASSERT( pInfo->hwndPbAdd );
|
|
pInfo->hwndPbReplace = GetDlgItem( hwndDlg, CID_LE_PB_Replace );
|
|
ASSERT( pInfo->hwndPbReplace );
|
|
pInfo->hwndPbDelete = GetDlgItem( hwndDlg, CID_LE_PB_Delete );
|
|
ASSERT( pInfo->hwndPbDelete );
|
|
pInfo->hwndPbOk = GetDlgItem( hwndDlg, IDOK );
|
|
ASSERT( pInfo->hwndPbOk );
|
|
pInfo->hwndEb = GetDlgItem( hwndDlg, CID_LE_EB_Item );
|
|
ASSERT( pInfo->hwndEb );
|
|
pInfo->hwndLb = GetDlgItem( hwndDlg, CID_LE_LB_List );
|
|
ASSERT( pInfo->hwndLb );
|
|
|
|
if (pArgs->pDestroyId)
|
|
{
|
|
// Create the empty list of deletions.
|
|
//
|
|
pInfo->pListDeletes = DtlCreateList( 0L );
|
|
if (!pInfo->pListDeletes)
|
|
{
|
|
ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
|
|
EndDialog( hwndDlg, FALSE );
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!pInfo->fSorted)
|
|
{
|
|
pInfo->hwndPbUp = GetDlgItem( hwndDlg, CID_LE_PB_Up );
|
|
ASSERT( pInfo->hwndPbUp );
|
|
pInfo->hwndPbDown = GetDlgItem( hwndDlg, CID_LE_PB_Down );
|
|
ASSERT( pInfo->hwndPbDown );
|
|
|
|
// Draw the graphical up and down arrow indicators.
|
|
//
|
|
pInfo->hbmUp = Button_CreateBitmap(
|
|
pInfo->hwndPbUp, BMS_UpArrowOnRight );
|
|
if (pInfo->hbmUp)
|
|
{
|
|
SendMessage( pInfo->hwndPbUp, BM_SETIMAGE, 0,
|
|
(LPARAM )pInfo->hbmUp );
|
|
}
|
|
|
|
pInfo->hbmDown = Button_CreateBitmap(
|
|
pInfo->hwndPbDown, BMS_DownArrowOnRight );
|
|
if (pInfo->hbmDown)
|
|
{
|
|
SendMessage( pInfo->hwndPbDown, BM_SETIMAGE, 0,
|
|
(LPARAM )pInfo->hbmDown );
|
|
}
|
|
}
|
|
|
|
if (pArgs->pfCheck)
|
|
{
|
|
pInfo->hwndCb = GetDlgItem( hwndDlg, CID_LE_CB_Promote );
|
|
ASSERT( pInfo->hwndCb );
|
|
SetWindowText( pInfo->hwndCb, pArgs->pszCheckLabel );
|
|
Button_SetCheck( pInfo->hwndCb, *pArgs->pfCheck );
|
|
}
|
|
|
|
Edit_LimitText( pInfo->hwndEb, pArgs->dwMaxItemLen );
|
|
|
|
// Set caller-defined dialog title and labels.
|
|
//
|
|
SetWindowText( pInfo->hwndDlg, pArgs->pszTitle );
|
|
SetWindowText( pInfo->hwndStItem, pArgs->pszItemLabel );
|
|
SetWindowText( pInfo->hwndStList, pArgs->pszListLabel );
|
|
|
|
// Fill the listbox.
|
|
//
|
|
for (pNode = DtlGetFirstNode( pArgs->pList );
|
|
pNode;
|
|
pNode = DtlGetNextNode( pNode ))
|
|
{
|
|
TCHAR* psz = (TCHAR* )DtlGetData( pNode );
|
|
ASSERT( psz );
|
|
|
|
ListBox_AddItem( pInfo->hwndLb, psz, (VOID* ) DtlGetNodeId( pNode ) );
|
|
}
|
|
|
|
c = ListBox_GetCount( pInfo->hwndLb );
|
|
if (c > 0)
|
|
{
|
|
// Select item selected by caller.
|
|
//
|
|
ListBox_SetCurSelNotify( pInfo->hwndLb, pArgs->iSelInitial );
|
|
LeEnableUpAndDownButtons( pInfo );
|
|
|
|
if (c == 1 && pInfo->fNoDeleteLast)
|
|
{
|
|
EnableWindow( pInfo->hwndPbDelete, FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Empty list.
|
|
//
|
|
if (!pInfo->fSorted)
|
|
{
|
|
EnableWindow( pInfo->hwndPbUp, FALSE );
|
|
EnableWindow( pInfo->hwndPbDown, FALSE );
|
|
}
|
|
EnableWindow( pInfo->hwndPbDelete, FALSE );
|
|
}
|
|
|
|
// Set default edit box contents, if any.
|
|
//
|
|
if (pArgs->pszDefaultItem)
|
|
{
|
|
SetWindowText( pInfo->hwndEb, pArgs->pszDefaultItem );
|
|
Edit_SetSel( pInfo->hwndEb, 0, -1 );
|
|
}
|
|
else
|
|
{
|
|
EnableWindow( pInfo->hwndPbAdd, FALSE );
|
|
EnableWindow( pInfo->hwndPbReplace, FALSE );
|
|
}
|
|
|
|
// Center dialog on the owner window.
|
|
//
|
|
CenterWindow( hwndDlg, GetParent( hwndDlg ) );
|
|
|
|
// Add context help button to title bar. Dlgedit.exe doesn't currently
|
|
// support this at resource edit time. When that's fixed set
|
|
// DS_CONTEXTHELP there and remove this call.
|
|
//
|
|
AddContextHelpButton( hwndDlg );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
LeItemTextFromListSelection(
|
|
IN LEINFO* pInfo )
|
|
|
|
// Copies the currently selected item in the list to the edit box.
|
|
// 'PInfo' is the dialog context.
|
|
//
|
|
{
|
|
TCHAR* psz;
|
|
INT iSel;
|
|
|
|
iSel = ListBox_GetCurSel( pInfo->hwndLb );
|
|
if (iSel >= 0)
|
|
{
|
|
psz = ListBox_GetPsz( pInfo->hwndLb, iSel );
|
|
if (psz)
|
|
{
|
|
SetWindowText( pInfo->hwndEb, psz );
|
|
Free( psz );
|
|
return;
|
|
}
|
|
}
|
|
|
|
SetWindowText( pInfo->hwndEb, TEXT("") );
|
|
}
|
|
|
|
|
|
VOID
|
|
LeReplace(
|
|
IN LEINFO* pInfo )
|
|
|
|
// Replace button click handler. 'PInfo' is the dialog context.
|
|
//
|
|
{
|
|
TCHAR* psz;
|
|
INT i;
|
|
LONG_PTR lId;
|
|
|
|
psz = GetText( pInfo->hwndEb );
|
|
if (!psz)
|
|
{
|
|
LeExitNoMemory( pInfo );
|
|
return;
|
|
}
|
|
|
|
if (pInfo->pArgs->dwfFlags & LEDFLAG_Unique)
|
|
{
|
|
if (ListBox_IndexFromString( pInfo->hwndLb, psz ) >= 0)
|
|
{
|
|
MSGARGS msgargs;
|
|
|
|
ZeroMemory( &msgargs, sizeof(msgargs) );
|
|
msgargs.apszArgs[ 0 ] = psz;
|
|
MsgDlg( pInfo->hwndDlg, SID_NotUnique, &msgargs );
|
|
Edit_SetSel( pInfo->hwndEb, 0, -1 );
|
|
SetFocus( pInfo->hwndEb );
|
|
Free( psz );
|
|
return;
|
|
}
|
|
}
|
|
|
|
i = ListBox_GetCurSel( pInfo->hwndLb );
|
|
lId = ListBox_GetItemData( pInfo->hwndLb, i );
|
|
ListBox_DeleteString( pInfo->hwndLb, i );
|
|
|
|
if (pInfo->fSorted)
|
|
{
|
|
i = ListBox_AddItem( pInfo->hwndLb, psz, (VOID* )lId );
|
|
}
|
|
else
|
|
{
|
|
ListBox_InsertString( pInfo->hwndLb, i, psz );
|
|
ListBox_SetItemData( pInfo->hwndLb, i, lId );
|
|
}
|
|
|
|
Free( psz );
|
|
ListBox_SetCurSel( pInfo->hwndLb, i );
|
|
SetFocus( pInfo->hwndEb );
|
|
SetWindowText( pInfo->hwndEb, TEXT("") );
|
|
}
|
|
|
|
|
|
BOOL
|
|
LeSaveSettings(
|
|
IN LEINFO* pInfo )
|
|
|
|
// Saves dialog settings in the stub API caller's list. 'PInfo' is the
|
|
// dialog context.
|
|
//
|
|
// Returns true if successful, false if does not validate.
|
|
//
|
|
{
|
|
DWORD dwErr;
|
|
DTLNODE* pNode;
|
|
DTLLIST* pList;
|
|
DTLLIST* pListNew;
|
|
TCHAR* psz;
|
|
LONG_PTR lId;
|
|
INT c;
|
|
INT i;
|
|
|
|
// Make new list from list box contents.
|
|
//
|
|
do
|
|
{
|
|
pListNew = DtlCreateList( 0L );
|
|
if (!pListNew)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
dwErr = 0;
|
|
c = ListBox_GetCount( pInfo->hwndLb );
|
|
|
|
for (i = 0; i < c; ++i)
|
|
{
|
|
psz = ListBox_GetPsz( pInfo->hwndLb, i );
|
|
if (!psz)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
lId = ListBox_GetItemData( pInfo->hwndLb, i );
|
|
ASSERT( lId>=0 );
|
|
|
|
pNode = DtlCreateNode( psz, lId );
|
|
if (!pNode)
|
|
{
|
|
Free( psz );
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
DtlAddNodeLast( pListNew, pNode );
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
if (dwErr != 0)
|
|
{
|
|
ErrorDlg( pInfo->hwndDlg, SID_OP_DisplayData, dwErr, NULL );
|
|
DtlDestroyList( pListNew, DestroyPszNode );
|
|
return FALSE;
|
|
}
|
|
|
|
// Free all data in the old list.
|
|
//
|
|
while (pNode = DtlGetFirstNode( pInfo->pArgs->pList ))
|
|
{
|
|
Free( (TCHAR* )DtlGetData( pNode ) );
|
|
DtlDeleteNode( pInfo->pArgs->pList, pNode );
|
|
}
|
|
|
|
// Free the node-IDs in the list of deletions.
|
|
//
|
|
if (pInfo->pListDeletes)
|
|
{
|
|
while (pNode = DtlGetFirstNode( pInfo->pListDeletes ))
|
|
{
|
|
pInfo->pArgs->pDestroyId( (DTLNODE* )DtlGetNodeId( pNode ) );
|
|
DtlDeleteNode( pInfo->pListDeletes, pNode );
|
|
}
|
|
}
|
|
|
|
// Move the new list onto caller's list.
|
|
//
|
|
while (pNode = DtlGetFirstNode( pListNew ))
|
|
{
|
|
DtlRemoveNode( pListNew, pNode );
|
|
DtlAddNodeLast( pInfo->pArgs->pList, pNode );
|
|
}
|
|
DtlDestroyList( pListNew, DestroyPszNode );
|
|
|
|
// Tell caller what the checkbox setting is.
|
|
//
|
|
if (pInfo->pArgs->pfCheck)
|
|
{
|
|
*pInfo->pArgs->pfCheck = Button_GetCheck( pInfo->hwndCb );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
LeTerm(
|
|
IN HWND hwndDlg )
|
|
|
|
// Called on WM_DESTROY. 'HwndDlg' is that handle of the dialog window.
|
|
//
|
|
{
|
|
LEINFO* pInfo = (LEINFO* )GetWindowLongPtr( hwndDlg, DWLP_USER );
|
|
|
|
TRACE( "LeTerm" );
|
|
|
|
if (pInfo)
|
|
{
|
|
if (pInfo->hbmUp)
|
|
{
|
|
DeleteObject( pInfo->hbmUp );
|
|
}
|
|
|
|
if (pInfo->hbmDown)
|
|
{
|
|
DeleteObject( pInfo->hbmDown );
|
|
}
|
|
|
|
DtlDestroyList( pInfo->pListDeletes, NULL );
|
|
Free( pInfo );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LeUp(
|
|
IN LEINFO* pInfo )
|
|
|
|
// Up button click handler. 'PInfo' is the dialog context.
|
|
//
|
|
{
|
|
TCHAR* psz;
|
|
INT i;
|
|
LONG_PTR lId;
|
|
|
|
ASSERT( !pInfo->fSorted );
|
|
|
|
i = ListBox_GetCurSel( pInfo->hwndLb );
|
|
psz = ListBox_GetPsz( pInfo->hwndLb, i );
|
|
if (!psz)
|
|
{
|
|
LeExitNoMemory( pInfo );
|
|
return;
|
|
}
|
|
|
|
ListBox_InsertString( pInfo->hwndLb, i - 1, psz );
|
|
Free( psz );
|
|
lId = ListBox_GetItemData( pInfo->hwndLb, i + 1 );
|
|
ListBox_DeleteString( pInfo->hwndLb, i + 1 );
|
|
ListBox_SetItemData( pInfo->hwndLb, i - 1, lId );
|
|
ListBox_SetCurSel( pInfo->hwndLb, i - 1 );
|
|
|
|
if (i == 1)
|
|
{
|
|
Button_MakeDefault( pInfo->hwndDlg, pInfo->hwndPbDown );
|
|
SetFocus( pInfo->hwndPbDown );
|
|
}
|
|
|
|
LeEnableUpAndDownButtons( pInfo );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// String Editor dialog entry point
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
StringEditorDlg(
|
|
IN HWND hwndOwner,
|
|
IN TCHAR* pszIn,
|
|
IN DWORD dwSidTitle,
|
|
IN DWORD dwSidLabel,
|
|
IN DWORD cbMax,
|
|
IN DWORD dwHelpId,
|
|
IN OUT TCHAR** ppszOut )
|
|
|
|
// Pops-up the String Editor dialog. 'PszIn' is the initial setting of
|
|
// the edit box or NULL for blank. 'DwSidTitle' and 'dwSidLabel' are the
|
|
// string resource IDs of the dialog title and edit box label. 'CbMax' is
|
|
// the maximum length of the to allow or 0 for no limit. 'DwHelpId' is
|
|
// the HID_* constant to associate with the label and edit field or -1 if
|
|
// none.
|
|
//
|
|
// Returns true if user pressed OK and succeeded, false if he pressed
|
|
// Cancel or encountered an error. If true, '*ppszNumber' is a heap block
|
|
// with the edited result. It is caller's responsibility to Free the
|
|
// returned block.
|
|
//
|
|
{
|
|
INT_PTR nStatus;
|
|
ZEARGS args;
|
|
|
|
TRACE( "StringEditorDlg" );
|
|
|
|
args.pszIn = pszIn;
|
|
args.dwSidTitle = dwSidTitle;
|
|
args.dwSidLabel = dwSidLabel;
|
|
args.cbMax = cbMax;
|
|
args.dwHelpId = dwHelpId;
|
|
args.ppszOut = ppszOut;
|
|
|
|
nStatus =
|
|
(BOOL )DialogBoxParam(
|
|
g_hinstDll,
|
|
MAKEINTRESOURCE( DID_ZE_StringEditor ),
|
|
hwndOwner,
|
|
ZeDlgProc,
|
|
(LPARAM )&args );
|
|
|
|
if (nStatus == -1)
|
|
{
|
|
ErrorDlg( hwndOwner, SID_OP_LoadDlg, ERROR_UNKNOWN, NULL );
|
|
nStatus = FALSE;
|
|
}
|
|
|
|
return (nStatus) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// String Editor dialog routines
|
|
// Listed alphabetically following dialog proc
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT_PTR CALLBACK
|
|
ZeDlgProc(
|
|
IN HWND hwnd,
|
|
IN UINT unMsg,
|
|
IN WPARAM wparam,
|
|
IN LPARAM lparam )
|
|
|
|
// DialogProc callback for the Edit Phone Number dialog. Parameters and
|
|
// return value are as described for standard windows 'DialogProc's.
|
|
//
|
|
{
|
|
#if 0
|
|
TRACE4( "ZeDlgProc(h=$%x,m=$%x,w=$%x,l=$%x)",
|
|
(DWORD )hwnd, (DWORD )unMsg, (DWORD )wparam, (DWORD )lparam );
|
|
#endif
|
|
|
|
switch (unMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
return ZeInit( hwnd, (ZEARGS* )lparam );
|
|
}
|
|
|
|
case WM_HELP:
|
|
case WM_CONTEXTMENU:
|
|
{
|
|
ZEINFO* pInfo;
|
|
|
|
pInfo = (ZEINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
|
|
if (pInfo && pInfo->pArgs->dwHelpId != (DWORD )-1)
|
|
{
|
|
DWORD adwZeHelp[ (2 + 1) * 2 ];
|
|
|
|
ZeroMemory( adwZeHelp, sizeof(adwZeHelp) );
|
|
adwZeHelp[ 0 ] = CID_ZE_ST_String;
|
|
adwZeHelp[ 2 ] = CID_ZE_EB_String;
|
|
adwZeHelp[ 1 ] = adwZeHelp[ 3 ] = pInfo->pArgs->dwHelpId;
|
|
|
|
ContextHelp( adwZeHelp, hwnd, unMsg, wparam, lparam );
|
|
break;
|
|
}
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
ZEINFO* pInfo = (ZEINFO* )GetWindowLongPtr( hwnd, DWLP_USER );
|
|
ASSERT( pInfo );
|
|
|
|
return ZeCommand(
|
|
pInfo, HIWORD( wparam ), LOWORD( wparam ), (HWND )lparam );
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
ZeTerm( hwnd );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ZeCommand(
|
|
IN ZEINFO* pInfo,
|
|
IN WORD wNotification,
|
|
IN WORD wId,
|
|
IN HWND hwndCtrl )
|
|
|
|
// Called on WM_COMMAND. 'PInfo' is the dialog context. 'WNotification'
|
|
// is the notification code of the command. 'wId' is the control/menu
|
|
// identifier of the command. 'HwndCtrl' is the control window handle of
|
|
// the command.
|
|
//
|
|
// Returns true if processed message, false otherwise.
|
|
//
|
|
{
|
|
TRACE3( "ZeCommand(n=%d,i=%d,c=$%x)",
|
|
(DWORD )wNotification, (DWORD )wId, (ULONG_PTR )hwndCtrl );
|
|
|
|
switch (wId)
|
|
{
|
|
case IDOK:
|
|
{
|
|
TRACE( "OK pressed" );
|
|
*pInfo->pArgs->ppszOut = GetText( pInfo->hwndEb );
|
|
EndDialog( pInfo->hwndDlg, (*pInfo->pArgs->ppszOut != NULL) );
|
|
return TRUE;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
{
|
|
TRACE( "Cancel pressed" );
|
|
EndDialog( pInfo->hwndDlg, FALSE );
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ZeInit(
|
|
IN HWND hwndDlg,
|
|
IN ZEARGS* pArgs )
|
|
|
|
// Called on WM_INITDIALOG. 'hwndDlg' is the handle of the owning window.
|
|
// 'PArgs' is caller's arguments as passed to the stub API.
|
|
//
|
|
// Return false if focus was set, true otherwise, i.e. as defined for
|
|
// WM_INITDIALOG.
|
|
//
|
|
{
|
|
DWORD dwErr;
|
|
TCHAR* psz;
|
|
ZEINFO* pInfo;
|
|
|
|
TRACE( "ZeInit" );
|
|
|
|
// Allocate the dialog context block. Initialize minimally for proper
|
|
// cleanup, then attach to the dialog window.
|
|
//
|
|
{
|
|
pInfo = Malloc( sizeof(*pInfo) );
|
|
if (!pInfo)
|
|
{
|
|
ErrorDlg( hwndDlg, SID_OP_LoadDlg, ERROR_NOT_ENOUGH_MEMORY, NULL );
|
|
EndDialog( hwndDlg, FALSE );
|
|
return TRUE;
|
|
}
|
|
|
|
ZeroMemory( pInfo, sizeof(*pInfo) );
|
|
pInfo->pArgs = pArgs;
|
|
pInfo->hwndDlg = hwndDlg;
|
|
|
|
SetWindowLongPtr( hwndDlg, DWLP_USER, (ULONG_PTR )pInfo );
|
|
TRACE( "Context set" );
|
|
}
|
|
|
|
pInfo->hwndEb = GetDlgItem( hwndDlg, CID_ZE_EB_String );
|
|
ASSERT( pInfo->hwndEb );
|
|
|
|
if (pArgs->cbMax > 0)
|
|
{
|
|
Edit_LimitText( pInfo->hwndEb, pArgs->cbMax );
|
|
}
|
|
|
|
psz = PszFromId( g_hinstDll, pArgs->dwSidTitle );
|
|
if (psz)
|
|
{
|
|
SetWindowText( hwndDlg, psz );
|
|
Free( psz );
|
|
}
|
|
|
|
psz = PszFromId( g_hinstDll, pArgs->dwSidLabel );
|
|
if (psz)
|
|
{
|
|
HWND hwndSt = GetDlgItem( hwndDlg, CID_ZE_ST_String );
|
|
ASSERT( hwndSt );
|
|
SetWindowText( hwndSt, psz );
|
|
Free( psz );
|
|
}
|
|
|
|
if (pArgs->pszIn)
|
|
{
|
|
SetWindowText( pInfo->hwndEb, pArgs->pszIn );
|
|
Edit_SetSel( pInfo->hwndEb, 0, -1 );
|
|
}
|
|
|
|
// Center dialog on the owner window.
|
|
//
|
|
CenterWindow( hwndDlg, GetParent( hwndDlg ) );
|
|
|
|
// Add context help button to title bar. Dlgedit.exe doesn't currently
|
|
// support this at resource edit time. When that's fixed set
|
|
// DS_CONTEXTHELP there and remove this call.
|
|
//
|
|
AddContextHelpButton( hwndDlg );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
ZeTerm(
|
|
IN HWND hwndDlg )
|
|
|
|
// Called on WM_DESTROY. 'HwndDlg' is that handle of the dialog window.
|
|
//
|
|
{
|
|
ZEINFO* pInfo = (ZEINFO* )GetWindowLongPtr( hwndDlg, DWLP_USER );
|
|
|
|
TRACE( "ZeTerm" );
|
|
|
|
if (pInfo)
|
|
{
|
|
Free( pInfo );
|
|
}
|
|
}
|