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