18 November 2007

A toolbar for Clippings

Many users have asked for a quicker, easier way to paste clippings as an alternative to right-clicking on a web page text box (or a new email message), pointing to Clippings in the context menu, and then picking from the submenu that appears. Shortcut keys, which was introduced in the first milestone release leading up to the eventual 3.0 release, is one answer to this request -- albeit for keyboard-savvy users.

There were several ideas that were suggested, all of which I considered:
  • A new sidebar UI
  • A new toolbar UI
  • Double-clicking on a clipping in Clippings Manager to paste it
  • Clippings "smart tags" -- like the Smart Tags in MS Office applications
  • Make the Clippings menu detachable
  • Placing the Clippings menu on the status bar icon. (This is actually the original placement of the Clippings menu in a very early, pre-1.0 release.)
A new sidebar UI seems like a sensible idea, but I don't like how it would narrow the viewable browser content area just so a user can paste a clipping from a list that's always visible (until the sidebar is closed). Obviously not a problem for people with large screens -- but remember that not everyone has the luxury of a large monitor (for example, I still use a 17" CRT at home). And if the user's collection of clippings in the "root" (a.k.a top level) folder is not that many, then all that empty space in the lower part of the list is just wasted screen real estate.

Smart tags are a great idea -- until you actually experience them. They can be annoying, especially when they follow you everywhere in its futile attempt to be overly helpful to you in every single use case scenario. I've turned off Smart Tags in Microsoft Word for that very reason.

While double-clicking on a clipping in the Clippings Manager tree list might seem convenient, the question is, if multiple browser windows/message compose windows are open at the same time, where does the double-clicked clipping go? Should a double-click action display a popup menu or a dialog box asking which window to paste the selected clipping into? Sure, let's suppose that by default the clipping will be pasted into the previously-focused host app window... but what if the user wanted it pasted in a different window?

That leaves the toolbar UI idea. The advantage of a toolbar is that it only needs to appear when when user wants it to, and that it can either be docked to the host app window, or floating (as a floating toolbar pallete). Not only can the user quickly access the Clippings menu with just one (left) click, but other commands (New, New From Clipboard, etc.) can be readily available. Here is what I have in mind:

Clippings toolbar, docked to the host app window.

Floating toolbar.

The toolbar idea seems to be the best option. It uses up only the screen space it needs (well, the floating toolbar at least), and the user can show or hide it as he pleases. And the floating toolbar can be moved anywhere on the screen -- where the user wants it to be -- so that its within easy reach, as opposed to a fixed sidebar UI, and without the annoyance of a "smart tag" that follows you everywhere. Now that I think about it more, is a docked toolbar really that necessary?

Edit: I just realized that my arguments against adding clipping pasting capabilities from the Clippings Manager are baseless. If one could insert a clipping into the current host app window from the toolbar, one should be able to do so from Clippings Manager, too. Silly me.

07 November 2007

Discovering the wonders of stringBundle.getFormattedString()

Localization in an XUL-based app or extension is accomplished by putting all UI strings into DTD or .properties files and bundling them into a Mozilla XPI. The latter type of file is used for storing UI strings that will be programmatically assigned to XUL elements like <label> and <description>, etc. during run-time. In this situation, one would need to define a <stringbundle> element in the XUL document and set its src attribute to the chrome URL of the .properties file containing the localized UI strings. Then to get the UI string, one would do the following in the JavaScript source:
// Get the <stringbundle> element in the XUL document
var strBundle = document.getElementsByTagName("stringbundle").item(0);

// Get the UI string
var theString = strBundle.getString("foobar");
The variable theString will then contain the localized string defined in the XUL document.

A problem for which I've discovered a solution only recently is how to embed another string within a localized string. This typically happens when the UI string contains one or more substrings that are only determined at run-time, such as file I/O error messages that gives the name of the file or folder.

Somehow I overlooked a method of the <stringbundle> object that can accomplish what I needed: getFormattedString(). The idea is that the UI string contains placeholders that will be substituted at run-time with another string that is provided in an array parameter. Each nth element in the array parameter corresponds to the nth placeholder in the string -- basically, the same idea in the C library function printf().

After reading the documentation on XULPlanet, I tried it out. I modified the UI string that defines the error message that appears if the user attempts to import an invalid Clippings file:
alertImportFailed = Cannot read file: \"%s\"\nThe selected file may not be a valid Clippings file.
Then in clippings.js, which contains Clippings Manager's UI logic, I modified the code that gets the error message as follows:
// gStrBundle is a global variable referencing the <stringbundle> element defined in clippings.xul
// path contains the full path of the file the user attempted to import
var err = gStrBundle.getFormattedString("alertImportFailed", [path]);
doAlert(err);
But it didn't work: only the first character in path was returned. So I did some searching on Google and came across the documentation on Devmo that indicated that an uppercase "S" is needed in the placeholder.
alertImportFailed = Cannot read file: \"%S\"\nThe selected file may not be a valid Clippings file.
Now it works. Armed with this knowledge, I even made the effort to point this out to a developer who had written a "bug fix" to work around the lowercase "s" problem.

And one other hint on the subject of localizing strings: to embed a non-breaking space in a UI string in a .properties file, use the hexadecimal Unicode escape character \u00A0.