You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractOrderedLayout.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /*
  2. * Copyright 2011 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.util.Iterator;
  18. import java.util.LinkedList;
  19. import com.vaadin.event.LayoutEvents.LayoutClickEvent;
  20. import com.vaadin.event.LayoutEvents.LayoutClickListener;
  21. import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
  22. import com.vaadin.server.Sizeable;
  23. import com.vaadin.shared.Connector;
  24. import com.vaadin.shared.EventId;
  25. import com.vaadin.shared.MouseEventDetails;
  26. import com.vaadin.shared.ui.MarginInfo;
  27. import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc;
  28. import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState;
  29. import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState.ChildComponentData;
  30. @SuppressWarnings("serial")
  31. public abstract class AbstractOrderedLayout extends AbstractLayout implements
  32. Layout.AlignmentHandler, Layout.SpacingHandler, LayoutClickNotifier,
  33. Layout.MarginHandler {
  34. private AbstractOrderedLayoutServerRpc rpc = new AbstractOrderedLayoutServerRpc() {
  35. @Override
  36. public void layoutClick(MouseEventDetails mouseDetails,
  37. Connector clickedConnector) {
  38. fireEvent(LayoutClickEvent.createEvent(AbstractOrderedLayout.this,
  39. mouseDetails, clickedConnector));
  40. }
  41. };
  42. public static final Alignment ALIGNMENT_DEFAULT = Alignment.TOP_LEFT;
  43. /**
  44. * Custom layout slots containing the components.
  45. */
  46. protected LinkedList<Component> components = new LinkedList<Component>();
  47. /* Child component alignments */
  48. /**
  49. * Constructs an empty AbstractOrderedLayout.
  50. */
  51. public AbstractOrderedLayout() {
  52. registerRpc(rpc);
  53. }
  54. @Override
  55. protected AbstractOrderedLayoutState getState() {
  56. return (AbstractOrderedLayoutState) super.getState();
  57. }
  58. /**
  59. * Add a component into this container. The component is added to the right
  60. * or under the previous component.
  61. *
  62. * @param c
  63. * the component to be added.
  64. */
  65. @Override
  66. public void addComponent(Component c) {
  67. // Add to components before calling super.addComponent
  68. // so that it is available to AttachListeners
  69. components.add(c);
  70. try {
  71. super.addComponent(c);
  72. } catch (IllegalArgumentException e) {
  73. components.remove(c);
  74. throw e;
  75. }
  76. componentAdded(c);
  77. }
  78. /**
  79. * Adds a component into this container. The component is added to the left
  80. * or on top of the other components.
  81. *
  82. * @param c
  83. * the component to be added.
  84. */
  85. public void addComponentAsFirst(Component c) {
  86. // If c is already in this, we must remove it before proceeding
  87. // see ticket #7668
  88. if (c.getParent() == this) {
  89. removeComponent(c);
  90. }
  91. components.addFirst(c);
  92. try {
  93. super.addComponent(c);
  94. } catch (IllegalArgumentException e) {
  95. components.remove(c);
  96. throw e;
  97. }
  98. componentAdded(c);
  99. }
  100. /**
  101. * Adds a component into indexed position in this container.
  102. *
  103. * @param c
  104. * the component to be added.
  105. * @param index
  106. * the index of the component position. The components currently
  107. * in and after the position are shifted forwards.
  108. */
  109. public void addComponent(Component c, int index) {
  110. // If c is already in this, we must remove it before proceeding
  111. // see ticket #7668
  112. if (c.getParent() == this) {
  113. // When c is removed, all components after it are shifted down
  114. if (index > getComponentIndex(c)) {
  115. index--;
  116. }
  117. removeComponent(c);
  118. }
  119. components.add(index, c);
  120. try {
  121. super.addComponent(c);
  122. } catch (IllegalArgumentException e) {
  123. components.remove(c);
  124. throw e;
  125. }
  126. componentAdded(c);
  127. }
  128. private void componentRemoved(Component c) {
  129. getState().childData.remove(c);
  130. }
  131. private void componentAdded(Component c) {
  132. getState().childData.put(c, new ChildComponentData());
  133. }
  134. /**
  135. * Removes the component from this container.
  136. *
  137. * @param c
  138. * the component to be removed.
  139. */
  140. @Override
  141. public void removeComponent(Component c) {
  142. components.remove(c);
  143. super.removeComponent(c);
  144. componentRemoved(c);
  145. }
  146. /**
  147. * Gets the component container iterator for going trough all the components
  148. * in the container.
  149. *
  150. * @return the Iterator of the components inside the container.
  151. */
  152. @Override
  153. public Iterator<Component> iterator() {
  154. return components.iterator();
  155. }
  156. /**
  157. * Gets the number of contained components. Consistent with the iterator
  158. * returned by {@link #getComponentIterator()}.
  159. *
  160. * @return the number of contained components
  161. */
  162. @Override
  163. public int getComponentCount() {
  164. return components.size();
  165. }
  166. /* Documented in superclass */
  167. @Override
  168. public void replaceComponent(Component oldComponent, Component newComponent) {
  169. // Gets the locations
  170. int oldLocation = -1;
  171. int newLocation = -1;
  172. int location = 0;
  173. for (final Iterator<Component> i = components.iterator(); i.hasNext();) {
  174. final Component component = i.next();
  175. if (component == oldComponent) {
  176. oldLocation = location;
  177. }
  178. if (component == newComponent) {
  179. newLocation = location;
  180. }
  181. location++;
  182. }
  183. if (oldLocation == -1) {
  184. addComponent(newComponent);
  185. } else if (newLocation == -1) {
  186. removeComponent(oldComponent);
  187. addComponent(newComponent, oldLocation);
  188. } else {
  189. // Both old and new are in the layout
  190. if (oldLocation > newLocation) {
  191. components.remove(oldComponent);
  192. components.add(newLocation, oldComponent);
  193. components.remove(newComponent);
  194. components.add(oldLocation, newComponent);
  195. } else {
  196. components.remove(newComponent);
  197. components.add(oldLocation, newComponent);
  198. components.remove(oldComponent);
  199. components.add(newLocation, oldComponent);
  200. }
  201. markAsDirty();
  202. }
  203. }
  204. @Override
  205. public void setComponentAlignment(Component childComponent,
  206. Alignment alignment) {
  207. ChildComponentData childData = getState().childData.get(childComponent);
  208. if (childData != null) {
  209. // Alignments are bit masks
  210. childData.alignmentBitmask = alignment.getBitMask();
  211. } else {
  212. throw new IllegalArgumentException(
  213. "Component must be added to layout before using setComponentAlignment()");
  214. }
  215. }
  216. /*
  217. * (non-Javadoc)
  218. *
  219. * @see com.vaadin.ui.Layout.AlignmentHandler#getComponentAlignment(com
  220. * .vaadin.ui.Component)
  221. */
  222. @Override
  223. public Alignment getComponentAlignment(Component childComponent) {
  224. ChildComponentData childData = getState().childData.get(childComponent);
  225. if (childData == null) {
  226. throw new IllegalArgumentException(
  227. "The given component is not a child of this layout");
  228. }
  229. return new Alignment(childData.alignmentBitmask);
  230. }
  231. /*
  232. * (non-Javadoc)
  233. *
  234. * @see com.vaadin.ui.Layout.SpacingHandler#setSpacing(boolean)
  235. */
  236. @Override
  237. public void setSpacing(boolean spacing) {
  238. getState().spacing = spacing;
  239. }
  240. /*
  241. * (non-Javadoc)
  242. *
  243. * @see com.vaadin.ui.Layout.SpacingHandler#isSpacing()
  244. */
  245. @Override
  246. public boolean isSpacing() {
  247. return getState().spacing;
  248. }
  249. /**
  250. * <p>
  251. * This method is used to control how excess space in layout is distributed
  252. * among components. Excess space may exist if layout is sized and contained
  253. * non relatively sized components don't consume all available space.
  254. *
  255. * <p>
  256. * Example how to distribute 1:3 (33%) for component1 and 2:3 (67%) for
  257. * component2 :
  258. *
  259. * <code>
  260. * layout.setExpandRatio(component1, 1);<br>
  261. * layout.setExpandRatio(component2, 2);
  262. * </code>
  263. *
  264. * <p>
  265. * If no ratios have been set, the excess space is distributed evenly among
  266. * all components.
  267. *
  268. * <p>
  269. * Note, that width or height (depending on orientation) needs to be defined
  270. * for this method to have any effect.
  271. *
  272. * @see Sizeable
  273. *
  274. * @param component
  275. * the component in this layout which expand ratio is to be set
  276. * @param ratio
  277. */
  278. public void setExpandRatio(Component component, float ratio) {
  279. ChildComponentData childData = getState().childData.get(component);
  280. if (childData == null) {
  281. throw new IllegalArgumentException(
  282. "The given component is not a child of this layout");
  283. }
  284. childData.expandRatio = ratio;
  285. }
  286. /**
  287. * Returns the expand ratio of given component.
  288. *
  289. * @param component
  290. * which expand ratios is requested
  291. * @return expand ratio of given component, 0.0f by default.
  292. */
  293. public float getExpandRatio(Component component) {
  294. ChildComponentData childData = getState().childData.get(component);
  295. if (childData == null) {
  296. throw new IllegalArgumentException(
  297. "The given component is not a child of this layout");
  298. }
  299. return childData.expandRatio;
  300. }
  301. @Override
  302. public void addLayoutClickListener(LayoutClickListener listener) {
  303. addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER,
  304. LayoutClickEvent.class, listener,
  305. LayoutClickListener.clickMethod);
  306. }
  307. /**
  308. * @deprecated As of 7.0, replaced by
  309. * {@link #addLayoutClickListener(LayoutClickListener)}
  310. **/
  311. @Override
  312. @Deprecated
  313. public void addListener(LayoutClickListener listener) {
  314. addLayoutClickListener(listener);
  315. }
  316. @Override
  317. public void removeLayoutClickListener(LayoutClickListener listener) {
  318. removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER,
  319. LayoutClickEvent.class, listener);
  320. }
  321. /**
  322. * @deprecated As of 7.0, replaced by
  323. * {@link #removeLayoutClickListener(LayoutClickListener)}
  324. **/
  325. @Override
  326. @Deprecated
  327. public void removeListener(LayoutClickListener listener) {
  328. removeLayoutClickListener(listener);
  329. }
  330. /**
  331. * Returns the index of the given component.
  332. *
  333. * @param component
  334. * The component to look up.
  335. * @return The index of the component or -1 if the component is not a child.
  336. */
  337. public int getComponentIndex(Component component) {
  338. return components.indexOf(component);
  339. }
  340. /**
  341. * Returns the component at the given position.
  342. *
  343. * @param index
  344. * The position of the component.
  345. * @return The component at the given index.
  346. * @throws IndexOutOfBoundsException
  347. * If the index is out of range.
  348. */
  349. public Component getComponent(int index) throws IndexOutOfBoundsException {
  350. return components.get(index);
  351. }
  352. @Override
  353. public void setMargin(boolean enabled) {
  354. setMargin(new MarginInfo(enabled));
  355. }
  356. /*
  357. * (non-Javadoc)
  358. *
  359. * @see com.vaadin.ui.Layout.MarginHandler#getMargin()
  360. */
  361. @Override
  362. public MarginInfo getMargin() {
  363. return new MarginInfo(getState().marginsBitmask);
  364. }
  365. /*
  366. * (non-Javadoc)
  367. *
  368. * @see com.vaadin.ui.Layout.MarginHandler#setMargin(MarginInfo)
  369. */
  370. @Override
  371. public void setMargin(MarginInfo marginInfo) {
  372. getState().marginsBitmask = marginInfo.getBitMask();
  373. }
  374. }