[jbpm-commits] JBoss JBPM SVN: r5542 - in projects/demos/trainticket-demo/jbpm-phone/trunk: src and 13 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Wed Aug 26 11:11:40 EDT 2009


Author: jbarrez
Date: 2009-08-26 11:11:39 -0400 (Wed, 26 Aug 2009)
New Revision: 5542

Added:
   projects/demos/trainticket-demo/jbpm-phone/trunk/pom.xml
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/LoadTest.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/Demo.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/InboxPanel.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/PhonePanel.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/SendSmsPanel.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/AbstractFilter.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/AePlayWave.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/ColorTintFilter.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/GraphicsUtilities.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/LightButton.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/ProgressGlassPane.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/PulsatingLogo.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/SmsReceivedGlassPane.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/SmsReceivedPopup.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/http/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/http/HttpListener.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/message/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/message/MessageQueue.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/Inbox.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/SmsMessage.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/service/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/service/SmsService.java
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/button-normal-pressed.png
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/button-normal.png
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/gotmail.wav
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/header-halo.png
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/phone.png
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/read_sms.png
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/sms_received.png
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/unread_sms.png
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/test/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/test/java/
   projects/demos/trainticket-demo/jbpm-phone/trunk/src/test/resources/
Modified:
   projects/demos/trainticket-demo/jbpm-phone/trunk/
Log:
Initial import for Client app 'jBPM-Phone'


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk
___________________________________________________________________
Name: svn:ignore
   + .settings
.classpath
.project
target


Added: projects/demos/trainticket-demo/jbpm-phone/trunk/pom.xml
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/pom.xml	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/pom.xml	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,56 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>org.jbpm.trainticketdemo</groupId>
+	<artifactId>jBPM-Phone</artifactId>
+	<version>1.0</version>
+
+	<dependencies>
+		<dependency>
+			<groupId>net.java.dev.timingframework</groupId>
+			<artifactId>timingframework</artifactId>
+			<version>1.0</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-httpclient</groupId>
+			<artifactId>commons-httpclient</artifactId>
+			<version>3.1</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<target>1.6</target>
+					<source>1.6</source>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifest>
+							<mainClass>org.jbpm.trainticketdemo.Demo</mainClass>
+						</manifest>
+					</archive>
+					<descriptorRefs>
+						<descriptorRef>jar-with-dependencies</descriptorRef>
+					</descriptorRefs>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>
\ No newline at end of file

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/LoadTest.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/LoadTest.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/LoadTest.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,95 @@
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Date;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.jbpm.trainticketdemo.http.HttpListener;
+import org.jbpm.trainticketdemo.model.Inbox;
+
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+/**
+ * @author Joram Barrez
+ */
+public class LoadTest {
+	
+private static final String POST_URL = "http://localhost:8080/jbpm-on-rails/receive_sms";
+	
+	private static final String PARAM_SEND_DATE = "sendDate";
+	
+	private static final String PARAM_MSG_CONTENT = "msgContent";
+	
+	private static final String PARAM_SENDER_NR = "senderNr";
+	
+	private static final String VALUE_SENDER_NR = "EMULATOR-http://localhost:4321/cellphone_sms_listener";
+
+	public static void main(String[] args) throws Exception {
+		
+		//Need http server for sms response
+		Inbox inbox = new Inbox();
+		HttpListener listener = new HttpListener(inbox);
+		listener.start();
+
+		long start = System.currentTimeMillis();
+		int max = 50;
+		
+		ExecutorService threadPool = Executors.newFixedThreadPool(5);
+		//ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(max);
+		//ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10,25,10, TimeUnit.SECONDS, queue);
+		for (int i=0; i < max; i++) {
+			threadPool.execute(new Runnable() {
+				public void run() {
+					PostMethod post = new PostMethod(POST_URL);
+					post.addParameter(PARAM_SEND_DATE, new Date().toString());
+					post.addParameter(PARAM_MSG_CONTENT, "TICKET brussel antwerpen");
+					post.addParameter(PARAM_SENDER_NR, VALUE_SENDER_NR);
+					HttpClient httpClient = new HttpClient();
+					try {
+						httpClient.executeMethod(post);
+					} catch (HttpException e) {
+						e.printStackTrace();
+					} catch (IOException e) {
+						e.printStackTrace();
+					}
+				}
+			});
+		}
+		
+		// waits until all threads are executed
+		threadPool.shutdown(); 
+		threadPool.awaitTermination(5, TimeUnit.HOURS);
+		
+		long end = System.currentTimeMillis();
+		System.out.println("TIME until all threads finished = " + (end-start));
+		
+		//listener.stop();
+	}
+	
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/Demo.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/Demo.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/Demo.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,65 @@
+package org.jbpm.trainticketdemo;
+import java.awt.Dimension;
+
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import org.jbpm.trainticketdemo.gui.PhonePanel;
+import org.jbpm.trainticketdemo.http.HttpListener;
+import org.jbpm.trainticketdemo.model.Inbox;
+
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+/**
+ * @author Joram Barrez
+ */
+public class Demo {
+	
+	public static void main(String[] args) throws Exception {
+		
+		// Set up SMS inbox for cellphone
+		final Inbox inbox = new Inbox();
+		
+		// Set up Http Listener
+		final HttpListener httpListener = new HttpListener(inbox);
+		httpListener.start();
+		
+		// Set up GUI
+		UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
+		SwingUtilities.invokeLater(new Runnable() {
+			
+			public void run() {
+				JFrame f = new JFrame("jBPM CellPhone Emulator");
+				f.setSize(new Dimension(323, 600));
+				PhonePanel phonePanel = new PhonePanel(inbox);
+				inbox.addChangeListener(phonePanel);
+				f.setContentPane(phonePanel);
+				f.setResizable(false);
+				f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+				f.setVisible(true);
+			}
+		});
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/InboxPanel.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/InboxPanel.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/InboxPanel.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,193 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.gui;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellRenderer;
+
+import org.jbpm.trainticketdemo.model.Inbox;
+import org.jbpm.trainticketdemo.model.SmsMessage;
+
+/**
+ * @author Joram Barrez
+ */
+public class InboxPanel extends JPanel {
+	
+	private static final long serialVersionUID = 1L;
+	
+	private Inbox inbox;
+	
+	private JScrollPane scrollPane;
+
+	private JTable messagesTable;
+	
+	public InboxPanel(Inbox inbox) {
+		this.inbox = inbox;
+		setLayout(null);
+		
+		scrollPane = new JScrollPane(initMessagesTable());
+		scrollPane.setBounds(0, 0, 246, 275);
+		scrollPane.setColumnHeader(null);
+		scrollPane.setBorder(BorderFactory.createEmptyBorder());
+		scrollPane.setBackground(Color.black);
+		scrollPane.getViewport().setBackground(Color.black);
+		scrollPane.getVerticalScrollBar().setBackground(Color.black);
+		scrollPane.getVerticalScrollBar().setForeground(Color.gray);
+		
+		setBackground(Color.black);
+		add(scrollPane);
+		repaint();
+	}
+	
+	private JTable initMessagesTable() {
+		
+		SingleColumnTableModel tableModel = new SingleColumnTableModel();
+		messagesTable = new JTable(tableModel);
+		messagesTable.setTableHeader(null);
+		messagesTable.setBackground(Color.black);
+		messagesTable.setBorder(BorderFactory.createEmptyBorder());
+		messagesTable.setRowHeight(120);
+		
+		Iterator<SmsMessage> smsIterator = inbox.iterator();
+		while (smsIterator.hasNext()) {
+			tableModel.add(smsIterator.next());
+		}
+		
+		messagesTable.getColumnModel().getColumn(0).setCellRenderer(new InboxCellRenderer());
+		
+		return messagesTable;
+	}
+	
+	private class SingleColumnTableModel extends AbstractTableModel {
+		
+		private static final long serialVersionUID = 1L;
+		
+		private ArrayList<SmsMessage> elements;
+		
+		public SingleColumnTableModel() {
+			this.elements = new ArrayList<SmsMessage>();
+		}
+
+		public int getColumnCount() {
+			return 1;
+		}
+
+		public int getRowCount() {
+			return elements.size();
+		}
+		
+		public void add(SmsMessage smsMessage) {
+			elements.add(smsMessage);
+		}
+		
+		@Override
+		public void setValueAt(Object obj, int rowIndex, int columnIndex) {
+			elements.add(rowIndex, (SmsMessage) obj);
+		}
+
+		public Object getValueAt(int rowIndex, int columnIndex) {
+			return elements.get(rowIndex);
+		}
+		
+	}
+	
+	private class InboxCellRenderer extends JPanel implements TableCellRenderer {
+		
+		private static final long serialVersionUID = 1L;
+
+		private BufferedImage readSmsImage;
+		private BufferedImage unreadSmsImage;
+		
+		private Font inboxFont = new Font(Font.SANS_SERIF, Font.BOLD, 12);
+		
+		public InboxCellRenderer() {
+			if (readSmsImage == null) {
+				try {
+					readSmsImage = ImageIO.read(InboxPanel.this.getClass().getResourceAsStream("/read_sms.png"));
+					unreadSmsImage = ImageIO.read(InboxPanel.this.getClass().getResourceAsStream("/unread_sms.png"));
+				} catch (IOException e) {
+					e.printStackTrace(); // Ok, no pic then :-)
+				}
+			}
+		}
+
+		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, 
+				boolean hasFocus, int row, int column) {
+
+			SmsMessage message = (SmsMessage) value;
+			JPanel cell = new JPanel();
+			cell.setBackground(Color.black);
+			cell.setLayout(null);
+			cell.setBorder(BorderFactory.createLineBorder(Color.gray));
+			
+			// SMS image
+			BufferedImage smsImage = message.isRead() ? readSmsImage : unreadSmsImage;
+			message.setRead(true);
+			JLabel imageLabel = new JLabel(new ImageIcon(smsImage));
+			imageLabel.setBackground(Color.black);
+			imageLabel.setBounds(10, 5, 32, 32);
+			cell.add(imageLabel);
+			
+			// SMS sender
+			JLabel senderLabel = new JLabel(message.getSender());
+			senderLabel.setBackground(Color.black);
+			senderLabel.setForeground(Color.white);
+			senderLabel.setFont(inboxFont);
+			senderLabel.setBounds(45, 5, 150, 32);
+			cell.add(senderLabel);
+			
+			// SMS content
+			JLabel contentLabel = new JLabel("<html>" + message.getContent() + "</html>"); // need html tag for text wrapping
+			contentLabel.setVerticalAlignment(JLabel.TOP);
+			contentLabel.setBackground(Color.gray);
+			contentLabel.setForeground(Color.white);
+			contentLabel.setFont(inboxFont);
+			contentLabel.setBounds(10, 40, 230, 70);
+			cell.add(contentLabel);
+			
+			return cell;			
+		}
+		
+		// The following methods override the defaults for performance reasons
+        public void validate() {}
+        public void revalidate() {}
+        protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {}
+        public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}
+
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/PhonePanel.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/PhonePanel.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/PhonePanel.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,154 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.gui;
+
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.swing.JPanel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.jbpm.trainticketdemo.gui.fancy.SmsReceivedPopup;
+import org.jbpm.trainticketdemo.model.Inbox;
+
+/**
+ * @author Joram Barrez
+ */
+public class PhonePanel extends JPanel implements MouseMotionListener, MouseListener, ChangeListener {
+
+	private static final long serialVersionUID = 1L;
+
+	private BufferedImage background;
+	
+	private Inbox inbox;
+	
+	private static final int INBOX_LEFT_X = 108;
+	private static final int INBOX_RIGHT_X = 155;
+	private static final int SMS_LEFT_X = 167;
+	private static final int SMS_RIGHT_X = 213;
+	private static final int SHUTDOWN_LEFT_X = 227;
+	private static final int SHUTDOWN_RIGHT_X = 271;
+	private static final int UP_Y = 429;
+	private static final int DOWN_Y = 477;
+	
+	public PhonePanel(Inbox inbox) {
+		super();
+		this.inbox = inbox;
+		setLayout(null);
+		addMouseListener(this);
+		addMouseMotionListener(this);
+		try {
+			this.background = ImageIO.read(this.getClass().getResourceAsStream("/phone.png"));
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+	
+	@Override
+	protected void paintComponent(Graphics g) {
+		super.paintComponent(g);
+		Graphics2D g2 = (Graphics2D)g;
+		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+		g2.drawImage(background, 0, 0, background.getWidth(), background.getHeight(), null);
+	}
+	
+	private static Rectangle screenBounds = new Rectangle(40, 142, 246, 277);
+
+	public void mouseClicked(MouseEvent mouseEvent) {
+		int x = mouseEvent.getX();
+		int y = mouseEvent.getY();
+		
+		if (inSmsButton(x, y)) {
+			
+			removeAll();
+			SendSmsPanel sendSmsPanel = new SendSmsPanel();
+			sendSmsPanel.setBounds(screenBounds);
+			add(sendSmsPanel);
+			repaint();
+			
+		} else if (inInboxButton(x, y)) {
+			
+			removeAll();
+			InboxPanel inboxPanel = new InboxPanel(inbox);
+			inboxPanel.setBounds(screenBounds);
+			add(inboxPanel);
+			repaint();
+			
+		} else if (inShutdownButton(x, y)) {
+			
+			System.exit(0);
+			
+		}
+	}
+	
+	private static final Cursor NORMAL_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
+	private static final Cursor HAND_CURSOR = new Cursor(Cursor.HAND_CURSOR);
+	
+	public void mouseMoved(MouseEvent mouseEvent) {
+		int x = mouseEvent.getX();
+		int y = mouseEvent.getY();
+		if (inInboxButton(x, y) || inSmsButton(x, y) || inShutdownButton(x, y)) {
+			setCursor(HAND_CURSOR);
+		} else {
+			setCursor(NORMAL_CURSOR);
+		}
+	}
+		
+	private boolean inInboxButton(int x, int y) {
+		return x >= INBOX_LEFT_X && x <= INBOX_RIGHT_X
+			&& y >= UP_Y && y <= DOWN_Y;
+	}
+	
+	private boolean inSmsButton(int x, int y) {
+		return x >= SMS_LEFT_X && x <= SMS_RIGHT_X
+			&& y >= UP_Y && y <= DOWN_Y;
+	}
+	
+	private boolean inShutdownButton(int x, int y) {
+		return x >= SHUTDOWN_LEFT_X && x <= SHUTDOWN_RIGHT_X
+			&& y >= UP_Y && y <= DOWN_Y;
+	}
+	
+	// Not used (but needed since we can only extend one class)
+	
+	public void mouseEntered(MouseEvent arg0) {}
+	public void mouseExited(MouseEvent arg0) {}
+	public void mousePressed(MouseEvent arg0) {}
+	public void mouseReleased(MouseEvent arg0) {}
+	public void mouseDragged(MouseEvent arg0) {}
+	
+	// This panel is a listener for changes in the inbox
+	public void stateChanged(ChangeEvent e) {
+		new SmsReceivedPopup(this).start();
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/SendSmsPanel.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/SendSmsPanel.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/SendSmsPanel.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,170 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.gui;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Random;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+
+import org.jbpm.trainticketdemo.gui.fancy.LightButton;
+import org.jbpm.trainticketdemo.gui.fancy.ProgressGlassPane;
+import org.jbpm.trainticketdemo.service.SmsService;
+
+/**
+ * @author Joram Barrez
+ */
+public class SendSmsPanel extends JPanel {
+
+	private static final long serialVersionUID = 1L;
+	
+	private JLabel phoneNrLabel;
+	private JTextField smsNrField;
+	private JLabel smsInputLabel;
+	private JTextArea smsInputArea;
+	private JButton sendSmsButton; 
+	
+	private static final Font LABEL_FONT = new Font(Font.MONOSPACED, Font.BOLD, 12);
+	private static final Font SMS_FONT = new Font(Font.MONOSPACED, Font.BOLD, 14); 
+	private static final Font SEND_SMS_FONT = new Font(Font.MONOSPACED, Font.BOLD, 11);
+	
+	public SendSmsPanel() {
+		
+		setLayout(null);
+		setBackground(Color.black);
+		
+		initPhoneNrLabel();
+		initSmsNrField();
+		initSmsInputLabel();
+		initSmsInputArea();
+		initSendSmsButton();
+	}
+
+	private void initSendSmsButton() {
+		sendSmsButton = new LightButton("Send SMS");
+		sendSmsButton.setFont(SEND_SMS_FONT);
+		sendSmsButton.setBounds(86, 245, 70, 30);
+		sendSmsButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent event) {
+					Thread t = new Thread() {
+						public void run() {
+							try {
+								// Do a send animation
+								ProgressGlassPane progressGlasspane = new ProgressGlassPane();
+								((JFrame)SwingUtilities.getWindowAncestor(SendSmsPanel.this)).setGlassPane(progressGlasspane); // Kinda dirty ...
+								progressGlasspane.setVisible(true);
+								SendSmsSimulator sendSmsSimulator = new SendSmsSimulator(progressGlasspane);
+								sendSmsSimulator.start();
+								
+								// Send the sms through http
+								SmsService.sendSms(smsInputArea.getText());
+								
+								smsNrField.setText("");
+								smsInputArea.setText("");
+							} catch (Exception e) {
+								e.printStackTrace();
+							}
+						};
+					};
+					t.start();
+			}
+		});
+		add(sendSmsButton);
+	}
+
+	private void initSmsInputArea() {
+		smsInputArea = new JTextArea();
+		smsInputArea.setBackground(Color.black);
+		smsInputArea.setFont(SMS_FONT);
+		smsInputArea.setForeground(Color.white);
+		smsInputArea.setBorder(BorderFactory.createLineBorder(Color.white));
+		smsInputArea.setBounds(14, 60, 200, 180);
+		smsInputArea.setLineWrap(true);
+		add(smsInputArea);
+	}
+
+	private void initSmsInputLabel() {
+		smsInputLabel = new JLabel();
+		smsInputLabel.setText("Text message");
+		smsInputLabel.setFont(LABEL_FONT);
+		smsInputLabel.setForeground(new Color(203, 186, 186));
+		smsInputLabel.setBounds(12, 42, 100, 20);
+		add(smsInputLabel);
+	}
+
+	private void initSmsNrField() {
+		smsNrField = new JTextField();
+		smsNrField.setBackground(Color.black);
+		smsNrField.setFont(SMS_FONT);
+		smsNrField.setForeground(Color.white);
+		smsNrField.setBorder(BorderFactory.createLineBorder(Color.white));
+		smsNrField.setBounds(14, 20, 150, 20);
+		add(smsNrField);
+	}
+
+	private void initPhoneNrLabel() {
+		phoneNrLabel = new JLabel();
+		phoneNrLabel.setText("Phone nr");
+		phoneNrLabel.setFont(LABEL_FONT);
+		phoneNrLabel.setForeground(new Color(203, 186, 186));
+		phoneNrLabel.setBounds(14, 0, 100, 20);
+		add(phoneNrLabel);
+	}
+	
+	private class SendSmsSimulator extends Thread {
+		
+		private ProgressGlassPane glasspane;
+		
+		private Random random;
+		
+		public SendSmsSimulator(ProgressGlassPane glassPane) {
+			this.glasspane = glassPane;
+			this.random = new Random();
+		}
+		
+		@Override
+		public void run() {
+			int i = 0;
+            do {
+                try {
+                    Thread.sleep(50);
+                } catch (InterruptedException ex) {}
+                i += random.nextInt(10);
+                glasspane.setProgress(i);
+            } while (i < 100);
+            glasspane.setVisible(false);
+            glasspane.setProgress(0);
+		}
+		
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/AbstractFilter.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/AbstractFilter.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/AbstractFilter.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,90 @@
+package org.jbpm.trainticketdemo.gui.fancy;
+/*
+ * $Id: AbstractFilter.java,v 1.1 2007/01/28 01:45:27 gfx Exp $
+ *
+ * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
+ *
+ * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
+ * Santa Clara, California 95054, U.S.A. All rights reserved.
+ *
+ * Copyright (c) 2006 Romain Guy <romain.guy at mac.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ColorModel;
+
+/**
+ * <p>Provides an abstract implementation of the <code>BufferedImageOp</code>
+ * interface. This class can be used to created new image filters based
+ * on <code>BufferedImageOp</code>.</p>
+ *
+ * @author Romain Guy <romain.guy at mac.com>
+ */
+
+public abstract class AbstractFilter implements BufferedImageOp {
+    public abstract BufferedImage filter(BufferedImage src, BufferedImage dest);
+
+    /**
+     * {@inheritDoc}
+     */
+    public Rectangle2D getBounds2D(BufferedImage src) {
+        return new Rectangle(0, 0, src.getWidth(), src.getHeight());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public BufferedImage createCompatibleDestImage(BufferedImage src,
+                                                   ColorModel destCM) {
+        if (destCM == null) {
+            destCM = src.getColorModel();
+        }
+
+        return new BufferedImage(destCM,
+                                 destCM.createCompatibleWritableRaster(
+                                         src.getWidth(), src.getHeight()),
+                                 destCM.isAlphaPremultiplied(), null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
+        return (Point2D) srcPt.clone();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public RenderingHints getRenderingHints() {
+        return null;
+    }
+}
Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/AePlayWave.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/AePlayWave.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/AePlayWave.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,96 @@
+package org.jbpm.trainticketdemo.gui.fancy;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.FloatControl;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.SourceDataLine;
+import javax.sound.sampled.UnsupportedAudioFileException;
+ 
+/**
+ * Borrowed from http://www.anyexample.com/programming/java/java_play_wav_sound_file.xml
+ * @jbarrez (small adaptations)
+ */
+public class AePlayWave {
+ 
+	private String filename;
+ 
+	private Position curPosition;
+ 
+	private final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb
+ 
+	enum Position {
+		LEFT, RIGHT, NORMAL
+	};
+ 
+	public AePlayWave(String wavfile) {
+		filename = wavfile;
+		curPosition = Position.NORMAL;
+	}
+ 
+	public AePlayWave(String wavfile, Position p) {
+		filename = wavfile;
+		curPosition = p;
+	}
+ 
+	public void play() {
+		AudioInputStream audioInputStream = null;
+		try {
+			InputStream is = this.getClass().getClassLoader().getResourceAsStream(filename);
+			audioInputStream = AudioSystem.getAudioInputStream(is);
+		} catch (UnsupportedAudioFileException e1) {
+			e1.printStackTrace();
+			return;
+		} catch (IOException e1) {
+			e1.printStackTrace();
+			return;
+		}
+ 
+		AudioFormat format = audioInputStream.getFormat();
+		SourceDataLine auline = null;
+		DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
+ 
+		try {
+			auline = (SourceDataLine) AudioSystem.getLine(info);
+			auline.open(format);
+		} catch (LineUnavailableException e) {
+			e.printStackTrace();
+			return;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return;
+		}
+ 
+		if (auline.isControlSupported(FloatControl.Type.PAN)) {
+			FloatControl pan = (FloatControl) auline
+					.getControl(FloatControl.Type.PAN);
+			if (curPosition == Position.RIGHT)
+				pan.setValue(1.0f);
+			else if (curPosition == Position.LEFT)
+				pan.setValue(-1.0f);
+		} 
+ 
+		auline.start();
+		int nBytesRead = 0;
+		byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
+ 
+		try {
+			while (nBytesRead != -1) {
+				nBytesRead = audioInputStream.read(abData, 0, abData.length);
+				if (nBytesRead >= 0)
+					auline.write(abData, 0, nBytesRead);
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+			return;
+		} finally {
+			auline.drain();
+			auline.close();
+		}
+ 
+	}
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/ColorTintFilter.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/ColorTintFilter.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/ColorTintFilter.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,157 @@
+package org.jbpm.trainticketdemo.gui.fancy;
+/*
+ * $Id: ColorTintFilter.java,v 1.1 2007/01/28 01:45:30 gfx Exp $
+ *
+ * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
+ *
+ * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
+ * Santa Clara, California 95054, U.S.A. All rights reserved.
+ *
+ * Copyright (c) 2006 Romain Guy <romain.guy at mac.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.awt.image.BufferedImage;
+import java.awt.Color;
+
+/**
+ * <p>A color tint filter can be used to mix a solid color to an image. The
+ * result is an image tinted by the specified color. The force of the effect
+ * can be controlled with the <code>mixValue</code>, a number between  0.0 and
+ * 1.0 that can be seen as the percentage of the mix (0.0 does not affect the
+ * source image and 1.0 replaces all the pixels by the solid color).</p>
+ * <p>The color of the pixels in the resulting image is computed as follows:</p>
+ * <pre>
+ * cR = cS * (1 - mixValue) + cM * mixValue
+ * </pre>
+ * <p>Definition of the parameters:</p>
+ * <ul>
+ *   <li><code>cR</code>: color of the resulting pixel</li>
+ *   <li><code>cS</code>: color of the source pixel</li>
+ *   <li><code>cM</code>: the solid color to mix with the source image</li>
+ *   <li><code>mixValue</code>: strength of the mix, a value between 0.0 and 1.0</li>
+ * </ul>
+ *
+ * @author Romain Guy <romain.guy at mac.com>
+ */
+
+public class ColorTintFilter extends AbstractFilter {
+    private final Color mixColor;
+    private final float mixValue;
+
+    private int[] preMultipliedRed;
+    private int[] preMultipliedGreen;
+    private int[] preMultipliedBlue;
+
+    /**
+     * <p>Creates a new color mixer filter. The specified color will be used
+     * to tint the source image, with a mixing strength defined by
+     * <code>mixValue</code>.</p>
+     *
+     * @param mixColor the solid color to mix with the source image
+     * @param mixValue the strength of the mix, between 0.0 and 1.0; if the
+     *   specified value lies outside this range, it is clamped
+     * @throws IllegalArgumentException if <code>mixColor</code> is null
+     */
+    public ColorTintFilter(Color mixColor, float mixValue) {
+        if (mixColor == null) {
+            throw new IllegalArgumentException("mixColor cannot be null");
+        }
+
+        this.mixColor = mixColor;
+        if (mixValue < 0.0f) {
+            mixValue = 0.0f;
+        } else if (mixValue > 1.0f) {
+            mixValue = 1.0f;
+        }
+        this.mixValue = mixValue;
+        
+        int mix_r = (int) (mixColor.getRed()   * mixValue);
+        int mix_g = (int) (mixColor.getGreen() * mixValue);
+        int mix_b = (int) (mixColor.getBlue()  * mixValue);
+        
+        // Since we use only lookup tables to apply the filter, this filter
+        // could be implemented as a LookupOp.
+        float factor = 1.0f - mixValue;
+        preMultipliedRed   = new int[256];
+        preMultipliedGreen = new int[256];
+        preMultipliedBlue  = new int[256];
+
+        for (int i = 0; i < 256; i++) {
+            int value = (int) (i * factor);
+            preMultipliedRed[i]   = value + mix_r;
+            preMultipliedGreen[i] = value + mix_g;
+            preMultipliedBlue[i]  = value + mix_b;
+        }
+    }
+
+    /**
+     * <p>Returns the mix value of this filter.</p>
+     *
+     * @return the mix value, between 0.0 and 1.0
+     */
+    public float getMixValue() {
+        return mixValue;
+    }
+
+    /**
+     * <p>Returns the solid mix color of this filter.</p> 
+     *
+     * @return the solid color used for mixing
+     */
+    public Color getMixColor() {
+        return mixColor;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public BufferedImage filter(BufferedImage src, BufferedImage dst) {
+        if (dst == null) {
+            dst = createCompatibleDestImage(src, null);
+        }
+
+        int width = src.getWidth();
+        int height = src.getHeight();
+
+        int[] pixels = new int[width * height];
+        GraphicsUtilities.getPixels(src, 0, 0, width, height, pixels);
+        mixColor(pixels);
+        GraphicsUtilities.setPixels(dst, 0, 0, width, height, pixels);
+
+        return dst;
+    }
+
+    private void mixColor(int[] pixels) {
+        for (int i = 0; i < pixels.length; i++) {
+            int argb = pixels[i];
+            pixels[i] = (argb & 0xFF000000) |
+                        preMultipliedRed[(argb >> 16)   & 0xFF] << 16 |
+                        preMultipliedGreen[(argb >> 8)  & 0xFF] <<  8 |
+                        preMultipliedBlue[argb & 0xFF];
+        }
+    }
+}
Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/GraphicsUtilities.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/GraphicsUtilities.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/GraphicsUtilities.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,528 @@
+package org.jbpm.trainticketdemo.gui.fancy;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.awt.GraphicsConfiguration;
+import java.awt.Transparency;
+import java.awt.Graphics;
+import java.awt.GraphicsEnvironment;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.io.IOException;
+import java.net.URL;
+import javax.imageio.ImageIO;
+
+/**
+ * <p><code>GraphicsUtilities</code> contains a set of tools to perform
+ * common graphics operations easily. These operations are divided into
+ * several themes, listed below.</p>
+ * <h2>Compatible Images</h2>
+ * <p>Compatible images can, and should, be used to increase drawing
+ * performance. This class provides a number of methods to load compatible
+ * images directly from files or to convert existing images to compatibles
+ * images.</p>
+ * <h2>Creating Thumbnails</h2>
+ * <p>This class provides a number of methods to easily scale down images.
+ * Some of these methods offer a trade-off between speed and result quality and
+ * shouuld be used all the time. They also offer the advantage of producing
+ * compatible images, thus automatically resulting into better runtime
+ * performance.</p>
+ * <p>All these methodes are both faster than
+ * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce
+ * better-looking results than the various <code>drawImage()</code> methods
+ * in {@link java.awt.Graphics}, which can be used for image scaling.</p>
+ * <h2>Image Manipulation</h2>
+ * <p>This class provides two methods to get and set pixels in a buffered image.
+ * These methods try to avoid unmanaging the image in order to keep good
+ * performance.</p>
+ *
+ * @author Romain Guy <romain.guy at mac.com>
+ */
+public class GraphicsUtilities {
+    private GraphicsUtilities() {
+    }
+
+    // Returns the graphics configuration for the primary screen
+    private static GraphicsConfiguration getGraphicsConfiguration() {
+        return GraphicsEnvironment.getLocalGraphicsEnvironment().
+                    getDefaultScreenDevice().getDefaultConfiguration();
+    }
+
+    /**
+     * <p>Returns a new <code>BufferedImage</code> using the same color model
+     * as the image passed as a parameter. The returned image is only compatible
+     * with the image passed as a parameter. This does not mean the returned
+     * image is compatible with the hardware.</p>
+     *
+     * @param image the reference image from which the color model of the new
+     *   image is obtained
+     * @return a new <code>BufferedImage</code>, compatible with the color model
+     *   of <code>image</code>
+     */
+    public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
+        ColorModel cm = image.getColorModel();
+        return new BufferedImage(cm,
+            cm.createCompatibleWritableRaster(image.getWidth(),
+                                              image.getHeight()),
+            cm.isAlphaPremultiplied(), null);
+    }
+
+    /**
+     * <p>Returns a new compatible image with the same width, height and
+     * transparency as the image specified as a parameter.</p>
+     *
+     * @see java.awt.Transparency
+     * @see #createCompatibleImage(int, int)
+     * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
+     * @see #createCompatibleTranslucentImage(int, int)
+     * @see #loadCompatibleImage(java.net.URL)
+     * @see #toCompatibleImage(java.awt.image.BufferedImage)
+     * @param image the reference image from which the dimension and the
+     *   transparency of the new image are obtained
+     * @return a new compatible <code>BufferedImage</code> with the same
+     *   dimension and transparency as <code>image</code>
+     */
+    public static BufferedImage createCompatibleImage(BufferedImage image) {
+        return createCompatibleImage(image, image.getWidth(), image.getHeight());
+    }
+
+    /**
+     * <p>Returns a new compatible image of the specified width and height, and
+     * the same transparency setting as the image specified as a parameter.</p>
+     *
+     * @see java.awt.Transparency
+     * @see #createCompatibleImage(java.awt.image.BufferedImage)
+     * @see #createCompatibleImage(int, int)
+     * @see #createCompatibleTranslucentImage(int, int)
+     * @see #loadCompatibleImage(java.net.URL)
+     * @see #toCompatibleImage(java.awt.image.BufferedImage)
+     * @param width the width of the new image
+     * @param height the height of the new image
+     * @param image the reference image from which the transparency of the new
+     *   image is obtained
+     * @return a new compatible <code>BufferedImage</code> with the same
+     *   transparency as <code>image</code> and the specified dimension
+     */
+    public static BufferedImage createCompatibleImage(BufferedImage image,
+                                                      int width, int height) {
+        return getGraphicsConfiguration().createCompatibleImage(width, height,
+                                                   image.getTransparency());
+    }
+
+    /**
+     * <p>Returns a new opaque compatible image of the specified width and
+     * height.</p>
+     *
+     * @see #createCompatibleImage(java.awt.image.BufferedImage)
+     * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
+     * @see #createCompatibleTranslucentImage(int, int)
+     * @see #loadCompatibleImage(java.net.URL)
+     * @see #toCompatibleImage(java.awt.image.BufferedImage)
+     * @param width the width of the new image
+     * @param height the height of the new image
+     * @return a new opaque compatible <code>BufferedImage</code> of the
+     *   specified width and height
+     */
+    public static BufferedImage createCompatibleImage(int width, int height) {
+        return getGraphicsConfiguration().createCompatibleImage(width, height);
+    }
+
+    /**
+     * <p>Returns a new translucent compatible image of the specified width
+     * and height.</p>
+     *
+     * @see #createCompatibleImage(java.awt.image.BufferedImage)
+     * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
+     * @see #createCompatibleImage(int, int)
+     * @see #loadCompatibleImage(java.net.URL)
+     * @see #toCompatibleImage(java.awt.image.BufferedImage)
+     * @param width the width of the new image
+     * @param height the height of the new image
+     * @return a new translucent compatible <code>BufferedImage</code> of the
+     *   specified width and height
+     */
+    public static BufferedImage createCompatibleTranslucentImage(int width,
+                                                                 int height) {
+        return getGraphicsConfiguration().createCompatibleImage(width, height,
+                                                   Transparency.TRANSLUCENT);
+    }
+
+    /**
+     * <p>Returns a new compatible image from a URL. The image is loaded from the
+     * specified location and then turned, if necessary into a compatible
+     * image.</p>
+     *
+     * @see #createCompatibleImage(java.awt.image.BufferedImage)
+     * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
+     * @see #createCompatibleImage(int, int)
+     * @see #createCompatibleTranslucentImage(int, int)
+     * @see #toCompatibleImage(java.awt.image.BufferedImage)
+     * @param resource the URL of the picture to load as a compatible image
+     * @return a new translucent compatible <code>BufferedImage</code> of the
+     *   specified width and height
+     * @throws java.io.IOException if the image cannot be read or loaded
+     */
+    public static BufferedImage loadCompatibleImage(URL resource)
+            throws IOException {
+        BufferedImage image = ImageIO.read(resource);
+        return toCompatibleImage(image);
+    }
+
+    /**
+     * <p>Return a new compatible image that contains a copy of the specified
+     * image. This method ensures an image is compatible with the hardware,
+     * and therefore optimized for fast blitting operations.</p>
+     *
+     * @see #createCompatibleImage(java.awt.image.BufferedImage)
+     * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
+     * @see #createCompatibleImage(int, int)
+     * @see #createCompatibleTranslucentImage(int, int)
+     * @see #loadCompatibleImage(java.net.URL)
+     * @param image the image to copy into a new compatible image
+     * @return a new compatible copy, with the
+     *   same width and height and transparency and content, of <code>image</code>
+     */
+    public static BufferedImage toCompatibleImage(BufferedImage image) {
+        if (image.getColorModel().equals(
+                getGraphicsConfiguration().getColorModel())) {
+            return image;
+        }
+
+        BufferedImage compatibleImage =
+                getGraphicsConfiguration().createCompatibleImage(
+                    image.getWidth(), image.getHeight(),
+                    image.getTransparency());
+        Graphics g = compatibleImage.getGraphics();
+        g.drawImage(image, 0, 0, null);
+        g.dispose();
+
+        return compatibleImage;
+    }
+
+    /**
+     * <p>Returns a thumbnail of a source image. <code>newSize</code> defines
+     * the length of the longest dimension of the thumbnail. The other
+     * dimension is then computed according to the dimensions ratio of the
+     * original picture.</p>
+     * <p>This method favors speed over quality. When the new size is less than
+     * half the longest dimension of the source image,
+     * {@link #createThumbnail(BufferedImage, int)} or
+     * {@link #createThumbnail(BufferedImage, int, int)} should be used instead
+     * to ensure the quality of the result without sacrificing too much
+     * performance.</p>
+     *
+     * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
+     * @see #createThumbnail(java.awt.image.BufferedImage, int)
+     * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
+     * @param image the source image
+     * @param newSize the length of the largest dimension of the thumbnail
+     * @return a new compatible <code>BufferedImage</code> containing a
+     *   thumbnail of <code>image</code>
+     * @throws IllegalArgumentException if <code>newSize</code> is larger than
+     *   the largest dimension of <code>image</code> or &lt;= 0
+     */
+    public static BufferedImage createThumbnailFast(BufferedImage image,
+                                                    int newSize) {
+        float ratio;
+        int width = image.getWidth();
+        int height = image.getHeight();
+
+        if (width > height) {
+            if (newSize >= width) {
+                throw new IllegalArgumentException("newSize must be lower than" +
+                                                   " the image width");
+            } else if (newSize <= 0) {
+                 throw new IllegalArgumentException("newSize must" +
+                                                    " be greater than 0");
+            }
+
+            ratio = (float) width / (float) height;
+            width = newSize;
+            height = (int) (newSize / ratio);
+        } else {
+            if (newSize >= height) {
+                throw new IllegalArgumentException("newSize must be lower than" +
+                                                   " the image height");
+            } else if (newSize <= 0) {
+                 throw new IllegalArgumentException("newSize must" +
+                                                    " be greater than 0");
+            }
+
+            ratio = (float) height / (float) width;
+            height = newSize;
+            width = (int) (newSize / ratio);
+        }
+
+        BufferedImage temp = createCompatibleImage(image, width, height);
+        Graphics2D g2 = temp.createGraphics();
+        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+        g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
+        g2.dispose();
+
+        return temp;
+    }
+
+    /**
+     * <p>Returns a thumbnail of a source image.</p>
+     * <p>This method favors speed over quality. When the new size is less than
+     * half the longest dimension of the source image,
+     * {@link #createThumbnail(BufferedImage, int)} or
+     * {@link #createThumbnail(BufferedImage, int, int)} should be used instead
+     * to ensure the quality of the result without sacrificing too much
+     * performance.</p>
+     *
+     * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
+     * @see #createThumbnail(java.awt.image.BufferedImage, int)
+     * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
+     * @param image the source image
+     * @param newWidth the width of the thumbnail
+     * @param newHeight the height of the thumbnail
+     * @return a new compatible <code>BufferedImage</code> containing a
+     *   thumbnail of <code>image</code>
+     * @throws IllegalArgumentException if <code>newWidth</code> is larger than
+     *   the width of <code>image</code> or if code>newHeight</code> is larger
+     *   than the height of <code>image</code> or if one of the dimensions
+     *   is &lt;= 0
+     */
+    public static BufferedImage createThumbnailFast(BufferedImage image,
+                                                    int newWidth, int newHeight) {
+        if (newWidth >= image.getWidth() ||
+            newHeight >= image.getHeight()) {
+            throw new IllegalArgumentException("newWidth and newHeight cannot" +
+                                               " be greater than the image" +
+                                               " dimensions");
+        } else if (newWidth <= 0 || newHeight <= 0) {
+            throw new IllegalArgumentException("newWidth and newHeight must" +
+                                               " be greater than 0");
+        }
+
+        BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
+        Graphics2D g2 = temp.createGraphics();
+        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+        g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
+        g2.dispose();
+
+        return temp;
+    }
+
+    /**
+     * <p>Returns a thumbnail of a source image. <code>newSize</code> defines
+     * the length of the longest dimension of the thumbnail. The other
+     * dimension is then computed according to the dimensions ratio of the
+     * original picture.</p>
+     * <p>This method offers a good trade-off between speed and quality.
+     * The result looks better than
+     * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when
+     * the new size is less than half the longest dimension of the source
+     * image, yet the rendering speed is almost similar.</p>
+     *
+     * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
+     * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
+     * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
+     * @param image the source image
+     * @param newSize the length of the largest dimension of the thumbnail
+     * @return a new compatible <code>BufferedImage</code> containing a
+     *   thumbnail of <code>image</code>
+     * @throws IllegalArgumentException if <code>newSize</code> is larger than
+     *   the largest dimension of <code>image</code> or &lt;= 0
+     */
+    public static BufferedImage createThumbnail(BufferedImage image,
+                                                int newSize) {
+        int width = image.getWidth();
+        int height = image.getHeight();
+
+        boolean isWidthGreater = width > height;
+
+        if (isWidthGreater) {
+            if (newSize >= width) {
+                throw new IllegalArgumentException("newSize must be lower than" +
+                                                   " the image width");
+            }
+        } else if (newSize >= height) {
+            throw new IllegalArgumentException("newSize must be lower than" +
+                                               " the image height");
+        }
+
+        if (newSize <= 0) {
+            throw new IllegalArgumentException("newSize must" +
+                                               " be greater than 0");
+        }
+
+        float ratioWH = (float) width / (float) height;
+        float ratioHW = (float) height / (float) width;
+
+        BufferedImage thumb = image;
+
+        do {
+            if (isWidthGreater) {
+                width /= 2;
+                if (width < newSize) {
+                    width = newSize;
+                }
+                height = (int) (width / ratioWH);
+            } else {
+                height /= 2;
+                if (height < newSize) {
+                    height = newSize;
+                }
+                width = (int) (height / ratioHW);
+            }
+
+
+            BufferedImage temp = createCompatibleImage(image, width, height);
+            Graphics2D g2 = temp.createGraphics();
+            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
+            g2.dispose();
+
+            thumb = temp;
+        } while (newSize != (isWidthGreater ? width : height));
+
+        return thumb;
+    }
+
+    /**
+     * <p>Returns a thumbnail of a source image.</p>
+     * <p>This method offers a good trade-off between speed and quality.
+     * The result looks better than
+     * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when
+     * the new size is less than half the longest dimension of the source
+     * image, yet the rendering speed is almost similar.</p>
+     *
+     * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
+     * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
+     * @see #createThumbnail(java.awt.image.BufferedImage, int)
+     * @param image the source image
+     * @param newWidth the width of the thumbnail
+     * @param newHeight the height of the thumbnail
+     * @return a new compatible <code>BufferedImage</code> containing a
+     *   thumbnail of <code>image</code>
+     * @throws IllegalArgumentException if <code>newWidth</code> is larger than
+     *   the width of <code>image</code> or if code>newHeight</code> is larger
+     *   than the height of <code>image or if one the dimensions is not &gt; 0</code>
+     */
+    public static BufferedImage createThumbnail(BufferedImage image,
+                                                int newWidth, int newHeight) {
+        int width = image.getWidth();
+        int height = image.getHeight();
+
+        if (newWidth >= width || newHeight >= height) {
+            throw new IllegalArgumentException("newWidth and newHeight cannot" +
+                                               " be greater than the image" +
+                                               " dimensions");
+        } else if (newWidth <= 0 || newHeight <= 0) {
+            throw new IllegalArgumentException("newWidth and newHeight must" +
+                                               " be greater than 0");
+        }
+
+        BufferedImage thumb = image;
+
+        do {
+            if (width > newWidth) {
+                width /= 2;
+                if (width < newWidth) {
+                    width = newWidth;
+                }
+            }
+
+            if (height > newHeight) {
+                height /= 2;
+                if (height < newHeight) {
+                    height = newHeight;
+                }
+            }
+
+            BufferedImage temp = createCompatibleImage(image, width, height);
+            Graphics2D g2 = temp.createGraphics();
+            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
+            g2.dispose();
+
+            thumb = temp;
+        } while (width != newWidth || height != newHeight);
+
+        return thumb;
+    }
+
+    /**
+     * <p>Returns an array of pixels, stored as integers, from a
+     * <code>BufferedImage</code>. The pixels are grabbed from a rectangular
+     * area defined by a location and two dimensions. Calling this method on
+     * an image of type different from <code>BufferedImage.TYPE_INT_ARGB</code>
+     * and <code>BufferedImage.TYPE_INT_RGB</code> will unmanage the image.</p>
+     *
+     * @param img the source image
+     * @param x the x location at which to start grabbing pixels
+     * @param y the y location at which to start grabbing pixels
+     * @param w the width of the rectangle of pixels to grab
+     * @param h the height of the rectangle of pixels to grab
+     * @param pixels a pre-allocated array of pixels of size w*h; can be null
+     * @return <code>pixels</code> if non-null, a new array of integers
+     *   otherwise
+     * @throws IllegalArgumentException is <code>pixels</code> is non-null and
+     *   of length &lt; w*h
+     */
+    public static int[] getPixels(BufferedImage img,
+                                  int x, int y, int w, int h, int[] pixels) {
+        if (w == 0 || h == 0) {
+            return new int[0];
+        }
+
+        if (pixels == null) {
+            pixels = new int[w * h];
+        } else if (pixels.length < w * h) {
+            throw new IllegalArgumentException("pixels array must have a length" +
+                                               " >= w*h");
+        }
+
+        int imageType = img.getType();
+        if (imageType == BufferedImage.TYPE_INT_ARGB ||
+            imageType == BufferedImage.TYPE_INT_RGB) {
+            Raster raster = img.getRaster();
+            return (int[]) raster.getDataElements(x, y, w, h, pixels);
+        }
+
+        // Unmanages the image
+        return img.getRGB(x, y, w, h, pixels, 0, w);
+    }
+
+    /**
+     * <p>Writes a rectangular area of pixels in the destination
+     * <code>BufferedImage</code>. Calling this method on
+     * an image of type different from <code>BufferedImage.TYPE_INT_ARGB</code>
+     * and <code>BufferedImage.TYPE_INT_RGB</code> will unmanage the image.</p>
+     *
+     * @param img the destination image
+     * @param x the x location at which to start storing pixels
+     * @param y the y location at which to start storing pixels
+     * @param w the width of the rectangle of pixels to store
+     * @param h the height of the rectangle of pixels to store
+     * @param pixels an array of pixels, stored as integers
+     * @throws IllegalArgumentException is <code>pixels</code> is non-null and
+     *   of length &lt; w*h
+     */
+    public static void setPixels(BufferedImage img,
+                                 int x, int y, int w, int h, int[] pixels) {
+        if (pixels == null || w == 0 || h == 0) {
+            return;
+        } else if (pixels.length < w * h) {
+            throw new IllegalArgumentException("pixels array must have a length" +
+                                               " >= w*h");
+        }
+
+        int imageType = img.getType();
+        if (imageType == BufferedImage.TYPE_INT_ARGB ||
+            imageType == BufferedImage.TYPE_INT_RGB) {
+            WritableRaster raster = img.getRaster();
+            raster.setDataElements(x, y, w, h, pixels);
+        } else {
+            // Unmanages the image
+            img.setRGB(x, y, w, h, pixels, 0, w);
+        }
+    }
+}
Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/LightButton.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/LightButton.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/LightButton.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,287 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.gui.fancy;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.RenderingHints;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.ButtonModel;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.plaf.basic.BasicButtonUI;
+
+/**
+ * @author Romain Guy & Chet Haase (Filthy Rich Clients)
+ * @author Joram Barrez (some small adaptations)
+ */
+public class LightButton extends JButton implements MouseListener {
+
+    private float shadowOffsetX;
+    private float shadowOffsetY;
+    
+    private Color shadowColor = Color.BLACK;
+    private int shadowDirection = 60;
+    
+    private Image normalButton, normalButtonPressed, buttonHighlight;
+    private int shadowDistance = 1;
+    private Insets sourceInsets = new Insets(6, 7, 6, 8);
+    private Dimension buttonDimension = new Dimension(116, 35);
+    private Color buttonForeground = Color.WHITE;
+    private Font buttonFont = new Font("Arial", Font.BOLD, 22);
+    
+    private float ghostValue;
+    
+    private boolean main = false;
+
+    public LightButton(String text) {
+        this();
+        setText(text);
+    }
+    
+    public LightButton(Action a) {
+        this();
+        setAction(a);
+    }
+    
+    public LightButton() {
+        computeShadow();
+
+        setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+        setForeground(buttonForeground);
+        setFont(buttonFont);
+        setContentAreaFilled(false);
+        setBorderPainted(false);
+        setFocusable(false);
+        
+        normalButton = loadImage("/button-normal.png");
+        normalButtonPressed = loadImage("/button-normal-pressed.png");
+        buttonHighlight = loadImage("/header-halo.png");
+        
+        // Hacky? Hacky!
+        setUI(new BasicButtonUI() {
+            @Override
+            public Dimension getMinimumSize(JComponent c) {
+                return getPreferredSize(c);
+            }
+            
+            @Override
+            public Dimension getMaximumSize(JComponent c) {
+                return getPreferredSize(c);
+            }
+            
+            @Override
+            public Dimension getPreferredSize(JComponent c) {
+                Insets insets = c.getInsets();
+                Dimension d = new Dimension(buttonDimension);
+                d.width += insets.left + insets.right;
+                d.height += insets.top + insets.bottom;
+                return d;
+            }
+        });
+        
+        addMouseListener(this);
+    }
+    
+    private static Image loadImage(String fileName) {
+        try {
+            return ImageIO.read(LightButton.class.getResource(fileName));
+        } catch (IOException ex) {
+            return null;
+        }
+    }
+    
+    private void computeShadow() {
+        double rads = Math.toRadians(shadowDirection);
+        shadowOffsetX = (float) Math.cos(rads) * shadowDistance;
+        shadowOffsetY = (float) Math.sin(rads) * shadowDistance;
+    }
+    
+    private Image getImage(boolean armed) {
+        return armed ? normalButtonPressed : normalButton;
+    }
+    
+    @Override
+    protected void paintComponent(Graphics g) {
+        Graphics2D g2 = (Graphics2D) g;
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                RenderingHints.VALUE_ANTIALIAS_ON);
+        
+        ButtonModel m = getModel();
+        Insets insets = getInsets();
+        
+        int width = getWidth() - insets.left - insets.right;
+        int height = getHeight() - insets.top - insets.bottom;
+        
+        tileStretchPaint(g2,this,(BufferedImage) getImage(m.isArmed()), sourceInsets);
+        
+        if (getModel().isRollover()) {
+            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            g2.drawImage(buttonHighlight,
+                    insets.left + 2, insets.top + 2,
+                    width - 4, height - 4, null);
+        }
+        
+        FontMetrics fm = getFontMetrics(getFont());
+        TextLayout layout = new TextLayout(getText(),
+                getFont(),
+                g2.getFontRenderContext());
+        Rectangle2D bounds = layout.getBounds();
+        
+        int x = (int) (getWidth() - insets.left - insets.right -
+                bounds.getWidth()) / 2;
+        //x -= 2;
+        int y = (getHeight() - insets.top - insets.bottom -
+                 fm.getMaxAscent() - fm.getMaxDescent()) / 2;
+        y += fm.getAscent() - 1;
+        
+        if (m.isArmed()) {
+            x += 1;
+            y += 1;
+        }
+        
+        g2.setColor(shadowColor);
+        layout.draw(g2,
+                x + (int) Math.ceil(shadowOffsetX),
+                y + (int) Math.ceil(shadowOffsetY));
+        g2.setColor(getForeground());
+        layout.draw(g2, x, y);
+    }
+    
+    /**
+     * Draws an image on top of a component by doing a 3x3 grid stretch of the image
+     * using the specified insets.
+     *
+     * From SwingLabs, licensed under LGPL
+     */
+    public static void tileStretchPaint(Graphics g, 
+                JComponent comp,
+                BufferedImage img,
+                Insets ins) {
+        
+        int left = ins.left;
+        int right = ins.right;
+        int top = ins.top;
+        int bottom = ins.bottom;
+        
+        // top
+        g.drawImage(img,
+                    0,0,left,top,
+                    0,0,left,top,
+                    null);
+        g.drawImage(img,
+                    left,                 0, 
+                    comp.getWidth() - right, top, 
+                    left,                 0, 
+                    img.getWidth()  - right, top, 
+                    null);
+        g.drawImage(img,
+                    comp.getWidth() - right, 0, 
+                    comp.getWidth(),         top, 
+                    img.getWidth()  - right, 0, 
+                    img.getWidth(),          top, 
+                    null);
+
+        // middle
+        g.drawImage(img,
+                    0,    top, 
+                    left, comp.getHeight()-bottom,
+                    0,    top,   
+                    left, img.getHeight()-bottom,
+                    null);
+        
+        g.drawImage(img,
+                    left,                  top, 
+                    comp.getWidth()-right,      comp.getHeight()-bottom,
+                    left,                  top,   
+                    img.getWidth()-right,  img.getHeight()-bottom,
+                    null);
+         
+        g.drawImage(img,
+                    comp.getWidth()-right,     top, 
+                    comp.getWidth(),           comp.getHeight()-bottom,
+                    img.getWidth()-right, top,   
+                    img.getWidth(),       img.getHeight()-bottom,
+                    null);
+        
+        // bottom
+        g.drawImage(img,
+                    0,comp.getHeight()-bottom, 
+                    left, comp.getHeight(),
+                    0,img.getHeight()-bottom,   
+                    left,img.getHeight(),
+                    null);
+        g.drawImage(img,
+                    left,                    comp.getHeight()-bottom, 
+                    comp.getWidth()-right,        comp.getHeight(),
+                    left,                    img.getHeight()-bottom,   
+                    img.getWidth()-right,    img.getHeight(),
+                    null);
+        g.drawImage(img,
+                    comp.getWidth()-right,     comp.getHeight()-bottom, 
+                    comp.getWidth(),           comp.getHeight(),
+                    img.getWidth()-right, img.getHeight()-bottom,   
+                    img.getWidth(),       img.getHeight(),
+                    null);
+    }
+    
+    private static final Cursor NORMAL_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
+	private static final Cursor HAND_CURSOR = new Cursor(Cursor.HAND_CURSOR);
+
+	public void mouseClicked(MouseEvent arg0) {
+
+	}
+
+	public void mouseEntered(MouseEvent arg0) {
+		setCursor(HAND_CURSOR);
+	}
+
+	public void mouseExited(MouseEvent arg0) {
+		setCursor(NORMAL_CURSOR);
+	}
+
+	public void mousePressed(MouseEvent arg0) {
+
+	}
+
+	public void mouseReleased(MouseEvent arg0) {
+
+	}
+
+}
+

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/ProgressGlassPane.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/ProgressGlassPane.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/ProgressGlassPane.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,150 @@
+package org.jbpm.trainticketdemo.gui.fancy;
+/*
+ * Copyright (c) 2007, Romain Guy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.GradientPaint;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.LinearGradientPaint;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+
+import javax.swing.JComponent;
+
+/**
+ *
+ * @author Romain Guy (Filthy Rich Clients)
+ * @jbarrez (small adaptations)
+ */
+public class ProgressGlassPane extends JComponent {
+	
+    private static final int BAR_WIDTH = 200;
+    private static final int BAR_HEIGHT = 10;
+    
+    private static final Color TEXT_COLOR = new Color(0x333333);
+    private static final Color BORDER_COLOR = new Color(0x333333);
+    
+    private static final float[] GRADIENT_FRACTIONS = new float[] {
+        0.0f, 0.499f, 0.5f, 1.0f
+    };
+    private static final Color[] GRADIENT_COLORS = new Color[] {
+        Color.GRAY, Color.DARK_GRAY, Color.BLACK, Color.GRAY
+    };
+    private static final Color GRADIENT_COLOR2 = Color.WHITE;
+    private static final Color GRADIENT_COLOR1 = Color.GRAY;
+
+    private String message = "Sending SMS...";
+    private int progress = 0;
+    
+    public ProgressGlassPane() {
+        setBackground(Color.WHITE);
+        setFont(new Font("Default", Font.BOLD, 16));
+    }
+
+    public int getProgress() {
+        return progress;
+    }
+
+    public void setProgress(int progress) {
+        int oldProgress = this.progress;
+        this.progress = progress;
+        
+        // computes the damaged area
+        FontMetrics metrics = getGraphics().getFontMetrics(getFont()); 
+        int w = (int) (BAR_WIDTH * ((float) oldProgress / 100.0f));
+        int x = w + (getWidth() - BAR_WIDTH) / 2;
+        int y = (getHeight() - BAR_HEIGHT) / 2;
+        y += metrics.getDescent() / 2;
+        
+        w = (int) (BAR_WIDTH * ((float) progress / 100.0f)) - w;
+        int h = BAR_HEIGHT;
+        
+        repaint(x, y, w, h);
+    }
+    
+    @Override
+    protected void paintComponent(Graphics g) {
+        // enables anti-aliasing
+        Graphics2D g2 = (Graphics2D) g;
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        
+        // sets a 65% translucent composite
+        AlphaComposite alpha = AlphaComposite.SrcOver.derive(0.65f);
+        g2.setComposite(alpha);
+        
+        // fills the background
+        g2.setColor(getBackground());;
+        //g2.fillRect(40, 142, 246, 277); // Only draw the visible part of the phone
+        Rectangle clip = g2.getClipBounds();
+        g2.fillRect(clip.x, clip.y, clip.width, clip.height);
+        
+        // centers the progress bar on screen
+        FontMetrics metrics = g.getFontMetrics();        
+        int x = (getWidth() - BAR_WIDTH) / 2;
+        int y = (getHeight() - BAR_HEIGHT - metrics.getDescent()) / 2;
+        
+        // draws the text
+        g2.setColor(TEXT_COLOR);
+        g2.drawString(message, x, y);
+        
+        // goes to the position of the progress bar
+        y += metrics.getDescent();
+        
+        // computes the size of the progress indicator
+        int w = (int) (BAR_WIDTH * ((float) progress / 100.0f));
+        int h = BAR_HEIGHT;
+        
+        // draws the content of the progress bar
+        Paint paint = g2.getPaint();
+        
+        // bar's background
+        Paint gradient = new GradientPaint(x, y, GRADIENT_COLOR1,
+                x, y + h, GRADIENT_COLOR2);
+        g2.setPaint(gradient);
+        g2.fillRect(x, y, BAR_WIDTH, BAR_HEIGHT);
+        
+        // actual progress
+        gradient = new LinearGradientPaint(x, y, x, y + h,
+                GRADIENT_FRACTIONS, GRADIENT_COLORS);
+        g2.setPaint(gradient);
+        g2.fillRect(x, y, w, h);
+        
+        g2.setPaint(paint);
+        
+        // draws the progress bar border
+        g2.drawRect(x, y, BAR_WIDTH, BAR_HEIGHT);
+    }
+}
Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/PulsatingLogo.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/PulsatingLogo.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/PulsatingLogo.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,121 @@
+package org.jbpm.trainticketdemo.gui.fancy;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ConvolveOp;
+import java.awt.image.Kernel;
+import java.io.IOException;
+
+import javax.swing.JComponent;
+
+import org.jdesktop.animation.timing.Animator;
+import org.jdesktop.animation.timing.interpolation.PropertySetter;
+
+public class PulsatingLogo extends JComponent {
+
+	private static final long serialVersionUID = 1L;
+	
+	private BufferedImage image;
+	private BufferedImage glow;
+	private float alpha = 0.0f;
+
+	public PulsatingLogo(String imageName) {
+		try {
+			image = GraphicsUtilities.loadCompatibleImage(getClass().getResource(imageName));
+		} catch (IOException ex) {
+			ex.printStackTrace();
+		}
+	}
+
+	@Override
+	public Dimension getPreferredSize() {
+		return new Dimension(image.getWidth(), image.getHeight());
+	}
+
+	@Override
+	protected void paintComponent(Graphics g) {
+		Graphics2D g2;
+
+		if (glow == null) {
+			glow = GraphicsUtilities.createCompatibleImage(image);
+			g2 = glow.createGraphics();
+			g2.drawImage(image, 0, 0, null);
+			g2.dispose();
+
+			BufferedImageOp filter = getGaussianBlurFilter(24, true);
+			glow = filter.filter(glow, null);
+			filter = getGaussianBlurFilter(24, false);
+			glow = filter.filter(glow, null);
+			filter = new ColorTintFilter(Color.WHITE, 1.0f);
+			glow = filter.filter(glow, null);
+
+			startAnimator();
+		}
+
+		int x = (getWidth() - image.getWidth()) / 2;
+		int y = (getHeight() - image.getHeight()) / 2;
+
+		g2 = (Graphics2D) g.create();
+
+		g2.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
+		g2.drawImage(glow, x, y, null);
+		g2.setComposite(AlphaComposite.SrcOver);
+		g2.drawImage(image, x, y, null);
+	}
+
+	private void startAnimator() {
+		PropertySetter setter = new PropertySetter(this, "alpha", 0.0f, 1.0f);
+		Animator animator = new Animator(600, Animator.INFINITE,
+				Animator.RepeatBehavior.REVERSE, setter);
+		animator.start();
+	}
+
+	public float getAlpha() {
+		return alpha;
+	}
+
+	public void setAlpha(float alpha) {
+		this.alpha = alpha;
+		repaint();
+	}
+
+	public static ConvolveOp getGaussianBlurFilter(int radius,
+			boolean horizontal) {
+		if (radius < 1) {
+			throw new IllegalArgumentException("Radius must be >= 1");
+		}
+
+		int size = radius * 2 + 1;
+		float[] data = new float[size];
+
+		float sigma = radius / 3.0f;
+		float twoSigmaSquare = 2.0f * sigma * sigma;
+		float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
+		float total = 0.0f;
+
+		for (int i = -radius; i <= radius; i++) {
+			float distance = i * i;
+			int index = i + radius;
+			data[index] = (float) Math.exp(-distance / twoSigmaSquare)
+					/ sigmaRoot;
+			total += data[index];
+		}
+
+		for (int i = 0; i < data.length; i++) {
+			data[i] /= total;
+		}
+
+		Kernel kernel = null;
+		if (horizontal) {
+			kernel = new Kernel(size, 1, data);
+		} else {
+			kernel = new Kernel(1, size, data);
+		}
+		return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
+	}
+}
\ No newline at end of file

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/SmsReceivedGlassPane.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/SmsReceivedGlassPane.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/SmsReceivedGlassPane.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,80 @@
+package org.jbpm.trainticketdemo.gui.fancy;
+/*
+ * Copyright (c) 2007, Romain Guy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+
+import javax.swing.JComponent;
+
+/**
+ * All credits to the great work of
+ * @author Romain Guy (Filthy Rich Clients)
+ */
+public class SmsReceivedGlassPane extends JComponent {
+	
+	private static final long serialVersionUID = 1L;
+    
+    private static final String MESSAGE = "SMS message received";
+    
+    public SmsReceivedGlassPane() {
+    	setLayout(null);
+        setBackground(Color.WHITE);
+        setFont(new Font("Default", Font.BOLD, 16));
+        
+        PulsatingLogo pulsatingLogo = new PulsatingLogo("/sms_received.png");
+        pulsatingLogo.setBounds(53, 175, 200, 200);
+        add(pulsatingLogo);
+    }
+    
+    @Override
+    protected void paintComponent(Graphics g) {
+    	
+        // enables anti-aliasing
+        Graphics2D g2 = (Graphics2D) g;
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        
+        AlphaComposite alpha = AlphaComposite.SrcOver.derive(0.35f);
+        g2.setComposite(alpha);
+        
+        // fills the background
+        g2.setColor(getBackground());
+        //g2.fillRect(40, 142, 246, 277); // Only draw the visible part of the phone
+        Rectangle clip = g2.getClipBounds();
+        g2.fillRect(clip.x, clip.y, clip.width, clip.height);
+    }
+
+}
Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/SmsReceivedPopup.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/SmsReceivedPopup.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/gui/fancy/SmsReceivedPopup.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,75 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.gui.fancy;
+
+import java.awt.Component;
+
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+
+
+/**
+ * @author Joram Barrez
+ */
+public class SmsReceivedPopup extends Thread {
+	
+	private JComponent source;
+	
+	public SmsReceivedPopup(JComponent source) {
+		this.source = source;
+	}
+	
+	@Override
+	public void run() {
+		
+		// For the demo clarity, we slow the receival of the sms down
+		try {
+			Thread.sleep(2500);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+		
+		// Show the new mail notification
+		SmsReceivedGlassPane glassPane = new SmsReceivedGlassPane();
+		JFrame frame = (JFrame)SwingUtilities.getWindowAncestor(source);
+		Component originalGlassPane = frame.getGlassPane();
+		frame.setGlassPane(glassPane); // Kinda dirty ...
+		glassPane.setVisible(true);
+		
+		// Play new message sound
+		new AePlayWave("gotmail.wav").play();
+		
+		// Wait a bit before removing the glass pane
+		try {
+			Thread.sleep(2000);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+		
+		// Restore to original
+		glassPane.setVisible(false);
+		frame.setGlassPane(originalGlassPane);
+		
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/http/HttpListener.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/http/HttpListener.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/http/HttpListener.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,131 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.http;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.net.URLDecoder;
+import java.util.concurrent.Executors;
+
+import org.jbpm.trainticketdemo.model.Inbox;
+import org.jbpm.trainticketdemo.model.SmsMessage;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ * @author Joram Barrez
+ */
+public class HttpListener {
+	
+	public static final int HTTP_PORT = 4321;
+	
+	public static final String URL = "/cellphone_sms_listener";
+	
+	private HttpServer server;
+	
+	private Inbox inbox;
+	
+	public HttpListener(Inbox inbox) throws Exception {
+		this.inbox = inbox;
+		InetSocketAddress address = new InetSocketAddress(HTTP_PORT);
+		server = HttpServer.create(address, 0);
+		server.createContext(URL, new SmsHandler());
+		server.setExecutor(Executors.newCachedThreadPool());
+	}
+	
+	public void start() {
+		server.start();
+	}
+	
+	public void stop() {
+		server.stop(0);
+	}
+	
+	private class SmsHandler implements HttpHandler {
+
+		public void handle(HttpExchange httpExchange) throws IOException {
+			if (httpExchange.getRequestMethod().equalsIgnoreCase("POST")) {
+				
+				// Read the raw content of the message
+				StringBuilder strb = new StringBuilder();				
+				BufferedReader br = null;
+				try {
+					br = new BufferedReader(new InputStreamReader(httpExchange.getRequestBody()));
+					String line = br.readLine();
+					while (line != null) {
+						strb.append(line);
+						line = br.readLine();
+					} 
+				} finally {
+					if (br != null) {
+						br.close();
+					}
+				}
+				httpExchange.sendResponseHeaders(200, 0);
+				httpExchange.close();
+				
+				// add the parsed message to the cellphone inbox
+				SmsMessage smsMessage = parseRawMessage(strb.toString());
+				inbox.addMessage(smsMessage);
+			}
+		}
+		
+		private SmsMessage parseRawMessage(String message)  {
+			
+			SmsMessage result = new SmsMessage();
+			
+			String decoded = "";
+			try {
+				decoded = URLDecoder.decode(message, "UTF-8");
+			} catch (UnsupportedEncodingException e) {
+				e.printStackTrace();
+			}
+			
+			// parse string of form 'date=Tue+18+2009&param2=1+a'
+			String[] splitOnParamChar = decoded.split("&");
+			for (String s : splitOnParamChar) {
+				
+				String[] splitOnKeyValue = s.split("=");
+				String key = splitOnKeyValue[0];
+				String value = splitOnKeyValue[1];
+				
+				if (key.equalsIgnoreCase("sendDate")) {
+					result.setDate(value);
+				} else if (key.equalsIgnoreCase("msgContent")) {
+					result.setContent(value);
+				} else if (key.equalsIgnoreCase("senderNr")) {
+					result.setSender(value);
+				}
+				
+			}
+			
+			return result;
+		}
+		
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/message/MessageQueue.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/message/MessageQueue.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/message/MessageQueue.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,84 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.message;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * @author Joram Barrez
+ */
+public class MessageQueue {
+	
+	private static MessageQueue instance;
+	
+	private Queue<String> sendQueue;
+	
+	private Queue<String> receiveQueue;
+	
+	public MessageQueue() {
+		this.sendQueue = new LinkedList<String>();
+		this.receiveQueue = new LinkedList<String>();
+	}
+	
+	public void addSendMessage(String message) {
+		sendQueue.add(message);
+	}
+	
+	public void addReceivedMessage(String message) {
+		receiveQueue.add(message);
+	}
+	
+	public synchronized String popMessageToSend() {
+		return sendQueue.poll();
+	}
+	
+	public synchronized String popReceivedMessage() {
+		return receiveQueue.poll();
+	}
+	
+	public synchronized List<String> popAllMessagesToSend() {
+		List<String> allMessages = new ArrayList<String>();
+		while (sendQueue.peek() != null) {
+			allMessages.add(sendQueue.poll());
+		}
+		return allMessages;
+	}
+	
+	public synchronized List<String> popAllReceivedMessages() {
+		List<String> allMessages = new ArrayList<String>();
+		while (receiveQueue.peek() != null) {
+			allMessages.add(receiveQueue.poll());
+		}
+		return allMessages;
+	}
+	
+	public static MessageQueue getInstance() {
+		if (instance == null) {
+			instance = new MessageQueue();
+		}
+		return instance;
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/Inbox.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/Inbox.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/Inbox.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,74 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * @author Joram Barrez
+ */
+public class Inbox {
+
+	private ArrayList<SmsMessage> messages;
+
+	private EventListenerList listenerList = new EventListenerList();
+
+	public Inbox() {
+		this.messages = new ArrayList<SmsMessage>();
+	}
+
+	public void addMessage(SmsMessage smsMessage) {
+		messages.add(smsMessage);
+		fireStateChanged();
+	}
+
+	public Iterator<SmsMessage> iterator() {
+		ArrayList<SmsMessage> reversedList = new ArrayList<SmsMessage>(messages);
+		Collections.reverse(reversedList);
+		return reversedList.iterator();
+	}
+
+	public void addChangeListener(ChangeListener l) {
+		listenerList.add(ChangeListener.class, l);
+	}
+
+	public void removeChangeListener(ChangeListener l) {
+		listenerList.remove(ChangeListener.class, l);
+	}
+
+	private final ChangeEvent changeEvent = new ChangeEvent(this);
+
+	private void fireStateChanged() {
+		Object[] listeners = listenerList.getListenerList();
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == ChangeListener.class)
+				((ChangeListener) listeners[i + 1]).stateChanged(changeEvent);
+		}
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/SmsMessage.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/SmsMessage.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/model/SmsMessage.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,69 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.model;
+
+/**
+ * @author Joram Barrez
+ */
+public class SmsMessage {
+	
+	private String sender;
+	
+	private String date;
+	
+	private String content;
+	
+	private boolean read;
+
+	public String getDate() {
+		return date;
+	}
+
+	public void setDate(String date) {
+		this.date = date;
+	}
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public String getSender() {
+		return sender;
+	}
+
+	public void setSender(String sender) {
+		this.sender = sender;
+	}
+
+	public boolean isRead() {
+		return read;
+	}
+
+	public void setRead(boolean read) {
+		this.read = read;
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/service/SmsService.java
===================================================================
--- projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/service/SmsService.java	                        (rev 0)
+++ projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/java/org/jbpm/trainticketdemo/service/SmsService.java	2009-08-26 15:11:39 UTC (rev 5542)
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.trainticketdemo.service;
+
+import java.util.Date;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.jbpm.trainticketdemo.http.HttpListener;
+
+/**
+ * @author Joram Barrez
+ */
+public class SmsService {
+	
+	private static final String POST_URL = "http://localhost:8080/jbpm-on-rails/receive_sms";
+	
+	private static final String PARAM_SEND_DATE = "sendDate";
+	
+	private static final String PARAM_MSG_CONTENT = "msgContent";
+	
+	private static final String PARAM_SENDER_NR = "senderNr";
+	
+	private static final String VALUE_SENDER_NR = "EMULATOR";
+	
+	public static void sendSms(final String textMessage) throws Exception {
+		PostMethod post = new PostMethod(POST_URL);
+		post.addParameter(PARAM_SEND_DATE, new Date().toString());
+		post.addParameter(PARAM_MSG_CONTENT, textMessage);
+		post.addParameter(PARAM_SENDER_NR, VALUE_SENDER_NR + 
+				"-http://localhost:" + HttpListener.HTTP_PORT + HttpListener.URL);
+		HttpClient httpClient = new HttpClient();
+		httpClient.executeMethod(post);
+	}
+
+}

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/button-normal-pressed.png
===================================================================
(Binary files differ)


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/button-normal-pressed.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/button-normal.png
===================================================================
(Binary files differ)


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/button-normal.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/gotmail.wav
===================================================================
(Binary files differ)


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/gotmail.wav
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/header-halo.png
===================================================================
(Binary files differ)


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/header-halo.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/phone.png
===================================================================
(Binary files differ)


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/phone.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/read_sms.png
===================================================================
(Binary files differ)


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/read_sms.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/sms_received.png
===================================================================
(Binary files differ)


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/sms_received.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/unread_sms.png
===================================================================
(Binary files differ)


Property changes on: projects/demos/trainticket-demo/jbpm-phone/trunk/src/main/resources/unread_sms.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream



More information about the jbpm-commits mailing list