hrev45662 adds 6 changesets to branch 'master' old head: 74ec65d8439c087c037adc141c418734d0b926d1 new head: 09cbc86d0d94f06f5bf65071fccddf83060ed10c overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=09cbc86+%5E74ec65d ---------------------------------------------------------------------------- a7000a0: Terminal: Add header VTKeyTbl.h ... exporting what is defined in VTKeyTbl.c. bda35ef: Terminal: Pull user input handling into state classes 314e8a2: BasicTerminalBuffer: Add {Previous,Next}LinePos() e9bad28: Terminal: Add a hyperlink mode When holding down Command, text under the mouse is checked whether it looks like a URL or a local path. If so, it is highlighted and can be clicked, which will open the URL/file. Right-clicking opens a context menu with items for opening the link/file or copying it to the clipboard. When additionally holding down Shift, path prefixes up to the component under the mouse will be considered (no effect for URLs). Changes: * Add HyperLink class. Encapsulates a type, the address, and an optional base address. Features an Open() method to open the address. * Move/add some string constants to TermConst. * Move TermView::CharClassifier to top level and rename to DefaultCharClassifier. * Introduce TermViewHighlight and TermViewHighlighter. The former refers to a range of text in a TermView's text buffer. It also contains a pointer to a TermViewHighlighter object, which specifies how the text range shall be rendered (colors and attributes). * TermView: - Add respective _{Add,Remove}Highlight() methods and adjust the code to support highlights. - Make the selection a TermViewHighlight. At least its visual aspect is now handled like other highlights. - Introduce an inner TextBufferSyncLocker. It is used instead of BAutolock when locking the text buffer to synchronize the visual buffer with it. After it unlocks it calls _VisibleTextBufferChanged(), if the visual text buffer has changed, which in turn calls a new callback on the active state. - Add WindowActivated() and ModifiersChanged() callbacks to the state interface. - Add new states HyperLinkState and HyperLinkMenuState which implement the new feature. Fix modifier issues 7e3f7da: BURL: Make the output to stderr DEBUG only 09cbc86: TTracker::RefsReceived(): Forward "be:*" message fields In some cases /bin/open puts some additional fields in the B_REFS_RECEIVED message, but those were completely ignored. This makes opening paths with appended line/column numbers actually work as expected. [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 19 files changed, 1891 insertions(+), 544 deletions(-) src/apps/terminal/BasicTerminalBuffer.cpp | 117 ++- src/apps/terminal/BasicTerminalBuffer.h | 13 + src/apps/terminal/HyperLink.cpp | 45 + src/apps/terminal/HyperLink.h | 41 + src/apps/terminal/Jamfile | 3 + src/apps/terminal/TermConst.cpp | 4 + src/apps/terminal/TermConst.h | 4 + src/apps/terminal/TermView.cpp | 752 ++++++---------- src/apps/terminal/TermView.h | 71 +- src/apps/terminal/TermViewHighlight.cpp | 19 + src/apps/terminal/TermViewHighlight.h | 83 ++ src/apps/terminal/TermViewStates.cpp | 1001 ++++++++++++++++++++++ src/apps/terminal/TermViewStates.h | 168 ++++ src/apps/terminal/TerminalCharClassifier.cpp | 43 +- src/apps/terminal/TerminalCharClassifier.h | 19 +- src/apps/terminal/VTKeyTbl.c | 4 + src/apps/terminal/VTKeyTbl.h | 13 + src/kits/support/Url.cpp | 4 + src/kits/tracker/Tracker.cpp | 31 +- ############################################################################ Commit: a7000a02a09b6bc2c310b85bb4257d817efcff49 URL: http://cgit.haiku-os.org/haiku/commit/?id=a7000a0 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Thu May 9 21:53:29 2013 UTC Terminal: Add header VTKeyTbl.h ... exporting what is defined in VTKeyTbl.c. ---------------------------------------------------------------------------- diff --git a/src/apps/terminal/VTKeyTbl.c b/src/apps/terminal/VTKeyTbl.c index 51e877a..ff5ada4 100644 --- a/src/apps/terminal/VTKeyTbl.c +++ b/src/apps/terminal/VTKeyTbl.c @@ -28,8 +28,12 @@ * */ + +#include "VTKeyTbl.h" + #include "VTkeymap.h" + int function_keycode_table[] = { F1_KEY, diff --git a/src/apps/terminal/VTKeyTbl.h b/src/apps/terminal/VTKeyTbl.h new file mode 100644 index 0000000..8f4c284 --- /dev/null +++ b/src/apps/terminal/VTKeyTbl.h @@ -0,0 +1,13 @@ +/* + * Copyright 2013 Ingo Weinhold, ingo_weinhold@xxxxxx. All rights reserved. + * Distributed under the terms of the MIT license. + */ +#ifndef VT_KEY_TABLE_H +#define VT_KEY_TABLE_H + + +extern int function_keycode_table[]; +extern char* function_key_char_table[]; + + +#endif // VT_KEY_TABLE_H ############################################################################ Commit: bda35ef5dc71c1c4a4e831233f937bcb130bd284 URL: http://cgit.haiku-os.org/haiku/commit/?id=bda35ef Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Thu May 9 21:55:49 2013 UTC Terminal: Pull user input handling into state classes ---------------------------------------------------------------------------- diff --git a/src/apps/terminal/Jamfile b/src/apps/terminal/Jamfile index c8a114e..7a480d9 100644 --- a/src/apps/terminal/Jamfile +++ b/src/apps/terminal/Jamfile @@ -33,6 +33,7 @@ Application Terminal : TermParse.cpp TermScrollView.cpp TermView.cpp + TermViewStates.cpp TermWindow.cpp TitlePlaceholderMapper.cpp VTKeyTbl.c diff --git a/src/apps/terminal/TermView.cpp b/src/apps/terminal/TermView.cpp index 89faf10..b448017 100644 --- a/src/apps/terminal/TermView.cpp +++ b/src/apps/terminal/TermView.cpp @@ -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); - - 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; + BMessage* currentMessage = Window()->CurrentMessage(); + int32 buttons = currentMessage->GetInt32("buttons", 0); + int32 modifiers = currentMessage->GetInt32("modifiers", 0); - switch (clicks) { - case 1: - fCheckMouseTracking = true; - fSelectGranularity = SELECT_CHARS; - break; + fActiveState->MouseDown(where, buttons, modifiers); - 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; - } - } + fMouseButtons = buttons; 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; - - if (fAutoScrollRunner != NULL) { - delete fAutoScrollRunner; - fAutoScrollRunner = NULL; - } + int32 buttons = Window()->CurrentMessage()->GetInt32("buttons", 0); - // When releasing the first mouse button, we copy the selected text to the - // clipboard. - int32 buttons; - Window()->CurrentMessage()->FindInt32("buttons", &buttons); + fActiveState->MouseUp(where, 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 diff --git a/src/apps/terminal/TermView.h b/src/apps/terminal/TermView.h index 9e07215..429097d 100644 --- a/src/apps/terminal/TermView.h +++ b/src/apps/terminal/TermView.h @@ -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; }; diff --git a/src/apps/terminal/TermViewStates.cpp b/src/apps/terminal/TermViewStates.cpp new file mode 100644 index 0000000..e74194d --- /dev/null +++ b/src/apps/terminal/TermViewStates.cpp @@ -0,0 +1,563 @@ +/* + * Copyright 2001-2013, Haiku, Inc. + * Copyright 2003-2004 Kian Duffy, myob@xxxxxxxxxxxxxxxxxxxxx + * Parts Copyright 1998-1999 Kazuho Okui and Takashi Murai. + * All rights reserved. Distributed under the terms of the MIT license. + * + * Authors: + * Stefano Ceccherini, stefano.ceccherini@xxxxxxxxx + * Kian Duffy, myob@xxxxxxxxxxxxxxxxxxxxx + * Y.Hayakawa, hida@xxxxxxxxxxxxxxxxxxxxxxxx + * Ingo Weinhold, ingo_weinhold@xxxxxx + * Clemens Zeidler, haiku@xxxxxxxxxxxxxxxxxx + * Siarzhuk Zharski, zharik@xxxxxx + */ + + +#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); + } + } +} diff --git a/src/apps/terminal/TermViewStates.h b/src/apps/terminal/TermViewStates.h new file mode 100644 index 0000000..26ff862 --- /dev/null +++ b/src/apps/terminal/TermViewStates.h @@ -0,0 +1,88 @@ +/* + * Copyright 2001-2013, Haiku, Inc. + * Copyright (c) 2003-4 Kian Duffy <myob@xxxxxxxxxxxxxxxxxxxxx> + * Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai. + * + * Distributed under the terms of the MIT license. + * Authors: + * Stefano Ceccherini, stefano.ceccherini@xxxxxxxxx + * Kian Duffy, myob@xxxxxxxxxxxxxxxxxxxxx + * Ingo Weinhold, ingo_weinhold@xxxxxx + * Siarzhuk Zharski, zharik@xxxxxx + */ +#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 ############################################################################ Commit: 314e8a20c6828d67b6f4c2362710c0a9ae27bc08 URL: http://cgit.haiku-os.org/haiku/commit/?id=314e8a2 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Fri May 10 23:03:39 2013 UTC BasicTerminalBuffer: Add {Previous,Next}LinePos() ---------------------------------------------------------------------------- diff --git a/src/apps/terminal/BasicTerminalBuffer.cpp b/src/apps/terminal/BasicTerminalBuffer.cpp index f5bcd61..dfbb54e 100644 --- a/src/apps/terminal/BasicTerminalBuffer.cpp +++ b/src/apps/terminal/BasicTerminalBuffer.cpp @@ -431,48 +431,31 @@ BasicTerminalBuffer::FindWord(const TermPos& pos, TermPos start(x, y); TermPos end(x + (IS_WIDTH(line->cells[x].attributes) ? FULL_WIDTH : HALF_WIDTH), y); - while (true) { - if (--x < 0) { - // Hit the beginning of the line -- continue at the end of the - // previous line, if it soft-breaks. - y--; - if ((line = _HistoryLineAt(y, lineBuffer)) == NULL - || !line->softBreak || line->length == 0) { - break; - } - x = line->length - 1; - } - if (x > 0 && IS_WIDTH(line->cells[x - 1].attributes)) - x--; - - if (classifier->Classify(line->cells[x].character) != type) + for (;;) { + TermPos previousPos = start; + if (!_PreviousLinePos(lineBuffer, line, previousPos) + || classifier->Classify(line->cells[previousPos.x].character) + != type) { break; + } - start.SetTo(x, y); + start = previousPos; } // find the end - x = end.x; - y = end.y; - line = _HistoryLineAt(y, lineBuffer); + line = _HistoryLineAt(end.y, lineBuffer); - while (true) { - if (x >= line->length) { - // Hit the end of the line -- if it soft-breaks continue with the - // next line. - if (!line->softBreak) - break; - y++; - x = 0; - if ((line = _HistoryLineAt(y, lineBuffer)) == NULL) - break; - } + for (;;) { + TermPos nextPos = end; + if (!_NormalizeLinePos(lineBuffer, line, nextPos)) + break; - if (classifier->Classify(line->cells[x].character) != type) + if (classifier->Classify(line->cells[nextPos.x].character) != type) break; - x += IS_WIDTH(line->cells[x].attributes) ? FULL_WIDTH : HALF_WIDTH; - end.SetTo(x, y); + nextPos.x += IS_WIDTH(line->cells[nextPos.x].attributes) + ? FULL_WIDTH : HALF_WIDTH; + end = nextPos; } _start = start; @@ -481,6 +464,34 @@ BasicTerminalBuffer::FindWord(const TermPos& pos, } +bool +BasicTerminalBuffer::PreviousLinePos(TermPos& pos) const +{ + TerminalLine* lineBuffer = ALLOC_LINE_ON_STACK(fWidth); + TerminalLine* line = _HistoryLineAt(pos.y, lineBuffer); + if (line == NULL || pos.x < 0 || pos.x >= fWidth) + return false; + + return _PreviousLinePos(lineBuffer, line, pos); +} + + +bool +BasicTerminalBuffer::NextLinePos(TermPos& pos, bool normalize) const +{ + TerminalLine* lineBuffer = ALLOC_LINE_ON_STACK(fWidth); + TerminalLine* line = _HistoryLineAt(pos.y, lineBuffer); + if (line == NULL || pos.x < 0 || pos.x > fWidth) + return false; + + if (!_NormalizeLinePos(lineBuffer, line, pos)) + return false; + + pos.x += IS_WIDTH(line->cells[pos.x].attributes) ? FULL_WIDTH : HALF_WIDTH; + return !normalize || _NormalizeLinePos(lineBuffer, line, pos); +} + + int32 BasicTerminalBuffer::LineLength(int32 index) const { @@ -1692,6 +1703,46 @@ BasicTerminalBuffer::_NextChar(TermPos& pos, UTF8Char& c) const } +bool +BasicTerminalBuffer::_PreviousLinePos(TerminalLine* lineBuffer, + TerminalLine*& line, TermPos& pos) const +{ + if (--pos.x < 0) { + // Hit the beginning of the line -- continue at the end of the + // previous line, if it soft-breaks. + pos.y--; + if ((line = _HistoryLineAt(pos.y, lineBuffer)) == NULL + || !line->softBreak || line->length == 0) { + return false; + } + pos.x = line->length - 1; + } + if (pos.x > 0 && IS_WIDTH(line->cells[pos.x - 1].attributes)) + pos.x--; + + return true; +} + + +bool +BasicTerminalBuffer::_NormalizeLinePos(TerminalLine* lineBuffer, + TerminalLine*& line, TermPos& pos) const +{ + if (pos.x < line->length) + return true; + + // Hit the end of the line -- if it soft-breaks continue with the + // next line. + if (!line->softBreak) + return false; + + pos.y++; + pos.x = 0; + line = _HistoryLineAt(pos.y, lineBuffer); + return line != NULL; +} + + #ifdef USE_DEBUG_SNAPSHOTS void diff --git a/src/apps/terminal/BasicTerminalBuffer.h b/src/apps/terminal/BasicTerminalBuffer.h index ed30bb0..757984f 100644 --- a/src/apps/terminal/BasicTerminalBuffer.h +++ b/src/apps/terminal/BasicTerminalBuffer.h @@ -107,6 +107,14 @@ public: int32 LineLength(int32 index) const; int32 GetLineColor(int32 index) const; + bool PreviousLinePos(TermPos& pos) const; + bool NextLinePos(TermPos& pos, bool normalize) const; + // normalize specifies that the returned + // position must be a valid position, i.e. + // actually point to a character (as opposed + // to just pointing to the position after a + // character). + bool Find(const char* pattern, const TermPos& start, bool forward, bool caseSensitive, bool matchWord, TermPos& matchStart, @@ -205,6 +213,11 @@ protected: bool _PreviousChar(TermPos& pos, UTF8Char& c) const; bool _NextChar(TermPos& pos, UTF8Char& c) const; + bool _PreviousLinePos(TerminalLine* lineBuffer, + TerminalLine*& line, TermPos& pos) const; + bool _NormalizeLinePos(TerminalLine* lineBuffer, + TerminalLine*& line, TermPos& pos) const; + protected: // screen width/height int32 fWidth; ############################################################################ Commit: e9bad28aafc6b71378bb71139cde6269bbb0afa7 URL: http://cgit.haiku-os.org/haiku/commit/?id=e9bad28 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Fri May 10 23:09:17 2013 UTC Terminal: Add a hyperlink mode When holding down Command, text under the mouse is checked whether it looks like a URL or a local path. If so, it is highlighted and can be clicked, which will open the URL/file. Right-clicking opens a context menu with items for opening the link/file or copying it to the clipboard. When additionally holding down Shift, path prefixes up to the component under the mouse will be considered (no effect for URLs). Changes: * Add HyperLink class. Encapsulates a type, the address, and an optional base address. Features an Open() method to open the address. * Move/add some string constants to TermConst. * Move TermView::CharClassifier to top level and rename to DefaultCharClassifier. * Introduce TermViewHighlight and TermViewHighlighter. The former refers to a range of text in a TermView's text buffer. It also contains a pointer to a TermViewHighlighter object, which specifies how the text range shall be rendered (colors and attributes). * TermView: - Add respective _{Add,Remove}Highlight() methods and adjust the code to support highlights. - Make the selection a TermViewHighlight. At least its visual aspect is now handled like other highlights. - Introduce an inner TextBufferSyncLocker. It is used instead of BAutolock when locking the text buffer to synchronize the visual buffer with it. After it unlocks it calls _VisibleTextBufferChanged(), if the visual text buffer has changed, which in turn calls a new callback on the active state. - Add WindowActivated() and ModifiersChanged() callbacks to the state interface. - Add new states HyperLinkState and HyperLinkMenuState which implement the new feature. Fix modifier issues ---------------------------------------------------------------------------- diff --git a/src/apps/terminal/HyperLink.cpp b/src/apps/terminal/HyperLink.cpp new file mode 100644 index 0000000..97e294f --- /dev/null +++ b/src/apps/terminal/HyperLink.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include "HyperLink.h" + +#include <errno.h> +#include <stdlib.h> + +#include "TermConst.h" + + +HyperLink::HyperLink() + : + fAddress(), + fType(TYPE_URL) +{ +} + + +HyperLink::HyperLink(const BString& address, Type type, + const BString& baseAddress) + : + fAddress(address), + fBaseAddress(baseAddress.IsEmpty() ? address : baseAddress), + fType(type) +{ +} + + +status_t +HyperLink::Open() +{ + if (!IsValid()) + return B_BAD_VALUE; + + // open with the "open" program + BString address(fAddress); + address.CharacterEscape(kShellEscapeCharacters, '\\'); + BString commandLine; + commandLine.SetToFormat("/bin/open %s", address.String()); + return system(commandLine) == 0 ? B_OK : errno; +} diff --git a/src/apps/terminal/HyperLink.h b/src/apps/terminal/HyperLink.h new file mode 100644 index 0000000..735c84c --- /dev/null +++ b/src/apps/terminal/HyperLink.h @@ -0,0 +1,41 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * Distributed under the terms of the MIT License. + */ +#ifndef HYPER_LINK_H +#define HYPER_LINK_H + + +#include <String.h> + + +class HyperLink { +public: + enum Type { + TYPE_URL, + TYPE_PATH, + TYPE_PATH_WITH_LINE, + TYPE_PATH_WITH_LINE_AND_COLUMN + }; + +public: + HyperLink(); + HyperLink(const BString& address, Type type, + const BString& baseAddress = BString()); + + bool IsValid() const { return !fAddress.IsEmpty(); } + + const BString& Address() const { return fAddress; } + const BString& BaseAddress() const { return fBaseAddress; } + Type GetType() const { return fType; } + + status_t Open(); + +private: + BString fAddress; + BString fBaseAddress; + Type fType; +}; + + +#endif // HYPER_LINK_H diff --git a/src/apps/terminal/Jamfile b/src/apps/terminal/Jamfile index 7a480d9..98a062a 100644 --- a/src/apps/terminal/Jamfile +++ b/src/apps/terminal/Jamfile @@ -16,6 +16,7 @@ Application Terminal : FindWindow.cpp Globals.cpp HistoryBuffer.cpp + HyperLink.cpp InlineInput.cpp PatternEvaluator.cpp PrefHandler.cpp @@ -33,6 +34,7 @@ Application Terminal : TermParse.cpp TermScrollView.cpp TermView.cpp + TermViewHighlight.cpp TermViewStates.cpp TermWindow.cpp TitlePlaceholderMapper.cpp diff --git a/src/apps/terminal/TermConst.cpp b/src/apps/terminal/TermConst.cpp index 1b88bc0..2e2c9cd 100644 --- a/src/apps/terminal/TermConst.cpp +++ b/src/apps/terminal/TermConst.cpp @@ -31,3 +31,7 @@ const char* const kTooTipSetWindowTitlePlaceholders = B_TRANSLATE( "\t%p\t-\tThe name of the active process in the current tab.\n" "\t%t\t-\tThe title of the current tab.\n" "\t%%\t-\tThe character '%'."); + +const char* const kShellEscapeCharacters = " ~`#$&*()\\|[]{};'\"<>?!"; +const char* const kDefaultAdditionalWordCharacters = ":@-./_~"; +const char* const kURLAdditionalWordCharacters = ":/-._~[]?#@!$&'()*+,;="; diff --git a/src/apps/terminal/TermConst.h b/src/apps/terminal/TermConst.h index 942d921..9d1c942 100644 --- a/src/apps/terminal/TermConst.h +++ b/src/apps/terminal/TermConst.h @@ -145,6 +145,10 @@ static const char* const PREF_WINDOW_TITLE = "Window title"; extern const char* const kTooTipSetTabTitlePlaceholders; extern const char* const kTooTipSetWindowTitlePlaceholders; +extern const char* const kShellEscapeCharacters; +extern const char* const kDefaultAdditionalWordCharacters; +extern const char* const kURLAdditionalWordCharacters; + // Cursor style enum { diff --git a/src/apps/terminal/TermView.cpp b/src/apps/terminal/TermView.cpp index b448017..16590e5 100644 --- a/src/apps/terminal/TermView.cpp +++ b/src/apps/terminal/TermView.cpp @@ -16,7 +16,6 @@ #include "TermView.h" -#include <ctype.h> #include <signal.h> #include <stdlib.h> #include <string.h> @@ -102,9 +101,6 @@ static const bigtime_t kCursorBlinkInterval = 500000; static const rgb_color kBlackColor = { 0, 0, 0, 255 }; static const rgb_color kWhiteColor = { 255, 255, 255, 255 }; -static const char* kDefaultSpecialWordChars = ":@-./_~"; -static const char* kEscapeCharacters = " ~`#$&*()\\|[]{};'\"<>?!"; - // secondary mouse button drop const int32 kSecondaryMouseDropAction = 'SMDA'; @@ -125,40 +121,28 @@ restrict_value(const Type& value, const Type& min, const Type& max) } -// #pragma mark - CharClassifier +// #pragma mark - TextBufferSyncLocker -class TermView::CharClassifier : public TerminalCharClassifier { +class TermView::TextBufferSyncLocker { public: - CharClassifier(const char* specialWordChars) + TextBufferSyncLocker(TermView* view) + : + fView(view) { - const char* p = specialWordChars; - while (p != NULL && *p) { - int count = UTF8Char::ByteCount(*p); - if (count <= 0 || count > 4) - break; - fSpecialWordChars.push_back(UTF8Char(p, count)); - p += count; - } + fView->fTextBuffer->Lock(); } - virtual int Classify(const UTF8Char& character) + ~TextBufferSyncLocker() { - if (character.IsSpace()) - return CHAR_TYPE_SPACE; - - if (character.IsAlNum()) - return CHAR_TYPE_WORD_CHAR; - - if (std::find(fSpecialWordChars.begin(), fSpecialWordChars.end(), - character) != fSpecialWordChars.end()) - return CHAR_TYPE_WORD_CHAR; + fView->fTextBuffer->Unlock(); - return CHAR_TYPE_WORD_DELIMITER; + if (fView->fVisibleTextBufferChanged) + fView->_VisibleTextBufferChanged(); } private: - std::vector<UTF8Char> fSpecialWordChars; + TermView* fView; }; @@ -299,6 +283,7 @@ TermView::_InitObject(const ShellParameters& shellParameters) fCursor = TermPos(0, 0); fTextBuffer = NULL; fVisibleTextBuffer = NULL; + fVisibleTextBufferChanged = false; fScrollBar = NULL; fInline = NULL; fSelectForeColor = kWhiteColor; @@ -308,8 +293,8 @@ TermView::_InitObject(const ShellParameters& shellParameters) fScrolledSinceLastSync = 0; fSyncRunner = NULL; fConsiderClockedSync = false; - fSelStart = TermPos(-1, -1); - fSelEnd = TermPos(-1, -1); + fSelection.SetHighlighter(this); + fSelection.SetRange(TermPos(0, 0), TermPos(0, 0)); fPrevPos = TermPos(-1, - 1); fReportX10MouseEvent = false; fReportNormalMouseEvent = false; @@ -318,6 +303,8 @@ TermView::_InitObject(const ShellParameters& shellParameters) fMouseClipboard = be_clipboard; fDefaultState = new(std::nothrow) DefaultState(this); fSelectState = new(std::nothrow) SelectState(this); + fHyperLinkState = new(std::nothrow) HyperLinkState(this); + fHyperLinkMenuState = new(std::nothrow) HyperLinkMenuState(this); fActiveState = NULL; fTextBuffer = new(std::nothrow) TerminalBuffer; @@ -329,8 +316,8 @@ TermView::_InitObject(const ShellParameters& shellParameters) return B_NO_MEMORY; // TODO: Make the special word chars user-settable! - fCharClassifier = new(std::nothrow) CharClassifier( - kDefaultSpecialWordChars); + fCharClassifier = new(std::nothrow) DefaultCharClassifier( + kDefaultAdditionalWordCharacters); if (fCharClassifier == NULL) return B_NO_MEMORY; @@ -362,8 +349,12 @@ TermView::_InitObject(const ShellParameters& shellParameters) if (error < B_OK) return error; - if (fDefaultState == NULL || fSelectState == NULL) + fHighlights.AddItem(&fSelection); + + if (fDefaultState == NULL || fSelectState == NULL || fHyperLinkState == NULL + || fHyperLinkMenuState == NULL) { return B_NO_MEMORY; + } SetLowColor(fTextBackColor); SetViewColor(B_TRANSPARENT_32_BIT); @@ -383,6 +374,8 @@ TermView::~TermView() delete fDefaultState; delete fSelectState; + delete fHyperLinkState; + delete fHyperLinkMenuState; delete fSyncRunner; delete fAutoScrollRunner; delete fCharClassifier; @@ -457,6 +450,20 @@ TermView::Archive(BMessage* data, bool deep) const } +rgb_color +TermView::ForegroundColor() +{ + return fSelectForeColor; +} + + +rgb_color +TermView::BackgroundColor() +{ + return fSelectBackColor; +} + + inline int32 TermView::_LineAt(float y) { @@ -582,12 +589,13 @@ TermView::SetTermSize(int rows, int columns, bool notifyShell) // synchronize the visible text buffer { - BAutolock _(fTextBuffer); + TextBufferSyncLocker _(this); _SynchronizeWithTextBuffer(0, -1); int32 offset = _LineAt(0); fVisibleTextBuffer->SynchronizeWith(fTextBuffer, offset, offset, offset + rows + 2); + fVisibleTextBufferChanged = true; } if (notifyShell) @@ -784,7 +792,8 @@ TermView::Copy(BClipboard *clipboard) return; BString copyStr; - fTextBuffer->GetStringFromRegion(copyStr, fSelStart, fSelEnd); + fTextBuffer->GetStringFromRegion(copyStr, fSelection.Start(), + fSelection.End()); if (clipboard->Lock()) { BMessage *clipMsg = NULL; @@ -934,8 +943,11 @@ TermView::_Deactivate() //! Draw part of a line in the given view. void TermView::_DrawLinePart(int32 x1, int32 y1, uint32 attr, char *buf, - int32 width, bool mouse, bool cursor, BView *inView) + int32 width, Highlight* highlight, bool cursor, BView *inView) { + if (highlight != NULL) + attr = highlight->Highlighter()->AdjustTextAttributes(attr); + inView->SetFont(IS_BOLD(attr) && !fEmulateBold ? &fBoldFont : &fHalfFont); // Set pen point @@ -958,9 +970,9 @@ TermView::_DrawLinePart(int32 x1, int32 y1, uint32 attr, char *buf, if (cursor) { rgb_fore = fCursorForeColor; rgb_back = fCursorBackColor; - } else if (mouse) { - rgb_fore = fSelectForeColor; - rgb_back = fSelectBackColor; + } else if (highlight != NULL) { + rgb_fore = highlight->Highlighter()->ForegroundColor(); + rgb_back = highlight->Highlighter()->BackgroundColor(); } else { // Reverse attribute(If selected area, don't reverse color). if (IS_INVERSE(attr)) { @@ -1025,7 +1037,7 @@ TermView::_DrawCursor() } } - bool selected = _CheckSelectedRegion(TermPos(fCursor.x, fCursor.y)); + Highlight* highlight = _CheckHighlightRegion(TermPos(fCursor.x, fCursor.y)); if (fVisibleTextBuffer->GetChar(fCursor.y - firstVisible, fCursor.x, character, attr) == A_CHAR && (fCursorStyle == BLOCK_CURSOR || !cursorVisible)) { @@ -1037,11 +1049,11 @@ TermView::_DrawCursor() buffer[bytes] = '\0'; _DrawLinePart(fCursor.x * fFontWidth, (int32)rect.top, attr, buffer, - width, selected, cursorVisible, this); + width, highlight, cursorVisible, this); } else { - if (selected) - SetHighColor(fSelectBackColor); - else if (cursorVisible ) + if (highlight != NULL) + SetHighColor(highlight->Highlighter()->BackgroundColor()); + else if (cursorVisible) SetHighColor(fCursorBackColor ); else { uint32 count = 0; @@ -1144,6 +1156,7 @@ void TermView::AttachedToWindow() { fMouseButtons = 0; + fModifiers = modifiers(); // update the terminal size because it may have changed while the TermView // was detached from the window. On such conditions FrameResized was not @@ -1162,7 +1175,7 @@ TermView::AttachedToWindow() &message, 500000); { - BAutolock _(fTextBuffer); + TextBufferSyncLocker _(this); fTextBuffer->SetListener(thisMessenger); _SynchronizeWithTextBuffer(0, -1); } @@ -1240,15 +1253,15 @@ TermView::Draw(BRect updateRect) for (int32 i = k; i <= x2;) { int32 lastColumn = x2; - bool insideSelection = _CheckSelectedRegion(j, i, lastColumn); + Highlight* highlight = _CheckHighlightRegion(j, i, lastColumn); // This will clip lastColumn to the selection start or end // to ensure the selection is not drawn at the same time as // something else int32 count = fVisibleTextBuffer->GetString(j - firstVisible, i, lastColumn, buf, attr); -// debug_printf(" fVisibleTextBuffer->GetString(%ld, %ld, %ld) -> (%ld, \"%.*s\"), selected: %d\n", -// j - firstVisible, i, lastColumn, count, (int)count, buf, insideSelection); +// debug_printf(" fVisibleTextBuffer->GetString(%ld, %ld, %ld) -> (%ld, \"%.*s\"), highlight: %p\n", +// j - firstVisible, i, lastColumn, count, (int)count, buf, highlight); if (count == 0) { // No chars to draw : we just fill the rectangle with the @@ -1258,8 +1271,9 @@ TermView::Draw(BRect updateRect) fFontWidth * nextColumn - 1, 0); rect.bottom = rect.top + fFontHeight - 1; - rgb_color rgb_back = insideSelection - ? fSelectBackColor : fTextBackColor; + rgb_color rgb_back = highlight != NULL + ? highlight->Highlighter()->BackgroundColor() + : fTextBackColor; if (fTextBuffer->IsAlternateScreenActive()) { // alternate screen uses cell attributes @@ -1294,7 +1308,7 @@ TermView::Draw(BRect updateRect) count = FULL_WIDTH; _DrawLinePart(fFontWidth * i, (int32)_LineOffset(j), - attr, buf, count, insideSelection, false, this); + attr, buf, count, highlight, false, this); i += count; } } @@ -1365,6 +1379,15 @@ TermView::WindowActivated(bool active) if (fActive) _Deactivate(); } + + fActiveState->WindowActivated(active); + + if (active) { + int32 oldModifiers = fModifiers; + fModifiers = modifiers(); + if (fModifiers != oldModifiers) + fActiveState->ModifiersChanged(oldModifiers, fModifiers); + } } @@ -1576,6 +1599,15 @@ TermView::MessageReceived(BMessage *msg) break; } + case B_MODIFIERS_CHANGED: + { + int32 oldModifiers = fModifiers; + fModifiers = msg->GetInt32("modifiers", 0); + if (fModifiers != oldModifiers) + fActiveState->ModifiersChanged(oldModifiers, fModifiers); + break; + } + case B_INPUT_METHOD_EVENT: { int32 opcode; @@ -1677,7 +1709,7 @@ TermView::MessageReceived(BMessage *msg) break; case MSG_TERMINAL_BUFFER_CHANGED: { - BAutolock _(fTextBuffer); + TextBufferSyncLocker _(this); _SynchronizeWithTextBuffer(0, -1); break; } @@ -1842,7 +1874,7 @@ TermView::ScrollTo(BPoint where) //debug_printf("fVisibleTextBuffer->ScrollBy(%ld)\n", newFirstLine - oldFirstLine); fVisibleTextBuffer->ScrollBy(newFirstLine - oldFirstLine); } - BAutolock _(fTextBuffer); + TextBufferSyncLocker _(this); if (diff < 0) _SynchronizeWithTextBuffer(newFirstLine, oldFirstLine - 1); else @@ -2004,11 +2036,11 @@ TermView::_DoSecondaryMouseDropAction(BMessage* msg) int32 slash = string.FindLast("/"); string.Truncate(slash); } - string.CharacterEscape(kEscapeCharacters, '\\'); + string.CharacterEscape(kShellEscapeCharacters, '\\'); itemString += string; break; } - string.CharacterEscape(kEscapeCharacters, '\\'); + string.CharacterEscape(kShellEscapeCharacters, '\\'); itemString += string; } @@ -2035,7 +2067,7 @@ TermView::_DoFileDrop(entry_ref& ref) BPath path(&ent); BString string(path.Path()); - string.CharacterEscape(kEscapeCharacters, '\\'); + string.CharacterEscape(kShellEscapeCharacters, '\\'); _WritePTY(string.String(), string.Length()); } @@ -2099,7 +2131,9 @@ TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop, // sync time not passed yet -- keep counting fScrolledSinceLastSync += linesScrolled; return; - } else if (fScrolledSinceLastSync + linesScrolled <= fRows) { + } + + if (fScrolledSinceLastSync + linesScrolled <= fRows) { // time's up, but not enough happened delete fSyncRunner; fSyncRunner = NULL; @@ -2111,6 +2145,8 @@ TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop, fScrolledSinceLastSync = 0; } + fVisibleTextBufferChanged = true; + // Simple case first -- complete invalidation. if (info.invalidateAll) { Invalidate(); @@ -2194,15 +2230,23 @@ TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop, fVisibleTextBuffer->ScrollBy(linesScrolled); } - // move selection - if (fSelStart != fSelEnd) { - fSelStart.y -= linesScrolled; - fSelEnd.y -= linesScrolled; - fInitialSelectionStart.y -= linesScrolled; - fInitialSelectionEnd.y -= linesScrolled; + // move highlights + for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { + if (highlight->IsEmpty()) + continue; + + highlight->ScrollRange(linesScrolled); + if (highlight == &fSelection) { + fInitialSelectionStart.y -= linesScrolled; + fInitialSelectionEnd.y -= linesScrolled; + } - if (fSelStart.y < -historySize) - _Deselect(); + if (highlight->Start().y < -historySize) { + if (highlight == &fSelection) + _Deselect(); + else + _ClearHighlight(highlight); + } } } @@ -2212,12 +2256,13 @@ TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop, info.dirtyBottom); // clear the selection, if affected - if (fSelStart != fSelEnd) { + if (!fSelection.IsEmpty()) { // TODO: We're clearing the selection more often than necessary -- // to avoid that, we'd also need to track the x coordinates of the // dirty range. - int32 selectionBottom = fSelEnd.x > 0 ? fSelEnd.y : fSelEnd.y - 1; - if (fSelStart.y <= info.dirtyBottom + int32 selectionBottom = fSelection.End().x > 0 + ? fSelection.End().y : fSelection.End().y - 1; + if (fSelection.Start().y <= info.dirtyBottom && info.dirtyTop <= selectionBottom) { _Deselect(); } @@ -2247,6 +2292,17 @@ TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop, } +void +TermView::_VisibleTextBufferChanged() +{ + if (!fVisibleTextBufferChanged) + return; + + fVisibleTextBufferChanged = false; + fActiveState->VisibleTextBufferChanged(); +} + + /*! Write strings to PTY device. If encoding system isn't UTF8, change encoding to UTF8 before writing PTY. */ @@ -2322,9 +2378,8 @@ TermView::MouseDown(BPoint where) BMessage* currentMessage = Window()->CurrentMessage(); int32 buttons = currentMessage->GetInt32("buttons", 0); - int32 modifiers = currentMessage->GetInt32("modifiers", 0); - fActiveState->MouseDown(where, buttons, modifiers); + fActiveState->MouseDown(where, buttons, fModifiers); fMouseButtons = buttons; fLastClickPoint = where; @@ -2334,7 +2389,7 @@ TermView::MouseDown(BPoint where) void TermView::MouseMoved(BPoint where, uint32 transit, const BMessage *message) { - fActiveState->MouseMoved(where, transit, message); + fActiveState->MouseMoved(where, transit, message, fModifiers); } @@ -2354,7 +2409,7 @@ void TermView::_Select(TermPos start, TermPos end, bool inclusive, bool setInitialSelection) { - BAutolock _(fTextBuffer); + TextBufferSyncLocker _(this); _SynchronizeWithTextBuffer(0, -1); @@ -2396,18 +2451,17 @@ TermView::_Select(TermPos start, TermPos end, bool inclusive, end.x = fColumns; } - if (fSelStart != fSelEnd) - _InvalidateTextRange(fSelStart, fSelEnd); + if (!fSelection.IsEmpty()) + _InvalidateTextRange(fSelection.Start(), fSelection.End()); - fSelStart = start; - fSelEnd = end; + fSelection.SetRange(start, end); if (setInitialSelection) { - fInitialSelectionStart = fSelStart; - fInitialSelectionEnd = fSelEnd; + fInitialSelectionStart = fSelection.Start(); + fInitialSelectionEnd = fSelection.End(); } - _InvalidateTextRange(fSelStart, fSelEnd); + _InvalidateTextRange(fSelection.Start(), fSelection.End()); } @@ -2419,8 +2473,8 @@ TermView::_ExtendSelection(TermPos pos, bool inclusive, if (!useInitialSelection && !_HasSelection()) return; - TermPos start = fSelStart; - TermPos end = fSelEnd; + TermPos start = fSelection.Start(); + TermPos end = fSelection.End(); if (useInitialSelection) { start = fInitialSelectionStart; @@ -2446,22 +2500,17 @@ void TermView::_Deselect() { //debug_printf("TermView::_Deselect(): has selection: %d\n", _HasSelection()); - if (!_HasSelection()) - return; - - _InvalidateTextRange(fSelStart, fSelEnd); - - fSelStart.SetTo(0, 0); - fSelEnd.SetTo(0, 0); - fInitialSelectionStart.SetTo(0, 0); - fInitialSelectionEnd.SetTo(0, 0); + if (_ClearHighlight(&fSelection)) { + fInitialSelectionStart.SetTo(0, 0); + fInitialSelectionEnd.SetTo(0, 0); + } } bool TermView::_HasSelection() const { - return fSelStart != fSelEnd; + return !fSelection.IsEmpty(); } @@ -2476,11 +2525,15 @@ TermView::_SelectWord(BPoint where, bool extend, bool useInitialSelection) return; if (extend) { - if (start < (useInitialSelection ? fInitialSelectionStart : fSelStart)) + if (start + < (useInitialSelection + ? fInitialSelectionStart : fSelection.Start())) { _ExtendSelection(start, false, useInitialSelection); - else if (end > (useInitialSelection ? fInitialSelectionEnd : fSelEnd)) + } else if (end + > (useInitialSelection + ? fInitialSelectionEnd : fSelection.End())) { _ExtendSelection(end, false, useInitialSelection); - else if (useInitialSelection) + } else if (useInitialSelection) _Select(start, end, false, false); } else _Select(start, end, false, !useInitialSelection); @@ -2494,47 +2547,101 @@ TermView::_SelectLine(BPoint where, bool extend, bool useInitialSelection) TermPos end = TermPos(0, start.y + 1); if (extend) { - if (start < (useInitialSelection ? fInitialSelectionStart : fSelStart)) + if (start + < (useInitialSelection + ? fInitialSelectionStart : fSelection.Start())) { _ExtendSelection(start, false, useInitialSelection); - else if (end > (useInitialSelection ? fInitialSelectionEnd : fSelEnd)) + } else if (end + > (useInitialSelection + ? fInitialSelectionEnd : fSelection.End())) { _ExtendSelection(end, false, useInitialSelection); - else if (useInitialSelection) + } else if (useInitialSelection) _Select(start, end, false, false); } else _Select(start, end, false, !useInitialSelection); } -bool -TermView::_CheckSelectedRegion(const TermPos &pos) const +void +TermView::_AddHighlight(Highlight* highlight) +{ + fHighlights.AddItem(highlight); + + if (!highlight->IsEmpty()) + _InvalidateTextRange(highlight->Start(), highlight->End()); +} + + +void +TermView::_RemoveHighlight(Highlight* highlight) { - return pos >= fSelStart && pos < fSelEnd; + fHighlights.RemoveItem(highlight); + + if (!highlight->IsEmpty()) + _InvalidateTextRange(highlight->Start(), highlight->End()); } bool -TermView::_CheckSelectedRegion(int32 row, int32 firstColumn, - int32& lastColumn) const +TermView::_ClearHighlight(Highlight* highlight) { - if (fSelStart == fSelEnd) + if (highlight->IsEmpty()) return false; - if (row == fSelStart.y && firstColumn < fSelStart.x - && lastColumn >= fSelStart.x) { - // region starts before the selection, but intersects with it - lastColumn = fSelStart.x - 1; - return false; + _InvalidateTextRange(highlight->Start(), highlight->End()); + + highlight->SetRange(TermPos(0, 0), TermPos(0, 0)); + return true; +} + + +TermView::Highlight* +TermView::_CheckHighlightRegion(const TermPos &pos) const +{ + for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { + if (highlight->RangeContains(pos)) + return highlight; } - if (row == fSelEnd.y && firstColumn < fSelEnd.x - && lastColumn >= fSelEnd.x) { - // region starts in the selection, but exceeds the end - lastColumn = fSelEnd.x - 1; - return true; + return NULL; +} + + +TermView::Highlight* +TermView::_CheckHighlightRegion(int32 row, int32 firstColumn, + int32& lastColumn) const +{ + Highlight* nextHighlight = NULL; + + for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { + if (highlight->IsEmpty()) + continue; + + if (row == highlight->Start().y && firstColumn < highlight->Start().x + && lastColumn >= highlight->Start().x) { + // region starts before the highlight, but intersects with it + if (nextHighlight == NULL + || highlight->Start().x < nextHighlight->Start().x) { + nextHighlight = highlight; + } + continue; + } + + if (row == highlight->End().y && firstColumn < highlight->End().x + && lastColumn >= highlight->End().x) { + // region starts in the highlight, but exceeds the end + lastColumn = highlight->End().x - 1; + return highlight; + } + + TermPos pos(firstColumn, row); + if (highlight->RangeContains(pos)) + return highlight; } - TermPos pos(firstColumn, row); - return pos >= fSelStart && pos < fSelEnd; + if (nextHighlight != NULL) + lastColumn = nextHighlight->Start().x - 1; + return NULL; } @@ -2560,15 +2667,15 @@ bool TermView::Find(const BString &str, bool forwardSearch, bool matchCase, bool matchWord) { - BAutolock _(fTextBuffer); + TextBufferSyncLocker _(this); _SynchronizeWithTextBuffer(0, -1); TermPos start; if (_HasSelection()) { if (forwardSearch) - start = fSelEnd; + start = fSelection.End(); else - start = fSelStart; + start = fSelection.Start(); } else { // search from the very beginning/end if (forwardSearch) @@ -2584,7 +2691,7 @@ TermView::Find(const BString &str, bool forwardSearch, bool matchCase, } _Select(matchStart, matchEnd, false, true); - _ScrollToRange(fSelStart, fSelEnd); + _ScrollToRange(fSelection.Start(), fSelection.End()); return true; } @@ -2596,7 +2703,7 @@ TermView::GetSelection(BString &str) { str.SetTo(""); BAutolock _(fTextBuffer); - fTextBuffer->GetStringFromRegion(str, fSelStart, fSelEnd); + fTextBuffer->GetStringFromRegion(str, fSelection.Start(), fSelection.End()); } @@ -2619,17 +2726,18 @@ TermView::InitiateDrag() BAutolock _(fTextBuffer); BString copyStr(""); - fTextBuffer->GetStringFromRegion(copyStr, fSelStart, fSelEnd); + fTextBuffer->GetStringFromRegion(copyStr, fSelection.Start(), + fSelection.End()); BMessage message(B_MIME_DATA); message.AddData("text/plain", B_MIME_TYPE, copyStr.String(), copyStr.Length()); - BPoint start = _ConvertFromTerminal(fSelStart); - BPoint end = _ConvertFromTerminal(fSelEnd); + BPoint start = _ConvertFromTerminal(fSelection.Start()); + BPoint end = _ConvertFromTerminal(fSelection.End()); BRect rect; - if (fSelStart.y == fSelEnd.y) + if (fSelection.Start().y == fSelection.End().y) rect.Set(start.x, start.y, end.x + fFontWidth, end.y + fFontHeight); else rect.Set(0, start.y, fColumns * fFontWidth, end.y + fFontHeight); @@ -2911,6 +3019,9 @@ TermView::Listener::NextTermView(TermView* view) } +// #pragma mark - + + #ifdef USE_DEBUG_SNAPSHOTS void diff --git a/src/apps/terminal/TermView.h b/src/apps/terminal/TermView.h index 429097d..5fc9c34 100644 --- a/src/apps/terminal/TermView.h +++ b/src/apps/terminal/TermView.h @@ -16,10 +16,12 @@ #include <Autolock.h> #include <Messenger.h> +#include <ObjectList.h> #include <String.h> #include <View.h> #include "TermPos.h" +#include "TermViewHighlight.h" class ActiveProcessInfo; @@ -30,6 +32,7 @@ class BScrollView; class BString; class BStringView; class BasicTerminalBuffer; +class DefaultCharClassifier; class InlineInput; class ResizeWindow; class ShellInfo; @@ -38,10 +41,14 @@ class TermBuffer; class TerminalBuffer; class Shell; -class TermView : public BView { + +class TermView : public BView, private TermViewHighlighter { public: class Listener; + typedef TermViewHighlighter Highlighter; + typedef TermViewHighlight Highlight; + public: TermView(BRect frame, const ShellParameters& shellParameters, @@ -141,17 +148,29 @@ protected: const char* property); private: - class CharClassifier; + class TextBufferSyncLocker; + friend class TextBufferSyncLocker; class State; class StandardBaseState; class DefaultState; class SelectState; + class HyperLinkState; + class HyperLinkMenuState; friend class State; friend class StandardBaseState; friend class DefaultState; friend class SelectState; + friend class HyperLinkState; + friend class HyperLinkMenuState; + + typedef BObjectList<Highlight> HighlightList; + +private: + // TermViewHighlighter + virtual rgb_color ForegroundColor(); + virtual rgb_color BackgroundColor(); private: // point and text offset conversion @@ -174,8 +193,9 @@ private: void _SwitchCursorBlinking(bool blinkingOn); void _DrawLinePart(int32 x1, int32 y1, uint32 attr, - char* buffer, int32 width, bool mouse, - bool cursor, BView* inView); + char* buffer, int32 width, + Highlight* highlight, bool cursor, + BView* inView); void _DrawCursor(); void _InvalidateTextRange(TermPos start, TermPos end); @@ -193,6 +213,7 @@ private: void _SynchronizeWithTextBuffer( int32 visibleDirtyTop, int32 visibleDirtyBottom); + void _VisibleTextBufferChanged(); void _WritePTY(const char* text, int32 numBytes); @@ -209,8 +230,12 @@ private: void _SelectLine(BPoint where, bool extend, bool useInitialSelection); - bool _CheckSelectedRegion(const TermPos& pos) const; - bool _CheckSelectedRegion(int32 row, + void _AddHighlight(Highlight* highlight); + void _RemoveHighlight(Highlight* highlight); + bool _ClearHighlight(Highlight* highlight); + + Highlight* _CheckHighlightRegion(const TermPos& pos) const; + Highlight* _CheckHighlightRegion(int32 row, int32 firstColumn, int32& lastColumn) const; void _UpdateSIGWINCH(); @@ -237,7 +262,7 @@ private: [ *** diff truncated: 965 lines dropped *** ] ############################################################################ Commit: 7e3f7da70a128469667d9c63d9262c723122c127 URL: http://cgit.haiku-os.org/haiku/commit/?id=7e3f7da Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Fri May 10 23:35:17 2013 UTC BURL: Make the output to stderr DEBUG only ---------------------------------------------------------------------------- ############################################################################ Revision: hrev45662 Commit: 09cbc86d0d94f06f5bf65071fccddf83060ed10c URL: http://cgit.haiku-os.org/haiku/commit/?id=09cbc86 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat May 11 01:44:41 2013 UTC TTracker::RefsReceived(): Forward "be:*" message fields In some cases /bin/open puts some additional fields in the B_REFS_RECEIVED message, but those were completely ignored. This makes opening paths with appended line/column numbers actually work as expected. ----------------------------------------------------------------------------