zeerd's blog         Search     Categories     Tags     Feed

闲来生雅趣,无事乐逍遥。对窗相望雪,一盏茶香飘。

MinGW写的简易记事本 Ver0.02

#MinGW #Notepad #Windows @Program


Contents:

MinGW下编写的记事本,也可以认为是API编程,SDK编程什么的。反正是没使用MFC……我不是科班出身的,这些名词界限搞不懂……

使用-mwindows参数编译…

#define _UNICODE /* for std c */
#define UNICODE /* for windows api */

#include <stdio.h>
#include <time.h>
#include <tchar.h>
#include <sys/types.h>
#include <windowsx.h>
#include <windows.h>
#include <commdlg.h>
#include <commctrl.h>

/*============================================================================*/
/*DEBUG(Add the define below if need) */
#define DEBUG
VOID DEBUG_Output(TCHAR* lpszFormat, ...);

/*============================================================================*/
/*CTRL ID */
#define CTRL_ID_STATIC (0)
#define CTRL_ID_EDIT_TEXT (1)
#define CTRL_ID_EDIT_INPUT (2)
#define CTRL_ID_EDIT_REPLACE (3)
#define CTRL_ID_BTN_INPUT (4)
#define CTRL_ID_BTN_CANCEL (5)
#define CTRL_ID_STATUSBAR (6)

/*============================================================================*/
/*MENU ID */
#define MENU_ID_NEW (10)
#define MENU_ID_OPEN (11)
#define MENU_ID_SAVE (12)
#define MENU_ID_SAVEAS (13)
#define MENU_ID_EXIT (14)

#define MENU_ID_UNDO (20)
#define MENU_ID_COPY (21)
#define MENU_ID_PASTE (22)
#define MENU_ID_CUT (23)
#define MENU_ID_DEL (24)
#define MENU_ID_SELALL (25)
#define MENU_ID_GOTO (26)
#define MENU_ID_FIND (27)
#define MENU_ID_NEXT (28)
#define MENU_ID_REPLACE (29)
#define MENU_ID_DATE (30)

#define MENU_ID_WRET (40)

#define MENU_ID_ABOUT (50)

/*============================================================================*/
/* CONST DEFINE */
#define MAX_STRING (256)

/* The return value of the most APIs is limited in integer. */
/* So limit the max edit characters'' number to 65535 */
/* If need to deal with any files larger than 65535 bytes , */
/* Some complex arithmetics (i.e. column number) should to be added. */
#define MAX_EDIT (65535U)

/* One line''s one used for Goto and Find , two used for Repalce */
#define FLAG_INPUTDLG_ONELINE (0)
#define FLAG_INPUTDLG_TWOLINE (1)

#define FLAG_INPUTFLG_NONE (0)
#define FLAG_INPUTFLG_GOTO (1)
#define FLAG_INPUTFLG_FIND (2)
#define FLAG_INPUTFLG_REPLACE (3)

#ifdef UNICODE
#define SN_CF_TEXT (CF_UNICODETEXT)
#else
#define SN_CF_TEXT (CF_TEXT)
#endif

/*============================================================================*/
/* TYPEDEF */
typedef struct {
    UINT nID;
    TCHAR *pText;
} MenuString_st;

/*============================================================================*/
/* CONST VARIABLE */
const TCHAR *szClassName = TEXT("MySimpleNotepad");
const TCHAR *szCaptionMain = TEXT("Simple Notepad Ver0.02");
const TCHAR *szFind = TEXT("Find:");
const TCHAR *szReplace = TEXT("Replace:");
const TCHAR *szGoto = TEXT("Goto:");
const TCHAR *szError = TEXT("Error");
const TCHAR *szSaveModify = TEXT("Save the modify?");

const MenuString_st stMenuString[]= {
    {MENU_ID_NEW,TEXT("New a file.")},
    {MENU_ID_OPEN,TEXT("Open a file.")},
    {MENU_ID_SAVE,TEXT("Save the file.")},
    {MENU_ID_SAVEAS,TEXT("Save the texts to an other file.")}
};

/*============================================================================*/
/* VARIABLE */
HANDLE hInstance;
HWND hWinMain;
HWND hInputDlg;
HWND hWinStatus;

INT nInputDlgSts;

BOOL bUndo;
BOOL bWantReturn;

TCHAR cCrtFile[MAX_PATH];
TCHAR cInputEdit[MAX_STRING];

/*============================================================================*/
/* PROTOTYPE */
INT _WinMain(VOID);

LRESULT CALLBACK _ProcWinMain(HWND hWnd,DWORD uMsg,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK _ProcInputDlg(HWND hWnd,DWORD uMsg,WPARAM wParam,LPARAM lParam);
BOOL _PreTranslateMessage(MSG* pMsg);

HWND CreateEdit(DWORD dwStyle);
HWND CreatInputDlg(TCHAR* pTitle,INT nFlag);
HWND CreateStatusBar(VOID);
HMENU CreateMainMenu(VOID);
HACCEL CreateAccelerator(VOID);

VOID OnSetFocus(VOID);
VOID OnSize(VOID);
VOID OnAbout(VOID);
VOID OnQuit(VOID);
VOID OnNew(VOID);
VOID OnOpen(VOID);
VOID OnSave(INT nFlag);
VOID OnUndo(VOID);
VOID OnCopy(VOID);
VOID OnPaste(VOID);
VOID OnCut(VOID);
VOID OnDel(VOID);
VOID OnSelAll(VOID);
VOID OnWantReturn(VOID);
VOID OnGoto(INT nFlag);
VOID OnFind(INT nFlag);
VOID OnReplace(INT nFlag);
VOID OnDateTime(VOID);
VOID OnMenuSelect(UINT);

VOID OnEditCurMoved(VOID);

VOID OnInputDlgClose(HWND hWnd);
VOID OnInputDlgTab(VOID);

VOID ModifyWinTitle(VOID);
VOID OpenGivenFile(TCHAR *pFile);
BOOL Find(TCHAR tcFind[],DWORD *dwStart,DWORD *dwEnd);
BOOL Edit_GetSelText(HWND hWnd,TCHAR *pBuffer,INT nLenMax);

/*============================================================================*/
/* FUNCTION */
VOID ModifyWinTitle(VOID)
{
    TCHAR *cTitle;
    INT nSize = MAX_PATH + _tcslen(szCaptionMain)
                + 3 /* For the length of " - " */
                + 1; /* For the length of "*" */
    cTitle = malloc(nSize * sizeof(TCHAR));
    ZeroMemory(cTitle,nSize);
    _stprintf(cTitle,TEXT("%s%s - %s"),
              (_tcslen(cCrtFile)==0)?TEXT("Untitled"):cCrtFile,
              (Edit_GetModify(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT))?TEXT("*"):TEXT("")),
              szCaptionMain);
    SetWindowText(hWinMain,cTitle);
    free(cTitle);
}

VOID OnSetFocus(VOID)
{
    if(GetFocus() != GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT)) {
        SetFocus(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));
    }
}

VOID OnSize(VOID)
{
    RECT stRect;
    RECT stRectSts;

    /* Keep the status at the bottom of the dlg */
    MoveWindow(hWinStatus,0,0,0,0,TRUE);

    /* Adjust the size of the edit ctrl */
    GetClientRect(hWinMain,&stRect);
    GetWindowRect(hWinStatus,&stRectSts);

    MoveWindow(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),/
               0,0,
               stRect.right-stRect.left,
               stRect.bottom-stRect.top-stRectSts.bottom+stRectSts.top,
               FALSE);

    /* Adjust the width of each part in the status dynamically */
    DWORD dwStatusWidth[] = {0,-1};
    dwStatusWidth[0] = stRect.right/3*2;
    SendMessage(hWinStatus,SB_SETPARTS,2,(LPARAM)dwStatusWidth);
}

VOID OnAbout(VOID)
{
    MessageBox(NULL,TEXT("Copyleft (C) 2010/n/nCompiler:MinGW-5.1.6[gcc version 3.4.5 (mingw-vista special r3) (with option -mwindows)]/n/nComment:The max file size is 65535 Bytes."),TEXT("About..."),MB_OK);
}

VOID OnQuit(VOID)
{
    if(Edit_GetModify(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT))) {
        INT nID = MessageBox(NULL,szSaveModify,szCaptionMain,MB_OKCANCEL);
        if(IDOK == nID) {
            OnSave(0);
        }
    }
    else {
    }
    DestroyWindow(hWinMain);
    PostQuitMessage(0);
}

VOID OnNew(VOID)
{
    if(Edit_GetModify(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT))) {
        INT nID = MessageBox(NULL,szSaveModify,szCaptionMain,MB_OKCANCEL);
        if(IDOK == nID) {
            OnSave(0);
        }
    }
    else {
    }
    SetWindowText(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),TEXT(""));
}

VOID OpenGivenFile(TCHAR *pFile)
{
    HANDLE hFile;
    DWORD dwSize,Num;
    CHAR *lpMultiByteStr;
    TCHAR *lpWideCharStr;

    hFile=CreateFile(pFile,GENERIC_READ,0,NULL,OPEN_ALWAYS,
                     FILE_ATTRIBUTE_NORMAL,NULL);
    if(INVALID_HANDLE_VALUE!= hFile )
    {
        SetFilePointer(hFile,0,0,FILE_BEGIN);

        dwSize = GetFileSize(hFile,NULL);

        lpMultiByteStr = malloc((dwSize+1) * sizeof(CHAR)); /*NOT sure if need to reserve a byte for ''/0'' */
        ZeroMemory(lpMultiByteStr,(dwSize+1) * sizeof(CHAR));

#ifdef UNICODE
        /* The text file is based on ansi , but the edit ctrl is based on unicode. */
        /* So need to convert the ansi strings to unicode format. */
        lpWideCharStr = malloc((dwSize+1) * sizeof(TCHAR)); /*NOT sure if need to reserve a byte for ''/0'' */
        ZeroMemory(lpWideCharStr,(dwSize+1) * sizeof(TCHAR));

        ReadFile(hFile,lpMultiByteStr,dwSize,&Num,NULL);

        MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,
                            lpMultiByteStr,(dwSize) * sizeof(CHAR),
                            lpWideCharStr,(dwSize) * sizeof(TCHAR));
#else
        lpWideCharStr = lpMultiByteStr;
#endif

        Edit_SetText(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),lpWideCharStr);

        free(lpMultiByteStr);
#ifdef UNICODE
        free(lpWideCharStr);
#endif
        CloseHandle(hFile);
    }

    ModifyWinTitle();

    SetFocus(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));
}

VOID OnOpen(VOID)
{
    if(Edit_GetModify(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT))) {
        INT nID = MessageBox(NULL,szSaveModify,szCaptionMain,MB_OKCANCEL);
        if(IDOK == nID) {
            OnSave(0);
        }
    }
    else {
    }

    TCHAR *cFilter = TEXT("Non-Unicode Text File(*.txt)/0*.TXT/0");
    OPENFILENAME ofFile;

    memset(&ofFile,0,sizeof(OPENFILENAME));
    ofFile.lStructSize = sizeof(OPENFILENAME);
    ofFile.hwndOwner = hWinMain;
    ofFile.hInstance = hInstance;
    ofFile.lpstrFilter = cFilter;
    ofFile.lpstrTitle = TEXT("Open");
    ofFile.lpstrFile = cCrtFile;
    ofFile.nMaxFile = MAX_PATH;
    ofFile.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

    if(GetOpenFileName(&ofFile)) {
        OpenGivenFile(cCrtFile);
    }

}

VOID OnSave(INT nFlag)
{
    if((nFlag) || (_tcslen(cCrtFile)==0)) {
        TCHAR *cFilter = TEXT("Non-Unicode Text File(*.txt)/0*.TXT/0");
        OPENFILENAME ofFile;

        memset(&ofFile,0,sizeof(OPENFILENAME));
        ofFile.lStructSize = sizeof(OPENFILENAME);
        ofFile.hwndOwner = hWinMain;
        ofFile.hInstance = hInstance;
        ofFile.lpstrFilter = cFilter;
        ofFile.lpstrTitle = TEXT("Save As");
        ofFile.lpstrFile = cCrtFile;
        ofFile.nMaxFile = MAX_PATH;
        ofFile.lpstrDefExt = TEXT(".txt");
        ofFile.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

        GetSaveFileName(&ofFile);
    }
    else {
    }

    if(_tcslen(cCrtFile) != 0) {
        HANDLE hFile;
        DWORD dwSize,Num;
        CHAR *lpMultiByteStr;
        TCHAR *lpWideCharStr;
        hFile=CreateFile(cCrtFile,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
                         FILE_ATTRIBUTE_NORMAL,NULL);
        if(INVALID_HANDLE_VALUE!= hFile )
        {

            SetFilePointer(hFile,0,0,FILE_BEGIN);

            dwSize = Edit_GetTextLength(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT))+1;

            lpWideCharStr = malloc((dwSize) * sizeof(TCHAR));
            ZeroMemory(lpWideCharStr,(dwSize) * sizeof(TCHAR));

#ifdef UNICODE
            /* The text file is based on ansi , but the edit ctrl is based on unicode. */
            /* So need to convert the unicode strings to ansi format. */
            lpMultiByteStr = malloc((dwSize) * sizeof(CHAR));
            ZeroMemory(lpMultiByteStr,(dwSize) * sizeof(CHAR));

            Edit_GetText(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),lpWideCharStr,dwSize);

            WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,
                                lpWideCharStr,dwSize * sizeof(TCHAR),
                                lpMultiByteStr,dwSize * sizeof(CHAR),
                                NULL,NULL);
#else
            lpMultiByteStr = lpWideCharStr;
#endif

            WriteFile(hFile,lpMultiByteStr,dwSize,&Num,NULL);

#ifdef UNICODE
            free(lpMultiByteStr);
#endif
            free(lpWideCharStr);

            CloseHandle(hFile);

            Edit_SetModify(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),FALSE);
        }
    }

    ModifyWinTitle();
}

VOID OnUndo(VOID)
{
    Edit_Undo(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));
}

VOID OnCopy(VOID)
{
    TCHAR * pText;
    HANDLE hGlobal;
    PTSTR pGlobal ;
    DWORD dwGetSel;
    DWORD dwEditLen;

    dwGetSel = Edit_GetSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));
    dwEditLen = Edit_GetTextLength(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));

    pText = malloc((dwEditLen+1) * sizeof(TCHAR));
    Edit_GetText(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),pText,dwEditLen);

    hGlobal = GlobalAlloc (GHND | GMEM_SHARE, (HIWORD(dwGetSel) - LOWORD(dwGetSel) + 1) * sizeof (TCHAR)) ;
    pGlobal = GlobalLock (hGlobal) ;
    _tcsncpy (pGlobal, &pText[LOWORD(dwGetSel)],HIWORD(dwGetSel) - LOWORD(dwGetSel)) ;
    GlobalUnlock (hGlobal) ;
    OpenClipboard (hWinMain) ;
    EmptyClipboard () ;
    SetClipboardData (SN_CF_TEXT, hGlobal) ;
    CloseClipboard () ;
}

VOID OnPaste(VOID)
{
    HANDLE hGlobal;
    PTSTR pGlobal ;
    OpenClipboard (hWinMain) ;
    if (IsClipboardFormatAvailable (SN_CF_TEXT)) {
        hGlobal = GetClipboardData (SN_CF_TEXT);
        pGlobal = GlobalLock (hGlobal) ;
        Edit_ReplaceSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),pGlobal);
        GlobalUnlock (hGlobal) ;
    }
    CloseClipboard () ;

}

VOID OnCut(VOID)
{
    OnCopy();
    OnDel();
}

VOID OnDel(VOID)
{
    Edit_ReplaceSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),TEXT(""));
}

VOID OnSelAll(VOID)
{
    Edit_SetSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),
                0,
                Edit_GetTextLength(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT)));
}

VOID OnWantReturn(VOID)
{
    DWORD dwStyle;

    bWantReturn = !bWantReturn;

    dwStyle = GetWindowLong(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),GWL_STYLE);
    if(bWantReturn) {
        dwStyle &= (~(ES_AUTOHSCROLL|WS_HSCROLL)) ;
    }
    else {
        dwStyle |= (ES_AUTOHSCROLL)|(WS_HSCROLL) ;
    }

    /* Cound not change the style after an edit-ctrl-created. */
    /* So need to re-create the edit ctrl for style changing. */
    DestroyWindow(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));
    CreateEdit(dwStyle);
    if(_tcslen(cCrtFile) != 0) {
        OpenGivenFile(cCrtFile);
    }
}

VOID OnDateTime(VOID)
{
    time_t t;
    struct tm *newtime;
    TCHAR tcDate[MAX_STRING];

    time(&t);
    newtime = localtime( &t );
    _stprintf(tcDate,TEXT("%02d:%02d %04d-%02d-%02d"),/
              newtime->tm_hour,newtime->tm_min,/
              newtime->tm_year+1900,newtime->tm_mon+1, newtime->tm_mday
             );
    Edit_ReplaceSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),tcDate);
}

VOID OnEditCurMoved(VOID)
{
    INT nRow,nCol;
    TCHAR tcBuff[MAX_STRING];

    /* Use EM_LINEFROMCHAR to get the current line. */
    nRow = Edit_LineFromChar(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),-1);
    /* We could get the index of the current charactor in an edit ctrl (A)
    and the count of the charactors till the last line (B)
    So the value of A-B would be the column.*/
    nCol = HIWORD(Edit_GetSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT))) - /
           Edit_LineIndex(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),-1);
    _stprintf(tcBuff,TEXT("Ln %d, Col %d"),nRow+1,nCol+1);

    /* Used to update the Line and column info in status bar. */
    SendMessage(hWinStatus,SB_SETTEXT,1,(LPARAM)tcBuff);

}

VOID OnMenuSelect(UINT uItem)
{
    INT n,nIdCnt = sizeof(stMenuString)/sizeof(stMenuString[0]);

    /* Used to update the menu hint info in status bar. */
    for(n=0; n<nIdCnt; n++) {
        if(uItem == stMenuString[n].nID) {
            SendMessage(hWinStatus,SB_SETTEXT,0,(LPARAM)stMenuString[n].pText);
            break;
        }
    }

    if(n >= nIdCnt) {
        SendMessage(hWinStatus,SB_SETTEXT,0,(LPARAM)TEXT(""));
    }
}

LRESULT CALLBACK _ProcWinMain(HWND hWnd,DWORD uMsg,WPARAM wParam,LPARAM lParam)
{
    if(WM_SIZE == uMsg) {
        OnSize();
    }
    else if(WM_CLOSE == uMsg) {
        OnQuit();
    }
    else if(WM_SETFOCUS == uMsg) {
        OnSetFocus();
    }
    else if(WM_INITMENUPOPUP == uMsg) {
//if((UINT) LOWORD(lParam) == 1){
        BOOL bEnable;
        bEnable = (Edit_GetSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT)) != 0) ? MF_ENABLED : MF_GRAYED ;
        EnableMenuItem ((HMENU) wParam, MENU_ID_UNDO, bUndo ? MF_ENABLED : MF_GRAYED) ;
        EnableMenuItem ((HMENU) wParam, MENU_ID_PASTE, IsClipboardFormatAvailable (SN_CF_TEXT) ? MF_ENABLED : MF_GRAYED) ;
        EnableMenuItem ((HMENU) wParam, MENU_ID_CUT, bEnable) ;
        EnableMenuItem ((HMENU) wParam, MENU_ID_COPY, bEnable) ;
        EnableMenuItem ((HMENU) wParam, MENU_ID_DEL, bEnable) ;
//}

        CheckMenuItem((HMENU) wParam,MENU_ID_WRET, bWantReturn?MF_CHECKED:MF_UNCHECKED);
    }
    else if(WM_MENUSELECT == uMsg) {
        OnMenuSelect((UINT) LOWORD(wParam));
    }
    else if(WM_DROPFILES == uMsg) {
        TCHAR lpszFileName[MAX_PATH];
        DragQueryFile((HANDLE)wParam, 0, lpszFileName, MAX_PATH);
        OpenGivenFile(lpszFileName);
        _tcscpy(cCrtFile,lpszFileName);
    }
    else if((WM_COMMAND == uMsg)/*&&(HIWORD(wParam) == BN_CLICKED)*/) {
        WORD wNotifyCode = HIWORD(wParam); // notification code
        WORD wID = LOWORD(wParam); // item, control, or accelerator identifier
        HWND hCtl = (HWND) lParam; // handle of control
        /*if(HIWORD(wParam) == BN_CLICKED){*/
        switch(wID) {
        case MENU_ID_ABOUT:
            OnAbout();
            break;
        case MENU_ID_EXIT:
            OnQuit();
            break;
        case MENU_ID_NEW:
            OnNew();
            break;
        case MENU_ID_OPEN:
            OnOpen();
            break;
        case MENU_ID_SAVE:
            OnSave(0);
            break;
        case MENU_ID_SAVEAS:
            OnSave(1);
            break;
        case MENU_ID_UNDO:
            OnUndo();
            break;
        case MENU_ID_COPY:
            OnCopy();
            break;
        case MENU_ID_PASTE:
            OnPaste();
            break;
        case MENU_ID_CUT:
            OnCut();
            break;
        case MENU_ID_DEL:
            OnDel();
            break;
        case MENU_ID_SELALL:
            OnSelAll();
            break;
        case MENU_ID_WRET:
            OnWantReturn();
            break;
        case MENU_ID_GOTO:
            OnGoto(0);
            break;
        case MENU_ID_FIND:
            OnFind(0);
            break;
        case MENU_ID_NEXT:
            OnFind(1);
            break;
        case MENU_ID_REPLACE:
            OnReplace(0);
            break;
        case MENU_ID_DATE:
            OnDateTime();
            break;
        default:
            DefWindowProc(hWnd,uMsg,wParam,lParam);
            break;
        }
        /*}
        else */if(EN_CHANGE == HIWORD(wParam)) {
            ModifyWinTitle();
            bUndo = TRUE;
        }
        else {
            DefWindowProc(hWnd,uMsg,wParam,lParam);
        }

    }
    else {
        DefWindowProc(hWnd,uMsg,wParam,lParam);
    }

}

VOID OnGoto(INT nFlag)
{
    INT nLine;
    TCHAR cBuff[MAX_STRING];

    if(0 == nFlag) {
        nInputDlgSts = FLAG_INPUTFLG_GOTO;
        CreatInputDlg(TEXT("Goto:"),FLAG_INPUTDLG_ONELINE);
        /* Set the current line number to default */
        nLine = Edit_LineFromChar(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),-1) + 1;
        _itot(nLine,cBuff,10);
        Edit_SetText(GetDlgItem(hInputDlg,CTRL_ID_EDIT_INPUT),cBuff);
    }
    else {
        Edit_GetText(GetDlgItem(hInputDlg,CTRL_ID_EDIT_INPUT),cInputEdit,MAX_STRING);
        nLine = Edit_LineIndex(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),_ttoi(cInputEdit)-1);
        Edit_SetSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),nLine,nLine);
        SetFocus(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));
    }
}

BOOL Find(TCHAR tcFind[],DWORD *dwStart,DWORD *dwEnd)
{
    DWORD dwSize,Num;
    DWORD dwFindStart;
    TCHAR *cBuffer;
    BOOL bRet = FALSE;

    dwSize = Edit_GetTextLength(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT))+1;
    cBuffer = malloc(dwSize * sizeof(TCHAR));
    ZeroMemory(cBuffer,dwSize * sizeof(TCHAR));

    Edit_GetText(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),cBuffer,dwSize);

    dwFindStart = Edit_GetSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));
    dwFindStart = HIWORD(dwFindStart);

    if(dwFindStart < dwSize) {
        TCHAR *pFound = _tcsstr(cBuffer+dwFindStart,tcFind);
        if(pFound != NULL) {
            *dwStart = (DWORD)pFound - (DWORD)cBuffer;
            *dwEnd = *dwStart + _tcslen(tcFind);
            bRet = TRUE;
        }
    }
    free(cBuffer);

    return bRet;
}

BOOL Edit_GetSelText(HWND hWnd,TCHAR *pBuffer,INT nLenMax)
{
    BOOL bRet = TRUE;

    if(Edit_GetSel(hWnd) != 0) {
        TCHAR * pText;
        DWORD dwGetSel;
        DWORD dwEditLen;
        DWORD dwSelLen;

        dwGetSel = Edit_GetSel(hWnd);
        dwEditLen = Edit_GetTextLength(hWnd);

        pText = malloc((dwEditLen+1) * sizeof(TCHAR));
        Edit_GetText(hWnd,pText,dwEditLen);

        dwSelLen = HIWORD(dwGetSel) - LOWORD(dwGetSel);
        dwSelLen = (nLenMax > dwSelLen)?dwSelLen:nLenMax;

        _tcsncpy (pBuffer, &pText[LOWORD(dwGetSel)],dwSelLen) ;
    }
    else {
        bRet = FALSE;
    }
}

VOID OnFind(INT nFlag)
{
    static TCHAR cString[MAX_STRING] = TEXT("");

    DWORD dwStart,dwEnd;

    if(0 == nFlag) {
        nInputDlgSts = FLAG_INPUTFLG_FIND;
        CreatInputDlg(_T("Find:"),FLAG_INPUTDLG_ONELINE);
        /* Set the selected text to default */
        Edit_GetSelText(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),cString,MAX_STRING);
        Edit_SetText(GetDlgItem(hInputDlg,CTRL_ID_EDIT_INPUT),cString);
    }
    else {
        if(GetDlgItem(hInputDlg,CTRL_ID_EDIT_INPUT)!=NULL) {
            Edit_GetText(GetDlgItem(hInputDlg,CTRL_ID_EDIT_INPUT),cString,MAX_STRING);
        }

        BOOL bFind = Find(cString,&dwStart,&dwEnd);

        if(bFind) {
            Edit_SetSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),dwStart,dwEnd);
        }
        else {
            TCHAR tcCaution[MAX_STRING] = TEXT("Can not find /"");
                                               _tcscat(tcCaution,cString);
                                               _tcscat(tcCaution,TEXT("/"!"));
                                                       MessageBox(NULL,tcCaution,szCaptionMain,MB_OK);
        }

    }
}

                              VOID OnReplace(INT nFlag)
{
    static TCHAR cFind[MAX_STRING] = TEXT("");
    static TCHAR cReplace[MAX_STRING] = TEXT("");

    DWORD dwStart,dwEnd;

    if(0 == nFlag) {
        nInputDlgSts = FLAG_INPUTFLG_REPLACE;
        CreatInputDlg(_T("Replace:"),FLAG_INPUTDLG_TWOLINE);
        /* Set the selected text to default */
        Edit_GetSelText(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),cFind,MAX_STRING);
        Edit_SetText(GetDlgItem(hInputDlg,CTRL_ID_EDIT_INPUT),cFind);
        Edit_SetText(GetDlgItem(hInputDlg,CTRL_ID_EDIT_REPLACE),cReplace);
    }
    else {
        Edit_GetText(GetDlgItem(hInputDlg,CTRL_ID_EDIT_INPUT),cFind,MAX_STRING);
        Edit_GetText(GetDlgItem(hInputDlg,CTRL_ID_EDIT_REPLACE),cReplace,MAX_STRING);

        BOOL bFind = Find(cFind,&dwStart,&dwEnd);

        if(bFind) {
            Edit_SetSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),dwStart,dwEnd);
            Edit_ReplaceSel(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT),cReplace);
        }
        else {
            TCHAR tcCaution[MAX_STRING] = TEXT("Can not find /"");
                                               _tcscat(tcCaution,cFind);
                                               _tcscat(tcCaution,TEXT("/"!"));
                                                       MessageBox(NULL,tcCaution,szCaptionMain,MB_OK);
        }

    }
}

                              VOID OnInputDlgClose(HWND hWnd)
{
    nInputDlgSts = FLAG_INPUTFLG_NONE;
    DestroyWindow(hWnd);
    hInputDlg = NULL;
    SetFocus(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));
}

VOID OnInputDlgTab(VOID)
{
    /* Tab order */
    INT nIdTab[][2]= {
        {CTRL_ID_EDIT_INPUT,CTRL_ID_EDIT_REPLACE },
        {CTRL_ID_EDIT_REPLACE,CTRL_ID_BTN_INPUT },
        {CTRL_ID_BTN_INPUT,CTRL_ID_BTN_CANCEL },
        {CTRL_ID_BTN_CANCEL,CTRL_ID_EDIT_INPUT }
    };
    INT n,nIdCnt = sizeof(nIdTab)/sizeof(nIdTab[0]);

    INT nID = GetWindowLong(GetFocus(),GWL_ID);

    for(n=0; n<nIdCnt; n++) {
        if(nID == nIdTab[n][0]) {
            if(GetDlgItem(hInputDlg,nIdTab[n][1])!= NULL) {
                SetFocus(GetDlgItem(hInputDlg,nIdTab[n][1]));
                break;
            }
            else {
                nID = nIdTab[n][1];
            }
        }
    }
}

LRESULT CALLBACK _ProcInputDlg(HWND hWnd,DWORD uMsg,WPARAM wParam,LPARAM lParam)
{
    if(WM_PAINT == uMsg) {
        UpdateWindow(hWnd);
    }
    else if(WM_CLOSE == uMsg) {
        OnInputDlgClose(hWnd);
    }
    else if(WM_KEYDOWN == uMsg) {
        INT nVirtKey =(INT) wParam;

        if(nVirtKey == VK_ESCAPE) {
            SendMessage(hWnd,WM_COMMAND,MAKELONG(CTRL_ID_BTN_CANCEL,BN_CLICKED),CTRL_ID_BTN_INPUT);
        }
        else if(nVirtKey == VK_RETURN) {
            SendMessage(hWnd,WM_COMMAND,MAKELONG(CTRL_ID_BTN_INPUT,BN_CLICKED),CTRL_ID_BTN_INPUT);
        }
    }
    else if((WM_COMMAND == uMsg)/*&&(HIWORD(wParam) == BN_CLICKED)*/) {
        WORD wNotifyCode = HIWORD(wParam); // notification code
        WORD wID = LOWORD(wParam); // item, control, or accelerator identifier
        HWND hCtl = (HWND) lParam; // handle of control
        switch(wID) {
        case CTRL_ID_BTN_INPUT:
            if(FLAG_INPUTFLG_GOTO == nInputDlgSts) {
                OnGoto(1);
                OnInputDlgClose(hWnd);
            }
            else if(FLAG_INPUTFLG_FIND == nInputDlgSts) {
                OnFind(1);
            }
            else if(FLAG_INPUTFLG_REPLACE == nInputDlgSts) {
                OnReplace(1);
            }
            else {
            }
            break;
        case CTRL_ID_BTN_CANCEL:
            OnInputDlgClose(hWnd);
            break;
        default:
            DefWindowProc(hWnd,uMsg,wParam,lParam);
            break;
        }
    }
    else {
//DefWindowProc(hWnd,uMsg,wParam,lParam);
    }

    DefWindowProc(hWnd,uMsg,wParam,lParam);
}

HMENU CreateMainMenu(VOID)
{
    HMENU hMenu = CreateMenu();
    HMENU hFile = CreateMenu();
    HMENU hEdit = CreateMenu();
    HMENU hForm = CreateMenu();
    HMENU hHelp = CreateMenu();

    if(hMenu&&hFile) {
        AppendMenu(hFile,MF_STRING,MENU_ID_NEW,TEXT("&New Ctrl + N"));
        AppendMenu(hFile,MF_STRING,MENU_ID_OPEN,TEXT("&Open... Ctrl + O"));
        AppendMenu(hFile,MF_STRING,MENU_ID_SAVE,TEXT("&Save Ctrl + S"));
        AppendMenu(hFile,MF_STRING,MENU_ID_SAVEAS,TEXT("Save &As..."));
        AppendMenu(hFile,MF_SEPARATOR,0,TEXT(""));
        AppendMenu(hFile,MF_STRING,MENU_ID_EXIT,TEXT("E&xit"));

        AppendMenu(hEdit,MF_STRING,MENU_ID_UNDO,TEXT("&Undo Ctrl + Z"));
        AppendMenu(hEdit,MF_SEPARATOR,0,TEXT(""));
        AppendMenu(hEdit,MF_STRING,MENU_ID_CUT,TEXT("Cu&t Ctrl + X"));
        AppendMenu(hEdit,MF_STRING,MENU_ID_COPY,TEXT("&Copy Ctrl + C"));
        AppendMenu(hEdit,MF_STRING,MENU_ID_PASTE,TEXT("&Paste Ctrl + V"));
        AppendMenu(hEdit,MF_STRING,MENU_ID_DEL,TEXT("De&l Del"));
        AppendMenu(hEdit,MF_SEPARATOR,0,TEXT(""));
        AppendMenu(hEdit,MF_STRING,MENU_ID_FIND,TEXT("&Find Ctrl + F"));
        AppendMenu(hEdit,MF_STRING,MENU_ID_NEXT,TEXT("Find &Next F3"));
        AppendMenu(hEdit,MF_STRING,MENU_ID_REPLACE,TEXT("&Replace Ctrl + H"));
        AppendMenu(hEdit,MF_STRING,MENU_ID_GOTO,TEXT("&Goto Ctrl + G"));
        AppendMenu(hEdit,MF_SEPARATOR,0,TEXT(""));
        AppendMenu(hEdit,MF_STRING,MENU_ID_SELALL,TEXT("Select &All Ctrl + A"));
        AppendMenu(hEdit,MF_STRING,MENU_ID_DATE,TEXT("&Date/Time F5"));

        AppendMenu(hForm,MF_STRING,MENU_ID_WRET,TEXT("&Want Return"));

        AppendMenu(hHelp,MF_STRING,MENU_ID_ABOUT,TEXT("&About"));

        AppendMenu(hMenu,MF_POPUP,(UINT)hFile,TEXT("&File"));
        AppendMenu(hMenu,MF_POPUP,(UINT)hEdit,TEXT("&Edit"));
        AppendMenu(hMenu,MF_POPUP,(UINT)hForm,TEXT("F&ormat"));
        AppendMenu(hMenu,MF_POPUP,(UINT)hHelp,TEXT("&Help"));
    }

    return hMenu;
}

HACCEL CreateAccelerator(VOID)
{
    ACCEL accel[] =
    {
        { FVIRTKEY, VK_F3, MENU_ID_NEXT },
        { FVIRTKEY, VK_F5, MENU_ID_DATE },

        { FCONTROL | FVIRTKEY, TEXT(''S''), MENU_ID_SAVE },
        { FCONTROL | FVIRTKEY, TEXT(''N''), MENU_ID_NEW },
        { FCONTROL | FVIRTKEY, TEXT(''O''), MENU_ID_OPEN },
        { FCONTROL | FVIRTKEY, TEXT(''C''), MENU_ID_COPY },
        { FCONTROL | FVIRTKEY, TEXT(''V''), MENU_ID_PASTE },
        { FCONTROL | FVIRTKEY, TEXT(''X''), MENU_ID_CUT },
        { FCONTROL | FVIRTKEY, TEXT(''Z''), MENU_ID_UNDO },
        { FCONTROL | FVIRTKEY, TEXT(''A''), MENU_ID_SELALL },
        { FCONTROL | FVIRTKEY, TEXT(''F''), MENU_ID_FIND },
        { FCONTROL | FVIRTKEY, TEXT(''H''), MENU_ID_REPLACE },
        { FCONTROL | FVIRTKEY, TEXT(''G''), MENU_ID_GOTO },
    };
    INT nCnt = sizeof(accel)/sizeof(accel[0]);

    return CreateAcceleratorTable(accel, nCnt);

}

HWND CreateEdit(DWORD dwStyle)
{
    HWND hWnd;
    RECT rect;

    GetClientRect(hWinMain,&rect);
    hWnd = CreateWindow(TEXT("Edit"),TEXT(""),/
                        dwStyle,/
                        0,0,rect.right-rect.left,rect.bottom-rect.top,/
                        hWinMain,(HMENU)CTRL_ID_EDIT_TEXT,hInstance,NULL);
    Edit_CanUndo(hWnd);
    SendMessage(hWnd,EM_SETLIMITTEXT,MAX_EDIT,0);
}

HWND CreateStatusBar(VOID)
{
    hWinStatus = CreateWindowEx(0,
                                TEXT("msctls_statusbar32"),NULL,/
                                WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP,/
                                0,0,0,0,/
                                hWinMain,(HMENU)CTRL_ID_STATUSBAR,hInstance,NULL);
}

HWND CreatInputDlg(TCHAR* pTitle,INT nFlag)
{
    TCHAR *pClassName = TEXT("MyInputDlg");
    const TCHAR *pStatic = szError;

    WNDCLASSEX stWndClass;
    MSG stMsg;
    HWND hNewWnd;
    RECT rect;

    INT nDlgX = 200;
    INT nDlgY = 200;
    INT nDlgW = 160;
    INT nDlgH = 120;

    INT nCtrlH = 24;
    INT nStaticX = 10;
    INT nEditX = 50;
    INT nLine1Y = 15;
    INT nLine2Y = 50;
    INT nStaticW = 35;
    INT nEditW = 90;

    INT nButtonW = 60;
    INT nButtonY = 50;
    INT nButton1X = 10;
    INT nButton2X = nDlgW - nButtonW - nButton1X*2;

    if(FLAG_INPUTDLG_TWOLINE == nFlag) {
        nDlgH += (10 + nCtrlH);
        nButtonY += (10 + nCtrlH);
        nStaticW += 35;
        nEditX += 35;
        nDlgW += 35;
        nButton2X += 35;
    }

    RtlZeroMemory(&stWndClass,sizeof(stWndClass));
    stWndClass.hCursor = LoadCursor(0,IDC_ARROW);
    stWndClass.hInstance = GetModuleHandle(NULL);
    stWndClass.cbSize = sizeof(WNDCLASSEX);
    stWndClass.style = CS_HREDRAW | CS_VREDRAW;
    stWndClass.lpfnWndProc = (WNDPROC)_ProcInputDlg;
    stWndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    stWndClass.lpszClassName = pClassName;

    RegisterClassEx(&stWndClass);

    hInputDlg = CreateWindowEx(WS_EX_CLIENTEDGE,
                               pClassName,pTitle,/
                               WS_OVERLAPPEDWINDOW,/
                               nDlgX,nDlgY,nDlgW,nDlgH,/
                               NULL,NULL,hInstance,NULL);

    if(FLAG_INPUTFLG_GOTO == nInputDlgSts) {
        pStatic = szGoto;
    }
    else if(FLAG_INPUTFLG_FIND == nInputDlgSts) {
        pStatic = szFind;
    }
    else if(FLAG_INPUTFLG_REPLACE == nInputDlgSts) {
        pStatic = szFind;
    }
    else {
    }
    hNewWnd = CreateWindow(TEXT("Static"),pStatic,/
                           WS_CHILDWINDOW|WS_VISIBLE|WS_BORDER,/
                           nStaticX,nLine1Y,nStaticW,nCtrlH,/
                           hInputDlg,(HMENU)CTRL_ID_STATIC,hInstance,NULL);
    ShowWindow(hNewWnd,SW_SHOWNORMAL);

    hNewWnd = CreateWindow(TEXT("Edit"),TEXT(""),/
                           WS_CHILDWINDOW|WS_VISIBLE|WS_BORDER|WS_TABSTOP,/
                           nEditX,nLine1Y,nEditW,nCtrlH,/
                           hInputDlg,(HMENU)CTRL_ID_EDIT_INPUT,hInstance,NULL);
    SetFocus(hNewWnd);
    ShowWindow(hNewWnd,SW_SHOWNORMAL);

    if(FLAG_INPUTDLG_TWOLINE == nFlag) {
        hNewWnd = CreateWindow(TEXT("Static"),szReplace,/
                               WS_CHILDWINDOW|WS_VISIBLE|WS_BORDER,/
                               nStaticX,nLine2Y,nStaticW,nCtrlH,/
                               hInputDlg,(HMENU)CTRL_ID_STATIC,hInstance,NULL);
        ShowWindow(hNewWnd,SW_SHOWNORMAL);

        hNewWnd = CreateWindow(TEXT("Edit"),TEXT(""),/
                               WS_CHILDWINDOW|WS_VISIBLE|WS_BORDER|WS_TABSTOP,/
                               nEditX,nLine2Y,nEditW,nCtrlH,/
                               hInputDlg,(HMENU)CTRL_ID_EDIT_REPLACE,hInstance,NULL);
        ShowWindow(hNewWnd,SW_SHOWNORMAL);
    }

    hNewWnd = CreateWindow(TEXT("Button"),TEXT("&OK"),/
                           WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|WS_TABSTOP,/
                           nButton1X,nButtonY,nButtonW,nCtrlH,/
                           hInputDlg,(HMENU)CTRL_ID_BTN_INPUT,hInstance,NULL);
    ShowWindow(hNewWnd,SW_SHOWNORMAL);

    hNewWnd = CreateWindow(TEXT("Button"),TEXT("&Cancel"),/
                           WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|WS_TABSTOP,/
                           nButton2X,nButtonY,nButtonW,nCtrlH,/
                           hInputDlg,(HMENU)CTRL_ID_BTN_CANCEL,hInstance,NULL);
    ShowWindow(hNewWnd,SW_SHOWNORMAL);

    ShowWindow(hInputDlg,SW_SHOWNORMAL);
    UpdateWindow(hInputDlg);

    return hInputDlg;
}

BOOL _PreTranslateMessage(MSG* pMsg)
{
    BOOL bRet = TRUE;
    if((NULL != hInputDlg)&&(WM_KEYDOWN == pMsg->message)) {
        if((VK_RETURN == pMsg->wParam)||
                (VK_ESCAPE == pMsg->wParam)
          ) {
            SendMessage(hInputDlg,pMsg->message,pMsg->wParam,pMsg->lParam);
        }
        else if(VK_TAB == pMsg->wParam) {
            OnInputDlgTab();
            bRet = FALSE;
        }
    }

    if((GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT) == pMsg->hwnd)&&
            ((WM_KEYUP == pMsg->message)||(WM_LBUTTONUP == pMsg->message))) {
        OnEditCurMoved();
    }

    if(WM_DROPFILES == pMsg->message) {
        SendMessage(hWinMain,pMsg->message,pMsg->wParam,pMsg->lParam);
    }

    return bRet;
}

INT _WinMain(VOID)
{
    MSG stMsg;
    HWND hNewWnd;
    HMENU hMenu;
    HACCEL hAccelerator;
    WNDCLASSEX stWndClass;

    hMenu = CreateMainMenu();
    hAccelerator = CreateAccelerator();
    hInstance = GetModuleHandle(NULL);

    memset(&stWndClass,0,sizeof(stWndClass));
    stWndClass.hCursor = LoadCursor(0,IDC_ARROW);
    stWndClass.hInstance = hInstance;
    stWndClass.cbSize = sizeof(WNDCLASSEX);
    stWndClass.style = CS_HREDRAW | CS_VREDRAW;
    stWndClass.lpfnWndProc = (WNDPROC)_ProcWinMain;
    stWndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    stWndClass.lpszClassName = szClassName;

    RegisterClassEx(&stWndClass);

    hWinMain = CreateWindowEx(WS_EX_CLIENTEDGE|WS_EX_ACCEPTFILES,/
                              szClassName,szCaptionMain,/
                              WS_OVERLAPPEDWINDOW,/
                              100,100,600,400,/
                              NULL,hMenu,hInstance,hAccelerator);
    ModifyWinTitle();

    hNewWnd = CreateEdit(WS_CHILDWINDOW|WS_VISIBLE|ES_MULTILINE|ES_WANTRETURN|/
                         ES_AUTOHSCROLL|ES_AUTOVSCROLL|WS_VSCROLL|WS_HSCROLL);
    SetFocus(GetDlgItem(hWinMain,CTRL_ID_EDIT_TEXT));

    CreateStatusBar();

    OpenGivenFile(cCrtFile);

    ShowWindow(hWinMain,SW_SHOWNORMAL);
    UpdateWindow(hWinMain);

    while(TRUE) {

        if(GetMessage(&stMsg,NULL,0,0)==0) {
            break;
        }

        if(!(TranslateAccelerator(hWinMain,hAccelerator,&stMsg))) {
            TranslateMessage(&stMsg);
            if(_PreTranslateMessage(&stMsg)) {
                DispatchMessage(&stMsg);
            }
        }

    }

    return 0;

}

/* There is no wmain in mingw 5.1.6 */
/* I find a "#if 0" in tchar.h , but don''t know why ... */
INT main(INT argc, TCHAR *argv[ ], CHAR *env[])
{
    bUndo = FALSE;
    bWantReturn = FALSE;

    hInputDlg = NULL;
    hInstance = NULL;
    hWinMain = NULL;
    hWinStatus = NULL;

    nInputDlgSts = FLAG_INPUTFLG_NONE;

    memset(cCrtFile,0,sizeof(cCrtFile));
    memset(cInputEdit,0,sizeof(cInputEdit));

    if(argc > 1) {
#ifdef UNICODE
        /* The command line is in ansi format , need convert. */
        INT nSize = _tcslen(argv[1])* sizeof(TCHAR);
        MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,
                            (CHAR*)argv[1],nSize,
                            cCrtFile,nSize * sizeof(TCHAR));
#else
        _tcscpy(cCrtFile,argv[1]);
#endif
    }

    _WinMain();
    ExitProcess(0);

    return 0;
}

VOID DEBUG_Output(TCHAR* lpszFormat, ...)
{
#ifdef DEBUG
    TCHAR tcBuff[MAX_STRING];
    va_list args;

    va_start(args, lpszFormat);
    _vstprintf(tcBuff,lpszFormat,args);
    va_end(args);

    MessageBox(NULL,tcBuff,TEXT("DEBUG"),MB_OK);
#endif
}

PS:这么长的代码几乎没有注释……汗啊!