API

에디트에 숫자 입력시 천단위로 자동 컴마(콤마) 찍기(Edit Comma)

디버그정 2016. 2. 3. 19:37


Edit_Comma.txt




// 음수, 소수점 고려
// 버퍼 길이에 0 전달시 필요한 크기(NULL 포함 길이) 리턴
int __stdcall AddComma(LPCTSTR src, LPSTR buf, int lenbuf)
{
int len, lenCommaRange, cCommas, res;
TCHAR szError[256];

if (!src) // 기본 체크
return 0;

if (!(len = lstrlen(src))) // 기본 체크
return 0;

// 컴마의 수는 (컴마를 찍을 수 있는 영역의 길이 - 1) / 3
// 소수점이 존재하는 경우는 . 앞 부분까지 길이로 설정하면 된다.
// 음수인 경우 처음에 -가 붙으므로 1을 빼준다.
for (lenCommaRange = 0; src[lenCommaRange] != '.' && src[lenCommaRange] != NULL; lenCommaRange++) {}
if (src[0] == '-')
--lenCommaRange;

cCommas = (lenCommaRange - 1) / 3;
res = len + cCommas; // 변환될 문자열의 길이

if (lenbuf == 0) // 버퍼 길이에 0 전달시 필요한 크기(NULL 포함 길이) 리턴
return res + 1;

if (!buf) // 기본 체크
return 0;

if (res + 1 > lenbuf) // 버퍼 크기 체크
{
wsprintf(szError, _T("버퍼의 크기(NULL 포함)가 작습니다.(%d 필요)"), res + 1);
MessageBox(NULL, szError, _T("AddComma"), MB_ICONERROR | MB_TOPMOST);
return 0;
}

if (cCommas)
{
if (src[0] == '-') // 음수인 경우
{
*buf++ = '-';
src++;
}

switch (lenCommaRange % 3) // 첫번째 컴마 전까지 복사한다. break가 없으므로 0이면 3회 반복, 2이면 2회 반복된다.
{
case 0: *buf++ = *src++;
case 2: *buf++ = *src++;
case 1: *buf++ = *src++;
}

while (cCommas) // 컴마 횟수만큼 (컴마, 복사, 복사, 복사)를 반복한다.
{
*buf++ = ',';
*buf++ = *src++;
*buf++ = *src++;
*buf++ = *src++;
--cCommas;
}
}

while (*src)
*buf++ = *src++;

*buf = NULL;

return res;
}


// 프로시저 부분
~~~~
int i, len, offset;
TCHAR szSrc[256], szTmp[256], szComma[256];

case IDC_EDIT_NUMBER:
switch (HIWORD(wParam))
{
case EN_CHANGE:
if (len = GetWindowText((HWND)lParam, szStr, 256))
{
// 셀의 위치를 기억했다가 컴마 찍은 후 위치 설정한다.
SendMessage((HWND)lParam, EM_GETSEL, NULL, (LPARAM)&i); // EN_CHANGE에서 커서는 한 지점(start, end값 동일)에 위치하므로 end 하나만

구해도 된다.
for (offset = 0; i < len; i++) // 보통 커서는 문자열 끝부분에 위치하므로 뒤에서부터 몇번째 위치인지 파악하는게 효율적이다.
{
if ((szStr[i] >= '0' && szStr[i] <= '9') || szStr[i] == '.' || szStr[i] == '-') // 숫자 관련 문자들 카운팅(컴마는 모두

지운 후 새로 찍으므로 제외)
offset++;
}

// 숫자 관련 문자들만 복사한다.(컴마는 새로 찍으므로 제외)
p = szTmp;
for (i = 0; i <len; i++)
{
if ((szStr[i] >= '0' && szStr[i] <= '9') || szStr[i] == '.' || szStr[i] == '-')
*p++ = szStr[i];
else if (szStr[i] != ',') // 숫자와 상관없는 문자인 경우 경보음을 내준다.
MessageBeep(0);
}
*p = NULL;

// 컴마 함수를 실행한다.
i = AddComma(szTmp, szComma, 256);

// ★ 반드시 문자열 비교하여 다른 경우에만 수행한다. 안 그러면 SetWindowText나 EM_REPLACESEL시 EN_CHANGE 메시지가 발생하므로 무한

루프에 빠진다.
if (0 != lstrcmp(szStr, szComma))
{
SetWindowText((HWND)lParam, szComma);

// 셀의 위치를 반영해준다.
if (offset)
{
for (i--; i >= 0; i--)
{
//if ((szComma[i] >= '0' && szComma[i] <= '9') || szComma[i] == '.' || szComma[i] == '-')
if (szComma[i] != ',') // 이전 단계에서 숫자와 관련되지 않은 잡문자들을 제거했기 때문에 간결한 식으로 처

리해도 된다.
offset--;

if (!offset) break;
}

// 컴마 경계의 경우 컴마 앞(숫자 뒤)에 커서를 위치시킨다. 123(커서),456와 123,(커서)456 중에 전자의 형식
// ★ 딜리트키(VK_DELETE) 누른 상태이면 커서를 컴마 뒤에 위치시켜야 다음 문자들을 삭제할 수 있다.
if (i > 0 && szComma[i - 1] == ',' && !(GetAsyncKeyState(VK_DELETE) & 0x8000))
i--;
}
SendMessage((HWND)lParam, EM_SETSEL, i, i);
}
}
break;
}
return TRUE;