xref: /haiku/src/apps/terminal/TermView.cpp (revision 020cbad9d40235a2c50a81a42d69912a5ff8fbc4)
1 /*
2  * Copyright 2001-2007, Haiku, Inc.
3  * Copyright 2003-2004 Kian Duffy, myob@users.sourceforge.net
4  * Parts Copyright 1998-1999 Kazuho Okui and Takashi Murai.
5  * All rights reserved. Distributed under the terms of the MIT license.
6  *
7  * Authors:
8  *		Stefano Ceccherini <stefano.ceccherini@gmail.com>
9  *		Kian Duffy, myob@users.sourceforge.net
10  *		Y.Hayakawa, hida@sawada.riec.tohoku.ac.jp
11  */
12 
13 
14 #include "TermView.h"
15 
16 #include "CodeConv.h"
17 #include "Shell.h"
18 #include "TermBuffer.h"
19 #include "TermConst.h"
20 #include "VTkeymap.h"
21 
22 #include <Alert.h>
23 #include <Beep.h>
24 #include <Clipboard.h>
25 #include <Debug.h>
26 #include <Input.h>
27 #include <Message.h>
28 #include <MessageRunner.h>
29 #include <Path.h>
30 #include <PopUpMenu.h>
31 #include <PropertyInfo.h>
32 #include <Roster.h>
33 #include <ScrollBar.h>
34 #include <String.h>
35 #include <Window.h>
36 
37 #include <ctype.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <termios.h>
42 
43 #include <new>
44 #include <algorithm>
45 
46 // defined VTKeyTbl.c
47 extern int function_keycode_table[];
48 extern char *function_key_char_table[];
49 
50 const static rgb_color kTermColorTable[8] = {
51 	{ 40,  40,  40, 0},	// black
52 	{204,   0,   0, 0},	// red
53 	{ 78, 154,   6, 0},	// green
54 	{218, 168,   0, 0},	// yellow
55 	{ 51, 102, 152, 0},	// blue
56 	{115,  68, 123, 0},	// magenta
57 	{  6, 152, 154, 0},	// cyan
58 	{245, 245, 245, 0},	// white
59 };
60 
61 
62 #define ROWS_DEFAULT 25
63 #define COLUMNS_DEFAULT 80
64 
65 
66 static property_info sPropList[] = {
67 	{ "encoding",
68 	{B_GET_PROPERTY, 0},
69 	{B_DIRECT_SPECIFIER, 0},
70 	"get terminal encoding"},
71 	{ "encoding",
72 	{B_SET_PROPERTY, 0},
73 	{B_DIRECT_SPECIFIER, 0},
74 	"set terminal encoding"},
75 	{ "tty",
76 	{B_GET_PROPERTY, 0},
77 	{B_DIRECT_SPECIFIER, 0},
78 	"get tty name."},
79 	{ 0  }
80 };
81 
82 
83 const static uint32 kUpdateSigWinch = 'Rwin';
84 
85 const static rgb_color kBlackColor = { 0, 0, 0, 255 };
86 const static rgb_color kWhiteColor = { 255, 255, 255, 255 };
87 
88 
89 
90 TermView::TermView(BRect frame, int32 argc, const char **argv, int32 historySize)
91 	: BView(frame, "termview", B_FOLLOW_ALL,
92 		B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE| B_PULSE_NEEDED),
93 	fShell(NULL),
94 	fWinchRunner(NULL),
95 	fFontWidth(0),
96 	fFontHeight(0),
97 	fFontAscent(0),
98 	fUpdateFlag(false),
99 	fInsertModeFlag(MODE_OVER),
100 	fScrollUpCount(0),
101 	fScrollBarRange(0),
102 	fFrameResized(false),
103 	fLastCursorTime(0),
104 	fCursorDrawFlag(true),
105 	fCursorStatus(true),
106 	fCursorBlinkingFlag(true),
107 	fCursorRedrawFlag(true),
108 	fCursorHeight(0),
109 	fCurPos(0, 0),
110 	fCurStack(0, 0),
111 	fBufferStartPos(-1),
112 	fTermRows(ROWS_DEFAULT),
113 	fTermColumns(COLUMNS_DEFAULT),
114 	fEncoding(M_UTF8),
115 	fTop(0),
116 	fTextBuffer(NULL),
117 	fScrollBar(NULL),
118 	fTextForeColor(kBlackColor),
119 	fTextBackColor(kWhiteColor),
120 	fCursorForeColor(kWhiteColor),
121 	fCursorBackColor(kBlackColor),
122 	fSelectForeColor(kWhiteColor),
123 	fSelectBackColor(kBlackColor),
124 	fScrTop(0),
125 	fScrBot(fTermRows - 1),
126 	fScrBufSize(historySize),
127 	fScrRegionSet(0),
128 	fClickPoint(0, 0),
129 	fSelStart(-1, -1),
130 	fSelEnd(-1, -1),
131 	fMouseTracking(false),
132 	fIMflag(false)
133 {
134 	_InitObject(argc, argv);
135 }
136 
137 
138 TermView::TermView(int rows, int columns, int32 argc, const char **argv, int32 historySize)
139 	: BView(BRect(0, 0, 0, 0), "termview", B_FOLLOW_ALL,
140 		B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE| B_PULSE_NEEDED),
141 	fShell(NULL),
142 	fWinchRunner(NULL),
143 	fFontWidth(0),
144 	fFontHeight(0),
145 	fFontAscent(0),
146 	fUpdateFlag(false),
147 	fInsertModeFlag(MODE_OVER),
148 	fScrollUpCount(0),
149 	fScrollBarRange(0),
150 	fFrameResized(false),
151 	fLastCursorTime(0),
152 	fCursorDrawFlag(true),
153 	fCursorStatus(true),
154 	fCursorBlinkingFlag(true),
155 	fCursorRedrawFlag(true),
156 	fCursorHeight(0),
157 	fCurPos(0, 0),
158 	fCurStack(0, 0),
159 	fBufferStartPos(-1),
160 	fTermRows(rows),
161 	fTermColumns(columns),
162 	fEncoding(M_UTF8),
163 	fTop(0),
164 	fTextBuffer(NULL),
165 	fScrollBar(NULL),
166 	fTextForeColor(kBlackColor),
167 	fTextBackColor(kWhiteColor),
168 	fCursorForeColor(kWhiteColor),
169 	fCursorBackColor(kBlackColor),
170 	fSelectForeColor(kWhiteColor),
171 	fSelectBackColor(kBlackColor),
172 	fScrTop(0),
173 	fScrBot(fTermRows - 1),
174 	fScrBufSize(historySize),
175 	fScrRegionSet(0),
176 	fClickPoint(0, 0),
177 	fSelStart(-1, -1),
178 	fSelEnd(-1, -1),
179 	fMouseTracking(false),
180 	fIMflag(false)
181 {
182 	_InitObject(argc, argv);
183 	SetTermSize(fTermRows, fTermColumns, true);
184 }
185 
186 
187 TermView::TermView(BMessage *archive)
188 	:
189 	BView(archive),
190 	fShell(NULL),
191 	fWinchRunner(NULL),
192 	fFontWidth(0),
193 	fFontHeight(0),
194 	fFontAscent(0),
195 	fUpdateFlag(false),
196 	fInsertModeFlag(MODE_OVER),
197 	fScrollUpCount(0),
198 	fScrollBarRange(0),
199 	fFrameResized(false),
200 	fLastCursorTime(0),
201 	fCursorDrawFlag(true),
202 	fCursorStatus(true),
203 	fCursorBlinkingFlag(true),
204 	fCursorRedrawFlag(true),
205 	fCursorHeight(0),
206 	fCurPos(0, 0),
207 	fCurStack(0, 0),
208 	fBufferStartPos(-1),
209 	fTermRows(ROWS_DEFAULT),
210 	fTermColumns(COLUMNS_DEFAULT),
211 	fEncoding(M_UTF8),
212 	fTop(0),
213 	fTextBuffer(NULL),
214 	fScrollBar(NULL),
215 	fTextForeColor(kBlackColor),
216 	fTextBackColor(kWhiteColor),
217 	fCursorForeColor(kWhiteColor),
218 	fCursorBackColor(kBlackColor),
219 	fSelectForeColor(kWhiteColor),
220 	fSelectBackColor(kBlackColor),
221 	fScrTop(0),
222 	fScrBot(fTermRows - 1),
223 	fScrBufSize(1000),
224 	fScrRegionSet(0),
225 	fClickPoint(0, 0),
226 	fSelStart(-1, -1),
227 	fSelEnd(-1, -1),
228 	fMouseTracking(false),
229 	fIMflag(false)
230 {
231 	if (archive->FindInt32("encoding", (int32 *)&fEncoding) < B_OK)
232 		fEncoding = M_UTF8;
233 	if (archive->FindInt32("columns", (int32 *)&fTermColumns) < B_OK)
234 		fTermColumns = COLUMNS_DEFAULT;
235 	if (archive->FindInt32("rows", (int32 *)&fTermRows) < B_OK)
236 		fTermRows = ROWS_DEFAULT;
237 
238 	// TODO: Retrieve arguments, colors, history size, etc. from archive
239 	_InitObject(0, NULL);
240 }
241 
242 
243 status_t
244 TermView::_InitObject(int32 argc, const char **argv)
245 {
246 	fTextBuffer = new (std::nothrow) TermBuffer(fTermRows, fTermColumns, fScrBufSize);
247 	if (fTextBuffer == NULL)
248 		return B_NO_MEMORY;
249 
250 	fShell = new (std::nothrow) Shell();
251 	if (fShell == NULL)
252 		return B_NO_MEMORY;
253 
254 	SetTermFont(be_fixed_font);
255 	SetTermSize(fTermRows, fTermColumns, false);
256 	//SetIMAware(false);
257 
258 	status_t status = fShell->Open(fTermRows, fTermColumns,
259 								EncodingAsShortString(fEncoding),
260 								argc, argv);
261 
262 	if (status < B_OK)
263 		return status;
264 
265 	status = _AttachShell(fShell);
266 	if (status < B_OK)
267 		return status;
268 
269 	return B_OK;
270 }
271 
272 
273 TermView::~TermView()
274 {
275 	Shell *shell = fShell;
276 		// _DetachShell sets fShell to NULL
277 
278 	_DetachShell();
279 
280 	delete fTextBuffer;
281 	delete shell;
282 }
283 
284 
285 /* static */
286 BArchivable *
287 TermView::Instantiate(BMessage* data)
288 {
289 	if (validate_instantiation(data, "TermView"))
290 		return new (std::nothrow) TermView(data);
291 
292 	return NULL;
293 }
294 
295 
296 status_t
297 TermView::Archive(BMessage* data, bool deep) const
298 {
299 	status_t status = BView::Archive(data, deep);
300 	if (status == B_OK)
301 		status = data->AddString("add_on", TERM_SIGNATURE);
302 	if (status == B_OK)
303 		status = data->AddInt32("encoding", (int32)fEncoding);
304 	if (status == B_OK)
305 		status = data->AddInt32("columns", (int32)fTermColumns);
306 	if (status == B_OK)
307 		status = data->AddInt32("rows", (int32)fTermRows);
308 
309 	return status;
310 }
311 
312 
313 void
314 TermView::GetPreferredSize(float *width, float *height)
315 {
316 	if (width)
317 		*width = fTermColumns * fFontWidth - 1;
318 	if (height)
319 		*height = fTermRows * fFontHeight - 1;
320 }
321 
322 
323 const char *
324 TermView::TerminalName() const
325 {
326 	if (fShell == NULL)
327 		return NULL;
328 
329 	return fShell->TTYName();
330 }
331 
332 
333 //! Get width and height for terminal font
334 void
335 TermView::GetFontSize(int* _width, int* _height)
336 {
337 	*_width = fFontWidth;
338 	*_height = fFontHeight;
339 }
340 
341 
342 //! Set number of rows and columns in terminal
343 BRect
344 TermView::SetTermSize(int rows, int cols, bool resize)
345 {
346 	if (rows > 0)
347 		fTermRows = rows;
348 	if (cols > 0)
349 		fTermColumns = cols;
350 
351 	fTextBuffer->ResizeTo(fTermRows, fTermColumns, 0);
352 
353 	fScrTop = 0;
354 	fScrBot = fTermRows - 1;
355 
356 	BRect rect(0, 0, fTermColumns * fFontWidth - 1, fTermRows * fFontHeight - 1);
357 
358 	if (resize)
359 		ResizeTo(rect.Width(), rect.Height());
360 
361 	return rect;
362 }
363 
364 
365 void
366 TermView::SetTextColor(rgb_color fore, rgb_color back)
367 {
368 	fTextForeColor = fore;
369 	fTextBackColor = back;
370 
371 	SetLowColor(fTextBackColor);
372 	SetViewColor(fTextBackColor);
373 }
374 
375 
376 void
377 TermView::SetSelectColor(rgb_color fore, rgb_color back)
378 {
379 	fSelectForeColor = fore;
380 	fSelectBackColor = back;
381 }
382 
383 
384 void
385 TermView::SetCursorColor(rgb_color fore, rgb_color back)
386 {
387 	fCursorForeColor = fore;
388 	fCursorBackColor = back;
389 }
390 
391 
392 int
393 TermView::Encoding() const
394 {
395 	return fEncoding;
396 }
397 
398 
399 void
400 TermView::SetEncoding(int encoding)
401 {
402 	// TODO: Shell::_Spawn() sets the "TTYPE" environment variable using
403 	// the string value of encoding. But when this function is called and
404 	// the encoding changes, the new value is never passed to Shell.
405 	fEncoding = encoding;
406 }
407 
408 
409 //! Sets font for terminal
410 void
411 TermView::SetTermFont(const BFont *font)
412 {
413 	char buf[4];
414 	int halfWidth = 0;
415 
416 	fHalfFont = font;
417 
418 	_FixFontAttributes(fHalfFont);
419 
420 	// calculate half font's max width
421 	// Not Bounding, check only A-Z(For case of fHalfFont is KanjiFont. )
422 	for (int c = 0x20 ; c <= 0x7e; c++){
423 		sprintf(buf, "%c", c);
424 		int tmpWidth = (int)fHalfFont.StringWidth(buf);
425 		if (tmpWidth > halfWidth)
426 			halfWidth = tmpWidth;
427 	}
428 
429 	fFontWidth = halfWidth;
430 
431 	font_height hh;
432 	fHalfFont.GetHeight(&hh);
433 
434 	int font_ascent = (int)hh.ascent;
435 	int font_descent =(int)hh.descent;
436 	int font_leading =(int)hh.leading;
437 
438 	if (font_leading == 0)
439 		font_leading = 1;
440 
441 	if (fTop)
442 		fTop = fTop / fFontHeight;
443 
444 	fFontAscent = font_ascent;
445 	fFontHeight = font_ascent + font_descent + font_leading + 1;
446 
447 	fTop = fTop * fFontHeight;
448 
449 	fCursorHeight = fFontHeight;
450 }
451 
452 
453 void
454 TermView::SetScrollBar(BScrollBar *scrollBar)
455 {
456 	fScrollBar = scrollBar;
457 }
458 
459 
460 void
461 TermView::SetTitle(const char *title)
462 {
463 	// TODO: Do something different in case we're a replicant,
464 	// or in case we are inside a BTabView ?
465 	if (Window())
466 		Window()->SetTitle(title);
467 }
468 
469 
470 void
471 TermView::Copy(BClipboard *clipboard)
472 {
473 	if (!_HasSelection())
474 		return;
475 
476 	BString copyStr;
477 	fTextBuffer->GetStringFromRegion(copyStr, fSelStart, fSelEnd);
478 
479 	if (clipboard->Lock()) {
480 		BMessage *clipMsg = NULL;
481 		clipboard->Clear();
482 
483 		if ((clipMsg = clipboard->Data()) != NULL) {
484 			clipMsg->AddData("text/plain", B_MIME_TYPE, copyStr.String(),
485 				copyStr.Length());
486 			clipboard->Commit();
487 		}
488 		clipboard->Unlock();
489 	}
490 
491 	// Deselecting the current selection is not the behavior that
492 	// R5's Terminal app displays. We want to mimic the behavior, so we will
493 	// no longer do the deselection
494 //	if (!fMouseTracking)
495 //		_DeSelect();
496 }
497 
498 
499 void
500 TermView::Paste(BClipboard *clipboard)
501 {
502 	if (clipboard->Lock()) {
503 		BMessage *clipMsg = clipboard->Data();
504 		char *text;
505 		ssize_t numBytes;
506 		if (clipMsg->FindData("text/plain", B_MIME_TYPE,
507 				(const void **)&text, &numBytes) == B_OK ) {
508 			// Clipboard text doesn't attached EOF?
509 			text[numBytes] = '\0';
510 			_WritePTY((uchar *)text, numBytes);
511 		}
512 
513 		clipboard->Unlock();
514 	}
515 }
516 
517 
518 void
519 TermView::SelectAll()
520 {
521 	int screen_top = fTop / fFontHeight;
522 	int viewheight = fTermRows;
523 
524 	int start_pos = screen_top -(fScrBufSize - viewheight * 2);
525 
526 	CurPos start, end;
527 	start.x = 0;
528 	end.x = fTermColumns -1;
529 
530 	if (start_pos > 0)
531 		start.y = start_pos;
532 	else
533 		start.y = 0;
534 
535 	end.y = fCurPos.y  + screen_top;
536 
537 	_Select(start, end);
538 }
539 
540 
541 void
542 TermView::Clear()
543 {
544 	_DeSelect();
545 	fTextBuffer->Clear();
546 
547 	fTop = 0;
548 	ScrollTo(0, 0);
549 
550 	if (LockLooper()) {
551 		SetHighColor(fTextBackColor);
552 		FillRect(Bounds());
553 		SetHighColor(fTextForeColor);
554 		UnlockLooper();
555 	}
556 
557 	// reset cursor pos
558 	SetCurPos(0, 0);
559 
560 	if (fScrollBar) {
561 		fScrollBar->SetRange(0, 0);
562 		fScrollBar->SetProportion(1);
563 	}
564 }
565 
566 
567 //! Print one character
568 void
569 TermView::Insert(uchar *string, ushort attr)
570 {
571 	int width = CodeConv::UTF8GetFontWidth((char*)string);
572 	if (width == FULL_WIDTH)
573 		attr |= A_WIDTH;
574 
575 	// check column over flow.
576 	if (fCurPos.x + width > fTermColumns) {
577 		UpdateLine();
578 		fCurPos.x = 0;
579 
580 		if (fCurPos.y == fTermRows -1)
581 			ScrollScreen();
582 		else
583 			fCurPos.y++;
584 	}
585 
586 	if (fInsertModeFlag == MODE_INSERT)
587 		fTextBuffer->InsertSpace(fCurPos, width);
588 
589 	fTextBuffer->WriteChar(fCurPos, string, attr);
590 
591 	if (!fUpdateFlag)
592 		fBufferStartPos = fCurPos.x;
593 
594 	fCurPos.x += width;
595 	fUpdateFlag = true;
596 }
597 
598 
599 //! Print a CR and move the cursor
600 void
601 TermView::InsertCR()
602 {
603 	UpdateLine();
604 	fTextBuffer->WriteCR(fCurPos);
605 	fCurPos.x = 0;
606 }
607 
608 
609 //! Print a LF and move the cursor
610 void
611 TermView::InsertLF()
612 {
613 	UpdateLine();
614 
615 	if (fScrRegionSet) {
616 		if (fCurPos.y == fScrBot) {
617 			ScrollRegion(-1, -1, SCRUP, 1);
618 			return;
619 		}
620 	}
621 
622 	if (fCurPos.x != fTermColumns){
623 		if (fCurPos.y == fTermRows -1)
624 			ScrollScreen();
625 		else
626 			fCurPos.y++;
627 	}
628 }
629 
630 
631 //! Print a NL and move the cursor
632 void
633 TermView::InsertNewLine(int num)
634 {
635 	ScrollRegion(fCurPos.y, -1, SCRDOWN, num);
636 }
637 
638 
639 //! Print a space
640 void
641 TermView::InsertSpace(int num)
642 {
643 	UpdateLine();
644 
645 	fTextBuffer->InsertSpace(fCurPos, num);
646 	_TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y));
647 }
648 
649 
650 //! Set or reset Insert mode
651 void
652 TermView::SetInsertMode(int flag)
653 {
654 	UpdateLine();
655 	fInsertModeFlag = flag;
656 }
657 
658 
659 //! Draw region
660 inline int
661 TermView::_TermDraw(const CurPos &start, const CurPos &end)
662 {
663 	int x1 = start.x;
664 	int y1 = start.y;
665 	int x2 = end.x;
666 	int y2 = end.y;
667 
668 	_Redraw(x1, y1 + fTop / fFontHeight,
669 		x2, y2 + fTop / fFontHeight);
670 
671 	return 0;
672 }
673 
674 
675 //! Draw region
676 int
677 TermView::_TermDrawSelectedRegion(CurPos start, CurPos end)
678 {
679 	CurPos inPos;
680 
681 	if (end < start) {
682 		inPos = start;
683 		start = end;
684 		end = inPos;
685 	}
686 
687 	if (start.y == end.y) {
688 		_Redraw(start.x, start.y, end.x, end.y);
689 	} else {
690 		_Redraw(start.x, start.y, fTermColumns, start.y);
691 
692 		if (end.y - start.y > 0)
693 			_Redraw(0, start.y + 1, fTermColumns, end.y - 1);
694 
695 		_Redraw(0, end.y, end.x, end.y);
696 	}
697 
698 	return 0;
699 }
700 
701 
702 //! Draw region
703 int
704 TermView::_TermDrawRegion(CurPos start, CurPos end)
705 {
706 	CurPos inPos;
707 	int top = fTop / fFontHeight;
708 
709 	if (end < start) {
710 		inPos = start;
711 		start = end;
712 		end = inPos;
713 	}
714 
715 	start.y += top;
716 	end.y += top;
717 
718 	if (start.y == end.y) {
719 		_Redraw(start.x, start.y, end.x, end.y);
720 	} else {
721 		_Redraw(start.x, start.y, fTermColumns - 1, start.y);
722 
723 		if (end.y - start.y > 0) {
724 			_Redraw(0, start.y + 1, fTermColumns - 1, end.y - 1);
725 		}
726 		_Redraw(0, end.y, end.x, end.y);
727 	}
728 
729 	return 0;
730 }
731 
732 
733 //! Erase below cursor below.
734 void
735 TermView::EraseBelow()
736 {
737 	UpdateLine();
738 
739 	fTextBuffer->EraseBelow(fCurPos);
740 	_TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y));
741 	if (fCurPos.y != fTermRows - 1)
742 		_TermDraw(CurPos(0, fCurPos.y + 1), CurPos(fTermColumns - 1, fTermRows - 1));
743 }
744 
745 
746 //! Delete num characters from current position.
747 void
748 TermView::DeleteChar(int num)
749 {
750 	UpdateLine();
751 
752 	fTextBuffer->DeleteChar(fCurPos, num);
753 	_TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y));
754 }
755 
756 
757 //! Delete cursor right characters.
758 void
759 TermView::DeleteColumns()
760 {
761 	UpdateLine();
762 
763 	fTextBuffer->DeleteChar(fCurPos, fTermColumns - fCurPos.x);
764 	_TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y));
765 }
766 
767 
768 //! Delete 'num' lines from current position with scrolling.
769 void
770 TermView::DeleteLine(int num)
771 {
772 	ScrollRegion(fCurPos.y, -1, SCRUP, num);
773 }
774 
775 
776 //! Sets cursor position
777 void
778 TermView::SetCurPos(int x, int y)
779 {
780 	UpdateLine();
781 
782 	if (x >= 0 && x < fTermColumns)
783 		fCurPos.x = x;
784 	if (y >= 0 && y < fTermRows)
785 		fCurPos.y = y;
786 }
787 
788 
789 //! Sets cursor x position
790 void
791 TermView::SetCurX(int x)
792 {
793 	if (x >= 0 && x < fTermRows) {
794 		UpdateLine();
795 		fCurPos.x = x;
796 	}
797 }
798 
799 
800 //! Sets cursor y position
801 void
802 TermView::SetCurY(int y)
803 {
804 	if (y >= 0 && y < fTermColumns) {
805 		UpdateLine();
806 		fCurPos.y = y;
807 	}
808 }
809 
810 
811 //! Gets cursor position
812 void
813 TermView::GetCurPos(CurPos *inCurPos)
814 {
815 	inCurPos->x = fCurPos.x;
816 	inCurPos->y = fCurPos.y;
817 }
818 
819 
820 //! Gets cursor x position
821 int
822 TermView::GetCurX()
823 {
824 	return fCurPos.x;
825 }
826 
827 
828 //! Gets cursor y position
829 int
830 TermView::GetCurY()
831 {
832 	return fCurPos.y;
833 }
834 
835 
836 //! Saves cursor position
837 void
838 TermView::SaveCursor()
839 {
840 	fCurStack = fCurPos;
841 }
842 
843 
844 //! Restores cursor position
845 void
846 TermView::RestoreCursor()
847 {
848 	UpdateLine();
849 	fCurPos = fCurStack;
850 }
851 
852 
853 //! Move cursor right by 'num' steps.
854 void
855 TermView::MoveCurRight(int num)
856 {
857 	UpdateLine();
858 
859 	if (fCurPos.x + num >= fTermColumns) {
860 		// Wrap around
861 		fCurPos.x = 0;
862 		InsertCR();
863 		InsertLF();
864 	} else
865 		fCurPos.x += num;
866 }
867 
868 
869 //! Move cursor left by 'num' steps.
870 void
871 TermView::MoveCurLeft(int num)
872 {
873 	UpdateLine();
874 
875 	fCurPos.x -= num;
876 	if (fCurPos.x < 0)
877 		fCurPos.x = 0;
878 }
879 
880 
881 //! Move cursor up by 'num' steps.
882 void
883 TermView::MoveCurUp(int num)
884 {
885 	UpdateLine();
886 
887 	fCurPos.y -= num;
888 
889 	if (fCurPos.y < 0)
890 		fCurPos.y = 0;
891 }
892 
893 
894 //! Move cursor down by 'num' steps.
895 void
896 TermView::MoveCurDown(int num)
897 {
898 	UpdateLine();
899 
900 	fCurPos.y += num;
901 
902 	if (fCurPos.y >= fTermRows)
903 		fCurPos.y = fTermRows - 1;
904 }
905 
906 
907 // TODO: Cleanup the next 3 functions!!!
908 void
909 TermView::DrawCursor()
910 {
911 	BRect rect(fFontWidth * fCurPos.x, fFontHeight * fCurPos.y + fTop,
912 		fFontWidth * (fCurPos.x + 1) - 1, fFontHeight * fCurPos.y + fTop + fCursorHeight - 1);
913 
914 	uchar buf[4];
915 	ushort attr;
916 
917 	int top = fTop / fFontHeight;
918 	bool m_flag = _CheckSelectedRegion(CurPos(fCurPos.x, fCurPos.y + fTop / fFontHeight));
919 	if (fTextBuffer->GetChar(fCurPos.y + top, fCurPos.x, buf, &attr) == A_CHAR) {
920 		int width;
921 		if (IS_WIDTH(attr))
922 			width = 2;
923 		else
924 			width = 1;
925 
926 		_DrawLines(fCurPos.x * fFontWidth,
927 			fCurPos.y * fFontHeight + fTop,
928 			attr, buf, width, m_flag, true, this);
929 	} else {
930 		if (m_flag)
931 			SetHighColor(fSelectBackColor);
932 		else
933 			SetHighColor(fCursorBackColor);
934 
935 		FillRect(rect);
936 	}
937 
938 	Sync();
939 }
940 
941 
942 void
943 TermView::BlinkCursor()
944 {
945 	if (fCursorDrawFlag
946 		&& fCursorBlinkingFlag
947 		&& Window()->IsActive()) {
948 		if (fCursorStatus)
949 			_TermDraw(fCurPos, fCurPos);
950 		else
951 			DrawCursor();
952 
953 		fCursorStatus = !fCursorStatus;
954 		fLastCursorTime = system_time();
955 	}
956 }
957 
958 
959 //! Draw / Clear cursor.
960 void
961 TermView::SetCurDraw(bool flag)
962 {
963 	if (!flag) {
964 		if (fCursorStatus)
965 			_TermDraw(fCurPos, fCurPos);
966 
967 		fCursorStatus = false;
968 		fCursorDrawFlag = false;
969 	} else {
970 		if (!fCursorDrawFlag) {
971 			fCursorDrawFlag = true;
972 			fCursorStatus = true;
973 
974 			if (LockLooper()) {
975 				DrawCursor();
976 				UnlockLooper();
977 			}
978 		}
979 	}
980 }
981 
982 
983 //! Sets cursor Blinking flag.
984 void
985 TermView::SetCurBlinking(bool flag)
986 {
987 	fCursorBlinkingFlag = flag;
988 }
989 
990 
991 //! Scroll terminal dir directory by 'num' steps.
992 void
993 TermView::ScrollRegion(int top, int bot, int dir, int num)
994 {
995 	UpdateLine();
996 
997 	if (top == -1)
998 		top = fScrTop;
999 
1000 	if (bot == -1)
1001 		bot = fScrBot;
1002 
1003 	if (top < fScrTop)
1004 		top = fScrTop;
1005 
1006 	if (bot > fScrBot)
1007 		bot = fScrBot;
1008 
1009 	fTextBuffer->ScrollRegion(top, bot , dir ,num);
1010 	_TermDraw(CurPos(0, top), CurPos(fTermColumns - 1, bot));
1011 }
1012 
1013 
1014 //! Sets terminal scroll region.
1015 void
1016 TermView::SetScrollRegion(int top, int bot)
1017 {
1018 	if (top >= 0 && top < fTermRows) {
1019 		if (bot >= 0 && bot < fTermRows) {
1020 			if (top > bot) {
1021 				fScrTop = bot;
1022 				fScrBot = top;
1023 			} else if (top < bot ) {
1024 				fScrTop = top;
1025 				fScrBot = bot;
1026 			}
1027 		}
1028 	}
1029 
1030 	if (fScrTop != 0 || fScrBot != fTermRows -1 )
1031 		fScrRegionSet = 1;
1032 	else
1033 		fScrRegionSet = 0;
1034 }
1035 
1036 
1037 //! Scroll to cursor position.
1038 void
1039 TermView::ScrollAtCursor()
1040 {
1041 	if (LockLooper()) {
1042 		_ResizeScrBarRange();
1043 		fScrollUpCount = 0;
1044 		ScrollTo(0, fTop);
1045 		UnlockLooper();
1046 	}
1047 }
1048 
1049 
1050 status_t
1051 TermView::_AttachShell(Shell *shell)
1052 {
1053 	if (shell == NULL)
1054 		return B_BAD_VALUE;
1055 
1056 	fShell = shell;
1057 	fShell->ViewAttached(this);
1058 
1059 	return B_OK;
1060 }
1061 
1062 
1063 void
1064 TermView::_DetachShell()
1065 {
1066 	fShell->ViewDetached();
1067 	fShell = NULL;
1068 }
1069 
1070 
1071 //! Draw character on offscreen bitmap.
1072 void
1073 TermView::_DrawLines(int x1, int y1, ushort attr, uchar *buf,
1074 	int width, int mouse, int cursor, BView *inView)
1075 {
1076 	int x2, y2;
1077 	int forecolor, backcolor;
1078 	rgb_color rgb_fore = fTextForeColor, rgb_back = fTextBackColor, rgb_tmp;
1079 
1080 	inView->SetFont(&fHalfFont);
1081 
1082 	// Set pen point
1083 	x2 = x1 + fFontWidth * width;
1084 	y2 = y1 + fFontHeight;
1085 
1086 	// color attribute
1087 	forecolor = IS_FORECOLOR(attr);
1088 	backcolor = IS_BACKCOLOR(attr);
1089 
1090 	if (IS_FORESET(attr))
1091 		rgb_fore = kTermColorTable[forecolor];
1092 
1093 	if (IS_BACKSET(attr))
1094 		rgb_back = kTermColorTable[backcolor];
1095 
1096 	// Selection check.
1097 	if (cursor) {
1098 		rgb_fore = fCursorForeColor;
1099 		rgb_back = fCursorBackColor;
1100 	} else if (mouse){
1101 		rgb_fore = fSelectForeColor;
1102 		rgb_back = fSelectBackColor;
1103 	} else {
1104 		// Reverse attribute(If selected area, don't reverse color).
1105 		if (IS_INVERSE(attr)) {
1106 			rgb_tmp = rgb_fore;
1107 			rgb_fore = rgb_back;
1108 			rgb_back = rgb_tmp;
1109 		}
1110 	}
1111 
1112 	// Fill color at Background color and set low color.
1113 	inView->SetHighColor(rgb_back);
1114 	inView->FillRect(BRect(x1, y1, x2 - 1, y2 - 1));
1115 	inView->SetLowColor(rgb_back);
1116 
1117 	inView->SetHighColor(rgb_fore);
1118 
1119 	// Draw character.
1120 	inView->MovePenTo(x1, y1 + fFontAscent);
1121 	inView->DrawString((char *) buf);
1122 
1123 	// bold attribute.
1124 	if (IS_BOLD(attr)) {
1125 		inView->MovePenTo(x1 + 1, y1 + fFontAscent);
1126 
1127 		inView->SetDrawingMode(B_OP_OVER);
1128 		inView->DrawString((char *)buf);
1129 		inView->SetDrawingMode(B_OP_COPY);
1130 	}
1131 
1132 	// underline attribute
1133 	if (IS_UNDER(attr)) {
1134 		inView->MovePenTo(x1, y1 + fFontAscent);
1135 		inView->StrokeLine(BPoint(x1 , y1 + fFontAscent),
1136 			BPoint(x2 , y1 + fFontAscent));
1137 	}
1138 }
1139 
1140 
1141 //! Resize scroll bar range and knob size.
1142 void
1143 TermView::_ResizeScrBarRange()
1144 {
1145 	if (fScrollBar == NULL)
1146 		return;
1147 
1148 	float viewheight = fTermRows * fFontHeight;
1149 	float start_pos = fTop -(fScrBufSize - fTermRows *2) * fFontHeight;
1150 
1151 	if (start_pos > 0) {
1152 		fScrollBar->SetRange(start_pos, viewheight + fTop - fFontHeight);
1153 	} else {
1154 		fScrollBar->SetRange(0, viewheight + fTop - fFontHeight);
1155 		fScrollBar->SetProportion( viewheight /(viewheight + fTop));
1156 	}
1157 }
1158 
1159 
1160 //! Scrolls screen.
1161 void
1162 TermView::ScrollScreen()
1163 {
1164 	fTop += fFontHeight;
1165 	fScrollUpCount++;
1166 	fTextBuffer->ScrollLine();
1167 
1168 	if (fScrollUpCount > fTermRows ) {
1169 		if (LockLooper()) {
1170 			_ResizeScrBarRange();
1171 			fScrollBarRange += fScrollUpCount;
1172 			fScrollUpCount = 0;
1173 			ScrollTo(0, fTop);
1174 			UnlockLooper();
1175 		}
1176 	}
1177 }
1178 
1179 
1180 //! Scrolls screen.
1181 void
1182 TermView::ScrollScreenDraw()
1183 {
1184 	if (fScrollUpCount){
1185 		if (LockLooper()) {
1186 			_ResizeScrBarRange();
1187 
1188 			fScrollBarRange += fScrollUpCount;
1189 			fScrollUpCount = 0;
1190 			ScrollTo(0, fTop);
1191 			UnlockLooper();
1192 		}
1193 	}
1194 }
1195 
1196 
1197 //!	Handler for SIGWINCH
1198 void
1199 TermView::_UpdateSIGWINCH()
1200 {
1201 	if (fFrameResized) {
1202 		if (_HasSelection())
1203 			_TermDrawSelectedRegion(fSelStart, fSelEnd);
1204 		ScrollTo(0, fTop);
1205 		_ResizeScrBarRange();
1206 
1207 		fShell->UpdateWindowSize(fTermRows, fTermColumns);
1208 
1209 		fFrameResized = false;
1210 		if (fScrRegionSet == 0)
1211 			fScrBot = fTermRows - 1;
1212 	}
1213 }
1214 
1215 
1216 //!	Device Status.
1217 void
1218 TermView::DeviceStatusReport(int n)
1219 {
1220 	char sbuf[16] ;
1221 	int len;
1222 
1223 	switch (n) {
1224 		case 5:
1225 			len = sprintf(sbuf,"\033[0n") ;
1226 			fShell->Write(sbuf, len);
1227 			break ;
1228 		case 6:
1229 			len = sprintf(sbuf,"\033[%d;%dR", fTermRows, fTermColumns) ;
1230 			fShell->Write(sbuf, len);
1231 			break ;
1232 		default:
1233 			return;
1234 	}
1235 }
1236 
1237 
1238 //!	Update line buffer.
1239 void
1240 TermView::UpdateLine()
1241 {
1242 	if (fUpdateFlag == true) {
1243 		if (fInsertModeFlag == MODE_INSERT) {
1244 			_TermDraw(CurPos(fBufferStartPos, fCurPos.y),
1245 				CurPos(fTermColumns - 1, fCurPos.y));
1246 		} else {
1247 			_TermDraw(CurPos(fBufferStartPos, fCurPos.y),
1248 				CurPos(fCurPos.x - 1, fCurPos.y));
1249 		}
1250 		fUpdateFlag = false;
1251 	}
1252 }
1253 
1254 
1255 void
1256 TermView::AttachedToWindow()
1257 {
1258 	SetFont(&fHalfFont);
1259 	MakeFocus(true);
1260 	if (fScrollBar)
1261 		fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows);
1262 
1263 	BMessage message(kUpdateSigWinch);
1264 	fWinchRunner = new (std::nothrow) BMessageRunner(BMessenger(this), &message, 500000);
1265 
1266 	Window()->SetPulseRate(1000000);
1267 }
1268 
1269 
1270 void
1271 TermView::DetachedFromWindow()
1272 {
1273 	delete fWinchRunner;
1274 	fWinchRunner = NULL;
1275 }
1276 
1277 
1278 void
1279 TermView::Pulse()
1280 {
1281 	//if (system_time() > fLastCursorTime + 1000000)
1282 		BlinkCursor();
1283 }
1284 
1285 
1286 void
1287 TermView::Draw(BRect updateRect)
1288 {
1289 	if (IsPrinting()) {
1290 		_DoPrint(updateRect);
1291 		return;
1292 	}
1293 
1294 	int x1 =(int)updateRect.left / fFontWidth;
1295 	int x2 =(int)updateRect.right / fFontWidth;
1296 
1297 	int y1 =(int)updateRect.top / fFontHeight;
1298 	int y2 =(int)updateRect.bottom / fFontHeight;
1299 
1300 	Window()->BeginViewTransaction();
1301 
1302 	for (int j = y1; j <= y2; j++) {
1303 		// If(x1, y1) Buffer is in string full width character,
1304 		// alignment start position.
1305 
1306 		int k = x1;
1307 		uchar buf[256];
1308 
1309 		ushort attr;
1310 		if (fTextBuffer->GetChar(j, k, buf, &attr) == IN_STRING)
1311 			k--;
1312 
1313 		if (k < 0)
1314 			k = 0;
1315 
1316 		for (int i = k; i <= x2;) {
1317 			int count = fTextBuffer->GetString(j, i, x2, buf, &attr);
1318 			bool insideSelection = _CheckSelectedRegion(CurPos(i, j));
1319 
1320 			if (count < 0) {
1321 				if (insideSelection) {
1322 					BRect eraseRect;
1323 					eraseRect.Set(fFontWidth * i,
1324 						fFontHeight * j,
1325 						fFontWidth * (i - count) -1,
1326 						fFontHeight * (j + 1) -1);
1327 
1328 					SetHighColor(fSelectBackColor);
1329 					FillRect(eraseRect);
1330 				}
1331 				i += abs(count);
1332 				continue;
1333 			}
1334 
1335 			_DrawLines(fFontWidth * i, fFontHeight * j,
1336 				attr, buf, count, insideSelection, false, this);
1337 			i += count;
1338 			if (i >= fTermColumns)
1339 				break;
1340 		}
1341 	}
1342 
1343 	if (fCursorStatus)
1344 		DrawCursor();
1345 
1346 	Window()->EndViewTransaction();
1347 }
1348 
1349 
1350 void
1351 TermView::_DoPrint(BRect updateRect)
1352 {
1353 	ushort attr;
1354 	uchar buf[256];
1355 
1356 	const int numLines =(int)((updateRect.Height()) / fFontHeight);
1357 
1358 	int y1 =(int)updateRect.top / fFontHeight;
1359 	y1 = y1 -(fScrBufSize - numLines * 2);
1360 	if (y1 < 0)
1361 		y1 = 0;
1362 
1363 	const int y2 = y1 + numLines -1;
1364 
1365 	const int x1 =(int)updateRect.left / fFontWidth;
1366 	const int x2 =(int)updateRect.right / fFontWidth;
1367 
1368 	for (int j = y1; j <= y2; j++) {
1369 		// If(x1, y1) Buffer is in string full width character,
1370 		// alignment start position.
1371 
1372 		int k = x1;
1373 		if (fTextBuffer->GetChar(j, k, buf, &attr) == IN_STRING)
1374 			k--;
1375 
1376 		if (k < 0)
1377 			k = 0;
1378 
1379 		for (int i = k; i <= x2;) {
1380 			int count = fTextBuffer->GetString(j, i, x2, buf, &attr);
1381 			if (count < 0) {
1382 				i += abs(count);
1383 				continue;
1384 			}
1385 
1386 			_DrawLines(fFontWidth * i, fFontHeight * j,
1387 				attr, buf, count, false, false, this);
1388 			i += count;
1389 		}
1390 	}
1391 }
1392 
1393 
1394 void
1395 TermView::WindowActivated(bool active)
1396 {
1397 	BView::WindowActivated(active);
1398 	if (active == false) {
1399 		// DoIMConfirm();
1400 	}
1401 }
1402 
1403 
1404 void
1405 TermView::KeyDown(const char *bytes, int32 numBytes)
1406 {
1407 	if (fIMflag)
1408 		return;
1409 
1410 	int32 key, mod, rawChar;
1411 	BMessage *currentMessage = Looper()->CurrentMessage();
1412 	if (currentMessage == NULL)
1413 		return;
1414 
1415 	currentMessage->FindInt32("modifiers", &mod);
1416 	currentMessage->FindInt32("key", &key);
1417 	currentMessage->FindInt32("raw_char", &rawChar);
1418 
1419 	// If bytes[0] equal intr character,
1420 	// send signal to shell process group.
1421 	struct termios tio;
1422 	fShell->GetAttr(tio);
1423 	if (*bytes == tio.c_cc[VINTR]) {
1424 		if (tio.c_lflag & ISIG)
1425 			fShell->Signal(SIGINT);
1426 	}
1427 
1428 	// Terminal filters RET, ENTER, F1...F12, and ARROW key code.
1429 	// TODO: Cleanup
1430 	if (numBytes == 1) {
1431 		switch (*bytes) {
1432 			case B_RETURN:
1433 				if (rawChar == B_RETURN) {
1434 					char c = 0x0d;
1435 					fShell->Write(&c, 1);
1436 					return;
1437 				}
1438 				break;
1439 
1440 			case B_LEFT_ARROW:
1441 				if (rawChar == B_LEFT_ARROW) {
1442 					fShell->Write(LEFT_ARROW_KEY_CODE, sizeof(LEFT_ARROW_KEY_CODE) - 1);
1443 					return;
1444 				}
1445 				break;
1446 
1447 			case B_RIGHT_ARROW:
1448 				if (rawChar == B_RIGHT_ARROW) {
1449 					fShell->Write(RIGHT_ARROW_KEY_CODE, sizeof(RIGHT_ARROW_KEY_CODE) - 1);
1450 					return;
1451 				}
1452 				break;
1453 
1454 			case B_UP_ARROW:
1455 				if (mod & B_SHIFT_KEY) {
1456 					if (Bounds().top > 0) {
1457 						ScrollBy(0, -fFontHeight);
1458 						Window()->UpdateIfNeeded();
1459 					}
1460 					return;
1461 				}
1462 				if (rawChar == B_UP_ARROW) {
1463 					fShell->Write(UP_ARROW_KEY_CODE, sizeof(UP_ARROW_KEY_CODE) - 1);
1464 					return;
1465 				}
1466 				break;
1467 
1468 			case B_DOWN_ARROW:
1469 				if (mod & B_SHIFT_KEY) {
1470 					ScrollBy(0, fFontHeight);
1471 					Window()->UpdateIfNeeded();
1472 					return;
1473 				}
1474 
1475 				if (rawChar == B_DOWN_ARROW) {
1476 					fShell->Write(DOWN_ARROW_KEY_CODE, sizeof(DOWN_ARROW_KEY_CODE) - 1);
1477 					return;
1478 				}
1479 				break;
1480 
1481 			case B_INSERT:
1482 				if (rawChar == B_INSERT) {
1483 					fShell->Write(INSERT_KEY_CODE, sizeof(INSERT_KEY_CODE) - 1);
1484 					return;
1485 				}
1486 				break;
1487 
1488 			case B_HOME:
1489 				if (rawChar == B_HOME) {
1490 					fShell->Write(HOME_KEY_CODE, sizeof(HOME_KEY_CODE) - 1);
1491 					return;
1492 				}
1493 				break;
1494 
1495 			case B_END:
1496 				if (rawChar == B_END) {
1497 					fShell->Write(END_KEY_CODE, sizeof(END_KEY_CODE) - 1);
1498 					return;
1499 				}
1500 				break;
1501 
1502 			case B_PAGE_UP:
1503 				if (mod & B_SHIFT_KEY) {
1504 					if (Bounds().top > 0) {
1505 						ScrollBy(0, -fFontHeight * fTermRows );
1506 						Window()->UpdateIfNeeded();
1507 					}
1508 					return;
1509 				}
1510 				if (rawChar == B_PAGE_UP) {
1511 					fShell->Write(PAGE_UP_KEY_CODE, sizeof(PAGE_UP_KEY_CODE) - 1);
1512 					return;
1513 				}
1514 				break;
1515 
1516 			case B_PAGE_DOWN:
1517 				if (mod & B_SHIFT_KEY) {
1518 					ScrollBy(0, fFontHeight * fTermRows);
1519 					Window()->UpdateIfNeeded();
1520 					return;
1521 				}
1522 
1523 				if (rawChar == B_PAGE_DOWN) {
1524 					fShell->Write(PAGE_DOWN_KEY_CODE, sizeof(PAGE_DOWN_KEY_CODE) - 1);
1525 					return;
1526 				}
1527 				break;
1528 
1529 			case B_FUNCTION_KEY:
1530 				// TODO: Why not just fShell->Write(key) ?
1531 				for (int32 i = 0; i < 12; i++) {
1532 					if (key == function_keycode_table[i]) {
1533 						fShell->Write(function_key_char_table[i], 5);
1534 						return;
1535 					}
1536 				}
1537 				break;
1538 			default:
1539 				break;
1540 		}
1541 	} else {
1542 		// input multibyte character
1543 		if (fEncoding != M_UTF8) {
1544 			char destBuffer[16];
1545 			int cnum = CodeConv::ConvertFromInternal(bytes, numBytes,
1546 				(char *)destBuffer, fEncoding);
1547 			fShell->Write(destBuffer, cnum);
1548 			return;
1549 		}
1550 	}
1551 
1552 	fShell->Write(bytes, numBytes);
1553 }
1554 
1555 
1556 void
1557 TermView::FrameResized(float width, float height)
1558 {
1559 	const int cols = ((int)width + 1) / fFontWidth;
1560 	const int rows = ((int)height + 1) / fFontHeight;
1561 
1562 	int offset = 0;
1563 
1564 	if (rows < fCurPos.y + 1) {
1565 		fTop += (fCurPos.y  + 1 - rows) * fFontHeight;
1566 		offset = fCurPos.y + 1 - rows;
1567 		fCurPos.y = rows - 1;
1568 	}
1569 	fTextBuffer->ResizeTo(rows, cols, offset);
1570 	fTermRows = rows;
1571 	fTermColumns = cols;
1572 
1573 	fFrameResized = true;
1574 }
1575 
1576 
1577 void
1578 TermView::MessageReceived(BMessage *msg)
1579 {
1580 	entry_ref ref;
1581 	char *ctrl_l = "";
1582 
1583 	switch (msg->what){
1584 		case B_ABOUT_REQUESTED:
1585 			// (replicant) about box requested
1586 			_AboutRequested();
1587 			break;
1588 
1589 		case B_SIMPLE_DATA:
1590 		{
1591 			int32 i = 0;
1592 			if (msg->FindRef("refs", i++, &ref) == B_OK) {
1593 				_DoFileDrop(ref);
1594 
1595 				while (msg->FindRef("refs", i++, &ref) == B_OK) {
1596 					_WritePTY((const uchar*)" ", 1);
1597 					_DoFileDrop(ref);
1598 				}
1599 			} else
1600 				BView::MessageReceived(msg);
1601 			break;
1602 		}
1603 
1604 		case B_MIME_DATA:
1605 		{
1606 			char *text;
1607 			int32 numBytes;
1608 			status_t sts;
1609 
1610 			if (msg->WasDropped()) {
1611 				sts = msg->FindData("text/plain",
1612 					B_MIME_TYPE, (const void **)&text, &numBytes);
1613 				if (sts != B_OK)
1614 					break;
1615 
1616 				_WritePTY((uchar *)text, numBytes);
1617 			}
1618 			break;
1619 		}
1620 
1621 		case B_COPY:
1622 			Copy(be_clipboard);
1623 			break;
1624 
1625 		case B_PASTE:
1626 		{
1627 			int32 code;
1628 			if (msg->FindInt32("index", &code) == B_OK)
1629 				Paste(be_clipboard);
1630 			break;
1631 		}
1632 
1633 		case B_SELECT_ALL:
1634 			SelectAll();
1635 			break;
1636 
1637 		case B_SET_PROPERTY:
1638 		{
1639 			int32 i;
1640 			int32 encodingID;
1641 			BMessage specifier;
1642 			msg->GetCurrentSpecifier(&i, &specifier);
1643 			if (!strcmp("encoding", specifier.FindString("property", i))){
1644 				msg->FindInt32 ("data", &encodingID);
1645 				SetEncoding(encodingID);
1646 				msg->SendReply(B_REPLY);
1647 			} else {
1648 				BView::MessageReceived(msg);
1649 			}
1650 			break;
1651 		}
1652 
1653 		case B_GET_PROPERTY:
1654 		{
1655 			int32 i;
1656 			BMessage specifier;
1657 			msg->GetCurrentSpecifier(&i, &specifier);
1658 			if (!strcmp("encoding", specifier.FindString("property", i))){
1659 				BMessage reply(B_REPLY);
1660 				reply.AddInt32("result", Encoding());
1661 				msg->SendReply(&reply);
1662 			} else if (!strcmp("tty", specifier.FindString("property", i))) {
1663 				BMessage reply(B_REPLY);
1664 				reply.AddString("result", TerminalName());
1665 				msg->SendReply(&reply);
1666 			} else {
1667 				BView::MessageReceived(msg);
1668 			}
1669 			break;
1670 		}
1671 
1672 		case MENU_CLEAR_ALL:
1673 			Clear();
1674 			fShell->Write(ctrl_l, 1);
1675 			break;
1676 
1677 
1678 //  case B_INPUT_METHOD_EVENT:
1679 //    {
1680    //   int32 op;
1681   //    msg->FindInt32("be:opcode", &op);
1682    //   switch (op){
1683    //   case B_INPUT_METHOD_STARTED:
1684 	//DoIMStart(msg);
1685 //	break;
1686 
1687 //      case B_INPUT_METHOD_STOPPED:
1688 //	DoIMStop(msg);
1689 //	break;
1690 
1691 //      case B_INPUT_METHOD_CHANGED:
1692 //	DoIMChange(msg);
1693 //	break;
1694 
1695 //      case B_INPUT_METHOD_LOCATION_REQUEST:
1696 //	DoIMLocation(msg);
1697 //	break;
1698     //  }
1699    // }
1700 		case kUpdateSigWinch:
1701 			_UpdateSIGWINCH();
1702 			break;
1703 		default:
1704 			BView::MessageReceived(msg);
1705 			break;
1706 	}
1707 }
1708 
1709 
1710 status_t
1711 TermView::GetSupportedSuites(BMessage *message)
1712 {
1713 	BPropertyInfo propInfo(sPropList);
1714 	message->AddString("suites", "suite/vnd.naan-termview");
1715 	message->AddFlat("messages", &propInfo);
1716 	return BView::GetSupportedSuites(message);
1717 }
1718 
1719 
1720 BHandler*
1721 TermView::ResolveSpecifier(BMessage *message, int32 index, BMessage *specifier,
1722 				int32 what, const char *property)
1723 {
1724 	BHandler *target = this;
1725 	BPropertyInfo propInfo(sPropList);
1726 	if (propInfo.FindMatch(message, index, specifier, what, property) < B_OK)
1727 		target = BView::ResolveSpecifier(message, index, specifier, what, property);
1728 
1729 	return target;
1730 }
1731 
1732 
1733 //! Gets dropped file full path and display it at cursor position.
1734 void
1735 TermView::_DoFileDrop(entry_ref &ref)
1736 {
1737 	BEntry ent(&ref);
1738 	BPath path(&ent);
1739 	BString string(path.Path());
1740 
1741 	string.CharacterEscape(" ~`#$&*()\\|[]{};'\"<>?!",'\\');
1742 	_WritePTY((const uchar *)string.String(), string.Length());
1743 }
1744 
1745 
1746 /*!	Write strings to PTY device. If encoding system isn't UTF8, change
1747 	encoding to UTF8 before writing PTY.
1748 */
1749 void
1750 TermView::_WritePTY(const uchar *text, int numBytes)
1751 {
1752 	if (fEncoding != M_UTF8) {
1753 		uchar *destBuffer = (uchar *)malloc(numBytes * 3);
1754 		numBytes = CodeConv::ConvertFromInternal((char*)text, numBytes,
1755 			(char*)destBuffer, fEncoding);
1756 		fShell->Write(destBuffer, numBytes);
1757 		free(destBuffer);
1758 	} else {
1759 		fShell->Write(text, numBytes);
1760 	}
1761 }
1762 
1763 
1764 void
1765 TermView::MouseDown(BPoint where)
1766 {
1767 	if (!IsFocus())
1768 		MakeFocus();
1769 
1770 	int32 buttons;
1771 	Window()->CurrentMessage()->FindInt32("buttons", &buttons);
1772 
1773 	// paste button
1774 	if ((buttons & (B_SECONDARY_MOUSE_BUTTON | B_TERTIARY_MOUSE_BUTTON)) != 0) {
1775 		if (_HasSelection()) {
1776 			// copy text from region
1777 			BString copy;
1778 			fTextBuffer->GetStringFromRegion(copy, fSelStart, fSelEnd);
1779 			_WritePTY((uchar *)copy.String(), copy.Length());
1780 		} else {
1781 			// copy text from clipboard.
1782 			Paste(be_clipboard);
1783 		}
1784 		return;
1785 	}
1786 
1787 	// Select Region
1788 	if (buttons == B_PRIMARY_MOUSE_BUTTON) {
1789 		int32 mod, clicks;
1790 		Window()->CurrentMessage()->FindInt32("modifiers", &mod);
1791 		Window()->CurrentMessage()->FindInt32("clicks", &clicks);
1792 
1793 		if (_HasSelection()) {
1794 			CurPos inPos = _ConvertToTerminal(where);
1795 			if (_CheckSelectedRegion(inPos)) {
1796 				if (mod & B_CONTROL_KEY) {
1797 					BPoint p;
1798 					uint32 bt;
1799 					do {
1800 						GetMouse(&p, &bt);
1801 
1802 						if (bt == 0) {
1803 							_DeSelect();
1804 							return;
1805 						}
1806 
1807 						snooze(40000);
1808 
1809 					} while (abs((int)(where.x - p.x)) < 4
1810 						&& abs((int)(where.y - p.y)) < 4);
1811 
1812 					InitiateDrag();
1813 					return;
1814 				}
1815 			}
1816 		}
1817 
1818 		// If mouse has a lot of movement, disable double/triple click.
1819 		/*BPoint inPoint = fClickPoint - where;
1820 		if (abs((int)inPoint.x) > 16 || abs((int)inPoint.y) > 16)
1821 			clicks = 1;
1822 		*/
1823 
1824 		SetMouseEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS,
1825 				B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
1826 
1827 		fClickPoint = where;
1828 
1829 		if (mod & B_SHIFT_KEY)
1830 			_AddSelectRegion(_ConvertToTerminal(where));
1831 		else
1832 			_DeSelect();
1833 
1834 
1835 		// If clicks larger than 3, reset mouse click counter.
1836 		clicks = clicks % 3;
1837 		if (clicks == 0)
1838 			clicks = 3;
1839 
1840 		switch (clicks) {
1841 			case 1:
1842 				fMouseTracking = true;
1843 	      			break;
1844 
1845 			case 2:
1846 				_SelectWord(where, mod);
1847 				break;
1848 
1849 			case 3:
1850 	 			_SelectLine(where, mod);
1851 				break;
1852 		}
1853 		return;
1854   	}
1855 
1856 	BView::MouseDown(where);
1857 }
1858 
1859 
1860 void
1861 TermView::MouseMoved(BPoint where, uint32 transit, const BMessage *message)
1862 {
1863 	BView::MouseMoved(where, transit, message);
1864 	if (!fMouseTracking)
1865 		return;
1866 
1867 	CurPos startPos = _ConvertToTerminal(fClickPoint);
1868 	CurPos endPos = _ConvertToTerminal(where);
1869 	if (endPos.y < 0)
1870 		return;
1871 
1872 	_DeSelect();
1873 	_Select(startPos, endPos);
1874 
1875 	// Scroll check
1876 	if (fScrollBar != NULL) {
1877 		// Get now scroll point
1878 		float scrollStart, scrollEnd;
1879 		fScrollBar->GetRange(&scrollStart, &scrollEnd);
1880 		float scrollPos = fScrollBar->Value();
1881 
1882 		if (where.y < Bounds().LeftTop().y ) {
1883 			// mouse point left of window
1884 			if (scrollPos != scrollStart)
1885 				ScrollTo(0, where.y);
1886 		}
1887 
1888 		if (where.y > Bounds().LeftBottom().y) {
1889 			// mouse point left of window
1890 			if (scrollPos != scrollEnd)
1891 				ScrollTo(0, where.y);
1892 		}
1893 	}
1894 }
1895 
1896 
1897 void
1898 TermView::MouseUp(BPoint where)
1899 {
1900 	BView::MouseUp(where);
1901 	fMouseTracking = false;
1902 }
1903 
1904 
1905 // Select a range of text
1906 void
1907 TermView::_Select(CurPos start, CurPos end)
1908 {
1909 	if (end < start)
1910 		std::swap(start, end);
1911 
1912 	if (start.x < 0)
1913 		start.x = 0;
1914 	if (end.x >= fTermColumns)
1915 		end.x = fTermColumns - 1;
1916 
1917 	uchar buf[4];
1918 	ushort attr;
1919 	if (fTextBuffer->GetChar(start.y, start.x, buf, &attr) == IN_STRING) {
1920 		start.x--;
1921 		if (start.x < 0)
1922 			start.x = 0;
1923 	}
1924 
1925 	if (fTextBuffer->GetChar(end.y, end.x, buf, &attr) == IN_STRING) {
1926 		end.x++;
1927 		if (end.x >= fTermColumns)
1928 			end.x = fTermColumns;
1929 	}
1930 
1931 	fSelStart = start;
1932 	fSelEnd = end;
1933 
1934 	fTextBuffer->Select(fSelStart, fSelEnd);
1935 	_TermDrawSelectedRegion(fSelStart, fSelEnd);
1936 }
1937 
1938 
1939 // Add select region(shift + mouse click)
1940 void
1941 TermView::_AddSelectRegion(CurPos pos)
1942 {
1943 	if (!_HasSelection())
1944 		return;
1945 
1946 	// error check, and if mouse point to a plase full width character,
1947 	// select point decliment.
1948 	if (pos.x >= fTermColumns)
1949 		pos.x = fTermColumns - 1;
1950 	else if (pos.x < 0)
1951 		pos.x = 0;
1952 
1953 	if (pos.y < 0)
1954 		pos.y = 0;
1955 
1956 	uchar buf[4];
1957 	ushort attr;
1958 	if (fTextBuffer->GetChar(pos.y, pos.x, buf, &attr) == IN_STRING) {
1959 		pos.x++;
1960 		if (pos.x >= fTermColumns)
1961 			pos.x = fTermColumns - 1;
1962 	}
1963 
1964 	CurPos start = fSelStart;
1965 	CurPos end = fSelEnd;
1966 	CurPos inPos;
1967 
1968 	// Mouse point is same as selected line.
1969 	if (pos.y == fSelStart.y && pos.y == fSelEnd.y) {
1970 
1971 		if (abs(pos.x - start.x) > abs(pos.x - end.x)) {
1972 
1973 			fSelStart = start;
1974 			fSelEnd = pos;
1975 			inPos = end;
1976 
1977 		} else {
1978 
1979 			fSelStart = end;
1980 			fSelEnd = pos;
1981 			inPos = start;
1982 		}
1983 		// else, End point set to near the start or end point.
1984 	} else if (abs(pos.y - start.y) > abs(pos.y - end.y)) {
1985 
1986 		fSelStart = start;
1987 		fSelEnd = pos;
1988 		inPos = end;
1989 	} else if (abs(pos.y - start.y) > abs(pos.y - end.y)) {
1990 		fSelStart = end;
1991 		fSelEnd = pos;
1992 		inPos = start;
1993 
1994 	} else {
1995 		if (start > end) {
1996 			inPos = start;
1997 			start = end;
1998 			end = inPos;
1999 		}
2000 
2001 		if (pos.y < start.y) {
2002 			fSelStart = end;
2003 			fSelEnd = pos;
2004 			inPos = start;
2005 		} else {
2006 			fSelStart = start;
2007 			fSelEnd = pos;
2008 			inPos = end;
2009 		}
2010 	}
2011 
2012 	fTextBuffer->Select(fSelStart, fSelEnd);
2013 	_TermDrawSelectedRegion(inPos, fSelEnd);
2014 }
2015 
2016 
2017 // Resize select region (mouse drag)
2018 void
2019 TermView::_ResizeSelectRegion(CurPos pos)
2020 {
2021 	//TODO: Broken. Selecting from right to left doesn't work.
2022 
2023 	CurPos inPos = fSelEnd;
2024 
2025 	// error check, and if mouse point to a plase full width character,
2026 	// select point decliment.
2027 	if (pos.x >= fTermColumns)
2028 		pos.x = fTermColumns - 1;
2029 	else if (pos.x < 0)
2030 		pos.x = 0;
2031 
2032 	if (pos.y < 0)
2033 		pos.y = 0;
2034 
2035 	uchar buf[4];
2036 	ushort attr;
2037 	if (fTextBuffer->GetChar(pos.y, pos.x, buf, &attr) == IN_STRING) {
2038 		pos.x++;
2039 
2040 		if (pos == inPos)
2041 			return;
2042 
2043 		if (pos.x >= fTermColumns)
2044 			pos.x = fTermColumns - 1;
2045 	}
2046 
2047 	fSelEnd = pos;
2048 
2049 	fTextBuffer->Select(fSelStart, fSelEnd);
2050 	_TermDrawSelectedRegion(inPos, pos);
2051 }
2052 
2053 
2054 // DeSelect a range of text
2055 void
2056 TermView::_DeSelect(void)
2057 {
2058 	if (!_HasSelection())
2059 		return;
2060 
2061 	fTextBuffer->DeSelect();
2062 
2063 	CurPos start = fSelStart;
2064 	CurPos end = fSelEnd;
2065 
2066 	fSelStart.Set(-1, -1);
2067 	fSelEnd.Set(-1, -1);
2068 
2069 	_TermDrawSelectedRegion(start, end);
2070 }
2071 
2072 
2073 bool
2074 TermView::_HasSelection() const
2075 {
2076 	return fSelStart != fSelEnd;
2077 }
2078 
2079 
2080 void
2081 TermView::_SelectWord(BPoint where, int mod)
2082 {
2083 	CurPos start, end, pos;
2084 	bool flag;
2085 
2086 	pos = _ConvertToTerminal(where);
2087 	flag = fTextBuffer->FindWord(pos, &start, &end);
2088 	fTextBuffer->Select(start, end);
2089 
2090 	if (mod & B_SHIFT_KEY) {
2091 		if (flag) {
2092 			if (start < fSelStart)
2093 				_AddSelectRegion(start);
2094 			else if (end > fSelEnd)
2095 				_AddSelectRegion(end);
2096 		} else
2097 			_AddSelectRegion(pos);
2098 	} else {
2099 		_DeSelect();
2100 		if (flag)
2101 			_Select(start, end);
2102 	}
2103 }
2104 
2105 
2106 void
2107 TermView::_SelectLine(BPoint where, int mod)
2108 {
2109 	CurPos pos = _ConvertToTerminal(where);
2110 
2111 	if (mod & B_SHIFT_KEY) {
2112 
2113 		CurPos start = CurPos(0, pos.y);
2114 		CurPos end = CurPos(fTermColumns - 1, pos.y);
2115 
2116 		if (start < fSelStart)
2117 			_AddSelectRegion(start);
2118 		else if (end > fSelEnd)
2119 			_AddSelectRegion(end);
2120 
2121 	} else {
2122 		_DeSelect();
2123 		_Select(CurPos(0, pos.y), CurPos(fTermColumns - 1, pos.y));
2124 	}
2125 }
2126 
2127 
2128 // Convert View visible area corrdination to cursor position.
2129 CurPos
2130 TermView::_ConvertToTerminal(const BPoint &p)
2131 {
2132 	return CurPos(p.x / fFontWidth, p.y / fFontHeight);
2133 }
2134 
2135 
2136 // Convert cursor position to view coordination.
2137 BPoint
2138 TermView::_ConvertFromTerminal(const CurPos &pos)
2139 {
2140 	return BPoint(fFontWidth * pos.x, pos.y * fFontHeight + fTop);
2141 }
2142 
2143 
2144 bool
2145 TermView::_CheckSelectedRegion(const CurPos &pos)
2146 {
2147 	CurPos start, end;
2148 
2149 	if (fSelStart > fSelEnd) {
2150 		start = fSelEnd;
2151 		end = fSelStart;
2152 	} else {
2153 		start = fSelStart;
2154 		end = fSelEnd;
2155 	}
2156 
2157 	if (pos >= start && pos <= end)
2158 		return true;
2159 
2160 	return false;
2161 
2162 }
2163 
2164 void
2165 TermView::GetFrameSize(float *width, float *height)
2166 {
2167 	if (width != NULL)
2168 		*width = fTermColumns * fFontWidth;
2169 
2170 	if (height == NULL)
2171 		return;
2172 
2173 	if (!fTop) {
2174 		*height = fTermRows * fFontHeight;
2175 		return;
2176 	}
2177 
2178 	if (fTop - fTermRows * fFontHeight > fScrBufSize * fFontHeight) {
2179 
2180 		*height = fScrBufSize * fFontHeight;
2181 		return;
2182 	}
2183 
2184 	*height = fTop + fTermRows * fFontHeight;
2185 }
2186 
2187 
2188 // Find a string, and select it if found
2189 bool
2190 TermView::Find(const BString &str, bool forwardSearch, bool matchCase, bool matchWord)
2191 {
2192 	//Get the buffer contents
2193 	BString buffer;
2194 	fTextBuffer->ToString(buffer);
2195 
2196 	CurPos selectionstart = fSelStart;
2197 	CurPos selectionend = fSelEnd;
2198 
2199 	int offset = 0;
2200 	if (selectionstart.x >= 0 || selectionstart.y >= 0) {
2201 		if (forwardSearch)
2202 			//Set the offset to the end of the selection
2203 			offset = (selectionend.y) * fTermColumns + selectionend.x;
2204 		else
2205 			offset = (selectionstart.y) * fTermColumns + selectionstart.x;
2206 	}
2207 
2208 	int initialresult = -1;
2209 	int result = B_ERROR;
2210 
2211 	for (;;) {
2212 		//Actual search
2213 		if (forwardSearch) {
2214 			if (matchCase)
2215 				result = buffer.FindFirst(str, offset);
2216 			else
2217 				result = buffer.IFindFirst(str, offset);
2218 		} else {
2219 			if (matchCase)
2220 				result = buffer.FindLast(str, offset);
2221 			else
2222 				result = buffer.IFindLast(str, offset);
2223 		}
2224 
2225 		if (result == B_ERROR) { //Wrap search like Be's Terminal
2226 			if (forwardSearch) {
2227 				if (matchCase)
2228 					result = buffer.FindFirst(str, 0);
2229 				else
2230 					result = buffer.IFindFirst(str, 0);
2231 			} else {
2232 				if (matchCase)
2233 					result = buffer.FindLast(str, buffer.Length());
2234 				else
2235 					result = buffer.IFindLast(str, buffer.Length());
2236 			}
2237 		}
2238 
2239 		if (result < 0)
2240 			return false;
2241 
2242 		if (matchWord) {
2243 			if (isalnum(buffer.ByteAt(result - 1)) || isalnum(buffer.ByteAt(result + str.Length()))) {
2244 				if (initialresult == -1) //Set the initial offset to the first result to aid word matching
2245 					initialresult = result;
2246 				else if (initialresult == result) //We went round the buffer, nothing found
2247 					return false;
2248 				if (forwardSearch)
2249 					offset = result + str.Length();
2250 				else
2251 					offset = result;
2252 				continue;
2253 			}
2254 			else
2255 				break;
2256 		}
2257 		else
2258 			break;
2259 	}
2260 
2261 	//Select the found text
2262 	selectionstart.y = result / fTermColumns;
2263 	selectionstart.x = result % fTermColumns;
2264 	//Length -1 since it seems to count the \0 as well
2265 	selectionend.y = (result + str.Length() - 1) / fTermColumns;
2266 	selectionend.x = (result + str.Length() - 1) % fTermColumns;
2267 	//Update the contents of the view
2268 	_DeSelect();
2269 	_Select(selectionstart, selectionend);
2270 
2271 	return true;
2272 }
2273 
2274 
2275 //! Get the selected text and copy to str
2276 void
2277 TermView::GetSelection(BString &str)
2278 {
2279 	str.SetTo("");
2280 	fTextBuffer->GetStringFromRegion(str, fSelStart, fSelEnd);
2281 }
2282 
2283 
2284 void
2285 TermView::NotifyQuit(int32 reason)
2286 {
2287 	// TODO: If we are a replicant, we can't just quit the BWindow, no?.
2288 	// Exactly, and the same is true for tabs!
2289 	Window()->PostMessage(B_QUIT_REQUESTED);
2290 }
2291 
2292 
2293 void
2294 TermView::CheckShellGone()
2295 {
2296 	if (!fShell)
2297 		return;
2298 
2299 	// check, if the shell does still live
2300 	pid_t pid = fShell->ProcessID();
2301 	team_info info;
2302 	if (get_team_info(pid, &info) == B_BAD_TEAM_ID) {
2303 		// the shell is gone
2304 		NotifyQuit(0);
2305 	}
2306 }
2307 
2308 
2309 void
2310 TermView::InitiateDrag()
2311 {
2312 	BString copyStr("");
2313 	fTextBuffer->GetStringFromRegion(copyStr, fSelStart, fSelEnd);
2314 
2315 	BMessage message(B_MIME_DATA);
2316 	message.AddData("text/plain", B_MIME_TYPE, copyStr.String(), copyStr.Length());
2317 
2318 	BPoint start = _ConvertFromTerminal(fSelStart);
2319 	BPoint end = _ConvertFromTerminal(fSelEnd);
2320 
2321 	BRect rect;
2322 	if (fSelStart.y == fSelEnd.y) {
2323 		rect.Set(start.x, start.y - fTop, end.x + fFontWidth,
2324 			end.y + fFontHeight - fTop);
2325 
2326 	} else {
2327 
2328 		rect.Set(0, start.y - fTop, fTermColumns * fFontWidth,
2329 			end.y + fFontHeight - fTop);
2330 	}
2331 
2332 	rect = rect & Bounds();
2333 
2334 	DragMessage(&message, rect);
2335 }
2336 
2337 
2338 inline void
2339 TermView::_Redraw(int x1, int y1, int x2, int y2)
2340 {
2341 	BRect rect(x1 * fFontWidth, y1 * fFontHeight,
2342 		(x2 + 1) * fFontWidth -1, (y2 + 1) * fFontHeight -1);
2343 
2344 	if (LockLooper()) {
2345 		Invalidate(rect);
2346 		UnlockLooper();
2347 	}
2348 }
2349 
2350 
2351 /* static */
2352 void
2353 TermView::_FixFontAttributes(BFont &font)
2354 {
2355 	font.SetSpacing(B_FIXED_SPACING);
2356 }
2357 
2358 
2359 void
2360 TermView::_AboutRequested()
2361 {
2362 	BAlert *alert = new (std::nothrow) BAlert("about",
2363 					"Terminal\n"
2364 					"\twritten by Kazuho Okui and Takashi Murai\n"
2365 					"\tupdated by Kian Duffy and others\n\n"
2366 					"\tCopyright " B_UTF8_COPYRIGHT "2003-2007, Haiku.\n", "Ok");
2367 	if (alert != NULL)
2368 		alert->Go();
2369 }
2370 
2371 
2372