blob: 4ed67c8de350e452ad4a37b0cf30c2a910b10128 [file] [log] [blame]
/*
* Copyright 2000-2012 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 com.intellij.application.options.codeStyle.arrangement.match;
import com.intellij.application.options.codeStyle.arrangement.ArrangementConstants;
import com.intellij.psi.codeStyle.arrangement.std.ArrangementStandardSettingsManager;
import com.intellij.application.options.codeStyle.arrangement.animation.ArrangementAnimationManager;
import com.intellij.application.options.codeStyle.arrangement.animation.ArrangementAnimationPanel;
import com.intellij.application.options.codeStyle.arrangement.color.ArrangementColorsProvider;
import com.intellij.application.options.codeStyle.arrangement.component.ArrangementAndMatchConditionComponent;
import com.intellij.application.options.codeStyle.arrangement.component.ArrangementAtomMatchConditionComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchConditionVisitor;
import com.intellij.psi.codeStyle.arrangement.std.ArrangementUiComponent;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* @author Denis Zhdanov
* @since 8/10/12 2:53 PM
*/
public class ArrangementMatchNodeComponentFactory {
private static final Logger LOG = Logger.getInstance("#" + ArrangementMatchNodeComponentFactory.class.getName());
@NotNull private final ArrangementStandardSettingsManager mySettingsManager;
@NotNull private final ArrangementColorsProvider myColorsProvider;
@NotNull private final ArrangementMatchingRulesControl myList;
public ArrangementMatchNodeComponentFactory(@NotNull ArrangementStandardSettingsManager manager,
@NotNull ArrangementColorsProvider provider,
@NotNull ArrangementMatchingRulesControl list)
{
mySettingsManager = manager;
myColorsProvider = provider;
myList = list;
}
/**
* Allows to build UI component for the given model.
*
* @param rendererTarget target model element for which UI component should be built
* @param rule rule which contains given 'renderer target' condition and serves as
* a data entry for the target list model
* @param allowModification flag which indicates whether given model can be changed at future
* @return renderer for the given model
*/
@NotNull
public ArrangementUiComponent getComponent(@NotNull final ArrangementMatchCondition rendererTarget,
@NotNull final StdArrangementMatchRule rule,
final boolean allowModification)
{
final Ref<ArrangementUiComponent> ref = new Ref<ArrangementUiComponent>();
rendererTarget.invite(new ArrangementMatchConditionVisitor() {
@Override
public void visit(@NotNull ArrangementAtomMatchCondition condition) {
RemoveAtomConditionCallback callback = allowModification ? new RemoveAtomConditionCallback(rule) : null;
ArrangementUiComponent component = new ArrangementAtomMatchConditionComponent(
mySettingsManager, myColorsProvider, condition, callback
);
ref.set(component);
}
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition condition) {
ref.set(new ArrangementAndMatchConditionComponent(rule, condition, ArrangementMatchNodeComponentFactory.this, mySettingsManager, allowModification));
}
});
return ref.get();
}
private class RemoveAtomConditionCallback implements Consumer<ArrangementAtomMatchConditionComponent>,
ArrangementAnimationManager.Callback
{
@NotNull private final StdArrangementMatchRule myRule;
@NotNull private Object myModelValue;
private int myRow;
RemoveAtomConditionCallback(@NotNull StdArrangementMatchRule rule) {
myRule = rule;
myModelValue = myRule;
}
@Override
public void consume(@NotNull ArrangementAtomMatchConditionComponent component) {
ArrangementAtomMatchCondition condition = component.getMatchCondition();
ArrangementMatchingRulesModel model = myList.getModel();
int i = getModelIndex();
if (i < 0) {
return;
}
myRow = i;
ArrangementMatchCondition existingCondition = myRule.getMatcher().getCondition();
if (existingCondition.equals(condition)) {
// We can't just remove an element at this time because that breaks last row rendering.
model.set(i, myModelValue = new DummyElement());
}
else {
assert existingCondition instanceof ArrangementCompositeMatchCondition;
Set<ArrangementMatchCondition> operands = ((ArrangementCompositeMatchCondition)existingCondition).getOperands();
operands.remove(condition);
if (operands.isEmpty()) {
// We can't just remove an element at this time because that breaks last row rendering.
model.set(i, myModelValue = new DummyElement());
}
else if (operands.size() == 1) {
myModelValue = new StdArrangementMatchRule(new StdArrangementEntryMatcher(operands.iterator().next()), myRule.getOrderType());
model.set(i, myModelValue);
}
else if (ArrangementConstants.LOG_RULE_MODIFICATION) {
LOG.info(String.format("Removed '%s' condition. Current rule state: %s", condition, myRule));
myModelValue = myRule;
}
}
ArrangementAnimationPanel panel = component.getAnimationPanel();
new ArrangementAnimationManager(panel, this).startAnimation();
}
@Override
public void onAnimationIteration(boolean finished) {
refreshRow();
if (myRow < 0) {
return;
}
myList.repaintRows(myRow, myRow, finished);
if (!finished) {
return;
}
if (myModelValue instanceof DummyElement) {
myList.removeRow(myRow);
}
}
private void refreshRow() {
ArrangementMatchingRulesModel model = myList.getModel();
if (myRow < 0 || myRow >= model.getSize()) {
myRow = getModelIndex();
}
else {
Object o = model.getElementAt(myRow);
if (o != myModelValue) {
myRow = getModelIndex();
}
}
}
private int getModelIndex() {
// We can't just use model.indexOf(myRule) because there is a possible case that the model contain equal
// rules (rule1.equals(rule2) == true). That's why we have a helper method for search by reference identity.
ArrangementMatchingRulesModel model = myList.getModel();
for (int i = 0, max = model.getSize(); i < max; i++) {
if (model.getElementAt(i) == myModelValue) {
return i;
}
}
return -1;
}
}
private static class DummyElement {
@Override
public String toString() {
return "dummy-" + System.identityHashCode(this);
}
}
}