1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.diagram.participant;
\r
14 import java.awt.Shape;
\r
15 import java.util.ArrayList;
\r
16 import java.util.Collections;
\r
17 import java.util.Comparator;
\r
18 import java.util.List;
\r
19 import java.util.Map.Entry;
\r
20 import java.util.Set;
\r
22 import org.eclipse.core.runtime.ListenerList;
\r
23 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
\r
24 import org.simantics.g2d.diagram.DiagramHints;
\r
25 import org.simantics.g2d.diagram.DiagramUtils;
\r
26 import org.simantics.g2d.diagram.IDiagram;
\r
27 import org.simantics.g2d.diagram.handler.PickContext;
\r
28 import org.simantics.g2d.diagram.handler.PickRequest;
\r
29 import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;
\r
30 import org.simantics.g2d.diagram.handler.TransactionContext.TransactionType;
\r
31 import org.simantics.g2d.element.ElementUtils;
\r
32 import org.simantics.g2d.element.IElement;
\r
33 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
\r
34 import org.simantics.scenegraph.g2d.events.command.Command;
\r
35 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
\r
36 import org.simantics.scenegraph.g2d.events.command.Commands;
\r
37 import org.simantics.utils.threads.ThreadUtils;
\r
39 import gnu.trove.map.hash.TObjectIntHashMap;
\r
42 * This participant handles commands that manipulate z-ordering of elements.
\r
44 * @author Toni Kalajainen
\r
46 public class ZOrderHandler extends AbstractDiagramParticipant {
\r
48 @Dependency Selection sel;
\r
49 @Dependency PickContext pickContext;
\r
51 private final ListenerList zOrderListeners = new ListenerList(ListenerList.IDENTITY);
\r
53 public void addOrderListener(ZOrderListener listener) {
\r
54 zOrderListeners.add(listener);
\r
57 public void removeOrderListener(ZOrderListener listener) {
\r
58 zOrderListeners.remove(listener);
\r
61 @EventHandler(priority = 0)
\r
62 public boolean handleCommand(CommandEvent ce) {
\r
63 assertDependencies();
\r
64 Command c = ce.command;
\r
66 if (c.equals( Commands.BRING_UP )) {
\r
67 final Set<IElement> selectedElements = sel.getAllSelections();
\r
68 if (selectedElements==null || selectedElements.isEmpty()) return true;
\r
69 //Shape area = ElementUtils.getElementBoundsOnDiagram(selectedElements);
\r
70 Shape area = ElementUtils.getElementShapesOnDiagram(selectedElements);
\r
71 if (area==null) return true;
\r
72 final ArrayList<IElement> pickedElements = new ArrayList<IElement>();
\r
73 PickRequest req = new PickRequest(area);
\r
74 req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
\r
75 pickContext.pick(diagram, req, pickedElements);
\r
76 DiagramUtils.inDiagramTransaction(diagram, TransactionType.WRITE, new Runnable() {
\r
79 List<IElement> elements = diagram.getElements();
\r
81 boolean changed = false;
\r
82 int nextPos = elements.size()-1;
\r
83 for (int i=pickedElements.size()-1; i>=0; i--)
\r
85 IElement e = pickedElements.get(i);
\r
86 int index = elements.indexOf(e);
\r
87 if (index != -1 && selectedElements.contains(e)) {
\r
88 changed |= diagram.moveTo(e, nextPos);
\r
93 notifyZOrderListeners(diagram);
\r
95 scheduleSynchronizeElementOrder(diagram);
\r
101 if (c.equals( Commands.SEND_DOWN)) {
\r
102 final Set<IElement> selectedElements = sel.getAllSelections();
\r
103 if (selectedElements==null || selectedElements.isEmpty()) return true;
\r
104 //Shape area = ElementUtils.getElementBoundsOnDiagram(selectedElements);
\r
105 Shape area = ElementUtils.getElementShapesOnDiagram(selectedElements);
\r
106 if (area==null) return true;
\r
107 final ArrayList<IElement> pickedElements = new ArrayList<IElement>();
\r
108 PickRequest req = new PickRequest(area);
\r
109 req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
\r
110 pickContext.pick(diagram, req, pickedElements);
\r
111 DiagramUtils.inDiagramTransaction(diagram, TransactionType.WRITE, new Runnable() {
\r
113 public void run() {
\r
114 List<IElement> elements = diagram.getElements();
\r
116 boolean changed = false;
\r
118 for (int i=0; i<pickedElements.size(); i++)
\r
120 IElement e = pickedElements.get(i);
\r
121 int index = elements.indexOf(e);
\r
122 if (index != -1 && selectedElements.contains(e)) {
\r
123 changed |= diagram.moveTo(e, nextPos);
\r
128 notifyZOrderListeners(diagram);
\r
130 scheduleSynchronizeElementOrder(diagram);
\r
136 if (c.equals( Commands.BRING_TO_TOP) ){
\r
137 DiagramUtils.inDiagramTransaction(diagram, TransactionType.WRITE, new Runnable() {
\r
139 public void run() {
\r
140 boolean changed = false;
\r
141 for (Entry<Integer, Set<IElement>> e : sel.getSelections().entrySet())
\r
143 ArrayList<IElement> ll = new ArrayList<IElement>(e.getValue());
\r
145 for (int i=ll.size()-1; i>=0; i--) {
\r
146 if (diagram.containsElement(ll.get(i)))
\r
147 changed |= diagram.bringToTop(ll.get(i));
\r
151 notifyZOrderListeners(diagram);
\r
153 scheduleSynchronizeElementOrder(diagram);
\r
159 if (c.equals( Commands.SEND_TO_BOTTOM) ){
\r
160 DiagramUtils.inDiagramTransaction(diagram, TransactionType.WRITE, new Runnable() {
\r
162 public void run() {
\r
163 boolean changed = false;
\r
164 for (Entry<Integer, Set<IElement>> e : sel.getSelections().entrySet())
\r
166 ArrayList<IElement> ll = new ArrayList<IElement>(e.getValue());
\r
168 for (int i=0; i<ll.size(); i++) {
\r
169 if (diagram.containsElement(ll.get(i)))
\r
170 changed |= diagram.sendToBottom(ll.get(i));
\r
174 notifyZOrderListeners(diagram);
\r
176 scheduleSynchronizeElementOrder(diagram);
\r
185 void scheduleSynchronizeElementOrder(final IDiagram diagram) {
\r
186 // Make sure that a mutator exists before doing this.
\r
187 if (diagram.getHint(DiagramHints.KEY_MUTATOR) == null)
\r
190 // Perform back-end write in background thread to make the UI more
\r
192 ThreadUtils.getBlockingWorkExecutor().execute(() -> {
\r
193 DiagramUtils.mutateDiagram(diagram, m -> m.synchronizeElementOrder());
\r
194 //System.out.println("z-order synchronized to back-end");
\r
198 void notifyZOrderListeners(IDiagram diagram) {
\r
199 for (Object l : zOrderListeners.getListeners()) {
\r
200 ((ZOrderListener) l).orderChanged(diagram);
\r
204 void _sortByOrder(List<IElement> list)
\r
206 List<IElement> elements = diagram.getElements();
\r
207 final TObjectIntHashMap<IElement> position = new TObjectIntHashMap<IElement>();
\r
208 for (IElement e : list)
\r
209 position.put(e, elements.indexOf(e));
\r
210 Comparator<IElement> c = new Comparator<IElement>() {
\r
212 public int compare(IElement o1, IElement o2) {
\r
213 int pos1 = position.get(o1);
\r
214 int pos2 = position.get(o2);
\r
218 Collections.sort(list, c);
\r