Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2011-05

Sorting StringGrid by column click

21 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Juha gmail

I almost always want my StringGrids to sort automatically when user clicks a column title.
So, I implemeted such feature as property ColumnClickSorts (r30630 + some others for images).
Jesus Reyes and others, please test.

Some notes:
- I created sort glyph images using my artistic skills and Gimp... ... ok, I just rotated an existing image 90 decrees. I would be happy if someone creates / finds better images.

- ImageList is created "on the fly" if not set by user. The sort glyphs and loaded dynamically in any case. The only potential error comes when user first clicks the columns, ImageList gets created, and later user's code assigns another ImageList. It wouldn't crash but wrong images would be shown. I think this is very rare and can easily be avoided, so it is not a real problem.

- Sorting works for all columns but the glyphs are shown only for persistent, user defined columns. It is so by definition. If you want to see images you must define columns.

- Column.Title.ImageIndex is now -1 by default. Value 0 as default is wrong because it is a valid list index.

Regards,
Juha

Reply by email

Alexander Klenin
to Juha gmail
Juha wrote 17 lines
I almost always want my StringGrids to sort automatically when user clicks a column title. So, I implemeted such feature as property ColumnClickSorts (r30630 + some others for images). Jesus Reyes and others, please test. Some notes: - I created sort glyph images using my artistic skills and Gimp... ?... ok, I just rotated an existing image 90 decrees. ?I would be happy if someone creates / finds better images. - ImageList is created "on the fly" if not set by user. The sort glyphs and loaded dynamically in any case. The only potential error comes when user first clicks the columns, ImageList gets created, and later user's code assigns another ImageList. It wouldn't crash but wrong images would be shown. I think this is very rare and can easily be avoided, so it is not a real problem.

When I implemented title images a few years ago, I did it exactly it to allow column sorting indicators.
However, I stopped before hard-coding sorting images into the StringGrid because I did not see an elegant way to do it -- in other words, I specifically wanted to avoid your kind of implementation.
Perhaps I was wrong -- a crude functionality is better than none, just wanted you to know.

- Sorting works for all columns but the glyphs are shown only for persistent, user defined columns. It is so by definition. If you want to see images you must define columns.

See http://bugs.freepascal.org/view.php?id=12823

- Column.Title.ImageIndex is now -1 by default. Value 0 as default is wrong because it is a valid list index.

Agreed, it was a mistake.

Alexander S. Klenin

Reply by email

Juha gmail
to Alexander Klenin

Alexander Klenin kirjoitti maanantai, 9. toukokuuta 2011 13:51:33:

Show quoted text 14 lines
> - ImageList is created "on the fly" if not set by user. > The sort glyphs and loaded dynamically in any case. > The only potential error comes when user first clicks the columns, > ImageList gets created, and later user's code assigns another ImageList. > It wouldn't crash but wrong images would be shown. I think this is very > rare and can easily be avoided, so it is not a real problem. When I implemented title images a few years ago, I did it exactly it to allow column sorting indicators. However, I stopped before hard-coding sorting images into the StringGrid because I did not see an elegant way to do it -- in other words, I specifically wanted to avoid your kind of implementation. Perhaps I was wrong -- a crude functionality is better than none, just wanted you to know.

Hmmm...
IMO this implementation is rather elegant. How else would you do it? You could maintain 2 ImageList references and then guess which one to use but it is not better.
Typically if a user enables the automatic sorting feature, he doesn't need to assign his own ImageList.
But, maybe there should be 2 separate settings for automatic sorting and for showing glyps. Don't know...

Show quoted text 5 lines
> - Sorting works for all columns but the glyphs are shown only for > persistent, user defined columns. It is so by definition. If you want to > see images you must define columns. See http://bugs.freepascal.org/view.php?id=12823

That would indeed improve things. I added a note to the issue.

Regards,
Juha

Reply by email

Juha gmail
to Juha gmail

Juha (gmail) kirjoitti maanantai, 9. toukokuuta 2011 14:11:47:

Typically if a user enables the automatic sorting feature, he doesn't need to assign his own ImageList.

Besides, the code still works if the user has assigned his own ImageList. The built-in glyps are added dynamically to the list. Elegant isn't it!

Juha

Reply by email

Alexander Klenin
to Juha gmail
Juha wrote 7 lines
Juha (gmail) kirjoitti maanantai, 9. toukokuuta 2011 14:11:47: > Typically if a user enables the automatic sorting feature, he doesn't need > to assign his own ImageList. Besides, the code still works if the user has assigned his own ImageList. The built-in glyps are added dynamically to the list. Elegant isn't it!

But what if the user wants his own images for sorting? Then again, maybe there are no such users...

Alexander S. Klenin

Reply by email

Juha gmail
to Alexander Klenin

Alexander Klenin kirjoitti maanantai, 9. toukokuuta 2011 14:42:04:

Show quoted text 6 lines
> Besides, the code still works if the user has assigned his own ImageList. > The built-in glyps are added dynamically to the list. > Elegant isn't it! But what if the user wants his own images for sorting? Then again, maybe there are no such users...

Then the whole column click handling must be implemented by the user. ColumnClickSorts can't be enabled then, and the grid behaves as before.

Other thing is that the current built-in images should be improved. They are just copied and rotated from another image.

Juha

Reply by email

Joshua Lim
to Alexander Klenin: But what if the user wants his own images for sorting? Then again, maybe there are no…

Hi,

I'm having problems getting idHTTP (from indy 10.5.8) to work due to a bug that causes the follow function not to work under Ubuntu (or any Linux platform i guess). It works under Windows however. I've been corresponding with Remy Lebeau since 2 weeks ago on this issue, you can find a list of emails on the Indy Dev Yahoo Group - http://tech.groups.yahoo.com/group/Indy-Dev-Public/messages (last post here - http://tech.groups.yahoo.com/group/Indy-Dev-Public/message/6445).

Has anyone of you encountered a similar problem?

Grateful for any comments. Thanks.

function TIdMBCSEncoding.GetCharCount(Bytes: PByte; ByteCount: Integer):
Integer;
{$IFDEF USE_ICONV}
var
  LChars: array[0..3] of WideChar;
  LBytesPtr, LCharsPtr: PAnsiChar;
  LByteCount, LCharsSize: size_t;
{$ENDIF}
begin
  {$IFDEF USE_ICONV}
  // RLebeau: iconv() does not allow for querying a pre-calculated
character count
  // for the input like Microsoft does, so have to determine the max
characters
  // by actually encoding the Ansi data to a real buffer.  We'll encode to a
  // small local buffer so we don't have to use a lot of memory...
  Result := 0;
  LBytesPtr := PAnsiChar(Bytes);
  LByteCount := ByteCount;
  while LByteCount > 0 do
  begin
    LCharsPtr := PAnsiChar(@LChars[0]);
    LCharsSize := SizeOf(LChars);
    //Kylix has an odd definition in iconv.  In Kylix, __outbytesleft is
defined as a var
    //while in FreePascal's libc and our IdIconv units define it as a
pSize_t
    if iconv(FToUTF16, @LBytesPtr, @LByteCount, @LCharsPtr, {$IFNDEF
) = size_t(-1) then
    begin
      Result := 0;
      Exit;
    end;
    // LBufferCount was decremented by the number of bytes stored in the
output buffer
    Inc(Result, (SizeOf(LChars)-LCharsSize) div SizeOf(WideChar));
  end;
  {$ELSE}
    {$IFDEF WINDOWS}
  Result := MultiByteToWideChar(FCodePage, FMBToWCharFlags,
PAnsiChar(Bytes), ByteCount, nil, 0);
    {$ELSE}
  ToDo('GetCharCount() method of TIdMBCSEncoding class is not
implemented for this platform yet'); {do not localize}
    {$ENDIF}
  {$ENDIF}
end;
{$IFDEF USE_ICONV}
constructor TIdMBCSEncoding.Create(const CharSet: AnsiString);
const
  // RLebeau: iconv() does not provide a maximum character byte size like
  // Microsoft does, so have to determine the max bytes by manually encoding
  // an actual Unicode codepoint.  We'll encode the largest codepoint that
  // UTF-16 supports, $10FFFD, for now...
  cValue: array[0..1] of Word = ($DBFF, $DFFD);
begin
      writelog('sys', 'Main', #9 + '0' + #9 + '1TIdMBCSEncoding.Create');
        writelog('sys', 'Main', #9 + '0' + #9 + 'CharSet:'+CharSet);
  inherited Create;
  FCharSet := CharSet;
//  FToUTF16 := iconv_open(PAnsiChar(CharSet), 'UTF-16');    {do not
localize}
//  FFromUTF16 := iconv_open('UTF-16', PAnsiChar(CharSet));  {do not
localize}
FToUTF16 := iconv_open('UTF-16', PAnsiChar(CharSet)); {do not
localize}   //amended by joshua on remy's instruction
FFromUTF16 := iconv_open(PAnsiChar(CharSet), 'UTF-16'); {do not
localize}  //amended by joshua on remy's instruction
  if (FToUTF16 = iconv_t(-1)) or (FFromUTF16 = iconv_t(-1)) then begin
    if FToUTF16  iconv_t(-1) then begin
      iconv_close(FToUTF16);
      FToUTF16 := iconv_t(-1);
    end;
    if FFromUTF16  iconv_t(-1) then begin
      iconv_close(FFromUTF16);
      FFromUTF16 := iconv_t(-1);
    end;
    raise EIdException.CreateResFmt(@RSInvalidCharSet, [CharSet]);
  end;
  FMaxCharSize := GetByteCount(PWideChar(@cValue[0]), 2);
  FIsSingleByte := FMaxCharSize = 1;
end;

Reply by email

Juha gmail
to Alexander Klenin: But what if the user wants his own images for sorting? Then again, maybe there are no…

Alexander Klenin kirjoitti maanantai, 9. toukokuuta 2011 14:42:04:

But what if the user wants his own images for sorting? Then again, maybe there are no such users...

One option is to add published properties ColumnSortImageUp and ColumnSortImageDown.
The user could set them to point his own images. If not set then the default images would be used. It would be very flexible but requires 2 more properties which may confuse some people.
What do you think?

Other thing:
If someone has nice glyph images for the columns, I will apply them.

Juha

Reply by email

Alexander Klenin
to Juha gmail
Juha wrote 10 lines
Alexander Klenin kirjoitti maanantai, 9. toukokuuta 2011 14:42:04: > But what if the user wants his own images for sorting? > Then again, maybe there are no such users... One option is to add published properties ColumnSortImageUp and ColumnSortImageDown. The user could set them to point his own images. If not set then the default images would be used. It would be very flexible but requires 2 more properties which may confuse some people. What do you think?

As a compromise, you hard-code, say,
ColumnSortImageUp = 0 and ColumnSortImageDown = 1, but document that prominently.
Another variant is to create a ColumnSorting property with sub-properties Enabled, ImageUp, ImageDown, UseBuiltinImages (if =true, use built-in ImageList even if the user supplied his own).

BTW, I agree with the commenter in Mantis who said that sorting type is a required
requisite. Sorting numeric columns as strings is not merely useless, but subtly dangerous, since it may deceive user with incorrect results.

Alexander S. Klenin

Reply by email

Hans-Peter Diettrich
to Alexander Klenin
BTW, I agree with the commenter in Mantis who said that sorting type is a required requisite. Sorting numeric columns as strings is not merely useless, but subtly dangerous, since it may deceive user with incorrect results.

A CustomSort method would be the way to go.

DoDi

Reply by email

Alexander Klenin
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote 10 lines
Alexander Klenin schrieb: > BTW, I agree with the commenter in Mantis who said that sorting type > is a required > requisite. Sorting numeric columns as strings is not merely useless, but > subtly > dangerous, since it may deceive user with incorrect results. A CustomSort method would be the way to go.

That is true (although, of course, it should be an event, not method), but at least numeric sort should IMHO be supported by default, since otherwise there is a high probability of user confusion.

Alexander S. Klenin

Reply by email

Jesus Reyes
to Alexander Klenin

--- El jue 12-may-11, Alexander Klenin escribi?:

Show quoted text 31 lines
De: Alexander Klenin Asunto: Re: [Lazarus] Sorting StringGrid by column click A: "Lazarus mailing list" Fecha: jueves, 12 de mayo de 2011, 7:45 On Thu, May 12, 2011 at 23:37, Hans-Peter Diettrich wrote: > Alexander Klenin schrieb: > >> BTW, I agree with the commenter in Mantis who said that sorting type >> is a required >> requisite. Sorting numeric columns as strings is not merely useless, but >> subtly >> dangerous, since it may deceive user with incorrect results. > > A CustomSort method would be the way to go. > That is true (although, of course, it should be an event, not method), but at least numeric sort should IMHO be supported by default, since otherwise there is a high probability of user confusion. -- Alexander S. Klenin

Just let's not forget that this is a generic *string* grid, why users should expect it to sort numbers when the grid is specifically made to hold strings?

Appart from strings, the grid already supports Custom Sorting, so it can sort any way the user wants, even numbers. Just write an handler for OnCompareCells, this is even documented: http://wiki.lazarus.freepascal.org/Grids_Reference_Page#Sorting_Columns_or_Rows

Jesus Reyes A.

Reply by email

Juha gmail
to Jesus Reyes

Jesus Reyes kirjoitti torstai, 12. toukokuuta 2011 22:33:50:

Just let's not forget that this is a generic *string* grid, why users should expect it to sort numbers when the grid is specifically made to hold strings?

Yes. If some columns could be defined to hold numbers and then sorted accordingly, should we implement validity and integrity checks, too? We would basically create a database.

Show quoted text 5 lines
Appart from strings, the grid already supports Custom Sorting, so it can sort any way the user wants, even numbers. Just write an handler for OnCompareCells, this is even documented: http://wiki.lazarus.freepascal.org/Grids_Reference_Page#Sorting_Columns_or _Rows

Yes and Custom Sorting can be used also when automatic column sorting is enabled through ColumnClickSorts property.
I realized SortOrder support for custom sorting is now important because column clicking automatically switches SortOrder. I added the parameter to TOnCompareCells. It breaks old code but the fix is easy and there is no Delphi compat issue because Delphi doesn't have such event.

I will return to Alexander's ideas of sort images later. The current code however is usable in every use case, including numeric custom sort and also user defined sort glyps (by not enabling ColumnClickSorts).

Juha

Reply by email

Juha gmail
to Juha gmail

Juha (gmail) kirjoitti perjantai, 13. toukokuuta 2011 13:46:08:

I realized SortOrder support for custom sorting is now important because column clicking automatically switches SortOrder. I added the parameter to TOnCompareCells. It breaks old code but the fix is easy and there is no Delphi compat issue because Delphi doesn't have such event.

And now I realized the result could be negated based on SortOrder after calling OnCompareCells instead of letting the user take care of it, like this:

function TCustomGrid.DoCompareCells(Acol, ARow, Bcol, BRow: Integer): Integer;
begin
  result := 0;
  if Assigned(OnCompareCells) then begin
    OnCompareCells(Self, ACol, ARow, BCol, BRow, FSortOrder, Result);
    if SortOrder=soDescending then
      result:=-result;
  end;
end;
Which way is better? I think the Descending-sort code is always identical:
    if SortOrder=soDescending then
      result:=-result;
and it could be better if users don't need to worry about it.

Juha

Reply by email

Alexander Klenin
to Juha gmail
Juha wrote
Which way is better? I think the Descending-sort code is always identical: ? ?if SortOrder=soDescending then ? ? ?result:=-result; and it could be better if users don't need to worry about it.

Of course.

Alexander S. Klenin

Reply by email

Jesus Reyes
to Juha gmail: Jesus Reyes kirjoitti torstai, 12. toukokuuta 2011 22:33:50: Yes. If some columns could…

--- El vie 13-may-11, Juha (gmail) escribi?:

Show quoted text 38 lines
De: Juha (gmail) Asunto: Re: [Lazarus] Sorting StringGrid by column click A: "Lazarus mailing list" Fecha: viernes, 13 de mayo de 2011, 5:46 Jesus Reyes kirjoitti torstai, 12. toukokuuta 2011 22:33:50: > Just let's not forget that this is a generic *string* grid, why users > should expect it to sort numbers when the grid is specifically made to > hold strings? Yes. If some columns could be defined to hold numbers and then sorted accordingly, should we implement validity and integrity checks, too? We would basically create a database. > Appart from strings, the grid already supports Custom Sorting, so it can > sort any way the user wants, even numbers. Just write an handler for > OnCompareCells, this is even documented: > http://wiki.lazarus.freepascal.org/Grids_Reference_Page#Sorting_Columns_or > _Rows Yes and Custom Sorting can be used also when automatic column sorting is enabled through ColumnClickSorts property. I realized SortOrder support for custom sorting is now important because column clicking automatically switches SortOrder. I added the parameter to TOnCompareCells. It breaks old code but the fix is easy and there is no Delphi compat issue because Delphi doesn't have such event.

I'm sorry this patch must be reverted, there is no reason to send SortOrder as parameter when SortOrder is a grid's public property.

And as you said it unnecessarily breaks code, for example semaphore grid which is included with Lazarus which by the way has exteneded sorting capabilities.

Show quoted text 6 lines
I will return to Alexander's ideas of sort images later. The current code however is usable in every use case, including numeric custom sort and also user defined sort glyps (by not enabling ColumnClickSorts).

I'm sorry I still have to review the code been too busy lately, but I was thinking that maybe we don't need default icons for ordering direction, first we need to check if the lcl do not use some similar resources, if it doesn't, I think as a default we could just draw it ourselves.

Juha

Jesus Reyes A.

Reply by email

Jesus Reyes
to Juha gmail: Juha (gmail) kirjoitti perjantai, 13. toukokuuta 2011 13:46:08: And now I realized the…

--- El vie 13-may-11, Juha (gmail) escribi?:

Show quoted text 26 lines
And now I realized the result could be negated based on SortOrder after calling OnCompareCells instead of letting the user take care of it, like this: function TCustomGrid.DoCompareCells(Acol, ARow, Bcol, BRow: Integer): Integer; begin ? result := 0; ? if Assigned(OnCompareCells) then begin ? ? OnCompareCells(Self, ACol, ARow, BCol, BRow, FSortOrder, Result); ? ? if SortOrder=soDescending then ? ? ? result:=-result; ? end; end; Which way is better? I think the Descending-sort code is always identical: ? ? if SortOrder=soDescending then ? ? ? result:=-result; and it could be better if users don't need to worry about it. Juha

At first sight it seems to be a good idea however by doing this, we couldn't say the grid support "custom" sorting but "limited custom" sorting capabilities, because it's based on the interpretation of the SortOrder property.

Because how would one sort multiple columns with different Sorting orders, for example one column sorting alphabetically in ascending order while at the same time another column, say a rate column, in descending order, the same for dates, numbers, etc.

Jesus Reyes A.

Reply by email

Jesus Reyes
to Alexander Klenin: Of course. Alexander S. Klenin

--- El vie 13-may-11, Alexander Klenin escribi?:

Show quoted text 16 lines
De: Alexander Klenin Asunto: Re: [Lazarus] Sorting StringGrid by column click A: "Lazarus mailing list" Fecha: viernes, 13 de mayo de 2011, 8:43 On Sat, May 14, 2011 at 00:26, Juha (gmail) wrote: > Which way is better? I think the Descending-sort code is always identical: > ? ?if SortOrder=soDescending then > ? ? ?result:=-result; > and it could be better if users don't need to worry about it. Of course.

I think if the user don't need to worry then the built-in string ascending sorting should be fine, in all other cases, i.e. custom sorting, the user needs to take care of doing it correctly, that includes planing the sort order.

Jesus Reyes A.

Reply by email

Juha gmail
to Jesus Reyes: --- El vie 13-may-11, Juha (gmail) escribi?: I'm sorry this patch must be reverted,…

Jesus Reyes kirjoitti perjantai, 13. toukokuuta 2011 21:28:00:

I'm sorry this patch must be reverted, there is no reason to send SortOrder as parameter when SortOrder is a grid's public property.

Your wiki documentation said SortOrder is not supported when using custom sort, so I figured there is some situation when the user's code can't read a grid's data. (I have changed the wiki page since that).

And as you said it unnecessarily breaks code, for example semaphore grid which is included with Lazarus which by the way has exteneded sorting capabilities.

I will revert it. I also don't see any use for it, now that I think of it.

I'm sorry I still have to review the code been too busy lately, but I was thinking that maybe we don't need default icons for ordering direction, first we need to check if the lcl do not use some similar resources,

Maybe arrow_down.png and arrow_up.png images/action dir. Are they always present when LCL is built or should they be copied.

if it doesn't, I think as a default we could just draw it ourselves.

Yes, draw or copy a free image from somewhere.

Juha

Reply by email

Juha gmail
to Jesus Reyes: --- El vie 13-may-11, Alexander Klenin escribi?: I think if the user don't need to worry…

Jesus Reyes kirjoitti perjantai, 13. toukokuuta 2011 21:45:30:

Show quoted text 12 lines
> > Which way is better? I think the Descending-sort code > is always identical: > > if SortOrder=soDescending then > > result:=-result; > > and it could be better if users don't need to worry about it. > > Of course. I think if the user don't need to worry then the built-in string ascending sorting should be fine, in all other cases, i.e. custom sorting, the user needs to take care of doing it correctly, that includes planing the sort order.

Things have changed a little because now clicking the same column many times changes sorting between ascending and descending.
Thus the custom sort code should take it into account (when ColumnClickSorts is enabled).

Juha

Reply by email

Hans-Peter Diettrich
to Jesus Reyes: --- El vie 13-may-11, Juha (gmail) escribi?: At first sight it seems to be a good idea…
Show quoted text 16 lines
> Which way is better? I think the Descending-sort code is always > identical: if SortOrder=soDescending then result:=-result; and it > could be better if users don't need to worry about it. > > Juha At first sight it seems to be a good idea however by doing this, we couldn't say the grid support "custom" sorting but "limited custom" sorting capabilities, because it's based on the interpretation of the SortOrder property. Because how would one sort multiple columns with different Sorting orders, for example one column sorting alphabetically in ascending order while at the same time another column, say a rate column, in descending order, the same for dates, numbers, etc.

That's the task of OnCompareCells. Ascending/Descending only reverts the row order of the entire grid.

DoDi

Reply by email