Compare commits
192 Commits
Author | SHA1 | Date | |
---|---|---|---|
ee087d302d | |||
5b24dac2b8 | |||
b6dc3eb98c | |||
ea3f554224 | |||
111be977c6 | |||
338eab1c96 | |||
fbc9052e5a | |||
0a49df1828 | |||
e32c6358e9 | |||
|
168d7cd03f | ||
|
66c00e2473 | ||
|
3cb9bfc228 | ||
|
950f4a0b70 | ||
|
12bae10384 | ||
|
7ae584bec9 | ||
|
0da15ef102 | ||
|
bb713abeed | ||
|
3f21f62889 | ||
|
dc70b00403 | ||
|
54eb4fdafa | ||
|
59c9544ab9 | ||
|
846f505f6b | ||
|
de405f01f1 | ||
|
4a7486305c | ||
|
26c5435b5c | ||
|
839646b154 | ||
|
e8bebb49fb | ||
|
c42a3805c2 | ||
|
f4eeb91d1a | ||
|
a3c55e918e | ||
|
0278179452 | ||
|
8cc257c24e | ||
|
216b3c5303 | ||
|
774778ee55 | ||
|
98032e2b89 | ||
|
d473d74112 | ||
|
d720c6c645 | ||
|
9b357359b6 | ||
|
15750f65d4 | ||
|
bc51d17932 | ||
|
4c8d8a9f2d | ||
|
621e00deaa | ||
|
100ca8fd74 | ||
|
1e05258381 | ||
|
4992d7cac9 | ||
|
e296e87003 | ||
|
269a9b9125 | ||
|
38dc560686 | ||
|
76bf676c2c | ||
|
284a4159d1 | ||
|
e4a756baf5 | ||
|
f78cd57ec7 | ||
|
769245b690 | ||
|
f9e700f27b | ||
|
00a5252542 | ||
|
5dff4de089 | ||
|
2650fca7f1 | ||
|
10677e5705 | ||
|
d076b739f5 | ||
|
5b2ecf7b65 | ||
|
cda3544cac | ||
|
b01421d45d | ||
|
f3ef7db088 | ||
|
81503112a8 | ||
|
1fff94b622 | ||
|
98071f270d | ||
|
602bdca7e3 | ||
|
cd182d54c3 | ||
|
3999e736be | ||
|
658091e947 | ||
|
7d11112226 | ||
|
8bb50c058d | ||
|
99d04935cb | ||
|
b5eadd6fc4 | ||
|
bc364b2ebd | ||
|
89336e844f | ||
|
76083b5972 | ||
|
cf42d133f6 | ||
|
72ef1ac277 | ||
|
2cc17b85f1 | ||
|
65a9a517f9 | ||
|
808492154d | ||
|
74d8a7f015 | ||
|
036177cfed | ||
|
b22c10c6c2 | ||
|
850edf9e25 | ||
|
1161f6b752 | ||
|
65dbab9c13 | ||
|
06556020d4 | ||
|
dd3387a0f1 | ||
|
cf5153c90b | ||
|
ac80d57cd3 | ||
|
3d89477f7e | ||
|
ebd4b117c9 | ||
|
9b3f587974 | ||
|
77fc3f2a07 | ||
|
f4fe5559b1 | ||
|
302bc9ccdd | ||
|
9025a2f8f6 | ||
|
aec172536e | ||
|
5b267ec67b | ||
|
17c5b9cbd1 | ||
|
e10ec26a21 | ||
|
d0aab2d0c6 | ||
|
b6d05665bf | ||
|
81fb56022c | ||
|
76f32ac834 | ||
|
4d124ffeba | ||
|
1b981b206c | ||
|
b59b628cb5 | ||
|
3c3d553768 | ||
|
39c3b83d3f | ||
|
1b1e31059b | ||
|
c185c8cd81 | ||
|
e6fcf736bb | ||
|
122a3bd1e3 | ||
|
dfab257a12 | ||
|
348c73a36f | ||
|
efab46f167 | ||
|
439a080f14 | ||
|
4492f87a39 | ||
|
1a3c0d5c4e | ||
|
ddc12cec64 | ||
|
19fc37c772 | ||
|
d743635865 | ||
|
6197363508 | ||
|
9c645e9a88 | ||
|
6bdf0ab526 | ||
|
dcd836ba8e | ||
|
afca12ecc9 | ||
|
0642c18a6e | ||
|
5809ed3959 | ||
|
6e2e0d5f00 | ||
|
1575e25898 | ||
|
6fe9184c8e | ||
|
098cd4cbb9 | ||
|
309d6915fc | ||
|
b690326cf4 | ||
|
0d96c26174 | ||
|
c04b73d9cc | ||
|
495336fc58 | ||
|
111da51c73 | ||
|
d08d054fbe | ||
|
652cc8703a | ||
|
f3a049777a | ||
|
e4a3b967de | ||
|
ab1c7f2393 | ||
|
24600b46d9 | ||
|
ba04aae34e | ||
|
6de924927a | ||
|
68b51640af | ||
|
9f4008a775 | ||
|
c964730f03 | ||
|
dbb2121525 | ||
|
5b3e2066a2 | ||
|
7f2c1a6d53 | ||
|
6d212ea771 | ||
|
cee6de1147 | ||
|
5b19542355 | ||
|
bff340e910 | ||
|
02fc6b5bb7 | ||
|
5000d266fe | ||
|
f1ab340b11 | ||
|
612234b345 | ||
|
e08d6d6c5d | ||
|
8232705a60 | ||
|
89ad2ac447 | ||
|
42d2d82c74 | ||
|
59a81a4f77 | ||
|
04c4a2b6ac | ||
|
1ff581d4a5 | ||
|
9148b8f6f6 | ||
|
e69faa148f | ||
|
efcb781ae4 | ||
|
6d451c42fd | ||
|
8d74a5a259 | ||
|
ffdce9c00d | ||
|
789e708f69 | ||
|
0914ea5b6c | ||
|
2b850b9586 | ||
|
5798774181 | ||
|
b47ab393c6 | ||
|
ceebe27075 | ||
|
83c8933a0a | ||
|
fb83bc9161 | ||
|
5a992da480 | ||
|
34e52d78bf | ||
|
0864b978e7 | ||
|
ed5bdce87c | ||
|
cb419c658f | ||
|
373f813c7c | ||
|
5582c44608 |
9
.gitignore
vendored
@ -1,2 +1,9 @@
|
||||
*.sw?
|
||||
build/
|
||||
dist/
|
||||
.gradle/
|
||||
staging/
|
||||
temp/
|
||||
release
|
||||
.sass-cache
|
||||
*.build.tar.gz
|
||||
*.sw?
|
||||
|
13
build.xml
@ -1,13 +0,0 @@
|
||||
<project name="JDB Labs TimeStamper Library for Java">
|
||||
|
||||
<import file="jdb-build-1.10.xml"/>
|
||||
<property environment="env"/>
|
||||
<property file="project.properties"/>
|
||||
|
||||
<target name="release" depends="build">
|
||||
|
||||
<mkdir dir="${basedir}/release"/>
|
||||
<copy file="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||
tofile="${basedir}/release/${name}-${version}.jar"/>
|
||||
</target>
|
||||
</project>
|
20
cli/build.gradle
Normal file
@ -0,0 +1,20 @@
|
||||
apply plugin: "groovy"
|
||||
apply plugin: "maven"
|
||||
|
||||
group = "com.jdblabs.timestamper"
|
||||
version = "1.2"
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral() }
|
||||
|
||||
dependencies {
|
||||
compile 'ch.qos.logback:logback-classic:1.1.2'
|
||||
compile 'ch.qos.logback:logback-core:1.1.2'
|
||||
compile 'com.martiansoftware:nailgun-server:0.9.1'
|
||||
compile 'org.slf4j:slf4j-api:1.7.10'
|
||||
compile 'com.jdbernard:jdb-util:3.4'
|
||||
compile 'com.jdblabs.timestamper:timestamper-lib:2.1'
|
||||
|
||||
compile files('lib/jansi-1.12-SNAPSHOT.jar')
|
||||
}
|
24
cli/build.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<project name="JDB Labs TimeStamper CLI" default="build">
|
||||
|
||||
<property environment="env"/>
|
||||
<property file="project.properties"/>
|
||||
<import file="jdb-build-1.10.xml"/>
|
||||
|
||||
<target name="package" depends="build">
|
||||
<property name="package.dir" value="${build.dir}/${name}-${version}"/>
|
||||
<mkdir dir="${package.dir}/lib"/>
|
||||
<copy file="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||
tofile="${package.dir}/${name}-${version}.jar"/>
|
||||
<copy todir="${package.dir}">
|
||||
<filterset><filter token="VERSION" value="${version}"/></filterset>
|
||||
<fileset dir="${resources.dir}/bin"/>
|
||||
</copy>
|
||||
<copy todir="${package.dir}/lib">
|
||||
<fileset dir="${build.dir}/lib/runtime/jar"/>
|
||||
<fileset dir="${resources.dir}/config"/>
|
||||
</copy>
|
||||
<zip basedir="${build.dir}" includes="${name}-${version}/"
|
||||
destfile="${build.dir}/${name}-${version}.zip"/>
|
||||
|
||||
</target>
|
||||
</project>
|
BIN
cli/lib/jansi-1.12-SNAPSHOT.jar
Normal file
4
cli/resources/bin/ts
Executable file
@ -0,0 +1,4 @@
|
||||
curdir="`pwd`"
|
||||
cd ~/programs/timestamper-cli-@VERSION@
|
||||
java -cp "lib:lib/*:./*" com.jdblabs.timestamper.cli.TimeStamperCLI -d "$curdir" "$@"
|
||||
cd "$curdir"
|
0
cli/resources/config/logback.groovy
Normal file
1
cli/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = "timestamper-cli"
|
@ -0,0 +1,320 @@
|
||||
package com.jdblabs.timestamper.cli
|
||||
|
||||
import com.jdbernard.io.NonBlockingInputStreamReader
|
||||
import com.jdbernard.util.SmartConfig
|
||||
import com.jdbernard.util.LightOptionParser
|
||||
import com.jdblabs.timestamper.core.Timeline
|
||||
import com.jdblabs.timestamper.core.TimelineMarker
|
||||
import com.jdblabs.timestamper.core.TimelineProperties
|
||||
import com.martiansoftware.nailgun.NGContext
|
||||
|
||||
import org.fusesource.jansi.AnsiConsole
|
||||
import static org.fusesource.jansi.Ansi.*
|
||||
|
||||
import static org.fusesource.jansi.Ansi.*
|
||||
import static org.fusesource.jansi.Ansi.Color.*
|
||||
|
||||
public class TimeStamperCLI {
|
||||
|
||||
private static String EOL = System.getProperty("line.separator")
|
||||
|
||||
private static TimeStamperCLI nailgunInst
|
||||
|
||||
protected TimelineProperties timelineProperties
|
||||
protected Timeline timeline
|
||||
|
||||
public static final String VERSION = "1.2"
|
||||
|
||||
protected static def cli = [
|
||||
'h': [longName: 'help'],
|
||||
'v': [longName: 'version'],
|
||||
'd': [longName: 'working-directory', arguments: 1],
|
||||
't': [longName: 'timeline-config', arguments: 1],
|
||||
'tty': [longName: 'tty', arguments: 1]]
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
TimeStamperCLI inst = new TimeStamperCLI()
|
||||
doMain(inst, args, System.in, System.out, System.err); }
|
||||
|
||||
public static void nailMain(NGContext context) {
|
||||
if (nailgunInst == null)
|
||||
nailgunInst = new TimeStamperCLI()
|
||||
|
||||
doMain(nailgunInst, context.args, context.in, context.out, context.err) }
|
||||
|
||||
protected static void doMain(TimeStamperCLI inst, String[] args,
|
||||
def sin, def out, def err) {
|
||||
|
||||
//out = new PrintStream(AnsiConsole.wrapOutputStream(out))
|
||||
def opts = LightOptionParser.parseOptions(cli, args as List)
|
||||
|
||||
File workingDir = new File(opts.d[0] ?: '.')
|
||||
String ttyDevice = opts.tty ?: '/dev/tty'
|
||||
|
||||
if (opts.h) println USAGE
|
||||
|
||||
if (opts.v) {
|
||||
println "TimeStamperCLI v${VERSION}"
|
||||
println "By JDB Labs (https://www.jdb-labs.com)"
|
||||
return }
|
||||
|
||||
if (opts.t) {
|
||||
File propFile = new File(opts.t)
|
||||
if (!propFile.isAbsolute()) propFile = new File(workingDir, opts.t)
|
||||
inst.showTimeline(propFile, sin, out, err, ttyDevice) }
|
||||
else if (inst.timeline == null) {
|
||||
// Look for .timestamperrc user config file
|
||||
File cfgFile = new File(
|
||||
System.getProperty('user.home'), ".timestamperrc")
|
||||
if (!cfgFile.exists() || !cfgFile.isFile())
|
||||
err.println "Could not find the user configuration file: " +
|
||||
cfgFile.canonicalPath
|
||||
else {
|
||||
def cfg = new SmartConfig(cfgFile)
|
||||
inst.showTimeline(new File(cfg.lastUsed), sin, out, err, ttyDevice) } }
|
||||
else { inst.showTimeline(sin, out, err, ttyDevice) } }
|
||||
|
||||
public TimeStamperCLI() { }
|
||||
|
||||
public void showTimeline(File timelinePropertiesFile,
|
||||
def sin, def out, def err, String ttyDevice) {
|
||||
if (!timelinePropertiesFile.exists() ||
|
||||
!timelinePropertiesFile.isFile()) {
|
||||
|
||||
err.println "No such timeline property file: " +
|
||||
timelinePropertiesFile.canonicalPath
|
||||
|
||||
return }
|
||||
|
||||
this.timelineProperties = new TimelineProperties(timelinePropertiesFile)
|
||||
this.timeline = timelineProperties.timeline
|
||||
|
||||
showTimeline(sin, out, err, ttyDevice) }
|
||||
|
||||
public void showTimeline(final def sin, def out, def err, String ttyDevice) {
|
||||
|
||||
//out.println ""
|
||||
def currentMarker = timeline.getLastMarker(new Date())
|
||||
def reader = new NonBlockingInputStreamReader(sin)
|
||||
Thread readerThread = new Thread(reader)
|
||||
readerThread.start()
|
||||
|
||||
boolean running = true
|
||||
|
||||
def blockingReadLine = {
|
||||
String line = null;
|
||||
while (line == null && readerThread.isAlive()) {
|
||||
line = reader.readLine()
|
||||
|
||||
Thread.sleep(200) }
|
||||
|
||||
return line }
|
||||
|
||||
def readNotes = {
|
||||
out.println(ansi().fg(YELLOW).
|
||||
a("Notes (end with EOF or a blank line):").reset())
|
||||
|
||||
String notes = ""
|
||||
String line = null
|
||||
line = blockingReadLine()
|
||||
|
||||
while(line != "" && line != "EOF" && line != null) {
|
||||
notes += line + EOL
|
||||
line = blockingReadLine() }
|
||||
|
||||
return notes }
|
||||
|
||||
def printPrompt = {
|
||||
out.print(formatMarker(currentMarker) + EOL +
|
||||
ansi().fg(YELLOW).a("> ").reset())
|
||||
out.flush() }
|
||||
|
||||
String line = null
|
||||
|
||||
printPrompt()
|
||||
while (running && readerThread.isAlive()) {
|
||||
|
||||
// Handle user input
|
||||
line = reader.readLine()
|
||||
if (line != null) {
|
||||
out.flush();
|
||||
switch (line) {
|
||||
case ~/quit|exit|\u0004/:
|
||||
running = false;
|
||||
break
|
||||
|
||||
case ~/n|new/:
|
||||
|
||||
// Read mark
|
||||
out.println(ansi().fg(YELLOW).a("New timestamp mark:").reset())
|
||||
String mark = blockingReadLine()
|
||||
|
||||
// Read notes
|
||||
String notes = readNotes();
|
||||
|
||||
// Create marker
|
||||
currentMarker = new TimelineMarker(new Date(), mark, notes)
|
||||
timeline.addMarker(currentMarker)
|
||||
if (timelineProperties.persistOnUpdate)
|
||||
timelineProperties.save()
|
||||
break
|
||||
|
||||
case ~/h|help/:
|
||||
out.println(ansi().fg(RED).
|
||||
a("Not yet implemented.").reset());
|
||||
break
|
||||
|
||||
case ~/l|list|history/:
|
||||
out.println(ansi().fg(RED).
|
||||
a("Not yet implemented.").reset());
|
||||
break
|
||||
|
||||
case ~/s|save/:
|
||||
timelineProperties.save()
|
||||
break
|
||||
|
||||
case ~/reload/:
|
||||
timeline = timelineProperties.reloadTimeline()
|
||||
currentMarker = timeline.getLastMarker(new Date())
|
||||
break
|
||||
|
||||
case ~/e|ed|edit/:
|
||||
reader.pause()
|
||||
out.println(ansi().fg(YELLOW).
|
||||
a("Press ENTER to launch an editor for the current marker..."))
|
||||
out.flush()
|
||||
blockingReadLine()
|
||||
currentMarker = edit(currentMarker, ttyDevice)
|
||||
reader.resume()
|
||||
break
|
||||
|
||||
case ~/d|del|delete/:
|
||||
timeline.removeMarker(currentMarker)
|
||||
currentMarker = timeline.getLastMarker(new Date())
|
||||
break
|
||||
|
||||
default:
|
||||
String notes = readNotes()
|
||||
currentMarker = new TimelineMarker(new Date(), line, notes)
|
||||
timeline.addMarker(currentMarker)
|
||||
if (timelineProperties.persistOnUpdate)
|
||||
timelineProperties.save()
|
||||
break
|
||||
}
|
||||
printPrompt()
|
||||
} else {
|
||||
out.print(ansi().saveCursorPosition().cursorUpLine().eraseLine().toString() +
|
||||
formatMarker(currentMarker) +
|
||||
ansi().cursorDown(1).restorCursorPosition().toString())
|
||||
out.flush();
|
||||
|
||||
Thread.sleep(200)
|
||||
}
|
||||
}
|
||||
|
||||
this.timelineProperties.syncTargets.each { it.shutdown() }
|
||||
|
||||
if (readerThread.isAlive()) {
|
||||
readerThread.interrupt();
|
||||
readerThread.join(500);
|
||||
if (readerThread.isAlive()) readerThread.stop(); }
|
||||
|
||||
out.println ""
|
||||
}
|
||||
|
||||
protected TimelineMarker edit(TimelineMarker tm, String ttyDevice) {
|
||||
File temp = File.createTempFile('timestamp-mark-', '.txt')
|
||||
temp.withPrintWriter { fout ->
|
||||
fout.println("""\
|
||||
# Edit the time, mark, and notes below. Any lines starting with '#' will be
|
||||
# ignored. When done, save the file and close the editor.""")
|
||||
fout.println(Timeline.longFormat.format(tm.timestamp))
|
||||
fout.println(tm.mark)
|
||||
fout.println("""\
|
||||
# Everything from the line below to the end of the file will be considered
|
||||
# notes for this timeline mark.""")
|
||||
fout.println(tm.notes) }
|
||||
|
||||
['sh', '-c', "\$EDITOR ${temp.canonicalPath} <${ttyDevice} >${ttyDevice}"].execute().waitFor()
|
||||
|
||||
Thread.sleep(200)
|
||||
|
||||
String newTimeStr
|
||||
String newMark
|
||||
String newNotes = ""
|
||||
|
||||
temp.eachLine { line ->
|
||||
if (line =~ /^\s*#/) return
|
||||
|
||||
if (!newTimeStr) newTimeStr = line
|
||||
else if (!newMark) newMark = line
|
||||
else newNotes += line + EOL }
|
||||
|
||||
Date newTime
|
||||
try { newTime = Timeline.longFormat.parse(newTimeStr) }
|
||||
catch(Exception e) {
|
||||
out.println(ansi().fg(RED).a("Invalid timestamp format. The " +
|
||||
"previous timestamp value will be retained."))
|
||||
newTime = currentMark.timestamp }
|
||||
|
||||
timeline.removeMarker(tm)
|
||||
def newMarker = new TimelineMarker(newTime, newMark, newNotes)
|
||||
timeline.addMarker(newMarker)
|
||||
if (timelineProperties.persistOnUpdate)
|
||||
timelineProperties.save()
|
||||
|
||||
return newMarker
|
||||
|
||||
}
|
||||
|
||||
protected static String formatMarker(TimelineMarker tm) {
|
||||
return ansi().fgBright(CYAN).
|
||||
a(Timeline.shortFormat.format(tm.timestamp) + " ").fg(GREEN).
|
||||
a("(" + getDuration(tm) + ") ").fg(WHITE).a(tm.mark).toString() }
|
||||
|
||||
protected static String getDuration(TimelineMarker tm) {
|
||||
Date currentTime = new Date()
|
||||
long seconds = currentTime.time - tm.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")
|
||||
|
||||
return sb.toString() }
|
||||
|
||||
public static final String USAGE = """\
|
||||
TimeStamperCLI v${VERSION}
|
||||
By JDB Labs (https://www.jdb-labs.com)
|
||||
|
||||
Usage:
|
||||
ts <OPTIONS>
|
||||
where OPTIONS is one or more of:
|
||||
|
||||
-h, --help Print this usage information.
|
||||
|
||||
-v, --version Print version information.
|
||||
|
||||
-d, --working-directory Set the application's working direcotry (defaults to
|
||||
the current directory of the executing process).
|
||||
|
||||
-t, --timeline-config Set the timeline configuration file to use to access
|
||||
the timeline. By default, the value of the
|
||||
`lastUsed` property in the \$HOME/.timestamperrc
|
||||
file is used to find the timeline configuration file
|
||||
to use.
|
||||
|
||||
--tty Manually set the name of the TTY device to use. This
|
||||
defaults to `/dev/tty`.
|
||||
"""
|
||||
|
||||
}
|
62
gui/.classpath
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main"/>
|
||||
<classpathentry kind="src" path="griffon-app/conf"/>
|
||||
<classpathentry kind="src" path="griffon-app/models"/>
|
||||
<classpathentry kind="src" path="griffon-app/views"/>
|
||||
<classpathentry kind="src" path="griffon-app/controllers"/>
|
||||
<classpathentry kind="src" path="griffon-app/resources"/>
|
||||
<classpathentry kind="src" path="test/integration"/>
|
||||
<classpathentry kind="src" path="test/unit"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/ant/lib/ant.jar"/>
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/swingx-0.9.3.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/swing-worker.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/commons-lang-2.4.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/ant-launcher-1.7.1.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/gant_groovy1.6-1.6.0.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/asm-2.2.3.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/commons-cli-1.0.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/groovy-all-1.6.4.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/swingxbuilder-0.1.6-SNAPSHOT.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/jline-0.9.94.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/svnkit-1.2.0.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/log4j-1.2.15.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/ant-nodeps-1.7.1.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/ant-1.7.1.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/ant-trax-1.7.1.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/commons-logging-1.1.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/ant-junit-1.7.1.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/MultipleGradientPaint.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/spring-2.5.6.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/lib/junit-3.8.2.jar" />
|
||||
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/dist/griffon-rt-0.2.1.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/dist/griffon-resources-0.2.1.jar" />
|
||||
|
||||
<classpathentry kind="var" path="GRIFFON_HOME/dist/griffon-cli-0.2.1.jar" />
|
||||
|
||||
|
||||
<classpathentry kind="output" path="staging/classes"/>
|
||||
</classpath>
|
4
gui/.hgignore
Executable file
@ -0,0 +1,4 @@
|
||||
.*.swp
|
||||
.*.swo
|
||||
staging
|
||||
dist
|
18
gui/.project
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>TimeStamper</name>
|
||||
<comment/>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
BIN
gui/CHANGE ME
Normal file
43
gui/TimeStamper.iml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module relativePaths="true" type="GRIFFON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Griffon" name="Griffon">
|
||||
<configuration />
|
||||
</facet>
|
||||
<!--
|
||||
<facet type="Spring" name="Spring">
|
||||
<configuration>
|
||||
<fileset id="Griffon" name="Griffon" removed="false">
|
||||
<file>file://$MODULE_DIR$/web-app/WEB-INF/applicationContext.xml</file>
|
||||
</fileset>
|
||||
</configuration>
|
||||
</facet>
|
||||
-->
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/griffon-app/conf" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/griffon-app/models" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/griffon-app/views" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/griffon-app/controllers" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/griffon-app/lifecycle" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test/integration" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test/unit" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Griffon 0.3" level="project" />
|
||||
<orderEntry type="module-library">
|
||||
<library name="Griffon User Library">
|
||||
<CLASSES>
|
||||
<root url="file://$MODULE_DIR$/lib" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<jarDirectory url="file://$MODULE_DIR$/lib" recursive="false" />
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
59
gui/TimeStamper.ipr
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project relativePaths="false" version="4">
|
||||
<component name="ProjectFileVersion" converted="true" />
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/TimeStamper.iml" filepath="$PROJECT_DIR$/TimeStamper.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="libraryTable">
|
||||
<library name="Griffon 0.3">
|
||||
<CLASSES>
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/log4j-1.2.15.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/spring-2.5.6.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/ant-trax-1.8.0.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/gant_groovy1.6-1.6.0.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/svnkit-1.2.0.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/junit-4.8.1.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/commons-cli-1.2.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/ant-nodeps-1.8.0.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/groovy-all-1.7.1.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/commons-lang-2.4.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/ant-junit-1.8.0.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/jline-0.9.94.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/ant-launcher-1.8.0.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/commons-logging-1.1.1.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/asm-3.2.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/lib/ant-1.8.0.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/dist/griffon-resources-0.3.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/dist/griffon-cli-0.3.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/dist/griffon-rt-0.3.jar!/" />
|
||||
|
||||
<root url="jar:///home/jdbernard/programs/griffon/dist/griffon-scripts-0.3.jar!/" />
|
||||
|
||||
|
||||
</CLASSES>
|
||||
</library>
|
||||
</component>
|
||||
</project>
|
65
gui/TimeStamper.iws
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project relativePaths="false" version="4">
|
||||
<component name="ProjectPane">
|
||||
<subPane>
|
||||
<PATH>
|
||||
<PATH_ELEMENT>
|
||||
<option name="myItemId" value="TimeStamper" />
|
||||
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
|
||||
</PATH_ELEMENT>
|
||||
<PATH_ELEMENT>
|
||||
<option name="myItemId" value="TimeStamper" />
|
||||
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
|
||||
</PATH_ELEMENT>
|
||||
<PATH_ELEMENT>
|
||||
<option name="myItemId" value="TimeStamper" />
|
||||
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
|
||||
</PATH_ELEMENT>
|
||||
</PATH>
|
||||
</subPane>
|
||||
</component>
|
||||
<component name="ProjectView">
|
||||
<navigator currentView="ProjectPane" proportions="0.5" version="1" splitterProportion="0.5">
|
||||
<flattenPackages />
|
||||
<showMembers />
|
||||
<showModules />
|
||||
<showLibraryContents />
|
||||
<hideEmptyPackages />
|
||||
<abbreviatePackageNames />
|
||||
<showStructure ProjectPane="false" />
|
||||
<autoscrollToSource />
|
||||
<autoscrollFromSource />
|
||||
<sortByType />
|
||||
</navigator>
|
||||
</component>
|
||||
<!--
|
||||
<component name="RunManager" selected="Griffon Application.Griffon:TimeStamper">
|
||||
<configuration default="false" name="Griffon:TimeStamper" type="GriffonRunConfigurationType" factoryName="Griffon Application">
|
||||
<module name="TimeStamper" />
|
||||
<setting name="vmparams" value="" />
|
||||
<setting name="griffonparams" value="" />
|
||||
<setting name="hostik" value="localhost" />
|
||||
<setting name="port" value="8080" />
|
||||
<setting name="jndi" value="false" />
|
||||
<setting name="recomp" value="false" />
|
||||
<setting name="recompileFreq" value="3" />
|
||||
<setting name="launchBrowser" value="true" />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" value="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="Griffon Application.Griffon:TimeStamper" />
|
||||
</list>
|
||||
</component>
|
||||
-->
|
||||
<component name="ToolWindowManager">
|
||||
<frame x="10" y="10" width="1260" height="984" extended-state="0" />
|
||||
<editor active="false" />
|
||||
<layout>
|
||||
<window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.25" sideWeight="0.6623068" order="0" side_tool="false" />
|
||||
</layout>
|
||||
</component>
|
||||
</project>
|
20
gui/TimeStamper.launch
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.codehaus.griffon.GriffonMain"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER" javaProject="TimeStamper" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="TimeStamper"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/TimeStamper" path="3" type="2"/> "/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="4"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="TimeStamper"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dbase.dir=${project_loc} -Dgriffon.env=development"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/TimeStamper"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
|
||||
</launchConfiguration>
|
73
gui/TimeStamper.tmproj
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>documents</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>filename</key>
|
||||
<string>TimeStamper.launch</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>filename</key>
|
||||
<string>build.xml</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>griffon-app</string>
|
||||
<key>regexFolderFilter</key>
|
||||
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
||||
<key>sourceDirectory</key>
|
||||
<string>griffon-app</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>test</string>
|
||||
<key>regexFolderFilter</key>
|
||||
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
||||
<key>sourceDirectory</key>
|
||||
<string>test</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>lib</string>
|
||||
<key>regexFolderFilter</key>
|
||||
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
||||
<key>sourceDirectory</key>
|
||||
<string>lib</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>scripts</string>
|
||||
<key>regexFolderFilter</key>
|
||||
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
||||
<key>sourceDirectory</key>
|
||||
<string>scripts</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>src</string>
|
||||
<key>regexFolderFilter</key>
|
||||
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
||||
<key>sourceDirectory</key>
|
||||
<string>src</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>web-app</string>
|
||||
<key>regexFolderFilter</key>
|
||||
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
||||
<key>sourceDirectory</key>
|
||||
<string>web-app</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>fileHierarchyDrawerWidth</key>
|
||||
<integer>200</integer>
|
||||
<key>metaData</key>
|
||||
<dict/>
|
||||
<key>showFileHierarchyDrawer</key>
|
||||
<true/>
|
||||
<key>windowFrame</key>
|
||||
<string>{{237, 127}, {742, 553}}</string>
|
||||
</dict>
|
||||
</plist>
|
10
gui/application.properties
Executable file
@ -0,0 +1,10 @@
|
||||
#Do not edit app.griffon.* properties, they may change automatically. DO NOT put application configuration in here, it is not the right place!
|
||||
#Thu, 25 Apr 2013 06:40:33 -0500
|
||||
#Thu Apr 01 22:28:40 CDT 2010
|
||||
app.version=2.1
|
||||
app.griffon.version=1.2.0
|
||||
app.name=TimeStamper
|
||||
|
||||
plugins.swing=1.2.0
|
||||
archetype.default=1.2.0
|
||||
app.toolkit=swing
|
97
gui/build.xml
Executable file
@ -0,0 +1,97 @@
|
||||
<project name="timestamper" default="test">
|
||||
|
||||
<!-- =================================
|
||||
target: clean
|
||||
================================= -->
|
||||
<target name="clean" description="--> Cleans a Griffon application">
|
||||
<griffon>
|
||||
<arg value="clean"/>
|
||||
</griffon>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: package
|
||||
================================= -->
|
||||
<target name="package" description="--> Packages up Griffon artifacts">
|
||||
<griffon>
|
||||
<arg value="package"/>
|
||||
</griffon>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: run-app
|
||||
================================= -->
|
||||
<target name="run-app" description="--> Run a Griffon application in standalone mode">
|
||||
<griffon>
|
||||
<arg value="run-app"/>
|
||||
</griffon>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: debug-app
|
||||
================================= -->
|
||||
<target name="debug-app" description="--> Run a Griffon application in standalone mode with debugging turned on">
|
||||
<griffon>
|
||||
<arg value="run-app"/>
|
||||
<arg value="-debug"/>
|
||||
</griffon>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: run-webstart
|
||||
================================= -->
|
||||
<target name="run-webstart" description="--> Run a Griffon application in webstart mode">
|
||||
<griffon>
|
||||
<arg value="run-webstart"/>
|
||||
</griffon>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: run-applet
|
||||
================================= -->
|
||||
<target name="run-applet" description="--> Run a Griffon application in applet mode">
|
||||
<griffon>
|
||||
<arg value="run-applet"/>
|
||||
</griffon>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: test
|
||||
================================= -->
|
||||
<target name="test" description="--> Run a Griffon applications unit tests">
|
||||
<griffon>
|
||||
<arg value="test-app"/>
|
||||
</griffon>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: dist
|
||||
================================= -->
|
||||
<target name="dist" description="--> Packages up Griffon artifacts in the Production Environment">
|
||||
<griffon>
|
||||
<arg value="prod"/>
|
||||
<arg value="package"/>
|
||||
</griffon>
|
||||
</target>
|
||||
|
||||
<!-- set up the griffon macro -->
|
||||
<property environment="env"/>
|
||||
<property name="griffon.home" value="${env.GRIFFON_HOME}"/>
|
||||
<property name="jdk.home" value="${env.JAVA_HOME}"/>
|
||||
<condition property="griffon" value="griffon.bat">
|
||||
<os family="windows"/>
|
||||
</condition>
|
||||
<property name="griffon" value="griffon" />
|
||||
<macrodef name="griffon">
|
||||
<element name="griffon-args" implicit="yes"/>
|
||||
<sequential>
|
||||
<exec executable="${griffon.home}/bin/${griffon}" failonerror="true">
|
||||
<env key="JAVA_HOME" value="${jdk.home}"/>
|
||||
<env key="GRIFFON_HOME" value="${griffon.home}"/>
|
||||
<griffon-args/>
|
||||
</exec>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<!-- end set up the griffon macro -->
|
||||
|
||||
</project>
|
BIN
gui/doc/feed.pdf
Executable file
77
gui/doc/feed.rst
Executable file
@ -0,0 +1,77 @@
|
||||
Centralized vs. decentralized
|
||||
-----------------------------
|
||||
|
||||
Centralized
|
||||
```````````
|
||||
|
||||
- one central list
|
||||
- remote apps that sync with central?
|
||||
|
||||
Decentralized
|
||||
`````````````
|
||||
|
||||
- sync to URL(s)?
|
||||
|
||||
- need a network protocol
|
||||
- HTTP?
|
||||
- SSL?
|
||||
|
||||
- group-wise sync?
|
||||
|
||||
- establish master/slaves?
|
||||
- easier than coordinated group-update:::
|
||||
|
||||
map each URL to synch -> the last time updated.
|
||||
if (update_period):
|
||||
forall URLs: synch
|
||||
else if (incoming_update):
|
||||
forall (URLs older than incoming update): synch
|
||||
|
||||
- synch based on hash of updates?
|
||||
|
||||
- need canonicalizer for text. Use XML?
|
||||
- hash algorithm:::
|
||||
|
||||
SHA-1 of:
|
||||
concatenate:
|
||||
date (YYYYMMDDhhmmssSSS)
|
||||
name
|
||||
notes
|
||||
|
||||
External Feeds
|
||||
--------------
|
||||
|
||||
Item format
|
||||
```````````
|
||||
|
||||
- time started
|
||||
- name/description
|
||||
- notes
|
||||
- category?
|
||||
|
||||
Pull from
|
||||
`````````
|
||||
|
||||
- needs to be optional
|
||||
- standardized input format
|
||||
|
||||
- easy to parse
|
||||
- no errors, false positives
|
||||
- restrictive.
|
||||
|
||||
- flexible input format
|
||||
|
||||
- matches regex's?
|
||||
- map groups to fields
|
||||
|
||||
Push to
|
||||
```````
|
||||
|
||||
- optional
|
||||
- standardized output
|
||||
|
||||
- cannot be flexible to match output medium
|
||||
|
||||
- flexible input format
|
||||
|
||||
- choose fields and format values
|
8
gui/doc/sample-txt-timeline-with-sync-info.txt
Executable file
@ -0,0 +1,8 @@
|
||||
# sync-options:
|
||||
# LOCAL TIMELINE: this file
|
||||
# SYNC TO: ssh://jdbernard@jdbernard.no-ip.org/timelines/jdbernard.timeline.txt
|
||||
# SYNC TO: http://www.twitter.com/jdbernard
|
||||
# pull only
|
||||
# update every 30 sec
|
||||
# SYNC TO: file:///home/jdbernard/timelines/jdbernard.timeline.bak
|
||||
# push only
|
BIN
gui/doc/uml.tsm
Normal file
27
gui/griffon-app/conf/Application.groovy
Executable file
@ -0,0 +1,27 @@
|
||||
application {
|
||||
title="TimeStamper"
|
||||
startupGroups=["TimeStamperMain"]
|
||||
autoShutdown=true
|
||||
}
|
||||
mvcGroups {
|
||||
LogDialog {
|
||||
model="com.jdblabs.timestamper.gui.LogDialogModel"
|
||||
controller="com.jdblabs.timestamper.gui.LogDialogController"
|
||||
view="com.jdblabs.timestamper.gui.LogDialogView"
|
||||
}
|
||||
TimeStamperMain {
|
||||
model="com.jdblabs.timestamper.gui.TimeStamperMainModel"
|
||||
view="com.jdblabs.timestamper.gui.TimeStamperMainView"
|
||||
controller="com.jdblabs.timestamper.gui.TimeStamperMainController"
|
||||
}
|
||||
PunchcardDialog {
|
||||
model="com.jdblabs.timestamper.gui.PunchcardDialogModel"
|
||||
view="com.jdblabs.timestamper.gui.PunchcardDialogView"
|
||||
controller="com.jdblabs.timestamper.gui.PunchcardDialogController"
|
||||
}
|
||||
NotesDialog {
|
||||
model="com.jdblabs.timestamper.gui.NotesDialogModel"
|
||||
view="com.jdblabs.timestamper.gui.NotesDialogView"
|
||||
controller="com.jdblabs.timestamper.gui.NotesDialogController"
|
||||
}
|
||||
}
|
191
gui/griffon-app/conf/BuildConfig.groovy
Normal file
@ -0,0 +1,191 @@
|
||||
// key signing information
|
||||
environments {
|
||||
development {
|
||||
signingkey {
|
||||
params {
|
||||
// sigfile = 'GRIFFON'
|
||||
// keystore = "${basedir}/griffon-app/conf/keys/devKeystore"
|
||||
// alias = 'development'
|
||||
storepass = 'BadStorePassword'
|
||||
keypass = 'BadKeyPassword'
|
||||
lazy = true // only sign when unsigned
|
||||
}
|
||||
}
|
||||
}
|
||||
test {
|
||||
griffon {
|
||||
jars {
|
||||
sign = false
|
||||
pack = false
|
||||
}
|
||||
}
|
||||
}
|
||||
production {
|
||||
signingkey {
|
||||
params {
|
||||
// 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 //keypass = 'BadKeyPassword'
|
||||
lazy = false // sign, regardless of existing signatures
|
||||
}
|
||||
}
|
||||
|
||||
griffon {
|
||||
jars {
|
||||
sign = true
|
||||
pack = true
|
||||
destDir = "${basedir}/staging"
|
||||
}
|
||||
webstart {
|
||||
codebase = 'CHANGE ME'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
griffon {
|
||||
memory {
|
||||
//max = '64m'
|
||||
//min = '2m'
|
||||
//minPermSize = '2m'
|
||||
//maxPermSize = '64m'
|
||||
}
|
||||
jars {
|
||||
sign = false
|
||||
pack = false
|
||||
destDir = "${basedir}/staging"
|
||||
jarName = "${appName}.jar"
|
||||
}
|
||||
extensions {
|
||||
jarUrls = []
|
||||
jnlpUrls = []
|
||||
/*
|
||||
props {
|
||||
someProperty = 'someValue'
|
||||
}
|
||||
resources {
|
||||
linux { // windows, macosx, solaris
|
||||
jars = []
|
||||
nativelibs = []
|
||||
props {
|
||||
someProperty = 'someValue'
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
webstart {
|
||||
codebase = "${new File(griffon.jars.destDir).toURI().toASCIIString()}"
|
||||
jnlp = 'application.jnlp'
|
||||
}
|
||||
applet {
|
||||
jnlp = 'applet.jnlp'
|
||||
html = 'applet.html'
|
||||
}
|
||||
}
|
||||
|
||||
// required for custom environments
|
||||
signingkey {
|
||||
params {
|
||||
def env = griffon.util.Environment.current.name
|
||||
sigfile = 'GRIFFON-' + env
|
||||
keystore = "${basedir}/griffon-app/conf/keys/${env}Keystore"
|
||||
alias = env
|
||||
// storepass = 'BadStorePassword'
|
||||
// keypass = 'BadKeyPassword'
|
||||
lazy = true // only sign when unsigned
|
||||
}
|
||||
}
|
||||
|
||||
griffon {
|
||||
doc {
|
||||
logo = '<a href="http://griffon-framework.org" target="_blank"><img alt="The Griffon Framework" src="../img/griffon.png" border="0"/></a>'
|
||||
sponsorLogo = "<br/>"
|
||||
footer = "<br/><br/>Made with Griffon (@griffon.version@)"
|
||||
}
|
||||
}
|
||||
|
||||
deploy {
|
||||
application {
|
||||
title = "${appName} ${appVersion}"
|
||||
vendor = System.properties['user.name']
|
||||
homepage = "http://localhost/${appName}"
|
||||
description {
|
||||
complete = "${appName} ${appVersion}"
|
||||
oneline = "${appName} ${appVersion}"
|
||||
minimal = "${appName} ${appVersion}"
|
||||
tooltip = "${appName} ${appVersion}"
|
||||
}
|
||||
icon {
|
||||
'default' {
|
||||
name = 'griffon-icon-64x64.png'
|
||||
width = '64'
|
||||
height = '64'
|
||||
}
|
||||
splash {
|
||||
name = 'griffon.png'
|
||||
width = '391'
|
||||
height = '123'
|
||||
}
|
||||
selected {
|
||||
name = 'griffon-icon-64x64.png'
|
||||
width = '64'
|
||||
height = '64'
|
||||
}
|
||||
disabled {
|
||||
name = 'griffon-icon-64x64.png'
|
||||
width = '64'
|
||||
height = '64'
|
||||
}
|
||||
rollover {
|
||||
name = 'griffon-icon-64x64.png'
|
||||
width = '64'
|
||||
height = '64'
|
||||
}
|
||||
shortcut {
|
||||
name = 'griffon-icon-64x64.png'
|
||||
width = '64'
|
||||
height = '64'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
griffon.project.dependency.resolution = {
|
||||
// inherit Griffon' default dependencies
|
||||
inherits("global") {
|
||||
}
|
||||
log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
|
||||
repositories {
|
||||
griffonHome()
|
||||
|
||||
// uncomment the below to enable remote dependency resolution
|
||||
// from public Maven repositories
|
||||
//mavenLocal()
|
||||
//mavenCentral()
|
||||
//mavenRepo "http://snapshots.repository.codehaus.org"
|
||||
//mavenRepo "http://repository.codehaus.org"
|
||||
//mavenRepo "http://download.java.net/maven/2/"
|
||||
//mavenRepo "http://repository.jboss.com/maven2/"
|
||||
}
|
||||
dependencies {
|
||||
// specify dependencies here under either 'build', 'compile', 'runtime' or 'test' scopes eg.
|
||||
|
||||
// runtime 'mysql:mysql-connector-java:5.1.5'
|
||||
}
|
||||
}
|
||||
|
||||
log4j = {
|
||||
// Example of changing the log pattern for the default console
|
||||
// appender:
|
||||
appenders {
|
||||
console name: 'stdout', layout: pattern(conversionPattern: '%d [%t] %-5p %c - %m%n')
|
||||
}
|
||||
|
||||
error 'org.codehaus.griffon',
|
||||
'org.springframework',
|
||||
'org.apache.karaf',
|
||||
'groovyx.net'
|
||||
warn 'griffon'
|
||||
}
|
||||
|
7
gui/griffon-app/conf/Builder.groovy
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
root {
|
||||
'groovy.swing.SwingBuilder' {
|
||||
controller = ['Threading']
|
||||
view = '*'
|
||||
}
|
||||
}
|
15
gui/griffon-app/conf/Config.groovy
Normal file
@ -0,0 +1,15 @@
|
||||
log4j = {
|
||||
// Example of changing the log pattern for the default console
|
||||
// appender:
|
||||
appenders {
|
||||
console name: 'stdout', layout: pattern(conversionPattern: '%d [%t] %-5p %c - %m%n')
|
||||
}
|
||||
|
||||
error 'org.codehaus.griffon'
|
||||
|
||||
info 'griffon.util',
|
||||
'griffon.core',
|
||||
'griffon.@application.toolkit@',
|
||||
'griffon.app'
|
||||
}
|
||||
|
5
gui/griffon-app/conf/Events.groovy
Normal file
@ -0,0 +1,5 @@
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
onNewInstance = { klass, type, instance ->
|
||||
instance.metaClass.logger = LoggerFactory.getLogger(klass.name)
|
||||
}
|
BIN
gui/griffon-app/conf/keys/productionKeystore
Normal file
43
gui/griffon-app/conf/webstart/applet.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script src="http://java.com/js/deployJava.js"></script>
|
||||
<script>
|
||||
var attributes = {id: 'TimeStamper',
|
||||
codebase:'@griffonAppCodebase@',
|
||||
code:'@griffonAppletClass@',
|
||||
archive:'@appletJars@',
|
||||
width:'240', height:'320'} ;
|
||||
var parameters = {fontSize:16,
|
||||
java_arguments: "-Djnlp.packEnabled=true",
|
||||
jnlp_href:'@griffonAppCodebase@/applet.jnlp',
|
||||
draggable:'true',
|
||||
image:'griffon.png',
|
||||
boxmessage:'Loading TimeStamper',
|
||||
boxbgcolor:'#FFFFFF', boxfgcolor:'#000000',
|
||||
codebase_lookup: 'false'} ;
|
||||
var version = '1.5.0' ;
|
||||
deployJava.runApplet(attributes, parameters, version);
|
||||
</script>
|
||||
<!--
|
||||
<APPLET CODEBASE='@griffonAppCodebase@'
|
||||
CODE='@griffonAppletClass@'
|
||||
ARCHIVE='@appletJars@'
|
||||
WIDTH='240' HEIGHT='320'>
|
||||
<PARAM NAME="java_arguments" VALUE="-Djnlp.packEnabled=true">
|
||||
<PARAM NAME='jnlp_href' VALUE='@griffonAppCodebase@/applet.jnlp'>
|
||||
<PARAM NAME='dragggable' VALUE='true'>
|
||||
<PARAM NAME='image' VALUE='griffon.png'>
|
||||
<PARAM NAME='boxmessage' VALUE='Loading TimeStamper'>
|
||||
<PARAM NAME='boxbgcolor' VALUE='#FFFFFF'>
|
||||
<PARAM NAME='boxfgcolor' VALUE='#000000'>
|
||||
<PARAM NAME='codebase_lookup' VALUE='false'>
|
||||
</APPLET>
|
||||
-->
|
||||
</body>
|
||||
</html>
|
55
gui/griffon-app/conf/webstart/applet.jnlp
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE jnlp SYSTEM "http://java.sun.com/dtd/JNLP-1.5.dtd">
|
||||
<jnlp
|
||||
version="2.1"
|
||||
codebase="@griffonAppCodebase@"
|
||||
href="@jnlpFileName@"
|
||||
>
|
||||
<information>
|
||||
<title>TimeStamper</title>
|
||||
<vendor>TimeStamper</vendor>
|
||||
<!--<homepage href="http://app.example.com/"/>-->
|
||||
<!--fallback description-->
|
||||
<description>TimeStamper</description>
|
||||
<description kind="one-line">TimeStamper</description>
|
||||
<description kind="short">TimeStamper</description>
|
||||
<description kind="tooltip">TimeStamper</description>
|
||||
<!-- fallback icon -->
|
||||
<icon href="griffon-icon-48x48.png" kind="default" width="48" height="48"/>
|
||||
<!-- icon used for splash screen -->
|
||||
<icon href="griffon.png" kind="splash" width="381" height="123"/>
|
||||
<!-- icon used in menu -->
|
||||
<icon href="griffon-icon-16x16.png" kind="shortcut" width="16" height="16"/>
|
||||
<!-- icon used on desktop -->
|
||||
<icon href="griffon-icon-32x32.png" kind="shortcut" width="32" height="32"/>
|
||||
<!-- to create shortcuts, uncomment this
|
||||
<shortcut online="true">
|
||||
<desktop/>
|
||||
<menu submenu="TimeStamper"/>
|
||||
</shortcut>
|
||||
-->
|
||||
<offline-allowed/>
|
||||
</information>
|
||||
<security>
|
||||
<all-permissions/>
|
||||
<!--<j2ee-application-client-permissions/>-->
|
||||
</security>
|
||||
<resources>
|
||||
<property name="jnlp.packEnabled" value="true"/>
|
||||
<j2se version="1.5+" @memoryOptions@/>
|
||||
<!-- auto-added jars follow, griffon-rt, app, and groovy -->
|
||||
@jnlpJars@
|
||||
<!-- Add all extra jars below here, or the app may break -->
|
||||
@jnlpExtensions@
|
||||
</resources>
|
||||
<applet-desc
|
||||
documentbase="@griffonAppCodebase@"
|
||||
name="TimeStamperApplet"
|
||||
main-class="@griffonAppletClass@"
|
||||
width="320"
|
||||
height="640">
|
||||
<!-- params are ignored when referenced from web page for 6u10 -->
|
||||
<!--<param name="key1" value="value1"/>-->
|
||||
<!--<param name="key2" value="value2"/>-->
|
||||
</applet-desc>
|
||||
</jnlp>
|
50
gui/griffon-app/conf/webstart/application.jnlp
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE jnlp SYSTEM "http://java.sun.com/dtd/JNLP-1.5.dtd">
|
||||
<jnlp
|
||||
version="2.1"
|
||||
codebase="@griffonAppCodebase@"
|
||||
href="@jnlpFileName@"
|
||||
>
|
||||
<information>
|
||||
<title>TimeStamper</title>
|
||||
<vendor>TimeStamper</vendor>
|
||||
<!--<homepage href="http://app.example.com/"/>-->
|
||||
<!--fallback description-->
|
||||
<description>TimeStamper</description>
|
||||
<description kind="one-line">TimeStamper</description>
|
||||
<description kind="short">TimeStamper</description>
|
||||
<description kind="tooltip">TimeStamper</description>
|
||||
<!-- fallback icon -->
|
||||
<icon href="griffon-icon-48x48.png" kind="default" width="48" height="48"/>
|
||||
<!-- icon used for splash screen -->
|
||||
<icon href="griffon.png" kind="splash" width="381" height="123"/>
|
||||
<!-- icon used in menu -->
|
||||
<icon href="griffon-icon-16x16.png" kind="shortcut" width="16" height="16"/>
|
||||
<!-- icon used on desktop -->
|
||||
<icon href="griffon-icon-32x32.png" kind="shortcut" width="32" height="32"/>
|
||||
<!-- to create shortcuts, uncomment this
|
||||
<shortcut online="true">
|
||||
<desktop/>
|
||||
<menu submenu="TimeStamper"/>
|
||||
</shortcut>
|
||||
<offline-allowed/>
|
||||
-->
|
||||
</information>
|
||||
<security>
|
||||
<all-permissions/>
|
||||
<!--<j2ee-application-client-permissions/>-->
|
||||
</security>
|
||||
<resources>
|
||||
<property name="jnlp.packEnabled" value="true"/>
|
||||
<j2se version="1.5+" @memoryOptions@/>
|
||||
<!-- auto-added jars follow, griffon-rt, app, and groovy -->
|
||||
@jnlpJars@
|
||||
<!-- Add all extra jars below here, or the app may break -->
|
||||
@jnlpExtensions@
|
||||
</resources>
|
||||
<application-desc main-class="@griffonApplicationClass@">
|
||||
<!-- params are ignored when referenced from web page for 6u10 -->
|
||||
<!--<param name="key1" value="value1"/>-->
|
||||
<!--<param name="key2" value="value2"/>-->
|
||||
</application-desc>
|
||||
</jnlp>
|
BIN
gui/griffon-app/conf/webstart/griffon-icon-128x128.png
Executable file
After Width: | Height: | Size: 14 KiB |
BIN
gui/griffon-app/conf/webstart/griffon-icon-16x16.png
Executable file
After Width: | Height: | Size: 1.0 KiB |
BIN
gui/griffon-app/conf/webstart/griffon-icon-24x24.png
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
gui/griffon-app/conf/webstart/griffon-icon-256x256.png
Executable file
After Width: | Height: | Size: 31 KiB |
BIN
gui/griffon-app/conf/webstart/griffon-icon-32x32.png
Executable file
After Width: | Height: | Size: 2.5 KiB |
BIN
gui/griffon-app/conf/webstart/griffon-icon-48x48.png
Executable file
After Width: | Height: | Size: 5.1 KiB |
BIN
gui/griffon-app/conf/webstart/griffon-icon-64x64.png
Executable file
After Width: | Height: | Size: 5.8 KiB |
BIN
gui/griffon-app/conf/webstart/griffon.png
Executable file
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,16 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
class LogDialogController {
|
||||
// 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 ->
|
||||
}
|
||||
*/
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import java.awt.Point
|
||||
|
||||
class NotesDialogController {
|
||||
// these will be injected by Griffon
|
||||
def model
|
||||
def view
|
||||
|
||||
void mvcGroupInit(Map args) {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import java.awt.Point
|
||||
|
||||
class PunchcardDialogController {
|
||||
// these will be injected by Griffon
|
||||
def model
|
||||
def view
|
||||
|
||||
void mvcGroupInit(Map args) {
|
||||
|
||||
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 ->
|
||||
}
|
||||
*/
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import com.jdbernard.util.SmartConfig
|
||||
import com.jdblabs.timestamper.core.TimelineMarker
|
||||
import com.jdblabs.timestamper.core.TimelineProperties
|
||||
|
||||
class TimeStamperMainController {
|
||||
// these will be injected by Griffon
|
||||
def model
|
||||
def view
|
||||
|
||||
def thisMVC
|
||||
def syncTimers = [:]
|
||||
|
||||
void mvcGroupInit(Map args) {
|
||||
|
||||
def configFile
|
||||
|
||||
logger.trace("Initializing TimeStamperMain MVC...")
|
||||
|
||||
// init mvc map
|
||||
thisMVC = ['model': model, 'view': view, 'controller': this]
|
||||
|
||||
model.notesDialogMVC = buildMVCGroup('NotesDialog', 'notesDialog',
|
||||
'mainMVC': thisMVC)
|
||||
|
||||
model.punchcardDialogMVC = buildMVCGroup('PunchcardDialog',
|
||||
'punchcardDialog', 'mainMVC': thisMVC)
|
||||
|
||||
// load application properties
|
||||
String userHomeDir = System.getProperty('user.home')
|
||||
configFile = new File(userHomeDir, ".timestamperrc")
|
||||
|
||||
logger.trace("Reading configuration from {}", configFile.canonicalPath)
|
||||
|
||||
model.config = new SmartConfig(configFile)
|
||||
|
||||
// load the last used timeline file
|
||||
String lastUsed = model.config.lastUsed
|
||||
if (lastUsed == "") {
|
||||
lastUsed = 'timeline.default.properties'
|
||||
model.config.setProperty('lastUsed', lastUsed)
|
||||
}
|
||||
|
||||
// load the plugin directory
|
||||
File pluginDir = model.config.getProperty("pluginDir", new File("plugins"))
|
||||
if (!pluginDir.exists()) pluginDir.mkdirs()
|
||||
|
||||
logger.trace("Adding plugin classpath: '{}'", pluginDir.canonicalPath)
|
||||
|
||||
def pluginLoader = new GroovyClassLoader(this.class.classLoader)
|
||||
pluginLoader.addURL(pluginDir.toURI().toURL())
|
||||
pluginDir.eachFileMatch(/.*\.jar/) { jarfile ->
|
||||
pluginLoader.addURL(jarfile.toURI().toURL()) }
|
||||
|
||||
// instantiate plugins
|
||||
model.config."plugin.classes".split(',').each { className -> try {
|
||||
if (className.trim() == "") return
|
||||
def pluginClass = pluginLoader.loadClass(className)
|
||||
model.plugins <<pluginClass.newInstance()
|
||||
|
||||
logger.trace("Loaded plugin: {}", className)
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
logger.warn("Unable to load plugin ${className}: " +
|
||||
"class not found.", cnfe)
|
||||
} catch (Throwable t) {
|
||||
logger.warn("Unable to load plugin ${className}.", t)
|
||||
} }
|
||||
|
||||
// run plugin startup hook
|
||||
model.plugins.each { plugin ->
|
||||
wrapPluginCall { plugin.onStartup(thisMVC) } }
|
||||
|
||||
logger.trace("Reading Timeline properties from '{}'", lastUsed)
|
||||
|
||||
load(new File(lastUsed))
|
||||
}
|
||||
|
||||
def wrapPluginCall(Closure c) {
|
||||
try { c() } catch (Throwable t) {
|
||||
logger.warn("Plugin threw an exception in ${functionName} " +
|
||||
"when passed ${param}:", t)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
def load = { File propertiesFile ->
|
||||
// pass through plugins
|
||||
propertiesFile = model.plugins.inject(propertiesFile) { file, plugin ->
|
||||
// call plugin
|
||||
def ret = wrapPluginCall { plugin.onTimelineLoad(thisMVC, file) }
|
||||
// if the plugin call succeeded, pass the result, else pass
|
||||
// the last one
|
||||
ret ? ret : file
|
||||
}
|
||||
|
||||
try {
|
||||
model.config.lastUsed = propertiesFile.canonicalPath
|
||||
} catch (IOException ioe) { logger.error(ioe) }
|
||||
|
||||
// load the properties file
|
||||
model.timelineProperties = new TimelineProperties(propertiesFile)
|
||||
|
||||
// load the main timeline
|
||||
model.timeline = model.timelineProperties.timeline
|
||||
|
||||
// load the last marker
|
||||
model.currentMarker = model.timeline.getLastMarker(new Date())
|
||||
}
|
||||
|
||||
def saveas = {
|
||||
|
||||
}
|
||||
|
||||
def exitGracefully = { evt = null ->
|
||||
|
||||
// hide the frame immediately
|
||||
view.frame.visible = false
|
||||
|
||||
logger.trace("Exiting gracefully.")
|
||||
|
||||
// save config
|
||||
logger.debug("Config: {}", model.config)
|
||||
model.config.save()
|
||||
|
||||
// save timeline and properties
|
||||
model.timelineProperties.save()
|
||||
|
||||
// call plugin exit hooks
|
||||
model.plugins.each { plugin ->
|
||||
wrapPluginCall { plugin.onExit(thisMVC) } }
|
||||
|
||||
logger.trace("Completed graceful shutdown.")
|
||||
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
def newTask = { mark, notes = "No comments.", timestamp = new Date() ->
|
||||
TimelineMarker tm = new TimelineMarker(timestamp, mark, notes)
|
||||
|
||||
// pass through the plugins
|
||||
tm = model.plugins.inject(tm) { marker, plugin ->
|
||||
def ret = wrapPluginCall { plugin.onNewTask(thisMVC, marker) }
|
||||
ret ? ret : marker
|
||||
}
|
||||
|
||||
model.timeline.addMarker(tm)
|
||||
model.currentMarker = model.timeline.getLastMarker(new Date())
|
||||
|
||||
// auto-persist if enabled
|
||||
if (model.timelineProperties.persistOnUpdate)
|
||||
model.timelineProperties.save()
|
||||
}
|
||||
|
||||
def deleteTask = { marker ->
|
||||
// pass through the plugins
|
||||
model.plugins.each { plugin ->
|
||||
wrapPluginCall { plugin.onDeleteTask(thisMVC, marker) } }
|
||||
|
||||
model.timeline.removeMarker(marker)
|
||||
model.currentMarker = model.timeline.getLastMarker(new Date())
|
||||
|
||||
// auto-persist if enabled
|
||||
if (model.timelineProperties.persistOnUpdate)
|
||||
model.timelineProperties.save()
|
||||
}
|
||||
|
||||
}
|
0
gui/griffon-app/i18n/messages.properties
Executable file
0
gui/griffon-app/i18n/resources.properties
Normal file
22
gui/griffon-app/lifecycle/Initialize.groovy
Executable file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This script is executed inside the EDT, so be sure to
|
||||
* call long running code in another thread.
|
||||
*
|
||||
* You have the following options
|
||||
* - SwingBuilder.doOutside { // your code }
|
||||
* - Thread.start { // your code }
|
||||
* - SwingXBuilder.withWorker( start: true ) {
|
||||
* onInit { // initialization (optional, runs in current thread) }
|
||||
* work { // your code }
|
||||
* onDone { // finish (runs inside EDT) }
|
||||
* }
|
||||
*
|
||||
* You have the following options to run code again inside EDT
|
||||
* - SwingBuilder.doLater { // your code }
|
||||
* - SwingBuilder.edt { // your code }
|
||||
* - SwingUtilities.invokeLater { // your code }
|
||||
*/
|
||||
|
||||
import groovy.swing.SwingBuilder
|
||||
|
||||
SwingBuilder.lookAndFeel('system', 'nimbus', ['metal', [boldFonts: false]])
|
18
gui/griffon-app/lifecycle/Ready.groovy
Executable file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* This script is executed inside the EDT, so be sure to
|
||||
* call long running code in another thread.
|
||||
*
|
||||
* You have the following options
|
||||
* - SwingBuilder.doOutside { // your code }
|
||||
* - Thread.start { // your code }
|
||||
* - SwingXBuilder.withWorker( start: true ) {
|
||||
* onInit { // initialization (optional, runs in current thread) }
|
||||
* work { // your code }
|
||||
* onDone { // finish (runs inside EDT) }
|
||||
* }
|
||||
*
|
||||
* You have the following options to run code again inside EDT
|
||||
* - SwingBuilder.doLater { // your code }
|
||||
* - SwingBuilder.edt { // your code }
|
||||
* - SwingUtilities.invokeLater { // your code }
|
||||
*/
|
18
gui/griffon-app/lifecycle/Shutdown.groovy
Executable file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* This script is executed inside the EDT, so be sure to
|
||||
* call long running code in another thread.
|
||||
*
|
||||
* You have the following options
|
||||
* - SwingBuilder.doOutside { // your code }
|
||||
* - Thread.start { // your code }
|
||||
* - SwingXBuilder.withWorker( start: true ) {
|
||||
* onInit { // initialization (optional, runs in current thread) }
|
||||
* work { // your code }
|
||||
* onDone { // finish (runs inside EDT) }
|
||||
* }
|
||||
*
|
||||
* You have the following options to run code again inside EDT
|
||||
* - SwingBuilder.doLater { // your code }
|
||||
* - SwingBuilder.edt { // your code }
|
||||
* - SwingUtilities.invokeLater { // your code }
|
||||
*/
|
19
gui/griffon-app/lifecycle/Startup.groovy
Executable file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* This script is executed inside the EDT, so be sure to
|
||||
* call long running code in another thread.
|
||||
*
|
||||
* You have the following options
|
||||
* - SwingBuilder.doOutside { // your code }
|
||||
* - Thread.start { // your code }
|
||||
* - SwingXBuilder.withWorker( start: true ) {
|
||||
* onInit { // initialization (optional, runs in current thread) }
|
||||
* work { // your code }
|
||||
* onDone { // finish (runs inside EDT) }
|
||||
* }
|
||||
*
|
||||
* You have the following options to run code again inside EDT
|
||||
* - SwingBuilder.doLater { // your code }
|
||||
* - SwingBuilder.edt { // your code }
|
||||
* - SwingUtilities.invokeLater { // your code }
|
||||
*/
|
||||
|
0
gui/griffon-app/lifecycle/Stop.groovy
Executable file
@ -0,0 +1,7 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import groovy.beans.Bindable
|
||||
|
||||
class LogDialogModel {
|
||||
def mainMVC
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import groovy.beans.Bindable
|
||||
|
||||
class NotesDialogModel {
|
||||
|
||||
// needs to be injected by buildMVCGroup call
|
||||
def mainMVC
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import groovy.beans.Bindable
|
||||
|
||||
class PunchcardDialogModel {
|
||||
|
||||
// needs to be injected by buildMVCGroup() call
|
||||
def mainMVC
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import groovy.beans.Bindable
|
||||
import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import java.util.Properties
|
||||
import com.jdbernard.util.SmartConfig
|
||||
import com.jdblabs.timestamper.core.Timeline
|
||||
import com.jdblabs.timestamper.core.TimelineMarker
|
||||
import com.jdblabs.timestamper.core.TimelineProperties
|
||||
|
||||
class TimeStamperMainModel {
|
||||
@Bindable TimelineMarker currentMarker
|
||||
@Bindable Timeline timeline
|
||||
@Bindable TimelineProperties timelineProperties
|
||||
SmartConfig config
|
||||
|
||||
List plugins = []
|
||||
|
||||
def notesDialogMVC
|
||||
def punchcardDialogMVC
|
||||
|
||||
@Bindable Point absoluteLocation
|
||||
@Bindable Rectangle frameSize
|
||||
}
|
BIN
gui/griffon-app/resources/12-em-cross.png
Normal file
After Width: | Height: | Size: 357 B |
BIN
gui/griffon-app/resources/16-em-cross-hover.png
Normal file
After Width: | Height: | Size: 506 B |
BIN
gui/griffon-app/resources/16-em-cross.png
Normal file
After Width: | Height: | Size: 469 B |
BIN
gui/griffon-app/resources/16-em-pencil.png
Normal file
After Width: | Height: | Size: 257 B |
BIN
gui/griffon-app/resources/16-file-archive.png
Normal file
After Width: | Height: | Size: 293 B |
BIN
gui/griffon-app/resources/16-tool-a-hover.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
gui/griffon-app/resources/16-tool-a.png
Normal file
After Width: | Height: | Size: 275 B |
BIN
gui/griffon-app/resources/24-em-cross.png
Normal file
After Width: | Height: | Size: 626 B |
BIN
gui/griffon-app/resources/24-message-info.png
Normal file
After Width: | Height: | Size: 746 B |
BIN
gui/griffon-app/resources/appointment-new-16x16.png
Executable file
After Width: | Height: | Size: 897 B |
BIN
gui/griffon-app/resources/appointment-new-32x32.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
gui/griffon-app/resources/delete-marker.png
Normal file
After Width: | Height: | Size: 321 B |
BIN
gui/griffon-app/resources/document-open-16x16.png
Normal file
After Width: | Height: | Size: 537 B |
BIN
gui/griffon-app/resources/document-save-16x16.png
Normal file
After Width: | Height: | Size: 911 B |
BIN
gui/griffon-app/resources/document-save-as-16x16.png
Normal file
After Width: | Height: | Size: 866 B |
BIN
gui/griffon-app/resources/griffon-icon-128x128.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
gui/griffon-app/resources/griffon-icon-16x16.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
gui/griffon-app/resources/griffon-icon-24x24.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
gui/griffon-app/resources/griffon-icon-256x256.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
gui/griffon-app/resources/griffon-icon-32x32.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
gui/griffon-app/resources/griffon-icon-48x48.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
gui/griffon-app/resources/griffon-icon-64x64.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
gui/griffon-app/resources/griffon.png
Normal file
After Width: | Height: | Size: 15 KiB |
20
gui/griffon-app/resources/logback.groovy
Normal file
@ -0,0 +1,20 @@
|
||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
|
||||
import ch.qos.logback.core.ConsoleAppender
|
||||
import ch.qos.logback.core.FileAppender
|
||||
import static ch.qos.logback.classic.Level.*
|
||||
|
||||
appender("CONSOLE", ConsoleAppender) {
|
||||
encoder(PatternLayoutEncoder) {
|
||||
pattern = "%level - %msg%n"
|
||||
}
|
||||
}
|
||||
|
||||
appender("FILE", FileAppender) {
|
||||
file="timestamper.log"
|
||||
encoder(PatternLayoutEncoder) {
|
||||
pattern = "%date %level %logger - %msg%n"
|
||||
}
|
||||
}
|
||||
|
||||
root(WARN, ["CONSOLE"])
|
||||
logger("com.jdblabs.*", TRACE, ["FILE"], false)
|
BIN
gui/griffon-app/resources/media-skip-backward.png
Normal file
After Width: | Height: | Size: 770 B |
BIN
gui/griffon-app/resources/media-skip-forward.png
Normal file
After Width: | Height: | Size: 771 B |
BIN
gui/griffon-app/resources/new-marker.png
Normal file
After Width: | Height: | Size: 345 B |
BIN
gui/griffon-app/resources/next-day.png
Normal file
After Width: | Height: | Size: 660 B |
BIN
gui/griffon-app/resources/next-week.png
Normal file
After Width: | Height: | Size: 782 B |
BIN
gui/griffon-app/resources/previous-day.png
Normal file
After Width: | Height: | Size: 636 B |
BIN
gui/griffon-app/resources/previous-week.png
Normal file
After Width: | Height: | Size: 764 B |
1880
gui/griffon-app/session.vim
Normal file
@ -0,0 +1,10 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import javax.swing.JDialog
|
||||
|
||||
dialog = dialog(new JDialog(model.mainMVC.view.frame),
|
||||
title: 'Error Messages...',
|
||||
modal: false) {
|
||||
|
||||
logger.trace( "Building LogDialog view." )
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import java.awt.Color
|
||||
import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import java.awt.Toolkit
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.JDialog
|
||||
import net.miginfocom.swing.MigLayout
|
||||
|
||||
Point mousePressRelativeToDialog
|
||||
Point offsetFromMainFrame = new Point(0,0)
|
||||
boolean coupledToMainFrame = false
|
||||
|
||||
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: 'Notes',
|
||||
modal: false,
|
||||
undecorated: true,
|
||||
minimumSize: [325, 200],
|
||||
mousePressed: mousePressed,
|
||||
mouseDragged: mouseDragged,
|
||||
iconImage: imageIcon('/16-em-pencil.png').image,
|
||||
iconImages: [imageIcon('/16-em-pencil.png').image],
|
||||
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.trace('Building NotesDialog GUI')
|
||||
panel(
|
||||
border:lineBorder(color: Color.BLACK, thickness:1, parent:true),
|
||||
layout: new MigLayout('insets 10 10 10 10, fill')
|
||||
) {
|
||||
scrollPane(constraints: 'growx, growy') {
|
||||
notesTextArea = textArea(lineWrap: true, columns: 20, rows: 5,
|
||||
wrapStyleWord: true,
|
||||
text: bind(source: model.mainMVC.model,
|
||||
sourceProperty: 'currentMarker',
|
||||
sourceValue: { model.mainMVC.model.currentMarker?.notes}),
|
||||
keyReleased: {
|
||||
if (model.mainMVC.model.currentMarker != null)
|
||||
model.mainMVC.model.currentMarker.notes =
|
||||
notesTextArea.text})
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
import java.awt.Color
|
||||
import java.awt.Point
|
||||
import java.awt.Toolkit
|
||||
import java.awt.Rectangle
|
||||
import java.beans.PropertyChangeListener
|
||||
import java.text.SimpleDateFormat
|
||||
import javax.swing.JDialog
|
||||
import java.util.Calendar
|
||||
import javax.swing.SwingConstants
|
||||
import com.jdblabs.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,
|
||||
undecorated: true,
|
||||
mousePressed: mousePressed,
|
||||
mouseDragged: mouseDragged,
|
||||
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.trace('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'))
|
||||
|
||||
model.mainMVC.model.addPropertyChangeListener('currentMarker', {
|
||||
dayDisplay.stateChanged(null) } as PropertyChangeListener)
|
||||
|
||||
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 }))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
package com.jdblabs.timestamper.gui
|
||||
|
||||
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.JDialog
|
||||
import javax.swing.JFileChooser
|
||||
import javax.swing.SwingConstants
|
||||
import javax.swing.Timer
|
||||
import com.jdblabs.timestamper.core.Timeline
|
||||
import net.miginfocom.swing.MigLayout
|
||||
|
||||
/* ========== *
|
||||
* 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
|
||||
}
|
||||
|
||||
showNotes = { evt = null ->
|
||||
model.notesDialogMVC.view.dialog.visible = notesVisibleButton.selected
|
||||
}
|
||||
|
||||
showPunchcard = { evt = null ->
|
||||
model.punchcardDialogMVC.view.dialog.visible = punchcardVisibleButton.selected
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
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) })
|
||||
checkBoxMenuItem(text: 'Save on update?',
|
||||
selected: bind(source: model, sourceProperty: 'timelineProperties',
|
||||
sourceValue: { model.timelineProperties?.persistOnUpdate }),
|
||||
actionPerformed: {
|
||||
model.timelineProperties.persistOnUpdate = it.source.selected })
|
||||
aboutMenuItem = checkBoxMenuItem(text: 'About...',
|
||||
actionPerformed: { aboutDialog.visible = aboutMenuItem.selected })
|
||||
}
|
||||
|
||||
fileDialog = fileChooser();
|
||||
|
||||
frame = application(title:'TimeStamper',
|
||||
location:[50,50],
|
||||
locationByPlatform:true,
|
||||
minimumSize: [325, 0],
|
||||
pack:true,
|
||||
undecorated:true,
|
||||
iconImage: imageIcon('/appointment-new-32x32.png').image,
|
||||
iconImages: [imageIcon('/appointment-new-32x32.png').image,
|
||||
imageIcon('/appointment-new-16x16.png').image],
|
||||
componentMoved: { evt -> model.absoluteLocation = frame.location }
|
||||
) {
|
||||
logger.trace('Building TimeStamperMain GUI')
|
||||
panel(
|
||||
border:lineBorder(color:Color.BLACK, thickness:1, parent:true),
|
||||
layout: new MigLayout('insets 0 5 0 0, fill','', '[]0[]0[]'),
|
||||
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(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(mouseClicked: { evt ->
|
||||
optionsMenu.show(evt.component, evt.x, evt.y) },
|
||||
icon: imageIcon('/16-tool-a.png'),
|
||||
rolloverIcon: imageIcon('/16-tool-a-hover.png'),
|
||||
border: emptyBorder(0),
|
||||
contentAreaFilled: false,
|
||||
hideActionText: true,
|
||||
toolTipText: 'Options Menu')
|
||||
button(actionPerformed: controller.&exitGracefully,
|
||||
icon: imageIcon('/16-em-cross.png'),
|
||||
rolloverIcon: imageIcon('/16-em-cross-hover.png'),
|
||||
border: emptyBorder(0),
|
||||
contentAreaFilled: false,
|
||||
hideActionText: true,
|
||||
toolTipText: 'Close Application')
|
||||
}
|
||||
|
||||
taskTextField = textField("Task name",
|
||||
constraints: "growx, span 2, w 250::",
|
||||
keyReleased: taskTextFieldChanged,
|
||||
toolTipText: 'The current task',
|
||||
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(
|
||||
actionPerformed: showNotes,
|
||||
icon: imageIcon('/16-em-pencil.png'),
|
||||
hideActionText: true,
|
||||
border: emptyBorder(4),
|
||||
toolTipText: 'Show/hide task notes.')
|
||||
punchcardVisibleButton = toggleButton(
|
||||
actionPerformed: showPunchcard,
|
||||
icon: imageIcon('/16-file-archive.png'),
|
||||
hideActionText: true,
|
||||
border: emptyBorder(4),
|
||||
toolTipText: 'Show/hide the timeline display.')
|
||||
}
|
||||
|
||||
totalTimeLabel = label("", constraints: 'alignx leading',
|
||||
font: timeFont, foreground: [0, 153, 0])
|
||||
|
||||
currentTimeLabel = label("00:00:00", constraints: 'align trailing',
|
||||
font: timeFont, foreground: [204, 0, 0])
|
||||
}
|
||||
}
|
||||
|
||||
aboutDialog = dialog(new JDialog(frame),
|
||||
visible: false,
|
||||
locationRelativeTo: null,
|
||||
pack: true,
|
||||
undecorated: true,
|
||||
title: "About TimeStamper v" + app.metadata.'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.metadata.'app.version'
|
||||
+ " by Jonathan Bernard", constraints: 'growx, wrap',
|
||||
horizontalAlignment: SwingConstants.CENTER)
|
||||
textField(text: 'http://www.jdb-labs.com/timestamper',
|
||||
constraints: 'growx', foreground: [0,0,200], editable: false,
|
||||
horizontalAlignment: SwingConstants.CENTER)
|
||||
}
|
||||
}
|
165
gui/griffonw
Normal file
@ -0,0 +1,165 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Griffon start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRIFFON_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Griffon"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/"
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED"
|
||||
|
||||
CLASSPATH=$APP_HOME/wrapper/griffon-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add APP_NAME to the JAVA_OPTS as -Xdock:name
|
||||
if $darwin; then
|
||||
JAVA_OPTS="$JAVA_OPTS -Xdock:name=$APP_NAME"
|
||||
# we may also want to set -Xdock:image
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRIFFON_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRIFFON_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRIFFON_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRIFFON_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GriffonWrapperMain "$@"
|
90
gui/griffonw.bat
Normal file
@ -0,0 +1,90 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Griffon startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRIFFON_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\wrapper\griffon-wrapper.jar
|
||||
|
||||
@rem Execute Griffon
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRIFFON_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GriffonWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRIFFON_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRIFFON_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|