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