/**
* ThisTime 2.0 (2008-03-30)
* Copyright 2007 Zach Scrivena
* zachscrivena@gmail.com
* http://thistime.sourceforge.net/
*
* Simple clock and timer program.
*
* TERMS AND CONDITIONS:
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package thistime;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.TrayIcon.MessageType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
/**
* Main class for ThisTime program.
* Simple clock and timer program.
*/
public class ThisTime
{
/** program name */
static final String NAME = "ThisTime";
/** program version */
static final String VERSION = "2.0";
/** program date */
static final String DATE = "2008-03-30";
/** URL for homepage */
static final String HOMEPAGE = "http://thistime.sourceforge.net/";
/** program description text, to be shown in "about" dialog */
static final String DESCRIPTION =
ThisTime.NAME + " " +
ThisTime.VERSION + " (" +
ThisTime.DATE + ")\n" +
"Copyright 2008 Zach Scrivena\n" +
"zachscrivena@gmail.com\n" +
ThisTime.HOMEPAGE;
/** URL for latest release information */
static final String LATEST_RELEASE_URL =
"http://thistime.sourceforge.net/latest.txt";
/** URL for bug reporting */
static final String BUG_REPORT =
"http://sourceforge.net/tracker/?func=add&group_id=193349&atid=945057";
/** clocks */
private final List<ClockUnit> clocks = new ArrayList<ClockUnit>();
/** clock unique ID */
private int clockUid = 0;
/** timers */
private final List<TimerUnit> timers = new ArrayList<TimerUnit>();
/** timer unique ID */
private int timerUid = 0;
/** "About" form */
private About about = null;
/**
* Constructor.
* This method must run on the EDT.
*/
ThisTime()
{
/**********************
* PROGRAM ICON IMAGE *
**********************/
final Image iconImage;
try
{
iconImage = ImageIO.read(TimerUnit.class.getResource("/thistime/resources/icon.png"));
}
catch (Exception e)
{
throw new TerminatingException("Failed to load program icon image (" + e.getMessage() + ").");
}
/***********************************
* POPUP MENU FOR SYSTEM TRAY ICON *
***********************************/
final PopupMenu popupMenu = new PopupMenu(ThisTime.NAME);
/* popup menu: "New Clock" */
final MenuItem clockItem = new MenuItem("New Clock");
clockItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
final ClockUnit c;
synchronized (clocks)
{
clockUid++;
c = new ClockUnit(
ThisTime.this,
"Clock #" + clockUid,
false);
clocks.add(c);
}
c.setVisible(true);
c.setExtendedState(JFrame.NORMAL);
c.toFront();
}
});
popupMenu.add(clockItem);
/* popup menu: "New Count Up Timer" */
final MenuItem countUpItem = new MenuItem("New Count Up Timer");
countUpItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
final TimerUnit t;
synchronized (timers)
{
timerUid++;
t = new TimerUnit(
ThisTime.this,
"Timer #" + timerUid,
true);
timers.add(t);
}
t.setVisible(true);
t.setExtendedState(JFrame.NORMAL);
t.toFront();
}
});
popupMenu.add(countUpItem);
/* popup menu: "New Count Down Timer" */
final MenuItem countDownItem = new MenuItem("New Count Down Timer");
countDownItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
final TimerUnit t;
synchronized (timers)
{
timerUid++;
t = new TimerUnit(
ThisTime.this,
"Timer #" + timerUid,
false);
timers.add(t);
}
t.setVisible(true);
t.setExtendedState(JFrame.NORMAL);
t.toFront();
}
});
popupMenu.add(countDownItem);
popupMenu.addSeparator();
/* popup menu: "Restore All" */
final MenuItem restoreAllItem = new MenuItem("Restore All");
restoreAllItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
synchronized (clocks)
{
for (ClockUnit c : clocks)
{
c.restoreClock();
}
}
synchronized (timers)
{
for (TimerUnit t : timers)
{
t.restoreTimer();
}
}
}
});
popupMenu.add(restoreAllItem);
/* popup menu: "Minimize All" */
final MenuItem minimizeAllItem = new MenuItem("Minimize All");
minimizeAllItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
synchronized (clocks)
{
for (ClockUnit c : clocks)
{
c.minimizeClock();
}
}
synchronized (timers)
{
for (TimerUnit t : timers)
{
t.minimizeTimer();
}
}
}
});
popupMenu.add(minimizeAllItem);
popupMenu.addSeparator();
/* popup menu: "About..." */
final MenuItem aboutItem = new MenuItem("About...");
aboutItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
if (about == null)
{
about = new About(iconImage);
}
about.setVisible(true);
about.setExtendedState(JFrame.NORMAL);
about.toFront();
}
});
popupMenu.add(aboutItem);
popupMenu.addSeparator();
/* popup menu: "Exit" */
final MenuItem exitItem = new MenuItem("Exit");
exitItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
/* confirm exit, if necessary */
int numClocksTimers = 0;
synchronized (clocks)
{
numClocksTimers += clocks.size();
}
synchronized (timers)
{
numClocksTimers += timers.size();
}
if (numClocksTimers > 0)
{
final int choice = JOptionPane.showConfirmDialog(
null,
"Exit now and close all clocks and timers?",
"Confirm Exit - " + ThisTime.NAME,
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (choice != JOptionPane.YES_OPTION)
{
return;
}
}
System.exit(0);
}
});
popupMenu.add(exitItem);
/********************
* SYSTEM TRAY ICON *
********************/
final TrayIcon trayIcon = new TrayIcon(iconImage, ThisTime.NAME, popupMenu);
trayIcon.setImageAutoSize(true);
try
{
SystemTray.getSystemTray().add(trayIcon);
}
catch (Exception e)
{
throw new TerminatingException("Failed to create system tray icon (" + e.getMessage() + ").");
}
trayIcon.displayMessage(
ThisTime.NAME,
"Right-click tray icon for program menu",
MessageType.INFO);
/* create a frame to get rid of splashscreen */
final JFrame f = new JFrame();
f.setVisible(true);
f.setVisible(false);
f.dispose();
}
/**
* Remove the specified clock unit.
*/
void removeClock(
final ClockUnit c)
{
synchronized (clocks)
{
clocks.remove(c);
}
}
/**
* Remove the specified timer unit.
*/
void removeTimer(
final TimerUnit t)
{
synchronized (timers)
{
timers.remove(t);
}
}
/**
* Provide a string description for the stack trace of the specified exception.
*
* @param e
* exception
* @return
* string description for the stack trace
*/
static String stackTraceString(
final Exception e)
{
final StringBuilder sb = new StringBuilder("Exception stack trace:");
for (StackTraceElement t : e.getStackTrace())
{
sb.append("\n ");
sb.append(t);
}
return sb.toString();
}
/**
* Main entry point for program.
*
* @param args
* command-line argument strings
*/
public static void main(
final String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
try
{
/* use system look and feel if possible */
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e)
{
/* ignore */
}
/* make tooltips appear instantaneously */
ToolTipManager.sharedInstance().setInitialDelay(0);
/* create program GUI */
try
{
new ThisTime();
}
catch (TerminatingException e)
{
SwingManipulator.showErrorDialog(
null,
"Initialization Error - " + ThisTime.NAME,
e.getMessage());
System.exit(e.getExitCode());
}
catch (Exception e)
{
SwingManipulator.showErrorDialog(
null,
"Initialization Error - " + ThisTime.NAME,
"Failed to initialize " + ThisTime.NAME + " because of an unexpected error: " + e +
"\nPlease help to improve " + ThisTime.NAME + " by filing a bug report at " +
ThisTime.HOMEPAGE + ".\n\n" +
stackTraceString(e));
System.exit(1);
}
}
});
}
}