Compare commits
108 Commits
Author | SHA1 | Date | |
---|---|---|---|
1064de3e1b | |||
8b0c751344 | |||
0f7e257f76 | |||
76225d1c50 | |||
0361d1b869 | |||
40cb602362 | |||
e2a306c1d6 | |||
e955cd5b24 | |||
4176dfea3a | |||
587e3c4509 | |||
f6a97c384a | |||
0c3d73dc2b | |||
9a0bf35882 | |||
be7c099b7b | |||
d04797460c | |||
8cf0bf5d98 | |||
ddad90ddef | |||
34ce2b61b9 | |||
661d5959c6 | |||
6665f09b7b | |||
bcb1c7c17c | |||
b0e3f5a9d8 | |||
fee4ba70a6 | |||
171adbb59d | |||
d01d6e37f4 | |||
b98596574d | |||
ea9f8ea7ac | |||
ae4a943e82 | |||
58a5321d95 | |||
7215b4969b | |||
c7891de310 | |||
a373af0658 | |||
de3ee05680 | |||
59440d2c9d | |||
6226ff21c5 | |||
71e035fdbe | |||
df854f864c | |||
7bccd83e23 | |||
b25d2be164 | |||
e0ab3cb401 | |||
d93c0cf348 | |||
9606e71cec | |||
98f4dda1ad | |||
393be347c9 | |||
f8fed9d937 | |||
ef16eafd48 | |||
4af0d09356 | |||
071c4b66e5 | |||
57a3af4f2f | |||
08b9df2086 | |||
339e88cddd | |||
0a2249018b | |||
ec3008937d | |||
10fcc34ea2 | |||
4127fbe41c | |||
0671d7728e | |||
7b5f26f24a | |||
|
db3e648d47 | ||
476a94c679 | |||
65edc56e08 | |||
d4db66a71e | |||
f8ccc831ef | |||
|
93a0a15f12 | ||
|
dc31d590a0 | ||
|
8b46cc19d8 | ||
|
567c2d2178 | ||
|
08dfbde57f | ||
|
a924d7b649 | ||
|
2404f6a3d1 | ||
|
2b5f82203c | ||
|
29959a6a8d | ||
|
6f247032a3 | ||
|
efd5f6adff | ||
|
49c5753ef1 | ||
|
3bdb2ecb1f | ||
|
28569a643e | ||
|
97eb286e32 | ||
|
fcab7a4cc6 | ||
|
20c82ea9ba | ||
|
11b18317bd | ||
|
d86da67284 | ||
|
063a869b51 | ||
|
46d4db0d6a | ||
|
34e01119a9 | ||
|
d6880d9cc1 | ||
|
582bf819f5 | ||
|
76d1e48ebb | ||
|
4fd297e03d | ||
|
d0e968b2b7 | ||
|
c0b02ca222 | ||
|
85753de955 | ||
|
0441f3c510 | ||
|
c01eaa0255 | ||
|
ec7c07f81f | ||
|
952064d903 | ||
|
31b9802477 | ||
|
ae0d782a5b | ||
|
846d1edc74 | ||
|
fd94f0e41a | ||
|
6f58a83ad4 | ||
|
47cf3cf0a4 | ||
|
e00e2e296e | ||
|
c26ba17dbb | ||
|
447e74f956 | ||
|
66b68160e5 | ||
|
f86316c68f | ||
|
5ff4665a07 | ||
|
faacfd859a |
4
.gitignore
vendored
@ -1,2 +1,4 @@
|
|||||||
*.sw*
|
*.sw*
|
||||||
*/build/
|
nimcache/
|
||||||
|
/pit
|
||||||
|
/pit_api
|
||||||
|
2
.mise.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[tools]
|
||||||
|
nim = "2.2.0"
|
138
README.md
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# Personal Issue Tracker
|
||||||
|
|
||||||
|
This is [Jonathan Bernard's](mailto:jonathan@jdbernard.com) personal issue
|
||||||
|
tracker. In it's current form it is essentially a way to keep an curated list of
|
||||||
|
TODO's, organizing them by workflow category (todo, todo-today, dormant, etc.)
|
||||||
|
and context (Personal, Work, etc.).
|
||||||
|
|
||||||
|
## Categories
|
||||||
|
|
||||||
|
`pit` organizes issues into the following workflow categories:
|
||||||
|
|
||||||
|
- `current` - actively in progress
|
||||||
|
- `todo` - to be addressed in the future
|
||||||
|
- `todo-today` - chosen to be addressed today
|
||||||
|
- `pending` - blocked by some third party
|
||||||
|
- `dormant` - long-term things I don't want to forget but don't need in front
|
||||||
|
of me every day.
|
||||||
|
- `done`
|
||||||
|
|
||||||
|
In my typical workflow the `todo` category serves as a collection point for
|
||||||
|
things I want to keep track of. Then on a a daily basis I review issues in the
|
||||||
|
`todo` category and move a selection to the `todo-today` category. I also try
|
||||||
|
to keep the total number of issues in the `todo` below about a dozen. If there
|
||||||
|
are more than a dozen things in my `todo` category I will identify the lowest
|
||||||
|
priority items and move them to the `dormant` category.
|
||||||
|
|
||||||
|
## Issue Properties
|
||||||
|
|
||||||
|
`pit` allows arbitrary properties to be attached to issues in the form of
|
||||||
|
key-value pairs. On the command line these can be provided via the `-p` or
|
||||||
|
`--properties` parameter in the form
|
||||||
|
`-p <prop1Name>:<prop1Value>;<prop2Name>:<prop2Value>[;...]`
|
||||||
|
|
||||||
|
There are a couple of properties that pit will recognize automatically:
|
||||||
|
|
||||||
|
- `context`: the context organization feature is implemented using issue
|
||||||
|
properties.
|
||||||
|
- `created`: `pit` uses this property to timestamp an issue when it is created.
|
||||||
|
- `completed`: `pit` uses this property to timestamp an issue when it is moved
|
||||||
|
to the `done` category.
|
||||||
|
- `pending`: `pit` looks to this property to provide extra information about
|
||||||
|
issues in the `pending` category. Typically I use this to note who or what is
|
||||||
|
blocking the issue and why.
|
||||||
|
|
||||||
|
Some other common properties I use are:
|
||||||
|
|
||||||
|
- `resolution`: for short notes about why an issue was moved to `done`,
|
||||||
|
especially if it the action wasn't taken or if it is not completely clear
|
||||||
|
that this issue was completed.
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
`pit` allows configuration via command-line options and via a configuration
|
||||||
|
file. There is some overlap between the two methods of configuring `pit`, but
|
||||||
|
it is not a complete mapping.
|
||||||
|
|
||||||
|
### Config File
|
||||||
|
|
||||||
|
`pit` looks for a JSON configuration file in the following places (in order):
|
||||||
|
|
||||||
|
1. From a file path passed on the command line via the `--config <cfgFile>` parameter,
|
||||||
|
2. `./.pitrc`, in the current working directory,
|
||||||
|
3. From a file path set in the `PITRC` environment variable.
|
||||||
|
4. `$HOME/.pitrc`, in the user's home directory.
|
||||||
|
|
||||||
|
|
||||||
|
#### Sample Config File
|
||||||
|
|
||||||
|
This example illustrates all of the possible configuration options.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"api": {
|
||||||
|
"apiKeys": [
|
||||||
|
"50cdcb660554e2d50fd88bd40b6579717bf00643f6ff57f108baf16c8c083f77",
|
||||||
|
"e4fc1aac49fc1f2f7f4ca6b1f04d41a4ccdd58e13bb53b41da97703d47267ceb",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"defaultContext": "personal",
|
||||||
|
"verbose": false,
|
||||||
|
"termWidth": 120,
|
||||||
|
"triggerPtk": true
|
||||||
|
},
|
||||||
|
"contexts": {
|
||||||
|
"nla-music": "New Life Music",
|
||||||
|
"nla-youth-band": "New Life Youth Band",
|
||||||
|
"acn": "Accenture",
|
||||||
|
"hff": "Hope Family Fellowship"
|
||||||
|
},
|
||||||
|
"tasksDir": "/mnt/c/Users/Jonathan Bernard/synced/tasks"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Explanation of configurable options.
|
||||||
|
|
||||||
|
In general, options supplied on the CLI directly will override options supplied
|
||||||
|
in the configuration file. All options are optional unless stated otherwise.
|
||||||
|
|
||||||
|
* `api`: configuration options specific to the API service.
|
||||||
|
|
||||||
|
- `apiKeys`: a list of Bearer tokens accepted by the API for the purpose of
|
||||||
|
authenticating API requests.
|
||||||
|
|
||||||
|
* `cli`: configuration options specific to the CLI.
|
||||||
|
|
||||||
|
- `defaultContext`: if present all invokations to the CLI will
|
||||||
|
be in this context. This is like adding a `--context <defaultContext>`
|
||||||
|
parameter to every CLI invocation. Any actual `--context` parameter will
|
||||||
|
override this value.
|
||||||
|
|
||||||
|
- `verbose`: Show issue details when listing issues (same as
|
||||||
|
`--verbose` flag).
|
||||||
|
|
||||||
|
- `termWidth`: Set the expected width of the terminal (for wrapping text).
|
||||||
|
|
||||||
|
- `triggerPtk`: If set to `true`, invoke the `ptk` command to start and stop
|
||||||
|
timers when issues move to the `current` and `done` categories
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
* `contexts`: `pit` allows issues to be organized into different contexts via
|
||||||
|
a `context` property on the issue. The CLI groups issues according to
|
||||||
|
context. When printing contexts the CLI will take the value from the issues'
|
||||||
|
`context` properties and capatalize it. In some cases you may wish to have a
|
||||||
|
different display value for a context. I like to use abbreviations for long
|
||||||
|
context names to reduce the need to type, `hff` for "Hope Family Fellowship",
|
||||||
|
for example. The `contexts` config option allows you to provide a map of
|
||||||
|
context values to context display names See the sample file below for an
|
||||||
|
example.
|
||||||
|
|
||||||
|
Note that this mapping does not have to have entries for all contexts, only
|
||||||
|
those you wish to provide with an alternate display form. For example, in the
|
||||||
|
configuration sample above the default context is `personal`, a value not
|
||||||
|
present in the `contexts` configuration. `personal` will be displayed as
|
||||||
|
"Personal"; it does not need an alternate display name.
|
||||||
|
|
||||||
|
* `tasksDir` **required**: a file path to the root directory for the issue
|
||||||
|
repository (same as `--tasks-dir` CLI parameter).
|
35
build.xml
@ -1,35 +0,0 @@
|
|||||||
<project name="Personal Issue Tracker" default="package">
|
|
||||||
<property file="version.properties"/>
|
|
||||||
<property environment="env"/>
|
|
||||||
|
|
||||||
<property
|
|
||||||
name="libpit.jar"
|
|
||||||
value="libpit/release/pit-${application.version}.jar"/>
|
|
||||||
|
|
||||||
<target name="clean">
|
|
||||||
<ant dir="libpit" target="clean" inheritAll="false"/>
|
|
||||||
<ant dir="pit-cli" target="clean" inheritAll="false"/>
|
|
||||||
<ant dir="pit-swing" target="clean" inheritAll="false"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="libpit">
|
|
||||||
<ant dir="libpit" target="release" inheritAll="false"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="pit-cli" depends="libpit">
|
|
||||||
<copy file="${libpit.jar}" todir="pit-cli/lib"/>
|
|
||||||
<ant dir="pit-cli" target="release" inheritAll="false"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="pit-swing" depends="libpit">
|
|
||||||
<copy file="${libpit.jar}" todir="pit-swing/lib"/>
|
|
||||||
<ant dir="pit-swing" fork="true" target="package" inheritAll="false"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="package" depends="libpit,pit-cli,pit-swing">
|
|
||||||
<mkdir dir="release/lib"/>
|
|
||||||
<copy file="pit-cli/release/pit-clii-${application.version}.jar" todir="release"/>
|
|
||||||
<copy file="pit-swing/dist/jar/pit-swing.jar" tofile="release/pit-swing-${application.version}.jar"/>
|
|
||||||
<copy file="libpit/release/pit-${application.version}.jar" todir="release/lib"/>
|
|
||||||
</target>
|
|
||||||
</project>
|
|
111
libpit/build.xml
@ -1,111 +0,0 @@
|
|||||||
<project name="Personal Issue Tracker" default="release">
|
|
||||||
<property file="../version.properties"/>
|
|
||||||
<property file="project.properties"/>
|
|
||||||
<property environment="env"/>
|
|
||||||
|
|
||||||
<path id="groovy.libs">
|
|
||||||
<fileset dir="${env.GROOVY_HOME}/lib">
|
|
||||||
<include name="**/*.jar"/>
|
|
||||||
</fileset>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<path id="groovyc.classpath">
|
|
||||||
<path refid="groovy.libs"/>
|
|
||||||
<fileset dir="${lib.dir}">
|
|
||||||
<include name="**/*.jar"/>
|
|
||||||
</fileset>
|
|
||||||
<pathelement path="${build.dir}/classes"/>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<path id="test.classpath">
|
|
||||||
<path refid="groovyc.classpath"/>
|
|
||||||
<pathelement path="${build.dir}/tests"/>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<taskdef name="groovyc"
|
|
||||||
classname="org.codehaus.groovy.ant.Groovyc"
|
|
||||||
classpathref="groovy.libs"/>
|
|
||||||
|
|
||||||
<target name="init">
|
|
||||||
<fail
|
|
||||||
unless="env.GROOVY_HOME"
|
|
||||||
message="GROOVY_HOME environment variable is not set."/>
|
|
||||||
<echo message="GROOVY_HOME: ${env.GROOVY_HOME}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="increment-build-number" depends="init">
|
|
||||||
<!-- Check to see if the application version has changed.
|
|
||||||
If it has, reset the build number to 0 -->
|
|
||||||
<condition property="build.number.final"
|
|
||||||
value="${build.number}"
|
|
||||||
else="0" >
|
|
||||||
<equals
|
|
||||||
arg1="${application.version}"
|
|
||||||
arg2="${expected.application.version}"/>
|
|
||||||
</condition>
|
|
||||||
|
|
||||||
<echo message="Version: ${application.version}"/>
|
|
||||||
<echo message="Build number: ${build.number.final}"/>
|
|
||||||
|
|
||||||
<!-- Write the actual application version and build number -->
|
|
||||||
<propertyfile file="project.properties">
|
|
||||||
<entry key="build.number" value="${build.number.final}"/>
|
|
||||||
<entry
|
|
||||||
key="expected.application.version"
|
|
||||||
value="${application.version}"/>
|
|
||||||
</propertyfile>
|
|
||||||
|
|
||||||
<!-- increment build number -->
|
|
||||||
<propertyfile file="project.properties">
|
|
||||||
<entry key="build.number" operation="+" type="int" default="0"/>
|
|
||||||
</propertyfile>
|
|
||||||
<property file="project.properties"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="clean">
|
|
||||||
<delete dir="${build.dir}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="compile" depends="init,increment-build-number">
|
|
||||||
<mkdir dir="${build.dir}/classes"/>
|
|
||||||
<groovyc
|
|
||||||
srcdir="${src.dir}"
|
|
||||||
destdir="${build.dir}/classes"
|
|
||||||
classpathref="groovyc.classpath"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="compile-tests" depends="init,compile">
|
|
||||||
<mkdir dir="${build.dir}/tests"/>
|
|
||||||
<groovyc
|
|
||||||
srcdir="${test.dir}"
|
|
||||||
destdir="${build.dir}/tests"
|
|
||||||
classpathref="groovyc.classpath"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="test" depends="compile-tests">
|
|
||||||
<junit fork="yes" haltonfailure="yes">
|
|
||||||
<classpath refid="test.classpath"/>
|
|
||||||
<formatter type="brief" usefile="false" />
|
|
||||||
<batchtest>
|
|
||||||
<fileset dir="${build.dir}/tests">
|
|
||||||
<include name="**/*Test.class"/>
|
|
||||||
</fileset>
|
|
||||||
</batchtest>
|
|
||||||
</junit>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="build" depends="compile,test">
|
|
||||||
<mkdir dir="${build.dir}/jar"/>
|
|
||||||
<jar
|
|
||||||
destfile="${build.dir}/jar/pit-${application.version}.${build.number.final}.jar"
|
|
||||||
basedir="${build.dir}/classes"
|
|
||||||
compress="on"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="release" depends="build">
|
|
||||||
<delete dir="${release.dir}"/>
|
|
||||||
<mkdir dir="${release.dir}"/>
|
|
||||||
<copy file="${build.dir}/jar/pit-${application.version}.${build.number.final}.jar"
|
|
||||||
tofile="${release.dir}/${release.jar}"/>
|
|
||||||
</target>
|
|
||||||
</project>
|
|
@ -1,11 +0,0 @@
|
|||||||
#Fri, 21 Oct 2011 16:18:33 -0500
|
|
||||||
#Sat Apr 24 17:08:00 CDT 2010
|
|
||||||
build.dir=build
|
|
||||||
src.dir=src
|
|
||||||
lib.shared.dir=../shared-libs
|
|
||||||
test.dir=test
|
|
||||||
build.number=11
|
|
||||||
expected.application.version=2.6.0
|
|
||||||
lib.dir=lib
|
|
||||||
release.dir=release
|
|
||||||
release.jar=pit-${application.version}.jar
|
|
1835
libpit/session.vim
@ -1,17 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
public enum Category {
|
|
||||||
BUG,
|
|
||||||
FEATURE,
|
|
||||||
TASK
|
|
||||||
|
|
||||||
public static Category toCategory(String s) {
|
|
||||||
for(c in Category.values())
|
|
||||||
if (c.name().startsWith(s.toUpperCase())) return c
|
|
||||||
throw new IllegalArgumentException("No category matches ${s}.")
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSymbol() { toString()[0].toLowerCase() }
|
|
||||||
|
|
||||||
public String toString() { return "${name()[0]}${name()[1..-1].toLowerCase()}" }
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
class Filter {
|
|
||||||
|
|
||||||
List<Category> categories = null
|
|
||||||
List<Status> status = null
|
|
||||||
List<String> projects = null
|
|
||||||
List<String> ids = null
|
|
||||||
int priority = 9
|
|
||||||
boolean acceptProjects = true
|
|
||||||
Closure issueSorter = defaultIssueSorter
|
|
||||||
Closure projectSorter = defaultProjectSorter
|
|
||||||
|
|
||||||
public static Closure defaultIssueSorter = { it.id.toInteger() }
|
|
||||||
public static Closure defaultProjectSorter = { it.name }
|
|
||||||
|
|
||||||
public boolean accept(Issue i) {
|
|
||||||
return (i.priority <= priority &&
|
|
||||||
(!categories || categories.contains(i.category)) &&
|
|
||||||
(!status || status.contains(i.status)) &&
|
|
||||||
(!ids || ids.contains(i.id)))
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean accept(Project p) {
|
|
||||||
return (acceptProjects &&
|
|
||||||
(!projects || projects.contains(p.name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean accept(String name) {
|
|
||||||
return (acceptProjects &&
|
|
||||||
(!projects || projects.contains(name)))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
public class FlatProjectView extends Project {
|
|
||||||
|
|
||||||
public FlatProjectView(String name) { super(name) }
|
|
||||||
|
|
||||||
public Issue createNewIssue(Map options) {
|
|
||||||
throw new UnsupportedOperationException("The FlatProjectView is " +
|
|
||||||
"read-only.")
|
|
||||||
}
|
|
||||||
|
|
||||||
public Project createNewProject(String name) {
|
|
||||||
throw new UnsupportedOperationException("The FlatProjectView is " +
|
|
||||||
"read-only.")
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean deleteIssue(Issue issue) { return false }
|
|
||||||
public boolean deleteProject(Project project) { return false }
|
|
||||||
|
|
||||||
public boolean delete() { return true }
|
|
||||||
|
|
||||||
public void eachIssue(Filter filter = null, Closure closure) {
|
|
||||||
def sorter = filter?.issueSorter ?: Filter.defaultIssueSorter
|
|
||||||
def gatherIssues
|
|
||||||
def gatheredIssues = []
|
|
||||||
|
|
||||||
gatherIssues = { project, f ->
|
|
||||||
project.eachIssue(f) { gatheredIssues << it }
|
|
||||||
project.eachProject(f) { gatherIssues(it, f) }
|
|
||||||
}
|
|
||||||
for (p in projects.values())
|
|
||||||
if (!filter || filter.accept(p))
|
|
||||||
gatherIssues(p, filter)
|
|
||||||
|
|
||||||
gatheredIssues.sort(sorter).each(closure)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
import java.lang.IllegalArgumentException as IAE
|
|
||||||
|
|
||||||
public abstract class Issue {
|
|
||||||
|
|
||||||
protected String id
|
|
||||||
protected Category category
|
|
||||||
protected Status status
|
|
||||||
protected int priority
|
|
||||||
protected String text
|
|
||||||
protected Date deliveryDate
|
|
||||||
protected Date creationDate
|
|
||||||
|
|
||||||
Issue(String id, Category c = Category.TASK, Status s = Status.NEW,
|
|
||||||
int p = 9) {
|
|
||||||
this.id = id
|
|
||||||
this.category = c
|
|
||||||
this.status = s
|
|
||||||
this.priority = p
|
|
||||||
this.creationDate = new Date()
|
|
||||||
this.deliveryDate = null
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() { return id; }
|
|
||||||
|
|
||||||
public Category getCategory() { return category }
|
|
||||||
|
|
||||||
public void setCategory(Category c) throws IOException {
|
|
||||||
if (c == null)
|
|
||||||
throw new IAE("Category cannot be null.")
|
|
||||||
|
|
||||||
this.category = c
|
|
||||||
}
|
|
||||||
|
|
||||||
public Status getStatus() { return status }
|
|
||||||
|
|
||||||
public void setStatus(Status s) throws IOException {
|
|
||||||
if (s == null)
|
|
||||||
throw new IAE("Status cannot be null.")
|
|
||||||
|
|
||||||
this.status = s
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPriority() { return priority }
|
|
||||||
|
|
||||||
public void setPriority(int p) throws IOException {
|
|
||||||
priority = Math.min(9, Math.max(0, p))
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle() { return text.readLines()[0] }
|
|
||||||
|
|
||||||
public String getText() { return text }
|
|
||||||
|
|
||||||
public void setText(String t) throws IOException { text = t }
|
|
||||||
|
|
||||||
public boolean hasDelivery() { return deliveryDate == null }
|
|
||||||
|
|
||||||
public Date getCreationDate() { return creationDate }
|
|
||||||
|
|
||||||
public Date getDeliveryDate() { return deliveryDate }
|
|
||||||
|
|
||||||
public void setDeliveryDate(Date dd) { deliveryDate = dd }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "${id}(${priority}-${status}): ${category} ${title}"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
public abstract class Project {
|
|
||||||
|
|
||||||
protected String name
|
|
||||||
Map<String, Issue> issues = [:]
|
|
||||||
Map<String, Project> projects = [:]
|
|
||||||
|
|
||||||
Project(String name) { this.name = name }
|
|
||||||
|
|
||||||
public void eachIssue(Filter filter = null, Closure c) {
|
|
||||||
def sorter = filter?.issueSorter ?: Filter.defaultIssueSorter
|
|
||||||
for (i in issues.values().sort(sorter))
|
|
||||||
if (!filter || filter.accept(i))
|
|
||||||
c.call(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
public void eachProject(Filter filter = null, Closure c) {
|
|
||||||
def sorter = filter?.projectSorter ?: Filter.defaultProjectSorter
|
|
||||||
for (p in projects.values().sort(sorter))
|
|
||||||
if (!filter || filter.accept(p))
|
|
||||||
c.call(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// walk every issue and project in this project recursively and execute the
|
|
||||||
// given closure on each issue that meets the filter criteria
|
|
||||||
public void walkProject(Filter filter, Closure c) {
|
|
||||||
this.eachIssue(filter, c)
|
|
||||||
this.eachProject(filter) { p -> p.walkProject(filter, c) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// This get all issues, including subissues
|
|
||||||
public List getAllIssues(Filter filter = null) {
|
|
||||||
List result = this.issues.findAll { filter.accept(it) }
|
|
||||||
this.eachProject(filter) { p -> result += p.getAllIssues(filter) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) { this.name = name }
|
|
||||||
|
|
||||||
public String getName() { return name }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String toString() { return name }
|
|
||||||
|
|
||||||
public abstract Issue createNewIssue(Map options)
|
|
||||||
|
|
||||||
public abstract Project createNewProject(String name)
|
|
||||||
|
|
||||||
public abstract boolean deleteIssue(Issue issue)
|
|
||||||
|
|
||||||
public abstract boolean deleteProject(Project project)
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
public abstract class Repository {
|
|
||||||
|
|
||||||
public abstract void persist()
|
|
||||||
public abstract Project[] getRootProjects()
|
|
||||||
public abstract Project createNewProject(String name)
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
public enum Status {
|
|
||||||
REASSIGNED('a'),
|
|
||||||
REJECTED('j'),
|
|
||||||
NEW('n'),
|
|
||||||
RESOLVED('s'),
|
|
||||||
VALIDATION_REQUIRED('v')
|
|
||||||
|
|
||||||
String symbol
|
|
||||||
|
|
||||||
protected Status(String s) { symbol = s }
|
|
||||||
|
|
||||||
public static Status toStatus(String str) {
|
|
||||||
Status retVal = null
|
|
||||||
for(status in Status.values()) {
|
|
||||||
if (status.symbol.equalsIgnoreCase(str) ||
|
|
||||||
status.name().startsWith(str.toUpperCase())) {
|
|
||||||
|
|
||||||
if (retVal != null)
|
|
||||||
throw new IllegalArgumentException("Request string is" +
|
|
||||||
" ambigous, '${str}' could represent ${retVal} or " +
|
|
||||||
"${status}, possibly others.")
|
|
||||||
|
|
||||||
retVal = status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retVal == null)
|
|
||||||
throw new IllegalArgumentException("No status matches '${str}'")
|
|
||||||
|
|
||||||
return retVal
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
def words = name().split("_")
|
|
||||||
String result = ""
|
|
||||||
words.each { result += "${it[0]}${it[1..-1].toLowerCase()} " }
|
|
||||||
return result[0..-2]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
package com.jdbernard.pit.file
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
import java.lang.IllegalArgumentException as IAE
|
|
||||||
|
|
||||||
public class FileIssue extends Issue {
|
|
||||||
|
|
||||||
protected File source
|
|
||||||
public static final String fileExp = /(\d+)([bft])([ajnsv])(\d).*/
|
|
||||||
|
|
||||||
public FileIssue(File file) {
|
|
||||||
|
|
||||||
super('REPLACE_ME')
|
|
||||||
|
|
||||||
def matcher = file.name =~ fileExp
|
|
||||||
if (!matcher)
|
|
||||||
throw new IllegalArgumentException("${file} " +
|
|
||||||
"is not a valid Issue file.")
|
|
||||||
|
|
||||||
super.@id = matcher[0][1]
|
|
||||||
super.@category = Category.toCategory(matcher[0][2])
|
|
||||||
super.@status = Status.toStatus(matcher[0][3])
|
|
||||||
super.@priority = matcher[0][4].toInteger()
|
|
||||||
|
|
||||||
this.source = file
|
|
||||||
|
|
||||||
super.@text = file.text
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCategory(Category c) throws IOException {
|
|
||||||
boolean renamed
|
|
||||||
renamed = source.renameTo(new File(source.canonicalFile.parentFile,
|
|
||||||
makeFilename(id, c, status, priority)))
|
|
||||||
|
|
||||||
if (!renamed)
|
|
||||||
throw new IOException("I was unable to set the category. "
|
|
||||||
+ "I need to rename the file for this issue, but something is "
|
|
||||||
+ "preventing me from doing so (maybe the path to the file is "
|
|
||||||
+ "no longer valid, or maybe the file is currently open in "
|
|
||||||
+ "some other program).")
|
|
||||||
else super.setCategory(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatus(Status s) throws IOException {
|
|
||||||
boolean renamed
|
|
||||||
renamed = source.renameTo(new File(source.canonicalFile.parentFile,
|
|
||||||
makeFilename(id, category, s, priority)))
|
|
||||||
|
|
||||||
if (!renamed)
|
|
||||||
throw new IOException("I was unable to set the status. "
|
|
||||||
+ "I need to rename the file for this issue, but something is "
|
|
||||||
+ "preventing me from doing so (maybe the path to the file is "
|
|
||||||
+ "no longer valid, or maybe the file is currently open in "
|
|
||||||
+ "some other program).")
|
|
||||||
else super.setStatus(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPriority(int p) throws IOException {
|
|
||||||
boolean renamed
|
|
||||||
renamed = source.renameTo(new File(source.canonicalFile.parentFile,
|
|
||||||
makeFilename(id, category, status, p)))
|
|
||||||
|
|
||||||
if (!renamed)
|
|
||||||
throw new IOException("I was unable to set the priority. "
|
|
||||||
+ "I need to rename the file for this issue, but something is "
|
|
||||||
+ "preventing me from doing so (maybe the path to the file is "
|
|
||||||
+ "no longer valid, or maybe the file is currently open in "
|
|
||||||
+ "some other program).")
|
|
||||||
else super.setPriority(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFilename() {
|
|
||||||
return makeFilename(id, category, status, priority)
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setText(String text) throws IOException {
|
|
||||||
try { source.write(text) }
|
|
||||||
catch (IOException ioe) {
|
|
||||||
throw new IOException("I could not save the new text for this "
|
|
||||||
+ "issue. I can not write to the file for this issue. I do not"
|
|
||||||
+ " know why, I am sorry (maybe the file can not be reached).")
|
|
||||||
}
|
|
||||||
|
|
||||||
super.setText(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean deleteFile() { return source.deleteDir() }
|
|
||||||
|
|
||||||
public static boolean isValidFilename(String name) {
|
|
||||||
return name ==~ fileExp
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String makeFilename(String id, Category category,
|
|
||||||
Status status, int priority) {
|
|
||||||
|
|
||||||
// bounds check priority
|
|
||||||
priority = Math.min(9, Math.max(0, priority))
|
|
||||||
|
|
||||||
//check for valid values of cateogry and id
|
|
||||||
if (category == null)
|
|
||||||
throw new IAE("Category must be non-null.")
|
|
||||||
if (status == null)
|
|
||||||
throw new IAE("Status must be non-null.")
|
|
||||||
if (!(id ==~ /\d+/))
|
|
||||||
throw new IAE( "'${id}' is not a legal value for id.")
|
|
||||||
|
|
||||||
return id + category.symbol + status.symbol + priority + ".rst";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
package com.jdbernard.pit.file
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
|
|
||||||
class FileProject extends Project {
|
|
||||||
|
|
||||||
protected File source
|
|
||||||
|
|
||||||
public FileProject(File dir) {
|
|
||||||
super(dir.canonicalFile.name)
|
|
||||||
|
|
||||||
if (!dir.isDirectory())
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"${dir.name} is not a directory.")
|
|
||||||
|
|
||||||
this.source = dir
|
|
||||||
|
|
||||||
dir.eachFile { child ->
|
|
||||||
|
|
||||||
// add sub projects
|
|
||||||
if (child.isDirectory()) {
|
|
||||||
if (child.name ==~ /\d+/ ||
|
|
||||||
child.isHidden()) return // just an issue folder
|
|
||||||
|
|
||||||
// otherwise build and add to list
|
|
||||||
projects[(child.name)] = new FileProject(child)
|
|
||||||
} else if (child.isFile() &&
|
|
||||||
FileIssue.isValidFilename(child.name)) {
|
|
||||||
def issue
|
|
||||||
|
|
||||||
// if exception, then not an issue
|
|
||||||
try { issue = new FileIssue(child) } catch (all) { return }
|
|
||||||
|
|
||||||
issues[(issue.id)] = issue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
super.setName(name)
|
|
||||||
source.renameTo(new File(source.canonicalFile.parentFile, name))
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileIssue createNewIssue(Map options) {
|
|
||||||
if (!options) options = [:]
|
|
||||||
if (!options.category) options.category = Category.TASK
|
|
||||||
if (!options.status) options.status = Status.NEW
|
|
||||||
if (!options.priority) options.priority = 5
|
|
||||||
if (!options.text) options.text = "Default issue title.\n" +
|
|
||||||
"====================\n"
|
|
||||||
String id
|
|
||||||
if (issues.size() == 0) id = '0000'
|
|
||||||
else {
|
|
||||||
id = (issues.values().max { it.id.toInteger() }).id
|
|
||||||
id = (id.toInteger() + 1).toString().padLeft(id.length(), '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
def issueFile = new File(source, FileIssue.makeFilename(id,
|
|
||||||
options.category, options.status, options.priority))
|
|
||||||
|
|
||||||
issueFile.createNewFile()
|
|
||||||
issueFile.write(options.text)
|
|
||||||
|
|
||||||
def issue = new FileIssue(issueFile)
|
|
||||||
issues[(issue.id)] = issue
|
|
||||||
|
|
||||||
return issue
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileProject createNewProject(String name) {
|
|
||||||
def newDir = new File(source, name)
|
|
||||||
newDir.mkdirs()
|
|
||||||
|
|
||||||
return new FileProject(newDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean deleteIssue(Issue issue) {
|
|
||||||
if (!issues[(issue.id)]) return false
|
|
||||||
|
|
||||||
issues.remove(issue.id)
|
|
||||||
if (issue instanceof FileIssue)
|
|
||||||
return issue.deleteFile()
|
|
||||||
|
|
||||||
else return true
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean deleteProject(Project project) {
|
|
||||||
if (!projects[(project.name)]) return false
|
|
||||||
|
|
||||||
projects.remove(project.name)
|
|
||||||
if (project instanceof FileProject)
|
|
||||||
return project.source.delete()
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return name }
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package com.jdbernard.pit.file
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
|
|
||||||
public class FileRepository extends Repository {
|
|
||||||
|
|
||||||
@Delegate FileProject fileProject
|
|
||||||
|
|
||||||
public FileRepository(File dir) {
|
|
||||||
assert dir.isDirectory()
|
|
||||||
fileProject = new FileProject(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
public void persist() {} // nothing to do
|
|
||||||
public Project[] getRootProjects() {
|
|
||||||
return [fileProject] as Project[]
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileProject createNewProject(String name) {
|
|
||||||
return fileProject.createNewProject()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package com.jdbernard.pit.util
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
|
|
||||||
if (args.size() != 1) {
|
|
||||||
println "Usage: Convert1_2 [dir]"
|
|
||||||
System.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
File rootDir = new File(args[0])
|
|
||||||
Scanner scan = new Scanner(System.in)
|
|
||||||
|
|
||||||
rootDir.eachFileRecurse { file ->
|
|
||||||
def m = file.name =~ /(\d+)([bcft])(\d).*/
|
|
||||||
if (m && file.isFile()) {
|
|
||||||
println m[0][0]
|
|
||||||
def parentFile = file.canonicalFile.parentFile
|
|
||||||
def c
|
|
||||||
def s
|
|
||||||
switch(m[0][2]) {
|
|
||||||
case "c":
|
|
||||||
println file.readLines()[0]
|
|
||||||
print "Issue was closed, was category does it belong in?"
|
|
||||||
c = Category.toCategory(scan.nextLine())
|
|
||||||
s = Status.RESOLVED
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
c = Category.toCategory(m[0][2])
|
|
||||||
s = Status.NEW
|
|
||||||
break
|
|
||||||
}
|
|
||||||
println "${m[0][2]}: ${c}"
|
|
||||||
file.renameTo(new File(parentFile,
|
|
||||||
FileIssue.makeFilename(m[0][1], c, s, m[0][3].toInteger())))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package com.jdbernard.pit.xml
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
|
|
||||||
public class XmlIssue extends Issue {
|
|
||||||
|
|
||||||
def issueNode
|
|
||||||
XmlProject project
|
|
||||||
XmlRepository repository
|
|
||||||
|
|
||||||
XmlIssue(def issueNode, XmlRepository repository, XmlProject project) {
|
|
||||||
super(issueNode.@id, issueNode.@category ?: Category.TASK,
|
|
||||||
issueNode.@status ?: Status.NEW, issueNode.@priority ?: 9)
|
|
||||||
|
|
||||||
this.issueNode = issueNode
|
|
||||||
this.project = project
|
|
||||||
this.repository = repository
|
|
||||||
}
|
|
||||||
|
|
||||||
XmlIssue(String id, Category c = Category.TASK, Status s = Status.NEW,
|
|
||||||
int p = 9, String text, XmlRepository repository, XmlProject project) {
|
|
||||||
super(id, c, s, p)
|
|
||||||
|
|
||||||
this.project = project
|
|
||||||
this.repository = repository
|
|
||||||
|
|
||||||
// Node constructor adds the node to the parent node
|
|
||||||
issueNode = new Node(project.projectNode, "Issue",
|
|
||||||
[id: id, category: c, status: s, priority: p])
|
|
||||||
|
|
||||||
this.text = text
|
|
||||||
issueNode.value = text
|
|
||||||
|
|
||||||
repository.persist()
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCategory(Category c) {
|
|
||||||
super.setCategory(c)
|
|
||||||
|
|
||||||
issueNode.@category = c.name()
|
|
||||||
repository.persist()
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatus(Status s) {
|
|
||||||
super.setStatus(s)
|
|
||||||
|
|
||||||
issueNode.@status = s.name()
|
|
||||||
repository.persist()
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPriority(int p) {
|
|
||||||
super.setPriority(p)
|
|
||||||
|
|
||||||
issueNode.@priority = p
|
|
||||||
repository.persist()
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setText(String t) {
|
|
||||||
super.setText(t)
|
|
||||||
|
|
||||||
issueNode.value = t
|
|
||||||
repository.persist()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package com.jdbernard.pit.xml
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
|
|
||||||
public class XmlProject extends Project {
|
|
||||||
|
|
||||||
def projectNode
|
|
||||||
XmlRepository repository
|
|
||||||
|
|
||||||
XmlProject(def projectNode, XmlRepository repository) {
|
|
||||||
super(projectNode.@name)
|
|
||||||
|
|
||||||
this.projectNode = projectNode
|
|
||||||
this.repository = repository
|
|
||||||
}
|
|
||||||
|
|
||||||
XmlProject(String name, def parentProject, XmlRepository repository) {
|
|
||||||
super(name)
|
|
||||||
|
|
||||||
// Node constructor adds the node to the parent node
|
|
||||||
projectNode = new Node(parentProject.projectNode, "Project",
|
|
||||||
[name: name])
|
|
||||||
|
|
||||||
repository.persist()
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
super.setName(name)
|
|
||||||
|
|
||||||
projectNode.@name = name
|
|
||||||
repository.persist()
|
|
||||||
}
|
|
||||||
|
|
||||||
public XmlIssue createNewIssue(Map options) {
|
|
||||||
if (!options) options = [:]
|
|
||||||
if (!options.category) options.category = Category.TASK
|
|
||||||
if (!options.status) options.status = Status.NEW
|
|
||||||
if (!options.priority) options.priority = 5
|
|
||||||
if (!options.text) options.text = "Default issue title.\n" +
|
|
||||||
"====================\n"
|
|
||||||
|
|
||||||
String id
|
|
||||||
if (issues.size() == 0) id = "0000"
|
|
||||||
else {
|
|
||||||
id = (issues.values().max { it.id.toInteger() }).id
|
|
||||||
id = (id.toInteger() + 1).toString().padLeft(id.length(), '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
// XmlIssue constructor will persist XML data
|
|
||||||
issues[(id)] = new XmlIssue(id, options.category, options.status,
|
|
||||||
options.priority, options.text, repository, this)
|
|
||||||
|
|
||||||
return issues[(id)]
|
|
||||||
}
|
|
||||||
|
|
||||||
public XmlProject createNewProject(String name) {
|
|
||||||
// XmlProject constructor persists the XML data
|
|
||||||
projects[(name)] = new XmlProject(name, this, repository)
|
|
||||||
return projects[(name)]
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean deleteIssue(Issue issue) {
|
|
||||||
if (!issues[(issue.id)]) return false
|
|
||||||
|
|
||||||
issues.remove(issue.id)
|
|
||||||
if (issue instanceof XmlIssue)
|
|
||||||
projectNode.remove(issue.issueNode)
|
|
||||||
|
|
||||||
repository.persist()
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean deleteProject(Project project) {
|
|
||||||
if (!projects[(project.name)]) return false
|
|
||||||
|
|
||||||
projects.remove(project.name)
|
|
||||||
if (project instanceof XmlProject)
|
|
||||||
projectNode.remove(project.projectNode)
|
|
||||||
|
|
||||||
repository.persist()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package com.jdbernard.pit.xml
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
import groovy.xml.XmlUtil
|
|
||||||
|
|
||||||
public class XmlRepository extends Repository {
|
|
||||||
|
|
||||||
def repository
|
|
||||||
def projects = []
|
|
||||||
File repoFile
|
|
||||||
|
|
||||||
public XmlRepository(File repoFile) {
|
|
||||||
|
|
||||||
this.repoFile = repoFile
|
|
||||||
repository = new XmlParser().parse(repoFile)
|
|
||||||
|
|
||||||
repository.Project.each { projectNode ->
|
|
||||||
projects << new XmlProject(projectNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void persist() {
|
|
||||||
repoFile.withOutputStream { XmlUtil.serialize(repository, it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public Project[] getRootProjects() {
|
|
||||||
return projects as XmlProject[]
|
|
||||||
}
|
|
||||||
|
|
||||||
public XmlProject createNewProject(String name) {
|
|
||||||
def newProject = new XmlProject(name, this, null)
|
|
||||||
repository << newProject.projectNode
|
|
||||||
|
|
||||||
persist()
|
|
||||||
return newProject
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean deleteProject(Project p) {
|
|
||||||
if (!projects.contains(p)) return false
|
|
||||||
|
|
||||||
projects.remove(p)
|
|
||||||
repository.remove(p.projectNode)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
import static org.junit.Assert.assertEquals
|
|
||||||
|
|
||||||
import static com.jdbernard.pit.Category.toCategory
|
|
||||||
|
|
||||||
class CategoryTest {
|
|
||||||
|
|
||||||
@Test void testToCategory() {
|
|
||||||
|
|
||||||
assertEquals toCategory("BUG"), Category.BUG
|
|
||||||
assertEquals toCategory("FEATURE"), Category.FEATURE
|
|
||||||
assertEquals toCategory("TASK"), Category.TASK
|
|
||||||
|
|
||||||
assertEquals toCategory("bug"), Category.BUG
|
|
||||||
assertEquals toCategory("feature"), Category.FEATURE
|
|
||||||
assertEquals toCategory("task"), Category.TASK
|
|
||||||
|
|
||||||
assertEquals toCategory("b"), Category.BUG
|
|
||||||
assertEquals toCategory("f"), Category.FEATURE
|
|
||||||
assertEquals toCategory("t"), Category.TASK
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testGetSymbol() {
|
|
||||||
|
|
||||||
assertEquals Category.BUG.symbol, "b"
|
|
||||||
assertEquals Category.FEATURE.symbol, "f"
|
|
||||||
assertEquals Category.TASK.symbol, "t"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.After
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue
|
|
||||||
import static org.junit.Assert.assertFalse
|
|
||||||
|
|
||||||
class FilterTest {
|
|
||||||
|
|
||||||
Project proj
|
|
||||||
|
|
||||||
@Before void setUpIssues() {
|
|
||||||
|
|
||||||
proj = new MockProject('proj1')
|
|
||||||
|
|
||||||
def issue = new MockIssue( '0000', Category.TASK, Status.NEW, 5)
|
|
||||||
proj.issues['0000'] = issue
|
|
||||||
|
|
||||||
issue = new MockIssue('0001', Category.BUG, Status.REJECTED, 3)
|
|
||||||
proj.issues['0001'] = issue
|
|
||||||
|
|
||||||
issue = new MockIssue('0002', Category.BUG, Status.RESOLVED, 9)
|
|
||||||
proj.issues['0002'] = issue
|
|
||||||
|
|
||||||
issue = new MockIssue('0003', Category.FEATURE, Status.REASSIGNED, 0)
|
|
||||||
proj.issues['0003'] = issue
|
|
||||||
|
|
||||||
def subProj = new MockProject('subproj1')
|
|
||||||
proj.projects['subproj1'] = subProj
|
|
||||||
|
|
||||||
subProj = new MockProject('subproj2')
|
|
||||||
proj.projects['subproj2'] = subProj
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testDefaultFilter() {
|
|
||||||
Filter f = new Filter()
|
|
||||||
|
|
||||||
proj.issues.values().each { assertTrue f.accept(it) }
|
|
||||||
proj.projects.values().each { assertTrue f.accept(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testPriorityIssueFilter() {
|
|
||||||
Filter f = new Filter(priority: 9)
|
|
||||||
|
|
||||||
proj.eachIssue { assertTrue f.accept(it) }
|
|
||||||
|
|
||||||
f.priority = 6
|
|
||||||
assertTrue f.accept(proj.issues['0000'])
|
|
||||||
assertTrue f.accept(proj.issues['0001'])
|
|
||||||
assertFalse f.accept(proj.issues['0002'])
|
|
||||||
assertTrue f.accept(proj.issues['0003'])
|
|
||||||
|
|
||||||
f.priority = 5
|
|
||||||
assertTrue f.accept(proj.issues['0000'])
|
|
||||||
assertTrue f.accept(proj.issues['0001'])
|
|
||||||
assertFalse f.accept(proj.issues['0002'])
|
|
||||||
assertTrue f.accept(proj.issues['0003'])
|
|
||||||
|
|
||||||
f.priority = 0
|
|
||||||
assertFalse f.accept(proj.issues['0000'])
|
|
||||||
assertFalse f.accept(proj.issues['0001'])
|
|
||||||
assertFalse f.accept(proj.issues['0002'])
|
|
||||||
assertTrue f.accept(proj.issues['0003'])
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testCategoryFilter() {
|
|
||||||
Filter f = new Filter(categories:
|
|
||||||
[Category.BUG, Category.FEATURE])
|
|
||||||
|
|
||||||
assertFalse f.accept(proj.issues['0000'])
|
|
||||||
assertTrue f.accept(proj.issues['0001'])
|
|
||||||
assertTrue f.accept(proj.issues['0002'])
|
|
||||||
assertTrue f.accept(proj.issues['0003'])
|
|
||||||
|
|
||||||
f.categories = [ Category.TASK ]
|
|
||||||
assertTrue f.accept(proj.issues['0000'])
|
|
||||||
assertFalse f.accept(proj.issues['0001'])
|
|
||||||
assertFalse f.accept(proj.issues['0002'])
|
|
||||||
assertFalse f.accept(proj.issues['0003'])
|
|
||||||
|
|
||||||
f.categories = [ Category.BUG, Category.TASK ]
|
|
||||||
assertTrue f.accept(proj.issues['0000'])
|
|
||||||
assertTrue f.accept(proj.issues['0001'])
|
|
||||||
assertTrue f.accept(proj.issues['0002'])
|
|
||||||
assertFalse f.accept(proj.issues['0003'])
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testStatusFilter() {
|
|
||||||
Filter f = new Filter(status:
|
|
||||||
[Status.NEW, Status.REASSIGNED, Status.REJECTED])
|
|
||||||
|
|
||||||
assertTrue f.accept(proj.issues['0000'])
|
|
||||||
assertTrue f.accept(proj.issues['0001'])
|
|
||||||
assertFalse f.accept(proj.issues['0002'])
|
|
||||||
assertTrue f.accept(proj.issues['0003'])
|
|
||||||
|
|
||||||
f.status = [ Status.RESOLVED ]
|
|
||||||
assertFalse f.accept(proj.issues['0000'])
|
|
||||||
assertFalse f.accept(proj.issues['0001'])
|
|
||||||
assertTrue f.accept(proj.issues['0002'])
|
|
||||||
assertFalse f.accept(proj.issues['0003'])
|
|
||||||
|
|
||||||
f.status = [ Status.NEW, Status.RESOLVED ]
|
|
||||||
assertTrue f.accept(proj.issues['0000'])
|
|
||||||
assertFalse f.accept(proj.issues['0001'])
|
|
||||||
assertTrue f.accept(proj.issues['0002'])
|
|
||||||
assertFalse f.accept(proj.issues['0003'])
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testProjectFilter() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testAcceptsProjectsFilter() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testCompositeFilter() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
public class MockIssue extends Issue {
|
|
||||||
public MockIssue(String id, Category c, Status s, int p) {
|
|
||||||
super (id, c, s, p)
|
|
||||||
}
|
|
||||||
public boolean delete() { return true }
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
class MockProject extends Project {
|
|
||||||
|
|
||||||
public MockProject(String name) { super(name) }
|
|
||||||
|
|
||||||
public Issue createNewIssue(Map options) {
|
|
||||||
return new MockIssue(options.id ?: 'n/a',
|
|
||||||
options.c ?: Category.TASK, options.s ?: Status.NEW,
|
|
||||||
options.p ?: 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
public Project createNewProject(String name) {
|
|
||||||
return new MockProject(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean delete() { return true }
|
|
||||||
public boolean deleteProject(Project project) { return true }
|
|
||||||
public boolean deleteIssue(Issue issue) { return true }
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
class MockRepository extends Repository {
|
|
||||||
|
|
||||||
public void persist() {}
|
|
||||||
|
|
||||||
public Project[] getRootProjects() { return [] as Project[] }
|
|
||||||
|
|
||||||
public Project createNewProject(String name) {
|
|
||||||
return new MockProject(name)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
import static org.junit.Assert.assertEquals
|
|
||||||
|
|
||||||
import static com.jdbernard.pit.Status.toStatus
|
|
||||||
|
|
||||||
public class StatusTest {
|
|
||||||
|
|
||||||
@Test void testToStatus() {
|
|
||||||
|
|
||||||
assertEquals Status.REASSIGNED, toStatus('REASSIGNED')
|
|
||||||
assertEquals Status.REJECTED, toStatus('REJECTED')
|
|
||||||
assertEquals Status.NEW, toStatus('NEW')
|
|
||||||
assertEquals Status.RESOLVED , toStatus('RESOLVED')
|
|
||||||
assertEquals Status.VALIDATION_REQUIRED,
|
|
||||||
toStatus('VALIDATION_REQUIRED')
|
|
||||||
|
|
||||||
assertEquals Status.REASSIGNED, toStatus('REA')
|
|
||||||
assertEquals Status.REJECTED, toStatus('REJ')
|
|
||||||
assertEquals Status.NEW, toStatus('NEW')
|
|
||||||
assertEquals Status.RESOLVED , toStatus('RES')
|
|
||||||
assertEquals Status.VALIDATION_REQUIRED,
|
|
||||||
toStatus('VAL')
|
|
||||||
|
|
||||||
assertEquals Status.REASSIGNED, toStatus('reassigned')
|
|
||||||
assertEquals Status.REJECTED, toStatus('rejected')
|
|
||||||
assertEquals Status.NEW, toStatus('new')
|
|
||||||
assertEquals Status.RESOLVED , toStatus('resolved')
|
|
||||||
assertEquals Status.VALIDATION_REQUIRED,
|
|
||||||
toStatus('validation_required')
|
|
||||||
|
|
||||||
assertEquals Status.REASSIGNED, toStatus('rea')
|
|
||||||
assertEquals Status.REJECTED, toStatus('rej')
|
|
||||||
assertEquals Status.NEW, toStatus('new')
|
|
||||||
assertEquals Status.RESOLVED , toStatus('res')
|
|
||||||
assertEquals Status.VALIDATION_REQUIRED,
|
|
||||||
toStatus('val')
|
|
||||||
|
|
||||||
assertEquals Status.REASSIGNED, toStatus('A')
|
|
||||||
assertEquals Status.REJECTED, toStatus('J')
|
|
||||||
assertEquals Status.NEW, toStatus('N')
|
|
||||||
assertEquals Status.RESOLVED , toStatus('S')
|
|
||||||
assertEquals Status.VALIDATION_REQUIRED, toStatus('V')
|
|
||||||
|
|
||||||
assertEquals Status.REASSIGNED, toStatus('a')
|
|
||||||
assertEquals Status.REJECTED, toStatus('j')
|
|
||||||
assertEquals Status.NEW, toStatus('n')
|
|
||||||
assertEquals Status.RESOLVED , toStatus('s')
|
|
||||||
assertEquals Status.VALIDATION_REQUIRED, toStatus('v')
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,230 +0,0 @@
|
|||||||
package com.jdbernard.pit.file
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
import org.junit.*
|
|
||||||
import static org.junit.Assert.assertTrue
|
|
||||||
import static org.junit.Assert.assertFalse
|
|
||||||
import static org.junit.Assert.assertEquals
|
|
||||||
|
|
||||||
class FileIssueTest {
|
|
||||||
|
|
||||||
def issues
|
|
||||||
File testDir
|
|
||||||
|
|
||||||
@Before void makeIssueFiles() {
|
|
||||||
File issueFile
|
|
||||||
issues = []
|
|
||||||
|
|
||||||
testDir = new File('testdir')
|
|
||||||
testDir.mkdirs()
|
|
||||||
|
|
||||||
issueFile = new File(testDir, '0001fn1.rst')
|
|
||||||
issueFile.write(
|
|
||||||
"Add the killer feature to the killer app.\n" +
|
|
||||||
"=========================================\n\n" +
|
|
||||||
"Make our killer app shine!.")
|
|
||||||
issues << new FileIssue(issueFile)
|
|
||||||
|
|
||||||
issueFile = new File(testDir, '0002ts5.rst')
|
|
||||||
issueFile.write(
|
|
||||||
"Obtain donuts.\n" +
|
|
||||||
"==============\n\n" +
|
|
||||||
"The office is seriously lacking in sugary donuts.\n\n" +
|
|
||||||
"We must rectify this at once!")
|
|
||||||
issues << new FileIssue(issueFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
@After void deleteIssueFiles() {
|
|
||||||
assert testDir.deleteDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testSetCategory() {
|
|
||||||
|
|
||||||
assertEquals issues[0].category, Category.FEATURE
|
|
||||||
assertEquals issues[1].category, Category.TASK
|
|
||||||
|
|
||||||
try {
|
|
||||||
issues[0].category = Category.TASK
|
|
||||||
issues[1].category = Category.BUG
|
|
||||||
} catch (Exception e) {
|
|
||||||
Assert.fail("An unexpected Exception occurred: "
|
|
||||||
+ e.getLocalizedMessage())
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals issues[0].category, Category.TASK
|
|
||||||
assertEquals issues[1].category, Category.BUG
|
|
||||||
|
|
||||||
assertTrue new File(testDir, '0001tn1.rst').exists()
|
|
||||||
assertTrue new File(testDir, '0002bs5.rst').exists()
|
|
||||||
assertFalse new File(testDir, '0001fn1.rst').exists()
|
|
||||||
assertFalse new File(testDir, '0002ts5.rst').exists()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testSetCategoryFails() {
|
|
||||||
FileInputStream fin
|
|
||||||
try {
|
|
||||||
// get a lock to the file to prevent the rename
|
|
||||||
def issueFile = new File('0001fn1.rst')
|
|
||||||
fin = new FileInputStream(issueFile)
|
|
||||||
|
|
||||||
// try to set the category
|
|
||||||
issues[0].category = Category.TASK
|
|
||||||
|
|
||||||
// should throw IOE before here
|
|
||||||
Assert.fail()
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
} catch (Exception e) {
|
|
||||||
Assert.fail("Unexpected exception: " + e.getLocalizedMessage())
|
|
||||||
} finally {
|
|
||||||
if (fin != null) fin.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testSetStatus() {
|
|
||||||
|
|
||||||
assertEquals issues[0].status, Status.NEW
|
|
||||||
assertEquals issues[1].status, Status.RESOLVED
|
|
||||||
|
|
||||||
try {
|
|
||||||
issues[0].status = Status.RESOLVED
|
|
||||||
issues[1].status = Status.REJECTED
|
|
||||||
} catch (Exception e) {
|
|
||||||
Assert.fail("An unexpected Exception occurred: "
|
|
||||||
+ e.getLocalizedMessage())
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue new File(testDir, '0001fs1.rst').exists()
|
|
||||||
assertTrue new File(testDir, '0002tj5.rst').exists()
|
|
||||||
assertFalse new File(testDir, '0001fn1.rst').exists()
|
|
||||||
assertFalse new File(testDir, '0002ts5.rst').exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testSetStatusFails() {
|
|
||||||
FileInputStream fin
|
|
||||||
try {
|
|
||||||
// get a lock to the file to prevent the rename
|
|
||||||
def issueFile = new File('0001fn1.rst')
|
|
||||||
fin = new FileInputStream(issueFile)
|
|
||||||
|
|
||||||
// try to set the status
|
|
||||||
issues[0].status = Status.REJECTED
|
|
||||||
|
|
||||||
// should throw IOE before here
|
|
||||||
Assert.fail()
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
} catch (Exception e) {
|
|
||||||
Assert.fail("Unexpected exception: " + e.getLocalizedMessage())
|
|
||||||
} finally {
|
|
||||||
if (fin != null) fin.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testSetPriority() {
|
|
||||||
|
|
||||||
assertEquals issues[0].priority, 1
|
|
||||||
assertEquals issues[1].priority, 5
|
|
||||||
|
|
||||||
try {
|
|
||||||
issues[0].priority = 2
|
|
||||||
issues[1].priority = 9
|
|
||||||
} catch (Exception e) {
|
|
||||||
Assert.fail("An unexpected Exception occurred: "
|
|
||||||
+ e.getLocalizedMessage())
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals issues[0].priority, 2
|
|
||||||
assertEquals issues[1].priority, 9
|
|
||||||
|
|
||||||
assertTrue new File(testDir, '0001fn2.rst').exists()
|
|
||||||
assertTrue new File(testDir, '0002ts9.rst').exists()
|
|
||||||
assertFalse new File(testDir, '0001fn1.rst').exists()
|
|
||||||
assertFalse new File(testDir, '0002ts5.rst').exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testSetPriorityFails() {
|
|
||||||
FileInputStream fin
|
|
||||||
try {
|
|
||||||
// get a lock to the file to prevent the rename
|
|
||||||
def issueFile = new File('0001fn1.rst')
|
|
||||||
fin = new FileInputStream(issueFile)
|
|
||||||
|
|
||||||
// try to set the priority
|
|
||||||
issues[0].priority = 9
|
|
||||||
|
|
||||||
// should throw IOE before here
|
|
||||||
Assert.fail()
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
} catch (Exception e) {
|
|
||||||
Assert.fail("Unexpected exception: " + e.getLocalizedMessage())
|
|
||||||
} finally {
|
|
||||||
if (fin != null) fin.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testConstruction() {
|
|
||||||
File issueFile = new File(testDir, '0001fn1.rst')
|
|
||||||
Issue issue = new FileIssue(issueFile)
|
|
||||||
|
|
||||||
assertEquals issue.id , "0001"
|
|
||||||
assertEquals issue.category , Category.FEATURE
|
|
||||||
assertEquals issue.status , Status.NEW
|
|
||||||
assertEquals issue.priority , 1
|
|
||||||
assertEquals issue.title , "Add the killer feature to the killer app."
|
|
||||||
assertEquals issue.text , "Add the killer feature to the killer app.\n" +
|
|
||||||
"=========================================\n\n" +
|
|
||||||
"Make our killer app shine!."
|
|
||||||
assertEquals issue.source , issueFile
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testSetTextFails() {
|
|
||||||
try {
|
|
||||||
// make the issue file un-writable
|
|
||||||
def issueFile = new File('0001fn1.rst')
|
|
||||||
if (issueFile.setReadOnly()) {
|
|
||||||
|
|
||||||
// try to write something
|
|
||||||
issues[0].text = "This should fail to be written."
|
|
||||||
|
|
||||||
// should throw IOE before here
|
|
||||||
Assert.fail()
|
|
||||||
} else {
|
|
||||||
println "Could not run testSetTextFails, unable to change " +
|
|
||||||
"the test isseu file's permissions."
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
} catch (Exception e) {
|
|
||||||
Assert.fail("Unexpected exception: " + e.getLocalizedMessage())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testMakeFilename() {
|
|
||||||
assertEquals FileIssue.makeFilename('0001', Category.BUG,
|
|
||||||
Status.NEW, 5), '0001bn5.rst'
|
|
||||||
assertEquals FileIssue.makeFilename('0010', Category.FEATURE,
|
|
||||||
Status.REASSIGNED, 1), '0010fa1.rst'
|
|
||||||
assertEquals FileIssue.makeFilename('0002', Category.FEATURE,
|
|
||||||
Status.REJECTED, 3), '0002fj3.rst'
|
|
||||||
assertEquals FileIssue.makeFilename('0001', Category.BUG,
|
|
||||||
Status.RESOLVED, -2), '0001bs0.rst'
|
|
||||||
assertEquals FileIssue.makeFilename('0001', Category.TASK,
|
|
||||||
Status.VALIDATION_REQUIRED, 10) , '0001tv9.rst'
|
|
||||||
assertEquals FileIssue.makeFilename('00101', Category.BUG,
|
|
||||||
Status.NEW, 5), '00101bn5.rst'
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileIssue.makeFilename('badid', Category.BUG, Status.NEW, 5)
|
|
||||||
assertTrue 'Issue.makeFilename() succeeded with bad id input.', false
|
|
||||||
} catch (IllegalArgumentException iae) {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileIssue.makeFilename('0002', null, Status.NEW, 5)
|
|
||||||
assertTrue 'Issue.makeFilename() succeeded given no Category.', false
|
|
||||||
} catch (IllegalArgumentException iae) {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileIssue.makeFilename('0002', Category.BUG, null, 5)
|
|
||||||
assertTrue 'Issue.makeFilename() succeeded given no Status.', false
|
|
||||||
} catch (IllegalArgumentException iae) {}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,162 +0,0 @@
|
|||||||
package com.jdbernard.pit.file
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import static org.junit.Assert.assertEquals
|
|
||||||
import static org.junit.Assert.assertFalse
|
|
||||||
import static org.junit.Assert.assertNotNull
|
|
||||||
import static org.junit.Assert.assertTrue
|
|
||||||
|
|
||||||
class FileProjectTest {
|
|
||||||
|
|
||||||
File testDir
|
|
||||||
Project rootProj
|
|
||||||
|
|
||||||
@Before void createTestProjects() {
|
|
||||||
|
|
||||||
testDir = new File('testdir')
|
|
||||||
assert !testDir.exists()
|
|
||||||
testDir.mkdirs()
|
|
||||||
|
|
||||||
/* TEST SUITE:
|
|
||||||
/testdir/
|
|
||||||
0001t5.rst
|
|
||||||
0002b5.rst
|
|
||||||
0003f2.rst
|
|
||||||
|
|
||||||
subproj1/
|
|
||||||
0001f3.rst
|
|
||||||
0002b4.rst
|
|
||||||
|
|
||||||
emptyproj/
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
def issueFile = new File(testDir, '0001tn5.rst')
|
|
||||||
issueFile.createNewFile()
|
|
||||||
issueFile.write('Test Issue 1\n' +
|
|
||||||
'============\n\n' +
|
|
||||||
'This is the first test issue.')
|
|
||||||
|
|
||||||
issueFile = new File(testDir, '0002ba5.rst')
|
|
||||||
issueFile.createNewFile()
|
|
||||||
issueFile.write('Test Bug\n' +
|
|
||||||
'========\n\n' +
|
|
||||||
'Yeah, it is a test bug.')
|
|
||||||
|
|
||||||
issueFile = new File(testDir, '0003fs2.rst')
|
|
||||||
issueFile.createNewFile()
|
|
||||||
issueFile.write('Important Feature Request\n' +
|
|
||||||
'=========================\n\n' +
|
|
||||||
'Here is our sweet feature. Please implement it!')
|
|
||||||
|
|
||||||
def subDir = new File(testDir, 'subproj1')
|
|
||||||
subDir.mkdirs()
|
|
||||||
|
|
||||||
issueFile = new File(subDir, '0001fv3.rst')
|
|
||||||
issueFile.createNewFile()
|
|
||||||
issueFile.write('First feature in subproject\n' +
|
|
||||||
'===========================\n\n' +
|
|
||||||
'Please make the grubblers grobble.')
|
|
||||||
|
|
||||||
issueFile = new File(subDir, '0002bj4.rst')
|
|
||||||
issueFile.createNewFile()
|
|
||||||
issueFile.write('Zippners are not zippning.\n' +
|
|
||||||
'==========================\n\n' +
|
|
||||||
'For some reason, the Zippners are bilperring, not zippning.')
|
|
||||||
|
|
||||||
subDir = new File(testDir, 'emptyproj')
|
|
||||||
subDir.mkdirs()
|
|
||||||
|
|
||||||
rootProj = new FileProject(testDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
@After void deleteTestProjects() {
|
|
||||||
assert testDir.deleteDir()
|
|
||||||
|
|
||||||
if (rootProj.source.exists())
|
|
||||||
assert rootProj.source.deleteDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testConstruction() {
|
|
||||||
Project proj = new FileProject(testDir)
|
|
||||||
|
|
||||||
assertEquals proj.name, 'testdir'
|
|
||||||
assertEquals proj.issues.size(), 3
|
|
||||||
assertEquals proj.projects.size(), 2
|
|
||||||
|
|
||||||
// Issue construction in general is under test in IssueTest
|
|
||||||
// just check that the issues actually exists
|
|
||||||
assertEquals proj.issues['0001'].id, '0001'
|
|
||||||
assertEquals proj.issues['0001'].title, 'Test Issue 1'
|
|
||||||
|
|
||||||
assertEquals proj.issues['0002'].id, '0002'
|
|
||||||
assertEquals proj.issues['0002'].title, 'Test Bug'
|
|
||||||
|
|
||||||
assertEquals proj.issues['0003'].id, '0003'
|
|
||||||
assertEquals proj.issues['0003'].title, 'Important Feature Request'
|
|
||||||
|
|
||||||
// check sub-project behaviour
|
|
||||||
assertNotNull proj.projects.subproj1
|
|
||||||
assertEquals proj.projects.subproj1.name, 'subproj1'
|
|
||||||
assertEquals proj.projects.subproj1.issues.size(), 2
|
|
||||||
assertEquals proj.projects.subproj1.projects.size(), 0
|
|
||||||
assertEquals proj.projects.subproj1.issues['0001'].id, '0001'
|
|
||||||
assertEquals proj.projects.subproj1.issues['0002'].id, '0002'
|
|
||||||
assertEquals proj.projects.subproj1.issues['0001'].title,
|
|
||||||
'First feature in subproject'
|
|
||||||
assertEquals proj.projects.subproj1.issues['0002'].title,
|
|
||||||
'Zippners are not zippning.'
|
|
||||||
|
|
||||||
assertNotNull proj.projects.emptyproj
|
|
||||||
assertEquals proj.projects.emptyproj.issues.size(), 0
|
|
||||||
assertEquals proj.projects.emptyproj.projects.size(), 0
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testRename() {
|
|
||||||
assert rootProj.name == 'testdir'
|
|
||||||
|
|
||||||
rootProj.name = 'renamedTestDir'
|
|
||||||
|
|
||||||
assertEquals rootProj.name, 'renamedTestDir'
|
|
||||||
assertTrue new File('renamedTestDir').exists()
|
|
||||||
|
|
||||||
assert rootProj.source.deleteDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test void testCreateNewIssue() {
|
|
||||||
|
|
||||||
// test correct increment of id, application of values
|
|
||||||
def newIssue = rootProj.createNewIssue(category: Category.BUG,
|
|
||||||
status: Status.REASSIGNED, priority: 4,
|
|
||||||
text: 'A newly made bug report.\n'+
|
|
||||||
'========================\n\n' +
|
|
||||||
'Testing the Project.createNewIssue() method.')
|
|
||||||
|
|
||||||
assertEquals newIssue.id, '0004'
|
|
||||||
assertEquals newIssue.category, Category.BUG
|
|
||||||
assertEquals newIssue.status, Status.REASSIGNED
|
|
||||||
assertEquals newIssue.priority, 4
|
|
||||||
assertEquals newIssue.text, 'A newly made bug report.\n'+
|
|
||||||
'========================\n\n' +
|
|
||||||
'Testing the Project.createNewIssue() method.'
|
|
||||||
assertEquals rootProj.issues[(newIssue.id)], newIssue
|
|
||||||
|
|
||||||
//test defaults and creation of issue in an empty project
|
|
||||||
newIssue = rootProj.projects.emptyproj.createNewIssue()
|
|
||||||
|
|
||||||
assertEquals newIssue.id, '0000'
|
|
||||||
assertEquals newIssue.priority, 5
|
|
||||||
assertEquals newIssue.category, Category.TASK
|
|
||||||
assertEquals newIssue.status, Status.NEW
|
|
||||||
assertEquals newIssue.text, 'Default issue title.\n' +
|
|
||||||
'====================\n'
|
|
||||||
|
|
||||||
assertEquals rootProj.projects.emptyproj.issues[(newIssue.id)],
|
|
||||||
newIssue
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package com.jdbernard.pit.xml
|
|
||||||
|
|
||||||
import com.jdbernard.pit.*
|
|
||||||
import groovy.util.Node
|
|
||||||
import org.junit.Test
|
|
||||||
import static org.junit.Assert.assertEquals
|
|
||||||
import static org.junit.Assert.assertFalse
|
|
||||||
import static org.junit.Assert.assertTrue
|
|
||||||
|
|
||||||
public class XmlIssueTest {
|
|
||||||
|
|
||||||
Node issueNode = new Node(null, 'Issue',
|
|
||||||
[id: '0000', category: 'BUG', status: 'RESOLVED', priority: 1],
|
|
||||||
'Test Issue')
|
|
||||||
|
|
||||||
@Test public void testDummyTest() {}
|
|
||||||
|
|
||||||
/*@Test public void testNodeConstructor() {
|
|
||||||
XmlIssue issue = new XmlIssue(issueNode)
|
|
||||||
|
|
||||||
assertEquals issue.text, 'Test Issue'
|
|
||||||
assertEquals issue.id, '0000'
|
|
||||||
assertEquals issue.category, Category.BUG
|
|
||||||
assertEquals issue.status, Status.RESOLVED
|
|
||||||
assertEquals issue.priority, 1
|
|
||||||
}*/
|
|
||||||
}
|
|
3
pit-cli/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
release/
|
|
||||||
build/
|
|
||||||
*.sw?
|
|
@ -1,118 +0,0 @@
|
|||||||
<project name="Personal Issue Tracker CLI">
|
|
||||||
<property file="../version.properties"/>
|
|
||||||
<property file="project.properties"/>
|
|
||||||
<property environment="env" />
|
|
||||||
|
|
||||||
<path id="groovy.libs">
|
|
||||||
<fileset dir="${env.GROOVY_HOME}/lib">
|
|
||||||
<include name="**/*.jar"/>
|
|
||||||
</fileset>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<path id="groovy.embeddable">
|
|
||||||
<fileset dir="${env.GROOVY_HOME}/embeddable">
|
|
||||||
<include name="**/*.jar"/>
|
|
||||||
</fileset>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
|
|
||||||
<path id="project.libs">
|
|
||||||
<fileset dir="${lib.dir}">
|
|
||||||
<include name="**/*.jar"/>
|
|
||||||
</fileset>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<path id="groovyc.classpath">
|
|
||||||
<path refid="groovy.libs"/>
|
|
||||||
<path refid="project.libs"/>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<path id="package.jars">
|
|
||||||
<path refid="groovy.embeddable"/>
|
|
||||||
<path refid="project.libs"/>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<taskdef name="groovyc"
|
|
||||||
classname="org.codehaus.groovy.ant.Groovyc"
|
|
||||||
classpathref="groovy.libs"/>
|
|
||||||
|
|
||||||
<target name="init">
|
|
||||||
<fail
|
|
||||||
unless="env.GROOVY_HOME"
|
|
||||||
message="GROOVY_HOME environment variable is not set."/>
|
|
||||||
<echo message="GROOVY_HOME: ${env.GROOVY_HOME}"/>
|
|
||||||
|
|
||||||
<fail message="Could not find PIT ${application.version} library.">
|
|
||||||
<condition>
|
|
||||||
<not>
|
|
||||||
<available
|
|
||||||
file="${lib.dir}/pit-${application.version}.jar"/>
|
|
||||||
</not>
|
|
||||||
</condition>
|
|
||||||
</fail>
|
|
||||||
<echo message="PIT library found at ${lib.dir}/pit-${application.version}.jar"/>
|
|
||||||
|
|
||||||
<fail message="The PIT project is at version ${application.version} but pit-cli is versioned as ${expected.application.version}. Ensure that pit-cli is updated tp reflect the changes in libpit and then run the 'upgrade-version' task to sync the pit-vli subproject with the PIT project.">
|
|
||||||
<condition>
|
|
||||||
<not>
|
|
||||||
<equals
|
|
||||||
arg1="${application.version}"
|
|
||||||
arg2="${expected.application.version}"/>
|
|
||||||
</not>
|
|
||||||
</condition>
|
|
||||||
</fail>
|
|
||||||
<echo message="Application version: ${application.version}"/>
|
|
||||||
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="upgrade-version">
|
|
||||||
<propertyfile file="project.properties">
|
|
||||||
<entry
|
|
||||||
key="expected.application.version"
|
|
||||||
value="${application.version}"/>
|
|
||||||
<entry key="build.number" value="0"/>
|
|
||||||
</propertyfile>
|
|
||||||
<echo message="pit-cli version upgraded to ${application.version}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="increment-build-number" depends="init">
|
|
||||||
<propertyfile file="project.properties">
|
|
||||||
<entry key="build.number" operation="+" type="int" default="0"/>
|
|
||||||
</propertyfile>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="clean">
|
|
||||||
<delete dir="${build.dir}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="compile" depends="init,increment-build-number">
|
|
||||||
<mkdir dir="${build.dir}/classes"/>
|
|
||||||
<groovyc
|
|
||||||
srcdir="${src.dir}"
|
|
||||||
destdir="${build.dir}/classes"
|
|
||||||
classpathref="groovyc.classpath"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="build" depends="compile">
|
|
||||||
<mkdir dir="${build.dir}/jar"/>
|
|
||||||
<unjar dest="${build.dir}/classes">
|
|
||||||
<path refid="package.jars"/>
|
|
||||||
</unjar>
|
|
||||||
<jar
|
|
||||||
destfile="${build.dir}/jar/${build.jar}"
|
|
||||||
basedir="${build.dir}/classes"
|
|
||||||
compress="on">
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="${main.class}"/>
|
|
||||||
</manifest>
|
|
||||||
</jar>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="release" depends="build">
|
|
||||||
<delete dir="${release.dir}"/>
|
|
||||||
<mkdir dir="${release.dir}"/>
|
|
||||||
<copy file="${build.dir}/jar/${build.jar}"
|
|
||||||
tofile="${release.dir}/${release.jar}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
</project>
|
|
@ -1,10 +0,0 @@
|
|||||||
#Tue, 25 Oct 2011 11:32:31 -0500
|
|
||||||
build.dir=build
|
|
||||||
src.dir=src
|
|
||||||
build.jar=pit-cli-${application.version}.${build.number}.jar
|
|
||||||
build.number=13
|
|
||||||
expected.application.version=2.6.0
|
|
||||||
lib.dir=lib
|
|
||||||
release.dir=release
|
|
||||||
release.jar=pit-cli-${application.version}.jar
|
|
||||||
main.class=com.jdbernard.pit.PersonalIssueTrackerCLI
|
|
@ -1,253 +0,0 @@
|
|||||||
package com.jdbernard.pit
|
|
||||||
|
|
||||||
import com.jdbernard.pit.file.*
|
|
||||||
|
|
||||||
import static java.lang.Math.max
|
|
||||||
import static java.lang.Math.min
|
|
||||||
|
|
||||||
// -------- command-line interface specification -------- //
|
|
||||||
|
|
||||||
def cli = new CliBuilder(usage: 'pit-cli [options]')
|
|
||||||
cli.h(longOpt: 'help', 'Show help information.')
|
|
||||||
cli.v(longOpt: 'verbose', 'Show verbose task information')
|
|
||||||
cli.l(longOpt: 'list', 'List issues. Unless otherwise specified it lists all '
|
|
||||||
+ 'sub projects and all unclosed issue categories.')
|
|
||||||
cli.i(argName: 'id', longOpt: 'id', args: 1,
|
|
||||||
'Filter issues by id. Accepts a comma-delimited list.')
|
|
||||||
cli.c(argName: 'category', longOpt: 'category', args: 1,
|
|
||||||
'Filter issues by category (bug, feature, task). Accepts a '
|
|
||||||
+ 'comma-delimited list. By default all categories are selected.')
|
|
||||||
cli.s(argName: 'status', longOpt: 'status', args: 1,
|
|
||||||
'Filter issues by status (new, reassigned, rejected, resolved, ' +
|
|
||||||
'validation_required)')
|
|
||||||
cli.p(argName: 'priority', longOpt: 'priority', args: 1,
|
|
||||||
'Filter issues by priority. This acts as a threshhold, listing all issues '
|
|
||||||
+ 'greater than or equal to the given priority.')
|
|
||||||
cli.r(argName: 'project', longOpt: 'project', args: 1,
|
|
||||||
'Filter issues by project (relative to the current directory). Accepts a '
|
|
||||||
+ 'comma-delimited list.')
|
|
||||||
/*cli.s(longOpt: 'show-subprojects',
|
|
||||||
'Include sup projects in listing (default behaviour)')
|
|
||||||
cli.S(longOpt: 'no-subprojects', 'Do not list subprojects.')*/ // TODO: figure out better flags for these options.
|
|
||||||
cli.P(argName: 'new-priority', longOpt: 'set-priority', args: 1,
|
|
||||||
required: false, 'Modify the priority of the selected issues.')
|
|
||||||
cli.C(argName: 'new-category', longOpt: 'set-category', args: 1,
|
|
||||||
required: false, 'Modify the category of the selected issues.')
|
|
||||||
cli.S(argName: 'new-status', longOpt: 'set-status', args: 1,
|
|
||||||
required: false, 'Modify the status of the selected issues.')
|
|
||||||
cli.n(longOpt: 'new-issue', 'Create a new issue.')
|
|
||||||
cli.d(longOpt: 'dir', argName: 'dir', args: 1, required: false,
|
|
||||||
'Use <dir> as the base directory (defaults to current directory).')
|
|
||||||
cli._(longOpt: 'version', 'Display PIT version information.')
|
|
||||||
|
|
||||||
// -------- parse CLI options -------- //
|
|
||||||
def VERSION = "2.6.0"
|
|
||||||
def opts = cli.parse(args)
|
|
||||||
def issuedb = [:]
|
|
||||||
def workingDir = new File('.')
|
|
||||||
|
|
||||||
// defaults for the issue filter/selector
|
|
||||||
def selectOpts = [
|
|
||||||
categories: ['bug', 'feature', 'task'],
|
|
||||||
status: ['new', 'reassigned', 'rejected',
|
|
||||||
'resolved', 'validation_required'],
|
|
||||||
priority: 9,
|
|
||||||
projects: [],
|
|
||||||
ids: [],
|
|
||||||
acceptProjects: true]
|
|
||||||
|
|
||||||
// defaults for changing properties of issue(s)
|
|
||||||
def assignOpts = [
|
|
||||||
category: Category.TASK,
|
|
||||||
status: Status.NEW,
|
|
||||||
priority: 5,
|
|
||||||
text: "New issue."]
|
|
||||||
|
|
||||||
if (!opts) opts.l = true; // default to 'list'
|
|
||||||
|
|
||||||
if (opts.h) {
|
|
||||||
cli.usage()
|
|
||||||
System.exit(0) }
|
|
||||||
|
|
||||||
// read the category filter designation(s)
|
|
||||||
if (opts.c) {
|
|
||||||
if (opts.c =~ /all/) {} // no-op, same as defaults
|
|
||||||
else { selectOpts.categories = opts.c.split(/[,\s]/) } }
|
|
||||||
|
|
||||||
// parse the categories names into Category objects
|
|
||||||
try { selectOpts.categories =
|
|
||||||
selectOpts.categories.collect { Category.toCategory(it) } }
|
|
||||||
catch (Exception e) {
|
|
||||||
println "Invalid category option: '-c ${e.localizedMessage}'."
|
|
||||||
println "Valid options are: \n${Category.values().join(', ')}"
|
|
||||||
println " (abbreviations are accepted)."
|
|
||||||
System.exit(1) }
|
|
||||||
|
|
||||||
// read the status filter designation(s)
|
|
||||||
if (opts.s) {
|
|
||||||
// -s all
|
|
||||||
if (opts.s =~ /all/) selectOpts.status = ['new', 'reassigned', 'rejected',
|
|
||||||
'resolved', 'validation_required']
|
|
||||||
// is <list>
|
|
||||||
else selectOpts.status = opts.s.split(/[,\s]/) }
|
|
||||||
|
|
||||||
// parse the statuses into Status objects
|
|
||||||
try { selectOpts.status =
|
|
||||||
selectOpts.status.collect { Status.toStatus(it) } }
|
|
||||||
catch (Exception e) {
|
|
||||||
println "Invalid status option: '-s ${e.localizedMessage}'."
|
|
||||||
println "Valid options are: \b${Status.values().join(', ')}"
|
|
||||||
println " (abbreviations are accepted.)"
|
|
||||||
System.exit(1) }
|
|
||||||
|
|
||||||
// read and parse the priority filter
|
|
||||||
if (opts.p) try {
|
|
||||||
selectOpts.priority = opts.p.toInteger() }
|
|
||||||
catch (NumberFormatException nfe) {
|
|
||||||
println "Not a valid priority value: '-p ${opts.p}'."
|
|
||||||
println "Valid values are: 0-9"
|
|
||||||
System.exit(1) }
|
|
||||||
|
|
||||||
// read and parse the projects filter
|
|
||||||
if (opts.r) { selectOpts.projects =
|
|
||||||
opts.r.toLowerCase().split(/[,\s]/).asType(List.class) }
|
|
||||||
|
|
||||||
// read and parse the ids filter
|
|
||||||
if (opts.i) { selectOpts.ids = opts.i.split(/[,\s]/).asType(List.class) }
|
|
||||||
|
|
||||||
// TODO: accept projects value from input
|
|
||||||
|
|
||||||
// read and parse the category to assign
|
|
||||||
if (opts.C) try { assignOpts.category = Category.toCategory(opts.C) }
|
|
||||||
catch (Exception e) {
|
|
||||||
println "Invalid category option: '-C ${e.localizedMessage}'."
|
|
||||||
println "Valid categories are: \n${Category.values().join(', ')}"
|
|
||||||
println " (abbreviations are accepted)."
|
|
||||||
System.exit(1) }
|
|
||||||
|
|
||||||
// read and parse the status to assign
|
|
||||||
if (opts.S) try { assignOpts.status = Status.toStatus(opts.S) }
|
|
||||||
catch (Exception e) {
|
|
||||||
println "Invalid status option: '-S ${e.localizedMessage}'."
|
|
||||||
println "Valid stasus options are: \n{Status.values().join(', ')}"
|
|
||||||
println " (abbreviations are accepted)."
|
|
||||||
System.exit(1) }
|
|
||||||
|
|
||||||
// read and parse the priority to assign
|
|
||||||
if (opts.P) try {assignOpts.priority = opts.P.toInteger() }
|
|
||||||
catch (NumberFormatException nfe) {
|
|
||||||
println "Not a valid priority value: '-P ${opts.P}'."
|
|
||||||
println "Valid values are: 0-9"
|
|
||||||
System.exit(1) }
|
|
||||||
|
|
||||||
// look for assignment text
|
|
||||||
if (opts.getArgs().length > 0) {
|
|
||||||
assignOpts.text = opts.getArgs()[0] }
|
|
||||||
|
|
||||||
// set the project working directory
|
|
||||||
if (opts.d) {
|
|
||||||
workingDir = new File(opts.d.trim())
|
|
||||||
if (!workingDir.exists()) {
|
|
||||||
println "Directory '${workingDir}' does not exist."
|
|
||||||
return -1 } }
|
|
||||||
def EOL = System.getProperty('line.separator')
|
|
||||||
|
|
||||||
// build issue list
|
|
||||||
issuedb = new FileProject(workingDir)
|
|
||||||
|
|
||||||
// build filter from options
|
|
||||||
def filter = new Filter(selectOpts)
|
|
||||||
|
|
||||||
// -------- Actions -------- //
|
|
||||||
// list version information first
|
|
||||||
if (opts.version) {
|
|
||||||
|
|
||||||
println "PIT CLI Version ${VERSION}"
|
|
||||||
println "Written by Jonathan Bernard\n" }
|
|
||||||
|
|
||||||
// list second
|
|
||||||
else if (opts.l) {
|
|
||||||
|
|
||||||
// local function (closure) to print a single issue
|
|
||||||
def printIssue = { issue, offset ->
|
|
||||||
println "${offset}${issue}"
|
|
||||||
if (opts.v) {
|
|
||||||
println ""
|
|
||||||
issue.text.eachLine { println "${offset} ${it}" }
|
|
||||||
println "" } }
|
|
||||||
|
|
||||||
// local function (closure) to print a project and all visible subprojects
|
|
||||||
def printProject
|
|
||||||
printProject = { project, offset ->
|
|
||||||
println "\n${offset}${project.name}"
|
|
||||||
println "${offset}${'-'.multiply(project.name.length())}"
|
|
||||||
project.eachIssue(filter) { printIssue(it, offset) }
|
|
||||||
project.eachProject(filter) { printProject(it, offset + " ") } }
|
|
||||||
|
|
||||||
// print all the issues in the root of this db
|
|
||||||
issuedb.eachIssue(filter) { printIssue(it, "") }
|
|
||||||
// print all projects
|
|
||||||
issuedb.eachProject(filter) { printProject(it, "") } }
|
|
||||||
|
|
||||||
// new issues third
|
|
||||||
else if (opts.n) {
|
|
||||||
def cat, priority
|
|
||||||
String text = ""
|
|
||||||
Issue issue
|
|
||||||
def sin = System.in.newReader()
|
|
||||||
|
|
||||||
if (opts.C) { cat = assignOpts.category }
|
|
||||||
else while(true) {
|
|
||||||
try {
|
|
||||||
print "Category (bug, feature, task, closed): "
|
|
||||||
cat = Category.toCategory(sin.readLine())
|
|
||||||
break }
|
|
||||||
catch (e) {
|
|
||||||
println "Invalid category: " + e.getLocalizedMessage()
|
|
||||||
println "Valid options are: \n${Category.values().join(', ')}"
|
|
||||||
println " (abbreviations are accepted)." } }
|
|
||||||
|
|
||||||
if (opts.P) { priority = assignOpts.priority }
|
|
||||||
else while (true) {
|
|
||||||
try {
|
|
||||||
print "Priority (0-9): "
|
|
||||||
priority = max(0, min(9, sin.readLine().toInteger()))
|
|
||||||
break }
|
|
||||||
catch (e) { println "Not a valid value." } }
|
|
||||||
|
|
||||||
if (opts.getArgs().length > 0) { text = assignOpts.text }
|
|
||||||
else {
|
|
||||||
println "Enter issue (use EOF): "
|
|
||||||
try {
|
|
||||||
def line = ""
|
|
||||||
while(true) {
|
|
||||||
line = sin.readLine()
|
|
||||||
|
|
||||||
if (line =~ /EOF/) break
|
|
||||||
|
|
||||||
text += line + EOL
|
|
||||||
} }
|
|
||||||
catch (e) {} }
|
|
||||||
|
|
||||||
issue = issuedb.createNewIssue(category: cat, priority: priority, text: text)
|
|
||||||
|
|
||||||
println "New issue created: "
|
|
||||||
println issue }
|
|
||||||
|
|
||||||
// last, changes to existing issues
|
|
||||||
else {
|
|
||||||
// change priority
|
|
||||||
if (opts.P) issuedb.walkProject(filter) {
|
|
||||||
it.priority = assignOpts.priority
|
|
||||||
println "[${it}] -- set priority to ${assignOpts.priority}"}
|
|
||||||
|
|
||||||
// change third
|
|
||||||
else if (opts.C) issuedb.walkProject(filter) {
|
|
||||||
it.category = assignOpts.cat
|
|
||||||
println "[${it}] -- set category to ${assignOpts.category}"}
|
|
||||||
|
|
||||||
// change status
|
|
||||||
else if (opts.S) issuedb.walkProject(filter) {
|
|
||||||
it.status = assignOpts.status
|
|
||||||
println "[${it}] -- set status to ${assignOpts.status}"}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
#Griffon Metadata file
|
|
||||||
#Thu Aug 05 10:29:59 CDT 2010
|
|
||||||
app.archetype=default
|
|
||||||
app.griffon.version=0.9
|
|
||||||
app.name=pit-swing
|
|
||||||
app.version=2.5.1
|
|
@ -1,33 +0,0 @@
|
|||||||
application {
|
|
||||||
title = 'PitSwing'
|
|
||||||
startupGroups = ['PIT']
|
|
||||||
|
|
||||||
// Should Griffon exit when no Griffon created frames are showing?
|
|
||||||
autoShutdown = true
|
|
||||||
|
|
||||||
// If you want some non-standard application class, apply it here
|
|
||||||
//frameClass = 'javax.swing.JFrame'
|
|
||||||
}
|
|
||||||
mvcGroups {
|
|
||||||
// MVC Group for "ProjectPanel"
|
|
||||||
'ProjectPanel' {
|
|
||||||
model = 'com.jdbernard.pit.swing.ProjectPanelModel'
|
|
||||||
view = 'com.jdbernard.pit.swing.ProjectPanelView'
|
|
||||||
controller = 'com.jdbernard.pit.swing.ProjectPanelController'
|
|
||||||
}
|
|
||||||
|
|
||||||
// MVC Group for "NewIssueDialog"
|
|
||||||
'NewIssueDialog' {
|
|
||||||
model = 'com.jdbernard.pit.swing.NewIssueDialogModel'
|
|
||||||
view = 'com.jdbernard.pit.swing.NewIssueDialogView'
|
|
||||||
controller = 'com.jdbernard.pit.swing.NewIssueDialogController'
|
|
||||||
}
|
|
||||||
|
|
||||||
// MVC Group for "PIT"
|
|
||||||
'PIT' {
|
|
||||||
model = 'com.jdbernard.pit.swing.PITModel'
|
|
||||||
view = 'com.jdbernard.pit.swing.PITView'
|
|
||||||
controller = 'com.jdbernard.pit.swing.PITController'
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
// 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 {
|
|
||||||
sigfile = 'GRIFFON'
|
|
||||||
keystore = 'CHANGE ME'
|
|
||||||
alias = 'CHANGE ME'
|
|
||||||
// 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'
|
|
||||||
//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.project.dependency.resolution = {
|
|
||||||
// inherit Griffon' default dependencies
|
|
||||||
inherits("global") {
|
|
||||||
}
|
|
||||||
log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
|
|
||||||
repositories {
|
|
||||||
griffonPlugins()
|
|
||||||
griffonHome()
|
|
||||||
griffonCentral()
|
|
||||||
|
|
||||||
// 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', 'test' or 'provided' scopes eg.
|
|
||||||
|
|
||||||
// runtime 'mysql:mysql-connector-java:5.1.5'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
griffon {
|
|
||||||
doc {
|
|
||||||
logo = '<a href="http://griffon.codehaus.org" target="_blank"><img alt="The Griffon Framework" src="../img/griffon.png" border="0"/></a>'
|
|
||||||
sponsorLogo = "<br/>"
|
|
||||||
footer = "<br/><br/>Made with Griffon (0.9)"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
root {
|
|
||||||
'groovy.swing.SwingBuilder' {
|
|
||||||
controller = ['Threading']
|
|
||||||
view = '*'
|
|
||||||
}
|
|
||||||
'griffon.app.ApplicationBuilder' {
|
|
||||||
view = '*'
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
// log4j configuration
|
|
||||||
log4j {
|
|
||||||
appender.stdout = 'org.apache.log4j.ConsoleAppender'
|
|
||||||
appender.'stdout.layout'='org.apache.log4j.PatternLayout'
|
|
||||||
appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'
|
|
||||||
appender.errors = 'org.apache.log4j.FileAppender'
|
|
||||||
appender.'errors.layout'='org.apache.log4j.PatternLayout'
|
|
||||||
appender.'errors.layout.ConversionPattern'='[%r] %c{2} %m%n'
|
|
||||||
appender.'errors.File'='stacktrace.log'
|
|
||||||
rootLogger='error,stdout'
|
|
||||||
logger {
|
|
||||||
griffon='error'
|
|
||||||
StackTrace='error,errors'
|
|
||||||
org {
|
|
||||||
codehaus.griffon.commons='info' // core / classloading
|
|
||||||
}
|
|
||||||
}
|
|
||||||
additivity.StackTrace=false
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
onNewInstance = { klass, type, instance ->
|
|
||||||
instance.metaClass.logger = LoggerFactory.getLogger(klass.name)
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
<!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: '@griffonAppName@',
|
|
||||||
codebase:'@griffonAppCodebase@',
|
|
||||||
code:'@griffonAppletClass@',
|
|
||||||
archive:'@appletJars@',
|
|
||||||
width:'@applet.width@', height:'@applet.height@'} ;
|
|
||||||
var parameters = {fontSize:16,
|
|
||||||
java_arguments: "-Djnlp.packEnabled=true",
|
|
||||||
jnlp_href:'@griffonAppCodebase@/applet.jnlp',
|
|
||||||
draggable:'true',
|
|
||||||
image:'griffon.png',
|
|
||||||
boxmessage:'Loading @griffonAppName@',
|
|
||||||
boxbgcolor:'#FFFFFF', boxfgcolor:'#000000',
|
|
||||||
codebase_lookup: 'false'@applet.script.params@} ;
|
|
||||||
var version = '1.5.0' ;
|
|
||||||
deployJava.runApplet(attributes, parameters, version);
|
|
||||||
</script>
|
|
||||||
<!--
|
|
||||||
<APPLET CODEBASE='@griffonAppCodebase@'
|
|
||||||
CODE='@griffonAppletClass@'
|
|
||||||
ARCHIVE='@appletJars@'
|
|
||||||
WIDTH='@applet.width@' HEIGHT='@applet.height@'>
|
|
||||||
<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 @griffonAppName@'>
|
|
||||||
<PARAM NAME='boxbgcolor' VALUE='#FFFFFF'>
|
|
||||||
<PARAM NAME='boxfgcolor' VALUE='#000000'>
|
|
||||||
<PARAM NAME='codebase_lookup' VALUE='false'>
|
|
||||||
@applet.tag.params@
|
|
||||||
</APPLET>
|
|
||||||
-->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!DOCTYPE jnlp SYSTEM "http://java.sun.com/dtd/JNLP-1.5.dtd">
|
|
||||||
<jnlp
|
|
||||||
version="@griffonAppVersion@"
|
|
||||||
codebase="@griffonAppCodebase@"
|
|
||||||
href="@jnlpFileName@"
|
|
||||||
>
|
|
||||||
<information>
|
|
||||||
<title>@griffonAppName@</title>
|
|
||||||
<vendor>@griffonAppName@</vendor>
|
|
||||||
<!--<homepage href="http://app.example.com/"/>-->
|
|
||||||
<!--fallback description-->
|
|
||||||
<description>@griffonAppName@</description>
|
|
||||||
<description kind="one-line">@griffonAppName@</description>
|
|
||||||
<description kind="short">@griffonAppName@</description>
|
|
||||||
<description kind="tooltip">@griffonAppName@</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="@griffonAppName@"/>
|
|
||||||
</shortcut>
|
|
||||||
-->
|
|
||||||
<offline-allowed/>
|
|
||||||
</information>
|
|
||||||
<security>
|
|
||||||
<all-permissions/>
|
|
||||||
<!--<j2ee-application-client-permissions/>-->
|
|
||||||
</security>
|
|
||||||
<resources>
|
|
||||||
<property name="griffon.runmode" value="applet"/>
|
|
||||||
<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@
|
|
||||||
@jnlpProperties@
|
|
||||||
</resources>
|
|
||||||
@jnlpResources@
|
|
||||||
<applet-desc
|
|
||||||
documentbase="@griffonAppCodebase@"
|
|
||||||
name="@griffonAppName@Applet"
|
|
||||||
main-class="@griffonAppletClass@"
|
|
||||||
width="@applet.width@"
|
|
||||||
height="@applet.height@">
|
|
||||||
<!-- params are ignored when referenced from web page for 6u10 -->
|
|
||||||
<!--<param name="key1" value="value1"/>-->
|
|
||||||
<!--<param name="key2" value="value2"/>-->
|
|
||||||
@applet.tag.params@
|
|
||||||
</applet-desc>
|
|
||||||
</jnlp>
|
|
@ -1,54 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!DOCTYPE jnlp SYSTEM "http://java.sun.com/dtd/JNLP-1.5.dtd">
|
|
||||||
<jnlp
|
|
||||||
version="@griffonAppVersion@"
|
|
||||||
codebase="@griffonAppCodebase@"
|
|
||||||
href="@jnlpFileName@"
|
|
||||||
>
|
|
||||||
<information>
|
|
||||||
<title>@griffonAppName@</title>
|
|
||||||
<vendor>@griffonAppName@</vendor>
|
|
||||||
<!--<homepage href="http://app.example.com/"/>-->
|
|
||||||
<!--fallback description-->
|
|
||||||
<description>@griffonAppName@</description>
|
|
||||||
<description kind="one-line">@griffonAppName@</description>
|
|
||||||
<description kind="short">@griffonAppName@</description>
|
|
||||||
<description kind="tooltip">@griffonAppName@</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="@griffonAppName@"/>
|
|
||||||
</shortcut>
|
|
||||||
<offline-allowed/>
|
|
||||||
-->
|
|
||||||
</information>
|
|
||||||
<security>
|
|
||||||
<all-permissions/>
|
|
||||||
<!--<j2ee-application-client-permissions/>-->
|
|
||||||
</security>
|
|
||||||
<resources>
|
|
||||||
<property name="griffon.runmode" value="webstart"/>
|
|
||||||
<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@
|
|
||||||
@jnlpProperties@
|
|
||||||
</resources>
|
|
||||||
@jnlpResources@
|
|
||||||
<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"/>-->
|
|
||||||
@applet.tag.params@
|
|
||||||
</application-desc>
|
|
||||||
</jnlp>
|
|
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 15 KiB |
@ -1,26 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Category
|
|
||||||
import com.jdbernard.pit.Status
|
|
||||||
|
|
||||||
class NewIssueDialogController {
|
|
||||||
// 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 show = {
|
|
||||||
view.titleTextField.text = ""
|
|
||||||
model.text = ""
|
|
||||||
view.categoryComboBox.selectedItem = Category.BUG
|
|
||||||
model.category = Category.BUG
|
|
||||||
view.statusComboBox.selectedItem = Status.NEW
|
|
||||||
model.status = Status.NEW
|
|
||||||
view.prioritySpinner.setValue(5)
|
|
||||||
model.priority = 5
|
|
||||||
view.dialog.visible = true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,172 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Category
|
|
||||||
import com.jdbernard.pit.FileProject
|
|
||||||
import javax.swing.JFileChooser
|
|
||||||
import javax.swing.SwingUtilities
|
|
||||||
|
|
||||||
class PITController {
|
|
||||||
|
|
||||||
// these will be injected by Griffon
|
|
||||||
def model
|
|
||||||
def view
|
|
||||||
|
|
||||||
void mvcGroupInit(Map args) {
|
|
||||||
|
|
||||||
model.newIssueDialogMVC = buildMVCGroup('NewIssueDialog')
|
|
||||||
|
|
||||||
SwingUtilities.invokeAndWait {
|
|
||||||
model.issueListRenderer = new IssueTableCellRenderer()
|
|
||||||
|
|
||||||
File pitHome, pitrcFile, pitswingrcFile
|
|
||||||
boolean logDbg = logger.isDebugEnabled()
|
|
||||||
Properties config = new Properties()
|
|
||||||
|
|
||||||
// look for config directory
|
|
||||||
pitHome = new File(System.getProperty('user.home'), '.pit')
|
|
||||||
if (logDbg) logger.debug("$pitHome is " +
|
|
||||||
(pitHome.exists() ? '' : 'not ') + "present.")
|
|
||||||
|
|
||||||
// look for general config options
|
|
||||||
pitrcFile = new File(pitHome, 'pitrc')
|
|
||||||
if (logDbg) logger.debug("$pitrcFile is " +
|
|
||||||
(pitrcFile.exists() ? '' : 'not ') + "present.")
|
|
||||||
|
|
||||||
// load general config (if present)
|
|
||||||
if (pitrcFile.exists() && pitrcFile.canRead()) {
|
|
||||||
pitrcFile.withInputStream { config.load(it) }
|
|
||||||
if (logDbg) logger.debug("Loaded pitrc")
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for swing specific config
|
|
||||||
pitswingrcFile = new File(pitHome, 'pitswingrc')
|
|
||||||
if (logDbg) logger.debug("$pitswingrcFile is " +
|
|
||||||
(pitswingrcFile.exists() ? '' : 'not ') + "present.")
|
|
||||||
|
|
||||||
// load swing specific config (if present)
|
|
||||||
if (pitswingrcFile.exists() && pitswingrcFile.canRead()) {
|
|
||||||
pitswingrcFile.withInputStream { config.load(it) }
|
|
||||||
if (logDbg) logger.debug("Loaded pitswingrc")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process configurable options
|
|
||||||
// ----------------------------
|
|
||||||
|
|
||||||
if (logDbg) {
|
|
||||||
logger.debug("Configurable properties:")
|
|
||||||
config.keySet().each { logger.debug(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// add custom category templates
|
|
||||||
Category.values().each { category ->
|
|
||||||
def expectedKey = "issue." + category.name().toLowerCase() +
|
|
||||||
".template"
|
|
||||||
if (logDbg) logger.debug("Looking for key: $expectedKey")
|
|
||||||
|
|
||||||
config.keySet().each { currentKey ->
|
|
||||||
if (currentKey == expectedKey)
|
|
||||||
model.templates[(category)] =
|
|
||||||
config.getProperty(expectedKey, "")
|
|
||||||
if (logDbg) logger.debug("Template for category $category: '" +
|
|
||||||
model.templates[(category)] + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// load custom issueListRenderer
|
|
||||||
// TODO: not yet supported (maybe no need)
|
|
||||||
|
|
||||||
// load initial repositories
|
|
||||||
if (config.containsKey('initial-repositories')) {
|
|
||||||
def initRepos = config.getProperty('initial-repositories', '')
|
|
||||||
initRepos = initRepos.split(/[:;,]/)
|
|
||||||
initRepos.each { repoPath -> loadProject(new File(repoPath)) }
|
|
||||||
if (logDbg) logger.debug("Init repos: '$initRepos'")
|
|
||||||
}
|
|
||||||
|
|
||||||
// load custom issue css
|
|
||||||
if (config.containsKey('issue.display.css')) {
|
|
||||||
def issueCSS = config.getProperty('issue.display.css', "")
|
|
||||||
|
|
||||||
// look for a file relative to the pit home directory
|
|
||||||
def cssFile
|
|
||||||
|
|
||||||
// use short-circuit logic to test several possible locations
|
|
||||||
if ((cssFile = new File(pitHome, issueCSS)).exists() ||
|
|
||||||
(cssFile = new File(pitHome.parentFile(), issueCSS)).exists() ||
|
|
||||||
(cssFile = new File(issueCSS)).exists())
|
|
||||||
issueCSS = cssFile.text
|
|
||||||
|
|
||||||
if (logDbg) logger.debug("CSS for issue display: $issueCSS")
|
|
||||||
model.issueCSS = issueCSS
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void refreshIssues() {
|
|
||||||
model.projectPanelMVCs.each { title, mvc ->
|
|
||||||
mvc.controller.refreshIssues()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def openProject = { evt = null ->
|
|
||||||
if (view.openDialog.showOpenDialog(view.frame) !=
|
|
||||||
JFileChooser.APPROVE_OPTIONS) return
|
|
||||||
|
|
||||||
loadProject(view.openDialog.selectedFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
def loadProject = { File projectDir ->
|
|
||||||
def newMVC
|
|
||||||
|
|
||||||
// if this is not a valid directory, do nothing
|
|
||||||
// TODO: log to the user that this is not a valid directory
|
|
||||||
if (!projectDir.exists() || !projectDir.isDirectory()) return
|
|
||||||
|
|
||||||
// create new ProjectPanel MVC
|
|
||||||
newMVC = buildMVCGroup('ProjectPanel',
|
|
||||||
mainMVC: [model: model, view: view, controller: this],
|
|
||||||
newIssueDialogMVC: model.newIssueDialogMVC,
|
|
||||||
issueCellRenderer: model.issueListRenderer,
|
|
||||||
issueCSS: model.issueCSS,
|
|
||||||
rootProject: new FileProject(projectDir))
|
|
||||||
newMVC.model.id = projectDir.name
|
|
||||||
|
|
||||||
// if we already have a tab with this id
|
|
||||||
if (model.projectPanelMVCs[(newMVC.model.id)]) {
|
|
||||||
|
|
||||||
// try using the canonical path
|
|
||||||
newMVC.model.id = projectDir.canonicalPath
|
|
||||||
|
|
||||||
// still not unique?
|
|
||||||
if (model.projectPanelMVCs[(newMVC.model.id)]) {
|
|
||||||
|
|
||||||
// first time this has happened?
|
|
||||||
if (!model.projectIdMap[(newMVC.model.id)])
|
|
||||||
model.projectIdMap[(newMVC.model.id)] = 0
|
|
||||||
|
|
||||||
// no? increment
|
|
||||||
else model.projectIdMap[(newMVC.model.id)] =
|
|
||||||
model.projectIdMap[(newMVC.model.id)] + 1
|
|
||||||
|
|
||||||
// use our new, unique id
|
|
||||||
newMVC.model.id += "-" + model.projectIdMap[(newMVC.model.id)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
model.projectPanelMVCs[(newMVC.model.id)] = newMVC
|
|
||||||
view.mainTabbedPane.addTab(newMVC.model.id, newMVC.view.panel)
|
|
||||||
}
|
|
||||||
|
|
||||||
def closeProject = { evt = null ->
|
|
||||||
model.projectPanelMVCs.remove(view.mainTabbedPane.getTitleAt(
|
|
||||||
view.mainTabbedPane.selectedIndex))
|
|
||||||
view.mainTabbedPane.remove(view.mainTabbedPane.selectedComponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
def shutdown = { evt = null ->
|
|
||||||
app.shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,222 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Category
|
|
||||||
import com.jdbernard.pit.FileProject
|
|
||||||
import com.jdbernard.pit.FlatProjectView
|
|
||||||
import com.jdbernard.pit.Issue
|
|
||||||
import com.jdbernard.pit.Project
|
|
||||||
import com.jdbernard.pit.Status
|
|
||||||
import javax.swing.DefaultListModel
|
|
||||||
import javax.swing.JOptionPane
|
|
||||||
import javax.swing.tree.DefaultMutableTreeNode
|
|
||||||
import javax.swing.tree.DefaultTreeModel
|
|
||||||
import org.dom4j.Document
|
|
||||||
import org.dom4j.io.OutputFormat
|
|
||||||
import org.dom4j.io.XMLWriter
|
|
||||||
import org.nuiton.jrst.JRSTGenerator
|
|
||||||
import org.nuiton.jrst.JRSTReader
|
|
||||||
|
|
||||||
class ProjectPanelController {
|
|
||||||
// these will be injected by Griffon
|
|
||||||
def model
|
|
||||||
def view
|
|
||||||
|
|
||||||
def jrstReader
|
|
||||||
def jrstGen
|
|
||||||
|
|
||||||
static URL rst2htmlXSL =
|
|
||||||
ProjectPanelController.class.getResource("/rst2xhtml.xsl")
|
|
||||||
|
|
||||||
void mvcGroupInit(Map args) {
|
|
||||||
jrstReader = new JRSTReader()
|
|
||||||
jrstGen = new JRSTGenerator()
|
|
||||||
|
|
||||||
refreshProject()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* displayProject
|
|
||||||
* @param project Project to display
|
|
||||||
*/
|
|
||||||
void displayProject(Project project) {
|
|
||||||
if (!project) return
|
|
||||||
|
|
||||||
view.issueTextArea.text = ""
|
|
||||||
view.issueTextDisplay.text = ""
|
|
||||||
view.issueTextPanelLayout.show(view.issueTextPanel, 'display')
|
|
||||||
|
|
||||||
// build a new IssueTableModel if none cached
|
|
||||||
if (!model.projectTableModels[(project.name)]) {
|
|
||||||
def itm = new IssueTableModel(project,
|
|
||||||
model.filter ?: model.mainMVC.model.filter)
|
|
||||||
itm.categoryIcons = model.mainMVC.model.categoryIcons
|
|
||||||
itm.statusIcons = model.mainMVC.model.statusIcons
|
|
||||||
model.projectTableModels[(project.name)] = itm
|
|
||||||
}
|
|
||||||
|
|
||||||
view.issueTable.setModel(model.projectTableModels[(project.name)])
|
|
||||||
|
|
||||||
def tcm = view.issueTable.columnModel
|
|
||||||
tcm.getColumn(0).maxWidth = 24
|
|
||||||
tcm.getColumn(1).maxWidth = 40
|
|
||||||
tcm.getColumn(2).maxWidth = 35
|
|
||||||
if (view.issueTable.model.columnCount == 5)
|
|
||||||
tcm.getColumn(4).maxWidth = 150
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayIssue(Issue issue) {
|
|
||||||
if (!issue) return
|
|
||||||
|
|
||||||
// hack because binding view.issueTextArea.font to
|
|
||||||
// mainMVC.model.issueDetailFont causes problems
|
|
||||||
if (view.issueTextArea.font != model.mainMVC.model.issueDetailFont)
|
|
||||||
view.issueTextArea.font = model.mainMVC.model.issueDetailFont
|
|
||||||
|
|
||||||
view.issueTextArea.text = issue.text
|
|
||||||
view.issueTextArea.caretPosition = 0
|
|
||||||
view.issueTextDisplay.text = rst2html(issue.text)
|
|
||||||
view.issueTextDisplay.caretPosition = 0
|
|
||||||
view.issueTextPanelLayout.show(view.issueTextPanel, 'display')
|
|
||||||
}
|
|
||||||
|
|
||||||
void showProejctPopup(Project project, def x, def y) {
|
|
||||||
model.popupProject = project
|
|
||||||
view.projectPopupMenu.show(view.projectTree, x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
void showIssuePopup(Issue issue, def x, def y) {
|
|
||||||
model.popupIssue = issue
|
|
||||||
view.issuePopupMenu.show(view.issueTable, x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
void refreshProject() {
|
|
||||||
if (model.rootProject) {
|
|
||||||
def rootNode = new DefaultMutableTreeNode()
|
|
||||||
def flatview = new FlatProjectView('All Issues')
|
|
||||||
flatview.projects[(model.rootProject.name)] = model.rootProject
|
|
||||||
rootNode.add(new DefaultMutableTreeNode(flatview))
|
|
||||||
rootNode.add(makeNodes(model.rootProject))
|
|
||||||
view.projectTree.model = new DefaultTreeModel(rootNode)
|
|
||||||
} else {
|
|
||||||
view.projectTree.model = new DefaultTreeModel(
|
|
||||||
new DefaultMutableTreeNode())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void refreshIssues() {
|
|
||||||
model.projectTableModels.clear()
|
|
||||||
displayProject(model.selectedProject)
|
|
||||||
}
|
|
||||||
|
|
||||||
def makeNodes(Project project) {
|
|
||||||
def rootNode = new DefaultMutableTreeNode(project)
|
|
||||||
project.eachProject(model.filter ?: model.mainMVC.model.filter)
|
|
||||||
{ rootNode.add(makeNodes(it)) }
|
|
||||||
return rootNode
|
|
||||||
}
|
|
||||||
|
|
||||||
def newProject = { evt ->
|
|
||||||
def name = JOptionPane.showInputDialog(model.mainMVC.view.frame,
|
|
||||||
'Project name:', 'New Project...', JOptionPane.QUESTION_MESSAGE)
|
|
||||||
|
|
||||||
def project
|
|
||||||
|
|
||||||
if (evt.source == view.newProjectButton)
|
|
||||||
project = model.selectedProject ?: model.rootProject
|
|
||||||
else project = model.popupProject ?: model.rootProject
|
|
||||||
def newProject = project.createNewProject(name)
|
|
||||||
|
|
||||||
project.projects[(newProject.name)] = newProject
|
|
||||||
refreshProject()
|
|
||||||
}
|
|
||||||
|
|
||||||
def deleteProject = { evt ->
|
|
||||||
def project
|
|
||||||
|
|
||||||
if (evt.source == view.deleteProjectButton)
|
|
||||||
project = model.selectedProject ?: model.rootProject
|
|
||||||
else project = model.popupProject ?: model.rootProject
|
|
||||||
|
|
||||||
project.delete()
|
|
||||||
|
|
||||||
model.rootProject = new FileProject(model.rootProject.source)
|
|
||||||
}
|
|
||||||
|
|
||||||
def newIssue = { evt = null ->
|
|
||||||
model.newIssueDialogMVC.controller.show()
|
|
||||||
if (model.newIssueDialogMVC.model.accept) {
|
|
||||||
def nidModel = model.newIssueDialogMVC.model
|
|
||||||
def issueText = nidModel.text
|
|
||||||
|
|
||||||
if (model.mainMVC.model.templates[(nidModel.category)]) {
|
|
||||||
issueText = model.mainMVC.model.templates[(nidModel.category)]
|
|
||||||
issueText = issueText.replaceFirst(/TITLE/,
|
|
||||||
nidModel.text)
|
|
||||||
}
|
|
||||||
|
|
||||||
def issue = model.selectedProject.createNewIssue(
|
|
||||||
category: nidModel.category,
|
|
||||||
status: nidModel.status,
|
|
||||||
priority: nidModel.priority,
|
|
||||||
text: issueText)
|
|
||||||
model.projectTableModels[(model.selectedProject.name)] = null
|
|
||||||
displayProject(model.selectedProject)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def deleteIssue = { evt ->
|
|
||||||
def issue
|
|
||||||
if (evt.source == view.deleteIssueButton)
|
|
||||||
issue = getSelectedIssue()
|
|
||||||
else issue = model.popupIssue
|
|
||||||
|
|
||||||
model.selectedProject.issues.remove(issue.id)
|
|
||||||
view.issueTable.model.issues.remove(issue)
|
|
||||||
|
|
||||||
issue.delete()
|
|
||||||
view.issueTable.invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
def getSelectedIssue() {
|
|
||||||
if (view.issueTable.selectionModel.isSelectionEmpty())
|
|
||||||
return null
|
|
||||||
|
|
||||||
return view.issueTable.model.issues[view.issueTable.
|
|
||||||
convertRowIndexToModel(view.issueTable.selectedRow)]
|
|
||||||
}
|
|
||||||
|
|
||||||
String rst2html(String rst) {
|
|
||||||
Document doc
|
|
||||||
StringWriter outString
|
|
||||||
StringBuilder result = new StringBuilder()
|
|
||||||
|
|
||||||
// read the RST in with the RST parser
|
|
||||||
new StringReader(rst).withReader { doc = jrstReader.read(it) }
|
|
||||||
|
|
||||||
// transform to XHTML
|
|
||||||
doc = jrstGen.transform(doc, rst2htmlXSL)
|
|
||||||
|
|
||||||
// write to the StringWriter
|
|
||||||
outString = new StringWriter()
|
|
||||||
outString.withWriter { new XMLWriter(it, new OutputFormat("", true)).write(doc) }
|
|
||||||
|
|
||||||
// java's embeded html is primitive, we need to massage the results
|
|
||||||
outString.toString().eachLine { line ->
|
|
||||||
|
|
||||||
// remove the XML version and encoding, title element, meta elems
|
|
||||||
if (line =~ /<\?.*\?>/ || line =~ /<meta.*$/ || line =~ /<title.*$/) { return }
|
|
||||||
|
|
||||||
// all other elements, remove all class, xmlns attributes
|
|
||||||
def m = (line =~ /(<\S+)(\s*(class|xmlns)=".*"\s*)*(\/?>.*)/)
|
|
||||||
if (m) line = m[0][1] + m[0][4]
|
|
||||||
|
|
||||||
result.append(line)
|
|
||||||
|
|
||||||
// add in the CSS information to the head
|
|
||||||
if (line =~ /<head>/) result.append('<style type="text/css">' +
|
|
||||||
model.issueCSS + '</style>')
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.toString()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* This script is executed inside the UI thread, so be sure to call
|
|
||||||
* long running code in another thread.
|
|
||||||
*
|
|
||||||
* You have the following options
|
|
||||||
* - execOutside { // your code }
|
|
||||||
* - execFuture { // your code }
|
|
||||||
* - Thread.start { // your code }
|
|
||||||
*
|
|
||||||
* You have the following options to run code again inside the UI thread
|
|
||||||
* - execAsync { // your code }
|
|
||||||
* - execSync { // your code }
|
|
||||||
*/
|
|
||||||
|
|
||||||
import groovy.swing.SwingBuilder
|
|
||||||
import griffon.util.GriffonPlatformHelper
|
|
||||||
import static griffon.util.GriffonApplicationUtils.*
|
|
||||||
|
|
||||||
GriffonPlatformHelper.tweakForNativePlatform(app)
|
|
||||||
SwingBuilder.lookAndFeel('org.pushingpixels.substance.api.skin.SubstanceCremeCoffeeLookAndFeel', 'nimbus', ['metal', [boldFonts: false]])
|
|
||||||
|
|
||||||
// make config directory
|
|
||||||
def confDir = new File(System.getProperty('user.home'), '.pit')
|
|
||||||
if (!confDir.exists()) confDir.mkdirs()
|
|
@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* This script is executed inside the UI thread, so be sure to call
|
|
||||||
* long running code in another thread.
|
|
||||||
*
|
|
||||||
* You have the following options
|
|
||||||
* - execOutside { // your code }
|
|
||||||
* - execFuture { // your code }
|
|
||||||
* - Thread.start { // your code }
|
|
||||||
*
|
|
||||||
* You have the following options to run code again inside the UI thread
|
|
||||||
* - execAsync { // your code }
|
|
||||||
* - execSync { // your code }
|
|
||||||
*/
|
|
@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* This script is executed inside the UI thread, so be sure to call
|
|
||||||
* long running code in another thread.
|
|
||||||
*
|
|
||||||
* You have the following options
|
|
||||||
* - execOutside { // your code }
|
|
||||||
* - execFuture { // your code }
|
|
||||||
* - Thread.start { // your code }
|
|
||||||
*
|
|
||||||
* You have the following options to run code again inside the UI thread
|
|
||||||
* - execAsync { // your code }
|
|
||||||
* - execSync { // your code }
|
|
||||||
*/
|
|
@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* This script is executed inside the UI thread, so be sure to call
|
|
||||||
* long running code in another thread.
|
|
||||||
*
|
|
||||||
* You have the following options
|
|
||||||
* - execOutside { // your code }
|
|
||||||
* - execFuture { // your code }
|
|
||||||
* - Thread.start { // your code }
|
|
||||||
*
|
|
||||||
* You have the following options to run code again inside the UI thread
|
|
||||||
* - execAsync { // your code }
|
|
||||||
* - execSync { // your code }
|
|
||||||
*/
|
|
@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* This script is executed inside the UI thread, so be sure to call
|
|
||||||
* long running code in another thread.
|
|
||||||
*
|
|
||||||
* You have the following options
|
|
||||||
* - execOutside { // your code }
|
|
||||||
* - execFuture { // your code }
|
|
||||||
* - Thread.start { // your code }
|
|
||||||
*
|
|
||||||
* You have the following options to run code again inside the UI thread
|
|
||||||
* - execAsync { // your code }
|
|
||||||
* - execSync { // your code }
|
|
||||||
*/
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Category
|
|
||||||
import com.jdbernard.pit.Status
|
|
||||||
import groovy.beans.Bindable
|
|
||||||
|
|
||||||
class NewIssueDialogModel {
|
|
||||||
@Bindable boolean accept
|
|
||||||
String text
|
|
||||||
Category category
|
|
||||||
Status status
|
|
||||||
int priority
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Category
|
|
||||||
import com.jdbernard.pit.Filter
|
|
||||||
import com.jdbernard.pit.Issue
|
|
||||||
import com.jdbernard.pit.Project
|
|
||||||
import com.jdbernard.pit.Status
|
|
||||||
import groovy.beans.Bindable
|
|
||||||
import java.awt.Font
|
|
||||||
import javax.swing.ImageIcon
|
|
||||||
|
|
||||||
class PITModel {
|
|
||||||
|
|
||||||
// filter for projects and classes
|
|
||||||
Filter filter = new Filter(categories: [],
|
|
||||||
status: [Status.NEW, Status.VALIDATION_REQUIRED])
|
|
||||||
|
|
||||||
def issueListRenderer
|
|
||||||
|
|
||||||
// map of category -> issue template
|
|
||||||
Map<Category, String> templates = [:]
|
|
||||||
|
|
||||||
String issueCSS = getClass().getResourceAsStream("/default-issue.css").text
|
|
||||||
|
|
||||||
Map<Category, ImageIcon> categoryIcons = [:]
|
|
||||||
Map<Category, ImageIcon> statusIcons = [:]
|
|
||||||
|
|
||||||
def newIssueDialogMVC
|
|
||||||
Map projectPanelMVCs = [:]
|
|
||||||
|
|
||||||
Map projectIdMap = [:]
|
|
||||||
|
|
||||||
@Bindable Font issueDetailFont = new Font(Font.MONOSPACED, Font.PLAIN, 10)
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Filter
|
|
||||||
import com.jdbernard.pit.Issue
|
|
||||||
import com.jdbernard.pit.Project
|
|
||||||
import groovy.beans.Bindable
|
|
||||||
|
|
||||||
class ProjectPanelModel {
|
|
||||||
|
|
||||||
// other GUI components
|
|
||||||
def mainMVC
|
|
||||||
def newIssueDialogMVC
|
|
||||||
|
|
||||||
// data owned by this panel
|
|
||||||
String id
|
|
||||||
@Bindable Project rootProject
|
|
||||||
@Bindable Project popupProject = null
|
|
||||||
@Bindable Project selectedProject = null
|
|
||||||
@Bindable Issue popupIssue = null
|
|
||||||
|
|
||||||
String issueCSS = ""
|
|
||||||
|
|
||||||
// cache the models
|
|
||||||
def projectTableModels = [:]
|
|
||||||
def issueCellRenderer
|
|
||||||
|
|
||||||
// local filter for projects and issues
|
|
||||||
Filter filter
|
|
||||||
}
|
|
Before Width: | Height: | Size: 733 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 774 B |
Before Width: | Height: | Size: 587 B |
@ -1,22 +0,0 @@
|
|||||||
body {
|
|
||||||
font-size: small;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: medium;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: small;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: small;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
font-size: small;
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
table,th,td{
|
|
||||||
border-style: solid;
|
|
||||||
}
|
|
Before Width: | Height: | Size: 715 B |
Before Width: | Height: | Size: 670 B |
Before Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 822 B |
@ -1,10 +0,0 @@
|
|||||||
log4j.rootLogger=DEBUG,stdout,fileout
|
|
||||||
|
|
||||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
|
||||||
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
|
|
||||||
|
|
||||||
log4j.appender.fileout=org.apache.log4j.FileAppender
|
|
||||||
log4j.appender.fileout.file=pit-swing.log
|
|
||||||
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
|
|
||||||
log4j.appender.fileout.layout.ConversionPattern=%-5p %C %d{DATE}: %m%n
|
|
||||||
log4j.appender.fileout.threshold=INFO
|
|
Before Width: | Height: | Size: 146 B |
Before Width: | Height: | Size: 559 B |
Before Width: | Height: | Size: 322 B |
Before Width: | Height: | Size: 221 B |
@ -1,495 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<xsl:stylesheet version="1.0"
|
|
||||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
||||||
xmlns="http://www.w3.org/TR/xhtml1/strict">
|
|
||||||
|
|
||||||
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
|
|
||||||
|
|
||||||
<xsl:template match="/document">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<meta name="generator" content="JRST http://maven-site.nuiton.org/jrst" />
|
|
||||||
<title><xsl:value-of select="title"/></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="comment">
|
|
||||||
<xsl:comment>
|
|
||||||
<xsl:text> </xsl:text>
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
<xsl:text> </xsl:text>
|
|
||||||
</xsl:comment>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="title">
|
|
||||||
<xsl:if test="name(..)='document'">
|
|
||||||
<h1 class="mainTitle">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</h1>
|
|
||||||
</xsl:if>
|
|
||||||
|
|
||||||
<xsl:if test="not(name(..)='document')">
|
|
||||||
<xsl:element name="h{count(ancestor::section) + 1}">
|
|
||||||
<xsl:attribute name="class">title</xsl:attribute>
|
|
||||||
<xsl:if test="@refid">
|
|
||||||
<a class="toc-backref" href="#{@refid}" id="{../@id}"><xsl:apply-templates/></a>
|
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="not(@refid)">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</xsl:if>
|
|
||||||
</xsl:element>
|
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="subtitle">
|
|
||||||
<xsl:element name="h2">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</xsl:element>
|
|
||||||
</xsl:template>
|
|
||||||
<!-- just eat it -->
|
|
||||||
<xsl:template match="substitution_definition">
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="docinfo">
|
|
||||||
<table class="docinfo" frame="void" rules="none">
|
|
||||||
<col class="docinfo-name" />
|
|
||||||
<col class="docinfo-content" />
|
|
||||||
<tbody valign="top">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="organization|address|contact|version|revision|status|date|copyright">
|
|
||||||
<tr>
|
|
||||||
<th class="docinfo-name">
|
|
||||||
<xsl:value-of select="name(.)"/> :
|
|
||||||
</th>
|
|
||||||
<td class="docinfo-content">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="author">
|
|
||||||
<xsl:if test="not(../../authors)">
|
|
||||||
<tr>
|
|
||||||
<th class="docinfo-name">
|
|
||||||
<xsl:value-of select="name(.)"/> :
|
|
||||||
</th>
|
|
||||||
<td class="docinfo-content">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="../../authors">
|
|
||||||
<xsl:variable name="num" select="position()"/>
|
|
||||||
<xsl:if test="$num=1">
|
|
||||||
<tr>
|
|
||||||
<th class="docinfo-name">
|
|
||||||
<xsl:value-of select="authors"/>authors :
|
|
||||||
</th>
|
|
||||||
<td class="docinfo-content">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="$num>1">
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
|
|
||||||
</th>
|
|
||||||
<td class="docinfo-content">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</xsl:if>
|
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
|
|
||||||
<xsl:template match="transition">
|
|
||||||
<hr/>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="section">
|
|
||||||
<a name="{@id}"></a>
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="list_item/paragraph[1] | definition_list_item/*/paragraph[1] | field/*/paragraph[1] | option/*/paragraph[1]">
|
|
||||||
<!--XXX - Unclear how to handle multi-paragraph list items.
|
|
||||||
| Certainly when they're single paragraphs, we don't want them
|
|
||||||
| wrapped in a <P> tag. This seems to work okay.
|
|
||||||
+-->
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="paragraph">
|
|
||||||
<p><xsl:apply-templates/></p>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="reference">
|
|
||||||
<xsl:if test="@refid">
|
|
||||||
<a href="{@refuri}#{@refid}" id="{@id}"><xsl:apply-templates/></a>
|
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="not(@refid)">
|
|
||||||
<a href="{@refuri}" id="{@id}"><xsl:apply-templates/></a>
|
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="emphasis">
|
|
||||||
<em><xsl:apply-templates/></em>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="strong">
|
|
||||||
<b><xsl:apply-templates/></b>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="literal">
|
|
||||||
<code><xsl:value-of select="text()"/></code>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="literal_block">
|
|
||||||
<pre class="literal_block"><xsl:value-of select="text()"/></pre>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="bullet_list">
|
|
||||||
<ul><xsl:apply-templates/></ul>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="enumerated_list">
|
|
||||||
<ol>
|
|
||||||
<xsl:choose>
|
|
||||||
<xsl:when test="@enumtype='arabic'">
|
|
||||||
<xsl:attribute name="type">1</xsl:attribute>
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:when test="@enumtype='loweralpha'">
|
|
||||||
<xsl:attribute name="type">a</xsl:attribute>
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:when test="@enumtype='upperalpha'">
|
|
||||||
<xsl:attribute name="type">A</xsl:attribute>
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:when test="@enumtype='lowerroman'">
|
|
||||||
<xsl:attribute name="type">i</xsl:attribute>
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:when test="@enumtype='upperroman'">
|
|
||||||
<xsl:attribute name="type">I</xsl:attribute>
|
|
||||||
</xsl:when>
|
|
||||||
</xsl:choose>
|
|
||||||
<xsl:copy-of select="@start"/>
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</ol>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="list_item">
|
|
||||||
<li><xsl:apply-templates/></li>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="field_list">
|
|
||||||
<div class="field_list"><xsl:apply-templates/></div>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="field">
|
|
||||||
|
|
||||||
<xsl:if test="not(../../docinfo)">
|
|
||||||
<div class="field"><xsl:apply-templates/></div>
|
|
||||||
</xsl:if>
|
|
||||||
|
|
||||||
<xsl:if test="../../docinfo">
|
|
||||||
<tr>
|
|
||||||
<th class="docinfo-name">
|
|
||||||
<xsl:value-of select="field_name/text()"/> :
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<xsl:apply-templates select="field_body/*"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</xsl:if>
|
|
||||||
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="field_name">
|
|
||||||
<span class="field_name"><xsl:apply-templates/></span>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="field_body">
|
|
||||||
<span class="field_body"><xsl:apply-templates/></span>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="definition_list">
|
|
||||||
<dl class="definition_list"><xsl:apply-templates/></dl>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="definition_list_item">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="term">
|
|
||||||
<dt class="term"><xsl:apply-templates/><xsl:call-template name="classifier"/></dt>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template name="classifier">
|
|
||||||
<xsl:for-each select="../classifier">
|
|
||||||
<span class="classifier"><xsl:apply-templates/></span>
|
|
||||||
</xsl:for-each>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="classifier">
|
|
||||||
<!-- do nothing -->
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="definition">
|
|
||||||
<dd class="definition"><xsl:apply-templates/></dd>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="image">
|
|
||||||
<xsl:choose>
|
|
||||||
<xsl:when test="(@target) and (@align)">
|
|
||||||
<div class="align-{@align}" align="{@align}">
|
|
||||||
<a href="{@target}">
|
|
||||||
<xsl:call-template name="img" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:when test="@target">
|
|
||||||
<a href="{@target}">
|
|
||||||
<xsl:call-template name="img" />
|
|
||||||
</a>
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:when test="@align">
|
|
||||||
<div class="align-{@align}" align="{@align}">
|
|
||||||
<xsl:call-template name="img" />
|
|
||||||
</div>
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:otherwise>
|
|
||||||
<xsl:call-template name="img" />
|
|
||||||
</xsl:otherwise>
|
|
||||||
</xsl:choose>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template name="img">
|
|
||||||
<xsl:element name="img">
|
|
||||||
<xsl:attribute name="alt"><xsl:value-of select="@alt"/></xsl:attribute>
|
|
||||||
<xsl:attribute name="src"><xsl:value-of select="@uri"/></xsl:attribute>
|
|
||||||
<xsl:if test="@width"><xsl:attribute name="width"><xsl:value-of select="@width"/></xsl:attribute></xsl:if>
|
|
||||||
<xsl:if test="@height"><xsl:attribute name="height"><xsl:value-of select="@height"/></xsl:attribute></xsl:if>
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</xsl:element>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
|
|
||||||
<xsl:template match="footer">
|
|
||||||
<hr/>
|
|
||||||
<p class="footer"><xsl:apply-templates/></p>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="header">
|
|
||||||
<p class="header"><xsl:apply-templates/></p>
|
|
||||||
<hr/>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
| Table
|
|
||||||
+-->
|
|
||||||
<xsl:template match="table">
|
|
||||||
<table border="1">
|
|
||||||
<colgroup>
|
|
||||||
<xsl:apply-templates select="tgroup/colspec"/>
|
|
||||||
</colgroup>
|
|
||||||
<xsl:apply-templates select="./tgroup/thead|./tgroup/tbody"/>
|
|
||||||
</table>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="tgroup/colspec">
|
|
||||||
<col width="{@colwidth}%"/>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="row">
|
|
||||||
<tr><xsl:apply-templates/></tr>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="thead">
|
|
||||||
<thead><xsl:apply-templates/></thead>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="thead/row/entry">
|
|
||||||
<th>
|
|
||||||
<xsl:if test="@morecols"><xsl:attribute name="colspan"><xsl:value-of select="@morecols+1"/></xsl:attribute></xsl:if>
|
|
||||||
<xsl:if test="@morerows"><xsl:attribute name="rowspan"><xsl:value-of select="@morerows+1"/></xsl:attribute></xsl:if>
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</th>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="tbody">
|
|
||||||
<tbody><xsl:apply-templates/></tbody>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="tbody/row/entry">
|
|
||||||
<td>
|
|
||||||
<xsl:if test="@morecols"><xsl:attribute name="colspan"><xsl:value-of select="@morecols+1"/></xsl:attribute></xsl:if>
|
|
||||||
<xsl:if test="@morerows"><xsl:attribute name="rowspan"><xsl:value-of select="@morerows+1"/></xsl:attribute></xsl:if>
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</td>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="admonition">
|
|
||||||
<div class="admonition">
|
|
||||||
<div class="{@class}">
|
|
||||||
<p class="{title}">
|
|
||||||
<xsl:apply-templates select="./title"/>
|
|
||||||
</p>
|
|
||||||
<p class="body">
|
|
||||||
<xsl:apply-templates select="child::*[position()>1]"/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="attention|caution|danger|error|hint|important|note|tip|warning">
|
|
||||||
<div class="{name(.)}">
|
|
||||||
<p class="title"><xsl:value-of select="name(.)"/> :</p>
|
|
||||||
<p class="body">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="block_quote">
|
|
||||||
|
|
||||||
<blockquote>
|
|
||||||
<xsl:if test="./attribution">
|
|
||||||
<p><xsl:apply-templates select="child::*[position()=1]"/></p>
|
|
||||||
<p class="attribution">
|
|
||||||
<xsl:apply-templates select="./attribution"/>
|
|
||||||
</p>
|
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="not(./attribution)">
|
|
||||||
<xsl:apply-templates select="child::*"/>
|
|
||||||
</xsl:if>
|
|
||||||
</blockquote>
|
|
||||||
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="doctest_block">
|
|
||||||
<pre class="doctest_block">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</pre>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="line_block">
|
|
||||||
<div class="line_block">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</div>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="line">
|
|
||||||
<div class="line">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</div>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="sidebar">
|
|
||||||
<div class="sidebar">
|
|
||||||
<p class="title">
|
|
||||||
<xsl:apply-templates select="./title"/>
|
|
||||||
</p>
|
|
||||||
<xsl:if test="./subtitle">
|
|
||||||
<p class="subtitle">
|
|
||||||
<xsl:apply-templates select="./subtitle"/>
|
|
||||||
</p>
|
|
||||||
</xsl:if>
|
|
||||||
<xsl:choose>
|
|
||||||
<xsl:when test="./subtitle">
|
|
||||||
<xsl:apply-templates select="child::*[position()>2]"/>
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:otherwise>
|
|
||||||
<xsl:apply-templates select="child::*[position()>1]"/>
|
|
||||||
</xsl:otherwise>
|
|
||||||
</xsl:choose>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="topic">
|
|
||||||
<div class="topic">
|
|
||||||
<p class="title">
|
|
||||||
<xsl:apply-templates select="./title"/>
|
|
||||||
</p>
|
|
||||||
<xsl:apply-templates select="child::*[position()>1]"/>
|
|
||||||
</div>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="option_list">
|
|
||||||
<table class="option_list">
|
|
||||||
<col class="option" />
|
|
||||||
<col class="description" />
|
|
||||||
<tbody valign="top">
|
|
||||||
<xsl:apply-templates/>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="option_list_item">
|
|
||||||
<tr>
|
|
||||||
<td class="option-group">
|
|
||||||
<kbd>
|
|
||||||
<xsl:apply-templates select="./option_group/option"/>
|
|
||||||
</kbd>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<xsl:apply-templates select="./description"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="option">
|
|
||||||
<span class="option">
|
|
||||||
<xsl:value-of select="option_string/text()"/>
|
|
||||||
<xsl:value-of select="./option_argument/@delimiter"/>
|
|
||||||
<xsl:apply-templates select="./option_argument"/>
|
|
||||||
</span>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="option_argument">
|
|
||||||
<var>
|
|
||||||
<xsl:value-of select="text()"/>,
|
|
||||||
</var>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="footnote">
|
|
||||||
<table class="footnote" frame="void" id="{@id}" rules="none">
|
|
||||||
<colgroup>
|
|
||||||
<col class="label"/>
|
|
||||||
<col/>
|
|
||||||
</colgroup>
|
|
||||||
<tbody valign="top">
|
|
||||||
<tr>
|
|
||||||
<td class="label">
|
|
||||||
<a class="backref" href="#{@backrefs}" name="{id}">
|
|
||||||
[<xsl:value-of select="label"/>]
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<!--
|
|
||||||
| <xsl:value-of select="child::*[position()>1]"/>
|
|
||||||
+-->
|
|
||||||
<xsl:apply-templates select="child::*[position()>1]"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="footnote_reference">
|
|
||||||
<a class="footnote_reference" href="#{@refid}" id="{@id}" name="{@id}">
|
|
||||||
[<xsl:value-of select="text()"/>]
|
|
||||||
</a>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
</xsl:stylesheet>
|
|
Before Width: | Height: | Size: 688 B |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 452 B |
Before Width: | Height: | Size: 185 B |
@ -1,65 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Category
|
|
||||||
import com.jdbernard.pit.Status
|
|
||||||
import java.awt.GridBagConstraints as GBC
|
|
||||||
import javax.swing.DefaultComboBoxModel
|
|
||||||
|
|
||||||
dialog = dialog(title: 'New Task...', modal: true, pack: true,
|
|
||||||
locationRelativeTo: null) {
|
|
||||||
|
|
||||||
gridBagLayout()
|
|
||||||
|
|
||||||
label('Title/Summary:',
|
|
||||||
constraints: gbc(gridx: 0, gridy: 0, gridwidth: 3,
|
|
||||||
insets: [5, 5, 0, 5], fill: GBC.HORIZONTAL))
|
|
||||||
titleTextField = textField(
|
|
||||||
constraints: gbc(gridx: 0, gridy: 1, gridwidth: 3,
|
|
||||||
insets: [0, 10, 0, 5], fill: GBC.HORIZONTAL))
|
|
||||||
|
|
||||||
label('Category:',
|
|
||||||
constraints: gbc(gridx: 0, gridy: 2, insets: [5, 5, 0, 0],
|
|
||||||
fill: GBC.HORIZONTAL))
|
|
||||||
categoryComboBox = comboBox(
|
|
||||||
constraints: gbc(gridx: 1, gridy: 2, insets: [5, 5, 0, 5],
|
|
||||||
fill: GBC.HORIZONTAL),
|
|
||||||
model: new DefaultComboBoxModel(Category.values()),
|
|
||||||
editable: false,
|
|
||||||
itemStateChanged: { model.category = categoryComboBox.selectedItem })
|
|
||||||
|
|
||||||
label('Status:',
|
|
||||||
constraints: gbc(gridx: 0, gridy: 3, insets: [5, 5, 0, 0],
|
|
||||||
fill: GBC.HORIZONTAL))
|
|
||||||
statusComboBox = comboBox(
|
|
||||||
constraints: gbc(gridx: 1, gridy: 3, insets: [5, 5, 0, 5],
|
|
||||||
fill: GBC.HORIZONTAL),
|
|
||||||
model: new DefaultComboBoxModel(Status.values()),
|
|
||||||
editable: false,
|
|
||||||
itemStateChanged: { model.status = statusComboBox.selectedItem })
|
|
||||||
|
|
||||||
label('Priority (0-9, 0 is highest priority):',
|
|
||||||
constraints: gbc(gridx: 0, gridy: 4, insets: [5, 5, 0, 0],
|
|
||||||
fill: GBC.HORIZONTAL))
|
|
||||||
prioritySpinner = spinner(
|
|
||||||
constraints: gbc( gridx: 1, gridy: 4, insets: [5, 5, 0, 5],
|
|
||||||
fill: GBC.HORIZONTAL),
|
|
||||||
model: spinnerNumberModel(maximum: 9, minimum: 0),
|
|
||||||
stateChanged: { model.priority = prioritySpinner.value })
|
|
||||||
|
|
||||||
button('Cancel',
|
|
||||||
actionPerformed: {
|
|
||||||
model.accept = false
|
|
||||||
dialog.visible = false
|
|
||||||
},
|
|
||||||
constraints: gbc(gridx: 0, gridy: 5, insets: [5, 5, 5, 5],
|
|
||||||
anchor: GBC.EAST))
|
|
||||||
button('Create Issue',
|
|
||||||
actionPerformed: {
|
|
||||||
model.text = titleTextField.text
|
|
||||||
model.accept = true
|
|
||||||
dialog.visible = false
|
|
||||||
},
|
|
||||||
constraints: gbc(gridx: 1, gridy: 5, insets: [5, 5, 5, 5],
|
|
||||||
anchor: GBC.WEST))
|
|
||||||
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Category
|
|
||||||
import com.jdbernard.pit.Status
|
|
||||||
import com.jdbernard.pit.Filter
|
|
||||||
import com.jdbernard.pit.Issue
|
|
||||||
import com.jdbernard.pit.Project
|
|
||||||
import com.jdbernard.pit.FileProject
|
|
||||||
import groovy.beans.Bindable
|
|
||||||
import java.awt.BorderLayout as BL
|
|
||||||
import java.awt.Color
|
|
||||||
import java.awt.GridBagConstraints as GBC
|
|
||||||
import javax.swing.DefaultComboBoxModel
|
|
||||||
import javax.swing.DefaultListModel
|
|
||||||
import javax.swing.JDialog
|
|
||||||
import javax.swing.JFileChooser
|
|
||||||
import javax.swing.JOptionPane
|
|
||||||
import net.miginfocom.swing.MigLayout
|
|
||||||
|
|
||||||
|
|
||||||
actions {
|
|
||||||
action(
|
|
||||||
id: 'openProject',
|
|
||||||
name: 'Open...',
|
|
||||||
icon: imageIcon('/folder.png'),
|
|
||||||
accelerator: shortcut('O'),
|
|
||||||
closure: controller.openProject
|
|
||||||
)
|
|
||||||
|
|
||||||
action(
|
|
||||||
id: 'closeProject',
|
|
||||||
name: 'Close',
|
|
||||||
enabled: bind { projectPanelMVCs.size() > 0 },
|
|
||||||
closure: controller.closeProject
|
|
||||||
)
|
|
||||||
|
|
||||||
action(
|
|
||||||
id: 'shutdown',
|
|
||||||
name: 'Exit',
|
|
||||||
icon: imageIcon('/shutdown.png'),
|
|
||||||
accelerator: shortcut('x'),
|
|
||||||
closure: controller.shutdown
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize category-related view data
|
|
||||||
Category.values().each {
|
|
||||||
model.categoryIcons[(it)] = imageIcon("/${it.name().toLowerCase()}.png")
|
|
||||||
model.filter.categories.add(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
Status.values().each {
|
|
||||||
model.statusIcons[(it)] = imageIcon("/${it.name().toLowerCase()}.png")
|
|
||||||
}
|
|
||||||
|
|
||||||
frame = application(title: 'Personal Issue Tracker',
|
|
||||||
minimumSize: [400, 200],
|
|
||||||
preferredSize: [800, 500],
|
|
||||||
pack: true,
|
|
||||||
locationRelativeTo: null,
|
|
||||||
iconImage: imageIcon('/icon64x64.png').image,
|
|
||||||
iconImages: [imageIcon('/icon64x64.png').image,
|
|
||||||
imageIcon('/icon32x32.png').image,
|
|
||||||
imageIcon('/icon16x16.png').image]
|
|
||||||
) {
|
|
||||||
|
|
||||||
// main menu
|
|
||||||
menuBar() {
|
|
||||||
menu("File") {
|
|
||||||
menuItem(openProject)
|
|
||||||
menuItem(closeProject)
|
|
||||||
separator()
|
|
||||||
menuItem(shutdown)
|
|
||||||
}
|
|
||||||
|
|
||||||
menu("View") {
|
|
||||||
menu('Category') {
|
|
||||||
Category.values().each { cat ->
|
|
||||||
checkBoxMenuItem(cat.toString(),
|
|
||||||
selected: model.filter.categories.contains(cat),
|
|
||||||
actionPerformed: {
|
|
||||||
if (model.filter.categories.contains(cat)) {
|
|
||||||
model.filter.categories.remove(cat)
|
|
||||||
evt.source.selected = false
|
|
||||||
} else {
|
|
||||||
model.filter.categories.add(cat)
|
|
||||||
evt.source.selected = true
|
|
||||||
}
|
|
||||||
controller.refreshIssues()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
menu('Status') {
|
|
||||||
Status.values().each { st ->
|
|
||||||
checkBoxMenuItem(st.toString(),
|
|
||||||
selected: model.filter.status.contains(st),
|
|
||||||
actionPerformed: { evt ->
|
|
||||||
if (model.filter.status.contains(st)) {
|
|
||||||
model.filter.status.remove(st)
|
|
||||||
evt.source.selected = false
|
|
||||||
} else {
|
|
||||||
model.filter.status.add(st)
|
|
||||||
evt.source.selected = true
|
|
||||||
}
|
|
||||||
controller.refreshIssues()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
separator()
|
|
||||||
|
|
||||||
menuItem('Detail Text Size...',
|
|
||||||
actionPerformed: {
|
|
||||||
def newSize = JOptionPane.showInputDialog(frame,
|
|
||||||
'New text size: ', 'Change Issue Detail Text Size...',
|
|
||||||
JOptionPane.QUESTION_MESSAGE)
|
|
||||||
if (newSize == null || !newSize.isFloat())
|
|
||||||
JOptionPane.showMessageDialog(frame,
|
|
||||||
'$newSize is not a valid size.',
|
|
||||||
'Change Issue Detail Size...',
|
|
||||||
JOptionPane.ERROR_MESSAGE)
|
|
||||||
else model.issueDetailFont = model.issueDetailFont
|
|
||||||
.deriveFont(newSize.toFloat())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
menu("Sort") {
|
|
||||||
sortMenuButtonGroup = buttonGroup()
|
|
||||||
checkBoxMenuItem('By ID',
|
|
||||||
buttonGroup: sortMenuButtonGroup,
|
|
||||||
actionPerformed: {
|
|
||||||
model.filter.issueSorter = { it.id }
|
|
||||||
controller.refreshIssues()
|
|
||||||
})
|
|
||||||
checkBoxMenuItem('By Category',
|
|
||||||
buttonGroup: sortMenuButtonGroup,
|
|
||||||
actionPerformed: {
|
|
||||||
model.filter.issueSorter = { it.category }
|
|
||||||
controller.refreshIssues()
|
|
||||||
})
|
|
||||||
checkBoxMenuItem('By Status',
|
|
||||||
buttonGroup: sortMenuButtonGroup,
|
|
||||||
actionPerformed: {
|
|
||||||
model.filter.issueSorter = { it.status }
|
|
||||||
controller.refreshIssues()
|
|
||||||
})
|
|
||||||
checkBoxMenuItem('By Priority',
|
|
||||||
buttonGroup: sortMenuButtonGroup,
|
|
||||||
actionPerformed: {
|
|
||||||
model.filter.issueSorter = { it.priority }
|
|
||||||
controller.refreshIssues()
|
|
||||||
})
|
|
||||||
checkBoxMenuItem('By Title',
|
|
||||||
buttonGroup: sortMenuButtonGroup,
|
|
||||||
actionPerformed: {
|
|
||||||
model.filter.issueSorter = { it.title }
|
|
||||||
controller.refreshIssues()
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mainTabbedPane = tabbedPane() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,298 +0,0 @@
|
|||||||
package com.jdbernard.pit.swing
|
|
||||||
|
|
||||||
import com.jdbernard.pit.Category
|
|
||||||
import com.jdbernard.pit.Status
|
|
||||||
import com.jdbernard.pit.Project
|
|
||||||
import com.jdbernard.pit.FlatProjectView
|
|
||||||
import java.awt.Font
|
|
||||||
import java.awt.GridBagConstraints as GBC
|
|
||||||
import java.awt.Point
|
|
||||||
import java.awt.event.MouseEvent
|
|
||||||
import javax.swing.JOptionPane
|
|
||||||
import javax.swing.JSplitPane
|
|
||||||
import javax.swing.JLabel
|
|
||||||
import javax.swing.JScrollPane
|
|
||||||
import javax.swing.JTable
|
|
||||||
import javax.swing.JTextField
|
|
||||||
import javax.swing.ListSelectionModel
|
|
||||||
import javax.swing.table.TableCellRenderer
|
|
||||||
import javax.swing.tree.DefaultMutableTreeNode
|
|
||||||
import javax.swing.tree.DefaultTreeCellRenderer
|
|
||||||
import javax.swing.tree.DefaultTreeModel
|
|
||||||
import javax.swing.tree.TreeSelectionModel
|
|
||||||
|
|
||||||
actions {
|
|
||||||
action (
|
|
||||||
id: 'newIssue',
|
|
||||||
name: 'New Issue',
|
|
||||||
icon: imageIcon("/add.png"),
|
|
||||||
accelerator: shortcut('N'),
|
|
||||||
closure: controller.newIssue,
|
|
||||||
enabled: bind { model.selectedProject != null }
|
|
||||||
)
|
|
||||||
|
|
||||||
action (
|
|
||||||
id: 'newProject',
|
|
||||||
name: 'New Project...',
|
|
||||||
icon: imageIcon("/add.png"),
|
|
||||||
closure: controller.newProject
|
|
||||||
)
|
|
||||||
|
|
||||||
action (
|
|
||||||
id: 'deleteProject',
|
|
||||||
name: 'Delete Project',
|
|
||||||
closure: controller.deleteProject,
|
|
||||||
enabled: bind { model.selectedProject != null }
|
|
||||||
)
|
|
||||||
|
|
||||||
action (
|
|
||||||
id: 'deleteProjectPop',
|
|
||||||
name: 'Delete Project',
|
|
||||||
icon: imageIcon("/delete.png"),
|
|
||||||
closure: controller.deleteProject,
|
|
||||||
enabled: bind { model.popupProject != null }
|
|
||||||
)
|
|
||||||
|
|
||||||
action (
|
|
||||||
id: 'deleteIssue',
|
|
||||||
name: 'Delete Issue',
|
|
||||||
icon: imageIcon("/delete.png"),
|
|
||||||
closure: controller.deleteIssue,
|
|
||||||
)
|
|
||||||
|
|
||||||
action (
|
|
||||||
id: 'deleteIssuePop',
|
|
||||||
name: 'Delete Issue',
|
|
||||||
icon: imageIcon("/delete.png"),
|
|
||||||
closure: controller.deleteIssue,
|
|
||||||
enabled: bind { model.popupIssue != null }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// popup menu for projects
|
|
||||||
projectPopupMenu = popupMenu() {
|
|
||||||
menuItem(newProject)
|
|
||||||
menuItem(deleteProjectPop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// popup menu for issues
|
|
||||||
issuePopupMenu = popupMenu() {
|
|
||||||
menuItem(newIssue)
|
|
||||||
menuItem(deleteIssuePop)
|
|
||||||
separator()
|
|
||||||
|
|
||||||
menu('Change Category', enabled: bind { model.popupIssue != null }) {
|
|
||||||
Category.values().each { category ->
|
|
||||||
menuItem(category.toString(),
|
|
||||||
icon: model.mainMVC.model.categoryIcons[(category)],
|
|
||||||
enabled: bind { model.popupIssue != null },
|
|
||||||
actionPerformed: {
|
|
||||||
try {
|
|
||||||
model.popupIssue.category = category
|
|
||||||
controller.refreshIssues()
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
JOptionPane.showMessage(mainMVC.view.frame,
|
|
||||||
ioe.getLocalizedMessage(), "Change Category",
|
|
||||||
JOptionPane.ERROR_MESSAGE)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
menu('Change Status', enabled: bind { model.popupIssue != null}) {
|
|
||||||
Status.values().each { status ->
|
|
||||||
menuItem(status.toString(),
|
|
||||||
icon: model.mainMVC.model.statusIcons[(status)],
|
|
||||||
enabled: bind { model.popupIssue != null },
|
|
||||||
actionPerformed: {
|
|
||||||
try {
|
|
||||||
model.popupIssue.status = status
|
|
||||||
controller.refreshIssues()
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
JOptionPane.showMessage(model.mainMVC.view.frame,
|
|
||||||
ioe.getLocalizedMessage(), 'Change Status',
|
|
||||||
JOptionPane.ERROR_MESSAGE)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
menuItem('Change Priority...',
|
|
||||||
enabled: bind { model.popupIssue != null },
|
|
||||||
actionPerformed: {
|
|
||||||
def newPriority = JOptionPane.showInputDialog(mainMVC.view.frame,
|
|
||||||
'New priority (0-9)', 'Change Priority...',
|
|
||||||
JOptionPane.QUESTION_MESSAGE)
|
|
||||||
try { model.popupIssue.priority = newPriority.toInteger() }
|
|
||||||
catch (NumberFormatException nfe) {
|
|
||||||
JOptionPane.showMessageDialog(mainMVC.view.frame,
|
|
||||||
'The priority value must be an integer in [0-9].',
|
|
||||||
'Change Priority...', JOptionPane.ERROR_MESSAGE)
|
|
||||||
return
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
JOptionPane.showMessageDialog(model.mainMVC.view.frame,
|
|
||||||
ioe.getLocalizedMessage(), 'Change Priority...',
|
|
||||||
JOptionPane.ERROR_MESSAGE)
|
|
||||||
}
|
|
||||||
controller.refreshIssues()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// main split view
|
|
||||||
panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
|
|
||||||
dividerLocation: 200,
|
|
||||||
oneTouchExpandable: true,
|
|
||||||
constraints: gbc(fill: GBC.BOTH, insets: [10, 10, 10, 10],
|
|
||||||
weightx: 2, weighty: 2)) {
|
|
||||||
|
|
||||||
// left side (project tree and buttons
|
|
||||||
panel(constraints: 'left') {
|
|
||||||
gridBagLayout()
|
|
||||||
|
|
||||||
// tree view of projects
|
|
||||||
scrollPane(constraints: gbc(fill: GBC.BOTH, gridx: 0, gridy: 0,
|
|
||||||
gridwidth: 2, weightx: 2, weighty: 2)) {
|
|
||||||
treeCellRenderer = new DefaultTreeCellRenderer()
|
|
||||||
treeCellRenderer.leafIcon = treeCellRenderer.closedIcon
|
|
||||||
|
|
||||||
projectTree = tree(cellRenderer: treeCellRenderer,
|
|
||||||
model: bind(source: model, sourceProperty: 'rootProject',
|
|
||||||
sourceValue: {
|
|
||||||
if (model.rootProject) {
|
|
||||||
def rootNode = new DefaultMutableTreeNode()
|
|
||||||
def flatview = new FlatProjectView('All Issues')
|
|
||||||
flatview.projects[(model.rootProject.name)] =
|
|
||||||
model.rootProject
|
|
||||||
rootNode.add(new DefaultMutableTreeNode(flatview))
|
|
||||||
rootNode.add(controller.makeNodes(model.rootProject))
|
|
||||||
new DefaultTreeModel(rootNode)
|
|
||||||
} else {
|
|
||||||
new DefaultTreeModel(new DefaultMutableTreeNode())
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
valueChanged: { evt ->
|
|
||||||
model.selectedProject = evt?.newLeadSelectionPath?.
|
|
||||||
lastPathComponent?.userObject ?: model.rootProject
|
|
||||||
controller.displayProject(model.selectedProject)
|
|
||||||
},
|
|
||||||
mouseClicked: { evt ->
|
|
||||||
if (evt.button == MouseEvent.BUTTON3) {
|
|
||||||
controller.showProjectPopup(
|
|
||||||
projectTree.getPathForLocation(evt.x, evt.y)?.
|
|
||||||
lastPathComponent?.userObject,
|
|
||||||
evt.x, evt.y)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
projectTree.rootVisible = false
|
|
||||||
|
|
||||||
projectTree.selectionModel.selectionMode =
|
|
||||||
TreeSelectionModel.SINGLE_TREE_SELECTION
|
|
||||||
}
|
|
||||||
|
|
||||||
newProjectButton = button(newProject,
|
|
||||||
constraints: gbc(fill: GBC.NONE, gridx: 0, gridy: 1,
|
|
||||||
anchor: GBC.WEST))
|
|
||||||
deleteProjectButton = button(deleteProject,
|
|
||||||
constraints: gbc(fill: GBC.NONE, gridx: 1, gridy: 1,
|
|
||||||
anchor: GBC.WEST))
|
|
||||||
}
|
|
||||||
|
|
||||||
// split between issues list and issue details
|
|
||||||
splitPane(orientation: JSplitPane.VERTICAL_SPLIT,
|
|
||||||
dividerLocation: 200, constraints: "right") {
|
|
||||||
|
|
||||||
panel(constraints: "top") {
|
|
||||||
gridBagLayout()
|
|
||||||
|
|
||||||
scrollPane(constraints: gbc(fill: GBC.BOTH, weightx: 2,
|
|
||||||
weighty: 2, gridx: 0, gridy: 0, gridwidth: 3)) {
|
|
||||||
|
|
||||||
issueTable = table(
|
|
||||||
autoCreateRowSorter: true,
|
|
||||||
autoResizeMode: JTable.AUTO_RESIZE_LAST_COLUMN,
|
|
||||||
cellSelectionEnabled: false,
|
|
||||||
columnSelectionAllowed: false,
|
|
||||||
dragEnabled: false,
|
|
||||||
rowSelectionAllowed: true,
|
|
||||||
showHorizontalLines: false,
|
|
||||||
showVerticalLines: false,
|
|
||||||
mouseClicked: { evt ->
|
|
||||||
if (evt.button == MouseEvent.BUTTON3) {
|
|
||||||
def translatedPoint = evt.locationOnScreen.clone()
|
|
||||||
translatedPoint.translate(-issueTable.locationOnScreen.@x,
|
|
||||||
-issueTable.locationOnScreen.@y)
|
|
||||||
def row = issueTable.rowAtPoint(translatedPoint)
|
|
||||||
|
|
||||||
issueTable.setRowSelectionInterval(row, row)
|
|
||||||
|
|
||||||
controller.showIssuePopup(
|
|
||||||
controller.getSelectedIssue(), evt.x, evt.y)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
issueTable.setDefaultRenderer(Object.class,
|
|
||||||
model.issueCellRenderer)
|
|
||||||
issueTable.selectionModel.valueChanged = { evt ->
|
|
||||||
if (evt.valueIsAdjusting) return
|
|
||||||
controller.displayIssue(controller.getSelectedIssue())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
wordWrapCheckBox = checkBox('Word wrap',
|
|
||||||
constraints: gbc(gridx: 0, gridy: 1, weightx: 2,
|
|
||||||
anchor: GBC.WEST), selected: true)
|
|
||||||
button(newIssue,
|
|
||||||
constraints: gbc(gridx: 1, gridy: 1, anchor: GBC.EAST))
|
|
||||||
|
|
||||||
deleteIssueButton = button(deleteIssue,
|
|
||||||
constraints: gbc(gridx: 2, gridy: 1, anchor: GBC.EAST),
|
|
||||||
enabled: bind(source: issueTable.selectionModel,
|
|
||||||
sourceEvent: 'valueChanged',
|
|
||||||
sourceValue: { !issueTable.selectionModel.isSelectionEmpty() }))
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollPane(constraints: "bottom",
|
|
||||||
horizontalScrollBarPolicy: JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) {
|
|
||||||
issueTextPanel = panel {
|
|
||||||
issueTextPanelLayout = cardLayout()
|
|
||||||
|
|
||||||
def leavingEditorClosure = {
|
|
||||||
def issue = controller.getSelectedIssue()
|
|
||||||
if (issue == null) return
|
|
||||||
if (issueTextArea.text != issue.text) {
|
|
||||||
issue.text = issueTextArea.text
|
|
||||||
issueTextDisplay.text = controller.rst2html(
|
|
||||||
issueTextArea.text)
|
|
||||||
}
|
|
||||||
issueTextPanelLayout.show(issueTextPanel, 'display')
|
|
||||||
}
|
|
||||||
|
|
||||||
issueTextArea = textArea(
|
|
||||||
constraints: 'editor',
|
|
||||||
wrapStyleWord: true,
|
|
||||||
lineWrap: bind(source: wordWrapCheckBox,
|
|
||||||
sourceProperty: 'selected'),
|
|
||||||
editable: bind(source: issueTable.selectionModel,
|
|
||||||
sourceEvent: 'valueChanged',
|
|
||||||
sourceValue:
|
|
||||||
{ !issueTable.selectionModel.isSelectionEmpty() }),
|
|
||||||
font: model.mainMVC.model.issueDetailFont,
|
|
||||||
focusGained: {},
|
|
||||||
focusLost: leavingEditorClosure,
|
|
||||||
mouseExited: leavingEditorClosure)
|
|
||||||
|
|
||||||
issueTextDisplay = editorPane(contentType: 'text/html',
|
|
||||||
constraints: 'display',
|
|
||||||
editable: false,
|
|
||||||
preferredSize: [10, 10],
|
|
||||||
mouseClicked: {evt ->
|
|
||||||
if (evt.clickCount > 1)
|
|
||||||
issueTextPanelLayout.show(issueTextPanel, 'editor')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
issueTextPanelLayout.show(issueTextPanel, "display")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,141 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
## ##
|
|
||||||
## Griffon wrapper script for UN*X ##
|
|
||||||
## ##
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
# Uncomment those lines to set JVM options. GRIFFON_OPTS and JAVA_OPTS can be used together.
|
|
||||||
# GRIFFON_OPTS="$GRIFFON_OPTS -Xmx512"
|
|
||||||
# JAVA_OPTS="$JAVA_OPTS -Xmx512"
|
|
||||||
|
|
||||||
GRIFFON_APP_NAME=Griffon
|
|
||||||
|
|
||||||
warn ( ) {
|
|
||||||
echo "${PROGNAME}: $*"
|
|
||||||
}
|
|
||||||
|
|
||||||
die ( ) {
|
|
||||||
warn "$*"
|
|
||||||
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
|
|
||||||
|
|
||||||
# Attempt to set JAVA_HOME if it's not already set.
|
|
||||||
if [ -z "$JAVA_HOME" ] ; then
|
|
||||||
if $darwin ; then
|
|
||||||
[ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] && export JAVA_HOME="/Library/Java/Home"
|
|
||||||
[ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
|
|
||||||
else
|
|
||||||
javaExecutable="`which javac`"
|
|
||||||
[ -z "$javaExecutable" -o "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME."
|
|
||||||
# readlink(1) is not available as standard on Solaris 10.
|
|
||||||
readLink=`which readlink`
|
|
||||||
[ `expr "$readLink" : '\([^ ]*\)'` = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME."
|
|
||||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
|
||||||
javaHome="`dirname \"$javaExecutable\"`"
|
|
||||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
|
||||||
export JAVA_HOME="$javaHome"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
|
||||||
if $cygwin ; then
|
|
||||||
[ -n "$JAVACMD" ] && JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
|
||||||
fi
|
|
||||||
|
|
||||||
STARTER_MAIN_CLASS=org.gradle.wrapper.GriffonWrapperMain
|
|
||||||
CLASSPATH=`dirname "$0"`/wrapper/griffon-wrapper.jar
|
|
||||||
WRAPPER_PROPERTIES=`dirname "$0"`/wrapper/griffon-wrapper.properties
|
|
||||||
# Determine the Java command to use to start the JVM.
|
|
||||||
if [ -z "$JAVACMD" ] ; then
|
|
||||||
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
|
|
||||||
else
|
|
||||||
JAVACMD="java"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
|
||||||
die "JAVA_HOME is not defined correctly, can not execute: $JAVACMD"
|
|
||||||
fi
|
|
||||||
if [ -z "$JAVA_HOME" ] ; then
|
|
||||||
warn "JAVA_HOME environment variable is not set"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add GRIFFON_APP_NAME to the JAVA_OPTS as -Xdock:name
|
|
||||||
if $darwin; then
|
|
||||||
JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GRIFFON_APP_NAME"
|
|
||||||
# we may also want to set -Xdock:image
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
|
||||||
if $cygwin ; then
|
|
||||||
JAVA_HOME=`cygpath --path --mixed "$JAVA_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
|
|
||||||
|
|
||||||
"$JAVACMD" $JAVA_OPTS $GRIFFON_OPTS \
|
|
||||||
-classpath "$CLASSPATH" \
|
|
||||||
-Dorg.gradle.wrapper.properties="$WRAPPER_PROPERTIES" \
|
|
||||||
$STARTER_MAIN_CLASS \
|
|
||||||
"$@"
|
|
@ -1,126 +0,0 @@
|
|||||||
@if "%DEBUG%" == "" @echo off
|
|
||||||
@rem ##########################################################################
|
|
||||||
@rem ##
|
|
||||||
@rem Griffon startup script for Windows ##
|
|
||||||
@rem ##
|
|
||||||
@rem ##########################################################################
|
|
||||||
|
|
||||||
@rem
|
|
||||||
@rem $Revision: 10602 $ $Date: 2008-01-25 02:49:54 +0100 (ven., 25 janv. 2008) $
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
|
||||||
|
|
||||||
@rem Uncomment those lines to set JVM options. GRIFFON_OPTS and JAVA_OPTS can be used together.
|
|
||||||
@rem set GRIFFON_OPTS=%GRIFFON_OPTS% -Xmx512
|
|
||||||
@rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512
|
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.\
|
|
||||||
|
|
||||||
@rem Determine the command interpreter to execute the "CD" later
|
|
||||||
set COMMAND_COM="cmd.exe"
|
|
||||||
if exist "%SystemRoot%\system32\cmd.exe" set COMMAND_COM="%SystemRoot%\system32\cmd.exe"
|
|
||||||
if exist "%SystemRoot%\command.com" set COMMAND_COM="%SystemRoot%\command.com"
|
|
||||||
|
|
||||||
@rem Use explicit find.exe to prevent cygwin and others find.exe from being used
|
|
||||||
set FIND_EXE="find.exe"
|
|
||||||
if exist "%SystemRoot%\system32\find.exe" set FIND_EXE="%SystemRoot%\system32\find.exe"
|
|
||||||
if exist "%SystemRoot%\command\find.exe" set FIND_EXE="%SystemRoot%\command\find.exe"
|
|
||||||
|
|
||||||
:check_JAVA_HOME
|
|
||||||
@rem Make sure we have a valid JAVA_HOME
|
|
||||||
if not "%JAVA_HOME%" == "" goto have_JAVA_HOME
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: Environment variable JAVA_HOME has not been set.
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
echo.
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:have_JAVA_HOME
|
|
||||||
@rem Validate JAVA_HOME
|
|
||||||
%COMMAND_COM% /C DIR "%JAVA_HOME%" 2>&1 | %FIND_EXE% /I /C "%JAVA_HOME%" >nul
|
|
||||||
if not errorlevel 1 goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME might be 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 if there are problems.
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:init
|
|
||||||
@rem get name of script to launch with full path
|
|
||||||
@rem Get command-line arguments, handling Windowz variants
|
|
||||||
SET _marker=%JAVA_HOME: =%
|
|
||||||
@rem IF NOT "%_marker%" == "%JAVA_HOME%" ECHO JAVA_HOME "%JAVA_HOME%" contains spaces. Please change to a location without spaces if this causes problems.
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
if "%eval[2+2]" == "4" goto 4NT_args
|
|
||||||
|
|
||||||
IF "%_marker%" == "%JAVA_HOME%" goto :win9xME_args
|
|
||||||
|
|
||||||
set _FIXPATH=
|
|
||||||
call :fixpath "%JAVA_HOME%"
|
|
||||||
set JAVA_HOME=%_FIXPATH:~1%
|
|
||||||
|
|
||||||
goto win9xME_args
|
|
||||||
|
|
||||||
:fixpath
|
|
||||||
if not %1.==. (
|
|
||||||
for /f "tokens=1* delims=;" %%a in (%1) do (
|
|
||||||
call :shortfilename "%%a" & call :fixpath "%%b"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
goto :EOF
|
|
||||||
:shortfilename
|
|
||||||
for %%i in (%1) do set _FIXPATH=%_FIXPATH%;%%~fsi
|
|
||||||
goto :EOF
|
|
||||||
|
|
||||||
|
|
||||||
: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 STARTER_MAIN_CLASS=org.gradle.wrapper.GriffonWrapperMain
|
|
||||||
set CLASSPATH=%DIRNAME%\wrapper\griffon-wrapper.jar
|
|
||||||
set WRAPPER_PROPERTIES=%DIRNAME%\wrapper\griffon-wrapper.properties
|
|
||||||
set JAVA_EXE=%JAVA_HOME%\bin\java.exe
|
|
||||||
|
|
||||||
set GRIFFON_OPTS=%JAVA_OPTS% %GRIFFON_OPTS% -Dorg.gradle.wrapper.properties="%WRAPPER_PROPERTIES%"
|
|
||||||
|
|
||||||
"%JAVA_EXE%" %GRIFFON_OPTS% -classpath "%CLASSPATH%" %STARTER_MAIN_CLASS% %CMD_LINE_ARGS%
|
|
||||||
|
|
||||||
:end
|
|
||||||
@rem End local scope for the variables with windows NT shell
|
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
|
||||||
|
|
||||||
if not "%OS%"=="Windows_NT" echo 1 > nul | choice /n /c:1
|
|
||||||
|
|
||||||
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 "%ERRORLEVEL%"
|
|
||||||
exit /b "%ERRORLEVEL%"
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|