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