001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.editprovider.client; 029 030import org.opencms.gwt.client.util.CmsPositionBean; 031 032import java.util.ArrayList; 033import java.util.Collections; 034import java.util.Comparator; 035import java.util.HashMap; 036import java.util.List; 037import java.util.Map; 038 039/** 040 * This class is used to calculate positions for a set of direct edit buttons so that 041 * they don't overlap.<p> 042 * 043 * @since 8.0.0 044 */ 045public class CmsEditablePositionCalculator { 046 047 /** 048 * A comparator class which compares position beans by their left edge.<p> 049 */ 050 protected class LeftComparator implements Comparator<CmsPositionBean> { 051 052 /** 053 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 054 */ 055 public int compare(CmsPositionBean o1, CmsPositionBean o2) { 056 057 int l1 = o1.getLeft(); 058 int l2 = o2.getLeft(); 059 if (l1 < l2) { 060 return -1; 061 } 062 if (l1 > l2) { 063 return +1; 064 } 065 return 0; 066 } 067 } 068 069 /** A map of positions by element id. */ 070 private Map<String, CmsPositionBean> m_positionMap = new HashMap<String, CmsPositionBean>(); 071 072 /** The internal list of positions. */ 073 private List<CmsPositionBean> m_positions = new ArrayList<CmsPositionBean>(); 074 075 /** The assumed width of a direct edit button bar. */ 076 private static int WIDTH = 65; 077 078 /** The assumed height of a direct edit button bar. */ 079 private static int HEIGHT = 24; 080 081 /** 082 * Creates a new instance.<p> 083 * 084 * @param positions the map of original positions by element id (will not be altered) 085 */ 086 public CmsEditablePositionCalculator(Map<String, CmsPositionBean> positions) { 087 088 for (Map.Entry<String, CmsPositionBean> entry : positions.entrySet()) { 089 CmsPositionBean newPos = new CmsPositionBean(entry.getValue()); 090 m_positionMap.put(entry.getKey(), newPos); 091 m_positions.add(newPos); 092 } 093 } 094 095 /** 096 * Calculates non-overlapping positions for the button bars and returns them in a map with 097 * the element ids as keys.<p> 098 * 099 * @return the map of non-overlapping positions 100 */ 101 public Map<String, CmsPositionBean> calculatePositions() { 102 103 int maxCollisions = 500; 104 // if there are more than 500 collisions, the style is probably messed up; give up. 105 while (checkCollision() && (maxCollisions > 0)) { 106 maxCollisions -= 1; 107 } 108 return m_positionMap; 109 } 110 111 /** 112 * Checks whether a collision occurs and handle it if necessary.<p> 113 * 114 * @return true if a collision occured 115 */ 116 protected boolean checkCollision() { 117 118 // sort the positions by their left x coordinate, so we can easily exclude 119 // pairs of positions which don't overlap horizontally 120 sortByLeft(); 121 int i; 122 for (i = 0; i < m_positions.size(); i++) { 123 for (int j = i + 1; (j < m_positions.size()) 124 && intersectsHorizontally(m_positions.get(i), m_positions.get(j)); j++) { 125 if (intersectsVertically(m_positions.get(i), m_positions.get(j))) { 126 handleCollision(m_positions.get(i), m_positions.get(j)); 127 return true; 128 } 129 } 130 } 131 return false; 132 } 133 134 /** 135 * Handles a collision by moving the lower position down.<p> 136 * 137 * @param p1 the first position 138 * @param p2 the second position 139 */ 140 protected void handleCollision(CmsPositionBean p1, CmsPositionBean p2) { 141 142 CmsPositionBean positionToChange = p1; 143 if (p1.getTop() <= p2.getTop()) { 144 positionToChange = p2; 145 } 146 positionToChange.setTop(positionToChange.getTop() + 25); 147 } 148 149 /** 150 * Checks for intersection of two one-dimensional intervals.<p> 151 * 152 * @param a1 the left edge of the first interval 153 * @param a2 the right edge of the first interval 154 * @param b1 the left edge of the second interval 155 * @param b2 the right edge of the second interval 156 * 157 * @return true if the intervals intersect 158 */ 159 protected boolean intersectIntervals(int a1, int a2, int b1, int b2) { 160 161 return !((a2 < b1) || (a1 > b2)); 162 } 163 164 /** 165 * Checks whether two positions intersect horizontally.<p> 166 * 167 * @param p1 the first position 168 * @param p2 the second position 169 * 170 * @return true if the positions intersect horizontally 171 */ 172 protected boolean intersectsHorizontally(CmsPositionBean p1, CmsPositionBean p2) { 173 174 return intersectIntervals(p1.getLeft(), p1.getLeft() + WIDTH, p2.getLeft(), p2.getLeft() + WIDTH); 175 } 176 177 /** 178 * Checks whether two positions intersect vertically.<p> 179 * 180 * @param p1 the first position 181 * @param p2 the second position 182 * 183 * @return if the positions intersect vertically 184 */ 185 protected boolean intersectsVertically(CmsPositionBean p1, CmsPositionBean p2) { 186 187 return intersectIntervals(p1.getTop(), p1.getTop() + HEIGHT, p2.getTop(), p2.getTop() + HEIGHT); 188 } 189 190 /** 191 * Sorts the internal list of positions by their left edge.<p> 192 */ 193 protected void sortByLeft() { 194 195 Collections.sort(m_positions, new LeftComparator()); 196 } 197 198}