Wednesday, May 14, 2008

New 0.9.26 features. Part 2. ImageList effects

I think that almost nobody knows that internally the LCL TCustomImageList contains an array of 32 bit (rgb + alpha) data. In this array the imagelist stores images in the best quality. When it is needed the imagelist use this array to create a widget set imagelist (at this moment this is only the case for the win32 widget set) with the best possible quality. Any time we can access the 32 bit image data and we can do any manipulation with it. If you ever worked with image data you should know that 32 bit data is one the easiest formats for manipulations.

But what manipulations do applications (controls) usually need? The most often used manipulation is the transform from multi-color to gray image. It is used for example in menus and bitbtns (a button with an image). The next two often used manipulations are image highlighting and shadowing, which are also used in menus and buttons.

So I added TCustomImageList.Draw(... ADrawEffect: TGraphicsDrawEffect) and TCustomImageList.GetBitmap(... AEffect: TGraphicsDrawEffect).


At this picture you can see all effects currently supported by the imagelist. And this is the description:
  1. Normal - no effect. You get an original image without any transform.
  2. Disabled - gray image. Red = Green = Blue. We get every component value by the following formula: (Red + Green + Blue) / 3.
  3. Highlighted - a bit shining image. To achieve shining we alphablend the original image with white.
  4. Shadowed - opposite of highlighted. To achieve shadowing we alphablend the original image with black.
  5. 1Bit - this effect is used for drawing bitbtn glyphs in windows applications with no theme support. Windows just cannot draw gray images - they can use only 3 colors on a button: clBtnFace (transparent), clBtnShadow and clBtnHighlight. To support disabled bitmap drawing on such buttons we have added this special effect. We are using a special formula to achieve sharp edges in order to save all image details.
Currently every bitbtn and menu use these methods to draw their glyphs. When you disable them , the gdeDisabled effect is used, when you move a mouse pointer over a control, gdeHighlight is used and when you press a control gdeShadowed is used.
Of course, these imagelist methods can be useful not only for menus and buttons - you can use them for your own controls and other needs too. And we hope you will do that. :-)

Sunday, February 10, 2008

GTK2 planned not before Lazarus 1.2?

Sometimes people ask: Why don't the Lazarus developers want to fix the gtk2 bug before Lazarus 1.0?

The Lazarus developers have limited resources, so in order to have Lazarus 1.0 sooner rather than later, some decisions were made to reduce the work load (scope) for Lazarus 1.0. Some of this is mentioned on the wiki.

Basically, this means we think that no new features are needed for Lazarus 1.0. That doesn't mean that there will be no new features until then, but that we won't delay the release of Lazarus 1.0 because of it.

The gtk2 interface could be regarded as a feature too, because in most cases the gtk1 interface works just fine or better (except it looks dated). I must admit it is getting more troublesome recently, because some distroes now ship with gtk2 only. Currently the gtk widgetset is more stable and has less bugs with Lazarus than gtk2. Until that situation has changed gtk1 will remain the default. As soon as gtk2 is better (i.e. less bugs and more stable) than gtk1, it will come the default. But this is no priority for the Lazarus team, it rather focuses on other bugs.

Fortunately Lazarus is open source and with your contributions you can steer the path of Lazarus. For example Andrew Haines and Ales Katona understood that and have contributed a lot of gtk2 patches, trying to improve the gtk2 widgetset, so that it become the default for Lazarus 1.0. If a couple of other people start helping, I have no doubt that Lazarus 1.0 will eventually be with gtk2.

New 0.9.26 features. Part 1. SendMessage and PostMessage

If you ever wrote some big application or component, I am sure you met the problem of postponed execution of some code. Delphi programmers usually used 2 solutions: timers and Windows message system abilities. This article is about message system approach. Note that Lazarus has a third solution: Application.QueueAsyncCall.

I suppose you already know something about the Windows message system. And you are familiar with SendMessage and PostMessage commands. They are used to deliver messages to controls. SendMessage sends message dirrectly to control window procedure. PostMessage adds message to the message queue, so control will get it only after processing all other pending messages.

Group of message numbers from 0 to WM_USER are used by Windows itself. So if you want to use message system in personal purpose you should use message numbers >= WM_USER.

Lets return to Lazarus where you want to indirectly perform some actions. Very usual task is when you need to destroy some control (your form for example) in event handler. Ofcource your application crash if you call Free in event handler - so you need to call Free somehow after event handler. But how? Call PostMessage in event handler and process posted message in message handler.

Delphi example:

const
MY_MESSAGE = WM_USER + 1; // <-- your message number
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject); // <-- some event handler
private
procedure MyMsgHandler(var Message: TMessage); message MY_MESSAGE; // <-- message handler
end;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
... //code before
PostMessage(Handle, MY_MESSAGE, 1, 0); // <-- here you post message to message queue
... // code after
end;

procedure TForm1.MyMsgHandler(var Message: TMessage);
begin
Free;
end;

If you used similar constructions in Delphi, you should know that they did not work in Lazarus. In the win32 widgetset code all WM_USER messages were blocked for some reasons:
- other widgetsets had no support for PostMessage and SendMessage
- few WM_USER messages were used by Windows and cause errors in LCL

After releasing 0.9.24 we reevaluated our decision since gtk and qt supported PostMessage and SendMessage. Then an implementation for Carbon appeared. Of course it would be a joke to block messages under Windows when all other widgetsets (where messages are aliens) support them. Now you can use PostMessage and SendMessage with few limitations:
- use Message numbers >= LM_USER
- sending messages outside application does not work (you cannot send message to another application)

And the last - if you need example - look at lazarus\examples\messages.

Friday, December 28, 2007

Lazarus fixes branch

While working on Lazarus 0.9.23, which took more than 6 months, Giuliano Colla proposed to create a stable branch. Lazarus development goes very rapid with about 100 commits each week and with each daily update, there is a reasonable chance that something got broken. This is not desirable for people that need to do production work with Lazarus.

The idea was welcomed, although I had some doubts about the feasibility, especially the man power needed. Lazarus development goes rapid and there is still a lot to do, so the current Lazarus developers can better spend their time on new development than on maintaining a stable branch. So, we offered Giuliano to maintain it him self and we would support him.

After the Lazarus 0.9.24 release in November, I created the fixes_0_9_24 branch and set the version number to 0.9.24.1. Windows snapshots using fpc 2.2.0 and the fixes branch are created on a daily basis from ftp. Giuliano would send me a list of revision numbers to be merged and I would apply thoses merges to the fixes branch. After a couple of merges, the branch became 'super'-stable, because there were no changes. A lot of changes are related and Giuliano wanted to test them before applying. This takes a lot of time. Also, it is sometimes not trivial to see the dependencies between the different revisions, espescially if a revisions contains new functionality, that you don't want to merge and a simple fix that is needed when you want to merge a later bug fix revision.

So we are still struggling to find a good strategy for maintaining the fixes branch. In the last week, I took a different approach. I looked at the list of revisions and merged the new component bar images, splash screen and most the bug fixes I made in Lazarus 0.9.25. Almost 200 revisions were merged from trunk to the fixes branch, so things might have been broken, but I have good hopes that 0.9.24.1 is at least as good as 0.9.24.

There are still about 500 revisions in trunk not (yet) merged:
  • Documentation hints
  • Handle rewrite
  • New project options and environment options dialog
  • A number of bug fixes for other widget sets than win32 (my only expertise).
  • Improvements to images in the toolbar of the IDE
  • Several improvements to the Lazarus IDE dialogs
Because it is hard to guess what people want in the fixes branch (except stability), we want to ask users of the fixes branch to indicate what they want to be merged from trunk. If we think the feature is stable already, we will try to merge it.

I encourage everybody to test the fixes branch and give feedback on the Lazarus mailing lists. If the fixes branch proves succesfull (stable updates without occassional breakage), I am considering to put snapshots of it in the Lazarus-Testing Ubuntu repository.

Monday, November 5, 2007

Lazarus 0.9.24 tagged

This afternoon I tagged Lazarus 0.9.24. The coming days we will build all the windows installers, rpms, debs and dmgs for the suported OS-es and CPU's.

As part of the release testing, I created an ubuntu lazarus-testing repository at http://www.hu.freepascal.org/lazarus/dists/lazarus-testing/. Currently it still contains Lazarus 0.9.22 and FPC 2.0.4, but as soon as the Lazarus 0.9.24 and debs are ready they will be put there, until the Lazarus 0.9.24 release can be announced. After the announcement, they will be available in the lazarus-stable repository too. For more information about how to get debs from the Lazarus testing repostitory, see the wiki.

Please, let us know if you have problems using the testing repository and during the upgrade from 0.9.22 to 0.9.24.

Monday, October 29, 2007

New poll on the lazarus site about Windows versions

Lately I have been trying to fix recompiling the Lazarus (needed to install components) from the IDE. On windows 2000 and higher, using .rc files is working OK if windres is not on the path, but on windows 98 there are still some problems.

Marc has done a large graphic rewrite in Lazarus 0.9.23 and he also noticed that there are noticeable difference between Windows 98 and Windows XP.

I wondered if there still many people using Windows 98 and Windows ME for Lazarus development or as target for LCL application. Therefore I created a new poll on the Lazarus website, asking people about the oldest Windows version is, that they use for Lazarus development.

Currently when we program on the win32 interface we try to use only functions available in windows 95, or provide a fall back if we want to use a function not available on windows 95. Some features like graphics, Unicode support, the shell provided by cmd32.exe and the availability of console (used for the debugger) are better and easier in Windows 2000 and later. So it would be nice if we could restrict ourselves to these newer Windows versions.

Personally, I don't think we can drop support for windows 98 yet, but I doubt anybody is using Windows 95 to program with Lazarus. Let us know where you stand and cast your votes in the poll. This poll is about the OS you use for Lazarus, a future poll may be held about the Windows versions you want your developed applications to run on.

Note: people who don't use Lazarus on Windows, don't need to vote.

Sunday, October 28, 2007

How TPageControl tab switching in designer has been solved

I think most users who used TPageControl in windows ide suffered from the inability to stitch its Tabs by clicking them in the designer. Indeed, the gtk ide had no such bug while the windows one had. I got annoyed by the fact too and as result I started my research of the problem.

My first assumption was that somewhere a csDesigning (you know that usual "if csDesigning in BlaBla.ComponentState" ;) ) condition was written and I spent much time (searching code) to prove myself that I am wrong.

Ok, if I am wrong (I thought) then it is by design or by the designer :) So I started my research of how the win32 designer works. Usually my research consists of Alt+F7 in Far manager for some search key (in this case for 'designer') and if Alt+F7 doesn't help, then in debugging the code. I suppose on that occasion I had been satisfied by search in files. First of all I found 'TWin32WidgetSet.GetDesignerDC' method and later I've checked that this method is indeed what I searched for. Look at TDesigner.DrawDesignerItems and you'll find that yourself.

So, I've known the enemy by sight :) If you notice GetDesignerDC creates a visually transparent window over WindowHandle and returns the Device Context for that overlay window. The Device Context is not a interesting thing for us, but the overlay window is. That overlay window is placed on top of a form (and all child controls). And the most interesting thing in that overlay window is the window procedure OverlayWindowProc.

Inside OverlayWindowProc we can find that overlay window easts all messages except WM_KEYFIRST..WM_KEYLAST, WM_MOUSEFIRST..WM_MOUSELAST and that bunch of messages it redirects to the parent (look at Windows.GetParent(Window)). If you look at TDesigner.DrawDesignerItems then you'll know that the Parent of OverlayWindow is the Form. So nothing wondering now that the PageControl Tabs cannot be switched in designer - PageControl gets no clicks (they are caught by our overlay window).

Hmm ... I thought about a solution. The first thing that flashed through my mind was sending those mouse clicks to the underlying control. But it is so easy to forget about some messages or make other errors if you want to emulate windows behaviour. So I rejected that way as defective. Another way is to make some parts of our overlay window transparent for mouse. Windows has a special message WM_NCHITTEST to check what a given coordinate means. And a window can return HTTRANSPARENT as result of a message to indicate that message must be sent to the underlying window (so this point is transparent for the window).

As result the whole solution consisted of handling WM_NCHITTEST in OverlayWindowProc and return HTTRANSPARENT if underlying control wanted mouse messages for that point at design time. I've added a new method GetDesignInteractive to TWSWinControl.

class function GetDesignInteractive(const AWinControl: TWinControl; AClientPos: TPoint): Boolean;


Now I had a method to check, but of course I needed the implementation of that method for TPageControl (which checked that mouse is over control Tabs). So I've added TWin32WSCustomNotebook.GetDesignInteractive, rebuilt the win32 ide and hurray - I switched PageControl tabs in designer w/o problems.

You can look at implementation in revisions: 12245, 12620 (method has been renamed).