diff --git a/libpit/project.properties b/libpit/project.properties index 1348ca1..4281214 100644 --- a/libpit/project.properties +++ b/libpit/project.properties @@ -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 diff --git a/libpit/release/pit-1.1.4.jar b/libpit/release/pit-1.1.4.jar deleted file mode 100644 index 8dc6a2a..0000000 Binary files a/libpit/release/pit-1.1.4.jar and /dev/null differ diff --git a/libpit/release/pit-1.1.5.jar b/libpit/release/pit-1.1.5.jar new file mode 100644 index 0000000..1c5ee24 Binary files /dev/null and b/libpit/release/pit-1.1.5.jar differ diff --git a/libpit/session.vim b/libpit/session.vim index ae6b9ff..d194a25 100644 --- a/libpit/session.vim +++ b/libpit/session.vim @@ -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 diff --git a/libpit/src/com/jdbernard/pit/Category.groovy b/libpit/src/com/jdbernard/pit/Category.groovy index 7a4eaf1..cc095fe 100644 --- a/libpit/src/com/jdbernard/pit/Category.groovy +++ b/libpit/src/com/jdbernard/pit/Category.groovy @@ -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()}" } } diff --git a/libpit/src/com/jdbernard/pit/FileIssue.groovy b/libpit/src/com/jdbernard/pit/FileIssue.groovy index df42258..7b59cb8 100644 --- a/libpit/src/com/jdbernard/pit/FileIssue.groovy +++ b/libpit/src/com/jdbernard/pit/FileIssue.groovy @@ -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).*/ } diff --git a/libpit/src/com/jdbernard/pit/FileProject.groovy b/libpit/src/com/jdbernard/pit/FileProject.groovy index 199899d..4d8b2fc 100644 --- a/libpit/src/com/jdbernard/pit/FileProject.groovy +++ b/libpit/src/com/jdbernard/pit/FileProject.groovy @@ -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 } diff --git a/libpit/src/com/jdbernard/pit/Project.groovy b/libpit/src/com/jdbernard/pit/Project.groovy index 507453b..9c47f9a 100644 --- a/libpit/src/com/jdbernard/pit/Project.groovy +++ b/libpit/src/com/jdbernard/pit/Project.groovy @@ -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() } diff --git a/libpit/test/com/jdbernard/pit/MockProject.groovy b/libpit/test/com/jdbernard/pit/MockProject.groovy index 2eb569d..9e1c2ee 100644 --- a/libpit/test/com/jdbernard/pit/MockProject.groovy +++ b/libpit/test/com/jdbernard/pit/MockProject.groovy @@ -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 } } diff --git a/pit-swing/griffon-app/views/com/jdbernard/pit/swing/PITView.groovy b/pit-swing/griffon-app/views/com/jdbernard/pit/swing/PITView.groovy index 48ee612..6927c79 100644 --- a/pit-swing/griffon-app/views/com/jdbernard/pit/swing/PITView.groovy +++ b/pit-swing/griffon-app/views/com/jdbernard/pit/swing/PITView.groovy @@ -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 } diff --git a/version.properties b/version.properties index a70b734..86ae6c2 100644 --- a/version.properties +++ b/version.properties @@ -1 +1 @@ -application.version=1.1.4 +application.version=1.1.5