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