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