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