Default Window Sizes in JFace

The problem with pixels

Default window sizes are a tricky issue. For a long time, developers liked to assume that all people had similar resolutions and screen sizes and simply hardcoded fixed pixel dimensions for all their dialogs. Some of these missteps are still around, like the Windows environment variable dialog that’s been stuck with a fixed (way too small and not resizable) size forever, or the fact that Windows still relies on virtual DPI settings instead of using the measurement provided by the screen. Not to mention the ongoing conflict between fixed-size image design elements and browser settings based font sizes for HTML designs.

Still, I believe it’s the responsibility of any GUI designer (be it for an application, a web site or something else) to make resolution independence a primary feature. To me, this means mostly two things:

  • Never make a dialog a fixed size unless there’s a really, really good reason for not allowing users to resize it.
  • Don’t use pixel measurements for default a minimum sizes if you can avoid it in any way.

Dynamic layouts

SWT and JFace already provide some support for dynamic sizing. Making a window resizable is a simple matter of setting a single shell flag. A shell can have a minimum size and an initial size, which can be persisted to remember the changes a user made to a dialog’s size and reusing these dimensions the next time the same dialog is opened. Layouts like GridLayout and FormLayout make creating resolution independent GUIs comparatively simple, and also allows some basic calculations of the required dimensions for their controls.

In some cases, a call to getShell().pack() can in fact be all that is required to set the size of a simple dialog. Things get complicated when the dialog contains controls with scroll bars, or text input fields with variable inputs. pack() has two different problems here, depending on when it is called:

  • Undersized windows if pack() is called before filling in the data. Text fields and scrollable elements, when empty, have very low minimum sizes, just large enough to show their borders. If these sizes are used to calculate the window size, it will likely be so small that data in these controls will be barely visible.
  • Inconsistently sized or oversized windows if pack() is called after filling in the data. Text fields calculate their preferred size depending on the text they contain. A dialog for entering the name of an element would still be too small if no name has been entered yet, but if the text field is initialized with a 100 character text, the same window’s initial size would be extremely wide. Tables and list boxes have similar issues and can easily case a rather small dialog to be blown up to the full screen height just to display as much of their content as possible.

For reasonably complex dialogs, the best solution seems to me to let the developer choose a reasonable initial size, and perhaps a minimum size as well. But how to make sure that the window is reasonably sized on systems with very low resolutions and a font size of 7pt as well as ultra high resolution displays with corresponding larger fonts (that is, fonts that physically have the same height, but are displayed at a higher resolution and therefore have a greater pixel height)?

Simple: Use character dimensions as the window size units instead of pixels. SWT can provide the height, in pixels, of a font’s glyphs, as well as the average character width. These can be used as factors for the default window size.

The best location to set the default size of a JFace window is the getInitialSize() method. It returns a Point instance containing the desired width and height, in pixels. There’s only one problem: Window#getInitialSize() has a simple default implementation that calculates the size via the current layout and can easily be overridden to provide a custom size. But Dialog‘s implementation of the same method overrides this behaviour so that it first calls the Window implementation to get a default size and then accesses the persisted dialog settings to replace the defaults with the user’s own dimensions, if available. In true Eclipse fashion, this is thrown into a single method, with no option for derived classes to step in and provide a default size without also skipping the dialog settings code.

I’ve therefore implemented a simple dialog class that extends JFace’s Dialog and duplicates its getInitialSize() implementation, extending it with a call to a separate method that derived classes can use to specify a default size. I also had to duplicate a few constants from the Dialog class that refer to keys used in the dialog settings, but are private in Dialog and therefore not available to derived classes.

Implementation

Here’s the final result, placed under the Eclipse license (like the code it duplicates). I’m skipping the package and import stuff as well as the auto generated constructor to save space:

public class DefaultSizeDialog extends Dialog
{
  /** These are copied from Dialog class, where they are private. */
  public static final String DIALOG_FONT_DATA = "DIALOG_FONT_NAME"; //$NON-NLS-1$
  public static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
  public static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$

  /**
    * Mostly a copy of the same method in Dialog, but with a call to a separate
    * method for providing a default size that is used if no persisted dialog
    * settings are available.
    * 
    * @see org.eclipse.jface.dialogs.Dialog#getInitialSize()
    */
  @Override
  protected Point getInitialSize()
  {
    Point result = getDefaultSize();

    // Check the dialog settings for a stored size.
    if((getDialogBoundsStrategy() & DIALOG_PERSISTSIZE) != 0)
    {
      IDialogSettings settings = getDialogBoundsSettings();

      if(settings != null)
      {
        // Check that the dialog font matches the font used
        // when the bounds was stored. If the font has changed,
        // we do not honor the stored settings.
        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132821
        boolean useStoredBounds = true;
        String previousDialogFontData = settings.get(DIALOG_FONT_DATA);

        // There is a previously stored font, so we will check it.
        // Note that if we haven't stored the font before, then we will
        // use the stored bounds. This allows restoring of dialog bounds
        // that were stored before we started storing the fontdata.
        if(previousDialogFontData != null && previousDialogFontData.length() > 0)
        {
          FontData[] fontDatas = JFaceResources.getDialogFont().getFontData();

          if(fontDatas.length > 0)
          {
            String currentDialogFontData = fontDatas[0].toString();
            useStoredBounds = currentDialogFontData.equalsIgnoreCase(previousDialogFontData);
          }
        }

        if(useStoredBounds)
        {
          try
          {
            // Get the stored width and height.
            int width = settings.getInt(DIALOG_WIDTH);

            if(width != DIALOG_DEFAULT_BOUNDS)
            {
              result.x = width;
            }

            int height = settings.getInt(DIALOG_HEIGHT);

            if(height != DIALOG_DEFAULT_BOUNDS)
            {
              result.y = height;
            }
          }
          catch(NumberFormatException e)
          {
          }
        }
      }
    }

    // No attempt is made to constrain the bounds. The default
    // constraining behavior in Window will be used.
    return result;
  }

  /**
    * Provides the dialog's default size. Duplicates the behaviour of JFace's
    * standard dialog. Subclasses may override.
    * 
    * @return Default size.
    */
  protected Point getDefaultSize()
  {
    return getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
  }
}
Posted in Eclipse | Tagged , , | Leave a comment

Internationalised front page in Drupal

Note: The following applies specifically to Drupal 7. There are plenty of howtos on this subject to be found on Google, but many of them refer to older Drupal versions, and few are clear on what to do if you’re not already familiar with the concept of multilingual variables in the first place. This Stack Overflow thread finally made things clear to me.

I’ve finally switched our choir’s website to a bilingual version. Formerly, the entire site was in German only; now, links in the header allow visitors to switch between German and English. As Drupal keeps track of its nodes and knows which two nodes are different language variants of the same content, menu entries work consistently and switching the language while “in” the site takes you to the selected language version of the correct page, i.e. the same one you were on before the language switch, only now in your chosen language.

The front page was a bit tricky, though.

Drupal knows that the front page is the default page (i.e. the one it takes you to when you don’t have a specific node id or name in your URL – e.g. http://www.amerlingchor.at/ or http://www.amerlingchor.at/en/), and it sets its links accordingly. Even if you navigated directly to the front page node (with the node name in the URL, like so: http://www.amerlingchor.at/de/startseite), the language switcher block, when displayed on the front page, will link to the unspecific URL (http://www.amerlingchor.at/de/ and http://www.amerlingchor.at/en/). That’s fine, as long as Drupal knows how to handle the default front page depending on the selected language. But by default it doesn’t.

The default front page is configured on the /admin/config/system/site-information page, under “Default front page”. Note that the text field is prefixed by a base URL, and on a multilingual site, that includes a language selector, like so: http://www.amerlingchor.at/de.

To make this setting multilingual, you need the variable translation module. Once this is enabled, the variables section of the multilingual settings page (/admin/config/regional/i18n/variable) allows you to pick which system variables should have different values for different languages. “Default front page” enables multilingual values for the setting we’re interested in.

After activating this variable and going back to the site information page, Drupal informs you that the default front page is now indeed a multilingual variable:

Optionally, specify a relative URL to display as the front page. Leave blank to display the default content feed. This is a multilingual variable.

This means that the settings page can now be switched between languages, and in each case, the default front page for the selected language can be specified. To easily switch the languages, the page now includes a list of language links at the top, but those don’t seem to work. Instead, manually edited the URL to switch to your desired language, e.g. by changing http://www.amerlingchor.at/de/admin/config/system/site-information to http://www.amerlingchor.at/en/admin/config/system/site-information. You will see that the value of the default front page field changes depending on the language, as will the language in the base URL displayed in front of the field. Just configure the correct path for each language, and you’re set. The node paths can be anything you like, and the front pages in different languages don’t necessarily have to use the same name.

Posted in Drupal | Tagged , , | Leave a comment

Stamping images with imagemagick

One of the reasons for starting a blog was to be able to write down stuff for future reference. Even if there’s a better guide elsewhere on the web, at least I’ll have a place where I know I can find the solution I found a year later without having to search for it.

So here’s a brief introduction to ImageMagick, found mostly by some Googling and trial & error rather than structured reading of manuals.

The goal

I want to upload some audio clips to YouTube. Nothing pirated, but official concert recordings of the choir I sing in. For a few of those, we actually have videos, but the rest of them should have a static image, composed of a group photo from the concert where the clip was recorded, the choir’s logo and a label displaying the title of the piece. As I need this for several clips and don’t want to mess around manually with GIMP every time, I wrote a little ImageMagick script to perform the task.

Requirements

I’m working with ImageMagick 6.6.9-7. I’m sure older versions will do, as I only used fairly standard functionality, but I didn’t do any research as to what the minimum required version is. ImageMagick is available for Windows, Apple and Unix systems, so the following should work pretty much everywhere. The final script is a Bash script, so Windows would require some tweaking.

To install ImageMagick in Ubuntu, if you don’t already have it, just do this:

sudo apt-get install imagemagick

Resources

As described above, I have two images and a text string and want to combine them into one stamped image. The base image is a group photo of the respective concert. That means that it different audio clips may use different base images. I’ve decided to stick to a 1280×720 resolution for all of them though, so all positioning numbers are based on this.

Here’s an example of a base image:

The name and path to the base image will be passed to the script as a parameter.

Next, the logo. To stay flexible, I’m using the full resolution version of our logo as the source and let ImageMagick scale it to my desired size. This’ll make it easier to change sizes later on if I want to.

Here’s the logo:

The logo file will be named logo-amerlingchor.png and placed in the same directory as the script.

Note that it’s a simple black text with transparent background. This’ll make it easy to stamp a white version of it with a black shadow on top of the base image.

Finally the label. We decided to use Century Gothic Italic as the font, based on our CD cover designs. The label should be stamped left aligned at the bottom of the base image, with a black background. After some experimenting I decided on a text size of 30, with height of 50 pixels for the background rectangle.

I installed the TTF file of the required font via KDE’s font installer to make it available to ImageMagick.

ImageMagick operations

ImageMagick has a few command line tools to convert and compose images. My first version of the script used the convert tool to stamp the text on the base image and store the result in a temporary file. Then the composite tool was called stamp the logo on top of the temp image and store the result to the target file.

The final version uses convert‘s ability to handle a list of operations to create the entire image with just one call.

Parameters

The script has three parameters:

  1. $source – The path and name of the source image.
  2. $text – The text to display in the label.
  3. $dest – The path and name of the image file that should be written by the script.

Base image

I start by calling convert and providing the base image:

convert -size 1280x720 $source

Label

Next, I specify the font family and size. To figure out the name of the available fonts, I used the following command:

convert -list font

So the font setup parameters look like this:

-pointsize 30
-font "Century-Gothic-Italic"

The draw operation allows be to draw both the black background rectangle and the white text on top of it:

-draw "gravity southwest \
       fill black rectangle 0,670,1280,720 \
       fill white text 15,10 \"$text\""

gravity southwest specifies the bottom left corner as the origin for relative coordinates. rectangle seems to use absolute coordinates with 0/0 being the top left corner of the image (so 0/670 is the top left corner and 1280/720 the bottom right corner of a 50 pixel high rectangle spanning the full width), but the text operation uses the specified origin, so 15/10 offsets the label by 15 pixels from the left edge and by 10 pixels from the bottom edge.

Logo

Finally, the logo. I’ve decided on a width of 350 pixels and an offset of 20/10 from the top left corner. I could use the following line to simply stamp the logo image there with these parameters:

logo-amerlingchor.png -geometry 350x200+20+10 -composite

Note that the specified vertical size – 200 pixels – is ignored because the scaled image maintains its aspect ratio. 0 would result in an empty image though, so I just used a random number roughly (very roughly) in the correct range.

The above command would stamp the black logo on the base image. I want a white image with a black background, though. The logical way to do this would be to compose a logo stamp by placing a white version of the logo on top of the black version, offset by a few pixels, and then to stamp the result on the base image. To create a white version of the logo, the -negate option comes in handy, which simply inverts all colours and keeps the transparent pixels transparent. For logo using only the colour black, this results in a white image. With a coloured logo as the starting point, the correct approach would be to create a matching shadow by using an all-black version of the original image.

I’m lazy though, so I didn’t compose the shadowed logo in advance. Instead, I simply stamp the logo on top of the base image twice:

logo-amerlingchor.png -geometry 350x200+22+12 -negate -composite
logo-amerlingchor.png -geometry 350x200+20+10 -negate -composite

The fact that the -negate operation is its own inverse operation is the key here. The first line stamps the black logo on the base image and then inverts the entire image, resulting in a negative version of the base image with a white logo. The second line stamps the original black version of the logo on top of this and again inverts the entire image – resulting in a real colour (twice inverted) version of the base image with a black (twice inverted) version of the logo and on top of that a white (once inverted) version of the logo.

The coordinates offset the shadow from the white logo by two pixels in both dimensions.

And this is what the result looks like:

The script

#!/bin/sh

source=$1
text=$2
dest=$3

convert -size 1280x720 $source \
  -pointsize 30 \
  -font "Century-Gothic-Italic" \
  -draw "gravity southwest \
         fill black rectangle 0,670,1280,720 \
         fill white text 15,10 \"$text\"" \
  logo-amerlingchor.png -geometry 350x200+22+12 -negate -composite \
  logo-amerlingchor.png -geometry 350x200+20+10 -negate -composite \
  $dest

For the above image, I call it like this:

./stamp.sh group_photo.jpg "Pierre Attaignant: Tourdion" tourdion.png
Posted in Graphics | Tagged , | 1 Comment

Mapping KDE actions to extra mouse buttons

Note: The following is written for Ubuntu 12.04 and KDE 4. Other distributions will behave similarly, and the described solution can be used for software other than KDE as well, but the specific details may differ in those cases. For more details, please refer to this article, which has been my primary source and describes the used tools in much more detail.

The issue

One of the features I’ve always particularly liked about KDE is the fact that you can assign (and reconfigure) keyboard shortcuts to pretty much anything. Once you’ve gotten used to pressing Ctrl+Alt+A to switch to the latest Window that requested focus (e.g. an instant message that arrived while you were typing an email), you’ll miss that feature on any system that doesn’t have it.

When KDE 4 first came out, its new composite desktop effects were all the rage. After a few release cycles they became stable enough to actually use them, too. But I soon realised that with all the cool looking new window switchers, I still preferred the classic list of icons with title labels for Alt-Tabbing through my desktop. Stuff like the Present Windows effect looked cool, but was pretty useless with my hands on the keyboard. And when I had my right hand on the mouse, I wasn’t keen on pressing some keyboard combination to trigger an effect to use with the mouse.

Modern power user mice have lots of buttons. So why not use those buttons as shortcuts to trigger certain actions? The problem is that KDE only knows about the three primary mouse buttons (plus the scroll wheel), supposedly due to limitations of the underlying QT libraries. Even though X11 has no problems recognising the additional buttons, KDE itself won’t handle them. And even if it did – it still doesn’t let you use mouse buttons as shortcuts. The shortcut configuration widget will only react to keyboard input.

Requirements

The clean solution would obviously be to simply have the shortcut configuration widget accept keyboard keys as well as mouse buttons (all of them), and other input events like gamepad buttons etc. as well. But as long as that’s not an option, we can simply trick X11 to produce arbitrary keyboard events when a specific mouse button is pressed.

In addition to the standard KDE installation, this solution requires two additional packages, xbindkeys and xautomation:

sudo apt-get install xbindkeys xautomation

xbindkeys provides a demon for translating keys (in our case: mouse button events) to actions (in our case: keyboard key combinations). When installed, the current Ubuntu version will start it automatically with X11/KDE.

Configuration

To define the mappings, xbindkeys uses a configuration file .xbindkeysrc in the user home directory. A template file can be created easily:

xbindkeys --defaults > ~/.xbindkeysrc

We can now edit this file to bind mouse buttons to key combinations. The default template file already contains a few example mappings which should make the syntax quite clear: The first line of each mapping defines the action to execute, the second (indented) line specifies which event should trigger the action. A blank line completes the mapping. Any text after a hash character (#) is a comment and will be ignored by the demon.

The xte tool from the xautomation package can be used to produce key combination event sequences. For example, the following command line will have the same effect as a user pressing the F1 key:

xte "key A"

There is another tool that can be used to determine the mouse button IDs. Running xev will open a small window which listens to mouse events and logs them to the console. Simply pressing a mouse button in that window will produce a log message showing the button’s ID.

My Logitech mouse has two buttons which by default are recognised as back and forward actions by browsers. I never use them to trigger these actions (I usually use Alt+Left and Alt+Right), so I want to map them to something else. KDE’s Desktop Grid and Present Windows switchers are neat effects which I would like to trigger via these buttons. The default shortcuts for these effects are Ctrl+F8 and Ctrl+F9.

The following lines will map mouse button 8 to Ctrl+F9 and mouse button 9 to Ctrl+F8:

# Bind "back" mouse button to Ctrl+F8
"xte 'keydown Control_L' 'key F8' 'keyup Control_L'"
  b:9

# Bind "forward" mouse button to Ctrl+F9
"xte 'keydown Control_L' 'key F9' 'keyup Control_L'"
  b:8

To make xbindkeys re-read the configuration file without restarting the X server, simply execute this:

killall xbindkeys && xbindkeys

Voilà: Desktop Grid and Present Windows at your fingertips!

More features

Different mappings can easily be derived from these lines. For example, if you’re not using the left/right tilt clicks of your mouse wheel for horizontal scrolling, you could map buttons 6 and 7 to actions of your choice.

Instead of mapping the mouse buttons directly to the configured keyboard shortcuts, you could alternatively map them to rather obscure key combinations like the following:

# Bind "scroll right" mouse wheel click to Super+Home
 "xte 'keydown Super_L' 'key Home' 'keyup Super_L'"
 b:7

You can then use KDE’s standard keyboard shortcut configuration dialogues to bind the mouse buttons to actions by pressing a mouse button over the desired shortcut configuration widget. This will enter the bound key combination to the selected action.

Posted in Computers, KDE, Linux | Tagged , , | 10 Comments

Blog! Blog!