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