Porting Simple Calculator from Ruby Shoes to Griffon

In this post, we're going to continue our series of ports and create a simple calculator. With the exception of a gradient painter to duplicate the effect in the Shoes example, we will again use stock Swing. Here is the requisite screenshot.


View the Ruby code here

Our model for this example is relatively simple with variables for the current number displayed on the calculator, the previous number, and the operation.

SimpleCalculatorModel.groovy

 
import groovy.beans.Bindable

class SimpleCalcModel {
   @Bindable number = 0 
   @Bindable previous = null
   @Bindable op = null
   
}

You may have noticed the @Bindable annotation in previous examples. For this port, we're going to break out the training wheels and take it for a spin. @Bindable automagically wraps the variable with a PropertyChangeLister which fires an event whenever that variable is changed. We can bind that variable to a component in our user interface to make sure it is constantly updated. That is what we are doing in our view.

SimpleCalculatorView.groovy

 
import java.awt.Font
import java.awt.Color
import java.awt.GradientPaint
import org.jdesktop.swingx.painter.*

application(title:'SimpleCalc',
  //size:[320,480],
  pack:true,
  //location:[50,50],
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image]
) {
	jxpanel(backgroundPainter:new MattePainter(
           new GradientPaint(
               200,0,
               new Color(238,238,204), 
               200,200,
               new Color(153,153,102)))){
	    vbox {
		hbox {
			label(font:new Font("Arial", Font.BOLD, 24), text:bind{model.number})
		}
		hbox {
			button("7", actionPerformed:{controller.pressNumber(7)})
			button("8", actionPerformed:{controller.pressNumber(8)})
			button("9", actionPerformed:{controller.pressNumber(9)})
			button("/", actionPerformed:{controller.doOperation("/")})
		}
		hbox {
			button("4", actionPerformed:{controller.pressNumber(4)})
			button("5", actionPerformed:{controller.pressNumber(5)})
			button("6", actionPerformed:{controller.pressNumber(6)})
			button("*", actionPerformed:{controller.doOperation("*")})
		}
		hbox {
			button("1", actionPerformed:{controller.pressNumber(1)})
			button("2", actionPerformed:{controller.pressNumber(2)})
			button("3", actionPerformed:{controller.pressNumber(3)})
			button("-", actionPerformed:{controller.doOperation("-")})
		}
		hbox {
			button("0", actionPerformed:{controller.pressNumber(0)})
			button("Clr", actionPerformed:{controller.clear()})
			button("=", actionPerformed:{controller.pressEquals()})
			button("+", actionPerformed:{controller.doOperation("+")})
		}
	  }
	}
}

As we'll see in the controller (below), all our operations act on our model variables and the text label is bound to the value stored in model.number. Paints are merely a short hand way to draw a matte color or paint, pinstripes, checkerboards, alpha values, or images. The class we are using to create our GradientPaint is a part of core Java but SwingX makes it a bit easier to use it. SwingX components are outside core Java and as such are a bit more robust and next-gen. They include various painters and shaped backgrounds, glass panes, login frames, authentication, and busy labels. To install the SwingXBuilder plugin, type griffon install-plugin swingx-builder at a command prompt (or select it from the menu if using NB). That command will import the SwingX components into a "jx" namespace so that they will not conflict with core Swing components and can be used along side them in the same user interface. The first, second, fourth, and fifth values correspond to the x1, y1 and x2, y2 positions in user space for the gradient and determine the direction and intensity.

SimpleCalculatorController.groovy

 
class SimpleCalcController {
    // these will be injected by Griffon
    def model
    def view

    void mvcGroupInit(Map args) {
        // this method is called after model and view are injected
    }

    def pressNumber = { num ->
		model.number = (model.number * 10) + num
	}
	
	def clear = {
		model.number = 0
	}
	
	def pressEquals = {
		switch (model.op) {
			case '*':
				model.number = (model.previous).multiply(model.number)
				break
			case '/':
				model.number = (model.previous).div(model.number)
				break
			case '+':
				model.number = (model.previous).plus(model.number)
				break
			case '-':
				model.number = (model.previous).minus(model.number)
				break
		}
		model.previous = null
		model.op = null
	}
	
	def doOperation = { op ->
		if (model.op) {
			pressEquals()
		} else {
			model.op = op
			model.previous = model.number 
			model.number = 0
		}
	}
	
}

Download the source code here.

Porting Simple Dialogs from Ruby Shoes to Griffon

Sometimes porting a project to Griffon yields less code. Some might even argue that it does most of the time. Unfortunately, this isn't one of those cases. So you might be wondering why I still decided to blog about the port even though it doesn't paint Griffon in the best light. Despite the increase in lines of code, I thought the example exposed some aspects of Swing that people might not know about. First the obligatory screen shots and Ruby code:

Simple dialogs constructs a window with a set of buttons each exposing a different dialog box type. Similar to the Simple Timer example, our SimpleDialogsModel has a single variable for a fileChooser that we will reuse across four of our buttons.

SimpleDialogsModel.groovy

 
import groovy.beans.Bindable
import javax.swing.JFileChooser

class SimpleDialogsModel {
   @Bindable fileChooser = new JFileChooser()
}

Our view file is a little more complex this time but to make it more readable, I moved all actions over 2 lines long to the controller. We should be promoting proper MVC design anyways. FlowLayout tells the application to layout components next to each other and auto-wrap based on the dimensions of the frame. The results of interacting with the dialog boxes is populated in result label when a dialog is closed.

SimpleDialogsView.groovy

 
import java.awt.FlowLayout
import java.awt.Color
import javax.swing.JOptionPane
import static javax.swing.JFileChooser.*
import javax.swing.JColorChooser

application(title:'SimpleDialogs',
  size:[300,150],
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image],
  layout:new FlowLayout()
) {
    button(text:'Ask', actionPerformed: {
		def result = JOptionPane.showInputDialog("What is your name?")
		view.result.text = "Your name is ${result}"
	})
	button(text:'Confirm', actionPerformed: {controller.confirm()})
	button(text:'Open File...', actionPerformed: {
		controller.openSaveFileOrDir('Open a File...', true, FILES_ONLY)
	})
	button(text:'Save File...',actionPerformed: {
		controller.openSaveFileOrDir('Open a File...', false, FILES_ONLY)
	})
	
	button(text:'Open Folder...', actionPerformed: {
		controller.openSaveFileOrDir('Open a Folder...', true, DIRECTORIES_ONLY)
	})
	button(text:'Save Folder...',actionPerformed: {
		controller.openSaveFileOrDir('Save a Folder...', false, DIRECTORIES_ONLY)
	})
	button(text:'Color', actionPerformed: {
		def color = JColorChooser.showDialog(null, "Pick a Color", Color.WHITE)
		view.result.text = "You selected R:${color.red},G:${color.green},B:${color.blue}"
	})
	label(id:'result')
}

JOptionPane is a great kitchen sink class for creating dialogs. It has presets for inputDialogs and we see in SimpleDialogsView but also confirm dialogs and message dialogs, some of which we will see later. Another great find is JColorChooser which shows a spiffy color selection tool that returns a java.awt.Color object when you select a color.

SimpleDialogsController.groovy

 
import javax.swing.JOptionPane
import static javax.swing.JOptionPane.*
import javax.swing.JFileChooser

class SimpleDialogsController {
    // these will be injected by Griffon
    def model
    def view

    void mvcGroupInit(Map args) {
        // this method is called after model and view are injected
    }
	
	def confirm = {
		def result = JOptionPane.showConfirmDialog(null,"Would you like to proceed?")
		switch(result) {
			case YES_OPTION:
				view.result.text = "You selected Yes."
				break
			case NO_OPTION:
				view.result.text = "You selected No."
				break
			case CANCEL_OPTION:
				view.result.text = "You selected Cancel."
				break
               }
	}

	
	def openSaveFileOrDir = { msg, openFile, selectionMode ->
		def result
		model.fileChooser.setDialogTitle(msg)
		model.fileChooser.setFileSelectionMode(selectionMode)
		if (openFile)
			result = model.fileChooser.showOpenDialog(null)
		else result = model.fileChooser.showSaveDialog(null)
		if (result == JFileChooser.APPROVE_OPTION) {
			def file = model.fileChooser.getSelectedFile()
			view.result.text = "You selected ${file.getName()}"
		}
	}
}

Our controller consists of two closures, a simple confirm closure to show a confirm dialog and respond to an answer and a slightly more complex closure to open or save a directory or file(no writes are done, it just reads the file object's name).

Though the Shoes example wins in terms of lines of code, one could argue that the Griffon example gives you more granularity and exposes code that Shoes has hidden. You can download the code here.

Griffon Year One

"Build it and they will come" is probably one of the biggest lies you could tell yourself when it comes to open source. In this arena, the adage should really be, "Build it, market the sh*t out of it, engage and build a community, and they MAY come." So many cool projects have withered on the vine because the creators didn't properly pimp and promote them. That's my major takeaway from Griffon's first year: soft skills like giving presentations, blogging, and being adequately responsive to bug reports/questions are really important.

In one year's time, we've gained a couple core committers, a boatload of plugins, seen Griffon presentations take place in Austrailia, Europe, and all across North America, and decent IDE support. And we haven't reached 1.0 yet!

Year two is already looking to be really exciting. There are a bunch of presentations this fall and Griffon in Action drops in March.

Porting Simple Timer from Shoes to Griffon

Shoes is a GUI toolkit for making cross-platform desktop applications. While the number of widgets somewhat limited, the simplicity makes it easier to dive into. Partnered with Ruby's terseness, it makes for very concise and readable code. It was created by _why the lucky stiff whose recent disappearance caused much Sturm und Drang in the Ruby community. Luckily, it was one of the projects successfully archived in the whymirror on Github. In this post, I'm going to port one of the examples to Griffon. Simple Timer is one of the first examples from the material creating a colored window, two buttons and a text label that responds to events from those buttons.


Here is what the Ruby code looks like:

 
Shoes.app :height => 150, :width => 250 do
  background rgb(240, 250, 208)
  stack :margin => 10 do
    button "Start" do
      @time = Time.now
      @label.replace "Stop watch started at #@time"
    end
    button "Stop" do
      @label.replace "Stopped, ", strong("#{Time.now - @time}"), " seconds elapsed."
    end
    @label = para "Press ", strong("start"), " to begin timing."
  end
end
For the Griffon version, we'll need to break that code into a couple parts. That's not to say that we couldn't put everything in one file, it's just that doing so would violate Griffon's MVC design. There is only one variable in the Ruby file that is not backed by a UI element, time. In our Griffon version, that variable will live in SimpleTimerModel.

SimpleTimerModel.groovy

 
import groovy.beans.Bindable

class SimpleTimerModel {
   @Bindable time
}
Shoes' stack keyword most closely correlates to a vertical box in Griffon. On the Griffon side, Shoes do...end blocks become actions, in this case actionPerfomed.

SimpleTimerView.groovy

 
import java.awt.Color

application(title:'SimpleTimer',
  size:[250,150],
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image]
) { //Start of code we added
    panel(background: new Color(240, 250, 208)) {
		vbox {
			button(text:'Start', actionPerformed: {
				model.time = new java.util.Date()
				controller.wrapLabelText(view.status, "Stop watch started at ${model.time}")
			})
			button(text:'Stop', actionPerformed:{
				def elapsed = (new java.util.Date().getTime() - model.time.getTime())/1000.0f
				view.status.text = "<html>Stopped, <strong>${elapsed}</strong> seconds elapsed."
				
			})
			label(id:'status', text:'<html>Press <strong>start</strong> to begin timing</html>')
		}
	} // End of code we added 
}
Now for the slightly difficult part. Shoes autowraps text fields based on the parent container whereas Java Swing doesn't. After a little Google-ing, I found a sample that I had to tweak a little to get working in Griffon. To honor MVC and keep our view clean, that code lives in the controller.

SimpleTimerController.groovy

 
import java.awt.Container;
import java.awt.FontMetrics;
import java.text.BreakIterator;
import javax.swing.SwingUtilities;

class SimpleTimerController {
    // these will be injected by Griffon
    def model
    def view

    void mvcGroupInit(Map args) {
        // this method is called after model and view are injected
    }
	
	// tweaked from sample on http://www.geekyramblings.org/2005/06/30/wrap-jlabel-text/
	private void wrapLabelText(label, text) {
		FontMetrics fm = label.getFontMetrics(label.getFont())
		Container container = label.getParent()
		int containerWidth = container.getWidth()

		BreakIterator boundary = BreakIterator.getWordInstance()
		boundary.setText(text)

		StringBuffer trial = new StringBuffer()
		StringBuffer real = new StringBuffer("")

		def words = text.split(" ")

		for (word in words) {
			trial.append(" "+word)
			int trialWidth = SwingUtilities.computeStringWidth(fm,
				trial.toString());
			if (trialWidth > containerWidth) {
				trial = new StringBuffer(word)
				real.append("<br>")
			}
			real.append(" "+word)
		}

		label.setText(real.toString())
	}
}


Besides the code enabling line wrapping, our Griffon version is very similiar to the Shoes version both in lines of code and composition. You can download the code here on Github For those that don't already have Griffon installed, visit here.

Griffon at RuPy 2009

I've just gotten word that my abstract for RuPy on Griffon has been accepted. RuPy will take place in Poznan, Poland on November 7th and 8th. RuPy is one of the many places Griffon project members will be speaking this fall*:

September
Sept 27 - 30th SG ‘09 Conferencia y Expo Monterrey, Mexico
October
Oct 3 - 4th Silicon Valley Code Camp Los Altos, CA
Oct 19 - 22th SpringOne 2GX New Orleans, LA
Oct 22 - 23rd The Strange Loop St. Louis, MO
November
Nov 2 - 6th Oredev Malmö, Sweden
Nov 7 - 8th RuPy Poznan, Poland

*all of these might not be specifically Griffon talks

Flash for Swing Developers

It doesn't take one a long time to realize that Swing sometimes gets a bum rap. Developers had no idea how to handle the GUI thread and were prone to unknowingly block it. Add to that the fact that the default widget set LookAndFeel left much to be desired gave users the impression that Java Swing was kludgy, slow, and ugly.

Be that as it may, Swing has been around for over ten years now and not even the much ballyhooed introduction of JavaFX Script will be enough to kill it. That one core reason while Griffon doesn't try to do away with Swing. It says, "I know it will be around and there is a lot of legacy code in it, so let's use it in a more efficient way." I was happily surprised to find that there is a project for Flash that allows you to use Swing conventions and classes to create user interfaces in Flash. You don't even need a commercial application (Adobe Flash CS4 and the like) to get started.

You'll need to install the Flex SDK and add it to your classpath.
Grab AsWing libraries and extract the swc file into your working directory.

Create the following file as hello.as


package {
    import org.aswing.*;
    import org.aswing.geom.IntDimension;    
    import flash.display.*;
    import flash.text.*;
    
    public class hello extends Sprite {
        public function hello() {
            var frame:JFrame = new JFrame(this, "HelloWorld");            
            frame.setSize(new IntDimension(200,120));
            var label:JLabel = new JLabel("Hello, World!");
            
            frame.getContentPane().append(label);            
            addChild(frame);
            
            frame.show();
        }
    }
}


From the command-line run: mxmlc -include-libraries=AsWingA3.swc hello.as

That should create a swf file in the same directory that, when run in the browser shows a frame with a label that says "Hello, World!". The above code should seem very familiar to Swing developers. The only key differences are the lines adding the frame and showing the frame.

In AsWing and in regular Flash, the top-level element is the Stage. addChild(frame) adds our top-level element for user interaction to that Stage.

frame.show() is deprecated in conventional Java but in Groovy, it acts as an alias to frame.setVisible(true). Of minor mention is the frame.getContentPane().append(label) line. Actionscript uses append instead of add that Swing prefers.

 

GTUG Campout and Trippy the travel assistant

I just got back from an very awesome GTUG Campout. The goal of the weekend was to code a functional Google app during the course of the weekend. On Friday, there were a couple of Wave tutorials but the balance of the weekend was all about hacking. Most of the teams chose to use Google Wave but there were a couple Android and general App Engine teams(Wave robots run in AppEngine).

My team and I created Trippy, a virtual travel assistant. We had airline and hotel information polling from Kayak and only after meticulously coding it and a handcoded XML to JSON parser (AppEngine doesn't like SAX or JDOM), we found that it horribly crashed on App Engine. Querying Yelp for points of interest in a given area was originally a wishlist feature but became the centerpiece of our demo.

How it works:
Trippy runs on the AppEngine and uses Google Wave to interact with users.
1. Trippy asks you what city you are interested in.
2. Trippy asks you activities you would like to do.
3. Trippy queries Yelp and returns the top three results for the keywords you entered including the name of the business, the phone number, address, and links to the business's review page on Yelp and a Google Map link.

To use Trippy:
Add him(trip-bot@appspot.com) to a wave and he'll start talking to you.

Trippy screenshot

We didn't win any prizes but I'm proud of what we accomplished in the weekend. Sometimes the best way to learn is to be thrown into the deep end and told to swim...thought a swimming analogy was apropos given the name of the technology.

You can wave me (wave to me, not quite sure of the proper verb yet) at j@wavesandbox.com. Yup, just j. The source code to Trippy is out there but needs to be cleaned up.
 
I have to thank the GTUG organizers and volunteers are awesome and organized a great event.
 

Ten tips to help you enjoy GTUG Campouts

1. Be ambitious.
Hello World apps don't win cool swag.

2. Know thy limits.
Having an ambitious idea is great but if you can't execute it all, scale it down to what is doable in the weekend.

3. Have at least Plans A, B, and C.
In our project, what was orginally an afterthought add-on turned out to be the centerpiece of the demo.

4. If you're getting more than 6 hours of sleep each night, you're not doing it right.
45 hours is not nearly as much time as you think.

5. Go outside and walk around for a couple minutes ever so often.
After spinning in mud for an hour or so, a quick walk around the Googleplex can make the solution come to you. Not recommended at night when the doors lock behind you.

6. Check you veganism at the door.
All those preservatives in that junk food keeps you going. Sure, there'll be rabbit food during meals but otherwise embrace the junk food.

7. Arrive early on day one to scope out the locations of outlets.
A laptop doing a bunch of builds on battery power is an uphappy laptop.

8. Learn to sense when a Shirley or another photog is near.
That way you can force their "candid" photos to feature your good angles hiding the bags under your eyes and acne you got from eating so much junk food.

9. Be interesting enough to be drawn by David Newman(@dnsf).
I loved his drawing of me at OpenSocial weekend so much that I use it as my twitter photo.

10. Be flexible. / Get in where you fit in.
If I were doing a project in my free time I might do it using Groovy in some shape or fashion. During GTUG Campout, people gravitate towards their programming LCD. In my case, it's Java. If your pitched idea doesn't attract a team, join someone else's. If your idea overlaps another team's, consider joining forces or at the very least co-locating so that you can work through common problems. One of the other teams had an idea that was only slightly similar to my team's but if we had partnered, we could have been that more awesome.

 

Using LWJGL with Griffon

LWJGL (Light-weight Java Game Library) is a game library that is the basis for many graphics libraries including jMonkeyEngine. You might be wondering why I'm covering LWJGL in a separate post when it is used in jMonkeyEngine which I covered several weeks ago. Though there is some overlap, each fits a specific need.

I'd describe the relationship like this: OpenGL/JOGL is a bicycle, LWJGL is a Vespa, and jME is a Harley. OpenGL is great if you want to touch the bare metal, LWJGL if you want to tweak and customize, jME if you singular goal is to make a game.

Getting started

  • Download LWJGL and drop the lwjgl.jar in your lib directory.
  • Find the native files for your operating system and extract them into lib/native
  • Download jME 2.0 Distribution. We aren't doing that much advanced stuff so we only need jme.jar, jinput.jar, and jme-awt.jar. Drop these into your lib directory.
  • Modify your Config.groovy file to contain the following:
     
    griffon {
       app {
        javaOpts = ["-Djava.library.path=${basedir}/lib/native"]
       }
    }
    

Linking our model and view

Our model and view are going to be fairly basic and initiate a model property called canvas that is added to the view. We don't need to do anything in our controller.

SimpleLWJGLModel.groovy

 
import groovy.beans.Bindable
 
class SimpleLWJGLModel {
   @Bindable canvas = new SimpleCanvas()
}

SimpleLWJGLView.groovy

 
application(title:'SimpleLWJGL',
  size:[300,300],
  resizable:false,
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image]
) {
    widget(model.canvas)
}

Drawing on our canvas

Here we draw a rotating quad.

 
import org.lwjgl.LWJGLException
import org.lwjgl.opengl.AWTGLCanvas
import org.lwjgl.opengl.Display
import org.lwjgl.opengl.GL11

public class SimpleCanvas extends AWTGLCanvas {
 
    float angle = 0
 
    public SimpleCanvas() throws LWJGLException {
      // Launch a thread to repaint the canvas 60 fps
      Thread.start{
          while (true) {
            if (isVisible()) {
              repaint()
            }
            Display.sync(60)
          }
      }
    }
 
    public void paintGL() {
	  GL11.with {
		  glClear(GL_COLOR_BUFFER_BIT)
		  glMatrixMode(GL_PROJECTION_MATRIX)
		  glLoadIdentity()
		  glOrtho(0, 640, 0, 480, 1, -1)
		  glMatrixMode(GL_MODELVIEW_MATRIX)
	 
		  glPushMatrix()
		  glTranslatef(320, 240, 0.0f)
		  glRotatef(angle, 0, 0, 1.0f)
		  glBegin(GL_QUADS)
		  glVertex2i(-50, -50)
		  glVertex2i(50, -50)
		  glVertex2i(50, 50)
		  glVertex2i(-50, 50)
		  glEnd()
		  glPopMatrix()
	  }
      angle += 1
 
      try {
        swapBuffers()
      } catch (Exception e) { }
    }   
}

One thing you might notice is that LWJGL requires a lot less setup than jME. Download the source here.

Griffon at The Strange Loop

I'm happy to report that my session entitled, "Griffon: Swing just got fun again" was accepted at The Strange Loop.

The Strange Loop is a developer conference taking place in St. Louis October 22nd and 23rd. The sessions are a nice mix of different technologies so it won't be hard to find something that interests you. Register by August 14th for half($75) of what it would you pay at the door.

Fellow Groovyists Hamley D'Arcy, Guillaume Laforge, and Jeff Brown will be presenting "Groovy Compiler Metaprogramming for Fun and Profit," "Groovy, to infinity and beyond!," and "Polyglot Web Development with Grails," respectively.

You can follow announcements from the conference on Twitter @strangeloop_stl or or on the website http://thestrangeloop.com.