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