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