I have changed the download links for the two latests GExperts releases. I will probably not bother with the older ones.

I have stolen yet another idea for a new GExperts feature, this time from Dave Nottage‘s Codex Delphi Expert. (But the code is all mine, including any bugs you might experience.)

The content of this sub menu is generated on the fly, so it will always contain the current entries of the Favorite Files Expert. Since that expert’s dialog is non modal, you can change the configuration and immediately see that the Favorites sub menu content changes. All entries are prefixed with numbers 0-9 and then letters A-T, so there is a maximum of 10+20=30 entries on each level. In addition there is X for Configure which opens the Expert’s configuration dialog:

Unfortunately this new feature will only be available for Delphi 7 and later. I was unable to get it to work in Delphi 6. Sorry about that! But maybe somebody else wants to try it? The source code is here.

There is no release with this feature yet. To get it, for now you have to compile your own GExperts dll which isn’t exactly rocket science anyway.

As mentioned before, I have asked to get a sub forum for GExperts on the new English speaking Delphi Praxis forum. I got it and here is the RSS feed with the current posts (which means: One 😉 ).

You might have heard that Google is “sunsetting” Google+ so the GExperts community there will no longer exist after August 2019.

There is still the GExperts Mailing List on Yahoo, but unfortunately either Google Mail or Yahoo changed something so I am no longer able to post to it (I get a rather unhelpful error message.)

So I have asked Daniel, the administrator of Delphi Praxis whether I can get a GExperts sub forum in the new English Delphi Praxis.

I haven’t received an answer yet, but I hope it will be positive. If yes, I will announce it here and as soon as that sub forum goes live.

As for my general Google+ activity, I have not yet decided what to do. Currently it looks likely that I won’t migrate to any other “social media” site at all. I definitely won’t go to Facebook.

Just in case it isn’t already common knowledge: The “Takeout” export of GMail will give you a single file in mbox format (which will be rather large).

That file contains the content of the “all mail” folder. And each of the messages in the file has a header like this:

X-Gmail-Labels: Archived,Important,Rechnung


So with the right tool it is possible to restore the labels you have set in GMail. Unfortunately I know of no such tool. And also I don’t know of any (Windows) email client that allows you to set user defined tags for emails.

EDIT: There is a tool called Google Takeout Data Converter that claims to do that. I haven’t tested it though.

EDIT2: Thunderbird apparently supports custom tags, not just the ones that are predefined.

EDIT3: Thunderbird also supports virtual folders e.g. based on tags.

In Part 1 I was ranting about input validation, now it’s about form sizes.

In my previous post I already said that I am not a big fan of fixed size dialogs. They are fine as long as they are large enough to show all of their content, but as soon as there is a list that can grow to an arbitrary size, fixed size dialogs are a no go. Look at the Microsoft Printer installation dialog again:

It contains two lists, one of printer manufacturers and the other of printer models of the selected manufacturer. Of course, it could be even worse and have one list only (And even that wouldn’t have surprised me really. Microsoft has all sorts of bad UIs.). So the list of manufacturers is sorted and even has incremental search. It’s also not too long so it’s probably fine for most users. But the list of printer models is really stupid:

1. All printer names are prefixed with the manufacturer name, so incremental search does not help.
2. The list is not high enough to display all entries. Thus there is a vertical scroll bar. OK, for several hundred printers listed for HP alone no screen would have been large enough, so it can’t be avoided. But one page only shows 4 lines and the last one is even only half visible.
3. And also, the list is not even wide enough to display the full text of the entries. So we get a horizontal scroll bar too which further reduces the number of lines visible.

In my opinion this is really bad UI design. That dialog could be improved a lot by simply changing it to be sizable, anchor both lists to the left, top and bottom and the right list to the right as well.

I faked that dialog with Delphi so I could show you what I mean:

I even reproduced the ridiculously large white areas around the lists. But this dialog can be sized and has even got a maximize button. Let’s see what it looks like if I make it just a little bit larger:

No horizontal scroll bar any more. And all visible lines are completely visible rather than only half of the last one. And if I increased the height even more, it would become much easier to scroll the printer list down to the right model.

OK, so we make all our dialogs sizeable and set the control’s anchors correctly. Easy as cake. Is that it?

No. With more freedom for the user we get more things he can screw up. Have a look at the next screen shot:

A sizeable dialog can also be made smaller and this is what it looks like. Eeek! So what can we do about it? We set size restrictions.

The easiest way to do that, is designing forms in the smallest size you want them to have and then set its minimum size to the designed size.

I have got two simple helper functions that do this for me:

type
TControlConstraints = (ccMinWidth, ccMinHeight, ccMaxWidth, ccMaxHeight);
TControlConstraintsSet = set of TControlConstraints;
const
ccMin = [ccMinWidth, ccMinHeight];
ccMax = [ccMaxWidth, ccMaxHeight];
ccAll = [ccMinWidth, ccMinHeight, ccMaxWidth, ccMaxHeight];
ccFixedHeightMinWidth = [ccMinWidth, ccMinHeight, ccMaxHeight];
ccFixedWidthMinHeight = [ccMinWidth, ccMinHeight, ccMaxWidth];

procedure TControl_SetConstraints(_Control: TControl; _Which: TControlConstraintsSet);
begin
if ccMinWidth in _Which then
_Control.Constraints.MinWidth := _Control.Width;
if ccMinHeight in _Which then
_Control.Constraints.MinHeight := _Control.Height;
if ccMaxWidth in _Which then
_Control.Constraints.MaxWidth := _Control.Width;
if ccMaxHeight in _Which then
_Control.Constraints.MaxHeight := _Control.Height;
end;

procedure TControl_SetMinConstraints(_Control: TControl);
begin
TControl_SetConstraints(_Control, ccMin);
end;


So, in the constructor I simply call:

constructor TMyForm.Create(_Owner: TComponent);
begin
inherited;
TControl_setMinConstraints(Self);
end;


Since we started out with a fixed size form, this does not have any negative effects. The form still has a minimum size that corresponds to the fixed size it used to have, so even users whose monitors are too small aren’t any worse off, and those who have large monitors can use all the space they want to.

Two more screenshots to just give you an idea of the difference:

This is the original dialog on a 24″ monitor with 1920×1280 pixel resolution:

It feels like a postage stamp.

And this is my fake dialog resized to about 1/4 of the monitor resolution:

(Of course here you see that I didn’t bother to add more lines to the lists than necessary to get my point across.)

Here is another example where a fixed sized dialog is not a good idea.

There is one entry field that takes a file name. In a fixed sized dialog you would usually end up with clipping that name because nowadays file names are very long since nobody has to type them any more. If you are a user, you still want to be able to see the full path. So, you simply make the dialog wider and tadaaa:

This is a special case where you probably don’t want the dialog to become any higher than the designed size. So what do you do? Simple, you also restrict the height:

constructor TMyForm.Create(_Owner: TComponent);
begin
inherited;
TControl_SetConstraints(Self, ccFixedHeightMinWidth);
end;


Now that user can resize it to make it wider, but the height will not change.

I’ll leave it at that for now. Expect more rambling in a future post.

And while you are waiting for that one, you can read what
David Millington said in his blog posts on How to Design a Great UI (part 2, part 3).

David Millington has published his third blog post on How to Design a Great UI. While I agree with many of his points I disagree with some others. So here is what I disagree with and why:

# Use BorderStyle bsDialog

This means several things:

1. The dialog has a thicker border than usual
2. The dialog has a close icon only.
3. The dialog cannot be sized.

The last point is what I strongly disagree with for dialogs like the one in his example. It has got a list of chapters from a book that are to be exported. This list can be rather long so it is quite likely that it at one time get a vertical scroll bar. Also, chapter headings might be much longer than simply “Chapter 1” (and you want to display those headings so the user can make an informed decision on which chapter(s) to export.), so it is also likely that the list gets a horizontal scroll bar (or even worse, if the control you use does not support horizontal scrolling: The items get cut off on the right.). This reduces usability significantly. Just have a look at the Microsoft Printer installation dialog:

We are no longer living in the 1990ies, when you could count yourself lucky if you owned a 17″ Monitor with a resolution of 1280×1024 pixels. Today, there are users who have got a a 30 inch monitor or even several of those and with resolutions up to 8K, where this dialog shows only in the center with the perceived size of a postage stamp.
As a user I want to maximize this kind of dialog so I see more entries of the list and also read the whole description.
So, rather than using BorderStyle = bsDialog, use bsSizeable and make sure that you set the Anchors and Align properties of all controls in a way they resize and move sensibly.

# Position: poOwnerFormCenter (or alternatively poMainFormCenter.)

Again, this was fine for the 1990ies. But nowadys the owner form can be huge (e.g. full screen on a 30″ monitor) and you clicked a button somewhere on that form (e.g. in the bottom right) to get to this dialog. Would you really like the new dialog to pop up centered on the owner form? Your mouse just moved to that button and your visual focus also did. Now, all of a sudden you have to move your focus (and your mouse) to the middle of the screen. My suggestion in that case would be to center the new dialog on the button the user clicked to open it. Of course this should still make sure that the dialog is fully visible and does not cross monitor borders.

There are a lot of command line tools that are very useful, but have one flaw: They directly modify a file in place and do not allow you to specify an output file instead.

So, if you e.g. want to compare the modified file to the original, you have to make a copy first. Or, if you just want to view the modified file but keep the original or you cannot modify the file itself because it is immutable due to whatever reason (e.g. it resides on a read only medium like a cdrom), again you have to make a copy first.

My particular use case was the great tool DrpojNormalizer written by Uwe Raabe (and later replaced by ProjectMagician) which I wanted to use for showing changes in Delphi .dproj files in BeyondCompare. These tools are actually Delphi IDE plugins but both come with a command line program too. But unfortunately both modify the file in place so they are one example of the above mentioned category of tools.

Enter InplaceExeWrapper which called as

InplaceExeWrapper --expectfilenameonstdout c:\path\to\dprojnormalizercmd.exe input.dproj output.dproj


does the following:

1. Create a temporary directory under %TEMP%
2. Copy the input file input.dproj there
3. Call the tool as
dprojnormalizer tempfile.dproj

4. Copy the modified file to the output file output.dproj
5. Delete the temporary directory

So basically it replaces the missing functionality of specifying an output file for the tool it calls.

Here is the full help on command line parameters and options:

Synopsis: InplaceExeWrapper [options] Executable InFile [OutFile]

Parameters:
Executable        : Executable to call (if set to "copy" we only copy InFile to OutFile)
InFile            : input filename
OutFile           : output filename, defaults to infile

Options:
--CheckResult=value : If set, the executable must return the value given for this option (must be a number)
--debug           : if given, some debug output is written to error.txt in the temp directory and the temp directo
ry will not be deleted.
--ExpectFilenameOnStdout : if set, the output of the executable must contain the filename
--help
-?
-h
-H                : display parameter help
--ShowCmdLine     : Show command line as passed to the program.
--StartupLog=value : Write a startup log to the given file.
--TempDir=value   : directory to use for temporary files (must exist)
--toStdOut        : if set, output is written to stdout and InFile is not changed


The option –ExpectFilenameOnStdout can be used to detect if the called program actually worked. If the output does not contain the file name InplaceExeWrapper assumes that the call failed.

There is the special “executable” parameter “copy” which simply copies InFile to OutFile. I needed it to make the file editable in BeyondCompare. Here is the required configuration to be added to Tools -> File Formats:

Name: Delphi Project
Conversion:

Path\to\InplaceExeWrapper.exe  --expectfilenameonstdout "Path\To\dprojnormalizercmd.exe" %s %t


Saving:

Path\to\InplaceExeWrapper.exe copy %s %t


InplaceExeWrapper has a project page on OSDN which includes a binary download as well as the source code.

Since I keep forgetting which project is hosted where, here is a list of all(?) my open source projects as of 2018-10-06 with links to the pages where they are being hosted.

Most of them are written in Delphi with some Lazarus (for Windows) sprinkled in.

My old homepage still lists other stuff, some of it written in Perl or even ReXX.

I have just released version 0.0.5 of my Denkzettel Companion tool. It is available from OSDN.

This version fixes a bug that prevented the program to start when the notes directory did not exist.
(Since nobody reported that problem, probably nobody but me has ever used the tool.)

Also, I have moved that project to OSDN now too.