Friday, December 29, 2006

It's Open Source stupid!

In the previous post i badly evaluated the gtk api, but in one thing it is far ahead from win32: it's open source.
To fix this bug i just looked in the gtk source the routine that insert items in a listbox (See gtk_list_insert_items in gtklist.c). Easy.

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 ;-) .

Saturday, December 16, 2006

Only one instance

Usually each time you launch an application a new instance is created, but sometimes is not desired to have more than one instance running. There was no way to force only one instance using Lazarus/fpc and was some discussion in the maillist the best way of implementing it (using mutex, file locking etc). I had a different idea: how about to use simpleipc, a IPC mechanism which comes in fpc?

So i started to implement such component based in simpleipc. First i searched how the Delphi programmers resolved this problem. I found two open source components TRunOnce and TInstanceControl. Both uses a file mapping technic which is win32 centric, so without use for me and i already had another idea. But i learned with them that the best place to do the implemantation is inside the Loaded procedure which is called just after the components are streamed and before the form is show.

The algorithm is pretty simple: a client (TSimpleIPCClient) checks is there's a corresponding server (TSimpleIPCServer) running, if so (a instance is already running) notify the server (the running instance) and terminate the app, otherwise it means that is the first instance so init a server and let the program go.

The next step was a little more difficult: how to pass the command line arguments to the second instance. I had 3 options:
  • Pass the cmdline as is and let the receiver (server) parse it. Not good since the parsing of the command line is not so simple and probably platform dependent
  • Pass each argument (ParamStr(x)) separately. It was considered but, in the server side, i needed a way to tell that the argument passing was starting (to set the length of the array), than pass the parameters itself and finally notify that the arguments finished (to fire the event). I could do that passing special chars as markers or with numerical values in the beginning of the stream. Definitely an overkill.
  • Create an easily parsable string with the parameters in the client side and then send once. The chosen one.
To the tests. Aside from a bug of the style for x:=1 to y do; everything worked fine. Only one annoyance: the form was being show in a few instants before it was killed. So, looking at TApplication.Run i found a ShowMainForm property and voila: no flashing form.

The results can be found here

Delphi is far away from Lazarus/fpc, no doubt, but this is an example that some problems can be resolved cleaner and easier with the open source solution.

PS: more difficult than write the component is creating a icon for it. And the result... LOL

Friday, December 15, 2006

Hiding a TNotebook tab

While developing with TNotebook, i found some problems using TPage.TabVisible property. If we Toggle TabVisible forth and back of more then one page, the tab title does not correspond to the page content. So i took a look into this issue.

To examine the bug i created a test project with a notebook containing 3 pages (Page1, Page2 and Page3 - creative names no?). If we set TabVisible of Page1 to False, everything is fine: the remaining pages are correct. Setting it back to True also works like expected. However, after setting TabVisible of Page1 and the Page2 to false the problem appears: the remaining page (which should be Page3) has the Title of the Page2 and no content (should have a TLabel) inside it.

To the work. A look in TCustomPage.SetTabVisible lead us to TCustomNotebook.AddRemovePageHandle. In this routine, if the TabVisible is set to true a page is added otherwise removed through a widgetset function. Here's the problem: to add the page its passed the VisibleIndex as the insert position but to remove the PageIndex is used as the position instead. In our example, when all Pages are visible, PageIndex = VisibleIndex so no problems, when Page1 is not visible Page2 gets VisibleIndex = 0 and PageIndex = 1. In this situation, setting Page2.TabVisible to false informs the widgetset to remove the page with index 1 (PageIndex) when should be 0 (VisibleIndex).

After the fix, the problem vanishes, but it still not working as should. If Page1 is the active tab and Page1.TabVisible is set to false no page is selected and the content area stay blank. Worse: Notebook.ActivePage still points to Page1. More work.

Examining the ActivePage property we discover that it depends of the TNotebook.PageIndex, so is necessary to update the pageindex properly after toggling the TabVisible. I found that in TNotebook.RemovePage method the new pageindex already computed but does not consider the TabVisible state. I reworked the routine creating to auxiliary functions ,with the suggestive names of SetNewPageIndexAfterPageRemove and GetNearestVisiblePage, which takes in account the TabVisible state.

The test program and the patch can be found in the bugreport.

The patch also does some cleanup in the code:
  • TCustomPage.SetParent set TabVisible to false before calling RemovePage. I removed the TabVisible assignment since RemovePage already does the job.
  • In GetActivePageComponent and GetActivePage i changed from PageIndex to fPageIndex (Notice that in other functions fPageIndex is already used)
PS: I tested in win32 but notice that the modifications are done only in LCL level so should not be problems with other interfaces.

Start (or better: begin)

In this blog i will write about the problems and solutions i found while developing with Lazarus and Freepascal. It will be mostly technical articles. Maybe it can be useful for someone else.