2 * Copyright 2001-2014, Haiku, Inc.
3 * Copyright 2003-2004 Kian Duffy, myob@users.sourceforge.net
4 * Parts Copyright 1998-1999 Kazuho Okui and Takashi Murai.
5 * All rights reserved. Distributed under the terms of the MIT license.
8 * Stefano Ceccherini, stefano.ceccherini@gmail.com
9 * Kian Duffy, myob@users.sourceforge.net
10 * Y.Hayakawa, hida@sawada.riec.tohoku.ac.jp
11 * Jonathan Schleifer, js@webkeks.org
12 * Ingo Weinhold, ingo_weinhold@gmx.de
13 * Clemens Zeidler, haiku@Clemens-Zeidler.de
14 * Siarzhuk Zharski, zharik@gmx.li
30 #include <Application.h>
33 #include <Clipboard.h>
35 #include <Directory.h>
41 #include <MessageRunner.h>
44 #include <PopUpMenu.h>
45 #include <PropertyInfo.h>
48 #include <ScrollBar.h>
49 #include <ScrollView.h>
51 #include <StringView.h>
55 #include "ActiveProcessInfo.h"
57 #include "InlineInput.h"
58 #include "PrefHandler.h"
60 #include "ShellParameters.h"
62 #include "TermConst.h"
63 #include "TerminalBuffer.h"
64 #include "TerminalCharClassifier.h"
65 #include "TermViewStates.h"
69 #define ROWS_DEFAULT 25
70 #define COLUMNS_DEFAULT 80
73 #undef B_TRANSLATION_CONTEXT
74 #define B_TRANSLATION_CONTEXT "Terminal TermView"
76 static property_info sPropList
[] = {
79 {B_DIRECT_SPECIFIER
, 0},
80 "get terminal encoding"},
83 {B_DIRECT_SPECIFIER
, 0},
84 "set terminal encoding"},
87 {B_DIRECT_SPECIFIER
, 0},
93 static const uint32 kUpdateSigWinch
= 'Rwin';
94 static const uint32 kBlinkCursor
= 'BlCr';
96 static const bigtime_t kSyncUpdateGranularity
= 100000; // 0.1 s
98 static const int32 kCursorBlinkIntervals
= 3;
99 static const int32 kCursorVisibleIntervals
= 2;
100 static const bigtime_t kCursorBlinkInterval
= 500000;
102 static const rgb_color kBlackColor
= { 0, 0, 0, 255 };
103 static const rgb_color kWhiteColor
= { 255, 255, 255, 255 };
105 // secondary mouse button drop
106 const int32 kSecondaryMouseDropAction
= 'SMDA';
117 template<typename Type
>
119 restrict_value(const Type
& value
, const Type
& min
, const Type
& max
)
121 return value
< min
? min
: (value
> max
? max
: value
);
125 template<typename Type
>
127 saturated_add(Type a
, Type b
)
129 const Type max
= (Type
)(-1);
130 return (max
- a
>= b
? a
+ b
: max
);
134 // #pragma mark - TextBufferSyncLocker
137 class TermView::TextBufferSyncLocker
{
139 TextBufferSyncLocker(TermView
* view
)
143 fView
->fTextBuffer
->Lock();
146 ~TextBufferSyncLocker()
148 fView
->fTextBuffer
->Unlock();
150 if (fView
->fVisibleTextBufferChanged
)
151 fView
->_VisibleTextBufferChanged();
159 // #pragma mark - TermView
162 TermView::TermView(BRect frame
, const ShellParameters
& shellParameters
,
165 BView(frame
, "termview", B_FOLLOW_ALL
,
166 B_WILL_DRAW
| B_FRAME_EVENTS
| B_FULL_UPDATE_ON_RESIZE
),
168 fColumns(COLUMNS_DEFAULT
),
172 fScrBufSize(historySize
),
173 fReportX10MouseEvent(false),
174 fReportNormalMouseEvent(false),
175 fReportButtonMouseEvent(false),
176 fReportAnyMouseEvent(false)
178 status_t status
= _InitObject(shellParameters
);
185 TermView::TermView(int rows
, int columns
,
186 const ShellParameters
& shellParameters
, int32 historySize
)
188 BView(BRect(0, 0, 0, 0), "termview", B_FOLLOW_ALL
,
189 B_WILL_DRAW
| B_FRAME_EVENTS
| B_FULL_UPDATE_ON_RESIZE
),
195 fScrBufSize(historySize
),
196 fReportX10MouseEvent(false),
197 fReportNormalMouseEvent(false),
198 fReportButtonMouseEvent(false),
199 fReportAnyMouseEvent(false)
201 status_t status
= _InitObject(shellParameters
);
207 // TODO: Don't show the dragger, since replicant capabilities
208 // don't work very well ATM.
210 BRect rect(0, 0, 16, 16);
211 rect.OffsetTo(Bounds().right - rect.Width(),
212 Bounds().bottom - rect.Height());
214 SetFlags(Flags() | B_DRAW_ON_CHILDREN | B_FOLLOW_ALL);
215 AddChild(new BDragger(rect, this,
216 B_FOLLOW_RIGHT|B_FOLLOW_BOTTOM, B_WILL_DRAW));*/
220 TermView::TermView(BMessage
* archive
)
224 fColumns(COLUMNS_DEFAULT
),
229 fReportX10MouseEvent(false),
230 fReportNormalMouseEvent(false),
231 fReportButtonMouseEvent(false),
232 fReportAnyMouseEvent(false)
234 BRect frame
= Bounds();
236 if (archive
->FindInt32("encoding", (int32
*)&fEncoding
) < B_OK
)
238 if (archive
->FindInt32("columns", (int32
*)&fColumns
) < B_OK
)
239 fColumns
= COLUMNS_DEFAULT
;
240 if (archive
->FindInt32("rows", (int32
*)&fRows
) < B_OK
)
241 fRows
= ROWS_DEFAULT
;
244 if (archive
->HasInt32("argc"))
245 archive
->FindInt32("argc", &argc
);
247 const char **argv
= new const char*[argc
];
248 for (int32 i
= 0; i
< argc
; i
++) {
249 archive
->FindString("argv", i
, (const char**)&argv
[i
]);
252 // TODO: Retrieve colors, history size, etc. from archive
253 status_t status
= _InitObject(ShellParameters(argc
, argv
));
257 bool useRect
= false;
258 if ((archive
->FindBool("use_rect", &useRect
) == B_OK
) && useRect
)
265 /*! Initializes the object for further use.
266 The members fRows, fColumns, fEncoding, and fScrBufSize must
267 already be initialized; they are not touched by this method.
270 TermView::_InitObject(const ShellParameters
& shellParameters
)
272 SetFlags(Flags() | B_WILL_DRAW
| B_FRAME_EVENTS
273 | B_FULL_UPDATE_ON_RESIZE
/* | B_INPUT_METHOD_AWARE*/);
277 fCursorBlinkRunner
= NULL
;
278 fAutoScrollRunner
= NULL
;
279 fResizeRunner
= NULL
;
281 fCharClassifier
= NULL
;
285 fEmulateBold
= false;
287 fFrameResized
= false;
288 fResizeViewDisableCount
= 0;
289 fLastActivityTime
= 0;
291 fCursorStyle
= BLOCK_CURSOR
;
292 fCursorBlinking
= true;
293 fCursorHidden
= false;
294 fCursor
= TermPos(0, 0);
296 fVisibleTextBuffer
= NULL
;
297 fVisibleTextBufferChanged
= false;
300 fSelectForeColor
= kWhiteColor
;
301 fSelectBackColor
= kBlackColor
;
304 fScrolledSinceLastSync
= 0;
306 fConsiderClockedSync
= false;
307 fSelection
.SetHighlighter(this);
308 fSelection
.SetRange(TermPos(0, 0), TermPos(0, 0));
309 fPrevPos
= TermPos(-1, - 1);
310 fReportX10MouseEvent
= false;
311 fReportNormalMouseEvent
= false;
312 fReportButtonMouseEvent
= false;
313 fReportAnyMouseEvent
= false;
314 fMouseClipboard
= be_clipboard
;
315 fDefaultState
= new(std::nothrow
) DefaultState(this);
316 fSelectState
= new(std::nothrow
) SelectState(this);
317 fHyperLinkState
= new(std::nothrow
) HyperLinkState(this);
318 fHyperLinkMenuState
= new(std::nothrow
) HyperLinkMenuState(this);
321 fTextBuffer
= new(std::nothrow
) TerminalBuffer
;
322 if (fTextBuffer
== NULL
)
325 fVisibleTextBuffer
= new(std::nothrow
) BasicTerminalBuffer
;
326 if (fVisibleTextBuffer
== NULL
)
329 // TODO: Make the special word chars user-settable!
330 fCharClassifier
= new(std::nothrow
) DefaultCharClassifier(
331 kDefaultAdditionalWordCharacters
);
332 if (fCharClassifier
== NULL
)
335 status_t error
= fTextBuffer
->Init(fColumns
, fRows
, fScrBufSize
);
338 fTextBuffer
->SetEncoding(fEncoding
);
340 error
= fVisibleTextBuffer
->Init(fColumns
, fRows
+ 2, 0);
344 fShell
= new (std::nothrow
) Shell();
348 SetTermFont(be_fixed_font
);
350 // set the shell parameters' encoding
351 ShellParameters
modifiedShellParameters(shellParameters
);
352 modifiedShellParameters
.SetEncoding(fEncoding
);
354 error
= fShell
->Open(fRows
, fColumns
, modifiedShellParameters
);
359 error
= _AttachShell(fShell
);
363 fHighlights
.AddItem(&fSelection
);
365 if (fDefaultState
== NULL
|| fSelectState
== NULL
|| fHyperLinkState
== NULL
366 || fHyperLinkMenuState
== NULL
) {
370 SetLowColor(fTextBackColor
);
371 SetViewColor(B_TRANSPARENT_32_BIT
);
373 _NextState(fDefaultState
);
379 TermView::~TermView()
381 Shell
* shell
= fShell
;
382 // _DetachShell sets fShell to NULL
386 delete fDefaultState
;
388 delete fHyperLinkState
;
389 delete fHyperLinkMenuState
;
391 delete fAutoScrollRunner
;
392 delete fCharClassifier
;
393 delete fVisibleTextBuffer
;
400 TermView::IsShellBusy() const
402 return fShell
!= NULL
&& fShell
->HasActiveProcesses();
407 TermView::GetActiveProcessInfo(ActiveProcessInfo
& _info
) const
409 if (fShell
== NULL
) {
414 return fShell
->GetActiveProcessInfo(_info
);
419 TermView::GetShellInfo(ShellInfo
& _info
) const
421 if (fShell
== NULL
) {
426 _info
= fShell
->Info();
433 TermView::Instantiate(BMessage
* data
)
435 if (validate_instantiation(data
, "TermView")) {
436 TermView
*view
= new (std::nothrow
) TermView(data
);
445 TermView::Archive(BMessage
* data
, bool deep
) const
447 status_t status
= BView::Archive(data
, deep
);
449 status
= data
->AddString("add_on", TERM_SIGNATURE
);
451 status
= data
->AddInt32("encoding", (int32
)fEncoding
);
453 status
= data
->AddInt32("columns", (int32
)fColumns
);
455 status
= data
->AddInt32("rows", (int32
)fRows
);
457 if (data
->ReplaceString("class", "TermView") != B_OK
)
458 data
->AddString("class", "TermView");
465 TermView::ForegroundColor()
467 return fSelectForeColor
;
472 TermView::BackgroundColor()
474 return fSelectBackColor
;
479 TermView::_LineAt(float y
)
481 int32 location
= int32(y
+ fScrollOffset
);
483 // Make sure negative offsets are rounded towards the lower neighbor, too.
485 location
-= fFontHeight
- 1;
487 return location
/ fFontHeight
;
492 TermView::_LineOffset(int32 index
)
494 return index
* fFontHeight
- fScrollOffset
;
498 // convert view coordinates to terminal text buffer position
500 TermView::_ConvertToTerminal(const BPoint
&p
)
502 return TermPos(p
.x
>= 0 ? (int32
)p
.x
/ fFontWidth
: -1, _LineAt(p
.y
));
506 // convert terminal text buffer position to view coordinates
508 TermView::_ConvertFromTerminal(const TermPos
&pos
)
510 return BPoint(fFontWidth
* pos
.x
, _LineOffset(pos
.y
));
515 TermView::_InvalidateTextRect(int32 x1
, int32 y1
, int32 x2
, int32 y2
)
517 // assume the worst case with full-width characters - invalidate 2 cells
518 BRect
rect(x1
* fFontWidth
, _LineOffset(y1
),
519 (x2
+ 1) * fFontWidth
* 2 - 1, _LineOffset(y2
+ 1) - 1);
520 //debug_printf("Invalidate((%f, %f) - (%f, %f))\n", rect.left, rect.top,
521 //rect.right, rect.bottom);
527 TermView::GetPreferredSize(float *width
, float *height
)
530 *width
= fColumns
* fFontWidth
- 1;
532 *height
= fRows
* fFontHeight
- 1;
537 TermView::TerminalName() const
542 return fShell
->TTYName();
546 //! Get width and height for terminal font
548 TermView::GetFontSize(int* _width
, int* _height
)
550 *_width
= fFontWidth
;
551 *_height
= fFontHeight
;
556 TermView::Rows() const
563 TermView::Columns() const
569 //! Set number of rows and columns in terminal
571 TermView::SetTermSize(int rows
, int columns
, bool notifyShell
)
573 //debug_printf("TermView::SetTermSize(%d, %d)\n", rows, columns);
579 // To keep things simple, get rid of the selection first.
583 BAutolock
_(fTextBuffer
);
584 if (fTextBuffer
->ResizeTo(columns
, rows
) != B_OK
585 || fVisibleTextBuffer
->ResizeTo(columns
, rows
+ 2, 0)
591 //debug_printf("Invalidate()\n");
594 if (fScrollBar
!= NULL
) {
595 _UpdateScrollBarRange();
596 fScrollBar
->SetSteps(fFontHeight
, fFontHeight
* fRows
);
599 BRect
rect(0, 0, fColumns
* fFontWidth
, fRows
* fFontHeight
);
601 // synchronize the visible text buffer
603 TextBufferSyncLocker
_(this);
605 _SynchronizeWithTextBuffer(0, -1);
606 int32 offset
= _LineAt(0);
607 fVisibleTextBuffer
->SynchronizeWith(fTextBuffer
, offset
, offset
,
609 fVisibleTextBufferChanged
= true;
613 fFrameResized
= true;
620 TermView::SetTermSize(BRect rect
, bool notifyShell
)
625 GetTermSizeFromRect(rect
, &rows
, &columns
);
626 SetTermSize(rows
, columns
, notifyShell
);
631 TermView::GetTermSizeFromRect(const BRect
&rect
, int *_rows
,
634 int columns
= (rect
.IntegerWidth() + 1) / fFontWidth
;
635 int rows
= (rect
.IntegerHeight() + 1) / fFontHeight
;
645 TermView::SetTextColor(rgb_color fore
, rgb_color back
)
647 fTextBackColor
= back
;
648 fTextForeColor
= fore
;
650 SetLowColor(fTextBackColor
);
655 TermView::SetCursorColor(rgb_color fore
, rgb_color back
)
657 fCursorForeColor
= fore
;
658 fCursorBackColor
= back
;
663 TermView::SetSelectColor(rgb_color fore
, rgb_color back
)
665 fSelectForeColor
= fore
;
666 fSelectBackColor
= back
;
671 TermView::SetTermColor(uint index
, rgb_color color
, bool dynamic
)
674 if (index
< kTermColorCount
)
675 fTextBuffer
->SetPaletteColor(index
, color
);
681 fTextForeColor
= color
;
684 fTextBackColor
= color
;
685 SetLowColor(fTextBackColor
);
688 fTextForeColor
= PrefHandler::Default()->getRGB(
689 PREF_TEXT_FORE_COLOR
);
692 fTextBackColor
= PrefHandler::Default()->getRGB(
693 PREF_TEXT_BACK_COLOR
);
694 SetLowColor(fTextBackColor
);
703 TermView::Encoding() const
710 TermView::SetEncoding(int encoding
)
712 fEncoding
= encoding
;
715 fShell
->SetEncoding(fEncoding
);
717 BAutolock
_(fTextBuffer
);
718 fTextBuffer
->SetEncoding(fEncoding
);
723 TermView::SetMouseClipboard(BClipboard
*clipboard
)
725 fMouseClipboard
= clipboard
;
730 TermView::GetTermFont(BFont
*font
) const
737 //! Sets font for terminal
739 TermView::SetTermFont(const BFont
*font
)
745 uint16 face
= fBoldFont
.Face();
746 fBoldFont
.SetFace(B_BOLD_FACE
| (face
& ~B_REGULAR_FACE
));
748 fHalfFont
.SetSpacing(B_FIXED_SPACING
);
750 // calculate half font's max width
751 // Not Bounding, check only A-Z (For case of fHalfFont is KanjiFont.)
752 for (int c
= 0x20; c
<= 0x7e; c
++) {
754 sprintf(buf
, "%c", c
);
755 int tmpWidth
= (int)fHalfFont
.StringWidth(buf
);
756 if (tmpWidth
> halfWidth
)
757 halfWidth
= tmpWidth
;
760 fFontWidth
= halfWidth
;
763 fHalfFont
.GetHeight(&hh
);
765 int font_ascent
= (int)hh
.ascent
;
766 int font_descent
=(int)hh
.descent
;
767 int font_leading
=(int)hh
.leading
;
769 if (font_leading
== 0)
772 fFontAscent
= font_ascent
;
773 fFontHeight
= font_ascent
+ font_descent
+ font_leading
+ 1;
775 fCursorStyle
= PrefHandler::Default() == NULL
? BLOCK_CURSOR
776 : PrefHandler::Default()->getCursor(PREF_CURSOR_STYLE
);
777 fCursorBlinking
= PrefHandler::Default()->getBool(PREF_BLINK_CURSOR
);
779 fEmulateBold
= PrefHandler::Default() == NULL
? false
780 : PrefHandler::Default()->getBool(PREF_EMULATE_BOLD
);
782 fAllowBold
= PrefHandler::Default() == NULL
? false
783 : PrefHandler::Default()->getBool(PREF_ALLOW_BOLD
);
786 if (fScrollBar
!= NULL
)
787 fScrollBar
->SetSteps(fFontHeight
, fFontHeight
* fRows
);
792 TermView::SetScrollBar(BScrollBar
*scrollBar
)
794 fScrollBar
= scrollBar
;
795 if (fScrollBar
!= NULL
)
796 fScrollBar
->SetSteps(fFontHeight
, fFontHeight
* fRows
);
801 TermView::Copy(BClipboard
*clipboard
)
803 BAutolock
_(fTextBuffer
);
805 if (!_HasSelection())
809 fTextBuffer
->GetStringFromRegion(copyStr
, fSelection
.Start(),
812 if (clipboard
->Lock()) {
813 BMessage
*clipMsg
= NULL
;
816 if ((clipMsg
= clipboard
->Data()) != NULL
) {
817 clipMsg
->AddData("text/plain", B_MIME_TYPE
, copyStr
.String(),
827 TermView::Paste(BClipboard
*clipboard
)
829 if (clipboard
->Lock()) {
830 BMessage
*clipMsg
= clipboard
->Data();
833 if (clipMsg
->FindData("text/plain", B_MIME_TYPE
,
834 (const void**)&text
, &numBytes
) == B_OK
) {
835 _WritePTY(text
, numBytes
);
846 TermView::SelectAll()
848 BAutolock
_(fTextBuffer
);
850 _Select(TermPos(0, -fTextBuffer
->HistorySize()),
851 TermPos(0, fTextBuffer
->Height()), false, true);
861 BAutolock
_(fTextBuffer
);
862 fTextBuffer
->Clear(true);
864 fVisibleTextBuffer
->Clear(true);
866 //debug_printf("Invalidate()\n");
871 fScrollBar
->SetRange(0, 0);
872 fScrollBar
->SetProportion(1);
879 TermView::_InvalidateTextRange(TermPos start
, TermPos end
)
882 std::swap(start
, end
);
884 if (start
.y
== end
.y
) {
885 _InvalidateTextRect(start
.x
, start
.y
, end
.x
, end
.y
);
887 _InvalidateTextRect(start
.x
, start
.y
, fColumns
, start
.y
);
889 if (end
.y
- start
.y
> 0)
890 _InvalidateTextRect(0, start
.y
+ 1, fColumns
, end
.y
- 1);
892 _InvalidateTextRect(0, end
.y
, end
.x
, end
.y
);
898 TermView::_AttachShell(Shell
*shell
)
905 return fShell
->AttachBuffer(TextBuffer());
910 TermView::_DetachShell()
912 fShell
->DetachBuffer();
918 TermView::_SwitchCursorBlinking(bool blinkingOn
)
921 if (fCursorBlinkRunner
== NULL
) {
922 BMessage
blinkMessage(kBlinkCursor
);
923 fCursorBlinkRunner
= new (std::nothrow
) BMessageRunner(
924 BMessenger(this), &blinkMessage
, kCursorBlinkInterval
);
927 // make sure the cursor becomes visible
929 _InvalidateTextRect(fCursor
.x
, fCursor
.y
, fCursor
.x
, fCursor
.y
);
930 delete fCursorBlinkRunner
;
931 fCursorBlinkRunner
= NULL
;
937 TermView::_Activate()
940 _SwitchCursorBlinking(fCursorBlinking
);
945 TermView::_Deactivate()
947 // make sure the cursor becomes visible
949 _InvalidateTextRect(fCursor
.x
, fCursor
.y
, fCursor
.x
, fCursor
.y
);
951 _SwitchCursorBlinking(false);
957 //! Draw part of a line in the given view.
959 TermView::_DrawLinePart(int32 x1
, int32 y1
, uint32 attr
, char *buf
,
960 int32 width
, Highlight
* highlight
, bool cursor
, BView
*inView
)
962 if (highlight
!= NULL
)
963 attr
= highlight
->Highlighter()->AdjustTextAttributes(attr
);
965 inView
->SetFont(IS_BOLD(attr
) && !fEmulateBold
&& fAllowBold
966 ? &fBoldFont
: &fHalfFont
);
969 int x2
= x1
+ fFontWidth
* width
;
970 int y2
= y1
+ fFontHeight
;
972 rgb_color rgb_fore
= fTextForeColor
;
973 rgb_color rgb_back
= fTextBackColor
;
976 int forecolor
= IS_FORECOLOR(attr
);
977 int backcolor
= IS_BACKCOLOR(attr
);
979 if (IS_FORESET(attr
))
980 rgb_fore
= fTextBuffer
->PaletteColor(forecolor
);
981 if (IS_BACKSET(attr
))
982 rgb_back
= fTextBuffer
->PaletteColor(backcolor
);
986 rgb_fore
= fCursorForeColor
;
987 rgb_back
= fCursorBackColor
;
988 } else if (highlight
!= NULL
) {
989 rgb_fore
= highlight
->Highlighter()->ForegroundColor();
990 rgb_back
= highlight
->Highlighter()->BackgroundColor();
992 // Reverse attribute(If selected area, don't reverse color).
993 if (IS_INVERSE(attr
)) {
994 rgb_color rgb_tmp
= rgb_fore
;
1000 // Fill color at Background color and set low color.
1001 inView
->SetHighColor(rgb_back
);
1002 inView
->FillRect(BRect(x1
, y1
, x2
- 1, y2
- 1));
1003 inView
->SetLowColor(rgb_back
);
1004 inView
->SetHighColor(rgb_fore
);
1007 if (IS_BOLD(attr
)) {
1009 inView
->MovePenTo(x1
- 1, y1
+ fFontAscent
- 1);
1010 inView
->DrawString((char *)buf
);
1011 inView
->SetDrawingMode(B_OP_BLEND
);
1013 rgb_color bright
= rgb_fore
;
1015 bright
.red
= saturated_add
<uint8
>(bright
.red
, 64);
1016 bright
.green
= saturated_add
<uint8
>(bright
.green
, 64);
1017 bright
.blue
= saturated_add
<uint8
>(bright
.blue
, 64);
1019 inView
->SetHighColor(bright
);
1023 inView
->MovePenTo(x1
, y1
+ fFontAscent
);
1024 inView
->DrawString((char *)buf
);
1025 inView
->SetDrawingMode(B_OP_COPY
);
1027 // underline attribute
1028 if (IS_UNDER(attr
)) {
1029 inView
->MovePenTo(x1
, y1
+ fFontAscent
);
1030 inView
->StrokeLine(BPoint(x1
, y1
+ fFontAscent
),
1031 BPoint(x2
, y1
+ fFontAscent
));
1036 /*! Caller must have locked fTextBuffer.
1039 TermView::_DrawCursor()
1041 BRect
rect(fFontWidth
* fCursor
.x
, _LineOffset(fCursor
.y
), 0, 0);
1042 rect
.right
= rect
.left
+ fFontWidth
- 1;
1043 rect
.bottom
= rect
.top
+ fFontHeight
- 1;
1044 int32 firstVisible
= _LineAt(0);
1049 bool cursorVisible
= _IsCursorVisible();
1051 if (cursorVisible
) {
1052 switch (fCursorStyle
) {
1053 case UNDERLINE_CURSOR
:
1054 rect
.top
= rect
.bottom
- 2;
1057 rect
.right
= rect
.left
+ 1;
1065 Highlight
* highlight
= _CheckHighlightRegion(TermPos(fCursor
.x
, fCursor
.y
));
1066 if (fVisibleTextBuffer
->GetChar(fCursor
.y
- firstVisible
, fCursor
.x
,
1067 character
, attr
) == A_CHAR
1068 && (fCursorStyle
== BLOCK_CURSOR
|| !cursorVisible
)) {
1070 int32 width
= IS_WIDTH(attr
) ? FULL_WIDTH
: HALF_WIDTH
;
1072 int32 bytes
= UTF8Char::ByteCount(character
.bytes
[0]);
1073 memcpy(buffer
, character
.bytes
, bytes
);
1074 buffer
[bytes
] = '\0';
1076 _DrawLinePart(fCursor
.x
* fFontWidth
, (int32
)rect
.top
, attr
, buffer
,
1077 width
, highlight
, cursorVisible
, this);
1079 if (highlight
!= NULL
)
1080 SetHighColor(highlight
->Highlighter()->BackgroundColor());
1081 else if (cursorVisible
)
1082 SetHighColor(fCursorBackColor
);
1085 rgb_color rgb_back
= fTextBackColor
;
1086 if (fTextBuffer
->IsAlternateScreenActive())
1087 // alternate screen uses cell attributes beyond the line ends
1088 fTextBuffer
->GetCellAttributes(
1089 fCursor
.y
, fCursor
.x
, attr
, count
);
1091 attr
= fVisibleTextBuffer
->GetLineColor(
1092 fCursor
.y
- firstVisible
);
1094 if (IS_BACKSET(attr
))
1095 rgb_back
= fTextBuffer
->PaletteColor(IS_BACKCOLOR(attr
));
1096 SetHighColor(rgb_back
);
1099 if (IS_WIDTH(attr
) && fCursorStyle
!= IBEAM_CURSOR
)
1100 rect
.right
+= fFontWidth
;
1108 TermView::_IsCursorVisible() const
1110 return !fCursorHidden
&& fCursorState
< kCursorVisibleIntervals
;
1115 TermView::_BlinkCursor()
1117 bool wasVisible
= _IsCursorVisible();
1119 if (!wasVisible
&& fInline
&& fInline
->IsActive())
1122 bigtime_t now
= system_time();
1123 if (Window()->IsActive() && now
- fLastActivityTime
>= kCursorBlinkInterval
)
1124 fCursorState
= (fCursorState
+ 1) % kCursorBlinkIntervals
;
1128 if (wasVisible
!= _IsCursorVisible())
1129 _InvalidateTextRect(fCursor
.x
, fCursor
.y
, fCursor
.x
, fCursor
.y
);
1134 TermView::_ActivateCursor(bool invalidate
)
1136 fLastActivityTime
= system_time();
1137 if (invalidate
&& fCursorState
!= 0)
1144 //! Update scroll bar range and knob size.
1146 TermView::_UpdateScrollBarRange()
1148 if (fScrollBar
== NULL
)
1153 BAutolock
_(fTextBuffer
);
1154 historySize
= fTextBuffer
->HistorySize();
1157 float viewHeight
= fRows
* fFontHeight
;
1158 float historyHeight
= (float)historySize
* fFontHeight
;
1160 //debug_printf("TermView::_UpdateScrollBarRange(): history: %ld, range: %f - 0\n",
1161 //historySize, -historyHeight);
1163 fScrollBar
->SetRange(-historyHeight
, 0);
1164 if (historySize
> 0)
1165 fScrollBar
->SetProportion(viewHeight
/ (viewHeight
+ historyHeight
));
1169 //! Handler for SIGWINCH
1171 TermView::_UpdateSIGWINCH()
1173 if (fFrameResized
) {
1174 fShell
->UpdateWindowSize(fRows
, fColumns
);
1175 fFrameResized
= false;
1181 TermView::AttachedToWindow()
1187 // update the terminal size because it may have changed while the TermView
1188 // was detached from the window. On such conditions FrameResized was not
1189 // called when the resize occured
1190 SetTermSize(Bounds(), true);
1193 fScrollBar
->SetSteps(fFontHeight
, fFontHeight
* fRows
);
1194 _UpdateScrollBarRange();
1197 BMessenger
thisMessenger(this);
1199 BMessage
message(kUpdateSigWinch
);
1200 fWinchRunner
= new (std::nothrow
) BMessageRunner(thisMessenger
,
1204 TextBufferSyncLocker
_(this);
1205 fTextBuffer
->SetListener(thisMessenger
);
1206 _SynchronizeWithTextBuffer(0, -1);
1209 be_clipboard
->StartWatching(thisMessenger
);
1214 TermView::DetachedFromWindow()
1216 be_clipboard
->StopWatching(BMessenger(this));
1218 _NextState(fDefaultState
);
1220 delete fWinchRunner
;
1221 fWinchRunner
= NULL
;
1223 delete fCursorBlinkRunner
;
1224 fCursorBlinkRunner
= NULL
;
1226 delete fResizeRunner
;
1227 fResizeRunner
= NULL
;
1230 BAutolock
_(fTextBuffer
);
1231 fTextBuffer
->UnsetListener();
1237 TermView::Draw(BRect updateRect
)
1239 int32 x1
= (int32
)updateRect
.left
/ fFontWidth
;
1240 int32 x2
= std::min((int)updateRect
.right
/ fFontWidth
, fColumns
- 1);
1242 int32 firstVisible
= _LineAt(0);
1243 int32 y1
= _LineAt(updateRect
.top
);
1244 int32 y2
= std::min(_LineAt(updateRect
.bottom
), (int32
)fRows
- 1);
1246 // clear the area to the right of the line ends
1248 float clearLeft
= fColumns
* fFontWidth
;
1249 if (clearLeft
<= updateRect
.right
) {
1250 BRect
rect(clearLeft
, updateRect
.top
, updateRect
.right
,
1252 SetHighColor(fTextBackColor
);
1257 // clear the area below the last line
1258 if (y2
== fRows
- 1) {
1259 float clearTop
= _LineOffset(fRows
);
1260 if (clearTop
<= updateRect
.bottom
) {
1261 BRect
rect(updateRect
.left
, clearTop
, updateRect
.right
,
1263 SetHighColor(fTextBackColor
);
1268 // draw the affected line parts
1272 for (int32 j
= y1
; j
<= y2
; j
++) {
1274 char buf
[fColumns
* 4 + 1];
1276 if (fVisibleTextBuffer
->IsFullWidthChar(j
- firstVisible
, k
))
1282 for (int32 i
= k
; i
<= x2
;) {
1283 int32 lastColumn
= x2
;
1284 Highlight
* highlight
= _CheckHighlightRegion(j
, i
, lastColumn
);
1285 // This will clip lastColumn to the selection start or end
1286 // to ensure the selection is not drawn at the same time as
1288 int32 count
= fVisibleTextBuffer
->GetString(j
- firstVisible
, i
,
1289 lastColumn
, buf
, attr
);
1291 // debug_printf(" fVisibleTextBuffer->GetString(%ld, %ld, %ld) -> (%ld, \"%.*s\"), highlight: %p\n",
1292 // j - firstVisible, i, lastColumn, count, (int)count, buf, highlight);
1295 // No chars to draw : we just fill the rectangle with the
1296 // back color of the last char at the left
1297 int nextColumn
= lastColumn
+ 1;
1298 BRect
rect(fFontWidth
* i
, _LineOffset(j
),
1299 fFontWidth
* nextColumn
- 1, 0);
1300 rect
.bottom
= rect
.top
+ fFontHeight
- 1;
1302 rgb_color rgb_back
= highlight
!= NULL
1303 ? highlight
->Highlighter()->BackgroundColor()
1306 if (fTextBuffer
->IsAlternateScreenActive()) {
1307 // alternate screen uses cell attributes
1308 // beyond the line ends
1310 fTextBuffer
->GetCellAttributes(j
, i
, attr
, count
);
1311 rect
.right
= rect
.left
+ fFontWidth
* count
- 1;
1312 nextColumn
= i
+ count
;
1314 attr
= fVisibleTextBuffer
->GetLineColor(j
- firstVisible
);
1316 if (IS_BACKSET(attr
)) {
1317 int backcolor
= IS_BACKCOLOR(attr
);
1318 rgb_back
= fTextBuffer
->PaletteColor(backcolor
);
1321 SetHighColor(rgb_back
);
1322 rgb_back
= HighColor();
1325 // Go on to the next block
1330 // Note: full-width characters GetString()-ed always
1331 // with count 1, so this hardcoding is safe. From the other
1332 // side - drawing the whole string with one call render the
1333 // characters not aligned to cells grid - that looks much more
1334 // inaccurate for full-width strings than for half-width ones.
1338 _DrawLinePart(fFontWidth
* i
, (int32
)_LineOffset(j
),
1339 attr
, buf
, count
, highlight
, false, this);
1345 if (fInline
&& fInline
->IsActive())
1346 _DrawInlineMethodString();
1348 if (fCursor
>= TermPos(x1
, y1
) && fCursor
<= TermPos(x2
, y2
))
1354 TermView::_DoPrint(BRect updateRect
)
1360 const int numLines
= (int)((updateRect
.Height()) / fFontHeight
);
1362 int y1
= (int)(updateRect
.top
) / fFontHeight
;
1363 y1
= y1
-(fScrBufSize
- numLines
* 2);
1367 const int y2
= y1
+ numLines
-1;
1369 const int x1
= (int)(updateRect
.left
) / fFontWidth
;
1370 const int x2
= (int)(updateRect
.right
) / fFontWidth
;
1372 for (int j
= y1
; j
<= y2
; j
++) {
1373 // If(x1, y1) Buffer is in string full width character,
1374 // alignment start position.
1377 if (fTextBuffer
->IsFullWidthChar(j
, k
))
1383 for (int i
= k
; i
<= x2
;) {
1384 int count
= fTextBuffer
->GetString(j
, i
, x2
, buf
, &attr
);
1390 _DrawLinePart(fFontWidth
* i
, fFontHeight
* j
,
1391 attr
, buf
, count
, false, false, this);
1400 TermView::WindowActivated(bool active
)
1402 BView::WindowActivated(active
);
1403 if (active
&& IsFocus()) {
1413 fActiveState
->WindowActivated(active
);
1418 TermView::MakeFocus(bool focusState
)
1420 BView::MakeFocus(focusState
);
1422 if (focusState
&& Window() && Window()->IsActive()) {
1433 TermView::KeyDown(const char *bytes
, int32 numBytes
)
1437 fActiveState
->KeyDown(bytes
, numBytes
);
1442 TermView::FrameResized(float width
, float height
)
1444 //debug_printf("TermView::FrameResized(%f, %f)\n", width, height);
1445 int32 columns
= ((int32
)width
+ 1) / fFontWidth
;
1446 int32 rows
= ((int32
)height
+ 1) / fFontHeight
;
1448 if (columns
== fColumns
&& rows
== fRows
)
1451 bool hasResizeView
= fResizeRunner
!= NULL
;
1452 if (!hasResizeView
) {
1453 // show the current size in a view
1454 fResizeView
= new BStringView(BRect(100, 100, 300, 140), "size", "");
1455 fResizeView
->SetAlignment(B_ALIGN_CENTER
);
1456 fResizeView
->SetFont(be_bold_font
);
1457 fResizeView
->SetViewColor(fTextBackColor
);
1458 fResizeView
->SetLowColor(fTextBackColor
);
1459 fResizeView
->SetHighColor(fTextForeColor
);
1461 BMessage
message(MSG_REMOVE_RESIZE_VIEW_IF_NEEDED
);
1462 fResizeRunner
= new(std::nothrow
) BMessageRunner(BMessenger(this),
1467 text
<< columns
<< " x " << rows
;
1468 fResizeView
->SetText(text
.String());
1469 fResizeView
->GetPreferredSize(&width
, &height
);
1470 fResizeView
->ResizeTo(width
* 1.5, height
* 1.5);
1471 fResizeView
->MoveTo((Bounds().Width() - fResizeView
->Bounds().Width()) / 2,
1472 (Bounds().Height()- fResizeView
->Bounds().Height()) / 2);
1473 if (!hasResizeView
&& fResizeViewDisableCount
< 1)
1474 AddChild(fResizeView
);
1476 if (fResizeViewDisableCount
> 0)
1477 fResizeViewDisableCount
--;
1479 SetTermSize(rows
, columns
, true);
1484 TermView::MessageReceived(BMessage
*msg
)
1486 if (fActiveState
->MessageReceived(msg
))
1490 const char *ctrl_l
= "\x0c";
1492 // first check for any dropped message
1493 if (msg
->WasDropped() && (msg
->what
== B_SIMPLE_DATA
1494 || msg
->what
== B_MIME_DATA
)) {
1501 if (msg
->FindRef("refs", i
++, &ref
) == B_OK
) {
1502 // first check if secondary mouse button is pressed
1504 msg
->FindInt32("buttons", &buttons
);
1506 if (buttons
== B_SECONDARY_MOUSE_BUTTON
) {
1508 _SecondaryMouseButtonDropped(msg
);
1514 while (msg
->FindRef("refs", i
++, &ref
) == B_OK
) {
1520 } else if (msg
->FindData("RGBColor", B_RGB_COLOR_TYPE
,
1521 (const void **)&color
, &numBytes
) == B_OK
1522 && numBytes
== sizeof(color
)) {
1523 // TODO: handle color drop
1524 // maybe only on replicants ?
1527 } else if (msg
->FindData("text/plain", B_MIME_TYPE
,
1528 (const void **)&text
, &numBytes
) == B_OK
) {
1529 _WritePTY(text
, numBytes
);
1534 switch (msg
->what
) {
1536 case B_REFS_RECEIVED
:
1538 // handle refs if they weren't dropped
1540 if (msg
->FindRef("refs", i
++, &ref
) == B_OK
) {
1543 while (msg
->FindRef("refs", i
++, &ref
) == B_OK
) {
1548 BView::MessageReceived(msg
);
1559 if (msg
->FindInt32("index", &code
) == B_OK
)
1560 Paste(be_clipboard
);
1564 case B_CLIPBOARD_CHANGED
:
1565 // This message originates from the system clipboard. Overwrite
1566 // the contents of the mouse clipboard with the ones from the
1567 // system clipboard, in case it contains text data.
1568 if (be_clipboard
->Lock()) {
1569 if (fMouseClipboard
->Lock()) {
1570 BMessage
* clipMsgA
= be_clipboard
->Data();
1573 if (clipMsgA
->FindData("text/plain", B_MIME_TYPE
,
1574 (const void**)&text
, &numBytes
) == B_OK
) {
1575 fMouseClipboard
->Clear();
1576 BMessage
* clipMsgB
= fMouseClipboard
->Data();
1577 clipMsgB
->AddData("text/plain", B_MIME_TYPE
,
1579 fMouseClipboard
->Commit();
1581 fMouseClipboard
->Unlock();
1583 be_clipboard
->Unlock();
1591 case B_SET_PROPERTY
:
1596 if (msg
->GetCurrentSpecifier(&i
, &specifier
) == B_OK
1597 && strcmp("encoding",
1598 specifier
.FindString("property", i
)) == 0) {
1599 msg
->FindInt32 ("data", &encodingID
);
1600 SetEncoding(encodingID
);
1601 msg
->SendReply(B_REPLY
);
1603 BView::MessageReceived(msg
);
1608 case B_GET_PROPERTY
:
1612 if (msg
->GetCurrentSpecifier(&i
, &specifier
) == B_OK
1613 && strcmp("encoding",
1614 specifier
.FindString("property", i
)) == 0) {
1615 BMessage
reply(B_REPLY
);
1616 reply
.AddInt32("result", Encoding());
1617 msg
->SendReply(&reply
);
1618 } else if (strcmp("tty",
1619 specifier
.FindString("property", i
)) == 0) {
1620 BMessage
reply(B_REPLY
);
1621 reply
.AddString("result", TerminalName());
1622 msg
->SendReply(&reply
);
1624 BView::MessageReceived(msg
);
1629 case B_MODIFIERS_CHANGED
:
1635 case B_INPUT_METHOD_EVENT
:
1638 if (msg
->FindInt32("be:opcode", &opcode
) == B_OK
) {
1640 case B_INPUT_METHOD_STARTED
:
1642 BMessenger messenger
;
1643 if (msg
->FindMessenger("be:reply_to",
1644 &messenger
) == B_OK
) {
1645 fInline
= new (std::nothrow
)
1646 InlineInput(messenger
);
1651 case B_INPUT_METHOD_STOPPED
:
1656 case B_INPUT_METHOD_CHANGED
:
1657 if (fInline
!= NULL
)
1658 _HandleInputMethodChanged(msg
);
1661 case B_INPUT_METHOD_LOCATION_REQUEST
:
1662 if (fInline
!= NULL
)
1663 _HandleInputMethodLocationRequest();
1673 case B_MOUSE_WHEEL_CHANGED
:
1675 // overridden to allow scrolling emulation in alternative screen
1677 BAutolock
locker(fTextBuffer
);
1679 if (fTextBuffer
->IsAlternateScreenActive()
1680 && msg
->FindFloat("be:wheel_delta_y", &deltaY
) == B_OK
1682 // We are in alternative screen mode and have a vertical delta
1683 // we can work with -- emulate scrolling via terminal escape
1687 // scroll pagewise, if one of Option, Command, or Control is
1690 const char* stepString
;
1691 if ((modifiers() & B_SHIFT_KEY
) != 0) {
1693 stepString
= deltaY
> 0
1694 ? PAGE_DOWN_KEY_CODE
: PAGE_UP_KEY_CODE
;
1695 steps
= abs((int)deltaY
);
1697 // three lines per step
1698 stepString
= deltaY
> 0
1699 ? DOWN_ARROW_KEY_CODE
: UP_ARROW_KEY_CODE
;
1700 steps
= 3 * abs((int)deltaY
);
1703 // We want to do only a single write(), so compose a string
1704 // repeating the sequence as often as required by the delta.
1706 for (int32 i
= 0; i
<steps
; i
++)
1707 toWrite
<< stepString
;
1709 _WritePTY(toWrite
.String(), toWrite
.Length());
1711 // let the BView's implementation handle the standard scrolling
1713 BView::MessageReceived(msg
);
1719 case MENU_CLEAR_ALL
:
1721 fShell
->Write(ctrl_l
, 1);
1726 case kUpdateSigWinch
:
1729 case kSecondaryMouseDropAction
:
1730 _DoSecondaryMouseDropAction(msg
);
1732 case MSG_TERMINAL_BUFFER_CHANGED
:
1734 TextBufferSyncLocker
_(this);
1735 _SynchronizeWithTextBuffer(0, -1);
1738 case MSG_SET_TERMINAL_TITLE
:
1741 if (msg
->FindString("title", &title
) == B_OK
) {
1742 if (fListener
!= NULL
)
1743 fListener
->SetTermViewTitle(this, title
);
1747 case MSG_SET_TERMINAL_COLORS
:
1750 if (msg
->FindInt32("count", &count
) != B_OK
)
1752 bool dynamic
= false;
1753 if (msg
->FindBool("dynamic", &dynamic
) != B_OK
)
1755 for (int i
= 0; i
< count
; i
++) {
1757 if (msg
->FindUInt8("index", i
, &index
) != B_OK
)
1761 rgb_color
* color
= 0;
1762 if (msg
->FindData("color", B_RGB_COLOR_TYPE
,
1763 i
, (const void**)&color
, &bytes
) != B_OK
)
1765 SetTermColor(index
, *color
, dynamic
);
1769 case MSG_RESET_TERMINAL_COLORS
:
1772 if (msg
->FindInt32("count", &count
) != B_OK
)
1774 bool dynamic
= false;
1775 if (msg
->FindBool("dynamic", &dynamic
) != B_OK
)
1777 for (int i
= 0; i
< count
; i
++) {
1779 if (msg
->FindUInt8("index", i
, &index
) != B_OK
)
1782 if (index
< kTermColorCount
)
1784 TermApp::DefaultPalette()[index
], dynamic
);
1788 case MSG_SET_CURSOR_STYLE
:
1790 int32 style
= BLOCK_CURSOR
;
1791 if (msg
->FindInt32("style", &style
) == B_OK
)
1792 fCursorStyle
= style
;
1794 bool blinking
= fCursorBlinking
;
1795 if (msg
->FindBool("blinking", &blinking
) == B_OK
) {
1796 fCursorBlinking
= blinking
;
1797 _SwitchCursorBlinking(fCursorBlinking
);
1800 bool hidden
= fCursorHidden
;
1801 if (msg
->FindBool("hidden", &hidden
) == B_OK
)
1802 fCursorHidden
= hidden
;
1805 case MSG_REPORT_MOUSE_EVENT
:
1808 if (msg
->FindBool("reportX10MouseEvent", &report
) == B_OK
)
1809 fReportX10MouseEvent
= report
;
1811 if (msg
->FindBool("reportNormalMouseEvent", &report
) == B_OK
)
1812 fReportNormalMouseEvent
= report
;
1814 if (msg
->FindBool("reportButtonMouseEvent", &report
) == B_OK
)
1815 fReportButtonMouseEvent
= report
;
1817 if (msg
->FindBool("reportAnyMouseEvent", &report
) == B_OK
)
1818 fReportAnyMouseEvent
= report
;
1821 case MSG_REMOVE_RESIZE_VIEW_IF_NEEDED
:
1825 GetMouse(&point
, &buttons
, false);
1829 if (fResizeView
!= NULL
) {
1830 fResizeView
->RemoveSelf();
1834 delete fResizeRunner
;
1835 fResizeRunner
= NULL
;
1839 case MSG_QUIT_TERMNAL
:
1842 if (msg
->FindInt32("reason", &reason
) != B_OK
)
1844 if (fListener
!= NULL
)
1845 fListener
->NotifyTermViewQuit(this, reason
);
1849 BView::MessageReceived(msg
);
1856 TermView::GetSupportedSuites(BMessage
*message
)
1858 BPropertyInfo
propInfo(sPropList
);
1859 message
->AddString("suites", "suite/vnd.naan-termview");
1860 message
->AddFlat("messages", &propInfo
);
1861 return BView::GetSupportedSuites(message
);
1866 TermView::ScrollTo(BPoint where
)
1868 //debug_printf("TermView::ScrollTo(): %f -> %f\n", fScrollOffset, where.y);
1869 float diff
= where
.y
- fScrollOffset
;
1873 float bottom
= Bounds().bottom
;
1874 int32 oldFirstLine
= _LineAt(0);
1875 int32 oldLastLine
= _LineAt(bottom
);
1876 int32 newFirstLine
= _LineAt(diff
);
1877 int32 newLastLine
= _LineAt(bottom
+ diff
);
1879 fScrollOffset
= where
.y
;
1881 // invalidate the current cursor position before scrolling
1882 _InvalidateTextRect(fCursor
.x
, fCursor
.y
, fCursor
.x
, fCursor
.y
);
1885 BRect
destRect(Frame().OffsetToCopy(Bounds().LeftTop()));
1886 BRect
sourceRect(destRect
.OffsetByCopy(0, diff
));
1887 //debug_printf("CopyBits(((%f, %f) - (%f, %f)) -> (%f, %f) - (%f, %f))\n",
1888 //sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom,
1889 //destRect.left, destRect.top, destRect.right, destRect.bottom);
1890 CopyBits(sourceRect
, destRect
);
1892 // sync visible text buffer with text buffer
1893 if (newFirstLine
!= oldFirstLine
|| newLastLine
!= oldLastLine
) {
1894 if (newFirstLine
!= oldFirstLine
)
1896 //debug_printf("fVisibleTextBuffer->ScrollBy(%ld)\n", newFirstLine - oldFirstLine);
1897 fVisibleTextBuffer
->ScrollBy(newFirstLine
- oldFirstLine
);
1899 TextBufferSyncLocker
_(this);
1901 _SynchronizeWithTextBuffer(newFirstLine
, oldFirstLine
- 1);
1903 _SynchronizeWithTextBuffer(oldLastLine
+ 1, newLastLine
);
1909 TermView::TargetedByScrollView(BScrollView
*scrollView
)
1911 BView::TargetedByScrollView(scrollView
);
1913 SetScrollBar(scrollView
? scrollView
->ScrollBar(B_VERTICAL
) : NULL
);
1918 TermView::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
1919 int32 what
, const char* property
)
1921 BHandler
* target
= this;
1922 BPropertyInfo
propInfo(sPropList
);
1923 if (propInfo
.FindMatch(message
, index
, specifier
, what
, property
) < B_OK
) {
1924 target
= BView::ResolveSpecifier(message
, index
, specifier
, what
,
1933 TermView::_SecondaryMouseButtonDropped(BMessage
* msg
)
1935 // Launch menu to choose what is to do with the msg data
1937 if (msg
->FindPoint("_drop_point_", &point
) != B_OK
)
1940 BMessage
* insertMessage
= new BMessage(*msg
);
1941 insertMessage
->what
= kSecondaryMouseDropAction
;
1942 insertMessage
->AddInt8("action", kInsert
);
1944 BMessage
* cdMessage
= new BMessage(*msg
);
1945 cdMessage
->what
= kSecondaryMouseDropAction
;
1946 cdMessage
->AddInt8("action", kChangeDirectory
);
1948 BMessage
* lnMessage
= new BMessage(*msg
);
1949 lnMessage
->what
= kSecondaryMouseDropAction
;
1950 lnMessage
->AddInt8("action", kLinkFiles
);
1952 BMessage
* mvMessage
= new BMessage(*msg
);
1953 mvMessage
->what
= kSecondaryMouseDropAction
;
1954 mvMessage
->AddInt8("action", kMoveFiles
);
1956 BMessage
* cpMessage
= new BMessage(*msg
);
1957 cpMessage
->what
= kSecondaryMouseDropAction
;
1958 cpMessage
->AddInt8("action", kCopyFiles
);
1960 BMenuItem
* insertItem
= new BMenuItem(
1961 B_TRANSLATE("Insert path"), insertMessage
);
1962 BMenuItem
* cdItem
= new BMenuItem(
1963 B_TRANSLATE("Change directory"), cdMessage
);
1964 BMenuItem
* lnItem
= new BMenuItem(
1965 B_TRANSLATE("Create link here"), lnMessage
);
1966 BMenuItem
* mvItem
= new BMenuItem(B_TRANSLATE("Move here"), mvMessage
);
1967 BMenuItem
* cpItem
= new BMenuItem(B_TRANSLATE("Copy here"), cpMessage
);
1968 BMenuItem
* chItem
= new BMenuItem(B_TRANSLATE("Cancel"), NULL
);
1970 // if the refs point to different directorys disable the cd menu item
1971 bool differentDirs
= false;
1972 BDirectory firstDir
;
1975 while (msg
->FindRef("refs", i
++, &ref
) == B_OK
) {
1979 if (node
.IsDirectory())
1982 entry
.GetParent(&dir
);
1986 dir
.GetNodeRef(&nodeRef
);
1987 firstDir
.SetTo(&nodeRef
);
1988 } else if (firstDir
!= dir
) {
1989 differentDirs
= true;
1994 cdItem
->SetEnabled(false);
1996 BPopUpMenu
*menu
= new BPopUpMenu(
1997 "Secondary mouse button drop menu");
1998 menu
->SetAsyncAutoDestruct(true);
1999 menu
->AddItem(insertItem
);
2000 menu
->AddSeparatorItem();
2001 menu
->AddItem(cdItem
);
2002 menu
->AddItem(lnItem
);
2003 menu
->AddItem(mvItem
);
2004 menu
->AddItem(cpItem
);
2005 menu
->AddSeparatorItem();
2006 menu
->AddItem(chItem
);
2007 menu
->SetTargetForItems(this);
2008 menu
->Go(point
, true, true, true);
2013 TermView::_DoSecondaryMouseDropAction(BMessage
* msg
)
2016 msg
->FindInt8("action", &action
);
2018 BString outString
= "";
2019 BString itemString
= "";
2024 case kChangeDirectory
:
2028 outString
= "ln -s ";
2041 bool listContainsDirectory
= false;
2044 while (msg
->FindRef("refs", i
++, &ref
) == B_OK
) {
2048 BString
string(path
.Path());
2050 if (node
.IsDirectory())
2051 listContainsDirectory
= true;
2056 if (action
== kChangeDirectory
) {
2057 if (!node
.IsDirectory()) {
2058 int32 slash
= string
.FindLast("/");
2059 string
.Truncate(slash
);
2061 string
.CharacterEscape(kShellEscapeCharacters
, '\\');
2062 itemString
+= string
;
2065 string
.CharacterEscape(kShellEscapeCharacters
, '\\');
2066 itemString
+= string
;
2069 if (listContainsDirectory
&& action
== kCopyFiles
)
2072 outString
+= itemString
;
2074 if (action
== kLinkFiles
|| action
== kMoveFiles
|| action
== kCopyFiles
)
2077 if (action
!= kInsert
)
2080 _WritePTY(outString
.String(), outString
.Length());
2084 //! Gets dropped file full path and display it at cursor position.
2086 TermView::_DoFileDrop(entry_ref
& ref
)
2090 BString
string(path
.Path());
2092 string
.CharacterEscape(kShellEscapeCharacters
, '\\');
2093 _WritePTY(string
.String(), string
.Length());
2097 /*! Text buffer must already be locked.
2100 TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop
,
2101 int32 visibleDirtyBottom
)
2103 TerminalBufferDirtyInfo
& info
= fTextBuffer
->DirtyInfo();
2104 int32 linesScrolled
= info
.linesScrolled
;
2106 //debug_printf("TermView::_SynchronizeWithTextBuffer(): dirty: %ld - %ld, "
2107 //"scrolled: %ld, visible dirty: %ld - %ld\n", info.dirtyTop, info.dirtyBottom,
2108 //info.linesScrolled, visibleDirtyTop, visibleDirtyBottom);
2110 bigtime_t now
= system_time();
2111 bigtime_t timeElapsed
= now
- fLastSyncTime
;
2112 if (timeElapsed
> 2 * kSyncUpdateGranularity
) {
2113 // last sync was ages ago
2114 fLastSyncTime
= now
;
2115 fScrolledSinceLastSync
= linesScrolled
;
2118 if (fSyncRunner
== NULL
) {
2119 // We consider clocked syncing when more than a full screen height has
2120 // been scrolled in less than a sync update period. Once we're
2121 // actively considering it, the same condition will convince us to
2123 if (fScrolledSinceLastSync
+ linesScrolled
<= fRows
) {
2124 // Condition doesn't hold yet. Reset if time is up, or otherwise
2126 if (timeElapsed
> kSyncUpdateGranularity
) {
2127 fConsiderClockedSync
= false;
2128 fLastSyncTime
= now
;
2129 fScrolledSinceLastSync
= linesScrolled
;
2131 fScrolledSinceLastSync
+= linesScrolled
;
2132 } else if (fConsiderClockedSync
) {
2133 // We are convinced -- create the sync runner.
2134 fLastSyncTime
= now
;
2135 fScrolledSinceLastSync
= 0;
2137 BMessage
message(MSG_TERMINAL_BUFFER_CHANGED
);
2138 fSyncRunner
= new(std::nothrow
) BMessageRunner(BMessenger(this),
2139 &message
, kSyncUpdateGranularity
);
2140 if (fSyncRunner
!= NULL
&& fSyncRunner
->InitCheck() == B_OK
)
2146 // Looks interesting so far. Reset the counts and consider clocked
2148 fConsiderClockedSync
= true;
2149 fLastSyncTime
= now
;
2150 fScrolledSinceLastSync
= 0;
2152 } else if (timeElapsed
< kSyncUpdateGranularity
) {
2153 // sync time not passed yet -- keep counting
2154 fScrolledSinceLastSync
+= linesScrolled
;
2158 if (fScrolledSinceLastSync
+ linesScrolled
<= fRows
) {
2159 // time's up, but not enough happened
2162 fLastSyncTime
= now
;
2163 fScrolledSinceLastSync
= linesScrolled
;
2165 // Things are still rolling, but the sync time's up.
2166 fLastSyncTime
= now
;
2167 fScrolledSinceLastSync
= 0;
2170 fVisibleTextBufferChanged
= true;
2172 // Simple case first -- complete invalidation.
2173 if (info
.invalidateAll
) {
2175 _UpdateScrollBarRange();
2178 fCursor
= fTextBuffer
->Cursor();
2179 _ActivateCursor(false);
2181 int32 offset
= _LineAt(0);
2182 fVisibleTextBuffer
->SynchronizeWith(fTextBuffer
, offset
, offset
,
2183 offset
+ fTextBuffer
->Height() + 2);
2189 BRect bounds
= Bounds();
2190 int32 firstVisible
= _LineAt(0);
2191 int32 lastVisible
= _LineAt(bounds
.bottom
);
2192 int32 historySize
= fTextBuffer
->HistorySize();
2194 bool doScroll
= false;
2195 if (linesScrolled
> 0) {
2196 _UpdateScrollBarRange();
2198 visibleDirtyTop
-= linesScrolled
;
2199 visibleDirtyBottom
-= linesScrolled
;
2201 if (firstVisible
< 0) {
2202 firstVisible
-= linesScrolled
;
2203 lastVisible
-= linesScrolled
;
2206 if (firstVisible
< -historySize
) {
2207 firstVisible
= -historySize
;
2209 scrollOffset
= -historySize
* fFontHeight
;
2210 // We need to invalidate the lower linesScrolled lines of the
2211 // visible text buffer, since those will be scrolled up and
2212 // need to be replaced. We just use visibleDirty{Top,Bottom}
2213 // for that purpose. Unless invoked from ScrollTo() (i.e.
2214 // user-initiated scrolling) those are unused. In the unlikely
2215 // case that the user is scrolling at the same time we may
2216 // invalidate too many lines, since we have to extend the given
2218 // Note that in the firstVisible == 0 case the new lines are
2219 // already in the dirty region, so they will be updated anyway.
2220 if (visibleDirtyTop
<= visibleDirtyBottom
) {
2221 if (lastVisible
< visibleDirtyTop
)
2222 visibleDirtyTop
= lastVisible
;
2223 if (visibleDirtyBottom
< lastVisible
+ linesScrolled
)
2224 visibleDirtyBottom
= lastVisible
+ linesScrolled
;
2226 visibleDirtyTop
= lastVisible
+ 1;
2227 visibleDirtyBottom
= lastVisible
+ linesScrolled
;
2230 scrollOffset
= fScrollOffset
- linesScrolled
* fFontHeight
;
2232 _ScrollTo(scrollOffset
, false);
2236 if (doScroll
&& lastVisible
>= firstVisible
2237 && !(info
.IsDirtyRegionValid() && firstVisible
>= info
.dirtyTop
2238 && lastVisible
<= info
.dirtyBottom
)) {
2240 float scrollBy
= linesScrolled
* fFontHeight
;
2241 BRect
destRect(Frame().OffsetToCopy(B_ORIGIN
));
2242 BRect
sourceRect(destRect
.OffsetByCopy(0, scrollBy
));
2244 // invalidate the current cursor position before scrolling
2245 _InvalidateTextRect(fCursor
.x
, fCursor
.y
, fCursor
.x
, fCursor
.y
);
2247 //debug_printf("CopyBits(((%f, %f) - (%f, %f)) -> (%f, %f) - (%f, %f))\n",
2248 //sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom,
2249 //destRect.left, destRect.top, destRect.right, destRect.bottom);
2250 CopyBits(sourceRect
, destRect
);
2252 fVisibleTextBuffer
->ScrollBy(linesScrolled
);
2256 for (int32 i
= 0; Highlight
* highlight
= fHighlights
.ItemAt(i
); i
++) {
2257 if (highlight
->IsEmpty())
2260 highlight
->ScrollRange(linesScrolled
);
2261 if (highlight
== &fSelection
) {
2262 fInitialSelectionStart
.y
-= linesScrolled
;
2263 fInitialSelectionEnd
.y
-= linesScrolled
;
2266 if (highlight
->Start().y
< -historySize
) {
2267 if (highlight
== &fSelection
)
2270 _ClearHighlight(highlight
);
2275 // invalidate dirty region
2276 if (info
.IsDirtyRegionValid()) {
2277 _InvalidateTextRect(0, info
.dirtyTop
, fTextBuffer
->Width() - 1,
2280 // clear the selection, if affected
2281 if (!fSelection
.IsEmpty()) {
2282 // TODO: We're clearing the selection more often than necessary --
2283 // to avoid that, we'd also need to track the x coordinates of the
2285 int32 selectionBottom
= fSelection
.End().x
> 0
2286 ? fSelection
.End().y
: fSelection
.End().y
- 1;
2287 if (fSelection
.Start().y
<= info
.dirtyBottom
2288 && info
.dirtyTop
<= selectionBottom
) {
2294 if (visibleDirtyTop
<= visibleDirtyBottom
)
2295 info
.ExtendDirtyRegion(visibleDirtyTop
, visibleDirtyBottom
);
2297 if (linesScrolled
!= 0 || info
.IsDirtyRegionValid()) {
2298 fVisibleTextBuffer
->SynchronizeWith(fTextBuffer
, firstVisible
,
2299 info
.dirtyTop
, info
.dirtyBottom
);
2302 // invalidate cursor, if it changed
2303 TermPos cursor
= fTextBuffer
->Cursor();
2304 if (fCursor
!= cursor
|| linesScrolled
!= 0) {
2305 // Before we scrolled we did already invalidate the old cursor.
2307 _InvalidateTextRect(fCursor
.x
, fCursor
.y
, fCursor
.x
, fCursor
.y
);
2309 _InvalidateTextRect(fCursor
.x
, fCursor
.y
, fCursor
.x
, fCursor
.y
);
2310 _ActivateCursor(false);
2318 TermView::_VisibleTextBufferChanged()
2320 if (!fVisibleTextBufferChanged
)
2323 fVisibleTextBufferChanged
= false;
2324 fActiveState
->VisibleTextBufferChanged();
2328 /*! Write strings to PTY device. If encoding system isn't UTF8, change
2329 encoding to UTF8 before writing PTY.
2332 TermView::_WritePTY(const char* text
, int32 numBytes
)
2334 if (fEncoding
!= M_UTF8
) {
2335 while (numBytes
> 0) {
2337 int32 bufferSize
= sizeof(buffer
);
2338 int32 sourceSize
= numBytes
;
2340 if (convert_to_utf8(fEncoding
, text
, &sourceSize
, buffer
,
2341 &bufferSize
, &state
) != B_OK
|| bufferSize
== 0) {
2345 fShell
->Write(buffer
, bufferSize
);
2347 numBytes
-= sourceSize
;
2350 fShell
->Write(text
, numBytes
);
2355 //! Returns the square of the actual pixel distance between both points
2357 TermView::_MouseDistanceSinceLastClick(BPoint where
)
2359 return (fLastClickPoint
.x
- where
.x
) * (fLastClickPoint
.x
- where
.x
)
2360 + (fLastClickPoint
.y
- where
.y
) * (fLastClickPoint
.y
- where
.y
);
2365 TermView::_SendMouseEvent(int32 buttons
, int32 mode
, int32 x
, int32 y
,
2369 if (buttons
== B_PRIMARY_MOUSE_BUTTON
)
2370 xtermButtons
= 32 + 0;
2371 else if (buttons
== B_SECONDARY_MOUSE_BUTTON
)
2372 xtermButtons
= 32 + 1;
2373 else if (buttons
== B_TERTIARY_MOUSE_BUTTON
)
2374 xtermButtons
= 32 + 2;
2376 xtermButtons
= 32 + 3;
2381 char xtermX
= x
+ 1 + 32;
2382 char xtermY
= y
+ 1 + 32;
2385 destBuffer
[0] = '\033';
2386 destBuffer
[1] = '[';
2387 destBuffer
[2] = 'M';
2388 destBuffer
[3] = xtermButtons
;
2389 destBuffer
[4] = xtermX
;
2390 destBuffer
[5] = xtermY
;
2391 fShell
->Write(destBuffer
, 6);
2396 TermView::MouseDown(BPoint where
)
2403 BMessage
* currentMessage
= Window()->CurrentMessage();
2404 int32 buttons
= currentMessage
->GetInt32("buttons", 0);
2406 fActiveState
->MouseDown(where
, buttons
, fModifiers
);
2408 fMouseButtons
= buttons
;
2409 fLastClickPoint
= where
;
2414 TermView::MouseMoved(BPoint where
, uint32 transit
, const BMessage
*message
)
2418 fActiveState
->MouseMoved(where
, transit
, message
, fModifiers
);
2423 TermView::MouseUp(BPoint where
)
2427 int32 buttons
= Window()->CurrentMessage()->GetInt32("buttons", 0);
2429 fActiveState
->MouseUp(where
, buttons
);
2431 fMouseButtons
= buttons
;
2435 //! Select a range of text.
2437 TermView::_Select(TermPos start
, TermPos end
, bool inclusive
,
2438 bool setInitialSelection
)
2440 TextBufferSyncLocker
_(this);
2442 _SynchronizeWithTextBuffer(0, -1);
2445 std::swap(start
, end
);
2450 //debug_printf("TermView::_Select(): (%ld, %ld) - (%ld, %ld)\n", start.x,
2451 //start.y, end.x, end.y);
2455 if (end
.x
>= fColumns
)
2458 TermPos
minPos(0, -fTextBuffer
->HistorySize());
2459 TermPos
maxPos(0, fTextBuffer
->Height());
2460 start
= restrict_value(start
, minPos
, maxPos
);
2461 end
= restrict_value(end
, minPos
, maxPos
);
2463 // if the end is past the end of the line, select the line break, too
2464 if (fTextBuffer
->LineLength(end
.y
) < end
.x
2465 && end
.y
< fTextBuffer
->Height()) {
2470 if (fTextBuffer
->IsFullWidthChar(start
.y
, start
.x
)) {
2476 if (fTextBuffer
->IsFullWidthChar(end
.y
, end
.x
)) {
2478 if (end
.x
>= fColumns
)
2482 if (!fSelection
.IsEmpty())
2483 _InvalidateTextRange(fSelection
.Start(), fSelection
.End());
2485 fSelection
.SetRange(start
, end
);
2487 if (setInitialSelection
) {
2488 fInitialSelectionStart
= fSelection
.Start();
2489 fInitialSelectionEnd
= fSelection
.End();
2492 _InvalidateTextRange(fSelection
.Start(), fSelection
.End());
2496 //! Extend selection (shift + mouse click).
2498 TermView::_ExtendSelection(TermPos pos
, bool inclusive
,
2499 bool useInitialSelection
)
2501 if (!useInitialSelection
&& !_HasSelection())
2504 TermPos start
= fSelection
.Start();
2505 TermPos end
= fSelection
.End();
2507 if (useInitialSelection
) {
2508 start
= fInitialSelectionStart
;
2509 end
= fInitialSelectionEnd
;
2513 if (pos
>= start
&& pos
>= end
)
2518 _Select(pos
, end
, false, !useInitialSelection
);
2520 _Select(start
, pos
, false, !useInitialSelection
);
2521 else if (useInitialSelection
)
2522 _Select(start
, end
, false, false);
2526 // clear the selection.
2528 TermView::_Deselect()
2530 //debug_printf("TermView::_Deselect(): has selection: %d\n", _HasSelection());
2531 if (_ClearHighlight(&fSelection
)) {
2532 fInitialSelectionStart
.SetTo(0, 0);
2533 fInitialSelectionEnd
.SetTo(0, 0);
2539 TermView::_HasSelection() const
2541 return !fSelection
.IsEmpty();
2546 TermView::_SelectWord(BPoint where
, bool extend
, bool useInitialSelection
)
2548 BAutolock
_(fTextBuffer
);
2550 TermPos pos
= _ConvertToTerminal(where
);
2552 if (!fTextBuffer
->FindWord(pos
, fCharClassifier
, true, start
, end
))
2557 < (useInitialSelection
2558 ? fInitialSelectionStart
: fSelection
.Start())) {
2559 _ExtendSelection(start
, false, useInitialSelection
);
2561 > (useInitialSelection
2562 ? fInitialSelectionEnd
: fSelection
.End())) {
2563 _ExtendSelection(end
, false, useInitialSelection
);
2564 } else if (useInitialSelection
)
2565 _Select(start
, end
, false, false);
2567 _Select(start
, end
, false, !useInitialSelection
);
2572 TermView::_SelectLine(BPoint where
, bool extend
, bool useInitialSelection
)
2574 TermPos start
= TermPos(0, _ConvertToTerminal(where
).y
);
2575 TermPos end
= TermPos(0, start
.y
+ 1);
2579 < (useInitialSelection
2580 ? fInitialSelectionStart
: fSelection
.Start())) {
2581 _ExtendSelection(start
, false, useInitialSelection
);
2583 > (useInitialSelection
2584 ? fInitialSelectionEnd
: fSelection
.End())) {
2585 _ExtendSelection(end
, false, useInitialSelection
);
2586 } else if (useInitialSelection
)
2587 _Select(start
, end
, false, false);
2589 _Select(start
, end
, false, !useInitialSelection
);
2594 TermView::_AddHighlight(Highlight
* highlight
)
2596 fHighlights
.AddItem(highlight
);
2598 if (!highlight
->IsEmpty())
2599 _InvalidateTextRange(highlight
->Start(), highlight
->End());
2604 TermView::_RemoveHighlight(Highlight
* highlight
)
2606 if (!highlight
->IsEmpty())
2607 _InvalidateTextRange(highlight
->Start(), highlight
->End());
2609 fHighlights
.RemoveItem(highlight
);
2614 TermView::_ClearHighlight(Highlight
* highlight
)
2616 if (highlight
->IsEmpty())
2619 _InvalidateTextRange(highlight
->Start(), highlight
->End());
2621 highlight
->SetRange(TermPos(0, 0), TermPos(0, 0));
2626 TermView::Highlight
*
2627 TermView::_CheckHighlightRegion(const TermPos
&pos
) const
2629 for (int32 i
= 0; Highlight
* highlight
= fHighlights
.ItemAt(i
); i
++) {
2630 if (highlight
->RangeContains(pos
))
2638 TermView::Highlight
*
2639 TermView::_CheckHighlightRegion(int32 row
, int32 firstColumn
,
2640 int32
& lastColumn
) const
2642 Highlight
* nextHighlight
= NULL
;
2644 for (int32 i
= 0; Highlight
* highlight
= fHighlights
.ItemAt(i
); i
++) {
2645 if (highlight
->IsEmpty())
2648 if (row
== highlight
->Start().y
&& firstColumn
< highlight
->Start().x
2649 && lastColumn
>= highlight
->Start().x
) {
2650 // region starts before the highlight, but intersects with it
2651 if (nextHighlight
== NULL
2652 || highlight
->Start().x
< nextHighlight
->Start().x
) {
2653 nextHighlight
= highlight
;
2658 if (row
== highlight
->End().y
&& firstColumn
< highlight
->End().x
2659 && lastColumn
>= highlight
->End().x
) {
2660 // region starts in the highlight, but exceeds the end
2661 lastColumn
= highlight
->End().x
- 1;
2665 TermPos
pos(firstColumn
, row
);
2666 if (highlight
->RangeContains(pos
))
2670 if (nextHighlight
!= NULL
)
2671 lastColumn
= nextHighlight
->Start().x
- 1;
2677 TermView::GetFrameSize(float *width
, float *height
)
2681 BAutolock
_(fTextBuffer
);
2682 historySize
= fTextBuffer
->HistorySize();
2686 *width
= fColumns
* fFontWidth
;
2689 *height
= (fRows
+ historySize
) * fFontHeight
;
2693 // Find a string, and select it if found
2695 TermView::Find(const BString
&str
, bool forwardSearch
, bool matchCase
,
2698 TextBufferSyncLocker
_(this);
2699 _SynchronizeWithTextBuffer(0, -1);
2702 if (_HasSelection()) {
2704 start
= fSelection
.End();
2706 start
= fSelection
.Start();
2708 // search from the very beginning/end
2710 start
= TermPos(0, -fTextBuffer
->HistorySize());
2712 start
= TermPos(0, fTextBuffer
->Height());
2715 TermPos matchStart
, matchEnd
;
2716 if (!fTextBuffer
->Find(str
.String(), start
, forwardSearch
, matchCase
,
2717 matchWord
, matchStart
, matchEnd
)) {
2721 _Select(matchStart
, matchEnd
, false, true);
2722 _ScrollToRange(fSelection
.Start(), fSelection
.End());
2728 //! Get the selected text and copy to str
2730 TermView::GetSelection(BString
&str
)
2733 BAutolock
_(fTextBuffer
);
2734 fTextBuffer
->GetStringFromRegion(str
, fSelection
.Start(), fSelection
.End());
2739 TermView::CheckShellGone() const
2744 // check, if the shell does still live
2745 pid_t pid
= fShell
->ProcessID();
2747 return get_team_info(pid
, &info
) == B_BAD_TEAM_ID
;
2752 TermView::InitiateDrag()
2754 BAutolock
_(fTextBuffer
);
2756 BString
copyStr("");
2757 fTextBuffer
->GetStringFromRegion(copyStr
, fSelection
.Start(),
2760 BMessage
message(B_MIME_DATA
);
2761 message
.AddData("text/plain", B_MIME_TYPE
, copyStr
.String(),
2764 BPoint start
= _ConvertFromTerminal(fSelection
.Start());
2765 BPoint end
= _ConvertFromTerminal(fSelection
.End());
2768 if (fSelection
.Start().y
== fSelection
.End().y
)
2769 rect
.Set(start
.x
, start
.y
, end
.x
+ fFontWidth
, end
.y
+ fFontHeight
);
2771 rect
.Set(0, start
.y
, fColumns
* fFontWidth
, end
.y
+ fFontHeight
);
2773 rect
= rect
& Bounds();
2775 DragMessage(&message
, rect
);
2780 TermView::_ScrollTo(float y
, bool scrollGfx
)
2785 if (fScrollBar
!= NULL
)
2786 fScrollBar
->SetValue(y
);
2788 ScrollTo(BPoint(0, y
));
2793 TermView::_ScrollToRange(TermPos start
, TermPos end
)
2796 std::swap(start
, end
);
2798 float startY
= _LineOffset(start
.y
);
2799 float endY
= _LineOffset(end
.y
) + fFontHeight
- 1;
2800 float height
= Bounds().Height();
2802 if (endY
- startY
> height
) {
2803 // The range is greater than the height. Scroll to the closest border.
2805 // already as good as it gets?
2806 if (startY
<= 0 && endY
>= height
)
2810 // scroll down to align the start with the top of the view
2811 _ScrollTo(fScrollOffset
+ startY
, true);
2813 // scroll up to align the end with the bottom of the view
2814 _ScrollTo(fScrollOffset
+ endY
- height
, true);
2817 // The range is smaller than the height.
2820 if (startY
>= 0 && endY
<= height
)
2824 // scroll up to make the start visible
2825 _ScrollTo(fScrollOffset
+ startY
, true);
2827 // scroll down to make the end visible
2828 _ScrollTo(fScrollOffset
+ endY
- height
, true);
2835 TermView::DisableResizeView(int32 disableCount
)
2837 fResizeViewDisableCount
+= disableCount
;
2842 TermView::_DrawInlineMethodString()
2844 if (!fInline
|| !fInline
->String())
2847 const int32 numChars
= BString(fInline
->String()).CountChars();
2849 BPoint startPoint
= _ConvertFromTerminal(fCursor
);
2850 BPoint endPoint
= startPoint
;
2851 endPoint
.x
+= fFontWidth
* numChars
;
2852 endPoint
.y
+= fFontHeight
+ 1;
2854 BRect
eraseRect(startPoint
, endPoint
);
2857 SetHighColor(fTextForeColor
);
2858 FillRect(eraseRect
);
2861 BPoint loc
= _ConvertFromTerminal(fCursor
);
2862 loc
.y
+= fFontHeight
;
2863 SetFont(&fHalfFont
);
2864 SetHighColor(fTextBackColor
);
2865 SetLowColor(fTextForeColor
);
2866 DrawString(fInline
->String(), loc
);
2871 TermView::_HandleInputMethodChanged(BMessage
*message
)
2873 const char *string
= NULL
;
2874 if (message
->FindString("be:string", &string
) < B_OK
|| string
== NULL
)
2877 _ActivateCursor(false);
2880 be_app
->ObscureCursor();
2882 // If we find the "be:confirmed" boolean (and the boolean is true),
2883 // it means it's over for now, so the current InlineInput object
2884 // should become inactive. We will probably receive a
2885 // B_INPUT_METHOD_STOPPED message after this one.
2887 if (message
->FindBool("be:confirmed", &confirmed
) != B_OK
)
2890 fInline
->SetString("");
2896 fInline
->SetString(string
);
2897 fInline
->ResetClauses();
2899 if (!confirmed
&& !fInline
->IsActive())
2900 fInline
->SetActive(true);
2902 // Get the clauses, and pass them to the InlineInput object
2903 // TODO: Find out if what we did it's ok, currently we don't consider
2904 // clauses at all, while the bebook says we should; though the visual
2905 // effect we obtained seems correct. Weird.
2906 int32 clauseCount
= 0;
2909 while (message
->FindInt32("be:clause_start", clauseCount
, &clauseStart
)
2911 && message
->FindInt32("be:clause_end", clauseCount
, &clauseEnd
)
2913 if (!fInline
->AddClause(clauseStart
, clauseEnd
))
2919 fInline
->SetString("");
2920 _ActivateCursor(true);
2922 // now we need to feed ourselves the individual characters as if the
2923 // user would have pressed them now - this lets KeyDown() pick out all
2924 // the special characters like B_BACKSPACE, cursor keys and the like:
2925 const char* currPos
= string
;
2926 const char* prevPos
= currPos
;
2927 while (*currPos
!= '\0') {
2928 if ((*currPos
& 0xC0) == 0xC0) {
2929 // found the start of an UTF-8 char, we collect while it lasts
2931 while ((*currPos
& 0xC0) == 0x80)
2933 } else if ((*currPos
& 0xC0) == 0x80) {
2934 // illegal: character starts with utf-8 intermediate byte, skip it
2935 prevPos
= ++currPos
;
2937 // single byte character/code, just feed that
2940 KeyDown(prevPos
, currPos
- prevPos
);
2946 // temporarily show transient state of inline input
2947 int32 selectionStart
= 0;
2948 int32 selectionEnd
= 0;
2949 message
->FindInt32("be:selection", 0, &selectionStart
);
2950 message
->FindInt32("be:selection", 1, &selectionEnd
);
2952 fInline
->SetSelectionOffset(selectionStart
);
2953 fInline
->SetSelectionLength(selectionEnd
- selectionStart
);
2960 TermView::_HandleInputMethodLocationRequest()
2962 BMessage
message(B_INPUT_METHOD_EVENT
);
2963 message
.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST
);
2965 BString
string(fInline
->String());
2967 const int32
&limit
= string
.CountChars();
2968 BPoint where
= _ConvertFromTerminal(fCursor
);
2969 where
.y
+= fFontHeight
;
2971 for (int32 i
= 0; i
< limit
; i
++) {
2972 // Add the location of the UTF8 characters
2974 where
.x
+= fFontWidth
;
2975 ConvertToScreen(&where
);
2977 message
.AddPoint("be:location_reply", where
);
2978 message
.AddFloat("be:height_reply", fFontHeight
);
2981 fInline
->Method()->SendMessage(&message
);
2986 TermView::_CancelInputMethod()
2991 InlineInput
*inlineInput
= fInline
;
2994 if (inlineInput
->IsActive() && Window()) {
2997 BMessage
message(B_INPUT_METHOD_EVENT
);
2998 message
.AddInt32("be:opcode", B_INPUT_METHOD_STOPPED
);
2999 inlineInput
->Method()->SendMessage(&message
);
3007 TermView::_UpdateModifiers()
3009 // TODO: This method is a general work-around for missing or out-of-order
3010 // B_MODIFIERS_CHANGED messages. This should really be fixed where it is
3011 // broken (app server?).
3012 int32 oldModifiers
= fModifiers
;
3013 fModifiers
= modifiers();
3014 if (fModifiers
!= oldModifiers
&& fActiveState
!= NULL
)
3015 fActiveState
->ModifiersChanged(oldModifiers
, fModifiers
);
3020 TermView::_NextState(State
* state
)
3022 if (state
!= fActiveState
) {
3023 if (fActiveState
!= NULL
)
3024 fActiveState
->Exited();
3025 fActiveState
= state
;
3026 fActiveState
->Entered();
3031 // #pragma mark - Listener
3034 TermView::Listener::~Listener()
3040 TermView::Listener::NotifyTermViewQuit(TermView
* view
, int32 reason
)
3046 TermView::Listener::SetTermViewTitle(TermView
* view
, const char* title
)
3052 TermView::Listener::PreviousTermView(TermView
* view
)
3058 TermView::Listener::NextTermView(TermView
* view
)
3066 #ifdef USE_DEBUG_SNAPSHOTS
3069 TermView::MakeDebugSnapshots()
3071 BAutolock
_(fTextBuffer
);
3072 time_t timeStamp
= time(NULL
);
3073 fTextBuffer
->MakeLinesSnapshots(timeStamp
, ".TextBuffer.dump");
3074 fVisibleTextBuffer
->MakeLinesSnapshots(timeStamp
, ".VisualTextBuffer.dump");
3079 TermView::StartStopDebugCapture()
3081 BAutolock
_(fTextBuffer
);
3082 fTextBuffer
->StartStopDebugCapture();