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