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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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, 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

34
35
36
37

38
39
40

41
42
43
44
45
46

47
48

49
50
51
52
53
54

55
56
57
58

59
60
61
62

63
64
65

66
67
68
69
70
71

72
73

74
75
76

77
78
79
80
81
82
83

84
85
86

87
88

89
90
91
92
93
94
95

96
97
98
99

100
101
102
103

104
105
106

107
108

109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125
126
127
128
129

130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165

166
167
168
169
170
171
172
173
174

175
176
177
178

179
180
181

182
183
184
185
186

187
188
189
190
191
192
193
194
195
196
197
198
199

200
201
202
203
204
205

206
207
208
209
210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225

226

227
228
229
230
231

232
233
234
235
236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

253
254
255

256
257
258
259

260
261
262
263
264
265
266

267
268
269
270
271
272
273
274
275

276

277
278

279
280
281
282
283
284
285
286

287
288
289

290
291
292

293
294
295

296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
311

312
313
314
315
316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

349
350
351

352
353
354
355
356
357
358
359
360
361

362
363
364
365
366
367

368
369
370

371
372
373

374
375
376

377
378
379

380
381
382
383
384
385
386
387

388
389
390

391
392
393

394
395
396

397
398
399

400
401
402
403
404
405
406
407
408
409
410

411
412
413
414
415
416
417
418
419
420
421

422
423
424
425
426
427
428
429
430
431

432

433
434
435
436
437
438
439

440
441
442
443
444
445
446
447
448

449
450

451
452
453
454
455
456
457
458
459

460
461
462
463
464
465
466
467
468
469
470
471
472

473
474
475
476

477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492

493
494
495
496

497
498
499
500
501
502
503
504
505
506

507
508
509
510
511
512
513
514

515
516
517

518
519
520
521
522
523

524
525
526

527
528
529

530
531
532
533
534

535
536
537
538
539
540
541

542
543
544
545
546
547

548
549
550
551
552

553
554
555

556
557
558

559
560
561
562

563
564
565
566
567
568
569
570
571
572
573
574

575
576
577
578

579
580
581
582
583
584

585
586
587
588
589
590
591
592
593
594
595
596
597

598
599

600
601
602
603
604
605

606
607
608

609
610
611
612

613
614
615
616
617
618
619
620
621
622
623
624
625
626
627

628
629

630
631
632
633

634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653

654
655

656
657
658

659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683

684
685
686

687
688
689

690

691
692
693
694
695

696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714

715
716
717

718

719
720
721
722
723

724
725
726
727
728

729

730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747

748
749

750
751

752
753
754
755
756
757
758
759
760
761
762
763
764
765
766

767

768
769
770
771
772
773
774
775
776
777
778
779
780

781
782

783

784
785
786
787
788

789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811

812
813
814
815
816
817
818
819
820
821
822
823
824
825
826

827
828
829
830
831
832
833
834
835

836
837
838
839
840
841
842
843
844

845

846
847
848
849
850
851
852