]> git.argeo.org Git - gpl/argeo-suite.git/blob - argeo/docbook/ui/AbstractDbkViewer.java
Prepare next development cycle
[gpl/argeo-suite.git] / argeo / docbook / ui / AbstractDbkViewer.java
1 package org.argeo.docbook.ui;
2
3 import static org.argeo.cms.ui.util.CmsUiUtils.fillWidth;
4
5 import java.util.ArrayList;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Observer;
10
11 import javax.jcr.Item;
12 import javax.jcr.Node;
13 import javax.jcr.NodeIterator;
14 import javax.jcr.RepositoryException;
15 import javax.jcr.Session;
16 import javax.jcr.nodetype.NodeType;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.argeo.cms.text.Paragraph;
21 import org.argeo.cms.text.TextInterpreter;
22 import org.argeo.cms.text.TextSection;
23 import org.argeo.cms.ui.CmsEditable;
24 import org.argeo.cms.ui.CmsImageManager;
25 import org.argeo.cms.ui.CmsView;
26 import org.argeo.cms.ui.util.CmsUiUtils;
27 import org.argeo.cms.ui.viewers.AbstractPageViewer;
28 import org.argeo.cms.ui.viewers.EditablePart;
29 import org.argeo.cms.ui.viewers.NodePart;
30 import org.argeo.cms.ui.viewers.PropertyPart;
31 import org.argeo.cms.ui.viewers.Section;
32 import org.argeo.cms.ui.viewers.SectionPart;
33 import org.argeo.cms.ui.widgets.EditableImage;
34 import org.argeo.cms.ui.widgets.EditableText;
35 import org.argeo.cms.ui.widgets.Img;
36 import org.argeo.cms.ui.widgets.StyledControl;
37 import org.argeo.entity.EntityType;
38 import org.argeo.jcr.JcrException;
39 import org.argeo.jcr.JcrUtils;
40 import org.eclipse.rap.fileupload.FileDetails;
41 import org.eclipse.rap.fileupload.FileUploadEvent;
42 import org.eclipse.rap.fileupload.FileUploadHandler;
43 import org.eclipse.rap.fileupload.FileUploadListener;
44 import org.eclipse.rap.rwt.RWT;
45 import org.eclipse.swt.SWT;
46 import org.eclipse.swt.events.KeyEvent;
47 import org.eclipse.swt.events.KeyListener;
48 import org.eclipse.swt.events.MouseAdapter;
49 import org.eclipse.swt.events.MouseEvent;
50 import org.eclipse.swt.events.MouseListener;
51 import org.eclipse.swt.graphics.Point;
52 import org.eclipse.swt.widgets.Composite;
53 import org.eclipse.swt.widgets.Control;
54 import org.eclipse.swt.widgets.Text;
55
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 Log log = LogFactory.getLog(AbstractDbkViewer.class);
60
61 private final Section mainSection;
62
63 private TextInterpreter textInterpreter = new DbkTextInterpreter();
64 private CmsImageManager imageManager;
65
66 private FileUploadListener fileUploadListener;
67 private DbkContextMenu styledTools;
68
69 private final boolean flat;
70
71 protected AbstractDbkViewer(Section parent, int style, CmsEditable cmsEditable) {
72 super(parent, style, cmsEditable);
73 CmsView cmsView = CmsView.getCmsView(parent);
74 // imageManager = cmsView.getImageManager();
75 imageManager = new DbkImageManager();
76 flat = SWT.FLAT == (style & SWT.FLAT);
77
78 if (getCmsEditable().canEdit()) {
79 fileUploadListener = new FUL();
80 styledTools = new DbkContextMenu(this, parent.getShell());
81 }
82 this.mainSection = parent;
83 initModelIfNeeded(mainSection.getNode());
84 // layout(this.mainSection);
85 }
86
87 @Override
88 public Control getControl() {
89 return mainSection;
90 }
91
92 protected void refresh(Control control) throws RepositoryException {
93 if (!(control instanceof Section))
94 return;
95 Section section = (Section) control;
96 if (section instanceof TextSection) {
97 CmsUiUtils.clear(section);
98 Node node = section.getNode();
99 TextSection textSection = (TextSection) section;
100 if (node.hasNode(DocBookTypes.TITLE)) {
101 if (section.getHeader() == null)
102 section.createHeader();
103 Node titleNode = node.getNode(DocBookTypes.TITLE);
104 DocBookSectionTitle title = newSectionTitle(textSection, titleNode);
105 title.setLayoutData(CmsUiUtils.fillWidth());
106 updateContent(title);
107 }
108
109 for (NodeIterator ni = node.getNodes(); ni.hasNext();) {
110 Node child = ni.nextNode();
111 SectionPart sectionPart = null;
112 if (child.isNodeType(DocBookTypes.MEDIAOBJECT)) {
113 if (child.hasNode(DocBookTypes.IMAGEOBJECT)) {
114 Node imageNode = child.getNode(DocBookTypes.IMAGEOBJECT).getNode(DocBookTypes.INFO)
115 .getNode(EntityType.box.get());
116 sectionPart = newImg(textSection, imageNode);
117 }
118 } else if (child.isNodeType(DocBookTypes.PARA)) {
119 sectionPart = newParagraph(textSection, child);
120 } else {
121 sectionPart = newSectionPart(textSection, child);
122 // if (sectionPart == null)
123 // throw new IllegalArgumentException("Unsupported node " + child);
124 // TODO list node types in exception
125 }
126 if (sectionPart != null && sectionPart instanceof Control)
127 ((Control) sectionPart).setLayoutData(CmsUiUtils.fillWidth());
128 }
129
130 // if (!flat)
131 for (NodeIterator ni = section.getNode().getNodes(DocBookNames.DBK_SECTION); ni.hasNext();) {
132 Node child = ni.nextNode();
133 if (child.isNodeType(DocBookTypes.SECTION)) {
134 TextSection newSection = new TextSection(section, SWT.NONE, child);
135 newSection.setLayoutData(CmsUiUtils.fillWidth());
136 refresh(newSection);
137 }
138 }
139 } else {
140 for (Section s : section.getSubSections().values())
141 refresh(s);
142 }
143 // section.layout(true, true);
144 }
145
146 /** To be overridden in order to provide additional SectionPart types */
147 protected SectionPart newSectionPart(TextSection textSection, Node node) {
148 return null;
149 }
150
151 // CRUD
152 protected Paragraph newParagraph(TextSection parent, Node node) throws RepositoryException {
153 Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
154 updateContent(paragraph);
155 paragraph.setLayoutData(fillWidth());
156 paragraph.setMouseListener(getMouseListener());
157 return paragraph;
158 }
159
160 protected Img newImg(TextSection parent, Node node) {
161 try {
162 Img img = new Img(parent, parent.getStyle(), node, imageManager) {
163 private static final long serialVersionUID = 1297900641952417540L;
164
165 @Override
166 protected void setContainerLayoutData(Composite composite) {
167 composite.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
168 }
169
170 @Override
171 protected void setControlLayoutData(Control control) {
172 control.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
173 }
174 };
175 img.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
176 updateContent(img);
177 img.setMouseListener(getMouseListener());
178 return img;
179 } catch (RepositoryException e) {
180 throw new JcrException("Cannot add new image " + node, e);
181 }
182 }
183
184 protected DocBookSectionTitle newSectionTitle(TextSection parent, Node titleNode) throws RepositoryException {
185 int style = parent.getStyle();
186 Composite titleParent = newSectionHeader(parent);
187 if (parent.isTitleReadOnly())
188 style = style | SWT.READ_ONLY;
189 DocBookSectionTitle title = new DocBookSectionTitle(titleParent, style, titleNode);
190 updateContent(title);
191 title.setMouseListener(getMouseListener());
192 return title;
193 }
194
195 /**
196 * To be overridden in order to provide additional processing at the section
197 * level.
198 *
199 * @return the parent to use for the {@link DocBookSectionTitle}, by default
200 * {@link Section#getHeader()}
201 */
202 protected Composite newSectionHeader(TextSection section) {
203 return section.getHeader();
204 }
205
206 protected DocBookSectionTitle prepareSectionTitle(Section newSection, String titleText) throws RepositoryException {
207 Node sectionNode = newSection.getNode();
208 Node titleNode;
209 if (!sectionNode.hasNode(DocBookTypes.TITLE)) {
210 titleNode = sectionNode.addNode(DocBookTypes.TITLE, DocBookTypes.TITLE);
211 } else {
212 titleNode = sectionNode.getNode(DocBookTypes.TITLE);
213 }
214 getTextInterpreter().write(titleNode, titleText);
215 if (newSection.getHeader() == null)
216 newSection.createHeader();
217 DocBookSectionTitle sectionTitle = newSectionTitle((TextSection) newSection, sectionNode);
218 return sectionTitle;
219 }
220
221 protected void updateContent(EditablePart part) throws RepositoryException {
222 if (part instanceof SectionPart) {
223 SectionPart sectionPart = (SectionPart) part;
224 Node partNode = sectionPart.getNode();
225
226 if (part instanceof StyledControl && (sectionPart.getSection() instanceof TextSection)) {
227 TextSection section = (TextSection) sectionPart.getSection();
228 StyledControl styledControl = (StyledControl) part;
229 if (partNode.isNodeType(DocBookTypes.PARA)) {
230 String style = partNode.hasProperty(DocBookNames.DBK_ROLE)
231 ? partNode.getProperty(DocBookNames.DBK_ROLE).getString()
232 : section.getDefaultTextStyle();
233 styledControl.setStyle(style);
234 }
235 }
236 // use control AFTER setting style, since it may have been reset
237
238 if (part instanceof EditableText) {
239 EditableText paragraph = (EditableText) part;
240 if (paragraph == getEdited())
241 paragraph.setText(textInterpreter.raw(partNode));
242 else
243 paragraph.setText(textInterpreter.readSimpleHtml(partNode));
244 } else if (part instanceof EditableImage) {
245 EditableImage editableImage = (EditableImage) part;
246 imageManager.load(partNode, part.getControl(), editableImage.getPreferredImageSize());
247 }
248 } else if (part instanceof DocBookSectionTitle) {
249 DocBookSectionTitle title = (DocBookSectionTitle) part;
250 title.setStyle(title.getSection().getTitleStyle());
251 // use control AFTER setting style
252 if (title == getEdited())
253 title.setText(textInterpreter.read(title.getNode()));
254 else
255 title.setText(textInterpreter.raw(title.getNode()));
256 }
257 }
258
259 // OVERRIDDEN FROM PARENT VIEWER
260 @Override
261 protected void save(EditablePart part) throws RepositoryException {
262 if (part instanceof EditableText) {
263 EditableText et = (EditableText) part;
264 if (!et.getEditable())
265 return;
266 String text = ((Text) et.getControl()).getText();
267
268 // String[] lines = text.split("[\r\n]+");
269 String[] lines = { text };
270 assert lines.length != 0;
271 saveLine(part, lines[0]);
272 if (lines.length > 1) {
273 ArrayList<Control> toLayout = new ArrayList<Control>();
274 if (part instanceof Paragraph) {
275 Paragraph currentParagraph = (Paragraph) et;
276 Section section = currentParagraph.getSection();
277 Node sectionNode = section.getNode();
278 Node currentParagraphN = currentParagraph.getNode();
279 for (int i = 1; i < lines.length; i++) {
280 Node newNode = sectionNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
281 // newNode.addMixin(CmsTypes.CMS_STYLED);
282 saveLine(newNode, lines[i]);
283 // second node was create as last, if it is not the next
284 // one, it
285 // means there are some in between and we can take the
286 // one at
287 // index+1 for the re-order
288 if (newNode.getIndex() > currentParagraphN.getIndex() + 1) {
289 sectionNode.orderBefore(p(newNode.getIndex()), p(currentParagraphN.getIndex() + 1));
290 }
291 Paragraph newParagraph = newParagraph((TextSection) section, newNode);
292 newParagraph.moveBelow(currentParagraph);
293 toLayout.add(newParagraph);
294
295 currentParagraph = newParagraph;
296 currentParagraphN = newNode;
297 }
298 }
299 // TODO or rather return the created paragraphs?
300 layout(toLayout.toArray(new Control[toLayout.size()]));
301 }
302 persistChanges(et.getNode());
303 }
304 }
305
306 protected void saveLine(EditablePart part, String line) {
307 if (part instanceof NodePart) {
308 saveLine(((NodePart) part).getNode(), line);
309 } else if (part instanceof PropertyPart) {
310 saveLine(((PropertyPart) part).getProperty(), line);
311 } else {
312 throw new IllegalArgumentException("Unsupported part " + part);
313 }
314 }
315
316 protected void saveLine(Item item, String line) {
317 line = line.trim();
318 textInterpreter.write(item, line);
319 }
320
321 @Override
322 protected void prepare(EditablePart part, Object caretPosition) {
323 Control control = part.getControl();
324 if (control instanceof Text) {
325 Text text = (Text) control;
326 if (caretPosition != null)
327 if (caretPosition instanceof Integer)
328 text.setSelection((Integer) caretPosition);
329 else if (caretPosition instanceof Point) {
330 // TODO find a way to position the caret at the right place
331 }
332 text.setData(RWT.ACTIVE_KEYS, new String[] { "BACKSPACE", "ESC", "TAB", "SHIFT+TAB", "ALT+ARROW_LEFT",
333 "ALT+ARROW_RIGHT", "ALT+ARROW_UP", "ALT+ARROW_DOWN", "RETURN", "CTRL+RETURN", "ENTER", "DELETE" });
334 text.setData(RWT.CANCEL_KEYS, new String[] { "RETURN", "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT" });
335 text.addKeyListener(this);
336 } else if (part instanceof Img) {
337 ((Img) part).setFileUploadListener(fileUploadListener);
338 }
339 }
340
341 // REQUIRED BY CONTEXT MENU
342 void setParagraphStyle(Paragraph paragraph, String style) {
343 try {
344 Node paragraphNode = paragraph.getNode();
345 if (style == null) {// default
346 if (paragraphNode.hasProperty(DocBookNames.DBK_ROLE))
347 paragraphNode.getProperty(DocBookNames.DBK_ROLE).remove();
348 } else {
349 paragraphNode.setProperty(DocBookNames.DBK_ROLE, style);
350 }
351 persistChanges(paragraphNode);
352 updateContent(paragraph);
353 layoutPage();
354 } catch (RepositoryException e1) {
355 throw new JcrException("Cannot set style " + style + " on " + paragraph, e1);
356 }
357 }
358
359 void insertPart(Section section, Node node) {
360 try {
361 refresh(section);
362 layoutPage();
363 } catch (RepositoryException e) {
364 throw new JcrException("Cannot insert part " + node + " in section " + section.getNode(), e);
365 }
366 }
367
368 void deletePart(SectionPart sectionPart) {
369 try {
370 Node node = sectionPart.getNode();
371 Session session = node.getSession();
372 if (sectionPart instanceof Img) {
373 // FIXME make it more robust
374 node = node.getParent().getParent().getParent();
375 }
376 node.remove();
377 session.save();
378 if (sectionPart instanceof Control)
379 ((Control) sectionPart).dispose();
380 layoutPage();
381 } catch (RepositoryException e1) {
382 throw new JcrException("Cannot delete " + sectionPart, e1);
383 }
384 }
385
386 void deleteSection(Section section) {
387 try {
388 Node node = section.getNode();
389 Session session = node.getSession();
390 node.remove();
391 session.save();
392 section.dispose();
393 layoutPage();
394 } catch (RepositoryException e1) {
395 throw new JcrException("Cannot delete " + section, e1);
396 }
397 }
398
399 String getRawParagraphText(Paragraph paragraph) {
400 return textInterpreter.raw(paragraph.getNode());
401 }
402
403 // COMMANDS
404 protected void splitEdit() {
405 checkEdited();
406 try {
407 if (getEdited() instanceof Paragraph) {
408 Paragraph paragraph = (Paragraph) getEdited();
409 Text text = (Text) paragraph.getControl();
410 int caretPosition = text.getCaretPosition();
411 String txt = text.getText();
412 String first = txt.substring(0, caretPosition);
413 String second = txt.substring(caretPosition);
414 Node firstNode = paragraph.getNode();
415 Node sectionNode = firstNode.getParent();
416
417 // FIXME set content the DocBook way
418 // firstNode.setProperty(CMS_CONTENT, first);
419 Node secondNode = sectionNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
420 // secondNode.addMixin(CmsTypes.CMS_STYLED);
421
422 // second node was create as last, if it is not the next one, it
423 // means there are some in between and we can take the one at
424 // index+1 for the re-order
425 if (secondNode.getIndex() > firstNode.getIndex() + 1) {
426 sectionNode.orderBefore(p(secondNode.getIndex()), p(firstNode.getIndex() + 1));
427 }
428
429 // if we die in between, at least we still have the whole text
430 // in the first node
431 try {
432 textInterpreter.write(secondNode, second);
433 textInterpreter.write(firstNode, first);
434 } catch (Exception e) {
435 // so that no additional nodes are created:
436 JcrUtils.discardUnderlyingSessionQuietly(firstNode);
437 throw e;
438 }
439
440 persistChanges(firstNode);
441
442 Paragraph secondParagraph = paragraphSplitted(paragraph, secondNode);
443 edit(secondParagraph, 0);
444 } else if (getEdited() instanceof DocBookSectionTitle) {
445 DocBookSectionTitle sectionTitle = (DocBookSectionTitle) getEdited();
446 Text text = (Text) sectionTitle.getControl();
447 String txt = text.getText();
448 int caretPosition = text.getCaretPosition();
449 Section section = sectionTitle.getSection();
450 Node sectionNode = section.getNode();
451 Node paragraphNode = sectionNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
452 // paragraphNode.addMixin(CmsTypes.CMS_STYLED);
453
454 textInterpreter.write(paragraphNode, txt.substring(caretPosition));
455 textInterpreter.write(sectionNode.getNode(DocBookTypes.TITLE), txt.substring(0, caretPosition));
456 sectionNode.orderBefore(p(paragraphNode.getIndex()), p(1));
457 persistChanges(sectionNode);
458
459 Paragraph paragraph = sectionTitleSplitted(sectionTitle, paragraphNode);
460 // section.layout();
461 edit(paragraph, 0);
462 }
463 } catch (RepositoryException e) {
464 throw new JcrException("Cannot split " + getEdited(), e);
465 }
466 }
467
468 protected void mergeWithPrevious() {
469 checkEdited();
470 try {
471 Paragraph paragraph = (Paragraph) getEdited();
472 Text text = (Text) paragraph.getControl();
473 String txt = text.getText();
474 Node paragraphNode = paragraph.getNode();
475 if (paragraphNode.getIndex() == 1)
476 return;// do nothing
477 Node sectionNode = paragraphNode.getParent();
478 Node previousNode = sectionNode.getNode(p(paragraphNode.getIndex() - 1));
479 String previousTxt = textInterpreter.read(previousNode);
480 textInterpreter.write(previousNode, previousTxt + txt);
481 paragraphNode.remove();
482 persistChanges(sectionNode);
483
484 Paragraph previousParagraph = paragraphMergedWithPrevious(paragraph, previousNode);
485 edit(previousParagraph, previousTxt.length());
486 } catch (RepositoryException e) {
487 throw new JcrException("Cannot stop editing", e);
488 }
489 }
490
491 protected void mergeWithNext() {
492 checkEdited();
493 try {
494 Paragraph paragraph = (Paragraph) getEdited();
495 Text text = (Text) paragraph.getControl();
496 String txt = text.getText();
497 Node paragraphNode = paragraph.getNode();
498 Node sectionNode = paragraphNode.getParent();
499 NodeIterator paragraphNodes = sectionNode.getNodes(DocBookNames.DBK_PARA);
500 long size = paragraphNodes.getSize();
501 if (paragraphNode.getIndex() == size)
502 return;// do nothing
503 Node nextNode = sectionNode.getNode(p(paragraphNode.getIndex() + 1));
504 String nextTxt = textInterpreter.read(nextNode);
505 textInterpreter.write(paragraphNode, txt + nextTxt);
506
507 Section section = paragraph.getSection();
508 Paragraph removed = (Paragraph) section.getSectionPart(nextNode.getIdentifier());
509
510 nextNode.remove();
511 persistChanges(sectionNode);
512
513 paragraphMergedWithNext(paragraph, removed);
514 edit(paragraph, txt.length());
515 } catch (RepositoryException e) {
516 throw new JcrException("Cannot stop editing", e);
517 }
518 }
519
520 protected synchronized void upload(EditablePart part) {
521 try {
522 if (part instanceof SectionPart) {
523 SectionPart sectionPart = (SectionPart) part;
524 Node partNode = sectionPart.getNode();
525 int partIndex = partNode.getIndex();
526 Section section = sectionPart.getSection();
527 Node sectionNode = section.getNode();
528
529 if (part instanceof Paragraph) {
530 // FIXME adapt to DocBook
531 Node newNode = sectionNode.addNode(DocBookNames.DBK_MEDIAOBJECT, NodeType.NT_FILE);
532 newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
533 JcrUtils.copyBytesAsFile(sectionNode, p(newNode.getIndex()), new byte[0]);
534 if (partIndex < newNode.getIndex() - 1) {
535 // was not last
536 sectionNode.orderBefore(p(newNode.getIndex()), p(partIndex - 1));
537 }
538 // sectionNode.orderBefore(p(partNode.getIndex()),
539 // p(newNode.getIndex()));
540 persistChanges(sectionNode);
541 Img img = newImg((TextSection) section, newNode);
542 edit(img, null);
543 layout(img.getControl());
544 } else if (part instanceof Img) {
545 if (getEdited() == part)
546 return;
547 edit(part, null);
548 layoutPage();
549 }
550 }
551 } catch (RepositoryException e) {
552 throw new JcrException("Cannot upload", e);
553 }
554 }
555
556 protected void deepen() {
557 if (flat)
558 return;
559 checkEdited();
560 try {
561 if (getEdited() instanceof Paragraph) {
562 Paragraph paragraph = (Paragraph) getEdited();
563 Text text = (Text) paragraph.getControl();
564 String txt = text.getText();
565 Node paragraphNode = paragraph.getNode();
566 Section section = paragraph.getSection();
567 Node sectionNode = section.getNode();
568 // main title
569 if (section == mainSection && section instanceof TextSection && paragraphNode.getIndex() == 1
570 && !sectionNode.hasNode(DocBookTypes.TITLE)) {
571 DocBookSectionTitle sectionTitle = prepareSectionTitle(section, txt);
572 edit(sectionTitle, 0);
573 return;
574 }
575 Node newSectionNode = sectionNode.addNode(DocBookNames.DBK_SECTION, DocBookTypes.SECTION);
576 // newSectionNode.addMixin(NodeType.MIX_TITLE);
577 sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
578
579 int paragraphIndex = paragraphNode.getIndex();
580 String sectionPath = sectionNode.getPath();
581 String newSectionPath = newSectionNode.getPath();
582 while (sectionNode.hasNode(p(paragraphIndex + 1))) {
583 Node parag = sectionNode.getNode(p(paragraphIndex + 1));
584 sectionNode.getSession().move(sectionPath + '/' + p(paragraphIndex + 1),
585 newSectionPath + '/' + DocBookNames.DBK_PARA);
586 SectionPart sp = section.getSectionPart(parag.getIdentifier());
587 if (sp instanceof Control)
588 ((Control) sp).dispose();
589 }
590 // create title
591 Node titleNode = newSectionNode.addNode(DocBookTypes.TITLE, DocBookTypes.TITLE);
592 getTextInterpreter().write(titleNode, txt);
593
594 TextSection newSection = new TextSection(section, section.getStyle(), newSectionNode);
595 newSection.setLayoutData(CmsUiUtils.fillWidth());
596 newSection.moveBelow(paragraph);
597
598 // dispose
599 paragraphNode.remove();
600 paragraph.dispose();
601
602 refresh(newSection);
603 newSection.getParent().layout();
604 layout(newSection);
605 persistChanges(sectionNode);
606 } else if (getEdited() instanceof DocBookSectionTitle) {
607 DocBookSectionTitle sectionTitle = (DocBookSectionTitle) getEdited();
608 Section section = sectionTitle.getSection();
609 Section parentSection = section.getParentSection();
610 if (parentSection == null)
611 return;// cannot deepen main section
612 Node sectionN = section.getNode();
613 Node parentSectionN = parentSection.getNode();
614 if (sectionN.getIndex() == 1)
615 return;// cannot deepen first section
616 Node previousSectionN = parentSectionN.getNode(h(sectionN.getIndex() - 1));
617 NodeIterator subSections = previousSectionN.getNodes(DocBookNames.DBK_SECTION);
618 int subsectionsCount = (int) subSections.getSize();
619 previousSectionN.getSession().move(sectionN.getPath(),
620 previousSectionN.getPath() + "/" + h(subsectionsCount + 1));
621 section.dispose();
622 TextSection newSection = new TextSection(section, section.getStyle(), sectionN);
623 refresh(newSection);
624 persistChanges(previousSectionN);
625 }
626 } catch (RepositoryException e) {
627 throw new JcrException("Cannot deepen " + getEdited(), e);
628 }
629 }
630
631 protected void undeepen() {
632 if (flat)
633 return;
634 checkEdited();
635 try {
636 if (getEdited() instanceof Paragraph) {
637 upload(getEdited());
638 } else if (getEdited() instanceof DocBookSectionTitle) {
639 DocBookSectionTitle sectionTitle = (DocBookSectionTitle) getEdited();
640 Section section = sectionTitle.getSection();
641 Node sectionNode = section.getNode();
642 Section parentSection = section.getParentSection();
643 if (parentSection == null)
644 return;// cannot undeepen main section
645
646 // choose in which section to merge
647 Section mergedSection;
648 if (sectionNode.getIndex() == 1)
649 mergedSection = section.getParentSection();
650 else {
651 Map<String, Section> parentSubsections = parentSection.getSubSections();
652 ArrayList<Section> lst = new ArrayList<Section>(parentSubsections.values());
653 mergedSection = lst.get(sectionNode.getIndex() - 1);
654 }
655 Node mergedNode = mergedSection.getNode();
656 boolean mergedHasSubSections = mergedNode.hasNode(DocBookNames.DBK_SECTION);
657
658 // title as paragraph
659 Node newParagrapheNode = mergedNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
660 // newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
661 if (mergedHasSubSections)
662 mergedNode.orderBefore(p(newParagrapheNode.getIndex()), h(1));
663 String txt = getTextInterpreter().read(sectionNode.getNode(DocBookTypes.TITLE));
664 getTextInterpreter().write(newParagrapheNode, txt);
665 // move
666 NodeIterator paragraphs = sectionNode.getNodes(DocBookNames.DBK_PARA);
667 while (paragraphs.hasNext()) {
668 Node p = paragraphs.nextNode();
669 SectionPart sp = section.getSectionPart(p.getIdentifier());
670 if (sp instanceof Control)
671 ((Control) sp).dispose();
672 mergedNode.getSession().move(p.getPath(), mergedNode.getPath() + '/' + DocBookNames.DBK_PARA);
673 if (mergedHasSubSections)
674 mergedNode.orderBefore(p(p.getIndex()), h(1));
675 }
676
677 Iterator<Section> subsections = section.getSubSections().values().iterator();
678 // NodeIterator sections = sectionNode.getNodes(CMS_H);
679 while (subsections.hasNext()) {
680 Section subsection = subsections.next();
681 Node s = subsection.getNode();
682 mergedNode.getSession().move(s.getPath(), mergedNode.getPath() + '/' + DocBookNames.DBK_SECTION);
683 subsection.dispose();
684 }
685
686 // remove section
687 section.getNode().remove();
688 section.dispose();
689
690 refresh(mergedSection);
691 mergedSection.getParent().layout();
692 layout(mergedSection);
693 persistChanges(mergedNode);
694 }
695 } catch (RepositoryException e) {
696 throw new JcrException("Cannot undeepen " + getEdited(), e);
697 }
698 }
699
700 // UI CHANGES
701 protected Paragraph paragraphSplitted(Paragraph paragraph, Node newNode) throws RepositoryException {
702 Section section = paragraph.getSection();
703 updateContent(paragraph);
704 Paragraph newParagraph = newParagraph((TextSection) section, newNode);
705 newParagraph.setLayoutData(CmsUiUtils.fillWidth());
706 newParagraph.moveBelow(paragraph);
707 layout(paragraph.getControl(), newParagraph.getControl());
708 return newParagraph;
709 }
710
711 protected Paragraph sectionTitleSplitted(DocBookSectionTitle sectionTitle, Node newNode)
712 throws RepositoryException {
713 updateContent(sectionTitle);
714 Paragraph newParagraph = newParagraph(sectionTitle.getSection(), newNode);
715 // we assume beforeFirst is not null since there was a sectionTitle
716 newParagraph.moveBelow(sectionTitle.getSection().getHeader());
717 layout(sectionTitle.getControl(), newParagraph.getControl());
718 return newParagraph;
719 }
720
721 protected Paragraph paragraphMergedWithPrevious(Paragraph removed, Node remaining) throws RepositoryException {
722 Section section = removed.getSection();
723 removed.dispose();
724
725 Paragraph paragraph = (Paragraph) section.getSectionPart(remaining.getIdentifier());
726 updateContent(paragraph);
727 layout(paragraph.getControl());
728 return paragraph;
729 }
730
731 protected void paragraphMergedWithNext(Paragraph remaining, Paragraph removed) throws RepositoryException {
732 removed.dispose();
733 updateContent(remaining);
734 layout(remaining.getControl());
735 }
736
737 // UTILITIES
738 protected String p(Integer index) {
739 StringBuilder sb = new StringBuilder(6);
740 sb.append(DocBookNames.DBK_PARA).append('[').append(index).append(']');
741 return sb.toString();
742 }
743
744 protected String h(Integer index) {
745 StringBuilder sb = new StringBuilder(5);
746 sb.append(DocBookNames.DBK_SECTION).append('[').append(index).append(']');
747 return sb.toString();
748 }
749
750 // GETTERS / SETTERS
751 public Section getMainSection() {
752 return mainSection;
753 }
754
755 public boolean isFlat() {
756 return flat;
757 }
758
759 public TextInterpreter getTextInterpreter() {
760 return textInterpreter;
761 }
762
763 // KEY LISTENER
764 @Override
765 public void keyPressed(KeyEvent ke) {
766 if (log.isTraceEnabled())
767 log.trace(ke);
768
769 if (getEdited() == null)
770 return;
771 boolean altPressed = (ke.stateMask & SWT.ALT) != 0;
772 boolean shiftPressed = (ke.stateMask & SWT.SHIFT) != 0;
773 boolean ctrlPressed = (ke.stateMask & SWT.CTRL) != 0;
774
775 try {
776 // Common
777 if (ke.keyCode == SWT.ESC) {
778 // cancelEdit();
779 saveEdit();
780 } else if (ke.character == '\r') {
781 if (!shiftPressed)
782 splitEdit();
783 } else if (ke.character == 'z') {
784 if (ctrlPressed)
785 cancelEdit();
786 } else if (ke.character == 'S') {
787 if (ctrlPressed)
788 saveEdit();
789 } else if (ke.character == '\t') {
790 if (!shiftPressed) {
791 deepen();
792 } else if (shiftPressed) {
793 undeepen();
794 }
795 } else {
796 if (getEdited() instanceof Paragraph) {
797 Paragraph paragraph = (Paragraph) getEdited();
798 Section section = paragraph.getSection();
799 if (altPressed && ke.keyCode == SWT.ARROW_RIGHT) {
800 edit(section.nextSectionPart(paragraph), 0);
801 } else if (altPressed && ke.keyCode == SWT.ARROW_LEFT) {
802 edit(section.previousSectionPart(paragraph), 0);
803 } else if (ke.character == SWT.BS) {
804 Text text = (Text) paragraph.getControl();
805 int caretPosition = text.getCaretPosition();
806 if (caretPosition == 0) {
807 mergeWithPrevious();
808 }
809 } else if (ke.character == SWT.DEL) {
810 Text text = (Text) paragraph.getControl();
811 int caretPosition = text.getCaretPosition();
812 int charcount = text.getCharCount();
813 if (caretPosition == charcount) {
814 mergeWithNext();
815 }
816 }
817 }
818 }
819 } catch (Exception e) {
820 ke.doit = false;
821 notifyEditionException(e);
822 }
823 }
824
825 @Override
826 public void keyReleased(KeyEvent e) {
827 }
828
829 // MOUSE LISTENER
830 @Override
831 protected MouseListener createMouseListener() {
832 return new ML();
833 }
834
835 private class ML extends MouseAdapter {
836 private static final long serialVersionUID = 8526890859876770905L;
837
838 @Override
839 public void mouseDoubleClick(MouseEvent e) {
840 if (e.button == 1) {
841 Control source = (Control) e.getSource();
842 EditablePart composite = findDataParent(source);
843 Point point = new Point(e.x, e.y);
844 if (composite instanceof Img) {
845 if (getCmsEditable().canEdit()) {
846 if (getCmsEditable().isEditing() && !(getEdited() instanceof Img)) {
847 if (source == mainSection)
848 return;
849 EditablePart part = findDataParent(source);
850 upload(part);
851 } else {
852 getCmsEditable().startEditing();
853 }
854 }
855 } else
856 edit(composite, source.toDisplay(point));
857 }
858 }
859
860 @Override
861 public void mouseDown(MouseEvent e) {
862 if (getCmsEditable().isEditing()) {
863 if (e.button == 3) {
864 EditablePart composite = findDataParent((Control) e.getSource());
865 if (styledTools != null) {
866 List<String> styles = getAvailableStyles(composite);
867 styledTools.show(composite, new Point(e.x, e.y), styles);
868 }
869 }
870 }
871 }
872
873 @Override
874 public void mouseUp(MouseEvent e) {
875 }
876 }
877
878 protected List<String> getAvailableStyles(EditablePart editablePart) {
879 return new ArrayList<>();
880 }
881
882 // FILE UPLOAD LISTENER
883 private class FUL implements FileUploadListener {
884 public void uploadProgress(FileUploadEvent event) {
885 // TODO Monitor upload progress
886 }
887
888 public void uploadFailed(FileUploadEvent event) {
889 throw new RuntimeException("Upload failed " + event, event.getException());
890 }
891
892 public void uploadFinished(FileUploadEvent event) {
893 for (FileDetails file : event.getFileDetails()) {
894 if (log.isDebugEnabled())
895 log.debug("Received: " + file.getFileName());
896 }
897 mainSection.getDisplay().syncExec(new Runnable() {
898 @Override
899 public void run() {
900 saveEdit();
901 }
902 });
903 FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource();
904 uploadHandler.dispose();
905 }
906 }
907 }