xref: /haiku/src/apps/terminal/TermView.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
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
43 extern int function_keycode_table[];
44 extern char *function_key_char_table[];
45 
46 extern int gNowCoding;	/* defined TermParse.cpp */
47 
48 rgb_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
60 extern PrefHandler *gTermPref;
61 
62 TermView::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 
139 TermView::~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
150 void
151 TermView::GetFontSize(int* _width, int* _height)
152 {
153 	*_width = fFontWidth;
154 	*_height = fFontHeight;
155 }
156 
157 
158 //! Set number of rows and columns in terminal
159 BRect
160 TermView::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
185 void
186 TermView::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
195 void
196 TermView::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
206 void
207 TermView::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
222 void
223 TermView::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 
269 void
270 TermView::SetScrollBar(BScrollBar *scrollBar)
271 {
272 	fScrollBar = scrollBar;
273 }
274 
275 
276 //! Print one character
277 void
278 TermView::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
308 void
309 TermView::PutCR()
310 {
311 	UpdateLine();
312 	fTextBuffer->WriteCR(fCurPos);
313 	fCurPos.x = 0;
314 }
315 
316 
317 //! Print a LF and move the cursor
318 void
319 TermView::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
340 void
341 TermView::PutNL(int num)
342 {
343 	ScrollRegion(fCurPos.y, -1, SCRDOWN, num);
344 }
345 
346 
347 //! Print a space
348 void
349 TermView::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
359 void
360 TermView::SetInsertMode(int flag)
361 {
362 	UpdateLine();
363 	fInsertModeFlag = flag;
364 }
365 
366 
367 //! Draw region
368 inline int
369 TermView::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
384 int
385 TermView::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
413 int
414 TermView::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.
448 void
449 TermView::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.
461 void
462 TermView::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.
472 void
473 TermView::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.
483 void
484 TermView::DeleteLine(int num)
485 {
486 	ScrollRegion(fCurPos.y, -1, SCRUP, num);
487 }
488 
489 
490 //! Sets cursor position
491 void
492 TermView::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
504 void
505 TermView::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
515 void
516 TermView::SetCurY(int y)
517 {
518 	if (y >= 0 && y < fTermColumns) {
519 		UpdateLine();
520 		fCurPos.y = y;
521 	}
522 }
523 
524 
525 //! Gets cursor position
526 void
527 TermView::GetCurPos(CurPos *inCurPos)
528 {
529 	inCurPos->x = fCurPos.x;
530 	inCurPos->y = fCurPos.y;
531 }
532 
533 
534 //! Gets cursor x position
535 int
536 TermView::GetCurX()
537 {
538 	return fCurPos.x;
539 }
540 
541 
542 //! Gets cursor y position
543 int
544 TermView::GetCurY()
545 {
546 	return fCurPos.y;
547 }
548 
549 
550 //! Saves cursor position
551 void
552 TermView::SaveCursor()
553 {
554 	fCurStack = fCurPos;
555 }
556 
557 
558 //! Restores cursor position
559 void
560 TermView::RestoreCursor()
561 {
562 	UpdateLine();
563 	fCurPos = fCurStack;
564 }
565 
566 
567 //! Move cursor right by 'num' steps.
568 void
569 TermView::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.
585 void
586 TermView::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.
597 void
598 TermView::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.
610 void
611 TermView::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 
622 void
623 TermView::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 
657 void
658 TermView::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.
674 void
675 TermView::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.
698 void
699 TermView::SetCurBlinking(bool flag)
700 {
701 	fCursorBlinkingFlag = flag;
702 }
703 
704 
705 //! Scroll terminal dir directory by 'num' steps.
706 void
707 TermView::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.
729 void
730 TermView::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.
752 void
753 TermView::ScrollAtCursor()
754 {
755 	if (LockLooper()) {
756 		ResizeScrBarRange();
757 		fScrollUpCount = 0;
758 		ScrollTo(0, fTop);
759 		UnlockLooper();
760 	}
761 }
762 
763 
764 thread_id
765 TermView::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.
791 int32
792 TermView::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.
866 int32
867 TermView::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.
1000 void
1001 TermView::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.
1074 void
1075 TermView::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.
1092 void
1093 TermView::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.
1112 void
1113 TermView::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
1129 void
1130 TermView::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 */
1156 void
1157 TermView::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.
1178 void
1179 TermView::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 
1194 void
1195 TermView::AttachedToWindow()
1196 {
1197 	SetFont(&fHalfFont);
1198 	MakeFocus(true);
1199 	fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows);
1200 }
1201 
1202 
1203 void
1204 TermView::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 
1270 void
1271 TermView::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 
1314 void
1315 TermView::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 
1326 void
1327 TermView::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 
1482 void
1483 TermView::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 
1503 void
1504 TermView::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.
1597 void
1598 TermView::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.
1610 void
1611 TermView::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.
1640 void
1641 TermView::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.
1660 void
1661 TermView::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.
1686 void
1687 TermView::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
1714 int
1715 TermView::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 */
1745 void
1746 TermView::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.)
1761 void
1762 TermView::SetupPop(void)
1763 {
1764 	fPopMenu = new BPopUpMenu("");
1765 	MakeEncodingMenu(fPopMenu, gNowCoding, true);
1766 	fPopMenu->SetTargetForItems(Window());
1767 }
1768 
1769 // convert button name to number.
1770 int32
1771 TermView::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 
1791 void
1792 TermView::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 
1940 void
1941 TermView::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
1954 void
1955 TermView::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)
1984 void
1985 TermView::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)
2067 void
2068 TermView::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
2105 void
2106 TermView::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 
2126 void
2127 TermView::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 
2158 void
2159 TermView::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.
2185 CurPos
2186 TermView::BPointToCurPos(const BPoint &p)
2187 {
2188 	return CurPos(p.x / fFontWidth, p.y / fFontHeight);
2189 }
2190 
2191 // Convert cursor position to view coordination.
2192 BPoint
2193 TermView::CurPosToBPoint(const CurPos &pos)
2194 {
2195 	return BPoint(fFontWidth * pos.x, pos.y * fFontHeight + fTop);
2196 }
2197 
2198 bool
2199 TermView::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 
2220 void
2221 TermView::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.
2244 void
2245 TermView::GetFontInfo(int *width, int *height)
2246 {
2247 	 *width = fFontWidth;
2248 	 *height = fFontHeight;
2249 }
2250 
2251 // Send DrawRect data to Draw Engine thread.
2252 inline void
2253 TermView::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