+package org.argeo.cms.text;
+
+import static javax.jcr.Property.JCR_TITLE;
+import static org.argeo.cms.ui.util.CmsUiUtils.fillWidth;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Observer;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.CmsImageManager;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.AbstractPageViewer;
+import org.argeo.cms.ui.viewers.EditablePart;
+import org.argeo.cms.ui.viewers.NodePart;
+import org.argeo.cms.ui.viewers.PropertyPart;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.EditableImage;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.argeo.cms.ui.widgets.Img;
+import org.argeo.cms.ui.widgets.StyledControl;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.fileupload.FileDetails;
+import org.eclipse.rap.fileupload.FileUploadEvent;
+import org.eclipse.rap.fileupload.FileUploadHandler;
+import org.eclipse.rap.fileupload.FileUploadListener;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+/** Base class for text viewers and editors. */
+public abstract class AbstractTextViewer extends AbstractPageViewer implements
+ CmsNames, KeyListener, Observer {
+ private static final long serialVersionUID = -2401274679492339668L;
+ private final static Log log = LogFactory.getLog(AbstractTextViewer.class);
+
+ private final Section mainSection;
+
+ private TextInterpreter textInterpreter = new TextInterpreterImpl();
+ private CmsImageManager imageManager = CmsUiUtils.getCmsView()
+ .getImageManager();
+
+ private FileUploadListener fileUploadListener;
+ private TextContextMenu styledTools;
+
+ private final boolean flat;
+
+ protected AbstractTextViewer(Section parent, int style,
+ CmsEditable cmsEditable) {
+ super(parent, style, cmsEditable);
+ flat = SWT.FLAT == (style & SWT.FLAT);
+
+ if (getCmsEditable().canEdit()) {
+ fileUploadListener = new FUL();
+ styledTools = new TextContextMenu(this, parent.getDisplay());
+ }
+ this.mainSection = parent;
+ initModelIfNeeded(mainSection.getNode());
+ // layout(this.mainSection);
+ }
+
+ @Override
+ public Control getControl() {
+ return mainSection;
+ }
+
+ protected void refresh(Control control) throws RepositoryException {
+ if (!(control instanceof Section))
+ return;
+ Section section = (Section) control;
+ if (section instanceof TextSection) {
+ CmsUiUtils.clear(section);
+ Node node = section.getNode();
+ TextSection textSection = (TextSection) section;
+ if (node.hasProperty(Property.JCR_TITLE)) {
+ if (section.getHeader() == null)
+ section.createHeader();
+ if (node.hasProperty(Property.JCR_TITLE)) {
+ SectionTitle title = newSectionTitle(textSection, node);
+ title.setLayoutData(CmsUiUtils.fillWidth());
+ updateContent(title);
+ }
+ }
+
+ for (NodeIterator ni = node.getNodes(CMS_P); ni.hasNext();) {
+ Node child = ni.nextNode();
+ final SectionPart sectionPart;
+ if (child.isNodeType(CmsTypes.CMS_IMAGE)
+ || child.isNodeType(NodeType.NT_FILE)) {
+ sectionPart = newImg(textSection, child);
+ } else if (child.isNodeType(CmsTypes.CMS_STYLED)) {
+ sectionPart = newParagraph(textSection, child);
+ } else {
+ sectionPart = newSectionPart(textSection, child);
+ if (sectionPart == null)
+ throw new CmsException("Unsupported node " + child);
+ // TODO list node types in exception
+ }
+ if (sectionPart instanceof Control)
+ ((Control) sectionPart).setLayoutData(CmsUiUtils.fillWidth());
+ }
+
+ if (!flat)
+ for (NodeIterator ni = section.getNode().getNodes(CMS_H); ni
+ .hasNext();) {
+ Node child = ni.nextNode();
+ if (child.isNodeType(CmsTypes.CMS_SECTION)) {
+ TextSection newSection = new TextSection(section,
+ SWT.NONE, child);
+ newSection.setLayoutData(CmsUiUtils.fillWidth());
+ refresh(newSection);
+ }
+ }
+ } else {
+ for (Section s : section.getSubSections().values())
+ refresh(s);
+ }
+ // section.layout();
+ }
+
+ /** To be overridden in order to provide additional SectionPart types */
+ protected SectionPart newSectionPart(TextSection textSection, Node node) {
+ return null;
+ }
+
+ // CRUD
+ protected Paragraph newParagraph(TextSection parent, Node node)
+ throws RepositoryException {
+ Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
+ updateContent(paragraph);
+ paragraph.setLayoutData(fillWidth());
+ paragraph.setMouseListener(getMouseListener());
+ return paragraph;
+ }
+
+ protected Img newImg(TextSection parent, Node node)
+ throws RepositoryException {
+ Img img = new Img(parent, parent.getStyle(), node) {
+ private static final long serialVersionUID = 1297900641952417540L;
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER,
+ SWT.DEFAULT));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER,
+ SWT.DEFAULT));
+ }
+ };
+ img.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ updateContent(img);
+ img.setMouseListener(getMouseListener());
+ return img;
+ }
+
+ protected SectionTitle newSectionTitle(TextSection parent, Node node)
+ throws RepositoryException {
+ SectionTitle title = new SectionTitle(parent.getHeader(),
+ parent.getStyle(), node.getProperty(JCR_TITLE));
+ updateContent(title);
+ title.setMouseListener(getMouseListener());
+ return title;
+ }
+
+ protected SectionTitle prepareSectionTitle(Section newSection,
+ String titleText) throws RepositoryException {
+ Node sectionNode = newSection.getNode();
+ if (!sectionNode.hasProperty(JCR_TITLE))
+ sectionNode.setProperty(Property.JCR_TITLE, "");
+ getTextInterpreter().write(sectionNode.getProperty(Property.JCR_TITLE),
+ titleText);
+ if (newSection.getHeader() == null)
+ newSection.createHeader();
+ SectionTitle sectionTitle = newSectionTitle((TextSection) newSection,
+ sectionNode);
+ return sectionTitle;
+ }
+
+ protected void updateContent(EditablePart part) throws RepositoryException {
+ if (part instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) part;
+ Node partNode = sectionPart.getNode();
+
+ if (part instanceof StyledControl
+ && (sectionPart.getSection() instanceof TextSection)) {
+ TextSection section = (TextSection) sectionPart.getSection();
+ StyledControl styledControl = (StyledControl) part;
+ if (partNode.isNodeType(CmsTypes.CMS_STYLED)) {
+ String style = partNode.hasProperty(CMS_STYLE) ? partNode
+ .getProperty(CMS_STYLE).getString() : section
+ .getDefaultTextStyle();
+ styledControl.setStyle(style);
+ }
+ }
+ // use control AFTER setting style, since it may have been reset
+
+ if (part instanceof EditableText) {
+ EditableText paragraph = (EditableText) part;
+ if (paragraph == getEdited())
+ paragraph.setText(textInterpreter.read(partNode));
+ else
+ paragraph.setText(textInterpreter.raw(partNode));
+ } else if (part instanceof EditableImage) {
+ EditableImage editableImage = (EditableImage) part;
+ imageManager.load(partNode, part.getControl(),
+ editableImage.getPreferredImageSize());
+ }
+ } else if (part instanceof SectionTitle) {
+ SectionTitle title = (SectionTitle) part;
+ title.setStyle(title.getSection().getTitleStyle());
+ // use control AFTER setting style
+ if (title == getEdited())
+ title.setText(textInterpreter.read(title.getProperty()));
+ else
+ title.setText(textInterpreter.raw(title.getProperty()));
+ }
+ }
+
+ // OVERRIDDEN FROM PARENT VIEWER
+ @Override
+ protected void save(EditablePart part) throws RepositoryException {
+ if (part instanceof EditableText) {
+ EditableText et = (EditableText) part;
+ String text = ((Text) et.getControl()).getText();
+
+ String[] lines = text.split("[\r\n]+");
+ assert lines.length != 0;
+ saveLine(part, lines[0]);
+ if (lines.length > 1) {
+ ArrayList<Control> toLayout = new ArrayList<Control>();
+ if (part instanceof Paragraph) {
+ Paragraph currentParagraph = (Paragraph) et;
+ Section section = currentParagraph.getSection();
+ Node sectionNode = section.getNode();
+ Node currentParagraphN = currentParagraph.getNode();
+ for (int i = 1; i < lines.length; i++) {
+ Node newNode = sectionNode.addNode(CMS_P);
+ newNode.addMixin(CmsTypes.CMS_STYLED);
+ saveLine(newNode, lines[i]);
+ // second node was create as last, if it is not the next
+ // one, it
+ // means there are some in between and we can take the
+ // one at
+ // index+1 for the re-order
+ if (newNode.getIndex() > currentParagraphN.getIndex() + 1) {
+ sectionNode.orderBefore(p(newNode.getIndex()),
+ p(currentParagraphN.getIndex() + 1));
+ }
+ Paragraph newParagraph = newParagraph(
+ (TextSection) section, newNode);
+ newParagraph.moveBelow(currentParagraph);
+ toLayout.add(newParagraph);
+
+ currentParagraph = newParagraph;
+ currentParagraphN = newNode;
+ }
+ persistChanges(sectionNode);
+ }
+ // TODO or rather return the created paragarphs?
+ layout(toLayout.toArray(new Control[toLayout.size()]));
+ }
+ }
+ }
+
+ protected void saveLine(EditablePart part, String line) {
+ if (part instanceof NodePart) {
+ saveLine(((NodePart) part).getNode(), line);
+ } else if (part instanceof PropertyPart) {
+ saveLine(((PropertyPart) part).getProperty(), line);
+ } else {
+ throw new CmsException("Unsupported part " + part);
+ }
+ }
+
+ protected void saveLine(Item item, String line) {
+ line = line.trim();
+ textInterpreter.write(item, line);
+ }
+
+ @Override
+ protected void prepare(EditablePart part, Object caretPosition) {
+ Control control = part.getControl();
+ if (control instanceof Text) {
+ Text text = (Text) control;
+ if (caretPosition != null)
+ if (caretPosition instanceof Integer)
+ text.setSelection((Integer) caretPosition);
+ else if (caretPosition instanceof Point) {
+ // TODO find a way to position the caret at the right place
+ }
+ text.setData(RWT.ACTIVE_KEYS, new String[] { "BACKSPACE", "ESC",
+ "TAB", "SHIFT+TAB", "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT",
+ "ALT+ARROW_UP", "ALT+ARROW_DOWN", "RETURN", "CTRL+RETURN",
+ "ENTER", "DELETE" });
+ text.setData(RWT.CANCEL_KEYS, new String[] { "RETURN",
+ "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT" });
+ text.addKeyListener(this);
+ } else if (part instanceof Img) {
+ ((Img) part).setFileUploadListener(fileUploadListener);
+ }
+ }
+
+ // REQUIRED BY CONTEXT MENU
+ void setParagraphStyle(Paragraph paragraph, String style) {
+ try {
+ Node paragraphNode = paragraph.getNode();
+ paragraphNode.setProperty(CMS_STYLE, style);
+ persistChanges(paragraphNode);
+ updateContent(paragraph);
+ layout(paragraph);
+ } catch (RepositoryException e1) {
+ throw new CmsException("Cannot set style " + style + " on "
+ + paragraph, e1);
+ }
+ }
+
+ void deletePart(SectionPart paragraph) {
+ try {
+ Node paragraphNode = paragraph.getNode();
+ Section section = paragraph.getSection();
+ Session session = paragraphNode.getSession();
+ paragraphNode.remove();
+ session.save();
+ if (paragraph instanceof Control)
+ ((Control) paragraph).dispose();
+ layout(section);
+ } catch (RepositoryException e1) {
+ throw new CmsException("Cannot delete " + paragraph, e1);
+ }
+ }
+
+ String getRawParagraphText(Paragraph paragraph) {
+ return textInterpreter.raw(paragraph.getNode());
+ }
+
+ // COMMANDS
+ protected void splitEdit() {
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ String txt = text.getText();
+ String first = txt.substring(0, caretPosition);
+ String second = txt.substring(caretPosition);
+ Node firstNode = paragraph.getNode();
+ Node sectionNode = firstNode.getParent();
+ firstNode.setProperty(CMS_CONTENT, first);
+ Node secondNode = sectionNode.addNode(CMS_P);
+ secondNode.addMixin(CmsTypes.CMS_STYLED);
+ // second node was create as last, if it is not the next one, it
+ // means there are some in between and we can take the one at
+ // index+1 for the re-order
+ if (secondNode.getIndex() > firstNode.getIndex() + 1) {
+ sectionNode.orderBefore(p(secondNode.getIndex()),
+ p(firstNode.getIndex() + 1));
+ }
+
+ // if we die in between, at least we still have the whole text
+ // in the first node
+ try {
+ textInterpreter.write(secondNode, second);
+ textInterpreter.write(firstNode, first);
+ } catch (Exception e) {
+ // so that no additional nodes are created:
+ JcrUtils.discardUnderlyingSessionQuietly(firstNode);
+ throw e;
+ }
+
+ persistChanges(firstNode);
+
+ Paragraph secondParagraph = paragraphSplitted(paragraph,
+ secondNode);
+ edit(secondParagraph, 0);
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Text text = (Text) sectionTitle.getControl();
+ String txt = text.getText();
+ int caretPosition = text.getCaretPosition();
+ Section section = sectionTitle.getSection();
+ Node sectionNode = section.getNode();
+ Node paragraphNode = sectionNode.addNode(CMS_P);
+ paragraphNode.addMixin(CmsTypes.CMS_STYLED);
+ textInterpreter.write(paragraphNode,
+ txt.substring(caretPosition));
+ textInterpreter.write(
+ sectionNode.getProperty(Property.JCR_TITLE),
+ txt.substring(0, caretPosition));
+ sectionNode.orderBefore(p(paragraphNode.getIndex()), p(1));
+ persistChanges(sectionNode);
+
+ Paragraph paragraph = sectionTitleSplitted(sectionTitle,
+ paragraphNode);
+ // section.layout();
+ edit(paragraph, 0);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot split " + getEdited(), e);
+ }
+ }
+
+ protected void mergeWithPrevious() {
+ checkEdited();
+ try {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ if (paragraphNode.getIndex() == 1)
+ return;// do nothing
+ Node sectionNode = paragraphNode.getParent();
+ Node previousNode = sectionNode
+ .getNode(p(paragraphNode.getIndex() - 1));
+ String previousTxt = textInterpreter.read(previousNode);
+ textInterpreter.write(previousNode, previousTxt + txt);
+ paragraphNode.remove();
+ persistChanges(sectionNode);
+
+ Paragraph previousParagraph = paragraphMergedWithPrevious(
+ paragraph, previousNode);
+ edit(previousParagraph, previousTxt.length());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot stop editing", e);
+ }
+ }
+
+ protected void mergeWithNext() {
+ checkEdited();
+ try {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ Node sectionNode = paragraphNode.getParent();
+ NodeIterator paragraphNodes = sectionNode.getNodes(CMS_P);
+ long size = paragraphNodes.getSize();
+ if (paragraphNode.getIndex() == size)
+ return;// do nothing
+ Node nextNode = sectionNode
+ .getNode(p(paragraphNode.getIndex() + 1));
+ String nextTxt = textInterpreter.read(nextNode);
+ textInterpreter.write(paragraphNode, txt + nextTxt);
+
+ Section section = paragraph.getSection();
+ Paragraph removed = (Paragraph) section.getSectionPart(nextNode
+ .getIdentifier());
+
+ nextNode.remove();
+ persistChanges(sectionNode);
+
+ paragraphMergedWithNext(paragraph, removed);
+ edit(paragraph, txt.length());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot stop editing", e);
+ }
+ }
+
+ protected synchronized void upload(EditablePart part) {
+ try {
+ if (part instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) part;
+ Node partNode = sectionPart.getNode();
+ int partIndex = partNode.getIndex();
+ Section section = sectionPart.getSection();
+ Node sectionNode = section.getNode();
+
+ if (part instanceof Paragraph) {
+ Node newNode = sectionNode.addNode(CMS_P, NodeType.NT_FILE);
+ newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+ JcrUtils.copyBytesAsFile(sectionNode,
+ p(newNode.getIndex()), new byte[0]);
+ if (partIndex < newNode.getIndex() - 1) {
+ // was not last
+ sectionNode.orderBefore(p(newNode.getIndex()),
+ p(partIndex - 1));
+ }
+ // sectionNode.orderBefore(p(partNode.getIndex()),
+ // p(newNode.getIndex()));
+ persistChanges(sectionNode);
+ Img img = newImg((TextSection) section, newNode);
+ edit(img, null);
+ layout(img.getControl());
+ } else if (part instanceof Img) {
+ if (getEdited() == part)
+ return;
+ edit(part, null);
+ layout(part.getControl());
+ }
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot upload", e);
+ }
+ }
+
+ protected void deepen() {
+ if (flat)
+ return;
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ Section section = paragraph.getSection();
+ Node sectionNode = section.getNode();
+ // main title
+ if (section == mainSection && section instanceof TextSection
+ && paragraphNode.getIndex() == 1
+ && !sectionNode.hasProperty(JCR_TITLE)) {
+ SectionTitle sectionTitle = prepareSectionTitle(section,
+ txt);
+ edit(sectionTitle, 0);
+ return;
+ }
+ Node newSectionNode = sectionNode.addNode(CMS_H,
+ CmsTypes.CMS_SECTION);
+ sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
+
+ int paragraphIndex = paragraphNode.getIndex();
+ String sectionPath = sectionNode.getPath();
+ String newSectionPath = newSectionNode.getPath();
+ while (sectionNode.hasNode(p(paragraphIndex + 1))) {
+ Node parag = sectionNode.getNode(p(paragraphIndex + 1));
+ sectionNode.getSession().move(
+ sectionPath + '/' + p(paragraphIndex + 1),
+ newSectionPath + '/' + CMS_P);
+ SectionPart sp = section.getSectionPart(parag
+ .getIdentifier());
+ if (sp instanceof Control)
+ ((Control) sp).dispose();
+ }
+ // create property
+ newSectionNode.setProperty(Property.JCR_TITLE, "");
+ getTextInterpreter().write(
+ newSectionNode.getProperty(Property.JCR_TITLE), txt);
+
+ TextSection newSection = new TextSection(section,
+ section.getStyle(), newSectionNode);
+ newSection.setLayoutData(CmsUiUtils.fillWidth());
+ newSection.moveBelow(paragraph);
+
+ // dispose
+ paragraphNode.remove();
+ paragraph.dispose();
+
+ refresh(newSection);
+ newSection.getParent().layout();
+ layout(newSection);
+ persistChanges(sectionNode);
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Section section = sectionTitle.getSection();
+ Section parentSection = section.getParentSection();
+ if (parentSection == null)
+ return;// cannot deepen main section
+ Node sectionN = section.getNode();
+ Node parentSectionN = parentSection.getNode();
+ if (sectionN.getIndex() == 1)
+ return;// cannot deepen first section
+ Node previousSectionN = parentSectionN.getNode(h(sectionN
+ .getIndex() - 1));
+ NodeIterator subSections = previousSectionN.getNodes(CMS_H);
+ int subsectionsCount = (int) subSections.getSize();
+ previousSectionN.getSession().move(
+ sectionN.getPath(),
+ previousSectionN.getPath() + "/"
+ + h(subsectionsCount + 1));
+ section.dispose();
+ TextSection newSection = new TextSection(section,
+ section.getStyle(), sectionN);
+ refresh(newSection);
+ persistChanges(previousSectionN);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot deepen " + getEdited(), e);
+ }
+ }
+
+ protected void undeepen() {
+ if (flat)
+ return;
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ upload(getEdited());
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Section section = sectionTitle.getSection();
+ Node sectionNode = section.getNode();
+ Section parentSection = section.getParentSection();
+ if (parentSection == null)
+ return;// cannot undeepen main section
+
+ // choose in which section to merge
+ Section mergedSection;
+ if (sectionNode.getIndex() == 1)
+ mergedSection = section.getParentSection();
+ else {
+ Map<String, Section> parentSubsections = parentSection
+ .getSubSections();
+ ArrayList<Section> lst = new ArrayList<Section>(
+ parentSubsections.values());
+ mergedSection = lst.get(sectionNode.getIndex() - 1);
+ }
+ Node mergedNode = mergedSection.getNode();
+ boolean mergedHasSubSections = mergedNode.hasNode(CMS_H);
+
+ // title as paragraph
+ Node newParagrapheNode = mergedNode.addNode(CMS_P);
+ newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
+ if (mergedHasSubSections)
+ mergedNode.orderBefore(p(newParagrapheNode.getIndex()),
+ h(1));
+ String txt = getTextInterpreter().read(
+ sectionNode.getProperty(Property.JCR_TITLE));
+ getTextInterpreter().write(newParagrapheNode, txt);
+ // move
+ NodeIterator paragraphs = sectionNode.getNodes(CMS_P);
+ while (paragraphs.hasNext()) {
+ Node p = paragraphs.nextNode();
+ SectionPart sp = section.getSectionPart(p.getIdentifier());
+ if (sp instanceof Control)
+ ((Control) sp).dispose();
+ mergedNode.getSession().move(p.getPath(),
+ mergedNode.getPath() + '/' + CMS_P);
+ if (mergedHasSubSections)
+ mergedNode.orderBefore(p(p.getIndex()), h(1));
+ }
+
+ Iterator<Section> subsections = section.getSubSections()
+ .values().iterator();
+ // NodeIterator sections = sectionNode.getNodes(CMS_H);
+ while (subsections.hasNext()) {
+ Section subsection = subsections.next();
+ Node s = subsection.getNode();
+ mergedNode.getSession().move(s.getPath(),
+ mergedNode.getPath() + '/' + CMS_H);
+ subsection.dispose();
+ }
+
+ // remove section
+ section.getNode().remove();
+ section.dispose();
+
+ refresh(mergedSection);
+ mergedSection.getParent().layout();
+ layout(mergedSection);
+ persistChanges(mergedNode);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot undeepen " + getEdited(), e);
+ }
+ }
+
+ // UI CHANGES
+ protected Paragraph paragraphSplitted(Paragraph paragraph, Node newNode)
+ throws RepositoryException {
+ Section section = paragraph.getSection();
+ updateContent(paragraph);
+ Paragraph newParagraph = newParagraph((TextSection) section, newNode);
+ newParagraph.setLayoutData(CmsUiUtils.fillWidth());
+ newParagraph.moveBelow(paragraph);
+ layout(paragraph.getControl(), newParagraph.getControl());
+ return newParagraph;
+ }
+
+ protected Paragraph sectionTitleSplitted(SectionTitle sectionTitle,
+ Node newNode) throws RepositoryException {
+ updateContent(sectionTitle);
+ Paragraph newParagraph = newParagraph(sectionTitle.getSection(),
+ newNode);
+ // we assume beforeFirst is not null since there was a sectionTitle
+ newParagraph.moveBelow(sectionTitle.getSection().getHeader());
+ layout(sectionTitle.getControl(), newParagraph.getControl());
+ return newParagraph;
+ }
+
+ protected Paragraph paragraphMergedWithPrevious(Paragraph removed,
+ Node remaining) throws RepositoryException {
+ Section section = removed.getSection();
+ removed.dispose();
+
+ Paragraph paragraph = (Paragraph) section.getSectionPart(remaining
+ .getIdentifier());
+ updateContent(paragraph);
+ layout(paragraph.getControl());
+ return paragraph;
+ }
+
+ protected void paragraphMergedWithNext(Paragraph remaining,
+ Paragraph removed) throws RepositoryException {
+ removed.dispose();
+ updateContent(remaining);
+ layout(remaining.getControl());
+ }
+
+ // UTILITIES
+ protected String p(Integer index) {
+ StringBuilder sb = new StringBuilder(6);
+ sb.append(CMS_P).append('[').append(index).append(']');
+ return sb.toString();
+ }
+
+ protected String h(Integer index) {
+ StringBuilder sb = new StringBuilder(5);
+ sb.append(CMS_H).append('[').append(index).append(']');
+ return sb.toString();
+ }
+
+ // GETTERS / SETTERS
+ public Section getMainSection() {
+ return mainSection;
+ }
+
+ public boolean isFlat() {
+ return flat;
+ }
+
+ public TextInterpreter getTextInterpreter() {
+ return textInterpreter;
+ }
+
+ // KEY LISTENER
+ @Override
+ public void keyPressed(KeyEvent ke) {
+ if (log.isTraceEnabled())
+ log.trace(ke);
+
+ if (getEdited() == null)
+ return;
+ boolean altPressed = (ke.stateMask & SWT.ALT) != 0;
+ boolean shiftPressed = (ke.stateMask & SWT.SHIFT) != 0;
+ boolean ctrlPressed = (ke.stateMask & SWT.CTRL) != 0;
+
+ try {
+ // Common
+ if (ke.keyCode == SWT.ESC) {
+ cancelEdit();
+ } else if (ke.character == '\r') {
+ splitEdit();
+ } else if (ke.character == 'S') {
+ if (ctrlPressed)
+ saveEdit();
+ } else if (ke.character == '\t') {
+ if (!shiftPressed) {
+ deepen();
+ } else if (shiftPressed) {
+ undeepen();
+ }
+ } else {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Section section = paragraph.getSection();
+ if (altPressed && ke.keyCode == SWT.ARROW_RIGHT) {
+ edit(section.nextSectionPart(paragraph), 0);
+ } else if (altPressed && ke.keyCode == SWT.ARROW_LEFT) {
+ edit(section.previousSectionPart(paragraph), 0);
+ } else if (ke.character == SWT.BS) {
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ if (caretPosition == 0) {
+ mergeWithPrevious();
+ }
+ } else if (ke.character == SWT.DEL) {
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ int charcount = text.getCharCount();
+ if (caretPosition == charcount) {
+ mergeWithNext();
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ ke.doit = false;
+ notifyEditionException(e);
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+
+ // MOUSE LISTENER
+ @Override
+ protected MouseListener createMouseListener() {
+ return new ML();
+ }
+
+ private class ML extends MouseAdapter {
+ private static final long serialVersionUID = 8526890859876770905L;
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ if (getCmsEditable().canEdit()) {
+ if (getCmsEditable().isEditing()
+ && !(getEdited() instanceof Img)) {
+ if (source == mainSection)
+ return;
+ EditablePart part = findDataParent(source);
+ upload(part);
+ } else {
+ getCmsEditable().startEditing();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (getCmsEditable().isEditing()) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ EditablePart composite = findDataParent(source);
+ Point point = new Point(e.x, e.y);
+ if (!(composite instanceof Img))
+ edit(composite, source.toDisplay(point));
+ } else if (e.button == 3) {
+ EditablePart composite = findDataParent((Control) e
+ .getSource());
+ if (styledTools != null)
+ styledTools.show(composite, new Point(e.x, e.y));
+ }
+ }
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+ }
+
+ // FILE UPLOAD LISTENER
+ private class FUL implements FileUploadListener {
+ public void uploadProgress(FileUploadEvent event) {
+ // TODO Monitor upload progress
+ }
+
+ public void uploadFailed(FileUploadEvent event) {
+ throw new CmsException("Upload failed " + event,
+ event.getException());
+ }
+
+ public void uploadFinished(FileUploadEvent event) {
+ for (FileDetails file : event.getFileDetails()) {
+ if (log.isDebugEnabled())
+ log.debug("Received: " + file.getFileName());
+ }
+ mainSection.getDisplay().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ saveEdit();
+ }
+ });
+ FileUploadHandler uploadHandler = (FileUploadHandler) event
+ .getSource();
+ uploadHandler.dispose();
+ }
+ }
+}
\ No newline at end of file