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