TyperZJ 文本编辑器——Windows编程上机作业之四

TyperZJ 文本编辑器——Windows编程上机作业之四

要求:
1. 改正typer程序中的不足
2. 增加读、写文件功能
3. 使用鼠标指定插入点
4. 在窗口标题中显示该程序运行的总时间(每秒更新一次)
5. 增加垂直滚动条,处理相应的操作。


实现:
1.水平滚动条与垂直滚动条都有,可使用鼠标滚轮,文件宽高都可多页(编辑过程中Caret移出窗口时,窗口会自动滚动);
2.可以新建,打开,编辑,保存,另存为文件(未考虑实际的文件编码,而是使用自定义的文件格式);
3.使用鼠标指定插入点;
4.文件修改未保存时新建或关闭会有询问;
5.窗口标题栏显示正在编辑的文件名,及程序运行总时间;
6.支持中英文混合编辑;







源码:
resource.h

 1 // {{NO_DEPENDENCIES}}
 2 //  Microsoft Visual C++ generated include file.
 3 //  Used by TyperZJ.rc
 4 //
 5 #define  IDR_MENU                        101
 6 #define  IDR_ACCE                        102
 7
 8 //  Next default values for new objects
 9 //  
10 #ifdef APSTUDIO_INVOKED
11 #ifndef APSTUDIO_READONLY_SYMBOLS
12 #define  _APS_NEXT_RESOURCE_VALUE        103
13 #define  _APS_NEXT_COMMAND_VALUE         40003
14 #define  _APS_NEXT_CONTROL_VALUE         1001
15 #define  _APS_NEXT_SYMED_VALUE           101
16 #endif
17 #endif
18



TyperZJ.rc

 1 //  Microsoft Visual C++ generated resource script.
 2 //
 3 #include  " resource.h "
 4
 5 #define  APSTUDIO_READONLY_SYMBOLS
 6 /**/ /////////////////////////////////////////////////////////////////////////////
 7 //
 8 //  Generated from the TEXTINCLUDE 2 resource.
 9 //
10 #include  " afxres.h "
11
12 /**/ /////////////////////////////////////////////////////////////////////////////
13 #undef  APSTUDIO_READONLY_SYMBOLS
14
15 /**/ /////////////////////////////////////////////////////////////////////////////
16 //  Chinese (Simplified, PRC) resources
17
18 #if  !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
19 LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
20
21 #ifdef APSTUDIO_INVOKED
22 /**/ /////////////////////////////////////////////////////////////////////////////
23 //
24 //  TEXTINCLUDE
25 //
26
27 1  TEXTINCLUDE 
28 BEGIN
29      " resource.h\0 "
30 END
31
32 2  TEXTINCLUDE 
33 BEGIN
34      " #include  "" afxres.h "" \r\n "
35      " \0 "
36 END
37
38 3  TEXTINCLUDE 
39 BEGIN
40      " \r\n "
41      " \0 "
42 END
43
44 #endif      //  APSTUDIO_INVOKED
45
46
47 /**/ /////////////////////////////////////////////////////////////////////////////
48 //
49 //  Menu
50 //
51
52 IDR_MENU MENU
53 BEGIN
54     POPUP  " 文件(&F) "
55     BEGIN
56         MENUITEM  " 新建(&N)\tCtrl+N " ,              ID_FILE_NEW
57         MENUITEM  " 打开(&O)\tCtrl+O " ,              ID_FILE_OPEN
58         MENUITEM  " 保存(&S)\tCtrl+S " ,              ID_FILE_SAVE
59         MENUITEM  " 另存为(&A) " ,                    ID_FILE_SAVE_AS
60         MENUITEM SEPARATOR
61         MENUITEM  " 退出(&X)\tCtrl+X " ,              ID_FILE_CLOSE
62     END
63 END
64
65
66 /**/ /////////////////////////////////////////////////////////////////////////////
67 //
68 //  Accelerator
69 //
70
71 IDR_ACCE  ACCELERATORS
72 BEGIN
73      " N " ,            ID_FILE_NEW,            VIRTKEY, CONTROL, NOINVERT
74      " O " ,            ID_FILE_OPEN,           VIRTKEY, CONTROL, NOINVERT
75      " S " ,            ID_FILE_SAVE,           VIRTKEY, CONTROL, NOINVERT
76      " X " ,            ID_FILE_CLOSE,          VIRTKEY, CONTROL, NOINVERT
77 END
78
79 #endif      //  Chinese (Simplified, PRC) resources
80 /**/ /////////////////////////////////////////////////////////////////////////////
81
82
83
84 #ifndef APSTUDIO_INVOKED
85 /**/ /////////////////////////////////////////////////////////////////////////////
86 //
87 //  Generated from the TEXTINCLUDE 3 resource.
88 //
89
90
91 /**/ /////////////////////////////////////////////////////////////////////////////
92 #endif      //  not APSTUDIO_INVOKED
93
94



TyperZJ.cpp

  1 #ifndef  UNICODE
  2 #define   UNICODE
  3 #endif
  4 #ifndef  _UNICODE
  5 #define   _UNICODE
  6 #endif
  7
  8
  9 #include  " resource.h "
 10 #include  " afxres.h "
 11 #include  < windows.h >
 12
 13 #include  < list >
 14 using  std::list;
 15
 16
 17 //  半角,全角字符宽高硬编码
 18 #define   CX_HALF  8
 19 #define   CY_HALF  16
 20 #define   CX_FULL  (CX_HALF+CX_HALF)
 21 #define   CY_FULL  CY_HALF
 22
 23
 24 /**/ //////// 文本
 25 //  内存中不保存回车换行,内存中的文本至少一行,会有空行
 26
 27 typedef list <  TCHAR  >  TLINE;
 28 typedef list <  TLINE  >  TTEXT;
 29 typedef TTEXT::iterator   TPOSLINE;
 30 typedef TLINE::iterator   TPOSCHAR;
 31
 32          //  当前行中当前字符宽度
 33 inline INT GetDeltaX(  const  TLINE  & line,  const  TPOSCHAR  & pc )  {
 34        return ( ((pc==line.end())||(((UINT)(*pc))<0x80)) ? CX_HALF : CX_FULL );
 35}

 36          //  当前行中当前字符左侧 X 坐标
 37 INT GetX( TLINE  & line,  const  TPOSCHAR  & pc )  {
 38        TPOSCHAR p;
 39        INT x = 0;
 40        for ( p = line.begin(); (p!=line.end())&&(p!=pc); ++p ) {
 41                x += ::GetDeltaX( line, p );
 42        }

 43        return x;
 44}

 45          //  当前行中当前 X 坐标右侧字符
 46 TPOSCHAR GetPoschar( TLINE  & line, INT x )  {
 47        TPOSCHAR p;
 48        for ( p = line.begin(); (p!=line.end())&&(x>0); ++p ) {
 49                x -= ::GetDeltaX( line, p );
 50        }

 51        return p;
 52}

 53          //  当前行中当前字符高度
 54 inline INT GetDeltaY(  const  TLINE  & line,  const  TPOSCHAR  & pc )  {
 55        return ( ((pc==line.end())||(((UINT)(*pc))<0x80)) ? CY_HALF : CY_FULL );
 56}

 57          //  当前文本中当前行高度
 58 inline INT GetDeltaY(  const  TTEXT  & text,  const  TPOSLINE  & pl )  {
 59        return ( (pl==text.end()) ? (CY_HALF) : (::GetDeltaY(*pl,pl->begin())) );
 60}

 61          //  当前文本中当前文本行上方 Y 坐标
 62 INT GetY( TTEXT  & text,  const  TPOSLINE  & pl )  {
 63        TPOSLINE p;
 64        INT y = 0;
 65        for ( p = text.begin(); (p!=text.end())&&(p!=pl); ++p ) {
 66                y += ::GetDeltaY( text, p );
 67        }

 68        return y;
 69}

 70          //  当前文本中当前 Y 坐标下方文本行
 71 TPOSLINE GetPosline( TTEXT  & text, INT y )  {
 72        TPOSLINE p;
 73        for ( p = text.begin(); (p!=text.end())&&(y>0); ++p ) {
 74                y -= ::GetDeltaY( text, p );
 75        }

 76        if ( p == text.end() ) {
 77                --p;
 78        }

 79        return p;
 80}

 81
 82          //  当前文本中最长的行的长度,单位 字符数
 83 INT GetLineMaxLength( TTEXT  & text )  {
 84        INT iMax = 0, tmp;
 85        TPOSLINE p;
 86        for ( p = text.begin(); p != text.end(); ++p ) {
 87                tmp = p->size();
 88                if ( tmp > iMax ) {
 89                        iMax = tmp;
 90                }

 91        }

 92        return iMax;
 93}

 94          //  文本宽度,单位 字符
 95 inline INT GetTextWidth( TTEXT  & text )  {
 96        return ::GetLineMaxLength( text );
 97}

 98          //  文本高度,单位 字符
 99 inline INT GetTextHeight( TTEXT  & text )  {
100        return text.size();
101}

102          //  文本区宽度,单位 像素
103 INT GetTextAreaWidth( TTEXT  & text )  {
104        INT res = 0, tmp;
105        TPOSLINE pl;
106        for ( pl = text.begin(); pl != text.end(); ++pl ) {
107                tmp = ::GetX( *pl, pl->end() );
108                if ( tmp > res ) {
109                        res = tmp;
110                }

111        }

112        return res;
113}

114          //  文本区高度,单位 像素
115 inline INT GetTextAreaHeight( TTEXT  & text )  {
116        return ::GetTextHeight(text) * CY_FULL; // 偷懒,因高度硬编码
117}

118
119
120 /**/ //////// 文件
121 //  只能读入自己保存的文件,因为未考虑实际的文件编码
122 //  内存中的文本与磁盘上的文件内容有些差别,但保证读写过程不会对文件造成修改,如:
123 //  读入空文件会得到非空的有一个空行的文本,写文件时会将其过滤
124 //  szFileDir    "F:\\ZJ\\"
125 //  szFileName   "a.txt"
126
127          //  文件对话框,bOpen TRUE OPEN,  FALSE SAVE;目录与文件名既输入也输出;放弃返回FALSE
128          //  iMaxLen 为 szFileDir, szFileName 字符数组的大小,包括结尾的 NULL 
129 BOOL GetFileName( HWND hWnd, LPTSTR szFileDir, LPTSTR szFileName, INT iMaxLen, BOOL bOpen )  {
130        ::OPENFILENAME ofn = {0};
131        ofn.lStructSize = sizeof(ofn);
132        ofn.hwndOwner   = hWnd;
133        ofn.hInstance   = NULL;
134        TCHAR szFilter[100= TEXT("文本文件\0*.txt\0所有文件\0*.*\0\0");
135        ofn.lpstrFilter = szFilter;
136        ofn.nFilterIndex = 1;
137        const int L = 1024;
138        TCHAR szFile[ L ];
139        ::lstrcpy( szFile, szFileName );
140        ofn.lpstrFile   = szFile;
141        ofn.nMaxFile    = L;
142        ofn.lpstrFileTitle   = NULL;
143        ofn.lpstrInitialDir  = szFileDir;
144        ofn.lpstrTitle  = ( bOpen ? TEXT("打开文件") : TEXT("保存文件") );
145        ofn.Flags       = OFN_EXPLORER;
146        BOOL res = ( bOpen ? ::GetOpenFileName( &ofn ) :  ::GetSaveFileName( &ofn ) );
147        if ( res ) {
148                ::lstrcpy( szFileName, szFile + ofn.nFileOffset );
149                szFile[ ofn.nFileOffset ] = 0;
150                ::lstrcpy( szFileDir, szFile );
151                // test begin
152                // ::MessageBox( hWnd, szFileDir, szFileName, MB_OK );
153                // test end
154                return TRUE;
155        }

156        return FALSE;
157}

158          //  文件若存在,就覆盖;不存在,就新建
159 BOOL SaveFile( TTEXT  & text, LPCTSTR szFileDir, LPCTSTR szFileName )  {
160        TCHAR szFile[ 1024 ];
161        ::lstrcpy( szFile, szFileDir );
162        ::lstrcat( szFile, szFileName );
163        HANDLE hFile = ::CreateFile( szFile, GENERIC_WRITE, 0, NULL, 
164                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
165        if ( hFile == INVALID_HANDLE_VALUE ) {
166                return FALSE;
167        }

168        
169        INT iMaxLen = ::GetLineMaxLength( text );
170        HGLOBAL hMem = ::GlobalAlloc( GMEM_FIXED, sizeof(TCHAR)*(iMaxLen+20) ); // +20 防空行
171        LPVOID  pMem = ::GlobalLock( hMem );
172
173        TPOSLINE pl;
174        for ( pl = text.begin(); pl != text.end(); /**//*++pl*/ ) {
175                TCHAR *buf = (TCHAR*)pMem;
176                TPOSCHAR pc;
177                DWORD n = 0, nw = 0;
178                for ( pc = pl->begin(); pc != pl->end(); ++pc ) {
179                        buf[ n++ ] = *pc;
180                }

181                if ( (++pl) != text.end() ) {
182                        buf[ n++ ] = TEXT('\n'); // 最后一行尾不要加换行符
183                }

184                n *= sizeof(TCHAR);
185                BYTE *pByte = (BYTE*)pMem;
186                while ( n > 0 ) {
187                        ::WriteFile( hFile, pByte, n, &nw, NULL );
188                        pByte += nw;
189                        n -= nw;
190                }

191        }

192
193        ::GlobalUnlock( hMem );
194        ::GlobalFree( hMem );
195        ::CloseHandle( hFile );
196        return TRUE;
197}

198          //  文件若不存在,就返回FALSE表示失败
199 BOOL LoadFile( TTEXT  & text, LPCTSTR szFileDir, LPCTSTR szFileName )  {
200         TCHAR szFile[ 1024 ];
201        ::lstrcpy( szFile, szFileDir );
202        ::lstrcat( szFile, szFileName );
203        HANDLE hFile = ::CreateFile( szFile, GENERIC_READ, 0, NULL, 
204                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
205        if ( hFile == INVALID_HANDLE_VALUE ) {
206                return FALSE;
207        }

208
209        INT iMaxLen = 1024 * 4;
210        HGLOBAL hMem = ::GlobalAlloc( GMEM_FIXED, iMaxLen+20 );
211        LPVOID  pMem = ::GlobalLock( hMem );
212
213        DWORD nr = 0, nm = 0;
214        text.clear();
215        text.push_back( TLINE() );
216        TPOSLINE pl = text.begin();
217        BYTE *pByte = (BYTE*)pMem;
218        // 考虑一次成功读入的字节数不是 sizeof(TCHAR) 的整数倍
219        while ( ::ReadFile( hFile, pByte+nm, iMaxLen-nm, &nr, NULL ) && (nr>0) ) {
220                DWORD i;
221                TCHAR *buf = (TCHAR*)pMem;
222                nr += nm;
223                nm = nr % (sizeof(TCHAR));
224                nr /= sizeof(TCHAR);
225                for ( i = 0; i < nr; ++i ) {
226                        if ( buf[ i ] == TEXT('\n') ) {
227                                text.push_back( TLINE() );
228                                pl = text.end();
229                                --pl;
230                        }

231                        else {
232                                pl->push_back( buf[ i ] );
233                        }

234                }

235                nr *= sizeof(TCHAR);
236                for ( i = 0; i < nm; ++i ) {
237                        pByte[ i ] = pByte[ nr + i ];
238                }

239        }

240
241        ::GlobalUnlock( hMem );
242        ::GlobalFree( hMem );
243        ::CloseHandle( hFile );
244        return TRUE;
245}

246
247
248 /**/ //////// 滚动条
249 //  滚动范围  0..文本宽度-窗口客户区宽度  0..文本高度-窗口客户区高度  单位 CX_HALE, CY_HALF
250 //  位置      窗口客户区左上角半字符在整个文本中的位置
251          //  nPage, nMin, nMax
252 void  CalcScrollInfo( HWND hWnd, TTEXT  & text, INT nBar, ::SCROLLINFO  & si )  {
253        RECT rt;
254        ::GetClientRect( hWnd, &rt );
255        if ( nBar == SB_HORZ ) {
256                si.nPage = ( rt.right - rt.left ) / CX_FULL * CX_FULL;
257                si.nMax  = ::GetTextAreaWidth( text );
258        }

259        if ( nBar == SB_VERT ) {
260                si.nPage = ( rt.bottom - rt.top ) / CY_FULL * CY_FULL;
261                si.nMax  = ::GetTextAreaHeight( text );
262        }

263        si.nMin = 0;
264}

265          //  获取两个滚动条的所有信息
266 void  GetBothScrollInfo( HWND hWnd, ::SCROLLINFO  & sih, ::SCROLLINFO  & siv )  {
267        sih.cbSize = sizeof(sih);
268        sih.fMask  = SIF_ALL;
269        ::GetScrollInfo( hWnd, SB_HORZ, &sih );
270        siv.cbSize = sizeof(siv);
271        siv.fMask  = SIF_ALL;
272        ::GetScrollInfo( hWnd, SB_VERT, &siv );
273}

274          //  nMin, nMax, nPage,  if ( bInit ) nPos=0
275 void   UpdateScrollBar( HWND hWnd, TTEXT  & text, BOOL bInit  =  FALSE )  {
276        INT  i, sb[] = { SB_HORZ, SB_VERT };
277        ::SCROLLINFO si, sio;
278        for ( i = 0; i < sizeof(sb)/sizeof(sb[0]); ++i ) {
279                sio.cbSize = sizeof(sio);
280                sio.fMask  = SIF_ALL;
281                ::GetScrollInfo( hWnd, sb[i], &sio );
282
283                ::CalcScrollInfo( hWnd, text, sb[i], si );
284                si.cbSize = sizeof(si);
285                si.fMask = SIF_DISABLENOSCROLL | SIF_ALL;
286                if ( bInit ) {
287                        si.nPos  = 0;
288                }

289                else {
290                        si.nPos = sio.nPos;
291                }

292                if ( si.nPos < si.nMin ) {
293                        si.nPos = si.nMin;
294                }

295                if ( si.nPos > si.nMax ) {
296                        si.nPos = si.nMax;
297                }

298                si.nTrackPos = sio.nTrackPos;
299                ::SetScrollInfo( hWnd, sb[i], &si, TRUE );
300        }

301}

302          //  窗口客户区左上角在文本区中的位置
303 inline  void  GetLeftTopDelta( HWND hWnd, INT  & leftX, INT  & topY )  {
304        ::SCROLLINFO sih, siv;
305        ::GetBothScrollInfo( hWnd, sih, siv );
306        leftX = sih.nPos;
307        topY  = siv.nPos;
308}

309
310 /**/ //////// Caret
311 void  UpdateCaretPos( HWND hWnd, TTEXT  & text, TPOSLINE  & pl, TPOSCHAR  & pc )  {
312        INT leftX, topY;
313        ::GetLeftTopDelta( hWnd, leftX, topY );
314        ::SetCaretPos( ::GetX( *pl, pc ) - leftX, ::GetY( text, pl ) - topY );
315}

316
317
318 /**/ //////// 窗口
319 TCHAR  szClassName[]    =  TEXT( " TyperZJ " );
320 TCHAR  szWndNameMid[]   =  TEXT( "  - TyperZJ -  " );
321
322          //  利用常量相等 SB_LINEUP==SB_LINELEFT, 
323          //  SB_LINEDOWN==SB_LINERIGHT, SB_PAGEUP==SB_PAGELEFT, SB_PAGEDOWN==SB_PAGERIGHT
324          //  SB_TOP==SB_LEFT, SB_BOTTOM==SB_RIGHT
325 void   OnScroll( HWND hWnd, INT nBar, WPARAM wParam, TTEXT  & text, TPOSLINE  & pl, TPOSCHAR  & pc )  {
326        ::SCROLLINFO si;
327        si.cbSize = sizeof(si);
328        si.fMask  = SIF_ALL;
329        ::GetScrollInfo( hWnd, nBar, &si );
330
331        switch ( LOWORD(wParam) ) {
332        case SB_LINELEFT : 
333                si.nPos -= CX_FULL;
334                break;
335        case SB_LINERIGHT : 
336                si.nPos += CY_FULL;
337                break;
338        case SB_PAGELEFT : 
339                si.nPos -= si.nPage;
340                break;
341        case SB_PAGERIGHT : 
342                si.nPos += si.nPage;
343                break;
344        case SB_THUMBTRACK : 
345                si.nPos = si.nTrackPos;
346                break;
347        }

348        if ( si.nPos < si.nMin ) {
349                si.nPos = si.nMin;
350        }

351        if ( si.nPos > si.nMax ) {
352                si.nPos = si.nMax;
353        }

354
355        si.fMask = SIF_POS;
356        ::SetScrollInfo( hWnd, nBar, &si, TRUE );
357        ::UpdateCaretPos( hWnd, text, pl, pc );
358        ::InvalidateRect( hWnd, NULL, TRUE );
359}

360          //  文本修改并更新滚动条nMin,nMax,nPage后,修改滚动条nPos,Caret
361 void  OnTextChange( HWND hWnd, TTEXT  & text, TPOSLINE  & pl, TPOSCHAR  & pc )  {
362        ::SCROLLINFO sih, siv;
363        ::GetBothScrollInfo( hWnd, sih, siv );
364
365        INT x  = ::GetX( *pl, pc );
366        INT xP = sih.nPos;
367        if ( x < sih.nPos ) {
368                sih.nPos =  x;
369        }

370        else if ( x >= sih.nPos + (INT)sih.nPage ) {
371                sih.nPos = x - sih.nPage;
372        }

373        if ( sih.nPos < sih.nMin ) {
374                sih.nPos = sih.nMin;
375        }

376        if ( sih.nPos > sih.nMax ) {
377                sih.nPos = sih.nMax;
378        }

379        if ( xP != sih.nPos ) {
380                sih.cbSize = sizeof(sih);
381                sih.fMask = SIF_POS;
382                ::SetScrollInfo( hWnd, SB_HORZ, &sih, TRUE );
383        }

384
385        INT y  = ::GetY( text, pl );
386        INT yP = siv.nPos;
387        if ( y < siv.nPos ) {
388                siv.nPos = y;
389        }

390        else if ( y >= siv.nPos + (INT)siv.nPage ) {
391                siv.nPos = y - siv.nPage;
392        }

393        if ( siv.nPos < siv.nMin ) {
394                siv.nPos = siv.nMin;
395        }

396        if ( siv.nPos > siv.nMax ) {
397                siv.nPos = siv.nMax;
398        }

399        if ( yP != siv.nPos ) {
400                siv.cbSize = sizeof(siv);
401                siv.fMask  = SIF_POS;
402                ::SetScrollInfo( hWnd, SB_VERT, &siv, TRUE );
403        }

404
405        ::UpdateCaretPos( hWnd, text, pl, pc );
406
407        ::InvalidateRect( hWnd, NULL, TRUE );
408}

409          //  更新窗口标题  文件名 + 程序名 + 程序运行时间
410 void  UpdateWindowText( HWND hWnd, LONG time, LPCTSTR szFileName )  {
411        static TCHAR text[ 1024 ];
412        TCHAR szTime[ 1024 ];
413        ::swprintf( szTime, TEXT(" <已运行 %ld 秒> "), time );
414        ::lstrcpy( text, szFileName );
415        ::lstrcat( text, szWndNameMid );
416        ::lstrcat( text, szTime );
417        ::SetWindowText( hWnd, text );
418}

419
420          //  窗口过程函数
421 LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )  {
422        // 文本
423        static TTEXT text;
424        static TPOSLINE pl;
425        static TPOSCHAR pc;
426
427        // 文件
428        static BOOL   bModified = FALSE;
429        static BOOL   bNamed    = FALSE;
430        #define  FILE_LEN  1024
431        static TCHAR  szFileDir[ FILE_LEN ] = {0};
432        static TCHAR  szFileName[ FILE_LEN ] = {0};
433
434        // 时间
435        // 误差在200毫秒以内,系统运行 49.7 天以内正确计时
436        static LONG  totTime = 0;  // 程序运行总时间,单位 秒
437        static DWORD preTick;  // 单位 毫秒
438
439        switch ( uMsg ) {
440        case WM_CREATE : 
441                ::SendMessage( hWnd, WM_COMMAND, ID_FILE_NEW, 0 );
442                ::SetTimer( hWnd, 0200, NULL );
443                preTick = ::GetTickCount();
444                ::UpdateWindowText( hWnd, totTime, szFileName );
445                return 0;
446
447        case WM_TIMER : 
448                {
449                        DWORD cnt = ::GetTickCount();
450                        if ( cnt - preTick >= 1000 ) {
451                                totTime += ( cnt - preTick ) / 1000;
452                                ::UpdateWindowText( hWnd, totTime, szFileName );
453                                preTick = cnt;
454                        }

455                }

456                return 0;
457
458        case WM_PAINT : 
459                {
460                        ::PAINTSTRUCT ps;
461                        ::HDC hdc;
462
463                        hdc = ::BeginPaint( hWnd, &ps );
464
465                        TPOSLINE il;
466                        INT  leftX, topY, y;
467                        ::GetLeftTopDelta( hWnd, leftX, topY );
468                        y = -topY;
469                        HFONT hf = ::CreateFont( 00000000
470                                DEFAULT_CHARSET, 000, FIXED_PITCH, NULL );
471                        HFONT hfOld = (HFONT)::SelectObject( hdc, hf );
472                        for ( il = text.begin(); il != text.end(); ++il ) {
473                                TPOSCHAR ic;
474                                TCHAR sz[ 3 ];
475                                INT x = -leftX;
476                                for ( ic = il->begin(); ic != il->end(); ++ic ) {
477                                        sz[ 0 ] = (*ic);
478                                        ::TextOut( hdc, x, y, sz, 1 );
479                                        x += ::GetDeltaX( *il, ic );
480                                }

481                                y += ::GetDeltaY( text, il );
482                        }

483                        ::SelectObject( hdc, hfOld );
484                        ::DeleteObject( hf );
485
486                        ::EndPaint( hWnd, &ps );
487                }

488                return 0;
489
490        // 鼠标选取,点击插入点
491        case WM_LBUTTONDOWN : 
492                {
493                        INT  leftX, topY;
494                        ::GetLeftTopDelta( hWnd, leftX, topY );
495                        INT x = leftX + LOWORD(lParam) - ( CX_HALF / 2 );      // 微调,找最近
496                        INT y = topY  + HIWORD(lParam) - ( CY_HALF / 3 * 2 );  // .
497                        pl = ::GetPosline( text, y );
498                        pc = ::GetPoschar( *pl, x );
499                        ::UpdateCaretPos( hWnd, text, pl, pc );
500                        ::InvalidateRect( hWnd, NULL, TRUE );
501                }

502                return 0;
503
504        // 输入
505        case WM_KEYDOWN : 
506                switch ( wParam ) {
507                case VK_HOME : 
508                        pc = pl->begin();
509                        break;
510                case VK_END : 
511                        pc = pl->end();
512                        break;
513                case VK_LEFT : 
514                        if ( pc != pl->begin() ) {
515                                --pc;
516                        }

517                        else if ( pl != text.begin() ) {
518                                --pl;
519                                pc = pl->end();
520                        }

521                        break;
522                case VK_RIGHT : 
523                        if ( pc != pl->end() ) {
524                                ++pc;
525                        }

526                        else if ( ++pl != text.end() ) {
527                                pc = pl->begin();
528                        }

529                        else {
530                                --pl;
531                        }

532                        break;
533                case VK_UP : 
534                        if ( pl != text.begin() ) {
535                                INT x = ::GetX( *pl, pc );
536                                --pl;
537                                pc = ::GetPoschar( *pl, x );
538                        }

539                        break;
540                case VK_DOWN : 
541                        if ( ++pl != text.end() ) {
542                                --pl;
543                                INT x = ::GetX( *pl, pc );
544                                ++pl;
545                                pc = ::GetPoschar( *pl, x );
546                        }

547                        else {
548                                --pl;
549                        }

550                        break;
551                case VK_DELETE : 
552                        {
553                                TPOSLINE pln = pl;
554                                ++pln;
555                                if ( pc != pl->end() ) {
556                                        pc = pl->erase( pc );
557                                }

558                                else if ( pln != text.end() ) {
559                                        INT x = ::GetX( *pl, pc );
560                                        INT y = ::GetY( text, pl );
561                                        TPOSCHAR pcn;
562                                        for ( pcn = pln->begin(); pcn != pln->end(); ++pcn ) {
563                                                pl->push_back( *pcn );
564                                        }

565                                        text.erase( pln );
566                                        pl = ::GetPosline( text, y );
567                                        pc = ::GetPoschar( *pl, x );
568                                }

569                                bModified = TRUE;
570                                ::UpdateScrollBar( hWnd, text );
571                        }

572                        break;
573                case VK_RETURN : 
574                        {
575                                TLINE line( pc, pl->end() );
576                                pl->erase( pc, pl->end() );
577                                ++pl;
578                                if ( pl == text.end() ) {
579                                        text.push_back( line );
580                                        pl = text.end();
581                                        --pl;
582                                        pc = pl->begin();
583                                }

584                                else {
585                                        pl = text.insert( pl, line );
586                                        pc = pl->begin();
587                                }

588                                bModified = TRUE;
589                                ::UpdateScrollBar( hWnd, text );
590                        }

591                        break;
592                }

593                ::OnTextChange( hWnd, text, pl, pc );
594                return 0;
595
596        case WM_CHAR : 
597                switch ( wParam ) {
598                case TEXT('\b') : 
599                        if ( pc != pl->begin() ) {
600                                ::SendMessage( hWnd, WM_KEYDOWN, VK_LEFT, 1 );
601                                ::SendMessage( hWnd, WM_KEYDOWN, VK_DELETE, 1 );
602                        }

603                        break;
604                case TEXT('\t') : 
605                        {
606                                TPOSCHAR p;
607                                INT ip = 0;
608                                for ( p = pl->begin(); (p!=pl->end())&&(p!=pc); ++p ) {
609                                        ++ip;
610                                }

611                                --ip;
612                                do {
613                                        ++ip;
614                                        ::SendMessage( hWnd, WM_CHAR, TEXT(' '), 1 );
615                                }
 while ( ip % 8 != 7 );
616                                bModified = TRUE;
617                                ::UpdateScrollBar( hWnd, text );
618                        }

619                        break;
620                case TEXT('\r') : 
621                        break;
622                case TEXT('\n') : 
623                        break;
624                case TEXT('\x1B') : 
625                        break;
626                default : 
627                        {
628                                TCHAR ch = wParam;
629                                if ( pc == pl->end() ) {
630                                        pl->push_back( ch );
631                                        pc = pl->end();
632                                }

633                                else {
634                                        pc = pl->insert( pc, ch );
635                                        ++pc;
636                                }

637                                bModified = TRUE;
638                                ::UpdateScrollBar( hWnd, text );
639                        }

640                        break;
641                }

642                ::OnTextChange( hWnd, text, pl, pc );
643                return 0;
644
645        // 滚动
646        case WM_HSCROLL : 
647                ::OnScroll( hWnd, SB_HORZ, wParam, text, pl, pc );
648                return 0;
649        case WM_VSCROLL : 
650                ::OnScroll( hWnd, SB_VERT, wParam, text, pl, pc );
651                return 0;
652        case WM_MOUSEWHEEL : 
653                {
654                        SHORT delta = HIWORD(wParam);
655                        if ( delta > 0 ) {
656                                ::SendMessage( hWnd, WM_VSCROLL, SB_LINEUP, 0 );
657                        }

658                        else if ( delta < 0 ) {
659                                ::SendMessage( hWnd, WM_VSCROLL, SB_LINEDOWN, 0 );
660                        }

661                }

662                return 0;
663
664        case WM_SIZE : 
665                ::UpdateScrollBar( hWnd, text );
666                ::UpdateCaretPos( hWnd, text, pl, pc );
667                ::InvalidateRect( hWnd, NULL, TRUE );
668                return 0;
669
670        // 输入焦点
671        case WM_SETFOCUS : 
672                ::CreateCaret( hWnd, NULL, 2, ::GetDeltaY( *pl, pc ) );
673                ::UpdateCaretPos( hWnd, text, pl, pc );
674                ::ShowCaret( hWnd );
675                return 0;
676        case WM_KILLFOCUS : 
677                ::HideCaret( hWnd );
678                ::DestroyCaret();
679                return 0;
680
681        // 菜单命令
682        case WM_COMMAND : 
683                switch ( LOWORD(wParam) ) {
684                // 文件
685                case ID_FILE_NEW : 
686                        if ( bModified ) {
687                                // 已修改
688                                INT res = ::MessageBox( hWnd, TEXT("文件已修改,是否保存?"), TEXT("文件已修改"), MB_YESNOCANCEL | MB_ICONQUESTION );
689                                if ( IDYES == res ) {
690                                        if ( ! ::SendMessage( hWnd, WM_COMMAND, ID_FILE_SAVE, 0 ) ) {
691                                                // 保存文件时放弃
692                                                return 0;
693                                        }

694                                }

695                                else if ( IDCANCEL == res ) {
696                                        // 放弃
697                                        return 0;
698                                }

699                        }

700                        text.clear();
701                        text.push_back( TLINE() );
702                        pl = text.begin();
703                        pc = pl->begin();
704                        ::lstrcpy( szFileName, TEXT("未命名.txt") );
705                        bNamed    = FALSE;
706                        bModified = FALSE;
707                        ::UpdateScrollBar( hWnd, text, TRUE );
708                        ::UpdateCaretPos( hWnd, text, pl, pc );
709                        ::InvalidateRect( hWnd, NULL, TRUE );
710                        // 成功
711                        return 1;
712
713                case ID_FILE_OPEN : 
714                        if ( bModified ) {
715                                // 已修改
716                                INT res = ::MessageBox( hWnd, TEXT("文件已修改,是否保存?"), TEXT("文件已修改"), MB_YESNOCANCEL | MB_ICONQUESTION );
717                                if ( IDYES == res ) {
718                                        if ( ! ::SendMessage( hWnd, WM_COMMAND, ID_FILE_SAVE, 0 ) ) {
719                                                // 保存文件时放弃
720                                                return 0;
721                                        }

722                                }

723                                else if ( IDCANCEL == res ) {
724                                        // 放弃
725                                        return 0;
726                                }

727                        }

728                        if ( ::GetFileName( hWnd, szFileDir, szFileName, FILE_LEN, TRUE ) ) {
729                                if ( ::LoadFile( text, szFileDir, szFileName ) ) {
730                                        bModified = FALSE;
731                                        bNamed    = TRUE;
732                                        pl = text.begin();
733                                        pc = pl->begin();
734                                        ::UpdateScrollBar( hWnd, text, TRUE );
735                                        ::UpdateCaretPos( hWnd, text, pl, pc );
736                                        ::InvalidateRect( hWnd, NULL, TRUE );
737                                        // 成功
738                                        return 1;
739                                }

740                                // 读入文件失败
741                                return 0;
742                        }

743                        // 打开时放弃
744                        return 0;
745
746                case ID_FILE_SAVE : 
747                        if ( bNamed ) {
748                                // 已命名
749                                if ( bModified ) {
750                                        // 已修改
751                                        if ( ::SaveFile( text, szFileDir, szFileName ) ) {
752                                                bModified = FALSE;
753                                                // 成功
754                                                return 1;
755                                        }

756                                        // 保存失败
757                                        return 0;
758                                }

759                                // 未修改
760                                return 1;
761                        }

762                        // 尚未命名
763                        return ::SendMessage( hWnd, WM_COMMAND, ID_FILE_SAVE_AS, 0 );
764
765                case ID_FILE_SAVE_AS : 
766                        if ( ::GetFileName( hWnd, szFileDir, szFileName, FILE_LEN, FALSE ) ) {
767                                if ( ::SaveFile( text, szFileDir, szFileName ) ) {
768                                        bModified = FALSE;
769                                        bNamed    = TRUE;
770                                        // 成功
771                                        return 1;
772                                }

773                                // 保存失败
774                                return 0;
775                        }

776                        // 放弃
777                        return 0;
778
779                case ID_FILE_CLOSE : 
780                        if ( bModified ) {
781                                INT res = ::MessageBox( hWnd, TEXT("文件已修改,是否保存?"), TEXT("文件已修改"), MB_YESNOCANCEL | MB_ICONQUESTION );
782                                if ( IDYES == res ) {
783                                        if ( ! ::SendMessage( hWnd, WM_COMMAND, ID_FILE_SAVE, 0 ) ) {
784                                                // 保存时放弃
785                                                return 0;
786                                        }

787                                }

788                                else if ( IDCANCEL == res ) {
789                                        // 放弃
790                                        return 0;
791                                }

792                        }

793                        ::DestroyWindow( hWnd );
794                        return 0;
795                }

796                break;
797
798        case WM_CLOSE : 
799                ::SendMessage( hWnd, WM_COMMAND, ID_FILE_CLOSE, 0 );
800                return 0;
801
802        case WM_DESTROY : 
803                ::KillTimer( hWnd, 0 );
804                ::PostQuitMessage( 0 );
805                return 0;
806        }

807        return ::DefWindowProc( hWnd, uMsg, wParam, lParam );
808}

809
810
811 INT  APIENTRY  WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmd, INT iShowCmd )  {
812        WNDCLASSEX wc;
813        wc.cbClsExtra    = 0;
814        wc.cbSize        = sizeof(wc);
815        wc.cbWndExtra    = 0;
816        wc.hbrBackground = (HBRUSH)::GetStockObject( WHITE_BRUSH );
817        wc.hCursor       = ::LoadCursor( NULL, IDC_IBEAM );
818        wc.hIcon         = ::LoadIcon( NULL, IDI_APPLICATION );
819        wc.hIconSm       = ::LoadIcon( NULL, IDI_APPLICATION );
820        wc.hInstance     = hInst;
821        wc.lpfnWndProc   = WndProc;
822        wc.lpszClassName = szClassName;
823        wc.lpszMenuName  = MAKEINTRESOURCE( IDR_MENU );
824        wc.style         = CS_HREDRAW | CS_VREDRAW;
825
826        if ( ! ::RegisterClassEx( &wc ) ) {
827                ::MessageBox( NULL, TEXT("RegisterClassEx  Failed"), TEXT("ERROR"), MB_OK | MB_ICONERROR );
828                return 0;
829        }

830
831        HWND hWnd = ::CreateWindowEx( 0, szClassName, szWndNameMid, 
832                WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, 
833                10030800600
834                NULL, NULL, wc.hInstance, NULL );
835        if ( ! hWnd ) {
836                ::MessageBox( NULL, TEXT("CreateWindowEx Failed"), TEXT("ERROR"), MB_OK | MB_ICONERROR );
837                return 0;
838        }

839        ::ShowWindow( hWnd, iShowCmd );
840        ::UpdateWindow( hWnd );
841
842        MSG msg;
843        HACCEL hAcce = ::LoadAccelerators( hInst, MAKEINTRESOURCE(IDR_ACCE) );
844        while ( ::GetMessage( &msg, NULL, 00 ) ) {
845                if ( ! ::TranslateAccelerator( hWnd, hAcce, &msg ) ) {
846                        ::TranslateMessage( &msg );
847                        ::DispatchMessage( &msg );
848                }

849        }

850        return msg.wParam;
851}

852


你可能感兴趣的:(TyperZJ 文本编辑器——Windows编程上机作业之四)