/* * Freeplane - mind map editor * Copyright (C) 2008 Dimitry Polivaev * * This file author is Dimitry Polivaev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.freeplane.features.filter; import java.util.Collection; import javax.swing.Icon; import javax.swing.ImageIcon; import org.freeplane.core.resources.ResourceController; import org.freeplane.features.filter.condition.ICondition; import org.freeplane.features.map.IMapSelection; import org.freeplane.features.map.MapChangeEvent; import org.freeplane.features.map.MapModel; import org.freeplane.features.map.NodeModel; import org.freeplane.features.mode.Controller; /** * @author Dimitry Polivaev */ public class Filter { static Filter createTransparentFilter() { return new Filter(null, true, false, false); } final private boolean appliesToVisibleNodesOnly; final private ICondition condition; final private int options; private boolean cancelled = false; public Filter(final ICondition condition, final boolean areAnchestorsShown, final boolean areDescendantsShown, final boolean applyToVisibleNodesOnly) { super(); this.condition = condition; int options = FilterInfo.FILTER_INITIAL_VALUE | FilterInfo.FILTER_SHOW_MATCHED; if (areAnchestorsShown) { options += FilterInfo.FILTER_SHOW_ANCESTOR; } options += FilterInfo.FILTER_SHOW_ECLIPSED; if (areDescendantsShown) { options += FilterInfo.FILTER_SHOW_DESCENDANT; } this.options = options; appliesToVisibleNodesOnly = condition != null && applyToVisibleNodesOnly; } void addFilterResult(final NodeModel node, final int flag) { node.getFilterInfo().add(flag); } protected boolean appliesToVisibleNodesOnly() { return appliesToVisibleNodesOnly; } static private Icon filterIcon; void displayFilterStatus() { if (filterIcon == null) { filterIcon = new ImageIcon(ResourceController.getResourceController().getResource("/images/filter.png")); } if (getCondition() != null) { Controller.getCurrentController().getViewController().addStatusInfo("filter", null, filterIcon); } else { Controller.getCurrentController().getViewController().removeStatus("filter"); } } /* * (non-Javadoc) * @see * freeplane.controller.filter.Filter#applyFilter(freeplane.modes.MindMap) */ public void applyFilter(Object source, final MapModel map, final boolean force) { if (map == null) { return; } try { displayFilterStatus(); Controller.getCurrentController().getViewController().setWaitingCursor(true); final Filter oldFilter = map.getFilter(); map.setFilter(this); if (force || !isConditionStronger(oldFilter)) { final NodeModel root = map.getRootNode(); resetFilter(root); if (filterChildren(root, checkNode(root), false)) { addFilterResult(root, FilterInfo.FILTER_SHOW_ANCESTOR); } } final IMapSelection selection = Controller.getCurrentController().getSelection(); final NodeModel selected = selection.getSelected(); final NodeModel selectedVisible = selected.getVisibleAncestorOrSelf(); selection.keepNodePosition(selectedVisible, 0.5f, 0.5f); refreshMap(source, map); selectVisibleNode(); } finally { Controller.getCurrentController().getViewController().setWaitingCursor(false); cancelled = false; } } private boolean applyFilter(final NodeModel node, final boolean isAncestorSelected, final boolean isAncestorEclipsed, boolean isDescendantSelected) { final boolean conditionSatisfied = checkNode(node); resetFilter(node); if (isAncestorSelected) { addFilterResult(node, FilterInfo.FILTER_SHOW_DESCENDANT); } if (conditionSatisfied) { isDescendantSelected = true; addFilterResult(node, FilterInfo.FILTER_SHOW_MATCHED); } else { addFilterResult(node, FilterInfo.FILTER_SHOW_HIDDEN); } if (isAncestorEclipsed) { addFilterResult(node, FilterInfo.FILTER_SHOW_ECLIPSED); } if (filterChildren(node, conditionSatisfied || isAncestorSelected, !conditionSatisfied || isAncestorEclipsed)) { addFilterResult(node, FilterInfo.FILTER_SHOW_ANCESTOR); isDescendantSelected = true; } return isDescendantSelected; } /* * (non-Javadoc) * @see freeplane.controller.filter.Filter#areAncestorsShown() */ public boolean areAncestorsShown() { return 0 != (options & FilterInfo.FILTER_SHOW_ANCESTOR); } /* * (non-Javadoc) * @see freeplane.controller.filter.Filter#areDescendantsShown() */ public boolean areDescendantsShown() { return 0 != (options & FilterInfo.FILTER_SHOW_DESCENDANT); } private boolean checkNode(final NodeModel node) { if (condition == null || cancelled) { return true; } if (appliesToVisibleNodesOnly && !node.isVisible()) { return false; } try { return condition.checkNode(node); } catch (FilterCancelledException e) { cancelled = true; return true; } } private boolean filterChildren(final NodeModel node, final boolean isAncestorSelected, final boolean isAncestorEclipsed) { boolean isDescendantSelected = false; for (final NodeModel child : Controller.getCurrentModeController().getMapController().childrenUnfolded(node)) { isDescendantSelected = applyFilter(child, isAncestorSelected, isAncestorEclipsed, isDescendantSelected); } return isDescendantSelected; } public ICondition getCondition() { return condition; } public boolean isConditionStronger(final Filter oldFilter) { return (!appliesToVisibleNodesOnly || appliesToVisibleNodesOnly == oldFilter.appliesToVisibleNodesOnly) && (condition != null && condition.equals(oldFilter.getCondition()) || condition == null && oldFilter.getCondition() == null); } /* * (non-Javadoc) * @see * freeplane.controller.filter.Filter#isVisible(freeplane.modes.MindMapNode) */ public boolean isVisible(final NodeModel node) { if (condition == null) { return true; } final int filterResult = node.getFilterInfo().get(); return ((options & FilterInfo.FILTER_SHOW_ANCESTOR) != 0 || (options & FilterInfo.FILTER_SHOW_ECLIPSED) >= (filterResult & FilterInfo.FILTER_SHOW_ECLIPSED)) && ((options & filterResult & ~FilterInfo.FILTER_SHOW_ECLIPSED) != 0); } private void refreshMap(Object source, MapModel map) { Controller.getCurrentModeController().getMapController().fireMapChanged(new MapChangeEvent(source, map, Filter.class, null, this)); } private void resetFilter(final NodeModel node) { node.getFilterInfo().reset(); } private void selectVisibleNode() { final IMapSelection mapSelection = Controller.getCurrentController().getSelection(); final Collection<NodeModel> selectedNodes = mapSelection.getSelection(); final NodeModel[] array = new NodeModel[selectedNodes.size()]; boolean next = false; for(NodeModel node : selectedNodes.toArray(array)){ if(next){ if (!node.isVisible()) { mapSelection.toggleSelected(node); } } else next = true; } NodeModel selected = mapSelection.getSelected(); if (!selected.isVisible()) { if(mapSelection.getSelection().size() > 1){ mapSelection.toggleSelected(selected); } else mapSelection.selectAsTheOnlyOneSelected(selected.getVisibleAncestorOrSelf()); } mapSelection.setSiblingMaxLevel(mapSelection.getSelected().getNodeLevel(false)); } public boolean matches(NodeModel nodeModel) { return 0 != (nodeModel.getFilterInfo().get() & FilterInfo.FILTER_SHOW_MATCHED); } }