Tutorials

Adjusting a JScrollPane with nested child JComponents

In Robot Overlord have a JScrollPane in Java Swing and inside it is a JPanel that contains other JPanels and so on and way WAY down at the bottom were the selectable elements like JTextField and JButton. When I hit the TAB key (or SHIFT+TAB) to move focus between components I want the newly focussed component to stay on screen – in other words, make the computer move the scroll bars just enough to bring the focus component into view in the Viewport.

The top answer I could find on Stack Exchange said to do either

// either call on child with zero offset
child.scrollRectToVisible(new Rectangle(child.getSize());
// or call on child's parent with child bounds
child.getParent().scrollRectToVisible(child.getBounds());

Which sounds great, but doesn’t work with nested items. JComponent.getSize() won’t return the absolute position of the component relative to the top-most JPanel in the Viewport. JComponent.getBounds() is slightly better, with an X and Y value relative to its parent.

The solution

Recursion to the rescue!

public void focusGained(FocusEvent e) {
    // the thing that got focus
    Component c = e.getComponent();
    // the bounds of the element
    Rectangle rec = c.getBounds();
    // add up all the parent offsets until we get to the JScrollPane.
    Container c0 = null;
    Container c1 = c.getParent();
    while( (c1!=null) && !(c1 instanceof JScrollPane) ) {
        Rectangle r2 = c1.getBounds();
        rec.x += r2.x;
        rec.y += r2.y;
        // don't forget the last component before the JScrollPane.
        // we'll need it later.
        c0 = c1;
        c1 = c1.getParent();
    }
    ((JComponent)c0).scrollRectToVisible(rec);
}

It would have been really nice if calling scrollRectToVisible() on the first component would work its way up the parent chain automatically, but until that beautiful day this is your workaround.

Tutorials

How to quickly replace money_format() in your PHP

If you’ve been using money_format() in your PHP code you may have recently (as of PHP 7.4.0) seen that it is now deprecated and the new hotness revolves around a class called NumberFormatter. Here’s how I quickly refactored to make the booboo go away.

I have a scheduled script that runs once a month to recalculate How much money you’d make if you bought Netflix shares instead of a Netflix subscription. Naturally, it uses money_format(formatString,value) all over the place. I didn’t want to manually change every instance, so….

At the top of my PHP file I added the following:

$fmt = numfmt_create( 'en-US', NumberFormatter::CURRENCY );
$symbol = $fmt->getSymbol(NumberFormatter::INTL_CURRENCY_SYMBOL);

function money_format($ignore,$value) {
        global $fmt,$symbol;

        return $fmt->formatCurrency($value,$symbol);
}

Then (because I am a VI user) I replaced all:

:%s/money_format/money_format2/g

Which changes money_format() I wrote in the first part into money_format2(), but it does that to everything everywhere in the file, so it’s all good. Merely redeclaring money_format() makes PHP sad.

because I am running a LAMP stack (Linux/Apache/MySQL/PHP) I had to also install php-intl:

$ sudo apt-get install php-intl

So there you go! Easy peasy, lemon squeezy. If you liked that share with your friends and so on.