TermView.cpp revision f2b50593a83669a4e181d492db3c771dfc38fadb
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
45
46// defined VTKeyTbl.c
47extern int function_keycode_table[];
48extern char *function_key_char_table[];
49
50extern int gNowCoding;	// defined in TermParse.cpp
51extern PrefHandler *gTermPref; // Global Preference Handler
52
53const 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
65TermView::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
123TermView::~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
134void
135TermView::GetFontSize(int* _width, int* _height)
136{
137	*_width = fFontWidth;
138	*_height = fFontHeight;
139}
140
141
142//! Set number of rows and columns in terminal
143BRect
144TermView::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
168void
169TermView::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
178void
179TermView::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
189void
190TermView::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
205void
206TermView::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
250void
251TermView::SetScrollBar(BScrollBar *scrollBar)
252{
253	fScrollBar = scrollBar;
254}
255
256
257//! Print one character
258void
259TermView::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
289void
290TermView::PutCR()
291{
292	UpdateLine();
293	fTextBuffer->WriteCR(fCurPos);
294	fCurPos.x = 0;
295}
296
297
298//! Print a LF and move the cursor
299void
300TermView::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
321void
322TermView::PutNL(int num)
323{
324	ScrollRegion(fCurPos.y, -1, SCRDOWN, num);
325}
326
327
328//! Print a space
329void
330TermView::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
340void
341TermView::SetInsertMode(int flag)
342{
343	UpdateLine();
344	fInsertModeFlag = flag;
345}
346
347
348//! Draw region
349inline int
350TermView::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
365int
366TermView::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
394int
395TermView::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.
429void
430TermView::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.
442void
443TermView::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.
453void
454TermView::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.
464void
465TermView::DeleteLine(int num)
466{
467	ScrollRegion(fCurPos.y, -1, SCRUP, num);
468}
469
470
471//! Sets cursor position
472void
473TermView::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
485void
486TermView::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
496void
497TermView::SetCurY(int y)
498{
499	if (y >= 0 && y < fTermColumns) {
500		UpdateLine();
501		fCurPos.y = y;
502	}
503}
504
505
506//! Gets cursor position
507void
508TermView::GetCurPos(CurPos *inCurPos)
509{
510	inCurPos->x = fCurPos.x;
511	inCurPos->y = fCurPos.y;
512}
513
514
515//! Gets cursor x position
516int
517TermView::GetCurX()
518{
519	return fCurPos.x;
520}
521
522
523//! Gets cursor y position
524int
525TermView::GetCurY()
526{
527	return fCurPos.y;
528}
529
530
531//! Saves cursor position
532void
533TermView::SaveCursor()
534{
535	fCurStack = fCurPos;
536}
537
538
539//! Restores cursor position
540void
541TermView::RestoreCursor()
542{
543	UpdateLine();
544	fCurPos = fCurStack;
545}
546
547
548//! Move cursor right by 'num' steps.
549void
550TermView::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.
565void
566TermView::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.
577void
578TermView::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.
590void
591TermView::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
602void
603TermView::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
637void
638TermView::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.
654void
655TermView::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.
678void
679TermView::SetCurBlinking(bool flag)
680{
681	fCursorBlinkingFlag = flag;
682}
683
684
685//! Scroll terminal dir directory by 'num' steps.
686void
687TermView::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.
709void
710TermView::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.
732void
733TermView::ScrollAtCursor()
734{
735	if (LockLooper()) {
736		ResizeScrBarRange();
737		fScrollUpCount = 0;
738		ScrollTo(0, fTop);
739		UnlockLooper();
740	}
741}
742
743
744thread_id
745TermView::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.
771int32
772TermView::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.
846int32
847TermView::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.
980void
981TermView::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.
1054void
1055TermView::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.
1072void
1073TermView::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.
1092void
1093TermView::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
1109void
1110TermView::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*/
1135void
1136TermView::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.
1157void
1158TermView::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
1173void
1174TermView::AttachedToWindow()
1175{
1176	SetFont(&fHalfFont);
1177	MakeFocus(true);
1178	fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows);
1179}
1180
1181
1182void
1183TermView::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
1249void
1250TermView::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
1293void
1294TermView::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
1305void
1306TermView::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
1461void
1462TermView::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
1482void
1483TermView::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.
1576void
1577TermView::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.
1589void
1590TermView::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.
1619void
1620TermView::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.
1639void
1640TermView::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.
1665void
1666TermView::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
1693int
1694TermView::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*/
1724void
1725TermView::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.)
1740void
1741TermView::SetupPop(void)
1742{
1743	fPopMenu = new BPopUpMenu("");
1744	MakeEncodingMenu(fPopMenu, gNowCoding, true);
1745	fPopMenu->SetTargetForItems(Window());
1746}
1747
1748// convert button name to number.
1749int32
1750TermView::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
1767void
1768TermView::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
1911void
1912TermView::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
1924void
1925TermView::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)
1954void
1955TermView::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)
2032void
2033TermView::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
2069void
2070TermView::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
2090void
2091TermView::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
2120void
2121TermView::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.
2144CurPos
2145TermView::BPointToCurPos(const BPoint &p)
2146{
2147	return CurPos(p.x / fFontWidth, p.y / fFontHeight);
2148}
2149
2150// Convert cursor position to view coordination.
2151BPoint
2152TermView::CurPosToBPoint(const CurPos &pos)
2153{
2154	return BPoint(fFontWidth * pos.x, pos.y * fFontHeight + fTop);
2155}
2156
2157bool
2158TermView::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
2177void
2178TermView::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.
2201void
2202TermView::GetFontInfo(int *width, int *height)
2203{
2204	 *width = fFontWidth;
2205	 *height = fFontHeight;
2206}
2207
2208// Find a string, and select it if found
2209bool
2210TermView::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
2232restart:
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
2294void
2295TermView::GetSelection(BString &str)
2296{
2297	str.SetTo("");
2298	fTextBuffer->GetStringFromRegion(str);
2299}
2300
2301
2302// Send DrawRect data to Draw Engine thread.
2303inline void
2304TermView::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