Serious bug-fixing, yo.

This commit is contained in:
Jonathan Bernard
2010-08-27 03:07:54 -05:00
parent 4d77789e78
commit 63d47d8d9c
46 changed files with 703 additions and 2205 deletions

View File

@ -1,22 +1,33 @@
application {
title="PitSwing"
startupGroups=["PIT"]
autoShutdown=true
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 {
NewIssueDialog {
model="com.jdbernard.pit.swing.NewIssueDialogModel"
controller="com.jdbernard.pit.swing.NewIssueDialogController"
view="com.jdbernard.pit.swing.NewIssueDialogView"
}
ProjectPanel {
model="com.jdbernard.pit.swing.ProjectPanelModel"
view="com.jdbernard.pit.swing.ProjectPanelView"
controller="com.jdbernard.pit.swing.ProjectPanelController"
}
PIT {
model="com.jdbernard.pit.swing.PITModel"
view="com.jdbernard.pit.swing.PITView"
controller="com.jdbernard.pit.swing.PITController"
}
// 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'
}
}

View File

@ -0,0 +1,135 @@
// 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)"
}
}

View File

@ -17,78 +17,3 @@ log4j {
}
additivity.StackTrace=false
}
// 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 = []
}
webstart {
codebase = "${new File(griffon.jars.destDir).toURI().toASCIIString()}"
jnlp = 'application.jnlp'
}
applet {
jnlp = 'applet.jnlp'
html = 'applet.html'
}
}

View File

@ -12,7 +12,7 @@
codebase:'@griffonAppCodebase@',
code:'@griffonAppletClass@',
archive:'@appletJars@',
width:'240', height:'320'} ;
width:'@applet.width@', height:'@applet.height@'} ;
var parameters = {fontSize:16,
java_arguments: "-Djnlp.packEnabled=true",
jnlp_href:'@griffonAppCodebase@/applet.jnlp',
@ -20,7 +20,7 @@
image:'griffon.png',
boxmessage:'Loading @griffonAppName@',
boxbgcolor:'#FFFFFF', boxfgcolor:'#000000',
codebase_lookup: 'false'} ;
codebase_lookup: 'false'@applet.script.params@} ;
var version = '1.5.0' ;
deployJava.runApplet(attributes, parameters, version);
</script>
@ -28,7 +28,7 @@
<APPLET CODEBASE='@griffonAppCodebase@'
CODE='@griffonAppletClass@'
ARCHIVE='@appletJars@'
WIDTH='240' HEIGHT='320'>
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'>
@ -37,6 +37,7 @@
<PARAM NAME='boxbgcolor' VALUE='#FFFFFF'>
<PARAM NAME='boxfgcolor' VALUE='#000000'>
<PARAM NAME='codebase_lookup' VALUE='false'>
@applet.tag.params@
</APPLET>
-->
</body>

View File

@ -35,21 +35,25 @@
<!--<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="320"
height="640">
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>

View File

@ -35,16 +35,20 @@
<!--<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>

View File

@ -13,6 +13,8 @@ class PITController {
void mvcGroupInit(Map args) {
model.newIssueDialogMVC = buildMVCGroup('NewIssueDialog')
SwingUtilities.invokeAndWait {
model.issueListRenderer = new IssueTableCellRenderer()
@ -27,27 +29,27 @@ class PITController {
// look for general config options
pitrcFile = new File(pitHome, 'pitrc')
if(logDbg) logger.debug("$pitrcFile is " +
if (logDbg) logger.debug("$pitrcFile is " +
(pitrcFile.exists() ? '' : 'not ') + "present.")
// load general config (if present)
if (pitrcFile.exists()) {
pitrcFile.withInputStream() { config.load(it) }
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.")
if (logDbg) logger.debug("$pitswingrcFile is " +
(pitswingrcFile.exists() ? '' : 'not ') + "present.")
// load swing specific config (if present)
if (pitswingrcFile.exists()) {
pitswingrcFile.withInputStream() { config.load(it) }
if(logDbg) logger.debug("Loaded pitswingrc")
if (pitswingrcFile.exists() && pitswingrcFile.canRead()) {
pitswingrcFile.withInputStream { config.load(it) }
if (logDbg) logger.debug("Loaded pitswingrc")
}
// Process Configurable Options
// Process configurable options
// ----------------------------
if (logDbg) {
@ -59,15 +61,16 @@ class PITController {
Category.values().each { category ->
def expectedKey = "issue." + category.name().toLowerCase() +
".template"
if(logDbg) logger.debug("Looking for key: $expectedKey")
if (logDbg) logger.debug("Looking for key: $expectedKey")
config.keySet().each { currentKey ->
if (currentKey == expectedKey)
model.templates[(category)] =
config.getProperty(expectedKey, "")
if (currentKey == expectedKey)
model.templates[(category)] =
config.getProperty(expectedKey, "")
if (logDbg) logger.debug("Template for category $category: '" +
model.templates[(category)] + "'")
}
}
// load custom issueListRenderer
@ -76,12 +79,12 @@ class PITController {
// load initial repositories
if (config.containsKey('initial-repositories')) {
def initRepos = config.getProperty('initial-repositories', '')
initRepos = initRepos.split(/[;:,]/)
initRepos = initRepos.split(/[:;,]/)
initRepos.each { repoPath -> loadProject(new File(repoPath)) }
if(logDbg) logger.debug("Init repos: '$initRepos'")
if (logDbg) logger.debug("Init repos: '$initRepos'")
}
// load custom issue CSS
// load custom issue css
if (config.containsKey('issue.display.css')) {
def issueCSS = config.getProperty('issue.display.css', "")
@ -89,20 +92,16 @@ class PITController {
def cssFile
// use short-circuit logic to test several possible locations
// for a css file
if ((cssFile = new File(pitHome, issueCSS)).exists() ||
(cssFile = new File(pitHome.parentFile(), issueCSS)).exists() ||
(cssFile = new File(issueCSS).exists()))
(cssFile = new File(issueCSS)).exists())
issueCSS = cssFile.text
if (logDbg) logger.debug("CS for issue display: $issueCSS")
if (logDbg) logger.debug("CSS for issue display: $issueCSS")
model.issueCSS = issueCSS
}
}
//
model.newIssueDialogMVC = buildMVCGroup('NewIssueDialog')
}
void refreshIssues() {
@ -111,12 +110,11 @@ class PITController {
}
}
def openProject = { evt = null ->
def openProject = { evt = null ->
if (view.openDialog.showOpenDialog(view.frame) !=
JFileChooser.APPROVE_OPTION) return
JFileChooser.APPROVE_OPTIONS) return
loadProject(view.openDialog.selectedFile)
}
def loadProject = { File projectDir ->
@ -124,7 +122,7 @@ class PITController {
// 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;
if (!projectDir.exists() || !projectDir.isDirectory()) return
// create new ProjectPanel MVC
newMVC = buildMVCGroup('ProjectPanel',
@ -134,28 +132,30 @@ class PITController {
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)])
if (!model.projectIdMap[(newMVC.model.id)])
model.projectIdMap[(newMVC.model.id)] = 0
// no? increment
else model.projectIdMap[(newMVC.model.id)] =
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)]
newMVC.model.id += "-" + model.projectIdMap[(newMVC.model.id)]
}
}
model.projectPanelMVCs[newMVC.model.id] = newMVC
model.projectPanelMVCs[(newMVC.model.id)] = newMVC
view.mainTabbedPane.addTab(newMVC.model.id, newMVC.view.panel)
}

View File

@ -34,17 +34,18 @@ class ProjectPanelController {
refreshProject()
}
/**
/**
* displayProject
* @param project Project to display.
*
* @param project Project to display
*/
void displayProject(Project project) {
view.issueTextArea.text = ""
view.issueTextDisplay.text = ""
view.issueTextPanelLayout.show(view.issueTextPanel, "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)
@ -67,7 +68,7 @@ class ProjectPanelController {
if (!issue) return
// hack because binding view.issueTextArea.font to
// mainMVC.mode.issueDetailFont causes problems
// mainMVC.model.issueDetailFont causes problems
if (view.issueTextArea.font != model.mainMVC.model.issueDetailFont)
view.issueTextArea.font = model.mainMVC.model.issueDetailFont
@ -75,10 +76,10 @@ class ProjectPanelController {
view.issueTextArea.caretPosition = 0
view.issueTextDisplay.text = rst2html(issue.text)
view.issueTextDisplay.caretPosition = 0
view.issueTextPanelLayout.show(view.issueTextPanel, "display")
view.issueTextPanelLayout.show(view.issueTextPanel, 'display')
}
void showProjectPopup(Project project, def x, def y) {
void showProejctPopup(Project project, def x, def y) {
model.popupProject = project
view.projectPopupMenu.show(view.projectTree, x, y)
}
@ -120,7 +121,7 @@ class ProjectPanelController {
def project
if (evt.source == view.newProjectButton)
if (evt.source == view.newProjectButton)
project = model.selectedProject ?: model.rootProject
else project = model.popupProject ?: model.rootProject
def newProject = project.createNewProject(name)
@ -134,7 +135,7 @@ class ProjectPanelController {
if (evt.source == view.deleteProjectButton)
project = model.selectedProject ?: model.rootProject
else project = model.popupProject ?: model.rootModel
else project = model.popupProject ?: model.rootProject
project.delete()
@ -185,7 +186,7 @@ class ProjectPanelController {
}
String rst2html(String rst) {
Document doc // memory model of document
Document doc
StringWriter outString
StringBuilder result = new StringBuilder()
@ -201,19 +202,18 @@ class ProjectPanelController {
// 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 elements
// 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
// 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">' +
if (line =~ /<head>/) result.append('<style type="text/css">' +
model.issueCSS + '</style>')
}

View File

@ -1,25 +1,20 @@
/*
* This script is executed inside the EDT, so be sure to
* call long running code in another thread.
* This script is executed inside the UI thread, so be sure to call
* long running code in another thread.
*
* You have the following options
* - SwingBuilder.doOutside { // your code }
* - execOutside { // your code }
* - execFuture { // your code }
* - Thread.start { // your code }
* - SwingXBuilder.withWorker( start: true ) {
* onInit { // initialization (optional, runs in current thread) }
* work { // your code }
* onDone { // finish (runs inside EDT) }
* }
*
* You have the following options to run code again inside EDT
* - SwingBuilder.doLater { // your code }
* - SwingBuilder.edt { // your code }
* - SwingUtilities.invokeLater { // your code }
* 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 griffon.util.GriffonApplicationHelper
import static griffon.util.GriffonApplicationUtils.*
GriffonPlatformHelper.tweakForNativePlatform(app)
SwingBuilder.lookAndFeel('org.pushingpixels.substance.api.skin.SubstanceCremeCoffeeLookAndFeel', 'nimbus', ['metal', [boldFonts: false]])

View File

@ -1,18 +1,13 @@
/*
* This script is executed inside the EDT, so be sure to
* call long running code in another thread.
* This script is executed inside the UI thread, so be sure to call
* long running code in another thread.
*
* You have the following options
* - SwingBuilder.doOutside { // your code }
* - execOutside { // your code }
* - execFuture { // your code }
* - Thread.start { // your code }
* - SwingXBuilder.withWorker( start: true ) {
* onInit { // initialization (optional, runs in current thread) }
* work { // your code }
* onDone { // finish (runs inside EDT) }
* }
*
* You have the following options to run code again inside EDT
* - SwingBuilder.doLater { // your code }
* - SwingBuilder.edt { // your code }
* - SwingUtilities.invokeLater { // your code }
* You have the following options to run code again inside the UI thread
* - execAsync { // your code }
* - execSync { // your code }
*/

View File

@ -1,18 +1,13 @@
/*
* This script is executed inside the EDT, so be sure to
* call long running code in another thread.
* This script is executed inside the UI thread, so be sure to call
* long running code in another thread.
*
* You have the following options
* - SwingBuilder.doOutside { // your code }
* - execOutside { // your code }
* - execFuture { // your code }
* - Thread.start { // your code }
* - SwingXBuilder.withWorker( start: true ) {
* onInit { // initialization (optional, runs in current thread) }
* work { // your code }
* onDone { // finish (runs inside EDT) }
* }
*
* You have the following options to run code again inside EDT
* - SwingBuilder.doLater { // your code }
* - SwingBuilder.edt { // your code }
* - SwingUtilities.invokeLater { // your code }
*/
* You have the following options to run code again inside the UI thread
* - execAsync { // your code }
* - execSync { // your code }
*/

View File

@ -1,18 +1,13 @@
/*
* This script is executed inside the EDT, so be sure to
* call long running code in another thread.
* This script is executed inside the UI thread, so be sure to call
* long running code in another thread.
*
* You have the following options
* - SwingBuilder.doOutside { // your code }
* - execOutside { // your code }
* - execFuture { // your code }
* - Thread.start { // your code }
* - SwingXBuilder.withWorker( start: true ) {
* onInit { // initialization (optional, runs in current thread) }
* work { // your code }
* onDone { // finish (runs inside EDT) }
* }
*
* You have the following options to run code again inside EDT
* - SwingBuilder.doLater { // your code }
* - SwingBuilder.edt { // your code }
* - SwingUtilities.invokeLater { // your code }
*/
* You have the following options to run code again inside the UI thread
* - execAsync { // your code }
* - execSync { // your code }
*/

View File

@ -0,0 +1,13 @@
/*
* 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 }
*/

View File

@ -5,7 +5,6 @@ import com.jdbernard.pit.Status
import groovy.beans.Bindable
class NewIssueDialogModel {
@Bindable boolean accept
String text
Category category

View File

@ -7,26 +7,28 @@ 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 issues
// filter for projects and classes
Filter filter = new Filter(categories: [],
status: [Status.NEW, Status.VALIDATION_REQUIRED])
def issueListRenderer
// map of category -> issue template
def templates = [:]
Map<Category, String> templates = [:]
def issueCSS = getClass().getResource("/default-issue.css").openStream().text
def categoryIcons = [:]
def statusIcons = [:]
String issueCSS = getClass().getResourceAsStream("/default-issue.css").text
Map<Category, ImageIcon> categoryIcons = [:]
Map<Category, ImageIcon> statusIcons = [:]
def newIssueDialogMVC
def projectPanelMVCs = [:]
Map projectPanelMVCs = [:]
def projectIdMap = [:]
Map projectIdMap = [:]
@Bindable issueDetailFont = new Font(Font.MONOSPACED, Font.PLAIN, 10)
@Bindable Font issueDetailFont = new Font(Font.MONOSPACED, Font.PLAIN, 10)
}

View File

@ -20,7 +20,7 @@ class ProjectPanelModel {
String issueCSS = ""
// cache the ListModels
// cache the models
def projectTableModels = [:]
def issueCellRenderer

View File

@ -61,4 +61,5 @@ dialog = dialog(title: 'New Task...', modal: true, pack: true,
},
constraints: gbc(gridx: 1, gridy: 5, insets: [5, 5, 5, 5],
anchor: GBC.WEST))
}

View File

@ -8,6 +8,7 @@ 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
@ -16,7 +17,6 @@ import javax.swing.JFileChooser
import javax.swing.JOptionPane
import net.miginfocom.swing.MigLayout
import java.awt.Color
actions {
action(
@ -53,20 +53,17 @@ Status.values().each {
model.statusIcons[(it)] = imageIcon("/${it.name().toLowerCase()}.png")
}
openDialog = fileChooser(fileSelectionMode: JFileChooser.DIRECTORIES_ONLY)
frame = application(title:'Personal Issue Tracker',
frame = application(title: 'Personal Issue Tracker',
minimumSize: [400, 200],
preferredSize: [800, 500],
pack:true,
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") {
@ -76,12 +73,12 @@ frame = application(title:'Personal Issue Tracker',
menuItem(shutdown)
}
menu('View') {
menu("View") {
menu('Category') {
Category.values().each { cat ->
checkBoxMenuItem(cat.toString(),
selected: model.filter.categories.contains(cat),
actionPerformed: { evt ->
actionPerformed: {
if (model.filter.categories.contains(cat)) {
model.filter.categories.remove(cat)
evt.source.selected = false
@ -120,17 +117,17 @@ frame = application(title:'Personal Issue Tracker',
JOptionPane.QUESTION_MESSAGE)
if (newSize == null || !newSize.isFloat())
JOptionPane.showMessageDialog(frame,
"$newSize is not a valid size.",
'Change Issue Detail Text Size...',
'$newSize is not a valid size.',
'Change Issue Detail Size...',
JOptionPane.ERROR_MESSAGE)
else model.issueDetailFont = model.issueDetailFont
.deriveFont(newSize.toFloat())
})
})
}
menu('Sort') {
menu("Sort") {
sortMenuButtonGroup = buttonGroup()
checkBoxMenuItem('By ID',
checkBoxMenuItem('By ID',
buttonGroup: sortMenuButtonGroup,
actionPerformed: {
model.filter.issueSorter = { it.id }
@ -160,6 +157,7 @@ frame = application(title:'Personal Issue Tracker',
model.filter.issueSorter = { it.title }
controller.refreshIssues()
})
}
}

View File

@ -75,13 +75,13 @@ projectPopupMenu = popupMenu() {
menuItem(deleteProjectPop)
}
// popup menu for isses
// popup menu for issues
issuePopupMenu = popupMenu() {
menuItem(newIssue)
menuItem(deleteIssuePop)
separator()
menu('Change Category', enabled: bind { model.popupIssue != null }) {
menu('Change Category', enabled: bind { model.popupIssue != null }) {
Category.values().each { category ->
menuItem(category.toString(),
icon: model.mainMVC.model.categoryIcons[(category)],
@ -91,15 +91,15 @@ issuePopupMenu = popupMenu() {
model.popupIssue.category = category
controller.refreshIssues()
} catch (IOException ioe) {
JOptionPane.showMessageDialog(mainMVC.view.frame,
ioe.getLocalizedMessage(), 'Change Category',
JOptionPane.showMessage(mainMVC.view.frame,
ioe.getLocalizedMessage(), "Change Category",
JOptionPane.ERROR_MESSAGE)
}
})
}
}
menu('Change Status', enabled: bind { model.popupIssue != null }) {
menu('Change Status', enabled: bind { model.popupIssue != null}) {
Status.values().each { status ->
menuItem(status.toString(),
icon: model.mainMVC.model.statusIcons[(status)],
@ -108,8 +108,8 @@ issuePopupMenu = popupMenu() {
try {
model.popupIssue.status = status
controller.refreshIssues()
} catch (IOException ioe) {
JOptionPane.showMessageDialog(mainMVC.view.frame,
} catch (IOException ioe) {
JOptionPane.showMessage(model.mainMVC.view.frame,
ioe.getLocalizedMessage(), 'Change Status',
JOptionPane.ERROR_MESSAGE)
}
@ -123,14 +123,14 @@ issuePopupMenu = popupMenu() {
def newPriority = JOptionPane.showInputDialog(mainMVC.view.frame,
'New priority (0-9)', 'Change Priority...',
JOptionPane.QUESTION_MESSAGE)
try { model.popupIssue.priority = newPriority.toInteger() }
try { model.popupIsse.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(mainMVC.view.fraw,
JOptionPane.showMessageDialog(model.mainMVC.view.frame,
ioe.getLocalizedMessage(), 'Change Priority...',
JOptionPane.ERROR_MESSAGE)
}
@ -140,28 +140,28 @@ issuePopupMenu = popupMenu() {
// main split view
panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
// dividerLocation: bind(source: model.mainModel, property: dividerLocation),
dividerLocation: 200,
oneTouchExpandable: true,
constraints: gbc(fill: GBC.BOTH, insets: [10,10,10,10],
constraints: gbc(fill: GBC.BOTH, insets: [10, 10, 10, 10],
weightx: 2, weighty: 2)) {
// left side (projects tree and buttons)
panel(constraints: "left") {
// left side (project tree and buttons
panel(constraints: 'left') {
gridBagLayout()
// tree view of projects
scrollPane(constraints: gbc(fill: GBC.BOTH, gridx: 0, gridy:0,
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: {
sourceValue: {
if (model.rootProject) {
def rootNode = new DefaultMutableTreeNode()
def flatview = new FlatProjectView('All Issues')
flatviews.projects[(model.rootProject.name)] =
flatview.projects[(model.rootProject.name)] =
model.rootProject
rootNode.add(new DefaultMutableTreeNode(flatview))
rootNode.add(controller.makeNodes(model.rootProject))
@ -178,18 +178,17 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
mouseClicked: { evt ->
if (evt.button == MouseEvent.BUTTON3) {
controller.showProjectPopup(
projectTree.getPathForLocation(evt.x, evt.y)
?.lastPathComponent?.userObject,
projectTree.getPathForLocation(evt.x, evt.y)?.
lastPathComponent?.userObject,
evt.x, evt.y)
}
})
projectTree.rootVisible = false
projectTree.selectionModel.selectionMode =
TreeSelectionModel.SINGLE_TREE_SELECTION
}
// project buttons
newProjectButton = button(newProject,
constraints: gbc(fill: GBC.NONE, gridx: 0, gridy: 1,
anchor: GBC.WEST))
@ -198,7 +197,7 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
anchor: GBC.WEST))
}
// split between issue list and issue details
// split between issues list and issue details
splitPane(orientation: JSplitPane.VERTICAL_SPLIT,
dividerLocation: 200, constraints: "right") {
@ -206,7 +205,7 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
gridBagLayout()
scrollPane(constraints: gbc(fill: GBC.BOTH, weightx: 2,
weighty: 2, gridx: 0, gridy: 0, gridwidth: 3)) {
weighty: 2, gridx: 0, gridy: 0, gridwidth: 3)) {
issueTable = table(
autoCreateRowSorter: true,
@ -223,7 +222,7 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
translatedPoint.translate(-issueTable.locationOnScreen.@x,
-issueTable.locationOnScreen.@y)
def row = issueTable.rowAtPoint(translatedPoint)
issueTable.setRowSelectionInterval(row, row)
controller.showIssuePopup(
@ -238,21 +237,6 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
controller.displayIssue(controller.getSelectedIssue())
}
/*issueList = list(
cellRenderer: model.issueCellRenderer,
selectionMode: ListSelectionModel.SINGLE_SELECTION,
valueChanged: { evt ->
controller.displayIssue(issueList.selectedValue)
},
mouseClicked: { evt ->
if (evt.button == MouseEvent.BUTTON3) {
issueList.selectedIndex = issueList.locationToIndex(
[evt.x, evt.y] as Point)
controller.showIssuePopup(
issueList.selectedValue, evt.x, evt.y)
}
})*/
}
wordWrapCheckBox = checkBox('Word wrap',
@ -266,7 +250,6 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
enabled: bind(source: issueTable.selectionModel,
sourceEvent: 'valueChanged',
sourceValue: { !issueTable.selectionModel.isSelectionEmpty() }))
}
scrollPane(constraints: "bottom",
@ -274,45 +257,38 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
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",
constraints: 'editor',
wrapStyleWord: true,
lineWrap: bind(source: wordWrapCheckBox,
sourceProperty: 'selected'),
editable: bind( source: issueTable.selectionModel,
editable: bind(source: issueTable.selectionModel,
sourceEvent: 'valueChanged',
sourceValue:
{ !issueTable.selectionModel.isSelectionEmpty() }),
font: model.mainMVC.model.issueDetailFont,
focusGained: {},
focusLost: {
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")
},
mouseExited: {
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")
})
issueTextDisplay = editorPane(contentType: "text/html",
constraints: "display",
focusLost: leavingEditorClosure,
mouseExited: leavingEditorClosure)
issueTextDisplay = editorPane(contentType: 'text/html',
constraints: 'display',
editable: false,
preferredSize: [10, 10],
mouseClicked: { evt ->
mouseClicked: {evt ->
if (evt.clickCount > 1)
issueTextPanelLayout.show(issueTextPanel, "editor")
issueTextPanelLayout.show(issueTextPanel, 'editor')
})
}