package com.kartoflane.superluminal2.core; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.TreeMap; import java.util.TreeSet; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import com.kartoflane.superluminal2.components.interfaces.Predicate; import com.kartoflane.superluminal2.components.interfaces.Selectable; import com.kartoflane.superluminal2.mvc.controllers.AbstractController; /** * The class governs when and in what order controllers are drawn. * Also implements several methods for selection. * * The first controller in a layer is the bottom-most one. * The last controller in a layer is the topmost one. * * @author kartoFlane * */ public class LayeredPainter implements PaintListener { public enum Layers { BACKGROUND, MOUNT, GIBS, IMAGES, GRID, ROOM, SYSTEM, STATION, GLOW, DOOR, PROP, SHIP_ORIGIN, CURSOR } private static final LayeredPainter instance = new LayeredPainter(); /** Specifies the layer order for selection and highlighting purposes. */ private static final Layers[] selectionOrder = { Layers.BACKGROUND, Layers.GRID, Layers.GIBS, Layers.IMAGES, Layers.MOUNT, Layers.ROOM, Layers.SYSTEM, Layers.DOOR, Layers.STATION, Layers.GLOW, Layers.PROP, Layers.SHIP_ORIGIN, Layers.CURSOR }; /** Specifies the order in which the layers are drawn. */ private final TreeMap<Layers, ArrayList<AbstractController>> layerMap = new TreeMap<Layers, ArrayList<AbstractController>>(); private final HashMap<Layers, Boolean> drawLayerMap = new HashMap<Layers, Boolean>(); public LayeredPainter() { for (Layers layer : Layers.values()) { // Add a bunch of empty lists to hold layers. layerMap.put(layer, new ArrayList<AbstractController>()); // Set all layers to be drawn. drawLayerMap.put(layer, true); } } public static LayeredPainter getInstance() { return instance; } /* * ================================== * XXX: Layering manipulation methods * ================================== */ public void setLayerDrawn(Layers layer, boolean draw) { if (layer == null) throw new IllegalArgumentException("Layer must not be null."); drawLayerMap.put(layer, draw); } public boolean isLayerDrawn(Layers layer) { if (layer == null) throw new IllegalArgumentException("Layer must not be null."); return drawLayerMap.get(layer); } /** * Places the Controller at the top of the layer. The newly added Controller * will be drawn last on the layer, and will therefore appear as topmost. */ public void add(AbstractController controller, Layers layer) { if (controller == null) throw new IllegalArgumentException("Controller is null."); if (layer == null) throw new IllegalArgumentException("Layer must not be null."); ArrayList<AbstractController> list = layerMap.get(layer); list.add(controller); } /** * Places the Controller at the bottom of the layer. The newly added Controller * will be drawn first on the layer, and will therefore appear as bottom-most. */ public void addToBottom(AbstractController controller, Layers layer) { if (controller == null) throw new IllegalArgumentException("Controller is null."); if (layer == null) throw new IllegalArgumentException("Layer must not be null."); ArrayList<AbstractController> list = layerMap.get(layer); list.add(0, controller); } /** * Removes the controller from all layers. */ public void remove(AbstractController controller) { if (controller == null) throw new IllegalArgumentException("Controller is null."); for (Layers layer : Layers.values()) { layerMap.get(layer).remove(controller); } } /** * Removes the controller from the specified layer. */ public boolean remove(AbstractController controller, Layers layer) { if (controller == null) throw new IllegalArgumentException("Controller is null."); if (layer == null) throw new IllegalArgumentException("Layer must not be null."); return layerMap.get(layer).remove(controller); } /* * =================================== * XXX: Helper, get & location methods * =================================== */ public boolean contains(AbstractController controller) { if (controller == null) throw new IllegalArgumentException("Controller is null."); for (Layers layer : Layers.values()) { if (drawLayerMap.get(layer)) { for (AbstractController c : layerMap.get(layer)) if (c == controller) return true; } } return false; } public TreeMap<Layers, ArrayList<AbstractController>> getLayerMap() { return layerMap; } /** @return sorted set of Layers in the order in which they are drawn. */ public TreeSet<Layers> getLayers() { TreeSet<Layers> layers = new TreeSet<Layers>(); for (Layers layer : Layers.values()) layers.add(layer); return layers; } /** @return array of Layers, specifying the layer order for selection and highlighting purposes. */ public Layers[] getSelectionOrder() { return selectionOrder.clone(); } public void paintControl(PaintEvent e) { Rectangle dirtyRect = new Rectangle(e.x - 2, e.y - 2, e.width + 4, e.height + 4); for (Layers layer : Layers.values()) { if (drawLayerMap.get(layer)) { for (AbstractController controller : layerMap.get(layer)) { if (controller.intersects(dirtyRect)) controller.redraw(e); } } } e.gc.dispose(); } /** @return the topmost visible Controller in a layer at a point, or null if none was found. */ public AbstractController getControllerAt(int x, int y, Layers layer) { if (layer == null) throw new IllegalArgumentException("Layer must not be null."); // Layers are arranged in such a way that the bottom-most controller is the first in the layer's list. // Therefore, we need to iterate over the list in reverse order to get the topmost controller. for (int i = layerMap.get(layer).size() - 1; i >= 0; i--) { AbstractController controller = layerMap.get(layer).get(i); if (controller.isVisible() && controller.contains(x, y)) return controller; } return null; } public AbstractController getControllerAt(Point p, Layers layer) { return getControllerAt(p.x, p.y, layer); } /** @return the topmost visible, selectable Controller in a layer at a point, or null if none was found. */ public AbstractController getSelectableControllerAt(int x, int y, Layers layer) { if (layer == null) throw new IllegalArgumentException("Layer must not be null."); if (!drawLayerMap.get(layer)) return null; // Layers are arranged in such a way that the bottom-most controller is the first in the layer's list. // Therefore, we need to iterate over the list in reverse order to get the topmost controller. for (int i = layerMap.get(layer).size() - 1; i >= 0; i--) { AbstractController controller = layerMap.get(layer).get(i); if (controller instanceof Selectable && ((Selectable) controller).isSelectable() && controller.isVisible() && controller.contains(x, y)) { return controller; } } return null; } public AbstractController getSelectableControllerAt(Point p, Layers layer) { return getSelectableControllerAt(p.x, p.y, layer); } /** @return the topmost visible Controller, on whichever layer. */ public AbstractController getControllerAt(int x, int y) { for (int i = selectionOrder.length - 1; i >= 0; i--) { Layers layer = selectionOrder[i]; if (drawLayerMap.get(layer)) { AbstractController controller = getControllerAt(x, y, layer); if (controller != null) return controller; } } return null; } public AbstractController getControllerAt(Point p) { return getControllerAt(p.x, p.y); } /** @eturn the topmost visible, selectable Controller at the given coordinates, on whichever layer. */ public AbstractController getSelectableControllerAt(int x, int y) { for (int i = selectionOrder.length - 1; i >= 0; i--) { Layers layer = selectionOrder[i]; if (drawLayerMap.get(layer)) { AbstractController controller = getSelectableControllerAt(x, y, layer); if (controller != null) return controller; } } return null; } public AbstractController getSelectableControllerAt(Point p) { return getSelectableControllerAt(p.x, p.y); } /** @return the topmost Controller matching the conditions set by the predicate. */ public AbstractController getControllerMatching(Predicate<AbstractController> p, Layers layer) { if (layer == null) throw new IllegalArgumentException("Layer must not be null."); for (int i = layerMap.get(layer).size() - 1; i >= 0; i--) { AbstractController controller = layerMap.get(layer).get(i); if (p.accept(controller)) { return controller; } } return null; } /** @return the topmost Controller matching the conditions set by the predicate, on whichever layer. */ public AbstractController getControllerMatching(Predicate<AbstractController> p) { for (Layers layer : selectionOrder) { if (drawLayerMap.get(layer)) { AbstractController controller = getControllerMatching(p, layer); if (controller != null) return controller; } } return null; } /** * @return a list of all controllers that contain the point */ public List<AbstractController> getAllControllersAt(int x, int y) { ArrayList<AbstractController> list = new ArrayList<AbstractController>(); for (Layers layer : selectionOrder) { for (AbstractController controller : layerMap.get(layer)) { if (controller != null && controller.contains(x, y)) list.add(controller); } } return list; } /** * @return a list of all controllers that contain the point */ public List<AbstractController> getAllControllersAt(Point p) { return getAllControllersAt(p.x, p.y); } /** * @return a list of all selectable controllers that contain the point */ public List<AbstractController> getAllSelectableControllersAt(int x, int y) { ArrayList<AbstractController> list = new ArrayList<AbstractController>(); for (Layers layer : selectionOrder) { if (drawLayerMap.get(layer)) { for (AbstractController controller : layerMap.get(layer)) { if (controller != null && controller.isSelectable() && controller.contains(x, y)) list.add(controller); } } } return list; } /** * @return a list of all selectable controllers that contain the point */ public List<AbstractController> getAllSelectableControllersAt(Point p) { return getAllSelectableControllersAt(p.x, p.y); } /** @return all controllers matching the conditions set by the predicate */ public List<AbstractController> getAllControllersMatching(Predicate<AbstractController> p) { ArrayList<AbstractController> list = new ArrayList<AbstractController>(); for (Layers layer : selectionOrder) { for (AbstractController controller : layerMap.get(layer)) { if (controller != null && p.accept(controller)) list.add(controller); } } return list; } /** @return all controllers on the given layer matching the conditions set by the predicate */ public List<AbstractController> getAllControllersMatching(Predicate<AbstractController> p, Layers layer) { if (layer == null) throw new IllegalArgumentException("Layer must not be null."); ArrayList<AbstractController> list = new ArrayList<AbstractController>(); for (AbstractController controller : layerMap.get(layer)) { if (controller != null && p.accept(controller)) list.add(controller); } return list; } }