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