1 package org
.argeo
.app
.ui
.docbook
;
3 import static org
.argeo
.app
.docbook
.DbkType
.para
;
4 import static org
.argeo
.app
.jcr
.docbook
.DbkJcrUtils
.addDbk
;
5 import static org
.argeo
.app
.jcr
.docbook
.DbkJcrUtils
.isDbk
;
7 import java
.util
.ArrayList
;
8 import java
.util
.Iterator
;
11 import java
.util
.Observer
;
13 import javax
.jcr
.Item
;
14 import javax
.jcr
.Node
;
15 import javax
.jcr
.NodeIterator
;
16 import javax
.jcr
.RepositoryException
;
17 import javax
.jcr
.Session
;
19 import org
.argeo
.api
.cms
.CmsLog
;
20 import org
.argeo
.api
.cms
.ux
.Cms2DSize
;
21 import org
.argeo
.api
.cms
.ux
.CmsEditable
;
22 import org
.argeo
.app
.docbook
.DbkAttr
;
23 import org
.argeo
.app
.docbook
.DbkType
;
24 import org
.argeo
.app
.jcr
.docbook
.DbkJcrUtils
;
25 import org
.argeo
.cms
.swt
.CmsSwtUtils
;
26 import org
.argeo
.cms
.swt
.SwtEditablePart
;
27 import org
.argeo
.cms
.ui
.viewers
.AbstractPageViewer
;
28 import org
.argeo
.cms
.ui
.viewers
.NodePart
;
29 import org
.argeo
.cms
.ui
.viewers
.PropertyPart
;
30 import org
.argeo
.cms
.ui
.viewers
.Section
;
31 import org
.argeo
.cms
.ui
.viewers
.SectionPart
;
32 import org
.argeo
.cms
.ui
.widgets
.EditableText
;
33 import org
.argeo
.cms
.ui
.widgets
.StyledControl
;
34 import org
.argeo
.jcr
.Jcr
;
35 import org
.argeo
.jcr
.JcrException
;
36 import org
.argeo
.jcr
.JcrUtils
;
37 import org
.eclipse
.rap
.fileupload
.FileDetails
;
38 import org
.eclipse
.rap
.fileupload
.FileUploadEvent
;
39 import org
.eclipse
.rap
.fileupload
.FileUploadHandler
;
40 import org
.eclipse
.rap
.fileupload
.FileUploadListener
;
41 import org
.eclipse
.rap
.rwt
.RWT
;
42 import org
.eclipse
.swt
.SWT
;
43 import org
.eclipse
.swt
.events
.KeyEvent
;
44 import org
.eclipse
.swt
.events
.KeyListener
;
45 import org
.eclipse
.swt
.events
.MouseAdapter
;
46 import org
.eclipse
.swt
.events
.MouseEvent
;
47 import org
.eclipse
.swt
.events
.MouseListener
;
48 import org
.eclipse
.swt
.graphics
.Point
;
49 import org
.eclipse
.swt
.graphics
.Rectangle
;
50 import org
.eclipse
.swt
.layout
.GridData
;
51 import org
.eclipse
.swt
.widgets
.Composite
;
52 import org
.eclipse
.swt
.widgets
.Control
;
53 import org
.eclipse
.swt
.widgets
.Label
;
54 import org
.eclipse
.swt
.widgets
.Text
;
56 /** Base class for text viewers and editors. */
57 public abstract class AbstractDbkViewer
extends AbstractPageViewer
implements KeyListener
, Observer
{
58 private static final long serialVersionUID
= -2401274679492339668L;
59 private final static CmsLog log
= CmsLog
.getLog(AbstractDbkViewer
.class);
61 private final Section mainSection
;
63 private TextInterpreter textInterpreter
= new DbkTextInterpreter();
64 private DbkImageManager imageManager
;
66 private FileUploadListener fileUploadListener
;
67 private DbkContextMenu styledTools
;
69 private final boolean flat
;
71 private boolean showMainTitle
= true;
73 private Integer maxMediaWidth
= null;
74 private String defaultSectionStyle
;
76 protected AbstractDbkViewer(Section parent
, int style
, CmsEditable cmsEditable
) {
77 super(parent
, style
, cmsEditable
);
78 // CmsView cmsView = CmsView.getCmsView(parent);
79 // imageManager = cmsView.getImageManager();
80 flat
= SWT
.FLAT
== (style
& SWT
.FLAT
);
82 if (getCmsEditable().canEdit()) {
83 fileUploadListener
= new FUL();
84 styledTools
= new DbkContextMenu(this, parent
.getShell());
86 this.mainSection
= parent
;
87 Node baseFolder
= Jcr
.getParent(mainSection
.getNode());
88 imageManager
= new DbkImageManager(baseFolder
);
89 initModelIfNeeded(mainSection
.getNode());
90 // layout(this.mainSection);
94 public Control
getControl() {
98 protected void refresh(Control control
) throws RepositoryException
{
99 if (!(control
instanceof Section
))
101 long begin
= System
.currentTimeMillis();
102 Section section
= (Section
) control
;
103 if (section
instanceof TextSection
) {
104 CmsSwtUtils
.clear(section
);
105 Node node
= section
.getNode();
106 TextSection textSection
= (TextSection
) section
;
107 String style
= node
.hasProperty(DbkAttr
.role
.name()) ? node
.getProperty(DbkAttr
.role
.name()).getString()
108 : getDefaultSectionStyle();
110 CmsSwtUtils
.style(textSection
, style
);
113 Node titleNode
= null;
114 // We give priority to ./title vs ./info/title, like the DocBook XSL
115 if (node
.hasNode(DbkType
.title
.get())) {
116 titleNode
= node
.getNode(DbkType
.title
.get());
117 } else if (node
.hasNode(DbkType
.info
.get() + '/' + DbkType
.title
.get())) {
118 titleNode
= node
.getNode(DbkType
.info
.get() + '/' + DbkType
.title
.get());
121 if (titleNode
!= null) {
122 boolean showTitle
= getMainSection() == section ? showMainTitle
: true;
124 if (section
.getHeader() == null)
125 section
.createHeader();
126 DbkSectionTitle title
= newSectionTitle(textSection
, titleNode
);
127 title
.setLayoutData(CmsSwtUtils
.fillWidth());
128 updateContent(title
);
133 for (NodeIterator ni
= node
.getNodes(); ni
.hasNext();) {
134 Node child
= ni
.nextNode();
135 SectionPart sectionPart
= null;
136 if (isDbk(child
, DbkType
.mediaobject
)) {
137 if (child
.hasNode(DbkType
.imageobject
.get())) {
138 sectionPart
= newImg(textSection
, child
);
139 } else if (child
.hasNode(DbkType
.videoobject
.get())) {
140 sectionPart
= newVideo(textSection
, child
);
142 throw new IllegalArgumentException("Unsupported media object " + child
);
144 } else if (isDbk(child
, DbkType
.info
)) {
145 // TODO enrich UI based on info
146 } else if (isDbk(child
, DbkType
.title
)) {
148 } else if (isDbk(child
, para
)) {
149 sectionPart
= newParagraph(textSection
, child
);
150 } else if (isDbk(child
, DbkType
.section
)) {
151 sectionPart
= newSectionPart(textSection
, child
);
152 // if (sectionPart == null)
153 // throw new IllegalArgumentException("Unsupported node " + child);
154 // TODO list node types in exception
156 throw new IllegalArgumentException("Unsupported node type for " + child
);
158 if (sectionPart
!= null && sectionPart
instanceof Control
)
159 ((Control
) sectionPart
).setLayoutData(CmsSwtUtils
.fillWidth());
163 for (NodeIterator ni
= section
.getNode().getNodes(DbkType
.section
.get()); ni
.hasNext();) {
164 Node child
= ni
.nextNode();
165 if (isDbk(child
, DbkType
.section
)) {
166 TextSection newSection
= newTextSection(section
, child
);
167 newSection
.setLayoutData(CmsSwtUtils
.fillWidth());
172 for (Section s
: section
.getSubSections().values())
175 // section.layout(true, true);
176 long duration
= System
.currentTimeMillis() - begin
;
177 // System.out.println(duration + " ms - " + DbkUtils.getTitle(section.getNode()));
180 /** To be overridden in order to provide additional SectionPart types */
181 protected TextSection
newTextSection(Section section
, Node node
) {
182 return new TextSection(section
, SWT
.NONE
, node
);
185 /** To be overridden in order to provide additional SectionPart types */
186 protected SectionPart
newSectionPart(TextSection textSection
, Node node
) {
191 protected Paragraph
newParagraph(TextSection parent
, Node node
) throws RepositoryException
{
192 Paragraph paragraph
= new Paragraph(parent
, parent
.getStyle(), node
);
193 updateContent(paragraph
);
194 paragraph
.setLayoutData(CmsSwtUtils
.fillWidth());
195 paragraph
.setMouseListener(getMouseListener());
196 paragraph
.setFocusListener(getFocusListener());
200 protected DbkImg
newImg(TextSection parent
, Node node
) {
202 DbkImg img
= new DbkImg(parent
, parent
.getStyle(), node
, imageManager
);
204 if (maxMediaWidth
!= null) {
205 imgGd
= new GridData(SWT
.CENTER
, SWT
.FILL
, false, false);
206 imgGd
.widthHint
= maxMediaWidth
;
207 img
.setPreferredSize(new Cms2DSize(maxMediaWidth
, 0));
209 imgGd
= CmsSwtUtils
.grabWidth(SWT
.CENTER
, SWT
.DEFAULT
);
211 img
.setLayoutData(imgGd
);
213 img
.setMouseListener(getMouseListener());
214 img
.setFocusListener(getFocusListener());
216 } catch (RepositoryException e
) {
217 throw new JcrException("Cannot add new image " + node
, e
);
221 protected DbkVideo
newVideo(TextSection parent
, Node node
) {
223 DbkVideo video
= new DbkVideo(parent
, getCmsEditable().canEdit() ? SWT
.NONE
: SWT
.READ_ONLY
, node
);
225 if (maxMediaWidth
!= null) {
226 gd
= new GridData(SWT
.CENTER
, SWT
.FILL
, false, false);
228 // gd.widthHint = maxMediaWidth;
229 // gd.heightHint = (int) (gd.heightHint * 0.5625);
231 gd
= new GridData(SWT
.CENTER
, SWT
.FILL
, false, false);
232 // gd.widthHint = video.getWidth();
233 // gd.heightHint = video.getHeight();
235 video
.setLayoutData(gd
);
236 updateContent(video
);
238 } catch (RepositoryException e
) {
239 throw new JcrException("Cannot add new image " + node
, e
);
243 protected DbkSectionTitle
newSectionTitle(TextSection parent
, Node titleNode
) throws RepositoryException
{
244 int style
= parent
.getStyle();
245 Composite titleParent
= newSectionHeader(parent
);
246 if (parent
.isTitleReadOnly())
247 style
= style
| SWT
.READ_ONLY
;
248 DbkSectionTitle title
= new DbkSectionTitle(titleParent
, style
, titleNode
);
249 updateContent(title
);
250 title
.setMouseListener(getMouseListener());
251 title
.setFocusListener(getFocusListener());
256 * To be overridden in order to provide additional processing at the section
259 * @return the parent to use for the {@link DbkSectionTitle}, by default
260 * {@link Section#getHeader()}
262 protected Composite
newSectionHeader(TextSection section
) {
263 return section
.getHeader();
266 protected DbkSectionTitle
prepareSectionTitle(Section newSection
, String titleText
) throws RepositoryException
{
267 Node sectionNode
= newSection
.getNode();
268 Node titleNode
= DbkJcrUtils
.getOrAddDbk(sectionNode
, DbkType
.title
);
269 getTextInterpreter().write(titleNode
, titleText
);
270 if (newSection
.getHeader() == null)
271 newSection
.createHeader();
272 DbkSectionTitle sectionTitle
= newSectionTitle((TextSection
) newSection
, sectionNode
);
276 protected void updateContent(SwtEditablePart part
) throws RepositoryException
{
277 if (part
instanceof SectionPart
) {
278 SectionPart sectionPart
= (SectionPart
) part
;
279 Node partNode
= sectionPart
.getNode();
281 if (part
instanceof StyledControl
&& (sectionPart
.getSection() instanceof TextSection
)) {
282 TextSection section
= (TextSection
) sectionPart
.getSection();
283 StyledControl styledControl
= (StyledControl
) part
;
284 if (isDbk(partNode
, para
)) {
285 String style
= partNode
.hasProperty(DbkAttr
.role
.name())
286 ? partNode
.getProperty(DbkAttr
.role
.name()).getString()
287 : section
.getDefaultTextStyle();
288 styledControl
.setStyle(style
);
291 // use control AFTER setting style, since it may have been reset
293 if (part
instanceof EditableText
) {
294 EditableText paragraph
= (EditableText
) part
;
295 if (paragraph
== getEdited())
296 paragraph
.setText(textInterpreter
.raw(partNode
));
298 paragraph
.setText(textInterpreter
.readSimpleHtml(partNode
));
299 } else if (part
instanceof DbkImg
) {
300 DbkImg editableImage
= (DbkImg
) part
;
301 // imageManager.load(partNode, part.getControl(),
302 // editableImage.getPreferredImageSize());
303 } else if (part
instanceof DbkVideo
) {
304 DbkVideo video
= (DbkVideo
) part
;
305 video
.load(part
.getControl());
307 } else if (part
instanceof DbkSectionTitle
) {
308 DbkSectionTitle title
= (DbkSectionTitle
) part
;
309 title
.setStyle(title
.getSection().getTitleStyle());
310 // use control AFTER setting style
311 if (title
== getEdited())
312 title
.setText(textInterpreter
.read(title
.getNode()));
314 title
.setText(textInterpreter
.readSimpleHtml(title
.getNode()));
318 // OVERRIDDEN FROM PARENT VIEWER
320 protected void save(SwtEditablePart part
) throws RepositoryException
{
321 if (part
instanceof EditableText
) {
322 EditableText et
= (EditableText
) part
;
323 if (!et
.getEditable())
325 String text
= ((Text
) et
.getControl()).getText();
327 // String[] lines = text.split("[\r\n]+");
328 String
[] lines
= { text
};
329 assert lines
.length
!= 0;
330 saveLine(part
, lines
[0]);
331 if (lines
.length
> 1) {
332 ArrayList
<Control
> toLayout
= new ArrayList
<Control
>();
333 if (part
instanceof Paragraph
) {
334 Paragraph currentParagraph
= (Paragraph
) et
;
335 Section section
= currentParagraph
.getSection();
336 Node sectionNode
= section
.getNode();
337 Node currentParagraphN
= currentParagraph
.getNode();
338 for (int i
= 1; i
< lines
.length
; i
++) {
339 Node newNode
= addDbk(sectionNode
, para
);
340 // newNode.addMixin(CmsTypes.CMS_STYLED);
341 saveLine(newNode
, lines
[i
]);
342 // second node was create as last, if it is not the next
344 // means there are some in between and we can take the
346 // index+1 for the re-order
347 if (newNode
.getIndex() > currentParagraphN
.getIndex() + 1) {
348 sectionNode
.orderBefore(p(newNode
.getIndex()), p(currentParagraphN
.getIndex() + 1));
350 Paragraph newParagraph
= newParagraph((TextSection
) section
, newNode
);
351 newParagraph
.moveBelow(currentParagraph
);
352 toLayout
.add(newParagraph
);
354 currentParagraph
= newParagraph
;
355 currentParagraphN
= newNode
;
358 // TODO or rather return the created paragraphs?
359 layout(toLayout
.toArray(new Control
[toLayout
.size()]));
361 persistChanges(et
.getNode());
365 protected void saveLine(SwtEditablePart part
, String line
) {
366 if (part
instanceof NodePart
) {
367 saveLine(((NodePart
) part
).getNode(), line
);
368 } else if (part
instanceof PropertyPart
) {
369 saveLine(((PropertyPart
) part
).getProperty(), line
);
371 throw new IllegalArgumentException("Unsupported part " + part
);
375 protected void saveLine(Item item
, String line
) {
377 textInterpreter
.write(item
, line
);
381 protected void prepare(SwtEditablePart part
, Object caretPosition
) {
382 Control control
= part
.getControl();
383 if (control
instanceof Text
) {
384 Text text
= (Text
) control
;
385 if (caretPosition
!= null)
386 if (caretPosition
instanceof Integer
)
387 text
.setSelection((Integer
) caretPosition
);
388 else if (caretPosition
instanceof Point
) {
390 // // TODO find a way to position the caret at the right place
391 // Point clickLocation = (Point) caretPosition;
392 // Point withinText = text.toControl(clickLocation);
393 // Rectangle bounds = text.getBounds();
394 // int width = bounds.width;
395 // int height = bounds.height;
396 // int textLength = text.getText().length();
397 // float area = width * height;
398 // float proportion = withinText.y * width + withinText.x;
399 // int pos = (int) (textLength * (proportion / area));
400 // text.setSelection(pos);
402 text
.setData(RWT
.ACTIVE_KEYS
, new String
[] { "BACKSPACE", "ESC", "TAB", "SHIFT+TAB", "ALT+ARROW_LEFT",
403 "ALT+ARROW_RIGHT", "ALT+ARROW_UP", "ALT+ARROW_DOWN", "RETURN", "CTRL+RETURN", "ENTER", "DELETE" });
404 text
.setData(RWT
.CANCEL_KEYS
, new String
[] { "RETURN", "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT" });
405 text
.addKeyListener(this);
406 } else if (part
instanceof DbkImg
) {
407 ((DbkImg
) part
).setFileUploadListener(fileUploadListener
);
411 // REQUIRED BY CONTEXT MENU
412 void setParagraphStyle(Paragraph paragraph
, String style
) {
414 Node paragraphNode
= paragraph
.getNode();
415 if (style
== null) {// default
416 if (paragraphNode
.hasProperty(DbkAttr
.role
.name()))
417 paragraphNode
.getProperty(DbkAttr
.role
.name()).remove();
419 paragraphNode
.setProperty(DbkAttr
.role
.name(), style
);
421 persistChanges(paragraphNode
);
422 updateContent(paragraph
);
424 } catch (RepositoryException e1
) {
425 throw new JcrException("Cannot set style " + style
+ " on " + paragraph
, e1
);
429 SectionPart
insertPart(Section section
, Node node
) {
433 for (Control control
: section
.getChildren()) {
434 if (control
instanceof SectionPart
) {
435 SectionPart sectionPart
= (SectionPart
) control
;
436 Node partNode
= sectionPart
.getNode();
437 if (partNode
.getPath().equals(node
.getPath()))
441 throw new IllegalStateException("New section part " + node
+ "not found");
442 } catch (RepositoryException e
) {
443 throw new JcrException("Cannot insert part " + node
+ " in section " + section
.getNode(), e
);
447 void addParagraph(SectionPart partBefore
, String txt
) {
448 Section section
= partBefore
.getSection();
449 SectionPart nextSectionPart
= section
.nextSectionPart(partBefore
);
450 Node newNode
= addDbk(section
.getNode(), para
);
451 textInterpreter
.write(newNode
, txt
!= null ? txt
: "");
452 if (nextSectionPart
!= null) {
454 Node nextNode
= nextSectionPart
.getNode();
455 section
.getNode().orderBefore(Jcr
.getIndexedName(newNode
), Jcr
.getIndexedName(nextNode
));
456 } catch (RepositoryException e
) {
457 throw new JcrException("Cannot order " + newNode
+ " before " + nextSectionPart
.getNode(), e
);
461 Paragraph paragraph
= (Paragraph
) insertPart(partBefore
.getSection(), newNode
);
465 void deletePart(SectionPart sectionPart
) {
467 Node node
= sectionPart
.getNode();
468 Session session
= node
.getSession();
469 if (sectionPart
instanceof DbkImg
) {
470 if (!isDbk(node
, DbkType
.mediaobject
))
471 throw new IllegalArgumentException("Node " + node
+ " is not a media object.");
475 if (sectionPart
instanceof Control
)
476 ((Control
) sectionPart
).dispose();
478 } catch (RepositoryException e1
) {
479 throw new JcrException("Cannot delete " + sectionPart
, e1
);
483 void deleteSection(Section section
) {
485 Node node
= section
.getNode();
486 Session session
= node
.getSession();
491 } catch (RepositoryException e1
) {
492 throw new JcrException("Cannot delete " + section
, e1
);
496 String
getRawParagraphText(Paragraph paragraph
) {
497 return textInterpreter
.raw(paragraph
.getNode());
501 protected void splitEdit() {
504 if (getEdited() instanceof Paragraph
) {
505 Paragraph paragraph
= (Paragraph
) getEdited();
506 Text text
= (Text
) paragraph
.getControl();
507 int caretPosition
= text
.getCaretPosition();
508 String txt
= text
.getText();
509 String first
= txt
.substring(0, caretPosition
);
510 String second
= txt
.substring(caretPosition
);
511 Node firstNode
= paragraph
.getNode();
512 Node sectionNode
= firstNode
.getParent();
514 // FIXME set content the DocBook way
515 // firstNode.setProperty(CMS_CONTENT, first);
516 Node secondNode
= addDbk(sectionNode
, para
);
517 // secondNode.addMixin(CmsTypes.CMS_STYLED);
519 // second node was create as last, if it is not the next one, it
520 // means there are some in between and we can take the one at
521 // index+1 for the re-order
522 if (secondNode
.getIndex() > firstNode
.getIndex() + 1) {
523 sectionNode
.orderBefore(p(secondNode
.getIndex()), p(firstNode
.getIndex() + 1));
526 // if we die in between, at least we still have the whole text
529 textInterpreter
.write(secondNode
, second
);
530 textInterpreter
.write(firstNode
, first
);
531 } catch (Exception e
) {
532 // so that no additional nodes are created:
533 JcrUtils
.discardUnderlyingSessionQuietly(firstNode
);
537 persistChanges(firstNode
);
539 Paragraph secondParagraph
= paragraphSplitted(paragraph
, secondNode
);
540 edit(secondParagraph
, 0);
541 } else if (getEdited() instanceof DbkSectionTitle
) {
542 DbkSectionTitle sectionTitle
= (DbkSectionTitle
) getEdited();
543 Text text
= (Text
) sectionTitle
.getControl();
544 String txt
= text
.getText();
545 int caretPosition
= text
.getCaretPosition();
546 Section section
= sectionTitle
.getSection();
547 Node sectionNode
= section
.getNode();
548 Node paragraphNode
= addDbk(sectionNode
, para
);
549 // paragraphNode.addMixin(CmsTypes.CMS_STYLED);
551 textInterpreter
.write(paragraphNode
, txt
.substring(caretPosition
));
552 textInterpreter
.write(sectionNode
.getNode(DbkType
.title
.get()), txt
.substring(0, caretPosition
));
553 sectionNode
.orderBefore(p(paragraphNode
.getIndex()), p(1));
554 persistChanges(sectionNode
);
556 Paragraph paragraph
= sectionTitleSplitted(sectionTitle
, paragraphNode
);
560 } catch (RepositoryException e
) {
561 throw new JcrException("Cannot split " + getEdited(), e
);
565 protected void mergeWithPrevious() {
568 Paragraph paragraph
= (Paragraph
) getEdited();
569 Text text
= (Text
) paragraph
.getControl();
570 String txt
= text
.getText();
571 Node paragraphNode
= paragraph
.getNode();
572 if (paragraphNode
.getIndex() == 1)
574 Node sectionNode
= paragraphNode
.getParent();
575 Node previousNode
= sectionNode
.getNode(p(paragraphNode
.getIndex() - 1));
576 String previousTxt
= textInterpreter
.read(previousNode
);
577 textInterpreter
.write(previousNode
, previousTxt
+ txt
);
578 paragraphNode
.remove();
579 persistChanges(sectionNode
);
581 Paragraph previousParagraph
= paragraphMergedWithPrevious(paragraph
, previousNode
);
582 edit(previousParagraph
, previousTxt
.length());
583 } catch (RepositoryException e
) {
584 throw new JcrException("Cannot stop editing", e
);
588 protected void mergeWithNext() {
591 Paragraph paragraph
= (Paragraph
) getEdited();
592 Text text
= (Text
) paragraph
.getControl();
593 String txt
= text
.getText();
594 Node paragraphNode
= paragraph
.getNode();
595 Node sectionNode
= paragraphNode
.getParent();
596 NodeIterator paragraphNodes
= sectionNode
.getNodes(DbkType
.para
.get());
597 long size
= paragraphNodes
.getSize();
598 if (paragraphNode
.getIndex() == size
)
600 Node nextNode
= sectionNode
.getNode(p(paragraphNode
.getIndex() + 1));
601 String nextTxt
= textInterpreter
.read(nextNode
);
602 textInterpreter
.write(paragraphNode
, txt
+ nextTxt
);
604 Section section
= paragraph
.getSection();
605 Paragraph removed
= (Paragraph
) section
.getSectionPart(nextNode
.getIdentifier());
608 persistChanges(sectionNode
);
610 paragraphMergedWithNext(paragraph
, removed
);
611 edit(paragraph
, txt
.length());
612 } catch (RepositoryException e
) {
613 throw new JcrException("Cannot stop editing", e
);
617 protected synchronized void upload(SwtEditablePart part
) {
619 if (part
instanceof SectionPart
) {
620 SectionPart sectionPart
= (SectionPart
) part
;
621 Node partNode
= sectionPart
.getNode();
622 int partIndex
= partNode
.getIndex();
623 Section section
= sectionPart
.getSection();
624 Node sectionNode
= section
.getNode();
626 if (part
instanceof Paragraph
) {
627 // FIXME adapt to DocBook
628 // Node newNode = sectionNode.addNode(DocBookNames.DBK_MEDIAOBJECT, NodeType.NT_FILE);
629 // newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
630 // JcrUtils.copyBytesAsFile(sectionNode, p(newNode.getIndex()), new byte[0]);
631 // if (partIndex < newNode.getIndex() - 1) {
633 // sectionNode.orderBefore(p(newNode.getIndex()), p(partIndex - 1));
635 // // sectionNode.orderBefore(p(partNode.getIndex()),
636 // // p(newNode.getIndex()));
637 // persistChanges(sectionNode);
638 // DbkImg img = newImg((TextSection) section, newNode);
640 // layout(img.getControl());
641 } else if (part
instanceof DbkImg
) {
642 if (getEdited() == part
)
648 } catch (RepositoryException e
) {
649 throw new JcrException("Cannot upload", e
);
653 protected void deepen() {
658 if (getEdited() instanceof Paragraph
) {
659 Paragraph paragraph
= (Paragraph
) getEdited();
660 Text text
= (Text
) paragraph
.getControl();
661 String txt
= text
.getText();
662 Node paragraphNode
= paragraph
.getNode();
663 Section section
= paragraph
.getSection();
664 Node sectionNode
= section
.getNode();
666 if (section
== mainSection
&& section
instanceof TextSection
&& paragraphNode
.getIndex() == 1
667 && !sectionNode
.hasNode(DbkType
.title
.get())) {
668 DbkSectionTitle sectionTitle
= prepareSectionTitle(section
, txt
);
669 edit(sectionTitle
, 0);
672 Node newSectionNode
= addDbk(sectionNode
, DbkType
.section
);
673 // newSectionNode.addMixin(NodeType.MIX_TITLE);
674 sectionNode
.orderBefore(h(newSectionNode
.getIndex()), h(1));
676 int paragraphIndex
= paragraphNode
.getIndex();
677 String sectionPath
= sectionNode
.getPath();
678 String newSectionPath
= newSectionNode
.getPath();
679 while (sectionNode
.hasNode(p(paragraphIndex
+ 1))) {
680 Node parag
= sectionNode
.getNode(p(paragraphIndex
+ 1));
681 sectionNode
.getSession().move(sectionPath
+ '/' + p(paragraphIndex
+ 1),
682 newSectionPath
+ '/' + DbkType
.para
.get());
683 SectionPart sp
= section
.getSectionPart(parag
.getIdentifier());
684 if (sp
instanceof Control
)
685 ((Control
) sp
).dispose();
688 Node titleNode
= DbkJcrUtils
.addDbk(newSectionNode
, DbkType
.title
);
689 // newSectionNode.addNode(DocBookType.TITLE, DocBookType.TITLE);
690 getTextInterpreter().write(titleNode
, txt
);
692 TextSection newSection
= new TextSection(section
, section
.getStyle(), newSectionNode
);
693 newSection
.setLayoutData(CmsSwtUtils
.fillWidth());
694 newSection
.moveBelow(paragraph
);
697 paragraphNode
.remove();
701 newSection
.getParent().layout();
703 persistChanges(sectionNode
);
704 } else if (getEdited() instanceof DbkSectionTitle
) {
705 DbkSectionTitle sectionTitle
= (DbkSectionTitle
) getEdited();
706 Section section
= sectionTitle
.getSection();
707 Section parentSection
= section
.getParentSection();
708 if (parentSection
== null)
709 return;// cannot deepen main section
710 Node sectionN
= section
.getNode();
711 Node parentSectionN
= parentSection
.getNode();
712 if (sectionN
.getIndex() == 1)
713 return;// cannot deepen first section
714 Node previousSectionN
= parentSectionN
.getNode(h(sectionN
.getIndex() - 1));
715 NodeIterator subSections
= previousSectionN
.getNodes(DbkType
.section
.get());
716 int subsectionsCount
= (int) subSections
.getSize();
717 previousSectionN
.getSession().move(sectionN
.getPath(),
718 previousSectionN
.getPath() + "/" + h(subsectionsCount
+ 1));
720 TextSection newSection
= new TextSection(section
, section
.getStyle(), sectionN
);
722 persistChanges(previousSectionN
);
724 } catch (RepositoryException e
) {
725 throw new JcrException("Cannot deepen " + getEdited(), e
);
729 protected void undeepen() {
734 if (getEdited() instanceof Paragraph
) {
736 } else if (getEdited() instanceof DbkSectionTitle
) {
737 DbkSectionTitle sectionTitle
= (DbkSectionTitle
) getEdited();
738 Section section
= sectionTitle
.getSection();
739 Node sectionNode
= section
.getNode();
740 Section parentSection
= section
.getParentSection();
741 if (parentSection
== null)
742 return;// cannot undeepen main section
744 // choose in which section to merge
745 Section mergedSection
;
746 if (sectionNode
.getIndex() == 1)
747 mergedSection
= section
.getParentSection();
749 Map
<String
, Section
> parentSubsections
= parentSection
.getSubSections();
750 ArrayList
<Section
> lst
= new ArrayList
<Section
>(parentSubsections
.values());
751 mergedSection
= lst
.get(sectionNode
.getIndex() - 1);
753 Node mergedNode
= mergedSection
.getNode();
754 boolean mergedHasSubSections
= mergedNode
.hasNode(DbkType
.section
.get());
756 // title as paragraph
757 Node newParagrapheNode
= addDbk(mergedNode
, para
);
758 // newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
759 if (mergedHasSubSections
)
760 mergedNode
.orderBefore(p(newParagrapheNode
.getIndex()), h(1));
761 String txt
= getTextInterpreter().read(sectionNode
.getNode(DbkType
.title
.get()));
762 getTextInterpreter().write(newParagrapheNode
, txt
);
764 NodeIterator paragraphs
= sectionNode
.getNodes(para
.get());
765 while (paragraphs
.hasNext()) {
766 Node p
= paragraphs
.nextNode();
767 SectionPart sp
= section
.getSectionPart(p
.getIdentifier());
768 if (sp
instanceof Control
)
769 ((Control
) sp
).dispose();
770 mergedNode
.getSession().move(p
.getPath(), mergedNode
.getPath() + '/' + para
.get());
771 if (mergedHasSubSections
)
772 mergedNode
.orderBefore(p(p
.getIndex()), h(1));
775 Iterator
<Section
> subsections
= section
.getSubSections().values().iterator();
776 // NodeIterator sections = sectionNode.getNodes(CMS_H);
777 while (subsections
.hasNext()) {
778 Section subsection
= subsections
.next();
779 Node s
= subsection
.getNode();
780 mergedNode
.getSession().move(s
.getPath(), mergedNode
.getPath() + '/' + DbkType
.section
.get());
781 subsection
.dispose();
785 section
.getNode().remove();
788 refresh(mergedSection
);
789 mergedSection
.getParent().layout();
790 layout(mergedSection
);
791 persistChanges(mergedNode
);
793 } catch (RepositoryException e
) {
794 throw new JcrException("Cannot undeepen " + getEdited(), e
);
799 protected Paragraph
paragraphSplitted(Paragraph paragraph
, Node newNode
) throws RepositoryException
{
800 Section section
= paragraph
.getSection();
801 updateContent(paragraph
);
802 Paragraph newParagraph
= newParagraph((TextSection
) section
, newNode
);
803 newParagraph
.setLayoutData(CmsSwtUtils
.fillWidth());
804 newParagraph
.moveBelow(paragraph
);
805 layout(paragraph
.getControl(), newParagraph
.getControl());
809 protected Paragraph
sectionTitleSplitted(DbkSectionTitle sectionTitle
, Node newNode
) throws RepositoryException
{
810 updateContent(sectionTitle
);
811 Paragraph newParagraph
= newParagraph(sectionTitle
.getSection(), newNode
);
812 // we assume beforeFirst is not null since there was a sectionTitle
813 newParagraph
.moveBelow(sectionTitle
.getSection().getHeader());
814 layout(sectionTitle
.getControl(), newParagraph
.getControl());
818 protected Paragraph
paragraphMergedWithPrevious(Paragraph removed
, Node remaining
) throws RepositoryException
{
819 Section section
= removed
.getSection();
822 Paragraph paragraph
= (Paragraph
) section
.getSectionPart(remaining
.getIdentifier());
823 updateContent(paragraph
);
824 layout(paragraph
.getControl());
828 protected void paragraphMergedWithNext(Paragraph remaining
, Paragraph removed
) throws RepositoryException
{
830 updateContent(remaining
);
831 layout(remaining
.getControl());
835 protected String
p(Integer index
) {
836 StringBuilder sb
= new StringBuilder(6);
837 sb
.append(para
.get()).append('[').append(index
).append(']');
838 return sb
.toString();
841 protected String
h(Integer index
) {
842 StringBuilder sb
= new StringBuilder(5);
843 sb
.append(DbkType
.section
.get()).append('[').append(index
).append(']');
844 return sb
.toString();
848 public Section
getMainSection() {
852 public boolean isFlat() {
856 public TextInterpreter
getTextInterpreter() {
857 return textInterpreter
;
862 public void keyPressed(KeyEvent ke
) {
863 if (log
.isTraceEnabled())
866 if (getEdited() == null)
868 boolean altPressed
= (ke
.stateMask
& SWT
.ALT
) != 0;
869 boolean shiftPressed
= (ke
.stateMask
& SWT
.SHIFT
) != 0;
870 boolean ctrlPressed
= (ke
.stateMask
& SWT
.CTRL
) != 0;
874 if (ke
.keyCode
== SWT
.ESC
) {
877 } else if (ke
.character
== '\r') {
880 } else if (ke
.character
== 'z') {
883 } else if (ke
.character
== 'S') {
886 } else if (ke
.character
== '\t') {
889 } else if (shiftPressed
) {
893 if (getEdited() instanceof Paragraph
) {
894 Paragraph paragraph
= (Paragraph
) getEdited();
895 Section section
= paragraph
.getSection();
896 if (altPressed
&& ke
.keyCode
== SWT
.ARROW_RIGHT
) {
897 edit(section
.nextSectionPart(paragraph
), 0);
898 } else if (altPressed
&& ke
.keyCode
== SWT
.ARROW_LEFT
) {
899 edit(section
.previousSectionPart(paragraph
), 0);
900 } else if (ke
.character
== SWT
.BS
) {
901 Text text
= (Text
) paragraph
.getControl();
902 int caretPosition
= text
.getCaretPosition();
903 if (caretPosition
== 0) {
906 } else if (ke
.character
== SWT
.DEL
) {
907 Text text
= (Text
) paragraph
.getControl();
908 int caretPosition
= text
.getCaretPosition();
909 int charcount
= text
.getCharCount();
910 if (caretPosition
== charcount
) {
916 } catch (Exception e
) {
918 notifyEditionException(e
);
923 public void keyReleased(KeyEvent e
) {
928 protected MouseListener
createMouseListener() {
932 private class ML
extends MouseAdapter
{
933 private static final long serialVersionUID
= 8526890859876770905L;
936 public void mouseDoubleClick(MouseEvent e
) {
938 Control source
= (Control
) e
.getSource();
939 SwtEditablePart composite
= findDataParent(source
);
940 Point point
= new Point(e
.x
, e
.y
);
941 if (composite
instanceof DbkImg
) {
942 if (getCmsEditable().canEdit()) {
943 if (getCmsEditable().isEditing() && !(getEdited() instanceof DbkImg
)) {
944 if (source
== mainSection
)
946 SwtEditablePart part
= findDataParent(source
);
949 getCmsEditable().startEditing();
952 } else if (source
instanceof Label
) {
953 Label lbl
= (Label
) source
;
954 Rectangle bounds
= lbl
.getBounds();
955 float width
= bounds
.width
;
956 float height
= bounds
.height
;
957 float textLength
= lbl
.getText().length();
958 float area
= width
* height
;
959 float charArea
= area
/ textLength
;
960 float lines
= textLength
/ width
;
961 float proportion
= point
.y
* width
+ point
.x
;
962 int pos
= (int) (textLength
* (proportion
/ area
));
964 edit(composite
, (Integer
) pos
);
966 edit(composite
, source
.toDisplay(point
));
972 public void mouseDown(MouseEvent e
) {
973 if (getCmsEditable().isEditing()) {
975 SwtEditablePart composite
= findDataParent((Control
) e
.getSource());
976 if (styledTools
!= null) {
977 List
<String
> styles
= getAvailableStyles(composite
);
978 styledTools
.show(composite
, new Point(e
.x
, e
.y
), styles
);
985 public void mouseUp(MouseEvent e
) {
989 protected List
<String
> getAvailableStyles(SwtEditablePart editablePart
) {
990 return new ArrayList
<>();
993 public void setMaxMediaWidth(Integer maxMediaWidth
) {
994 this.maxMediaWidth
= maxMediaWidth
;
997 public void setShowMainTitle(boolean showMainTitle
) {
998 this.showMainTitle
= showMainTitle
;
1001 public String
getDefaultSectionStyle() {
1002 return defaultSectionStyle
;
1005 public void setDefaultSectionStyle(String defaultSectionStyle
) {
1006 this.defaultSectionStyle
= defaultSectionStyle
;
1009 // FILE UPLOAD LISTENER
1010 private class FUL
implements FileUploadListener
{
1011 public void uploadProgress(FileUploadEvent event
) {
1012 // TODO Monitor upload progress
1015 public void uploadFailed(FileUploadEvent event
) {
1016 throw new RuntimeException("Upload failed " + event
, event
.getException());
1019 public void uploadFinished(FileUploadEvent event
) {
1020 for (FileDetails file
: event
.getFileDetails()) {
1021 if (log
.isDebugEnabled())
1022 log
.debug("Received: " + file
.getFileName());
1024 mainSection
.getDisplay().syncExec(new Runnable() {
1030 FileUploadHandler uploadHandler
= (FileUploadHandler
) event
.getSource();
1031 uploadHandler
.dispose();