31 December 2011

CodeSnip v4 Preview Now Available

By the skin of my teeth I've kept the promise I made in my earlier post to release an alpha, or preview, version of CodeSnip v4 by the end of 2011 - with 20 mins to spare! Phew.

The main new feature of the program is that snippets etc. can now be opened in multiple tabs, allowing more than one to be viewed at the same time.

There are several other UI changes and a lot of behind that scenes re-coding. You can see a full list in the update log.

The preview is only available on SourceForge - in the CodeSnip project's Release-4.0.0-alpha.1 directory.

If you decide to try the preview, be careful, and read the release's read-me file before installing.

Enjoy.

24 October 2011

CodeSnip v4: progress at last

Back in December 2010 I announced I'd started work on CodeSnip v4 and expected to release a new beta in a "few months". Now, nearly eleven months later I've made some noticable progress and am considering an alpha release! I'm hoping to get this out by the year end.

One of first new features I've decided on is to add multiple tab browsing of the database. This means that more than one snippet can be viewed in tabs in the main display.

One consequence of this change is that test compilation of Delphi Snippets have been demoted from the main display to a dialog box.

The source code for the new version is in the v4-dev branch of the CodeSnip Subversion repo. Use at your own risk.

Update:The v4 development code has now been integrated into the trunk and the v4-dev branch has been removed.

There's still an opportunity to suggest new features using the Feature Tracker on SourceForge.

08 September 2011

Help with Delphi XE2 program paths please

I've just updated CodeSnip to v3.9.0 to allow code snippets to be test compiled by Delphi XE2 (32 bit only).

But I've had to make some educated guesses in adding that support because I don't yet have XE2. I'm hoping you can help by either confirming or correcting the following assumptions:
  •  The registry key \Software\Embarcadero\BDS\9.0 exists in the HKEY_LOCAL_MACHINE hive.
  • The registry key contains a string value named RootDir that has a string value that provides Delphi XE2's installation path.
  • The 32 bit compiler is located in the Bin sub-directory of the installation path and is named DCC32.exe.
The new version of CodeSnip depends on these assumptions being true!

Further, I'd like to add support for the 64 bit compiler. To do this I need to know what the file name is (is it DCC64.exe?) and where it's located.

If you can help please do so via comments or by contacting me via my website contact page.

20 May 2011

Curious bug in CodeSnip???

A user of my CodeSnip program has reported a strange bug where, after using the Snippets Editor to add a new snippet, the editor dialog box refuses to close, hanging the program.

Apparently this happens only sometimes, and affects v3.8.9 and some earlier versions.

I can't reproduce the problem and was wandering if anyone else has experienced this bug. If so it would be very helpful if you could leave a comment describing what happened and what you were doing when the bug arose.

Thanks

05 May 2011

Delphi, Javascript and Floating Point Parameters

I recently came across an interesting little problem when using Delphi to call JavaScript in an HTML document loaded into a TWebBrowser control.

The code used the execScript method of the IHTMLWindow interface, which requires that a string containing the JavaScript function call is passed as one of its parameters.

So you have to assemble a string containing the function call and its parameters, something like this:

var
  JSFn: string;
  StrParam: string;
  IntParam: Integer;
  FloatParam: Double;
begin
  StrParam := 'Say \"Hello\"';  // double quotes must be escaped
  IntParam := 42;
  FloatParam := 1234.56789;
  JSFn := Format('Foo(%d, "%s", %.8f);', [IntParam, StrParam, FloatParam]);
  ExecJS(JSFn);
end;

I'll show ExecJS later. It's just a wrapper that ultimately calls IHTMLWindow.execScript with the required JavaScript.

The interesting bit is what happens to the floating point parameter. Delphi consults the current locale when formatting floats, so the decimal separator used in the resulting string depends of the locale. For example, on my UK English system JSFn has value:

Foo(42, "Say \"Hello\"", 1234.56789000);

whereas when I switch to a German locale the value changes to:

Foo(42, "Say \"Hello\"", 1234,56789000);

Now when the function call is passed to the JavaScript engine it has to interpret the string as valid JavaScript. What surprised me at first was the fact that, on the German locale, the float parameter was truncated to an integer value whilst on the UK locale it parsed correctly.

Once I thought about it more it became clear why. When the string containing the function call is passed to the JavaScript engine it has to be parsed. The string must contain valid JavaScript source code. And the JavaScript spec says that decimal numbers use '.' as the decimal separator, which means that any ',' separator is not valid. In fact ',' is the parameter separator so a parameter of '123,4567' is interpreted as two integer parameters: 123 and 4567.

Assume you have an HTML document that contains the following JavaScript in its <head> section:

<script type="text/JavaScript">
  function showNum(n1, n2) {
    alert(
      'n1 = ' + n1 + '\n\n' + 'n2 = ' + n2
    );
  }
</script>

You also have a Delphi program that loads the HTML document into a TWebBrowser control and has the following code attached to a button click:

ExecJS(
  Format('showNum(%.8f, %.8f)', [123.456, -42.56])
);

Running the Delphi program with a UK English locale will work as expected and you see the JavaScript alert box displaying this text:

n1 = 123.456
n2 = -42.56

which is what you expect. Switch to a German locale and run again and you'll get:

n1 = 123
n2 = 45600000

Luckily the fix is simple. We simply get information about the current decimal separator and change it to a '.'.

Here's my first attempt:

function JSFloat(const F: Double): string;
begin
  Result := FloatToStr(F);
  if DecimalSeparator <> '.' then
    Result := StringReplace(Result, DecimalSeparator, '.', [rfReplaceAll]);
end;

This uses the DecimalSeparator global variable that Delphi sets correctly for the current locale when the program starts.

When we change the code that calls the JavaScript function to the following, everything works no matter what the locale.

ExecJS(
  Format('showNum(%s, %s)', [JSFloat(123.456), JSFloat(-42.56)])
);

Using DecimalSeparator is not thread safe, and I think the code is rather clunky, so here's another, thread safe, version of JSFloat that I prefer:

function JSFloat(const F: Double): string;
var
  FS: TFormatSettings;
begin
  GetLocaleFormatSettings(GetThreadLocale, FS);
  FS.DecimalSeparator := '.';
  Result := FloatToStr(F, FS);
end;

This gets the format settings for the current thread's locale and then replaces the decimal separator with the required '.'. It uses the extended version of FloatToStr to convert the float according the provided locale.

Note that this code doesn't change the locale's decimal separator because we're operating on a copy of the locale information. Therefore we won't trample on any other parts of the program that may need the the correct decimal separator for the locale.

One further tweak suggests itself that takes advantage of the fact that the Format routine can take an optional TFormatSettings parameter:

var
  FS: TFormatSettings;
begin
  GetLocaleFormatSettings(GetThreadLocale, FS);
  FS.DecimalSeparator := '.';
  ExecJS(
    Format('showNum(%.8f, %.8f);', [123.456, -42.56], FS)
  );
end;

And finally, the promised implementation of the ExecJS method. Note that this is a method of the form that contains the browser control, which we assume is named WebBrowser1.

procedure TForm1.ExecJS(const Fn: string);
var
  Doc: IHTMLDocument2;      // current HTML document
  HTMLWindow: IHTMLWindow2; // parent window of current HTML document
begin
  // Get reference to current document
  Doc := WebBrowser1.Document as IHTMLDocument2;
  Assert(Assigned(Doc));
  // Get parent window of current document
  HTMLWindow := Doc.parentWindow;
  Assert(Assigned(HTMLWindow));
  // Run JavaScript
  HTMLWindow.execScript(Fn, 'JavaScript') // execute function
end;

22 April 2011

New host for DelphiDabbler (part 2)

The site on the old server has now been taken down, so if you are getting any "can't find server" errors when accessing www.delphidabbler.com its time to flush your DNS cache.

The migration seems to have gone well and I've cleared up what glitches I have found.

If you've found an error you can burst my bubble by emailing me about the problem on
teething [at] delphidabbler [dot] com

So, with a bit of luck, it's back to Delphi coding soon!

12 April 2011

New New Wiki Wiki

Forgot to mention before, but there's now a wiki at wiki.delphidabbler.com.

It's currently being used for a series of FAQs about some of my Delphi Library components and units. The hope is that others may add to it. You can also leave new questions there.

At present there are FAQs for:

I may also use the wiki for some program documentation at some point.

11 April 2011

New host for DelphiDabbler

As of today DelphiDabbler.com has moved to a new web host. The nameservers have been updated and the relocated site should start to appear over the next couple of days.

Why the move?. Down to money really. This is a non commercial site with falling advertising revenue (albeit with increasing traffic!) and my old web host hiked the price while admittedly providing a host of new features. Consequently I've had a shop around and shaved nearly £90 off the price while retaining all the features I need. The site does seem a little less responsive than it was, but for me that's a price worth paying (or not paying) in the current financial climate.

There are no changes to the site itself - both www.delphidabbler.com and wiki.delphidabbler.com still work as before.

There may be a few teething problems because I've had to make some behind the scenes changes to suit the new host setup. If you find any problems please let me know by emailing
teething [at] delphidabbler [dot] com

I hope it all doesn't end in tears.