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.

AbstractSplitPanel.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  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.io.Serializable;
  18. import java.lang.reflect.Method;
  19. import java.util.Iterator;
  20. import com.vaadin.event.ComponentEventListener;
  21. import com.vaadin.event.MouseEvents.ClickEvent;
  22. import com.vaadin.server.Sizeable;
  23. import com.vaadin.shared.EventId;
  24. import com.vaadin.shared.MouseEventDetails;
  25. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelRpc;
  26. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState;
  27. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState.SplitterState;
  28. import com.vaadin.util.ReflectTools;
  29. /**
  30. * AbstractSplitPanel.
  31. *
  32. * <code>AbstractSplitPanel</code> is base class for a component container that
  33. * can contain two components. The components are split by a divider element.
  34. *
  35. * @author Vaadin Ltd.
  36. * @since 6.5
  37. */
  38. public abstract class AbstractSplitPanel extends AbstractComponentContainer {
  39. // TODO use Unit in AbstractSplitPanelState and remove these
  40. private Unit posUnit;
  41. private Unit posMinUnit;
  42. private Unit posMaxUnit;
  43. private AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() {
  44. @Override
  45. public void splitterClick(MouseEventDetails mouseDetails) {
  46. fireEvent(new SplitterClickEvent(AbstractSplitPanel.this,
  47. mouseDetails));
  48. }
  49. @Override
  50. public void setSplitterPosition(float position) {
  51. getSplitterState().position = position;
  52. }
  53. };
  54. public AbstractSplitPanel() {
  55. registerRpc(rpc);
  56. setSplitPosition(50, Unit.PERCENTAGE, false);
  57. setSplitPositionLimits(0, Unit.PERCENTAGE, 100, Unit.PERCENTAGE);
  58. }
  59. /**
  60. * Modifiable and Serializable Iterator for the components, used by
  61. * {@link AbstractSplitPanel#getComponentIterator()}.
  62. */
  63. private class ComponentIterator implements Iterator<Component>,
  64. Serializable {
  65. int i = 0;
  66. @Override
  67. public boolean hasNext() {
  68. if (i < getComponentCount()) {
  69. return true;
  70. }
  71. return false;
  72. }
  73. @Override
  74. public Component next() {
  75. if (!hasNext()) {
  76. return null;
  77. }
  78. i++;
  79. if (i == 1) {
  80. return (getFirstComponent() == null ? getSecondComponent()
  81. : getFirstComponent());
  82. } else if (i == 2) {
  83. return getSecondComponent();
  84. }
  85. return null;
  86. }
  87. @Override
  88. public void remove() {
  89. if (i == 1) {
  90. if (getFirstComponent() != null) {
  91. setFirstComponent(null);
  92. i = 0;
  93. } else {
  94. setSecondComponent(null);
  95. }
  96. } else if (i == 2) {
  97. setSecondComponent(null);
  98. }
  99. }
  100. }
  101. /**
  102. * Add a component into this container. The component is added to the right
  103. * or under the previous component.
  104. *
  105. * @param c
  106. * the component to be added.
  107. */
  108. @Override
  109. public void addComponent(Component c) {
  110. if (getFirstComponent() == null) {
  111. setFirstComponent(c);
  112. } else if (getSecondComponent() == null) {
  113. setSecondComponent(c);
  114. } else {
  115. throw new UnsupportedOperationException(
  116. "Split panel can contain only two components");
  117. }
  118. }
  119. /**
  120. * Sets the first component of this split panel. Depending on the direction
  121. * the first component is shown at the top or to the left.
  122. *
  123. * @param c
  124. * The component to use as first component
  125. */
  126. public void setFirstComponent(Component c) {
  127. if (getFirstComponent() == c) {
  128. // Nothing to do
  129. return;
  130. }
  131. if (getFirstComponent() != null) {
  132. // detach old
  133. removeComponent(getFirstComponent());
  134. }
  135. getState().firstChild = c;
  136. if (c != null) {
  137. super.addComponent(c);
  138. }
  139. }
  140. /**
  141. * Sets the second component of this split panel. Depending on the direction
  142. * the second component is shown at the bottom or to the left.
  143. *
  144. * @param c
  145. * The component to use as first component
  146. */
  147. public void setSecondComponent(Component c) {
  148. if (getSecondComponent() == c) {
  149. // Nothing to do
  150. return;
  151. }
  152. if (getSecondComponent() != null) {
  153. // detach old
  154. removeComponent(getSecondComponent());
  155. }
  156. getState().secondChild = c;
  157. if (c != null) {
  158. super.addComponent(c);
  159. }
  160. }
  161. /**
  162. * Gets the first component of this split panel. Depending on the direction
  163. * this is either the component shown at the top or to the left.
  164. *
  165. * @return the first component of this split panel
  166. */
  167. public Component getFirstComponent() {
  168. return (Component) getState().firstChild;
  169. }
  170. /**
  171. * Gets the second component of this split panel. Depending on the direction
  172. * this is either the component shown at the top or to the left.
  173. *
  174. * @return the second component of this split panel
  175. */
  176. public Component getSecondComponent() {
  177. return (Component) getState().secondChild;
  178. }
  179. /**
  180. * Removes the component from this container.
  181. *
  182. * @param c
  183. * the component to be removed.
  184. */
  185. @Override
  186. public void removeComponent(Component c) {
  187. super.removeComponent(c);
  188. if (c == getFirstComponent()) {
  189. getState().firstChild = null;
  190. } else if (c == getSecondComponent()) {
  191. getState().secondChild = null;
  192. }
  193. markAsDirty();
  194. }
  195. /*
  196. * (non-Javadoc)
  197. *
  198. * @see com.vaadin.ui.ComponentContainer#getComponentIterator()
  199. */
  200. @Override
  201. public Iterator<Component> iterator() {
  202. return new ComponentIterator();
  203. }
  204. /**
  205. * Gets the number of contained components. Consistent with the iterator
  206. * returned by {@link #getComponentIterator()}.
  207. *
  208. * @return the number of contained components (zero, one or two)
  209. */
  210. @Override
  211. public int getComponentCount() {
  212. int count = 0;
  213. if (getFirstComponent() != null) {
  214. count++;
  215. }
  216. if (getSecondComponent() != null) {
  217. count++;
  218. }
  219. return count;
  220. }
  221. /* Documented in superclass */
  222. @Override
  223. public void replaceComponent(Component oldComponent, Component newComponent) {
  224. if (oldComponent == getFirstComponent()) {
  225. setFirstComponent(newComponent);
  226. } else if (oldComponent == getSecondComponent()) {
  227. setSecondComponent(newComponent);
  228. }
  229. markAsDirty();
  230. }
  231. /**
  232. * Moves the position of the splitter.
  233. *
  234. * @param pos
  235. * the new size of the first region in the unit that was last
  236. * used (default is percentage). Fractions are only allowed when
  237. * unit is percentage.
  238. */
  239. public void setSplitPosition(float pos) {
  240. setSplitPosition(pos, posUnit, false);
  241. }
  242. /**
  243. * Moves the position of the splitter.
  244. *
  245. * @param pos
  246. * the new size of the region in the unit that was last used
  247. * (default is percentage). Fractions are only allowed when unit
  248. * is percentage.
  249. *
  250. * @param reverse
  251. * if set to true the split splitter position is measured by the
  252. * second region else it is measured by the first region
  253. */
  254. public void setSplitPosition(float pos, boolean reverse) {
  255. setSplitPosition(pos, posUnit, reverse);
  256. }
  257. /**
  258. * Moves the position of the splitter with given position and unit.
  259. *
  260. * @param pos
  261. * the new size of the first region. Fractions are only allowed
  262. * when unit is percentage.
  263. * @param unit
  264. * the unit (from {@link Sizeable}) in which the size is given.
  265. */
  266. public void setSplitPosition(float pos, Unit unit) {
  267. setSplitPosition(pos, unit, false);
  268. }
  269. /**
  270. * Moves the position of the splitter with given position and unit.
  271. *
  272. * @param pos
  273. * the new size of the first region. Fractions are only allowed
  274. * when unit is percentage.
  275. * @param unit
  276. * the unit (from {@link Sizeable}) in which the size is given.
  277. * @param reverse
  278. * if set to true the split splitter position is measured by the
  279. * second region else it is measured by the first region
  280. *
  281. */
  282. public void setSplitPosition(float pos, Unit unit, boolean reverse) {
  283. if (unit != Unit.PERCENTAGE && unit != Unit.PIXELS) {
  284. throw new IllegalArgumentException(
  285. "Only percentage and pixel units are allowed");
  286. }
  287. if (unit != Unit.PERCENTAGE) {
  288. pos = Math.round(pos);
  289. }
  290. SplitterState splitterState = getSplitterState();
  291. splitterState.position = pos;
  292. splitterState.positionUnit = unit.getSymbol();
  293. splitterState.positionReversed = reverse;
  294. posUnit = unit;
  295. }
  296. /**
  297. * Returns the current position of the splitter, in
  298. * {@link #getSplitPositionUnit()} units.
  299. *
  300. * @return position of the splitter
  301. */
  302. public float getSplitPosition() {
  303. return getSplitterState().position;
  304. }
  305. /**
  306. * Returns the unit of position of the splitter
  307. *
  308. * @return unit of position of the splitter
  309. */
  310. public Unit getSplitPositionUnit() {
  311. return posUnit;
  312. }
  313. /**
  314. * Sets the minimum split position to the given position and unit. If the
  315. * split position is reversed, maximum and minimum are also reversed.
  316. *
  317. * @param pos
  318. * the minimum position of the split
  319. * @param unit
  320. * the unit (from {@link Sizeable}) in which the size is given.
  321. * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS
  322. */
  323. public void setMinSplitPosition(int pos, Unit unit) {
  324. setSplitPositionLimits(pos, unit, getSplitterState().maxPosition,
  325. posMaxUnit);
  326. }
  327. /**
  328. * Returns the current minimum position of the splitter, in
  329. * {@link #getMinSplitPositionUnit()} units.
  330. *
  331. * @return the minimum position of the splitter
  332. */
  333. public float getMinSplitPosition() {
  334. return getSplitterState().minPosition;
  335. }
  336. /**
  337. * Returns the unit of the minimum position of the splitter.
  338. *
  339. * @return the unit of the minimum position of the splitter
  340. */
  341. public Unit getMinSplitPositionUnit() {
  342. return posMinUnit;
  343. }
  344. /**
  345. * Sets the maximum split position to the given position and unit. If the
  346. * split position is reversed, maximum and minimum are also reversed.
  347. *
  348. * @param pos
  349. * the maximum position of the split
  350. * @param unit
  351. * the unit (from {@link Sizeable}) in which the size is given.
  352. * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS
  353. */
  354. public void setMaxSplitPosition(float pos, Unit unit) {
  355. setSplitPositionLimits(getSplitterState().minPosition, posMinUnit, pos,
  356. unit);
  357. }
  358. /**
  359. * Returns the current maximum position of the splitter, in
  360. * {@link #getMaxSplitPositionUnit()} units.
  361. *
  362. * @return the maximum position of the splitter
  363. */
  364. public float getMaxSplitPosition() {
  365. return getSplitterState().maxPosition;
  366. }
  367. /**
  368. * Returns the unit of the maximum position of the splitter
  369. *
  370. * @return the unit of the maximum position of the splitter
  371. */
  372. public Unit getMaxSplitPositionUnit() {
  373. return posMaxUnit;
  374. }
  375. /**
  376. * Sets the maximum and minimum position of the splitter. If the split
  377. * position is reversed, maximum and minimum are also reversed.
  378. *
  379. * @param minPos
  380. * the new minimum position
  381. * @param minPosUnit
  382. * the unit (from {@link Sizeable}) in which the minimum position
  383. * is given.
  384. * @param maxPos
  385. * the new maximum position
  386. * @param maxPosUnit
  387. * the unit (from {@link Sizeable}) in which the maximum position
  388. * is given.
  389. */
  390. private void setSplitPositionLimits(float minPos, Unit minPosUnit,
  391. float maxPos, Unit maxPosUnit) {
  392. if ((minPosUnit != Unit.PERCENTAGE && minPosUnit != Unit.PIXELS)
  393. || (maxPosUnit != Unit.PERCENTAGE && maxPosUnit != Unit.PIXELS)) {
  394. throw new IllegalArgumentException(
  395. "Only percentage and pixel units are allowed");
  396. }
  397. SplitterState state = getSplitterState();
  398. state.minPosition = minPos;
  399. state.minPositionUnit = minPosUnit.getSymbol();
  400. posMinUnit = minPosUnit;
  401. state.maxPosition = maxPos;
  402. state.maxPositionUnit = maxPosUnit.getSymbol();
  403. posMaxUnit = maxPosUnit;
  404. }
  405. /**
  406. * Lock the SplitPanels position, disabling the user from dragging the split
  407. * handle.
  408. *
  409. * @param locked
  410. * Set <code>true</code> if locked, <code>false</code> otherwise.
  411. */
  412. public void setLocked(boolean locked) {
  413. getSplitterState().locked = locked;
  414. }
  415. /**
  416. * Is the SplitPanel handle locked (user not allowed to change split
  417. * position by dragging).
  418. *
  419. * @return <code>true</code> if locked, <code>false</code> otherwise.
  420. */
  421. public boolean isLocked() {
  422. return getSplitterState().locked;
  423. }
  424. /**
  425. * <code>SplitterClickListener</code> interface for listening for
  426. * <code>SplitterClickEvent</code> fired by a <code>SplitPanel</code>.
  427. *
  428. * @see SplitterClickEvent
  429. * @since 6.2
  430. */
  431. public interface SplitterClickListener extends ComponentEventListener {
  432. public static final Method clickMethod = ReflectTools.findMethod(
  433. SplitterClickListener.class, "splitterClick",
  434. SplitterClickEvent.class);
  435. /**
  436. * SplitPanel splitter has been clicked
  437. *
  438. * @param event
  439. * SplitterClickEvent event.
  440. */
  441. public void splitterClick(SplitterClickEvent event);
  442. }
  443. public static class SplitterClickEvent extends ClickEvent {
  444. public SplitterClickEvent(Component source,
  445. MouseEventDetails mouseEventDetails) {
  446. super(source, mouseEventDetails);
  447. }
  448. }
  449. public void addSplitterClickListener(SplitterClickListener listener) {
  450. addListener(EventId.CLICK_EVENT_IDENTIFIER, SplitterClickEvent.class,
  451. listener, SplitterClickListener.clickMethod);
  452. }
  453. /**
  454. * @deprecated As of 7.0, replaced by
  455. * {@link #addSplitterClickListener(SplitterClickListener)}
  456. **/
  457. @Deprecated
  458. public void addListener(SplitterClickListener listener) {
  459. addSplitterClickListener(listener);
  460. }
  461. public void removeSplitterClickListener(SplitterClickListener listener) {
  462. removeListener(EventId.CLICK_EVENT_IDENTIFIER,
  463. SplitterClickEvent.class, listener);
  464. }
  465. /**
  466. * @deprecated As of 7.0, replaced by
  467. * {@link #removeSplitterClickListener(SplitterClickListener)}
  468. **/
  469. @Deprecated
  470. public void removeListener(SplitterClickListener listener) {
  471. removeSplitterClickListener(listener);
  472. }
  473. @Override
  474. protected AbstractSplitPanelState getState() {
  475. return (AbstractSplitPanelState) super.getState();
  476. }
  477. private SplitterState getSplitterState() {
  478. return getState().splitterState;
  479. }
  480. }