Thursday, March 18, 2010

Converting My Swing Break Reminder to Groovy: Part 1



I have ergonomic muscle strain issues. Experts recommend that you take breaks every 15 minutes to alleviate that pain, and give your muscles a rest. So, a long time ago, I wrote a small java app that would remind me to take a break every fifteen minutes. Here is the code for it:

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowStateListener;
import java.awt.event.WindowEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Date;
import java.util.Locale;
import java.util.ArrayList;
import java.text.NumberFormat;
import java.text.DecimalFormat;
import com.ocpsoft.pretty.time.BasicTimeFormat;
import com.ocpsoft.pretty.time.TimeFormat;
import com.ocpsoft.pretty.time.TimeUnit;
import com.ocpsoft.pretty.time.PrettyTime;
import com.ocpsoft.pretty.time.units.Second;
import com.ocpsoft.pretty.time.units.Minute;

/**
* Created by IntelliJ IDEA.
* User: BrentFisher
* Date: Aug 18, 2009
* Time: 2:07:18 PM
* To change this template use File | Settings | File Templates.
*/
public class TimeReminderWindow {

public static void main(String [] args){
TimeReminderWindow trw = new TimeReminderWindow();
trw.start();

}

long timeToWait = 15 * 60 * 1000;// 15 minutes
long lastUpdate = System.currentTimeMillis();

private void start() {
final JFrame frame = new JFrame("Time Reminder");
final JButton button = new JButton("Click here to reset");
final PrettyTime p = new PrettyTime();

frame.getContentPane().add(new JLabel("Take a break",SwingConstants.CENTER), BorderLayout.CENTER);
frame.getContentPane().add(button, BorderLayout.NORTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame.setBounds(screenSize.width/2 - (640/2),
screenSize.height/2 - (480/2),640,480);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
lastUpdate = System.currentTimeMillis();
frame.setState(JFrame.ICONIFIED);
}
});
Timer timer = new Timer(1000, new ActionListener(){
public void actionPerformed(ActionEvent e) {
long now = System.currentTimeMillis();
if(now - lastUpdate > timeToWait){
frame.setState(JFrame.NORMAL);
frame.toFront();
}
frame.setTitle("Break " + p.format(new Date(lastUpdate + timeToWait)));

}
});
lastUpdate = System.currentTimeMillis();
timer.start();
// frame.pack();
frame.setVisible(true);
}
}


The Book Groovy in Action claims that writing Swing code in Java is three to four times longer. So, I'm gonna put that to the test in this article. Plus, I've always wanted to put in some helpful ergonomic reminders in too, so I'll see if I can do that.

Here is the code, and here is the reminder window:

import javax.swing.*;
import java.awt.*;
import java.util.Date;
import com.ocpsoft.pretty.time.PrettyTime;
import groovy.swing.SwingBuilder;
import java.awt.BorderLayout as BL
import java.awt.event.ActionListener
import java.awt.event.ActionEvent

long timeToWait = 15 * 60 * 1000;// 15 minutes
long lastUpdate = System.currentTimeMillis();
def swing = new SwingBuilder()
final PrettyTime p = new PrettyTime();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
def button1 = swing.button( 'Click here to reset')

def frame = swing.frame(title: 'Time Reminder',
defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
bounds: new Rectangle((int) screenSize.width / 2 - (640 / 2),
(int) screenSize.height / 2 - (480 / 2), 640, 480)) {
panel(layout: new BL()) {
widget(button1,constraints: BL.NORTH)
label constraints: BL.CENTER, horizontalAlignment: SwingConstants.CENTER, 'Take a break'
}
}
button1.actionPerformed = {
lastUpdate = System.currentTimeMillis();
frame.setState(JFrame.ICONIFIED);
}

Timer timer = new Timer(1000, {ActionEvent e ->
long now = System.currentTimeMillis();
if (now - lastUpdate > timeToWait) {
frame.setState(JFrame.NORMAL);
frame.toFront();
}
frame.setTitle("Break ${p.format(new Date(lastUpdate + timeToWait))}");
} as ActionListener);

lastUpdate = System.currentTimeMillis();
timer.start();
// frame.pack();
frame.setVisible(true);



But I have to say, the whole part of getting the button registered for action performed was pretty tricky and I spent more than half an hour doing it. The code was reduced to 43 lines from 71, but the builder part was actually trickier than I thought it would be, and both use 15 lines of code. So, I'm not completely sold on its usefulness.

Part II will be to query Google for some ergonomic images, and automatically put those on the screen. Stay tuned...