/** \file mswedit.c * Text entry widgets */ /* XTrackCAD - Model Railroad CAD * Copyright (C) 2005 Dave Bullis * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "mswint.h" struct wString_t { WOBJ_COMMON char * valueP; wIndex_t valueL; wStringCallBack_p action; wBool_t enter_pressed; /**< flag if enter was pressed */ }; #ifdef LATER struct wInteger_t { WOBJ_COMMON long low, high; long * valueP; long oldValue; wIntegerCallBack_p action; }; struct wFloat_t { WOBJ_COMMON double low, high; double * valueP; double oldValue; wFloatCallBack_p action; }; #endif // LATER static XWNDPROC oldEditProc = NULL; static XWNDPROC newEditProc; static void triggerString( wString_p b ); #ifdef LATER static void triggerInteger( wControl_p b ); static void triggerFloat( wControl_p b ); #endif LRESULT FAR PASCAL _export pushEdit( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { wIndex_t inx = (wIndex_t)GetWindowLongPtr( hWnd, GWL_ID ); wString_p b = (wString_p)mswMapIndex(inx); switch (message) { case WM_CHAR: if (b != NULL) { switch (wParam) { case VK_RETURN: triggerString(b); return (LRESULT)0; break; case 0x1B: case 0x09: SetFocus(((wControl_p)(b->parent))->hWnd); SendMessage(((wControl_p)(b->parent))->hWnd, WM_CHAR, wParam, lParam); return (LRESULT)0; } } break; } return CallWindowProc(oldEditProc, hWnd, message, wParam, lParam); } /* ***************************************************************************** * * String Boxes * ***************************************************************************** */ void wStringSetValue( wString_p b, const char * arg ) { WORD len = (WORD)strlen( arg ); SendMessage( b->hWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)arg ); SendMessage( b->hWnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1 ); SendMessage( b->hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0 ); SendMessage( b->hWnd, EM_SETMODIFY, (WPARAM)FALSE, (LPARAM)0 ); } void wStringSetWidth( wString_p b, wWinPix_t w ) { int rc; b->w = w; rc = SetWindowPos( b->hWnd, HWND_TOP, 0, 0, b->w, b->h, SWP_NOMOVE|SWP_NOZORDER ); } const char * wStringGetValue( wString_p b ) { static char buff[1024]; SendMessage( b->hWnd, WM_GETTEXT, (WPARAM)sizeof buff, (LPARAM)buff ); return buff; } /** * Get the string from a entry field. The returned pointer has to be free() after processing is complete. * * \param bs IN string entry field * * \return pointer to entered string or NULL if entry field is empty. */ static char *getString(wString_p bs) { char *tmpBuffer = NULL; UINT chars = (UINT)SendMessage(bs->hWnd, EM_LINELENGTH, (WPARAM)0, (LPARAM)0); if (chars) { tmpBuffer = malloc(chars > sizeof(WORD)? chars + 1 : sizeof(WORD) + 1); *(WORD *)tmpBuffer = chars; SendMessage(bs->hWnd, (UINT)EM_GETLINE, (WPARAM)0, (LPARAM)tmpBuffer); tmpBuffer[chars] = '\0'; } else { tmpBuffer = malloc(2); tmpBuffer[0] = '\n'; tmpBuffer[1] = '\0'; } return (tmpBuffer); } /** * Retrieve and process string entry. If a string has been entered, the callback for * the specific entry field is called. * * \param b IN string entry field */ static void triggerString( wString_p b) { const char *output = "\n"; char *enteredString = getString(b); if (enteredString) { if (b->valueP) { strcpy(b->valueP, enteredString); } if (b->action) { b->enter_pressed = TRUE; b->action(output, b->data); } free(enteredString); } } LRESULT stringProc( wControl_p b, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { wString_p bs = (wString_p)b; int modified; switch (message) { case WM_COMMAND: switch (WCMD_PARAM_NOTF) { case EN_KILLFOCUS: modified = (int)SendMessage(bs->hWnd, (UINT)EM_GETMODIFY, (WPARAM)0, (LPARAM)0); if (!modified) { break; } char *enteredString = getString(bs); if (enteredString) { if (bs->valueP) { strcpy(bs->valueP, enteredString); } if (bs->action) { bs->action(enteredString, bs->data); mswSetTrigger(NULL, NULL); } free(enteredString); } SendMessage(bs->hWnd, (UINT)EM_SETMODIFY, (WPARAM)FALSE, (LPARAM)0); } break; } return DefWindowProc(hWnd, message, wParam, lParam); } static callBacks_t stringCallBacks = { mswRepaintLabel, NULL, stringProc }; wString_p wStringCreate( wWin_p parent, wWinPix_t x, wWinPix_t y, const char * helpStr, const char * labelStr, long option, wWinPix_t width, char *valueP, wIndex_t valueL, wStringCallBack_p action, void *data ) { wString_p b; RECT rect; int index; DWORD style = 0; b = (wString_p)mswAlloc( parent, B_STRING, mswStrdup(labelStr), sizeof *b, data, &index ); mswComputePos( (wControl_p)b, x, y ); b->option = option; b->valueP = valueP; b->valueL = valueL; b->labelY += 2; b->action = action; if (option & BO_READONLY) { style |= ES_READONLY; } b->hWnd = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", NULL, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | style, b->x, b->y, width, mswEditHeight, ((wControl_p)parent)->hWnd, (HMENU)(UINT_PTR)index, mswHInst, NULL ); if (b->hWnd == NULL) { mswFail("CreateWindow(STRING)"); return b; } newEditProc = MakeProcInstance( (XWNDPROC)pushEdit, mswHInst ); oldEditProc = (XWNDPROC)GetWindowLongPtr(b->hWnd, GWLP_WNDPROC); SetWindowLongPtr(b->hWnd, GWLP_WNDPROC, (LONG_PTR)newEditProc); #ifdef _OLDCODE oldEditProc = (XWNDPROC)GetWindowLongPtr(b->hWnd, GWL_WNDPROC ); SetWindowLong( b->hWnd, GWL_WNDPROC, (LONG)newEditProc ); #endif // WIN64 if (b->valueP) { SendMessage( b->hWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)b->valueP ); } SendMessage( b->hWnd, EM_SETMODIFY, (WPARAM)FALSE, (LPARAM)0 ); SendMessage( b->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, (LPARAM)0 ); GetWindowRect( b->hWnd, &rect ); b->w = rect.right - rect.left; b->h = rect.bottom - rect.top; mswAddButton( (wControl_p)b, TRUE, helpStr ); mswCallBacks[B_STRING] = &stringCallBacks; mswChainFocus( (wControl_p)b ); return b; } #ifdef LATER /* ***************************************************************************** * * Integer Value Boxes * ***************************************************************************** */ #define MININT ((long)0x80000000) #define MAXINT ((long)0x7FFFFFFF) void wIntegerSetValue( wInteger_p b, long arg ) { b->oldValue = arg; wsprintf( mswTmpBuff, "%ld", arg ); SendMessage( b->hWnd, WM_SETTEXT, 0, (DWORD)(LPSTR)mswTmpBuff ); SendMessage( b->hWnd, EM_SETMODIFY, FALSE, 0L ); } long wIntegerGetValue( wInteger_p b ) { return b->oldValue; } static void triggerInteger( wControl_p b ) { wInteger_p bi = (wInteger_p)b; int cnt; long value; char * cp; if (bi->action) { *(WPARAM*)&mswTmpBuff[0] = 78; cnt = (int)SendMessage( bi->hWnd, (UINT)EM_GETLINE, 0, (DWORD)(LPSTR)mswTmpBuff ); mswTmpBuff[cnt] = '\0'; if (strcmp( mswTmpBuff, "-" )==0 ) { return; } value = strtol( mswTmpBuff, &cp, 10 ); if (*cp != '\0' || value < bi->low || value > bi->high ) { return; } if (bi->oldValue == value) { return; } if (bi->valueP) { *bi->valueP = value; } bi->oldValue = value; bi->action( value, bi->data ); } } LRESULT integerProc( wControl_p b, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { wInteger_p bi = (wInteger_p)b; int inx; int cnt; long value; char * cp; wBool_t ok; int modified; switch( message ) { case WM_COMMAND: switch (WCMD_PARAM_NOTF) { case EN_KILLFOCUS: ok = TRUE; modified = (int)SendMessage( bi->hWnd, (UINT)EM_GETMODIFY, 0, 0L ); if (!modified) { break; } *(WPARAM*)&mswTmpBuff[0] = 78; cnt = (int)SendMessage( bi->hWnd, (UINT)EM_GETLINE, 0, (DWORD)(LPSTR)mswTmpBuff ); mswTmpBuff[cnt] = '\0'; if (strcmp( mswTmpBuff, "-" )==0 && 0 >= bi->low && 0 <= bi->high ) { value = 0; } else { value = strtol( mswTmpBuff, &cp, 10 ); if (*cp != '\0' || value < bi->low || value > bi->high ) { inx = GetWindowWord( bi->hWnd, GWW_ID ); if (wWinIsVisible(bi->parent)) { PostMessage( ((wControl_p)(bi->parent))->hWnd, WM_NOTVALID, inx, 0L ); return TRUE; } else { if (value < bi->low) { value = bi->low; } else { value = bi->high; } sprintf( mswTmpBuff, "%ld", value ); SendMessage( bi->hWnd, (UINT)WM_SETTEXT, 0, (DWORD)(LPSTR)mswTmpBuff ); } } } bi->oldValue = value; if (bi->valueP) { *bi->valueP = value; } if (bi->action) { bi->action( value, bi->data ); mswSetTrigger( NULL, NULL ); } SendMessage( bi->hWnd, (UINT)EM_SETMODIFY, FALSE, 0L ); } break; case WM_NOTVALID: wsprintf( mswTmpBuff, "Please enter a value between %ld and %ld", bi->low, bi->high ); if (bi->low > MININT && bi->high < MAXINT) sprintf( mswTmpBuff, "Please enter an integer value between %ld and %ld", bi->low, bi->high ); else if (bi->low > MININT) sprintf( mswTmpBuff, "Please enter an integer value greater or equal to %ld", bi->low ); else if (bi->high < MAXINT) sprintf( mswTmpBuff, "Please enter an integer value less or equal to %ld", bi->high ); else { strcpy( mswTmpBuff, "Please enter an integer value" ); } MessageBox( bi->hWnd, mswTmpBuff, "Invalid entry", MB_OK ); SetFocus( bi->hWnd ); #ifdef WIN32 SendMessage( bi->hWnd, EM_SETSEL, 0, 0x7fff ); SendMessage( bi->hWnd, EM_SCROLLCARET, 0, 0L ); #else SendMessage( bi->hWnd, EM_SETSEL, 0, MAKELONG(0,0x7fff) ); #endif return TRUE; } return DefWindowProc( hWnd, message, wParam, lParam ); } static callBacks_t integerCallBacks = { mswRepaintLabel, NULL, integerProc }; wInteger_p wIntegerCreate( wWin_p parent, wWinPix_t x, wWinPix_t y, const char * helpStr, const char * labelStr, long option, wWinPix_t width, long low, long high, long *valueP, wIntegerCallBack_p action, void *data ) { wInteger_p b; RECT rect; int index; DWORD style = 0; b = mswAlloc( parent, B_INTEGER, mswStrdup(labelStr), sizeof *b, data, &index ); mswComputePos( (wControl_p)b, x, y ); b->option = option; b->low = low; b->high = high; b->valueP = valueP; b->labelY += 2; b->action = action; if (option & BO_READONLY) { style |= ES_READONLY; } b->hWnd = CreateWindow( "EDIT", NULL, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | style, b->x, b->y, width, mswEditHeight, ((wControl_p)parent)->hWnd, (HMENU)index, mswHInst, NULL ); if (b->hWnd == NULL) { mswFail("CreateWindow(INTEGER)"); return b; } newEditProc = MakeProcInstance( (XWNDPROC)pushEdit, mswHInst ); oldEditProc = (XWNDPROC)GetWindowLong(b->hWnd, GWL_WNDPROC ); SetWindowLong( b->hWnd, GWL_WNDPROC, (LONG)newEditProc ); if ( !mswThickFont ) { SendMessage( b->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, 0L ); } if (b->valueP) { wsprintf( mswTmpBuff, "%ld", *b->valueP ); SendMessage( b->hWnd, WM_SETTEXT, 0, (DWORD)(LPSTR)mswTmpBuff ); b->oldValue = *b->valueP; } else { b->oldValue = 0; } SendMessage( b->hWnd, EM_SETMODIFY, FALSE, 0L ); GetWindowRect( b->hWnd, &rect ); b->w = rect.right - rect.left; b->h = rect.bottom - rect.top; mswAddButton( (wControl_p)b, TRUE, helpStr ); mswCallBacks[ B_INTEGER ] = &integerCallBacks; mswChainFocus( (wControl_p)b ); return b; } /* ***************************************************************************** * * Floating Point Value Boxes * ***************************************************************************** */ #define MINFLT (-1000000) #define MAXFLT (1000000) void wFloatSetValue( wFloat_p b, double arg ) { b->oldValue = arg; sprintf( mswTmpBuff, "%0.3f", arg ); SendMessage( b->hWnd, WM_SETTEXT, 0, (DWORD)(LPSTR)mswTmpBuff ); SendMessage( b->hWnd, EM_SETMODIFY, FALSE, 0L ); } double wFloatGetValue( wFloat_p b ) { return b->oldValue; } static void triggerFloat( wControl_p b ) { wFloat_p bf = (wFloat_p)b; int cnt; double value; char * cp; if (bf->action) { *(WPARAM*)&mswTmpBuff[0] = 78; cnt = (int)SendMessage( bf->hWnd, (UINT)EM_GETLINE, 0, (DWORD)(LPSTR)mswTmpBuff ); mswTmpBuff[cnt] = '\0'; if (strcmp( mswTmpBuff, "-" )==0) { return; } value = strtod( mswTmpBuff, &cp ); if (*cp != '\0' || value < bf->low || value > bf->high ) { return; } if (bf->oldValue == value) { return; } bf->oldValue = value; if (bf->valueP) { *bf->valueP = value; } bf->action( wFloatGetValue(bf), bf->data ); } } LRESULT floatProc( wControl_p b, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { wFloat_p bf = (wFloat_p)b; int inx; int cnt; double value; char * cp; wBool_t ok; int modified; switch( message ) { case WM_COMMAND: switch (HIWORD(lParam)) { case EN_KILLFOCUS: ok = TRUE; modified = (int)SendMessage( bf->hWnd, (UINT)EM_GETMODIFY, 0, 0L ); if (!modified) { break; } *(WPARAM*)&mswTmpBuff[0] = 78; cnt = (int)SendMessage( bf->hWnd, (UINT)EM_GETLINE, 0, (DWORD)(LPSTR)mswTmpBuff ); mswTmpBuff[cnt] = '\0'; if (strcmp( mswTmpBuff, "-" )==0 && 0 >= bf->low && 0 <= bf->high ) { value = 0; } else { value = strtod( mswTmpBuff, &cp ); if (*cp != '\0' || value < bf->low || value > bf->high ) { inx = GetWindowWord( bf->hWnd, GWW_ID ); if (wWinIsVisible(bf->parent)) { PostMessage( ((wControl_p)(bf->parent))->hWnd, WM_NOTVALID, inx, 0L ); return TRUE; } else { if (value < bf->low) { value = bf->low; } else { value = bf->high; } sprintf( mswTmpBuff, "%0.3f", value ); SendMessage( bf->hWnd, (UINT)WM_SETTEXT, 0, (DWORD)(LPSTR)mswTmpBuff ); } } } bf->oldValue = value; if (bf->valueP) { *bf->valueP = value; } if (bf->action) { bf->action( value, bf->data ); mswSetTrigger( NULL, NULL ); } SendMessage( bf->hWnd, (UINT)EM_SETMODIFY, FALSE, 0L ); } break; case WM_NOTVALID: if (bf->low > MINFLT && bf->high < MAXFLT) sprintf( mswTmpBuff, "Please enter an float value between %0.3f and %0.3f", bf->low, bf->high ); else if (bf->low > MINFLT) sprintf( mswTmpBuff, "Please enter an float value greater or equal to %0.3f", bf->low ); else if (bf->high < MAXFLT) sprintf( mswTmpBuff, "Please enter an float value less or equal to %0.3f", bf->high ); else { strcpy( mswTmpBuff, "Please enter an float value" ); } MessageBox( bf->hWnd, mswTmpBuff, "Invalid entry", MB_OK ); SetFocus( bf->hWnd ); #ifdef WIN32 SendMessage( bi->hWnd, EM_SETSEL, 0, 0x7fff ); SendMessage( bi->hWnd, EM_SCROLLCARET, 0, 0L ); #else SendMessage( bi->hWnd, EM_SETSEL, 0, MAKELONG(0,0x7fff) ); #endif return TRUE; } return DefWindowProc( hWnd, message, wParam, lParam ); } static callBacks_t floatCallBacks = { mswRepaintLabel, NULL, floatProc }; wFloat_p wFloatCreate( wWin_p parent, wWinPix_t x, wWinPix_t y, const char * helpStr, const char * labelStr, long option, wWinPix_t width, double low, double high, double *valueP, wFloatCallBack_p action, void *data ) { wFloat_p b; RECT rect; int index; DWORD style = 0; b = mswAlloc( parent, B_FLOAT, mswStrdup(labelStr), sizeof *b, data, &index ); mswComputePos( (wControl_p)b, x, y ); b->option = option; b->low = low; b->high = high; b->valueP = valueP; b->labelY += 2; b->action = action; if (option & BO_READONLY) { style |= ES_READONLY; } b->hWnd = CreateWindow( "EDIT", NULL, ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | style, b->x, b->y, width, mswEditHeight, ((wControl_p)parent)->hWnd, (HMENU)index, mswHInst, NULL ); if (b->hWnd == NULL) { mswFail("CreateWindow(FLOAT)"); return b; } newEditProc = MakeProcInstance( (XWNDPROC)pushEdit, mswHInst ); oldEditProc = (XWNDPROC)GetWindowLong(b->hWnd, GWL_WNDPROC ); SetWindowLong( b->hWnd, GWL_WNDPROC, (LONG)newEditProc ); if (b->valueP) { b->oldValue = *b->valueP; } else { b->oldValue = 0.0; } if (b->valueP) { sprintf( mswTmpBuff, "%0.3f", *b->valueP ); } else { strcpy( mswTmpBuff, "0.000" ); } if ( !mswThickFont ) { SendMessage( b->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, 0L ); } SendMessage( b->hWnd, WM_SETTEXT, 0, (DWORD)(LPSTR)mswTmpBuff ); SendMessage( b->hWnd, EM_SETMODIFY, FALSE, 0L ); GetWindowRect( b->hWnd, &rect ); b->w = rect.right - rect.left; b->h = rect.bottom - rect.top; mswAddButton( (wControl_p)b, TRUE, helpStr ); mswCallBacks[ B_FLOAT ] = &floatCallBacks; mswChainFocus( (wControl_p)b ); return b; } #endif