/* * Copyright 2003-2016 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.mps.nodeEditor.cells; import jetbrains.mps.openapi.editor.EditorContext; import jetbrains.mps.openapi.editor.cells.SubstituteInfo; import jetbrains.mps.smodel.ModelAccessHelper; import jetbrains.mps.smodel.NodeReadAccessCasterInEditor; import jetbrains.mps.smodel.NodeReadAccessInEditorListener; import jetbrains.mps.util.Computable; import jetbrains.mps.util.Pair; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeReference; import org.jetbrains.mps.openapi.model.SNodeUtil; import org.jetbrains.mps.openapi.module.ModelAccess; /** * Author: Sergey Dmitriev * Created Sep 14, 2003 */ public class EditorCell_Property extends EditorCell_Label implements SynchronizeableEditorCell { private final ModelAccessor myModelAccessor; private boolean myCommitInProgress; private boolean myCommitInCommand = true; private String myLastModelText; public EditorCell_Property(EditorContext editorContext, ModelAccessor accessor, SNode node) { super(editorContext, node, accessor.getText()); myModelAccessor = accessor; if (myModelAccessor instanceof TransactionalPropertyAccessor) { TransactionalPropertyAccessor propertyAccessor = (TransactionalPropertyAccessor) myModelAccessor; propertyAccessor.setCell(this); } synchronizeViewWithModel(); } public static EditorCell_Property create(jetbrains.mps.openapi.editor.EditorContext editorContext, ModelAccessor modelAccessor, SNode node) { NodeReadAccessInEditorListener listener = NodeReadAccessCasterInEditor.getReadAccessListener(); if (modelAccessor instanceof PropertyAccessor) { if (listener != null) { listener.clearCleanlyReadAccessProperties(); } } EditorCell_Property result = new EditorCell_Property(editorContext, modelAccessor, node); if (listener != null) { // TODO: specify property name directly - we know it from PropertyAccessor addPropertyDependenciesToEditor(listener, result); } return result; } private static void addPropertyDependenciesToEditor(NodeReadAccessInEditorListener listener, EditorCell_Property result) { for (Pair<SNodeReference, String> pair : listener.popCleanlyReadAccessedProperties()) { result.getEditorComponent().getUpdater().getCurrentUpdateSession().registerCleanDependency(result, pair); } } @Override public void synchronizeViewWithModel() { String text = myModelAccessor.getText(); myLastModelText = text; setErrorState(!isValidText(text)); setText(text); } @Override public void setSelected(boolean selected) { boolean oldSelected = isSelected(); super.setSelected(selected); if (oldSelected && !selected && isTransactional()) { final Runnable commitCommand = new Runnable() { @Override public void run() { commit(); } }; if (myCommitInCommand) { getModelAccess().executeCommandInEDT(commitCommand); } else { getModelAccess().runWriteInEDT(commitCommand); } } } public boolean hasUncommittedValue() { if (!isTransactional()) { return false; } TransactionalModelAccessor transactionalModelAccessor = (TransactionalModelAccessor) myModelAccessor; return transactionalModelAccessor.hasValueToCommit(); } /** * should be executed inside write action * * @return true if new value was committed to model / false if nothing was changed */ public boolean commit() { getModelAccess().checkWriteAccess(); // a solution for MPS-13531 // better solution is to redispatch all currently waiting EDT commands inside MPSProject.dispose() method // currently not available - not possible to redispatch all waiting commands from AWT Thread. if (!SNodeUtil.isAccessible(getSNode(), getContext().getRepository())) { return false; } if (myCommitInProgress) { return false; } myCommitInProgress = true; try { boolean result = false; if (isTransactional()) { TransactionalModelAccessor transactionalModelAccessor = (TransactionalModelAccessor) myModelAccessor; if (transactionalModelAccessor.hasValueToCommit()) { transactionalModelAccessor.commit(); result = true; } getEditor().relayout(); return result; } } finally { myCommitInProgress = false; } return false; } @Override public void changeText(String text) { super.changeText(text); if (isValidText(text) && isEditable()) { myModelAccessor.setText(text); synchronizeViewWithModel(); return; } if (isTransactional()) { ((TransactionalModelAccessor) myModelAccessor).resetUncommittedValue(); } setErrorState(!isValidText(text)); } public String getLastModelText() { return myLastModelText; } @Override public boolean isValidText(final String text) { return new ModelAccessHelper(getModelAccess()).runReadAction(new Computable<Boolean>() { @Override public Boolean compute() { return myModelAccessor.isValidText(text); } }); } @Override public SubstituteInfo getSubstituteInfo() { final SubstituteInfo substituteInfo = super.getSubstituteInfo(); return new ModelAccessHelper(getModelAccess()).runReadAction(new Computable<SubstituteInfo>() { @Override public SubstituteInfo compute() { if (substituteInfo != null) { substituteInfo.setOriginalText(myModelAccessor.getText()); } return substituteInfo; } }); } public ModelAccessor getModelAccessor() { return myModelAccessor; } public void setCommitInCommand(boolean commit) { myCommitInCommand = commit; } @Override public void synchronize() { synchronizeViewWithModel(); } @Override public boolean canBeSynchronized() { return false; } private ModelAccess getModelAccess() { return getContext().getRepository().getModelAccess(); } private boolean isTransactional() { return myModelAccessor instanceof TransactionalModelAccessor; } @Override public void onAdd() { super.onAdd(); if (isTransactional()) { getEditor().getCellTracker().addTransactionalCell(this); } } @Override public void onRemove() { if (isTransactional()) { getEditor().getCellTracker().removeTransactionalCell(this); } super.onRemove(); } }