Incremental GUI updates. Copied over code for TimelineDayDisplay.
This commit is contained in:
parent
02fc6b5bb7
commit
bff340e910
@ -1,4 +1,4 @@
|
||||
#Thu Dec 17 08:19:45 CST 2009
|
||||
app.version=0.1
|
||||
app.version=2.0
|
||||
app.griffon.version=0.2
|
||||
app.name=TimeStamper
|
||||
|
@ -1,21 +1,8 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
import java.awt.event.KeyEvent
|
||||
|
||||
gracefulExitAction = action (
|
||||
name: 'Graceful Exit',
|
||||
closure: controller.&exitGracefully
|
||||
)
|
||||
|
||||
toolsMenuAction = action (
|
||||
name: 'Show Tools Menu',
|
||||
closure: controller.&showToolsMenu
|
||||
)
|
||||
|
||||
showNotesAction = action (
|
||||
name: 'Show Notes',
|
||||
closure: controller.&showNotes
|
||||
)
|
||||
|
||||
showPunchcardAction = action (
|
||||
name: 'Show Punchcard',
|
||||
closure: controller.&showPunchcard
|
||||
)
|
||||
|
@ -9,20 +9,27 @@ application {
|
||||
//frameClass = 'javax.swing.JFrame'
|
||||
}
|
||||
mvcGroups {
|
||||
// MVC Group for "com.jdbernard.timestamper.PunchcardDialog"
|
||||
'PunchcardDialog' {
|
||||
model = 'com.jdbernard.timestamper.PunchcardDialogModel'
|
||||
view = 'com.jdbernard.timestamper.PunchcardDialogView'
|
||||
controller = 'com.jdbernard.timestamper.PunchcardDialogController'
|
||||
}
|
||||
|
||||
// MVC Group for "com.jdbernard.timestamper.NotesDialog"
|
||||
'NotesDialog' {
|
||||
actions = 'com.jdbernard.timestamper.NotesDialogActions'
|
||||
model = 'com.jdbernard.timestamper.NotesDialogModel'
|
||||
controller = 'com.jdbernard.timestamper.NotesDialogController'
|
||||
view = 'com.jdbernard.timestamper.NotesDialogView'
|
||||
controller = 'com.jdbernard.timestamper.NotesDialogController'
|
||||
}
|
||||
|
||||
// MVC Group for "com.jdbernard.timestamper.TimeStamperMain"
|
||||
'TimeStamperMain' {
|
||||
actions = 'com.jdbernard.timestamper.TimeStamperMainActions'
|
||||
model = 'com.jdbernard.timestamper.TimeStamperMainModel'
|
||||
controller = 'com.jdbernard.timestamper.TimeStamperMainController'
|
||||
view = 'com.jdbernard.timestamper.TimeStamperMainView'
|
||||
controller = 'com.jdbernard.timestamper.TimeStamperMainController'
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,26 +1,11 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import java.awt.Toolkit
|
||||
|
||||
class NotesDialogController {
|
||||
// these will be injected by Griffon
|
||||
def model
|
||||
def view
|
||||
|
||||
|
||||
void mvcGroupInit(Map args) {
|
||||
}
|
||||
|
||||
Point mousePressRelativeToDialog
|
||||
|
||||
def mousePressed = { evt -> mousePressRelativeToDialog = evt?.point }
|
||||
|
||||
def mouseDragged = { evt ->
|
||||
GUIUtil.componentDragged(view.notesDialog, evt,
|
||||
mousePressRelativeToDialog,
|
||||
new Rectangle(Toolkit.defaultToolkit.screenSize),
|
||||
app.views.TimeStamperMain.frame.bounds)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
class PunchcardDialogController {
|
||||
// these will be injected by Griffon
|
||||
def model
|
||||
def view
|
||||
|
||||
void mvcGroupInit(Map args) {
|
||||
// this method is called after model and view are injected
|
||||
}
|
||||
|
||||
/*
|
||||
def action = { evt = null ->
|
||||
}
|
||||
*/
|
||||
}
|
@ -1,49 +1,64 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import java.awt.Toolkit
|
||||
import java.util.Timer
|
||||
import com.jdbernard.timestamper.core.TimelineMarker
|
||||
import com.jdbernard.timestamper.core.TimelineProperties
|
||||
|
||||
class TimeStamperMainController {
|
||||
// these will be injected by Griffon
|
||||
def model
|
||||
def view
|
||||
|
||||
Timer updateTimer
|
||||
|
||||
Point mousePressRelativeToFrame
|
||||
|
||||
void mvcGroupInit(Map args) {
|
||||
def notes = buildMVCGroup('NotesDialog')
|
||||
def punchcard = buildMVCGroup('PunchcardDialog')
|
||||
view.notesDialog = notes.view.notesDialog
|
||||
view.punchcardDialog = punchcard.view.punchcardDialog
|
||||
|
||||
// load application properties
|
||||
Properties prop = new Properties()
|
||||
String userHomeDir = System.getProperty('user.home')
|
||||
model.configFile = new File(userHomeDir, ".timestamperrc")
|
||||
if (!model.configFile.exists()) model.configFile.createNewFile()
|
||||
|
||||
try { model.configFile.withInputStream { prop.load(it) } }
|
||||
catch (IOException ioe) { /* TODO */ }
|
||||
|
||||
model.config = prop
|
||||
|
||||
// load the last used timeline file
|
||||
String lastUsed = model.config.getProperty('lastUsed', null)
|
||||
if (lastUsed == null) {
|
||||
lastUsed = 'timeline.default.properties'
|
||||
model.config.setProperty('lastUsed', lastUsed)
|
||||
}
|
||||
File propertyFile = new File(lastUsed)
|
||||
if (!propertyFile.exists()) propertyFile.createNewFile()
|
||||
|
||||
model.timelineProperties = new TimelineProperties(propertyFile)
|
||||
|
||||
// load the main timeline
|
||||
model.timeline = model.timelineProperties.timeline
|
||||
|
||||
// load the last marker
|
||||
model.currentMarker = model.timeline.getLastMarker(new Date())
|
||||
|
||||
updateTimer
|
||||
}
|
||||
|
||||
def exitGracefully = { evt = null ->
|
||||
// save config
|
||||
try { model.configFile.withOutputStream { out ->
|
||||
model.config.store(out, null) } }
|
||||
catch (IOException ioe) {}
|
||||
|
||||
// save timeline and properties
|
||||
model.timelineProperties.save()
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
def showToolsMenu = { evt = null ->
|
||||
|
||||
}
|
||||
|
||||
def showNotes = { evt = null ->
|
||||
view.notesDialog.visible = view.notesVisibleButton.selected
|
||||
}
|
||||
|
||||
def showPunchcard = { evt = null ->
|
||||
|
||||
}
|
||||
|
||||
def mousePressed = { evt = null ->
|
||||
mousePressRelativeToFrame = evt?.point
|
||||
}
|
||||
|
||||
def mouseDragged = { evt = null ->
|
||||
GUIUtil.componentDragged(view.frame, evt, mousePressRelativeToFrame,
|
||||
new Rectangle(Toolkit.defaultToolkit.screenSize))
|
||||
def newTask = { mark ->
|
||||
model.currentMarker = new TimelineMarker(new Date(), mark,
|
||||
"No comments.")
|
||||
model.timeline.addMarker(model.currentMarker)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
import groovy.beans.Bindable
|
||||
|
||||
class PunchcardDialogModel {
|
||||
// @Bindable String propName
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
import groovy.beans.Bindable
|
||||
import java.awt.Point
|
||||
import java.util.Properties
|
||||
import com.jdbernard.timestamper.core.Timeline
|
||||
import com.jdbernard.timestamper.core.TimelineMarker
|
||||
import com.jdbernard.timestamper.core.TimelineProperties
|
||||
@ -8,5 +10,9 @@ import com.jdbernard.timestamper.core.TimelineProperties
|
||||
class TimeStamperMainModel {
|
||||
@Bindable TimelineMarker currentMarker
|
||||
@Bindable Timeline timeline
|
||||
@Bindable TimelineProperties properties
|
||||
@Bindable TimelineProperties timelineProperties
|
||||
@Bindable Properties config
|
||||
File configFile
|
||||
|
||||
@Bindable Point absoluteLocation
|
||||
}
|
||||
|
@ -1,27 +1,55 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
import java.awt.Color
|
||||
import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import java.awt.Toolkit
|
||||
import javax.swing.BoxLayout
|
||||
import net.miginfocom.swing.MigLayout
|
||||
|
||||
Point mousePressRelativeToDialog
|
||||
Point offsetFromMainFrame
|
||||
|
||||
mousePressed = { evt -> mousePressRelativeToDialog = evt?.point }
|
||||
|
||||
mouseDragged = { evt ->
|
||||
GUIUtil.componentDragged(view.notesDialog, evt,
|
||||
mousePressRelativeToDialog,
|
||||
new Rectangle(Toolkit.defaultToolkit.screenSize),
|
||||
app.views.TimeStamperMain.frame.bounds)
|
||||
|
||||
Point p = app.views.TimeStamperMain.frame.location
|
||||
offsetFromMainFrame = new Point(notesDialog.location)
|
||||
offsetFromMainFrame.translate((int) -p.x, (int) -p.y)
|
||||
}
|
||||
|
||||
notesDialog = dialog(
|
||||
title: 'Notes',
|
||||
modal: false,
|
||||
undecorated: true,
|
||||
minimumSize: [325, 200],
|
||||
iconImage: imageIcon('/16-em-pencil.png').image,
|
||||
iconImages: [imageIcon('/16-em-pencil.png').image]
|
||||
iconImages: [imageIcon('/16-em-pencil.png').image],
|
||||
location: bind(source: app.models.TimeStamperMain,
|
||||
sourceProperty: 'absoluteLocation',
|
||||
converter: { loc ->
|
||||
Point p = new Point(offsetFromMainFrame)
|
||||
p.translate((int) loc.x, (int) loc.y)
|
||||
return p})
|
||||
) {
|
||||
|
||||
panel(
|
||||
border:lineBorder(color: Color.BLACK, thickness:1, parent:true),
|
||||
mousePressed: controller.&mousePressed,
|
||||
mouseDragged: controller.&mouseDragged,
|
||||
mousePressed: mousePressed,
|
||||
mouseDragged: mouseDragged,
|
||||
layout: new MigLayout('insets 5 5 5 5, fill')
|
||||
) {
|
||||
scrollPane(constraints: 'growx, growy') {
|
||||
scrollPane(constraints: 'growx, growy, spany 2') {
|
||||
notesTextArea = textArea(lineWrap: true, columns: 20, rows: 5,
|
||||
wrapStyleWord: true)
|
||||
wrapStyleWord: true,
|
||||
text: bind(source: app.models.TimeStamperMain,
|
||||
sourceProperty: 'currentMarker',
|
||||
sourceValue: { app.models.TimeStamperMain.currentMarker?.notes}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
punchcardDialog = dialog(
|
||||
/* title: 'Punchcard',
|
||||
modal: false,
|
||||
undecorated: true,
|
||||
iconImage: iconImage('/16-file-archive.png').image,
|
||||
iconImages: [iconImage('/16-file-archive.png').image],
|
||||
minimumSize: [325, 600]*/
|
||||
) {
|
||||
|
||||
}
|
@ -1,10 +1,88 @@
|
||||
package com.jdbernard.timestamper
|
||||
|
||||
import groovy.beans.Bindable
|
||||
import java.awt.Color
|
||||
import java.awt.Font
|
||||
import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import java.awt.Toolkit
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.Timer
|
||||
import com.jdbernard.timestamper.core.Timeline
|
||||
import net.miginfocom.swing.MigLayout
|
||||
|
||||
Point mousePressRelativeToFrame
|
||||
|
||||
/* ========== *
|
||||
* GUI Events *
|
||||
* ========== */
|
||||
|
||||
taskTextFieldChanged = { evt = null ->
|
||||
if (evt.keyCode == KeyEvent.VK_ENTER) {
|
||||
taskTextField.font = taskBoldFont
|
||||
controller.newTask(taskTextField.text)
|
||||
}
|
||||
|
||||
else if (evt.keyCode == KeyEvent.VK_ESCAPE) {
|
||||
taskTextField.font = taskBoldFont
|
||||
taskTextField.text = model.currentMarker.mark
|
||||
}
|
||||
|
||||
else if (!evt.isActionKey())
|
||||
taskTextField.font = taskThinFont
|
||||
}
|
||||
|
||||
showToolsMenu = { evt = null ->
|
||||
|
||||
}
|
||||
|
||||
showNotes = { evt = null ->
|
||||
notesDialog.visible = notesVisibleButton.selected
|
||||
}
|
||||
|
||||
showPunchcard = { evt = null ->
|
||||
|
||||
}
|
||||
|
||||
mousePressed = { evt = null ->
|
||||
mousePressRelativeToFrame = evt?.point
|
||||
}
|
||||
|
||||
mouseDragged = { evt = null ->
|
||||
GUIUtil.componentDragged(frame, evt, mousePressRelativeToFrame,
|
||||
new Rectangle(Toolkit.defaultToolkit.screenSize))
|
||||
}
|
||||
|
||||
/* ============== *
|
||||
* GUI Definition *
|
||||
* ============== */
|
||||
|
||||
updateTimer = new Timer(1000, action(name: 'GUI Refresh', closure: {
|
||||
Date currentTime = new Date()
|
||||
currentTimeLabel.text = Timeline.shortFormat.format(currentTime)
|
||||
if (model.currentMarker != null) {
|
||||
long seconds = currentTime.time - model.currentMarker.timestamp.time
|
||||
seconds /= 1000
|
||||
long minutes = seconds / 60
|
||||
seconds = seconds % 60
|
||||
long hours = minutes / 60
|
||||
minutes %= 60
|
||||
long days = hours / 24
|
||||
hours %= 24
|
||||
|
||||
StringBuilder sb = new StringBuilder()
|
||||
if (days > 0) sb.append(days + "day ")
|
||||
if (hours > 0) sb.append(hours + "hr ")
|
||||
if (minutes > 0) sb.append(minutes + "min ")
|
||||
sb.append(seconds + "sec")
|
||||
|
||||
totalTimeLabel.text = sb.toString()
|
||||
} else totalTimeLabel.text = ""
|
||||
}))
|
||||
|
||||
updateTimer.start()
|
||||
|
||||
frame = application(title:'TimeStamper',
|
||||
//size:[320,480],
|
||||
pack:true,
|
||||
@ -13,23 +91,29 @@ frame = application(title:'TimeStamper',
|
||||
locationByPlatform:true,
|
||||
iconImage: imageIcon('/appointment-new-32x32.png').image,
|
||||
iconImages: [imageIcon('/appointment-new-32x32.png').image,
|
||||
imageIcon('/appointment-new-16x16.png').image]
|
||||
imageIcon('/appointment-new-16x16.png').image],
|
||||
componentMoved: { evt -> model.absoluteLocation = frame.location }
|
||||
) {
|
||||
panel(
|
||||
border:lineBorder(color:Color.BLACK, thickness:1, parent:true),
|
||||
layout: new MigLayout('insets 0 5 0 0, fill','', '[]0[]0[]'),
|
||||
mousePressed: controller.&mousePressed,
|
||||
mouseDragged: controller.&mouseDragged
|
||||
mousePressed: mousePressed,
|
||||
mouseDragged: mouseDragged
|
||||
) {
|
||||
def mainFont = new Font(Font.SANS_SERIF, Font.BOLD, 12)
|
||||
def timeFont = new Font(Font.SANS_SERIF, Font.BOLD, 14)
|
||||
label("Current task started at ", font: mainFont)
|
||||
label("00:00:00", constraints: 'align leading',
|
||||
font: timeFont, foreground: [0, 102, 102])
|
||||
label(constraints: 'align leading', font: timeFont,
|
||||
foreground: [0, 102, 102],
|
||||
text: bind(source: model, sourceProperty: 'currentMarker',
|
||||
sourceValue: {
|
||||
model.currentMarker == null ? "00:00:00" :
|
||||
Timeline.shortFormat.format(model.currentMarker.timestamp)
|
||||
}))
|
||||
|
||||
panel(constraints: 'alignx trailing, aligny top, wrap') {
|
||||
boxLayout(axis: BoxLayout.X_AXIS)
|
||||
button(toolsMenuAction,
|
||||
button(actionPerformed: showToolsMenu,
|
||||
icon: imageIcon('/16-tool-a.png'),
|
||||
rolloverIcon: imageIcon('/16-tool-a-hover.png'),
|
||||
border: emptyBorder(0),
|
||||
@ -43,23 +127,35 @@ frame = application(title:'TimeStamper',
|
||||
hideActionText: true)
|
||||
}
|
||||
|
||||
textField("Task name", constraints: "growx, span 2, w 250::")
|
||||
taskTextField = textField("Task name",
|
||||
constraints: "growx, span 2, w 250::",
|
||||
keyReleased: taskTextFieldChanged,
|
||||
text: bind(source: model, sourceProperty: 'currentMarker',
|
||||
sourceValue: { model.currentMarker.mark }))
|
||||
|
||||
taskThinFont = taskTextField.font
|
||||
taskBoldFont = taskTextField.font.deriveFont(Font.BOLD)
|
||||
|
||||
panel(constraints: 'alignx leading, aligny top, gapright 5px, wrap') {
|
||||
boxLayout(axis: BoxLayout.X_AXIS)
|
||||
notesVisibleButton = toggleButton(showNotesAction, icon: imageIcon('/16-em-pencil.png'),
|
||||
notesVisibleButton = toggleButton(
|
||||
actionPerformed: showNotes,
|
||||
icon: imageIcon('/16-em-pencil.png'),
|
||||
hideActionText: true,
|
||||
border: emptyBorder(4))
|
||||
punchcardVisibleButton = toggleButton(showPunchcardAction,
|
||||
punchcardVisibleButton = toggleButton(
|
||||
actionPerformed: showPunchcard,
|
||||
icon: imageIcon('/16-file-archive.png'),
|
||||
hideActionText: true,
|
||||
border: emptyBorder(4))
|
||||
}
|
||||
|
||||
label("2hr 18min 56sec", constraints: 'alignx leading', font: timeFont,
|
||||
foreground: [0, 153, 0])
|
||||
totalTimeLabel = label("", constraints: 'alignx leading',
|
||||
font: timeFont, foreground: [0, 153, 0])
|
||||
|
||||
label("00:00:00", constraints: 'align trailing', font: timeFont,
|
||||
foreground: [204, 0, 0])
|
||||
currentTimeLabel = label("00:00:00", constraints: 'align trailing',
|
||||
font: timeFont, foreground: [204, 0, 0])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,6 +23,8 @@ public class Timeline implements Iterable<TimelineMarker> {
|
||||
timelineList = new TreeSet<TimelineMarker>();
|
||||
}
|
||||
|
||||
public void addMarker(TimelineMarker tm) { timelineList.add(tm); }
|
||||
|
||||
public void addMarker(Date timestamp, String name, String notes) {
|
||||
timelineList.add(new TimelineMarker(timestamp, name, notes));
|
||||
}
|
||||
|
@ -68,7 +68,14 @@ public class TimelineProperties {
|
||||
// load local timeline
|
||||
strURI = config.getProperty(LOCAL_TIMELINE_URI, "");
|
||||
if ("".equals(strURI)) {
|
||||
timelineURI = new File("timeline.default.txt").toURI();
|
||||
File defaultTimelineFile = new File("timeline.default.txt");
|
||||
try {
|
||||
if (!defaultTimelineFile.exists())
|
||||
defaultTimelineFile.createNewFile();
|
||||
} catch (IOException ioe) {
|
||||
// TODO
|
||||
}
|
||||
timelineURI = defaultTimelineFile.toURI();
|
||||
} else {
|
||||
try { timelineURI = new URI(strURI); }
|
||||
catch (URISyntaxException urise) {
|
||||
|
660
src/main/com/jdbernard/timestamper/gui/TimelineDayDisplay.java
Normal file
660
src/main/com/jdbernard/timestamper/gui/TimelineDayDisplay.java
Normal file
@ -0,0 +1,660 @@
|
||||
/* TimelineDayDisplay.java
|
||||
* Author: Jonathan Bernard - jonathan.bernard@gemalto.com
|
||||
*/
|
||||
|
||||
package jdbernard.timestamper.gui;
|
||||
|
||||
import com.jdbernard.timestamper.core.TimelineMarker;
|
||||
import com.jdbernard.timestamper.core.Timeline;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jbernard
|
||||
*/
|
||||
public class TimelineDayDisplay extends JComponent implements MouseListener,
|
||||
ChangeListener {
|
||||
|
||||
private class MarkerDisplayEntry {
|
||||
public TimelineMarker marker;
|
||||
public float relY;
|
||||
public float relHeight;
|
||||
public Rectangle2D markBounds;
|
||||
public Rectangle2D notesBounds;
|
||||
public Rectangle bounds;
|
||||
}
|
||||
|
||||
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 ArrayList<TimeLegendEntry> timeLegendLocations;
|
||||
private TimelineMarker currentMarker;
|
||||
private ArrayList<ChangeListener> changeListeners = new ArrayList<ChangeListener>();
|
||||
|
||||
private Point lastMousePress;
|
||||
|
||||
private Font markFont;// = getFont().deriveFont(Font.BOLD);
|
||||
private Font notesFont;// = getFont();
|
||||
|
||||
private Color evenTrans = new Color(0.75f, 0.75f, 0.75f, 0.4f);
|
||||
private Color evenOpaque = new Color(0.75f, 0.75f, 0.75f, 1f);
|
||||
private Color oddTrans = new Color(0.5f, 0.5f, 0.5f, 0.4f);
|
||||
private Color oddOpaque = new Color(0.5f, 0.5f, 0.5f, 1f);
|
||||
private Color selectedTrans = new Color(0.5f, 0.75f, 0.5f, 0.4f);
|
||||
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 Date rangeStartDate = new Date();
|
||||
private Date rangeEndDate = new Date();
|
||||
|
||||
public TimelineDayDisplay() {
|
||||
super();
|
||||
setDay(new Date(), false);
|
||||
addMouseListener(this);
|
||||
}
|
||||
|
||||
public TimelineDayDisplay(Calendar day) {
|
||||
setDay(day.getTime(), false);
|
||||
addMouseListener(this);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
day.add(Calendar.DAY_OF_YEAR, 1);
|
||||
rangeEndDate = day.getTime();
|
||||
|
||||
if (update) updateMarkers(getGraphics());
|
||||
}
|
||||
|
||||
public void setMarkFont(Font f) {
|
||||
markFont = f;
|
||||
}
|
||||
|
||||
public Font getMarkFont() {
|
||||
return markFont;
|
||||
}
|
||||
|
||||
public void setNotesFont(Font f) {
|
||||
notesFont = f;
|
||||
}
|
||||
|
||||
public Font getNotesFont() {
|
||||
return notesFont;
|
||||
}
|
||||
|
||||
public void setFontColor(Color f) {
|
||||
fontColor = new Color(f.getRGB());
|
||||
}
|
||||
|
||||
public Color getFontColor() {
|
||||
return fontColor;
|
||||
}
|
||||
public void setEvenColor(Color c) {
|
||||
evenOpaque = new Color(c.getRGB());
|
||||
evenTrans = new Color((float) c.getRed() / 255f,
|
||||
(float) c.getGreen() / 255f,
|
||||
(float) c.getBlue() / 255f, 0.4f);
|
||||
}
|
||||
|
||||
public Color getEvenColor() {
|
||||
return evenOpaque;
|
||||
}
|
||||
|
||||
public void setOddColor(Color c) {
|
||||
oddOpaque = new Color(c.getRGB());
|
||||
oddTrans = new Color((float) c.getRed() / 255f,
|
||||
(float) c.getGreen() / 255f,
|
||||
(float) c.getBlue() / 255f, 0.4f);
|
||||
}
|
||||
|
||||
public Color getOddColor() {
|
||||
return oddOpaque;
|
||||
}
|
||||
|
||||
public void setSelectedColor(Color c) {
|
||||
selectedOpaque = new Color(c.getRGB());
|
||||
selectedTrans = new Color((float) c.getRed() / 255f,
|
||||
(float) c.getGreen() / 255f,
|
||||
(float) c.getBlue() / 255f, 0.4f);
|
||||
}
|
||||
|
||||
public Color getSelectedColor() {
|
||||
return selectedOpaque;
|
||||
}
|
||||
|
||||
public TimelineMarker getSelectedTimelineMarker() {
|
||||
return currentMarker;
|
||||
}
|
||||
|
||||
public void addMarker(Date timestamp, String mark, String notes) {
|
||||
/*Timeline timeline = TimeStamperApp.getApplication()
|
||||
.getTimelineProperties().getTimeline();
|
||||
timeline.addMarker(timestamp, mark, notes);
|
||||
updateMarkers(getGraphics());*/
|
||||
}
|
||||
|
||||
public void deleteSelectedMarker() {
|
||||
/*Timeline timeline = TimeStamperApp.getApplication()
|
||||
.getTimelineProperties().getTimeline();
|
||||
timeline.removeMarker(currentMarker);
|
||||
updateMarkers(getGraphics());*/
|
||||
}
|
||||
|
||||
public void updateSelectedMarker(String notes) {
|
||||
currentMarker.setNotes(notes);
|
||||
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()
|
||||
.getTimelineProperties().getTimeline();
|
||||
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();
|
||||
|
||||
// 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
|
||||
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<TimelineMarker> itr = timeline.iterator();
|
||||
|
||||
while (!itr.next().equals(tm));
|
||||
|
||||
ArrayList<TimelineMarker> markers = new ArrayList<TimelineMarker>();
|
||||
while (rangeEndDate.after(tm.getTimestamp())) {
|
||||
markers.add(tm);
|
||||
if (itr.hasNext()) tm = itr.next();
|
||||
else break;
|
||||
}
|
||||
|
||||
markers.add(tm);
|
||||
|
||||
for (int i = 0; i < markers.size() - 1; i++) {
|
||||
MarkerDisplayEntry markerEntry = new MarkerDisplayEntry();
|
||||
|
||||
markerEntry.marker = markers.get(i);
|
||||
|
||||
// set string bounds
|
||||
markerEntry.markBounds = getFontMetrics(markFont)
|
||||
.getStringBounds(markers.get(i).getMark(), g);
|
||||
markerEntry.notesBounds = getFontMetrics(notesFont)
|
||||
.getStringBounds(markers.get(i).getNotes(), g);
|
||||
|
||||
// calculate upper bound
|
||||
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 {
|
||||
// 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) && 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()
|
||||
- rangeStartDate.getTime();
|
||||
else if (i == markers.size() - 2)
|
||||
// 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()
|
||||
- markerEntry.marker.getTimestamp().getTime();
|
||||
markerEntry.relHeight /= rangeDiff;
|
||||
|
||||
markerEntries.add(markerEntry);
|
||||
}
|
||||
repaint();*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
removeAll();
|
||||
|
||||
if (markerEntries == null) updateMarkers(g);
|
||||
|
||||
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 hourHeight = canvasBounds.getHeight() / 24.0;
|
||||
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
Rectangle2D stringBounds = getFontMetrics(getFont()).getStringBounds("00:00 ", g);
|
||||
|
||||
// draw hour lines
|
||||
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)));
|
||||
|
||||
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++) {
|
||||
|
||||
MarkerDisplayEntry curEntry = markerEntries.get(i);
|
||||
|
||||
Rectangle2D markBounds;
|
||||
Rectangle2D notesBounds;
|
||||
|
||||
boolean selected = curEntry.marker.equals(currentMarker);
|
||||
|
||||
// if i == 0, this is the default
|
||||
curEntry.bounds = new Rectangle();
|
||||
curEntry.bounds.y = 3;
|
||||
curEntry.bounds.x = canvasBounds.x + (int) stringBounds.getWidth() + 5;
|
||||
curEntry.bounds.height = 1;
|
||||
curEntry.bounds.width = canvasBounds.width - (int) stringBounds.getWidth() - 8;
|
||||
|
||||
double relTime;
|
||||
|
||||
// calculate upper bound
|
||||
curEntry.bounds.y = (int) Math.round(curEntry.relY
|
||||
* canvasBounds.getHeight());
|
||||
|
||||
if (i == 0) curEntry.bounds.y += 3;
|
||||
|
||||
// calculate lower bound
|
||||
curEntry.bounds.height = (int) Math.round(curEntry.relHeight
|
||||
* canvasBounds.getHeight());
|
||||
|
||||
if (i ==0) curEntry.bounds.height -= 6;
|
||||
else curEntry.bounds.height -= 3;
|
||||
|
||||
// draw box
|
||||
if (selected) g.setColor(selectedTrans);
|
||||
else g.setColor((i % 2 == 0 ? evenTrans : oddTrans));
|
||||
g.fillRect(curEntry.bounds.x, curEntry.bounds.y, curEntry.bounds.width, curEntry.bounds.height);
|
||||
|
||||
if (selected) g.setColor(selectedOpaque);
|
||||
else g2d.setColor((i % 2 == 0 ? evenOpaque : oddOpaque));
|
||||
g2d.setStroke(new BasicStroke(3f));
|
||||
g2d.drawRect(curEntry.bounds.x, curEntry.bounds.y, curEntry.bounds.width, curEntry.bounds.height);
|
||||
|
||||
// draw timestamp name
|
||||
markBounds = (Rectangle2D) curEntry.markBounds.clone();
|
||||
markBounds.setRect(curEntry.bounds.x + 3,
|
||||
curEntry.bounds.y + stringBounds.getHeight(),
|
||||
markBounds.getWidth(), markBounds.getHeight());
|
||||
|
||||
g.setColor(fontColor);
|
||||
g.setFont(markFont);
|
||||
g.drawString(curEntry.marker.getMark(),
|
||||
(int) markBounds.getX(), (int) markBounds.getY());
|
||||
|
||||
// draw notes
|
||||
notesBounds = (Rectangle2D) curEntry.notesBounds.clone();
|
||||
notesBounds.setRect(curEntry.bounds.x + 6,
|
||||
curEntry.bounds.y + stringBounds.getHeight() + markBounds.getHeight(),
|
||||
notesBounds.getWidth(), notesBounds.getHeight());
|
||||
|
||||
if (curEntry.bounds.contains(notesBounds)) {
|
||||
g.setFont(notesFont);
|
||||
g.drawString(curEntry.marker.getNotes(),
|
||||
(int) notesBounds.getX(), (int) notesBounds.getY());
|
||||
}
|
||||
}
|
||||
|
||||
g.setColor(Color.BLACK);
|
||||
g.drawRect(canvasBounds.x, canvasBounds.y, canvasBounds.width, canvasBounds.height);
|
||||
}
|
||||
|
||||
public void addChangeListener(ChangeListener cl) {
|
||||
changeListeners.add(cl);
|
||||
}
|
||||
|
||||
public boolean removeChangeListener(ChangeListener cl) {
|
||||
return changeListeners.remove(cl);
|
||||
}
|
||||
|
||||
private void fireChangeEvent() {
|
||||
for (ChangeListener cl : changeListeners)
|
||||
cl.stateChanged(new ChangeEvent(this));
|
||||
}
|
||||
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
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;
|
||||
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) {
|
||||
}
|
||||
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void stateChanged(ChangeEvent ce) {
|
||||
updateMarkers(getGraphics());
|
||||
repaint();
|
||||
}
|
||||
|
||||
}
|
10
test/integration/PunchcardDialogTests.groovy
Normal file
10
test/integration/PunchcardDialogTests.groovy
Normal file
@ -0,0 +1,10 @@
|
||||
import griffon.util.IGriffonApplication
|
||||
|
||||
class PunchcardDialogTests extends GroovyTestCase {
|
||||
|
||||
IGriffonApplication app
|
||||
|
||||
void testSomething() {
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user