OverlayIcon – Displaying overlayed icons in JTree

In companion to the CompoundIcon article, here is a class to allow you to build up icons with overlays that can be used anywhere, but specifically in JTree.

/*
 * (c)2008 mharrison 
 * This class is released under GPLv3
 */
package uk.co.lummie.code;
import java.awt.Component;
import java.awt.Graphics;
import java.util.Vector;
import javax.swing.Icon;
 
public class OverlayedIcon implements Icon {
 
    private Vector _icons = new Vector();
    private int _spaceSize = 2;
 
    public OverlayedIcon(Icon[] icons) {
        for (int i = 0; i < icons.length; i++) {
            _icons.add(icons[i]);
        }
    }
 
    @Override
    public int getIconHeight() {
        int result = 0;
        for (Icon icon : getIcons()) {
            result = Math.max(result, icon.getIconHeight());
        }
        return result;
    }
 
    @Override
    public int getIconWidth() {
        int result = 0;
        for (Icon icon : getIcons()) {
            result = Math.max(result, icon.getIconWidth());
        }
        return result;
    }
 
    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        int h = getIconHeight();
        int w = getIconWidth();
 
        for (Icon icon : getIcons()) {
            icon.paintIcon(c, g, x + (w - icon.getIconWidth()) / 2, y + (h - icon.getIconHeight()) / 2);
        }
    }
 
    public int getSpaceSize() {
        return _spaceSize;
    }
 
    public void setSpaceSize(int spaceSize) {
        this._spaceSize = spaceSize;
    }
 
    public void add(Icon icon) {
        _icons.add(icon);
    }
 
    public Vector getIcons() {
        return _icons;
    }
}

CompoundIcon – Displaying more than one icon in JTree

I came across the requirement to display more than one Icon against the nodes in a Jtree. After several hours /days of building custom TreeCellRenderers and TreeCellEditors, inspiration hit. The JLabel can only display one icon, so lets override Icon to display more than one icon, but pretend it’s just a single Icon still.

The following Java class implements a CompoundIcon. Simply create an instance of it passing in the array of Icons you wish to be displayed. Use setSpaceSize to change the space between the icons when the are rendered. Use add, to append new icons if needed after the class has been constructed.

The instance can then be used wherever and Icon is needed.

/*
 * (c)2008 mharrison 
 * This class is released under GPLv3
 */
package uk.co.lummie.code;
 
import java.awt.Component;
import java.awt.Graphics;
import java.util.Vector;
import javax.swing.Icon;
 
public class CompoundIcon implements Icon {
 
    private Vector _icons = new Vector();
    private int _spaceSize = 2;
 
    public CompoundIcon(Icon[] icons) {
        for (int i = 0; i < icons.length; i++) {
            _icons.add(icons[i]);
        }
    }
 
    @Override
    public int getIconHeight() {
        int result = 0;
        for (Icon icon : getIcons()) {
            result = Math.max(result, icon.getIconHeight());
        }
        return result;
    }
 
    @Override
    public int getIconWidth() {
        int result = 0;
        for (Icon icon : getIcons()) {
            result += icon.getIconWidth();
            result += getSpaceSize();
        }
        return result;
    }
 
    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        int h = getIconHeight();
        int offset = 0;
 
        for (Icon icon : getIcons()) {
            icon.paintIcon(c, g, x + offset, y + (h - icon.getIconHeight()) / 2);
            offset += icon.getIconWidth();
            offset += getSpaceSize();
        }
    }
 
    public int getSpaceSize() {
        return _spaceSize;
    }
 
    public void setSpaceSize(int spaceSize) {
        this._spaceSize = spaceSize;
    }
 
    public void add(Icon icon) {
        _icons.add(icon);
    }
 
    public Vector getIcons() {
        return _icons;
    }
}

JQuery and human readable email addresses

Ive just started playing with the power that is Jquery and came up with this nice use for the Ajax functionality in Jquery.

You want to put your email address on your web site so people can contact you, but don’t want to put a mailto: anchor in the html, because it will be spidered and added to some morons Spam list.

Here is a simple and server independent solution:

Firstly, create a file on your site, it doesn’t have to have a html extension, e.g. email.me
In the content of the file, add the html snippet that you would like to display to the user as your email address (usually an anchor tag with a mailto: href). This is the html that will be inserted into your web page when the user clicks on the Show email address text.

e.g.

<a href="mailto:someone@somewhere">someone@somewhere</a>

In your web pages, where you wish someone to be able to see your email address by clicking, enter a span with a class of email.

<span class="email">Click to see email</span>

in the ready function for your document enter the following:

$('.email').click(function(el){
    $(this).hide().load('email.me').fadeIn(1000);
});

This will add a click function to all elements assigned the email class, that when clicked, will replace the current content with the content of the email.me file (faded in :) ). In this case your email mailto anchor. This principle can be used to display any content. Maybe you want to hide the comment form for your blog, until a display has been clicked, give it a try.

Javascript – Rails like pluralize function

Found this on scripts.dzone.com blogged for reference.

/*
* This script depends on no outside libraries.
*/
 
Inflector = {
/*
     * The order of all these lists has been reversed from the way 
     * ActiveSupport had them to keep the correct priority.
     */
    Inflections: {
        plural: [
            [/(quiz)$/i,               "$1zes"  ],
            [/^(ox)$/i,                "$1en"   ],
            [/([m|l])ouse$/i,          "$1ice"  ],
            [/(matr|vert|ind)ix|ex$/i, "$1ices" ],
            [/(x|ch|ss|sh)$/i,         "$1es"   ],
            [/([^aeiouy]|qu)y$/i,      "$1ies"  ],
            [/(hive)$/i,               "$1s"    ],
            [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
            [/sis$/i,                  "ses"    ],
            [/([ti])um$/i,             "$1a"    ],
            [/(buffal|tomat)o$/i,      "$1oes"  ],
            [/(bu)s$/i,                "$1ses"  ],
            [/(alias|status)$/i,       "$1es"   ],
            [/(octop|vir)us$/i,        "$1i"    ],
            [/(ax|test)is$/i,          "$1es"   ],
            [/s$/i,                    "s"      ],
            [/$/,                      "s"      ]
        ],
        singular: [
            [/(quiz)zes$/i,                                                    "$1"     ],
            [/(matr)ices$/i,                                                   "$1ix"   ],
            [/(vert|ind)ices$/i,                                               "$1ex"   ],
            [/^(ox)en/i,                                                       "$1"     ],
            [/(alias|status)es$/i,                                             "$1"     ],
            [/(octop|vir)i$/i,                                                 "$1us"   ],
            [/(cris|ax|test)es$/i,                                             "$1is"   ],
            [/(shoe)s$/i,                                                      "$1"     ],
            [/(o)es$/i,                                                        "$1"     ],
            [/(bus)es$/i,                                                      "$1"     ],
            [/([m|l])ice$/i,                                                   "$1ouse" ],
            [/(x|ch|ss|sh)es$/i,                                               "$1"     ],
            [/(m)ovies$/i,                                                     "$1ovie" ],
            [/(s)eries$/i,                                                     "$1eries"],
            [/([^aeiouy]|qu)ies$/i,                                            "$1y"    ],
            [/([lr])ves$/i,                                                    "$1f"    ],
            [/(tive)s$/i,                                                      "$1"     ],
            [/(hive)s$/i,                                                      "$1"     ],
            [/([^f])ves$/i,                                                    "$1fe"   ],
            [/(^analy)ses$/i,                                                  "$1sis"  ],
            [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, "$1$2sis"],
            [/([ti])a$/i,                                                      "$1um"   ],
            [/(n)ews$/i,                                                       "$1ews"  ],
            [/s$/i,                                                            ""       ]
        ],
        irregular: [
            ['move',   'moves'   ],
            ['sex',    'sexes'   ],
            ['child',  'children'],
            ['man',    'men'     ],
            ['person', 'people'  ]
        ],
        uncountable: [
            "sheep",
            "fish",
            "series",
            "species",
            "money",
            "rice",
            "information",
            "equipment"
        ]
    },
    ordinalize: function(number) {
        if (11 &lt;= parseInt(number) % 100 &amp;&amp; parseInt(number) % 100 &lt;= 13) {
            return number + "th";
        } else {
            switch (parseInt(number) % 10) {
                case  1: return number + "st";
                case  2: return number + "nd";
                case  3: return number + "rd";
                default: return number + "th";
            }
        }
    },
    pluralize: function(word) {
        for (var i = 0; i &lt; Inflector.Inflections.uncountable.length; i++) {
            var uncountable = Inflector.Inflections.uncountable[i];
            if (word.toLowerCase() == uncountable) {
                return uncountable;
            }
        }
        for (var i = 0; i &lt; Inflector.Inflections.irregular.length; i++) {
            var singular = Inflector.Inflections.irregular[i][0];
            var plural   = Inflector.Inflections.irregular[i][1];
            if ((word.toLowerCase() == singular) || (word == plural)) {
                return plural;
            }
        }
        for (var i = 0; i &lt; Inflector.Inflections.plural.length; i++) {
            var regex          = Inflector.Inflections.plural[i][0];
            var replace_string = Inflector.Inflections.plural[i][1];
            if (regex.test(word)) {
                return word.replace(regex, replace_string);
            }
        }
    }
}
 
function ordinalize(number) {
    return Inflector.ordinalize(number);
}
 
/*
 * Javascript doesnt have optional parameters or overloading so I had to use
 * the ever popular pseudo options hash object technique.
 * required properties:
 *     count    Number of objects to pluralize
 *     singular Singular noun for the objects
 * optional property:
 *     plural   Plural (probably irregular) noun for the objects
 * examples:
 *      pluralize({ count: total_count, singular: "Issue" })
 *      pluralize({ count: total_count, singular: "Goose", plural: "Geese" })
 */
function pluralize(options) {
    return options.count + " " + (1 == parseInt(options.count) ?
        options.singular :
        options.plural || Inflector.pluralize(options.singular));
}

Installing and managing edge Rails

Great article here:

http://www.sitepoint.com/blogs/2006/07/11/installing-and-managing-edge-rails/

How to stop webrick from the command line

To create a bash file to stop webrick.

In a terminal type gedit stopWebrick and add the following to the contents of the file and save it.  Give the file execute permission with chmod of using the file properties in nautilus.

#!/bin/bash
kill -9 `ps -o pid,cmd --no-heading -p $(pgrep ruby) | grep script/server | awk '{print $1}'`

This will kill the first ruby process it finds running as script/srever

Run it as needed.

UserSumChallenge Module

The following module is my first ever Ruby/Rails Module.

The UserChallenge module will create a simple random sum, that can be prompted to the user for solving as part as your authentication scheme during a registration process.
Simply create an instance of UserSumChallenge or UserSumImageChallenge and store against the user session. If you use the image challenge, call render to create the image otherwise just add the question attribute to your output.

# UserChallenge provides several mechanisms to aid in establishing a real user is on the end of a submit
 
# Typically used for user registration processes or commenting, the UserChallenge module will allow you to provide
 
# a challenge question and validate the repsponse.
 
# Mixins include
 
#       * SumChallenge - provides a simple arimethetic sum to be solved
 
#       * RandomTextChallenge - supplies a random string of characters
 
# Supporting Classes include
#       * SumTextChallenger - Class that will provide a question as a text string
#       * SumImageChallenger - Class that will create a png image with the sum in it
#       * RandomTextChallenger - Class that will provide a random text string
#       * RandomTextImageChallenger - Class that will create a png image with the random text string in it
 
module UserChallenge
 
 # This module creates a challenge that is a basic sum, expecting the respose to be the sums result, e.g. 2+6
 module SumChallenge
 #Initailizes the challenge to a sum
 #optionally specify the max values for the left and right 
 def createChallenge(lMax=10, rMax=10)
 	lValue = rand(lMax)
 	rValue = rand(rMax)
 	@solution = lValue + rValue
 	@question = "#{lValue} + #{rValue}"
 end
 
 # checks the value passed in against the expected result 
 def checkResponse(value)
 	return (@solution == value.to_i)
 end
end
 
 # This module creates a challenge that is a series of random characters in a specified range, expecting the respose to be the same string module RandomTextChallenge
 # Initailizes the challenge to a sum
 # optionally specify the number of characters to generate and the range of characters to choose from 
 def createChallenge(count=6, chars = 'a'..'z')
 	@question = ""
	count.times do
		index = rand(chars.entries.length-1)
		@question += chars.entries[index]
 	end
 	@solution = @question
 end
 
 # checks the value passed in against the expected result 
 def checkResponse(value)
	 return (@solution == value.to_s)
	 end
 end
 
 # A simple class that generates a sum challenge in text form
 class SumTextChallenger
	 include SumChallenge
 
	 # the sum question as a string
	 attr_reader :question
	 # the solution as an integer
	 attr_reader :solution
 
	 def initialize()
		 createChallenge()
	 end
 end
 
 # A class that generates a sum challenge in image form
 class SumImageChallenger
 	require "RMagick"
	include SumChallenge
 
 	# the sum question as a string
	attr_reader :question
 	# the solution as an integer
 	attr_reader :solution
 
 	def initialize()
		createChallenge()
 	end
 
 # returns an image blob with the sum question layered ontop of the specified background
 def render(backgroundImageUrl)
 	background = Magick::Image.read(backgroundImageUrl).first
 
 	croppedImg = background.crop(0,0,128,28)
 
 	text = Magick::Draw.new
	text.annotate(croppedImg, 128, 28, 0, 0, @question) {
		self.gravity = Magick::SouthGravity
		self.pointsize = 24
		self.stroke = 'transparent'
		self.fill = '#0000A9'
		self.font_weight = Magick::BoldWeight
	}
 
 	croppedImg = croppedImg.blur_image
 	return croppedImg.to_blob
 	end
 end
 
 # A simple class that generates a random text challenge in text form
 class RandomTextChallenger
 include RandomTextChallenge
 
 # the sum question as a string
 attr_reader :question
 # the solution as an integer
 attr_reader :solution
 
 def initialize()
 	createChallenge()
 	end
 end
 
 # A class that generates a random text challenge in image form
 class RandomTextImageChallenger
 	require "RMagick"
 	include RandomTextChallenge
 
 	# the sum question as a string
	attr_reader :question
 	# the solution as an integer
 	attr_reader :solution
 
	def initialize()
 	createChallenge()
	end
 
 	# returns an image blob with the sum question layered ontop of the specified background
 	def render(backgroundImageUrl, width=128)
		background = Magick::Image.read(backgroundImageUrl).first
 
 		croppedImg = background.crop(0,0,width,28)
 
 		text = Magick::Draw.new
		text.annotate(croppedImg, width, 28, 0, 0, @question) {
		self.gravity = Magick::SouthGravity
		self.pointsize = 22
		self.stroke = 'transparent'
 		self.fill = '#0000A9'
 		self.font_weight = Magick::BoldWeight
 		}
 
 		croppedImg = croppedImg.blur_image
		return croppedImg.to_blob
	 	end
 	end
end

Here is a simple controller that uses a ImageChallenge. Notice the background being supplied to the render method

class MainController &lt; ApplicationController  require "UserChallenge"
 
  def welcome
    @challenge = UserChallenge::UserSumImageChallenge.new
    session["uc"] = @challenge#
  end
 
  def image
    storePath = "/home/user/path"
    filename = storePath + "/Camouflage.png"
 
    challenge = session["uc"]
    session["uc"] = nil
    send_data challenge.render(filename), :filename =&gt; "confirm.png", :type =&gt; 'image/png', :disposition =&gt; 'inline'  
  end
 
end

Installing RMagick on Ubuntu

Taken from the rubyonrails wiki, for my reference:

If you need RMagick support for your web app, your going to need to install ImageMagick and RMagick.

sudo apt-get install imagemagick
dpkg -l | grep magick

This will install the most current imagemagick and then

list the ‘magick’ packages installed. Look for what version of
libmagick got installed and make sure you specify THAT version number when you install libmagick-dev.

sudo apt-get install libmagick9-dev
sudo gem install rmagick

After a lengthy build process, you’re ready to edit images in ruby!

Raaum's Rails Reader

Rails Documentation for the Working Developer

http://rails.raaum.org/

Installing Ruby and rails on Ubuntu Feisty

The image “http://www.rubyonrails.org/images/rails.png” cannot be displayed, because it contains errors.

Found these instructions at http://www.aptana.com/forums/viewtopic.php?p=10640

Blogging to ensure I have a copy, credits go to janmartin.

Lets start with a fresh Ubuntu Feisty Fawn 7.04 installation:

  • sudo apt-get install build-essential
  • sudo apt-get install sun-java6-jdk
  • sudo update-java-alternatives –list
  • sudo apt-get install ruby irb ri rake rdoc ruby1.8-dev rubygems
  • sudo apt-get install libmysql-ruby mysql-server
  • sudo gem update –system
  • sudo gem install rails –include-dependencies

Some tests:

  • ruby –version
  • rails –version
  • mysql –version
  • gem list

END of command line installation

Download
Aptana + Rails (Linux).
Extract it into ~/aptana

Follow these instructions:
# Open up Aptana, and Navigate to the Help > Software Updates > Find and Install menu.
# Select “Search for new features to install”, click “Finish”
# Select “Ruby on Rails Development Environment”, click “Finish”
# Select the Ruby on Rails Development Environment feature.
# Continue through the dialog boxes until complete.

Follow these instructions:
http://www.aptana.com/forums/viewtopic.php?t=1397

Configure:
Menu -> Window -> Preferences

Rails -> Configuration
Rails path: /usr/bin/rails
Rake path: /usr/bin/rake

Ruby
Installed interpreters:
/usr

Ri/rdoc
RDoc path:/usr/bin/rdoc
Ri path: /usr/bin/ri

Finished!

Powered by ScribeFire.