[haiku-commits] haiku: hrev45662 - src/apps/terminal

  • From: ingo_weinhold@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Sat, 11 May 2013 04:45:11 +0200 (CEST)

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.

----------------------------------------------------------------------------


Other related posts: