Firebird News: Android port of the Firebird Jdbc driver (Jaybird) 2.2.2 is released
The Wiert Corner - irregular stream of stuff: jpluimers
I mailed the DelphiFeeds people to add the MonkeyStyler Blog by Mike Sutton.
It is a nice Delphi related blog focussing on FireMonkey stuff.
–jeroen
Filed under: Delphi, Delphi 1, Delphi 2005, Delphi 2006, Delphi 2007, Delphi 2009, Delphi 2010, Delphi 3, Delphi 4, Delphi 5, Delphi 6, Delphi 7, Delphi 8, Delphi x64, Delphi XE, Delphi XE2, Delphi XE3, Development, FireMonkey, OS X FMX, Software Development
Smart Mobile StudioSmart Mobile Studio: Smart Mobile Studio 1.1 RC (build 1.1.0.400)
Smart Mobile StudioSmart Mobile Studio: Smart Contest 2013 – Round #1
Delphi Haven: XE3 and Subversion/Google Code
Just a small heads up, but it has come to my attention that the XE3 IDE’s Subversion (SVN) integration does not support the older, pre-v1.7 Subversion format, at least out of the box. While there are interop issues with recent versions of TortoiseSVN (the Subversion client I actually use) too, the XE3 IDE goes a step further by just hanging when the ‘Open From Version Control’ command is used against a repository in the older format.
While this in practice won’t bother many people, Google Code (a free-as-in-beer way to publish open source code) still uses Subversion v1.6… which means anything Delphi-related there whose author chose Subversion precisely because the Delphi IDE provides SVN support in the box may now look slightly foolish… Anyhow, this is all just a slightly roundabout way of saying I’ve added a ZIP of the sample code for my XE2 book here!
[Edit - my initial post was overly negative about TortoiseSVN (thanks to Robert Love and M J Marshall for correcting me in the comments). The issue is that TortoiseSVN v1.7x forces you to upgrade working copies from v1.6x to v1.7x, which means you can't use both it and the XE or XE2 IDE interchangeably, assuming you haven't modified the IDE's behaviour to use the newer SVN DLLs. However, if you only use TortoiseSVN as your client, it can happily work with v1.6x servers. This though gives even less reason for the XE3 IDE to just hang!]
The Wiert Corner - irregular stream of stuff: jpluimers
Thanks Lennart Aasenden for sharing this on FaceBook: Mariuz’s Blog: Adobe Photoshop 1.0 Source Code About 75% is in Pascal.
This was back when I was already a professional Turbo Pascal for PC programmer, not yet a Mac programmer, but doing Pascal on VMS to assist a client in the scaleable font industry.
The code was written in Object Pascal, and based on MacApp.
Back then Apple’s Object Pascal was one of the few IDEs available to develop Macintosh software. Later on, you also had Turbo Pascal and THINK Pascal (which many Macintosh developers preferred, was later acquired by Symantec, and died). A big reason they liked it so much was the THINK integrated debugger, which was lightyears ahead of any Pascal product on any other platform.
Apple had great documentation, not only on their compilers and libraries, but also one that everyone should hav read: Apple Human Interface Guidelines: The Apple Desktop Interface: Inc. Apple Computer: 9780201177534: Amazon.com: Books.
The Adobe Photoshop 1.0 source code can be downloaded from the Computer History Museum | @CHM : Adobe Photoshop Source Code page.
The source is a very interesting read, and a great comments on it by Grady Booch.
This is how everyone should think about their code.
–jeroen
PS: A nice introduction to Object Pascal for a Macintosh is at MacTech | The journal of Apple technology..
Filed under: Delphi, Development, Object Pascal, Pascal, Software Development, Think Pascal Tagged: apple technology, computer, computer history museum, desktop interface, human interface guidelines, mac programmer, macintosh developers, object pascal, software, technology
twm’s blog: 3.5 gigabyte text file - meet LargeTextViewer
Recently a customer sent me an XML file which was 3.5 gigabyte in size. I had to parse this file and met some new challenges.
The first one was that I did not have any tool to display a file that large. All text editors balked at it and the few viewers (for Windows) that claimed they could handle large files I found on the Internet were slow like dogs or very difficult to use (does nobody teach those kids user interface design nowadays?). I made do with the Linux less command for a while but always putting the file(s) on the server, ssh’ing into it just to display it was an annoyance in itself.
So I ended up rolling my own. It’s of course written in Delphi and needed only a few lines of code. It relies heavily on my TdzVirtualStringGrid component to only keep that part of the file in memory that is currently being displayed. It immediately displays the first few lines of the file and in the background it reads through the file to find CR/LF characters and creates a list of Int64 values with the stream offsets of every single line in the file. While indexing you can only scroll down up to the point that has been indexed but that’s the only restriction. For the above mentioned 3.5 gigabyte file indexing takes around 30 seconds on my computer.
Apart from displaying the file, it does nothing. There is no search function and it even does not display line numbers. But I thought it might be useful to somebody else, so I put it into my dzlib svn repository on sourceforge. You can find it in the subdirectory tools. so after I received some patches from Daniela Osterhagen for it I put up a sourceforge project for it. There is also a downloadable executable now.
Firebird News: SplendidCRM 7 on FirebirdSQL demo pages and changes
Delphi Haven: Annoying FireMonkey buglet/oversight of the week
My personal FireMonkey buglet/oversight of the week is this: setting a TTextControl descendant’s Text property inside its constructor doesn’t do anything. For those who don’t know, TTextControl is the base class for things like TLabel, TListBoxItem and TExpander in FMX. Create custom descendants of these, then, and the following sort of code will not work:
type TMyExpander = class(TExpander) public constructor Create(const AOwner: TComponent; const AText: string); reintroduce; end; constructor TMyExpander.Create(const AOwner: TComponent; const AText: string); begin inherited Create(AOwner); Text := AText; end;
The reason is quite simple: TTextControl sets a flag in Create that prevents changes to the Text property doing anything until that flag is reset in an override of AfterConstruction. In true ‘FM squared’ fashion, the code is nevertheless convoluted enough to give you RSI from having to press F7 so much in trying to track this down. Argh!!!!
Delphi Haven: Writing a simple FireMonkey TListLayout implementation
In FireMonkey, the usual way to group controls is to use a ‘layout’ of some sort. Conceptually, a layout is just a container control with no visual appearance of its own, at least by default. This contrasts to something like a panel or group box, which is also a container but one that the user sees as such.
A few layout classes are provided with Delphi. The simplest is TLayout, which does nothing more than implement the basic defintion of a layout control; beyond it stands TScrollBox, TScaleLayout, TGridLayout and TFlowLayout. You can read about them in the help, though in a nutshell, they do the following -
- TScrollBox shows scroll bars as its contents extend beyond its own visible client area (the scroll bars are auto-hidden if not needed)
- TScaleLayout resizes its controls as it itself is resized
- TGridLayout positions and sizes its constituent controls in fixed-sized cells
- TFlowLayout positions controls like the words in a paragraph, first left to right (or right to left via a property setting), then top to bottom. Controls are then moved accordingly when the layout is resized.
While it isn’t a layout control strictly speaking, an honorary mention also goes to the FireMonkey TListBox, whose fixed-width items can host child controls.
That said, I was recently wanting a layout object to organise controls top aligned from from top to bottom. Unfortunately, none of the standard layout classes met my requirements, which were as thus:
- Each control should fill the parent’s client width, unless the standard Paddings and Margins properties indicate otherwise.
- However, a control’s height should be specific to the control.
- The first control would be located at the top of the parent, the second immediately below the first (perhaps with a standard gap), the third immediately below the second and so on.
- It should be easy to change the order of controls.
- Ideally a vertical scroll bar should show if the controls cannot fit.
Requirement (1) ruled out TFlowLayout, number (2) ruled out TListBox and TGridLayout, none of the requirements made TScaleLayout relevant, and only (5) could be serviced by TScrollBox. Writing a custom layout class for the task proved pretty easy however:
- Create a new package project, and set its ‘description’ to something appropriate under Project|Options, Description (e.g., ‘List Layout Control’).
- Add a new unit to the package, and add System.SysUtils, System.Classes and FMX.Types to the unit’s interface section uses clause.
- Following TFlowLayout and its peers, declare a class descending from TControl. Publish the usual FMX control properties inherited from (but not published by) the base class.
- Following TFlowLayout again, add a new published property called VerticalSpacing, typed to Single, and add overrides for the DoAddObject, DoInsertObject and DoRealign protected methods.
- Declare the usual Register procedure needed for registering a custom component with the IDE.
Following this, the unit’s interface section should look like this. I’ve also added a ComponentPlatforms attribute to say the class supports Windows and OS X (‘any’ target would be more exact to be honest):
uses System.SysUtils, System.Classes, FMX.Types; type [ComponentPlatforms(pidWin32 or pidWin64 or pidOSX32)] TListLayout = class(TControl) strict private FVerticalGap: Single; procedure SetVerticalGap(const Value: Single); protected procedure DoRealign; override; procedure DoAddObject(AObject: TFmxObject); override; procedure DoRemoveObject(AObject: TFmxObject); override; published property Align; property Anchors; property ClipChildren; property ClipParent; property Cursor; property DesignVisible; property DragMode; property EnableDragHighlight; property Enabled; property Locked; property Height; property HitTest; property Margins; property Opacity; property Padding; property PopupMenu; property Position; property RotationAngle; property RotationCenter; property Scale; property TouchTargetExpansion; property VerticalGap: Single read FVerticalGap write SetVerticalGap; property Visible; property Width; property OnApplyStyleLookup; property OnDragEnter; property OnDragLeave; property OnDragOver; property OnDragDrop; property OnDragEnd; property OnClick; property OnDblClick; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnMouseWheel; property OnMouseEnter; property OnMouseLeave; property OnPainting; property OnPaint; property OnResize; end; procedure Register;
The implementation of Register is as you would expect if you’ve ever written a custom VCL control, namely a simple call to RegisterComponents. DoAddObject and DoInsertObject then just call the inherited implementation before requesting the control realigns its children. Lastly, the VerticalGap property setter assigns the backing field before requesting a realignment too:
procedure Register; begin RegisterComponents('Samples', [TListLayout]); end; { TListLayout } procedure TListLayout.DoAddObject(AObject: TFmxObject); begin inherited; Realign; end; procedure TListLayout.DoRemoveObject(AObject: TFmxObject); begin inherited; Realign; end; procedure TListLayout.SetVerticalGap(const Value: Single); begin if Value = FVerticalGap then Exit; FVerticalGap := Value; Realign; end;
The final thing to implement is the DoRealign override. As a bit of an aside, DoRealign itself embodies the XE2 to XE3 FireMonkey transition (a lot of good work done, but a lot still to complete) in microcosm: in XE2, there was just Realign, which was a public, virtual method. As implemented in TControl, it did a whole load of checks to see whether controls should be realigned before finally doing the actual realigning. This was bad design, since if you wished to customise how realignment is performed in a descendant of TControl, you had to duplicate all those initial checks in your Realign override. In XE3, in contrast, Realign has been devirtualised and instead paired with a virtual, protected DoRealign method. In the new scheme, Realign still performs all the initial checks it did before, however it then delegates to DoRealign to do the actual repositioning and resizing. All well and good, but the refactoring wasn’t quite finished – to prevent the possibility of recursive calls to Realign/DoRealign, DoRealign still needs to set a protected FDisableRealign field to True, then reset it to False once it has finished. Really Realign should do that for you though, wrapping the FDisableAlign assignments in a try/finally block – if that were done, FDisableAlign could then be withdrawn into strict private scope. Nonetheless, it’s not a big issue.
Anyhow, here’s what my DoRealign implementation looks like:
procedure TListLayout.DoRealign; var Control: TControl; NextY, StdWidth: Single; begin if ControlsCount = 0 then Exit; FDisableAlign := True; try NextY := Margins.Top; StdWidth := Width - Margins.Left - Margins.Right; for Control in Controls do if Control.Visible then begin NextY := NextY + Control.Padding.Top; Control.SetBounds(Margins.Left + Control.Padding.Left, NextY, StdWidth - Control.Padding.Right - Control.Padding.Left, Control.Height); NextY := NextY + Control.Height + Control.Padding.Bottom + VerticalGap; end; finally FDisableAlign := False; end; end;
If you’re following along, save everything, switch to the Release build configuration before adding and compiling for the Win64 and OS X target platforms (if you only have the Starter edition, that’s fine, however there won’t be any platforms beyond Win32 to compile for). The first time you compile the package there will be a prompt for adding fmx to the package’s requires clause – accept it. Then, toggle back to the default Win32 target, right click on the BPL’s node in the Project Manager, and choose Install. Finally, for each target platform, add the DCU output folder to the IDE’s search path (Tools|Options, Environment Options -> Delphi Options -> Library, Library Path); if desired, also add the .pas folder to the IDE’s browse path. E.g., if I were to save custom control units under E:\Delphi\Lib, this would give a default DCU output folder for 32 bit Windows of E:\Delphi\Lib\Win32\Release. If you toggle the project’s build configuration for Debug and recompile, you can also add the ..\Debug folders to the debug DCU search path as well. [In case it weren't obvious, these instructions are in case you aren't familiar with how to manually install a custom control - FMX or VCL - in the IDE. If you are, then there's nothing particular to my example control, or shouldn't be.] If all goes well, TListLayout should now be available in the Tool Palette when designing a form.
Now, the DoRealign implementation explicitly fulfils requirements (1) to (3) in my original list. Requirement (4) is also implicitly met, since our DoRealign lays out controls in the order they appear in the Controls array property, and that order can be changed by setting a sub-control’s Index as desired. So, if MyPanel is at the bottom of the layout control, setting its Index to 0 will move it to the top. Requirement (5) can then be met simply by nesting the TListLayout inside a TScrollBox, and setting its Align property to alTop:
Here, the form has a top-aligned TToolbar (StyleLookup set to ‘HeaderItemStyle’, as the default toolbar style looks pretty ugly IMO!), followed by a client-aligned TScrollbox with its Padding set to (2, 2, 2, 2). This then contains a top-aligned TListLayout with VerticalSpacing set to 4, with the layout itself containing three top-aligned panels, each of which has its Margins set to (8, 8, 8, 8), a left-aligned TLabel added and a client-aligned TEdit too.
Firebird News: New version of gbak scheduler
DelphiTools.info: Delphi array constructors performance (or lack of)
In Delphi you can initialize a dynamic array in two ways, either manually or via the Create magic constructor:
type TIntegerArray = arrayof Integer; procedure Test; var a : TIntegerArray; begin // magic constructor a := TIntegerArray.Create(1, 2); // manual creation SetLength(a, 2); a[0] := 1; a[1] := 2; end;
The outcome in both cases is the same, are all things equal?
Some array initializations are more equal than others
The first method is less verbose in code, but quite a bit less efficient, if you check the CPU view, that becomes obvious
TestUnit.pas.32: a := TIntegerArray.Create(1, 2); 00511335 8D45F8 lea eax,[ebp-$08] 00511338 8B15F0125100 mov edx,[$005112f0] 0051133E E89576EFFF call @DynArrayClear // anybody knows why? 00511343 6A02 push $02 00511345 8D45F8 lea eax,[ebp-$08] 00511348 B901000000 mov ecx,$00000001 0051134D 8B15F0125100 mov edx,[$005112f0] 00511353 E87476EFFF call @DynArraySetLength 00511358 83C404 add esp,$04 0051135B 8B45F8 mov eax,[ebp-$08] 0051135E C70001000000 mov [eax],$00000001 00511364 8B45F8 mov eax,[ebp-$08] 00511367 C7400402000000 mov [eax+$04],$00000002 0051136E 8B55F8 mov edx,[ebp-$08] 00511371 8D45FC lea eax,[ebp-$04] 00511374 8B0DF0125100 mov ecx,[$005112f0] 0051137A E89576EFFF call @DynArrayAsg // Manual initialization TestUnit.pas.35: SetLength(a, 2); 0051137F 6A02 push $02 00511381 8D45FC lea eax,[ebp-$04] 00511384 B901000000 mov ecx,$00000001 00511389 8B15F0125100 mov edx,[$005112f0] 0051138F E83876EFFF call @DynArraySetLength 00511394 83C404 add esp,$04 TestUnit.pas.36: a[0] := 1; 00511397 8B45FC mov eax,[ebp-$04] 0051139A C70001000000 mov [eax],$00000001 TestUnit.pas.37: a[1] := 2; 005113A0 8B45FC mov eax,[ebp-$04] 005113A3 C7400402000000 mov [eax+$04],$00000002
Now before you complain on the compiler capability, you’ve got to realize that the two ways of initializing a dynamic arrays are not equivalent:
- the magic constructor creates an array, then assigns it, so the array variable is always in a well-defined state
- the manual initialization mutates the array in several steps, so the array during the intermediate state is in an unfinished step
Of course, in the limited Test procedure, the compiler could figure out the array isn’t visible from the outside, and thus use the shorter form, but that’s an optimization that would apply only to a local variables.
A more generic optimization would be to have the compiler waive the temporary array when the array that is initialized isn’t referenced anywhere else (so intermediate states don’t matter), that’s possible given that dynamic arrays are reference-counted.
Overhead in detail
The final outcome is that using the Create magic constructor can incur quite a bit of overhead:
- a DynArrayClear call (not sure why it’s there), that will release the previously assigned block of memory for the temporary array
- a DynArraySetLength, that will allocate a new block of memory and zero it
- a DynArrayAssign, that will trigger the release of the memory for the existing array (if it wasn’t empty), along with a bus lock for the reference count overhead
- extra initialization and finalization for the temporary array
In a multi-threaded applications, all that extra memory management and bus locking is going to have a disproportionate impact on performance. If you test the above snippets in a multi-threaded environment, you’ll notice that when using the array constructor, execution quickly becomes single threaded, bottle-necking on the memory manager and bus locks.
The manual initialization only has a single DynArraySetLength call, and if the array is not empty, this may not result in a new block being allocated (as the existing memory block could just be resized in place). So if you initialize the same array variable more than once, the manual form can be quite cheap.
A better array initializer?
Now that I showed you the magic array Create constructor is no good, what if you still want something convenient? Well open arrays can come to the rescue:
procedure InitializeArray(var a : TIntegerArray; const values : array of Integer); begin SetLength(a, Length(values)); if Length(values)>0 then Move(values[0], a[0], Length(values)*SizeOf(Integer)); end; ... InitializeArray(a, [1, 2]);
The above function won’t be as efficient as manual initialization: there is an extra function call and the values will be copied twice. However it eliminates all the extra memory management and bus locking, so will scale quite better in multi-thread, while being compact syntax and code-wise.
Note that for a managed type (String, Interface…) then System.Move can’t be used, you’ll need to use either asm hackery or a for-to-do loop with item-by-item assignment, which will incur a performance hit, and often make it non-competitive with the manual version.
Need even more speed?
In the grand scheme of things however, all the above approaches suffer from the SetLength call, which is quite complex (have a look at DynArraySetLength in the System.pas unit… and weep), so if you know there is a chance the dynamic array wasn’t resized, in the manual version, you can gain by doing
if Length(a)<>Length(value) then SetLength(a, Length(Values));
Which can when the SetLength is waived, net you more than a mind boggling 10x speedup (ten times).
Ouch! Why doesn’t the RTL do that?
Well, it doesn’t do that because it can’t, as Delphi’s dynamic arrays are not some kind of hybrid half-way between a value type and a reference type, and SetLength is the key stone where all the hybridization happens (for more on the subject, see Dynamic Arrays as Reference or Value Type).
And FWIW, in DWScript, arrays are first-class reference types, which means they can have more capability, and their initialization syntax is also more compact, the above initialization is just:
a := [1, 2];
And if you’re using Smart Pascal and running it in Chrome V8 or node.js, well, let’s just say you’ll need to use all the above tricks for Delphi to come ahead performance-wise.
The road to Delphi: Added Linux support to the TSMBIOS Project
Great news for the Free Pascal developers, I just added Linux support to the TSMBIOS project.
Note : The TSMBIOS read the SMBIOS info using the /dev/mem device file which provides access to system physical memory, so the code must be executed using a user with the proper permissions.
Andreano Lanusse | Technology and Software Development: What does AnyDAC acquisition by Embarcadero mean for Delphi and C++Builder Developers?
Finally the day has come, Embarcadero acquired AnyDAC and there is a lot to talk about, early last year I invested a lot of time evaluating AnyDAC and know you know why – I was very impressive with the amount of features, AnyDAC is everything dbExpress tried to be and never reached.
The reason I say that it’s because AnyDAC really allows developers to target multiples database without the workarounds required by dbExpress, like: Field Mapping when you persist the TFields. For long time AnyDAC allowed developers that use Delphi/C++Builder Professional edition to connect their application to Enterprise databases like Oracle, SQL Server, Sybase, Informix and others, something that was only possible through Enterprise and Architect edition, and much more.
Beyond that, there is a lot of specific database features supported by AnyDAC, which I consider very helpful in any database development, here some examples per database:
- For all databases
- Automatic Connection Recovery
- Unified DB Events Support
- Simplified architecture, it means no more Query + ClientDataSet + Provider just for a bi-directional cursor
- Much better performance when compared with dbExpress
- TADMemTable – in memory dataset, faster than TClientDataSet
- Much easier to migrate BDE applications to AnyDAC than the traditional dbExpress architecture
- Oracle
- SELECT FOR UPDATE clause – no more workaround doing UPDATE just to lock the record
- Query execution aborting – we always need that right?
- ROWID columns for fast data editing and refreshing
- RETURNING clause for fast data refreshing – no more select after the update to get a new value generated by trigger
- and more…
- Firebird
- RETURNING clause for fast data refreshing
- Trusted authentication
- DB services – backup, restore, validate, security, etc
- Query execution aborting
- PostgreSQL
- Yep, now you can connect natively with PostgreSQL
- and more…
- SQL Server
- Batch commands with multiple result sets – Now you can use that and improve your app performance
- Query execution aborting
- and more…
- MySQL
- AUTO_INCREMENT columns and retrieving of last inserted value.
- Drivers for several databases on Windows, Mac and iOS
This is a small list of interesting things on AnyDAC, you can find more here.
AnyDAC is a step forward and worth the effort to migrate from dbExpress to AnyDAC, there are lot of feature needed by Delphi and C++Builder developers, many of these features has been requested for a long time and you will have when using AnyDAC.
You may ask what is going to happen with dbExpress? BDE is dead, but still around , dbExpress is not dead and I don’t think will die soon, but there is no reason to keep two database access technology since AnyDAC is way better than dbExpress.
AnyDAC trial is available for download here.
Related Posts
Andreano Lanusse | Technology and Software Development
Follow me on Twitter: @andreanolanusse
The Wiert Corner - irregular stream of stuff: jpluimers
About 3 years ago, I wrote a small article about the Cards.dll that I encapsulated even longer ago.
I just did some looking around to see on which versions of Windows Cards.dll was still available, as Card.dll has been there since the Windows 16-bit era.
Conclusion: this C# example shows was available on Windows XP, but it seems not available on Windows Vista and up.
The successor is CardGames.dll, which is far bigger than Cards.dll, only has resources (but way more than Cards.dll), and no code.
I’ll probably use XN Resource Editor 3.1 for some investigation later on to see how to get some demos running on more modern versions of Windows (:
–jeroen
via:
- Delphi – back in 1996 – CARDS.DLL component wrapper in Delphi 1 and 2! « The Wiert Corner – irregular stream of Wiert stuff.
- Drawing Cards with Cards.dll – CodeProject.
- 25783 XN Resource Editor 3.1.
- Cards DLL Demo.
- How to change card backs? – Page 2 – Windows 7 Forums.
Filed under: .NET, C#, C# 1.0, C# 2.0, C# 3.0, C# 4.0, C# 5.0, Delphi, Development, Software Development
Firebird News: New Firebird / IB Expert Version 2013.02.18 available
Firebird News: I’ved created an RSS aggregation site for Object Pascal related blogs
Žarko Gajić: Have Hints For Some Delphi Controls Stay Longer – Control Specific Hint Hide Timeout Values
Ah, even after years of programming in Delphi, there’s still something new I learn every day (lucky me).
Just recently I needed to have hints (help tooltips) for some buttons to stay visible longer than hints for other controls (like edits, memos and alike).
Found no straight forward solution here…
… but the solution is really simple
On Hints and Application
Hints are defined using the Hint property every TControl descendant exposes. Hint property is a string value specifying the text that appears when users move (and stop) the mouse over the control. Usually you would use hints to provide some more info about the underlying control (imagine a button on a toolbar and a hint providing more details / help to what the button does). The ShowHint property then determines if the Hint text appears for the control – it is False by default.
The actual display of the hints is under the Application control (global object controlling your Delphi application). The global Application object of type TApplication also has the ShowHint and Hint properties. By default, ShowHint for Application is true. The Hint property stores the Hint value of the control when the mouse is moving over the control.
The Application further exposes the following hint related properties
- HintColor – specifies the color of the hint boxes for the Hints for the application. Default is clInfoBk.
- HintPause – specifies the time interval in milliseconds that passes before the control’s Hint appears when the user places the mouse pointer on a control.
- HintShortPuase – specifies the time in milliseconds to wait before bringing up a Hint if another Hint has already been shown.
- HintHidePause – specifies the time interval in milliseconds to wait before hiding the hint if the mouse has not moved from the control. The default is 2500 (2.5 seconds)
By looking at the above, I would like to have HintHidePuase be longer for some specific controls in my application.
If I set Application.HintHidePause = 10000 then all controls will display their hints longer – therefore a no go.
Just to mention: each Hint can actually have two parts: short and long – separated using the pipe “|” character. Short hints are what gets displayed in the pop-up window. The “long” part of the Hint is displayed by the TStatusBar control. Use the GetShortHint and GetLongHint function to extract short and long parts from the Hint value. If there’s no “|” in Hint, long value is an empty string.
TApplicationEvents To The Rescue
Now, there’s the TApplicationEvents component you can drop on a form. The TApplicationEvents wraps up events of the global Application object. The TApplicationEvents has two interesting hint-related events: OnHint and OnShowHint.
The OnHint event is of the “standard” TNotifyEvent type. From the OnHint event you can read Application.Hint and eventually alter or display it somewhere also (like in a status bar).
The OnShowHint seems to be much more promising! The OnShowHint occurs when the application is about to display the popup window with the hint – and we can use it to change the appearance and behavior of the hint tooltip! Here’s the signature:
TShowHintEvent = procedure ( var HintStr: string; var CanShow: Boolean; var HintInfo: Vcl.Controls.THintInfo) of object;
The HintStr is the text of the hint. The CanShow is the last moment to permit or prevent the hint for displaying. The HintInfo is what I’m looking for!
The HintInfo parameter contains information about the appearance and behavior of the popup window. Changing its fields will customize the way the Hint is displayed!
A peek at the THintInfo brings the declaration:
THintInfo = record HintControl: TControl; HintWindowClass: THintWindowClass; HintPos: TPoint; HintMaxWidth: Integer; HintColor: TColor; CursorRect: TRect; CursorPos: TPoint; ReshowTimeout: Integer; HideTimeout: Integer; HintStr: string; HintData: TCustomData; end;
Note the two fields:
- HintControl – the control for which hint processing is occurring.
- HideTimeout field – the number of milliseconds to show the hint. By default, it is set to the value of the Application variable’s HintHidePause property.
Ha! To have four time stay-longer hints for some buttons I can: if HintControl is TButton then HideTimeout := 4 * Application.HintHidePause;
Finally, if you’ve come so far, here’s a short code example.
- Drop a few buttons on a form
- Drop the TApplicationEvents also
- Drop some more controls of whatever type
Here’s OnCreate (not to have to specify Hint/ShowHint for controls manually):
procedure THintsForm.FormCreate(Sender: TObject); var i : integer; begin for i := 0 to -1 + self.ControlCount do begin Controls[i].ShowHint := true; Controls[i].Hint := 'Hint for ' + Controls[i].Name end; end;
And here’s Application’s OnShowHint – where I will ensure longer hints for buttons:
procedure THintsForm.ApplicationEvents1ShowHint( var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo); begin if HintInfo.HintControl is TButton then begin //make hints stay longer for buttons HintInfo.HideTimeout := HintInfo.HideTimeout * 4; //alter the hint string HintInfo.HintStr := Format('%s%sWill stay for %d ms', [HintInfo.HintStr, #13#10, HintInfo.HideTimeout]); end; end;
Run this and note that the popup window with the hint will stay visible for 10 seconds if you place your mouse over the button(s) and do not move it.
p.s.
A confession: in my real application I needed to have longer visible hints for the nodes of the virtual tree view.
More Hints Related Solutions
While it is quite easy to have a hint for a button, how would you display a hint for a particular list view item or a menu item?
How to Display Menu Item Hints
Display Custom Hints for Status Bar Panels
Display Custom TTreeView Item Hints
Custom Hints For Each Tab in Delphi’s TTabSet or TTabControl
Display Custom Hints for TListView Sub Items
Display Long TListBox Delphi Items as Hints
Disable Automatic Hint Feature for the TTreeView
Implementing OnMouseOver for Items in a TComboBox, with custom hints
Display Long TListBox Delphi Items as Hints
The road to Delphi: How distinguish when Windows was installed in Legacy BIOS or UEFI mode using Delphi?
As part of the TSMBIOS project, I needed a method to distinguish when Windows was installed in Legacy BIOS or UEFI mode. The solution was provided by the GetFirmwareEnvironmentVariable function.
The msdn documentation states
Firmware variables are not supported on a legacy BIOS-based system. The GetFirmwareEnvironmentVariable function will always fail on a legacy BIOS-based system, or if Windows was installed using legacy BIOS on a system that supports both legacy BIOS and UEFI. To identify these conditions, call the function with a dummy firmware environment name such as an empty string (“”) for the lpName parameter and a dummy GUID such as “{00000000-0000-0000-0000-000000000000}” for the lpGuid parameter. On a legacy BIOS-based system, or on a system that supports both legacy BIOS and UEFI where Windows was installed using legacy BIOS, the function will fail with ERROR_INVALID_FUNCTION. On a UEFI-based system, the function will fail with an error specific to the firmware, such as ERROR_NOACCESS, to indicate that the dummy GUID namespace does not exist.
.
So the Delphi code to detect such condition will be something like so
{$APPTYPE CONSOLE} uses Windows, SysUtils; function GetFirmwareEnvironmentVariableA(lpName, lpGuid: LPCSTR; pBuffer: Pointer; nSize: DWORD): DWORD; stdcall; external kernel32 name 'GetFirmwareEnvironmentVariableA'; begin try GetFirmwareEnvironmentVariableA('','{00000000-0000-0000-0000-000000000000}', nil,0); if (GetLastError = ERROR_INVALID_FUNCTION) then Writeln('Legacy BIOS') else Writeln('UEFI Boot Mode'); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Readln; end.
The Wiert Corner - irregular stream of stuff: jpluimers
Right now, documentation on Delphi Conditional Defines is on pages like Conditional compilation (Delphi) – RAD Studio XE2, but it is limited as it is for one specific version of Delphi only.
However, over the course of Delphi versions, compiler platforms and bitness, and not forget Free Pascal and Turbo Pascal/Borland Pascal, the matrix has become huge.
There is no complete documentation on that in one place. Right now include files like Defines.inc, the DSPack.inc, the JCL include directory the JVCL common include directory and the Jedi.inc documentation contain the collective knowledge about this.
Someone should condense that in a table and – more important– keep it up to date.
At least now there is a post collecting some of the links that contain the knowledge (:
Found one that contains these columns
- Product & Version
- VERxxx defines
- __BORLANDC__ value
- RTLVersion
- CompilerVersion
- Package Version
via Compiler/RTL version overview « Muetze1.
–jeroen
Filed under: Borland Pascal, Delphi, Delphi 1, Delphi 2005, Delphi 2006, Delphi 2007, Delphi 2009, Delphi 2010, Delphi 3, Delphi 4, Delphi 5, Delphi 6, Delphi 7, Delphi 8, Delphi x64, Delphi XE, Delphi XE2, Delphi XE3, Development, FreePascal, Pascal, Software Development, Turbo Pascal