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