Added rst2html code to convert issue text to HTML and display nicely.

This commit is contained in:
Jonathan Bernard
2010-05-27 05:13:29 -05:00
parent 1beb2397e1
commit 5650034cff
22 changed files with 644 additions and 62 deletions

View File

@ -30,6 +30,8 @@ class PITController {
configBinding.templates = model.templates
configBinding.issueListRenderer = model.issueListRenderer
configBinding.initialRepositories = []
configBinding.issueCSS = model.issueCSS
configBinding.PIT_HOME = config.parentFile
def configScript = loader.parseClass(config)
.newInstance(configBinding)
@ -55,6 +57,12 @@ class PITController {
loadProject(repo)
}
}
// open any custom CSS for issue dsiplay
if (configBinding.issueCSS instanceof File)
model.issueCSS = configBinding.issueCSS.text
else
model.issueCSS = configBinding.issueCSS
}
}
@ -88,6 +96,7 @@ class PITController {
mainMVC: [model: model, view: view, controller: this],
newIssueDialogMVC: model.newIssueDialogMVC,
issueCellRenderer: model.issueListRenderer,
issueCSS: model.issueCSS,
rootProject: new FileProject(projectDir))
newMVC.model.id = projectDir.name

View File

@ -10,13 +10,27 @@ import javax.swing.DefaultListModel
import javax.swing.JOptionPane
import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.DefaultTreeModel
import org.dom4j.Document
import org.dom4j.io.OutputFormat
import org.dom4j.io.XMLWriter
import org.nuiton.jrst.JRSTGenerator
import org.nuiton.jrst.JRSTReader
class ProjectPanelController {
// these will be injected by Griffon
def model
def view
def jrstReader
def jrstGen
static URL rst2htmlXSL =
ProjectPanelController.class.getResource("/rst2xhtml.xsl")
void mvcGroupInit(Map args) {
jrstReader = new JRSTReader()
jrstGen = new JRSTGenerator()
refreshProject()
}
@ -27,6 +41,8 @@ class ProjectPanelController {
*/
void displayProject(Project project) {
view.issueTextArea.text = ""
view.issueTextDisplay.text = ""
view.issueTextPanelLayout.show(view.issueTextPanel, "display")
if (!project) return
if (!model.projectTableModels[(project.name)]) {
@ -57,6 +73,9 @@ class ProjectPanelController {
view.issueTextArea.text = issue.text
view.issueTextArea.caretPosition = 0
view.issueTextDisplay.text = rst2html(issue.text)
view.issueTextDisplay.caretPosition = 0
view.issueTextPanelLayout.show(view.issueTextPanel, "display")
}
void showProjectPopup(Project project, def x, def y) {
@ -164,4 +183,40 @@ class ProjectPanelController {
return view.issueTable.model.issues[view.issueTable.
convertRowIndexToModel(view.issueTable.selectedRow)]
}
String rst2html(String rst) {
Document doc // memory model of document
StringWriter outString
StringBuilder result = new StringBuilder()
// read the RST in with the RST parser
new StringReader(rst).withReader { doc = jrstReader.read(it) }
// transform to XHTML
doc = jrstGen.transform(doc, rst2htmlXSL)
// write to the StringWriter
outString = new StringWriter()
outString.withWriter { new XMLWriter(it, new OutputFormat("", true)).write(doc) }
// java's embeded html is primitive, we need to massage the results
outString.toString().eachLine { line ->
// remove the XML version and encoding, title element,
// meta elements
if (line =~ /<\?.*\?>/ || line =~ /<meta.*$/ || line =~ /<title.*$/) { return }
// all other elements, remove all class,xmlns attributes
def m = (line =~ /(<\S+)(\s*(class|xmlns)=".*"\s*)*(\/?>.*)/)
if (m) line = m[0][1] + m[0][4]
result.append(line)
// add in the CSS information to the head
if (line =~/<head>/) result.append(model.issueCSS)
}
println result.toString()
return result.toString()
}
}

View File

@ -18,6 +18,8 @@ class PITModel {
// map of category -> issue template
def templates = [:]
def issueCSS = getClass().getResource("/default-issue.css").openStream().text
def categoryIcons = [:]
def statusIcons = [:]

View File

@ -18,6 +18,8 @@ class ProjectPanelModel {
@Bindable Project selectedProject = null
@Bindable Issue popupIssue = null
String issueCSS = ""
// cache the ListModels
def projectTableModels = [:]
def issueCellRenderer

View File

@ -0,0 +1,24 @@
<style type="text/css">
body {
font-size: small;
}
h1 {
font-size: medium;
text-decoration: underline;
}
h2 {
font-size: small;
}
h3 {
font-size: small;
font-style: italic;
}
h4 {
font-size: small;
font-weight: normal;
font-style: italic;
}
table,th,td{
border-style: solid;
}
</style>

View File

@ -0,0 +1,495 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/document">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="generator" content="JRST http://maven-site.nuiton.org/jrst" />
<title><xsl:value-of select="title"/></title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="comment">
<xsl:comment>
<xsl:text> </xsl:text>
<xsl:apply-templates/>
<xsl:text> </xsl:text>
</xsl:comment>
</xsl:template>
<xsl:template match="title">
<xsl:if test="name(..)='document'">
<h1 class="mainTitle">
<xsl:apply-templates/>
</h1>
</xsl:if>
<xsl:if test="not(name(..)='document')">
<xsl:element name="h{count(ancestor::section) + 1}">
<xsl:attribute name="class">title</xsl:attribute>
<xsl:if test="@refid">
<a class="toc-backref" href="#{@refid}" id="{../@id}"><xsl:apply-templates/></a>
</xsl:if>
<xsl:if test="not(@refid)">
<xsl:apply-templates/>
</xsl:if>
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template match="subtitle">
<xsl:element name="h2">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- just eat it -->
<xsl:template match="substitution_definition">
</xsl:template>
<xsl:template match="docinfo">
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<xsl:apply-templates/>
</tbody>
</table>
</xsl:template>
<xsl:template match="organization|address|contact|version|revision|status|date|copyright">
<tr>
<th class="docinfo-name">
<xsl:value-of select="name(.)"/> :
</th>
<td class="docinfo-content">
<xsl:apply-templates/>
</td>
</tr>
</xsl:template>
<xsl:template match="author">
<xsl:if test="not(../../authors)">
<tr>
<th class="docinfo-name">
<xsl:value-of select="name(.)"/> :
</th>
<td class="docinfo-content">
<xsl:apply-templates/>
</td>
</tr>
</xsl:if>
<xsl:if test="../../authors">
<xsl:variable name="num" select="position()"/>
<xsl:if test="$num=1">
<tr>
<th class="docinfo-name">
<xsl:value-of select="authors"/>authors :
</th>
<td class="docinfo-content">
<xsl:apply-templates/>
</td>
</tr>
</xsl:if>
<xsl:if test="$num>1">
<tr>
<th>
</th>
<td class="docinfo-content">
<xsl:apply-templates/>
</td>
</tr>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template match="transition">
<hr/>
</xsl:template>
<xsl:template match="section">
<a name="{@id}"></a>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="list_item/paragraph[1] | definition_list_item/*/paragraph[1] | field/*/paragraph[1] | option/*/paragraph[1]">
<!--XXX - Unclear how to handle multi-paragraph list items.
| Certainly when they're single paragraphs, we don't want them
| wrapped in a <P> tag. This seems to work okay.
+-->
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="paragraph">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="reference">
<xsl:if test="@refid">
<a href="{@refuri}#{@refid}" id="{@id}"><xsl:apply-templates/></a>
</xsl:if>
<xsl:if test="not(@refid)">
<a href="{@refuri}" id="{@id}"><xsl:apply-templates/></a>
</xsl:if>
</xsl:template>
<xsl:template match="emphasis">
<em><xsl:apply-templates/></em>
</xsl:template>
<xsl:template match="strong">
<b><xsl:apply-templates/></b>
</xsl:template>
<xsl:template match="literal">
<code><xsl:value-of select="text()"/></code>
</xsl:template>
<xsl:template match="literal_block">
<pre class="literal_block"><xsl:value-of select="text()"/></pre>
</xsl:template>
<xsl:template match="bullet_list">
<ul><xsl:apply-templates/></ul>
</xsl:template>
<xsl:template match="enumerated_list">
<ol>
<xsl:choose>
<xsl:when test="@enumtype='arabic'">
<xsl:attribute name="type">1</xsl:attribute>
</xsl:when>
<xsl:when test="@enumtype='loweralpha'">
<xsl:attribute name="type">a</xsl:attribute>
</xsl:when>
<xsl:when test="@enumtype='upperalpha'">
<xsl:attribute name="type">A</xsl:attribute>
</xsl:when>
<xsl:when test="@enumtype='lowerroman'">
<xsl:attribute name="type">i</xsl:attribute>
</xsl:when>
<xsl:when test="@enumtype='upperroman'">
<xsl:attribute name="type">I</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:copy-of select="@start"/>
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="list_item">
<li><xsl:apply-templates/></li>
</xsl:template>
<xsl:template match="field_list">
<div class="field_list"><xsl:apply-templates/></div>
</xsl:template>
<xsl:template match="field">
<xsl:if test="not(../../docinfo)">
<div class="field"><xsl:apply-templates/></div>
</xsl:if>
<xsl:if test="../../docinfo">
<tr>
<th class="docinfo-name">
<xsl:value-of select="field_name/text()"/> :
</th>
<td>
<xsl:apply-templates select="field_body/*"/>
</td>
</tr>
</xsl:if>
</xsl:template>
<xsl:template match="field_name">
<span class="field_name"><xsl:apply-templates/></span>
</xsl:template>
<xsl:template match="field_body">
<span class="field_body"><xsl:apply-templates/></span>
</xsl:template>
<xsl:template match="definition_list">
<dl class="definition_list"><xsl:apply-templates/></dl>
</xsl:template>
<xsl:template match="definition_list_item">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="term">
<dt class="term"><xsl:apply-templates/><xsl:call-template name="classifier"/></dt>
</xsl:template>
<xsl:template name="classifier">
<xsl:for-each select="../classifier">
<span class="classifier"><xsl:apply-templates/></span>
</xsl:for-each>
</xsl:template>
<xsl:template match="classifier">
<!-- do nothing -->
</xsl:template>
<xsl:template match="definition">
<dd class="definition"><xsl:apply-templates/></dd>
</xsl:template>
<xsl:template match="image">
<xsl:choose>
<xsl:when test="(@target) and (@align)">
<div class="align-{@align}" align="{@align}">
<a href="{@target}">
<xsl:call-template name="img" />
</a>
</div>
</xsl:when>
<xsl:when test="@target">
<a href="{@target}">
<xsl:call-template name="img" />
</a>
</xsl:when>
<xsl:when test="@align">
<div class="align-{@align}" align="{@align}">
<xsl:call-template name="img" />
</div>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="img" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="img">
<xsl:element name="img">
<xsl:attribute name="alt"><xsl:value-of select="@alt"/></xsl:attribute>
<xsl:attribute name="src"><xsl:value-of select="@uri"/></xsl:attribute>
<xsl:if test="@width"><xsl:attribute name="width"><xsl:value-of select="@width"/></xsl:attribute></xsl:if>
<xsl:if test="@height"><xsl:attribute name="height"><xsl:value-of select="@height"/></xsl:attribute></xsl:if>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="footer">
<hr/>
<p class="footer"><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="header">
<p class="header"><xsl:apply-templates/></p>
<hr/>
</xsl:template>
<!--
| Table
+-->
<xsl:template match="table">
<table border="1">
<colgroup>
<xsl:apply-templates select="tgroup/colspec"/>
</colgroup>
<xsl:apply-templates select="./tgroup/thead|./tgroup/tbody"/>
</table>
</xsl:template>
<xsl:template match="tgroup/colspec">
<col width="{@colwidth}%"/>
</xsl:template>
<xsl:template match="row">
<tr><xsl:apply-templates/></tr>
</xsl:template>
<xsl:template match="thead">
<thead><xsl:apply-templates/></thead>
</xsl:template>
<xsl:template match="thead/row/entry">
<th>
<xsl:if test="@morecols"><xsl:attribute name="colspan"><xsl:value-of select="@morecols+1"/></xsl:attribute></xsl:if>
<xsl:if test="@morerows"><xsl:attribute name="rowspan"><xsl:value-of select="@morerows+1"/></xsl:attribute></xsl:if>
<xsl:apply-templates/>
</th>
</xsl:template>
<xsl:template match="tbody">
<tbody><xsl:apply-templates/></tbody>
</xsl:template>
<xsl:template match="tbody/row/entry">
<td>
<xsl:if test="@morecols"><xsl:attribute name="colspan"><xsl:value-of select="@morecols+1"/></xsl:attribute></xsl:if>
<xsl:if test="@morerows"><xsl:attribute name="rowspan"><xsl:value-of select="@morerows+1"/></xsl:attribute></xsl:if>
<xsl:apply-templates/>
</td>
</xsl:template>
<xsl:template match="admonition">
<div class="admonition">
<div class="{@class}">
<p class="{title}">
<xsl:apply-templates select="./title"/>
</p>
<p class="body">
<xsl:apply-templates select="child::*[position()>1]"/>
</p>
</div>
</div>
</xsl:template>
<xsl:template match="attention|caution|danger|error|hint|important|note|tip|warning">
<div class="{name(.)}">
<p class="title"><xsl:value-of select="name(.)"/> :</p>
<p class="body">
<xsl:apply-templates/>
</p>
</div>
</xsl:template>
<xsl:template match="block_quote">
<blockquote>
<xsl:if test="./attribution">
<p><xsl:apply-templates select="child::*[position()=1]"/></p>
<p class="attribution">
<xsl:apply-templates select="./attribution"/>
</p>
</xsl:if>
<xsl:if test="not(./attribution)">
<xsl:apply-templates select="child::*"/>
</xsl:if>
</blockquote>
</xsl:template>
<xsl:template match="doctest_block">
<pre class="doctest_block">
<xsl:apply-templates/>
</pre>
</xsl:template>
<xsl:template match="line_block">
<div class="line_block">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="line">
<div class="line">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="sidebar">
<div class="sidebar">
<p class="title">
<xsl:apply-templates select="./title"/>
</p>
<xsl:if test="./subtitle">
<p class="subtitle">
<xsl:apply-templates select="./subtitle"/>
</p>
</xsl:if>
<xsl:choose>
<xsl:when test="./subtitle">
<xsl:apply-templates select="child::*[position()>2]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="child::*[position()>1]"/>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:template>
<xsl:template match="topic">
<div class="topic">
<p class="title">
<xsl:apply-templates select="./title"/>
</p>
<xsl:apply-templates select="child::*[position()>1]"/>
</div>
</xsl:template>
<xsl:template match="option_list">
<table class="option_list">
<col class="option" />
<col class="description" />
<tbody valign="top">
<xsl:apply-templates/>
</tbody>
</table>
</xsl:template>
<xsl:template match="option_list_item">
<tr>
<td class="option-group">
<kbd>
<xsl:apply-templates select="./option_group/option"/>
</kbd>
</td>
<td>
<xsl:apply-templates select="./description"/>
</td>
</tr>
</xsl:template>
<xsl:template match="option">
<span class="option">
<xsl:value-of select="option_string/text()"/>
<xsl:value-of select="./option_argument/@delimiter"/>
<xsl:apply-templates select="./option_argument"/>
</span>
</xsl:template>
<xsl:template match="option_argument">
<var>
<xsl:value-of select="text()"/>,
</var>
</xsl:template>
<xsl:template match="footnote">
<table class="footnote" frame="void" id="{@id}" rules="none">
<colgroup>
<col class="label"/>
<col/>
</colgroup>
<tbody valign="top">
<tr>
<td class="label">
<a class="backref" href="#{@backrefs}" name="{id}">
[<xsl:value-of select="label"/>]
</a>
</td>
<td>
<!--
| <xsl:value-of select="child::*[position()>1]"/>
+-->
<xsl:apply-templates select="child::*[position()>1]"/>
</td>
</tr>
</tbody>
</table>
</xsl:template>
<xsl:template match="footnote_reference">
<a class="footnote_reference" href="#{@refid}" id="{@id}" name="{@id}">
[<xsl:value-of select="text()"/>]
</a>
</xsl:template>
</xsl:stylesheet>

View File

@ -57,7 +57,8 @@ Status.values().each {
openDialog = fileChooser(fileSelectionMode: JFileChooser.DIRECTORIES_ONLY)
frame = application(title:'Personal Issue Tracker',
minimumSize: [800, 500],
minimumSize: [400, 200],
preferredSize: [800, 500],
pack:true,
locationRelativeTo: null,
iconImage: imageIcon('/icon64x64.png').image,

View File

@ -10,6 +10,8 @@ import java.awt.Point
import java.awt.event.MouseEvent
import javax.swing.JOptionPane
import javax.swing.JSplitPane
import javax.swing.JLabel
import javax.swing.JScrollPane
import javax.swing.JTable
import javax.swing.JTextField
import javax.swing.ListSelectionModel
@ -261,32 +263,60 @@ panel = splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT,
deleteIssueButton = button(deleteIssue,
constraints: gbc(gridx: 2, gridy: 1, anchor: GBC.EAST),
enabled: bind(source: issueTable.selectionModel, sourceEvent: 'valueChanged',
enabled: bind(source: issueTable.selectionModel,
sourceEvent: 'valueChanged',
sourceValue: { !issueTable.selectionModel.isSelectionEmpty() }))
}
scrollPane(constraints: "bottom") {
issueTextArea = textArea(
wrapStyleWord: true,
lineWrap: bind(source: wordWrapCheckBox,
sourceProperty: 'selected'),
editable: bind( source: issueTable.selectionModel, sourceEvent: 'valueChanged',
sourceValue: { !issueTable.selectionModel.isSelectionEmpty() }),
font: model.mainMVC.model.issueDetailFont,
focusGained: {},
focusLost: {
def issue = controller.getSelectedIssue()
if (issue == null) return
if (issueTextArea.text != issue.text)
issue.text = issueTextArea.text
},
mouseExited: {
def issue = controller.getSelectedIssue()
if (issue == null) return
if (issueTextArea.text != issue.text)
issue.text = issueTextArea.text
})
scrollPane(constraints: "bottom",
horizontalScrollBarPolicy: JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) {
issueTextPanel = panel {
issueTextPanelLayout = cardLayout()
issueTextArea = textArea(
constraints: "editor",
wrapStyleWord: true,
lineWrap: bind(source: wordWrapCheckBox,
sourceProperty: 'selected'),
editable: bind( source: issueTable.selectionModel,
sourceEvent: 'valueChanged',
sourceValue:
{ !issueTable.selectionModel.isSelectionEmpty() }),
font: model.mainMVC.model.issueDetailFont,
focusGained: {},
focusLost: {
def issue = controller.getSelectedIssue()
if (issue == null) return
if (issueTextArea.text != issue.text) {
issue.text = issueTextArea.text
issueTextDisplay.text = controller.rst2html(
issueTextArea.text)
}
issueTextPanelLayout.show(issueTextPanel, "display")
},
mouseExited: {
def issue = controller.getSelectedIssue()
if (issue == null) return
if (issueTextArea.text != issue.text) {
issue.text = issueTextArea.text
issueTextDisplay.text = controller.rst2html(
issueTextArea.text)
}
issueTextPanelLayout.show(issueTextPanel, "display")
})
issueTextDisplay = editorPane(contentType: "text/html",
constraints: "display",
editable: false,
preferredSize: [200, 200],
mouseClicked: { evt ->
if (evt.clickCount > 1)
issueTextPanelLayout.show(issueTextPanel, "editor")
})
}
issueTextPanelLayout.show(issueTextPanel, "display")
}
}
}