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