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