]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/AbstractDbkViewer.java
Deal with tokens
[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 newSectionNode.addMixin(NodeType.MIX_TITLE);
518 sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
519
520 int paragraphIndex = paragraphNode.getIndex();
521 String sectionPath = sectionNode.getPath();
522 String newSectionPath = newSectionNode.getPath();
523 while (sectionNode.hasNode(p(paragraphIndex + 1))) {
524 Node parag = sectionNode.getNode(p(paragraphIndex + 1));
525 sectionNode.getSession().move(sectionPath + '/' + p(paragraphIndex + 1),
526 newSectionPath + '/' + DocBookNames.DBK_PARA);
527 SectionPart sp = section.getSectionPart(parag.getIdentifier());
528 if (sp instanceof Control)
529 ((Control) sp).dispose();
530 }
531 // create property
532 newSectionNode.setProperty(Property.JCR_TITLE, "");
533 getTextInterpreter().write(newSectionNode.getProperty(Property.JCR_TITLE), txt);
534
535 TextSection newSection = new TextSection(section, section.getStyle(), newSectionNode);
536 newSection.setLayoutData(CmsUtils.fillWidth());
537 newSection.moveBelow(paragraph);
538
539 // dispose
540 paragraphNode.remove();
541 paragraph.dispose();
542
543 refresh(newSection);
544 newSection.getParent().layout();
545 layout(newSection);
546 persistChanges(sectionNode);
547 } else if (getEdited() instanceof SectionTitle) {
548 SectionTitle sectionTitle = (SectionTitle) getEdited();
549 Section section = sectionTitle.getSection();
550 Section parentSection = section.getParentSection();
551 if (parentSection == null)
552 return;// cannot deepen main section
553 Node sectionN = section.getNode();
554 Node parentSectionN = parentSection.getNode();
555 if (sectionN.getIndex() == 1)
556 return;// cannot deepen first section
557 Node previousSectionN = parentSectionN.getNode(h(sectionN.getIndex() - 1));
558 NodeIterator subSections = previousSectionN.getNodes(DocBookNames.DBK_SECTION);
559 int subsectionsCount = (int) subSections.getSize();
560 previousSectionN.getSession().move(sectionN.getPath(),
561 previousSectionN.getPath() + "/" + h(subsectionsCount + 1));
562 section.dispose();
563 TextSection newSection = new TextSection(section, section.getStyle(), sectionN);
564 refresh(newSection);
565 persistChanges(previousSectionN);
566 }
567 } catch (RepositoryException e) {
568 throw new CmsException("Cannot deepen " + getEdited(), e);
569 }
570 }
571
572 protected void undeepen() {
573 if (flat)
574 return;
575 checkEdited();
576 try {
577 if (getEdited() instanceof Paragraph) {
578 upload(getEdited());
579 } else if (getEdited() instanceof SectionTitle) {
580 SectionTitle sectionTitle = (SectionTitle) getEdited();
581 Section section = sectionTitle.getSection();
582 Node sectionNode = section.getNode();
583 Section parentSection = section.getParentSection();
584 if (parentSection == null)
585 return;// cannot undeepen main section
586
587 // choose in which section to merge
588 Section mergedSection;
589 if (sectionNode.getIndex() == 1)
590 mergedSection = section.getParentSection();
591 else {
592 Map<String, Section> parentSubsections = parentSection.getSubSections();
593 ArrayList<Section> lst = new ArrayList<Section>(parentSubsections.values());
594 mergedSection = lst.get(sectionNode.getIndex() - 1);
595 }
596 Node mergedNode = mergedSection.getNode();
597 boolean mergedHasSubSections = mergedNode.hasNode(DocBookNames.DBK_SECTION);
598
599 // title as paragraph
600 Node newParagrapheNode = mergedNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
601 // newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
602 if (mergedHasSubSections)
603 mergedNode.orderBefore(p(newParagrapheNode.getIndex()), h(1));
604 String txt = getTextInterpreter().read(sectionNode.getProperty(Property.JCR_TITLE));
605 getTextInterpreter().write(newParagrapheNode, txt);
606 // move
607 NodeIterator paragraphs = sectionNode.getNodes(DocBookNames.DBK_PARA);
608 while (paragraphs.hasNext()) {
609 Node p = paragraphs.nextNode();
610 SectionPart sp = section.getSectionPart(p.getIdentifier());
611 if (sp instanceof Control)
612 ((Control) sp).dispose();
613 mergedNode.getSession().move(p.getPath(), mergedNode.getPath() + '/' + DocBookNames.DBK_PARA);
614 if (mergedHasSubSections)
615 mergedNode.orderBefore(p(p.getIndex()), h(1));
616 }
617
618 Iterator<Section> subsections = section.getSubSections().values().iterator();
619 // NodeIterator sections = sectionNode.getNodes(CMS_H);
620 while (subsections.hasNext()) {
621 Section subsection = subsections.next();
622 Node s = subsection.getNode();
623 mergedNode.getSession().move(s.getPath(), mergedNode.getPath() + '/' + DocBookNames.DBK_SECTION);
624 subsection.dispose();
625 }
626
627 // remove section
628 section.getNode().remove();
629 section.dispose();
630
631 refresh(mergedSection);
632 mergedSection.getParent().layout();
633 layout(mergedSection);
634 persistChanges(mergedNode);
635 }
636 } catch (RepositoryException e) {
637 throw new CmsException("Cannot undeepen " + getEdited(), e);
638 }
639 }
640
641 // UI CHANGES
642 protected Paragraph paragraphSplitted(Paragraph paragraph, Node newNode) throws RepositoryException {
643 Section section = paragraph.getSection();
644 updateContent(paragraph);
645 Paragraph newParagraph = newParagraph((TextSection) section, newNode);
646 newParagraph.setLayoutData(CmsUtils.fillWidth());
647 newParagraph.moveBelow(paragraph);
648 layout(paragraph.getControl(), newParagraph.getControl());
649 return newParagraph;
650 }
651
652 protected Paragraph sectionTitleSplitted(SectionTitle sectionTitle, Node newNode) throws RepositoryException {
653 updateContent(sectionTitle);
654 Paragraph newParagraph = newParagraph(sectionTitle.getSection(), newNode);
655 // we assume beforeFirst is not null since there was a sectionTitle
656 newParagraph.moveBelow(sectionTitle.getSection().getHeader());
657 layout(sectionTitle.getControl(), newParagraph.getControl());
658 return newParagraph;
659 }
660
661 protected Paragraph paragraphMergedWithPrevious(Paragraph removed, Node remaining) throws RepositoryException {
662 Section section = removed.getSection();
663 removed.dispose();
664
665 Paragraph paragraph = (Paragraph) section.getSectionPart(remaining.getIdentifier());
666 updateContent(paragraph);
667 layout(paragraph.getControl());
668 return paragraph;
669 }
670
671 protected void paragraphMergedWithNext(Paragraph remaining, Paragraph removed) throws RepositoryException {
672 removed.dispose();
673 updateContent(remaining);
674 layout(remaining.getControl());
675 }
676
677 // UTILITIES
678 protected String p(Integer index) {
679 StringBuilder sb = new StringBuilder(6);
680 sb.append(DocBookNames.DBK_PARA).append('[').append(index).append(']');
681 return sb.toString();
682 }
683
684 protected String h(Integer index) {
685 StringBuilder sb = new StringBuilder(5);
686 sb.append(DocBookNames.DBK_SECTION).append('[').append(index).append(']');
687 return sb.toString();
688 }
689
690 // GETTERS / SETTERS
691 public Section getMainSection() {
692 return mainSection;
693 }
694
695 public boolean isFlat() {
696 return flat;
697 }
698
699 public TextInterpreter getTextInterpreter() {
700 return textInterpreter;
701 }
702
703 // KEY LISTENER
704 @Override
705 public void keyPressed(KeyEvent ke) {
706 if (log.isTraceEnabled())
707 log.trace(ke);
708
709 if (getEdited() == null)
710 return;
711 boolean altPressed = (ke.stateMask & SWT.ALT) != 0;
712 boolean shiftPressed = (ke.stateMask & SWT.SHIFT) != 0;
713 boolean ctrlPressed = (ke.stateMask & SWT.CTRL) != 0;
714
715 try {
716 // Common
717 if (ke.keyCode == SWT.ESC) {
718 cancelEdit();
719 } else if (ke.character == '\r') {
720 splitEdit();
721 } else if (ke.character == 'S') {
722 if (ctrlPressed)
723 saveEdit();
724 } else if (ke.character == '\t') {
725 if (!shiftPressed) {
726 deepen();
727 } else if (shiftPressed) {
728 undeepen();
729 }
730 } else {
731 if (getEdited() instanceof Paragraph) {
732 Paragraph paragraph = (Paragraph) getEdited();
733 Section section = paragraph.getSection();
734 if (altPressed && ke.keyCode == SWT.ARROW_RIGHT) {
735 edit(section.nextSectionPart(paragraph), 0);
736 } else if (altPressed && ke.keyCode == SWT.ARROW_LEFT) {
737 edit(section.previousSectionPart(paragraph), 0);
738 } else if (ke.character == SWT.BS) {
739 Text text = (Text) paragraph.getControl();
740 int caretPosition = text.getCaretPosition();
741 if (caretPosition == 0) {
742 mergeWithPrevious();
743 }
744 } else if (ke.character == SWT.DEL) {
745 Text text = (Text) paragraph.getControl();
746 int caretPosition = text.getCaretPosition();
747 int charcount = text.getCharCount();
748 if (caretPosition == charcount) {
749 mergeWithNext();
750 }
751 }
752 }
753 }
754 } catch (Exception e) {
755 ke.doit = false;
756 notifyEditionException(e);
757 }
758 }
759
760 @Override
761 public void keyReleased(KeyEvent e) {
762 }
763
764 // MOUSE LISTENER
765 @Override
766 protected MouseListener createMouseListener() {
767 return new ML();
768 }
769
770 private class ML extends MouseAdapter {
771 private static final long serialVersionUID = 8526890859876770905L;
772
773 @Override
774 public void mouseDoubleClick(MouseEvent e) {
775 if (e.button == 1) {
776 Control source = (Control) e.getSource();
777 if (getCmsEditable().canEdit()) {
778 if (getCmsEditable().isEditing() && !(getEdited() instanceof Img)) {
779 if (source == mainSection)
780 return;
781 EditablePart part = findDataParent(source);
782 upload(part);
783 } else {
784 getCmsEditable().startEditing();
785 }
786 }
787 }
788 }
789
790 @Override
791 public void mouseDown(MouseEvent e) {
792 if (getCmsEditable().isEditing()) {
793 if (e.button == 1) {
794 Control source = (Control) e.getSource();
795 EditablePart composite = findDataParent(source);
796 Point point = new Point(e.x, e.y);
797 if (!(composite instanceof Img))
798 edit(composite, source.toDisplay(point));
799 } else if (e.button == 3) {
800 EditablePart composite = findDataParent((Control) e.getSource());
801 if (styledTools != null)
802 styledTools.show(composite, new Point(e.x, e.y));
803 }
804 }
805 }
806
807 @Override
808 public void mouseUp(MouseEvent e) {
809 }
810 }
811
812 // FILE UPLOAD LISTENER
813 private class FUL implements FileUploadListener {
814 public void uploadProgress(FileUploadEvent event) {
815 // TODO Monitor upload progress
816 }
817
818 public void uploadFailed(FileUploadEvent event) {
819 throw new CmsException("Upload failed " + event, event.getException());
820 }
821
822 public void uploadFinished(FileUploadEvent event) {
823 for (FileDetails file : event.getFileDetails()) {
824 if (log.isDebugEnabled())
825 log.debug("Received: " + file.getFileName());
826 }
827 mainSection.getDisplay().syncExec(new Runnable() {
828 @Override
829 public void run() {
830 saveEdit();
831 }
832 });
833 FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource();
834 uploadHandler.dispose();
835 }
836 }
837 }