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