Small enhancements to libpit to facilitate pit-swing.

Added delete() and createNewProject(String) to Project class.
Added nicer toString() method to Category enum.
This commit is contained in:
Jonathan Bernard 2010-02-24 03:05:37 -06:00
parent 45516a5cd9
commit 60109087e5
11 changed files with 145 additions and 34 deletions

View File

@ -1,10 +1,10 @@
#Mon Feb 22 07:08:09 CST 2010 #Wed Feb 24 03:03:11 CST 2010
build.dir=build build.dir=build
src.dir=src src.dir=src
lib.shared.dir=../shared-libs lib.shared.dir=../shared-libs
test.dir=test test.dir=test
build.number=2 build.number=4
expected.application.version=1.1.4 expected.application.version=1.1.5
lib.dir=lib lib.dir=lib
release.dir=release release.dir=release
release.jar=pit-${application.version}.jar release.jar=pit-${application.version}.jar

Binary file not shown.

Binary file not shown.

View File

@ -43,9 +43,9 @@ badd +1 test/com/jdbernard/pit/IssueTest.groovy
badd +6 src/com/jdbernard/pit/Project.groovy badd +6 src/com/jdbernard/pit/Project.groovy
badd +1 test/com/jdbernard/pit/ProjectTest.groovy badd +1 test/com/jdbernard/pit/ProjectTest.groovy
badd +1 src/com/jdbernard/pit/FileIssue.groovy badd +1 src/com/jdbernard/pit/FileIssue.groovy
badd +0 test/com/jdbernard/pit/FileIssueTest.groovy badd +1 test/com/jdbernard/pit/FileIssueTest.groovy
badd +1 src/com/jdbernard/pit/FileProject.groovy badd +1 src/com/jdbernard/pit/FileProject.groovy
badd +0 test/com/jdbernard/pit/FileProjectTest.groovy badd +1 test/com/jdbernard/pit/FileProjectTest.groovy
args build.xml args build.xml
edit build.xml edit build.xml
set splitbelow splitright set splitbelow splitright
@ -701,12 +701,12 @@ setlocal nowinfixwidth
setlocal wrap setlocal wrap
setlocal wrapmargin=0 setlocal wrapmargin=0
silent! normal! zE silent! normal! zE
let s:l = 103 - ((26 * winheight(0) + 39) / 78) let s:l = 1 - ((0 * winheight(0) + 39) / 78)
if s:l < 1 | let s:l = 1 | endif if s:l < 1 | let s:l = 1 | endif
exe s:l exe s:l
normal! zt normal! zt
103 1
normal! 04l normal! 0
wincmd w wincmd w
exe 'vert 1resize ' . ((&columns * 91 + 91) / 182) exe 'vert 1resize ' . ((&columns * 91 + 91) / 182)
exe 'vert 2resize ' . ((&columns * 90 + 91) / 182) exe 'vert 2resize ' . ((&columns * 90 + 91) / 182)
@ -1260,12 +1260,12 @@ setlocal nowinfixwidth
setlocal wrap setlocal wrap
setlocal wrapmargin=0 setlocal wrapmargin=0
silent! normal! zE silent! normal! zE
let s:l = 40 - ((39 * winheight(0) + 39) / 78) let s:l = 1 - ((0 * winheight(0) + 39) / 78)
if s:l < 1 | let s:l = 1 | endif if s:l < 1 | let s:l = 1 | endif
exe s:l exe s:l
normal! zt normal! zt
40 1
normal! 029l normal! 0
wincmd w wincmd w
argglobal argglobal
edit test/com/jdbernard/pit/FileProjectTest.groovy edit test/com/jdbernard/pit/FileProjectTest.groovy
@ -1364,17 +1364,16 @@ setlocal nowinfixwidth
setlocal wrap setlocal wrap
setlocal wrapmargin=0 setlocal wrapmargin=0
silent! normal! zE silent! normal! zE
let s:l = 154 - ((77 * winheight(0) + 39) / 78) let s:l = 1 - ((0 * winheight(0) + 39) / 78)
if s:l < 1 | let s:l = 1 | endif if s:l < 1 | let s:l = 1 | endif
exe s:l exe s:l
normal! zt normal! zt
154 1
normal! 0 normal! 0
wincmd w wincmd w
2wincmd w
exe 'vert 1resize ' . ((&columns * 91 + 91) / 182) exe 'vert 1resize ' . ((&columns * 91 + 91) / 182)
exe 'vert 2resize ' . ((&columns * 90 + 91) / 182) exe 'vert 2resize ' . ((&columns * 90 + 91) / 182)
tabnext 6 tabnext 1
if exists('s:wipebuf') if exists('s:wipebuf')
silent exe 'bwipe ' . s:wipebuf silent exe 'bwipe ' . s:wipebuf
endif endif

View File

@ -8,9 +8,11 @@ public enum Category {
public static Category toCategory(String s) { public static Category toCategory(String s) {
for(c in Category.values()) for(c in Category.values())
if (c.toString().startsWith(s.toUpperCase())) return c if (c.name().startsWith(s.toUpperCase())) return c
throw new IllegalArgumentException("No category matches ${s}.") throw new IllegalArgumentException("No category matches ${s}.")
} }
public String getSymbol() { toString()[0].toLowerCase() } public String getSymbol() { toString()[0].toLowerCase() }
public String toString() { return "${name()[0]}${name()[1..-1].toLowerCase()}" }
} }

View File

@ -36,6 +36,11 @@ public class FileIssue extends Issue {
public String getFilename() { return makeFilename(id, category, priority) } public String getFilename() { return makeFilename(id, category, priority) }
public void setText(String text) {
super.setText(text)
source.write(text)
}
public static boolean isValidFilename(String name) { public static boolean isValidFilename(String name) {
return name ==~ /(\d+)([bcft])(\d).*/ return name ==~ /(\d+)([bcft])(\d).*/
} }

View File

@ -5,7 +5,7 @@ class FileProject extends Project {
protected File source protected File source
public FileProject(File dir) { public FileProject(File dir) {
super(dir.name) super(dir.canonicalFile.name)
if (!dir.isDirectory()) if (!dir.isDirectory())
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -65,6 +65,15 @@ class FileProject extends Project {
return issue return issue
} }
public FileProject createNewProject(String name) {
def newDir = new File(source, name)
newDir.mkdirs()
return new FileProject(newDir)
}
public boolean delete() { return source.delete() }
@Override @Override
public String toString() { return name } public String toString() { return name }

View File

@ -30,4 +30,8 @@ public abstract class Project {
String toString() { return name } String toString() { return name }
public abstract Issue createNewIssue(Map options) public abstract Issue createNewIssue(Map options)
public abstract Project createNewProject(String name)
public abstract boolean delete()
} }

View File

@ -7,4 +7,10 @@ class MockProject extends Project {
public Issue createNewIssue(Map options) { public Issue createNewIssue(Map options) {
throw new UnsupportedOperationException() throw new UnsupportedOperationException()
} }
public Project createNewProject(String name) {
throw new UnsupportedOperationException()
}
public boolean delete() { return true }
} }

View File

@ -1,11 +1,15 @@
package com.jdbernard.pit.swing package com.jdbernard.pit.swing
import com.jdbernard.pit.Category import com.jdbernard.pit.Category
import com.jdbernard.pit.Filter
import com.jdbernard.pit.Issue import com.jdbernard.pit.Issue
import com.jdbernard.pit.Project import com.jdbernard.pit.Project
import com.jdbernard.pit.FileProject import com.jdbernard.pit.FileProject
import groovy.beans.Bindable
import java.awt.event.MouseEvent
import javax.swing.DefaultListModel import javax.swing.DefaultListModel
import javax.swing.JFileChooser import javax.swing.JFileChooser
import javax.swing.JOptionPane
import javax.swing.JSplitPane import javax.swing.JSplitPane
import javax.swing.ListSelectionModel import javax.swing.ListSelectionModel
import javax.swing.tree.DefaultMutableTreeNode import javax.swing.tree.DefaultMutableTreeNode
@ -14,34 +18,78 @@ import javax.swing.tree.DefaultTreeModel
import javax.swing.tree.TreeSelectionModel import javax.swing.tree.TreeSelectionModel
import net.miginfocom.swing.MigLayout import net.miginfocom.swing.MigLayout
// VIEW-Specific data /* ********************
* VIEW-Specific data
* ********************/
// cache the ListModels
projectListModels = [:] projectListModels = [:]
categoryIcons = [(Category.BUG): imageIcon('/bug.png'), // map of category -> list icon
(Category.CLOSED): imageIcon('/closed.png'), categoryIcons = [:]
(Category.FEATURE): imageIcon('/feature.png'),
(Category.TASK): imageIcon('/task.png')]
openDialog = fileChooser(fileSelectionMode: JFileChooser.DIRECTORIES_ONLY) // filter for projects and issues
filter = new Filter(categories: [])
// event methods @Bindable def popupProject = null
displayProject = { evt = null ->
def project= evt?.newLeadSelectionPath?.lastPathComponent?.userObject // initialize category-related view data
Category.values().each {
categoryIcons[(it)] = imageIcon("/${it.name().toLowerCase()}.png")
filter.categories.add(it)
}
/* ***************
* event methods
* ***************/
/**
* displayProject
* @param project Project to display.
*
*/
displayProject = { project = null ->
issueTextArea.text = "" issueTextArea.text = ""
if (!project) return if (!project) return
if (!projectListModels[(project.name)]) { if (!projectListModels[(project.name)]) {
def model = new DefaultListModel() def model = new DefaultListModel()
project.eachIssue { model.addElement(it) } project.eachIssue(filter) { model.addElement(it) }
projectListModels[(project.name)] = model projectListModels[(project.name)] = model
} }
issueList.setModel(projectListModels[(project.name)]) issueList.setModel(projectListModels[(project.name)])
} }
displayIssue = { evt = null -> displayIssue = { issue = null ->
if (issueList.selectedValue) if (issue) issueTextArea.text = issue.text
issueTextArea.text = issueList.selectedValue.text }
showProjectPopup = { project, x, y ->
popupProject = project
projectPopupMenu[1].enabled = project != null
projectPopupMenu.show(projectTree, x, y)
}
/* ****************
* GUI components
* ****************/
openDialog = fileChooser(fileSelectionMode: JFileChooser.DIRECTORIES_ONLY)
projectPopupMenu = popupMenu() {
menuItem('New Project...',
actionPerformed: {
def name = JOptionPane.showInputDialog(frame, 'Project name:',
'New Project...', JOptionPane.QUESTION_MESSAGE)
if (!popupProject) popupProject = model.rootProject
def newProject = popupProject.createNewProject(name)
projectTree.model = new DefaultTreeModel(
makeNodes(model.rootProject))
})
menuItem('Delete Project',
actionPerformed: { popupProject.delete() })
} }
frame = application(title:'Personal Issue Tracker', frame = application(title:'Personal Issue Tracker',
@ -69,6 +117,29 @@ frame = application(title:'Personal Issue Tracker',
projectDir = openDialog.selectedFile projectDir = openDialog.selectedFile
model.rootProject = new FileProject(projectDir) model.rootProject = new FileProject(projectDir)
}) })
menuItem('Exit', actionPerformed: { app.shutdown() })
}
menu('View') {
Category.values().each {
checkBoxMenuItem(it.toString(),
selected: filter.categories.contains(it),
actionPerformed: { evt ->
def cat = Category.toCategory(evt.source.text)
if (filter.categories.contains(cat)) {
filter.categories.remove(cat)
evt.source.selected = false
} else {
filter.categories.add(cat)
evt.source.selected = true
}
projectListModels.clear()
displayProject(projectTree.leadSelectionPath
?.lastPathComponent?.userObject)
})
}
} }
} }
@ -89,7 +160,19 @@ frame = application(title:'Personal Issue Tracker',
new DefaultTreeModel(makeNodes(model.rootProject)) new DefaultTreeModel(makeNodes(model.rootProject))
} else new DefaultTreeModel() } else new DefaultTreeModel()
}), }),
valueChanged: displayProject) valueChanged: { evt ->
displayProject(evt?.newLeadSelectionPath
?.lastPathComponent?.userObject)
},
mouseClicked: { evt ->
if (evt.button == MouseEvent.BUTTON3) {
showProjectPopup(
projectTree.getPathForLocation(evt.x, evt.y)
?.lastPathComponent?.userObject,
evt.x, evt.y)
}
})
projectTree.selectionModel.selectionMode = projectTree.selectionModel.selectionMode =
TreeSelectionModel.SINGLE_TREE_SELECTION TreeSelectionModel.SINGLE_TREE_SELECTION
} }
@ -103,7 +186,7 @@ frame = application(title:'Personal Issue Tracker',
cellRenderer: new IssueListCellRenderer( cellRenderer: new IssueListCellRenderer(
issueIcons: categoryIcons), issueIcons: categoryIcons),
selectionMode: ListSelectionModel.SINGLE_SELECTION, selectionMode: ListSelectionModel.SINGLE_SELECTION,
valueChanged: displayIssue) valueChanged: { displayIssue(issueList.selectedValue) })
} }
scrollPane(constraints: "bottom") { scrollPane(constraints: "bottom") {
issueTextArea = textArea() issueTextArea = textArea()
@ -112,8 +195,11 @@ frame = application(title:'Personal Issue Tracker',
} }
} }
/* ******************
* Auxilary methods
* ******************/
def makeNodes(Project project) { def makeNodes(Project project) {
def rootNode = new DefaultMutableTreeNode(project) def rootNode = new DefaultMutableTreeNode(project)
project.eachProject { rootNode.add(makeNodes(it)) } project.eachProject(filter) { rootNode.add(makeNodes(it)) }
return rootNode return rootNode
} }

View File

@ -1 +1 @@
application.version=1.1.4 application.version=1.1.5