Finished catching up to 1.7 functionality.
This commit is contained in:
parent
cee6de1147
commit
6d212ea771
griffon-app
conf
controllers/com/jdbernard/timestamper
lifecycle
models/com/jdbernard/timestamper
resources
delete-marker.pnglog4j.propertiesnew-marker.pngnext-day.pngnext-week.pngprevious-day.pngprevious-week.png
views/com/jdbernard/timestamper
lib
src/main/com/jdbernard
stacktrace.log@ -45,8 +45,8 @@ environments {
|
|||||||
signingkey {
|
signingkey {
|
||||||
params {
|
params {
|
||||||
sigfile = 'GRIFFON'
|
sigfile = 'GRIFFON'
|
||||||
keystore = 'CHANGE ME'
|
keystore = 'keystore.jks'
|
||||||
alias = 'CHAMGE ME'
|
alias = 'timestamper'
|
||||||
// NOTE: for production keys it is more secure to rely on key prompting
|
// NOTE: for production keys it is more secure to rely on key prompting
|
||||||
// no value means we will prompt //storepass = 'BadStorePassword'
|
// no value means we will prompt //storepass = 'BadStorePassword'
|
||||||
// no value means we will prompt //keypass = 'BadKeyPassword'
|
// no value means we will prompt //keypass = 'BadKeyPassword'
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
package com.jdbernard.timestamper
|
package com.jdbernard.timestamper
|
||||||
|
|
||||||
|
import java.awt.Point
|
||||||
|
|
||||||
class PunchcardDialogController {
|
class PunchcardDialogController {
|
||||||
// these will be injected by Griffon
|
// these will be injected by Griffon
|
||||||
def model
|
def model
|
||||||
def view
|
def view
|
||||||
|
|
||||||
void mvcGroupInit(Map args) {
|
void mvcGroupInit(Map args) {
|
||||||
// this method is called after model and view are injected
|
|
||||||
|
def loc = model.mainMVC.view.frame.location
|
||||||
|
Point p = new Point(0, (int) model.mainMVC.view.frame.bounds.height + 50)
|
||||||
|
p.translate((int) loc.x, (int) loc.y)
|
||||||
|
view.dialog.location = p
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
def action = { evt = null ->
|
def action = { evt = null ->
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,7 @@ class TimeStamperMainController {
|
|||||||
|
|
||||||
void mvcGroupInit(Map args) {
|
void mvcGroupInit(Map args) {
|
||||||
|
|
||||||
if (logger.isTraceEnabled())
|
logger.traceIfEnabled("Initializing TimeStamperMain MVC...")
|
||||||
logger.trace("Initializeing TimeStamperMain MVC...")
|
|
||||||
|
|
||||||
def thisMVC = ['model': model, 'view': view, 'controller': this]
|
def thisMVC = ['model': model, 'view': view, 'controller': this]
|
||||||
|
|
||||||
@ -27,6 +26,9 @@ class TimeStamperMainController {
|
|||||||
model.configFile = new File(userHomeDir, ".timestamperrc")
|
model.configFile = new File(userHomeDir, ".timestamperrc")
|
||||||
if (!model.configFile.exists()) model.configFile.createNewFile()
|
if (!model.configFile.exists()) model.configFile.createNewFile()
|
||||||
|
|
||||||
|
logger.traceIfEnabled("Reading configuration from "
|
||||||
|
+ "'${model.configFile.name}'")
|
||||||
|
|
||||||
try { model.configFile.withInputStream { prop.load(it) } }
|
try { model.configFile.withInputStream { prop.load(it) } }
|
||||||
catch (IOException ioe) {
|
catch (IOException ioe) {
|
||||||
logger.error('Unable to load configuration', ioe)
|
logger.error('Unable to load configuration', ioe)
|
||||||
@ -40,27 +42,41 @@ class TimeStamperMainController {
|
|||||||
lastUsed = 'timeline.default.properties'
|
lastUsed = 'timeline.default.properties'
|
||||||
model.config.setProperty('lastUsed', lastUsed)
|
model.config.setProperty('lastUsed', lastUsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.traceIfEnabled("Reading Timeline properties from '${lastUsed}'")
|
||||||
|
|
||||||
File propertyFile = new File(lastUsed)
|
File propertyFile = new File(lastUsed)
|
||||||
if (!propertyFile.exists()) propertyFile.createNewFile()
|
if (!propertyFile.exists()) propertyFile.createNewFile()
|
||||||
|
|
||||||
model.timelineProperties = new TimelineProperties(propertyFile)
|
load(propertyFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
def load = { File propertiesFile ->
|
||||||
|
try {
|
||||||
|
model.config.setProperty('lastUsed', propertiesFile.canonicalPath)
|
||||||
|
} catch (IOException ioe) { logger.error(ioe) }
|
||||||
|
|
||||||
|
// load the properties file
|
||||||
|
model.timelineProperties = new TimelineProperties(propertiesFile)
|
||||||
|
|
||||||
// load the main timeline
|
// load the main timeline
|
||||||
model.timeline = model.timelineProperties.timeline
|
model.timeline = model.timelineProperties.timeline
|
||||||
|
|
||||||
// load the last marker
|
// load the last marker
|
||||||
model.currentMarker = model.timeline.getLastMarker(new Date())
|
model.currentMarker = model.timeline.getLastMarker(new Date())
|
||||||
|
}
|
||||||
if (logger.isTraceEnabled())
|
|
||||||
logger.trace("TimeStamperMain MVC initialization complete.")
|
|
||||||
|
|
||||||
|
def saveas = {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def exitGracefully = { evt = null ->
|
def exitGracefully = { evt = null ->
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) logger.trace("Exiting gracefully.")
|
logger.traceIfEnabled("Exiting gracefully.")
|
||||||
|
|
||||||
// save config
|
// save config
|
||||||
|
logger.debugIfEnabled("Config: ${model.config}")
|
||||||
|
logger.debugIfEnabled("Storing config to file: ${model.configFile.path}")
|
||||||
try { model.configFile.withOutputStream { out ->
|
try { model.configFile.withOutputStream { out ->
|
||||||
model.config.store(out, null) } }
|
model.config.store(out, null) } }
|
||||||
catch (IOException ioe) {
|
catch (IOException ioe) {
|
||||||
@ -70,15 +86,20 @@ class TimeStamperMainController {
|
|||||||
// save timeline and properties
|
// save timeline and properties
|
||||||
model.timelineProperties.save()
|
model.timelineProperties.save()
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) logger.trace("Completed graceful shutdown.")
|
logger.traceIfEnabled("Completed graceful shutdown.")
|
||||||
|
|
||||||
app.shutdown()
|
app.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
def newTask = { mark ->
|
def newTask = { mark, notes = "No comments.", timestamp = new Date() ->
|
||||||
model.currentMarker = new TimelineMarker(new Date(), mark,
|
TimelineMarker tm = new TimelineMarker(timestamp, mark, notes)
|
||||||
"No comments.")
|
model.timeline.addMarker(tm)
|
||||||
model.timeline.addMarker(model.currentMarker)
|
model.currentMarker = model.timeline.getLastMarker(new Date())
|
||||||
|
}
|
||||||
|
|
||||||
|
def deleteTask = { marker ->
|
||||||
|
model.timeline.removeMarker(marker)
|
||||||
|
model.currentMarker = model.timeline.getLastMarker(new Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,21 @@
|
|||||||
|
|
||||||
import groovy.swing.SwingBuilder
|
import groovy.swing.SwingBuilder
|
||||||
import griffon.util.GriffonPlatformHelper
|
import griffon.util.GriffonPlatformHelper
|
||||||
|
import org.apache.log4j.Logger
|
||||||
|
|
||||||
GriffonPlatformHelper.tweakForNativePlatform(app)
|
GriffonPlatformHelper.tweakForNativePlatform(app)
|
||||||
SwingBuilder.lookAndFeel('mac', 'nimbus', 'gtk', ['metal', [boldFonts: false]])
|
SwingBuilder.lookAndFeel('system', 'nimbus', ['metal', [boldFonts: false]])
|
||||||
|
|
||||||
|
Logger.metaClass.traceIfEnabled = { String message, Throwable t = null ->
|
||||||
|
if (delegate.isTraceEnabled()) {
|
||||||
|
if (t != null) delegate.trace(message, t)
|
||||||
|
else delegate.trace(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.metaClass.debugIfEnabled = { String message, Throwable t = null ->
|
||||||
|
if (delegate.isDebugEnabled()) {
|
||||||
|
if (t != null) delegate.debug(message, t)
|
||||||
|
else delegate.debug(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -15,4 +15,5 @@
|
|||||||
* - SwingBuilder.doLater { // your code }
|
* - SwingBuilder.doLater { // your code }
|
||||||
* - SwingBuilder.edt { // your code }
|
* - SwingBuilder.edt { // your code }
|
||||||
* - SwingUtilities.invokeLater { // your code }
|
* - SwingUtilities.invokeLater { // your code }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package com.jdbernard.timestamper
|
package com.jdbernard.timestamper
|
||||||
|
|
||||||
import groovy.beans.Bindable
|
import groovy.beans.Bindable
|
||||||
|
|
||||||
class PunchcardDialogModel {
|
class PunchcardDialogModel {
|
||||||
|
Before ![]() (image error) Size: 321 B After ![]() (image error) Size: 321 B ![]() ![]() |
@ -1,13 +1,12 @@
|
|||||||
log4j.rootLogger=ERROR, stdout
|
log4j.rootLogger=trace, stdout
|
||||||
log4j.logger.com.jdbernard.timestamper=TRACE
|
|
||||||
|
# set up stdout
|
||||||
# set up stdout
|
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
log4j.appender.stdout.layout.ConversionPattern=[%t] %-5p - %m%n
|
||||||
log4j.appender.stdout.layout.ConversionPattern=[%t] %-5p - %m%n
|
|
||||||
|
# set up fileout
|
||||||
# set up fileout
|
log4j.appender.fileout=org.apache.log4j.FileAppender
|
||||||
log4j.appender.fileout=org.apache.log4j.FileAppender
|
|
||||||
log4j.appender.fileout.File=TimeStamper.log
|
log4j.appender.fileout.File=TimeStamper.log
|
||||||
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
|
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
|
||||||
log4j.appender.fileout.layout.ConversionPattern=%5p [%t] - %m%n
|
log4j.appender.fileout.layout.ConversionPattern=%5p [%t] - %m%n
|
||||||
|
Before ![]() (image error) Size: 345 B After ![]() (image error) Size: 345 B ![]() ![]() |
Before ![]() (image error) Size: 660 B After ![]() (image error) Size: 660 B ![]() ![]() |
Before ![]() (image error) Size: 782 B After ![]() (image error) Size: 782 B ![]() ![]() |
Before ![]() (image error) Size: 636 B After ![]() (image error) Size: 636 B ![]() ![]() |
Before ![]() (image error) Size: 764 B After ![]() (image error) Size: 764 B ![]() ![]() |
@ -5,6 +5,8 @@ import java.awt.Point
|
|||||||
import java.awt.Rectangle
|
import java.awt.Rectangle
|
||||||
import java.awt.Toolkit
|
import java.awt.Toolkit
|
||||||
import javax.swing.BoxLayout
|
import javax.swing.BoxLayout
|
||||||
|
import javax.swing.JDialog
|
||||||
|
import com.jdbernard.GUIUtil
|
||||||
import net.miginfocom.swing.MigLayout
|
import net.miginfocom.swing.MigLayout
|
||||||
|
|
||||||
Point mousePressRelativeToDialog
|
Point mousePressRelativeToDialog
|
||||||
@ -26,11 +28,13 @@ mouseDragged = { evt ->
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog = dialog(
|
dialog = dialog(new JDialog(model.mainMVC.view.frame),
|
||||||
title: 'Notes',
|
title: 'Notes',
|
||||||
modal: false,
|
modal: false,
|
||||||
undecorated: true,
|
undecorated: true,
|
||||||
minimumSize: [325, 200],
|
minimumSize: [325, 200],
|
||||||
|
mousePressed: mousePressed,
|
||||||
|
mouseDragged: mouseDragged,
|
||||||
iconImage: imageIcon('/16-em-pencil.png').image,
|
iconImage: imageIcon('/16-em-pencil.png').image,
|
||||||
iconImages: [imageIcon('/16-em-pencil.png').image],
|
iconImages: [imageIcon('/16-em-pencil.png').image],
|
||||||
location: bind(source: model.mainMVC.model,
|
location: bind(source: model.mainMVC.model,
|
||||||
@ -42,18 +46,21 @@ dialog = dialog(
|
|||||||
return p
|
return p
|
||||||
} else return dialog.location })
|
} else return dialog.location })
|
||||||
) {
|
) {
|
||||||
|
logger.traceIfEnabled('Building NotesDialog GUI')
|
||||||
panel(
|
panel(
|
||||||
border:lineBorder(color: Color.BLACK, thickness:1, parent:true),
|
border:lineBorder(color: Color.BLACK, thickness:1, parent:true),
|
||||||
mousePressed: mousePressed,
|
layout: new MigLayout('insets 10 10 10 10, fill')
|
||||||
mouseDragged: mouseDragged,
|
|
||||||
layout: new MigLayout('insets 5 5 5 5, fill')
|
|
||||||
) {
|
) {
|
||||||
scrollPane(constraints: 'growx, growy') {
|
scrollPane(constraints: 'growx, growy') {
|
||||||
notesTextArea = textArea(lineWrap: true, columns: 20, rows: 5,
|
notesTextArea = textArea(lineWrap: true, columns: 20, rows: 5,
|
||||||
wrapStyleWord: true,
|
wrapStyleWord: true,
|
||||||
text: bind(source: model.mainMVC.model,
|
text: bind(source: model.mainMVC.model,
|
||||||
sourceProperty: 'currentMarker',
|
sourceProperty: 'currentMarker',
|
||||||
sourceValue: { model.mainMVC.model.currentMarker?.notes}))
|
sourceValue: { model.mainMVC.model.currentMarker?.notes}),
|
||||||
|
keyReleased: {
|
||||||
|
if (model.mainMVC.model.currentMarker != null)
|
||||||
|
model.mainMVC.model.currentMarker.notes =
|
||||||
|
notesTextArea.text})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,163 @@
|
|||||||
package com.jdbernard.timestamper
|
package com.jdbernard.timestamper
|
||||||
|
|
||||||
punchcardDialog = dialog(
|
import java.awt.Color
|
||||||
/* title: 'Punchcard',
|
import java.awt.Point
|
||||||
|
import java.awt.Toolkit
|
||||||
|
import java.awt.Rectangle
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import javax.swing.JDialog
|
||||||
|
import java.util.Calendar
|
||||||
|
import javax.swing.SwingConstants
|
||||||
|
import com.jdbernard.GUIUtil
|
||||||
|
import com.jdbernard.timestamper.gui.TimelineDayDisplay
|
||||||
|
import com.toedter.calendar.JDateChooser
|
||||||
|
import net.miginfocom.swing.MigLayout
|
||||||
|
|
||||||
|
Point mousePressRelativeToDialog
|
||||||
|
Point offsetFromMainFrame = new Point(0,0)
|
||||||
|
boolean coupledToMainFrame = false
|
||||||
|
|
||||||
|
dateFormatter = new SimpleDateFormat("EEE MMM dd")
|
||||||
|
|
||||||
|
mousePressed = { evt -> mousePressRelativeToDialog = evt?.point }
|
||||||
|
|
||||||
|
mouseDragged = { evt ->
|
||||||
|
GUIUtil.componentDragged(dialog, evt, mousePressRelativeToDialog,
|
||||||
|
new Rectangle(Toolkit.defaultToolkit.screenSize),
|
||||||
|
model.mainMVC.view.frame.bounds)
|
||||||
|
|
||||||
|
offsetFromMainFrame = GUIUtil.calculateOffset(
|
||||||
|
model.mainMVC.view.frame, dialog)
|
||||||
|
|
||||||
|
coupledToMainFrame = GUIUtil.componentsCoupled(dialog,
|
||||||
|
model.mainMVC.view.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog = dialog(new JDialog(model.mainMVC.view.frame),
|
||||||
|
title: 'Punchcard',
|
||||||
modal: false,
|
modal: false,
|
||||||
undecorated: true,
|
undecorated: true,
|
||||||
iconImage: iconImage('/16-file-archive.png').image,
|
mousePressed: mousePressed,
|
||||||
iconImages: [iconImage('/16-file-archive.png').image],
|
mouseDragged: mouseDragged,
|
||||||
minimumSize: [325, 600]*/
|
iconImage: imageIcon('/16-file-archive.png').image,
|
||||||
|
iconImages: [imageIcon('/16-file-archive.png').image],
|
||||||
|
minimumSize: [450, 500],
|
||||||
|
location: bind(source: model.mainMVC.model,
|
||||||
|
sourceProperty: 'absoluteLocation',
|
||||||
|
converter: { loc ->
|
||||||
|
if (coupledToMainFrame) {
|
||||||
|
Point p = new Point(offsetFromMainFrame)
|
||||||
|
p.translate((int) loc.x, (int) loc.y)
|
||||||
|
return p
|
||||||
|
} else return dialog.location })
|
||||||
) {
|
) {
|
||||||
|
logger.traceIfEnabled('Building PunchcardDialog GUI')
|
||||||
|
panel(
|
||||||
|
border:lineBorder(color: Color.BLACK, thickness:1, parent:true),
|
||||||
|
layout: new MigLayout('fill, insets 10 10 10 10',
|
||||||
|
'[grow]', '[grow]10[grow 0]')
|
||||||
|
) {
|
||||||
|
dayDisplay = widget(constraints: 'grow, gp 200',
|
||||||
|
new TimelineDayDisplay(model.mainMVC.model.timeline),
|
||||||
|
timeline: bind(source: model.mainMVC.model,
|
||||||
|
sourceProperty: 'timeline'),
|
||||||
|
stateChanged: bind(source: model.mainMVC.model,
|
||||||
|
sourceProperty: 'currentMarker'))
|
||||||
|
|
||||||
|
panel(
|
||||||
|
border: lineBorder(color: Color.BLACK, thickness: 1),
|
||||||
|
constraints: 'growx, growy 0, newline, bottom',
|
||||||
|
layout: new MigLayout('fill, insets 0 10 0 10',
|
||||||
|
'[grow]10[grow 0]', '[][][grow 0]')
|
||||||
|
) {
|
||||||
|
dateChooser = widget(new JDateChooser(),
|
||||||
|
constraints: 'growx, gaptop 10',
|
||||||
|
dateFormatString: 'MMM d, yyyy HH:mm',
|
||||||
|
date: bind(source: dayDisplay,
|
||||||
|
sourceProperty: 'selectedTimelineMarker',
|
||||||
|
converter: { it?.timestamp }))
|
||||||
|
|
||||||
|
panel(constraints: 'spany 3, wrap, al trailing',
|
||||||
|
layout: new MigLayout('insets 0', '[]0[]0[]0[]0[]',
|
||||||
|
'[]0[]0[]0[]0[]')
|
||||||
|
) {
|
||||||
|
label(background: [255, 255, 153],
|
||||||
|
constraints: 'growx, spanx 5, wrap',
|
||||||
|
horizontalAlignment: SwingConstants.CENTER,
|
||||||
|
opaque: true,
|
||||||
|
text: bind(source: dayDisplay,
|
||||||
|
sourceProperty: 'rangeStart',
|
||||||
|
converter: { dateFormatter.format(it) }))
|
||||||
|
|
||||||
|
Calendar calendar = Calendar.getInstance()
|
||||||
|
button(icon: imageIcon('/previous-week.png'),
|
||||||
|
border: emptyBorder(4),
|
||||||
|
actionPerformed: {
|
||||||
|
calendar.time = dayDisplay.rangeStart
|
||||||
|
calendar.add(Calendar.WEEK_OF_YEAR, -1)
|
||||||
|
dayDisplay.day = calendar.time })
|
||||||
|
button(icon: imageIcon('/previous-day.png'),
|
||||||
|
border: emptyBorder(4),
|
||||||
|
actionPerformed: {
|
||||||
|
calendar.time = dayDisplay.rangeStart
|
||||||
|
calendar.add(Calendar.DAY_OF_YEAR, -1)
|
||||||
|
dayDisplay.day = calendar.time })
|
||||||
|
button(text: 'Today',
|
||||||
|
border: emptyBorder(4),
|
||||||
|
actionPerformed: {
|
||||||
|
calendar = Calendar.getInstance()
|
||||||
|
dayDisplay.day = calendar.time })
|
||||||
|
button(icon: imageIcon('/next-day.png'),
|
||||||
|
border: emptyBorder(4),
|
||||||
|
actionPerformed: {
|
||||||
|
calendar.time = dayDisplay.rangeStart
|
||||||
|
calendar.add(Calendar.DAY_OF_YEAR, 1)
|
||||||
|
dayDisplay.day = calendar.time })
|
||||||
|
button(icon: imageIcon('/next-week.png'),
|
||||||
|
constraints: 'wrap',
|
||||||
|
border: emptyBorder(4),
|
||||||
|
actionPerformed: {
|
||||||
|
calendar.time = dayDisplay.rangeStart
|
||||||
|
calendar.add(Calendar.WEEK_OF_YEAR, 1)
|
||||||
|
dayDisplay.day = calendar.time })
|
||||||
|
|
||||||
|
button(text: 'New Marker', icon: imageIcon('/new-marker.png'),
|
||||||
|
constraints: 'growx, spanx 5, wrap',
|
||||||
|
horizontalAlignment: SwingConstants.CENTER,
|
||||||
|
actionPerformed: {
|
||||||
|
model.mainMVC.controller.newTask(markTextField.text,
|
||||||
|
notesTextField.text, dateChooser.date) })
|
||||||
|
button(text: 'Delete Marker',
|
||||||
|
icon: imageIcon('/delete-marker.png'),
|
||||||
|
constraints: 'growx, spanx 5, wrap',
|
||||||
|
horizontalAlignment: SwingConstants.CENTER,
|
||||||
|
actionPerformed: {
|
||||||
|
model.mainMVC.controller.deleteTask(
|
||||||
|
dayDisplay.selectedTimelineMarker) })
|
||||||
|
button(text: 'Apply Changes',
|
||||||
|
icon: imageIcon('/document-save-16x16.png'),
|
||||||
|
constraints: 'growx, spanx 5',
|
||||||
|
horizontalAlignment: SwingConstants.CENTER,
|
||||||
|
actionPerformed: {
|
||||||
|
Date d = dateChooser.date
|
||||||
|
String m = markTextField.text
|
||||||
|
String n = notesTextField.text
|
||||||
|
model.mainMVC.controller.deleteTask(
|
||||||
|
dayDisplay.selectedTimelineMarker)
|
||||||
|
model.mainMVC.controller.newTask(m, n, d) })
|
||||||
|
}
|
||||||
|
|
||||||
|
markTextField = textField(constraints: 'growx, wrap',
|
||||||
|
text: bind(source: dayDisplay,
|
||||||
|
sourceProperty: 'selectedTimelineMarker',
|
||||||
|
converter: { it?.mark }))
|
||||||
|
scrollPane(constraints: 'growx, gapbottom 10',
|
||||||
|
minimumSize: [0, 50]) {
|
||||||
|
notesTextField = textArea( wrapStyleWord: true, lineWrap: true,
|
||||||
|
text: bind(source: dayDisplay,
|
||||||
|
sourceProperty: 'selectedTimelineMarker',
|
||||||
|
converter: { it?.notes }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,11 @@ import java.awt.Rectangle
|
|||||||
import java.awt.Toolkit
|
import java.awt.Toolkit
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import javax.swing.BoxLayout
|
import javax.swing.BoxLayout
|
||||||
|
import javax.swing.JDialog
|
||||||
|
import javax.swing.JFileChooser
|
||||||
|
import javax.swing.SwingConstants
|
||||||
import javax.swing.Timer
|
import javax.swing.Timer
|
||||||
|
import com.jdbernard.GUIUtil
|
||||||
import com.jdbernard.timestamper.core.Timeline
|
import com.jdbernard.timestamper.core.Timeline
|
||||||
import net.miginfocom.swing.MigLayout
|
import net.miginfocom.swing.MigLayout
|
||||||
|
|
||||||
@ -30,16 +34,12 @@ taskTextFieldChanged = { evt = null ->
|
|||||||
taskTextField.font = taskThinFont
|
taskTextField.font = taskThinFont
|
||||||
}
|
}
|
||||||
|
|
||||||
showToolsMenu = { evt = null ->
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
showNotes = { evt = null ->
|
showNotes = { evt = null ->
|
||||||
model.notesDialogMVC.view.dialog.visible = notesVisibleButton.selected
|
model.notesDialogMVC.view.dialog.visible = notesVisibleButton.selected
|
||||||
}
|
}
|
||||||
|
|
||||||
showPunchcard = { evt = null ->
|
showPunchcard = { evt = null ->
|
||||||
|
model.punchcardDialogMVC.view.dialog.visible = punchcardVisibleButton.selected
|
||||||
}
|
}
|
||||||
|
|
||||||
mousePressed = { evt = null ->
|
mousePressed = { evt = null ->
|
||||||
@ -80,17 +80,34 @@ updateTimer = new Timer(1000, action(name: 'GUI Refresh', closure: {
|
|||||||
|
|
||||||
updateTimer.start()
|
updateTimer.start()
|
||||||
|
|
||||||
|
optionsMenu = popupMenu() {
|
||||||
|
menuItem(icon: imageIcon('/document-save-16x16.png'), text: 'Save Timeline',
|
||||||
|
actionPerformed: { model.timelineProperties.save() })
|
||||||
|
menuItem(icon: imageIcon('/document-save-as-16x16.png'),
|
||||||
|
text: 'Save a new copy...', actionPerformed: controller.&saveas)
|
||||||
|
menuItem(icon: imageIcon('/document-open-16x16.png'),
|
||||||
|
text: 'Load Timeline...', actionPerformed: {
|
||||||
|
if (fileDialog.showOpenDialog(frame) ==
|
||||||
|
JFileChooser.APPROVE_OPTION)
|
||||||
|
controller.load(fileDialog.selectedFile) })
|
||||||
|
aboutMenuItem = checkBoxMenuItem(text: 'About...',
|
||||||
|
actionPerformed: { aboutDialog.visible = aboutMenuItem.selected })
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDialog = fileChooser();
|
||||||
|
|
||||||
frame = application(title:'TimeStamper',
|
frame = application(title:'TimeStamper',
|
||||||
//size:[320,480],
|
location:[50,50],
|
||||||
pack:true,
|
|
||||||
//location:[50,50],
|
|
||||||
undecorated:true,
|
|
||||||
locationByPlatform:true,
|
locationByPlatform:true,
|
||||||
|
minimumSize: [325, 0],
|
||||||
|
pack:true,
|
||||||
|
undecorated:true,
|
||||||
iconImage: imageIcon('/appointment-new-32x32.png').image,
|
iconImage: imageIcon('/appointment-new-32x32.png').image,
|
||||||
iconImages: [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 }
|
componentMoved: { evt -> model.absoluteLocation = frame.location }
|
||||||
) {
|
) {
|
||||||
|
logger.traceIfEnabled('Building TimeStamperMain GUI')
|
||||||
panel(
|
panel(
|
||||||
border:lineBorder(color:Color.BLACK, thickness:1, parent:true),
|
border:lineBorder(color:Color.BLACK, thickness:1, parent:true),
|
||||||
layout: new MigLayout('insets 0 5 0 0, fill','', '[]0[]0[]'),
|
layout: new MigLayout('insets 0 5 0 0, fill','', '[]0[]0[]'),
|
||||||
@ -110,25 +127,29 @@ frame = application(title:'TimeStamper',
|
|||||||
|
|
||||||
panel(constraints: 'alignx trailing, aligny top, wrap') {
|
panel(constraints: 'alignx trailing, aligny top, wrap') {
|
||||||
boxLayout(axis: BoxLayout.X_AXIS)
|
boxLayout(axis: BoxLayout.X_AXIS)
|
||||||
button(actionPerformed: showToolsMenu,
|
button(mouseClicked: { evt ->
|
||||||
|
optionsMenu.show(evt.component, evt.x, evt.y) },
|
||||||
icon: imageIcon('/16-tool-a.png'),
|
icon: imageIcon('/16-tool-a.png'),
|
||||||
rolloverIcon: imageIcon('/16-tool-a-hover.png'),
|
rolloverIcon: imageIcon('/16-tool-a-hover.png'),
|
||||||
border: emptyBorder(0),
|
border: emptyBorder(0),
|
||||||
contentAreaFilled: false,
|
contentAreaFilled: false,
|
||||||
hideActionText: true)
|
hideActionText: true,
|
||||||
|
toolTipText: 'Options Menu')
|
||||||
button(actionPerformed: controller.&exitGracefully,
|
button(actionPerformed: controller.&exitGracefully,
|
||||||
icon: imageIcon('/16-em-cross.png'),
|
icon: imageIcon('/16-em-cross.png'),
|
||||||
rolloverIcon: imageIcon('/16-em-cross-hover.png'),
|
rolloverIcon: imageIcon('/16-em-cross-hover.png'),
|
||||||
border: emptyBorder(0),
|
border: emptyBorder(0),
|
||||||
contentAreaFilled: false,
|
contentAreaFilled: false,
|
||||||
hideActionText: true)
|
hideActionText: true,
|
||||||
|
toolTipText: 'Close Application')
|
||||||
}
|
}
|
||||||
|
|
||||||
taskTextField = textField("Task name",
|
taskTextField = textField("Task name",
|
||||||
constraints: "growx, span 2, w 250::",
|
constraints: "growx, span 2, w 250::",
|
||||||
keyReleased: taskTextFieldChanged,
|
keyReleased: taskTextFieldChanged,
|
||||||
|
toolTipText: 'The current task',
|
||||||
text: bind(source: model, sourceProperty: 'currentMarker',
|
text: bind(source: model, sourceProperty: 'currentMarker',
|
||||||
sourceValue: { model.currentMarker.mark }))
|
sourceValue: { model.currentMarker?.mark }))
|
||||||
|
|
||||||
taskThinFont = taskTextField.font
|
taskThinFont = taskTextField.font
|
||||||
taskBoldFont = taskTextField.font.deriveFont(Font.BOLD)
|
taskBoldFont = taskTextField.font.deriveFont(Font.BOLD)
|
||||||
@ -139,12 +160,14 @@ frame = application(title:'TimeStamper',
|
|||||||
actionPerformed: showNotes,
|
actionPerformed: showNotes,
|
||||||
icon: imageIcon('/16-em-pencil.png'),
|
icon: imageIcon('/16-em-pencil.png'),
|
||||||
hideActionText: true,
|
hideActionText: true,
|
||||||
border: emptyBorder(4))
|
border: emptyBorder(4),
|
||||||
|
toolTipText: 'Show/hide task notes.')
|
||||||
punchcardVisibleButton = toggleButton(
|
punchcardVisibleButton = toggleButton(
|
||||||
actionPerformed: showPunchcard,
|
actionPerformed: showPunchcard,
|
||||||
icon: imageIcon('/16-file-archive.png'),
|
icon: imageIcon('/16-file-archive.png'),
|
||||||
hideActionText: true,
|
hideActionText: true,
|
||||||
border: emptyBorder(4))
|
border: emptyBorder(4),
|
||||||
|
toolTipText: 'Show/hide the timeline display.')
|
||||||
}
|
}
|
||||||
|
|
||||||
totalTimeLabel = label("", constraints: 'alignx leading',
|
totalTimeLabel = label("", constraints: 'alignx leading',
|
||||||
@ -155,4 +178,24 @@ frame = application(title:'TimeStamper',
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aboutDialog = dialog(new JDialog(frame),
|
||||||
|
visible: false,
|
||||||
|
locationRelativeTo: null,
|
||||||
|
pack: true,
|
||||||
|
undecorated: true,
|
||||||
|
title: "About TimeStamper v" + app.applicationProperties.'app.version'
|
||||||
|
) {
|
||||||
|
panel(layout: new MigLayout('fill'),
|
||||||
|
border: lineBorder(color: Color.BLACK, thickness: 1)) {
|
||||||
|
|
||||||
|
label(font: new Font(Font.SANS_SERIF, Font.PLAIN, 18),
|
||||||
|
text: "TimeStamper", constraints: 'growx, wrap',
|
||||||
|
horizontalAlignment: SwingConstants.CENTER)
|
||||||
|
label(text: "version " + app.applicationProperties.'app.version'
|
||||||
|
+ " by Jonathan Bernard", constraints: 'growx, wrap',
|
||||||
|
horizontalAlignment: SwingConstants.CENTER)
|
||||||
|
textField(text: 'http://www.jdbernard.com/timestamper',
|
||||||
|
constraints: 'growx', foreground: [0,0,200], editable: false,
|
||||||
|
horizontalAlignment: SwingConstants.CENTER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BIN
keystore.jks
Normal file
BIN
keystore.jks
Normal file
Binary file not shown.
BIN
lib/jcalendar-1.3.2.jar
Executable file
BIN
lib/jcalendar-1.3.2.jar
Executable file
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
package com.jdbernard.timestamper
|
package com.jdbernard
|
||||||
|
|
||||||
import java.awt.Point
|
import java.awt.Point
|
||||||
import java.awt.Rectangle
|
import java.awt.Rectangle
|
@ -214,14 +214,16 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TimelineDayDisplay() {
|
||||||
|
this(null, Calendar.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
public TimelineDayDisplay(Timeline t) {
|
public TimelineDayDisplay(Timeline t) {
|
||||||
super();
|
this(t, Calendar.getInstance());
|
||||||
setDay(new Date(), false);
|
|
||||||
this.timeline = t;
|
|
||||||
addMouseListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimelineDayDisplay(Timeline t, Calendar day) {
|
public TimelineDayDisplay(Timeline t, Calendar day) {
|
||||||
|
super();
|
||||||
setDay(day.getTime(), false);
|
setDay(day.getTime(), false);
|
||||||
addMouseListener(this);
|
addMouseListener(this);
|
||||||
this.timeline = t;
|
this.timeline = t;
|
||||||
@ -232,16 +234,31 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
pcs.addPropertyChangeListener(l);
|
pcs.addPropertyChangeListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addPropertyChangeListener(String propertyName,
|
||||||
|
PropertyChangeListener l) {
|
||||||
|
pcs.addPropertyChangeListener(propertyName, l);
|
||||||
|
}
|
||||||
|
|
||||||
public void removePropertyChangeListener(PropertyChangeListener l) {
|
public void removePropertyChangeListener(PropertyChangeListener l) {
|
||||||
pcs.removePropertyChangeListener(l);
|
pcs.removePropertyChangeListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Timeline getTimeline() { return timeline; }
|
||||||
|
|
||||||
|
public void setTimeline(Timeline t) {
|
||||||
|
if (timeline == t) return;
|
||||||
|
pcs.firePropertyChange("timeline", timeline, timeline = t);
|
||||||
|
updateMarkers(getGraphics());
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the range for the visible timeline segment.
|
* Set the range for the visible timeline segment.
|
||||||
* @param start The beginning of the desired timeline segment.
|
* @param start The beginning of the desired timeline segment.
|
||||||
* @param end The end of the desired timeline segment.
|
* @param end The end of the desired timeline segment.
|
||||||
*/
|
*/
|
||||||
public void setDisplayInterval(Date start, Date end) {
|
public void setDisplayInterval(Date start, Date end) {
|
||||||
|
if (start == rangeStartDate && end == rangeEndDate) return;
|
||||||
pcs.firePropertyChange("rangeStart", rangeStartDate, rangeStartDate = start);
|
pcs.firePropertyChange("rangeStart", rangeStartDate, rangeStartDate = start);
|
||||||
pcs.firePropertyChange("rangeEnd", rangeEndDate, rangeEndDate = end);
|
pcs.firePropertyChange("rangeEnd", rangeEndDate, rangeEndDate = end);
|
||||||
}
|
}
|
||||||
@ -299,31 +316,39 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
* @param f The font to use.
|
* @param f The font to use.
|
||||||
*/
|
*/
|
||||||
public void setMarkFont(Font f) {
|
public void setMarkFont(Font f) {
|
||||||
|
if (markFont == f) return;
|
||||||
pcs.firePropertyChange("markFont", markFont, markFont = f);
|
pcs.firePropertyChange("markFont", markFont, markFont = f);
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Font getMarkFont() { return markFont; }
|
public Font getMarkFont() { return markFont; }
|
||||||
|
|
||||||
public void setNotesFont(Font f) {
|
public void setNotesFont(Font f) {
|
||||||
|
if (notesFont == f) return;
|
||||||
pcs.firePropertyChange("notesFont", notesFont, notesFont = f);
|
pcs.firePropertyChange("notesFont", notesFont, notesFont = f);
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Font getNotesFont() { return notesFont; }
|
public Font getNotesFont() { return notesFont; }
|
||||||
|
|
||||||
public void setFontColor(Color f) {
|
public void setFontColor(Color f) {
|
||||||
|
if (fontColor == f) return;
|
||||||
pcs.firePropertyChange("fontColor", fontColor,
|
pcs.firePropertyChange("fontColor", fontColor,
|
||||||
fontColor = new Color(f.getRGB()));
|
fontColor = new Color(f.getRGB()));
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getFontColor() { return fontColor; }
|
public Color getFontColor() { return fontColor; }
|
||||||
|
|
||||||
public void setEvenColor(Color c) {
|
public void setEvenColor(Color c) {
|
||||||
|
if (evenOpaque == c) return;
|
||||||
evenTrans = new Color((float) c.getRed() / 255f,
|
evenTrans = new Color((float) c.getRed() / 255f,
|
||||||
(float) c.getGreen() / 255f,
|
(float) c.getGreen() / 255f,
|
||||||
(float) c.getBlue() / 255f, 0.4f);
|
(float) c.getBlue() / 255f, 0.4f);
|
||||||
|
|
||||||
pcs.firePropertyChange("evenColor", evenOpaque,
|
pcs.firePropertyChange("evenColor", evenOpaque,
|
||||||
evenOpaque = new Color(c.getRGB()));
|
evenOpaque = new Color(c.getRGB()));
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getEvenColor() {
|
public Color getEvenColor() {
|
||||||
@ -331,13 +356,14 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setOddColor(Color c) {
|
public void setOddColor(Color c) {
|
||||||
oddOpaque = new Color(c.getRGB());
|
if (oddOpaque == c) return;
|
||||||
oddTrans = new Color((float) c.getRed() / 255f,
|
oddTrans = new Color((float) c.getRed() / 255f,
|
||||||
(float) c.getGreen() / 255f,
|
(float) c.getGreen() / 255f,
|
||||||
(float) c.getBlue() / 255f, 0.4f);
|
(float) c.getBlue() / 255f, 0.4f);
|
||||||
|
|
||||||
pcs.firePropertyChange("oddColor", oddOpaque,
|
pcs.firePropertyChange("oddColor", oddOpaque,
|
||||||
oddOpaque = new Color(c.getRGB()));
|
oddOpaque = new Color(c.getRGB()));
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getOddColor() {
|
public Color getOddColor() {
|
||||||
@ -345,12 +371,13 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedColor(Color c) {
|
public void setSelectedColor(Color c) {
|
||||||
selectedOpaque = new Color(c.getRGB());
|
if (selectedOpaque == c) return;
|
||||||
selectedTrans = new Color((float) c.getRed() / 255f,
|
selectedTrans = new Color((float) c.getRed() / 255f,
|
||||||
(float) c.getGreen() / 255f,
|
(float) c.getGreen() / 255f,
|
||||||
(float) c.getBlue() / 255f, 0.4f);
|
(float) c.getBlue() / 255f, 0.4f);
|
||||||
pcs.firePropertyChange("selectedColor", selectedOpaque,
|
pcs.firePropertyChange("selectedColor", selectedOpaque,
|
||||||
selectedOpaque = new Color(c.getRGB()));
|
selectedOpaque = new Color(c.getRGB()));
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getSelectedColor() {
|
public Color getSelectedColor() {
|
||||||
@ -375,6 +402,8 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
*/
|
*/
|
||||||
private void updateMarkers(Graphics g) {
|
private void updateMarkers(Graphics g) {
|
||||||
|
|
||||||
|
if (timeline == null) return;
|
||||||
|
|
||||||
Insets insets = this.getInsets();
|
Insets insets = this.getInsets();
|
||||||
Rectangle bounds = this.getBounds();
|
Rectangle bounds = this.getBounds();
|
||||||
Rectangle canvasBounds = new Rectangle(insets.left, insets.top,
|
Rectangle canvasBounds = new Rectangle(insets.left, insets.top,
|
||||||
@ -498,6 +527,8 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
public void paintComponent(Graphics g) {
|
public void paintComponent(Graphics g) {
|
||||||
removeAll();
|
removeAll();
|
||||||
|
|
||||||
|
if (timeline == null) return;
|
||||||
|
|
||||||
if (markerEntries == null) updateMarkers(g);
|
if (markerEntries == null) updateMarkers(g);
|
||||||
|
|
||||||
Insets insets = this.getInsets();
|
Insets insets = this.getInsets();
|
||||||
@ -614,7 +645,8 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
|
|
||||||
// should only match one entry
|
// should only match one entry
|
||||||
if (absBounds.contains(e.getLocationOnScreen())) {
|
if (absBounds.contains(e.getLocationOnScreen())) {
|
||||||
currentMarker = markerEntry.marker;
|
pcs.firePropertyChange("selectedTimelineMarker",
|
||||||
|
currentMarker, currentMarker = markerEntry.marker);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -678,4 +710,8 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
|
|||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setStateChanged(Object o) {
|
||||||
|
stateChanged(null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user