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