mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
* Add a binary editor to allow editing (var)binary and (tiny|medium|long)blob fields without messing up NULs etc.
* Add a few notes about where AVs seem to randomly occur when GridPost{Insert,Update,Delete} and EnsureDataLoaded invokes the query logic from outside the main thread. Fixes issue #659 as far as the grids go, but not for export functions.
This commit is contained in:
@ -39,6 +39,7 @@ uses
|
|||||||
view in '..\..\source\view.pas' {frmView},
|
view in '..\..\source\view.pas' {frmView},
|
||||||
selectdbobject in '..\..\source\selectdbobject.pas' {frmSelectDBObject},
|
selectdbobject in '..\..\source\selectdbobject.pas' {frmSelectDBObject},
|
||||||
texteditor in '..\..\source\texteditor.pas' {frmTextEditor},
|
texteditor in '..\..\source\texteditor.pas' {frmTextEditor},
|
||||||
|
bineditor in '..\..\source\bineditor.pas' {frmBinEditor},
|
||||||
grideditlinks in '..\..\source\grideditlinks.pas',
|
grideditlinks in '..\..\source\grideditlinks.pas',
|
||||||
uVistaFuncs in '..\..\source\uVistaFuncs.pas';
|
uVistaFuncs in '..\..\source\uVistaFuncs.pas';
|
||||||
|
|
||||||
|
@ -63,6 +63,9 @@
|
|||||||
<DCCReference Include="..\..\source\about.pas">
|
<DCCReference Include="..\..\source\about.pas">
|
||||||
<Form>AboutBox</Form>
|
<Form>AboutBox</Form>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
|
<DCCReference Include="..\..\source\bineditor.pas">
|
||||||
|
<Form>frmBinEditor</Form>
|
||||||
|
</DCCReference>
|
||||||
<DCCReference Include="..\..\source\childwin.pas">
|
<DCCReference Include="..\..\source\childwin.pas">
|
||||||
<Form>MDIChild</Form>
|
<Form>MDIChild</Form>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
@ -109,7 +112,6 @@
|
|||||||
<Form>MainForm</Form>
|
<Form>MainForm</Form>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
<DCCReference Include="..\..\source\mysql_structures.pas" />
|
<DCCReference Include="..\..\source\mysql_structures.pas" />
|
||||||
<DCCReference Include="..\..\source\memoeditor.pas">
|
|
||||||
<Form>frmMemoEditor</Form>
|
<Form>frmMemoEditor</Form>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
<DCCReference Include="..\..\source\mysqlconn.pas" />
|
<DCCReference Include="..\..\source\mysqlconn.pas" />
|
||||||
@ -140,6 +142,9 @@
|
|||||||
<DCCReference Include="..\..\source\tbl_properties.pas">
|
<DCCReference Include="..\..\source\tbl_properties.pas">
|
||||||
<Form>tbl_properties_form</Form>
|
<Form>tbl_properties_form</Form>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
|
<DCCReference Include="..\..\source\texteditor.pas">
|
||||||
|
<Form>frmTextEditor</Form>
|
||||||
|
</DCCReference>
|
||||||
<DCCReference Include="..\..\source\threading.pas" />
|
<DCCReference Include="..\..\source\threading.pas" />
|
||||||
<DCCReference Include="..\..\source\updatecheck.pas">
|
<DCCReference Include="..\..\source\updatecheck.pas">
|
||||||
<Form>frmUpdateCheck</Form>
|
<Form>frmUpdateCheck</Form>
|
||||||
@ -152,4 +157,4 @@
|
|||||||
<Form>frmView</Form>
|
<Form>frmView</Form>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
97
source/bineditor.dfm
Normal file
97
source/bineditor.dfm
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
object frmBinEditor: TfrmBinEditor
|
||||||
|
Left = 0
|
||||||
|
Top = 0
|
||||||
|
BorderStyle = bsSizeToolWin
|
||||||
|
Caption = 'Binary editor'
|
||||||
|
ClientHeight = 95
|
||||||
|
ClientWidth = 215
|
||||||
|
Color = clBtnFace
|
||||||
|
Constraints.MinHeight = 100
|
||||||
|
Constraints.MinWidth = 130
|
||||||
|
Font.Charset = DEFAULT_CHARSET
|
||||||
|
Font.Color = clWindowText
|
||||||
|
Font.Height = -11
|
||||||
|
Font.Name = 'Tahoma'
|
||||||
|
Font.Style = []
|
||||||
|
OldCreateOrder = False
|
||||||
|
Position = poMainFormCenter
|
||||||
|
OnCloseQuery = FormCloseQuery
|
||||||
|
OnCreate = FormCreate
|
||||||
|
OnDestroy = FormDestroy
|
||||||
|
OnShow = FormShow
|
||||||
|
DesignSize = (
|
||||||
|
215
|
||||||
|
95)
|
||||||
|
PixelsPerInch = 96
|
||||||
|
TextHeight = 13
|
||||||
|
object lblTextLength: TLabel
|
||||||
|
Left = 103
|
||||||
|
Top = 77
|
||||||
|
Width = 65
|
||||||
|
Height = 13
|
||||||
|
Anchors = [akLeft, akBottom]
|
||||||
|
BiDiMode = bdLeftToRight
|
||||||
|
Caption = 'lblTextLength'
|
||||||
|
ParentBiDiMode = False
|
||||||
|
end
|
||||||
|
object memoText: TTntMemo
|
||||||
|
Left = 0
|
||||||
|
Top = 0
|
||||||
|
Width = 215
|
||||||
|
Height = 72
|
||||||
|
Align = alTop
|
||||||
|
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||||
|
Lines.Strings = (
|
||||||
|
'memoText')
|
||||||
|
ScrollBars = ssBoth
|
||||||
|
TabOrder = 0
|
||||||
|
WantTabs = True
|
||||||
|
OnChange = memoTextChange
|
||||||
|
OnKeyDown = memoTextKeyDown
|
||||||
|
end
|
||||||
|
object tlbStandard: TToolBar
|
||||||
|
Left = 0
|
||||||
|
Top = 73
|
||||||
|
Width = 97
|
||||||
|
Height = 22
|
||||||
|
Align = alNone
|
||||||
|
Anchors = [akLeft, akBottom]
|
||||||
|
Caption = 'tlbStandard'
|
||||||
|
Images = MainForm.PngImageListMain
|
||||||
|
ParentShowHint = False
|
||||||
|
ShowHint = True
|
||||||
|
TabOrder = 1
|
||||||
|
object btnWrap: TToolButton
|
||||||
|
Left = 0
|
||||||
|
Top = 0
|
||||||
|
Hint = 'Wrap long lines'
|
||||||
|
Caption = 'Wrap long lines'
|
||||||
|
ImageIndex = 62
|
||||||
|
OnClick = btnWrapClick
|
||||||
|
end
|
||||||
|
object btnLoadBinary: TToolButton
|
||||||
|
Left = 23
|
||||||
|
Top = 0
|
||||||
|
Hint = 'Load binary file'
|
||||||
|
Caption = 'Load binary file'
|
||||||
|
ImageIndex = 52
|
||||||
|
OnClick = btnLoadBinaryClick
|
||||||
|
end
|
||||||
|
object btnCancel: TToolButton
|
||||||
|
Left = 46
|
||||||
|
Top = 0
|
||||||
|
Hint = 'Cancel'
|
||||||
|
Caption = 'Cancel'
|
||||||
|
ImageIndex = 26
|
||||||
|
OnClick = btnCancelClick
|
||||||
|
end
|
||||||
|
object btnApply: TToolButton
|
||||||
|
Left = 69
|
||||||
|
Top = 0
|
||||||
|
Hint = 'Apply changes'
|
||||||
|
Caption = 'Apply changes'
|
||||||
|
ImageIndex = 55
|
||||||
|
OnClick = btnApplyClick
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
198
source/bineditor.pas
Normal file
198
source/bineditor.pas
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
unit bineditor;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Windows, Classes, Graphics, Forms, Controls, helpers, StdCtrls, TntStdCtrls, Registry, VirtualTrees,
|
||||||
|
ComCtrls, ToolWin, Dialogs, SysUtils;
|
||||||
|
|
||||||
|
{$I const.inc}
|
||||||
|
|
||||||
|
type
|
||||||
|
TfrmBinEditor = class(TMemoEditor)
|
||||||
|
memoText: TTntMemo;
|
||||||
|
tlbStandard: TToolBar;
|
||||||
|
btnWrap: TToolButton;
|
||||||
|
btnLoadBinary: TToolButton;
|
||||||
|
btnApply: TToolButton;
|
||||||
|
btnCancel: TToolButton;
|
||||||
|
lblTextLength: TLabel;
|
||||||
|
procedure btnApplyClick(Sender: TObject);
|
||||||
|
procedure btnCancelClick(Sender: TObject);
|
||||||
|
procedure btnLoadBinaryClick(Sender: TObject);
|
||||||
|
procedure btnWrapClick(Sender: TObject);
|
||||||
|
procedure FormDestroy(Sender: TObject);
|
||||||
|
procedure FormShow(Sender: TObject);
|
||||||
|
procedure memoTextChange(Sender: TObject);
|
||||||
|
procedure memoTextKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||||||
|
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
||||||
|
procedure FormCreate(Sender: TObject);
|
||||||
|
private
|
||||||
|
{ Private declarations }
|
||||||
|
FModified: Boolean;
|
||||||
|
procedure SetModified(NewVal: Boolean);
|
||||||
|
property Modified: Boolean read FModified write SetModified;
|
||||||
|
public
|
||||||
|
function GetText: WideString; override;
|
||||||
|
procedure SetText(text: WideString); override;
|
||||||
|
procedure SetMaxLength(len: integer); override;
|
||||||
|
procedure SetFont(font: TFont); override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses main;
|
||||||
|
|
||||||
|
{$R *.dfm}
|
||||||
|
|
||||||
|
|
||||||
|
function TfrmBinEditor.GetText: WideString;
|
||||||
|
begin
|
||||||
|
Result := '0x' + memoText.Text;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.SetText(text: WideString);
|
||||||
|
begin
|
||||||
|
// Skip '0x'.
|
||||||
|
memoText.Text := Copy(text, 3);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.SetMaxLength(len: integer);
|
||||||
|
begin
|
||||||
|
// Input: Length in bytes.
|
||||||
|
memoText.MaxLength := len * 2;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.SetFont(font: TFont);
|
||||||
|
begin
|
||||||
|
memoText.Font := font;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.FormCreate(Sender: TObject);
|
||||||
|
begin
|
||||||
|
InheritFont(Font);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.FormDestroy(Sender: TObject);
|
||||||
|
var
|
||||||
|
reg: TRegistry;
|
||||||
|
begin
|
||||||
|
reg := TRegistry.Create;
|
||||||
|
if reg.OpenKey(REGPATH, False) then begin
|
||||||
|
reg.WriteInteger( REGNAME_EDITOR_WIDTH, Width );
|
||||||
|
reg.WriteInteger( REGNAME_EDITOR_HEIGHT, Height );
|
||||||
|
reg.CloseKey;
|
||||||
|
end;
|
||||||
|
reg.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.FormShow(Sender: TObject);
|
||||||
|
begin
|
||||||
|
// Restore form dimensions
|
||||||
|
Width := Mainform.GetRegValue(REGNAME_EDITOR_WIDTH, DEFAULT_EDITOR_WIDTH);
|
||||||
|
Height := Mainform.GetRegValue(REGNAME_EDITOR_HEIGHT, DEFAULT_EDITOR_HEIGHT);
|
||||||
|
// Fix label position:
|
||||||
|
lblTextLength.Top := tlbStandard.Top + (tlbStandard.Height-lblTextLength.Height) div 2;
|
||||||
|
SetWindowSizeGrip(Handle, True);
|
||||||
|
memoText.SelectAll;
|
||||||
|
memoText.SetFocus;
|
||||||
|
memoTextChange(Sender);
|
||||||
|
Modified := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.memoTextKeyDown(Sender: TObject; var Key: Word; Shift:
|
||||||
|
TShiftState);
|
||||||
|
begin
|
||||||
|
case Key of
|
||||||
|
// Cancel by Escape
|
||||||
|
VK_ESCAPE: btnCancelClick(Sender);
|
||||||
|
// Apply changes and end editing by Ctrl + Enter
|
||||||
|
VK_RETURN: if ssCtrl in Shift then btnApplyClick(Sender);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.btnWrapClick(Sender: TObject);
|
||||||
|
var
|
||||||
|
WasModified: Boolean;
|
||||||
|
begin
|
||||||
|
Screen.Cursor := crHourglass;
|
||||||
|
// Changing the scrollbars invoke the OnChange event. We avoid thinking the text was really modified.
|
||||||
|
WasModified := Modified;
|
||||||
|
if memoText.ScrollBars = ssBoth then
|
||||||
|
memoText.ScrollBars := ssVertical
|
||||||
|
else
|
||||||
|
memoText.ScrollBars := ssBoth;
|
||||||
|
TToolbutton(Sender).Down := memoText.ScrollBars = ssVertical;
|
||||||
|
Modified := WasModified;
|
||||||
|
Screen.Cursor := crDefault;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.btnLoadBinaryClick(Sender: TObject);
|
||||||
|
var
|
||||||
|
d: TOpenDialog;
|
||||||
|
begin
|
||||||
|
d := TOpenDialog.Create(Self);
|
||||||
|
d.Filter := 'All binary files (*.*)|*.*';
|
||||||
|
d.FilterIndex := 0;
|
||||||
|
if d.Execute then try
|
||||||
|
Screen.Cursor := crHourglass;
|
||||||
|
memoText.Text := BinToWideHex(ReadBinaryFile(d.FileName, memoText.MaxLength));
|
||||||
|
finally
|
||||||
|
Screen.Cursor := crDefault;
|
||||||
|
end;
|
||||||
|
d.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.btnCancelClick(Sender: TObject);
|
||||||
|
var
|
||||||
|
DoPost: Boolean;
|
||||||
|
begin
|
||||||
|
if Modified then
|
||||||
|
DoPost := MessageDlg('Apply modifications?', mtConfirmation, [mbYes, mbNo], 0) = mrYes
|
||||||
|
else
|
||||||
|
DoPost := False;
|
||||||
|
if DoPost then
|
||||||
|
TCustomVirtualStringTree(Owner).EndEditNode
|
||||||
|
else
|
||||||
|
TCustomVirtualStringTree(Owner).CancelEditNode;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
||||||
|
begin
|
||||||
|
btnCancelClick(Sender);
|
||||||
|
CanClose := False; // Done by editor link
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.btnApplyClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
TCustomVirtualStringTree(Owner).EndEditNode;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.memoTextChange(Sender: TObject);
|
||||||
|
begin
|
||||||
|
lblTextLength.Caption := FormatNumber(Length(memoText.Text) / 2) + ' bytes.';
|
||||||
|
if memoText.MaxLength > 0 then
|
||||||
|
lblTextLength.Caption := lblTextLength.Caption + ' (Max: '+FormatNumber(memoText.MaxLength / 2)+')';
|
||||||
|
Modified := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TfrmBinEditor.SetModified(NewVal: Boolean);
|
||||||
|
begin
|
||||||
|
if FModified <> NewVal then begin
|
||||||
|
FModified := NewVal;
|
||||||
|
btnApply.Enabled := FModified;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
end.
|
@ -1204,7 +1204,11 @@ begin
|
|||||||
rx.Expression := '^((tiny|medium|long)?text|(var)?char)\b(\(\d+\))?';
|
rx.Expression := '^((tiny|medium|long)?text|(var)?char)\b(\(\d+\))?';
|
||||||
if rx.Exec(ColType) then begin
|
if rx.Exec(ColType) then begin
|
||||||
FDataGridResult.Columns[idx].IsText := True;
|
FDataGridResult.Columns[idx].IsText := True;
|
||||||
if ColType = 'tinytext' then
|
if rx.Match[4] <> '' then
|
||||||
|
FDataGridResult.Columns[idx].MaxLength := MakeInt(rx.Match[4])
|
||||||
|
else if ColType = 'tinytext' then
|
||||||
|
// 255 is the width in bytes. If characters that use multiple bytes are
|
||||||
|
// contained, the width in characters is decreased below this number.
|
||||||
FDataGridResult.Columns[idx].MaxLength := 255
|
FDataGridResult.Columns[idx].MaxLength := 255
|
||||||
else if ColType = 'text' then
|
else if ColType = 'text' then
|
||||||
FDataGridResult.Columns[idx].MaxLength := 65535
|
FDataGridResult.Columns[idx].MaxLength := 65535
|
||||||
@ -1212,14 +1216,13 @@ begin
|
|||||||
FDataGridResult.Columns[idx].MaxLength := 16777215
|
FDataGridResult.Columns[idx].MaxLength := 16777215
|
||||||
else if ColType = 'longtext' then
|
else if ColType = 'longtext' then
|
||||||
FDataGridResult.Columns[idx].MaxLength := 4294967295
|
FDataGridResult.Columns[idx].MaxLength := 4294967295
|
||||||
else if rx.Match[4] <> '' then
|
else
|
||||||
FDataGridResult.Columns[idx].MaxLength := MakeInt(rx.Match[4])
|
// Fallback for unknown column types
|
||||||
else // Fallback for unknown column types
|
|
||||||
FDataGridResult.Columns[idx].MaxLength := MaxInt;
|
FDataGridResult.Columns[idx].MaxLength := MaxInt;
|
||||||
end;
|
end;
|
||||||
rx.Expression := '^((tiny|medium|long)?blob|(var)?binary|bit)\b';
|
rx.Expression := '^((tiny|medium|long)?blob|(var)?binary|bit)\b';
|
||||||
if rx.Exec(ColType) then
|
if rx.Exec(ColType) then
|
||||||
FDataGridResult.Columns[idx].IsBlob := True;
|
FDataGridResult.Columns[idx].IsBinary := True;
|
||||||
if Copy(ColType, 1, 5) = 'enum(' then begin
|
if Copy(ColType, 1, 5) = 'enum(' then begin
|
||||||
FDataGridResult.Columns[idx].IsEnum := True;
|
FDataGridResult.Columns[idx].IsEnum := True;
|
||||||
FDataGridResult.Columns[idx].EnumVals := WideStrings.TWideStringList.Create;
|
FDataGridResult.Columns[idx].EnumVals := WideStrings.TWideStringList.Create;
|
||||||
@ -2482,7 +2485,7 @@ begin
|
|||||||
else if ds.Fields[i].DataType in [ftWideString, ftMemo, ftWideMemo] then
|
else if ds.Fields[i].DataType in [ftWideString, ftMemo, ftWideMemo] then
|
||||||
FQueryGridResult.Columns[i].IsText := True
|
FQueryGridResult.Columns[i].IsText := True
|
||||||
else if ds.Fields[i].DataType in [ftBlob] then
|
else if ds.Fields[i].DataType in [ftBlob] then
|
||||||
FQueryGridResult.Columns[i].IsBlob := True;
|
FQueryGridResult.Columns[i].IsBinary := True;
|
||||||
end;
|
end;
|
||||||
SetLength(FQueryGridResult.Rows, 0);
|
SetLength(FQueryGridResult.Rows, 0);
|
||||||
SetLength(FQueryGridResult.Rows, ds.RecordCount);
|
SetLength(FQueryGridResult.Rows, ds.RecordCount);
|
||||||
@ -2491,8 +2494,8 @@ begin
|
|||||||
FQueryGridResult.Rows[i].Loaded := True;
|
FQueryGridResult.Rows[i].Loaded := True;
|
||||||
SetLength(FQueryGridResult.Rows[i].Cells, ds.FieldCount);
|
SetLength(FQueryGridResult.Rows[i].Cells, ds.FieldCount);
|
||||||
for j:=0 to ds.FieldCount-1 do begin
|
for j:=0 to ds.FieldCount-1 do begin
|
||||||
if FQueryGridResult.Columns[j].IsBlob then
|
if FQueryGridResult.Columns[j].IsBinary then
|
||||||
FQueryGridResult.Rows[i].Cells[j].Text := Utf8Decode(ds.Fields[j].AsString)
|
FQueryGridResult.Rows[i].Cells[j].Text := '0x' + BinToWideHex(ds.Fields[j].AsString)
|
||||||
else
|
else
|
||||||
FQueryGridResult.Rows[i].Cells[j].Text := ds.Fields[j].AsWideString;
|
FQueryGridResult.Rows[i].Cells[j].Text := ds.Fields[j].AsWideString;
|
||||||
FQueryGridResult.Rows[i].Cells[j].IsNull := ds.Fields[j].IsNull;
|
FQueryGridResult.Rows[i].Cells[j].IsNull := ds.Fields[j].IsNull;
|
||||||
@ -3575,6 +3578,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Create instance of the progress form (but don't show it yet)
|
// Create instance of the progress form (but don't show it yet)
|
||||||
|
// Todo: This apparently causes an exception if invoked via an event handler?
|
||||||
|
// Classes.TStream.ReadComponent(???)
|
||||||
|
// Classes.InternalReadComponentRes(???,???,???)
|
||||||
|
// Classes.InitComponent(TfrmQueryProgress)
|
||||||
|
// Classes.InitInheritedComponent($17E0710,TForm)
|
||||||
|
// Forms.TCustomForm.Create(???)
|
||||||
FProgressForm := TFrmQueryProgress.Create(Self);
|
FProgressForm := TFrmQueryProgress.Create(Self);
|
||||||
|
|
||||||
{ Launch a thread of execution that passes the query to the server
|
{ Launch a thread of execution that passes the query to the server
|
||||||
@ -5478,8 +5487,8 @@ begin
|
|||||||
for i := start to start + limit - 1 do begin
|
for i := start to start + limit - 1 do begin
|
||||||
SetLength(FDataGridResult.Rows[i].Cells, ds.Fields.Count);
|
SetLength(FDataGridResult.Rows[i].Cells, ds.Fields.Count);
|
||||||
for j := 0 to ds.Fields.Count - 1 do begin
|
for j := 0 to ds.Fields.Count - 1 do begin
|
||||||
if FDataGridResult.Columns[j].IsBlob then
|
if FDataGridResult.Columns[j].IsBinary then
|
||||||
FDataGridResult.Rows[i].Cells[j].Text := Utf8Decode(ds.Fields[j].AsString)
|
FDataGridResult.Rows[i].Cells[j].Text := '0x' + BinToWideHex(ds.Fields[j].AsString)
|
||||||
else
|
else
|
||||||
FDataGridResult.Rows[i].Cells[j].Text := ds.Fields[j].AsWideString;
|
FDataGridResult.Rows[i].Cells[j].Text := ds.Fields[j].AsWideString;
|
||||||
FDataGridResult.Rows[i].Cells[j].IsNull := ds.Fields[j].IsNull;
|
FDataGridResult.Rows[i].Cells[j].IsNull := ds.Fields[j].IsNull;
|
||||||
@ -5489,6 +5498,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
MainForm.ShowStatus( STATUS_MSG_READY );
|
MainForm.ShowStatus( STATUS_MSG_READY );
|
||||||
|
// Todo: Seen an AV next line when this method was invoked via an event handler.
|
||||||
FreeAndNil(ds);
|
FreeAndNil(ds);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -5573,7 +5583,7 @@ begin
|
|||||||
else if r.Columns[Column].isText then
|
else if r.Columns[Column].isText then
|
||||||
if isNull then cl := $60CC60 else cl := clGreen
|
if isNull then cl := $60CC60 else cl := clGreen
|
||||||
// Text field
|
// Text field
|
||||||
else if r.Columns[Column].isBlob then
|
else if r.Columns[Column].isBinary then
|
||||||
if isNull then cl := $CC60CC else cl := clPurple
|
if isNull then cl := $CC60CC else cl := clPurple
|
||||||
else
|
else
|
||||||
if isNull then cl := COLOR_NULLVALUE else cl := clWindowText;
|
if isNull then cl := COLOR_NULLVALUE else cl := clWindowText;
|
||||||
@ -5723,7 +5733,8 @@ begin
|
|||||||
if Row.Cells[i].Modified then begin
|
if Row.Cells[i].Modified then begin
|
||||||
Val := Row.Cells[i].NewText;
|
Val := Row.Cells[i].NewText;
|
||||||
if FDataGridResult.Columns[i].IsFloat then Val := FloatStr(Val);
|
if FDataGridResult.Columns[i].IsFloat then Val := FloatStr(Val);
|
||||||
Val := esc(Val);
|
if not FDataGridResult.Columns[i].IsBinary then Val := esc(Val);
|
||||||
|
if FDataGridResult.Columns[i].IsBinary then CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + FDataGridResult.Columns[i].Name + '".');
|
||||||
if Row.Cells[i].NewIsNull then Val := 'NULL';
|
if Row.Cells[i].NewIsNull then Val := 'NULL';
|
||||||
sql := sql + ' ' + mask(FDataGridResult.Columns[i].Name) + '=' + Val + ', ';
|
sql := sql + ' ' + mask(FDataGridResult.Columns[i].Name) + '=' + Val + ', ';
|
||||||
end;
|
end;
|
||||||
@ -5759,8 +5770,8 @@ begin
|
|||||||
ds := ExecSelectQuery(sql);
|
ds := ExecSelectQuery(sql);
|
||||||
if ds.RecordCount = 1 then begin
|
if ds.RecordCount = 1 then begin
|
||||||
for i := 0 to ds.FieldCount - 1 do begin
|
for i := 0 to ds.FieldCount - 1 do begin
|
||||||
if FDataGridResult.Columns[i].IsBlob then
|
if FDataGridResult.Columns[i].IsBinary then
|
||||||
Row.Cells[i].Text := Utf8Decode(ds.Fields[i].AsString)
|
Row.Cells[i].Text := '0x' + BinToWideHex(ds.Fields[i].AsString)
|
||||||
else
|
else
|
||||||
Row.Cells[i].Text := ds.Fields[i].AsWideString;
|
Row.Cells[i].Text := ds.Fields[i].AsWideString;
|
||||||
Row.Cells[i].IsNull := ds.Fields[i].IsNull;
|
Row.Cells[i].IsNull := ds.Fields[i].IsNull;
|
||||||
@ -5814,7 +5825,7 @@ begin
|
|||||||
KeyVal := Row.Cells[j].Text;
|
KeyVal := Row.Cells[j].Text;
|
||||||
// Quote if needed
|
// Quote if needed
|
||||||
if FDataGridResult.Columns[j].IsFloat then KeyVal := FloatStr(KeyVal);
|
if FDataGridResult.Columns[j].IsFloat then KeyVal := FloatStr(KeyVal);
|
||||||
KeyVal := esc(KeyVal);
|
if not FDataGridResult.Columns[j].IsBinary then KeyVal := esc(KeyVal);
|
||||||
if Row.Cells[j].IsNull then KeyVal := ' IS NULL'
|
if Row.Cells[j].IsNull then KeyVal := ' IS NULL'
|
||||||
else KeyVal := '=' + KeyVal;
|
else KeyVal := '=' + KeyVal;
|
||||||
Result := Result + mask(KeyCols[i]) + KeyVal + ' AND ';
|
Result := Result + mask(KeyCols[i]) + KeyVal + ' AND ';
|
||||||
@ -5898,7 +5909,8 @@ begin
|
|||||||
Cols := Cols + mask(FDataGridResult.Columns[i].Name) + ', ';
|
Cols := Cols + mask(FDataGridResult.Columns[i].Name) + ', ';
|
||||||
Val := Row.Cells[i].NewText;
|
Val := Row.Cells[i].NewText;
|
||||||
if FDataGridResult.Columns[i].IsFloat then Val := FloatStr(Val);
|
if FDataGridResult.Columns[i].IsFloat then Val := FloatStr(Val);
|
||||||
Val := esc(Val);
|
if not FDataGridResult.Columns[i].IsBinary then Val := esc(Val);
|
||||||
|
if FDataGridResult.Columns[i].IsBinary then CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + FDataGridResult.Columns[i].Name + '".');
|
||||||
if Row.Cells[i].NewIsNull then Val := 'NULL';
|
if Row.Cells[i].NewIsNull then Val := 'NULL';
|
||||||
Vals := Vals + Val + ', ';
|
Vals := Vals + Val + ', ';
|
||||||
end;
|
end;
|
||||||
@ -6080,7 +6092,10 @@ var
|
|||||||
DateTimeEditor: TDateTimeEditorLink;
|
DateTimeEditor: TDateTimeEditorLink;
|
||||||
EnumEditor: TEnumEditorLink;
|
EnumEditor: TEnumEditorLink;
|
||||||
begin
|
begin
|
||||||
if FDataGridResult.Columns[Column].IsText then begin
|
if
|
||||||
|
FDataGridResult.Columns[Column].IsText or
|
||||||
|
FDataGridResult.Columns[Column].IsBinary
|
||||||
|
then begin
|
||||||
MemoEditor := TMemoEditorLink.Create;
|
MemoEditor := TMemoEditorLink.Create;
|
||||||
MemoEditor.MaxLength := FDataGridResult.Columns[Column].MaxLength;
|
MemoEditor.MaxLength := FDataGridResult.Columns[Column].MaxLength;
|
||||||
EditLink := MemoEditor;
|
EditLink := MemoEditor;
|
||||||
|
@ -4,8 +4,8 @@ unit grideditlinks;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses Windows, Forms, Graphics, messages, VirtualTrees, texteditor, ComCtrls, SysUtils, Classes,
|
uses Windows, Forms, Graphics, messages, VirtualTrees, texteditor, bineditor, ComCtrls, SysUtils, Classes,
|
||||||
mysql_structures, Main, helpers, TntStdCtrls, WideStrings, StdCtrls;
|
mysql_structures, Main, ChildWin, helpers, TntStdCtrls, WideStrings, StdCtrls;
|
||||||
|
|
||||||
type
|
type
|
||||||
TMemoEditorLink = class(TInterfacedObject, IVTEditLink)
|
TMemoEditorLink = class(TInterfacedObject, IVTEditLink)
|
||||||
@ -95,6 +95,7 @@ end;
|
|||||||
function TMemoEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
|
function TMemoEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
|
||||||
// Retrieves the true text bounds from the owner tree.
|
// Retrieves the true text bounds from the owner tree.
|
||||||
var
|
var
|
||||||
|
IsBinary: Boolean;
|
||||||
Text: WideString;
|
Text: WideString;
|
||||||
F: TFont;
|
F: TFont;
|
||||||
begin
|
begin
|
||||||
@ -110,11 +111,15 @@ begin
|
|||||||
F := TFont.Create;
|
F := TFont.Create;
|
||||||
FTree.GetTextInfo(Node, Column, F, FTextBounds, Text);
|
FTree.GetTextInfo(Node, Column, F, FTextBounds, Text);
|
||||||
|
|
||||||
|
IsBinary := MainForm.ChildWin.FDataGridResult.Columns[Column].IsBinary;
|
||||||
|
|
||||||
// Get wide text of the node.
|
// Get wide text of the node.
|
||||||
Text := FTree.Text[FNode, FColumn];
|
Text := FTree.Text[FNode, FColumn];
|
||||||
|
|
||||||
// Create the editor form
|
// Create the text editor form
|
||||||
FForm := TfrmTextEditor.Create(Ftree);
|
if IsBinary then FForm := TfrmBinEditor.Create(Ftree)
|
||||||
|
else FForm := TfrmTextEditor.Create(Ftree);
|
||||||
|
|
||||||
FForm.SetFont(F);
|
FForm.SetFont(F);
|
||||||
FForm.SetText(Text);
|
FForm.SetText(Text);
|
||||||
FForm.SetMaxLength(MaxLength);
|
FForm.SetMaxLength(MaxLength);
|
||||||
|
@ -51,7 +51,7 @@ type
|
|||||||
DataType: Byte; // @see constants in mysql_structures.pas
|
DataType: Byte; // @see constants in mysql_structures.pas
|
||||||
MaxLength: Cardinal;
|
MaxLength: Cardinal;
|
||||||
IsPriPart: Boolean;
|
IsPriPart: Boolean;
|
||||||
IsBlob: Boolean;
|
IsBinary: Boolean;
|
||||||
IsText: Boolean;
|
IsText: Boolean;
|
||||||
IsEnum: Boolean;
|
IsEnum: Boolean;
|
||||||
IsInt: Boolean;
|
IsInt: Boolean;
|
||||||
@ -150,8 +150,12 @@ type
|
|||||||
function GetFileCharset(Stream: TFileStream): TFileCharset;
|
function GetFileCharset(Stream: TFileStream): TFileCharset;
|
||||||
function ReadTextfileChunk(Stream: TFileStream; FileCharset: TFileCharset; ChunkSize: Int64 = 0): WideString;
|
function ReadTextfileChunk(Stream: TFileStream; FileCharset: TFileCharset; ChunkSize: Int64 = 0): WideString;
|
||||||
function ReadTextfile(Filename: String): WideString;
|
function ReadTextfile(Filename: String): WideString;
|
||||||
|
function ReadBinaryFile(Filename: String; MaxBytes: Int64): string;
|
||||||
procedure CopyToClipboard(Value: WideString);
|
procedure CopyToClipboard(Value: WideString);
|
||||||
procedure StreamToClipboard(S: TMemoryStream);
|
procedure StreamToClipboard(S: TMemoryStream);
|
||||||
|
function WideHexToBin(text: WideString): string;
|
||||||
|
function BinToWideHex(bin: string): WideString;
|
||||||
|
procedure CheckHex(text: WideString; errorMessage: string);
|
||||||
|
|
||||||
var
|
var
|
||||||
MYSQL_KEYWORDS : TStringList;
|
MYSQL_KEYWORDS : TStringList;
|
||||||
@ -194,6 +198,38 @@ var
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function WideHexToBin(text: WideString): string;
|
||||||
|
var
|
||||||
|
buf: string;
|
||||||
|
begin
|
||||||
|
// Todo: test.
|
||||||
|
buf := text;
|
||||||
|
SetLength(Result, Length(text) div 2);
|
||||||
|
HexToBin(@buf[1], @Result[1], Length(Result));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function BinToWideHex(bin: string): WideString;
|
||||||
|
var
|
||||||
|
buf: string;
|
||||||
|
begin
|
||||||
|
SetLength(buf, Length(bin) * 2);
|
||||||
|
BinToHex(@bin[1], @buf[1], Length(buf));
|
||||||
|
Result := buf;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure CheckHex(text: WideString; errorMessage: string);
|
||||||
|
const
|
||||||
|
allowed: string = '0123456789abcdefABCDEF';
|
||||||
|
var
|
||||||
|
i: Cardinal;
|
||||||
|
begin
|
||||||
|
for i := 1 to Length(text) do begin
|
||||||
|
if Pos(text[i], allowed) < 1 then begin
|
||||||
|
raise Exception.Create(errorMessage);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{***
|
{***
|
||||||
Convert a TStringList to a string using a separator-string
|
Convert a TStringList to a string using a separator-string
|
||||||
@ -2444,6 +2480,17 @@ begin
|
|||||||
Stream.Free;
|
Stream.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function ReadBinaryFile(Filename: String; MaxBytes: Int64): string;
|
||||||
|
var
|
||||||
|
Stream: TFileStream;
|
||||||
|
begin
|
||||||
|
Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyNone);
|
||||||
|
Stream.Position := 0;
|
||||||
|
if (MaxBytes < 1) or (MaxBytes > Stream.Size) then MaxBytes := Stream.Size;
|
||||||
|
SetLength(Result, MaxBytes);
|
||||||
|
Stream.Read(PChar(Result)^, Length(Result));
|
||||||
|
Stream.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TUniClipboard }
|
{ TUniClipboard }
|
||||||
|
|
||||||
|
@ -1230,7 +1230,7 @@ var
|
|||||||
f : Textfile;
|
f : Textfile;
|
||||||
tmppath : array[0..MAX_PATH] of char;
|
tmppath : array[0..MAX_PATH] of char;
|
||||||
Content : WideString;
|
Content : WideString;
|
||||||
IsBlob : Boolean;
|
IsBinary : Boolean;
|
||||||
begin
|
begin
|
||||||
g := ChildWin.ActiveGrid;
|
g := ChildWin.ActiveGrid;
|
||||||
if g = nil then begin messagebeep(MB_ICONASTERISK); exit; end;
|
if g = nil then begin messagebeep(MB_ICONASTERISK); exit; end;
|
||||||
@ -1238,19 +1238,19 @@ begin
|
|||||||
showstatus('Saving contents to file...');
|
showstatus('Saving contents to file...');
|
||||||
if g = Childwin.DataGrid then begin
|
if g = Childwin.DataGrid then begin
|
||||||
Content := Childwin.FDataGridResult.Rows[Childwin.DataGrid.FocusedNode.Index].Cells[Childwin.DataGrid.FocusedColumn].Text;
|
Content := Childwin.FDataGridResult.Rows[Childwin.DataGrid.FocusedNode.Index].Cells[Childwin.DataGrid.FocusedColumn].Text;
|
||||||
IsBlob := Childwin.FDataGridResult.Columns[Childwin.DataGrid.FocusedColumn].IsBlob;
|
IsBinary := Childwin.FDataGridResult.Columns[Childwin.DataGrid.FocusedColumn].IsBinary;
|
||||||
end else begin
|
end else begin
|
||||||
Content := Childwin.FQueryGridResult.Rows[Childwin.QueryGrid.FocusedNode.Index].Cells[Childwin.QueryGrid.FocusedColumn].Text;
|
Content := Childwin.FQueryGridResult.Rows[Childwin.QueryGrid.FocusedNode.Index].Cells[Childwin.QueryGrid.FocusedColumn].Text;
|
||||||
IsBlob := Childwin.FQueryGridResult.Columns[Childwin.QueryGrid.FocusedColumn].IsBlob;
|
IsBinary := Childwin.FQueryGridResult.Columns[Childwin.QueryGrid.FocusedColumn].IsBinary;
|
||||||
end;
|
end;
|
||||||
childwin.logsql(g.Name);
|
childwin.logsql(g.Name);
|
||||||
|
|
||||||
GetTempPath(MAX_PATH, tmppath);
|
GetTempPath(MAX_PATH, tmppath);
|
||||||
filename := tmppath;
|
filename := tmppath;
|
||||||
filename := filename+'\'+APPNAME+'-preview.';
|
filename := filename+'\'+APPNAME+'-preview.';
|
||||||
if IsBlob then begin
|
if IsBinary then begin
|
||||||
if pos('JFIF', copy(Content, 0, 20)) <> 0 then
|
if pos('JFIF', copy(Content, 0, 20)) <> 0 then
|
||||||
filename := filename + 'jpg'
|
filename := filename + 'jpeg'
|
||||||
else if StrCmpBegin('GIF', Content) then
|
else if StrCmpBegin('GIF', Content) then
|
||||||
filename := filename + 'gif'
|
filename := filename + 'gif'
|
||||||
else if StrCmpBegin('BM', Content) then
|
else if StrCmpBegin('BM', Content) then
|
||||||
|
@ -4,7 +4,7 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Windows, Classes, Graphics, Forms, Controls, helpers, StdCtrls, TntStdCtrls, Registry, VirtualTrees,
|
Windows, Classes, Graphics, Forms, Controls, helpers, StdCtrls, TntStdCtrls, Registry, VirtualTrees,
|
||||||
ComCtrls, ToolWin, Dialogs;
|
ComCtrls, ToolWin, Dialogs, SysUtils;
|
||||||
|
|
||||||
{$I const.inc}
|
{$I const.inc}
|
||||||
|
|
||||||
@ -60,6 +60,7 @@ end;
|
|||||||
|
|
||||||
procedure TfrmTextEditor.SetMaxLength(len: integer);
|
procedure TfrmTextEditor.SetMaxLength(len: integer);
|
||||||
begin
|
begin
|
||||||
|
// Input: Length in number of bytes.
|
||||||
memoText.MaxLength := len;
|
memoText.MaxLength := len;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user