1 /*
2 * $Id: mfcterm.cpp,v 1.1 2005-09-18 22:05:38 dhmunro Exp $
3 * richedit class for play MDI development environment
4 */
5 /* Copyright (c) 2005, The Regents of the University of California.
6 * All rights reserved.
7 * This file is part of yorick (http://yorick.sourceforge.net).
8 * Read the accompanying LICENSE file for details.
9 */
10
11 #include "mfcapp.h"
12 #include "mfcterm.h"
13 #include "mfcres.h"
14 #include "playw.h"
15
16 #include <afxole.h>
17
18 #include <malloc.h>
19
20 #ifdef _DEBUG
21 #define new DEBUG_NEW
22 #undef THIS_FILE
23 static char THIS_FILE[] = __FILE__;
24 #endif
25
26 static HANDLE w_sending = 0;
27 static void mfc_sender(void *context);
28 static void mfc_stdout(char *output_line, long len);
29 static void mfc_stderr(char *output_line, long len);
30 static void mfc_bstdout(char *output_line);
31 static void mfc_bstderr(char *output_line);
32 static int mfc_deliver(const char *buf, long len);
33
34 static char *w_deprompt(char *text);
35
IMPLEMENT_DYNCREATE(mfc_edit_view,CRichEditView)36 IMPLEMENT_DYNCREATE(mfc_edit_view, CRichEditView)
37
38 BEGIN_MESSAGE_MAP(mfc_edit_view, CRichEditView)
39 ON_COMMAND(ID_GOTO_LINE, on_goto_line)
40 ON_COMMAND(ID_GOTO_OUT, on_goto_out)
41 ON_COMMAND(ID_SELECT_OUT, on_select_out)
42 ON_COMMAND(ID_OPEN_LINE, on_open_line)
43 ON_COMMAND(ID_CHOOSE_FILE, on_choose_file)
44 ON_COMMAND(ID_EDIT_REDO, on_redo)
45 ON_UPDATE_COMMAND_UI(ID_GOTO_OUT, on_update_term)
46 ON_UPDATE_COMMAND_UI(ID_SELECT_OUT, on_update_term)
47 ON_UPDATE_COMMAND_UI(ID_OPEN_LINE, on_update_term)
48 ON_UPDATE_COMMAND_UI(ID_CHOOSE_FILE, on_update_term)
49 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, on_update_redo)
50 ON_WM_CREATE()
51
52 ON_COMMAND(ID_FILE_PRINT, CRichEditView::OnFilePrint)
53 ON_COMMAND(ID_FILE_PRINT_DIRECT, CRichEditView::OnFilePrint)
54 ON_COMMAND(ID_FILE_PRINT_PREVIEW, CRichEditView::OnFilePrintPreview)
55 END_MESSAGE_MAP()
56
57 mfc_edit_view::mfc_edit_view()
58 {
59 m_nWordWrap = WrapNone;
60 is_term = riched2 = 0;
61 }
62
~mfc_edit_view()63 mfc_edit_view::~mfc_edit_view()
64 {
65 }
66
67 BOOL
PreCreateWindow(CREATESTRUCT & cs)68 mfc_edit_view::PreCreateWindow(CREATESTRUCT& cs)
69 {
70 BOOL result = CRichEditView::PreCreateWindow(cs);
71
72 #if (_RICHEDIT_VER < 0x200) && !defined(NO_RICHEDIT2)
73 // The following lines will turn on richedit 2.0 or 3.0 controls.
74 // However, the compatibility with the rest of the class is botched so that
75 // many of the MFC functions only work with 1.0 semantics (_RICHEDIT_VER is
76 // set in afxwin.h to force 1.0 in riched.h).
77 // I prefer not to risk this with the terminal and history windows, but
78 // the lure of multilevel undo is irresistable for editor windows.
79 static int re2_initialized = 0;
80 if (result) {
81 if (!re2_initialized) {
82 if (!LoadLibraryA("riched20.dll")) // riched32.dll is 1.0 version
83 re2_initialized = 2;
84 else
85 re2_initialized = 1;
86 }
87 if (re2_initialized==1) {
88 cs.lpszClass = RICHEDIT_CLASSA; // RICHEDIT_CLASS set to 1.0 version
89 // unless _RICHEDIT_VER >= 0x200
90 riched2 = 1;
91 }
92 }
93 #endif
94
95 return result;
96 }
97
98 void
OnInitialUpdate()99 mfc_edit_view::OnInitialUpdate()
100 {
101 CRichEditView::OnInitialUpdate();
102 // set the printing margins (720 twips = 1/2 inch).
103 SetMargins(CRect(720, 720, 720, 720));
104 }
105
106 BOOL
OnPreparePrinting(CPrintInfo * pInfo)107 mfc_edit_view::OnPreparePrinting(CPrintInfo* pInfo)
108 {
109 // default preparation
110 return DoPreparePrinting(pInfo);
111 }
112
113 void
OnDestroy()114 mfc_edit_view::OnDestroy()
115 {
116 CRichEditView::OnDestroy();
117
118 // Deactivate the item on destruction; this is important
119 // when a splitter view is being used.
120 COleClientItem* item = GetDocument()->GetInPlaceActiveItem(this);
121 if (item && item->GetActiveView()==this)
122 item->Deactivate();
123 }
124
125 static CFont fixed_font;
126 static int font_init = 0;
127
128 int
OnCreate(LPCREATESTRUCT lpcs)129 mfc_edit_view::OnCreate(LPCREATESTRUCT lpcs)
130 {
131 if (CCtrlView::OnCreate(lpcs) != 0)
132 return -1;
133 GetRichEditCtrl().LimitText(lMaxSize);
134 GetRichEditCtrl().SetEventMask(ENM_SELCHANGE | ENM_CHANGE | ENM_SCROLL);
135 VERIFY(GetRichEditCtrl().SetOLECallback(&m_xRichEditOleCallback));
136 m_lpRichEditOle = GetRichEditCtrl().GetIRichEditOle();
137 DragAcceptFiles(0);
138 GetRichEditCtrl().SetOptions(ECOOP_OR, ECO_AUTOWORDSELECTION);
139
140 if (!font_init) {
141 HFONT f = (HFONT)GetStockObject(ANSI_FIXED_FONT);
142 LOGFONT lf;
143 if (::GetObject((HGDIOBJ)f, sizeof(LOGFONT), &lf)) {
144 fixed_font.CreateFontIndirect(&lf);
145 } else {
146 return 1;
147 }
148 font_init = 1;
149 }
150 GetRichEditCtrl().SetFont(&fixed_font,0);
151
152 WrapChanged();
153 ASSERT(m_lpRichEditOle != NULL);
154 return 0;
155 }
156
157 // without this, idiot will paste files and other objects into window
158 HRESULT
QueryAcceptData(LPDATAOBJECT lpdataobj,CLIPFORMAT * lpcfFormat,DWORD reco,BOOL fReally,HGLOBAL hMetaPict)159 mfc_edit_view::QueryAcceptData(LPDATAOBJECT lpdataobj,
160 CLIPFORMAT* lpcfFormat, DWORD reco,
161 BOOL fReally, HGLOBAL hMetaPict)
162 {
163 if (*lpcfFormat == CF_TEXT) return S_OK;
164 COleDataObject dataobj;
165 dataobj.Attach(lpdataobj, FALSE);
166 if (*lpcfFormat==0 && dataobj.IsDataAvailable(CF_TEXT)) {
167 *lpcfFormat = CF_TEXT;
168 return S_OK;
169 }
170 return S_FALSE;
171 }
172
173 class mfc_goto : public CDialog
174 {
175 public:
176 mfc_goto(mfc_edit_view *v);
177 virtual void OnOK();
178 mfc_edit_view *view;
179 DECLARE_MESSAGE_MAP()
180 };
181
BEGIN_MESSAGE_MAP(mfc_goto,CDialog)182 BEGIN_MESSAGE_MAP(mfc_goto, CDialog)
183 END_MESSAGE_MAP()
184
185 mfc_goto::mfc_goto(mfc_edit_view *v) : CDialog(IDD_GOTO_LINE)
186 {
187 view = v;
188 }
189
190 void
OnOK()191 mfc_goto::OnOK()
192 {
193 USES_CONVERSION;
194 CEdit *ce = (CEdit *)GetDlgItem(IDC_GOTO_LINE);
195 TCHAR wline[16];
196 char *line;
197 int n = ce->GetLine(0, wline, 12);
198 line = T2A(wline);
199 if (n>0) {
200 line[n] = '\0';
201 n = atoi(line)-1;
202 if (n < 0) n = 0;
203 n = view->GetRichEditCtrl().LineIndex(n);
204 view->GetRichEditCtrl().SetSel(n,n);
205 }
206 CDialog::OnOK();
207 }
208
209 void
on_goto_line()210 mfc_edit_view::on_goto_line()
211 {
212 mfc_goto goto_dlg(this);
213 goto_dlg.DoModal();
214 }
215
216 void
on_redo()217 mfc_edit_view::on_redo()
218 {
219 ASSERT(::IsWindow(m_hWnd));
220 ::SendMessage(m_hWnd, EM_REDO, 0, 0);
221 m_bSyncCharFormat = m_bSyncParaFormat = 1;
222 }
223
224 void
on_update_term(CCmdUI * ui)225 mfc_edit_view::on_update_term(CCmdUI *ui)
226 {
227 ui->Enable(is_term);
228 }
229
230 void
on_update_redo(CCmdUI * ui)231 mfc_edit_view::on_update_redo(CCmdUI *ui)
232 {
233 if (riched2) {
234 ASSERT(::IsWindow(m_hWnd));
235 ui->Enable((BOOL)::SendMessage(m_hWnd, EM_CANREDO, 0, 0));
236 } else {
237 ui->Enable(0);
238 }
239 }
240
241 /*------------------------------------------------------------------------*/
242
IMPLEMENT_DYNCREATE(mfc_edit_child,CMDIChildWnd)243 IMPLEMENT_DYNCREATE(mfc_edit_child, CMDIChildWnd)
244
245 BEGIN_MESSAGE_MAP(mfc_edit_child, CMDIChildWnd)
246 ON_WM_CLOSE()
247 END_MESSAGE_MAP()
248
249 mfc_edit_child::mfc_edit_child()
250 {
251 }
252
~mfc_edit_child()253 mfc_edit_child::~mfc_edit_child()
254 {
255 }
256
257 void
OnClose()258 mfc_edit_child::OnClose()
259 {
260 if (this == term_view->GetParent())
261 AfxGetApp()->m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_TERM, 0);
262 else if (this == hist_view->GetParent())
263 AfxGetApp()->m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_HIST, 0);
264 else
265 CMDIChildWnd::OnClose();
266 }
267
268 /*------------------------------------------------------------------------*/
269
IMPLEMENT_DYNCREATE(mfc_edit_doc,CRichEditDoc)270 IMPLEMENT_DYNCREATE(mfc_edit_doc, CRichEditDoc)
271
272 BEGIN_MESSAGE_MAP(mfc_edit_doc, CRichEditDoc)
273 //{{AFX_MSG_MAP(mfc_edit_doc)
274 ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
275 //}}AFX_MSG_MAP
276 // Enable default OLE container implementation
277 ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_LINKS, CRichEditDoc::OnUpdateEditLinksMenu)
278 ON_COMMAND(ID_OLE_EDIT_LINKS, CRichEditDoc::OnEditLinks)
279 ON_UPDATE_COMMAND_UI_RANGE(ID_OLE_VERB_FIRST, ID_OLE_VERB_LAST, CRichEditDoc::OnUpdateObjectVerbMenu)
280 END_MESSAGE_MAP()
281
282 mfc_edit_doc::mfc_edit_doc()
283 {
284 }
285
mfc_edit_doc(CMultiDocTemplate * mdt,int hist)286 mfc_edit_doc::mfc_edit_doc(CMultiDocTemplate *mdt, int hist)
287 {
288 mdt->AddDocument(this);
289 m_pDocTemplate = mdt;
290 mfc_edit_child *frame = new mfc_edit_child;
291 CCreateContext context;
292 context.m_pCurrentFrame = 0;
293 context.m_pCurrentDoc = this;
294 context.m_pNewViewClass = RUNTIME_CLASS(mfc_term_view);
295 context.m_pNewDocTemplate = mdt;
296 frame->LoadFrame(IDR_EDITFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,
297 0, &context);
298 SetTitle(hist? _T("*history*") : _T("*terminal*"));
299 OnNewDocument();
300 if (!hist)
301 ((mfc_term_view *)GetView())->is_term = 1;
302 }
303
~mfc_edit_doc()304 mfc_edit_doc::~mfc_edit_doc()
305 {
306 }
307
308 CRichEditCntrItem*
CreateClientItem(REOBJECT * preo) const309 mfc_edit_doc::CreateClientItem(REOBJECT* preo) const
310 {
311 return 0;
312 }
313
314 void
Serialize(CArchive & ar)315 mfc_edit_doc::Serialize(CArchive& ar)
316 {
317 if (ar.IsStoring()) {
318 // TODO: add storing code here
319 } else {
320 // TODO: add loading code here
321 }
322
323 // Calling the base class CRichEditDoc enables serialization
324 // of the container document's COleClientItem objects.
325 m_bRTF = 0;
326 CRichEditDoc::Serialize(ar);
327 }
328
329 BOOL
OnNewDocument()330 mfc_edit_doc::OnNewDocument()
331 {
332 if (!CRichEditDoc::OnNewDocument())
333 return FALSE;
334
335 // TODO: add reinitialization code here
336 // (SDI documents will reuse this document)
337
338 return TRUE;
339 }
340
341 BOOL
OnOpenDocument(LPCTSTR lpszPathName)342 mfc_edit_doc::OnOpenDocument(LPCTSTR lpszPathName)
343 {
344 if (!CRichEditDoc::OnOpenDocument(lpszPathName))
345 return FALSE;
346
347 // TODO: Add your specialized creation code here
348
349 return TRUE;
350 }
351
352 BOOL
SaveModified()353 mfc_edit_doc::SaveModified()
354 {
355 mfc_edit_view *view = (mfc_edit_view *)GetView();
356 if (view==term_view || view==hist_view)
357 return 1;
358 else
359 return CRichEditDoc::SaveModified();
360 }
361
362 void
OnFileClose()363 mfc_edit_doc::OnFileClose()
364 {
365 mfc_edit_view *view = (mfc_edit_view *)GetView();
366 if (view == term_view)
367 AfxGetApp()->m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_TERM, 0);
368 else if (view == hist_view)
369 AfxGetApp()->m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_HIST, 0);
370 else
371 COleServerDoc::OnCloseDocument();
372 }
373
374 /*------------------------------------------------------------------------*/
375
IMPLEMENT_DYNCREATE(mfc_term_view,mfc_edit_view)376 IMPLEMENT_DYNCREATE(mfc_term_view, mfc_edit_view)
377
378 BEGIN_MESSAGE_MAP(mfc_term_view, mfc_edit_view)
379 ON_COMMAND(ID_EDIT_UNDO, on_undo)
380 END_MESSAGE_MAP()
381
382 mfc_term_view::mfc_term_view()
383 {
384 mark = smin = smax = len = 0;
385 is_visible = recalling = recursing = 0;
386 }
387
~mfc_term_view()388 mfc_term_view::~mfc_term_view()
389 {
390 }
391
392 BOOL
PreCreateWindow(CREATESTRUCT & cs)393 mfc_term_view::PreCreateWindow(CREATESTRUCT& cs)
394 {
395 return CRichEditView::PreCreateWindow(cs);
396 }
397
398 void
OnInitialUpdate()399 mfc_term_view::OnInitialUpdate()
400 {
401 mfc_edit_view::OnInitialUpdate();
402 }
403
404 LRESULT
WindowProc(UINT message,WPARAM wParam,LPARAM lParam)405 mfc_term_view::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
406 {
407 LRESULT result = 0;
408 int key_unshifted = 0;
409 long len0 = 0;
410 int top = !recursing;
411
412 if (top) {
413 if (message==WM_DESTROY) top = 0;
414 recursing = 1;
415 }
416
417 switch (message) {
418 case WM_KEYDOWN:
419 case WM_CHAR:
420 key_unshifted = (GetKeyState(VK_SHIFT)>=0 && GetKeyState(VK_CONTROL)>=0 &&
421 GetKeyState(VK_MENU)>=0);
422 if (key_unshifted) {
423 if (wParam == '\r') {
424 if (message == WM_KEYDOWN) {
425 if (this == term_view) send_or_copy();
426 else recall_line();
427 }
428 if (top) recursing = 0;
429 return 0;
430 } else if (this==term_view && message==WM_KEYDOWN) {
431 if ((wParam==VK_UP || wParam==VK_DOWN) && at_eob()) {
432 if (wParam == VK_UP) recall_prev();
433 else recall_next();
434 recalling = 1;
435 if (top) recursing = 0;
436 return 0;
437 }
438 }
439 }
440 /* drop through */
441 case WM_CUT:
442 case WM_PASTE:
443 case EM_REPLACESEL:
444 get_state();
445 result = CRichEditView::WindowProc(message, wParam, lParam);
446 if (smin<mark ||
447 (message==WM_KEYDOWN && wParam=='\b' && smin==smax && smin==mark)) {
448 if (smax > mark) mark = smax;
449 mark += eob() - len;
450 }
451 if (this==term_view && message==WM_KEYDOWN && wParam==VK_HOME)
452 home_mark();
453 recalling = 0;
454 break;
455
456 default:
457 if (top && recalling) get_state();
458 result = CRichEditView::WindowProc(message, wParam, lParam);
459 if (top && recalling) {
460 long smin0=smin, smax0=smax, len0=len;
461 get_state();
462 recalling = (smin0==smin && smax0==smax && len0==len);
463 }
464 }
465
466 if (top) recursing = 0;
467 return result;
468 }
469
470 void
on_undo()471 mfc_term_view::on_undo()
472 {
473 /* note: WM_UNDO never sent or never reaches WindowProc
474 * -- neither does ID_EDIT_UNDO WM_COMMAND message?? */
475 long len0 = eob();
476 CRichEditView::OnEditUndo();
477 get_state();
478 if (smin < mark)
479 mark += len - len0;
480 recalling = 0;
481 }
482
483 void
get_state()484 mfc_term_view::get_state()
485 {
486 len = eob();
487 GetRichEditCtrl().GetSel(smin, smax);
488 }
489
490 long
bol(int offset)491 mfc_term_view::bol(int offset)
492 {
493 long line = GetRichEditCtrl().LineFromChar(-1);
494 if (offset) {
495 line += offset;
496 if (line < 0)
497 line = 0;
498 else if (line >= GetRichEditCtrl().GetLineCount())
499 line = GetRichEditCtrl().GetLineCount() - 1;
500 }
501 return GetRichEditCtrl().LineIndex(line);
502 }
503
504 long
eol(int offset)505 mfc_term_view::eol(int offset)
506 {
507 long i = bol(offset);
508 return i + GetRichEditCtrl().LineLength(i);
509 }
510
511 long
eob(int beg)512 mfc_term_view::eob(int beg)
513 {
514 long i = GetRichEditCtrl().LineIndex(GetRichEditCtrl().GetLineCount() - 1);
515 if (!beg) i += GetRichEditCtrl().LineLength(i);
516 return i;
517 }
518
519 int
at_eob()520 mfc_term_view::at_eob()
521 {
522 long mn, mx;
523 GetRichEditCtrl().GetSel(mn, mx);
524 return (mn==mx && mx==eob());
525 }
526
527 CString
grab_line(int offset) const528 mfc_term_view::grab_line(int offset) const
529 {
530 long line = GetRichEditCtrl().LineFromChar(-1);
531 if (offset) {
532 line += offset;
533 if (line<0 || line>=GetRichEditCtrl().GetLineCount())
534 return (const char *)0;
535 }
536 int len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
537 LPTSTR s = (LPTSTR)_alloca((len + 2)*2); // copied from MFC GetSelText
538 GetRichEditCtrl().GetLine(line, s, len+2);
539 s[len] = '\0';
540 return s;
541 }
542
543 void
select_lines(int off1,int off2)544 mfc_term_view::select_lines(int off1, int off2)
545 {
546 GetRichEditCtrl().SetSel(bol(off1), bol(off2));
547 }
548
549 void
save_line(const char * txt)550 mfc_term_view::save_line(const char *txt) // to end of hist_view
551 {
552 USES_CONVERSION;
553 long mn0, mx0;
554 GetRichEditCtrl().GetSel(mn0, mx0);
555 long i = eob(1);
556 if (mx0 > i) mn0 = mx0 = i;
557 GetRichEditCtrl().SetSel(i, eob(0));
558 GetRichEditCtrl().ReplaceSel(A2T(txt));
559 GetRichEditCtrl().SetSel(mn0, mx0);
560 }
561
562 void
set_command(const char * txt)563 mfc_term_view::set_command(const char *txt) // at end of term_view
564 {
565 USES_CONVERSION;
566 GetRichEditCtrl().SetSel(mark, eob(0));
567 GetRichEditCtrl().ReplaceSel(A2T(txt));
568 long mn, mx;
569 GetRichEditCtrl().GetSel(mn, mx);
570 GetRichEditCtrl().SetSel(mx, mx);
571 }
572
573 void
add_output(const char * txt)574 mfc_term_view::add_output(const char *txt) // at end of term_view
575 {
576 USES_CONVERSION;
577 if (::IsWindow(m_hWnd)) {
578 long mn0, mx0, i, m = mark;
579 GetRichEditCtrl().GetSel(mn0, mx0);
580 GetRichEditCtrl().SetSel(mark, mark);
581 GetRichEditCtrl().ReplaceSel(A2T(txt));
582 GetRichEditCtrl().GetSel(i, mark);
583 if (mx0>m || (mx0==m && mn0==mx0)) {
584 if (mn0 < m) mn0 = mx0;
585 mn0 += mark-m, mx0 += mark-m;
586 }
587 GetRichEditCtrl().SetSel(mn0, mx0);
588 }
589 }
590
591 void
send_or_copy()592 mfc_term_view::send_or_copy() // bound to Enter in term_view
593 {
594 USES_CONVERSION;
595 long i, m;
596 GetRichEditCtrl().GetSel(i, m);
597 m = GetRichEditCtrl().LineIndex(GetRichEditCtrl().LineFromChar(mark));
598 if (i >= m) { // send new input as command line
599 m = eob();
600 //if (GetRichEditCtrl().LineLength(m)) {
601 GetRichEditCtrl().SetSel(m, m);
602 GetRichEditCtrl().ReplaceSel(_T("\r\n"));
603 GetRichEditCtrl().GetSel(i, m);
604 //}
605 GetRichEditCtrl().SetSel(mark, m);
606 CString txt = GetRichEditCtrl().GetSelText();
607 char *ttxt = T2A(txt);
608 if (mfc_deliver(ttxt, txt.GetLength())) {
609 hist_view->save_line(ttxt);
610 mark = m;
611 }
612 GetRichEditCtrl().SetSel(m, m);
613
614 } else { // copy current line as pending new command line
615 CString txt = grab_line();
616 set_command(w_deprompt(T2A(txt)));
617 }
618 }
619
620 void
recall_prev()621 mfc_term_view::recall_prev() // bound to VK_UP in term_view
622 {
623 USES_CONVERSION;
624 long i;
625 if (!recalling) {
626 i = hist_view->eob(1);
627 hist_view->GetRichEditCtrl().SetSel(i, i);
628 } else {
629 i = hist_view->bol();
630 }
631 long j = hist_view->bol(-1);
632 if (j < i) {
633 hist_view->GetRichEditCtrl().SetSel(j, j);
634 CString txt = hist_view->grab_line();
635 set_command(T2A(txt));
636 } else {
637 MessageBeep(MB_OK);
638 }
639 }
640
641 void
recall_next()642 mfc_term_view::recall_next() // bound to VK_DOWN in term_view
643 {
644 USES_CONVERSION;
645 long i = hist_view->bol(1);
646 if (i < hist_view->eob(1)) {
647 hist_view->GetRichEditCtrl().SetSel(i, i);
648 CString txt = hist_view->grab_line();
649 set_command(T2A(txt));
650 } else {
651 MessageBeep(MB_OK);
652 }
653 }
654
655 void
home_mark()656 mfc_term_view::home_mark() // after VK_HOME in term_view
657 {
658 long i, m;
659 GetRichEditCtrl().GetSel(i, m);
660 if (i < mark) { // adjust to mark
661 if (GetRichEditCtrl().LineFromChar(mark) ==
662 GetRichEditCtrl().LineFromChar(i))
663 GetRichEditCtrl().SetSel(mark, mark);
664 }
665 }
666
667 void
recall_line()668 mfc_term_view::recall_line() // bound to Enter in hist_view
669 {
670 USES_CONVERSION;
671 CString txt = grab_line();
672 term_view->set_command(T2A(txt));
673 long i = bol(0); // get first char of current line
674 if (i == bol(1)) { // this line typed at end: send it
675 term_view->send_or_copy();
676 i = eob();
677 } else if (term_view->is_visible) {
678 CMDIFrameWnd *fw = (CMDIFrameWnd *)(the_boss.m_pMainWnd);
679 fw->MDIActivate(term_view->GetParent());
680 }
681 GetRichEditCtrl().SetSel(i, i);
682 }
683
684 mfc_term_view *term_view = 0;
685 mfc_term_view *hist_view = 0;
686
687 /*------------------------------------------------------------------------*/
688
689 int
mfc_stdinit(void (** wout)(char *,long),void (** werr)(char *,long))690 mfc_stdinit(void (**wout)(char*,long), void (**werr)(char*,long))
691 { /* we are in worker thread here */
692 int result = 1;
693 *wout = *werr = 0;
694 w_sending = CreateEvent(0, 1, 0, 0);
695 if (w_sending) {
696 the_boss.m_pMainWnd->SendMessage(ID_CALL_FUNC, (WPARAM)0,
697 (LPARAM)&mfc_term_init);
698 w_add_input(w_sending, mfc_sender, 0);
699 *wout = mfc_stdout;
700 *werr = mfc_stderr;
701 result = 0;
702 }
703 return result;
704 }
705
706 /* ARGSUSED */
707 static void
mfc_sender(void * context)708 mfc_sender(void *context)
709 { /* we are in worker thread here */
710 ResetEvent(w_sending); /* set in boss thread to trigger this callback */
711 w_deliver(w_sendbuf(-1));
712 }
713
714 static void
mfc_stdout(char * output_line,long len)715 mfc_stdout(char *output_line, long len)
716 { /* we are in worker thread here */
717 if (the_boss.m_pMainWnd)
718 the_boss.m_pMainWnd->SendMessage(ID_CALL_FUNC, (WPARAM)output_line,
719 (LPARAM)&mfc_bstdout);
720 }
721
722 static void
mfc_stderr(char * output_line,long len)723 mfc_stderr(char *output_line, long len)
724 { /* we are in worker thread here */
725 if (the_boss.m_pMainWnd)
726 the_boss.m_pMainWnd->SendMessage(ID_CALL_FUNC, (WPARAM)output_line,
727 (LPARAM)&mfc_bstderr);
728 }
729
730 static void
mfc_bstdout(char * output_line)731 mfc_bstdout(char *output_line)
732 { /* we are in boss thread here */
733 term_view->add_output(output_line);
734 }
735
736 static void
mfc_bstderr(char * output_line)737 mfc_bstderr(char *output_line)
738 { /* we are in boss thread here */
739 term_view->add_output(output_line);
740 }
741
742 static int
mfc_deliver(const char * buf,long len)743 mfc_deliver(const char *buf, long len)
744 { /* we are in boss thread here */
745 int ready = (WaitForSingleObject(w_sending,0) == WAIT_TIMEOUT);
746 if (ready && len>=0) {
747 char *line = w_sendbuf(len);
748 while (len--) *line++= *buf++;
749 line[0] = '\0';
750 SetEvent(w_sending);
751 }
752 return ready;
753 }
754
755 void
mfc_reset_stdin()756 mfc_reset_stdin()
757 { /* abandon any pending input if SIGINT received
758 * -- without this, deadlock is possible
759 * -- actually, should be called from wpoll when clearing queue */
760 ResetEvent(w_sending);
761 }
762
763 /*------------------------------------------------------------------------*/
764
765 void
on_choose_file()766 mfc_edit_view::on_choose_file()
767 {
768 CFileDialog dlg(1);
769 CString name;
770 dlg.m_ofn.lpstrTitle = _T("Insert Filename");
771 dlg.m_ofn.lpstrFile = name.GetBuffer(1025);
772 if (dlg.DoModal() == IDOK)
773 GetRichEditCtrl().ReplaceSel(name);
774 name.ReleaseBuffer();
775 }
776
777 void
on_goto_out()778 mfc_edit_view::on_goto_out()
779 {
780 USES_CONVERSION;
781 int len;
782 TCHAR ws[80];
783 char *s;
784 long line = GetRichEditCtrl().LineFromChar(-1);
785 for (line-- ; line>0 ; line--) {
786 len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
787 if (len>78) len = 78;
788 GetRichEditCtrl().GetLine(line, ws, len);
789 ws[len] = '\0';
790 s = T2A(ws);
791 if (w_deprompt(s) != s) break;
792 }
793 line = GetRichEditCtrl().LineIndex(line+1);
794 GetRichEditCtrl().SetSel(line, line);
795 }
796
797 void
on_select_out()798 mfc_edit_view::on_select_out()
799 {
800 USES_CONVERSION;
801 int len;
802 TCHAR ws[80];
803 char *s;
804 long line = GetRichEditCtrl().LineFromChar(-1);
805 long linemx = GetRichEditCtrl().GetLineCount();
806 long line0 = line;
807 long i, j;
808 for (line-- ; line>=0 ; line--) {
809 len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
810 if (len>78) len = 78;
811 GetRichEditCtrl().GetLine(line, ws, len);
812 ws[len] = '\0';
813 s = T2A(ws);
814 if (w_deprompt(s) != s) break;
815 }
816 i = GetRichEditCtrl().LineIndex(line+1);
817 for (line=line0 ; line<linemx ; line++) {
818 len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
819 if (len>78) len = 78;
820 GetRichEditCtrl().GetLine(line, ws, len);
821 ws[len] = '\0';
822 s = T2A(ws);
823 if (w_deprompt(s) != s) break;
824 }
825 j = GetRichEditCtrl().LineIndex(line);
826 GetRichEditCtrl().SetSel(i, j);
827 }
828
829 void
on_open_line()830 mfc_edit_view::on_open_line()
831 {
832 USES_CONVERSION;
833 int len, i, n, oline=0;
834 TCHAR ws[1076];
835 char *s;
836 long line = GetRichEditCtrl().LineFromChar(-1);
837 for (n=0 ; n<10 ; n++,line--) {
838 len = GetRichEditCtrl().LineLength(GetRichEditCtrl().LineIndex(line));
839 if (len>1075) len = 1075;
840 GetRichEditCtrl().GetLine(line, ws, len);
841 ws[len] = '\0';
842 s = T2A(ws);
843 for (i=4 ; i<len-8 ; i++) {
844 if (s[i]==':' &&
845 s[i-1]=='E' && s[i-2]=='N' && s[i-3]=='I' && s[i-4]=='L') {
846 for (i++ ; i<len-7 ; i++) if (s[i]!=' ' && s[i]!='\t') break;
847 while (s[i]>='0' && s[i]<='9') oline = 10*oline + (s[i++]-'0');
848 for (i+=4 ; i<len-1 ; i++) {
849 if (s[i]==':' &&
850 s[i-1]=='E' && s[i-2]=='L' && s[i-3]=='I' && s[i-4]=='F') {
851 for (i++ ; i<len ; i++) if (s[i]!=' ' && s[i]!='\t') break;
852 if (i < len) {
853 /* document name s line number oline */
854 mfc_edit_doc *doc=
855 (mfc_edit_doc *)the_boss.OpenDocumentFile(A2T(&s[i]));
856 if (doc) {
857 mfc_edit_view *view = (mfc_edit_view *)doc->GetView();
858 long ll = view->GetRichEditCtrl().LineIndex(oline-1);
859 view->GetRichEditCtrl().SetSel(ll, ll);
860 } else {
861 MessageBeep(MB_OK);
862 }
863 return;
864 }
865 }
866 }
867 }
868 }
869 }
870 MessageBeep(MB_OK);
871 }
872
873 static char *
w_deprompt(char * text)874 w_deprompt(char *text)
875 {
876 char *t = text;
877 /* remove regexp "^[a-zA-Z0-9_-]*> *" from text */
878 while ((t[0]>='a' && t[0]<='z') || (t[0]>='A' && t[0]<='Z') ||
879 (t[0]>='0' && t[0]<='9') || t[0]=='_' || t[0]=='-') t++;
880 if (t[0]=='>') {
881 t++;
882 while (t[0]==' ' || t[0]=='\t') t++;
883 } else {
884 t = text;
885 }
886 return t;
887 }
888