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