Copying directories
|
@ -0,0 +1,101 @@
|
|||
# Opening a JavaScript debugger within Oxygen [JS Operations](https://www.oxygenxml.com/doc/versions/23.1/ug-editor/topics/dg-default-author-operations.html#dg-default-author-operations__jsoperation)
|
||||
|
||||
Even though its authors state that this operation "[can be used to execute small pieces of Javascript code](https://github.com/oxygenxml/javascript-sample-operations)",
|
||||
all the Oxygen and Java public APIs are exposed and this operation can be used for fairly complex tasks.
|
||||
|
||||
The main limitation is then the lack of a debugger and relying on logs rapidly becomes very tedious to develop complex code.
|
||||
|
||||
The good news is that Oxygen uses Mozilla's [Rhino JavaScript engine](https://en.wikipedia.org/wiki/Rhino_(JavaScript_engine)) and that this engine comes with a
|
||||
[debugger](https://www-archive.mozilla.org/rhino/debugger.html).
|
||||
|
||||
This rambling proposes a way to launch the debugger from a JS Operation.
|
||||
|
||||
## Installation
|
||||
|
||||
To do so, add this function to your commons.js file:
|
||||
|
||||
```javascript
|
||||
startDebugger = function () {
|
||||
|
||||
var runnable = {
|
||||
run: function () {
|
||||
main.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var context = Packages.org.mozilla.javascript.Context.getCurrentContext(); // Within the current context...
|
||||
var contextFactory = context.getFactory();
|
||||
var scope = Packages.org.mozilla.javascript.tools.shell.Environment(runnable.__parent__); // and the scope of the runnable variable's parent ...
|
||||
var main = Packages.org.mozilla.javascript.tools. debugger.Main.mainEmbedded(contextFactory, scope, 'Debugger'); // start a debugging session ...
|
||||
main.setExitAction(java.lang.Runnable(runnable)); // , clean the resources at exit time...
|
||||
main.setVisible(true); // and make it visible/
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
And use it in the script parameter of the JS operation:
|
||||
```javascript
|
||||
doOperation = startDebugger;
|
||||
```
|
||||
|
||||
To call the action which uses this operation, you can attach it to a menu or tool icon or just define a shortkey and you're done !
|
||||
|
||||
Call this action and you'll pop up a nice vintage debugger :
|
||||
![debugger.jpg](debugger.jpg)
|
||||
|
||||
Use the File menu to open or run the file to debug (such as [test.js]):
|
||||
![debugger2.jpg](debugger2.jpg)
|
||||
|
||||
There are a few things to know before using it, though...
|
||||
|
||||
## Limitations
|
||||
|
||||
### Scope
|
||||
|
||||
I haven't found a way to simply grab the current scope object in JavaScript and the closest method I have found is:
|
||||
```javascript
|
||||
Packages.org.mozilla.javascript.tools.shell.Environment(runnable.__parent__)
|
||||
```
|
||||
|
||||
This creates a scope with everything in the runnable's parent's scope augmented with the environment variables with may not be present in the current scope.
|
||||
|
||||
|
||||
All the other methods I have tried gave scopes which were, on the contrary, reduced compared to the current scope and lacking, for instance, the authorAcces object
|
||||
without which you can't interact with Oxygen.
|
||||
|
||||
### Functions seen as objects
|
||||
|
||||
Just try it by yourself in the "evaluate" window:
|
||||
```javascript
|
||||
function a() {}
|
||||
|
||||
b = function() {}
|
||||
```
|
||||
"a" is seen as an object and not as a function, meaning that you can't call it. "b" is, correctly, seen as a function.
|
||||
|
||||
### Strict behavior on instanciation.
|
||||
|
||||
The word "new" is mandatory when instanciating a "class" (which is not the case when a JS operation script is run out of the debugger):
|
||||
|
||||
```javascript
|
||||
aclass = function () {
|
||||
this.setFoo(1);
|
||||
return this;
|
||||
}
|
||||
|
||||
aclass.prototype.setFoo = function (v) {
|
||||
this.foo = v;
|
||||
}
|
||||
|
||||
anObject = new aclass() ; // works as expected
|
||||
|
||||
anobject = aclass(); // fails with a message saying that there is no setFoo function in the object
|
||||
|
||||
```
|
||||
|
||||
# Framework
|
||||
|
||||
This directory includes the Oxygen sample framework on which ou can test the debugger but it should work on any framework.
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0"?>
|
||||
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
|
||||
<system
|
||||
systemId="http://www.oxygenxml.com/SDF/sdf.xsd"
|
||||
uri="schema/sdf.xsd"/>
|
||||
<system
|
||||
systemId="http://www.oxygenxml.com/SDF/abs.xsd"
|
||||
uri="schema/abs.xsd"/>
|
||||
</catalog>
|
|
@ -0,0 +1,17 @@
|
|||
startDebugger = function () {
|
||||
|
||||
var runnable = {
|
||||
run: function () {
|
||||
main.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var context = Packages.org.mozilla.javascript.Context.getCurrentContext(); // Within the current context...
|
||||
var contextFactory = context.getFactory();
|
||||
var scope = Packages.org.mozilla.javascript.tools.shell.Environment(runnable.__parent__); // and the scope of the runnable variable's parent ...
|
||||
var main = Packages.org.mozilla.javascript.tools. debugger.Main.mainEmbedded(contextFactory, scope, 'Debugger'); // start a debugging session ...
|
||||
main.setExitAction(java.lang.Runnable(runnable)); // , clean the resources at exit time...
|
||||
main.setVisible(true); // and make it visible/
|
||||
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/* Element from another namespace */
|
||||
@namespace abs "http://www.oxygenxml.com/sample/documentation/abstracts";
|
||||
|
||||
abs|def{
|
||||
font-family:monospace;
|
||||
font-size:smaller;
|
||||
}
|
||||
abs|def:before{
|
||||
content:"Definition:";
|
||||
color:gray;
|
||||
}
|
||||
|
||||
/* Vertical flow */
|
||||
book,
|
||||
section,
|
||||
para,
|
||||
title,
|
||||
image,
|
||||
ref {
|
||||
display:block;
|
||||
}
|
||||
|
||||
/* Horizontal flow */
|
||||
b,i {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
section{
|
||||
margin-left:1em;
|
||||
margin-top:1em;
|
||||
}
|
||||
|
||||
section{
|
||||
-oxy-foldable:true;
|
||||
-oxy-not-foldable-child: title;
|
||||
}
|
||||
|
||||
link[href]:before{
|
||||
display:inline;
|
||||
link:attr(href);
|
||||
content: "Click to open: " attr(href);
|
||||
}
|
||||
|
||||
link:before {
|
||||
content:oxy_link-text();
|
||||
}
|
||||
|
||||
/* Title rendering*/
|
||||
title{
|
||||
font-size: 2.4em;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
* * title{
|
||||
font-size: 2.0em;
|
||||
}
|
||||
* * * title{
|
||||
font-size: 1.6em;
|
||||
}
|
||||
* * * * title{
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
book,
|
||||
article{
|
||||
counter-reset:sect;
|
||||
}
|
||||
book > section,
|
||||
article > section{
|
||||
counter-increment:sect;
|
||||
}
|
||||
book > section > title:before,
|
||||
article > section > title:before{
|
||||
content: "Section: " counter(sect) " ";
|
||||
}
|
||||
|
||||
/* Inlines rendering*/
|
||||
b {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
i {
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
/*Table rendering */
|
||||
table{
|
||||
display:table;
|
||||
border:1px solid navy;
|
||||
margin:1em;
|
||||
max-width:1000px;
|
||||
min-width:150px;
|
||||
}
|
||||
|
||||
table[width]{
|
||||
width:attr(width, length);
|
||||
}
|
||||
|
||||
table[bgcolor],
|
||||
tr[bgcolor],
|
||||
td[bgcolor]{
|
||||
background-color:attr(bgcolor);
|
||||
color:inherit;
|
||||
}
|
||||
|
||||
tr, header{
|
||||
display:table-row;
|
||||
}
|
||||
|
||||
customcol {
|
||||
text-align:left;
|
||||
display:block;
|
||||
color:#444444;
|
||||
background-color:inherit;
|
||||
margin:0.2em;
|
||||
padding:2px;
|
||||
font-family:monospace;
|
||||
font-size:small;
|
||||
}
|
||||
|
||||
header{
|
||||
background-color: silver;
|
||||
color:inherit
|
||||
}
|
||||
|
||||
td{
|
||||
display:table-cell;
|
||||
border:1px solid navy;
|
||||
padding:1em;
|
||||
}
|
||||
|
||||
image{
|
||||
display:block;
|
||||
content: attr(href, url);
|
||||
margin-left:2em;
|
||||
}
|
||||
|
||||
author, name, country {
|
||||
display:block;
|
||||
margin-left:20px;
|
||||
}
|
||||
|
||||
name:before {
|
||||
content: oxy_label(text, "Author: ", width, 100px, color, gray);
|
||||
}
|
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 158 KiB |
After Width: | Height: | Size: 777 B |
After Width: | Height: | Size: 927 B |
After Width: | Height: | Size: 762 B |
After Width: | Height: | Size: 826 B |
After Width: | Height: | Size: 811 B |
After Width: | Height: | Size: 928 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 946 B |
After Width: | Height: | Size: 766 B |
After Width: | Height: | Size: 870 B |
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="21.1">
|
||||
<meta>
|
||||
<filters directoryPatterns="" filePatterns="\Qjs-debugger.xpr\E" positiveFilePatterns="" showHiddenFiles="false"/>
|
||||
<options/>
|
||||
</meta>
|
||||
<projectTree name="js-debugger.xpr">
|
||||
<folder path="."/>
|
||||
</projectTree>
|
||||
</project>
|
|
@ -0,0 +1 @@
|
|||
France, Spain, Great Britain
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace=
|
||||
"http://www.oxygenxml.com/sample/documentation/abstracts">
|
||||
<xs:element name="def" type="xs:string"/>
|
||||
</xs:schema>
|
|
@ -0,0 +1,112 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.oxygenxml.com/sample/documentation"
|
||||
xmlns:doc="http://www.oxygenxml.com/sample/documentation"
|
||||
xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts"
|
||||
elementFormDefault="qualified">
|
||||
<xs:import namespace="http://www.oxygenxml.com/sample/documentation/abstracts"
|
||||
schemaLocation="abs.xsd"/>
|
||||
<xs:element name="book" type="doc:articleType"/>
|
||||
<xs:element name="article" type="doc:articleType"/>
|
||||
<xs:element name="section" type="doc:sectionType"/>
|
||||
<xs:complexType name="sectionType">
|
||||
<xs:complexContent>
|
||||
<xs:restriction base="doc:articleType">
|
||||
<xs:sequence>
|
||||
<xs:element name="title" type="xs:string"/>
|
||||
<xs:element ref="abs:def" minOccurs="0"/>
|
||||
<xs:choice>
|
||||
<xs:sequence>
|
||||
<xs:element ref="doc:section" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element ref="doc:para"/>
|
||||
<xs:element ref="doc:ref"/>
|
||||
<xs:element ref="doc:image"/>
|
||||
<xs:element ref="doc:table"/>
|
||||
</xs:choice>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:restriction>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="para" type="doc:paragraphType"/>
|
||||
<xs:complexType name="paragraphType" mixed="true">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="b"/>
|
||||
<xs:element name="i"/>
|
||||
<xs:element name="link"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:element name="ref">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="location" type="xs:anyURI" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="image">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="href" type="xs:anyURI" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="table">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element form="qualified" minOccurs="0" name="title" type="xs:string"/>
|
||||
<xs:element name="customcol" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="width" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="header" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="td" maxOccurs="unbounded" type="doc:paragraphType"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="tr" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="td" type="doc:tdType" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="width" type="xs:string"/>
|
||||
<xs:attribute name="bgcolor" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:complexType name="tdType">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="doc:paragraphType">
|
||||
<xs:attribute name="row_span" type="xs:integer"/>
|
||||
<xs:attribute name="column_span" type="xs:integer"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="articleType">
|
||||
<xs:sequence>
|
||||
<xs:element name="title" type="xs:string"/>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="author">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="name"/>
|
||||
<xs:element name="country"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element ref="abs:def" minOccurs="0"/>
|
||||
<xs:choice>
|
||||
<xs:sequence>
|
||||
<xs:element ref="doc:section" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element ref="doc:para"/>
|
||||
<xs:element ref="doc:ref"/>
|
||||
<xs:element ref="doc:image"/>
|
||||
<xs:element ref="doc:table"/>
|
||||
</xs:choice>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<book xmlns="http://www.oxygenxml.com/sample/documentation"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts">
|
||||
<title>My Technical Book</title>
|
||||
<author>
|
||||
<name>Jean</name>
|
||||
<country>France</country>
|
||||
</author>
|
||||
<section>
|
||||
<title>XML</title>
|
||||
<abs:def>Extensible Markup Language</abs:def>
|
||||
<para>In this section of the book I will explain different XML applications.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Accessing XML data.</title>
|
||||
<section>
|
||||
<title>XSLT</title>
|
||||
<abs:def>Extensible stylesheet language transformation (XSLT) is a language for
|
||||
transforming XML documents into other XML documents.</abs:def>
|
||||
<para>A list of XSL elements and what they do..</para>
|
||||
<table>
|
||||
<header>
|
||||
<td>XSLT Elements</td>
|
||||
<td>Description</td>
|
||||
</header>
|
||||
<tr>
|
||||
<td>
|
||||
<b>xsl:stylesheet</b>
|
||||
</td>
|
||||
<td>The <i>xsl:stylesheet</i> element is always the top-level element of an XSL
|
||||
stylesheet. The name <i>xsl:transform</i> may be used as a synonym.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<b>xsl:template</b>
|
||||
</td>
|
||||
<td>The <i>xsl:template</i> element has an optional mode attribute. If this is
|
||||
present, the template will only be matched when the same mode is used in the
|
||||
invoking <i>xsl:apply-templates</i> element.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<b>for-each</b>
|
||||
</td>
|
||||
<td>The xsl:for-each element causes iteration over the nodes selected by a
|
||||
node-set expression.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td column_span="2">End of the list</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
<section>
|
||||
<title>XPath</title>
|
||||
<abs:def>XPath (XML Path Language) is a terse (non-XML) syntax for addressing portions
|
||||
of an XML document. </abs:def>
|
||||
<para>Some of the XPath functions.</para>
|
||||
<table>
|
||||
<header>
|
||||
<td>Function</td>
|
||||
<td>Description</td>
|
||||
</header>
|
||||
<tr>
|
||||
<td>format-number</td>
|
||||
<td>The <i>format-number</i> function converts its first argument to a string
|
||||
using the format pattern string specified by the second argument and the
|
||||
decimal-format named by the third argument, or the default decimal-format,
|
||||
if there is no third argument</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>current</td>
|
||||
<td>The <i>current</i> function returns a node-set that has the current node as
|
||||
its only member.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>generate-id</td>
|
||||
<td>The <i>generate-id</i> function returns a string that uniquely identifies
|
||||
the node in the argument node-set that is first in document order.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>Documentation frameworks</title>
|
||||
<para>One of the most important documentation frameworks is Docbook.</para>
|
||||
<image href="http://www.xmlhack.com/images/docbook.gif"/>
|
||||
<para>The other is the topic oriented DITA, promoted by OASIS.</para>
|
||||
<image href="http://www.oasis-open.org/images/standards/oasis_standard.jpg"/>
|
||||
</section>
|
||||
</book>
|
|
@ -0,0 +1,16 @@
|
|||
package simple.documentation.framework;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
|
||||
/**
|
||||
* Provides access to Author functions, to specific components corresponding to
|
||||
* editor, document, workspace, tables, change tracking and utility informations and actions.
|
||||
*/
|
||||
public interface AuthorAccessProvider {
|
||||
/**
|
||||
* Gets access to Author functions and components.
|
||||
*
|
||||
* @return The Author access.
|
||||
*/
|
||||
AuthorAccess getAuthorAccess();
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package simple.documentation.framework;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
|
||||
import ro.sync.ecss.extensions.api.DocumentTypeCustomRuleMatcher;
|
||||
|
||||
/**
|
||||
* Matching rule to the SDF document type.
|
||||
*
|
||||
*/
|
||||
public class CustomRule implements DocumentTypeCustomRuleMatcher {
|
||||
|
||||
/**
|
||||
* Check if the SDF document type should be used for the given document properties.
|
||||
*/
|
||||
public boolean matches(
|
||||
String systemID,
|
||||
String rootNamespace,
|
||||
String rootLocalName,
|
||||
String doctypePublicID,
|
||||
Attributes rootAttributes) {
|
||||
boolean matches = true;
|
||||
int attributesCount = rootAttributes.getLength();
|
||||
for (int i = 0; i < attributesCount; i++) {
|
||||
String localName = rootAttributes.getLocalName(i);
|
||||
if ("version".equals(localName)) {
|
||||
if ("2.0".equals(rootAttributes.getValue(i))) {
|
||||
// Do not match the documents with "2.0" version
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Checks if the current Document Type Association"
|
||||
+ " is matching the document.";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,552 @@
|
|||
package simple.documentation.framework;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ro.sync.contentcompletion.xml.CIAttribute;
|
||||
import ro.sync.contentcompletion.xml.CIElement;
|
||||
import ro.sync.contentcompletion.xml.CIElementAdapter;
|
||||
|
||||
/**
|
||||
* Simple Documentation Framework element.
|
||||
*/
|
||||
public class SDFElement extends CIElementAdapter {
|
||||
/**
|
||||
* The namespace.
|
||||
*/
|
||||
protected String namespace = null;
|
||||
|
||||
/**
|
||||
* The element name.
|
||||
*/
|
||||
protected String name = null;
|
||||
|
||||
/**
|
||||
* The proxy.
|
||||
*/
|
||||
protected String proxy = null;
|
||||
|
||||
/**
|
||||
* The type description.
|
||||
*/
|
||||
private String typeDescription = null;
|
||||
|
||||
/**
|
||||
* List of attributes.
|
||||
*/
|
||||
protected List<CIAttribute> attributes = null;
|
||||
|
||||
/**
|
||||
* The possible values as <code>String</code> list.
|
||||
*/
|
||||
private List<String> possiblesValuesList;
|
||||
|
||||
/**
|
||||
* The element model description.
|
||||
*/
|
||||
private String modelDescription;
|
||||
|
||||
/**
|
||||
* The facets. One facet can be null if it is not defined.
|
||||
*/
|
||||
private String lengthFacetValue;
|
||||
|
||||
/**
|
||||
* The content type of the element.
|
||||
*/
|
||||
private int contentType = CONTENT_TYPE_NOT_DETERMINED;
|
||||
|
||||
/**
|
||||
* True if content is nillable. Used only for XML Schema.
|
||||
*/
|
||||
private boolean nillable;
|
||||
|
||||
/**
|
||||
* Facet values.
|
||||
*/
|
||||
private String minLengthFacetValue;
|
||||
private String maxLengthFacetValue;
|
||||
private String whitespaceFacetValue;
|
||||
private String minInclusiveFacetValue;
|
||||
private String minExclusiveFacetValue;
|
||||
private String maxInclusiveFacetValue;
|
||||
private String maxExclusiveFacetValue;
|
||||
private String totalDigitsFacetValue;
|
||||
private String fractionDigitsFacetValue;
|
||||
private String facetPatternValue;
|
||||
|
||||
/**
|
||||
* Guess some following elements if possible
|
||||
*/
|
||||
protected List<CIElement> guessElements = null;
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#getGuessElements()
|
||||
*/
|
||||
@Override
|
||||
public List<CIElement> getGuessElements() {
|
||||
if (guessElements != null && guessElements.isEmpty()) {
|
||||
// Return null is the list is empty.
|
||||
return null;
|
||||
}
|
||||
return guessElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#addGuessElement(ro.sync.contentcompletion.xml.CIElement)
|
||||
*/
|
||||
@Override
|
||||
public void addGuessElement(CIElement e) {
|
||||
if (guessElements == null) {
|
||||
guessElements = new ArrayList<CIElement>();
|
||||
}
|
||||
guessElements.add(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string representing the name or null.
|
||||
*/
|
||||
@Override
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#getNamespace()
|
||||
*/
|
||||
@Override
|
||||
public String getNamespace(){
|
||||
return namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the element has a namespace.
|
||||
*/
|
||||
private boolean xmlns;
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setDeclareXmlns(boolean)
|
||||
*/
|
||||
@Override
|
||||
public void setDeclareXmlns(boolean xmlns) {
|
||||
this.xmlns = xmlns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setContentType(int)
|
||||
*/
|
||||
@Override
|
||||
public void setContentType(int contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#isEmpty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getContentType() == CONTENT_TYPE_EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#getContentType()
|
||||
*/
|
||||
@Override
|
||||
public int getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#isDeclareXmlns()
|
||||
*/
|
||||
@Override
|
||||
public boolean isDeclareXmlns() {
|
||||
return xmlns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setName(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setPrefix(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setPrefix(String proxy) {
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setNamespace(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#getQName()
|
||||
*/
|
||||
@Override
|
||||
public String getQName() {
|
||||
if (getPrefix() != null && !"".equals(getPrefix())) {
|
||||
return getPrefix() + ":" + getName();
|
||||
} else {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
boolean result = false;
|
||||
if (o instanceof SDFElement) {
|
||||
CIElement cie = (CIElement) o;
|
||||
result = compareTo(cie) == 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#getAttributes()
|
||||
*/
|
||||
@Override
|
||||
public List<CIAttribute> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setAttributes(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public void setAttributes(List<CIAttribute> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the name and the namespace and compares it with the other name and namespace,
|
||||
* as strings.
|
||||
* @param other The object to compare to.
|
||||
* @return The value <code>0</code> if the argument object is equal to this object.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(CIElement other){
|
||||
String n1 = getName() == null ? "": getName();
|
||||
String nm1 = getNamespace() == null ? "": getNamespace();
|
||||
|
||||
String n2 = other.getName() == null ? "": other.getName();
|
||||
String nm2 = other.getNamespace() == null ? "": other.getNamespace();
|
||||
|
||||
int result = n1.compareTo(n2);
|
||||
if(result == 0) {
|
||||
result = nm1.compareTo(nm2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name.
|
||||
*
|
||||
* @return The name.
|
||||
*/
|
||||
@Override
|
||||
public String toString(){
|
||||
String toRet = String.valueOf(getName());
|
||||
return toRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#hasPrefix()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasPrefix() {
|
||||
return getPrefix() != null && !"".equals(getPrefix());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#getPrefix()
|
||||
*/
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param modelDescription The modelDescription to set.
|
||||
*/
|
||||
@Override
|
||||
public void setModelDescription(String modelDescription) {
|
||||
this.modelDescription = modelDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fractionDigitsFacetValue The fractionDigitsFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetFractionDigitsValue(String fractionDigitsFacetValue) {
|
||||
this.fractionDigitsFacetValue = fractionDigitsFacetValue;
|
||||
}
|
||||
/**
|
||||
* @param maxExclusiveFacetValue The maxExclusiveFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetMaxExclusiveValue(String maxExclusiveFacetValue) {
|
||||
this.maxExclusiveFacetValue = maxExclusiveFacetValue;
|
||||
}
|
||||
/**
|
||||
* @param maxInclusiveFacetValue The maxInclusiveFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetMaxInclusiveValue(String maxInclusiveFacetValue) {
|
||||
this.maxInclusiveFacetValue = maxInclusiveFacetValue;
|
||||
}
|
||||
/**
|
||||
* @param maxLengthFacetValue The maxLengthFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetMaxLengthValue(String maxLengthFacetValue) {
|
||||
this.maxLengthFacetValue = maxLengthFacetValue;
|
||||
}
|
||||
/**
|
||||
* @param minInclusiveFacetValue The minInclusiveFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetMinInclusiveValue(String minInclusiveFacetValue) {
|
||||
this.minInclusiveFacetValue = minInclusiveFacetValue;
|
||||
}
|
||||
/**
|
||||
* @param possiblesValuesList The possiblesValuesList to set.
|
||||
*/
|
||||
@Override
|
||||
public void setPossiblesValues(List<String> possiblesValuesList) {
|
||||
this.possiblesValuesList = possiblesValuesList;
|
||||
}
|
||||
/**
|
||||
* @param totalDigitsFacetValue The totalDigitsFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetTotalDigitsValue(String totalDigitsFacetValue) {
|
||||
this.totalDigitsFacetValue = totalDigitsFacetValue;
|
||||
}
|
||||
/**
|
||||
* @param whitespaceFacetValue The whitespaceFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetWhitespaceValue(String whitespaceFacetValue) {
|
||||
this.whitespaceFacetValue = whitespaceFacetValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lengthFacetValue The lengthFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetLengthValue(String lengthFacetValue) {
|
||||
this.lengthFacetValue = lengthFacetValue;
|
||||
}
|
||||
/**
|
||||
* @param minLengthFacetValue The minLengthFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetMinLengthValue(String minLengthFacetValue) {
|
||||
this.minLengthFacetValue = minLengthFacetValue;
|
||||
}
|
||||
/**
|
||||
* @param minExclusiveFacetValue The minExclusiveFacetValue to set.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetMinExclusiveValue(String minExclusiveFacetValue) {
|
||||
this.minExclusiveFacetValue = minExclusiveFacetValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.NodeDescription#getFacetFractionDigitsValue()
|
||||
*/
|
||||
@Override
|
||||
public String getFacetFractionDigitsValue() {
|
||||
return fractionDigitsFacetValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.NodeDescription#getFacetTotalDigitsValue()
|
||||
*/
|
||||
@Override
|
||||
public String getFacetTotalDigitsValue() {
|
||||
return totalDigitsFacetValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.NodeDescription#getFacetMaxInclusiveValue()
|
||||
*/
|
||||
@Override
|
||||
public String getFacetMaxInclusiveValue() {
|
||||
return maxInclusiveFacetValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.NodeDescription#getFacetMaxExclusiveValue()
|
||||
*/
|
||||
@Override
|
||||
public String getFacetMaxExclusiveValue() {
|
||||
return maxExclusiveFacetValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the value of MIN_INCLUSIVE facet, can be null if it is not defined.
|
||||
*
|
||||
* @return The value of MIN_INCLUSIVE facet.
|
||||
*/
|
||||
@Override
|
||||
public String getFacetMinInclusiveValue() {
|
||||
return minInclusiveFacetValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of MIN_EXCLUSIVE facet, can be null if it is not defined.
|
||||
*
|
||||
* @return The value of MIN_EXCLUSIVE facet.
|
||||
*/
|
||||
@Override
|
||||
public String getFacetMinExclusiveValue() {
|
||||
return minExclusiveFacetValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the possible values as <code>String</code> list.
|
||||
*
|
||||
* @return The possible values.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPossibleValues() {
|
||||
return possiblesValuesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model description.
|
||||
* @return The model description.
|
||||
*/
|
||||
@Override
|
||||
public String getModelDescription() {
|
||||
return modelDescription;
|
||||
}
|
||||
/**
|
||||
* Get the value of LENGTH facet, can be null if it is not defined.
|
||||
*
|
||||
* @return The value of length facet.
|
||||
*/
|
||||
@Override
|
||||
public String getFacetLengthValue() {
|
||||
return lengthFacetValue;
|
||||
}
|
||||
/**
|
||||
* Get the value of MIN LENGTH facet, can be null if it is not defined.
|
||||
*
|
||||
* @return The value of MIN LENGTH facet.
|
||||
*/
|
||||
@Override
|
||||
public String getFacetMinLengthValue() {
|
||||
return minLengthFacetValue;
|
||||
}
|
||||
/**
|
||||
* Get the value of MAX LENGTH facet, can be null if it is not defined.
|
||||
*
|
||||
* @return The value of MAX LENGTH facet.
|
||||
*/
|
||||
@Override
|
||||
public String getFacetMaxLengthValue() {
|
||||
return maxLengthFacetValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of WHITESPACE facet, can be null if it is not defined.
|
||||
*
|
||||
* @return The value of WHITESPACE facet.
|
||||
*/
|
||||
@Override
|
||||
public String getFacetWhitespaceValue() {
|
||||
return whitespaceFacetValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list with pattern facets.
|
||||
*
|
||||
* @return The list with pattern facets, can be null if no FACET_PATTERN defined.
|
||||
*/
|
||||
@Override
|
||||
public String getFacetPattern(){
|
||||
return facetPatternValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list with pattern facets.
|
||||
*
|
||||
* @param patternFacets The list with pattern facets.
|
||||
*/
|
||||
@Override
|
||||
public void setFacetPattern(String patternFacets){
|
||||
this.facetPatternValue = patternFacets;
|
||||
}
|
||||
|
||||
/**
|
||||
* The annotation string, null as default.
|
||||
*/
|
||||
protected String annotation;
|
||||
|
||||
/**
|
||||
* Get the annotation for the element.
|
||||
*
|
||||
* @return A text that explain how to use the attribute, or null.
|
||||
*/
|
||||
@Override
|
||||
public String getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setAnnotation(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAnnotation(String annotation){
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#getTypeDescription()
|
||||
*/
|
||||
@Override
|
||||
public String getTypeDescription() {
|
||||
return typeDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setTypeDescription(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setTypeDescription(String typeDescription) {
|
||||
this.typeDescription = typeDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#setNillable(boolean)
|
||||
*/
|
||||
@Override
|
||||
public void setNillable(boolean nillable) {
|
||||
this.nillable = nillable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.contentcompletion.xml.CIElement#isNillable()
|
||||
*/
|
||||
@Override
|
||||
public boolean isNillable() {
|
||||
return nillable;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
package simple.documentation.framework;
|
||||
|
||||
import ro.sync.contentcompletion.xml.SchemaManagerFilter;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorExtensionStateListener;
|
||||
import ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler;
|
||||
import ro.sync.ecss.extensions.api.AuthorReferenceResolver;
|
||||
import ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler;
|
||||
import ro.sync.ecss.extensions.api.AuthorTableCellSpanProvider;
|
||||
import ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider;
|
||||
import ro.sync.ecss.extensions.api.CustomAttributeValueEditor;
|
||||
import ro.sync.ecss.extensions.api.ExtensionsBundle;
|
||||
import ro.sync.ecss.extensions.api.StylesFilter;
|
||||
import ro.sync.ecss.extensions.api.link.ElementLocatorProvider;
|
||||
import ro.sync.ecss.extensions.api.link.LinkTextResolver;
|
||||
import ro.sync.ecss.extensions.api.structure.AuthorBreadCrumbCustomizer;
|
||||
import ro.sync.ecss.extensions.api.structure.AuthorOutlineCustomizer;
|
||||
import ro.sync.ecss.extensions.commons.DefaultElementLocatorProvider;
|
||||
import simple.documentation.framework.extensions.SDFAttributesValueEditor;
|
||||
import simple.documentation.framework.extensions.SDFAuthorBreadCrumbCustomizer;
|
||||
import simple.documentation.framework.extensions.SDFAuthorExtensionStateListener;
|
||||
import simple.documentation.framework.extensions.SDFAuthorOutlineCustomizer;
|
||||
import simple.documentation.framework.extensions.SDFExternalObjectInsertionHandler;
|
||||
import simple.documentation.framework.extensions.SDFReferencesResolver;
|
||||
import simple.documentation.framework.extensions.SDFSchemaAwareEditingHandler;
|
||||
import simple.documentation.framework.extensions.SDFSchemaManagerFilter;
|
||||
import simple.documentation.framework.extensions.SDFStylesFilter;
|
||||
import simple.documentation.framework.extensions.TableCellSpanProvider;
|
||||
import simple.documentation.framework.extensions.TableColumnWidthProvider;
|
||||
|
||||
/**
|
||||
* Simple Document Framework extension bundle.
|
||||
*
|
||||
*/
|
||||
public class SDFExtensionsBundle extends ExtensionsBundle {
|
||||
/**
|
||||
* Simple documentation framework state listener.
|
||||
*/
|
||||
private SDFAuthorExtensionStateListener sdfAuthorExtensionStateListener;
|
||||
|
||||
/**
|
||||
* Editor for attributes values.
|
||||
*/
|
||||
@Override
|
||||
public CustomAttributeValueEditor createCustomAttributeValueEditor(boolean forEclipsePlugin) {
|
||||
return new SDFAttributesValueEditor(new AuthorAccessProvider() {
|
||||
|
||||
public AuthorAccess getAuthorAccess() {
|
||||
AuthorAccess access = null;
|
||||
if (sdfAuthorExtensionStateListener != null) {
|
||||
// Get the Author access.
|
||||
access = sdfAuthorExtensionStateListener.getAuthorAccess();
|
||||
}
|
||||
return access;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple documentation framework state listener.
|
||||
*/
|
||||
@Override
|
||||
public AuthorExtensionStateListener createAuthorExtensionStateListener() {
|
||||
sdfAuthorExtensionStateListener = new SDFAuthorExtensionStateListener();
|
||||
return sdfAuthorExtensionStateListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter for content completion proposals from the schema manager.
|
||||
*/
|
||||
@Override
|
||||
public SchemaManagerFilter createSchemaManagerFilter() {
|
||||
return new SDFSchemaManagerFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default element locator.
|
||||
*/
|
||||
@Override
|
||||
public ElementLocatorProvider createElementLocatorProvider() {
|
||||
return new DefaultElementLocatorProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand content references.
|
||||
*/
|
||||
@Override
|
||||
public AuthorReferenceResolver createAuthorReferenceResolver() {
|
||||
return new SDFReferencesResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS styles filtering.
|
||||
*/
|
||||
@Override
|
||||
public StylesFilter createAuthorStylesFilter() {
|
||||
return new SDFStylesFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for table cell span informations.
|
||||
*/
|
||||
@Override
|
||||
public AuthorTableCellSpanProvider createAuthorTableCellSpanProvider() {
|
||||
return new TableCellSpanProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Table column width provider responsible of handling modifications regarding
|
||||
* table width and column widths.
|
||||
*/
|
||||
@Override
|
||||
public AuthorTableColumnWidthProvider createAuthorTableColumnWidthProvider() {
|
||||
return new TableColumnWidthProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Editing support for SDF documents responsible of handling typing and paste events inside section and tables.
|
||||
*/
|
||||
@Override
|
||||
public AuthorSchemaAwareEditingHandler getAuthorSchemaAwareEditingHandler() {
|
||||
return new SDFSchemaAwareEditingHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Author Outline customizer used for custom filtering and nodes rendering in the Outline.
|
||||
*/
|
||||
@Override
|
||||
public AuthorOutlineCustomizer createAuthorOutlineCustomizer() {
|
||||
return new SDFAuthorOutlineCustomizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Document Framework Author customizer used for custom nodes rendering
|
||||
* in the Breadcrumb.
|
||||
*/
|
||||
@Override
|
||||
public AuthorBreadCrumbCustomizer createAuthorBreadCrumbCustomizer() {
|
||||
return new SDFAuthorBreadCrumbCustomizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createExternalObjectInsertionHandler()
|
||||
*/
|
||||
@Override
|
||||
public AuthorExternalObjectInsertionHandler createExternalObjectInsertionHandler() {
|
||||
return new SDFExternalObjectInsertionHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* The unique identifier of the Document Type.
|
||||
* This identifier will be used to store custom SDF options.
|
||||
*/
|
||||
@Override
|
||||
public String getDocumentTypeID() {
|
||||
return "Simple.Document.Framework.document.type";
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "A custom extensions bundle used for the Simple Document Framework";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.ExtensionsBundle#createLinkTextResolver()
|
||||
*/
|
||||
@Override
|
||||
public LinkTextResolver createLinkTextResolver() {
|
||||
return new SDFLinkTextResolver();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package simple.documentation.framework;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.URIResolver;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.link.InvalidLinkException;
|
||||
import ro.sync.ecss.extensions.api.link.LinkTextResolver;
|
||||
import ro.sync.ecss.extensions.api.node.AttrValue;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
|
||||
/**
|
||||
* Resolve a link and obtains a text representation. The content of the link represent the name and
|
||||
* the absolute location of the referred file.
|
||||
*/
|
||||
public class SDFLinkTextResolver extends LinkTextResolver {
|
||||
|
||||
/**
|
||||
* The author access.
|
||||
*/
|
||||
private AuthorAccess authorAccess;
|
||||
|
||||
/**
|
||||
* Logger for logging.
|
||||
*/
|
||||
private static final Logger logger = LogManager.getLogger(SDFLinkTextResolver.class.getName());
|
||||
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.link.LinkTextResolver#resolveReference(ro.sync.ecss.extensions.api.node.AuthorNode)
|
||||
*/
|
||||
@Override
|
||||
public String resolveReference(AuthorNode node) throws InvalidLinkException {
|
||||
String linkText = null;
|
||||
if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT
|
||||
&& "link".equals(ro.sync.basic.xml.BasicXmlUtil.getLocalName(node.getName()))) {
|
||||
AuthorElement element = (AuthorElement) node;
|
||||
AuthorElement[] authorElements = element.getElementsByLocalName("ref");
|
||||
|
||||
if (authorElements != null && authorElements.length > 0) {
|
||||
// Get the first 'ref' element from link.
|
||||
AuthorElement refElem = authorElements[0];
|
||||
AttrValue locationAttribute = refElem.getAttribute("location");
|
||||
String locationVal = locationAttribute.getValue();
|
||||
URIResolver uriResolver = authorAccess.getXMLUtilAccess().getURIResolver();
|
||||
try {
|
||||
Source resolve = uriResolver.resolve(locationVal, authorAccess.getEditorAccess().getEditorLocation().toString());
|
||||
String systemId = resolve.getSystemId();
|
||||
linkText = "[" + locationVal + "] - " + systemId;
|
||||
} catch (TransformerException e) {
|
||||
logger.warn(e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return linkText;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.link.LinkTextResolver#activated(ro.sync.ecss.extensions.api.AuthorAccess)
|
||||
*/
|
||||
@Override
|
||||
public void activated(AuthorAccess authorAccess) {
|
||||
this.authorAccess = authorAccess;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package simple.documentation.framework.callouts;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorDocumentController;
|
||||
import ro.sync.ecss.extensions.api.callouts.AuthorCalloutRenderingInformation;
|
||||
import ro.sync.ecss.extensions.api.callouts.CalloutsRenderingInformationProvider;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
import ro.sync.exml.view.graphics.Color;
|
||||
|
||||
|
||||
/**
|
||||
* Callouts rendering provider.
|
||||
*
|
||||
* @author marius
|
||||
*/
|
||||
public class CalloutsRenderingProvider extends CalloutsRenderingInformationProvider {
|
||||
/**
|
||||
* The author access.
|
||||
*/
|
||||
private final AuthorAccess authorAccess;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param authorAccess The author access.
|
||||
*/
|
||||
public CalloutsRenderingProvider(AuthorAccess authorAccess) {
|
||||
this.authorAccess = authorAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderAsCallout(AuthorPersistentHighlight persistentHighlight) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorCalloutRenderingInformation getCalloutRenderingInformation(
|
||||
final AuthorPersistentHighlight hl) {
|
||||
return new AuthorCalloutRenderingInformation() {
|
||||
|
||||
@Override
|
||||
public long getTimestamp() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentFromTarget(int arg0) {
|
||||
try {
|
||||
AuthorDocumentController documentController = authorAccess.getDocumentController();
|
||||
AuthorNode nodeAtOffset = documentController.getNodeAtOffset(hl.getEndOffset());
|
||||
int startOffset = hl.getStartOffset() - nodeAtOffset.getStartOffset();
|
||||
int endOffset = hl.getEndOffset() - nodeAtOffset.getEndOffset();
|
||||
String textContent = nodeAtOffset.getTextContent();
|
||||
return textContent.substring(startOffset, endOffset);
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment(int arg0) {
|
||||
return hl.getClonedProperties().get(AuthorPersistentHighlight.COMMENT_ATTRIBUTE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor() {
|
||||
return Color.COLOR_DARK_YELLOW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCalloutType() {
|
||||
return "Paragraph review";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthor() {
|
||||
return "para_reviewer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAdditionalData() {
|
||||
LinkedHashMap<String,String> clonedProperties = hl.getClonedProperties();
|
||||
clonedProperties.put("Level", "Superior");
|
||||
return clonedProperties;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package simple.documentation.framework.callouts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlightActionsProvider;
|
||||
import ro.sync.exml.workspace.api.editor.page.author.actions.AuthorActionsProvider;
|
||||
|
||||
/**
|
||||
* Provides actions that are shown on the contextual menu of the callout
|
||||
*/
|
||||
public class SDFAuthorPersistentHighlightActionsProvider implements
|
||||
AuthorPersistentHighlightActionsProvider {
|
||||
/**
|
||||
* The list with contextual actions for a custom callout.
|
||||
*/
|
||||
private List<AbstractAction> actions = new ArrayList<AbstractAction>();
|
||||
/**
|
||||
* The default action.
|
||||
*/
|
||||
private AbstractAction defaultAction;
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param authorAccess The author access.
|
||||
*/
|
||||
public SDFAuthorPersistentHighlightActionsProvider(AuthorAccess authorAccess) {
|
||||
AuthorActionsProvider actionsProvider = authorAccess.getEditorAccess().getActionsProvider();
|
||||
Map<String, Object> authorCommonActions = actionsProvider.getAuthorCommonActions();
|
||||
Set<String> keySet = authorCommonActions.keySet();
|
||||
for (String key : keySet) {
|
||||
if (key != null) {
|
||||
if ("Edit/Add_Comment".equals(key) ||
|
||||
"Edit/Edit_Comment".equals(key) ||
|
||||
"Edit/Remove_Comments".equals(key)) {
|
||||
actions.add((AbstractAction) authorCommonActions.get(key));
|
||||
if ("Edit/Edit_Comment".equals(key)) {
|
||||
defaultAction = (AbstractAction) authorCommonActions.get(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlightActionsProvider#getDefaultAction(ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight)
|
||||
*/
|
||||
public AbstractAction getDefaultAction(AuthorPersistentHighlight hl) {
|
||||
return defaultAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlightActionsProvider#getActions(ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight)
|
||||
*/
|
||||
public List<AbstractAction> getActions(AuthorPersistentHighlight hl) {
|
||||
return actions;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.CustomAttributeValueEditor;
|
||||
import ro.sync.ecss.extensions.api.EditedAttribute;
|
||||
import simple.documentation.framework.AuthorAccessProvider;
|
||||
|
||||
/**
|
||||
* Simple Document Framework attributes value editor.
|
||||
*/
|
||||
public class SDFAttributesValueEditor extends CustomAttributeValueEditor {
|
||||
/**
|
||||
* Provides access to Author functions and components.
|
||||
*/
|
||||
private final AuthorAccessProvider authorAccessProvider;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param authorAccessProvider Provides access to Author functions and components.
|
||||
*/
|
||||
public SDFAttributesValueEditor(AuthorAccessProvider authorAccessProvider) {
|
||||
this.authorAccessProvider = authorAccessProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for the current attribute.
|
||||
*
|
||||
* @param attr The Edited attribute information.
|
||||
* @param parentComponent The parent component/composite.
|
||||
*/
|
||||
public String getAttributeValue(EditedAttribute attr, Object parentComponent) {
|
||||
String refValue = null;
|
||||
AuthorAccess authorAccess = authorAccessProvider.getAuthorAccess();
|
||||
if (authorAccess != null) {
|
||||
File refFile = authorAccess.getWorkspaceAccess().chooseFile(
|
||||
// Title
|
||||
"Choose reference file",
|
||||
// Extensions
|
||||
null,
|
||||
// Filter description
|
||||
null,
|
||||
// Open for save
|
||||
false);
|
||||
if (refFile != null) {
|
||||
refValue = refFile.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
return refValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of the attribute value editor.
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Reference attribute value editor";
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the attributes that it handles.
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldHandleAttribute(EditedAttribute attribute) {
|
||||
return attribute.getAttributeQName().endsWith("ref");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
import ro.sync.ecss.extensions.api.structure.AuthorBreadCrumbCustomizer;
|
||||
import ro.sync.ecss.extensions.api.structure.RenderingInformation;
|
||||
|
||||
/**
|
||||
* Simple Document Framework Author customizer used for custom nodes rendering
|
||||
* in the Breadcrumb.
|
||||
*/
|
||||
public class SDFAuthorBreadCrumbCustomizer extends AuthorBreadCrumbCustomizer {
|
||||
|
||||
/**
|
||||
* For every node render its name in the Outline (with the first letter capitalized).
|
||||
* Set 'Paragraph' as render for 'para' nodes
|
||||
*/
|
||||
@Override
|
||||
public void customizeRenderingInformation(RenderingInformation renderInfo) {
|
||||
// Get the node to be rendered
|
||||
AuthorNode node = renderInfo.getNode();
|
||||
|
||||
String name = node.getName();
|
||||
if (name.length() > 0) {
|
||||
if ("para".equals(name)) {
|
||||
name = "Paragraph";
|
||||
} else {
|
||||
if(renderInfo.getNode().getName().equals("customcol")) {
|
||||
// Do not display the customcol elements in the BreadCrumb
|
||||
name = null;
|
||||
} else {
|
||||
// Capitalize the first letter
|
||||
name = name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
renderInfo.setIgnoreNodeFromDisplay(true);
|
||||
} else {
|
||||
// Set the render text
|
||||
renderInfo.setRenderedText("[" + name + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't show the popUp menu for the bread crumb.
|
||||
*/
|
||||
@Override
|
||||
public void customizePopUpMenu(Object popUp, AuthorAccess authorAccess) {
|
||||
if (popUp instanceof JPopupMenu) {
|
||||
JPopupMenu popupMenu = (JPopupMenu)popUp;
|
||||
// Remove all the components from the popUp menu .
|
||||
popupMenu.removeAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorCaretListener;
|
||||
import ro.sync.ecss.extensions.api.AuthorExtensionStateListener;
|
||||
import ro.sync.ecss.extensions.api.AuthorListener;
|
||||
import ro.sync.ecss.extensions.api.AuthorMouseListener;
|
||||
import ro.sync.ecss.extensions.api.OptionChangedEvent;
|
||||
import ro.sync.ecss.extensions.api.OptionListener;
|
||||
import ro.sync.ecss.extensions.api.callouts.AuthorCalloutsController;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight.PersistentHighlightType;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
|
||||
import ro.sync.ecss.extensions.api.highlights.ColorHighlightPainter;
|
||||
import ro.sync.ecss.extensions.api.highlights.HighlightPainter;
|
||||
import ro.sync.ecss.extensions.api.highlights.PersistentHighlightRenderer;
|
||||
import ro.sync.ecss.extensions.api.structure.AuthorPopupMenuCustomizer;
|
||||
import ro.sync.exml.view.graphics.Color;
|
||||
import ro.sync.exml.workspace.api.Platform;
|
||||
import ro.sync.exml.workspace.api.editor.page.author.actions.AuthorActionsProvider;
|
||||
import ro.sync.basic.util.URLUtil;
|
||||
import simple.documentation.framework.callouts.CalloutsRenderingProvider;
|
||||
import simple.documentation.framework.callouts.SDFAuthorPersistentHighlightActionsProvider;
|
||||
import simple.documentation.framework.filters.SDFDocumentFilter;
|
||||
import simple.documentation.framework.listeners.SDFAuthorCaretListener;
|
||||
import simple.documentation.framework.listeners.SDFAuthorListener;
|
||||
import simple.documentation.framework.listeners.SDFAuthorMouseListener;
|
||||
import simple.documentation.framework.operations.highlight.HighlightProperties;
|
||||
|
||||
/**
|
||||
* Simple Document Framework state listener used to register custom listeners(caret listener, mouse
|
||||
* listener, document listener and option listener) when the framework is activated.
|
||||
*
|
||||
*/
|
||||
public class SDFAuthorExtensionStateListener implements AuthorExtensionStateListener {
|
||||
/**
|
||||
* The key used to store a custom option.
|
||||
*/
|
||||
private String customKey = "sdf.custom.option.key";
|
||||
|
||||
/**
|
||||
* Custom caret listener to be added on activate and removed on deactivate
|
||||
*/
|
||||
private AuthorCaretListener sdfCaretListener;
|
||||
|
||||
/**
|
||||
* Custom mouse listener
|
||||
*/
|
||||
private AuthorMouseListener sdfMouseListener;
|
||||
|
||||
/**
|
||||
* Option listener to be added in the option storage.
|
||||
*/
|
||||
private OptionListener sdfOptionListener;
|
||||
|
||||
/**
|
||||
* Custom author listener
|
||||
*/
|
||||
private AuthorListener sdfAuthorDocumentListener;
|
||||
|
||||
/**
|
||||
* The access to the author functions.
|
||||
*/
|
||||
private AuthorAccess authorAccess;
|
||||
|
||||
/**
|
||||
* Map between author name and the corresponding highlight/colors association
|
||||
*/
|
||||
public static Map<String, Map<PersistentHighlightType, HighlightColors>> authorHighlightColors = null;
|
||||
|
||||
/**
|
||||
* Defines the color for a highlight
|
||||
*/
|
||||
public static class HighlightColors {
|
||||
private Color bgColor;
|
||||
private Color decorationColor;
|
||||
|
||||
/**
|
||||
* @param bgColor The background color.
|
||||
* @param decorationColor The color used for decoration
|
||||
*/
|
||||
public HighlightColors(Color bgColor, Color decorationColor) {
|
||||
super();
|
||||
this.bgColor = bgColor;
|
||||
this.decorationColor = decorationColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the bgColor.
|
||||
*/
|
||||
public Color getBgColor() {
|
||||
return bgColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the decorationColor.
|
||||
*/
|
||||
public Color getDecorationColor() {
|
||||
return decorationColor;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
authorHighlightColors = new HashMap<String, Map<PersistentHighlightType, HighlightColors>>();
|
||||
|
||||
// Set colors for Author_1
|
||||
Map<PersistentHighlightType, HighlightColors> colorsMap = new HashMap<PersistentHighlightType, HighlightColors>();
|
||||
// Set colors for Insert Change highlight
|
||||
colorsMap.put(PersistentHighlightType.CHANGE_INSERT,
|
||||
new HighlightColors(new Color(230, 255, 230), new Color(130, 255, 130)));
|
||||
// Set colors for Delete Change highlight
|
||||
colorsMap.put(PersistentHighlightType.CHANGE_DELETE,
|
||||
new HighlightColors(new Color(255, 255, 230), new Color(255, 255, 130)));
|
||||
authorHighlightColors.put("Author_1", colorsMap);
|
||||
|
||||
// Set colors for Author_2
|
||||
colorsMap = new HashMap<PersistentHighlightType, HighlightColors>();
|
||||
// Set colors for Insert Change highlight
|
||||
colorsMap.put(PersistentHighlightType.CHANGE_INSERT,
|
||||
new HighlightColors(new Color(255, 255, 230), new Color(255, 255, 130)));
|
||||
// Set colors for Delete Change highlight
|
||||
colorsMap.put(PersistentHighlightType.CHANGE_DELETE,
|
||||
new HighlightColors(new Color(240, 240, 240), new Color(64, 64, 64)));
|
||||
authorHighlightColors.put("Author_2", colorsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* The SDF Author extension is activated.
|
||||
*/
|
||||
public void activated(final AuthorAccess authorAccess) {
|
||||
this.authorAccess = authorAccess;
|
||||
sdfOptionListener = new OptionListener(customKey) {
|
||||
@Override
|
||||
public void optionValueChanged(OptionChangedEvent newValue) {
|
||||
// The custom option changed.
|
||||
}
|
||||
};
|
||||
|
||||
// Add document filter.
|
||||
authorAccess.getDocumentController().setDocumentFilter(new SDFDocumentFilter(authorAccess));
|
||||
|
||||
// Add an option listener.
|
||||
authorAccess.getOptionsStorage().addOptionListener(sdfOptionListener);
|
||||
|
||||
// Add author document listeners.
|
||||
sdfAuthorDocumentListener = new SDFAuthorListener(authorAccess);
|
||||
authorAccess.getDocumentController().addAuthorListener(sdfAuthorDocumentListener);
|
||||
|
||||
if (authorAccess.getWorkspaceAccess().getPlatform() != Platform.WEBAPP) {
|
||||
// Add mouse listener.
|
||||
sdfMouseListener = new SDFAuthorMouseListener(authorAccess);
|
||||
authorAccess.getEditorAccess().addAuthorMouseListener(sdfMouseListener);
|
||||
|
||||
// Add custom tooltip
|
||||
String tooltip = "[SDF] " + URLUtil.getDescription(authorAccess.getEditorAccess().getEditorLocation());
|
||||
authorAccess.getEditorAccess().setEditorTabTooltipText(tooltip);
|
||||
|
||||
// Add caret listener.
|
||||
sdfCaretListener = new SDFAuthorCaretListener(authorAccess);
|
||||
//authorAccess.getEditorAccess().addAuthorCaretListener(sdfCaretListener);
|
||||
|
||||
// Use the actions provider to switch to "No Tags" mode.
|
||||
AuthorActionsProvider actionsProvider = authorAccess.getEditorAccess().getActionsProvider();
|
||||
Map<String, Object> authorCommonActions = actionsProvider.getAuthorCommonActions();
|
||||
// Switch to no tags Author/No_tags
|
||||
actionsProvider.invokeAction(authorCommonActions.get("Author/No_tags"));
|
||||
|
||||
// Set highlights tool tip and painter
|
||||
authorAccess.getEditorAccess().getPersistentHighlighter().setHighlightRenderer(new PersistentHighlightRenderer() {
|
||||
public String getTooltip(AuthorPersistentHighlight highlight) {
|
||||
// Get highlight properties
|
||||
Map<String, String> properties = highlight.getClonedProperties();
|
||||
String highlightID = properties.get(HighlightProperties.ID);
|
||||
String highlightAuthor = properties.get(HighlightProperties.AUTHOR);
|
||||
String highlightComment = properties.get(HighlightProperties.COMMENT);
|
||||
|
||||
StringBuilder tooltip = new StringBuilder();
|
||||
// Add create date
|
||||
if (highlightID != null) {
|
||||
try {
|
||||
tooltip.append("Id: ").append(highlightID).append("\n");
|
||||
} catch (NumberFormatException e) {
|
||||
// Wrong date
|
||||
}
|
||||
}
|
||||
// Add author
|
||||
if (highlightAuthor != null) {
|
||||
tooltip.append("Author: ").append(highlightAuthor).append("\n");;
|
||||
}
|
||||
// Add comment
|
||||
if (highlightComment != null) {
|
||||
tooltip.append("Comment: ").append(highlightComment);
|
||||
}
|
||||
return tooltip.toString();
|
||||
}
|
||||
|
||||
public HighlightPainter getHighlightPainter(AuthorPersistentHighlight highlight) {
|
||||
ColorHighlightPainter painter = new ColorHighlightPainter();
|
||||
painter.setBgColor(new Color(0, 0, 255, 20));
|
||||
return painter;
|
||||
}
|
||||
});
|
||||
|
||||
// Set reviews tool tip and painters
|
||||
authorAccess.getReviewController().setReviewRenderer(new PersistentHighlightRenderer() {
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.highlights.PersistentHighlightRenderer#getTooltip(ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight)
|
||||
*/
|
||||
public String getTooltip(AuthorPersistentHighlight highlight) {
|
||||
String tooltip = "Review Tooltip";
|
||||
|
||||
PersistentHighlightType type = highlight.getType();
|
||||
if (type == PersistentHighlightType.CHANGE_DELETE) {
|
||||
// Delete highlight
|
||||
tooltip = "Deleted by " + highlight.getClonedProperties().get("author");
|
||||
} else if (type == PersistentHighlightType.CHANGE_INSERT) {
|
||||
// Insert highlight
|
||||
tooltip = "Inserted by " + highlight.getClonedProperties().get("author");
|
||||
} else if (type == PersistentHighlightType.COMMENT) {
|
||||
// Comment highlight
|
||||
// If a null value is returned, the default tooltip text will be used
|
||||
tooltip = null;
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.highlights.PersistentHighlightRenderer#getHighlightPainter(ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight)
|
||||
*/
|
||||
public HighlightPainter getHighlightPainter(AuthorPersistentHighlight highlight) {
|
||||
HighlightPainter reviewPainter = null;
|
||||
Map<String, String> properties = highlight.getClonedProperties();
|
||||
String authorName = properties.get("author");
|
||||
if (authorName != null) {
|
||||
// Get the highlight/colors association for this author name
|
||||
Map<PersistentHighlightType, HighlightColors> highlightColorsMap = authorHighlightColors.get(authorName);
|
||||
if (highlightColorsMap != null) {
|
||||
// Get the associated colors for this type of highlight
|
||||
HighlightColors highlightColors = highlightColorsMap.get(highlight.getType());
|
||||
if (highlightColors != null) {
|
||||
reviewPainter = new ColorHighlightPainter();
|
||||
// Background color
|
||||
((ColorHighlightPainter) reviewPainter).setBgColor(highlightColors.getBgColor());
|
||||
// Decoration color
|
||||
((ColorHighlightPainter) reviewPainter).setColor(highlightColors.getDecorationColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a null value is returned the default highlight painter is used.
|
||||
return reviewPainter;
|
||||
}
|
||||
});
|
||||
|
||||
// Get the list with all track changes highlights from the document
|
||||
AuthorPersistentHighlight[] changeHighlights = authorAccess.getReviewController().getChangeHighlights();
|
||||
if(changeHighlights.length > 0 && !authorAccess.getReviewController().isTrackingChanges()) {
|
||||
// If the document has track changes highlights, then set track changes mode to ON
|
||||
authorAccess.getReviewController().toggleTrackChanges();
|
||||
}
|
||||
|
||||
// Add a popup menu customizer. Group Cut, Copy, Paste and Paste as XML actions in Edit menu.
|
||||
authorAccess.getEditorAccess().addPopUpMenuCustomizer(new AuthorPopupMenuCustomizer() {
|
||||
// Customize popup menu
|
||||
@SuppressWarnings("serial")
|
||||
public void customizePopUpMenu(Object popUp, final AuthorAccess authorAccess) {
|
||||
JPopupMenu popupMenu = (JPopupMenu) popUp;
|
||||
Component[] components = popupMenu.getComponents();
|
||||
// The new 'Edit' menu
|
||||
JMenu editMenu = new JMenu("Edit");
|
||||
boolean shouldAddEditMenu = false;
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
if (components[i] instanceof JMenuItem) {
|
||||
JMenuItem menuItem = (JMenuItem) components[i];
|
||||
if (menuItem.getAction() != null) {
|
||||
Object name = menuItem.getAction().getValue(Action.NAME);
|
||||
if ("Cut".equals(name) || "Copy".equals(name) || "Paste".equals(name)|| "Paste as XML".equals(name)) {
|
||||
// Remove edit actions
|
||||
popupMenu.remove(menuItem);
|
||||
// Add edit actions to edit menu
|
||||
editMenu.add(menuItem);
|
||||
shouldAddEditMenu = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldAddEditMenu) {
|
||||
popupMenu.add(editMenu, 0);
|
||||
}
|
||||
|
||||
final URL selectedUrl;
|
||||
try {
|
||||
final String selectedText = authorAccess.getEditorAccess().getSelectedText();
|
||||
if (selectedText != null) {
|
||||
selectedUrl = new URL(selectedText);
|
||||
// Open selected url in system application
|
||||
popupMenu.add(new JMenuItem(new AbstractAction("Open in system application") {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
authorAccess.getWorkspaceAccess().openInExternalApplication(selectedUrl, true);
|
||||
}
|
||||
}), 0);
|
||||
}
|
||||
} catch (MalformedURLException e2) {}
|
||||
}
|
||||
});
|
||||
|
||||
// Add a provider for callouts rendering
|
||||
CalloutsRenderingProvider calloutsRenderingProvider = new CalloutsRenderingProvider(authorAccess);
|
||||
AuthorCalloutsController authorCalloutsController =
|
||||
authorAccess.getReviewController().getAuthorCalloutsController();
|
||||
authorCalloutsController.setCalloutsRenderingInformationProvider(calloutsRenderingProvider);
|
||||
|
||||
// Show insertions callouts
|
||||
authorCalloutsController.setShowInsertionsCallouts(true);
|
||||
|
||||
// Add an actions provider for actions that are shown on the contextual menu of the
|
||||
// custom callout
|
||||
AuthorPersistentHighlighter highlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
|
||||
SDFAuthorPersistentHighlightActionsProvider highlightActionsProvider =
|
||||
new SDFAuthorPersistentHighlightActionsProvider(authorAccess);
|
||||
highlighter.setHighlightsActionsProvider(highlightActionsProvider);
|
||||
}
|
||||
|
||||
// Other custom initializations...
|
||||
}
|
||||
|
||||
/**
|
||||
* The SDF Author extension is deactivated.
|
||||
*/
|
||||
public void deactivated(AuthorAccess authorAccess) {
|
||||
|
||||
// Remove the option listener.
|
||||
authorAccess.getOptionsStorage().removeOptionListener(sdfOptionListener);
|
||||
|
||||
// Remove document listeners.
|
||||
authorAccess.getDocumentController().removeAuthorListener(sdfAuthorDocumentListener);
|
||||
|
||||
if (authorAccess.getWorkspaceAccess().getPlatform() != Platform.WEBAPP) {
|
||||
// Remove mouse listener.
|
||||
authorAccess.getEditorAccess().removeAuthorMouseListener(sdfMouseListener);
|
||||
|
||||
// Remove caret listener.
|
||||
authorAccess.getEditorAccess().removeAuthorCaretListener(sdfCaretListener);
|
||||
}
|
||||
|
||||
// Other actions...
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Simple Document Framework state listener";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Author access.
|
||||
*
|
||||
* @return The access to Author functions and components.
|
||||
*/
|
||||
public AuthorAccess getAuthorAccess() {
|
||||
return authorAccess;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
import ro.sync.ecss.extensions.api.structure.AuthorOutlineCustomizer;
|
||||
import ro.sync.ecss.extensions.api.structure.RenderingInformation;
|
||||
|
||||
/**
|
||||
* Simple Document Framework Author Outline customizer used for custom filtering and nodes rendering
|
||||
* in the Outline.
|
||||
*/
|
||||
public class SDFAuthorOutlineCustomizer extends AuthorOutlineCustomizer {
|
||||
|
||||
/**
|
||||
* Ignore 'b' nodes.
|
||||
*/
|
||||
@Override
|
||||
public boolean ignoreNode(AuthorNode node) {
|
||||
if (node.getName().equals("b")) {
|
||||
return true;
|
||||
}
|
||||
// Default behavior
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For every node render its name in the Outline (with the first letter capitalized).
|
||||
* Set 'Paragraph' as render for 'para' nodes
|
||||
* Render the title text for 'section' nodes and add 'Section' as additional info and tooltip.
|
||||
*/
|
||||
@Override
|
||||
public void customizeRenderingInformation(
|
||||
RenderingInformation renderInfo) {
|
||||
// Get the node to be rendered
|
||||
AuthorNode node = renderInfo.getNode();
|
||||
|
||||
String name = node.getName();
|
||||
if (name.length() > 0) {
|
||||
if ("para".equals(name)) {
|
||||
name = "Paragraph";
|
||||
} else {
|
||||
// Capitalize the first letter
|
||||
name = name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
|
||||
}
|
||||
}
|
||||
|
||||
// Render additional attribute value
|
||||
String additionalAttrValue = renderInfo.getAdditionalRenderedAttributeValue();
|
||||
if (additionalAttrValue != null && additionalAttrValue.length() > 0) {
|
||||
renderInfo.setAdditionalRenderedAttributeValue("[" + additionalAttrValue + "]");
|
||||
}
|
||||
|
||||
// Set the render text
|
||||
renderInfo.setRenderedText("[" + name + "]");
|
||||
|
||||
// Render the title text in the 'section' additional info.
|
||||
if (node.getName().equals("section")) {
|
||||
if (node instanceof AuthorElement) {
|
||||
// Get the 'section' content nodes
|
||||
List<AuthorNode> contentNodes = ((AuthorElement) node).getContentNodes();
|
||||
if (contentNodes.size() > 0) {
|
||||
AuthorNode authorNode = contentNodes.get(0);
|
||||
if ("title".equals(authorNode.getName())) {
|
||||
try {
|
||||
// Render the title
|
||||
String textContent = authorNode.getTextContent();
|
||||
if (textContent != null && textContent.trim().length() > 0) {
|
||||
if (textContent.length() > 20) {
|
||||
textContent = textContent.substring(0, 20);
|
||||
}
|
||||
renderInfo.setRenderedText(textContent);
|
||||
// Set 'Section' as additional info
|
||||
renderInfo.setAdditionalRenderedText("[Section]");
|
||||
// Set the tooltip text
|
||||
renderInfo.setTooltipText(textContent);
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the popUp menu.
|
||||
* If the selected node is a 'title' from 'section' remove the 'Delete'
|
||||
* action from the popUp menu and add the 'Remove content' action if the selection
|
||||
* is single.
|
||||
*/
|
||||
@Override
|
||||
public void customizePopUpMenu(Object popUp, final AuthorAccess authorAccess) {
|
||||
if (authorAccess != null) {
|
||||
if (popUp instanceof JPopupMenu) {
|
||||
JPopupMenu popupMenu = (JPopupMenu)popUp;
|
||||
// Find the selected paths from the Outliner
|
||||
TreePath[] selectedPaths = authorAccess.getOutlineAccess().getSelectedPaths(true);
|
||||
for (TreePath treePath : selectedPaths) {
|
||||
Object[] authorNodesPath = treePath.getPath();
|
||||
if (authorNodesPath.length > 0) {
|
||||
// Find the selected node (last node from the list of path nodes)
|
||||
final AuthorNode selectedAuthorNode = (AuthorNode) authorNodesPath[authorNodesPath.length - 1];
|
||||
// Find if the current selected node is a title from section
|
||||
if ("title".equals(selectedAuthorNode.getName()) &&
|
||||
"section".equals(selectedAuthorNode.getParent().getName())) {
|
||||
// Find the popUp item corresponding to the 'Delete' action
|
||||
Component[] components = popupMenu.getComponents();
|
||||
int deleteItemIndex = 0;
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
if (components[i] instanceof JMenuItem) {
|
||||
String itemText = ((JMenuItem) components[i]).getText();
|
||||
if ("Delete".equals(itemText)) {
|
||||
// The 'Remove Content' item will be added at the same index
|
||||
deleteItemIndex = i;
|
||||
// Remove the 'Delete' action
|
||||
popupMenu.remove(deleteItemIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedPaths.length == 1) {
|
||||
// Add the 'Remove content' action
|
||||
JMenuItem sdfMenuItem = new JMenuItem("Remove Content");
|
||||
sdfMenuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
// Remove title content
|
||||
int startOffset = selectedAuthorNode.getStartOffset();
|
||||
int endOffset = selectedAuthorNode.getEndOffset();
|
||||
if (endOffset > startOffset + 1) {
|
||||
authorAccess.getDocumentController().delete(
|
||||
startOffset + 1,
|
||||
endOffset);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Add the menu item in the menu
|
||||
popupMenu.add(sdfMenuItem, deleteItemIndex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
|
||||
import ro.sync.basic.util.URLUtil;
|
||||
|
||||
/**
|
||||
* Accepts insertion of images and XML files from external sources.
|
||||
*/
|
||||
public class SDFExternalObjectInsertionHandler extends AuthorExternalObjectInsertionHandler {
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler#insertURLs(ro.sync.ecss.extensions.api.AuthorAccess, java.util.List, int)
|
||||
*/
|
||||
@Override
|
||||
public void insertURLs(AuthorAccess authorAccess, List<URL> urls, int source)
|
||||
throws AuthorOperationException {
|
||||
for (URL url : urls) {
|
||||
String xmlFragment = null;
|
||||
if (authorAccess.getUtilAccess().isSupportedImageURL(url)) {
|
||||
// Insert an image element
|
||||
xmlFragment = "<image href=\"" + url.toString() + "\"/>";
|
||||
} else {
|
||||
// Insert
|
||||
xmlFragment = "<ref location=\"" + url.toString() + "\"/>";
|
||||
}
|
||||
if (xmlFragment != null) {
|
||||
// Create am image fragment
|
||||
AuthorDocumentFragment imageFragment = authorAccess.getDocumentController().createNewDocumentFragmentInContext(
|
||||
xmlFragment, authorAccess.getEditorAccess().getCaretOffset());
|
||||
// Insert the fragment
|
||||
authorAccess.getDocumentController().insertFragment(authorAccess.getEditorAccess().getCaretOffset(), imageFragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler#acceptSource(ro.sync.ecss.extensions.api.AuthorAccess, int)
|
||||
*/
|
||||
@Override
|
||||
public boolean acceptSource(AuthorAccess authorAccess, int source) {
|
||||
if (source == DND_EXTERNAL) {
|
||||
// Accept only from external source
|
||||
return true;
|
||||
}
|
||||
// For urls from other sources (Dita Maps Manager, Project) a new tab will be open.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorExternalObjectInsertionHandler#acceptURLs(ro.sync.ecss.extensions.api.AuthorAccess, java.util.List, int)
|
||||
*/
|
||||
@Override
|
||||
public boolean acceptURLs(AuthorAccess authorAccess, List<URL> urls, int source) {
|
||||
boolean accept = acceptSource(authorAccess, source);
|
||||
if (accept) {
|
||||
for (int i = 0; i < urls.size(); i++) {
|
||||
// Accept only XML and image files
|
||||
if (!authorAccess.getUtilAccess().isSupportedImageURL(urls.get(i)) &&
|
||||
!"xml".equalsIgnoreCase(URLUtil.getExtension(urls.get(i).toString()))) {
|
||||
accept = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return accept;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.xml.transform.sax.SAXSource;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.xml.sax.EntityResolver;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorReferenceResolver;
|
||||
import ro.sync.ecss.extensions.api.node.AttrValue;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
|
||||
/**
|
||||
* References resolver for Simple Documentation Framework.
|
||||
*
|
||||
*/
|
||||
public class SDFReferencesResolver implements AuthorReferenceResolver {
|
||||
/**
|
||||
* Logger for logging.
|
||||
*/
|
||||
private static Logger logger = LogManager.getLogger(
|
||||
SDFReferencesResolver.class.getName());
|
||||
|
||||
/**
|
||||
* Verifies if the handler considers the node to have references.
|
||||
*
|
||||
* @param node The node to be analyzed.
|
||||
* @return <code>true</code> if it is has references.
|
||||
*/
|
||||
public boolean hasReferences(AuthorNode node) {
|
||||
boolean hasReferences = false;
|
||||
if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
|
||||
AuthorElement element = (AuthorElement) node;
|
||||
if ("ref".equals(element.getLocalName())) {
|
||||
AttrValue attrValue = element.getAttribute("location");
|
||||
hasReferences = attrValue != null;
|
||||
}
|
||||
}
|
||||
return hasReferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the node that contains the expanded referred content.
|
||||
*
|
||||
* @param node The node that contains references.
|
||||
* @return The display name of the node.
|
||||
*/
|
||||
public String getDisplayName(AuthorNode node) {
|
||||
String displayName = "ref-fragment";
|
||||
if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
|
||||
AuthorElement element = (AuthorElement) node;
|
||||
if ("ref".equals(element.getLocalName())) {
|
||||
AttrValue attrValue = element.getAttribute("location");
|
||||
if (attrValue != null) {
|
||||
displayName = attrValue.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the references of the node.
|
||||
*
|
||||
* The returning SAXSource will be used for creating the referred content
|
||||
* using the parser and source inside it.
|
||||
*
|
||||
* @param node The clone of the node.
|
||||
* @param systemID The system ID of the node with references.
|
||||
* @param authorAccess The author access implementation.
|
||||
* @param entityResolver The entity resolver that can be used to resolve:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Resources that are already opened in editor.
|
||||
* For this case the InputSource will contains the editor content.</li>
|
||||
* <li>Resources resolved through XML catalog.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return The SAX source including the parser and the parser's input source.
|
||||
*/
|
||||
public SAXSource resolveReference(
|
||||
AuthorNode node,
|
||||
String systemID,
|
||||
AuthorAccess authorAccess,
|
||||
EntityResolver entityResolver) {
|
||||
SAXSource saxSource = null;
|
||||
if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
|
||||
AuthorElement element = (AuthorElement) node;
|
||||
if ("ref".equals(element.getLocalName())) {
|
||||
AttrValue attrValue = element.getAttribute("location");
|
||||
if (attrValue != null){
|
||||
String attrStringVal = attrValue.getValue();
|
||||
if (attrStringVal.length() > 0) {
|
||||
try {
|
||||
URL absoluteUrl = new URL(new URL(systemID),
|
||||
authorAccess.getUtilAccess().correctURL(attrStringVal));
|
||||
InputSource inputSource = entityResolver.resolveEntity(null,
|
||||
absoluteUrl.toString());
|
||||
if(inputSource == null) {
|
||||
inputSource = new InputSource(absoluteUrl.toString());
|
||||
}
|
||||
XMLReader xmlReader = authorAccess.getXMLUtilAccess().newNonValidatingXMLReader();
|
||||
xmlReader.setEntityResolver(entityResolver);
|
||||
saxSource = new SAXSource(xmlReader, inputSource);
|
||||
} catch (MalformedURLException e) {
|
||||
logger.error(e, e);
|
||||
} catch (SAXException e) {
|
||||
logger.error(e, e);
|
||||
} catch (IOException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return saxSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unique identifier for the node reference.
|
||||
*
|
||||
* The unique identifier is used to avoid resolving the references
|
||||
* recursively.
|
||||
*
|
||||
* @param node The node that has reference.
|
||||
* @return An unique identifier for the reference node.
|
||||
*/
|
||||
public String getReferenceUniqueID(AuthorNode node) {
|
||||
String id = null;
|
||||
if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
|
||||
AuthorElement element = (AuthorElement) node;
|
||||
if ("ref".equals(element.getLocalName())) {
|
||||
AttrValue attrValue = element.getAttribute("location");
|
||||
if (attrValue != null) {
|
||||
id = attrValue.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the systemID of the referred content.
|
||||
*
|
||||
* @param node The reference node.
|
||||
* @param authorAccess The author access.
|
||||
*
|
||||
* @return The systemID of the referred content.
|
||||
*/
|
||||
public String getReferenceSystemID(AuthorNode node,
|
||||
AuthorAccess authorAccess) {
|
||||
String systemID = null;
|
||||
if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
|
||||
AuthorElement element = (AuthorElement) node;
|
||||
if ("ref".equals(element.getLocalName())) {
|
||||
AttrValue attrValue = element.getAttribute("location");
|
||||
if (attrValue != null) {
|
||||
String attrStringVal = attrValue.getValue();
|
||||
try {
|
||||
URL absoluteUrl = new URL(node.getXMLBaseURL(),
|
||||
authorAccess.getUtilAccess().correctURL(attrStringVal));
|
||||
systemID = absoluteUrl.toString();
|
||||
} catch (MalformedURLException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return systemID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the references of the given node must be refreshed
|
||||
* when the attribute with the specified name has changed.
|
||||
*
|
||||
* @param node The node with the references.
|
||||
* @param attributeName The name of the changed attribute.
|
||||
* @return <code>true</code> if the references must be refreshed.
|
||||
*/
|
||||
public boolean isReferenceChanged(AuthorNode node, String attributeName) {
|
||||
return "location".equals(attributeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The description of the author extension.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Resolves the 'ref' references";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,353 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import ro.sync.contentcompletion.xml.ContextElement;
|
||||
import ro.sync.contentcompletion.xml.WhatElementsCanGoHereContext;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandlerAdapter;
|
||||
import ro.sync.ecss.extensions.api.AuthorSchemaManager;
|
||||
import ro.sync.ecss.extensions.api.InvalidEditException;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
|
||||
/**
|
||||
* Specific editing support for SDF documents. Handles typing and paste events inside section and tables.
|
||||
*/
|
||||
public class SDFSchemaAwareEditingHandler extends AuthorSchemaAwareEditingHandlerAdapter {
|
||||
|
||||
private static final String SDF_NAMESPACE = "http://www.oxygenxml.com/sample/documentation";
|
||||
/**
|
||||
* SDF table element name.
|
||||
*/
|
||||
private static final String SDF_TABLE = "table";
|
||||
/**
|
||||
* SDF table row name.
|
||||
*/
|
||||
private static final String SDF_TABLE_ROW = "tr";
|
||||
/**
|
||||
* SDF table cell name.
|
||||
*/
|
||||
private static final String SDF_TABLE_CELL = "td";
|
||||
/**
|
||||
* SDF section element name.
|
||||
*/
|
||||
private static final String SECTION = "section";
|
||||
/**
|
||||
* SDF para element name.
|
||||
*/
|
||||
protected static final String PARA = "para";
|
||||
/**
|
||||
* SDF title element name.
|
||||
*/
|
||||
protected static final String TITLE = "title";
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleDelete(int, int, ro.sync.ecss.extensions.api.AuthorAccess, boolean)
|
||||
*/
|
||||
@Override
|
||||
public boolean handleDelete(int offset, int deleteType, AuthorAccess authorAccess, boolean wordLevel)
|
||||
throws InvalidEditException {
|
||||
// Not handled.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleDeleteElementTags(ro.sync.ecss.extensions.api.node.AuthorNode, ro.sync.ecss.extensions.api.AuthorAccess)
|
||||
*/
|
||||
@Override
|
||||
public boolean handleDeleteElementTags(AuthorNode nodeToUnwrap, AuthorAccess authorAccess)
|
||||
throws InvalidEditException {
|
||||
// Not handled.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleDeleteSelection(int, int, int, ro.sync.ecss.extensions.api.AuthorAccess)
|
||||
*/
|
||||
@Override
|
||||
public boolean handleDeleteSelection(int selectionStart, int selectionEnd, int generatedByActionId,
|
||||
AuthorAccess authorAccess) throws InvalidEditException {
|
||||
// Not handled.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleJoinElements(ro.sync.ecss.extensions.api.node.AuthorNode, java.util.List, ro.sync.ecss.extensions.api.AuthorAccess)
|
||||
*/
|
||||
@Override
|
||||
public boolean handleJoinElements(AuthorNode targetNode, List<AuthorNode> nodesToJoin, AuthorAccess authorAccess)
|
||||
throws InvalidEditException {
|
||||
// Not handled.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handlePasteFragment(int, ro.sync.ecss.extensions.api.node.AuthorDocumentFragment[], int, ro.sync.ecss.extensions.api.AuthorAccess)
|
||||
*/
|
||||
@Override
|
||||
public boolean handlePasteFragment(int offset, AuthorDocumentFragment[] fragmentsToInsert, int actionId,
|
||||
AuthorAccess authorAccess) throws InvalidEditException {
|
||||
boolean handleInsertionEvent = false;
|
||||
AuthorSchemaManager authorSchemaManager = authorAccess.getDocumentController().getAuthorSchemaManager();
|
||||
if (!authorSchemaManager.isLearnSchema() &&
|
||||
!authorSchemaManager.hasLoadingErrors() &&
|
||||
authorSchemaManager.getAuthorSchemaAwareOptions().isEnableSmartPaste()) {
|
||||
handleInsertionEvent = handleInsertionEvent(offset, fragmentsToInsert, authorAccess);
|
||||
}
|
||||
return handleInsertionEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorSchemaAwareEditingHandler#handleTyping(int, char, ro.sync.ecss.extensions.api.AuthorAccess)
|
||||
*/
|
||||
@Override
|
||||
public boolean handleTyping(int offset, char ch, AuthorAccess authorAccess)
|
||||
throws InvalidEditException {
|
||||
boolean handleTyping = false;
|
||||
AuthorSchemaManager authorSchemaManager = authorAccess.getDocumentController().getAuthorSchemaManager();
|
||||
if (!authorSchemaManager.isLearnSchema() &&
|
||||
!authorSchemaManager.hasLoadingErrors() &&
|
||||
authorSchemaManager.getAuthorSchemaAwareOptions().isEnableSmartTyping()) {
|
||||
try {
|
||||
AuthorDocumentFragment characterFragment =
|
||||
authorAccess.getDocumentController().createNewDocumentTextFragment(String.valueOf(ch));
|
||||
handleTyping = handleInsertionEvent(offset, new AuthorDocumentFragment[] {characterFragment}, authorAccess);
|
||||
} catch (AuthorOperationException e) {
|
||||
throw new InvalidEditException(e.getMessage(), "Invalid typing event: " + e.getMessage(), e, false);
|
||||
}
|
||||
}
|
||||
return handleTyping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an insertion event (either typing or paste).
|
||||
*
|
||||
* @param offset Offset where the insertion event occurred.
|
||||
* @param fragmentsToInsert Fragments that must be inserted at the given offset.
|
||||
* @param authorAccess Author access.
|
||||
* @return <code>true</code> if the event was handled, <code>false</code> otherwise.
|
||||
*
|
||||
* @throws InvalidEditException The event was rejected because it is invalid.
|
||||
*/
|
||||
private boolean handleInsertionEvent(
|
||||
int offset,
|
||||
AuthorDocumentFragment[] fragmentsToInsert,
|
||||
AuthorAccess authorAccess) throws InvalidEditException {
|
||||
AuthorSchemaManager authorSchemaManager = authorAccess.getDocumentController().getAuthorSchemaManager();
|
||||
boolean handleEvent = false;
|
||||
try {
|
||||
AuthorNode nodeAtInsertionOffset = authorAccess.getDocumentController().getNodeAtOffset(offset);
|
||||
if (isElementWithNameAndNamespace(nodeAtInsertionOffset, SDF_TABLE)) {
|
||||
// Check if the fragment is allowed as it is.
|
||||
boolean canInsertFragments = authorSchemaManager.canInsertDocumentFragments(
|
||||
fragmentsToInsert,
|
||||
offset,
|
||||
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS);
|
||||
if (!canInsertFragments) {
|
||||
handleEvent = handleInvalidInsertionEventInTable(
|
||||
offset,
|
||||
fragmentsToInsert,
|
||||
authorAccess,
|
||||
authorSchemaManager);
|
||||
}
|
||||
} else if(isElementWithNameAndNamespace(nodeAtInsertionOffset, SECTION)) {
|
||||
// Check if the fragment is allowed as it is.
|
||||
boolean canInsertFragments = authorSchemaManager.canInsertDocumentFragments(
|
||||
fragmentsToInsert,
|
||||
offset,
|
||||
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS);
|
||||
if (!canInsertFragments) {
|
||||
// Insertion in 'section' element
|
||||
handleEvent = handleInvalidInsertionEventInSect(
|
||||
offset,
|
||||
fragmentsToInsert,
|
||||
authorAccess,
|
||||
authorSchemaManager);
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
throw new InvalidEditException(e.getMessage(), "Invalid typing event: " + e.getMessage(), e, false);
|
||||
} catch (AuthorOperationException e) {
|
||||
throw new InvalidEditException(e.getMessage(), "Invalid typing event: " + e.getMessage(), e, false);
|
||||
}
|
||||
return handleEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the given node is an element with the given local name and from the SDF namespace.
|
||||
*/
|
||||
protected boolean isElementWithNameAndNamespace(AuthorNode node, String elementLocalName) {
|
||||
boolean result = false;
|
||||
if(node.getType() == AuthorNode.NODE_TYPE_ELEMENT) {
|
||||
AuthorElement element = (AuthorElement) node;
|
||||
result = elementLocalName.equals(element.getLocalName()) && element.getNamespace().equals(SDF_NAMESPACE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to handle invalid insertion events in a SDF 'table'.
|
||||
* A row element will be inserted with a new cell in which the fragments will be inserted.
|
||||
*
|
||||
* @param offset Offset where the insertion event occurred.
|
||||
* @param fragmentsToInsert Fragments that must be inserted at the given offset.
|
||||
* @param authorAccess Author access.
|
||||
* @return <code>true</code> if the event was handled, <code>false</code> otherwise.
|
||||
*/
|
||||
private boolean handleInvalidInsertionEventInTable(
|
||||
int offset,
|
||||
AuthorDocumentFragment[] fragmentsToInsert,
|
||||
AuthorAccess authorAccess,
|
||||
AuthorSchemaManager authorSchemaManager) throws BadLocationException, AuthorOperationException {
|
||||
boolean handleEvent = false;
|
||||
// Typing/paste inside a SDF table. We will try to wrap the fragment into a new cell and insert it inside a new row.
|
||||
WhatElementsCanGoHereContext context = authorSchemaManager.createWhatElementsCanGoHereContext(offset);
|
||||
StringBuilder xmlFragment = new StringBuilder("<");
|
||||
xmlFragment.append(SDF_TABLE_ROW);
|
||||
if (SDF_NAMESPACE != null && SDF_NAMESPACE.length() != 0) {
|
||||
xmlFragment.append(" xmlns=\"").append(SDF_NAMESPACE).append("\"");
|
||||
}
|
||||
xmlFragment.append("/>");
|
||||
|
||||
// Check if a row can be inserted at the current offset.
|
||||
boolean canInsertRow = authorSchemaManager.canInsertDocumentFragments(
|
||||
new AuthorDocumentFragment[] {authorAccess.getDocumentController().createNewDocumentFragmentInContext(xmlFragment.toString(), offset)},
|
||||
context,
|
||||
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS);
|
||||
|
||||
// Derive the context by adding a new row element with a cell.
|
||||
if (canInsertRow) {
|
||||
pushContextElement(context, SDF_TABLE_ROW);
|
||||
pushContextElement(context, SDF_TABLE_CELL);
|
||||
|
||||
// Test if fragments can be inserted in the new context.
|
||||
if (authorSchemaManager.canInsertDocumentFragments(
|
||||
fragmentsToInsert,
|
||||
context,
|
||||
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS)) {
|
||||
|
||||
// Insert a new row with a cell.
|
||||
xmlFragment = new StringBuilder("<");
|
||||
xmlFragment.append(SDF_TABLE_ROW);
|
||||
|
||||
if (SDF_NAMESPACE != null && SDF_NAMESPACE.length() != 0) {
|
||||
xmlFragment.append(" xmlns=\"").append(SDF_NAMESPACE).append("\"");
|
||||
}
|
||||
xmlFragment.append("><");
|
||||
xmlFragment.append(SDF_TABLE_CELL);
|
||||
xmlFragment.append("/></");
|
||||
xmlFragment.append(SDF_TABLE_ROW);
|
||||
xmlFragment.append(">");
|
||||
authorAccess.getDocumentController().insertXMLFragment(xmlFragment.toString(), offset);
|
||||
|
||||
// Get the newly inserted cell.
|
||||
AuthorNode newCell = authorAccess.getDocumentController().getNodeAtOffset(offset + 2);
|
||||
for (int i = 0; i < fragmentsToInsert.length; i++) {
|
||||
authorAccess.getDocumentController().insertFragment(newCell.getEndOffset(), fragmentsToInsert[i]);
|
||||
}
|
||||
|
||||
handleEvent = true;
|
||||
}
|
||||
}
|
||||
return handleEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the given context by adding the specified element.
|
||||
*/
|
||||
protected void pushContextElement(WhatElementsCanGoHereContext context, String elementName) {
|
||||
ContextElement contextElement = new ContextElement();
|
||||
contextElement.setQName(elementName);
|
||||
contextElement.setNamespace(SDF_NAMESPACE);
|
||||
context.pushContextElement(contextElement, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to handle invalid insertion events in 'section'.
|
||||
* The solution is to insert the <code>fragmentsToInsert</code> into a 'title' element if the sect element is empty or
|
||||
* into a 'para' element if the sect already contains a 'title'.
|
||||
*
|
||||
* @param offset Offset where the insertion event occurred.
|
||||
* @param fragmentsToInsert Fragments that must be inserted at the given offset.
|
||||
* @param authorAccess Author access.
|
||||
* @return <code>true</code> if the event was handled, <code>false</code> otherwise.
|
||||
*/
|
||||
private boolean handleInvalidInsertionEventInSect(int offset, AuthorDocumentFragment[] fragmentsToInsert, AuthorAccess authorAccess,
|
||||
AuthorSchemaManager authorSchemaManager) throws BadLocationException, AuthorOperationException {
|
||||
boolean handleEvent = false;
|
||||
// Typing/paste inside an section.
|
||||
AuthorElement sectionElement = (AuthorElement) authorAccess.getDocumentController().getNodeAtOffset(offset);
|
||||
|
||||
if (sectionElement.getStartOffset() + 1 == sectionElement.getEndOffset()) {
|
||||
// Empty section element
|
||||
WhatElementsCanGoHereContext context = authorSchemaManager.createWhatElementsCanGoHereContext(offset);
|
||||
// Derive the context by adding a title.
|
||||
pushContextElement(context, TITLE);
|
||||
|
||||
// Test if fragments can be inserted in 'title' element
|
||||
if (authorSchemaManager.canInsertDocumentFragments(
|
||||
fragmentsToInsert,
|
||||
context,
|
||||
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS)) {
|
||||
// Create a title structure and insert fragments inside
|
||||
StringBuilder xmlFragment = new StringBuilder("<").append(TITLE);
|
||||
if (SDF_NAMESPACE != null && SDF_NAMESPACE.length() != 0) {
|
||||
xmlFragment.append(" xmlns=\"").append(SDF_NAMESPACE).append("\"");
|
||||
}
|
||||
xmlFragment.append(">").append("</").append(TITLE).append(">");
|
||||
// Insert title
|
||||
authorAccess.getDocumentController().insertXMLFragment(xmlFragment.toString(), offset);
|
||||
|
||||
// Insert fragments
|
||||
AuthorNode newParaNode = authorAccess.getDocumentController().getNodeAtOffset(offset + 1);
|
||||
for (int i = 0; i < fragmentsToInsert.length; i++) {
|
||||
authorAccess.getDocumentController().insertFragment(newParaNode.getEndOffset(), fragmentsToInsert[i]);
|
||||
}
|
||||
handleEvent = true;
|
||||
}
|
||||
} else {
|
||||
// Check if there is just a title.
|
||||
List<AuthorNode> contentNodes = sectionElement.getContentNodes();
|
||||
if (contentNodes.size() == 1) {
|
||||
AuthorNode child = contentNodes.get(0);
|
||||
boolean isTitleChild = isElementWithNameAndNamespace(child, TITLE);
|
||||
if (isTitleChild && child.getEndOffset() < offset) {
|
||||
// We are after the title.
|
||||
|
||||
// Empty sect element
|
||||
WhatElementsCanGoHereContext context = authorSchemaManager.createWhatElementsCanGoHereContext(offset);
|
||||
// Derive the context by adding a para
|
||||
pushContextElement(context, PARA);
|
||||
|
||||
// Test if fragments can be inserted in 'para' element
|
||||
if (authorSchemaManager.canInsertDocumentFragments(
|
||||
fragmentsToInsert,
|
||||
context,
|
||||
AuthorSchemaManager.VALIDATION_MODE_STRICT_FIRST_CHILD_LAX_OTHERS)) {
|
||||
// Create a para structure and insert fragments inside
|
||||
StringBuilder xmlFragment = new StringBuilder("<").append(PARA);
|
||||
if (SDF_NAMESPACE != null && SDF_NAMESPACE.length() != 0) {
|
||||
xmlFragment.append(" xmlns=\"").append(SDF_NAMESPACE).append("\"");
|
||||
}
|
||||
xmlFragment.append(">").append("</").append(PARA).append(">");
|
||||
// Insert para
|
||||
authorAccess.getDocumentController().insertXMLFragment(xmlFragment.toString(), offset);
|
||||
|
||||
// Insert fragments
|
||||
AuthorNode newParaNode = authorAccess.getDocumentController().getNodeAtOffset(offset + 1);
|
||||
for (int i = 0; i < fragmentsToInsert.length; i++) {
|
||||
authorAccess.getDocumentController().insertFragment(newParaNode.getEndOffset(), fragmentsToInsert[i]);
|
||||
}
|
||||
handleEvent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return handleEvent;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import ro.sync.contentcompletion.xml.CIAttribute;
|
||||
import ro.sync.contentcompletion.xml.CIElement;
|
||||
import ro.sync.contentcompletion.xml.CIValue;
|
||||
import ro.sync.contentcompletion.xml.Context;
|
||||
import ro.sync.contentcompletion.xml.ContextElement;
|
||||
import ro.sync.contentcompletion.xml.SchemaManagerFilter;
|
||||
import ro.sync.contentcompletion.xml.WhatAttributesCanGoHereContext;
|
||||
import ro.sync.contentcompletion.xml.WhatElementsCanGoHereContext;
|
||||
import ro.sync.contentcompletion.xml.WhatPossibleValuesHasAttributeContext;
|
||||
import simple.documentation.framework.SDFElement;
|
||||
|
||||
/**
|
||||
* Schema manager filter for the Simple Documentation Framework.
|
||||
* Filters custom elements, attributes, elements values and attributes values.
|
||||
*
|
||||
*/
|
||||
public class SDFSchemaManagerFilter implements SchemaManagerFilter {
|
||||
|
||||
/**
|
||||
* Filter values of the "href" attribute of an image element.
|
||||
*/
|
||||
public List<CIValue> filterAttributeValues(List<CIValue> attributeValues,
|
||||
WhatPossibleValuesHasAttributeContext context) {
|
||||
// If the element from the current context is the "image" element and the current context
|
||||
// attribute is "href" then add a custom URL value to the list of default content completion
|
||||
// proposals.
|
||||
if (context != null) {
|
||||
ContextElement currentElement = context.getParentElement();
|
||||
String attributeName = context.getAttributeName();
|
||||
if("image".equals(currentElement.getQName()) && "href".equals(attributeName)) {
|
||||
CIValue newValue = new CIValue("Custom_image_URL");
|
||||
if (attributeValues == null) {
|
||||
attributeValues = new ArrayList<CIValue>();
|
||||
}
|
||||
// Add the new value.
|
||||
attributeValues.add(newValue);
|
||||
}
|
||||
}
|
||||
return attributeValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter attributes of the "table" element.
|
||||
*/
|
||||
public List<CIAttribute> filterAttributes(List<CIAttribute> attributes,
|
||||
WhatAttributesCanGoHereContext context) {
|
||||
// If the element from the current context is the 'table' element add the
|
||||
// attribute named 'frame' to the list of default content completion proposals
|
||||
if (context != null) {
|
||||
ContextElement contextElement = context.getParentElement();
|
||||
if ("table".equals(contextElement.getQName())) {
|
||||
CIAttribute frameAttribute = new CIAttribute();
|
||||
frameAttribute.setName("frame");
|
||||
frameAttribute.setRequired(false);
|
||||
frameAttribute.setFixed(false);
|
||||
frameAttribute.setDefaultValue("void");
|
||||
if (attributes == null) {
|
||||
attributes = new ArrayList<CIAttribute>();
|
||||
}
|
||||
attributes.add(frameAttribute);
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the value of the "title" element from the "section".
|
||||
*/
|
||||
public List<CIValue> filterElementValues(List<CIValue> elementValues,
|
||||
Context context) {
|
||||
// If the element from the current context is the title of the section element then add the
|
||||
// "Custom Section Title" value to the list of default content completion proposals
|
||||
if (context != null) {
|
||||
Stack<ContextElement> elementStack = context.getElementStack();
|
||||
if (elementStack != null) {
|
||||
ContextElement contextElement = elementStack.peek();
|
||||
if(contextElement != null) {
|
||||
int size = elementStack.size();
|
||||
if ("title".equals(contextElement.getQName())) {
|
||||
ContextElement parentElement = elementStack.elementAt(size - 2);
|
||||
if (parentElement != null && "section".equals(parentElement.getQName())) {
|
||||
elementValues = new ArrayList<CIValue>();
|
||||
CIValue val = new CIValue("Custom Section Title");
|
||||
elementValues.add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return elementValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter "header" elements.
|
||||
*/
|
||||
public List<CIElement> filterElements(List<CIElement> elements,
|
||||
WhatElementsCanGoHereContext context) {
|
||||
// If the element from the current context is the 'header' element remove the
|
||||
// 'td' element from the list of content completion proposals and add the
|
||||
// 'th' element.
|
||||
if (context != null) {
|
||||
Stack<ContextElement> elementStack = context.getElementStack();
|
||||
if (elementStack != null) {
|
||||
ContextElement contextElement = context.getElementStack().peek();
|
||||
if ("header".equals(contextElement.getQName())) {
|
||||
if (elements != null) {
|
||||
for (Iterator<CIElement> iterator = elements.iterator(); iterator.hasNext();) {
|
||||
CIElement element = iterator.next();
|
||||
// Remove the 'td' element
|
||||
if ("td".equals(element.getQName())) {
|
||||
elements.remove(element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
elements = new ArrayList<CIElement>();
|
||||
}
|
||||
// Insert the 'th' element in the list of content completion proposals
|
||||
CIElement thElement = new SDFElement();
|
||||
thElement.setName("th");
|
||||
elements.add(thElement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the given context is null then the given list of content completion elements contains
|
||||
// global elements.
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Implementation for the Simple Documentation Framework schema manager filter.";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ro.sync.ecss.css.EditorContent;
|
||||
import ro.sync.ecss.css.LabelContent;
|
||||
import ro.sync.ecss.css.StaticContent;
|
||||
import ro.sync.ecss.css.Styles;
|
||||
import ro.sync.ecss.extensions.api.StylesFilter;
|
||||
import ro.sync.ecss.extensions.api.editor.InplaceEditorArgumentKeys;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
|
||||
/**
|
||||
*
|
||||
* Style Filter for Simple Documentation Framework.
|
||||
*
|
||||
*/
|
||||
public class SDFStylesFilter implements StylesFilter {
|
||||
|
||||
/**
|
||||
* Filter the style of "para", "b" and "i" elements.
|
||||
*/
|
||||
public Styles filter(Styles styles, AuthorNode authorNode) {
|
||||
//Use fixed font size of 12 for "para".
|
||||
if(authorNode.getName().equals("para")) {
|
||||
ro.sync.exml.view.graphics.Font original = styles.getFont();
|
||||
ro.sync.exml.view.graphics.Font modified = new ro.sync.exml.view.graphics.Font(
|
||||
original.getName(), original.getStyle(), 12);
|
||||
styles.setProperty(Styles.KEY_FONT, modified);
|
||||
}
|
||||
|
||||
//Use foreground color red for "b"
|
||||
if(authorNode.getName().equals("b")) {
|
||||
ro.sync.exml.view.graphics.Color red = ro.sync.exml.view.graphics.Color.COLOR_RED;
|
||||
styles.setProperty(Styles.KEY_FOREGROUND_COLOR, red);
|
||||
}
|
||||
|
||||
//Use left border width of 5 pixels for "i"
|
||||
if(authorNode.getName().equals("i")) {
|
||||
styles.setProperty(Styles.KEY_BORDER_LEFT_WIDTH, new Integer(5));
|
||||
}
|
||||
|
||||
if(authorNode.getType() == AuthorNode.NODE_TYPE_PSEUDO_ELEMENT
|
||||
&& "before".equals(authorNode.getName())) {
|
||||
authorNode = authorNode.getParent();
|
||||
if ("country".equals(authorNode.getName())) {
|
||||
// This is the BEFORE pseudo element of the "country" element.
|
||||
// Read the supported countries from the configuration file.
|
||||
// This will be a comma separated enumeration: France, Spain, Great Britain
|
||||
String countries = readCountriesFromFile();
|
||||
Map<String, Object> formControlArgs = new HashMap<String, Object>();
|
||||
formControlArgs.put(InplaceEditorArgumentKeys.PROPERTY_EDIT_QUALIFIED, "#text");
|
||||
formControlArgs.put(InplaceEditorArgumentKeys.PROPERTY_TYPE, InplaceEditorArgumentKeys.TYPE_COMBOBOX);
|
||||
formControlArgs.put(InplaceEditorArgumentKeys.PROPERTY_VALUES, countries);
|
||||
formControlArgs.put(InplaceEditorArgumentKeys.PROPERTY_EDITABLE, "false");
|
||||
|
||||
// We also add a label in form of the form control.
|
||||
Map<String, Object> labelProps = new HashMap<String, Object>();
|
||||
labelProps.put("text", "Country: ");
|
||||
labelProps.put("styles", "* {width: 100px; color: gray;}");
|
||||
StaticContent[] mixedContent = new StaticContent[] {new LabelContent(labelProps), new EditorContent(formControlArgs)};
|
||||
styles.setProperty(Styles.KEY_MIXED_CONTENT, mixedContent);
|
||||
}
|
||||
}
|
||||
|
||||
// The previously added form control is the only way the element can be edited.
|
||||
if ("country".equals(authorNode.getName())) {
|
||||
styles.setProperty(Styles.KEY_VISIBITY, "-oxy-collapse-text");
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter is invoked every time the styles are needed so we will make sure
|
||||
* we load the countries only once.
|
||||
*/
|
||||
private String countries = null;
|
||||
|
||||
/**
|
||||
* Read the available countries from the configuration file.
|
||||
*
|
||||
* @return The supported countries, read from the configuration file.
|
||||
* This will be a comma separated enumeration: France, Spain, Great Britain.
|
||||
*/
|
||||
private String readCountriesFromFile() {
|
||||
if (countries == null) {
|
||||
StringBuilder countriesBuilder = new StringBuilder();
|
||||
// Our countries file is located in the framework folder. To compute
|
||||
// the framework location we will use the fact that the JAR this class is in
|
||||
// is located inside the framework folder.
|
||||
String classLocation = "/" + getClass().getCanonicalName().replace(".", "/") + ".class";
|
||||
URL resource = getClass().getResource(classLocation);
|
||||
|
||||
if (resource != null) {
|
||||
// The URL for the class looks like this:
|
||||
// jar:file:/D:/projects/eXml/frameworks/sdf/sdf.jar!/simple/documentation/framework/extensions/SDFStylesFilter.class
|
||||
String jarURL = resource.toString();
|
||||
// This is the URL of the JAR form where the class was loaded.
|
||||
jarURL = jarURL.substring(jarURL.indexOf(":") + 1, jarURL.indexOf("!/"));
|
||||
|
||||
try {
|
||||
// We know the resources are next to the JAR.
|
||||
URL resourceFile = new URL(new URL(jarURL), "resources/countries.txt");
|
||||
InputStream openStream = resourceFile.openStream();
|
||||
try {
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(openStream, "UTF8");
|
||||
char[] cbuf = new char[1024];
|
||||
int length = -1;
|
||||
while ((length = inputStreamReader.read(cbuf)) != -1) {
|
||||
countriesBuilder.append(cbuf, 0, length);
|
||||
}
|
||||
} finally {
|
||||
openStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
countries = countriesBuilder.toString();
|
||||
}
|
||||
|
||||
return countries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Implementation for the Simple Documentation Framework style filter.";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorTableCellSpanProvider;
|
||||
import ro.sync.ecss.extensions.api.node.AttrValue;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
|
||||
/**
|
||||
* Simple Documentation Framework table cell span provider.
|
||||
*
|
||||
*/
|
||||
public class TableCellSpanProvider implements AuthorTableCellSpanProvider {
|
||||
|
||||
/**
|
||||
* Extracts the integer specifying what is the width (in columns) of the cell
|
||||
* representing in the table layout the cell element.
|
||||
*/
|
||||
public Integer getColSpan(AuthorElement cell) {
|
||||
Integer colSpan = null;
|
||||
|
||||
AttrValue attrValue = cell.getAttribute("column_span");
|
||||
if(attrValue != null) {
|
||||
// The attribute was found.
|
||||
String cs = attrValue.getValue();
|
||||
if(cs != null) {
|
||||
try {
|
||||
colSpan = new Integer(cs);
|
||||
} catch (NumberFormatException ex) {
|
||||
// The attribute value was not a number.
|
||||
}
|
||||
}
|
||||
}
|
||||
return colSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the integer specifying what is the height (in rows) of the cell
|
||||
* representing in the table layout the cell element.
|
||||
*/
|
||||
public Integer getRowSpan(AuthorElement cell) {
|
||||
Integer rowSpan = null;
|
||||
|
||||
AttrValue attrValue = cell.getAttribute("row_span");
|
||||
if(attrValue != null) {
|
||||
// The attribute was found.
|
||||
String rs = attrValue.getValue();
|
||||
if(rs != null) {
|
||||
try {
|
||||
rowSpan = new Integer(rs);
|
||||
} catch (NumberFormatException ex) {
|
||||
// The attribute value was not a number.
|
||||
}
|
||||
}
|
||||
}
|
||||
return rowSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true considering the column specifications always available.
|
||||
*/
|
||||
public boolean hasColumnSpecifications(AuthorElement tableElement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignored. We do not extract data from the <code>table</code> element.
|
||||
*/
|
||||
public void init(AuthorElement table) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Implementation for the Simple Documentation Framework table layout.";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
package simple.documentation.framework.extensions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorDocumentController;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider;
|
||||
import ro.sync.ecss.extensions.api.WidthRepresentation;
|
||||
import ro.sync.ecss.extensions.api.node.AttrValue;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
|
||||
/**
|
||||
* Simple Documentation Framework table column width provider.
|
||||
*
|
||||
*/
|
||||
public class TableColumnWidthProvider implements AuthorTableColumnWidthProvider {
|
||||
|
||||
/**
|
||||
* Cols start offset
|
||||
*/
|
||||
private int colsStartOffset;
|
||||
|
||||
/**
|
||||
* Cols end offset
|
||||
*/
|
||||
private int colsEndOffset;
|
||||
|
||||
/**
|
||||
* Column widths specifications
|
||||
*/
|
||||
private List<WidthRepresentation> colWidthSpecs = new ArrayList<WidthRepresentation>();
|
||||
|
||||
/**
|
||||
* The table element
|
||||
*/
|
||||
private AuthorElement tableElement;
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#commitColumnWidthModifications(ro.sync.ecss.extensions.api.AuthorDocumentController, ro.sync.ecss.extensions.api.WidthRepresentation[], java.lang.String)
|
||||
*/
|
||||
public void commitColumnWidthModifications(AuthorDocumentController authorDocumentController,
|
||||
WidthRepresentation[] colWidths, String tableCellsTagName) throws AuthorOperationException {
|
||||
if ("td".equals(tableCellsTagName)) {
|
||||
if (colWidths != null && tableElement != null) {
|
||||
if (colsStartOffset >= 0 && colsEndOffset >= 0 && colsStartOffset < colsEndOffset) {
|
||||
authorDocumentController.delete(colsStartOffset,
|
||||
colsEndOffset);
|
||||
}
|
||||
String xmlFragment = createXMLFragment(colWidths);
|
||||
int offset = -1;
|
||||
AuthorElement[] header = tableElement.getElementsByLocalName("header");
|
||||
if (header != null && header.length > 0) {
|
||||
// Insert the cols elements before the 'header' element
|
||||
offset = header[0].getStartOffset();
|
||||
} else {
|
||||
AuthorElement[] title = tableElement.getElementsByLocalName("title");
|
||||
if (title != null && title.length > 0) {
|
||||
// Insert the cols elements after the 'title' element
|
||||
offset = title[0].getStartOffset() + 1;
|
||||
} else {
|
||||
// Just insert after table start tag
|
||||
offset = tableElement.getStartOffset() + 1;
|
||||
}
|
||||
}
|
||||
if (offset == -1) {
|
||||
throw new AuthorOperationException("No valid offset to insert the columns width specification.");
|
||||
}
|
||||
authorDocumentController.insertXMLFragment(xmlFragment, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the XML fragment representing the column specifications.
|
||||
*
|
||||
* @param widthRepresentations
|
||||
* @return The XML fragment as a string.
|
||||
*/
|
||||
private String createXMLFragment(WidthRepresentation[] widthRepresentations) {
|
||||
StringBuffer fragment = new StringBuffer();
|
||||
String ns = tableElement.getNamespace();
|
||||
for (int i = 0; i < widthRepresentations.length; i++) {
|
||||
WidthRepresentation width = widthRepresentations[i];
|
||||
fragment.append("<customcol");
|
||||
String strRepresentation = width.getWidthRepresentation();
|
||||
if (strRepresentation != null) {
|
||||
fragment.append(" width=\"" + width.getWidthRepresentation() + "\"");
|
||||
}
|
||||
if (ns != null && ns.length() > 0) {
|
||||
fragment.append(" xmlns=\"" + ns + "\"");
|
||||
}
|
||||
fragment.append("/>");
|
||||
}
|
||||
return fragment.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#commitTableWidthModification(ro.sync.ecss.extensions.api.AuthorDocumentController, int, java.lang.String)
|
||||
*/
|
||||
public void commitTableWidthModification(AuthorDocumentController authorDocumentController,
|
||||
int newTableWidth, String tableCellsTagName) throws AuthorOperationException {
|
||||
if ("td".equals(tableCellsTagName)) {
|
||||
if (newTableWidth > 0) {
|
||||
if (tableElement != null) {
|
||||
String newWidth = String.valueOf(newTableWidth);
|
||||
|
||||
authorDocumentController.setAttribute(
|
||||
"width",
|
||||
new AttrValue(newWidth),
|
||||
tableElement);
|
||||
} else {
|
||||
throw new AuthorOperationException("Cannot find the element representing the table.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#getCellWidth(ro.sync.ecss.extensions.api.node.AuthorElement, int, int)
|
||||
*/
|
||||
public List<WidthRepresentation> getCellWidth(AuthorElement cellElement, int colNumberStart,
|
||||
int colSpan) {
|
||||
List<WidthRepresentation> toReturn = null;
|
||||
int size = colWidthSpecs.size();
|
||||
if (size >= colNumberStart && size >= colNumberStart + colSpan) {
|
||||
toReturn = new ArrayList<WidthRepresentation>(colSpan);
|
||||
for (int i = colNumberStart; i < colNumberStart + colSpan; i ++) {
|
||||
// Add the column widths
|
||||
toReturn.add(colWidthSpecs.get(i));
|
||||
}
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#getTableWidth(java.lang.String)
|
||||
*/
|
||||
public WidthRepresentation getTableWidth(String tableCellsTagName) {
|
||||
WidthRepresentation toReturn = null;
|
||||
if (tableElement != null && "td".equals(tableCellsTagName)) {
|
||||
AttrValue widthAttr = tableElement.getAttribute("width");
|
||||
if (widthAttr != null) {
|
||||
String width = widthAttr.getValue();
|
||||
if (width != null) {
|
||||
toReturn = new WidthRepresentation(width, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#init(ro.sync.ecss.extensions.api.node.AuthorElement)
|
||||
*/
|
||||
public void init(AuthorElement tableElement) {
|
||||
this.tableElement = tableElement;
|
||||
AuthorElement[] colChildren = tableElement.getElementsByLocalName("customcol");
|
||||
if (colChildren != null && colChildren.length > 0) {
|
||||
for (int i = 0; i < colChildren.length; i++) {
|
||||
AuthorElement colChild = colChildren[i];
|
||||
if (i == 0) {
|
||||
colsStartOffset = colChild.getStartOffset();
|
||||
}
|
||||
if (i == colChildren.length - 1) {
|
||||
colsEndOffset = colChild.getEndOffset();
|
||||
}
|
||||
// Determine the 'width' for this col.
|
||||
AttrValue colWidthAttribute = colChild.getAttribute("width");
|
||||
String colWidth = null;
|
||||
if (colWidthAttribute != null) {
|
||||
colWidth = colWidthAttribute.getValue();
|
||||
// Add WidthRepresentation objects for the columns this 'customcol' specification
|
||||
// spans over.
|
||||
colWidthSpecs.add(new WidthRepresentation(colWidth, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isAcceptingFixedColumnWidths(java.lang.String)
|
||||
*/
|
||||
public boolean isAcceptingFixedColumnWidths(String tableCellsTagName) {
|
||||
return "td".equals(tableCellsTagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isAcceptingPercentageColumnWidths(java.lang.String)
|
||||
*/
|
||||
public boolean isAcceptingPercentageColumnWidths(String tableCellsTagName) {
|
||||
return "td".equals(tableCellsTagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isAcceptingProportionalColumnWidths(java.lang.String)
|
||||
*/
|
||||
public boolean isAcceptingProportionalColumnWidths(String tableCellsTagName) {
|
||||
return "td".equals(tableCellsTagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isTableAcceptingWidth(java.lang.String)
|
||||
*/
|
||||
public boolean isTableAcceptingWidth(String tableCellsTagName) {
|
||||
return "td".equals(tableCellsTagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorTableColumnWidthProvider#isTableAndColumnsResizable(java.lang.String)
|
||||
*/
|
||||
public boolean isTableAndColumnsResizable(String tableCellsTagName) {
|
||||
return "td".equals(tableCellsTagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Implementation for the Simple Documentation Framework table layout.";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package simple.documentation.framework.filters;
|
||||
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorDocumentFilter;
|
||||
import ro.sync.ecss.extensions.api.AuthorDocumentFilterBypass;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
|
||||
/**
|
||||
* Simple Documentation Framework document filter used to restrict insertion and deletion of
|
||||
* different nodes from the document when the strict mode is activated.
|
||||
*
|
||||
*/
|
||||
public class SDFDocumentFilter extends AuthorDocumentFilter {
|
||||
|
||||
/**
|
||||
* The author access.
|
||||
*/
|
||||
private final AuthorAccess authorAccess;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param access The author access.
|
||||
*/
|
||||
public SDFDocumentFilter(AuthorAccess access) {
|
||||
this.authorAccess = access;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the strict mode is activated
|
||||
* @return <code>True</code> if the strict mode is activated.
|
||||
*/
|
||||
private boolean isStrictModeActivated() {
|
||||
String strictMode = authorAccess.getOptionsStorage().getOption("strictMode", "false");
|
||||
return "true".equals(strictMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert node filter.
|
||||
*/
|
||||
@Override
|
||||
public boolean insertNode(AuthorDocumentFilterBypass filterBypass,
|
||||
int caretOffset, AuthorNode element) {
|
||||
// Restrict the insertion of the "title" element if the parent element already contains a
|
||||
// title element.
|
||||
if (isStrictModeActivated()) {
|
||||
String restrict = "title";
|
||||
if(element instanceof AuthorElement) {
|
||||
String elementName = ((AuthorElement) element).getLocalName();
|
||||
if (restrict.equals(elementName)) {
|
||||
try {
|
||||
AuthorNode nodeAtOffset = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
|
||||
if (nodeAtOffset != null && nodeAtOffset instanceof AuthorElement) {
|
||||
AuthorElement[] elements = ((AuthorElement) nodeAtOffset).getElementsByLocalName(restrict);
|
||||
if (elements != null && elements.length > 0) {
|
||||
AuthorElement titleChild = elements[0];
|
||||
if (titleChild != null) {
|
||||
authorAccess.getWorkspaceAccess().showInformationMessage("Title already added.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.insertNode(filterBypass, caretOffset, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert text filter.
|
||||
*/
|
||||
@Override
|
||||
public void insertText(AuthorDocumentFilterBypass filterBypass, int caretOffset,
|
||||
String toInsert) {
|
||||
super.insertText(filterBypass, caretOffset, toInsert);
|
||||
// If the strict mode is activated and the element where the text is inserted is the "content"
|
||||
// element then surround the inserted text into a "para" element.
|
||||
if (isStrictModeActivated()) {
|
||||
try {
|
||||
AuthorNode nodeAtOffset = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
|
||||
if (nodeAtOffset != null && nodeAtOffset instanceof AuthorElement) {
|
||||
if ("content".equals(((AuthorElement)nodeAtOffset).getLocalName())) {
|
||||
try {
|
||||
filterBypass.surroundInFragment("<para/>", caretOffset, caretOffset + toInsert.length() - 1);
|
||||
authorAccess.getEditorAccess().setCaretPosition(caretOffset + toInsert.length() + 1);
|
||||
} catch (AuthorOperationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package simple.documentation.framework.listeners;
|
||||
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorCaretEvent;
|
||||
import ro.sync.ecss.extensions.api.AuthorCaretListener;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
import ro.sync.exml.view.graphics.Rectangle;
|
||||
import ro.sync.exml.workspace.api.Platform;
|
||||
import simple.documentation.framework.ui.SDFPopupWindow;
|
||||
|
||||
/**
|
||||
* Author caret listener used to display the XML fragment corresponding to a para element
|
||||
* found at the caret position.
|
||||
*
|
||||
*/
|
||||
public class SDFAuthorCaretListener implements AuthorCaretListener {
|
||||
|
||||
/**
|
||||
* Access to the author specific functions.
|
||||
*/
|
||||
private AuthorAccess authorAcess;
|
||||
|
||||
/**
|
||||
* The popup used to display the XML fragment.
|
||||
*/
|
||||
private SDFPopupWindow popupWindow;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param authorAccess Access to the author specific functions.
|
||||
*/
|
||||
public SDFAuthorCaretListener(AuthorAccess authorAccess) {
|
||||
this.authorAcess = authorAccess;
|
||||
// Use the information popup only if this is the standalone Oxygen version.
|
||||
if (authorAccess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
|
||||
// Create the information popup window.
|
||||
popupWindow = new SDFPopupWindow(authorAccess, "XML Fragment:");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caret moved.
|
||||
* Display the XML fragment corresponding to a para element found at the caret
|
||||
* position in a popup window.
|
||||
*/
|
||||
public void caretMoved(AuthorCaretEvent caretEvent) {
|
||||
// Verify if the node corresponding to the new caret position corresponds to a "para" element.
|
||||
if (authorAcess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
|
||||
int caretOffset = authorAcess.getEditorAccess().getCaretOffset();
|
||||
try {
|
||||
AuthorNode nodeAtOffset = authorAcess.getDocumentController().getNodeAtOffset(caretOffset);
|
||||
if (nodeAtOffset != null && nodeAtOffset instanceof AuthorElement) {
|
||||
AuthorElement element = (AuthorElement) nodeAtOffset;
|
||||
if ("para".equals(element.getLocalName())) {
|
||||
AuthorDocumentFragment paraFragment = authorAcess.getDocumentController().createDocumentFragment(element, true);
|
||||
String serializeFragmentToXML = authorAcess.getDocumentController().serializeFragmentToXML(paraFragment);
|
||||
// Find the x and y coordinates from the caret shape (the popup window location).
|
||||
Rectangle modelToView = authorAcess.getEditorAccess().modelToViewRectangle(authorAcess.getEditorAccess().getCaretOffset());
|
||||
popupWindow.setTimeToDisplay(3);
|
||||
popupWindow.display(
|
||||
serializeFragmentToXML,
|
||||
modelToView.x + modelToView.width,
|
||||
modelToView.y + modelToView.height,
|
||||
10);
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package simple.documentation.framework.listeners;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AttributeChangedEvent;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorListenerAdapter;
|
||||
import ro.sync.ecss.extensions.api.DocumentContentDeletedEvent;
|
||||
import ro.sync.ecss.extensions.api.DocumentContentInsertedEvent;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
|
||||
/**
|
||||
* Simple Documentation Framework Author listener.
|
||||
*/
|
||||
public class SDFAuthorListener extends AuthorListenerAdapter {
|
||||
|
||||
/**
|
||||
* Access to the author specific functions.
|
||||
*/
|
||||
private AuthorAccess authorAccess;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param authorAccess Access to the author specific functions
|
||||
*/
|
||||
public SDFAuthorListener(AuthorAccess authorAccess) {
|
||||
this.authorAccess = authorAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorListenerAdapter#attributeChanged(ro.sync.ecss.extensions.api.AttributeChangedEvent)
|
||||
*/
|
||||
@Override
|
||||
public void attributeChanged(AttributeChangedEvent e) {
|
||||
String strictMode = authorAccess.getOptionsStorage().getOption("strictMode", "false");
|
||||
if ("true".equals(strictMode)) {
|
||||
// If the changed attribute is the "column_span" or "row_span" attribute of the "td"
|
||||
// element then verify if the new value is an integer value
|
||||
AuthorNode ownerAuthorNode = e.getOwnerAuthorNode();
|
||||
if (ownerAuthorNode instanceof AuthorElement) {
|
||||
AuthorElement ownerAuthorElement = (AuthorElement) ownerAuthorNode;
|
||||
if ("td".equals(ownerAuthorElement.getLocalName())) {
|
||||
String attributeName = e.getAttributeName();
|
||||
if ("column_span".equals(attributeName) || "row_span".equals(attributeName)) {
|
||||
String spanValue = ownerAuthorElement.getAttribute(attributeName).getValue();
|
||||
try {
|
||||
Integer.parseInt(spanValue);
|
||||
} catch (NumberFormatException ex) {
|
||||
authorAccess.getWorkspaceAccess().showInformationMessage("The value " + spanValue + " of attribute " + attributeName +
|
||||
" is not valid.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorListenerAdapter#beforeContentDelete(ro.sync.ecss.extensions.api.DocumentContentDeletedEvent)
|
||||
*/
|
||||
@Override
|
||||
public void beforeContentDelete(DocumentContentDeletedEvent deleteEvent) {
|
||||
String strictMode = authorAccess.getOptionsStorage().getOption("strictMode", "false");
|
||||
if ("true".equals(strictMode)) {
|
||||
// If the section title is deleted an error message will inform the user that the title
|
||||
// is required.
|
||||
if (deleteEvent.getType() != DocumentContentDeletedEvent.INSERT_TEXT_EVENT
|
||||
&& deleteEvent.getType() != DocumentContentDeletedEvent.DELETE_TEXT_EVENT) {
|
||||
AuthorNode changedNode = deleteEvent.getParentNode();
|
||||
if (changedNode instanceof AuthorElement) {
|
||||
AuthorElement changedElement = (AuthorElement) changedNode;
|
||||
// Section element
|
||||
if ("section".equals(changedElement.getLocalName())) {
|
||||
AuthorElement[] titleElements = changedElement.getElementsByLocalName("title");
|
||||
// If the section has one "title" child element
|
||||
if (titleElements.length == 1) {
|
||||
// Find if the deleted element is the "title" one.
|
||||
AuthorDocumentFragment deletedFragment = deleteEvent.getDeletedFragment();
|
||||
List<AuthorNode> contentNodes = deletedFragment.getContentNodes();
|
||||
for (AuthorNode authorNode : contentNodes) {
|
||||
if (authorNode instanceof AuthorElement) {
|
||||
if ("title".equals(((AuthorElement)authorNode).getLocalName())) {
|
||||
String errorMessage = "The section must have a title.";
|
||||
authorAccess.getWorkspaceAccess().showErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.beforeContentDelete(deleteEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorListenerAdapter#contentInserted(ro.sync.ecss.extensions.api.DocumentContentInsertedEvent)
|
||||
*/
|
||||
@Override
|
||||
public void contentInserted(DocumentContentInsertedEvent e) {
|
||||
AuthorNode node = e.getParentNode();
|
||||
AuthorNode parentNode = node.getParent();
|
||||
// For 'section' nodes the title text is rendered in the Outline
|
||||
// (see customizeRenderingInformation method from SDFAuthorOutlineCustomizer)
|
||||
// so we need to refresh the section node from the Outline when the title
|
||||
// text has changed.
|
||||
if ("title".equals(node.getName()) && "section".equals(parentNode.getName())) {
|
||||
authorAccess.getOutlineAccess().refreshNodes(new AuthorNode[] {parentNode});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorListenerAdapter#contentDeleted(ro.sync.ecss.extensions.api.DocumentContentDeletedEvent)
|
||||
*/
|
||||
@Override
|
||||
public void contentDeleted(DocumentContentDeletedEvent e) {
|
||||
AuthorNode node = e.getParentNode();
|
||||
AuthorNode parentNode = node.getParent();
|
||||
// For 'section' nodes the title text is rendered in the Outline
|
||||
// (see customizeRenderingInformation method from SDFAuthorOutlineCustomizer)
|
||||
// so we need to refresh the section node from the Outline when the title
|
||||
// text has changed.
|
||||
if ("title".equals(node.getName()) && "section".equals(parentNode.getName())) {
|
||||
authorAccess.getOutlineAccess().refreshNodes(new AuthorNode[] {parentNode});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package simple.documentation.framework.listeners;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorMouseEvent;
|
||||
import ro.sync.ecss.extensions.api.AuthorMouseListener;
|
||||
import ro.sync.exml.workspace.api.Platform;
|
||||
import simple.documentation.framework.ui.SDFPopupWindow;
|
||||
|
||||
/**
|
||||
* Custom author mouse listener used to display the mouse coordinates in a popup window
|
||||
* on mouse clicked.
|
||||
*
|
||||
*/
|
||||
public class SDFAuthorMouseListener implements AuthorMouseListener {
|
||||
|
||||
/**
|
||||
* Access to the author specific functions.
|
||||
*/
|
||||
private AuthorAccess authorAccess;
|
||||
|
||||
/**
|
||||
* The popup used to display the mouse coordinates.
|
||||
*/
|
||||
private SDFPopupWindow popupWindow;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param authorAccess Access to the author specific functions
|
||||
*/
|
||||
public SDFAuthorMouseListener(AuthorAccess authorAccess) {
|
||||
this.authorAccess = authorAccess;
|
||||
// Use the information popup only if this is the standalone Oxygen version.
|
||||
if (authorAccess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
|
||||
popupWindow = new SDFPopupWindow(authorAccess, "Position");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void mouseClicked(AuthorMouseEvent e) {
|
||||
// Display the mouse coordinates.
|
||||
if (authorAccess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
|
||||
if (e.clickCount == 2) {
|
||||
String toDisplay = "X: " + e.X + " Y: " + e.Y;
|
||||
popupWindow.setTimeToDisplay(2);
|
||||
popupWindow.display(toDisplay, e.X, e.Y, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mousePressed(AuthorMouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseReleased(AuthorMouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseDragged(AuthorMouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseMoved(AuthorMouseEvent e) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package simple.documentation.framework.operations;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.JFileChooser;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
import ro.sync.basic.util.URLUtil;
|
||||
|
||||
/**
|
||||
* Operation to save the Author node at caret in a separate document and refresh the new file path in the project.
|
||||
*/
|
||||
public class ExtractNodeToFileOperation implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args) throws IllegalArgumentException,
|
||||
AuthorOperationException {
|
||||
int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
|
||||
try {
|
||||
// Get node at caret
|
||||
AuthorNode nodeAtCaret = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setMultiSelectionEnabled(false);
|
||||
|
||||
// Show Save Dialog
|
||||
if (fileChooser.showSaveDialog((Component) authorAccess.getWorkspaceAccess().getParentFrame())
|
||||
== JFileChooser.APPROVE_OPTION) {
|
||||
File outputFile = fileChooser.getSelectedFile();
|
||||
FileOutputStream fos = new FileOutputStream(outputFile);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
|
||||
|
||||
// Write the node fragment
|
||||
AuthorDocumentFragment fragment = authorAccess.getDocumentController().createDocumentFragment(nodeAtCaret, true);
|
||||
String xmlFragment = authorAccess.getDocumentController().serializeFragmentToXML(fragment);
|
||||
writer.write(xmlFragment);
|
||||
writer.close();
|
||||
|
||||
// Open file
|
||||
URL outputFileUrl = URLUtil.correct(outputFile);
|
||||
authorAccess.getWorkspaceAccess().open(outputFileUrl);
|
||||
|
||||
// Refresh in project
|
||||
authorAccess.getWorkspaceAccess().refreshInProject(outputFileUrl);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
authorAccess.getWorkspaceAccess().showErrorMessage(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Save the Author node at caret in a separate document and refresh the new file path in the project";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package simple.documentation.framework.operations;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.event.PopupMenuEvent;
|
||||
import javax.swing.event.PopupMenuListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import ro.sync.contentcompletion.xml.CIElement;
|
||||
import ro.sync.contentcompletion.xml.WhatElementsCanGoHereContext;
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.AuthorSchemaManager;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
|
||||
import ro.sync.exml.view.graphics.Point;
|
||||
import ro.sync.exml.view.graphics.Rectangle;
|
||||
|
||||
/**
|
||||
* Show a popup menu that contains the name of all elements that can be inserted at the caret offset.
|
||||
*/
|
||||
public class InsertElementOperation implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public void doOperation(final AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
try {
|
||||
//Get the caret offset
|
||||
final int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
|
||||
//The schema manager
|
||||
final AuthorSchemaManager schemaManager = authorAccess.getDocumentController().getAuthorSchemaManager();
|
||||
//The context of elements.
|
||||
WhatElementsCanGoHereContext ctxt = schemaManager.createWhatElementsCanGoHereContext(caretOffset);
|
||||
//Get the list of elements which can be inserted here
|
||||
final List<CIElement> childrenElements = schemaManager.getChildrenElements(ctxt);
|
||||
JPopupMenu jpo = new JPopupMenu();
|
||||
for (int i = 0; i < childrenElements.size(); i++) {
|
||||
final int index = i;
|
||||
jpo.add(new JMenuItem(new AbstractAction(childrenElements.get(index).getName()) {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
CIElement toInsert = childrenElements.get(index);
|
||||
try {
|
||||
//The CIElement contains all data necessary to make a small XML fragment with
|
||||
//the string to insert and then call
|
||||
// authorAccess.getDocumentController().createNewDocumentFragmentInContext(xmlFragment, caretOffset);
|
||||
//But Oxygen 11.2 will come with a easier method:
|
||||
//Create a document fragment from the CIElement.
|
||||
AuthorDocumentFragment frag = schemaManager.createAuthorDocumentFragment(toInsert);
|
||||
//Now you can process the fragment and remove/add attributes.
|
||||
authorAccess.getDocumentController().insertFragment(caretOffset, frag);
|
||||
} catch (BadLocationException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
Rectangle mtv = authorAccess.getEditorAccess().modelToViewRectangle(caretOffset);
|
||||
Point popupLocation = authorAccess.getEditorAccess().getLocationOnScreenAsPoint(mtv.x, mtv.y);
|
||||
jpo.setLocation(popupLocation.x, popupLocation.y);
|
||||
JFrame oxygenFrame = (JFrame)authorAccess.getWorkspaceAccess().getParentFrame();
|
||||
|
||||
// Get the author component
|
||||
JPanel authorComponent = (JPanel)authorAccess.getEditorAccess().getAuthorComponent();
|
||||
// Get the glass pane
|
||||
final Component glassPane = authorComponent.getRootPane().getGlassPane();
|
||||
if (glassPane != null) {
|
||||
glassPane.setVisible(true);
|
||||
// Set wait cursor
|
||||
glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
}
|
||||
|
||||
// Show popup menu
|
||||
jpo.show(oxygenFrame, popupLocation.x - oxygenFrame.getLocation().x, popupLocation.y - oxygenFrame.getLocation().y);
|
||||
|
||||
// Add a popup menu listener
|
||||
jpo.addPopupMenuListener(new PopupMenuListener() {
|
||||
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
|
||||
}
|
||||
|
||||
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
|
||||
// Reset cursor to default
|
||||
if (glassPane != null) {
|
||||
glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
glassPane.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void popupMenuCanceled(PopupMenuEvent e) {
|
||||
}
|
||||
});
|
||||
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Insert element at the caret position.";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
package simple.documentation.framework.operations;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
|
||||
|
||||
/**
|
||||
* Insert image operation.
|
||||
*/
|
||||
public class InsertImageOperation implements AuthorOperation {
|
||||
|
||||
//
|
||||
// Implementing the Author Operation Interface.
|
||||
//
|
||||
|
||||
/**
|
||||
* Performs the operation.
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess,
|
||||
ArgumentsMap arguments)
|
||||
throws IllegalArgumentException,
|
||||
AuthorOperationException {
|
||||
|
||||
JFrame oxygenFrame = (JFrame) authorAccess.getWorkspaceAccess().getParentFrame();
|
||||
String href = displayURLDialog(oxygenFrame);
|
||||
if (href.length() != 0) {
|
||||
// Creates the image XML fragment.
|
||||
String imageFragment =
|
||||
"<image xmlns='http://www.oxygenxml.com/sample/documentation'" +
|
||||
" href='" + href + "'/>";
|
||||
|
||||
// Inserts this fragment at the caret position.
|
||||
int caretPosition = authorAccess.getEditorAccess().getCaretOffset();
|
||||
authorAccess.getDocumentController().insertXMLFragment(imageFragment, caretPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Has no arguments.
|
||||
*
|
||||
* @return null.
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A description of the operation.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Inserts an image element. Asks the" +
|
||||
" user for a URL reference.";
|
||||
}
|
||||
|
||||
//
|
||||
// End of interface implementation.
|
||||
//
|
||||
|
||||
//
|
||||
// Auxiliary methods.
|
||||
//
|
||||
|
||||
/**
|
||||
* Displays the URL dialog.
|
||||
*
|
||||
* @param parentFrame The parent frame for
|
||||
* the dialog.
|
||||
* @return The selected URL string value,
|
||||
* or the empty string if the user canceled
|
||||
* the URL selection.
|
||||
*/
|
||||
private String displayURLDialog(JFrame parentFrame) {
|
||||
|
||||
final JDialog dlg = new JDialog(parentFrame,
|
||||
"Enter the value for the href attribute", true);
|
||||
JPanel mainContent = new JPanel(new GridBagLayout());
|
||||
|
||||
// The text field.
|
||||
GridBagConstraints cstr = new GridBagConstraints();
|
||||
cstr.gridx = 0;
|
||||
cstr.gridy = 0;
|
||||
cstr.weightx = 0;
|
||||
cstr.gridwidth = 1;
|
||||
cstr.fill = GridBagConstraints.HORIZONTAL;
|
||||
mainContent.add(new JLabel("Image URI:"), cstr);
|
||||
|
||||
cstr.gridx = 1;
|
||||
cstr.weightx = 1;
|
||||
final JTextField urlField = new JTextField();
|
||||
urlField.setColumns(15);
|
||||
mainContent.add(urlField, cstr);
|
||||
|
||||
// Add the "Browse button."
|
||||
cstr.gridx = 2;
|
||||
cstr.weightx = 0;
|
||||
JButton browseButton = new JButton("Browse");
|
||||
browseButton.addActionListener(new ActionListener() {
|
||||
|
||||
/**
|
||||
* Shows a file chooser dialog.
|
||||
*/
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
|
||||
fileChooser.setMultiSelectionEnabled(false);
|
||||
// Accepts only the image files.
|
||||
fileChooser.setFileFilter(new FileFilter() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Image files";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File f) {
|
||||
String fileName = f.getName();
|
||||
return f.isFile() &&
|
||||
( fileName.endsWith(".jpeg")
|
||||
|| fileName.endsWith(".jpg")
|
||||
|| fileName.endsWith(".gif")
|
||||
|| fileName.endsWith(".png")
|
||||
|| fileName.endsWith(".svg"));
|
||||
}
|
||||
});
|
||||
if (fileChooser.showOpenDialog(dlg)
|
||||
== JFileChooser.APPROVE_OPTION) {
|
||||
File file = fileChooser.getSelectedFile();
|
||||
try {
|
||||
// Set the file into the text field.
|
||||
urlField.setText(file.toURI().toURL().toString());
|
||||
} catch (MalformedURLException ex) {
|
||||
// This should not happen.
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mainContent.add(browseButton, cstr);
|
||||
|
||||
// Add the "Ok" button to the layout.
|
||||
cstr.gridx = 0;
|
||||
cstr.gridy = 1;
|
||||
cstr.weightx = 0;
|
||||
JButton okButton = new JButton("Ok");
|
||||
okButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
dlg.setVisible(false);
|
||||
}
|
||||
});
|
||||
mainContent.add(okButton, cstr);
|
||||
mainContent.setBorder(
|
||||
BorderFactory.createEmptyBorder(10, 5, 10, 5));
|
||||
|
||||
// Add the "Cancel" button to the layout.
|
||||
cstr.gridx = 2;
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
cancelButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
urlField.setText("");
|
||||
dlg.setVisible(false);
|
||||
}
|
||||
});
|
||||
mainContent.add(cancelButton, cstr);
|
||||
|
||||
// When the user closes the dialog
|
||||
// from the window decoration,
|
||||
// assume "Cancel" action.
|
||||
dlg.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
urlField.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
dlg.getContentPane().add(mainContent);
|
||||
dlg.pack();
|
||||
dlg.setLocationRelativeTo(parentFrame);
|
||||
dlg.setVisible(true);
|
||||
return urlField.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method.
|
||||
*
|
||||
* @param args The arguments are ignored.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
InsertImageOperation operation =
|
||||
new InsertImageOperation();
|
||||
System.out.println("Choosen URL: " +
|
||||
operation.displayURLDialog(new JFrame()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package simple.documentation.framework.operations;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
|
||||
/**
|
||||
* Open the selected text in a new TXT editor.
|
||||
*/
|
||||
public class OpenInNewEditor implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap arg1) throws IllegalArgumentException,
|
||||
AuthorOperationException {
|
||||
String selectedText = authorAccess.getEditorAccess().getSelectedText();
|
||||
// Open in new editor
|
||||
authorAccess.getWorkspaceAccess().createNewEditor("txt", null, selectedText);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
// No arguments
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Open new editor";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
package simple.documentation.framework.operations;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
|
||||
/**
|
||||
* Query DB operation.
|
||||
*/
|
||||
public class QueryDatabaseOperation implements AuthorOperation{
|
||||
private static String ARG_JDBC_DRIVER ="jdbc_driver";
|
||||
private static String ARG_USER ="user";
|
||||
private static String ARG_PASSWORD ="password";
|
||||
private static String ARG_SQL ="sql";
|
||||
private static String ARG_CONNECTION ="connection";
|
||||
|
||||
/**
|
||||
* @return The array of arguments the developer must specify when
|
||||
* configuring the action.
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
ArgumentDescriptor args[] = new ArgumentDescriptor[] {
|
||||
new ArgumentDescriptor(
|
||||
ARG_JDBC_DRIVER,
|
||||
ArgumentDescriptor.TYPE_STRING,
|
||||
"The name of the Java class that is the JDBC driver."),
|
||||
new ArgumentDescriptor(
|
||||
ARG_CONNECTION,
|
||||
ArgumentDescriptor.TYPE_STRING,
|
||||
"The database URL connection string."),
|
||||
new ArgumentDescriptor(
|
||||
ARG_USER,
|
||||
ArgumentDescriptor.TYPE_STRING,
|
||||
"The name of the database user."),
|
||||
new ArgumentDescriptor(
|
||||
ARG_PASSWORD,
|
||||
ArgumentDescriptor.TYPE_STRING,
|
||||
"The database password."),
|
||||
new ArgumentDescriptor(
|
||||
ARG_SQL,
|
||||
ArgumentDescriptor.TYPE_STRING,
|
||||
"The SQL statement to be executed.")
|
||||
};
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The operation description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Executes a database query and puts the result in a table.";
|
||||
}
|
||||
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap map)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
// Collects the arguments.
|
||||
String jdbcDriver =
|
||||
(String)map.getArgumentValue(ARG_JDBC_DRIVER);
|
||||
String connection =
|
||||
(String)map.getArgumentValue(ARG_CONNECTION);
|
||||
String user =
|
||||
(String)map.getArgumentValue(ARG_USER);
|
||||
String password =
|
||||
(String)map.getArgumentValue(ARG_PASSWORD);
|
||||
String sql =
|
||||
(String)map.getArgumentValue(ARG_SQL);
|
||||
int caretPosition = authorAccess.getEditorAccess().getCaretOffset();
|
||||
try {
|
||||
if (jdbcDriver == null) {
|
||||
throw new AuthorOperationException("No jdbc driver provided.");
|
||||
}
|
||||
if (connection == null) {
|
||||
throw new AuthorOperationException("No connection provided.");
|
||||
}
|
||||
if (user == null) {
|
||||
throw new AuthorOperationException("No user provided.");
|
||||
}
|
||||
if (sql == null) {
|
||||
throw new AuthorOperationException("No sql provided.");
|
||||
}
|
||||
authorAccess.getDocumentController().insertXMLFragment(
|
||||
getFragment(jdbcDriver, connection, user, password, sql),
|
||||
caretPosition);
|
||||
} catch (SQLException e) {
|
||||
throw new AuthorOperationException(
|
||||
"The operation failed due to the following database error: " +
|
||||
e.getMessage(), e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AuthorOperationException(
|
||||
"The JDBC database driver was not found. Tried to load ' " +
|
||||
jdbcDriver + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a connection to the database, executes
|
||||
* the SQL statement and creates an XML fragment
|
||||
* containing a table element that wraps the data
|
||||
* from the result set.
|
||||
*
|
||||
*
|
||||
* @param jdbcDriver The class name of the JDBC driver.
|
||||
* @param connectionURL The connection URL.
|
||||
* @param user The database user.
|
||||
* @param password The password.
|
||||
* @param sql The SQL statement.
|
||||
* @return The string containing the XML fragment.
|
||||
*
|
||||
* @throws SQLException thrown when there is a
|
||||
* problem accessing the database or there are
|
||||
* erors in the SQL expression.
|
||||
* @throws ClassNotFoundException when the JDBC
|
||||
* driver class could not be loaded.
|
||||
*/
|
||||
private String getFragment(
|
||||
String jdbcDriver,
|
||||
String connectionURL,
|
||||
String user,
|
||||
String password,
|
||||
String sql) throws
|
||||
SQLException,
|
||||
ClassNotFoundException {
|
||||
Properties pr = new Properties();
|
||||
pr.put("characterEncoding", "UTF8");
|
||||
pr.put("useUnicode", "TRUE");
|
||||
pr.put("user", user);
|
||||
pr.put("password", password);
|
||||
// Loads the database driver.
|
||||
Class.forName(jdbcDriver);
|
||||
// Opens the connection
|
||||
Connection connection =
|
||||
DriverManager.getConnection(connectionURL, pr);
|
||||
java.sql.Statement statement =
|
||||
connection.createStatement();
|
||||
ResultSet resultSet =
|
||||
statement.executeQuery(sql);
|
||||
StringBuffer fragmentBuffer = new StringBuffer();
|
||||
fragmentBuffer.append(
|
||||
"<table xmlns='http://www.oxygenxml.com/sample/documentation'>");
|
||||
//
|
||||
// Creates the table header.
|
||||
//
|
||||
fragmentBuffer.append("<header>");
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
int columnCount = metaData.getColumnCount();
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
fragmentBuffer.append("<td>");
|
||||
fragmentBuffer.append(
|
||||
xmlEscape(metaData.getColumnName(i)));
|
||||
fragmentBuffer.append("</td>");
|
||||
}
|
||||
fragmentBuffer.append("</header>");
|
||||
//
|
||||
// Creates the table content.
|
||||
//
|
||||
while (resultSet.next()) {
|
||||
fragmentBuffer.append("<tr>");
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
fragmentBuffer.append("<td>");
|
||||
fragmentBuffer.append(
|
||||
xmlEscape(resultSet.getObject(i)));
|
||||
fragmentBuffer.append("</td>");
|
||||
}
|
||||
fragmentBuffer.append("</tr>");
|
||||
}
|
||||
fragmentBuffer.append("</table>");
|
||||
// Cleanup
|
||||
resultSet.close();
|
||||
statement.close();
|
||||
connection.close();
|
||||
return fragmentBuffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Some of the values from the database table
|
||||
* may contain characters that must be escaped
|
||||
* in XML, to ensure the fragment is well formed.
|
||||
*
|
||||
* @param object The object from the database.
|
||||
* @return The escaped string representation.
|
||||
*/
|
||||
private String xmlEscape(Object object) {
|
||||
String str = String.valueOf(object);
|
||||
return str.
|
||||
replaceAll("&", "&").
|
||||
replaceAll("<", "<");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package simple.documentation.framework.operations;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.access.AuthorEditorAccess;
|
||||
|
||||
/**
|
||||
* Refresh CSS operation.
|
||||
*
|
||||
*/
|
||||
public class SDFRefreshCSSOperation implements AuthorOperation {
|
||||
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
AuthorEditorAccess access = authorAccess.getEditorAccess();
|
||||
// Reload the CSS files and perform a refresh on the whole document to recompute
|
||||
// the layout
|
||||
access.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments.
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
// No arguments
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Refresh CSS operation for Simple Documentation Framework";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package simple.documentation.framework.operations;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
|
||||
/**
|
||||
* Show file status operation.
|
||||
*
|
||||
*/
|
||||
public class SDFShowFileStatusOperation implements AuthorOperation {
|
||||
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
// Build the file status message.
|
||||
StringBuilder message = new StringBuilder();
|
||||
// Editor location
|
||||
message.append("Location: " + authorAccess.getEditorAccess().getEditorLocation() + "\n");
|
||||
// Determine if the document from the editor contains unsaved modifications.
|
||||
message.append("Modified: " + authorAccess.getEditorAccess().isModified() + "\n");
|
||||
// Determine if the document from the editor was ever saved.
|
||||
message.append("Untitled: " + authorAccess.getEditorAccess().isNewDocument());
|
||||
|
||||
// Show the informations about the file status
|
||||
authorAccess.getWorkspaceAccess().showInformationMessage(message.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments.
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
// No arguments
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Show the status of the current file";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package simple.documentation.framework.operations;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Strict Mode Operation used to change the permissions to change parts of the document content.
|
||||
*
|
||||
*/
|
||||
public class SDFStrictModeOperation implements AuthorOperation {
|
||||
|
||||
// The strict mode key used to store the strict mode option.
|
||||
private String strictModeOptionKey = "strictMode";
|
||||
|
||||
/**
|
||||
* The Strict mode has changed.
|
||||
*/
|
||||
public void doOperation(final AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
|
||||
// Get the strict mode option value from the option storage.
|
||||
String strictMode = authorAccess.getOptionsStorage().getOption(strictModeOptionKey, "false");
|
||||
boolean enabled = Boolean.parseBoolean(strictMode);
|
||||
|
||||
// Change the strict mode option state
|
||||
enabled = !enabled;
|
||||
|
||||
// Save the new value of the strict mode option
|
||||
authorAccess.getOptionsStorage().setOption(strictModeOptionKey, String.valueOf(enabled));
|
||||
|
||||
// Show the strict mode operation status.
|
||||
String statusMessage = "Strict Mode: " + (enabled ? " ON " : "OFF");
|
||||
authorAccess.getWorkspaceAccess().showStatusMessage(statusMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments.
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
// No arguments
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Strict mode operation";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package simple.documentation.framework.operations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorDocument;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorElement;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
import ro.sync.ecss.extensions.api.structure.AuthorOutlineCustomizer;
|
||||
import simple.documentation.framework.extensions.SDFAuthorOutlineCustomizer;
|
||||
|
||||
/**
|
||||
* Select in the Outline the children nodes of the node at caret.
|
||||
*/
|
||||
public class SelectNodesInOutlinerOperation implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
// Renderer customizer used to find if a given node is filtered in the Outline.
|
||||
AuthorOutlineCustomizer rendererCustomizer = new SDFAuthorOutlineCustomizer();
|
||||
|
||||
int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
|
||||
try {
|
||||
// Node at caret position
|
||||
AuthorNode currentNode = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
|
||||
if (currentNode instanceof AuthorElement) {
|
||||
AuthorElement currentElement = (AuthorElement)currentNode;
|
||||
// Find the content nodes
|
||||
List<AuthorNode> contentNodes = currentElement.getContentNodes();
|
||||
List<TreePath> selectTreePaths = new ArrayList<TreePath>();
|
||||
LinkedList<AuthorNode> reversedPath = findReversedPath(authorAccess, rendererCustomizer,
|
||||
currentNode);
|
||||
|
||||
for (AuthorNode authorNode : contentNodes) {
|
||||
if (!rendererCustomizer.ignoreNode(authorNode)) {
|
||||
LinkedList<AuthorNode> pathList = new LinkedList<AuthorNode>(reversedPath);
|
||||
pathList.add(authorNode);
|
||||
// Add the children tree path in the selected tree paths list
|
||||
selectTreePaths.add(new TreePath(pathList.toArray(new AuthorNode[0])));
|
||||
}
|
||||
}
|
||||
|
||||
// Select the children tree paths in the Outline
|
||||
authorAccess.getOutlineAccess().setSelectionPaths(selectTreePaths.toArray(new TreePath[0]));
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Select nodes in the Outliner";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the list of node parents up to and including the root node, where the
|
||||
* original node is the last element in the returned array.
|
||||
*
|
||||
* @param authorAccess The author access.
|
||||
* @param rendererCustomizer Renderer customizer used to find if a given node
|
||||
* is filtered in the Outline.
|
||||
* @param node The node to reverse path for.
|
||||
* @return The path nodes list.
|
||||
*/
|
||||
private LinkedList<AuthorNode> findReversedPath(AuthorAccess authorAccess,
|
||||
AuthorOutlineCustomizer rendererCustomizer, AuthorNode node) {
|
||||
AuthorDocument document = authorAccess.getDocumentController().getAuthorDocumentNode();
|
||||
|
||||
// Builds the path.
|
||||
LinkedList<AuthorNode> reversedPath = new LinkedList<AuthorNode>();
|
||||
while (node != null && !rendererCustomizer.ignoreNode(node)) {
|
||||
reversedPath.addFirst(node);
|
||||
if (node == document) {
|
||||
// Just added root.
|
||||
break;
|
||||
}
|
||||
node = node.getParent();
|
||||
}
|
||||
return reversedPath;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package simple.documentation.framework.operations;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.StringWriter;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.sax.SAXSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.access.AuthorXMLUtilAccess;
|
||||
|
||||
/**
|
||||
* Custom transform operation using Saxon EE transformer.
|
||||
*/
|
||||
public class TrasformOperation implements AuthorOperation {
|
||||
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap arguments)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
// Choose the XSLT used in the transform operation
|
||||
File xsltFile = authorAccess.getWorkspaceAccess().chooseFile(
|
||||
// Title
|
||||
"Choose the XSLT",
|
||||
// Extensions
|
||||
new String[] {"xsl", "xslt"},
|
||||
// Filter description
|
||||
"XSLT files",
|
||||
// Open for save
|
||||
false);
|
||||
|
||||
if (xsltFile != null) {
|
||||
try {
|
||||
String xslSystemId = xsltFile.toURI().toURL().toString();
|
||||
// Create the XSLT source used in transform operation
|
||||
Source xslSource = new SAXSource(new InputSource(xslSystemId));
|
||||
// Create a XSL transformer without Oxygen options
|
||||
Transformer xslTransformer = authorAccess.getXMLUtilAccess().createXSLTTransformer(xslSource, null, AuthorXMLUtilAccess.TRANSFORMER_SAXON_ENTERPRISE_EDITION, false);
|
||||
Source editorSource = new SAXSource(new InputSource(authorAccess.getEditorAccess().createContentReader()));
|
||||
StringWriter result = new StringWriter();
|
||||
// Transform the current document using the specified XSLT
|
||||
xslTransformer.transform(editorSource, new StreamResult(result));
|
||||
StringBuffer resultBuffer = result.getBuffer();
|
||||
// Display the result
|
||||
authorAccess.getWorkspaceAccess().showInformationMessage("Transformation result: " + resultBuffer.toString());
|
||||
authorAccess.getWorkspaceAccess().showStatusMessage("Transformation finished");
|
||||
} catch (TransformerConfigurationException e) {
|
||||
// Display the error message
|
||||
authorAccess.getWorkspaceAccess().showErrorMessage(e.getMessage());
|
||||
} catch (TransformerException e) {
|
||||
// Display the error message
|
||||
authorAccess.getWorkspaceAccess().showErrorMessage(e.getMessage());
|
||||
} catch (MalformedURLException e) {
|
||||
// Display the error message
|
||||
authorAccess.getWorkspaceAccess().showErrorMessage(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Custom transform operation using Saxon EE transformer.";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package simple.documentation.framework.operations.highlight;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
|
||||
|
||||
/**
|
||||
* Operation used to highlight element from the caret position.
|
||||
*/
|
||||
public class AddHighlightOperation implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap map) throws IllegalArgumentException,
|
||||
AuthorOperationException {
|
||||
// Show dialog for adding highlight comment
|
||||
AuthorPersistentHighlighter highlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
|
||||
EditHighlightsDialog commentDlg = new EditHighlightsDialog(
|
||||
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame(),
|
||||
"Add highlight comment",
|
||||
true,
|
||||
null,
|
||||
authorAccess);
|
||||
commentDlg.showDialog();
|
||||
if (commentDlg.getResult() == OKCancelDialog.RESULT_OK) {
|
||||
int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
|
||||
try {
|
||||
// Highlight the node at caret
|
||||
AuthorNode nodeAtCaret = authorAccess.getDocumentController().getNodeAtOffset(caretOffset);
|
||||
String authorName = authorAccess.getReviewController().getReviewerAuthorName();
|
||||
String timestamp = authorAccess.getReviewController().getCurrentTimestamp();
|
||||
|
||||
// Compose highlight properties
|
||||
LinkedHashMap<String, String> properties = new LinkedHashMap<String, String>();
|
||||
properties.put(HighlightProperties.ID, timestamp);
|
||||
properties.put(HighlightProperties.AUTHOR, authorName);
|
||||
properties.put(HighlightProperties.COMMENT, commentDlg.getComment());
|
||||
// Add highlight
|
||||
highlighter.addHighlight(nodeAtCaret.getStartOffset(), nodeAtCaret.getEndOffset(), properties);
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Highlight element from the caret position.";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package simple.documentation.framework.operations.highlight;
|
||||
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
|
||||
|
||||
/**
|
||||
* Dialog used for changing the current review author
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ChangeReviewAuthorDialog extends OKCancelDialog {
|
||||
/**
|
||||
* Combo box containing all possible author names
|
||||
*/
|
||||
private JComboBox<String> authorNamesComboBox;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param parentFrame The parent frame.
|
||||
* @param title The dialog title.
|
||||
* @param authorNames All the possible author names.
|
||||
*/
|
||||
public ChangeReviewAuthorDialog(
|
||||
JFrame parentFrame,
|
||||
String title,
|
||||
String[] authorNames) {
|
||||
super(parentFrame, title, true);
|
||||
// Add label
|
||||
add(new JLabel("Choose Review Author: "));
|
||||
// Add the combobox containing possible author names
|
||||
authorNamesComboBox = new JComboBox<String>(authorNames);
|
||||
add(authorNamesComboBox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the dialog.
|
||||
*/
|
||||
public void showDialog() {
|
||||
setLocationRelativeTo(null);
|
||||
pack();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected author name.
|
||||
*
|
||||
* @return The selected author name.
|
||||
*/
|
||||
public String getSelectedAuthorName() {
|
||||
return (String) authorNamesComboBox.getSelectedItem();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package simple.documentation.framework.operations.highlight;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
|
||||
|
||||
/**
|
||||
* Operation that allow changing the author.
|
||||
*/
|
||||
public class ChangeReviewAuthorOperation implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
ChangeReviewAuthorDialog commentDlg = new ChangeReviewAuthorDialog(
|
||||
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame(),
|
||||
"Change Review Author",
|
||||
new String[] {"Author_1", "Author_2", "Author_3", "Default"});
|
||||
// Show the dialog
|
||||
commentDlg.showDialog();
|
||||
if (commentDlg.getResult() == OKCancelDialog.RESULT_OK) {
|
||||
String reviewerAuthorName = commentDlg.getSelectedAuthorName();
|
||||
// If the the reviewer author name is set to null, the default author name is used.
|
||||
reviewerAuthorName = "Default".equals(reviewerAuthorName) ? null : reviewerAuthorName;
|
||||
// Set the reviewer author name
|
||||
authorAccess.getReviewController().setReviewerAuthorName(reviewerAuthorName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Change review author name";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
package simple.documentation.framework.operations.highlight;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.border.BevelBorder;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
|
||||
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
|
||||
|
||||
/**
|
||||
* Dialog used for adding or editing highlights.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EditHighlightsDialog extends OKCancelDialog {
|
||||
/**
|
||||
* Information label (presents highlight ID and author).
|
||||
*/
|
||||
private JLabel infoLabel;
|
||||
/**
|
||||
* Comment area.
|
||||
*/
|
||||
private JTextArea commentArea;
|
||||
/**
|
||||
* Map between highlight and edited properties.
|
||||
*/
|
||||
private Map<AuthorPersistentHighlight, LinkedHashMap<String, String>> mapHighlightsToProps;
|
||||
/**
|
||||
* Current edited highlight index.
|
||||
*/
|
||||
private final int[] currentIndex = new int[1];
|
||||
/**
|
||||
* Current edited highlight.
|
||||
*/
|
||||
private AuthorPersistentHighlight currentHighlight;
|
||||
/**
|
||||
* The Author access.
|
||||
*/
|
||||
private final AuthorAccess authorAccess;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param parentFrame The parent frame.
|
||||
* @param title The dialog title.
|
||||
* @param modal <code>true</code> if modal.
|
||||
* @param highlights List of highlights to be edited.
|
||||
* @param auhorAccess The Author access
|
||||
*/
|
||||
public EditHighlightsDialog(
|
||||
JFrame parentFrame,
|
||||
String title,
|
||||
boolean modal,
|
||||
final List<AuthorPersistentHighlight> highlights,
|
||||
final AuthorAccess auhorAccess) {
|
||||
super(parentFrame, title, modal);
|
||||
this.authorAccess = auhorAccess;
|
||||
|
||||
boolean editHighlights = highlights != null && highlights.size() > 0;
|
||||
|
||||
commentArea = new JTextArea();
|
||||
commentArea.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
|
||||
|
||||
// Add scroll pane
|
||||
JScrollPane commentAreaPane = new JScrollPane(commentArea);
|
||||
commentAreaPane.setPreferredSize(new Dimension(400, 200));
|
||||
commentArea.setMargin(new Insets(10, 10, 10 , 10));
|
||||
// Add comment area
|
||||
add(commentAreaPane, BorderLayout.CENTER);
|
||||
|
||||
if (editHighlights) {
|
||||
mapHighlightsToProps = new LinkedHashMap<AuthorPersistentHighlight, LinkedHashMap<String,String>>();
|
||||
currentHighlight = highlights.get(0);
|
||||
JPanel northPanel = new JPanel();
|
||||
infoLabel = new JLabel();
|
||||
northPanel.add(infoLabel);
|
||||
// Add next button if necessary
|
||||
if (highlights.size() > 1) {
|
||||
final JButton nextButton = new JButton("Next");
|
||||
nextButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
saveCurrentHighlightProps(currentHighlight);
|
||||
|
||||
currentIndex[0] = currentIndex[0] + 1;
|
||||
currentHighlight = highlights.get(currentIndex[0]);
|
||||
|
||||
// Display next highlight
|
||||
displayHighlightProps(currentHighlight);
|
||||
|
||||
// Disable next button if necessary
|
||||
if (highlights.size() == currentIndex[0] + 1) {
|
||||
nextButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Add next button
|
||||
northPanel.add(nextButton);
|
||||
}
|
||||
add(northPanel, BorderLayout.NORTH);
|
||||
|
||||
// Display properties for the current highlight
|
||||
displayHighlightProps(currentHighlight);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCurrentHighlightProps(AuthorPersistentHighlight highlight) {
|
||||
// Save edited properties for the current map
|
||||
LinkedHashMap<String, String> props = highlight.getClonedProperties();
|
||||
props.put(HighlightProperties.COMMENT, commentArea.getText());
|
||||
mapHighlightsToProps.put(highlight, props);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.commons.ui.OKCancelDialog#doOK()
|
||||
*/
|
||||
@Override
|
||||
protected void doOK() {
|
||||
if (currentHighlight != null) {
|
||||
saveCurrentHighlightProps(currentHighlight);
|
||||
if (mapHighlightsToProps != null) {
|
||||
Set<AuthorPersistentHighlight> highlights = mapHighlightsToProps.keySet();
|
||||
for (AuthorPersistentHighlight highlight : highlights) {
|
||||
// Update the timestamp
|
||||
mapHighlightsToProps.get(highlight).put(HighlightProperties.ID, authorAccess.getReviewController().getCurrentTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
super.doOK();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display highlight properties.
|
||||
*
|
||||
* @param highlight The current highlight.
|
||||
*/
|
||||
private void displayHighlightProps(AuthorPersistentHighlight highlight) {
|
||||
LinkedHashMap<String, String> cloneProperties = highlight.getClonedProperties();
|
||||
|
||||
// Highlight properties
|
||||
String id = cloneProperties.get(HighlightProperties.ID);
|
||||
String author = cloneProperties.get(HighlightProperties.AUTHOR);
|
||||
String comment = cloneProperties.get(HighlightProperties.COMMENT);
|
||||
|
||||
// Display highlight comment
|
||||
commentArea.setText(comment);
|
||||
|
||||
// Display highlight ID and author
|
||||
infoLabel.setText("Id: " + id + " Author: " + author);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the dialog.
|
||||
*/
|
||||
public void showDialog() {
|
||||
setLocationRelativeTo(null);
|
||||
pack();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last edited comment.
|
||||
*
|
||||
* @return The inserted comment.
|
||||
*/
|
||||
public String getComment() {
|
||||
return commentArea.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the highlights to properties map.
|
||||
*/
|
||||
public Map<AuthorPersistentHighlight, LinkedHashMap<String, String>> getMapHighlightsToProps() {
|
||||
return mapHighlightsToProps;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package simple.documentation.framework.operations.highlight;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlight;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
|
||||
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
|
||||
|
||||
/**
|
||||
* Operation used to edit the highlights from the caret position.
|
||||
*/
|
||||
public class EditHighlightsOperation implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
AuthorPersistentHighlighter highlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
|
||||
AuthorPersistentHighlight[] highlights = highlighter.getHighlights();
|
||||
if (highlights.length > 0) {
|
||||
int caretOffset = authorAccess.getEditorAccess().getCaretOffset();
|
||||
List<AuthorPersistentHighlight> caretHighlights = new ArrayList<AuthorPersistentHighlight>();
|
||||
// Remove highlights from the caret position
|
||||
for (AuthorPersistentHighlight highlight : highlights) {
|
||||
// Get the highlights from the caret position
|
||||
if (highlight.getStartOffset() <= caretOffset && highlight.getEndOffset() >= caretOffset) {
|
||||
caretHighlights.add(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
if (caretHighlights.size() > 0) {
|
||||
// Show edit highlights dialog
|
||||
EditHighlightsDialog commentDlg = new EditHighlightsDialog(
|
||||
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame(),
|
||||
"Add highlight comment",
|
||||
true,
|
||||
caretHighlights,
|
||||
authorAccess);
|
||||
commentDlg.showDialog();
|
||||
// Save edited highlights if dialog result is OK
|
||||
if (commentDlg.getResult() == OKCancelDialog.RESULT_OK) {
|
||||
Map<AuthorPersistentHighlight, LinkedHashMap<String, String>> mapHighlightsToProps = commentDlg.getMapHighlightsToProps();
|
||||
Set<AuthorPersistentHighlight> highlightsSet = mapHighlightsToProps.keySet();
|
||||
for (AuthorPersistentHighlight h : highlightsSet) {
|
||||
// Save edited properties
|
||||
highlighter.setProperties(h, mapHighlightsToProps.get(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Edit Highlights from the caret position";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package simple.documentation.framework.operations.highlight;
|
||||
|
||||
/**
|
||||
* Hightlight properties names
|
||||
*/
|
||||
public interface HighlightProperties {
|
||||
/**
|
||||
* Highlight author.
|
||||
*/
|
||||
static final String AUTHOR = "author";
|
||||
/**
|
||||
* Highlight ID.
|
||||
*/
|
||||
static final String ID = "id";
|
||||
/**
|
||||
* Highlight comment.
|
||||
*/
|
||||
static final String COMMENT = "comment";
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package simple.documentation.framework.operations.highlight;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
|
||||
|
||||
/**
|
||||
* Operation used to remove all persistent highlights.
|
||||
*/
|
||||
public class RemoveAllHighlightsOperation implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
AuthorPersistentHighlighter highlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
|
||||
int highlights = highlighter.getHighlights().length;
|
||||
// Remove all highlights
|
||||
highlighter.removeAllHighlights();
|
||||
authorAccess.getWorkspaceAccess().showInformationMessage(highlights == 1 ? "1 highlight removed" : highlights + " highlights removed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Remove all persistent highlights";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package simple.documentation.framework.operations.highlight;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorHighlighter;
|
||||
import ro.sync.ecss.extensions.api.highlights.AuthorPersistentHighlighter;
|
||||
import ro.sync.ecss.extensions.api.highlights.ColorHighlightPainter;
|
||||
import ro.sync.ecss.extensions.api.node.AuthorNode;
|
||||
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
|
||||
import ro.sync.exml.view.graphics.Color;
|
||||
|
||||
|
||||
/**
|
||||
* Operation used to highlight a paragraph as reviewed.
|
||||
*/
|
||||
public class ReviewParaOperation implements AuthorOperation {
|
||||
/**
|
||||
* Highlight author.
|
||||
*/
|
||||
static final String AUTHOR = "author";
|
||||
/**
|
||||
* Highlight ID.
|
||||
*/
|
||||
static final String ID = "id";
|
||||
/**
|
||||
* Highlight comment.
|
||||
*/
|
||||
static final String COMMENT = "comment";
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap map) throws IllegalArgumentException,
|
||||
AuthorOperationException {
|
||||
// Highlight the selected paragraph if any.
|
||||
AuthorNode selectedNode = authorAccess.getEditorAccess().getFullySelectedNode();
|
||||
if (selectedNode != null) {
|
||||
// Show dialog for adding highlight comment
|
||||
AuthorPersistentHighlighter persistentHighlighter = authorAccess.getEditorAccess().getPersistentHighlighter();
|
||||
AuthorHighlighter highlighter = authorAccess.getEditorAccess().getHighlighter();
|
||||
EditHighlightsDialog commentDlg = new EditHighlightsDialog(
|
||||
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame(),
|
||||
"Review Paragraph",
|
||||
true,
|
||||
null,
|
||||
authorAccess);
|
||||
commentDlg.showDialog();
|
||||
if (commentDlg.getResult() == OKCancelDialog.RESULT_OK) {
|
||||
|
||||
// Get author name and timestamp.
|
||||
String authorName = authorAccess.getReviewController().getReviewerAuthorName();
|
||||
String timestamp = authorAccess.getReviewController().getCurrentTimestamp();
|
||||
|
||||
// Compose highlight properties
|
||||
LinkedHashMap<String, String> properties = new LinkedHashMap<String, String>();
|
||||
properties.put(ID, timestamp);
|
||||
properties.put(AUTHOR, authorName);
|
||||
String comment = commentDlg.getComment();
|
||||
properties.put(COMMENT, comment);
|
||||
int startOffset = selectedNode.getStartOffset();
|
||||
int endOffset = selectedNode.getEndOffset();
|
||||
if (comment != null && comment.trim().length() > 0) {
|
||||
// Add a persistent highlight
|
||||
persistentHighlighter.addHighlight(startOffset, endOffset, properties);
|
||||
} else {
|
||||
// Add non-persistent highlight
|
||||
|
||||
ColorHighlightPainter painter = new ColorHighlightPainter();
|
||||
painter.setTextForegroundColor(Color.COLOR_RED_DARKER);
|
||||
|
||||
try {
|
||||
highlighter.addHighlight(startOffset, endOffset, painter, null);
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(
|
||||
(Component) authorAccess.getWorkspaceAccess().getParentFrame(),
|
||||
"Select the whole paragraph!!!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Review selected paragraph.";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package simple.documentation.framework.operations.table;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
|
||||
import ro.sync.ecss.extensions.api.ArgumentDescriptor;
|
||||
import ro.sync.ecss.extensions.api.ArgumentsMap;
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperation;
|
||||
import ro.sync.ecss.extensions.api.AuthorOperationException;
|
||||
import ro.sync.exml.workspace.api.Platform;
|
||||
import simple.documentation.framework.operations.table.TableCustomizerDialog.TableInfo;
|
||||
|
||||
/**
|
||||
* Operation used to insert a SDF table.
|
||||
*/
|
||||
public class InsertTableOperation implements AuthorOperation {
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#doOperation(ro.sync.ecss.extensions.api.AuthorAccess, ro.sync.ecss.extensions.api.ArgumentsMap)
|
||||
*/
|
||||
public void doOperation(AuthorAccess authorAccess, ArgumentsMap args)
|
||||
throws IllegalArgumentException, AuthorOperationException {
|
||||
// Show the 'Insert table' dialog
|
||||
TableInfo tableInfo = null;
|
||||
if(authorAccess.getWorkspaceAccess().getPlatform() == Platform.STANDALONE) {
|
||||
TableCustomizerDialog tableCustomizerDialog = new TableCustomizerDialog(
|
||||
(JFrame) authorAccess.getWorkspaceAccess().getParentFrame());
|
||||
tableCustomizerDialog.setLocationRelativeTo(
|
||||
(Component) authorAccess.getWorkspaceAccess().getParentFrame());
|
||||
tableInfo = tableCustomizerDialog.showDialog();
|
||||
}
|
||||
|
||||
if (tableInfo != null) {
|
||||
// Create the table XML fragment
|
||||
StringBuffer tableXMLFragment = new StringBuffer();
|
||||
tableXMLFragment.append("<table xmlns=\"http://www.oxygenxml.com/sample/documentation\"");
|
||||
if (tableInfo.getTableBackgroundColor() != null) {
|
||||
tableXMLFragment.append(" bgcolor=\"rgb(" +
|
||||
tableInfo.getTableBackgroundColor().getRed() + "," +
|
||||
tableInfo.getTableBackgroundColor().getGreen() + "," +
|
||||
tableInfo.getTableBackgroundColor().getBlue() +
|
||||
")\"");
|
||||
}
|
||||
tableXMLFragment.append(">");
|
||||
if(tableInfo.getTitle() != null && tableInfo.getTitle().trim().length() > 0) {
|
||||
tableXMLFragment.append("<title>" + tableInfo.getTitle().trim() + "</title>");
|
||||
}
|
||||
|
||||
// Add table body
|
||||
int columns = tableInfo.getColumnsNumber();
|
||||
int rows = tableInfo.getRowsNumber();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
tableXMLFragment.append("<tr>");
|
||||
for (int j = 0; j < columns; j++) {
|
||||
tableXMLFragment.append("<td></td>");
|
||||
}
|
||||
tableXMLFragment.append("</tr>");
|
||||
}
|
||||
|
||||
tableXMLFragment.append("</table>");
|
||||
|
||||
|
||||
// Insert the table
|
||||
authorAccess.getDocumentController().insertXMLFragmentSchemaAware(
|
||||
tableXMLFragment.toString(),
|
||||
authorAccess.getEditorAccess().getCaretOffset());
|
||||
} else {
|
||||
// User canceled the operation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No arguments. The operation will display a dialog for choosing the table attributes.
|
||||
*
|
||||
* @see ro.sync.ecss.extensions.api.AuthorOperation#getArguments()
|
||||
*/
|
||||
public ArgumentDescriptor[] getArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ro.sync.ecss.extensions.api.Extension#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Insert a SDF table";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,339 @@
|
|||
package simple.documentation.framework.operations.table;
|
||||
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JColorChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
|
||||
import ro.sync.ecss.extensions.commons.table.operations.TableCustomizerConstants;
|
||||
import ro.sync.ecss.extensions.commons.ui.OKCancelDialog;
|
||||
|
||||
/**
|
||||
* Dialog used to customize the insertion of a table (number of rows, columns, table caption).
|
||||
* It is used on Standalone implementation.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TableCustomizerDialog extends OKCancelDialog implements TableCustomizerConstants {
|
||||
|
||||
/**
|
||||
* If selected the user can specify the table title.
|
||||
*/
|
||||
private JCheckBox titleCheckbox;
|
||||
|
||||
/**
|
||||
* Text field for specify the table title.
|
||||
*/
|
||||
private JTextField titleTextField;
|
||||
|
||||
/**
|
||||
* Used to specify the number of rows.
|
||||
*/
|
||||
private JSpinner rowsSpinner;
|
||||
|
||||
/**
|
||||
* Used to specify the number of columns.
|
||||
*/
|
||||
private JSpinner columnsSpinner;
|
||||
|
||||
/**
|
||||
* If selected the user can specify the table background color.
|
||||
*/
|
||||
private JCheckBox tableBgColorCheckbox;
|
||||
|
||||
/**
|
||||
* Button used to choose table background color.
|
||||
*/
|
||||
private JButton tableBgColorButton;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param parentFrame The parent {@link JFrame} of the dialog.
|
||||
*/
|
||||
public TableCustomizerDialog(
|
||||
JFrame parentFrame) {
|
||||
super(parentFrame, "Insert Table", true);
|
||||
|
||||
// The main panel
|
||||
JPanel mainPanel = new JPanel(new GridBagLayout());
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
GridBagConstraints gridBagConstr = new GridBagConstraints();
|
||||
gridBagConstr.anchor = GridBagConstraints.WEST;
|
||||
gridBagConstr.gridy = 0;
|
||||
gridBagConstr.gridx = 0;
|
||||
gridBagConstr.weightx = 0;
|
||||
gridBagConstr.gridwidth = 1;
|
||||
gridBagConstr.insets = new Insets(5, 0, 5, 5);
|
||||
gridBagConstr.fill = GridBagConstraints.NONE;
|
||||
|
||||
// Title check box
|
||||
titleCheckbox = new JCheckBox("Title");
|
||||
titleCheckbox.setName("Title checkbox");
|
||||
titleCheckbox.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
titleTextField.setEditable(titleCheckbox.isSelected());
|
||||
}
|
||||
});
|
||||
titleCheckbox.setBorder(BorderFactory.createEmptyBorder());
|
||||
mainPanel.add(titleCheckbox, gridBagConstr);
|
||||
|
||||
// Title text field
|
||||
titleTextField = new JTextField();
|
||||
titleTextField.setName("Title text field");
|
||||
gridBagConstr.gridx ++;
|
||||
gridBagConstr.weightx = 1;
|
||||
gridBagConstr.fill = GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstr.insets = new Insets(5, 0, 5, 0);
|
||||
mainPanel.add(titleTextField, gridBagConstr);
|
||||
|
||||
gridBagConstr.gridy ++;
|
||||
gridBagConstr.gridx = 0;
|
||||
gridBagConstr.weightx = 0;
|
||||
gridBagConstr.gridwidth = 1;
|
||||
gridBagConstr.insets = new Insets(5, 0, 5, 5);
|
||||
gridBagConstr.fill = GridBagConstraints.BOTH;
|
||||
|
||||
// Table bgcolor box
|
||||
tableBgColorCheckbox = new JCheckBox("Table Background");
|
||||
tableBgColorButton = new JButton();
|
||||
tableBgColorCheckbox.setName("Table Background");
|
||||
tableBgColorCheckbox.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
tableBgColorButton.setEnabled(tableBgColorCheckbox.isSelected());
|
||||
}
|
||||
});
|
||||
tableBgColorCheckbox.setBorder(BorderFactory.createEmptyBorder());
|
||||
mainPanel.add(tableBgColorCheckbox, gridBagConstr);
|
||||
|
||||
// Table bg color
|
||||
tableBgColorButton.setIcon(new Icon() {
|
||||
public void paintIcon(Component parent, Graphics g, int x, int y) {
|
||||
Color color = tableBgColorButton.getBackground();
|
||||
if (color == null) {
|
||||
return;
|
||||
}
|
||||
Color used4Draw = color;
|
||||
if (parent != null && !parent.isEnabled()) {
|
||||
used4Draw = parent.getBackground();
|
||||
}
|
||||
g.setColor(used4Draw);
|
||||
g.fillRect(x, y, getIconWidth(), getIconHeight());
|
||||
g.setColor(used4Draw.darker());
|
||||
g.drawRect(x, y, getIconWidth() - 1, getIconHeight() - 1);
|
||||
}
|
||||
|
||||
public int getIconWidth() {
|
||||
return tableBgColorButton.getWidth();
|
||||
}
|
||||
|
||||
public int getIconHeight() {
|
||||
return tableBgColorButton.getHeight();
|
||||
}
|
||||
});
|
||||
|
||||
tableBgColorButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
Color initialBackground = tableBgColorButton.getBackground();
|
||||
Color background = JColorChooser.showDialog(null,
|
||||
"Choose Color", initialBackground);
|
||||
if (background != null) {
|
||||
tableBgColorButton.setBackground(background);
|
||||
|
||||
tableBgColorButton.setContentAreaFilled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
tableBgColorButton.setPreferredSize(new Dimension(100, 15));
|
||||
gridBagConstr.gridx ++;
|
||||
gridBagConstr.weightx = 1;
|
||||
gridBagConstr.fill = GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstr.insets = new Insets(2, 2, 2, 2);
|
||||
mainPanel.add(tableBgColorButton, gridBagConstr);
|
||||
|
||||
// Table size panel
|
||||
JPanel sizePanel = new JPanel(new GridBagLayout());
|
||||
sizePanel.setBorder(BorderFactory.createTitledBorder("Table Size"));
|
||||
|
||||
gridBagConstr.gridy ++;
|
||||
gridBagConstr.gridx = 0;
|
||||
gridBagConstr.weightx = 1;
|
||||
gridBagConstr.fill = GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstr.gridwidth = 2;
|
||||
gridBagConstr.insets = new Insets(5, 0, 5, 0);
|
||||
mainPanel.add(sizePanel, gridBagConstr);
|
||||
|
||||
// 'Rows' label
|
||||
JLabel rowsLabel = new JLabel("Rows");
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 0;
|
||||
c.insets = new Insets(0, 5, 5, 5);
|
||||
sizePanel.add(rowsLabel, c);
|
||||
|
||||
// Number of rows text field
|
||||
rowsSpinner = new JSpinner();
|
||||
rowsSpinner.setName("Rows spinner");
|
||||
rowsSpinner.setModel(new SpinnerNumberModel(2, 0, 100, 1));
|
||||
c.gridx++;
|
||||
c.weightx = 1;
|
||||
sizePanel.add(rowsSpinner, c);
|
||||
|
||||
// 'Columns' label
|
||||
JLabel columnsLabel = new JLabel("Columns");
|
||||
c.gridx++;
|
||||
c.weightx = 0;
|
||||
sizePanel.add(columnsLabel, c);
|
||||
|
||||
// Number of rows text field
|
||||
columnsSpinner = new JSpinner();
|
||||
columnsSpinner.setName("Columns spinner");
|
||||
columnsSpinner.setModel(new SpinnerNumberModel(2, 0, 100, 1));
|
||||
c.gridx++;
|
||||
c.weightx = 1;
|
||||
sizePanel.add(columnsSpinner, c);
|
||||
|
||||
//Add the main panel
|
||||
getContentPane().add(mainPanel, BorderLayout.CENTER);
|
||||
|
||||
pack();
|
||||
setResizable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains informations about the table element.
|
||||
*/
|
||||
class TableInfo {
|
||||
/**
|
||||
* Table title.
|
||||
*/
|
||||
private final String title;
|
||||
/**
|
||||
* Table rows number.
|
||||
*/
|
||||
private final int rowsNumber;
|
||||
/**
|
||||
* Table columns number.
|
||||
*/
|
||||
private final int columnsNumber;
|
||||
/**
|
||||
* Table background color.
|
||||
*/
|
||||
private final Color tableBgColor;
|
||||
|
||||
/**
|
||||
* @param title Table title.
|
||||
* @param rowsNumber Table rows number.
|
||||
* @param columnsNumber Table columns number.
|
||||
* @param tableBgColor Table background color.
|
||||
*/
|
||||
public TableInfo(String title, int rowsNumber, int columnsNumber, Color tableBgColor) {
|
||||
this.title = title;
|
||||
this.rowsNumber = rowsNumber;
|
||||
this.columnsNumber = columnsNumber;
|
||||
this.tableBgColor = tableBgColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the title.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the rows number.
|
||||
*/
|
||||
public int getRowsNumber() {
|
||||
return rowsNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the columns number.
|
||||
*/
|
||||
public int getColumnsNumber() {
|
||||
return columnsNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the table background color.
|
||||
*/
|
||||
public Color getTableBackgroundColor() {
|
||||
return tableBgColor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the dialog to customize the table attributes.
|
||||
*
|
||||
* @return The object containing informations about the table to be inserted.
|
||||
* If <code>null</code> then the user canceled the table insertion.
|
||||
*/
|
||||
public TableInfo showDialog() {
|
||||
// Reset components to default values
|
||||
titleTextField.setEditable(true);
|
||||
titleTextField.setText("");
|
||||
titleCheckbox.setSelected(true);
|
||||
|
||||
tableBgColorButton.setEnabled(false);
|
||||
tableBgColorButton.setBackground(tableBgColorCheckbox.getBackground());
|
||||
tableBgColorCheckbox.setSelected(false);
|
||||
|
||||
// Set the default number of rows and columns
|
||||
rowsSpinner.setValue(new Integer(3));
|
||||
columnsSpinner.setValue(new Integer(2));
|
||||
|
||||
// Request focus in title field
|
||||
titleTextField.requestFocus();
|
||||
|
||||
super.setVisible(true);
|
||||
|
||||
TableInfo tableInfo = null;
|
||||
if(getResult() == RESULT_OK) {
|
||||
// Title
|
||||
String title = null;
|
||||
if(titleCheckbox.isSelected()) {
|
||||
title = titleTextField.getText();
|
||||
title = ro.sync.basic.xml.BasicXmlUtil.escape(title);
|
||||
}
|
||||
// Table background color
|
||||
Color tableBgColor = null;
|
||||
if(tableBgColorCheckbox.isSelected()) {
|
||||
tableBgColor = tableBgColorButton.getBackground();
|
||||
}
|
||||
int rowsNumber = ((Integer)rowsSpinner.getValue()).intValue();
|
||||
int columnsNumber = ((Integer)columnsSpinner.getValue()).intValue();
|
||||
|
||||
tableInfo =
|
||||
new TableInfo(
|
||||
title,
|
||||
rowsNumber,
|
||||
columnsNumber,
|
||||
tableBgColor);
|
||||
} else {
|
||||
// Cancel was pressed
|
||||
}
|
||||
return tableInfo;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package simple.documentation.framework.ui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.IllegalComponentStateException;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JWindow;
|
||||
|
||||
import ro.sync.ecss.extensions.api.AuthorAccess;
|
||||
import ro.sync.exml.view.graphics.Point;
|
||||
|
||||
/**
|
||||
* Popup window used to display Simple Documentation Framework specific information.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SDFPopupWindow extends JWindow {
|
||||
|
||||
/**
|
||||
* Text area used to display useful informations.
|
||||
*/
|
||||
private JTextArea infoTextArea;
|
||||
|
||||
/**
|
||||
* Access to the author specific functions.
|
||||
*/
|
||||
AuthorAccess authorAccess;
|
||||
|
||||
/**
|
||||
* The display time of the popup window (seconds).
|
||||
*/
|
||||
private int timeToDisplay;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param access Author access.
|
||||
* @param infoDescription Description.
|
||||
*/
|
||||
public SDFPopupWindow(AuthorAccess access, String infoDescription) {
|
||||
super((JFrame) access.getWorkspaceAccess().getParentFrame());
|
||||
this.authorAccess = access;
|
||||
|
||||
// Create information text area.
|
||||
infoTextArea = new JTextArea();
|
||||
infoTextArea.setLineWrap(true);
|
||||
infoTextArea.setWrapStyleWord(true);
|
||||
infoTextArea.setEditable(false);
|
||||
infoTextArea.setFocusable(false);
|
||||
|
||||
JPanel mainContent = new JPanel(new BorderLayout());
|
||||
if (infoDescription != null) {
|
||||
mainContent.add(new JLabel(infoDescription), BorderLayout.NORTH);
|
||||
}
|
||||
mainContent.setFocusable(false);
|
||||
mainContent.add(infoTextArea, BorderLayout.SOUTH);
|
||||
mainContent.setBorder(BorderFactory.createLineBorder(Color.black));
|
||||
getContentPane().add(mainContent);
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time to display this popup window.
|
||||
*
|
||||
* @param timeToDisplay The display time in seconds.
|
||||
*/
|
||||
public void setTimeToDisplay(int timeToDisplay) {
|
||||
this.timeToDisplay = timeToDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified text information.
|
||||
*
|
||||
* @param text The text to be displayed in the popup window.
|
||||
* @param relX The "x" coordinate relative to the viewport.
|
||||
* @param relY The "y" coordinate relative to the viewport.
|
||||
* @param delta The translation point where the popup should be displayed from the given (x, y) point.
|
||||
*/
|
||||
public void display(String text, int relX, int relY, int delta) {
|
||||
// Transform the given relative coordinates into absolute coordinates.
|
||||
try {
|
||||
Point translatedPoint = authorAccess.getEditorAccess().getLocationOnScreenAsPoint(relX, relY);
|
||||
setVisible(false);
|
||||
infoTextArea.setText(text);
|
||||
setLocation(translatedPoint.x + delta, translatedPoint.y + delta);
|
||||
pack();
|
||||
// Show the information popup window
|
||||
setVisible(true);
|
||||
|
||||
// Hide the window when the given display time is finished.
|
||||
if (timeToDisplay > 0) {
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
setVisible(false);
|
||||
}
|
||||
}, timeToDisplay * 1000);
|
||||
}
|
||||
} catch (IllegalComponentStateException e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<article
|
||||
xmlns="http://www.oxygenxml.com/sample/documentation"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<title></title>
|
||||
<section>
|
||||
<title></title>
|
||||
<para></para>
|
||||
<para></para>
|
||||
</section>
|
||||
</article>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<book xmlns="http://www.oxygenxml.com/sample/documentation"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts">
|
||||
<title>Book Template Title</title>
|
||||
<section>
|
||||
<title>Section Title</title>
|
||||
<abs:def/>
|
||||
<para>This content is copyrighted:</para>
|
||||
<table>
|
||||
<title>Table Title</title>
|
||||
<header>
|
||||
<td>Company</td>
|
||||
<td>Date</td>
|
||||
</header>
|
||||
<tr>
|
||||
<td/>
|
||||
<td/>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
</book>
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Unexpected things happen here...
|
||||
*/
|
||||
|
||||
function a() {}
|
||||
|
||||
b = function() {}
|
||||
|
||||
// Notice the weird behavior :
|
||||
|
||||
typeof a; // object
|
||||
typeof b; // function
|
||||
|
||||
aclass = function () {
|
||||
this.setFoo(1);
|
||||
return this;
|
||||
}
|
||||
|
||||
typeof aclass;
|
||||
|
||||
aclass.prototype.setFoo = function (v) {
|
||||
this.foo = v;
|
||||
}
|
||||
|
||||
anObject = new aclass() ;
|
||||
|
||||
anObject.setFoo(2);
|
||||
|
||||
anObject.foo;
|
||||
|
||||
// Also worth mentionning, you ***must*** use new and this doesn't work :
|
||||
//
|
||||
// anobject = aclass();
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
|
||||
xpath-default-namespace="http://www.oxygenxml.com/sample/documentation">
|
||||
<xsl:template match="/">
|
||||
<html>
|
||||
<xsl:apply-templates/>
|
||||
</html>
|
||||
</xsl:template>
|
||||
<xsl:template match="section">
|
||||
<xsl:apply-templates/>
|
||||
</xsl:template>
|
||||
<xsl:template match="image">
|
||||
<img src="{@href}"/>
|
||||
</xsl:template>
|
||||
<xsl:template match="para">
|
||||
<p>
|
||||
<xsl:apply-templates/>
|
||||
</p>
|
||||
</xsl:template>
|
||||
<xsl:template match="abs:def"
|
||||
xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts">
|
||||
<p>
|
||||
<u>
|
||||
<xsl:apply-templates/>
|
||||
</u>
|
||||
</p>
|
||||
</xsl:template>
|
||||
<xsl:template match="title">
|
||||
<h1>
|
||||
<xsl:apply-templates/>
|
||||
</h1>
|
||||
</xsl:template>
|
||||
<xsl:template match="b">
|
||||
<b>
|
||||
<xsl:apply-templates/>
|
||||
</b>
|
||||
</xsl:template>
|
||||
<xsl:template match="i">
|
||||
<i>
|
||||
<xsl:apply-templates/>
|
||||
</i>
|
||||
</xsl:template>
|
||||
<xsl:template match="table">
|
||||
<table frame="box" border="1px">
|
||||
<xsl:apply-templates/>
|
||||
</table>
|
||||
</xsl:template>
|
||||
<xsl:template match="header">
|
||||
<tr>
|
||||
<xsl:apply-templates/>
|
||||
</tr>
|
||||
</xsl:template>
|
||||
<xsl:template match="tr">
|
||||
<tr>
|
||||
<xsl:apply-templates/>
|
||||
</tr>
|
||||
</xsl:template>
|
||||
<xsl:template match="td">
|
||||
<td>
|
||||
<xsl:apply-templates/>
|
||||
</td>
|
||||
</xsl:template>
|
||||
<xsl:template match="header/header/td">
|
||||
<th>
|
||||
<xsl:apply-templates/>
|
||||
</th>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|