In the middle of updating pit-swing.
This commit is contained in:
@ -0,0 +1,64 @@
|
||||
package com.jdbernard.pit.swing
|
||||
|
||||
import com.jdbernard.pit.Category
|
||||
import com.jdbernard.pit.Status
|
||||
import java.awt.GridBagConstraints as GBC
|
||||
import javax.swing.DefaultComboBoxModel
|
||||
|
||||
dialog = dialog(title: 'New Task...', modal: true, pack: true,
|
||||
locationRelativeTo: null) {
|
||||
|
||||
gridBagLayout()
|
||||
|
||||
label('Title/Summary:',
|
||||
constraints: gbc(gridx: 0, gridy: 0, gridwidth: 3,
|
||||
insets: [5, 5, 0, 5], fill: GBC.HORIZONTAL))
|
||||
titleTextField = textField(
|
||||
constraints: gbc(gridx: 0, gridy: 1, gridwidth: 3,
|
||||
insets: [0, 10, 0, 5], fill: GBC.HORIZONTAL),
|
||||
keyTyped: { model.text = titleTextField.text })
|
||||
|
||||
label('Category:',
|
||||
constraints: gbc(gridx: 0, gridy: 2, insets: [5, 5, 0, 0],
|
||||
fill: GBC.HORIZONTAL))
|
||||
categoryComboBox = comboBox(
|
||||
constraints: gbc(gridx: 1, gridy: 2, insets: [5, 5, 0, 5],
|
||||
fill: GBC.HORIZONTAL),
|
||||
model: new DefaultComboBoxModel(Category.values()),
|
||||
editable: false,
|
||||
itemStateChanged: { model.category = categoryComboBox.selectedValue })
|
||||
|
||||
label('Status:',
|
||||
constraints: gbc(gridx: 0, gridy: 3, insets: [5, 5, 0, 0],
|
||||
fill: GBC.HORIZONTAL))
|
||||
statusComboBox = comboBox(
|
||||
constraints: gbc(gridx: 1, gridy: 3, insets: [5, 5, 0, 5],
|
||||
fill: GBC.HORIZONTAL),
|
||||
model: new DefaultComboBoxModel(Status.values()),
|
||||
editable: false,
|
||||
itemStateChanged: { model.status = statusComboBox.selectedValue })
|
||||
|
||||
label('Priority (0-9, 0 is highest priority):',
|
||||
constraints: gbc(gridx: 0, gridy: 4, insets: [5, 5, 0, 0],
|
||||
fill: GBC.HORIZONTAL))
|
||||
prioritySpinner = spinner(
|
||||
constraints: gbc( gridx: 1, gridy: 4, insets: [5, 5, 0, 5],
|
||||
fill: GBC.HORIZONTAL),
|
||||
model: spinnerNumberModel(maximum: 9, minimum: 0),
|
||||
stateChanged: { model.priority = prioritySpinner.value })
|
||||
|
||||
button('Cancel',
|
||||
actionPerformed: {
|
||||
model.accept = false
|
||||
dialog.visible = false
|
||||
},
|
||||
constraints: gbc(gridx: 0, gridy: 5, insets: [5, 5, 5, 5],
|
||||
anchor: GBC.EAST))
|
||||
button('Create Issue',
|
||||
actionPerformed: {
|
||||
model.accept = true
|
||||
dialog.visible = false
|
||||
},
|
||||
constraints: gbc(gridx: 1, gridy: 5, insets: [5, 5, 5, 5],
|
||||
anchor: GBC.WEST))
|
||||
}
|
@ -9,195 +9,45 @@ import com.jdbernard.pit.FileProject
|
||||
import groovy.beans.Bindable
|
||||
import java.awt.BorderLayout as BL
|
||||
import java.awt.GridBagConstraints as GBC
|
||||
import java.awt.Font
|
||||
import java.awt.Point
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.DefaultComboBoxModel
|
||||
import javax.swing.DefaultListModel
|
||||
import javax.swing.JDialog
|
||||
import javax.swing.JFileChooser
|
||||
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
|
||||
import net.miginfocom.swing.MigLayout
|
||||
|
||||
import java.awt.Color
|
||||
|
||||
actions {
|
||||
action (
|
||||
id: 'newIssue',
|
||||
name: 'New Issue',
|
||||
icon: imageIcon("/add.png"),
|
||||
accelerator: shortcut('N'),
|
||||
closure: controller.newIssue,
|
||||
enabled: bind { model.selectedProject != null }
|
||||
action(
|
||||
id: 'openProject',
|
||||
name: 'Open...',
|
||||
icon: imageIcon('/folder.png'),
|
||||
accelerator: shortcut('O'),
|
||||
closure: controller.openProject
|
||||
)
|
||||
|
||||
action (
|
||||
id: 'createIssue',
|
||||
name: 'Create Issue',
|
||||
closure: controller.createIssue
|
||||
)
|
||||
|
||||
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 }
|
||||
action(
|
||||
id: 'shutdown',
|
||||
name: 'Exit',
|
||||
icon: imageIcon('/shutdown.png'),
|
||||
accelerator: shortcut('x'),
|
||||
closure: controller.shutdown
|
||||
)
|
||||
}
|
||||
|
||||
/* ****************
|
||||
* GUI components
|
||||
* ****************/
|
||||
categoryIcons = [:]
|
||||
statusIcons = [:]
|
||||
|
||||
// initialize category-related view data
|
||||
Category.values().each {
|
||||
categoryIcons[(it)] = imageIcon("/${it.name().toLowerCase()}.png")
|
||||
model.categoryIcons[(it)] = imageIcon("/${it.name().toLowerCase()}.png")
|
||||
model.filter.categories.add(it)
|
||||
}
|
||||
|
||||
Status.values().each {
|
||||
statusIcons[(it)] = imageIcon("/${it.name().toLowerCase()}.png")
|
||||
model.statusIcons[(it)] = imageIcon("/${it.name().toLowerCase()}.png")
|
||||
}
|
||||
|
||||
|
||||
openDialog = fileChooser(fileSelectionMode: JFileChooser.DIRECTORIES_ONLY)
|
||||
|
||||
newIssueDialog = dialog(title: 'New Task...', modal: true, pack: true,
|
||||
locationRelativeTo: null) {
|
||||
|
||||
gridBagLayout()
|
||||
|
||||
label('Title/Summary:',
|
||||
constraints: gbc(gridx: 0, gridy: 0, gridwidth: 3,
|
||||
insets: [5, 5, 0, 5], fill: GBC.HORIZONTAL))
|
||||
titleTextField = textField(
|
||||
constraints: gbc(gridx: 0, gridy: 1, gridwidth: 3,
|
||||
insets: [0, 10, 0, 5], fill: GBC.HORIZONTAL))
|
||||
|
||||
label('Category:',
|
||||
constraints: gbc(gridx: 0, gridy: 2, insets: [5, 5, 0, 0],
|
||||
fill: GBC.HORIZONTAL))
|
||||
categoryComboBox = comboBox(
|
||||
constraints: gbc(gridx: 1, gridy: 2, insets: [5, 5, 0, 5],
|
||||
fill: GBC.HORIZONTAL),
|
||||
model: new DefaultComboBoxModel(Category.values()))
|
||||
|
||||
label('Status:',
|
||||
constraints: gbc(gridx: 0, gridy: 3, insets: [5, 5, 0, 0],
|
||||
fill: GBC.HORIZONTAL))
|
||||
statusComboBox = comboBox(
|
||||
constraints: gbc(gridx: 1, gridy: 3, insets: [5, 5, 0, 5],
|
||||
fill: GBC.HORIZONTAL),
|
||||
model: new DefaultComboBoxModel(Status.values()))
|
||||
|
||||
label('Priority (0-9, 0 is highest priority):',
|
||||
constraints: gbc(gridx: 0, gridy: 4, insets: [5, 5, 0, 0],
|
||||
fill: GBC.HORIZONTAL))
|
||||
prioritySpinner = spinner(
|
||||
constraints: gbc( gridx: 1, gridy: 4, insets: [5, 5, 0, 5],
|
||||
fill: GBC.HORIZONTAL),
|
||||
model: spinnerNumberModel(maximum: 9, minimum: 0))
|
||||
|
||||
button('Cancel', actionPerformed: { newIssueDialog.visible = false },
|
||||
constraints: gbc(gridx: 0, gridy: 5, insets: [5, 5, 5, 5],
|
||||
anchor: GBC.EAST))
|
||||
button(createIssue,
|
||||
constraints: gbc(gridx: 1, gridy: 5, insets: [5, 5, 5, 5],
|
||||
anchor: GBC.WEST))
|
||||
}
|
||||
|
||||
projectPopupMenu = popupMenu() {
|
||||
menuItem(newProject)
|
||||
menuItem(deleteProjectPop)
|
||||
}
|
||||
|
||||
issuePopupMenu = popupMenu() {
|
||||
menuItem(newIssue)
|
||||
menuItem(deleteIssuePop)
|
||||
separator()
|
||||
|
||||
menu('Change Category') {
|
||||
Category.values().each { category ->
|
||||
menuItem(category.toString(),
|
||||
icon: 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: 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(frame,
|
||||
'New priority (0-9)', 'Change Priority...',
|
||||
JOptionPane.QUESTION_MESSAGE)
|
||||
try { model.popupIssue.priority = newPriority.toInteger() }
|
||||
catch (exception) {
|
||||
JOptionPane.showMessage(frame, 'The priority value must ' +
|
||||
'be an integer in [0-9].', 'Change Priority...',
|
||||
JOptionPane.ERROR_MESSAGE)
|
||||
return
|
||||
}
|
||||
issueList.invalidate()
|
||||
issueList.repaint()
|
||||
})
|
||||
}
|
||||
|
||||
frame = application(title:'Personal Issue Tracker',
|
||||
minimumSize: [800, 500],
|
||||
pack:true,
|
||||
@ -211,16 +61,8 @@ frame = application(title:'Personal Issue Tracker',
|
||||
// main menu
|
||||
menuBar() {
|
||||
menu("File") {
|
||||
menuItem('Open...', actionPerformed: {
|
||||
def projectDir
|
||||
if (openDialog.showOpenDialog(frame) !=
|
||||
JFileChooser.APPROVE_OPTION) return
|
||||
|
||||
projectDir = openDialog.selectedFile
|
||||
model.rootProject = new FileProject(projectDir)
|
||||
})
|
||||
|
||||
menuItem('Exit', actionPerformed: { app.shutdown() })
|
||||
menuItem(openProject)
|
||||
menuItem(shutdown)
|
||||
}
|
||||
|
||||
menu('View') {
|
||||
@ -302,127 +144,7 @@ frame = application(title:'Personal Issue Tracker',
|
||||
}
|
||||
}
|
||||
|
||||
gridBagLayout()
|
||||
mainTabbedPane = tabbedPane() {
|
||||
|
||||
// main split view
|
||||
splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
|
||||
dividerLocation: 280,
|
||||
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: bind(source: model,
|
||||
sourceProperty: 'issueListRenderer'),
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,241 @@
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user