/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.editpolicies; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.jface.dialogs.MessageDialog; import org.xmind.core.IBoundary; import org.xmind.core.IRelationship; import org.xmind.core.ISheet; import org.xmind.core.ISummary; import org.xmind.core.ITopic; import org.xmind.core.ITopicRange; import org.xmind.core.IWorkbook; import org.xmind.core.marker.IMarkerRef; import org.xmind.core.util.Point; import org.xmind.gef.IViewer; import org.xmind.gef.command.ICommandStack; import org.xmind.ui.commands.AddBoundaryCommand; import org.xmind.ui.commands.AddRelationshipCommand; import org.xmind.ui.commands.AddSummaryCommand; import org.xmind.ui.commands.AddTopicCommand; import org.xmind.ui.commands.ModifyPositionCommand; import org.xmind.ui.commands.ModifyRangeCommand; import org.xmind.ui.internal.actions.ActionConstants; import org.xmind.ui.internal.dialogs.DialogMessages; /** * @author Karelun Huang */ public class SortTopicCommandBuilder extends DeleteCommandBuilder { private class Range { int startIndex; int endIndex; public Range(int startIndex, int endIndex) { this.startIndex = startIndex; this.endIndex = endIndex; } public boolean equals(Object obj) { if (obj == this) return true; if (obj != this && !(obj instanceof Range)) return false; Range that = (Range) obj; return this.startIndex == that.startIndex && this.endIndex == that.endIndex; } } private String type; private Map<ITopicRange, Range> cacheRanges = null; private List<IRelationship> cacheRelations = null; private Map<ISummary, ITopic> cacheSummaryTopics = null; public SortTopicCommandBuilder(IViewer viewer, ICommandStack commandStack) { super(viewer, commandStack); } public void setSortType(String type) { this.type = type; } public void sort(ITopic parent) { List<ITopic> children = new ArrayList<ITopic>( parent.getChildren(ITopic.ATTACHED)); if (children.isEmpty()) return; cacheRelationships(parent); cacheTopicRanges(parent); List<ITopic> topics = resort(children); if (!canResort(topics)) { MessageDialog.openWarning(null, DialogMessages.SortMessageDialog_Title, DialogMessages.SortMessageDialog_Messages); return; } for (ITopic topic : children) { deleteTopic(topic, true); } children.clear(); for (int i = 0; i < topics.size(); i++) { ITopic topic = topics.get(i); addTopic(topic, parent, i); modifyTopicPosition(topic); } topics.clear(); addRelationships(parent); addTopicRanges(parent); } private void modifyTopicPosition(ITopic topic) { Point position = topic.getPosition(); if (position == null) return; ModifyPositionCommand command = new ModifyPositionCommand(topic, null); add(command, true); } private boolean canResort(List<ITopic> topics) { if (cacheRanges == null) return true; for (Entry<ITopicRange, Range> entry : cacheRanges.entrySet()) { ITopicRange topicRange = entry.getKey(); List<ITopic> enTopics = topicRange.getEnclosingTopics(); if (enTopics.isEmpty() || enTopics.size() == 1) continue; List<Integer> newIndexList = null; for (ITopic topic : enTopics) { int index = topics.indexOf(topic); if (newIndexList == null) newIndexList = new ArrayList<Integer>(); newIndexList.add(index); } Collections.sort(newIndexList, new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { return o1 - o2; } }); int flag = -1; for (int index : newIndexList) { if (flag == -1) { flag = index; continue; } if (Math.abs(flag - index) > 1) return false; flag = index; } int startIndex = newIndexList.get(0); int endIndex = newIndexList.get(newIndexList.size() - 1); Range range = entry.getValue(); range.startIndex = startIndex; range.endIndex = endIndex; newIndexList.clear(); } return true; } private void cacheRelationships(ITopic topic) { ISheet sheet = topic.getOwnedSheet(); Set<IRelationship> relations = sheet.getRelationships(); if (relations.isEmpty()) return; Iterator<IRelationship> itera = relations.iterator(); while (itera.hasNext()) { IRelationship next = itera.next(); if (canAddInCache(topic, next)) { if (cacheRelations == null) cacheRelations = new ArrayList<IRelationship>(); cacheRelations.add(next); } } } private boolean canAddInCache(ITopic topic, IRelationship raltionship) { IWorkbook workbook = topic.getOwnedWorkbook(); String end1Id = raltionship.getEnd1Id(); String end2Id = raltionship.getEnd2Id(); Object obj1 = workbook.getElementById(end1Id); Object obj2 = workbook.getElementById(end2Id); return isPosterity(topic, obj1) || isPosterity(topic, obj2); } private boolean isPosterity(ITopic parent, Object obj) { if (obj instanceof ITopicRange) { return isPosterityOfRange(parent, (ITopicRange) obj); } else if (obj instanceof ITopic) { return isPosterityOfTopic(parent, (ITopic) obj); } return false; } private boolean isPosterityOfTopic(ITopic parent, ITopic topic) { if (topic.isRoot()) return false; ITopic parentTopic = topic.getParent(); if (parentTopic.equals(parent)) return true; if (parentTopic.isRoot()) return false; return isPosterityOfTopic(parentTopic, topic); } private boolean isPosterityOfRange(ITopic parent, ITopicRange topicRange) { ITopic parentTopic = topicRange.getParent(); if (parentTopic.equals(parent)) return true; if (parentTopic.isRoot()) return false; return isPosterityOfRange(parentTopic, topicRange); } private void addRelationships(ITopic parent) { if (cacheRelations == null || cacheRelations.isEmpty()) return; for (IRelationship relationship : cacheRelations) { AddRelationshipCommand command = new AddRelationshipCommand( relationship, parent.getOwnedSheet()); add(command, true); } } private void addTopicRanges(ITopic parent) { if (cacheRanges == null || cacheRanges.isEmpty()) return; for (Entry<ITopicRange, Range> entry : cacheRanges.entrySet()) { ITopicRange topicRange = entry.getKey(); Range range = entry.getValue(); if (topicRange instanceof ISummary) { ISummary summary = (ISummary) topicRange; ITopic summaryTopic = cacheSummaryTopics.get(summary); AddTopicCommand addTopicCommand = new AddTopicCommand( summaryTopic, parent, -1, ITopic.SUMMARY); add(addTopicCommand, false); AddSummaryCommand summaryCommand = new AddSummaryCommand( summary, parent); add(summaryCommand, false); } else if (topicRange instanceof IBoundary) { IBoundary boundary = (IBoundary) topicRange; AddBoundaryCommand boundaryCommand = new AddBoundaryCommand( boundary, parent); add(boundaryCommand, false); } ModifyRangeCommand modifyStart = new ModifyRangeCommand(topicRange, range.startIndex, true); add(modifyStart, false); ModifyRangeCommand modifyend = new ModifyRangeCommand(topicRange, range.endIndex, false); add(modifyend, false); } } private void cacheTopicRanges(ITopic parent) { if (cacheRanges == null) cacheRanges = new HashMap<ITopicRange, Range>(); if (cacheSummaryTopics == null) cacheSummaryTopics = new HashMap<ISummary, ITopic>(); Set<IBoundary> boundaries = parent.getBoundaries(); if (!boundaries.isEmpty()) { Iterator<IBoundary> itera = boundaries.iterator(); while (itera.hasNext()) { IBoundary next = itera.next(); if (!next.isMasterBoundary()) { int startIndex = next.getStartIndex(); int endIndex = next.getEndIndex(); if (startIndex >= 0 && endIndex >= 0) { cacheRanges.put(next, new Range(startIndex, endIndex)); } } } } Set<ISummary> summaries = parent.getSummaries(); if (!summaries.isEmpty()) { Iterator<ISummary> itera = summaries.iterator(); while (itera.hasNext()) { ISummary next = itera.next(); int startIndex = next.getStartIndex(); int endIndex = next.getEndIndex(); if (startIndex >= 0 && endIndex >= 0) { ITopic summaryTopic = next.getTopic(); cacheRanges.put(next, new Range(startIndex, endIndex)); cacheSummaryTopics.put(next, summaryTopic); } } } } private void addTopic(ITopic topic, ITopic parent, int toIndex) { AddTopicCommand command = new AddTopicCommand(topic, parent, toIndex, ITopic.ATTACHED); add(command, true); } private List<ITopic> resort(List<ITopic> oldTopics) { ArrayList<ITopic> newTopics = new ArrayList<ITopic>(oldTopics.size()); for (ITopic topic : oldTopics) newTopics.add(topic); Collections.sort(newTopics, new Comparator<ITopic>() { public int compare(ITopic o1, ITopic o2) { if (ActionConstants.SORT_TITLE_ID.equals(type)) { String text1 = o1.getTitleText(); String text2 = o2.getTitleText(); return text1.compareToIgnoreCase(text2); } else if (ActionConstants.SORT_PRIORITY_ID.equals(type)) { int p1 = getPriority(o1); int p2 = getPriority(o2); return p1 - p2; } else if (ActionConstants.SORT_MODIFIED_ID.equals(type)) { long time1 = o1.getModifiedTime(); long time2 = o2.getModifiedTime(); long ex = time1 - time2; return (int) ex; } return 0; } }); return newTopics; } private int getPriority(ITopic topic) { Iterator<IMarkerRef> itera = topic.getMarkerRefs().iterator(); while (itera.hasNext()) { IMarkerRef next = itera.next(); String markerId = next.getMarkerId(); if (markerId.startsWith("priority")) { //$NON-NLS-1$ int index = markerId.indexOf('-'); String number = markerId.substring(index + 1); return Integer.parseInt(number); } } return Integer.MAX_VALUE; } }