Java tutorial
/* * Copyright 2012-2015 JetBrains s.r.o * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.jetpad.cell.toDom; import com.google.common.base.Strings; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.Style; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Timer; import jetbrains.jetpad.base.Handler; import jetbrains.jetpad.base.Registration; import jetbrains.jetpad.cell.Cell; import jetbrains.jetpad.cell.CellPropertySpec; import jetbrains.jetpad.cell.indent.IndentCell; import jetbrains.jetpad.cell.indent.IndentContainerCellListener; import jetbrains.jetpad.cell.indent.updater.CellWrapper; import jetbrains.jetpad.cell.indent.updater.IndentUpdater; import jetbrains.jetpad.cell.indent.updater.IndentUpdaterTarget; import jetbrains.jetpad.cell.toUtil.AncestorUtil; import jetbrains.jetpad.cell.toUtil.CounterUtil; import jetbrains.jetpad.geometry.Rectangle; import jetbrains.jetpad.mapper.Mapper; import jetbrains.jetpad.mapper.MappingContext; import jetbrains.jetpad.model.collections.CollectionItemEvent; import jetbrains.jetpad.model.composite.Composites; import jetbrains.jetpad.model.event.EventHandler; import jetbrains.jetpad.model.property.PropertyChangeEvent; import jetbrains.jetpad.projectional.domUtil.DomTextEditor; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; class IndentRootCellMapper extends BaseCellMapper<IndentCell> { private Set<BaseCellMapper<?>> myCellMappers; private IndentUpdater<Node> myIndentUpdater; private Registration myRegistration; private Timer myPositionUpdater; private Map<Mapper<?, ?>, Runnable> myPositionUpdaters = new HashMap<>(); IndentRootCellMapper(IndentCell source, CellToDomContext ctx) { super(source, ctx, DOM.createDiv()); myCellMappers = createChildSet(); myIndentUpdater = new IndentUpdater<Node>(getSource(), getTarget(), new IndentUpdaterTarget<Node>() { @Override public Element newLine() { Element result = DOM.createDiv(); result.addClassName(CellContainerToDomMapper.CSS.horizontal()); return result; } @Override public Element newIndent(int size) { Element result = DOM.createDiv(); DomTextEditor editor = new DomTextEditor(result); editor.setText(Strings.repeat(" ", size)); return result; } @Override public CellWrapper<Node> wrap(final Cell cell) { final BaseCellMapper<?> mapper = createMapper(cell); CounterUtil.updateOnAdd(getSource(), cell, mapper); mapper.setAncestorBackground(AncestorUtil.getAncestorBackground(getSource(), cell)); myCellMappers.add(mapper); final Registration visibilityReg = cell.visible() .addHandler(new EventHandler<PropertyChangeEvent<Boolean>>() { @Override public void onEvent(PropertyChangeEvent<Boolean> event) { myIndentUpdater.visibilityChanged(cell, event); } }); return new CellWrapper<Node>() { @Override public Node item() { return mapper.getTarget(); } @Override public void remove() { CounterUtil.updateOnRemove(getSource(), cell, mapper); myCellMappers.remove(mapper); visibilityReg.remove(); } }; } @Override public List<Node> children(Node item) { if (item instanceof Element) { return divWrappedElementChildren((Element) item); } else { throw new IllegalStateException(); } } @Override public Element parent(Node item) { if (item instanceof Element) { return item.getParentElement().getParentElement(); } else { throw new IllegalStateException(); } } }) { @Override protected void onVisibilityChanged(Cell cell, PropertyChangeEvent<Boolean> event) { myIndentUpdater.visibilityChanged(cell, event); } }; } @Override protected boolean isAutoChildManagement() { return false; } @Override protected boolean isAutoPopupManagement() { return false; } @Override protected void onAttach(MappingContext ctx) { super.onAttach(ctx); for (Cell child : getSource().children()) { myIndentUpdater.childAdded(child); } myIndentUpdater.initialized(); myRegistration = getSource().addListener(new IndentContainerCellListener() { @Override public void childAdded(CollectionItemEvent<Cell> event) { myIndentUpdater.childAdded(event.getItem()); } @Override public void childRemoved(CollectionItemEvent<Cell> event) { myIndentUpdater.childRemoved(event.getItem()); } @Override public void propertyChanged(Cell cell, final CellPropertySpec<?> prop, final PropertyChangeEvent<?> event) { if (CounterUtil.isCounterProp(prop)) { iterateLeaves(cell, new Handler<Cell>() { @Override public void handle(Cell item) { BaseCellMapper<?> mapper = (BaseCellMapper<?>) getDescendantMapper(item); if (mapper == null) { throw new IllegalStateException(); } if (CounterUtil.update(mapper, prop, event)) { mapper.refreshProperties(); } } }); } else if (Cell.isPopupProp(prop)) { PropertyChangeEvent<Cell> popupChangeEvent = (PropertyChangeEvent<Cell>) event; if (popupChangeEvent.getOldValue() != null) { BaseCellMapper<?> popupMapper = (BaseCellMapper<?>) getDescendantMapper( popupChangeEvent.getOldValue()); myCellMappers.remove(popupMapper); popupMapper.getTarget().removeFromParent(); myPositionUpdaters.remove(popupMapper); if (myPositionUpdaters.isEmpty()) { myPositionUpdater.cancel(); } } if (popupChangeEvent.getNewValue() != null) { BaseCellMapper<?> popupMapper = createMapper(popupChangeEvent.getNewValue()); myCellMappers.add(popupMapper); final PopupPositioner positioner = new PopupPositioner(getContext()); final Rectangle bounds = cell.getBounds(); final Element popupElement = popupMapper.getTarget(); popupElement.getStyle().setPosition(Style.Position.ABSOLUTE); getContext().rootElement.appendChild(popupElement); Runnable updater = new Runnable() { @Override public void run() { if (prop == Cell.BOTTOM_POPUP) { positioner.positionBottom(bounds, popupElement); } else if (prop == Cell.FRONT_POPUP) { positioner.positionFront(bounds, popupElement); } else if (prop == Cell.LEFT_POPUP) { positioner.positionLeft(bounds, popupElement); } else if (prop == Cell.RIGHT_POPUP) { positioner.positionRight(bounds, popupElement); } } }; updater.run(); if (myPositionUpdaters.isEmpty()) { myPositionUpdater.scheduleRepeating(50); } myPositionUpdaters.put(popupMapper, updater); } } else if (prop == Cell.VISIBLE) { myIndentUpdater.visibilityChanged(cell, (PropertyChangeEvent<Boolean>) event); } else if (prop == Cell.BACKGROUND) { iterateLeaves(cell, new Handler<Cell>() { @Override public void handle(Cell item) { BaseCellMapper itemMapper = (BaseCellMapper) getDescendantMapper(item); itemMapper.setAncestorBackground(AncestorUtil.getAncestorBackground(getSource(), item)); itemMapper.refreshProperties(); } }); } } private void iterateLeaves(Cell cell, Handler<Cell> handler) { for (Cell child : cell.children()) { if (!Composites.isVisible(child)) continue; if (child instanceof IndentCell) { iterateLeaves(child, handler); } else { handler.handle(child); } } } }); myPositionUpdater = new Timer() { @Override public void run() { for (Runnable r : myPositionUpdaters.values()) { r.run(); } } }; } @Override protected void onDetach() { List<Cell> children = getSource().children(); for (int i = children.size() - 1; i >= 0; i--) { Cell c = children.get(i); myIndentUpdater.childRemoved(c); } myPositionUpdater.cancel(); myRegistration.remove(); super.onDetach(); } }