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