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