TermView.cpp revision df06c6d8c0a904feaf1db6f308ca59377ab91453
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
47extern int function_keycode_table[];
48extern char *function_key_char_table[];
49
50const 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
62const int32 kOffset = 3;
63
64#define ROWS_DEFAULT 25
65#define COLUMNS_DEFAULT 80
66
67
68static 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
85const static uint32 kUpdateSigWinch = 'Rwin';
86
87const static rgb_color kBlackColor = { 0, 0, 0, 255 };
88const static rgb_color kWhiteColor = { 255, 255, 255, 255 };
89
90
91
92TermView::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
140TermView::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
189TermView::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
259status_t
260TermView::_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
293TermView::~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 */
306BArchivable *
307TermView::Instantiate(BMessage* data)
308{
309	if (validate_instantiation(data, "TermView"))
310		return new (std::nothrow) TermView(data);
311
312	return NULL;
313}
314
315
316status_t
317TermView::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
333void
334TermView::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
343const char *
344TermView::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
354void
355TermView::GetFontSize(int* _width, int* _height)
356{
357	*_width = fFontWidth;
358	*_height = fFontHeight;
359}
360
361
362//! Set number of rows and columns in terminal
363BRect
364TermView::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
386void
387TermView::SetTextColor(rgb_color fore, rgb_color back)
388{
389	fTextForeColor = fore;
390	fTextBackColor = back;
391
392	SetLowColor(fTextBackColor);
393	SetViewColor(fTextBackColor);
394}
395
396
397void
398TermView::SetSelectColor(rgb_color fore, rgb_color back)
399{
400	fSelectForeColor = fore;
401	fSelectBackColor = back;
402}
403
404
405void
406TermView::SetCursorColor(rgb_color fore, rgb_color back)
407{
408	fCursorForeColor = fore;
409	fCursorBackColor = back;
410}
411
412
413int
414TermView::Encoding() const
415{
416	return fEncoding;
417}
418
419
420void
421TermView::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
430void
431TermView::GetTermFont(BFont *font) const
432{
433	if (font != NULL)
434		*font = fHalfFont;
435}
436
437
438//! Sets font for terminal
439void
440TermView::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
486void
487TermView::SetScrollBar(BScrollBar *scrollBar)
488{
489	fScrollBar = scrollBar;
490	if (fScrollBar != NULL) {
491		fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows);
492	}
493}
494
495
496void
497TermView::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
506void
507TermView::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
535void
536TermView::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
554void
555TermView::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
577void
578TermView::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
604void
605TermView::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
636void
637TermView::InsertCR()
638{
639	UpdateLine();
640	fTextBuffer->WriteCR(fCurPos);
641	fCurPos.x = 0;
642}
643
644
645//! Print a LF and move the cursor
646void
647TermView::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
668void
669TermView::InsertNewLine(int num)
670{
671	ScrollRegion(fCurPos.y, -1, SCRDOWN, num);
672}
673
674
675//! Print a space
676void
677TermView::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
687void
688TermView::SetInsertMode(int flag)
689{
690	UpdateLine();
691	fInsertModeFlag = flag;
692}
693
694
695//! Draw region
696inline int
697TermView::_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
712int
713TermView::_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
739int
740TermView::_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.
770void
771TermView::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.
783void
784TermView::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.
794void
795TermView::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.
805void
806TermView::DeleteLine(int num)
807{
808	ScrollRegion(fCurPos.y, -1, SCRUP, num);
809}
810
811
812//! Sets cursor position
813void
814TermView::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
826void
827TermView::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
837void
838TermView::SetCurY(int y)
839{
840	if (y >= 0 && y < fTermColumns) {
841		UpdateLine();
842		fCurPos.y = y;
843	}
844}
845
846
847//! Gets cursor position
848void
849TermView::GetCurPos(CurPos *inCurPos)
850{
851	inCurPos->x = fCurPos.x;
852	inCurPos->y = fCurPos.y;
853}
854
855
856//! Gets cursor x position
857int
858TermView::GetCurX()
859{
860	return fCurPos.x;
861}
862
863
864//! Gets cursor y position
865int
866TermView::GetCurY()
867{
868	return fCurPos.y;
869}
870
871
872//! Saves cursor position
873void
874TermView::SaveCursor()
875{
876	fCurStack = fCurPos;
877}
878
879
880//! Restores cursor position
881void
882TermView::RestoreCursor()
883{
884	UpdateLine();
885	fCurPos = fCurStack;
886}
887
888
889//! Move cursor right by 'num' steps.
890void
891TermView::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.
906void
907TermView::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.
918void
919TermView::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.
931void
932TermView::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!!!
944void
945TermView::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
980void
981TermView::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.
998void
999TermView::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.
1022void
1023TermView::SetCurBlinking(bool flag)
1024{
1025	fCursorBlinkingFlag = flag;
1026}
1027
1028
1029//! Scroll terminal dir directory by 'num' steps.
1030void
1031TermView::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.
1053void
1054TermView::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.
1076void
1077TermView::ScrollAtCursor()
1078{
1079	if (LockLooper()) {
1080		_ResizeScrBarRange();
1081		fScrollUpCount = 0;
1082		ScrollTo(0, fTop);
1083		UnlockLooper();
1084	}
1085}
1086
1087
1088status_t
1089TermView::_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
1101void
1102TermView::_DetachShell()
1103{
1104	fShell->ViewDetached();
1105	fShell = NULL;
1106}
1107
1108
1109//! Draw character on offscreen bitmap.
1110void
1111TermView::_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.
1182void
1183TermView::_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.
1201void
1202TermView::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.
1221void
1222TermView::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
1238void
1239TermView::_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.
1257void
1258TermView::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.
1279void
1280TermView::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
1295void
1296TermView::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
1313void
1314TermView::DetachedFromWindow()
1315{
1316	delete fWinchRunner;
1317	fWinchRunner = NULL;
1318}
1319
1320
1321void
1322TermView::Pulse()
1323{
1324	//if (system_time() > fLastCursorTime + 1000000)
1325		BlinkCursor();
1326}
1327
1328
1329void
1330TermView::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
1393void
1394TermView::_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
1437void
1438TermView::WindowActivated(bool active)
1439{
1440	BView::WindowActivated(active);
1441	if (active == false) {
1442		// DoIMConfirm();
1443	}
1444}
1445
1446
1447void
1448TermView::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
1599void
1600TermView::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
1623void
1624TermView::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
1756status_t
1757TermView::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
1766BHandler*
1767TermView::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.
1780void
1781TermView::_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*/
1795void
1796TermView::_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
1810void
1811TermView::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
1906void
1907TermView::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
1943void
1944TermView::MouseUp(BPoint where)
1945{
1946	BView::MouseUp(where);
1947	fMouseTracking = false;
1948}
1949
1950
1951// Select a range of text
1952void
1953TermView::_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)
1986void
1987TermView::_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)
2064void
2065TermView::_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
2101void
2102TermView::_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
2119bool
2120TermView::_HasSelection() const
2121{
2122	return fSelStart != fSelEnd;
2123}
2124
2125
2126void
2127TermView::_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
2152void
2153TermView::_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.
2175CurPos
2176TermView::_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.
2183BPoint
2184TermView::_ConvertFromTerminal(const CurPos &pos)
2185{
2186	return BPoint(fFontWidth * pos.x + kOffset,
2187		pos.y * fFontHeight + fTop + kOffset);
2188}
2189
2190
2191bool
2192TermView::_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
2211void
2212TermView::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
2236bool
2237TermView::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
2323void
2324TermView::GetSelection(BString &str)
2325{
2326	str.SetTo("");
2327	fTextBuffer->GetStringFromRegion(str, fSelStart, fSelEnd);
2328}
2329
2330
2331void
2332TermView::NotifyQuit(int32 reason)
2333{
2334	// implemented in subclasses
2335}
2336
2337
2338void
2339TermView::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
2354void
2355TermView::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
2383inline void
2384TermView::_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 */
2399void
2400TermView::_FixFontAttributes(BFont &font)
2401{
2402	font.SetSpacing(B_FIXED_SPACING);
2403}
2404
2405
2406void
2407TermView::_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