Version 1.7 - Added 30-minute auto save
committer: Jonathan Bernard <jdbernard@gmail.com>
This commit is contained in:
parent
efcb781ae4
commit
e69faa148f
@ -1,12 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
|
||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/1">
|
||||
<file>file:/C:/Documents%20and%20Settings/jbernard/My%20Documents/Development/TimeStamper/src/jdbernard/timestamper/AboutDialog.java</file>
|
||||
<file>file:/C:/Documents%20and%20Settings/jbernard/My%20Documents/Development/TimeStamper/src/jdbernard/timestamper/PunchcardDisplayDialog.java</file>
|
||||
<file>file:/C:/Documents%20and%20Settings/jbernard/My%20Documents/Development/TimeStamper/src/jdbernard/timestamper/TimeStamperApp.java</file>
|
||||
<file>file:/C:/Documents%20and%20Settings/jbernard/My%20Documents/Development/TimeStamper/src/jdbernard/timestamper/TimeStamperView.java</file>
|
||||
<file>file:/C:/Documents%20and%20Settings/jbernard/My%20Documents/Development/TimeStamper/src/jdbernard/timestamper/Timeline.java</file>
|
||||
<file>file:/C:/Documents%20and%20Settings/jbernard/My%20Documents/Development/TimeStamper/src/jdbernard/timestamper/TimelineDayDisplay.java</file>
|
||||
</open-files>
|
||||
</project-private>
|
||||
|
@ -2,7 +2,7 @@ application.desc=Simple application used to track activities throughout time.
|
||||
application.homepage=
|
||||
application.title=TimeStamper
|
||||
application.vendor=Jonathan Bernard
|
||||
application.version=1.5
|
||||
application.version=1.7
|
||||
build.classes.dir=${build.dir}/classes
|
||||
build.classes.excludes=**/*.java,**/*.form
|
||||
# This directory is removed when the project is cleaned:
|
||||
|
@ -48,54 +48,7 @@
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_ListenersCodePost" type="java.lang.String" value="mainPanel.addMouseMotionListener(this);"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="dayDisplay" alignment="0" pref="405" max="32767" attributes="0"/>
|
||||
<Component id="detailPanel" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="dayDisplay" pref="339" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="detailPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="jdbernard.timestamper.TimelineDayDisplay" name="dayDisplay">
|
||||
<Properties>
|
||||
<Property name="name" type="java.lang.String" value="dayDisplay" noResource="true"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[100, 100]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="405" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="339" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="detailPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
|
@ -6,16 +6,16 @@
|
||||
|
||||
package jdbernard.timestamper;
|
||||
|
||||
import java.awt.Frame;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import org.jdesktop.application.Action;
|
||||
@ -27,18 +27,31 @@ import org.jdesktop.application.Action;
|
||||
public class PunchcardDisplayDialog extends JDialog
|
||||
implements MouseMotionListener, ChangeListener {
|
||||
|
||||
/* Layout of class code:
|
||||
* Constructors
|
||||
* Generated GUI init code
|
||||
* Generated class variables
|
||||
* Additional class variables
|
||||
* Action methods
|
||||
* Listener methods
|
||||
* Additional methods
|
||||
*/
|
||||
|
||||
/** Creates new form PunchcardDisplayDialog */
|
||||
public PunchcardDisplayDialog(Frame parent, boolean modal,
|
||||
Timeline timeline, Calendar day) {
|
||||
public PunchcardDisplayDialog(JFrame parent, boolean modal,
|
||||
Calendar day) {
|
||||
super(parent, modal);
|
||||
this.timeline = timeline;
|
||||
this.day = day;
|
||||
initComponents();
|
||||
|
||||
dayDisplay.setDay(day.getTime());
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
|
||||
dayDisplay.addChangeListener(this);
|
||||
addChangeListener(dayDisplay);
|
||||
|
||||
markTextField.setFont(dayDisplay.getMarkFont());
|
||||
notesTextArea.setFont(dayDisplay.getNotesFont());
|
||||
dayDisplay.setTimeline(timeline);
|
||||
dayDisplay.setDay(day);
|
||||
dayDisplay.addChangeListener(this);
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
@ -296,100 +309,6 @@ private void mainPanelMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:e
|
||||
mousePressRelativeToWindow = evt.getPoint();
|
||||
}//GEN-LAST:event_mainPanelMousePressed
|
||||
|
||||
public void setTimeline(Timeline t) {
|
||||
timeline = t;
|
||||
dayDisplay.setTimeline(t);
|
||||
}
|
||||
|
||||
public Timeline getTimeline() {
|
||||
return timeline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String args[]) throws Exception {
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Timeline timeline = null;
|
||||
Calendar day = Calendar.getInstance();
|
||||
try { timeline = Timeline.readFromFile("test-timeline.txt"); }
|
||||
catch (Exception e) {
|
||||
System.err.println("Could not open timeline text:");
|
||||
e.printStackTrace(System.err);
|
||||
timeline = new Timeline();
|
||||
try { new File("PLACE_HERE").createNewFile(); }
|
||||
catch (Exception e1) {}
|
||||
}
|
||||
PunchcardDisplayDialog dialog = new PunchcardDisplayDialog(new javax.swing.JFrame(), true, timeline, day);
|
||||
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosing(java.awt.event.WindowEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Action
|
||||
public void previousWeek() {
|
||||
day.add(Calendar.WEEK_OF_YEAR, -1);
|
||||
dayDisplay.setDay(day);
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void previousDay() {
|
||||
day.add(Calendar.DAY_OF_YEAR, -1);
|
||||
dayDisplay.setDay(day);
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void currentDay() {
|
||||
day = Calendar.getInstance();
|
||||
dayDisplay.setDay(day);
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void nextDay() {
|
||||
day.add(Calendar.DAY_OF_YEAR, 1);
|
||||
dayDisplay.setDay(day);
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void nextWeek() {
|
||||
day.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
dayDisplay.setDay(day);
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void newMarker() {
|
||||
dayDisplay.addMarker(timestampDateChooser.getDate(),
|
||||
markTextField.getText(), notesTextArea.getText());
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void deleteMarker() {
|
||||
dayDisplay.deleteSelectedMarker();
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void saveMarkerChanges() {
|
||||
deleteMarker();
|
||||
newMarker();
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel buttonPanel;
|
||||
@ -414,10 +333,71 @@ private void mainPanelMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:e
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
private Calendar day;
|
||||
private Timeline timeline;
|
||||
private SimpleDateFormat dateFormatter =
|
||||
new SimpleDateFormat("EEE MMM dd");
|
||||
private SimpleDateFormat dateFormatter = new SimpleDateFormat("EEE MMM dd");
|
||||
private Point mousePressRelativeToWindow;
|
||||
private ArrayList<ChangeListener> changeListeners =
|
||||
new ArrayList<ChangeListener>();
|
||||
|
||||
@Action
|
||||
public void previousWeek() {
|
||||
day.add(Calendar.WEEK_OF_YEAR, -1);
|
||||
dayDisplay.setDay(day.getTime());
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void previousDay() {
|
||||
day.add(Calendar.DAY_OF_YEAR, -1);
|
||||
dayDisplay.setDay(day.getTime());
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void currentDay() {
|
||||
day = Calendar.getInstance();
|
||||
dayDisplay.setDay(day.getTime());
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void nextDay() {
|
||||
day.add(Calendar.DAY_OF_YEAR, 1);
|
||||
dayDisplay.setDay(day.getTime());
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void nextWeek() {
|
||||
day.add(Calendar.WEEK_OF_YEAR, 1);
|
||||
dayDisplay.setDay(day.getTime());
|
||||
dateLabel.setText(dateFormatter.format(day.getTime()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void newMarker() {
|
||||
dayDisplay.addMarker(timestampDateChooser.getDate(),
|
||||
markTextField.getText(), notesTextArea.getText());
|
||||
fireChangeEvent();
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void deleteMarker() {
|
||||
dayDisplay.deleteSelectedMarker();
|
||||
fireChangeEvent();
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void saveMarkerChanges() {
|
||||
deleteMarker();
|
||||
newMarker();
|
||||
}
|
||||
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
setLocation(TimeStamperView.calculateWindowMovement(
|
||||
@ -429,11 +409,66 @@ private void mainPanelMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:e
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
Timeline.TimelineMarker marker = dayDisplay.getSelectedTimelineMarker();
|
||||
timestampDateChooser.setDate(marker.getTimestamp());
|
||||
private void fireChangeEvent() {
|
||||
ChangeEvent ce = new ChangeEvent(this);
|
||||
|
||||
markTextField.setText(marker.getMark());
|
||||
notesTextArea.setText(marker.getNotes());
|
||||
for (ChangeListener cl : changeListeners)
|
||||
cl.stateChanged(ce);
|
||||
}
|
||||
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
if (e.getSource() == dayDisplay) {
|
||||
Timeline.TimelineMarker marker = dayDisplay.getSelectedTimelineMarker();
|
||||
|
||||
if (marker == null) {
|
||||
timestampDateChooser.setDate(null);
|
||||
markTextField.setText("");
|
||||
notesTextArea.setText("");
|
||||
} else {
|
||||
timestampDateChooser.setDate(marker.getTimestamp());
|
||||
markTextField.setText(marker.getMark());
|
||||
notesTextArea.setText(marker.getNotes());
|
||||
}
|
||||
} else {
|
||||
for (ChangeListener cl : changeListeners)
|
||||
if (cl != e.getSource()) cl.stateChanged(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void addChangeListener(ChangeListener cl) {
|
||||
changeListeners.add(cl);
|
||||
}
|
||||
|
||||
public boolean removeChangeListener(ChangeListener cl) {
|
||||
return changeListeners.remove(cl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*
|
||||
public static void main(String args[]) throws Exception {
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Timeline timeline = null;
|
||||
Calendar day = Calendar.getInstance();
|
||||
try { timeline = Timeline.readFromFile("test-timeline.txt"); }
|
||||
catch (Exception e) {
|
||||
System.err.println("Could not open timeline text:");
|
||||
e.printStackTrace(System.err);
|
||||
timeline = new Timeline();
|
||||
try { new File("PLACE_HERE").createNewFile(); }
|
||||
catch (Exception e1) {}
|
||||
}
|
||||
PunchcardDisplayDialog dialog = new PunchcardDisplayDialog(new javax.swing.JFrame(), true, timeline, day);
|
||||
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosing(java.awt.event.WindowEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
});
|
||||
}*/
|
||||
|
||||
}
|
||||
|
@ -33,26 +33,32 @@ implements Application.ExitListener {
|
||||
public TimeStamperApp() {
|
||||
super();
|
||||
|
||||
// set defaults for fields
|
||||
activeTimeline = new Timeline();
|
||||
|
||||
// set up logger
|
||||
log = Logger.getLogger("jdbernard.timestamper");
|
||||
log.setLevel(Level.CONFIG);
|
||||
log.setLevel(Level.ALL);
|
||||
|
||||
// add console handler
|
||||
ConsoleHandler ch = new ConsoleHandler();
|
||||
ch.setLevel(Level.INFO);
|
||||
|
||||
log.addHandler(ch);
|
||||
|
||||
// try to add file handler
|
||||
try {
|
||||
FileHandler fh = new FileHandler("TimeStamper.log", true);
|
||||
fh.setFormatter(new SimpleFormatter());
|
||||
fh.setLevel(Level.ALL);
|
||||
log.addHandler(fh);
|
||||
} catch (IOException ioe) {
|
||||
log.warning("Could not open log file for writing. Switching console"
|
||||
+ " logging to verbose.");
|
||||
ch.setLevel(Level.ALL);
|
||||
}
|
||||
|
||||
// load configuration
|
||||
try {
|
||||
config = new Properties();
|
||||
File cfgFile = new File("timestamper.config");
|
||||
@ -64,6 +70,7 @@ implements Application.ExitListener {
|
||||
log.warning("Could not load configuration options.");
|
||||
}
|
||||
|
||||
// load the last used timeline
|
||||
loadTimeline(config.getProperty("lastUsedTimelineFilename"));
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,15 @@ import java.awt.Toolkit;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import org.jdesktop.application.Action;
|
||||
import org.jdesktop.application.ResourceMap;
|
||||
import org.jdesktop.application.SingleFrameApplication;
|
||||
@ -26,7 +29,8 @@ import sun.security.jca.GetInstance;
|
||||
/**
|
||||
* The application's main frame.
|
||||
*/
|
||||
public class TimeStamperView extends FrameView implements MouseMotionListener {
|
||||
public class TimeStamperView extends FrameView implements MouseMotionListener,
|
||||
ChangeListener {
|
||||
|
||||
public TimeStamperView(SingleFrameApplication app) {
|
||||
super(app);
|
||||
@ -72,6 +76,14 @@ public class TimeStamperView extends FrameView implements MouseMotionListener {
|
||||
}
|
||||
}, 0, 1000);
|
||||
|
||||
saveTimelineTimer = new Timer();
|
||||
saveTimelineTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
saveTimeline();
|
||||
}
|
||||
}, 0, 1000*60*30);
|
||||
thinTaskFont = taskTextField.getFont().deriveFont(Font.PLAIN);
|
||||
boldTaskFont = thinTaskFont.deriveFont(Font.BOLD);
|
||||
|
||||
@ -263,6 +275,7 @@ public class TimeStamperView extends FrameView implements MouseMotionListener {
|
||||
startTimeLabel.setText(Timeline.shortFormat.format(d));
|
||||
taskTextField.setFont(boldTaskFont);
|
||||
mostRecentTask = d;
|
||||
fireChangeEvent();
|
||||
} else {
|
||||
taskTextField.setFont(thinTaskFont);
|
||||
}
|
||||
@ -329,6 +342,7 @@ public class TimeStamperView extends FrameView implements MouseMotionListener {
|
||||
private Point mousePressRelativeToFrame;
|
||||
private JFileChooser fileChooser;
|
||||
private Timer updateTimer;
|
||||
private Timer saveTimelineTimer;
|
||||
private Font boldTaskFont;
|
||||
private Font thinTaskFont;
|
||||
private Date mostRecentTask;
|
||||
@ -338,13 +352,15 @@ public class TimeStamperView extends FrameView implements MouseMotionListener {
|
||||
private boolean notesVisible = false;
|
||||
private boolean punchcardVisible = false;
|
||||
private boolean aboutDialogVisible = false;
|
||||
private ArrayList<ChangeListener> changeListeners =
|
||||
new ArrayList<ChangeListener>();
|
||||
|
||||
private PunchcardDisplayDialog getPunchcardDisplayDialog() {
|
||||
if (punchcardDisplayDialog == null) {
|
||||
punchcardDisplayDialog = new PunchcardDisplayDialog(this.getFrame(),
|
||||
false,
|
||||
((TimeStamperApp) getApplication()).getActiveTimeline(),
|
||||
Calendar.getInstance());
|
||||
false, Calendar.getInstance());
|
||||
punchcardDisplayDialog.addChangeListener(this);
|
||||
addChangeListener(punchcardDisplayDialog);
|
||||
}
|
||||
return punchcardDisplayDialog;
|
||||
}
|
||||
@ -434,6 +450,26 @@ public class TimeStamperView extends FrameView implements MouseMotionListener {
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void stateChanged(ChangeEvent ce) {
|
||||
refreshDialog();
|
||||
}
|
||||
|
||||
public void addChangeListener(ChangeListener cl) {
|
||||
changeListeners.add(cl);
|
||||
}
|
||||
|
||||
public boolean removeChangeListener(ChangeListener cl) {
|
||||
return changeListeners.remove(cl);
|
||||
}
|
||||
|
||||
private void fireChangeEvent() {
|
||||
ChangeEvent ce = new ChangeEvent(this);
|
||||
for (ChangeListener cl : changeListeners)
|
||||
cl.stateChanged(ce);
|
||||
|
||||
refreshDialog();
|
||||
}
|
||||
|
||||
@Action
|
||||
public void saveTimeline() {
|
||||
((TimeStamperApp) getApplication()).saveTimeline();
|
||||
@ -456,16 +492,13 @@ public class TimeStamperView extends FrameView implements MouseMotionListener {
|
||||
((TimeStamperApp) getApplication()).loadTimeline(
|
||||
fileChooser.getSelectedFile().getAbsolutePath());
|
||||
|
||||
refreshDialog();
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
public void refreshDialog() {
|
||||
Timeline t = ((TimeStamperApp) getApplication()).getActiveTimeline();
|
||||
Timeline.TimelineMarker lastMarker = t.getLastMarker(new Date());
|
||||
|
||||
if (punchcardDisplayDialog != null)
|
||||
punchcardDisplayDialog.setTimeline(t);
|
||||
|
||||
if (lastMarker != null) {
|
||||
mostRecentTask = lastMarker.getTimestamp();
|
||||
startTimeLabel.setText(Timeline.shortFormat.format(lastMarker.getTimestamp()));
|
||||
|
@ -126,6 +126,14 @@ public class Timeline implements Iterable<Timeline.TimelineMarker> {
|
||||
return timelineList.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the a representation of the timeline to a stream. This method
|
||||
* flushes the stream after it finishes writing but does not close the
|
||||
* stream.
|
||||
* @param stream An open stream to write the timeline representation to.
|
||||
* @param timeline The timeline to write.
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static void writeToStream(OutputStream stream, Timeline timeline)
|
||||
throws IOException {
|
||||
Writer out = new OutputStreamWriter(stream);
|
||||
@ -162,6 +170,13 @@ public class Timeline implements Iterable<Timeline.TimelineMarker> {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a representation of a timeline to a file.
|
||||
* @param filename The path to the destination file.
|
||||
* @param timeline The timeline to write.
|
||||
* @throws java.io.IOException
|
||||
* @throws java.io.FileNotFoundException
|
||||
*/
|
||||
public static void writeToFile(String filename, Timeline timeline)
|
||||
throws IOException, FileNotFoundException {
|
||||
OutputStream out = new FileOutputStream(filename);
|
||||
@ -169,6 +184,13 @@ public class Timeline implements Iterable<Timeline.TimelineMarker> {
|
||||
out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <b>Timeline</b> instance from a given stream.
|
||||
* @param stream The stream to read from.
|
||||
* @return A new <b>Timeline</b> instance specified by the input stream.
|
||||
* @throws java.io.IOException
|
||||
* @throws java.io.FileNotFoundException
|
||||
*/
|
||||
public static Timeline readFromStream(InputStream stream)
|
||||
throws IOException, FileNotFoundException {
|
||||
Scanner in = new Scanner(stream);
|
||||
@ -235,6 +257,13 @@ public class Timeline implements Iterable<Timeline.TimelineMarker> {
|
||||
return timeline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Timline instance from a file representation.
|
||||
* @param filename The path of the source file.
|
||||
* @return The new Timeline instance.
|
||||
* @throws java.io.IOException
|
||||
* @throws java.io.FileNotFoundException
|
||||
*/
|
||||
public static Timeline readFromFile(String filename)
|
||||
throws IOException, FileNotFoundException {
|
||||
InputStream in = new FileInputStream(filename);
|
||||
|
@ -27,7 +27,8 @@ import javax.swing.event.ChangeListener;
|
||||
*
|
||||
* @author jbernard
|
||||
*/
|
||||
public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
public class TimelineDayDisplay extends JComponent implements MouseListener,
|
||||
ChangeListener {
|
||||
|
||||
private class MarkerDisplayEntry {
|
||||
public Timeline.TimelineMarker marker;
|
||||
@ -38,13 +39,158 @@ public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
public Rectangle bounds;
|
||||
}
|
||||
|
||||
private Timeline timeline;
|
||||
private class TimeLegendEntry {
|
||||
public double relY;
|
||||
public String label;
|
||||
}
|
||||
|
||||
private enum TimeDelta {
|
||||
Hourly (Calendar.HOUR_OF_DAY, 1) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:00",
|
||||
c.get(Calendar.HOUR_OF_DAY));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 60l * 30l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
ThirtyMin (Calendar.MINUTE, 30) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 60l * 15l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
FifteenMin (Calendar.MINUTE, 15) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 60l * 10l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
TenMin (Calendar.MINUTE, 10) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 60l * 5l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
FiveMin (Calendar.MINUTE, 5) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 60l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
Minute (Calendar.MINUTE, 1) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 30l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
ThirtySec (Calendar.SECOND, 30) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.MINUTE), c.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 15l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
FifteenSec (Calendar.SECOND, 15) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.MINUTE), c.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 10l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
TenSec (Calendar.SECOND, 10) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.MINUTE), c.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l * 5l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
FiveSec (Calendar.SECOND, 5) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.MINUTE), c.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1000l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
Second (Calendar.SECOND, 1) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$02d",
|
||||
c.get(Calendar.MINUTE), c.get(Calendar.SECOND));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return ((height * 1500l) / millisec < 25);
|
||||
}
|
||||
},
|
||||
SubSecond (Calendar.MILLISECOND, 100) {
|
||||
public String formatCalendar(Calendar c) {
|
||||
return String.format("%1$02d:%2$03d",
|
||||
c.get(Calendar.SECOND), c.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
public boolean fitsInHeight(double height, double millisec) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private int INTERVAL;
|
||||
private int AMOUNT;
|
||||
|
||||
private TimeDelta(int interval, int amount) {
|
||||
INTERVAL = interval;
|
||||
AMOUNT = amount;
|
||||
}
|
||||
|
||||
public Calendar addToCalendar(Calendar c) {
|
||||
c.add(INTERVAL, AMOUNT);
|
||||
return c;
|
||||
}
|
||||
|
||||
public abstract boolean fitsInHeight(double height, double millisec);
|
||||
|
||||
public abstract String formatCalendar(Calendar c); { }
|
||||
}
|
||||
|
||||
private ArrayList<MarkerDisplayEntry> markerEntries;
|
||||
private Calendar day = Calendar.getInstance();
|
||||
private Calendar nextDay = Calendar.getInstance();
|
||||
private ArrayList<TimeLegendEntry> timeLegendLocations;
|
||||
private Timeline.TimelineMarker currentMarker;
|
||||
private ArrayList<ChangeListener> changeListeners = new ArrayList<ChangeListener>();
|
||||
|
||||
private Point lastMousePress;
|
||||
|
||||
private Font markFont;// = getFont().deriveFont(Font.BOLD);
|
||||
private Font notesFont;// = getFont();
|
||||
|
||||
@ -56,42 +202,64 @@ public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
private Color selectedOpaque = new Color(0.5f, 0.75f, 0.5f, 1f);
|
||||
private Color fontColor = new Color(0.1f, 0.1f, 0.1f, 1f);
|
||||
|
||||
private long oneDayInMilli = (24 * 60 * 60 * 1000);
|
||||
private Date rangeStartDate = new Date();
|
||||
private Date rangeEndDate = new Date();
|
||||
|
||||
public TimelineDayDisplay() {
|
||||
super();
|
||||
setDay(new Date(), false);
|
||||
addMouseListener(this);
|
||||
}
|
||||
|
||||
public TimelineDayDisplay(Timeline timeline, Calendar day) {
|
||||
this();
|
||||
this.timeline = timeline;
|
||||
this.day = day;
|
||||
public TimelineDayDisplay(Calendar day) {
|
||||
setDay(day.getTime(), false);
|
||||
addMouseListener(this);
|
||||
updateMarkers(getGraphics());
|
||||
}
|
||||
|
||||
public void setTimeline(Timeline t) {
|
||||
timeline = t;
|
||||
updateMarkers(getGraphics());
|
||||
/**
|
||||
* Set the range for the visible timeline segment.
|
||||
* @param start The beginning of the desired timeline segment.
|
||||
* @param end The end of the desired timeline segment.
|
||||
*/
|
||||
public void setDisplayInterval(Date start, Date end) {
|
||||
rangeStartDate = start;
|
||||
rangeEndDate = end;
|
||||
}
|
||||
|
||||
public Timeline getTimeline() {
|
||||
return timeline;
|
||||
/**
|
||||
* Set the component to show the timeline segment for a specific day. The
|
||||
* visible area will show the full 24-hour day.
|
||||
* @param d The date of the day to display. The exact time of the variable
|
||||
* can be any time in the desired day.
|
||||
*/
|
||||
public void setDay(Date d) {
|
||||
setDay(d, true);
|
||||
}
|
||||
|
||||
public void setDay(Calendar d) {
|
||||
day.setTime(d.getTime());
|
||||
/**
|
||||
* There is the special case of instance initialization, where it is
|
||||
* desirable to call setDay to handle the range start and end calculations
|
||||
* but where we do not want to immediately update the gui, because it may
|
||||
* not be fully initialized yet.
|
||||
* @param d Day to set as the current day (component will show the range
|
||||
* representing the day from start to finish.
|
||||
* @param update If <b><pre>true</pre></b>,
|
||||
* <code>updateMarkers(getGraphics)</code> is called after the range
|
||||
* calculations are made.
|
||||
*/
|
||||
private void setDay(Date d, boolean update) {
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.setTime(d);
|
||||
day.set(Calendar.HOUR_OF_DAY, 0);
|
||||
day.set(Calendar.MINUTE, 0);
|
||||
day.set(Calendar.SECOND, 0);
|
||||
rangeStartDate = day.getTime();
|
||||
|
||||
nextDay.setTime(day.getTime());
|
||||
nextDay.add(Calendar.DAY_OF_YEAR, 1);
|
||||
updateMarkers(getGraphics());
|
||||
}
|
||||
day.add(Calendar.DAY_OF_YEAR, 1);
|
||||
rangeEndDate = day.getTime();
|
||||
|
||||
public Calendar getDay() {
|
||||
return day;
|
||||
if (update) updateMarkers(getGraphics());
|
||||
}
|
||||
|
||||
public void setMarkFont(Font f) {
|
||||
@ -155,11 +323,13 @@ public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
}
|
||||
|
||||
public void addMarker(Date timestamp, String mark, String notes) {
|
||||
Timeline timeline = TimeStamperApp.getApplication().getActiveTimeline();
|
||||
timeline.addMarker(timestamp, mark, notes);
|
||||
updateMarkers(getGraphics());
|
||||
}
|
||||
|
||||
public void deleteSelectedMarker() {
|
||||
Timeline timeline = TimeStamperApp.getApplication().getActiveTimeline();
|
||||
timeline.removeMarker(currentMarker);
|
||||
updateMarkers(getGraphics());
|
||||
}
|
||||
@ -169,25 +339,81 @@ public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
updateMarkers(getGraphics());
|
||||
}
|
||||
|
||||
/**
|
||||
* updateMarkers sets the internal list of TimelineMarkers, based on the
|
||||
* currently visible timeline. The drawing of the display is split between
|
||||
* this method, which constructs the data representation of what needs to
|
||||
* be drawn, and the paintComponents method, which does the drawing. This is
|
||||
* done to save computation, only recalculating markers when needed.
|
||||
*/
|
||||
private void updateMarkers(Graphics g) {
|
||||
|
||||
Timeline timeline = TimeStamperApp.getApplication().getActiveTimeline();
|
||||
Insets insets = this.getInsets();
|
||||
Rectangle bounds = this.getBounds();
|
||||
Rectangle canvasBounds = new Rectangle(insets.left, insets.top,
|
||||
bounds.width - insets.left - insets.right - 1,
|
||||
bounds.height - insets.top - insets.bottom - 1);
|
||||
|
||||
Rectangle2D stringBounds = getFontMetrics(getFont()).getStringBounds("00:00 ", g);
|
||||
|
||||
long rangeDiff = rangeEndDate.getTime() - rangeStartDate.getTime();
|
||||
|
||||
markerEntries = new ArrayList<MarkerDisplayEntry>();
|
||||
timeLegendLocations = new ArrayList<TimeLegendEntry>();
|
||||
|
||||
if (markFont == null) markFont = getFont().deriveFont(Font.BOLD);
|
||||
if (notesFont == null) notesFont = getFont();
|
||||
|
||||
// get all relevant markers
|
||||
Timeline.TimelineMarker tm = timeline.getLastMarker(day.getTime());
|
||||
// calculate positions of all visible hour lines
|
||||
// choose the increment of time to view
|
||||
TimeDelta timeDelta = TimeDelta.Hourly;
|
||||
if (rangeDiff == 0) rangeDiff = 1;
|
||||
|
||||
for (TimeDelta d : TimeDelta.values()) {
|
||||
if (d.fitsInHeight(canvasBounds.getHeight(), rangeDiff)) {
|
||||
timeDelta = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Calendar timeCounter = Calendar.getInstance();
|
||||
timeCounter.setTime(rangeStartDate);
|
||||
timeCounter.set(Calendar.MINUTE, 0);
|
||||
timeCounter.set(Calendar.SECOND, 0);
|
||||
|
||||
while (rangeStartDate.after(timeCounter.getTime()))
|
||||
timeDelta.addToCalendar(timeCounter);
|
||||
|
||||
while (rangeEndDate.after(timeCounter.getTime())) {
|
||||
TimeLegendEntry entry = new TimeLegendEntry();
|
||||
entry.relY = ((double) (timeCounter.getTimeInMillis()
|
||||
- rangeStartDate.getTime()) / (double) rangeDiff);
|
||||
entry.label = timeDelta.formatCalendar(timeCounter);
|
||||
timeLegendLocations.add(entry);
|
||||
timeDelta.addToCalendar(timeCounter);
|
||||
}
|
||||
|
||||
// get all relevant markers starting from the marker just before the
|
||||
// visible start of the display
|
||||
Timeline.TimelineMarker tm = timeline.getLastMarker(rangeStartDate);
|
||||
|
||||
// If there is no previous marker
|
||||
if (tm == null)
|
||||
// try to get the first marker
|
||||
try { tm = timeline.iterator().next(); }
|
||||
// and if there aren't any markers at all, just return, the array is
|
||||
// empty so the display will be empty
|
||||
catch (Exception e) { return; }
|
||||
|
||||
// Now we want to step through the timeline, capturing all markers
|
||||
// between the visible ranges.
|
||||
Iterator<Timeline.TimelineMarker> itr = timeline.iterator();
|
||||
|
||||
while (!itr.next().equals(tm));
|
||||
|
||||
ArrayList<Timeline.TimelineMarker> markers = new ArrayList<Timeline.TimelineMarker>();
|
||||
while (nextDay.getTime().after(tm.getTimestamp())) {
|
||||
while (rangeEndDate.after(tm.getTimestamp())) {
|
||||
markers.add(tm);
|
||||
if (itr.hasNext()) tm = itr.next();
|
||||
else break;
|
||||
@ -207,29 +433,35 @@ public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
.getStringBounds(markers.get(i).getNotes(), g);
|
||||
|
||||
// calculate upper bound
|
||||
if ((i == 0) && day.getTime().after(markerEntry.marker.getTimestamp())) {
|
||||
if ((i == 0) && rangeStartDate.after(markerEntry.marker.getTimestamp())) {
|
||||
//if this is the first marker (before the start time) set the
|
||||
// Y coor to 0, top of display
|
||||
markerEntry.relY = 0;
|
||||
} else {
|
||||
// calculate time in ms since the beginning of the day
|
||||
markerEntry.relY = markers.get(i).getTimestamp().getTime()
|
||||
- day.getTimeInMillis();
|
||||
// calculate percentage of total time in day
|
||||
markerEntry.relY /= oneDayInMilli;
|
||||
// otherwise, calculate how far down (%-wise) the mark is
|
||||
markerEntry.relY = (float) (((double) (markerEntry.marker.getTimestamp().getTime()
|
||||
- rangeStartDate.getTime())) / (double) rangeDiff);
|
||||
}
|
||||
|
||||
// calculate lower bound
|
||||
if ((i == 0) && day.getTime().after(markerEntry.marker.getTimestamp()))
|
||||
if ((i == 0) && rangeStartDate.after(markerEntry.marker.getTimestamp()))
|
||||
// if this is the first marker (before the start time), set the
|
||||
// height to equal the top of the next marker
|
||||
markerEntry.relHeight =
|
||||
markers.get(i + 1).getTimestamp().getTime()
|
||||
- day.getTimeInMillis();
|
||||
- rangeStartDate.getTime();
|
||||
else if (i == markers.size() - 2)
|
||||
markerEntry.relHeight = nextDay.getTimeInMillis()
|
||||
- markers.get(i).getTimestamp().getTime();
|
||||
// if this is the last visible marker, set the height to extend
|
||||
// to the bottom of the display
|
||||
markerEntry.relHeight = rangeEndDate.getTime()
|
||||
- markerEntry.marker.getTimestamp().getTime();
|
||||
else
|
||||
// set the height to the difference between this marker and the
|
||||
// next.
|
||||
markerEntry.relHeight =
|
||||
markers.get(i + 1).getTimestamp().getTime()
|
||||
- markers.get(i).getTimestamp().getTime();
|
||||
markerEntry.relHeight /= oneDayInMilli;
|
||||
- markerEntry.marker.getTimestamp().getTime();
|
||||
markerEntry.relHeight /= rangeDiff;
|
||||
|
||||
markerEntries.add(markerEntry);
|
||||
}
|
||||
@ -253,17 +485,15 @@ public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
Rectangle2D stringBounds = getFontMetrics(getFont()).getStringBounds("00:00 ", g);
|
||||
|
||||
// draw hour lines
|
||||
for (int i = 1; i < 24; i++) {
|
||||
int lineY = (int) Math.round(hourHeight * (double) i);
|
||||
g.drawLine(canvasBounds.x + (int) stringBounds.getWidth(), canvasBounds.y + lineY,
|
||||
canvasBounds.x + canvasBounds.width,
|
||||
canvasBounds.y + lineY);
|
||||
for (TimeLegendEntry legendEntry : timeLegendLocations) {
|
||||
g.drawLine(canvasBounds.x + (int) stringBounds.getWidth(),
|
||||
(int) (canvasBounds.y + (canvasBounds.height * legendEntry.relY)),
|
||||
canvasBounds.x + canvasBounds.width,
|
||||
(int) (canvasBounds.y + (canvasBounds.height * legendEntry.relY)));
|
||||
|
||||
//draw hours labels
|
||||
if ((hourHeight > (stringBounds.getHeight()) || ((i % 2) == 0))) {
|
||||
g.drawString(String.format("%1$02d:00", i),
|
||||
canvasBounds.x + 2, lineY + (int) (stringBounds.getHeight() / 2));
|
||||
}
|
||||
g.drawString(legendEntry.label, canvasBounds.x + 2,
|
||||
(int) (canvasBounds.y + (canvasBounds.height * legendEntry.relY)
|
||||
+ (stringBounds.getHeight() / 2)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < markerEntries.size(); i++) {
|
||||
@ -349,25 +579,66 @@ public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
}
|
||||
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
Point topLeft = getLocationOnScreen();
|
||||
for (MarkerDisplayEntry markerEntry : markerEntries) {
|
||||
Rectangle absBounds = new Rectangle(markerEntry.bounds);
|
||||
absBounds.translate(topLeft.x, topLeft.y);
|
||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||
Point topLeft = getLocationOnScreen();
|
||||
currentMarker = null;
|
||||
for (MarkerDisplayEntry markerEntry : markerEntries) {
|
||||
Rectangle absBounds = new Rectangle(markerEntry.bounds);
|
||||
absBounds.translate(topLeft.x, topLeft.y);
|
||||
|
||||
// should only match one entry
|
||||
if (absBounds.contains(e.getLocationOnScreen())) {
|
||||
currentMarker = markerEntry.marker;
|
||||
repaint();
|
||||
fireChangeEvent();
|
||||
break;
|
||||
// should only match one entry
|
||||
if (absBounds.contains(e.getLocationOnScreen())) {
|
||||
currentMarker = markerEntry.marker;
|
||||
break;
|
||||
}
|
||||
}
|
||||
repaint();
|
||||
fireChangeEvent();
|
||||
} else if (e.getButton() == MouseEvent.BUTTON3) {
|
||||
setDay(rangeStartDate);
|
||||
}
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
lastMousePress = e.getPoint();
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
Insets insets = this.getInsets();
|
||||
Rectangle bounds = this.getBounds();
|
||||
Rectangle canvasBounds = new Rectangle(insets.left, insets.top,
|
||||
bounds.width - insets.left - insets.right - 1,
|
||||
bounds.height - insets.top - insets.bottom - 1);
|
||||
|
||||
double rangeDiff = rangeEndDate.getTime() - rangeStartDate.getTime();
|
||||
double y1 = lastMousePress.getY();
|
||||
double y2 = e.getY();
|
||||
|
||||
if (Math.abs(y2 - y1) < 5) return;
|
||||
|
||||
// get time for y1
|
||||
long time1 = (long) Math.round((((y1 - canvasBounds.y)
|
||||
/ canvasBounds.height) * rangeDiff) + rangeStartDate.getTime());
|
||||
long time2 = (long) Math.round((((y2 - canvasBounds.y)
|
||||
/ canvasBounds.height) * rangeDiff) + rangeStartDate.getTime());
|
||||
|
||||
// left click, scroll
|
||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||
long difference = time1 - time2;
|
||||
rangeStartDate.setTime(rangeStartDate.getTime() + difference);
|
||||
rangeEndDate.setTime(rangeEndDate.getTime() + difference);
|
||||
}
|
||||
// right click, zoom
|
||||
else if (e.getButton() == MouseEvent.BUTTON3) {
|
||||
if (time1 < time2) {
|
||||
rangeStartDate.setTime(time1);
|
||||
rangeEndDate.setTime(time2);
|
||||
} else {
|
||||
rangeStartDate.setTime(time2);
|
||||
rangeEndDate.setTime(time1);
|
||||
}
|
||||
}
|
||||
updateMarkers(getGraphics());
|
||||
}
|
||||
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
@ -375,4 +646,10 @@ public class TimelineDayDisplay extends JComponent implements MouseListener {
|
||||
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void stateChanged(ChangeEvent ce) {
|
||||
updateMarkers(getGraphics());
|
||||
repaint();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Application.name = TimeStamper
|
||||
Application.title = TimeStamper
|
||||
Application.version = 1.5
|
||||
Application.version = 1.7
|
||||
Application.vendor = Jonathan Bernard
|
||||
Application.homepage =
|
||||
Application.description = Simple application used to track activities throughout time.
|
||||
|
Binary file not shown.
1
todo.xml
Executable file
1
todo.xml
Executable file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><TodoList name="TimeStamper Todo List"><Description>Features, bugfixes, things that need to be done.</Description><Owner>TimeStamper Development</Owner><TaskList name="unallocated"><Task created="20081023170946" name="WeekTimelineDisplay" priority="0"><Description>Create a display that shows a full week.</Description></Task><Task created="20081023171011" name="MonthTimelineDisplay" priority="0"><Description>Show a full month.</Description></Task><Task allocated="20081029175454" created="20081029180059" name="Add GUI Action Controlsto DayTimelineDisplay" priority="1"><Description>Allow the GUI actions of the component to be enabled/disabled programatically. For example, add setZoomAllowed(boolean allowed) function to enable/disable the ability to zoom with the mouse.</Description></Task></TaskList><TaskList name="old"><Task allocated="20081023170513" created="20081023221630" finished="20081023235630" name="Auto-scale DatyTimelineDisplay Time Scale" priority="1"><Description/></Task><Task allocated="20081023170513" created="20081023235727" finished="20081023235730" name="Click and Drag Scroll On the DayTimelineDisplay" priority="1"><Description>The user is able to click and drag on the timeline displayed and the timeline follows the mouse.</Description></Task><Task allocated="20081023170513" created="20081023193554" finished="20081023193602" name="Deselect markers in DayTimelineDisplay" priority="1"><Description>Allow the user to select no marker in the DayTimelineDisplay by clicking in a region of the display that is not covered by an activity.</Description></Task><Task allocated="20081023170513" created="20081023170924" finished="20081023221612" name="Zoom for DayTimelineDisplay" priority="1"><Description>When the user clicks and drags, the ui should zoom to the region the user covered in the vertical access. For example, if the display shows a full day and the user clicks in 9:00 and drags to 13:00, the ui should refresh to put 9:00 at the top, 13:00 at the bottom, and re-scale the rest in between.</Description></Task></TaskList><TaskList name="current"><Task allocated="20081029175454" created="20081024010642" finished="20081029175523" name="Update All Displays When One Changes" priority="1"><Description>When you make changes to the DayTimelineDisplay, for example, those changes need to be propogated through the tool back to the main display.</Description></Task></TaskList></TodoList>
|
Loading…
x
Reference in New Issue
Block a user