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