Replace "Image view" action code which opened BLOBs in external app by a home brown preview area for images in BLOBs, using GraphicEx library for detecting image types. Also, add a "Save to file" action for any grid cell content. Fixes issue #1960. Fixes issue #492. Fixes issue #955.

This commit is contained in:
Ansgar Becker
2010-06-03 23:19:27 +00:00
parent 340d3638f8
commit 0767d5387a
3 changed files with 358 additions and 117 deletions

View File

@@ -93,6 +93,8 @@ const
REGNAME_TOOLBARQUERYTOP = 'ToolBarQueryTop';
REGNAME_QUERYMEMOHEIGHT = 'querymemoheight';
REGNAME_DBTREEWIDTH = 'dbtreewidth';
REGNAME_PREVIEW_HEIGHT = 'DataPreviewHeight';
REGNAME_PREVIEW_ENABLED = 'DataPreviewEnabled';
REGNAME_SQLOUTHEIGHT = 'sqloutheight';
REGNAME_QUERYHELPERSWIDTH = 'queryhelperswidth';
REGNAME_STOPONERRORSINBATCH = 'StopOnErrorsInBatchMode';

View File

@@ -339,13 +339,14 @@ object MainForm: TMainForm
BevelOuter = bvNone
TabOrder = 0
OnDblClick = panelTopDblClick
object Splitter1: TSplitter
object spltDBtree: TSplitter
Left = 169
Top = 0
Width = 4
Height = 357
Cursor = crSizeWE
ResizeStyle = rsUpdate
OnMoved = spltPreviewMoved
end
object pnlLeft: TPanel
Left = 0
@@ -355,11 +356,22 @@ object MainForm: TMainForm
Align = alLeft
BevelOuter = bvNone
TabOrder = 0
object spltPreview: TSplitter
Left = 0
Top = 253
Width = 169
Height = 4
Cursor = crSizeNS
Align = alBottom
ResizeStyle = rsUpdate
Visible = False
OnMoved = spltPreviewMoved
end
object DBtree: TVirtualStringTree
Left = 0
Top = 0
Width = 169
Height = 336
Height = 253
Align = alClient
Constraints.MinWidth = 40
DragMode = dmAutomatic
@@ -411,6 +423,78 @@ object MainForm: TMainForm
WideText = 'Size'
end>
end
object pnlPreview: TPanel
Left = 0
Top = 257
Width = 169
Height = 100
Align = alBottom
BevelOuter = bvNone
TabOrder = 1
Visible = False
DesignSize = (
169
100)
object imgPreview: TImage
AlignWithMargins = True
Left = 0
Top = 25
Width = 169
Height = 75
Margins.Left = 0
Margins.Top = 25
Margins.Right = 0
Margins.Bottom = 0
Align = alClient
Center = True
Proportional = True
Stretch = True
end
object lblPreviewTitle: TLabel
Left = 3
Top = 0
Width = 85
Height = 25
Anchors = [akLeft, akTop, akRight]
AutoSize = False
Caption = 'Preview ...'
EllipsisPosition = epEndEllipsis
ParentShowHint = False
ShowAccelChar = False
ShowHint = True
Layout = tlCenter
end
object ToolBarPreview: TToolBar
Left = 100
Top = 1
Width = 70
Height = 23
Align = alNone
Anchors = [akTop, akRight]
Caption = 'ToolBarPreview'
Images = ImageListMain
TabOrder = 0
Wrapable = False
object btnPreviewCopy: TToolButton
Left = 0
Top = 0
Action = actCopy
end
object btnPreviewSaveToFile: TToolButton
Left = 23
Top = 0
Action = actDataSaveBlobToFile
end
object btnPreviewClose: TToolButton
Left = 46
Top = 0
Hint = 'Close preview'
Caption = 'btnPreviewClose'
ImageIndex = 26
OnClick = actDataPreviewExecute
end
end
end
end
object pnlRight: TPanel
Left = 173
@@ -1581,7 +1665,7 @@ object MainForm: TMainForm
AutoHotkeys = maManual
Images = ImageListMain
Left = 40
Top = 64
Top = 32
object File1: TMenuItem
Tag = 17
Caption = '&File'
@@ -1812,7 +1896,7 @@ object MainForm: TMainForm
object ActionList1: TActionList
Images = ImageListMain
Left = 8
Top = 64
Top = 32
object actSessionManager: TAction
Category = 'File'
Caption = 'Session manager'
@@ -1983,13 +2067,13 @@ object MainForm: TMainForm
ShortCut = 24696
OnExecute = actExecuteQueryExecute
end
object actImageView: TAction
Category = 'Export/Import'
Caption = 'Image view'
Hint = 'View image contents'
object actDataPreview: TAction
Category = 'Data'
Caption = 'Image preview'
Hint = 'Preview image contents from BLOB cells'
ImageIndex = 152
OnExecute = actImageViewExecute
OnUpdate = actImageViewUpdate
OnExecute = actDataPreviewExecute
OnUpdate = actDataPreviewUpdate
end
object actInsertFiles: TAction
Category = 'Export/Import'
@@ -2519,6 +2603,13 @@ object MainForm: TMainForm
ShortCut = 24654
OnExecute = actDataSetNullExecute
end
object actDataSaveBlobToFile: TAction
Category = 'Data'
Caption = 'Save BLOB to file ...'
Hint = 'Save contents to local file ...'
ImageIndex = 10
OnExecute = actDataSaveBlobToFileExecute
end
end
object SaveDialog2: TSaveDialog
DefaultExt = 'reg'
@@ -2526,27 +2617,27 @@ object MainForm: TMainForm
Options = [ofOverwritePrompt, ofHideReadOnly, ofEnableSizing]
Title = 'Export settings from registry...'
Left = 8
Top = 200
Top = 128
end
object OpenDialog2: TOpenDialog
DefaultExt = 'reg'
Filter = 'Registry-files (*.reg)|*.reg|All files (*.*)|*.*'
Title = 'Import settings to registry...'
Left = 72
Top = 232
Top = 160
end
object menuConnections: TPopupMenu
AutoHotkeys = maManual
Images = ImageListMain
OnPopup = menuConnectionsPopup
Left = 72
Top = 64
Top = 32
end
object ImageListMain: TImageList
ColorDepth = cd32Bit
DrawingStyle = dsTransparent
Left = 104
Top = 232
Top = 160
Bitmap = {
494C010199009C00040010001000FFFFFFFF2110FFFFFFFFFFFFFFFF424D3600
0000000000003600000028000000400000007002000001002000000000000070
@@ -7706,13 +7797,13 @@ object MainForm: TMainForm
object PopupQueryLoad: TPopupMenu
Images = ImageListMain
Left = 104
Top = 64
Top = 32
end
object popupDB: TPopupMenu
Images = ImageListMain
OnPopup = popupDBPopup
Left = 136
Top = 64
Top = 32
object menuEditObject: TMenuItem
Action = actEditObject
ShortCut = 32781
@@ -7797,7 +7888,7 @@ object MainForm: TMainForm
Images = ImageListMain
OnPopup = popupHostPopup
Left = 9
Top = 96
Top = 64
object Copy2: TMenuItem
Action = actCopy
end
@@ -7856,25 +7947,25 @@ object MainForm: TMainForm
VariableAttri.Foreground = clPurple
SQLDialect = sqlMySQL
Left = 7
Top = 304
Top = 232
end
object OpenDialog1: TOpenDialog
DefaultExt = 'sql'
Filter = 'SQL-Scripts (*.sql)|*.sql|All files (*.*)|*.*'
Left = 7
Top = 232
Top = 160
end
object TimerHostUptime: TTimer
OnTimer = TimerHostUptimeTimer
Left = 7
Top = 269
Top = 197
end
object popupDataGrid: TPopupMenu
AutoHotkeys = maManual
Images = ImageListMain
OnPopup = popupDataGridPopup
Left = 104
Top = 96
Top = 64
object Copy3: TMenuItem
Action = actCopy
end
@@ -8098,7 +8189,10 @@ object MainForm: TMainForm
end
end
object ViewasHTML1: TMenuItem
Action = actImageView
Action = actDataPreview
end
object SaveBLOBtofile1: TMenuItem
Action = actDataSaveBlobToFile
end
object InsertfilesintoBLOBfields3: TMenuItem
Action = actInsertFiles
@@ -8117,12 +8211,12 @@ object MainForm: TMainForm
object TimerConnected: TTimer
OnTimer = TimerConnectedTimer
Left = 103
Top = 269
Top = 197
end
object popupSqlLog: TPopupMenu
Images = ImageListMain
Left = 8
Top = 128
Top = 96
object Copy1: TMenuItem
Action = actCopy
end
@@ -8155,7 +8249,7 @@ object MainForm: TMainForm
Interval = 5000
OnTimer = actRefreshExecute
Left = 72
Top = 269
Top = 197
end
object SaveDialogExportData: TSaveDialog
DefaultExt = 'csv'
@@ -8165,12 +8259,12 @@ object MainForm: TMainForm
Options = [ofOverwritePrompt, ofEnableSizing]
OnTypeChange = SaveDialogExportDataTypeChange
Left = 40
Top = 200
Top = 128
end
object popupListHeader: TVTHeaderPopupMenu
Images = ImageListMain
Left = 40
Top = 128
Top = 96
end
object SynCompletionProposal: TSynCompletionProposal
Options = [scoLimitToMatchedText, scoUseInsertList, scoUsePrettyText, scoUseBuiltInTimer, scoEndCharCompletion, scoCompleteWithTab, scoCompleteWithEnter]
@@ -8205,31 +8299,31 @@ object MainForm: TMainForm
OnAfterCodeCompletion = SynCompletionProposalAfterCodeCompletion
OnCodeCompletion = SynCompletionProposalCodeCompletion
Left = 40
Top = 304
Top = 232
end
object OpenDialogSQLFile: TOpenDialog
DefaultExt = 'sql'
Filter = 'SQL-Scripts (*.sql)|*.sql|All files (*.*)|*.*'
Options = [ofHideReadOnly, ofAllowMultiSelect, ofEnableSizing]
Left = 40
Top = 232
Top = 160
end
object SaveDialogSQLFile: TSaveDialog
DefaultExt = 'sql'
Filter = 'SQL-Scripts (*.sql)|*.sql|All Files (*.*)|*.*'
Options = [ofOverwritePrompt, ofHideReadOnly, ofEnableSizing]
Left = 72
Top = 200
Top = 128
end
object SynEditSearch1: TSynEditSearch
Left = 72
Top = 304
Top = 232
end
object popupQuery: TPopupMenu
Images = ImageListMain
OnPopup = popupQueryPopup
Left = 104
Top = 128
Top = 96
object MenuRun: TMenuItem
Action = actExecuteQuery
end
@@ -8306,7 +8400,7 @@ object MainForm: TMainForm
object popupQueryHelpers: TPopupMenu
Images = ImageListMain
Left = 136
Top = 128
Top = 96
object menuInsertSnippetAtCursor: TMenuItem
Caption = 'Insert at cursor'
Default = True
@@ -8358,7 +8452,7 @@ object MainForm: TMainForm
object popupFilter: TPopupMenu
Images = ImageListMain
Left = 72
Top = 128
Top = 96
object menuFilterCopy: TMenuItem
Action = actCopy
end
@@ -8387,7 +8481,7 @@ object MainForm: TMainForm
object popupRefresh: TPopupMenu
Images = ImageListMain
Left = 40
Top = 96
Top = 64
object menuAutoRefresh: TMenuItem
Caption = 'Auto refresh'
ShortCut = 16500
@@ -8403,7 +8497,7 @@ object MainForm: TMainForm
Images = ImageListMain
OnPopup = popupMainTabsPopup
Left = 72
Top = 96
Top = 64
object menuNewQueryTab: TMenuItem
Action = actNewQueryTab
end
@@ -8418,11 +8512,11 @@ object MainForm: TMainForm
Interval = 500
OnTimer = TimerFilterVTTimer
Left = 40
Top = 269
Top = 197
end
object SynEditRegexSearch1: TSynEditRegexSearch
Left = 104
Top = 304
Top = 232
end
object SynExporterHTML1: TSynExporterHTML
Color = clWindow
@@ -8436,11 +8530,11 @@ object MainForm: TMainForm
Title = 'Untitled'
UseBackground = False
Left = 136
Top = 304
Top = 232
end
object BalloonHint1: TBalloonHint
Delay = 100
Left = 104
Top = 200
Top = 128
end
end

View File

@@ -10,7 +10,7 @@ unit Main;
interface
uses
Windows, SysUtils, Classes, Graphics, GraphUtil, Forms, Controls, Menus, StdCtrls, Dialogs, Buttons,
Windows, SysUtils, Classes, GraphicEx, Graphics, GraphUtil, Forms, Controls, Menus, StdCtrls, Dialogs, Buttons,
Messages, ExtCtrls, ComCtrls, StdActns, ActnList, ImgList, ToolWin, Clipbrd, SynMemo,
SynEdit, SynEditTypes, SynEditKeyCmds, VirtualTrees, DateUtils,
ShlObj, SynEditMiscClasses, SynEditSearch, SynEditRegexSearch, SynCompletionProposal, SynEditHighlighter,
@@ -126,7 +126,7 @@ type
Exportdata1: TMenuItem;
CopyasXMLdata1: TMenuItem;
actExecuteCurrentQuery: TAction;
actImageView: TAction;
actDataPreview: TAction;
actInsertFiles: TAction;
InsertfilesintoBLOBfields1: TMenuItem;
actExportTables: TAction;
@@ -212,7 +212,7 @@ type
panelTop: TPanel;
pnlLeft: TPanel;
DBtree: TVirtualStringTree;
Splitter1: TSplitter;
spltDBtree: TSplitter;
PageControlMain: TPageControl;
tabData: TTabSheet;
tabDatabase: TTabSheet;
@@ -476,6 +476,16 @@ type
tabsetQuery: TTabSet;
BalloonHint1: TBalloonHint;
actDataSetNull: TAction;
pnlPreview: TPanel;
spltPreview: TSplitter;
imgPreview: TImage;
lblPreviewTitle: TLabel;
ToolBarPreview: TToolBar;
btnPreviewCopy: TToolButton;
btnPreviewSaveToFile: TToolButton;
btnPreviewClose: TToolButton;
actDataSaveBlobToFile: TAction;
SaveBLOBtofile1: TMenuItem;
procedure actCreateDBObjectExecute(Sender: TObject);
procedure menuConnectionsPopup(Sender: TObject);
procedure actExitApplicationExecute(Sender: TObject);
@@ -502,7 +512,8 @@ type
procedure actCreateDatabaseExecute(Sender: TObject);
procedure actDataCancelChangesExecute(Sender: TObject);
procedure actExportDataExecute(Sender: TObject);
procedure actImageViewExecute(Sender: TObject);
procedure actDataPreviewExecute(Sender: TObject);
procedure UpdatePreviewPanel;
procedure actInsertFilesExecute(Sender: TObject);
procedure actDataDeleteExecute(Sender: TObject);
procedure actDataFirstExecute(Sender: TObject);
@@ -765,7 +776,9 @@ type
procedure StatusBarMouseLeave(Sender: TObject);
procedure AnyGridStartOperation(Sender: TBaseVirtualTree; OperationKind: TVTOperationKind);
procedure AnyGridEndOperation(Sender: TBaseVirtualTree; OperationKind: TVTOperationKind);
procedure actImageViewUpdate(Sender: TObject);
procedure actDataPreviewUpdate(Sender: TObject);
procedure spltPreviewMoved(Sender: TObject);
procedure actDataSaveBlobToFileExecute(Sender: TObject);
private
LastHintMousepos: TPoint;
LastHintControlIndex: Integer;
@@ -958,7 +971,8 @@ implementation
uses
About, printlist, mysql_structures, UpdateCheck, uVistaFuncs, runsqlfile,
column_selection, data_sorting, grideditlinks;
column_selection, data_sorting, grideditlinks, jpeg, GIFImg;
{$R *.DFM}
@@ -1124,6 +1138,8 @@ begin
MainReg.WriteInteger( REGNAME_QUERYMEMOHEIGHT, pnlQueryMemo.Height );
MainReg.WriteInteger( REGNAME_QUERYHELPERSWIDTH, pnlQueryHelpers.Width );
MainReg.WriteInteger( REGNAME_DBTREEWIDTH, pnlLeft.width );
MainReg.WriteInteger(REGNAME_PREVIEW_HEIGHT, pnlPreview.Height);
MainReg.WriteBool(REGNAME_PREVIEW_ENABLED, actDataPreview.Checked);
MainReg.WriteInteger( REGNAME_SQLOUTHEIGHT, SynMemoSQLLog.Height );
MainReg.WriteBool(REGNAME_FILTERACTIVE, pnlFilterVT.Tag=Integer(True));
// Convert set to string.
@@ -1288,6 +1304,9 @@ begin
pnlQueryMemo.Height := GetRegValue(REGNAME_QUERYMEMOHEIGHT, pnlQueryMemo.Height);
pnlQueryHelpers.Width := GetRegValue(REGNAME_QUERYHELPERSWIDTH, pnlQueryHelpers.Width);
pnlLeft.Width := GetRegValue(REGNAME_DBTREEWIDTH, pnlLeft.Width);
pnlPreview.Height := GetRegValue(REGNAME_PREVIEW_HEIGHT, pnlPreview.Height);
if GetRegValue(REGNAME_PREVIEW_ENABLED, actDataPreview.Checked) and (not actDataPreview.Checked) then
actDataPreviewExecute(actDataPreview);
SynMemoSQLLog.Height := GetRegValue(REGNAME_SQLOUTHEIGHT, SynMemoSQLLog.Height);
// Force status bar position to below log memo
StatusBar.Top := SynMemoSQLLog.Top + SynMemoSQLLog.Height;
@@ -2345,7 +2364,7 @@ begin
end;
procedure TMainForm.actImageViewUpdate(Sender: TObject);
procedure TMainForm.actDataPreviewUpdate(Sender: TObject);
var
Grid: TVirtualStringTree;
begin
@@ -2357,50 +2376,160 @@ begin
end;
procedure TMainForm.actImageViewExecute(Sender: TObject);
procedure TMainForm.actDataPreviewExecute(Sender: TObject);
var
MakeVisible: Boolean;
begin
// Show or hide preview area
actDataPreview.Checked := not actDataPreview.Checked;
MakeVisible := actDataPreview.Checked;
pnlPreview.Visible := MakeVisible;
spltPreview.Visible := MakeVisible;
if MakeVisible then
UpdatePreviewPanel;
end;
procedure TMainForm.UpdatePreviewPanel;
var
Grid: TVirtualStringTree;
Results: TMySQLQuery;
RowNum: PCardinal;
Filename, Suffix: String;
ImgType: String;
Content, Header: AnsiString;
f: Textfile;
ContentStream: TMemoryStream;
StrLen: Integer;
GraphicClass: TGraphicExGraphicClass;
Graphic: TGraphic;
AllExtensions: TStringList;
i: Integer;
begin
// Open BLOB as image in associated application
// Load BLOB contents into preview area
Grid := ActiveGrid;
if not Assigned(Grid) then
Exit;
Results := GridResult(Grid);
Screen.Cursor := crHourGlass;
ShowStatusMsg('Saving contents to file...');
AnyGridEnsureFullRow(Grid, Grid.FocusedNode);
RowNum := Grid.GetNodeData(Grid.FocusedNode);
Results.RecNo := RowNum^;
Content := AnsiString(Results.Col(Grid.FocusedColumn));
Header := Copy(Content, 1, 50);
Suffix := '';
if Copy(Header, 7, 4) = 'JFIF' then
Suffix := 'jpeg'
else if Copy(Header, 2, 3) = 'PNG' then
Suffix := 'png'
else if Copy(Header, 1, 3) = 'GIF' then
Suffix := 'gif'
else if Copy(Header, 1, 2) = 'BM' then
Suffix := 'bmp'
else if Copy(Header, 3, 2) = #42#0 then
Suffix := 'tif';
try
ShowStatusMsg('Loading contents into image viewer ...');
lblPreviewTitle.Caption := 'Loading ...';
lblPreviewTitle.Repaint;
imgPreview.Picture := nil;
AnyGridEnsureFullRow(Grid, Grid.FocusedNode);
RowNum := Grid.GetNodeData(Grid.FocusedNode);
Results.RecNo := RowNum^;
if Suffix = '' then
MessageDlg('Unrecognized image format. Only JPEG, PNG, GIF and BMP are supported.', mtError, [mbOk], 0)
else begin
Filename := GetTempDir+'\'+APPNAME+'-preview.' + Suffix;
AssignFile(f, Filename);
Rewrite(f);
Write(f, Content);
CloseFile(f);
ShellExec(Filename);
Content := AnsiString(Results.Col(Grid.FocusedColumn));
StrLen := Length(Content);
ContentStream := TMemoryStream.Create;
ContentStream.Write(Content[1], StrLen);
ContentStream.Position := 0;
GraphicClass := FileFormatList.GraphicFromContent(ContentStream);
Graphic := nil;
ContentStream.Position := 0;
ImgType := 'UnknownType';
if GraphicClass <> nil then begin
AllExtensions := TStringList.Create;
FileFormatList.GetExtensionList(AllExtensions);
for i:=0 to AllExtensions.Count-1 do begin
if FileFormatList.GraphicFromExtension(AllExtensions[i]) = GraphicClass then begin
ImgType := UpperCase(AllExtensions[i]);
break;
end;
end;
Graphic := GraphicClass.Create;
end else begin
Header := Copy(Content, 1, 50);
if Copy(Header, 7, 4) = 'JFIF' then begin
ImgType := 'JPEG';
Graphic := TJPEGImage.Create;
end else if Copy(Header, 1, 3) = 'GIF' then begin
ImgType := 'GIF';
Graphic := TGIFImage.Create;
end else if Copy(Header, 1, 2) = 'BM' then begin
ImgType := 'BMP';
Graphic := TBitmap.Create;
end;
end;
if Assigned(Graphic) then begin
try
Graphic.LoadFromStream(ContentStream);
imgPreview.Picture.Graphic := Graphic;
lblPreviewTitle.Caption := ImgType+': '+
IntToStr(Graphic.Width)+' x '+IntToStr(Graphic.Height)+' pixels, 100%, '+
FormatByteNumber(StrLen);
spltPreview.OnMoved(spltPreview);
except on E:EInvalidGraphic do
lblPreviewTitle.Caption := ImgType+': ' + E.Message;
end;
FreeAndNil(ContentStream);
end else
lblPreviewTitle.Caption := 'No image detected.';
finally
lblPreviewTitle.Hint := lblPreviewTitle.Caption;
ShowStatusMsg;
Screen.Cursor := crDefault;
end;
end;
ShowStatusMsg;
Screen.Cursor := crDefault;
procedure TMainForm.spltPreviewMoved(Sender: TObject);
var
rx: TRegExpr;
ZoomFactorW, ZoomFactorH: Integer;
begin
// Do not overscale image so it's never zoomed to more than 100%
if (imgPreview.Picture.Graphic = nil) or (imgPreview.Picture.Graphic.Empty) then
Exit;
imgPreview.Stretch := (imgPreview.Picture.Width > imgPreview.Width) or (imgPreview.Picture.Height > imgPreview.Height);
ZoomFactorW := Trunc(Min(imgPreview.Picture.Width, imgPreview.Width) / imgPreview.Picture.Width * 100);
ZoomFactorH := Trunc(Min(imgPreview.Picture.Height, imgPreview.Height) / imgPreview.Picture.Height * 100);
rx := TRegExpr.Create;
rx.Expression := '(\D)(\d+%)';
lblPreviewTitle.Caption := rx.Replace(lblPreviewTitle.Caption, '${1}'+IntToStr(Min(ZoomFactorH, ZoomFactorW))+'%', true);
lblPreviewTitle.Hint := lblPreviewTitle.Caption;
rx.Free;
end;
procedure TMainForm.actDataSaveBlobToFileExecute(Sender: TObject);
var
Grid: TVirtualStringTree;
Results: TMySQLQuery;
RowNum: PCardinal;
Content: AnsiString;
FileStream: TFileStream;
StrLen: Integer;
Dialog: TSaveDialog;
begin
// Save BLOB to local file
Grid := ActiveGrid;
Results := GridResult(Grid);
Dialog := TSaveDialog.Create(Self);
Dialog.Filter := 'All files (*.*)|*.*';
Dialog.FileName := Results.ColumnOrgNames[Grid.FocusedColumn];
if not (Results.DataType(Grid.FocusedColumn).Category in [dtcBinary, dtcSpatial]) then
Dialog.FileName := Dialog.FileName + '.txt';
if Dialog.Execute then begin
Screen.Cursor := crHourGlass;
AnyGridEnsureFullRow(Grid, Grid.FocusedNode);
RowNum := Grid.GetNodeData(Grid.FocusedNode);
Results.RecNo := RowNum^;
if Results.DataType(Grid.FocusedColumn).Category in [dtcBinary, dtcSpatial] then
Content := AnsiString(Results.Col(Grid.FocusedColumn))
else
Content := Utf8Encode(Results.Col(Grid.FocusedColumn));
StrLen := Length(Content);
try
FileStream := TFileStream.Create(Dialog.FileName, fmCreate or fmOpenWrite);
FileStream.Write(Content[1], StrLen);
except on E:Exception do
MessageDlg(E.Message, mtError, [mbOK], 0);
end;
FreeAndNil(FileStream);
Screen.Cursor := crDefault;
end;
Dialog.Free;
end;
@@ -4059,6 +4188,8 @@ begin
actDataLast.Enabled := inDataOrQueryTabNotEmpty and inGrid;
actDataPostChanges.Enabled := GridHasChanges;
actDataCancelChanges.Enabled := GridHasChanges;
actDataSaveBlobToFile.Enabled := inDataOrQueryTabNotEmpty and Assigned(Grid.FocusedNode);
actDataPreview.Enabled := inDataOrQueryTabNotEmpty and Assigned(Grid.FocusedNode);
// Activate export-options if we're on Data- or Query-tab
actCopyAsCSV.Enabled := inDataOrQueryTabNotEmpty;
@@ -4066,7 +4197,6 @@ begin
actCopyAsXML.Enabled := inDataOrQueryTabNotEmpty;
actCopyAsSQL.Enabled := inDataOrQueryTabNotEmpty;
actExportData.Enabled := inDataOrQueryTabNotEmpty;
actImageView.Enabled := inDataOrQueryTabNotEmpty and Assigned(Grid.FocusedNode);
actDataSetNull.Enabled := inDataOrQueryTab and Assigned(Results) and Assigned(Grid.FocusedNode);
// Manually invoke OnChange event of tabset to fill helper list with data
@@ -6979,6 +7109,8 @@ procedure TMainForm.AnyGridFocusChanged(Sender: TBaseVirtualTree; Node: PVirtual
begin
ValidateControls(Sender);
UpdateLineCharPanel;
if pnlPreview.Visible then
UpdatePreviewPanel;
end;
@@ -7824,6 +7956,7 @@ end;
procedure TMainForm.actCopyOrCutExecute(Sender: TObject);
var
Control: TWinControl;
SendingControl: TComponent;
Edit: TCustomEdit;
Combo: TCustomComboBox;
Grid: TVirtualStringTree;
@@ -7831,50 +7964,63 @@ var
Success, DoCut: Boolean;
SQLStream: TMemoryStream;
IsResultGrid: Boolean;
ClpFormat: Word;
ClpData: THandle;
APalette: HPalette;
begin
// Copy text from a focused control to clipboard
Success := False;
Control := Screen.ActiveControl;
DoCut := Sender = actCut;
if Control is TCustomEdit then begin
Edit := TCustomEdit(Control);
if Edit.SelLength > 0 then begin
if DoCut then Edit.CutToClipboard
else Edit.CopyToClipboard;
Success := True;
end;
end else if Control is TCustomComboBox then begin
Combo := TCustomComboBox(Control);
if Combo.SelLength > 0 then begin
Clipboard.AsText := Combo.SelText;
if DoCut then Combo.SelText := '';
Success := True;
end;
end else if Control is TVirtualStringTree then begin
Grid := Control as TVirtualStringTree;
if Assigned(Grid.FocusedNode) then begin
IsResultGrid := Grid = ActiveGrid;
AnyGridEnsureFullRow(Grid, Grid.FocusedNode);
Clipboard.AsText := Grid.Text[Grid.FocusedNode, Grid.FocusedColumn];
if IsResultGrid and DoCut then
Grid.Text[Grid.FocusedNode, Grid.FocusedColumn] := '';
Success := True;
end;
end else if Control is TSynMemo then begin
SynMemo := Control as TSynMemo;
if SynMemo.SelAvail then begin
// Create both text and HTML clipboard format, so rich text applications can paste highlighted SQL
SynExporterHTML1.ExportAll(Explode(CRLF, SynMemo.SelText));
if DoCut then SynMemo.CutToClipboard
else SynMemo.CopyToClipboard;
SQLStream := TMemoryStream.Create;
SynExporterHTML1.SaveToStream(SQLStream);
StreamToClipboard(nil, SQLStream, False);
SendingControl := (Sender as TAction).ActionComponent;
Screen.Cursor := crHourglass;
try
if SendingControl = btnPreviewCopy then begin
imgPreview.Picture.SaveToClipBoardFormat(ClpFormat, ClpData, APalette);
ClipBoard.SetAsHandle(ClpFormat, ClpData);
Success := True;
end else if Control is TCustomEdit then begin
Edit := TCustomEdit(Control);
if Edit.SelLength > 0 then begin
if DoCut then Edit.CutToClipboard
else Edit.CopyToClipboard;
Success := True;
end;
end else if Control is TCustomComboBox then begin
Combo := TCustomComboBox(Control);
if Combo.SelLength > 0 then begin
Clipboard.AsText := Combo.SelText;
if DoCut then Combo.SelText := '';
Success := True;
end;
end else if Control is TVirtualStringTree then begin
Grid := Control as TVirtualStringTree;
if Assigned(Grid.FocusedNode) then begin
IsResultGrid := Grid = ActiveGrid;
AnyGridEnsureFullRow(Grid, Grid.FocusedNode);
Clipboard.AsText := Grid.Text[Grid.FocusedNode, Grid.FocusedColumn];
if IsResultGrid and DoCut then
Grid.Text[Grid.FocusedNode, Grid.FocusedColumn] := '';
Success := True;
end;
end else if Control is TSynMemo then begin
SynMemo := Control as TSynMemo;
if SynMemo.SelAvail then begin
// Create both text and HTML clipboard format, so rich text applications can paste highlighted SQL
SynExporterHTML1.ExportAll(Explode(CRLF, SynMemo.SelText));
if DoCut then SynMemo.CutToClipboard
else SynMemo.CopyToClipboard;
SQLStream := TMemoryStream.Create;
SynExporterHTML1.SaveToStream(SQLStream);
StreamToClipboard(nil, SQLStream, False);
Success := True;
end;
end;
finally
if not Success then
MessageBeep(MB_ICONASTERISK);
Screen.Cursor := crDefault;
end;
if not Success then
MessageBeep(MB_ICONASTERISK);
end;
@@ -8990,7 +9136,6 @@ begin
ShowStatusMsg('', 1);
end;
procedure TMainForm.AnyGridStartOperation(Sender: TBaseVirtualTree; OperationKind: TVTOperationKind);
begin
// Display status message on long running sort operations