| version 1.9, 2005/03/20 23:48:03 | version 1.18, 2011/03/07 09:54:11 | 
| Line 1 | Line 1 | 
| #include        "compiler.h" | /** | 
| #include        "strres.h" | * @file        dialogs.cpp | 
| #include        "bmpdata.h" | * @brief       Dialog subroutines | 
| #include        "oemtext.h" | * | 
| #include        "dosio.h" | * @author      $Author$ | 
| #include        "commng.h" | * @date        $Date$ | 
| #include        "dialogs.h" | */ | 
|  |  | 
|  | #include "compiler.h" | 
|  | #include "resource.h" | 
|  | #include "strres.h" | 
|  | #include "bmpdata.h" | 
|  | #include "oemtext.h" | 
|  | #include "dosio.h" | 
|  | #include "commng.h" | 
|  | #include "dialogs.h" | 
|  | #include "np2.h" | 
 | #if defined(MT32SOUND_DLL) | #if defined(MT32SOUND_DLL) | 
| #include        "mt32snd.h" | #include "mt32snd.h" | 
 | #endif | #endif | 
 |  |  | 
 |  | #if !defined(__GNUC__) | 
 |  | #pragma comment(lib, "winmm.lib") | 
 |  | #endif  // !defined(__GNUC__) | 
 |  |  | 
 |  | // ---- enable | 
 |  |  | 
 |  | void dlgs_enablebyautocheck(HWND hWnd, UINT uID, UINT uCheckID) | 
 |  | { | 
 |  | EnableWindow(GetDlgItem(hWnd, uID), | 
 |  | (SendDlgItemMessage(hWnd, uCheckID, BM_GETCHECK, 0, 0) != 0)); | 
 |  | } | 
 |  |  | 
| const TCHAR str_nc[] = _T("N/C"); | void dlgs_disablebyautocheck(HWND hWnd, UINT uID, UINT uCheckID) | 
|  | { | 
|  | EnableWindow(GetDlgItem(hWnd, uID), | 
|  | (SendDlgItemMessage(hWnd, uCheckID, BM_GETCHECK, 0, 0) == 0)); | 
 |  |  | 
| const TCHAR str_int0[] = _T("INT0"); | } | 
| const TCHAR str_int1[] = _T("INT1"); |  | 
| const TCHAR str_int2[] = _T("INT2"); |  | 
| const TCHAR str_int4[] = _T("INT4"); |  | 
| const TCHAR str_int5[] = _T("INT5"); |  | 
| const TCHAR str_int6[] = _T("INT6"); |  | 
 |  |  | 
 |  |  | 
 | // ---- file select | // ---- file select | 
 |  |  | 
| BOOL dlgs_selectfile(HWND hWnd, const FILESEL *item, | static BOOL openFileParam(LPOPENFILENAME lpOFN, PCFSPARAM pcParam, | 
| OEMCHAR *path, UINT size, int *ro) { | OEMCHAR *pszPath, UINT uSize, | 
|  | BOOL (WINAPI * fnAPI)(LPOPENFILENAME lpofn)) | 
|  | { | 
|  | LPTSTR          lpszTitle; | 
|  | LPTSTR          lpszFilter; | 
|  | LPTSTR          lpszDefExt; | 
|  | LPTSTR          p; | 
|  | #if defined(OSLANG_UTF8) | 
|  | TCHAR           szPath[MAX_PATH]; | 
|  | #endif  // defined(OSLANG_UTF8) | 
|  | BOOL            bResult; | 
|  |  | 
|  | if ((lpOFN == NULL) || (pcParam == NULL) || | 
|  | (pszPath == NULL) || (uSize == 0) || (fnAPI == NULL)) | 
|  | { | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if (!HIWORD(pcParam->lpszTitle)) | 
|  | { | 
|  | lpszTitle = lockstringresource(pcParam->lpszTitle); | 
|  | lpOFN->lpstrTitle = lpszTitle; | 
|  | } | 
|  | else | 
|  | { | 
|  | lpszTitle = NULL; | 
|  | lpOFN->lpstrTitle = pcParam->lpszTitle; | 
|  | } | 
|  |  | 
|  | if (!HIWORD(pcParam->lpszFilter)) | 
|  | { | 
|  | lpszFilter = lockstringresource(pcParam->lpszFilter); | 
|  | lpOFN->lpstrFilter = lpszFilter; | 
|  | } | 
|  | else | 
|  | { | 
|  | lpszFilter = NULL; | 
|  | lpOFN->lpstrFilter = pcParam->lpszFilter; | 
|  | } | 
|  |  | 
|  | if (!HIWORD(pcParam->lpszDefExt)) | 
|  | { | 
|  | lpszDefExt = lockstringresource(pcParam->lpszDefExt); | 
|  | lpOFN->lpstrDefExt = lpszDefExt; | 
|  | } | 
|  | else | 
|  | { | 
|  | lpszDefExt = NULL; | 
|  | lpOFN->lpstrDefExt = pcParam->lpszDefExt; | 
|  | } | 
|  |  | 
|  | lpOFN->nFilterIndex = pcParam->nFilterIndex; | 
|  |  | 
|  |  | 
|  | p = lpszFilter; | 
|  | if (p) | 
|  | { | 
|  | while(*p != '\0') | 
|  | { | 
|  | #if !defined(_UNICODE) | 
|  | if (IsDBCSLeadByte((BYTE)*p)) | 
|  | { | 
|  | p += 2; | 
|  | continue; | 
|  | } | 
|  | #endif  // !defined(_UNICODE) | 
|  | if (*p == '|') | 
|  | { | 
|  | *p = '\0'; | 
|  | } | 
|  | p++; | 
|  | } | 
|  | } | 
 |  |  | 
 | OPENFILENAME    ofn; |  | 
 | #if defined(OSLANG_UTF8) | #if defined(OSLANG_UTF8) | 
| TCHAR                   _path[MAX_PATH]; | oemtotchar(szPath, NELEMENTS(szPath), pszPath, -1); | 
| #endif | lpOFN->lpstrFile = szPath; | 
|  | lpOFN->nMaxFile = NELEMENTS(szPath); | 
|  | #else   // defined(OSLANG_UTF8) | 
|  | lpOFN->lpstrFile = pszPath; | 
|  | lpOFN->nMaxFile = uSize; | 
|  | #endif  // defined(OSLANG_UTF8) | 
|  |  | 
|  | bResult = (*fnAPI)(lpOFN); | 
 |  |  | 
 | if ((item == NULL) || (path == NULL) || (size == 0)) { |  | 
 | return(FALSE); |  | 
 | } |  | 
 | ZeroMemory(&ofn, sizeof(OPENFILENAME)); |  | 
 | ofn.lStructSize = sizeof(OPENFILENAME); |  | 
 | ofn.hwndOwner = hWnd; |  | 
 | ofn.lpstrFilter = item->filter; |  | 
 | ofn.nFilterIndex = item->defindex; |  | 
 | #if defined(OSLANG_UTF8) | #if defined(OSLANG_UTF8) | 
| oemtotchar(_path, NELEMENTS(_path), path, -1); | if (bResult) | 
| ofn.lpstrFile = _path; | { | 
| ofn.nMaxFile = NELEMENTS(_path); | tchartooem(pszPath, uSize, szPath, -1); | 
| #else |  | 
| ofn.lpstrFile = path; |  | 
| ofn.nMaxFile = size; |  | 
| #endif |  | 
| ofn.Flags = OFN_FILEMUSTEXIST; |  | 
| ofn.lpstrDefExt = item->ext; |  | 
| ofn.lpstrTitle = item->title; |  | 
| if (!GetOpenFileName(&ofn)) { |  | 
| return(FALSE); |  | 
 | } | } | 
| #if defined(OSLANG_UTF8) | #endif  // defined(OSLANG_UTF8) | 
| tchartooem(path, NELEMENTS(path), _path, -1); |  | 
| #endif | if (lpszTitle) | 
| if (ro) { | { | 
| *ro = ofn.Flags & OFN_READONLY; | unlockstringresource(lpszTitle); | 
|  | } | 
|  | if (lpszFilter) | 
|  | { | 
|  | unlockstringresource(lpszFilter); | 
|  | } | 
|  | if (lpszDefExt) | 
|  | { | 
|  | unlockstringresource(lpszDefExt); | 
 | } | } | 
 | return(TRUE); |  | 
 | } |  | 
 |  |  | 
| BOOL dlgs_selectwritefile(HWND hWnd, const FILESEL *item, | return bResult; | 
| OEMCHAR *path, UINT size) { | } | 
 |  |  | 
 |  | BOOL dlgs_openfile(HWND hWnd, PCFSPARAM pcParam, | 
 |  | OEMCHAR *pszPath, UINT uSize, int *pnRO) | 
 |  | { | 
 | OPENFILENAME    ofn; | OPENFILENAME    ofn; | 
| #if defined(OSLANG_UTF8) | BOOL                    bResult; | 
| TCHAR                   _path[MAX_PATH]; |  | 
| #endif |  | 
 |  |  | 
| if ((item == NULL) || (path == NULL) || (size == 0)) { | ZeroMemory(&ofn, sizeof(ofn)); | 
| return(FALSE); | ofn.lStructSize = sizeof(ofn); | 
| } |  | 
| ZeroMemory(&ofn, sizeof(OPENFILENAME)); |  | 
| ofn.lStructSize = sizeof(OPENFILENAME); |  | 
 | ofn.hwndOwner = hWnd; | ofn.hwndOwner = hWnd; | 
| ofn.lpstrFilter = item->filter; | ofn.Flags = OFN_FILEMUSTEXIST; | 
| ofn.nFilterIndex = item->defindex; |  | 
| #if defined(OSLANG_UTF8) | if (pnRO == NULL) | 
| oemtotchar(_path, NELEMENTS(_path), path, -1); | { | 
| ofn.lpstrFile = _path; | ofn.Flags |= OFN_HIDEREADONLY; | 
| ofn.nMaxFile = NELEMENTS(_path); |  | 
| #else |  | 
| ofn.lpstrFile = path; |  | 
| ofn.nMaxFile = size; |  | 
| #endif |  | 
| ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; |  | 
| ofn.lpstrDefExt = item->ext; |  | 
| ofn.lpstrTitle = item->title; |  | 
| if (!GetSaveFileName(&ofn)) { |  | 
| return(FALSE); |  | 
 | } | } | 
| #if defined(OSLANG_UTF8) |  | 
| tchartooem(path, NELEMENTS(path), _path, -1); | bResult = openFileParam(&ofn, pcParam, pszPath, uSize, GetOpenFileName); | 
| #endif |  | 
| return(TRUE); | if ((bResult) && (pnRO != NULL)) | 
|  | { | 
|  | *pnRO = ofn.Flags & OFN_READONLY; | 
|  | } | 
|  |  | 
|  | return bResult; | 
 | } | } | 
 |  |  | 
| BOOL dlgs_selectwritenum(HWND hWnd, const FILESEL *item, | BOOL dlgs_createfile(HWND hWnd, PCFSPARAM pcParam, | 
| OEMCHAR *path, UINT size) { | OEMCHAR *pszPath, UINT uSize) | 
|  | { | 
|  | OPENFILENAME    ofn; | 
 |  |  | 
| OEMCHAR *file; | ZeroMemory(&ofn, sizeof(ofn)); | 
| OEMCHAR *p; | ofn.lStructSize = sizeof(ofn); | 
| OEMCHAR *q; | ofn.hwndOwner = hWnd; | 
| UINT    i; | ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; | 
| BOOL    r; | return openFileParam(&ofn, pcParam, pszPath, uSize, GetSaveFileName); | 
|  | } | 
 |  |  | 
| if ((item == NULL) || (path == NULL) || (size == 0)) { | BOOL dlgs_createfilenum(HWND hWnd, PCFSPARAM pcParam, | 
| return(FALSE); | OEMCHAR *pszPath, UINT uSize) | 
| } | { | 
| file = (OEMCHAR *)_MALLOC((size + 16) * sizeof(OEMCHAR), path); | OEMCHAR *pszNum[4]; | 
| if (file == NULL) { | OEMCHAR *pszFile; | 
| return(FALSE); | UINT uCount; | 
| } | UINT uPos; | 
| p = file_getname(path); |  | 
| milstr_ncpy(file, path, size); | if (!pszPath) | 
| file_cutname(file); | { | 
| q = file + OEMSTRLEN(file); | return FALSE; | 
|  | } | 
| for (i=0; i<10000; i++) { |  | 
| OEMSPRINTF(q, p, i); | ZeroMemory(pszNum, sizeof(pszNum)); | 
| if (file_attr(file) == (short)-1) { | pszFile = file_getname(pszPath); | 
|  | uCount = 0; | 
|  | while(1) | 
|  | { | 
|  | pszFile = milstr_chr(pszPath, '#'); | 
|  | if (!pszFile) | 
|  | { | 
|  | break; | 
|  | } | 
|  | *pszFile = '0'; | 
|  | pszNum[uCount] = pszFile; | 
|  | uCount = (uCount + 1) % NELEMENTS(pszNum); | 
|  | pszFile++; | 
|  | } | 
|  |  | 
|  | while(file_attr(pszPath) != (short)-1) | 
|  | { | 
|  | uPos = max(uCount, NELEMENTS(pszNum)); | 
|  | while(uPos) | 
|  | { | 
|  | pszFile = pszNum[(uPos - 1) % NELEMENTS(pszNum)]; | 
|  | *pszFile = *pszFile + 1; | 
|  | if (*pszFile < ('0' + 10)) | 
|  | { | 
|  | break; | 
|  | } | 
|  | *pszFile = '0'; | 
|  | uPos--; | 
|  | } | 
|  | if (!uPos) | 
|  | { | 
 | break; | break; | 
 | } | } | 
 | } | } | 
| r = dlgs_selectwritefile(hWnd, item, file, size); | return dlgs_createfile(hWnd, pcParam, pszPath, uSize); | 
| if (r) { |  | 
| milstr_ncpy(path, file, size); |  | 
| } |  | 
| _MFREE(file); |  | 
| return(r); |  | 
 | } | } | 
 |  |  | 
 |  |  | 
 | // ---- mimpi def file | // ---- mimpi def file | 
 |  |  | 
| static const TCHAR mimpi_title[] = _T("Open MIMPI define file"); | static const FSPARAM fpMIMPI = | 
| static const TCHAR mimpi_ext[] = _T("def"); | { | 
| static const TCHAR mimpi_filter[] = _T("MIMPI define file(*.def)\0*.def\0"); | MAKEINTRESOURCE(IDS_MIMPITITLE), | 
| static const FILESEL mimpi = {mimpi_title, mimpi_ext, mimpi_filter, 1}; | MAKEINTRESOURCE(IDS_MIMPIEXT), | 
|  | MAKEINTRESOURCE(IDS_MIMPIFILTER), | 
|  | 1 | 
|  | }; | 
 |  |  | 
 | void dlgs_browsemimpidef(HWND hWnd, UINT16 res) { | void dlgs_browsemimpidef(HWND hWnd, UINT16 res) { | 
 |  |  | 
| Line 148  const OEMCHAR *p; | Line 258  const OEMCHAR *p; | 
 |  |  | 
 | subwnd = GetDlgItem(hWnd, res); | subwnd = GetDlgItem(hWnd, res); | 
 | GetWindowText(subwnd, path, NELEMENTS(path)); | GetWindowText(subwnd, path, NELEMENTS(path)); | 
| if (dlgs_selectfile(hWnd, &mimpi, path, NELEMENTS(path), NULL)) { | if (dlgs_openfile(hWnd, &fpMIMPI, path, NELEMENTS(path), NULL)) { | 
 | p = path; | p = path; | 
 | } | } | 
 | else { | else { | 
| Line 172  void dlgs_setliststr(HWND hWnd, UINT16 r | Line 282  void dlgs_setliststr(HWND hWnd, UINT16 r | 
 | } | } | 
 |  |  | 
 | void dlgs_setlistuint32(HWND hWnd, UINT16 res, const UINT32 *item, UINT items) { | void dlgs_setlistuint32(HWND hWnd, UINT16 res, const UINT32 *item, UINT items) { | 
 |  |  | 
 | HWND    wnd; | HWND    wnd; | 
 | UINT    i; | UINT    i; | 
 | OEMCHAR str[16]; | OEMCHAR str[16]; | 
| Line 184  void dlgs_setlistuint32(HWND hWnd, UINT1 | Line 293  void dlgs_setlistuint32(HWND hWnd, UINT1 | 
 | } | } | 
 | } | } | 
 |  |  | 
 |  | void dlgs_setcbitem(HWND hWnd, UINT uID, PCCBPARAM pcItem, UINT uItems) | 
 |  | { | 
 |  | HWND    hItem; | 
 |  | UINT    i; | 
 |  | LPCTSTR lpcszStr; | 
 |  | TCHAR   szString[128]; | 
 |  | int             nIndex; | 
 |  |  | 
 |  | hItem = GetDlgItem(hWnd, uID); | 
 |  | for (i=0; i<uItems; i++) | 
 |  | { | 
 |  | lpcszStr = pcItem[i].lpcszString; | 
 |  | if (!HIWORD(lpcszStr)) | 
 |  | { | 
 |  | if (!loadstringresource(LOWORD(lpcszStr), | 
 |  | szString, NELEMENTS(szString))) | 
 |  | { | 
 |  | continue; | 
 |  | } | 
 |  | lpcszStr = szString; | 
 |  | } | 
 |  | nIndex = (int)SendMessage(hItem, CB_ADDSTRING, 0, (LPARAM)lpcszStr); | 
 |  | if (nIndex >= 0) | 
 |  | { | 
 |  | SendMessage(hItem, CB_SETITEMDATA, | 
 |  | (WPARAM)nIndex, (LPARAM)pcItem[i].nItemData); | 
 |  | } | 
 |  | } | 
 |  | } | 
 |  |  | 
 |  | void dlgs_setcbnumber(HWND hWnd, UINT uID, PCCBNPARAM pcItem, UINT uItems) | 
 |  | { | 
 |  | HWND    hItem; | 
 |  | UINT    i; | 
 |  | TCHAR   szValue[16]; | 
 |  | int             nIndex; | 
 |  |  | 
 |  | hItem = GetDlgItem(hWnd, uID); | 
 |  | for (i=0; i<uItems; i++) | 
 |  | { | 
 |  | wsprintf(szValue, str_u, pcItem[i].uValue); | 
 |  | nIndex = (int)SendMessage(hItem, CB_ADDSTRING, 0, (LPARAM)szValue); | 
 |  | if (nIndex >= 0) | 
 |  | { | 
 |  | SendMessage(hItem, CB_SETITEMDATA, | 
 |  | (WPARAM)nIndex, (LPARAM)pcItem[i].nItemData); | 
 |  | } | 
 |  | } | 
 |  | } | 
 |  |  | 
 |  | void dlgs_setcbcur(HWND hWnd, UINT uID, int nItemData) | 
 |  | { | 
 |  | HWND    hItem; | 
 |  | int             nItems; | 
 |  | int             i; | 
 |  |  | 
 |  | hItem = GetDlgItem(hWnd, uID); | 
 |  | nItems = (int)SendMessage(hItem, CB_GETCOUNT, 0, 0); | 
 |  | for (i=0; i<nItems; i++) | 
 |  | { | 
 |  | if (SendMessage(hItem, CB_GETITEMDATA, (WPARAM)i, 0) == nItemData) | 
 |  | { | 
 |  | SendMessage(hItem, CB_SETCURSEL, (WPARAM)i, 0); | 
 |  | break; | 
 |  | } | 
 |  | } | 
 |  | } | 
 |  |  | 
 |  | int dlgs_getcbcur(HWND hWnd, UINT uID, int nDefault) | 
 |  | { | 
 |  | HWND    hItem; | 
 |  | int             nPos; | 
 |  |  | 
 |  | hItem = GetDlgItem(hWnd, uID); | 
 |  | nPos = (int)SendMessage(hItem, CB_GETCURSEL, 0, 0); | 
 |  | if (nPos >= 0) | 
 |  | { | 
 |  | return (int)SendMessage(hItem, CB_GETITEMDATA, (WPARAM)nPos, 0); | 
 |  | } | 
 |  | return nDefault; | 
 |  | } | 
 |  |  | 
 |  |  | 
 | // ---- MIDIデバイスのリスト | // ---- MIDIデバイスのリスト | 
 |  |  | 
 |  | static void insertnc(HWND hWnd, int nPos) | 
 |  | { | 
 |  | TCHAR   szNC[128]; | 
 |  |  | 
 |  | loadstringresource(LOWORD(IDS_NONCONNECT), szNC, NELEMENTS(szNC)); | 
 |  | SendMessage(hWnd, CB_INSERTSTRING, (WPARAM)nPos, (LPARAM)szNC); | 
 |  | } | 
 |  |  | 
 | void dlgs_setlistmidiout(HWND hWnd, UINT16 res, const OEMCHAR *defname) { | void dlgs_setlistmidiout(HWND hWnd, UINT16 res, const OEMCHAR *defname) { | 
 |  |  | 
 | HWND            wnd; | HWND            wnd; | 
| Line 199  void dlgs_setlistmidiout(HWND hWnd, UINT | Line 398  void dlgs_setlistmidiout(HWND hWnd, UINT | 
 | wnd = GetDlgItem(hWnd, res); | wnd = GetDlgItem(hWnd, res); | 
 | defcur = 0; | defcur = 0; | 
 | devs = midiOutGetNumDevs(); | devs = midiOutGetNumDevs(); | 
| SendMessage(wnd, CB_INSERTSTRING, (WPARAM)0, (LPARAM)str_nc); | insertnc(wnd, 0); | 
 | SendMessage(wnd, CB_INSERTSTRING, (WPARAM)1, (LPARAM)cmmidi_midimapper); | SendMessage(wnd, CB_INSERTSTRING, (WPARAM)1, (LPARAM)cmmidi_midimapper); | 
 | if (!milstr_cmp(defname, cmmidi_midimapper)) { | if (!milstr_cmp(defname, cmmidi_midimapper)) { | 
 | defcur = 1; | defcur = 1; | 
| Line 246  void dlgs_setlistmidiin(HWND hWnd, UINT1 | Line 445  void dlgs_setlistmidiin(HWND hWnd, UINT1 | 
 | wnd = GetDlgItem(hWnd, res); | wnd = GetDlgItem(hWnd, res); | 
 | defcur = 0; | defcur = 0; | 
 | num = midiInGetNumDevs(); | num = midiInGetNumDevs(); | 
| SendMessage(wnd, CB_INSERTSTRING, (WPARAM)0, (LPARAM)str_nc); | insertnc(wnd, 0); | 
 | for (i=0; i<num; i++) { | for (i=0; i<num; i++) { | 
 | if (midiInGetDevCaps(i, &mic, sizeof(mic)) == MMSYSERR_NOERROR) { | if (midiInGetDevCaps(i, &mic, sizeof(mic)) == MMSYSERR_NOERROR) { | 
 | SendMessage(wnd, CB_INSERTSTRING, | SendMessage(wnd, CB_INSERTSTRING, | 
| Line 260  void dlgs_setlistmidiin(HWND hWnd, UINT1 | Line 459  void dlgs_setlistmidiin(HWND hWnd, UINT1 | 
 | } | } | 
 |  |  | 
 |  |  | 
 |  |  | 
 | // ---- draw | // ---- draw | 
 |  |  | 
 | void dlgs_drawbmp(HDC hdc, UINT8 *bmp) { | void dlgs_drawbmp(HDC hdc, UINT8 *bmp) { | 
| Line 299  dsdb_err1: | Line 499  dsdb_err1: | 
 | _MFREE(bmp); | _MFREE(bmp); | 
 | } | } | 
 |  |  | 
 |  |  | 
 |  | // ---- | 
 |  |  | 
 |  | BOOL dlgs_getitemrect(HWND hWnd, UINT uID, RECT *pRect) | 
 |  | { | 
 |  | HWND    hItem; | 
 |  | POINT   pt; | 
 |  |  | 
 |  | if (pRect == NULL) | 
 |  | { | 
 |  | return FALSE; | 
 |  | } | 
 |  | hItem = GetDlgItem(hWnd, uID); | 
 |  | if (!GetWindowRect(hItem, pRect)) | 
 |  | { | 
 |  | return FALSE; | 
 |  | } | 
 |  | ZeroMemory(&pt, sizeof(pt)); | 
 |  | if (!ClientToScreen(hWnd, &pt)) | 
 |  | { | 
 |  | return FALSE; | 
 |  | } | 
 |  | pRect->left -= pt.x; | 
 |  | pRect->top -= pt.y; | 
 |  | pRect->right -= pt.x; | 
 |  | pRect->bottom -= pt.y; | 
 |  | return TRUE; | 
 |  | } | 
 |  |  |