Dreams are Weird

I haven’t had much of a chance to catch up on a few things I was hoping to post about, but I had the most unusual experience Tuesday morning. It was so strange, in fact, that I can’t help myself from sharing it with you.

I have almost always had trouble sleeping. As far back as I can remember, I tossed and turned most school nights for whatever silly reasons kept a young boy of that age awake. I suppose it would have been (and may still be) diagnosed as some form of insomnia, but I confess that sometimes–sometimes–it is worth more to me than all the gold in the world.

Monday night/Tuesday morning was one such experience. For the first time in my life, I couldn’t get any sleep because every thirty minutes I was waking up laughing. I’m not talking about a foggy-minded chuckle, either. You know the sort: You’ve stayed up far later than any sane person would otherwise do and everything is just stupidly hialrious. Except it totally wasn’t like that at all. I think I woke up laughing at least 5 times with a loud cackle. Worse, it was my laughter that woke me up. Every time. I really wish I could remember what I was dreaming about each of those times, but I can only recall one. It made absolutely no sense, but the premise was so ridiculous I couldn’t keep myself from laughing.

It would’ve been classified as a partial flash back dream. I know you have all had this sort from time to time: You’re back in school, you’re sitting in a familiar class, maybe the instructor is someone you know–or someone you don’t–and you haven’t any idea what you’re doing there. You’re just there. I don’t recall a great deal about the class other than it was a rather peculiar combination of some room I vaguely recall from high school mixed with about three other locations at two different college campuses. I also recall that it was a mixed class–military, older folks, and younger folks all tossed in together.

Oh, and the marine I was sitting behind in this dream was ranting about some enlisted army man he couldn’t stand. That’s where this dream begins to break down into hilariousness. Not only was the marine complaining out loud, but before he was finished, he pulled a banjo out of thin air and started singing his disgust with his compatriot from another branch of the services. Weirder still, in the middle of his melodic rant about some poor army serviceman, two other guys in the class room pulled out their own banjos and started singing backup vocals. The entire scene was so outrageous and so silly that I started laughing. And laughing. And laughing.

Then I woke up–still laughing.

Sleep deprivation sucks. However, I have to confess something to you: If you’re going to be sleep deprived, it’s just way too awesome to be deprived because you can’t stop laughing in your sleep.

No comments.
***

Favicons and You

First of all, I want to tell you something very important:

No matter what you’ve read about free or open source tools to create favicons, it’s all wrong. Forget it. Ignore it. Listen to me.

Good, got that?

A little background: I wasted a good two or three hours this Saturday hunting down decent tools to convert transparent PNGs to favicons. Nothing would work. I tried png2ico (which I used back in 2002), icoutils, and xpm2wico (XPMs don’t support 8 bit alpha–just so you know), and the results were less than spectacular. icoutils tried to work, but I was left with one tiny image that contained tinier copies of itself vertically aligned, mangled, and contorted. It looked terrible, and I have no idea why I couldn’t get it to export even a single 16×16 icon that didn’t look like a Russian matryoshka doll.

I found the answer. The answer is to use IcoFX. It’s Windows only, but it works great under an XP virtual machine. Best of all it supports 8 bit transparency, and the output looks fantastic. I highly recommend this little app, and I suggested sending the author a donation if you use it.

I’m really disappointed about two things: 1) That IcoFX doesn’t show up under any search related to favicons (instead you get crummy online converters that don’t work) and 2) that you have to specifically search for icon editors. Seriously, people! Stop linking to horrible, awful, worthless online converters and start linking to IcoFX instead! Those online converters are absolute rubbish, and all of the F/OSS apps that generate ICOs don’t work well if you’re dealing with 8 bit transparency. IcoFX is the only app that works and works well. I can’t stress this enough, and because I wasted so much time chasing dead ends looking for favicon utilities, I hope I can save you some time. Don’t bother with anything but this tool. It’s free, it’s fast, and it just works.

Spread the word about this little app, not nonsense about tools that are almost 10 years old. Oh, and those online converters? Forget it.

No comments.
***

Java and Multiple Desktops

If you’re using a single monitor, this article won’t be of much use to you. For the rest of you who have two (or more) monitors plugged into your box, you may be able to glean something of useful from this post. I’m sure the topic has been written to death elsewhere, and while there’s a few different ways to accomplish the same thing, this is my solution.

An example and the wrong solution, told as a story

First, the problem. If you’re written any GUI applications, I know you’ve done this at least once before: You create your GUI, you start attaching widgets to it, you launch it, you debug it, and about two or three hours into the project, you’re growing increasingly more annoyed with the window positioning. Maybe the window is attaching itself to the upper left corner, or maybe your window manager is genuinely trying hard to do the Right Thing thus leaving your application to appear randomly around the screen in a feeble attempt to cascade against something that doesn’t exist. Frustrated, you do something like this (SWT example):

// This assumes that display has already been created elsewhere.
// app is a class representing the main window for the application.
private void centerOnScreen ()
{
    Rectangle bounds = new Rectangle(0, 0, 0, 0);
    Rectangle desktopBounds = display.getBounds();
 
    bounds.x = (desktopBounds.width / 2) - (app.DEFAULT_WIDTH / 2);
    bounds.y = (desktopBounds.height / 2) - (app.DEFAULT_HEIGHT / 2);
    bounds.width = app.DEFAULT_WIDTH;
    bounds.height = app.DEFAULT_HEIGHT;
 
    shell.setBounds(bounds); // That'll teach 'em.
}

Your application is now centering itself on the screen. Great! No longer do you have to hunt around for the silly thing during debugging to drag it around, resize it, or otherwise mutter unsavory curses under your breath.

You then plug in a second monitor, and immediately those same unsavory words you uttered earlier have grown significantly worse. Now, with a second monitor, your nifty centerOnScreen() method centers the application between windows. “Curses,” you say, “I though I had the damn thing fixed!” Since your boss was kind enough to purchase two identical monitors, you figure you’ll draft up a quick fix:

// This assumes that display has already been created elsewhere.
// app is a class representing the main window for the application.
private void centerOnScreen ()
{
    Rectangle bounds = new Rectangle(0, 0, 0, 0);
    Rectangle desktopBounds = display.getBounds();
 
    bounds.x = (desktopBounds.width / 2) - (app.DEFAULT_WIDTH / 2);
    bounds.y = (desktopBounds.height / 2) - (app.DEFAULT_HEIGHT / 2);
    bounds.width = app.DEFAULT_WIDTH;
    bounds.height = app.DEFAULT_HEIGHT;
 
    // XXX: No one will ever have a monitor greater than 1920 pixels wide.
    if (desktopBounds.width > 1920)
        bounds.x = (desktopBounds.width / 2 / 2) - (app.DEFAULT_WIDTH / 2);
 
    shell.setBounds(bounds); // That'll teach 'em.
}

After such harrowing labor, your application is now back to normal and centering itself on the left-most monitor. You smile smugly, commit the changes, and go home for the day.

The next morning, you get an e-mail from one of the other developers in the office. He’s not particularly happy:

From: Bob Jones <[email protected]>
To: UI Design Team <[email protected]>
Subject: what’s wrong with the ui?

Hey guys, I just noticed that some changes made since yesterday have the application appearing kind of off my left screen. It spills over a bit onto the right monitor.

For what it’s worth, my monitors are of two different sizes so it’s kinda funky.

Oops. Dividing the desktop size in two (you have two monitors of identical dimensions) and then dividing that number in two doesn’t quite work when individual attached screens differ in width. Worse, what happens when someone buys a monitor with a horizontal resolution greater than 1920?

From: Milton Pencilpicker <[email protected]>
To: UI Design Team <[email protected]>
Subject: App keeps appearing waaaaaaay off to the side

Guys,

Did you say the app was supposed to start centering after the changes made yesterday? It’s still broken. It centers vertically just fine, but it’s about a quarter of the way over to the left. Just thought I’d let you know.

Yeah, this solution isn’t going to work. You need to account for resolutions on a per screen basis. So what do you do?

Probe some screens

Java exposes individuals screens through the GraphicsEnvironment singleton. The advantage of this method over getBounds() (or the Swing equivalent) is that you can easily determine the default device. Here’s one such example:

private GraphicsDevice defaultDevice;
private int defaultDeviceOffset = 0;
private ArrayList<Dimension> screens;
private int totalWidth = 0;
 
/**
 * Probe attached displays.
 * This method collects data related to all attached displays.
 * For illustrative purposes, we're recording the dimension of each
 * attached screen and recording it in the local screens arraylist.
 * We then add up the total screen width.
 */
private void probeDisplays ()
{
    GraphicsDevice[] devices = GraphicsEnvironment
        .getLocalGraphicsEnvironment()
        .getScreenDevices();
 
    defaultDevice = GraphicsEnvironment
        .getLocalGraphicsEnvironment()
        .getDefaultScreenDevice();
 
    for (int i = 0; i < devices.length; i++) {
        if (devices[i].equals(defaultDevice))
            // Do something when we encounter the default device.
            // One example would be to calculate the total screen
            // width thusfar. For our example purposes, we're going
            // to record the default device offset versus all other
            // attached screens.
            defaultDeviceOffset = i; // Mostly meaningless; sample purposes only.
 
        DisplayMode dm = devices[i].getDisplayMode();
        Dimension d = new Dimension(dm.getWidth(), dm.getHeight());
        screens.add(d);
        totalWidth += dm.getWidth();
    }
}

By recording individual screens, our centerOnScreen method can now be performed on a screen-by-screen basis:

/**
 * Revised centerOnScreen.
 */
private void centerOnScreen ()
{
    // Center the window based upon the default device dimensions.
 
    Rectangle bounds = new Rectangle(0, 0, 0, 0);
    int widthSoFar = 0;
 
    for (int i = 0; i <= defaultDeviceOffset; i++) {
        widthSoFar += screens.get(i).getWidth();
    }
 
    bounds.x = (screens.get(i).getWidth() / 2) - (app.DEFAULT_WIDTH / 2) + widthSoFar;
    bounds.y = (screens.get(i).getHeight() / 2) - (app.DEFAULT_HEIGHT / 2);
    bounds.width = app.DEFAULT_WIDTH;
    bounds.height = app.DEFAULT_HEIGHT;
 
    shell.setBounds(bounds);
}

By collecting metrics from the GraphicsEnvironment singleton, we’ve established what 1) the default device dimensions and and 2) have established code that will automatically correct for centering the application on the default device regardless of whether it is to the left or right of the other monitor or monitors.

Some Improvements

I want to keep this article short, so I’ll offer some suggestions for further improvements.

  • If you wanted to restore the window position after it has been closed and restarted, just record the current window boundaries. Assuming monitor positions don’t change between launches, the application will restart from the last position it was closed at.
  • It would be trivial to examine the window positions just prior to restoring the previous session to determine if it will be drawn within the boundaries of the desktop. You could either examine each screen individually (more accurate) or by gathering the desktop boundaries regardless of display (less code, less accurate). The best option is certainly to compare the window’s last position with the dimensions and structure of the probed screen devices. You can correct for changes in resolution, missing monitors, and a few other unexpected situations.
  • This example only corrects for the most common scenario where desktop monitors are side-by-side. This is the default on Windows without 3rd party software and is generally a safe assumption. For platforms using Xorg (or similar), individual monitors can be to the left, right, top, or bottom of the primary display. NVIDIA’s drives provide fairly fine-grained control over monitor positioning, so if you’re targeting a multi-monitor environment under *nix, you may wish to do further testing and examine window positions based on height.
  • This code can easily be adapted to Swing.
  • While the sample code won’t work out of the box with JFace, it shouldn’t be too difficult to modify the sample code so it’ll work. Be sure to use getShell() to obtain window locations and dimensions but be mindful that this will probably break if you try to access the shell object during a close action. I’ll be making another post to demonstrate one possible solution.

Should you find bugs or would like to offer corrections to the example code in this article, please feel free to post. Obviously, corrections or suggestions that extend beyond the spirit of the code won’t be included. If you would like to offer criticism, please post full code snippets (even if you’re copying part of what I’ve written in this article) for clarity; should someone new to Java come across my blog, you would be doing them a fantastic service by limiting the amount of vertical scrolling necessary to read the example code and your critiques. :)

I waive all rights to the code in this article and hereby place it into the public domain.

No comments.
***