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
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, [email protected].
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, [email protected].
|
||||
* 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
|
||||
@@ -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
|
||||
|
||||
@@ -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 = ":/-._~[]?#@!$&'()*+,;=";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+236
-125
@@ -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;
|
||||
fView->fTextBuffer->Unlock();
|
||||
|
||||
if (character.IsAlNum())
|
||||
return CHAR_TYPE_WORD_CHAR;
|
||||
|
||||
if (std::find(fSpecialWordChars.begin(), fSpecialWordChars.end(),
|
||||
character) != fSpecialWordChars.end())
|
||||
return CHAR_TYPE_WORD_CHAR;
|
||||
|
||||
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;
|
||||
|
||||
if (fSelStart.y < -historySize)
|
||||
_Deselect();
|
||||
highlight->ScrollRange(linesScrolled);
|
||||
if (highlight == &fSelection) {
|
||||
fInitialSelectionStart.y -= linesScrolled;
|
||||
fInitialSelectionEnd.y -= linesScrolled;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return pos >= fSelStart && pos < fSelEnd;
|
||||
fHighlights.AddItem(highlight);
|
||||
|
||||
if (!highlight->IsEmpty())
|
||||
_InvalidateTextRange(highlight->Start(), highlight->End());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::_RemoveHighlight(Highlight* highlight)
|
||||
{
|
||||
fHighlights.RemoveItem(highlight);
|
||||
|
||||
if (!highlight->IsEmpty())
|
||||
_InvalidateTextRange(highlight->Start(), highlight->End());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermView::_CheckSelectedRegion(int32 row, int32 firstColumn,
|
||||
TermView::_ClearHighlight(Highlight* highlight)
|
||||
{
|
||||
if (highlight->IsEmpty())
|
||||
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;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TermView::Highlight*
|
||||
TermView::_CheckHighlightRegion(int32 row, int32 firstColumn,
|
||||
int32& lastColumn) const
|
||||
{
|
||||
if (fSelStart == fSelEnd)
|
||||
return false;
|
||||
Highlight* nextHighlight = NULL;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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:
|
||||
BMessageRunner* fAutoScrollRunner;
|
||||
BMessageRunner* fResizeRunner;
|
||||
BStringView* fResizeView;
|
||||
CharClassifier* fCharClassifier;
|
||||
DefaultCharClassifier* fCharClassifier;
|
||||
|
||||
// Font and Width
|
||||
BFont fHalfFont;
|
||||
@@ -272,6 +297,7 @@ private:
|
||||
// Object pointer.
|
||||
TerminalBuffer* fTextBuffer;
|
||||
BasicTerminalBuffer* fVisibleTextBuffer;
|
||||
bool fVisibleTextBufferChanged;
|
||||
BScrollBar* fScrollBar;
|
||||
InlineInput* fInline;
|
||||
|
||||
@@ -297,14 +323,16 @@ private:
|
||||
bool fConsiderClockedSync;
|
||||
|
||||
// selection
|
||||
TermPos fSelStart;
|
||||
TermPos fSelEnd;
|
||||
Highlight fSelection;
|
||||
TermPos fInitialSelectionStart;
|
||||
TermPos fInitialSelectionEnd;
|
||||
BPoint fLastClickPoint;
|
||||
|
||||
HighlightList fHighlights;
|
||||
|
||||
// mouse
|
||||
int32 fMouseButtons;
|
||||
int32 fModifiers;
|
||||
TermPos fPrevPos;
|
||||
bool fReportX10MouseEvent;
|
||||
bool fReportNormalMouseEvent;
|
||||
@@ -315,6 +343,8 @@ private:
|
||||
// states
|
||||
DefaultState* fDefaultState;
|
||||
SelectState* fSelectState;
|
||||
HyperLinkState* fHyperLinkState;
|
||||
HyperLinkMenuState* fHyperLinkMenuState;
|
||||
State* fActiveState;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "TermViewHighlight.h"
|
||||
|
||||
|
||||
TermViewHighlighter::~TermViewHighlighter()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
TermViewHighlighter::AdjustTextAttributes(uint32 attributes)
|
||||
{
|
||||
return attributes;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef TERMVIEW_HIGHLIGHT_H
|
||||
#define TERMVIEW_HIGHLIGHT_H
|
||||
|
||||
|
||||
#include <GraphicsDefs.h>
|
||||
|
||||
#include "TermPos.h"
|
||||
|
||||
|
||||
class TermViewHighlighter {
|
||||
public:
|
||||
virtual ~TermViewHighlighter();
|
||||
|
||||
virtual rgb_color ForegroundColor() = 0;
|
||||
virtual rgb_color BackgroundColor() = 0;
|
||||
virtual uint32 AdjustTextAttributes(uint32 attributes);
|
||||
};
|
||||
|
||||
|
||||
class TermViewHighlight {
|
||||
public:
|
||||
TermViewHighlight()
|
||||
:
|
||||
fHighlighter(NULL),
|
||||
fStart(-1, -1),
|
||||
fEnd(-1, -1)
|
||||
{
|
||||
}
|
||||
|
||||
TermViewHighlighter* Highlighter() const
|
||||
{
|
||||
return fHighlighter;
|
||||
}
|
||||
|
||||
void SetHighlighter(TermViewHighlighter* highligher)
|
||||
{
|
||||
fHighlighter = highligher;
|
||||
}
|
||||
|
||||
const TermPos& Start() const
|
||||
{
|
||||
return fStart;
|
||||
}
|
||||
|
||||
const TermPos& End() const
|
||||
{
|
||||
return fEnd;
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return fStart == fEnd;
|
||||
}
|
||||
|
||||
bool RangeContains(const TermPos& pos) const
|
||||
{
|
||||
return pos >= fStart && pos < fEnd;
|
||||
}
|
||||
|
||||
void SetRange(const TermPos& start, const TermPos& end)
|
||||
{
|
||||
fStart = start;
|
||||
fEnd = end;
|
||||
}
|
||||
|
||||
void ScrollRange(int32 byLines)
|
||||
{
|
||||
fStart.y -= byLines;
|
||||
fEnd.y -= byLines;
|
||||
}
|
||||
|
||||
private:
|
||||
TermViewHighlighter* fHighlighter;
|
||||
TermPos fStart;
|
||||
TermPos fEnd;
|
||||
};
|
||||
|
||||
|
||||
#endif // TERMVIEW_HIGHLIGHT_H
|
||||
@@ -16,17 +16,30 @@
|
||||
|
||||
#include "TermViewStates.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <Catalog.h>
|
||||
#include <Clipboard.h>
|
||||
#include <Cursor.h>
|
||||
#include <LayoutBuilder.h>
|
||||
#include <MessageRunner.h>
|
||||
#include <PopUpMenu.h>
|
||||
#include <ScrollBar.h>
|
||||
#include <UTF8.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "Shell.h"
|
||||
#include "TermConst.h"
|
||||
#include "TerminalBuffer.h"
|
||||
#include "VTkeymap.h"
|
||||
#include "VTKeyTbl.h"
|
||||
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
#define B_TRANSLATION_CONTEXT "Terminal TermView"
|
||||
|
||||
|
||||
// selection granularity
|
||||
enum {
|
||||
SELECT_CHARS,
|
||||
@@ -36,6 +49,13 @@ enum {
|
||||
|
||||
static const uint32 kAutoScroll = 'AScr';
|
||||
|
||||
static const uint32 kMessageOpenLink = 'OLnk';
|
||||
static const uint32 kMessageCopyLink = 'CLnk';
|
||||
static const uint32 kMessageMenuClosed = 'MClo';
|
||||
|
||||
|
||||
static const char* const kKnownURLProtocols = "http:https:ftp:mailto";
|
||||
|
||||
|
||||
// #pragma mark - State
|
||||
|
||||
@@ -71,6 +91,12 @@ TermView::State::MessageReceived(BMessage* message)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::ModifiersChanged(int32 oldModifiers, int32 modifiers)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::KeyDown(const char* bytes, int32 numBytes)
|
||||
{
|
||||
@@ -85,7 +111,7 @@ TermView::State::MouseDown(BPoint where, int32 buttons, int32 modifiers)
|
||||
|
||||
void
|
||||
TermView::State::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message)
|
||||
const BMessage* message, int32 modifiers)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -96,6 +122,18 @@ TermView::State::MouseUp(BPoint where, int32 buttons)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::WindowActivated(bool active)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::State::VisibleTextBufferChanged()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - StandardBaseState
|
||||
|
||||
|
||||
@@ -107,25 +145,22 @@ TermView::StandardBaseState::StandardBaseState(TermView* view)
|
||||
|
||||
|
||||
bool
|
||||
TermView::StandardBaseState::_StandardMouseMoved(BPoint where)
|
||||
TermView::StandardBaseState::_StandardMouseMoved(BPoint where, int32 modifiers)
|
||||
{
|
||||
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,
|
||||
fView->_SendMouseEvent(fView->fMouseButtons, modifiers,
|
||||
clickPos.x, clickPos.y, true);
|
||||
}
|
||||
fView->fPrevPos = clickPos;
|
||||
} else {
|
||||
fView->_SendMouseEvent(fView->fMouseButtons, modifier, clickPos.x,
|
||||
fView->_SendMouseEvent(fView->fMouseButtons, modifiers, clickPos.x,
|
||||
clickPos.y, true);
|
||||
}
|
||||
|
||||
@@ -143,6 +178,13 @@ TermView::DefaultState::DefaultState(TermView* view)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::DefaultState::ModifiersChanged(int32 oldModifiers, int32 modifiers)
|
||||
{
|
||||
_CheckEnterHyperLinkState(modifiers);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::DefaultState::KeyDown(const char* bytes, int32 numBytes)
|
||||
{
|
||||
@@ -341,9 +383,32 @@ TermView::DefaultState::MouseDown(BPoint where, int32 buttons, int32 modifiers)
|
||||
|
||||
void
|
||||
TermView::DefaultState::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message)
|
||||
const BMessage* message, int32 modifiers)
|
||||
{
|
||||
_StandardMouseMoved(where);
|
||||
if (_CheckEnterHyperLinkState(modifiers))
|
||||
return;
|
||||
|
||||
_StandardMouseMoved(where, modifiers);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::DefaultState::WindowActivated(bool active)
|
||||
{
|
||||
if (active)
|
||||
_CheckEnterHyperLinkState(fView->fModifiers);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermView::DefaultState::_CheckEnterHyperLinkState(int32 modifiers)
|
||||
{
|
||||
if ((modifiers & B_COMMAND_KEY) != 0 && fView->Window()->IsActive()) {
|
||||
fView->_NextState(fView->fHyperLinkState);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -368,7 +433,7 @@ TermView::SelectState::Prepare(BPoint where, int32 modifiers)
|
||||
|
||||
if (fView->_HasSelection()) {
|
||||
TermPos inPos = fView->_ConvertToTerminal(where);
|
||||
if (fView->_CheckSelectedRegion(inPos)) {
|
||||
if (fView->fSelection.RangeContains(inPos)) {
|
||||
if (modifiers & B_CONTROL_KEY) {
|
||||
BPoint p;
|
||||
uint32 bt;
|
||||
@@ -448,9 +513,9 @@ TermView::SelectState::MessageReceived(BMessage* message)
|
||||
|
||||
void
|
||||
TermView::SelectState::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message)
|
||||
const BMessage* message, int32 modifiers)
|
||||
{
|
||||
if (_StandardMouseMoved(where))
|
||||
if (_StandardMouseMoved(where, modifiers))
|
||||
return;
|
||||
|
||||
if (fCheckMouseTracking) {
|
||||
@@ -561,3 +626,376 @@ TermView::SelectState::_AutoScrollUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - HyperLinkState
|
||||
|
||||
|
||||
TermView::HyperLinkState::HyperLinkState(TermView* view)
|
||||
:
|
||||
State(view),
|
||||
fURLCharClassifier(kURLAdditionalWordCharacters),
|
||||
fPathComponentCharClassifier(
|
||||
BString(kDefaultAdditionalWordCharacters).RemoveFirst("/")),
|
||||
fHighlight(),
|
||||
fHighlightActive(false)
|
||||
{
|
||||
fHighlight.SetHighlighter(this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::Entered()
|
||||
{
|
||||
_UpdateHighlight();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::Exited()
|
||||
{
|
||||
_DeactivateHighlight();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::ModifiersChanged(int32 oldModifiers, int32 modifiers)
|
||||
{
|
||||
if ((modifiers & B_COMMAND_KEY) == 0)
|
||||
fView->_NextState(fView->fDefaultState);
|
||||
else
|
||||
_UpdateHighlight();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::MouseDown(BPoint where, int32 buttons,
|
||||
int32 modifiers)
|
||||
{
|
||||
TermPos start;
|
||||
TermPos end;
|
||||
HyperLink link;
|
||||
bool pathPrefixOnly = (modifiers & B_SHIFT_KEY) != 0;
|
||||
if (!_GetHyperLinkAt(where, pathPrefixOnly, link, start, end))
|
||||
return;
|
||||
|
||||
if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0) {
|
||||
link.Open();
|
||||
} else if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
|
||||
fView->fHyperLinkMenuState->Prepare(where, link);
|
||||
fView->_NextState(fView->fHyperLinkMenuState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message, int32 modifiers)
|
||||
{
|
||||
_UpdateHighlight(where, modifiers);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::WindowActivated(bool active)
|
||||
{
|
||||
if (!active)
|
||||
fView->_NextState(fView->fDefaultState);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::VisibleTextBufferChanged()
|
||||
{
|
||||
_UpdateHighlight();
|
||||
}
|
||||
|
||||
|
||||
rgb_color
|
||||
TermView::HyperLinkState::ForegroundColor()
|
||||
{
|
||||
return make_color(0, 0, 255);
|
||||
}
|
||||
|
||||
|
||||
rgb_color
|
||||
TermView::HyperLinkState::BackgroundColor()
|
||||
{
|
||||
return fView->fTextBackColor;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
TermView::HyperLinkState::AdjustTextAttributes(uint32 attributes)
|
||||
{
|
||||
return attributes | UNDERLINE;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermView::HyperLinkState::_GetHyperLinkAt(BPoint where, bool pathPrefixOnly,
|
||||
HyperLink& _link, TermPos& _start, TermPos& _end)
|
||||
{
|
||||
TerminalBuffer* textBuffer = fView->fTextBuffer;
|
||||
BAutolock textBufferLocker(textBuffer);
|
||||
|
||||
TermPos pos = fView->_ConvertToTerminal(where);
|
||||
|
||||
// try to get a URL first
|
||||
BString text;
|
||||
if (!textBuffer->FindWord(pos, &fURLCharClassifier, false, _start, _end))
|
||||
return false;
|
||||
|
||||
text.Truncate(0);
|
||||
textBuffer->GetStringFromRegion(text, _start, _end);
|
||||
text.Trim();
|
||||
|
||||
// We're only happy, if it has a protocol part which we know.
|
||||
int32 colonIndex = text.FindFirst(':');
|
||||
if (colonIndex >= 0) {
|
||||
BString protocol(text, colonIndex);
|
||||
if (strstr(kKnownURLProtocols, protocol) != NULL) {
|
||||
_link = HyperLink(text, HyperLink::TYPE_URL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no obvious URL -- try file name
|
||||
if (!textBuffer->FindWord(pos, fView->fCharClassifier, false, _start, _end))
|
||||
return false;
|
||||
|
||||
// In path-prefix-only mode we determine the end position anew by omitting
|
||||
// the '/' in the allowed word chars.
|
||||
if (pathPrefixOnly) {
|
||||
TermPos componentStart;
|
||||
TermPos componentEnd;
|
||||
if (textBuffer->FindWord(pos, &fPathComponentCharClassifier, false,
|
||||
componentStart, componentEnd)) {
|
||||
_end = componentEnd;
|
||||
} else {
|
||||
// That means pos points to a '/'. We simply use the previous
|
||||
// position.
|
||||
_end = pos;
|
||||
if (_start == _end) {
|
||||
// Well, must be just "/". Advance to the next position.
|
||||
if (!textBuffer->NextLinePos(_end, false))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text.Truncate(0);
|
||||
textBuffer->GetStringFromRegion(text, _start, _end);
|
||||
text.Trim();
|
||||
if (text.IsEmpty())
|
||||
return false;
|
||||
|
||||
// check, whether the file exists
|
||||
struct stat st;
|
||||
if (lstat(text, &st) == 0) {
|
||||
_link = HyperLink(text, HyperLink::TYPE_PATH);
|
||||
return true;
|
||||
}
|
||||
|
||||
// As such this isn't an existing path. Try a few common alternative cases:
|
||||
// * "<path>:"
|
||||
// * "<path>:<line>"
|
||||
// * "<path>:<line>:"
|
||||
// * "<path>:<line>:<column>"
|
||||
// * "<path>:<line>:<column>:"
|
||||
|
||||
if (text.Length() <= 1)
|
||||
return false;
|
||||
|
||||
if (text[text.Length() - 1] == ':') {
|
||||
text.Truncate(text.Length() - 1);
|
||||
if (!textBuffer->PreviousLinePos(_end))
|
||||
return false;
|
||||
|
||||
if (lstat(text, &st) == 0) {
|
||||
_link = HyperLink(text, HyperLink::TYPE_PATH);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
BString path = text;
|
||||
|
||||
for (int32 i = 0; i < 2; i++) {
|
||||
int32 colonIndex = path.FindLast(':');
|
||||
if (colonIndex <= 0 || colonIndex == path.Length() - 1)
|
||||
return false;
|
||||
|
||||
char* numberEnd;
|
||||
strtol(path.String() + colonIndex + 1, &numberEnd, 0);
|
||||
if (*numberEnd != '\0')
|
||||
return false;
|
||||
|
||||
path.Truncate(colonIndex);
|
||||
if (lstat(path, &st) == 0) {
|
||||
_link = HyperLink(text,
|
||||
i == 0
|
||||
? HyperLink::TYPE_PATH_WITH_LINE
|
||||
: HyperLink::TYPE_PATH_WITH_LINE_AND_COLUMN,
|
||||
path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::_UpdateHighlight()
|
||||
{
|
||||
BPoint where;
|
||||
uint32 buttons;
|
||||
fView->GetMouse(&where, &buttons, false);
|
||||
_UpdateHighlight(where, fView->fModifiers);
|
||||
}
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::_UpdateHighlight(BPoint where, int32 modifiers)
|
||||
{
|
||||
TermPos start;
|
||||
TermPos end;
|
||||
HyperLink link;
|
||||
bool pathPrefixOnly = (modifiers & B_SHIFT_KEY) != 0;
|
||||
if (_GetHyperLinkAt(where, pathPrefixOnly, link, start, end))
|
||||
_ActivateHighlight(start, end);
|
||||
else
|
||||
_DeactivateHighlight();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::_ActivateHighlight(const TermPos& start,
|
||||
const TermPos& end)
|
||||
{
|
||||
if (fHighlightActive) {
|
||||
if (fHighlight.Start() == start && fHighlight.End() == end)
|
||||
return;
|
||||
_DeactivateHighlight();
|
||||
}
|
||||
|
||||
fHighlight.SetRange(start, end);
|
||||
fView->_AddHighlight(&fHighlight);
|
||||
BCursor cursor(B_CURSOR_ID_FOLLOW_LINK);
|
||||
fView->SetViewCursor(&cursor);
|
||||
fHighlightActive = true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkState::_DeactivateHighlight()
|
||||
{
|
||||
if (fHighlightActive) {
|
||||
fView->_RemoveHighlight(&fHighlight);
|
||||
BCursor cursor(B_CURSOR_ID_SYSTEM_DEFAULT);
|
||||
fView->SetViewCursor(&cursor);
|
||||
fHighlightActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - HyperLinkMenuState
|
||||
|
||||
|
||||
class TermView::HyperLinkMenuState::PopUpMenu : public BPopUpMenu {
|
||||
public:
|
||||
PopUpMenu(const BMessenger& messageTarget)
|
||||
:
|
||||
BPopUpMenu("open hyperlink"),
|
||||
fMessageTarget(messageTarget)
|
||||
{
|
||||
SetAsyncAutoDestruct(true);
|
||||
}
|
||||
|
||||
~PopUpMenu()
|
||||
{
|
||||
fMessageTarget.SendMessage(kMessageMenuClosed);
|
||||
}
|
||||
|
||||
private:
|
||||
BMessenger fMessageTarget;
|
||||
};
|
||||
|
||||
|
||||
TermView::HyperLinkMenuState::HyperLinkMenuState(TermView* view)
|
||||
:
|
||||
State(view),
|
||||
fLink()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkMenuState::Prepare(BPoint point, const HyperLink& link)
|
||||
{
|
||||
fLink = link;
|
||||
|
||||
// open context menu
|
||||
PopUpMenu* menu = new PopUpMenu(fView);
|
||||
BLayoutBuilder::Menu<> menuBuilder(menu);
|
||||
switch (link.GetType()) {
|
||||
case HyperLink::TYPE_URL:
|
||||
menuBuilder
|
||||
.AddItem(B_TRANSLATE("Open link"), kMessageOpenLink)
|
||||
.AddItem(B_TRANSLATE("Copy link location"), kMessageCopyLink);
|
||||
break;
|
||||
|
||||
case HyperLink::TYPE_PATH:
|
||||
case HyperLink::TYPE_PATH_WITH_LINE:
|
||||
case HyperLink::TYPE_PATH_WITH_LINE_AND_COLUMN:
|
||||
menuBuilder
|
||||
.AddItem(B_TRANSLATE("Open path"), kMessageOpenLink)
|
||||
.AddItem(B_TRANSLATE("Copy path"), kMessageCopyLink);
|
||||
break;
|
||||
}
|
||||
menu->SetTargetForItems(fView);
|
||||
menu->Go(fView->ConvertToScreen(point), true, true, true);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermView::HyperLinkMenuState::Exited()
|
||||
{
|
||||
fLink = HyperLink();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermView::HyperLinkMenuState::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case kMessageOpenLink:
|
||||
if (fLink.IsValid())
|
||||
fLink.Open();
|
||||
return true;
|
||||
|
||||
case kMessageCopyLink:
|
||||
if (fLink.IsValid()) {
|
||||
if (!be_clipboard->Lock())
|
||||
return true;
|
||||
|
||||
be_clipboard->Clear();
|
||||
|
||||
if (BMessage *data = be_clipboard->Data()) {
|
||||
data->AddData("text/plain", B_MIME_TYPE,
|
||||
fLink.Address().String(),
|
||||
fLink.Address().Length());
|
||||
be_clipboard->Commit();
|
||||
}
|
||||
|
||||
be_clipboard->Unlock();
|
||||
}
|
||||
return true;
|
||||
|
||||
case kMessageMenuClosed:
|
||||
fView->_NextState(fView->fDefaultState);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#define TERMVIEW_STATES_H
|
||||
|
||||
|
||||
#include "HyperLink.h"
|
||||
#include "TerminalCharClassifier.h"
|
||||
#include "TermView.h"
|
||||
|
||||
|
||||
@@ -28,14 +30,20 @@ public:
|
||||
virtual bool MessageReceived(BMessage* message);
|
||||
// returns true, if handled
|
||||
|
||||
virtual void ModifiersChanged(int32 oldModifiers,
|
||||
int32 modifiers);
|
||||
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);
|
||||
const BMessage* message, int32 modifiers);
|
||||
virtual void MouseUp(BPoint where, int32 buttons);
|
||||
|
||||
virtual void WindowActivated(bool active);
|
||||
|
||||
virtual void VisibleTextBufferChanged();
|
||||
|
||||
protected:
|
||||
TermView* fView;
|
||||
};
|
||||
@@ -46,7 +54,8 @@ public:
|
||||
StandardBaseState(TermView* view);
|
||||
|
||||
protected:
|
||||
bool _StandardMouseMoved(BPoint where);
|
||||
bool _StandardMouseMoved(BPoint where,
|
||||
int32 modifiers);
|
||||
};
|
||||
|
||||
|
||||
@@ -54,12 +63,20 @@ class TermView::DefaultState : public TermView::StandardBaseState {
|
||||
public:
|
||||
DefaultState(TermView* view);
|
||||
|
||||
virtual void ModifiersChanged(int32 oldModifiers,
|
||||
int32 modifiers);
|
||||
|
||||
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);
|
||||
const BMessage* message, int32 modifiers);
|
||||
|
||||
virtual void WindowActivated(bool active);
|
||||
|
||||
private:
|
||||
bool _CheckEnterHyperLinkState(int32 modifiers);
|
||||
};
|
||||
|
||||
|
||||
@@ -72,7 +89,7 @@ public:
|
||||
virtual bool MessageReceived(BMessage* message);
|
||||
|
||||
virtual void MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message);
|
||||
const BMessage* message, int32 modifiers);
|
||||
virtual void MouseUp(BPoint where, int32 buttons);
|
||||
|
||||
private:
|
||||
@@ -85,4 +102,67 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class TermView::HyperLinkState : public TermView::State,
|
||||
private TermViewHighlighter {
|
||||
public:
|
||||
HyperLinkState(TermView* view);
|
||||
|
||||
virtual void Entered();
|
||||
virtual void Exited();
|
||||
|
||||
virtual void ModifiersChanged(int32 oldModifiers,
|
||||
int32 modifiers);
|
||||
|
||||
virtual void MouseDown(BPoint where, int32 buttons,
|
||||
int32 modifiers);
|
||||
virtual void MouseMoved(BPoint where, uint32 transit,
|
||||
const BMessage* message, int32 modifiers);
|
||||
|
||||
virtual void WindowActivated(bool active);
|
||||
|
||||
virtual void VisibleTextBufferChanged();
|
||||
|
||||
private:
|
||||
// TermViewHighlighter
|
||||
virtual rgb_color ForegroundColor();
|
||||
virtual rgb_color BackgroundColor();
|
||||
virtual uint32 AdjustTextAttributes(uint32 attributes);
|
||||
|
||||
private:
|
||||
bool _GetHyperLinkAt(BPoint where,
|
||||
bool pathPrefixOnly, HyperLink& _link,
|
||||
TermPos& _start, TermPos& _end);
|
||||
|
||||
void _UpdateHighlight();
|
||||
void _UpdateHighlight(BPoint where, int32 modifiers);
|
||||
void _ActivateHighlight(const TermPos& start,
|
||||
const TermPos& end);
|
||||
void _DeactivateHighlight();
|
||||
|
||||
private:
|
||||
DefaultCharClassifier fURLCharClassifier;
|
||||
DefaultCharClassifier fPathComponentCharClassifier;
|
||||
TermViewHighlight fHighlight;
|
||||
bool fHighlightActive;
|
||||
};
|
||||
|
||||
|
||||
class TermView::HyperLinkMenuState : public TermView::State {
|
||||
public:
|
||||
HyperLinkMenuState(TermView* view);
|
||||
|
||||
void Prepare(BPoint point, const HyperLink& link);
|
||||
|
||||
virtual void Exited();
|
||||
|
||||
virtual bool MessageReceived(BMessage* message);
|
||||
|
||||
private:
|
||||
class PopUpMenu;
|
||||
|
||||
private:
|
||||
HyperLink fLink;
|
||||
};
|
||||
|
||||
|
||||
#endif // TERMVIEW_STATES_H
|
||||
|
||||
@@ -1,11 +1,52 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Copyright 2008-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "TerminalCharClassifier.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
// #pragma mark - TerminalCharClassifier
|
||||
|
||||
|
||||
TerminalCharClassifier::~TerminalCharClassifier()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - DefaultCharClassifier
|
||||
|
||||
|
||||
DefaultCharClassifier::DefaultCharClassifier(const char* additionalWordChars)
|
||||
{
|
||||
const char* p = additionalWordChars;
|
||||
while (p != NULL && *p != '\0') {
|
||||
int count = UTF8Char::ByteCount(*p);
|
||||
if (count <= 0 || count > 4)
|
||||
break;
|
||||
fAdditionalWordChars.push_back(UTF8Char(p, count));
|
||||
p += count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
DefaultCharClassifier::Classify(const UTF8Char& character)
|
||||
{
|
||||
if (character.IsSpace())
|
||||
return CHAR_TYPE_SPACE;
|
||||
|
||||
if (character.IsAlNum())
|
||||
return CHAR_TYPE_WORD_CHAR;
|
||||
|
||||
if (std::find(fAdditionalWordChars.begin(), fAdditionalWordChars.end(),
|
||||
character) != fAdditionalWordChars.end()) {
|
||||
return CHAR_TYPE_WORD_CHAR;
|
||||
}
|
||||
|
||||
return CHAR_TYPE_WORD_DELIMITER;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Copyright 2008-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef TERMINAL_CHAR_CLASSIFIER_H
|
||||
#define TERMINAL_CHAR_CLASSIFIER_H
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "UTF8Char.h"
|
||||
|
||||
|
||||
enum {
|
||||
CHAR_TYPE_SPACE,
|
||||
CHAR_TYPE_WORD_CHAR,
|
||||
@@ -23,4 +28,16 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class DefaultCharClassifier: public TerminalCharClassifier {
|
||||
public:
|
||||
DefaultCharClassifier(
|
||||
const char* additionalWordChars);
|
||||
|
||||
virtual int Classify(const UTF8Char& character);
|
||||
|
||||
private:
|
||||
std::vector<UTF8Char> fAdditionalWordChars;
|
||||
};
|
||||
|
||||
|
||||
#endif // TERMINAL_CHAR_CLASSIFIER_H
|
||||
|
||||
Reference in New Issue
Block a user