2
* ========================================================================
3
* Copyright 2006-2007 University of Washington
5
* Licensed under the Apache License, Version 2.0 (the "License");
6
* you may not use this file except in compliance with the License.
7
* You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* ========================================================================
21
article entitled "About Rich Edit Controls" here (remove all spaces from url):
22
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/
23
platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp
31
The following list describes which versions of Rich Edit are included in
32
which releases of Microsoft Windows.
33
Windows XP SP1 Includes Rich Edit 4.1, Rich Edit 3.0, and a Rich Edit 1.0 emulator.
34
Windows XP Includes Rich Edit 3.0 with a Rich Edit 1.0 emulator.
35
Windows Me Includes Rich Edit 1.0 and 3.0.
36
Windows 2000 Includes Rich Edit 3.0 with a Rich Edit 1.0 emulator.
37
Windows NT 4.0 Includes Rich Edit 1.0 and 2.0.
38
Windows 98 Includes Rich Edit 1.0 and 2.0.
39
Windows 95 Includes only Rich Edit 1.0. However, Riched20.dll is
40
compatible with Windows 95 and may be installed by an
41
application that requires it.
43
We're using richedit v2 since it is the first to have Unicode support. Does
44
potentially limit us to Win98 unless we install riched20.dll or it's
47
#define _RICHEDIT_VER 0x0200
56
static const TCHAR g_mswin_tw_class_name[] = TEXT("PineTWClass");
58
// Maximum amount of text allowed in these textwindows.
59
// Set via a EM_EXLIMITTEXT message.
60
static const LPARAM g_max_text = 8 * 1024 * 1024 - 1;
65
static LRESULT CALLBACK mswin_tw_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
66
static UINT Edit_ExSetSel(HWND hwnd_edit, LONG cpMin, LONG cpMax);
67
static UINT Edit_ExGetTextLen(HWND hwnd_edit, DWORD flags);
68
static BOOL Edit_ExIsReadOnly(HWND hwnd_edit);
71
* mswin_tw_create() - Create a mswin textwindow.
74
mswin_tw_create(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR title)
77
static int s_mswin_tw_class_registered = 0;
79
mswin_tw->hwnd = NULL;
80
mswin_tw->hwnd_edit = NULL;
82
if(!s_mswin_tw_class_registered)
86
LoadLibrary(TEXT("riched20.dll"));
88
memset(&wndclass, 0, sizeof(wndclass));
89
wndclass.style = CS_BYTEALIGNWINDOW;
90
wndclass.lpfnWndProc = mswin_tw_wndproc;
91
wndclass.cbClsExtra = 0;
92
wndclass.cbWndExtra = sizeof(ULONG_PTR);
93
wndclass.hInstance = mswin_tw->hInstance ;
94
wndclass.hIcon = LoadIcon (mswin_tw->hInstance, MAKEINTRESOURCE( ALPINEICON));
95
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
96
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
97
wndclass.lpszMenuName = MAKEINTRESOURCE(TEXTWINMENU);
98
wndclass.lpszClassName = g_mswin_tw_class_name ;
100
RegisterClass(&wndclass);
102
s_mswin_tw_class_registered = 1;
105
// If we're passed CW_USEDEFAULT as the position, then use right/bottom
106
// as the width/height. Otherwise use the full rect.
107
width = (mswin_tw->rcSize.left == CW_USEDEFAULT) ?
108
mswin_tw->rcSize.right : mswin_tw->rcSize.right - mswin_tw->rcSize.left;
109
height = (mswin_tw->rcSize.top == CW_USEDEFAULT) ?
110
mswin_tw->rcSize.bottom : mswin_tw->rcSize.bottom - mswin_tw->rcSize.top;
112
mswin_tw->hwnd = CreateWindow(
113
g_mswin_tw_class_name, title,
115
mswin_tw->rcSize.left,
116
mswin_tw->rcSize.top,
120
mswin_tw->hInstance, mswin_tw);
128
* mswin_tw_close() - close the mswin textwindow.
131
mswin_tw_close(MSWIN_TEXTWINDOW *mswin_tw)
133
if(mswin_tw && mswin_tw->hwnd)
134
DestroyWindow(mswin_tw->hwnd);
138
* mswin_tw_showwindow() - show the main hwnd (SW_SHOWNORMAL, etc.).
141
mswin_tw_showwindow(MSWIN_TEXTWINDOW *mswin_tw, int nCmdShow)
143
if(mswin_tw && mswin_tw->hwnd)
144
ShowWindow(mswin_tw->hwnd, nCmdShow);
148
* mswin_tw_setfont() - Sets the hfont for the entire edit control.
151
mswin_tw_setfont(MSWIN_TEXTWINDOW *mswin_tw, HFONT hfont)
153
if(mswin_tw && mswin_tw->hwnd_edit)
154
SetWindowFont(mswin_tw->hwnd_edit, hfont, TRUE);
158
* mswin_tw_setcolor() - Set colors for entire edit control. If we're
159
* passed -1 for mswin_tw, then set the colors for all textwindows.
162
mswin_tw_setcolor(MSWIN_TEXTWINDOW *mswin_tw,
163
COLORREF TextColor, COLORREF BackColor)
165
if(mswin_tw == (MSWIN_TEXTWINDOW *)-1)
169
while(hwnd = FindWindowEx(NULL, hwnd, g_mswin_tw_class_name, NULL))
171
mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(
172
hwnd, GWLP_USERDATA);
175
mswin_tw_setcolor(mswin_tw, TextColor, BackColor);
179
else if(mswin_tw && mswin_tw->hwnd_edit)
183
memset(&cf2, 0, sizeof(cf2));
184
cf2.cbSize = sizeof(cf2);
185
cf2.dwMask = CFM_COLOR;
186
cf2.crTextColor = TextColor;
188
SendMessage(mswin_tw->hwnd_edit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
189
SendMessage(mswin_tw->hwnd_edit, EM_SETBKGNDCOLOR, 0, BackColor);
194
* mswin_tw_puts_lptstr() - Stuffs a LPTSTR string at the end of our edit
198
mswin_tw_puts_lptstr(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR msg)
200
if(mswin_tw && mswin_tw->hwnd_edit)
204
TCHAR lf_str[] = TEXT("\n");
206
Edit_ExSetSel(mswin_tw->hwnd_edit, -1, -1);
207
Edit_ReplaceSel(mswin_tw->hwnd_edit, msg);
209
Edit_ReplaceSel(mswin_tw->hwnd_edit, lf_str);
211
Edit_ScrollCaret(mswin_tw->hwnd_edit);
219
* mswin_tw_printf() - printf version of mswin_tw_puts_lptstr.
222
mswin_tw_printf(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR fmt, ...)
227
va_start(vlist, fmt);
228
_vsntprintf(msg, ARRAYSIZE(msg), fmt, vlist);
231
msg[ARRAYSIZE(msg) - 1] = 0;
232
return mswin_tw_puts_lptstr(mswin_tw, msg);
236
* mswin_tw_gettextlength() - Returns the number of TCHARs in the edit control.
239
mswin_tw_gettextlength(MSWIN_TEXTWINDOW *mswin_tw)
241
if(mswin_tw && mswin_tw->hwnd_edit)
242
return (UINT)Edit_ExGetTextLen(mswin_tw->hwnd_edit, GTL_USECRLF);
248
* mswin_tw_gettext() - Return value is the number of TCHARs copied into the
252
mswin_tw_gettext(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR lptstr_ret, int lptstr_len)
254
if(mswin_tw && mswin_tw->hwnd_edit)
259
gt.cb = lptstr_len * sizeof(TCHAR);
260
gt.flags = GT_DEFAULT | GT_USECRLF;
262
gt.lpDefaultChar = NULL;
263
gt.lpUsedDefChar = NULL;
266
lresult = SendMessage(mswin_tw->hwnd_edit, EM_GETTEXTEX,
267
(WPARAM)>, (LPARAM)lptstr_ret);
275
* mswin_tw_setsel() - Set edit control selection.
278
mswin_tw_setsel(MSWIN_TEXTWINDOW *mswin_tw, LONG min, LONG max)
280
if(mswin_tw && mswin_tw->hwnd_edit)
282
Edit_ExSetSel(mswin_tw->hwnd_edit, min, max);
287
* mswin_tw_clear() - Clear all text from edit control.
289
void mswin_tw_clear(MSWIN_TEXTWINDOW *mswin_tw)
291
if(mswin_tw && mswin_tw->hwnd_edit)
295
stex.flags = ST_DEFAULT;
296
stex.codepage = 1200; // Unicode (see richedit.h)
298
SendMessage(mswin_tw->hwnd_edit, EM_SETTEXTEX,
299
(WPARAM)&stex, (LPARAM)TEXT(""));
301
if(mswin_tw->clear_callback)
302
mswin_tw->clear_callback(mswin_tw);
307
* MySetWindowLongPtr() - Little wrapper routine which calls the
308
* Windows SetWindowLongPtr() and removes the stupid warning which is
309
* just seriously lame.
312
MySetWindowLongPtr(HWND hwnd, int nIndex, void *NewLongPtr)
314
// warning C4244: 'function': conversion from 'LONG_PTR' to 'LONG',
315
// possible loss of data
316
#pragma warning(push)
317
#pragma warning(disable: 4244)
318
return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)NewLongPtr);
323
* mswin_tw_wm_command() - WM_CONTEXTMENU handler for textwindows
326
mswin_tw_wm_contextmenu(MSWIN_TEXTWINDOW *mswin_tw, HWND hwnd, HWND hwndContext,
331
hMenu = CreatePopupMenu();
343
{ IDM_EDIT_COPY, TEXT("&Copy") },
344
{ IDM_EDIT_COPY_APPEND, TEXT("Copy &Append") },
345
{ IDM_EDIT_CLEAR, TEXT("Clea&r") },
348
memset(&mitem, 0, sizeof(MENUITEMINFO));
349
mitem.cbSize = sizeof(MENUITEMINFO);
350
mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
351
mitem.fType = MFT_STRING;
353
SendMessage(mswin_tw->hwnd_edit, EM_EXGETSEL, (WPARAM)0, (LPARAM)&cr);
355
for(i = 0; i < ARRAYSIZE(s_popup_menu); i++)
357
switch(s_popup_menu[i].wID)
360
// Only enable it if there is a clear callback set.
361
mitem.fState = (mswin_tw->clear_callback) ?
362
MFS_ENABLED : MFS_GRAYED;
365
case IDM_EDIT_COPY_APPEND:
366
// Only enable if there is a selection.
367
mitem.fState = (cr.cpMax > cr.cpMin) ?
368
MFS_ENABLED : MFS_GRAYED;
371
mitem.fState = MFS_ENABLED;
375
mitem.wID = s_popup_menu[i].wID;
376
mitem.dwTypeData = s_popup_menu[i].dwTypeData;
377
mitem.cch = (UINT)_tcslen(s_popup_menu[i].dwTypeData);
378
InsertMenuItem(hMenu, i, FALSE, &mitem);
381
TrackPopupMenu(hMenu,
382
TPM_LEFTALIGN | TPM_TOPALIGN |
383
TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
384
xPos, yPos, 0, hwnd, NULL);
391
* mswin_tw_wm_command() - WM_COMMAND handler for textwindows
394
mswin_tw_wm_command(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
396
MSWIN_TEXTWINDOW *mswin_tw;
398
mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_USERDATA);
407
if(mswin_tw->print_callback)
408
mswin_tw->print_callback(mswin_tw);
412
SendMessage(mswin_tw->hwnd_edit, WM_COPY, 0, 0);
416
mswin_tw_clear(mswin_tw);
419
case IDM_EDIT_COPY_APPEND:
423
int text_len0, text_len1;
424
HWND hwnd_edit = mswin_tw->hwnd_edit;
425
BOOL EditIsReadOnly = Edit_ExIsReadOnly(hwnd_edit);
427
SetWindowRedraw(hwnd_edit, FALSE);
428
Edit_SetReadOnly(hwnd_edit, FALSE);
430
// Get current selection.
431
SendMessage(hwnd_edit, EM_EXGETSEL, (WPARAM)0, (LPARAM)&cr);
433
// Get current length.
434
text_len0 = Edit_ExGetTextLen(hwnd_edit, 0);
436
// Paste current clip right before our new selection.
437
Edit_ExSetSel(hwnd_edit, cr.cpMin, cr.cpMin);
438
SendMessage(hwnd_edit, WM_PASTE, 0, 0);
441
text_len1 = Edit_ExGetTextLen(hwnd_edit, 0);
443
// Select new and old clip and copy em.
444
Edit_ExSetSel(hwnd_edit, cr.cpMin, cr.cpMax + text_len1 - text_len0);
445
SendMessage(hwnd_edit, WM_COPY, 0, 0);
447
// Undo our paste and restore original selection.
448
SendMessage(hwnd_edit, WM_UNDO, 0, 0);
449
SendMessage(hwnd_edit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
451
// Set back to read only.
452
Edit_SetReadOnly(hwnd_edit, EditIsReadOnly);
453
SetWindowRedraw(hwnd_edit, TRUE);
457
case IDM_EDIT_SEL_ALL:
458
Edit_ExSetSel(mswin_tw->hwnd_edit, 0, -1);
464
* mswin_tw_wm_notify() - WM_NOTIFY handler for textwindows
467
mswin_tw_wm_notify(HWND hwnd, int idCtrl, NMHDR *nmhdr)
469
HWND hwnd_edit = nmhdr->hwndFrom;
471
if(nmhdr->code == EN_MSGFILTER)
473
MSGFILTER *msg_filter = (MSGFILTER *)nmhdr;
475
if(msg_filter->msg == WM_KEYDOWN)
477
if(msg_filter->wParam == 'E')
479
int control_down = GetKeyState(VK_CONTROL) < 0;
480
int shift_down = GetKeyState(VK_SHIFT) < 0;
482
// Ctrl+Shift+E toggles the readonly attribute on the text
484
if(control_down && shift_down)
485
Edit_SetReadOnly(hwnd_edit, !Edit_ExIsReadOnly(hwnd_edit));
489
else if(msg_filter->msg == WM_CHAR)
491
// Only override these keys if this buffer is readonly.
492
if(Edit_ExIsReadOnly(hwnd_edit))
494
switch(msg_filter->wParam)
497
SendMessage(hwnd_edit, EM_SCROLL, SB_LINEUP, 0);
500
SendMessage(hwnd_edit, EM_SCROLL, SB_LINEDOWN, 0);
504
SendMessage(hwnd_edit, EM_SCROLL, SB_PAGEUP, 0);
508
SendMessage(hwnd_edit, EM_SCROLL, SB_PAGEDOWN, 0);
514
else if(nmhdr->code == EN_LINK)
516
ENLINK *enlink = (ENLINK *)nmhdr;
518
if(enlink->msg == WM_LBUTTONDOWN)
521
TCHAR link_buf[1024];
524
tr.lpstrText = link_buf;
526
tr.chrg = enlink->chrg;
527
if(tr.chrg.cpMax - tr.chrg.cpMin > ARRAYSIZE(link_buf))
528
tr.chrg.cpMax = tr.chrg.cpMin + ARRAYSIZE(link_buf);
530
SendMessage(hwnd_edit, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
531
ShellExecute(hwnd, TEXT("Open"), link_buf, NULL, NULL, SW_SHOWNORMAL);
540
* mswin_tw_wndproc() - Main window proc for mswin textwindows.
542
static LRESULT CALLBACK
543
mswin_tw_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
545
MSWIN_TEXTWINDOW *mswin_tw;
547
mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_USERDATA);
553
CREATESTRUCT *pcs = (CREATESTRUCT *)lp;
555
mswin_tw = (MSWIN_TEXTWINDOW *)pcs->lpCreateParams;
557
MySetWindowLongPtr(hwnd, GWLP_USERDATA, mswin_tw);
559
mswin_tw->hwnd_edit = CreateWindowEx(
560
WS_EX_CLIENTEDGE, RICHEDIT_CLASS, 0,
561
WS_VISIBLE | WS_CHILD |
562
WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
563
WS_VSCROLL | WS_HSCROLL |
564
ES_MULTILINE | ES_READONLY | ES_NOHIDESEL,
566
hwnd, 0, mswin_tw->hInstance, 0);
568
// We want link and key event notifications.
569
SendMessage(mswin_tw->hwnd_edit, EM_SETEVENTMASK,
570
0, (ENM_KEYEVENTS | ENM_LINK));
572
// Specifies the maximum amount of text that can be entered.
573
SendMessage(mswin_tw->hwnd_edit, EM_EXLIMITTEXT, 0, g_max_text);
575
// Enable automatic detection of URLs by our rich edit control.
576
SendMessage(mswin_tw->hwnd_edit, EM_AUTOURLDETECT, TRUE, 0);
581
mswin_tw_wm_contextmenu(mswin_tw, hwnd, (HWND)wp, GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
585
return mswin_tw_wm_notify(hwnd, (int)wp, (NMHDR *)lp);
588
HANDLE_WM_COMMAND(hwnd, wp, lp, mswin_tw_wm_command);
592
SetFocus(mswin_tw->hwnd_edit);
596
MoveWindow(mswin_tw->hwnd_edit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE);
599
case WM_WINDOWPOSCHANGED:
602
WINDOWPOS *wpos = (WINDOWPOS *)lp;
604
mswin_tw->rcSize.left = wpos->x;
605
mswin_tw->rcSize.top = wpos->y;
606
mswin_tw->rcSize.right = wpos->x + wpos->cx;
607
mswin_tw->rcSize.bottom = wpos->y + wpos->cy;
612
mswin_tw->hwnd = NULL;
613
mswin_tw->hwnd_edit = NULL;
615
if(mswin_tw->close_callback)
616
mswin_tw->close_callback(mswin_tw);
623
return DefWindowProc(hwnd, msg, wp, lp);
627
* Edit_ExGetTextLen() - Helper routine for getting count of chars.
630
Edit_ExGetTextLen(HWND hwnd_edit, DWORD flags)
634
gtl.flags = GTL_PRECISE | GTL_NUMCHARS | flags;
635
gtl.codepage = 1200; // Unicode (see richedit.h)
636
return (UINT)SendMessage(hwnd_edit, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
640
* Edit_ExSetSel() - Helper routine for setting edit selection.
643
Edit_ExSetSel(HWND hwnd_edit, LONG cpMin, LONG cpMax)
648
cpMin = Edit_ExGetTextLen(hwnd_edit, 0);
652
return (UINT)SendMessage(hwnd_edit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
656
* Edit_ExIsReadOnly() - TRUE if edit buffer is read only.
659
Edit_ExIsReadOnly(HWND hwnd_edit)
663
edit_opts = SendMessage(hwnd_edit, EM_GETOPTIONS, 0, 0);
664
return !!(edit_opts & ECO_READONLY);