/**
* Copyright 2013 European Parliament
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and limitations under the Licence.
*/
package org.nsesa.editor.gwt.an.drafting.client.ui.main.document;
import com.allen_sauer.gwt.dnd.client.DragContext;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.allen_sauer.gwt.dnd.client.drop.VerticalPanelDropController;
import com.bfr.client.selection.Range;
import com.bfr.client.selection.RangeEndPoint;
import com.bfr.client.selection.Selection;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Text;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.google.web.bindery.event.shared.HandlerRegistration;
import org.nsesa.editor.gwt.an.common.client.event.SelectionEvent;
import org.nsesa.editor.gwt.an.common.client.event.SelectionEventHandler;
import org.nsesa.editor.gwt.an.common.client.mode.StructureViewMode;
import org.nsesa.editor.gwt.an.common.client.ui.overlay.document.AkomaNtosoUtil;
import org.nsesa.editor.gwt.an.common.client.ui.overlay.document.gen.akomantoso20.Num;
import org.nsesa.editor.gwt.an.common.client.ui.overlay.document.gen.akomantoso20.Preface;
import org.nsesa.editor.gwt.an.drafting.client.event.DocumentToggleStructureEvent;
import org.nsesa.editor.gwt.an.drafting.client.event.DocumentToggleStructureEventHandler;
import org.nsesa.editor.gwt.an.drafting.client.ui.main.document.numbering.NumberingController;
import org.nsesa.editor.gwt.an.drafting.client.ui.main.document.outline.OutlineController;
import org.nsesa.editor.gwt.an.drafting.client.ui.main.document.source.SourceController;
import org.nsesa.editor.gwt.an.drafting.client.ui.main.document.path.PathController;
import org.nsesa.editor.gwt.an.drafting.client.ui.main.document.picker.PickerController;
import org.nsesa.editor.gwt.core.client.ClientFactory;
import org.nsesa.editor.gwt.core.client.ServiceFactory;
import org.nsesa.editor.gwt.core.client.event.*;
import org.nsesa.editor.gwt.core.client.event.document.DocumentScrollToEvent;
import org.nsesa.editor.gwt.core.client.event.document.DocumentScrollToEventHandler;
import org.nsesa.editor.gwt.core.client.event.visualstructure.*;
import org.nsesa.editor.gwt.core.client.event.widget.*;
import org.nsesa.editor.gwt.core.client.keyboard.KeyboardListener;
import org.nsesa.editor.gwt.core.client.mode.ActiveState;
import org.nsesa.editor.gwt.core.client.ui.document.DefaultDocumentController;
import org.nsesa.editor.gwt.core.client.ui.document.DocumentInjector;
import org.nsesa.editor.gwt.core.client.ui.document.sourcefile.actionbar.ActionBarController;
import org.nsesa.editor.gwt.core.client.ui.document.sourcefile.actionbar.create.ActionBarCreatePanelController;
import org.nsesa.editor.gwt.core.client.ui.overlay.*;
import org.nsesa.editor.gwt.core.client.ui.overlay.document.*;
import org.nsesa.editor.gwt.core.client.ui.rte.event.RTEStructureChangedEvent;
import org.nsesa.editor.gwt.core.client.ui.rte.event.RTEStructureChangedEventHandler;
import org.nsesa.editor.gwt.core.client.util.NodeUtil;
import org.nsesa.editor.gwt.core.client.util.OverlayUtil;
import org.nsesa.editor.gwt.core.client.util.OverlayWidgetHolder;
import org.nsesa.editor.gwt.core.shared.DocumentContentDTO;
import org.nsesa.editor.gwt.core.shared.DocumentDTO;
import org.nsesa.editor.gwt.core.shared.OverlayWidgetOrigin;
import org.nsesa.editor.gwt.inline.client.ui.inline.InlineEditorController;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Date: 05/02/13 09:32
*
* @author <a href="mailto:philip.luppens@gmail.com">Philip Luppens</a>
* @version $Id$
*/
public class DraftingDocumentController extends DefaultDocumentController {
private static final Logger LOG = Logger.getLogger(DraftingDocumentController.class.getName());
public static final int SCROLL_TO_OFFSET = 200;
private final OverlaySnippetFactory overlaySnippetFactory;
private final OverlaySnippetEvaluator overlaySnippetEvaluator;
private final OverlayWidgetInjectionStrategy overlayWidgetInjectionStrategy;
private final Formatter formatter;
private OutlineController outlineController;
private SourceController sourceController;
private ActionBarController actionBarController;
private PickerController pickerController;
private NumberingController numberingController;
private PathController pathController;
// popup with available child controllers
private ActionBarCreatePanelController actionBarCreatePanelController;
private FocusPanel actionBarCreatePanelFocusPanel = new FocusPanel();
private PopupPanel actionBarCreatePanelControllerPopup = new PopupPanel();
// ------------- selector ------------------------
private final OverlayWidgetSelector upDownOverlayWidgetSelector = new OverlayWidgetSelector() {
@Override
public boolean select(OverlayWidget toSelect) {
return toSelect.getOverlayElement().getClassName().contains("nsesa-inline")
|| AkomaNtosoUtil.representsBlock(toSelect);
}
};
// ------------- event handler registration -----------
private HandlerRegistration documentToggleStructureEventHandler;
private HandlerRegistration switchTabEventHandlerRegistration;
private HandlerRegistration notificationEventHandlerRegistration;
private HandlerRegistration confirmationEventHandlerRegistration;
private HandlerRegistration criticalEventHandlerRegistration;
private HandlerRegistration overlayWidgetSelectEventHandlerRegistration;
private HandlerRegistration resizeEventHandlerRegistration;
private HandlerRegistration rteStructureChangedEventHandlerRegistration;
private HandlerRegistration documentScrollToEventHandlerRegistration;
private HandlerRegistration keyComboHandlerRegistration;
private HandlerRegistration draftingToggleEventHandlerRegistration;
private HandlerRegistration draftingAttributesToggleEventHandlerRegistration;
private HandlerRegistration numberingTypeEventHandlerRegistration;
private HandlerRegistration formatEventHandlerRegistration;
// ----------------- keyboard shortcuts ---------------------
private KeyboardListener.KeyCombo ctrlEnter = new KeyboardListener.KeyCombo(false, false, true, KeyCodes.KEY_ENTER);
private KeyboardListener.KeyCombo enter = new KeyboardListener.KeyCombo(false, false, false, KeyCodes.KEY_ENTER);
private KeyboardListener.KeyCombo tab = new KeyboardListener.KeyCombo(false, false, false, KeyCodes.KEY_TAB);
private KeyboardListener.KeyCombo escape = new KeyboardListener.KeyCombo(false, false, false, KeyCodes.KEY_ESCAPE);
private KeyboardListener.KeyCombo delete = new KeyboardListener.KeyCombo(false, false, false, KeyCodes.KEY_DELETE);
private KeyboardListener.KeyCombo ctrlS = new KeyboardListener.KeyCombo(false, false, true, 83); // 83 is 's' key
private KeyboardListener.KeyCombo upArrow = new KeyboardListener.KeyCombo(false, false, false, KeyCodes.KEY_UP);
private KeyboardListener.KeyCombo downArrow = new KeyboardListener.KeyCombo(false, false, false, KeyCodes.KEY_DOWN);
private KeyboardListener.KeyCombo altUpArrow = new KeyboardListener.KeyCombo(false, true, false, KeyCodes.KEY_UP);
private KeyboardListener.KeyCombo altDownArrow = new KeyboardListener.KeyCombo(false, true, false, KeyCodes.KEY_DOWN);
private Range currentSelectionRange;
private HandlerRegistration selectionEventHandlerRegistration;
private HandlerRegistration overlayWidgetModifyHandlerRegistration;
private HandlerRegistration overlayWidgetDeleteHandlerRegistration;
private HandlerRegistration overlayWidgetNewEventHandlerRegistration;
@Inject
public DraftingDocumentController(final ClientFactory clientFactory,
final ServiceFactory serviceFactory,
final OverlayFactory overlayFactory,
final Locator locator,
final Creator creator,
final Mover mover,
final OverlayWidgetInjectionStrategy overlayWidgetInjectionStrategy,
final OverlaySnippetFactory overlaySnippetFactory,
final OverlaySnippetEvaluator overlaySnippetEvaluator,
final @Named("xml") Formatter formatter) {
super(clientFactory, serviceFactory, overlayFactory, locator, creator, mover);
this.overlaySnippetFactory = overlaySnippetFactory;
this.overlaySnippetEvaluator = overlaySnippetEvaluator;
this.formatter = formatter;
this.overlayWidgetInjectionStrategy = overlayWidgetInjectionStrategy;
}
@Override
public void registerKeyCombos() {
super.registerKeyCombos();
clientFactory.getKeyboardListener().register(ctrlEnter, enter, tab, escape, delete, ctrlS, upArrow, downArrow, altUpArrow, altDownArrow);
}
@Override
public void registerModes() {
super.registerModes();
registerMode(StructureViewMode.KEY, new StructureViewMode(this));
}
@Override
public void registerListeners() {
documentScrollToEventHandlerRegistration = clientFactory.getEventBus().addHandler(DocumentScrollToEvent.TYPE, new DocumentScrollToEventHandler() {
@Override
public void onEvent(DocumentScrollToEvent event) {
sourceFileController.scrollTo(event.getTarget(), event.getOffset());
}
});
// forward the resize event
resizeEventHandlerRegistration = clientFactory.getEventBus().addHandler(ResizeEvent.TYPE, new ResizeEventHandler() {
@Override
public void onEvent(ResizeEvent event) {
documentEventBus.fireEvent(event);
view.setDocumentHeight(event.getHeight());
}
});
rteStructureChangedEventHandlerRegistration = clientFactory.getEventBus().addHandler(RTEStructureChangedEvent.TYPE, new RTEStructureChangedEventHandler() {
@Override
public void onEvent(final RTEStructureChangedEvent event) {
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
handleRTEOverlayWidgetModify(event.getOverlayWidget());
}
});
}
});
selectionEventHandlerRegistration = clientFactory.getEventBus().addHandler(SelectionEvent.TYPE, new SelectionEventHandler() {
@Override
public void onEvent(SelectionEvent event) {
if (event.getSelection().getRange() != null && !"".equalsIgnoreCase(event.getSelection().getRange().getText().trim())) {
setCurrentSelectionRange(event.getSelection().getRange());
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Current selection: " + (currentSelectionRange != null ? currentSelectionRange.getHtmlText() : "<<null>>"));
}
}
}
});
overlayWidgetSelectEventHandlerRegistration = documentEventBus.addHandler(OverlayWidgetSelectEvent.TYPE, new OverlayWidgetSelectEventHandler() {
@Override
public void onEvent(OverlayWidgetSelectEvent event) {
final OverlayWidget activeOverlayWidget = event.getOverlayWidget();
sourceFileController.setActiveOverlayWidget(activeOverlayWidget);
sourceController.setOverlayWidget(activeOverlayWidget);
if (activeOverlayWidget != null) {
final OverlayWidget block = AkomaNtosoUtil.findEnclosingBlock(activeOverlayWidget);
if (block != null) {
//actionBarCreatePanelController.setOverlayWidget(block);
//actionBarController.attach(block, DraftingDocumentController.this);
// actionBarController.setLocation(locator.getLocation(block, document.getLanguageIso(), true));
numberingController.setOverlayWidget(block);
}
pickerController.setOverlayWidget(activeOverlayWidget);
pathController.setOverlayWidget(activeOverlayWidget);
// set the cursor
final Range browserRange = Selection.getBrowserRange();
if (browserRange != null) {
final OverlayWidgetHolder holder = new OverlayWidgetHolder();
activeOverlayWidget.walk(new OverlayWidgetWalker.DefaultOverlayWidgetVisitor() {
@Override
public boolean visit(OverlayWidget visited) {
if (holder.getOverlayWidget() == null /* only find the first one */ && visited.getOverlayElement().getClassName().contains("caretPosition")) {
holder.setOverlayWidget(visited);
return false;
}
return true;
}
});
if (holder.getOverlayWidget() == null) {
activeOverlayWidget.walk(new OverlayWidgetWalker.DefaultOverlayWidgetVisitor() {
@Override
public boolean visit(OverlayWidget visited) {
if (holder.getOverlayWidget() == null /* only find the first one */ && activeOverlayWidget != visited) {
if (AkomaNtosoUtil.representsNum(visited)) {
holder.setOverlayWidget(visited.getNextSibling(new OverlayWidgetSelector.AnyOverlayWidgetSelector()));
}
return false;
}
return true;
}
});
}
final OverlayWidget toFocus = holder.getOverlayWidget() != null ? holder.getOverlayWidget() : activeOverlayWidget;
activeOverlayWidget.getOverlayElement().focus();
final Text text = NodeUtil.getText(toFocus.getOverlayElement(), true);
if (text != null) {
final String trimmedContent = text.getData().trim();
if (toFocus.getOverlayElement().getClassName().contains("caretPosition") || (trimmedContent.startsWith("{")) && trimmedContent.endsWith("}")) {
Selection.getSelection().setRange(new Range(new RangeEndPoint(text, true), new RangeEndPoint(text, false)));
}
} else {
// no text part found? Hmmm ...
}
// clean up the caret position
toFocus.getOverlayElement().removeClassName("caretPosition");
}
}
}
});
numberingTypeEventHandlerRegistration = documentEventBus.addHandler(OverlayWidgetNumberingTypeEvent.TYPE, new OverlayWidgetNumberingTypeEventHandler() {
@Override
public void onEvent(OverlayWidgetNumberingTypeEvent event) {
event.getOverlayWidget().setOrigin(OverlayWidgetOrigin.GENERATED);
setNumbering(event.getOverlayWidget().getParentOverlayWidget(), event.getNumberingType());
}
});
formatEventHandlerRegistration = documentEventBus.addHandler(OverlayWidgetFormatEvent.TYPE, new OverlayWidgetFormatEventHandler() {
@Override
public void onEvent(OverlayWidgetFormatEvent event) {
event.getOverlayWidget().setOrigin(OverlayWidgetOrigin.GENERATED);
setFormat(event.getOverlayWidget().getParentOverlayWidget(), event.getFormat());
}
});
// forward the critical error event
criticalEventHandlerRegistration = documentEventBus.addHandler(CriticalErrorEvent.TYPE, new CriticalErrorEventHandler() {
@Override
public void onEvent(CriticalErrorEvent event) {
clientFactory.getEventBus().fireEvent(event);
}
});
// forward the confirmation event
confirmationEventHandlerRegistration = documentEventBus.addHandler(ConfirmationEvent.TYPE, new ConfirmationEventHandler() {
@Override
public void onEvent(ConfirmationEvent event) {
clientFactory.getEventBus().fireEvent(event);
}
});
// forward the notification event to the parent event bus
notificationEventHandlerRegistration = documentEventBus.addHandler(NotificationEvent.TYPE, new NotificationEventHandler() {
@Override
public void onEvent(NotificationEvent event) {
clientFactory.getEventBus().fireEvent(event);
}
});
// handle requests to switch the document tabs
switchTabEventHandlerRegistration = documentEventBus.addHandler(SwitchTabEvent.TYPE, new SwitchTabEventHandler() {
@Override
public void onEvent(SwitchTabEvent event) {
view.switchToTab(event.getTabIndex());
}
});
documentToggleStructureEventHandler = documentEventBus.addHandler(DocumentToggleStructureEvent.TYPE, new DocumentToggleStructureEventHandler() {
@Override
public void onEvent(DocumentToggleStructureEvent event) {
applyState(StructureViewMode.KEY, new ActiveState(event.isToggle()));
}
});
overlayWidgetModifyHandlerRegistration = documentEventBus.addHandler(OverlayWidgetModifyEvent.TYPE, new OverlayWidgetModifyEventHandler() {
@Override
public void onEvent(OverlayWidgetModifyEvent event) {
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(event.getOverlayWidget(), DraftingDocumentController.this));
}
});
overlayWidgetDeleteHandlerRegistration = documentEventBus.addHandler(OverlayWidgetDeleteEvent.TYPE, new OverlayWidgetDeleteEventHandler() {
@Override
public void onEvent(OverlayWidgetDeleteEvent event) {
handleOverlayWidgetDelete(event.getOverlayWidget());
}
});
overlayWidgetNewEventHandlerRegistration = documentEventBus.addHandler(OverlayWidgetNewEvent.TYPE, new OverlayWidgetNewEventHandler() {
@Override
public void onEvent(final OverlayWidgetNewEvent event) {
final OverlayWidget activeOverlayWidget = sourceFileController.getActiveOverlayWidget();
if (AkomaNtosoUtil.representsBlock(activeOverlayWidget)) {
OverlayWidget result = event.getChild();
result.setOrigin(OverlayWidgetOrigin.GENERATED);
OverlaySnippet snippet = overlaySnippetFactory.getSnippet(activeOverlayWidget);
if (snippet != null) {
overlaySnippetEvaluator.addEvaluator(
new DefaultNumEvaluator(
clientFactory,
overlayWidgetInjectionStrategy,
locator,
result,
event.getParentOverlayWidget(),
event.getReference()));
final String content = snippet.getContent(overlaySnippetEvaluator);
if (content != null) {
result.getOverlayElement().setInnerHTML(content);
result.getChildOverlayWidgets().clear();
result = overlayFactory.getAmendableWidget(result.getOverlayElement());
overlayWidgetInjectionStrategy.injectAsSibling(activeOverlayWidget, result);
result.walk(new OverlayWidgetWalker.DefaultOverlayWidgetVisitor() {
@Override
public boolean visit(OverlayWidget visited) {
//if (AkomaNtosoUtil.representsBlock(visited)) {
visited.setUIListener(sourceFileController);
//}
return super.visit(visited);
}
});
// make sure we can edit this
result.setAmendable(true);
markOverlayWidgetAsAmendable(result);
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(result, DraftingDocumentController.this));
reCalculateNumbering(activeOverlayWidget.getParentOverlayWidget());
redrawOutline(activeOverlayWidget.getRoot());
}
}
}
}
});
// listen for keyboard combos
keyComboHandlerRegistration = clientFactory.getEventBus().addHandler(KeyComboEvent.TYPE, new KeyComboEventHandler() {
@Override
public void onEvent(KeyComboEvent event) {
event.getNativeEvent().preventDefault();
final OverlayWidget activeOverlayWidget = sourceFileController.getActiveOverlayWidget();
if (ctrlEnter.equals(event.getKeyCombo())) {
// ------------- CTRL + ENTER -------------
if (!actionBarCreatePanelControllerPopup.isShowing()) {
if (activeOverlayWidget != null) {
actionBarCreatePanelController.setOverlayWidget(activeOverlayWidget);
actionBarCreatePanelControllerPopup.showRelativeTo(actionBarController.getView().getChildHandler());
actionBarCreatePanelController.getView().asWidget().setVisible(true);
actionBarCreatePanelControllerPopup.show();
actionBarCreatePanelFocusPanel.setFocus(true);
}
}
} else if (escape.equals(event.getKeyCombo())) {
// ------------- Escape -------------
if (actionBarCreatePanelControllerPopup.isShowing()) {
actionBarCreatePanelControllerPopup.hide();
actionBarCreatePanelFocusPanel.setFocus(false);
} else {
cancelModify();
}
} else if (ctrlS.equals(event.getKeyCombo())) {
// ------------- CTRL + s -------------
final String content = formatter.format(sourceFileController.getOverlayWidgets().get(0));
serviceFactory.getGwtDocumentService().saveDocumentContent(clientFactory.getClientContext(), document.getDocumentID(), content, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
clientFactory.getEventBus().fireEvent(new CriticalErrorEvent("Could not save document.", caught));
}
@Override
public void onSuccess(Void result) {
// all fine!
clientFactory.getEventBus().fireEvent(new NotificationEvent("Document successfully saved."));
}
});
} else if (downArrow.equals(event.getKeyCombo())) {
// ------------- Down arrow -------------
if (actionBarCreatePanelControllerPopup.isShowing()) {
actionBarCreatePanelFocusPanel.setFocus(true);
actionBarCreatePanelController.highlightNext();
} else {
final OverlayWidget next = activeOverlayWidget != null ? activeOverlayWidget.next(upDownOverlayWidgetSelector) : sourceFileController.getOverlayWidgets().get(0);
if (next != null) {
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(next, DraftingDocumentController.this));
clientFactory.getEventBus().fireEvent(new DocumentScrollToEvent(next.asWidget(), DraftingDocumentController.this, false, SCROLL_TO_OFFSET));
}
}
} else if (upArrow.equals(event.getKeyCombo())) {
// ------------- Up arrow -------------
if (actionBarCreatePanelControllerPopup.isShowing()) {
actionBarCreatePanelFocusPanel.setFocus(true);
actionBarCreatePanelController.highlightPrevious();
} else {
if (activeOverlayWidget != null) {
final OverlayWidget previous = activeOverlayWidget.previous(upDownOverlayWidgetSelector);
if (previous != null) {
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(previous, DraftingDocumentController.this));
clientFactory.getEventBus().fireEvent(new DocumentScrollToEvent(previous.asWidget(), DraftingDocumentController.this, false, SCROLL_TO_OFFSET));
}
}
}
} else if (enter.equals(event.getKeyCombo())) {
// ------------- Single ENTER -------------
if (actionBarCreatePanelControllerPopup.isShowing()) {
actionBarCreatePanelControllerPopup.hide();
// there is no need to scroll into document
// clientFactory.getEventBus().fireEvent(new DocumentScrollToEvent(activeOverlayWidget.asWidget(), DraftingDocumentController.this, false, SCROLL_TO_OFFSET));
final OverlayWidget selectedSibling = actionBarCreatePanelController.getSelectedSibling();
if (selectedSibling != null) {
LOG.info("Selected as sibling: " + selectedSibling);
final com.google.gwt.user.client.Element clone = DOM.clone(selectedSibling.asWidget().getElement(), true);
final OverlayWidget cloneSibling = overlayFactory.getAmendableWidget(clone);
// number and format get given by the siblings
// but they need to be explicitly set to null
cloneSibling.setNumberingType(actionBarCreatePanelController.getSelectedSibling().getNumberingType());
cloneSibling.setFormat(actionBarCreatePanelController.getSelectedSibling().getFormat());
documentEventBus.fireEvent(new OverlayWidgetNewEvent(activeOverlayWidget.getParentOverlayWidget(), activeOverlayWidget, cloneSibling));
}
final OverlayWidget selectedChild = actionBarCreatePanelController.getSelectedChild();
if (selectedChild != null) {
LOG.info("Selected as child: " + selectedChild);
final com.google.gwt.user.client.Element clone = DOM.clone(selectedChild.asWidget().getElement(), true);
final OverlayWidget cloneChild = overlayFactory.getAmendableWidget(clone);
cloneChild.setNumberingType(actionBarCreatePanelController.getSelectedChild().getNumberingType());
cloneChild.setFormat(actionBarCreatePanelController.getSelectedChild().getFormat());
documentEventBus.fireEvent(new OverlayWidgetNewEvent(activeOverlayWidget, activeOverlayWidget, cloneChild));
}
} else if (activeOverlayWidget != null) {
// we might not have selected a block, so go up
final OverlayWidget block = AkomaNtosoUtil.findEnclosingBlock(activeOverlayWidget);
if (block != null) {
OverlayWidget result = overlayFactory.getAmendableWidget(block.getNamespaceURI(), block.getType());
result.setOrigin(OverlayWidgetOrigin.GENERATED);
OverlaySnippet snippet = overlaySnippetFactory.getSnippet(block);
if (snippet != null) {
overlaySnippetEvaluator.addEvaluator(
new DefaultNumEvaluator(
clientFactory,
overlayWidgetInjectionStrategy,
locator,
result,
block.getParentOverlayWidget(),
block));
final String content = snippet.getContent(overlaySnippetEvaluator);
if (content != null) {
result.getOverlayElement().setInnerHTML(content);
result.getChildOverlayWidgets().clear();
result = overlayFactory.getAmendableWidget(result.getOverlayElement());
overlayWidgetInjectionStrategy.injectAsSibling(block, result);
result.walk(new OverlayWidgetWalker.DefaultOverlayWidgetVisitor() {
@Override
public boolean visit(OverlayWidget visited) {
// if (AkomaNtosoUtil.representsBlock(visited)) {
visited.setUIListener(sourceFileController);
// }
return super.visit(visited);
}
});
// make sure we can edit this
result.setAmendable(true);
markOverlayWidgetAsAmendable(result);
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(result, DraftingDocumentController.this));
reCalculateNumbering(block.getParentOverlayWidget());
redrawOutline(block.getRoot());
}
}
}
}
} else if (tab.equals(event.getKeyCombo())) {
// ------------- Single TAB -------------
} else if (delete.equals(event.getKeyCombo())) {
// ------------- DELETE -------------
if (activeOverlayWidget != null) {
handleOverlayWidgetDelete(activeOverlayWidget);
restoreFocusOnViewPort();
}
} else if (altUpArrow.equals(event.getKeyCombo())) {
// ------------- CTRL + Up arrow -------------
if (activeOverlayWidget != null) {
if (mover.canMoveUp(activeOverlayWidget)) {
activeOverlayWidget.moveUp();
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(activeOverlayWidget, DraftingDocumentController.this));
clientFactory.getEventBus().fireEvent(new DocumentScrollToEvent(activeOverlayWidget.asWidget(), DraftingDocumentController.this, false, SCROLL_TO_OFFSET));
// TODO schedule via timer
reCalculateNumbering(activeOverlayWidget.getParentOverlayWidget());
redrawOutline(activeOverlayWidget.getRoot());
}
});
}
}
} else if (altDownArrow.equals(event.getKeyCombo())) {
// ------------- CTRL + Down arrow -------------
if (activeOverlayWidget != null) {
if (mover.canMoveDown(activeOverlayWidget)) {
activeOverlayWidget.moveDown();
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(activeOverlayWidget, DraftingDocumentController.this));
clientFactory.getEventBus().fireEvent(new DocumentScrollToEvent(activeOverlayWidget.asWidget(), DraftingDocumentController.this, false, SCROLL_TO_OFFSET));
// TODO schedule via timer
reCalculateNumbering(activeOverlayWidget.getParentOverlayWidget());
redrawOutline(activeOverlayWidget.getRoot());
}
});
}
}
}
}
});
draftingToggleEventHandlerRegistration = clientFactory.getEventBus().addHandler(VisualStructureToggleEvent.TYPE, new VisualStructureToggleEventHandler() {
@Override
public void onEvent(VisualStructureToggleEvent event) {
//inlineEditorController.getRichTextEditor().toggleVisualStructure(event.isShown());
}
});
draftingAttributesToggleEventHandlerRegistration = clientFactory.getEventBus().addHandler(VisualStructureAttributesToggleEvent.TYPE, new VisualStructureAttributesToggleEventHandler() {
@Override
public void onEvent(VisualStructureAttributesToggleEvent event) {
//inlineEditorController.getRichTextEditor().toggleVisualStructureAttributes(event.isShown());
}
});
}
public void handleOverlayWidgetDelete(final OverlayWidget overlayWidget) {
final OverlayWidget parentOverlayWidget = overlayWidget.getParentOverlayWidget();
if (parentOverlayWidget != null) {
// find the target to make active after the deletion
OverlayWidget target = overlayWidget.next(upDownOverlayWidgetSelector);
if (target == null) target = overlayWidget.previous(upDownOverlayWidgetSelector);
parentOverlayWidget.removeOverlayWidget(overlayWidget);
overlayWidget.getOverlayElement().removeFromParent();
if (target != null) {
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(target, DraftingDocumentController.this));
}
// redraw the outline
reCalculateNumbering(parentOverlayWidget);
redrawOutline(parentOverlayWidget.getRoot());
}
}
public void handleRTEOverlayWidgetModify(final OverlayWidget parent) {
for (final OverlayWidget child : parent.getChildOverlayWidgets()) {
OverlayWidget num = OverlayUtil.findSingle("num", child);
if (num != null && num.getParentOverlayWidget() == child) {
num.setInnerHTML(locator.getNum(child, document.getLanguageIso(), true));
}
}
}
public void cancelModify() {
restoreFocusOnViewPort();
}
public void modifyOverlayWidget(final OverlayWidget overlayWidget, final String innerHTML) {
/*final Element overlayElement = inlineEditorController.getOverlayWidget().getOverlayElement();
// TODO overlay again, and correct the tree
for (OverlayWidget child : inlineEditorController.getOverlayWidget().getChildOverlayWidgets()) {
child.onDetach();
}
inlineEditorController.getOverlayWidget().getChildOverlayWidgets().clear();
overlayElement.setInnerHTML(innerHTML);
for (int i = 0; i < overlayElement.getChildCount(); i++) {
final Node child = overlayElement.getChild(i);
if (Node.ELEMENT_NODE == child.getNodeType()) {
final OverlayWidget amendableWidget = overlayFactory.getAmendableWidget((Element) child);
if (amendableWidget != null) {
overlayWidget.addOverlayWidget(amendableWidget);
} else {
LOG.warning("Could not get overlay for " + child);
}
}
}
overlayWidget.walk(new OverlayWidgetWalker.DefaultOverlayWidgetVisitor() {
@Override
public boolean visit(OverlayWidget visited) {
// if the widget is amendable, register a listener for its events
if (visited != null && visited.isAmendable() != null && visited.isAmendable()) {
visited.setUIListener(sourceFileController);
} else {
LOG.info(visited + " is NOT amendable.");
}
return true;
}
});
// remove the nsesa-inline class name if it's there
// so the next time we don't automatically attach the inline editor
final String className = overlayElement.getClassName();
if (className.contains("nsesa-inline")) {
overlayElement.setClassName(className.replace("nsesa-inline", ""));
}*/
// highlight
// sourceFileController.highlight(overlayWidget.asWidget(), "black", 1);
reCalculateNumbering(overlayWidget.getParentOverlayWidget());
}
public void redrawOutline(OverlayWidget root) {
outlineController.setRootOverlayWidget(root);
}
public void reCalculateNumbering(OverlayWidget parent) {
if (parent != null) {
for (final OverlayWidget child : parent.getChildOverlayWidgets()) {
OverlayWidget num = OverlayUtil.xpathSingle("//*/num", child);
if (num != null) {
final String formatted = locator.getNum(child, document.getLanguageIso(), true);
num.setInnerHTML(formatted);
// force a reset
child.setUnformattedIndex(null);
child.setFormattedIndex(null);
}
}
redrawOutline(parent.getRoot());
}
}
public void setNumbering(OverlayWidget parent, NumberingType numberingType) {
if (parent != null) {
for (final OverlayWidget child : parent.getChildOverlayWidgets()) {
child.setOrigin(OverlayWidgetOrigin.GENERATED);
child.setNumberingType(numberingType);
child.setUnformattedIndex(null);
child.setFormattedIndex(null);
}
reCalculateNumbering(parent);
}
}
public void setFormat(OverlayWidget parent, Format format) {
if (parent != null) {
for (final OverlayWidget child : parent.getChildOverlayWidgets()) {
child.setOrigin(OverlayWidgetOrigin.GENERATED);
child.setFormat(format);
child.setUnformattedIndex(null);
child.setFormattedIndex(null);
}
}
reCalculateNumbering(parent);
}
@Override
public void onDocumentContentLoaded(DocumentContentDTO content) {
showLoadingIndicator(true, "Parsing document.");
sourceFileController.setContent(content);
showLoadingIndicator(true, "Building document tree.");
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
overlay();
}
});
}
public void overlay() {
sourceFileController.overlay();
showLoadingIndicator(true, "Done overlaying document.");
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
clientFactory.getEventBus().fireEvent(new ResizeEvent(Window.getClientHeight(), Window.getClientWidth()));
showLoadingIndicator(false, "Done retrieving document.");
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
final List<OverlayWidget> overlayWidgets = sourceFileController.getOverlayWidgets();
if (overlayWidgets != null && !overlayWidgets.isEmpty()) {
// only display the first one
redrawOutline(overlayWidgets.get(0));
}
}
});
// find the first element that has the nsesa-inline class name, and select it
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
final List<OverlayWidget> overlayWidgets = sourceFileController.getOverlayWidgets();
if (overlayWidgets != null && !overlayWidgets.isEmpty()) {
// only display the first one
overlayWidgets.get(0).walk(new OverlayWidgetWalker.DefaultOverlayWidgetVisitor() {
@Override
public boolean visit(OverlayWidget visited) {
if (visited.getOverlayElement().getClassName().contains("nsesa-select")) {
documentEventBus.fireEvent(new OverlayWidgetSelectEvent(visited, DraftingDocumentController.this));
return false;
}
return true;
}
});
}
}
});
// for giggles, set the document root
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
final List<OverlayWidget> overlayWidgets = sourceFileController.getOverlayWidgets();
if (overlayWidgets != null && !overlayWidgets.isEmpty()) {
// only display the first one
sourceController.setOverlayWidget(overlayWidgets.get(0));
}
}
});
// set content editable on certain elements
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
final List<OverlayWidget> overlayWidgets = sourceFileController.getOverlayWidgets();
if (overlayWidgets != null && !overlayWidgets.isEmpty()) {
for (OverlayWidget root : overlayWidgets) {
root.walk(new OverlayWidgetWalker.DefaultOverlayWidgetVisitor() {
@Override
public boolean visit(OverlayWidget visited) {
markOverlayWidgetAsAmendable(visited);
return super.visit(visited);
}
});
}
}
}
});
clientFactory.getScheduler().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
final List<OverlayWidget> overlayWidgets = sourceFileController.getOverlayWidgets();
if (overlayWidgets != null && !overlayWidgets.isEmpty()) {
// only display the first one
pickerController.setOverlayWidget(overlayWidgets.get(0));
pathController.setOverlayWidget(overlayWidgets.get(0));
}
}
});
}
});
}
});
}
private void markOverlayWidgetAsAmendable(OverlayWidget visited) {
if (visited.isAmendable()) {
visited.getOverlayElement().setAttribute("contenteditable", "true");
}
if (visited instanceof Preface) {
visited.getOverlayElement().setAttribute("contenteditable", "true");
}
if (visited.isImmutable()) {
visited.getOverlayElement().setAttribute("contenteditable", "false");
}
if (visited instanceof Num) {
visited.getOverlayElement().setAttribute("contenteditable", "false");
}
}
@Override
public void setDocument(DocumentDTO document) {
this.document = document;
this.documentID = document.getDocumentID();
// update the document title
this.view.setDocumentTitle(document.getName());
// set the deadline
this.deadlineController.setDeadline(document.getDeadline());
}
@Override
public void removeListeners() {
switchTabEventHandlerRegistration.removeHandler();
notificationEventHandlerRegistration.removeHandler();
confirmationEventHandlerRegistration.removeHandler();
criticalEventHandlerRegistration.removeHandler();
overlayWidgetSelectEventHandlerRegistration.removeHandler();
resizeEventHandlerRegistration.removeHandler();
rteStructureChangedEventHandlerRegistration.removeHandler();
documentScrollToEventHandlerRegistration.removeHandler();
documentToggleStructureEventHandler.removeHandler();
keyComboHandlerRegistration.removeHandler();
draftingToggleEventHandlerRegistration.removeHandler();
draftingAttributesToggleEventHandlerRegistration.removeHandler();
overlayWidgetDeleteHandlerRegistration.removeHandler();
overlayWidgetModifyHandlerRegistration.removeHandler();
overlayWidgetNewEventHandlerRegistration.removeHandler();
selectionEventHandlerRegistration.removeHandler();
numberingTypeEventHandlerRegistration.removeHandler();
formatEventHandlerRegistration.removeHandler();
}
@Override
public void setInjector(DocumentInjector injector) {
this.documentEventBus = injector.getDocumentEventBus();
this.view = injector.getDocumentView();
this.deadlineController = injector.getDeadlineController();
this.deadlineController.setDocumentController(this);
this.sourceFileController = injector.getSourceFileController();
this.sourceFileController.setDocumentController(this);
// TODO this is ugly ...
if (injector instanceof DraftingDocumentInjector) {
final DraftingDocumentInjector draftingDocumentInjector = (DraftingDocumentInjector) injector;
this.outlineController = draftingDocumentInjector.getOutlineController();
this.outlineController.setDocumentController(this);
this.sourceController = draftingDocumentInjector.getSourceController();
this.sourceController.setDocumentController(this);
this.sourceController.setFormatter(formatter);
this.actionBarController = draftingDocumentInjector.getActionBarController();
this.actionBarController.setSourceFileController(sourceFileController);
this.actionBarCreatePanelController = draftingDocumentInjector.getActionBarCreatePanelController();
this.actionBarCreatePanelController.setSourceFileController(sourceFileController);
this.actionBarCreatePanelFocusPanel.setWidget(actionBarCreatePanelController.getView());
this.actionBarCreatePanelControllerPopup.setWidget(actionBarCreatePanelFocusPanel);
this.pathController = draftingDocumentInjector.getPathController();
this.pathController.setDocumentController(this);
this.pickerController = draftingDocumentInjector.getPickerController();
this.pickerController.setDocumentController(this);
this.numberingController = draftingDocumentInjector.getNumberingController();
this.numberingController.setDocumentController(this);
}
}
@Override
public DocumentInjector getInjector() {
return GWT.create(DraftingDocumentInjector.class);
}
public Range getCurrentSelectionRange() {
return currentSelectionRange;
}
public void setCurrentSelectionRange(Range currentSelectionRange) {
this.currentSelectionRange = currentSelectionRange;
}
/**
* Force focus on the page since when we are using keyboards in the editor we are loosing the focus
* on the document
*/
private void restoreFocusOnViewPort() {
TextBox dummyBox = new TextBox();
RootLayoutPanel.get().add(dummyBox);
dummyBox.setFocus(true);
dummyBox.removeFromParent();
}
}