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
src.dir=src
lib.shared.dir=../shared-libs
test.dir=test
build.number=2
expected.application.version=1.1.4
build.number=4
expected.application.version=1.1.5
lib.dir=lib
release.dir=release
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 +1 test/com/jdbernard/pit/ProjectTest.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 +0 test/com/jdbernard/pit/FileProjectTest.groovy
badd +1 test/com/jdbernard/pit/FileProjectTest.groovy
args build.xml
edit build.xml
set splitbelow splitright
@ -701,12 +701,12 @@ setlocal nowinfixwidth
setlocal wrap
setlocal wrapmargin=0
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
exe s:l
normal! zt
103
normal! 04l
1
normal! 0
wincmd w
exe 'vert 1resize ' . ((&columns * 91 + 91) / 182)
exe 'vert 2resize ' . ((&columns * 90 + 91) / 182)
@ -1260,12 +1260,12 @@ setlocal nowinfixwidth
setlocal wrap
setlocal wrapmargin=0
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
exe s:l
normal! zt
40
normal! 029l
1
normal! 0
wincmd w
argglobal
edit test/com/jdbernard/pit/FileProjectTest.groovy
@ -1364,17 +1364,16 @@ setlocal nowinfixwidth
setlocal wrap
setlocal wrapmargin=0
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
exe s:l
normal! zt
154
1
normal! 0
wincmd w
2wincmd w
exe 'vert 1resize ' . ((&columns * 91 + 91) / 182)
exe 'vert 2resize ' . ((&columns * 90 + 91) / 182)
tabnext 6
tabnext 1
if exists('s:wipebuf')
silent exe 'bwipe ' . s:wipebuf
endif

View File

@ -8,9 +8,11 @@ public enum Category {
public static Category toCategory(String s) {
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}.")
}
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 void setText(String text) {
super.setText(text)
source.write(text)
}
public static boolean isValidFilename(String name) {
return name ==~ /(\d+)([bcft])(\d).*/
}

View File

@ -5,7 +5,7 @@ class FileProject extends Project {
protected File source
public FileProject(File dir) {
super(dir.name)
super(dir.canonicalFile.name)
if (!dir.isDirectory())
throw new IllegalArgumentException(
@ -65,6 +65,15 @@ class FileProject extends Project {
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
public String toString() { return name }

View File

@ -30,4 +30,8 @@ public abstract class Project {
String toString() { return name }
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) {
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
import com.jdbernard.pit.Category
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.event.MouseEvent
import javax.swing.DefaultListModel
import javax.swing.JFileChooser
import javax.swing.JOptionPane
import javax.swing.JSplitPane
import javax.swing.ListSelectionModel
import javax.swing.tree.DefaultMutableTreeNode
@ -14,34 +18,78 @@ import javax.swing.tree.DefaultTreeModel
import javax.swing.tree.TreeSelectionModel
import net.miginfocom.swing.MigLayout
// VIEW-Specific data
/* ********************
* VIEW-Specific data
* ********************/
// cache the ListModels
projectListModels = [:]
categoryIcons = [(Category.BUG): imageIcon('/bug.png'),
(Category.CLOSED): imageIcon('/closed.png'),
(Category.FEATURE): imageIcon('/feature.png'),
(Category.TASK): imageIcon('/task.png')]
// map of category -> list icon
categoryIcons = [:]
openDialog = fileChooser(fileSelectionMode: JFileChooser.DIRECTORIES_ONLY)
// filter for projects and issues
filter = new Filter(categories: [])
// event methods
displayProject = { evt = null ->
def project= evt?.newLeadSelectionPath?.lastPathComponent?.userObject
@Bindable def popupProject = null
// 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 = ""
if (!project) return
if (!projectListModels[(project.name)]) {
def model = new DefaultListModel()
project.eachIssue { model.addElement(it) }
project.eachIssue(filter) { model.addElement(it) }
projectListModels[(project.name)] = model
}
issueList.setModel(projectListModels[(project.name)])
}
displayIssue = { evt = null ->
if (issueList.selectedValue)
issueTextArea.text = issueList.selectedValue.text
displayIssue = { issue = null ->
if (issue) issueTextArea.text = issue.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',
@ -69,6 +117,29 @@ frame = application(title:'Personal Issue Tracker',
projectDir = openDialog.selectedFile
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))
} 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 =
TreeSelectionModel.SINGLE_TREE_SELECTION
}
@ -103,7 +186,7 @@ frame = application(title:'Personal Issue Tracker',
cellRenderer: new IssueListCellRenderer(
issueIcons: categoryIcons),
selectionMode: ListSelectionModel.SINGLE_SELECTION,
valueChanged: displayIssue)
valueChanged: { displayIssue(issueList.selectedValue) })
}
scrollPane(constraints: "bottom") {
issueTextArea = textArea()
@ -112,8 +195,11 @@ frame = application(title:'Personal Issue Tracker',
}
}
/* ******************
* Auxilary methods
* ******************/
def makeNodes(Project project) {
def rootNode = new DefaultMutableTreeNode(project)
project.eachProject { rootNode.add(makeNodes(it)) }
project.eachProject(filter) { rootNode.add(makeNodes(it)) }
return rootNode
}

View File

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