Copying
|
@ -0,0 +1,59 @@
|
||||||
|
# Alltogether: 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) through inclusion
|
||||||
|
|
||||||
|
This rambling shows how a [debugger](../js-debugger) can be launched using an [inclusion](../js-include).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To do so:
|
||||||
|
### 1) add the include() function to your commons.js file:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
include = function (filepath, isAbsolute) {
|
||||||
|
if (isAbsolute === undefined || isAbsolute == false) {
|
||||||
|
filepath = Packages.ro.sync.ecss.extensions.commons.operations.CommonsOperationsUtil.expandAndResolvePath(authorAccess, filepath);
|
||||||
|
}
|
||||||
|
var text = new java.lang.String(java.nio.file.Files.readAllBytes(java.nio.file.Paths. get (java.net.URI(filepath))));
|
||||||
|
text = String(text);
|
||||||
|
eval(text);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 2) create a debugger.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/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 3) Use these functions in your doOperation() method:
|
||||||
|
```javascript
|
||||||
|
doOperation = function () {
|
||||||
|
include('debugger.js');
|
||||||
|
startDebugger();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## See also
|
||||||
|
More details on these hacks:
|
||||||
|
* [debugger](../js-debugger)
|
||||||
|
* [inclusion](../js-include)
|
||||||
|
# Framework
|
||||||
|
|
||||||
|
This directory includes the Oxygen sample framework on which ou can test these techniques but they 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,8 @@
|
||||||
|
include = function (filepath, isAbsolute) {
|
||||||
|
if (isAbsolute === undefined || isAbsolute == false) {
|
||||||
|
filepath = Packages.ro.sync.ecss.extensions.commons.operations.CommonsOperationsUtil.expandAndResolvePath(authorAccess, filepath);
|
||||||
|
}
|
||||||
|
var text = new java.lang.String(java.nio.file.Files.readAllBytes(java.nio.file.Paths. get (java.net.URI(filepath))));
|
||||||
|
text = String(text);
|
||||||
|
eval(text);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
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,3 @@
|
||||||
|
hello = function (){
|
||||||
|
Packages.java.lang.System.out.println("Hello world.");
|
||||||
|
}
|
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,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="22.0">
|
||||||
|
<meta>
|
||||||
|
<filters directoryPatterns="" filePatterns="\Qjs-debugger.xpr\E" positiveFilePatterns="" showHiddenFiles="false"/>
|
||||||
|
<options>
|
||||||
|
<serialized version="22.0" xml:space="preserve">
|
||||||
|
<serializableOrderedMap>
|
||||||
|
<entry>
|
||||||
|
<String>additional.frameworks.directories</String>
|
||||||
|
<String-array>
|
||||||
|
<String>/home/vdv/projects/pro/Remix/oxygen/frameworks/http___oxygen.remix.rece.hachette_livre.fr_oxygenUpdate.xml</String>
|
||||||
|
<String>/home/vdv/projects/tea/dyomedea/ramblings/oxygen/js-require</String>
|
||||||
|
</String-array>
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
<String>key.editor.document.type.custom.locations.option.pane</String>
|
||||||
|
<Boolean>true</Boolean>
|
||||||
|
</entry>
|
||||||
|
</serializableOrderedMap>
|
||||||
|
</serialized>
|
||||||
|
</options>
|
||||||
|
</meta>
|
||||||
|
<projectTree name="js-require.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,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>
|