How can I prevent that a LCL defined focused empty ListBox implementing some kind of search field - freepascal

I'm working with
FreePascal 3.2.0,
Lazarus 2.0.12,
GTK2 GUI backend
under Ubuntu Linux 18.04.
I have a strange behavior of a TListBox component.
When I focus a empty ListBox by clicking (SetFocus) and
press the [space] key,
some kind of Edit or SearchField appears (..see image below).
The field vanishes after a short period (3 s).
How can I switch off this "feature".
My current work around is to use a handler for the onEnter event to take away the ListBox focus (full code example see below).
procedure TFrmMain.lbxTestEnter(Sender: TObject);
begin
if mUseWorkAround then begin
if LbxTest.Items.Count = 0 then begin
PnlButton.SetFocus;
exit;
end;
end;
end;
I'm not sure if this is the right way.
Full Code Example:
unit UFrmTestListBox;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,
StdCtrls;
const
PNL_MESSAGE = 'WORK AROUND IS %sACTIVE! ';
type
{ TFrmMain }
TFrmMain = class(TForm)
BtnToggle: TButton;
GbxList: TGroupBox;
GbxLog: TGroupBox;
LbxTest: TListBox;
MmLog: TMemo;
PnlButton: TPanel;
SplMain: TSplitter;
procedure BtnToggleClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure lbxTestEnter(Sender: TObject);
private
mUseWorkAround: Boolean;
mCounter: Integer;
procedure OutLn(S:String);
public
end;
var
FrmMain: TFrmMain;
implementation
{$R *.lfm}
procedure TFrmMain.FormCreate(Sender: TObject);
begin
mUseWorkAround:= false;
mCounter := 0;
PnlButton.Caption:=Format(PNL_MESSAGE,['NOT ']);
end;
procedure TFrmMain.lbxTestEnter(Sender: TObject);
begin
inc(mCounter);
OutLn('');
OutLn(Format('--- %d -----------------------', [mCounter]));
OutLn('ListBox onEnter event');
if mUseWorkAround then begin
OutLn('Work around is active!');
if LbxTest.Items.Count = 0 then begin
OutLn('ListBox is empty, change focus to the button panel.');
PnlButton.SetFocus;
exit;
end;
end else begin
OutLn('Work around NOT active!');
OutLn('ListBox is empty, press [SPACE] & EditField appears.')
end;
end;
procedure TFrmMain.BtnToggleClick(Sender: TObject);
begin
mUseWorkAround:=not mUseWorkAround;
if mUseWorkAround then
PnlButton.Caption:=Format(PNL_MESSAGE,[''])
else
PnlButton.Caption:=Format(PNL_MESSAGE,['NOT ']);
end;
procedure TFrmMain.OutLn(S:String);
begin
MmLog.Lines.BeginUpdate;
MmLog.Lines.Append(S);
MmLog.Lines.EndUpdate;
MmLog.SelStart := Length(MmLog.Lines.Text);
end;
end.

Related

add items to a listbox in a function (Delphi 7)

I would like to write a function that checks if a certain letter is in a certain word.
That is the current function (sry for the german)
function woistderbuchstabe (wort, buchstabe:String):String;
VAR i: Integer;
begin
for i:=1 to length(wort) do
if wort[i]=buchstabe then
showmessage(INTtoSTR(i))
//LB_ausgabe.items.add(INTtoSTR(i));
end;
The way it's written now the function actually works. It shows one or several messages with the position(s) of the letter searched for (the variable "buchstabe") in the word "wort". E.g. for wort=abctc and buchstabe=c it shows 3 and 5.
But if i would write it this way
function woistderbuchstabe (wort, buchstabe:String):String;
VAR i: Integer;
begin
for i:=1 to length(wort) do
if wort[i]=buchstabe then
LB_ausgabe.items.add(INTtoSTR(i));
end;
(remove the showmessage and make the ListBox thing actual code)
then I get the error
Undefined Identifier: 'LB_ausgabe'
This is the complete code of the Unit
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
E_kette: TEdit;
E_buchstabe: TEdit;
B_start: TButton;
LB_ausgabe: TListBox;
procedure B_startClick(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function woistderbuchstabe (wort, buchstabe:String):String;
VAR i: Integer;
begin
for i:=1 to length(wort) do
if wort[i]=buchstabe then
showmessage(INTtoSTR(i))
//LB_ausgabe.items.add(INTtoSTR(i));
end;
procedure TForm1.B_startClick(Sender: TObject);
begin
woistderbuchstabe (E_kette.text, E_buchstabe.text);
end;
end.
Pls try to be specific as I'm pretty clueless about Delphi.
Thanks in advance
Function woistderbuchstabe is not a member of your class TForm1... so it doesn't have direct access to it's members unless you specify an instance. I suggest this fix:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
E_kette: TEdit;
E_buchstabe: TEdit;
B_start: TButton;
LB_ausgabe: TListBox;
procedure B_startClick(Sender: TObject);
private
{ Private-Deklarationen }
function woistderbuchstabe (wort, buchstabe:String):String;
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function TForm1.woistderbuchstabe (wort, buchstabe:String):String;
VAR i: Integer;
begin
for i:=1 to length(wort) do
if wort[i]=buchstabe then
LB_ausgabe.items.add(INTtoSTR(i));
end;
procedure TForm1.B_startClick(Sender: TObject);
begin
woistderbuchstabe (E_kette.text, E_buchstabe.text);
end;
end.
But you can also just reference your Form1: TForm1 instance (global variable), in your function (I recommend you stick with the OO approach, though):
Form1.LB_ausgabe.items.add(INTtoSTR(i));
PS: Check Pos and PosEx functions too, as they are probably (I never benchmarked) faster solution, since they are asm implemented.
In my opinion, if you might need to use this logic from somewhere else, you need to decouple it from your user interface. You can do this by changing your function to a procedure that accepts a more generic class to populate (like a plain old TStrings) as a parameter. As TStrings is a common base for TComboBox.Items, TListBox.Items, TMemo.Lines and is used in many other places, this seems like the most flexible way to accomplish what you want to do.
procedure woistderbuchstabe (List: TStrings; wort, buchstabe:String);
VAR i: Integer;
begin
for i := 1 to length(wort) do
if wort[i] = buchstabe then
List.Add(InttoStr(i));
end;
This allows you to use the procedure with your TListBox (call it with LB_ausgabe.items, a TMemo, using Memo1.Lines, a TComboBox with Combobox1.Items, a TRichEdit with RichEdit1.Lines, or a plain old TStringList directly with SL.
You can now call it from anywhere you want, such as a TForm.Button1Click(Sender: TObject), using ListBox1.Items, or a standalone method that creates and passes in a TStringList. It's not tied to a specific form, so it's more flexible and able to be reused elsewhere.

Is there a delphi ScrollInView equivalent in Free Pascal?

I have a ScrollBox and I'm adding controls to it at runtime. However, when the controls exceed the ScrollBox height, I want the ScrollBox to scroll all the way to the bottom so that the newly added controls are visible.
Doing some research, I've found something called "ScrollInView" for delphi. Seeing how many (quite a lot) of Delphi methods/functions are available in Free Pascal, do you know of any equivalent to this particular one? If not, can you help me achieve my goal (Auto-scrolling the ScrollBox to the bottom) with a different solution?
Thanks in advance,
Oscar
Something like this?
procedure TForm1.Button1Click(Sender: TObject);
begin
with TEdit.Create(Self) do
begin
Parent := ScrollBox1;
Left := 10;
Top := ScrollBox1.ControlCount * 40;
ScrollBox1.VertScrollBar.Position := Top;
end;
end;
And here is hte simple implementation of the ScrollInView method:
TScrollBoxHelper = class helper for TScrollBox
procedure ScrollInView(AControl: TControl);
end;
implementation
procedure TScrollBoxHelper.ScrollInView(AControl: TControl);
begin
if AControl.Parent = Self then
begin
Self.VertScrollBar.Position := AControl.Top;
Self.HorzScrollBar.Position := AControl.Left;
end;
end;
Usage:
procedure TForm1.Button2Click(Sender: TObject);
begin
ScrollBox1.ScrollInView(ScrollBox1.Controls[3]);
end;

Delphi XE2 HTML PAGE how can i input value and press a button [duplicate]

i have a html page that it have 3 forms with 3 submit buttons . buttons have no name but they have value :
<input type="submit" VALUE="Login">
How can i find this button with its value and click on it ?
Thanks
procedure TForm1.Button1Click(Sender: TObject);
var
ovElements: OleVariant;
i: Integer;
begin
ovElements := WebBrowser1.OleObject.Document.forms.item(0).elements;
for i := 0 to (ovElements.Length - 1) do
if (ovElements.item(i).tagName = 'INPUT') and
(ovElements.item(i).type = 'SUBMIT') and
(ovElements.item(i).Value = 'Login') then
ovElements.item(i).Click;
end;
I use in this case my Procedure WB_send_Click_by_Value:
procedure WB_send_Click_by_Value(WB: TWebbrowser;form_nr:nativeint;tag,typ,val: string);
var ovElements: OleVariant;
i: Integer;
begin
ovElements := WB.OleObject.Document.forms.item(form_nr).elements;
for i := 0 to (ovElements.Length - 1) do
begin
if AnsiSameText(ovElements.item(i).tagName,tag) and
AnsiSameText(ovElements.item(i).type,typ) and
AnsiSameText(ovElements.item(i).value,val) then
ovElements.item(i).Click;
end;
end;
I use this Procedure for Buttons in WebPage Formular 1 like:
WB_send_Click_by_Value(Webbrowser1,0,'input','submit','ok');
or e.g. for RadioButtons in Form 2 like:
WB_send_Click_by_Value(Webbrowser1,1,'input','radio','dns');

How can I use the IconField argument in LoadFromDataSet function of TGMMarker (GMLIB)

I am using loadfromdataset function of TGMMarker object in GMLib, but I can't get the IconField to work.
I have a BLOB field with a png image to use as a Icon. I load the very same image file directly from the
folder with no problems, but when I try to do it with the IconField argument it gives error.
Here's the code
inherited;
GMMap1.Active := True;
GMMarker1.LoadFromDataSet(Dscameras.DataSet,'Latitude','Longitude',
'Descrição','Distintivo');
Gives this error :"Erro de Script" "Constante de cadeia não finalizada"
The minimum code is:
procedure TForm1.FormCreate(Sender: TObject);
begin
ClientDataSet1.LoadFromFile('markers.xml');
GMMap1.Active := True;
end;
procedure TForm1.GMMap1AfterPageLoaded(Sender: TObject; First: Boolean);
begin
if First then
begin
GMMap1.DoMap;
GMMarker1.LoadFromDataSet(ClientDataSet1, 'lat', 'lng', 'title');
GMMarker1.ZoomToPoints;
end;
end;

Calling TEdit objects based on DB query

I have a form with 7 TEdit having name EditPhone1, EditPhone2 and so on.
In the same form I query a DB to get data to fill those TEdits. Of course I cannot know in advance how many results the query will return.
How can I call the various TEdit objects when looping on the rowcount of the query?
Use FindComponent to "convert" a component name to the component itself:
var
Edit: TEdit;
I: Integer;
begin
DataSet.First;
I := 1;
while not DataSet.Eof do
begin
Edit := TEdit(FindComponent(Format('EditPhone%d', [I])));
if Edit <> nil then
Edit.Text := DataSet.FieldValues['PhoneNo'];
DataSet.Next;
Inc(I);
end;
Now, this requires to hard-code the EditPhone%d string into the source which results in all kinds of maintainability issues. For example: consider renaming the edits.
Alternative 1:
To not rely on the component names, you could instead make use of TLama's idea and add all the edits to a list:
uses
... , Generics.Collections;
type
TForm1 = class(TForm)
EditPhone1: TEdit;
...
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FEdits: TList<TEdit>;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FEdits := TList<TEdit>.Create;
FEdits.AddRange([EditPhone1, EditPhone2, EditPhone3, EditPhone4, EditPhone5,
EditPhone6, EditPhone7]);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FEdits.Free;
end;
procedure TForm1.ADOQuery1AfterOpen(DataSet: TDataSet);
var
I: Integer;
begin
DataSet.First;
I := 0;
while (not DataSet.Eof) and (I < FEdits.Count) do
begin
FEdits[I].Text := DataSet.FieldValues['PhoneNo'];
DataSet.Next;
Inc(I);
end;
end;
This still requires some maintenance in case of adding edits in future.
Alternative 2:
You could also loop over all edits in the form to find the ones tagged to be added to the list, instead of adding them each explicitly:
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
begin
FEdits := TList<TEdit>.Create;
for I := 0 to ComponentCount - 1 do
if (Components[I] is TEdit) and (TEdit(Components[I]).Tag = 1) then
FEdits.Add(TEdit(Components[I]));
end;
But keeping those tags up to date is another burden.
Alternative 3:
I suggest you use a TDBGrid which is a data-component. Opening the linked dataset will automatically add all phone numbers to the grid. With some settings, the grid may kind of look like a couple of edits below each other.
You can, for example, use Tag property, to find needed component. Set all you TEdit's tag from 1 to 7 (or more), and find component by:
Var I: Integer;
MyEdit : TEdit;
For I = 0 To Self.ComponentCount - 1 Do
if (Self.Components[I] IS TEdit) AND (Self.Components[I] AS TEdit).Tag = YourTag
MyEdit = (Self.Components[I] AS TEdit);
You can also dynamically create so many TEdits, you need, and assign Tag property on creation, and find it this code later in runtime.
I'd suggest using DBCtrlGrid. You place your controls for one row on it, and it repeats the controls for as many rows as your data set has.
Get query result (usually using .RowCount property of TDataset return)
After getting the number of row, do iteration to make TEdit and set the text property
Here is sample of code:
...
For i:=0 to RowCount do
Begin
A:=TEdit.Create(self);
A.Parent:=AForm;
A.Top:=i*14;
A.Text:=ADataset.Field(i).AsString;
End;
...