Keilly: November 2008

Sunday, November 30, 2008

Listen Again v3.4



Update: Widget currently broken due to BBC iPlayer update. Hope to get a fix soon....

Listen to the large archive of recent BBC Radio programmes directly from Dashboard.

Currently supports: Radio 1, 1xtra, Radio 2, Radio 3, Radio 4, 5 live, 5 extra, 6 Music, 7, Asian Network, Radio Scotland, Radio Ulster, Radio Wales

Requires RealPlayer 10.1
Update, RealPlayer 11 doesn't play when Dashboard is hidden. Use RealPlayer 10.1

Download BBCListenAgain Widget

Shortcut Keys:
Space - Play/Pause
Up/Down - Volume
Left/Right - Skip
s - Stop
v - Voiceover feedback
p - Focus on station popup

What's new:
3.4
- Fixed to work with another iPlayer change
- Fixed 'Listen Live'

3.33
- Fixed to work with another iPlayer change
- Added auto update mechanism
- Pressing the 'v' key will enable additional spoken feedback.

3.31
- Descriptions of programmes fixed (broken due to BBC slightly rearranging their iPlayer content)

3.3
- Works with new BBC iPlayer website
- Accessibility: Voiceover use is detected and certain actions are spoken if turned on.
- Better font resizing
- Busy feedback

This has been developed and tested on Mac 10.5

VoiceOver users: when selecting stations use VoiceOver keys with the arrows to select the station from the menu.

Troubleshooting:
Try and play directly from the BBC website. If it works there then it will almost certainly work in Dashboard. If not the problem has historically been issues with RealPlayer - try uninstalling and reinstalling RealPlayer using the version from the BBC website. Reboot after the reinstall to restart Dashboard.

Let me know any other problems.

Mac OS X 10.4 Tiger is required. If you're using Safari, click the download link. When the widget download is complete, Show Dashboard, click the Plus sign to display the Widget Bar and click the widget's icon in the Widget Bar to open it. If you're using a browser other than Safari, click the download link. When the widget download is complete, unarchive it and place it in /Library/Widgets/ in your home folder. Show Dashboard, click the Plus sign to display the Widget Bar and click the widget's icon in the Widget Bar to open it.

Thursday, November 20, 2008

MouseEvents for non-rectangluar components in Swing

Warning: Swing hack ahead!

In Swing component bounds are defined as x,y,width,height, i.e. rectangular.

Also in Swing it's very hard to trap mouse events only for a particular section of a component. Sure we can test in our mouse listener if the location of the event is in a particular section, but this isn't always good enough because the mere act of receiving a mouse event means that the parents of that component never get a crack at processing the event. (See here).

This means that in Swing it is very difficult to simulate non-rectangular components.

Consider the resize grip area in this custom text area:

Although it looks triangular, the grip is actually a (rectangular) component layered in front of the text area. We want the user to be able to grab the grip and drag to resize, but also click in the non-grip area and interact directly with the text field.

This is what we have by default:


This is what we want:


Visually its easy to split into two: make the component non-opaque and paint the corner section with some grip graphics in the paintComponent method - letting the parent show through in the unpainted corner.

However event-wise there's no API to split a component into responsive and non-responsive areas. It would be nice for example if JComponent.processMouseEvent call returned a boolean indicating if the event was actually processed. It doesn't, so we have to hack in a fix.

Luckily Component.getCursor() is continually polled as the mouse cursor moves over a component. Overriding this we can detect where the mouse location is over our grip area and act accordingly.
If it's over the visible grip area we can add a mouse listener and start receiving events, if it's over the transparent area we remove the listener and the events are automatically routed to the parent.

Here's the code for the grip:

public Cursor getCursor(){
PointerInfo info = MouseInfo.getPointerInfo();
Point p = info.getLocation();
SwingUtilities.convertPointFromScreen(p, this);

// Test if in the visible area
if (p.x+p.y > getHeight()){
if (!Arrays.asList(getMouseListeners()).contains(ml))
addMouseListener(ml);

return Cursor.getPredefinedCursor(
Cursor.SE_RESIZE_CURSOR);
}

// In the transparent area, stop listening
if (Arrays.asList(getMouseListeners()).contains(ml))
removeMouseListener(ml);

return super.getCursor();
}

Of course you can make the location test as complex as you like creating components of any shape.