diff --git a/pit-swing/CHANGE ME b/pit-swing/CHANGE ME index 688e9e9..d3435ca 100644 Binary files a/pit-swing/CHANGE ME and b/pit-swing/CHANGE ME differ diff --git a/pit-swing/application.properties b/pit-swing/application.properties index 1ae9b23..e7d3722 100644 --- a/pit-swing/application.properties +++ b/pit-swing/application.properties @@ -2,5 +2,5 @@ #Thu May 13 17:04:40 CDT 2010 app.griffon.version=0.3 app.name=pit-swing -app.version=2.2.0 +app.version=2.3.1 plugins.fest=0.3 diff --git a/pit-swing/griffon-app/controllers/com/jdbernard/pit/swing/PITController.groovy b/pit-swing/griffon-app/controllers/com/jdbernard/pit/swing/PITController.groovy index 072ca42..c08e4ee 100644 --- a/pit-swing/griffon-app/controllers/com/jdbernard/pit/swing/PITController.groovy +++ b/pit-swing/griffon-app/controllers/com/jdbernard/pit/swing/PITController.groovy @@ -13,9 +13,7 @@ class PITController { void mvcGroupInit(Map args) { SwingUtilities.invokeAndWait { - model.issueListRenderer = new IssueListCellRenderer() - model.issueListRenderer.categoryIcons = model.categoryIcons - model.issueListRenderer.statusIcons = model.statusIcons + model.issueListRenderer = new IssueTableCellRenderer() def config = new File(System.getProperty('user.home'), '.pit') config = new File(config, 'pit_swing.groovy') diff --git a/pit-swing/griffon-app/controllers/com/jdbernard/pit/swing/ProjectPanelController.groovy b/pit-swing/griffon-app/controllers/com/jdbernard/pit/swing/ProjectPanelController.groovy index 999cab4..e51b78c 100644 --- a/pit-swing/griffon-app/controllers/com/jdbernard/pit/swing/ProjectPanelController.groovy +++ b/pit-swing/griffon-app/controllers/com/jdbernard/pit/swing/ProjectPanelController.groovy @@ -29,14 +29,22 @@ class ProjectPanelController { view.issueTextArea.text = "" if (!project) return - if (!model.projectListModels[(project.name)]) { - def dlm = new DefaultListModel() - project.eachIssue(model.filter ?: model.mainMVC.model.filter) - { dlm.addElement(it) } - model.projectListModels[(project.name)] = dlm + 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.issueList.setModel(model.projectListModels[(project.name)]) + 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) { @@ -58,7 +66,7 @@ class ProjectPanelController { void showIssuePopup(Issue issue, def x, def y) { model.popupIssue = issue - view.issuePopupMenu.show(view.issueList, x, y) + view.issuePopupMenu.show(view.issueTable, x, y) } void refreshProject() { @@ -76,7 +84,7 @@ class ProjectPanelController { } void refreshIssues() { - model.projectListModels.clear() + model.projectTableModels.clear() displayProject(model.selectedProject) } @@ -131,7 +139,7 @@ class ProjectPanelController { status: nidModel.status, priority: nidModel.priority, text: issueText) - model.projectListModels[(model.selectedProject.name)] = null + model.projectTableModels[(model.selectedProject.name)] = null displayProject(model.selectedProject) } } @@ -139,20 +147,21 @@ class ProjectPanelController { def deleteIssue = { evt -> def issue if (evt.source == view.deleteIssueButton) - issue = view.issueList.selectedValue + issue = getSelectedIssue() else issue = model.popupIssue model.selectedProject.issues.remove(issue.id) - model.projectListModels[(model.selectedProject.name)] - .removeElement(issue) + view.issueTable.model.issues.remove(issue) issue.delete() + view.issueTable.invlidate() } - def changeCategory = { evt -> - model.popupIssue.status = status - view.issueList.invalidate() - view.issueList.repaint() - } + def getSelectedIssue() { + if (view.issueTable.selectionModel.isSelectionEmpty()) + return null + return view.issueTable.model.issues[view.issueTable. + convertRowIndexToModel(view.issueTable.selectedRow)] + } } diff --git a/pit-swing/griffon-app/models/com/jdbernard/pit/swing/ProjectPanelModel.groovy b/pit-swing/griffon-app/models/com/jdbernard/pit/swing/ProjectPanelModel.groovy index 686ae29..ed17010 100644 --- a/pit-swing/griffon-app/models/com/jdbernard/pit/swing/ProjectPanelModel.groovy +++ b/pit-swing/griffon-app/models/com/jdbernard/pit/swing/ProjectPanelModel.groovy @@ -19,7 +19,7 @@ class ProjectPanelModel { @Bindable Issue popupIssue = null // cache the ListModels - def projectListModels = [:] + def projectTableModels = [:] def issueCellRenderer // local filter for projects and issues diff --git a/pit-swing/griffon-app/views/com/jdbernard/pit/swing/ProjectPanelView.groovy b/pit-swing/griffon-app/views/com/jdbernard/pit/swing/ProjectPanelView.groovy index d3024b5..3f4339b 100644 --- a/pit-swing/griffon-app/views/com/jdbernard/pit/swing/ProjectPanelView.groovy +++ b/pit-swing/griffon-app/views/com/jdbernard/pit/swing/ProjectPanelView.groovy @@ -10,8 +10,10 @@ import java.awt.Point import java.awt.event.MouseEvent import javax.swing.JOptionPane import javax.swing.JSplitPane +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 @@ -83,8 +85,14 @@ issuePopupMenu = popupMenu() { icon: model.mainMVC.model.categoryIcons[(category)], enabled: bind { model.popupIssue != null }, actionPerformed: { - model.popupIssue.category = category - controller.refreshIssues() + try { + model.popupIssue.category = category + controller.refreshIssues() + } catch (IOException ioe) { + JOptionPane.showMessageDialog(mainMVC.view.frame, + ioe.getLocalizedMessage(), 'Change Category', + JOptionPane.ERROR_MESSAGE) + } }) } } @@ -95,8 +103,14 @@ issuePopupMenu = popupMenu() { icon: model.mainMVC.model.statusIcons[(status)], enabled: bind { model.popupIssue != null }, actionPerformed: { - model.popupIssue.status = status - controller.refreshIssues() + try { + model.popupIssue.status = status + controller.refreshIssues() + } catch (IOException ioe) { + JOptionPane.showMessageDialog(mainMVC.view.frame, + ioe.getLocalizedMessage(), 'Change Status', + JOptionPane.ERROR_MESSAGE) + } }) } } @@ -108,11 +122,15 @@ issuePopupMenu = popupMenu() { 'New priority (0-9)', 'Change Priority...', JOptionPane.QUESTION_MESSAGE) try { model.popupIssue.priority = newPriority.toInteger() } - catch (exception) { + 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, + ioe.getLocalizedMessage(), 'Change Priority...', + JOptionPane.ERROR_MESSAGE) } controller.refreshIssues() }) @@ -188,7 +206,37 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT, scrollPane(constraints: gbc(fill: GBC.BOTH, weightx: 2, weighty: 2, gridx: 0, gridy: 0, gridwidth: 3)) { - issueList = list( + 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()) + } + + /*issueList = list( cellRenderer: model.issueCellRenderer, selectionMode: ListSelectionModel.SINGLE_SELECTION, valueChanged: { evt -> @@ -202,7 +250,7 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT, controller.showIssuePopup( issueList.selectedValue, evt.x, evt.y) } - }) + })*/ } wordWrapCheckBox = checkBox('Word wrap', @@ -213,8 +261,8 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT, deleteIssueButton = button(deleteIssue, constraints: gbc(gridx: 2, gridy: 1, anchor: GBC.EAST), - enabled: bind(source: issueList, sourceEvent: 'valueChanged', - sourceValue: { issueList.selectedValue != null })) + enabled: bind(source: issueTable.selectionModel, sourceEvent: 'valueChanged', + sourceValue: { !issueTable.selectionModel.isSelectionEmpty() })) } @@ -223,19 +271,21 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT, wrapStyleWord: true, lineWrap: bind(source: wordWrapCheckBox, sourceProperty: 'selected'), - editable: bind( source: issueList, sourceEvent: 'valueChanged', - sourceValue: { issueList.selectedValue != null }), + editable: bind( source: issueTable.selectionModel, sourceEvent: 'valueChanged', + sourceValue: { !issueTable.selectionModel.isSelectionEmpty() }), font: model.mainMVC.model.issueDetailFont, focusGained: {}, focusLost: { - if (!issueList?.selectedValue) return - if (issueTextArea.text != issueList.selectedValue.text) - issueList.selectedValue.text = issueTextArea.text + def issue = controller.getSelectedIssue() + if (issue == null) return + if (issueTextArea.text != issue.text) + issue.text = issueTextArea.text }, mouseExited: { - if (!issueList?.selectedValue) return - if (issueTextArea.text != issueList.selectedValue.text) - issueList.selectedValue.text = issueTextArea.text + def issue = controller.getSelectedIssue() + if (issue == null) return + if (issueTextArea.text != issue.text) + issue.text = issueTextArea.text }) } } diff --git a/pit-swing/src/main/com/jdbernard/pit/swing/IssueTableCellRenderer.groovy b/pit-swing/src/main/com/jdbernard/pit/swing/IssueTableCellRenderer.groovy new file mode 100644 index 0000000..bd8a48f --- /dev/null +++ b/pit-swing/src/main/com/jdbernard/pit/swing/IssueTableCellRenderer.groovy @@ -0,0 +1,30 @@ +package com.jdbernard.pit.swing + +import java.awt.Component +import java.awt.Dimension +import javax.swing.Icon +import javax.swing.JLabel +import javax.swing.JTable +import javax.swing.table.DefaultTableCellRenderer + +public class IssueTableCellRenderer extends DefaultTableCellRenderer { + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int col) { + super.getTableCellRendererComponent(table, value, isSelected, + hasFocus, row, col) + + if (value instanceof String) { + if (col > 2) { + setHorizontalAlignment(JLabel.LEADING) + } else { + setText("" + getText() + "") + setHorizontalAlignment(JLabel.TRAILING) + } + + } + + return this + } + +} diff --git a/pit-swing/src/main/com/jdbernard/pit/swing/IssueTableModel.groovy b/pit-swing/src/main/com/jdbernard/pit/swing/IssueTableModel.groovy new file mode 100644 index 0000000..c539dc7 --- /dev/null +++ b/pit-swing/src/main/com/jdbernard/pit/swing/IssueTableModel.groovy @@ -0,0 +1,75 @@ +package com.jdbernard.pit.swing + +import com.jdbernard.pit.Filter +import com.jdbernard.pit.Issue +import com.jdbernard.pit.Project +import javax.swing.Icon +import javax.swing.table.AbstractTableModel + +public class IssueTableModel extends AbstractTableModel { + + def issues = [] + boolean projectsVisible = false + def categoryIcons = [:] + def statusIcons = [:] + + public IssueTableModel(Project p, Filter f = null) { + p.eachIssue(f) { issues << it } + } + + public int getRowCount() { + return issues.size + } + + public int getColumnCount() { + if (projectsVisible) return 5 + else return 4 + } + + public Object getValueAt(int row, int column) { + if (row >= getRowCount() || column > getColumnCount()) + return null + + switch(column) { + case 0: return getIcon(issues[row]); break + case 1: return issues[row].id; break + case 2: return "(" + issues[row].priority + "): "; break + case 3: return issues[row].title; break + case 4: return issues[row].project.name; break + default: return "Invalid row index."; break + } + } + + public Class getColumnClass(int column) { + switch (column) { + case 0: return Icon.class; break + default: return String.class; break + } + } + + public String getColumnName(int column) { + switch (column) { + case 0: return "C/S"; break + case 1: return "ID"; break + case 2: return "P"; break + case 3: return "Title/Summary"; break + case 4: return "Project"; break + default: return "ERR"; break + } + } + + public boolean isCellEditable(int row, int col) { return false } + + private Icon getIcon(Issue issue) { + def icon + if (categoryIcons[(issue.category)]) { + icon = categoryIcons[(issue.category)] + + if (statusIcons[(issue.status)]) + icon = new CompositeIcon([icon, statusIcons[(issue.status)]]) + } + + return icon + } + +} diff --git a/release/pit-swing-2.3.1.jar b/release/pit-swing-2.3.1.jar new file mode 100644 index 0000000..1d6e32b Binary files /dev/null and b/release/pit-swing-2.3.1.jar differ