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