mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
Fix and simplify delimiter customizing, now that the dropdown was moved to the mainform and didn't fire the OnExit event any longer for some reason. The new mechanism just uses a button (surely including a new icon, sigh...) which asks via InputQuery for the new value. Delimmiter "validation" is only done when setting the value per button, not in parseSql() any longer - avoids being bug-per-bug compatible with MySQL's SQL parser at least in the non interactive mode.
(Hope that code is ok with Franciscos original implementation. At least the old validation IF's are adapted)
This commit is contained in:
@ -103,8 +103,8 @@ const
|
||||
REGNAME_SQLOUTHEIGHT = 'sqloutheight';
|
||||
REGNAME_QUERYHELPERSWIDTH = 'queryhelperswidth';
|
||||
REGNAME_SQLWHEREFILE = 'SQLWhereFile';
|
||||
REGNAME_DELIMITERS = 'delimiters';
|
||||
REGNAME_DELIMITERSELECTED = 'delimiterselected';
|
||||
REGNAME_DELIMITER = 'Delimiter';
|
||||
DEFAULT_DELIMITER = ';';
|
||||
REGNAME_SQLHELPWINLEFT = 'SQLHelp_WindowLeft';
|
||||
REGNAME_SQLHELPWINTOP = 'SQLHelp_WindowTop';
|
||||
REGNAME_SQLHELPWINWIDTH = 'SQLHelp_WindowWidth';
|
||||
@ -209,9 +209,6 @@ const
|
||||
{TeraByte} NAME_TB = ' TB';
|
||||
{PetaByte} NAME_PB = ' PB';
|
||||
|
||||
// See reference: mysql.cpp Ver 14.12 Distrib 5.0.45, for Win32 (ia32): Line 112
|
||||
DEFAULT_DELIMITER = ';';
|
||||
|
||||
// Copied constants from [delphi11]\source\win32\rtl\win\ShlObj.pas to make them
|
||||
// available in Delphi 10. We don't use the constants from ShlObj until delphi 10
|
||||
// support is removed.
|
||||
|
BIN
res/icons/delimiter.png
Normal file
BIN
res/icons/delimiter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 266 B |
@ -516,7 +516,6 @@ type
|
||||
function GetSelectedTable: string;
|
||||
procedure SetSelectedDatabase(db: string);
|
||||
procedure SetSelectedTable(table: string);
|
||||
procedure ProcessClientSQL(command: WideString; parameter: WideString);
|
||||
procedure SaveListSetup( List: TVirtualStringTree );
|
||||
procedure RestoreListSetup( List: TVirtualStringTree );
|
||||
procedure SetVisibleListColumns( List: TVirtualStringTree; Columns: TStringList );
|
||||
@ -2184,7 +2183,7 @@ begin
|
||||
MainForm.actQueryStopOnErrors.Enabled := InQueryTab;
|
||||
MainForm.actQueryWordWrap.Enabled := InQueryTab;
|
||||
Mainform.actClearQueryEditor.Enabled := InQueryTab and NotEmpty;
|
||||
Mainform.ComboBoxQueryDelimiter.Enabled := InQueryTab;
|
||||
Mainform.actSetDelimiter.Enabled := InQueryTab;
|
||||
end;
|
||||
|
||||
|
||||
@ -2435,22 +2434,9 @@ var
|
||||
recordcount : Integer;
|
||||
ds : TDataSet;
|
||||
begin
|
||||
if ( CurrentLine ) then
|
||||
begin
|
||||
// Run current line
|
||||
SQL := parseSQL( SynMemoQuery.LineText, Mainform.Delimiter, ProcessClientSQL );
|
||||
end
|
||||
else
|
||||
if ( Selection ) then
|
||||
begin
|
||||
// Run selection
|
||||
SQL := parseSQL( SynMemoQuery.SelText, Mainform.Delimiter, ProcessClientSQL );
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Run all
|
||||
SQL := parseSQL( SynMemoQuery.Text, Mainform.Delimiter, ProcessClientSQL );
|
||||
end;
|
||||
if CurrentLine then SQL := parseSQL(SynMemoQuery.LineText, Mainform.Delimiter)
|
||||
else if Selection then SQL := parseSQL(SynMemoQuery.SelText, Mainform.Delimiter)
|
||||
else SQL := parseSQL(SynMemoQuery.Text, Mainform.Delimiter);
|
||||
|
||||
if ( SQL.Count = 0 ) then
|
||||
begin
|
||||
@ -5077,24 +5063,6 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
{***
|
||||
Callback procedure able to handle client-side SQL statements such as DELIMITER
|
||||
|
||||
@param command The command/option to be called
|
||||
@param parameter The parameter of command
|
||||
}
|
||||
procedure TMDIChild.ProcessClientSQL(command: WideString; parameter: WideString);
|
||||
begin
|
||||
if command = 'DELIMITER' then
|
||||
Mainform.ComboBoxQueryDelimiterAdd(parameter)
|
||||
else if command = 'CLIENTSQL_ERROR' then begin
|
||||
LogSQL( parameter, True );
|
||||
if Mainform.actQueryStopOnErrors.Checked then
|
||||
raise Exception.Create(parameter);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{**
|
||||
Save setup of a VirtualStringTree to registry
|
||||
}
|
||||
|
@ -44,9 +44,7 @@ type
|
||||
function explode(separator, a: String) :TStringList;
|
||||
procedure ensureValidIdentifier(name: String);
|
||||
function getEnumValues(str: WideString): WideString;
|
||||
function IsValidDelimiter(var s: WideString): WideString;
|
||||
type TParseSQLProcessCommand = procedure(command: WideString; parameter: WideString) of object;
|
||||
function parsesql(sql: WideString; delimiter: WideString; processcommand: TParseSQLProcessCommand = nil) : TWideStringList;
|
||||
function parsesql(sql: WideString; delimiter: WideString) : TWideStringList;
|
||||
function sstr(str: WideString; len: Integer) : WideString;
|
||||
function encrypt(str: String): String;
|
||||
function decrypt(str: String): String;
|
||||
@ -355,41 +353,6 @@ end;
|
||||
|
||||
|
||||
|
||||
{***
|
||||
Test that a delimiter looks reasonable.
|
||||
|
||||
@param s a string to be trimmed and tested.
|
||||
@return s an error message if validation fails, a nil string if it succeeds.
|
||||
}
|
||||
function IsValidDelimiter(var s: WideString): WideString;
|
||||
begin
|
||||
result := '';
|
||||
s := Trim(s);
|
||||
// Test for empty delimiter.
|
||||
if s = '' then result := 'DELIMITER must be followed by a non-comment character or string';
|
||||
// Disallow backslash, because the MySQL CLI does so for some reason.
|
||||
// Then again, is there any reason to be bug-per-bug compatible with some random SQL parser?
|
||||
if Pos('\', s) > 0 then result := 'Backslash disallowed in DELIMITER (because the MySQL CLI does not accept it)';
|
||||
// Disallow stuff which would be negated by the comment parsing logic.
|
||||
if
|
||||
(Pos('/*', s) > 0) or
|
||||
(Pos('--', s) > 0) or
|
||||
(Pos('#', s) > 0)
|
||||
then result := 'Start-of-comment tokens disallowed in DELIMITER (because it would be ignored)';
|
||||
// Disallow stuff which would be negated by the SQL parser (and could slightly confuse it, if at end-of-string).
|
||||
if
|
||||
(Pos('''', s) > 0) or
|
||||
(Pos('`', s) > 0) or
|
||||
(Pos('"', s) > 0)
|
||||
then result := 'String literal markers disallowed in DELIMITER (because it would be ignored)';
|
||||
|
||||
if result <> '' then begin
|
||||
result := WideFormat('Invalid delimiter %s: %s.', [s, result]);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{***
|
||||
Return true if given character represents whitespace.
|
||||
Limitations: only recognizes ANSI whitespace.
|
||||
@ -459,10 +422,9 @@ end;
|
||||
|
||||
@param String (possibly large) bunch of SQL-statements, separated by semicolon
|
||||
@param String SQL start delimiter
|
||||
@param TParseSQLProcessCommand Method that execute actions relative to an object
|
||||
@return TStringList Separated statements
|
||||
}
|
||||
function parsesql(sql: WideString; delimiter: WideString; processcommand: TParseSQLProcessCommand = nil) : TWideStringList;
|
||||
function parsesql(sql: WideString; delimiter: WideString) : TWideStringList;
|
||||
var
|
||||
i, j, start, len : Integer;
|
||||
tmp : WideString;
|
||||
@ -472,26 +434,6 @@ var
|
||||
delimiter_length : Integer;
|
||||
encloser, secchar, thdchar : WideChar;
|
||||
conditional : WideString;
|
||||
msg : WideString;
|
||||
|
||||
{***
|
||||
If a callback for processing client SQL etc was given, invoke it.
|
||||
}
|
||||
procedure CallProcessCommand(command: WideString; parameter: WideString);
|
||||
begin
|
||||
if Assigned(processcommand) then processcommand(command, parameter);
|
||||
end;
|
||||
|
||||
{***
|
||||
Updates the delimiter in the GUI from the one specified via pseudo-SQL.
|
||||
}
|
||||
procedure UpdateDelimiterData(execute_callback: Boolean = true);
|
||||
begin
|
||||
if (execute_callback) then CallProcessCommand('DELIMITER', delimiter);
|
||||
// update the delimiter variables helper
|
||||
delimiter_length := Length(delimiter);
|
||||
end;
|
||||
|
||||
begin
|
||||
result := TWideStringList.Create;
|
||||
sql := trim(sql);
|
||||
@ -507,8 +449,6 @@ begin
|
||||
encloser := ' ';
|
||||
conditional := '';
|
||||
|
||||
UpdateDelimiterData(false);
|
||||
|
||||
i := 0;
|
||||
while i < len do begin
|
||||
i := i + 1;
|
||||
@ -589,14 +529,7 @@ begin
|
||||
if indelimiter then begin
|
||||
if (sql[i] in [WideChar(#13), WideChar(#10)]) or (i = len) then begin
|
||||
if (i = len) then j := 1 else j := 0;
|
||||
tmp := copy(sql, start + 10, i + j - (start + 10));
|
||||
msg := IsValidDelimiter(tmp);
|
||||
if msg = '' then begin
|
||||
delimiter := tmp;
|
||||
UpdateDelimiterData(true);
|
||||
end else begin
|
||||
CallProcessCommand('CLIENTSQL_ERROR', msg);
|
||||
end;
|
||||
delimiter := copy(sql, start + 10, i + j - (start + 10));
|
||||
indelimiter := false;
|
||||
start := i + 1;
|
||||
end;
|
||||
@ -648,6 +581,7 @@ begin
|
||||
end;
|
||||
|
||||
// Add sql sentence.
|
||||
delimiter_length := Length(delimiter);
|
||||
if ((not instring) and (scanReverse(sql, i, delimiter, delimiter_length, false)) or (i = len)) then begin
|
||||
if (i < len) then j := delimiter_length else begin
|
||||
// end of string, add sql sentence but only remove delimiter if it's there
|
||||
|
@ -401,7 +401,7 @@ object MainForm: TMainForm
|
||||
object ToolBarQuery: TToolBar
|
||||
Left = 398
|
||||
Top = 28
|
||||
Width = 353
|
||||
Width = 266
|
||||
Height = 22
|
||||
Align = alNone
|
||||
AutoSize = True
|
||||
@ -463,42 +463,10 @@ object MainForm: TMainForm
|
||||
Top = 0
|
||||
Action = actQueryWordWrap
|
||||
end
|
||||
object Panel1: TPanel
|
||||
object btnSetDelimiter: TToolButton
|
||||
Left = 243
|
||||
Top = 0
|
||||
Width = 110
|
||||
Height = 22
|
||||
BevelOuter = bvNone
|
||||
UseDockManager = False
|
||||
ParentBackground = False
|
||||
TabOrder = 0
|
||||
object LabelQueryDelimiter: TLabel
|
||||
Left = 6
|
||||
Top = 4
|
||||
Width = 45
|
||||
Height = 13
|
||||
Caption = 'Delimiter:'
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clBlack
|
||||
Font.Height = -11
|
||||
Font.Name = 'Tahoma'
|
||||
Font.Style = []
|
||||
ParentFont = False
|
||||
end
|
||||
object ComboBoxQueryDelimiter: TComboBox
|
||||
Left = 55
|
||||
Top = 0
|
||||
Width = 45
|
||||
Height = 21
|
||||
Enabled = False
|
||||
ItemHeight = 13
|
||||
TabOrder = 0
|
||||
OnExit = ComboBoxQueryDelimiterExit
|
||||
Items.Strings = (
|
||||
';'
|
||||
';;'
|
||||
'//')
|
||||
end
|
||||
Action = actSetDelimiter
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1279,6 +1247,14 @@ object MainForm: TMainForm
|
||||
ShortCut = 16466
|
||||
OnExecute = actQueryReplaceExecute
|
||||
end
|
||||
object actSetDelimiter: TAction
|
||||
Category = 'SQL'
|
||||
Caption = 'Set delimiter used in SQL execution'
|
||||
Enabled = False
|
||||
Hint = 'Set delimiter used in SQL execution'
|
||||
ImageIndex = 106
|
||||
OnExecute = actSetDelimiterExecute
|
||||
end
|
||||
end
|
||||
object SaveDialog2: TSaveDialog
|
||||
DefaultExt = 'reg'
|
||||
@ -4413,6 +4389,19 @@ object MainForm: TMainForm
|
||||
44AE426082}
|
||||
Name = 'PngImage105'
|
||||
Background = clWindow
|
||||
end
|
||||
item
|
||||
PngImage.Data = {
|
||||
89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF
|
||||
61000000017352474200AECE1CE90000001874455874536F6674776172650050
|
||||
61696E742E4E45542076332E313072B225920000007D4944415478DA6364A010
|
||||
30E2927861CE700B89BB40E224431B3906A842B9D50362401512F718D0800324
|
||||
19407120526C00D00BEB80942894BB02E885A9A41A30F0B100F2820ED4901C92
|
||||
BD0035640E904A066213A00167C931009C9C819AD570A9C1E785C3404A1C884B
|
||||
80066C22C700504A9C0FD4FC1C9F2B294E480072902F11D735460A0000000049
|
||||
454E44AE426082}
|
||||
Name = 'PngImage106'
|
||||
Background = clWindow
|
||||
end>
|
||||
PngOptions = [pngBlendOnDisabled, pngGrayscaleOnDisabled]
|
||||
Left = 8
|
||||
|
127
source/main.pas
127
source/main.pas
@ -218,12 +218,11 @@ type
|
||||
btnQueryReplace: TToolButton;
|
||||
btnStopOnErrors: TToolButton;
|
||||
btnQueryWordwrap: TToolButton;
|
||||
Panel1: TPanel;
|
||||
ComboBoxQueryDelimiter: TComboBox;
|
||||
LabelQueryDelimiter: TLabel;
|
||||
PopupQueryLoad: TPopupMenu;
|
||||
btnEditView: TToolButton;
|
||||
btnExecuteLine: TToolButton;
|
||||
actSetDelimiter: TAction;
|
||||
btnSetDelimiter: TToolButton;
|
||||
procedure actCreateFieldExecute(Sender: TObject);
|
||||
procedure actEditTablePropertiesExecute(Sender: TObject);
|
||||
procedure actCreateTableExecute(Sender: TObject);
|
||||
@ -284,10 +283,10 @@ type
|
||||
procedure actRefreshExecute(Sender: TObject);
|
||||
procedure actSaveSQLExecute(Sender: TObject);
|
||||
procedure actSaveSQLSnippetExecute(Sender: TObject);
|
||||
procedure actSetDelimiterExecute(Sender: TObject);
|
||||
procedure actSQLhelpExecute(Sender: TObject);
|
||||
procedure actUpdateCheckExecute(Sender: TObject);
|
||||
procedure actWebbrowse(Sender: TObject);
|
||||
procedure ComboBoxQueryDelimiterExit(Sender: TObject);
|
||||
procedure EnsureConnected;
|
||||
function ExecuteRemoteQuery(sender: THandle; query: string): TDataSet;
|
||||
procedure ExecuteRemoteNonQuery(sender: THandle; query: string);
|
||||
@ -302,6 +301,7 @@ type
|
||||
function GetChildwin: TMDIChild;
|
||||
function GetParamValue(const paramChar: Char; const paramName:
|
||||
string; var curIdx: Byte; out paramValue: string): Boolean;
|
||||
procedure UpdateDelimiterHint;
|
||||
public
|
||||
MaintenanceForm: TOptimize;
|
||||
ViewForm: TfrmView;
|
||||
@ -316,7 +316,6 @@ type
|
||||
procedure popupQueryLoadClick( sender: TObject );
|
||||
procedure FillPopupQueryLoad;
|
||||
procedure PopupQueryLoadRemoveAbsentFiles( sender: TObject );
|
||||
procedure ComboBoxQueryDelimiterAdd( delimiter: WideString );
|
||||
function GetRegValue( valueName: String; defaultValue: Integer; Session: String = '' ) : Integer; Overload;
|
||||
function GetRegValue( valueName: String; defaultValue: Boolean; Session: String = '' ) : Boolean; Overload;
|
||||
function GetRegValue( valueName: String; defaultValue: String; Session: String = '' ) : String; Overload;
|
||||
@ -487,9 +486,8 @@ begin
|
||||
WriteInteger(REGNAME_TOOLBARQUERYLEFT, ToolBarQuery.Left);
|
||||
WriteInteger(REGNAME_TOOLBARQUERYTOP, ToolBarQuery.Top);
|
||||
|
||||
// Save the delimiters
|
||||
WriteString( REGNAME_DELIMITERS, ComboBoxQueryDelimiter.Items.Text );
|
||||
WriteInteger( REGNAME_DELIMITERSELECTED, ComboBoxQueryDelimiter.ItemIndex );
|
||||
// Save delimiter
|
||||
WriteString( REGNAME_DELIMITER, Delimiter );
|
||||
end;
|
||||
CloseKey;
|
||||
Free;
|
||||
@ -523,7 +521,6 @@ procedure TMainForm.FormCreate(Sender: TObject);
|
||||
var
|
||||
ws : String;
|
||||
Monitor: TMonitor;
|
||||
delimiters: String;
|
||||
const
|
||||
MoveWinThreshold: Byte = 80;
|
||||
begin
|
||||
@ -563,14 +560,9 @@ begin
|
||||
ToolBarQuery.Left := GetRegValue(REGNAME_TOOLBARQUERYLEFT, ToolBarQuery.Left);
|
||||
ToolBarQuery.Top := GetRegValue(REGNAME_TOOLBARQUERYTOP, ToolBarQuery.Top);
|
||||
|
||||
// Delimiter stuff
|
||||
delimiters := Trim( Mainform.GetRegValue(REGNAME_DELIMITERS, '') );
|
||||
if delimiters <> '' then begin
|
||||
ComboBoxQueryDelimiter.Items.Text := delimiters;
|
||||
ComboBoxQueryDelimiter.ItemIndex := GetRegValue( REGNAME_DELIMITERSELECTED, 0 );
|
||||
end else
|
||||
ComboBoxQueryDelimiter.ItemIndex := ComboBoxQueryDelimiter.Items.IndexOf( DEFAULT_DELIMITER );
|
||||
Delimiter := ComboBoxQueryDelimiter.Text;
|
||||
// Delimiter
|
||||
Delimiter := GetRegValue(REGNAME_DELIMITER, DEFAULT_DELIMITER);
|
||||
UpdateDelimiterHint;
|
||||
|
||||
// Beautify AppRevision
|
||||
if Pos('$Rev: WC', AppRevision) < 1 then
|
||||
@ -1816,61 +1808,6 @@ begin
|
||||
Childwin.SynMemoQuery.WordWrap := TAction(Sender).Checked;
|
||||
end;
|
||||
|
||||
procedure TMainForm.ComboBoxQueryDelimiterExit(Sender: TObject);
|
||||
begin
|
||||
// a delimiter couldn't be empty
|
||||
ComboBoxQueryDelimiter.Text := Trim(ComboBoxQueryDelimiter.Text);
|
||||
// verify if the delimiter combobox isn't empty
|
||||
if ComboBoxQueryDelimiter.Text = '' then begin
|
||||
MessageDlg( 'A delimiter is needed.', mtWarning, [mbOK], 0);
|
||||
ComboBoxQueryDelimiter.SetFocus;
|
||||
end else begin
|
||||
// add the new delimiter to combobox
|
||||
ComboBoxQueryDelimiterAdd(ComboBoxQueryDelimiter.Text);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{***
|
||||
Add a new query delimiter and select it
|
||||
@param term The delimiter to add and/or select
|
||||
}
|
||||
procedure TMainform.ComboBoxQueryDelimiterAdd( delimiter: WideString );
|
||||
var
|
||||
index: Integer;
|
||||
found: Boolean;
|
||||
msg: String;
|
||||
begin
|
||||
// See reference: mysql.cpp Ver 14.12 Distrib 5.0.45, for Win32 (ia32): Line 824
|
||||
// Check that delimiter does not contain a backslash
|
||||
msg := IsValidDelimiter( delimiter );
|
||||
if msg <> '' then begin
|
||||
// rollback the delimiter
|
||||
ComboBoxQueryDelimiter.Text := Delimiter;
|
||||
// notify the user
|
||||
raise Exception.Create( msg );
|
||||
end else begin
|
||||
// the delimiter is case-sensitive, following the implementation
|
||||
// in the MySQL CLI, so we must locate it by hand
|
||||
found := False;
|
||||
for index := 0 to ComboBoxQueryDelimiter.Items.Count - 1 do begin
|
||||
if ComboBoxQueryDelimiter.Items[index] = Delimiter then begin
|
||||
ComboBoxQueryDelimiter.ItemIndex := index;
|
||||
found := True;
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
|
||||
if not found then begin
|
||||
ComboBoxQueryDelimiter.Items.Add( Delimiter );
|
||||
ComboBoxQueryDelimiter.ItemIndex := ComboBoxQueryDelimiter.Items.Count - 1;
|
||||
end;
|
||||
|
||||
Delimiter := ComboBoxQueryDelimiter.Text;
|
||||
Childwin.LogSQL( Format( 'Delimiter changed to %s.', [Delimiter] ));
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.FindDialogQueryFind(Sender: TObject);
|
||||
var
|
||||
@ -2055,4 +1992,50 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
{**
|
||||
Change default delimiter for SQL execution
|
||||
}
|
||||
procedure TMainForm.actSetDelimiterExecute(Sender: TObject);
|
||||
var
|
||||
newVal: String;
|
||||
ok: Boolean;
|
||||
begin
|
||||
// Use a while loop to redisplay the input dialog after setting an invalid value
|
||||
ok := False;
|
||||
while not ok do begin
|
||||
newVal := delimiter;
|
||||
if InputQuery('Set delimiter', 'Delimiter used within SQL execution:', newVal) then begin
|
||||
// Validate new value
|
||||
newVal := Trim(newVal);
|
||||
if (newVal = '')
|
||||
or (Pos('\', newVal) > 0)
|
||||
or (Pos('/*', newVal) > 0)
|
||||
or (Pos('--', newVal) > 0)
|
||||
or (Pos('#', newVal) > 0)
|
||||
or (Pos('''', newVal) > 0)
|
||||
or (Pos('`', newVal) > 0)
|
||||
or (Pos('"', newVal) > 0)
|
||||
then begin
|
||||
MessageDlg('Invalid value: The delimiter must not be empty or contain comment or quoting characters.',
|
||||
mtError, [mbOK], 0);
|
||||
end else begin
|
||||
Delimiter := newVal;
|
||||
UpdateDelimiterHint;
|
||||
ok := True;
|
||||
end;
|
||||
end else // Cancel clicked
|
||||
ok := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{**
|
||||
Sets the hint of the SetDelimiter TAction so it includes the delimiter itself
|
||||
and the user has just to move the mouse over it to see it.
|
||||
}
|
||||
procedure TMainForm.UpdateDelimiterHint;
|
||||
begin
|
||||
actSetDelimiter.Hint := actSetDelimiter.Caption + ' (current value: '+delimiter+')';
|
||||
end;
|
||||
|
||||
end.
|
||||
|
Reference in New Issue
Block a user