mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2026-03-13 09:24:25 +08:00
Merge 5 GridToXYZ export routines into one, so there is less code to maintain and to make it easier to add further export formats.
This commit is contained in:
@@ -67,6 +67,8 @@ type
|
||||
end;
|
||||
TSQLBatch = TObjectList<TSQLSentence>;
|
||||
|
||||
TGridExportFormat = (efUnknown, efCSV, efHTML, efXML, efSQL, efLaTeX);
|
||||
|
||||
|
||||
{$I const.inc}
|
||||
|
||||
@@ -81,11 +83,7 @@ type
|
||||
function encrypt(str: String): String;
|
||||
function decrypt(str: String): String;
|
||||
function htmlentities(str: String): String;
|
||||
procedure GridToHtml(Grid: TVirtualStringTree; S: TStream);
|
||||
procedure GridToCsv(Grid: TVirtualStringTree; Separator, Encloser, Terminator: String; S: TStream);
|
||||
procedure GridToXml(Grid: TVirtualStringTree; S: TStream);
|
||||
procedure GridToSql(Grid: TVirtualStringTree; S: TStream);
|
||||
procedure GridToLaTeX(Grid: TVirtualStringTree; S: TStream);
|
||||
procedure GridExport(Grid: TVirtualStringTree; S: TStream; ExportFormat: TGridExportFormat);
|
||||
function BestTableName(Data: TMySQLQuery): String;
|
||||
function esc2ascii(str: String): String;
|
||||
function urlencode(url: String): String;
|
||||
@@ -544,24 +542,11 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure ExportStatusMsg(Node: PVirtualNode; RootNodeCount: Cardinal; StreamSize: Int64);
|
||||
begin
|
||||
Mainform.ShowStatusMsg('Exporting row '+FormatNumber(Node.Index+1)+' of '+FormatNumber(RootNodeCount)+
|
||||
' ('+IntToStr(Trunc((Node.Index+1) / RootNodeCount *100))+'%, '+FormatByteNumber(StreamSize)+')'
|
||||
);
|
||||
Mainform.ProgressBarStatus.Position := Node.Index+1;
|
||||
end;
|
||||
|
||||
|
||||
{***
|
||||
Converts a Grid to a HTML-Table.
|
||||
@param Grid Object which holds data to export
|
||||
@param string Text used in <title>
|
||||
}
|
||||
procedure GridToHtml(Grid: TVirtualStringTree; S: TStream);
|
||||
procedure GridExport(Grid: TVirtualStringTree; S: TStream; ExportFormat: TGridExportFormat);
|
||||
var
|
||||
i, MaxSize: Integer;
|
||||
tmp, Data, Generator, Title: String;
|
||||
MaxSize: Integer;
|
||||
Col: TColumnIndex;
|
||||
Header, Data, tmp, Encloser, Separator, Terminator, TableName: String;
|
||||
Node: PVirtualNode;
|
||||
GridData: TMySQLQuery;
|
||||
SelectionOnly: Boolean;
|
||||
@@ -570,357 +555,212 @@ var
|
||||
begin
|
||||
// Only process selected nodes for "Copy as ..." actions
|
||||
SelectionOnly := S is TMemoryStream;
|
||||
|
||||
Mainform.DataGridEnsureFullRows(Grid, SelectionOnly);
|
||||
GridData := Mainform.GridResult(Grid);
|
||||
Title := BestTableName(GridData);
|
||||
|
||||
MaxSize := GetRegValue(REGNAME_COPYMAXSIZE, DEFAULT_COPYMAXSIZE) * SIZE_MB;
|
||||
|
||||
if SelectionOnly then
|
||||
NodeCount := Grid.SelectedCount
|
||||
else
|
||||
NodeCount := Grid.RootNodeCount;
|
||||
EnableProgressBar(NodeCount);
|
||||
Generator := APPNAME+' '+Mainform.AppVersion;
|
||||
tmp :=
|
||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ' + CRLF +
|
||||
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' + CRLF + CRLF +
|
||||
'<html>' + CRLF +
|
||||
' <head>' + CRLF +
|
||||
' <title>' + Title + '</title>' + CRLF +
|
||||
' <meta name="GENERATOR" content="'+ Generator + '">' + CRLF +
|
||||
' <style type="text/css">' + CRLF +
|
||||
' thead tr {background-color: ActiveCaption; color: CaptionText;}' + CRLF +
|
||||
' th, td {vertical-align: top; font-family: "'+Grid.Font.Name+'"; font-size: '+IntToStr(Grid.Font.Size)+'pt; padding: '+IntToStr(Grid.TextMargin-1)+'px; }' + CRLF +
|
||||
' table, td {border: 1px solid silver;}' + CRLF +
|
||||
' table {border-collapse: collapse;}' + CRLF;
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns.
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
// Adjust preferred width of columns.
|
||||
tmp := tmp +
|
||||
' thead .col' + IntToStr(i) + ' {width: ' + IntToStr(Grid.Header.Columns[i].Width) + 'px;}' + CRLF;
|
||||
// Right-justify all cells to match the grid on screen.
|
||||
if Grid.Header.Columns[i].Alignment = taRightJustify then
|
||||
tmp := tmp +
|
||||
' .col' + IntToStr(i) + ' {text-align: right;}' + CRLF;
|
||||
end;
|
||||
// Note for above:
|
||||
// Indentation seems to be a mess. I think we should stick to putting long lines on one line,
|
||||
// and just let the editor figure out how to break it if it doesn't fit on screen. That way
|
||||
// the editor can break, indent, and put a visual marker in the gutter to denote the wrap.
|
||||
|
||||
tmp := tmp +
|
||||
' </style>' + CRLF +
|
||||
' </head>' + CRLF + CRLF +
|
||||
' <body>' + CRLF + CRLF +
|
||||
' <table caption="' + Title + ' (' + inttostr(NodeCount) + ' rows)">' + CRLF +
|
||||
' <thead>' + CRLF +
|
||||
' <tr>' + CRLF;
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns.
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
// Add header item.
|
||||
tmp := tmp + ' <th class="col' + IntToStr(i) + '">' + Grid.Header.Columns[i].Text + '</th>' + CRLF;
|
||||
end;
|
||||
tmp := tmp +
|
||||
' </tr>' + CRLF +
|
||||
' </thead>' + CRLF +
|
||||
' <tbody>' + CRLF;
|
||||
StreamWrite(S, tmp);
|
||||
|
||||
if SelectionOnly then Node := Grid.GetFirstSelected else Node := Grid.GetFirst;
|
||||
while Assigned(Node) do begin
|
||||
// Update status once in a while.
|
||||
if (Node.Index+1) mod 100 = 0 then
|
||||
ExportStatusMsg(Node, NodeCount, S.Size);
|
||||
RowNum := Grid.GetNodeData(Node);
|
||||
GridData.RecNo := RowNum^;
|
||||
tmp := ' <tr>' + CRLF;
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
Data := GridData.Col(i);
|
||||
// Handle nulls.
|
||||
if GridData.IsNull(i) then Data := TEXT_NULL;
|
||||
// Keep formatted numeric values
|
||||
if Mainform.prefExportLocaleNumbers and (GridData.DataType(i).Category in [dtcInteger, dtcReal]) then
|
||||
Data := FormatNumber(Data, False);
|
||||
// Escape HTML control characters in data.
|
||||
Data := htmlentities(Data);
|
||||
tmp := tmp + ' <td class="col' + IntToStr(i) + '">' + Data + '</td>' + CRLF;
|
||||
end;
|
||||
tmp := tmp + ' </tr>' + CRLF;
|
||||
StreamWrite(S, tmp);
|
||||
if SelectionOnly then Node := Grid.GetNextSelected(Node) else Node := Grid.GetNext(Node);
|
||||
if (MaxSize > 0) and Assigned(Node) and (S is TMemoryStream) and (S.Size >= MaxSize) then begin
|
||||
MessageDlg(
|
||||
Format(MSG_COPYMAXSIZE, [FormatByteNumber(MaxSize), FormatNumber(Node.Index), FormatNumber(NodeCount)]),
|
||||
mtWarning, [mbOK], 0);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
// footer:
|
||||
tmp :=
|
||||
' </tbody>' + CRLF +
|
||||
' </table>' + CRLF + CRLF +
|
||||
' <p>' + CRLF +
|
||||
' <em>generated ' + DateToStr(now) + ' ' + TimeToStr(now) +
|
||||
' by <a href="'+APPDOMAIN+'">' + Generator + '</a></em>' + CRLF +
|
||||
' </p>' + CRLF + CRLF +
|
||||
' </body>' + CRLF +
|
||||
'</html>' + CRLF;
|
||||
StreamWrite(S, tmp);
|
||||
Mainform.ProgressBarStatus.Visible := False;
|
||||
Mainform.ShowStatusMsg;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{***
|
||||
Converts grid contents to CSV-values.
|
||||
@param Grid Object which holds data to export
|
||||
@param string Field-separator
|
||||
@param string Field-encloser
|
||||
@param string Line-terminator
|
||||
}
|
||||
procedure GridToCsv(Grid: TVirtualStringTree; Separator, Encloser, Terminator: String; S: TStream);
|
||||
var
|
||||
i, MaxSize: Integer;
|
||||
tmp, Data: String;
|
||||
Node: PVirtualNode;
|
||||
GridData: TMySQLQuery;
|
||||
SelectionOnly: Boolean;
|
||||
NodeCount: Cardinal;
|
||||
RowNum: PCardinal;
|
||||
begin
|
||||
// Only process selected nodes for "Copy as ..." actions
|
||||
SelectionOnly := S is TMemoryStream;
|
||||
|
||||
Mainform.DataGridEnsureFullRows(Grid, SelectionOnly);
|
||||
GridData := Mainform.GridResult(Grid);
|
||||
|
||||
separator := esc2ascii(separator);
|
||||
encloser := esc2ascii(encloser);
|
||||
terminator := esc2ascii(terminator);
|
||||
MaxSize := GetRegValue(REGNAME_COPYMAXSIZE, DEFAULT_COPYMAXSIZE) * SIZE_MB;
|
||||
|
||||
if SelectionOnly then
|
||||
NodeCount := Grid.SelectedCount
|
||||
else
|
||||
NodeCount := Grid.RootNodeCount;
|
||||
EnableProgressBar(NodeCount);
|
||||
|
||||
tmp := '';
|
||||
// Columns
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
Data := Grid.Header.Columns[i].Text;
|
||||
// Alter column name in header if data is not raw.
|
||||
if (GridData.DataType(i).Category in [dtcBinary, dtcSpatial]) and (not Mainform.actBlobAsText.Checked) then
|
||||
Data := 'HEX(' + Data + ')';
|
||||
// Add header item.
|
||||
if tmp <> '' then tmp := tmp + Separator;
|
||||
tmp := tmp + Encloser + Data + Encloser;
|
||||
end;
|
||||
tmp := tmp + Terminator;
|
||||
StreamWrite(S, tmp);
|
||||
|
||||
// Data:
|
||||
if SelectionOnly then Node := Grid.GetFirstSelected else Node := Grid.GetFirst;
|
||||
while Assigned(Node) do begin
|
||||
if (Node.Index+1) mod 100 = 0 then
|
||||
ExportStatusMsg(Node, NodeCount, S.Size);
|
||||
RowNum := Grid.GetNodeData(Node);
|
||||
GridData.RecNo := RowNum^;
|
||||
tmp := '';
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
if (GridData.DataType(i).Category in [dtcBinary, dtcSpatial]) and (not Mainform.actBlobAsText.Checked) then
|
||||
Data := GridData.BinColAsHex(i)
|
||||
else
|
||||
Data := GridData.Col(i);
|
||||
// Unformat numeric values
|
||||
if Mainform.prefExportLocaleNumbers and (GridData.DataType(i).Category in [dtcInteger, dtcReal]) then
|
||||
Data := FormatNumber(Data, False);
|
||||
// Escape encloser characters inside data per de-facto CSV.
|
||||
Data := StringReplace(Data, Encloser, Encloser + Encloser, [rfReplaceAll]);
|
||||
// Special handling for NULL (MySQL-ism, not de-facto CSV: unquote value)
|
||||
if GridData.IsNull(i) then Data := 'NULL'
|
||||
else Data := Encloser + Data + Encloser;
|
||||
// Add cell.
|
||||
if tmp <> '' then tmp := tmp + Separator;
|
||||
tmp := tmp + Data;
|
||||
end;
|
||||
tmp := tmp + Terminator;
|
||||
StreamWrite(S, tmp);
|
||||
if SelectionOnly then Node := Grid.GetNextSelected(Node) else Node := Grid.GetNext(Node);
|
||||
if (MaxSize > 0) and Assigned(Node) and (S is TMemoryStream) and (S.Size >= MaxSize) then begin
|
||||
MessageDlg(
|
||||
Format(MSG_COPYMAXSIZE, [FormatByteNumber(MaxSize), FormatNumber(Node.Index), FormatNumber(NodeCount)]),
|
||||
mtWarning, [mbOK], 0);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
Mainform.ProgressBarStatus.Visible := False;
|
||||
Mainform.ShowStatusMsg;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{***
|
||||
Converts grid contents to XML.
|
||||
@param Grid Object which holds data to export
|
||||
@param string Text used as root-element
|
||||
}
|
||||
procedure GridToXml(Grid: TVirtualStringTree; S: TStream);
|
||||
var
|
||||
i, MaxSize: Integer;
|
||||
tmp, Data, root: String;
|
||||
Node: PVirtualNode;
|
||||
GridData: TMySQLQuery;
|
||||
SelectionOnly: Boolean;
|
||||
NodeCount: Cardinal;
|
||||
RowNum: PCardinal;
|
||||
begin
|
||||
// Only process selected nodes for "Copy as ..." actions
|
||||
SelectionOnly := S is TMemoryStream;
|
||||
|
||||
Mainform.DataGridEnsureFullRows(Grid, SelectionOnly);
|
||||
GridData := Mainform.GridResult(Grid);
|
||||
root := BestTableName(GridData);
|
||||
|
||||
MaxSize := GetRegValue(REGNAME_COPYMAXSIZE, DEFAULT_COPYMAXSIZE) * SIZE_MB;
|
||||
|
||||
if SelectionOnly then
|
||||
NodeCount := Grid.SelectedCount
|
||||
else
|
||||
NodeCount := Grid.RootNodeCount;
|
||||
EnableProgressBar(NodeCount);
|
||||
tmp := '<?xml version="1.0"?>' + CRLF + CRLF +
|
||||
'<table name="'+root+'">' + CRLF;
|
||||
StreamWrite(S, tmp);
|
||||
|
||||
if SelectionOnly then Node := Grid.GetFirstSelected else Node := Grid.GetFirst;
|
||||
while Assigned(Node) do begin
|
||||
if (Node.Index+1) mod 100 = 0 then
|
||||
ExportStatusMsg(Node, NodeCount, S.Size);
|
||||
RowNum := Grid.GetNodeData(Node);
|
||||
GridData.RecNo := RowNum^;
|
||||
tmp := #9'<row>' + CRLF;
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
// Print cell start tag.
|
||||
tmp := tmp + #9#9'<' + Grid.Header.Columns[i].Text;
|
||||
if GridData.IsNull(i) then tmp := tmp + ' isnull="true" />' + CRLF
|
||||
else begin
|
||||
if (GridData.DataType(i).Category in [dtcBinary, dtcSpatial]) and (not Mainform.actBlobAsText.Checked) then
|
||||
tmp := tmp + ' format="hex"';
|
||||
tmp := tmp + '>';
|
||||
if (GridData.DataType(i).Category in [dtcBinary, dtcSpatial]) and (not Mainform.actBlobAsText.Checked) then
|
||||
Data := GridData.BinColAsHex(i)
|
||||
else
|
||||
Data := GridData.Col(i);
|
||||
// Escape XML control characters in data.
|
||||
Data := htmlentities(Data);
|
||||
// Add data and cell end tag.
|
||||
tmp := tmp + Data + '</' + Grid.Header.Columns[i].Text + '>' + CRLF;
|
||||
end;
|
||||
end;
|
||||
tmp := tmp + #9'</row>' + CRLF;
|
||||
StreamWrite(S, tmp);
|
||||
if SelectionOnly then Node := Grid.GetNextSelected(Node) else Node := Grid.GetNext(Node);
|
||||
if (MaxSize > 0) and Assigned(Node) and (S is TMemoryStream) and (S.Size >= MaxSize) then begin
|
||||
MessageDlg(
|
||||
Format(MSG_COPYMAXSIZE, [FormatByteNumber(MaxSize), FormatNumber(Node.Index), FormatNumber(NodeCount)]),
|
||||
mtWarning, [mbOK], 0);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
// footer:
|
||||
tmp := '</table>' + CRLF;
|
||||
StreamWrite(S, tmp);
|
||||
Mainform.ProgressBarStatus.Visible := False;
|
||||
Mainform.ShowStatusMsg;
|
||||
end;
|
||||
|
||||
|
||||
{***
|
||||
Converts grid contents to XML.
|
||||
@param Grid Object which holds data to export
|
||||
@param string Text used as tablename in INSERTs
|
||||
}
|
||||
procedure GridToSql(Grid: TVirtualStringTree; S: TStream);
|
||||
var
|
||||
i, MaxSize: Integer;
|
||||
tmp, Data, TableName: String;
|
||||
Node: PVirtualNode;
|
||||
GridData: TMySQLQuery;
|
||||
SelectionOnly: Boolean;
|
||||
NodeCount: Cardinal;
|
||||
RowNum: PCardinal;
|
||||
begin
|
||||
// Only process selected nodes for "Copy as ..." actions
|
||||
SelectionOnly := S is TMemoryStream;
|
||||
|
||||
Mainform.DataGridEnsureFullRows(Grid, SelectionOnly);
|
||||
GridData := Mainform.GridResult(Grid);
|
||||
TableName := BestTableName(GridData);
|
||||
|
||||
MaxSize := GetRegValue(REGNAME_COPYMAXSIZE, DEFAULT_COPYMAXSIZE) * SIZE_MB;
|
||||
Header := '';
|
||||
case ExportFormat of
|
||||
efHTML: begin
|
||||
Header :=
|
||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ' + CRLF +
|
||||
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' + CRLF + CRLF +
|
||||
'<html>' + CRLF +
|
||||
' <head>' + CRLF +
|
||||
' <title>' + TableName + '</title>' + CRLF +
|
||||
' <meta name="GENERATOR" content="'+ APPNAME+' '+Mainform.AppVersion + '">' + CRLF +
|
||||
' <style type="text/css">' + CRLF +
|
||||
' thead tr {background-color: ActiveCaption; color: CaptionText;}' + CRLF +
|
||||
' th, td {vertical-align: top; font-family: "'+Grid.Font.Name+'"; font-size: '+IntToStr(Grid.Font.Size)+'pt; padding: '+IntToStr(Grid.TextMargin-1)+'px; }' + CRLF +
|
||||
' table, td {border: 1px solid silver;}' + CRLF +
|
||||
' table {border-collapse: collapse;}' + CRLF;
|
||||
Col := Grid.Header.Columns.GetFirstVisibleColumn;
|
||||
while Col > NoColumn do begin
|
||||
// Adjust preferred width of columns.
|
||||
Header := Header +
|
||||
' thead .col' + IntToStr(Col) + ' {width: ' + IntToStr(Grid.Header.Columns[Col].Width) + 'px;}' + CRLF;
|
||||
// Right-justify all cells to match the grid on screen.
|
||||
if Grid.Header.Columns[Col].Alignment = taRightJustify then
|
||||
Header := Header + ' .col' + IntToStr(Col) + ' {text-align: right;}' + CRLF;
|
||||
Col := Grid.Header.Columns.GetNextVisibleColumn(Col);
|
||||
end;
|
||||
Header := Header +
|
||||
' </style>' + CRLF +
|
||||
' </head>' + CRLF + CRLF +
|
||||
' <body>' + CRLF + CRLF +
|
||||
' <table caption="' + TableName + ' (' + inttostr(NodeCount) + ' rows)">' + CRLF +
|
||||
' <thead>' + CRLF +
|
||||
' <tr>' + CRLF;
|
||||
Col := Grid.Header.Columns.GetFirstVisibleColumn;
|
||||
while Col > NoColumn do begin
|
||||
Header := Header + ' <th class="col' + IntToStr(Col) + '">' + Grid.Header.Columns[Col].Text + '</th>' + CRLF;
|
||||
Col := Grid.Header.Columns.GetNextVisibleColumn(Col);
|
||||
end;
|
||||
Header := Header +
|
||||
' </tr>' + CRLF +
|
||||
' </thead>' + CRLF +
|
||||
' <tbody>' + CRLF;
|
||||
end;
|
||||
|
||||
efCSV: begin
|
||||
Separator := esc2ascii(Mainform.prefCSVSeparator);
|
||||
Encloser := esc2ascii(Mainform.prefCSVEncloser);
|
||||
Terminator := esc2ascii(Mainform.prefCSVTerminator);
|
||||
Col := Grid.Header.Columns.GetFirstVisibleColumn;
|
||||
while Col > NoColumn do begin
|
||||
// Alter column name in header if data is not raw.
|
||||
Data := Grid.Header.Columns[Col].Text;
|
||||
if (GridData.DataType(Col).Category in [dtcBinary, dtcSpatial]) and (not Mainform.actBlobAsText.Checked) then
|
||||
Data := 'HEX(' + Data + ')';
|
||||
// Add header item.
|
||||
if Header <> '' then
|
||||
Header := Header + Separator;
|
||||
Header := Header + Encloser + Data + Encloser;
|
||||
Col := Grid.Header.Columns.GetNextVisibleColumn(Col);
|
||||
end;
|
||||
Header := Header + Terminator;
|
||||
end;
|
||||
|
||||
efXML: begin
|
||||
Header := '<?xml version="1.0"?>' + CRLF + CRLF +
|
||||
'<table name="'+TableName+'">' + CRLF;
|
||||
end;
|
||||
|
||||
efLaTeX: begin
|
||||
Header := '\begin{tabular}{';
|
||||
Separator := ' & ';
|
||||
Encloser := '';
|
||||
Terminator := '\\ '+CRLF;
|
||||
Col := Grid.Header.Columns.GetFirstVisibleColumn;
|
||||
while Col > NoColumn do begin
|
||||
Header := Header + ' c ';
|
||||
Col := Grid.Header.Columns.GetNextVisibleColumn(Col);
|
||||
end;
|
||||
Header := Header + '}' + CRLF;
|
||||
end;
|
||||
end;
|
||||
StreamWrite(S, Header);
|
||||
|
||||
if SelectionOnly then
|
||||
NodeCount := Grid.SelectedCount
|
||||
Node := Grid.GetFirstSelected
|
||||
else
|
||||
NodeCount := Grid.RootNodeCount;
|
||||
EnableProgressBar(NodeCount);
|
||||
|
||||
if SelectionOnly then Node := Grid.GetFirstSelected else Node := Grid.GetFirst;
|
||||
Node := Grid.GetFirst;
|
||||
while Assigned(Node) do begin
|
||||
if (Node.Index+1) mod 100 = 0 then
|
||||
ExportStatusMsg(Node, NodeCount, S.Size);
|
||||
// Update status once in a while.
|
||||
if (Node.Index+1) mod 100 = 0 then begin
|
||||
Mainform.ShowStatusMsg('Exporting row '+FormatNumber(Node.Index+1)+' of '+FormatNumber(NodeCount)+
|
||||
' ('+IntToStr(Trunc((Node.Index+1) / NodeCount *100))+'%, '+FormatByteNumber(S.Size)+')'
|
||||
);
|
||||
Mainform.ProgressBarStatus.Position := Node.Index+1;
|
||||
end;
|
||||
RowNum := Grid.GetNodeData(Node);
|
||||
GridData.RecNo := RowNum^;
|
||||
tmp := 'INSERT INTO '+Mainform.Mask(Tablename)+' (';
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
tmp := tmp + Mainform.mask(Grid.Header.Columns[i].Text)+', ';
|
||||
end;
|
||||
Delete(tmp, Length(tmp)-1, 2);
|
||||
tmp := tmp + ') VALUES (';
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
if GridData.IsNull(i) then
|
||||
tmp := tmp + 'NULL'
|
||||
else begin
|
||||
if (GridData.DataType(i).Category in [dtcBinary, dtcSpatial]) and (not Mainform.actBlobAsText.Checked) then
|
||||
Data := GridData.BinColAsHex(i)
|
||||
else
|
||||
Data := GridData.Col(i);
|
||||
if not (GridData.DataType(i).Category in [dtcInteger, dtcReal]) then
|
||||
Data := esc(Data);
|
||||
tmp := tmp + Data;
|
||||
|
||||
// Row preamble
|
||||
case ExportFormat of
|
||||
efHTML: tmp := ' <tr>' + CRLF;
|
||||
|
||||
efXML: tmp := #9'<row>' + CRLF;
|
||||
|
||||
efSQL: begin
|
||||
tmp := 'INSERT INTO '+Mainform.Mask(Tablename)+' (';
|
||||
Col := Grid.Header.Columns.GetFirstVisibleColumn;
|
||||
while Col > NoColumn do begin
|
||||
tmp := tmp + Mainform.mask(Grid.Header.Columns[Col].Text)+', ';
|
||||
Col := Grid.Header.Columns.GetNextVisibleColumn(Col);
|
||||
end;
|
||||
Delete(tmp, Length(tmp)-1, 2);
|
||||
tmp := tmp + ') VALUES (';
|
||||
end;
|
||||
|
||||
else tmp := '';
|
||||
end;
|
||||
|
||||
Col := Grid.Header.Columns.GetFirstVisibleColumn;
|
||||
while Col > NoColumn do begin
|
||||
if (GridData.DataType(Col).Category in [dtcBinary, dtcSpatial]) and (not Mainform.actBlobAsText.Checked) then
|
||||
Data := GridData.BinColAsHex(Col)
|
||||
else
|
||||
Data := GridData.Col(Col);
|
||||
// Keep formatted numeric values
|
||||
if (GridData.DataType(Col).Category in [dtcInteger, dtcReal])
|
||||
and (ExportFormat in [efCSV, efHTML])
|
||||
and Mainform.prefExportLocaleNumbers then
|
||||
Data := FormatNumber(Data, False);
|
||||
|
||||
case ExportFormat of
|
||||
efHTML: begin
|
||||
// Handle nulls.
|
||||
if GridData.IsNull(Col) then
|
||||
Data := TEXT_NULL;
|
||||
// Escape HTML control characters in data.
|
||||
Data := htmlentities(Data);
|
||||
tmp := tmp + ' <td class="col' + IntToStr(Col) + '">' + Data + '</td>' + CRLF;
|
||||
end;
|
||||
|
||||
efCSV, efLaTeX: begin
|
||||
// Escape encloser characters inside data per de-facto CSV.
|
||||
Data := StringReplace(Data, Encloser, Encloser+Encloser, [rfReplaceAll]);
|
||||
// Special handling for NULL (MySQL-ism, not de-facto CSV: unquote value)
|
||||
if GridData.IsNull(Col) then
|
||||
Data := 'NULL'
|
||||
else
|
||||
Data := Encloser + Data + Encloser;
|
||||
// Add cell.
|
||||
if tmp <> '' then tmp := tmp + Separator;
|
||||
tmp := tmp + Data;
|
||||
end;
|
||||
|
||||
efXML: begin
|
||||
// Print cell start tag.
|
||||
tmp := tmp + #9#9'<' + Grid.Header.Columns[Col].Text;
|
||||
if GridData.IsNull(Col) then
|
||||
tmp := tmp + ' isnull="true" />' + CRLF
|
||||
else begin
|
||||
if (GridData.DataType(Col).Category in [dtcBinary, dtcSpatial]) and (not Mainform.actBlobAsText.Checked) then
|
||||
tmp := tmp + ' format="hex"';
|
||||
tmp := tmp + '>' + htmlentities(Data) + '</' + Grid.Header.Columns[Col].Text + '>' + CRLF;
|
||||
end;
|
||||
end;
|
||||
|
||||
efSQL: begin
|
||||
if GridData.IsNull(Col) then
|
||||
Data := 'NULL'
|
||||
else if not (GridData.DataType(Col).Category in [dtcInteger, dtcReal]) then
|
||||
Data := esc(Data);
|
||||
tmp := tmp + Data + ', ';
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
Col := Grid.Header.Columns.GetNextVisibleColumn(Col);
|
||||
end;
|
||||
|
||||
// Row epilogue
|
||||
case ExportFormat of
|
||||
efHTML:
|
||||
tmp := tmp + ' </tr>' + CRLF;
|
||||
efCSV, efLaTeX:
|
||||
tmp := tmp + Terminator;
|
||||
efXML:
|
||||
tmp := tmp + #9'</row>' + CRLF;
|
||||
efSQL: begin
|
||||
Delete(tmp, Length(tmp)-1, 2);
|
||||
tmp := tmp + ');' + CRLF;
|
||||
end;
|
||||
tmp := tmp + ', ';
|
||||
end;
|
||||
Delete(tmp, Length(tmp)-1, 2);
|
||||
tmp := tmp + ');' + CRLF;
|
||||
StreamWrite(S, tmp);
|
||||
if SelectionOnly then Node := Grid.GetNextSelected(Node) else Node := Grid.GetNext(Node);
|
||||
|
||||
if SelectionOnly then
|
||||
Node := Grid.GetNextSelected(Node)
|
||||
else
|
||||
Node := Grid.GetNext(Node);
|
||||
if (MaxSize > 0) and Assigned(Node) and (S is TMemoryStream) and (S.Size >= MaxSize) then begin
|
||||
MessageDlg(
|
||||
Format(MSG_COPYMAXSIZE, [FormatByteNumber(MaxSize), FormatNumber(Node.Index), FormatNumber(NodeCount)]),
|
||||
@@ -928,38 +768,34 @@ begin
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
// footer:
|
||||
tmp := CRLF;
|
||||
|
||||
// Footer
|
||||
case ExportFormat of
|
||||
efHTML: begin
|
||||
tmp :=
|
||||
' </tbody>' + CRLF +
|
||||
' </table>' + CRLF + CRLF +
|
||||
' <p>' + CRLF +
|
||||
' <em>generated ' + DateToStr(now) + ' ' + TimeToStr(now) +
|
||||
' by <a href="'+APPDOMAIN+'">' + APPNAME + ' ' + Mainform.AppVersion + '</a></em>' + CRLF +
|
||||
' </p>' + CRLF + CRLF +
|
||||
' </body>' + CRLF +
|
||||
'</html>' + CRLF;
|
||||
end;
|
||||
efXML:
|
||||
tmp := '</table>' + CRLF;
|
||||
efLaTeX:
|
||||
tmp := '\end{tabular}' + CRLF;
|
||||
else
|
||||
tmp := '';
|
||||
end;
|
||||
StreamWrite(S, tmp);
|
||||
|
||||
Mainform.ProgressBarStatus.Visible := False;
|
||||
Mainform.ShowStatusMsg;
|
||||
end;
|
||||
|
||||
|
||||
{***
|
||||
Converts grid contents to LaTeX table.
|
||||
@param Grid Object which holds data to export
|
||||
}
|
||||
procedure GridToLaTeX(Grid: TVirtualStringTree; S: TStream);
|
||||
var
|
||||
i: Integer;
|
||||
tmp: String;
|
||||
begin
|
||||
tmp := '\begin{tabular}{';
|
||||
// Columns
|
||||
for i:=0 to Grid.Header.Columns.Count-1 do begin
|
||||
// Skip hidden key columns
|
||||
if not (coVisible in Grid.Header.Columns[i].Options) then
|
||||
Continue;
|
||||
// Add header item.
|
||||
tmp := tmp + ' c ';
|
||||
end;
|
||||
tmp := tmp + '}' + CRLF;
|
||||
StreamWrite(S, tmp);
|
||||
GridToCsv(Grid, ' & ', '', '\\ \r\n', s);
|
||||
StreamWrite(S, '\end{tabular}'+CRLF);
|
||||
end;
|
||||
|
||||
|
||||
function BestTableName(Data: TMySQLQuery): String;
|
||||
begin
|
||||
|
||||
@@ -1938,7 +1938,7 @@ object MainForm: TMainForm
|
||||
Caption = 'Copy selected rows as SQL'
|
||||
Enabled = False
|
||||
ImageIndex = 114
|
||||
OnExecute = actCopyAsSQLExecute
|
||||
OnExecute = actCopyDataExecute
|
||||
end
|
||||
object actPaste: TAction
|
||||
Tag = 58
|
||||
@@ -2002,7 +2002,7 @@ object MainForm: TMainForm
|
||||
Caption = 'Copy selected rows as HTML'
|
||||
Enabled = False
|
||||
ImageIndex = 32
|
||||
OnExecute = actCopyAsHTMLExecute
|
||||
OnExecute = actCopyDataExecute
|
||||
end
|
||||
object actCopyAsCSV: TAction
|
||||
Tag = 48
|
||||
@@ -2010,21 +2010,21 @@ object MainForm: TMainForm
|
||||
Caption = 'Copy selected rows as CSV'
|
||||
Enabled = False
|
||||
ImageIndex = 49
|
||||
OnExecute = actCopyAsCSVExecute
|
||||
OnExecute = actCopyDataExecute
|
||||
end
|
||||
object actCopyAsXML: TAction
|
||||
Category = 'Export/Import'
|
||||
Caption = 'Copy selected rows as XML'
|
||||
Enabled = False
|
||||
ImageIndex = 48
|
||||
OnExecute = actCopyAsXMLExecute
|
||||
OnExecute = actCopyDataExecute
|
||||
end
|
||||
object actCopyAsLaTeX: TAction
|
||||
Category = 'Export/Import'
|
||||
Caption = 'Copy selected rows as LaTeX table'
|
||||
Enabled = False
|
||||
ImageIndex = 153
|
||||
OnExecute = actCopyAsLaTeXExecute
|
||||
OnExecute = actCopyDataExecute
|
||||
end
|
||||
object actExportData: TAction
|
||||
Category = 'Export/Import'
|
||||
|
||||
134
source/main.pas
134
source/main.pas
@@ -503,14 +503,12 @@ type
|
||||
procedure actApplyFilterExecute(Sender: TObject);
|
||||
procedure actClearEditorExecute(Sender: TObject);
|
||||
procedure actTableToolsExecute(Sender: TObject);
|
||||
procedure actCopyAsHTMLExecute(Sender: TObject);
|
||||
procedure actCopyAsCSVExecute(Sender: TObject);
|
||||
procedure actCopyDataExecute(Sender: TObject);
|
||||
procedure actPrintListExecute(Sender: TObject);
|
||||
procedure actCopyTableExecute(Sender: TObject);
|
||||
procedure ShowStatusMsg(Msg: String=''; PanelNr: Integer=6);
|
||||
function mask(str: String; HasMultiSegments: Boolean=False) : String;
|
||||
procedure actExecuteQueryExecute(Sender: TObject);
|
||||
procedure actCopyAsXMLExecute(Sender: TObject);
|
||||
procedure actCreateDatabaseExecute(Sender: TObject);
|
||||
procedure actDataCancelChangesExecute(Sender: TObject);
|
||||
procedure actExportDataExecute(Sender: TObject);
|
||||
@@ -545,7 +543,6 @@ type
|
||||
procedure actSQLhelpExecute(Sender: TObject);
|
||||
procedure actUpdateCheckExecute(Sender: TObject);
|
||||
procedure actWebbrowse(Sender: TObject);
|
||||
procedure actCopyAsSQLExecute(Sender: TObject);
|
||||
procedure actSelectTreeBackgroundExecute(Sender: TObject);
|
||||
procedure popupQueryPopup(Sender: TObject);
|
||||
procedure lboxQueryHelpersClick(Sender: TObject);
|
||||
@@ -783,7 +780,6 @@ type
|
||||
procedure spltPreviewMoved(Sender: TObject);
|
||||
procedure actDataSaveBlobToFileExecute(Sender: TObject);
|
||||
procedure DataGridColumnResize(Sender: TVTHeader; Column: TColumnIndex);
|
||||
procedure actCopyAsLaTeXExecute(Sender: TObject);
|
||||
private
|
||||
LastHintMousepos: TPoint;
|
||||
LastHintControlIndex: Integer;
|
||||
@@ -2245,79 +2241,43 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.actCopyAsCSVExecute(Sender: TObject);
|
||||
procedure TMainForm.actCopyDataExecute(Sender: TObject);
|
||||
var
|
||||
S: TMemoryStream;
|
||||
S, HTML: TMemoryStream;
|
||||
act: TAction;
|
||||
Content: AnsiString;
|
||||
ExportFormat: TGridExportFormat;
|
||||
begin
|
||||
// Copy data in focused grid as CSV
|
||||
Screen.Cursor := crHourglass;
|
||||
S := TMemoryStream.Create;
|
||||
try
|
||||
GridToCsv(ActiveGrid, prefCSVSeparator, prefCSVEncloser, prefCSVTerminator, S);
|
||||
StreamToClipboard(S, nil, False);
|
||||
finally
|
||||
ShowStatusMsg('Freeing data...');
|
||||
S.Free;
|
||||
ShowStatusMsg;
|
||||
Screen.Cursor := crDefault;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.actCopyAsHTMLExecute(Sender: TObject);
|
||||
var
|
||||
S: TMemoryStream;
|
||||
begin
|
||||
// Copy data in focused grid as HTML table
|
||||
Screen.Cursor := crHourglass;
|
||||
S := TMemoryStream.Create;
|
||||
try
|
||||
GridToHtml(ActiveGrid, S);
|
||||
StreamToClipboard(S, S, True);
|
||||
finally
|
||||
ShowStatusMsg('Freeing data...');
|
||||
S.Free;
|
||||
ShowStatusMsg;
|
||||
Screen.Cursor := crDefault;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.actCopyAsXMLExecute(Sender: TObject);
|
||||
var
|
||||
S: TMemoryStream;
|
||||
begin
|
||||
// Copy data in focused grid as XML
|
||||
Screen.Cursor := crHourglass;
|
||||
S := TMemoryStream.Create;
|
||||
try
|
||||
GridToXml(ActiveGrid, S);
|
||||
StreamToClipboard(S, nil, False);
|
||||
finally
|
||||
ShowStatusMsg('Freeing data...');
|
||||
S.Free;
|
||||
ShowStatusMsg;
|
||||
Screen.Cursor := crDefault;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.actCopyAsSQLExecute(Sender: TObject);
|
||||
var
|
||||
S, HTML: TMemoryStream;
|
||||
Content: AnsiString;
|
||||
begin
|
||||
// Copy data in focused grid as SQL
|
||||
Screen.Cursor := crHourglass;
|
||||
S := TMemoryStream.Create;
|
||||
try
|
||||
GridToSql(ActiveGrid, S);
|
||||
SetLength(Content, S.Size);
|
||||
S.Position := 0;
|
||||
S.Read(PAnsiChar(Content)^, S.Size);
|
||||
SynExporterHTML1.ExportAll(Explode(CRLF, UTF8ToString(Content)));
|
||||
HTML := TMemoryStream.Create;
|
||||
SynExporterHTML1.SaveToStream(HTML);
|
||||
act := Sender as TAction;
|
||||
if act = actCopyAsCSV then
|
||||
ExportFormat := efCSV
|
||||
else if act = actCopyAsHTML then
|
||||
ExportFormat := efHTML
|
||||
else if act = actCopyAsXML then
|
||||
ExportFormat := efXML
|
||||
else if act = actCopyAsSQL then
|
||||
ExportFormat := efSQL
|
||||
else if act = actCopyAsLaTeX then
|
||||
ExportFormat := efLaTeX
|
||||
else
|
||||
ExportFormat := efUnknown;
|
||||
GridExport(ActiveGrid, S, ExportFormat);
|
||||
case ExportFormat of
|
||||
efSQL: begin
|
||||
SetLength(Content, S.Size);
|
||||
S.Position := 0;
|
||||
S.Read(PAnsiChar(Content)^, S.Size);
|
||||
SynExporterHTML1.ExportAll(Explode(CRLF, UTF8ToString(Content)));
|
||||
HTML := TMemoryStream.Create;
|
||||
SynExporterHTML1.SaveToStream(HTML);
|
||||
end;
|
||||
efHTML: HTML := S;
|
||||
else HTML := nil;
|
||||
end;
|
||||
StreamToClipboard(S, HTML, False);
|
||||
finally
|
||||
ShowStatusMsg('Freeing data...');
|
||||
@@ -2328,30 +2288,11 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
|
||||
procedure TMainForm.actCopyAsLaTeXExecute(Sender: TObject);
|
||||
var
|
||||
S: TMemoryStream;
|
||||
begin
|
||||
// Copy data in focused grid as LaTeX table
|
||||
Screen.Cursor := crHourglass;
|
||||
S := TMemoryStream.Create;
|
||||
try
|
||||
GridToLaTeX(ActiveGrid, S);
|
||||
StreamToClipboard(S, S, True);
|
||||
finally
|
||||
ShowStatusMsg('Freeing data...');
|
||||
S.Free;
|
||||
ShowStatusMsg;
|
||||
Screen.Cursor := crDefault;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.actExportDataExecute(Sender: TObject);
|
||||
var
|
||||
Dialog: TSaveDialog;
|
||||
FS: TFileStream;
|
||||
ExportFormat: TGridExportFormat;
|
||||
begin
|
||||
// Save data in current dataset into various text file formats
|
||||
Dialog := SaveDialogExportData;
|
||||
@@ -2359,14 +2300,9 @@ begin
|
||||
Dialog.Title := 'Export result set from '+Dialog.Filename+'...';
|
||||
if Dialog.Execute and (Dialog.FileName <> '') then try
|
||||
Screen.Cursor := crHourGlass;
|
||||
ExportFormat := TGridExportFormat(Dialog.FilterIndex);
|
||||
FS := TFileStream.Create(Dialog.FileName, fmCreate or fmOpenWrite);
|
||||
case Dialog.FilterIndex of
|
||||
1: GridToCsv(ActiveGrid, prefCSVSeparator, prefCSVEncloser, prefCSVTerminator, FS);
|
||||
2: GridToHtml(ActiveGrid, FS);
|
||||
3: GridToXml(ActiveGrid, FS);
|
||||
4: GridToSql(ActiveGrid, FS);
|
||||
5: GridToLaTeX(ActiveGrid, FS);
|
||||
end;
|
||||
GridExport(ActiveGrid, FS, ExportFormat);
|
||||
finally
|
||||
ShowStatusMsg('Freeing data...');
|
||||
FreeAndNil(FS);
|
||||
|
||||
Reference in New Issue
Block a user