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