You've already forked lazarus-ccr
fpspreadsheet: Modify hyperlink infrastructure to support non-label cells as well. Add hyperlinks to biff8 reader. Show hyperlink tooltip in TsWorksheetGrid.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3971 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -9,7 +9,6 @@
|
|||||||
<Title Value="demo_ctrls"/>
|
<Title Value="demo_ctrls"/>
|
||||||
<ResourceType Value="res"/>
|
<ResourceType Value="res"/>
|
||||||
<UseXPManifest Value="True"/>
|
<UseXPManifest Value="True"/>
|
||||||
<Icon Value="-1"/>
|
|
||||||
</General>
|
</General>
|
||||||
<i18n>
|
<i18n>
|
||||||
<EnableI18N LFM="False"/>
|
<EnableI18N LFM="False"/>
|
||||||
|
@ -559,7 +559,7 @@ object MainForm: TMainForm
|
|||||||
TabOrder = 0
|
TabOrder = 0
|
||||||
object CellIndicator: TsCellIndicator
|
object CellIndicator: TsCellIndicator
|
||||||
Left = 0
|
Left = 0
|
||||||
Height = 27
|
Height = 23
|
||||||
Top = 0
|
Top = 0
|
||||||
Width = 138
|
Width = 138
|
||||||
Align = alTop
|
Align = alTop
|
||||||
|
@ -405,7 +405,7 @@ end;
|
|||||||
|
|
||||||
procedure TMainForm.ToolButton4Click(Sender: TObject);
|
procedure TMainForm.ToolButton4Click(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
WorkbookSource.Worksheet.WriteHyperlink(0, 0, hkCell, 'B5', 'Go to B5');
|
WorkbookSource.Worksheet.WriteHyperlink(0, 0, '#Sheet2!B5', 'Go to B5');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.WorksheetGridClickHyperlink(Sender: TObject;
|
procedure TMainForm.WorksheetGridClickHyperlink(Sender: TObject;
|
||||||
|
@ -219,8 +219,7 @@ end;
|
|||||||
|
|
||||||
procedure TCellHelper.SetHyperlink(const AValue: TsHyperlink);
|
procedure TCellHelper.SetHyperlink(const AValue: TsHyperlink);
|
||||||
begin
|
begin
|
||||||
Worksheet.WriteHyperlink(@self, AValue.Kind, AValue.Target,
|
Worksheet.WriteHyperlink(@self, AValue.Target, AValue.Tooltip);
|
||||||
Worksheet.ReadAsUTF8Text(@self), AValue.Tooltip);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCellHelper.SetNumberFormat(const AValue: TsNumberFormat);
|
procedure TCellHelper.SetNumberFormat(const AValue: TsNumberFormat);
|
||||||
|
@ -156,7 +156,8 @@ type
|
|||||||
FPointSeparatorSettings: TFormatSettings;
|
FPointSeparatorSettings: TFormatSettings;
|
||||||
// Streams with the contents of files
|
// Streams with the contents of files
|
||||||
FSMeta, FSSettings, FSStyles, FSContent, FSMimeType, FSMetaInfManifest: TStream;
|
FSMeta, FSSettings, FSStyles, FSContent, FSMimeType, FSMetaInfManifest: TStream;
|
||||||
// Helpers
|
|
||||||
|
{ Helpers }
|
||||||
procedure CreateNumFormatList; override;
|
procedure CreateNumFormatList; override;
|
||||||
procedure CreateStreams;
|
procedure CreateStreams;
|
||||||
procedure DestroyStreams;
|
procedure DestroyStreams;
|
||||||
@ -164,7 +165,8 @@ type
|
|||||||
procedure ListAllNumFormats; override;
|
procedure ListAllNumFormats; override;
|
||||||
procedure ListAllRowStyles;
|
procedure ListAllRowStyles;
|
||||||
procedure ResetStreams;
|
procedure ResetStreams;
|
||||||
// Routines to write those files
|
|
||||||
|
{ Routines to write those files }
|
||||||
procedure WriteContent;
|
procedure WriteContent;
|
||||||
procedure WriteMimetype;
|
procedure WriteMimetype;
|
||||||
procedure WriteMetaInfManifest;
|
procedure WriteMetaInfManifest;
|
||||||
@ -172,23 +174,22 @@ type
|
|||||||
procedure WriteSettings;
|
procedure WriteSettings;
|
||||||
procedure WriteStyles;
|
procedure WriteStyles;
|
||||||
procedure WriteWorksheet(AStream: TStream; CurSheet: TsWorksheet);
|
procedure WriteWorksheet(AStream: TStream; CurSheet: TsWorksheet);
|
||||||
|
|
||||||
{ Record writing methods }
|
{ Record writing methods }
|
||||||
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
ACell: PCell); override;
|
ACell: PCell); override;
|
||||||
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: Boolean; ACell: PCell); override;
|
const AValue: Boolean; ACell: PCell); override;
|
||||||
|
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
|
const AValue: TDateTime; ACell: PCell); override;
|
||||||
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteError(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: TsErrorValue; ACell: PCell); override;
|
const AValue: TsErrorValue; ACell: PCell); override;
|
||||||
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
ACell: PCell); override;
|
ACell: PCell); override;
|
||||||
procedure WriteHyperlink(AStream: TStream; const ARow, ACol: Cardinal;
|
|
||||||
ACell: PCell); override;
|
|
||||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: string; ACell: PCell); override;
|
const AValue: string; ACell: PCell); override;
|
||||||
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: double; ACell: PCell); override;
|
const AValue: double; ACell: PCell); override;
|
||||||
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
|
|
||||||
const AValue: TDateTime; ACell: PCell); override;
|
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(AWorkbook: TsWorkbook); override;
|
constructor Create(AWorkbook: TsWorkbook); override;
|
||||||
@ -768,7 +769,7 @@ var
|
|||||||
begin
|
begin
|
||||||
Result := false;
|
Result := false;
|
||||||
|
|
||||||
if ACell^.ContentType = cctHyperlink then
|
if FWorksheet.IsHyperlink(ACell) then
|
||||||
FWorksheet.WriteFont(ACell, HYPERLINK_FONTINDEX)
|
FWorksheet.WriteFont(ACell, HYPERLINK_FONTINDEX)
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
@ -1466,7 +1467,7 @@ var
|
|||||||
nodeName: String;
|
nodeName: String;
|
||||||
s: String;
|
s: String;
|
||||||
cell: PCell;
|
cell: PCell;
|
||||||
hyperlink: TsHyperlink;
|
hyperlink: string;
|
||||||
begin
|
begin
|
||||||
{ We were forced to activate PreserveWhiteSpace in the DOMParser in order to
|
{ We were forced to activate PreserveWhiteSpace in the DOMParser in order to
|
||||||
catch the spaces inserted in formatting texts. However, this adds lots of
|
catch the spaces inserted in formatting texts. However, this adds lots of
|
||||||
@ -1477,7 +1478,7 @@ begin
|
|||||||
while Assigned(childnode) do
|
while Assigned(childnode) do
|
||||||
begin
|
begin
|
||||||
nodeName := childNode.NodeName;
|
nodeName := childNode.NodeName;
|
||||||
hyperlink.Kind := hkNone;
|
hyperlink := '';
|
||||||
if nodeName = 'text:p' then begin
|
if nodeName = 'text:p' then begin
|
||||||
subnode := childnode.FirstChild;
|
subnode := childnode.FirstChild;
|
||||||
while Assigned(subnode) do
|
while Assigned(subnode) do
|
||||||
@ -1486,28 +1487,7 @@ begin
|
|||||||
if nodename = 'text:a' then begin
|
if nodename = 'text:a' then begin
|
||||||
s := GetAttrValue(subnode, 'xlink:type');
|
s := GetAttrValue(subnode, 'xlink:type');
|
||||||
if s = 'simple' then
|
if s = 'simple' then
|
||||||
begin
|
hyperlink := GetAttrValue(subnode, 'xlink:href');
|
||||||
s := GetAttrValue(subnode, 'xlink:href');
|
|
||||||
if s <> '' then
|
|
||||||
begin
|
|
||||||
if s[1]='#' then
|
|
||||||
begin
|
|
||||||
hyperlink.Kind := hkCell;
|
|
||||||
hyperlink.Target := Copy(s, 2, Length(s));
|
|
||||||
end else
|
|
||||||
begin
|
|
||||||
hyperlink.Kind := hkURI;
|
|
||||||
hyperlink.Target := s;
|
|
||||||
{
|
|
||||||
if IsAbsoluteUri(s) then
|
|
||||||
hyperlink.Target := s
|
|
||||||
else
|
|
||||||
hyperlink.Target := FileNameToUri(s);
|
|
||||||
}
|
|
||||||
end;
|
|
||||||
hyperlink.Tooltip := '';
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
subnode := subnode.NextSibling;
|
subnode := subnode.NextSibling;
|
||||||
end;
|
end;
|
||||||
@ -1528,13 +1508,7 @@ begin
|
|||||||
spanNode := spanNode.NextSibling;
|
spanNode := spanNode.NextSibling;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
{
|
|
||||||
case childnode.NodeType of
|
|
||||||
TEXT_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE: ; // ignored
|
|
||||||
else
|
|
||||||
cellText := cellText + childnode.TextContent;
|
|
||||||
end;
|
|
||||||
}
|
|
||||||
childnode := childnode.NextSibling;
|
childnode := childnode.NextSibling;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1544,10 +1518,10 @@ begin
|
|||||||
cell := @FVirtualCell;
|
cell := @FVirtualCell;
|
||||||
end else
|
end else
|
||||||
cell := FWorksheet.GetCell(ARow, ACol);
|
cell := FWorksheet.GetCell(ARow, ACol);
|
||||||
if hyperlink.Kind = hkNone then
|
|
||||||
FWorkSheet.WriteUTF8Text(cell, cellText)
|
FWorkSheet.WriteUTF8Text(cell, cellText);
|
||||||
else
|
if hyperlink <> '' then
|
||||||
FWorksheet.WriteHyperlink(cell, hyperlink.Kind, hyperlink.Target, cellText, hyperlink.Tooltip);
|
FWorksheet.WriteHyperlink(cell, hyperlink);
|
||||||
|
|
||||||
styleName := GetAttrValue(ACellNode, 'table:style-name');
|
styleName := GetAttrValue(ACellNode, 'table:style-name');
|
||||||
ApplyStyleToCell(cell, stylename);
|
ApplyStyleToCell(cell, stylename);
|
||||||
@ -3495,6 +3469,10 @@ var
|
|||||||
begin
|
begin
|
||||||
Unused(ARow, ACol);
|
Unused(ARow, ACol);
|
||||||
|
|
||||||
|
// Hyperlink
|
||||||
|
if FWorksheet.IsHyperlink(ACell) then
|
||||||
|
FWorkbook.AddErrorMsg(rsODSHyperlinksOfTextCellsOnly, [GetCellString(ARow, ACol)]);
|
||||||
|
|
||||||
// Comment
|
// Comment
|
||||||
comment := WriteCommentXMLAsString(FWorksheet.ReadComment(ACell));
|
comment := WriteCommentXMLAsString(FWorksheet.ReadComment(ACell));
|
||||||
|
|
||||||
@ -3571,6 +3549,10 @@ begin
|
|||||||
DisplayStr := rsFALSE;
|
DisplayStr := rsFALSE;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Hyperlink
|
||||||
|
if FWorksheet.IsHyperlink(ACell) then
|
||||||
|
FWorkbook.AddErrorMsg(rsODSHyperlinksOfTextCellsOnly, [GetCellString(ARow, ACol)]);
|
||||||
|
|
||||||
AppendToStream(AStream, Format(
|
AppendToStream(AStream, Format(
|
||||||
'<table:table-cell office:value-type="%s" office:boolean-value="%s" %s %s >' +
|
'<table:table-cell office:value-type="%s" office:boolean-value="%s" %s %s >' +
|
||||||
comment +
|
comment +
|
||||||
@ -4068,6 +4050,10 @@ begin
|
|||||||
end else
|
end else
|
||||||
spannedStr := '';
|
spannedStr := '';
|
||||||
|
|
||||||
|
// Hyperlink
|
||||||
|
if FWorksheet.IsHyperlink(ACell) then
|
||||||
|
FWorkbook.AddErrorMsg(rsODSHyperlinksOfTextCellsOnly, [GetCellString(ARow, ACol)]);
|
||||||
|
|
||||||
// Convert string formula to the format needed by ods: semicolon list separators!
|
// Convert string formula to the format needed by ods: semicolon list separators!
|
||||||
parser := TsSpreadsheetParser.Create(FWorksheet);
|
parser := TsSpreadsheetParser.Create(FWorksheet);
|
||||||
try
|
try
|
||||||
@ -4150,15 +4136,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Writes a hyperlink
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure TsSpreadOpenDocWriter.WriteHyperlink(AStream: TStream;
|
|
||||||
const ARow, ACol: Cardinal; ACell: PCell);
|
|
||||||
begin
|
|
||||||
WriteLabel(AStream, ARow, ACol, ACell^.UTF8StringValue, ACell);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Writes a cell with text content
|
Writes a cell with text content
|
||||||
|
|
||||||
@ -4174,9 +4151,10 @@ var
|
|||||||
spannedStr: String;
|
spannedStr: String;
|
||||||
r1,c1,r2,c2: Cardinal;
|
r1,c1,r2,c2: Cardinal;
|
||||||
txt: ansistring;
|
txt: ansistring;
|
||||||
textp, link, comment: String;
|
textp, target, comment: String;
|
||||||
fmt: TsCellFormat;
|
fmt: TsCellFormat;
|
||||||
hyperlink: TsHyperlink;
|
hyperlink: PsHyperlink;
|
||||||
|
u: TUri;
|
||||||
begin
|
begin
|
||||||
Unused(ARow, ACol);
|
Unused(ARow, ACol);
|
||||||
|
|
||||||
@ -4208,17 +4186,24 @@ begin
|
|||||||
GetCellString(ARow, ACol)
|
GetCellString(ARow, ACol)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ACell^.ContentType = cctHyperlink then
|
if FWorksheet.IsHyperlink(ACell) then
|
||||||
begin
|
begin
|
||||||
hyperlink := FWorksheet.ReadHyperlink(ACell);
|
hyperlink := FWorksheet.FindHyperlink(ACell);
|
||||||
case hyperlink.Kind of
|
target := hyperlink^.Target;
|
||||||
hkCell: link := '#' + hyperlink.Target;
|
if target[1] <> '#' then
|
||||||
hkURI : link := hyperlink.Target;
|
begin
|
||||||
|
u := ParseURI(target);
|
||||||
|
if u.Protocol = '' then begin
|
||||||
|
//UriToFileName(hyperlink^.Target, target);
|
||||||
|
target := 'file:///' + ExpandFileName(target);
|
||||||
|
ValidXMLText(target);
|
||||||
|
// if not IsAbsoluteURI(target) then target := '..\' + target;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
textp := Format(
|
textp := Format(
|
||||||
'<text:p>'+
|
'<text:p>'+
|
||||||
'<text:a xlink:href="%s" xlink:type="simple">%s</text:a>'+
|
'<text:a xlink:href="%s" xlink:type="simple">%s</text:a>'+
|
||||||
'</text:p>', [link, txt]);
|
'</text:p>', [target, txt]);
|
||||||
end else
|
end else
|
||||||
textp := '<text:p>' + txt + '</text:p>';
|
textp := '<text:p>' + txt + '</text:p>';
|
||||||
|
|
||||||
@ -4283,6 +4268,10 @@ begin
|
|||||||
DisplayStr := FloatToStr(AValue); // Uses locale decimal separator
|
DisplayStr := FloatToStr(AValue); // Uses locale decimal separator
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Hyperlink
|
||||||
|
if FWorksheet.IsHyperlink(ACell) then
|
||||||
|
FWorkbook.AddErrorMsg(rsODSHyperlinksOfTextCellsOnly, [GetCellString(ARow, ACol)]);
|
||||||
|
|
||||||
AppendToStream(AStream, Format(
|
AppendToStream(AStream, Format(
|
||||||
'<table:table-cell office:value-type="%s" office:value="%s" %s %s >' +
|
'<table:table-cell office:value-type="%s" office:value="%s" %s %s >' +
|
||||||
comment +
|
comment +
|
||||||
@ -4335,6 +4324,10 @@ begin
|
|||||||
// Comment
|
// Comment
|
||||||
comment := WriteCommentXMLAsString(FWorksheet.ReadComment(ACell));
|
comment := WriteCommentXMLAsString(FWorksheet.ReadComment(ACell));
|
||||||
|
|
||||||
|
// Hyperlink
|
||||||
|
if FWorksheet.IsHyperlink(ACell) then
|
||||||
|
FWorkbook.AddErrorMsg(rsODSHyperlinksOfTextCellsOnly, [GetCellString(ARow, ACol)]);
|
||||||
|
|
||||||
// nfTimeInterval is a special case - let's handle it first:
|
// nfTimeInterval is a special case - let's handle it first:
|
||||||
|
|
||||||
if (fmt.NumberFormat = nfTimeInterval) then
|
if (fmt.NumberFormat = nfTimeInterval) then
|
||||||
|
@ -487,11 +487,13 @@ type
|
|||||||
function IsHyperlink(ACell: PCell): Boolean;
|
function IsHyperlink(ACell: PCell): Boolean;
|
||||||
function ReadHyperlink(ARow, ACol: Cardinal): TsHyperlink; overload;
|
function ReadHyperlink(ARow, ACol: Cardinal): TsHyperlink; overload;
|
||||||
function ReadHyperlink(ACell: PCell): TsHyperlink;
|
function ReadHyperlink(ACell: PCell): TsHyperlink;
|
||||||
procedure RemoveHyperlink(ACell: PCell; AKeepText: Boolean);
|
procedure RemoveHyperlink(ACell: PCell);
|
||||||
function WriteHyperlink(ARow, ACol: Cardinal; AKind: TsHyperlinkKind;
|
procedure SplitHyperlink(AValue: String; out ATarget, ABookmark: String);
|
||||||
ATarget: String; ADisplayText: String = ''; ATooltip: String = ''): PCell; overload;
|
function ValidHyperlink(AValue: String; out AErrMsg: String): Boolean;
|
||||||
procedure WriteHyperlink(ACell: PCell; AKind: TsHyperlinkKind;
|
function WriteHyperlink(ARow, ACol: Cardinal; ATarget: String;
|
||||||
ATarget: String; ADisplayText: String = ''; ATooltip: String = ''); overload;
|
ATooltip: String = ''): PCell; overload;
|
||||||
|
procedure WriteHyperlink(ACell: PCell; ATarget: String;
|
||||||
|
ATooltip: String = ''); overload;
|
||||||
|
|
||||||
{ Merged cells }
|
{ Merged cells }
|
||||||
procedure MergeCells(ARow1, ACol1, ARow2, ACol2: Cardinal); overload;
|
procedure MergeCells(ARow1, ACol1, ARow2, ACol2: Cardinal); overload;
|
||||||
@ -859,7 +861,7 @@ procedure DumpFontsToFile(AWorkbook: TsWorkbook; AFileName: String);
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Math, StrUtils, TypInfo, lazutf8,
|
Math, StrUtils, TypInfo, lazutf8, URIParser,
|
||||||
fpsPatches, fpsStrings, fpsStreams, uvirtuallayer_ole,
|
fpsPatches, fpsStrings, fpsStreams, uvirtuallayer_ole,
|
||||||
fpsUtils, fpsreaderwriter, fpsCurrency, fpsExprParser,
|
fpsUtils, fpsreaderwriter, fpsCurrency, fpsExprParser,
|
||||||
fpsNumFormat, fpsNumFormatParser;
|
fpsNumFormat, fpsNumFormatParser;
|
||||||
@ -1626,7 +1628,7 @@ end;
|
|||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
function TsWorksheet.IsHyperlink(ACell: PCell): Boolean;
|
function TsWorksheet.IsHyperlink(ACell: PCell): Boolean;
|
||||||
begin
|
begin
|
||||||
Result := (ACell <> nil) and (ACell^.ContentType = cctHyperlink);
|
Result := (ACell <> nil) and (cfHyperlink in ACell^.Flags);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
@ -1658,7 +1660,8 @@ begin
|
|||||||
Result := hyperlink^
|
Result := hyperlink^
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
Result.Kind := hkNone;
|
Result.Row := ACell^.Row;
|
||||||
|
Result.Col := ACell^.Col;
|
||||||
Result.Target := '';
|
Result.Target := '';
|
||||||
Result.Tooltip := '';
|
Result.Tooltip := '';
|
||||||
end;
|
end;
|
||||||
@ -1669,7 +1672,7 @@ end;
|
|||||||
the associated TsHyperlink record. Cell content type is converted to
|
the associated TsHyperlink record. Cell content type is converted to
|
||||||
cctUTF8String.
|
cctUTF8String.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorksheet.RemoveHyperlink(ACell: PCell; AKeepText: Boolean);
|
procedure TsWorksheet.RemoveHyperlink(ACell: PCell);
|
||||||
var
|
var
|
||||||
hyperlink: TsHyperlink;
|
hyperlink: TsHyperlink;
|
||||||
AVLNode: TAvlTreeNode;
|
AVLNode: TAvlTreeNode;
|
||||||
@ -1683,49 +1686,114 @@ begin
|
|||||||
if AVLNode <> nil then begin
|
if AVLNode <> nil then begin
|
||||||
Dispose(PsHyperlink(AVLNode.Data));
|
Dispose(PsHyperlink(AVLNode.Data));
|
||||||
FHyperlinks.Delete(AVLNode);
|
FHyperlinks.Delete(AVLNode);
|
||||||
if AKeepText then
|
Exclude(ACell^.Flags, cfHyperlink);
|
||||||
ACell^.ContentType := cctUTF8String
|
|
||||||
else
|
|
||||||
ACell^.ContentType := cctEmpty;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Separates the target and bookmark parts of a hyperlink (separated by '#').
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsWorksheet.SplitHyperlink(AValue: String; out ATarget, ABookmark: String);
|
||||||
|
var
|
||||||
|
p: Integer;
|
||||||
|
begin
|
||||||
|
p := pos('#', AValue);
|
||||||
|
if p = 0 then
|
||||||
|
begin
|
||||||
|
ATarget := AValue;
|
||||||
|
ABookmark := '';
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
ATarget := Copy(AValue, 1, p-1);
|
||||||
|
ABookmark := Copy(AValue, p+1, Length(AValue));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Checks whether the passed string represents a valid hyperlink target
|
||||||
|
|
||||||
|
@param AValue String to be checked. Must be either a fully qualified URI,
|
||||||
|
or a # followed by a cell address in the current workbook
|
||||||
|
@param AErrMsg Error message in case that the string is not correct.
|
||||||
|
@returns TRUE if the string is correct, FALSE otherwise
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsWorksheet.ValidHyperlink(AValue: String; out AErrMsg: String): Boolean;
|
||||||
|
var
|
||||||
|
uri: TUri;
|
||||||
|
mark: String;
|
||||||
|
sheet: TsWorksheet;
|
||||||
|
r, c: Cardinal;
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
AErrMsg := '';
|
||||||
|
if AValue = '' then
|
||||||
|
begin
|
||||||
|
AErrMsg := rsEmptyHyperlink;
|
||||||
|
exit;
|
||||||
|
end else
|
||||||
|
if (AValue[1] = '#') then
|
||||||
|
begin
|
||||||
|
Delete(AValue, 1, 1);
|
||||||
|
if not FWorkbook.TryStrToCell(AValue, sheet, r, c) then
|
||||||
|
begin
|
||||||
|
AErrMsg := Format(rsNoValidHyperlinkInternal, ['#'+AValue]);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
uri := ParseURI(AValue);
|
||||||
|
if SameText(uri.Protocol, 'mailto') then
|
||||||
|
begin
|
||||||
|
Result := true; // To do: Check email address here...
|
||||||
|
exit;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
Result := true;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Assigns a hyperlink to the cell at the specified row and column
|
Assigns a hyperlink to the cell at the specified row and column
|
||||||
|
Cell content is not affected by the presence of a hyperlink.
|
||||||
|
|
||||||
@param ARow Row index of the cell considered
|
@param ARow Row index of the cell considered
|
||||||
@param ACol Column index of the cell considered
|
@param ACol Column index of the cell considered
|
||||||
@param AKind Hyperlink type (to cell, external file, URL)
|
@param ATarget Hyperlink address given as a fully qualitifed URI for
|
||||||
@param ATarget Depending on AKind: cell address, filename, or URL
|
external links, or as a # followed by a cell address
|
||||||
if empty the hyperlink is removed from the cell.
|
for internal links.
|
||||||
@param ADisplayText Text shown in cell. If empty the destination is shown
|
|
||||||
@param ATooltip Text for popup tooltip hint used by Excel
|
@param ATooltip Text for popup tooltip hint used by Excel
|
||||||
@return Pointer to the cell with the hyperlink
|
@returns Pointer to the cell with the hyperlink
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
function TsWorksheet.WriteHyperlink(ARow, ACol: Cardinal; AKind: TsHyperlinkKind;
|
function TsWorksheet.WriteHyperlink(ARow, ACol: Cardinal; ATarget: String;
|
||||||
ATarget: String; ADisplayText: String = ''; ATooltip: String = ''): PCell;
|
ATooltip: String = ''): PCell;
|
||||||
begin
|
begin
|
||||||
Result := GetCell(ARow, ACol);
|
Result := GetCell(ARow, ACol);
|
||||||
WriteHyperlink(Result, AKind, ATarget, ADisplayText, ATooltip);
|
WriteHyperlink(Result, ATarget, ATooltip);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Assigns a hyperlink to the specified cell.
|
Assigns a hyperlink to the specified cell.
|
||||||
|
|
||||||
@param ACell Pointer to the cell considered
|
@param ACell Pointer to the cell considered
|
||||||
@param AKind Hyperlink type (to cell, external file, URL)
|
@param ATarget Hyperlink address given as a fully qualitifed URI for
|
||||||
@param ATarget Depending on AKind: cell address, filename, or URL
|
external links, or as a # followed by a cell address
|
||||||
if empty the hyperlink is removed from the cell.
|
for internal links. An existing hyperlink is removed if
|
||||||
@param ADisplayText Text shown in cell. If empty the destination is shown
|
ATarget is empty.
|
||||||
@param ATooltip Text for popup tooltip hint used by Excel
|
@param ATooltip Text for popup tooltip hint used by Excel
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorksheet.WriteHyperlink(ACell: PCell; AKind: TsHyperlinkKind;
|
procedure TsWorksheet.WriteHyperlink(ACell: PCell; ATarget: String;
|
||||||
ATarget: String; ADisplayText: String = ''; ATooltip: String = '');
|
ATooltip: String = '');
|
||||||
var
|
var
|
||||||
hyperlink: PsHyperlink;
|
hyperlink: PsHyperlink;
|
||||||
addNew: Boolean;
|
addNew: Boolean;
|
||||||
row, col: Cardinal;
|
row, col: Cardinal;
|
||||||
|
r, c: Cardinal;
|
||||||
fmt: TsCellFormat;
|
fmt: TsCellFormat;
|
||||||
|
fn: String;
|
||||||
|
err: String;
|
||||||
begin
|
begin
|
||||||
if ACell = nil then
|
if ACell = nil then
|
||||||
exit;
|
exit;
|
||||||
@ -1734,25 +1802,32 @@ begin
|
|||||||
col := ACell^.Col;
|
col := ACell^.Col;
|
||||||
|
|
||||||
// Remove the hyperlink if an empty destination is passed
|
// Remove the hyperlink if an empty destination is passed
|
||||||
if ATarget = '' then
|
if (ATarget = '') then
|
||||||
RemoveHyperlink(ACell, false)
|
RemoveHyperlink(ACell)
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
|
{
|
||||||
|
if not ValidHyperlink(ATarget, err) then
|
||||||
|
raise Exception.Create(err);
|
||||||
|
}
|
||||||
hyperlink := FindHyperlink(ACell);
|
hyperlink := FindHyperlink(ACell);
|
||||||
addNew := (hyperlink = nil);
|
addNew := (hyperlink = nil);
|
||||||
if addNew then New(hyperlink);
|
if addNew then New(hyperlink);
|
||||||
hyperlink^.Row := row;
|
hyperlink^.Row := row;
|
||||||
hyperlink^.Col := col;
|
hyperlink^.Col := col;
|
||||||
hyperlink^.Kind := AKind;
|
|
||||||
hyperlink^.Target := ATarget;
|
hyperlink^.Target := ATarget;
|
||||||
hyperlink^.Tooltip := ATooltip;
|
hyperlink^.Tooltip := ATooltip;
|
||||||
if addNew then FHyperlinks.Add(hyperlink);
|
if addNew then FHyperlinks.Add(hyperlink);
|
||||||
|
Include(ACell^.Flags, cfHyperlink);
|
||||||
|
|
||||||
ACell^.ContentType := cctHyperlink;
|
if ACell^.ContentType = cctEmpty then
|
||||||
if ADisplayText <> '' then
|
begin
|
||||||
ACell^.UTF8StringValue := ADisplayText
|
ACell^.ContentType := cctUTF8String;
|
||||||
else
|
if UriToFileName(hyperlink^.Target, fn) then
|
||||||
ACell^.UTF8StringValue := ATarget;
|
ACell^.UTF8StringValue := fn
|
||||||
|
else
|
||||||
|
ACell^.UTF8StringValue := hyperlink^.Target;
|
||||||
|
end;
|
||||||
|
|
||||||
fmt := ReadCellFormat(ACell);
|
fmt := ReadCellFormat(ACell);
|
||||||
if fmt.FontIndex = DEFAULT_FONTINDEX then
|
if fmt.FontIndex = DEFAULT_FONTINDEX then
|
||||||
@ -2744,6 +2819,7 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell;
|
|||||||
|
|
||||||
var
|
var
|
||||||
fmt: PsCellFormat;
|
fmt: PsCellFormat;
|
||||||
|
hyperlink: PsHyperlink;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Result := '';
|
Result := '';
|
||||||
@ -2755,8 +2831,7 @@ begin
|
|||||||
case ContentType of
|
case ContentType of
|
||||||
cctNumber:
|
cctNumber:
|
||||||
Result := FloatToStrNoNaN(NumberValue, fmt^.NumberFormat, fmt^.NumberFormatStr);
|
Result := FloatToStrNoNaN(NumberValue, fmt^.NumberFormat, fmt^.NumberFormatStr);
|
||||||
cctUTF8String,
|
cctUTF8String:
|
||||||
cctHyperlink:
|
|
||||||
Result := UTF8StringValue;
|
Result := UTF8StringValue;
|
||||||
cctDateTime:
|
cctDateTime:
|
||||||
Result := DateTimeToStrNoNaN(DateTimeValue, fmt^.NumberFormat, fmt^.NumberFormatStr);
|
Result := DateTimeToStrNoNaN(DateTimeValue, fmt^.NumberFormat, fmt^.NumberFormatStr);
|
||||||
@ -2775,6 +2850,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
else
|
else
|
||||||
Result := '';
|
Result := '';
|
||||||
|
if IsHyperlink(ACell) then
|
||||||
|
begin
|
||||||
|
hyperlink := FindHyperlink(ACell);
|
||||||
|
if hyperlink <> nil then Result := hyperlink^.Target;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -4602,44 +4682,9 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if IsHyperlink(ACell) then
|
WriteUTF8Text(ACell, AValue);
|
||||||
begin
|
|
||||||
// Preserve hyperlinks. Modify only the display test.
|
|
||||||
WriteUTF8Text(ACell, AValue);
|
|
||||||
ACell^.ContentType := cctHyperlink;
|
|
||||||
end else
|
|
||||||
WriteUTF8Text(ACell, AValue);
|
|
||||||
end;
|
|
||||||
(*
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Assigns a comment to a cell
|
|
||||||
|
|
||||||
@param ARow Cell row index
|
|
||||||
@param ACol Cell column index
|
|
||||||
@param AComment Text to be used as comment. Can contain line-breaks.
|
|
||||||
@return Pointer to the cell
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
function TsWorksheet.WriteComment(ARow, ACol: Cardinal;
|
|
||||||
const AComment: String): PCell;
|
|
||||||
begin
|
|
||||||
Result := GetCell(ARow, ACol);
|
|
||||||
WriteComment(Result, AComment);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Assigns a comment to a cell
|
|
||||||
|
|
||||||
@param ACell Pointer to the cell
|
|
||||||
@param AComment Text to be used as comment. Can contain line-breaks.
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure TsWorksheet.WriteComment(ACell: PCell; const AComment: String);
|
|
||||||
begin
|
|
||||||
if ACell <> nil then begin
|
|
||||||
ACell^.Comment := AComment;
|
|
||||||
ChangedCell(ACell^.Row, ACell^.Col);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Writes a currency value to a given cell. Its number format can be provided
|
Writes a currency value to a given cell. Its number format can be provided
|
||||||
optionally by specifying various parameters.
|
optionally by specifying various parameters.
|
||||||
|
@ -2648,6 +2648,7 @@ end;
|
|||||||
procedure TsSpreadsheetInspector.UpdateCellValue(ACell: PCell; AStrings: TStrings);
|
procedure TsSpreadsheetInspector.UpdateCellValue(ACell: PCell; AStrings: TStrings);
|
||||||
var
|
var
|
||||||
hyperlink: PsHyperlink;
|
hyperlink: PsHyperlink;
|
||||||
|
comment: String;
|
||||||
s: String;
|
s: String;
|
||||||
begin
|
begin
|
||||||
if ACell = nil then
|
if ACell = nil then
|
||||||
@ -2680,22 +2681,6 @@ begin
|
|||||||
AStrings.Add(Format('UTF8StringValue=%s', [ACell^.UTF8StringValue]));
|
AStrings.Add(Format('UTF8StringValue=%s', [ACell^.UTF8StringValue]));
|
||||||
if ACell^.ContentType = cctBool then
|
if ACell^.ContentType = cctBool then
|
||||||
AStrings.Add(Format('BoolValue=%s', [BoolToStr(ACell^.BoolValue)]));
|
AStrings.Add(Format('BoolValue=%s', [BoolToStr(ACell^.BoolValue)]));
|
||||||
if ACell^.ContentType = cctHyperlink then
|
|
||||||
begin
|
|
||||||
AStrings.Add(Format('UTF8StringValue=%s', [ACell^.UTF8StringValue]));
|
|
||||||
hyperlink := Worksheet.FindHyperlink(ACell);
|
|
||||||
if hyperlink <> nil then
|
|
||||||
begin
|
|
||||||
s := hyperlink^.Target;
|
|
||||||
case hyperlink^.Kind of
|
|
||||||
hkNone: s := s + ' <error>';
|
|
||||||
hkCell: s := s + ' (internal cell reference)';
|
|
||||||
hkURI : s := s + ' (external URI)';
|
|
||||||
end;
|
|
||||||
end else
|
|
||||||
s := '<error>';
|
|
||||||
AStrings.Add(Format('Hyperlink=%s', [s]));
|
|
||||||
end;
|
|
||||||
if ACell^.ContentType = cctError then
|
if ACell^.ContentType = cctError then
|
||||||
AStrings.Add(Format('ErrorValue=%s', [GetEnumName(TypeInfo(TsErrorValue), ord(ACell^.ErrorValue))]));
|
AStrings.Add(Format('ErrorValue=%s', [GetEnumName(TypeInfo(TsErrorValue), ord(ACell^.ErrorValue))]));
|
||||||
AStrings.Add(Format('FormulaValue=%s', [Worksheet.ReadFormulaAsString(ACell, true)]));
|
AStrings.Add(Format('FormulaValue=%s', [Worksheet.ReadFormulaAsString(ACell, true)]));
|
||||||
@ -2705,6 +2690,23 @@ begin
|
|||||||
AStrings.Add(Format('SharedFormulaBase=%s', [GetCellString(
|
AStrings.Add(Format('SharedFormulaBase=%s', [GetCellString(
|
||||||
ACell^.SharedFormulaBase^.Row, ACell^.SharedFormulaBase^.Col)
|
ACell^.SharedFormulaBase^.Row, ACell^.SharedFormulaBase^.Col)
|
||||||
]));
|
]));
|
||||||
|
if (cfHyperlink in ACell^.Flags) then
|
||||||
|
begin
|
||||||
|
hyperlink := Worksheet.FindHyperlink(ACell);
|
||||||
|
if hyperlink <> nil then
|
||||||
|
begin
|
||||||
|
if hyperlink^.Tooltip <> '' then
|
||||||
|
s := hyperlink^.Target + ' (tooltip: ' + hyperlink^.Tooltip + ')'
|
||||||
|
else
|
||||||
|
s := hyperlink^.Target;
|
||||||
|
AStrings.Add(Format('Hyperlink=%s', [s]));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if (cfHasComment in ACell^.Flags) then
|
||||||
|
begin
|
||||||
|
comment := Worksheet.ReadComment(ACell);
|
||||||
|
AStrings.Add(Format('Comment=%s', [comment]));
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -1024,7 +1024,7 @@ begin
|
|||||||
cell := FDrawingCell;
|
cell := FDrawingCell;
|
||||||
|
|
||||||
// Nothing to do in these cases (like in Excel):
|
// Nothing to do in these cases (like in Excel):
|
||||||
if (cell = nil) or not (cell^.ContentType in [cctUTF8String, cctHyperlink]) then // ... non-label cells
|
if (cell = nil) or not (cell^.ContentType in [cctUTF8String]) then // ... non-label cells
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
|
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
|
||||||
@ -1840,13 +1840,13 @@ begin
|
|||||||
cell := Worksheet.FindCell(sr, GetWorksheetCol(gc));
|
cell := Worksheet.FindCell(sr, GetWorksheetCol(gc));
|
||||||
// Empty cell --> proceed with next cell to the left
|
// Empty cell --> proceed with next cell to the left
|
||||||
if (cell = nil) or (cell^.ContentType = cctEmpty) or
|
if (cell = nil) or (cell^.ContentType = cctEmpty) or
|
||||||
((cell^.ContentType in [cctUTF8String, cctHyperLink]) and (cell^.UTF8StringValue = ''))
|
((cell^.ContentType = cctUTF8String) and (cell^.UTF8StringValue = ''))
|
||||||
then
|
then
|
||||||
Continue;
|
Continue;
|
||||||
// Overflow possible from non-merged, non-right-aligned, horizontal label cells
|
// Overflow possible from non-merged, non-right-aligned, horizontal label cells
|
||||||
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
|
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
|
||||||
if (not Worksheet.IsMerged(cell)) and
|
if (not Worksheet.IsMerged(cell)) and
|
||||||
(cell^.ContentType in [cctUTF8String, cctHyperlink]) and
|
(cell^.ContentType = cctUTF8String) and
|
||||||
not (uffTextRotation in fmt^.UsedFormattingFields) and
|
not (uffTextRotation in fmt^.UsedFormattingFields) and
|
||||||
(uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haRight)
|
(uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haRight)
|
||||||
then
|
then
|
||||||
@ -1867,13 +1867,13 @@ begin
|
|||||||
cell := Worksheet.FindCell(sr, GetWorksheetCol(gcLast));
|
cell := Worksheet.FindCell(sr, GetWorksheetCol(gcLast));
|
||||||
// Empty cell --> proceed with next cell to the right
|
// Empty cell --> proceed with next cell to the right
|
||||||
if (cell = nil) or (cell^.ContentType = cctEmpty) or
|
if (cell = nil) or (cell^.ContentType = cctEmpty) or
|
||||||
((cell^.ContentType in [cctUTF8String, cctHyperlink]) and (cell^.UTF8StringValue = ''))
|
((cell^.ContentType = cctUTF8String) and (cell^.UTF8StringValue = ''))
|
||||||
then
|
then
|
||||||
continue;
|
continue;
|
||||||
// Overflow possible from non-merged, horizontal, non-left-aligned label cells
|
// Overflow possible from non-merged, horizontal, non-left-aligned label cells
|
||||||
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
|
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
|
||||||
if (not Worksheet.IsMerged(cell)) and
|
if (not Worksheet.IsMerged(cell)) and
|
||||||
(cell^.ContentType in [cctUTF8String, cctHyperlink]) and
|
(cell^.ContentType = cctUTF8String) and
|
||||||
not (uffTextRotation in fmt^.UsedFormattingFields) and
|
not (uffTextRotation in fmt^.UsedFormattingFields) and
|
||||||
(uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haLeft)
|
(uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haLeft)
|
||||||
then
|
then
|
||||||
@ -2161,6 +2161,7 @@ end;
|
|||||||
procedure TsCustomWorksheetGrid.ExecuteHyperlink;
|
procedure TsCustomWorksheetGrid.ExecuteHyperlink;
|
||||||
var
|
var
|
||||||
hyperlink: TsHyperlink;
|
hyperlink: TsHyperlink;
|
||||||
|
target, bookmark: String;
|
||||||
sheetname: String;
|
sheetname: String;
|
||||||
sheet: TsWorksheet;
|
sheet: TsWorksheet;
|
||||||
r, c: Cardinal;
|
r, c: Cardinal;
|
||||||
@ -2169,27 +2170,24 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
|
|
||||||
hyperlink := Worksheet.ReadHyperlink(FHyperlinkCell);
|
hyperlink := Worksheet.ReadHyperlink(FHyperlinkCell);
|
||||||
case hyperlink.Kind of
|
Worksheet.SplitHyperlink(hyperlink.Target, target, bookmark);
|
||||||
hkNone:
|
if target = '' then begin
|
||||||
; // nothing to do
|
// Goes to a cell within the current workbook
|
||||||
hkCell:
|
if ParseSheetCellString(bookmark, sheetname, r, c) then
|
||||||
// Goes to a cell (unlike Excel, we don't support range here)
|
begin
|
||||||
if ParseSheetCellString(hyperlink.Target, sheetname, r, c) then
|
if sheetname <> '' then
|
||||||
begin
|
begin
|
||||||
if sheetname <> '' then
|
sheet := Workbook.GetWorksheetByName(sheetname);
|
||||||
begin
|
if sheet = nil then
|
||||||
sheet := Workbook.GetWorksheetByName(sheetname);
|
raise Exception.CreateFmt(rsWorksheetNotFound, [sheetname]);
|
||||||
if sheet = nil then
|
Workbook.SelectWorksheet(sheet);
|
||||||
raise Exception.CreateFmt(rsWorksheetNotFound, [sheetname]);
|
end;
|
||||||
Workbook.SelectWorksheet(sheet);
|
Worksheet.SelectCell(r, c);
|
||||||
end;
|
end else
|
||||||
Worksheet.SelectCell(r, c);
|
raise Exception.CreateFmt(rsNoValidHyperlinkInternal, [hyperlink.Target]);
|
||||||
end else
|
end else
|
||||||
raise Exception.CreateFmt(rsHyperlinkNotAValidCell, [hyperlink.Target]);
|
// Fires the OnClickHyperlink event which should open a file or a URL
|
||||||
else
|
if Assigned(FOnClickHyperlink) then FOnClickHyperlink(self, hyperlink);
|
||||||
// Fires the OnClickHyperlink event which should open a file or a URL
|
|
||||||
if Assigned(FOnClickHyperlink) then FOnClickHyperlink(self, hyperlink);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -2637,16 +2635,44 @@ end;
|
|||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
This function defines the text to be displayed as a cell hint. By default, it
|
This function defines the text to be displayed as a cell hint. By default, it
|
||||||
is the comment attached to a cell; it can further be modified by using the
|
is the comment and/or the hyperlink attached to a cell; it can further be
|
||||||
OnGetCellHint event.
|
modified by using the OnGetCellHint event.
|
||||||
Option goCellHints must be active for the cell hint feature to work.
|
Option goCellHints must be active for the cell hint feature to work.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
function TsCustomWorksheetGrid.GetCellHintText(ACol, ARow: Integer): String;
|
function TsCustomWorksheetGrid.GetCellHintText(ACol, ARow: Integer): String;
|
||||||
var
|
var
|
||||||
cell: PCell;
|
cell: PCell;
|
||||||
|
hyperlink: PsHyperlink;
|
||||||
|
comment: String;
|
||||||
begin
|
begin
|
||||||
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
||||||
Result := Worksheet.ReadComment(cell);
|
if cell = nil then
|
||||||
|
Result := ''
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// Read comment
|
||||||
|
comment := Worksheet.ReadComment(cell);
|
||||||
|
// Read hyperlink info
|
||||||
|
if Worksheet.IsHyperlink(cell) then begin
|
||||||
|
hyperlink := Worksheet.FindHyperlink(cell);
|
||||||
|
if hyperlink <> nil then
|
||||||
|
begin
|
||||||
|
if hyperlink^.ToolTip <> '' then
|
||||||
|
Result := hyperlink^.ToolTip
|
||||||
|
else
|
||||||
|
Result := Format('Hyperlink: %s' + LineEnding + rsStdHyperlinkTooltip,
|
||||||
|
[hyperlink^.Target]
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
// Combine comment and hyperlink
|
||||||
|
if (Result <> '') and (comment <> '') then
|
||||||
|
Result := comment + LineEnding + LineEnding + Result
|
||||||
|
else
|
||||||
|
if (Result = '') and (comment <> '') then
|
||||||
|
Result := comment;
|
||||||
|
end;
|
||||||
|
|
||||||
if Assigned(OnGetCellHint) then
|
if Assigned(OnGetCellHint) then
|
||||||
OnGetCellHint(self, ACol, ARow, Result);
|
OnGetCellHint(self, ACol, ARow, Result);
|
||||||
end;
|
end;
|
||||||
@ -3772,7 +3798,7 @@ var
|
|||||||
fmt: PsCellFormat;
|
fmt: PsCellFormat;
|
||||||
begin
|
begin
|
||||||
Result := Worksheet.ReadAsUTF8Text(ACell);
|
Result := Worksheet.ReadAsUTF8Text(ACell);
|
||||||
if (Result = '') or ((ACell <> nil) and (ACell^.ContentType in [cctUTF8String, cctHyperlink]))
|
if (Result = '') or ((ACell <> nil) and (ACell^.ContentType = cctUTF8String))
|
||||||
then
|
then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
|
@ -115,8 +115,6 @@ type
|
|||||||
const AValue: TsErrorValue; ACell: PCell); virtual; abstract;
|
const AValue: TsErrorValue; ACell: PCell); virtual; abstract;
|
||||||
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
ACell: PCell); virtual;
|
ACell: PCell); virtual;
|
||||||
procedure WriteHyperlink(AStream: TStream; const ARow, ACol: Cardinal;
|
|
||||||
ACell: PCell); virtual;
|
|
||||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: string; ACell: PCell); virtual; abstract;
|
const AValue: string; ACell: PCell); virtual; abstract;
|
||||||
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
@ -582,8 +580,6 @@ begin
|
|||||||
WriteNumber(AStream, ACell^.Row, ACell^.Col, ACell^.NumberValue, ACell);
|
WriteNumber(AStream, ACell^.Row, ACell^.Col, ACell^.NumberValue, ACell);
|
||||||
cctUTF8String:
|
cctUTF8String:
|
||||||
WriteLabel(AStream, ACell^.Row, ACell^.Col, ACell^.UTF8StringValue, ACell);
|
WriteLabel(AStream, ACell^.Row, ACell^.Col, ACell^.UTF8StringValue, ACell);
|
||||||
cctHyperlink:
|
|
||||||
WriteHyperlink(AStream, ACell^.Row, ACell^.Col, ACell);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if FWorksheet.ReadComment(ACell) <> '' then
|
if FWorksheet.ReadComment(ACell) <> '' then
|
||||||
@ -636,23 +632,6 @@ begin
|
|||||||
Unused(ARow, ACol, ACell);
|
Unused(ARow, ACol, ACell);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Basic method which is called when writing a hyperlink to a stream.
|
|
||||||
Present implementation does nothing. Needs to be overridden by descendants.
|
|
||||||
|
|
||||||
@param AStream Stream to be written
|
|
||||||
@param ARow Row index of the cell containing the hyperlink
|
|
||||||
@param ACol Column index of the cell containing the formula
|
|
||||||
@param ACell Pointer to the cell containing the hyperlink and
|
|
||||||
being written to the stream
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure TsCustomSpreadWriter.WriteHyperlink(AStream: TStream;
|
|
||||||
const ARow, ACol: Cardinal; ACell: PCell);
|
|
||||||
begin
|
|
||||||
Unused(AStream);
|
|
||||||
Unused(ARow, ACol, ACell);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Default file writing method.
|
Default file writing method.
|
||||||
|
|
||||||
|
@ -55,7 +55,11 @@ resourcestring
|
|||||||
rsIndexInSSTOutOfRange = 'Index %d in SST out of range (0-%d).';
|
rsIndexInSSTOutOfRange = 'Index %d in SST out of range (0-%d).';
|
||||||
rsAmbiguousDecThouSeparator = 'Assuming usage of decimal separator in "%s".';
|
rsAmbiguousDecThouSeparator = 'Assuming usage of decimal separator in "%s".';
|
||||||
rsCodePageNotSupported = 'Code page "%s" is not supported. Using "cp1252" (Latin 1) instead.';
|
rsCodePageNotSupported = 'Code page "%s" is not supported. Using "cp1252" (Latin 1) instead.';
|
||||||
rsHyperlinkNotAValidCell = 'Hyperlink target "%s" is not a valid cell address.';
|
rsNoValidHyperlinkInternal = 'The hyperlink "%s" is not a valid cell address.';
|
||||||
|
rsNoValidHyperlinkURI = 'The hyperlink "%s" is not a valid URI.';
|
||||||
|
rsEmptyHyperlink = 'The hyperlink is not specified.';
|
||||||
|
rsODSHyperlinksOfTextCellsOnly = 'Cell %s: OpenDocument supports hyperlinks for text cells only.';
|
||||||
|
rsStdHyperlinkTooltip = 'Press the left mouse button a bit longer to activate the hyperlink.';
|
||||||
|
|
||||||
|
|
||||||
rsTRUE = 'TRUE'; // wp: Do we really want to translate these strings?
|
rsTRUE = 'TRUE'; // wp: Do we really want to translate these strings?
|
||||||
|
@ -130,7 +130,7 @@ type
|
|||||||
|
|
||||||
{@@ Describes the <b>type of content</b> in a cell of a TsWorksheet }
|
{@@ Describes the <b>type of content</b> in a cell of a TsWorksheet }
|
||||||
TCellContentType = (cctEmpty, cctFormula, cctNumber, cctUTF8String,
|
TCellContentType = (cctEmpty, cctFormula, cctNumber, cctUTF8String,
|
||||||
cctDateTime, cctBool, cctError, cctHyperlink);
|
cctDateTime, cctBool, cctError);
|
||||||
|
|
||||||
{@@ The record TsComment describes a comment attached to a cell.
|
{@@ The record TsComment describes a comment attached to a cell.
|
||||||
@param Row (0-based) row index of the cell
|
@param Row (0-based) row index of the cell
|
||||||
@ -143,22 +143,19 @@ type
|
|||||||
|
|
||||||
{@@ Pointer to a TsComment record }
|
{@@ Pointer to a TsComment record }
|
||||||
PsComment = ^TsComment;
|
PsComment = ^TsComment;
|
||||||
|
(*
|
||||||
{@@ Specifies whether a hyperlink refers to a cell address within the current
|
{@@ Specifies whether a hyperlink refers to an internal cell address
|
||||||
workbook, or a URI }
|
within the current workbook, or a URI (file://, http://, mailto, etc). }
|
||||||
TsHyperlinkKind = (hkNone, hkCell, hkURI);
|
TsHyperlinkKind = (hkNone, hkInternal, hkURI);
|
||||||
|
*)
|
||||||
{@@ The record TsHyperlink contains info on a hyperlink in a cell
|
{@@ The record TsHyperlink contains info on a hyperlink in a cell
|
||||||
@param Row Row index of the cell containing the hyperlink
|
@param Row Row index of the cell containing the hyperlink
|
||||||
@param Col Column index of the cell containing the hyperlink
|
@param Col Column index of the cell containing the hyperlink
|
||||||
@param Kind Specifies whether clicking on the hyperlink results in
|
@param Target Target of hyperlink: URI of file, web link, mail; or:
|
||||||
jumping the a cell address within the current workbook,
|
internal link (# followed by cell address)
|
||||||
or opens a URL
|
|
||||||
@param Target Target of hyperlink (cell address, filename, URL)
|
|
||||||
@param Note Text displayed as a popup hint by Excel }
|
@param Note Text displayed as a popup hint by Excel }
|
||||||
TsHyperlink = record
|
TsHyperlink = record
|
||||||
Row, Col: Cardinal;
|
Row, Col: Cardinal;
|
||||||
Kind: TsHyperlinkKind;
|
|
||||||
Target: String;
|
Target: String;
|
||||||
Tooltip: String;
|
Tooltip: String;
|
||||||
end;
|
end;
|
||||||
@ -450,7 +447,7 @@ type
|
|||||||
TsCalcState = (csNotCalculated, csCalculating, csCalculated);
|
TsCalcState = (csNotCalculated, csCalculating, csCalculated);
|
||||||
|
|
||||||
{@@ Cell flag }
|
{@@ Cell flag }
|
||||||
TsCellFlag = (cfCalculating, cfCalculated, cfHasComment, cfMerged);
|
TsCellFlag = (cfCalculating, cfCalculated, cfHasComment, cfHyperlink, cfMerged);
|
||||||
|
|
||||||
{@@ Set of cell flags }
|
{@@ Set of cell flags }
|
||||||
TsCellFlags = set of TsCellFlag;
|
TsCellFlags = set of TsCellFlag;
|
||||||
|
@ -87,6 +87,8 @@ type
|
|||||||
procedure ReadCONTINUE(const AStream: TStream);
|
procedure ReadCONTINUE(const AStream: TStream);
|
||||||
procedure ReadFONT(const AStream: TStream);
|
procedure ReadFONT(const AStream: TStream);
|
||||||
procedure ReadFORMAT(AStream: TStream); override;
|
procedure ReadFORMAT(AStream: TStream); override;
|
||||||
|
procedure ReadHyperLink(AStream: TStream);
|
||||||
|
procedure ReadHyperlinkToolTip(AStream: TStream);
|
||||||
procedure ReadLABEL(AStream: TStream); override;
|
procedure ReadLABEL(AStream: TStream); override;
|
||||||
procedure ReadLabelSST(const AStream: TStream);
|
procedure ReadLabelSST(const AStream: TStream);
|
||||||
procedure ReadMergedCells(const AStream: TStream);
|
procedure ReadMergedCells(const AStream: TStream);
|
||||||
@ -135,12 +137,10 @@ type
|
|||||||
procedure WriteFont(AStream: TStream; AFont: TsFont);
|
procedure WriteFont(AStream: TStream; AFont: TsFont);
|
||||||
procedure WriteFonts(AStream: TStream);
|
procedure WriteFonts(AStream: TStream);
|
||||||
procedure WriteIndex(AStream: TStream);
|
procedure WriteIndex(AStream: TStream);
|
||||||
procedure WriteHyperlink(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteHyperlink(AStream: TStream; AHyperlink: PsHyperlink;
|
||||||
ACell: PCell); override;
|
|
||||||
procedure WriteHYPERLINKRecord(AStream: TStream; AHyperlink: PsHyperlink;
|
|
||||||
AWorksheet: TsWorksheet);
|
AWorksheet: TsWorksheet);
|
||||||
procedure WriteHyperlinks(AStream: TStream; AWorksheet: TsWorksheet);
|
procedure WriteHyperlinks(AStream: TStream; AWorksheet: TsWorksheet);
|
||||||
procedure WriteHYPERLINKTOOLTIP(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteHyperlinkToolTip(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const ATooltip: String);
|
const ATooltip: String);
|
||||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: string; ACell: PCell); override;
|
const AValue: string; ACell: PCell); override;
|
||||||
@ -259,7 +259,7 @@ var
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Math, lconvencoding, URIParser, DOS,
|
Math, lconvencoding, URIParser,
|
||||||
fpsStrings, fpsStreams, fpsReaderWriter, fpsExprParser, xlsEscher;
|
fpsStrings, fpsStreams, fpsReaderWriter, fpsExprParser, xlsEscher;
|
||||||
|
|
||||||
const
|
const
|
||||||
@ -270,7 +270,7 @@ const
|
|||||||
INT_EXCEL_ID_LABELSST = $00FD; // BIFF8 only
|
INT_EXCEL_ID_LABELSST = $00FD; // BIFF8 only
|
||||||
INT_EXCEL_ID_TXO = $01B6; // BIFF8 only
|
INT_EXCEL_ID_TXO = $01B6; // BIFF8 only
|
||||||
INT_EXCEL_ID_HYPERLINK = $01B8; // BIFF8 only
|
INT_EXCEL_ID_HYPERLINK = $01B8; // BIFF8 only
|
||||||
INT_EXCEL_ID_HYPERLINKTOOLTIP = $0800; // BIFF8 only
|
INT_EXCEL_ID_HLINKTOOLTIP = $0800; // BIFF8 only
|
||||||
{%H-}INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3;
|
{%H-}INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3;
|
||||||
|
|
||||||
{ Excel OBJ subrecord IDs }
|
{ Excel OBJ subrecord IDs }
|
||||||
@ -688,6 +688,8 @@ begin
|
|||||||
INT_EXCEL_ID_BOOLERROR : ReadBool(AStream);
|
INT_EXCEL_ID_BOOLERROR : ReadBool(AStream);
|
||||||
INT_EXCEL_ID_CONTINUE : ReadCONTINUE(AStream);
|
INT_EXCEL_ID_CONTINUE : ReadCONTINUE(AStream);
|
||||||
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
||||||
|
INT_EXCEL_ID_HYPERLINK : ReadHyperlink(AStream);
|
||||||
|
INT_EXCEL_ID_HLINKTOOLTIP: ReadHyperlinkToolTip(AStream);
|
||||||
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
|
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
|
||||||
INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream);
|
INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream);
|
||||||
INT_EXCEL_ID_NOTE : ReadNOTE(AStream);
|
INT_EXCEL_ID_NOTE : ReadNOTE(AStream);
|
||||||
@ -1418,11 +1420,182 @@ begin
|
|||||||
NumFormatList.AnalyzeAndAdd(fmtIndex, fmtString);
|
NumFormatList.AnalyzeAndAdd(fmtIndex, fmtString);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Reads a HYPERLINK record
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsSpreadBIFF8Reader.ReadHyperlink(AStream: TStream);
|
||||||
|
var
|
||||||
|
row, col, row1, col1, row2, col2: word;
|
||||||
|
guid: TGUID;
|
||||||
|
flags: DWord;
|
||||||
|
widestr: widestring;
|
||||||
|
len: DWord;
|
||||||
|
isInternal: Boolean;
|
||||||
|
link: String;
|
||||||
|
linkDos: String;
|
||||||
|
mark: String;
|
||||||
|
dirUpCount: Word;
|
||||||
|
ansistr: ansistring;
|
||||||
|
size: DWord;
|
||||||
|
buf: array of byte;
|
||||||
|
begin
|
||||||
|
{ Row and column index range of cells using the hyperlink }
|
||||||
|
row1 := WordLEToN(AStream.ReadWord);
|
||||||
|
row2 := WordLEToN(AStream.ReadWord);
|
||||||
|
col1 := WordLEToN(AStream.ReadWord);
|
||||||
|
col2 := WordLEToN(AStream.ReadWord);
|
||||||
|
|
||||||
|
{ GUID of standard link }
|
||||||
|
AStream.ReadBuffer(guid, SizeOf(guid));
|
||||||
|
|
||||||
|
{ unknown DWord }
|
||||||
|
AStream.ReadDWord;
|
||||||
|
|
||||||
|
{ Flags }
|
||||||
|
flags := DWordLEToN(AStream.ReadDWord);
|
||||||
|
|
||||||
|
{ Description }
|
||||||
|
if flags and MASK_HLINK_DESCRIPTION = MASK_HLINK_DESCRIPTION then
|
||||||
|
begin
|
||||||
|
// not used because there is always a "normal" cell to which the hyperlink is associated.
|
||||||
|
// character count of description incl trailing zero
|
||||||
|
len := DWordLEToN(AStream.ReadDWord);
|
||||||
|
// Character array (16-bit characters, with trailing zero word)
|
||||||
|
SetLength(wideStr, len);
|
||||||
|
AStream.ReadBuffer(wideStr[1], len*SizeOf(wideChar));
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Target frame: external link (URI or local file) }
|
||||||
|
link := '';
|
||||||
|
if flags and MASK_HLINK_LINK <> 0 then
|
||||||
|
// if flags and (MASK_HLINK_LINK or MASK_HLINK_ABSOLUTE) = (MASK_HLINK_LINK or MASK_HLINK_ABSOLUTE) then
|
||||||
|
begin
|
||||||
|
AStream.ReadBuffer(guid, SizeOf(guid));
|
||||||
|
|
||||||
|
// Check for URL
|
||||||
|
if GuidToString(guid) = '{79EAC9E0-BAF9-11CE-8C82-00AA004BA90B}' then
|
||||||
|
begin
|
||||||
|
// Character count incl trailing zero
|
||||||
|
len := DWordLEToN(AStream.ReadDWord);
|
||||||
|
// Character array of URL (16-bit-characters, with trailing zero word)
|
||||||
|
SetLength(wideStr, len);
|
||||||
|
AStream.ReadBuffer(wideStr[1], len*SizeOf(wideChar));
|
||||||
|
link := UTF8Encode(wideStr);
|
||||||
|
end else
|
||||||
|
// Check for local file
|
||||||
|
if GuidToString(guid) = '{00000303-0000-0000-C000-000000000046}' then
|
||||||
|
begin
|
||||||
|
dirUpCount := WordLEToN(AStream.ReadWord);
|
||||||
|
// Character count of the shortened file path and name, incl trailing zero byte
|
||||||
|
len := DWordLEToN(AStream.ReadDWord);
|
||||||
|
// Character array of the shortened file path and name in 8.3-DOS-format.
|
||||||
|
// This field can be filled with a long file name too.
|
||||||
|
// No Unicode string header, always 8-bit characters, zeroterminated.
|
||||||
|
SetLength(ansiStr, len);
|
||||||
|
AStream.ReadBuffer(ansiStr[1], len*SizeOf(ansiChar));
|
||||||
|
linkDos := AnsiToUTF8(ansiStr);
|
||||||
|
while dirUpCount > 0 do
|
||||||
|
begin
|
||||||
|
linkDos := '..\' + linkDos;
|
||||||
|
dec(dirUpCount);
|
||||||
|
end;
|
||||||
|
// 6 unknown DWord values
|
||||||
|
AStream.ReadDWord;
|
||||||
|
AStream.ReadDWord;
|
||||||
|
AStream.ReadDWord;
|
||||||
|
AStream.ReadDWord;
|
||||||
|
AStream.ReadDWord;
|
||||||
|
AStream.ReadDWord;
|
||||||
|
// Size of the following file link field including string length field
|
||||||
|
// and additional data field
|
||||||
|
size := DWordLEToN(AStream.ReadDWord);
|
||||||
|
if size > 0 then
|
||||||
|
begin
|
||||||
|
// Size of the extended file path and name.
|
||||||
|
size := DWordLEToN(AStream.ReadDWord);
|
||||||
|
len := size div 2;
|
||||||
|
// Unknown
|
||||||
|
AStream.ReadWord;
|
||||||
|
// Character array of the extended file path and name
|
||||||
|
// no Unicode string header, always 16-bit characters, not zero-terminated
|
||||||
|
SetLength(wideStr, len);
|
||||||
|
AStream.ReadBuffer(wideStr[1], len*SizeOf(wideChar));
|
||||||
|
link := UTF8Encode(widestr);
|
||||||
|
end else
|
||||||
|
link := linkDos;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Text mark }
|
||||||
|
if flags and MASK_HLINK_TEXTMARK = MASK_HLINK_TEXTMARK then
|
||||||
|
begin
|
||||||
|
// Character count of the text mark, including trailing zero word
|
||||||
|
len := DWordLEToN(AStream.ReadDWord);
|
||||||
|
// Character array of the text mark without "#" sign
|
||||||
|
// no Unicode string header, always 16-bit characters, zero-terminated
|
||||||
|
SetLength(wideStr, len);
|
||||||
|
AStream.ReadBuffer(wideStr[1], len*SizeOf(wideChar));
|
||||||
|
mark := UTF8Encode(wideStr);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Add bookmark to hyperlink target
|
||||||
|
if (link <> '') and (mark <> '') then
|
||||||
|
link := link + '#' + mark
|
||||||
|
else
|
||||||
|
if (link = '') then
|
||||||
|
link := '#' + mark;
|
||||||
|
|
||||||
|
// Add hyperlink(s) to worksheet
|
||||||
|
for row := row1 to row2 do
|
||||||
|
for col := col1 to col2 do
|
||||||
|
FWorksheet.WriteHyperlink(row, col, link);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Reads a HYPERLINK TOOLTIP record
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsSpreadBIFF8Reader.ReadHyperlinkToolTip(AStream: TStream);
|
||||||
|
var
|
||||||
|
txt: String;
|
||||||
|
widestr: widestring;
|
||||||
|
row, col, row1, col1, row2, col2: Word;
|
||||||
|
hyperlink: PsHyperlink;
|
||||||
|
numbytes: Integer;
|
||||||
|
begin
|
||||||
|
{ Record type; this matches the BIFF record type }
|
||||||
|
AStream.ReadWord;
|
||||||
|
|
||||||
|
{ Row and column index range of cells using the hyperlink tooltip }
|
||||||
|
row1 := WordLEToN(AStream.ReadWord);
|
||||||
|
row2 := WordLEToN(AStream.ReadWord);
|
||||||
|
col1 := WordLEToN(AStream.ReadWord);
|
||||||
|
col2 := WordLEToN(AStream.ReadWord);
|
||||||
|
|
||||||
|
{ Hyperlink tooltip, a null-terminated unicode string }
|
||||||
|
numbytes := RecordSize - 5*SizeOf(word);
|
||||||
|
SetLength(wideStr, numbytes div 2);
|
||||||
|
AStream.ReadBuffer(wideStr[1], numbytes);
|
||||||
|
txt := UTF8Encode(wideStr);
|
||||||
|
|
||||||
|
{ Add tooltip to hyperlinks }
|
||||||
|
for row := row1 to row2 do
|
||||||
|
for col := col1 to col2 do
|
||||||
|
begin
|
||||||
|
hyperlink := FWorksheet.FindHyperlink(row, col);
|
||||||
|
if hyperlink <> nil then
|
||||||
|
hyperlink^.ToolTip := txt;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
{ TsSpreadBIFF8Writer }
|
{ TsSpreadBIFF8Writer }
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Constructor of the Excel 8 writer
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
constructor TsSpreadBIFF8Writer.Create(AWorkbook: TsWorkbook);
|
constructor TsSpreadBIFF8Writer.Create(AWorkbook: TsWorkbook);
|
||||||
begin
|
begin
|
||||||
inherited Create(AWorkbook);
|
inherited Create(AWorkbook);
|
||||||
@ -2171,22 +2344,10 @@ begin
|
|||||||
{ OBS: It seems to be no problem just ignoring this part of the record }
|
{ OBS: It seems to be no problem just ignoring this part of the record }
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Inherited method for writing a hyperlink
|
|
||||||
Just writes the cell text; the hyperlink is written together with the other
|
|
||||||
hyperlinks later.
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure TsSpreadBIFF8Writer.WriteHyperlink(AStream: TStream;
|
|
||||||
const ARow, ACol: Cardinal; ACell: PCell);
|
|
||||||
begin
|
|
||||||
WriteLabel(AStream, ARow, ACol, FWorksheet.ReadAsUTF8Text(ACell), ACell);
|
|
||||||
ACell^.ContentType := cctHyperlink;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Writes an Excel 8 HYPERLINK record
|
Writes an Excel 8 HYPERLINK record
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsSpreadBIFF8Writer.WriteHYPERLINKRecord(AStream: TStream;
|
procedure TsSpreadBIFF8Writer.WriteHyperlink(AStream: TStream;
|
||||||
AHyperlink: PsHyperlink; AWorksheet: TsWorksheet);
|
AHyperlink: PsHyperlink; AWorksheet: TsWorksheet);
|
||||||
var
|
var
|
||||||
temp: TStream;
|
temp: TStream;
|
||||||
@ -2196,16 +2357,18 @@ var
|
|||||||
descr: String;
|
descr: String;
|
||||||
fn: String;
|
fn: String;
|
||||||
flags: DWord;
|
flags: DWord;
|
||||||
markpos: Integer;
|
|
||||||
size: Integer;
|
size: Integer;
|
||||||
cell: PCell;
|
cell: PCell;
|
||||||
|
isInternal: Boolean;
|
||||||
|
target, bookmark: String;
|
||||||
begin
|
begin
|
||||||
cell := AWorksheet.FindCell(AHyperlink^.Row, AHyperlink^.Col);
|
cell := AWorksheet.FindCell(AHyperlink^.Row, AHyperlink^.Col);
|
||||||
if (cell = nil) or (AHyperlink^.Kind = hkNone) then
|
if (cell = nil) or (AHyperlink^.Target='') then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
descr := AWorksheet.ReadAsUTF8Text(cell); // Hyperlink description
|
descr := AWorksheet.ReadAsUTF8Text(cell); // Hyperlink description
|
||||||
markpos := UTF8Pos('#', AHyperlink^.Target); // Position of # in hyperlink target
|
AWorksheet.SplitHyperlink(AHyperlink^.Target, target, bookmark);
|
||||||
|
isInternal := (target = '');
|
||||||
|
|
||||||
// Since the length of the record is not known in the first place we write
|
// Since the length of the record is not known in the first place we write
|
||||||
// the data to a temporary stream at first.
|
// the data to a temporary stream at first.
|
||||||
@ -2226,16 +2389,14 @@ begin
|
|||||||
|
|
||||||
{ option flags }
|
{ option flags }
|
||||||
flags := 0;
|
flags := 0;
|
||||||
case AHyperlink^.Kind of
|
if isInternal then
|
||||||
hkCell:
|
flags := MASK_HLINK_TEXTMARK or MASK_HLINK_DESCRIPTION
|
||||||
flags := MASK_HLINK_TEXTMARK or MASK_HLINK_DESCRIPTION;
|
else
|
||||||
hkURI:
|
flags := MASK_HLINK_LINK or MASK_HLINK_ABSOLUTE;
|
||||||
flags := MASK_HLINK_LINK or MASK_HLINK_ABSOLUTE;
|
|
||||||
end;
|
|
||||||
if descr <> AHyperlink^.Target then
|
if descr <> AHyperlink^.Target then
|
||||||
flags := flags or MASK_HLINK_DESCRIPTION; // has description
|
flags := flags or MASK_HLINK_DESCRIPTION; // has description
|
||||||
if markpos > 0 then // has # in target
|
if bookmark <> '' then
|
||||||
flags := flags or MASK_HLINK_TEXTMARK;
|
flags := flags or MASK_HLINK_TEXTMARK; // link contains a bookmark
|
||||||
temp.WriteDWord(DWordToLE(flags));
|
temp.WriteDWord(DWordToLE(flags));
|
||||||
|
|
||||||
{ description }
|
{ description }
|
||||||
@ -2248,9 +2409,9 @@ begin
|
|||||||
temp.WriteBuffer(wideStr[1], (Length(wideStr)+1)*SizeOf(widechar));
|
temp.WriteBuffer(wideStr[1], (Length(wideStr)+1)*SizeOf(widechar));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if AHyperlink^.Kind = hkURI then
|
if target <> '' then
|
||||||
begin
|
begin
|
||||||
if URIToFilename(AHyperlink^.Target, fn) then // URI is a local file
|
if URIToFilename(target, fn) then // URI is a local file
|
||||||
begin
|
begin
|
||||||
{ GUID of file moniker }
|
{ GUID of file moniker }
|
||||||
guid := StringToGuid('{00000303-0000-0000-C000-000000000046}');
|
guid := StringToGuid('{00000303-0000-0000-C000-000000000046}');
|
||||||
@ -2286,7 +2447,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else begin { Hyperlink target is a URL }
|
else begin { Hyperlink target is a URL }
|
||||||
widestr := UTF8Decode(AHyperlink^.Target);
|
widestr := UTF8Decode(target);
|
||||||
{ GUID of URL Moniker }
|
{ GUID of URL Moniker }
|
||||||
guid := StringToGUID('{79EAC9E0-BAF9-11CE-8C82-00AA004BA90B}');
|
guid := StringToGUID('{79EAC9E0-BAF9-11CE-8C82-00AA004BA90B}');
|
||||||
temp.WriteBuffer(guid, SizeOf(guid));
|
temp.WriteBuffer(guid, SizeOf(guid));
|
||||||
@ -2298,13 +2459,10 @@ begin
|
|||||||
end; // hkURI
|
end; // hkURI
|
||||||
|
|
||||||
// Hyperlink contains a text mark (#)
|
// Hyperlink contains a text mark (#)
|
||||||
if flags and MASK_HLINK_TEXTMARK <> 0 then
|
if bookmark <> '' then
|
||||||
begin
|
begin
|
||||||
// Extract text mark without "#" and convert to 16-bit characters
|
// Convert to 16-bit characters
|
||||||
if markpos > 0 then
|
widestr := UTF8Decode(bookmark);
|
||||||
widestr := UTF8Decode(UTF8Copy(AHyperlink^.Target, markpos+1, Length(AHyperlink^.Target)))
|
|
||||||
else if AHyperlink^.Kind = hkCell then
|
|
||||||
widestr := UTF8Decode(AHyperlink^.Target);
|
|
||||||
{ Character count of text mark, incl trailing zero }
|
{ Character count of text mark, incl trailing zero }
|
||||||
temp.WriteDWord(DWordToLE(Length(wideStr) + 1));
|
temp.WriteDWord(DWordToLE(Length(wideStr) + 1));
|
||||||
{ Character array (16-bit characters) plus trailing zeros }
|
{ Character array (16-bit characters) plus trailing zeros }
|
||||||
@ -2340,7 +2498,7 @@ procedure TsSpreadBIFF8Writer.WriteHyperlinksCallback(AHyperlink: PsHyperlink;
|
|||||||
AStream: TStream);
|
AStream: TStream);
|
||||||
begin
|
begin
|
||||||
{ Write HYPERLINK record }
|
{ Write HYPERLINK record }
|
||||||
WriteHyperlinkRecord(AStream, AHyperlink, FWorksheet);
|
WriteHyperlink(AStream, AHyperlink, FWorksheet);
|
||||||
|
|
||||||
{ Write HYPERLINK TOOLTIP record }
|
{ Write HYPERLINK TOOLTIP record }
|
||||||
if AHyperlink^.Tooltip <> '' then
|
if AHyperlink^.Tooltip <> '' then
|
||||||
@ -2358,11 +2516,11 @@ begin
|
|||||||
widestr := UTF8Decode(ATooltip);
|
widestr := UTF8Decode(ATooltip);
|
||||||
|
|
||||||
{ BIFF record header }
|
{ BIFF record header }
|
||||||
WriteBiffHeader(AStream, INT_EXCEL_ID_HYPERLINKTOOLTIP,
|
WriteBiffHeader(AStream, INT_EXCEL_ID_HLINKTOOLTIP,
|
||||||
10 + (Length(wideStr)+1) * SizeOf(widechar));
|
10 + (Length(wideStr)+1) * SizeOf(widechar));
|
||||||
|
|
||||||
{ Repeated record ID }
|
{ Repeated record ID }
|
||||||
AStream.WriteWord(WordToLe(INT_EXCEL_ID_HYPERLINKTOOLTIP));
|
AStream.WriteWord(WordToLe(INT_EXCEL_ID_HLINKTOOLTIP));
|
||||||
|
|
||||||
{ Cell range using the same hyperlink tooltip - we support only single cells }
|
{ Cell range using the same hyperlink tooltip - we support only single cells }
|
||||||
AStream.WriteWord(WordToLE(ARow)); // first row
|
AStream.WriteWord(WordToLE(ARow)); // first row
|
||||||
|
@ -180,8 +180,6 @@ type
|
|||||||
const AValue: TsErrorValue; ACell: PCell); override;
|
const AValue: TsErrorValue; ACell: PCell); override;
|
||||||
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
ACell: PCell); override;
|
ACell: PCell); override;
|
||||||
procedure WriteHyperlink(AStream: TStream; const ARow, ACol: Cardinal;
|
|
||||||
ACell: PCell); override;
|
|
||||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: string; ACell: PCell); override;
|
const AValue: string; ACell: PCell); override;
|
||||||
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
@ -351,8 +349,8 @@ type
|
|||||||
THyperlinkListData = class
|
THyperlinkListData = class
|
||||||
ID: String;
|
ID: String;
|
||||||
CellRef: String;
|
CellRef: String;
|
||||||
Kind: TsHyperlinkKind;
|
Target: String;
|
||||||
Location: String;
|
TextMark: String;
|
||||||
Display: String;
|
Display: String;
|
||||||
Tooltip: String;
|
Tooltip: String;
|
||||||
end;
|
end;
|
||||||
@ -531,7 +529,13 @@ begin
|
|||||||
for r := r1 to r2 do
|
for r := r1 to r2 do
|
||||||
for c := c1 to c2 do
|
for c := c1 to c2 do
|
||||||
with hyperlinkData do
|
with hyperlinkData do
|
||||||
AWorksheet.WriteHyperlink(r, c, Kind, Location, Display, ToolTip);
|
if Target = '' then
|
||||||
|
AWorksheet.WriteHyperlink(r, c, '#'+TextMark, ToolTip)
|
||||||
|
else
|
||||||
|
if TextMark = '' then
|
||||||
|
AWorksheet.WriteHyperlink(r, c, Target, ToolTip)
|
||||||
|
else
|
||||||
|
AWorksheet.WriteHyperlink(r, c, Target+'#'+TextMark, ToolTip);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1276,10 +1280,10 @@ begin
|
|||||||
hyperlinkData := THyperlinkListData.Create;
|
hyperlinkData := THyperlinkListData.Create;
|
||||||
hyperlinkData.CellRef := GetAttrValue(node, 'ref');
|
hyperlinkData.CellRef := GetAttrValue(node, 'ref');
|
||||||
hyperlinkData.ID := GetAttrValue(node, 'r:id');
|
hyperlinkData.ID := GetAttrValue(node, 'r:id');
|
||||||
hyperlinkData.Location := GetAttrValue(node, 'location');
|
hyperlinkData.Target := '';
|
||||||
|
hyperlinkData.TextMark := GetAttrValue(node, 'location');
|
||||||
hyperlinkData.Display := GetAttrValue(node, 'display');
|
hyperlinkData.Display := GetAttrValue(node, 'display');
|
||||||
hyperlinkData.Tooltip := GetAttrValue(node, 'tooltip');
|
hyperlinkData.Tooltip := GetAttrValue(node, 'tooltip');
|
||||||
hyperlinkData.Kind := hkCell;
|
|
||||||
end;
|
end;
|
||||||
FHyperlinkList.Add(hyperlinkData);
|
FHyperlinkList.Add(hyperlinkData);
|
||||||
node := node.NextSibling;
|
node := node.NextSibling;
|
||||||
@ -1302,10 +1306,13 @@ begin
|
|||||||
hyperlinkData := FindHyperlinkID(s);
|
hyperlinkData := FindHyperlinkID(s);
|
||||||
if hyperlinkData <> nil then begin
|
if hyperlinkData <> nil then begin
|
||||||
s := GetAttrValue(node, 'Target');
|
s := GetAttrValue(node, 'Target');
|
||||||
if s <> '' then hyperlinkData.Location := s;
|
if s <> '' then hyperlinkData.Target := s;
|
||||||
s := GetAttrValue(node, 'TargetMode');
|
s := GetAttrValue(node, 'TargetMode');
|
||||||
if s = 'External' then
|
if s <> 'External' then // Only "External" accepted!
|
||||||
hyperlinkData.Kind := hkURI
|
begin
|
||||||
|
hyperlinkData.Target := '';
|
||||||
|
hyperlinkData.TextMark := '';
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -2182,6 +2189,7 @@ procedure TsSpreadOOXMLWriter.WriteHyperlinks(AStream: TStream;
|
|||||||
AWorksheet: TsWorksheet);
|
AWorksheet: TsWorksheet);
|
||||||
var
|
var
|
||||||
hyperlink: PsHyperlink;
|
hyperlink: PsHyperlink;
|
||||||
|
target, bookmark: String;
|
||||||
s: String;
|
s: String;
|
||||||
txt: String;
|
txt: String;
|
||||||
AVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
@ -2198,14 +2206,15 @@ begin
|
|||||||
AVLNode := AWorksheet.Hyperlinks.FindLowest;
|
AVLNode := AWorksheet.Hyperlinks.FindLowest;
|
||||||
while AVLNode <> nil do begin
|
while AVLNode <> nil do begin
|
||||||
hyperlink := PsHyperlink(AVLNode.Data);
|
hyperlink := PsHyperlink(AVLNode.Data);
|
||||||
|
AWorksheet.SplitHyperlink(hyperlink^.Target, target, bookmark);
|
||||||
s := Format('ref="%s"', [GetCellString(hyperlink^.Row, hyperlink^.Col)]);
|
s := Format('ref="%s"', [GetCellString(hyperlink^.Row, hyperlink^.Col)]);
|
||||||
if hyperlink^.Kind <> hkCell then
|
if target <> '' then
|
||||||
begin
|
begin
|
||||||
s := Format('%s r:id="rId%d"', [s, FNext_rId]);
|
s := Format('%s r:id="rId%d"', [s, FNext_rId]);
|
||||||
inc(FNext_rId);
|
inc(FNext_rId);
|
||||||
end;
|
end;
|
||||||
if hyperlink^.Kind = hkCell then
|
if target = '' then
|
||||||
s := Format('%s location="%s"', [s, hyperlink^.Target]);
|
s := Format('%s location="%s"', [s, bookmark]);
|
||||||
txt := AWorksheet.ReadAsUTF8Text(hyperlink^.Row, hyperlink^.Col);
|
txt := AWorksheet.ReadAsUTF8Text(hyperlink^.Row, hyperlink^.Col);
|
||||||
if (txt <> '') and (txt <> hyperlink^.Target) then
|
if (txt <> '') and (txt <> hyperlink^.Target) then
|
||||||
s := Format('%s display="%s"', [s, txt]);
|
s := Format('%s display="%s"', [s, txt]);
|
||||||
@ -2695,12 +2704,10 @@ begin
|
|||||||
while Assigned(AVLNode) do
|
while Assigned(AVLNode) do
|
||||||
begin
|
begin
|
||||||
hyperlink := PsHyperlink(AVLNode.Data);
|
hyperlink := PsHyperlink(AVLNode.Data);
|
||||||
if hyperlink^.Kind <> hkCell then
|
if hyperlink^.Target <> '' then
|
||||||
begin
|
begin
|
||||||
s := Format('Id="rId%d" Type="%s" Target="%s"',
|
s := Format('Id="rId%d" Type="%s" Target="%s" TargetMode="External"',
|
||||||
[FNext_rId, SCHEMAS_HYPERLINKS, hyperlink^.Target]);
|
[FNext_rId, SCHEMAS_HYPERLINKS, hyperlink^.Target]);
|
||||||
if hyperlink^.Kind <> hkCell then
|
|
||||||
s := s + ' TargetMode="External"';
|
|
||||||
AppendToStream(FSSheetRels[FCurSheetNum],
|
AppendToStream(FSSheetRels[FCurSheetNum],
|
||||||
'<Relationship ' + s + ' />');
|
'<Relationship ' + s + ' />');
|
||||||
inc(FNext_rId);
|
inc(FNext_rId);
|
||||||
@ -3289,13 +3296,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadOOXMLWriter.WriteHyperlink(AStream: TStream;
|
|
||||||
const ARow, ACol: Cardinal; ACell: PCell);
|
|
||||||
begin
|
|
||||||
if FWorksheet.IsHyperlink(ACell) then
|
|
||||||
WriteLabel(AStream, ARow, ACol, FWorksheet.ReadAsUTF8Text(ACell), ACell);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Writes a string to the stream
|
Writes a string to the stream
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user