<?xml version="1.0" ?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
<deviceType>urn:schemas-upnp-org:device:echo:1</deviceType>
<friendlyName>Echo device</friendlyName>
<manufacturer>Jan Newmarch</manufacturer>
<manufacturerURL>http://jan.newmarch.name</manufacturerURL>
<modelDescription>Jan's Simple Echo Device</modelDescription>
<modelName>Echo</modelName>
<modelNumber>1.0</modelNumber>
<modelURL>http://jan.newmarch.name</modelURL>
<serialNumber>1234567890</serialNumber>
<UDN>uuid:jan_echo_server_1</UDN>
<UPC>123456789012</UPC>
<iconList>
<icon>
<mimetype>image/gif</mimetype>
<width>48</width>
<height>32</height>
<depth>8</depth>
<url>/description/icon.gif</url>
</icon>
</iconList>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:echo:1</serviceType>
<serviceId>urn:schemas-upnp-org:serviceId:echo:1</serviceId>
<SCPDURL>/service_description.xml</SCPDURL>
<controlURL>/service/timer/control</controlURL>
<eventSubURL>/service/timer/eventSub</eventSubURL>
</service>
</serviceList>
<presentationURL>/presentation</presentationURL>
</device>
</root>
<?xml version="1.0"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0" >
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<actionList>
<action>
<name>Echo</name>
<argumentList>
<argument>
<name>InText</name>
<relatedStateVariable>Text</relatedStateVariable>
<direction>in</direction>
</argument>
<argument>
<name>ReturnText</name>
<relatedStateVariable>Text</relatedStateVariable>
<direction>out</direction>
</argument>
</argumentList>
</action>
</actionList>
<serviceStateTable>
<stateVariable sendEvents="no">
<name>Text</name>
<dataType>string</dataType>
</stateVariable>
</serviceStateTable>
</scpd>
/******************************************************************
*
* Echo
*
* Copyright (C) Jan Newmarch
*
* File : EchoDevice.java
*
******************************************************************/
package device;
// import service.*;
import java.io.*;
import org.cybergarage.upnp.*;
import org.cybergarage.upnp.device.*;
import org.cybergarage.upnp.control.*;
import org.cybergarage.http.*;
import org.cybergarage.upnp.ssdp.*;
public class EchoDevice extends Device implements ActionListener
{
private final static String DESCRIPTION_FILE_NAME = "description/description.xml";
private StateVariable textVar;
public static void main(String[] args) {
UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);
org.cybergarage.util.Debug.on();
EchoDevice dev = null;
try {
dev = new EchoDevice();
} catch(InvalidDescriptionException e) {
e.printStackTrace();
System.exit(1);
}
System.out.println("Running...");
// This is a hack to keep the server alive.
// If the device had a GUI, the AWT/Swing would keep it alive
Object keepAlive = new Object();
synchronized(keepAlive) {
try {
// Wait for a "notify" from another thread
// that will never be sent.
// So we stay alive for ever
keepAlive.wait();
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
}
public EchoDevice() throws InvalidDescriptionException
{
super(new File(DESCRIPTION_FILE_NAME));
Action getEchoAction = getAction("Echo");
getEchoAction.setActionListener(this);
ServiceList serviceList = getServiceList();
// there should only be an echo service
Service service = serviceList.getService(0);
// service.setQueryListener(this);
// we don't use this
textVar = getStateVariable("Text");
setLeaseTime(60);
start();
}
public String echo(String txt) {
return("Echo from \"" + getFriendlyName() + "\": " + txt);
}
public boolean actionControlReceived(Action action)
{
String actionName = action.getName();
if (actionName.equals("Echo")) {
System.out.println("Echo action control");
Argument inArg = action.getArgument("InText");
String txt = inArg.getValue();
Argument returnArg = action.getArgument("ReturnText");
String returnTxt = echo(txt);
returnArg.setValue(returnTxt);
return true;
}
return false;
}
}
/******************************************************************
*
* CyberUPnP for Java
*
* Copyright (C) Satoshi Konno 2002
*
* File : ClockDevice.java
*
******************************************************************/
package controlPoint;
import java.io.*;
import org.cybergarage.upnp.*;
import org.cybergarage.upnp.device.*;
import org.cybergarage.upnp.control.*;
import org.cybergarage.http.*;
import org.cybergarage.upnp.ssdp.*;
public class Client extends ControlPoint implements NotifyListener {
public static void main(String[] args) {
UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);
org.cybergarage.util.Debug.on();
new Client();
}
public Client()
{
addNotifyListener(this);
start();
loop();
}
public void loop() {
for (int n = 0; n < 100; n++) {
System.out.println("Sending: hello " + n);
echoToAll("hello " + n);
try {
Thread.currentThread().sleep(1000);
} catch(Exception e) {
}
}
System.exit(0);
}
private void echoToAll(String txt) {
DeviceList devList = getDeviceList();
// System.out.println("Discovered devices: " + devList.size());
Device dev = null;
for (int n = 0; n < devList.size(); n++) {
dev = devList.getDevice(n);
// System.out.println(" Type: " + dev.getDeviceType());
if (dev.getDeviceType().equals("urn:schemas-upnp-org:device:echo:1")) {
ServiceList services = dev.getServiceList();
for (int m = 0; m < services.size(); m++) {
Service svc = services.getService(m);
if (svc.getServiceType().equals("urn:schemas-upnp-org:service:echo:1")) {
echoToOne(svc, txt);;
}
}
}
}
}
private void echoToOne(Service svc, String txt) {
Action echoAction = svc.getAction("Echo");
Argument inArg = echoAction.getArgument("InText");
inArg.setValue(txt);
if (echoAction.postControlAction()) {
Argument arg = echoAction.getArgument("ReturnText");
String value = arg.getValue();
System.out.println(value);
}
}
public void deviceNotifyReceived(SSDPPacket ssdpPacket) {
// uncomment this to see all discovery packets
// System.out.println("New SSDPPacket, all " + ssdpPacket);
}
}
A build.xml is
<project name="UPnP demos" default="usage" basedir=".">
<!-- CONFIGURABLE STUFF HERE -->
<!-- where this directory is -->
<property name="top" value="/home/httpd/html/internetdevices/upnp/java/echo"/>
<property name="upnp_lib" value="${top}/lib/clink132a.jar"/>
<property name="xerces_lib" value="${top}/lib/xercesImpl.jar"/>
<property name="xml_lib" value="${top}/lib/xml-apis.jar"/>
<!-- END CONFIGURABLE STUFF -->
<!-- Directories -->
<!-- Show the usage options to the user -->
<target name="usage" >
<echo message=""/>
<echo message=""/>
<echo message="-------------------------------------------------------------"/>
<echo message=""/>
<echo message="available targets are:"/>
<echo message=""/>
<echo message=" compile"/>
<echo message=" rundevice"/>
<echo message=" runclient"/>
<echo message=" clean"/>
<echo message=" usage"/>
<echo message=""/>
<echo message="See comments inside the build.xml file for more details."/>
<echo message="-------------------------------------------------------------"/>
<echo message=""/>
<echo message=""/>
</target>
<target name="all" depends="init,compile"/>
<!-- CLEAN -->
<target name="clean">
<!-- Delete our the ${build}, and ${deploy} directory trees -->
<delete dir="classes"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile N deploy -->
</target>
<target name="compile">
<mkdir dir="classes"/>
<javac destdir="classes" srcdir="src"
classpath="${upnp_lib}"
target="1.1"/>
</target>
<target name="runclient" depends="compile">
<java
classpath="${upnp_lib}:${xerces_lib}:${xml_lib}:classes"
classname="controlPoint.Client"/>
</target>
<target name="rundevice" depends="compile">
<exec executable="java">
<env key="CLASSPATH"
path="${upnp_lib}:${xerces_lib}:${xml_lib}:classes"/>
<arg line="device.EchoDevice"/>
</exec>
</target>
</project>
javac
with classpath set to include
src
directory
clink132a.jar
xercesImpl.jar
xml-apis.jar
java
with classpath set to
classes
directory
clink132a.jar
xercesImpl.jar
xml-apis.jar
java device.EchoDevice
java controlPoint.Client
date
which is defined by ISO 8601 by
XML. This represents local time of day using a 24-hour clock as a
string "HH:MM" or "HH:MM:SS". The validity should be boolean
time GetTime();
void SetTime(time);
boolean IsValidTime();
These will depend on state variables Time
and ValidTime
Time
state could change say once a second - this
may be too frequent for network traffic, so choose no. The
ValidTime
will change rarely, and could be important to
other timers, so say yes
<?xml version="1.0"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0" >
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<actionList>
<action>
<name>SetTime</name>
<argumentList>
<argument>
<name>NewTime</name>
<relatedStateVariable>Time</relatedStateVariable>
<direction>in</direction>
</argument>
</argumentList>
</action>
<action>
<name>GetTime</name>
<argumentList>
<argument>
<name>CurrentTime</name>
<relatedStateVariable>Time</relatedStateVariable>
<direction>out</direction>
</argument>
</argumentList>
</action>
<action>
<name>TimeValid</name>
<argumentList>
<argument>
<name>Valid</name>
<relatedStateVariable>TimeValid</relatedStateVariable>
<direction>out</direction>
</argument>
</argumentList>
</action>
</actionList>
<serviceStateTable>
<stateVariable sendEvents="no">
<name>Time</name>
<dataType>time</dataType>
</stateVariable>
<stateVariable sendEvents="yes">
<name>TimeValid</name>
<dataType>boolean</dataType>
</stateVariable>
</serviceStateTable>
</scpd>
All the source code is available from
here . This includes an ant build.xml
file.
You can use this to build/run the examples by
ant compileclock
ant runtickerclock
ant runcomputerclock
If you don't want to do it this way, you can run any Java compiler/runtime or IDE with
the classpath set to include the files
clink123a.jar
xercesImpl.jar
xml-apis.jar
These files are in the zip file with the source code
A convenience class to handle ISO 8601 time format
package xmltypes;
public class Time {
private int hours = 0;
private int mins = 0;
private int secs = 0;
public Time(String str) {
setTime(str);
}
public Time(int h, int m) {
hours = h;
mins = m;
secs = 0;
}
public Time(int h, int m, int s) {
hours = h;
mins = m;
secs = s;
}
private void setTime(String str) {
String[] parts = str.split(":");
if (parts.length == 2) {
try {
hours = Integer.parseInt(parts[0]);
mins = Integer.parseInt(parts[1]);
secs = 0;
} catch (NumberFormatException e) {
}
} else if (parts.length == 3) {
try {
hours = Integer.parseInt(parts[0]);
mins = Integer.parseInt(parts[1]);
secs = Integer.parseInt(parts[2]);
} catch(NumberFormatException e) {
}
}
}
private void setTime(int hours, int mins, int secs) {
this.hours = hours;
this.mins = mins;
this.secs = secs;
}
public void increment() {
if (++secs == 60) {
secs = 0;
if (++mins == 60) {
mins = 0;
if (++hours == 24) {
hours = 0;
}
}
}
}
public String toString() {
return paddedString(hours) + ":" + paddedString(mins) + ":" + paddedString(secs);
}
private String paddedString(int n) {
if (n <= 9) {
return "0" + n;
} else {
return "" + n;
}
}
}
Interface for different implementations of timers, matches the methods defined for the timer service
/**
* Timer service as per Timer XML description
* This may be subclassed to provide functionality
*/
package service;
import xmltypes.*;
public interface Timer {
public void setTime(Time t);
public Time getTime();
public boolean isValidTime();
}
Implementation of timer that gets its time from the computer clock using
the Calendar
class
package service;
import java.util.Calendar;
import xmltypes.*;
public class ComputerTimer implements Timer {
public void setTime(Time t) {
// void
}
public Time getTime() {
Calendar now = Calendar.getInstance();
return new Time(now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE),
now.get(Calendar.SECOND));
}
public boolean isValidTime() {
return true;
}
}
Another implementation that runs a "ticker" in a thread to update the time. This timer is invalid until something else sets its time
/**
* Timer service as per Timer XML description
* This may be subclassed to provide functionality
*/
package service;
import xmltypes.*;
public class TickerTimer implements Timer {
private Time time;
private boolean isValid;
private Ticker ticker;
/**
* Constructor with no starting time has
* invalid timer and any time
*/
public TickerTimer() {
time = new Time("12:00:00");
isValid = false;
new Ticker(time).start();
}
public TickerTimer(Time t) {
time = t;
isValid = true;
ticker = new Ticker(time);
ticker.start();
}
public void setTime(Time t) {
System.out.println("Setting time to " + t);
time = t;
isValid = true;
if (ticker != null) {
ticker.stopRunning();
}
ticker = new Ticker(time);
ticker.start();
}
public Time getTime() {
return time;
}
public boolean isValidTime() {
if (isValid) {
return true;
} else {
return false;
}
}
}
class Ticker extends Thread {
private Time time;
private boolean keepRunning = true;
public Ticker(Time t) {
time = t;
}
public void run() {
while (keepRunning) {
try {
sleep(1000);
} catch(InterruptedException e) {
}
time.increment();
}
}
public void stopRunning() {
keepRunning = false;
}
}
Device
which means it broadcasts information about itself to control points
actionControlReceived()
method - it is an
ActionListener
deviceNotifyReceived()
method - it is a
NotifyListener
/******************************************************************
*
* CyberUPnP for Java
*
* Copyright (C) Satoshi Konno 2002
*
* File : ClockDevice.java
*
******************************************************************/
package device;
import service.*;
import xmltypes.*;
import java.io.*;
import org.cybergarage.upnp.*;
import org.cybergarage.upnp.device.*;
import org.cybergarage.upnp.control.*;
import org.cybergarage.http.*;
import org.cybergarage.upnp.ssdp.*;
public class ClockDevice extends Device implements ActionListener, QueryListener, NotifyListener
{
private final static String DESCRIPTION_FILE_NAME = "description/description.xml";
private final static String PRESENTATION_URI = "clock/presentation";
private ControlPoint ctrlPoint = new ControlPoint();
private Timer timer;
private Service timerService;
private Device lastDeviceSeen;
public ClockDevice() throws InvalidDescriptionException
{
super(DESCRIPTION_FILE_NAME);
// super(new File(DESCRIPTION_FILE_NAME));
UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);
org.cybergarage.util.Debug.on();
ctrlPoint.addNotifyListener(this);
ctrlPoint.start();
Action getTimeAction = getAction("GetTime");
getTimeAction.setActionListener(this);
Action setTimeAction = getAction("SetTime");
setTimeAction.setActionListener(this);
Action timeValidAction = getAction("TimeValid");
timeValidAction.setActionListener(this);
ServiceList serviceList = getServiceList();
// there should only be a timer service
timerService = serviceList.getService(0);
timerService.setQueryListener(this);
setLeaseTime(60);
}
public void setTimer(Timer t) {
timer = t;
System.out.println("Our timer service is " + t);
}
////////////////////////////////////////////////
// ActionListener
////////////////////////////////////////////////
public boolean actionControlReceived(Action action)
{
String actionName = action.getName();
if (actionName.equals("GetTime") == true) {
System.out.println("GetTime action control");
Time now = timer.getTime();
Argument timeArg = action.getArgument("CurrentTime");
timeArg.setValue(now.toString());
return true;
}
if (actionName.equals("SetTime") == true) {
System.out.println("SetTime action control");
Argument timeArg = action.getArgument("NewTime");
String newTime = timeArg.getValue();
timer.setTime(new Time(newTime));
System.out.println("SetTime action to " + newTime);
return true;
}
if (actionName.equals("TimeValid") == true) {
System.out.println("TimeValid action control");
boolean timeValid = timer.isValidTime();
Argument timeArg = action.getArgument("Valid");
timeArg.setValue("" + timeValid);
return true;
}
return false;
}
////////////////////////////////////////////////
// QueryListener
////////////////////////////////////////////////
public boolean queryControlReceived(StateVariable stateVar)
{
System.out.println("query control");
if (stateVar.getName().equals("Time")) {
stateVar.setValue(getTime().toString());
return true;
} else if (stateVar.getName().equals("TimeValid")) {
stateVar.setValue("" + isValidTime());
return true;
}
return false;
}
////////////////////////////////////////////////
// HttpRequestListner
////////////////////////////////////////////////
public void httpRequestRecieved(HTTPRequest httpReq)
{
System.out.println("HTTP request: " + httpReq);
String uri = httpReq.getURI();
if (uri.startsWith(PRESENTATION_URI) == false) {
super.httpRequestRecieved(httpReq);
return;
}
}
public void setTime(Time time) {
System.out.println("setTime in clock device to " + time);
timer.setTime(time);
}
public Time getTime() {
return timer.getTime();
}
public boolean isValidTime() {
return timer.isValidTime();
}
public void deviceNotifyReceived(SSDPPacket ssdpPacket) {
System.out.println("New SSDPPacket, all " + ssdpPacket);
/* There doesn;t seem to be a simple way of getting the new
* device from the packet, even though CyberLink creates a
* new device from it - and adds it to a list. Looks like we
* have to get the whole list and search through it for the
* device created by this packet :-(
*/
DeviceList devList = ctrlPoint.getDeviceList();
Device dev = null;
System.out.println("Devices: " + devList.size());
for (int n = 0; n < devList.size(); n++) {
if (devList.getDevice(n).getSSDPPacket() == ssdpPacket) {
System.out.println("Found the new device matching SSDP packet");
dev = devList.getDevice(n);
System.out.println(" Presentation URI " + dev.getPresentationURL());
System.out.println(" Presentation base " + dev.getURLBase());
break;
}
}
if (dev == null) {
// couldn't find it? That shouldn't happen...
return;
}
/* this doesn't work :-( device changes each time
if (dev == lastDeviceSeen) {
// UPnP sends repeat messages to try to make sure things
// get through. But we don't want to repeat work
System.out.println("Seen this device before");
lastDeviceSeen = dev;
return;
}
*/
ServiceList timerServices = new ServiceList();
if (dev.getDeviceType().equals("urn:schemas-upnp-org:device:clock:1")) {
ServiceList services = dev.getServiceList();
for (int m = 0; m < services.size(); m++) {
Service svc = services.getService(m);
if (svc.getServiceType().equals("urn:schemas-upnp-org:service:timer:1")) {
timerServices.add(svc);
}
}
}
tryClockValidation(timerServices);
}
private ServiceList getTimerServices() {
ServiceList timerServices = new ServiceList();
DeviceList devList = ctrlPoint.getDeviceList();
System.out.println("Devices: " + devList.size());
for (int n = 0; n < devList.size(); n++) {
Device dev = devList.getDevice(n);
if (dev.getDeviceType().equals("urn:schemas-upnp-org:device:clock:1")) {
ServiceList services = dev.getServiceList();
for (int m = 0; m < services.size(); m++) {
Service svc = services.getService(m);
if (svc.getServiceType().equals("urn:schemas-upnp-org:service:timer:1")) {
timerServices.add(svc);
}
}
}
}
return timerServices;
}
private void tryClockValidation(ServiceList services) {
org.cybergarage.util.Debug.on();
for (int n = 0; n < services.size(); n++) {
Service svc = (Service) services.elementAt(n);
System.out.println("Checking service " + svc);
Action validAction = svc.getAction("TimeValid");
if (validAction.postControlAction()) {
System.out.println("Checking TimeValid succeeded");
Argument arg = validAction.getArgument("Valid");
String value = arg.getValue();
System.out.println("valid time? " + value);
if (isValidTime() && value.equals("false")) {
// other clock needs to be set
Action setTimeAction = svc.getAction("SetTime");
setTimeAction.setArgumentValue("NewTime",
getTime().toString());
setTimeAction.postControlAction();
} else if (! isValidTime() && value.equals("true")) {
Action getTimeAction = svc.getAction("GetTime");
if ( ! getTimeAction.postControlAction()) {
System.out.println("GetTime post action failed");
}
/*
ArgumentList outArgList = getTimeAction.getOutputArgumentList();
// there should only be one output arg, the CurrentTime
Argument currentTimeArg = outArgList.getArgument(n);
String currTime = currentTimeArg.getValue();
System.out.println("Current time from other: " + currTime);
setTime(new Time(currTime));
*/
Argument arg2 = getTimeAction.getArgument("CurrentTime");
Object val = arg2.getValue();
System.out.println("Arg type " + val.getClass());
System.out.println("setTime in clock validation to " + arg2.getValue());
setTime(new Time(arg2.getValue()));
}
}
}
}
}
This creates a clock device, sets a ticker timer in it and starts the frame
package clock;
import device.*;
import service.*;
import org.cybergarage.util.*;
import org.cybergarage.upnp.device.*;
public class TickerClock {
////////////////////////////////////////////////
// main
////////////////////////////////////////////////
public static void main(String args[])
{
ClockDevice clockDev = null;
try {
clockDev = new ClockDevice();
}
catch (InvalidDescriptionException e) {
e.printStackTrace();
System.exit(1);
}
clockDev.setTimer(new TickerTimer());
ClockFrame clock;
if (args.length > 0) {
clock= new ClockFrame(clockDev, args[0]);
} else {
clock = new ClockFrame(clockDev);
}
clock.start();
}
}
This creates a clock device, sets a computer timer in it and starts the frame
package clock;
import device.*;
import service.*;
import org.cybergarage.util.*;
import org.cybergarage.upnp.device.*;
public class ComputerClock {
////////////////////////////////////////////////
// main
////////////////////////////////////////////////
public static void main(String args[])
{
ClockDevice clockDev = null;
try {
clockDev = new ClockDevice();
}
catch (InvalidDescriptionException e) {
e.printStackTrace();
System.exit(1);
}
clockDev.setTimer(new ComputerTimer());
ClockFrame clock;
if (args.length > 0) {
clock= new ClockFrame(clockDev, args[0]);
} else {
clock = new ClockFrame(clockDev);
}
clock.start(); }
}
A JFrame
to display the clock
/******************************************************************
*
* CyberUPnP for Java
*
* Copyright (C) Satoshi Konno 2002-2003
*
* File : SampleClock.java
*
******************************************************************/
package clock;
import device.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import org.cybergarage.util.*;
import org.cybergarage.upnp.*;
import org.cybergarage.upnp.device.*;
public class ClockFrame extends JFrame implements Runnable, WindowListener
{
private final static String DEFAULT_TITLE = "CyberLink Sample Clock";
private ClockDevice clockDev;
private ClockPane clockPane;
public ClockFrame(ClockDevice clockDev) {
this(clockDev, DEFAULT_TITLE);
}
public ClockFrame(ClockDevice clockDev, String title)
{
super(title);
this.clockDev = clockDev;
getContentPane().setLayout(new BorderLayout());
clockPane = new ClockPane(clockDev);
getContentPane().add(clockPane, BorderLayout.CENTER);
addWindowListener(this);
pack();
setVisible(true);
}
public ClockPane getClockPanel()
{
return clockPane;
}
public ClockDevice getClockDevice()
{
return clockDev;
}
////////////////////////////////////////////////
// run
////////////////////////////////////////////////
private Thread timerThread = null;
public void run()
{
Thread thisThread = Thread.currentThread();
while (timerThread == thisThread) {
// getClockDevice().update();
getClockPanel().repaint();
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {}
}
}
public void start()
{
clockDev.start();
timerThread = new Thread(this);
timerThread.start();
}
public void stop()
{
clockDev.stop();
timerThread = null;
}
////////////////////////////////////////////////
// main
////////////////////////////////////////////////
public void windowActivated(WindowEvent e)
{
}
public void windowClosed(WindowEvent e)
{
}
public void windowClosing(WindowEvent e)
{
stop();
System.exit(0);
}
public void windowDeactivated(WindowEvent e)
{
}
public void windowDeiconified(WindowEvent e)
{
}
public void windowIconified(WindowEvent e)
{
}
public void windowOpened(WindowEvent e)
{
}
}
And a JPane
to display the clock
/******************************************************************
*
* CyberUPnP for Java
*
* Copyright (C) Satoshi Konno 2002
*
* File : SampleClockPane.java
*
******************************************************************/
package clock;
import device.*;
import xmltypes.*;
import java.io.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;
import javax.imageio.ImageIO;
import org.cybergarage.util.*;
public class ClockPane extends JPanel
{
private ClockDevice clockDev;
private Color lastBlink = Color.BLACK;
public ClockPane(ClockDevice clockDev)
{
this.clockDev = clockDev;
loadImage();
initPanel();
}
////////////////////////////////////////////////
// Background
////////////////////////////////////////////////
private final static String CLOCK_PANEL_IMAGE = "images/clock.jpg";
private BufferedImage panelmage;
private void loadImage()
{
File f = new File(CLOCK_PANEL_IMAGE);
try {
panelmage = ImageIO.read(f);
}
catch (Exception e) {
Debug.warning(e);
}
}
private BufferedImage getPaneImage()
{
return panelmage;
}
////////////////////////////////////////////////
// Background
////////////////////////////////////////////////
private void initPanel()
{
BufferedImage panelmage = getPaneImage();
setPreferredSize(new Dimension(panelmage.getWidth(), panelmage.getHeight()));
}
////////////////////////////////////////////////
// Font
////////////////////////////////////////////////
private final static String DEFAULT_FONT_NAME = "Lucida Console";
private final static int DEFAULT_TIME_FONT_SIZE = 48;
private final static int DEFAULT_DATE_FONT_SIZE = 18;
private final static int DEFAULT_SECOND_BLOCK_HEIGHT = 8;
private final static int DEFAULT_SECOND_BLOCK_FONT_SIZE = 10;
private Font timeFont = null;
private Font dateFont = null;
private Font secondFont = null;
private Font getFont(Graphics g, int size)
{
Font font = new Font(DEFAULT_FONT_NAME, Font.PLAIN, size);
if (font != null)
return font;
return g.getFont();
}
private Font getTimeFont(Graphics g)
{
if (timeFont == null)
timeFont = getFont(g, DEFAULT_TIME_FONT_SIZE);
return timeFont;
}
private Font getDateFont(Graphics g)
{
if (dateFont == null)
dateFont = getFont(g, DEFAULT_DATE_FONT_SIZE);
return dateFont;
}
private Font getSecondFont(Graphics g)
{
if (secondFont == null)
secondFont = getFont(g, DEFAULT_SECOND_BLOCK_FONT_SIZE);
return secondFont;
}
////////////////////////////////////////////////
// paint
////////////////////////////////////////////////
private void drawClockInfo(Graphics g)
{
int winWidth = getWidth();
int winHeight = getHeight();
if (clockDev.isValidTime()) {
g.setColor(Color.BLACK);
} else {
if (lastBlink == Color.WHITE) {
g.setColor(Color.BLACK);
lastBlink = Color.BLACK;
} else {
g.setColor(Color.WHITE);
lastBlink = Color.WHITE;
}
}
//// Time String ////
Time now = clockDev.getTime();
String timeStr = now.toString();
Font timeFont = getTimeFont(g);
g.setFont(timeFont);
FontMetrics timeFontMetric = g.getFontMetrics();
Rectangle2D timeStrBounds = timeFontMetric.getStringBounds(timeStr, g);
int timeStrWidth = (int)timeStrBounds.getWidth();
int timeStrHeight = (int)timeStrBounds.getHeight();
int timeStrX = (winWidth-timeStrWidth)/2;
int timeStrY = (winHeight+timeStrHeight)/2;
int timeStrOffset = timeStrHeight/8/2;
g.drawString(
timeStr,
timeStrX,
timeStrY);
//// Date String ////
String dateStr = "Time";
Font dateFont = getDateFont(g);
g.setFont(dateFont);
FontMetrics dateFontMetric = g.getFontMetrics();
Rectangle2D dateStrBounds = dateFontMetric.getStringBounds(dateStr, g);
g.drawString(
dateStr,
(winWidth-(int)dateStrBounds.getWidth())/2,
timeStrY-timeStrHeight-timeStrOffset);
}
private void clear(Graphics g)
{
g.setColor(Color.GRAY);
g.clearRect(0, 0, getWidth(), getHeight());
}
private void drawPanelImage(Graphics g)
{
g.drawImage(getPaneImage(), 0, 0, null);
}
public void paint(Graphics g)
{
clear(g);
drawPanelImage(g);
drawClockInfo(g);
}
}