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