Wednesday, December 27, 2006

Eliminating duplicate events of TComboBox and TListBox

One year ago i posted a bug in Lazarus about the problems of TComboBox events. The win32 part was resolved but not the gtk one. So i think it was time to learn something about gtk programming.
To help me find where the problem was i used the MultiLog, a logging component useful to debug gui apps.

The diagnostic: i wrote a small app that has a combobox with the OnChange and OnSelect events set. After selecting an item in the dropdown list two events are fired: first OnSelect and than OnChange. I put a call to Logger.SendCallstack in both events handler and i got from where the events were being called:

- [internal gtk calls] > GtkListSelectChild > DeliverMessage > LMSelChange
- [internal gtk calls] > GtkChangedCB > DeliverMessage > LMChanged

Than i figured that GtkListSelectChild is attached to the 'unselect_child' signal of the TGtkCombo.list handle (the dropdown list) and that GtkChangedCB is attached to the 'changed' signal of the TGtkCombo.entry handle (the edit box).

The next step was to see what is going on there. After reading the gtk 1.2 reference, i put log messages in GtkChangedCB and GtkListSelectChild to get the gtk_signal_n_emissions for both. The idea was that i could break the OnChange firing using gtk_signal_emit_stop function. But unfortunately, only apparentelly as you will se later, i was wrong. According to the results (see figure 1) it seemed that the 'changed' signal was started after the 'unselect_child' signal, so there would be no way to stopping it inside GtkListSelectChild.
Figure 1

The things was not going as planned and i had the idea of connecting to the 'select_child' signal (i used a function called GtkListSelectChildNew). At first look, it was a hope since this signal is triggered twice, before and after 'changed' (See figure 2) and i could use the LockOnChange procedure to avoid its execution.

Figure 2

So i incremented the LockOnChange in the first GtkListSelectChildNew and.... Nothing!!. This strategy did not prevent the message deliver in GtkChangedCB.

At this time i knew that there were something very very wrong and after examining the handles of the PGtkWidgets (PGtkCombo, PGtkList, PGtkEntry) i found that i was getting the PGtkCombo handle in the wrong way: i was relying in the TGtkWidget.parent field of the list and entry handles. This parent field is anything but the combo handle (I dont know who is more stupid: me or gtk). The right way is using the data parameter of the callback functions that holds the LCL Object address from which i can get the Handle field.

From this point everything got easier. The 'changed' signal is emitted before 'unselect_child' (see Figure 3) so the gtk_signal_emit_stop (using the correct handle) called inside GtkListSelectChild prevents the GtkChangedCB to be fired. My initial idea was correct, only not properly implemented!.

Figure 3

But is not over yet. After stopping in GtkSelectChild the 'changed' signal, if i click in the dropdown list only OnSelect is called, as desired, but the first change in edit box calls OnSelect ('unselect_child' signal) instead of OnChange ('changed' signal), the subsequent changes in the edit box call OnChange correctly.

No panic!. I noticed that the 'select_child' is not fired in this case, so i could use
it instead of 'unselect_child'. But this brings another problem: 'select_child' is fired twice. But nothing that some refactoring can not resolve.

The final schema i got:
- GtkListUnselectChild is connected to 'unselect_child' signal. Inside it the signal to 'changed' is stopped as well one of the 'select_child' signals. No message is delivered.
- GtkListSelectChild is connected to 'select_child' signal. The previous routine guaranties that is called once. The message is delivered to the LCL
- No changes to GtkChangedCB

I fixed the problem of TListBox.OnSelectionChange with similar approach.

Now it's time to do some more testing, clear and comment the code so i can send a patch.

The programs i used to do the tests can be found here and here

I have some experience with the win32 api and this was the first time i looked deep in the gtk programming. What i can say is that a convuloted gui api is not exclusive of the Windows programmers ;-) .

2 comments:

clinique said...

Hello, I made a small addon to multilog to enable logging to console, I would like to transmit it to you for integration in Multilog package if you think it's appropriate

Luiz Américo said...

You can send to my email
luizamericop at gmail.com