242 lines
8.7 KiB
Groovy
242 lines
8.7 KiB
Groovy
package com.jdbernard.pit.swing
|
|
|
|
import com.jdbernard.pit.Category
|
|
import com.jdbernard.pit.Status
|
|
import com.jdbernard.pit.Project
|
|
import java.awt.Font
|
|
import java.awt.GridBagConstraints as GBC
|
|
import java.awt.Point
|
|
import java.awt.event.MouseEvent
|
|
import javax.swing.JOptionPane
|
|
import javax.swing.JSplitPane
|
|
import javax.swing.JTextField
|
|
import javax.swing.ListSelectionModel
|
|
import javax.swing.tree.DefaultMutableTreeNode
|
|
import javax.swing.tree.DefaultTreeCellRenderer
|
|
import javax.swing.tree.DefaultTreeModel
|
|
import javax.swing.tree.TreeSelectionModel
|
|
|
|
actions {
|
|
action (
|
|
id: 'newIssue',
|
|
name: 'New Issue',
|
|
icon: imageIcon("/add.png"),
|
|
accelerator: shortcut('N'),
|
|
closure: controller.newIssue,
|
|
enabled: bind { model.selectedProject != null }
|
|
)
|
|
|
|
action (
|
|
id: 'newProject',
|
|
name: 'New Project...',
|
|
icon: imageIcon("/add.png"),
|
|
closure: controller.newProject
|
|
)
|
|
|
|
action (
|
|
id: 'deleteProject',
|
|
name: 'Delete Project',
|
|
closure: controller.deleteProject,
|
|
enabled: bind {model.selectedProject != null }
|
|
)
|
|
|
|
action (
|
|
id: 'deleteProjectPop',
|
|
name: 'Delete Project',
|
|
icon: imageIcon("/delete.png"),
|
|
closure: controller.deleteProject,
|
|
enabled: bind { model.popupProject != null }
|
|
)
|
|
action (
|
|
id: 'deleteIssue',
|
|
name: 'Delete Issue',
|
|
icon: imageIcon("/delete.png"),
|
|
closure: controller.deleteIssue,
|
|
)
|
|
|
|
action (
|
|
id: 'deleteIssuePop',
|
|
name: 'Delete Issue',
|
|
icon: imageIcon("/delete.png"),
|
|
closure: controller.deleteIssue,
|
|
enabled: bind { model.popupIssue != null }
|
|
)
|
|
}
|
|
|
|
// popup menu for projects
|
|
projectPopupMenu = popupMenu() {
|
|
menuItem(newProject)
|
|
menuItem(deleteProjectPop)
|
|
}
|
|
|
|
// popup menu for isses
|
|
issuePopupMenu = popupMenu() {
|
|
menuItem(newIssue)
|
|
menuItem(deleteIssuePop)
|
|
separator()
|
|
|
|
menu('Change Category') {
|
|
Category.values().each { category ->
|
|
menuItem(category.toString(),
|
|
icon: model.mainMVC.model.categoryIcons[(category)],
|
|
enabled: bind { model.popupIssue != null },
|
|
actionPerformed: {
|
|
model.popupIssue.category = category
|
|
issueList.invalidate()
|
|
issueList.repaint()
|
|
})
|
|
}
|
|
}
|
|
|
|
menu('Change Status') {
|
|
Status.values().each { status ->
|
|
menuItem(status.toString(),
|
|
icon: model.mainMVC.model.statusIcons[(status)],
|
|
enabled: bind { model.popupIssue != null },
|
|
actionPerformed: {
|
|
model.popupIssue.status = status
|
|
issueList.invalidate()
|
|
issueList.repaint()
|
|
})
|
|
}
|
|
}
|
|
|
|
menuItem('Change Priority...',
|
|
enabled: bind { model.popupIssue != null },
|
|
actionPerformed: {
|
|
def newPriority = JOptionPane.showInputDialog(mainMVC.view.frame,
|
|
'New priority (0-9)', 'Change Priority...',
|
|
JOptionPane.QUESTION_MESSAGE)
|
|
try { model.popupIssue.priority = newPriority.toInteger() }
|
|
catch (exception) {
|
|
JOptionPane.showMessageDialog(mainMVC.view.frame,
|
|
'The priority value must be an integer in [0-9].',
|
|
'Change Priority...', JOptionPane.ERROR_MESSAGE)
|
|
return
|
|
}
|
|
issueList.invalidate()
|
|
issueList.repaint()
|
|
})
|
|
}
|
|
|
|
// main split view
|
|
panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
|
|
// dividerLocation: bind(source: model.mainModel, property: dividerLocation),
|
|
constraints: gbc(fill: GBC.BOTH, insets: [10,10,10,10],
|
|
weightx: 2, weighty: 2)) {
|
|
|
|
// left side (projects tree and buttons)
|
|
panel(constraints: "left") {
|
|
gridBagLayout()
|
|
|
|
// tree view of projects
|
|
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: {
|
|
if (model.rootProject) {
|
|
projectTree.rootVisible =
|
|
model.rootProject.issues.size()
|
|
new DefaultTreeModel(controller.makeNodes(
|
|
model.rootProject))
|
|
} else {
|
|
projectTree.rootVisible = false
|
|
new DefaultTreeModel(new DefaultMutableTreeNode())
|
|
}
|
|
}),
|
|
valueChanged: { evt ->
|
|
model.selectedProject = evt?.newLeadSelectionPath?.
|
|
lastPathComponent?.userObject ?: model.rootProject
|
|
controller.displayProject(model.selectedProject)
|
|
},
|
|
mouseClicked: { evt ->
|
|
if (evt.button == MouseEvent.BUTTON3) {
|
|
controller.showProjectPopup(
|
|
projectTree.getPathForLocation(evt.x, evt.y)
|
|
?.lastPathComponent?.userObject,
|
|
evt.x, evt.y)
|
|
}
|
|
})
|
|
projectTree.model = new DefaultTreeModel(
|
|
new DefaultMutableTreeNode())
|
|
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))
|
|
deleteProjectButton = button(deleteProject,
|
|
constraints: gbc(fill: GBC.NONE, gridx: 1, gridy: 1,
|
|
anchor: GBC.WEST))
|
|
}
|
|
|
|
// split between issue list and issue details
|
|
splitPane(orientation: JSplitPane.VERTICAL_SPLIT,
|
|
dividerLocation: 200, constraints: "right") {
|
|
|
|
panel(constraints: "top") {
|
|
gridBagLayout()
|
|
|
|
scrollPane(constraints: gbc(fill: GBC.BOTH, weightx: 2,
|
|
weighty: 2, gridx: 0, gridy: 0, gridwidth: 3)) {
|
|
|
|
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',
|
|
constraints: gbc(gridx: 0, gridy: 1, weightx: 2,
|
|
anchor: GBC.WEST), selected: true)
|
|
button(newIssue,
|
|
constraints: gbc(gridx: 1, gridy: 1, anchor: GBC.EAST))
|
|
|
|
deleteIssueButton = button(deleteIssue,
|
|
constraints: gbc(gridx: 2, gridy: 1, anchor: GBC.EAST),
|
|
enabled: bind(source: issueList, sourceEvent: 'valueChanged',
|
|
sourceValue: { issueList.selectedValue != null }))
|
|
|
|
}
|
|
scrollPane(constraints: "bottom") {
|
|
issueTextArea = textArea(
|
|
wrapStyleWord: true,
|
|
lineWrap: bind(source: wordWrapCheckBox,
|
|
sourceProperty: 'selected'),
|
|
editable: bind( source: issueList, sourceEvent: 'valueChanged',
|
|
sourceValue: { issueList.selectedValue != null }),
|
|
font: new Font(Font.MONOSPACED, Font.PLAIN, 10),
|
|
focusGained: {},
|
|
focusLost: {
|
|
if (!issueList?.selectedValue) return
|
|
if (issueTextArea.text != issueList.selectedValue.text)
|
|
issueList.selectedValue.text = issueTextArea.text
|
|
},
|
|
mouseExited: {
|
|
if (!issueList?.selectedValue) return
|
|
if (issueTextArea.text != issueList.selectedValue.text)
|
|
issueList.selectedValue.text = issueTextArea.text
|
|
})
|
|
}
|
|
}
|
|
}
|