3
* richedit class for play MDI development environment
5
* Copyright (c) 1999. See accompanying LEGAL file for details.
20
static char THIS_FILE[] = __FILE__;
23
static HANDLE w_sending = 0;
24
static void mfc_sender(void *context);
25
static void mfc_stdout(char *output_line, long len);
26
static void mfc_stderr(char *output_line, long len);
27
static void mfc_bstdout(char *output_line);
28
static void mfc_bstderr(char *output_line);
29
static int mfc_deliver(const char *buf, long len);
31
static char *w_deprompt(char *text);
33
IMPLEMENT_DYNCREATE(mfc_edit_view, CRichEditView)
35
BEGIN_MESSAGE_MAP(mfc_edit_view, CRichEditView)
36
ON_COMMAND(ID_GOTO_LINE, on_goto_line)
37
ON_COMMAND(ID_GOTO_OUT, on_goto_out)
38
ON_COMMAND(ID_SELECT_OUT, on_select_out)
39
ON_COMMAND(ID_OPEN_LINE, on_open_line)
40
ON_COMMAND(ID_CHOOSE_FILE, on_choose_file)
41
ON_COMMAND(ID_EDIT_REDO, on_redo)
42
ON_UPDATE_COMMAND_UI(ID_GOTO_OUT, on_update_term)
43
ON_UPDATE_COMMAND_UI(ID_SELECT_OUT, on_update_term)
44
ON_UPDATE_COMMAND_UI(ID_OPEN_LINE, on_update_term)
45
ON_UPDATE_COMMAND_UI(ID_CHOOSE_FILE, on_update_term)
46
ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, on_update_redo)
49
ON_COMMAND(ID_FILE_PRINT, CRichEditView::OnFilePrint)
50
ON_COMMAND(ID_FILE_PRINT_DIRECT, CRichEditView::OnFilePrint)
51
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CRichEditView::OnFilePrintPreview)
54
mfc_edit_view::mfc_edit_view()
56
m_nWordWrap = WrapNone;
57
is_term = riched2 = 0;
60
mfc_edit_view::~mfc_edit_view()
65
mfc_edit_view::PreCreateWindow(CREATESTRUCT& cs)
67
BOOL result = CRichEditView::PreCreateWindow(cs);
69
#if (_RICHEDIT_VER < 0x200) && !defined(NO_RICHEDIT2)
70
// The following lines will turn on richedit 2.0 or 3.0 controls.
71
// However, the compatibility with the rest of the class is botched so that
72
// many of the MFC functions only work with 1.0 semantics (_RICHEDIT_VER is
73
// set in afxwin.h to force 1.0 in riched.h).
74
// I prefer not to risk this with the terminal and history windows, but
75
// the lure of multilevel undo is irresistable for editor windows.
76
static int re2_initialized = 0;
78
if (!re2_initialized) {
79
if (!LoadLibraryA("riched20.dll")) // riched32.dll is 1.0 version
84
if (re2_initialized==1) {
85
cs.lpszClass = RICHEDIT_CLASSA; // RICHEDIT_CLASS set to 1.0 version
86
// unless _RICHEDIT_VER >= 0x200
96
mfc_edit_view::OnInitialUpdate()
98
CRichEditView::OnInitialUpdate();
99
// set the printing margins (720 twips = 1/2 inch).
100
SetMargins(CRect(720, 720, 720, 720));
104
mfc_edit_view::OnPreparePrinting(CPrintInfo* pInfo)
106
// default preparation
107
return DoPreparePrinting(pInfo);
111
mfc_edit_view::OnDestroy()
113
CRichEditView::OnDestroy();
115
// Deactivate the item on destruction; this is important
116
// when a splitter view is being used.
117
COleClientItem* item = GetDocument()->GetInPlaceActiveItem(this);
118
if (item && item->GetActiveView()==this)
122
static CFont fixed_font;
123
static int font_init = 0;
126
mfc_edit_view::OnCreate(LPCREATESTRUCT lpcs)
128
if (CCtrlView::OnCreate(lpcs) != 0)
130
GetRichEditCtrl().LimitText(lMaxSize);
131
GetRichEditCtrl().SetEventMask(ENM_SELCHANGE | ENM_CHANGE | ENM_SCROLL);
132
VERIFY(GetRichEditCtrl().SetOLECallback(&m_xRichEditOleCallback));
133
m_lpRichEditOle = GetRichEditCtrl().GetIRichEditOle();
135
GetRichEditCtrl().SetOptions(ECOOP_OR, ECO_AUTOWORDSELECTION);
138
HFONT f = (HFONT)GetStockObject(ANSI_FIXED_FONT);
140
if (::GetObject((HGDIOBJ)f, sizeof(LOGFONT), &lf)) {
141
fixed_font.CreateFontIndirect(&lf);
147
GetRichEditCtrl().SetFont(&fixed_font,0);
150
ASSERT(m_lpRichEditOle != NULL);
154
// without this, idiot will paste files and other objects into window
156
mfc_edit_view::QueryAcceptData(LPDATAOBJECT lpdataobj,
157
CLIPFORMAT* lpcfFormat, DWORD reco,
158
BOOL fReally, HGLOBAL hMetaPict)
160
if (*lpcfFormat == CF_TEXT) return S_OK;
161
COleDataObject dataobj;
162
dataobj.Attach(lpdataobj, FALSE);
163
if (*lpcfFormat==0 && dataobj.IsDataAvailable(CF_TEXT)) {
164
*lpcfFormat = CF_TEXT;
170
class mfc_goto : public CDialog
173
mfc_goto(mfc_edit_view *v);
176
DECLARE_MESSAGE_MAP()
179
BEGIN_MESSAGE_MAP(mfc_goto, CDialog)
182
mfc_goto::mfc_goto(mfc_edit_view *v) : CDialog(IDD_GOTO_LINE)
190
CEdit *ce = (CEdit *)GetDlgItem(IDC_GOTO_LINE);
192
int n = ce->GetLine(0, line, 12);
197
n = view->GetRichEditCtrl().LineIndex(n);
198
view->GetRichEditCtrl().SetSel(n,n);
204
mfc_edit_view::on_goto_line()
206
mfc_goto goto_dlg(this);
211
mfc_edit_view::on_redo()
213
ASSERT(::IsWindow(m_hWnd));
214
::SendMessage(m_hWnd, EM_REDO, 0, 0);
215
m_bSyncCharFormat = m_bSyncParaFormat = 1;
219
mfc_edit_view::on_update_term(CCmdUI *ui)
225
mfc_edit_view::on_update_redo(CCmdUI *ui)
228
ASSERT(::IsWindow(m_hWnd));
229
ui->Enable((BOOL)::SendMessage(m_hWnd, EM_CANREDO, 0, 0));
235
/*------------------------------------------------------------------------*/
237
IMPLEMENT_DYNCREATE(mfc_edit_child, CMDIChildWnd)
239
BEGIN_MESSAGE_MAP(mfc_edit_child, CMDIChildWnd)
243
mfc_edit_child::mfc_edit_child()
247
mfc_edit_child::~mfc_edit_child()
252
mfc_edit_child::OnClose()
254
if (this == term_view->GetParent())
255
AfxGetApp()->m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_TERM, 0);
256
else if (this == hist_view->GetParent())
257
AfxGetApp()->m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_HIST, 0);
259
CMDIChildWnd::OnClose();
262
/*------------------------------------------------------------------------*/
264
IMPLEMENT_DYNCREATE(mfc_edit_doc, CRichEditDoc)
266
BEGIN_MESSAGE_MAP(mfc_edit_doc, CRichEditDoc)
267
//{{AFX_MSG_MAP(mfc_edit_doc)
268
ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
270
// Enable default OLE container implementation
271
ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_LINKS, CRichEditDoc::OnUpdateEditLinksMenu)
272
ON_COMMAND(ID_OLE_EDIT_LINKS, CRichEditDoc::OnEditLinks)
273
ON_UPDATE_COMMAND_UI_RANGE(ID_OLE_VERB_FIRST, ID_OLE_VERB_LAST, CRichEditDoc::OnUpdateObjectVerbMenu)
276
mfc_edit_doc::mfc_edit_doc()
280
mfc_edit_doc::mfc_edit_doc(CMultiDocTemplate *mdt, int hist)
282
mdt->AddDocument(this);
283
m_pDocTemplate = mdt;
284
mfc_edit_child *frame = new mfc_edit_child;
285
CCreateContext context;
286
context.m_pCurrentFrame = 0;
287
context.m_pCurrentDoc = this;
288
context.m_pNewViewClass = RUNTIME_CLASS(mfc_term_view);
289
context.m_pNewDocTemplate = mdt;
290
frame->LoadFrame(IDR_EDITFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,
292
SetTitle(hist? "*history*" : "*terminal*");
295
((mfc_term_view *)GetView())->is_term = 1;
298
mfc_edit_doc::~mfc_edit_doc()
303
mfc_edit_doc::CreateClientItem(REOBJECT* preo) const
309
mfc_edit_doc::Serialize(CArchive& ar)
311
if (ar.IsStoring()) {
312
// TODO: add storing code here
314
// TODO: add loading code here
317
// Calling the base class CRichEditDoc enables serialization
318
// of the container document's COleClientItem objects.
320
CRichEditDoc::Serialize(ar);
324
mfc_edit_doc::OnNewDocument()
326
if (!CRichEditDoc::OnNewDocument())
329
// TODO: add reinitialization code here
330
// (SDI documents will reuse this document)
336
mfc_edit_doc::OnOpenDocument(LPCTSTR lpszPathName)
338
if (!CRichEditDoc::OnOpenDocument(lpszPathName))
341
// TODO: Add your specialized creation code here
347
mfc_edit_doc::SaveModified()
349
mfc_edit_view *view = (mfc_edit_view *)GetView();
350
if (view==term_view || view==hist_view)
353
return CRichEditDoc::SaveModified();
357
mfc_edit_doc::OnFileClose()
359
mfc_edit_view *view = (mfc_edit_view *)GetView();
360
if (view == term_view)
361
AfxGetApp()->m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_TERM, 0);
362
else if (view == hist_view)
363
AfxGetApp()->m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_HIST, 0);
365
COleServerDoc::OnCloseDocument();
368
/*------------------------------------------------------------------------*/
370
IMPLEMENT_DYNCREATE(mfc_term_view, mfc_edit_view)
372
BEGIN_MESSAGE_MAP(mfc_term_view, mfc_edit_view)
373
ON_COMMAND(ID_EDIT_UNDO, on_undo)
376
mfc_term_view::mfc_term_view()
378
mark = smin = smax = len = 0;
379
is_visible = recalling = recursing = 0;
382
mfc_term_view::~mfc_term_view()
387
mfc_term_view::PreCreateWindow(CREATESTRUCT& cs)
389
return CRichEditView::PreCreateWindow(cs);
393
mfc_term_view::OnInitialUpdate()
395
mfc_edit_view::OnInitialUpdate();
399
mfc_term_view::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
402
int key_unshifted = 0;
404
int top = !recursing;
407
if (message==WM_DESTROY) top = 0;
414
key_unshifted = (GetKeyState(VK_SHIFT)>=0 && GetKeyState(VK_CONTROL)>=0 &&
415
GetKeyState(VK_MENU)>=0);
417
if (wParam == '\r') {
418
if (message == WM_KEYDOWN) {
419
if (this == term_view) send_or_copy();
422
if (top) recursing = 0;
424
} else if (this==term_view && message==WM_KEYDOWN) {
425
if ((wParam==VK_UP || wParam==VK_DOWN) && at_eob()) {
426
if (wParam == VK_UP) recall_prev();
429
if (top) recursing = 0;
439
result = CRichEditView::WindowProc(message, wParam, lParam);
441
(message==WM_KEYDOWN && wParam=='\b' && smin==smax && smin==mark)) {
442
if (smax > mark) mark = smax;
445
if (this==term_view && message==WM_KEYDOWN && wParam==VK_HOME)
451
if (top && recalling) get_state();
452
result = CRichEditView::WindowProc(message, wParam, lParam);
453
if (top && recalling) {
454
long smin0=smin, smax0=smax, len0=len;
456
recalling = (smin0==smin && smax0==smax && len0==len);
460
if (top) recursing = 0;
465
mfc_term_view::on_undo()
467
/* note: WM_UNDO never sent or never reaches WindowProc
468
* -- neither does ID_EDIT_UNDO WM_COMMAND message?? */
470
CRichEditView::OnEditUndo();
478
mfc_term_view::get_state()
481
GetRichEditCtrl().GetSel(smin, smax);
485
mfc_term_view::bol(int offset)
487
long line = GetRichEditCtrl().LineFromChar(-1);
492
else if (line >= GetRichEditCtrl().GetLineCount())
493
line = GetRichEditCtrl().GetLineCount() - 1;
495
return GetRichEditCtrl().LineIndex(line);
499
mfc_term_view::eol(int offset)
501
long i = bol(offset);
502
return i + GetRichEditCtrl().LineLength(i);
506
mfc_term_view::eob(int beg)
508
long i = GetRichEditCtrl().LineIndex(GetRichEditCtrl().GetLineCount() - 1);
509
if (!beg) i += GetRichEditCtrl().LineLength(i);
514
mfc_term_view::at_eob()
517
GetRichEditCtrl().GetSel(mn, mx);
518
return (mn==mx && mx==eob());
522
mfc_term_view::grab_line(int offset) const
524
long line = GetRichEditCtrl().LineFromChar(-1);
527
if (line<0 || line>=GetRichEditCtrl().GetLineCount())
528
return (const char *)0;
530
int len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
531
LPSTR s = (char*)_alloca((len + 2)*2); // copied from MFC GetSelText
532
GetRichEditCtrl().GetLine(line, s, len+2);
538
mfc_term_view::select_lines(int off1, int off2)
540
GetRichEditCtrl().SetSel(bol(off1), bol(off2));
544
mfc_term_view::save_line(const char *txt) // to end of hist_view
547
GetRichEditCtrl().GetSel(mn0, mx0);
549
if (mx0 > i) mn0 = mx0 = i;
550
GetRichEditCtrl().SetSel(i, eob(0));
551
GetRichEditCtrl().ReplaceSel(txt);
552
GetRichEditCtrl().SetSel(mn0, mx0);
556
mfc_term_view::set_command(const char *txt) // at end of term_view
558
GetRichEditCtrl().SetSel(mark, eob(0));
559
GetRichEditCtrl().ReplaceSel(txt);
561
GetRichEditCtrl().GetSel(mn, mx);
562
GetRichEditCtrl().SetSel(mx, mx);
566
mfc_term_view::add_output(const char *txt) // at end of term_view
568
if (::IsWindow(m_hWnd)) {
569
long mn0, mx0, i, m = mark;
570
GetRichEditCtrl().GetSel(mn0, mx0);
571
GetRichEditCtrl().SetSel(mark, mark);
572
GetRichEditCtrl().ReplaceSel(txt);
573
GetRichEditCtrl().GetSel(i, mark);
574
if (mx0>m || (mx0==m && mn0==mx0)) {
575
if (mn0 < m) mn0 = mx0;
576
mn0 += mark-m, mx0 += mark-m;
578
GetRichEditCtrl().SetSel(mn0, mx0);
583
mfc_term_view::send_or_copy() // bound to Enter in term_view
586
GetRichEditCtrl().GetSel(i, m);
587
m = GetRichEditCtrl().LineIndex(GetRichEditCtrl().LineFromChar(mark));
588
if (i >= m) { // send new input as command line
590
//if (GetRichEditCtrl().LineLength(m)) {
591
GetRichEditCtrl().SetSel(m, m);
592
GetRichEditCtrl().ReplaceSel("\r\n");
593
GetRichEditCtrl().GetSel(i, m);
595
GetRichEditCtrl().SetSel(mark, m);
596
CString txt = GetRichEditCtrl().GetSelText();
597
if (mfc_deliver(txt, txt.GetLength())) {
598
hist_view->save_line(txt);
601
GetRichEditCtrl().SetSel(m, m);
603
} else { // copy current line as pending new command line
604
CString txt = grab_line();
605
set_command(w_deprompt((char *)(const char *)txt));
610
mfc_term_view::recall_prev() // bound to VK_UP in term_view
614
i = hist_view->eob(1);
615
hist_view->GetRichEditCtrl().SetSel(i, i);
617
i = hist_view->bol();
619
long j = hist_view->bol(-1);
621
hist_view->GetRichEditCtrl().SetSel(j, j);
622
CString txt = hist_view->grab_line();
630
mfc_term_view::recall_next() // bound to VK_DOWN in term_view
632
long i = hist_view->bol(1);
633
if (i < hist_view->eob(1)) {
634
hist_view->GetRichEditCtrl().SetSel(i, i);
635
CString txt = hist_view->grab_line();
643
mfc_term_view::home_mark() // after VK_HOME in term_view
646
GetRichEditCtrl().GetSel(i, m);
647
if (i < mark) { // adjust to mark
648
if (GetRichEditCtrl().LineFromChar(mark) ==
649
GetRichEditCtrl().LineFromChar(i))
650
GetRichEditCtrl().SetSel(mark, mark);
655
mfc_term_view::recall_line() // bound to Enter in hist_view
657
CString txt = grab_line();
658
term_view->set_command(txt);
659
long i = bol(0); // get first char of current line
660
if (i == bol(1)) { // this line typed at end: send it
661
term_view->send_or_copy();
663
} else if (term_view->is_visible) {
664
CMDIFrameWnd *fw = (CMDIFrameWnd *)(the_boss.m_pMainWnd);
665
fw->MDIActivate(term_view->GetParent());
667
GetRichEditCtrl().SetSel(i, i);
670
mfc_term_view *term_view = 0;
671
mfc_term_view *hist_view = 0;
673
/*------------------------------------------------------------------------*/
676
mfc_stdinit(void (**wout)(char*,long), void (**werr)(char*,long))
677
{ /* we are in worker thread here */
680
w_sending = CreateEvent(0, 1, 0, 0);
682
the_boss.m_pMainWnd->SendMessage(ID_CALL_FUNC, (WPARAM)0,
683
(LPARAM)&mfc_term_init);
684
w_add_input(w_sending, mfc_sender, 0);
694
mfc_sender(void *context)
695
{ /* we are in worker thread here */
696
ResetEvent(w_sending); /* set in boss thread to trigger this callback */
697
w_deliver(w_sendbuf(-1));
701
mfc_stdout(char *output_line, long len)
702
{ /* we are in worker thread here */
703
if (the_boss.m_pMainWnd)
704
the_boss.m_pMainWnd->SendMessage(ID_CALL_FUNC, (WPARAM)output_line,
705
(LPARAM)&mfc_bstdout);
709
mfc_stderr(char *output_line, long len)
710
{ /* we are in worker thread here */
711
if (the_boss.m_pMainWnd)
712
the_boss.m_pMainWnd->SendMessage(ID_CALL_FUNC, (WPARAM)output_line,
713
(LPARAM)&mfc_bstderr);
717
mfc_bstdout(char *output_line)
718
{ /* we are in boss thread here */
719
term_view->add_output(output_line);
723
mfc_bstderr(char *output_line)
724
{ /* we are in boss thread here */
725
term_view->add_output(output_line);
729
mfc_deliver(const char *buf, long len)
730
{ /* we are in boss thread here */
731
int ready = (WaitForSingleObject(w_sending,0) == WAIT_TIMEOUT);
732
if (ready && len>=0) {
733
char *line = w_sendbuf(len);
734
while (len--) *line++= *buf++;
743
{ /* abandon any pending input if SIGINT received
744
* -- without this, deadlock is possible
745
* -- actually, should be called from wpoll when clearing queue */
746
ResetEvent(w_sending);
749
/*------------------------------------------------------------------------*/
752
mfc_edit_view::on_choose_file()
756
dlg.m_ofn.lpstrTitle = "Insert Filename";
757
dlg.m_ofn.lpstrFile = name.GetBuffer(1025);
758
if (dlg.DoModal() == IDOK)
759
GetRichEditCtrl().ReplaceSel(name);
760
name.ReleaseBuffer();
764
mfc_edit_view::on_goto_out()
768
long line = GetRichEditCtrl().LineFromChar(-1);
769
for (line-- ; line>0 ; line--) {
770
len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
771
if (len>78) len = 78;
772
GetRichEditCtrl().GetLine(line, s, len);
774
if (w_deprompt(s) != s) break;
776
line = GetRichEditCtrl().LineIndex(line+1);
777
GetRichEditCtrl().SetSel(line, line);
781
mfc_edit_view::on_select_out()
785
long line = GetRichEditCtrl().LineFromChar(-1);
786
long linemx = GetRichEditCtrl().GetLineCount();
789
for (line-- ; line>=0 ; line--) {
790
len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
791
if (len>78) len = 78;
792
GetRichEditCtrl().GetLine(line, s, len);
794
if (w_deprompt(s) != s) break;
796
i = GetRichEditCtrl().LineIndex(line+1);
797
for (line=line0 ; line<linemx ; line++) {
798
len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
799
if (len>78) len = 78;
800
GetRichEditCtrl().GetLine(line, s, len);
802
if (w_deprompt(s) != s) break;
804
j = GetRichEditCtrl().LineIndex(line);
805
GetRichEditCtrl().SetSel(i, j);
809
mfc_edit_view::on_open_line()
811
int len, i, n, oline=0;
813
long line = GetRichEditCtrl().LineFromChar(-1);
814
for (n=0 ; n<10 ; n++,line--) {
815
len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
816
if (len>1075) len = 1075;
817
GetRichEditCtrl().GetLine(line, s, len);
819
for (i=4 ; i<len-8 ; i++) {
821
s[i-1]=='E' && s[i-2]=='N' && s[i-3]=='I' && s[i-4]=='L') {
822
for (i++ ; i<len-7 ; i++) if (s[i]!=' ' && s[i]!='\t') break;
823
while (s[i]>='0' && s[i]<='9') oline = 10*oline + (s[i++]-'0');
824
for (i+=4 ; i<len-1 ; i++) {
826
s[i-1]=='E' && s[i-2]=='L' && s[i-3]=='I' && s[i-4]=='F') {
827
for (i++ ; i<len ; i++) if (s[i]!=' ' && s[i]!='\t') break;
829
/* document name s line number oline */
831
(mfc_edit_doc *)the_boss.OpenDocumentFile(&s[i]);
833
mfc_edit_view *view = (mfc_edit_view *)doc->GetView();
834
long ll = view->GetRichEditCtrl().LineIndex(oline-1);
835
view->GetRichEditCtrl().SetSel(ll, ll);
850
w_deprompt(char *text)
853
/* remove regexp "^[a-zA-Z0-9_-]*> *" from text */
854
while ((t[0]>='a' && t[0]<='z') || (t[0]>='A' && t[0]<='Z') ||
855
(t[0]>='0' && t[0]<='9') || t[0]=='_' || t[0]=='-') t++;
858
while (t[0]==' ' || t[0]=='\t') t++;