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