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