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