Terminal: support for hyperlinks (#18827)
* support for invisible text * support for DECSTR - soft terminal reset Change-Id: I5cc333c7409a16fb41bcd57c5ebf2f277bd31370 Reviewed-on: https://review.haiku-os.org/c/haiku/+/9661 Reviewed-by: Adrien Destugues <[email protected]> Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
committed by
Adrien Destugues
parent
cfd2df0eba
commit
1a163d0ce9
@@ -15,7 +15,9 @@
|
||||
HyperLink::HyperLink()
|
||||
:
|
||||
fAddress(),
|
||||
fType(TYPE_URL)
|
||||
fType(TYPE_URL),
|
||||
fOSCRef(0),
|
||||
fOSCID(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -24,7 +26,9 @@ HyperLink::HyperLink(const BString& address, Type type)
|
||||
:
|
||||
fText(address),
|
||||
fAddress(address),
|
||||
fType(type)
|
||||
fType(type),
|
||||
fOSCRef(0),
|
||||
fOSCID(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -33,7 +37,20 @@ HyperLink::HyperLink(const BString& text, const BString& address, Type type)
|
||||
:
|
||||
fText(text),
|
||||
fAddress(address),
|
||||
fType(type)
|
||||
fType(type),
|
||||
fOSCRef(0),
|
||||
fOSCID(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HyperLink::HyperLink(const BString& address, uint32 ref, const BString& id)
|
||||
:
|
||||
fText(NULL),
|
||||
fAddress(address),
|
||||
fType(TYPE_OSC_URL),
|
||||
fOSCRef(ref),
|
||||
fOSCID(id)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@ public:
|
||||
TYPE_URL,
|
||||
TYPE_PATH,
|
||||
TYPE_PATH_WITH_LINE,
|
||||
TYPE_PATH_WITH_LINE_AND_COLUMN
|
||||
TYPE_PATH_WITH_LINE_AND_COLUMN,
|
||||
TYPE_OSC_URL
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -23,12 +24,15 @@ public:
|
||||
HyperLink(const BString& address, Type type);
|
||||
HyperLink(const BString& text,
|
||||
const BString& address, Type type);
|
||||
HyperLink(const BString& address, uint32 ref, const BString& id);
|
||||
|
||||
bool IsValid() const { return !fAddress.IsEmpty(); }
|
||||
|
||||
const BString& Text() const { return fText; }
|
||||
const BString& Address() const { return fAddress; }
|
||||
Type GetType() const { return fType; }
|
||||
const BString& OSCID() const { return fOSCID; }
|
||||
const uint32& OSCRef() const { return fOSCRef; }
|
||||
|
||||
status_t Open();
|
||||
|
||||
@@ -36,6 +40,8 @@ private:
|
||||
BString fText;
|
||||
BString fAddress;
|
||||
Type fType;
|
||||
uint32 fOSCRef;
|
||||
BString fOSCID;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -220,9 +220,10 @@ enum {
|
||||
#define DUMPCR 0x0040
|
||||
#define UNDERSET 0x0020
|
||||
#define OVERLINE 0x0010
|
||||
#define HIDDEN 0x0008
|
||||
#define FORECOLOR 0xFF0000
|
||||
#define BACKCOLOR 0xFF000000
|
||||
#define CHAR_ATTRIBUTES 0xFFFF7730
|
||||
#define CHAR_ATTRIBUTES 0xFFFF7738
|
||||
|
||||
#define FORECOLORED(x) ((x) << 16)
|
||||
#define BACKCOLORED(x) ((x) << 24)
|
||||
|
||||
@@ -646,6 +646,12 @@ TermParse::EscParse()
|
||||
param[nparam++] = ' ';
|
||||
break;
|
||||
|
||||
case CASE_CSI_EXCL: // ESC [N p
|
||||
// part of soft reset DECSTR
|
||||
if (nparam < NPARAM)
|
||||
param[nparam++] = '!';
|
||||
break;
|
||||
|
||||
case CASE_DEC_DOL: // ESC [? N p
|
||||
// part of change cursor style DECRQM
|
||||
if (nparam < NPARAM)
|
||||
@@ -838,6 +844,10 @@ TermParse::EscParse()
|
||||
attributes |= INVERSE;
|
||||
break;
|
||||
|
||||
case 8: /* Hidden */
|
||||
attributes |= HIDDEN;
|
||||
break;
|
||||
|
||||
case 21: /* Double Underline */
|
||||
attributes.SetUnder(DOUBLE_UNDERLINE);
|
||||
break;
|
||||
@@ -854,6 +864,10 @@ TermParse::EscParse()
|
||||
attributes &= ~INVERSE;
|
||||
break;
|
||||
|
||||
case 28: /* Not Hidden */
|
||||
attributes &= ~HIDDEN;
|
||||
break;
|
||||
|
||||
case 53: /* Overline */
|
||||
attributes |= OVERLINE;
|
||||
break;
|
||||
@@ -1298,6 +1312,14 @@ TermParse::EscParse()
|
||||
parsestate = groundtable;
|
||||
break;
|
||||
|
||||
case CASE_DECSTR: // DECSTR - soft terminal reset
|
||||
if (nparam == 2 && param[1] == '!') {
|
||||
Attributes attributes;
|
||||
fBuffer->SetAttributes(attributes);
|
||||
}
|
||||
parsestate = groundtable;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1710,6 +1732,34 @@ TermParse::_ProcessOperatingSystemControls(uchar* params)
|
||||
fBuffer->ResetColors(indexes, 1, true);
|
||||
}
|
||||
break;
|
||||
// handle hyperlinks
|
||||
case 8:
|
||||
{
|
||||
char* id = NULL;
|
||||
char* start = (char*)params;
|
||||
char* end = strpbrk(start, ";:");
|
||||
for (; end != NULL; start = end + 1) {
|
||||
end = strpbrk(start, ";:");
|
||||
if (end == NULL)
|
||||
break;
|
||||
if (end - start > 3 && strncmp(start, "id=", 3) == 0)
|
||||
id = strndup(start + 3, end - start + 3);
|
||||
if (*end == ';')
|
||||
break;
|
||||
}
|
||||
if (end == NULL)
|
||||
break;
|
||||
BString uri(end + 1);
|
||||
Attributes attributes = fBuffer->GetAttributes();
|
||||
if (uri.IsEmpty()) {
|
||||
attributes.SetHyperlink(0);
|
||||
} else {
|
||||
attributes.SetHyperlink(fBuffer->PutHyperLink(id, uri));
|
||||
}
|
||||
free(id);
|
||||
fBuffer->SetAttributes(attributes);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// printf("%d -> %s\n", mode, params);
|
||||
break;
|
||||
|
||||
@@ -452,7 +452,7 @@ TermView::BackgroundColor()
|
||||
}
|
||||
|
||||
|
||||
inline int32
|
||||
int32
|
||||
TermView::_LineAt(float y) const
|
||||
{
|
||||
int32 location = int32(y + fScrollOffset);
|
||||
@@ -1061,6 +1061,9 @@ void
|
||||
TermView::_DrawLinePart(float x1, float y1, Attributes attr,
|
||||
char *buf, int32 width, Highlight* highlight, bool cursor, BView *inView)
|
||||
{
|
||||
if (attr.IsHidden())
|
||||
return;
|
||||
|
||||
if (highlight != NULL)
|
||||
attr.state = highlight->Highlighter()->AdjustTextAttributes(attr.state);
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ private:
|
||||
|
||||
private:
|
||||
// point and text offset conversion
|
||||
inline int32 _LineAt(float y) const;
|
||||
int32 _LineAt(float y) const;
|
||||
inline float _LineOffset(int32 index) const;
|
||||
TermPos _ConvertToTerminal(const BPoint& point) const;
|
||||
inline BPoint _ConvertFromTerminal(const TermPos& pos) const;
|
||||
|
||||
@@ -833,10 +833,19 @@ TermView::HyperLinkState::_GetHyperLinkAt(BPoint where, bool pathPrefixOnly,
|
||||
HyperLink& _link, TermPos& _start, TermPos& _end)
|
||||
{
|
||||
TerminalBuffer* textBuffer = fView->fTextBuffer;
|
||||
BasicTerminalBuffer* visibleTextBuffer = fView->fVisibleTextBuffer;
|
||||
BAutolock textBufferLocker(textBuffer);
|
||||
int32 firstVisible = fView->_LineAt(0);
|
||||
|
||||
TermPos pos = fView->_ConvertToTerminal(where);
|
||||
|
||||
UTF8Char character;
|
||||
Attributes attr;
|
||||
if (visibleTextBuffer->GetChar(pos.y - firstVisible, pos.x, character, attr) == A_CHAR
|
||||
&& attr.Hyperlink() != 0) {
|
||||
return textBuffer->GetHyperLink(attr.Hyperlink(), _link);
|
||||
}
|
||||
|
||||
// try to get a URL first
|
||||
BString text;
|
||||
if (!textBuffer->FindWord(pos, &fURLCharClassifier, false, _start, _end))
|
||||
@@ -1120,6 +1129,7 @@ TermView::HyperLinkMenuState::Prepare(BPoint point, const HyperLink& link)
|
||||
BLayoutBuilder::Menu<> menuBuilder(menu);
|
||||
switch (link.GetType()) {
|
||||
case HyperLink::TYPE_URL:
|
||||
case HyperLink::TYPE_OSC_URL:
|
||||
menuBuilder
|
||||
.AddItem(B_TRANSLATE("Open link"), kMessageOpenLink)
|
||||
.AddItem(B_TRANSLATE("Copy link location"), kMessageCopyLink);
|
||||
@@ -1161,8 +1171,7 @@ TermView::HyperLinkMenuState::MessageReceived(BMessage* message)
|
||||
case kMessageCopyAbsolutePath:
|
||||
{
|
||||
if (fLink.IsValid()) {
|
||||
BString toCopy = message->what == kMessageCopyLink
|
||||
? fLink.Text() : fLink.Address();
|
||||
BString toCopy = fLink.Address();
|
||||
|
||||
if (!be_clipboard->Lock())
|
||||
return true;
|
||||
|
||||
@@ -34,7 +34,8 @@ TerminalBuffer::TerminalBuffer()
|
||||
fColorsPalette(NULL),
|
||||
fListenerValid(false),
|
||||
fMode(MODE_INTERPRET_META_KEY | MODE_META_KEY_SENDS_ESCAPE | MODE_CURSOR_BLINKING),
|
||||
fCursorStyle(BLOCK_CURSOR)
|
||||
fCursorStyle(BLOCK_CURSOR),
|
||||
fNextOSCRef(1)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -44,6 +45,14 @@ TerminalBuffer::~TerminalBuffer()
|
||||
free(fAlternateScreen);
|
||||
delete fAlternateHistory;
|
||||
delete[] fColorsPalette;
|
||||
|
||||
HyperLinkRefMap::Iterator iterator = fHyperLinkForRef.GetIterator();
|
||||
while (iterator.HasNext()) {
|
||||
HyperLinkRefMap::Entry entry = iterator.Next();
|
||||
delete entry.value;
|
||||
}
|
||||
fHyperLinkForID.Clear();
|
||||
fHyperLinkForRef.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -321,3 +330,33 @@ TerminalBuffer::_SwitchScreenBuffer()
|
||||
std::swap(fAttributes, fAlternateAttributes);
|
||||
fAlternateScreenActive = !fAlternateScreenActive;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
TerminalBuffer::PutHyperLink(const char* id, BString& uri)
|
||||
{
|
||||
HyperLink* hyperLink = NULL;
|
||||
if (id != NULL)
|
||||
hyperLink = fHyperLinkForID.Get(id);
|
||||
if (hyperLink == NULL) {
|
||||
hyperLink = new (std::nothrow) HyperLink(uri, fNextOSCRef++, id);
|
||||
if (id != NULL)
|
||||
fHyperLinkForID.Put(id, hyperLink);
|
||||
HyperLink* oldHyperLink = fHyperLinkForRef.Get(hyperLink->OSCRef());
|
||||
fHyperLinkForRef.Put(hyperLink->OSCRef(), hyperLink);
|
||||
delete oldHyperLink;
|
||||
}
|
||||
return hyperLink->OSCRef();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TerminalBuffer::GetHyperLink(uint32 ref, HyperLink &_link)
|
||||
{
|
||||
HyperLink* hyperLink = fHyperLinkForRef.Get(ref);
|
||||
if (hyperLink == NULL) {
|
||||
return false;
|
||||
}
|
||||
_link = *hyperLink;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
#include <Locker.h>
|
||||
#include <Messenger.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <HashString.h>
|
||||
|
||||
#include "BasicTerminalBuffer.h"
|
||||
#include "HyperLink.h"
|
||||
|
||||
|
||||
class TerminalBuffer : public BasicTerminalBuffer, public BLocker {
|
||||
@@ -57,6 +61,9 @@ public:
|
||||
void SetMode(uint32 mode);
|
||||
void ResetMode(uint32 mode);
|
||||
|
||||
uint32 PutHyperLink(const char* id, BString& uri);
|
||||
bool GetHyperLink(uint32 ref, HyperLink& _link);
|
||||
|
||||
protected:
|
||||
virtual void NotifyListener();
|
||||
|
||||
@@ -78,6 +85,12 @@ private:
|
||||
|
||||
uint32 fMode;
|
||||
int fCursorStyle;
|
||||
|
||||
typedef HashMap<HashKey32<int32>, HyperLink*> HyperLinkRefMap;
|
||||
HyperLinkRefMap fHyperLinkForRef;
|
||||
typedef HashMap<HashString, HyperLink*> HyperLinkIDMap;
|
||||
HyperLinkIDMap fHyperLinkForID;
|
||||
uint32 fNextOSCRef;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@ struct Attributes {
|
||||
uint32 background;
|
||||
uint32 underline;
|
||||
int underlineStyle;
|
||||
uint32 hyperlink;
|
||||
|
||||
Attributes() : state(0), foreground(0), background(0), underline(0), underlineStyle(0) {}
|
||||
Attributes() : state(0), foreground(0), background(0), underline(0), underlineStyle(0),
|
||||
hyperlink(0) {}
|
||||
|
||||
inline void Reset()
|
||||
{
|
||||
@@ -41,6 +43,7 @@ struct Attributes {
|
||||
inline bool IsUnder() const { return (state & UNDERLINE) == UNDERLINE; }
|
||||
inline bool IsInverse() const { return (state & INVERSE) == INVERSE; }
|
||||
inline bool IsOver() const { return (state & OVERLINE) == OVERLINE; }
|
||||
inline bool IsHidden() const { return (state & HIDDEN) == HIDDEN; }
|
||||
inline bool IsMouse() const { return (state & MOUSE) == MOUSE; }
|
||||
inline bool IsForeSet() const { return (state & FORESET) == FORESET; }
|
||||
inline bool IsBackSet() const { return (state & BACKSET) == BACKSET; }
|
||||
@@ -96,6 +99,11 @@ struct Attributes {
|
||||
state |= UNDERLINE;
|
||||
}
|
||||
|
||||
inline void SetHyperlink(uint32 id)
|
||||
{
|
||||
hyperlink = id;
|
||||
}
|
||||
|
||||
inline void UnsetForeground()
|
||||
{
|
||||
state &= ~FORESET;
|
||||
@@ -159,6 +167,12 @@ struct Attributes {
|
||||
return underlineStyle;
|
||||
}
|
||||
|
||||
inline uint32
|
||||
Hyperlink() const
|
||||
{
|
||||
return hyperlink;
|
||||
}
|
||||
|
||||
inline Attributes&
|
||||
operator&=(uint32 value) { state &= value; return *this; }
|
||||
|
||||
@@ -178,7 +192,8 @@ struct Attributes {
|
||||
&& foreground == other.foreground
|
||||
&& background == other.background
|
||||
&& underline == other.underline
|
||||
&& underlineStyle == other.underlineStyle;
|
||||
&& underlineStyle == other.underlineStyle
|
||||
&& hyperlink == other.hyperlink;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@@ -188,7 +203,8 @@ struct Attributes {
|
||||
|| foreground != other.foreground
|
||||
|| background != other.background
|
||||
|| underline != other.underline
|
||||
|| underlineStyle != other.underlineStyle;
|
||||
|| underlineStyle != other.underlineStyle
|
||||
|| hyperlink != other.hyperlink;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -203,7 +219,8 @@ struct TerminalCell {
|
||||
return (attributes.state & CHAR_ATTRIBUTES)
|
||||
!= (other.state & CHAR_ATTRIBUTES)
|
||||
|| attributes.foreground != other.foreground
|
||||
|| attributes.background != other.background;
|
||||
|| attributes.background != other.background
|
||||
|| attributes.hyperlink != other.hyperlink;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1038,7 +1038,7 @@ CASE_IGNORE,
|
||||
CASE_IGNORE,
|
||||
/* SP ! " # */
|
||||
CASE_CSI_SP,
|
||||
CASE_ESC_IGNORE,
|
||||
CASE_CSI_EXCL,
|
||||
CASE_ESC_IGNORE,
|
||||
CASE_ESC_IGNORE,
|
||||
/* $ % & ' */
|
||||
@@ -1137,7 +1137,7 @@ CASE_SGR,
|
||||
CASE_CPR,
|
||||
CASE_GROUND_STATE,
|
||||
/* p q r s */
|
||||
CASE_GROUND_STATE,
|
||||
CASE_DECSTR,
|
||||
CASE_DECSCUSR_ETC,
|
||||
CASE_DECSTBM,
|
||||
CASE_GROUND_STATE,
|
||||
|
||||
@@ -104,4 +104,6 @@
|
||||
#define CASE_REP 101
|
||||
#define CASE_DEC_DOL 102
|
||||
#define CASE_DECRQM 103
|
||||
#define CASE_CSI_EXCL 104
|
||||
#define CASE_DECSTR 105
|
||||
|
||||
|
||||
Reference in New Issue
Block a user