要求:
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
/**///////// Caret311
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, 0, 200, 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( 0, 0, 0, 0, 0, 0, 0, 0,
470
DEFAULT_CHARSET, 0, 0, 0, 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
100, 30, 800, 600,
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, 0, 0 ) )
{
845
if ( ! ::TranslateAccelerator( hWnd, hAcce, &msg ) )
{
846
::TranslateMessage( &msg );
847
::DispatchMessage( &msg );
848
}
849
}
850
return msg.wParam;
851
}
852