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