Saturday, April 3, 2010

Saving window position with FXRegistry and using those settings at startup

In my last post I described my discovery of where the FXRegistry settings are saved in the Windows Registry.  I used the 'imageviewer.rb' sample/example program code to help me figure out how to read/write these settings.

I started off by simply using the x, y, width, height settings in my test app.  I found that the width and height settings were getting saved and retrieved correctly because the window size (when the app started) always matched what it was when it last closed. (good)  However, the window always started in the middle of the screen. (not so good)  Why is that?

Many frustrating hours later of searching and tinkering, I eventually thought to start playing with the "show( PLACEMENT_SCREEN )" line of code in the application.  That was it!

If you read the FXTopWindow API doc, you'll see a description of the Initial window placement options.  In particular, I found these three interesting:
  1. PLACEMENT_SCREEN - place it centered on the screen
  2. PLACEMENT_VISIBLE - place window to be fully visible
  3. PLACEMENT_DEFAULT - place it at the default size and location
So, I replaced the 'show' command option in imageviewer.rb and here's what I noticed. (BTW, if you try this with the same sample program make sure you  quit the program using CTRL+Q. Otherwise the settings won't get saved!)

The imageviewer code includes PLACEMENT_SCREEN, and regardless of what 'position' settings you quit the app with, it will always open in the centre of the screen - just as advertised.

I changed the line to use PLACEMENT_DEFAULT, and the window now opened exactly where I left it when it closed.  Yay!  That's exactly what I was expecting the code to do but it wasn't.  Anywhere in the primary or secondary monitor or even with the window app partially off the screen completely - it keeps appearing exactly where it last was.

Then my 'tester' brain kicked in (because I'm a tester by trade, not a programmer) and I wondered if there was a way to prevent a user from exiting the app off-screen (i.e. where you can't see most of the app window at all) and having it open in the same spot, partially hidden off screen?  It seemed like a lot of icky code to check the x & y coordinates and enforce some kind of 'good' placement settings.  And it gets ickier when you consider that every monitor has different resolution settings, and multiple monitors and all that bother.  blah.

So I tried PLACEMENT_VISIBLE.  This one was neat.  When I exited the app anywhere on screen, it opened the next time exactly where I left it. (good)  Then I tried exiting the app with the window partially or mostly hidden off screen, and when I start it the next time it pops on screen close to where it was, except I can see the whole window -- i.e. it's "visible".  Therefore, working as advertised! (good!)

I moved the app to my second monitor and exited the app and it appeared in the second monitor too.  The difference between this setting and PLACEMENT_DEFAULT is that with 'default' I can save the coordinates off-screen and that's where the window will next appear, but with 'visible' the window always appears in a way where you can see the whole app UI.

Cool!  Problem solved.  I don't have to write and maintain a whole bunch of complex code to idiot-proof a minor, undocumented feature of my app.  =)

The only thing left is to hopefully update the imageviewer.rb code so that it includes a different show/placement line so that it uses the x/y position settings it saves.  I'll forward this note to Lyle to see what he thinks.

Cheers!

FXRegistry and the Windows Registry

Someone pointed me to the FXRegistry class when I was faced with the problem of trying to save/retrieve settings from the FXRuby app I'm writing.

I read the FXRuby API docs but they didn't tell me much.  An internet search brought me to the Fox-Toolkit.org documentation page, and while more informative, I still didn't have clear examples of how to do this.

Turns out the FXRuby User's Guide Examples Page includes a program (imageviewer.rb) that uses FXRegistry to save/retrieve some settings.  That was beginning to help but I still had lots of questions. I found another example in chapter 12 of the FXRuby book ("FXRuby: Create Lean and Mean GUIs with Ruby" by Lyle Johnson). Section 12.3 talks about resizing layouts and I didn't care for that, but I did care about the fact that it saves and reads the settings from the registry.  The code for splitter.rb is available online. (NOTE: this splitter.rb app is different from the splitter.rb app you find in the FXRuby examples page and directory on your hard drive.)

What did I learn from all this?  Settings are magically saved somewhere, but no one could tell me exactly where in MS Windows. (I'm currently running/testing this on Windows XP.)  So I opened up 'regedit', ran the FXRuby apps and looked for the saved settings.

Here's what I found.

The 'splitter.rb' app saved the settings in: \HKEY_CURRENT_USER\Software\FoxDefault\Application\Settings

The 'imageviewer.rb' app saved the settings in: \HKEY_CURRENT_USER\Software\FoxTest\ImageViewer\...

Okay. So, I copied the registry commands from imageviewer.rb to my test app and ran it.  Where did the settings get saved?  Turns out they were saved in the same location as the splitter.rb.  Why? How do I change that?  How do I control where the settings go?

I noticed that there were other settings under the 'FoxTest' key in the Registry.  I ran a lot of sample code that I found on the net, and several of them must have written to the registry.

I stared back and forth at the code for the 3 apps (mine, splitter and imageviewer) until it hit me - I found the difference and the answer I was looking for!

If you look at the bottom of the imageviewer code, you will see the following line:

  application = FXApp.new("ImageViewer", "FoxTest")

When I look at the splitter code, it looks slightly different:

  FXApp.new do |app|
    SplitterExample.new(app)
    app.create
    app.run
  end

My source code looked like:

  application = FXApp.new

Aha! There it is!

The imageviewer code specifies the application name in the 'FXApp.new' creation line and that is used in the FXRegistry key names.  The splitter code and my code didn't specify any names so it gets saved to "FoxDefault\Application".

So, I changed my code to now read:

  application = FXApp.new( "TestApp", "FoxTest")

And I found the settings saved in: \HKEY_CURRENT_USER\Software\FoxTest\TestApp\

Yay!  I now know where the FXRegistry settings are saved in Windows Registry and how to change the location.  Cool.