Terminal: Pull user input handling into state classes
This commit is contained in:
@@ -33,6 +33,7 @@ Application Terminal :
|
||||
TermParse.cpp
|
||||
TermScrollView.cpp
|
||||
TermView.cpp
|
||||
TermViewStates.cpp
|
||||
TermWindow.cpp
|
||||
TitlePlaceholderMapper.cpp
|
||||
VTKeyTbl.c
|
||||
|
||||
+35
-378
@@ -62,22 +62,13 @@
|
||||
#include "TermConst.h"
|
||||
#include "TerminalBuffer.h"
|
||||
#include "TerminalCharClassifier.h"
|
||||
#include "TermViewStates.h"
|
||||
#include "VTkeymap.h"
|
||||
|
||||
|
||||
// defined in VTKeyTbl.c
|
||||
extern int function_keycode_table[];
|
||||
extern char *function_key_char_table[];
|
||||
|
||||
#define ROWS_DEFAULT 25
|
||||
#define COLUMNS_DEFAULT 80
|
||||
|
||||
// selection granularity
|
||||
enum {
|
||||
SELECT_CHARS,
|
||||
SELECT_WORDS,
|
||||
SELECT_LINES
|
||||
};
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
#define B_TRANSLATION_CONTEXT "Terminal TermView"
|
||||
@@ -101,7 +92,6 @@ static property_info sPropList[] = {
|
||||
|
||||
static const uint32 kUpdateSigWinch = 'Rwin';
|
||||
static const uint32 kBlinkCursor = 'BlCr';
|
||||
static const uint32 kAutoScroll = 'AScr';
|
||||
|
||||
static const bigtime_t kSyncUpdateGranularity = 100000; // 0.1 s
|
||||
|
||||
@@ -320,14 +310,15 @@ TermView::_InitObject(const ShellParameters& shellParameters)
|
||||
fConsiderClockedSync = false;
|
||||
fSelStart = TermPos(-1, -1);
|
||||
fSelEnd = TermPos(-1, -1);
|
||||
fMouseTracking = false;
|
||||
fCheckMouseTracking = false;
|
||||
fPrevPos = TermPos(-1, - 1);
|
||||
fReportX10MouseEvent = false;
|
||||
fReportNormalMouseEvent = false;
|
||||
fReportButtonMouseEvent = false;
|
||||
fReportAnyMouseEvent = false;
|
||||
fMouseClipboard = be_clipboard;
|
||||
fDefaultState = new(std::nothrow) DefaultState(this);
|
||||
fSelectState = new(std::nothrow) SelectState(this);
|
||||
fActiveState = NULL;
|
||||
|
||||
fTextBuffer = new(std::nothrow) TerminalBuffer;
|
||||
if (fTextBuffer == NULL)
|
||||
@@ -371,9 +362,14 @@ TermView::_InitObject(const ShellParameters& shellParameters)
|
||||
if (error < B_OK)
|
||||
return error;
|
||||
|
||||
if (fDefaultState == NULL || fSelectState == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
SetLowColor(fTextBackColor);
|
||||
SetViewColor(B_TRANSPARENT_32_BIT);
|
||||
|
||||
_NextState(fDefaultState);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -385,6 +381,8 @@ TermView::~TermView()
|
||||
|
||||
_DetachShell();
|
||||
|
||||
delete fDefaultState;
|
||||
delete fSelectState;
|
||||
delete fSyncRunner;
|
||||
delete fAutoScrollRunner;
|
||||
delete fCharClassifier;
|
||||
@@ -1388,165 +1386,7 @@ TermView::MakeFocus(bool focusState)
|
||||
void
|
||||
TermView::KeyDown(const char *bytes, int32 numBytes)
|
||||
{
|
||||
int32 key, mod, rawChar;
|
||||
BMessage *currentMessage = Looper()->CurrentMessage();
|
||||
if (currentMessage == NULL)
|
||||
return;
|
||||
|
||||
currentMessage->FindInt32("modifiers", &mod);
|
||||
currentMessage->FindInt32("key", &key);
|
||||
currentMessage->FindInt32("raw_char", &rawChar);
|
||||
|
||||
_ActivateCursor(true);
|
||||
|
||||
// handle multi-byte chars
|
||||
if (numBytes > 1) {
|
||||
if (fEncoding != M_UTF8) {
|
||||
char destBuffer[16];
|
||||
int32 destLen = sizeof(destBuffer);
|
||||
int32 state = 0;
|
||||
convert_from_utf8(fEncoding, bytes, &numBytes, destBuffer,
|
||||
&destLen, &state, '?');
|
||||
_ScrollTo(0, true);
|
||||
fShell->Write(destBuffer, destLen);
|
||||
return;
|
||||
}
|
||||
|
||||
_ScrollTo(0, true);
|
||||
fShell->Write(bytes, numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
// Terminal filters RET, ENTER, F1...F12, and ARROW key code.
|
||||
const char *toWrite = NULL;
|
||||
|
||||
switch (*bytes) {
|
||||
case B_RETURN:
|
||||
if (rawChar == B_RETURN)
|
||||
toWrite = "\r";
|
||||
break;
|
||||
|
||||
case B_DELETE:
|
||||
toWrite = DELETE_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_BACKSPACE:
|
||||
// Translate only the actual backspace key to the backspace
|
||||
// code. CTRL-H shall just be echoed.
|
||||
if (!((mod & B_CONTROL_KEY) && rawChar == 'h'))
|
||||
toWrite = BACKSPACE_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_LEFT_ARROW:
|
||||
if (rawChar == B_LEFT_ARROW) {
|
||||
if ((mod & B_SHIFT_KEY) != 0) {
|
||||
if (fListener != NULL)
|
||||
fListener->PreviousTermView(this);
|
||||
return;
|
||||
}
|
||||
if ((mod & B_CONTROL_KEY) || (mod & B_COMMAND_KEY))
|
||||
toWrite = CTRL_LEFT_ARROW_KEY_CODE;
|
||||
else
|
||||
toWrite = LEFT_ARROW_KEY_CODE;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_RIGHT_ARROW:
|
||||
if (rawChar == B_RIGHT_ARROW) {
|
||||
if ((mod & B_SHIFT_KEY) != 0) {
|
||||
if (fListener != NULL)
|
||||
fListener->NextTermView(this);
|
||||
return;
|
||||
}
|
||||
if ((mod & B_CONTROL_KEY) || (mod & B_COMMAND_KEY))
|
||||
toWrite = CTRL_RIGHT_ARROW_KEY_CODE;
|
||||
else
|
||||
toWrite = RIGHT_ARROW_KEY_CODE;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_UP_ARROW:
|
||||
if (mod & B_SHIFT_KEY) {
|
||||
_ScrollTo(fScrollOffset - fFontHeight, true);
|
||||
return;
|
||||
}
|
||||
if (rawChar == B_UP_ARROW) {
|
||||
if (mod & B_CONTROL_KEY)
|
||||
toWrite = CTRL_UP_ARROW_KEY_CODE;
|
||||
else
|
||||
toWrite = UP_ARROW_KEY_CODE;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_DOWN_ARROW:
|
||||
if (mod & B_SHIFT_KEY) {
|
||||
_ScrollTo(fScrollOffset + fFontHeight, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rawChar == B_DOWN_ARROW) {
|
||||
if (mod & B_CONTROL_KEY)
|
||||
toWrite = CTRL_DOWN_ARROW_KEY_CODE;
|
||||
else
|
||||
toWrite = DOWN_ARROW_KEY_CODE;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_INSERT:
|
||||
if (rawChar == B_INSERT)
|
||||
toWrite = INSERT_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_HOME:
|
||||
if (rawChar == B_HOME)
|
||||
toWrite = HOME_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_END:
|
||||
if (rawChar == B_END)
|
||||
toWrite = END_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_PAGE_UP:
|
||||
if (mod & B_SHIFT_KEY) {
|
||||
_ScrollTo(fScrollOffset - fFontHeight * fRows, true);
|
||||
return;
|
||||
}
|
||||
if (rawChar == B_PAGE_UP)
|
||||
toWrite = PAGE_UP_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_PAGE_DOWN:
|
||||
if (mod & B_SHIFT_KEY) {
|
||||
_ScrollTo(fScrollOffset + fFontHeight * fRows, true);
|
||||
return;
|
||||
}
|
||||
if (rawChar == B_PAGE_DOWN)
|
||||
toWrite = PAGE_DOWN_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_FUNCTION_KEY:
|
||||
for (int32 i = 0; i < 12; i++) {
|
||||
if (key == function_keycode_table[i]) {
|
||||
toWrite = function_key_char_table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If the above code proposed an alternative string to write, we get it's
|
||||
// length. Otherwise we write exactly the bytes passed to this method.
|
||||
size_t toWriteLen;
|
||||
if (toWrite != NULL) {
|
||||
toWriteLen = strlen(toWrite);
|
||||
} else {
|
||||
toWrite = bytes;
|
||||
toWriteLen = numBytes;
|
||||
}
|
||||
|
||||
_ScrollTo(0, true);
|
||||
fShell->Write(toWrite, toWriteLen);
|
||||
fActiveState->KeyDown(bytes, numBytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -1593,6 +1433,9 @@ TermView::FrameResized(float width, float height)
|
||||
void
|
||||
TermView::MessageReceived(BMessage *msg)
|
||||
{
|
||||
if (fActiveState->MessageReceived(msg))
|
||||
return;
|
||||
|
||||
entry_ref ref;
|
||||
const char *ctrl_l = "\x0c";
|
||||
|
||||
@@ -1829,9 +1672,6 @@ TermView::MessageReceived(BMessage *msg)
|
||||
case kUpdateSigWinch:
|
||||
_UpdateSIGWINCH();
|
||||
break;
|
||||
case kAutoScroll:
|
||||
_AutoScrollUpdate();
|
||||
break;
|
||||
case kSecondaryMouseDropAction:
|
||||
_DoSecondaryMouseDropAction(msg);
|
||||
break;
|
||||
@@ -2480,98 +2320,13 @@ TermView::MouseDown(BPoint where)
|
||||
if (!IsFocus())
|
||||
MakeFocus();
|
||||
|
||||
int32 buttons;
|
||||
int32 modifier;
|
||||
Window()->CurrentMessage()->FindInt32("buttons", &buttons);
|
||||
Window()->CurrentMessage()->FindInt32("modifiers", &modifier);
|
||||
BMessage* currentMessage = Window()->CurrentMessage();
|
||||
int32 buttons = currentMessage->GetInt32("buttons", 0);
|
||||
int32 modifiers = currentMessage->GetInt32("modifiers", 0);
|
||||
|
||||
fActiveState->MouseDown(where, buttons, modifiers);
|
||||
|
||||
fMouseButtons = buttons;
|
||||
|
||||
if (fReportAnyMouseEvent || fReportButtonMouseEvent
|
||||
|| fReportNormalMouseEvent || fReportX10MouseEvent) {
|
||||
TermPos clickPos = _ConvertToTerminal(where);
|
||||
_SendMouseEvent(buttons, modifier, clickPos.x, clickPos.y, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// paste button
|
||||
if ((buttons & (B_SECONDARY_MOUSE_BUTTON | B_TERTIARY_MOUSE_BUTTON)) != 0) {
|
||||
Paste(fMouseClipboard);
|
||||
fLastClickPoint = where;
|
||||
return;
|
||||
}
|
||||
|
||||
// Select Region
|
||||
if (buttons == B_PRIMARY_MOUSE_BUTTON) {
|
||||
int32 clicks;
|
||||
Window()->CurrentMessage()->FindInt32("clicks", &clicks);
|
||||
|
||||
if (_HasSelection()) {
|
||||
TermPos inPos = _ConvertToTerminal(where);
|
||||
if (_CheckSelectedRegion(inPos)) {
|
||||
if (modifier & B_CONTROL_KEY) {
|
||||
BPoint p;
|
||||
uint32 bt;
|
||||
do {
|
||||
GetMouse(&p, &bt);
|
||||
|
||||
if (bt == 0) {
|
||||
_Deselect();
|
||||
return;
|
||||
}
|
||||
|
||||
snooze(40000);
|
||||
|
||||
} while (abs((int)(where.x - p.x)) < 4
|
||||
&& abs((int)(where.y - p.y)) < 4);
|
||||
|
||||
InitiateDrag();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If mouse has moved too much, disable double/triple click.
|
||||
if (_MouseDistanceSinceLastClick(where) > 8)
|
||||
clicks = 1;
|
||||
|
||||
SetMouseEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS,
|
||||
B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
|
||||
|
||||
TermPos clickPos = _ConvertToTerminal(where);
|
||||
|
||||
if (modifier & B_SHIFT_KEY) {
|
||||
fInitialSelectionStart = clickPos;
|
||||
fInitialSelectionEnd = clickPos;
|
||||
_ExtendSelection(fInitialSelectionStart, true, false);
|
||||
} else {
|
||||
_Deselect();
|
||||
fInitialSelectionStart = clickPos;
|
||||
fInitialSelectionEnd = clickPos;
|
||||
}
|
||||
|
||||
// If clicks larger than 3, reset mouse click counter.
|
||||
clicks = (clicks - 1) % 3 + 1;
|
||||
|
||||
switch (clicks) {
|
||||
case 1:
|
||||
fCheckMouseTracking = true;
|
||||
fSelectGranularity = SELECT_CHARS;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_SelectWord(where, (modifier & B_SHIFT_KEY) != 0, false);
|
||||
fMouseTracking = true;
|
||||
fSelectGranularity = SELECT_WORDS;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_SelectLine(where, (modifier & B_SHIFT_KEY) != 0, false);
|
||||
fMouseTracking = true;
|
||||
fSelectGranularity = SELECT_LINES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fLastClickPoint = where;
|
||||
}
|
||||
|
||||
@@ -2579,111 +2334,17 @@ TermView::MouseDown(BPoint where)
|
||||
void
|
||||
TermView::MouseMoved(BPoint where, uint32 transit, const BMessage *message)
|
||||
{
|
||||
if (fReportAnyMouseEvent || fReportButtonMouseEvent) {
|
||||
int32 modifier;
|
||||
Window()->CurrentMessage()->FindInt32("modifiers", &modifier);
|
||||
|
||||
TermPos clickPos = _ConvertToTerminal(where);
|
||||
|
||||
if (fReportButtonMouseEvent) {
|
||||
if (fPrevPos.x != clickPos.x || fPrevPos.y != clickPos.y) {
|
||||
_SendMouseEvent(fMouseButtons, modifier, clickPos.x, clickPos.y,
|
||||
true);
|
||||
}
|
||||
fPrevPos = clickPos;
|
||||
return;
|
||||
}
|
||||
_SendMouseEvent(fMouseButtons, modifier, clickPos.x, clickPos.y, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fCheckMouseTracking) {
|
||||
if (_MouseDistanceSinceLastClick(where) > 9)
|
||||
fMouseTracking = true;
|
||||
}
|
||||
if (!fMouseTracking)
|
||||
return;
|
||||
|
||||
bool doAutoScroll = false;
|
||||
|
||||
if (where.y < 0) {
|
||||
doAutoScroll = true;
|
||||
fAutoScrollSpeed = where.y;
|
||||
where.x = 0;
|
||||
where.y = 0;
|
||||
}
|
||||
|
||||
BRect bounds(Bounds());
|
||||
if (where.y > bounds.bottom) {
|
||||
doAutoScroll = true;
|
||||
fAutoScrollSpeed = where.y - bounds.bottom;
|
||||
where.x = bounds.right;
|
||||
where.y = bounds.bottom;
|
||||
}
|
||||
|
||||
if (doAutoScroll) {
|
||||
if (fAutoScrollRunner == NULL) {
|
||||
BMessage message(kAutoScroll);
|
||||
fAutoScrollRunner = new (std::nothrow) BMessageRunner(
|
||||
BMessenger(this), &message, 10000);
|
||||
}
|
||||
} else {
|
||||
delete fAutoScrollRunner;
|
||||
fAutoScrollRunner = NULL;
|
||||
}
|
||||
|
||||
switch (fSelectGranularity) {
|
||||
case SELECT_CHARS:
|
||||
{
|
||||
// If we just start selecting, we first select the initially
|
||||
// hit char, so that we get a proper initial selection -- the char
|
||||
// in question, which will thus always be selected, regardless of
|
||||
// whether selecting forward or backward.
|
||||
if (fInitialSelectionStart == fInitialSelectionEnd) {
|
||||
_Select(fInitialSelectionStart, fInitialSelectionEnd, true,
|
||||
true);
|
||||
}
|
||||
|
||||
_ExtendSelection(_ConvertToTerminal(where), true, true);
|
||||
break;
|
||||
}
|
||||
case SELECT_WORDS:
|
||||
_SelectWord(where, true, true);
|
||||
break;
|
||||
case SELECT_LINES:
|
||||
_SelectLine(where, true, true);
|
||||
break;
|
||||
}
|
||||
fActiveState->MouseMoved(where, transit, message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::MouseUp(BPoint where)
|
||||
{
|
||||
fCheckMouseTracking = false;
|
||||
fMouseTracking = false;
|
||||
int32 buttons = Window()->CurrentMessage()->GetInt32("buttons", 0);
|
||||
|
||||
if (fAutoScrollRunner != NULL) {
|
||||
delete fAutoScrollRunner;
|
||||
fAutoScrollRunner = NULL;
|
||||
}
|
||||
fActiveState->MouseUp(where, buttons);
|
||||
|
||||
// When releasing the first mouse button, we copy the selected text to the
|
||||
// clipboard.
|
||||
int32 buttons;
|
||||
Window()->CurrentMessage()->FindInt32("buttons", &buttons);
|
||||
|
||||
if (fReportAnyMouseEvent || fReportButtonMouseEvent
|
||||
|| fReportNormalMouseEvent) {
|
||||
TermPos clickPos = _ConvertToTerminal(where);
|
||||
_SendMouseEvent(0, 0, clickPos.x, clickPos.y, false);
|
||||
} else {
|
||||
if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0
|
||||
&& (fMouseButtons & B_PRIMARY_MOUSE_BUTTON) != 0) {
|
||||
Copy(fMouseClipboard);
|
||||
}
|
||||
|
||||
}
|
||||
fMouseButtons = buttons;
|
||||
}
|
||||
|
||||
@@ -2844,22 +2505,6 @@ TermView::_SelectLine(BPoint where, bool extend, bool useInitialSelection)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::_AutoScrollUpdate()
|
||||
{
|
||||
if (fMouseTracking && fAutoScrollRunner != NULL && fScrollBar != NULL) {
|
||||
float value = fScrollBar->Value();
|
||||
_ScrollTo(value + fAutoScrollSpeed, true);
|
||||
if (fAutoScrollSpeed < 0) {
|
||||
_ExtendSelection(_ConvertToTerminal(BPoint(0, 0)), true, true);
|
||||
} else {
|
||||
_ExtendSelection(_ConvertToTerminal(Bounds().RightBottom()), true,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermView::_CheckSelectedRegion(const TermPos &pos) const
|
||||
{
|
||||
@@ -3222,6 +2867,18 @@ TermView::_CancelInputMethod()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::_NextState(State* state)
|
||||
{
|
||||
if (state != fActiveState) {
|
||||
if (fActiveState != NULL)
|
||||
fActiveState->Exited();
|
||||
fActiveState = state;
|
||||
fActiveState->Entered();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Listener
|
||||
|
||||
|
||||
|
||||
@@ -143,6 +143,16 @@ protected:
|
||||
private:
|
||||
class CharClassifier;
|
||||
|
||||
class State;
|
||||
class StandardBaseState;
|
||||
class DefaultState;
|
||||
class SelectState;
|
||||
|
||||
friend class State;
|
||||
friend class StandardBaseState;
|
||||
friend class DefaultState;
|
||||
friend class SelectState;
|
||||
|
||||
private:
|
||||
// point and text offset conversion
|
||||
inline int32 _LineAt(float y);
|
||||
@@ -199,8 +209,6 @@ private:
|
||||
void _SelectLine(BPoint where, bool extend,
|
||||
bool useInitialSelection);
|
||||
|
||||
void _AutoScrollUpdate();
|
||||
|
||||
bool _CheckSelectedRegion(const TermPos& pos) const;
|
||||
bool _CheckSelectedRegion(int32 row,
|
||||
int32 firstColumn, int32& lastColumn) const;
|
||||
@@ -218,6 +226,8 @@ private:
|
||||
void _HandleInputMethodLocationRequest();
|
||||
void _CancelInputMethod();
|
||||
|
||||
void _NextState(State* state);
|
||||
|
||||
private:
|
||||
Listener* fListener;
|
||||
Shell* fShell;
|
||||
@@ -252,8 +262,6 @@ private:
|
||||
// Cursor position.
|
||||
TermPos fCursor;
|
||||
|
||||
int32 fMouseButtons;
|
||||
|
||||
// Terminal rows and columns.
|
||||
int fColumns;
|
||||
int fRows;
|
||||
@@ -293,18 +301,21 @@ private:
|
||||
TermPos fSelEnd;
|
||||
TermPos fInitialSelectionStart;
|
||||
TermPos fInitialSelectionEnd;
|
||||
bool fMouseTracking;
|
||||
bool fCheckMouseTracking;
|
||||
int fSelectGranularity;
|
||||
BPoint fLastClickPoint;
|
||||
|
||||
// mouse
|
||||
int32 fMouseButtons;
|
||||
TermPos fPrevPos;
|
||||
bool fReportX10MouseEvent;
|
||||
bool fReportNormalMouseEvent;
|
||||
bool fReportButtonMouseEvent;
|
||||
bool fReportAnyMouseEvent;
|
||||
BClipboard* fMouseClipboard;
|
||||
|
||||
// states
|
||||
DefaultState* fDefaultState;
|
||||
SelectState* fSelectState;
|
||||
State* fActiveState;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* Copyright 2001-2013, Haiku, Inc.
|
||||
* Copyright 2003-2004 Kian Duffy, [email protected]
|
||||
* Parts Copyright 1998-1999 Kazuho Okui and Takashi Murai.
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*
|
||||
* Authors:
|
||||
* Stefano Ceccherini, [email protected]
|
||||
* Kian Duffy, [email protected]
|
||||
* Y.Hayakawa, [email protected]
|
||||
* Ingo Weinhold, [email protected]
|
||||
* Clemens Zeidler, [email protected]
|
||||
* Siarzhuk Zharski, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "TermViewStates.h"
|
||||
|
||||
#include <MessageRunner.h>
|
||||
#include <ScrollBar.h>
|
||||
#include <UTF8.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "Shell.h"
|
||||
#include "TermConst.h"
|
||||
#include "VTkeymap.h"
|
||||
#include "VTKeyTbl.h"
|
||||
|
||||
|
||||
// selection granularity
|
||||
enum {
|
||||
SELECT_CHARS,
|
||||
SELECT_WORDS,
|
||||
SELECT_LINES
|
||||
};
|
||||
|
||||
static const uint32 kAutoScroll = 'AScr';
|
||||
|
||||
|
||||
// #pragma mark - State
|
||||
|
||||
|
||||
TermView::State::State(TermView* view)
|
||||
:
|
||||
fView(view)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
TermView::State::~State()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::Entered()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::Exited()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermView::State::MessageReceived(BMessage* message)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::KeyDown(const char* bytes, int32 numBytes)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::MouseDown(BPoint where, int32 buttons, int32 modifiers)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::MouseUp(BPoint where, int32 buttons)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - StandardBaseState
|
||||
|
||||
|
||||
TermView::StandardBaseState::StandardBaseState(TermView* view)
|
||||
:
|
||||
State(view)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermView::StandardBaseState::_StandardMouseMoved(BPoint where)
|
||||
{
|
||||
if (!fView->fReportAnyMouseEvent && !fView->fReportButtonMouseEvent)
|
||||
return false;
|
||||
|
||||
int32 modifier;
|
||||
fView->Window()->CurrentMessage()->FindInt32("modifiers", &modifier);
|
||||
|
||||
TermPos clickPos = fView->_ConvertToTerminal(where);
|
||||
|
||||
if (fView->fReportButtonMouseEvent) {
|
||||
if (fView->fPrevPos.x != clickPos.x
|
||||
|| fView->fPrevPos.y != clickPos.y) {
|
||||
fView->_SendMouseEvent(fView->fMouseButtons, modifier,
|
||||
clickPos.x, clickPos.y, true);
|
||||
}
|
||||
fView->fPrevPos = clickPos;
|
||||
} else {
|
||||
fView->_SendMouseEvent(fView->fMouseButtons, modifier, clickPos.x,
|
||||
clickPos.y, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - DefaultState
|
||||
|
||||
|
||||
TermView::DefaultState::DefaultState(TermView* view)
|
||||
:
|
||||
StandardBaseState(view)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::DefaultState::KeyDown(const char* bytes, int32 numBytes)
|
||||
{
|
||||
int32 key, mod, rawChar;
|
||||
BMessage *currentMessage = fView->Looper()->CurrentMessage();
|
||||
if (currentMessage == NULL)
|
||||
return;
|
||||
|
||||
currentMessage->FindInt32("modifiers", &mod);
|
||||
currentMessage->FindInt32("key", &key);
|
||||
currentMessage->FindInt32("raw_char", &rawChar);
|
||||
|
||||
fView->_ActivateCursor(true);
|
||||
|
||||
// handle multi-byte chars
|
||||
if (numBytes > 1) {
|
||||
if (fView->fEncoding != M_UTF8) {
|
||||
char destBuffer[16];
|
||||
int32 destLen = sizeof(destBuffer);
|
||||
int32 state = 0;
|
||||
convert_from_utf8(fView->fEncoding, bytes, &numBytes, destBuffer,
|
||||
&destLen, &state, '?');
|
||||
fView->_ScrollTo(0, true);
|
||||
fView->fShell->Write(destBuffer, destLen);
|
||||
return;
|
||||
}
|
||||
|
||||
fView->_ScrollTo(0, true);
|
||||
fView->fShell->Write(bytes, numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
// Terminal filters RET, ENTER, F1...F12, and ARROW key code.
|
||||
const char *toWrite = NULL;
|
||||
|
||||
switch (*bytes) {
|
||||
case B_RETURN:
|
||||
if (rawChar == B_RETURN)
|
||||
toWrite = "\r";
|
||||
break;
|
||||
|
||||
case B_DELETE:
|
||||
toWrite = DELETE_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_BACKSPACE:
|
||||
// Translate only the actual backspace key to the backspace
|
||||
// code. CTRL-H shall just be echoed.
|
||||
if (!((mod & B_CONTROL_KEY) && rawChar == 'h'))
|
||||
toWrite = BACKSPACE_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_LEFT_ARROW:
|
||||
if (rawChar == B_LEFT_ARROW) {
|
||||
if ((mod & B_SHIFT_KEY) != 0) {
|
||||
if (fView->fListener != NULL)
|
||||
fView->fListener->PreviousTermView(fView);
|
||||
return;
|
||||
}
|
||||
if ((mod & B_CONTROL_KEY) || (mod & B_COMMAND_KEY))
|
||||
toWrite = CTRL_LEFT_ARROW_KEY_CODE;
|
||||
else
|
||||
toWrite = LEFT_ARROW_KEY_CODE;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_RIGHT_ARROW:
|
||||
if (rawChar == B_RIGHT_ARROW) {
|
||||
if ((mod & B_SHIFT_KEY) != 0) {
|
||||
if (fView->fListener != NULL)
|
||||
fView->fListener->NextTermView(fView);
|
||||
return;
|
||||
}
|
||||
if ((mod & B_CONTROL_KEY) || (mod & B_COMMAND_KEY))
|
||||
toWrite = CTRL_RIGHT_ARROW_KEY_CODE;
|
||||
else
|
||||
toWrite = RIGHT_ARROW_KEY_CODE;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_UP_ARROW:
|
||||
if (mod & B_SHIFT_KEY) {
|
||||
fView->_ScrollTo(fView->fScrollOffset - fView->fFontHeight,
|
||||
true);
|
||||
return;
|
||||
}
|
||||
if (rawChar == B_UP_ARROW) {
|
||||
if (mod & B_CONTROL_KEY)
|
||||
toWrite = CTRL_UP_ARROW_KEY_CODE;
|
||||
else
|
||||
toWrite = UP_ARROW_KEY_CODE;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_DOWN_ARROW:
|
||||
if (mod & B_SHIFT_KEY) {
|
||||
fView->_ScrollTo(fView->fScrollOffset + fView->fFontHeight,
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rawChar == B_DOWN_ARROW) {
|
||||
if (mod & B_CONTROL_KEY)
|
||||
toWrite = CTRL_DOWN_ARROW_KEY_CODE;
|
||||
else
|
||||
toWrite = DOWN_ARROW_KEY_CODE;
|
||||
}
|
||||
break;
|
||||
|
||||
case B_INSERT:
|
||||
if (rawChar == B_INSERT)
|
||||
toWrite = INSERT_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_HOME:
|
||||
if (rawChar == B_HOME)
|
||||
toWrite = HOME_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_END:
|
||||
if (rawChar == B_END)
|
||||
toWrite = END_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_PAGE_UP:
|
||||
if (mod & B_SHIFT_KEY) {
|
||||
fView->_ScrollTo(
|
||||
fView->fScrollOffset - fView->fFontHeight * fView->fRows,
|
||||
true);
|
||||
return;
|
||||
}
|
||||
if (rawChar == B_PAGE_UP)
|
||||
toWrite = PAGE_UP_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_PAGE_DOWN:
|
||||
if (mod & B_SHIFT_KEY) {
|
||||
fView->_ScrollTo(
|
||||
fView->fScrollOffset + fView->fFontHeight * fView->fRows,
|
||||
true);
|
||||
return;
|
||||
}
|
||||
if (rawChar == B_PAGE_DOWN)
|
||||
toWrite = PAGE_DOWN_KEY_CODE;
|
||||
break;
|
||||
|
||||
case B_FUNCTION_KEY:
|
||||
for (int32 i = 0; i < 12; i++) {
|
||||
if (key == function_keycode_table[i]) {
|
||||
toWrite = function_key_char_table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If the above code proposed an alternative string to write, we get it's
|
||||
// length. Otherwise we write exactly the bytes passed to this method.
|
||||
size_t toWriteLen;
|
||||
if (toWrite != NULL) {
|
||||
toWriteLen = strlen(toWrite);
|
||||
} else {
|
||||
toWrite = bytes;
|
||||
toWriteLen = numBytes;
|
||||
}
|
||||
|
||||
fView->_ScrollTo(0, true);
|
||||
fView->fShell->Write(toWrite, toWriteLen);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::DefaultState::MouseDown(BPoint where, int32 buttons, int32 modifiers)
|
||||
{
|
||||
if (fView->fReportAnyMouseEvent || fView->fReportButtonMouseEvent
|
||||
|| fView->fReportNormalMouseEvent || fView->fReportX10MouseEvent) {
|
||||
TermPos clickPos = fView->_ConvertToTerminal(where);
|
||||
fView->_SendMouseEvent(buttons, modifiers, clickPos.x, clickPos.y,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
|
||||
// paste button
|
||||
if ((buttons & (B_SECONDARY_MOUSE_BUTTON | B_TERTIARY_MOUSE_BUTTON)) != 0) {
|
||||
fView->Paste(fView->fMouseClipboard);
|
||||
return;
|
||||
}
|
||||
|
||||
// Select Region
|
||||
if (buttons == B_PRIMARY_MOUSE_BUTTON) {
|
||||
fView->fSelectState->Prepare(where, modifiers);
|
||||
fView->_NextState(fView->fSelectState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::DefaultState::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message)
|
||||
{
|
||||
_StandardMouseMoved(where);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - SelectState
|
||||
|
||||
|
||||
TermView::SelectState::SelectState(TermView* view)
|
||||
:
|
||||
StandardBaseState(view),
|
||||
fSelectGranularity(SELECT_CHARS),
|
||||
fCheckMouseTracking(false),
|
||||
fMouseTracking(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::SelectState::Prepare(BPoint where, int32 modifiers)
|
||||
{
|
||||
int32 clicks;
|
||||
fView->Window()->CurrentMessage()->FindInt32("clicks", &clicks);
|
||||
|
||||
if (fView->_HasSelection()) {
|
||||
TermPos inPos = fView->_ConvertToTerminal(where);
|
||||
if (fView->_CheckSelectedRegion(inPos)) {
|
||||
if (modifiers & B_CONTROL_KEY) {
|
||||
BPoint p;
|
||||
uint32 bt;
|
||||
do {
|
||||
fView->GetMouse(&p, &bt);
|
||||
|
||||
if (bt == 0) {
|
||||
fView->_Deselect();
|
||||
return;
|
||||
}
|
||||
|
||||
snooze(40000);
|
||||
|
||||
} while (abs((int)(where.x - p.x)) < 4
|
||||
&& abs((int)(where.y - p.y)) < 4);
|
||||
|
||||
fView->InitiateDrag();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If mouse has moved too much, disable double/triple click.
|
||||
if (fView->_MouseDistanceSinceLastClick(where) > 8)
|
||||
clicks = 1;
|
||||
|
||||
fView->SetMouseEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS,
|
||||
B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
|
||||
|
||||
TermPos clickPos = fView->_ConvertToTerminal(where);
|
||||
|
||||
if (modifiers & B_SHIFT_KEY) {
|
||||
fView->fInitialSelectionStart = clickPos;
|
||||
fView->fInitialSelectionEnd = clickPos;
|
||||
fView->_ExtendSelection(fView->fInitialSelectionStart, true, false);
|
||||
} else {
|
||||
fView->_Deselect();
|
||||
fView->fInitialSelectionStart = clickPos;
|
||||
fView->fInitialSelectionEnd = clickPos;
|
||||
}
|
||||
|
||||
// If clicks larger than 3, reset mouse click counter.
|
||||
clicks = (clicks - 1) % 3 + 1;
|
||||
|
||||
switch (clicks) {
|
||||
case 1:
|
||||
fCheckMouseTracking = true;
|
||||
fSelectGranularity = SELECT_CHARS;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
fView->_SelectWord(where, (modifiers & B_SHIFT_KEY) != 0, false);
|
||||
fMouseTracking = true;
|
||||
fSelectGranularity = SELECT_WORDS;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
fView->_SelectLine(where, (modifiers & B_SHIFT_KEY) != 0, false);
|
||||
fMouseTracking = true;
|
||||
fSelectGranularity = SELECT_LINES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermView::SelectState::MessageReceived(BMessage* message)
|
||||
{
|
||||
if (message->what == kAutoScroll) {
|
||||
_AutoScrollUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::SelectState::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message)
|
||||
{
|
||||
if (_StandardMouseMoved(where))
|
||||
return;
|
||||
|
||||
if (fCheckMouseTracking) {
|
||||
if (fView->_MouseDistanceSinceLastClick(where) > 9)
|
||||
fMouseTracking = true;
|
||||
}
|
||||
if (!fMouseTracking)
|
||||
return;
|
||||
|
||||
bool doAutoScroll = false;
|
||||
|
||||
if (where.y < 0) {
|
||||
doAutoScroll = true;
|
||||
fView->fAutoScrollSpeed = where.y;
|
||||
where.x = 0;
|
||||
where.y = 0;
|
||||
}
|
||||
|
||||
BRect bounds(fView->Bounds());
|
||||
if (where.y > bounds.bottom) {
|
||||
doAutoScroll = true;
|
||||
fView->fAutoScrollSpeed = where.y - bounds.bottom;
|
||||
where.x = bounds.right;
|
||||
where.y = bounds.bottom;
|
||||
}
|
||||
|
||||
if (doAutoScroll) {
|
||||
if (fView->fAutoScrollRunner == NULL) {
|
||||
BMessage message(kAutoScroll);
|
||||
fView->fAutoScrollRunner = new (std::nothrow) BMessageRunner(
|
||||
BMessenger(fView), &message, 10000);
|
||||
}
|
||||
} else {
|
||||
delete fView->fAutoScrollRunner;
|
||||
fView->fAutoScrollRunner = NULL;
|
||||
}
|
||||
|
||||
switch (fSelectGranularity) {
|
||||
case SELECT_CHARS:
|
||||
{
|
||||
// If we just start selecting, we first select the initially
|
||||
// hit char, so that we get a proper initial selection -- the char
|
||||
// in question, which will thus always be selected, regardless of
|
||||
// whether selecting forward or backward.
|
||||
if (fView->fInitialSelectionStart == fView->fInitialSelectionEnd) {
|
||||
fView->_Select(fView->fInitialSelectionStart,
|
||||
fView->fInitialSelectionEnd, true, true);
|
||||
}
|
||||
|
||||
fView->_ExtendSelection(fView->_ConvertToTerminal(where), true,
|
||||
true);
|
||||
break;
|
||||
}
|
||||
case SELECT_WORDS:
|
||||
fView->_SelectWord(where, true, true);
|
||||
break;
|
||||
case SELECT_LINES:
|
||||
fView->_SelectLine(where, true, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::SelectState::MouseUp(BPoint where, int32 buttons)
|
||||
{
|
||||
fCheckMouseTracking = false;
|
||||
fMouseTracking = false;
|
||||
|
||||
if (fView->fAutoScrollRunner != NULL) {
|
||||
delete fView->fAutoScrollRunner;
|
||||
fView->fAutoScrollRunner = NULL;
|
||||
}
|
||||
|
||||
// When releasing the first mouse button, we copy the selected text to the
|
||||
// clipboard.
|
||||
|
||||
if (fView->fReportAnyMouseEvent || fView->fReportButtonMouseEvent
|
||||
|| fView->fReportNormalMouseEvent) {
|
||||
TermPos clickPos = fView->_ConvertToTerminal(where);
|
||||
fView->_SendMouseEvent(0, 0, clickPos.x, clickPos.y, false);
|
||||
} else {
|
||||
if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0
|
||||
&& (fView->fMouseButtons & B_PRIMARY_MOUSE_BUTTON) != 0) {
|
||||
fView->Copy(fView->fMouseClipboard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fView->_NextState(fView->fDefaultState);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::SelectState::_AutoScrollUpdate()
|
||||
{
|
||||
if (fMouseTracking && fView->fAutoScrollRunner != NULL
|
||||
&& fView->fScrollBar != NULL) {
|
||||
float value = fView->fScrollBar->Value();
|
||||
fView->_ScrollTo(value + fView->fAutoScrollSpeed, true);
|
||||
if (fView->fAutoScrollSpeed < 0) {
|
||||
fView->_ExtendSelection(
|
||||
fView->_ConvertToTerminal(BPoint(0, 0)), true, true);
|
||||
} else {
|
||||
fView->_ExtendSelection(
|
||||
fView->_ConvertToTerminal(fView->Bounds().RightBottom()), true,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2001-2013, Haiku, Inc.
|
||||
* Copyright (c) 2003-4 Kian Duffy <myob@users.sourceforge.net>
|
||||
* Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
|
||||
*
|
||||
* Distributed under the terms of the MIT license.
|
||||
* Authors:
|
||||
* Stefano Ceccherini, stefano.ceccherini@gmail.com
|
||||
* Kian Duffy, myob@users.sourceforge.net
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
* Siarzhuk Zharski, zharik@gmx.li
|
||||
*/
|
||||
#ifndef TERMVIEW_STATES_H
|
||||
#define TERMVIEW_STATES_H
|
||||
|
||||
|
||||
#include "TermView.h"
|
||||
|
||||
|
||||
class TermView::State {
|
||||
public:
|
||||
State(TermView* view);
|
||||
virtual ~State();
|
||||
|
||||
virtual void Entered();
|
||||
virtual void Exited();
|
||||
|
||||
virtual bool MessageReceived(BMessage* message);
|
||||
// returns true, if handled
|
||||
|
||||
virtual void KeyDown(const char* bytes, int32 numBytes);
|
||||
|
||||
virtual void MouseDown(BPoint where, int32 buttons,
|
||||
int32 modifiers);
|
||||
virtual void MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message);
|
||||
virtual void MouseUp(BPoint where, int32 buttons);
|
||||
|
||||
protected:
|
||||
TermView* fView;
|
||||
};
|
||||
|
||||
|
||||
class TermView::StandardBaseState : public TermView::State {
|
||||
public:
|
||||
StandardBaseState(TermView* view);
|
||||
|
||||
protected:
|
||||
bool _StandardMouseMoved(BPoint where);
|
||||
};
|
||||
|
||||
|
||||
class TermView::DefaultState : public TermView::StandardBaseState {
|
||||
public:
|
||||
DefaultState(TermView* view);
|
||||
|
||||
virtual void KeyDown(const char* bytes, int32 numBytes);
|
||||
|
||||
virtual void MouseDown(BPoint where, int32 buttons,
|
||||
int32 modifiers);
|
||||
virtual void MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message);
|
||||
};
|
||||
|
||||
|
||||
class TermView::SelectState : public TermView::StandardBaseState {
|
||||
public:
|
||||
SelectState(TermView* view);
|
||||
|
||||
void Prepare(BPoint where, int32 modifiers);
|
||||
|
||||
virtual bool MessageReceived(BMessage* message);
|
||||
|
||||
virtual void MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message);
|
||||
virtual void MouseUp(BPoint where, int32 buttons);
|
||||
|
||||
private:
|
||||
void _AutoScrollUpdate();
|
||||
|
||||
private:
|
||||
int32 fSelectGranularity;
|
||||
bool fCheckMouseTracking;
|
||||
bool fMouseTracking;
|
||||
};
|
||||
|
||||
|
||||
#endif // TERMVIEW_STATES_H
|
||||
Reference in New Issue
Block a user