Recent

Author Topic: [SOLVED] Converting a string/index to upper/lower ...  (Read 5065 times)

1HuntnMan

  • Full Member
  • ***
  • Posts: 197
  • From Delphi 7 to Lazarus
    • NewFound Photo Art
Re: Converting a string/index to upper/lower ...
« Reply #15 on: April 11, 2024, 06:33:55 pm »
Yup, just found that and I already had the JvDBSearchEdit-Property-SearchOptions set as see attach scrncapture. I'm moving on and just putting in the help that using context-sensitive search you have to enter as-is you stored it or something like that...

According to this property setting, there shouldn't be an issue if the user types an index search in all lower caps to find data in an indexed field that the data stored by the user is in all upper-case or upper first letter and the rest lower.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5508
  • Compiler Developer
Re: Converting a string/index to upper/lower ...
« Reply #16 on: April 11, 2024, 10:08:56 pm »
All I mean is that UTF8 is the default in Lazarus and that it is not a native FPC string type. Hence you will need to jump through hoops and take some quirky conversions from decent string types for granted.

Note I am not against that, I merely want to say UTF8 is NOT native so it can become a trouble, as it often does.

I seem to remember there was quite a discussion over supporting UTF8 on the compiler level and it did not materialize. Nowadays, with UNICODE16 string support in the compiler and the assignment compatibility that is less of an issue, but every now and then the lion puts up its roar....
It is really frustrating to have to use UTF8whatever conversion calls.... Your code looks sick, Ill. Dying.
And the conversion unit isn't even part of FPC, which is even more frustrating. It should belong in the RTL.

UTF-8 is simply a code page as far as the compiler/RTL are concerned and thus it is covered by the code page aware AnsiString and thus its a native type. The only thing one needs to be aware of is when indexing a string as a single character might span multiple Byte. Lazarus doesn't use anything special here and simply relies on the mechanisms that the compiler/RTL provide.

1HuntnMan

  • Full Member
  • ***
  • Posts: 197
  • From Delphi 7 to Lazarus
    • NewFound Photo Art
Re: Converting a string/index to upper/lower ...
« Reply #17 on: April 13, 2024, 06:00:30 pm »
Okay, thanks. I'm moving on to continue finishing the development. I'll come back to this later.  Thanks all.

What is rather troublesome is that the JvDBSearchEdit-SearchOptions property allows you to check to search case-insensitive but doesn't seem to work.  See attached...

wp

  • Hero Member
  • *****
  • Posts: 11988
Re: Converting a string/index to upper/lower ...
« Reply #18 on: April 13, 2024, 07:11:23 pm »
In the JvDBSearchEdit demo, the control is working properly. It uses the entered string as a criterion to search a record in the associated dataset in which the field value of the associated DataField matches the entered string. When loCaseInsensitive is in the SearchOptions the case of the field values is ignored, and when loPartialKey is in the SearchOptions the found field value begins with the given string. As I said earlier, I do not fully understand the issue, but I have the impression that you are searching for field names, rather than the values of a given field - this is not what JVDBSearchEdit is doing.

1HuntnMan

  • Full Member
  • ***
  • Posts: 197
  • From Delphi 7 to Lazarus
    • NewFound Photo Art
Re: Converting a string/index to upper/lower ...
« Reply #19 on: April 18, 2024, 08:48:12 pm »
WP, with my testing on this project, mostly from a fellow photographer, you can see from the 2 procedures that I provided, the user is selecting an Index.  Then they use the JvDBSearchEdit to context search via the Index they selected from the DBLookUpCombo.  So they are searching by index.
For example if the user selects Client Name in the DBLookupCombo and then tabs to the JvDBSearchEdit and starts typing for example the Client "Asheville Photography Gallery".  When they select to search by Client Name the Index for the table is set to CUSTOMER.  So, he starts typing to find "Ash..." the second he types the letter 'a', it should move the pointer in the table to the first Client that begins with the letter 'a' but even though in the property of the JvDBSearchEdit is set to CaseInsensitive and also the index CUSTOMER which is also set to ixCaseInsensitive, if he types 'a' nothing happens because there is no Customer stored beginning with a lower-case 'a'.  In the test data, all the customers are stored with the first letter capitalized, i.e. Asheville vs. asheville.  He has to capitalize the first letter A and the pointer jumps directly to Asheville Photography Gallery because it happens to be the first customer starting with a capital A in the table.  But, if he types a lowercase 'a', nothing happens because he didn't type capital A.  So, don't understand totally why this is happening when the CUSTOMER field is indexed ixCaseInsensitive and also the JvDBSearchEdit property is also set to CaseInsensitive.  It should find Asheville no matter if the user types 'a' or 'A' ...

TRon

  • Hero Member
  • *****
  • Posts: 2647
Re: Converting a string/index to upper/lower ...
« Reply #20 on: April 18, 2024, 11:56:37 pm »
Perhaps it is me doing something wrong but I seem to get very confused by your description about the behaviour of jvDBSearchEdit.

I made a simple test project and when executed it is impossible for me to type a character that isn't 'valid' (according to jvDBSearchEdit).

While typing the edit corrects what I write to match the contents of a field, no matter what I try to do (that is unless typing something that does not match (anymore)).

The first attached picture shows user typing m (lowercase, the edit component itself turns it into uppercase and automatically "selects" the rest of the field) and the second is after typing i after the initial m character (also here the component corrects the resulting edit field automatically as well as selects the rest of the characters).

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, DB, SQLDB, SQLite3Conn, Forms, Controls, Graphics, Dialogs,
  9.   DBGrids, StdCtrls, JvDBSearchEdit;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     DataSource1: TDataSource;
  17.     DBGrid1: TDBGrid;
  18.     JvDBSearchEdit1: TJvDBSearchEdit;
  19.     Memo1: TMemo;
  20.     SQLite3Connection1: TSQLite3Connection;
  21.     SQLQuery1: TSQLQuery;
  22.     SQLTransaction1: TSQLTransaction;
  23.     procedure FormCreate(Sender: TObject);
  24.     procedure FormDestroy(Sender: TObject);
  25.     procedure JvDBSearchEdit1Change(Sender: TObject);
  26.   private
  27.     procedure CreateDB;
  28.   public
  29.  
  30.   end;
  31.  
  32. var
  33.   Form1: TForm1;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. { TForm1 }
  40.  
  41. procedure TForm1.FormCreate(Sender: TObject);
  42. begin
  43.   // Clear/init Memo
  44.   Memo1.Clear;
  45.   Memo1.Lines.Values['Status'] := 'Entering form creation';
  46.  
  47.   // Setup transaction
  48.   SQLTransaction1.Active := false;
  49.   SQLTransaction1.Action := caCommitRetaining;
  50.   SQLTransaction1.DataBase := SQLite3Connection1;
  51.  
  52.   // Setup connection
  53.   SQLite3Connection1.Connected := false;
  54.   SQLite3Connection1.LoginPrompt :=false;
  55.   SQLite3Connection1.DatabaseName := ':memory:';
  56.   SQLite3Connection1.Transaction := SQLTransaction1;
  57.  
  58.   // Setup query
  59.   SQLQuery1.DataBase := SQLite3Connection1;
  60.   SQLQuery1.Transaction := SQLTransaction1;
  61.  
  62.   // Setup dataset
  63.   DataSource1.DataSet := SQLQuery1;
  64.  
  65.   // Setup grid
  66.   DBGrid1.DataSource := DataSource1;
  67.  
  68.   // Setup jvdbsearchedit
  69.   JvDBSearchEdit1.DataSource := DataSource1;
  70.   JvDBSearchEdit1.DataField := 'Name';
  71.  
  72.   // Create the database
  73.   CreateDB;
  74.  
  75.   SQLQuery1.Close;
  76.   SQLQuery1.SQL.Text:= 'select * from DATA';
  77.   SQLQuery1.Open;
  78.  
  79.   Memo1.Lines.Values['RecordCount'] := SQLQuery1.RecordCount.ToString;
  80.   Memo1.Lines.Values['Status'] := 'Leaving form creation';
  81. end;
  82.  
  83. procedure TForm1.FormDestroy(Sender: TObject);
  84. begin
  85.   SQLQuery1.Close;
  86.   SQLTransaction1.Active := False;
  87.   SQLite3Connection1.Connected := False;
  88. end;
  89.  
  90. procedure TForm1.JvDBSearchEdit1Change(Sender: TObject);
  91. begin
  92. //  Memo1.Lines.Values['jvDBSearch.DataResult'] := (Sender as TJvDBSearchEdit).DataResult;
  93.   Memo1.Lines.Values['jvDBSearch.DataResult'] := (Sender as TJvDBSearchEdit).Text;
  94.   Memo1.Lines.Values['Status'] := 'Updated jvDBSearchEdit';
  95. end;
  96.  
  97. procedure TForm1.CreateDB;
  98. const
  99.   ItemNames: array of string =
  100.   (
  101.     'Bill','John','Eliza','george','Melinda',
  102.     'Sunshine','Omega','Alice','Fabio','Alvin',
  103.     'medusa', 'Gary', 'michael'
  104.   );
  105. var
  106.   ItemName: string;
  107. begin
  108.   // Setup a table "DATA" in newyl created database
  109.   SQLite3Connection1.ExecuteDirect
  110.   (
  111.     'CREATE TABLE "DATA"'+
  112.     '(' +
  113.       ' "id" Integer NOT NULL PRIMARY KEY AUTOINCREMENT,' +
  114.       ' "Name" Char(128) NOT NULL' +
  115.     ');'
  116.   );
  117.  
  118.   // Populate table
  119.   for ItemName in ItemNames do
  120.   begin
  121.     SQLQuery1.SQL.text := 'INSERT INTO ' + '"DATA"' + '(Name) VALUES (:Name);';
  122.     SQLQuery1.Params.ParamByName('Name').AsString     := ItemName;
  123.     SQLQuery1.ExecSQL;
  124.   end;
  125.  
  126.   // Make database magic happen
  127.   SQLTransaction1.Commit;
  128. end;
  129.  
  130. end.
  131.  
No fancy visual edit of properties, all manual in code. I only placed the components on a form.
« Last Edit: April 19, 2024, 12:29:04 am by TRon »

wp

  • Hero Member
  • *****
  • Posts: 11988
Re: Converting a string/index to upper/lower ...
« Reply #21 on: April 20, 2024, 01:14:55 pm »
TRon, I now created a usable project from your code and ran it - and it is working as expected. I also added a checkbox to temporarily disable the "loCaseInsensitive" option, and this works also (for example: when "mel" is typed and loCaseInsensitive is active the record "Melinda" is found; it is not found, when loCaseInsensitive is not active.

1HuntnMan, I think from your other posts, that you are using a TDbf database. There is a problem with it: TDbf has a strange "understanding" of what "loPartialKey" means - it is happy when the first character matches, any other characters are ignored! Run the following project:
Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. uses
  4.   SysUtils, DB, DBF;
  5.  
  6. const
  7.   FILENAME = 'test.dbf';
  8.   // Select one of the following SEARCH_FOR constants:
  9.   SEARCH_FOR = 'Axyz';   // <--- THIS WILL BE FOUND ALTHOUGH IT IS NOT IN THE DB
  10.   //SEARCH_FOR = 'xyz';  // <--- THIS WILL NOT BE FOUND.
  11.  
  12. var
  13.   Dbf1: TDbf;
  14.  
  15. begin
  16.   if not DirectoryExists('data') then CreateDir('data');
  17.  
  18.   Dbf1 := TDbf.Create(nil);
  19.   Dbf1.FilePath := 'data';
  20.   Dbf1.TableName := FILENAME;
  21.  
  22.   if not FileExists(dbf1.FilePath + FILENAME) then begin
  23.     // Create table
  24.     Dbf1.TableLevel := 7;
  25.     Dbf1.Exclusive := True;
  26.     Dbf1.FieldDefs.Add('Country', ftString, 25, True);
  27.     Dbf1.CreateTable;
  28.  
  29.     // Create a primary and two secondary indexes
  30.     Dbf1.Open;
  31.     Dbf1.AddIndex('idxByCountry', 'Country', [ixCaseInsensitive]);
  32.  
  33.     // Add some data...
  34.     Dbf1.Append;
  35.     Dbf1.FieldByName('Country').AsString := 'France';
  36.     Dbf1.Post;
  37.  
  38.     Dbf1.Append;
  39.     Dbf1.FieldByName('Country').AsString := 'Egypt';
  40.     Dbf1.Post;
  41.  
  42.     Dbf1.Append;
  43.     Dbf1.FieldByName('Country').AsString := 'Indonesia';
  44.     Dbf1.Post;
  45.  
  46.     Dbf1.Append;
  47.     Dbf1.FieldByName('Country').AsString := 'Austria';
  48.     Dbf1.Post;
  49.   end
  50.   else
  51.     Dbf1.Open;
  52.  
  53. //  Dbf1.IndexName := 'idxByCountry';
  54.  
  55.   if Dbf1.Locate('Country', SEARCH_FOR, [loCaseInsensitive, loPartialKey]) then
  56.     WriteLn(SEARCH_FOR, ' found.')
  57.   else
  58.     WriteLn(SEARCH_FOR, ' not found.');
  59.  
  60.   Dbf1.Close;
  61.   Dbf1.Free;
  62.  
  63.   ReadLn;
  64. end.
This code creates a simple dBase table containing some country names, one of them is 'Austria'. When I now call Dbf1.Locate('Country, 'Axyz', [loCaseInsensitive, loPartialKey]) the function reports "found" although the country name 'Axyz' is not in the table! It seems to be sufficient when the first character matches.

Why is this important? Because TJvDBSearchEdit calls it internally in one of its procedures.

I'll write a bug report about this erroneous behaviour, maybe it gets fixed, or maybe I find a fix myself. BUT: This is FPC code and it may take a long time until the fix will be in a released version. Maybe it is better to modify TJvDBSearchEdit such that this critical function can be by-passed. Give me some time...

BTW: The "other" TDbf version in svn is working correctly.

[EDIT]
Bug report for the TDbf issue with patch: https://gitlab.com/freepascal.org/fpc/source/-/issues/40748

« Last Edit: April 20, 2024, 05:26:05 pm by wp »

TRon

  • Hero Member
  • *****
  • Posts: 2647
Re: Converting a string/index to upper/lower ...
« Reply #22 on: April 21, 2024, 02:26:41 am »
@wp: Apologies for not having provided a complete project. My initial thought was that TS did not understood the behaviour of the component correctly. It completely slipped my mind that it could be related to the database back-end.

Good find !!

Thank you very much for the report and suggested solution.

1HuntnMan

  • Full Member
  • ***
  • Posts: 197
  • From Delphi 7 to Lazarus
    • NewFound Photo Art
Re: Converting a string/index to upper/lower ...
« Reply #23 on: April 24, 2024, 06:35:19 pm »
Okay, I couldn't figure this out on my end although I have other forms with no problem.  I basically removed this unit form my project and just recreated it from scratch and no problems.  I used the unit code from the unit I ditched to help recreate the new form by just having the source up in Note++.
Another issue I ran into was with JvDBLookUpCombo just looking up a photographer's name.  It just died. You could click on the component and nothing.  So, replaced the JvDBLookUpCombo with the DBLookUpCombo and the same issue.  There must of been some issue with the code or form.  But on that same form/unit I ditched I had a JvDBLookUpCombo for the user to lookup the status of a photograph. It looked up from a small table with 2 fields, status and description. This worked fine on the unit I ditched.
So, everything testing and working properly. But, thanks for the input and help I saved all you and WP's suggestions and comments in my own little Laz doc.
Take Care... 1HuntnMan 

1HuntnMan

  • Full Member
  • ***
  • Posts: 197
  • From Delphi 7 to Lazarus
    • NewFound Photo Art
Re: Converting a string/index to upper/lower ...
« Reply #24 on: April 25, 2024, 07:01:25 pm »
Yup, I have and I think that's the default for the Property-SearchOptions [loCaseInsensitive,loPartialKey] i.e. both are checked True...

 

TinyPortal © 2005-2018