Changing the mouse wheel scroll behaviour of TListBox

 Delphi  Comments Off on Changing the mouse wheel scroll behaviour of TListBox
Mar 302014
 

While working on my dzMdbViewer I wondered why the mouse wheel didn’t seem to scroll the listbox containing the table names but did work in the dbtable. It turned out to be a misconception on my side:

The listbox contained only a few items so it didn’t have a vertical scroll bar. The default behaviour is to scroll only the view without changing the currently selected item. Of course this does not have any effect if all items are already visible.

But that wasn’t what I wanted the mouse wheel to do. I wanted it to have the same effect as on the dbtable, that is: Select the next/previous line.

Unfortunately a listbox does not have an OnMouseWheel event (a dbtable does have one), so I could not simply assign an event handler and be done with it. I resorted to a proven hack and created an interposer class that overrides TListBox.DoMouseWheel:

type
  TListBox = class(StdCtrls.TListBox)
    ///<summary>
    /// Overrides the default behaviour of the TListbox for mouse wheel scrolling:
    /// Rather than scroll the view without affecting the selected item, select the
    /// next/previous item as if the user pressed the up/down arrow key. </summary>
    function DoMouseWheel(_Shift: TShiftState; _WheelDelta: Integer; _MousePos: TPoint): Boolean; override;
  end;

type
  Tfr_AccessDb = class(TFrame)
    lb_Tables: TListBox;
  private
  public
  end;

implementation

{$R *.dfm}

uses
  Math,
  u_dzVclUtils;

{ TListBox }

function TListBox.DoMouseWheel(_Shift: TShiftState; _WheelDelta: Integer; _MousePos: TPoint): Boolean;
var
  Idx: Integer;
begin
  // calculate the index of the item to select
  Idx := ItemIndex - Sign(_WheelDelta);
  if Idx >= Items.Count then
    Idx := Items.Count
  else if Idx < 0 then
    Idx := 0;
  // select it
  ItemIndex := Idx;
  // and simulate a mouse click on it so the selected table gets displayed
  Self.Click;

  // tell the caller that the event has been handled
  Result := True;
end;

Simple, isn't it?

I found a more involved version on The Spirit of Delphi that overrides the WndProc method and changes the Message to a keyboard message. I like my solution better, it "feels" cleaner.

The other solution also creates a control rather than using an interposer class. That's cleaner than an interposer class but I'm not a great fan of writing my own components to solve shortcomings like this. It's just too much overhead.

 Posted by on 2014-03-30 at 19:37

Convert from Subversion to Mercurial fast

 Mercurial  Comments Off on Convert from Subversion to Mercurial fast
Mar 292014
 

In my previous post about converting from Subversion to Mercurial I assumed that I would want to migrate the history of changes from my svn repository to the new hg repository.

For some projects, I don’t really care about the history so I decided to take the quick and dirty route:

  • Create a new, empty master hg repository (on a server)
  • Clone that hg repository to a local directory
  • Check out the project from svn to the same local directory
  • Add a new .hgignore file to ignore the .svn subdirectory
  • Add the .hg subdirectory to the svn:ignore property
  • Add .hgignore to svn and commit that change
  • Add everything to hg, commit and push that change

That’s it. Now you have got a directory that contains your sources and they are managed with both, Subversion and Mercurial. Now, get into the habit of committing changes to both as long as you want. Once you are comfortable with Mercurial, just stop using Subversion. You might want to delete the .svn subdirectory at that point.

 Posted by on 2014-03-29 at 19:11

Logging into a Mercurial repository on Sourceforge with an SSH private key

 Mercurial  Comments Off on Logging into a Mercurial repository on Sourceforge with an SSH private key
Mar 272014
 

I was just about to post the following to stackexchange.com:

I have got a mercurial repository on sourceforge.net, generated an ssh key with PuttyGen and uploaded it to Shell Services Configuration as described in the relevant site documentation. I then started Pageant and entered my passphrase.

Now, I can connect to shell.sourceforge.net without entering a password and create a session there that works fine. So, the key has been generated correctly and uploading the public key also worked fine.

As I understand it, I should now also be able to use the command

hg pull

on a sourceforge repository without the need to enter my password. I therefore added the following line to my mercurial.ini file, in the ui section:

ssh="C:\Program Files (x86)\PuTTY\plink.exe" -ssh -agent -v -i "D:\path\to\my\private_key.ppk"

Unfortunately any hg command that requires a connection to the sourceforge repository times out. If I remove that line again, everything works fine, but I have to enter my password.

hg --debug pull

emits the following lines:

And then I tried it again to get the error messages for posting into the question. Guess what? This time it worked. I swear, I didn’t do anything different than before and I haven’t changed anything. All previous attempts failed with the error “Server refused our key”. So I guess sourceforge changed something. Maybe it was a case of synchronizing the public key to all servers taking forever (several days) or maybe they came around to reading my support request and doing something about it.

Don’t you hate it when things start working without you knowing what caused it to fail in the first place? I certainly do!

 Posted by on 2014-03-27 at 21:51

Using MSConfig to disable autostart

 Windows  Comments Off on Using MSConfig to disable autostart
Mar 272014
 

I guess most of you know that you can use msconfig (Start->Run->”msconfig”) to disable programs that are automatically started by Windows. And you might have wondered where these entries go, when you disable them. Especially it looks like magic if entries in the start menu’s Startup folders are concerned: You disable them in msconfig and the shortcuts disappear from the folder. You enable them, and the shortcuts reappear again. So how does msconfig know about these disabled entries?

Googling brought me (after lots of irrelevant entries, Google search seems to be getting worse by the day) to
this Stackoverflow question which also contains the answer:

They are stored in the registry under

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\MSConfig\startupreg

for programs that are started through the registry autostart and under

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\MSConfig\startupfolder

for programs started from the startup folder.

Thanks to my colleague Steffen who brought this up. Learned something useful today.

 Posted by on 2014-03-27 at 12:46

I bought a Kindle

 Kindle  Comments Off on I bought a Kindle
Mar 162014
 

I had decided to never buy a Kindle because I take issue with Amazon controlling it, not myself. Also, I hate it that all Kindle e-books from Amazon are encumbered by DRM.

Nevertheless, as the price dropped below 50 Euros for the pre-Paperwhite version, I could no longer resist and bought it. The rationalization is that last holiday I run out of books halfway through and had to hunt through the hotel’s book stock and try to buy some English books in a Spanish holiday town mostly frequented by Germans (where there were quite a few shops selling used German books, but not a single one selling English books, used or new). So, next time this happens all I need is some Internet access. We’ll see how that works out.

As for the device itself:
The good:
The e-ink display is awesome. It makes reading a lot less stressful to the eyes than my phone or tablet display. Also, the Kindle weights a lot less and goes a for many days with one battery charge. I look forward to reading some “thick” books on it without hurting my wrists holding them.

The bad:
The positioning of the buttons for turning the pages takes a while to get used to. They are not where I could use them when holding the Kindle with one hand. Also organizing my books is more trouble than I would have thought. Where is the web interface for doing that task on my desktop computer? The built in web browser (which is beta) is not of much use. Since I already have got a smart phone and a tablet I don’t expect to use it at all.
Buying a book from the Kindle is a lot more hassle than I would have thought. Yes, you can browse through the categories but once you are down to the smallest sub category you are still left with a list of hundreds of books. Yes, you can search but I found that not very useful. Where are the suggestions that most of us like about the Amazon website? The actual buying on the other hand is too simple. You select the book and “click” on the buy button. That’s it, you get a confirmation, your credit card is charged and the download starts immediately. I would have preferred a shopping cart like in the Amazon shop (no I haven’t activated one click shopping there either), so I can still reconsider whether I really want to buy this / these books. At least you can cancel the order immediately on the confirmation page. I did that the first time I unintentionally ordered a book where I had downloaded the preview. The money was refunded and the book deleted from my account within a few minutes. I expect to continue buying through the Amazon web page rather than from the Kindle.
Instead of buying you can also download a preview (apparently the first few pages) of a book for free and -if you have Amazon Prime – lend one book every month for free. Of course one book a month is a lot less than what I read normally so I don’t expect to use that feature much. Also, Amazon.de only offers German books for lending, while I prefer English books.

The ugly:
One Word: DRM. You cannot read the Kindle e-books you “buy” on anything else but either a Kindle device or the Kindle programs/Apps. If at one time in the future I want to buy an e-book reader from a different vendor, I cannot transfer any of the e-books I bought in Kindle format to the new device. Also, Amazon has proven that they can and will delete books from their customer’s Kindles even though these customers had legally bought these books through the Amazon web site. Also, there are cases where they deleted a customer account and wiped all Kindle content.
So, if you buy a Kindle, you must understand that you are not actually buying the device, and if you buy a Kindle e-book you are not actually buying the book. Amazon can take it away from you and render the device useless whenever they think they have a reason for doing so.
On the upside the Kindle can also display other e-book formats that do not have DRM. So if you convert the e-books you already own to e.g. mobi format you can transfer them to your Kindle either via USB cable or by sending it to the e-mail <address>@kindle.com associated with it. There is a nice tool for doing that called Calibre. It also allows you to configure your Kindle’s e-mail address so it can automatically send the books to it.

And, of course, DRM can be broken and has been so in every single form that was sold so far. This is also true for the Kindle e-book format. There are multiple tools for removing the DRM from these e-books so they can be converted to different formats freely. They are probably illegal and I won’t link to them (that’s illegal in Germany too) but Google is your friend. I even managed to convert the one book that I had “lent” from Amazon for just that purpose (The book was crap, but hey, I never intended to read it in the first place.).

 Posted by on 2014-03-16 at 13:34

Using jclDebug

 Delphi  Comments Off on Using jclDebug
Mar 082014
 

Today I was at the Delphi Frühstück (breakfast) at Comcept GmbH in Cologne. It was quite an inspiring talk there. Thanks to the guys who organized it, I am looking forward to going there again.

One of the questions that came up was how to use jclDebug for getting source code location and call stack for error reporting. Since I have been using it at work for several years I decided to blog about it. So, here it goes:

First, you need the JCL (Jedi Code Library), in partiuclar you need the unit jclDebug and any other unit it depends on.

You include jclDebug in the uses clause of your program and initialize stack tracing like this:

initialization
  // Enable raw mode (default mode uses stack frames which aren't always generated by the compiler)
  Include(JclStackTrackingOptions, stRawMode);
  // Disable stack tracking in dynamically loaded modules (it makes stack tracking code a bit faster)
  Include(JclStackTrackingOptions, stStaticModuleList);
  // Initialize Exception tracking
  JclStartExceptionTracking;
finalization
  JclStopExceptionTracking;
end.

I usually put this code in the unit of the main form of my programs, but it can go anywhere, maybe into a unit you always include into your programs, like logging?

Once you have done that, you need to patch into the exception handling code of your application. One option is to assign the Application.OnException event:

  Application.OnException := HandleAppException;

In that event you then have access to the call stack of the exception:

procedure TMainForm.HandleAppException(_Sender: TObject; _e: Exception);
begin
  LogException(_e);
  Tf_dzDialog.ShowException(_e, Self);
end;

Of course, the above is not the interesting part. That’s in either LogException or Tf_dzDialog.ShowException. Both are part of my dzlib library which is linked from this blog.

procedure TAbstractLogger.LogException(_e: exception; const _Where: string; _IncludeCallstack: boolean);
var
  s: string;
  sl: TStringList;
begin
  if _Where <> '' then
    s := _Where + ': '
  else
    s := '';
  LogError(s + _e.ClassName + ': ' + _e.Message);
  if _IncludeCallstack and Assigned(gblOnGetCallstack) then begin
    sl := TStringList.Create;
    try
      LogDebug('');
      gblOnGetCallstack(sl);
      for s in sl do
        LogDebug(s);
      LogDebug('');
    finally
      sl.Free;
    end;
  end;
end;

So, now we still need the code called from gblOnGetCallstack. Here it is:

procedure GetCallstack(_sl: TStrings);
begin
  JclLastExceptStackListToStrings(_sl, False, True, True, False);
end;

That’s about half the work. With the above code you only get a call stack containing the addresses, which may be helpful, but not as helpful as the names of the methods would be. Fort this you need the information from the map file. The linker only creates it it you configure it to do so. Select “Detailed” for the “Map file” option.

Once you put a map file into the same directory as the executable, your call stack will contain the names of the methods rather than just the addresses.

Unfortunately map files have two disadvantages:

  • They are plain text so they become very large. Also, they could possibly be used to reverse engineer your program.
  • They are a separate file and it is easy to forget adding them whenever you distribute a new executable. If you use the wrong or out of date map file you will get some rather strange call stacks. (Trust me, I have been there.)

The guys who develop the JCL have thought about that and came up with solutions for both points:
makejcldbg is a tool that creates a jcldbg file from the map file. This is a binary format that is much smaller than the map file and also more difficult to read (it’s probably still possible to use it for reverse engineering though). Also it has an option to add the created debug information to the executable, so you can’t forget to distribute it.

One call to

makejcldbg -e executablefile.map

does the trick. That’s it. Now you can have exception reporting with a clear text call stack.

In a previous blog post I have written about the batch files I use for building projects. One task they do, is call makejcldbg from the post build event. Feel free to use them in your own projects.

 Posted by on 2014-03-08 at 19:10

Printing from DOSBox

 DOSBox  Comments Off on Printing from DOSBox
Mar 032014
 

DOSBox is a DOS-emulator that was originally developed to run old DOS games that had problems with the standard DOS emulations of Windows. It does have some additional uses though.

We have got an old DOS program that still hasn’t been fully ported from Borland Pascal to Delphi. Since Windows 7 64 Bit does no longer run DOS programs, we now run this program in DOSBox. One of the main functions of this programs is printing some curves on a dot matrix printer on fan-fold paper (Just learned a new word, thanks dict.leo.org.) Getting printing to work takes some configuration.

First, you need DOSBox Megabuild 6 since apparently printing is not fully implemented in the official version.

Then you must decide how you want to print:

  • Print to a printer attached to your computer (this includes a pdf printer provided by e.g. PDFCreator)
  • Print to png (or even bmp) files
  • Print to a file that contains the data sent to the printer
  • Print directly to a dot matrix printer

Whatever you want to do, you need to modify the .conf file (or start DOSBox with the -conf parameter pointing to a modified .conf file).

The .conf file is in INI format, so editing it can be done with any text editor. The installer also creates a menu item in the DOSBox start menu folder, subfolder config called “Edit Configuration” which you can use to edit the configuration with notepad.

Printing to a local printer

If you want to print to a local printer, do the following:

[parallel]
parallel1=printer

[printer]
printoutput=printer
multipage=false

You might want to adjust the other configuration options in the [printer] section, in particular multipage must be set to true, if you want to print to a PDF file and don’t want to create a new PDF for each page.

If you print from a DOS program within DOSBox, you will get the Windows printer select dialog. This won’t happen immediately but after a while (when enough data has been printed) or only after exiting the program or even DOSBox. There is a shortcut for “Eject Page” which is mapped to F2 by default. It might also help to get this dialog (I haven’t tried that because it probably has some side effects.)

Printing to a png/bmp file

If instead you want to print to a png or bmp file, one per page, you change the entries under [printer] like this:

[printer]
printoutput=png
docpath=c:\some\directory

Don’t forget to set the docpath to some suitable location otherwise you will hunt for the files all the time.

Other possible values for printoutput are bmp for generating bitmap files and ps for generating PostScript output. I haven’t tested them.

Printing to a file that contains the data sent to the printer

To write the data that is sent to a printer to a file, configure DOSBox like this:

[parallel]
parallel1=file

This will create files in the capture folder, the filename will be the name of the program that printed them, followed by _<3digitnumber>.prt. If you want to append everything to one file, specify the filename like this:

[parallel]
parallel1=file append:c:\outputfile.prt

The files created with this method can be copied to the printer without changes to print them. But note that this is raw output containing control codes for the printer that is printer dependent. So if your program knows how to print to an Epson ESC/P dot matrix printer, you can copy the file to such a printer and it will work. If you copy it to a different printer you will most likely get garbage.

Printing directly to a dot matrix printer

To directly print to a dot matrix printer, you configure DOSBox like this:

[parallel]
parallel1=file dev:lpt1

If the printer is directly connected to the host’s first parallel port, this should already work (I haven’t tried it though). If not, you need to mount it using

net use lpt1 \\server\SharedPrinter

This will directly print to a network printer. Again, be warned that this only works if your DOS program knows how to print to this particular printer type.

Alternatively you can specify the printer share directly in the [parallel1] section like this:

[parallel]
parallel1=file append:\\server\SharedPrinter
 Posted by on 2014-03-03 at 18:34