<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-38057291</id><updated>2011-09-05T08:52:58.671-07:00</updated><category term='lazarus'/><category term='report'/><category term='fpc'/><category term='valgrind'/><category term='zlibar'/><category term='uniqueinstance'/><category term='sqlite'/><category term='code optimization'/><category term='atbinhex'/><category term='gtk'/><category term='utf-8'/><category term='rtti'/><category term='multilog'/><category term='dataset'/><category term='qt'/><category term='cairo'/><category term='virtualtreeview'/><category term='JSON'/><category term='lazreport'/><title type='text'>On the road with Lazarus/Freepascal</title><subtitle type='html'>A blog dedicated to register my experiences while programming with Lazarus and Freepascal</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>37</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-38057291.post-23742709966440996</id><published>2011-07-10T14:16:00.000-07:00</published><updated>2011-07-10T16:01:20.949-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lazreport'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><category scheme='http://www.blogger.com/atom/ns#' term='report'/><title type='text'>Generic cross data report with lazreport</title><content type='html'>In the lazreport repository there's a &lt;a href="http://svn.freepascal.org/svn/lazarus/trunk/components/lazreport/samples/userds/"&gt;demo app&lt;/a&gt; showing how to create a cross data report. It uses two instances of TfrUserDataset: one for the master (row) data and one for the cross (column) data. At first glance the component does not provide another way to build such reports. A deeper look shows the contrary. Here are the steps to build a cross data report with arbitrary number of rows and columns.&lt;br /&gt;&lt;br /&gt;WARNING: to follow this guide is necessary basic lazreport knowledge.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Prepare the report&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Nothing special here&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Create an empty report&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add a Master Data band&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add a Cross Data band&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add a Text Object inside the Cross Data&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In the Text Object put a variable named value: [value]&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Add handler to retrieve the value&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Those familiar with lazreport will have no problems:&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TForm1.frReport1GetValue(const ParName: String; var ParValue: Variant);&lt;br /&gt;begin&lt;br /&gt;  if ParName = 'value' then&lt;br /&gt;    ParValue := IntToStr(FRow) + ' - ' + IntToStr(FCol);&lt;br /&gt;end; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just the column and row indexes for demonstration purpose. Using together with matrix like data structures the retrieve of actual data is straightforward.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Set the number of rows and columns&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The most attentive developers will notice that no dataset (even the virtual dataset) was linked to each band. In fact running the report at this stage will lead to a blank page. &lt;br /&gt;If the number of columns and rows are previously know just set the Virtual Dataset option for each band. This is not an optimum solution since not always we have that info. Here's how to set the Virtual Dataset record count at runtime:&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TForm1.frReport1BeginDoc;&lt;br /&gt;var&lt;br /&gt;  BandView: TfrBandView;&lt;br /&gt;begin&lt;br /&gt;  BandView := frReport1.FindObject('MasterData1') as TfrBandView;&lt;br /&gt;  BandView.DataSet := '9';&lt;br /&gt;  BandView := frReport1.FindObject('CrossData1') as TfrBandView;&lt;br /&gt;  BandView.DataSet := '2';&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will create a report with nine rows and two columns. Yep, you read right: lazreport store the number of the records of band's Virtual Dataset in an string field, the same field that store the name of an associated TfrDataset.&lt;br /&gt;WARNING: don't look at lazreport source. It may scare the faint hearted ;-)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Track the row and column positions&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The tricky part. The first thing to do is add two integer fields (FRow and FCol) to the Form/Data Module containing the TfrReport instance.&lt;br /&gt;&lt;br /&gt;To get the column add an event to OnPrintColumn, and store the ColNo parameter:&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TForm1.frReport1PrintColumn(ColNo: Integer; var ColWidth: Integer);&lt;br /&gt;begin&lt;br /&gt;  FCol := ColNo;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice that the Lazarus IDE will create an event declaration with the second parameter named Width. This will not compile with {$mode ObjFpc}. Renaming it to ColWidth will make the compiler happy.&lt;br /&gt;&lt;br /&gt;There's not an event that pass the current row position. The first try is to hook into the OnBeginBand&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TForm1.frReport1BeginBand(Band: TfrBand);&lt;br /&gt;begin&lt;br /&gt;  Inc(FRow);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Running the report with this will show wrong row indexes because it will increment in all bands not only the Master/Row band. The fix is easy:&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TForm1.frReport1BeginBand(Band: TfrBand);&lt;br /&gt;begin&lt;br /&gt;  if Band.Typ = btMasterData then&lt;br /&gt;    Inc(FRow);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's done, add Data Header and Cross Header bands, glue with actual data and the generic cross data report is done. The &lt;a href="http://www.luizmed.kinghost.net/misc/CrossReport.zip"&gt;sample project&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-23742709966440996?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/23742709966440996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=23742709966440996' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/23742709966440996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/23742709966440996'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2011/07/generic-cross-data-report-with.html' title='Generic cross data report with lazreport'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-5867002775872475889</id><published>2011-07-01T16:04:00.000-07:00</published><updated>2011-07-02T09:42:25.400-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Make a generic control behaves like a "DropDown window"</title><content type='html'>Some controls, like the dropdown list of a combo box, disappears as soon as focus is lost. In web applications / pages this concept is expanded further by allowing &lt;a href="http://aext.net/2009/08/perfect-sign-in-dropdown-box-likes-twitter-with-jquery/"&gt;form&lt;/a&gt; &lt;a href="http://thefinishedbox.com/freebies/scripts/jquery-dropdown-login/"&gt;controls&lt;/a&gt; &lt;a href="http://addyosmani.com/blog/formbox/"&gt;inside&lt;/a&gt; the drop down window.&lt;br /&gt;&lt;br /&gt;To make a generic LCL control works like those web widgets, basically is necessary to hide it when the focus is lost. If the drop down control is a form this can be accomplished as simple as setting the BorderStyle to bsNone and using the Deactivate handler to hide itself:&lt;br /&gt;&lt;pre class="brush: pascal"&gt;procedure TMyForm.FormDeactivate(Sender: TObject);&lt;br /&gt;begin&lt;br /&gt; Hide;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For TFrame is possible to put an instance of it in an temp TForm configured as above. For other control classes, created at design time and with a parent already assigned, this may work but is not desired.&lt;br /&gt;&lt;br /&gt;The alternative is to detect when the focus has changed and then check if the focused control is outside the "drop down" control. &lt;strike&gt;Unfortunately, AFAIK, there's no way in VCL/LCL to detect globally when the focus changed.&lt;/strike&gt; Well, in fact there's an event that just do that: Screen.OnActiveControlChange. The drawback of using this event each time a "drop down" control is used is that will override a previously set event handler. Fortunately, LCL provides an alternative to set multiple handlers through Screen.AddHandlerActiveControlChanged.&lt;br /&gt;&lt;br /&gt;So, for a TPanel descendant we would use something like to add remove the handler when the visible state is changed:&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TMyPanel.VisibleChanged;&lt;br /&gt;begin&lt;br /&gt; if Visible then&lt;br /&gt;   Screen.AddHandlerActiveControlChanged(@FocusChangeHandler)&lt;br /&gt; else&lt;br /&gt;   Screen.RemoveHandlerActiveControlChanged(@FocusChangeHandler);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the handler code check if the focused control is itself or a child. If not hide:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TMyPanel.FocusChangeHandler(Sender: TObject; LastControl: TControl);&lt;br /&gt;var&lt;br /&gt; AControl: TControl;&lt;br /&gt;begin&lt;br /&gt; AControl := Screen.ActiveControl;&lt;br /&gt; if (AControl &amp;lt;&amp;gt; Self) and not IsParentOf(AControl) then&lt;br /&gt;   Visible := False;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yep! When the focus goes to outside of the "drop down" control it will automatically hide itself.&lt;br /&gt;&lt;br /&gt;But...&lt;br /&gt;&lt;br /&gt;Sometimes clicking outside of the control will not change the focus so it will not hide like desired. The solution is to detect user inputs (mouse click) globally. Setting OnMouse* events for all form controls is a no-no for obvious reasons. Using Application.OnUserInput event is an idea but has the same drawback of Screen.OnActiveControlChange. Similar problem, similar solution: is possible to set multiple handlers through Application.AddOnUserInputHandler.&lt;br /&gt;&lt;br /&gt;The updated VisibleChanged code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TMyPanel.VisibleChanged;&lt;br /&gt;begin&lt;br /&gt; if Visible then&lt;br /&gt; begin&lt;br /&gt;   Screen.AddHandlerActiveControlChanged(@FocusChangeHandler);&lt;br /&gt;   Application.AddOnUserInputHandler(@UserInputHandler);&lt;br /&gt; end&lt;br /&gt; else&lt;br /&gt; begin&lt;br /&gt;   Screen.RemoveHandlerActiveControlChanged(@FocusChangeHandler);&lt;br /&gt;   Application.RemoveOnUserInputHandler(@UserInputHandler);&lt;br /&gt; end;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the input handler that checks if the control where mouse is over is outside or not:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TMyPanel.UserInputHandler(Sender: TObject; Msg: Cardinal);&lt;br /&gt;var&lt;br /&gt; AControl: TControl;&lt;br /&gt;begin&lt;br /&gt; case Msg of&lt;br /&gt;   LM_LBUTTONDOWN, LM_LBUTTONDBLCLK, LM_RBUTTONDOWN, LM_RBUTTONDBLCLK,&lt;br /&gt;   LM_MBUTTONDOWN, LM_MBUTTONDBLCLK, LM_XBUTTONDOWN, LM_XBUTTONDBLCLK:&lt;br /&gt;   begin&lt;br /&gt;     AControl := Application.MouseControl;&lt;br /&gt;     if (AControl &amp;lt;&amp;gt; Self) and not IsParentOf(AControl) then&lt;br /&gt;       Visible := False;&lt;br /&gt;   end;&lt;br /&gt; end;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Relatively simple but doing that for each control would be annoying so i wrote a component that takes care of it (among other few details). &lt;a href="http://code.google.com/p/luipack/source/browse/trunk/luicontrols/dropdownwindow.pas"&gt;Enjoy&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-5867002775872475889?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/5867002775872475889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=5867002775872475889' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/5867002775872475889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/5867002775872475889'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2011/07/make-generic-control-behaves-like.html' title='Make a generic control behaves like a &quot;DropDown window&quot;'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-382453263581154017</id><published>2010-08-05T11:16:00.000-07:00</published><updated>2010-08-05T16:03:24.906-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='JSON'/><title type='text'>JSON to the rescue</title><content type='html'>Tired of creating boilerplate dialogs for every TDataSet i needed to edit, i developed a generic dialog with a data grid plus the basic edit buttons (add, delete). To edit a TDataset i just call a wrapper function that setup the grid with some configuration.&lt;br /&gt;&lt;br /&gt;While most of the time i just needed to edit a single field, it was desirable to keep the ability to edit more than one field. Also it would be necessary also to set the column width for each field. So i was passing a record with the following structure to configure the dialog:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;  TDataDialogInfo = record&lt;br /&gt;    FieldNames: String;&lt;br /&gt;    FieldWidths: array of Integer;&lt;br /&gt;    Title: String;&lt;br /&gt;  end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Title stores the dialog title, FieldNames store the Field names in semicolon separated string and FieldWidths the width of corresponding field. Here is the first problem: without adding another record type with the field name and width there is noway to guarantee the correspondence between the values leading to error easily.&lt;br /&gt;&lt;br /&gt;Another problem is that there's no way in fpc to create the record on the fly so i need to declare a constant somewhere. To use a dialog with Title "Edit Test Dataset" and editing field "Name" i need to do:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;const&lt;br /&gt;  Info: TDataDialogInfo = (&lt;br /&gt;    FieldNames: 'Name';&lt;br /&gt;    FieldWidths: nil;&lt;br /&gt;    Title: 'Edit Test Dataset'&lt;br /&gt;    );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice that even if i don't need/want to set the width i have to declare the record field Widths.&lt;br /&gt;&lt;br /&gt;I used this way for some time, and is really a lot less work than creating a TForm and setting the components for each TDataset, until i needed to add an option to show more rows than the usual in the grid. How to add such option? Adding a field to the TDataDialogInfo record would make all previous defined constants unusable and i would need to fix than all. What if later i needed to add another option?&lt;br /&gt;&lt;br /&gt;It was at this moment that i think: "How it would be good if pascal had the ability to define objects with arbitrary fields on the fly, like in JavaScript!". Well, in fact, although indirectly, is possible to do it in pascal thanks to the native fpc implementation of &lt;a href="http://www.json.org/"&gt;JSON&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So instead of using a record to pass the configuration now i use a string with a (valid) JSON definition. In the previous example (Title "Edit Test Dataset" and editing field "Name") now i do:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;Info = '{"title": "Edit Test Dataset", "fields": {"name": "Name", "title": "Person"}}';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If i just need to define the field name:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;Info = '"Name"';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If i want to edit two fields:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;Info = '["Id", "Name"]';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And i can set different properties for each field&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;Info = '{"title": "Edit Test Dataset",'+&lt;br /&gt;    '"fields": ["Id", {"name": "Name", "title": "Person"},' +&lt;br /&gt;    ' { "name": "Phone", "width": 100}]}';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice that the format of the property (in this case "fields") vary from a single string to a JSON array or object. Better: you can mix in the same property different formats! Flexibility it's your name. All of this backward compatible: i can add another property and previous definitions will still work!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-382453263581154017?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/382453263581154017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=382453263581154017' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/382453263581154017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/382453263581154017'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2010/08/json-to-rescue.html' title='JSON to the rescue'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-6565602226568466960</id><published>2010-08-01T14:05:00.000-07:00</published><updated>2010-08-01T14:38:16.968-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><title type='text'>The cost of accessing object fields (part 2)</title><content type='html'>In the last post, we could see the benefits of using a temporary variable to access frequently used object fields. What if the object field is accessed only two times. The benefit would be maintained?&lt;br /&gt;Let's see this example:&lt;br /&gt;Before:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal "&gt;&lt;br /&gt;begin&lt;br /&gt;  if FDataLink.Field &lt;&gt; nil then&lt;br /&gt;    Caption := FDataLink.Field.DisplayText&lt;br /&gt;  else&lt;br /&gt;    Caption := '';&lt;br /&gt;end; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After:&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;var&lt;br /&gt;  DataLinkField: TField;&lt;br /&gt;begin&lt;br /&gt;  DataLinkField := FDataLink.Field;&lt;br /&gt;  if DataLinkField &lt;&gt; nil then&lt;br /&gt;    Caption := DataLinkField.DisplayText&lt;br /&gt;  else&lt;br /&gt;    Caption := '';&lt;br /&gt;end; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It seems that &lt;a href="http://www.luizmed.kinghost.net/misc/objectfieldaccess_few.htm"&gt;yes&lt;/a&gt;, although very little (saves only two instructions). This is the kind of optimization to be done on only very sensitive areas.&lt;br /&gt;&lt;br /&gt;Since the benefit was mainly due to the compiler saving the local variable in a register, a doubt that i had in mind was what would happen in a method with many parameters? The addition of the variable would still be beneficial?&lt;br /&gt;&lt;br /&gt;So i tested the addition of a variable in a method with the following header&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure DoIt(Sender, Sender2, Sender3: TObject);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As we can &lt;a href="http://www.luizmed.kinghost.net/misc/objectfieldaccess_3param.htm"&gt;see&lt;/a&gt;, the version with the local variable is still smaller.&lt;br /&gt;&lt;br /&gt;All in all, some like to say that &lt;a href="http://www.phrases.org.uk/meanings/226400.html"&gt;less is more&lt;/a&gt;, but sometimes, as in this case, more is less!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-6565602226568466960?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/6565602226568466960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=6565602226568466960' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/6565602226568466960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/6565602226568466960'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2010/08/cost-of-accessing-object-fields-part-2.html' title='The cost of accessing object fields (part 2)'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-3695033123502594549</id><published>2010-07-25T07:20:00.000-07:00</published><updated>2010-08-01T14:36:28.385-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>The cost of accessing object fields (part 1)</title><content type='html'>The common sense make us believe that adding more code and/or more variables leads to bigger programs. Looking at the &lt;a href="http://www.luizmed.kinghost.net/misc/enummap2.htm"&gt;generated code&lt;/a&gt; of one example in the &lt;a href="http://lazarusroad.blogspot.com/2010/07/condition-check-versus-type-map.html"&gt;previous post&lt;/a&gt;, the addition of one variable made the executable smaller. This occurs because fpc is smart enough to reuse registers (in this case eax).&lt;br /&gt;&lt;br /&gt;This week, while fixing one Lazarus &lt;a href="http://bugs.freepascal.org/view.php?id=1179"&gt;bug&lt;/a&gt; i noticed the following pattern in the generated code of method TDBEdit.DataChange:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; movl 12(%ebx),%eax&lt;br /&gt; movl 24(%eax),%eax&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Basically this is the code to access FDataLink.Field property (the first instruction get the FDataLink address and the second get the Field address). So what would happen if this field was "buffered" in a TField local variable? &lt;br /&gt;&lt;br /&gt;Before:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TDBEdit.DataChange(Sender: TObject);&lt;br /&gt;begin&lt;br /&gt;  if FDataLink.Field &lt;&gt; nil then begin&lt;br /&gt;    Alignment := FDataLink.Field.Alignment; &lt;br /&gt;  [..]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;After:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TDBEdit.DataChange(Sender: TObject);&lt;br /&gt;var&lt;br /&gt;  DataLinkField: TField;&lt;br /&gt;begin&lt;br /&gt;  DataLinkField := FDataLink.Field;&lt;br /&gt;  if DataLinkField &lt;&gt; nil then begin&lt;br /&gt;    Alignment := DataLinkField.Alignment; &lt;br /&gt;  [..]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This simple change lead to these &lt;a href="http://www.luizmed.kinghost.net/misc/objectfieldaccess.htm"&gt;differences&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As expected the code became smaller but two things surprised me:&lt;br /&gt;&lt;li&gt; There's no increase in the temporary memory allocated&lt;br /&gt;&lt;li&gt; The variable assignment did cost nothing (not even one instruction)&lt;br /&gt;&lt;br /&gt;The above test was done with a "clone" of TDBEdit.DataChange in a test project. To make sure there are no confounding factors i also tested with the original code to confirm the &lt;a href="http://www.luizmed.kinghost.net/misc/objectfieldaccess_real.htm"&gt;differences&lt;/a&gt;. Notice that in this case, although the code is also smaller, the addition of the variable increase the temporary memory allocated as well the variable assignment requires one extra instruction. Bad.&lt;br /&gt;&lt;br /&gt;But there was one last hope: compile LCL with -O2 option (i assumed that LCL was already compiled with that optimization turned on). Seems that my assumption was wrong. The -O2 option did the trick: the same &lt;a href="http://www.luizmed.kinghost.net/misc/objectfieldaccess_real_o2.htm"&gt;result&lt;/a&gt; as before.&lt;br /&gt;&lt;br /&gt;In the next post i will play with a few more scenarios.&lt;br /&gt;&lt;br /&gt;And remember: don't forget to put -O2 in LCL build options when doing a release, it makes difference.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-3695033123502594549?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/3695033123502594549/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=3695033123502594549' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/3695033123502594549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/3695033123502594549'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2010/07/cost-of-acessing-object-fields-part-1.html' title='The cost of accessing object fields (part 1)'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-4316949901129494903</id><published>2010-07-21T16:28:00.000-07:00</published><updated>2010-07-25T15:39:36.256-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><title type='text'>Condition check versus a type map</title><content type='html'>Often, the programmer is faced with the need to translate from one type to another, e.g., given a boolean variable return a corresponding integer value. As a real world example see a piece of Lazarus code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;  if NewWordWrap then&lt;br /&gt;    gtk_text_view_set_wrap_mode(AGtkTextView, GTK_WRAP_WORD)&lt;br /&gt;  else&lt;br /&gt;    gtk_text_view_set_wrap_mode(AGtkTextView, GTK_WRAP_NONE); &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;NewWordWrap is a boolean variable, but the gtk function expects an integer. To translate from type to another a condition check is done.&lt;br /&gt;&lt;br /&gt;Another way to handle this would be creating a map array with the type to be translated. Lazarus also has an example of this technique:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;const&lt;br /&gt;  WidgetDirection : array[boolean] of longint = (GTK_TEXT_DIR_LTR, GTK_TEXT_DIR_RTL);&lt;br /&gt;[..]&lt;br /&gt;  gtk_widget_set_direction(AGtkWidget, WidgetDirection[UseRightToLeftAlign]);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here is the same pattern: UseRightToLeftAlign is a boolean variable and the gtk function expects a integer, but instead of checking for the variable value a boolean to integer map (WidgetDirection) is used.&lt;br /&gt;&lt;br /&gt;While the map approach seems faster because avoids a check, it adds an additional constant. I decided to look at the generated code to see the actual benefits.&lt;br /&gt;&lt;br /&gt;Check the condition code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;  if B then&lt;br /&gt;    DoIt(CONST_1)&lt;br /&gt;  else&lt;br /&gt;    DoIt(CONST_2);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Map code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;const&lt;br /&gt;  BoolMap: array[Boolean] of Integer = (CONST_2, CONST_1);&lt;br /&gt;&lt;br /&gt;  DoIt(BoolMap[B])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here is the &lt;a href="http://www.luizmed.kinghost.net/misc/boolmap.html"&gt;generated code&lt;/a&gt;. This shows a clear advantage to the map approach. Notice that in this small example the size of executables were the same.&lt;br /&gt;&lt;br /&gt;I also tested a more complex type than boolean: an enumerated.&lt;br /&gt;&lt;br /&gt;Check the condition code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;  case E of&lt;br /&gt;    EnumA: DoIt(CONST_1);&lt;br /&gt;    EnumB: DoIt(CONST_2);&lt;br /&gt;    EnumC: DoIt(CONST_3);&lt;br /&gt;  end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Map code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;const&lt;br /&gt;  EnumMap: array[TMyEnum] of Integer = (CONST_1, CONST_2, CONST_3);&lt;br /&gt;&lt;br /&gt;  DoIt(EnumMap[E])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;a href="http://www.luizmed.kinghost.net/misc/enummap.htm"&gt;result&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now with a slight optimized code for the condition check...&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;var&lt;br /&gt;  I: Integer;&lt;br /&gt;&lt;br /&gt;  case E of&lt;br /&gt;    EnumA: I := CONST_1;&lt;br /&gt;    EnumB: I := CONST_2;&lt;br /&gt;    EnumC: I := CONST_3;&lt;br /&gt;  end;&lt;br /&gt;  DoIt(I);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;... i got &lt;a href="http://www.luizmed.kinghost.net/misc/enummap2.htm"&gt;this&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-4316949901129494903?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/4316949901129494903/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=4316949901129494903' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/4316949901129494903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/4316949901129494903'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2010/07/condition-check-versus-type-map.html' title='Condition check versus a type map'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-1163701433595106444</id><published>2010-05-21T11:58:00.000-07:00</published><updated>2011-07-11T11:48:52.758-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='rtti'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>The discover of RTTI</title><content type='html'>&lt;p&gt;I never got much interest, or knowledge, by Delphi/fpc RTTI. But the recent fuzz about the new &lt;a href="http://robstechcorner.blogspot.com/search/label/RTTI"&gt;RTTI&lt;/a&gt; &lt;a href="http://www.malcolmgroves.com/blog/?p=476"&gt;features&lt;/a&gt; introduced in Delphi 2010 raised my curiosity even if the fpc provides only the &lt;a href="http://www.blong.com/Conferences/BorConUK98/DelphiRTTI/CB140.htm"&gt;old style&lt;/a&gt; RTTI. &lt;/p&gt;&lt;p&gt;The opportunity to learn, and use, RTTI came when i figured the possibility to enhance/clear some of my code.&lt;/p&gt;&lt;p&gt;To show a generic TForm i created a &lt;strike&gt;very imaginative&lt;/strike&gt; simple function:&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;function ShowForm(FormClass: TFormClass; Owner: TWinControl): TModalResult;&lt;br /&gt;var&lt;br /&gt;  Form: TForm;&lt;br /&gt;begin&lt;br /&gt;  Form := FormClass.Create(Owner);&lt;br /&gt;  try&lt;br /&gt;    Result := Form.ShowModal;&lt;br /&gt;  finally&lt;br /&gt;    Form.Destroy;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;This little function works nice and save some boilerplate code but it's limited to forms that don't need to initialize a property before is called since i don't know class type before hand.&lt;/p&gt;&lt;p&gt;As stated before, messages can be used to notify with arbitrary information any TControl, so i created a variant of the ShowForm that takes two ordinal (LPARAM and WPARAM) parameters and send a CM_INIT message to the created TForm instance. The TForm descendant would need to add a CM_INIT message handler and interpret the TLMessage parameter.&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;function ShowForm(FormClass: TFormClass; Owner: TWinControl; WData: WPARAM = 0; LData: LPARAM = 0): TModalResult;&lt;br /&gt;var&lt;br /&gt;  Form: TForm;&lt;br /&gt;begin&lt;br /&gt;  Form := FormClass.Create(Owner);&lt;br /&gt;  try&lt;br /&gt;    Form.Perform(CM_INIT, WData, LData);&lt;br /&gt;    Result := Form.ShowModal;&lt;br /&gt;  finally&lt;br /&gt;    Form.Destroy;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;So to set to true the MyBool field of a TForm descendant i would call:&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;ShowForm(TMyForm, nil, 1);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And in the CM_INIT handler:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;procedure TMyForm.CMInit(var Msg: TLMessage);&lt;br /&gt;begin&lt;br /&gt;  MyBool := (Msg.lParam = 1);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;It worked nice for simple variables like a integer or boolean, but things started to look clumsy when i needed to pass a variable of string or TObject type. Furthermore there's the limitation of restricted number of variables and the danger of the need to assume the meaning of the Msg(TLMessage) fields&lt;/p&gt;&lt;br /&gt;&lt;p&gt;There's where the RTTI ability to set arbitrary properties came in hand. All i needed to do is publish a property in the TForm descendant and set it through RTTI functions. This way i got type safety, unlimited number of parameters/variables and clearer code.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The new ShowForm interface:&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;function ShowForm(FormClass: TFormClass; Owner: TWinControl; FormProperties: array of const): TModalResult;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;FormProperties is a array of const where the even items are the property names and the odd items, the property values.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;What about RTTI? Pretty simple and direct:&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;//stripped code (no type check, no array of const parsing)&lt;br /&gt;uses typinfo;&lt;br /&gt;[..]&lt;br /&gt;  ClassInfo := Form.ClassInfo;&lt;br /&gt;  PropInfo := GetPropInfo(ClassInfo, PropertyName);&lt;br /&gt;  case PropInfo^.PropType^.Kind of&lt;br /&gt;    tkAString, tkSString:&lt;br /&gt;      SetStrProp(Form, PropInfo, StrPropertyValue);&lt;br /&gt;    tkInteger:&lt;br /&gt;      SetOrdProp(Form, PropInfo, IntPropertyValue);&lt;br /&gt;    tkBool:&lt;br /&gt;      SetOrdProp(Form, PropInfo, Integer(BoolPropertyValue));&lt;br /&gt;  end; &lt;br /&gt;[..]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Now to set MyBool property of TMyForm i do:&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush: pascal"&gt;&lt;br /&gt;ShowForm(TMyForm, nil, ['MyBool', True]);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;A lot clearer no? ;-)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-1163701433595106444?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/1163701433595106444/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=1163701433595106444' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/1163701433595106444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/1163701433595106444'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2010/05/discover-of-rtti.html' title='The discover of RTTI'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-75180268605466882</id><published>2010-02-25T16:39:00.001-08:00</published><updated>2010-02-25T16:48:57.534-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Draw Rotated Text</title><content type='html'>Current version of Lazarus provides the ability to draws text in an arbitrary rotation angle. Although is needed just to set the TFont.Orientation property to configure the feature, the position of the draw text will change according to the  angle. So, to make things easier, i wrote a routine that draws a rotated text centered in a given Rect.&lt;br /&gt;&lt;br /&gt;If someone needs something similar:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;type&lt;br /&gt;  TRotateType = (rtNone, rtCounterClockWise, rtClockWise, rtFlip);&lt;br /&gt;&lt;br /&gt;procedure DrawRotateText(Canvas: TCanvas; const R: TRect; &lt;br /&gt;  const Text: String; RotateType: TRotateType);&lt;br /&gt;var&lt;br /&gt;  TextExtent: TSize;&lt;br /&gt;  SavedOrientation: Integer;&lt;br /&gt;begin&lt;br /&gt;  SavedOrientation := Canvas.Font.Orientation;&lt;br /&gt;  TextExtent := Canvas.TextExtent(Text);&lt;br /&gt;  case RotateType of&lt;br /&gt;    rtNone:&lt;br /&gt;    begin&lt;br /&gt;      Canvas.Font.Orientation := 0;&lt;br /&gt;      Canvas.TextOut((R.Right - R.Left - TextExtent.cx) div 2,&lt;br /&gt;        (R.Bottom - R.Left - TextExtent.cy) div 2, Text);&lt;br /&gt;    end;&lt;br /&gt;    rtCounterClockWise:&lt;br /&gt;    begin&lt;br /&gt;      Canvas.Font.Orientation := 900;&lt;br /&gt;      Canvas.TextOut((R.Right - R.Left - TextExtent.cy) div 2,&lt;br /&gt;        (R.Bottom - R.Left + TextExtent.cx) div 2, Text);&lt;br /&gt;    end;&lt;br /&gt;    rtFlip:&lt;br /&gt;    begin&lt;br /&gt;      Canvas.Font.Orientation := 1800;&lt;br /&gt;      Canvas.TextOut((R.Right - R.Left + TextExtent.cx) div 2,&lt;br /&gt;        (R.Bottom - R.Left + TextExtent.cy) div 2, Text);&lt;br /&gt;    end;&lt;br /&gt;    rtClockWise:&lt;br /&gt;    begin&lt;br /&gt;      Canvas.Font.Orientation := -900;&lt;br /&gt;      Canvas.TextOut((R.Right - R.Left + TextExtent.cy) div 2,&lt;br /&gt;        (R.Bottom - R.Left - TextExtent.cx) div 2, Text);&lt;br /&gt;    end;&lt;br /&gt;  end;&lt;br /&gt;  Canvas.Font.Orientation := SavedOrientation;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-75180268605466882?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/75180268605466882/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=75180268605466882' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/75180268605466882'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/75180268605466882'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2010/02/draw-rotated-text.html' title='Draw Rotated Text'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-3420276174049718955</id><published>2010-02-11T04:17:00.000-08:00</published><updated>2010-02-11T04:20:45.410-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualtreeview'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Finally...</title><content type='html'>... the &lt;a href="http://lazarusroad.blogspot.com/search/label/virtualtreeview"&gt;VirtualTreeView port&lt;/a&gt; is released.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://forum.lazarus.freepascal.org/index.php/topic,8601.0.html"&gt;release notes&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-3420276174049718955?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/3420276174049718955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=3420276174049718955' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/3420276174049718955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/3420276174049718955'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2010/02/finally.html' title='Finally...'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-3021054394580184072</id><published>2010-02-03T14:05:00.000-08:00</published><updated>2010-02-03T14:23:26.699-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='utf-8'/><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='dataset'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlite'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Convert database files from Ansi to UTF-8</title><content type='html'>While porting old Delphi projects that uses paradox files i faced the problem that the data was stored in an ANSI code page (the current Lazarus version expects UTF-8 encoded strings).&lt;br /&gt;&lt;br /&gt;So i wrote an simple application that can convert the encoding of database files (sqlite3, dbf, paradox).&lt;br /&gt;&lt;br /&gt;That program can be useful for more than legacy code: after converting a spreadsheet data to a sqlite3 file using &lt;a href="http://www.sqlmaestro.com/products/sqlite/datawizard/"&gt;Sqlite Data Wizard&lt;/a&gt;. The resulted file was in ANSI and there was no option (AFAIK) to convert directly to UTF-8.&lt;br /&gt;&lt;br /&gt;I put it &lt;a href="http://sites.google.com/site/silvioprogbs/downloads/ConvertDB.zip?attredirects=0&amp;amp;d=1"&gt;here&lt;/a&gt; in hope that can be used by someone else.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-3021054394580184072?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/3021054394580184072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=3021054394580184072' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/3021054394580184072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/3021054394580184072'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2010/02/convert-database-files-from-ansi-to-utf.html' title='Convert database files from Ansi to UTF-8'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-5740262282364705806</id><published>2009-11-30T02:05:00.001-08:00</published><updated>2010-07-25T15:47:37.922-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Using messages to notify events</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;Lately, i've been using frames extensively in my projects and recently i faced a little problem while working with them: how to notify the parent control (often a TForm) changes in the state of the frame controls/data?&lt;br /&gt;&lt;br /&gt;I tried some approaches:&lt;br /&gt;&lt;br /&gt;1) Add an event handler to the TDatasource.OnDataChange event.&lt;br /&gt;This has the disadvantage of not being a generic solution since not always the frame has a TDataSource. Also this event is fired in changes to all fields of the respective TDataset, and often only a few fields need to be monitored, leading to some overhead. Yet is not possible to set the event at design time due to bug &lt;a href="http://bugs.freepascal.org/view.php?id=14947"&gt;14947&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;2)Create handlers for events of child controls.&lt;br /&gt;Works by setting in the parent form handlers for events, e.g. OnChange, of child controls of the frame. It has the drawback of needing more than one event handler in the case where is necessary to monitor more than one control/field.&lt;br /&gt;Another problem is that such event handlers can be cleared by the IDE due to bug &lt;a href="http://bugs.freepascal.org/view.php?id=14835"&gt;14835&lt;/a&gt;. Not to say that sometimes the changes in the frame are not propagated to frame instances. In this case the workaround i found to sync the frames was to remove and add the frame again, loosing all properties set in the frame parent control.&lt;br /&gt;&lt;br /&gt;So, i decided to implement a specific event for the frame. The first approach i thought was adding a TNotifyEvent property to the frame but this has almost the same drawbacks&lt;span class="descricao"&gt; of approach 2. At this point the idea of using messages came in my mind.&lt;br /&gt;&lt;br /&gt;AFAIK messages are used mostly in LCL and seldom in user projects so i had doubts if would work. Here is what i did:&lt;br /&gt;&lt;br /&gt;1) Declare a custom message id constant&lt;br /&gt;I declared the following constant in a shared unit:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;LM_CHILDDATACHANGED = LM_USER + 1;&lt;br /&gt;&lt;/blockquote&gt;2) Create a method in the frame to send the message&lt;br /&gt;I created a method SendDataChangeMsg with the following code:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;var&lt;br /&gt; Form: TCustomForm;&lt;br /&gt;begin&lt;br /&gt; Form := GetFirstParentForm(Self);&lt;br /&gt; if Form &amp;lt;&amp;gt; nil then&lt;br /&gt;   Form.Perform(LM_CHILDDATACHANGED, 0, 0);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;The code is pretty straight. It get the parent form and then send the message through Perform method&lt;br /&gt;&lt;br /&gt;3) Hook the events of the controls that should be monitored&lt;br /&gt;Mostly OnChange events. Just called SendDataChangeMsg inside them&lt;br /&gt;&lt;br /&gt;4) Add a property ValidData&lt;br /&gt;This property checks if the data in the monitored controls are valid&lt;br /&gt;&lt;br /&gt;5) Intercept the message at the frame parent (TForm)&lt;br /&gt;In the TForm i put the frame, i created a method to intercept the message declared as follow:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;procedure ChildDataChanged(var Msg: TLMessage); message&lt;br /&gt; LM_CHILDDATACHANGED;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I'm using that to allow saving or not the data so i put a code to enable/disable the save button:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;SaveButton.Enabled := Frame.ValidData;&lt;br /&gt;&lt;/blockquote&gt;That's all. It's working fine for me. In the end i got a pretty clear notify system. Now i can remove/add the frame as necessary without the need to reconnect the event handlers every time.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img src="http://img.zemanta.com/pixy.gif?x-id=536779dc-56e2-8e84-bcdf-1af0c0ad6e86" alt="" class="zemanta-pixie-img" /&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-5740262282364705806?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/5740262282364705806/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=5740262282364705806' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/5740262282364705806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/5740262282364705806'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2009/11/using-messages-to-notify-events.html' title='Using messages to notify events'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-907038521305676941</id><published>2009-11-15T14:06:00.000-08:00</published><updated>2009-11-15T14:41:05.368-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Qt and Lazarus: a nice surprise (again)</title><content type='html'>Long, long time ago i &lt;a href="http://lazarusroad.blogspot.com/2007/10/strange-days.html"&gt;wrote&lt;/a&gt; how the Lazarus Qt interface impressed me by its functionality. Yesterday, after updating my Lazarus working copy to do some debugging in Linux, i rebuilt the IDE as usual. But the IDE looked a little different, most notably the source editor font. I've got sometime to figure that the IDE was compiled using the Qt interface. I use the gtk2 interface but somehow i left the LCL build configured to Qt, so the unexpected result.&lt;br /&gt;&lt;br /&gt;Again, the Qt interface surprised me. The IDE ran fine with good looking and very responsive except by bugs &lt;a href="http://bugs.freepascal.org/view.php?id=15101"&gt;15101&lt;/a&gt; and &lt;a href="http://bugs.freepascal.org/view.php?id=15103"&gt;15103&lt;/a&gt;. Unfortunately, i use a lot the features that these bugs affect and i will stay with gtk2 for now. But as soon as these bugs are fixed i will give a try to Qt again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-907038521305676941?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/907038521305676941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=907038521305676941' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/907038521305676941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/907038521305676941'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2009/11/qt-and-lazarus-nice-surprise-again.html' title='Qt and Lazarus: a nice surprise (again)'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-7092621893049473832</id><published>2009-11-01T01:17:00.000-07:00</published><updated>2009-11-01T02:23:51.879-08:00</updated><title type='text'>Using TDBEdit to handle date fields</title><content type='html'>If you try to link a date field with a TDBEdit, when an user inserts an invalid date, the control reacts with a non user friendly exception message. So, for long time, i used a TMaskEdit to edit date values and then update manually the dataset.&lt;br /&gt;After some debate about TMaskEdit in the mail list i tried to workaround this problem.&lt;br /&gt;&lt;br /&gt;The first think i did was to set the EditMask property to !99/99/9999;1;_ and hardcode the ShortDateFormat with a compatible format, e.g. 'dd/mm/yyyy'.&lt;br /&gt;&lt;br /&gt;To validate the value inserted i created a handler to be linked to TDBEdit OnExit event(the color setting is optional):&lt;br /&gt;&lt;br /&gt;&lt;pre wrap=""&gt;var&lt;br /&gt;D: TDateTime;&lt;br /&gt;DateEdit: TDBEdit;&lt;br /&gt;begin&lt;br /&gt;DateEdit := Sender as TDBEdit;&lt;br /&gt;if not TryStrToDate(DateEdit.Text, D) then&lt;br /&gt;begin&lt;br /&gt;ShowMessage(DateEdit.Text + ' is not a valid date');&lt;br /&gt;DateEdit.Clear;&lt;br /&gt;DateEdit.Color := clRed;&lt;br /&gt;//or set a default value&lt;br /&gt;//DateEdit.Text := '10/10/1900';&lt;br /&gt;end&lt;br /&gt;else&lt;br /&gt;DateEdit.Color := clWindow;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;So you clear the TDBEdit and get a empty string that is valid value right? Not so fast. The message dialog will trigger an KillFocus message updating the Dataset with the old (and invalid) value. Calling ShowMessage after Clear does not help because the the control will try to update the dataset with "  /  /    ", that is also not a valid value.&lt;br /&gt;&lt;br /&gt;The solution i found was to handle the OnSetText event of the date field to skip all invalid values:&lt;br /&gt;&lt;br /&gt;&lt;pre wrap=""&gt;var&lt;br /&gt;D: TDateTime;&lt;br /&gt;begin&lt;br /&gt;if not TryStrToDate(aText, D) then&lt;br /&gt;  Sender.Value := Null&lt;br /&gt;else&lt;br /&gt;  Sender.AsDateTime := D;&lt;br /&gt;end;&lt;/pre&gt;&lt;br /&gt;With this approach is possible to allow date edition with masks safely.&lt;br /&gt;&lt;br /&gt;However, setting events for each TDBEdit and date field is annoying, so i created an TDBEdit descendant to handle this issue. It can be found in the LuiControls package (&lt;a href="http://luipack.googlecode.com/svn/trunk/luicontrols"&gt;svn version&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;In time:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Rx package has a TDBDateEdit component, but it does not allow editing with masks and don't manage invalid dates.&lt;/li&gt;&lt;li&gt;It's required a recent (post 0.9.28) svn version because of a bug in TMaskEdit in the earlier versions&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-7092621893049473832?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/7092621893049473832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=7092621893049473832' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7092621893049473832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7092621893049473832'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2009/11/using-tdbedit-to-handle-date-fields.html' title='Using TDBEdit to handle date fields'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-9196090813052424313</id><published>2008-11-15T18:14:00.000-08:00</published><updated>2010-07-25T15:44:04.799-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><title type='text'>Effect of using a constant parameter for string types</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;Is not rare to find implementations of procedures/functions/methods that uses a value parameter for read only string &lt;a href="http://www.freepascal.org/docs-html/ref/refse56.html#x122-12900011.4"&gt;arguments&lt;/a&gt;. While i always use constant parameters for such cases, the real benefit of this code practice was not clear. Until today.&lt;br /&gt;&lt;br /&gt;I made a small application that implements two versions of a procedure identical except by the type of parameter (Value vs Constant)...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;program asmConstParameter;&lt;br /&gt;&lt;br /&gt;{$Mode ObjFpc}&lt;br /&gt;{$H+}&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;  SysUtils, Types;&lt;br /&gt;&lt;br /&gt;procedure DoIt(V: String);&lt;br /&gt;begin&lt;br /&gt;  Writeln(V);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure ByValue(V: String);&lt;br /&gt;var&lt;br /&gt;  S: String;&lt;br /&gt;begin&lt;br /&gt;  S := V;&lt;br /&gt;  DoIt(S);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure ByReference(const V: String);&lt;br /&gt;var&lt;br /&gt;  S: String;&lt;br /&gt;begin&lt;br /&gt;  S := V;&lt;br /&gt;  DoIt(S);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt;  X: String;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;  X := 'Test';&lt;br /&gt;  ByValue(X);&lt;br /&gt;  ByReference(X);&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and examined the assembler output. See the &lt;a href="http://www.luizmed.kinghost.net/misc/coddiff.htm"&gt;difference&lt;/a&gt; yourself. Using optimizations through -O compiler options does not change the produced code.&lt;br /&gt;&lt;br /&gt;So using a constant parameter has a practical effect, is not only a good code practice.&lt;br /&gt;&lt;br /&gt;BTW: just for curiosity i put &lt;a href="http://www.freepascal.org/docs-html/prog/progsu32.html#x39-370001.1.32"&gt;{$IMPLICITEXCEPTIONS OFF}&lt;/a&gt; in the program header. &lt;a href="http://www.luizmed.kinghost.net/misc/coddiff_noexception.htm"&gt;Not bad&lt;/a&gt;. Be aware that this info is here &lt;span style="font-weight: bold; font-style: italic;"&gt;just for curiosity&lt;/span&gt; ;-) .&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;UPDATE&lt;/span&gt;: Using constant arguments also benefits ShortString types. &lt;a href="http://www.luizmed.kinghost.net/misc/coddif_short.htm"&gt;See&lt;/a&gt;.&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-9196090813052424313?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/9196090813052424313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=9196090813052424313' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/9196090813052424313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/9196090813052424313'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2008/11/effect-of-using-constant-parameter-for.html' title='Effect of using a constant parameter for string types'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-6224029570842086933</id><published>2008-10-26T11:52:00.000-07:00</published><updated>2008-10-26T12:36:15.161-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualtreeview'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Status of Virtual Treeview port</title><content type='html'>It has been more than one year after the &lt;a href="http://lazarusroad.blogspot.com/2007/03/and-not-alone.html"&gt;last blog update&lt;/a&gt; about the Virtual Treeview (VTV) port and some people may be asking what happened with it.&lt;br /&gt;&lt;br /&gt;The port is far from dead. In fact, it is fully working under Win32, Gtk1/2 and Qt since, at least, the previous six months. I was just waiting to the Lazarus 0.9.26 release to do an official release. Lazarus 0.9.26 is out, so where is the new VTV?&lt;br /&gt;&lt;br /&gt;One of the main features of Lazarus 0.9.26 is the Unicode support for Win32 interface that, in your turn, uses UTF8 encoding. Currently VTV supports Unicode by using UTF-16/WideString and was working fine. After the LCL Unicode switch some encoding conversion problems appeared when iterating with strings returned by databases or LCL controls, so i decided to anticipate the migration to UTF-8 before doing a release.&lt;br /&gt;&lt;br /&gt;This task is not trivial and i'll start to work on it only after December, so don't expect an release soon.&lt;br /&gt;&lt;br /&gt;This has some advantages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; There will be no need to further string types changes (WideString -&gt; String) in applications created with the released component&lt;/li&gt;&lt;li&gt;It will be faster since WideString is known to be slower specially under Win32&lt;/li&gt;&lt;/ul&gt;Anyway if someone is interested in testing the component check the instructions on how to use the svn version &lt;a href="http://code.google.com/p/luipack/wiki/VirtualTreeview"&gt;here&lt;/a&gt;. Be aware that a change from WideString to String type will be necessary in the long run.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-6224029570842086933?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/6224029570842086933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=6224029570842086933' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/6224029570842086933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/6224029570842086933'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2008/10/status-of-virtual-treeview-port.html' title='Status of Virtual Treeview port'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-7369369556723388231</id><published>2008-06-01T13:54:00.000-07:00</published><updated>2008-12-10T20:02:07.469-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><category scheme='http://www.blogger.com/atom/ns#' term='cairo'/><title type='text'>LCL, Gtk2, Pango and Cairo</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Introduction&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Lately, in the Lazarus mail list, has been a lot of discussion about the performance of LCL/TextOut under Gtk2 and many (erroneous) arguments used derives from the lack of proper knowledge, including from myself. So in this article, i'll try to clear the things a bit.&lt;br /&gt;&lt;br /&gt;When Gtk2 was released back in 2002, Pango was one of the Gtk2 main new features. Pango provides support to render Unicode text (encoded in utf8) with &lt;a href="http://www.ibm.com/developerworks/library/l-u-pango1/"&gt;advanced layouts&lt;/a&gt;. But i was also one of the reasons of the degraded performance of Gtk2 when compared with its predecessor.&lt;br /&gt;&lt;br /&gt;Pango has a flexible design allowing to use different backends (Cairo, Xft, Win32, X) to render text. Until version 2.6 Gtk2 used the Xft backend that was replaced, starting from version 2.8, by &lt;a href="http://cairographics.org/"&gt;cairo&lt;/a&gt; (a 2D vector drawing library) as the default renderer. At that time the performance dropped even more, but after some work in &lt;a href="http://www.gnome.org/%7Efederico/news-2005-10.html#gtkfilechooser-profile-4"&gt;pango&lt;/a&gt; and in cairo, the things got &lt;a href="http://blogs.gnome.org/xan/2006/12/11/performance-measurement-iv/"&gt;better&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So, the first point to take in consideration when evaluating pango performance is the version of pango and cairo. More on this later.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Testing LCL&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In order to get an accurate diagnostic, i wrote an test application that fills an entire window with text using TextOut and compared the results (output quality and time to draw) of the Linux widgetsets (Gtk1, Gtk2, Qt).&lt;br /&gt;&lt;br /&gt;Here is the output:&lt;br /&gt;&lt;span style="text-decoration: underline;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vsnitvZr5Ps/SEMll-NvaEI/AAAAAAAAACs/mH2wD7JNMM8/s1600-h/textout-lcl.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_vsnitvZr5Ps/SEMll-NvaEI/AAAAAAAAACs/mH2wD7JNMM8/s400/textout-lcl.png" alt="" id="BLOGGER_PHOTO_ID_5207046928456443970" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The time to draw an entire screen (average times of 15 iterations):&lt;br /&gt;&lt;br /&gt;gtk1: 24ms&lt;br /&gt;gtk2: 150ms&lt;br /&gt;qt: 92ms&lt;br /&gt;&lt;br /&gt;The gtk1 widgetset is really fast but it has two drawbacks: the font quality is low and the screen flickers while updating.&lt;br /&gt;The gtk2 widgetset is the slowest loosing to qt by 60%, but in other hand has the sharpest font draw. An important point is that when double buffer is disabled (LCL disables it by default), the screen flickers just like gtk1, but if double buffer is enabled there's no flicker and the screen is updated instantaneous.&lt;br /&gt;The qt widgetset is in the midterm both in quality and in speed. Not sure why qt text looks blurred: if is a configuration or the default font is not so good. There's also no screen flicker since qt is all double buffered.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Gtk2 dissected (almost)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Pango allows more than one engine to be used and there's also the options to draw directly using cairo or the old gdk functions (that use the X11 bitmap fonts). So i wrote an  application using direct calls to the gtk2 api. It draws text with default Gtk2 (Pango/Gdk), Pango/Xft, Xft, Cairo, Gdk/X11.&lt;br /&gt;&lt;br /&gt;The output (Cairo is equal to Gtk2 and Gdk/X11 is equal to LCL/Gtk1):&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vsnitvZr5Ps/SEMmmzKozLI/AAAAAAAAAC0/AoP8gTihOfQ/s1600-h/textout-gtk2-xft.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_vsnitvZr5Ps/SEMmmzKozLI/AAAAAAAAAC0/AoP8gTihOfQ/s400/textout-gtk2-xft.png" alt="" id="BLOGGER_PHOTO_ID_5207048042182134962" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The time results:&lt;br /&gt;&lt;br /&gt;Gtk2 (Pango/Gdk): 130ms&lt;br /&gt;Cairo: 90ms&lt;br /&gt;Xft: 70ms&lt;br /&gt;Gdk/X11: 12ms&lt;br /&gt;Pango/Xft: 110ms&lt;br /&gt;&lt;br /&gt;Gtk2: very close to LCL/Gtk2&lt;br /&gt;Cairo: the same quality of Pango/Gdk (I would be very surprised if was different ;-)) but significantly faster&lt;br /&gt;Xft: Almost two time faster then Pango/Gdk but with lower quality output (An configuration issue?)&lt;br /&gt;Gdk/X11: basically the same output of gtk1. Again really faster but without the screen flicker of gtk1.&lt;br /&gt;Pango/Xft: faster than Pango/Gdk, slower than Xft. Out of option since is not working at all: it draws always at 0,0 coordinates.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Alternatives to pango?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;From this tests, using directly Cairo to draw text seems to be a good option to replace Pango: the same quality and faster. But is not that easy. With direct calls to Cairo is needed to do all sort of text position (bidi, alignment) and text styles (underline) manually. Also it would be necessary to do the font selection and loading logic manually in a system specific way,i.e., is necessary different code to Linux/Win32/MacOSX.&lt;br /&gt;&lt;br /&gt;Another option is using Xft directly. Aside from the different/worse output, it would be necessary to change the widgetset to retrieve the XftDraw handle for each drawable which is a complex task &lt;span class="descricao"&gt;principally for double buffered controls. All in a system specific way.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Gdk/X11: the quality of output and lack of Unicode support makes a no-no option. At least for me.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Is there a direct/easy/faster replacement for Pango under LCL/Gtk2? No.&lt;br /&gt;&lt;br /&gt;Here is necessary to make another question: there's a real need to replace it? Like shown earlier, default Pango is really slow compared to other alternatives, but, principally when double buffer is enabled, there's no visible glitches or general system slowdown. Is also necessary to take in account the increase of code size and complexity in LCL/Gtk2 side to make such change.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Notes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The test applications can be found &lt;a href="http://www.geocities.com/camara_luiz/BenchTextOut.zip"&gt;here&lt;/a&gt; and &lt;a href="http://www.geocities.com/camara_luiz/BenchTextOutGtk2.zip"&gt;here&lt;/a&gt;. Is necessary the package &lt;a href="https://svn.bountysource.com/luipack/trunk/chronolog"&gt;chronolog&lt;/a&gt;;&lt;/li&gt;&lt;li&gt;The test were conducted in a Celeron 1.4, 512MB, with an intel integrated video running Ubuntu 8.04;&lt;/li&gt;&lt;li&gt;Upgrading from Ubuntu 7.10 to 8.04 leaded to an significant speed in all widgetsets (Gtk2 250 &gt; 150 / Gtk1 50 &gt; 24ms);&lt;/li&gt;&lt;li&gt;I also tested drawing with low level Pango api. I'll post the results later;&lt;/li&gt;&lt;li&gt;&lt;strike&gt;Win32 took impressive 5ms in the same test&lt;/strike&gt; Not to be considered since the win32 test was done in another (more powerful) machine;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A point that is not directly related to text draw but affects performance of LCL/Gtk2 is the fact that the LCL/Gtk2 test application calls 4 times FcFontSort, while a Gtk2 pure application calls only once. This is an expensive call that takes almost 20% of the application time.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-7369369556723388231?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/7369369556723388231/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=7369369556723388231' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7369369556723388231'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7369369556723388231'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2008/06/lcl-gtk2-pango-and-cairo.html' title='LCL, Gtk2, Pango and Cairo'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vsnitvZr5Ps/SEMll-NvaEI/AAAAAAAAACs/mH2wD7JNMM8/s72-c/textout-lcl.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-7868633743983703149</id><published>2008-03-15T16:17:00.000-07:00</published><updated>2010-07-25T15:44:04.800-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Reduce memory usage of LCL</title><content type='html'>To test the conclusions of the last post i modified the field order of some LCL classes to group together Boolean fields.&lt;br /&gt;&lt;br /&gt;Here's the return value of the InstanceSize property:&lt;br /&gt;&lt;br /&gt;TButton&lt;br /&gt;  before: 874 bytes&lt;br /&gt;  after:  829 bytes (saved 24, 16 and 5 bytes in TControl, TWinControl and TCustomButton respectively)&lt;br /&gt;&lt;br /&gt;TMenuItem&lt;br /&gt;  before:  152 bytes&lt;br /&gt;  after: 136 bytes&lt;br /&gt;&lt;br /&gt;In an application with 100 controls and 20 menu items you save 2400 (considering only TControl memory) and 320 bytes respectively.&lt;br /&gt;&lt;br /&gt;Maybe be this is negligible in computers with 1GB or more of RAM, but for mobile platforms it makes a difference.&lt;br /&gt;&lt;br /&gt;The patch is &lt;a href="http://www.geocities.com/camara_luiz/lclmemory1.zip"&gt;here&lt;/a&gt;. Have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-7868633743983703149?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/7868633743983703149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=7868633743983703149' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7868633743983703149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7868633743983703149'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2008/03/reduce-memory-usage-of-lcl.html' title='Reduce memory usage of LCL'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-6039677584794603404</id><published>2008-02-19T07:05:00.000-08:00</published><updated>2010-07-25T15:47:37.923-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><title type='text'>Memory layout (and size) of a object</title><content type='html'>After reading a &lt;a href="http://www.geocities.com/svi37/cyber/delphi9/iimplementation.html"&gt;article about memory layout of objects in Delphi&lt;/a&gt; i was curious about how fpc behaves. So i did some small tests:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Memory layout of objects (instances of a class)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;At offset 0 resides the virtual method table. Starting from ofsset 4 comes the fields. Just like in Delphi.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Number of associated methods&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;The number of associated methods and if they are virtual does not influence the object size. Just like in Delphi.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Type of the fields&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;According to the cited article, Delphi reserves 4 bytes for each field even if the type has a size of 1 byte. Here comes the fun.&lt;br /&gt;&lt;br /&gt;Take the following classes:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;  TOneFlagClass = class&lt;br /&gt;  Flag1: Boolean;&lt;br /&gt; end;&lt;br /&gt;&lt;br /&gt; TTwoFlagClass = class&lt;br /&gt;  Flag1: Boolean;&lt;br /&gt;  Flag2: Boolean;&lt;br /&gt; end;&lt;/blockquote&gt;&lt;br /&gt;The size of TOneFlagClass and TTwoFlagClass are 5 and 6 bytes respectively  (4 for the vmt and 1 for each field). The memory offsets of Flag1 and Flag2 are 4 and 5.&lt;br /&gt;&lt;br /&gt;Delphi is a bit different here. The size of both classes are 8. The memory offsets of the fields are the same as fpc.&lt;br /&gt;&lt;br /&gt;At this time i think: "In this case is better to place less than 4 bytes fields at the end of the class declaration to avoid subsequent fields to be accessed outside the dword boundary"&lt;br /&gt;&lt;br /&gt;I was wrong. In fact half wrong:&lt;br /&gt;&lt;br /&gt;Take the following classes:&lt;br /&gt;&lt;blockquote&gt;  TFlagFirstClass = class&lt;br /&gt;    Flag1: Boolean;&lt;br /&gt;    Int1: Integer;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;  TFlagLastClass = class&lt;br /&gt;    Int1: Integer;&lt;br /&gt;    Flag1: Boolean;&lt;br /&gt;  end;&lt;/blockquote&gt;The size of TFlagFirstClass and TFlagLastClass are 12 and 9 respectively. The compiler allocates 4 bytes for the boolean field to maintain subsequent fields (that has a size of 4 bytes) aligned with the dword boundary.&lt;br /&gt;&lt;br /&gt;If another boolean field (Flag2) is  added just after Flag1, the instance size is not affected. In fact, grouping 4 boolean (or another 1 byte type) fields together will lead to the same instance size as only one boolean field if those are succeeded by Integer or Pointer like types.&lt;br /&gt;&lt;br /&gt;In the end, my suggestion is still valid: put the "less than 4 bytes field types" at the end of the field declaration of the class (or group together in groups with 4 bytes in total). You will save some memory.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;If you are not convinced compare size of a class with the following fields sequence:&lt;br /&gt;Boolean, Integer, Boolean, Integer, Boolean, Integer, Boolean, Integer&lt;br /&gt;Boolean, Boolean, Boolean, Boolean, Integer, Integer, Integer, Integer&lt;br /&gt; Integer, Integer, Integer, Integer, Boolean, Boolean, Boolean, Boolean&lt;br /&gt;&lt;br /&gt;Some notes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Object here is not referenced as the &lt;span style="font-style: italic;"&gt;object type&lt;/span&gt; (that has the same memory layout of a record), but as the instance of a class&lt;br /&gt;&lt;/li&gt;&lt;li&gt;There's no difference between mode delphi and objfpc&lt;/li&gt;&lt;li&gt;It's valid only for i386 architeture. No idea how this works in ppc, amd64, arm&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-6039677584794603404?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/6039677584794603404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=6039677584794603404' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/6039677584794603404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/6039677584794603404'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2008/02/memory-layout-and-size-of-object.html' title='Memory layout (and size) of a object'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-8531243214005486304</id><published>2008-01-23T14:20:00.000-08:00</published><updated>2010-07-25T15:47:37.924-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><title type='text'>Effect of buffer size in deflate and md5</title><content type='html'>I tested the effect of buffer size in compressing a file using deflate procedure (paszlib unit) and calculating the md5 (using the functions of md5 unit).&lt;br /&gt;&lt;br /&gt;I loaded a 30MB file in memory and did the compression/md5 calculation. The buffer size varied from 1024 to 512.000.&lt;br /&gt;&lt;br /&gt;To my surprise no significantly difference was found, so no graph this time since is almost a plain line.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-8531243214005486304?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/8531243214005486304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=8531243214005486304' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/8531243214005486304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/8531243214005486304'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2008/01/effect-of-buffer-size-in-deflate-and.html' title='Effect of buffer size in deflate and md5'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-8699469397175210818</id><published>2007-10-26T18:55:00.000-07:00</published><updated>2010-07-25T15:48:11.734-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><category scheme='http://www.blogger.com/atom/ns#' term='cairo'/><title type='text'>What Time Is It?</title><content type='html'>&lt;a href="http://www.ralph-glass.homepage.t-online.de/clock/readme.html"&gt;How&lt;/a&gt; &lt;a href="http://macslow.thepimp.net/?page_id=23"&gt;many&lt;/a&gt; &lt;a href="http://gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/ch15s07.html"&gt;cairo&lt;/a&gt; &lt;a href="http://chrislord.net/blog/Software/Dates/libjana-knows-what-time-it-is.enlighten"&gt;clocks&lt;/a&gt; the world needs?&lt;br /&gt;&lt;br /&gt;I don't think we have sufficient!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;object height="355" width="425"&gt;&lt;param name="movie" value="http://www.youtube.com/v/K-PL6Sf_Ydo&amp;amp;rel=1"&gt;&lt;param name="wmode" value="transparent"&gt;&lt;embed src="http://www.youtube.com/v/K-PL6Sf_Ydo&amp;amp;rel=1" type="application/x-shockwave-flash" wmode="transparent" height="355" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-8699469397175210818?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/8699469397175210818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=8699469397175210818' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/8699469397175210818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/8699469397175210818'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/10/what-time-is-it.html' title='What Time Is It?'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-5324968906153564546</id><published>2007-10-15T10:35:00.000-07:00</published><updated>2010-07-25T15:47:37.925-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><title type='text'>Effect of buffer size for reading files [Linux]</title><content type='html'>Reading a file entirely in memory is not a good idea  as stated before, but how large should be the memory buffer?&lt;br /&gt;&lt;br /&gt;I did a test under Linux (Ubuntu 7.04) reading the fpc2.2.0 installation file (29MB) using different buffer sizes.&lt;br /&gt;&lt;br /&gt;Here's the result:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vsnitvZr5Ps/RxOoE3-p7_I/AAAAAAAAACU/XjHIaqhbspY/s1600-h/chronoviewchart.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_vsnitvZr5Ps/RxOoE3-p7_I/AAAAAAAAACU/XjHIaqhbspY/s400/chronoviewchart.png" alt="" id="BLOGGER_PHOTO_ID_5121622002950074354" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The time to read the file decreases as the buffer size increases until the buffer is 128kb then, as the buffer gets bigger, the trend inverts.&lt;br /&gt;&lt;br /&gt;Some notes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The test was executed three times for each buffer size. The results are expressed as the Median;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The Y axis is the time to read all the file in microseconds. The X axis is the buffer size in bytes;&lt;/li&gt;&lt;li&gt;The first time the file is read is significantly  slower than subsequent reads. Probably this is an effect of the OS file system buffering (I did not find a way to skip it). This limits further analysis. However, excluding the first run, all other results are consistent across the same buffer size. All results can be browsed &lt;a href="http://www.geocities.com/camara_luiz/benchmarks/readfilebench.htm"&gt;here&lt;/a&gt;.  &lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-5324968906153564546?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/5324968906153564546/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=5324968906153564546' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/5324968906153564546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/5324968906153564546'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/10/effect-of-buffer-size-for-reading-files.html' title='Effect of buffer size for reading files [Linux]'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vsnitvZr5Ps/RxOoE3-p7_I/AAAAAAAAACU/XjHIaqhbspY/s72-c/chronoviewchart.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-9034563219165425054</id><published>2007-10-01T08:55:00.000-07:00</published><updated>2007-10-01T09:07:03.282-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Strange days...</title><content type='html'>Since day 1 of Lazarus, the gtk1 widgetset is the default, and reference, interface under Linux.&lt;br /&gt;&lt;br /&gt;Today, after one of my applications crashed using gtk1, i tried it with the Qt4 interface: it worked flawlessly!!.&lt;br /&gt;&lt;br /&gt;The Qt interface, given the recent work, is in the way to become the standard widgetset under Linux. Wait and see.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-9034563219165425054?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/9034563219165425054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=9034563219165425054' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/9034563219165425054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/9034563219165425054'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/10/strange-days.html' title='Strange days...'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-7905182338152819308</id><published>2007-09-25T05:41:00.000-07:00</published><updated>2010-07-25T15:44:04.803-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='valgrind'/><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><title type='text'>Update: Using Valgrind/massif with fpc</title><content type='html'>In a previous &lt;a href="http://lazarusroad.blogspot.com/2007/09/using-valgrind-to-profile-fpc.html"&gt;post&lt;/a&gt; i claimed that the option to hide memory allocation wrappers in massif was broken or not working with fpc.&lt;br /&gt;&lt;br /&gt;The problem is that i was using only the pascal name of the function (CMEM_CGETMEM) instead of the mangled internal name (CMEM_CGETMEM$LONGINT$$POINTER).&lt;br /&gt;&lt;br /&gt; &lt;div id="comments-bar-info"&gt;  &lt;/div&gt; Thanks to  &lt;a href="http://www.camelot.homedns.org/%7Emichalis/" onclick="" rel="nofollow"&gt;Michalis Kamburelis&lt;/a&gt;  that gave the hint and also provided this script:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;#!/bin/sh&lt;br /&gt;set -eu&lt;br /&gt;&lt;br /&gt;valgrind --tool=massif \&lt;br /&gt; --alloc-fn='CMEM_CGETMEM$LONGINT$$POINTER' \&lt;br /&gt; --alloc-fn='CMEM_CREALLOCMEM$POINTER$LONGINT$$POINTER' \&lt;br /&gt; --alloc-fn='SYSTEM_GETMEM$LONGINT$$POINTER' \&lt;br /&gt; --alloc-fn='SYSTEM_GETMEM$POINTER$LONGINT' \&lt;br /&gt; --alloc-fn='SYSTEM_REALLOCMEM$POINTER$LONGINT$$POINTER' \&lt;br /&gt; --format=html \&lt;br /&gt; "$@"&lt;/blockquote&gt;I updated the previous charts so they show where in pascal code the memory is allocated.&lt;br /&gt;&lt;br /&gt;PS: it's annoying that the function names are truncated in the charts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-7905182338152819308?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/7905182338152819308/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=7905182338152819308' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7905182338152819308'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7905182338152819308'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/09/update-using-valgrindmassif-with-fpc.html' title='Update: Using Valgrind/massif with fpc'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-238172977144606998</id><published>2007-09-23T12:11:00.000-07:00</published><updated>2010-07-25T15:44:04.804-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='valgrind'/><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='zlibar'/><title type='text'>Zlibar memory behavior compressing many small files</title><content type='html'>The previous analysis of zlibar memory behavior was done taking as example the compression of one big file. Let's with many small files (all *.pas files under lazarus/lcl dir).&lt;br /&gt;&lt;br /&gt;Original (load the entire file in memory):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vsnitvZr5Ps/Rvj9Qn-p78I/AAAAAAAAAB8/TdZoGyN_eoA/s1600-h/zlibar.orig.many.fixed.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_vsnitvZr5Ps/Rvj9Qn-p78I/AAAAAAAAAB8/TdZoGyN_eoA/s400/zlibar.orig.many.fixed.png" alt="" id="BLOGGER_PHOTO_ID_5114115838930710466" border="0" /&gt;&lt;/a&gt;The heaptrc dump:&lt;br /&gt;18573 memory blocks allocated : 496119560/496173048&lt;br /&gt;18573 memory blocks freed     : 496119560/496173048&lt;br /&gt;0 unfreed memory blocks : 0&lt;br /&gt;True heap size : 4423680&lt;br /&gt;True free heap : 4423680&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;After memory optimization (load file in small buffer):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vsnitvZr5Ps/Rvj9JX-p77I/AAAAAAAAAB0/LWG_PQVgBQ8/s1600-h/zlibar.opt1.many.fixed.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_vsnitvZr5Ps/Rvj9JX-p77I/AAAAAAAAAB0/LWG_PQVgBQ8/s400/zlibar.opt1.many.fixed.png" alt="" id="BLOGGER_PHOTO_ID_5114115714376658866" border="0" /&gt;&lt;/a&gt;The heaptrc dump:&lt;br /&gt;17446 memory blocks allocated : 439659064/439708048&lt;br /&gt;17446 memory blocks freed     : 439659064/439708048&lt;br /&gt;0 unfreed memory blocks : 0&lt;br /&gt;True heap size : 2326528&lt;br /&gt;True free heap : 2326528&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;We can take some conclusions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The memory usage is almost equal over time (the graph scale does not help much here)&lt;br /&gt;UPDATE: the heaptrc dump shows that the original code really takes more memory.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In the optimized build the memory is allocated in a continuous fashion, always growing. The original build the memory is allocated and freed all over time while still growing in the end. This can lead to more memory fragmentation.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In the optimized build there's not the final peak. This is not really expected since the section of code responsible by the peak was not changed. Some options: 1) the peak exists but valgrind does not detect 2) a bug in the optimized code 3) an unexpected (and good) side effect&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-238172977144606998?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/238172977144606998/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=238172977144606998' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/238172977144606998'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/238172977144606998'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/09/zlibar-memory-behavior-compressing-many.html' title='Zlibar memory behavior compressing many small files'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vsnitvZr5Ps/Rvj9Qn-p78I/AAAAAAAAAB8/TdZoGyN_eoA/s72-c/zlibar.orig.many.fixed.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-1497334892172123418</id><published>2007-09-23T08:28:00.000-07:00</published><updated>2010-07-25T15:44:04.809-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='valgrind'/><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='zlibar'/><title type='text'>Reduce zlibar memory usage - step1</title><content type='html'>Previously we learned that zlibar uses 1.5MB of memory to compress a 1.1MB file.  It compress each file in three passes: 1) loads all the file into memory 2) feed the deflate function with this data using a small buffer as a bridge 3) calculate the md5 signature transversing the file data again.&lt;br /&gt;&lt;br /&gt;The option is to compress using only one pass: load the file data incrementally into a memory buffer and than feed deflate and md5 functions with it.  It has two advantages: the memory usage (in this step) is constrained by the size of the buffer and you save the memory copy from the stream (that holds the file data) to the deflate buffer and to md5 buffer.&lt;br /&gt;&lt;br /&gt;The modified InternalCompressStream function would be something like:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;MD5Init(Context);&lt;br /&gt;z.next_in := @input_buffer;&lt;br /&gt;z.avail_in := FileRead(InStream, input_buffer, MAX_IN_BUF_SIZE);&lt;br /&gt;MD5Update(Context, input_buffer, z.avail_in);&lt;br /&gt;while z.avail_in &gt; 0 do&lt;br /&gt;begin&lt;br /&gt;repeat&lt;br /&gt;z.next_out := @output_buffer;&lt;br /&gt;z.avail_out := MAX_OUT_BUF_SIZE;&lt;br /&gt;err := deflate(z, Z_NO_FLUSH);&lt;br /&gt;OutStream.Write(output_buffer, MAX_OUT_BUF_SIZE - z.avail_out);&lt;br /&gt;until Z.avail_out &gt; 0;&lt;br /&gt;z.next_in := @input_buffer;&lt;br /&gt;z.avail_in := FileRead(InStream, input_buffer, MAX_IN_BUF_SIZE);&lt;br /&gt;MD5Update(Context, input_buffer, z.avail_in);&lt;br /&gt;end;&lt;br /&gt;MD5Final(Context, Result.Md5Sum);&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;Lets run valgrind/massif to see what we got:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vsnitvZr5Ps/RvkAhX-p7-I/AAAAAAAAACM/VbUQ5HdJNm8/s1600-h/zlibar.opt1.fixed.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_vsnitvZr5Ps/RvkAhX-p7-I/AAAAAAAAACM/VbUQ5HdJNm8/s400/zlibar.opt1.fixed.png" alt="" id="BLOGGER_PHOTO_ID_5114119425228402658" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Comparing with the &lt;a href="http://3.bp.blogspot.com/_vsnitvZr5Ps/RvAtOLOE-6I/AAAAAAAAABM/efSK4rE7hzQ/s1600-h/new.png"&gt;previous graph&lt;/a&gt; we notice a great memory usage reduction: from 1.5MB to 0.5MB.&lt;br /&gt;&lt;br /&gt;The heaptrc dump:&lt;br /&gt;55 memory blocks allocated : 2811961/2812056&lt;br /&gt;55 memory blocks freed     : 2811961/2812056&lt;br /&gt;0 unfreed memory blocks : 0&lt;br /&gt;True heap size : 950272&lt;br /&gt;True free heap : 950272&lt;br /&gt;&lt;br /&gt;Let's do a deeper analysis:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The memory used by deflate functions is close to the &lt;a href="http://www.zlib.net/zlib_tech.html"&gt;expected 256kb&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;The pink area is the memory used by the stream that holds the compressed data. It is allocated incrementally so the ascending angle.&lt;/li&gt;&lt;li&gt;There's a peak after the deflate memory is freed and just before the program finishes. This represents the copy from the compressed stream to the output stream that doubles the data in memory. More on this later.&lt;/li&gt;&lt;/ul&gt;Someone may say that load a file using a small buffer is slower than reading all data directly in memory. This is true but it would be reasonable only with small files. In a general usage packer it would be a big limitation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-1497334892172123418?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/1497334892172123418/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=1497334892172123418' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/1497334892172123418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/1497334892172123418'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/09/reduce-zlibar-memory-usage-step1.html' title='Reduce zlibar memory usage - step1'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vsnitvZr5Ps/RvkAhX-p7-I/AAAAAAAAACM/VbUQ5HdJNm8/s72-c/zlibar.opt1.fixed.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-490758127730580038</id><published>2007-09-18T11:23:00.000-07:00</published><updated>2010-07-25T15:44:04.810-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='valgrind'/><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='code optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='zlibar'/><title type='text'>Using Valgrind to profile fpc applications</title><content type='html'>Before starting to optimize is necessary to know beforehand what and where to optimize. It's here that the profiler tools plays a role. &lt;a href="http://valgrind.org/"&gt;Valgrind&lt;/a&gt;, and its brother &lt;a href="http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindIndex"&gt;KCachegrind&lt;/a&gt;, are know unix profiler tools that makes success in the C crowd. Let's see if  is useful to fpc programmers.&lt;br /&gt;&lt;br /&gt;Zlibar is a fpc component that encapsulates the paszlib functions in a programmer friendly way. I use it in the Becape application and for sometime i have a plan to optimize it.&lt;br /&gt;&lt;br /&gt;The heart of the component is the TZlibWriteArchive.CreateArchive method that compress the files (InputFiles) into a stream (OutputStream):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;TmpStream := TMemoryStream.Create;&lt;br /&gt;TmpFile := TMemoryStream.Create;&lt;br /&gt;[..]&lt;br /&gt;for X := 0 to fInputFiles.Count-1 do begin&lt;br /&gt;[..]&lt;br /&gt;TmpFile.LoadFromFile(fInputFiles.FileName[X]);&lt;br /&gt;[..]&lt;br /&gt;FileInfo.CompressedSize := InternalCompressStream(X, TmpFile, TmpStream); //(1)&lt;br /&gt;FileInfo.Md5Sum := StreamMD5(TmpFile);&lt;br /&gt;[..]&lt;br /&gt;end;&lt;br /&gt;WriteHeader(AHeader);&lt;br /&gt;OutStream.CopyFrom(TmpStream, TmpStream.Size);//(2)&lt;br /&gt;[..]&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;It creates two temporary memory streams (TmpFile and TmpStream). TmpFile will hold the uncompressed  data of the file being added. TmpStream will be filled with the compressed data in step (1). The process continues until all files are compressed in the TmpStream.&lt;br /&gt;After that the header is written in the OutputStream and then the compressed data is written in the OutputStream.&lt;br /&gt;&lt;br /&gt;The problems:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The file is stored entirely in memory before is processed. For small files is fine but for larger files it would be problems.&lt;/li&gt;&lt;li&gt;Even for small files the memory of   TmpFile will be reallocated in most of the LoadFromFile calls&lt;/li&gt;&lt;li&gt;After step (2), you will have three streams in the heap: an uncompressed file, the compressed data of all files and a header + the compressed data of all files&lt;/li&gt;&lt;/ol&gt;To see this in action i created a small application that just compress only file (the VirtualTrees.pas with 1.1MB), and compiled with -gv (Generate code for Valgrind) and -gl. Run with valgrind(callgrind):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;valgrind --tool=callgrind ./zlibar_opt&lt;/blockquote&gt;It was created a file with the pattern callgrind.out.[pid] that i loaded in KCacheGrind. In this tool is possible to see most of the function calls the application did, the times that each function were called, who called who, and the time each function spent.&lt;br /&gt;To my surprise the memory allocation routines does not spent much time (in fact was zero). The most expensive was, of course, the compression related functions.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vsnitvZr5Ps/RvAuMrOE-7I/AAAAAAAAABU/R6XUXoIZ5Ug/s1600-h/KCachegrind.png"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_vsnitvZr5Ps/RvAuMrOE-7I/AAAAAAAAABU/R6XUXoIZ5Ug/s400/KCachegrind.png" alt="" id="BLOGGER_PHOTO_ID_5111636372360592306" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now let's use the massif tool:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;valgrind --tool=massif ./zlibar_opt&lt;/blockquote&gt;It creates two files: massif.[pid].ps and massif.[pid].txt. The txt file contains info about the callstack and how much memory each function allocated. The ps file contains a graphic showing  the functions that were responsible for most memory allocation and the evolution in time. See below:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vsnitvZr5Ps/Rvj-2X-p79I/AAAAAAAAACE/cyFC2sxa0EE/s1600-h/zlibar.orig.fixed.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_vsnitvZr5Ps/Rvj-2X-p79I/AAAAAAAAACE/cyFC2sxa0EE/s400/zlibar.orig.fixed.png" alt="" id="BLOGGER_PHOTO_ID_5114117586982399954" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;strike&gt;Now you say "what hell is this"?&lt;br /&gt;To work with valgrind the -gv option forces the use of the cmem memory manager which is a wrapper around malloc, so massif understand the cmem* functions as the programmers allocation routines. I tried to use the &lt;span style="font-style: italic;"&gt;fn-alloc&lt;/span&gt; option to force the display of the pascal functions without success.&lt;/strike&gt;&lt;br /&gt;Update: i was passing only the pascal name function (CMEM_GETMEM) to fn-alloc while is necessary also the parameter list names. I updated the charts to show where in pascal the memory is allocated.&lt;br /&gt;&lt;br /&gt;Anyway in the graphic we can see that 1.5MB of heap memory is allocated (used?*) at its peak and that is the point where we can optimize.&lt;br /&gt;&lt;br /&gt;Here's the heaptrc dump:&lt;br /&gt;58 memory blocks allocated : 3926105/3926208&lt;br /&gt;58 memory blocks freed     : 3926105/3926208&lt;br /&gt;0 unfreed memory blocks : 0&lt;br /&gt;True heap size : 950272&lt;br /&gt;True free heap : 950272&lt;br /&gt;&lt;br /&gt;In the next articles, i will take a look in the possible optimizations.&lt;br /&gt;&lt;br /&gt;Notes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;* The fpc heap manager pre allocates space in the heap that sometimes is not all used but i dont know if this is still valid when using the cmem functions&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The -gv option is necessary to run the massif tool but is dispensable when using the callgrind&lt;/li&gt;&lt;li&gt;The valgrind checkmem tool is of little utility to fpc since it provides the heaptrc unit with a lot of advantages&lt;/li&gt;&lt;li&gt;One drawback of valgrind is that is exclusive to unix. No windows.&lt;/li&gt;&lt;li&gt;For more info see the valgrind &lt;a href="http://valgrind.org/docs/manual/manual.html"&gt;manual&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-490758127730580038?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/490758127730580038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=490758127730580038' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/490758127730580038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/490758127730580038'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/09/using-valgrind-to-profile-fpc.html' title='Using Valgrind to profile fpc applications'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vsnitvZr5Ps/RvAuMrOE-7I/AAAAAAAAABU/R6XUXoIZ5Ug/s72-c/KCachegrind.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-4741707223660499498</id><published>2007-09-18T11:13:00.000-07:00</published><updated>2007-09-18T11:22:54.633-07:00</updated><title type='text'>Back to blogging and to lower levels</title><content type='html'>This blog has been quiet lately, but is not dead.&lt;br /&gt;&lt;br /&gt;In the last months a played a lot with LCL, sometimes in the VirtualTreeView port, sometimes writing original components. I will take a rest in high level/GUI programming and return to lower levels.&lt;br /&gt;&lt;br /&gt;Starting from today i will post a sequence of articles about memory usage and general optimizations  to fpc/Lazarus applications.&lt;br /&gt;&lt;br /&gt;Later, i plan to talk about file system integration and IPC techniques.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-4741707223660499498?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/4741707223660499498/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=4741707223660499498' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/4741707223660499498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/4741707223660499498'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/09/back-to-blogging-and-to-lower-levels.html' title='Back to blogging and to lower levels'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-1707286570519316586</id><published>2007-03-15T13:09:00.000-07:00</published><updated>2008-10-26T11:50:40.820-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualtreeview'/><title type='text'>... and not alone!</title><content type='html'>After getting VTV to work reasonably well under Windows, i started to port it to Linux/gtk.&lt;br /&gt;&lt;br /&gt;It's already compiling but crashes at start with the following message:&lt;br /&gt;&lt;br /&gt;"BadMatch (invalid parameter attributes)"&lt;br /&gt;&lt;br /&gt;Searching such message in Google i get &lt;span style=""&gt;&lt;b&gt;32.800&lt;/b&gt; &lt;span style="font-size:100%;"&gt;results.&lt;br /&gt;&lt;br /&gt;At least, i'm not alone ;-)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-1707286570519316586?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/1707286570519316586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=1707286570519316586' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/1707286570519316586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/1707286570519316586'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/03/and-not-alone.html' title='... and not alone!'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-5675105690748620888</id><published>2007-03-15T13:08:00.001-07:00</published><updated>2008-12-10T20:02:08.871-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualtreeview'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Not so far ...</title><content type='html'>Before than i think i got VTV to work under Windows (In fact is working since, at least, two weeks).&lt;br /&gt;&lt;br /&gt;Is almost everything there: Header, OLE drag and drop (and VCL too), Unicode, Bidi. Alpha Blend is still missing. There  are also some graphic glitches with the more advanced examples.&lt;br /&gt;&lt;br /&gt;Here's a sample:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vsnitvZr5Ps/Rfm_ik3dT4I/AAAAAAAAABA/o0pWjPtOCvw/s1600-h/vtvdemo.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_vsnitvZr5Ps/Rfm_ik3dT4I/AAAAAAAAABA/o0pWjPtOCvw/s400/vtvdemo.png" alt="" id="BLOGGER_PHOTO_ID_5042271858550722434" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-5675105690748620888?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/5675105690748620888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=5675105690748620888' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/5675105690748620888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/5675105690748620888'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/03/not-so-far.html' title='Not so far ...'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vsnitvZr5Ps/Rfm_ik3dT4I/AAAAAAAAABA/o0pWjPtOCvw/s72-c/vtvdemo.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-1736749641305028179</id><published>2007-02-15T10:54:00.000-08:00</published><updated>2007-02-18T15:30:44.965-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualtreeview'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Traps of Delphi to Lazarus conversion</title><content type='html'>There's only a thing worse than a bug difficult to resolve: a bug difficult to find. As an example, i was getting a weird bug in the drawing of VTV. The colors were being painted wrongly and only after a few hours i figured what was going on. See the code below:&lt;br /&gt;&lt;br /&gt; with Canvas do&lt;br /&gt; begin&lt;br /&gt;   [..]&lt;br /&gt;   Brush.Color:=Color;&lt;br /&gt;   [..]&lt;br /&gt; end;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Apparently nothing wrong. But the Devil resides in the details. Under Delphi there's no TCanvas.Color while there's in LCL. The result of this code, in Delphi, is that Brush.Color is assigned to  TControl.Color. In LCL, Brush.Color is assigned to TCanvas.Color (which in your turn points back to Brush.Color!!).&lt;br /&gt;&lt;br /&gt;You can't even say that there's a bug in LCL or in the program. Is just one of traps found while converting Delphi code.&lt;br /&gt;&lt;br /&gt;The same occurs with TCanvas.Width/Height (does not exist in Delphi) and TBitmap.Width/Height when used inside compound "with" statements.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-1736749641305028179?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/1736749641305028179/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=1736749641305028179' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/1736749641305028179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/1736749641305028179'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/02/traps-of-delphi-to-lazarus-conversion.html' title='Traps of Delphi to Lazarus conversion'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-2088826599429241120</id><published>2007-02-12T03:34:00.000-08:00</published><updated>2007-02-18T15:31:17.008-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualtreeview'/><category scheme='http://www.blogger.com/atom/ns#' term='atbinhex'/><title type='text'>Children of the port</title><content type='html'>A working new VirtualTreeview is far from reality but there are already some results.&lt;br /&gt;&lt;br /&gt;Since this is really a complex software, a lot of debugging will be necessary so i needed to extend the Multilog component/package. I just released a new version. But one of the functions i added was the ability to log memory areas and i needed a component to display it. This way i ended porting ATBinHex (in fact is a grandchild of the port ;-)).&lt;br /&gt;&lt;br /&gt;As a bonus i implemented TOleStream that was missing from Delphi.&lt;br /&gt;&lt;br /&gt;You can find them at https://luipack.bountysource.com/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-2088826599429241120?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/2088826599429241120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=2088826599429241120' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/2088826599429241120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/2088826599429241120'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/02/children-of-port.html' title='Children of the port'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-830647526149070133</id><published>2007-02-03T12:22:00.000-08:00</published><updated>2007-02-18T15:32:36.927-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualtreeview'/><category scheme='http://www.blogger.com/atom/ns#' term='atbinhex'/><title type='text'>Ports of Delphi components</title><content type='html'>Last week i started to port two Delphi components: the already know VirtualTreeView and &lt;a href="http://atorg.net.ru/delphi/index.htm"&gt;ATBinHex&lt;/a&gt; a Hex viewer. At the moment VirtualTreeView is compilable but does not work and ATBinHex works under Windows only. Both code can be get in &lt;a href="https://luipack.bountysource.com/"&gt;LuiPack&lt;/a&gt;, a project i started to centralize  and help keep track the components i develop.&lt;br /&gt;&lt;br /&gt;Before someone would ask: "Why do a new port of VirtualTreeView if already exists one?", here are the answers:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The original port of VirtualTreeView (VTV) was based in version 4.0.17 from December 2003. Currently we are in version 4.5.1. A lot of changes happened after that. One option was to see what changed and than backport to the lazarus version. But two problems arise: At the time of 4.0.17 the VTV code was not in a version control system making hard to know what changed. Furthemore, the chances that applying these changes would conflict with the modifications done in lazarus port are high.&lt;br /&gt;So this time, the VTV code is in a svn server being easy to see what changed and than backport small modifications to lazarus if appropriate.&lt;/li&gt;&lt;li&gt;Some features were removed in the port process (CheckBox images, AlphaBlending, Bidi support) and other bugs introduced. To completelly fix them and add the missing features would require deep knowledge of how VTV works and the success was not guaranteed. So i believe the work to do a new port would be only a more hard than to fix it.&lt;/li&gt;&lt;li&gt;In that time LCL/fpc was far from what is now. This allow to do a easier port without the need to remove features. So, instead of removing features the idea is to fix the LCL, where appropriate or redo with LCL/fpc functions.&lt;/li&gt;&lt;/ul&gt;PS: Don't expect a working VTV soon, because of the decision to not remove features will lead to a long road.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-830647526149070133?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/830647526149070133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=830647526149070133' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/830647526149070133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/830647526149070133'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2007/02/ports-of-delphi-components.html' title='Ports of Delphi components'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-4224022406052836379</id><published>2006-12-29T03:50:00.000-08:00</published><updated>2006-12-29T04:04:25.006-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><title type='text'>It's Open Source stupid!</title><content type='html'>In the previous post i badly evaluated the gtk api, but in one thing it is far ahead from win32: it's open source.&lt;br /&gt;To fix &lt;a href="http://www.freepascal.org/mantis/view.php?id=1428"&gt;this&lt;/a&gt; bug i just looked in the gtk source the routine that insert items in a listbox (See gtk_list_insert_items in &lt;a href="ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.9.tar.gz"&gt;gtklist.c&lt;/a&gt;). Easy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-4224022406052836379?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/4224022406052836379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=4224022406052836379' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/4224022406052836379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/4224022406052836379'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2006/12/its-open-source-stupid.html' title='It&apos;s Open Source stupid!'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-7624607087729474591</id><published>2006-12-27T11:22:00.000-08:00</published><updated>2008-12-10T20:02:09.593-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='multilog'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Eliminating duplicate events of TComboBox and TListBox</title><content type='html'>One year ago i posted a &lt;a href="http://www.freepascal.org/mantis/view.php?id=1495"&gt;bug&lt;/a&gt; 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.&lt;br /&gt;To help me find where the problem was i used the &lt;a href="http://wiki.lazarus.freepascal.org/MultiLog"&gt;MultiLog,&lt;/a&gt; a logging component useful to debug gui apps.&lt;br /&gt;&lt;br /&gt;The diagnostic: i wrote a small app that has a combobox with the &lt;span style="font-weight: bold;"&gt;OnChange&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;OnSelect&lt;/span&gt; events set. After selecting an item in the dropdown list two events are fired: first &lt;span style="font-weight: bold;"&gt;OnSelect&lt;/span&gt; and than &lt;span style="font-weight: bold;"&gt;OnChange&lt;/span&gt;. I put a call to &lt;span style="font-weight: bold;"&gt;Logger.SendCallstack&lt;/span&gt; in both events handler and i got from where the events were being called:&lt;br /&gt;&lt;br /&gt;- [internal gtk calls] &gt; GtkListSelectChild &gt; DeliverMessage &gt; LMSelChange&lt;br /&gt;- [internal gtk calls] &gt; GtkChangedCB &gt; DeliverMessage &gt; LMChanged&lt;br /&gt;&lt;br /&gt;Than i figured that &lt;span style="font-weight: bold;"&gt;GtkListSelectChild&lt;/span&gt; is attached to the 'unselect_child' signal of the TGtkCombo.list handle (the dropdown list) and that &lt;span style="font-weight: bold;"&gt;GtkChangedCB&lt;/span&gt; is attached to the 'changed' signal of the TGtkCombo.entry handle (the edit box).&lt;br /&gt;&lt;br /&gt;The next step was to see what is going on there. After reading the gtk 1.2 &lt;a href="http://developer.gnome.org/doc/API/gtk/gtk-signals.html"&gt;reference&lt;/a&gt;, i put log messages in &lt;span style="font-weight: bold;"&gt;GtkChangedCB&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;GtkListSelectChild &lt;/span&gt;to get the &lt;span style="font-weight: bold;"&gt;gtk_signal_n_emissions&lt;/span&gt; for both. The idea was that i could break the &lt;span style="font-weight: bold;"&gt;OnChange&lt;/span&gt; firing using &lt;span style="font-weight: bold;"&gt;gtk_signal_emit_stop&lt;/span&gt; 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 &lt;span style="font-weight: bold;"&gt;GtkListSelectChild&lt;/span&gt;.&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vsnitvZr5Ps/RZLQzKREI6I/AAAAAAAAAAc/5zgTFIrf1Hs/s1600-h/emissions1-2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_vsnitvZr5Ps/RZLQzKREI6I/AAAAAAAAAAc/5zgTFIrf1Hs/s320/emissions1-2.png" alt="" id="BLOGGER_PHOTO_ID_5013298912564618146" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:78%;"&gt;Figure 1&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;The things was not going as planned and i had the idea of connecting to the 'select_child' signal (i used a function called &lt;span style="font-weight: bold;"&gt;GtkListSelectChildNew&lt;/span&gt;). 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 &lt;span style="font-weight: bold;"&gt;LockOnChange&lt;/span&gt; procedure to avoid its execution.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vsnitvZr5Ps/RZLR9aREI7I/AAAAAAAAAAk/UzdoI0bPAOI/s1600-h/emissions2-2.png"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_vsnitvZr5Ps/RZLR9aREI7I/AAAAAAAAAAk/UzdoI0bPAOI/s400/emissions2-2.png" alt="" id="BLOGGER_PHOTO_ID_5013300188169905074" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:78%;"&gt;Figure 2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So i incremented the &lt;span style="font-weight: bold;"&gt;LockOnChange&lt;/span&gt; in the first &lt;span style="font-weight: bold;"&gt;GtkListSelectChildNew&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;/span&gt;and.... Nothing!!. This strategy did not prevent the message deliver in &lt;span style="font-weight: bold;"&gt;GtkChangedCB&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;From this point everything got easier. The 'changed' signal is emitted before 'unselect_child' (see Figure 3) so the &lt;span style="font-weight: bold;"&gt;gtk_signal_emit_stop&lt;/span&gt;  (using the correct handle) called inside &lt;span style="font-weight: bold;"&gt;GtkListSelectChild&lt;/span&gt; prevents the &lt;span style="font-weight: bold;"&gt;GtkChangedCB&lt;/span&gt; to be fired. My initial idea was correct, only not properly implemented!.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vsnitvZr5Ps/RZLUPaREI8I/AAAAAAAAAA0/FuWjv2pkUEU/s1600-h/emiisions3-2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_vsnitvZr5Ps/RZLUPaREI8I/AAAAAAAAAA0/FuWjv2pkUEU/s400/emiisions3-2.png" alt="" id="BLOGGER_PHOTO_ID_5013302696430805954" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:78%;"&gt;Figure 3&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;But is not over yet. After stopping in &lt;span style="font-weight: bold;"&gt;GtkSelectChild&lt;/span&gt; the 'changed' signal, if i click in the dropdown list only &lt;span style="font-weight: bold;"&gt;OnSelect&lt;/span&gt; is called, as desired,  but the first change in edit box calls &lt;span style="font-weight: bold;"&gt;OnSelect&lt;/span&gt; ('unselect_child' signal) instead of &lt;span style="font-weight: bold;"&gt;OnChange&lt;/span&gt; ('changed' signal), the subsequent changes in the edit box call &lt;span style="font-weight: bold;"&gt;OnChange&lt;/span&gt; correctly.&lt;br /&gt;&lt;br /&gt;No panic!. I noticed that the 'select_child' is not fired in this case, so i could use&lt;br /&gt;it instead of 'unselect_child'. But this brings another problem: 'select_child' is fired twice. But nothing that some refactoring can not resolve.&lt;br /&gt;&lt;br /&gt;The final schema i got:&lt;br /&gt;- &lt;span style="font-weight: bold;"&gt;GtkListUnselectChild&lt;/span&gt; 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.&lt;br /&gt;- &lt;span style="font-weight: bold;"&gt;GtkListSelectChild&lt;/span&gt; is connected to 'select_child' signal. The previous routine guaranties that is called once. The message is delivered to the LCL&lt;br /&gt;- No changes to &lt;span style="font-weight: bold;"&gt;GtkChangedCB&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I fixed the &lt;a href="http://www.freepascal.org/mantis/view.php?id=1427"&gt;problem&lt;/a&gt; of &lt;span style="font-weight: bold;"&gt;TListBox.OnSelectionChange&lt;/span&gt; with similar approach.&lt;br /&gt;&lt;br /&gt;Now it's time to do some more testing, clear and comment the code so i can send a patch.&lt;br /&gt;&lt;br /&gt;The programs i used to do the tests can be found &lt;a href="http://www.geocities.com/camara_luiz/listitem.zip"&gt;here&lt;/a&gt; and &lt;a href="http://www.geocities.com/camara_luiz/comoboitem.zip"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;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 ;-) .&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-7624607087729474591?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/7624607087729474591/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=7624607087729474591' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7624607087729474591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/7624607087729474591'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2006/12/eliminating-duplicate-events-of.html' title='Eliminating duplicate events of TComboBox and TListBox'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vsnitvZr5Ps/RZLQzKREI6I/AAAAAAAAAAc/5zgTFIrf1Hs/s72-c/emissions1-2.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-4440878807079680841</id><published>2006-12-16T16:13:00.000-08:00</published><updated>2006-12-28T12:14:31.676-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fpc'/><category scheme='http://www.blogger.com/atom/ns#' term='uniqueinstance'/><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Only one instance</title><content type='html'>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?&lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://www.wilsonc.demon.co.uk/delphi.htm"&gt;TRunOnce&lt;/a&gt; and &lt;a href="http://delphi.about.com/library/weekly/aa110203a.htm"&gt;TInstanceControl&lt;/a&gt;. 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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The next step was a little more difficult: how to pass the command line arguments to the second instance. I had 3 options:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;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&lt;/li&gt;&lt;li&gt;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.&lt;/li&gt;&lt;li&gt;Create an easily parsable string with the parameters in the client side and then send once. The chosen one.      &lt;/li&gt;&lt;/ul&gt;To the tests. Aside from a bug of the style &lt;span style="font-style: italic;"&gt;for x:=1 to y do&lt;/span&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;;  &lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;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 &lt;span style="font-style: italic;"&gt;voila&lt;/span&gt;: no flashing form.&lt;br /&gt;&lt;br /&gt;The results can be found &lt;a href="http://wiki.lazarus.freepascal.org/UniqueInstance"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;PS: more difficult than write the component is creating a icon for it. And the result... LOL&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-4440878807079680841?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/4440878807079680841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=4440878807079680841' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/4440878807079680841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/4440878807079680841'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2006/12/only-one-instance.html' title='Only one instance'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-116618020517114147</id><published>2006-12-15T02:49:00.000-08:00</published><updated>2006-12-27T12:43:06.678-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lazarus'/><title type='text'>Hiding a TNotebook tab</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The test program and the patch can be found in the &lt;a href="http://www.freepascal.org/mantis/view.php?id=7349"&gt;bugreport&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The patch also does some cleanup in the code:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;TCustomPage.SetParent set TabVisible to false before calling RemovePage. I removed the TabVisible assignment since RemovePage already does the job.&lt;/li&gt;&lt;li&gt;In GetActivePageComponent and GetActivePage i changed from PageIndex to fPageIndex (Notice that in other functions fPageIndex is already used)&lt;/li&gt;&lt;/ul&gt;PS: I tested in win32 but notice that the modifications are done only in LCL level so should not be problems with other interfaces.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-116618020517114147?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/116618020517114147/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=116618020517114147' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/116618020517114147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/116618020517114147'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2006/12/hiding-atnotebook-tab.html' title='Hiding a TNotebook tab'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-38057291.post-116617967867522171</id><published>2006-12-15T02:42:00.000-08:00</published><updated>2006-12-15T02:47:58.683-08:00</updated><title type='text'>Start (or better: begin)</title><content type='html'>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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/38057291-116617967867522171?l=lazarusroad.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazarusroad.blogspot.com/feeds/116617967867522171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=38057291&amp;postID=116617967867522171' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/116617967867522171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/38057291/posts/default/116617967867522171'/><link rel='alternate' type='text/html' href='http://lazarusroad.blogspot.com/2006/12/start-or-better-begin.html' title='Start (or better: begin)'/><author><name>Luiz Américo</name><uri>http://www.blogger.com/profile/10187299451635803731</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_vsnitvZr5Ps/S3P_bAmu4NI/AAAAAAAAAEM/2YoEaSFx7dU/S220/outback_crop2.png'/></author><thr:total>3</thr:total></entry></feed>
