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