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