Recent

Author Topic: Using TBookmarklist question  (Read 1010 times)

daveinhull

  • Full Member
  • ***
  • Posts: 249
  • 1 divided by nothing must still be 1!
Using TBookmarklist question
« on: December 28, 2018, 06:58:06 pm »
Hi,

I'm using a TBookmarklist to manage tickboxes in a DB Grid. So the DBGrid has two fields, the TBookmarklist in the first one and the actual database field I'm interested in selecting in the second.

All this is being done in a modal form. So OnCreate I
Code: Pascal  [Select]
  1.   RecList1 := TBookmarkList.Create(DbGrid1);  

where RecList1 is defined as
Code: Pascal  [Select]
  1.     RecList1: TBookmarklist;

Then in the DBGrid UserCheckboxState I do
Code: Pascal  [Select]
  1. procedure TForm2.DBGrid1UserCheckboxState(Sender: TObject; Column: TColumn; var AState: TCheckboxState);
  2. begin
  3.   if RecList1.CurrentRowSelected then
  4.     AState := cbChecked
  5.   else
  6.     AState := cbUnchecked;
  7. end;

and in the DBGrid CellClick I do
Code: Pascal  [Select]
  1. procedure TForm2.DBGrid1CellClick(Column: TColumn);
  2. begin
  3.   If Column.Index=0 then
  4.     RecList1.CurrentRowSelected := not RecList1.CurrentRowSelected;
  5. end;  

On the main form I press a button and it shows this form Modal and everything works well. I have a button which cancels the Modal form as
Code: Pascal  [Select]
  1.   ModalResult := mrCancel;  

But the next time I press the main form button to ShowModal this form I get an error External SIGSERV

This only happens if I actually select some of the tickboxes the first time I show the form, i.e. I can show and close the form as many times as I want so long as I don't select any tickboxes.

I think it must have something to do with memory getting released maybe when the form is hidden (I assume it is hidden when I ModalResult := mrCancel as the create routine isn't called again.)

Anyone got any thoughts in this?

Thanks
Dave
Version #:1.8.4 Date 2019-01-08 FPC Version: 3.0.4 and SVN Revision 57972 for x86_64-win64-win32/win64

daveinhull

  • Full Member
  • ***
  • Posts: 249
  • 1 divided by nothing must still be 1!
Re: Using TBookmarklist question
« Reply #1 on: December 29, 2018, 07:04:48 pm »
Some further information:

It would appear that the cause of this problem is if I close and then re-open the dataset associated with the grid that is connected to the Bookmarklist and the issue is in DBGrids (I think), but I'm just not good enough to find out what is going on.

Any help?

Thanks
Version #:1.8.4 Date 2019-01-08 FPC Version: 3.0.4 and SVN Revision 57972 for x86_64-win64-win32/win64

daveinhull

  • Full Member
  • ***
  • Posts: 249
  • 1 divided by nothing must still be 1!
Re: Using TBookmarklist question
« Reply #2 on: December 29, 2018, 07:16:03 pm »
Ok, so a little bit more confident than I thought and traced the code through to

Code: Pascal  [Select]
  1. procedure VisitAll;
  2.   begin
  3.     AIndex := 0;
  4.     i := 0;
  5.     while i<FList.Count do begin
  6.       if FUseCompareBookmarks then
  7.         CompareRes := FDataset.CompareBookmarks(Item, TBookmark(FList[I]))
  8.       else
  9.         CompareRes := MyCompareBookmarks(FDataset, pointer(Item), FList[I]);
  10.       if CompareRes=0 then begin
  11.         result := true;
  12.         AIndex := i;
  13.         exit;
  14.       end;
  15.       inc(i);
  16.     end;
  17.   end;    

It errors on the line (7)
Code: Pascal  [Select]
  1.         CompareRes := FDataset.CompareBookmarks(Item, TBookmark(FList[I]))

Any thoughts?
Dave
Version #:1.8.4 Date 2019-01-08 FPC Version: 3.0.4 and SVN Revision 57972 for x86_64-win64-win32/win64

lucamar

  • Hero Member
  • *****
  • Posts: 1555
Re: Using TBookmarklist question
« Reply #3 on: December 29, 2018, 07:34:03 pm »
A wild guess: either FList got freed/destroyed somewhere before reaching this point or "I" is way out of bounds.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 1.8.4 & 2.0.2 w/FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

daveinhull

  • Full Member
  • ***
  • Posts: 249
  • 1 divided by nothing must still be 1!
Re: Using TBookmarklist question
« Reply #4 on: December 30, 2018, 02:49:23 pm »
I think I have found why it errors.

In DBGrids there is this code
Code: Pascal  [Select]

      if FGrid.DataSource.DataSet=FDataset then
        exit;
      FDataset := FGrid.DataSource.DataSet;
     
      // Note.
      //
      // Some dataset descendants do not implement CompareBookmarks, for these we
      // use MyCompareBookmarks in the hope the allocated bookmark memory is used
      // to hold some kind of record index.
      FUseCompareBookmarks := TMethod(@FDataset.CompareBookmarks).Code<>pointer(@TDataset.CompareBookmarks);
     
      // Note.
      //
      // fpc help say CompareBookmarks should return -1, 0 or 1 ... which imply that
      // bookmarks should be a sorted array (or list). In this scenario binary search
      // is the prefered method for finding a bookmark.
      //
      // The problem here is that TBufDataset and TSQLQuery (and thus TCustomSQLQuery
      // and TCustomBufDataset) CompareBookmarks only return 0 or -1 (some kind of
      // is this a valid bookmark or not), the result is that it appears as an unsorted
      // list (or array) and binary search should not be used.
      //
      // The weird thing is that if we use MyCompareBookmarks which deals with comparing
      // the memory reserved for bookmarks in the hope bookmarks are just some kind of
      // reocord indexes, currently work fine for TCustomBufDataset derived datasets.
      // however using CompareBookmarks is always the right thing to use where implemented.
      //
      // As Dbgrid should be TDataset implementation agnostic this is a way I found
      // to know if the dataset is derived from TCustomBufDataset or not.
      // Once TCustomBufDataset is fixed, remove this ugly note & hack.
      case FDataset.ClassName of
        'TSQLQuery','TBufDataset','TCustomSQLQuery','TCustomBufDataset':
          FCanDoBinarySearch := false;
        else
          FCanDoBinarySearch := true;
      end;


Which suggests that TSQLQuery is a funny dataset and doesn't comply with the CompareBookmarks return values (see comments). But TMethod does return True.
So at the end of this routine, FUseCOmpareBookmarks is True and FCanDoBinarySearch is False.

In the TBookmarklist.Find routine there is this code
Code: Pascal  [Select]

      procedure VisitAll;
      begin
        AIndex := 0;
        i := 0;
        while i<FList.Count do begin
          if FCanDoBinarySearch then
          //if FUseCompareBookmarks then
            CompareRes := FDataset.CompareBookmarks(Item, TBookmark(FList))
          else
            CompareRes := MyCompareBookmarks(FDataset, pointer(Item), FList);
          if CompareRes=0 then begin
            result := true;
            AIndex := i;
            exit;
          end;
          inc(i);
        end;
      end;
     
    begin
      {$ifdef dbgDBGrid}
      DebugLn('%s.Find', [ClassName]);
      {$endif}
     
      Result := False;
      if Item=nil then
        Exit;
      if FCanDoBinarySearch then
        BinarySearch
      else
        VisitAll;
    end;


The main part of the routine checks whether we can do a binary sourt or not and for a TSQLQuery we should so we call VisitAll.

However in VisitAll it then uses FCanDoBinarySearch (which is true) to call the FDataset.COmpareBookmarks routine, but we had already established that this didn't work for a TSQLQuery.

So changing the If condition to FUseCompareBookmarks to get it to go to MyCompareBookmarks works and we don't get an error.

However, it still doesn't work correctly as each time I call the ShowModal form the bookmarklist is now what appears to be random.

Just to clarify what I'm doing. I have create a Form with a DBGrid on it which I'm using for a Filter selection a bit like the MS Access filter selection. The form is opened with a procedural call which specifies which  column of the main DBGrid should be filtered.

So it closes the local SQLQuery, changes the select to the required column and opens it again. It also makes sure that a seperate Bookmarklist is used (I have an array of bookmarks corresponding to the column index).

The problem appears to be in the closing of the SQLQuery whcih I think calls TBookmarkList.Clear somewhere along the line, but can't see where.

Can anyone take this a further as I've about exhausted my knowledge of what is going on, I'm not clear how Bookmarks work.

I do have a work around and that is to have a separate Bookmarklist AND SQLQuery for each column that might be filtered, but I'd like to get some answer on this problem just for knowledge.

Thanks
Dave
Version #:1.8.4 Date 2019-01-08 FPC Version: 3.0.4 and SVN Revision 57972 for x86_64-win64-win32/win64

daveinhull

  • Full Member
  • ***
  • Posts: 249
  • 1 divided by nothing must still be 1!
Re: Using TBookmarklist question
« Reply #5 on: December 31, 2018, 01:40:45 am »
Anyone can help????
Version #:1.8.4 Date 2019-01-08 FPC Version: 3.0.4 and SVN Revision 57972 for x86_64-win64-win32/win64