Index: trunk/hiptop/pester/net/sabi/pester/Alarm.java
===================================================================
--- trunk/hiptop/pester/net/sabi/pester/Alarm.java	(revision 249)
+++ trunk/hiptop/pester/net/sabi/pester/Alarm.java	(revision 250)
@@ -21,5 +21,5 @@
     private String mMessage;
     private int mType;
-    private int mInterval; // XXX rename to 'interval'
+    private int mPeriod;
     private Date mDate;
     private RingToneObject mAlert;
@@ -38,5 +38,5 @@
     }
     public int getPeriod() {
-	return mInterval;
+	return mPeriod;
     }
     public boolean getUsesPeriod() {
@@ -58,5 +58,5 @@
     public void setPeriod(int period, boolean repeating) {
 	mType = repeating ? TYPE_PERIODIC_REPEATING : TYPE_PERIODIC;
-	mInterval = period;
+	mPeriod = period;
     }
     public void setDate(Date date) {
@@ -75,5 +75,5 @@
 	    dataStream.writeByte(VERSION_1);
 	    dataStream.writeUTF(mMessage);
-	    dataStream.writeInt(mInterval);
+	    dataStream.writeInt(mPeriod);
 	    dataStream.writeInt(mDate.getUnixTimeGMT());
 	    dataStream.writeInt(mType);
@@ -96,5 +96,5 @@
 	    }
 	    mMessage = dataStream.readUTF();
-	    mInterval = dataStream.readInt();
+	    mPeriod = dataStream.readInt();
 	    mType = dataStream.readInt();
 	} catch (Exception e) {
@@ -128,5 +128,5 @@
 	if (getUsesPeriod()) {
 	    mDate = new Date();
-	    mDate.addSeconds(mInterval);
+	    mDate.addSeconds(mPeriod);
 	}
 	resume();
@@ -154,7 +154,7 @@
 	switch (mType) {
 	case TYPE_PERIODIC:
-	    sb.append("periodic (").append(mInterval).append("s) "); break;
+	    sb.append("periodic (").append(mPeriod).append("s) "); break;
 	case TYPE_PERIODIC_REPEATING:
-	    sb.append("repeating periodic (").append(mInterval).append("s) ");
+	    sb.append("repeating periodic (").append(mPeriod).append("s) ");
 	    break;
 	case TYPE_DATE:
@@ -170,9 +170,9 @@
 	    return null;
 	String layout = LocaleUtils.getDateTimePattern();
-	String timeFormat = LocaleUtils.getMediumTimeFormat();
 	String dateFormat = LocaleUtils.getMediumDateFormat();
-	String timeString = DateFormat.withFormat(timeFormat, date);
-	String dateString = DateFormat.withFormat(dateFormat, date);
+	String dateString;
+	String timeFormat;
 	if (relative) {
+	    timeFormat = LocaleUtils.getShortTimeFormat();
 	    int daysFromToday = date.getDaysBetween(new Date());
 	    if (daysFromToday == 0)
@@ -182,5 +182,11 @@
 	    else if (daysFromToday < 7)
 		dateString = date.getDayString();
-	}
+	    else
+		dateString = DateFormat.withFormat(dateFormat, date);
+	} else {
+	    dateString = DateFormat.withFormat(dateFormat, date);
+	    timeFormat = LocaleUtils.getMediumTimeFormat();
+	}
+	String timeString = DateFormat.withFormat(timeFormat, date);
 	return StringFormat.withFormat(layout, timeString, dateString);
     }
Index: trunk/hiptop/pester/net/sabi/pester/AlarmListView.java
===================================================================
--- trunk/hiptop/pester/net/sabi/pester/AlarmListView.java	(revision 249)
+++ trunk/hiptop/pester/net/sabi/pester/AlarmListView.java	(revision 250)
@@ -3,78 +3,24 @@
 import danger.app.Application;
 import danger.app.Event;
-import danger.audio.ToneClass;
-import danger.audio.ToneRights;
-
-import danger.internal.Date;
 import danger.ui.ActiveListView;
-import danger.ui.Button;
-import danger.ui.CheckBox;
-import danger.ui.DateTimeEditor;
-import danger.ui.DatePicker;
-import danger.ui.DialogWindow;
-import danger.ui.MenuItem;
-import danger.ui.NumberEditor;
-import danger.ui.PopupMenu;
-import danger.ui.RadioButton;
-import danger.ui.Rect;
-import danger.ui.RingTonePicker;
-import danger.ui.TypeAheadTextField;
 import danger.util.ActiveList;
-import danger.util.StdActiveList;
-import danger.util.DEBUG;
 
 public class AlarmListView extends ActiveListView
     implements Resources, Commands {
-    private static Pester sPester;
+
+    // XXX some of these shouldn't conceptually be static
     private static AlarmListWindow sAlarmListWindow;
-    // XXX some of these shouldn't conceptually be static, but who cares
-    private static StdActiveList sAlarmList;
-    private static Alarms sAlarms;
+    private static AlarmSetDialog sAlarmSetDialog;
 
-    private static DialogWindow sEditWindow;
-    private TypeAheadTextField sMessageField;
-    private RadioButton sInButton, sAtButton;
-    private NumberEditor sPeriodField;
-    private PopupMenu sPeriodUnitsPopup;
-    // XXX don't allow <5 second repeating alarms
-    private CheckBox sRepeatCheckBox;
-    private DateTimeEditor sTimeEditor, sDateEditor;
-    private DatePicker sDatePicker;
-    private RingTonePicker sAlertPicker;
-    private Button sDiscardAlarmButton, sSetAlarmButton;
+    public void onDecoded() {
+	sAlarmListWindow = (AlarmListWindow)getWindow();
+	setList(Alarms.getList());
+	super.onDecoded();
+    }
 
-    private static Alarm sFocusedAlarm, sEditingAlarm;
-
-    private static final int EVENT_CANCEL_ALARM = -1; // XXX move this?
-    
-    public void onDecoded() {
-	sPester = (Pester)Application.getCurrentApp();
-	sAlarmListWindow = (AlarmListWindow)getWindow();
-	sAlarmList = new StdActiveList();
-	setList(sAlarmList);
-
-	sEditWindow = sPester.getDialog(ID_ALARM_SET_DIALOG, this);
-	sEditWindow.setCancelButtonEvent(new Event(this, EVENT_CANCEL_ALARM));
-	sMessageField = (TypeAheadTextField)sEditWindow.getDescendantWithID(ID_MESSAGE_FIELD);
-	sEditWindow.disableBottomRightButtonOnEmptyField(sMessageField);
-	sInButton = (RadioButton)sEditWindow.getDescendantWithID(ID_IN_BUTTON);
-	sAtButton = (RadioButton)sEditWindow.getDescendantWithID(ID_AT_BUTTON);
-	sPeriodField = (NumberEditor)sEditWindow.getDescendantWithID(ID_PERIOD_FIELD);
-	sPeriodUnitsPopup = (PopupMenu)sEditWindow.getDescendantWithID(ID_PERIOD_UNITS_POPUP);
-	sRepeatCheckBox = (CheckBox)sEditWindow.getDescendantWithID(ID_PERIOD_REPEAT_CHECKBOX);
-	sTimeEditor = (DateTimeEditor)sEditWindow.getDescendantWithID(ID_TIME_EDITOR);
-	sDateEditor = (DateTimeEditor)sEditWindow.getDescendantWithID(ID_DATE_EDITOR);
-	sDatePicker = (DatePicker)sEditWindow.getDescendantWithID(ID_DATE_PICKER);
-	sAlertPicker = (RingTonePicker)sEditWindow.getDescendantWithID(ID_ALERT_PICKER);
-	sSetAlarmButton = (Button)sEditWindow.getDescendantWithID(ID_SET_ALARM_BUTTON);
-	sDiscardAlarmButton = (Button)sEditWindow.getDescendantWithID(ID_DISCARD_ALARM_BUTTON);
-	sInButton.setValue(1);
-	sDateEditor.limitToDangerEpoch();
-	sAlertPicker.setGroupFilter(ToneClass.FOREGROUND | ToneClass.CUSTOM);
-	sAlertPicker.setRights(ToneRights.FULL);
-	// this doesn't work properly yet
-	// sAlertPicker.setCanRecord(true);
-
-	super.onDecoded();
+    protected AlarmSetDialog alarmSetDialog() {
+	if (sAlarmSetDialog == null)
+	    sAlarmSetDialog = AlarmSetDialog.getDialog();
+	return sAlarmSetDialog;
     }
 
@@ -83,5 +29,5 @@
 	case Event.DEVICE_BUTTON_BACK:
 	case Event.DEVICE_BUTTON_CANCEL:
-	    sPester.returnToLauncher();
+	    Application.getCurrentApp().returnToLauncher();
 	    return true;
 	}
@@ -89,123 +35,12 @@
     }
 
-    protected void editDateOrPeriod(Alarm alarm) {
-        boolean usesPeriod = alarm.getUsesPeriod();
-	if (usesPeriod) {
-	    int period = alarm.getPeriod();
-	    for (int i = sPeriodUnitsPopup.itemCount() - 1 ; i >= 0 ; --i) {
-		int unitsPerSec = sPeriodUnitsPopup.getItem(i).getEvent().data;
-		if (period % unitsPerSec == 0) {
-		    sPeriodUnitsPopup.setValue(i);
-		    sPeriodField.setValue(period / unitsPerSec);
-		    break;
-		}
-	    }
-	    sRepeatCheckBox.setValue(alarm.getRepeating() ? 1 : 0);
-	}
-
-	Date date = alarm.getDate();
-	sTimeEditor.setDate(date);
-	sDateEditor.setDate(date);
-	sDatePicker.setDate(date);
-
-	sInButton.setValue(usesPeriod ? 1 : 0);
-	sAtButton.setValue(usesPeriod ? 0 : 1);
-    }
-
-    protected void editAlarm(Alarm alarm, boolean asNew) {
-	sEditingAlarm = alarm;
-	alarm.beginEditing();
-	if (asNew) {
-	    sDiscardAlarmButton.hide();
-	} else {
-	    sMessageField.setText(alarm.getMessage());
-	    sDiscardAlarmButton.show();
-	    editDateOrPeriod(alarm);
-	    sAlertPicker.setRingTone(sEditingAlarm.getAlert());
-	}
-	constrainDate();
-	sEditWindow.show();
-    }
-
-    protected Date editingDate() {
-	Date date = sDateEditor.getDate();
-	date.setTime(sTimeEditor.getDate().getTime());
-	date.setSeconds(0);
-	return date;
-    }
-
-    // XXX make this into a more general validation method;
-    // XXX check before dismissing dialog that alarm is valid
-    // XXX and display alarm date
-    // XXX default to now somehow?  Would be useful.
-    protected void constrainDate() {
-	// XXX schedule every minute (second?) if in AT mode
-	// XXX could use onPeriodicPulse() perhaps
-	Date now = new Date();
-	sDateEditor.min(now);
-	sDatePicker.min(now);
-	sSetAlarmButton.setEnabled(sInButton.getValue() == 1 ||
-				   editingDate().compareTo(now) > 0);
-    }
-
     public boolean receiveEvent(Event e) {
 	switch (e.type) {
 	case EVENT_NEW_ALARM:
-	    editAlarm(new Alarm(), true);
-	    return true;
-	case EVENT_SET_ALARM:
-	    sEditingAlarm.setMessage(sMessageField.toString());
-	    if (sInButton.getValue() == 1) {
-		int itemIndex = sPeriodUnitsPopup.getValue();
-		MenuItem itemAtIndex = sPeriodUnitsPopup.getItem(itemIndex);
-		Event event = itemAtIndex.getEvent();
-		sEditingAlarm.setPeriod(sPeriodField.getValue() * event.data,
-					sRepeatCheckBox.getValue() == 1);
-	    } else {
-		sEditingAlarm.setDate(editingDate());
-	    }
-	    sEditingAlarm.setAlert(sAlertPicker.getRingTone());
-	    // schedule before adding so the tooltip is correct
-	    sEditingAlarm.schedule();
-	    if (sEditingAlarm != sFocusedAlarm) { // new alarm
-		sAlarmList.addItem(sEditingAlarm);
-		setFocusedItem(sEditingAlarm);
-		sFocusedAlarm = sEditingAlarm;
-	    }
-	    return true;
-	case EVENT_CANCEL_ALARM:
-	    if (sEditingAlarm == sFocusedAlarm) // existing alarm
-		sEditingAlarm.resume();
-	    sEditingAlarm = null;
+	    alarmSetDialog().editAlarm(new Alarm(), true);
 	    return true;
 	case EVENT_DISCARD_ALARM:
 	    // XXX confirmation
-	    sAlarmList.removeItem(sFocusedAlarm);
-	    return true;
-	case EVENT_IN:
-	    sAtButton.setValue(0);
-	    constrainDate();
-	    return true;
-	case EVENT_AT:
-	    sInButton.setValue(0);
-	    constrainDate();
-	    return true;
-	case EVENT_TIME_EDITOR:
-	    Date date = editingDate();
-	    // XXX do this on exit from the field, not on change
-	    if (date.compareTo(new Date()) <= 0) {
-		date.addDays(1);
-		sDatePicker.setDate(date);
-		sDateEditor.setDate(date);
-	    }
-	    constrainDate();
-	    return true;
-        case EVENT_DATE_EDITOR:
-	    sDatePicker.setDate(sDateEditor.getDate());
-	    constrainDate();
-	    return true;
-        case EVENT_DATE_PICKER:
-	    sDateEditor.setDate((Date)e.argument);
-	    constrainDate();
+	    Alarms.removeAlarm((Alarm)getFocusedItem());
 	    return true;
 	}
@@ -214,8 +49,7 @@
 
     protected void itemActivated(Object item) {
-	editAlarm(sFocusedAlarm, false);
-    }
-    protected void itemFocused(Object item) {
-	sFocusedAlarm = (Alarm)item;
+	if (item == null)
+	    return;
+	alarmSetDialog().editAlarm((Alarm)item, false);
     }
 
@@ -225,4 +59,5 @@
     public void onItemAdded(ActiveList list, Object item, int index) {
 	sAlarmListWindow.updateAlarmCount();
+	setFocus(index);
 	super.onItemAdded(list, item, index);
     }
Index: trunk/hiptop/pester/net/sabi/pester/AlarmTimeEditor.java
===================================================================
--- trunk/hiptop/pester/net/sabi/pester/AlarmTimeEditor.java	(revision 250)
+++ trunk/hiptop/pester/net/sabi/pester/AlarmTimeEditor.java	(revision 250)
@@ -0,0 +1,30 @@
+package net.sabi.pester;
+
+import danger.app.Event;
+import danger.ui.DateTimeEditor;
+import danger.util.DEBUG;
+
+public class AlarmTimeEditor extends DateTimeEditor
+    implements Resources, Commands {
+
+    public void takeFocus(boolean inFromAbove) {
+	DEBUG.p("taking focus" + (inFromAbove ? " in from above" : ""));
+	super.takeFocus(inFromAbove);
+    }
+
+    // XXX this is not working because we gain/lose focus moving through fields
+    public void loseFocus() {
+	DEBUG.p("losing focus");
+	if (isFocused()) DEBUG.p("had focus");
+	DEBUG.p("parent leaf was " + getParentContainer().getFocusedLeaf());
+	DEBUG.p("my leaf was " + getFocusedLeaf());
+	super.loseFocus();
+	if (isFocused()) DEBUG.p("still have focus");
+	DEBUG.p("parent leaf is " + getParentContainer().getFocusedLeaf());
+	DEBUG.p("my leaf is " + getFocusedLeaf());
+	DEBUG.p("sibling is " + getSibling());
+	Event event = getEvent();
+	event.type = EVENT_TIME_EDITOR_LOST_FOCUS;
+	event.sendToTarget();
+    }
+}
Index: trunk/hiptop/pester/net/sabi/pester/Alarms.java
===================================================================
--- trunk/hiptop/pester/net/sabi/pester/Alarms.java	(revision 249)
+++ trunk/hiptop/pester/net/sabi/pester/Alarms.java	(revision 250)
@@ -3,10 +3,11 @@
 import java.util.Comparator;
 import danger.app.DataStore;
+import danger.util.StdActiveList;
 
-public class Alarms {
+public class Alarms extends StdActiveList {
     // max # records in a datastore
     private static final int MAX_ALARM_COUNT = 50;
 
-    private static Alarms sAlarms = null;
+    private static Alarms sAlarmList = null;
     private static DataStore sDataStore;
 
@@ -14,10 +15,16 @@
 	sDataStore = DataStore.createDataStore("alarms", true /* auto sync */);
     }
-    
-    public static Alarms getInstance() {
-	if (sAlarms == null)
-	    sAlarms = new Alarms();
 
-	return sAlarms;
+    public static Alarms getList() {
+	if (sAlarmList == null)
+	    sAlarmList = new Alarms();
+	return sAlarmList;
+    }
+
+    public static void addAlarm(Alarm alarm) {
+	sAlarmList.addItem(alarm);
+    }
+    public static void removeAlarm(Alarm alarm) {
+	sAlarmList.removeItem(alarm);
     }
 
