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