2010-10-28

Thoughts on document reader software

A document reader program should be unobtrusive. When you get a document on your computer that it is supposed to handle, you should just have to double-click on the document, and your operating system should open the associated program. It should do this quickly, without a lot of fanfare. The document is the focus of the user's attention, not the program. There should be nearly no need to open the reader program on its own, without a document; although, a link to the program in an appropriate folder on the Start Menu isn't uncalled for, should such a need arise (e.g., if the user wants to manually check for updates or change default settings for the program, he shouldn't have to find an irrelevant document to open first).

To these points, I say:

Adobe, quit installing an icon to Acrobat Reader on my desktop, without asking, every time you do an update; get rid of the bloat that causes Reader to take half a minute to open a document (running a service or pre-loading half your program into memory when I haven't even opened a document yet is not an acceptable option); and when I say "disable the splash screen", do not ignore that setting or re-enable it and think I won't notice.

Yes, I'm aware of alternatives, such as Foxit Reader, that aren't nearly as bad; unfortunately, I have to keep Acrobat around for those forms and bills that alternatives aren't able to process.

2010-10-24

IntelliMouse Explorer 1.0 in Windows 7

I have an old IntelliMouse Explorer. It's the original version, wired, but still works great. Alongside my Natural Keyboard Pro, it's an old, functional, comfortable piece of hardware that I refuse to get rid of. The replacements that have come along since often fall short in various ways. And, much like the Natural Keyboard Pro and other strong Microsoft hardware input devices from years gone by, Microsoft's software drivers have stopped supporting them.

There's no real reason for them not to work today. The keyboards haven't changed much, except to add or change the extra control keys sprinkled around the standard 121. Mice, even less so; they have the same X-Y directional input, five buttons, and a scroll wheel they've had for over a decade. But if you install the current version of IntelliType or IntelliPoint, they will refuse to detect your older keyboard and mouse; and even though the operating system will use them just fine for standard functions, all the fancy buttons and the ability to remap them (that used to work on older versions of the software) won't be available.

I came across this blog post on Blogfeld.com that describes in detail how to get a Natural Keyboard Pro to have full functionality in Vista and Windows 7. I followed these instructions earlier this month, and I can verify that they work flawlessly with the current version of IntelliType software (currently version 8). I thought maybe the same technique could be applied to get my old IntelliMouse Explorer to work with IntelliPoint 8 as well.

I won't post the details here — Blogfeld already does an excellent job at describing everything — I'll just indicate what I did to apply his technique to IntelliPoint.

I searched for an old version of IntelliPoint off of Microsoft's download site. You can still download IntelliPoint 5.2 from their site (link as of the time of this post is here, but you can search for "IntelliPoint 5" on download.microsoft.com to find it) and installed it on a Windows XP workstation in order to get the old files.

On the Windows 7 machine, I opened up the point64.inf file (IntelliPoint's version of IntelliType's type64.inf — and yes, I'm using 64-bit; the 32-bit version would naturally be point32.inf), and in the [MsMfg…] section, I added the following string to the block of IDs listed:

%HID\Vid_045E&Pid_001E.DeviceDesc%=HID_Filtr_Inst, HID\Vid_045E&Pid_001E

Further down in the [Strings] section, I added:

HID\VID_045E&PID_001E.DeviceDesc="Microsoft USB IntelliMouse Explorer (IntelliPoint)"

The next step was to modify the IPointDevices.xml file. This one required a little more thought, as IntelliPoint 5 did not have an IPointDevices.xml file to copy from. I noticed, however, that the IntelliMouse Explorer 3, which is supported in IntelliPoint 8, has the exact same configuration as the IntelliMouse Explorer 1. So, I found the <Device> section that describes the IntelliMouse 3, copied it, and pasted it to the end of IPointDevices.xml. I changed the <Name> node to read, simply, "IntelliMouse Explorer", changed the <OemAbbreviation> node to "IME", and changed the value under <HWID Type='PID'> to read "0x001E" (the last four characters of the USB ID, used in the point64.inf file above). I also had to change the ID in the <Device> node itself to something that was not used elsewhere in the file — '10' was good enough.

I followed the rest of the instructions from Blogfeld, and sure enough, it worked great. The configuration screen in IntelliPoint uses the images of the IntelliMouse Explorer 3, and it allows configuration of all five buttons and the scroll wheel, including per-application settings, like any other mouse it "officially" supports.

2010-10-20

Firefox + Ajax + Refresh = Disaster

Usually, when coding a web page that's targeting users of IE and Firefox, the browser that's going to cause the lesser amount of problems is Firefox. So I was genuinely surprised when I came across a bug reported for Firefox only that came down to what I consider the browser misbehaving.

The requirements for our app included a series of dropdown boxes, where the selection a user makes in one dropdown drives the choices that appear in the next one (what's commonly referred to as a "cascading dropdown"). For a nicer user experience, this is typically done with AJAX, so that the request/response that generates the second dropdown upon selection of the first doesn't require an entire page refresh. ASP.Net makes this really easy with the UpdatePanel control. Controls inside of an UpdatePanel can be refreshed without reloading the entire page. It's not as lean as a pure AJAX call could be, since the server reprocesses the whole page, but the coding time is greatly reduced.

Our environment includes a standard master page that includes a ScriptManager component (required for using UpdatePanels) and the following script:


<script language="javascript" type="text/javascript"> 
    function onEndRequest(sender, args) {  
        ajaxPostBackButton.disabled = false;  
        var error = args.get_error();  
        if (error != null) {  
            window.location = "../errorPage.aspx";  
        }  
        var updateProgressPanel = $get("<%=this.UpdateProgressPanel.ClientID %>");  
        updateProgressPanel.className = "HideObject";  
    }  
    function onBeginRequest(sender, args) {  
        var ajaxPostBackButtonId = args.get_postBackElement().id;  
        ajaxPostBackButton = document.getElementById(ajaxPostBackButtonId);  
        ajaxPostBackButton.disabled = true;  
        var updateProgressPanel = $get("<%=this.UpdateProgressPanel.ClientID %>");  
        updateProgressPanel.className = "DisplayProgressLayer";  
    }  
    var ajaxPostBackButton;  
    Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(onBeginRequest);  
    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(onEndRequest);  
</script> 

The script, in essence, binds a couple of functions to the AJAX start and stop methods that do this:

  • On start:
    • Disable the control used to trigger the AJAX call (this helps guard against double-posting)
    • Show a div that contains a "loading" animated gif to let the user know something's happening
  • On end:
    • Enable the control used to trigger the AJAX call
    • Check for an error, and if found, redirect the browser to the standard error page
    • Hide the div with the "loading" gif

Now, if the user makes a selection in the first dropdown, everything runs normally, and the second dropdown appears. If the user then presses F5 to refresh their browser, the browser reloads the page from its initial load state, i.e., with the first dropdown with the initial "Please select…" option selected, and no second dropdown.

At least, that's the way it works in IE. In Firefox, what I was seeing was, the first dropdown was getting selected to the option I had selected before I hit refresh, it was disabled, and there was no second dropdown.

Finding out why was no easy task. With the help of Firebug, I was able to show that, on refresh, neither the onBeginRequest nor the onEndRequest methods were being called, and those were the only places the dropdown's enabled state was being tinkered with. I could only conclude that Firefox itself was setting this state. But why, and how do I stop it?

A couple hours of internet searching on why a dropdown in an UpdatePanel would be disabled failed to yield any useful information. I did find one user complaining about Firefox repopulating form values with prior input on refresh; unfortunately, that user's request for how to get around it was met with a snarky response about how it was a useful feature of Firefox and how the user was mentally deficient for not appreciating it. Sorry, but when you're coding a web application that is trying to control the content of form values and states and react to changes, and the browser breaks all rules and changes those states without raising any events to react to, I'll have to go with the feature being deficient and buggy.

Coming at the problem the next day with a fresh set of search terms, I came across this blog post: Firefox refresh viewstate updatepanel bug hell!!! The post describes a more serious error that can occur with Firefox's mucking about with a form after refresh that got updated with AJAX. The solution, renaming the form's ID on every refresh, seemed a little more of a brute-force hack than I wanted, and he mentions it doesn't work well in a master page scenario anyway (which we're in).

The comments on that post, however, point to an article on developer.mozilla.org that describes the feature in more detail and, more importantly, how to turn it off. By adding the nonstandard attribute autocomplete="off" to the page's <FORM> tag, it suppresses this bothersome behavior and lets the page work as expected.

We're now determining if this action is something that should be done site-wide (add it in the master page's markup), as it could be an uncaught bug on other pages; or if it's something that should be done on a page-by-page basis, by adding this.Page.Form.Attributes["autocomplete"] = "off"; to the prerender event of any affected page.