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