Archives for September 2009

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